diff --git a/libraries/ESP8266mDNS/ESP8266mDNS.cpp b/libraries/ESP8266mDNS/ESP8266mDNS.cpp index 766b980b87..87bca2f4ee 100644 --- a/libraries/ESP8266mDNS/ESP8266mDNS.cpp +++ b/libraries/ESP8266mDNS/ESP8266mDNS.cpp @@ -98,16 +98,112 @@ struct MDNSService { uint16_t _txtLen; // length of all txts }; -struct MDNSTxt{ - MDNSTxt * _next; - String _txt; -}; +MDNSTxt::MDNSTxt() : + _next(nullptr), + _txt() +{} + +MDNSTxt::~MDNSTxt(){ + if(_next != nullptr){ + delete _next; + } +} + +MDNSAnswer::MDNSAnswer() : + txts(nullptr), + ip(), //NOTE: should value-initialize to 0 + port(0), + hostname(nullptr) +{} + +MDNSAnswer::~MDNSAnswer(){ + if(txts != nullptr){ + delete txts; + } + if(hostname != nullptr){ + delete[] hostname; + } +} + +String MDNSAnswer::getHostname(){ + return hostname; +} + +IPAddress MDNSAnswer::getIP(){ + return IPAddress(ip); +} -struct MDNSAnswer { - MDNSAnswer* next; - uint8_t ip[4]; - uint16_t port; - char *hostname; +uint16_t MDNSAnswer::getPort(){ + return port; +} + +int MDNSAnswer::numTxt(){ + MDNSTxt *txt = txts; + int numTxt = 0; + while(txt != nullptr){ + numTxt++; + txt = txt->_next; + } + return numTxt; +} + +bool MDNSAnswer::hasTxt(const char * key){ + MDNSTxt *txt = txts; + while(txt != nullptr){ + if(txt->_txt.startsWith(String(key)+'=')){ + return true; + } + txt = txt->_next; + } + return false; +} + +String MDNSAnswer::getTxt(const char * key){ + MDNSTxt *txt = txts; + String cmp = String(key)+'='; + while(txt != nullptr){ + if(txt->_txt.startsWith(cmp)){ + return txt->_txt.substring(cmp.length()); + } + txt = txt->_next; + } + return String(); +} + +std::pair MDNSAnswer::getTxt(int idx){ + String tmp = getTxtString(idx); + int loc = tmp.indexOf('='); + if(loc == -1){ + return std::make_pair(tmp,String()); + } + else if(loc+1 == tmp.length()){ //the = is the last character + return std::make_pair(tmp.substring(0,loc),String()); + } + else{ + return std::make_pair(tmp.substring(0,loc),tmp.substring(loc+1)); + } +} + +String MDNSAnswer::getTxtString(int idx){ + MDNSTxt *txt = txts; + while(txt != nullptr && idx-- > 0){ + txt = txt->_next; + } + if(idx > 0 || txt==nullptr){ + return String(); + } + return txt->_txt; +} + +struct MDNSAnswerList { + MDNSAnswerList() : next(nullptr), answer() {} + ~MDNSAnswerList(){ + if(next != nullptr){ + delete next; + } + } + MDNSAnswerList* next; + MDNSAnswer answer; }; struct MDNSQuery { @@ -116,14 +212,18 @@ struct MDNSQuery { }; -MDNSResponder::MDNSResponder() : _conn(0) { - _services = 0; - _instanceName = ""; - _answers = 0; - _query = 0; - _newQuery = false; - _waitingForAnswers = false; -} +MDNSResponder::MDNSResponder() : + _services(nullptr), + _conn(nullptr), + _hostName(), + _instanceName(), + _answers(nullptr), + _query(nullptr), + _newQuery(false), + _waitingForAnswers(false), + _answerCallback() +{} + MDNSResponder::~MDNSResponder() { if (_query != 0) { os_free(_query); @@ -131,15 +231,10 @@ MDNSResponder::~MDNSResponder() { } // 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; + if(_answers != nullptr){ + delete _answers; + _answers = nullptr; } - _answers = 0; if (_conn) { _conn->unref(); @@ -235,7 +330,7 @@ bool MDNSResponder::addServiceTxt(char *name, char *proto, char *key, char *valu 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; + MDNSTxt *newtxt = new MDNSTxt(); newtxt->_txt = String(key) + "=" + String(value); newtxt->_next = 0; if(servicePtr->_txts == 0) { //no services have been added @@ -356,7 +451,11 @@ int MDNSResponder::queryService(char *service, char *proto) { 0x00, 0x01 //Class IN }; _conn->append(reinterpret_cast(ptrAttrs), 4); - _conn->send(); + if(!_conn->send()){ +#ifdef MDNS_DEBUG_ERR + Serial.println("ERROR: Query send failed!"); +#endif + } } #ifdef MDNS_DEBUG_TX @@ -371,42 +470,82 @@ int MDNSResponder::queryService(char *service, char *proto) { String MDNSResponder::hostname(int idx) { MDNSAnswer *answer = _getAnswerFromIdx(idx); - if (answer == 0) { + if (answer == nullptr) { return String(); } - return answer->hostname; + return answer->getHostname(); } IPAddress MDNSResponder::IP(int idx) { MDNSAnswer *answer = _getAnswerFromIdx(idx); - if (answer == 0) { + if (answer == nullptr) { return IPAddress(); } - return IPAddress(answer->ip); + return answer->getIP(); } uint16_t MDNSResponder::port(int idx) { MDNSAnswer *answer = _getAnswerFromIdx(idx); - if (answer == 0) { + if (answer == nullptr) { return 0; } return answer->port; } +int MDNSResponder::numTxt(int idx) { + MDNSAnswer *answer = _getAnswerFromIdx(idx); + if (answer == nullptr) { + return 0; + } + return answer->numTxt(); +} + +bool MDNSResponder::hasTxt(int idx, const char * key) { + MDNSAnswer *answer = _getAnswerFromIdx(idx); + if (answer == nullptr) { + return false; + } + return answer->hasTxt(key); +} + +String MDNSResponder::txt(int idx, const char * key) { + MDNSAnswer *answer = _getAnswerFromIdx(idx); + if (answer == nullptr) { + return String(); + } + return answer->getTxt(key); +} + +String MDNSResponder::txt(int idx, int txtIdx) { + MDNSAnswer *answer = _getAnswerFromIdx(idx); + if (answer == nullptr) { + return String(); + } + return answer->getTxtString(txtIdx); +} + +MDNSAnswer* MDNSResponder::getAnswer(int idx) { + return _getAnswerFromIdx(idx); +} + +void MDNSResponder::setAnswerCallback(std::function cb){ + _answerCallback = cb; +} + MDNSAnswer* MDNSResponder::_getAnswerFromIdx(int idx) { - MDNSAnswer *answer = _answers; - while (answer != 0 && idx-- > 0) { + MDNSAnswerList *answer = _answers; + while (answer != nullptr && idx-- > 0) { answer = answer->next; } - if (idx > 0) { - return 0; + if (idx > 0 || answer == nullptr) { + return nullptr; } - return answer; + return &answer->answer; } int MDNSResponder::_getNumAnswers() { int numAnswers = 0; - MDNSAnswer *answer = _answers; + MDNSAnswerList *answer = _answers; while (answer != 0) { numAnswers++; answer = answer->next; @@ -492,7 +631,7 @@ void MDNSResponder::_parsePacket(){ Serial.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 && !_answerCallback) { #ifdef MDNS_DEBUG_RX Serial.println("Not expecting any answers right now, returning"); #endif @@ -514,8 +653,9 @@ void MDNSResponder::_parsePacket(){ uint16_t answerPort = 0; uint8_t answerIp[4] = { 0,0,0,0 }; char answerHostName[255]; + MDNSTxt * txtPtr = nullptr; bool serviceMatch = false; - MDNSAnswer *answer; + MDNSAnswerList *answer; uint8_t partsCollected = 0; uint8_t stringsRead = 0; @@ -523,14 +663,10 @@ void MDNSResponder::_parsePacket(){ // 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; + if(_answers != nullptr){ + delete _answers; + _answers = nullptr; } - _answers = 0; _newQuery = false; } @@ -551,6 +687,9 @@ void MDNSResponder::_parsePacket(){ Serial.println("failed to read the response name"); #endif _conn->flush(); + if(txtPtr){ + delete txtPtr; + } return; } _conn_readS(serviceName, tmp8); @@ -589,6 +728,9 @@ void MDNSResponder::_parsePacket(){ Serial.printf("Data len too long! %u\n", answerRdlength); #endif _conn->flush(); + if(txtPtr){ + delete txtPtr; + } return; } } @@ -615,14 +757,25 @@ void MDNSResponder::_parsePacket(){ else if (answerType == MDNS_TYPE_TXT) { partsCollected |= 0x02; - _conn_readS(hostName, answerRdlength); // Read rdata + while(answerRdlength){ + MDNSTxt *newtxt = new MDNSTxt(); + uint16_t txtRdLength = _conn_read8(); + answerRdlength--; + txtRdLength = (answerRdlength_txt = String(hostName); + newtxt->_next = txtPtr; + txtPtr = newtxt; #ifdef MDNS_DEBUG_RX - Serial.printf("TXT %d ", answerRdlength); - for (int n = 0; n < answerRdlength; n++) { - Serial.printf("%c", hostName[n]); - } - Serial.println(); + Serial.printf(" %d:", txtRdLength); + for (int n = 0; n < txtRdLength; n++) { + Serial.printf("%02x ", hostName[n]); + } + Serial.printf("\n%s\n", hostName); #endif + } } else if (answerType == MDNS_TYPE_SRV) { @@ -673,38 +826,52 @@ void MDNSResponder::_parsePacket(){ } if ((partsCollected == 0x0F) && serviceMatch) { + if(_waitingForAnswers){ #ifdef MDNS_DEBUG_RX - Serial.println("All answers parsed, adding to _answers list.."); + Serial.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; - } - else { - answer = _answers; - while (answer->next != 0) { + // Add new answer to answer list + if (_answers == nullptr) { + _answers = new MDNSAnswerList(); + answer = _answers; + } + else { + answer = _answers; + while (answer->next != nullptr) { + answer = answer->next; + } + answer->next = new MDNSAnswerList(); answer = answer->next; } - answer->next = (struct MDNSAnswer*)(os_malloc(sizeof(struct MDNSAnswer))); - answer = answer->next; } - answer->next = 0; - answer->hostname = 0; + else{ //must want an answerCallback + answer = new MDNSAnswerList(); + } // Populate new answer - answer->port = answerPort; + answer->answer.txts = txtPtr; + answer->answer.port = answerPort; for (int i = 0; i < 4; i++) { - answer->ip[i] = answerIp[i]; + answer->answer.ip[i] = answerIp[i]; + } + answer->answer.hostname = new char [strlen(answerHostName) + 1]; + os_strcpy(answer->answer.hostname, answerHostName); + if(!_waitingForAnswers){ + if(_answerCallback){ //double-check just to be sure, but this should be OK + _answerCallback(&answer->answer); + } + delete answer; } - answer->hostname = (char *)os_malloc(strlen(answerHostName) + 1); - os_strcpy(answer->hostname, answerHostName); _conn->flush(); + //NOTE: don't delete txtPtr because it's either part of _answers or has been deleted return; } } _conn->flush(); + if(txtPtr){ + delete txtPtr; + } return; } @@ -1191,7 +1358,11 @@ void MDNSResponder::_replyToInstanceRequest(uint8_t questionMask, uint8_t respon ip_addr_t ifaddr; ifaddr.addr = multicastInterface; _conn->setMulticastInterface(ifaddr); - _conn->send(); + if(!_conn->send()){ +#ifdef MDNS_DEBUG_ERR + Serial.println("ERROR: Reply send failed!"); +#endif + } } #if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_MDNS) diff --git a/libraries/ESP8266mDNS/ESP8266mDNS.h b/libraries/ESP8266mDNS/ESP8266mDNS.h index 37874194ec..ef0d8b4c2b 100644 --- a/libraries/ESP8266mDNS/ESP8266mDNS.h +++ b/libraries/ESP8266mDNS/ESP8266mDNS.h @@ -54,8 +54,38 @@ License (MIT license): class UdpContext; struct MDNSService; -struct MDNSTxt; -struct MDNSAnswer; +class MDNSTxt{ +friend class MDNSAnswer; +friend class MDNSResponder; +public: + MDNSTxt(); + ~MDNSTxt(); +private: + MDNSTxt * _next; + String _txt; +}; +class MDNSAnswer { +friend class MDNSResponder; +public: + MDNSAnswer(); + ~MDNSAnswer(); + + String getHostname(); + IPAddress getIP(); + uint16_t getPort(); + int numTxt(); + bool hasTxt(const char * key); + String getTxt(const char * key); + std::pair getTxt(int idx); + String getTxtString(int idx); + +private: + MDNSTxt * txts; + uint8_t ip[4]; + uint16_t port; + char *hostname; +}; +struct MDNSAnswerList; class MDNSResponder { public: @@ -98,6 +128,13 @@ class MDNSResponder { String hostname(int idx); IPAddress IP(int idx); uint16_t port(int idx); + int numTxt(int idx); + bool hasTxt(int idx, const char * key); + String txt(int idx, const char * key); + String txt(int idx, int txtIdx); + MDNSAnswer* getAnswer(int idx); + + void setAnswerCallback(std::function); void enableArduino(uint16_t port, bool auth=false); @@ -114,12 +151,13 @@ class MDNSResponder { UdpContext* _conn; String _hostName; String _instanceName; - struct MDNSAnswer * _answers; + struct MDNSAnswerList * _answers; struct MDNSQuery * _query; bool _newQuery; bool _waitingForAnswers; WiFiEventHandler _disconnectedHandler; WiFiEventHandler _gotIPHandler; + std::function _answerCallback; uint16_t _getServicePort(char *service, char *proto); diff --git a/libraries/ESP8266mDNS/examples/mDNS-SD_Extended/mDNS-SD_Extended.ino b/libraries/ESP8266mDNS/examples/mDNS-SD_Extended/mDNS-SD_Extended.ino index d3feedee56..0ca6bd0b0b 100644 --- a/libraries/ESP8266mDNS/examples/mDNS-SD_Extended/mDNS-SD_Extended.ino +++ b/libraries/ESP8266mDNS/examples/mDNS-SD_Extended/mDNS-SD_Extended.ino @@ -42,6 +42,8 @@ void setup() { } Serial.println("mDNS responder started"); MDNS.addService("esp", "tcp", 8080); // Announce esp tcp service on port 8080 + MDNS.addServiceTxt("esp", "tcp", "foo", "bar"); + MDNS.addServiceTxt("esp", "tcp", "chipid", String(ESP.getChipId(), HEX)); Serial.println("Sending mDNS query"); int n = MDNS.queryService("esp", "tcp"); // Send out query for esp tcp services @@ -62,6 +64,15 @@ void setup() { Serial.print(":"); Serial.print(MDNS.port(i)); Serial.println(")"); + if(MDNS.numTxt(i)>0){ + int num = MDNS.numTxt(i); + Serial.println(" TXT"); + for(int j=0; j < num; j++){ + Serial.print(" "); + Serial.println(MDNS.txt(i,j)); + } + Serial.println(" ENDTXT"); + } } } Serial.println();