diff --git a/libraries/ESP8266mDNS/src/ESP8266mDNS.cpp b/libraries/ESP8266mDNS/src/ESP8266mDNS.cpp index 62d54a7edb..c784a0d790 100644 --- a/libraries/ESP8266mDNS/src/ESP8266mDNS.cpp +++ b/libraries/ESP8266mDNS/src/ESP8266mDNS.cpp @@ -1,10 +1,10 @@ #include /* - * MDNS responder global instance - * - * Class type that is instantiated depends on the type mapping in ESP8266mDNS.h - */ + MDNS responder global instance + + Class type that is instantiated depends on the type mapping in ESP8266mDNS.h +*/ #if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_MDNS) MDNSResponder MDNS; #endif diff --git a/libraries/ESP8266mDNS/src/ESP8266mDNS.h b/libraries/ESP8266mDNS/src/ESP8266mDNS.h index 2987f655c2..66d40b1b2e 100644 --- a/libraries/ESP8266mDNS/src/ESP8266mDNS.h +++ b/libraries/ESP8266mDNS/src/ESP8266mDNS.h @@ -1,44 +1,44 @@ /* - ESP8266mDNS.h - mDNSResponder for ESP8266 family - This file is part of the esp8266 core for Arduino environment. - - Legacy_ESP8266mDNS: - The well known, thouroughly tested (yet no flawless) default mDNS library for the ESP8266 family - - LEA_ESP8266mDNS: - An (currently) experimental mDNS implementation, that supports a lot more of mDNS features than Legacy_ESP8266mDNS, like: - - Presenting a DNS-SD service to interested observers, eg. a http server by presenting _http._tcp service - - Support for multi-level compressed names in input; in output only a very simple one-leven full-name compression is implemented - - Probing host and service domains for uniqueness in the local network - - Tiebreaking while probing is supportet in a very minimalistic way (the 'higher' IP address wins the tiebreak) - - Announcing available services after successful probing - - Using fixed service TXT items or - - Using dynamic service TXT items for presented services (via callback) - - Remove services (and un-announcing them to the observers by sending goodbye-messages) - - Static queries for DNS-SD services (creating a fixed answer set after a certain timeout period) - - Dynamic queries for DNS-SD services with cached and updated answers and user notifications - - Support for multi-homed client host domains - - See 'LEA_ESP8266mDNS/EPS8266mDNS.h' for more implementation details and usage informations. - See 'examples/mDNS_Clock' and 'examples/mDNS_ServiceMonitor' for implementation examples of the advanced features. - - LEA_ESP8266mDNS is (mostly) client source code compatible to 'Legacy_ESP8266mDNS', so it could be - use as a 'drop-in' replacement in existing projects. - - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + ESP8266mDNS.h - mDNSResponder for ESP8266 family + This file is part of the esp8266 core for Arduino environment. + + Legacy_ESP8266mDNS: + The well known, thouroughly tested (yet no flawless) default mDNS library for the ESP8266 family + + LEA_ESP8266mDNS: + An (currently) experimental mDNS implementation, that supports a lot more of mDNS features than Legacy_ESP8266mDNS, like: + - Presenting a DNS-SD service to interested observers, eg. a http server by presenting _http._tcp service + - Support for multi-level compressed names in input; in output only a very simple one-leven full-name compression is implemented + - Probing host and service domains for uniqueness in the local network + - Tiebreaking while probing is supportet in a very minimalistic way (the 'higher' IP address wins the tiebreak) + - Announcing available services after successful probing + - Using fixed service TXT items or + - Using dynamic service TXT items for presented services (via callback) + - Remove services (and un-announcing them to the observers by sending goodbye-messages) + - Static queries for DNS-SD services (creating a fixed answer set after a certain timeout period) + - Dynamic queries for DNS-SD services with cached and updated answers and user notifications + - Support for multi-homed client host domains + + See 'LEA_ESP8266mDNS/EPS8266mDNS.h' for more implementation details and usage informations. + See 'examples/mDNS_Clock' and 'examples/mDNS_ServiceMonitor' for implementation examples of the advanced features. + + LEA_ESP8266mDNS is (mostly) client source code compatible to 'Legacy_ESP8266mDNS', so it could be + use as a 'drop-in' replacement in existing projects. + + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -47,10 +47,10 @@ #if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_MDNS) - // Maps the implementation to use to the global namespace type - //using MDNSResponder = Legacy_MDNSResponder::MDNSResponder; //legacy - using MDNSResponder = esp8266::MDNSImplementation::MDNSResponder; //new - - extern MDNSResponder MDNS; +// Maps the implementation to use to the global namespace type +//using MDNSResponder = Legacy_MDNSResponder::MDNSResponder; //legacy +using MDNSResponder = esp8266::MDNSImplementation::MDNSResponder; //new + +extern MDNSResponder MDNS; #endif diff --git a/libraries/ESP8266mDNS/src/ESP8266mDNS_Legacy.cpp b/libraries/ESP8266mDNS/src/ESP8266mDNS_Legacy.cpp index 80f55ad81a..af98b46b6b 100644 --- a/libraries/ESP8266mDNS/src/ESP8266mDNS_Legacy.cpp +++ b/libraries/ESP8266mDNS/src/ESP8266mDNS_Legacy.cpp @@ -1,31 +1,31 @@ /* -ESP8266 Multicast DNS (port of CC3000 Multicast DNS library) -Version 1.1 -Copyright (c) 2013 Tony DiCola (tony@tonydicola.com) -ESP8266 port (c) 2015 Ivan Grokhotkov (ivan@esp8266.com) -MDNS-SD Suport 2015 Hristo Gochkov -Extended MDNS-SD support 2016 Lars Englund (lars.englund@gmail.com) - - -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. + ESP8266 Multicast DNS (port of CC3000 Multicast DNS library) + Version 1.1 + Copyright (c) 2013 Tony DiCola (tony@tonydicola.com) + ESP8266 port (c) 2015 Ivan Grokhotkov (ivan@esp8266.com) + MDNS-SD Suport 2015 Hristo Gochkov + Extended MDNS-SD support 2016 Lars Englund (lars.englund@gmail.com) + + + 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. */ @@ -44,9 +44,9 @@ License (MIT license): #include "debug.h" extern "C" { - #include "osapi.h" - #include "ets_sys.h" - #include "user_interface.h" +#include "osapi.h" +#include "ets_sys.h" +#include "user_interface.h" } #include "WiFiUdp.h" @@ -59,7 +59,8 @@ extern "C" { -namespace Legacy_MDNSResponder { +namespace Legacy_MDNSResponder +{ #ifdef DEBUG_ESP_MDNS @@ -94,1189 +95,1421 @@ static const IPAddress MDNS_MULTICAST_ADDR(224, 0, 0, 251); static const int MDNS_MULTICAST_TTL = 1; static const int MDNS_PORT = 5353; -struct MDNSService { - MDNSService* _next; - char _name[32]; - char _proto[4]; - uint16_t _port; - uint16_t _txtLen; // length of all txts - struct MDNSTxt * _txts; +struct MDNSService +{ + MDNSService* _next; + char _name[32]; + char _proto[4]; + uint16_t _port; + uint16_t _txtLen; // length of all txts + struct MDNSTxt * _txts; }; -struct MDNSTxt{ - MDNSTxt * _next; - String _txt; +struct MDNSTxt +{ + MDNSTxt * _next; + String _txt; }; -struct MDNSAnswer { - MDNSAnswer* next; - uint8_t ip[4]; - uint16_t port; - char *hostname; +struct MDNSAnswer +{ + MDNSAnswer* next; + uint8_t ip[4]; + uint16_t port; + char *hostname; }; -struct MDNSQuery { - char _service[32]; - char _proto[4]; +struct MDNSQuery +{ + char _service[32]; + char _proto[4]; }; -MDNSResponder::MDNSResponder() : _conn(0) { - _services = 0; - _instanceName = ""; - _answers = 0; - _query = 0; - _newQuery = false; - _waitingForAnswers = false; -} -MDNSResponder::~MDNSResponder() { - if (_query != 0) { - os_free(_query); +MDNSResponder::MDNSResponder() : _conn(0) +{ + _services = 0; + _instanceName = ""; + _answers = 0; _query = 0; - } - - // Clear answer list - MDNSAnswer *answer; - int numAnswers = _getNumAnswers(); - for (int n = numAnswers - 1; n >= 0; n--) { - answer = _getAnswerFromIdx(n); - os_free(answer->hostname); - os_free(answer); - answer = 0; - } - _answers = 0; - - if (_conn) { - _conn->unref(); - } + _newQuery = false; + _waitingForAnswers = false; } +MDNSResponder::~MDNSResponder() +{ + if (_query != 0) + { + os_free(_query); + _query = 0; + } -bool MDNSResponder::begin(const char* hostname){ - size_t n = strlen(hostname); - if (n > 63) { // max size for a single label. - return false; - } + // Clear answer list + MDNSAnswer *answer; + int numAnswers = _getNumAnswers(); + for (int n = numAnswers - 1; n >= 0; n--) + { + answer = _getAnswerFromIdx(n); + os_free(answer->hostname); + os_free(answer); + answer = 0; + } + _answers = 0; - // Copy in hostname characters as lowercase - _hostName = hostname; - _hostName.toLowerCase(); + if (_conn) + { + _conn->unref(); + } +} - // If instance name is not already set copy hostname to instance name - if (_instanceName.equals("") ) _instanceName=hostname; +bool MDNSResponder::begin(const char* hostname) +{ + size_t n = strlen(hostname); + if (n > 63) // max size for a single label. + { + return false; + } - _gotIPHandler = WiFi.onStationModeGotIP([this](const WiFiEventStationModeGotIP& event){ - (void) event; - _restart(); - }); + // Copy in hostname characters as lowercase + _hostName = hostname; + _hostName.toLowerCase(); - _disconnectedHandler = WiFi.onStationModeDisconnected([this](const WiFiEventStationModeDisconnected& event) { - (void) event; - _restart(); - }); + // If instance name is not already set copy hostname to instance name + if (_instanceName.equals("")) + { + _instanceName = hostname; + } + + _gotIPHandler = WiFi.onStationModeGotIP([this](const WiFiEventStationModeGotIP & event) + { + (void) event; + _restart(); + }); - return _listen(); + _disconnectedHandler = WiFi.onStationModeDisconnected([this](const WiFiEventStationModeDisconnected & event) + { + (void) event; + _restart(); + }); + + return _listen(); } -void MDNSResponder::notifyAPChange() { - _restart(); +void MDNSResponder::notifyAPChange() +{ + _restart(); } -void MDNSResponder::_restart() { - if (_conn) { - _conn->unref(); - _conn = nullptr; - } - _listen(); +void MDNSResponder::_restart() +{ + if (_conn) + { + _conn->unref(); + _conn = nullptr; + } + _listen(); } -bool MDNSResponder::_listen() { - // Open the MDNS socket if it isn't already open. - if (!_conn) { - #ifdef DEBUG_ESP_MDNS_RX - DEBUG_ESP_PORT.println("MDNS listening"); - #endif +bool MDNSResponder::_listen() +{ + // Open the MDNS socket if it isn't already open. + if (!_conn) + { +#ifdef DEBUG_ESP_MDNS_RX + DEBUG_ESP_PORT.println("MDNS listening"); +#endif - IPAddress mdns(MDNS_MULTICAST_ADDR); + IPAddress mdns(MDNS_MULTICAST_ADDR); - if (igmp_joingroup(IP4_ADDR_ANY4, mdns)!= ERR_OK) { - return false; - } + if (igmp_joingroup(IP4_ADDR_ANY4, mdns) != ERR_OK) + { + return false; + } - _conn = new UdpContext; - _conn->ref(); + _conn = new UdpContext; + _conn->ref(); - if (!_conn->listen(IP_ADDR_ANY, MDNS_PORT)) { - return false; + if (!_conn->listen(IP_ADDR_ANY, MDNS_PORT)) + { + return false; + } + _conn->setMulticastTTL(MDNS_MULTICAST_TTL); + _conn->onRx(std::bind(&MDNSResponder::update, this)); + _conn->connect(mdns, MDNS_PORT); } - _conn->setMulticastTTL(MDNS_MULTICAST_TTL); - _conn->onRx(std::bind(&MDNSResponder::update, this)); - _conn->connect(mdns, MDNS_PORT); - } - return true; + return true; } -void MDNSResponder::update() { - if (!_conn || !_conn->next()) - return; - _parsePacket(); +void MDNSResponder::update() +{ + if (!_conn || !_conn->next()) + { + return; + } + _parsePacket(); } -void MDNSResponder::setInstanceName(String name){ - if (name.length() > 63) - return; - _instanceName = name; +void MDNSResponder::setInstanceName(String name) +{ + if (name.length() > 63) + { + return; + } + _instanceName = name; } -bool MDNSResponder::addServiceTxt(char *name, char *proto, char *key, char *value){ - MDNSService* servicePtr; - - uint8_t txtLen = os_strlen(key) + os_strlen(value) + 1; // Add one for equals sign - txtLen += 1; //accounts for length byte added when building the txt responce - //Find the service - for (servicePtr = _services; servicePtr; servicePtr = servicePtr->_next) { - //Checking Service names - if(strcmp(servicePtr->_name, name) == 0 && strcmp(servicePtr->_proto, proto) == 0) { - //found a service name match - if (servicePtr->_txtLen + txtLen > 1300) - return false; //max txt record size - MDNSTxt *newtxt = new MDNSTxt; - newtxt->_txt = String(key) + "=" + String(value); - newtxt->_next = 0; - if(servicePtr->_txts == 0) { //no services have been added - //Adding First TXT to service - servicePtr->_txts = newtxt; - servicePtr->_txtLen += txtLen; - return true; - } else { - MDNSTxt * txtPtr = servicePtr->_txts; - while(txtPtr->_next != 0) { - txtPtr = txtPtr->_next; +bool MDNSResponder::addServiceTxt(char *name, char *proto, char *key, char *value) +{ + MDNSService* servicePtr; + + uint8_t txtLen = os_strlen(key) + os_strlen(value) + 1; // Add one for equals sign + txtLen += 1; //accounts for length byte added when building the txt responce + //Find the service + for (servicePtr = _services; servicePtr; servicePtr = servicePtr->_next) + { + //Checking Service names + if (strcmp(servicePtr->_name, name) == 0 && strcmp(servicePtr->_proto, proto) == 0) + { + //found a service name match + if (servicePtr->_txtLen + txtLen > 1300) + { + return false; //max txt record size + } + MDNSTxt *newtxt = new MDNSTxt; + newtxt->_txt = String(key) + "=" + String(value); + newtxt->_next = 0; + if (servicePtr->_txts == 0) //no services have been added + { + //Adding First TXT to service + servicePtr->_txts = newtxt; + servicePtr->_txtLen += txtLen; + return true; + } + else + { + MDNSTxt * txtPtr = servicePtr->_txts; + while (txtPtr->_next != 0) + { + txtPtr = txtPtr->_next; + } + //adding another TXT to service + txtPtr->_next = newtxt; + servicePtr->_txtLen += txtLen; + return true; + } } - //adding another TXT to service - txtPtr->_next = newtxt; - servicePtr->_txtLen += txtLen; - return true; - } } - } - return false; + return false; } -void MDNSResponder::addService(char *name, char *proto, uint16_t port){ - if(_getServicePort(name, proto) != 0) - return; - if(os_strlen(name) > 32 || os_strlen(proto) != 3) - return; //bad arguments - struct MDNSService *srv = (struct MDNSService*)(os_malloc(sizeof(struct MDNSService))); - os_strcpy(srv->_name, name); - os_strcpy(srv->_proto, proto); - srv->_port = port; - srv->_next = 0; - srv->_txts = 0; - srv->_txtLen = 0; - - if(_services == 0) { - _services = srv; - } else { - MDNSService* servicePtr = _services; - while(servicePtr->_next != 0) - servicePtr = servicePtr->_next; - servicePtr->_next = srv; - } - +void MDNSResponder::addService(char *name, char *proto, uint16_t port) +{ + if (_getServicePort(name, proto) != 0) + { + return; + } + if (os_strlen(name) > 32 || os_strlen(proto) != 3) + { + return; //bad arguments + } + struct MDNSService *srv = (struct MDNSService*)(os_malloc(sizeof(struct MDNSService))); + os_strcpy(srv->_name, name); + os_strcpy(srv->_proto, proto); + srv->_port = port; + srv->_next = 0; + srv->_txts = 0; + srv->_txtLen = 0; + + if (_services == 0) + { + _services = srv; + } + else + { + MDNSService* servicePtr = _services; + while (servicePtr->_next != 0) + { + servicePtr = servicePtr->_next; + } + servicePtr->_next = srv; + } + } -int MDNSResponder::queryService(char *service, char *proto) { +int MDNSResponder::queryService(char *service, char *proto) +{ #ifdef DEBUG_ESP_MDNS_TX - DEBUG_ESP_PORT.printf("queryService %s %s\n", service, proto); -#endif - while(_answers!=0){ - MDNSAnswer *currAnswer = _answers; - _answers = _answers->next; - os_free(currAnswer->hostname); - os_free(currAnswer); - currAnswer = 0; - } - if (_query != 0) { - os_free(_query); - _query = 0; - } - _query = (struct MDNSQuery*)(os_malloc(sizeof(struct MDNSQuery))); - os_strcpy(_query->_service, service); - os_strcpy(_query->_proto, proto); - _newQuery = true; - - char underscore[] = "_"; - - // build service name with _ - char serviceName[os_strlen(service) + 2]; - os_strcpy(serviceName, underscore); - os_strcat(serviceName, service); - size_t serviceNameLen = os_strlen(serviceName); - - //build proto name with _ - char protoName[5]; - os_strcpy(protoName, underscore); - os_strcat(protoName, proto); - size_t protoNameLen = 4; - - //local string - char localName[] = "local"; - size_t localNameLen = 5; - - //terminator - char terminator[] = "\0"; - - // Only supports sending one PTR query - uint8_t questionCount = 1; - - _waitingForAnswers = true; - for (int itfn = 0; itfn < 2; itfn++) { - struct ip_info ip_info; + DEBUG_ESP_PORT.printf("queryService %s %s\n", service, proto); +#endif + while (_answers != 0) + { + MDNSAnswer *currAnswer = _answers; + _answers = _answers->next; + os_free(currAnswer->hostname); + os_free(currAnswer); + currAnswer = 0; + } + if (_query != 0) + { + os_free(_query); + _query = 0; + } + _query = (struct MDNSQuery*)(os_malloc(sizeof(struct MDNSQuery))); + os_strcpy(_query->_service, service); + os_strcpy(_query->_proto, proto); + _newQuery = true; - wifi_get_ip_info((!itfn) ? SOFTAP_IF : STATION_IF, &ip_info); - if (!ip_info.ip.addr) - continue; - _conn->setMulticastInterface(IPAddress(ip_info.ip.addr)); + char underscore[] = "_"; - // Write the header - _conn->flush(); - uint8_t head[12] = { - 0x00, 0x00, //ID = 0 - 0x00, 0x00, //Flags = response + authoritative answer - 0x00, questionCount, //Question count - 0x00, 0x00, //Answer count - 0x00, 0x00, //Name server records - 0x00, 0x00 //Additional records - }; - _conn->append(reinterpret_cast(head), 12); + // build service name with _ + char serviceName[os_strlen(service) + 2]; + os_strcpy(serviceName, underscore); + os_strcat(serviceName, service); + size_t serviceNameLen = os_strlen(serviceName); + + //build proto name with _ + char protoName[5]; + os_strcpy(protoName, underscore); + os_strcat(protoName, proto); + size_t protoNameLen = 4; + + //local string + char localName[] = "local"; + size_t localNameLen = 5; + + //terminator + char terminator[] = "\0"; // Only supports sending one PTR query - // Send the Name field (eg. "_http._tcp.local") - _conn->append(reinterpret_cast(&serviceNameLen), 1); // lenght of "_" + service - _conn->append(reinterpret_cast(serviceName), serviceNameLen); // "_" + service - _conn->append(reinterpret_cast(&protoNameLen), 1); // lenght of "_" + proto - _conn->append(reinterpret_cast(protoName), protoNameLen); // "_" + proto - _conn->append(reinterpret_cast(&localNameLen), 1); // lenght of "local" - _conn->append(reinterpret_cast(localName), localNameLen); // "local" - _conn->append(reinterpret_cast(&terminator), 1); // terminator - - //Send the type and class - uint8_t ptrAttrs[4] = { - 0x00, 0x0c, //PTR record query - 0x00, 0x01 //Class IN - }; - _conn->append(reinterpret_cast(ptrAttrs), 4); - _conn->send(); - } + uint8_t questionCount = 1; + + _waitingForAnswers = true; + for (int itfn = 0; itfn < 2; itfn++) + { + struct ip_info ip_info; + + wifi_get_ip_info((!itfn) ? SOFTAP_IF : STATION_IF, &ip_info); + if (!ip_info.ip.addr) + { + continue; + } + _conn->setMulticastInterface(IPAddress(ip_info.ip.addr)); + + // Write the header + _conn->flush(); + uint8_t head[12] = + { + 0x00, 0x00, //ID = 0 + 0x00, 0x00, //Flags = response + authoritative answer + 0x00, questionCount, //Question count + 0x00, 0x00, //Answer count + 0x00, 0x00, //Name server records + 0x00, 0x00 //Additional records + }; + _conn->append(reinterpret_cast(head), 12); + + // Only supports sending one PTR query + // Send the Name field (eg. "_http._tcp.local") + _conn->append(reinterpret_cast(&serviceNameLen), 1); // lenght of "_" + service + _conn->append(reinterpret_cast(serviceName), serviceNameLen); // "_" + service + _conn->append(reinterpret_cast(&protoNameLen), 1); // lenght of "_" + proto + _conn->append(reinterpret_cast(protoName), protoNameLen); // "_" + proto + _conn->append(reinterpret_cast(&localNameLen), 1); // lenght of "local" + _conn->append(reinterpret_cast(localName), localNameLen); // "local" + _conn->append(reinterpret_cast(&terminator), 1); // terminator + + //Send the type and class + uint8_t ptrAttrs[4] = + { + 0x00, 0x0c, //PTR record query + 0x00, 0x01 //Class IN + }; + _conn->append(reinterpret_cast(ptrAttrs), 4); + _conn->send(); + } #ifdef DEBUG_ESP_MDNS_TX - DEBUG_ESP_PORT.println("Waiting for answers.."); + DEBUG_ESP_PORT.println("Waiting for answers.."); #endif - delay(1000); + delay(1000); - _waitingForAnswers = false; + _waitingForAnswers = false; - return _getNumAnswers(); + return _getNumAnswers(); } -String MDNSResponder::hostname(int idx) { - MDNSAnswer *answer = _getAnswerFromIdx(idx); - if (answer == 0) { - return String(); - } - return answer->hostname; +String MDNSResponder::hostname(int idx) +{ + MDNSAnswer *answer = _getAnswerFromIdx(idx); + if (answer == 0) + { + return String(); + } + return answer->hostname; } -IPAddress MDNSResponder::IP(int idx) { - MDNSAnswer *answer = _getAnswerFromIdx(idx); - if (answer == 0) { - return IPAddress(); - } - return IPAddress(answer->ip); +IPAddress MDNSResponder::IP(int idx) +{ + MDNSAnswer *answer = _getAnswerFromIdx(idx); + if (answer == 0) + { + return IPAddress(); + } + return IPAddress(answer->ip); } -uint16_t MDNSResponder::port(int idx) { - MDNSAnswer *answer = _getAnswerFromIdx(idx); - if (answer == 0) { - return 0; - } - return answer->port; +uint16_t MDNSResponder::port(int idx) +{ + MDNSAnswer *answer = _getAnswerFromIdx(idx); + if (answer == 0) + { + return 0; + } + return answer->port; } -MDNSAnswer* MDNSResponder::_getAnswerFromIdx(int idx) { - MDNSAnswer *answer = _answers; - while (answer != 0 && idx-- > 0) { - answer = answer->next; - } - if (idx > 0) { - return 0; - } - return answer; +MDNSAnswer* MDNSResponder::_getAnswerFromIdx(int idx) +{ + MDNSAnswer *answer = _answers; + while (answer != 0 && idx-- > 0) + { + answer = answer->next; + } + if (idx > 0) + { + return 0; + } + return answer; } -int MDNSResponder::_getNumAnswers() { - int numAnswers = 0; - MDNSAnswer *answer = _answers; - while (answer != 0) { - numAnswers++; - answer = answer->next; - } - return numAnswers; +int MDNSResponder::_getNumAnswers() +{ + int numAnswers = 0; + MDNSAnswer *answer = _answers; + while (answer != 0) + { + numAnswers++; + answer = answer->next; + } + return numAnswers; } -MDNSTxt * MDNSResponder::_getServiceTxt(char *name, char *proto){ - MDNSService* servicePtr; - for (servicePtr = _services; servicePtr; servicePtr = servicePtr->_next) { - if(servicePtr->_port > 0 && strcmp(servicePtr->_name, name) == 0 && strcmp(servicePtr->_proto, proto) == 0){ - if (servicePtr->_txts == 0) - return nullptr; - return servicePtr->_txts; +MDNSTxt * MDNSResponder::_getServiceTxt(char *name, char *proto) +{ + MDNSService* servicePtr; + for (servicePtr = _services; servicePtr; servicePtr = servicePtr->_next) + { + if (servicePtr->_port > 0 && strcmp(servicePtr->_name, name) == 0 && strcmp(servicePtr->_proto, proto) == 0) + { + if (servicePtr->_txts == 0) + { + return nullptr; + } + return servicePtr->_txts; + } } - } - return nullptr; + return nullptr; } -uint16_t MDNSResponder::_getServiceTxtLen(char *name, char *proto){ - MDNSService* servicePtr; - for (servicePtr = _services; servicePtr; servicePtr = servicePtr->_next) { - if(servicePtr->_port > 0 && strcmp(servicePtr->_name, name) == 0 && strcmp(servicePtr->_proto, proto) == 0){ - if (servicePtr->_txts == 0) - return false; - return servicePtr->_txtLen; +uint16_t MDNSResponder::_getServiceTxtLen(char *name, char *proto) +{ + MDNSService* servicePtr; + for (servicePtr = _services; servicePtr; servicePtr = servicePtr->_next) + { + if (servicePtr->_port > 0 && strcmp(servicePtr->_name, name) == 0 && strcmp(servicePtr->_proto, proto) == 0) + { + if (servicePtr->_txts == 0) + { + return false; + } + return servicePtr->_txtLen; + } } - } - return 0; + return 0; } -uint16_t MDNSResponder::_getServicePort(char *name, char *proto){ - MDNSService* servicePtr; - for (servicePtr = _services; servicePtr; servicePtr = servicePtr->_next) { - if(servicePtr->_port > 0 && strcmp(servicePtr->_name, name) == 0 && strcmp(servicePtr->_proto, proto) == 0){ - return servicePtr->_port; +uint16_t MDNSResponder::_getServicePort(char *name, char *proto) +{ + MDNSService* servicePtr; + for (servicePtr = _services; servicePtr; servicePtr = servicePtr->_next) + { + if (servicePtr->_port > 0 && strcmp(servicePtr->_name, name) == 0 && strcmp(servicePtr->_proto, proto) == 0) + { + return servicePtr->_port; + } } - } - return 0; + return 0; } -IPAddress MDNSResponder::_getRequestMulticastInterface(){ - struct ip_info ip_info; - bool match_ap = false; - if (wifi_get_opmode() & SOFTAP_MODE) { - const IPAddress& remote_ip = _conn->getRemoteAddress(); - wifi_get_ip_info(SOFTAP_IF, &ip_info); - IPAddress infoIp(ip_info.ip); - IPAddress infoMask(ip_info.netmask); - if (ip_info.ip.addr && ip_addr_netcmp((const ip_addr_t*)remote_ip, (const ip_addr_t*)infoIp, ip_2_ip4((const ip_addr_t*)infoMask))) - match_ap = true; - } - if (!match_ap) - wifi_get_ip_info(STATION_IF, &ip_info); - return IPAddress(ip_info.ip.addr); +IPAddress MDNSResponder::_getRequestMulticastInterface() +{ + struct ip_info ip_info; + bool match_ap = false; + if (wifi_get_opmode() & SOFTAP_MODE) + { + const IPAddress& remote_ip = _conn->getRemoteAddress(); + wifi_get_ip_info(SOFTAP_IF, &ip_info); + IPAddress infoIp(ip_info.ip); + IPAddress infoMask(ip_info.netmask); + if (ip_info.ip.addr && ip_addr_netcmp((const ip_addr_t*)remote_ip, (const ip_addr_t*)infoIp, ip_2_ip4((const ip_addr_t*)infoMask))) + { + match_ap = true; + } + } + if (!match_ap) + { + wifi_get_ip_info(STATION_IF, &ip_info); + } + return IPAddress(ip_info.ip.addr); } -void MDNSResponder::_parsePacket(){ - int i; - char tmp; - bool serviceParsed = false; - bool protoParsed = false; - bool localParsed = false; +void MDNSResponder::_parsePacket() +{ + int i; + char tmp; + bool serviceParsed = false; + bool protoParsed = false; + bool localParsed = false; - char hostName[255]; - uint8_t hostNameLen; + char hostName[255]; + uint8_t hostNameLen; - char serviceName[32]; - uint8_t serviceNameLen; - uint16_t servicePort = 0; + char serviceName[32]; + uint8_t serviceNameLen; + uint16_t servicePort = 0; - char protoName[32]; - protoName[0] = 0; - uint8_t protoNameLen = 0; + char protoName[32]; + protoName[0] = 0; + uint8_t protoNameLen = 0; - uint16_t packetHeader[6]; + uint16_t packetHeader[6]; - for(i=0; i<6; i++) - packetHeader[i] = _conn_read16(); + for (i = 0; i < 6; i++) + { + packetHeader[i] = _conn_read16(); + } - if ((packetHeader[1] & 0x8000) != 0) { // Read answers + if ((packetHeader[1] & 0x8000) != 0) // Read answers + { #ifdef DEBUG_ESP_MDNS_RX - DEBUG_ESP_PORT.printf("Reading answers RX: REQ, ID:%u, Q:%u, A:%u, NS:%u, ADD:%u\n", packetHeader[0], packetHeader[2], packetHeader[3], packetHeader[4], packetHeader[5]); + DEBUG_ESP_PORT.printf("Reading answers RX: REQ, ID:%u, Q:%u, A:%u, NS:%u, ADD:%u\n", packetHeader[0], packetHeader[2], packetHeader[3], packetHeader[4], packetHeader[5]); #endif - if (!_waitingForAnswers) { + if (!_waitingForAnswers) + { #ifdef DEBUG_ESP_MDNS_RX - DEBUG_ESP_PORT.println("Not expecting any answers right now, returning"); + DEBUG_ESP_PORT.println("Not expecting any answers right now, returning"); #endif - _conn->flush(); - return; - } + _conn->flush(); + return; + } - int numAnswers = packetHeader[3] + packetHeader[5]; - // Assume that the PTR answer always comes first and that it is always accompanied by a TXT, SRV, AAAA (optional) and A answer in the same packet. - if (numAnswers < 4) { + int numAnswers = packetHeader[3] + packetHeader[5]; + // Assume that the PTR answer always comes first and that it is always accompanied by a TXT, SRV, AAAA (optional) and A answer in the same packet. + if (numAnswers < 4) + { #ifdef DEBUG_ESP_MDNS_RX - DEBUG_ESP_PORT.printf("Expected a packet with 4 or more answers, got %u\n", numAnswers); + DEBUG_ESP_PORT.printf("Expected a packet with 4 or more answers, got %u\n", numAnswers); #endif - _conn->flush(); - return; - } - - uint8_t tmp8; - uint16_t answerPort = 0; - uint8_t answerIp[4] = { 0,0,0,0 }; - char answerHostName[255]; - bool serviceMatch = false; - MDNSAnswer *answer; - uint8_t partsCollected = 0; - uint8_t stringsRead = 0; + _conn->flush(); + return; + } - answerHostName[0] = '\0'; + uint8_t tmp8; + uint16_t answerPort = 0; + uint8_t answerIp[4] = { 0, 0, 0, 0 }; + char answerHostName[255]; + bool serviceMatch = false; + MDNSAnswer *answer; + uint8_t partsCollected = 0; + uint8_t stringsRead = 0; - // Clear answer list - if (_newQuery) { - int oldAnswers = _getNumAnswers(); - for (int n = oldAnswers - 1; n >= 0; n--) { - answer = _getAnswerFromIdx(n); - os_free(answer->hostname); - os_free(answer); - answer = 0; - } - _answers = 0; - _newQuery = false; - } + answerHostName[0] = '\0'; - while (numAnswers--) { - // Read name - stringsRead = 0; - size_t last_bufferpos = 0; - do { - tmp8 = _conn_read8(); - if (tmp8 == 0x00) { // End of name - break; + // Clear answer list + if (_newQuery) + { + int oldAnswers = _getNumAnswers(); + for (int n = oldAnswers - 1; n >= 0; n--) + { + answer = _getAnswerFromIdx(n); + os_free(answer->hostname); + os_free(answer); + answer = 0; + } + _answers = 0; + _newQuery = false; } - if (tmp8 & 0xC0) { // Compressed pointer - uint16_t offset = ((((uint16_t)tmp8) & ~0xC0) << 8) | _conn_read8(); - if (_conn->isValidOffset(offset)) { - if (0 == last_bufferpos) - last_bufferpos = _conn->tell(); + + while (numAnswers--) + { + // Read name + stringsRead = 0; + size_t last_bufferpos = 0; + do + { + tmp8 = _conn_read8(); + if (tmp8 == 0x00) // End of name + { + break; + } + if (tmp8 & 0xC0) // Compressed pointer + { + uint16_t offset = ((((uint16_t)tmp8) & ~0xC0) << 8) | _conn_read8(); + if (_conn->isValidOffset(offset)) + { + if (0 == last_bufferpos) + { + last_bufferpos = _conn->tell(); + } #ifdef DEBUG_ESP_MDNS_RX - DEBUG_ESP_PORT.print("Compressed pointer, jumping from "); - DEBUG_ESP_PORT.print(last_bufferpos); - DEBUG_ESP_PORT.print(" to "); - DEBUG_ESP_PORT.println(offset); + DEBUG_ESP_PORT.print("Compressed pointer, jumping from "); + DEBUG_ESP_PORT.print(last_bufferpos); + DEBUG_ESP_PORT.print(" to "); + DEBUG_ESP_PORT.println(offset); #endif - _conn->seek(offset); - tmp8 = _conn_read8(); - } - else { + _conn->seek(offset); + tmp8 = _conn_read8(); + } + else + { #ifdef DEBUG_ESP_MDNS_RX - DEBUG_ESP_PORT.print("Skipping malformed compressed pointer"); + DEBUG_ESP_PORT.print("Skipping malformed compressed pointer"); #endif - tmp8 = _conn_read8(); - break; - } - } - if(stringsRead > 3){ + tmp8 = _conn_read8(); + break; + } + } + if (stringsRead > 3) + { #ifdef DEBUG_ESP_MDNS_RX - DEBUG_ESP_PORT.println("failed to read the response name"); + DEBUG_ESP_PORT.println("failed to read the response name"); #endif - _conn->flush(); - return; - } - _conn_readS(serviceName, tmp8); - serviceName[tmp8] = '\0'; + _conn->flush(); + return; + } + _conn_readS(serviceName, tmp8); + serviceName[tmp8] = '\0'; #ifdef DEBUG_ESP_MDNS_RX - DEBUG_ESP_PORT.printf(" %d ", tmp8); - for (int n = 0; n < tmp8; n++) { - DEBUG_ESP_PORT.printf("%c", serviceName[n]); - } - DEBUG_ESP_PORT.println(); + DEBUG_ESP_PORT.printf(" %d ", tmp8); + for (int n = 0; n < tmp8; n++) + { + DEBUG_ESP_PORT.printf("%c", serviceName[n]); + } + DEBUG_ESP_PORT.println(); #endif - if (serviceName[0] == '_') { - if (strcmp(&serviceName[1], _query->_service) == 0) { - serviceMatch = true; + if (serviceName[0] == '_') + { + if (strcmp(&serviceName[1], _query->_service) == 0) + { + serviceMatch = true; #ifdef DEBUG_ESP_MDNS_RX - DEBUG_ESP_PORT.printf("found matching service: %s\n", _query->_service); + DEBUG_ESP_PORT.printf("found matching service: %s\n", _query->_service); #endif - } - } - stringsRead++; - } while (true); - if (last_bufferpos > 0) - { - _conn->seek(last_bufferpos); + } + } + stringsRead++; + } while (true); + if (last_bufferpos > 0) + { + _conn->seek(last_bufferpos); #ifdef DEBUG_ESP_MDNS_RX - DEBUG_ESP_PORT.print("Compressed pointer, jumping back to "); - DEBUG_ESP_PORT.println(last_bufferpos); + DEBUG_ESP_PORT.print("Compressed pointer, jumping back to "); + DEBUG_ESP_PORT.println(last_bufferpos); #endif - } - - uint16_t answerType = _conn_read16(); // Read type - uint16_t answerClass = _conn_read16(); // Read class - uint32_t answerTtl = _conn_read32(); // Read ttl - uint16_t answerRdlength = _conn_read16(); // Read rdlength - - (void) answerClass; - (void) answerTtl; - - if(answerRdlength > 255){ - if(answerType == MDNS_TYPE_TXT && answerRdlength < 1460){ - while(--answerRdlength) _conn->read(); - } else { + } + + uint16_t answerType = _conn_read16(); // Read type + uint16_t answerClass = _conn_read16(); // Read class + uint32_t answerTtl = _conn_read32(); // Read ttl + uint16_t answerRdlength = _conn_read16(); // Read rdlength + + (void) answerClass; + (void) answerTtl; + + if (answerRdlength > 255) + { + if (answerType == MDNS_TYPE_TXT && answerRdlength < 1460) + { + while (--answerRdlength) + { + _conn->read(); + } + } + else + { #ifdef DEBUG_ESP_MDNS_RX - DEBUG_ESP_PORT.printf("Data len too long! %u\n", answerRdlength); + DEBUG_ESP_PORT.printf("Data len too long! %u\n", answerRdlength); #endif - _conn->flush(); - return; - } - } + _conn->flush(); + return; + } + } #ifdef DEBUG_ESP_MDNS_RX - DEBUG_ESP_PORT.printf("type: %04x rdlength: %d\n", answerType, answerRdlength); + DEBUG_ESP_PORT.printf("type: %04x rdlength: %d\n", answerType, answerRdlength); #endif - if (answerType == MDNS_TYPE_PTR) { - partsCollected |= 0x01; - _conn_readS(hostName, answerRdlength); // Read rdata - if(hostName[answerRdlength-2] & 0xc0){ - memcpy(answerHostName, hostName+1, answerRdlength-3); - answerHostName[answerRdlength-3] = '\0'; - } + if (answerType == MDNS_TYPE_PTR) + { + partsCollected |= 0x01; + _conn_readS(hostName, answerRdlength); // Read rdata + if (hostName[answerRdlength - 2] & 0xc0) + { + memcpy(answerHostName, hostName + 1, answerRdlength - 3); + answerHostName[answerRdlength - 3] = '\0'; + } #ifdef DEBUG_ESP_MDNS_RX - DEBUG_ESP_PORT.printf("PTR %d ", answerRdlength); - for (int n = 0; n < answerRdlength; n++) { - DEBUG_ESP_PORT.printf("%c", hostName[n]); - } - DEBUG_ESP_PORT.println(); + DEBUG_ESP_PORT.printf("PTR %d ", answerRdlength); + for (int n = 0; n < answerRdlength; n++) + { + DEBUG_ESP_PORT.printf("%c", hostName[n]); + } + DEBUG_ESP_PORT.println(); #endif - } + } - else if (answerType == MDNS_TYPE_TXT) { - partsCollected |= 0x02; - _conn_readS(hostName, answerRdlength); // Read rdata + else if (answerType == MDNS_TYPE_TXT) + { + partsCollected |= 0x02; + _conn_readS(hostName, answerRdlength); // Read rdata #ifdef DEBUG_ESP_MDNS_RX - DEBUG_ESP_PORT.printf("TXT %d ", answerRdlength); - for (int n = 0; n < answerRdlength; n++) { - DEBUG_ESP_PORT.printf("%c", hostName[n]); - } - DEBUG_ESP_PORT.println(); + DEBUG_ESP_PORT.printf("TXT %d ", answerRdlength); + for (int n = 0; n < answerRdlength; n++) + { + DEBUG_ESP_PORT.printf("%c", hostName[n]); + } + DEBUG_ESP_PORT.println(); #endif - } - - else if (answerType == MDNS_TYPE_SRV) { - partsCollected |= 0x04; - uint16_t answerPrio = _conn_read16(); // Read priority - uint16_t answerWeight = _conn_read16(); // Read weight - answerPort = _conn_read16(); // Read port - last_bufferpos = 0; - - (void) answerPrio; - (void) answerWeight; - - // Read hostname - tmp8 = _conn_read8(); - if (tmp8 & 0xC0) { // Compressed pointer - uint16_t offset = ((((uint16_t)tmp8) & ~0xC0) << 8) | _conn_read8(); - if (_conn->isValidOffset(offset)) { - last_bufferpos = _conn->tell(); + } + + else if (answerType == MDNS_TYPE_SRV) + { + partsCollected |= 0x04; + uint16_t answerPrio = _conn_read16(); // Read priority + uint16_t answerWeight = _conn_read16(); // Read weight + answerPort = _conn_read16(); // Read port + last_bufferpos = 0; + + (void) answerPrio; + (void) answerWeight; + + // Read hostname + tmp8 = _conn_read8(); + if (tmp8 & 0xC0) // Compressed pointer + { + uint16_t offset = ((((uint16_t)tmp8) & ~0xC0) << 8) | _conn_read8(); + if (_conn->isValidOffset(offset)) + { + last_bufferpos = _conn->tell(); #ifdef DEBUG_ESP_MDNS_RX - DEBUG_ESP_PORT.print("Compressed pointer, jumping from "); - DEBUG_ESP_PORT.print(last_bufferpos); - DEBUG_ESP_PORT.print(" to "); - DEBUG_ESP_PORT.println(offset); + DEBUG_ESP_PORT.print("Compressed pointer, jumping from "); + DEBUG_ESP_PORT.print(last_bufferpos); + DEBUG_ESP_PORT.print(" to "); + DEBUG_ESP_PORT.println(offset); #endif - _conn->seek(offset); - tmp8 = _conn_read8(); - } - else { + _conn->seek(offset); + tmp8 = _conn_read8(); + } + else + { #ifdef DEBUG_ESP_MDNS_RX - DEBUG_ESP_PORT.print("Skipping malformed compressed pointer"); + DEBUG_ESP_PORT.print("Skipping malformed compressed pointer"); #endif - tmp8 = _conn_read8(); - break; - } - } - _conn_readS(answerHostName, tmp8); - answerHostName[tmp8] = '\0'; + tmp8 = _conn_read8(); + break; + } + } + _conn_readS(answerHostName, tmp8); + answerHostName[tmp8] = '\0'; #ifdef DEBUG_ESP_MDNS_RX - DEBUG_ESP_PORT.printf("SRV %d ", tmp8); - for (int n = 0; n < tmp8; n++) { - DEBUG_ESP_PORT.printf("%02x ", answerHostName[n]); - } - DEBUG_ESP_PORT.printf("\n%s\n", answerHostName); + DEBUG_ESP_PORT.printf("SRV %d ", tmp8); + for (int n = 0; n < tmp8; n++) + { + DEBUG_ESP_PORT.printf("%02x ", answerHostName[n]); + } + DEBUG_ESP_PORT.printf("\n%s\n", answerHostName); #endif - if (last_bufferpos > 0) - { - _conn->seek(last_bufferpos); - tmp8 = 2; // Size of compression octets + if (last_bufferpos > 0) + { + _conn->seek(last_bufferpos); + tmp8 = 2; // Size of compression octets #ifdef DEBUG_ESP_MDNS_RX - DEBUG_ESP_PORT.print("Compressed pointer, jumping back to "); - DEBUG_ESP_PORT.println(last_bufferpos); + DEBUG_ESP_PORT.print("Compressed pointer, jumping back to "); + DEBUG_ESP_PORT.println(last_bufferpos); #endif - } - if (answerRdlength - (6 + 1 + tmp8) > 0) { // Skip any remaining rdata - _conn_readS(hostName, answerRdlength - (6 + 1 + tmp8)); - } - } - - else if (answerType == MDNS_TYPE_A) { - partsCollected |= 0x08; - for (int i = 0; i < 4; i++) { - answerIp[i] = _conn_read8(); - } - } - else { + } + if (answerRdlength - (6 + 1 + tmp8) > 0) // Skip any remaining rdata + { + _conn_readS(hostName, answerRdlength - (6 + 1 + tmp8)); + } + } + + else if (answerType == MDNS_TYPE_A) + { + partsCollected |= 0x08; + for (int i = 0; i < 4; i++) + { + answerIp[i] = _conn_read8(); + } + } + else + { #ifdef DEBUG_ESP_MDNS_RX - DEBUG_ESP_PORT.printf("Ignoring unsupported type %02x\n", tmp8); + DEBUG_ESP_PORT.printf("Ignoring unsupported type %02x\n", tmp8); #endif - for (int n = 0; n < answerRdlength; n++) - (void)_conn_read8(); - } - - if ((partsCollected == 0x0F) && serviceMatch) { + for (int n = 0; n < answerRdlength; n++) + { + (void)_conn_read8(); + } + } + + if ((partsCollected == 0x0F) && serviceMatch) + { #ifdef DEBUG_ESP_MDNS_RX - DEBUG_ESP_PORT.println("All answers parsed, adding to _answers list.."); + DEBUG_ESP_PORT.println("All answers parsed, adding to _answers list.."); #endif - // Add new answer to answer list - if (_answers == 0) { - _answers = (struct MDNSAnswer*)(os_malloc(sizeof(struct MDNSAnswer))); - answer = _answers; + // Add new answer to answer list + if (_answers == 0) + { + _answers = (struct MDNSAnswer*)(os_malloc(sizeof(struct MDNSAnswer))); + answer = _answers; + } + else + { + answer = _answers; + while (answer->next != 0) + { + answer = answer->next; + } + answer->next = (struct MDNSAnswer*)(os_malloc(sizeof(struct MDNSAnswer))); + answer = answer->next; + } + answer->next = 0; + answer->hostname = 0; + + // Populate new answer + answer->port = answerPort; + for (int i = 0; i < 4; i++) + { + answer->ip[i] = answerIp[i]; + } + answer->hostname = (char *)os_malloc(strlen(answerHostName) + 1); + os_strcpy(answer->hostname, answerHostName); + _conn->flush(); + return; + } } - else { - answer = _answers; - while (answer->next != 0) { - answer = answer->next; - } - answer->next = (struct MDNSAnswer*)(os_malloc(sizeof(struct MDNSAnswer))); - answer = answer->next; - } - answer->next = 0; - answer->hostname = 0; - // Populate new answer - answer->port = answerPort; - for (int i = 0; i < 4; i++) { - answer->ip[i] = answerIp[i]; - } - answer->hostname = (char *)os_malloc(strlen(answerHostName) + 1); - os_strcpy(answer->hostname, answerHostName); _conn->flush(); return; - } } - - _conn->flush(); - return; - } - // PARSE REQUEST NAME + // PARSE REQUEST NAME - hostNameLen = _conn_read8() % 255; - _conn_readS(hostName, hostNameLen); - hostName[hostNameLen] = '\0'; + hostNameLen = _conn_read8() % 255; + _conn_readS(hostName, hostNameLen); + hostName[hostNameLen] = '\0'; - if(hostName[0] == '_'){ - serviceParsed = true; - memcpy(serviceName, hostName+1, hostNameLen); - serviceNameLen = hostNameLen-1; - hostNameLen = 0; - } - - if(hostNameLen > 0 && !_hostName.equals(hostName) && !_instanceName.equals(hostName)){ -#ifdef DEBUG_ESP_MDNS_ERR - DEBUG_ESP_PORT.printf("ERR_NO_HOST: %s\n", hostName); - DEBUG_ESP_PORT.printf("hostname: %s\n", _hostName.c_str() ); - DEBUG_ESP_PORT.printf("instance: %s\n", _instanceName.c_str() ); -#endif - _conn->flush(); - return; - } - - if(!serviceParsed){ - serviceNameLen = _conn_read8() % 255; - _conn_readS(serviceName, serviceNameLen); - serviceName[serviceNameLen] = '\0'; - - if(serviceName[0] == '_'){ - memmove(serviceName, serviceName+1, serviceNameLen); - serviceNameLen--; - serviceParsed = true; - } else if(serviceNameLen == 5 && strcmp("local", serviceName) == 0){ - tmp = _conn_read8(); - if(tmp == 0){ + if (hostName[0] == '_') + { serviceParsed = true; - serviceNameLen = 0; - protoParsed = true; - protoNameLen = 0; - localParsed = true; - } else { + memcpy(serviceName, hostName + 1, hostNameLen); + serviceNameLen = hostNameLen - 1; + hostNameLen = 0; + } + + if (hostNameLen > 0 && !_hostName.equals(hostName) && !_instanceName.equals(hostName)) + { #ifdef DEBUG_ESP_MDNS_ERR - DEBUG_ESP_PORT.printf("ERR_FQDN: %s\n", serviceName); + DEBUG_ESP_PORT.printf("ERR_NO_HOST: %s\n", hostName); + DEBUG_ESP_PORT.printf("hostname: %s\n", _hostName.c_str()); + DEBUG_ESP_PORT.printf("instance: %s\n", _instanceName.c_str()); #endif _conn->flush(); return; - } - } else { + } + + if (!serviceParsed) + { + serviceNameLen = _conn_read8() % 255; + _conn_readS(serviceName, serviceNameLen); + serviceName[serviceNameLen] = '\0'; + + if (serviceName[0] == '_') + { + memmove(serviceName, serviceName + 1, serviceNameLen); + serviceNameLen--; + serviceParsed = true; + } + else if (serviceNameLen == 5 && strcmp("local", serviceName) == 0) + { + tmp = _conn_read8(); + if (tmp == 0) + { + serviceParsed = true; + serviceNameLen = 0; + protoParsed = true; + protoNameLen = 0; + localParsed = true; + } + else + { +#ifdef DEBUG_ESP_MDNS_ERR + DEBUG_ESP_PORT.printf("ERR_FQDN: %s\n", serviceName); +#endif + _conn->flush(); + return; + } + } + else + { #ifdef DEBUG_ESP_MDNS_ERR - DEBUG_ESP_PORT.printf("ERR_SERVICE: %s\n", serviceName); + DEBUG_ESP_PORT.printf("ERR_SERVICE: %s\n", serviceName); #endif - _conn->flush(); - return; + _conn->flush(); + return; + } } - } - - if(!protoParsed){ - protoNameLen = _conn_read8() % 255; - _conn_readS(protoName, protoNameLen); - protoName[protoNameLen] = '\0'; - if(protoNameLen == 4 && protoName[0] == '_'){ - memmove(protoName, protoName+1, protoNameLen); - protoNameLen--; - protoParsed = true; - } else if(strcmp("services", serviceName) == 0 && strcmp("_dns-sd", protoName) == 0){ - _conn->flush(); - IPAddress interface = _getRequestMulticastInterface(); - _replyToTypeEnumRequest(interface); - return; - } else { + + if (!protoParsed) + { + protoNameLen = _conn_read8() % 255; + _conn_readS(protoName, protoNameLen); + protoName[protoNameLen] = '\0'; + if (protoNameLen == 4 && protoName[0] == '_') + { + memmove(protoName, protoName + 1, protoNameLen); + protoNameLen--; + protoParsed = true; + } + else if (strcmp("services", serviceName) == 0 && strcmp("_dns-sd", protoName) == 0) + { + _conn->flush(); + IPAddress interface = _getRequestMulticastInterface(); + _replyToTypeEnumRequest(interface); + return; + } + else + { #ifdef DEBUG_ESP_MDNS_ERR - DEBUG_ESP_PORT.printf("ERR_PROTO: %s\n", protoName); + DEBUG_ESP_PORT.printf("ERR_PROTO: %s\n", protoName); #endif - _conn->flush(); - return; + _conn->flush(); + return; + } } - } - - if(!localParsed){ - char localName[32]; - uint8_t localNameLen = _conn_read8() % 31; - _conn_readS(localName, localNameLen); - localName[localNameLen] = '\0'; - tmp = _conn_read8(); - if(localNameLen == 5 && strcmp("local", localName) == 0 && tmp == 0){ - localParsed = true; - } else { + + if (!localParsed) + { + char localName[32]; + uint8_t localNameLen = _conn_read8() % 31; + _conn_readS(localName, localNameLen); + localName[localNameLen] = '\0'; + tmp = _conn_read8(); + if (localNameLen == 5 && strcmp("local", localName) == 0 && tmp == 0) + { + localParsed = true; + } + else + { #ifdef DEBUG_ESP_MDNS_ERR - DEBUG_ESP_PORT.printf("ERR_FQDN: %s\n", localName); + DEBUG_ESP_PORT.printf("ERR_FQDN: %s\n", localName); #endif - _conn->flush(); - return; + _conn->flush(); + return; + } } - } - if(serviceNameLen > 0 && protoNameLen > 0){ - servicePort = _getServicePort(serviceName, protoName); - if(servicePort == 0){ + if (serviceNameLen > 0 && protoNameLen > 0) + { + servicePort = _getServicePort(serviceName, protoName); + if (servicePort == 0) + { #ifdef DEBUG_ESP_MDNS_ERR - DEBUG_ESP_PORT.printf("ERR_NO_SERVICE: %s\n", serviceName); + DEBUG_ESP_PORT.printf("ERR_NO_SERVICE: %s\n", serviceName); #endif - _conn->flush(); - return; + _conn->flush(); + return; + } } - } else if(serviceNameLen > 0 || protoNameLen > 0){ + else if (serviceNameLen > 0 || protoNameLen > 0) + { #ifdef DEBUG_ESP_MDNS_ERR - DEBUG_ESP_PORT.printf("ERR_SERVICE_PROTO: %s\n", serviceName); + DEBUG_ESP_PORT.printf("ERR_SERVICE_PROTO: %s\n", serviceName); #endif - _conn->flush(); - return; - } + _conn->flush(); + return; + } - // RESPOND + // RESPOND #ifdef DEBUG_ESP_MDNS_RX - DEBUG_ESP_PORT.printf("RX: REQ, ID:%u, Q:%u, A:%u, NS:%u, ADD:%u\n", packetHeader[0], packetHeader[2], packetHeader[3], packetHeader[4], packetHeader[5]); + DEBUG_ESP_PORT.printf("RX: REQ, ID:%u, Q:%u, A:%u, NS:%u, ADD:%u\n", packetHeader[0], packetHeader[2], packetHeader[3], packetHeader[4], packetHeader[5]); #endif - uint16_t currentType; - uint16_t currentClass; - - int numQuestions = packetHeader[2]; - if(numQuestions > 4) numQuestions = 4; - uint16_t questions[4]; - int question = 0; + uint16_t currentType; + uint16_t currentClass; - while(numQuestions--){ - currentType = _conn_read16(); - if(currentType & MDNS_NAME_REF){ //new header handle it better! - currentType = _conn_read16(); + int numQuestions = packetHeader[2]; + if (numQuestions > 4) + { + numQuestions = 4; } - currentClass = _conn_read16(); - if(currentClass & MDNS_CLASS_IN) questions[question++] = currentType; + uint16_t questions[4]; + int question = 0; - if(numQuestions > 0){ - if(_conn_read16() != 0xC00C){//new question but for another host/service - _conn->flush(); - numQuestions = 0; - } - } + while (numQuestions--) + { + currentType = _conn_read16(); + if (currentType & MDNS_NAME_REF) //new header handle it better! + { + currentType = _conn_read16(); + } + currentClass = _conn_read16(); + if (currentClass & MDNS_CLASS_IN) + { + questions[question++] = currentType; + } + + if (numQuestions > 0) + { + if (_conn_read16() != 0xC00C) //new question but for another host/service + { + _conn->flush(); + numQuestions = 0; + } + } #ifdef DEBUG_ESP_MDNS_RX - DEBUG_ESP_PORT.printf("REQ: "); - if(hostNameLen > 0) - DEBUG_ESP_PORT.printf("%s.", hostName); - if(serviceNameLen > 0) - DEBUG_ESP_PORT.printf("_%s.", serviceName); - if(protoNameLen > 0) - DEBUG_ESP_PORT.printf("_%s.", protoName); - DEBUG_ESP_PORT.printf("local. "); - - if(currentType == MDNS_TYPE_AAAA) - DEBUG_ESP_PORT.printf(" AAAA "); - else if(currentType == MDNS_TYPE_A) - DEBUG_ESP_PORT.printf(" A "); - else if(currentType == MDNS_TYPE_PTR) - DEBUG_ESP_PORT.printf(" PTR "); - else if(currentType == MDNS_TYPE_SRV) - DEBUG_ESP_PORT.printf(" SRV "); - else if(currentType == MDNS_TYPE_TXT) - DEBUG_ESP_PORT.printf(" TXT "); - else - DEBUG_ESP_PORT.printf(" 0x%04X ", currentType); - - if(currentClass == MDNS_CLASS_IN) - DEBUG_ESP_PORT.printf(" IN "); - else if(currentClass == MDNS_CLASS_IN_FLUSH_CACHE) - DEBUG_ESP_PORT.printf(" IN[F] "); - else - DEBUG_ESP_PORT.printf(" 0x%04X ", currentClass); - - DEBUG_ESP_PORT.printf("\n"); + DEBUG_ESP_PORT.printf("REQ: "); + if (hostNameLen > 0) + { + DEBUG_ESP_PORT.printf("%s.", hostName); + } + if (serviceNameLen > 0) + { + DEBUG_ESP_PORT.printf("_%s.", serviceName); + } + if (protoNameLen > 0) + { + DEBUG_ESP_PORT.printf("_%s.", protoName); + } + DEBUG_ESP_PORT.printf("local. "); + + if (currentType == MDNS_TYPE_AAAA) + { + DEBUG_ESP_PORT.printf(" AAAA "); + } + else if (currentType == MDNS_TYPE_A) + { + DEBUG_ESP_PORT.printf(" A "); + } + else if (currentType == MDNS_TYPE_PTR) + { + DEBUG_ESP_PORT.printf(" PTR "); + } + else if (currentType == MDNS_TYPE_SRV) + { + DEBUG_ESP_PORT.printf(" SRV "); + } + else if (currentType == MDNS_TYPE_TXT) + { + DEBUG_ESP_PORT.printf(" TXT "); + } + else + { + DEBUG_ESP_PORT.printf(" 0x%04X ", currentType); + } + + if (currentClass == MDNS_CLASS_IN) + { + DEBUG_ESP_PORT.printf(" IN "); + } + else if (currentClass == MDNS_CLASS_IN_FLUSH_CACHE) + { + DEBUG_ESP_PORT.printf(" IN[F] "); + } + else + { + DEBUG_ESP_PORT.printf(" 0x%04X ", currentClass); + } + + DEBUG_ESP_PORT.printf("\n"); #endif - } - uint8_t questionMask = 0; - uint8_t responseMask = 0; - for(i=0;i_next) { - if(servicePtr->_port > 0){ - char *service = servicePtr->_name; - char *proto = servicePtr->_proto; - //uint16_t port = servicePtr->_port; +void MDNSResponder::_replyToTypeEnumRequest(IPAddress multicastInterface) +{ + MDNSService* servicePtr; + for (servicePtr = _services; servicePtr; servicePtr = servicePtr->_next) + { + if (servicePtr->_port > 0) + { + char *service = servicePtr->_name; + char *proto = servicePtr->_proto; + //uint16_t port = servicePtr->_port; #ifdef DEBUG_ESP_MDNS_TX - DEBUG_ESP_PORT.printf("TX: service:%s, proto:%s\n", service, proto); + DEBUG_ESP_PORT.printf("TX: service:%s, proto:%s\n", service, proto); #endif - char sdHostName[] = "_services"; - size_t sdHostNameLen = 9; - char sdServiceName[] = "_dns-sd"; - size_t sdServiceNameLen = 7; - char sdProtoName[] = "_udp"; - size_t sdProtoNameLen = 4; - - char underscore[] = "_"; - - // build service name with _ - char serviceName[os_strlen(service) + 2]; - os_strcpy(serviceName, underscore); - os_strcat(serviceName, service); - size_t serviceNameLen = os_strlen(serviceName); - - //build proto name with _ - char protoName[5]; - os_strcpy(protoName, underscore); - os_strcat(protoName, proto); - size_t protoNameLen = 4; - - //local string - char localName[] = "local"; - size_t localNameLen = 5; - - //terminator - char terminator[] = "\0"; - - //Write the header - _conn->flush(); - uint8_t head[12] = { - 0x00, 0x00, //ID = 0 - 0x84, 0x00, //Flags = response + authoritative answer - 0x00, 0x00, //Question count - 0x00, 0x01, //Answer count - 0x00, 0x00, //Name server records - 0x00, 0x00, //Additional records - }; - _conn->append(reinterpret_cast(head), 12); - - // Send the Name field (ie. "_services._dns-sd._udp.local") - _conn->append(reinterpret_cast(&sdHostNameLen), 1); // length of "_services" - _conn->append(reinterpret_cast(sdHostName), sdHostNameLen); // "_services" - _conn->append(reinterpret_cast(&sdServiceNameLen), 1); // length of "_dns-sd" - _conn->append(reinterpret_cast(sdServiceName), sdServiceNameLen);// "_dns-sd" - _conn->append(reinterpret_cast(&sdProtoNameLen), 1); // length of "_udp" - _conn->append(reinterpret_cast(sdProtoName), sdProtoNameLen); // "_udp" - _conn->append(reinterpret_cast(&localNameLen), 1); // length "local" - _conn->append(reinterpret_cast(localName), localNameLen); // "local" - _conn->append(reinterpret_cast(&terminator), 1); // terminator - - //Send the type, class, ttl and rdata length - uint8_t ptrDataLen = serviceNameLen + protoNameLen + localNameLen + 4; // 4 is three label sizes and the terminator - uint8_t ptrAttrs[10] = { - 0x00, 0x0c, //PTR record query - 0x00, 0x01, //Class IN - 0x00, 0x00, 0x11, 0x94, //TTL 4500 - 0x00, ptrDataLen, //RData length - }; - _conn->append(reinterpret_cast(ptrAttrs), 10); - - //Send the RData (ie. "_http._tcp.local") - _conn->append(reinterpret_cast(&serviceNameLen), 1); // length of "_http" - _conn->append(reinterpret_cast(serviceName), serviceNameLen); // "_http" - _conn->append(reinterpret_cast(&protoNameLen), 1); // length of "_tcp" - _conn->append(reinterpret_cast(protoName), protoNameLen); // "_tcp" - _conn->append(reinterpret_cast(&localNameLen), 1); // length "local" - _conn->append(reinterpret_cast(localName), localNameLen); // "local" - _conn->append(reinterpret_cast(&terminator), 1); // terminator - - _conn->setMulticastInterface(multicastInterface); - _conn->send(); + char sdHostName[] = "_services"; + size_t sdHostNameLen = 9; + char sdServiceName[] = "_dns-sd"; + size_t sdServiceNameLen = 7; + char sdProtoName[] = "_udp"; + size_t sdProtoNameLen = 4; + + char underscore[] = "_"; + + // build service name with _ + char serviceName[os_strlen(service) + 2]; + os_strcpy(serviceName, underscore); + os_strcat(serviceName, service); + size_t serviceNameLen = os_strlen(serviceName); + + //build proto name with _ + char protoName[5]; + os_strcpy(protoName, underscore); + os_strcat(protoName, proto); + size_t protoNameLen = 4; + + //local string + char localName[] = "local"; + size_t localNameLen = 5; + + //terminator + char terminator[] = "\0"; + + //Write the header + _conn->flush(); + uint8_t head[12] = + { + 0x00, 0x00, //ID = 0 + 0x84, 0x00, //Flags = response + authoritative answer + 0x00, 0x00, //Question count + 0x00, 0x01, //Answer count + 0x00, 0x00, //Name server records + 0x00, 0x00, //Additional records + }; + _conn->append(reinterpret_cast(head), 12); + + // Send the Name field (ie. "_services._dns-sd._udp.local") + _conn->append(reinterpret_cast(&sdHostNameLen), 1); // length of "_services" + _conn->append(reinterpret_cast(sdHostName), sdHostNameLen); // "_services" + _conn->append(reinterpret_cast(&sdServiceNameLen), 1); // length of "_dns-sd" + _conn->append(reinterpret_cast(sdServiceName), sdServiceNameLen);// "_dns-sd" + _conn->append(reinterpret_cast(&sdProtoNameLen), 1); // length of "_udp" + _conn->append(reinterpret_cast(sdProtoName), sdProtoNameLen); // "_udp" + _conn->append(reinterpret_cast(&localNameLen), 1); // length "local" + _conn->append(reinterpret_cast(localName), localNameLen); // "local" + _conn->append(reinterpret_cast(&terminator), 1); // terminator + + //Send the type, class, ttl and rdata length + uint8_t ptrDataLen = serviceNameLen + protoNameLen + localNameLen + 4; // 4 is three label sizes and the terminator + uint8_t ptrAttrs[10] = + { + 0x00, 0x0c, //PTR record query + 0x00, 0x01, //Class IN + 0x00, 0x00, 0x11, 0x94, //TTL 4500 + 0x00, ptrDataLen, //RData length + }; + _conn->append(reinterpret_cast(ptrAttrs), 10); + + //Send the RData (ie. "_http._tcp.local") + _conn->append(reinterpret_cast(&serviceNameLen), 1); // length of "_http" + _conn->append(reinterpret_cast(serviceName), serviceNameLen); // "_http" + _conn->append(reinterpret_cast(&protoNameLen), 1); // length of "_tcp" + _conn->append(reinterpret_cast(protoName), protoNameLen); // "_tcp" + _conn->append(reinterpret_cast(&localNameLen), 1); // length "local" + _conn->append(reinterpret_cast(localName), localNameLen); // "local" + _conn->append(reinterpret_cast(&terminator), 1); // terminator + + _conn->setMulticastInterface(multicastInterface); + _conn->send(); + } } - } } -void MDNSResponder::_replyToInstanceRequest(uint8_t questionMask, uint8_t responseMask, char * service, char *proto, uint16_t port, IPAddress multicastInterface) { - int i; - if(questionMask == 0) return; - if(responseMask == 0) return; +void MDNSResponder::_replyToInstanceRequest(uint8_t questionMask, uint8_t responseMask, char * service, char *proto, uint16_t port, IPAddress multicastInterface) +{ + int i; + if (questionMask == 0) + { + return; + } + if (responseMask == 0) + { + return; + } #ifdef DEBUG_ESP_MDNS_TX DEBUG_ESP_PORT.printf("TX: qmask:%01X, rmask:%01X, service:%s, proto:%s, port:%u\n", questionMask, responseMask, service, proto, port); #endif - String instanceName = _instanceName; - size_t instanceNameLen = instanceName.length(); - - String hostName = _hostName; - size_t hostNameLen = hostName.length(); - - char underscore[] = "_"; - - // build service name with _ - char serviceName[os_strlen(service)+2]; - os_strcpy(serviceName,underscore); - os_strcat(serviceName, service); - size_t serviceNameLen = os_strlen(serviceName); - - //build proto name with _ - char protoName[5]; - os_strcpy(protoName,underscore); - os_strcat(protoName, proto); - size_t protoNameLen = 4; - - //local string - char localName[] = "local"; - size_t localNameLen = 5; - - //terminator - char terminator[] = "\0"; - - uint8_t answerMask = responseMask & questionMask; - uint8_t answerCount = 0; - uint8_t additionalMask = responseMask & ~questionMask; - uint8_t additionalCount = 0; - for(i=0;i<4;i++){ - if(answerMask & (1 << i)) - answerCount++; - if(additionalMask & (1 << i)) - additionalCount++; - } - - - //Write the header - _conn->flush(); - uint8_t head[12] = { - 0x00, 0x00, //ID = 0 - 0x84, 0x00, //Flags = response + authoritative answer - 0x00, 0x00, //Question count - 0x00, answerCount, //Answer count - 0x00, 0x00, //Name server records - 0x00, additionalCount, //Additional records - }; - _conn->append(reinterpret_cast(head), 12); - - for(int responseSection = 0; responseSection < 2; ++responseSection) { - - // PTR Response - if((responseSection == 0 ? answerMask : additionalMask) & 0x8){ - // Send the Name field (ie. "_http._tcp.local") - _conn->append(reinterpret_cast(&serviceNameLen), 1); // length of "_http" - _conn->append(reinterpret_cast(serviceName), serviceNameLen); // "_http" - _conn->append(reinterpret_cast(&protoNameLen), 1); // length of "_tcp" - _conn->append(reinterpret_cast(protoName), protoNameLen); // "_tcp" - _conn->append(reinterpret_cast(&localNameLen), 1); // length "local" - _conn->append(reinterpret_cast(localName), localNameLen); // "local" - _conn->append(reinterpret_cast(&terminator), 1); // terminator - - //Send the type, class, ttl and rdata length - uint8_t ptrDataLen = instanceNameLen + serviceNameLen + protoNameLen + localNameLen + 5; // 5 is four label sizes and the terminator - uint8_t ptrAttrs[10] = { - 0x00, 0x0c, //PTR record query - 0x00, 0x01, //Class IN - 0x00, 0x00, 0x00, 0x78, //TTL 120 - 0x00, ptrDataLen, //RData length - }; - _conn->append(reinterpret_cast(ptrAttrs), 10); - - //Send the RData (ie. "My IOT device._http._tcp.local") - _conn->append(reinterpret_cast(&instanceNameLen), 1); // length of "My IOT device" - _conn->append(reinterpret_cast(instanceName.c_str()), instanceNameLen);// "My IOT device" - _conn->append(reinterpret_cast(&serviceNameLen), 1); // length of "_http" - _conn->append(reinterpret_cast(serviceName), serviceNameLen); // "_http" - _conn->append(reinterpret_cast(&protoNameLen), 1); // length of "_tcp" - _conn->append(reinterpret_cast(protoName), protoNameLen); // "_tcp" - _conn->append(reinterpret_cast(&localNameLen), 1); // length "local" - _conn->append(reinterpret_cast(localName), localNameLen); // "local" - _conn->append(reinterpret_cast(&terminator), 1); // terminator - } + String instanceName = _instanceName; + size_t instanceNameLen = instanceName.length(); - //TXT Responce - if((responseSection == 0 ? answerMask : additionalMask) & 0x4){ - //Send the name field (ie. "My IOT device._http._tcp.local") - _conn->append(reinterpret_cast(&instanceNameLen), 1); // length of "My IOT device" - _conn->append(reinterpret_cast(instanceName.c_str()), instanceNameLen);// "My IOT device" - _conn->append(reinterpret_cast(&serviceNameLen), 1); // length of "_http" - _conn->append(reinterpret_cast(serviceName), serviceNameLen); // "_http" - _conn->append(reinterpret_cast(&protoNameLen), 1); // length of "_tcp" - _conn->append(reinterpret_cast(protoName), protoNameLen); // "_tcp" - _conn->append(reinterpret_cast(&localNameLen), 1); // length "local" - _conn->append(reinterpret_cast(localName), localNameLen); // "local" - _conn->append(reinterpret_cast(&terminator), 1); // terminator - - //Send the type, class, ttl and rdata length - uint8_t txtDataLen = _getServiceTxtLen(service,proto); - uint8_t txtAttrs[10] = { - 0x00, 0x10, //TXT record query - 0x80, 0x01, //Class IN, with cache flush - 0x00, 0x00, 0x11, 0x94, //TTL 4500 - 0x00, txtDataLen, //RData length - }; - _conn->append(reinterpret_cast(txtAttrs), 10); - - //Send the RData - MDNSTxt * txtPtr = _getServiceTxt(service,proto); - while(txtPtr !=0){ - uint8_t txtLen = txtPtr->_txt.length(); - _conn->append(reinterpret_cast(&txtLen), 1); // length of txt - _conn->append(reinterpret_cast(txtPtr->_txt.c_str()), txtLen);// the txt - txtPtr = txtPtr->_next; - } - } + String hostName = _hostName; + size_t hostNameLen = hostName.length(); + char underscore[] = "_"; - //SRV Responce - if((responseSection == 0 ? answerMask : additionalMask) & 0x2){ - //Send the name field (ie. "My IOT device._http._tcp.local") - _conn->append(reinterpret_cast(&instanceNameLen), 1); // length of "My IOT device" - _conn->append(reinterpret_cast(instanceName.c_str()), instanceNameLen);// "My IOT device" - _conn->append(reinterpret_cast(&serviceNameLen), 1); // length of "_http" - _conn->append(reinterpret_cast(serviceName), serviceNameLen); // "_http" - _conn->append(reinterpret_cast(&protoNameLen), 1); // length of "_tcp" - _conn->append(reinterpret_cast(protoName), protoNameLen); // "_tcp" - _conn->append(reinterpret_cast(&localNameLen), 1); // length "local" - _conn->append(reinterpret_cast(localName), localNameLen); // "local" - _conn->append(reinterpret_cast(&terminator), 1); // terminator - - //Send the type, class, ttl, rdata length, priority and weight - uint8_t srvDataSize = hostNameLen + localNameLen + 3; // 3 is 2 lable size bytes and the terminator - srvDataSize += 6; // Size of Priority, weight and port - uint8_t srvAttrs[10] = { - 0x00, 0x21, //Type SRV - 0x80, 0x01, //Class IN, with cache flush - 0x00, 0x00, 0x00, 0x78, //TTL 120 - 0x00, srvDataSize, //RData length - }; - _conn->append(reinterpret_cast(srvAttrs), 10); - - //Send the RData Priority weight and port - uint8_t srvRData[6] = { - 0x00, 0x00, //Priority 0 - 0x00, 0x00, //Weight 0 - (uint8_t)((port >> 8) & 0xFF), (uint8_t)(port & 0xFF) - }; - _conn->append(reinterpret_cast(srvRData), 6); - //Send the RData (ie. "esp8266.local") - _conn->append(reinterpret_cast(&hostNameLen), 1); // length of "esp8266" - _conn->append(reinterpret_cast(hostName.c_str()), hostNameLen);// "esp8266" - _conn->append(reinterpret_cast(&localNameLen), 1); // length "local" - _conn->append(reinterpret_cast(localName), localNameLen); // "local" - _conn->append(reinterpret_cast(&terminator), 1); // terminator + // build service name with _ + char serviceName[os_strlen(service) + 2]; + os_strcpy(serviceName, underscore); + os_strcat(serviceName, service); + size_t serviceNameLen = os_strlen(serviceName); + //build proto name with _ + char protoName[5]; + os_strcpy(protoName, underscore); + os_strcat(protoName, proto); + size_t protoNameLen = 4; + + //local string + char localName[] = "local"; + size_t localNameLen = 5; + + //terminator + char terminator[] = "\0"; + + uint8_t answerMask = responseMask & questionMask; + uint8_t answerCount = 0; + uint8_t additionalMask = responseMask & ~questionMask; + uint8_t additionalCount = 0; + for (i = 0; i < 4; i++) + { + if (answerMask & (1 << i)) + { + answerCount++; + } + if (additionalMask & (1 << i)) + { + additionalCount++; + } } - // A Response - if((responseSection == 0 ? answerMask : additionalMask) & 0x1){ - //Send the RData (ie. "esp8266.local") - _conn->append(reinterpret_cast(&hostNameLen), 1); // length of "esp8266" - _conn->append(reinterpret_cast(hostName.c_str()), hostNameLen);// "esp8266" - _conn->append(reinterpret_cast(&localNameLen), 1); // length "local" - _conn->append(reinterpret_cast(localName), localNameLen); // "local" - _conn->append(reinterpret_cast(&terminator), 1); // terminator - - uint8_t aaaAttrs[10] = { - 0x00, 0x01, //TYPE A - 0x80, 0x01, //Class IN, with cache flush - 0x00, 0x00, 0x00, 0x78, //TTL 120 - 0x00, 0x04, //DATA LEN - }; - _conn->append(reinterpret_cast(aaaAttrs), 10); - - // Send RData - uint32_t ip = multicastInterface; - uint8_t aaaRData[4] = { - (uint8_t)(ip & 0xFF), //IP first octet - (uint8_t)((ip >> 8) & 0xFF), //IP second octet - (uint8_t)((ip >> 16) & 0xFF), //IP third octet - (uint8_t)((ip >> 24) & 0xFF) //IP fourth octet - }; - _conn->append(reinterpret_cast(aaaRData), 4); + + //Write the header + _conn->flush(); + uint8_t head[12] = + { + 0x00, 0x00, //ID = 0 + 0x84, 0x00, //Flags = response + authoritative answer + 0x00, 0x00, //Question count + 0x00, answerCount, //Answer count + 0x00, 0x00, //Name server records + 0x00, additionalCount, //Additional records + }; + _conn->append(reinterpret_cast(head), 12); + + for (int responseSection = 0; responseSection < 2; ++responseSection) + { + + // PTR Response + if ((responseSection == 0 ? answerMask : additionalMask) & 0x8) + { + // Send the Name field (ie. "_http._tcp.local") + _conn->append(reinterpret_cast(&serviceNameLen), 1); // length of "_http" + _conn->append(reinterpret_cast(serviceName), serviceNameLen); // "_http" + _conn->append(reinterpret_cast(&protoNameLen), 1); // length of "_tcp" + _conn->append(reinterpret_cast(protoName), protoNameLen); // "_tcp" + _conn->append(reinterpret_cast(&localNameLen), 1); // length "local" + _conn->append(reinterpret_cast(localName), localNameLen); // "local" + _conn->append(reinterpret_cast(&terminator), 1); // terminator + + //Send the type, class, ttl and rdata length + uint8_t ptrDataLen = instanceNameLen + serviceNameLen + protoNameLen + localNameLen + 5; // 5 is four label sizes and the terminator + uint8_t ptrAttrs[10] = + { + 0x00, 0x0c, //PTR record query + 0x00, 0x01, //Class IN + 0x00, 0x00, 0x00, 0x78, //TTL 120 + 0x00, ptrDataLen, //RData length + }; + _conn->append(reinterpret_cast(ptrAttrs), 10); + + //Send the RData (ie. "My IOT device._http._tcp.local") + _conn->append(reinterpret_cast(&instanceNameLen), 1); // length of "My IOT device" + _conn->append(reinterpret_cast(instanceName.c_str()), instanceNameLen);// "My IOT device" + _conn->append(reinterpret_cast(&serviceNameLen), 1); // length of "_http" + _conn->append(reinterpret_cast(serviceName), serviceNameLen); // "_http" + _conn->append(reinterpret_cast(&protoNameLen), 1); // length of "_tcp" + _conn->append(reinterpret_cast(protoName), protoNameLen); // "_tcp" + _conn->append(reinterpret_cast(&localNameLen), 1); // length "local" + _conn->append(reinterpret_cast(localName), localNameLen); // "local" + _conn->append(reinterpret_cast(&terminator), 1); // terminator + } + + //TXT Responce + if ((responseSection == 0 ? answerMask : additionalMask) & 0x4) + { + //Send the name field (ie. "My IOT device._http._tcp.local") + _conn->append(reinterpret_cast(&instanceNameLen), 1); // length of "My IOT device" + _conn->append(reinterpret_cast(instanceName.c_str()), instanceNameLen);// "My IOT device" + _conn->append(reinterpret_cast(&serviceNameLen), 1); // length of "_http" + _conn->append(reinterpret_cast(serviceName), serviceNameLen); // "_http" + _conn->append(reinterpret_cast(&protoNameLen), 1); // length of "_tcp" + _conn->append(reinterpret_cast(protoName), protoNameLen); // "_tcp" + _conn->append(reinterpret_cast(&localNameLen), 1); // length "local" + _conn->append(reinterpret_cast(localName), localNameLen); // "local" + _conn->append(reinterpret_cast(&terminator), 1); // terminator + + //Send the type, class, ttl and rdata length + uint8_t txtDataLen = _getServiceTxtLen(service, proto); + uint8_t txtAttrs[10] = + { + 0x00, 0x10, //TXT record query + 0x80, 0x01, //Class IN, with cache flush + 0x00, 0x00, 0x11, 0x94, //TTL 4500 + 0x00, txtDataLen, //RData length + }; + _conn->append(reinterpret_cast(txtAttrs), 10); + + //Send the RData + MDNSTxt * txtPtr = _getServiceTxt(service, proto); + while (txtPtr != 0) + { + uint8_t txtLen = txtPtr->_txt.length(); + _conn->append(reinterpret_cast(&txtLen), 1); // length of txt + _conn->append(reinterpret_cast(txtPtr->_txt.c_str()), txtLen);// the txt + txtPtr = txtPtr->_next; + } + } + + + //SRV Responce + if ((responseSection == 0 ? answerMask : additionalMask) & 0x2) + { + //Send the name field (ie. "My IOT device._http._tcp.local") + _conn->append(reinterpret_cast(&instanceNameLen), 1); // length of "My IOT device" + _conn->append(reinterpret_cast(instanceName.c_str()), instanceNameLen);// "My IOT device" + _conn->append(reinterpret_cast(&serviceNameLen), 1); // length of "_http" + _conn->append(reinterpret_cast(serviceName), serviceNameLen); // "_http" + _conn->append(reinterpret_cast(&protoNameLen), 1); // length of "_tcp" + _conn->append(reinterpret_cast(protoName), protoNameLen); // "_tcp" + _conn->append(reinterpret_cast(&localNameLen), 1); // length "local" + _conn->append(reinterpret_cast(localName), localNameLen); // "local" + _conn->append(reinterpret_cast(&terminator), 1); // terminator + + //Send the type, class, ttl, rdata length, priority and weight + uint8_t srvDataSize = hostNameLen + localNameLen + 3; // 3 is 2 lable size bytes and the terminator + srvDataSize += 6; // Size of Priority, weight and port + uint8_t srvAttrs[10] = + { + 0x00, 0x21, //Type SRV + 0x80, 0x01, //Class IN, with cache flush + 0x00, 0x00, 0x00, 0x78, //TTL 120 + 0x00, srvDataSize, //RData length + }; + _conn->append(reinterpret_cast(srvAttrs), 10); + + //Send the RData Priority weight and port + uint8_t srvRData[6] = + { + 0x00, 0x00, //Priority 0 + 0x00, 0x00, //Weight 0 + (uint8_t)((port >> 8) & 0xFF), (uint8_t)(port & 0xFF) + }; + _conn->append(reinterpret_cast(srvRData), 6); + //Send the RData (ie. "esp8266.local") + _conn->append(reinterpret_cast(&hostNameLen), 1); // length of "esp8266" + _conn->append(reinterpret_cast(hostName.c_str()), hostNameLen);// "esp8266" + _conn->append(reinterpret_cast(&localNameLen), 1); // length "local" + _conn->append(reinterpret_cast(localName), localNameLen); // "local" + _conn->append(reinterpret_cast(&terminator), 1); // terminator + + } + + // A Response + if ((responseSection == 0 ? answerMask : additionalMask) & 0x1) + { + //Send the RData (ie. "esp8266.local") + _conn->append(reinterpret_cast(&hostNameLen), 1); // length of "esp8266" + _conn->append(reinterpret_cast(hostName.c_str()), hostNameLen);// "esp8266" + _conn->append(reinterpret_cast(&localNameLen), 1); // length "local" + _conn->append(reinterpret_cast(localName), localNameLen); // "local" + _conn->append(reinterpret_cast(&terminator), 1); // terminator + + uint8_t aaaAttrs[10] = + { + 0x00, 0x01, //TYPE A + 0x80, 0x01, //Class IN, with cache flush + 0x00, 0x00, 0x00, 0x78, //TTL 120 + 0x00, 0x04, //DATA LEN + }; + _conn->append(reinterpret_cast(aaaAttrs), 10); + + // Send RData + uint32_t ip = multicastInterface; + uint8_t aaaRData[4] = + { + (uint8_t)(ip & 0xFF), //IP first octet + (uint8_t)((ip >> 8) & 0xFF), //IP second octet + (uint8_t)((ip >> 16) & 0xFF), //IP third octet + (uint8_t)((ip >> 24) & 0xFF) //IP fourth octet + }; + _conn->append(reinterpret_cast(aaaRData), 4); + } } - } - _conn->setMulticastInterface(multicastInterface); - _conn->send(); + _conn->setMulticastInterface(multicastInterface); + _conn->send(); } #if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_MDNS) diff --git a/libraries/ESP8266mDNS/src/ESP8266mDNS_Legacy.h b/libraries/ESP8266mDNS/src/ESP8266mDNS_Legacy.h index 6d241ae06e..9d3cfd2f62 100644 --- a/libraries/ESP8266mDNS/src/ESP8266mDNS_Legacy.h +++ b/libraries/ESP8266mDNS/src/ESP8266mDNS_Legacy.h @@ -1,43 +1,43 @@ /* -ESP8266 Multicast DNS (port of CC3000 Multicast DNS library) -Version 1.1 -Copyright (c) 2013 Tony DiCola (tony@tonydicola.com) -ESP8266 port (c) 2015 Ivan Grokhotkov (ivan@esp8266.com) -Extended MDNS-SD support 2016 Lars Englund (lars.englund@gmail.com) - -This is a simple implementation of multicast DNS query support for an Arduino -running on ESP8266 chip. Only support for resolving address queries is currently -implemented. - -Requirements: -- ESP8266WiFi library - -Usage: -- Include the ESP8266 Multicast DNS library in the sketch. -- Call the begin method in the sketch's setup and provide a domain name (without - the '.local' suffix, i.e. just provide 'foo' to resolve 'foo.local'), and the - Adafruit CC3000 class instance. Optionally provide a time to live (in seconds) - for the DNS record--the default is 1 hour. -- Call the update method in each iteration of the sketch's loop function. - -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. + ESP8266 Multicast DNS (port of CC3000 Multicast DNS library) + Version 1.1 + Copyright (c) 2013 Tony DiCola (tony@tonydicola.com) + ESP8266 port (c) 2015 Ivan Grokhotkov (ivan@esp8266.com) + Extended MDNS-SD support 2016 Lars Englund (lars.englund@gmail.com) + + This is a simple implementation of multicast DNS query support for an Arduino + running on ESP8266 chip. Only support for resolving address queries is currently + implemented. + + Requirements: + - ESP8266WiFi library + + Usage: + - Include the ESP8266 Multicast DNS library in the sketch. + - Call the begin method in the sketch's setup and provide a domain name (without + the '.local' suffix, i.e. just provide 'foo' to resolve 'foo.local'), and the + Adafruit CC3000 class instance. Optionally provide a time to live (in seconds) + for the DNS record--the default is 1 hour. + - Call the update method in each iteration of the sketch's loop function. + + 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. */ #ifndef ESP8266MDNS_LEGACY_H @@ -54,95 +54,108 @@ License (MIT license): class UdpContext; -namespace Legacy_MDNSResponder { +namespace Legacy_MDNSResponder +{ struct MDNSService; struct MDNSTxt; struct MDNSAnswer; -class MDNSResponder { +class MDNSResponder +{ public: - MDNSResponder(); - ~MDNSResponder(); - bool begin(const char* hostName); - bool begin(const String& hostName) { - return begin(hostName.c_str()); - } - //for compatibility - bool begin(const char* hostName, IPAddress ip, uint32_t ttl=120){ - (void) ip; - (void) ttl; - return begin(hostName); - } - bool begin(const String& hostName, IPAddress ip, uint32_t ttl=120) { - return begin(hostName.c_str(), ip, ttl); - } - /* Application should call this whenever AP is configured/disabled */ - void notifyAPChange(); - void update(); - - void addService(char *service, char *proto, uint16_t port); - void addService(const char *service, const char *proto, uint16_t port){ - addService((char *)service, (char *)proto, port); - } - void addService(const String& service, const String& proto, uint16_t port){ - addService(service.c_str(), proto.c_str(), port); - } - - bool addServiceTxt(char *name, char *proto, char * key, char * value); - bool addServiceTxt(const char *name, const char *proto, const char *key,const char * value){ - return addServiceTxt((char *)name, (char *)proto, (char *)key, (char *)value); - } - bool addServiceTxt(const String& name, const String& proto, const String& key, const String& value){ - return addServiceTxt(name.c_str(), proto.c_str(), key.c_str(), value.c_str()); - } - - int queryService(char *service, char *proto); - int queryService(const char *service, const char *proto){ - return queryService((char *)service, (char *)proto); - } - int queryService(const String& service, const String& proto){ - return queryService(service.c_str(), proto.c_str()); - } - String hostname(int idx); - IPAddress IP(int idx); - uint16_t port(int idx); - - void enableArduino(uint16_t port, bool auth=false); - - void setInstanceName(String name); - void setInstanceName(const char * name){ - setInstanceName(String(name)); - } - void setInstanceName(char * name){ - setInstanceName(String(name)); - } + MDNSResponder(); + ~MDNSResponder(); + bool begin(const char* hostName); + bool begin(const String& hostName) + { + return begin(hostName.c_str()); + } + //for compatibility + bool begin(const char* hostName, IPAddress ip, uint32_t ttl = 120) + { + (void) ip; + (void) ttl; + return begin(hostName); + } + bool begin(const String& hostName, IPAddress ip, uint32_t ttl = 120) + { + return begin(hostName.c_str(), ip, ttl); + } + /* Application should call this whenever AP is configured/disabled */ + void notifyAPChange(); + void update(); + + void addService(char *service, char *proto, uint16_t port); + void addService(const char *service, const char *proto, uint16_t port) + { + addService((char *)service, (char *)proto, port); + } + void addService(const String& service, const String& proto, uint16_t port) + { + addService(service.c_str(), proto.c_str(), port); + } + + bool addServiceTxt(char *name, char *proto, char * key, char * value); + bool addServiceTxt(const char *name, const char *proto, const char *key, const char * value) + { + return addServiceTxt((char *)name, (char *)proto, (char *)key, (char *)value); + } + bool addServiceTxt(const String& name, const String& proto, const String& key, const String& value) + { + return addServiceTxt(name.c_str(), proto.c_str(), key.c_str(), value.c_str()); + } + + int queryService(char *service, char *proto); + int queryService(const char *service, const char *proto) + { + return queryService((char *)service, (char *)proto); + } + int queryService(const String& service, const String& proto) + { + return queryService(service.c_str(), proto.c_str()); + } + String hostname(int idx); + IPAddress IP(int idx); + uint16_t port(int idx); + + void enableArduino(uint16_t port, bool auth = false); + + void setInstanceName(String name); + void setInstanceName(const char * name) + { + setInstanceName(String(name)); + } + void setInstanceName(char * name) + { + setInstanceName(String(name)); + } private: - struct MDNSService * _services; - UdpContext* _conn; - String _hostName; - String _instanceName; - struct MDNSAnswer * _answers; - struct MDNSQuery * _query; - bool _newQuery; - bool _waitingForAnswers; - WiFiEventHandler _disconnectedHandler; - WiFiEventHandler _gotIPHandler; - - - uint16_t _getServicePort(char *service, char *proto); - MDNSTxt * _getServiceTxt(char *name, char *proto); - uint16_t _getServiceTxtLen(char *name, char *proto); - IPAddress _getRequestMulticastInterface(); - void _parsePacket(); - void _replyToTypeEnumRequest(IPAddress multicastInterface); - void _replyToInstanceRequest(uint8_t questionMask, uint8_t responseMask, char * service, char *proto, uint16_t port, IPAddress multicastInterface); - MDNSAnswer* _getAnswerFromIdx(int idx); - int _getNumAnswers(); - bool _listen(); - void _restart(); + struct MDNSService * _services; + UdpContext* _conn; + String _hostName; + String _instanceName; + struct MDNSAnswer * _answers; + struct MDNSQuery * _query; + bool _newQuery; + bool _waitingForAnswers; + WiFiEventHandler _disconnectedHandler; + WiFiEventHandler _gotIPHandler; + + + uint16_t _getServicePort(char *service, char *proto); + MDNSTxt * _getServiceTxt(char *name, char *proto); + uint16_t _getServiceTxtLen(char *name, char *proto); + IPAddress _getRequestMulticastInterface(); + void _parsePacket(); + void _replyToTypeEnumRequest(IPAddress multicastInterface); + void _replyToInstanceRequest(uint8_t questionMask, uint8_t responseMask, char * service, char *proto, uint16_t port, IPAddress multicastInterface); + MDNSAnswer* _getAnswerFromIdx(int idx); + int _getNumAnswers(); + bool _listen(); + void _restart(); }; } // namespace Legacy_MDNSResponder diff --git a/libraries/ESP8266mDNS/src/LEAmDNS.cpp b/libraries/ESP8266mDNS/src/LEAmDNS.cpp index 1b468fb269..95f07079d7 100644 --- a/libraries/ESP8266mDNS/src/LEAmDNS.cpp +++ b/libraries/ESP8266mDNS/src/LEAmDNS.cpp @@ -1,1212 +1,1380 @@ -/* - * 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 -#include - -#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), -#ifdef ENABLE_ESP_MDNS_RESPONDER_PASSIV_MODE - m_bPassivModeEnabled(true), -#else - m_bPassivModeEnabled(false), -#endif - m_netif(nullptr) { -} - -/* - * 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, const IPAddress& p_IPAddress, uint32_t p_u32TTL) { - - (void)p_u32TTL; // ignored - bool bResult = false; - - if (0 == m_pUDPContext) { - if (_setHostname(p_pcHostname)) { - - //// select interface - - m_netif = nullptr; - IPAddress ipAddress = p_IPAddress; - - if (!ipAddress.isSet()) { - - IPAddress sta = WiFi.localIP(); - IPAddress ap = WiFi.softAPIP(); - - if (!sta.isSet() && !ap.isSet()) { - - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] internal interfaces (STA, AP) are not set (none was specified)\n"))); - return false; - } - - if (ap.isSet()) { - - if (sta.isSet()) - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] default interface AP selected over STA (none was specified)\n"))); - else - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] default interface AP selected\n"))); - ipAddress = ap; - - } else { - - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] default interface STA selected (none was specified)\n"))); - ipAddress = sta; - - } - - // continue to ensure interface is UP - } - - // check existence of this IP address in the interface list - bool found = false; - m_netif = nullptr; - for (auto a: addrList) - if (ipAddress == a.addr()) { - if (a.ifUp()) { - found = true; - m_netif = a.interface(); - break; - } - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] found interface for IP '%s' but it is not UP\n"), ipAddress.toString().c_str());); - } - if (!found) { - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] interface defined by IP '%s' not found\n"), ipAddress.toString().c_str());); - return false; - } - - //// done selecting the interface - - if (m_netif->num == STATION_IF) { - - m_GotIPHandler = WiFi.onStationModeGotIP([this](const WiFiEventStationModeGotIP& pEvent) { - (void) pEvent; - // Ensure that _restart() runs in USER context - schedule_function([this]() { MDNSResponder::_restart(); }); - }); - - m_DisconnectedHandler = WiFi.onStationModeDisconnected([this](const WiFiEventStationModeDisconnected& pEvent) { - (void) pEvent; - // Ensure that _restart() runs in USER context - schedule_function([this]() { MDNSResponder::_restart(); }); - }); - } - - bResult = _restart(); - } - DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] begin: FAILED for '%s'!\n"), (p_pcHostname ?: "-")); } ); - } - else { - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] begin: Ignoring multiple calls to begin (Ignored host domain: '%s')!\n"), (p_pcHostname ?: "-"));); - } - return bResult; -} - -/* - * MDNSResponder::close - * - * Ends the MDNS responder. - * Announced services are unannounced (by multicasting a goodbye message) - * - */ -bool MDNSResponder::close(void) { - - m_GotIPHandler.reset(); // reset WiFi event callbacks. - m_DisconnectedHandler.reset(); - - _announce(false, true); - _resetProbeStatus(false); // Stop probing - - _releaseServiceQueries(); - _releaseUDPContext(); - _releaseHostname(); - - return true; -} - -/* - * MDNSResponder::end - * - * Ends the MDNS responder. - * for compatibility with esp32 - * - */ - -bool MDNSResponder::end(void) { - return close(); -} - -/* - * 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_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] addService: %s to add '%s.%s.%s'!\n"), (hResult ? "Succeeded" : "FAILED"), (p_pcName ?: "-"), p_pcService, p_pcProtocol); ); - 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::MDNSDynamicServiceTxtCallbackFunc p_fnCallback) { - - m_fnServiceTxtCallback = p_fnCallback; - - 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::MDNSDynamicServiceTxtCallbackFunc p_fnCallback) { - - bool bResult = false; - - stcMDNSService* pService = _findService(p_hService); - if (pService) { - pService->m_fnTxtCallback = p_fnCallback; - - 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"));); - } - 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::MDNSServiceQueryCallbackFunc p_fnCallback) { - 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_bLegacyQuery = false; - - if (_sendMDNSServiceQuery(*pServiceQuery)) { - pServiceQuery->m_u8SentCount = 1; - pServiceQuery->m_ResendTimeout.reset(MDNS_DYNAMIC_QUERY_RESEND_DELAY); - - hResult = (hMDNSServiceQuery)pServiceQuery; - } - else { - _removeServiceQuery(pServiceQuery); - } - } - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] installServiceQuery: %s for '%s.%s'!\n\n"), (hResult ? "Succeeded" : "FAILED"), (p_pcService ?: "-"), (p_pcProtocol ?: "-"));); - DEBUG_EX_ERR(if (!hResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] installServiceQuery: FAILED for '%s.%s'!\n\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); -} - -std::vector MDNSResponder::answerInfo (const MDNSResponder::hMDNSServiceQuery p_hServiceQuery) { - std::vector tempVector; - for (uint32_t i=0;ianswerAtIndex(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::setHostProbeResultCallback(MDNSResponder::MDNSHostProbeFn p_fnCallback) { - - m_HostProbeInformation.m_fnHostProbeResultCallback = p_fnCallback; - - return true; -} - -bool MDNSResponder::setHostProbeResultCallback(MDNSHostProbeFn1 pfn) { - using namespace std::placeholders; - return setHostProbeResultCallback([this, pfn](const char* p_pcDomainName, bool p_bProbeResult) { pfn(*this, p_pcDomainName, p_bProbeResult); }); -} - -/* - * 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::MDNSServiceProbeFn p_fnCallback) { - - bool bResult = false; - - stcMDNSService* pService = _findService(p_hService); - if (pService) { - pService->m_ProbeInformation.m_fnServiceProbeResultCallback = p_fnCallback; - - bResult = true; - } - return bResult; -} - -bool MDNSResponder::setServiceProbeResultCallback(const MDNSResponder::hMDNSService p_hService, - MDNSResponder::MDNSServiceProbeFn1 p_fnCallback) { - using namespace std::placeholders; - return setServiceProbeResultCallback(p_hService, [this, p_fnCallback](const char* p_pcServiceName, const hMDNSService p_hMDNSService, bool p_bProbeResult) { - p_fnCallback(*this, p_pcServiceName, p_hMDNSService, p_bProbeResult); - }); -} - - -/* - * 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) { - - if (m_bPassivModeEnabled) { - m_bPassivModeEnabled = false; - } - 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(true, true)); -} - -/* - * 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 - - +/* + 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 +#include + +#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), +#ifdef ENABLE_ESP_MDNS_RESPONDER_PASSIV_MODE + m_bPassivModeEnabled(true), +#else + m_bPassivModeEnabled(false), +#endif + m_netif(nullptr) +{ +} + +/* + 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, const IPAddress& p_IPAddress, uint32_t p_u32TTL) +{ + + (void)p_u32TTL; // ignored + bool bResult = false; + + if (0 == m_pUDPContext) + { + if (_setHostname(p_pcHostname)) + { + + //// select interface + + m_netif = nullptr; + IPAddress ipAddress = p_IPAddress; + + if (!ipAddress.isSet()) + { + + IPAddress sta = WiFi.localIP(); + IPAddress ap = WiFi.softAPIP(); + + if (!sta.isSet() && !ap.isSet()) + { + + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] internal interfaces (STA, AP) are not set (none was specified)\n"))); + return false; + } + + if (ap.isSet()) + { + + if (sta.isSet()) + { + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] default interface AP selected over STA (none was specified)\n"))); + } + else + { + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] default interface AP selected\n"))); + } + ipAddress = ap; + + } + else + { + + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] default interface STA selected (none was specified)\n"))); + ipAddress = sta; + + } + + // continue to ensure interface is UP + } + + // check existence of this IP address in the interface list + bool found = false; + m_netif = nullptr; + for (auto a : addrList) + if (ipAddress == a.addr()) + { + if (a.ifUp()) + { + found = true; + m_netif = a.interface(); + break; + } + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] found interface for IP '%s' but it is not UP\n"), ipAddress.toString().c_str());); + } + if (!found) + { + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] interface defined by IP '%s' not found\n"), ipAddress.toString().c_str());); + return false; + } + + //// done selecting the interface + + if (m_netif->num == STATION_IF) + { + + m_GotIPHandler = WiFi.onStationModeGotIP([this](const WiFiEventStationModeGotIP & pEvent) + { + (void) pEvent; + // Ensure that _restart() runs in USER context + schedule_function([this]() + { + MDNSResponder::_restart(); + }); + }); + + m_DisconnectedHandler = WiFi.onStationModeDisconnected([this](const WiFiEventStationModeDisconnected & pEvent) + { + (void) pEvent; + // Ensure that _restart() runs in USER context + schedule_function([this]() + { + MDNSResponder::_restart(); + }); + }); + } + + bResult = _restart(); + } + DEBUG_EX_ERR(if (!bResult) + { + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] begin: FAILED for '%s'!\n"), (p_pcHostname ? : "-")); + }); + } + else + { + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] begin: Ignoring multiple calls to begin (Ignored host domain: '%s')!\n"), (p_pcHostname ? : "-"));); + } + return bResult; +} + +/* + MDNSResponder::close + + Ends the MDNS responder. + Announced services are unannounced (by multicasting a goodbye message) + +*/ +bool MDNSResponder::close(void) +{ + + m_GotIPHandler.reset(); // reset WiFi event callbacks. + m_DisconnectedHandler.reset(); + + _announce(false, true); + _resetProbeStatus(false); // Stop probing + + _releaseServiceQueries(); + _releaseUDPContext(); + _releaseHostname(); + + return true; +} + +/* + MDNSResponder::end + + Ends the MDNS responder. + for compatibility with esp32 + +*/ + +bool MDNSResponder::end(void) +{ + return close(); +} + +/* + 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_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] addService: %s to add '%s.%s.%s'!\n"), (hResult ? "Succeeded" : "FAILED"), (p_pcName ? : "-"), p_pcService, p_pcProtocol);); + 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::MDNSDynamicServiceTxtCallbackFunc p_fnCallback) +{ + + m_fnServiceTxtCallback = p_fnCallback; + + 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::MDNSDynamicServiceTxtCallbackFunc p_fnCallback) +{ + + bool bResult = false; + + stcMDNSService* pService = _findService(p_hService); + if (pService) + { + pService->m_fnTxtCallback = p_fnCallback; + + 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"));); + } + 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::MDNSServiceQueryCallbackFunc p_fnCallback) +{ + 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_bLegacyQuery = false; + + if (_sendMDNSServiceQuery(*pServiceQuery)) + { + pServiceQuery->m_u8SentCount = 1; + pServiceQuery->m_ResendTimeout.reset(MDNS_DYNAMIC_QUERY_RESEND_DELAY); + + hResult = (hMDNSServiceQuery)pServiceQuery; + } + else + { + _removeServiceQuery(pServiceQuery); + } + } + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] installServiceQuery: %s for '%s.%s'!\n\n"), (hResult ? "Succeeded" : "FAILED"), (p_pcService ? : "-"), (p_pcProtocol ? : "-"));); + DEBUG_EX_ERR(if (!hResult) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] installServiceQuery: FAILED for '%s.%s'!\n\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); +} + +std::vector MDNSResponder::answerInfo(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery) +{ + std::vector tempVector; + for (uint32_t i = 0; i < answerCount(p_hServiceQuery); i++) + { + tempVector.emplace_back(*this, p_hServiceQuery, i); + } + return tempVector; +} + +/* + 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::setHostProbeResultCallback(MDNSResponder::MDNSHostProbeFn p_fnCallback) +{ + + m_HostProbeInformation.m_fnHostProbeResultCallback = p_fnCallback; + + return true; +} + +bool MDNSResponder::setHostProbeResultCallback(MDNSHostProbeFn1 pfn) +{ + using namespace std::placeholders; + return setHostProbeResultCallback([this, pfn](const char* p_pcDomainName, bool p_bProbeResult) + { + pfn(*this, p_pcDomainName, p_bProbeResult); + }); +} + +/* + 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::MDNSServiceProbeFn p_fnCallback) +{ + + bool bResult = false; + + stcMDNSService* pService = _findService(p_hService); + if (pService) + { + pService->m_ProbeInformation.m_fnServiceProbeResultCallback = p_fnCallback; + + bResult = true; + } + return bResult; +} + +bool MDNSResponder::setServiceProbeResultCallback(const MDNSResponder::hMDNSService p_hService, + MDNSResponder::MDNSServiceProbeFn1 p_fnCallback) +{ + using namespace std::placeholders; + return setServiceProbeResultCallback(p_hService, [this, p_fnCallback](const char* p_pcServiceName, const hMDNSService p_hMDNSService, bool p_bProbeResult) + { + p_fnCallback(*this, p_pcServiceName, p_hMDNSService, p_bProbeResult); + }); +} + + +/* + 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) +{ + + if (m_bPassivModeEnabled) + { + m_bPassivModeEnabled = false; + } + 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(true, true)); +} + +/* + 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 + + diff --git a/libraries/ESP8266mDNS/src/LEAmDNS.h b/libraries/ESP8266mDNS/src/LEAmDNS.h index ed05a66a26..da3aa01a12 100644 --- a/libraries/ESP8266mDNS/src/LEAmDNS.h +++ b/libraries/ESP8266mDNS/src/LEAmDNS.h @@ -1,1403 +1,1456 @@ -/* - * LEAmDNS.h - * (c) 2018, LaborEtArs - * - * Version 0.9 beta - * - * Some notes (from LaborEtArs, 2018): - * Essentially, this is an rewrite of the original EPS8266 Multicast DNS code (ESP8266mDNS). - * The target of this rewrite was to keep the existing interface as stable as possible while - * adding and extending the supported set of mDNS features. - * A lot of the additions were basicly taken from Erik Ekman's lwIP mdns app code. - * - * Supported mDNS features (in some cases somewhat limited): - * - Presenting a DNS-SD service to interested observers, eg. a http server by presenting _http._tcp service - * - Support for multi-level compressed names in input; in output only a very simple one-leven full-name compression is implemented - * - Probing host and service domains for uniqueness in the local network - * - Tiebreaking while probing is supportet in a very minimalistic way (the 'higher' IP address wins the tiebreak) - * - Announcing available services after successful probing - * - Using fixed service TXT items or - * - Using dynamic service TXT items for presented services (via callback) - * - Remove services (and un-announcing them to the observers by sending goodbye-messages) - * - Static queries for DNS-SD services (creating a fixed answer set after a certain timeout period) - * - Dynamic queries for DNS-SD services with cached and updated answers and user notifications - * - * - * Usage: - * In most cases, this implementation should work as a 'drop-in' replacement for the original - * ESP8266 Multicast DNS code. Adjustments to the existing code would only be needed, if some - * of the new features should be used. - * - * For presenting services: - * In 'setup()': - * Install a callback for the probing of host (and service) domains via 'MDNS.setProbeResultCallback(probeResultCallback, &userData);' - * Register DNS-SD services with 'MDNSResponder::hMDNSService hService = MDNS.addService("MyESP", "http", "tcp", 5000);' - * (Install additional callbacks for the probing of these service domains via 'MDNS.setServiceProbeResultCallback(hService, probeResultCallback, &userData);') - * Add service TXT items with 'MDNS.addServiceTxt(hService, "c#", "1");' or by installing a service TXT callback - * using 'MDNS.setDynamicServiceTxtCallback(dynamicServiceTxtCallback, &userData);' or service specific - * 'MDNS.setDynamicServiceTxtCallback(hService, dynamicServiceTxtCallback, &userData);' - * Call MDNS.begin("MyHostname"); - * - * In 'probeResultCallback(MDNSResponder* p_MDNSResponder, const char* p_pcDomain, MDNSResponder:hMDNSService p_hService, bool p_bProbeResult, void* p_pUserdata)': - * Check the probe result and update the host or service domain name if the probe failed - * - * In 'dynamicServiceTxtCallback(MDNSResponder* p_MDNSResponder, const hMDNSService p_hService, void* p_pUserdata)': - * Add dynamic TXT items by calling 'MDNS.addDynamicServiceTxt(p_hService, "c#", "1");' - * - * In loop(): - * Call 'MDNS.update();' - * - * - * For querying services: - * Static: - * Call 'uint32_t u32AnswerCount = MDNS.queryService("http", "tcp");' - * Iterate answers by: 'for (uint32_t u=0; u // for UdpContext.h -#include "WiFiUdp.h" -#include "lwip/udp.h" -#include "debug.h" -#include "include/UdpContext.h" -#include -#include -#include - - -#include "ESP8266WiFi.h" - - -namespace esp8266 { - -/** - * LEAmDNS - */ -namespace MDNSImplementation { - -//this should be defined at build time -#ifndef ARDUINO_BOARD -#define ARDUINO_BOARD "generic" -#endif - -#define MDNS_IP4_SUPPORT -//#define MDNS_IP6_SUPPORT - - -#ifdef MDNS_IP4_SUPPORT - #define MDNS_IP4_SIZE 4 -#endif -#ifdef MDNS_IP6_SUPPORT - #define MDNS_IP6_SIZE 16 -#endif -/* - * Maximum length for all service txts for one service - */ -#define MDNS_SERVICE_TXT_MAXLENGTH 1300 -/* - * Maximum length for a full domain name eg. MyESP._http._tcp.local - */ -#define MDNS_DOMAIN_MAXLENGTH 256 -/* - * Maximum length of on label in a domain name (length info fits into 6 bits) - */ -#define MDNS_DOMAIN_LABEL_MAXLENGTH 63 -/* - * Maximum length of a service name eg. http - */ -#define MDNS_SERVICE_NAME_LENGTH 15 -/* - * Maximum length of a service protocol name eg. tcp - */ -#define MDNS_SERVICE_PROTOCOL_LENGTH 3 -/* - * Default timeout for static service queries - */ -#define MDNS_QUERYSERVICES_WAIT_TIME 1000 - - -/** - * MDNSResponder - */ -class MDNSResponder { -public: - /* INTERFACE */ - MDNSResponder(void); - virtual ~MDNSResponder(void); - - // Start the MDNS responder by setting the default hostname - // Later call MDNS::update() in every 'loop' to run the process loop - // (probing, announcing, responding, ...) - // if interfaceAddress is not specified, default interface is STA, or AP when STA is not set - bool begin(const char* p_pcHostname, const IPAddress& p_IPAddress = INADDR_ANY, uint32_t p_u32TTL = 120 /*ignored*/); - bool begin(const String& p_strHostname, const IPAddress& p_IPAddress = INADDR_ANY, uint32_t p_u32TTL = 120 /*ignored*/) {return begin(p_strHostname.c_str(), p_IPAddress, p_u32TTL);} - - // Finish MDNS processing - bool close(void); - // for esp32 compatability - bool end(void); - // Change hostname (probing is restarted) - bool setHostname(const char* p_pcHostname); - // for compatibility... - bool setHostname(String p_strHostname); - - /** - * hMDNSService (opaque handle to access the service) - */ - typedef const void* hMDNSService; - - // Add a new service to the MDNS responder. If no name (instance name) is given (p_pcName = 0) - // the current hostname is used. If the hostname is changed later, the instance names for - // these 'auto-named' services are changed to the new name also (and probing is restarted). - // The usual '_' before p_pcService (eg. http) and protocol (eg. tcp) may be given. - hMDNSService addService(const char* p_pcName, - const char* p_pcService, - const char* p_pcProtocol, - uint16_t p_u16Port); - // Removes a service from the MDNS responder - bool removeService(const hMDNSService p_hService); - bool removeService(const char* p_pcInstanceName, - const char* p_pcServiceName, - const char* p_pcProtocol); - // for compatibility... - bool addService(String p_strServiceName, - String p_strProtocol, - uint16_t p_u16Port); - - - // Change the services instance name (and restart probing). - bool setServiceName(const hMDNSService p_hService, - const char* p_pcInstanceName); - //for compatibility - //Warning: this has the side effect of changing the hostname. - //TODO: implement instancename different from hostname - void setInstanceName(const char* p_pcHostname) {setHostname(p_pcHostname);} - // for esp32 compatibilty - void setInstanceName(const String& s_pcHostname) {setInstanceName(s_pcHostname.c_str());} - - /** - * hMDNSTxt (opaque handle to access the TXT items) - */ - typedef void* hMDNSTxt; - - // Add a (static) MDNS TXT item ('key' = 'value') to the service - hMDNSTxt addServiceTxt(const hMDNSService p_hService, - const char* p_pcKey, - const char* p_pcValue); - hMDNSTxt addServiceTxt(const hMDNSService p_hService, - const char* p_pcKey, - uint32_t p_u32Value); - hMDNSTxt addServiceTxt(const hMDNSService p_hService, - const char* p_pcKey, - uint16_t p_u16Value); - hMDNSTxt addServiceTxt(const hMDNSService p_hService, - const char* p_pcKey, - uint8_t p_u8Value); - hMDNSTxt addServiceTxt(const hMDNSService p_hService, - const char* p_pcKey, - int32_t p_i32Value); - hMDNSTxt addServiceTxt(const hMDNSService p_hService, - const char* p_pcKey, - int16_t p_i16Value); - hMDNSTxt addServiceTxt(const hMDNSService p_hService, - const char* p_pcKey, - int8_t p_i8Value); - - // Remove an existing (static) MDNS TXT item from the service - bool removeServiceTxt(const hMDNSService p_hService, - const hMDNSTxt p_hTxt); - bool removeServiceTxt(const hMDNSService p_hService, - const char* p_pcKey); - bool removeServiceTxt(const char* p_pcinstanceName, - const char* p_pcServiceName, - const char* p_pcProtocol, - const char* p_pcKey); - // for compatibility... - bool addServiceTxt(const char* p_pcService, - const char* p_pcProtocol, - const char* p_pcKey, - const char* p_pcValue); - bool addServiceTxt(String p_strService, - String p_strProtocol, - String p_strKey, - String p_strValue); - - /** - * MDNSDynamicServiceTxtCallbackFn - * Callback function for dynamic MDNS TXT items - */ - - typedef std::function MDNSDynamicServiceTxtCallbackFunc; - - // Set a global callback for dynamic MDNS TXT items. The callback function is called - // every time, a TXT item is needed for one of the installed services. - bool setDynamicServiceTxtCallback(MDNSDynamicServiceTxtCallbackFunc p_fnCallback); - // Set a service specific callback for dynamic MDNS TXT items. The callback function - // is called every time, a TXT item is needed for the given service. - bool setDynamicServiceTxtCallback(const hMDNSService p_hService, - MDNSDynamicServiceTxtCallbackFunc p_fnCallback); - - // Add a (dynamic) MDNS TXT item ('key' = 'value') to the service - // Dynamic TXT items are removed right after one-time use. So they need to be added - // every time the value s needed (via callback). - hMDNSTxt addDynamicServiceTxt(hMDNSService p_hService, - const char* p_pcKey, - const char* p_pcValue); - hMDNSTxt addDynamicServiceTxt(hMDNSService p_hService, - const char* p_pcKey, - uint32_t p_u32Value); - hMDNSTxt addDynamicServiceTxt(hMDNSService p_hService, - const char* p_pcKey, - uint16_t p_u16Value); - hMDNSTxt addDynamicServiceTxt(hMDNSService p_hService, - const char* p_pcKey, - uint8_t p_u8Value); - hMDNSTxt addDynamicServiceTxt(hMDNSService p_hService, - const char* p_pcKey, - int32_t p_i32Value); - hMDNSTxt addDynamicServiceTxt(hMDNSService p_hService, - const char* p_pcKey, - int16_t p_i16Value); - hMDNSTxt addDynamicServiceTxt(hMDNSService p_hService, - const char* p_pcKey, - int8_t p_i8Value); - - // Perform a (static) service query. The function returns after p_u16Timeout milliseconds - // The answers (the number of received answers is returned) can be retrieved by calling - // - answerHostname (or hostname) - // - answerIP (or IP) - // - answerPort (or port) - uint32_t queryService(const char* p_pcService, - const char* p_pcProtocol, - const uint16_t p_u16Timeout = MDNS_QUERYSERVICES_WAIT_TIME); - bool removeQuery(void); - // for compatibility... - uint32_t queryService(String p_strService, - String p_strProtocol); - - const char* answerHostname(const uint32_t p_u32AnswerIndex); - IPAddress answerIP(const uint32_t p_u32AnswerIndex); - uint16_t answerPort(const uint32_t p_u32AnswerIndex); - // for compatibility... - String hostname(const uint32_t p_u32AnswerIndex); - IPAddress IP(const uint32_t p_u32AnswerIndex); - uint16_t port(const uint32_t p_u32AnswerIndex); - - /** - * hMDNSServiceQuery (opaque handle to access dynamic service queries) - */ - typedef const void* hMDNSServiceQuery; - - /** - * enuServiceQueryAnswerType - */ - typedef enum _enuServiceQueryAnswerType { - ServiceQueryAnswerType_ServiceDomain = (1 << 0), // Service instance name - ServiceQueryAnswerType_HostDomainAndPort = (1 << 1), // Host domain and service port - ServiceQueryAnswerType_Txts = (1 << 2), // TXT items -#ifdef MDNS_IP4_SUPPORT - ServiceQueryAnswerType_IP4Address = (1 << 3), // IP4 address -#endif -#ifdef MDNS_IP6_SUPPORT - ServiceQueryAnswerType_IP6Address = (1 << 4), // IP6 address -#endif - } enuServiceQueryAnswerType; - - enum class AnswerType : uint32_t { - Unknown = 0, - ServiceDomain = ServiceQueryAnswerType_ServiceDomain, - HostDomainAndPort = ServiceQueryAnswerType_HostDomainAndPort, - Txt = ServiceQueryAnswerType_Txts, -#ifdef MDNS_IP4_SUPPORT - IP4Address = ServiceQueryAnswerType_IP4Address, -#endif -#ifdef MDNS_IP6_SUPPORT - IP6Address = ServiceQueryAnswerType_IP6Address, -#endif - }; - - /** - * MDNSServiceQueryCallbackFn - * Callback function for received answers for dynamic service queries - */ - struct MDNSServiceInfo; // forward declaration - typedef std::function MDNSServiceQueryCallbackFunc; - - // Install a dynamic service query. For every received answer (part) the given callback - // function is called. The query will be updated every time, the TTL for an answer - // has timed-out. - // The answers can also be retrieved by calling - // - answerCount - // - answerServiceDomain - // - hasAnswerHostDomain/answerHostDomain - // - hasAnswerIP4Address/answerIP4Address - // - hasAnswerIP6Address/answerIP6Address - // - hasAnswerPort/answerPort - // - hasAnswerTxts/answerTxts - hMDNSServiceQuery installServiceQuery(const char* p_pcService, - const char* p_pcProtocol, - MDNSServiceQueryCallbackFunc p_fnCallback); - // Remove a dynamic service query - bool removeServiceQuery(hMDNSServiceQuery p_hServiceQuery); - - uint32_t answerCount(const hMDNSServiceQuery p_hServiceQuery); - std::vector answerInfo (const MDNSResponder::hMDNSServiceQuery p_hServiceQuery); - - const char* answerServiceDomain(const hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex); - bool hasAnswerHostDomain(const hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex); - const char* answerHostDomain(const hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex); -#ifdef MDNS_IP4_SUPPORT - bool hasAnswerIP4Address(const hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex); - uint32_t answerIP4AddressCount(const hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex); - IPAddress answerIP4Address(const hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex, - const uint32_t p_u32AddressIndex); -#endif -#ifdef MDNS_IP6_SUPPORT - bool hasAnswerIP6Address(const hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex); - uint32_t answerIP6AddressCount(const hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex); - IPAddress answerIP6Address(const hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex, - const uint32_t p_u32AddressIndex); -#endif - bool hasAnswerPort(const hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex); - uint16_t answerPort(const hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex); - bool hasAnswerTxts(const hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex); - // Get the TXT items as a ';'-separated string - const char* answerTxts(const hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex); - - /** - * MDNSProbeResultCallbackFn - * Callback function for (host and service domain) probe results - */ - typedef std::function MDNSHostProbeFn; - - typedef std::function MDNSHostProbeFn1; - - typedef std::function MDNSServiceProbeFn; - - typedef std::function MDNSServiceProbeFn1; - - // Set a global callback function for host and service probe results - // The callback function is called, when the probing for the host domain - // (or a service domain, which hasn't got a service specific callback) - // Succeeds or fails. - // In case of failure, the failed domain name should be changed. - bool setHostProbeResultCallback(MDNSHostProbeFn p_fnCallback); - bool setHostProbeResultCallback(MDNSHostProbeFn1 p_fnCallback); - - // Set a service specific probe result callback - bool setServiceProbeResultCallback(const MDNSResponder::hMDNSService p_hService, - MDNSServiceProbeFn p_fnCallback); - bool setServiceProbeResultCallback(const MDNSResponder::hMDNSService p_hService, - MDNSServiceProbeFn1 p_fnCallback); - - // Application should call this whenever AP is configured/disabled - bool notifyAPChange(void); - - // 'update' should be called in every 'loop' to run the MDNS processing - bool update(void); - - // 'announce' can be called every time, the configuration of some service - // changes. Mainly, this would be changed content of TXT items. - bool announce(void); - - // Enable OTA update - hMDNSService enableArduino(uint16_t p_u16Port, - bool p_bAuthUpload = false); - - // Domain name helper - static bool indexDomain(char*& p_rpcDomain, - const char* p_pcDivider = "-", - const char* p_pcDefaultDomain = 0); - - /** STRUCTS **/ - -public: - /** - * MDNSServiceInfo, used in application callbacks - */ - struct MDNSServiceInfo - { - MDNSServiceInfo(MDNSResponder& p_pM,MDNSResponder::hMDNSServiceQuery p_hS,uint32_t p_u32A) - : p_pMDNSResponder(p_pM), - p_hServiceQuery(p_hS), - p_u32AnswerIndex(p_u32A) - {}; - struct CompareKey - { - bool operator()(char const *a, char const *b) const - { - return strcmp(a, b) < 0; - } - }; - using KeyValueMap = std::map; - protected: - MDNSResponder& p_pMDNSResponder; - MDNSResponder::hMDNSServiceQuery p_hServiceQuery; - uint32_t p_u32AnswerIndex; - KeyValueMap keyValueMap; - public: - const char* serviceDomain(){ - return p_pMDNSResponder.answerServiceDomain(p_hServiceQuery, p_u32AnswerIndex); - }; - bool hostDomainAvailable() - { - return (p_pMDNSResponder.hasAnswerHostDomain(p_hServiceQuery, p_u32AnswerIndex)); - } - const char* hostDomain(){ - return (hostDomainAvailable()) ? - p_pMDNSResponder.answerHostDomain(p_hServiceQuery, p_u32AnswerIndex) : nullptr; - }; - bool hostPortAvailable() - { - return (p_pMDNSResponder.hasAnswerPort(p_hServiceQuery, p_u32AnswerIndex)); - } - uint16_t hostPort(){ - return (hostPortAvailable()) ? - p_pMDNSResponder.answerPort(p_hServiceQuery, p_u32AnswerIndex) : 0; - }; - bool IP4AddressAvailable() - { - return (p_pMDNSResponder.hasAnswerIP4Address(p_hServiceQuery,p_u32AnswerIndex )); - } - std::vector IP4Adresses(){ - std::vector internalIP; - if (IP4AddressAvailable()) { - uint16_t cntIP4Adress = p_pMDNSResponder.answerIP4AddressCount(p_hServiceQuery, p_u32AnswerIndex); - for (uint32_t u2 = 0; u2 < cntIP4Adress; ++u2) { - internalIP.emplace_back(p_pMDNSResponder.answerIP4Address(p_hServiceQuery, p_u32AnswerIndex, u2)); - } - } - return internalIP; - }; - bool txtAvailable() - { - return (p_pMDNSResponder.hasAnswerTxts(p_hServiceQuery, p_u32AnswerIndex)); - } - const char* strKeyValue (){ - return (txtAvailable()) ? - p_pMDNSResponder.answerTxts(p_hServiceQuery, p_u32AnswerIndex) : nullptr; - }; - const KeyValueMap& keyValues() - { - if (txtAvailable() && keyValueMap.size() == 0) - { - for (auto kv = p_pMDNSResponder._answerKeyValue(p_hServiceQuery, p_u32AnswerIndex);kv != nullptr;kv = kv->m_pNext) { - keyValueMap.emplace(std::pair(kv->m_pcKey,kv->m_pcValue)); - } - } - return keyValueMap; - } - const char* value(const char* key) - { - char* result = nullptr; - - for (stcMDNSServiceTxt* pTxt=p_pMDNSResponder._answerKeyValue(p_hServiceQuery, p_u32AnswerIndex); pTxt; pTxt=pTxt->m_pNext) { - if ((key) && - (0 == strcmp(pTxt->m_pcKey, key))) { - result = pTxt->m_pcValue; - break; - } - } - return result; - } - }; -protected: - - /** - * stcMDNSServiceTxt - */ - struct stcMDNSServiceTxt { - stcMDNSServiceTxt* m_pNext; - char* m_pcKey; - char* m_pcValue; - bool m_bTemp; - - stcMDNSServiceTxt(const char* p_pcKey = 0, - const char* p_pcValue = 0, - bool p_bTemp = false); - stcMDNSServiceTxt(const stcMDNSServiceTxt& p_Other); - ~stcMDNSServiceTxt(void); - - stcMDNSServiceTxt& operator=(const stcMDNSServiceTxt& p_Other); - bool clear(void); - - char* allocKey(size_t p_stLength); - bool setKey(const char* p_pcKey, - size_t p_stLength); - bool setKey(const char* p_pcKey); - bool releaseKey(void); - - char* allocValue(size_t p_stLength); - bool setValue(const char* p_pcValue, - size_t p_stLength); - bool setValue(const char* p_pcValue); - bool releaseValue(void); - - bool set(const char* p_pcKey, - const char* p_pcValue, - bool p_bTemp = false); - - bool update(const char* p_pcValue); - - size_t length(void) const; - }; - - /** - * stcMDNSTxts - */ - struct stcMDNSServiceTxts { - stcMDNSServiceTxt* m_pTxts; - - stcMDNSServiceTxts(void); - stcMDNSServiceTxts(const stcMDNSServiceTxts& p_Other); - ~stcMDNSServiceTxts(void); - - stcMDNSServiceTxts& operator=(const stcMDNSServiceTxts& p_Other); - - bool clear(void); - - bool add(stcMDNSServiceTxt* p_pTxt); - bool remove(stcMDNSServiceTxt* p_pTxt); - - bool removeTempTxts(void); - - stcMDNSServiceTxt* find(const char* p_pcKey); - const stcMDNSServiceTxt* find(const char* p_pcKey) const; - stcMDNSServiceTxt* find(const stcMDNSServiceTxt* p_pTxt); - - uint16_t length(void) const; - - size_t c_strLength(void) const; - bool c_str(char* p_pcBuffer); - - size_t bufferLength(void) const; - bool buffer(char* p_pcBuffer); - - bool compare(const stcMDNSServiceTxts& p_Other) const; - bool operator==(const stcMDNSServiceTxts& p_Other) const; - bool operator!=(const stcMDNSServiceTxts& p_Other) const; - }; - - /** - * enuContentFlags - */ - typedef enum _enuContentFlags { - // Host - ContentFlag_A = 0x01, - ContentFlag_PTR_IP4 = 0x02, - ContentFlag_PTR_IP6 = 0x04, - ContentFlag_AAAA = 0x08, - // Service - ContentFlag_PTR_TYPE = 0x10, - ContentFlag_PTR_NAME = 0x20, - ContentFlag_TXT = 0x40, - ContentFlag_SRV = 0x80, - } enuContentFlags; - - /** - * stcMDNS_MsgHeader - */ - struct stcMDNS_MsgHeader { - uint16_t m_u16ID; // Identifier - bool m_1bQR : 1; // Query/Response flag - unsigned char m_4bOpcode : 4; // Operation code - bool m_1bAA : 1; // Authoritative Answer flag - bool m_1bTC : 1; // Truncation flag - bool m_1bRD : 1; // Recursion desired - bool m_1bRA : 1; // Recursion available - unsigned char m_3bZ : 3; // Zero - unsigned char m_4bRCode : 4; // Response code - uint16_t m_u16QDCount; // Question count - uint16_t m_u16ANCount; // Answer count - uint16_t m_u16NSCount; // Authority Record count - uint16_t m_u16ARCount; // Additional Record count - - stcMDNS_MsgHeader(uint16_t p_u16ID = 0, - bool p_bQR = false, - unsigned char p_ucOpcode = 0, - bool p_bAA = false, - bool p_bTC = false, - bool p_bRD = false, - bool p_bRA = false, - unsigned char p_ucRCode = 0, - uint16_t p_u16QDCount = 0, - uint16_t p_u16ANCount = 0, - uint16_t p_u16NSCount = 0, - uint16_t p_u16ARCount = 0); - }; - - /** - * stcMDNS_RRDomain - */ - struct stcMDNS_RRDomain { - char m_acName[MDNS_DOMAIN_MAXLENGTH]; // Encoded domain name - uint16_t m_u16NameLength; // Length (incl. '\0') - - stcMDNS_RRDomain(void); - stcMDNS_RRDomain(const stcMDNS_RRDomain& p_Other); - - stcMDNS_RRDomain& operator=(const stcMDNS_RRDomain& p_Other); - - bool clear(void); - - bool addLabel(const char* p_pcLabel, - bool p_bPrependUnderline = false); - - bool compare(const stcMDNS_RRDomain& p_Other) const; - bool operator==(const stcMDNS_RRDomain& p_Other) const; - bool operator!=(const stcMDNS_RRDomain& p_Other) const; - bool operator>(const stcMDNS_RRDomain& p_Other) const; - - size_t c_strLength(void) const; - bool c_str(char* p_pcBuffer); - }; - - /** - * stcMDNS_RRAttributes - */ - struct stcMDNS_RRAttributes { - uint16_t m_u16Type; // Type - uint16_t m_u16Class; // Class, nearly always 'IN' - - stcMDNS_RRAttributes(uint16_t p_u16Type = 0, - uint16_t p_u16Class = 1 /*DNS_RRCLASS_IN Internet*/); - stcMDNS_RRAttributes(const stcMDNS_RRAttributes& p_Other); - - stcMDNS_RRAttributes& operator=(const stcMDNS_RRAttributes& p_Other); - }; - - /** - * stcMDNS_RRHeader - */ - struct stcMDNS_RRHeader { - stcMDNS_RRDomain m_Domain; - stcMDNS_RRAttributes m_Attributes; - - stcMDNS_RRHeader(void); - stcMDNS_RRHeader(const stcMDNS_RRHeader& p_Other); - - stcMDNS_RRHeader& operator=(const stcMDNS_RRHeader& p_Other); - - bool clear(void); - }; - - /** - * stcMDNS_RRQuestion - */ - struct stcMDNS_RRQuestion { - stcMDNS_RRQuestion* m_pNext; - stcMDNS_RRHeader m_Header; - bool m_bUnicast; // Unicast reply requested - - stcMDNS_RRQuestion(void); - }; - - /** - * enuAnswerType - */ - typedef enum _enuAnswerType { - AnswerType_A, - AnswerType_PTR, - AnswerType_TXT, - AnswerType_AAAA, - AnswerType_SRV, - AnswerType_Generic - } enuAnswerType; - - /** - * stcMDNS_RRAnswer - */ - struct stcMDNS_RRAnswer { - stcMDNS_RRAnswer* m_pNext; - const enuAnswerType m_AnswerType; - stcMDNS_RRHeader m_Header; - bool m_bCacheFlush; // Cache flush command bit - uint32_t m_u32TTL; // Validity time in seconds - - virtual ~stcMDNS_RRAnswer(void); - - enuAnswerType answerType(void) const; - - bool clear(void); - - protected: - stcMDNS_RRAnswer(enuAnswerType p_AnswerType, - const stcMDNS_RRHeader& p_Header, - uint32_t p_u32TTL); - }; - -#ifdef MDNS_IP4_SUPPORT - /** - * stcMDNS_RRAnswerA - */ - struct stcMDNS_RRAnswerA : public stcMDNS_RRAnswer { - IPAddress m_IPAddress; - - stcMDNS_RRAnswerA(const stcMDNS_RRHeader& p_Header, - uint32_t p_u32TTL); - ~stcMDNS_RRAnswerA(void); - - bool clear(void); - }; -#endif - - /** - * stcMDNS_RRAnswerPTR - */ - struct stcMDNS_RRAnswerPTR : public stcMDNS_RRAnswer { - stcMDNS_RRDomain m_PTRDomain; - - stcMDNS_RRAnswerPTR(const stcMDNS_RRHeader& p_Header, - uint32_t p_u32TTL); - ~stcMDNS_RRAnswerPTR(void); - - bool clear(void); - }; - - /** - * stcMDNS_RRAnswerTXT - */ - struct stcMDNS_RRAnswerTXT : public stcMDNS_RRAnswer { - stcMDNSServiceTxts m_Txts; - - stcMDNS_RRAnswerTXT(const stcMDNS_RRHeader& p_Header, - uint32_t p_u32TTL); - ~stcMDNS_RRAnswerTXT(void); - - bool clear(void); - }; - -#ifdef MDNS_IP6_SUPPORT - /** - * stcMDNS_RRAnswerAAAA - */ - struct stcMDNS_RRAnswerAAAA : public stcMDNS_RRAnswer { - //TODO: IP6Address m_IPAddress; - - stcMDNS_RRAnswerAAAA(const stcMDNS_RRHeader& p_Header, - uint32_t p_u32TTL); - ~stcMDNS_RRAnswerAAAA(void); - - bool clear(void); - }; -#endif - - /** - * stcMDNS_RRAnswerSRV - */ - struct stcMDNS_RRAnswerSRV : public stcMDNS_RRAnswer { - uint16_t m_u16Priority; - uint16_t m_u16Weight; - uint16_t m_u16Port; - stcMDNS_RRDomain m_SRVDomain; - - stcMDNS_RRAnswerSRV(const stcMDNS_RRHeader& p_Header, - uint32_t p_u32TTL); - ~stcMDNS_RRAnswerSRV(void); - - bool clear(void); - }; - - /** - * stcMDNS_RRAnswerGeneric - */ - struct stcMDNS_RRAnswerGeneric : public stcMDNS_RRAnswer { - uint16_t m_u16RDLength; // Length of variable answer - uint8_t* m_pu8RDData; // Offset of start of variable answer in packet - - stcMDNS_RRAnswerGeneric(const stcMDNS_RRHeader& p_Header, - uint32_t p_u32TTL); - ~stcMDNS_RRAnswerGeneric(void); - - bool clear(void); - }; - - - /** - * enuProbingStatus - */ - typedef enum _enuProbingStatus { - ProbingStatus_WaitingForData, - ProbingStatus_ReadyToStart, - ProbingStatus_InProgress, - ProbingStatus_Done - } enuProbingStatus; - - /** - * stcProbeInformation - */ - struct stcProbeInformation { - enuProbingStatus m_ProbingStatus; - uint8_t m_u8SentCount; // Used for probes and announcements - esp8266::polledTimeout::oneShotMs m_Timeout; // Used for probes and announcements - //clsMDNSTimeFlag m_TimeFlag; // Used for probes and announcements - bool m_bConflict; - bool m_bTiebreakNeeded; - MDNSHostProbeFn m_fnHostProbeResultCallback; - MDNSServiceProbeFn m_fnServiceProbeResultCallback; - - stcProbeInformation(void); - - bool clear(bool p_bClearUserdata = false); - }; - - - /** - * stcMDNSService - */ - struct stcMDNSService { - stcMDNSService* m_pNext; - char* m_pcName; - bool m_bAutoName; // Name was set automatically to hostname (if no name was supplied) - char* m_pcService; - char* m_pcProtocol; - uint16_t m_u16Port; - uint8_t m_u8ReplyMask; - stcMDNSServiceTxts m_Txts; - MDNSDynamicServiceTxtCallbackFunc m_fnTxtCallback; - stcProbeInformation m_ProbeInformation; - - stcMDNSService(const char* p_pcName = 0, - const char* p_pcService = 0, - const char* p_pcProtocol = 0); - ~stcMDNSService(void); - - bool setName(const char* p_pcName); - bool releaseName(void); - - bool setService(const char* p_pcService); - bool releaseService(void); - - bool setProtocol(const char* p_pcProtocol); - bool releaseProtocol(void); - }; - - /** - * stcMDNSServiceQuery - */ - struct stcMDNSServiceQuery { - /** - * stcAnswer - */ - struct stcAnswer { - /** - * stcTTL - */ - struct stcTTL { - /** - * timeoutLevel_t - */ - typedef uint8_t timeoutLevel_t; - /** - * TIMEOUTLEVELs - */ - const timeoutLevel_t TIMEOUTLEVEL_UNSET = 0; - const timeoutLevel_t TIMEOUTLEVEL_BASE = 80; - const timeoutLevel_t TIMEOUTLEVEL_INTERVAL = 5; - const timeoutLevel_t TIMEOUTLEVEL_FINAL = 100; - - uint32_t m_u32TTL; - esp8266::polledTimeout::oneShotMs m_TTLTimeout; - timeoutLevel_t m_timeoutLevel; - - stcTTL(void); - bool set(uint32_t p_u32TTL); - - bool flagged(void); - bool restart(void); - - bool prepareDeletion(void); - bool finalTimeoutLevel(void) const; - - unsigned long timeout(void) const; - }; -#ifdef MDNS_IP4_SUPPORT - /** - * stcIP4Address - */ - struct stcIP4Address { - stcIP4Address* m_pNext; - IPAddress m_IPAddress; - stcTTL m_TTL; - - stcIP4Address(IPAddress p_IPAddress, - uint32_t p_u32TTL = 0); - }; -#endif -#ifdef MDNS_IP6_SUPPORT - /** - * stcIP6Address - */ - struct stcIP6Address { - stcIP6Address* m_pNext; - IP6Address m_IPAddress; - stcTTL m_TTL; - - stcIP6Address(IPAddress p_IPAddress, - uint32_t p_u32TTL = 0); - }; -#endif - - stcAnswer* m_pNext; - // The service domain is the first 'answer' (from PTR answer, using service and protocol) to be set - // Defines the key for additional answer, like host domain, etc. - stcMDNS_RRDomain m_ServiceDomain; // 1. level answer (PTR), eg. MyESP._http._tcp.local - char* m_pcServiceDomain; - stcTTL m_TTLServiceDomain; - stcMDNS_RRDomain m_HostDomain; // 2. level answer (SRV, using service domain), eg. esp8266.local - char* m_pcHostDomain; - uint16_t m_u16Port; // 2. level answer (SRV, using service domain), eg. 5000 - stcTTL m_TTLHostDomainAndPort; - stcMDNSServiceTxts m_Txts; // 2. level answer (TXT, using service domain), eg. c#=1 - char* m_pcTxts; - stcTTL m_TTLTxts; -#ifdef MDNS_IP4_SUPPORT - stcIP4Address* m_pIP4Addresses; // 3. level answer (A, using host domain), eg. 123.456.789.012 -#endif -#ifdef MDNS_IP6_SUPPORT - stcIP6Address* m_pIP6Addresses; // 3. level answer (AAAA, using host domain), eg. 1234::09 -#endif - uint32_t m_u32ContentFlags; - - stcAnswer(void); - ~stcAnswer(void); - - bool clear(void); - - char* allocServiceDomain(size_t p_stLength); - bool releaseServiceDomain(void); - - char* allocHostDomain(size_t p_stLength); - bool releaseHostDomain(void); - - char* allocTxts(size_t p_stLength); - bool releaseTxts(void); - -#ifdef MDNS_IP4_SUPPORT - bool releaseIP4Addresses(void); - bool addIP4Address(stcIP4Address* p_pIP4Address); - bool removeIP4Address(stcIP4Address* p_pIP4Address); - const stcIP4Address* findIP4Address(const IPAddress& p_IPAddress) const; - stcIP4Address* findIP4Address(const IPAddress& p_IPAddress); - uint32_t IP4AddressCount(void) const; - const stcIP4Address* IP4AddressAtIndex(uint32_t p_u32Index) const; - stcIP4Address* IP4AddressAtIndex(uint32_t p_u32Index); -#endif -#ifdef MDNS_IP6_SUPPORT - bool releaseIP6Addresses(void); - bool addIP6Address(stcIP6Address* p_pIP6Address); - bool removeIP6Address(stcIP6Address* p_pIP6Address); - const stcIP6Address* findIP6Address(const IPAddress& p_IPAddress) const; - stcIP6Address* findIP6Address(const IPAddress& p_IPAddress); - uint32_t IP6AddressCount(void) const; - const stcIP6Address* IP6AddressAtIndex(uint32_t p_u32Index) const; - stcIP6Address* IP6AddressAtIndex(uint32_t p_u32Index); -#endif - }; - - stcMDNSServiceQuery* m_pNext; - stcMDNS_RRDomain m_ServiceTypeDomain; // eg. _http._tcp.local - MDNSServiceQueryCallbackFunc m_fnCallback; - bool m_bLegacyQuery; - uint8_t m_u8SentCount; - esp8266::polledTimeout::oneShotMs m_ResendTimeout; - bool m_bAwaitingAnswers; - stcAnswer* m_pAnswers; - - stcMDNSServiceQuery(void); - ~stcMDNSServiceQuery(void); - - bool clear(void); - - uint32_t answerCount(void) const; - const stcAnswer* answerAtIndex(uint32_t p_u32Index) const; - stcAnswer* answerAtIndex(uint32_t p_u32Index); - uint32_t indexOfAnswer(const stcAnswer* p_pAnswer) const; - - bool addAnswer(stcAnswer* p_pAnswer); - bool removeAnswer(stcAnswer* p_pAnswer); - - stcAnswer* findAnswerForServiceDomain(const stcMDNS_RRDomain& p_ServiceDomain); - stcAnswer* findAnswerForHostDomain(const stcMDNS_RRDomain& p_HostDomain); - }; - - /** - * stcMDNSSendParameter - */ - struct stcMDNSSendParameter { - protected: - /** - * stcDomainCacheItem - */ - struct stcDomainCacheItem { - stcDomainCacheItem* m_pNext; - const void* m_pHostnameOrService; // Opaque id for host or service domain (pointer) - bool m_bAdditionalData; // Opaque flag for special info (service domain included) - uint16_t m_u16Offset; // Offset in UDP output buffer - - stcDomainCacheItem(const void* p_pHostnameOrService, - bool p_bAdditionalData, - uint32_t p_u16Offset); - }; - - public: - uint16_t m_u16ID; // Query ID (used only in lagacy queries) - stcMDNS_RRQuestion* m_pQuestions; // A list of queries - uint8_t m_u8HostReplyMask; // Flags for reply components/answers - bool m_bLegacyQuery; // Flag: Legacy query - bool m_bResponse; // Flag: Response to a query - bool m_bAuthorative; // Flag: Authorative (owner) response - bool m_bCacheFlush; // Flag: Clients should flush their caches - bool m_bUnicast; // Flag: Unicast response - bool m_bUnannounce; // Flag: Unannounce service - uint16_t m_u16Offset; // Current offset in UDP write buffer (mainly for domain cache) - stcDomainCacheItem* m_pDomainCacheItems; // Cached host and service domains - - stcMDNSSendParameter(void); - ~stcMDNSSendParameter(void); - - bool clear(void); - - bool shiftOffset(uint16_t p_u16Shift); - - bool addDomainCacheItem(const void* p_pHostnameOrService, - bool p_bAdditionalData, - uint16_t p_u16Offset); - uint16_t findCachedDomainOffset(const void* p_pHostnameOrService, - bool p_bAdditionalData) const; - }; - - // Instance variables - stcMDNSService* m_pServices; - UdpContext* m_pUDPContext; - char* m_pcHostname; - stcMDNSServiceQuery* m_pServiceQueries; - WiFiEventHandler m_DisconnectedHandler; - WiFiEventHandler m_GotIPHandler; - MDNSDynamicServiceTxtCallbackFunc m_fnServiceTxtCallback; - bool m_bPassivModeEnabled; - stcProbeInformation m_HostProbeInformation; - CONST netif* m_netif; // network interface to run on - - /** CONTROL **/ - /* MAINTENANCE */ - bool _process(bool p_bUserContext); - bool _restart(void); - - /* RECEIVING */ - bool _parseMessage(void); - bool _parseQuery(const stcMDNS_MsgHeader& p_Header); - - bool _parseResponse(const stcMDNS_MsgHeader& p_Header); - bool _processAnswers(const stcMDNS_RRAnswer* p_pPTRAnswers); - bool _processPTRAnswer(const stcMDNS_RRAnswerPTR* p_pPTRAnswer, - bool& p_rbFoundNewKeyAnswer); - bool _processSRVAnswer(const stcMDNS_RRAnswerSRV* p_pSRVAnswer, - bool& p_rbFoundNewKeyAnswer); - bool _processTXTAnswer(const stcMDNS_RRAnswerTXT* p_pTXTAnswer); -#ifdef MDNS_IP4_SUPPORT - bool _processAAnswer(const stcMDNS_RRAnswerA* p_pAAnswer); -#endif -#ifdef MDNS_IP6_SUPPORT - bool _processAAAAAnswer(const stcMDNS_RRAnswerAAAA* p_pAAAAAnswer); -#endif - - /* PROBING */ - bool _updateProbeStatus(void); - bool _resetProbeStatus(bool p_bRestart = true); - bool _hasProbesWaitingForAnswers(void) const; - bool _sendHostProbe(void); - bool _sendServiceProbe(stcMDNSService& p_rService); - bool _cancelProbingForHost(void); - bool _cancelProbingForService(stcMDNSService& p_rService); - - /* ANNOUNCE */ - bool _announce(bool p_bAnnounce, - bool p_bIncludeServices); - bool _announceService(stcMDNSService& p_rService, - bool p_bAnnounce = true); - - /* SERVICE QUERY CACHE */ - bool _hasServiceQueriesWaitingForAnswers(void) const; - bool _checkServiceQueryCache(void); - - /** TRANSFER **/ - /* SENDING */ - bool _sendMDNSMessage(stcMDNSSendParameter& p_SendParameter); - bool _sendMDNSMessage_Multicast(MDNSResponder::stcMDNSSendParameter& p_rSendParameter); - bool _prepareMDNSMessage(stcMDNSSendParameter& p_SendParameter, - IPAddress p_IPAddress); - bool _sendMDNSServiceQuery(const stcMDNSServiceQuery& p_ServiceQuery); - bool _sendMDNSQuery(const stcMDNS_RRDomain& p_QueryDomain, - uint16_t p_u16QueryType, - stcMDNSServiceQuery::stcAnswer* p_pKnownAnswers = 0); - - const IPAddress _getResponseMulticastInterface() const { return IPAddress(m_netif->ip_addr); } - - uint8_t _replyMaskForHost(const stcMDNS_RRHeader& p_RRHeader, - bool* p_pbFullNameMatch = 0) const; - uint8_t _replyMaskForService(const stcMDNS_RRHeader& p_RRHeader, - const stcMDNSService& p_Service, - bool* p_pbFullNameMatch = 0) const; - - /* RESOURCE RECORD */ - bool _readRRQuestion(stcMDNS_RRQuestion& p_rQuestion); - bool _readRRAnswer(stcMDNS_RRAnswer*& p_rpAnswer); -#ifdef MDNS_IP4_SUPPORT - bool _readRRAnswerA(stcMDNS_RRAnswerA& p_rRRAnswerA, - uint16_t p_u16RDLength); -#endif - bool _readRRAnswerPTR(stcMDNS_RRAnswerPTR& p_rRRAnswerPTR, - uint16_t p_u16RDLength); - bool _readRRAnswerTXT(stcMDNS_RRAnswerTXT& p_rRRAnswerTXT, - uint16_t p_u16RDLength); -#ifdef MDNS_IP6_SUPPORT - bool _readRRAnswerAAAA(stcMDNS_RRAnswerAAAA& p_rRRAnswerAAAA, - uint16_t p_u16RDLength); -#endif - bool _readRRAnswerSRV(stcMDNS_RRAnswerSRV& p_rRRAnswerSRV, - uint16_t p_u16RDLength); - bool _readRRAnswerGeneric(stcMDNS_RRAnswerGeneric& p_rRRAnswerGeneric, - uint16_t p_u16RDLength); - - bool _readRRHeader(stcMDNS_RRHeader& p_rHeader); - bool _readRRDomain(stcMDNS_RRDomain& p_rRRDomain); - bool _readRRDomain_Loop(stcMDNS_RRDomain& p_rRRDomain, - uint8_t p_u8Depth); - bool _readRRAttributes(stcMDNS_RRAttributes& p_rAttributes); - - /* DOMAIN NAMES */ - bool _buildDomainForHost(const char* p_pcHostname, - stcMDNS_RRDomain& p_rHostDomain) const; - bool _buildDomainForDNSSD(stcMDNS_RRDomain& p_rDNSSDDomain) const; - bool _buildDomainForService(const stcMDNSService& p_Service, - bool p_bIncludeName, - stcMDNS_RRDomain& p_rServiceDomain) const; - bool _buildDomainForService(const char* p_pcService, - const char* p_pcProtocol, - stcMDNS_RRDomain& p_rServiceDomain) const; -#ifdef MDNS_IP4_SUPPORT - bool _buildDomainForReverseIP4(IPAddress p_IP4Address, - stcMDNS_RRDomain& p_rReverseIP4Domain) const; -#endif -#ifdef MDNS_IP6_SUPPORT - bool _buildDomainForReverseIP6(IPAddress p_IP4Address, - stcMDNS_RRDomain& p_rReverseIP6Domain) const; -#endif - - /* UDP */ - bool _udpReadBuffer(unsigned char* p_pBuffer, - size_t p_stLength); - bool _udpRead8(uint8_t& p_ru8Value); - bool _udpRead16(uint16_t& p_ru16Value); - bool _udpRead32(uint32_t& p_ru32Value); - - bool _udpAppendBuffer(const unsigned char* p_pcBuffer, - size_t p_stLength); - bool _udpAppend8(uint8_t p_u8Value); - bool _udpAppend16(uint16_t p_u16Value); - bool _udpAppend32(uint32_t p_u32Value); - -#if not defined ESP_8266_MDNS_INCLUDE || defined DEBUG_ESP_MDNS_RESPONDER - bool _udpDump(bool p_bMovePointer = false); - bool _udpDump(unsigned p_uOffset, - unsigned p_uLength); -#endif - - /* READ/WRITE MDNS STRUCTS */ - bool _readMDNSMsgHeader(stcMDNS_MsgHeader& p_rMsgHeader); - - bool _write8(uint8_t p_u8Value, - stcMDNSSendParameter& p_rSendParameter); - bool _write16(uint16_t p_u16Value, - stcMDNSSendParameter& p_rSendParameter); - bool _write32(uint32_t p_u32Value, - stcMDNSSendParameter& p_rSendParameter); - - bool _writeMDNSMsgHeader(const stcMDNS_MsgHeader& p_MsgHeader, - stcMDNSSendParameter& p_rSendParameter); - bool _writeMDNSRRAttributes(const stcMDNS_RRAttributes& p_Attributes, - stcMDNSSendParameter& p_rSendParameter); - bool _writeMDNSRRDomain(const stcMDNS_RRDomain& p_Domain, - stcMDNSSendParameter& p_rSendParameter); - bool _writeMDNSHostDomain(const char* m_pcHostname, - bool p_bPrependRDLength, - stcMDNSSendParameter& p_rSendParameter); - bool _writeMDNSServiceDomain(const stcMDNSService& p_Service, - bool p_bIncludeName, - bool p_bPrependRDLength, - stcMDNSSendParameter& p_rSendParameter); - - bool _writeMDNSQuestion(stcMDNS_RRQuestion& p_Question, - stcMDNSSendParameter& p_rSendParameter); - -#ifdef MDNS_IP4_SUPPORT - bool _writeMDNSAnswer_A(IPAddress p_IPAddress, - stcMDNSSendParameter& p_rSendParameter); - bool _writeMDNSAnswer_PTR_IP4(IPAddress p_IPAddress, - stcMDNSSendParameter& p_rSendParameter); -#endif - bool _writeMDNSAnswer_PTR_TYPE(stcMDNSService& p_rService, - stcMDNSSendParameter& p_rSendParameter); - bool _writeMDNSAnswer_PTR_NAME(stcMDNSService& p_rService, - stcMDNSSendParameter& p_rSendParameter); - bool _writeMDNSAnswer_TXT(stcMDNSService& p_rService, - stcMDNSSendParameter& p_rSendParameter); -#ifdef MDNS_IP6_SUPPORT - bool _writeMDNSAnswer_AAAA(IPAddress p_IPAddress, - stcMDNSSendParameter& p_rSendParameter); - bool _writeMDNSAnswer_PTR_IP6(IPAddress p_IPAddress, - stcMDNSSendParameter& p_rSendParameter); -#endif - bool _writeMDNSAnswer_SRV(stcMDNSService& p_rService, - stcMDNSSendParameter& p_rSendParameter); - - /** HELPERS **/ - /* UDP CONTEXT */ - bool _callProcess(void); - bool _allocUDPContext(void); - bool _releaseUDPContext(void); - - /* SERVICE QUERY */ - stcMDNSServiceQuery* _allocServiceQuery(void); - bool _removeServiceQuery(stcMDNSServiceQuery* p_pServiceQuery); - bool _removeLegacyServiceQuery(void); - stcMDNSServiceQuery* _findServiceQuery(hMDNSServiceQuery p_hServiceQuery); - stcMDNSServiceQuery* _findLegacyServiceQuery(void); - bool _releaseServiceQueries(void); - stcMDNSServiceQuery* _findNextServiceQueryByServiceType(const stcMDNS_RRDomain& p_ServiceDomain, - const stcMDNSServiceQuery* p_pPrevServiceQuery); - - /* HOSTNAME */ - bool _setHostname(const char* p_pcHostname); - bool _releaseHostname(void); - - /* SERVICE */ - stcMDNSService* _allocService(const char* p_pcName, - const char* p_pcService, - const char* p_pcProtocol, - uint16_t p_u16Port); - bool _releaseService(stcMDNSService* p_pService); - bool _releaseServices(void); - - stcMDNSService* _findService(const char* p_pcName, - const char* p_pcService, - const char* p_pcProtocol); - stcMDNSService* _findService(const hMDNSService p_hService); - - size_t _countServices(void) const; - - /* SERVICE TXT */ - stcMDNSServiceTxt* _allocServiceTxt(stcMDNSService* p_pService, - const char* p_pcKey, - const char* p_pcValue, - bool p_bTemp); - bool _releaseServiceTxt(stcMDNSService* p_pService, - stcMDNSServiceTxt* p_pTxt); - stcMDNSServiceTxt* _updateServiceTxt(stcMDNSService* p_pService, - stcMDNSServiceTxt* p_pTxt, - const char* p_pcValue, - bool p_bTemp); - - stcMDNSServiceTxt* _findServiceTxt(stcMDNSService* p_pService, - const char* p_pcKey); - stcMDNSServiceTxt* _findServiceTxt(stcMDNSService* p_pService, - const hMDNSTxt p_hTxt); - - stcMDNSServiceTxt* _addServiceTxt(stcMDNSService* p_pService, - const char* p_pcKey, - const char* p_pcValue, - bool p_bTemp); - - stcMDNSServiceTxt* _answerKeyValue(const hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex); - - bool _collectServiceTxts(stcMDNSService& p_rService); - bool _releaseTempServiceTxts(stcMDNSService& p_rService); - const stcMDNSServiceTxt* _serviceTxts(const char* p_pcName, - const char* p_pcService, - const char* p_pcProtocol); - - /* MISC */ -#if not defined ESP_8266_MDNS_INCLUDE || defined DEBUG_ESP_MDNS_RESPONDER - bool _printRRDomain(const stcMDNS_RRDomain& p_rRRDomain) const; - bool _printRRAnswer(const MDNSResponder::stcMDNS_RRAnswer& p_RRAnswer) const; -#endif -}; - -}// namespace MDNSImplementation - -}// namespace esp8266 - -#endif // MDNS_H +/* + LEAmDNS.h + (c) 2018, LaborEtArs + + Version 0.9 beta + + Some notes (from LaborEtArs, 2018): + Essentially, this is an rewrite of the original EPS8266 Multicast DNS code (ESP8266mDNS). + The target of this rewrite was to keep the existing interface as stable as possible while + adding and extending the supported set of mDNS features. + A lot of the additions were basicly taken from Erik Ekman's lwIP mdns app code. + + Supported mDNS features (in some cases somewhat limited): + - Presenting a DNS-SD service to interested observers, eg. a http server by presenting _http._tcp service + - Support for multi-level compressed names in input; in output only a very simple one-leven full-name compression is implemented + - Probing host and service domains for uniqueness in the local network + - Tiebreaking while probing is supportet in a very minimalistic way (the 'higher' IP address wins the tiebreak) + - Announcing available services after successful probing + - Using fixed service TXT items or + - Using dynamic service TXT items for presented services (via callback) + - Remove services (and un-announcing them to the observers by sending goodbye-messages) + - Static queries for DNS-SD services (creating a fixed answer set after a certain timeout period) + - Dynamic queries for DNS-SD services with cached and updated answers and user notifications + + + Usage: + In most cases, this implementation should work as a 'drop-in' replacement for the original + ESP8266 Multicast DNS code. Adjustments to the existing code would only be needed, if some + of the new features should be used. + + For presenting services: + In 'setup()': + Install a callback for the probing of host (and service) domains via 'MDNS.setProbeResultCallback(probeResultCallback, &userData);' + Register DNS-SD services with 'MDNSResponder::hMDNSService hService = MDNS.addService("MyESP", "http", "tcp", 5000);' + (Install additional callbacks for the probing of these service domains via 'MDNS.setServiceProbeResultCallback(hService, probeResultCallback, &userData);') + Add service TXT items with 'MDNS.addServiceTxt(hService, "c#", "1");' or by installing a service TXT callback + using 'MDNS.setDynamicServiceTxtCallback(dynamicServiceTxtCallback, &userData);' or service specific + 'MDNS.setDynamicServiceTxtCallback(hService, dynamicServiceTxtCallback, &userData);' + Call MDNS.begin("MyHostname"); + + In 'probeResultCallback(MDNSResponder* p_MDNSResponder, const char* p_pcDomain, MDNSResponder:hMDNSService p_hService, bool p_bProbeResult, void* p_pUserdata)': + Check the probe result and update the host or service domain name if the probe failed + + In 'dynamicServiceTxtCallback(MDNSResponder* p_MDNSResponder, const hMDNSService p_hService, void* p_pUserdata)': + Add dynamic TXT items by calling 'MDNS.addDynamicServiceTxt(p_hService, "c#", "1");' + + In loop(): + Call 'MDNS.update();' + + + For querying services: + Static: + Call 'uint32_t u32AnswerCount = MDNS.queryService("http", "tcp");' + Iterate answers by: 'for (uint32_t u=0; u // for UdpContext.h +#include "WiFiUdp.h" +#include "lwip/udp.h" +#include "debug.h" +#include "include/UdpContext.h" +#include +#include +#include + + +#include "ESP8266WiFi.h" + + +namespace esp8266 +{ + +/** + LEAmDNS +*/ +namespace MDNSImplementation +{ + +//this should be defined at build time +#ifndef ARDUINO_BOARD +#define ARDUINO_BOARD "generic" +#endif + +#define MDNS_IP4_SUPPORT +//#define MDNS_IP6_SUPPORT + + +#ifdef MDNS_IP4_SUPPORT +#define MDNS_IP4_SIZE 4 +#endif +#ifdef MDNS_IP6_SUPPORT +#define MDNS_IP6_SIZE 16 +#endif +/* + Maximum length for all service txts for one service +*/ +#define MDNS_SERVICE_TXT_MAXLENGTH 1300 +/* + Maximum length for a full domain name eg. MyESP._http._tcp.local +*/ +#define MDNS_DOMAIN_MAXLENGTH 256 +/* + Maximum length of on label in a domain name (length info fits into 6 bits) +*/ +#define MDNS_DOMAIN_LABEL_MAXLENGTH 63 +/* + Maximum length of a service name eg. http +*/ +#define MDNS_SERVICE_NAME_LENGTH 15 +/* + Maximum length of a service protocol name eg. tcp +*/ +#define MDNS_SERVICE_PROTOCOL_LENGTH 3 +/* + Default timeout for static service queries +*/ +#define MDNS_QUERYSERVICES_WAIT_TIME 1000 + + +/** + MDNSResponder +*/ +class MDNSResponder +{ +public: + /* INTERFACE */ + MDNSResponder(void); + virtual ~MDNSResponder(void); + + // Start the MDNS responder by setting the default hostname + // Later call MDNS::update() in every 'loop' to run the process loop + // (probing, announcing, responding, ...) + // if interfaceAddress is not specified, default interface is STA, or AP when STA is not set + bool begin(const char* p_pcHostname, const IPAddress& p_IPAddress = INADDR_ANY, uint32_t p_u32TTL = 120 /*ignored*/); + bool begin(const String& p_strHostname, const IPAddress& p_IPAddress = INADDR_ANY, uint32_t p_u32TTL = 120 /*ignored*/) + { + return begin(p_strHostname.c_str(), p_IPAddress, p_u32TTL); + } + + // Finish MDNS processing + bool close(void); + // for esp32 compatability + bool end(void); + // Change hostname (probing is restarted) + bool setHostname(const char* p_pcHostname); + // for compatibility... + bool setHostname(String p_strHostname); + + /** + hMDNSService (opaque handle to access the service) + */ + typedef const void* hMDNSService; + + // Add a new service to the MDNS responder. If no name (instance name) is given (p_pcName = 0) + // the current hostname is used. If the hostname is changed later, the instance names for + // these 'auto-named' services are changed to the new name also (and probing is restarted). + // The usual '_' before p_pcService (eg. http) and protocol (eg. tcp) may be given. + hMDNSService addService(const char* p_pcName, + const char* p_pcService, + const char* p_pcProtocol, + uint16_t p_u16Port); + // Removes a service from the MDNS responder + bool removeService(const hMDNSService p_hService); + bool removeService(const char* p_pcInstanceName, + const char* p_pcServiceName, + const char* p_pcProtocol); + // for compatibility... + bool addService(String p_strServiceName, + String p_strProtocol, + uint16_t p_u16Port); + + + // Change the services instance name (and restart probing). + bool setServiceName(const hMDNSService p_hService, + const char* p_pcInstanceName); + //for compatibility + //Warning: this has the side effect of changing the hostname. + //TODO: implement instancename different from hostname + void setInstanceName(const char* p_pcHostname) + { + setHostname(p_pcHostname); + } + // for esp32 compatibilty + void setInstanceName(const String& s_pcHostname) + { + setInstanceName(s_pcHostname.c_str()); + } + + /** + hMDNSTxt (opaque handle to access the TXT items) + */ + typedef void* hMDNSTxt; + + // Add a (static) MDNS TXT item ('key' = 'value') to the service + hMDNSTxt addServiceTxt(const hMDNSService p_hService, + const char* p_pcKey, + const char* p_pcValue); + hMDNSTxt addServiceTxt(const hMDNSService p_hService, + const char* p_pcKey, + uint32_t p_u32Value); + hMDNSTxt addServiceTxt(const hMDNSService p_hService, + const char* p_pcKey, + uint16_t p_u16Value); + hMDNSTxt addServiceTxt(const hMDNSService p_hService, + const char* p_pcKey, + uint8_t p_u8Value); + hMDNSTxt addServiceTxt(const hMDNSService p_hService, + const char* p_pcKey, + int32_t p_i32Value); + hMDNSTxt addServiceTxt(const hMDNSService p_hService, + const char* p_pcKey, + int16_t p_i16Value); + hMDNSTxt addServiceTxt(const hMDNSService p_hService, + const char* p_pcKey, + int8_t p_i8Value); + + // Remove an existing (static) MDNS TXT item from the service + bool removeServiceTxt(const hMDNSService p_hService, + const hMDNSTxt p_hTxt); + bool removeServiceTxt(const hMDNSService p_hService, + const char* p_pcKey); + bool removeServiceTxt(const char* p_pcinstanceName, + const char* p_pcServiceName, + const char* p_pcProtocol, + const char* p_pcKey); + // for compatibility... + bool addServiceTxt(const char* p_pcService, + const char* p_pcProtocol, + const char* p_pcKey, + const char* p_pcValue); + bool addServiceTxt(String p_strService, + String p_strProtocol, + String p_strKey, + String p_strValue); + + /** + MDNSDynamicServiceTxtCallbackFn + Callback function for dynamic MDNS TXT items + */ + + typedef std::function MDNSDynamicServiceTxtCallbackFunc; + + // Set a global callback for dynamic MDNS TXT items. The callback function is called + // every time, a TXT item is needed for one of the installed services. + bool setDynamicServiceTxtCallback(MDNSDynamicServiceTxtCallbackFunc p_fnCallback); + // Set a service specific callback for dynamic MDNS TXT items. The callback function + // is called every time, a TXT item is needed for the given service. + bool setDynamicServiceTxtCallback(const hMDNSService p_hService, + MDNSDynamicServiceTxtCallbackFunc p_fnCallback); + + // Add a (dynamic) MDNS TXT item ('key' = 'value') to the service + // Dynamic TXT items are removed right after one-time use. So they need to be added + // every time the value s needed (via callback). + hMDNSTxt addDynamicServiceTxt(hMDNSService p_hService, + const char* p_pcKey, + const char* p_pcValue); + hMDNSTxt addDynamicServiceTxt(hMDNSService p_hService, + const char* p_pcKey, + uint32_t p_u32Value); + hMDNSTxt addDynamicServiceTxt(hMDNSService p_hService, + const char* p_pcKey, + uint16_t p_u16Value); + hMDNSTxt addDynamicServiceTxt(hMDNSService p_hService, + const char* p_pcKey, + uint8_t p_u8Value); + hMDNSTxt addDynamicServiceTxt(hMDNSService p_hService, + const char* p_pcKey, + int32_t p_i32Value); + hMDNSTxt addDynamicServiceTxt(hMDNSService p_hService, + const char* p_pcKey, + int16_t p_i16Value); + hMDNSTxt addDynamicServiceTxt(hMDNSService p_hService, + const char* p_pcKey, + int8_t p_i8Value); + + // Perform a (static) service query. The function returns after p_u16Timeout milliseconds + // The answers (the number of received answers is returned) can be retrieved by calling + // - answerHostname (or hostname) + // - answerIP (or IP) + // - answerPort (or port) + uint32_t queryService(const char* p_pcService, + const char* p_pcProtocol, + const uint16_t p_u16Timeout = MDNS_QUERYSERVICES_WAIT_TIME); + bool removeQuery(void); + // for compatibility... + uint32_t queryService(String p_strService, + String p_strProtocol); + + const char* answerHostname(const uint32_t p_u32AnswerIndex); + IPAddress answerIP(const uint32_t p_u32AnswerIndex); + uint16_t answerPort(const uint32_t p_u32AnswerIndex); + // for compatibility... + String hostname(const uint32_t p_u32AnswerIndex); + IPAddress IP(const uint32_t p_u32AnswerIndex); + uint16_t port(const uint32_t p_u32AnswerIndex); + + /** + hMDNSServiceQuery (opaque handle to access dynamic service queries) + */ + typedef const void* hMDNSServiceQuery; + + /** + enuServiceQueryAnswerType + */ + typedef enum _enuServiceQueryAnswerType + { + ServiceQueryAnswerType_ServiceDomain = (1 << 0), // Service instance name + ServiceQueryAnswerType_HostDomainAndPort = (1 << 1), // Host domain and service port + ServiceQueryAnswerType_Txts = (1 << 2), // TXT items +#ifdef MDNS_IP4_SUPPORT + ServiceQueryAnswerType_IP4Address = (1 << 3), // IP4 address +#endif +#ifdef MDNS_IP6_SUPPORT + ServiceQueryAnswerType_IP6Address = (1 << 4), // IP6 address +#endif + } enuServiceQueryAnswerType; + + enum class AnswerType : uint32_t + { + Unknown = 0, + ServiceDomain = ServiceQueryAnswerType_ServiceDomain, + HostDomainAndPort = ServiceQueryAnswerType_HostDomainAndPort, + Txt = ServiceQueryAnswerType_Txts, +#ifdef MDNS_IP4_SUPPORT + IP4Address = ServiceQueryAnswerType_IP4Address, +#endif +#ifdef MDNS_IP6_SUPPORT + IP6Address = ServiceQueryAnswerType_IP6Address, +#endif + }; + + /** + MDNSServiceQueryCallbackFn + Callback function for received answers for dynamic service queries + */ + struct MDNSServiceInfo; // forward declaration + typedef std::function MDNSServiceQueryCallbackFunc; + + // Install a dynamic service query. For every received answer (part) the given callback + // function is called. The query will be updated every time, the TTL for an answer + // has timed-out. + // The answers can also be retrieved by calling + // - answerCount + // - answerServiceDomain + // - hasAnswerHostDomain/answerHostDomain + // - hasAnswerIP4Address/answerIP4Address + // - hasAnswerIP6Address/answerIP6Address + // - hasAnswerPort/answerPort + // - hasAnswerTxts/answerTxts + hMDNSServiceQuery installServiceQuery(const char* p_pcService, + const char* p_pcProtocol, + MDNSServiceQueryCallbackFunc p_fnCallback); + // Remove a dynamic service query + bool removeServiceQuery(hMDNSServiceQuery p_hServiceQuery); + + uint32_t answerCount(const hMDNSServiceQuery p_hServiceQuery); + std::vector answerInfo(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery); + + const char* answerServiceDomain(const hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex); + bool hasAnswerHostDomain(const hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex); + const char* answerHostDomain(const hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex); +#ifdef MDNS_IP4_SUPPORT + bool hasAnswerIP4Address(const hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex); + uint32_t answerIP4AddressCount(const hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex); + IPAddress answerIP4Address(const hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex, + const uint32_t p_u32AddressIndex); +#endif +#ifdef MDNS_IP6_SUPPORT + bool hasAnswerIP6Address(const hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex); + uint32_t answerIP6AddressCount(const hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex); + IPAddress answerIP6Address(const hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex, + const uint32_t p_u32AddressIndex); +#endif + bool hasAnswerPort(const hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex); + uint16_t answerPort(const hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex); + bool hasAnswerTxts(const hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex); + // Get the TXT items as a ';'-separated string + const char* answerTxts(const hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex); + + /** + MDNSProbeResultCallbackFn + Callback function for (host and service domain) probe results + */ + typedef std::function MDNSHostProbeFn; + + typedef std::function MDNSHostProbeFn1; + + typedef std::function MDNSServiceProbeFn; + + typedef std::function MDNSServiceProbeFn1; + + // Set a global callback function for host and service probe results + // The callback function is called, when the probing for the host domain + // (or a service domain, which hasn't got a service specific callback) + // Succeeds or fails. + // In case of failure, the failed domain name should be changed. + bool setHostProbeResultCallback(MDNSHostProbeFn p_fnCallback); + bool setHostProbeResultCallback(MDNSHostProbeFn1 p_fnCallback); + + // Set a service specific probe result callback + bool setServiceProbeResultCallback(const MDNSResponder::hMDNSService p_hService, + MDNSServiceProbeFn p_fnCallback); + bool setServiceProbeResultCallback(const MDNSResponder::hMDNSService p_hService, + MDNSServiceProbeFn1 p_fnCallback); + + // Application should call this whenever AP is configured/disabled + bool notifyAPChange(void); + + // 'update' should be called in every 'loop' to run the MDNS processing + bool update(void); + + // 'announce' can be called every time, the configuration of some service + // changes. Mainly, this would be changed content of TXT items. + bool announce(void); + + // Enable OTA update + hMDNSService enableArduino(uint16_t p_u16Port, + bool p_bAuthUpload = false); + + // Domain name helper + static bool indexDomain(char*& p_rpcDomain, + const char* p_pcDivider = "-", + const char* p_pcDefaultDomain = 0); + + /** STRUCTS **/ + +public: + /** + MDNSServiceInfo, used in application callbacks + */ + struct MDNSServiceInfo + { + MDNSServiceInfo(MDNSResponder& p_pM, MDNSResponder::hMDNSServiceQuery p_hS, uint32_t p_u32A) + : p_pMDNSResponder(p_pM), + p_hServiceQuery(p_hS), + p_u32AnswerIndex(p_u32A) + {}; + struct CompareKey + { + bool operator()(char const *a, char const *b) const + { + return strcmp(a, b) < 0; + } + }; + using KeyValueMap = std::map; + protected: + MDNSResponder& p_pMDNSResponder; + MDNSResponder::hMDNSServiceQuery p_hServiceQuery; + uint32_t p_u32AnswerIndex; + KeyValueMap keyValueMap; + public: + const char* serviceDomain() + { + return p_pMDNSResponder.answerServiceDomain(p_hServiceQuery, p_u32AnswerIndex); + }; + bool hostDomainAvailable() + { + return (p_pMDNSResponder.hasAnswerHostDomain(p_hServiceQuery, p_u32AnswerIndex)); + } + const char* hostDomain() + { + return (hostDomainAvailable()) ? + p_pMDNSResponder.answerHostDomain(p_hServiceQuery, p_u32AnswerIndex) : nullptr; + }; + bool hostPortAvailable() + { + return (p_pMDNSResponder.hasAnswerPort(p_hServiceQuery, p_u32AnswerIndex)); + } + uint16_t hostPort() + { + return (hostPortAvailable()) ? + p_pMDNSResponder.answerPort(p_hServiceQuery, p_u32AnswerIndex) : 0; + }; + bool IP4AddressAvailable() + { + return (p_pMDNSResponder.hasAnswerIP4Address(p_hServiceQuery, p_u32AnswerIndex)); + } + std::vector IP4Adresses() + { + std::vector internalIP; + if (IP4AddressAvailable()) + { + uint16_t cntIP4Adress = p_pMDNSResponder.answerIP4AddressCount(p_hServiceQuery, p_u32AnswerIndex); + for (uint32_t u2 = 0; u2 < cntIP4Adress; ++u2) + { + internalIP.emplace_back(p_pMDNSResponder.answerIP4Address(p_hServiceQuery, p_u32AnswerIndex, u2)); + } + } + return internalIP; + }; + bool txtAvailable() + { + return (p_pMDNSResponder.hasAnswerTxts(p_hServiceQuery, p_u32AnswerIndex)); + } + const char* strKeyValue() + { + return (txtAvailable()) ? + p_pMDNSResponder.answerTxts(p_hServiceQuery, p_u32AnswerIndex) : nullptr; + }; + const KeyValueMap& keyValues() + { + if (txtAvailable() && keyValueMap.size() == 0) + { + for (auto kv = p_pMDNSResponder._answerKeyValue(p_hServiceQuery, p_u32AnswerIndex); kv != nullptr; kv = kv->m_pNext) + { + keyValueMap.emplace(std::pair(kv->m_pcKey, kv->m_pcValue)); + } + } + return keyValueMap; + } + const char* value(const char* key) + { + char* result = nullptr; + + for (stcMDNSServiceTxt* pTxt = p_pMDNSResponder._answerKeyValue(p_hServiceQuery, p_u32AnswerIndex); pTxt; pTxt = pTxt->m_pNext) + { + if ((key) && + (0 == strcmp(pTxt->m_pcKey, key))) + { + result = pTxt->m_pcValue; + break; + } + } + return result; + } + }; +protected: + + /** + stcMDNSServiceTxt + */ + struct stcMDNSServiceTxt + { + stcMDNSServiceTxt* m_pNext; + char* m_pcKey; + char* m_pcValue; + bool m_bTemp; + + stcMDNSServiceTxt(const char* p_pcKey = 0, + const char* p_pcValue = 0, + bool p_bTemp = false); + stcMDNSServiceTxt(const stcMDNSServiceTxt& p_Other); + ~stcMDNSServiceTxt(void); + + stcMDNSServiceTxt& operator=(const stcMDNSServiceTxt& p_Other); + bool clear(void); + + char* allocKey(size_t p_stLength); + bool setKey(const char* p_pcKey, + size_t p_stLength); + bool setKey(const char* p_pcKey); + bool releaseKey(void); + + char* allocValue(size_t p_stLength); + bool setValue(const char* p_pcValue, + size_t p_stLength); + bool setValue(const char* p_pcValue); + bool releaseValue(void); + + bool set(const char* p_pcKey, + const char* p_pcValue, + bool p_bTemp = false); + + bool update(const char* p_pcValue); + + size_t length(void) const; + }; + + /** + stcMDNSTxts + */ + struct stcMDNSServiceTxts + { + stcMDNSServiceTxt* m_pTxts; + + stcMDNSServiceTxts(void); + stcMDNSServiceTxts(const stcMDNSServiceTxts& p_Other); + ~stcMDNSServiceTxts(void); + + stcMDNSServiceTxts& operator=(const stcMDNSServiceTxts& p_Other); + + bool clear(void); + + bool add(stcMDNSServiceTxt* p_pTxt); + bool remove(stcMDNSServiceTxt* p_pTxt); + + bool removeTempTxts(void); + + stcMDNSServiceTxt* find(const char* p_pcKey); + const stcMDNSServiceTxt* find(const char* p_pcKey) const; + stcMDNSServiceTxt* find(const stcMDNSServiceTxt* p_pTxt); + + uint16_t length(void) const; + + size_t c_strLength(void) const; + bool c_str(char* p_pcBuffer); + + size_t bufferLength(void) const; + bool buffer(char* p_pcBuffer); + + bool compare(const stcMDNSServiceTxts& p_Other) const; + bool operator==(const stcMDNSServiceTxts& p_Other) const; + bool operator!=(const stcMDNSServiceTxts& p_Other) const; + }; + + /** + enuContentFlags + */ + typedef enum _enuContentFlags + { + // Host + ContentFlag_A = 0x01, + ContentFlag_PTR_IP4 = 0x02, + ContentFlag_PTR_IP6 = 0x04, + ContentFlag_AAAA = 0x08, + // Service + ContentFlag_PTR_TYPE = 0x10, + ContentFlag_PTR_NAME = 0x20, + ContentFlag_TXT = 0x40, + ContentFlag_SRV = 0x80, + } enuContentFlags; + + /** + stcMDNS_MsgHeader + */ + struct stcMDNS_MsgHeader + { + uint16_t m_u16ID; // Identifier + bool m_1bQR : 1; // Query/Response flag + unsigned char m_4bOpcode : 4; // Operation code + bool m_1bAA : 1; // Authoritative Answer flag + bool m_1bTC : 1; // Truncation flag + bool m_1bRD : 1; // Recursion desired + bool m_1bRA : 1; // Recursion available + unsigned char m_3bZ : 3; // Zero + unsigned char m_4bRCode : 4; // Response code + uint16_t m_u16QDCount; // Question count + uint16_t m_u16ANCount; // Answer count + uint16_t m_u16NSCount; // Authority Record count + uint16_t m_u16ARCount; // Additional Record count + + stcMDNS_MsgHeader(uint16_t p_u16ID = 0, + bool p_bQR = false, + unsigned char p_ucOpcode = 0, + bool p_bAA = false, + bool p_bTC = false, + bool p_bRD = false, + bool p_bRA = false, + unsigned char p_ucRCode = 0, + uint16_t p_u16QDCount = 0, + uint16_t p_u16ANCount = 0, + uint16_t p_u16NSCount = 0, + uint16_t p_u16ARCount = 0); + }; + + /** + stcMDNS_RRDomain + */ + struct stcMDNS_RRDomain + { + char m_acName[MDNS_DOMAIN_MAXLENGTH]; // Encoded domain name + uint16_t m_u16NameLength; // Length (incl. '\0') + + stcMDNS_RRDomain(void); + stcMDNS_RRDomain(const stcMDNS_RRDomain& p_Other); + + stcMDNS_RRDomain& operator=(const stcMDNS_RRDomain& p_Other); + + bool clear(void); + + bool addLabel(const char* p_pcLabel, + bool p_bPrependUnderline = false); + + bool compare(const stcMDNS_RRDomain& p_Other) const; + bool operator==(const stcMDNS_RRDomain& p_Other) const; + bool operator!=(const stcMDNS_RRDomain& p_Other) const; + bool operator>(const stcMDNS_RRDomain& p_Other) const; + + size_t c_strLength(void) const; + bool c_str(char* p_pcBuffer); + }; + + /** + stcMDNS_RRAttributes + */ + struct stcMDNS_RRAttributes + { + uint16_t m_u16Type; // Type + uint16_t m_u16Class; // Class, nearly always 'IN' + + stcMDNS_RRAttributes(uint16_t p_u16Type = 0, + uint16_t p_u16Class = 1 /*DNS_RRCLASS_IN Internet*/); + stcMDNS_RRAttributes(const stcMDNS_RRAttributes& p_Other); + + stcMDNS_RRAttributes& operator=(const stcMDNS_RRAttributes& p_Other); + }; + + /** + stcMDNS_RRHeader + */ + struct stcMDNS_RRHeader + { + stcMDNS_RRDomain m_Domain; + stcMDNS_RRAttributes m_Attributes; + + stcMDNS_RRHeader(void); + stcMDNS_RRHeader(const stcMDNS_RRHeader& p_Other); + + stcMDNS_RRHeader& operator=(const stcMDNS_RRHeader& p_Other); + + bool clear(void); + }; + + /** + stcMDNS_RRQuestion + */ + struct stcMDNS_RRQuestion + { + stcMDNS_RRQuestion* m_pNext; + stcMDNS_RRHeader m_Header; + bool m_bUnicast; // Unicast reply requested + + stcMDNS_RRQuestion(void); + }; + + /** + enuAnswerType + */ + typedef enum _enuAnswerType + { + AnswerType_A, + AnswerType_PTR, + AnswerType_TXT, + AnswerType_AAAA, + AnswerType_SRV, + AnswerType_Generic + } enuAnswerType; + + /** + stcMDNS_RRAnswer + */ + struct stcMDNS_RRAnswer + { + stcMDNS_RRAnswer* m_pNext; + const enuAnswerType m_AnswerType; + stcMDNS_RRHeader m_Header; + bool m_bCacheFlush; // Cache flush command bit + uint32_t m_u32TTL; // Validity time in seconds + + virtual ~stcMDNS_RRAnswer(void); + + enuAnswerType answerType(void) const; + + bool clear(void); + + protected: + stcMDNS_RRAnswer(enuAnswerType p_AnswerType, + const stcMDNS_RRHeader& p_Header, + uint32_t p_u32TTL); + }; + +#ifdef MDNS_IP4_SUPPORT + /** + stcMDNS_RRAnswerA + */ + struct stcMDNS_RRAnswerA : public stcMDNS_RRAnswer + { + IPAddress m_IPAddress; + + stcMDNS_RRAnswerA(const stcMDNS_RRHeader& p_Header, + uint32_t p_u32TTL); + ~stcMDNS_RRAnswerA(void); + + bool clear(void); + }; +#endif + + /** + stcMDNS_RRAnswerPTR + */ + struct stcMDNS_RRAnswerPTR : public stcMDNS_RRAnswer + { + stcMDNS_RRDomain m_PTRDomain; + + stcMDNS_RRAnswerPTR(const stcMDNS_RRHeader& p_Header, + uint32_t p_u32TTL); + ~stcMDNS_RRAnswerPTR(void); + + bool clear(void); + }; + + /** + stcMDNS_RRAnswerTXT + */ + struct stcMDNS_RRAnswerTXT : public stcMDNS_RRAnswer + { + stcMDNSServiceTxts m_Txts; + + stcMDNS_RRAnswerTXT(const stcMDNS_RRHeader& p_Header, + uint32_t p_u32TTL); + ~stcMDNS_RRAnswerTXT(void); + + bool clear(void); + }; + +#ifdef MDNS_IP6_SUPPORT + /** + stcMDNS_RRAnswerAAAA + */ + struct stcMDNS_RRAnswerAAAA : public stcMDNS_RRAnswer + { + //TODO: IP6Address m_IPAddress; + + stcMDNS_RRAnswerAAAA(const stcMDNS_RRHeader& p_Header, + uint32_t p_u32TTL); + ~stcMDNS_RRAnswerAAAA(void); + + bool clear(void); + }; +#endif + + /** + stcMDNS_RRAnswerSRV + */ + struct stcMDNS_RRAnswerSRV : public stcMDNS_RRAnswer + { + uint16_t m_u16Priority; + uint16_t m_u16Weight; + uint16_t m_u16Port; + stcMDNS_RRDomain m_SRVDomain; + + stcMDNS_RRAnswerSRV(const stcMDNS_RRHeader& p_Header, + uint32_t p_u32TTL); + ~stcMDNS_RRAnswerSRV(void); + + bool clear(void); + }; + + /** + stcMDNS_RRAnswerGeneric + */ + struct stcMDNS_RRAnswerGeneric : public stcMDNS_RRAnswer + { + uint16_t m_u16RDLength; // Length of variable answer + uint8_t* m_pu8RDData; // Offset of start of variable answer in packet + + stcMDNS_RRAnswerGeneric(const stcMDNS_RRHeader& p_Header, + uint32_t p_u32TTL); + ~stcMDNS_RRAnswerGeneric(void); + + bool clear(void); + }; + + + /** + enuProbingStatus + */ + typedef enum _enuProbingStatus + { + ProbingStatus_WaitingForData, + ProbingStatus_ReadyToStart, + ProbingStatus_InProgress, + ProbingStatus_Done + } enuProbingStatus; + + /** + stcProbeInformation + */ + struct stcProbeInformation + { + enuProbingStatus m_ProbingStatus; + uint8_t m_u8SentCount; // Used for probes and announcements + esp8266::polledTimeout::oneShotMs m_Timeout; // Used for probes and announcements + //clsMDNSTimeFlag m_TimeFlag; // Used for probes and announcements + bool m_bConflict; + bool m_bTiebreakNeeded; + MDNSHostProbeFn m_fnHostProbeResultCallback; + MDNSServiceProbeFn m_fnServiceProbeResultCallback; + + stcProbeInformation(void); + + bool clear(bool p_bClearUserdata = false); + }; + + + /** + stcMDNSService + */ + struct stcMDNSService + { + stcMDNSService* m_pNext; + char* m_pcName; + bool m_bAutoName; // Name was set automatically to hostname (if no name was supplied) + char* m_pcService; + char* m_pcProtocol; + uint16_t m_u16Port; + uint8_t m_u8ReplyMask; + stcMDNSServiceTxts m_Txts; + MDNSDynamicServiceTxtCallbackFunc m_fnTxtCallback; + stcProbeInformation m_ProbeInformation; + + stcMDNSService(const char* p_pcName = 0, + const char* p_pcService = 0, + const char* p_pcProtocol = 0); + ~stcMDNSService(void); + + bool setName(const char* p_pcName); + bool releaseName(void); + + bool setService(const char* p_pcService); + bool releaseService(void); + + bool setProtocol(const char* p_pcProtocol); + bool releaseProtocol(void); + }; + + /** + stcMDNSServiceQuery + */ + struct stcMDNSServiceQuery + { + /** + stcAnswer + */ + struct stcAnswer + { + /** + stcTTL + */ + struct stcTTL + { + /** + timeoutLevel_t + */ + typedef uint8_t timeoutLevel_t; + /** + TIMEOUTLEVELs + */ + const timeoutLevel_t TIMEOUTLEVEL_UNSET = 0; + const timeoutLevel_t TIMEOUTLEVEL_BASE = 80; + const timeoutLevel_t TIMEOUTLEVEL_INTERVAL = 5; + const timeoutLevel_t TIMEOUTLEVEL_FINAL = 100; + + uint32_t m_u32TTL; + esp8266::polledTimeout::oneShotMs m_TTLTimeout; + timeoutLevel_t m_timeoutLevel; + + stcTTL(void); + bool set(uint32_t p_u32TTL); + + bool flagged(void); + bool restart(void); + + bool prepareDeletion(void); + bool finalTimeoutLevel(void) const; + + unsigned long timeout(void) const; + }; +#ifdef MDNS_IP4_SUPPORT + /** + stcIP4Address + */ + struct stcIP4Address + { + stcIP4Address* m_pNext; + IPAddress m_IPAddress; + stcTTL m_TTL; + + stcIP4Address(IPAddress p_IPAddress, + uint32_t p_u32TTL = 0); + }; +#endif +#ifdef MDNS_IP6_SUPPORT + /** + stcIP6Address + */ + struct stcIP6Address + { + stcIP6Address* m_pNext; + IP6Address m_IPAddress; + stcTTL m_TTL; + + stcIP6Address(IPAddress p_IPAddress, + uint32_t p_u32TTL = 0); + }; +#endif + + stcAnswer* m_pNext; + // The service domain is the first 'answer' (from PTR answer, using service and protocol) to be set + // Defines the key for additional answer, like host domain, etc. + stcMDNS_RRDomain m_ServiceDomain; // 1. level answer (PTR), eg. MyESP._http._tcp.local + char* m_pcServiceDomain; + stcTTL m_TTLServiceDomain; + stcMDNS_RRDomain m_HostDomain; // 2. level answer (SRV, using service domain), eg. esp8266.local + char* m_pcHostDomain; + uint16_t m_u16Port; // 2. level answer (SRV, using service domain), eg. 5000 + stcTTL m_TTLHostDomainAndPort; + stcMDNSServiceTxts m_Txts; // 2. level answer (TXT, using service domain), eg. c#=1 + char* m_pcTxts; + stcTTL m_TTLTxts; +#ifdef MDNS_IP4_SUPPORT + stcIP4Address* m_pIP4Addresses; // 3. level answer (A, using host domain), eg. 123.456.789.012 +#endif +#ifdef MDNS_IP6_SUPPORT + stcIP6Address* m_pIP6Addresses; // 3. level answer (AAAA, using host domain), eg. 1234::09 +#endif + uint32_t m_u32ContentFlags; + + stcAnswer(void); + ~stcAnswer(void); + + bool clear(void); + + char* allocServiceDomain(size_t p_stLength); + bool releaseServiceDomain(void); + + char* allocHostDomain(size_t p_stLength); + bool releaseHostDomain(void); + + char* allocTxts(size_t p_stLength); + bool releaseTxts(void); + +#ifdef MDNS_IP4_SUPPORT + bool releaseIP4Addresses(void); + bool addIP4Address(stcIP4Address* p_pIP4Address); + bool removeIP4Address(stcIP4Address* p_pIP4Address); + const stcIP4Address* findIP4Address(const IPAddress& p_IPAddress) const; + stcIP4Address* findIP4Address(const IPAddress& p_IPAddress); + uint32_t IP4AddressCount(void) const; + const stcIP4Address* IP4AddressAtIndex(uint32_t p_u32Index) const; + stcIP4Address* IP4AddressAtIndex(uint32_t p_u32Index); +#endif +#ifdef MDNS_IP6_SUPPORT + bool releaseIP6Addresses(void); + bool addIP6Address(stcIP6Address* p_pIP6Address); + bool removeIP6Address(stcIP6Address* p_pIP6Address); + const stcIP6Address* findIP6Address(const IPAddress& p_IPAddress) const; + stcIP6Address* findIP6Address(const IPAddress& p_IPAddress); + uint32_t IP6AddressCount(void) const; + const stcIP6Address* IP6AddressAtIndex(uint32_t p_u32Index) const; + stcIP6Address* IP6AddressAtIndex(uint32_t p_u32Index); +#endif + }; + + stcMDNSServiceQuery* m_pNext; + stcMDNS_RRDomain m_ServiceTypeDomain; // eg. _http._tcp.local + MDNSServiceQueryCallbackFunc m_fnCallback; + bool m_bLegacyQuery; + uint8_t m_u8SentCount; + esp8266::polledTimeout::oneShotMs m_ResendTimeout; + bool m_bAwaitingAnswers; + stcAnswer* m_pAnswers; + + stcMDNSServiceQuery(void); + ~stcMDNSServiceQuery(void); + + bool clear(void); + + uint32_t answerCount(void) const; + const stcAnswer* answerAtIndex(uint32_t p_u32Index) const; + stcAnswer* answerAtIndex(uint32_t p_u32Index); + uint32_t indexOfAnswer(const stcAnswer* p_pAnswer) const; + + bool addAnswer(stcAnswer* p_pAnswer); + bool removeAnswer(stcAnswer* p_pAnswer); + + stcAnswer* findAnswerForServiceDomain(const stcMDNS_RRDomain& p_ServiceDomain); + stcAnswer* findAnswerForHostDomain(const stcMDNS_RRDomain& p_HostDomain); + }; + + /** + stcMDNSSendParameter + */ + struct stcMDNSSendParameter + { + protected: + /** + stcDomainCacheItem + */ + struct stcDomainCacheItem + { + stcDomainCacheItem* m_pNext; + const void* m_pHostnameOrService; // Opaque id for host or service domain (pointer) + bool m_bAdditionalData; // Opaque flag for special info (service domain included) + uint16_t m_u16Offset; // Offset in UDP output buffer + + stcDomainCacheItem(const void* p_pHostnameOrService, + bool p_bAdditionalData, + uint32_t p_u16Offset); + }; + + public: + uint16_t m_u16ID; // Query ID (used only in lagacy queries) + stcMDNS_RRQuestion* m_pQuestions; // A list of queries + uint8_t m_u8HostReplyMask; // Flags for reply components/answers + bool m_bLegacyQuery; // Flag: Legacy query + bool m_bResponse; // Flag: Response to a query + bool m_bAuthorative; // Flag: Authorative (owner) response + bool m_bCacheFlush; // Flag: Clients should flush their caches + bool m_bUnicast; // Flag: Unicast response + bool m_bUnannounce; // Flag: Unannounce service + uint16_t m_u16Offset; // Current offset in UDP write buffer (mainly for domain cache) + stcDomainCacheItem* m_pDomainCacheItems; // Cached host and service domains + + stcMDNSSendParameter(void); + ~stcMDNSSendParameter(void); + + bool clear(void); + + bool shiftOffset(uint16_t p_u16Shift); + + bool addDomainCacheItem(const void* p_pHostnameOrService, + bool p_bAdditionalData, + uint16_t p_u16Offset); + uint16_t findCachedDomainOffset(const void* p_pHostnameOrService, + bool p_bAdditionalData) const; + }; + + // Instance variables + stcMDNSService* m_pServices; + UdpContext* m_pUDPContext; + char* m_pcHostname; + stcMDNSServiceQuery* m_pServiceQueries; + WiFiEventHandler m_DisconnectedHandler; + WiFiEventHandler m_GotIPHandler; + MDNSDynamicServiceTxtCallbackFunc m_fnServiceTxtCallback; + bool m_bPassivModeEnabled; + stcProbeInformation m_HostProbeInformation; + CONST netif* m_netif; // network interface to run on + + /** CONTROL **/ + /* MAINTENANCE */ + bool _process(bool p_bUserContext); + bool _restart(void); + + /* RECEIVING */ + bool _parseMessage(void); + bool _parseQuery(const stcMDNS_MsgHeader& p_Header); + + bool _parseResponse(const stcMDNS_MsgHeader& p_Header); + bool _processAnswers(const stcMDNS_RRAnswer* p_pPTRAnswers); + bool _processPTRAnswer(const stcMDNS_RRAnswerPTR* p_pPTRAnswer, + bool& p_rbFoundNewKeyAnswer); + bool _processSRVAnswer(const stcMDNS_RRAnswerSRV* p_pSRVAnswer, + bool& p_rbFoundNewKeyAnswer); + bool _processTXTAnswer(const stcMDNS_RRAnswerTXT* p_pTXTAnswer); +#ifdef MDNS_IP4_SUPPORT + bool _processAAnswer(const stcMDNS_RRAnswerA* p_pAAnswer); +#endif +#ifdef MDNS_IP6_SUPPORT + bool _processAAAAAnswer(const stcMDNS_RRAnswerAAAA* p_pAAAAAnswer); +#endif + + /* PROBING */ + bool _updateProbeStatus(void); + bool _resetProbeStatus(bool p_bRestart = true); + bool _hasProbesWaitingForAnswers(void) const; + bool _sendHostProbe(void); + bool _sendServiceProbe(stcMDNSService& p_rService); + bool _cancelProbingForHost(void); + bool _cancelProbingForService(stcMDNSService& p_rService); + + /* ANNOUNCE */ + bool _announce(bool p_bAnnounce, + bool p_bIncludeServices); + bool _announceService(stcMDNSService& p_rService, + bool p_bAnnounce = true); + + /* SERVICE QUERY CACHE */ + bool _hasServiceQueriesWaitingForAnswers(void) const; + bool _checkServiceQueryCache(void); + + /** TRANSFER **/ + /* SENDING */ + bool _sendMDNSMessage(stcMDNSSendParameter& p_SendParameter); + bool _sendMDNSMessage_Multicast(MDNSResponder::stcMDNSSendParameter& p_rSendParameter); + bool _prepareMDNSMessage(stcMDNSSendParameter& p_SendParameter, + IPAddress p_IPAddress); + bool _sendMDNSServiceQuery(const stcMDNSServiceQuery& p_ServiceQuery); + bool _sendMDNSQuery(const stcMDNS_RRDomain& p_QueryDomain, + uint16_t p_u16QueryType, + stcMDNSServiceQuery::stcAnswer* p_pKnownAnswers = 0); + + const IPAddress _getResponseMulticastInterface() const + { + return IPAddress(m_netif->ip_addr); + } + + uint8_t _replyMaskForHost(const stcMDNS_RRHeader& p_RRHeader, + bool* p_pbFullNameMatch = 0) const; + uint8_t _replyMaskForService(const stcMDNS_RRHeader& p_RRHeader, + const stcMDNSService& p_Service, + bool* p_pbFullNameMatch = 0) const; + + /* RESOURCE RECORD */ + bool _readRRQuestion(stcMDNS_RRQuestion& p_rQuestion); + bool _readRRAnswer(stcMDNS_RRAnswer*& p_rpAnswer); +#ifdef MDNS_IP4_SUPPORT + bool _readRRAnswerA(stcMDNS_RRAnswerA& p_rRRAnswerA, + uint16_t p_u16RDLength); +#endif + bool _readRRAnswerPTR(stcMDNS_RRAnswerPTR& p_rRRAnswerPTR, + uint16_t p_u16RDLength); + bool _readRRAnswerTXT(stcMDNS_RRAnswerTXT& p_rRRAnswerTXT, + uint16_t p_u16RDLength); +#ifdef MDNS_IP6_SUPPORT + bool _readRRAnswerAAAA(stcMDNS_RRAnswerAAAA& p_rRRAnswerAAAA, + uint16_t p_u16RDLength); +#endif + bool _readRRAnswerSRV(stcMDNS_RRAnswerSRV& p_rRRAnswerSRV, + uint16_t p_u16RDLength); + bool _readRRAnswerGeneric(stcMDNS_RRAnswerGeneric& p_rRRAnswerGeneric, + uint16_t p_u16RDLength); + + bool _readRRHeader(stcMDNS_RRHeader& p_rHeader); + bool _readRRDomain(stcMDNS_RRDomain& p_rRRDomain); + bool _readRRDomain_Loop(stcMDNS_RRDomain& p_rRRDomain, + uint8_t p_u8Depth); + bool _readRRAttributes(stcMDNS_RRAttributes& p_rAttributes); + + /* DOMAIN NAMES */ + bool _buildDomainForHost(const char* p_pcHostname, + stcMDNS_RRDomain& p_rHostDomain) const; + bool _buildDomainForDNSSD(stcMDNS_RRDomain& p_rDNSSDDomain) const; + bool _buildDomainForService(const stcMDNSService& p_Service, + bool p_bIncludeName, + stcMDNS_RRDomain& p_rServiceDomain) const; + bool _buildDomainForService(const char* p_pcService, + const char* p_pcProtocol, + stcMDNS_RRDomain& p_rServiceDomain) const; +#ifdef MDNS_IP4_SUPPORT + bool _buildDomainForReverseIP4(IPAddress p_IP4Address, + stcMDNS_RRDomain& p_rReverseIP4Domain) const; +#endif +#ifdef MDNS_IP6_SUPPORT + bool _buildDomainForReverseIP6(IPAddress p_IP4Address, + stcMDNS_RRDomain& p_rReverseIP6Domain) const; +#endif + + /* UDP */ + bool _udpReadBuffer(unsigned char* p_pBuffer, + size_t p_stLength); + bool _udpRead8(uint8_t& p_ru8Value); + bool _udpRead16(uint16_t& p_ru16Value); + bool _udpRead32(uint32_t& p_ru32Value); + + bool _udpAppendBuffer(const unsigned char* p_pcBuffer, + size_t p_stLength); + bool _udpAppend8(uint8_t p_u8Value); + bool _udpAppend16(uint16_t p_u16Value); + bool _udpAppend32(uint32_t p_u32Value); + +#if not defined ESP_8266_MDNS_INCLUDE || defined DEBUG_ESP_MDNS_RESPONDER + bool _udpDump(bool p_bMovePointer = false); + bool _udpDump(unsigned p_uOffset, + unsigned p_uLength); +#endif + + /* READ/WRITE MDNS STRUCTS */ + bool _readMDNSMsgHeader(stcMDNS_MsgHeader& p_rMsgHeader); + + bool _write8(uint8_t p_u8Value, + stcMDNSSendParameter& p_rSendParameter); + bool _write16(uint16_t p_u16Value, + stcMDNSSendParameter& p_rSendParameter); + bool _write32(uint32_t p_u32Value, + stcMDNSSendParameter& p_rSendParameter); + + bool _writeMDNSMsgHeader(const stcMDNS_MsgHeader& p_MsgHeader, + stcMDNSSendParameter& p_rSendParameter); + bool _writeMDNSRRAttributes(const stcMDNS_RRAttributes& p_Attributes, + stcMDNSSendParameter& p_rSendParameter); + bool _writeMDNSRRDomain(const stcMDNS_RRDomain& p_Domain, + stcMDNSSendParameter& p_rSendParameter); + bool _writeMDNSHostDomain(const char* m_pcHostname, + bool p_bPrependRDLength, + stcMDNSSendParameter& p_rSendParameter); + bool _writeMDNSServiceDomain(const stcMDNSService& p_Service, + bool p_bIncludeName, + bool p_bPrependRDLength, + stcMDNSSendParameter& p_rSendParameter); + + bool _writeMDNSQuestion(stcMDNS_RRQuestion& p_Question, + stcMDNSSendParameter& p_rSendParameter); + +#ifdef MDNS_IP4_SUPPORT + bool _writeMDNSAnswer_A(IPAddress p_IPAddress, + stcMDNSSendParameter& p_rSendParameter); + bool _writeMDNSAnswer_PTR_IP4(IPAddress p_IPAddress, + stcMDNSSendParameter& p_rSendParameter); +#endif + bool _writeMDNSAnswer_PTR_TYPE(stcMDNSService& p_rService, + stcMDNSSendParameter& p_rSendParameter); + bool _writeMDNSAnswer_PTR_NAME(stcMDNSService& p_rService, + stcMDNSSendParameter& p_rSendParameter); + bool _writeMDNSAnswer_TXT(stcMDNSService& p_rService, + stcMDNSSendParameter& p_rSendParameter); +#ifdef MDNS_IP6_SUPPORT + bool _writeMDNSAnswer_AAAA(IPAddress p_IPAddress, + stcMDNSSendParameter& p_rSendParameter); + bool _writeMDNSAnswer_PTR_IP6(IPAddress p_IPAddress, + stcMDNSSendParameter& p_rSendParameter); +#endif + bool _writeMDNSAnswer_SRV(stcMDNSService& p_rService, + stcMDNSSendParameter& p_rSendParameter); + + /** HELPERS **/ + /* UDP CONTEXT */ + bool _callProcess(void); + bool _allocUDPContext(void); + bool _releaseUDPContext(void); + + /* SERVICE QUERY */ + stcMDNSServiceQuery* _allocServiceQuery(void); + bool _removeServiceQuery(stcMDNSServiceQuery* p_pServiceQuery); + bool _removeLegacyServiceQuery(void); + stcMDNSServiceQuery* _findServiceQuery(hMDNSServiceQuery p_hServiceQuery); + stcMDNSServiceQuery* _findLegacyServiceQuery(void); + bool _releaseServiceQueries(void); + stcMDNSServiceQuery* _findNextServiceQueryByServiceType(const stcMDNS_RRDomain& p_ServiceDomain, + const stcMDNSServiceQuery* p_pPrevServiceQuery); + + /* HOSTNAME */ + bool _setHostname(const char* p_pcHostname); + bool _releaseHostname(void); + + /* SERVICE */ + stcMDNSService* _allocService(const char* p_pcName, + const char* p_pcService, + const char* p_pcProtocol, + uint16_t p_u16Port); + bool _releaseService(stcMDNSService* p_pService); + bool _releaseServices(void); + + stcMDNSService* _findService(const char* p_pcName, + const char* p_pcService, + const char* p_pcProtocol); + stcMDNSService* _findService(const hMDNSService p_hService); + + size_t _countServices(void) const; + + /* SERVICE TXT */ + stcMDNSServiceTxt* _allocServiceTxt(stcMDNSService* p_pService, + const char* p_pcKey, + const char* p_pcValue, + bool p_bTemp); + bool _releaseServiceTxt(stcMDNSService* p_pService, + stcMDNSServiceTxt* p_pTxt); + stcMDNSServiceTxt* _updateServiceTxt(stcMDNSService* p_pService, + stcMDNSServiceTxt* p_pTxt, + const char* p_pcValue, + bool p_bTemp); + + stcMDNSServiceTxt* _findServiceTxt(stcMDNSService* p_pService, + const char* p_pcKey); + stcMDNSServiceTxt* _findServiceTxt(stcMDNSService* p_pService, + const hMDNSTxt p_hTxt); + + stcMDNSServiceTxt* _addServiceTxt(stcMDNSService* p_pService, + const char* p_pcKey, + const char* p_pcValue, + bool p_bTemp); + + stcMDNSServiceTxt* _answerKeyValue(const hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex); + + bool _collectServiceTxts(stcMDNSService& p_rService); + bool _releaseTempServiceTxts(stcMDNSService& p_rService); + const stcMDNSServiceTxt* _serviceTxts(const char* p_pcName, + const char* p_pcService, + const char* p_pcProtocol); + + /* MISC */ +#if not defined ESP_8266_MDNS_INCLUDE || defined DEBUG_ESP_MDNS_RESPONDER + bool _printRRDomain(const stcMDNS_RRDomain& p_rRRDomain) const; + bool _printRRAnswer(const MDNSResponder::stcMDNS_RRAnswer& p_RRAnswer) const; +#endif +}; + +}// namespace MDNSImplementation + +}// namespace esp8266 + +#endif // MDNS_H diff --git a/libraries/ESP8266mDNS/src/LEAmDNS_Control.cpp b/libraries/ESP8266mDNS/src/LEAmDNS_Control.cpp index 61beb73396..41e9524aba 100644 --- a/libraries/ESP8266mDNS/src/LEAmDNS_Control.cpp +++ b/libraries/ESP8266mDNS/src/LEAmDNS_Control.cpp @@ -1,1832 +1,2134 @@ -/* - * LEAmDNS_Control.cpp - * - * License (MIT license): - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - */ - -#include -#include -#include -#include -#include -#include - -/* - * ESP8266mDNS Control.cpp - */ - -extern "C" { - #include "user_interface.h" -} - -#include "LEAmDNS_lwIPdefs.h" -#include "LEAmDNS_Priv.h" - -namespace esp8266 { -/* - * LEAmDNS - */ -namespace MDNSImplementation { - -/** - * CONTROL - */ - - -/** - * MAINTENANCE - */ - -/* - * MDNSResponder::_process - * - * Run the MDNS process. - * Is called, every time the UDPContext receives data AND - * should be called in every 'loop' by calling 'MDNS::update()'. - * - */ -bool MDNSResponder::_process(bool p_bUserContext) { - - bool bResult = true; - - if (!p_bUserContext) { - - if ((m_pUDPContext) && // UDPContext available AND - (m_pUDPContext->next())) { // has content - - //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _update: Calling _parseMessage\n"));); - bResult = _parseMessage(); - //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parsePacket %s\n"), (bResult ? "succeeded" : "FAILED"));); - } - } - else { - bResult = (m_netif != nullptr) && - (m_netif->flags & NETIF_FLAG_UP) && // network interface is up and running +/* + LEAmDNS_Control.cpp + + License (MIT license): + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + +*/ + +#include +#include +#include +#include +#include +#include + +/* + ESP8266mDNS Control.cpp +*/ + +extern "C" { +#include "user_interface.h" +} + +#include "LEAmDNS_lwIPdefs.h" +#include "LEAmDNS_Priv.h" + +namespace esp8266 +{ +/* + LEAmDNS +*/ +namespace MDNSImplementation +{ + +/** + CONTROL +*/ + + +/** + MAINTENANCE +*/ + +/* + MDNSResponder::_process + + Run the MDNS process. + Is called, every time the UDPContext receives data AND + should be called in every 'loop' by calling 'MDNS::update()'. + +*/ +bool MDNSResponder::_process(bool p_bUserContext) +{ + + bool bResult = true; + + if (!p_bUserContext) + { + + if ((m_pUDPContext) && // UDPContext available AND + (m_pUDPContext->next())) // has content + { + + //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _update: Calling _parseMessage\n"));); + bResult = _parseMessage(); + //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parsePacket %s\n"), (bResult ? "succeeded" : "FAILED"));); + } + } + else + { + bResult = (m_netif != nullptr) && + (m_netif->flags & NETIF_FLAG_UP) && // network interface is up and running _updateProbeStatus() && // Probing _checkServiceQueryCache(); // Service query cache check - } - return bResult; -} - -/* - * MDNSResponder::_restart - */ -bool MDNSResponder::_restart(void) { - - return ((m_netif != nullptr) && - (m_netif->flags & NETIF_FLAG_UP) && // network interface is up and running - (_resetProbeStatus(true)) && // Stop and restart probing - (_allocUDPContext())); // Restart UDP -} - - -/** - * RECEIVING - */ - -/* - * MDNSResponder::_parseMessage - */ -bool MDNSResponder::_parseMessage(void) { - DEBUG_EX_INFO( - unsigned long ulStartTime = millis(); - unsigned uStartMemory = ESP.getFreeHeap(); - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseMessage (Time: %lu ms, heap: %u bytes, from %s(%u), to %s(%u))\n"), ulStartTime, uStartMemory, - IPAddress(m_pUDPContext->getRemoteAddress()).toString().c_str(), m_pUDPContext->getRemotePort(), - IPAddress(m_pUDPContext->getDestAddress()).toString().c_str(), m_pUDPContext->getLocalPort()); - ); - //DEBUG_EX_INFO(_udpDump();); - - bool bResult = false; - - stcMDNS_MsgHeader header; - if (_readMDNSMsgHeader(header)) { - if (0 == header.m_4bOpcode) { // A standard query - if (header.m_1bQR) { // Received a response -> answers to a query - //DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseMessage: Reading answers: ID:%u, Q:%u, A:%u, NS:%u, AR:%u\n"), header.m_u16ID, header.m_u16QDCount, header.m_u16ANCount, header.m_u16NSCount, header.m_u16ARCount);); - bResult = _parseResponse(header); - } - else { // Received a query (Questions) - //DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseMessage: Reading query: ID:%u, Q:%u, A:%u, NS:%u, AR:%u\n"), header.m_u16ID, header.m_u16QDCount, header.m_u16ANCount, header.m_u16NSCount, header.m_u16ARCount);); - bResult = _parseQuery(header); - } - } - else { - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseMessage: Received UNEXPECTED opcode:%u. Ignoring message!\n"), header.m_4bOpcode);); - m_pUDPContext->flush(); - } - } - else { - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseMessage: FAILED to read header\n"));); - m_pUDPContext->flush(); - } - DEBUG_EX_INFO( - unsigned uFreeHeap = ESP.getFreeHeap(); - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseMessage: Done (%s after %lu ms, ate %i bytes, remaining %u)\n\n"), (bResult ? "Succeeded" : "FAILED"), (millis() - ulStartTime), (uStartMemory - uFreeHeap), uFreeHeap); - ); - return bResult; -} - -/* - * MDNSResponder::_parseQuery - * - * Queries are of interest in two cases: - * 1. allow for tiebreaking while probing in the case of a race condition between two instances probing for - * the same name at the same time - * 2. provide answers to questions for our host domain or any presented service - * - * When reading the questions, a set of (planned) responses is created, eg. a reverse PTR question for the host domain - * gets an A (IP address) response, a PTR question for the _services._dns-sd domain gets a PTR (type) response for any - * registered service, ... - * - * As any mDNS responder should be able to handle 'legacy' queries (from DNS clients), this case is handled here also. - * Legacy queries have got only one (unicast) question and are directed to the local DNS port (not the multicast port). - * - * 1. - */ -bool MDNSResponder::_parseQuery(const MDNSResponder::stcMDNS_MsgHeader& p_MsgHeader) { - - bool bResult = true; - - stcMDNSSendParameter sendParameter; - uint8_t u8HostOrServiceReplies = 0; - for (uint16_t qd=0; ((bResult) && (qdm_pNext) { - // Define service replies, BUT only answer queries after probing is done - uint8_t u8ReplyMaskForQuestion = (((m_bPassivModeEnabled) || - (ProbingStatus_Done == pService->m_ProbeInformation.m_ProbingStatus)) - ? _replyMaskForService(questionRR.m_Header, *pService, 0) - : 0); - u8HostOrServiceReplies |= (pService->m_u8ReplyMask |= u8ReplyMaskForQuestion); - DEBUG_EX_INFO(if (u8ReplyMaskForQuestion) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Service reply needed for (%s.%s.%s): 0x%X (%s)\n"), (pService->m_pcName ?: m_pcHostname), pService->m_pcService, pService->m_pcProtocol, u8ReplyMaskForQuestion, IPAddress(m_pUDPContext->getRemoteAddress()).toString().c_str()); } ); - - // Check tiebreak need for service domain - if (ProbingStatus_InProgress == pService->m_ProbeInformation.m_ProbingStatus) { - bool bFullNameMatch = false; - if ((_replyMaskForService(questionRR.m_Header, *pService, &bFullNameMatch)) && - (bFullNameMatch)) { - // We're in 'probing' state and someone is asking for this service domain: this might be - // a race-condition: Two services with the same domain names try simutanously to probe their domains - // See: RFC 6762, 8.2 (Tiebraking) - // However, we're using a max. reduced approach for tiebreaking here: The 'higher' SRV host wins! - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Possible race-condition for service domain %s.%s.%s detected while probing.\n"), (pService->m_pcName ?: m_pcHostname), pService->m_pcService, pService->m_pcProtocol);); - - pService->m_ProbeInformation.m_bTiebreakNeeded = true; - } - } - } - - // Handle unicast and legacy specialities - // If only one question asks for unicast reply, the whole reply packet is send unicast - if (((DNS_MQUERY_PORT != m_pUDPContext->getRemotePort()) || // Unicast (maybe legacy) query OR - (questionRR.m_bUnicast)) && // Expressivly unicast query - (!sendParameter.m_bUnicast)) { - - sendParameter.m_bUnicast = true; - sendParameter.m_bCacheFlush = false; - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Unicast response for %s!\n"), IPAddress(m_pUDPContext->getRemoteAddress()).toString().c_str());); - - if ((DNS_MQUERY_PORT != m_pUDPContext->getRemotePort()) && // Unicast (maybe legacy) query AND - (1 == p_MsgHeader.m_u16QDCount) && // Only one question AND - ((sendParameter.m_u8HostReplyMask) || // Host replies OR - (u8HostOrServiceReplies))) { // Host or service replies available - // We're a match for this legacy query, BUT - // make sure, that the query comes from a local host - ip_info IPInfo_Local; - ip_info IPInfo_Remote; - if (((IPInfo_Remote.ip.addr = m_pUDPContext->getRemoteAddress())) && - (((wifi_get_ip_info(SOFTAP_IF, &IPInfo_Local)) && - (ip4_addr_netcmp(&IPInfo_Remote.ip, &IPInfo_Local.ip, &IPInfo_Local.netmask))) || // Remote IP in SOFTAP's subnet OR - ((wifi_get_ip_info(STATION_IF, &IPInfo_Local)) && - (ip4_addr_netcmp(&IPInfo_Remote.ip, &IPInfo_Local.ip, &IPInfo_Local.netmask))))) { // Remote IP in STATION's subnet - - DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Legacy query from local host %s, id %u!\n"), IPAddress(m_pUDPContext->getRemoteAddress()).toString().c_str(), p_MsgHeader.m_u16ID);); - - sendParameter.m_u16ID = p_MsgHeader.m_u16ID; - sendParameter.m_bLegacyQuery = true; - sendParameter.m_pQuestions = new stcMDNS_RRQuestion; - if ((bResult = (0 != sendParameter.m_pQuestions))) { - sendParameter.m_pQuestions->m_Header.m_Domain = questionRR.m_Header.m_Domain; - sendParameter.m_pQuestions->m_Header.m_Attributes.m_u16Type = questionRR.m_Header.m_Attributes.m_u16Type; - sendParameter.m_pQuestions->m_Header.m_Attributes.m_u16Class = questionRR.m_Header.m_Attributes.m_u16Class; - } - else { - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: FAILED to add legacy question!\n"));); - } - } - else { - DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Legacy query from NON-LOCAL host!\n"));); - bResult = false; - } - } - } - } - else { - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: FAILED to read question!\n"));); - } - } // for questions - - //DEBUG_EX_INFO(if (u8HostOrServiceReplies) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Reply needed: %u (%s: %s->%s)\n"), u8HostOrServiceReplies, clsTimeSyncer::timestr(), IPAddress(m_pUDPContext->getRemoteAddress()).toString().c_str(), IPAddress(m_pUDPContext->getDestAddress()).toString().c_str()); } ); - - // Handle known answers - uint32_t u32Answers = (p_MsgHeader.m_u16ANCount + p_MsgHeader.m_u16NSCount + p_MsgHeader.m_u16ARCount); - DEBUG_EX_INFO(if ((u8HostOrServiceReplies) && (u32Answers)) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Known answers(%u):\n"), u32Answers); } ); - - for (uint32_t an=0; ((bResult) && (anm_Header.m_Attributes.m_u16Type) && // No ANY type answer - (DNS_RRCLASS_ANY != pKnownRRAnswer->m_Header.m_Attributes.m_u16Class)) { // No ANY class answer - - // Find match between planned answer (sendParameter.m_u8HostReplyMask) and this 'known answer' - uint8_t u8HostMatchMask = (sendParameter.m_u8HostReplyMask & _replyMaskForHost(pKnownRRAnswer->m_Header)); - if ((u8HostMatchMask) && // The RR in the known answer matches an RR we are planning to send, AND - ((MDNS_HOST_TTL / 2) <= pKnownRRAnswer->m_u32TTL)) { // The TTL of the known answer is longer than half of the new host TTL (120s) - - // Compare contents - if (AnswerType_PTR == pKnownRRAnswer->answerType()) { - stcMDNS_RRDomain hostDomain; - if ((_buildDomainForHost(m_pcHostname, hostDomain)) && - (((stcMDNS_RRAnswerPTR*)pKnownRRAnswer)->m_PTRDomain == hostDomain)) { - // Host domain match -#ifdef MDNS_IP4_SUPPORT - if (u8HostMatchMask & ContentFlag_PTR_IP4) { - // IP4 PTR was asked for, but is already known -> skipping - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: IP4 PTR already known... skipping!\n"));); - sendParameter.m_u8HostReplyMask &= ~ContentFlag_PTR_IP4; - } -#endif -#ifdef MDNS_IP6_SUPPORT - if (u8HostMatchMask & ContentFlag_PTR_IP6) { - // IP6 PTR was asked for, but is already known -> skipping - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: IP6 PTR already known... skipping!\n"));); - sendParameter.m_u8HostReplyMask &= ~ContentFlag_PTR_IP6; - } -#endif - } - } - else if (u8HostMatchMask & ContentFlag_A) { - // IP4 address was asked for -#ifdef MDNS_IP4_SUPPORT - if ((AnswerType_A == pKnownRRAnswer->answerType()) && - (((stcMDNS_RRAnswerA*)pKnownRRAnswer)->m_IPAddress == _getResponseMulticastInterface())) { - - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: IP4 address already known... skipping!\n"));); - sendParameter.m_u8HostReplyMask &= ~ContentFlag_A; - } // else: RData NOT IP4 length !! -#endif - } - else if (u8HostMatchMask & ContentFlag_AAAA) { - // IP6 address was asked for -#ifdef MDNS_IP6_SUPPORT - if ((AnswerType_AAAA == pAnswerRR->answerType()) && - (((stcMDNS_RRAnswerAAAA*)pAnswerRR)->m_IPAddress == _getResponseMulticastInterface())) { - - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: IP6 address already known... skipping!\n"));); - sendParameter.m_u8HostReplyMask &= ~ContentFlag_AAAA; - } // else: RData NOT IP6 length !! -#endif - } - } // Host match /*and TTL*/ - - // - // Check host tiebreak possibility - if (m_HostProbeInformation.m_bTiebreakNeeded) { - stcMDNS_RRDomain hostDomain; - if ((_buildDomainForHost(m_pcHostname, hostDomain)) && - (pKnownRRAnswer->m_Header.m_Domain == hostDomain)) { - // Host domain match -#ifdef MDNS_IP4_SUPPORT - if (AnswerType_A == pKnownRRAnswer->answerType()) { - IPAddress localIPAddress(_getResponseMulticastInterface()); - if (((stcMDNS_RRAnswerA*)pKnownRRAnswer)->m_IPAddress == localIPAddress) { - // SAME IP address -> We've received an old message from ourselfs (same IP) - DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Tiebreak (IP4) WON (was an old message)!\n"));); - m_HostProbeInformation.m_bTiebreakNeeded = false; - } - else { - if ((uint32_t)(((stcMDNS_RRAnswerA*)pKnownRRAnswer)->m_IPAddress) > (uint32_t)localIPAddress) { // The OTHER IP is 'higher' -> LOST - // LOST tiebreak - DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Tiebreak (IP4) LOST (lower)!\n"));); - _cancelProbingForHost(); - m_HostProbeInformation.m_bTiebreakNeeded = false; - } - else { // WON tiebreak - //TiebreakState = TiebreakState_Won; // We received an 'old' message from ourselfs -> Just ignore - DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Tiebreak (IP4) WON (higher IP)!\n"));); - m_HostProbeInformation.m_bTiebreakNeeded = false; - } - } - } -#endif -#ifdef MDNS_IP6_SUPPORT - if (AnswerType_AAAA == pAnswerRR->answerType()) { - // TODO - } -#endif - } - } // Host tiebreak possibility - - // Check service answers - for (stcMDNSService* pService=m_pServices; pService; pService=pService->m_pNext) { - - uint8_t u8ServiceMatchMask = (pService->m_u8ReplyMask & _replyMaskForService(pKnownRRAnswer->m_Header, *pService)); - - if ((u8ServiceMatchMask) && // The RR in the known answer matches an RR we are planning to send, AND - ((MDNS_SERVICE_TTL / 2) <= pKnownRRAnswer->m_u32TTL)) { // The TTL of the known answer is longer than half of the new service TTL (4500s) - - if (AnswerType_PTR == pKnownRRAnswer->answerType()) { - stcMDNS_RRDomain serviceDomain; - if ((u8ServiceMatchMask & ContentFlag_PTR_TYPE) && - (_buildDomainForService(*pService, false, serviceDomain)) && - (serviceDomain == ((stcMDNS_RRAnswerPTR*)pKnownRRAnswer)->m_PTRDomain)) { - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Service type PTR already known... skipping!\n"));); - pService->m_u8ReplyMask &= ~ContentFlag_PTR_TYPE; - } - if ((u8ServiceMatchMask & ContentFlag_PTR_NAME) && - (_buildDomainForService(*pService, true, serviceDomain)) && - (serviceDomain == ((stcMDNS_RRAnswerPTR*)pKnownRRAnswer)->m_PTRDomain)) { - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Service name PTR already known... skipping!\n"));); - pService->m_u8ReplyMask &= ~ContentFlag_PTR_NAME; - } - } - else if (u8ServiceMatchMask & ContentFlag_SRV) { - DEBUG_EX_ERR(if (AnswerType_SRV != pKnownRRAnswer->answerType()) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: ERROR! INVALID answer type (SRV)!\n"));); - stcMDNS_RRDomain hostDomain; - if ((_buildDomainForHost(m_pcHostname, hostDomain)) && - (hostDomain == ((stcMDNS_RRAnswerSRV*)pKnownRRAnswer)->m_SRVDomain)) { // Host domain match - - if ((MDNS_SRV_PRIORITY == ((stcMDNS_RRAnswerSRV*)pKnownRRAnswer)->m_u16Priority) && - (MDNS_SRV_WEIGHT == ((stcMDNS_RRAnswerSRV*)pKnownRRAnswer)->m_u16Weight) && - (pService->m_u16Port == ((stcMDNS_RRAnswerSRV*)pKnownRRAnswer)->m_u16Port)) { - - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Service SRV answer already known... skipping!\n"));); - pService->m_u8ReplyMask &= ~ContentFlag_SRV; - } // else: Small differences -> send update message - } - } - else if (u8ServiceMatchMask & ContentFlag_TXT) { - DEBUG_EX_ERR(if (AnswerType_TXT != pKnownRRAnswer->answerType()) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: ERROR! INVALID answer type (TXT)!\n"));); - _collectServiceTxts(*pService); - if (pService->m_Txts == ((stcMDNS_RRAnswerTXT*)pKnownRRAnswer)->m_Txts) { - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Service TXT answer already known... skipping!\n"));); - pService->m_u8ReplyMask &= ~ContentFlag_TXT; - } - _releaseTempServiceTxts(*pService); - } - } // Service match and enough TTL - - // - // Check service tiebreak possibility - if (pService->m_ProbeInformation.m_bTiebreakNeeded) { - stcMDNS_RRDomain serviceDomain; - if ((_buildDomainForService(*pService, true, serviceDomain)) && - (pKnownRRAnswer->m_Header.m_Domain == serviceDomain)) { - // Service domain match - if (AnswerType_SRV == pKnownRRAnswer->answerType()) { - stcMDNS_RRDomain hostDomain; - if ((_buildDomainForHost(m_pcHostname, hostDomain)) && - (hostDomain == ((stcMDNS_RRAnswerSRV*)pKnownRRAnswer)->m_SRVDomain)) { // Host domain match - - // We've received an old message from ourselfs (same SRV) - DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Tiebreak (SRV) won (was an old message)!\n"));); - pService->m_ProbeInformation.m_bTiebreakNeeded = false; - } - else { - if (((stcMDNS_RRAnswerSRV*)pKnownRRAnswer)->m_SRVDomain > hostDomain) { // The OTHER domain is 'higher' -> LOST - // LOST tiebreak - DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Tiebreak (SRV) LOST (lower)!\n"));); - _cancelProbingForService(*pService); - pService->m_ProbeInformation.m_bTiebreakNeeded = false; - } - else { // WON tiebreak - //TiebreakState = TiebreakState_Won; // We received an 'old' message from ourselfs -> Just ignore - DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Tiebreak (SRV) won (higher)!\n"));); - pService->m_ProbeInformation.m_bTiebreakNeeded = false; - } - } - } - } - } // service tiebreak possibility - } // for services - } // ANY answers - } - else { - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: FAILED to read known answer!\n"));); - } - - if (pKnownRRAnswer) { - delete pKnownRRAnswer; - pKnownRRAnswer = 0; - } - } // for answers - - if (bResult) { - // Check, if a reply is needed - uint8_t u8ReplyNeeded = sendParameter.m_u8HostReplyMask; - for (stcMDNSService* pService=m_pServices; pService; pService=pService->m_pNext) { - u8ReplyNeeded |= pService->m_u8ReplyMask; - } - - if (u8ReplyNeeded) { - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Sending answer(0x%X)...\n"), u8ReplyNeeded);); - - sendParameter.m_bResponse = true; - sendParameter.m_bAuthorative = true; - - bResult = _sendMDNSMessage(sendParameter); - } - DEBUG_EX_INFO( - else { - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: No reply needed\n")); - } - ); - } - else { - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Something FAILED!\n"));); - m_pUDPContext->flush(); - } - - // - // Check and reset tiebreak-states - if (m_HostProbeInformation.m_bTiebreakNeeded) { - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: UNSOLVED tiebreak-need for host domain!\n"));); - m_HostProbeInformation.m_bTiebreakNeeded = false; - } - for (stcMDNSService* pService=m_pServices; pService; pService=pService->m_pNext) { - if (pService->m_ProbeInformation.m_bTiebreakNeeded) { - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: UNSOLVED tiebreak-need for service domain (%s.%s.%s)\n"), (pService->m_pcName ?: m_pcHostname), pService->m_pcService, pService->m_pcProtocol);); - pService->m_ProbeInformation.m_bTiebreakNeeded = false; - } - } - DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: FAILED!\n")); }); - return bResult; -} - -/* - * MDNSResponder::_parseResponse - * - * Responses are of interest in two cases: - * 1. find domain name conflicts while probing - * 2. get answers to service queries - * - * In both cases any included questions are ignored - * - * 1. If any answer has a domain name similar to one of the domain names we're planning to use (and are probing for), - * then we've got a 'probing conflict'. The conflict has to be solved on our side of the conflict (eg. by - * setting a new hostname and restart probing). The callback 'm_fnProbeResultCallback' is called with - * 'p_bProbeResult=false' in this case. - * - * 2. Service queries like '_http._tcp.local' will (if available) produce PTR, SRV, TXT and A/AAAA answers. - * All stored answers are pivoted by the service instance name (from the PTR record). Other answer parts, - * like host domain or IP address are than attached to this element. - * Any answer part carries a TTL, this is also stored (incl. the reception time); if the TTL is '0' the - * answer (part) is withdrawn by the sender and should be removed from any cache. RFC 6762, 10.1 proposes to - * set the caches TTL-value to 1 second in such a case and to delete the item only, if no update has - * has taken place in this second. - * Answer parts may arrive in 'unsorted' order, so they are grouped into three levels: - * Level 1: PRT - names the service instance (and is used as pivot), voids all other parts if is withdrawn or outdates - * Level 2: SRV - links the instance name to a host domain and port, voids A/AAAA parts if is withdrawn or outdates - * TXT - links the instance name to services TXTs - * Level 3: A/AAAA - links the host domain to an IP address - */ -bool MDNSResponder::_parseResponse(const MDNSResponder::stcMDNS_MsgHeader& p_MsgHeader) { - //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseResponse\n"));); - //DEBUG_EX_INFO(_udpDump();); - - bool bResult = false; - - // A response should be the result of a query or a probe - if ((_hasServiceQueriesWaitingForAnswers()) || // Waiting for query answers OR - (_hasProbesWaitingForAnswers())) { // Probe responses - - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseResponse: Received a response\n")); - //_udpDump(); - ); - - bResult = true; - // - // Ignore questions here - stcMDNS_RRQuestion dummyRRQ; - for (uint16_t qd=0; ((bResult) && (qdm_pNext = pCollectedRRAnswers; - pCollectedRRAnswers = pRRAnswer; - } - else { - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseResponse: FAILED to read answer!\n"));); - if (pRRAnswer) { - delete pRRAnswer; - pRRAnswer = 0; - } - bResult = false; - } - } // for answers - - // - // Process answers - if (bResult) { - bResult = ((!pCollectedRRAnswers) || - (_processAnswers(pCollectedRRAnswers))); - } - else { // Some failure while reading answers - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseResponse: FAILED to read answers!\n"));); - m_pUDPContext->flush(); - } - - // Delete collected answers - while (pCollectedRRAnswers) { - //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseResponse: DELETING answer!\n"));); - stcMDNS_RRAnswer* pNextAnswer = pCollectedRRAnswers->m_pNext; - delete pCollectedRRAnswers; - pCollectedRRAnswers = pNextAnswer; - } - } - else { // Received an unexpected response -> ignore - /*DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseResponse: Received an unexpected response... ignoring!\nDUMP:\n")); - bool bDumpResult = true; - for (uint16_t qd=0; ((bDumpResult) && (qdflush(); - bResult = true; - } - DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseResponse: FAILED!\n")); }); - return bResult; -} - -/* - * MDNSResponder::_processAnswers - * Host: - * A (0x01): eg. esp8266.local A OP TTL 123.456.789.012 - * AAAA (01Cx): eg. esp8266.local AAAA OP TTL 1234:5678::90 - * PTR (0x0C, IP4): eg. 012.789.456.123.in-addr.arpa PTR OP TTL esp8266.local - * PTR (0x0C, IP6): eg. 90.0.0.0.0.0.0.0.0.0.0.0.78.56.34.12.ip6.arpa PTR OP TTL esp8266.local - * Service: - * PTR (0x0C, srv name): eg. _http._tcp.local PTR OP TTL MyESP._http._tcp.local - * PTR (0x0C, srv type): eg. _services._dns-sd._udp.local PTR OP TTL _http._tcp.local - * SRV (0x21): eg. MyESP._http._tcp.local SRV OP TTL PRIORITY WEIGHT PORT esp8266.local - * TXT (0x10): eg. MyESP._http._tcp.local TXT OP TTL c#=1 - * - */ -bool MDNSResponder::_processAnswers(const MDNSResponder::stcMDNS_RRAnswer* p_pAnswers) { - - bool bResult = false; - - if (p_pAnswers) { - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processAnswers: Processing answers...\n"));); - bResult = true; - - // Answers may arrive in an unexpected order. So we loop our answers as long, as we - // can connect new information to service queries - bool bFoundNewKeyAnswer; - do { - bFoundNewKeyAnswer = false; - - const stcMDNS_RRAnswer* pRRAnswer = p_pAnswers; - while ((pRRAnswer) && - (bResult)) { - // 1. level answer (PTR) - if (AnswerType_PTR == pRRAnswer->answerType()) { - // eg. _http._tcp.local PTR xxxx xx MyESP._http._tcp.local - bResult = _processPTRAnswer((stcMDNS_RRAnswerPTR*)pRRAnswer, bFoundNewKeyAnswer); // May 'enable' new SRV or TXT answers to be linked to queries - } - // 2. level answers - // SRV -> host domain and port - else if (AnswerType_SRV == pRRAnswer->answerType()) { - // eg. MyESP_http._tcp.local SRV xxxx xx yy zz 5000 esp8266.local - bResult = _processSRVAnswer((stcMDNS_RRAnswerSRV*)pRRAnswer, bFoundNewKeyAnswer); // May 'enable' new A/AAAA answers to be linked to queries - } - // TXT -> Txts - else if (AnswerType_TXT == pRRAnswer->answerType()) { - // eg. MyESP_http._tcp.local TXT xxxx xx c#=1 - bResult = _processTXTAnswer((stcMDNS_RRAnswerTXT*)pRRAnswer); - } - // 3. level answers -#ifdef MDNS_IP4_SUPPORT - // A -> IP4Address - else if (AnswerType_A == pRRAnswer->answerType()) { - // eg. esp8266.local A xxxx xx 192.168.2.120 - bResult = _processAAnswer((stcMDNS_RRAnswerA*)pRRAnswer); - } -#endif -#ifdef MDNS_IP6_SUPPORT - // AAAA -> IP6Address - else if (AnswerType_AAAA == pRRAnswer->answerType()) { - // eg. esp8266.local AAAA xxxx xx 09cf::0c - bResult = _processAAAAAnswer((stcMDNS_RRAnswerAAAA*)pRRAnswer); - } -#endif - - // Finally check for probing conflicts - // Host domain - if ((ProbingStatus_InProgress == m_HostProbeInformation.m_ProbingStatus) && - ((AnswerType_A == pRRAnswer->answerType()) || - (AnswerType_AAAA == pRRAnswer->answerType()))) { - - stcMDNS_RRDomain hostDomain; - if ((_buildDomainForHost(m_pcHostname, hostDomain)) && - (pRRAnswer->m_Header.m_Domain == hostDomain)) { - - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processAnswers: Probing CONFLICT found with: %s.local\n"), m_pcHostname);); - _cancelProbingForHost(); - } - } - // Service domains - for (stcMDNSService* pService=m_pServices; pService; pService=pService->m_pNext) { - if ((ProbingStatus_InProgress == pService->m_ProbeInformation.m_ProbingStatus) && - ((AnswerType_TXT == pRRAnswer->answerType()) || - (AnswerType_SRV == pRRAnswer->answerType()))) { - - stcMDNS_RRDomain serviceDomain; - if ((_buildDomainForService(*pService, true, serviceDomain)) && - (pRRAnswer->m_Header.m_Domain == serviceDomain)) { - - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processAnswers: Probing CONFLICT found with: %s.%s.%s\n"), (pService->m_pcName ?: m_pcHostname), pService->m_pcService, pService->m_pcProtocol);); - _cancelProbingForService(*pService); - } - } - } - - pRRAnswer = pRRAnswer->m_pNext; // Next collected answer - } // while (answers) - } while ((bFoundNewKeyAnswer) && - (bResult)); - } // else: No answers provided - DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processAnswers: FAILED!\n")); }); - return bResult; -} - -/* - * MDNSResponder::_processPTRAnswer - */ -bool MDNSResponder::_processPTRAnswer(const MDNSResponder::stcMDNS_RRAnswerPTR* p_pPTRAnswer, - bool& p_rbFoundNewKeyAnswer) { - - bool bResult = false; - - if ((bResult = (0 != p_pPTRAnswer))) { - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processPTRAnswer: Processing PTR answers...\n"));); - // eg. _http._tcp.local PTR xxxx xx MyESP._http._tcp.local - // Check pending service queries for eg. '_http._tcp' - - stcMDNSServiceQuery* pServiceQuery = _findNextServiceQueryByServiceType(p_pPTRAnswer->m_Header.m_Domain, 0); - while (pServiceQuery) { - if (pServiceQuery->m_bAwaitingAnswers) { - // Find answer for service domain (eg. MyESP._http._tcp.local) - stcMDNSServiceQuery::stcAnswer* pSQAnswer = pServiceQuery->findAnswerForServiceDomain(p_pPTRAnswer->m_PTRDomain); - if (pSQAnswer) { // existing answer - if (p_pPTRAnswer->m_u32TTL) { // Received update message - pSQAnswer->m_TTLServiceDomain.set(p_pPTRAnswer->m_u32TTL); // Update TTL tag - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processPTRAnswer: Updated TTL(%d) for "), (int)p_pPTRAnswer->m_u32TTL); - _printRRDomain(pSQAnswer->m_ServiceDomain); - DEBUG_OUTPUT.printf_P(PSTR("\n")); - ); - } - else { // received goodbye-message - pSQAnswer->m_TTLServiceDomain.prepareDeletion(); // Prepare answer deletion according to RFC 6762, 10.1 - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processPTRAnswer: 'Goodbye' received for ")); - _printRRDomain(pSQAnswer->m_ServiceDomain); - DEBUG_OUTPUT.printf_P(PSTR("\n")); - ); - } - } - else if ((p_pPTRAnswer->m_u32TTL) && // Not just a goodbye-message - ((pSQAnswer = new stcMDNSServiceQuery::stcAnswer))) { // Not yet included -> add answer - pSQAnswer->m_ServiceDomain = p_pPTRAnswer->m_PTRDomain; - pSQAnswer->m_u32ContentFlags |= ServiceQueryAnswerType_ServiceDomain; - pSQAnswer->m_TTLServiceDomain.set(p_pPTRAnswer->m_u32TTL); - pSQAnswer->releaseServiceDomain(); - - bResult = pServiceQuery->addAnswer(pSQAnswer); - p_rbFoundNewKeyAnswer = true; - if (pServiceQuery->m_fnCallback) { - MDNSServiceInfo serviceInfo(*this,(hMDNSServiceQuery)pServiceQuery, pServiceQuery->indexOfAnswer(pSQAnswer)); - pServiceQuery->m_fnCallback(serviceInfo, static_cast(ServiceQueryAnswerType_ServiceDomain), true); - } - } - } - pServiceQuery = _findNextServiceQueryByServiceType(p_pPTRAnswer->m_Header.m_Domain, pServiceQuery); - } - } // else: No p_pPTRAnswer - DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processPTRAnswer: FAILED!\n")); }); - return bResult; -} - -/* - * MDNSResponder::_processSRVAnswer - */ -bool MDNSResponder::_processSRVAnswer(const MDNSResponder::stcMDNS_RRAnswerSRV* p_pSRVAnswer, - bool& p_rbFoundNewKeyAnswer) { - - bool bResult = false; - - if ((bResult = (0 != p_pSRVAnswer))) { - // eg. MyESP._http._tcp.local SRV xxxx xx yy zz 5000 esp8266.local - - stcMDNSServiceQuery* pServiceQuery = m_pServiceQueries; - while (pServiceQuery) { - stcMDNSServiceQuery::stcAnswer* pSQAnswer = pServiceQuery->findAnswerForServiceDomain(p_pSRVAnswer->m_Header.m_Domain); - if (pSQAnswer) { // Answer for this service domain (eg. MyESP._http._tcp.local) available - if (p_pSRVAnswer->m_u32TTL) { // First or update message (TTL != 0) - pSQAnswer->m_TTLHostDomainAndPort.set(p_pSRVAnswer->m_u32TTL); // Update TTL tag - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processSRVAnswer: Updated TTL(%d) for "), (int)p_pSRVAnswer->m_u32TTL); - _printRRDomain(pSQAnswer->m_ServiceDomain); - DEBUG_OUTPUT.printf_P(PSTR(" host domain and port\n")); - ); - // Host domain & Port - if ((pSQAnswer->m_HostDomain != p_pSRVAnswer->m_SRVDomain) || - (pSQAnswer->m_u16Port != p_pSRVAnswer->m_u16Port)) { - - pSQAnswer->m_HostDomain = p_pSRVAnswer->m_SRVDomain; - pSQAnswer->releaseHostDomain(); - pSQAnswer->m_u16Port = p_pSRVAnswer->m_u16Port; - pSQAnswer->m_u32ContentFlags |= ServiceQueryAnswerType_HostDomainAndPort; - - p_rbFoundNewKeyAnswer = true; - if (pServiceQuery->m_fnCallback) { - MDNSServiceInfo serviceInfo(*this, (hMDNSServiceQuery)pServiceQuery, pServiceQuery->indexOfAnswer(pSQAnswer)); - pServiceQuery->m_fnCallback(serviceInfo, static_cast(ServiceQueryAnswerType_HostDomainAndPort), true); - } - } - } - else { // Goodby message - pSQAnswer->m_TTLHostDomainAndPort.prepareDeletion(); // Prepare answer deletion according to RFC 6762, 10.1 - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processSRVAnswer: 'Goodbye' received for ")); - _printRRDomain(pSQAnswer->m_ServiceDomain); - DEBUG_OUTPUT.printf_P(PSTR(" host domain and port\n")); - ); - } - } - pServiceQuery = pServiceQuery->m_pNext; - } // while(service query) - } // else: No p_pSRVAnswer - DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processSRVAnswer: FAILED!\n")); }); - return bResult; -} - -/* - * MDNSResponder::_processTXTAnswer - */ -bool MDNSResponder::_processTXTAnswer(const MDNSResponder::stcMDNS_RRAnswerTXT* p_pTXTAnswer) { - - bool bResult = false; - - if ((bResult = (0 != p_pTXTAnswer))) { - // eg. MyESP._http._tcp.local TXT xxxx xx c#=1 - - stcMDNSServiceQuery* pServiceQuery = m_pServiceQueries; - while (pServiceQuery) { - stcMDNSServiceQuery::stcAnswer* pSQAnswer = pServiceQuery->findAnswerForServiceDomain(p_pTXTAnswer->m_Header.m_Domain); - if (pSQAnswer) { // Answer for this service domain (eg. MyESP._http._tcp.local) available - if (p_pTXTAnswer->m_u32TTL) { // First or update message - pSQAnswer->m_TTLTxts.set(p_pTXTAnswer->m_u32TTL); // Update TTL tag - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processTXTAnswer: Updated TTL(%d) for "), (int)p_pTXTAnswer->m_u32TTL); - _printRRDomain(pSQAnswer->m_ServiceDomain); - DEBUG_OUTPUT.printf_P(PSTR(" TXTs\n")); - ); - if (!pSQAnswer->m_Txts.compare(p_pTXTAnswer->m_Txts)) { - pSQAnswer->m_Txts = p_pTXTAnswer->m_Txts; - pSQAnswer->m_u32ContentFlags |= ServiceQueryAnswerType_Txts; - pSQAnswer->releaseTxts(); - - if (pServiceQuery->m_fnCallback) { - MDNSServiceInfo serviceInfo(*this, (hMDNSServiceQuery)pServiceQuery, pServiceQuery->indexOfAnswer(pSQAnswer)); - pServiceQuery->m_fnCallback(serviceInfo , static_cast(ServiceQueryAnswerType_Txts), true); - } - } - } - else { // Goodby message - pSQAnswer->m_TTLTxts.prepareDeletion(); // Prepare answer deletion according to RFC 6762, 10.1 - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processTXTAnswer: 'Goodbye' received for ")); - _printRRDomain(pSQAnswer->m_ServiceDomain); - DEBUG_OUTPUT.printf_P(PSTR(" TXTs\n")); - ); - } - } - pServiceQuery = pServiceQuery->m_pNext; - } // while(service query) - } // else: No p_pTXTAnswer - DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processTXTAnswer: FAILED!\n")); }); - return bResult; -} - -#ifdef MDNS_IP4_SUPPORT - /* - * MDNSResponder::_processAAnswer - */ - bool MDNSResponder::_processAAnswer(const MDNSResponder::stcMDNS_RRAnswerA* p_pAAnswer) { - - bool bResult = false; - - if ((bResult = (0 != p_pAAnswer))) { - // eg. esp8266.local A xxxx xx 192.168.2.120 - - stcMDNSServiceQuery* pServiceQuery = m_pServiceQueries; - while (pServiceQuery) { - stcMDNSServiceQuery::stcAnswer* pSQAnswer = pServiceQuery->findAnswerForHostDomain(p_pAAnswer->m_Header.m_Domain); - if (pSQAnswer) { // Answer for this host domain (eg. esp8266.local) available - stcMDNSServiceQuery::stcAnswer::stcIP4Address* pIP4Address = pSQAnswer->findIP4Address(p_pAAnswer->m_IPAddress); - if (pIP4Address) { - // Already known IP4 address - if (p_pAAnswer->m_u32TTL) { // Valid TTL -> Update answers TTL - pIP4Address->m_TTL.set(p_pAAnswer->m_u32TTL); - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processAAnswer: Updated TTL(%d) for "), (int)p_pAAnswer->m_u32TTL); - _printRRDomain(pSQAnswer->m_ServiceDomain); - DEBUG_OUTPUT.printf_P(PSTR(" IP4Address (%s)\n"), pIP4Address->m_IPAddress.toString().c_str()); - ); - } - else { // 'Goodbye' message for known IP4 address - pIP4Address->m_TTL.prepareDeletion(); // Prepare answer deletion according to RFC 6762, 10.1 - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processAAnswer: 'Goodbye' received for ")); - _printRRDomain(pSQAnswer->m_ServiceDomain); - DEBUG_OUTPUT.printf_P(PSTR(" IP4 address (%s)\n"), pIP4Address->m_IPAddress.toString().c_str()); - ); - } - } - else { - // Until now unknown IP4 address -> Add (if the message isn't just a 'Goodbye' note) - if (p_pAAnswer->m_u32TTL) { // NOT just a 'Goodbye' message - pIP4Address = new stcMDNSServiceQuery::stcAnswer::stcIP4Address(p_pAAnswer->m_IPAddress, p_pAAnswer->m_u32TTL); - if ((pIP4Address) && - (pSQAnswer->addIP4Address(pIP4Address))) { - - pSQAnswer->m_u32ContentFlags |= ServiceQueryAnswerType_IP4Address; - if (pServiceQuery->m_fnCallback) { - MDNSServiceInfo serviceInfo (*this, (hMDNSServiceQuery)pServiceQuery, pServiceQuery->indexOfAnswer(pSQAnswer)); - pServiceQuery->m_fnCallback(serviceInfo, static_cast(ServiceQueryAnswerType_IP4Address), true); - } - } - else { - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processAAnswer: FAILED to add IP4 address (%s)!\n"), p_pAAnswer->m_IPAddress.toString().c_str());); - } - } - } - } - pServiceQuery = pServiceQuery->m_pNext; - } // while(service query) - } // else: No p_pAAnswer - DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processAAnswer: FAILED!\n")); }); - return bResult; - } -#endif - -#ifdef MDNS_IP6_SUPPORT - /* - * MDNSResponder::_processAAAAAnswer - */ - bool MDNSResponder::_processAAAAAnswer(const MDNSResponder::stcMDNS_RRAnswerAAAA* p_pAAAAAnswer) { - - bool bResult = false; - - if ((bResult = (0 != p_pAAAAAnswer))) { - // eg. esp8266.local AAAA xxxx xx 0bf3::0c - - stcMDNSServiceQuery* pServiceQuery = m_pServiceQueries; - while (pServiceQuery) { - stcMDNSServiceQuery::stcAnswer* pSQAnswer = pServiceQuery->findAnswerForHostDomain(p_pAAAAAnswer->m_Header.m_Domain); - if (pSQAnswer) { // Answer for this host domain (eg. esp8266.local) available - stcIP6Address* pIP6Address = pSQAnswer->findIP6Address(p_pAAAAAnswer->m_IPAddress); - if (pIP6Address) { - // Already known IP6 address - if (p_pAAAAAnswer->m_u32TTL) { // Valid TTL -> Update answers TTL - pIP6Address->m_TTL.set(p_pAAAAAnswer->m_u32TTL); - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processAAnswer: Updated TTL(%lu) for "), p_pAAAAAnswer->m_u32TTL); - _printRRDomain(pSQAnswer->m_ServiceDomain); - DEBUG_OUTPUT.printf_P(PSTR(" IP6 address (%s)\n"), pIP6Address->m_IPAddress.toString().c_str()); - ); - } - else { // 'Goodbye' message for known IP6 address - pIP6Address->m_TTL.prepareDeletion(); // Prepare answer deletion according to RFC 6762, 10.1 - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processAAnswer: 'Goodbye' received for ")); - _printRRDomain(pSQAnswer->m_ServiceDomain); - DEBUG_OUTPUT.printf_P(PSTR(" IP6 address (%s)\n"), pIP6Address->m_IPAddress.toString().c_str()); - ); - } - } - else { - // Until now unknown IP6 address -> Add (if the message isn't just a 'Goodbye' note) - if (p_pAAAAAnswer->m_u32TTL) { // NOT just a 'Goodbye' message - pIP6Address = new stcIP6Address(p_pAAAAAnswer->m_IPAddress, p_pAAAAAnswer->m_u32TTL); - if ((pIP6Address) && - (pSQAnswer->addIP6Address(pIP6Address))) { - - pSQAnswer->m_u32ContentFlags |= ServiceQueryAnswerType_IP6Address; - - if (pServiceQuery->m_fnCallback) { - pServiceQuery->m_fnCallback(this, (hMDNSServiceQuery)pServiceQuery, pServiceQuery->indexOfAnswer(pSQAnswer), ServiceQueryAnswerType_IP6Address, true, pServiceQuery->m_pUserdata); - } - } - else { - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processAAnswer: FAILED to add IP6 address (%s)!\n"), p_pAAAAAnswer->m_IPAddress.toString().c_str());); - } - } - } - } - pServiceQuery = pServiceQuery->m_pNext; - } // while(service query) - } // else: No p_pAAAAAnswer - - return bResult; - } -#endif - - -/* - * PROBING - */ - -/* - * MDNSResponder::_updateProbeStatus - * - * Manages the (outgoing) probing process. - * - If probing has not been started yet (ProbingStatus_NotStarted), the initial delay (see RFC 6762) is determined and - * the process is started - * - After timeout (of initial or subsequential delay) a probe message is send out for three times. If the message has - * already been sent out three times, the probing has been successful and is finished. - * - * Conflict management is handled in '_parseResponse ff.' - * Tiebraking is handled in 'parseQuery ff.' - */ -bool MDNSResponder::_updateProbeStatus(void) { - - bool bResult = true; - - // - // Probe host domain - if ((ProbingStatus_ReadyToStart == m_HostProbeInformation.m_ProbingStatus) && // Ready to get started AND - //TODO: Fix the following to allow Ethernet shield or other interfaces - (_getResponseMulticastInterface() != IPAddress())) { // Has IP address - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Starting host probing...\n"));); - - // First probe delay SHOULD be random 0-250 ms - m_HostProbeInformation.m_Timeout.reset(rand() % MDNS_PROBE_DELAY); - m_HostProbeInformation.m_ProbingStatus = ProbingStatus_InProgress; - } - else if ((ProbingStatus_InProgress == m_HostProbeInformation.m_ProbingStatus) && // Probing AND - (m_HostProbeInformation.m_Timeout.expired())) { // Time for next probe - - if (MDNS_PROBE_COUNT > m_HostProbeInformation.m_u8SentCount) { // Send next probe - if ((bResult = _sendHostProbe())) { - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Did sent host probe\n\n"));); - m_HostProbeInformation.m_Timeout.reset(MDNS_PROBE_DELAY); - ++m_HostProbeInformation.m_u8SentCount; - } - } - else { // Probing finished - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Done host probing.\n"));); - m_HostProbeInformation.m_ProbingStatus = ProbingStatus_Done; - m_HostProbeInformation.m_Timeout.resetToNeverExpires(); - if (m_HostProbeInformation.m_fnHostProbeResultCallback) { - m_HostProbeInformation.m_fnHostProbeResultCallback(m_pcHostname, true); - } - - // Prepare to announce host - m_HostProbeInformation.m_u8SentCount = 0; - m_HostProbeInformation.m_Timeout.reset(MDNS_ANNOUNCE_DELAY); - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Prepared host announcing.\n\n"));); - } - } // else: Probing already finished OR waiting for next time slot - else if ((ProbingStatus_Done == m_HostProbeInformation.m_ProbingStatus) && - (m_HostProbeInformation.m_Timeout.expired())) { - - if ((bResult = _announce(true, false))) { // Don't announce services here - ++m_HostProbeInformation.m_u8SentCount; - - if (MDNS_ANNOUNCE_COUNT > m_HostProbeInformation.m_u8SentCount) { - m_HostProbeInformation.m_Timeout.reset(MDNS_ANNOUNCE_DELAY); - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Announcing host (%d).\n\n"), m_HostProbeInformation.m_u8SentCount);); - } - else { - m_HostProbeInformation.m_Timeout.resetToNeverExpires(); - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Done host announcing.\n\n"));); - } - } - } - - // - // Probe services - for (stcMDNSService* pService=m_pServices; ((bResult) && (pService)); pService=pService->m_pNext) { - if (ProbingStatus_ReadyToStart == pService->m_ProbeInformation.m_ProbingStatus) { // Ready to get started - - pService->m_ProbeInformation.m_Timeout.reset(MDNS_PROBE_DELAY); // More or equal than first probe for host domain - pService->m_ProbeInformation.m_ProbingStatus = ProbingStatus_InProgress; - } - else if ((ProbingStatus_InProgress == pService->m_ProbeInformation.m_ProbingStatus) && // Probing AND - (pService->m_ProbeInformation.m_Timeout.expired())) { // Time for next probe - - if (MDNS_PROBE_COUNT > pService->m_ProbeInformation.m_u8SentCount) { // Send next probe - if ((bResult = _sendServiceProbe(*pService))) { - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Did sent service probe (%u)\n\n"), (pService->m_ProbeInformation.m_u8SentCount + 1));); - pService->m_ProbeInformation.m_Timeout.reset(MDNS_PROBE_DELAY); - ++pService->m_ProbeInformation.m_u8SentCount; - } - } - else { // Probing finished - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Done service probing %s.%s.%s\n\n"), (pService->m_pcName ?: m_pcHostname), pService->m_pcService, pService->m_pcProtocol);); - pService->m_ProbeInformation.m_ProbingStatus = ProbingStatus_Done; - pService->m_ProbeInformation.m_Timeout.resetToNeverExpires(); - if (pService->m_ProbeInformation.m_fnServiceProbeResultCallback) { - pService->m_ProbeInformation.m_fnServiceProbeResultCallback(pService->m_pcName, pService, true); - } - // Prepare to announce service - pService->m_ProbeInformation.m_u8SentCount = 0; - pService->m_ProbeInformation.m_Timeout.reset(MDNS_ANNOUNCE_DELAY); - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Prepared service announcing.\n\n"));); - } - } // else: Probing already finished OR waiting for next time slot - else if ((ProbingStatus_Done == pService->m_ProbeInformation.m_ProbingStatus) && - (pService->m_ProbeInformation.m_Timeout.expired())) { - - if ((bResult = _announceService(*pService))) { // Announce service - ++pService->m_ProbeInformation.m_u8SentCount; - - if (MDNS_ANNOUNCE_COUNT > pService->m_ProbeInformation.m_u8SentCount) { - pService->m_ProbeInformation.m_Timeout.reset(MDNS_ANNOUNCE_DELAY); - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Announcing service %s.%s.%s (%d)\n\n"), (pService->m_pcName ?: m_pcHostname), pService->m_pcService, pService->m_pcProtocol, pService->m_ProbeInformation.m_u8SentCount);); - } - else { - pService->m_ProbeInformation.m_Timeout.resetToNeverExpires(); - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Done service announcing for %s.%s.%s\n\n"), (pService->m_pcName ?: m_pcHostname), pService->m_pcService, pService->m_pcProtocol);); - } - } - } - } - DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: FAILED!\n\n")); }); - return bResult; -} - -/* - * MDNSResponder::_resetProbeStatus - * - * Resets the probe status. - * If 'p_bRestart' is set, the status is set to ProbingStatus_NotStarted. Consequently, - * when running 'updateProbeStatus' (which is done in every '_update' loop), the probing - * process is restarted. - */ -bool MDNSResponder::_resetProbeStatus(bool p_bRestart /*= true*/) { - - m_HostProbeInformation.clear(false); - m_HostProbeInformation.m_ProbingStatus = (p_bRestart ? ProbingStatus_ReadyToStart : ProbingStatus_Done); - - for (stcMDNSService* pService=m_pServices; pService; pService=pService->m_pNext) { - pService->m_ProbeInformation.clear(false); - pService->m_ProbeInformation.m_ProbingStatus = (p_bRestart ? ProbingStatus_ReadyToStart : ProbingStatus_Done); - } - return true; -} - -/* - * MDNSResponder::_hasProbesWaitingForAnswers - */ -bool MDNSResponder::_hasProbesWaitingForAnswers(void) const { - - bool bResult = ((ProbingStatus_InProgress == m_HostProbeInformation.m_ProbingStatus) && // Probing - (0 < m_HostProbeInformation.m_u8SentCount)); // And really probing - - for (stcMDNSService* pService=m_pServices; ((!bResult) && (pService)); pService=pService->m_pNext) { - bResult = ((ProbingStatus_InProgress == pService->m_ProbeInformation.m_ProbingStatus) && // Probing - (0 < pService->m_ProbeInformation.m_u8SentCount)); // And really probing - } - return bResult; -} - -/* - * MDNSResponder::_sendHostProbe - * - * Asks (probes) in the local network for the planned host domain - * - (eg. esp8266.local) - * - * To allow 'tiebreaking' (see '_parseQuery'), the answers for these questions are delivered in - * the 'knwon answers' section of the query. - * Host domain: - * - A/AAAA (eg. esp8266.esp -> 192.168.2.120) - */ -bool MDNSResponder::_sendHostProbe(void) { - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _sendHostProbe (%s, %lu)\n"), m_pcHostname, millis());); - - bool bResult = true; - - // Requests for host domain - stcMDNSSendParameter sendParameter; - sendParameter.m_bCacheFlush = false; // RFC 6762 10.2 - - sendParameter.m_pQuestions = new stcMDNS_RRQuestion; - if (((bResult = (0 != sendParameter.m_pQuestions))) && - ((bResult = _buildDomainForHost(m_pcHostname, sendParameter.m_pQuestions->m_Header.m_Domain)))) { - - //sendParameter.m_pQuestions->m_bUnicast = true; - sendParameter.m_pQuestions->m_Header.m_Attributes.m_u16Type = DNS_RRTYPE_ANY; - sendParameter.m_pQuestions->m_Header.m_Attributes.m_u16Class = (0x8000 | DNS_RRCLASS_IN); // Unicast & INternet - - // Add known answers -#ifdef MDNS_IP4_SUPPORT - sendParameter.m_u8HostReplyMask |= ContentFlag_A; // Add A answer -#endif -#ifdef MDNS_IP6_SUPPORT - sendParameter.m_u8HostReplyMask |= ContentFlag_AAAA; // Add AAAA answer -#endif - } - else { - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _sendHostProbe: FAILED to create host question!\n"));); - if (sendParameter.m_pQuestions) { - delete sendParameter.m_pQuestions; - sendParameter.m_pQuestions = 0; - } - } - DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _sendHostProbe: FAILED!\n")); }); - return ((bResult) && - (_sendMDNSMessage(sendParameter))); -} - -/* - * MDNSResponder::_sendServiceProbe - * - * Asks (probes) in the local network for the planned service instance domain - * - (eg. MyESP._http._tcp.local). - * - * To allow 'tiebreaking' (see '_parseQuery'), the answers for these questions are delivered in - * the 'knwon answers' section of the query. - * Service domain: - * - SRV (eg. MyESP._http._tcp.local -> 5000 esp8266.local) - * - PTR NAME (eg. _http._tcp.local -> MyESP._http._tcp.local) (TODO: Check if needed, maybe TXT is better) - */ -bool MDNSResponder::_sendServiceProbe(stcMDNSService& p_rService) { - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _sendServiceProbe (%s.%s.%s, %lu)\n"), (p_rService.m_pcName ?: m_pcHostname), p_rService.m_pcService, p_rService.m_pcProtocol, millis());); - - bool bResult = true; - - // Requests for service instance domain - stcMDNSSendParameter sendParameter; - sendParameter.m_bCacheFlush = false; // RFC 6762 10.2 - - sendParameter.m_pQuestions = new stcMDNS_RRQuestion; - if (((bResult = (0 != sendParameter.m_pQuestions))) && - ((bResult = _buildDomainForService(p_rService, true, sendParameter.m_pQuestions->m_Header.m_Domain)))) { - - sendParameter.m_pQuestions->m_bUnicast = true; - sendParameter.m_pQuestions->m_Header.m_Attributes.m_u16Type = DNS_RRTYPE_ANY; - sendParameter.m_pQuestions->m_Header.m_Attributes.m_u16Class = (0x8000 | DNS_RRCLASS_IN); // Unicast & INternet - - // Add known answers - p_rService.m_u8ReplyMask = (ContentFlag_SRV | ContentFlag_PTR_NAME); // Add SRV and PTR NAME answers - } - else { - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _sendServiceProbe: FAILED to create service question!\n"));); - if (sendParameter.m_pQuestions) { - delete sendParameter.m_pQuestions; - sendParameter.m_pQuestions = 0; - } - } - DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _sendServiceProbe: FAILED!\n")); }); - return ((bResult) && - (_sendMDNSMessage(sendParameter))); -} - -/* - * MDNSResponder::_cancelProbingForHost - */ -bool MDNSResponder::_cancelProbingForHost(void) { - - bool bResult = false; - - m_HostProbeInformation.clear(false); - // Send host notification - if (m_HostProbeInformation.m_fnHostProbeResultCallback) { - m_HostProbeInformation.m_fnHostProbeResultCallback(m_pcHostname, false); - - bResult = true; - } - - for (stcMDNSService* pService=m_pServices; ((!bResult) && (pService)); pService=pService->m_pNext) { - bResult = _cancelProbingForService(*pService); - } - return bResult; -} - -/* - * MDNSResponder::_cancelProbingForService - */ -bool MDNSResponder::_cancelProbingForService(stcMDNSService& p_rService) { - - bool bResult = false; - - p_rService.m_ProbeInformation.clear(false); - // Send notification - if (p_rService.m_ProbeInformation.m_fnServiceProbeResultCallback) { - p_rService.m_ProbeInformation.m_fnServiceProbeResultCallback(p_rService.m_pcName,&p_rService,false); - bResult = true; - } - return bResult; -} - - - -/** - * ANNOUNCING - */ - -/* - * MDNSResponder::_announce - * - * Announces the host domain: - * - A/AAAA (eg. esp8266.local -> 192.168.2.120) - * - PTR (eg. 192.168.2.120.in-addr.arpa -> esp8266.local) - * - * and all presented services: - * - PTR_TYPE (_services._dns-sd._udp.local -> _http._tcp.local) - * - PTR_NAME (eg. _http._tcp.local -> MyESP8266._http._tcp.local) - * - SRV (eg. MyESP8266._http._tcp.local -> 5000 esp8266.local) - * - TXT (eg. MyESP8266._http._tcp.local -> c#=1) - * - * Goodbye (Un-Announcing) for the host domain and all services is also handled here. - * Goodbye messages are created by setting the TTL for the answer to 0, this happens - * inside the '_writeXXXAnswer' procs via 'sendParameter.m_bUnannounce = true' - */ -bool MDNSResponder::_announce(bool p_bAnnounce, - bool p_bIncludeServices) { - - bool bResult = false; - - stcMDNSSendParameter sendParameter; - if (ProbingStatus_Done == m_HostProbeInformation.m_ProbingStatus) { - - bResult = true; - - sendParameter.m_bResponse = true; // Announces are 'Unsolicited authorative responses' - sendParameter.m_bAuthorative = true; - sendParameter.m_bUnannounce = !p_bAnnounce; // When unannouncing, the TTL is set to '0' while creating the answers - - // Announce host - sendParameter.m_u8HostReplyMask = 0; - #ifdef MDNS_IP4_SUPPORT - sendParameter.m_u8HostReplyMask |= ContentFlag_A; // A answer - sendParameter.m_u8HostReplyMask |= ContentFlag_PTR_IP4; // PTR_IP4 answer - #endif - #ifdef MDNS_IP6_SUPPORT - sendParameter.m_u8HostReplyMask |= ContentFlag_AAAA; // AAAA answer - sendParameter.m_u8HostReplyMask |= ContentFlag_PTR_IP6; // PTR_IP6 answer - #endif - - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _announce: Announcing host %s (content 0x%X)\n"), m_pcHostname, sendParameter.m_u8HostReplyMask);); - - if (p_bIncludeServices) { - // Announce services (service type, name, SRV (location) and TXTs) - for (stcMDNSService* pService=m_pServices; ((bResult) && (pService)); pService=pService->m_pNext) { - if (ProbingStatus_Done == pService->m_ProbeInformation.m_ProbingStatus) { - pService->m_u8ReplyMask = (ContentFlag_PTR_TYPE | ContentFlag_PTR_NAME | ContentFlag_SRV | ContentFlag_TXT); - - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _announce: Announcing service %s.%s.%s (content %u)\n"), (pService->m_pcName ?: m_pcHostname), pService->m_pcService, pService->m_pcProtocol, pService->m_u8ReplyMask);); - } - } - } - } - DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _announce: FAILED!\n")); }); - return ((bResult) && - (_sendMDNSMessage(sendParameter))); -} - -/* - * MDNSResponder::_announceService - */ -bool MDNSResponder::_announceService(stcMDNSService& p_rService, - bool p_bAnnounce /*= true*/) { - - bool bResult = false; - - stcMDNSSendParameter sendParameter; - if (ProbingStatus_Done == p_rService.m_ProbeInformation.m_ProbingStatus) { - - sendParameter.m_bResponse = true; // Announces are 'Unsolicited authorative responses' - sendParameter.m_bAuthorative = true; - sendParameter.m_bUnannounce = !p_bAnnounce; // When unannouncing, the TTL is set to '0' while creating the answers - - // DON'T announce host - sendParameter.m_u8HostReplyMask = 0; - - // Announce services (service type, name, SRV (location) and TXTs) - p_rService.m_u8ReplyMask = (ContentFlag_PTR_TYPE | ContentFlag_PTR_NAME | ContentFlag_SRV | ContentFlag_TXT); - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _announceService: Announcing service %s.%s.%s (content 0x%X)\n"), (p_rService.m_pcName ?: m_pcHostname), p_rService.m_pcService, p_rService.m_pcProtocol, p_rService.m_u8ReplyMask);); - - bResult = true; - } - DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _announceService: FAILED!\n")); }); - return ((bResult) && - (_sendMDNSMessage(sendParameter))); -} - - -/** - * SERVICE QUERY CACHE - */ - -/* - * MDNSResponder::_hasServiceQueriesWaitingForAnswers - */ -bool MDNSResponder::_hasServiceQueriesWaitingForAnswers(void) const { - - bool bOpenQueries = false; - - for (stcMDNSServiceQuery* pServiceQuery=m_pServiceQueries; pServiceQuery; pServiceQuery=pServiceQuery->m_pNext) { - if (pServiceQuery->m_bAwaitingAnswers) { - bOpenQueries = true; - break; - } - } - return bOpenQueries; -} - -/* - * MDNSResponder::_checkServiceQueryCache - * - * For any 'living' service query (m_bAwaitingAnswers == true) all available answers (their components) - * are checked for topicality based on the stored reception time and the answers TTL. - * When the components TTL is outlasted by more than 80%, a new question is generated, to get updated information. - * When no update arrived (in time), the component is removed from the answer (cache). - * - */ -bool MDNSResponder::_checkServiceQueryCache(void) { - - bool bResult = true; - - DEBUG_EX_INFO( - bool printedInfo = false; - ); - for (stcMDNSServiceQuery* pServiceQuery=m_pServiceQueries; ((bResult) && (pServiceQuery)); pServiceQuery=pServiceQuery->m_pNext) { - - // - // Resend dynamic service queries, if not already done often enough - if ((!pServiceQuery->m_bLegacyQuery) && - (MDNS_DYNAMIC_QUERY_RESEND_COUNT > pServiceQuery->m_u8SentCount) && - (pServiceQuery->m_ResendTimeout.expired())) { - - if ((bResult = _sendMDNSServiceQuery(*pServiceQuery))) { - ++pServiceQuery->m_u8SentCount; - pServiceQuery->m_ResendTimeout.reset((MDNS_DYNAMIC_QUERY_RESEND_COUNT > pServiceQuery->m_u8SentCount) - ? (MDNS_DYNAMIC_QUERY_RESEND_DELAY * (pServiceQuery->m_u8SentCount - 1)) - : esp8266::polledTimeout::oneShotMs::neverExpires); - } - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: %s to resend service query!"), (bResult ? "Succeeded" : "FAILED")); - printedInfo = true; - ); - } - - // - // Schedule updates for cached answers - if (pServiceQuery->m_bAwaitingAnswers) { - stcMDNSServiceQuery::stcAnswer* pSQAnswer = pServiceQuery->m_pAnswers; - while ((bResult) && - (pSQAnswer)) { - stcMDNSServiceQuery::stcAnswer* pNextSQAnswer = pSQAnswer->m_pNext; - - // 1. level answer - if ((bResult) && - (pSQAnswer->m_TTLServiceDomain.flagged())) { - - if (!pSQAnswer->m_TTLServiceDomain.finalTimeoutLevel()) { - - bResult = ((_sendMDNSServiceQuery(*pServiceQuery)) && - (pSQAnswer->m_TTLServiceDomain.restart())); - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: PTR update scheduled for ")); - _printRRDomain(pSQAnswer->m_ServiceDomain); - DEBUG_OUTPUT.printf_P(PSTR(" %s\n"), (bResult ? "OK" : "FAILURE")); - printedInfo = true; - ); - } - else { - // Timed out! -> Delete - if (pServiceQuery->m_fnCallback) { - MDNSServiceInfo serviceInfo(*this, (hMDNSServiceQuery)pServiceQuery, pServiceQuery->indexOfAnswer(pSQAnswer)); - pServiceQuery->m_fnCallback(serviceInfo, static_cast(ServiceQueryAnswerType_ServiceDomain), false); - } - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: Will remove PTR answer for ")); - _printRRDomain(pSQAnswer->m_ServiceDomain); - DEBUG_OUTPUT.printf_P(PSTR("\n")); - printedInfo = true; - ); - - bResult = pServiceQuery->removeAnswer(pSQAnswer); - pSQAnswer = 0; - continue; // Don't use this answer anymore - } - } // ServiceDomain flagged - - // 2. level answers - // HostDomain & Port (from SRV) - if ((bResult) && - (pSQAnswer->m_TTLHostDomainAndPort.flagged())) { - - if (!pSQAnswer->m_TTLHostDomainAndPort.finalTimeoutLevel()) { - - bResult = ((_sendMDNSQuery(pSQAnswer->m_ServiceDomain, DNS_RRTYPE_SRV)) && - (pSQAnswer->m_TTLHostDomainAndPort.restart())); - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: SRV update scheduled for ")); - _printRRDomain(pSQAnswer->m_ServiceDomain); - DEBUG_OUTPUT.printf_P(PSTR(" host domain and port %s\n"), (bResult ? "OK" : "FAILURE")); - printedInfo = true; - ); - } - else { - // Timed out! -> Delete - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: Will remove SRV answer for ")); - _printRRDomain(pSQAnswer->m_ServiceDomain); - DEBUG_OUTPUT.printf_P(PSTR(" host domain and port\n")); - printedInfo = true; - ); - // Delete - pSQAnswer->m_HostDomain.clear(); - pSQAnswer->releaseHostDomain(); - pSQAnswer->m_u16Port = 0; - pSQAnswer->m_TTLHostDomainAndPort.set(0); - uint32_t u32ContentFlags = ServiceQueryAnswerType_HostDomainAndPort; - // As the host domain is the base for the IP4- and IP6Address, remove these too - #ifdef MDNS_IP4_SUPPORT - pSQAnswer->releaseIP4Addresses(); - u32ContentFlags |= ServiceQueryAnswerType_IP4Address; - #endif - #ifdef MDNS_IP6_SUPPORT - pSQAnswer->releaseIP6Addresses(); - u32ContentFlags |= ServiceQueryAnswerType_IP6Address; - #endif - - // Remove content flags for deleted answer parts - pSQAnswer->m_u32ContentFlags &= ~u32ContentFlags; - if (pServiceQuery->m_fnCallback) { - MDNSServiceInfo serviceInfo(*this, (hMDNSServiceQuery)pServiceQuery, pServiceQuery->indexOfAnswer(pSQAnswer)); - pServiceQuery->m_fnCallback(serviceInfo,static_cast(u32ContentFlags), false); - } - } - } // HostDomainAndPort flagged - - // Txts (from TXT) - if ((bResult) && - (pSQAnswer->m_TTLTxts.flagged())) { - - if (!pSQAnswer->m_TTLTxts.finalTimeoutLevel()) { - - bResult = ((_sendMDNSQuery(pSQAnswer->m_ServiceDomain, DNS_RRTYPE_TXT)) && - (pSQAnswer->m_TTLTxts.restart())); - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: TXT update scheduled for ")); - _printRRDomain(pSQAnswer->m_ServiceDomain); - DEBUG_OUTPUT.printf_P(PSTR(" TXTs %s\n"), (bResult ? "OK" : "FAILURE")); - printedInfo = true; - ); - } - else { - // Timed out! -> Delete - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: Will remove TXT answer for ")); - _printRRDomain(pSQAnswer->m_ServiceDomain); - DEBUG_OUTPUT.printf_P(PSTR(" TXTs\n")); - printedInfo = true; - ); - // Delete - pSQAnswer->m_Txts.clear(); - pSQAnswer->m_TTLTxts.set(0); - - // Remove content flags for deleted answer parts - pSQAnswer->m_u32ContentFlags &= ~ServiceQueryAnswerType_Txts; - - if (pServiceQuery->m_fnCallback) { - MDNSServiceInfo serviceInfo(*this, (hMDNSServiceQuery)pServiceQuery, pServiceQuery->indexOfAnswer(pSQAnswer)); - pServiceQuery->m_fnCallback(serviceInfo, static_cast(ServiceQueryAnswerType_Txts), false); - } - } - } // TXTs flagged - - // 3. level answers -#ifdef MDNS_IP4_SUPPORT - // IP4Address (from A) - stcMDNSServiceQuery::stcAnswer::stcIP4Address* pIP4Address = pSQAnswer->m_pIP4Addresses; - bool bAUpdateQuerySent = false; - while ((pIP4Address) && - (bResult)) { - - stcMDNSServiceQuery::stcAnswer::stcIP4Address* pNextIP4Address = pIP4Address->m_pNext; // Get 'next' early, as 'current' may be deleted at the end... - - if (pIP4Address->m_TTL.flagged()) { - - if (!pIP4Address->m_TTL.finalTimeoutLevel()) { // Needs update - - if ((bAUpdateQuerySent) || - ((bResult = _sendMDNSQuery(pSQAnswer->m_HostDomain, DNS_RRTYPE_A)))) { - - pIP4Address->m_TTL.restart(); - bAUpdateQuerySent = true; - - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: IP4 update scheduled for ")); - _printRRDomain(pSQAnswer->m_ServiceDomain); - DEBUG_OUTPUT.printf_P(PSTR(" IP4 address (%s)\n"), (pIP4Address->m_IPAddress.toString().c_str())); - printedInfo = true; - ); - } - } - else { - // Timed out! -> Delete - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: Will remove IP4 answer for ")); - _printRRDomain(pSQAnswer->m_ServiceDomain); - DEBUG_OUTPUT.printf_P(PSTR(" IP4 address\n")); - printedInfo = true; - ); - pSQAnswer->removeIP4Address(pIP4Address); - if (!pSQAnswer->m_pIP4Addresses) { // NO IP4 address left -> remove content flag - pSQAnswer->m_u32ContentFlags &= ~ServiceQueryAnswerType_IP4Address; - } - // Notify client - if (pServiceQuery->m_fnCallback) { - MDNSServiceInfo serviceInfo(*this, (hMDNSServiceQuery)pServiceQuery, pServiceQuery->indexOfAnswer(pSQAnswer)); - pServiceQuery->m_fnCallback(serviceInfo, static_cast(ServiceQueryAnswerType_IP4Address), false); - } - } - } // IP4 flagged - - pIP4Address = pNextIP4Address; // Next - } // while -#endif -#ifdef MDNS_IP6_SUPPORT - // IP6Address (from AAAA) - stcMDNSServiceQuery::stcAnswer::stcIP6Address* pIP6Address = pSQAnswer->m_pIP6Addresses; - bool bAAAAUpdateQuerySent = false; - while ((pIP6Address) && - (bResult)) { - - stcMDNSServiceQuery::stcAnswer::stcIP6Address* pNextIP6Address = pIP6Address->m_pNext; // Get 'next' early, as 'current' may be deleted at the end... - - if (pIP6Address->m_TTL.flagged()) { - - if (!pIP6Address->m_TTL.finalTimeoutLevel()) { // Needs update - - if ((bAAAAUpdateQuerySent) || - ((bResult = _sendMDNSQuery(pSQAnswer->m_HostDomain, DNS_RRTYPE_AAAA)))) { - - pIP6Address->m_TTL.restart(); - bAAAAUpdateQuerySent = true; - - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: IP6 update scheduled for ")); - _printRRDomain(pSQAnswer->m_ServiceDomain); - DEBUG_OUTPUT.printf_P(PSTR(" IP6 address (%s)\n"), (pIP6Address->m_IPAddress.toString().c_str())); - printedInfo = true; - ); - } - } - else { - // Timed out! -> Delete - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: Will remove answer for ")); - _printRRDomain(pSQAnswer->m_ServiceDomain); - DEBUG_OUTPUT.printf_P(PSTR(" IP6Address\n")); - printedInfo = true; - ); - pSQAnswer->removeIP6Address(pIP6Address); - if (!pSQAnswer->m_pIP6Addresses) { // NO IP6 address left -> remove content flag - pSQAnswer->m_u32ContentFlags &= ~ServiceQueryAnswerType_IP6Address; - } - // Notify client - if (pServiceQuery->m_fnCallback) { - pServiceQuery->m_fnCallback(this, (hMDNSServiceQuery)pServiceQuery, pServiceQuery->indexOfAnswer(pSQAnswer), ServiceQueryAnswerType_IP6Address, false, pServiceQuery->m_pUserdata); - } - } - } // IP6 flagged - - pIP6Address = pNextIP6Address; // Next - } // while -#endif - pSQAnswer = pNextSQAnswer; - } - } - } - DEBUG_EX_INFO( - if (printedInfo) { - DEBUG_OUTPUT.printf_P(PSTR("\n")); - } - ); - DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: FAILED!\n")); }); - return bResult; -} - - -/* - * MDNSResponder::_replyMaskForHost - * - * Determines the relavant host answers for the given question. - * - A question for the hostname (eg. esp8266.local) will result in an A/AAAA (eg. 192.168.2.129) reply. - * - A question for the reverse IP address (eg. 192-168.2.120.inarpa.arpa) will result in an PTR_IP4 (eg. esp8266.local) reply. - * - * In addition, a full name match (question domain == host domain) is marked. - */ -uint8_t MDNSResponder::_replyMaskForHost(const MDNSResponder::stcMDNS_RRHeader& p_RRHeader, - bool* p_pbFullNameMatch /*= 0*/) const { - //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _replyMaskForHost\n"));); - - uint8_t u8ReplyMask = 0; - (p_pbFullNameMatch ? *p_pbFullNameMatch = false : 0); - - if ((DNS_RRCLASS_IN == p_RRHeader.m_Attributes.m_u16Class) || - (DNS_RRCLASS_ANY == p_RRHeader.m_Attributes.m_u16Class)) { - - if ((DNS_RRTYPE_PTR == p_RRHeader.m_Attributes.m_u16Type) || - (DNS_RRTYPE_ANY == p_RRHeader.m_Attributes.m_u16Type)) { - // PTR request -#ifdef MDNS_IP4_SUPPORT - stcMDNS_RRDomain reverseIP4Domain; - if ((_buildDomainForReverseIP4(_getResponseMulticastInterface(), reverseIP4Domain)) && - (p_RRHeader.m_Domain == reverseIP4Domain)) { - // Reverse domain match - u8ReplyMask |= ContentFlag_PTR_IP4; - } -#endif -#ifdef MDNS_IP6_SUPPORT - // TODO -#endif - } // Address qeuest - - stcMDNS_RRDomain hostDomain; - if ((_buildDomainForHost(m_pcHostname, hostDomain)) && - (p_RRHeader.m_Domain == hostDomain)) { // Host domain match - - (p_pbFullNameMatch ? (*p_pbFullNameMatch = true) : (0)); - -#ifdef MDNS_IP4_SUPPORT - if ((DNS_RRTYPE_A == p_RRHeader.m_Attributes.m_u16Type) || - (DNS_RRTYPE_ANY == p_RRHeader.m_Attributes.m_u16Type)) { - // IP4 address request - u8ReplyMask |= ContentFlag_A; - } -#endif -#ifdef MDNS_IP6_SUPPORT - if ((DNS_RRTYPE_AAAA == p_RRHeader.m_Attributes.m_u16Type) || - (DNS_RRTYPE_ANY == p_RRHeader.m_Attributes.m_u16Type)) { - // IP6 address request - u8ReplyMask |= ContentFlag_AAAA; - } -#endif - } - } - else { - //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _replyMaskForHost: INVALID RR-class (0x%04X)!\n"), p_RRHeader.m_Attributes.m_u16Class);); - } - DEBUG_EX_INFO(if (u8ReplyMask) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _replyMaskForHost: 0x%X\n"), u8ReplyMask); } ); - return u8ReplyMask; -} - -/* - * MDNSResponder::_replyMaskForService - * - * Determines the relevant service answers for the given question - * - A PTR dns-sd service enum question (_services.dns-sd._udp.local) will result into an PTR_TYPE (eg. _http._tcp.local) answer - * - A PTR service type question (eg. _http._tcp.local) will result into an PTR_NAME (eg. MyESP._http._tcp.local) answer - * - A PTR service name question (eg. MyESP._http._tcp.local) will result into an PTR_NAME (eg. MyESP._http._tcp.local) answer - * - A SRV service name question (eg. MyESP._http._tcp.local) will result into an SRV (eg. 5000 MyESP.local) answer - * - A TXT service name question (eg. MyESP._http._tcp.local) will result into an TXT (eg. c#=1) answer - * - * In addition, a full name match (question domain == service instance domain) is marked. - */ -uint8_t MDNSResponder::_replyMaskForService(const MDNSResponder::stcMDNS_RRHeader& p_RRHeader, - const MDNSResponder::stcMDNSService& p_Service, - bool* p_pbFullNameMatch /*= 0*/) const { - - uint8_t u8ReplyMask = 0; - (p_pbFullNameMatch ? *p_pbFullNameMatch = false : 0); - - if ((DNS_RRCLASS_IN == p_RRHeader.m_Attributes.m_u16Class) || - (DNS_RRCLASS_ANY == p_RRHeader.m_Attributes.m_u16Class)) { - - stcMDNS_RRDomain DNSSDDomain; - if ((_buildDomainForDNSSD(DNSSDDomain)) && // _services._dns-sd._udp.local - (p_RRHeader.m_Domain == DNSSDDomain) && - ((DNS_RRTYPE_PTR == p_RRHeader.m_Attributes.m_u16Type) || - (DNS_RRTYPE_ANY == p_RRHeader.m_Attributes.m_u16Type))) { - // Common service info requested - u8ReplyMask |= ContentFlag_PTR_TYPE; - } - - stcMDNS_RRDomain serviceDomain; - if ((_buildDomainForService(p_Service, false, serviceDomain)) && // eg. _http._tcp.local - (p_RRHeader.m_Domain == serviceDomain) && - ((DNS_RRTYPE_PTR == p_RRHeader.m_Attributes.m_u16Type) || - (DNS_RRTYPE_ANY == p_RRHeader.m_Attributes.m_u16Type))) { - // Special service info requested - u8ReplyMask |= ContentFlag_PTR_NAME; - } - - if ((_buildDomainForService(p_Service, true, serviceDomain)) && // eg. MyESP._http._tcp.local - (p_RRHeader.m_Domain == serviceDomain)) { - - (p_pbFullNameMatch ? (*p_pbFullNameMatch = true) : (0)); - - if ((DNS_RRTYPE_SRV == p_RRHeader.m_Attributes.m_u16Type) || - (DNS_RRTYPE_ANY == p_RRHeader.m_Attributes.m_u16Type)) { - // Instance info SRV requested - u8ReplyMask |= ContentFlag_SRV; - } - if ((DNS_RRTYPE_TXT == p_RRHeader.m_Attributes.m_u16Type) || - (DNS_RRTYPE_ANY == p_RRHeader.m_Attributes.m_u16Type)) { - // Instance info TXT requested - u8ReplyMask |= ContentFlag_TXT; - } - } - } - else { - //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _replyMaskForService: INVALID RR-class (0x%04X)!\n"), p_RRHeader.m_Attributes.m_u16Class);); - } - DEBUG_EX_INFO(if (u8ReplyMask) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _replyMaskForService(%s.%s.%s): 0x%X\n"), p_Service.m_pcName, p_Service.m_pcService, p_Service.m_pcProtocol, u8ReplyMask); } ); - return u8ReplyMask; -} - -} // namespace MDNSImplementation - -} // namespace esp8266 + } + return bResult; +} + +/* + MDNSResponder::_restart +*/ +bool MDNSResponder::_restart(void) +{ + + return ((m_netif != nullptr) && + (m_netif->flags & NETIF_FLAG_UP) && // network interface is up and running + (_resetProbeStatus(true)) && // Stop and restart probing + (_allocUDPContext())); // Restart UDP +} + + +/** + RECEIVING +*/ + +/* + MDNSResponder::_parseMessage +*/ +bool MDNSResponder::_parseMessage(void) +{ + DEBUG_EX_INFO( + unsigned long ulStartTime = millis(); + unsigned uStartMemory = ESP.getFreeHeap(); + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseMessage (Time: %lu ms, heap: %u bytes, from %s(%u), to %s(%u))\n"), ulStartTime, uStartMemory, + IPAddress(m_pUDPContext->getRemoteAddress()).toString().c_str(), m_pUDPContext->getRemotePort(), + IPAddress(m_pUDPContext->getDestAddress()).toString().c_str(), m_pUDPContext->getLocalPort()); + ); + //DEBUG_EX_INFO(_udpDump();); + + bool bResult = false; + + stcMDNS_MsgHeader header; + if (_readMDNSMsgHeader(header)) + { + if (0 == header.m_4bOpcode) // A standard query + { + if (header.m_1bQR) // Received a response -> answers to a query + { + //DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseMessage: Reading answers: ID:%u, Q:%u, A:%u, NS:%u, AR:%u\n"), header.m_u16ID, header.m_u16QDCount, header.m_u16ANCount, header.m_u16NSCount, header.m_u16ARCount);); + bResult = _parseResponse(header); + } + else // Received a query (Questions) + { + //DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseMessage: Reading query: ID:%u, Q:%u, A:%u, NS:%u, AR:%u\n"), header.m_u16ID, header.m_u16QDCount, header.m_u16ANCount, header.m_u16NSCount, header.m_u16ARCount);); + bResult = _parseQuery(header); + } + } + else + { + DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseMessage: Received UNEXPECTED opcode:%u. Ignoring message!\n"), header.m_4bOpcode);); + m_pUDPContext->flush(); + } + } + else + { + DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseMessage: FAILED to read header\n"));); + m_pUDPContext->flush(); + } + DEBUG_EX_INFO( + unsigned uFreeHeap = ESP.getFreeHeap(); + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseMessage: Done (%s after %lu ms, ate %i bytes, remaining %u)\n\n"), (bResult ? "Succeeded" : "FAILED"), (millis() - ulStartTime), (uStartMemory - uFreeHeap), uFreeHeap); + ); + return bResult; +} + +/* + MDNSResponder::_parseQuery + + Queries are of interest in two cases: + 1. allow for tiebreaking while probing in the case of a race condition between two instances probing for + the same name at the same time + 2. provide answers to questions for our host domain or any presented service + + When reading the questions, a set of (planned) responses is created, eg. a reverse PTR question for the host domain + gets an A (IP address) response, a PTR question for the _services._dns-sd domain gets a PTR (type) response for any + registered service, ... + + As any mDNS responder should be able to handle 'legacy' queries (from DNS clients), this case is handled here also. + Legacy queries have got only one (unicast) question and are directed to the local DNS port (not the multicast port). + + 1. +*/ +bool MDNSResponder::_parseQuery(const MDNSResponder::stcMDNS_MsgHeader& p_MsgHeader) +{ + + bool bResult = true; + + stcMDNSSendParameter sendParameter; + uint8_t u8HostOrServiceReplies = 0; + for (uint16_t qd = 0; ((bResult) && (qd < p_MsgHeader.m_u16QDCount)); ++qd) + { + + stcMDNS_RRQuestion questionRR; + if ((bResult = _readRRQuestion(questionRR))) + { + // Define host replies, BUT only answer queries after probing is done + u8HostOrServiceReplies = + sendParameter.m_u8HostReplyMask |= (((m_bPassivModeEnabled) || + (ProbingStatus_Done == m_HostProbeInformation.m_ProbingStatus)) + ? _replyMaskForHost(questionRR.m_Header, 0) + : 0); + DEBUG_EX_INFO(if (u8HostOrServiceReplies) + { + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Host reply needed 0x%X\n"), u8HostOrServiceReplies); + }); + + // Check tiebreak need for host domain + if (ProbingStatus_InProgress == m_HostProbeInformation.m_ProbingStatus) + { + bool bFullNameMatch = false; + if ((_replyMaskForHost(questionRR.m_Header, &bFullNameMatch)) && + (bFullNameMatch)) + { + // We're in 'probing' state and someone is asking for our host domain: this might be + // a race-condition: Two host with the same domain names try simutanously to probe their domains + // See: RFC 6762, 8.2 (Tiebraking) + // However, we're using a max. reduced approach for tiebreaking here: The higher IP-address wins! + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Possible race-condition for host domain detected while probing.\n"));); + + m_HostProbeInformation.m_bTiebreakNeeded = true; + } + } + + // Define service replies + for (stcMDNSService* pService = m_pServices; pService; pService = pService->m_pNext) + { + // Define service replies, BUT only answer queries after probing is done + uint8_t u8ReplyMaskForQuestion = (((m_bPassivModeEnabled) || + (ProbingStatus_Done == pService->m_ProbeInformation.m_ProbingStatus)) + ? _replyMaskForService(questionRR.m_Header, *pService, 0) + : 0); + u8HostOrServiceReplies |= (pService->m_u8ReplyMask |= u8ReplyMaskForQuestion); + DEBUG_EX_INFO(if (u8ReplyMaskForQuestion) + { + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Service reply needed for (%s.%s.%s): 0x%X (%s)\n"), (pService->m_pcName ? : m_pcHostname), pService->m_pcService, pService->m_pcProtocol, u8ReplyMaskForQuestion, IPAddress(m_pUDPContext->getRemoteAddress()).toString().c_str()); + }); + + // Check tiebreak need for service domain + if (ProbingStatus_InProgress == pService->m_ProbeInformation.m_ProbingStatus) + { + bool bFullNameMatch = false; + if ((_replyMaskForService(questionRR.m_Header, *pService, &bFullNameMatch)) && + (bFullNameMatch)) + { + // We're in 'probing' state and someone is asking for this service domain: this might be + // a race-condition: Two services with the same domain names try simutanously to probe their domains + // See: RFC 6762, 8.2 (Tiebraking) + // However, we're using a max. reduced approach for tiebreaking here: The 'higher' SRV host wins! + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Possible race-condition for service domain %s.%s.%s detected while probing.\n"), (pService->m_pcName ? : m_pcHostname), pService->m_pcService, pService->m_pcProtocol);); + + pService->m_ProbeInformation.m_bTiebreakNeeded = true; + } + } + } + + // Handle unicast and legacy specialities + // If only one question asks for unicast reply, the whole reply packet is send unicast + if (((DNS_MQUERY_PORT != m_pUDPContext->getRemotePort()) || // Unicast (maybe legacy) query OR + (questionRR.m_bUnicast)) && // Expressivly unicast query + (!sendParameter.m_bUnicast)) + { + + sendParameter.m_bUnicast = true; + sendParameter.m_bCacheFlush = false; + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Unicast response for %s!\n"), IPAddress(m_pUDPContext->getRemoteAddress()).toString().c_str());); + + if ((DNS_MQUERY_PORT != m_pUDPContext->getRemotePort()) && // Unicast (maybe legacy) query AND + (1 == p_MsgHeader.m_u16QDCount) && // Only one question AND + ((sendParameter.m_u8HostReplyMask) || // Host replies OR + (u8HostOrServiceReplies))) // Host or service replies available + { + // We're a match for this legacy query, BUT + // make sure, that the query comes from a local host + ip_info IPInfo_Local; + ip_info IPInfo_Remote; + if (((IPInfo_Remote.ip.addr = m_pUDPContext->getRemoteAddress())) && + (((wifi_get_ip_info(SOFTAP_IF, &IPInfo_Local)) && + (ip4_addr_netcmp(&IPInfo_Remote.ip, &IPInfo_Local.ip, &IPInfo_Local.netmask))) || // Remote IP in SOFTAP's subnet OR + ((wifi_get_ip_info(STATION_IF, &IPInfo_Local)) && + (ip4_addr_netcmp(&IPInfo_Remote.ip, &IPInfo_Local.ip, &IPInfo_Local.netmask))))) // Remote IP in STATION's subnet + { + + DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Legacy query from local host %s, id %u!\n"), IPAddress(m_pUDPContext->getRemoteAddress()).toString().c_str(), p_MsgHeader.m_u16ID);); + + sendParameter.m_u16ID = p_MsgHeader.m_u16ID; + sendParameter.m_bLegacyQuery = true; + sendParameter.m_pQuestions = new stcMDNS_RRQuestion; + if ((bResult = (0 != sendParameter.m_pQuestions))) + { + sendParameter.m_pQuestions->m_Header.m_Domain = questionRR.m_Header.m_Domain; + sendParameter.m_pQuestions->m_Header.m_Attributes.m_u16Type = questionRR.m_Header.m_Attributes.m_u16Type; + sendParameter.m_pQuestions->m_Header.m_Attributes.m_u16Class = questionRR.m_Header.m_Attributes.m_u16Class; + } + else + { + DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: FAILED to add legacy question!\n"));); + } + } + else + { + DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Legacy query from NON-LOCAL host!\n"));); + bResult = false; + } + } + } + } + else + { + DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: FAILED to read question!\n"));); + } + } // for questions + + //DEBUG_EX_INFO(if (u8HostOrServiceReplies) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Reply needed: %u (%s: %s->%s)\n"), u8HostOrServiceReplies, clsTimeSyncer::timestr(), IPAddress(m_pUDPContext->getRemoteAddress()).toString().c_str(), IPAddress(m_pUDPContext->getDestAddress()).toString().c_str()); } ); + + // Handle known answers + uint32_t u32Answers = (p_MsgHeader.m_u16ANCount + p_MsgHeader.m_u16NSCount + p_MsgHeader.m_u16ARCount); + DEBUG_EX_INFO(if ((u8HostOrServiceReplies) && (u32Answers)) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Known answers(%u):\n"), u32Answers); + }); + + for (uint32_t an = 0; ((bResult) && (an < u32Answers)); ++an) + { + stcMDNS_RRAnswer* pKnownRRAnswer = 0; + if (((bResult = _readRRAnswer(pKnownRRAnswer))) && + (pKnownRRAnswer)) + { + + if ((DNS_RRTYPE_ANY != pKnownRRAnswer->m_Header.m_Attributes.m_u16Type) && // No ANY type answer + (DNS_RRCLASS_ANY != pKnownRRAnswer->m_Header.m_Attributes.m_u16Class)) // No ANY class answer + { + + // Find match between planned answer (sendParameter.m_u8HostReplyMask) and this 'known answer' + uint8_t u8HostMatchMask = (sendParameter.m_u8HostReplyMask & _replyMaskForHost(pKnownRRAnswer->m_Header)); + if ((u8HostMatchMask) && // The RR in the known answer matches an RR we are planning to send, AND + ((MDNS_HOST_TTL / 2) <= pKnownRRAnswer->m_u32TTL)) // The TTL of the known answer is longer than half of the new host TTL (120s) + { + + // Compare contents + if (AnswerType_PTR == pKnownRRAnswer->answerType()) + { + stcMDNS_RRDomain hostDomain; + if ((_buildDomainForHost(m_pcHostname, hostDomain)) && + (((stcMDNS_RRAnswerPTR*)pKnownRRAnswer)->m_PTRDomain == hostDomain)) + { + // Host domain match +#ifdef MDNS_IP4_SUPPORT + if (u8HostMatchMask & ContentFlag_PTR_IP4) + { + // IP4 PTR was asked for, but is already known -> skipping + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: IP4 PTR already known... skipping!\n"));); + sendParameter.m_u8HostReplyMask &= ~ContentFlag_PTR_IP4; + } +#endif +#ifdef MDNS_IP6_SUPPORT + if (u8HostMatchMask & ContentFlag_PTR_IP6) + { + // IP6 PTR was asked for, but is already known -> skipping + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: IP6 PTR already known... skipping!\n"));); + sendParameter.m_u8HostReplyMask &= ~ContentFlag_PTR_IP6; + } +#endif + } + } + else if (u8HostMatchMask & ContentFlag_A) + { + // IP4 address was asked for +#ifdef MDNS_IP4_SUPPORT + if ((AnswerType_A == pKnownRRAnswer->answerType()) && + (((stcMDNS_RRAnswerA*)pKnownRRAnswer)->m_IPAddress == _getResponseMulticastInterface())) + { + + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: IP4 address already known... skipping!\n"));); + sendParameter.m_u8HostReplyMask &= ~ContentFlag_A; + } // else: RData NOT IP4 length !! +#endif + } + else if (u8HostMatchMask & ContentFlag_AAAA) + { + // IP6 address was asked for +#ifdef MDNS_IP6_SUPPORT + if ((AnswerType_AAAA == pAnswerRR->answerType()) && + (((stcMDNS_RRAnswerAAAA*)pAnswerRR)->m_IPAddress == _getResponseMulticastInterface())) + { + + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: IP6 address already known... skipping!\n"));); + sendParameter.m_u8HostReplyMask &= ~ContentFlag_AAAA; + } // else: RData NOT IP6 length !! +#endif + } + } // Host match /*and TTL*/ + + // + // Check host tiebreak possibility + if (m_HostProbeInformation.m_bTiebreakNeeded) + { + stcMDNS_RRDomain hostDomain; + if ((_buildDomainForHost(m_pcHostname, hostDomain)) && + (pKnownRRAnswer->m_Header.m_Domain == hostDomain)) + { + // Host domain match +#ifdef MDNS_IP4_SUPPORT + if (AnswerType_A == pKnownRRAnswer->answerType()) + { + IPAddress localIPAddress(_getResponseMulticastInterface()); + if (((stcMDNS_RRAnswerA*)pKnownRRAnswer)->m_IPAddress == localIPAddress) + { + // SAME IP address -> We've received an old message from ourselfs (same IP) + DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Tiebreak (IP4) WON (was an old message)!\n"));); + m_HostProbeInformation.m_bTiebreakNeeded = false; + } + else + { + if ((uint32_t)(((stcMDNS_RRAnswerA*)pKnownRRAnswer)->m_IPAddress) > (uint32_t)localIPAddress) // The OTHER IP is 'higher' -> LOST + { + // LOST tiebreak + DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Tiebreak (IP4) LOST (lower)!\n"));); + _cancelProbingForHost(); + m_HostProbeInformation.m_bTiebreakNeeded = false; + } + else // WON tiebreak + { + //TiebreakState = TiebreakState_Won; // We received an 'old' message from ourselfs -> Just ignore + DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Tiebreak (IP4) WON (higher IP)!\n"));); + m_HostProbeInformation.m_bTiebreakNeeded = false; + } + } + } +#endif +#ifdef MDNS_IP6_SUPPORT + if (AnswerType_AAAA == pAnswerRR->answerType()) + { + // TODO + } +#endif + } + } // Host tiebreak possibility + + // Check service answers + for (stcMDNSService* pService = m_pServices; pService; pService = pService->m_pNext) + { + + uint8_t u8ServiceMatchMask = (pService->m_u8ReplyMask & _replyMaskForService(pKnownRRAnswer->m_Header, *pService)); + + if ((u8ServiceMatchMask) && // The RR in the known answer matches an RR we are planning to send, AND + ((MDNS_SERVICE_TTL / 2) <= pKnownRRAnswer->m_u32TTL)) // The TTL of the known answer is longer than half of the new service TTL (4500s) + { + + if (AnswerType_PTR == pKnownRRAnswer->answerType()) + { + stcMDNS_RRDomain serviceDomain; + if ((u8ServiceMatchMask & ContentFlag_PTR_TYPE) && + (_buildDomainForService(*pService, false, serviceDomain)) && + (serviceDomain == ((stcMDNS_RRAnswerPTR*)pKnownRRAnswer)->m_PTRDomain)) + { + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Service type PTR already known... skipping!\n"));); + pService->m_u8ReplyMask &= ~ContentFlag_PTR_TYPE; + } + if ((u8ServiceMatchMask & ContentFlag_PTR_NAME) && + (_buildDomainForService(*pService, true, serviceDomain)) && + (serviceDomain == ((stcMDNS_RRAnswerPTR*)pKnownRRAnswer)->m_PTRDomain)) + { + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Service name PTR already known... skipping!\n"));); + pService->m_u8ReplyMask &= ~ContentFlag_PTR_NAME; + } + } + else if (u8ServiceMatchMask & ContentFlag_SRV) + { + DEBUG_EX_ERR(if (AnswerType_SRV != pKnownRRAnswer->answerType()) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: ERROR! INVALID answer type (SRV)!\n"));); + stcMDNS_RRDomain hostDomain; + if ((_buildDomainForHost(m_pcHostname, hostDomain)) && + (hostDomain == ((stcMDNS_RRAnswerSRV*)pKnownRRAnswer)->m_SRVDomain)) // Host domain match + { + + if ((MDNS_SRV_PRIORITY == ((stcMDNS_RRAnswerSRV*)pKnownRRAnswer)->m_u16Priority) && + (MDNS_SRV_WEIGHT == ((stcMDNS_RRAnswerSRV*)pKnownRRAnswer)->m_u16Weight) && + (pService->m_u16Port == ((stcMDNS_RRAnswerSRV*)pKnownRRAnswer)->m_u16Port)) + { + + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Service SRV answer already known... skipping!\n"));); + pService->m_u8ReplyMask &= ~ContentFlag_SRV; + } // else: Small differences -> send update message + } + } + else if (u8ServiceMatchMask & ContentFlag_TXT) + { + DEBUG_EX_ERR(if (AnswerType_TXT != pKnownRRAnswer->answerType()) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: ERROR! INVALID answer type (TXT)!\n"));); + _collectServiceTxts(*pService); + if (pService->m_Txts == ((stcMDNS_RRAnswerTXT*)pKnownRRAnswer)->m_Txts) + { + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Service TXT answer already known... skipping!\n"));); + pService->m_u8ReplyMask &= ~ContentFlag_TXT; + } + _releaseTempServiceTxts(*pService); + } + } // Service match and enough TTL + + // + // Check service tiebreak possibility + if (pService->m_ProbeInformation.m_bTiebreakNeeded) + { + stcMDNS_RRDomain serviceDomain; + if ((_buildDomainForService(*pService, true, serviceDomain)) && + (pKnownRRAnswer->m_Header.m_Domain == serviceDomain)) + { + // Service domain match + if (AnswerType_SRV == pKnownRRAnswer->answerType()) + { + stcMDNS_RRDomain hostDomain; + if ((_buildDomainForHost(m_pcHostname, hostDomain)) && + (hostDomain == ((stcMDNS_RRAnswerSRV*)pKnownRRAnswer)->m_SRVDomain)) // Host domain match + { + + // We've received an old message from ourselfs (same SRV) + DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Tiebreak (SRV) won (was an old message)!\n"));); + pService->m_ProbeInformation.m_bTiebreakNeeded = false; + } + else + { + if (((stcMDNS_RRAnswerSRV*)pKnownRRAnswer)->m_SRVDomain > hostDomain) // The OTHER domain is 'higher' -> LOST + { + // LOST tiebreak + DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Tiebreak (SRV) LOST (lower)!\n"));); + _cancelProbingForService(*pService); + pService->m_ProbeInformation.m_bTiebreakNeeded = false; + } + else // WON tiebreak + { + //TiebreakState = TiebreakState_Won; // We received an 'old' message from ourselfs -> Just ignore + DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Tiebreak (SRV) won (higher)!\n"));); + pService->m_ProbeInformation.m_bTiebreakNeeded = false; + } + } + } + } + } // service tiebreak possibility + } // for services + } // ANY answers + } + else + { + DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: FAILED to read known answer!\n"));); + } + + if (pKnownRRAnswer) + { + delete pKnownRRAnswer; + pKnownRRAnswer = 0; + } + } // for answers + + if (bResult) + { + // Check, if a reply is needed + uint8_t u8ReplyNeeded = sendParameter.m_u8HostReplyMask; + for (stcMDNSService* pService = m_pServices; pService; pService = pService->m_pNext) + { + u8ReplyNeeded |= pService->m_u8ReplyMask; + } + + if (u8ReplyNeeded) + { + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Sending answer(0x%X)...\n"), u8ReplyNeeded);); + + sendParameter.m_bResponse = true; + sendParameter.m_bAuthorative = true; + + bResult = _sendMDNSMessage(sendParameter); + } + DEBUG_EX_INFO( + else + { + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: No reply needed\n")); + } + ); + } + else + { + DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Something FAILED!\n"));); + m_pUDPContext->flush(); + } + + // + // Check and reset tiebreak-states + if (m_HostProbeInformation.m_bTiebreakNeeded) + { + DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: UNSOLVED tiebreak-need for host domain!\n"));); + m_HostProbeInformation.m_bTiebreakNeeded = false; + } + for (stcMDNSService* pService = m_pServices; pService; pService = pService->m_pNext) + { + if (pService->m_ProbeInformation.m_bTiebreakNeeded) + { + DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: UNSOLVED tiebreak-need for service domain (%s.%s.%s)\n"), (pService->m_pcName ? : m_pcHostname), pService->m_pcService, pService->m_pcProtocol);); + pService->m_ProbeInformation.m_bTiebreakNeeded = false; + } + } + DEBUG_EX_ERR(if (!bResult) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: FAILED!\n")); + }); + return bResult; +} + +/* + MDNSResponder::_parseResponse + + Responses are of interest in two cases: + 1. find domain name conflicts while probing + 2. get answers to service queries + + In both cases any included questions are ignored + + 1. If any answer has a domain name similar to one of the domain names we're planning to use (and are probing for), + then we've got a 'probing conflict'. The conflict has to be solved on our side of the conflict (eg. by + setting a new hostname and restart probing). The callback 'm_fnProbeResultCallback' is called with + 'p_bProbeResult=false' in this case. + + 2. Service queries like '_http._tcp.local' will (if available) produce PTR, SRV, TXT and A/AAAA answers. + All stored answers are pivoted by the service instance name (from the PTR record). Other answer parts, + like host domain or IP address are than attached to this element. + Any answer part carries a TTL, this is also stored (incl. the reception time); if the TTL is '0' the + answer (part) is withdrawn by the sender and should be removed from any cache. RFC 6762, 10.1 proposes to + set the caches TTL-value to 1 second in such a case and to delete the item only, if no update has + has taken place in this second. + Answer parts may arrive in 'unsorted' order, so they are grouped into three levels: + Level 1: PRT - names the service instance (and is used as pivot), voids all other parts if is withdrawn or outdates + Level 2: SRV - links the instance name to a host domain and port, voids A/AAAA parts if is withdrawn or outdates + TXT - links the instance name to services TXTs + Level 3: A/AAAA - links the host domain to an IP address +*/ +bool MDNSResponder::_parseResponse(const MDNSResponder::stcMDNS_MsgHeader& p_MsgHeader) +{ + //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseResponse\n"));); + //DEBUG_EX_INFO(_udpDump();); + + bool bResult = false; + + // A response should be the result of a query or a probe + if ((_hasServiceQueriesWaitingForAnswers()) || // Waiting for query answers OR + (_hasProbesWaitingForAnswers())) // Probe responses + { + + DEBUG_EX_INFO( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseResponse: Received a response\n")); + //_udpDump(); + ); + + bResult = true; + // + // Ignore questions here + stcMDNS_RRQuestion dummyRRQ; + for (uint16_t qd = 0; ((bResult) && (qd < p_MsgHeader.m_u16QDCount)); ++qd) + { + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseResponse: Received a response containing a question... ignoring!\n"));); + bResult = _readRRQuestion(dummyRRQ); + } // for queries + + // + // Read and collect answers + stcMDNS_RRAnswer* pCollectedRRAnswers = 0; + uint32_t u32NumberOfAnswerRRs = (p_MsgHeader.m_u16ANCount + p_MsgHeader.m_u16NSCount + p_MsgHeader.m_u16ARCount); + for (uint32_t an = 0; ((bResult) && (an < u32NumberOfAnswerRRs)); ++an) + { + stcMDNS_RRAnswer* pRRAnswer = 0; + if (((bResult = _readRRAnswer(pRRAnswer))) && + (pRRAnswer)) + { + //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseResponse: ADDING answer!\n"));); + pRRAnswer->m_pNext = pCollectedRRAnswers; + pCollectedRRAnswers = pRRAnswer; + } + else + { + DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseResponse: FAILED to read answer!\n"));); + if (pRRAnswer) + { + delete pRRAnswer; + pRRAnswer = 0; + } + bResult = false; + } + } // for answers + + // + // Process answers + if (bResult) + { + bResult = ((!pCollectedRRAnswers) || + (_processAnswers(pCollectedRRAnswers))); + } + else // Some failure while reading answers + { + DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseResponse: FAILED to read answers!\n"));); + m_pUDPContext->flush(); + } + + // Delete collected answers + while (pCollectedRRAnswers) + { + //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseResponse: DELETING answer!\n"));); + stcMDNS_RRAnswer* pNextAnswer = pCollectedRRAnswers->m_pNext; + delete pCollectedRRAnswers; + pCollectedRRAnswers = pNextAnswer; + } + } + else // Received an unexpected response -> ignore + { + /* DEBUG_EX_INFO( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseResponse: Received an unexpected response... ignoring!\nDUMP:\n")); + bool bDumpResult = true; + for (uint16_t qd=0; ((bDumpResult) && (qdflush(); + bResult = true; + } + DEBUG_EX_ERR(if (!bResult) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseResponse: FAILED!\n")); + }); + return bResult; +} + +/* + MDNSResponder::_processAnswers + Host: + A (0x01): eg. esp8266.local A OP TTL 123.456.789.012 + AAAA (01Cx): eg. esp8266.local AAAA OP TTL 1234:5678::90 + PTR (0x0C, IP4): eg. 012.789.456.123.in-addr.arpa PTR OP TTL esp8266.local + PTR (0x0C, IP6): eg. 90.0.0.0.0.0.0.0.0.0.0.0.78.56.34.12.ip6.arpa PTR OP TTL esp8266.local + Service: + PTR (0x0C, srv name): eg. _http._tcp.local PTR OP TTL MyESP._http._tcp.local + PTR (0x0C, srv type): eg. _services._dns-sd._udp.local PTR OP TTL _http._tcp.local + SRV (0x21): eg. MyESP._http._tcp.local SRV OP TTL PRIORITY WEIGHT PORT esp8266.local + TXT (0x10): eg. MyESP._http._tcp.local TXT OP TTL c#=1 + +*/ +bool MDNSResponder::_processAnswers(const MDNSResponder::stcMDNS_RRAnswer* p_pAnswers) +{ + + bool bResult = false; + + if (p_pAnswers) + { + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processAnswers: Processing answers...\n"));); + bResult = true; + + // Answers may arrive in an unexpected order. So we loop our answers as long, as we + // can connect new information to service queries + bool bFoundNewKeyAnswer; + do + { + bFoundNewKeyAnswer = false; + + const stcMDNS_RRAnswer* pRRAnswer = p_pAnswers; + while ((pRRAnswer) && + (bResult)) + { + // 1. level answer (PTR) + if (AnswerType_PTR == pRRAnswer->answerType()) + { + // eg. _http._tcp.local PTR xxxx xx MyESP._http._tcp.local + bResult = _processPTRAnswer((stcMDNS_RRAnswerPTR*)pRRAnswer, bFoundNewKeyAnswer); // May 'enable' new SRV or TXT answers to be linked to queries + } + // 2. level answers + // SRV -> host domain and port + else if (AnswerType_SRV == pRRAnswer->answerType()) + { + // eg. MyESP_http._tcp.local SRV xxxx xx yy zz 5000 esp8266.local + bResult = _processSRVAnswer((stcMDNS_RRAnswerSRV*)pRRAnswer, bFoundNewKeyAnswer); // May 'enable' new A/AAAA answers to be linked to queries + } + // TXT -> Txts + else if (AnswerType_TXT == pRRAnswer->answerType()) + { + // eg. MyESP_http._tcp.local TXT xxxx xx c#=1 + bResult = _processTXTAnswer((stcMDNS_RRAnswerTXT*)pRRAnswer); + } + // 3. level answers +#ifdef MDNS_IP4_SUPPORT + // A -> IP4Address + else if (AnswerType_A == pRRAnswer->answerType()) + { + // eg. esp8266.local A xxxx xx 192.168.2.120 + bResult = _processAAnswer((stcMDNS_RRAnswerA*)pRRAnswer); + } +#endif +#ifdef MDNS_IP6_SUPPORT + // AAAA -> IP6Address + else if (AnswerType_AAAA == pRRAnswer->answerType()) + { + // eg. esp8266.local AAAA xxxx xx 09cf::0c + bResult = _processAAAAAnswer((stcMDNS_RRAnswerAAAA*)pRRAnswer); + } +#endif + + // Finally check for probing conflicts + // Host domain + if ((ProbingStatus_InProgress == m_HostProbeInformation.m_ProbingStatus) && + ((AnswerType_A == pRRAnswer->answerType()) || + (AnswerType_AAAA == pRRAnswer->answerType()))) + { + + stcMDNS_RRDomain hostDomain; + if ((_buildDomainForHost(m_pcHostname, hostDomain)) && + (pRRAnswer->m_Header.m_Domain == hostDomain)) + { + + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processAnswers: Probing CONFLICT found with: %s.local\n"), m_pcHostname);); + _cancelProbingForHost(); + } + } + // Service domains + for (stcMDNSService* pService = m_pServices; pService; pService = pService->m_pNext) + { + if ((ProbingStatus_InProgress == pService->m_ProbeInformation.m_ProbingStatus) && + ((AnswerType_TXT == pRRAnswer->answerType()) || + (AnswerType_SRV == pRRAnswer->answerType()))) + { + + stcMDNS_RRDomain serviceDomain; + if ((_buildDomainForService(*pService, true, serviceDomain)) && + (pRRAnswer->m_Header.m_Domain == serviceDomain)) + { + + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processAnswers: Probing CONFLICT found with: %s.%s.%s\n"), (pService->m_pcName ? : m_pcHostname), pService->m_pcService, pService->m_pcProtocol);); + _cancelProbingForService(*pService); + } + } + } + + pRRAnswer = pRRAnswer->m_pNext; // Next collected answer + } // while (answers) + } while ((bFoundNewKeyAnswer) && + (bResult)); + } // else: No answers provided + DEBUG_EX_ERR(if (!bResult) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processAnswers: FAILED!\n")); + }); + return bResult; +} + +/* + MDNSResponder::_processPTRAnswer +*/ +bool MDNSResponder::_processPTRAnswer(const MDNSResponder::stcMDNS_RRAnswerPTR* p_pPTRAnswer, + bool& p_rbFoundNewKeyAnswer) +{ + + bool bResult = false; + + if ((bResult = (0 != p_pPTRAnswer))) + { + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processPTRAnswer: Processing PTR answers...\n"));); + // eg. _http._tcp.local PTR xxxx xx MyESP._http._tcp.local + // Check pending service queries for eg. '_http._tcp' + + stcMDNSServiceQuery* pServiceQuery = _findNextServiceQueryByServiceType(p_pPTRAnswer->m_Header.m_Domain, 0); + while (pServiceQuery) + { + if (pServiceQuery->m_bAwaitingAnswers) + { + // Find answer for service domain (eg. MyESP._http._tcp.local) + stcMDNSServiceQuery::stcAnswer* pSQAnswer = pServiceQuery->findAnswerForServiceDomain(p_pPTRAnswer->m_PTRDomain); + if (pSQAnswer) // existing answer + { + if (p_pPTRAnswer->m_u32TTL) // Received update message + { + pSQAnswer->m_TTLServiceDomain.set(p_pPTRAnswer->m_u32TTL); // Update TTL tag + DEBUG_EX_INFO( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processPTRAnswer: Updated TTL(%d) for "), (int)p_pPTRAnswer->m_u32TTL); + _printRRDomain(pSQAnswer->m_ServiceDomain); + DEBUG_OUTPUT.printf_P(PSTR("\n")); + ); + } + else // received goodbye-message + { + pSQAnswer->m_TTLServiceDomain.prepareDeletion(); // Prepare answer deletion according to RFC 6762, 10.1 + DEBUG_EX_INFO( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processPTRAnswer: 'Goodbye' received for ")); + _printRRDomain(pSQAnswer->m_ServiceDomain); + DEBUG_OUTPUT.printf_P(PSTR("\n")); + ); + } + } + else if ((p_pPTRAnswer->m_u32TTL) && // Not just a goodbye-message + ((pSQAnswer = new stcMDNSServiceQuery::stcAnswer))) // Not yet included -> add answer + { + pSQAnswer->m_ServiceDomain = p_pPTRAnswer->m_PTRDomain; + pSQAnswer->m_u32ContentFlags |= ServiceQueryAnswerType_ServiceDomain; + pSQAnswer->m_TTLServiceDomain.set(p_pPTRAnswer->m_u32TTL); + pSQAnswer->releaseServiceDomain(); + + bResult = pServiceQuery->addAnswer(pSQAnswer); + p_rbFoundNewKeyAnswer = true; + if (pServiceQuery->m_fnCallback) + { + MDNSServiceInfo serviceInfo(*this, (hMDNSServiceQuery)pServiceQuery, pServiceQuery->indexOfAnswer(pSQAnswer)); + pServiceQuery->m_fnCallback(serviceInfo, static_cast(ServiceQueryAnswerType_ServiceDomain), true); + } + } + } + pServiceQuery = _findNextServiceQueryByServiceType(p_pPTRAnswer->m_Header.m_Domain, pServiceQuery); + } + } // else: No p_pPTRAnswer + DEBUG_EX_ERR(if (!bResult) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processPTRAnswer: FAILED!\n")); + }); + return bResult; +} + +/* + MDNSResponder::_processSRVAnswer +*/ +bool MDNSResponder::_processSRVAnswer(const MDNSResponder::stcMDNS_RRAnswerSRV* p_pSRVAnswer, + bool& p_rbFoundNewKeyAnswer) +{ + + bool bResult = false; + + if ((bResult = (0 != p_pSRVAnswer))) + { + // eg. MyESP._http._tcp.local SRV xxxx xx yy zz 5000 esp8266.local + + stcMDNSServiceQuery* pServiceQuery = m_pServiceQueries; + while (pServiceQuery) + { + stcMDNSServiceQuery::stcAnswer* pSQAnswer = pServiceQuery->findAnswerForServiceDomain(p_pSRVAnswer->m_Header.m_Domain); + if (pSQAnswer) // Answer for this service domain (eg. MyESP._http._tcp.local) available + { + if (p_pSRVAnswer->m_u32TTL) // First or update message (TTL != 0) + { + pSQAnswer->m_TTLHostDomainAndPort.set(p_pSRVAnswer->m_u32TTL); // Update TTL tag + DEBUG_EX_INFO( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processSRVAnswer: Updated TTL(%d) for "), (int)p_pSRVAnswer->m_u32TTL); + _printRRDomain(pSQAnswer->m_ServiceDomain); + DEBUG_OUTPUT.printf_P(PSTR(" host domain and port\n")); + ); + // Host domain & Port + if ((pSQAnswer->m_HostDomain != p_pSRVAnswer->m_SRVDomain) || + (pSQAnswer->m_u16Port != p_pSRVAnswer->m_u16Port)) + { + + pSQAnswer->m_HostDomain = p_pSRVAnswer->m_SRVDomain; + pSQAnswer->releaseHostDomain(); + pSQAnswer->m_u16Port = p_pSRVAnswer->m_u16Port; + pSQAnswer->m_u32ContentFlags |= ServiceQueryAnswerType_HostDomainAndPort; + + p_rbFoundNewKeyAnswer = true; + if (pServiceQuery->m_fnCallback) + { + MDNSServiceInfo serviceInfo(*this, (hMDNSServiceQuery)pServiceQuery, pServiceQuery->indexOfAnswer(pSQAnswer)); + pServiceQuery->m_fnCallback(serviceInfo, static_cast(ServiceQueryAnswerType_HostDomainAndPort), true); + } + } + } + else // Goodby message + { + pSQAnswer->m_TTLHostDomainAndPort.prepareDeletion(); // Prepare answer deletion according to RFC 6762, 10.1 + DEBUG_EX_INFO( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processSRVAnswer: 'Goodbye' received for ")); + _printRRDomain(pSQAnswer->m_ServiceDomain); + DEBUG_OUTPUT.printf_P(PSTR(" host domain and port\n")); + ); + } + } + pServiceQuery = pServiceQuery->m_pNext; + } // while(service query) + } // else: No p_pSRVAnswer + DEBUG_EX_ERR(if (!bResult) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processSRVAnswer: FAILED!\n")); + }); + return bResult; +} + +/* + MDNSResponder::_processTXTAnswer +*/ +bool MDNSResponder::_processTXTAnswer(const MDNSResponder::stcMDNS_RRAnswerTXT* p_pTXTAnswer) +{ + + bool bResult = false; + + if ((bResult = (0 != p_pTXTAnswer))) + { + // eg. MyESP._http._tcp.local TXT xxxx xx c#=1 + + stcMDNSServiceQuery* pServiceQuery = m_pServiceQueries; + while (pServiceQuery) + { + stcMDNSServiceQuery::stcAnswer* pSQAnswer = pServiceQuery->findAnswerForServiceDomain(p_pTXTAnswer->m_Header.m_Domain); + if (pSQAnswer) // Answer for this service domain (eg. MyESP._http._tcp.local) available + { + if (p_pTXTAnswer->m_u32TTL) // First or update message + { + pSQAnswer->m_TTLTxts.set(p_pTXTAnswer->m_u32TTL); // Update TTL tag + DEBUG_EX_INFO( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processTXTAnswer: Updated TTL(%d) for "), (int)p_pTXTAnswer->m_u32TTL); + _printRRDomain(pSQAnswer->m_ServiceDomain); + DEBUG_OUTPUT.printf_P(PSTR(" TXTs\n")); + ); + if (!pSQAnswer->m_Txts.compare(p_pTXTAnswer->m_Txts)) + { + pSQAnswer->m_Txts = p_pTXTAnswer->m_Txts; + pSQAnswer->m_u32ContentFlags |= ServiceQueryAnswerType_Txts; + pSQAnswer->releaseTxts(); + + if (pServiceQuery->m_fnCallback) + { + MDNSServiceInfo serviceInfo(*this, (hMDNSServiceQuery)pServiceQuery, pServiceQuery->indexOfAnswer(pSQAnswer)); + pServiceQuery->m_fnCallback(serviceInfo, static_cast(ServiceQueryAnswerType_Txts), true); + } + } + } + else // Goodby message + { + pSQAnswer->m_TTLTxts.prepareDeletion(); // Prepare answer deletion according to RFC 6762, 10.1 + DEBUG_EX_INFO( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processTXTAnswer: 'Goodbye' received for ")); + _printRRDomain(pSQAnswer->m_ServiceDomain); + DEBUG_OUTPUT.printf_P(PSTR(" TXTs\n")); + ); + } + } + pServiceQuery = pServiceQuery->m_pNext; + } // while(service query) + } // else: No p_pTXTAnswer + DEBUG_EX_ERR(if (!bResult) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processTXTAnswer: FAILED!\n")); + }); + return bResult; +} + +#ifdef MDNS_IP4_SUPPORT +/* + MDNSResponder::_processAAnswer +*/ +bool MDNSResponder::_processAAnswer(const MDNSResponder::stcMDNS_RRAnswerA* p_pAAnswer) +{ + + bool bResult = false; + + if ((bResult = (0 != p_pAAnswer))) + { + // eg. esp8266.local A xxxx xx 192.168.2.120 + + stcMDNSServiceQuery* pServiceQuery = m_pServiceQueries; + while (pServiceQuery) + { + stcMDNSServiceQuery::stcAnswer* pSQAnswer = pServiceQuery->findAnswerForHostDomain(p_pAAnswer->m_Header.m_Domain); + if (pSQAnswer) // Answer for this host domain (eg. esp8266.local) available + { + stcMDNSServiceQuery::stcAnswer::stcIP4Address* pIP4Address = pSQAnswer->findIP4Address(p_pAAnswer->m_IPAddress); + if (pIP4Address) + { + // Already known IP4 address + if (p_pAAnswer->m_u32TTL) // Valid TTL -> Update answers TTL + { + pIP4Address->m_TTL.set(p_pAAnswer->m_u32TTL); + DEBUG_EX_INFO( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processAAnswer: Updated TTL(%d) for "), (int)p_pAAnswer->m_u32TTL); + _printRRDomain(pSQAnswer->m_ServiceDomain); + DEBUG_OUTPUT.printf_P(PSTR(" IP4Address (%s)\n"), pIP4Address->m_IPAddress.toString().c_str()); + ); + } + else // 'Goodbye' message for known IP4 address + { + pIP4Address->m_TTL.prepareDeletion(); // Prepare answer deletion according to RFC 6762, 10.1 + DEBUG_EX_INFO( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processAAnswer: 'Goodbye' received for ")); + _printRRDomain(pSQAnswer->m_ServiceDomain); + DEBUG_OUTPUT.printf_P(PSTR(" IP4 address (%s)\n"), pIP4Address->m_IPAddress.toString().c_str()); + ); + } + } + else + { + // Until now unknown IP4 address -> Add (if the message isn't just a 'Goodbye' note) + if (p_pAAnswer->m_u32TTL) // NOT just a 'Goodbye' message + { + pIP4Address = new stcMDNSServiceQuery::stcAnswer::stcIP4Address(p_pAAnswer->m_IPAddress, p_pAAnswer->m_u32TTL); + if ((pIP4Address) && + (pSQAnswer->addIP4Address(pIP4Address))) + { + + pSQAnswer->m_u32ContentFlags |= ServiceQueryAnswerType_IP4Address; + if (pServiceQuery->m_fnCallback) + { + MDNSServiceInfo serviceInfo(*this, (hMDNSServiceQuery)pServiceQuery, pServiceQuery->indexOfAnswer(pSQAnswer)); + pServiceQuery->m_fnCallback(serviceInfo, static_cast(ServiceQueryAnswerType_IP4Address), true); + } + } + else + { + DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processAAnswer: FAILED to add IP4 address (%s)!\n"), p_pAAnswer->m_IPAddress.toString().c_str());); + } + } + } + } + pServiceQuery = pServiceQuery->m_pNext; + } // while(service query) + } // else: No p_pAAnswer + DEBUG_EX_ERR(if (!bResult) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processAAnswer: FAILED!\n")); + }); + return bResult; +} +#endif + +#ifdef MDNS_IP6_SUPPORT +/* + MDNSResponder::_processAAAAAnswer +*/ +bool MDNSResponder::_processAAAAAnswer(const MDNSResponder::stcMDNS_RRAnswerAAAA* p_pAAAAAnswer) +{ + + bool bResult = false; + + if ((bResult = (0 != p_pAAAAAnswer))) + { + // eg. esp8266.local AAAA xxxx xx 0bf3::0c + + stcMDNSServiceQuery* pServiceQuery = m_pServiceQueries; + while (pServiceQuery) + { + stcMDNSServiceQuery::stcAnswer* pSQAnswer = pServiceQuery->findAnswerForHostDomain(p_pAAAAAnswer->m_Header.m_Domain); + if (pSQAnswer) // Answer for this host domain (eg. esp8266.local) available + { + stcIP6Address* pIP6Address = pSQAnswer->findIP6Address(p_pAAAAAnswer->m_IPAddress); + if (pIP6Address) + { + // Already known IP6 address + if (p_pAAAAAnswer->m_u32TTL) // Valid TTL -> Update answers TTL + { + pIP6Address->m_TTL.set(p_pAAAAAnswer->m_u32TTL); + DEBUG_EX_INFO( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processAAnswer: Updated TTL(%lu) for "), p_pAAAAAnswer->m_u32TTL); + _printRRDomain(pSQAnswer->m_ServiceDomain); + DEBUG_OUTPUT.printf_P(PSTR(" IP6 address (%s)\n"), pIP6Address->m_IPAddress.toString().c_str()); + ); + } + else // 'Goodbye' message for known IP6 address + { + pIP6Address->m_TTL.prepareDeletion(); // Prepare answer deletion according to RFC 6762, 10.1 + DEBUG_EX_INFO( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processAAnswer: 'Goodbye' received for ")); + _printRRDomain(pSQAnswer->m_ServiceDomain); + DEBUG_OUTPUT.printf_P(PSTR(" IP6 address (%s)\n"), pIP6Address->m_IPAddress.toString().c_str()); + ); + } + } + else + { + // Until now unknown IP6 address -> Add (if the message isn't just a 'Goodbye' note) + if (p_pAAAAAnswer->m_u32TTL) // NOT just a 'Goodbye' message + { + pIP6Address = new stcIP6Address(p_pAAAAAnswer->m_IPAddress, p_pAAAAAnswer->m_u32TTL); + if ((pIP6Address) && + (pSQAnswer->addIP6Address(pIP6Address))) + { + + pSQAnswer->m_u32ContentFlags |= ServiceQueryAnswerType_IP6Address; + + if (pServiceQuery->m_fnCallback) + { + pServiceQuery->m_fnCallback(this, (hMDNSServiceQuery)pServiceQuery, pServiceQuery->indexOfAnswer(pSQAnswer), ServiceQueryAnswerType_IP6Address, true, pServiceQuery->m_pUserdata); + } + } + else + { + DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processAAnswer: FAILED to add IP6 address (%s)!\n"), p_pAAAAAnswer->m_IPAddress.toString().c_str());); + } + } + } + } + pServiceQuery = pServiceQuery->m_pNext; + } // while(service query) + } // else: No p_pAAAAAnswer + + return bResult; +} +#endif + + +/* + PROBING +*/ + +/* + MDNSResponder::_updateProbeStatus + + Manages the (outgoing) probing process. + - If probing has not been started yet (ProbingStatus_NotStarted), the initial delay (see RFC 6762) is determined and + the process is started + - After timeout (of initial or subsequential delay) a probe message is send out for three times. If the message has + already been sent out three times, the probing has been successful and is finished. + + Conflict management is handled in '_parseResponse ff.' + Tiebraking is handled in 'parseQuery ff.' +*/ +bool MDNSResponder::_updateProbeStatus(void) +{ + + bool bResult = true; + + // + // Probe host domain + if ((ProbingStatus_ReadyToStart == m_HostProbeInformation.m_ProbingStatus) && // Ready to get started AND + //TODO: Fix the following to allow Ethernet shield or other interfaces + (_getResponseMulticastInterface() != IPAddress())) // Has IP address + { + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Starting host probing...\n"));); + + // First probe delay SHOULD be random 0-250 ms + m_HostProbeInformation.m_Timeout.reset(rand() % MDNS_PROBE_DELAY); + m_HostProbeInformation.m_ProbingStatus = ProbingStatus_InProgress; + } + else if ((ProbingStatus_InProgress == m_HostProbeInformation.m_ProbingStatus) && // Probing AND + (m_HostProbeInformation.m_Timeout.expired())) // Time for next probe + { + + if (MDNS_PROBE_COUNT > m_HostProbeInformation.m_u8SentCount) // Send next probe + { + if ((bResult = _sendHostProbe())) + { + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Did sent host probe\n\n"));); + m_HostProbeInformation.m_Timeout.reset(MDNS_PROBE_DELAY); + ++m_HostProbeInformation.m_u8SentCount; + } + } + else // Probing finished + { + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Done host probing.\n"));); + m_HostProbeInformation.m_ProbingStatus = ProbingStatus_Done; + m_HostProbeInformation.m_Timeout.resetToNeverExpires(); + if (m_HostProbeInformation.m_fnHostProbeResultCallback) + { + m_HostProbeInformation.m_fnHostProbeResultCallback(m_pcHostname, true); + } + + // Prepare to announce host + m_HostProbeInformation.m_u8SentCount = 0; + m_HostProbeInformation.m_Timeout.reset(MDNS_ANNOUNCE_DELAY); + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Prepared host announcing.\n\n"));); + } + } // else: Probing already finished OR waiting for next time slot + else if ((ProbingStatus_Done == m_HostProbeInformation.m_ProbingStatus) && + (m_HostProbeInformation.m_Timeout.expired())) + { + + if ((bResult = _announce(true, false))) // Don't announce services here + { + ++m_HostProbeInformation.m_u8SentCount; + + if (MDNS_ANNOUNCE_COUNT > m_HostProbeInformation.m_u8SentCount) + { + m_HostProbeInformation.m_Timeout.reset(MDNS_ANNOUNCE_DELAY); + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Announcing host (%d).\n\n"), m_HostProbeInformation.m_u8SentCount);); + } + else + { + m_HostProbeInformation.m_Timeout.resetToNeverExpires(); + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Done host announcing.\n\n"));); + } + } + } + + // + // Probe services + for (stcMDNSService* pService = m_pServices; ((bResult) && (pService)); pService = pService->m_pNext) + { + if (ProbingStatus_ReadyToStart == pService->m_ProbeInformation.m_ProbingStatus) // Ready to get started + { + + pService->m_ProbeInformation.m_Timeout.reset(MDNS_PROBE_DELAY); // More or equal than first probe for host domain + pService->m_ProbeInformation.m_ProbingStatus = ProbingStatus_InProgress; + } + else if ((ProbingStatus_InProgress == pService->m_ProbeInformation.m_ProbingStatus) && // Probing AND + (pService->m_ProbeInformation.m_Timeout.expired())) // Time for next probe + { + + if (MDNS_PROBE_COUNT > pService->m_ProbeInformation.m_u8SentCount) // Send next probe + { + if ((bResult = _sendServiceProbe(*pService))) + { + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Did sent service probe (%u)\n\n"), (pService->m_ProbeInformation.m_u8SentCount + 1));); + pService->m_ProbeInformation.m_Timeout.reset(MDNS_PROBE_DELAY); + ++pService->m_ProbeInformation.m_u8SentCount; + } + } + else // Probing finished + { + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Done service probing %s.%s.%s\n\n"), (pService->m_pcName ? : m_pcHostname), pService->m_pcService, pService->m_pcProtocol);); + pService->m_ProbeInformation.m_ProbingStatus = ProbingStatus_Done; + pService->m_ProbeInformation.m_Timeout.resetToNeverExpires(); + if (pService->m_ProbeInformation.m_fnServiceProbeResultCallback) + { + pService->m_ProbeInformation.m_fnServiceProbeResultCallback(pService->m_pcName, pService, true); + } + // Prepare to announce service + pService->m_ProbeInformation.m_u8SentCount = 0; + pService->m_ProbeInformation.m_Timeout.reset(MDNS_ANNOUNCE_DELAY); + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Prepared service announcing.\n\n"));); + } + } // else: Probing already finished OR waiting for next time slot + else if ((ProbingStatus_Done == pService->m_ProbeInformation.m_ProbingStatus) && + (pService->m_ProbeInformation.m_Timeout.expired())) + { + + if ((bResult = _announceService(*pService))) // Announce service + { + ++pService->m_ProbeInformation.m_u8SentCount; + + if (MDNS_ANNOUNCE_COUNT > pService->m_ProbeInformation.m_u8SentCount) + { + pService->m_ProbeInformation.m_Timeout.reset(MDNS_ANNOUNCE_DELAY); + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Announcing service %s.%s.%s (%d)\n\n"), (pService->m_pcName ? : m_pcHostname), pService->m_pcService, pService->m_pcProtocol, pService->m_ProbeInformation.m_u8SentCount);); + } + else + { + pService->m_ProbeInformation.m_Timeout.resetToNeverExpires(); + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Done service announcing for %s.%s.%s\n\n"), (pService->m_pcName ? : m_pcHostname), pService->m_pcService, pService->m_pcProtocol);); + } + } + } + } + DEBUG_EX_ERR(if (!bResult) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: FAILED!\n\n")); + }); + return bResult; +} + +/* + MDNSResponder::_resetProbeStatus + + Resets the probe status. + If 'p_bRestart' is set, the status is set to ProbingStatus_NotStarted. Consequently, + when running 'updateProbeStatus' (which is done in every '_update' loop), the probing + process is restarted. +*/ +bool MDNSResponder::_resetProbeStatus(bool p_bRestart /*= true*/) +{ + + m_HostProbeInformation.clear(false); + m_HostProbeInformation.m_ProbingStatus = (p_bRestart ? ProbingStatus_ReadyToStart : ProbingStatus_Done); + + for (stcMDNSService* pService = m_pServices; pService; pService = pService->m_pNext) + { + pService->m_ProbeInformation.clear(false); + pService->m_ProbeInformation.m_ProbingStatus = (p_bRestart ? ProbingStatus_ReadyToStart : ProbingStatus_Done); + } + return true; +} + +/* + MDNSResponder::_hasProbesWaitingForAnswers +*/ +bool MDNSResponder::_hasProbesWaitingForAnswers(void) const +{ + + bool bResult = ((ProbingStatus_InProgress == m_HostProbeInformation.m_ProbingStatus) && // Probing + (0 < m_HostProbeInformation.m_u8SentCount)); // And really probing + + for (stcMDNSService* pService = m_pServices; ((!bResult) && (pService)); pService = pService->m_pNext) + { + bResult = ((ProbingStatus_InProgress == pService->m_ProbeInformation.m_ProbingStatus) && // Probing + (0 < pService->m_ProbeInformation.m_u8SentCount)); // And really probing + } + return bResult; +} + +/* + MDNSResponder::_sendHostProbe + + Asks (probes) in the local network for the planned host domain + - (eg. esp8266.local) + + To allow 'tiebreaking' (see '_parseQuery'), the answers for these questions are delivered in + the 'knwon answers' section of the query. + Host domain: + - A/AAAA (eg. esp8266.esp -> 192.168.2.120) +*/ +bool MDNSResponder::_sendHostProbe(void) +{ + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _sendHostProbe (%s, %lu)\n"), m_pcHostname, millis());); + + bool bResult = true; + + // Requests for host domain + stcMDNSSendParameter sendParameter; + sendParameter.m_bCacheFlush = false; // RFC 6762 10.2 + + sendParameter.m_pQuestions = new stcMDNS_RRQuestion; + if (((bResult = (0 != sendParameter.m_pQuestions))) && + ((bResult = _buildDomainForHost(m_pcHostname, sendParameter.m_pQuestions->m_Header.m_Domain)))) + { + + //sendParameter.m_pQuestions->m_bUnicast = true; + sendParameter.m_pQuestions->m_Header.m_Attributes.m_u16Type = DNS_RRTYPE_ANY; + sendParameter.m_pQuestions->m_Header.m_Attributes.m_u16Class = (0x8000 | DNS_RRCLASS_IN); // Unicast & INternet + + // Add known answers +#ifdef MDNS_IP4_SUPPORT + sendParameter.m_u8HostReplyMask |= ContentFlag_A; // Add A answer +#endif +#ifdef MDNS_IP6_SUPPORT + sendParameter.m_u8HostReplyMask |= ContentFlag_AAAA; // Add AAAA answer +#endif + } + else + { + DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _sendHostProbe: FAILED to create host question!\n"));); + if (sendParameter.m_pQuestions) + { + delete sendParameter.m_pQuestions; + sendParameter.m_pQuestions = 0; + } + } + DEBUG_EX_ERR(if (!bResult) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _sendHostProbe: FAILED!\n")); + }); + return ((bResult) && + (_sendMDNSMessage(sendParameter))); +} + +/* + MDNSResponder::_sendServiceProbe + + Asks (probes) in the local network for the planned service instance domain + - (eg. MyESP._http._tcp.local). + + To allow 'tiebreaking' (see '_parseQuery'), the answers for these questions are delivered in + the 'knwon answers' section of the query. + Service domain: + - SRV (eg. MyESP._http._tcp.local -> 5000 esp8266.local) + - PTR NAME (eg. _http._tcp.local -> MyESP._http._tcp.local) (TODO: Check if needed, maybe TXT is better) +*/ +bool MDNSResponder::_sendServiceProbe(stcMDNSService& p_rService) +{ + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _sendServiceProbe (%s.%s.%s, %lu)\n"), (p_rService.m_pcName ? : m_pcHostname), p_rService.m_pcService, p_rService.m_pcProtocol, millis());); + + bool bResult = true; + + // Requests for service instance domain + stcMDNSSendParameter sendParameter; + sendParameter.m_bCacheFlush = false; // RFC 6762 10.2 + + sendParameter.m_pQuestions = new stcMDNS_RRQuestion; + if (((bResult = (0 != sendParameter.m_pQuestions))) && + ((bResult = _buildDomainForService(p_rService, true, sendParameter.m_pQuestions->m_Header.m_Domain)))) + { + + sendParameter.m_pQuestions->m_bUnicast = true; + sendParameter.m_pQuestions->m_Header.m_Attributes.m_u16Type = DNS_RRTYPE_ANY; + sendParameter.m_pQuestions->m_Header.m_Attributes.m_u16Class = (0x8000 | DNS_RRCLASS_IN); // Unicast & INternet + + // Add known answers + p_rService.m_u8ReplyMask = (ContentFlag_SRV | ContentFlag_PTR_NAME); // Add SRV and PTR NAME answers + } + else + { + DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _sendServiceProbe: FAILED to create service question!\n"));); + if (sendParameter.m_pQuestions) + { + delete sendParameter.m_pQuestions; + sendParameter.m_pQuestions = 0; + } + } + DEBUG_EX_ERR(if (!bResult) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _sendServiceProbe: FAILED!\n")); + }); + return ((bResult) && + (_sendMDNSMessage(sendParameter))); +} + +/* + MDNSResponder::_cancelProbingForHost +*/ +bool MDNSResponder::_cancelProbingForHost(void) +{ + + bool bResult = false; + + m_HostProbeInformation.clear(false); + // Send host notification + if (m_HostProbeInformation.m_fnHostProbeResultCallback) + { + m_HostProbeInformation.m_fnHostProbeResultCallback(m_pcHostname, false); + + bResult = true; + } + + for (stcMDNSService* pService = m_pServices; ((!bResult) && (pService)); pService = pService->m_pNext) + { + bResult = _cancelProbingForService(*pService); + } + return bResult; +} + +/* + MDNSResponder::_cancelProbingForService +*/ +bool MDNSResponder::_cancelProbingForService(stcMDNSService& p_rService) +{ + + bool bResult = false; + + p_rService.m_ProbeInformation.clear(false); + // Send notification + if (p_rService.m_ProbeInformation.m_fnServiceProbeResultCallback) + { + p_rService.m_ProbeInformation.m_fnServiceProbeResultCallback(p_rService.m_pcName, &p_rService, false); + bResult = true; + } + return bResult; +} + + + +/** + ANNOUNCING +*/ + +/* + MDNSResponder::_announce + + Announces the host domain: + - A/AAAA (eg. esp8266.local -> 192.168.2.120) + - PTR (eg. 192.168.2.120.in-addr.arpa -> esp8266.local) + + and all presented services: + - PTR_TYPE (_services._dns-sd._udp.local -> _http._tcp.local) + - PTR_NAME (eg. _http._tcp.local -> MyESP8266._http._tcp.local) + - SRV (eg. MyESP8266._http._tcp.local -> 5000 esp8266.local) + - TXT (eg. MyESP8266._http._tcp.local -> c#=1) + + Goodbye (Un-Announcing) for the host domain and all services is also handled here. + Goodbye messages are created by setting the TTL for the answer to 0, this happens + inside the '_writeXXXAnswer' procs via 'sendParameter.m_bUnannounce = true' +*/ +bool MDNSResponder::_announce(bool p_bAnnounce, + bool p_bIncludeServices) +{ + + bool bResult = false; + + stcMDNSSendParameter sendParameter; + if (ProbingStatus_Done == m_HostProbeInformation.m_ProbingStatus) + { + + bResult = true; + + sendParameter.m_bResponse = true; // Announces are 'Unsolicited authorative responses' + sendParameter.m_bAuthorative = true; + sendParameter.m_bUnannounce = !p_bAnnounce; // When unannouncing, the TTL is set to '0' while creating the answers + + // Announce host + sendParameter.m_u8HostReplyMask = 0; +#ifdef MDNS_IP4_SUPPORT + sendParameter.m_u8HostReplyMask |= ContentFlag_A; // A answer + sendParameter.m_u8HostReplyMask |= ContentFlag_PTR_IP4; // PTR_IP4 answer +#endif +#ifdef MDNS_IP6_SUPPORT + sendParameter.m_u8HostReplyMask |= ContentFlag_AAAA; // AAAA answer + sendParameter.m_u8HostReplyMask |= ContentFlag_PTR_IP6; // PTR_IP6 answer +#endif + + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _announce: Announcing host %s (content 0x%X)\n"), m_pcHostname, sendParameter.m_u8HostReplyMask);); + + if (p_bIncludeServices) + { + // Announce services (service type, name, SRV (location) and TXTs) + for (stcMDNSService* pService = m_pServices; ((bResult) && (pService)); pService = pService->m_pNext) + { + if (ProbingStatus_Done == pService->m_ProbeInformation.m_ProbingStatus) + { + pService->m_u8ReplyMask = (ContentFlag_PTR_TYPE | ContentFlag_PTR_NAME | ContentFlag_SRV | ContentFlag_TXT); + + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _announce: Announcing service %s.%s.%s (content %u)\n"), (pService->m_pcName ? : m_pcHostname), pService->m_pcService, pService->m_pcProtocol, pService->m_u8ReplyMask);); + } + } + } + } + DEBUG_EX_ERR(if (!bResult) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _announce: FAILED!\n")); + }); + return ((bResult) && + (_sendMDNSMessage(sendParameter))); +} + +/* + MDNSResponder::_announceService +*/ +bool MDNSResponder::_announceService(stcMDNSService& p_rService, + bool p_bAnnounce /*= true*/) +{ + + bool bResult = false; + + stcMDNSSendParameter sendParameter; + if (ProbingStatus_Done == p_rService.m_ProbeInformation.m_ProbingStatus) + { + + sendParameter.m_bResponse = true; // Announces are 'Unsolicited authorative responses' + sendParameter.m_bAuthorative = true; + sendParameter.m_bUnannounce = !p_bAnnounce; // When unannouncing, the TTL is set to '0' while creating the answers + + // DON'T announce host + sendParameter.m_u8HostReplyMask = 0; + + // Announce services (service type, name, SRV (location) and TXTs) + p_rService.m_u8ReplyMask = (ContentFlag_PTR_TYPE | ContentFlag_PTR_NAME | ContentFlag_SRV | ContentFlag_TXT); + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _announceService: Announcing service %s.%s.%s (content 0x%X)\n"), (p_rService.m_pcName ? : m_pcHostname), p_rService.m_pcService, p_rService.m_pcProtocol, p_rService.m_u8ReplyMask);); + + bResult = true; + } + DEBUG_EX_ERR(if (!bResult) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _announceService: FAILED!\n")); + }); + return ((bResult) && + (_sendMDNSMessage(sendParameter))); +} + + +/** + SERVICE QUERY CACHE +*/ + +/* + MDNSResponder::_hasServiceQueriesWaitingForAnswers +*/ +bool MDNSResponder::_hasServiceQueriesWaitingForAnswers(void) const +{ + + bool bOpenQueries = false; + + for (stcMDNSServiceQuery* pServiceQuery = m_pServiceQueries; pServiceQuery; pServiceQuery = pServiceQuery->m_pNext) + { + if (pServiceQuery->m_bAwaitingAnswers) + { + bOpenQueries = true; + break; + } + } + return bOpenQueries; +} + +/* + MDNSResponder::_checkServiceQueryCache + + For any 'living' service query (m_bAwaitingAnswers == true) all available answers (their components) + are checked for topicality based on the stored reception time and the answers TTL. + When the components TTL is outlasted by more than 80%, a new question is generated, to get updated information. + When no update arrived (in time), the component is removed from the answer (cache). + +*/ +bool MDNSResponder::_checkServiceQueryCache(void) +{ + + bool bResult = true; + + DEBUG_EX_INFO( + bool printedInfo = false; + ); + for (stcMDNSServiceQuery* pServiceQuery = m_pServiceQueries; ((bResult) && (pServiceQuery)); pServiceQuery = pServiceQuery->m_pNext) + { + + // + // Resend dynamic service queries, if not already done often enough + if ((!pServiceQuery->m_bLegacyQuery) && + (MDNS_DYNAMIC_QUERY_RESEND_COUNT > pServiceQuery->m_u8SentCount) && + (pServiceQuery->m_ResendTimeout.expired())) + { + + if ((bResult = _sendMDNSServiceQuery(*pServiceQuery))) + { + ++pServiceQuery->m_u8SentCount; + pServiceQuery->m_ResendTimeout.reset((MDNS_DYNAMIC_QUERY_RESEND_COUNT > pServiceQuery->m_u8SentCount) + ? (MDNS_DYNAMIC_QUERY_RESEND_DELAY * (pServiceQuery->m_u8SentCount - 1)) + : esp8266::polledTimeout::oneShotMs::neverExpires); + } + DEBUG_EX_INFO( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: %s to resend service query!"), (bResult ? "Succeeded" : "FAILED")); + printedInfo = true; + ); + } + + // + // Schedule updates for cached answers + if (pServiceQuery->m_bAwaitingAnswers) + { + stcMDNSServiceQuery::stcAnswer* pSQAnswer = pServiceQuery->m_pAnswers; + while ((bResult) && + (pSQAnswer)) + { + stcMDNSServiceQuery::stcAnswer* pNextSQAnswer = pSQAnswer->m_pNext; + + // 1. level answer + if ((bResult) && + (pSQAnswer->m_TTLServiceDomain.flagged())) + { + + if (!pSQAnswer->m_TTLServiceDomain.finalTimeoutLevel()) + { + + bResult = ((_sendMDNSServiceQuery(*pServiceQuery)) && + (pSQAnswer->m_TTLServiceDomain.restart())); + DEBUG_EX_INFO( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: PTR update scheduled for ")); + _printRRDomain(pSQAnswer->m_ServiceDomain); + DEBUG_OUTPUT.printf_P(PSTR(" %s\n"), (bResult ? "OK" : "FAILURE")); + printedInfo = true; + ); + } + else + { + // Timed out! -> Delete + if (pServiceQuery->m_fnCallback) + { + MDNSServiceInfo serviceInfo(*this, (hMDNSServiceQuery)pServiceQuery, pServiceQuery->indexOfAnswer(pSQAnswer)); + pServiceQuery->m_fnCallback(serviceInfo, static_cast(ServiceQueryAnswerType_ServiceDomain), false); + } + DEBUG_EX_INFO( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: Will remove PTR answer for ")); + _printRRDomain(pSQAnswer->m_ServiceDomain); + DEBUG_OUTPUT.printf_P(PSTR("\n")); + printedInfo = true; + ); + + bResult = pServiceQuery->removeAnswer(pSQAnswer); + pSQAnswer = 0; + continue; // Don't use this answer anymore + } + } // ServiceDomain flagged + + // 2. level answers + // HostDomain & Port (from SRV) + if ((bResult) && + (pSQAnswer->m_TTLHostDomainAndPort.flagged())) + { + + if (!pSQAnswer->m_TTLHostDomainAndPort.finalTimeoutLevel()) + { + + bResult = ((_sendMDNSQuery(pSQAnswer->m_ServiceDomain, DNS_RRTYPE_SRV)) && + (pSQAnswer->m_TTLHostDomainAndPort.restart())); + DEBUG_EX_INFO( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: SRV update scheduled for ")); + _printRRDomain(pSQAnswer->m_ServiceDomain); + DEBUG_OUTPUT.printf_P(PSTR(" host domain and port %s\n"), (bResult ? "OK" : "FAILURE")); + printedInfo = true; + ); + } + else + { + // Timed out! -> Delete + DEBUG_EX_INFO( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: Will remove SRV answer for ")); + _printRRDomain(pSQAnswer->m_ServiceDomain); + DEBUG_OUTPUT.printf_P(PSTR(" host domain and port\n")); + printedInfo = true; + ); + // Delete + pSQAnswer->m_HostDomain.clear(); + pSQAnswer->releaseHostDomain(); + pSQAnswer->m_u16Port = 0; + pSQAnswer->m_TTLHostDomainAndPort.set(0); + uint32_t u32ContentFlags = ServiceQueryAnswerType_HostDomainAndPort; + // As the host domain is the base for the IP4- and IP6Address, remove these too +#ifdef MDNS_IP4_SUPPORT + pSQAnswer->releaseIP4Addresses(); + u32ContentFlags |= ServiceQueryAnswerType_IP4Address; +#endif +#ifdef MDNS_IP6_SUPPORT + pSQAnswer->releaseIP6Addresses(); + u32ContentFlags |= ServiceQueryAnswerType_IP6Address; +#endif + + // Remove content flags for deleted answer parts + pSQAnswer->m_u32ContentFlags &= ~u32ContentFlags; + if (pServiceQuery->m_fnCallback) + { + MDNSServiceInfo serviceInfo(*this, (hMDNSServiceQuery)pServiceQuery, pServiceQuery->indexOfAnswer(pSQAnswer)); + pServiceQuery->m_fnCallback(serviceInfo, static_cast(u32ContentFlags), false); + } + } + } // HostDomainAndPort flagged + + // Txts (from TXT) + if ((bResult) && + (pSQAnswer->m_TTLTxts.flagged())) + { + + if (!pSQAnswer->m_TTLTxts.finalTimeoutLevel()) + { + + bResult = ((_sendMDNSQuery(pSQAnswer->m_ServiceDomain, DNS_RRTYPE_TXT)) && + (pSQAnswer->m_TTLTxts.restart())); + DEBUG_EX_INFO( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: TXT update scheduled for ")); + _printRRDomain(pSQAnswer->m_ServiceDomain); + DEBUG_OUTPUT.printf_P(PSTR(" TXTs %s\n"), (bResult ? "OK" : "FAILURE")); + printedInfo = true; + ); + } + else + { + // Timed out! -> Delete + DEBUG_EX_INFO( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: Will remove TXT answer for ")); + _printRRDomain(pSQAnswer->m_ServiceDomain); + DEBUG_OUTPUT.printf_P(PSTR(" TXTs\n")); + printedInfo = true; + ); + // Delete + pSQAnswer->m_Txts.clear(); + pSQAnswer->m_TTLTxts.set(0); + + // Remove content flags for deleted answer parts + pSQAnswer->m_u32ContentFlags &= ~ServiceQueryAnswerType_Txts; + + if (pServiceQuery->m_fnCallback) + { + MDNSServiceInfo serviceInfo(*this, (hMDNSServiceQuery)pServiceQuery, pServiceQuery->indexOfAnswer(pSQAnswer)); + pServiceQuery->m_fnCallback(serviceInfo, static_cast(ServiceQueryAnswerType_Txts), false); + } + } + } // TXTs flagged + + // 3. level answers +#ifdef MDNS_IP4_SUPPORT + // IP4Address (from A) + stcMDNSServiceQuery::stcAnswer::stcIP4Address* pIP4Address = pSQAnswer->m_pIP4Addresses; + bool bAUpdateQuerySent = false; + while ((pIP4Address) && + (bResult)) + { + + stcMDNSServiceQuery::stcAnswer::stcIP4Address* pNextIP4Address = pIP4Address->m_pNext; // Get 'next' early, as 'current' may be deleted at the end... + + if (pIP4Address->m_TTL.flagged()) + { + + if (!pIP4Address->m_TTL.finalTimeoutLevel()) // Needs update + { + + if ((bAUpdateQuerySent) || + ((bResult = _sendMDNSQuery(pSQAnswer->m_HostDomain, DNS_RRTYPE_A)))) + { + + pIP4Address->m_TTL.restart(); + bAUpdateQuerySent = true; + + DEBUG_EX_INFO( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: IP4 update scheduled for ")); + _printRRDomain(pSQAnswer->m_ServiceDomain); + DEBUG_OUTPUT.printf_P(PSTR(" IP4 address (%s)\n"), (pIP4Address->m_IPAddress.toString().c_str())); + printedInfo = true; + ); + } + } + else + { + // Timed out! -> Delete + DEBUG_EX_INFO( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: Will remove IP4 answer for ")); + _printRRDomain(pSQAnswer->m_ServiceDomain); + DEBUG_OUTPUT.printf_P(PSTR(" IP4 address\n")); + printedInfo = true; + ); + pSQAnswer->removeIP4Address(pIP4Address); + if (!pSQAnswer->m_pIP4Addresses) // NO IP4 address left -> remove content flag + { + pSQAnswer->m_u32ContentFlags &= ~ServiceQueryAnswerType_IP4Address; + } + // Notify client + if (pServiceQuery->m_fnCallback) + { + MDNSServiceInfo serviceInfo(*this, (hMDNSServiceQuery)pServiceQuery, pServiceQuery->indexOfAnswer(pSQAnswer)); + pServiceQuery->m_fnCallback(serviceInfo, static_cast(ServiceQueryAnswerType_IP4Address), false); + } + } + } // IP4 flagged + + pIP4Address = pNextIP4Address; // Next + } // while +#endif +#ifdef MDNS_IP6_SUPPORT + // IP6Address (from AAAA) + stcMDNSServiceQuery::stcAnswer::stcIP6Address* pIP6Address = pSQAnswer->m_pIP6Addresses; + bool bAAAAUpdateQuerySent = false; + while ((pIP6Address) && + (bResult)) + { + + stcMDNSServiceQuery::stcAnswer::stcIP6Address* pNextIP6Address = pIP6Address->m_pNext; // Get 'next' early, as 'current' may be deleted at the end... + + if (pIP6Address->m_TTL.flagged()) + { + + if (!pIP6Address->m_TTL.finalTimeoutLevel()) // Needs update + { + + if ((bAAAAUpdateQuerySent) || + ((bResult = _sendMDNSQuery(pSQAnswer->m_HostDomain, DNS_RRTYPE_AAAA)))) + { + + pIP6Address->m_TTL.restart(); + bAAAAUpdateQuerySent = true; + + DEBUG_EX_INFO( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: IP6 update scheduled for ")); + _printRRDomain(pSQAnswer->m_ServiceDomain); + DEBUG_OUTPUT.printf_P(PSTR(" IP6 address (%s)\n"), (pIP6Address->m_IPAddress.toString().c_str())); + printedInfo = true; + ); + } + } + else + { + // Timed out! -> Delete + DEBUG_EX_INFO( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: Will remove answer for ")); + _printRRDomain(pSQAnswer->m_ServiceDomain); + DEBUG_OUTPUT.printf_P(PSTR(" IP6Address\n")); + printedInfo = true; + ); + pSQAnswer->removeIP6Address(pIP6Address); + if (!pSQAnswer->m_pIP6Addresses) // NO IP6 address left -> remove content flag + { + pSQAnswer->m_u32ContentFlags &= ~ServiceQueryAnswerType_IP6Address; + } + // Notify client + if (pServiceQuery->m_fnCallback) + { + pServiceQuery->m_fnCallback(this, (hMDNSServiceQuery)pServiceQuery, pServiceQuery->indexOfAnswer(pSQAnswer), ServiceQueryAnswerType_IP6Address, false, pServiceQuery->m_pUserdata); + } + } + } // IP6 flagged + + pIP6Address = pNextIP6Address; // Next + } // while +#endif + pSQAnswer = pNextSQAnswer; + } + } + } + DEBUG_EX_INFO( + if (printedInfo) +{ + DEBUG_OUTPUT.printf_P(PSTR("\n")); + } + ); + DEBUG_EX_ERR(if (!bResult) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: FAILED!\n")); + }); + return bResult; +} + + +/* + MDNSResponder::_replyMaskForHost + + Determines the relavant host answers for the given question. + - A question for the hostname (eg. esp8266.local) will result in an A/AAAA (eg. 192.168.2.129) reply. + - A question for the reverse IP address (eg. 192-168.2.120.inarpa.arpa) will result in an PTR_IP4 (eg. esp8266.local) reply. + + In addition, a full name match (question domain == host domain) is marked. +*/ +uint8_t MDNSResponder::_replyMaskForHost(const MDNSResponder::stcMDNS_RRHeader& p_RRHeader, + bool* p_pbFullNameMatch /*= 0*/) const +{ + //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _replyMaskForHost\n"));); + + uint8_t u8ReplyMask = 0; + (p_pbFullNameMatch ? *p_pbFullNameMatch = false : 0); + + if ((DNS_RRCLASS_IN == p_RRHeader.m_Attributes.m_u16Class) || + (DNS_RRCLASS_ANY == p_RRHeader.m_Attributes.m_u16Class)) + { + + if ((DNS_RRTYPE_PTR == p_RRHeader.m_Attributes.m_u16Type) || + (DNS_RRTYPE_ANY == p_RRHeader.m_Attributes.m_u16Type)) + { + // PTR request +#ifdef MDNS_IP4_SUPPORT + stcMDNS_RRDomain reverseIP4Domain; + if ((_buildDomainForReverseIP4(_getResponseMulticastInterface(), reverseIP4Domain)) && + (p_RRHeader.m_Domain == reverseIP4Domain)) + { + // Reverse domain match + u8ReplyMask |= ContentFlag_PTR_IP4; + } +#endif +#ifdef MDNS_IP6_SUPPORT + // TODO +#endif + } // Address qeuest + + stcMDNS_RRDomain hostDomain; + if ((_buildDomainForHost(m_pcHostname, hostDomain)) && + (p_RRHeader.m_Domain == hostDomain)) // Host domain match + { + + (p_pbFullNameMatch ? (*p_pbFullNameMatch = true) : (0)); + +#ifdef MDNS_IP4_SUPPORT + if ((DNS_RRTYPE_A == p_RRHeader.m_Attributes.m_u16Type) || + (DNS_RRTYPE_ANY == p_RRHeader.m_Attributes.m_u16Type)) + { + // IP4 address request + u8ReplyMask |= ContentFlag_A; + } +#endif +#ifdef MDNS_IP6_SUPPORT + if ((DNS_RRTYPE_AAAA == p_RRHeader.m_Attributes.m_u16Type) || + (DNS_RRTYPE_ANY == p_RRHeader.m_Attributes.m_u16Type)) + { + // IP6 address request + u8ReplyMask |= ContentFlag_AAAA; + } +#endif + } + } + else + { + //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _replyMaskForHost: INVALID RR-class (0x%04X)!\n"), p_RRHeader.m_Attributes.m_u16Class);); + } + DEBUG_EX_INFO(if (u8ReplyMask) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _replyMaskForHost: 0x%X\n"), u8ReplyMask); + }); + return u8ReplyMask; +} + +/* + MDNSResponder::_replyMaskForService + + Determines the relevant service answers for the given question + - A PTR dns-sd service enum question (_services.dns-sd._udp.local) will result into an PTR_TYPE (eg. _http._tcp.local) answer + - A PTR service type question (eg. _http._tcp.local) will result into an PTR_NAME (eg. MyESP._http._tcp.local) answer + - A PTR service name question (eg. MyESP._http._tcp.local) will result into an PTR_NAME (eg. MyESP._http._tcp.local) answer + - A SRV service name question (eg. MyESP._http._tcp.local) will result into an SRV (eg. 5000 MyESP.local) answer + - A TXT service name question (eg. MyESP._http._tcp.local) will result into an TXT (eg. c#=1) answer + + In addition, a full name match (question domain == service instance domain) is marked. +*/ +uint8_t MDNSResponder::_replyMaskForService(const MDNSResponder::stcMDNS_RRHeader& p_RRHeader, + const MDNSResponder::stcMDNSService& p_Service, + bool* p_pbFullNameMatch /*= 0*/) const +{ + + uint8_t u8ReplyMask = 0; + (p_pbFullNameMatch ? *p_pbFullNameMatch = false : 0); + + if ((DNS_RRCLASS_IN == p_RRHeader.m_Attributes.m_u16Class) || + (DNS_RRCLASS_ANY == p_RRHeader.m_Attributes.m_u16Class)) + { + + stcMDNS_RRDomain DNSSDDomain; + if ((_buildDomainForDNSSD(DNSSDDomain)) && // _services._dns-sd._udp.local + (p_RRHeader.m_Domain == DNSSDDomain) && + ((DNS_RRTYPE_PTR == p_RRHeader.m_Attributes.m_u16Type) || + (DNS_RRTYPE_ANY == p_RRHeader.m_Attributes.m_u16Type))) + { + // Common service info requested + u8ReplyMask |= ContentFlag_PTR_TYPE; + } + + stcMDNS_RRDomain serviceDomain; + if ((_buildDomainForService(p_Service, false, serviceDomain)) && // eg. _http._tcp.local + (p_RRHeader.m_Domain == serviceDomain) && + ((DNS_RRTYPE_PTR == p_RRHeader.m_Attributes.m_u16Type) || + (DNS_RRTYPE_ANY == p_RRHeader.m_Attributes.m_u16Type))) + { + // Special service info requested + u8ReplyMask |= ContentFlag_PTR_NAME; + } + + if ((_buildDomainForService(p_Service, true, serviceDomain)) && // eg. MyESP._http._tcp.local + (p_RRHeader.m_Domain == serviceDomain)) + { + + (p_pbFullNameMatch ? (*p_pbFullNameMatch = true) : (0)); + + if ((DNS_RRTYPE_SRV == p_RRHeader.m_Attributes.m_u16Type) || + (DNS_RRTYPE_ANY == p_RRHeader.m_Attributes.m_u16Type)) + { + // Instance info SRV requested + u8ReplyMask |= ContentFlag_SRV; + } + if ((DNS_RRTYPE_TXT == p_RRHeader.m_Attributes.m_u16Type) || + (DNS_RRTYPE_ANY == p_RRHeader.m_Attributes.m_u16Type)) + { + // Instance info TXT requested + u8ReplyMask |= ContentFlag_TXT; + } + } + } + else + { + //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _replyMaskForService: INVALID RR-class (0x%04X)!\n"), p_RRHeader.m_Attributes.m_u16Class);); + } + DEBUG_EX_INFO(if (u8ReplyMask) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _replyMaskForService(%s.%s.%s): 0x%X\n"), p_Service.m_pcName, p_Service.m_pcService, p_Service.m_pcProtocol, u8ReplyMask); + }); + return u8ReplyMask; +} + +} // namespace MDNSImplementation + +} // namespace esp8266 diff --git a/libraries/ESP8266mDNS/src/LEAmDNS_Helpers.cpp b/libraries/ESP8266mDNS/src/LEAmDNS_Helpers.cpp index cf5cab07b5..d23941ce53 100644 --- a/libraries/ESP8266mDNS/src/LEAmDNS_Helpers.cpp +++ b/libraries/ESP8266mDNS/src/LEAmDNS_Helpers.cpp @@ -1,750 +1,850 @@ -/* - * LEAmDNS_Helpers.cpp - * - * License (MIT license): - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - */ - -#include "lwip/igmp.h" - -#include "LEAmDNS_lwIPdefs.h" -#include "LEAmDNS_Priv.h" - - -namespace { - -/* - * strrstr (static) - * - * Backwards search for p_pcPattern in p_pcString - * Based on: https://stackoverflow.com/a/1634398/2778898 - * - */ -const char* strrstr(const char*__restrict p_pcString, const char*__restrict p_pcPattern) { - - const char* pcResult = 0; - - size_t stStringLength = (p_pcString ? strlen(p_pcString) : 0); - size_t stPatternLength = (p_pcPattern ? strlen(p_pcPattern) : 0); - - if ((stStringLength) && - (stPatternLength) && - (stPatternLength <= stStringLength)) { - // Pattern is shorter or has the same length tham the string - - for (const char* s=(p_pcString + stStringLength - stPatternLength); s>=p_pcString; --s) { - if (0 == strncmp(s, p_pcPattern, stPatternLength)) { - pcResult = s; - break; - } - } - } - return pcResult; -} - - -} // anonymous - - - - - -namespace esp8266 { - -/* - * LEAmDNS - */ -namespace MDNSImplementation { - -/** - * HELPERS - */ - -/* - * MDNSResponder::indexDomain (static) - * - * Updates the given domain 'p_rpcHostname' by appending a delimiter and an index number. - * - * If the given domain already hasa numeric index (after the given delimiter), this index - * incremented. If not, the delimiter and index '2' is added. - * - * If 'p_rpcHostname' is empty (==0), the given default name 'p_pcDefaultHostname' is used, - * if no default is given, 'esp8266' is used. - * - */ -/*static*/ bool MDNSResponder::indexDomain(char*& p_rpcDomain, - const char* p_pcDivider /*= "-"*/, - const char* p_pcDefaultDomain /*= 0*/) { - - bool bResult = false; - - // Ensure a divider exists; use '-' as default - const char* pcDivider = (p_pcDivider ?: "-"); - - if (p_rpcDomain) { - const char* pFoundDivider = strrstr(p_rpcDomain, pcDivider); - if (pFoundDivider) { // maybe already extended - char* pEnd = 0; - unsigned long ulIndex = strtoul((pFoundDivider + strlen(pcDivider)), &pEnd, 10); - if ((ulIndex) && - ((pEnd - p_rpcDomain) == (ptrdiff_t)strlen(p_rpcDomain)) && - (!*pEnd)) { // Valid (old) index found - - char acIndexBuffer[16]; - sprintf(acIndexBuffer, "%lu", (++ulIndex)); - size_t stLength = ((pFoundDivider - p_rpcDomain + strlen(pcDivider)) + strlen(acIndexBuffer) + 1); - char* pNewHostname = new char[stLength]; - if (pNewHostname) { - memcpy(pNewHostname, p_rpcDomain, (pFoundDivider - p_rpcDomain + strlen(pcDivider))); - pNewHostname[pFoundDivider - p_rpcDomain + strlen(pcDivider)] = 0; - strcat(pNewHostname, acIndexBuffer); - - delete[] p_rpcDomain; - p_rpcDomain = pNewHostname; - - bResult = true; - } - else { - DEBUG_EX_ERR(DEBUG_OUTPUT.println(F("[MDNSResponder] indexDomain: FAILED to alloc new hostname!"));); - } - } - else { - pFoundDivider = 0; // Flag the need to (base) extend the hostname - } - } - - if (!pFoundDivider) { // not yet extended (or failed to increment extension) -> start indexing - size_t stLength = strlen(p_rpcDomain) + (strlen(pcDivider) + 1 + 1); // Name + Divider + '2' + '\0' - char* pNewHostname = new char[stLength]; - if (pNewHostname) { - sprintf(pNewHostname, "%s%s2", p_rpcDomain, pcDivider); - - delete[] p_rpcDomain; - p_rpcDomain = pNewHostname; - - bResult = true; - } - else { - DEBUG_EX_ERR(DEBUG_OUTPUT.println(F("[MDNSResponder] indexDomain: FAILED to alloc new hostname!"));); - } - } - } - else { - // No given host domain, use base or default - const char* cpcDefaultName = (p_pcDefaultDomain ?: "esp8266"); - - size_t stLength = strlen(cpcDefaultName) + 1; // '\0' - p_rpcDomain = new char[stLength]; - if (p_rpcDomain) { - strncpy(p_rpcDomain, cpcDefaultName, stLength); - bResult = true; - } - else { - DEBUG_EX_ERR(DEBUG_OUTPUT.println(F("[MDNSResponder] indexDomain: FAILED to alloc new hostname!"));); - } - } - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] indexDomain: %s\n"), p_rpcDomain);); - return bResult; -} - - -/* - * UDP CONTEXT - */ - -bool MDNSResponder::_callProcess(void) { - DEBUG_EX_INFO(DEBUG_OUTPUT.printf("[MDNSResponder] _callProcess (%lu, triggered by: %s)\n", millis(), IPAddress(m_pUDPContext->getRemoteAddress()).toString().c_str());); - - return _process(false); -} - -/* - * MDNSResponder::_allocUDPContext - * - * (Re-)Creates the one-and-only UDP context for the MDNS responder. - * The context is added to the 'multicast'-group and listens to the MDNS port (5353). - * The travel-distance for multicast messages is set to 1 (local, via MDNS_MULTICAST_TTL). - * Messages are received via the MDNSResponder '_update' function. CAUTION: This function - * is called from the WiFi stack side of the ESP stack system. - * - */ -bool MDNSResponder::_allocUDPContext(void) { - DEBUG_EX_INFO(DEBUG_OUTPUT.println("[MDNSResponder] _allocUDPContext");); - - bool bResult = false; - - _releaseUDPContext(); - -#ifdef MDNS_IP4_SUPPORT - ip_addr_t multicast_addr = DNS_MQUERY_IPV4_GROUP_INIT; -#endif -#ifdef MDNS_IP6_SUPPORT - //TODO: set multicast address (lwip_joingroup() is IPv4 only at the time of writing) - multicast_addr.addr = DNS_MQUERY_IPV6_GROUP_INIT; -#endif - if (ERR_OK == igmp_joingroup(ip_2_ip4(&m_netif->ip_addr), ip_2_ip4(&multicast_addr))) { - m_pUDPContext = new UdpContext; - m_pUDPContext->ref(); - - if (m_pUDPContext->listen(IP4_ADDR_ANY, DNS_MQUERY_PORT)) { - m_pUDPContext->setMulticastTTL(MDNS_MULTICAST_TTL); - m_pUDPContext->onRx(std::bind(&MDNSResponder::_callProcess, this)); - - bResult = m_pUDPContext->connect(&multicast_addr, DNS_MQUERY_PORT); - } - } - return bResult; -} - -/* - * MDNSResponder::_releaseUDPContext - */ -bool MDNSResponder::_releaseUDPContext(void) { - - if (m_pUDPContext) { - m_pUDPContext->unref(); - m_pUDPContext = 0; - } - return true; -} - - -/* - * SERVICE QUERY - */ - -/* - * MDNSResponder::_allocServiceQuery - */ -MDNSResponder::stcMDNSServiceQuery* MDNSResponder::_allocServiceQuery(void) { - - stcMDNSServiceQuery* pServiceQuery = new stcMDNSServiceQuery; - if (pServiceQuery) { - // Link to query list - pServiceQuery->m_pNext = m_pServiceQueries; - m_pServiceQueries = pServiceQuery; - } - return m_pServiceQueries; -} - -/* - * MDNSResponder::_removeServiceQuery - */ -bool MDNSResponder::_removeServiceQuery(MDNSResponder::stcMDNSServiceQuery* p_pServiceQuery) { - - bool bResult = false; - - if (p_pServiceQuery) { - stcMDNSServiceQuery* pPred = m_pServiceQueries; - while ((pPred) && - (pPred->m_pNext != p_pServiceQuery)) { - pPred = pPred->m_pNext; - } - if (pPred) { - pPred->m_pNext = p_pServiceQuery->m_pNext; - delete p_pServiceQuery; - bResult = true; - } - else { // No predecesor - if (m_pServiceQueries == p_pServiceQuery) { - m_pServiceQueries = p_pServiceQuery->m_pNext; - delete p_pServiceQuery; - bResult = true; - } - else { - DEBUG_EX_ERR(DEBUG_OUTPUT.println("[MDNSResponder] _releaseServiceQuery: INVALID service query!");); - } - } - } - return bResult; -} - -/* - * MDNSResponder::_removeLegacyServiceQuery - */ -bool MDNSResponder::_removeLegacyServiceQuery(void) { - - stcMDNSServiceQuery* pLegacyServiceQuery = _findLegacyServiceQuery(); - return (pLegacyServiceQuery ? _removeServiceQuery(pLegacyServiceQuery) : true); -} - -/* - * MDNSResponder::_findServiceQuery - * - * 'Convert' hMDNSServiceQuery to stcMDNSServiceQuery* (ensure existance) - * - */ -MDNSResponder::stcMDNSServiceQuery* MDNSResponder::_findServiceQuery(MDNSResponder::hMDNSServiceQuery p_hServiceQuery) { - - stcMDNSServiceQuery* pServiceQuery = m_pServiceQueries; - while (pServiceQuery) { - if ((hMDNSServiceQuery)pServiceQuery == p_hServiceQuery) { - break; - } - pServiceQuery = pServiceQuery->m_pNext; - } - return pServiceQuery; -} - -/* - * MDNSResponder::_findLegacyServiceQuery - */ -MDNSResponder::stcMDNSServiceQuery* MDNSResponder::_findLegacyServiceQuery(void) { - - stcMDNSServiceQuery* pServiceQuery = m_pServiceQueries; - while (pServiceQuery) { - if (pServiceQuery->m_bLegacyQuery) { - break; - } - pServiceQuery = pServiceQuery->m_pNext; - } - return pServiceQuery; -} - -/* - * MDNSResponder::_releaseServiceQueries - */ -bool MDNSResponder::_releaseServiceQueries(void) { - while (m_pServiceQueries) { - stcMDNSServiceQuery* pNext = m_pServiceQueries->m_pNext; - delete m_pServiceQueries; - m_pServiceQueries = pNext; - } - return true; -} - -/* - * MDNSResponder::_findNextServiceQueryByServiceType - */ -MDNSResponder::stcMDNSServiceQuery* MDNSResponder::_findNextServiceQueryByServiceType(const stcMDNS_RRDomain& p_ServiceTypeDomain, - const stcMDNSServiceQuery* p_pPrevServiceQuery) { - stcMDNSServiceQuery* pMatchingServiceQuery = 0; - - stcMDNSServiceQuery* pServiceQuery = (p_pPrevServiceQuery ? p_pPrevServiceQuery->m_pNext : m_pServiceQueries); - while (pServiceQuery) { - if (p_ServiceTypeDomain == pServiceQuery->m_ServiceTypeDomain) { - pMatchingServiceQuery = pServiceQuery; - break; - } - pServiceQuery = pServiceQuery->m_pNext; - } - return pMatchingServiceQuery; -} - - -/* - * HOSTNAME - */ - -/* - * MDNSResponder::_setHostname - */ -bool MDNSResponder::_setHostname(const char* p_pcHostname) { - //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _allocHostname (%s)\n"), p_pcHostname);); - - bool bResult = false; - - _releaseHostname(); - - size_t stLength = 0; - if ((p_pcHostname) && - (MDNS_DOMAIN_LABEL_MAXLENGTH >= (stLength = strlen(p_pcHostname)))) { // char max size for a single label - // Copy in hostname characters as lowercase - if ((bResult = (0 != (m_pcHostname = new char[stLength + 1])))) { -#ifdef MDNS_FORCE_LOWERCASE_HOSTNAME - size_t i = 0; - for (; i= strlen(p_pcName))) && - (p_pcService) && - (MDNS_SERVICE_NAME_LENGTH >= strlen(p_pcService)) && - (p_pcProtocol) && - (MDNS_SERVICE_PROTOCOL_LENGTH >= strlen(p_pcProtocol)) && - (p_u16Port) && - (0 != (pService = new stcMDNSService)) && - (pService->setName(p_pcName ?: m_pcHostname)) && - (pService->setService(p_pcService)) && - (pService->setProtocol(p_pcProtocol))) { - - pService->m_bAutoName = (0 == p_pcName); - pService->m_u16Port = p_u16Port; - - // Add to list (or start list) - pService->m_pNext = m_pServices; - m_pServices = pService; - } - return pService; -} - -/* - * MDNSResponder::_releaseService - */ -bool MDNSResponder::_releaseService(MDNSResponder::stcMDNSService* p_pService) { - - bool bResult = false; - - if (p_pService) { - stcMDNSService* pPred = m_pServices; - while ((pPred) && - (pPred->m_pNext != p_pService)) { - pPred = pPred->m_pNext; - } - if (pPred) { - pPred->m_pNext = p_pService->m_pNext; - delete p_pService; - bResult = true; - } - else { // No predecesor - if (m_pServices == p_pService) { - m_pServices = p_pService->m_pNext; - delete p_pService; - bResult = true; - } - else { - DEBUG_EX_ERR(DEBUG_OUTPUT.println("[MDNSResponder] _releaseService: INVALID service!");); - } - } - } - return bResult; -} - -/* - * MDNSResponder::_releaseServices - */ -bool MDNSResponder::_releaseServices(void) { - - stcMDNSService* pService = m_pServices; - while (pService) { - _releaseService(pService); - pService = m_pServices; - } - return true; -} - -/* - * MDNSResponder::_findService - */ -MDNSResponder::stcMDNSService* MDNSResponder::_findService(const char* p_pcName, - const char* p_pcService, - const char* p_pcProtocol) { - - stcMDNSService* pService = m_pServices; - while (pService) { - if ((0 == strcmp(pService->m_pcName, p_pcName)) && - (0 == strcmp(pService->m_pcService, p_pcService)) && - (0 == strcmp(pService->m_pcProtocol, p_pcProtocol))) { - - break; - } - pService = pService->m_pNext; - } - return pService; -} - -/* - * MDNSResponder::_findService - */ -MDNSResponder::stcMDNSService* MDNSResponder::_findService(const MDNSResponder::hMDNSService p_hService) { - - stcMDNSService* pService = m_pServices; - while (pService) { - if (p_hService == (hMDNSService)pService) { - break; - } - pService = pService->m_pNext; - } - return pService; -} - - -/* - * SERVICE TXT - */ - -/* - * MDNSResponder::_allocServiceTxt - */ -MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_allocServiceTxt(MDNSResponder::stcMDNSService* p_pService, - const char* p_pcKey, - const char* p_pcValue, - bool p_bTemp) { - - stcMDNSServiceTxt* pTxt = 0; - - if ((p_pService) && - (p_pcKey) && - (MDNS_SERVICE_TXT_MAXLENGTH > (p_pService->m_Txts.length() + - 1 + // Length byte - (p_pcKey ? strlen(p_pcKey) : 0) + - 1 + // '=' - (p_pcValue ? strlen(p_pcValue) : 0)))) { - - pTxt = new stcMDNSServiceTxt; - if (pTxt) { - size_t stLength = (p_pcKey ? strlen(p_pcKey) : 0); - pTxt->m_pcKey = new char[stLength + 1]; - if (pTxt->m_pcKey) { - strncpy(pTxt->m_pcKey, p_pcKey, stLength); pTxt->m_pcKey[stLength] = 0; - } - - if (p_pcValue) { - stLength = (p_pcValue ? strlen(p_pcValue) : 0); - pTxt->m_pcValue = new char[stLength + 1]; - if (pTxt->m_pcValue) { - strncpy(pTxt->m_pcValue, p_pcValue, stLength); pTxt->m_pcValue[stLength] = 0; - } - } - pTxt->m_bTemp = p_bTemp; - - // Add to list (or start list) - p_pService->m_Txts.add(pTxt); - } - } - return pTxt; -} - -/* - * MDNSResponder::_releaseServiceTxt - */ -bool MDNSResponder::_releaseServiceTxt(MDNSResponder::stcMDNSService* p_pService, - MDNSResponder::stcMDNSServiceTxt* p_pTxt) { - - return ((p_pService) && - (p_pTxt) && - (p_pService->m_Txts.remove(p_pTxt))); -} - -/* - * MDNSResponder::_updateServiceTxt - */ -MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_updateServiceTxt(MDNSResponder::stcMDNSService* p_pService, - MDNSResponder::stcMDNSServiceTxt* p_pTxt, - const char* p_pcValue, - bool p_bTemp) { - - if ((p_pService) && - (p_pTxt) && - (MDNS_SERVICE_TXT_MAXLENGTH > (p_pService->m_Txts.length() - - (p_pTxt->m_pcValue ? strlen(p_pTxt->m_pcValue) : 0) + - (p_pcValue ? strlen(p_pcValue) : 0)))) { - p_pTxt->update(p_pcValue); - p_pTxt->m_bTemp = p_bTemp; - } - return p_pTxt; -} - -/* - * MDNSResponder::_findServiceTxt - */ -MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_findServiceTxt(MDNSResponder::stcMDNSService* p_pService, - const char* p_pcKey) { - - return (p_pService ? p_pService->m_Txts.find(p_pcKey) : 0); -} - -/* - * MDNSResponder::_findServiceTxt - */ -MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_findServiceTxt(MDNSResponder::stcMDNSService* p_pService, - const hMDNSTxt p_hTxt) { - - return (((p_pService) && (p_hTxt)) ? p_pService->m_Txts.find((stcMDNSServiceTxt*)p_hTxt) : 0); -} - -/* - * MDNSResponder::_addServiceTxt - */ -MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_addServiceTxt(MDNSResponder::stcMDNSService* p_pService, - const char* p_pcKey, - const char* p_pcValue, - bool p_bTemp) { - stcMDNSServiceTxt* pResult = 0; - - if ((p_pService) && - (p_pcKey) && - (strlen(p_pcKey))) { - - stcMDNSServiceTxt* pTxt = p_pService->m_Txts.find(p_pcKey); - if (pTxt) { - pResult = _updateServiceTxt(p_pService, pTxt, p_pcValue, p_bTemp); - } - else { - pResult = _allocServiceTxt(p_pService, p_pcKey, p_pcValue, p_bTemp); - } - } - return pResult; -} - -MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_answerKeyValue(const hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex) { - stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); - stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); - // Fill m_pcTxts (if not already done) - return (pSQAnswer) ? pSQAnswer->m_Txts.m_pTxts : 0; -} - -/* - * MDNSResponder::_collectServiceTxts - */ -bool MDNSResponder::_collectServiceTxts(MDNSResponder::stcMDNSService& p_rService) { - - // Call Dynamic service callbacks - if (m_fnServiceTxtCallback) { - m_fnServiceTxtCallback((hMDNSService)&p_rService); - } - if (p_rService.m_fnTxtCallback) { - p_rService.m_fnTxtCallback((hMDNSService)&p_rService); - } - return true; -} - -/* - * MDNSResponder::_releaseTempServiceTxts - */ -bool MDNSResponder::_releaseTempServiceTxts(MDNSResponder::stcMDNSService& p_rService) { - - return (p_rService.m_Txts.removeTempTxts()); -} - - -/* - * MISC - */ - -#ifdef DEBUG_ESP_MDNS_RESPONDER - /* - * MDNSResponder::_printRRDomain - */ - bool MDNSResponder::_printRRDomain(const MDNSResponder::stcMDNS_RRDomain& p_RRDomain) const { - - //DEBUG_OUTPUT.printf_P(PSTR("Domain: ")); - - const char* pCursor = p_RRDomain.m_acName; - uint8_t u8Length = *pCursor++; - if (u8Length) { - while (u8Length) { - for (uint8_t u=0; um_IPAddress.toString().c_str()); - break; -#endif - case DNS_RRTYPE_PTR: - DEBUG_OUTPUT.printf_P(PSTR("PTR ")); - _printRRDomain(((const stcMDNS_RRAnswerPTR*)&p_RRAnswer)->m_PTRDomain); - break; - case DNS_RRTYPE_TXT: { - size_t stTxtLength = ((const stcMDNS_RRAnswerTXT*)&p_RRAnswer)->m_Txts.c_strLength(); - char* pTxts = new char[stTxtLength]; - if (pTxts) { - ((/*const c_str()!!*/stcMDNS_RRAnswerTXT*)&p_RRAnswer)->m_Txts.c_str(pTxts); - DEBUG_OUTPUT.printf_P(PSTR("TXT(%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 "), ((const stcMDNS_RRAnswerSRV*)&p_RRAnswer)->m_u16Port); - _printRRDomain(((const stcMDNS_RRAnswerSRV*)&p_RRAnswer)->m_SRVDomain); - break; - default: - DEBUG_OUTPUT.printf_P(PSTR("generic ")); - break; - } - DEBUG_OUTPUT.printf_P(PSTR("\n")); - - return true; - } -#endif - -} // namespace MDNSImplementation - -} // namespace esp8266 - - - - +/* + LEAmDNS_Helpers.cpp + + License (MIT license): + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + +*/ + +#include "lwip/igmp.h" + +#include "LEAmDNS_lwIPdefs.h" +#include "LEAmDNS_Priv.h" + + +namespace +{ + +/* + strrstr (static) + + Backwards search for p_pcPattern in p_pcString + Based on: https://stackoverflow.com/a/1634398/2778898 + +*/ +const char* strrstr(const char*__restrict p_pcString, const char*__restrict p_pcPattern) +{ + + const char* pcResult = 0; + + size_t stStringLength = (p_pcString ? strlen(p_pcString) : 0); + size_t stPatternLength = (p_pcPattern ? strlen(p_pcPattern) : 0); + + if ((stStringLength) && + (stPatternLength) && + (stPatternLength <= stStringLength)) + { + // Pattern is shorter or has the same length tham the string + + for (const char* s = (p_pcString + stStringLength - stPatternLength); s >= p_pcString; --s) + { + if (0 == strncmp(s, p_pcPattern, stPatternLength)) + { + pcResult = s; + break; + } + } + } + return pcResult; +} + + +} // anonymous + + + + + +namespace esp8266 +{ + +/* + LEAmDNS +*/ +namespace MDNSImplementation +{ + +/** + HELPERS +*/ + +/* + MDNSResponder::indexDomain (static) + + Updates the given domain 'p_rpcHostname' by appending a delimiter and an index number. + + If the given domain already hasa numeric index (after the given delimiter), this index + incremented. If not, the delimiter and index '2' is added. + + If 'p_rpcHostname' is empty (==0), the given default name 'p_pcDefaultHostname' is used, + if no default is given, 'esp8266' is used. + +*/ +/*static*/ bool MDNSResponder::indexDomain(char*& p_rpcDomain, + const char* p_pcDivider /*= "-"*/, + const char* p_pcDefaultDomain /*= 0*/) +{ + + bool bResult = false; + + // Ensure a divider exists; use '-' as default + const char* pcDivider = (p_pcDivider ? : "-"); + + if (p_rpcDomain) + { + const char* pFoundDivider = strrstr(p_rpcDomain, pcDivider); + if (pFoundDivider) // maybe already extended + { + char* pEnd = 0; + unsigned long ulIndex = strtoul((pFoundDivider + strlen(pcDivider)), &pEnd, 10); + if ((ulIndex) && + ((pEnd - p_rpcDomain) == (ptrdiff_t)strlen(p_rpcDomain)) && + (!*pEnd)) // Valid (old) index found + { + + char acIndexBuffer[16]; + sprintf(acIndexBuffer, "%lu", (++ulIndex)); + size_t stLength = ((pFoundDivider - p_rpcDomain + strlen(pcDivider)) + strlen(acIndexBuffer) + 1); + char* pNewHostname = new char[stLength]; + if (pNewHostname) + { + memcpy(pNewHostname, p_rpcDomain, (pFoundDivider - p_rpcDomain + strlen(pcDivider))); + pNewHostname[pFoundDivider - p_rpcDomain + strlen(pcDivider)] = 0; + strcat(pNewHostname, acIndexBuffer); + + delete[] p_rpcDomain; + p_rpcDomain = pNewHostname; + + bResult = true; + } + else + { + DEBUG_EX_ERR(DEBUG_OUTPUT.println(F("[MDNSResponder] indexDomain: FAILED to alloc new hostname!"));); + } + } + else + { + pFoundDivider = 0; // Flag the need to (base) extend the hostname + } + } + + if (!pFoundDivider) // not yet extended (or failed to increment extension) -> start indexing + { + size_t stLength = strlen(p_rpcDomain) + (strlen(pcDivider) + 1 + 1); // Name + Divider + '2' + '\0' + char* pNewHostname = new char[stLength]; + if (pNewHostname) + { + sprintf(pNewHostname, "%s%s2", p_rpcDomain, pcDivider); + + delete[] p_rpcDomain; + p_rpcDomain = pNewHostname; + + bResult = true; + } + else + { + DEBUG_EX_ERR(DEBUG_OUTPUT.println(F("[MDNSResponder] indexDomain: FAILED to alloc new hostname!"));); + } + } + } + else + { + // No given host domain, use base or default + const char* cpcDefaultName = (p_pcDefaultDomain ? : "esp8266"); + + size_t stLength = strlen(cpcDefaultName) + 1; // '\0' + p_rpcDomain = new char[stLength]; + if (p_rpcDomain) + { + strncpy(p_rpcDomain, cpcDefaultName, stLength); + bResult = true; + } + else + { + DEBUG_EX_ERR(DEBUG_OUTPUT.println(F("[MDNSResponder] indexDomain: FAILED to alloc new hostname!"));); + } + } + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] indexDomain: %s\n"), p_rpcDomain);); + return bResult; +} + + +/* + UDP CONTEXT +*/ + +bool MDNSResponder::_callProcess(void) +{ + DEBUG_EX_INFO(DEBUG_OUTPUT.printf("[MDNSResponder] _callProcess (%lu, triggered by: %s)\n", millis(), IPAddress(m_pUDPContext->getRemoteAddress()).toString().c_str());); + + return _process(false); +} + +/* + MDNSResponder::_allocUDPContext + + (Re-)Creates the one-and-only UDP context for the MDNS responder. + The context is added to the 'multicast'-group and listens to the MDNS port (5353). + The travel-distance for multicast messages is set to 1 (local, via MDNS_MULTICAST_TTL). + Messages are received via the MDNSResponder '_update' function. CAUTION: This function + is called from the WiFi stack side of the ESP stack system. + +*/ +bool MDNSResponder::_allocUDPContext(void) +{ + DEBUG_EX_INFO(DEBUG_OUTPUT.println("[MDNSResponder] _allocUDPContext");); + + bool bResult = false; + + _releaseUDPContext(); + +#ifdef MDNS_IP4_SUPPORT + ip_addr_t multicast_addr = DNS_MQUERY_IPV4_GROUP_INIT; +#endif +#ifdef MDNS_IP6_SUPPORT + //TODO: set multicast address (lwip_joingroup() is IPv4 only at the time of writing) + multicast_addr.addr = DNS_MQUERY_IPV6_GROUP_INIT; +#endif + if (ERR_OK == igmp_joingroup(ip_2_ip4(&m_netif->ip_addr), ip_2_ip4(&multicast_addr))) + { + m_pUDPContext = new UdpContext; + m_pUDPContext->ref(); + + if (m_pUDPContext->listen(IP4_ADDR_ANY, DNS_MQUERY_PORT)) + { + m_pUDPContext->setMulticastTTL(MDNS_MULTICAST_TTL); + m_pUDPContext->onRx(std::bind(&MDNSResponder::_callProcess, this)); + + bResult = m_pUDPContext->connect(&multicast_addr, DNS_MQUERY_PORT); + } + } + return bResult; +} + +/* + MDNSResponder::_releaseUDPContext +*/ +bool MDNSResponder::_releaseUDPContext(void) +{ + + if (m_pUDPContext) + { + m_pUDPContext->unref(); + m_pUDPContext = 0; + } + return true; +} + + +/* + SERVICE QUERY +*/ + +/* + MDNSResponder::_allocServiceQuery +*/ +MDNSResponder::stcMDNSServiceQuery* MDNSResponder::_allocServiceQuery(void) +{ + + stcMDNSServiceQuery* pServiceQuery = new stcMDNSServiceQuery; + if (pServiceQuery) + { + // Link to query list + pServiceQuery->m_pNext = m_pServiceQueries; + m_pServiceQueries = pServiceQuery; + } + return m_pServiceQueries; +} + +/* + MDNSResponder::_removeServiceQuery +*/ +bool MDNSResponder::_removeServiceQuery(MDNSResponder::stcMDNSServiceQuery* p_pServiceQuery) +{ + + bool bResult = false; + + if (p_pServiceQuery) + { + stcMDNSServiceQuery* pPred = m_pServiceQueries; + while ((pPred) && + (pPred->m_pNext != p_pServiceQuery)) + { + pPred = pPred->m_pNext; + } + if (pPred) + { + pPred->m_pNext = p_pServiceQuery->m_pNext; + delete p_pServiceQuery; + bResult = true; + } + else // No predecesor + { + if (m_pServiceQueries == p_pServiceQuery) + { + m_pServiceQueries = p_pServiceQuery->m_pNext; + delete p_pServiceQuery; + bResult = true; + } + else + { + DEBUG_EX_ERR(DEBUG_OUTPUT.println("[MDNSResponder] _releaseServiceQuery: INVALID service query!");); + } + } + } + return bResult; +} + +/* + MDNSResponder::_removeLegacyServiceQuery +*/ +bool MDNSResponder::_removeLegacyServiceQuery(void) +{ + + stcMDNSServiceQuery* pLegacyServiceQuery = _findLegacyServiceQuery(); + return (pLegacyServiceQuery ? _removeServiceQuery(pLegacyServiceQuery) : true); +} + +/* + MDNSResponder::_findServiceQuery + + 'Convert' hMDNSServiceQuery to stcMDNSServiceQuery* (ensure existance) + +*/ +MDNSResponder::stcMDNSServiceQuery* MDNSResponder::_findServiceQuery(MDNSResponder::hMDNSServiceQuery p_hServiceQuery) +{ + + stcMDNSServiceQuery* pServiceQuery = m_pServiceQueries; + while (pServiceQuery) + { + if ((hMDNSServiceQuery)pServiceQuery == p_hServiceQuery) + { + break; + } + pServiceQuery = pServiceQuery->m_pNext; + } + return pServiceQuery; +} + +/* + MDNSResponder::_findLegacyServiceQuery +*/ +MDNSResponder::stcMDNSServiceQuery* MDNSResponder::_findLegacyServiceQuery(void) +{ + + stcMDNSServiceQuery* pServiceQuery = m_pServiceQueries; + while (pServiceQuery) + { + if (pServiceQuery->m_bLegacyQuery) + { + break; + } + pServiceQuery = pServiceQuery->m_pNext; + } + return pServiceQuery; +} + +/* + MDNSResponder::_releaseServiceQueries +*/ +bool MDNSResponder::_releaseServiceQueries(void) +{ + while (m_pServiceQueries) + { + stcMDNSServiceQuery* pNext = m_pServiceQueries->m_pNext; + delete m_pServiceQueries; + m_pServiceQueries = pNext; + } + return true; +} + +/* + MDNSResponder::_findNextServiceQueryByServiceType +*/ +MDNSResponder::stcMDNSServiceQuery* MDNSResponder::_findNextServiceQueryByServiceType(const stcMDNS_RRDomain& p_ServiceTypeDomain, + const stcMDNSServiceQuery* p_pPrevServiceQuery) +{ + stcMDNSServiceQuery* pMatchingServiceQuery = 0; + + stcMDNSServiceQuery* pServiceQuery = (p_pPrevServiceQuery ? p_pPrevServiceQuery->m_pNext : m_pServiceQueries); + while (pServiceQuery) + { + if (p_ServiceTypeDomain == pServiceQuery->m_ServiceTypeDomain) + { + pMatchingServiceQuery = pServiceQuery; + break; + } + pServiceQuery = pServiceQuery->m_pNext; + } + return pMatchingServiceQuery; +} + + +/* + HOSTNAME +*/ + +/* + MDNSResponder::_setHostname +*/ +bool MDNSResponder::_setHostname(const char* p_pcHostname) +{ + //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _allocHostname (%s)\n"), p_pcHostname);); + + bool bResult = false; + + _releaseHostname(); + + size_t stLength = 0; + if ((p_pcHostname) && + (MDNS_DOMAIN_LABEL_MAXLENGTH >= (stLength = strlen(p_pcHostname)))) // char max size for a single label + { + // Copy in hostname characters as lowercase + if ((bResult = (0 != (m_pcHostname = new char[stLength + 1])))) + { +#ifdef MDNS_FORCE_LOWERCASE_HOSTNAME + size_t i = 0; + for (; i < stLength; ++i) + { + m_pcHostname[i] = (isupper(p_pcHostname[i]) ? tolower(p_pcHostname[i]) : p_pcHostname[i]); + } + m_pcHostname[i] = 0; +#else + strncpy(m_pcHostname, p_pcHostname, (stLength + 1)); +#endif + } + } + return bResult; +} + +/* + MDNSResponder::_releaseHostname +*/ +bool MDNSResponder::_releaseHostname(void) +{ + + if (m_pcHostname) + { + delete[] m_pcHostname; + m_pcHostname = 0; + } + return true; +} + + +/* + SERVICE +*/ + +/* + MDNSResponder::_allocService +*/ +MDNSResponder::stcMDNSService* MDNSResponder::_allocService(const char* p_pcName, + const char* p_pcService, + const char* p_pcProtocol, + uint16_t p_u16Port) +{ + + stcMDNSService* pService = 0; + if (((!p_pcName) || + (MDNS_DOMAIN_LABEL_MAXLENGTH >= strlen(p_pcName))) && + (p_pcService) && + (MDNS_SERVICE_NAME_LENGTH >= strlen(p_pcService)) && + (p_pcProtocol) && + (MDNS_SERVICE_PROTOCOL_LENGTH >= strlen(p_pcProtocol)) && + (p_u16Port) && + (0 != (pService = new stcMDNSService)) && + (pService->setName(p_pcName ? : m_pcHostname)) && + (pService->setService(p_pcService)) && + (pService->setProtocol(p_pcProtocol))) + { + + pService->m_bAutoName = (0 == p_pcName); + pService->m_u16Port = p_u16Port; + + // Add to list (or start list) + pService->m_pNext = m_pServices; + m_pServices = pService; + } + return pService; +} + +/* + MDNSResponder::_releaseService +*/ +bool MDNSResponder::_releaseService(MDNSResponder::stcMDNSService* p_pService) +{ + + bool bResult = false; + + if (p_pService) + { + stcMDNSService* pPred = m_pServices; + while ((pPred) && + (pPred->m_pNext != p_pService)) + { + pPred = pPred->m_pNext; + } + if (pPred) + { + pPred->m_pNext = p_pService->m_pNext; + delete p_pService; + bResult = true; + } + else // No predecesor + { + if (m_pServices == p_pService) + { + m_pServices = p_pService->m_pNext; + delete p_pService; + bResult = true; + } + else + { + DEBUG_EX_ERR(DEBUG_OUTPUT.println("[MDNSResponder] _releaseService: INVALID service!");); + } + } + } + return bResult; +} + +/* + MDNSResponder::_releaseServices +*/ +bool MDNSResponder::_releaseServices(void) +{ + + stcMDNSService* pService = m_pServices; + while (pService) + { + _releaseService(pService); + pService = m_pServices; + } + return true; +} + +/* + MDNSResponder::_findService +*/ +MDNSResponder::stcMDNSService* MDNSResponder::_findService(const char* p_pcName, + const char* p_pcService, + const char* p_pcProtocol) +{ + + stcMDNSService* pService = m_pServices; + while (pService) + { + if ((0 == strcmp(pService->m_pcName, p_pcName)) && + (0 == strcmp(pService->m_pcService, p_pcService)) && + (0 == strcmp(pService->m_pcProtocol, p_pcProtocol))) + { + + break; + } + pService = pService->m_pNext; + } + return pService; +} + +/* + MDNSResponder::_findService +*/ +MDNSResponder::stcMDNSService* MDNSResponder::_findService(const MDNSResponder::hMDNSService p_hService) +{ + + stcMDNSService* pService = m_pServices; + while (pService) + { + if (p_hService == (hMDNSService)pService) + { + break; + } + pService = pService->m_pNext; + } + return pService; +} + + +/* + SERVICE TXT +*/ + +/* + MDNSResponder::_allocServiceTxt +*/ +MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_allocServiceTxt(MDNSResponder::stcMDNSService* p_pService, + const char* p_pcKey, + const char* p_pcValue, + bool p_bTemp) +{ + + stcMDNSServiceTxt* pTxt = 0; + + if ((p_pService) && + (p_pcKey) && + (MDNS_SERVICE_TXT_MAXLENGTH > (p_pService->m_Txts.length() + + 1 + // Length byte + (p_pcKey ? strlen(p_pcKey) : 0) + + 1 + // '=' + (p_pcValue ? strlen(p_pcValue) : 0)))) + { + + pTxt = new stcMDNSServiceTxt; + if (pTxt) + { + size_t stLength = (p_pcKey ? strlen(p_pcKey) : 0); + pTxt->m_pcKey = new char[stLength + 1]; + if (pTxt->m_pcKey) + { + strncpy(pTxt->m_pcKey, p_pcKey, stLength); pTxt->m_pcKey[stLength] = 0; + } + + if (p_pcValue) + { + stLength = (p_pcValue ? strlen(p_pcValue) : 0); + pTxt->m_pcValue = new char[stLength + 1]; + if (pTxt->m_pcValue) + { + strncpy(pTxt->m_pcValue, p_pcValue, stLength); pTxt->m_pcValue[stLength] = 0; + } + } + pTxt->m_bTemp = p_bTemp; + + // Add to list (or start list) + p_pService->m_Txts.add(pTxt); + } + } + return pTxt; +} + +/* + MDNSResponder::_releaseServiceTxt +*/ +bool MDNSResponder::_releaseServiceTxt(MDNSResponder::stcMDNSService* p_pService, + MDNSResponder::stcMDNSServiceTxt* p_pTxt) +{ + + return ((p_pService) && + (p_pTxt) && + (p_pService->m_Txts.remove(p_pTxt))); +} + +/* + MDNSResponder::_updateServiceTxt +*/ +MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_updateServiceTxt(MDNSResponder::stcMDNSService* p_pService, + MDNSResponder::stcMDNSServiceTxt* p_pTxt, + const char* p_pcValue, + bool p_bTemp) +{ + + if ((p_pService) && + (p_pTxt) && + (MDNS_SERVICE_TXT_MAXLENGTH > (p_pService->m_Txts.length() - + (p_pTxt->m_pcValue ? strlen(p_pTxt->m_pcValue) : 0) + + (p_pcValue ? strlen(p_pcValue) : 0)))) + { + p_pTxt->update(p_pcValue); + p_pTxt->m_bTemp = p_bTemp; + } + return p_pTxt; +} + +/* + MDNSResponder::_findServiceTxt +*/ +MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_findServiceTxt(MDNSResponder::stcMDNSService* p_pService, + const char* p_pcKey) +{ + + return (p_pService ? p_pService->m_Txts.find(p_pcKey) : 0); +} + +/* + MDNSResponder::_findServiceTxt +*/ +MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_findServiceTxt(MDNSResponder::stcMDNSService* p_pService, + const hMDNSTxt p_hTxt) +{ + + return (((p_pService) && (p_hTxt)) ? p_pService->m_Txts.find((stcMDNSServiceTxt*)p_hTxt) : 0); +} + +/* + MDNSResponder::_addServiceTxt +*/ +MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_addServiceTxt(MDNSResponder::stcMDNSService* p_pService, + const char* p_pcKey, + const char* p_pcValue, + bool p_bTemp) +{ + stcMDNSServiceTxt* pResult = 0; + + if ((p_pService) && + (p_pcKey) && + (strlen(p_pcKey))) + { + + stcMDNSServiceTxt* pTxt = p_pService->m_Txts.find(p_pcKey); + if (pTxt) + { + pResult = _updateServiceTxt(p_pService, pTxt, p_pcValue, p_bTemp); + } + else + { + pResult = _allocServiceTxt(p_pService, p_pcKey, p_pcValue, p_bTemp); + } + } + return pResult; +} + +MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_answerKeyValue(const hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex) +{ + stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); + stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); + // Fill m_pcTxts (if not already done) + return (pSQAnswer) ? pSQAnswer->m_Txts.m_pTxts : 0; +} + +/* + MDNSResponder::_collectServiceTxts +*/ +bool MDNSResponder::_collectServiceTxts(MDNSResponder::stcMDNSService& p_rService) +{ + + // Call Dynamic service callbacks + if (m_fnServiceTxtCallback) + { + m_fnServiceTxtCallback((hMDNSService)&p_rService); + } + if (p_rService.m_fnTxtCallback) + { + p_rService.m_fnTxtCallback((hMDNSService)&p_rService); + } + return true; +} + +/* + MDNSResponder::_releaseTempServiceTxts +*/ +bool MDNSResponder::_releaseTempServiceTxts(MDNSResponder::stcMDNSService& p_rService) +{ + + return (p_rService.m_Txts.removeTempTxts()); +} + + +/* + MISC +*/ + +#ifdef DEBUG_ESP_MDNS_RESPONDER +/* + MDNSResponder::_printRRDomain +*/ +bool MDNSResponder::_printRRDomain(const MDNSResponder::stcMDNS_RRDomain& p_RRDomain) const +{ + + //DEBUG_OUTPUT.printf_P(PSTR("Domain: ")); + + const char* pCursor = p_RRDomain.m_acName; + uint8_t u8Length = *pCursor++; + if (u8Length) + { + while (u8Length) + { + for (uint8_t u = 0; u < u8Length; ++u) + { + DEBUG_OUTPUT.printf_P(PSTR("%c"), *(pCursor++)); + } + u8Length = *pCursor++; + if (u8Length) + { + DEBUG_OUTPUT.printf_P(PSTR(".")); + } + } + } + else // empty domain + { + DEBUG_OUTPUT.printf_P(PSTR("-empty-")); + } + //DEBUG_OUTPUT.printf_P(PSTR("\n")); + + return true; +} + +/* + MDNSResponder::_printRRAnswer +*/ +bool MDNSResponder::_printRRAnswer(const MDNSResponder::stcMDNS_RRAnswer& p_RRAnswer) const +{ + + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] RRAnswer: ")); + _printRRDomain(p_RRAnswer.m_Header.m_Domain); + DEBUG_OUTPUT.printf_P(PSTR(" Type:0x%04X Class:0x%04X TTL:%u, "), p_RRAnswer.m_Header.m_Attributes.m_u16Type, p_RRAnswer.m_Header.m_Attributes.m_u16Class, p_RRAnswer.m_u32TTL); + switch (p_RRAnswer.m_Header.m_Attributes.m_u16Type & (~0x8000)) // Topmost bit might carry 'cache flush' flag + { +#ifdef MDNS_IP4_SUPPORT + case DNS_RRTYPE_A: + DEBUG_OUTPUT.printf_P(PSTR("A IP:%s"), ((const stcMDNS_RRAnswerA*)&p_RRAnswer)->m_IPAddress.toString().c_str()); + break; +#endif + case DNS_RRTYPE_PTR: + DEBUG_OUTPUT.printf_P(PSTR("PTR ")); + _printRRDomain(((const stcMDNS_RRAnswerPTR*)&p_RRAnswer)->m_PTRDomain); + break; + case DNS_RRTYPE_TXT: + { + size_t stTxtLength = ((const stcMDNS_RRAnswerTXT*)&p_RRAnswer)->m_Txts.c_strLength(); + char* pTxts = new char[stTxtLength]; + if (pTxts) + { + ((/*const c_str()!!*/stcMDNS_RRAnswerTXT*)&p_RRAnswer)->m_Txts.c_str(pTxts); + DEBUG_OUTPUT.printf_P(PSTR("TXT(%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 "), ((const stcMDNS_RRAnswerSRV*)&p_RRAnswer)->m_u16Port); + _printRRDomain(((const stcMDNS_RRAnswerSRV*)&p_RRAnswer)->m_SRVDomain); + break; + default: + DEBUG_OUTPUT.printf_P(PSTR("generic ")); + break; + } + DEBUG_OUTPUT.printf_P(PSTR("\n")); + + return true; +} +#endif + +} // namespace MDNSImplementation + +} // namespace esp8266 + + + + diff --git a/libraries/ESP8266mDNS/src/LEAmDNS_Priv.h b/libraries/ESP8266mDNS/src/LEAmDNS_Priv.h index 6371478a96..cc56b133a9 100644 --- a/libraries/ESP8266mDNS/src/LEAmDNS_Priv.h +++ b/libraries/ESP8266mDNS/src/LEAmDNS_Priv.h @@ -1,180 +1,182 @@ -/* - * LEAmDNS_Priv.h - * - * 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. - * - */ - -#ifndef MDNS_PRIV_H -#define MDNS_PRIV_H - -namespace esp8266 { - -/* - * LEAmDNS - */ - -namespace MDNSImplementation { - -// Enable class debug functions -#define ESP_8266_MDNS_INCLUDE -//#define DEBUG_ESP_MDNS_RESPONDER - -#if !defined(DEBUG_ESP_MDNS_RESPONDER) && defined(DEBUG_ESP_MDNS) -#define DEBUG_ESP_MDNS_RESPONDER -#endif - -#ifndef LWIP_OPEN_SRC - #define LWIP_OPEN_SRC -#endif - -// -// If ENABLE_ESP_MDNS_RESPONDER_PASSIV_MODE is defined, the mDNS responder ignores a successful probing -// This allows to drive the responder in a environment, where 'update()' isn't called in the loop -//#define ENABLE_ESP_MDNS_RESPONDER_PASSIV_MODE - -// Enable/disable debug trace macros -#ifdef DEBUG_ESP_MDNS_RESPONDER -#define DEBUG_ESP_MDNS_INFO -#define DEBUG_ESP_MDNS_ERR -#define DEBUG_ESP_MDNS_TX -#define DEBUG_ESP_MDNS_RX -#endif - -#ifdef DEBUG_ESP_MDNS_RESPONDER - #ifdef DEBUG_ESP_MDNS_INFO - #define DEBUG_EX_INFO(A) A - #else - #define DEBUG_EX_INFO(A) do { (void)0; } while (0) - #endif - #ifdef DEBUG_ESP_MDNS_ERR - #define DEBUG_EX_ERR(A) A - #else - #define DEBUG_EX_ERR(A) do { (void)0; } while (0) - #endif - #ifdef DEBUG_ESP_MDNS_TX - #define DEBUG_EX_TX(A) A - #else - #define DEBUG_EX_TX(A) do { (void)0; } while (0) - #endif - #ifdef DEBUG_ESP_MDNS_RX - #define DEBUG_EX_RX(A) A - #else - #define DEBUG_EX_RX(A) do { (void)0; } while (0) - #endif - - #ifdef DEBUG_ESP_PORT - #define DEBUG_OUTPUT DEBUG_ESP_PORT - #else - #define DEBUG_OUTPUT Serial - #endif -#else - #define DEBUG_EX_INFO(A) do { (void)0; } while (0) - #define DEBUG_EX_ERR(A) do { (void)0; } while (0) - #define DEBUG_EX_TX(A) do { (void)0; } while (0) - #define DEBUG_EX_RX(A) do { (void)0; } while (0) -#endif - - -/* Replaced by 'lwip/prot/dns.h' definitions -#ifdef MDNS_IP4_SUPPORT - #define MDNS_MULTICAST_ADDR_IP4 (IPAddress(224, 0, 0, 251)) // ip_addr_t v4group = DNS_MQUERY_IPV4_GROUP_INIT -#endif -#ifdef MDNS_IP6_SUPPORT - #define MDNS_MULTICAST_ADDR_IP6 (IPAddress("FF02::FB")) // ip_addr_t v6group = DNS_MQUERY_IPV6_GROUP_INIT -#endif*/ -//#define MDNS_MULTICAST_PORT 5353 - -/* - * This is NOT the TTL (Time-To-Live) for MDNS records, but the - * subnet level distance MDNS records should travel. - * 1 sets the subnet distance to 'local', which is default for MDNS. - * (Btw.: 255 would set it to 'as far as possible' -> internet) - * - * However, RFC 3171 seems to force 255 instead - */ -#define MDNS_MULTICAST_TTL 255/*1*/ - -/* - * This is the MDNS record TTL - * Host level records are set to 2min (120s) - * service level records are set to 75min (4500s) - */ -#define MDNS_HOST_TTL 120 -#define MDNS_SERVICE_TTL 4500 - -/* - * Compressed labels are flaged by the two topmost bits of the length byte being set - */ -#define MDNS_DOMAIN_COMPRESS_MARK 0xC0 -/* - * Avoid endless recursion because of malformed compressed labels - */ -#define MDNS_DOMAIN_MAX_REDIRCTION 6 - -/* - * Default service priority and weight in SRV answers - */ -#define MDNS_SRV_PRIORITY 0 -#define MDNS_SRV_WEIGHT 0 - -/* - * Delay between and number of probes for host and service domains - * Delay between and number of announces for host and service domains - * Delay between and number of service queries; the delay is multiplied by the resent number in '_checkServiceQueryCache' - */ -#define MDNS_PROBE_DELAY 250 -#define MDNS_PROBE_COUNT 3 -#define MDNS_ANNOUNCE_DELAY 1000 -#define MDNS_ANNOUNCE_COUNT 8 -#define MDNS_DYNAMIC_QUERY_RESEND_COUNT 5 -#define MDNS_DYNAMIC_QUERY_RESEND_DELAY 5000 - - -/* - * Force host domain to use only lowercase letters - */ -//#define MDNS_FORCE_LOWERCASE_HOSTNAME - -/* - * Enable/disable the usage of the F() macro in debug trace printf calls. - * There needs to be an PGM comptible printf function to use this. - * - * USE_PGM_PRINTF and F - */ -#define USE_PGM_PRINTF - -#ifdef USE_PGM_PRINTF -#else - #ifdef F - #undef F - #endif - #define F(A) A -#endif - -} // namespace MDNSImplementation - -} // namespace esp8266 - -// Include the main header, so the submodlues only need to include this header -#include "LEAmDNS.h" - - -#endif // MDNS_PRIV_H +/* + LEAmDNS_Priv.h + + 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. + +*/ + +#ifndef MDNS_PRIV_H +#define MDNS_PRIV_H + +namespace esp8266 +{ + +/* + LEAmDNS +*/ + +namespace MDNSImplementation +{ + +// Enable class debug functions +#define ESP_8266_MDNS_INCLUDE +//#define DEBUG_ESP_MDNS_RESPONDER + +#if !defined(DEBUG_ESP_MDNS_RESPONDER) && defined(DEBUG_ESP_MDNS) +#define DEBUG_ESP_MDNS_RESPONDER +#endif + +#ifndef LWIP_OPEN_SRC +#define LWIP_OPEN_SRC +#endif + +// +// If ENABLE_ESP_MDNS_RESPONDER_PASSIV_MODE is defined, the mDNS responder ignores a successful probing +// This allows to drive the responder in a environment, where 'update()' isn't called in the loop +//#define ENABLE_ESP_MDNS_RESPONDER_PASSIV_MODE + +// Enable/disable debug trace macros +#ifdef DEBUG_ESP_MDNS_RESPONDER +#define DEBUG_ESP_MDNS_INFO +#define DEBUG_ESP_MDNS_ERR +#define DEBUG_ESP_MDNS_TX +#define DEBUG_ESP_MDNS_RX +#endif + +#ifdef DEBUG_ESP_MDNS_RESPONDER +#ifdef DEBUG_ESP_MDNS_INFO +#define DEBUG_EX_INFO(A) A +#else +#define DEBUG_EX_INFO(A) do { (void)0; } while (0) +#endif +#ifdef DEBUG_ESP_MDNS_ERR +#define DEBUG_EX_ERR(A) A +#else +#define DEBUG_EX_ERR(A) do { (void)0; } while (0) +#endif +#ifdef DEBUG_ESP_MDNS_TX +#define DEBUG_EX_TX(A) A +#else +#define DEBUG_EX_TX(A) do { (void)0; } while (0) +#endif +#ifdef DEBUG_ESP_MDNS_RX +#define DEBUG_EX_RX(A) A +#else +#define DEBUG_EX_RX(A) do { (void)0; } while (0) +#endif + +#ifdef DEBUG_ESP_PORT +#define DEBUG_OUTPUT DEBUG_ESP_PORT +#else +#define DEBUG_OUTPUT Serial +#endif +#else +#define DEBUG_EX_INFO(A) do { (void)0; } while (0) +#define DEBUG_EX_ERR(A) do { (void)0; } while (0) +#define DEBUG_EX_TX(A) do { (void)0; } while (0) +#define DEBUG_EX_RX(A) do { (void)0; } while (0) +#endif + + +/* Replaced by 'lwip/prot/dns.h' definitions + #ifdef MDNS_IP4_SUPPORT + #define MDNS_MULTICAST_ADDR_IP4 (IPAddress(224, 0, 0, 251)) // ip_addr_t v4group = DNS_MQUERY_IPV4_GROUP_INIT + #endif + #ifdef MDNS_IP6_SUPPORT + #define MDNS_MULTICAST_ADDR_IP6 (IPAddress("FF02::FB")) // ip_addr_t v6group = DNS_MQUERY_IPV6_GROUP_INIT + #endif*/ +//#define MDNS_MULTICAST_PORT 5353 + +/* + This is NOT the TTL (Time-To-Live) for MDNS records, but the + subnet level distance MDNS records should travel. + 1 sets the subnet distance to 'local', which is default for MDNS. + (Btw.: 255 would set it to 'as far as possible' -> internet) + + However, RFC 3171 seems to force 255 instead +*/ +#define MDNS_MULTICAST_TTL 255/*1*/ + +/* + This is the MDNS record TTL + Host level records are set to 2min (120s) + service level records are set to 75min (4500s) +*/ +#define MDNS_HOST_TTL 120 +#define MDNS_SERVICE_TTL 4500 + +/* + Compressed labels are flaged by the two topmost bits of the length byte being set +*/ +#define MDNS_DOMAIN_COMPRESS_MARK 0xC0 +/* + Avoid endless recursion because of malformed compressed labels +*/ +#define MDNS_DOMAIN_MAX_REDIRCTION 6 + +/* + Default service priority and weight in SRV answers +*/ +#define MDNS_SRV_PRIORITY 0 +#define MDNS_SRV_WEIGHT 0 + +/* + Delay between and number of probes for host and service domains + Delay between and number of announces for host and service domains + Delay between and number of service queries; the delay is multiplied by the resent number in '_checkServiceQueryCache' +*/ +#define MDNS_PROBE_DELAY 250 +#define MDNS_PROBE_COUNT 3 +#define MDNS_ANNOUNCE_DELAY 1000 +#define MDNS_ANNOUNCE_COUNT 8 +#define MDNS_DYNAMIC_QUERY_RESEND_COUNT 5 +#define MDNS_DYNAMIC_QUERY_RESEND_DELAY 5000 + + +/* + Force host domain to use only lowercase letters +*/ +//#define MDNS_FORCE_LOWERCASE_HOSTNAME + +/* + Enable/disable the usage of the F() macro in debug trace printf calls. + There needs to be an PGM comptible printf function to use this. + + USE_PGM_PRINTF and F +*/ +#define USE_PGM_PRINTF + +#ifdef USE_PGM_PRINTF +#else +#ifdef F +#undef F +#endif +#define F(A) A +#endif + +} // namespace MDNSImplementation + +} // namespace esp8266 + +// Include the main header, so the submodlues only need to include this header +#include "LEAmDNS.h" + + +#endif // MDNS_PRIV_H diff --git a/libraries/ESP8266mDNS/src/LEAmDNS_Structs.cpp b/libraries/ESP8266mDNS/src/LEAmDNS_Structs.cpp index e54beb50db..ce475de3ba 100644 --- a/libraries/ESP8266mDNS/src/LEAmDNS_Structs.cpp +++ b/libraries/ESP8266mDNS/src/LEAmDNS_Structs.cpp @@ -1,2218 +1,2476 @@ -/* - * LEAmDNS_Structs.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" -#include "LEAmDNS_lwIPdefs.h" - -namespace esp8266 { - -/* - * LEAmDNS - */ -namespace MDNSImplementation { - -/** - * STRUCTS - */ - -/** - * MDNSResponder::stcMDNSServiceTxt - * - * One MDNS TXT item. - * m_pcValue may be '\0'. - * Objects can be chained together (list, m_pNext). - * A 'm_bTemp' flag differentiates between static and dynamic items. - * Output as byte array 'c#=1' is supported. - */ - -/* - * MDNSResponder::stcMDNSServiceTxt::stcMDNSServiceTxt constructor - */ -MDNSResponder::stcMDNSServiceTxt::stcMDNSServiceTxt(const char* p_pcKey /*= 0*/, - const char* p_pcValue /*= 0*/, - bool p_bTemp /*= false*/) -: m_pNext(0), - m_pcKey(0), - m_pcValue(0), - m_bTemp(p_bTemp) { - - setKey(p_pcKey); - setValue(p_pcValue); -} - -/* - * MDNSResponder::stcMDNSServiceTxt::stcMDNSServiceTxt copy-constructor - */ -MDNSResponder::stcMDNSServiceTxt::stcMDNSServiceTxt(const MDNSResponder::stcMDNSServiceTxt& p_Other) -: m_pNext(0), - m_pcKey(0), - m_pcValue(0), - m_bTemp(false) { - - operator=(p_Other); -} - -/* - * MDNSResponder::stcMDNSServiceTxt::~stcMDNSServiceTxt destructor - */ -MDNSResponder::stcMDNSServiceTxt::~stcMDNSServiceTxt(void) { - - clear(); -} - -/* - * MDNSResponder::stcMDNSServiceTxt::operator= - */ -MDNSResponder::stcMDNSServiceTxt& MDNSResponder::stcMDNSServiceTxt::operator=(const MDNSResponder::stcMDNSServiceTxt& p_Other) { - - if (&p_Other != this) { - clear(); - set(p_Other.m_pcKey, p_Other.m_pcValue, p_Other.m_bTemp); - } - return *this; -} - -/* - * MDNSResponder::stcMDNSServiceTxt::clear - */ -bool MDNSResponder::stcMDNSServiceTxt::clear(void) { - - releaseKey(); - releaseValue(); - return true; -} - -/* - * MDNSResponder::stcMDNSServiceTxt::allocKey - */ -char* MDNSResponder::stcMDNSServiceTxt::allocKey(size_t p_stLength) { - - releaseKey(); - if (p_stLength) { - m_pcKey = new char[p_stLength + 1]; - } - return m_pcKey; -} - -/* - * MDNSResponder::stcMDNSServiceTxt::setKey - */ -bool MDNSResponder::stcMDNSServiceTxt::setKey(const char* p_pcKey, - size_t p_stLength) { - - bool bResult = false; - - releaseKey(); - if (p_stLength) { - if (allocKey(p_stLength)) { - strncpy(m_pcKey, p_pcKey, p_stLength); - m_pcKey[p_stLength] = 0; - bResult = true; - } - } - return bResult; -} - -/* - * MDNSResponder::stcMDNSServiceTxt::setKey - */ -bool MDNSResponder::stcMDNSServiceTxt::setKey(const char* p_pcKey) { - - return setKey(p_pcKey, (p_pcKey ? strlen(p_pcKey) : 0)); -} - -/* - * MDNSResponder::stcMDNSServiceTxt::releaseKey - */ -bool MDNSResponder::stcMDNSServiceTxt::releaseKey(void) { - - if (m_pcKey) { - delete[] m_pcKey; - m_pcKey = 0; - } - return true; -} - -/* - * MDNSResponder::stcMDNSServiceTxt::allocValue - */ -char* MDNSResponder::stcMDNSServiceTxt::allocValue(size_t p_stLength) { - - releaseValue(); - if (p_stLength) { - m_pcValue = new char[p_stLength + 1]; - } - return m_pcValue; -} - -/* - * MDNSResponder::stcMDNSServiceTxt::setValue - */ -bool MDNSResponder::stcMDNSServiceTxt::setValue(const char* p_pcValue, - size_t p_stLength) { - - bool bResult = false; - - releaseValue(); - if (p_stLength) { - if (allocValue(p_stLength)) { - strncpy(m_pcValue, p_pcValue, p_stLength); - m_pcValue[p_stLength] = 0; - bResult = true; - } - } - else { // No value -> also OK - bResult = true; - } - return bResult; -} - -/* - * MDNSResponder::stcMDNSServiceTxt::setValue - */ -bool MDNSResponder::stcMDNSServiceTxt::setValue(const char* p_pcValue) { - - return setValue(p_pcValue, (p_pcValue ? strlen(p_pcValue) : 0)); -} - -/* - * MDNSResponder::stcMDNSServiceTxt::releaseValue - */ -bool MDNSResponder::stcMDNSServiceTxt::releaseValue(void) { - - if (m_pcValue) { - delete[] m_pcValue; - m_pcValue = 0; - } - return true; -} - -/* - * MDNSResponder::stcMDNSServiceTxt::set - */ -bool MDNSResponder::stcMDNSServiceTxt::set(const char* p_pcKey, - const char* p_pcValue, - bool p_bTemp /*= false*/) { - - m_bTemp = p_bTemp; - return ((setKey(p_pcKey)) && - (setValue(p_pcValue))); -} - -/* - * MDNSResponder::stcMDNSServiceTxt::update - */ -bool MDNSResponder::stcMDNSServiceTxt::update(const char* p_pcValue) { - - return setValue(p_pcValue); -} - -/* - * MDNSResponder::stcMDNSServiceTxt::length - * - * length of eg. 'c#=1' without any closing '\0' - */ -size_t MDNSResponder::stcMDNSServiceTxt::length(void) const { - - size_t stLength = 0; - if (m_pcKey) { - stLength += strlen(m_pcKey); // Key - stLength += 1; // '=' - stLength += (m_pcValue ? strlen(m_pcValue) : 0); // Value - } - return stLength; -} - - -/** - * MDNSResponder::stcMDNSServiceTxts - * - * A list of zero or more MDNS TXT items. - * Dynamic TXT items can be removed by 'removeTempTxts'. - * A TXT item can be looke up by its 'key' member. - * Export as ';'-separated byte array is supported. - * Export as 'length byte coded' byte array is supported. - * Comparision ((all A TXT items in B and equal) AND (all B TXT items in A and equal)) is supported. - * - */ - -/* - * MDNSResponder::stcMDNSServiceTxts::stcMDNSServiceTxts contructor - */ -MDNSResponder::stcMDNSServiceTxts::stcMDNSServiceTxts(void) -: m_pTxts(0) { - -} - -/* - * MDNSResponder::stcMDNSServiceTxts::stcMDNSServiceTxts copy-constructor - */ -MDNSResponder::stcMDNSServiceTxts::stcMDNSServiceTxts(const stcMDNSServiceTxts& p_Other) -: m_pTxts(0) { - - operator=(p_Other); -} - -/* - * MDNSResponder::stcMDNSServiceTxts::~stcMDNSServiceTxts destructor - */ -MDNSResponder::stcMDNSServiceTxts::~stcMDNSServiceTxts(void) { - - clear(); -} - -/* - * MDNSResponder::stcMDNSServiceTxts::operator= - */ -MDNSResponder::stcMDNSServiceTxts& MDNSResponder::stcMDNSServiceTxts::operator=(const stcMDNSServiceTxts& p_Other) { - - if (this != &p_Other) { - clear(); - - for (stcMDNSServiceTxt* pOtherTxt=p_Other.m_pTxts; pOtherTxt; pOtherTxt=pOtherTxt->m_pNext) { - add(new stcMDNSServiceTxt(*pOtherTxt)); - } - } - return *this; -} - -/* - * MDNSResponder::stcMDNSServiceTxts::clear - */ -bool MDNSResponder::stcMDNSServiceTxts::clear(void) { - - while (m_pTxts) { - stcMDNSServiceTxt* pNext = m_pTxts->m_pNext; - delete m_pTxts; - m_pTxts = pNext; - } - return true; -} - -/* - * MDNSResponder::stcMDNSServiceTxts::add - */ -bool MDNSResponder::stcMDNSServiceTxts::add(MDNSResponder::stcMDNSServiceTxt* p_pTxt) { - - bool bResult = false; - - if (p_pTxt) { - p_pTxt->m_pNext = m_pTxts; - m_pTxts = p_pTxt; - bResult = true; - } - return bResult; -} - -/* - * MDNSResponder::stcMDNSServiceTxts::remove - */ -bool MDNSResponder::stcMDNSServiceTxts::remove(stcMDNSServiceTxt* p_pTxt) { - - bool bResult = false; - - if (p_pTxt) { - stcMDNSServiceTxt* pPred = m_pTxts; - while ((pPred) && - (pPred->m_pNext != p_pTxt)) { - pPred = pPred->m_pNext; - } - if (pPred) { - pPred->m_pNext = p_pTxt->m_pNext; - delete p_pTxt; - bResult = true; - } - else if (m_pTxts == p_pTxt) { // No predecesor, but first item - m_pTxts = p_pTxt->m_pNext; - delete p_pTxt; - bResult = true; - } - } - return bResult; -} - -/* - * MDNSResponder::stcMDNSServiceTxts::removeTempTxts - */ -bool MDNSResponder::stcMDNSServiceTxts::removeTempTxts(void) { - - bool bResult = true; - - stcMDNSServiceTxt* pTxt = m_pTxts; - while ((bResult) && - (pTxt)) { - stcMDNSServiceTxt* pNext = pTxt->m_pNext; - if (pTxt->m_bTemp) { - bResult = remove(pTxt); - } - pTxt = pNext; - } - return bResult; -} - -/* - * MDNSResponder::stcMDNSServiceTxts::find - */ -MDNSResponder::stcMDNSServiceTxt* MDNSResponder::stcMDNSServiceTxts::find(const char* p_pcKey) { - - stcMDNSServiceTxt* pResult = 0; - - for (stcMDNSServiceTxt* pTxt=m_pTxts; pTxt; pTxt=pTxt->m_pNext) { - if ((p_pcKey) && - (0 == strcmp(pTxt->m_pcKey, p_pcKey))) { - pResult = pTxt; - break; - } - } - return pResult; -} - -/* - * MDNSResponder::stcMDNSServiceTxts::find - */ -const MDNSResponder::stcMDNSServiceTxt* MDNSResponder::stcMDNSServiceTxts::find(const char* p_pcKey) const { - - const stcMDNSServiceTxt* pResult = 0; - - for (const stcMDNSServiceTxt* pTxt=m_pTxts; pTxt; pTxt=pTxt->m_pNext) { - if ((p_pcKey) && - (0 == strcmp(pTxt->m_pcKey, p_pcKey))) { - - pResult = pTxt; - break; - } - } - return pResult; -} - -/* - * MDNSResponder::stcMDNSServiceTxts::find - */ -MDNSResponder::stcMDNSServiceTxt* MDNSResponder::stcMDNSServiceTxts::find(const stcMDNSServiceTxt* p_pTxt) { - - stcMDNSServiceTxt* pResult = 0; - - for (stcMDNSServiceTxt* pTxt=m_pTxts; pTxt; pTxt=pTxt->m_pNext) { - if (p_pTxt == pTxt) { - pResult = pTxt; - break; - } - } - return pResult; -} - -/* - * MDNSResponder::stcMDNSServiceTxts::length - */ -uint16_t MDNSResponder::stcMDNSServiceTxts::length(void) const { - - uint16_t u16Length = 0; - - stcMDNSServiceTxt* pTxt = m_pTxts; - while (pTxt) { - u16Length += 1; // Length byte - u16Length += pTxt->length(); // Text - pTxt = pTxt->m_pNext; - } - return u16Length; -} - -/* - * MDNSResponder::stcMDNSServiceTxts::c_strLength - * - * (incl. closing '\0'). Length bytes place is used for delimiting ';' and closing '\0' - */ -size_t MDNSResponder::stcMDNSServiceTxts::c_strLength(void) const { - - return length(); -} - -/* - * MDNSResponder::stcMDNSServiceTxts::c_str - */ -bool MDNSResponder::stcMDNSServiceTxts::c_str(char* p_pcBuffer) { - - bool bResult = false; - - if (p_pcBuffer) { - bResult = true; - - *p_pcBuffer = 0; - for (stcMDNSServiceTxt* pTxt=m_pTxts; ((bResult) && (pTxt)); pTxt = pTxt->m_pNext) { - size_t stLength; - if ((bResult = (0 != (stLength = (pTxt->m_pcKey ? strlen(pTxt->m_pcKey) : 0))))) { - if (pTxt != m_pTxts) { - *p_pcBuffer++ = ';'; - } - strncpy(p_pcBuffer, pTxt->m_pcKey, stLength); p_pcBuffer[stLength] = 0; - p_pcBuffer += stLength; - *p_pcBuffer++ = '='; - if ((stLength = (pTxt->m_pcValue ? strlen(pTxt->m_pcValue) : 0))) { - strncpy(p_pcBuffer, pTxt->m_pcValue, stLength); p_pcBuffer[stLength] = 0; - p_pcBuffer += stLength; - } - } - } - *p_pcBuffer++ = 0; - } - return bResult; -} - -/* - * MDNSResponder::stcMDNSServiceTxts::bufferLength - * - * (incl. closing '\0'). - */ -size_t MDNSResponder::stcMDNSServiceTxts::bufferLength(void) const { - - return (length() + 1); -} - -/* - * MDNSResponder::stcMDNSServiceTxts::toBuffer - */ -bool MDNSResponder::stcMDNSServiceTxts::buffer(char* p_pcBuffer) { - - bool bResult = false; - - if (p_pcBuffer) { - bResult = true; - - *p_pcBuffer = 0; - for (stcMDNSServiceTxt* pTxt=m_pTxts; ((bResult) && (pTxt)); pTxt = pTxt->m_pNext) { - *(unsigned char*)p_pcBuffer++ = pTxt->length(); - size_t stLength; - if ((bResult = (0 != (stLength = (pTxt->m_pcKey ? strlen(pTxt->m_pcKey) : 0))))) { - memcpy(p_pcBuffer, pTxt->m_pcKey, stLength); - p_pcBuffer += stLength; - *p_pcBuffer++ = '='; - if ((stLength = (pTxt->m_pcValue ? strlen(pTxt->m_pcValue) : 0))) { - memcpy(p_pcBuffer, pTxt->m_pcValue, stLength); - p_pcBuffer += stLength; - } - } - } - *p_pcBuffer++ = 0; - } - return bResult; -} - -/* - * MDNSResponder::stcMDNSServiceTxts::compare - */ -bool MDNSResponder::stcMDNSServiceTxts::compare(const MDNSResponder::stcMDNSServiceTxts& p_Other) const { - - bool bResult = false; - - if ((bResult = (length() == p_Other.length()))) { - // Compare A->B - for (const stcMDNSServiceTxt* pTxt=m_pTxts; ((bResult) && (pTxt)); pTxt=pTxt->m_pNext) { - const stcMDNSServiceTxt* pOtherTxt = p_Other.find(pTxt->m_pcKey); - bResult = ((pOtherTxt) && - (pTxt->m_pcValue) && - (pOtherTxt->m_pcValue) && - (strlen(pTxt->m_pcValue) == strlen(pOtherTxt->m_pcValue)) && - (0 == strcmp(pTxt->m_pcValue, pOtherTxt->m_pcValue))); - } - // Compare B->A - for (const stcMDNSServiceTxt* pOtherTxt=p_Other.m_pTxts; ((bResult) && (pOtherTxt)); pOtherTxt=pOtherTxt->m_pNext) { - const stcMDNSServiceTxt* pTxt = find(pOtherTxt->m_pcKey); - bResult = ((pTxt) && - (pOtherTxt->m_pcValue) && - (pTxt->m_pcValue) && - (strlen(pOtherTxt->m_pcValue) == strlen(pTxt->m_pcValue)) && - (0 == strcmp(pOtherTxt->m_pcValue, pTxt->m_pcValue))); - } - } - return bResult; -} - -/* - * MDNSResponder::stcMDNSServiceTxts::operator== - */ -bool MDNSResponder::stcMDNSServiceTxts::operator==(const stcMDNSServiceTxts& p_Other) const { - - return compare(p_Other); -} - -/* - * MDNSResponder::stcMDNSServiceTxts::operator!= - */ -bool MDNSResponder::stcMDNSServiceTxts::operator!=(const stcMDNSServiceTxts& p_Other) const { - - return !compare(p_Other); -} - - -/** - * MDNSResponder::stcMDNS_MsgHeader - * - * A MDNS message haeder. - * - */ - -/* - * MDNSResponder::stcMDNS_MsgHeader::stcMDNS_MsgHeader - */ -MDNSResponder::stcMDNS_MsgHeader::stcMDNS_MsgHeader(uint16_t p_u16ID /*= 0*/, - bool p_bQR /*= false*/, - unsigned char p_ucOpcode /*= 0*/, - bool p_bAA /*= false*/, - bool p_bTC /*= false*/, - bool p_bRD /*= false*/, - bool p_bRA /*= false*/, - unsigned char p_ucRCode /*= 0*/, - uint16_t p_u16QDCount /*= 0*/, - uint16_t p_u16ANCount /*= 0*/, - uint16_t p_u16NSCount /*= 0*/, - uint16_t p_u16ARCount /*= 0*/) -: m_u16ID(p_u16ID), - m_1bQR(p_bQR), m_4bOpcode(p_ucOpcode), m_1bAA(p_bAA), m_1bTC(p_bTC), m_1bRD(p_bRD), - m_1bRA(p_bRA), m_3bZ(0), m_4bRCode(p_ucRCode), - m_u16QDCount(p_u16QDCount), - m_u16ANCount(p_u16ANCount), - m_u16NSCount(p_u16NSCount), - m_u16ARCount(p_u16ARCount) { - -} - - -/** - * MDNSResponder::stcMDNS_RRDomain - * - * A MDNS domain object. - * The labels of the domain are stored (DNS-like encoded) in 'm_acName': - * [length byte]varlength label[length byte]varlength label[0] - * 'm_u16NameLength' stores the used length of 'm_acName'. - * Dynamic label addition is supported. - * Comparison is supported. - * Export as byte array 'esp8266.local' is supported. - * - */ - -/* - * MDNSResponder::stcMDNS_RRDomain::stcMDNS_RRDomain constructor - */ -MDNSResponder::stcMDNS_RRDomain::stcMDNS_RRDomain(void) -: m_u16NameLength(0) { - - clear(); -} - -/* - * MDNSResponder::stcMDNS_RRDomain::stcMDNS_RRDomain copy-constructor - */ -MDNSResponder::stcMDNS_RRDomain::stcMDNS_RRDomain(const stcMDNS_RRDomain& p_Other) -: m_u16NameLength(0) { - - operator=(p_Other); -} - -/* - * MDNSResponder::stcMDNS_RRDomain::operator = - */ -MDNSResponder::stcMDNS_RRDomain& MDNSResponder::stcMDNS_RRDomain::operator=(const stcMDNS_RRDomain& p_Other) { - - if (&p_Other != this) { - memcpy(m_acName, p_Other.m_acName, sizeof(m_acName)); - m_u16NameLength = p_Other.m_u16NameLength; - } - return *this; -} - -/* - * MDNSResponder::stcMDNS_RRDomain::clear - */ -bool MDNSResponder::stcMDNS_RRDomain::clear(void) { - - memset(m_acName, 0, sizeof(m_acName)); - m_u16NameLength = 0; - return true; -} - -/* - * MDNSResponder::stcMDNS_RRDomain::addLabel - */ -bool MDNSResponder::stcMDNS_RRDomain::addLabel(const char* p_pcLabel, - bool p_bPrependUnderline /*= false*/) { - - bool bResult = false; - - size_t stLength = (p_pcLabel - ? (strlen(p_pcLabel) + (p_bPrependUnderline ? 1 : 0)) - : 0); - if ((MDNS_DOMAIN_LABEL_MAXLENGTH >= stLength) && - (MDNS_DOMAIN_MAXLENGTH >= (m_u16NameLength + (1 + stLength)))) { - // Length byte - m_acName[m_u16NameLength] = (unsigned char)stLength; // Might be 0! - ++m_u16NameLength; - // Label - if (stLength) { - if (p_bPrependUnderline) { - m_acName[m_u16NameLength++] = '_'; - --stLength; - } - strncpy(&(m_acName[m_u16NameLength]), p_pcLabel, stLength); m_acName[m_u16NameLength + stLength] = 0; - m_u16NameLength += stLength; - } - bResult = true; - } - return bResult; -} - -/* - * MDNSResponder::stcMDNS_RRDomain::compare - */ -bool MDNSResponder::stcMDNS_RRDomain::compare(const stcMDNS_RRDomain& p_Other) const { - - bool bResult = false; - - if (m_u16NameLength == p_Other.m_u16NameLength) { - const char* pT = m_acName; - const char* pO = p_Other.m_acName; - while ((pT) && - (pO) && - (*((unsigned char*)pT) == *((unsigned char*)pO)) && // Same length AND - (0 == strncasecmp((pT + 1), (pO + 1), *((unsigned char*)pT)))) { // Same content - if (*((unsigned char*)pT)) { // Not 0 - pT += (1 + *((unsigned char*)pT)); // Shift by length byte and lenght - pO += (1 + *((unsigned char*)pO)); - } - else { // Is 0 -> Successfully reached the end - bResult = true; - break; - } - } - } - return bResult; -} - -/* - * MDNSResponder::stcMDNS_RRDomain::operator == - */ -bool MDNSResponder::stcMDNS_RRDomain::operator==(const stcMDNS_RRDomain& p_Other) const { - - return compare(p_Other); -} - -/* - * MDNSResponder::stcMDNS_RRDomain::operator != - */ -bool MDNSResponder::stcMDNS_RRDomain::operator!=(const stcMDNS_RRDomain& p_Other) const { - - return !compare(p_Other); -} - -/* - * MDNSResponder::stcMDNS_RRDomain::operator > - */ -bool MDNSResponder::stcMDNS_RRDomain::operator>(const stcMDNS_RRDomain& p_Other) const { - - // TODO: Check, if this is a good idea... - return !compare(p_Other); -} - -/* - * MDNSResponder::stcMDNS_RRDomain::c_strLength - */ -size_t MDNSResponder::stcMDNS_RRDomain::c_strLength(void) const { - - size_t stLength = 0; - - unsigned char* pucLabelLength = (unsigned char*)m_acName; - while (*pucLabelLength) { - stLength += (*pucLabelLength + 1 /* +1 for '.' or '\0'*/); - pucLabelLength += (*pucLabelLength + 1); - } - return stLength; -} - -/* - * MDNSResponder::stcMDNS_RRDomain::c_str - */ -bool MDNSResponder::stcMDNS_RRDomain::c_str(char* p_pcBuffer) { - - bool bResult = false; - - if (p_pcBuffer) { - *p_pcBuffer = 0; - unsigned char* pucLabelLength = (unsigned char*)m_acName; - while (*pucLabelLength) { - memcpy(p_pcBuffer, (const char*)(pucLabelLength + 1), *pucLabelLength); - p_pcBuffer += *pucLabelLength; - pucLabelLength += (*pucLabelLength + 1); - *p_pcBuffer++ = (*pucLabelLength ? '.' : '\0'); - } - bResult = true; - } - return bResult; -} - - -/** - * MDNSResponder::stcMDNS_RRAttributes - * - * A MDNS attributes object. - * - */ - -/* - * MDNSResponder::stcMDNS_RRAttributes::stcMDNS_RRAttributes constructor - */ -MDNSResponder::stcMDNS_RRAttributes::stcMDNS_RRAttributes(uint16_t p_u16Type /*= 0*/, - uint16_t p_u16Class /*= 1 DNS_RRCLASS_IN Internet*/) -: m_u16Type(p_u16Type), - m_u16Class(p_u16Class) { - -} - -/* - * MDNSResponder::stcMDNS_RRAttributes::stcMDNS_RRAttributes copy-constructor - */ -MDNSResponder::stcMDNS_RRAttributes::stcMDNS_RRAttributes(const MDNSResponder::stcMDNS_RRAttributes& p_Other) { - - operator=(p_Other); -} - -/* - * MDNSResponder::stcMDNS_RRAttributes::operator = - */ -MDNSResponder::stcMDNS_RRAttributes& MDNSResponder::stcMDNS_RRAttributes::operator=(const MDNSResponder::stcMDNS_RRAttributes& p_Other) { - - if (&p_Other != this) { - m_u16Type = p_Other.m_u16Type; - m_u16Class = p_Other.m_u16Class; - } - return *this; -} - - -/** - * MDNSResponder::stcMDNS_RRHeader - * - * A MDNS record header (domain and attributes) object. - * - */ - -/* - * MDNSResponder::stcMDNS_RRHeader::stcMDNS_RRHeader constructor - */ -MDNSResponder::stcMDNS_RRHeader::stcMDNS_RRHeader(void) { - -} - -/* - * MDNSResponder::stcMDNS_RRHeader::stcMDNS_RRHeader copy-constructor - */ -MDNSResponder::stcMDNS_RRHeader::stcMDNS_RRHeader(const stcMDNS_RRHeader& p_Other) { - - operator=(p_Other); -} - -/* - * MDNSResponder::stcMDNS_RRHeader::operator = - */ -MDNSResponder::stcMDNS_RRHeader& MDNSResponder::stcMDNS_RRHeader::operator=(const MDNSResponder::stcMDNS_RRHeader& p_Other) { - - if (&p_Other != this) { - m_Domain = p_Other.m_Domain; - m_Attributes = p_Other.m_Attributes; - } - return *this; -} - -/* - * MDNSResponder::stcMDNS_RRHeader::clear - */ -bool MDNSResponder::stcMDNS_RRHeader::clear(void) { - - m_Domain.clear(); - return true; -} - - -/** - * MDNSResponder::stcMDNS_RRQuestion - * - * A MDNS question record object (header + question flags) - * - */ - -/* - * MDNSResponder::stcMDNS_RRQuestion::stcMDNS_RRQuestion constructor - */ -MDNSResponder::stcMDNS_RRQuestion::stcMDNS_RRQuestion(void) -: m_pNext(0), - m_bUnicast(false) { - -} - - -/** - * MDNSResponder::stcMDNS_RRAnswer - * - * A MDNS answer record object (header + answer content). - * This is a 'virtual' base class for all other MDNS answer classes. - * - */ - -/* - * MDNSResponder::stcMDNS_RRAnswer::stcMDNS_RRAnswer constructor - */ -MDNSResponder::stcMDNS_RRAnswer::stcMDNS_RRAnswer(enuAnswerType p_AnswerType, - const MDNSResponder::stcMDNS_RRHeader& p_Header, - uint32_t p_u32TTL) -: m_pNext(0), - m_AnswerType(p_AnswerType), - m_Header(p_Header), - m_u32TTL(p_u32TTL) { - - // Extract 'cache flush'-bit - m_bCacheFlush = (m_Header.m_Attributes.m_u16Class & 0x8000); - m_Header.m_Attributes.m_u16Class &= (~0x8000); -} - -/* - * MDNSResponder::stcMDNS_RRAnswer::~stcMDNS_RRAnswer destructor - */ -MDNSResponder::stcMDNS_RRAnswer::~stcMDNS_RRAnswer(void) { - -} - -/* - * MDNSResponder::stcMDNS_RRAnswer::answerType - */ -MDNSResponder::enuAnswerType MDNSResponder::stcMDNS_RRAnswer::answerType(void) const { - - return m_AnswerType; -} - -/* - * MDNSResponder::stcMDNS_RRAnswer::clear - */ -bool MDNSResponder::stcMDNS_RRAnswer::clear(void) { - - m_pNext = 0; - m_Header.clear(); - return true; -} - - -/** - * MDNSResponder::stcMDNS_RRAnswerA - * - * A MDNS A answer object. - * Extends the base class by an IP4 address member. - * - */ - -#ifdef MDNS_IP4_SUPPORT - /* - * MDNSResponder::stcMDNS_RRAnswerA::stcMDNS_RRAnswerA constructor - */ - MDNSResponder::stcMDNS_RRAnswerA::stcMDNS_RRAnswerA(const MDNSResponder::stcMDNS_RRHeader& p_Header, - uint32_t p_u32TTL) - : stcMDNS_RRAnswer(AnswerType_A, p_Header, p_u32TTL), - m_IPAddress(0, 0, 0, 0) { - - } - - /* - * MDNSResponder::stcMDNS_RRAnswerA::stcMDNS_RRAnswerA destructor - */ - MDNSResponder::stcMDNS_RRAnswerA::~stcMDNS_RRAnswerA(void) { - - clear(); - } - - /* - * MDNSResponder::stcMDNS_RRAnswerA::clear - */ - bool MDNSResponder::stcMDNS_RRAnswerA::clear(void) { - - m_IPAddress = IPAddress(0, 0, 0, 0); - return true; - } -#endif - - -/** - * MDNSResponder::stcMDNS_RRAnswerPTR - * - * A MDNS PTR answer object. - * Extends the base class by a MDNS domain member. - * - */ - -/* - * MDNSResponder::stcMDNS_RRAnswerPTR::stcMDNS_RRAnswerPTR constructor - */ -MDNSResponder::stcMDNS_RRAnswerPTR::stcMDNS_RRAnswerPTR(const MDNSResponder::stcMDNS_RRHeader& p_Header, - uint32_t p_u32TTL) -: stcMDNS_RRAnswer(AnswerType_PTR, p_Header, p_u32TTL) { - -} - -/* - * MDNSResponder::stcMDNS_RRAnswerPTR::~stcMDNS_RRAnswerPTR destructor - */ -MDNSResponder::stcMDNS_RRAnswerPTR::~stcMDNS_RRAnswerPTR(void) { - - clear(); -} - -/* - * MDNSResponder::stcMDNS_RRAnswerPTR::clear - */ -bool MDNSResponder::stcMDNS_RRAnswerPTR::clear(void) { - - m_PTRDomain.clear(); - return true; -} - - -/** - * MDNSResponder::stcMDNS_RRAnswerTXT - * - * A MDNS TXT answer object. - * Extends the base class by a MDNS TXT items list member. - * - */ - -/* - * MDNSResponder::stcMDNS_RRAnswerTXT::stcMDNS_RRAnswerTXT constructor - */ -MDNSResponder::stcMDNS_RRAnswerTXT::stcMDNS_RRAnswerTXT(const MDNSResponder::stcMDNS_RRHeader& p_Header, - uint32_t p_u32TTL) -: stcMDNS_RRAnswer(AnswerType_TXT, p_Header, p_u32TTL) { - -} - -/* - * MDNSResponder::stcMDNS_RRAnswerTXT::~stcMDNS_RRAnswerTXT destructor - */ -MDNSResponder::stcMDNS_RRAnswerTXT::~stcMDNS_RRAnswerTXT(void) { - - clear(); -} - -/* - * MDNSResponder::stcMDNS_RRAnswerTXT::clear - */ -bool MDNSResponder::stcMDNS_RRAnswerTXT::clear(void) { - - m_Txts.clear(); - return true; -} - - -/** - * MDNSResponder::stcMDNS_RRAnswerAAAA - * - * A MDNS AAAA answer object. - * (Should) extend the base class by an IP6 address member. - * - */ - -#ifdef MDNS_IP6_SUPPORT - /* - * MDNSResponder::stcMDNS_RRAnswerAAAA::stcMDNS_RRAnswerAAAA constructor - */ - MDNSResponder::stcMDNS_RRAnswerAAAA::stcMDNS_RRAnswerAAAA(const MDNSResponder::stcMDNS_RRHeader& p_Header, - uint32_t p_u32TTL) - : stcMDNS_RRAnswer(AnswerType_AAAA, p_Header, p_u32TTL) { - - } - - /* - * MDNSResponder::stcMDNS_RRAnswerAAAA::~stcMDNS_RRAnswerAAAA destructor - */ - MDNSResponder::stcMDNS_RRAnswerAAAA::~stcMDNS_RRAnswerAAAA(void) { - - clear(); - } - - /* - * MDNSResponder::stcMDNS_RRAnswerAAAA::clear - */ - bool MDNSResponder::stcMDNS_RRAnswerAAAA::clear(void) { - - return true; - } -#endif - - -/** - * MDNSResponder::stcMDNS_RRAnswerSRV - * - * A MDNS SRV answer object. - * Extends the base class by a port member. - * - */ - -/* - * MDNSResponder::stcMDNS_RRAnswerSRV::stcMDNS_RRAnswerSRV constructor - */ -MDNSResponder::stcMDNS_RRAnswerSRV::stcMDNS_RRAnswerSRV(const MDNSResponder::stcMDNS_RRHeader& p_Header, - uint32_t p_u32TTL) -: stcMDNS_RRAnswer(AnswerType_SRV, p_Header, p_u32TTL), - m_u16Priority(0), - m_u16Weight(0), - m_u16Port(0) { - -} - -/* - * MDNSResponder::stcMDNS_RRAnswerSRV::~stcMDNS_RRAnswerSRV destructor - */ -MDNSResponder::stcMDNS_RRAnswerSRV::~stcMDNS_RRAnswerSRV(void) { - - clear(); -} - -/* - * MDNSResponder::stcMDNS_RRAnswerSRV::clear - */ -bool MDNSResponder::stcMDNS_RRAnswerSRV::clear(void) { - - m_u16Priority = 0; - m_u16Weight = 0; - m_u16Port = 0; - m_SRVDomain.clear(); - return true; -} - - -/** - * MDNSResponder::stcMDNS_RRAnswerGeneric - * - * An unknown (generic) MDNS answer object. - * Extends the base class by a RDATA buffer member. - * - */ - -/* - * MDNSResponder::stcMDNS_RRAnswerGeneric::stcMDNS_RRAnswerGeneric constructor - */ -MDNSResponder::stcMDNS_RRAnswerGeneric::stcMDNS_RRAnswerGeneric(const stcMDNS_RRHeader& p_Header, - uint32_t p_u32TTL) -: stcMDNS_RRAnswer(AnswerType_Generic, p_Header, p_u32TTL), - m_u16RDLength(0), - m_pu8RDData(0) { - -} - -/* - * MDNSResponder::stcMDNS_RRAnswerGeneric::~stcMDNS_RRAnswerGeneric destructor - */ -MDNSResponder::stcMDNS_RRAnswerGeneric::~stcMDNS_RRAnswerGeneric(void) { - - clear(); -} - -/* - * MDNSResponder::stcMDNS_RRAnswerGeneric::clear - */ -bool MDNSResponder::stcMDNS_RRAnswerGeneric::clear(void) { - - if (m_pu8RDData) { - delete[] m_pu8RDData; - m_pu8RDData = 0; - } - m_u16RDLength = 0; - - return true; -} - - -/** - * MDNSResponder::stcProbeInformation - * - * Probing status information for a host or service domain - * - */ - -/* - * MDNSResponder::stcProbeInformation::stcProbeInformation constructor - */ -MDNSResponder::stcProbeInformation::stcProbeInformation(void) -: m_ProbingStatus(ProbingStatus_WaitingForData), - m_u8SentCount(0), - m_Timeout(esp8266::polledTimeout::oneShotMs::neverExpires), - m_bConflict(false), - m_bTiebreakNeeded(false), - m_fnHostProbeResultCallback(0), - m_fnServiceProbeResultCallback(0) { -} - -/* - * MDNSResponder::stcProbeInformation::clear - */ -bool MDNSResponder::stcProbeInformation::clear(bool p_bClearUserdata /*= false*/) { - - m_ProbingStatus = ProbingStatus_WaitingForData; - m_u8SentCount = 0; - m_Timeout.resetToNeverExpires(); - m_bConflict = false; - m_bTiebreakNeeded = false; - if (p_bClearUserdata) { - m_fnHostProbeResultCallback = 0; - m_fnServiceProbeResultCallback = 0; - } - return true; -} - -/** - * MDNSResponder::stcMDNSService - * - * A MDNS service object (to be announced by the MDNS responder) - * The service instance may be '\0'; in this case the hostname is used - * and the flag m_bAutoName is set. If the hostname changes, all 'auto- - * named' services are renamed also. - * m_u8Replymask is used while preparing a response to a MDNS query. It is - * resetted in '_sendMDNSMessage' afterwards. - */ - -/* - * MDNSResponder::stcMDNSService::stcMDNSService constructor - */ -MDNSResponder::stcMDNSService::stcMDNSService(const char* p_pcName /*= 0*/, - const char* p_pcService /*= 0*/, - const char* p_pcProtocol /*= 0*/) -: m_pNext(0), - m_pcName(0), - m_bAutoName(false), - m_pcService(0), - m_pcProtocol(0), - m_u16Port(0), - m_u8ReplyMask(0), - m_fnTxtCallback(0) { - - setName(p_pcName); - setService(p_pcService); - setProtocol(p_pcProtocol); -} - -/* - * MDNSResponder::stcMDNSService::~stcMDNSService destructor - */ -MDNSResponder::stcMDNSService::~stcMDNSService(void) { - - releaseName(); - releaseService(); - releaseProtocol(); -} - -/* - * MDNSResponder::stcMDNSService::setName - */ -bool MDNSResponder::stcMDNSService::setName(const char* p_pcName) { - - bool bResult = false; - - releaseName(); - size_t stLength = (p_pcName ? strlen(p_pcName) : 0); - if (stLength) { - if ((bResult = (0 != (m_pcName = new char[stLength + 1])))) { - strncpy(m_pcName, p_pcName, stLength); - m_pcName[stLength] = 0; - } - } - else { - bResult = true; - } - return bResult; -} - -/* - * MDNSResponder::stcMDNSService::releaseName - */ -bool MDNSResponder::stcMDNSService::releaseName(void) { - - if (m_pcName) { - delete[] m_pcName; - m_pcName = 0; - } - return true; -} - -/* - * MDNSResponder::stcMDNSService::setService - */ -bool MDNSResponder::stcMDNSService::setService(const char* p_pcService) { - - bool bResult = false; - - releaseService(); - size_t stLength = (p_pcService ? strlen(p_pcService) : 0); - if (stLength) { - if ((bResult = (0 != (m_pcService = new char[stLength + 1])))) { - strncpy(m_pcService, p_pcService, stLength); - m_pcService[stLength] = 0; - } - } - else { - bResult = true; - } - return bResult; -} - -/* - * MDNSResponder::stcMDNSService::releaseService - */ -bool MDNSResponder::stcMDNSService::releaseService(void) { - - if (m_pcService) { - delete[] m_pcService; - m_pcService = 0; - } - return true; -} - -/* - * MDNSResponder::stcMDNSService::setProtocol - */ -bool MDNSResponder::stcMDNSService::setProtocol(const char* p_pcProtocol) { - - bool bResult = false; - - releaseProtocol(); - size_t stLength = (p_pcProtocol ? strlen(p_pcProtocol) : 0); - if (stLength) { - if ((bResult = (0 != (m_pcProtocol = new char[stLength + 1])))) { - strncpy(m_pcProtocol, p_pcProtocol, stLength); - m_pcProtocol[stLength] = 0; - } - } - else { - bResult = true; - } - return bResult; -} - -/* - * MDNSResponder::stcMDNSService::releaseProtocol - */ -bool MDNSResponder::stcMDNSService::releaseProtocol(void) { - - if (m_pcProtocol) { - delete[] m_pcProtocol; - m_pcProtocol = 0; - } - return true; -} - - -/** - * MDNSResponder::stcMDNSServiceQuery - * - * A MDNS service query object. - * Service queries may be static or dynamic. - * As the static service query is processed in the blocking function 'queryService', - * only one static service service may exist. The processing of the answers is done - * on the WiFi-stack side of the ESP stack structure (via 'UDPContext.onRx(_update)'). - * - */ - -/** - * MDNSResponder::stcMDNSServiceQuery::stcAnswer - * - * One answer for a service query. - * Every answer must contain - * - a service instance entry (pivot), - * and may contain - * - a host domain, - * - a port - * - an IP4 address - * (- an IP6 address) - * - a MDNS TXTs - * The existance of a component is flaged in 'm_u32ContentFlags'. - * For every answer component a TTL value is maintained. - * Answer objects can be connected to a linked list. - * - * For the host domain, service domain and TXTs components, a char array - * representation can be retrieved (which is created on demand). - * - */ - -/** - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL - * - * The TTL (Time-To-Live) for an specific answer content. - * The 80% and outdated states are calculated based on the current time (millis) - * and the 'set' time (also millis). - * If the answer is scheduled for an update, the corresponding flag should be set. - * - * / - -/ * - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::stcTTL constructor - * / -MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::stcTTL(uint32_t p_u32TTL / *= 0* /) -: m_bUpdateScheduled(false) { - - set(p_u32TTL * 1000); -} - -/ * - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::set - * / -bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::set(uint32_t p_u32TTL) { - - m_TTLTimeFlag.restart(p_u32TTL * 1000); - m_bUpdateScheduled = false; - - return true; -} - -/ * - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::has80Percent - * / -bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::has80Percent(void) const { - - return ((m_TTLTimeFlag.getTimeout()) && - (!m_bUpdateScheduled) && - (m_TTLTimeFlag.hypotheticalTimeout((m_TTLTimeFlag.getTimeout() * 800) / 1000))); -} - -/ * - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::isOutdated - * / -bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::isOutdated(void) const { - - return ((m_TTLTimeFlag.getTimeout()) && - (m_TTLTimeFlag.flagged())); -}*/ - - -/** - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL - * - * The TTL (Time-To-Live) for an specific answer content. - * The 80% and outdated states are calculated based on the current time (millis) - * and the 'set' time (also millis). - * If the answer is scheduled for an update, the corresponding flag should be set. - * - */ - -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::stcTTL constructor - */ -MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::stcTTL(void) -: m_u32TTL(0), - m_TTLTimeout(esp8266::polledTimeout::oneShotMs::neverExpires), - m_timeoutLevel(TIMEOUTLEVEL_UNSET) { - -} - -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::set - */ -bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::set(uint32_t p_u32TTL) { - - m_u32TTL = p_u32TTL; - if (m_u32TTL) { - m_timeoutLevel = TIMEOUTLEVEL_BASE; // Set to 80% - m_TTLTimeout.reset(timeout()); - } - else { - m_timeoutLevel = TIMEOUTLEVEL_UNSET; // undef - m_TTLTimeout.resetToNeverExpires(); - } - return true; -} - -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::flagged - */ -bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::flagged(void) { - - return ((m_u32TTL) && - (TIMEOUTLEVEL_UNSET != m_timeoutLevel) && - (m_TTLTimeout.expired())); -} - -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::restart - */ -bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::restart(void) { - - bool bResult = true; - - if ((TIMEOUTLEVEL_BASE <= m_timeoutLevel) && // >= 80% AND - (TIMEOUTLEVEL_FINAL > m_timeoutLevel)) { // < 100% - - m_timeoutLevel += TIMEOUTLEVEL_INTERVAL; // increment by 5% - m_TTLTimeout.reset(timeout()); - } - else { - bResult = false; - m_TTLTimeout.resetToNeverExpires(); - m_timeoutLevel = TIMEOUTLEVEL_UNSET; - } - return bResult; -} - -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::prepareDeletion - */ -bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::prepareDeletion(void) { - - m_timeoutLevel = TIMEOUTLEVEL_FINAL; - m_TTLTimeout.reset(1 * 1000); // See RFC 6762, 10.1 - - return true; -} - -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::finalTimeoutLevel - */ -bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::finalTimeoutLevel(void) const { - - return (TIMEOUTLEVEL_FINAL == m_timeoutLevel); -} - -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::timeout - */ -unsigned long MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::timeout(void) const { - - uint32_t u32Timeout = esp8266::polledTimeout::oneShotMs::neverExpires; - - if (TIMEOUTLEVEL_BASE == m_timeoutLevel) { // 80% - u32Timeout = (m_u32TTL * 800); // to milliseconds - } - else if ((TIMEOUTLEVEL_BASE < m_timeoutLevel) && // >80% AND - (TIMEOUTLEVEL_FINAL >= m_timeoutLevel)) { // <= 100% - - u32Timeout = (m_u32TTL * 50); - } // else: invalid - return u32Timeout; -} - - -#ifdef MDNS_IP4_SUPPORT -/** - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address - * - */ - -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address::stcIP4Address constructor - */ -MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address::stcIP4Address(IPAddress p_IPAddress, - uint32_t p_u32TTL /*= 0*/) -: m_pNext(0), - m_IPAddress(p_IPAddress) { - - m_TTL.set(p_u32TTL); -} -#endif - - -/** - * MDNSResponder::stcMDNSServiceQuery::stcAnswer - */ - -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcAnswer constructor - */ -MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcAnswer(void) -: m_pNext(0), - m_pcServiceDomain(0), - m_pcHostDomain(0), - m_u16Port(0), - m_pcTxts(0), -#ifdef MDNS_IP4_SUPPORT - m_pIP4Addresses(0), -#endif -#ifdef MDNS_IP6_SUPPORT - m_pIP6Addresses(0), -#endif - m_u32ContentFlags(0) { -} - -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::~stcAnswer destructor - */ -MDNSResponder::stcMDNSServiceQuery::stcAnswer::~stcAnswer(void) { - - clear(); -} - -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::clear - */ -bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::clear(void) { - - return ((releaseTxts()) && -#ifdef MDNS_IP4_SUPPORT - (releaseIP4Addresses()) && -#endif -#ifdef MDNS_IP6_SUPPORT - (releaseIP6Addresses()) -#endif - (releaseHostDomain()) && - (releaseServiceDomain())); -} - -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::allocServiceDomain - * - * Alloc memory for the char array representation of the service domain. - * - */ -char* MDNSResponder::stcMDNSServiceQuery::stcAnswer::allocServiceDomain(size_t p_stLength) { - - releaseServiceDomain(); - if (p_stLength) { - m_pcServiceDomain = new char[p_stLength]; - } - return m_pcServiceDomain; -} - -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseServiceDomain - */ -bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseServiceDomain(void) { - - if (m_pcServiceDomain) { - delete[] m_pcServiceDomain; - m_pcServiceDomain = 0; - } - return true; -} - -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::allocHostDomain - * - * Alloc memory for the char array representation of the host domain. - * - */ -char* MDNSResponder::stcMDNSServiceQuery::stcAnswer::allocHostDomain(size_t p_stLength) { - - releaseHostDomain(); - if (p_stLength) { - m_pcHostDomain = new char[p_stLength]; - } - return m_pcHostDomain; -} - -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseHostDomain - */ -bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseHostDomain(void) { - - if (m_pcHostDomain) { - delete[] m_pcHostDomain; - m_pcHostDomain = 0; - } - return true; -} - -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::allocTxts - * - * Alloc memory for the char array representation of the TXT items. - * - */ -char* MDNSResponder::stcMDNSServiceQuery::stcAnswer::allocTxts(size_t p_stLength) { - - releaseTxts(); - if (p_stLength) { - m_pcTxts = new char[p_stLength]; - } - return m_pcTxts; -} - -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseTxts - */ -bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseTxts(void) { - - if (m_pcTxts) { - delete[] m_pcTxts; - m_pcTxts = 0; - } - return true; -} - -#ifdef MDNS_IP4_SUPPORT -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseIP4Addresses - */ -bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseIP4Addresses(void) { - - while (m_pIP4Addresses) { - stcIP4Address* pNext = m_pIP4Addresses->m_pNext; - delete m_pIP4Addresses; - m_pIP4Addresses = pNext; - } - return true; -} - -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::addIP4Address - */ -bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::addIP4Address(MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address* p_pIP4Address) { - - bool bResult = false; - - if (p_pIP4Address) { - p_pIP4Address->m_pNext = m_pIP4Addresses; - m_pIP4Addresses = p_pIP4Address; - bResult = true; - } - return bResult; -} - -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::removeIP4Address - */ -bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::removeIP4Address(MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address* p_pIP4Address) { - - bool bResult = false; - - if (p_pIP4Address) { - stcIP4Address* pPred = m_pIP4Addresses; - while ((pPred) && - (pPred->m_pNext != p_pIP4Address)) { - pPred = pPred->m_pNext; - } - if (pPred) { - pPred->m_pNext = p_pIP4Address->m_pNext; - delete p_pIP4Address; - bResult = true; - } - else if (m_pIP4Addresses == p_pIP4Address) { // No predecesor, but first item - m_pIP4Addresses = p_pIP4Address->m_pNext; - delete p_pIP4Address; - bResult = true; - } - } - return bResult; -} - -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP4Address (const) - */ -const MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address* MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP4Address(const IPAddress& p_IPAddress) const { - - return (stcIP4Address*)(((const stcAnswer*)this)->findIP4Address(p_IPAddress)); -} - -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP4Address - */ -MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address* MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP4Address(const IPAddress& p_IPAddress) { - - stcIP4Address* pIP4Address = m_pIP4Addresses; - while (pIP4Address) { - if (pIP4Address->m_IPAddress == p_IPAddress) { - break; - } - pIP4Address = pIP4Address->m_pNext; - } - return pIP4Address; -} - -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP4AddressCount - */ -uint32_t MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP4AddressCount(void) const { - - uint32_t u32Count = 0; - - stcIP4Address* pIP4Address = m_pIP4Addresses; - while (pIP4Address) { - ++u32Count; - pIP4Address = pIP4Address->m_pNext; - } - return u32Count; -} - -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP4AddressAtIndex - */ -MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address* MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP4AddressAtIndex(uint32_t p_u32Index) { - - return (stcIP4Address*)(((const stcAnswer*)this)->IP4AddressAtIndex(p_u32Index)); -} - -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP4AddressAtIndex (const) - */ -const MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address* MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP4AddressAtIndex(uint32_t p_u32Index) const { - - const stcIP4Address* pIP4Address = 0; - - if (((uint32_t)(-1) != p_u32Index) && - (m_pIP4Addresses)) { - - uint32_t u32Index; - for (pIP4Address=m_pIP4Addresses, u32Index=0; ((pIP4Address) && (u32Indexm_pNext, ++u32Index); - } - return pIP4Address; -} -#endif - -#ifdef MDNS_IP6_SUPPORT -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseIP6Addresses - */ -bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseIP6Addresses(void) { - - while (m_pIP6Addresses) { - stcIP6Address* pNext = m_pIP6Addresses->m_pNext; - delete m_pIP6Addresses; - m_pIP6Addresses = pNext; - } - return true; -} - -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::addIP6Address - */ -bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::addIP6Address(MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP6Address* p_pIP6Address) { - - bool bResult = false; - - if (p_pIP6Address) { - p_pIP6Address->m_pNext = m_pIP6Addresses; - m_pIP6Addresses = p_pIP6Address; - bResult = true; - } - return bResult; -} - -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::removeIP6Address - */ -bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::removeIP6Address(MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP6Address* p_pIP6Address) { - - bool bResult = false; - - if (p_pIP6Address) { - stcIP6Address* pPred = m_pIP6Addresses; - while ((pPred) && - (pPred->m_pNext != p_pIP6Address)) { - pPred = pPred->m_pNext; - } - if (pPred) { - pPred->m_pNext = p_pIP6Address->m_pNext; - delete p_pIP6Address; - bResult = true; - } - else if (m_pIP6Addresses == p_pIP6Address) { // No predecesor, but first item - m_pIP6Addresses = p_pIP6Address->m_pNext; - delete p_pIP6Address; - bResult = true; - } - } - return bResult; -} - -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP6Address - */ -MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP6Address* MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP6Address(const IP6Address& p_IPAddress) { - - return (stcIP6Address*)(((const stcAnswer*)this)->findIP6Address(p_IPAddress)); -} - -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP6Address (const) - */ -const MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP6Address* MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP6Address(const IPAddress& p_IPAddress) const { - - const stcIP6Address* pIP6Address = m_pIP6Addresses; - while (pIP6Address) { - if (p_IP6Address->m_IPAddress == p_IPAddress) { - break; - } - pIP6Address = pIP6Address->m_pNext; - } - return pIP6Address; -} - -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP6AddressCount - */ -uint32_t MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP6AddressCount(void) const { - - uint32_t u32Count = 0; - - stcIP6Address* pIP6Address = m_pIP6Addresses; - while (pIP6Address) { - ++u32Count; - pIP6Address = pIP6Address->m_pNext; - } - return u32Count; -} - -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP6AddressAtIndex (const) - */ -const MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP6Address* MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP6AddressAtIndex(uint32_t p_u32Index) const { - - return (stcIP6Address*)(((const stcAnswer*)this)->IP6AddressAtIndex(p_u32Index)); -} - -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP6AddressAtIndex - */ -MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP6Address* MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP6AddressAtIndex(uint32_t p_u32Index) { - - stcIP6Address* pIP6Address = 0; - - if (((uint32_t)(-1) != p_u32Index) && - (m_pIP6Addresses)) { - - uint32_t u32Index; - for (pIP6Address=m_pIP6Addresses, u32Index=0; ((pIP6Address) && (u32Indexm_pNext, ++u32Index); - } - return pIP6Address; -} -#endif - - -/** - * MDNSResponder::stcMDNSServiceQuery - * - * A service query object. - * A static query is flaged via 'm_bLegacyQuery'; while the function 'queryService' - * is waiting for answers, the internal flag 'm_bAwaitingAnswers' is set. When the - * timeout is reached, the flag is removed. These two flags are only used for static - * service queries. - * All answers to the service query are stored in 'm_pAnswers' list. - * Individual answers may be addressed by index (in the list of answers). - * Every time a answer component is added (or changes) in a dynamic service query, - * the callback 'm_fnCallback' is called. - * The answer list may be searched by service and host domain. - * - * Service query object may be connected to a linked list. - */ - -/* - * MDNSResponder::stcMDNSServiceQuery::stcMDNSServiceQuery constructor - */ -MDNSResponder::stcMDNSServiceQuery::stcMDNSServiceQuery(void) -: m_pNext(0), - m_fnCallback(0), - m_bLegacyQuery(false), - m_u8SentCount(0), - m_ResendTimeout(esp8266::polledTimeout::oneShotMs::neverExpires), - m_bAwaitingAnswers(true), - m_pAnswers(0) { - - clear(); -} - -/* - * MDNSResponder::stcMDNSServiceQuery::~stcMDNSServiceQuery destructor - */ -MDNSResponder::stcMDNSServiceQuery::~stcMDNSServiceQuery(void) { - - clear(); -} - -/* - * MDNSResponder::stcMDNSServiceQuery::clear - */ -bool MDNSResponder::stcMDNSServiceQuery::clear(void) { - - m_fnCallback = 0; - m_bLegacyQuery = false; - m_u8SentCount = 0; - m_ResendTimeout.resetToNeverExpires(); - m_bAwaitingAnswers = true; - while (m_pAnswers) { - stcAnswer* pNext = m_pAnswers->m_pNext; - delete m_pAnswers; - m_pAnswers = pNext; - } - return true; -} - -/* - * MDNSResponder::stcMDNSServiceQuery::answerCount - */ -uint32_t MDNSResponder::stcMDNSServiceQuery::answerCount(void) const { - - uint32_t u32Count = 0; - - stcAnswer* pAnswer = m_pAnswers; - while (pAnswer) { - ++u32Count; - pAnswer = pAnswer->m_pNext; - } - return u32Count; -} - -/* - * MDNSResponder::stcMDNSServiceQuery::answerAtIndex - */ -const MDNSResponder::stcMDNSServiceQuery::stcAnswer* MDNSResponder::stcMDNSServiceQuery::answerAtIndex(uint32_t p_u32Index) const { - - const stcAnswer* pAnswer = 0; - - if (((uint32_t)(-1) != p_u32Index) && - (m_pAnswers)) { - - uint32_t u32Index; - for (pAnswer=m_pAnswers, u32Index=0; ((pAnswer) && (u32Indexm_pNext, ++u32Index); - } - return pAnswer; -} - -/* - * MDNSResponder::stcMDNSServiceQuery::answerAtIndex - */ -MDNSResponder::stcMDNSServiceQuery::stcAnswer* MDNSResponder::stcMDNSServiceQuery::answerAtIndex(uint32_t p_u32Index) { - - return (stcAnswer*)(((const stcMDNSServiceQuery*)this)->answerAtIndex(p_u32Index)); -} - -/* - * MDNSResponder::stcMDNSServiceQuery::indexOfAnswer - */ -uint32_t MDNSResponder::stcMDNSServiceQuery::indexOfAnswer(const MDNSResponder::stcMDNSServiceQuery::stcAnswer* p_pAnswer) const { - - uint32_t u32Index = 0; - - for (const stcAnswer* pAnswer=m_pAnswers; pAnswer; pAnswer=pAnswer->m_pNext, ++u32Index) { - if (pAnswer == p_pAnswer) { - return u32Index; - } - } - return ((uint32_t)(-1)); -} - -/* - * MDNSResponder::stcMDNSServiceQuery::addAnswer - */ -bool MDNSResponder::stcMDNSServiceQuery::addAnswer(MDNSResponder::stcMDNSServiceQuery::stcAnswer* p_pAnswer) { - - bool bResult = false; - - if (p_pAnswer) { - p_pAnswer->m_pNext = m_pAnswers; - m_pAnswers = p_pAnswer; - bResult = true; - } - return bResult; -} - -/* - * MDNSResponder::stcMDNSServiceQuery::removeAnswer - */ -bool MDNSResponder::stcMDNSServiceQuery::removeAnswer(MDNSResponder::stcMDNSServiceQuery::stcAnswer* p_pAnswer) { - - bool bResult = false; - - if (p_pAnswer) { - stcAnswer* pPred = m_pAnswers; - while ((pPred) && - (pPred->m_pNext != p_pAnswer)) { - pPred = pPred->m_pNext; - } - if (pPred) { - pPred->m_pNext = p_pAnswer->m_pNext; - delete p_pAnswer; - bResult = true; - } - else if (m_pAnswers == p_pAnswer) { // No predecesor, but first item - m_pAnswers = p_pAnswer->m_pNext; - delete p_pAnswer; - bResult = true; - } - } - return bResult; -} - -/* - * MDNSResponder::stcMDNSServiceQuery::findAnswerForServiceDomain - */ -MDNSResponder::stcMDNSServiceQuery::stcAnswer* MDNSResponder::stcMDNSServiceQuery::findAnswerForServiceDomain(const MDNSResponder::stcMDNS_RRDomain& p_ServiceDomain) { - - stcAnswer* pAnswer = m_pAnswers; - while (pAnswer) { - if (pAnswer->m_ServiceDomain == p_ServiceDomain) { - break; - } - pAnswer = pAnswer->m_pNext; - } - return pAnswer; -} - -/* - * MDNSResponder::stcMDNSServiceQuery::findAnswerForHostDomain - */ -MDNSResponder::stcMDNSServiceQuery::stcAnswer* MDNSResponder::stcMDNSServiceQuery::findAnswerForHostDomain(const MDNSResponder::stcMDNS_RRDomain& p_HostDomain) { - - stcAnswer* pAnswer = m_pAnswers; - while (pAnswer) { - if (pAnswer->m_HostDomain == p_HostDomain) { - break; - } - pAnswer = pAnswer->m_pNext; - } - return pAnswer; -} - - -/** - * MDNSResponder::stcMDNSSendParameter - * - * A 'collection' of properties and flags for one MDNS query or response. - * Mainly managed by the 'Control' functions. - * The current offset in the UPD output buffer is tracked to be able to do - * a simple host or service domain compression. - * - */ - -/** - * MDNSResponder::stcMDNSSendParameter::stcDomainCacheItem - * - * A cached host or service domain, incl. the offset in the UDP output buffer. - * - */ - -/* - * MDNSResponder::stcMDNSSendParameter::stcDomainCacheItem::stcDomainCacheItem constructor - */ -MDNSResponder::stcMDNSSendParameter::stcDomainCacheItem::stcDomainCacheItem(const void* p_pHostnameOrService, - bool p_bAdditionalData, - uint32_t p_u16Offset) -: m_pNext(0), - m_pHostnameOrService(p_pHostnameOrService), - m_bAdditionalData(p_bAdditionalData), - m_u16Offset(p_u16Offset) { - -} - -/** - * MDNSResponder::stcMDNSSendParameter - */ - -/* - * MDNSResponder::stcMDNSSendParameter::stcMDNSSendParameter constructor - */ -MDNSResponder::stcMDNSSendParameter::stcMDNSSendParameter(void) -: m_pQuestions(0), - m_pDomainCacheItems(0) { - - clear(); -} - -/* - * MDNSResponder::stcMDNSSendParameter::~stcMDNSSendParameter destructor - */ -MDNSResponder::stcMDNSSendParameter::~stcMDNSSendParameter(void) { - - clear(); -} - -/* - * MDNSResponder::stcMDNSSendParameter::clear - */ -bool MDNSResponder::stcMDNSSendParameter::clear(void) { - - m_u16ID = 0; - m_u8HostReplyMask = 0; - m_u16Offset = 0; - - m_bLegacyQuery = false; - m_bResponse = false; - m_bAuthorative = false; - m_bUnicast = false; - m_bUnannounce = false; - - m_bCacheFlush = true; - - while (m_pQuestions) { - stcMDNS_RRQuestion* pNext = m_pQuestions->m_pNext; - delete m_pQuestions; - m_pQuestions = pNext; - } - while (m_pDomainCacheItems) { - stcDomainCacheItem* pNext = m_pDomainCacheItems->m_pNext; - delete m_pDomainCacheItems; - m_pDomainCacheItems = pNext; - } - return true; -} - -/* - * MDNSResponder::stcMDNSSendParameter::shiftOffset - */ -bool MDNSResponder::stcMDNSSendParameter::shiftOffset(uint16_t p_u16Shift) { - - m_u16Offset += p_u16Shift; - return true; -} - -/* - * MDNSResponder::stcMDNSSendParameter::addDomainCacheItem - */ -bool MDNSResponder::stcMDNSSendParameter::addDomainCacheItem(const void* p_pHostnameOrService, - bool p_bAdditionalData, - uint16_t p_u16Offset) { - - bool bResult = false; - - stcDomainCacheItem* pNewItem = 0; - if ((p_pHostnameOrService) && - (p_u16Offset) && - ((pNewItem = new stcDomainCacheItem(p_pHostnameOrService, p_bAdditionalData, p_u16Offset)))) { - - pNewItem->m_pNext = m_pDomainCacheItems; - bResult = ((m_pDomainCacheItems = pNewItem)); - } - return bResult; -} - -/* - * MDNSResponder::stcMDNSSendParameter::findCachedDomainOffset - */ -uint16_t MDNSResponder::stcMDNSSendParameter::findCachedDomainOffset(const void* p_pHostnameOrService, - bool p_bAdditionalData) const { - - const stcDomainCacheItem* pCacheItem = m_pDomainCacheItems; - - for (; pCacheItem; pCacheItem=pCacheItem->m_pNext) { - if ((pCacheItem->m_pHostnameOrService == p_pHostnameOrService) && - (pCacheItem->m_bAdditionalData == p_bAdditionalData)) { // Found cache item - break; - } - } - return (pCacheItem ? pCacheItem->m_u16Offset : 0); -} - -} // namespace MDNSImplementation - -} // namespace esp8266 - - - +/* + LEAmDNS_Structs.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" +#include "LEAmDNS_lwIPdefs.h" + +namespace esp8266 +{ + +/* + LEAmDNS +*/ +namespace MDNSImplementation +{ + +/** + STRUCTS +*/ + +/** + MDNSResponder::stcMDNSServiceTxt + + One MDNS TXT item. + m_pcValue may be '\0'. + Objects can be chained together (list, m_pNext). + A 'm_bTemp' flag differentiates between static and dynamic items. + Output as byte array 'c#=1' is supported. +*/ + +/* + MDNSResponder::stcMDNSServiceTxt::stcMDNSServiceTxt constructor +*/ +MDNSResponder::stcMDNSServiceTxt::stcMDNSServiceTxt(const char* p_pcKey /*= 0*/, + const char* p_pcValue /*= 0*/, + bool p_bTemp /*= false*/) + : m_pNext(0), + m_pcKey(0), + m_pcValue(0), + m_bTemp(p_bTemp) +{ + + setKey(p_pcKey); + setValue(p_pcValue); +} + +/* + MDNSResponder::stcMDNSServiceTxt::stcMDNSServiceTxt copy-constructor +*/ +MDNSResponder::stcMDNSServiceTxt::stcMDNSServiceTxt(const MDNSResponder::stcMDNSServiceTxt& p_Other) + : m_pNext(0), + m_pcKey(0), + m_pcValue(0), + m_bTemp(false) +{ + + operator=(p_Other); +} + +/* + MDNSResponder::stcMDNSServiceTxt::~stcMDNSServiceTxt destructor +*/ +MDNSResponder::stcMDNSServiceTxt::~stcMDNSServiceTxt(void) +{ + + clear(); +} + +/* + MDNSResponder::stcMDNSServiceTxt::operator= +*/ +MDNSResponder::stcMDNSServiceTxt& MDNSResponder::stcMDNSServiceTxt::operator=(const MDNSResponder::stcMDNSServiceTxt& p_Other) +{ + + if (&p_Other != this) + { + clear(); + set(p_Other.m_pcKey, p_Other.m_pcValue, p_Other.m_bTemp); + } + return *this; +} + +/* + MDNSResponder::stcMDNSServiceTxt::clear +*/ +bool MDNSResponder::stcMDNSServiceTxt::clear(void) +{ + + releaseKey(); + releaseValue(); + return true; +} + +/* + MDNSResponder::stcMDNSServiceTxt::allocKey +*/ +char* MDNSResponder::stcMDNSServiceTxt::allocKey(size_t p_stLength) +{ + + releaseKey(); + if (p_stLength) + { + m_pcKey = new char[p_stLength + 1]; + } + return m_pcKey; +} + +/* + MDNSResponder::stcMDNSServiceTxt::setKey +*/ +bool MDNSResponder::stcMDNSServiceTxt::setKey(const char* p_pcKey, + size_t p_stLength) +{ + + bool bResult = false; + + releaseKey(); + if (p_stLength) + { + if (allocKey(p_stLength)) + { + strncpy(m_pcKey, p_pcKey, p_stLength); + m_pcKey[p_stLength] = 0; + bResult = true; + } + } + return bResult; +} + +/* + MDNSResponder::stcMDNSServiceTxt::setKey +*/ +bool MDNSResponder::stcMDNSServiceTxt::setKey(const char* p_pcKey) +{ + + return setKey(p_pcKey, (p_pcKey ? strlen(p_pcKey) : 0)); +} + +/* + MDNSResponder::stcMDNSServiceTxt::releaseKey +*/ +bool MDNSResponder::stcMDNSServiceTxt::releaseKey(void) +{ + + if (m_pcKey) + { + delete[] m_pcKey; + m_pcKey = 0; + } + return true; +} + +/* + MDNSResponder::stcMDNSServiceTxt::allocValue +*/ +char* MDNSResponder::stcMDNSServiceTxt::allocValue(size_t p_stLength) +{ + + releaseValue(); + if (p_stLength) + { + m_pcValue = new char[p_stLength + 1]; + } + return m_pcValue; +} + +/* + MDNSResponder::stcMDNSServiceTxt::setValue +*/ +bool MDNSResponder::stcMDNSServiceTxt::setValue(const char* p_pcValue, + size_t p_stLength) +{ + + bool bResult = false; + + releaseValue(); + if (p_stLength) + { + if (allocValue(p_stLength)) + { + strncpy(m_pcValue, p_pcValue, p_stLength); + m_pcValue[p_stLength] = 0; + bResult = true; + } + } + else // No value -> also OK + { + bResult = true; + } + return bResult; +} + +/* + MDNSResponder::stcMDNSServiceTxt::setValue +*/ +bool MDNSResponder::stcMDNSServiceTxt::setValue(const char* p_pcValue) +{ + + return setValue(p_pcValue, (p_pcValue ? strlen(p_pcValue) : 0)); +} + +/* + MDNSResponder::stcMDNSServiceTxt::releaseValue +*/ +bool MDNSResponder::stcMDNSServiceTxt::releaseValue(void) +{ + + if (m_pcValue) + { + delete[] m_pcValue; + m_pcValue = 0; + } + return true; +} + +/* + MDNSResponder::stcMDNSServiceTxt::set +*/ +bool MDNSResponder::stcMDNSServiceTxt::set(const char* p_pcKey, + const char* p_pcValue, + bool p_bTemp /*= false*/) +{ + + m_bTemp = p_bTemp; + return ((setKey(p_pcKey)) && + (setValue(p_pcValue))); +} + +/* + MDNSResponder::stcMDNSServiceTxt::update +*/ +bool MDNSResponder::stcMDNSServiceTxt::update(const char* p_pcValue) +{ + + return setValue(p_pcValue); +} + +/* + MDNSResponder::stcMDNSServiceTxt::length + + length of eg. 'c#=1' without any closing '\0' +*/ +size_t MDNSResponder::stcMDNSServiceTxt::length(void) const +{ + + size_t stLength = 0; + if (m_pcKey) + { + stLength += strlen(m_pcKey); // Key + stLength += 1; // '=' + stLength += (m_pcValue ? strlen(m_pcValue) : 0); // Value + } + return stLength; +} + + +/** + MDNSResponder::stcMDNSServiceTxts + + A list of zero or more MDNS TXT items. + Dynamic TXT items can be removed by 'removeTempTxts'. + A TXT item can be looke up by its 'key' member. + Export as ';'-separated byte array is supported. + Export as 'length byte coded' byte array is supported. + Comparision ((all A TXT items in B and equal) AND (all B TXT items in A and equal)) is supported. + +*/ + +/* + MDNSResponder::stcMDNSServiceTxts::stcMDNSServiceTxts contructor +*/ +MDNSResponder::stcMDNSServiceTxts::stcMDNSServiceTxts(void) + : m_pTxts(0) +{ + +} + +/* + MDNSResponder::stcMDNSServiceTxts::stcMDNSServiceTxts copy-constructor +*/ +MDNSResponder::stcMDNSServiceTxts::stcMDNSServiceTxts(const stcMDNSServiceTxts& p_Other) + : m_pTxts(0) +{ + + operator=(p_Other); +} + +/* + MDNSResponder::stcMDNSServiceTxts::~stcMDNSServiceTxts destructor +*/ +MDNSResponder::stcMDNSServiceTxts::~stcMDNSServiceTxts(void) +{ + + clear(); +} + +/* + MDNSResponder::stcMDNSServiceTxts::operator= +*/ +MDNSResponder::stcMDNSServiceTxts& MDNSResponder::stcMDNSServiceTxts::operator=(const stcMDNSServiceTxts& p_Other) +{ + + if (this != &p_Other) + { + clear(); + + for (stcMDNSServiceTxt* pOtherTxt = p_Other.m_pTxts; pOtherTxt; pOtherTxt = pOtherTxt->m_pNext) + { + add(new stcMDNSServiceTxt(*pOtherTxt)); + } + } + return *this; +} + +/* + MDNSResponder::stcMDNSServiceTxts::clear +*/ +bool MDNSResponder::stcMDNSServiceTxts::clear(void) +{ + + while (m_pTxts) + { + stcMDNSServiceTxt* pNext = m_pTxts->m_pNext; + delete m_pTxts; + m_pTxts = pNext; + } + return true; +} + +/* + MDNSResponder::stcMDNSServiceTxts::add +*/ +bool MDNSResponder::stcMDNSServiceTxts::add(MDNSResponder::stcMDNSServiceTxt* p_pTxt) +{ + + bool bResult = false; + + if (p_pTxt) + { + p_pTxt->m_pNext = m_pTxts; + m_pTxts = p_pTxt; + bResult = true; + } + return bResult; +} + +/* + MDNSResponder::stcMDNSServiceTxts::remove +*/ +bool MDNSResponder::stcMDNSServiceTxts::remove(stcMDNSServiceTxt* p_pTxt) +{ + + bool bResult = false; + + if (p_pTxt) + { + stcMDNSServiceTxt* pPred = m_pTxts; + while ((pPred) && + (pPred->m_pNext != p_pTxt)) + { + pPred = pPred->m_pNext; + } + if (pPred) + { + pPred->m_pNext = p_pTxt->m_pNext; + delete p_pTxt; + bResult = true; + } + else if (m_pTxts == p_pTxt) // No predecesor, but first item + { + m_pTxts = p_pTxt->m_pNext; + delete p_pTxt; + bResult = true; + } + } + return bResult; +} + +/* + MDNSResponder::stcMDNSServiceTxts::removeTempTxts +*/ +bool MDNSResponder::stcMDNSServiceTxts::removeTempTxts(void) +{ + + bool bResult = true; + + stcMDNSServiceTxt* pTxt = m_pTxts; + while ((bResult) && + (pTxt)) + { + stcMDNSServiceTxt* pNext = pTxt->m_pNext; + if (pTxt->m_bTemp) + { + bResult = remove(pTxt); + } + pTxt = pNext; + } + return bResult; +} + +/* + MDNSResponder::stcMDNSServiceTxts::find +*/ +MDNSResponder::stcMDNSServiceTxt* MDNSResponder::stcMDNSServiceTxts::find(const char* p_pcKey) +{ + + stcMDNSServiceTxt* pResult = 0; + + for (stcMDNSServiceTxt* pTxt = m_pTxts; pTxt; pTxt = pTxt->m_pNext) + { + if ((p_pcKey) && + (0 == strcmp(pTxt->m_pcKey, p_pcKey))) + { + pResult = pTxt; + break; + } + } + return pResult; +} + +/* + MDNSResponder::stcMDNSServiceTxts::find +*/ +const MDNSResponder::stcMDNSServiceTxt* MDNSResponder::stcMDNSServiceTxts::find(const char* p_pcKey) const +{ + + const stcMDNSServiceTxt* pResult = 0; + + for (const stcMDNSServiceTxt* pTxt = m_pTxts; pTxt; pTxt = pTxt->m_pNext) + { + if ((p_pcKey) && + (0 == strcmp(pTxt->m_pcKey, p_pcKey))) + { + + pResult = pTxt; + break; + } + } + return pResult; +} + +/* + MDNSResponder::stcMDNSServiceTxts::find +*/ +MDNSResponder::stcMDNSServiceTxt* MDNSResponder::stcMDNSServiceTxts::find(const stcMDNSServiceTxt* p_pTxt) +{ + + stcMDNSServiceTxt* pResult = 0; + + for (stcMDNSServiceTxt* pTxt = m_pTxts; pTxt; pTxt = pTxt->m_pNext) + { + if (p_pTxt == pTxt) + { + pResult = pTxt; + break; + } + } + return pResult; +} + +/* + MDNSResponder::stcMDNSServiceTxts::length +*/ +uint16_t MDNSResponder::stcMDNSServiceTxts::length(void) const +{ + + uint16_t u16Length = 0; + + stcMDNSServiceTxt* pTxt = m_pTxts; + while (pTxt) + { + u16Length += 1; // Length byte + u16Length += pTxt->length(); // Text + pTxt = pTxt->m_pNext; + } + return u16Length; +} + +/* + MDNSResponder::stcMDNSServiceTxts::c_strLength + + (incl. closing '\0'). Length bytes place is used for delimiting ';' and closing '\0' +*/ +size_t MDNSResponder::stcMDNSServiceTxts::c_strLength(void) const +{ + + return length(); +} + +/* + MDNSResponder::stcMDNSServiceTxts::c_str +*/ +bool MDNSResponder::stcMDNSServiceTxts::c_str(char* p_pcBuffer) +{ + + bool bResult = false; + + if (p_pcBuffer) + { + bResult = true; + + *p_pcBuffer = 0; + for (stcMDNSServiceTxt* pTxt = m_pTxts; ((bResult) && (pTxt)); pTxt = pTxt->m_pNext) + { + size_t stLength; + if ((bResult = (0 != (stLength = (pTxt->m_pcKey ? strlen(pTxt->m_pcKey) : 0))))) + { + if (pTxt != m_pTxts) + { + *p_pcBuffer++ = ';'; + } + strncpy(p_pcBuffer, pTxt->m_pcKey, stLength); p_pcBuffer[stLength] = 0; + p_pcBuffer += stLength; + *p_pcBuffer++ = '='; + if ((stLength = (pTxt->m_pcValue ? strlen(pTxt->m_pcValue) : 0))) + { + strncpy(p_pcBuffer, pTxt->m_pcValue, stLength); p_pcBuffer[stLength] = 0; + p_pcBuffer += stLength; + } + } + } + *p_pcBuffer++ = 0; + } + return bResult; +} + +/* + MDNSResponder::stcMDNSServiceTxts::bufferLength + + (incl. closing '\0'). +*/ +size_t MDNSResponder::stcMDNSServiceTxts::bufferLength(void) const +{ + + return (length() + 1); +} + +/* + MDNSResponder::stcMDNSServiceTxts::toBuffer +*/ +bool MDNSResponder::stcMDNSServiceTxts::buffer(char* p_pcBuffer) +{ + + bool bResult = false; + + if (p_pcBuffer) + { + bResult = true; + + *p_pcBuffer = 0; + for (stcMDNSServiceTxt* pTxt = m_pTxts; ((bResult) && (pTxt)); pTxt = pTxt->m_pNext) + { + *(unsigned char*)p_pcBuffer++ = pTxt->length(); + size_t stLength; + if ((bResult = (0 != (stLength = (pTxt->m_pcKey ? strlen(pTxt->m_pcKey) : 0))))) + { + memcpy(p_pcBuffer, pTxt->m_pcKey, stLength); + p_pcBuffer += stLength; + *p_pcBuffer++ = '='; + if ((stLength = (pTxt->m_pcValue ? strlen(pTxt->m_pcValue) : 0))) + { + memcpy(p_pcBuffer, pTxt->m_pcValue, stLength); + p_pcBuffer += stLength; + } + } + } + *p_pcBuffer++ = 0; + } + return bResult; +} + +/* + MDNSResponder::stcMDNSServiceTxts::compare +*/ +bool MDNSResponder::stcMDNSServiceTxts::compare(const MDNSResponder::stcMDNSServiceTxts& p_Other) const +{ + + bool bResult = false; + + if ((bResult = (length() == p_Other.length()))) + { + // Compare A->B + for (const stcMDNSServiceTxt* pTxt = m_pTxts; ((bResult) && (pTxt)); pTxt = pTxt->m_pNext) + { + const stcMDNSServiceTxt* pOtherTxt = p_Other.find(pTxt->m_pcKey); + bResult = ((pOtherTxt) && + (pTxt->m_pcValue) && + (pOtherTxt->m_pcValue) && + (strlen(pTxt->m_pcValue) == strlen(pOtherTxt->m_pcValue)) && + (0 == strcmp(pTxt->m_pcValue, pOtherTxt->m_pcValue))); + } + // Compare B->A + for (const stcMDNSServiceTxt* pOtherTxt = p_Other.m_pTxts; ((bResult) && (pOtherTxt)); pOtherTxt = pOtherTxt->m_pNext) + { + const stcMDNSServiceTxt* pTxt = find(pOtherTxt->m_pcKey); + bResult = ((pTxt) && + (pOtherTxt->m_pcValue) && + (pTxt->m_pcValue) && + (strlen(pOtherTxt->m_pcValue) == strlen(pTxt->m_pcValue)) && + (0 == strcmp(pOtherTxt->m_pcValue, pTxt->m_pcValue))); + } + } + return bResult; +} + +/* + MDNSResponder::stcMDNSServiceTxts::operator== +*/ +bool MDNSResponder::stcMDNSServiceTxts::operator==(const stcMDNSServiceTxts& p_Other) const +{ + + return compare(p_Other); +} + +/* + MDNSResponder::stcMDNSServiceTxts::operator!= +*/ +bool MDNSResponder::stcMDNSServiceTxts::operator!=(const stcMDNSServiceTxts& p_Other) const +{ + + return !compare(p_Other); +} + + +/** + MDNSResponder::stcMDNS_MsgHeader + + A MDNS message haeder. + +*/ + +/* + MDNSResponder::stcMDNS_MsgHeader::stcMDNS_MsgHeader +*/ +MDNSResponder::stcMDNS_MsgHeader::stcMDNS_MsgHeader(uint16_t p_u16ID /*= 0*/, + bool p_bQR /*= false*/, + unsigned char p_ucOpcode /*= 0*/, + bool p_bAA /*= false*/, + bool p_bTC /*= false*/, + bool p_bRD /*= false*/, + bool p_bRA /*= false*/, + unsigned char p_ucRCode /*= 0*/, + uint16_t p_u16QDCount /*= 0*/, + uint16_t p_u16ANCount /*= 0*/, + uint16_t p_u16NSCount /*= 0*/, + uint16_t p_u16ARCount /*= 0*/) + : m_u16ID(p_u16ID), + m_1bQR(p_bQR), m_4bOpcode(p_ucOpcode), m_1bAA(p_bAA), m_1bTC(p_bTC), m_1bRD(p_bRD), + m_1bRA(p_bRA), m_3bZ(0), m_4bRCode(p_ucRCode), + m_u16QDCount(p_u16QDCount), + m_u16ANCount(p_u16ANCount), + m_u16NSCount(p_u16NSCount), + m_u16ARCount(p_u16ARCount) +{ + +} + + +/** + MDNSResponder::stcMDNS_RRDomain + + A MDNS domain object. + The labels of the domain are stored (DNS-like encoded) in 'm_acName': + [length byte]varlength label[length byte]varlength label[0] + 'm_u16NameLength' stores the used length of 'm_acName'. + Dynamic label addition is supported. + Comparison is supported. + Export as byte array 'esp8266.local' is supported. + +*/ + +/* + MDNSResponder::stcMDNS_RRDomain::stcMDNS_RRDomain constructor +*/ +MDNSResponder::stcMDNS_RRDomain::stcMDNS_RRDomain(void) + : m_u16NameLength(0) +{ + + clear(); +} + +/* + MDNSResponder::stcMDNS_RRDomain::stcMDNS_RRDomain copy-constructor +*/ +MDNSResponder::stcMDNS_RRDomain::stcMDNS_RRDomain(const stcMDNS_RRDomain& p_Other) + : m_u16NameLength(0) +{ + + operator=(p_Other); +} + +/* + MDNSResponder::stcMDNS_RRDomain::operator = +*/ +MDNSResponder::stcMDNS_RRDomain& MDNSResponder::stcMDNS_RRDomain::operator=(const stcMDNS_RRDomain& p_Other) +{ + + if (&p_Other != this) + { + memcpy(m_acName, p_Other.m_acName, sizeof(m_acName)); + m_u16NameLength = p_Other.m_u16NameLength; + } + return *this; +} + +/* + MDNSResponder::stcMDNS_RRDomain::clear +*/ +bool MDNSResponder::stcMDNS_RRDomain::clear(void) +{ + + memset(m_acName, 0, sizeof(m_acName)); + m_u16NameLength = 0; + return true; +} + +/* + MDNSResponder::stcMDNS_RRDomain::addLabel +*/ +bool MDNSResponder::stcMDNS_RRDomain::addLabel(const char* p_pcLabel, + bool p_bPrependUnderline /*= false*/) +{ + + bool bResult = false; + + size_t stLength = (p_pcLabel + ? (strlen(p_pcLabel) + (p_bPrependUnderline ? 1 : 0)) + : 0); + if ((MDNS_DOMAIN_LABEL_MAXLENGTH >= stLength) && + (MDNS_DOMAIN_MAXLENGTH >= (m_u16NameLength + (1 + stLength)))) + { + // Length byte + m_acName[m_u16NameLength] = (unsigned char)stLength; // Might be 0! + ++m_u16NameLength; + // Label + if (stLength) + { + if (p_bPrependUnderline) + { + m_acName[m_u16NameLength++] = '_'; + --stLength; + } + strncpy(&(m_acName[m_u16NameLength]), p_pcLabel, stLength); m_acName[m_u16NameLength + stLength] = 0; + m_u16NameLength += stLength; + } + bResult = true; + } + return bResult; +} + +/* + MDNSResponder::stcMDNS_RRDomain::compare +*/ +bool MDNSResponder::stcMDNS_RRDomain::compare(const stcMDNS_RRDomain& p_Other) const +{ + + bool bResult = false; + + if (m_u16NameLength == p_Other.m_u16NameLength) + { + const char* pT = m_acName; + const char* pO = p_Other.m_acName; + while ((pT) && + (pO) && + (*((unsigned char*)pT) == *((unsigned char*)pO)) && // Same length AND + (0 == strncasecmp((pT + 1), (pO + 1), *((unsigned char*)pT)))) // Same content + { + if (*((unsigned char*)pT)) // Not 0 + { + pT += (1 + * ((unsigned char*)pT)); // Shift by length byte and lenght + pO += (1 + * ((unsigned char*)pO)); + } + else // Is 0 -> Successfully reached the end + { + bResult = true; + break; + } + } + } + return bResult; +} + +/* + MDNSResponder::stcMDNS_RRDomain::operator == +*/ +bool MDNSResponder::stcMDNS_RRDomain::operator==(const stcMDNS_RRDomain& p_Other) const +{ + + return compare(p_Other); +} + +/* + MDNSResponder::stcMDNS_RRDomain::operator != +*/ +bool MDNSResponder::stcMDNS_RRDomain::operator!=(const stcMDNS_RRDomain& p_Other) const +{ + + return !compare(p_Other); +} + +/* + MDNSResponder::stcMDNS_RRDomain::operator > +*/ +bool MDNSResponder::stcMDNS_RRDomain::operator>(const stcMDNS_RRDomain& p_Other) const +{ + + // TODO: Check, if this is a good idea... + return !compare(p_Other); +} + +/* + MDNSResponder::stcMDNS_RRDomain::c_strLength +*/ +size_t MDNSResponder::stcMDNS_RRDomain::c_strLength(void) const +{ + + size_t stLength = 0; + + unsigned char* pucLabelLength = (unsigned char*)m_acName; + while (*pucLabelLength) + { + stLength += (*pucLabelLength + 1 /* +1 for '.' or '\0'*/); + pucLabelLength += (*pucLabelLength + 1); + } + return stLength; +} + +/* + MDNSResponder::stcMDNS_RRDomain::c_str +*/ +bool MDNSResponder::stcMDNS_RRDomain::c_str(char* p_pcBuffer) +{ + + bool bResult = false; + + if (p_pcBuffer) + { + *p_pcBuffer = 0; + unsigned char* pucLabelLength = (unsigned char*)m_acName; + while (*pucLabelLength) + { + memcpy(p_pcBuffer, (const char*)(pucLabelLength + 1), *pucLabelLength); + p_pcBuffer += *pucLabelLength; + pucLabelLength += (*pucLabelLength + 1); + *p_pcBuffer++ = (*pucLabelLength ? '.' : '\0'); + } + bResult = true; + } + return bResult; +} + + +/** + MDNSResponder::stcMDNS_RRAttributes + + A MDNS attributes object. + +*/ + +/* + MDNSResponder::stcMDNS_RRAttributes::stcMDNS_RRAttributes constructor +*/ +MDNSResponder::stcMDNS_RRAttributes::stcMDNS_RRAttributes(uint16_t p_u16Type /*= 0*/, + uint16_t p_u16Class /*= 1 DNS_RRCLASS_IN Internet*/) + : m_u16Type(p_u16Type), + m_u16Class(p_u16Class) +{ + +} + +/* + MDNSResponder::stcMDNS_RRAttributes::stcMDNS_RRAttributes copy-constructor +*/ +MDNSResponder::stcMDNS_RRAttributes::stcMDNS_RRAttributes(const MDNSResponder::stcMDNS_RRAttributes& p_Other) +{ + + operator=(p_Other); +} + +/* + MDNSResponder::stcMDNS_RRAttributes::operator = +*/ +MDNSResponder::stcMDNS_RRAttributes& MDNSResponder::stcMDNS_RRAttributes::operator=(const MDNSResponder::stcMDNS_RRAttributes& p_Other) +{ + + if (&p_Other != this) + { + m_u16Type = p_Other.m_u16Type; + m_u16Class = p_Other.m_u16Class; + } + return *this; +} + + +/** + MDNSResponder::stcMDNS_RRHeader + + A MDNS record header (domain and attributes) object. + +*/ + +/* + MDNSResponder::stcMDNS_RRHeader::stcMDNS_RRHeader constructor +*/ +MDNSResponder::stcMDNS_RRHeader::stcMDNS_RRHeader(void) +{ + +} + +/* + MDNSResponder::stcMDNS_RRHeader::stcMDNS_RRHeader copy-constructor +*/ +MDNSResponder::stcMDNS_RRHeader::stcMDNS_RRHeader(const stcMDNS_RRHeader& p_Other) +{ + + operator=(p_Other); +} + +/* + MDNSResponder::stcMDNS_RRHeader::operator = +*/ +MDNSResponder::stcMDNS_RRHeader& MDNSResponder::stcMDNS_RRHeader::operator=(const MDNSResponder::stcMDNS_RRHeader& p_Other) +{ + + if (&p_Other != this) + { + m_Domain = p_Other.m_Domain; + m_Attributes = p_Other.m_Attributes; + } + return *this; +} + +/* + MDNSResponder::stcMDNS_RRHeader::clear +*/ +bool MDNSResponder::stcMDNS_RRHeader::clear(void) +{ + + m_Domain.clear(); + return true; +} + + +/** + MDNSResponder::stcMDNS_RRQuestion + + A MDNS question record object (header + question flags) + +*/ + +/* + MDNSResponder::stcMDNS_RRQuestion::stcMDNS_RRQuestion constructor +*/ +MDNSResponder::stcMDNS_RRQuestion::stcMDNS_RRQuestion(void) + : m_pNext(0), + m_bUnicast(false) +{ + +} + + +/** + MDNSResponder::stcMDNS_RRAnswer + + A MDNS answer record object (header + answer content). + This is a 'virtual' base class for all other MDNS answer classes. + +*/ + +/* + MDNSResponder::stcMDNS_RRAnswer::stcMDNS_RRAnswer constructor +*/ +MDNSResponder::stcMDNS_RRAnswer::stcMDNS_RRAnswer(enuAnswerType p_AnswerType, + const MDNSResponder::stcMDNS_RRHeader& p_Header, + uint32_t p_u32TTL) + : m_pNext(0), + m_AnswerType(p_AnswerType), + m_Header(p_Header), + m_u32TTL(p_u32TTL) +{ + + // Extract 'cache flush'-bit + m_bCacheFlush = (m_Header.m_Attributes.m_u16Class & 0x8000); + m_Header.m_Attributes.m_u16Class &= (~0x8000); +} + +/* + MDNSResponder::stcMDNS_RRAnswer::~stcMDNS_RRAnswer destructor +*/ +MDNSResponder::stcMDNS_RRAnswer::~stcMDNS_RRAnswer(void) +{ + +} + +/* + MDNSResponder::stcMDNS_RRAnswer::answerType +*/ +MDNSResponder::enuAnswerType MDNSResponder::stcMDNS_RRAnswer::answerType(void) const +{ + + return m_AnswerType; +} + +/* + MDNSResponder::stcMDNS_RRAnswer::clear +*/ +bool MDNSResponder::stcMDNS_RRAnswer::clear(void) +{ + + m_pNext = 0; + m_Header.clear(); + return true; +} + + +/** + MDNSResponder::stcMDNS_RRAnswerA + + A MDNS A answer object. + Extends the base class by an IP4 address member. + +*/ + +#ifdef MDNS_IP4_SUPPORT +/* + MDNSResponder::stcMDNS_RRAnswerA::stcMDNS_RRAnswerA constructor +*/ +MDNSResponder::stcMDNS_RRAnswerA::stcMDNS_RRAnswerA(const MDNSResponder::stcMDNS_RRHeader& p_Header, + uint32_t p_u32TTL) + : stcMDNS_RRAnswer(AnswerType_A, p_Header, p_u32TTL), + m_IPAddress(0, 0, 0, 0) +{ + +} + +/* + MDNSResponder::stcMDNS_RRAnswerA::stcMDNS_RRAnswerA destructor +*/ +MDNSResponder::stcMDNS_RRAnswerA::~stcMDNS_RRAnswerA(void) +{ + + clear(); +} + +/* + MDNSResponder::stcMDNS_RRAnswerA::clear +*/ +bool MDNSResponder::stcMDNS_RRAnswerA::clear(void) +{ + + m_IPAddress = IPAddress(0, 0, 0, 0); + return true; +} +#endif + + +/** + MDNSResponder::stcMDNS_RRAnswerPTR + + A MDNS PTR answer object. + Extends the base class by a MDNS domain member. + +*/ + +/* + MDNSResponder::stcMDNS_RRAnswerPTR::stcMDNS_RRAnswerPTR constructor +*/ +MDNSResponder::stcMDNS_RRAnswerPTR::stcMDNS_RRAnswerPTR(const MDNSResponder::stcMDNS_RRHeader& p_Header, + uint32_t p_u32TTL) + : stcMDNS_RRAnswer(AnswerType_PTR, p_Header, p_u32TTL) +{ + +} + +/* + MDNSResponder::stcMDNS_RRAnswerPTR::~stcMDNS_RRAnswerPTR destructor +*/ +MDNSResponder::stcMDNS_RRAnswerPTR::~stcMDNS_RRAnswerPTR(void) +{ + + clear(); +} + +/* + MDNSResponder::stcMDNS_RRAnswerPTR::clear +*/ +bool MDNSResponder::stcMDNS_RRAnswerPTR::clear(void) +{ + + m_PTRDomain.clear(); + return true; +} + + +/** + MDNSResponder::stcMDNS_RRAnswerTXT + + A MDNS TXT answer object. + Extends the base class by a MDNS TXT items list member. + +*/ + +/* + MDNSResponder::stcMDNS_RRAnswerTXT::stcMDNS_RRAnswerTXT constructor +*/ +MDNSResponder::stcMDNS_RRAnswerTXT::stcMDNS_RRAnswerTXT(const MDNSResponder::stcMDNS_RRHeader& p_Header, + uint32_t p_u32TTL) + : stcMDNS_RRAnswer(AnswerType_TXT, p_Header, p_u32TTL) +{ + +} + +/* + MDNSResponder::stcMDNS_RRAnswerTXT::~stcMDNS_RRAnswerTXT destructor +*/ +MDNSResponder::stcMDNS_RRAnswerTXT::~stcMDNS_RRAnswerTXT(void) +{ + + clear(); +} + +/* + MDNSResponder::stcMDNS_RRAnswerTXT::clear +*/ +bool MDNSResponder::stcMDNS_RRAnswerTXT::clear(void) +{ + + m_Txts.clear(); + return true; +} + + +/** + MDNSResponder::stcMDNS_RRAnswerAAAA + + A MDNS AAAA answer object. + (Should) extend the base class by an IP6 address member. + +*/ + +#ifdef MDNS_IP6_SUPPORT +/* + MDNSResponder::stcMDNS_RRAnswerAAAA::stcMDNS_RRAnswerAAAA constructor +*/ +MDNSResponder::stcMDNS_RRAnswerAAAA::stcMDNS_RRAnswerAAAA(const MDNSResponder::stcMDNS_RRHeader& p_Header, + uint32_t p_u32TTL) + : stcMDNS_RRAnswer(AnswerType_AAAA, p_Header, p_u32TTL) +{ + +} + +/* + MDNSResponder::stcMDNS_RRAnswerAAAA::~stcMDNS_RRAnswerAAAA destructor +*/ +MDNSResponder::stcMDNS_RRAnswerAAAA::~stcMDNS_RRAnswerAAAA(void) +{ + + clear(); +} + +/* + MDNSResponder::stcMDNS_RRAnswerAAAA::clear +*/ +bool MDNSResponder::stcMDNS_RRAnswerAAAA::clear(void) +{ + + return true; +} +#endif + + +/** + MDNSResponder::stcMDNS_RRAnswerSRV + + A MDNS SRV answer object. + Extends the base class by a port member. + +*/ + +/* + MDNSResponder::stcMDNS_RRAnswerSRV::stcMDNS_RRAnswerSRV constructor +*/ +MDNSResponder::stcMDNS_RRAnswerSRV::stcMDNS_RRAnswerSRV(const MDNSResponder::stcMDNS_RRHeader& p_Header, + uint32_t p_u32TTL) + : stcMDNS_RRAnswer(AnswerType_SRV, p_Header, p_u32TTL), + m_u16Priority(0), + m_u16Weight(0), + m_u16Port(0) +{ + +} + +/* + MDNSResponder::stcMDNS_RRAnswerSRV::~stcMDNS_RRAnswerSRV destructor +*/ +MDNSResponder::stcMDNS_RRAnswerSRV::~stcMDNS_RRAnswerSRV(void) +{ + + clear(); +} + +/* + MDNSResponder::stcMDNS_RRAnswerSRV::clear +*/ +bool MDNSResponder::stcMDNS_RRAnswerSRV::clear(void) +{ + + m_u16Priority = 0; + m_u16Weight = 0; + m_u16Port = 0; + m_SRVDomain.clear(); + return true; +} + + +/** + MDNSResponder::stcMDNS_RRAnswerGeneric + + An unknown (generic) MDNS answer object. + Extends the base class by a RDATA buffer member. + +*/ + +/* + MDNSResponder::stcMDNS_RRAnswerGeneric::stcMDNS_RRAnswerGeneric constructor +*/ +MDNSResponder::stcMDNS_RRAnswerGeneric::stcMDNS_RRAnswerGeneric(const stcMDNS_RRHeader& p_Header, + uint32_t p_u32TTL) + : stcMDNS_RRAnswer(AnswerType_Generic, p_Header, p_u32TTL), + m_u16RDLength(0), + m_pu8RDData(0) +{ + +} + +/* + MDNSResponder::stcMDNS_RRAnswerGeneric::~stcMDNS_RRAnswerGeneric destructor +*/ +MDNSResponder::stcMDNS_RRAnswerGeneric::~stcMDNS_RRAnswerGeneric(void) +{ + + clear(); +} + +/* + MDNSResponder::stcMDNS_RRAnswerGeneric::clear +*/ +bool MDNSResponder::stcMDNS_RRAnswerGeneric::clear(void) +{ + + if (m_pu8RDData) + { + delete[] m_pu8RDData; + m_pu8RDData = 0; + } + m_u16RDLength = 0; + + return true; +} + + +/** + MDNSResponder::stcProbeInformation + + Probing status information for a host or service domain + +*/ + +/* + MDNSResponder::stcProbeInformation::stcProbeInformation constructor +*/ +MDNSResponder::stcProbeInformation::stcProbeInformation(void) + : m_ProbingStatus(ProbingStatus_WaitingForData), + m_u8SentCount(0), + m_Timeout(esp8266::polledTimeout::oneShotMs::neverExpires), + m_bConflict(false), + m_bTiebreakNeeded(false), + m_fnHostProbeResultCallback(0), + m_fnServiceProbeResultCallback(0) +{ +} + +/* + MDNSResponder::stcProbeInformation::clear +*/ +bool MDNSResponder::stcProbeInformation::clear(bool p_bClearUserdata /*= false*/) +{ + + m_ProbingStatus = ProbingStatus_WaitingForData; + m_u8SentCount = 0; + m_Timeout.resetToNeverExpires(); + m_bConflict = false; + m_bTiebreakNeeded = false; + if (p_bClearUserdata) + { + m_fnHostProbeResultCallback = 0; + m_fnServiceProbeResultCallback = 0; + } + return true; +} + +/** + MDNSResponder::stcMDNSService + + A MDNS service object (to be announced by the MDNS responder) + The service instance may be '\0'; in this case the hostname is used + and the flag m_bAutoName is set. If the hostname changes, all 'auto- + named' services are renamed also. + m_u8Replymask is used while preparing a response to a MDNS query. It is + resetted in '_sendMDNSMessage' afterwards. +*/ + +/* + MDNSResponder::stcMDNSService::stcMDNSService constructor +*/ +MDNSResponder::stcMDNSService::stcMDNSService(const char* p_pcName /*= 0*/, + const char* p_pcService /*= 0*/, + const char* p_pcProtocol /*= 0*/) + : m_pNext(0), + m_pcName(0), + m_bAutoName(false), + m_pcService(0), + m_pcProtocol(0), + m_u16Port(0), + m_u8ReplyMask(0), + m_fnTxtCallback(0) +{ + + setName(p_pcName); + setService(p_pcService); + setProtocol(p_pcProtocol); +} + +/* + MDNSResponder::stcMDNSService::~stcMDNSService destructor +*/ +MDNSResponder::stcMDNSService::~stcMDNSService(void) +{ + + releaseName(); + releaseService(); + releaseProtocol(); +} + +/* + MDNSResponder::stcMDNSService::setName +*/ +bool MDNSResponder::stcMDNSService::setName(const char* p_pcName) +{ + + bool bResult = false; + + releaseName(); + size_t stLength = (p_pcName ? strlen(p_pcName) : 0); + if (stLength) + { + if ((bResult = (0 != (m_pcName = new char[stLength + 1])))) + { + strncpy(m_pcName, p_pcName, stLength); + m_pcName[stLength] = 0; + } + } + else + { + bResult = true; + } + return bResult; +} + +/* + MDNSResponder::stcMDNSService::releaseName +*/ +bool MDNSResponder::stcMDNSService::releaseName(void) +{ + + if (m_pcName) + { + delete[] m_pcName; + m_pcName = 0; + } + return true; +} + +/* + MDNSResponder::stcMDNSService::setService +*/ +bool MDNSResponder::stcMDNSService::setService(const char* p_pcService) +{ + + bool bResult = false; + + releaseService(); + size_t stLength = (p_pcService ? strlen(p_pcService) : 0); + if (stLength) + { + if ((bResult = (0 != (m_pcService = new char[stLength + 1])))) + { + strncpy(m_pcService, p_pcService, stLength); + m_pcService[stLength] = 0; + } + } + else + { + bResult = true; + } + return bResult; +} + +/* + MDNSResponder::stcMDNSService::releaseService +*/ +bool MDNSResponder::stcMDNSService::releaseService(void) +{ + + if (m_pcService) + { + delete[] m_pcService; + m_pcService = 0; + } + return true; +} + +/* + MDNSResponder::stcMDNSService::setProtocol +*/ +bool MDNSResponder::stcMDNSService::setProtocol(const char* p_pcProtocol) +{ + + bool bResult = false; + + releaseProtocol(); + size_t stLength = (p_pcProtocol ? strlen(p_pcProtocol) : 0); + if (stLength) + { + if ((bResult = (0 != (m_pcProtocol = new char[stLength + 1])))) + { + strncpy(m_pcProtocol, p_pcProtocol, stLength); + m_pcProtocol[stLength] = 0; + } + } + else + { + bResult = true; + } + return bResult; +} + +/* + MDNSResponder::stcMDNSService::releaseProtocol +*/ +bool MDNSResponder::stcMDNSService::releaseProtocol(void) +{ + + if (m_pcProtocol) + { + delete[] m_pcProtocol; + m_pcProtocol = 0; + } + return true; +} + + +/** + MDNSResponder::stcMDNSServiceQuery + + A MDNS service query object. + Service queries may be static or dynamic. + As the static service query is processed in the blocking function 'queryService', + only one static service service may exist. The processing of the answers is done + on the WiFi-stack side of the ESP stack structure (via 'UDPContext.onRx(_update)'). + +*/ + +/** + MDNSResponder::stcMDNSServiceQuery::stcAnswer + + One answer for a service query. + Every answer must contain + - a service instance entry (pivot), + and may contain + - a host domain, + - a port + - an IP4 address + (- an IP6 address) + - a MDNS TXTs + The existance of a component is flaged in 'm_u32ContentFlags'. + For every answer component a TTL value is maintained. + Answer objects can be connected to a linked list. + + For the host domain, service domain and TXTs components, a char array + representation can be retrieved (which is created on demand). + +*/ + +/** + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL + + The TTL (Time-To-Live) for an specific answer content. + The 80% and outdated states are calculated based on the current time (millis) + and the 'set' time (also millis). + If the answer is scheduled for an update, the corresponding flag should be set. + + / + + / * + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::stcTTL constructor + / + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::stcTTL(uint32_t p_u32TTL / *= 0* /) + : m_bUpdateScheduled(false) { + + set(p_u32TTL * 1000); + } + + / * + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::set + / + bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::set(uint32_t p_u32TTL) { + + m_TTLTimeFlag.restart(p_u32TTL * 1000); + m_bUpdateScheduled = false; + + return true; + } + + / * + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::has80Percent + / + bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::has80Percent(void) const { + + return ((m_TTLTimeFlag.getTimeout()) && + (!m_bUpdateScheduled) && + (m_TTLTimeFlag.hypotheticalTimeout((m_TTLTimeFlag.getTimeout() * 800) / 1000))); + } + + / * + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::isOutdated + / + bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::isOutdated(void) const { + + return ((m_TTLTimeFlag.getTimeout()) && + (m_TTLTimeFlag.flagged())); + }*/ + + +/** + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL + + The TTL (Time-To-Live) for an specific answer content. + The 80% and outdated states are calculated based on the current time (millis) + and the 'set' time (also millis). + If the answer is scheduled for an update, the corresponding flag should be set. + +*/ + +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::stcTTL constructor +*/ +MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::stcTTL(void) + : m_u32TTL(0), + m_TTLTimeout(esp8266::polledTimeout::oneShotMs::neverExpires), + m_timeoutLevel(TIMEOUTLEVEL_UNSET) +{ + +} + +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::set +*/ +bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::set(uint32_t p_u32TTL) +{ + + m_u32TTL = p_u32TTL; + if (m_u32TTL) + { + m_timeoutLevel = TIMEOUTLEVEL_BASE; // Set to 80% + m_TTLTimeout.reset(timeout()); + } + else + { + m_timeoutLevel = TIMEOUTLEVEL_UNSET; // undef + m_TTLTimeout.resetToNeverExpires(); + } + return true; +} + +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::flagged +*/ +bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::flagged(void) +{ + + return ((m_u32TTL) && + (TIMEOUTLEVEL_UNSET != m_timeoutLevel) && + (m_TTLTimeout.expired())); +} + +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::restart +*/ +bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::restart(void) +{ + + bool bResult = true; + + if ((TIMEOUTLEVEL_BASE <= m_timeoutLevel) && // >= 80% AND + (TIMEOUTLEVEL_FINAL > m_timeoutLevel)) // < 100% + { + + m_timeoutLevel += TIMEOUTLEVEL_INTERVAL; // increment by 5% + m_TTLTimeout.reset(timeout()); + } + else + { + bResult = false; + m_TTLTimeout.resetToNeverExpires(); + m_timeoutLevel = TIMEOUTLEVEL_UNSET; + } + return bResult; +} + +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::prepareDeletion +*/ +bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::prepareDeletion(void) +{ + + m_timeoutLevel = TIMEOUTLEVEL_FINAL; + m_TTLTimeout.reset(1 * 1000); // See RFC 6762, 10.1 + + return true; +} + +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::finalTimeoutLevel +*/ +bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::finalTimeoutLevel(void) const +{ + + return (TIMEOUTLEVEL_FINAL == m_timeoutLevel); +} + +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::timeout +*/ +unsigned long MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::timeout(void) const +{ + + uint32_t u32Timeout = esp8266::polledTimeout::oneShotMs::neverExpires; + + if (TIMEOUTLEVEL_BASE == m_timeoutLevel) // 80% + { + u32Timeout = (m_u32TTL * 800); // to milliseconds + } + else if ((TIMEOUTLEVEL_BASE < m_timeoutLevel) && // >80% AND + (TIMEOUTLEVEL_FINAL >= m_timeoutLevel)) // <= 100% + { + + u32Timeout = (m_u32TTL * 50); + } // else: invalid + return u32Timeout; +} + + +#ifdef MDNS_IP4_SUPPORT +/** + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address + +*/ + +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address::stcIP4Address constructor +*/ +MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address::stcIP4Address(IPAddress p_IPAddress, + uint32_t p_u32TTL /*= 0*/) + : m_pNext(0), + m_IPAddress(p_IPAddress) +{ + + m_TTL.set(p_u32TTL); +} +#endif + + +/** + MDNSResponder::stcMDNSServiceQuery::stcAnswer +*/ + +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcAnswer constructor +*/ +MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcAnswer(void) + : m_pNext(0), + m_pcServiceDomain(0), + m_pcHostDomain(0), + m_u16Port(0), + m_pcTxts(0), +#ifdef MDNS_IP4_SUPPORT + m_pIP4Addresses(0), +#endif +#ifdef MDNS_IP6_SUPPORT + m_pIP6Addresses(0), +#endif + m_u32ContentFlags(0) +{ +} + +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::~stcAnswer destructor +*/ +MDNSResponder::stcMDNSServiceQuery::stcAnswer::~stcAnswer(void) +{ + + clear(); +} + +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::clear +*/ +bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::clear(void) +{ + + return ((releaseTxts()) && +#ifdef MDNS_IP4_SUPPORT + (releaseIP4Addresses()) && +#endif +#ifdef MDNS_IP6_SUPPORT + (releaseIP6Addresses()) +#endif + (releaseHostDomain()) && + (releaseServiceDomain())); +} + +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::allocServiceDomain + + Alloc memory for the char array representation of the service domain. + +*/ +char* MDNSResponder::stcMDNSServiceQuery::stcAnswer::allocServiceDomain(size_t p_stLength) +{ + + releaseServiceDomain(); + if (p_stLength) + { + m_pcServiceDomain = new char[p_stLength]; + } + return m_pcServiceDomain; +} + +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseServiceDomain +*/ +bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseServiceDomain(void) +{ + + if (m_pcServiceDomain) + { + delete[] m_pcServiceDomain; + m_pcServiceDomain = 0; + } + return true; +} + +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::allocHostDomain + + Alloc memory for the char array representation of the host domain. + +*/ +char* MDNSResponder::stcMDNSServiceQuery::stcAnswer::allocHostDomain(size_t p_stLength) +{ + + releaseHostDomain(); + if (p_stLength) + { + m_pcHostDomain = new char[p_stLength]; + } + return m_pcHostDomain; +} + +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseHostDomain +*/ +bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseHostDomain(void) +{ + + if (m_pcHostDomain) + { + delete[] m_pcHostDomain; + m_pcHostDomain = 0; + } + return true; +} + +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::allocTxts + + Alloc memory for the char array representation of the TXT items. + +*/ +char* MDNSResponder::stcMDNSServiceQuery::stcAnswer::allocTxts(size_t p_stLength) +{ + + releaseTxts(); + if (p_stLength) + { + m_pcTxts = new char[p_stLength]; + } + return m_pcTxts; +} + +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseTxts +*/ +bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseTxts(void) +{ + + if (m_pcTxts) + { + delete[] m_pcTxts; + m_pcTxts = 0; + } + return true; +} + +#ifdef MDNS_IP4_SUPPORT +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseIP4Addresses +*/ +bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseIP4Addresses(void) +{ + + while (m_pIP4Addresses) + { + stcIP4Address* pNext = m_pIP4Addresses->m_pNext; + delete m_pIP4Addresses; + m_pIP4Addresses = pNext; + } + return true; +} + +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::addIP4Address +*/ +bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::addIP4Address(MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address* p_pIP4Address) +{ + + bool bResult = false; + + if (p_pIP4Address) + { + p_pIP4Address->m_pNext = m_pIP4Addresses; + m_pIP4Addresses = p_pIP4Address; + bResult = true; + } + return bResult; +} + +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::removeIP4Address +*/ +bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::removeIP4Address(MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address* p_pIP4Address) +{ + + bool bResult = false; + + if (p_pIP4Address) + { + stcIP4Address* pPred = m_pIP4Addresses; + while ((pPred) && + (pPred->m_pNext != p_pIP4Address)) + { + pPred = pPred->m_pNext; + } + if (pPred) + { + pPred->m_pNext = p_pIP4Address->m_pNext; + delete p_pIP4Address; + bResult = true; + } + else if (m_pIP4Addresses == p_pIP4Address) // No predecesor, but first item + { + m_pIP4Addresses = p_pIP4Address->m_pNext; + delete p_pIP4Address; + bResult = true; + } + } + return bResult; +} + +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP4Address (const) +*/ +const MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address* MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP4Address(const IPAddress& p_IPAddress) const +{ + + return (stcIP4Address*)(((const stcAnswer*)this)->findIP4Address(p_IPAddress)); +} + +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP4Address +*/ +MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address* MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP4Address(const IPAddress& p_IPAddress) +{ + + stcIP4Address* pIP4Address = m_pIP4Addresses; + while (pIP4Address) + { + if (pIP4Address->m_IPAddress == p_IPAddress) + { + break; + } + pIP4Address = pIP4Address->m_pNext; + } + return pIP4Address; +} + +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP4AddressCount +*/ +uint32_t MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP4AddressCount(void) const +{ + + uint32_t u32Count = 0; + + stcIP4Address* pIP4Address = m_pIP4Addresses; + while (pIP4Address) + { + ++u32Count; + pIP4Address = pIP4Address->m_pNext; + } + return u32Count; +} + +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP4AddressAtIndex +*/ +MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address* MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP4AddressAtIndex(uint32_t p_u32Index) +{ + + return (stcIP4Address*)(((const stcAnswer*)this)->IP4AddressAtIndex(p_u32Index)); +} + +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP4AddressAtIndex (const) +*/ +const MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address* MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP4AddressAtIndex(uint32_t p_u32Index) const +{ + + const stcIP4Address* pIP4Address = 0; + + if (((uint32_t)(-1) != p_u32Index) && + (m_pIP4Addresses)) + { + + uint32_t u32Index; + for (pIP4Address = m_pIP4Addresses, u32Index = 0; ((pIP4Address) && (u32Index < p_u32Index)); pIP4Address = pIP4Address->m_pNext, ++u32Index); + } + return pIP4Address; +} +#endif + +#ifdef MDNS_IP6_SUPPORT +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseIP6Addresses +*/ +bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseIP6Addresses(void) +{ + + while (m_pIP6Addresses) + { + stcIP6Address* pNext = m_pIP6Addresses->m_pNext; + delete m_pIP6Addresses; + m_pIP6Addresses = pNext; + } + return true; +} + +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::addIP6Address +*/ +bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::addIP6Address(MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP6Address* p_pIP6Address) +{ + + bool bResult = false; + + if (p_pIP6Address) + { + p_pIP6Address->m_pNext = m_pIP6Addresses; + m_pIP6Addresses = p_pIP6Address; + bResult = true; + } + return bResult; +} + +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::removeIP6Address +*/ +bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::removeIP6Address(MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP6Address* p_pIP6Address) +{ + + bool bResult = false; + + if (p_pIP6Address) + { + stcIP6Address* pPred = m_pIP6Addresses; + while ((pPred) && + (pPred->m_pNext != p_pIP6Address)) + { + pPred = pPred->m_pNext; + } + if (pPred) + { + pPred->m_pNext = p_pIP6Address->m_pNext; + delete p_pIP6Address; + bResult = true; + } + else if (m_pIP6Addresses == p_pIP6Address) // No predecesor, but first item + { + m_pIP6Addresses = p_pIP6Address->m_pNext; + delete p_pIP6Address; + bResult = true; + } + } + return bResult; +} + +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP6Address +*/ +MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP6Address* MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP6Address(const IP6Address& p_IPAddress) +{ + + return (stcIP6Address*)(((const stcAnswer*)this)->findIP6Address(p_IPAddress)); +} + +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP6Address (const) +*/ +const MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP6Address* MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP6Address(const IPAddress& p_IPAddress) const +{ + + const stcIP6Address* pIP6Address = m_pIP6Addresses; + while (pIP6Address) + { + if (p_IP6Address->m_IPAddress == p_IPAddress) + { + break; + } + pIP6Address = pIP6Address->m_pNext; + } + return pIP6Address; +} + +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP6AddressCount +*/ +uint32_t MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP6AddressCount(void) const +{ + + uint32_t u32Count = 0; + + stcIP6Address* pIP6Address = m_pIP6Addresses; + while (pIP6Address) + { + ++u32Count; + pIP6Address = pIP6Address->m_pNext; + } + return u32Count; +} + +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP6AddressAtIndex (const) +*/ +const MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP6Address* MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP6AddressAtIndex(uint32_t p_u32Index) const +{ + + return (stcIP6Address*)(((const stcAnswer*)this)->IP6AddressAtIndex(p_u32Index)); +} + +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP6AddressAtIndex +*/ +MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP6Address* MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP6AddressAtIndex(uint32_t p_u32Index) +{ + + stcIP6Address* pIP6Address = 0; + + if (((uint32_t)(-1) != p_u32Index) && + (m_pIP6Addresses)) + { + + uint32_t u32Index; + for (pIP6Address = m_pIP6Addresses, u32Index = 0; ((pIP6Address) && (u32Index < p_u32Index)); pIP6Address = pIP6Address->m_pNext, ++u32Index); + } + return pIP6Address; +} +#endif + + +/** + MDNSResponder::stcMDNSServiceQuery + + A service query object. + A static query is flaged via 'm_bLegacyQuery'; while the function 'queryService' + is waiting for answers, the internal flag 'm_bAwaitingAnswers' is set. When the + timeout is reached, the flag is removed. These two flags are only used for static + service queries. + All answers to the service query are stored in 'm_pAnswers' list. + Individual answers may be addressed by index (in the list of answers). + Every time a answer component is added (or changes) in a dynamic service query, + the callback 'm_fnCallback' is called. + The answer list may be searched by service and host domain. + + Service query object may be connected to a linked list. +*/ + +/* + MDNSResponder::stcMDNSServiceQuery::stcMDNSServiceQuery constructor +*/ +MDNSResponder::stcMDNSServiceQuery::stcMDNSServiceQuery(void) + : m_pNext(0), + m_fnCallback(0), + m_bLegacyQuery(false), + m_u8SentCount(0), + m_ResendTimeout(esp8266::polledTimeout::oneShotMs::neverExpires), + m_bAwaitingAnswers(true), + m_pAnswers(0) +{ + + clear(); +} + +/* + MDNSResponder::stcMDNSServiceQuery::~stcMDNSServiceQuery destructor +*/ +MDNSResponder::stcMDNSServiceQuery::~stcMDNSServiceQuery(void) +{ + + clear(); +} + +/* + MDNSResponder::stcMDNSServiceQuery::clear +*/ +bool MDNSResponder::stcMDNSServiceQuery::clear(void) +{ + + m_fnCallback = 0; + m_bLegacyQuery = false; + m_u8SentCount = 0; + m_ResendTimeout.resetToNeverExpires(); + m_bAwaitingAnswers = true; + while (m_pAnswers) + { + stcAnswer* pNext = m_pAnswers->m_pNext; + delete m_pAnswers; + m_pAnswers = pNext; + } + return true; +} + +/* + MDNSResponder::stcMDNSServiceQuery::answerCount +*/ +uint32_t MDNSResponder::stcMDNSServiceQuery::answerCount(void) const +{ + + uint32_t u32Count = 0; + + stcAnswer* pAnswer = m_pAnswers; + while (pAnswer) + { + ++u32Count; + pAnswer = pAnswer->m_pNext; + } + return u32Count; +} + +/* + MDNSResponder::stcMDNSServiceQuery::answerAtIndex +*/ +const MDNSResponder::stcMDNSServiceQuery::stcAnswer* MDNSResponder::stcMDNSServiceQuery::answerAtIndex(uint32_t p_u32Index) const +{ + + const stcAnswer* pAnswer = 0; + + if (((uint32_t)(-1) != p_u32Index) && + (m_pAnswers)) + { + + uint32_t u32Index; + for (pAnswer = m_pAnswers, u32Index = 0; ((pAnswer) && (u32Index < p_u32Index)); pAnswer = pAnswer->m_pNext, ++u32Index); + } + return pAnswer; +} + +/* + MDNSResponder::stcMDNSServiceQuery::answerAtIndex +*/ +MDNSResponder::stcMDNSServiceQuery::stcAnswer* MDNSResponder::stcMDNSServiceQuery::answerAtIndex(uint32_t p_u32Index) +{ + + return (stcAnswer*)(((const stcMDNSServiceQuery*)this)->answerAtIndex(p_u32Index)); +} + +/* + MDNSResponder::stcMDNSServiceQuery::indexOfAnswer +*/ +uint32_t MDNSResponder::stcMDNSServiceQuery::indexOfAnswer(const MDNSResponder::stcMDNSServiceQuery::stcAnswer* p_pAnswer) const +{ + + uint32_t u32Index = 0; + + for (const stcAnswer* pAnswer = m_pAnswers; pAnswer; pAnswer = pAnswer->m_pNext, ++u32Index) + { + if (pAnswer == p_pAnswer) + { + return u32Index; + } + } + return ((uint32_t)(-1)); +} + +/* + MDNSResponder::stcMDNSServiceQuery::addAnswer +*/ +bool MDNSResponder::stcMDNSServiceQuery::addAnswer(MDNSResponder::stcMDNSServiceQuery::stcAnswer* p_pAnswer) +{ + + bool bResult = false; + + if (p_pAnswer) + { + p_pAnswer->m_pNext = m_pAnswers; + m_pAnswers = p_pAnswer; + bResult = true; + } + return bResult; +} + +/* + MDNSResponder::stcMDNSServiceQuery::removeAnswer +*/ +bool MDNSResponder::stcMDNSServiceQuery::removeAnswer(MDNSResponder::stcMDNSServiceQuery::stcAnswer* p_pAnswer) +{ + + bool bResult = false; + + if (p_pAnswer) + { + stcAnswer* pPred = m_pAnswers; + while ((pPred) && + (pPred->m_pNext != p_pAnswer)) + { + pPred = pPred->m_pNext; + } + if (pPred) + { + pPred->m_pNext = p_pAnswer->m_pNext; + delete p_pAnswer; + bResult = true; + } + else if (m_pAnswers == p_pAnswer) // No predecesor, but first item + { + m_pAnswers = p_pAnswer->m_pNext; + delete p_pAnswer; + bResult = true; + } + } + return bResult; +} + +/* + MDNSResponder::stcMDNSServiceQuery::findAnswerForServiceDomain +*/ +MDNSResponder::stcMDNSServiceQuery::stcAnswer* MDNSResponder::stcMDNSServiceQuery::findAnswerForServiceDomain(const MDNSResponder::stcMDNS_RRDomain& p_ServiceDomain) +{ + + stcAnswer* pAnswer = m_pAnswers; + while (pAnswer) + { + if (pAnswer->m_ServiceDomain == p_ServiceDomain) + { + break; + } + pAnswer = pAnswer->m_pNext; + } + return pAnswer; +} + +/* + MDNSResponder::stcMDNSServiceQuery::findAnswerForHostDomain +*/ +MDNSResponder::stcMDNSServiceQuery::stcAnswer* MDNSResponder::stcMDNSServiceQuery::findAnswerForHostDomain(const MDNSResponder::stcMDNS_RRDomain& p_HostDomain) +{ + + stcAnswer* pAnswer = m_pAnswers; + while (pAnswer) + { + if (pAnswer->m_HostDomain == p_HostDomain) + { + break; + } + pAnswer = pAnswer->m_pNext; + } + return pAnswer; +} + + +/** + MDNSResponder::stcMDNSSendParameter + + A 'collection' of properties and flags for one MDNS query or response. + Mainly managed by the 'Control' functions. + The current offset in the UPD output buffer is tracked to be able to do + a simple host or service domain compression. + +*/ + +/** + MDNSResponder::stcMDNSSendParameter::stcDomainCacheItem + + A cached host or service domain, incl. the offset in the UDP output buffer. + +*/ + +/* + MDNSResponder::stcMDNSSendParameter::stcDomainCacheItem::stcDomainCacheItem constructor +*/ +MDNSResponder::stcMDNSSendParameter::stcDomainCacheItem::stcDomainCacheItem(const void* p_pHostnameOrService, + bool p_bAdditionalData, + uint32_t p_u16Offset) + : m_pNext(0), + m_pHostnameOrService(p_pHostnameOrService), + m_bAdditionalData(p_bAdditionalData), + m_u16Offset(p_u16Offset) +{ + +} + +/** + MDNSResponder::stcMDNSSendParameter +*/ + +/* + MDNSResponder::stcMDNSSendParameter::stcMDNSSendParameter constructor +*/ +MDNSResponder::stcMDNSSendParameter::stcMDNSSendParameter(void) + : m_pQuestions(0), + m_pDomainCacheItems(0) +{ + + clear(); +} + +/* + MDNSResponder::stcMDNSSendParameter::~stcMDNSSendParameter destructor +*/ +MDNSResponder::stcMDNSSendParameter::~stcMDNSSendParameter(void) +{ + + clear(); +} + +/* + MDNSResponder::stcMDNSSendParameter::clear +*/ +bool MDNSResponder::stcMDNSSendParameter::clear(void) +{ + + m_u16ID = 0; + m_u8HostReplyMask = 0; + m_u16Offset = 0; + + m_bLegacyQuery = false; + m_bResponse = false; + m_bAuthorative = false; + m_bUnicast = false; + m_bUnannounce = false; + + m_bCacheFlush = true; + + while (m_pQuestions) + { + stcMDNS_RRQuestion* pNext = m_pQuestions->m_pNext; + delete m_pQuestions; + m_pQuestions = pNext; + } + while (m_pDomainCacheItems) + { + stcDomainCacheItem* pNext = m_pDomainCacheItems->m_pNext; + delete m_pDomainCacheItems; + m_pDomainCacheItems = pNext; + } + return true; +} + +/* + MDNSResponder::stcMDNSSendParameter::shiftOffset +*/ +bool MDNSResponder::stcMDNSSendParameter::shiftOffset(uint16_t p_u16Shift) +{ + + m_u16Offset += p_u16Shift; + return true; +} + +/* + MDNSResponder::stcMDNSSendParameter::addDomainCacheItem +*/ +bool MDNSResponder::stcMDNSSendParameter::addDomainCacheItem(const void* p_pHostnameOrService, + bool p_bAdditionalData, + uint16_t p_u16Offset) +{ + + bool bResult = false; + + stcDomainCacheItem* pNewItem = 0; + if ((p_pHostnameOrService) && + (p_u16Offset) && + ((pNewItem = new stcDomainCacheItem(p_pHostnameOrService, p_bAdditionalData, p_u16Offset)))) + { + + pNewItem->m_pNext = m_pDomainCacheItems; + bResult = ((m_pDomainCacheItems = pNewItem)); + } + return bResult; +} + +/* + MDNSResponder::stcMDNSSendParameter::findCachedDomainOffset +*/ +uint16_t MDNSResponder::stcMDNSSendParameter::findCachedDomainOffset(const void* p_pHostnameOrService, + bool p_bAdditionalData) const +{ + + const stcDomainCacheItem* pCacheItem = m_pDomainCacheItems; + + for (; pCacheItem; pCacheItem = pCacheItem->m_pNext) + { + if ((pCacheItem->m_pHostnameOrService == p_pHostnameOrService) && + (pCacheItem->m_bAdditionalData == p_bAdditionalData)) // Found cache item + { + break; + } + } + return (pCacheItem ? pCacheItem->m_u16Offset : 0); +} + +} // namespace MDNSImplementation + +} // namespace esp8266 + + + diff --git a/libraries/ESP8266mDNS/src/LEAmDNS_Transfer.cpp b/libraries/ESP8266mDNS/src/LEAmDNS_Transfer.cpp index 53eb320259..7400abec42 100644 --- a/libraries/ESP8266mDNS/src/LEAmDNS_Transfer.cpp +++ b/libraries/ESP8266mDNS/src/LEAmDNS_Transfer.cpp @@ -1,1587 +1,1779 @@ -/* - * 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 && - 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())) && - (m_pUDPContext->send(ipRemote, m_pUDPContext->getRemotePort()))); - } - else { // Multicast response - bResult = _sendMDNSMessage_Multicast(p_rSendParameter); - } - - // 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) { - - bool bResult = false; - - IPAddress fromIPAddress; - fromIPAddress = _getResponseMulticastInterface(); - 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(p_rSendParameter.m_u16ID, 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; -} - -/** - * 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 - - - - - - +/* + 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 && + 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())) && + (m_pUDPContext->send(ipRemote, m_pUDPContext->getRemotePort()))); + } + else // Multicast response + { + bResult = _sendMDNSMessage_Multicast(p_rSendParameter); + } + + // 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) +{ + + bool bResult = false; + + IPAddress fromIPAddress; + fromIPAddress = _getResponseMulticastInterface(); + 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(p_rSendParameter.m_u16ID, 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; +} + +/** + 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; ((u < p_uLength) && (_udpRead8(u8Byte))); ++u) + { + DEBUG_OUTPUT.printf_P(PSTR("%02x "), u8Byte); + } + // Return to start position + m_pUDPContext->seek(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 + + + + + + diff --git a/libraries/ESP8266mDNS/src/LEAmDNS_lwIPdefs.h b/libraries/ESP8266mDNS/src/LEAmDNS_lwIPdefs.h index c58aebdc74..a3bcc4b370 100644 --- a/libraries/ESP8266mDNS/src/LEAmDNS_lwIPdefs.h +++ b/libraries/ESP8266mDNS/src/LEAmDNS_lwIPdefs.h @@ -1,26 +1,26 @@ /* - * LEAmDNS_Priv.h - * - * 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. - * - */ + LEAmDNS_Priv.h + + 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. + +*/ #ifndef MDNS_LWIPDEFS_H #define MDNS_LWIPDEFS_H diff --git a/tests/restyle-all.sh b/tests/restyle-all.sh deleted file mode 100755 index e751f476de..0000000000 --- a/tests/restyle-all.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/sh - -set -e - -org=$(cd ${0%/*}; pwd) -cd ${org}/.. -pwd -test -d cores/esp8266 -test -d libraries - -# this warning question will be removed after restyle-all.sh is renamed to restyle.sh -echo "This is dangerous if you have modified your local repository" -echo "type iknowwhatido to continue" -read ans -test "$ans" = iknowwhatido || exit 1 - -for d in cores/esp8266 libraries; do - for e in c cpp h; do - find $d -name "*.$e" -exec \ - astyle \ - --suffix=none \ - --options=${org}/astyle_core.conf {} \; - done -done - -for d in libraries; do - find $d -name "*.ino" -exec \ - astyle \ - --suffix=none \ - --options=${org}/astyle_examples.conf {} \; -done diff --git a/tests/restyle-examples-only.sh b/tests/restyle-examples-only.sh new file mode 100755 index 0000000000..513a891900 --- /dev/null +++ b/tests/restyle-examples-only.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +set -e + +org=$(cd ${0%/*}; pwd) +cd ${org}/.. +pwd +test -d cores/esp8266 +test -d libraries + +# in a near future, restyle-all.sh will be renamed to restyle.sh +# and will be checked against CI + +for d in libraries; do + find $d -name "*.ino" -exec \ + astyle \ + --suffix=none \ + --options=${org}/astyle_examples.conf {} \; +done diff --git a/tests/restyle.sh b/tests/restyle.sh index 513a891900..32a18c1078 100755 --- a/tests/restyle.sh +++ b/tests/restyle.sh @@ -8,12 +8,21 @@ pwd test -d cores/esp8266 test -d libraries -# in a near future, restyle-all.sh will be renamed to restyle.sh -# and will be checked against CI +#all="cores/esp8266 libraries" +all="libraries/ESP8266mDNS" + +for d in $all; do + for e in c cpp h; do + find $d -name "*.$e" -exec \ + astyle \ + --suffix=none \ + --options=${org}/astyle_core.conf {} \; + done +done for d in libraries; do - find $d -name "*.ino" -exec \ - astyle \ - --suffix=none \ - --options=${org}/astyle_examples.conf {} \; + find $d -name "*.ino" -exec \ + astyle \ + --suffix=none \ + --options=${org}/astyle_examples.conf {} \; done