/* * LEAmDNS.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 "LEAmDNS_Priv.h" namespace esp8266 { /* * LEAmDNS */ namespace MDNSImplementation { /** * STRINGIZE */ #ifndef STRINGIZE #define STRINGIZE(x) #x #endif #ifndef STRINGIZE_VALUE_OF #define STRINGIZE_VALUE_OF(x) STRINGIZE(x) #endif /** * INTERFACE */ /** * MDNSResponder::MDNSResponder */ MDNSResponder::MDNSResponder(void) : m_pServices(0), m_pUDPContext(0), m_pcHostname(0), m_pServiceQueries(0), m_fnServiceTxtCallback(0), m_pServiceTxtCallbackUserdata(0) { } /* * MDNSResponder::~MDNSResponder */ MDNSResponder::~MDNSResponder(void) { _resetProbeStatus(false); _releaseServiceQueries(); _releaseHostname(); _releaseUDPContext(); _releaseServices(); } /* * MDNSResponder::begin * * Set the host domain (for probing) and install WiFi event handlers for * IP assignment and disconnection management. In both cases, the MDNS responder * is restarted (reset and restart probe status) * Finally the responder is (re)started * */ bool MDNSResponder::begin(const char* p_pcHostname) { bool bResult = false; if (_setHostname(p_pcHostname)) { m_GotIPHandler = WiFi.onStationModeGotIP([this](const WiFiEventStationModeGotIP& pEvent) { (void) pEvent; _restart(); }); m_DisconnectedHandler = WiFi.onStationModeDisconnected([this](const WiFiEventStationModeDisconnected& pEvent) { (void) pEvent; _restart(); }); bResult = _restart(); } DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] begin: FAILED for '%s'!\n"), (p_pcHostname ?: "-")); } ); return bResult; } /* * MDNSResponder::begin (LEGACY) */ bool MDNSResponder::begin(const char* p_pcHostname, IPAddress p_IPAddress, uint32_t p_u32TTL /*= 120*/) { (void) p_IPAddress; (void) p_u32TTL; return begin(p_pcHostname); } /* * MDNSResponder::close * * Ends the MDNS responder. * Announced services are unannounced (by multicasting a goodbye message) * */ bool MDNSResponder::close(void) { _announce(false); _resetProbeStatus(false); // Stop probing _releaseServiceQueries(); _releaseUDPContext(); _releaseHostname(); return true; } /* * MDNSResponder::setHostname * * Replaces the current hostname and restarts probing. * For services without own instance name (when the host name was used a instance * name), the instance names are replaced also (and the probing is restarted). * */ bool MDNSResponder::setHostname(const char* p_pcHostname) { bool bResult = false; if (_setHostname(p_pcHostname)) { m_HostProbeInformation.m_ProbingStatus = ProbingStatus_ReadyToStart; // Replace 'auto-set' service names bResult = true; for (stcMDNSService* pService=m_pServices; ((bResult) && (pService)); pService=pService->m_pNext) { if (pService->m_bAutoName) { bResult = pService->setName(p_pcHostname); pService->m_ProbeInformation.m_ProbingStatus = ProbingStatus_ReadyToStart; } } } DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] setHostname: FAILED for '%s'!\n"), (p_pcHostname ?: "-")); } ); return bResult; } /* * MDNSResponder::setHostname (LEGACY) */ bool MDNSResponder::setHostname(String p_strHostname) { return setHostname(p_strHostname.c_str()); } /* * SERVICES */ /* * MDNSResponder::addService * * Add service; using hostname if no name is explicitly provided for the service * The usual '_' underline, which is prepended to service and protocol, eg. _http, * may be given. If not, it is added automatically. * */ MDNSResponder::hMDNSService MDNSResponder::addService(const char* p_pcName, const char* p_pcService, const char* p_pcProtocol, uint16_t p_u16Port) { hMDNSService hResult = 0; if (((!p_pcName) || // NO name OR (MDNS_DOMAIN_LABEL_MAXLENGTH >= os_strlen(p_pcName))) && // Fitting name (p_pcService) && (MDNS_SERVICE_NAME_LENGTH >= os_strlen(p_pcService)) && (p_pcProtocol) && ((MDNS_SERVICE_PROTOCOL_LENGTH - 1) != os_strlen(p_pcProtocol)) && (p_u16Port)) { if (!_findService((p_pcName ?: m_pcHostname), p_pcService, p_pcProtocol)) { // Not already used if (0 != (hResult = (hMDNSService)_allocService(p_pcName, p_pcService, p_pcProtocol, p_u16Port))) { // Start probing ((stcMDNSService*)hResult)->m_ProbeInformation.m_ProbingStatus = ProbingStatus_ReadyToStart; } } } // else: bad arguments DEBUG_EX_ERR(if (!hResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] addService: FAILED to add '%s.%s.%s'!\n"), (p_pcName ?: "-"), p_pcService, p_pcProtocol); } ); return hResult; } /* * MDNSResponder::removeService * * Unanounce a service (by sending a goodbye message) and remove it * from the MDNS responder * */ bool MDNSResponder::removeService(const MDNSResponder::hMDNSService p_hService) { stcMDNSService* pService = 0; bool bResult = (((pService = _findService(p_hService))) && (_announceService(*pService, false)) && (_releaseService(pService))); DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] removeService: FAILED!\n")); } ); return bResult; } /* * MDNSResponder::removeService */ bool MDNSResponder::removeService(const char* p_pcName, const char* p_pcService, const char* p_pcProtocol) { return removeService((hMDNSService)_findService((p_pcName ?: m_pcHostname), p_pcService, p_pcProtocol)); } /* * MDNSResponder::addService (LEGACY) */ bool MDNSResponder::addService(String p_strService, String p_strProtocol, uint16_t p_u16Port) { return (0 != addService(m_pcHostname, p_strService.c_str(), p_strProtocol.c_str(), p_u16Port)); } /* * MDNSResponder::setServiceName */ bool MDNSResponder::setServiceName(const MDNSResponder::hMDNSService p_hService, const char* p_pcInstanceName) { stcMDNSService* pService = 0; bool bResult = (((!p_pcInstanceName) || (MDNS_DOMAIN_LABEL_MAXLENGTH >= os_strlen(p_pcInstanceName))) && ((pService = _findService(p_hService))) && (pService->setName(p_pcInstanceName)) && ((pService->m_ProbeInformation.m_ProbingStatus = ProbingStatus_ReadyToStart))); DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] setServiceName: FAILED for '%s'!\n"), (p_pcInstanceName ?: "-")); } ); return bResult; } /* * SERVICE TXT */ /* * MDNSResponder::addServiceTxt * * Add a static service TXT item ('Key'='Value') to a service. * */ MDNSResponder::hMDNSTxt MDNSResponder::addServiceTxt(const MDNSResponder::hMDNSService p_hService, const char* p_pcKey, const char* p_pcValue) { hMDNSTxt hTxt = 0; stcMDNSService* pService = _findService(p_hService); if (pService) { hTxt = (hMDNSTxt)_addServiceTxt(pService, p_pcKey, p_pcValue, false); } DEBUG_EX_ERR(if (!hTxt) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] addServiceTxt: FAILED for '%s=%s'!\n"), (p_pcKey ?: "-"), (p_pcValue ?: "-")); } ); return hTxt; } /* * MDNSResponder::addServiceTxt (uint32_t) * * Formats: http://www.cplusplus.com/reference/cstdio/printf/ */ MDNSResponder::hMDNSTxt MDNSResponder::addServiceTxt(const MDNSResponder::hMDNSService p_hService, const char* p_pcKey, uint32_t p_u32Value) { char acBuffer[32]; *acBuffer = 0; sprintf(acBuffer, "%u", p_u32Value); return addServiceTxt(p_hService, p_pcKey, acBuffer); } /* * MDNSResponder::addServiceTxt (uint16_t) */ MDNSResponder::hMDNSTxt MDNSResponder::addServiceTxt(const MDNSResponder::hMDNSService p_hService, const char* p_pcKey, uint16_t p_u16Value) { char acBuffer[16]; *acBuffer = 0; sprintf(acBuffer, "%hu", p_u16Value); return addServiceTxt(p_hService, p_pcKey, acBuffer); } /* * MDNSResponder::addServiceTxt (uint8_t) */ MDNSResponder::hMDNSTxt MDNSResponder::addServiceTxt(const MDNSResponder::hMDNSService p_hService, const char* p_pcKey, uint8_t p_u8Value) { char acBuffer[8]; *acBuffer = 0; sprintf(acBuffer, "%hhu", p_u8Value); return addServiceTxt(p_hService, p_pcKey, acBuffer); } /* * MDNSResponder::addServiceTxt (int32_t) */ MDNSResponder::hMDNSTxt MDNSResponder::addServiceTxt(const MDNSResponder::hMDNSService p_hService, const char* p_pcKey, int32_t p_i32Value) { char acBuffer[32]; *acBuffer = 0; sprintf(acBuffer, "%i", p_i32Value); return addServiceTxt(p_hService, p_pcKey, acBuffer); } /* * MDNSResponder::addServiceTxt (int16_t) */ MDNSResponder::hMDNSTxt MDNSResponder::addServiceTxt(const MDNSResponder::hMDNSService p_hService, const char* p_pcKey, int16_t p_i16Value) { char acBuffer[16]; *acBuffer = 0; sprintf(acBuffer, "%hi", p_i16Value); return addServiceTxt(p_hService, p_pcKey, acBuffer); } /* * MDNSResponder::addServiceTxt (int8_t) */ MDNSResponder::hMDNSTxt MDNSResponder::addServiceTxt(const MDNSResponder::hMDNSService p_hService, const char* p_pcKey, int8_t p_i8Value) { char acBuffer[8]; *acBuffer = 0; sprintf(acBuffer, "%hhi", p_i8Value); return addServiceTxt(p_hService, p_pcKey, acBuffer); } /* * MDNSResponder::removeServiceTxt * * Remove a static service TXT item from a service. */ bool MDNSResponder::removeServiceTxt(const MDNSResponder::hMDNSService p_hService, const MDNSResponder::hMDNSTxt p_hTxt) { bool bResult = false; stcMDNSService* pService = _findService(p_hService); if (pService) { stcMDNSServiceTxt* pTxt = _findServiceTxt(pService, p_hTxt); if (pTxt) { bResult = _releaseServiceTxt(pService, pTxt); } } DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] removeServiceTxt: FAILED!\n")); } ); return bResult; } /* * MDNSResponder::removeServiceTxt */ bool MDNSResponder::removeServiceTxt(const MDNSResponder::hMDNSService p_hService, const char* p_pcKey) { bool bResult = false; stcMDNSService* pService = _findService(p_hService); if (pService) { stcMDNSServiceTxt* pTxt = _findServiceTxt(pService, p_pcKey); if (pTxt) { bResult = _releaseServiceTxt(pService, pTxt); } } DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] removeServiceTxt: FAILED for '%s'!\n"), (p_pcKey ?: "-")); } ); return bResult; } /* * MDNSResponder::removeServiceTxt */ bool MDNSResponder::removeServiceTxt(const char* p_pcName, const char* p_pcService, const char* p_pcProtocol, const char* p_pcKey) { bool bResult = false; stcMDNSService* pService = _findService((p_pcName ?: m_pcHostname), p_pcService, p_pcProtocol); if (pService) { stcMDNSServiceTxt* pTxt = _findServiceTxt(pService, p_pcKey); if (pTxt) { bResult = _releaseServiceTxt(pService, pTxt); } } return bResult; } /* * MDNSResponder::addServiceTxt (LEGACY) */ bool MDNSResponder::addServiceTxt(const char* p_pcService, const char* p_pcProtocol, const char* p_pcKey, const char* p_pcValue) { return (0 != _addServiceTxt(_findService(m_pcHostname, p_pcService, p_pcProtocol), p_pcKey, p_pcValue, false)); } /* * MDNSResponder::addServiceTxt (LEGACY) */ bool MDNSResponder::addServiceTxt(String p_strService, String p_strProtocol, String p_strKey, String p_strValue) { return (0 != _addServiceTxt(_findService(m_pcHostname, p_strService.c_str(), p_strProtocol.c_str()), p_strKey.c_str(), p_strValue.c_str(), false)); } /* * MDNSResponder::setDynamicServiceTxtCallback (global) * * Set a global callback for dynamic service TXT items. The callback is called, whenever * service TXT items are needed. * */ bool MDNSResponder::setDynamicServiceTxtCallback(MDNSResponder::MDNSDynamicServiceTxtCallbackFn p_fnCallback, void* p_pUserdata) { m_fnServiceTxtCallback = p_fnCallback; m_pServiceTxtCallbackUserdata = p_pUserdata; return true; } /* * MDNSResponder::setDynamicServiceTxtCallback (service specific) * * Set a service specific callback for dynamic service TXT items. The callback is called, whenever * service TXT items are needed for the given service. * */ bool MDNSResponder::setDynamicServiceTxtCallback(MDNSResponder::hMDNSService p_hService, MDNSResponder::MDNSDynamicServiceTxtCallbackFn p_fnCallback, void* p_pUserdata) { bool bResult = false; stcMDNSService* pService = _findService(p_hService); if (pService) { pService->m_fnTxtCallback = p_fnCallback; pService->m_pTxtCallbackUserdata = p_pUserdata; bResult = true; } DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] setDynamicServiceTxtCallback: FAILED!\n")); } ); return bResult; } /* * MDNSResponder::addDynamicServiceTxt */ MDNSResponder::hMDNSTxt MDNSResponder::addDynamicServiceTxt(MDNSResponder::hMDNSService p_hService, const char* p_pcKey, const char* p_pcValue) { //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] addDynamicServiceTxt (%s=%s)\n"), p_pcKey, p_pcValue);); hMDNSTxt hTxt = 0; stcMDNSService* pService = _findService(p_hService); if (pService) { hTxt = _addServiceTxt(pService, p_pcKey, p_pcValue, true); } DEBUG_EX_ERR(if (!hTxt) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] addDynamicServiceTxt: FAILED for '%s=%s'!\n"), (p_pcKey ?: "-"), (p_pcValue ?: "-")); } ); return hTxt; } /* * MDNSResponder::addDynamicServiceTxt (uint32_t) */ MDNSResponder::hMDNSTxt MDNSResponder::addDynamicServiceTxt(MDNSResponder::hMDNSService p_hService, const char* p_pcKey, uint32_t p_u32Value) { char acBuffer[32]; *acBuffer = 0; sprintf(acBuffer, "%u", p_u32Value); return addDynamicServiceTxt(p_hService, p_pcKey, acBuffer); } /* * MDNSResponder::addDynamicServiceTxt (uint16_t) */ MDNSResponder::hMDNSTxt MDNSResponder::addDynamicServiceTxt(MDNSResponder::hMDNSService p_hService, const char* p_pcKey, uint16_t p_u16Value) { char acBuffer[16]; *acBuffer = 0; sprintf(acBuffer, "%hu", p_u16Value); return addDynamicServiceTxt(p_hService, p_pcKey, acBuffer); } /* * MDNSResponder::addDynamicServiceTxt (uint8_t) */ MDNSResponder::hMDNSTxt MDNSResponder::addDynamicServiceTxt(MDNSResponder::hMDNSService p_hService, const char* p_pcKey, uint8_t p_u8Value) { char acBuffer[8]; *acBuffer = 0; sprintf(acBuffer, "%hhu", p_u8Value); return addDynamicServiceTxt(p_hService, p_pcKey, acBuffer); } /* * MDNSResponder::addDynamicServiceTxt (int32_t) */ MDNSResponder::hMDNSTxt MDNSResponder::addDynamicServiceTxt(MDNSResponder::hMDNSService p_hService, const char* p_pcKey, int32_t p_i32Value) { char acBuffer[32]; *acBuffer = 0; sprintf(acBuffer, "%i", p_i32Value); return addDynamicServiceTxt(p_hService, p_pcKey, acBuffer); } /* * MDNSResponder::addDynamicServiceTxt (int16_t) */ MDNSResponder::hMDNSTxt MDNSResponder::addDynamicServiceTxt(MDNSResponder::hMDNSService p_hService, const char* p_pcKey, int16_t p_i16Value) { char acBuffer[16]; *acBuffer = 0; sprintf(acBuffer, "%hi", p_i16Value); return addDynamicServiceTxt(p_hService, p_pcKey, acBuffer); } /* * MDNSResponder::addDynamicServiceTxt (int8_t) */ MDNSResponder::hMDNSTxt MDNSResponder::addDynamicServiceTxt(MDNSResponder::hMDNSService p_hService, const char* p_pcKey, int8_t p_i8Value) { char acBuffer[8]; *acBuffer = 0; sprintf(acBuffer, "%hhi", p_i8Value); return addDynamicServiceTxt(p_hService, p_pcKey, acBuffer); } /** * STATIC SERVICE QUERY (LEGACY) */ /* * MDNSResponder::queryService * * Perform a (blocking) static service query. * The arrived answers can be queried by calling: * - answerHostname (or 'hostname') * - answerIP (or 'IP') * - answerPort (or 'port') * */ uint32_t MDNSResponder::queryService(const char* p_pcService, const char* p_pcProtocol, const uint16_t p_u16Timeout /*= MDNS_QUERYSERVICES_WAIT_TIME*/) { DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] queryService '%s.%s'\n"), p_pcService, p_pcProtocol);); uint32_t u32Result = 0; stcMDNSServiceQuery* pServiceQuery = 0; if ((p_pcService) && (os_strlen(p_pcService)) && (p_pcProtocol) && (os_strlen(p_pcProtocol)) && (p_u16Timeout) && (_removeLegacyServiceQuery()) && ((pServiceQuery = _allocServiceQuery())) && (_buildDomainForService(p_pcService, p_pcProtocol, pServiceQuery->m_ServiceTypeDomain))) { pServiceQuery->m_bLegacyQuery = true; if (_sendMDNSServiceQuery(*pServiceQuery)) { // Wait for answers to arrive DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] queryService: Waiting %u ms for answers...\n"), p_u16Timeout);); delay(p_u16Timeout); // All answers should have arrived by now -> stop adding new answers pServiceQuery->m_bAwaitingAnswers = false; u32Result = pServiceQuery->answerCount(); } else { // FAILED to send query _removeServiceQuery(pServiceQuery); } } else { DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] queryService: INVALID input data!\n"), p_pcService, p_pcProtocol);); } return u32Result; } /* * MDNSResponder::removeQuery * * Remove the last static service query (and all answers). * */ bool MDNSResponder::removeQuery(void) { return _removeLegacyServiceQuery(); } /* * MDNSResponder::queryService (LEGACY) */ uint32_t MDNSResponder::queryService(String p_strService, String p_strProtocol) { return queryService(p_strService.c_str(), p_strProtocol.c_str()); } /* * MDNSResponder::answerHostname */ const char* MDNSResponder::answerHostname(const uint32_t p_u32AnswerIndex) { stcMDNSServiceQuery* pServiceQuery = _findLegacyServiceQuery(); stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); if ((pSQAnswer) && (pSQAnswer->m_HostDomain.m_u16NameLength) && (!pSQAnswer->m_pcHostDomain)) { char* pcHostDomain = pSQAnswer->allocHostDomain(pSQAnswer->m_HostDomain.c_strLength()); if (pcHostDomain) { pSQAnswer->m_HostDomain.c_str(pcHostDomain); } } return (pSQAnswer ? pSQAnswer->m_pcHostDomain : 0); } #ifdef MDNS_IP4_SUPPORT /* * MDNSResponder::answerIP */ IPAddress MDNSResponder::answerIP(const uint32_t p_u32AnswerIndex) { const stcMDNSServiceQuery* pServiceQuery = _findLegacyServiceQuery(); const stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); const stcMDNSServiceQuery::stcAnswer::stcIP4Address* pIP4Address = (((pSQAnswer) && (pSQAnswer->m_pIP4Addresses)) ? pSQAnswer->IP4AddressAtIndex(0) : 0); return (pIP4Address ? pIP4Address->m_IPAddress : IPAddress()); } #endif #ifdef MDNS_IP6_SUPPORT /* * MDNSResponder::answerIP6 */ IPAddress MDNSResponder::answerIP6(const uint32_t p_u32AnswerIndex) { const stcMDNSServiceQuery* pServiceQuery = _findLegacyServiceQuery(); const stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); const stcMDNSServiceQuery::stcAnswer::stcIP6Address* pIP6Address = (((pSQAnswer) && (pSQAnswer->m_pIP6Addresses)) ? pSQAnswer->IP6AddressAtIndex(0) : 0); return (pIP6Address ? pIP6Address->m_IPAddress : IP6Address()); } #endif /* * MDNSResponder::answerPort */ uint16_t MDNSResponder::answerPort(const uint32_t p_u32AnswerIndex) { const stcMDNSServiceQuery* pServiceQuery = _findLegacyServiceQuery(); const stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); return (pSQAnswer ? pSQAnswer->m_u16Port : 0); } /* * MDNSResponder::hostname (LEGACY) */ String MDNSResponder::hostname(const uint32_t p_u32AnswerIndex) { return String(answerHostname(p_u32AnswerIndex)); } /* * MDNSResponder::IP (LEGACY) */ IPAddress MDNSResponder::IP(const uint32_t p_u32AnswerIndex) { return answerIP(p_u32AnswerIndex); } /* * MDNSResponder::port (LEGACY) */ uint16_t MDNSResponder::port(const uint32_t p_u32AnswerIndex) { return answerPort(p_u32AnswerIndex); } /** * DYNAMIC SERVICE QUERY */ /* * MDNSResponder::installServiceQuery * * Add a dynamic service query and a corresponding callback to the MDNS responder. * The callback will be called for every answer update. * The answers can also be queried by calling: * - answerServiceDomain * - answerHostDomain * - answerIP4Address/answerIP6Address * - answerPort * - answerTxts * */ MDNSResponder::hMDNSServiceQuery MDNSResponder::installServiceQuery(const char* p_pcService, const char* p_pcProtocol, MDNSResponder::MDNSServiceQueryCallbackFn p_fnCallback, void* p_pUserdata) { hMDNSServiceQuery hResult = 0; stcMDNSServiceQuery* pServiceQuery = 0; if ((p_pcService) && (os_strlen(p_pcService)) && (p_pcProtocol) && (os_strlen(p_pcProtocol)) && (p_fnCallback) && ((pServiceQuery = _allocServiceQuery())) && (_buildDomainForService(p_pcService, p_pcProtocol, pServiceQuery->m_ServiceTypeDomain))) { pServiceQuery->m_fnCallback = p_fnCallback; pServiceQuery->m_pUserdata = p_pUserdata; pServiceQuery->m_bLegacyQuery = false; if (_sendMDNSServiceQuery(*pServiceQuery)) { hResult = (hMDNSServiceQuery)pServiceQuery; } else { _removeServiceQuery(pServiceQuery); } } DEBUG_EX_ERR(if (!hResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] installServiceQuery: FAILED for '%s.%s'!\n"), (p_pcService ?: "-"), (p_pcProtocol ?: "-")); } ); return hResult; } /* * MDNSResponder::removeServiceQuery * * Remove a dynamic service query (and all collected answers) from the MDNS responder * */ bool MDNSResponder::removeServiceQuery(MDNSResponder::hMDNSServiceQuery p_hServiceQuery) { stcMDNSServiceQuery* pServiceQuery = 0; bool bResult = (((pServiceQuery = _findServiceQuery(p_hServiceQuery))) && (_removeServiceQuery(pServiceQuery))); DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] removeServiceQuery: FAILED!\n")); } ); return bResult; } /* * MDNSResponder::answerCount */ uint32_t MDNSResponder::answerCount(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery) { stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); return (pServiceQuery ? pServiceQuery->answerCount() : 0); } /* * MDNSResponder::answerServiceDomain * * Returns the domain for the given service. * If not already existing, the string is allocated, filled and attached to the answer. * */ const char* MDNSResponder::answerServiceDomain(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, const uint32_t p_u32AnswerIndex) { stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); // Fill m_pcServiceDomain (if not already done) if ((pSQAnswer) && (pSQAnswer->m_ServiceDomain.m_u16NameLength) && (!pSQAnswer->m_pcServiceDomain)) { pSQAnswer->m_pcServiceDomain = pSQAnswer->allocServiceDomain(pSQAnswer->m_ServiceDomain.c_strLength()); if (pSQAnswer->m_pcServiceDomain) { pSQAnswer->m_ServiceDomain.c_str(pSQAnswer->m_pcServiceDomain); } } return (pSQAnswer ? pSQAnswer->m_pcServiceDomain : 0); } /* * MDNSResponder::hasAnswerHostDomain */ bool MDNSResponder::hasAnswerHostDomain(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, const uint32_t p_u32AnswerIndex) { stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); return ((pSQAnswer) && (pSQAnswer->m_u32ContentFlags & ServiceQueryAnswerType_HostDomainAndPort)); } /* * MDNSResponder::answerHostDomain * * Returns the host domain for the given service. * If not already existing, the string is allocated, filled and attached to the answer. * */ const char* MDNSResponder::answerHostDomain(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, const uint32_t p_u32AnswerIndex) { stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); // Fill m_pcHostDomain (if not already done) if ((pSQAnswer) && (pSQAnswer->m_HostDomain.m_u16NameLength) && (!pSQAnswer->m_pcHostDomain)) { pSQAnswer->m_pcHostDomain = pSQAnswer->allocHostDomain(pSQAnswer->m_HostDomain.c_strLength()); if (pSQAnswer->m_pcHostDomain) { pSQAnswer->m_HostDomain.c_str(pSQAnswer->m_pcHostDomain); } } return (pSQAnswer ? pSQAnswer->m_pcHostDomain : 0); } #ifdef MDNS_IP4_SUPPORT /* * MDNSResponder::hasAnswerIP4Address */ bool MDNSResponder::hasAnswerIP4Address(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, const uint32_t p_u32AnswerIndex) { stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); return ((pSQAnswer) && (pSQAnswer->m_u32ContentFlags & ServiceQueryAnswerType_IP4Address)); } /* * MDNSResponder::answerIP4AddressCount */ uint32_t MDNSResponder::answerIP4AddressCount(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, const uint32_t p_u32AnswerIndex) { stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); return (pSQAnswer ? pSQAnswer->IP4AddressCount() : 0); } /* * MDNSResponder::answerIP4Address */ IPAddress MDNSResponder::answerIP4Address(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, const uint32_t p_u32AnswerIndex, const uint32_t p_u32AddressIndex) { stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); stcMDNSServiceQuery::stcAnswer::stcIP4Address* pIP4Address = (pSQAnswer ? pSQAnswer->IP4AddressAtIndex(p_u32AddressIndex) : 0); return (pIP4Address ? pIP4Address->m_IPAddress : IPAddress()); } #endif #ifdef MDNS_IP6_SUPPORT /* * MDNSResponder::hasAnswerIP6Address */ bool MDNSResponder::hasAnswerIP6Address(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, const uint32_t p_u32AnswerIndex) { stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); return ((pSQAnswer) && (pSQAnswer->m_u32ContentFlags & ServiceQueryAnswerType_HostIP6Address)); } /* * MDNSResponder::answerIP6AddressCount */ uint32_t MDNSResponder::answerIP6AddressCount(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, const uint32_t p_u32AnswerIndex) { stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); return (pSQAnswer ? pSQAnswer->IP6AddressCount() : 0); } /* * MDNSResponder::answerIP6Address */ IPAddress MDNSResponder::answerIP6Address(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, const uint32_t p_u32AnswerIndex, const uint32_t p_u32AddressIndex) { stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); stcMDNSServiceQuery::stcAnswer::stcIP6Address* pIP6Address = (pSQAnswer ? pSQAnswer->IP6AddressAtIndex(p_u32AddressIndex) : 0); return (pIP6Address ? pIP6Address->m_IPAddress : IPAddress()); } #endif /* * MDNSResponder::hasAnswerPort */ bool MDNSResponder::hasAnswerPort(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, const uint32_t p_u32AnswerIndex) { stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); return ((pSQAnswer) && (pSQAnswer->m_u32ContentFlags & ServiceQueryAnswerType_HostDomainAndPort)); } /* * MDNSResponder::answerPort */ uint16_t MDNSResponder::answerPort(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, const uint32_t p_u32AnswerIndex) { stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); return (pSQAnswer ? pSQAnswer->m_u16Port : 0); } /* * MDNSResponder::hasAnswerTxts */ bool MDNSResponder::hasAnswerTxts(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, const uint32_t p_u32AnswerIndex) { stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); return ((pSQAnswer) && (pSQAnswer->m_u32ContentFlags & ServiceQueryAnswerType_Txts)); } /* * MDNSResponder::answerTxts * * Returns all TXT items for the given service as a ';'-separated string. * If not already existing; the string is alloced, filled and attached to the answer. * */ const char* MDNSResponder::answerTxts(const MDNSResponder::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) if ((pSQAnswer) && (pSQAnswer->m_Txts.m_pTxts) && (!pSQAnswer->m_pcTxts)) { pSQAnswer->m_pcTxts = pSQAnswer->allocTxts(pSQAnswer->m_Txts.c_strLength()); if (pSQAnswer->m_pcTxts) { pSQAnswer->m_Txts.c_str(pSQAnswer->m_pcTxts); } } return (pSQAnswer ? pSQAnswer->m_pcTxts : 0); } /* * PROBING */ /* * MDNSResponder::setProbeResultCallback * * Set a global callback for probe results. The callback is called, when probing * for the host domain (or a service domain, without specific probe result callback) * failes or succeedes. * In the case of failure, the domain name should be changed via 'setHostname' or 'setServiceName'. * When succeeded, the host or service domain will be announced by the MDNS responder. * */ bool MDNSResponder::setProbeResultCallback(MDNSResponder::MDNSProbeResultCallbackFn p_fnCallback, void* p_pUserdata) { m_HostProbeInformation.m_fnProbeResultCallback = p_fnCallback; m_HostProbeInformation.m_pProbeResultCallbackUserdata = p_pUserdata; return true; } /* * MDNSResponder::setServiceProbeResultCallback * * Set a service specific callback for probe results. The callback is called, when probing * for the service domain failes or succeedes. * In the case of failure, the service name should be changed via 'setServiceName'. * When succeeded, the service domain will be announced by the MDNS responder. * */ bool MDNSResponder::setServiceProbeResultCallback(const MDNSResponder::hMDNSService p_hService, MDNSResponder::MDNSProbeResultCallbackFn p_fnCallback, void* p_pUserdata) { bool bResult = false; stcMDNSService* pService = _findService(p_hService); if (pService) { pService->m_ProbeInformation.m_fnProbeResultCallback = p_fnCallback; pService->m_ProbeInformation.m_pProbeResultCallbackUserdata = p_pUserdata; bResult = true; } return bResult; } /* * MISC */ /* * MDNSResponder::notifyAPChange * * Should be called, whenever the AP for the MDNS responder changes. * A bit of this is caught by the event callbacks installed in the constructor. * */ bool MDNSResponder::notifyAPChange(void) { return _restart(); } /* * MDNSResponder::update * * Should be called in every 'loop'. * */ bool MDNSResponder::update(void) { return _process(true); } /* * MDNSResponder::announce * * Should be called, if the 'configuration' changes. Mainly this will be changes in the TXT items... */ bool MDNSResponder::announce(void) { return (_announce()); } /* * MDNSResponder::enableArduino * * Enable the OTA update service. * */ MDNSResponder::hMDNSService MDNSResponder::enableArduino(uint16_t p_u16Port, bool p_bAuthUpload /*= false*/) { hMDNSService hService = addService(0, "arduino", "tcp", p_u16Port); if (hService) { if ((!addServiceTxt(hService, "tcp_check", "no")) || (!addServiceTxt(hService, "ssh_upload", "no")) || (!addServiceTxt(hService, "board", STRINGIZE_VALUE_OF(ARDUINO_BOARD))) || (!addServiceTxt(hService, "auth_upload", (p_bAuthUpload) ? "yes" : "no"))) { removeService(hService); hService = 0; } } return hService; } } //namespace MDNSImplementation } //namespace esp8266