/* * LEAmDNS_Transfer.cpp * * License (MIT license): * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * */ extern "C" { #include "user_interface.h" } #include "LEAmDNS_lwIPdefs.h" #include "LEAmDNS_Priv.h" namespace esp8266 { /* * LEAmDNS */ namespace MDNSImplementation { /** * CONST STRINGS */ static const char* scpcLocal = "local"; static const char* scpcServices = "services"; static const char* scpcDNSSD = "dns-sd"; static const char* scpcUDP = "udp"; //static const char* scpcTCP = "tcp"; #ifdef MDNS_IP4_SUPPORT static const char* scpcReverseIP4Domain = "in-addr"; #endif #ifdef MDNS_IP6_SUPPORT static const char* scpcReverseIP6Domain = "ip6"; #endif static const char* scpcReverseTopDomain = "arpa"; /** * TRANSFER */ /** * SENDING */ /* * MDNSResponder::_sendMDNSMessage * * Unicast responses are prepared and sent directly to the querier. * Multicast responses or queries are transferred to _sendMDNSMessage_Multicast * * Any reply flags in installed services are removed at the end! * */ bool MDNSResponder::_sendMDNSMessage(MDNSResponder::stcMDNSSendParameter& p_rSendParameter) { bool bResult = true; if (p_rSendParameter.m_bResponse) { if (p_rSendParameter.m_bUnicast) { // Unicast response -> Send to querier DEBUG_EX_ERR(if (!m_pUDPContext->getRemoteAddress()) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _sendMDNSMessage: MISSING remote address for response!\n")); }); IPAddress ipRemote; ipRemote = m_pUDPContext->getRemoteAddress(); bResult = ((_prepareMDNSMessage(p_rSendParameter, _getResponseMulticastInterface(SOFTAP_MODE | STATION_MODE))) && (m_pUDPContext->send(ipRemote, m_pUDPContext->getRemotePort()))); } else { // Multicast response -> Send via the same network interface, that received the query bResult = _sendMDNSMessage_Multicast(p_rSendParameter, (SOFTAP_MODE | STATION_MODE)); } } else { // Multicast query -> Send by all available network interfaces const int caiWiFiOpModes[2] = { SOFTAP_MODE, STATION_MODE }; for (int iInterfaceId=0; ((bResult) && (iInterfaceId<=1)); ++iInterfaceId) { if (wifi_get_opmode() & caiWiFiOpModes[iInterfaceId]) { bResult = _sendMDNSMessage_Multicast(p_rSendParameter, caiWiFiOpModes[iInterfaceId]); } } } // Finally clear service reply masks for (stcMDNSService* pService=m_pServices; pService; pService=pService->m_pNext) { pService->m_u8ReplyMask = 0; } DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _sendMDNSMessage: FAILED!\n")); }); return bResult; } /* * MDNSResponder::_sendMDNSMessage_Multicast * * Fills the UDP output buffer (via _prepareMDNSMessage) and sends the buffer * via the selected WiFi interface (Station or AP) */ bool MDNSResponder::_sendMDNSMessage_Multicast(MDNSResponder::stcMDNSSendParameter& p_rSendParameter, int p_iWiFiOpMode) { bool bResult = false; IPAddress fromIPAddress; fromIPAddress = _getResponseMulticastInterface(p_iWiFiOpMode); m_pUDPContext->setMulticastInterface(fromIPAddress); #ifdef MDNS_IP4_SUPPORT IPAddress toMulticastAddress(DNS_MQUERY_IPV4_GROUP_INIT); #endif #ifdef MDNS_IP6_SUPPORT //TODO: set multicast address IPAddress toMulticastAddress(DNS_MQUERY_IPV6_GROUP_INIT); #endif DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _sendMDNSMessage_Multicast: Will send to '%s'.\n"), toMulticastAddress.toString().c_str());); bResult = ((_prepareMDNSMessage(p_rSendParameter, fromIPAddress)) && (m_pUDPContext->send(toMulticastAddress, DNS_MQUERY_PORT))); DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _sendMDNSMessage_Multicast: FAILED!\n")); }); return bResult; } /* * MDNSResponder::_prepareMDNSMessage * * The MDNS message is composed in a two-step process. * In the first loop 'only' the header informations (mainly number of answers) are collected, * while in the seconds loop, the header and all queries and answers are written to the UDP * output buffer. * */ bool MDNSResponder::_prepareMDNSMessage(MDNSResponder::stcMDNSSendParameter& p_rSendParameter, IPAddress p_IPAddress) { DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage\n"));); bool bResult = true; // Prepare header; count answers stcMDNS_MsgHeader msgHeader(0, p_rSendParameter.m_bResponse, 0, p_rSendParameter.m_bAuthorative); // If this is a response, the answers are anwers, // else this is a query or probe and the answers go into auth section uint16_t& ru16Answers = (p_rSendParameter.m_bResponse ? msgHeader.m_u16ANCount : msgHeader.m_u16NSCount); /** * enuSequence */ enum enuSequence { Sequence_Count = 0, Sequence_Send = 1 }; // Two step sequence: 'Count' and 'Send' for (uint32_t sequence=Sequence_Count; ((bResult) && (sequence<=Sequence_Send)); ++sequence) { DEBUG_EX_INFO( if (Sequence_Send == sequence) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: ID:%u QR:%u OP:%u AA:%u TC:%u RD:%u RA:%u R:%u QD:%u AN:%u NS:%u AR:%u\n"), (unsigned)msgHeader.m_u16ID, (unsigned)msgHeader.m_1bQR, (unsigned)msgHeader.m_4bOpcode, (unsigned)msgHeader.m_1bAA, (unsigned)msgHeader.m_1bTC, (unsigned)msgHeader.m_1bRD, (unsigned)msgHeader.m_1bRA, (unsigned)msgHeader.m_4bRCode, (unsigned)msgHeader.m_u16QDCount, (unsigned)msgHeader.m_u16ANCount, (unsigned)msgHeader.m_u16NSCount, (unsigned)msgHeader.m_u16ARCount); } ); // Count/send // Header bResult = ((Sequence_Count == sequence) ? true : _writeMDNSMsgHeader(msgHeader, p_rSendParameter)); DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSMsgHeader FAILED!\n"));); // Questions for (stcMDNS_RRQuestion* pQuestion=p_rSendParameter.m_pQuestions; ((bResult) && (pQuestion)); pQuestion=pQuestion->m_pNext) { ((Sequence_Count == sequence) ? ++msgHeader.m_u16QDCount : (bResult = _writeMDNSQuestion(*pQuestion, p_rSendParameter))); DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSQuestion FAILED!\n"));); } // Answers and authorative answers #ifdef MDNS_IP4_SUPPORT if ((bResult) && (p_rSendParameter.m_u8HostReplyMask & ContentFlag_A)) { ((Sequence_Count == sequence) ? ++ru16Answers : (bResult = _writeMDNSAnswer_A(p_IPAddress, p_rSendParameter))); DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_A(A) FAILED!\n"));); } if ((bResult) && (p_rSendParameter.m_u8HostReplyMask & ContentFlag_PTR_IP4)) { ((Sequence_Count == sequence) ? ++ru16Answers : (bResult = _writeMDNSAnswer_PTR_IP4(p_IPAddress, p_rSendParameter))); DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_PTR_IP4 FAILED!\n"));); } #endif #ifdef MDNS_IP6_SUPPORT if ((bResult) && (p_rSendParameter.m_u8HostReplyMask & ContentFlag_AAAA)) { ((Sequence_Count == sequence) ? ++ru16Answers : (bResult = _writeMDNSAnswer_AAAA(p_IPAddress, p_rSendParameter))); DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_AAAA(A) FAILED!\n"));); } if ((bResult) && (p_rSendParameter.m_u8HostReplyMask & ContentFlag_PTR_IP6)) { ((Sequence_Count == sequence) ? ++ru16Answers : (bResult = _writeMDNSAnswer_PTR_IP6(p_IPAddress, p_rSendParameter))); DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_PTR_IP6 FAILED!\n"));); } #endif for (stcMDNSService* pService=m_pServices; ((bResult) && (pService)); pService=pService->m_pNext) { if ((bResult) && (pService->m_u8ReplyMask & ContentFlag_PTR_TYPE)) { ((Sequence_Count == sequence) ? ++ru16Answers : (bResult = _writeMDNSAnswer_PTR_TYPE(*pService, p_rSendParameter))); DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_PTR_TYPE FAILED!\n"));); } if ((bResult) && (pService->m_u8ReplyMask & ContentFlag_PTR_NAME)) { ((Sequence_Count == sequence) ? ++ru16Answers : (bResult = _writeMDNSAnswer_PTR_NAME(*pService, p_rSendParameter))); DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_PTR_NAME FAILED!\n"));); } if ((bResult) && (pService->m_u8ReplyMask & ContentFlag_SRV)) { ((Sequence_Count == sequence) ? ++ru16Answers : (bResult = _writeMDNSAnswer_SRV(*pService, p_rSendParameter))); DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_SRV(A) FAILED!\n"));); } if ((bResult) && (pService->m_u8ReplyMask & ContentFlag_TXT)) { ((Sequence_Count == sequence) ? ++ru16Answers : (bResult = _writeMDNSAnswer_TXT(*pService, p_rSendParameter))); DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_TXT(A) FAILED!\n"));); } } // for services // Additional answers #ifdef MDNS_IP4_SUPPORT bool bNeedsAdditionalAnswerA = false; #endif #ifdef MDNS_IP6_SUPPORT bool bNeedsAdditionalAnswerAAAA = false; #endif for (stcMDNSService* pService=m_pServices; ((bResult) && (pService)); pService=pService->m_pNext) { if ((bResult) && (pService->m_u8ReplyMask & ContentFlag_PTR_NAME) && // If PTR_NAME is requested, AND (!(pService->m_u8ReplyMask & ContentFlag_SRV))) { // NOT SRV -> add SRV as additional answer ((Sequence_Count == sequence) ? ++msgHeader.m_u16ARCount : (bResult = _writeMDNSAnswer_SRV(*pService, p_rSendParameter))); DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_SRV(B) FAILED!\n"));); } if ((bResult) && (pService->m_u8ReplyMask & ContentFlag_PTR_NAME) && // If PTR_NAME is requested, AND (!(pService->m_u8ReplyMask & ContentFlag_TXT))) { // NOT TXT -> add TXT as additional answer ((Sequence_Count == sequence) ? ++msgHeader.m_u16ARCount : (bResult = _writeMDNSAnswer_TXT(*pService, p_rSendParameter))); DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_TXT(B) FAILED!\n"));); } if ((pService->m_u8ReplyMask & (ContentFlag_PTR_NAME | ContentFlag_SRV)) || // If service instance name or SRV OR (p_rSendParameter.m_u8HostReplyMask & (ContentFlag_A | ContentFlag_AAAA))) { // any host IP address is requested #ifdef MDNS_IP4_SUPPORT if ((bResult) && (!(p_rSendParameter.m_u8HostReplyMask & ContentFlag_A))) { // Add IP4 address bNeedsAdditionalAnswerA = true; } #endif #ifdef MDNS_IP6_SUPPORT if ((bResult) && (!(p_rSendParameter.m_u8HostReplyMask & ContentFlag_AAAA))) { // Add IP6 address bNeedsAdditionalAnswerAAAA = true; } #endif } } // for services // Answer A needed? #ifdef MDNS_IP4_SUPPORT if ((bResult) && (bNeedsAdditionalAnswerA)) { ((Sequence_Count == sequence) ? ++msgHeader.m_u16ARCount : (bResult = _writeMDNSAnswer_A(p_IPAddress, p_rSendParameter))); DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_A(B) FAILED!\n"));); } #endif #ifdef MDNS_IP6_SUPPORT // Answer AAAA needed? if ((bResult) && (bNeedsAdditionalAnswerAAAA)) { ((Sequence_Count == sequence) ? ++msgHeader.m_u16ARCount : (bResult = _writeMDNSAnswer_AAAA(p_IPAddress, p_rSendParameter))); DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_AAAA(B) FAILED!\n"));); } #endif DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: Loop %i FAILED!\n"), sequence);); } // for sequence DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: FAILED!\n"));); return bResult; } /* * MDNSResponder::_sendMDNSServiceQuery * * Creates and sends a PTR query for the given service domain. * */ bool MDNSResponder::_sendMDNSServiceQuery(const MDNSResponder::stcMDNSServiceQuery& p_ServiceQuery) { return _sendMDNSQuery(p_ServiceQuery.m_ServiceTypeDomain, DNS_RRTYPE_PTR); } /* * MDNSResponder::_sendMDNSQuery * * Creates and sends a query for the given domain and query type. * */ bool MDNSResponder::_sendMDNSQuery(const MDNSResponder::stcMDNS_RRDomain& p_QueryDomain, uint16_t p_u16QueryType, stcMDNSServiceQuery::stcAnswer* p_pKnownAnswers /*= 0*/) { bool bResult = false; stcMDNSSendParameter sendParameter; if (0 != ((sendParameter.m_pQuestions = new stcMDNS_RRQuestion))) { sendParameter.m_pQuestions->m_Header.m_Domain = p_QueryDomain; sendParameter.m_pQuestions->m_Header.m_Attributes.m_u16Type = p_u16QueryType; // It seems, that some mDNS implementations don't support 'unicast response' questions... sendParameter.m_pQuestions->m_Header.m_Attributes.m_u16Class = (/*0x8000 |*/ DNS_RRCLASS_IN); // /*Unicast &*/ INternet // TODO: Add knwon answer to the query (void)p_pKnownAnswers; bResult = _sendMDNSMessage(sendParameter); } // else: FAILED to alloc question DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _sendMDNSQuery: FAILED to alloc question!\n"));); return bResult; } /* * MDNSResponder::_getResponseMulticastInterface * * Selects the appropriate interface for responses. * If AP mode is enabled and the remote contact is in the APs local net, then the * AP interface is used to send the response. * Otherwise the Station interface (if available) is used. * */ IPAddress MDNSResponder::_getResponseMulticastInterface(int p_iWiFiOpModes) const { ip_info IPInfo_Local; bool bFoundMatch = false; if ((p_iWiFiOpModes & SOFTAP_MODE) && (wifi_get_opmode() & SOFTAP_MODE)) { //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _getResponseMulticastInterface: SOFTAP_MODE\n"));); // Get remote IP address IPAddress IP_Remote; IP_Remote = m_pUDPContext->getRemoteAddress(); // Get local (AP) IP address wifi_get_ip_info(SOFTAP_IF, &IPInfo_Local); if ((IPInfo_Local.ip.addr) && // Has local AP IP address AND (ip4_addr_netcmp(ip_2_ip4((const ip_addr_t*)IP_Remote), &IPInfo_Local.ip, &IPInfo_Local.netmask))) { // Remote address is in the same subnet as the AP bFoundMatch = true; } } if ((!bFoundMatch) && (p_iWiFiOpModes & STATION_MODE) && (wifi_get_opmode() & STATION_MODE)) { //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _getResponseMulticastInterface: STATION_MODE\n"));); // Get local (STATION) IP address wifi_get_ip_info(STATION_IF, &IPInfo_Local); } //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _getResponseMulticastInterface(%i): %s\n"), p_iWiFiOpModes, IPAddress(IPInfo_Local.ip).toString().c_str());); return IPAddress(IPInfo_Local.ip); } /** * HELPERS */ /** * RESOURCE RECORDS */ /* * MDNSResponder::_readRRQuestion * * Reads a question (eg. MyESP._http._tcp.local ANY IN) from the UPD input buffer. * */ bool MDNSResponder::_readRRQuestion(MDNSResponder::stcMDNS_RRQuestion& p_rRRQuestion) { DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRQuestion\n"));); bool bResult = false; if ((bResult = _readRRHeader(p_rRRQuestion.m_Header))) { // Extract unicast flag from class field p_rRRQuestion.m_bUnicast = (p_rRRQuestion.m_Header.m_Attributes.m_u16Class & 0x8000); p_rRRQuestion.m_Header.m_Attributes.m_u16Class &= (~0x8000); DEBUG_EX_INFO( DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRQuestion ")); _printRRDomain(p_rRRQuestion.m_Header.m_Domain); DEBUG_OUTPUT.printf_P(PSTR(" Type:0x%04X Class:0x%04X %s\n"), (unsigned)p_rRRQuestion.m_Header.m_Attributes.m_u16Type, (unsigned)p_rRRQuestion.m_Header.m_Attributes.m_u16Class, (p_rRRQuestion.m_bUnicast ? "Unicast" : "Multicast")); ); } DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRQuestion: FAILED!\n")); }); return bResult; } /* * MDNSResponder::_readRRAnswer * * Reads an answer (eg. _http._tcp.local PTR OP TTL MyESP._http._tcp.local) * from the UDP input buffer. * After reading the domain and type info, the further processing of the answer * is transferred the answer specific reading functions. * Unknown answer types are processed by the generic answer reader (to remove them * from the input buffer). * */ bool MDNSResponder::_readRRAnswer(MDNSResponder::stcMDNS_RRAnswer*& p_rpRRAnswer) { //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswer\n"));); bool bResult = false; stcMDNS_RRHeader header; uint32_t u32TTL; uint16_t u16RDLength; if ((_readRRHeader(header)) && (_udpRead32(u32TTL)) && (_udpRead16(u16RDLength))) { /*DEBUG_EX_INFO( DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswer: Reading 0x%04X answer (class:0x%04X, TTL:%u, RDLength:%u) for "), header.m_Attributes.m_u16Type, header.m_Attributes.m_u16Class, u32TTL, u16RDLength); _printRRDomain(header.m_Domain); DEBUG_OUTPUT.printf_P(PSTR("\n")); );*/ switch (header.m_Attributes.m_u16Type & (~0x8000)) { // Topmost bit might carry 'cache flush' flag #ifdef MDNS_IP4_SUPPORT case DNS_RRTYPE_A: p_rpRRAnswer = new stcMDNS_RRAnswerA(header, u32TTL); bResult = _readRRAnswerA(*(stcMDNS_RRAnswerA*&)p_rpRRAnswer, u16RDLength); break; #endif case DNS_RRTYPE_PTR: p_rpRRAnswer = new stcMDNS_RRAnswerPTR(header, u32TTL); bResult = _readRRAnswerPTR(*(stcMDNS_RRAnswerPTR*&)p_rpRRAnswer, u16RDLength); break; case DNS_RRTYPE_TXT: p_rpRRAnswer = new stcMDNS_RRAnswerTXT(header, u32TTL); bResult = _readRRAnswerTXT(*(stcMDNS_RRAnswerTXT*&)p_rpRRAnswer, u16RDLength); break; #ifdef MDNS_IP6_SUPPORT case DNS_RRTYPE_AAAA: p_rpRRAnswer = new stcMDNS_RRAnswerAAAA(header, u32TTL); bResult = _readRRAnswerAAAA(*(stcMDNS_RRAnswerAAAA*&)p_rpRRAnswer, u16RDLength); break; #endif case DNS_RRTYPE_SRV: p_rpRRAnswer = new stcMDNS_RRAnswerSRV(header, u32TTL); bResult = _readRRAnswerSRV(*(stcMDNS_RRAnswerSRV*&)p_rpRRAnswer, u16RDLength); break; default: p_rpRRAnswer = new stcMDNS_RRAnswerGeneric(header, u32TTL); bResult = _readRRAnswerGeneric(*(stcMDNS_RRAnswerGeneric*&)p_rpRRAnswer, u16RDLength); break; } DEBUG_EX_INFO( if ((bResult) && (p_rpRRAnswer)) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswer: ")); _printRRDomain(p_rpRRAnswer->m_Header.m_Domain); DEBUG_OUTPUT.printf_P(PSTR(" Type:0x%04X Class:0x%04X TTL:%u, RDLength:%u "), p_rpRRAnswer->m_Header.m_Attributes.m_u16Type, p_rpRRAnswer->m_Header.m_Attributes.m_u16Class, p_rpRRAnswer->m_u32TTL, u16RDLength); switch (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"), ((stcMDNS_RRAnswerA*&)p_rpRRAnswer)->m_IPAddress.toString().c_str()); break; #endif case DNS_RRTYPE_PTR: DEBUG_OUTPUT.printf_P(PSTR("PTR ")); _printRRDomain(((stcMDNS_RRAnswerPTR*&)p_rpRRAnswer)->m_PTRDomain); break; case DNS_RRTYPE_TXT: { size_t stTxtLength = ((stcMDNS_RRAnswerTXT*&)p_rpRRAnswer)->m_Txts.c_strLength(); char* pTxts = new char[stTxtLength]; if (pTxts) { ((stcMDNS_RRAnswerTXT*&)p_rpRRAnswer)->m_Txts.c_str(pTxts); DEBUG_OUTPUT.printf_P(PSTR("TXT(%u) %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 "), ((stcMDNS_RRAnswerSRV*&)p_rpRRAnswer)->m_u16Port); _printRRDomain(((stcMDNS_RRAnswerSRV*&)p_rpRRAnswer)->m_SRVDomain); break; default: DEBUG_OUTPUT.printf_P(PSTR("generic ")); break; } DEBUG_OUTPUT.printf_P(PSTR("\n")); } else { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswer: FAILED to read specific answer of type 0x%04X!\n"), p_rpRRAnswer->m_Header.m_Attributes.m_u16Type); } ); // DEBUG_EX_INFO } DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswer: FAILED!\n"));); return bResult; } #ifdef MDNS_IP4_SUPPORT /* * MDNSResponder::_readRRAnswerA */ bool MDNSResponder::_readRRAnswerA(MDNSResponder::stcMDNS_RRAnswerA& p_rRRAnswerA, uint16_t p_u16RDLength) { uint32_t u32IP4Address; bool bResult = ((MDNS_IP4_SIZE == p_u16RDLength) && (_udpReadBuffer((unsigned char*)&u32IP4Address, MDNS_IP4_SIZE)) && ((p_rRRAnswerA.m_IPAddress = IPAddress(u32IP4Address)))); DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswerA: FAILED!\n"));); return bResult; } #endif /* * MDNSResponder::_readRRAnswerPTR */ bool MDNSResponder::_readRRAnswerPTR(MDNSResponder::stcMDNS_RRAnswerPTR& p_rRRAnswerPTR, uint16_t p_u16RDLength) { bool bResult = ((p_u16RDLength) && (_readRRDomain(p_rRRAnswerPTR.m_PTRDomain))); DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswerPTR: FAILED!\n"));); return bResult; } /* * MDNSResponder::_readRRAnswerTXT * * Read TXT items from a buffer like 4c#=15ff=20 */ bool MDNSResponder::_readRRAnswerTXT(MDNSResponder::stcMDNS_RRAnswerTXT& p_rRRAnswerTXT, uint16_t p_u16RDLength) { DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswerTXT: RDLength:%u\n"), p_u16RDLength);); bool bResult = true; p_rRRAnswerTXT.clear(); if (p_u16RDLength) { bResult = false; unsigned char* pucBuffer = new unsigned char[p_u16RDLength]; if (pucBuffer) { if (_udpReadBuffer(pucBuffer, p_u16RDLength)) { bResult = true; const unsigned char* pucCursor = pucBuffer; while ((pucCursor < (pucBuffer + p_u16RDLength)) && (bResult)) { bResult = false; stcMDNSServiceTxt* pTxt = 0; unsigned char ucLength = *pucCursor++; // Length of the next txt item if (ucLength) { DEBUG_EX_INFO( static char sacBuffer[64]; *sacBuffer = 0; uint8_t u8MaxLength = ((ucLength > (sizeof(sacBuffer) - 1)) ? (sizeof(sacBuffer) - 1) : ucLength); os_strncpy(sacBuffer, (const char*)pucCursor, u8MaxLength); sacBuffer[u8MaxLength] = 0; DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswerTXT: Item(%u): %s\n"), ucLength, sacBuffer); ); unsigned char* pucEqualSign = (unsigned char*)os_strchr((const char*)pucCursor, '='); // Position of the '=' sign unsigned char ucKeyLength; if ((pucEqualSign) && ((ucKeyLength = (pucEqualSign - pucCursor)))) { unsigned char ucValueLength = (ucLength - (pucEqualSign - pucCursor + 1)); bResult = (((pTxt = new stcMDNSServiceTxt)) && (pTxt->setKey((const char*)pucCursor, ucKeyLength)) && (pTxt->setValue((const char*)(pucEqualSign + 1), ucValueLength))); } else { DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswerTXT: INVALID TXT format (No '=')!\n"));); } pucCursor += ucLength; } else { // no/zero length TXT DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswerTXT: TXT answer contains no items.\n"));); bResult = true; } if ((bResult) && (pTxt)) { // Everythings fine so far // Link TXT item to answer TXTs pTxt->m_pNext = p_rRRAnswerTXT.m_Txts.m_pTxts; p_rRRAnswerTXT.m_Txts.m_pTxts = pTxt; } else { // At least no TXT (migth be OK, if length was 0) OR an error if (!bResult) { DEBUG_EX_ERR( DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswerTXT: FAILED to read TXT item!\n")); DEBUG_OUTPUT.printf_P(PSTR("RData dump:\n")); _udpDump((m_pUDPContext->tell() - p_u16RDLength), p_u16RDLength); DEBUG_OUTPUT.printf_P(PSTR("\n")); ); } if (pTxt) { delete pTxt; pTxt = 0; } p_rRRAnswerTXT.clear(); } } // while DEBUG_EX_ERR( if (!bResult) { // Some failure DEBUG_OUTPUT.printf_P(PSTR("RData dump:\n")); _udpDump((m_pUDPContext->tell() - p_u16RDLength), p_u16RDLength); DEBUG_OUTPUT.printf_P(PSTR("\n")); } ); } else { DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswerTXT: FAILED to read TXT content!\n"));); } // Clean up delete[] pucBuffer; } else { DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswerTXT: FAILED to alloc buffer for TXT content!\n"));); } } else { DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswerTXT: WARNING! No content!\n"));); } DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswerTXT: FAILED!\n"));); return bResult; } #ifdef MDNS_IP6_SUPPORT bool MDNSResponder::_readRRAnswerAAAA(MDNSResponder::stcMDNS_RRAnswerAAAA& p_rRRAnswerAAAA, uint16_t p_u16RDLength) { bool bResult = false; // TODO: Implement return bResult; } #endif /* * MDNSResponder::_readRRAnswerSRV */ bool MDNSResponder::_readRRAnswerSRV(MDNSResponder::stcMDNS_RRAnswerSRV& p_rRRAnswerSRV, uint16_t p_u16RDLength) { bool bResult = (((3 * sizeof(uint16_t)) < p_u16RDLength) && (_udpRead16(p_rRRAnswerSRV.m_u16Priority)) && (_udpRead16(p_rRRAnswerSRV.m_u16Weight)) && (_udpRead16(p_rRRAnswerSRV.m_u16Port)) && (_readRRDomain(p_rRRAnswerSRV.m_SRVDomain))); DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswerSRV: FAILED!\n"));); return bResult; } /* * MDNSResponder::_readRRAnswerGeneric */ bool MDNSResponder::_readRRAnswerGeneric(MDNSResponder::stcMDNS_RRAnswerGeneric& p_rRRAnswerGeneric, uint16_t p_u16RDLength) { bool bResult = (0 == p_u16RDLength); p_rRRAnswerGeneric.clear(); if (((p_rRRAnswerGeneric.m_u16RDLength = p_u16RDLength)) && ((p_rRRAnswerGeneric.m_pu8RDData = new unsigned char[p_rRRAnswerGeneric.m_u16RDLength]))) { bResult = _udpReadBuffer(p_rRRAnswerGeneric.m_pu8RDData, p_rRRAnswerGeneric.m_u16RDLength); } DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswerGeneric: FAILED!\n"));); return bResult; } /* * MDNSResponder::_readRRHeader */ bool MDNSResponder::_readRRHeader(MDNSResponder::stcMDNS_RRHeader& p_rRRHeader) { //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRHeader\n"));); bool bResult = ((_readRRDomain(p_rRRHeader.m_Domain)) && (_readRRAttributes(p_rRRHeader.m_Attributes))); DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRHeader: FAILED!\n"));); return bResult; } /* * MDNSResponder::_readRRDomain * * Reads a (maybe multilevel compressed) domain from the UDP input buffer. * */ bool MDNSResponder::_readRRDomain(MDNSResponder::stcMDNS_RRDomain& p_rRRDomain) { //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRDomain\n"));); bool bResult = ((p_rRRDomain.clear()) && (_readRRDomain_Loop(p_rRRDomain, 0))); DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRDomain: FAILED!\n"));); return bResult; } /* * MDNSResponder::_readRRDomain_Loop * * Reads a domain from the UDP input buffer. For every compression level, the functions * calls itself recursively. To avoid endless recursion because of malformed MDNS records, * the maximum recursion depth is set by MDNS_DOMAIN_MAX_REDIRCTION. * */ bool MDNSResponder::_readRRDomain_Loop(MDNSResponder::stcMDNS_RRDomain& p_rRRDomain, uint8_t p_u8Depth) { //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRDomain_Loop(%u)\n"), p_u8Depth);); bool bResult = false; if (MDNS_DOMAIN_MAX_REDIRCTION >= p_u8Depth) { bResult = true; uint8_t u8Len = 0; do { //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRDomain_Loop(%u): Offset:%u p0:%02x\n"), p_u8Depth, m_pUDPContext->tell(), m_pUDPContext->peek());); _udpRead8(u8Len); if (u8Len & MDNS_DOMAIN_COMPRESS_MARK) { // Compressed label(s) uint16_t u16Offset = ((u8Len & ~MDNS_DOMAIN_COMPRESS_MARK) << 8); // Implicit BE to LE conversion! _udpRead8(u8Len); u16Offset |= u8Len; if (m_pUDPContext->isValidOffset(u16Offset)) { size_t stCurrentPosition = m_pUDPContext->tell(); // Prepare return from recursion //DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRDomain_Loop(%u): Redirecting from %u to %u!\n"), p_u8Depth, stCurrentPosition, u16Offset);); m_pUDPContext->seek(u16Offset); if (_readRRDomain_Loop(p_rRRDomain, p_u8Depth + 1)) { // Do recursion //DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRDomain_Loop(%u): Succeeded to read redirected label! Returning to %u\n"), p_u8Depth, stCurrentPosition);); m_pUDPContext->seek(stCurrentPosition); // Restore after recursion } else { DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRDomain_Loop(%u): FAILED to read redirected label!\n"), p_u8Depth);); bResult = false; } } else { DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRDomain_Loop(%u): INVALID offset in redirection!\n"), p_u8Depth);); bResult = false; } break; } else { // Normal (uncompressed) label (maybe '\0' only) if (MDNS_DOMAIN_MAXLENGTH > (p_rRRDomain.m_u16NameLength + u8Len)) { // Add length byte p_rRRDomain.m_acName[p_rRRDomain.m_u16NameLength] = u8Len; ++(p_rRRDomain.m_u16NameLength); if (u8Len) { // Add name if ((bResult = _udpReadBuffer((unsigned char*)&(p_rRRDomain.m_acName[p_rRRDomain.m_u16NameLength]), u8Len))) { /*DEBUG_EX_INFO( p_rRRDomain.m_acName[p_rRRDomain.m_u16NameLength + u8Len] = 0; // Closing '\0' for printing DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRDomain_Loop(%u): Domain label (%u): %s\n"), p_u8Depth, (unsigned)(p_rRRDomain.m_acName[p_rRRDomain.m_u16NameLength - 1]), &(p_rRRDomain.m_acName[p_rRRDomain.m_u16NameLength])); );*/ p_rRRDomain.m_u16NameLength += u8Len; } } //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRDomain_Loop(2) offset:%u p0:%x\n"), m_pUDPContext->tell(), m_pUDPContext->peek());); } else { DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRDomain_Loop(%u): ERROR! Domain name too long (%u + %u)!\n"), p_u8Depth, p_rRRDomain.m_u16NameLength, u8Len);); bResult = false; break; } } } while ((bResult) && (0 != u8Len)); } else { DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRDomain_Loop(%u): ERROR! Too many redirections!\n"), p_u8Depth);); } return bResult; } /* * MDNSResponder::_readRRAttributes */ bool MDNSResponder::_readRRAttributes(MDNSResponder::stcMDNS_RRAttributes& p_rRRAttributes) { //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAttributes\n"));); bool bResult = ((_udpRead16(p_rRRAttributes.m_u16Type)) && (_udpRead16(p_rRRAttributes.m_u16Class))); DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAttributes: FAILED!\n"));); return bResult; } /* * DOMAIN NAMES */ /* * MDNSResponder::_buildDomainForHost * * Builds a MDNS host domain (eg. esp8266.local) for the given hostname. * */ bool MDNSResponder::_buildDomainForHost(const char* p_pcHostname, MDNSResponder::stcMDNS_RRDomain& p_rHostDomain) const { p_rHostDomain.clear(); bool bResult = ((p_pcHostname) && (*p_pcHostname) && (p_rHostDomain.addLabel(p_pcHostname)) && (p_rHostDomain.addLabel(scpcLocal)) && (p_rHostDomain.addLabel(0))); DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _buildDomainForHost: FAILED!\n"));); return bResult; } /* * MDNSResponder::_buildDomainForDNSSD * * Builds the '_services._dns-sd._udp.local' domain. * Used while detecting generic service enum question (DNS-SD) and answering these questions. * */ bool MDNSResponder::_buildDomainForDNSSD(MDNSResponder::stcMDNS_RRDomain& p_rDNSSDDomain) const { p_rDNSSDDomain.clear(); bool bResult = ((p_rDNSSDDomain.addLabel(scpcServices, true)) && (p_rDNSSDDomain.addLabel(scpcDNSSD, true)) && (p_rDNSSDDomain.addLabel(scpcUDP, true)) && (p_rDNSSDDomain.addLabel(scpcLocal)) && (p_rDNSSDDomain.addLabel(0))); DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _buildDomainForDNSSD: FAILED!\n"));); return bResult; } /* * MDNSResponder::_buildDomainForService * * Builds the domain for the given service (eg. _http._tcp.local or * MyESP._http._tcp.local (if p_bIncludeName is set)). * */ bool MDNSResponder::_buildDomainForService(const MDNSResponder::stcMDNSService& p_Service, bool p_bIncludeName, MDNSResponder::stcMDNS_RRDomain& p_rServiceDomain) const { p_rServiceDomain.clear(); bool bResult = (((!p_bIncludeName) || (p_rServiceDomain.addLabel(p_Service.m_pcName))) && (p_rServiceDomain.addLabel(p_Service.m_pcService, true)) && (p_rServiceDomain.addLabel(p_Service.m_pcProtocol, true)) && (p_rServiceDomain.addLabel(scpcLocal)) && (p_rServiceDomain.addLabel(0))); DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _buildDomainForService: FAILED!\n"));); return bResult; } /* * MDNSResponder::_buildDomainForService * * Builds the domain for the given service properties (eg. _http._tcp.local). * The usual prepended '_' are added, if missing in the input strings. * */ bool MDNSResponder::_buildDomainForService(const char* p_pcService, const char* p_pcProtocol, MDNSResponder::stcMDNS_RRDomain& p_rServiceDomain) const { p_rServiceDomain.clear(); bool bResult = ((p_pcService) && (p_pcProtocol) && (p_rServiceDomain.addLabel(p_pcService, ('_' != *p_pcService))) && (p_rServiceDomain.addLabel(p_pcProtocol, ('_' != *p_pcProtocol))) && (p_rServiceDomain.addLabel(scpcLocal)) && (p_rServiceDomain.addLabel(0))); DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _buildDomainForService: FAILED for (%s.%s)!\n"), (p_pcService ?: "-"), (p_pcProtocol ?: "-"));); return bResult; } #ifdef MDNS_IP4_SUPPORT /* * MDNSResponder::_buildDomainForReverseIP4 * * The IP4 address is stringized by printing the four address bytes into a char buffer in reverse order * and adding 'in-addr.arpa' (eg. 012.789.456.123.in-addr.arpa). * Used while detecting reverse IP4 questions and answering these */ bool MDNSResponder::_buildDomainForReverseIP4(IPAddress p_IP4Address, MDNSResponder::stcMDNS_RRDomain& p_rReverseIP4Domain) const { bool bResult = true; p_rReverseIP4Domain.clear(); char acBuffer[32]; for (int i=MDNS_IP4_SIZE; ((bResult) && (i>=1)); --i) { itoa(p_IP4Address[i - 1], acBuffer, 10); bResult = p_rReverseIP4Domain.addLabel(acBuffer); } bResult = ((bResult) && (p_rReverseIP4Domain.addLabel(scpcReverseIP4Domain)) && (p_rReverseIP4Domain.addLabel(scpcReverseTopDomain)) && (p_rReverseIP4Domain.addLabel(0))); DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _buildDomainForReverseIP4: FAILED!\n"));); return bResult; } #endif #ifdef MDNS_IP6_SUPPORT /* * MDNSResponder::_buildDomainForReverseIP6 * * Used while detecting reverse IP6 questions and answering these */ bool MDNSResponder::_buildDomainForReverseIP6(IPAddress p_IP4Address, MDNSResponder::stcMDNS_RRDomain& p_rReverseIP6Domain) const { // TODO: Implement return false; } #endif /* * UDP */ /* * MDNSResponder::_udpReadBuffer */ bool MDNSResponder::_udpReadBuffer(unsigned char* p_pBuffer, size_t p_stLength) { bool bResult = ((m_pUDPContext) && (true/*m_pUDPContext->getSize() > p_stLength*/) && (p_pBuffer) && (p_stLength) && ((p_stLength == m_pUDPContext->read((char*)p_pBuffer, p_stLength)))); DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _udpReadBuffer: FAILED!\n")); }); return bResult; } /* * MDNSResponder::_udpRead8 */ bool MDNSResponder::_udpRead8(uint8_t& p_ru8Value) { return _udpReadBuffer((unsigned char*)&p_ru8Value, sizeof(p_ru8Value)); } /* * MDNSResponder::_udpRead16 */ bool MDNSResponder::_udpRead16(uint16_t& p_ru16Value) { bool bResult = false; if (_udpReadBuffer((unsigned char*)&p_ru16Value, sizeof(p_ru16Value))) { p_ru16Value = lwip_ntohs(p_ru16Value); bResult = true; } return bResult; } /* * MDNSResponder::_udpRead32 */ bool MDNSResponder::_udpRead32(uint32_t& p_ru32Value) { bool bResult = false; if (_udpReadBuffer((unsigned char*)&p_ru32Value, sizeof(p_ru32Value))) { p_ru32Value = lwip_ntohl(p_ru32Value); bResult = true; } return bResult; } /* * MDNSResponder::_udpAppendBuffer */ bool MDNSResponder::_udpAppendBuffer(const unsigned char* p_pcBuffer, size_t p_stLength) { bool bResult = ((m_pUDPContext) && (p_pcBuffer) && (p_stLength) && (p_stLength == m_pUDPContext->append((const char*)p_pcBuffer, p_stLength))); DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _udpAppendBuffer: FAILED!\n")); }); return bResult; } /* * MDNSResponder::_udpAppend8 */ bool MDNSResponder::_udpAppend8(uint8_t p_u8Value) { return (_udpAppendBuffer((unsigned char*)&p_u8Value, sizeof(p_u8Value))); } /* * MDNSResponder::_udpAppend16 */ bool MDNSResponder::_udpAppend16(uint16_t p_u16Value) { p_u16Value = lwip_htons(p_u16Value); return (_udpAppendBuffer((unsigned char*)&p_u16Value, sizeof(p_u16Value))); } /* * MDNSResponder::_udpAppend32 */ bool MDNSResponder::_udpAppend32(uint32_t p_u32Value) { p_u32Value = lwip_htonl(p_u32Value); return (_udpAppendBuffer((unsigned char*)&p_u32Value, sizeof(p_u32Value))); } #ifdef DEBUG_ESP_MDNS_RESPONDER /* * MDNSResponder::_udpDump */ bool MDNSResponder::_udpDump(bool p_bMovePointer /*= false*/) { const uint8_t cu8BytesPerLine = 16; uint32_t u32StartPosition = m_pUDPContext->tell(); DEBUG_OUTPUT.println("UDP Context Dump:"); uint32_t u32Counter = 0; uint8_t u8Byte = 0; while (_udpRead8(u8Byte)) { DEBUG_OUTPUT.printf_P(PSTR("%02x %s"), u8Byte, ((++u32Counter % cu8BytesPerLine) ? "" : "\n")); } DEBUG_OUTPUT.printf_P(PSTR("%sDone: %u bytes\n"), (((u32Counter) && (u32Counter % cu8BytesPerLine)) ? "\n" : ""), u32Counter); if (!p_bMovePointer) { // Restore m_pUDPContext->seek(u32StartPosition); } return true; } /* * MDNSResponder::_udpDump */ bool MDNSResponder::_udpDump(unsigned p_uOffset, unsigned p_uLength) { if ((m_pUDPContext) && (m_pUDPContext->isValidOffset(p_uOffset))) { unsigned uCurrentPosition = m_pUDPContext->tell(); // Remember start position m_pUDPContext->seek(p_uOffset); uint8_t u8Byte; for (unsigned u=0; ((useek(uCurrentPosition); } return true; } #endif /** * READ/WRITE MDNS STRUCTS */ /* * MDNSResponder::_readMDNSMsgHeader * * Read a MDNS header from the UDP input buffer. * | 8 | 8 | 8 | 8 | * 00| Identifier | Flags & Codes | * 01| Question count | Answer count | * 02| NS answer count | Ad answer count | * * All 16-bit and 32-bit elements need to be translated form network coding to host coding (done in _udpRead16 and _udpRead32) * In addition, bitfield memory order is undefined in C standard (GCC doesn't order them in the coded direction...), so they * need some mapping here */ bool MDNSResponder::_readMDNSMsgHeader(MDNSResponder::stcMDNS_MsgHeader& p_rMsgHeader) { bool bResult = false; uint8_t u8B1; uint8_t u8B2; if ((_udpRead16(p_rMsgHeader.m_u16ID)) && (_udpRead8(u8B1))&& (_udpRead8(u8B2)) && (_udpRead16(p_rMsgHeader.m_u16QDCount)) && (_udpRead16(p_rMsgHeader.m_u16ANCount)) && (_udpRead16(p_rMsgHeader.m_u16NSCount)) && (_udpRead16(p_rMsgHeader.m_u16ARCount))) { p_rMsgHeader.m_1bQR = (u8B1 & 0x80); // Query/Responde flag p_rMsgHeader.m_4bOpcode = (u8B1 & 0x78); // Operation code (0: Standard query, others ignored) p_rMsgHeader.m_1bAA = (u8B1 & 0x04); // Authorative answer p_rMsgHeader.m_1bTC = (u8B1 & 0x02); // Truncation flag p_rMsgHeader.m_1bRD = (u8B1 & 0x01); // Recursion desired p_rMsgHeader.m_1bRA = (u8B2 & 0x80); // Recursion available p_rMsgHeader.m_3bZ = (u8B2 & 0x70); // Zero p_rMsgHeader.m_4bRCode = (u8B2 & 0x0F); // Response code /*DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readMDNSMsgHeader: ID:%u QR:%u OP:%u AA:%u TC:%u RD:%u RA:%u R:%u QD:%u AN:%u NS:%u AR:%u\n"), (unsigned)p_rMsgHeader.m_u16ID, (unsigned)p_rMsgHeader.m_1bQR, (unsigned)p_rMsgHeader.m_4bOpcode, (unsigned)p_rMsgHeader.m_1bAA, (unsigned)p_rMsgHeader.m_1bTC, (unsigned)p_rMsgHeader.m_1bRD, (unsigned)p_rMsgHeader.m_1bRA, (unsigned)p_rMsgHeader.m_4bRCode, (unsigned)p_rMsgHeader.m_u16QDCount, (unsigned)p_rMsgHeader.m_u16ANCount, (unsigned)p_rMsgHeader.m_u16NSCount, (unsigned)p_rMsgHeader.m_u16ARCount););*/ bResult = true; } DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readMDNSMsgHeader: FAILED!\n")); } ); return bResult; } /* * MDNSResponder::_write8 */ bool MDNSResponder::_write8(uint8_t p_u8Value, MDNSResponder::stcMDNSSendParameter& p_rSendParameter) { return ((_udpAppend8(p_u8Value)) && (p_rSendParameter.shiftOffset(sizeof(p_u8Value)))); } /* * MDNSResponder::_write16 */ bool MDNSResponder::_write16(uint16_t p_u16Value, MDNSResponder::stcMDNSSendParameter& p_rSendParameter) { return ((_udpAppend16(p_u16Value)) && (p_rSendParameter.shiftOffset(sizeof(p_u16Value)))); } /* * MDNSResponder::_write32 */ bool MDNSResponder::_write32(uint32_t p_u32Value, MDNSResponder::stcMDNSSendParameter& p_rSendParameter) { return ((_udpAppend32(p_u32Value)) && (p_rSendParameter.shiftOffset(sizeof(p_u32Value)))); } /* * MDNSResponder::_writeMDNSMsgHeader * * Write MDNS header to the UDP output buffer. * * All 16-bit and 32-bit elements need to be translated form host coding to network coding (done in _udpAppend16 and _udpAppend32) * In addition, bitfield memory order is undefined in C standard (GCC doesn't order them in the coded direction...), so they * need some mapping here */ bool MDNSResponder::_writeMDNSMsgHeader(const MDNSResponder::stcMDNS_MsgHeader& p_MsgHeader, MDNSResponder::stcMDNSSendParameter& p_rSendParameter) { /*DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSMsgHeader: ID:%u QR:%u OP:%u AA:%u TC:%u RD:%u RA:%u R:%u QD:%u AN:%u NS:%u AR:%u\n"), (unsigned)p_MsgHeader.m_u16ID, (unsigned)p_MsgHeader.m_1bQR, (unsigned)p_MsgHeader.m_4bOpcode, (unsigned)p_MsgHeader.m_1bAA, (unsigned)p_MsgHeader.m_1bTC, (unsigned)p_MsgHeader.m_1bRD, (unsigned)p_MsgHeader.m_1bRA, (unsigned)p_MsgHeader.m_4bRCode, (unsigned)p_MsgHeader.m_u16QDCount, (unsigned)p_MsgHeader.m_u16ANCount, (unsigned)p_MsgHeader.m_u16NSCount, (unsigned)p_MsgHeader.m_u16ARCount););*/ uint8_t u8B1((p_MsgHeader.m_1bQR << 7) | (p_MsgHeader.m_4bOpcode << 3) | (p_MsgHeader.m_1bAA << 2) | (p_MsgHeader.m_1bTC << 1) | (p_MsgHeader.m_1bRD)); uint8_t u8B2((p_MsgHeader.m_1bRA << 7) | (p_MsgHeader.m_3bZ << 4) | (p_MsgHeader.m_4bRCode)); bool bResult = ((_write16(p_MsgHeader.m_u16ID, p_rSendParameter)) && (_write8(u8B1, p_rSendParameter)) && (_write8(u8B2, p_rSendParameter)) && (_write16(p_MsgHeader.m_u16QDCount, p_rSendParameter)) && (_write16(p_MsgHeader.m_u16ANCount, p_rSendParameter)) && (_write16(p_MsgHeader.m_u16NSCount, p_rSendParameter)) && (_write16(p_MsgHeader.m_u16ARCount, p_rSendParameter))); DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSMsgHeader: FAILED!\n")); }); return bResult; } /* * MDNSResponder::_writeRRAttributes */ bool MDNSResponder::_writeMDNSRRAttributes(const MDNSResponder::stcMDNS_RRAttributes& p_Attributes, MDNSResponder::stcMDNSSendParameter& p_rSendParameter) { bool bResult = ((_write16(p_Attributes.m_u16Type, p_rSendParameter)) && (_write16(p_Attributes.m_u16Class, p_rSendParameter))); DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSRRAttributes: FAILED!\n")); }); return bResult; } /* * MDNSResponder::_writeMDNSRRDomain */ bool MDNSResponder::_writeMDNSRRDomain(const MDNSResponder::stcMDNS_RRDomain& p_Domain, MDNSResponder::stcMDNSSendParameter& p_rSendParameter) { bool bResult = ((_udpAppendBuffer((const unsigned char*)p_Domain.m_acName, p_Domain.m_u16NameLength)) && (p_rSendParameter.shiftOffset(p_Domain.m_u16NameLength))); DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSRRDomain: FAILED!\n")); }); return bResult; } /* * MDNSResponder::_writeMDNSHostDomain * * Write a host domain to the UDP output buffer. * If the domain record is part of the answer, the records length is * prepended (p_bPrependRDLength is set). * * A very simple form of name compression is applied here: * If the domain is written to the UDP output buffer, the write offset is stored * together with a domain id (the pointer) in a p_rSendParameter substructure (cache). * If the same domain (pointer) should be written to the UDP output later again, * the old offset is retrieved from the cache, marked as a compressed domain offset * and written to the output buffer. * */ bool MDNSResponder::_writeMDNSHostDomain(const char* p_pcHostname, bool p_bPrependRDLength, MDNSResponder::stcMDNSSendParameter& p_rSendParameter) { // The 'skip-compression' version is handled in '_writeMDNSAnswer_SRV' uint16_t u16CachedDomainOffset = p_rSendParameter.findCachedDomainOffset((const void*)p_pcHostname, false); stcMDNS_RRDomain hostDomain; bool bResult = (u16CachedDomainOffset // Found cached domain -> mark as compressed domain ? ((MDNS_DOMAIN_COMPRESS_MARK > ((u16CachedDomainOffset >> 8) & ~MDNS_DOMAIN_COMPRESS_MARK)) && // Valid offset ((!p_bPrependRDLength) || (_write16(2, p_rSendParameter))) && // Length of 'Cxxx' (_write8(((u16CachedDomainOffset >> 8) | MDNS_DOMAIN_COMPRESS_MARK), p_rSendParameter)) && // Compression mark (and offset) (_write8((uint8_t)(u16CachedDomainOffset & 0xFF), p_rSendParameter))) // No cached domain -> add this domain to cache and write full domain name : ((_buildDomainForHost(p_pcHostname, hostDomain)) && // eg. esp8266.local ((!p_bPrependRDLength) || (_write16(hostDomain.m_u16NameLength, p_rSendParameter))) && // RDLength (if needed) (p_rSendParameter.addDomainCacheItem((const void*)p_pcHostname, false, p_rSendParameter.m_u16Offset)) && (_writeMDNSRRDomain(hostDomain, p_rSendParameter)))); DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSHostDomain: FAILED!\n")); }); return bResult; } /* * MDNSResponder::_writeMDNSServiceDomain * * Write a service domain to the UDP output buffer. * If the domain record is part of the answer, the records length is * prepended (p_bPrependRDLength is set). * * A very simple form of name compression is applied here: see '_writeMDNSHostDomain' * The cache differentiates of course between service domains which includes * the instance name (p_bIncludeName is set) and thoose who don't. * */ bool MDNSResponder::_writeMDNSServiceDomain(const MDNSResponder::stcMDNSService& p_Service, bool p_bIncludeName, bool p_bPrependRDLength, MDNSResponder::stcMDNSSendParameter& p_rSendParameter) { // The 'skip-compression' version is handled in '_writeMDNSAnswer_SRV' uint16_t u16CachedDomainOffset = p_rSendParameter.findCachedDomainOffset((const void*)&p_Service, p_bIncludeName); stcMDNS_RRDomain serviceDomain; bool bResult = (u16CachedDomainOffset // Found cached domain -> mark as compressed domain ? ((MDNS_DOMAIN_COMPRESS_MARK > ((u16CachedDomainOffset >> 8) & ~MDNS_DOMAIN_COMPRESS_MARK)) && // Valid offset ((!p_bPrependRDLength) || (_write16(2, p_rSendParameter))) && // Lenght of 'Cxxx' (_write8(((u16CachedDomainOffset >> 8) | MDNS_DOMAIN_COMPRESS_MARK), p_rSendParameter)) && // Compression mark (and offset) (_write8((uint8_t)(u16CachedDomainOffset & 0xFF), p_rSendParameter))) // No cached domain -> add this domain to cache and write full domain name : ((_buildDomainForService(p_Service, p_bIncludeName, serviceDomain)) && // eg. MyESP._http._tcp.local ((!p_bPrependRDLength) || (_write16(serviceDomain.m_u16NameLength, p_rSendParameter))) && // RDLength (if needed) (p_rSendParameter.addDomainCacheItem((const void*)&p_Service, p_bIncludeName, p_rSendParameter.m_u16Offset)) && (_writeMDNSRRDomain(serviceDomain, p_rSendParameter)))); DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSServiceDomain: FAILED!\n")); }); return bResult; } /* * MDNSResponder::_writeMDNSQuestion * * Write a MDNS question to the UDP output buffer * * QNAME (host/service domain, eg. esp8266.local) * QTYPE (16bit, eg. ANY) * QCLASS (16bit, eg. IN) * */ bool MDNSResponder::_writeMDNSQuestion(MDNSResponder::stcMDNS_RRQuestion& p_Question, MDNSResponder::stcMDNSSendParameter& p_rSendParameter) { DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSQuestion\n"));); bool bResult = ((_writeMDNSRRDomain(p_Question.m_Header.m_Domain, p_rSendParameter)) && (_writeMDNSRRAttributes(p_Question.m_Header.m_Attributes, p_rSendParameter))); DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSQuestion: FAILED!\n")); }); return bResult; } #ifdef MDNS_IP4_SUPPORT /* * MDNSResponder::_writeMDNSAnswer_A * * Write a MDNS A answer to the UDP output buffer. * * NAME (var, host/service domain, eg. esp8266.local * TYPE (16bit, eg. A) * CLASS (16bit, eg. IN) * TTL (32bit, eg. 120) * RDLENGTH (16bit, eg 4) * RDATA (var, eg. 123.456.789.012) * * eg. esp8266.local A 0x8001 120 4 123.456.789.012 * Ref: http://www.zytrax.com/books/dns/ch8/a.html */ bool MDNSResponder::_writeMDNSAnswer_A(IPAddress p_IPAddress, MDNSResponder::stcMDNSSendParameter& p_rSendParameter) { DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_A (%s)\n"), p_IPAddress.toString().c_str());); stcMDNS_RRAttributes attributes(DNS_RRTYPE_A, ((p_rSendParameter.m_bCacheFlush ? 0x8000 : 0) | DNS_RRCLASS_IN)); // Cache flush? & INternet const unsigned char aucIPAddress[MDNS_IP4_SIZE] = { p_IPAddress[0], p_IPAddress[1], p_IPAddress[2], p_IPAddress[3] }; bool bResult = ((_writeMDNSHostDomain(m_pcHostname, false, p_rSendParameter)) && (_writeMDNSRRAttributes(attributes, p_rSendParameter)) && // TYPE & CLASS (_write32((p_rSendParameter.m_bUnannounce ? 0 : MDNS_HOST_TTL), p_rSendParameter)) && // TTL (_write16(MDNS_IP4_SIZE, p_rSendParameter)) && // RDLength (_udpAppendBuffer(aucIPAddress, MDNS_IP4_SIZE)) && // RData (p_rSendParameter.shiftOffset(MDNS_IP4_SIZE))); DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_A: FAILED!\n")); }); return bResult; } /* * MDNSResponder::_writeMDNSAnswer_PTR_IP4 * * Write a MDNS reverse IP4 PTR answer to the UDP output buffer. * See: '_writeMDNSAnswer_A' * * eg. 012.789.456.123.in-addr.arpa PTR 0x8001 120 15 esp8266.local * Used while answering reverse IP4 questions */ bool MDNSResponder::_writeMDNSAnswer_PTR_IP4(IPAddress p_IPAddress, MDNSResponder::stcMDNSSendParameter& p_rSendParameter) { DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_PTR_IP4 (%s)\n"), p_IPAddress.toString().c_str());); stcMDNS_RRDomain reverseIP4Domain; stcMDNS_RRAttributes attributes(DNS_RRTYPE_PTR, ((p_rSendParameter.m_bCacheFlush ? 0x8000 : 0) | DNS_RRCLASS_IN)); // Cache flush? & INternet stcMDNS_RRDomain hostDomain; bool bResult = ((_buildDomainForReverseIP4(p_IPAddress, reverseIP4Domain)) && // 012.789.456.123.in-addr.arpa (_writeMDNSRRDomain(reverseIP4Domain, p_rSendParameter)) && (_writeMDNSRRAttributes(attributes, p_rSendParameter)) && // TYPE & CLASS (_write32((p_rSendParameter.m_bUnannounce ? 0 : MDNS_HOST_TTL), p_rSendParameter)) && // TTL (_writeMDNSHostDomain(m_pcHostname, true, p_rSendParameter))); // RDLength & RData (host domain, eg. esp8266.local) DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_PTR_IP4: FAILED!\n")); }); return bResult; } #endif /* * MDNSResponder::_writeMDNSAnswer_PTR_TYPE * * Write a MDNS PTR answer to the UDP output buffer. * See: '_writeMDNSAnswer_A' * * PTR all-services -> service type * eg. _services._dns-sd._udp.local PTR 0x8001 5400 xx _http._tcp.local * http://www.zytrax.com/books/dns/ch8/ptr.html */ bool MDNSResponder::_writeMDNSAnswer_PTR_TYPE(MDNSResponder::stcMDNSService& p_rService, MDNSResponder::stcMDNSSendParameter& p_rSendParameter) { DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_PTR_TYPE\n"));); stcMDNS_RRDomain dnssdDomain; stcMDNS_RRDomain serviceDomain; stcMDNS_RRAttributes attributes(DNS_RRTYPE_PTR, DNS_RRCLASS_IN); // No cache flush! only INternet bool bResult = ((_buildDomainForDNSSD(dnssdDomain)) && // _services._dns-sd._udp.local (_writeMDNSRRDomain(dnssdDomain, p_rSendParameter)) && (_writeMDNSRRAttributes(attributes, p_rSendParameter)) && // TYPE & CLASS (_write32((p_rSendParameter.m_bUnannounce ? 0 : MDNS_SERVICE_TTL), p_rSendParameter)) && // TTL (_writeMDNSServiceDomain(p_rService, false, true, p_rSendParameter))); // RDLength & RData (service domain, eg. _http._tcp.local) DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_PTR_TYPE: FAILED!\n")); }); return bResult; } /* * MDNSResponder::_writeMDNSAnswer_PTR_NAME * * Write a MDNS PTR answer to the UDP output buffer. * See: '_writeMDNSAnswer_A' * * PTR service type -> service name * eg. _http.tcp.local PTR 0x8001 120 xx myESP._http._tcp.local * http://www.zytrax.com/books/dns/ch8/ptr.html */ bool MDNSResponder::_writeMDNSAnswer_PTR_NAME(MDNSResponder::stcMDNSService& p_rService, MDNSResponder::stcMDNSSendParameter& p_rSendParameter) { DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_PTR_NAME\n"));); stcMDNS_RRAttributes attributes(DNS_RRTYPE_PTR, DNS_RRCLASS_IN); // No cache flush! only INternet bool bResult = ((_writeMDNSServiceDomain(p_rService, false, false, p_rSendParameter)) && // _http._tcp.local (_writeMDNSRRAttributes(attributes, p_rSendParameter)) && // TYPE & CLASS (_write32((p_rSendParameter.m_bUnannounce ? 0 : MDNS_SERVICE_TTL), p_rSendParameter)) && // TTL (_writeMDNSServiceDomain(p_rService, true, true, p_rSendParameter))); // RDLength & RData (service domain, eg. MyESP._http._tcp.local) DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_PTR_NAME: FAILED!\n")); }); return bResult; } /* * MDNSResponder::_writeMDNSAnswer_TXT * * Write a MDNS TXT answer to the UDP output buffer. * See: '_writeMDNSAnswer_A' * * The TXT items in the RDATA block are 'length byte encoded': [len]vardata * * eg. myESP._http._tcp.local TXT 0x8001 120 4 c#=1 * http://www.zytrax.com/books/dns/ch8/txt.html */ bool MDNSResponder::_writeMDNSAnswer_TXT(MDNSResponder::stcMDNSService& p_rService, MDNSResponder::stcMDNSSendParameter& p_rSendParameter) { DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_TXT\n"));); bool bResult = false; stcMDNS_RRAttributes attributes(DNS_RRTYPE_TXT, ((p_rSendParameter.m_bCacheFlush ? 0x8000 : 0) | DNS_RRCLASS_IN)); // Cache flush? & INternet if ((_collectServiceTxts(p_rService)) && (_writeMDNSServiceDomain(p_rService, true, false, p_rSendParameter)) && // MyESP._http._tcp.local (_writeMDNSRRAttributes(attributes, p_rSendParameter)) && // TYPE & CLASS (_write32((p_rSendParameter.m_bUnannounce ? 0 : MDNS_SERVICE_TTL), p_rSendParameter)) && // TTL (_write16(p_rService.m_Txts.length(), p_rSendParameter))) { // RDLength bResult = true; // RData Txts for (stcMDNSServiceTxt* pTxt=p_rService.m_Txts.m_pTxts; ((bResult) && (pTxt)); pTxt = pTxt->m_pNext) { unsigned char ucLengthByte = pTxt->length(); bResult = ((_udpAppendBuffer((unsigned char*)&ucLengthByte, sizeof(ucLengthByte))) && // Length (p_rSendParameter.shiftOffset(sizeof(ucLengthByte))) && ((size_t)os_strlen(pTxt->m_pcKey) == m_pUDPContext->append(pTxt->m_pcKey, os_strlen(pTxt->m_pcKey))) && // Key (p_rSendParameter.shiftOffset((size_t)os_strlen(pTxt->m_pcKey))) && (1 == m_pUDPContext->append("=", 1)) && // = (p_rSendParameter.shiftOffset(1)) && ((!pTxt->m_pcValue) || (((size_t)os_strlen(pTxt->m_pcValue) == m_pUDPContext->append(pTxt->m_pcValue, os_strlen(pTxt->m_pcValue))) && // Value (p_rSendParameter.shiftOffset((size_t)os_strlen(pTxt->m_pcValue)))))); DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_TXT: FAILED to write %sTxt %s=%s!\n"), (pTxt->m_bTemp ? "temp. " : ""), (pTxt->m_pcKey ?: "?"), (pTxt->m_pcValue ?: "?")); }); } } _releaseTempServiceTxts(p_rService); DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_TXT: FAILED!\n")); }); return bResult; } #ifdef MDNS_IP6_SUPPORT /* * MDNSResponder::_writeMDNSAnswer_AAAA * * Write a MDNS AAAA answer to the UDP output buffer. * See: '_writeMDNSAnswer_A' * * eg. esp8266.local AAAA 0x8001 120 16 xxxx::xx * http://www.zytrax.com/books/dns/ch8/aaaa.html */ bool MDNSResponder::_writeMDNSAnswer_AAAA(IPAddress p_IPAddress, MDNSResponder::stcMDNSSendParameter& p_rSendParameter) { DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_AAAA\n"));); stcMDNS_RRAttributes attributes(DNS_RRTYPE_AAAA, ((p_rSendParameter.m_bCacheFlush ? 0x8000 : 0) | DNS_RRCLASS_IN)); // Cache flush? & INternet bool bResult = ((_writeMDNSHostDomain(m_pcHostname, false, p_rSendParameter)) && // esp8266.local (_writeMDNSRRAttributes(attributes, p_rSendParameter)) && // TYPE & CLASS (_write32((p_rSendParameter.m_bUnannounce ? 0 : MDNS_HOST_TTL), p_rSendParameter)) && // TTL (_write16(MDNS_IP6_SIZE, p_rSendParameter)) && // RDLength (false /*TODO: IP6 version of: _udpAppendBuffer((uint32_t)p_IPAddress, MDNS_IP4_SIZE)*/)); // RData DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_AAAA: FAILED!\n")); }); return bResult; } /* * MDNSResponder::_writeMDNSAnswer_PTR_IP6 * * Write a MDNS reverse IP6 PTR answer to the UDP output buffer. * See: '_writeMDNSAnswer_A' * * eg. xxxx::xx.in6.arpa PTR 0x8001 120 15 esp8266.local * Used while answering reverse IP6 questions */ bool MDNSResponder::_writeMDNSAnswer_PTR_IP6(IPAddress p_IPAddress, MDNSResponder::stcMDNSSendParameter& p_rSendParameter) { DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_PTR_IP6\n"));); stcMDNS_RRDomain reverseIP6Domain; stcMDNS_RRAttributes attributes(DNS_RRTYPE_PTR, ((p_rSendParameter.m_bCacheFlush ? 0x8000 : 0) | DNS_RRCLASS_IN)); // Cache flush? & INternet bool bResult = ((_buildDomainForReverseIP6(p_IPAddress, reverseIP6Domain)) && // xxxx::xx.ip6.arpa (_writeMDNSRRDomain(reverseIP6Domain, p_rSendParameter)) && (_writeMDNSRRAttributes(attributes, p_rSendParameter)) && // TYPE & CLASS (_write32((p_rSendParameter.m_bUnannounce ? 0 : MDNS_HOST_TTL), p_rSendParameter)) && // TTL (_writeMDNSHostDomain(m_pcHostname, true, p_rSendParameter))); // RDLength & RData (host domain, eg. esp8266.local) DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_PTR_IP6: FAILED!\n")); }); return bResult; } #endif /* * MDNSResponder::_writeMDNSAnswer_SRV * * eg. MyESP._http.tcp.local SRV 0x8001 120 0 0 60068 esp8266.local * http://www.zytrax.com/books/dns/ch8/srv.html ???? Include instance name ???? */ bool MDNSResponder::_writeMDNSAnswer_SRV(MDNSResponder::stcMDNSService& p_rService, MDNSResponder::stcMDNSSendParameter& p_rSendParameter) { DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_SRV\n"));); uint16_t u16CachedDomainOffset = (p_rSendParameter.m_bLegacyQuery ? 0 : p_rSendParameter.findCachedDomainOffset((const void*)m_pcHostname, false)); stcMDNS_RRAttributes attributes(DNS_RRTYPE_SRV, ((p_rSendParameter.m_bCacheFlush ? 0x8000 : 0) | DNS_RRCLASS_IN)); // Cache flush? & INternet stcMDNS_RRDomain hostDomain; bool bResult = ((_writeMDNSServiceDomain(p_rService, true, false, p_rSendParameter)) && // MyESP._http._tcp.local (_writeMDNSRRAttributes(attributes, p_rSendParameter)) && // TYPE & CLASS (_write32((p_rSendParameter.m_bUnannounce ? 0 : MDNS_SERVICE_TTL), p_rSendParameter)) && // TTL (!u16CachedDomainOffset // No cache for domain name (or no compression allowed) ? ((_buildDomainForHost(m_pcHostname, hostDomain)) && (_write16((sizeof(uint16_t /*Prio*/) + // RDLength sizeof(uint16_t /*Weight*/) + sizeof(uint16_t /*Port*/) + hostDomain.m_u16NameLength), p_rSendParameter)) && // Domain length (_write16(MDNS_SRV_PRIORITY, p_rSendParameter)) && // Priority (_write16(MDNS_SRV_WEIGHT, p_rSendParameter)) && // Weight (_write16(p_rService.m_u16Port, p_rSendParameter)) && // Port (p_rSendParameter.addDomainCacheItem((const void*)m_pcHostname, false, p_rSendParameter.m_u16Offset)) && (_writeMDNSRRDomain(hostDomain, p_rSendParameter))) // Host, eg. esp8266.local // Cache available for domain : ((MDNS_DOMAIN_COMPRESS_MARK > ((u16CachedDomainOffset >> 8) & ~MDNS_DOMAIN_COMPRESS_MARK)) && // Valid offset (_write16((sizeof(uint16_t /*Prio*/) + // RDLength sizeof(uint16_t /*Weight*/) + sizeof(uint16_t /*Port*/) + 2), p_rSendParameter)) && // Length of 'C0xx' (_write16(MDNS_SRV_PRIORITY, p_rSendParameter)) && // Priority (_write16(MDNS_SRV_WEIGHT, p_rSendParameter)) && // Weight (_write16(p_rService.m_u16Port, p_rSendParameter)) && // Port (_write8(((u16CachedDomainOffset >> 8) | MDNS_DOMAIN_COMPRESS_MARK), p_rSendParameter)) && // Compression mark (and offset) (_write8((uint8_t)u16CachedDomainOffset, p_rSendParameter))))); // Offset DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_SRV: FAILED!\n")); }); return bResult; } } // namespace MDNSImplementation } // namespace esp8266