From 60e14eaca0e0654f88857f1aa8790123c12690ba Mon Sep 17 00:00:00 2001 From: Slavey Karadzhov Date: Wed, 4 Nov 2020 15:21:05 +0100 Subject: [PATCH 01/52] Initial untested version. --- .../mdns/include/Network/Mdns/Finder.h | 88 ++++++ Sming/Components/mdns/src/Finder.cpp | 291 ++++++++++++++++++ 2 files changed, 379 insertions(+) create mode 100644 Sming/Components/mdns/include/Network/Mdns/Finder.h create mode 100644 Sming/Components/mdns/src/Finder.cpp diff --git a/Sming/Components/mdns/include/Network/Mdns/Finder.h b/Sming/Components/mdns/include/Network/Mdns/Finder.h new file mode 100644 index 0000000000..d0861528fd --- /dev/null +++ b/Sming/Components/mdns/include/Network/Mdns/Finder.h @@ -0,0 +1,88 @@ +/** + * Since big portions of the code are copied from Finder.h and Finder.cpp are copied + * from MrDunk's mDNS code these files are distributed under the same license as his project. + * + * MIT license: https://github.com/mrdunk/esp8266_mdns/blob/master/LICENCE.txt + */ +#pragma once + +#include +#include +#include +#include + +namespace mDNS +{ +enum QuestionType { + MDNS_TYPE_A = 0x0001, + MDNS_TYPE_PTR = 0x000C, + MDNS_TYPE_HINFO = 0x000D, + MDNS_TYPE_TXT = 0x0010, + MDNS_TYPE_AAAA = 0x001C, + MDNS_TYPE_SRV = 0x0021 +}; + +#define MDNS_IP 224, 0, 0, 251 +#define MDNS_TARGET_PORT 5353 +#define MDNS_SOURCE_PORT 5353 +#define MDNS_TTL 255 + +#define MAX_PACKET_SIZE 1024 + +// The mDNS spec says this should never be more than 256 (including trailing '\0'). +#define MAX_MDNS_NAME_LEN 256 + +// A single mDNS Query. +typedef struct Query { + char name[MAX_MDNS_NAME_LEN]; // Question Name: Contains the object, domain or zone name. + enum QuestionType type; // Question Type: Type of question being asked by client. + unsigned int klass; // Question Class: Normally the value 1 for Internet (“IN”) + bool isUnicastResponse; // + bool isValid; // False if problems were encountered decoding packet. +} Query; + +// A single mDNS Answer. +typedef struct Answer { + char name[MAX_MDNS_NAME_LEN]; // object, domain or zone name. + char data[MAX_MDNS_NAME_LEN]; // The data portion of the resource record. + unsigned int type; // ResourceRecord Type. + unsigned int klass; // ResourceRecord Class: Normally the value 1 for Internet (“IN”) + unsigned long int ttl; // ResourceRecord Time To Live: Number of seconds ths should be remembered. + bool isCachedFlush; // Flush cache of records matching this name. + bool isValid; // False if problems were encountered decoding packet. +} Answer; + +class Finder : protected UdpConnection +{ +public: + using AnswerCallback = Delegate; + + Finder(); + + ~Finder(); + + void setAnswerCallback(AnswerCallback callback) + { + onAnswer = callback; + } + + bool search(const String& hostname, const QuestionType& type = MDNS_TYPE_SRV); + + bool search(const Query& query); + +protected: + void onReceive(pbuf* buf, IpAddress remoteIP, uint16_t remotePort) override; + + void processData(UdpConnection& connection, char* data, int size, IpAddress remoteIP, uint16_t remotePort); + +private: + AnswerCallback onAnswer; + + bool writeToBuffer(const uint8_t value, char* p_name_buffer, int* p_name_buffer_pos, const int name_buffer_len); + int parseText(char* buffer, const int bufferLength, const int dataLength, const uint8_t* packet, int packetPos); + + int nameFromDnsPointer(char* name, int namePos, const int nameLEngth, const uint8_t* packet, int packetPos, + const bool recurse = false); +}; + +} // namespace mDNS diff --git a/Sming/Components/mdns/src/Finder.cpp b/Sming/Components/mdns/src/Finder.cpp new file mode 100644 index 0000000000..52720814c2 --- /dev/null +++ b/Sming/Components/mdns/src/Finder.cpp @@ -0,0 +1,291 @@ +#include + +namespace mDNS +{ +Finder::Finder() +{ + UdpConnection::joinMulticastGroup(IpAddress(MDNS_IP)); + UdpConnection::setMulticastTtl(MDNS_TTL); + + onDataCallback = UdpConnectionDataDelegate(&Finder::processData, this); +} + +Finder::~Finder() +{ + UdpConnection::leaveMulticastGroup(IpAddress(MDNS_IP)); +} + +bool Finder::search(const String& hostname, const QuestionType& type) +{ + if(hostname.length() > MAX_MDNS_NAME_LEN - 1) { + return false; + } + + Query query; + memcpy(query.name, hostname.c_str(), hostname.length()); + query.type = type; + query.klass = 1; // "INternet" + query.isUnicastResponse = false; + query.isValid = true; + + return search(query); +} + +bool Finder::search(const Query& query) +{ + uint8_t buffer[MAX_PACKET_SIZE] = {0}; + + size_t pos = 0; + // The first two bytes are the transaction id and they are not used in MDNS + buffer[pos++] = 0; + buffer[pos++] = 0; + + // 2 bytes for Flags + buffer[pos++] = 0; // 0b00000000 for Query, 0b10000000 for Answer. + buffer[pos++] = 0; + + // 2 bytes for number of questions + buffer[pos++] = 0; + buffer[pos++] = 1; // one + + // 2 bytes for number of Answer RRs + buffer[pos++] = 0; + buffer[pos++] = 0; + + // 2 bytes for Authority PRs + buffer[pos++] = 0; + buffer[pos++] = 0; + + // 2 bytes for Additional PRs + buffer[pos++] = 0; + buffer[pos++] = 0; + + size_t word_start = 0, word_end = 0; + + auto name = query.name; + while(true) { + if(name[word_end] == '.' || name[word_end] == '\0') { + const size_t word_length = word_end - word_start; + + buffer[pos++] = (uint8_t)word_length; + for(size_t i = word_start; i < word_end; ++i) { + buffer[pos++] = name[i]; + } + if(name[word_end] == '\0') { + break; + } + word_end++; // Skip the '.' character. + word_start = word_end; + } + word_end++; + } + + buffer[pos++] = '\0'; // End of name. + + // 2 bytes for type + buffer[pos++] = (query.type & 0xFF00) >> 8; + buffer[pos++] = query.type & 0xFF; + + // 2 bytes for class + unsigned int qclass = 0; + if(query.isUnicastResponse) { + qclass = 0b1000000000000000; + } + qclass += query.klass; + buffer[pos++] = (qclass & 0xFF00) >> 8; + buffer[pos++] = qclass & 0xFF; + + return UdpConnection::sendTo(IpAddress(MDNS_IP), MDNS_TARGET_PORT, (const char*)buffer, pos); +} + +void Finder::processData(UdpConnection& connection, char* data, int size, IpAddress remoteIP, uint16_t remotePort) +{ + // process the answer here... + + // check if we have a response or a query + if(!(data[2] & 0b10000000)) { + debug_d("Message is not a response. Ignoring."); + return; + } + + bool truncated = data[2] & 0b00000010; + // TODO: If it's truncated we can expect more data soon so we should wait for additional records before deciding whether to respond. + + if(data[3] & 0b00001111) { + // Non zero Response code implies error. + debug_w("Got errored MDNS answer"); + return; + } + + // Number of incoming queries. + size_t questionsCount = (data[4] << 8) + data[5]; + if(questionsCount > 0) { + // we are interested only in responses. + return; + } + + // Number of incoming answers. + size_t answersCount = (data[6] << 8) + data[7]; + + // Number of incoming Name Server resource records. + size_t nsCount = (data[8] << 8) + data[9]; + + // Number of incoming Additional resource records. + size_t additionalCount = (data[10] << 8) + data[11]; + + size_t pos = 12; // starting from the 12 byte we should have our answers + for(size_t i = 0; i < (answersCount + nsCount + additionalCount); i++) { + Answer answer; + + answer.type = (data[pos++] << 8); + answer.type += data[pos++]; + + uint8_t rrclass_0 = data[pos++]; + uint8_t rrclass_1 = data[pos++]; + answer.isCachedFlush = (0b10000000 & rrclass_0); + answer.klass = ((rrclass_0 & 0b01111111) << 8) + rrclass_1; + + answer.ttl = (data[pos++] << 24); + answer.ttl += (data[pos++] << 16); + answer.ttl += (data[pos++] << 8); + answer.ttl += data[pos++]; + + if(pos > (size_t)size) { + // We've over-run the returned data. + // Something has gone wrong receiving or parsing the data. + answer.isValid = false; + return; + } + + size_t rdlength = (data[pos++] << 8); + rdlength += data[pos++]; + + switch(answer.type) { + case MDNS_TYPE_A: // Returns a 32-bit IPv4 address + if(MAX_MDNS_NAME_LEN >= 16) { + sprintf(answer.data, "%u.%u.%u.%u", data[pos], data[pos + 1], data[pos + 2], data[pos + 3]); + } else { + sprintf(answer.data, "ipv4"); + } + pos += 4; + break; + case MDNS_TYPE_PTR: // Pointer to a canonical name. + pos = nameFromDnsPointer(answer.data, 0, MAX_MDNS_NAME_LEN, (const uint8_t*)data, pos); + break; + case MDNS_TYPE_HINFO: // HINFO. host information + pos = parseText(answer.data, MAX_MDNS_NAME_LEN, rdlength, (const uint8_t*)data, pos); + break; + case MDNS_TYPE_TXT: // Originally for arbitrary human-readable text in a DNS record. + // We only return the first MAX_MDNS_NAME_LEN bytes of thir record type. + pos = parseText(answer.data, MAX_MDNS_NAME_LEN, rdlength, (const uint8_t*)data, pos); + break; + case MDNS_TYPE_AAAA: // Returns a 128-bit IPv6 address. + { + int data_pos = 0; + for(size_t i = 0; i < rdlength; i++) { + if(data_pos < MAX_MDNS_NAME_LEN - 3) { + sprintf(answer.data + data_pos, "%02X:", data[pos++]); + } else { + pos++; + } + data_pos += 3; + } + answer.data[--data_pos] = '\0'; // Remove trailing ':' + } break; + case MDNS_TYPE_SRV: // Server Selection. + { + unsigned int priority = (data[pos++] << 8); + priority += data[pos++]; + unsigned int weight = (data[pos++] << 8); + weight += data[pos++]; + unsigned int port = (data[pos++] << 8); + port += data[pos++]; + sprintf(answer.data, "p=%u;w=%u;port=%u;host=", priority, weight, port); + + pos = nameFromDnsPointer(answer.data, strlen(answer.data), MAX_MDNS_NAME_LEN - strlen(answer.data) - 1, + (const uint8_t*)data, pos); + } break; + default: { + int data_pos = 0; + for(size_t i = 0; i < rdlength; i++) { + if(data_pos < MAX_MDNS_NAME_LEN - 3) { + sprintf(answer.data + data_pos, "%02X ", data[pos++]); + } else { + pos++; + } + data_pos += 3; + } + } break; + } + + answer.isValid = true; + + if(answer.isValid) { + if(onAnswer) { + onAnswer(answer); + } + } + } +} + +bool Finder::writeToBuffer(const uint8_t value, char* name, int* namePos, const int nameLength) +{ + if(*namePos < nameLength - 1) { + *(name + *namePos) = value; + (*namePos)++; + *(name + *namePos) = '\0'; + return true; + } + (*namePos)++; + return false; +} + +int Finder::parseText(char* buffer, const int bufferLength, const int dataLength, const uint8_t* packet, int packetPos) +{ + int i, bufferPos = 0; + for(i = 0; i < dataLength; i++) { + writeToBuffer(packet[packetPos++], buffer, &bufferPos, bufferLength); + } + buffer[bufferPos] = '\0'; + return packetPos; +} + +int Finder::nameFromDnsPointer(char* name, int namePos, const int nameLength, const uint8_t* packet, int packetPos, + const bool recurse) +{ + if(recurse) { + // Since we are adding more to an already populated buffer, + // replace the trailing EOL with the FQDN seperator. + namePos--; + writeToBuffer('.', name, &namePos, nameLength); + } + + if(packet[packetPos] < 0xC0) { + // Since the first 2 bits are not set, + // this is the start of a name section. + // http://www.tcpipguide.com/free/t_DNSNameNotationandMessageCompressionTechnique.htm + + const size_t word_len = packet[packetPos++]; + for(size_t l = 0; l < word_len; l++) { + writeToBuffer(*(packet + packetPos++), name, &namePos, nameLength); + } + + writeToBuffer('\0', name, &namePos, nameLength); + + if(packet[packetPos] > 0) { + // Next word. + packetPos = nameFromDnsPointer(name, namePos, nameLength, packet, packetPos, true); + } else { + // End of string. + packetPos++; + } + } else { + // Message Compression used. Next 2 bytes are a pointer to the actual name section. + int pointer = (packet[packetPos++] - 0xC0) << 8; + pointer += packet[packetPos++]; + nameFromDnsPointer(name, namePos, nameLength, packet, pointer, false); + } + return packetPos; +} + +} // namespace mDNS From 5941ca331cb2d8ecefa94620b6f81badeb592263 Mon Sep 17 00:00:00 2001 From: mikee47 Date: Mon, 1 Mar 2021 21:10:49 +0000 Subject: [PATCH 02/52] wip --- .../mdns/include/Network/Mdns/Finder.h | 86 ++++++---- Sming/Components/mdns/src/Finder.cpp | 161 ++++++++++-------- Sming/Core/Network/UdpConnection.cpp | 24 ++- samples/Basic_Mdns/.cproject | 151 ++++++++++++++++ samples/Basic_Mdns/.project | 28 +++ samples/Basic_Mdns/Makefile | 9 + samples/Basic_Mdns/README.rst | 4 + samples/Basic_Mdns/app/application.cpp | 59 +++++++ samples/Basic_Mdns/component.mk | 1 + 9 files changed, 414 insertions(+), 109 deletions(-) create mode 100644 samples/Basic_Mdns/.cproject create mode 100644 samples/Basic_Mdns/.project create mode 100644 samples/Basic_Mdns/Makefile create mode 100644 samples/Basic_Mdns/README.rst create mode 100644 samples/Basic_Mdns/app/application.cpp create mode 100644 samples/Basic_Mdns/component.mk diff --git a/Sming/Components/mdns/include/Network/Mdns/Finder.h b/Sming/Components/mdns/include/Network/Mdns/Finder.h index d0861528fd..db28b5ebdc 100644 --- a/Sming/Components/mdns/include/Network/Mdns/Finder.h +++ b/Sming/Components/mdns/include/Network/Mdns/Finder.h @@ -32,57 +32,79 @@ enum QuestionType { // The mDNS spec says this should never be more than 256 (including trailing '\0'). #define MAX_MDNS_NAME_LEN 256 -// A single mDNS Query. -typedef struct Query { - char name[MAX_MDNS_NAME_LEN]; // Question Name: Contains the object, domain or zone name. - enum QuestionType type; // Question Type: Type of question being asked by client. - unsigned int klass; // Question Class: Normally the value 1 for Internet (“IN”) +/** + * @brief A single mDNS Query + */ +struct Query { + char name[MAX_MDNS_NAME_LEN]; ///< Question Name: Contains the object, domain or zone name. + enum QuestionType type; ///< Question Type: Type of question being asked by client. + unsigned int klass; ///< Question Class: Normally the value 1 for Internet (“IN”) bool isUnicastResponse; // - bool isValid; // False if problems were encountered decoding packet. -} Query; - -// A single mDNS Answer. -typedef struct Answer { - char name[MAX_MDNS_NAME_LEN]; // object, domain or zone name. - char data[MAX_MDNS_NAME_LEN]; // The data portion of the resource record. - unsigned int type; // ResourceRecord Type. - unsigned int klass; // ResourceRecord Class: Normally the value 1 for Internet (“IN”) - unsigned long int ttl; // ResourceRecord Time To Live: Number of seconds ths should be remembered. - bool isCachedFlush; // Flush cache of records matching this name. - bool isValid; // False if problems were encountered decoding packet. -} Answer; + bool isValid; ///< False if problems were encountered decoding packet. +}; + +/** + * @brief A single mDNS Answer + */ +struct Answer { + char name[MAX_MDNS_NAME_LEN]; ///< object, domain or zone name. + char data[MAX_MDNS_NAME_LEN]; ///< The data portion of the resource record. + unsigned int type; ///< ResourceRecord Type. + unsigned int klass; ///< ResourceRecord Class: Normally the value 1 for Internet (“IN”) + unsigned long int ttl; ///< ResourceRecord Time To Live: Number of seconds ths should be remembered. + bool isCachedFlush; ///< Flush cache of records matching this name. + bool isValid; ///< False if problems were encountered decoding packet. +}; class Finder : protected UdpConnection { public: - using AnswerCallback = Delegate; + using AnswerDelegate = Delegate; - Finder(); + Finder() : out(*this) + { + } ~Finder(); - void setAnswerCallback(AnswerCallback callback) + void onAnswer(AnswerDelegate callback) { - onAnswer = callback; + answerCallback = callback; } - bool search(const String& hostname, const QuestionType& type = MDNS_TYPE_SRV); + bool search(const String& hostname, QuestionType type = MDNS_TYPE_SRV); bool search(const Query& query); protected: void onReceive(pbuf* buf, IpAddress remoteIP, uint16_t remotePort) override; - void processData(UdpConnection& connection, char* data, int size, IpAddress remoteIP, uint16_t remotePort); - private: - AnswerCallback onAnswer; - - bool writeToBuffer(const uint8_t value, char* p_name_buffer, int* p_name_buffer_pos, const int name_buffer_len); - int parseText(char* buffer, const int bufferLength, const int dataLength, const uint8_t* packet, int packetPos); - - int nameFromDnsPointer(char* name, int namePos, const int nameLEngth, const uint8_t* packet, int packetPos, - const bool recurse = false); + /* + * Need a separate UDP connection for sending requests + */ + class UdpOut : public UdpConnection + { + public: + UdpOut(Finder& finder) : finder(finder) + { + } + + protected: + void onReceive(pbuf* buf, IpAddress remoteIP, uint16_t remotePort) override; + Finder& finder; + }; + + void initialise(); + bool writeToBuffer(uint8_t value, char* p_name_buffer, uint16_t& p_name_buffer_pos, uint16_t name_buffer_len); + uint16_t parseText(char* buffer, uint16_t bufferLength, uint16_t dataLength, const uint8_t* packet, uint16_t packetPos); + + uint16_t nameFromDnsPointer(char* name, uint16_t namePos, uint16_t nameLEngth, const uint8_t* packet, uint16_t packetPos, + bool recurse = false); + + AnswerDelegate answerCallback; + UdpOut out; + bool initialised{false}; }; } // namespace mDNS diff --git a/Sming/Components/mdns/src/Finder.cpp b/Sming/Components/mdns/src/Finder.cpp index 52720814c2..1cfb03d7a6 100644 --- a/Sming/Components/mdns/src/Finder.cpp +++ b/Sming/Components/mdns/src/Finder.cpp @@ -2,26 +2,20 @@ namespace mDNS { -Finder::Finder() -{ - UdpConnection::joinMulticastGroup(IpAddress(MDNS_IP)); - UdpConnection::setMulticastTtl(MDNS_TTL); - - onDataCallback = UdpConnectionDataDelegate(&Finder::processData, this); -} - Finder::~Finder() { - UdpConnection::leaveMulticastGroup(IpAddress(MDNS_IP)); + if(initialised) { + UdpConnection::leaveMulticastGroup(IpAddress(MDNS_IP)); + } } -bool Finder::search(const String& hostname, const QuestionType& type) +bool Finder::search(const String& hostname, QuestionType type) { if(hostname.length() > MAX_MDNS_NAME_LEN - 1) { return false; } - Query query; + Query query{}; memcpy(query.name, hostname.c_str(), hostname.length()); query.type = type; query.klass = 1; // "INternet" @@ -33,9 +27,9 @@ bool Finder::search(const String& hostname, const QuestionType& type) bool Finder::search(const Query& query) { - uint8_t buffer[MAX_PACKET_SIZE] = {0}; + uint8_t buffer[MAX_PACKET_SIZE]{}; - size_t pos = 0; + uint16_t pos{0}; // The first two bytes are the transaction id and they are not used in MDNS buffer[pos++] = 0; buffer[pos++] = 0; @@ -60,17 +54,17 @@ bool Finder::search(const Query& query) buffer[pos++] = 0; buffer[pos++] = 0; - size_t word_start = 0, word_end = 0; + uint16_t word_start{0}; + uint16_t word_end{0}; - auto name = query.name; + const char* name = query.name; while(true) { if(name[word_end] == '.' || name[word_end] == '\0') { - const size_t word_length = word_end - word_start; + const uint8_t word_length = word_end - word_start; - buffer[pos++] = (uint8_t)word_length; - for(size_t i = word_start; i < word_end; ++i) { - buffer[pos++] = name[i]; - } + buffer[pos++] = word_length; + memcpy(&buffer[pos], &name[word_start], word_length); + pos += word_length; if(name[word_end] == '\0') { break; } @@ -95,12 +89,35 @@ bool Finder::search(const Query& query) buffer[pos++] = (qclass & 0xFF00) >> 8; buffer[pos++] = qclass & 0xFF; - return UdpConnection::sendTo(IpAddress(MDNS_IP), MDNS_TARGET_PORT, (const char*)buffer, pos); + initialise(); + listen(0); + return sendTo(IpAddress(MDNS_IP), MDNS_TARGET_PORT, reinterpret_cast(buffer), pos); +} + +void Finder::initialise() +{ + if(!initialised) { + joinMulticastGroup(IpAddress(MDNS_IP)); + listen(MDNS_SOURCE_PORT); + setMulticastTtl(MDNS_TTL); + initialised = true; + } } -void Finder::processData(UdpConnection& connection, char* data, int size, IpAddress remoteIP, uint16_t remotePort) +void Finder::UdpOut::onReceive(pbuf* buf, IpAddress remoteIP, uint16_t remotePort) { + finder.onReceive(buf, remoteIP, remotePort); +} + +void Finder::onReceive(pbuf* buf, IpAddress remoteIP, uint16_t remotePort) +{ + // debug_d("Finder::onReceive(%u)", buf->len); + // process the answer here... + auto data = static_cast(buf->payload); + auto size = buf->len; + + // m_printHex("MDNS", data, size, 0); // check if we have a response or a query if(!(data[2] & 0b10000000)) { @@ -118,26 +135,28 @@ void Finder::processData(UdpConnection& connection, char* data, int size, IpAddr } // Number of incoming queries. - size_t questionsCount = (data[4] << 8) + data[5]; + uint16_t questionsCount = (data[4] << 8) + data[5]; if(questionsCount > 0) { // we are interested only in responses. return; } // Number of incoming answers. - size_t answersCount = (data[6] << 8) + data[7]; + uint16_t answersCount = (data[6] << 8) + data[7]; // Number of incoming Name Server resource records. - size_t nsCount = (data[8] << 8) + data[9]; + uint16_t nsCount = (data[8] << 8) + data[9]; // Number of incoming Additional resource records. - size_t additionalCount = (data[10] << 8) + data[11]; + uint16_t additionalCount = (data[10] << 8) + data[11]; - size_t pos = 12; // starting from the 12 byte we should have our answers - for(size_t i = 0; i < (answersCount + nsCount + additionalCount); i++) { + uint16_t pos = 12; // starting from the 12 byte we should have our answers + for(uint16_t i = 0; i < (answersCount + nsCount + additionalCount); i++) { Answer answer; - answer.type = (data[pos++] << 8); + pos = nameFromDnsPointer(answer.name, 0, MAX_MDNS_NAME_LEN, data, pos); + + answer.type = data[pos++] << 8; answer.type += data[pos++]; uint8_t rrclass_0 = data[pos++]; @@ -150,14 +169,14 @@ void Finder::processData(UdpConnection& connection, char* data, int size, IpAddr answer.ttl += (data[pos++] << 8); answer.ttl += data[pos++]; - if(pos > (size_t)size) { + if(pos > size_t(size)) { // We've over-run the returned data. // Something has gone wrong receiving or parsing the data. answer.isValid = false; return; } - size_t rdlength = (data[pos++] << 8); + uint16_t rdlength = data[pos++] << 8; rdlength += data[pos++]; switch(answer.type) { @@ -169,20 +188,23 @@ void Finder::processData(UdpConnection& connection, char* data, int size, IpAddr } pos += 4; break; + case MDNS_TYPE_PTR: // Pointer to a canonical name. - pos = nameFromDnsPointer(answer.data, 0, MAX_MDNS_NAME_LEN, (const uint8_t*)data, pos); + pos = nameFromDnsPointer(answer.data, 0, MAX_MDNS_NAME_LEN, data, pos); break; + case MDNS_TYPE_HINFO: // HINFO. host information - pos = parseText(answer.data, MAX_MDNS_NAME_LEN, rdlength, (const uint8_t*)data, pos); + pos = parseText(answer.data, MAX_MDNS_NAME_LEN, rdlength, data, pos); break; + case MDNS_TYPE_TXT: // Originally for arbitrary human-readable text in a DNS record. // We only return the first MAX_MDNS_NAME_LEN bytes of thir record type. - pos = parseText(answer.data, MAX_MDNS_NAME_LEN, rdlength, (const uint8_t*)data, pos); + pos = parseText(answer.data, MAX_MDNS_NAME_LEN, rdlength, data, pos); break; - case MDNS_TYPE_AAAA: // Returns a 128-bit IPv6 address. - { - int data_pos = 0; - for(size_t i = 0; i < rdlength; i++) { + + case MDNS_TYPE_AAAA: { // Returns a 128-bit IPv6 address. + uint16_t data_pos{0}; + for(uint16_t i = 0; i < rdlength; i++) { if(data_pos < MAX_MDNS_NAME_LEN - 3) { sprintf(answer.data + data_pos, "%02X:", data[pos++]); } else { @@ -191,9 +213,10 @@ void Finder::processData(UdpConnection& connection, char* data, int size, IpAddr data_pos += 3; } answer.data[--data_pos] = '\0'; // Remove trailing ':' - } break; - case MDNS_TYPE_SRV: // Server Selection. - { + break; + } + + case MDNS_TYPE_SRV: { // Server Selection. unsigned int priority = (data[pos++] << 8); priority += data[pos++]; unsigned int weight = (data[pos++] << 8); @@ -203,11 +226,13 @@ void Finder::processData(UdpConnection& connection, char* data, int size, IpAddr sprintf(answer.data, "p=%u;w=%u;port=%u;host=", priority, weight, port); pos = nameFromDnsPointer(answer.data, strlen(answer.data), MAX_MDNS_NAME_LEN - strlen(answer.data) - 1, - (const uint8_t*)data, pos); - } break; - default: { - int data_pos = 0; - for(size_t i = 0; i < rdlength; i++) { + data, pos); + break; + } + + default: + uint16_t data_pos{0}; + for(uint16_t i = 0; i < rdlength; i++) { if(data_pos < MAX_MDNS_NAME_LEN - 3) { sprintf(answer.data + data_pos, "%02X ", data[pos++]); } else { @@ -215,49 +240,47 @@ void Finder::processData(UdpConnection& connection, char* data, int size, IpAddr } data_pos += 3; } - } break; } answer.isValid = true; - if(answer.isValid) { - if(onAnswer) { - onAnswer(answer); - } + if(answerCallback) { + answerCallback(answer); } } } -bool Finder::writeToBuffer(const uint8_t value, char* name, int* namePos, const int nameLength) +bool Finder::writeToBuffer(uint8_t value, char* name, uint16_t& namePos, uint16_t nameLength) { - if(*namePos < nameLength - 1) { - *(name + *namePos) = value; - (*namePos)++; - *(name + *namePos) = '\0'; + if(namePos + 1 < nameLength) { + name[namePos++] = value; + name[namePos] = '\0'; return true; } - (*namePos)++; + + namePos++; return false; } -int Finder::parseText(char* buffer, const int bufferLength, const int dataLength, const uint8_t* packet, int packetPos) +uint16_t Finder::parseText(char* buffer, uint16_t bufferLength, uint16_t dataLength, const uint8_t* packet, + uint16_t packetPos) { - int i, bufferPos = 0; - for(i = 0; i < dataLength; i++) { - writeToBuffer(packet[packetPos++], buffer, &bufferPos, bufferLength); + uint16_t bufferPos{0}; + for(uint16_t i = 0; i < dataLength; i++) { + writeToBuffer(packet[packetPos++], buffer, bufferPos, bufferLength); } buffer[bufferPos] = '\0'; return packetPos; } -int Finder::nameFromDnsPointer(char* name, int namePos, const int nameLength, const uint8_t* packet, int packetPos, - const bool recurse) +uint16_t Finder::nameFromDnsPointer(char* name, uint16_t namePos, uint16_t nameLength, const uint8_t* packet, + uint16_t packetPos, bool recurse) { if(recurse) { // Since we are adding more to an already populated buffer, // replace the trailing EOL with the FQDN seperator. namePos--; - writeToBuffer('.', name, &namePos, nameLength); + writeToBuffer('.', name, namePos, nameLength); } if(packet[packetPos] < 0xC0) { @@ -265,12 +288,12 @@ int Finder::nameFromDnsPointer(char* name, int namePos, const int nameLength, co // this is the start of a name section. // http://www.tcpipguide.com/free/t_DNSNameNotationandMessageCompressionTechnique.htm - const size_t word_len = packet[packetPos++]; - for(size_t l = 0; l < word_len; l++) { - writeToBuffer(*(packet + packetPos++), name, &namePos, nameLength); + const uint8_t word_len = packet[packetPos++]; + for(uint8_t l = 0; l < word_len; l++) { + writeToBuffer(packet[packetPos++], name, namePos, nameLength); } - writeToBuffer('\0', name, &namePos, nameLength); + writeToBuffer('\0', name, namePos, nameLength); if(packet[packetPos] > 0) { // Next word. @@ -281,7 +304,7 @@ int Finder::nameFromDnsPointer(char* name, int namePos, const int nameLength, co } } else { // Message Compression used. Next 2 bytes are a pointer to the actual name section. - int pointer = (packet[packetPos++] - 0xC0) << 8; + uint16_t pointer = (packet[packetPos++] - 0xC0) << 8; pointer += packet[packetPos++]; nameFromDnsPointer(name, namePos, nameLength, packet, pointer, false); } diff --git a/Sming/Core/Network/UdpConnection.cpp b/Sming/Core/Network/UdpConnection.cpp index d0daf7d29b..73a4005335 100644 --- a/Sming/Core/Network/UdpConnection.cpp +++ b/Sming/Core/Network/UdpConnection.cpp @@ -119,19 +119,27 @@ void UdpConnection::staticOnReceive(void* arg, struct udp_pcb* pcb, struct pbuf* bool UdpConnection::setMulticast(IpAddress ip) { #if LWIP_MULTICAST_TX_OPTIONS - udp_set_multicast_netif_addr(udp, (ip4_addr_t*)ip); - return true; -#else - return false; + if(udp == nullptr) { + initialize(nullptr); + } + if(udp != nullptr) { + udp_set_multicast_netif_addr(udp, (ip4_addr_t*)ip); + return true; + } #endif + return false; } bool UdpConnection::setMulticastTtl(size_t ttl) { #if LWIP_MULTICAST_TX_OPTIONS - udp_set_multicast_ttl(udp, ttl); - return true; -#else - return false; + if(udp == nullptr) { + initialize(nullptr); + } + if(udp != nullptr) { + udp_set_multicast_ttl(udp, ttl); + return true; + } #endif + return false; } diff --git a/samples/Basic_Mdns/.cproject b/samples/Basic_Mdns/.cproject new file mode 100644 index 0000000000..e1450b6031 --- /dev/null +++ b/samples/Basic_Mdns/.cproject @@ -0,0 +1,151 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + make + -f ${ProjDirPath}/Makefile + all + true + true + true + + + make + -f ${ProjDirPath}/Makefile + clean + true + true + true + + + make + -f ${ProjDirPath}/Makefile + flash + true + true + true + + + make + -f ${ProjDirPath}/Makefile + flashonefile + true + true + true + + + make + -f ${ProjDirPath}/Makefile + flashinit + true + true + true + + + make + -f ${ProjDirPath}/Makefile + flashboot + true + true + true + + + make + -f ${ProjDirPath}/Makefile + rebuild + true + true + true + + + + + + + + + + + + + + + + + + + + diff --git a/samples/Basic_Mdns/.project b/samples/Basic_Mdns/.project new file mode 100644 index 0000000000..301ff6d716 --- /dev/null +++ b/samples/Basic_Mdns/.project @@ -0,0 +1,28 @@ + + + Basic_Mdns + + + SmingFramework + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.core.ccnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/samples/Basic_Mdns/Makefile b/samples/Basic_Mdns/Makefile new file mode 100644 index 0000000000..ff51b6c3a7 --- /dev/null +++ b/samples/Basic_Mdns/Makefile @@ -0,0 +1,9 @@ +##################################################################### +#### Please don't change this file. Use component.mk instead #### +##################################################################### + +ifndef SMING_HOME +$(error SMING_HOME is not set: please configure it as an environment variable) +endif + +include $(SMING_HOME)/project.mk diff --git a/samples/Basic_Mdns/README.rst b/samples/Basic_Mdns/README.rst new file mode 100644 index 0000000000..d03d2b53c5 --- /dev/null +++ b/samples/Basic_Mdns/README.rst @@ -0,0 +1,4 @@ +Basic MDNS +========== + +Demonstrates use of the :component:`MDNS` library to discover local devices. diff --git a/samples/Basic_Mdns/app/application.cpp b/samples/Basic_Mdns/app/application.cpp new file mode 100644 index 0000000000..0c4f96ad9b --- /dev/null +++ b/samples/Basic_Mdns/app/application.cpp @@ -0,0 +1,59 @@ +#include +#include + +// If you want, you can define WiFi settings globally in Eclipse Environment Variables +#ifndef WIFI_SSID +#define WIFI_SSID "PleaseEnterSSID" // Put you SSID and Password here +#define WIFI_PWD "PleaseEnterPass" +#endif + +mDNS::Finder finder; + +void gotIP(IpAddress ip, IpAddress netmask, IpAddress gateway) +{ + Serial.print(F("Connected. Got IP: ")); + Serial.println(ip); + + finder.onAnswer([](mDNS::Answer& answer) { + // m_printHex("ANSWER", &answer, sizeof(answer)); + debug_i(">> name: %s", answer.name); + debug_i(" data: %s", answer.data); + debug_i(" type: 0x%04x", answer.type); + debug_i(" class: 0x%04x", answer.klass); + debug_i(" ttl: %u", answer.ttl); + debug_i(" flsh?: %u", answer.isCachedFlush); + debug_i(" vald?: %u", answer.isValid); + }); + // bool ok = finder.search("_googlecast._tcp.local"); + String hostname = F("_googlecast._tcp.local"); + mDNS::Query query{}; + memcpy(query.name, hostname.c_str(), hostname.length()); + query.type = mDNS::MDNS_TYPE_PTR; + query.klass = 1; // "INternet" + query.isUnicastResponse = false; + query.isValid = true; + bool ok = finder.search(query); + + debug_i("search(): %s", ok ? "OK" : "FAIL"); + // finder.search("googlecast._tcp.local"); + // finder.search("_googlecast"); + // finder.search("googlecast"); +} + +void connectFail(const String& ssid, MacAddress bssid, WifiDisconnectReason reason) +{ + Serial.println(F("I'm NOT CONNECTED!")); +} + +void init() +{ + Serial.begin(SERIAL_BAUD_RATE); + Serial.systemDebugOutput(true); // Allow debug print to serial + + // Setup the WIFI connection + WifiStation.enable(true); + WifiStation.config(WIFI_SSID, WIFI_PWD); // Put you SSID and Password here + + WifiEvents.onStationGotIP(gotIP); + WifiEvents.onStationDisconnect(connectFail); +} diff --git a/samples/Basic_Mdns/component.mk b/samples/Basic_Mdns/component.mk new file mode 100644 index 0000000000..1560c918d0 --- /dev/null +++ b/samples/Basic_Mdns/component.mk @@ -0,0 +1 @@ +COMPONENT_DEPENDS := mdns From bf420fd1478964b85f51a2affb5b34647563e455 Mon Sep 17 00:00:00 2001 From: mikee47 Date: Tue, 2 Mar 2021 07:58:50 +0000 Subject: [PATCH 03/52] QuestionType -> ResourceType (strong enum), refactor packet read/write --- .../mdns/include/Network/Mdns/Finder.h | 54 +-- Sming/Components/mdns/src/Finder.cpp | 333 ++++++++++-------- samples/Basic_Mdns/app/application.cpp | 9 +- 3 files changed, 229 insertions(+), 167 deletions(-) diff --git a/Sming/Components/mdns/include/Network/Mdns/Finder.h b/Sming/Components/mdns/include/Network/Mdns/Finder.h index db28b5ebdc..4e4157f2b0 100644 --- a/Sming/Components/mdns/include/Network/Mdns/Finder.h +++ b/Sming/Components/mdns/include/Network/Mdns/Finder.h @@ -13,15 +13,6 @@ namespace mDNS { -enum QuestionType { - MDNS_TYPE_A = 0x0001, - MDNS_TYPE_PTR = 0x000C, - MDNS_TYPE_HINFO = 0x000D, - MDNS_TYPE_TXT = 0x0010, - MDNS_TYPE_AAAA = 0x001C, - MDNS_TYPE_SRV = 0x0021 -}; - #define MDNS_IP 224, 0, 0, 251 #define MDNS_TARGET_PORT 5353 #define MDNS_SOURCE_PORT 5353 @@ -32,13 +23,32 @@ enum QuestionType { // The mDNS spec says this should never be more than 256 (including trailing '\0'). #define MAX_MDNS_NAME_LEN 256 +/** + * @brief MDNS resource type identifiers + * + * (name, value, description) + */ +#define MDNS_RESOURCE_TYPE_MAP(XX) \ + XX(A, 0x0001, "32-bit IPv4 address") \ + XX(PTR, 0x000C, "Pointer to a canonical name") \ + XX(HINFO, 0x000D, "Host Information") \ + XX(TXT, 0x0010, "Arbitrary human-readable text") \ + XX(AAAA, 0x001C, "128-bit IPv6 address") \ + XX(SRV, 0x0021, "Server selection") + +enum class ResourceType : uint16_t { +#define XX(name, value, desc) name = value, + MDNS_RESOURCE_TYPE_MAP(XX) +#undef XX +}; + /** * @brief A single mDNS Query */ struct Query { char name[MAX_MDNS_NAME_LEN]; ///< Question Name: Contains the object, domain or zone name. - enum QuestionType type; ///< Question Type: Type of question being asked by client. - unsigned int klass; ///< Question Class: Normally the value 1 for Internet (“IN”) + ResourceType type; ///< Question Type: Type of question being asked by client. + uint16_t klass; ///< Question Class: Normally the value 1 for Internet (“IN”) bool isUnicastResponse; // bool isValid; ///< False if problems were encountered decoding packet. }; @@ -49,11 +59,14 @@ struct Query { struct Answer { char name[MAX_MDNS_NAME_LEN]; ///< object, domain or zone name. char data[MAX_MDNS_NAME_LEN]; ///< The data portion of the resource record. - unsigned int type; ///< ResourceRecord Type. - unsigned int klass; ///< ResourceRecord Class: Normally the value 1 for Internet (“IN”) - unsigned long int ttl; ///< ResourceRecord Time To Live: Number of seconds ths should be remembered. - bool isCachedFlush; ///< Flush cache of records matching this name. - bool isValid; ///< False if problems were encountered decoding packet. + uint16_t dataLen; + void* rawData; + uint16_t rawDataLen; + ResourceType type; ///< ResourceRecord Type. + uint16_t klass; ///< ResourceRecord Class: Normally the value 1 for Internet (“IN”) + uint32_t ttl; ///< ResourceRecord Time To Live: Number of seconds ths should be remembered. + bool isCachedFlush; ///< Flush cache of records matching this name. + bool isValid; ///< False if problems were encountered decoding packet. }; class Finder : protected UdpConnection @@ -72,7 +85,7 @@ class Finder : protected UdpConnection answerCallback = callback; } - bool search(const String& hostname, QuestionType type = MDNS_TYPE_SRV); + bool search(const String& hostname, ResourceType type = ResourceType::SRV); bool search(const Query& query); @@ -96,11 +109,6 @@ class Finder : protected UdpConnection }; void initialise(); - bool writeToBuffer(uint8_t value, char* p_name_buffer, uint16_t& p_name_buffer_pos, uint16_t name_buffer_len); - uint16_t parseText(char* buffer, uint16_t bufferLength, uint16_t dataLength, const uint8_t* packet, uint16_t packetPos); - - uint16_t nameFromDnsPointer(char* name, uint16_t namePos, uint16_t nameLEngth, const uint8_t* packet, uint16_t packetPos, - bool recurse = false); AnswerDelegate answerCallback; UdpOut out; @@ -108,3 +116,5 @@ class Finder : protected UdpConnection }; } // namespace mDNS + +String toString(mDNS::ResourceType type); diff --git a/Sming/Components/mdns/src/Finder.cpp b/Sming/Components/mdns/src/Finder.cpp index 1cfb03d7a6..fb7741ec5b 100644 --- a/Sming/Components/mdns/src/Finder.cpp +++ b/Sming/Components/mdns/src/Finder.cpp @@ -1,5 +1,126 @@ #include +String toString(mDNS::ResourceType type) +{ + switch(type) { +#define XX(name, value, desc) \ + case mDNS::ResourceType::name: \ + return F(#name); + MDNS_RESOURCE_TYPE_MAP(XX) +#undef XX + default: + return String(unsigned(type)); + } +} + +namespace +{ +struct Packet { + uint8_t* data; + mutable uint16_t pos; + + void* ptr() const + { + return data + pos; + } + + uint8_t peek8() const + { + return data[pos]; + } + + uint8_t read8() const + { + return data[pos++]; + } + + uint16_t read16() const + { + return (read8() << 8) | read8(); + } + + uint32_t read32() const + { + return (read16() << 16) | read16(); + } + + void write(const void* s, uint16_t len) + { + memcpy(&data[pos], s, len); + pos += len; + } + + void write8(uint8_t value) + { + data[pos++] = value; + } + + void write16(uint16_t value) + { + write8(value >> 8); + write8(value & 0xff); + } +}; + +bool writeToBuffer(uint8_t value, char* name, uint16_t& namePos, uint16_t nameLength) +{ + if(namePos + 1 < nameLength) { + name[namePos++] = value; + name[namePos] = '\0'; + return true; + } + + namePos++; + return false; +} + +void parseText(Packet& pkt, char* buffer, uint16_t bufferLength, uint16_t dataLength) +{ + uint16_t bufferPos{0}; + for(uint16_t i = 0; i < dataLength; i++) { + writeToBuffer(pkt.read8(), buffer, bufferPos, bufferLength); + } + buffer[bufferPos] = '\0'; +} + +void nameFromDnsPointer(const Packet& pkt, char* name, uint16_t namePos, uint16_t nameLength, bool recurse) +{ + if(recurse) { + // Since we are adding more to an already populated buffer, + // replace the trailing EOL with the FQDN seperator. + namePos--; + writeToBuffer('.', name, namePos, nameLength); + } + + if(pkt.peek8() < 0xC0) { + // Since the first 2 bits are not set, + // this is the start of a name section. + // http://www.tcpipguide.com/free/t_DNSNameNotationandMessageCompressionTechnique.htm + + const uint8_t word_len = pkt.read8(); + for(uint8_t l = 0; l < word_len; l++) { + writeToBuffer(pkt.read8(), name, namePos, nameLength); + } + + writeToBuffer('\0', name, namePos, nameLength); + + if(pkt.peek8() > 0) { + // Next word + nameFromDnsPointer(pkt, name, namePos, nameLength, true); + } else { + // End of string. + pkt.read8(); + } + } else { + // Message Compression used. Next 2 bytes are a pointer to the actual name section. + uint16_t pointer = pkt.read16() & 0x3000; + Packet tmp{pkt.data, pointer}; + nameFromDnsPointer(tmp, name, namePos, nameLength, false); + } +} + +} // namespace + namespace mDNS { Finder::~Finder() @@ -9,7 +130,7 @@ Finder::~Finder() } } -bool Finder::search(const String& hostname, QuestionType type) +bool Finder::search(const String& hostname, ResourceType type) { if(hostname.length() > MAX_MDNS_NAME_LEN - 1) { return false; @@ -29,30 +150,26 @@ bool Finder::search(const Query& query) { uint8_t buffer[MAX_PACKET_SIZE]{}; - uint16_t pos{0}; + Packet pkt{buffer, 0}; + // The first two bytes are the transaction id and they are not used in MDNS - buffer[pos++] = 0; - buffer[pos++] = 0; + pkt.write16(0); // 2 bytes for Flags - buffer[pos++] = 0; // 0b00000000 for Query, 0b10000000 for Answer. - buffer[pos++] = 0; + pkt.write8(0); // 0b00000000 for Query, 0b10000000 for Answer. + pkt.write8(0); // 2 bytes for number of questions - buffer[pos++] = 0; - buffer[pos++] = 1; // one + pkt.write16(1); // 2 bytes for number of Answer RRs - buffer[pos++] = 0; - buffer[pos++] = 0; + pkt.write16(0); // 2 bytes for Authority PRs - buffer[pos++] = 0; - buffer[pos++] = 0; + pkt.write16(0); // 2 bytes for Additional PRs - buffer[pos++] = 0; - buffer[pos++] = 0; + pkt.write16(0); uint16_t word_start{0}; uint16_t word_end{0}; @@ -62,9 +179,8 @@ bool Finder::search(const Query& query) if(name[word_end] == '.' || name[word_end] == '\0') { const uint8_t word_length = word_end - word_start; - buffer[pos++] = word_length; - memcpy(&buffer[pos], &name[word_start], word_length); - pos += word_length; + pkt.write8(word_length); + pkt.write(&name[word_start], word_length); if(name[word_end] == '\0') { break; } @@ -74,11 +190,10 @@ bool Finder::search(const Query& query) word_end++; } - buffer[pos++] = '\0'; // End of name. + pkt.write8('\0'); // End of name. // 2 bytes for type - buffer[pos++] = (query.type & 0xFF00) >> 8; - buffer[pos++] = query.type & 0xFF; + pkt.write16(uint16_t(query.type)); // 2 bytes for class unsigned int qclass = 0; @@ -86,12 +201,11 @@ bool Finder::search(const Query& query) qclass = 0b1000000000000000; } qclass += query.klass; - buffer[pos++] = (qclass & 0xFF00) >> 8; - buffer[pos++] = qclass & 0xFF; + pkt.write16(qclass); initialise(); listen(0); - return sendTo(IpAddress(MDNS_IP), MDNS_TARGET_PORT, reinterpret_cast(buffer), pos); + return sendTo(IpAddress(MDNS_IP), MDNS_TARGET_PORT, reinterpret_cast(pkt.data), pkt.pos); } void Finder::initialise() @@ -111,7 +225,9 @@ void Finder::UdpOut::onReceive(pbuf* buf, IpAddress remoteIP, uint16_t remotePor void Finder::onReceive(pbuf* buf, IpAddress remoteIP, uint16_t remotePort) { - // debug_d("Finder::onReceive(%u)", buf->len); + if(!answerCallback) { + return; + } // process the answer here... auto data = static_cast(buf->payload); @@ -134,181 +250,114 @@ void Finder::onReceive(pbuf* buf, IpAddress remoteIP, uint16_t remotePort) return; } + Packet pkt{data, 4}; + // Number of incoming queries. - uint16_t questionsCount = (data[4] << 8) + data[5]; + uint16_t questionsCount = pkt.read16(); if(questionsCount > 0) { // we are interested only in responses. return; } // Number of incoming answers. - uint16_t answersCount = (data[6] << 8) + data[7]; + uint16_t answersCount = pkt.read16(); // Number of incoming Name Server resource records. - uint16_t nsCount = (data[8] << 8) + data[9]; + uint16_t nsCount = pkt.read16(); // Number of incoming Additional resource records. - uint16_t additionalCount = (data[10] << 8) + data[11]; + uint16_t additionalCount = pkt.read16(); - uint16_t pos = 12; // starting from the 12 byte we should have our answers + // List of answers for(uint16_t i = 0; i < (answersCount + nsCount + additionalCount); i++) { - Answer answer; + Answer answer{}; - pos = nameFromDnsPointer(answer.name, 0, MAX_MDNS_NAME_LEN, data, pos); + nameFromDnsPointer(pkt, answer.name, 0, MAX_MDNS_NAME_LEN, false); - answer.type = data[pos++] << 8; - answer.type += data[pos++]; + answer.type = ResourceType(pkt.read16()); - uint8_t rrclass_0 = data[pos++]; - uint8_t rrclass_1 = data[pos++]; + uint8_t rrclass_0 = pkt.read8(); + uint8_t rrclass_1 = pkt.read8(); answer.isCachedFlush = (0b10000000 & rrclass_0); answer.klass = ((rrclass_0 & 0b01111111) << 8) + rrclass_1; - answer.ttl = (data[pos++] << 24); - answer.ttl += (data[pos++] << 16); - answer.ttl += (data[pos++] << 8); - answer.ttl += data[pos++]; + answer.ttl = pkt.read32(); - if(pos > size_t(size)) { - // We've over-run the returned data. + if(pkt.pos > size_t(size)) { + debug_e("[MDNS] Packet overrun, pos = %u, size = %u", pkt.pos, size); // Something has gone wrong receiving or parsing the data. answer.isValid = false; - return; + if(answerCallback) { + answerCallback(answer); + } + break; } - uint16_t rdlength = data[pos++] << 8; - rdlength += data[pos++]; + uint16_t rdlength = pkt.read16(); + + answer.rawData = pkt.ptr(); + answer.rawDataLen = rdlength; switch(answer.type) { - case MDNS_TYPE_A: // Returns a 32-bit IPv4 address - if(MAX_MDNS_NAME_LEN >= 16) { - sprintf(answer.data, "%u.%u.%u.%u", data[pos], data[pos + 1], data[pos + 2], data[pos + 3]); - } else { - sprintf(answer.data, "ipv4"); - } - pos += 4; + case ResourceType::A: // Returns a 32-bit IPv4 address + answer.dataLen = sprintf(answer.data, "%u.%u.%u.%u", pkt.read8(), pkt.read8(), pkt.read8(), pkt.read8()); break; - case MDNS_TYPE_PTR: // Pointer to a canonical name. - pos = nameFromDnsPointer(answer.data, 0, MAX_MDNS_NAME_LEN, data, pos); + case ResourceType::PTR: // Pointer to a canonical name. + nameFromDnsPointer(pkt, answer.data, 0, MAX_MDNS_NAME_LEN, false); + answer.dataLen = strlen(answer.data); break; - case MDNS_TYPE_HINFO: // HINFO. host information - pos = parseText(answer.data, MAX_MDNS_NAME_LEN, rdlength, data, pos); + case ResourceType::HINFO: // HINFO. host information + parseText(pkt, answer.data, MAX_MDNS_NAME_LEN, rdlength); + answer.dataLen = rdlength; break; - case MDNS_TYPE_TXT: // Originally for arbitrary human-readable text in a DNS record. - // We only return the first MAX_MDNS_NAME_LEN bytes of thir record type. - pos = parseText(answer.data, MAX_MDNS_NAME_LEN, rdlength, data, pos); + case ResourceType::TXT: // Originally for arbitrary human-readable text in a DNS record. + // We only return the first MAX_MDNS_NAME_LEN bytes of this record type. + parseText(pkt, answer.data, MAX_MDNS_NAME_LEN, rdlength); + answer.dataLen = rdlength; break; - case MDNS_TYPE_AAAA: { // Returns a 128-bit IPv6 address. + case ResourceType::AAAA: { // Returns a 128-bit IPv6 address. uint16_t data_pos{0}; for(uint16_t i = 0; i < rdlength; i++) { + auto c = pkt.read8(); if(data_pos < MAX_MDNS_NAME_LEN - 3) { - sprintf(answer.data + data_pos, "%02X:", data[pos++]); - } else { - pos++; + sprintf(answer.data + data_pos, "%02X:", c); } data_pos += 3; } - answer.data[--data_pos] = '\0'; // Remove trailing ':' + answer.data[data_pos - 1] = '\0'; // Remove trailing ':' + answer.dataLen = data_pos; break; } - case MDNS_TYPE_SRV: { // Server Selection. - unsigned int priority = (data[pos++] << 8); - priority += data[pos++]; - unsigned int weight = (data[pos++] << 8); - weight += data[pos++]; - unsigned int port = (data[pos++] << 8); - port += data[pos++]; - sprintf(answer.data, "p=%u;w=%u;port=%u;host=", priority, weight, port); - - pos = nameFromDnsPointer(answer.data, strlen(answer.data), MAX_MDNS_NAME_LEN - strlen(answer.data) - 1, - data, pos); + case ResourceType::SRV: { // Server Selection. + auto priority = pkt.read16(); + auto weight = pkt.read16(); + auto port = pkt.read16(); + auto len = sprintf(answer.data, "p=%u;w=%u;port=%u;host=", priority, weight, port); + nameFromDnsPointer(pkt, answer.data, len, MAX_MDNS_NAME_LEN - len - 1, false); + answer.dataLen = strlen(answer.data); break; } default: uint16_t data_pos{0}; for(uint16_t i = 0; i < rdlength; i++) { + auto c = pkt.read8(); if(data_pos < MAX_MDNS_NAME_LEN - 3) { - sprintf(answer.data + data_pos, "%02X ", data[pos++]); - } else { - pos++; + sprintf(answer.data + data_pos, "%02X ", c); } data_pos += 3; } + answer.dataLen = strlen(answer.data); } answer.isValid = true; - - if(answerCallback) { - answerCallback(answer); - } - } -} - -bool Finder::writeToBuffer(uint8_t value, char* name, uint16_t& namePos, uint16_t nameLength) -{ - if(namePos + 1 < nameLength) { - name[namePos++] = value; - name[namePos] = '\0'; - return true; - } - - namePos++; - return false; -} - -uint16_t Finder::parseText(char* buffer, uint16_t bufferLength, uint16_t dataLength, const uint8_t* packet, - uint16_t packetPos) -{ - uint16_t bufferPos{0}; - for(uint16_t i = 0; i < dataLength; i++) { - writeToBuffer(packet[packetPos++], buffer, bufferPos, bufferLength); - } - buffer[bufferPos] = '\0'; - return packetPos; -} - -uint16_t Finder::nameFromDnsPointer(char* name, uint16_t namePos, uint16_t nameLength, const uint8_t* packet, - uint16_t packetPos, bool recurse) -{ - if(recurse) { - // Since we are adding more to an already populated buffer, - // replace the trailing EOL with the FQDN seperator. - namePos--; - writeToBuffer('.', name, namePos, nameLength); - } - - if(packet[packetPos] < 0xC0) { - // Since the first 2 bits are not set, - // this is the start of a name section. - // http://www.tcpipguide.com/free/t_DNSNameNotationandMessageCompressionTechnique.htm - - const uint8_t word_len = packet[packetPos++]; - for(uint8_t l = 0; l < word_len; l++) { - writeToBuffer(packet[packetPos++], name, namePos, nameLength); - } - - writeToBuffer('\0', name, namePos, nameLength); - - if(packet[packetPos] > 0) { - // Next word. - packetPos = nameFromDnsPointer(name, namePos, nameLength, packet, packetPos, true); - } else { - // End of string. - packetPos++; - } - } else { - // Message Compression used. Next 2 bytes are a pointer to the actual name section. - uint16_t pointer = (packet[packetPos++] - 0xC0) << 8; - pointer += packet[packetPos++]; - nameFromDnsPointer(name, namePos, nameLength, packet, pointer, false); + answerCallback(answer); } - return packetPos; } } // namespace mDNS diff --git a/samples/Basic_Mdns/app/application.cpp b/samples/Basic_Mdns/app/application.cpp index 0c4f96ad9b..6977e16cf5 100644 --- a/samples/Basic_Mdns/app/application.cpp +++ b/samples/Basic_Mdns/app/application.cpp @@ -17,18 +17,21 @@ void gotIP(IpAddress ip, IpAddress netmask, IpAddress gateway) finder.onAnswer([](mDNS::Answer& answer) { // m_printHex("ANSWER", &answer, sizeof(answer)); debug_i(">> name: %s", answer.name); - debug_i(" data: %s", answer.data); - debug_i(" type: 0x%04x", answer.type); + debug_i(" type: %s (0x%04X)", toString(answer.type).c_str(), unsigned(answer.type)); debug_i(" class: 0x%04x", answer.klass); debug_i(" ttl: %u", answer.ttl); debug_i(" flsh?: %u", answer.isCachedFlush); debug_i(" vald?: %u", answer.isValid); + // auto len = strlen(answer.data); + m_printHex(" data", answer.data, answer.dataLen); + m_printHex(" raw ", answer.rawData, answer.rawDataLen); + // debug_i(" data: %s", answer.data); }); // bool ok = finder.search("_googlecast._tcp.local"); String hostname = F("_googlecast._tcp.local"); mDNS::Query query{}; memcpy(query.name, hostname.c_str(), hostname.length()); - query.type = mDNS::MDNS_TYPE_PTR; + query.type = mDNS::ResourceType::PTR; query.klass = 1; // "INternet" query.isUnicastResponse = false; query.isValid = true; From 54c6c41b387883ca4b370131221511dc43d0de1e Mon Sep 17 00:00:00 2001 From: mikee47 Date: Tue, 2 Mar 2021 08:53:02 +0000 Subject: [PATCH 04/52] Simplify using String for name and data fields --- .../mdns/include/Network/Mdns/Finder.h | 16 +- Sming/Components/mdns/src/Finder.cpp | 139 ++++++++---------- samples/Basic_Mdns/app/application.cpp | 4 +- 3 files changed, 79 insertions(+), 80 deletions(-) diff --git a/Sming/Components/mdns/include/Network/Mdns/Finder.h b/Sming/Components/mdns/include/Network/Mdns/Finder.h index 4e4157f2b0..0eae61deab 100644 --- a/Sming/Components/mdns/include/Network/Mdns/Finder.h +++ b/Sming/Components/mdns/include/Network/Mdns/Finder.h @@ -57,16 +57,26 @@ struct Query { * @brief A single mDNS Answer */ struct Answer { - char name[MAX_MDNS_NAME_LEN]; ///< object, domain or zone name. - char data[MAX_MDNS_NAME_LEN]; ///< The data portion of the resource record. - uint16_t dataLen; void* rawData; uint16_t rawDataLen; + String name; ///< object, domain or zone name. + String data; ///< The decoded data portion of the resource record. ResourceType type; ///< ResourceRecord Type. uint16_t klass; ///< ResourceRecord Class: Normally the value 1 for Internet (“IN”) uint32_t ttl; ///< ResourceRecord Time To Live: Number of seconds ths should be remembered. bool isCachedFlush; ///< Flush cache of records matching this name. bool isValid; ///< False if problems were encountered decoding packet. + // Decoded fields dependent upon resource type + union { + struct { + uint32_t addr; + } a; + struct { + uint16_t priority; + uint16_t weight; + uint16_t port; + } srv; + }; }; class Finder : protected UdpConnection diff --git a/Sming/Components/mdns/src/Finder.cpp b/Sming/Components/mdns/src/Finder.cpp index fb7741ec5b..cdf82a4feb 100644 --- a/Sming/Components/mdns/src/Finder.cpp +++ b/Sming/Components/mdns/src/Finder.cpp @@ -1,4 +1,6 @@ #include +#include +#include String toString(mDNS::ResourceType type) { @@ -19,11 +21,21 @@ struct Packet { uint8_t* data; mutable uint16_t pos; - void* ptr() const + const void* ptr() const { return data + pos; } + void* ptr() + { + return data + pos; + } + + void skip(uint16_t len) + { + pos += len; + } + uint8_t peek8() const { return data[pos]; @@ -44,12 +56,23 @@ struct Packet { return (read16() << 16) | read16(); } - void write(const void* s, uint16_t len) + void read(void* buffer, uint16_t len) const { - memcpy(&data[pos], s, len); + memcpy(buffer, ptr(), len); pos += len; } + String readString(uint16_t length) const + { + String s; + if(s.setLength(length)) { + read(s.begin(), length); + } else { + pos += length; + } + return s; + } + void write8(uint8_t value) { data[pos++] = value; @@ -60,63 +83,46 @@ struct Packet { write8(value >> 8); write8(value & 0xff); } -}; -bool writeToBuffer(uint8_t value, char* name, uint16_t& namePos, uint16_t nameLength) -{ - if(namePos + 1 < nameLength) { - name[namePos++] = value; - name[namePos] = '\0'; - return true; + void write32(uint32_t value) + { + write16(value >> 16); + write16(value & 0xffff); } - namePos++; - return false; -} - -void parseText(Packet& pkt, char* buffer, uint16_t bufferLength, uint16_t dataLength) -{ - uint16_t bufferPos{0}; - for(uint16_t i = 0; i < dataLength; i++) { - writeToBuffer(pkt.read8(), buffer, bufferPos, bufferLength); + void write(const void* s, uint16_t len) + { + memcpy(ptr(), s, len); + pos += len; } - buffer[bufferPos] = '\0'; -} +}; -void nameFromDnsPointer(const Packet& pkt, char* name, uint16_t namePos, uint16_t nameLength, bool recurse) +String nameFromDnsPointer(const Packet& pkt, const uint8_t* start) { - if(recurse) { - // Since we are adding more to an already populated buffer, - // replace the trailing EOL with the FQDN seperator. - namePos--; - writeToBuffer('.', name, namePos, nameLength); - } - if(pkt.peek8() < 0xC0) { // Since the first 2 bits are not set, // this is the start of a name section. // http://www.tcpipguide.com/free/t_DNSNameNotationandMessageCompressionTechnique.htm const uint8_t word_len = pkt.read8(); - for(uint8_t l = 0; l < word_len; l++) { - writeToBuffer(pkt.read8(), name, namePos, nameLength); - } + String s = pkt.readString(word_len); - writeToBuffer('\0', name, namePos, nameLength); - - if(pkt.peek8() > 0) { + if(pkt.peek8() != 0) { // Next word - nameFromDnsPointer(pkt, name, namePos, nameLength, true); + s += '.'; + s += nameFromDnsPointer(pkt, start); } else { // End of string. pkt.read8(); } - } else { - // Message Compression used. Next 2 bytes are a pointer to the actual name section. - uint16_t pointer = pkt.read16() & 0x3000; - Packet tmp{pkt.data, pointer}; - nameFromDnsPointer(tmp, name, namePos, nameLength, false); + + return s; } + + // Message Compression used. Next 2 bytes are a pointer to the actual name section. + uint16_t pointer = pkt.read16() & 0x3fff; + Packet tmp{const_cast(start + pointer), 0}; + return nameFromDnsPointer(tmp, start); } } // namespace @@ -272,8 +278,7 @@ void Finder::onReceive(pbuf* buf, IpAddress remoteIP, uint16_t remotePort) for(uint16_t i = 0; i < (answersCount + nsCount + additionalCount); i++) { Answer answer{}; - nameFromDnsPointer(pkt, answer.name, 0, MAX_MDNS_NAME_LEN, false); - + answer.name = nameFromDnsPointer(pkt, data); answer.type = ResourceType(pkt.read16()); uint8_t rrclass_0 = pkt.read8(); @@ -300,59 +305,43 @@ void Finder::onReceive(pbuf* buf, IpAddress remoteIP, uint16_t remotePort) switch(answer.type) { case ResourceType::A: // Returns a 32-bit IPv4 address - answer.dataLen = sprintf(answer.data, "%u.%u.%u.%u", pkt.read8(), pkt.read8(), pkt.read8(), pkt.read8()); + answer.a.addr = pkt.read32(); + answer.data = IpAddress(answer.a.addr).toString(); break; case ResourceType::PTR: // Pointer to a canonical name. - nameFromDnsPointer(pkt, answer.data, 0, MAX_MDNS_NAME_LEN, false); - answer.dataLen = strlen(answer.data); + answer.data = nameFromDnsPointer(pkt, data); break; case ResourceType::HINFO: // HINFO. host information - parseText(pkt, answer.data, MAX_MDNS_NAME_LEN, rdlength); - answer.dataLen = rdlength; + answer.data = pkt.readString(rdlength); break; case ResourceType::TXT: // Originally for arbitrary human-readable text in a DNS record. // We only return the first MAX_MDNS_NAME_LEN bytes of this record type. - parseText(pkt, answer.data, MAX_MDNS_NAME_LEN, rdlength); - answer.dataLen = rdlength; + answer.data = pkt.readString(rdlength); break; case ResourceType::AAAA: { // Returns a 128-bit IPv6 address. - uint16_t data_pos{0}; - for(uint16_t i = 0; i < rdlength; i++) { - auto c = pkt.read8(); - if(data_pos < MAX_MDNS_NAME_LEN - 3) { - sprintf(answer.data + data_pos, "%02X:", c); - } - data_pos += 3; - } - answer.data[data_pos - 1] = '\0'; // Remove trailing ':' - answer.dataLen = data_pos; + answer.data = makeHexString(static_cast(pkt.ptr()), rdlength, ':'); + pkt.skip(rdlength); break; } case ResourceType::SRV: { // Server Selection. - auto priority = pkt.read16(); - auto weight = pkt.read16(); - auto port = pkt.read16(); - auto len = sprintf(answer.data, "p=%u;w=%u;port=%u;host=", priority, weight, port); - nameFromDnsPointer(pkt, answer.data, len, MAX_MDNS_NAME_LEN - len - 1, false); - answer.dataLen = strlen(answer.data); + answer.srv.priority = pkt.read16(); + answer.srv.weight = pkt.read16(); + answer.srv.port = pkt.read16(); + answer.data = nameFromDnsPointer(pkt, data); + // char buffer[64]; + // sprintf(buffer, "p=%u;w=%u;port=%u;host=", priority, weight, port); + // answer.data = buffer; break; } default: - uint16_t data_pos{0}; - for(uint16_t i = 0; i < rdlength; i++) { - auto c = pkt.read8(); - if(data_pos < MAX_MDNS_NAME_LEN - 3) { - sprintf(answer.data + data_pos, "%02X ", c); - } - data_pos += 3; - } - answer.dataLen = strlen(answer.data); + answer.data = makeHexString(static_cast(pkt.ptr()), rdlength, ' '); + pkt.skip(rdlength); } answer.isValid = true; diff --git a/samples/Basic_Mdns/app/application.cpp b/samples/Basic_Mdns/app/application.cpp index 6977e16cf5..4fc88d02a4 100644 --- a/samples/Basic_Mdns/app/application.cpp +++ b/samples/Basic_Mdns/app/application.cpp @@ -16,14 +16,14 @@ void gotIP(IpAddress ip, IpAddress netmask, IpAddress gateway) finder.onAnswer([](mDNS::Answer& answer) { // m_printHex("ANSWER", &answer, sizeof(answer)); - debug_i(">> name: %s", answer.name); + debug_i(">> name: %s", answer.name.c_str()); debug_i(" type: %s (0x%04X)", toString(answer.type).c_str(), unsigned(answer.type)); debug_i(" class: 0x%04x", answer.klass); debug_i(" ttl: %u", answer.ttl); debug_i(" flsh?: %u", answer.isCachedFlush); debug_i(" vald?: %u", answer.isValid); // auto len = strlen(answer.data); - m_printHex(" data", answer.data, answer.dataLen); + m_printHex(" data", answer.data.c_str(), answer.data.length()); m_printHex(" raw ", answer.rawData, answer.rawDataLen); // debug_i(" data: %s", answer.data); }); From 99ca3273c57c19f8407b2a94ad4a173a9940d790 Mon Sep 17 00:00:00 2001 From: mikee47 Date: Tue, 2 Mar 2021 09:32:52 +0000 Subject: [PATCH 05/52] Lists WIP --- .../mdns/include/Network/Mdns/Finder.h | 28 ++++++++++++++++++- Sming/Components/mdns/src/Finder.cpp | 17 +++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/Sming/Components/mdns/include/Network/Mdns/Finder.h b/Sming/Components/mdns/include/Network/Mdns/Finder.h index 0eae61deab..30394eb861 100644 --- a/Sming/Components/mdns/include/Network/Mdns/Finder.h +++ b/Sming/Components/mdns/include/Network/Mdns/Finder.h @@ -10,6 +10,7 @@ #include #include #include +#include namespace mDNS { @@ -56,7 +57,12 @@ struct Query { /** * @brief A single mDNS Answer */ -struct Answer { +class Answer : public LinkedObjectTemplate +{ +public: + using List = LinkedObjectListTemplate; + using OwnedList = OwnedLinkedObjectListTemplate; + void* rawData; uint16_t rawDataLen; String name; ///< object, domain or zone name. @@ -79,6 +85,26 @@ struct Answer { }; }; +/** + * @brief Encapsulates a response packet for flexible interrogation + */ +class Response : public Answer::OwnedList +{ +public: + Response(const uint8_t data, size_t length) : data(data), length(length) + { + } + + size_t count() const + { + return read16() + } + +private: + const uint8_t* data; + uint16_t length; +}; + class Finder : protected UdpConnection { public: diff --git a/Sming/Components/mdns/src/Finder.cpp b/Sming/Components/mdns/src/Finder.cpp index cdf82a4feb..a68586ea40 100644 --- a/Sming/Components/mdns/src/Finder.cpp +++ b/Sming/Components/mdns/src/Finder.cpp @@ -125,6 +125,23 @@ String nameFromDnsPointer(const Packet& pkt, const uint8_t* start) return nameFromDnsPointer(tmp, start); } +void skipNameFromDnsPointer(const Packet& pkt, const uint8_t* start) +{ + if(pkt.peek8() < 0xC0) { + const uint8_t word_len = pkt.read8(); + pkt.skip(word_len); + if(pkt.peek8() != 0) { + skipNameFromDnsPointer(pkt, start); + } else { + pkt.read8(); + } + } else { + uint16_t pointer = pkt.read16() & 0x3fff; + Packet tmp{const_cast(start + pointer), 0}; + skipNameFromDnsPointer(tmp, start); + } +} + } // namespace namespace mDNS From 7619c4b6eddd5ef99cef4082f185a471b8de374f Mon Sep 17 00:00:00 2001 From: mikee47 Date: Tue, 2 Mar 2021 09:41:22 +0000 Subject: [PATCH 06/52] Refactor into classes --- Sming/Components/mdns/component.mk | 4 +- .../mdns/include/Network/Mdns/Finder.h | 156 --------- Sming/Components/mdns/src/Answer.cpp | 82 +++++ Sming/Components/mdns/src/Finder.cpp | 297 ++---------------- Sming/Components/mdns/src/Name.cpp | 55 ++++ Sming/Components/mdns/src/Packet.h | 95 ++++++ Sming/Components/mdns/src/ResourceType.cpp | 14 + Sming/Components/mdns/src/Responder.cpp | 2 +- Sming/Components/mdns/src/Response.cpp | 59 ++++ .../mdns/src/include/Network/Mdns/Answer.h | 59 ++++ .../mdns/src/include/Network/Mdns/Finder.h | 72 +++++ .../mdns/src/include/Network/Mdns/Name.h | 34 ++ .../mdns/src/include/Network/Mdns/Query.h | 18 ++ .../src/include/Network/Mdns/ResourceType.h | 28 ++ .../include/Network/Mdns/Responder.h | 0 .../mdns/src/include/Network/Mdns/Response.h | 49 +++ .../{ => src}/include/Network/Mdns/Service.h | 0 samples/Basic_Mdns/app/application.cpp | 29 +- 18 files changed, 604 insertions(+), 449 deletions(-) delete mode 100644 Sming/Components/mdns/include/Network/Mdns/Finder.h create mode 100644 Sming/Components/mdns/src/Answer.cpp create mode 100644 Sming/Components/mdns/src/Name.cpp create mode 100644 Sming/Components/mdns/src/Packet.h create mode 100644 Sming/Components/mdns/src/ResourceType.cpp create mode 100644 Sming/Components/mdns/src/Response.cpp create mode 100644 Sming/Components/mdns/src/include/Network/Mdns/Answer.h create mode 100644 Sming/Components/mdns/src/include/Network/Mdns/Finder.h create mode 100644 Sming/Components/mdns/src/include/Network/Mdns/Name.h create mode 100644 Sming/Components/mdns/src/include/Network/Mdns/Query.h create mode 100644 Sming/Components/mdns/src/include/Network/Mdns/ResourceType.h rename Sming/Components/mdns/{ => src}/include/Network/Mdns/Responder.h (100%) create mode 100644 Sming/Components/mdns/src/include/Network/Mdns/Response.h rename Sming/Components/mdns/{ => src}/include/Network/Mdns/Service.h (100%) diff --git a/Sming/Components/mdns/component.mk b/Sming/Components/mdns/component.mk index f07f849a3f..f25ff5d412 100644 --- a/Sming/Components/mdns/component.mk +++ b/Sming/Components/mdns/component.mk @@ -1,7 +1,7 @@ COMPONENT_SRCDIRS := src -COMPONENT_INCDIRS := include +COMPONENT_INCDIRS := src/include -COMPONENT_DOXYGEN_INPUT := include +COMPONENT_DOXYGEN_INPUT := src/include COMPONENT_VARS += ENABLE_CUSTOM_LWIP diff --git a/Sming/Components/mdns/include/Network/Mdns/Finder.h b/Sming/Components/mdns/include/Network/Mdns/Finder.h deleted file mode 100644 index 30394eb861..0000000000 --- a/Sming/Components/mdns/include/Network/Mdns/Finder.h +++ /dev/null @@ -1,156 +0,0 @@ -/** - * Since big portions of the code are copied from Finder.h and Finder.cpp are copied - * from MrDunk's mDNS code these files are distributed under the same license as his project. - * - * MIT license: https://github.com/mrdunk/esp8266_mdns/blob/master/LICENCE.txt - */ -#pragma once - -#include -#include -#include -#include -#include - -namespace mDNS -{ -#define MDNS_IP 224, 0, 0, 251 -#define MDNS_TARGET_PORT 5353 -#define MDNS_SOURCE_PORT 5353 -#define MDNS_TTL 255 - -#define MAX_PACKET_SIZE 1024 - -// The mDNS spec says this should never be more than 256 (including trailing '\0'). -#define MAX_MDNS_NAME_LEN 256 - -/** - * @brief MDNS resource type identifiers - * - * (name, value, description) - */ -#define MDNS_RESOURCE_TYPE_MAP(XX) \ - XX(A, 0x0001, "32-bit IPv4 address") \ - XX(PTR, 0x000C, "Pointer to a canonical name") \ - XX(HINFO, 0x000D, "Host Information") \ - XX(TXT, 0x0010, "Arbitrary human-readable text") \ - XX(AAAA, 0x001C, "128-bit IPv6 address") \ - XX(SRV, 0x0021, "Server selection") - -enum class ResourceType : uint16_t { -#define XX(name, value, desc) name = value, - MDNS_RESOURCE_TYPE_MAP(XX) -#undef XX -}; - -/** - * @brief A single mDNS Query - */ -struct Query { - char name[MAX_MDNS_NAME_LEN]; ///< Question Name: Contains the object, domain or zone name. - ResourceType type; ///< Question Type: Type of question being asked by client. - uint16_t klass; ///< Question Class: Normally the value 1 for Internet (“IN”) - bool isUnicastResponse; // - bool isValid; ///< False if problems were encountered decoding packet. -}; - -/** - * @brief A single mDNS Answer - */ -class Answer : public LinkedObjectTemplate -{ -public: - using List = LinkedObjectListTemplate; - using OwnedList = OwnedLinkedObjectListTemplate; - - void* rawData; - uint16_t rawDataLen; - String name; ///< object, domain or zone name. - String data; ///< The decoded data portion of the resource record. - ResourceType type; ///< ResourceRecord Type. - uint16_t klass; ///< ResourceRecord Class: Normally the value 1 for Internet (“IN”) - uint32_t ttl; ///< ResourceRecord Time To Live: Number of seconds ths should be remembered. - bool isCachedFlush; ///< Flush cache of records matching this name. - bool isValid; ///< False if problems were encountered decoding packet. - // Decoded fields dependent upon resource type - union { - struct { - uint32_t addr; - } a; - struct { - uint16_t priority; - uint16_t weight; - uint16_t port; - } srv; - }; -}; - -/** - * @brief Encapsulates a response packet for flexible interrogation - */ -class Response : public Answer::OwnedList -{ -public: - Response(const uint8_t data, size_t length) : data(data), length(length) - { - } - - size_t count() const - { - return read16() - } - -private: - const uint8_t* data; - uint16_t length; -}; - -class Finder : protected UdpConnection -{ -public: - using AnswerDelegate = Delegate; - - Finder() : out(*this) - { - } - - ~Finder(); - - void onAnswer(AnswerDelegate callback) - { - answerCallback = callback; - } - - bool search(const String& hostname, ResourceType type = ResourceType::SRV); - - bool search(const Query& query); - -protected: - void onReceive(pbuf* buf, IpAddress remoteIP, uint16_t remotePort) override; - -private: - /* - * Need a separate UDP connection for sending requests - */ - class UdpOut : public UdpConnection - { - public: - UdpOut(Finder& finder) : finder(finder) - { - } - - protected: - void onReceive(pbuf* buf, IpAddress remoteIP, uint16_t remotePort) override; - Finder& finder; - }; - - void initialise(); - - AnswerDelegate answerCallback; - UdpOut out; - bool initialised{false}; -}; - -} // namespace mDNS - -String toString(mDNS::ResourceType type); diff --git a/Sming/Components/mdns/src/Answer.cpp b/Sming/Components/mdns/src/Answer.cpp new file mode 100644 index 0000000000..ab823bbf98 --- /dev/null +++ b/Sming/Components/mdns/src/Answer.cpp @@ -0,0 +1,82 @@ +#include "include/Network/Mdns/Answer.h" +#include "include/Network/Mdns/Response.h" +#include "Packet.h" +#include +#include +#include + +namespace mDNS +{ +bool Answer::parse(Packet& pkt) +{ + auto size = response.getSize(); + + namePtr = pkt.data; + auto namelen = getName().getDataLength(); + pkt.skip(namelen); + + type = ResourceType(pkt.read16()); + + uint8_t rrclass_0 = pkt.read8(); + uint8_t rrclass_1 = pkt.read8(); + isCachedFlush = (0b10000000 & rrclass_0); + klass = ((rrclass_0 & 0b01111111) << 8) + rrclass_1; + + ttl = pkt.read32(); + + if(pkt.pos > size) { + debug_e("[MDNS] Packet overrun, pos = %u, size = %u", pkt.pos, size); + // Something has gone wrong receiving or parsing the data. + isValid = false; + return false; + } + + uint16_t rdlength = pkt.read16(); + + rawData = pkt.ptr(); + rawDataLen = rdlength; + + switch(type) { + case ResourceType::A: // Returns a 32-bit IPv4 address + a.addr = pkt.read32(); + data = IpAddress(a.addr).toString(); + break; + + case ResourceType::PTR: // Pointer to a canonical name. + data = Name(response, pkt.data); + break; + + case ResourceType::HINFO: // HINFO. host information + data = pkt.readString(rdlength); + break; + + case ResourceType::TXT: // Originally for arbitrary human-readable text in a DNS record. + // We only return the first MAX_MDNS_NAME_LEN bytes of this record type. + data = pkt.readString(rdlength); + break; + + case ResourceType::AAAA: { // Returns a 128-bit IPv6 address. + data = makeHexString(static_cast(pkt.ptr()), rdlength, ':'); + break; + } + + case ResourceType::SRV: { // Server Selection. + srv.priority = pkt.read16(); + srv.weight = pkt.read16(); + srv.port = pkt.read16(); + data = Name(response, pkt.data); + // char buffer[64]; + // sprintf(buffer, "p=%u;w=%u;port=%u;host=", priority, weight, port); + // answer->data = buffer; + break; + } + + default: + data = makeHexString(static_cast(pkt.ptr()), rdlength, ' '); + } + + pkt.data = rawData + rawDataLen; + isValid = true; + return true; +} +} // namespace mDNS \ No newline at end of file diff --git a/Sming/Components/mdns/src/Finder.cpp b/Sming/Components/mdns/src/Finder.cpp index a68586ea40..82347de145 100644 --- a/Sming/Components/mdns/src/Finder.cpp +++ b/Sming/Components/mdns/src/Finder.cpp @@ -1,148 +1,6 @@ -#include -#include -#include - -String toString(mDNS::ResourceType type) -{ - switch(type) { -#define XX(name, value, desc) \ - case mDNS::ResourceType::name: \ - return F(#name); - MDNS_RESOURCE_TYPE_MAP(XX) -#undef XX - default: - return String(unsigned(type)); - } -} - -namespace -{ -struct Packet { - uint8_t* data; - mutable uint16_t pos; - - const void* ptr() const - { - return data + pos; - } - - void* ptr() - { - return data + pos; - } - - void skip(uint16_t len) - { - pos += len; - } - - uint8_t peek8() const - { - return data[pos]; - } - - uint8_t read8() const - { - return data[pos++]; - } - - uint16_t read16() const - { - return (read8() << 8) | read8(); - } - - uint32_t read32() const - { - return (read16() << 16) | read16(); - } - - void read(void* buffer, uint16_t len) const - { - memcpy(buffer, ptr(), len); - pos += len; - } - - String readString(uint16_t length) const - { - String s; - if(s.setLength(length)) { - read(s.begin(), length); - } else { - pos += length; - } - return s; - } - - void write8(uint8_t value) - { - data[pos++] = value; - } - - void write16(uint16_t value) - { - write8(value >> 8); - write8(value & 0xff); - } - - void write32(uint32_t value) - { - write16(value >> 16); - write16(value & 0xffff); - } - - void write(const void* s, uint16_t len) - { - memcpy(ptr(), s, len); - pos += len; - } -}; - -String nameFromDnsPointer(const Packet& pkt, const uint8_t* start) -{ - if(pkt.peek8() < 0xC0) { - // Since the first 2 bits are not set, - // this is the start of a name section. - // http://www.tcpipguide.com/free/t_DNSNameNotationandMessageCompressionTechnique.htm - - const uint8_t word_len = pkt.read8(); - String s = pkt.readString(word_len); - - if(pkt.peek8() != 0) { - // Next word - s += '.'; - s += nameFromDnsPointer(pkt, start); - } else { - // End of string. - pkt.read8(); - } - - return s; - } - - // Message Compression used. Next 2 bytes are a pointer to the actual name section. - uint16_t pointer = pkt.read16() & 0x3fff; - Packet tmp{const_cast(start + pointer), 0}; - return nameFromDnsPointer(tmp, start); -} - -void skipNameFromDnsPointer(const Packet& pkt, const uint8_t* start) -{ - if(pkt.peek8() < 0xC0) { - const uint8_t word_len = pkt.read8(); - pkt.skip(word_len); - if(pkt.peek8() != 0) { - skipNameFromDnsPointer(pkt, start); - } else { - pkt.read8(); - } - } else { - uint16_t pointer = pkt.read16() & 0x3fff; - Packet tmp{const_cast(start + pointer), 0}; - skipNameFromDnsPointer(tmp, start); - } -} - -} // namespace +#include "include/Network/Mdns/Finder.h" +#include "include/Network/Mdns/Response.h" +#include "Packet.h" namespace mDNS { @@ -155,12 +13,8 @@ Finder::~Finder() bool Finder::search(const String& hostname, ResourceType type) { - if(hostname.length() > MAX_MDNS_NAME_LEN - 1) { - return false; - } - Query query{}; - memcpy(query.name, hostname.c_str(), hostname.length()); + query.name = hostname; query.type = type; query.klass = 1; // "INternet" query.isUnicastResponse = false; @@ -171,6 +25,10 @@ bool Finder::search(const String& hostname, ResourceType type) bool Finder::search(const Query& query) { + if(query.name.length() > MAX_MDNS_NAME_LEN - 1) { + return false; + } + uint8_t buffer[MAX_PACKET_SIZE]{}; Packet pkt{buffer, 0}; @@ -197,21 +55,16 @@ bool Finder::search(const Query& query) uint16_t word_start{0}; uint16_t word_end{0}; - const char* name = query.name; - while(true) { - if(name[word_end] == '.' || name[word_end] == '\0') { - const uint8_t word_length = word_end - word_start; - - pkt.write8(word_length); - pkt.write(&name[word_start], word_length); - if(name[word_end] == '\0') { - break; - } - word_end++; // Skip the '.' character. - word_start = word_end; - } - word_end++; - } + size_t pos{0}; + auto& name = query.name; + auto namelen = name.length(); + do { + int sep = name.indexOf('.', pos); + auto wordLength = (sep >= 0) ? (sep - pos) : (namelen - pos); + pkt.write8(wordLength); + pkt.write(name.c_str() + pos, wordLength); + pos = sep + 1; + } while(pos > 0); pkt.write8('\0'); // End of name. @@ -252,117 +105,9 @@ void Finder::onReceive(pbuf* buf, IpAddress remoteIP, uint16_t remotePort) return; } - // process the answer here... - auto data = static_cast(buf->payload); - auto size = buf->len; - - // m_printHex("MDNS", data, size, 0); - - // check if we have a response or a query - if(!(data[2] & 0b10000000)) { - debug_d("Message is not a response. Ignoring."); - return; - } - - bool truncated = data[2] & 0b00000010; - // TODO: If it's truncated we can expect more data soon so we should wait for additional records before deciding whether to respond. - - if(data[3] & 0b00001111) { - // Non zero Response code implies error. - debug_w("Got errored MDNS answer"); - return; - } - - Packet pkt{data, 4}; - - // Number of incoming queries. - uint16_t questionsCount = pkt.read16(); - if(questionsCount > 0) { - // we are interested only in responses. - return; - } - - // Number of incoming answers. - uint16_t answersCount = pkt.read16(); - - // Number of incoming Name Server resource records. - uint16_t nsCount = pkt.read16(); - - // Number of incoming Additional resource records. - uint16_t additionalCount = pkt.read16(); - - // List of answers - for(uint16_t i = 0; i < (answersCount + nsCount + additionalCount); i++) { - Answer answer{}; - - answer.name = nameFromDnsPointer(pkt, data); - answer.type = ResourceType(pkt.read16()); - - uint8_t rrclass_0 = pkt.read8(); - uint8_t rrclass_1 = pkt.read8(); - answer.isCachedFlush = (0b10000000 & rrclass_0); - answer.klass = ((rrclass_0 & 0b01111111) << 8) + rrclass_1; - - answer.ttl = pkt.read32(); - - if(pkt.pos > size_t(size)) { - debug_e("[MDNS] Packet overrun, pos = %u, size = %u", pkt.pos, size); - // Something has gone wrong receiving or parsing the data. - answer.isValid = false; - if(answerCallback) { - answerCallback(answer); - } - break; - } - - uint16_t rdlength = pkt.read16(); - - answer.rawData = pkt.ptr(); - answer.rawDataLen = rdlength; - - switch(answer.type) { - case ResourceType::A: // Returns a 32-bit IPv4 address - answer.a.addr = pkt.read32(); - answer.data = IpAddress(answer.a.addr).toString(); - break; - - case ResourceType::PTR: // Pointer to a canonical name. - answer.data = nameFromDnsPointer(pkt, data); - break; - - case ResourceType::HINFO: // HINFO. host information - answer.data = pkt.readString(rdlength); - break; - - case ResourceType::TXT: // Originally for arbitrary human-readable text in a DNS record. - // We only return the first MAX_MDNS_NAME_LEN bytes of this record type. - answer.data = pkt.readString(rdlength); - break; - - case ResourceType::AAAA: { // Returns a 128-bit IPv6 address. - answer.data = makeHexString(static_cast(pkt.ptr()), rdlength, ':'); - pkt.skip(rdlength); - break; - } - - case ResourceType::SRV: { // Server Selection. - answer.srv.priority = pkt.read16(); - answer.srv.weight = pkt.read16(); - answer.srv.port = pkt.read16(); - answer.data = nameFromDnsPointer(pkt, data); - // char buffer[64]; - // sprintf(buffer, "p=%u;w=%u;port=%u;host=", priority, weight, port); - // answer.data = buffer; - break; - } - - default: - answer.data = makeHexString(static_cast(pkt.ptr()), rdlength, ' '); - pkt.skip(rdlength); - } - - answer.isValid = true; - answerCallback(answer); + Response response(buf->payload, buf->len); + if(response.parse()) { + answerCallback(response); } } diff --git a/Sming/Components/mdns/src/Name.cpp b/Sming/Components/mdns/src/Name.cpp new file mode 100644 index 0000000000..fdbc28f66a --- /dev/null +++ b/Sming/Components/mdns/src/Name.cpp @@ -0,0 +1,55 @@ +#include "include/Network/Mdns/Name.h" +#include "include/Network/Mdns/Response.h" +#include "Packet.h" + +namespace mDNS +{ +String Name::toString() const +{ + String s; + Packet pkt{data, 0}; + + while(true) { + if(pkt.peek8() < 0xC0) { + // Since the first 2 bits are not set, + // this is the start of a name section. + // http://www.tcpipguide.com/free/t_DNSNameNotationandMessageCompressionTechnique.htm + + const uint8_t word_len = pkt.read8(); + s += pkt.readString(word_len); + + if(pkt.peek8() == 0) { + return s; // End of string + } + + // Next word + s += '.'; + continue; + } + + // Message Compression used. Next 2 bytes are a pointer to the actual name section. + uint16_t pointer = pkt.read16() & 0x3fff; + pkt.data = response.resolvePointer(pointer); + pkt.pos = 0; + } +} + +uint16_t Name::getDataLength() const +{ + Packet pkt{data, 0}; + + while(true) { + if(pkt.peek8() < 0xC0) { + const uint8_t word_len = pkt.read8(); + pkt.skip(word_len); + if(pkt.peek8() == 0) { + return pkt.data - data; + } + } else { + // Pointer at end + return (pkt.data - data) + sizeof(uint16_t); + } + } +} + +} // namespace mDNS diff --git a/Sming/Components/mdns/src/Packet.h b/Sming/Components/mdns/src/Packet.h new file mode 100644 index 0000000000..daf47efbd0 --- /dev/null +++ b/Sming/Components/mdns/src/Packet.h @@ -0,0 +1,95 @@ +#pragma once + +#include + +namespace mDNS +{ +/** + * @brief Helper class for reading/writing packet content + */ +struct Packet { + uint8_t* data; + mutable uint16_t pos; + + const uint8_t* ptr() const + { + return data + pos; + } + + uint8_t* ptr() + { + return data + pos; + } + + void skip(uint16_t len) const + { + pos += len; + } + + uint8_t peek8() const + { + return data[pos]; + } + + uint8_t read8() const + { + return data[pos++]; + } + + uint16_t peek16() const + { + return (data[pos] << 8) | data[pos + 1]; + } + + uint16_t read16() const + { + return (read8() << 8) | read8(); + } + + uint32_t read32() const + { + return (read16() << 16) | read16(); + } + + void read(void* buffer, uint16_t len) const + { + memcpy(buffer, ptr(), len); + pos += len; + } + + String readString(uint16_t length) const + { + String s; + if(s.setLength(length)) { + read(s.begin(), length); + } else { + pos += length; + } + return s; + } + + void write8(uint8_t value) + { + data[pos++] = value; + } + + void write16(uint16_t value) + { + write8(value >> 8); + write8(value & 0xff); + } + + void write32(uint32_t value) + { + write16(value >> 16); + write16(value & 0xffff); + } + + void write(const void* s, uint16_t len) + { + memcpy(ptr(), s, len); + pos += len; + } +}; + +} // namespace mDNS \ No newline at end of file diff --git a/Sming/Components/mdns/src/ResourceType.cpp b/Sming/Components/mdns/src/ResourceType.cpp new file mode 100644 index 0000000000..075f1ded1b --- /dev/null +++ b/Sming/Components/mdns/src/ResourceType.cpp @@ -0,0 +1,14 @@ +#include "include/Network/Mdns/ResourceType.h" + +String toString(mDNS::ResourceType type) +{ + switch(type) { +#define XX(name, value, desc) \ + case mDNS::ResourceType::name: \ + return F(#name); + MDNS_RESOURCE_TYPE_MAP(XX) +#undef XX + default: + return String(unsigned(type)); + } +} diff --git a/Sming/Components/mdns/src/Responder.cpp b/Sming/Components/mdns/src/Responder.cpp index de00cf2159..2ae3ae632b 100644 --- a/Sming/Components/mdns/src/Responder.cpp +++ b/Sming/Components/mdns/src/Responder.cpp @@ -1,4 +1,4 @@ -#include +#include "include/Network/Mdns/Responder.h" #include #if MDNS_LWIP >= 0x0200 diff --git a/Sming/Components/mdns/src/Response.cpp b/Sming/Components/mdns/src/Response.cpp new file mode 100644 index 0000000000..0ac85a8540 --- /dev/null +++ b/Sming/Components/mdns/src/Response.cpp @@ -0,0 +1,59 @@ +#include "include/Network/Mdns/Response.h" +#include "Packet.h" +#include +#include + +namespace mDNS +{ +bool Response::parse() +{ + // check if we have a response or a query + if(!isAnswer()) { + debug_d("Message is not a response. Ignoring."); + return false; + } + + // TODO: If it's truncated we can expect more data soon so we should wait for additional records before deciding whether to respond. + // if(isTruncated()) + // { + // } + + // Non zero Response code implies error. + if(getResponseCode() != 0) { + debug_w("Got errored MDNS answer"); + return false; + } + + Packet pkt{data, 4}; + + // Number of incoming queries. + uint16_t questionsCount = pkt.read16(); + if(questionsCount > 0) { + // we are interested only in responses. + return false; + } + + // Number of incoming answers. + uint16_t answersCount = pkt.read16(); + + // Number of incoming Name Server resource records. + uint16_t nsCount = pkt.read16(); + + // Number of incoming Additional resource records. + uint16_t additionalCount = pkt.read16(); + + // List of answers + bool ok{true}; + for(uint16_t i = 0; i < (answersCount + nsCount + additionalCount); i++) { + auto answer = new Answer(*this); + add(answer); + if(!answer->parse(pkt)) { + ok = false; + break; + } + } + + return ok; +} + +} // namespace mDNS diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Answer.h b/Sming/Components/mdns/src/include/Network/Mdns/Answer.h new file mode 100644 index 0000000000..56006312ae --- /dev/null +++ b/Sming/Components/mdns/src/include/Network/Mdns/Answer.h @@ -0,0 +1,59 @@ +#pragma once + +#include +#include "Name.h" +#include "ResourceType.h" + +namespace mDNS +{ +class Response; +struct Packet; + +/** + * @brief A single mDNS Answer + */ +class Answer : public LinkedObjectTemplate +{ +public: + using List = LinkedObjectListTemplate; + using OwnedList = OwnedLinkedObjectListTemplate; + + Answer(Response& response) : response(response) + { + } + + bool parse(Packet& pkt); + + /** + * @brief Object, domain or zone name + */ + Name getName() const + { + return Name(response, namePtr); + } + + Response& response; + uint8_t* namePtr; + uint8_t* rawData; + uint16_t rawDataLen; + // Name name; ///< object, domain or zone name. + String data; ///< The decoded data portion of the resource record. + ResourceType type; ///< ResourceRecord Type. + uint16_t klass; ///< ResourceRecord Class: Normally the value 1 for Internet (“IN”) + uint32_t ttl; ///< ResourceRecord Time To Live: Number of seconds ths should be remembered. + bool isCachedFlush; ///< Flush cache of records matching this name. + bool isValid; ///< False if problems were encountered decoding packet. + // Decoded fields dependent upon resource type + union { + struct { + uint32_t addr; + } a; + struct { + uint16_t priority; + uint16_t weight; + uint16_t port; + } srv; + }; +}; + +} // namespace mDNS \ No newline at end of file diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Finder.h b/Sming/Components/mdns/src/include/Network/Mdns/Finder.h new file mode 100644 index 0000000000..178e2938ea --- /dev/null +++ b/Sming/Components/mdns/src/include/Network/Mdns/Finder.h @@ -0,0 +1,72 @@ +/** + * Since big portions of the code are copied from Finder.h and Finder.cpp are copied + * from MrDunk's mDNS code these files are distributed under the same license as his project. + * + * MIT license: https://github.com/mrdunk/esp8266_mdns/blob/master/LICENCE.txt + */ +#pragma once + +#include +#include +#include "Query.h" +#include "Response.h" + +namespace mDNS +{ +#define MDNS_IP 224, 0, 0, 251 +#define MDNS_TARGET_PORT 5353 +#define MDNS_SOURCE_PORT 5353 +#define MDNS_TTL 255 + +#define MAX_PACKET_SIZE 1024 + +// The mDNS spec says this should never be more than 256 (including trailing '\0'). +#define MAX_MDNS_NAME_LEN 256 + +class Finder : protected UdpConnection +{ +public: + using AnswerDelegate = Delegate; + + Finder() : out(*this) + { + } + + ~Finder(); + + void onAnswer(AnswerDelegate callback) + { + answerCallback = callback; + } + + bool search(const String& hostname, ResourceType type = ResourceType::SRV); + + bool search(const Query& query); + +protected: + void onReceive(pbuf* buf, IpAddress remoteIP, uint16_t remotePort) override; + +private: + /* + * Need a separate UDP connection for sending requests + */ + class UdpOut : public UdpConnection + { + public: + UdpOut(Finder& finder) : finder(finder) + { + } + + protected: + void onReceive(pbuf* buf, IpAddress remoteIP, uint16_t remotePort) override; + Finder& finder; + }; + + void initialise(); + + AnswerDelegate answerCallback; + UdpOut out; + bool initialised{false}; +}; + +} // namespace mDNS diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Name.h b/Sming/Components/mdns/src/include/Network/Mdns/Name.h new file mode 100644 index 0000000000..6ab8a22e5a --- /dev/null +++ b/Sming/Components/mdns/src/include/Network/Mdns/Name.h @@ -0,0 +1,34 @@ +#pragma once + +#include + +namespace mDNS +{ +class Response; +struct Packet; + +/** + * @brief Encoded DNS name + */ +class Name +{ +public: + Name(Response& response, uint8_t* data) : response(response), data(data) + { + } + + uint16_t getDataLength() const; + + String toString() const; + + operator String() const + { + return toString(); + } + +private: + Response& response; + uint8_t* data; +}; + +} // namespace mDNS \ No newline at end of file diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Query.h b/Sming/Components/mdns/src/include/Network/Mdns/Query.h new file mode 100644 index 0000000000..73a876b78e --- /dev/null +++ b/Sming/Components/mdns/src/include/Network/Mdns/Query.h @@ -0,0 +1,18 @@ +#pragma once + +#include "ResourceType.h" + +namespace mDNS +{ +/** + * @brief A single mDNS Query + */ +struct Query { + String name; ///< Question Name: Contains the object, domain or zone name. + ResourceType type; ///< Question Type: Type of question being asked by client. + uint16_t klass; ///< Question Class: Normally the value 1 for Internet (“IN”) + bool isUnicastResponse; // + bool isValid; ///< False if problems were encountered decoding packet. +}; + +} // namespace mDNS diff --git a/Sming/Components/mdns/src/include/Network/Mdns/ResourceType.h b/Sming/Components/mdns/src/include/Network/Mdns/ResourceType.h new file mode 100644 index 0000000000..890e3ad9dd --- /dev/null +++ b/Sming/Components/mdns/src/include/Network/Mdns/ResourceType.h @@ -0,0 +1,28 @@ +#pragma once + +#include + +/** + * @brief MDNS resource type identifiers + * + * (name, value, description) + */ +#define MDNS_RESOURCE_TYPE_MAP(XX) \ + XX(A, 0x0001, "32-bit IPv4 address") \ + XX(PTR, 0x000C, "Pointer to a canonical name") \ + XX(HINFO, 0x000D, "Host Information") \ + XX(TXT, 0x0010, "Arbitrary human-readable text") \ + XX(AAAA, 0x001C, "128-bit IPv6 address") \ + XX(SRV, 0x0021, "Server selection") + +namespace mDNS +{ +enum class ResourceType : uint16_t { +#define XX(name, value, desc) name = value, + MDNS_RESOURCE_TYPE_MAP(XX) +#undef XX +}; + +} // namespace mDNS + +String toString(mDNS::ResourceType type); diff --git a/Sming/Components/mdns/include/Network/Mdns/Responder.h b/Sming/Components/mdns/src/include/Network/Mdns/Responder.h similarity index 100% rename from Sming/Components/mdns/include/Network/Mdns/Responder.h rename to Sming/Components/mdns/src/include/Network/Mdns/Responder.h diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Response.h b/Sming/Components/mdns/src/include/Network/Mdns/Response.h new file mode 100644 index 0000000000..10dcc60b6d --- /dev/null +++ b/Sming/Components/mdns/src/include/Network/Mdns/Response.h @@ -0,0 +1,49 @@ +#pragma once + +#include "Answer.h" + +namespace mDNS +{ +/** + * @brief Encapsulates a response packet for flexible interrogation + */ +class Response : public Answer::OwnedList +{ +public: + Response(void* data, uint16_t size) : data(static_cast(data)), size(size) + { + } + + bool parse(); + + bool isAnswer() const + { + return data[2] & 0x80; + } + + bool isTruncated() const + { + return data[2] & 0x02; + } + + uint8_t getResponseCode() const + { + return data[3] & 0x0f; + } + + uint8_t* resolvePointer(uint16_t pointer) + { + return data + pointer; + } + + uint16_t getSize() const + { + return size; + } + +private: + uint8_t* data; + uint16_t size; +}; + +} // namespace mDNS diff --git a/Sming/Components/mdns/include/Network/Mdns/Service.h b/Sming/Components/mdns/src/include/Network/Mdns/Service.h similarity index 100% rename from Sming/Components/mdns/include/Network/Mdns/Service.h rename to Sming/Components/mdns/src/include/Network/Mdns/Service.h diff --git a/samples/Basic_Mdns/app/application.cpp b/samples/Basic_Mdns/app/application.cpp index 4fc88d02a4..bffc98696f 100644 --- a/samples/Basic_Mdns/app/application.cpp +++ b/samples/Basic_Mdns/app/application.cpp @@ -14,23 +14,24 @@ void gotIP(IpAddress ip, IpAddress netmask, IpAddress gateway) Serial.print(F("Connected. Got IP: ")); Serial.println(ip); - finder.onAnswer([](mDNS::Answer& answer) { - // m_printHex("ANSWER", &answer, sizeof(answer)); - debug_i(">> name: %s", answer.name.c_str()); - debug_i(" type: %s (0x%04X)", toString(answer.type).c_str(), unsigned(answer.type)); - debug_i(" class: 0x%04x", answer.klass); - debug_i(" ttl: %u", answer.ttl); - debug_i(" flsh?: %u", answer.isCachedFlush); - debug_i(" vald?: %u", answer.isValid); - // auto len = strlen(answer.data); - m_printHex(" data", answer.data.c_str(), answer.data.length()); - m_printHex(" raw ", answer.rawData, answer.rawDataLen); - // debug_i(" data: %s", answer.data); + finder.onAnswer([](mDNS::Response& response) { + Serial.println(); + debug_i("RESPONSE!!"); + for(auto& answer : response) { + debug_i(">> name: %s", String(answer.getName()).c_str()); + debug_i(" type: %s (0x%04X)", toString(answer.type).c_str(), unsigned(answer.type)); + debug_i(" class: 0x%04x", answer.klass); + debug_i(" ttl: %u", answer.ttl); + debug_i(" flsh?: %u", answer.isCachedFlush); + debug_i(" vald?: %u", answer.isValid); + m_printHex(" data", answer.data.c_str(), answer.data.length()); + m_printHex(" raw ", answer.rawData, answer.rawDataLen); + } }); + // bool ok = finder.search("_googlecast._tcp.local"); - String hostname = F("_googlecast._tcp.local"); mDNS::Query query{}; - memcpy(query.name, hostname.c_str(), hostname.length()); + query.name = F("_googlecast._tcp.local"); query.type = mDNS::ResourceType::PTR; query.klass = 1; // "INternet" query.isUnicastResponse = false; From 44d6962137cbf3fe12d4a6224d688564c40e80eb Mon Sep 17 00:00:00 2001 From: mikee47 Date: Tue, 2 Mar 2021 15:01:02 +0000 Subject: [PATCH 07/52] save incoming data for testing --- Sming/Components/mdns/src/Finder.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Sming/Components/mdns/src/Finder.cpp b/Sming/Components/mdns/src/Finder.cpp index 82347de145..17cd4bdc16 100644 --- a/Sming/Components/mdns/src/Finder.cpp +++ b/Sming/Components/mdns/src/Finder.cpp @@ -1,6 +1,7 @@ #include "include/Network/Mdns/Finder.h" #include "include/Network/Mdns/Response.h" #include "Packet.h" +#include namespace mDNS { @@ -101,6 +102,15 @@ void Finder::UdpOut::onReceive(pbuf* buf, IpAddress remoteIP, uint16_t remotePor void Finder::onReceive(pbuf* buf, IpAddress remoteIP, uint16_t remotePort) { + // auto& hostfs = IFS::Host::getFileSystem(); + // String filename = remoteIP.toString(); + // filename += ".mdns"; + // auto f = hostfs.open(filename, IFS::OpenFlag::Create | IFS::OpenFlag::Truncate | IFS::OpenFlag::Write); + // hostfs.write(f, buf->payload, buf->len); + // hostfs.close(f); + // debug_i("Saved '%s'", filename.c_str()); + // return; + if(!answerCallback) { return; } From 57eafaa38f9f639721ca9cc7ef86c50ddae684f1 Mon Sep 17 00:00:00 2001 From: mikee47 Date: Tue, 2 Mar 2021 15:20:26 +0000 Subject: [PATCH 08/52] Test packets --- Sming/Components/mdns/src/Answer.cpp | 9 ++-- Sming/Components/mdns/src/Name.cpp | 7 ++- samples/Basic_Mdns/app/application.cpp | 43 ++++++++++++------ .../Basic_Mdns/resource/192.168.1.100.mdns | Bin 0 -> 497 bytes .../Basic_Mdns/resource/192.168.1.103.mdns | Bin 0 -> 346 bytes .../Basic_Mdns/resource/192.168.1.107.mdns | Bin 0 -> 110 bytes 6 files changed, 39 insertions(+), 20 deletions(-) create mode 100644 samples/Basic_Mdns/resource/192.168.1.100.mdns create mode 100644 samples/Basic_Mdns/resource/192.168.1.103.mdns create mode 100644 samples/Basic_Mdns/resource/192.168.1.107.mdns diff --git a/Sming/Components/mdns/src/Answer.cpp b/Sming/Components/mdns/src/Answer.cpp index ab823bbf98..a783c51682 100644 --- a/Sming/Components/mdns/src/Answer.cpp +++ b/Sming/Components/mdns/src/Answer.cpp @@ -11,7 +11,7 @@ bool Answer::parse(Packet& pkt) { auto size = response.getSize(); - namePtr = pkt.data; + namePtr = pkt.ptr(); auto namelen = getName().getDataLength(); pkt.skip(namelen); @@ -33,6 +33,7 @@ bool Answer::parse(Packet& pkt) uint16_t rdlength = pkt.read16(); + auto dataPos = pkt.pos; rawData = pkt.ptr(); rawDataLen = rdlength; @@ -43,7 +44,7 @@ bool Answer::parse(Packet& pkt) break; case ResourceType::PTR: // Pointer to a canonical name. - data = Name(response, pkt.data); + data = Name(response, pkt.ptr()); break; case ResourceType::HINFO: // HINFO. host information @@ -64,7 +65,7 @@ bool Answer::parse(Packet& pkt) srv.priority = pkt.read16(); srv.weight = pkt.read16(); srv.port = pkt.read16(); - data = Name(response, pkt.data); + data = Name(response, pkt.ptr()); // char buffer[64]; // sprintf(buffer, "p=%u;w=%u;port=%u;host=", priority, weight, port); // answer->data = buffer; @@ -75,7 +76,7 @@ bool Answer::parse(Packet& pkt) data = makeHexString(static_cast(pkt.ptr()), rdlength, ' '); } - pkt.data = rawData + rawDataLen; + pkt.pos = dataPos + rawDataLen; isValid = true; return true; } diff --git a/Sming/Components/mdns/src/Name.cpp b/Sming/Components/mdns/src/Name.cpp index fdbc28f66a..44f5b2048f 100644 --- a/Sming/Components/mdns/src/Name.cpp +++ b/Sming/Components/mdns/src/Name.cpp @@ -43,13 +43,16 @@ uint16_t Name::getDataLength() const const uint8_t word_len = pkt.read8(); pkt.skip(word_len); if(pkt.peek8() == 0) { - return pkt.data - data; + pkt.read8(); + break; } } else { // Pointer at end - return (pkt.data - data) + sizeof(uint16_t); + pkt.read16(); + break; } } + return pkt.pos; } } // namespace mDNS diff --git a/samples/Basic_Mdns/app/application.cpp b/samples/Basic_Mdns/app/application.cpp index bffc98696f..a4ede91489 100644 --- a/samples/Basic_Mdns/app/application.cpp +++ b/samples/Basic_Mdns/app/application.cpp @@ -1,6 +1,8 @@ #include #include +IMPORT_FSTR(testFile, PROJECT_DIR "/resource/192.168.1.100.mdns") + // If you want, you can define WiFi settings globally in Eclipse Environment Variables #ifndef WIFI_SSID #define WIFI_SSID "PleaseEnterSSID" // Put you SSID and Password here @@ -9,25 +11,28 @@ mDNS::Finder finder; +void printResponse(mDNS::Response& response) +{ + Serial.println(); + debug_i("RESPONSE!!"); + for(auto& answer : response) { + debug_i(">> name: %s", String(answer.getName()).c_str()); + debug_i(" type: %s (0x%04X)", toString(answer.type).c_str(), unsigned(answer.type)); + debug_i(" class: 0x%04x", answer.klass); + debug_i(" ttl: %u", answer.ttl); + debug_i(" flsh?: %u", answer.isCachedFlush); + debug_i(" vald?: %u", answer.isValid); + m_printHex(" data", answer.data.c_str(), answer.data.length()); + m_printHex(" raw ", answer.rawData, answer.rawDataLen); + } +} + void gotIP(IpAddress ip, IpAddress netmask, IpAddress gateway) { Serial.print(F("Connected. Got IP: ")); Serial.println(ip); - finder.onAnswer([](mDNS::Response& response) { - Serial.println(); - debug_i("RESPONSE!!"); - for(auto& answer : response) { - debug_i(">> name: %s", String(answer.getName()).c_str()); - debug_i(" type: %s (0x%04X)", toString(answer.type).c_str(), unsigned(answer.type)); - debug_i(" class: 0x%04x", answer.klass); - debug_i(" ttl: %u", answer.ttl); - debug_i(" flsh?: %u", answer.isCachedFlush); - debug_i(" vald?: %u", answer.isValid); - m_printHex(" data", answer.data.c_str(), answer.data.length()); - m_printHex(" raw ", answer.rawData, answer.rawDataLen); - } - }); + finder.onAnswer(printResponse); // bool ok = finder.search("_googlecast._tcp.local"); mDNS::Query query{}; @@ -49,11 +54,21 @@ void connectFail(const String& ssid, MacAddress bssid, WifiDisconnectReason reas Serial.println(F("I'm NOT CONNECTED!")); } +void test() +{ + String data(testFile); + mDNS::Response response(data.begin(), data.length()); + response.parse(); + printResponse(response); +} + void init() { Serial.begin(SERIAL_BAUD_RATE); Serial.systemDebugOutput(true); // Allow debug print to serial + test(); + // Setup the WIFI connection WifiStation.enable(true); WifiStation.config(WIFI_SSID, WIFI_PWD); // Put you SSID and Password here diff --git a/samples/Basic_Mdns/resource/192.168.1.100.mdns b/samples/Basic_Mdns/resource/192.168.1.100.mdns new file mode 100644 index 0000000000000000000000000000000000000000..7f15eaab348616b1707c6ba0203535ea2b47e6ec GIT binary patch literal 497 zcmZQzXkh>XMj&S9j!)0ePtQqBPAo2Ai7!bmV9m)-PRwCo-~r25FgR)i2LKCV$lose`Cgc+wK{Rh7vfoXbMVb+3oVc3r5ZcG@~$?dxBG6O~Gev5$q-4-%j4dYObl z5{I5onHO30UJ|qGaXMfQ;vcX9 FkpTP|7_k5V literal 0 HcmV?d00001 From 747a3f7d8105503c390dc834267a16af3674944a Mon Sep 17 00:00:00 2001 From: mikee47 Date: Tue, 2 Mar 2021 17:03:33 +0000 Subject: [PATCH 09/52] Do minimum amount of parsing, fetch fields on request --- Sming/Components/mdns/src/Answer.cpp | 153 +++++++++++++----- Sming/Components/mdns/src/Finder.cpp | 5 +- Sming/Components/mdns/src/Response.cpp | 5 +- .../mdns/src/include/Network/Mdns/Answer.h | 117 ++++++++++++-- samples/Basic_Mdns/app/application.cpp | 6 +- 5 files changed, 223 insertions(+), 63 deletions(-) diff --git a/Sming/Components/mdns/src/Answer.cpp b/Sming/Components/mdns/src/Answer.cpp index a783c51682..3432b975a8 100644 --- a/Sming/Components/mdns/src/Answer.cpp +++ b/Sming/Components/mdns/src/Answer.cpp @@ -27,57 +27,128 @@ bool Answer::parse(Packet& pkt) if(pkt.pos > size) { debug_e("[MDNS] Packet overrun, pos = %u, size = %u", pkt.pos, size); // Something has gone wrong receiving or parsing the data. - isValid = false; return false; } - uint16_t rdlength = pkt.read16(); - - auto dataPos = pkt.pos; - rawData = pkt.ptr(); - rawDataLen = rdlength; + recordSize = pkt.read16(); + record = pkt.ptr(); + pkt.pos += recordSize; + return true; +} +String Answer::getRecordString() const +{ switch(type) { - case ResourceType::A: // Returns a 32-bit IPv4 address - a.addr = pkt.read32(); - data = IpAddress(a.addr).toString(); - break; - - case ResourceType::PTR: // Pointer to a canonical name. - data = Name(response, pkt.ptr()); - break; - - case ResourceType::HINFO: // HINFO. host information - data = pkt.readString(rdlength); - break; - - case ResourceType::TXT: // Originally for arbitrary human-readable text in a DNS record. - // We only return the first MAX_MDNS_NAME_LEN bytes of this record type. - data = pkt.readString(rdlength); - break; - - case ResourceType::AAAA: { // Returns a 128-bit IPv6 address. - data = makeHexString(static_cast(pkt.ptr()), rdlength, ':'); - break; + case mDNS::ResourceType::A: + return getA().toString(); + case mDNS::ResourceType::PTR: + return getPTR().toString(); + case mDNS::ResourceType::HINFO: + return getHINFO().toString(); + case mDNS::ResourceType::TXT: + return getTXT().toString(); + case mDNS::ResourceType::AAAA: + return getAAAA().toString(); + case mDNS::ResourceType::SRV: + return getSRV().toString(); + default: + return getRecord().toString(); } +} + +String Answer::Record::toString() const +{ + return makeHexString(answer.record, answer.recordSize, ' '); +} + +IpAddress Answer::A::getAddress() const +{ + return Packet{answer.record}.read32(); +} - case ResourceType::SRV: { // Server Selection. - srv.priority = pkt.read16(); - srv.weight = pkt.read16(); - srv.port = pkt.read16(); - data = Name(response, pkt.ptr()); - // char buffer[64]; - // sprintf(buffer, "p=%u;w=%u;port=%u;host=", priority, weight, port); - // answer->data = buffer; - break; +Name Answer::PTR::getName() const +{ + return Name(answer.response, answer.record); +} + +String Answer::TXT::toString(const char* sep) const +{ + String s; + Packet pkt{answer.record, 0}; + while(pkt.pos < answer.recordSize) { + auto len = pkt.read8(); + if(s) { + s += sep; + } + s += pkt.readString(len); } + return s; +} - default: - data = makeHexString(static_cast(pkt.ptr()), rdlength, ' '); +uint8_t Answer::TXT::count() const +{ + if(mCount == 0) { + Packet pkt{answer.record, 0}; + while(pkt.pos < answer.recordSize) { + auto len = pkt.read8(); + pkt.skip(len); + ++mCount; + } } + return mCount; +} - pkt.pos = dataPos + rawDataLen; - isValid = true; - return true; +String Answer::TXT::operator[](uint8_t index) const +{ + uint8_t len; + auto p = get(index, len); + return p ? String(p, len) : nullptr; } + +const char* Answer::TXT::get(uint8_t index, uint8_t& len) const +{ + Packet pkt{answer.record, 0}; + for(; pkt.pos < answer.recordSize; --index) { + len = pkt.read8(); + if(index == 0) { + return reinterpret_cast(pkt.ptr()); + } + pkt.skip(len); + } + return nullptr; +} + +String Answer::AAAA::toString() const +{ + return makeHexString(answer.record, answer.recordSize, ':'); +} + +uint16_t Answer::SRV::getPriority() const +{ + return Packet{answer.record}.read16(); +} + +uint16_t Answer::SRV::getWeight() const +{ + return Packet{answer.record, 2}.read16(); +} + +uint16_t Answer::SRV::getPort() const +{ + return Packet{answer.record, 4}.read16(); +} + +Name Answer::SRV::getHost() const +{ + return Name(answer.response, answer.record + 6); +} + +String Answer::SRV::toString() const +{ + // char buffer[64]; + // sprintf(buffer, "p=%u;w=%u;port=%u;host=", priority, weight, port); + // answer->data = buffer; + return "todo"; +} + } // namespace mDNS \ No newline at end of file diff --git a/Sming/Components/mdns/src/Finder.cpp b/Sming/Components/mdns/src/Finder.cpp index 17cd4bdc16..56f2c21ab4 100644 --- a/Sming/Components/mdns/src/Finder.cpp +++ b/Sming/Components/mdns/src/Finder.cpp @@ -116,9 +116,8 @@ void Finder::onReceive(pbuf* buf, IpAddress remoteIP, uint16_t remotePort) } Response response(buf->payload, buf->len); - if(response.parse()) { - answerCallback(response); - } + response.parse(); + answerCallback(response); } } // namespace mDNS diff --git a/Sming/Components/mdns/src/Response.cpp b/Sming/Components/mdns/src/Response.cpp index 0ac85a8540..0b4c151a65 100644 --- a/Sming/Components/mdns/src/Response.cpp +++ b/Sming/Components/mdns/src/Response.cpp @@ -46,8 +46,9 @@ bool Response::parse() bool ok{true}; for(uint16_t i = 0; i < (answersCount + nsCount + additionalCount); i++) { auto answer = new Answer(*this); - add(answer); - if(!answer->parse(pkt)) { + if(answer->parse(pkt)) { + add(answer); + } else { ok = false; break; } diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Answer.h b/Sming/Components/mdns/src/include/Network/Mdns/Answer.h index 56006312ae..d2605ee5c1 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Answer.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Answer.h @@ -3,6 +3,7 @@ #include #include "Name.h" #include "ResourceType.h" +#include namespace mDNS { @@ -18,6 +19,69 @@ class Answer : public LinkedObjectTemplate using List = LinkedObjectListTemplate; using OwnedList = OwnedLinkedObjectListTemplate; + struct Record { + Record(const Answer& answer) : answer(answer) + { + } + + String toString() const; + + const Answer& answer; + }; + + // IP4 address + struct A : public Record { + using Record::Record; + IpAddress getAddress() const; + String toString() const + { + return getAddress().toString(); + } + }; + + // Pointer to a canonical name + struct PTR : public Record { + using Record::Record; + Name getName() const; + String toString() const + { + return getName(); + } + }; + + // Host information + struct HINFO : public Record { + using Record::Record; + }; + + // Originally for arbitrary human-readable text in a DNS record + struct TXT : public Record { + using Record::Record; + uint8_t count() const; + String operator[](uint8_t index) const; + String toString(const char* sep = ", ") const; + + private: + const char* get(uint8_t index, uint8_t& len) const; + mutable uint8_t mCount{0}; + }; + + // A 128-bit IPv6 address + struct AAAA : public Record { + using Record::Record; + String toString() const; + }; + + // Server Selection + struct SRV : public Record { + using Record::Record; + uint16_t getPriority() const; + uint16_t getWeight() const; + uint16_t getPort() const; + Name getHost() const; + String toString() const; + }; + Answer(Response& response) : response(response) { } @@ -32,28 +96,53 @@ class Answer : public LinkedObjectTemplate return Name(response, namePtr); } + Record getRecord() const + { + return Record(*this); + } + + String getRecordString() const; + + A getA() const + { + return A(*this); + } + + PTR getPTR() const + { + return PTR(*this); + } + + HINFO getHINFO() const + { + return HINFO(*this); + } + + TXT getTXT() const + { + return TXT(*this); + } + + AAAA getAAAA() const + { + return AAAA(*this); + } + + SRV getSRV() const + { + return SRV(*this); + } + Response& response; uint8_t* namePtr; - uint8_t* rawData; - uint16_t rawDataLen; + uint8_t* record; + uint16_t recordSize; // Name name; ///< object, domain or zone name. String data; ///< The decoded data portion of the resource record. ResourceType type; ///< ResourceRecord Type. uint16_t klass; ///< ResourceRecord Class: Normally the value 1 for Internet (“IN”) uint32_t ttl; ///< ResourceRecord Time To Live: Number of seconds ths should be remembered. bool isCachedFlush; ///< Flush cache of records matching this name. - bool isValid; ///< False if problems were encountered decoding packet. - // Decoded fields dependent upon resource type - union { - struct { - uint32_t addr; - } a; - struct { - uint16_t priority; - uint16_t weight; - uint16_t port; - } srv; - }; }; } // namespace mDNS \ No newline at end of file diff --git a/samples/Basic_Mdns/app/application.cpp b/samples/Basic_Mdns/app/application.cpp index a4ede91489..846b6ddca0 100644 --- a/samples/Basic_Mdns/app/application.cpp +++ b/samples/Basic_Mdns/app/application.cpp @@ -17,13 +17,13 @@ void printResponse(mDNS::Response& response) debug_i("RESPONSE!!"); for(auto& answer : response) { debug_i(">> name: %s", String(answer.getName()).c_str()); + debug_i(" data: %s", answer.getRecordString().c_str()); debug_i(" type: %s (0x%04X)", toString(answer.type).c_str(), unsigned(answer.type)); debug_i(" class: 0x%04x", answer.klass); debug_i(" ttl: %u", answer.ttl); debug_i(" flsh?: %u", answer.isCachedFlush); - debug_i(" vald?: %u", answer.isValid); - m_printHex(" data", answer.data.c_str(), answer.data.length()); - m_printHex(" raw ", answer.rawData, answer.rawDataLen); + // m_printHex(" data", answer.data.c_str(), answer.data.length()); + // m_printHex(" raw ", answer.rawData, answer.rawDataLen); } } From 7db07fe660d760585fa08bef4e9ade140b6a21d3 Mon Sep 17 00:00:00 2001 From: mikee47 Date: Tue, 2 Mar 2021 17:58:35 +0000 Subject: [PATCH 10/52] Implement answer and txt lookup --- Sming/Components/mdns/src/Answer.cpp | 14 ++++++++++++++ Sming/Components/mdns/src/Response.cpp | 10 ++++++++++ .../mdns/src/include/Network/Mdns/Answer.h | 13 +++++++++++++ .../mdns/src/include/Network/Mdns/Response.h | 2 ++ samples/Basic_Mdns/app/application.cpp | 13 +++++++++++++ 5 files changed, 52 insertions(+) diff --git a/Sming/Components/mdns/src/Answer.cpp b/Sming/Components/mdns/src/Answer.cpp index 3432b975a8..487b689df7 100644 --- a/Sming/Components/mdns/src/Answer.cpp +++ b/Sming/Components/mdns/src/Answer.cpp @@ -105,6 +105,20 @@ String Answer::TXT::operator[](uint8_t index) const return p ? String(p, len) : nullptr; } +String Answer::TXT::getValue(const char* name, uint16_t namelen) const +{ + Packet pkt{answer.record, 0}; + while(pkt.pos < answer.recordSize) { + auto len = pkt.read8(); + auto entry = reinterpret_cast(pkt.ptr()); + if(len > namelen && entry[namelen] == '=' && memicmp(entry, name, namelen) == 0) { + return String(entry + namelen + 1, len - namelen - 1); + } + pkt.skip(len); + } + return nullptr; +} + const char* Answer::TXT::get(uint8_t index, uint8_t& len) const { Packet pkt{answer.record, 0}; diff --git a/Sming/Components/mdns/src/Response.cpp b/Sming/Components/mdns/src/Response.cpp index 0b4c151a65..3ed8408c19 100644 --- a/Sming/Components/mdns/src/Response.cpp +++ b/Sming/Components/mdns/src/Response.cpp @@ -57,4 +57,14 @@ bool Response::parse() return ok; } +Answer* Response::operator[](ResourceType type) +{ + for(auto& ans: *this) { + if(ans.type == type) { + return &ans; + } + } + return nullptr; +} + } // namespace mDNS diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Answer.h b/Sming/Components/mdns/src/include/Network/Mdns/Answer.h index d2605ee5c1..8314c759c0 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Answer.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Answer.h @@ -59,7 +59,20 @@ class Answer : public LinkedObjectTemplate using Record::Record; uint8_t count() const; String operator[](uint8_t index) const; + String operator[](const char* name) const + { + return getValue(name); + } String toString(const char* sep = ", ") const; + String getValue(const char* name, uint16_t namelen) const; + String getValue(const char* name) const + { + return getValue(name, strlen(name)); + } + String getValue(const String& name) const + { + return getValue(name.c_str(), name.length()); + } private: const char* get(uint8_t index, uint8_t& len) const; diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Response.h b/Sming/Components/mdns/src/include/Network/Mdns/Response.h index 10dcc60b6d..0758eba32e 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Response.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Response.h @@ -41,6 +41,8 @@ class Response : public Answer::OwnedList return size; } + Answer* operator[](ResourceType type); + private: uint8_t* data; uint16_t size; diff --git a/samples/Basic_Mdns/app/application.cpp b/samples/Basic_Mdns/app/application.cpp index 846b6ddca0..e9a6d7e9a1 100644 --- a/samples/Basic_Mdns/app/application.cpp +++ b/samples/Basic_Mdns/app/application.cpp @@ -25,6 +25,19 @@ void printResponse(mDNS::Response& response) // m_printHex(" data", answer.data.c_str(), answer.data.length()); // m_printHex(" raw ", answer.rawData, answer.rawDataLen); } + + auto answer = response[mDNS::ResourceType::TXT]; + if(answer != nullptr) { + auto txt = answer->getTXT(); + auto s = txt["md"]; + debug_i("md = '%s'", s.c_str()); + } + + answer = response[mDNS::ResourceType::A]; + if(answer != nullptr) { + auto a = answer->getA(); + debug_i("addr = %s", a.toString().c_str()); + } } void gotIP(IpAddress ip, IpAddress netmask, IpAddress gateway) From b848a983ab60534158c9bd812d530f6077830d39 Mon Sep 17 00:00:00 2001 From: mikee47 Date: Tue, 2 Mar 2021 22:06:46 +0000 Subject: [PATCH 11/52] Refactor into `Resource` namespace --- Sming/Components/mdns/src/Answer.cpp | 178 ++++-------------- Sming/Components/mdns/src/Finder.cpp | 15 +- Sming/Components/mdns/src/Name.cpp | 7 +- Sming/Components/mdns/src/Resource.cpp | 160 ++++++++++++++++ Sming/Components/mdns/src/ResourceType.cpp | 14 -- Sming/Components/mdns/src/Response.cpp | 26 +-- .../mdns/src/include/Network/Mdns/Answer.h | 133 +++---------- .../mdns/src/include/Network/Mdns/Finder.h | 17 +- .../mdns/src/include/Network/Mdns/Query.h | 2 +- .../mdns/src/include/Network/Mdns/Resource.h | 153 +++++++++++++++ .../src/include/Network/Mdns/ResourceType.h | 28 --- samples/Basic_Mdns/app/application.cpp | 23 ++- 12 files changed, 427 insertions(+), 329 deletions(-) create mode 100644 Sming/Components/mdns/src/Resource.cpp delete mode 100644 Sming/Components/mdns/src/ResourceType.cpp create mode 100644 Sming/Components/mdns/src/include/Network/Mdns/Resource.h delete mode 100644 Sming/Components/mdns/src/include/Network/Mdns/ResourceType.h diff --git a/Sming/Components/mdns/src/Answer.cpp b/Sming/Components/mdns/src/Answer.cpp index 487b689df7..1962a0b2f5 100644 --- a/Sming/Components/mdns/src/Answer.cpp +++ b/Sming/Components/mdns/src/Answer.cpp @@ -1,168 +1,70 @@ #include "include/Network/Mdns/Answer.h" #include "include/Network/Mdns/Response.h" #include "Packet.h" -#include -#include #include namespace mDNS { -bool Answer::parse(Packet& pkt) +ResourceType Answer::getType() const { - auto size = response.getSize(); - - namePtr = pkt.ptr(); - auto namelen = getName().getDataLength(); - pkt.skip(namelen); - - type = ResourceType(pkt.read16()); - - uint8_t rrclass_0 = pkt.read8(); - uint8_t rrclass_1 = pkt.read8(); - isCachedFlush = (0b10000000 & rrclass_0); - klass = ((rrclass_0 & 0b01111111) << 8) + rrclass_1; - - ttl = pkt.read32(); - - if(pkt.pos > size) { - debug_e("[MDNS] Packet overrun, pos = %u, size = %u", pkt.pos, size); - // Something has gone wrong receiving or parsing the data. - return false; - } - - recordSize = pkt.read16(); - record = pkt.ptr(); - pkt.pos += recordSize; - return true; + return ResourceType(Packet{namePtr, nameLen}.read16()); } -String Answer::getRecordString() const +uint16_t Answer::getClass() const { - switch(type) { - case mDNS::ResourceType::A: - return getA().toString(); - case mDNS::ResourceType::PTR: - return getPTR().toString(); - case mDNS::ResourceType::HINFO: - return getHINFO().toString(); - case mDNS::ResourceType::TXT: - return getTXT().toString(); - case mDNS::ResourceType::AAAA: - return getAAAA().toString(); - case mDNS::ResourceType::SRV: - return getSRV().toString(); - default: - return getRecord().toString(); - } + auto rrclass = Packet{namePtr, uint16_t(nameLen + 2)}.read16(); + return rrclass & 0x7fff; } -String Answer::Record::toString() const +bool Answer::isCachedFlush() const { - return makeHexString(answer.record, answer.recordSize, ' '); + auto rrclass = Packet{namePtr, uint16_t(nameLen + 2)}.read16(); + return rrclass & 0x8000; } -IpAddress Answer::A::getAddress() const +uint32_t Answer::getTtl() const { - return Packet{answer.record}.read32(); + return Packet{namePtr, uint16_t(nameLen + 4)}.read32(); } -Name Answer::PTR::getName() const +bool Answer::parse(Packet& pkt) { - return Name(answer.response, answer.record); -} + auto size = response.getSize(); -String Answer::TXT::toString(const char* sep) const -{ - String s; - Packet pkt{answer.record, 0}; - while(pkt.pos < answer.recordSize) { - auto len = pkt.read8(); - if(s) { - s += sep; - } - s += pkt.readString(len); - } - return s; -} + namePtr = pkt.ptr(); + nameLen = getName().getDataLength(); + pkt.skip(nameLen + 8); -uint8_t Answer::TXT::count() const -{ - if(mCount == 0) { - Packet pkt{answer.record, 0}; - while(pkt.pos < answer.recordSize) { - auto len = pkt.read8(); - pkt.skip(len); - ++mCount; - } + if(pkt.pos > size) { + debug_e("[MDNS] Packet overrun, pos = %u, size = %u", pkt.pos, size); + // Something has gone wrong receiving or parsing the data. + return false; } - return mCount; -} - -String Answer::TXT::operator[](uint8_t index) const -{ - uint8_t len; - auto p = get(index, len); - return p ? String(p, len) : nullptr; -} -String Answer::TXT::getValue(const char* name, uint16_t namelen) const -{ - Packet pkt{answer.record, 0}; - while(pkt.pos < answer.recordSize) { - auto len = pkt.read8(); - auto entry = reinterpret_cast(pkt.ptr()); - if(len > namelen && entry[namelen] == '=' && memicmp(entry, name, namelen) == 0) { - return String(entry + namelen + 1, len - namelen - 1); - } - pkt.skip(len); - } - return nullptr; + recordSize = pkt.read16(); + pkt.pos += recordSize; + return true; } -const char* Answer::TXT::get(uint8_t index, uint8_t& len) const +String Answer::getRecordString() const { - Packet pkt{answer.record, 0}; - for(; pkt.pos < answer.recordSize; --index) { - len = pkt.read8(); - if(index == 0) { - return reinterpret_cast(pkt.ptr()); - } - pkt.skip(len); + using namespace Resource; + switch(getType()) { + case Type::A: + return A(*this).toString(); + case Type::PTR: + return PTR(*this).toString(); + case Type::HINFO: + return HINFO(*this).toString(); + case Type::TXT: + return TXT(*this).toString(); + case Type::AAAA: + return AAAA(*this).toString(); + case Type::SRV: + return SRV(*this).toString(); + default: + return Record(*this).toString(); } - return nullptr; -} - -String Answer::AAAA::toString() const -{ - return makeHexString(answer.record, answer.recordSize, ':'); -} - -uint16_t Answer::SRV::getPriority() const -{ - return Packet{answer.record}.read16(); -} - -uint16_t Answer::SRV::getWeight() const -{ - return Packet{answer.record, 2}.read16(); -} - -uint16_t Answer::SRV::getPort() const -{ - return Packet{answer.record, 4}.read16(); -} - -Name Answer::SRV::getHost() const -{ - return Name(answer.response, answer.record + 6); -} - -String Answer::SRV::toString() const -{ - // char buffer[64]; - // sprintf(buffer, "p=%u;w=%u;port=%u;host=", priority, weight, port); - // answer->data = buffer; - return "todo"; } -} // namespace mDNS \ No newline at end of file +} // namespace mDNS diff --git a/Sming/Components/mdns/src/Finder.cpp b/Sming/Components/mdns/src/Finder.cpp index 56f2c21ab4..146a4edef9 100644 --- a/Sming/Components/mdns/src/Finder.cpp +++ b/Sming/Components/mdns/src/Finder.cpp @@ -3,6 +3,13 @@ #include "Packet.h" #include +#define MDNS_IP 224, 0, 0, 251 +#define MDNS_TARGET_PORT 5353 +#define MDNS_SOURCE_PORT 5353 +#define MDNS_TTL 255 + +#define MAX_PACKET_SIZE 1024 + namespace mDNS { Finder::~Finder() @@ -30,9 +37,8 @@ bool Finder::search(const Query& query) return false; } - uint8_t buffer[MAX_PACKET_SIZE]{}; - - Packet pkt{buffer, 0}; + uint8_t buffer[MAX_PACKET_SIZE]; + Packet pkt{buffer}; // The first two bytes are the transaction id and they are not used in MDNS pkt.write16(0); @@ -53,9 +59,6 @@ bool Finder::search(const Query& query) // 2 bytes for Additional PRs pkt.write16(0); - uint16_t word_start{0}; - uint16_t word_end{0}; - size_t pos{0}; auto& name = query.name; auto namelen = name.length(); diff --git a/Sming/Components/mdns/src/Name.cpp b/Sming/Components/mdns/src/Name.cpp index 44f5b2048f..d774066898 100644 --- a/Sming/Components/mdns/src/Name.cpp +++ b/Sming/Components/mdns/src/Name.cpp @@ -7,7 +7,7 @@ namespace mDNS String Name::toString() const { String s; - Packet pkt{data, 0}; + Packet pkt{data}; while(true) { if(pkt.peek8() < 0xC0) { @@ -29,14 +29,13 @@ String Name::toString() const // Message Compression used. Next 2 bytes are a pointer to the actual name section. uint16_t pointer = pkt.read16() & 0x3fff; - pkt.data = response.resolvePointer(pointer); - pkt.pos = 0; + pkt = Packet{response.resolvePointer(pointer)}; } } uint16_t Name::getDataLength() const { - Packet pkt{data, 0}; + Packet pkt{data}; while(true) { if(pkt.peek8() < 0xC0) { diff --git a/Sming/Components/mdns/src/Resource.cpp b/Sming/Components/mdns/src/Resource.cpp new file mode 100644 index 0000000000..e27df1fc26 --- /dev/null +++ b/Sming/Components/mdns/src/Resource.cpp @@ -0,0 +1,160 @@ +#include "include/Network/Mdns/Resource.h" +#include "include/Network/Mdns/Answer.h" +#include "Packet.h" +#include + +String toString(mDNS::ResourceType type) +{ + switch(type) { +#define XX(name, value, desc) \ + case mDNS::ResourceType::name: \ + return F(#name); + MDNS_RESOURCE_TYPE_MAP(XX) +#undef XX + default: + return String(unsigned(type)); + } +} + +namespace mDNS +{ +namespace Resource +{ +uint8_t* Record::recordPtr() const +{ + return answer.getRecordPtr(); +} + +uint16_t Record::recordSize() const +{ + return answer.getRecordSize(); +} + +String Record::toString() const +{ + return makeHexString(recordPtr(), recordSize(), ' '); +} + +IpAddress A::getAddress() const +{ + // Keep bytes in network order + uint32_t addr; + memcpy(&addr, recordPtr(), sizeof(addr)); + return addr; +} + +Name PTR::getName() const +{ + return Name(answer.getResponse(), recordPtr()); +} + +String TXT::toString(const String& sep) const +{ + String s; + Packet pkt{recordPtr()}; + auto size = recordSize(); + while(pkt.pos < size) { + auto len = pkt.read8(); + if(s) { + s += sep; + } + s += pkt.readString(len); + } + return s; +} + +uint8_t TXT::count() const +{ + if(mCount == 0) { + Packet pkt{recordPtr()}; + auto size = recordSize(); + while(pkt.pos < size) { + auto len = pkt.read8(); + pkt.skip(len); + ++mCount; + } + } + return mCount; +} + +String TXT::operator[](uint8_t index) const +{ + uint8_t len; + auto p = get(index, len); + return p ? String(p, len) : nullptr; +} + +String TXT::getValue(const char* name, uint16_t namelen) const +{ + Packet pkt{recordPtr()}; + auto size = recordSize(); + while(pkt.pos < size) { + auto len = pkt.read8(); + auto entry = reinterpret_cast(pkt.ptr()); + if(len > namelen && entry[namelen] == '=' && memicmp(entry, name, namelen) == 0) { + return String(entry + namelen + 1, len - namelen - 1); + } + pkt.skip(len); + } + return nullptr; +} + +const char* TXT::get(uint8_t index, uint8_t& len) const +{ + Packet pkt{recordPtr()}; + auto size = recordSize(); + for(; pkt.pos < size; --index) { + len = pkt.read8(); + if(index == 0) { + return reinterpret_cast(pkt.ptr()); + } + pkt.skip(len); + } + return nullptr; +} + +String AAAA::toString() const +{ + return makeHexString(recordPtr(), recordSize(), ':'); +} + +uint16_t SRV::getPriority() const +{ + return Packet{recordPtr()}.read16(); +} + +uint16_t SRV::getWeight() const +{ + return Packet{recordPtr(), 2}.read16(); +} + +uint16_t SRV::getPort() const +{ + return Packet{recordPtr(), 4}.read16(); +} + +Name SRV::getHost() const +{ + return Name(answer.getResponse(), recordPtr() + 6); +} + +String SRV::toString(const String& sep) const +{ + String s; + s.reserve(32); + s += "p="; + s += getPriority(); + s += sep; + s += "w="; + s += getWeight(); + s += sep; + s += F("port="); + s += getPort(); + s += sep; + s += F("host="); + s += getHost(); + return s; +} + +} // namespace Resource +} // namespace mDNS diff --git a/Sming/Components/mdns/src/ResourceType.cpp b/Sming/Components/mdns/src/ResourceType.cpp deleted file mode 100644 index 075f1ded1b..0000000000 --- a/Sming/Components/mdns/src/ResourceType.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include "include/Network/Mdns/ResourceType.h" - -String toString(mDNS::ResourceType type) -{ - switch(type) { -#define XX(name, value, desc) \ - case mDNS::ResourceType::name: \ - return F(#name); - MDNS_RESOURCE_TYPE_MAP(XX) -#undef XX - default: - return String(unsigned(type)); - } -} diff --git a/Sming/Components/mdns/src/Response.cpp b/Sming/Components/mdns/src/Response.cpp index 3ed8408c19..38c07fc00c 100644 --- a/Sming/Components/mdns/src/Response.cpp +++ b/Sming/Components/mdns/src/Response.cpp @@ -27,31 +27,31 @@ bool Response::parse() Packet pkt{data, 4}; // Number of incoming queries. - uint16_t questionsCount = pkt.read16(); - if(questionsCount > 0) { + auto questionsCount = pkt.read16(); + if(questionsCount != 0) { // we are interested only in responses. return false; } // Number of incoming answers. - uint16_t answersCount = pkt.read16(); + auto answersCount = pkt.read16(); // Number of incoming Name Server resource records. - uint16_t nsCount = pkt.read16(); + auto nsCount = pkt.read16(); // Number of incoming Additional resource records. - uint16_t additionalCount = pkt.read16(); + auto additionalCount = pkt.read16(); // List of answers bool ok{true}; - for(uint16_t i = 0; i < (answersCount + nsCount + additionalCount); i++) { + auto recordCount = answersCount + nsCount + additionalCount; + for(uint16_t i = 0; i < recordCount; i++) { auto answer = new Answer(*this); - if(answer->parse(pkt)) { - add(answer); - } else { - ok = false; - break; + if(!answer->parse(pkt)) { + delete answer; + return false; } + add(answer); } return ok; @@ -59,8 +59,8 @@ bool Response::parse() Answer* Response::operator[](ResourceType type) { - for(auto& ans: *this) { - if(ans.type == type) { + for(auto& ans : *this) { + if(ans.getType() == type) { return &ans; } } diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Answer.h b/Sming/Components/mdns/src/include/Network/Mdns/Answer.h index 8314c759c0..4582d398b6 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Answer.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Answer.h @@ -2,8 +2,7 @@ #include #include "Name.h" -#include "ResourceType.h" -#include +#include "Resource.h" namespace mDNS { @@ -19,82 +18,6 @@ class Answer : public LinkedObjectTemplate using List = LinkedObjectListTemplate; using OwnedList = OwnedLinkedObjectListTemplate; - struct Record { - Record(const Answer& answer) : answer(answer) - { - } - - String toString() const; - - const Answer& answer; - }; - - // IP4 address - struct A : public Record { - using Record::Record; - IpAddress getAddress() const; - String toString() const - { - return getAddress().toString(); - } - }; - - // Pointer to a canonical name - struct PTR : public Record { - using Record::Record; - Name getName() const; - String toString() const - { - return getName(); - } - }; - - // Host information - struct HINFO : public Record { - using Record::Record; - }; - - // Originally for arbitrary human-readable text in a DNS record - struct TXT : public Record { - using Record::Record; - uint8_t count() const; - String operator[](uint8_t index) const; - String operator[](const char* name) const - { - return getValue(name); - } - String toString(const char* sep = ", ") const; - String getValue(const char* name, uint16_t namelen) const; - String getValue(const char* name) const - { - return getValue(name, strlen(name)); - } - String getValue(const String& name) const - { - return getValue(name.c_str(), name.length()); - } - - private: - const char* get(uint8_t index, uint8_t& len) const; - mutable uint8_t mCount{0}; - }; - - // A 128-bit IPv6 address - struct AAAA : public Record { - using Record::Record; - String toString() const; - }; - - // Server Selection - struct SRV : public Record { - using Record::Record; - uint16_t getPriority() const; - uint16_t getWeight() const; - uint16_t getPort() const; - Name getHost() const; - String toString() const; - }; - Answer(Response& response) : response(response) { } @@ -109,53 +32,45 @@ class Answer : public LinkedObjectTemplate return Name(response, namePtr); } - Record getRecord() const - { - return Record(*this); - } + Resource::Type getType() const; - String getRecordString() const; + /** + * @brief ResourceRecord Class: Normally the value 1 for Internet (“IN”) + */ + uint16_t getClass() const; - A getA() const - { - return A(*this); - } + /** + * @brief Flush cache of records matching this name + */ + bool isCachedFlush() const; - PTR getPTR() const - { - return PTR(*this); - } + /** + * @brief ResourceRecord Time To Live: Number of seconds ths should be remembered + */ + uint32_t getTtl() const; - HINFO getHINFO() const - { - return HINFO(*this); - } + String getRecordString() const; - TXT getTXT() const + Response& getResponse() const { - return TXT(*this); + return response; } - AAAA getAAAA() const + uint8_t* getRecordPtr() const { - return AAAA(*this); + return namePtr + nameLen + 10; } - SRV getSRV() const + uint16_t getRecordSize() const { - return SRV(*this); + return recordSize; } +private: Response& response; uint8_t* namePtr; - uint8_t* record; uint16_t recordSize; - // Name name; ///< object, domain or zone name. - String data; ///< The decoded data portion of the resource record. - ResourceType type; ///< ResourceRecord Type. - uint16_t klass; ///< ResourceRecord Class: Normally the value 1 for Internet (“IN”) - uint32_t ttl; ///< ResourceRecord Time To Live: Number of seconds ths should be remembered. - bool isCachedFlush; ///< Flush cache of records matching this name. + uint16_t nameLen; }; -} // namespace mDNS \ No newline at end of file +} // namespace mDNS diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Finder.h b/Sming/Components/mdns/src/include/Network/Mdns/Finder.h index 178e2938ea..af30042aab 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Finder.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Finder.h @@ -3,6 +3,14 @@ * from MrDunk's mDNS code these files are distributed under the same license as his project. * * MIT license: https://github.com/mrdunk/esp8266_mdns/blob/master/LICENCE.txt + * + * References: + * + * Zero-configuration networking (DNS-SD) https://en.wikipedia.org/wiki/Zero-configuration_networking + * Multicast DNS https://tools.ietf.org/html/rfc6762 + * DNS-Based Service Discovery https://tools.ietf.org/html/rfc6763 + * DNS record types https://en.wikipedia.org/wiki/List_of_DNS_record_types + * */ #pragma once @@ -13,15 +21,8 @@ namespace mDNS { -#define MDNS_IP 224, 0, 0, 251 -#define MDNS_TARGET_PORT 5353 -#define MDNS_SOURCE_PORT 5353 -#define MDNS_TTL 255 - -#define MAX_PACKET_SIZE 1024 - // The mDNS spec says this should never be more than 256 (including trailing '\0'). -#define MAX_MDNS_NAME_LEN 256 +static constexpr size_t MAX_MDNS_NAME_LEN{256}; class Finder : protected UdpConnection { diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Query.h b/Sming/Components/mdns/src/include/Network/Mdns/Query.h index 73a876b78e..4550d3aec7 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Query.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Query.h @@ -1,6 +1,6 @@ #pragma once -#include "ResourceType.h" +#include "Resource.h" namespace mDNS { diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Resource.h b/Sming/Components/mdns/src/include/Network/Mdns/Resource.h new file mode 100644 index 0000000000..c33898ddeb --- /dev/null +++ b/Sming/Components/mdns/src/include/Network/Mdns/Resource.h @@ -0,0 +1,153 @@ +#pragma once + +#include "Name.h" +#include + +/** + * @brief MDNS resource type identifiers + * + * (name, value, description) + */ +#define MDNS_RESOURCE_TYPE_MAP(XX) \ + XX(A, 0x0001, "32-bit IPv4 address") \ + XX(PTR, 0x000C, "Pointer to a canonical name") \ + XX(HINFO, 0x000D, "Host Information") \ + XX(TXT, 0x0010, "Arbitrary human-readable text") \ + XX(AAAA, 0x001C, "128-bit IPv6 address") \ + XX(SRV, 0x0021, "Server selection") + +namespace mDNS +{ +class Answer; + +namespace Resource +{ +enum class Type : uint16_t { +#define XX(name, value, desc) name = value, + MDNS_RESOURCE_TYPE_MAP(XX) +#undef XX +}; + +class Record +{ +public: + Record(const Answer& answer) : answer(answer) + { + } + + String toString() const; + +protected: + uint8_t* recordPtr() const; + uint16_t recordSize() const; + + const Answer& answer; +}; + +// IP4 address +class A : public Record +{ +public: + using Record::Record; + + IpAddress getAddress() const; + + String toString() const + { + return getAddress().toString(); + } +}; + +// Pointer to a canonical name +class PTR : public Record +{ +public: + using Record::Record; + + Name getName() const; + + String toString() const + { + return getName(); + } +}; + +// Host information +class HINFO : public Record +{ +public: + using Record::Record; +}; + +// Originally for arbitrary human-readable text in a DNS record +class TXT : public Record +{ +public: + using Record::Record; + + uint8_t count() const; + + String operator[](uint8_t index) const; + + String operator[](const char* name) const + { + return getValue(name); + } + + String operator[](const String& name) const + { + return getValue(name.c_str()); + } + + String toString(const String& sep = "; ") const; + + String getValue(const char* name, uint16_t namelen) const; + + String getValue(const char* name) const + { + return getValue(name, strlen(name)); + } + + String getValue(const String& name) const + { + return getValue(name.c_str(), name.length()); + } + +private: + const char* get(uint8_t index, uint8_t& len) const; + mutable uint8_t mCount{0}; +}; + +// A 128-bit IPv6 address +class AAAA : public Record +{ +public: + using Record::Record; + + String toString() const; +}; + +// Server Selection +class SRV : public Record +{ +public: + using Record::Record; + + uint16_t getPriority() const; + + uint16_t getWeight() const; + + uint16_t getPort() const; + + Name getHost() const; + + String toString(const String& sep = "; ") const; +}; + +} // namespace Resource + +using ResourceType = Resource::Type; + +} // namespace mDNS + +String toString(mDNS::ResourceType type); diff --git a/Sming/Components/mdns/src/include/Network/Mdns/ResourceType.h b/Sming/Components/mdns/src/include/Network/Mdns/ResourceType.h deleted file mode 100644 index 890e3ad9dd..0000000000 --- a/Sming/Components/mdns/src/include/Network/Mdns/ResourceType.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include - -/** - * @brief MDNS resource type identifiers - * - * (name, value, description) - */ -#define MDNS_RESOURCE_TYPE_MAP(XX) \ - XX(A, 0x0001, "32-bit IPv4 address") \ - XX(PTR, 0x000C, "Pointer to a canonical name") \ - XX(HINFO, 0x000D, "Host Information") \ - XX(TXT, 0x0010, "Arbitrary human-readable text") \ - XX(AAAA, 0x001C, "128-bit IPv6 address") \ - XX(SRV, 0x0021, "Server selection") - -namespace mDNS -{ -enum class ResourceType : uint16_t { -#define XX(name, value, desc) name = value, - MDNS_RESOURCE_TYPE_MAP(XX) -#undef XX -}; - -} // namespace mDNS - -String toString(mDNS::ResourceType type); diff --git a/samples/Basic_Mdns/app/application.cpp b/samples/Basic_Mdns/app/application.cpp index e9a6d7e9a1..987883a120 100644 --- a/samples/Basic_Mdns/app/application.cpp +++ b/samples/Basic_Mdns/app/application.cpp @@ -18,24 +18,22 @@ void printResponse(mDNS::Response& response) for(auto& answer : response) { debug_i(">> name: %s", String(answer.getName()).c_str()); debug_i(" data: %s", answer.getRecordString().c_str()); - debug_i(" type: %s (0x%04X)", toString(answer.type).c_str(), unsigned(answer.type)); - debug_i(" class: 0x%04x", answer.klass); - debug_i(" ttl: %u", answer.ttl); - debug_i(" flsh?: %u", answer.isCachedFlush); - // m_printHex(" data", answer.data.c_str(), answer.data.length()); - // m_printHex(" raw ", answer.rawData, answer.rawDataLen); + debug_i(" type: %s (0x%04X)", toString(answer.getType()).c_str(), unsigned(answer.getType())); + debug_i(" class: 0x%04x", answer.getClass()); + debug_i(" ttl: %u", answer.getTtl()); + debug_i(" flush: %u", answer.isCachedFlush()); } auto answer = response[mDNS::ResourceType::TXT]; if(answer != nullptr) { - auto txt = answer->getTXT(); + mDNS::Resource::TXT txt(*answer); auto s = txt["md"]; debug_i("md = '%s'", s.c_str()); } answer = response[mDNS::ResourceType::A]; if(answer != nullptr) { - auto a = answer->getA(); + mDNS::Resource::A a(*answer); debug_i("addr = %s", a.toString().c_str()); } } @@ -69,10 +67,19 @@ void connectFail(const String& ssid, MacAddress bssid, WifiDisconnectReason reas void test() { + debug_i("sizeof(mDNS::Finder) = %u", sizeof(mDNS::Finder)); + debug_i("sizeof(mDNS::Response) = %u", sizeof(mDNS::Response)); + debug_i("sizeof(mDNS::Answer) = %u", sizeof(mDNS::Answer)); + debug_i("sizeof(LinkedObject) = %u", sizeof(LinkedObject)); + + Serial.println(_F("** Parsing test packet **")); String data(testFile); mDNS::Response response(data.begin(), data.length()); response.parse(); printResponse(response); + Serial.println(_F("** End of test packet **")); + Serial.println(); + Serial.println(); } void init() From f8b8d9d6d658df24c7f4ea4b02878c300feba188 Mon Sep 17 00:00:00 2001 From: mikee47 Date: Tue, 2 Mar 2021 23:23:53 +0000 Subject: [PATCH 12/52] Comments, tidy --- Sming/Components/mdns/{ => src}/.cs | 0 Sming/Components/mdns/src/Finder.cpp | 26 +++------- Sming/Components/mdns/src/Resource.cpp | 12 +++++ .../mdns/src/include/Network/Mdns/Answer.h | 12 +++++ .../mdns/src/include/Network/Mdns/Finder.h | 29 +++++++++-- .../mdns/src/include/Network/Mdns/Query.h | 9 ++-- .../mdns/src/include/Network/Mdns/Resource.h | 30 +++++++++--- .../mdns/src/include/Network/Mdns/Response.h | 49 ++++++++++++++++--- samples/Basic_Mdns/app/application.cpp | 21 +++----- 9 files changed, 133 insertions(+), 55 deletions(-) rename Sming/Components/mdns/{ => src}/.cs (100%) diff --git a/Sming/Components/mdns/.cs b/Sming/Components/mdns/src/.cs similarity index 100% rename from Sming/Components/mdns/.cs rename to Sming/Components/mdns/src/.cs diff --git a/Sming/Components/mdns/src/Finder.cpp b/Sming/Components/mdns/src/Finder.cpp index 146a4edef9..0eb317a7fe 100644 --- a/Sming/Components/mdns/src/Finder.cpp +++ b/Sming/Components/mdns/src/Finder.cpp @@ -21,13 +21,7 @@ Finder::~Finder() bool Finder::search(const String& hostname, ResourceType type) { - Query query{}; - query.name = hostname; - query.type = type; - query.klass = 1; // "INternet" - query.isUnicastResponse = false; - query.isValid = true; - + Query query{hostname, type}; return search(query); } @@ -78,7 +72,7 @@ bool Finder::search(const Query& query) // 2 bytes for class unsigned int qclass = 0; if(query.isUnicastResponse) { - qclass = 0b1000000000000000; + qclass = 0x8000; } qclass += query.klass; pkt.write16(qclass); @@ -105,22 +99,14 @@ void Finder::UdpOut::onReceive(pbuf* buf, IpAddress remoteIP, uint16_t remotePor void Finder::onReceive(pbuf* buf, IpAddress remoteIP, uint16_t remotePort) { - // auto& hostfs = IFS::Host::getFileSystem(); - // String filename = remoteIP.toString(); - // filename += ".mdns"; - // auto f = hostfs.open(filename, IFS::OpenFlag::Create | IFS::OpenFlag::Truncate | IFS::OpenFlag::Write); - // hostfs.write(f, buf->payload, buf->len); - // hostfs.close(f); - // debug_i("Saved '%s'", filename.c_str()); - // return; - if(!answerCallback) { return; } - Response response(buf->payload, buf->len); - response.parse(); - answerCallback(response); + Response response(remoteIP, remotePort, buf->payload, buf->len); + if(response.parse()) { + answerCallback(response); + } } } // namespace mDNS diff --git a/Sming/Components/mdns/src/Resource.cpp b/Sming/Components/mdns/src/Resource.cpp index e27df1fc26..c49972ca20 100644 --- a/Sming/Components/mdns/src/Resource.cpp +++ b/Sming/Components/mdns/src/Resource.cpp @@ -20,6 +20,8 @@ namespace mDNS { namespace Resource { +/* Record */ + uint8_t* Record::recordPtr() const { return answer.getRecordPtr(); @@ -35,6 +37,8 @@ String Record::toString() const return makeHexString(recordPtr(), recordSize(), ' '); } +/* A */ + IpAddress A::getAddress() const { // Keep bytes in network order @@ -43,11 +47,15 @@ IpAddress A::getAddress() const return addr; } +/* PTR */ + Name PTR::getName() const { return Name(answer.getResponse(), recordPtr()); } +/* TXT */ + String TXT::toString(const String& sep) const { String s; @@ -113,11 +121,15 @@ const char* TXT::get(uint8_t index, uint8_t& len) const return nullptr; } +/* AAAA */ + String AAAA::toString() const { return makeHexString(recordPtr(), recordSize(), ':'); } +/* SRV */ + uint16_t SRV::getPriority() const { return Packet{recordPtr()}.read16(); diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Answer.h b/Sming/Components/mdns/src/include/Network/Mdns/Answer.h index 4582d398b6..086d48e4df 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Answer.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Answer.h @@ -32,6 +32,9 @@ class Answer : public LinkedObjectTemplate return Name(response, namePtr); } + /** + * @brief ResourceRecord type + */ Resource::Type getType() const; /** @@ -49,6 +52,9 @@ class Answer : public LinkedObjectTemplate */ uint32_t getTtl() const; + /** + * @brief Get content of record as string + */ String getRecordString() const; Response& getResponse() const @@ -56,11 +62,17 @@ class Answer : public LinkedObjectTemplate return response; } + /** + * @brief Get pointer to Resource Record data + */ uint8_t* getRecordPtr() const { return namePtr + nameLen + 10; } + /** + * @brief Get size of Resource Record + */ uint16_t getRecordSize() const { return recordSize; diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Finder.h b/Sming/Components/mdns/src/include/Network/Mdns/Finder.h index af30042aab..0d5b609eb4 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Finder.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Finder.h @@ -3,9 +3,9 @@ * from MrDunk's mDNS code these files are distributed under the same license as his project. * * MIT license: https://github.com/mrdunk/esp8266_mdns/blob/master/LICENCE.txt - * + * * References: - * + * * Zero-configuration networking (DNS-SD) https://en.wikipedia.org/wiki/Zero-configuration_networking * Multicast DNS https://tools.ietf.org/html/rfc6762 * DNS-Based Service Discovery https://tools.ietf.org/html/rfc6763 @@ -24,9 +24,15 @@ namespace mDNS // The mDNS spec says this should never be more than 256 (including trailing '\0'). static constexpr size_t MAX_MDNS_NAME_LEN{256}; +/** + * @brief Locates mDNS services by issuing queries + */ class Finder : protected UdpConnection { public: + /** + * @brief Callback to be invoked for each received response. + */ using AnswerDelegate = Delegate; Finder() : out(*this) @@ -35,13 +41,30 @@ class Finder : protected UdpConnection ~Finder(); + /** + * @brief Set callback to be invoked for each received response + * + * An mDNS-SD (Multicast-DNS Service Discovery) response contains related answer records. + * The full set of answer records is passed to the callback. + */ void onAnswer(AnswerDelegate callback) { answerCallback = callback; } - bool search(const String& hostname, ResourceType type = ResourceType::SRV); + /** + * @brief Send a multicast query request + * @param hostname Name to find, e.g. "_googlecast._tcp.local" + * @param type + * @retval bool false if parameters failed validation or UDP request could not be sent + */ + bool search(const String& hostname, ResourceType type = ResourceType::PTR); + /** + * @brief Send a multicast query request + * @param query Parameters for query + * @retval bool false if parameters failed validation or UDP request could not be sent + */ bool search(const Query& query); protected: diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Query.h b/Sming/Components/mdns/src/include/Network/Mdns/Query.h index 4550d3aec7..d20fa57970 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Query.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Query.h @@ -8,11 +8,10 @@ namespace mDNS * @brief A single mDNS Query */ struct Query { - String name; ///< Question Name: Contains the object, domain or zone name. - ResourceType type; ///< Question Type: Type of question being asked by client. - uint16_t klass; ///< Question Class: Normally the value 1 for Internet (“IN”) - bool isUnicastResponse; // - bool isValid; ///< False if problems were encountered decoding packet. + String name; ///< Contains the object, domain or zone name + ResourceType type{ResourceType::PTR}; ///< Type of question being asked by client + uint16_t klass{1}; ///< Normally the value 1 for Internet ('IN') + bool isUnicastResponse{false}; }; } // namespace mDNS diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Resource.h b/Sming/Components/mdns/src/include/Network/Mdns/Resource.h index c33898ddeb..12429b40fe 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Resource.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Resource.h @@ -28,6 +28,9 @@ enum class Type : uint16_t { #undef XX }; +/** + * @brief Resource Record with no specific type + */ class Record { public: @@ -44,7 +47,9 @@ class Record const Answer& answer; }; -// IP4 address +/** + * @brief 'A' record containing IP4 address + */ class A : public Record { public: @@ -58,7 +63,9 @@ class A : public Record } }; -// Pointer to a canonical name +/** + * @brief 'PTR' record containing pointer to a canonical name + */ class PTR : public Record { public: @@ -72,14 +79,21 @@ class PTR : public Record } }; -// Host information +/** + * @brief 'HINFO' record containing Host information + */ class HINFO : public Record { public: using Record::Record; }; -// Originally for arbitrary human-readable text in a DNS record +/** + * @brief 'TXT' record containing attribute list + * + * Originally for arbitrary human-readable text in a DNS record. + * Content is a set of name=value pairs. Value can be binary. + */ class TXT : public Record { public: @@ -118,7 +132,9 @@ class TXT : public Record mutable uint8_t mCount{0}; }; -// A 128-bit IPv6 address +/** + * @brief 'AAAA' record containing 128-bit IPv6 address + */ class AAAA : public Record { public: @@ -127,7 +143,9 @@ class AAAA : public Record String toString() const; }; -// Server Selection +/** + * @brief 'SRV' Service Locator record + */ class SRV : public Record { public: diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Response.h b/Sming/Components/mdns/src/include/Network/Mdns/Response.h index 0758eba32e..092f2886f8 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Response.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Response.h @@ -5,37 +5,64 @@ namespace mDNS { /** - * @brief Encapsulates a response packet for flexible interrogation + * @brief Encapsulates a response packet for flexible introspection */ class Response : public Answer::OwnedList { public: - Response(void* data, uint16_t size) : data(static_cast(data)), size(size) + Response(IpAddress remoteIp, uint16_t remotePort, void* data, uint16_t size) + : remoteIp(remoteIp), remotePort(remotePort), data(static_cast(data)), size(size) { } + /** + * @brief Parse response data + * @retval bool true if response parsed successfully, false indicates a problem + * + * Does basic validation and builds a list of answers. + */ bool parse(); + /** + * @brief Address of sender from UDP packet + */ + IpAddress getRemoteIp() const + { + return remoteIp; + } + + /** + * @brief UDP port in response + */ + uint16_t getRemotePort() const + { + return remotePort; + } + + /** + * @brief Check that response contains answers, not queries + */ bool isAnswer() const { return data[2] & 0x80; } + /** + * @brief If set, indicates record is split across multiple packets + */ bool isTruncated() const { return data[2] & 0x02; } + /** + * @brief Non-zero indicates error + */ uint8_t getResponseCode() const { return data[3] & 0x0f; } - uint8_t* resolvePointer(uint16_t pointer) - { - return data + pointer; - } - uint16_t getSize() const { return size; @@ -44,6 +71,14 @@ class Response : public Answer::OwnedList Answer* operator[](ResourceType type); private: + friend class Name; + uint8_t* resolvePointer(uint16_t pointer) + { + return data + pointer; + } + + IpAddress remoteIp; + uint16_t remotePort; uint8_t* data; uint16_t size; }; diff --git a/samples/Basic_Mdns/app/application.cpp b/samples/Basic_Mdns/app/application.cpp index 987883a120..51e05086c8 100644 --- a/samples/Basic_Mdns/app/application.cpp +++ b/samples/Basic_Mdns/app/application.cpp @@ -14,7 +14,11 @@ mDNS::Finder finder; void printResponse(mDNS::Response& response) { Serial.println(); - debug_i("RESPONSE!!"); + Serial.print(F("RESPONSE from ")); + Serial.print(response.getRemoteIp().toString()); + Serial.print(':'); + Serial.println(response.getRemotePort()); + for(auto& answer : response) { debug_i(">> name: %s", String(answer.getName()).c_str()); debug_i(" data: %s", answer.getRecordString().c_str()); @@ -45,19 +49,8 @@ void gotIP(IpAddress ip, IpAddress netmask, IpAddress gateway) finder.onAnswer(printResponse); - // bool ok = finder.search("_googlecast._tcp.local"); - mDNS::Query query{}; - query.name = F("_googlecast._tcp.local"); - query.type = mDNS::ResourceType::PTR; - query.klass = 1; // "INternet" - query.isUnicastResponse = false; - query.isValid = true; - bool ok = finder.search(query); - + bool ok = finder.search(F("_googlecast._tcp.local")); debug_i("search(): %s", ok ? "OK" : "FAIL"); - // finder.search("googlecast._tcp.local"); - // finder.search("_googlecast"); - // finder.search("googlecast"); } void connectFail(const String& ssid, MacAddress bssid, WifiDisconnectReason reason) @@ -74,7 +67,7 @@ void test() Serial.println(_F("** Parsing test packet **")); String data(testFile); - mDNS::Response response(data.begin(), data.length()); + mDNS::Response response(0U, 0, data.begin(), data.length()); response.parse(); printResponse(response); Serial.println(_F("** End of test packet **")); From 28e35d44214a5272571bc0f9c5a502eed457a3f2 Mon Sep 17 00:00:00 2001 From: mikee47 Date: Tue, 2 Mar 2021 23:32:05 +0000 Subject: [PATCH 13/52] Add file headers --- Sming/Components/mdns/src/Answer.cpp | 10 ++++++++++ Sming/Components/mdns/src/Finder.cpp | 10 ++++++++++ Sming/Components/mdns/src/Name.cpp | 10 ++++++++++ Sming/Components/mdns/src/Packet.h | 10 ++++++++++ Sming/Components/mdns/src/Resource.cpp | 10 ++++++++++ Sming/Components/mdns/src/Responder.cpp | 10 ++++++++++ Sming/Components/mdns/src/Response.cpp | 10 ++++++++++ .../mdns/src/include/Network/Mdns/Answer.h | 10 ++++++++++ .../mdns/src/include/Network/Mdns/Finder.h | 15 ++++++++++++--- .../mdns/src/include/Network/Mdns/Name.h | 10 ++++++++++ .../mdns/src/include/Network/Mdns/Query.h | 10 ++++++++++ .../mdns/src/include/Network/Mdns/Resource.h | 10 ++++++++++ .../mdns/src/include/Network/Mdns/Responder.h | 10 ++++++++++ .../mdns/src/include/Network/Mdns/Response.h | 10 ++++++++++ .../mdns/src/include/Network/Mdns/Service.h | 10 ++++++++++ 15 files changed, 152 insertions(+), 3 deletions(-) diff --git a/Sming/Components/mdns/src/Answer.cpp b/Sming/Components/mdns/src/Answer.cpp index 1962a0b2f5..cf5acfcdbe 100644 --- a/Sming/Components/mdns/src/Answer.cpp +++ b/Sming/Components/mdns/src/Answer.cpp @@ -1,3 +1,13 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * Answer.cpp + * + ****/ + #include "include/Network/Mdns/Answer.h" #include "include/Network/Mdns/Response.h" #include "Packet.h" diff --git a/Sming/Components/mdns/src/Finder.cpp b/Sming/Components/mdns/src/Finder.cpp index 0eb317a7fe..5e5122d06d 100644 --- a/Sming/Components/mdns/src/Finder.cpp +++ b/Sming/Components/mdns/src/Finder.cpp @@ -1,3 +1,13 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * Finder.cpp + * + ****/ + #include "include/Network/Mdns/Finder.h" #include "include/Network/Mdns/Response.h" #include "Packet.h" diff --git a/Sming/Components/mdns/src/Name.cpp b/Sming/Components/mdns/src/Name.cpp index d774066898..9852d58f09 100644 --- a/Sming/Components/mdns/src/Name.cpp +++ b/Sming/Components/mdns/src/Name.cpp @@ -1,3 +1,13 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * Name.cpp + * + ****/ + #include "include/Network/Mdns/Name.h" #include "include/Network/Mdns/Response.h" #include "Packet.h" diff --git a/Sming/Components/mdns/src/Packet.h b/Sming/Components/mdns/src/Packet.h index daf47efbd0..276a8da447 100644 --- a/Sming/Components/mdns/src/Packet.h +++ b/Sming/Components/mdns/src/Packet.h @@ -1,3 +1,13 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * Packet.h + * + ****/ + #pragma once #include diff --git a/Sming/Components/mdns/src/Resource.cpp b/Sming/Components/mdns/src/Resource.cpp index c49972ca20..1975c1e766 100644 --- a/Sming/Components/mdns/src/Resource.cpp +++ b/Sming/Components/mdns/src/Resource.cpp @@ -1,3 +1,13 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * Resource.cpp + * + ****/ + #include "include/Network/Mdns/Resource.h" #include "include/Network/Mdns/Answer.h" #include "Packet.h" diff --git a/Sming/Components/mdns/src/Responder.cpp b/Sming/Components/mdns/src/Responder.cpp index 2ae3ae632b..12cf5ff2ce 100644 --- a/Sming/Components/mdns/src/Responder.cpp +++ b/Sming/Components/mdns/src/Responder.cpp @@ -1,3 +1,13 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * Responder.cpp + * + ****/ + #include "include/Network/Mdns/Responder.h" #include diff --git a/Sming/Components/mdns/src/Response.cpp b/Sming/Components/mdns/src/Response.cpp index 38c07fc00c..a7bd7852a2 100644 --- a/Sming/Components/mdns/src/Response.cpp +++ b/Sming/Components/mdns/src/Response.cpp @@ -1,3 +1,13 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * Response.cpp + * + ****/ + #include "include/Network/Mdns/Response.h" #include "Packet.h" #include diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Answer.h b/Sming/Components/mdns/src/include/Network/Mdns/Answer.h index 086d48e4df..7ec08ffe62 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Answer.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Answer.h @@ -1,3 +1,13 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * Answer.h + * + ****/ + #pragma once #include diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Finder.h b/Sming/Components/mdns/src/include/Network/Mdns/Finder.h index 0d5b609eb4..177fd2f1dd 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Finder.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Finder.h @@ -1,9 +1,17 @@ -/** - * Since big portions of the code are copied from Finder.h and Finder.cpp are copied - * from MrDunk's mDNS code these files are distributed under the same license as his project. +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * Finder.h + * + * Code originally based on MrDunk's mDNS code distributed under the MIT license. * * MIT license: https://github.com/mrdunk/esp8266_mdns/blob/master/LICENCE.txt * + * However, code has been essentially rewritten so Sming LGPL v3 license applies. + * * References: * * Zero-configuration networking (DNS-SD) https://en.wikipedia.org/wiki/Zero-configuration_networking @@ -12,6 +20,7 @@ * DNS record types https://en.wikipedia.org/wiki/List_of_DNS_record_types * */ + #pragma once #include diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Name.h b/Sming/Components/mdns/src/include/Network/Mdns/Name.h index 6ab8a22e5a..a872ccc59f 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Name.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Name.h @@ -1,3 +1,13 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * Name.h + * + ****/ + #pragma once #include diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Query.h b/Sming/Components/mdns/src/include/Network/Mdns/Query.h index d20fa57970..36074d2d82 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Query.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Query.h @@ -1,3 +1,13 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * Query.h + * + ****/ + #pragma once #include "Resource.h" diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Resource.h b/Sming/Components/mdns/src/include/Network/Mdns/Resource.h index 12429b40fe..43f37c0350 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Resource.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Resource.h @@ -1,3 +1,13 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * Resource.h + * + ****/ + #pragma once #include "Name.h" diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Responder.h b/Sming/Components/mdns/src/include/Network/Mdns/Responder.h index c1afb8773a..e1ec7969b7 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Responder.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Responder.h @@ -1,3 +1,13 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * Responder.h + * + ****/ + #pragma once #include "Service.h" diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Response.h b/Sming/Components/mdns/src/include/Network/Mdns/Response.h index 092f2886f8..a56a27593a 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Response.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Response.h @@ -1,3 +1,13 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * Response.h + * + ****/ + #pragma once #include "Answer.h" diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Service.h b/Sming/Components/mdns/src/include/Network/Mdns/Service.h index baa485ed4b..f40d1f4bd9 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Service.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Service.h @@ -1,3 +1,13 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * Service.h + * + ****/ + #pragma once #include From 667ed8579ecf7470f6a10c392e39cc95dc80237f Mon Sep 17 00:00:00 2001 From: mikee47 Date: Wed, 3 Mar 2021 08:59:30 +0000 Subject: [PATCH 14/52] Test on ESP8266, fix. --- Sming/Components/mdns/src/Finder.cpp | 41 ++++++++++++------- .../mdns/src/include/Network/Mdns/Finder.h | 2 +- samples/Basic_Mdns/app/application.cpp | 8 +++- 3 files changed, 34 insertions(+), 17 deletions(-) diff --git a/Sming/Components/mdns/src/Finder.cpp b/Sming/Components/mdns/src/Finder.cpp index 5e5122d06d..1b5579560a 100644 --- a/Sming/Components/mdns/src/Finder.cpp +++ b/Sming/Components/mdns/src/Finder.cpp @@ -11,7 +11,7 @@ #include "include/Network/Mdns/Finder.h" #include "include/Network/Mdns/Response.h" #include "Packet.h" -#include +#include #define MDNS_IP 224, 0, 0, 251 #define MDNS_TARGET_PORT 5353 @@ -73,33 +73,46 @@ bool Finder::search(const Query& query) pkt.write(name.c_str() + pos, wordLength); pos = sep + 1; } while(pos > 0); - - pkt.write8('\0'); // End of name. + pkt.write8(0); // End of name. // 2 bytes for type pkt.write16(uint16_t(query.type)); // 2 bytes for class - unsigned int qclass = 0; + uint16_t qclass = query.klass; if(query.isUnicastResponse) { - qclass = 0x8000; + qclass |= 0x8000; } - qclass += query.klass; pkt.write16(qclass); initialise(); - listen(0); - return sendTo(IpAddress(MDNS_IP), MDNS_TARGET_PORT, reinterpret_cast(pkt.data), pkt.pos); + out.listen(0); + return out.sendTo(IpAddress(MDNS_IP), MDNS_TARGET_PORT, reinterpret_cast(pkt.data), pkt.pos); } -void Finder::initialise() +bool Finder::initialise() { - if(!initialised) { - joinMulticastGroup(IpAddress(MDNS_IP)); - listen(MDNS_SOURCE_PORT); - setMulticastTtl(MDNS_TTL); - initialised = true; + if(initialised) { + return true; + } + + auto localIp = WifiStation.getIP(); + + if(!joinMulticastGroup(localIp, IpAddress(MDNS_IP))) { + debug_w("[mDNS] joinMulticastGroup() failed"); + return false; } + + if(!listen(MDNS_SOURCE_PORT)) { + debug_e("[mDNS] listen failed"); + return false; + } + + setMulticast(localIp); + setMulticastTtl(MDNS_TTL); + + initialised = true; + return true; } void Finder::UdpOut::onReceive(pbuf* buf, IpAddress remoteIP, uint16_t remotePort) diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Finder.h b/Sming/Components/mdns/src/include/Network/Mdns/Finder.h index 177fd2f1dd..46807f2f12 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Finder.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Finder.h @@ -95,7 +95,7 @@ class Finder : protected UdpConnection Finder& finder; }; - void initialise(); + bool initialise(); AnswerDelegate answerCallback; UdpOut out; diff --git a/samples/Basic_Mdns/app/application.cpp b/samples/Basic_Mdns/app/application.cpp index 51e05086c8..7aa9f94cb9 100644 --- a/samples/Basic_Mdns/app/application.cpp +++ b/samples/Basic_Mdns/app/application.cpp @@ -49,8 +49,12 @@ void gotIP(IpAddress ip, IpAddress netmask, IpAddress gateway) finder.onAnswer(printResponse); - bool ok = finder.search(F("_googlecast._tcp.local")); - debug_i("search(): %s", ok ? "OK" : "FAIL"); + auto timer = new Timer; + timer->initializeMs<10000>(InterruptCallback([]() { + bool ok = finder.search(F("_googlecast._tcp.local")); + debug_i("search(): %s", ok ? "OK" : "FAIL"); + })); + timer->start(); } void connectFail(const String& ssid, MacAddress bssid, WifiDisconnectReason reason) From 7c06682f4350fcde4fd58189bf81823a95f7b6c8 Mon Sep 17 00:00:00 2001 From: mikee47 Date: Sun, 7 Mar 2021 09:11:50 +0000 Subject: [PATCH 15/52] Add packet callback (for debugging) --- Sming/Components/mdns/src/Finder.cpp | 4 ++++ .../mdns/src/include/Network/Mdns/Finder.h | 11 +++++++++ samples/Basic_Mdns/app/application.cpp | 23 +++++++++++++++++++ 3 files changed, 38 insertions(+) diff --git a/Sming/Components/mdns/src/Finder.cpp b/Sming/Components/mdns/src/Finder.cpp index 1b5579560a..203d8b68fd 100644 --- a/Sming/Components/mdns/src/Finder.cpp +++ b/Sming/Components/mdns/src/Finder.cpp @@ -122,6 +122,10 @@ void Finder::UdpOut::onReceive(pbuf* buf, IpAddress remoteIP, uint16_t remotePor void Finder::onReceive(pbuf* buf, IpAddress remoteIP, uint16_t remotePort) { + if(packetCallback) { + packetCallback(remoteIP, remotePort, static_cast(buf->payload), buf->len); + } + if(!answerCallback) { return; } diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Finder.h b/Sming/Components/mdns/src/include/Network/Mdns/Finder.h index 46807f2f12..9b65765e1a 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Finder.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Finder.h @@ -44,6 +44,11 @@ class Finder : protected UdpConnection */ using AnswerDelegate = Delegate; + /** + * @brief Callback to be invoked with raw data (debugging, etc.) + */ + using PacketDelegate = Delegate; + Finder() : out(*this) { } @@ -61,6 +66,11 @@ class Finder : protected UdpConnection answerCallback = callback; } + void onPacket(PacketDelegate callback) + { + packetCallback = callback; + } + /** * @brief Send a multicast query request * @param hostname Name to find, e.g. "_googlecast._tcp.local" @@ -98,6 +108,7 @@ class Finder : protected UdpConnection bool initialise(); AnswerDelegate answerCallback; + PacketDelegate packetCallback; UdpOut out; bool initialised{false}; }; diff --git a/samples/Basic_Mdns/app/application.cpp b/samples/Basic_Mdns/app/application.cpp index 7aa9f94cb9..6232e21845 100644 --- a/samples/Basic_Mdns/app/application.cpp +++ b/samples/Basic_Mdns/app/application.cpp @@ -1,5 +1,6 @@ #include #include +#include IMPORT_FSTR(testFile, PROJECT_DIR "/resource/192.168.1.100.mdns") @@ -11,6 +12,25 @@ IMPORT_FSTR(testFile, PROJECT_DIR "/resource/192.168.1.100.mdns") mDNS::Finder finder; +#ifdef ARCH_HOST +void savePacket(IpAddress remoteIP, uint16_t remotePort, const uint8_t* data, size_t length) +{ + auto& hostfs = IFS::Host::getFileSystem(); + String filename; + filename = "out/mdns/"; + hostfs.makedirs(filename); + filename += DateTime(SystemClock.now()).toISO8601(); + filename += '-'; + filename += remoteIP.toString(); + filename += '-'; + filename += remotePort; + filename += ".bin"; + filename.replace(':', '-'); + hostfs.setContent(filename, data, length); + // debug_i("Saved '%s'", filename.c_str()); +} +#endif + void printResponse(mDNS::Response& response) { Serial.println(); @@ -48,6 +68,9 @@ void gotIP(IpAddress ip, IpAddress netmask, IpAddress gateway) Serial.println(ip); finder.onAnswer(printResponse); +#ifdef ARCH_HOST + finder.onPacket(savePacket); +#endif auto timer = new Timer; timer->initializeMs<10000>(InterruptCallback([]() { From 2e5f83b94da0481fe59daca66319449f83a9fe5e Mon Sep 17 00:00:00 2001 From: mikee47 Date: Wed, 3 Mar 2021 14:04:05 +0000 Subject: [PATCH 16/52] Scan all test files --- samples/Basic_Mdns/app/application.cpp | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/samples/Basic_Mdns/app/application.cpp b/samples/Basic_Mdns/app/application.cpp index 6232e21845..4e156736cb 100644 --- a/samples/Basic_Mdns/app/application.cpp +++ b/samples/Basic_Mdns/app/application.cpp @@ -92,14 +92,24 @@ void test() debug_i("sizeof(mDNS::Answer) = %u", sizeof(mDNS::Answer)); debug_i("sizeof(LinkedObject) = %u", sizeof(LinkedObject)); - Serial.println(_F("** Parsing test packet **")); - String data(testFile); - mDNS::Response response(0U, 0, data.begin(), data.length()); - response.parse(); - printResponse(response); - Serial.println(_F("** End of test packet **")); - Serial.println(); - Serial.println(); + auto& fs = IFS::Host::getFileSystem(); + Directory dir(fs); + if(dir.open("resource")) { + while(dir.next()) { + String filename = dir.stat().name; + Serial.println(_F("** Parsing '")); + Serial.println(filename); + Serial.println(_F("' **")); + String data(fs.getContent(filename)); + // String data(testFile); + mDNS::Response response(0U, 0, data.begin(), data.length()); + response.parse(); + printResponse(response); + Serial.println(_F("** End of test packet **")); + Serial.println(); + Serial.println(); + } + } } void init() From be906dafc4b0c70bbe7e9ccc07857049e509a851 Mon Sep 17 00:00:00 2001 From: mikee47 Date: Wed, 3 Mar 2021 16:15:13 +0000 Subject: [PATCH 17/52] Revise name parsing to support extraction of components --- Sming/Components/mdns/src/Finder.cpp | 2 +- Sming/Components/mdns/src/Name.cpp | 97 +++++++++++++------ .../mdns/src/include/Network/Mdns/Finder.h | 3 - .../mdns/src/include/Network/Mdns/Name.h | 35 ++++++- 4 files changed, 100 insertions(+), 37 deletions(-) diff --git a/Sming/Components/mdns/src/Finder.cpp b/Sming/Components/mdns/src/Finder.cpp index 203d8b68fd..932bd590ab 100644 --- a/Sming/Components/mdns/src/Finder.cpp +++ b/Sming/Components/mdns/src/Finder.cpp @@ -37,7 +37,7 @@ bool Finder::search(const String& hostname, ResourceType type) bool Finder::search(const Query& query) { - if(query.name.length() > MAX_MDNS_NAME_LEN - 1) { + if(query.name.length() > Name::maxLength - 1) { return false; } diff --git a/Sming/Components/mdns/src/Name.cpp b/Sming/Components/mdns/src/Name.cpp index 9852d58f09..885e41349d 100644 --- a/Sming/Components/mdns/src/Name.cpp +++ b/Sming/Components/mdns/src/Name.cpp @@ -14,54 +14,91 @@ namespace mDNS { -String Name::toString() const +Name::Info Name::parse() const { - String s; + Info info{}; Packet pkt{data}; - while(true) { if(pkt.peek8() < 0xC0) { - // Since the first 2 bits are not set, - // this is the start of a name section. - // http://www.tcpipguide.com/free/t_DNSNameNotationandMessageCompressionTechnique.htm - - const uint8_t word_len = pkt.read8(); - s += pkt.readString(word_len); - + ++info.components; + auto wordLen = pkt.read8(); + info.textLength += wordLen; + pkt.skip(wordLen); if(pkt.peek8() == 0) { - return s; // End of string + if(info.dataLength == 0) { + info.dataLength = pkt.pos + 1; + } + return info; } - - // Next word - s += '.'; - continue; + ++info.textLength; // separator + } else { + uint16_t pointer = pkt.read16() & 0x3fff; + if(info.dataLength == 0) { + info.dataLength = pkt.pos; + } + pkt = Packet{response.resolvePointer(pointer)}; } - - // Message Compression used. Next 2 bytes are a pointer to the actual name section. - uint16_t pointer = pkt.read16() & 0x3fff; - pkt = Packet{response.resolvePointer(pointer)}; } } -uint16_t Name::getDataLength() const +uint16_t Name::read(char* buffer, uint16_t bufSize, uint8_t firstElement, uint8_t count) const { + uint16_t pos{0}; Packet pkt{data}; - - while(true) { + while(count != 0) { if(pkt.peek8() < 0xC0) { - const uint8_t word_len = pkt.read8(); - pkt.skip(word_len); - if(pkt.peek8() == 0) { - pkt.read8(); + auto wordLen = pkt.read8(); + if(firstElement == 0) { + --count; + if(pos + wordLen > bufSize) { + break; + } + pkt.read(&buffer[pos], wordLen); + pos += wordLen; + } else { + --firstElement; + pkt.skip(wordLen); + } + if(count == 0 || pkt.peek8() == 0) { + break; + } + if(pos >= bufSize) { break; } + if(pos != 0) { + buffer[pos++] = '.'; + } } else { - // Pointer at end - pkt.read16(); - break; + uint16_t pointer = pkt.read16() & 0x3fff; + pkt = Packet{response.resolvePointer(pointer)}; } } - return pkt.pos; + return pos; +} + +String Name::getString(uint8_t firstElement, uint8_t count) const +{ + char buffer[maxLength]; + auto len = read(buffer, maxLength, firstElement, count); + return String(buffer, len); +} + +String Name::getDomain() const +{ + auto info = parse(); + return getString(info.components - 1, 1); +} + +String Name::getService() const +{ + auto info = parse(); + return getString(info.components - 2, 1); +} + +String Name::getInstance() const +{ + auto info = parse(); + return getString(0, info.components - 2); } } // namespace mDNS diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Finder.h b/Sming/Components/mdns/src/include/Network/Mdns/Finder.h index 9b65765e1a..ce7d3ab281 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Finder.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Finder.h @@ -30,9 +30,6 @@ namespace mDNS { -// The mDNS spec says this should never be more than 256 (including trailing '\0'). -static constexpr size_t MAX_MDNS_NAME_LEN{256}; - /** * @brief Locates mDNS services by issuing queries */ diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Name.h b/Sming/Components/mdns/src/include/Network/Mdns/Name.h index a872ccc59f..0949a0ef50 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Name.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Name.h @@ -19,26 +19,55 @@ struct Packet; /** * @brief Encoded DNS name + * + * mDNS-SD names are represented as instance.service.domain */ class Name { public: + // The mDNS spec says this should never be more than 256 (including trailing '\0'). + static constexpr size_t maxLength{256}; + Name(Response& response, uint8_t* data) : response(response), data(data) { } - uint16_t getDataLength() const; + /** + * @brief Get number of bytes occupied by the name + * Not the same as the string length because content is encoded. + */ + uint16_t getDataLength() const + { + return parse().dataLength; + } - String toString() const; + String toString() const + { + return getString(0, 255); + } operator String() const { return toString(); } + String getDomain() const; + String getService() const; + String getInstance() const; + private: + struct Info { + uint16_t dataLength; + uint16_t textLength; + uint8_t components; + }; + + Info parse() const; + uint16_t read(char* buffer, uint16_t bufSize, uint8_t firstElement, uint8_t count) const; + String getString(uint8_t firstElement, uint8_t count) const; + Response& response; uint8_t* data; }; -} // namespace mDNS \ No newline at end of file +} // namespace mDNS From 9193905fe264d8cc013d6dbc47e7f417588f9b87 Mon Sep 17 00:00:00 2001 From: mikee47 Date: Wed, 3 Mar 2021 16:17:55 +0000 Subject: [PATCH 18/52] +savefile --- samples/Basic_Mdns/app/application.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/samples/Basic_Mdns/app/application.cpp b/samples/Basic_Mdns/app/application.cpp index 4e156736cb..8256cbcd51 100644 --- a/samples/Basic_Mdns/app/application.cpp +++ b/samples/Basic_Mdns/app/application.cpp @@ -27,7 +27,6 @@ void savePacket(IpAddress remoteIP, uint16_t remotePort, const uint8_t* data, si filename += ".bin"; filename.replace(':', '-'); hostfs.setContent(filename, data, length); - // debug_i("Saved '%s'", filename.c_str()); } #endif From d050dd8ea4b4bc4d8ff4525eb1bb1017371048bc Mon Sep 17 00:00:00 2001 From: mikee47 Date: Wed, 3 Mar 2021 16:18:26 +0000 Subject: [PATCH 19/52] Add question support to response, separate list --- Sming/Components/mdns/src/Answer.cpp | 2 +- Sming/Components/mdns/src/Question.cpp | 53 +++++++++ Sming/Components/mdns/src/Response.cpp | 28 ++--- .../mdns/src/include/Network/Mdns/Question.h | 71 ++++++++++++ .../mdns/src/include/Network/Mdns/Response.h | 6 +- samples/Basic_Mdns/app/application.cpp | 102 +++++++++++++----- 6 files changed, 222 insertions(+), 40 deletions(-) create mode 100644 Sming/Components/mdns/src/Question.cpp create mode 100644 Sming/Components/mdns/src/include/Network/Mdns/Question.h diff --git a/Sming/Components/mdns/src/Answer.cpp b/Sming/Components/mdns/src/Answer.cpp index cf5acfcdbe..7cc0dc69b1 100644 --- a/Sming/Components/mdns/src/Answer.cpp +++ b/Sming/Components/mdns/src/Answer.cpp @@ -46,7 +46,7 @@ bool Answer::parse(Packet& pkt) pkt.skip(nameLen + 8); if(pkt.pos > size) { - debug_e("[MDNS] Packet overrun, pos = %u, size = %u", pkt.pos, size); + debug_e("[MDNS] Answer packet overrun, pos = %u, size = %u", pkt.pos, size); // Something has gone wrong receiving or parsing the data. return false; } diff --git a/Sming/Components/mdns/src/Question.cpp b/Sming/Components/mdns/src/Question.cpp new file mode 100644 index 0000000000..575df7f735 --- /dev/null +++ b/Sming/Components/mdns/src/Question.cpp @@ -0,0 +1,53 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * Question.cpp + * + ****/ + +#include "include/Network/Mdns/Question.h" +#include "include/Network/Mdns/Response.h" +#include "Packet.h" +#include + +namespace mDNS +{ +uint16_t Question::getClass() const +{ + auto qclass = Packet{namePtr, uint16_t(nameLen + 2)}.read16(); + return qclass & 0x7fff; +} + +bool Question::isUnicastResponse() const +{ + auto qclass = Packet{namePtr, uint16_t(nameLen + 2)}.read16(); + return qclass & 0x8000; +} + +Resource::Type Question::getType() const +{ + auto type = Packet{namePtr, nameLen}.read16(); + return Resource::Type(type); +} + +bool Question::parse(Packet& pkt) +{ + auto size = response.getSize(); + + namePtr = pkt.ptr(); + nameLen = getName().getDataLength(); + pkt.skip(nameLen + 4); + + if(pkt.pos > size) { + debug_e("[MDNS] Question packet overrun, pos = %u, size = %u", pkt.pos, size); + // Something has gone wrong receiving or parsing the data. + return false; + } + + return true; +} + +} // namespace mDNS diff --git a/Sming/Components/mdns/src/Response.cpp b/Sming/Components/mdns/src/Response.cpp index a7bd7852a2..c4417e6d3c 100644 --- a/Sming/Components/mdns/src/Response.cpp +++ b/Sming/Components/mdns/src/Response.cpp @@ -17,12 +17,6 @@ namespace mDNS { bool Response::parse() { - // check if we have a response or a query - if(!isAnswer()) { - debug_d("Message is not a response. Ignoring."); - return false; - } - // TODO: If it's truncated we can expect more data soon so we should wait for additional records before deciding whether to respond. // if(isTruncated()) // { @@ -30,7 +24,7 @@ bool Response::parse() // Non zero Response code implies error. if(getResponseCode() != 0) { - debug_w("Got errored MDNS answer"); + debug_w("Got errored MDNS response"); return false; } @@ -38,10 +32,6 @@ bool Response::parse() // Number of incoming queries. auto questionsCount = pkt.read16(); - if(questionsCount != 0) { - // we are interested only in responses. - return false; - } // Number of incoming answers. auto answersCount = pkt.read16(); @@ -52,7 +42,17 @@ bool Response::parse() // Number of incoming Additional resource records. auto additionalCount = pkt.read16(); - // List of answers + // List of questions + for(uint16_t i = 0; i < questionsCount; i++) { + auto question = new Question(*this); + if(!question->parse(pkt)) { + delete question; + return false; + } + questions.add(question); + } + + // List of answers, namespaces and additional records bool ok{true}; auto recordCount = answersCount + nsCount + additionalCount; for(uint16_t i = 0; i < recordCount; i++) { @@ -61,7 +61,7 @@ bool Response::parse() delete answer; return false; } - add(answer); + answers.add(answer); } return ok; @@ -69,7 +69,7 @@ bool Response::parse() Answer* Response::operator[](ResourceType type) { - for(auto& ans : *this) { + for(auto& ans : answers) { if(ans.getType() == type) { return &ans; } diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Question.h b/Sming/Components/mdns/src/include/Network/Mdns/Question.h new file mode 100644 index 0000000000..a49352aeae --- /dev/null +++ b/Sming/Components/mdns/src/include/Network/Mdns/Question.h @@ -0,0 +1,71 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * Question.h + * + ****/ + +#pragma once + +#include +#include "Name.h" +#include "Resource.h" + +namespace mDNS +{ +class Response; +struct Packet; + +/** + * @brief A single mDNS Question + */ +class Question : public LinkedObjectTemplate +{ +public: + using List = LinkedObjectListTemplate; + using OwnedList = OwnedLinkedObjectListTemplate; + + Question(Response& response) : response(response) + { + } + + bool parse(Packet& pkt); + + /** + * @brief Object, domain or zone name + */ + Name getName() const + { + return Name(response, namePtr); + } + + /** + * @brief ResourceRecord type + */ + Resource::Type getType() const; + + /** + * @brief ResourceRecord Class: Normally the value 1 for Internet (“IN”) + */ + uint16_t getClass() const; + + /** + * @brief Whether response to this question should be unicast or multicast + */ + bool isUnicastResponse() const; + + Response& getResponse() const + { + return response; + } + +private: + Response& response; + uint8_t* namePtr; + uint16_t nameLen; +}; + +} // namespace mDNS diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Response.h b/Sming/Components/mdns/src/include/Network/Mdns/Response.h index a56a27593a..2a246c6d79 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Response.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Response.h @@ -10,6 +10,7 @@ #pragma once +#include "Question.h" #include "Answer.h" namespace mDNS @@ -17,7 +18,7 @@ namespace mDNS /** * @brief Encapsulates a response packet for flexible introspection */ -class Response : public Answer::OwnedList +class Response { public: Response(IpAddress remoteIp, uint16_t remotePort, void* data, uint16_t size) @@ -80,6 +81,9 @@ class Response : public Answer::OwnedList Answer* operator[](ResourceType type); + Question::OwnedList questions; + Answer::OwnedList answers; + private: friend class Name; uint8_t* resolvePointer(uint16_t pointer) diff --git a/samples/Basic_Mdns/app/application.cpp b/samples/Basic_Mdns/app/application.cpp index 8256cbcd51..db39e85612 100644 --- a/samples/Basic_Mdns/app/application.cpp +++ b/samples/Basic_Mdns/app/application.cpp @@ -30,21 +30,64 @@ void savePacket(IpAddress remoteIP, uint16_t remotePort, const uint8_t* data, si } #endif +void printQuestion(mDNS::Question& question) +{ + Serial.println(F(">> Question")); + + auto name = question.getName(); + Serial.print(F(" name: ")); + Serial.println(name); + + Serial.print(F(" instance: ")); + Serial.println(name.getInstance()); + Serial.print(F(" service: ")); + Serial.println(name.getService()); + Serial.print(F(" domain: ")); + Serial.println(name.getDomain()); + + Serial.print(F(" type: ")); + auto type = question.getType(); + Serial.print(toString(type)); + Serial.print(F(" (0x")); + Serial.print(unsigned(type), HEX); + Serial.println(")"); + + Serial.print(F(" class: 0x")); + Serial.println(question.getClass(), HEX); + Serial.print(F(" ucast: ")); + Serial.println(question.isUnicastResponse()); +} + +void printAnswer(mDNS::Answer& answer) +{ + debug_i(">> name: %s", String(answer.getName()).c_str()); + debug_i(" data: %s", answer.getRecordString().c_str()); + debug_i(" type: %s (0x%04X)", toString(answer.getType()).c_str(), unsigned(answer.getType())); + debug_i(" class: 0x%04x", answer.getClass()); + debug_i(" ttl: %u", answer.getTtl()); + debug_i(" flush: %u", answer.isCachedFlush()); +} + void printResponse(mDNS::Response& response) { Serial.println(); - Serial.print(F("RESPONSE from ")); - Serial.print(response.getRemoteIp().toString()); - Serial.print(':'); - Serial.println(response.getRemotePort()); - - for(auto& answer : response) { - debug_i(">> name: %s", String(answer.getName()).c_str()); - debug_i(" data: %s", answer.getRecordString().c_str()); - debug_i(" type: %s (0x%04X)", toString(answer.getType()).c_str(), unsigned(answer.getType())); - debug_i(" class: 0x%04x", answer.getClass()); - debug_i(" ttl: %u", answer.getTtl()); - debug_i(" flush: %u", answer.isCachedFlush()); + Serial.print(response.isAnswer() ? F("REQUEST") : F("RESPONSE")); + auto ip = response.getRemoteIp(); + if(uint32_t(ip) != 0) { + Serial.print(F(" from ")); + Serial.print(response.getRemoteIp().toString()); + Serial.print(':'); + Serial.println(response.getRemotePort()); + } else { + Serial.println(); + } + + for(auto& question : response.questions) { + printQuestion(question); + } + + for(auto& answer : response.answers) { + printAnswer(answer); } auto answer = response[mDNS::ResourceType::TXT]; @@ -84,6 +127,20 @@ void connectFail(const String& ssid, MacAddress bssid, WifiDisconnectReason reas Serial.println(F("I'm NOT CONNECTED!")); } +void parseFile(const String& name, const String& data) +{ + Serial.println(); + Serial.print(_F("** Parsing '")); + Serial.print(name); + Serial.println(_F("' **")); + mDNS::Response response(0U, 0, const_cast(data.begin()), data.length()); + if(response.parse()) { + printResponse(response); + } + Serial.println(_F("** End of test packet **")); + Serial.println(); +} + void test() { debug_i("sizeof(mDNS::Finder) = %u", sizeof(mDNS::Finder)); @@ -91,24 +148,21 @@ void test() debug_i("sizeof(mDNS::Answer) = %u", sizeof(mDNS::Answer)); debug_i("sizeof(LinkedObject) = %u", sizeof(LinkedObject)); +#ifdef ARCH_HOST + auto& fs = IFS::Host::getFileSystem(); - Directory dir(fs); + IFS::Directory dir(&fs); if(dir.open("resource")) { while(dir.next()) { - String filename = dir.stat().name; - Serial.println(_F("** Parsing '")); - Serial.println(filename); - Serial.println(_F("' **")); + String filename = dir.getDirName() + "/" + String(dir.stat().name); String data(fs.getContent(filename)); - // String data(testFile); - mDNS::Response response(0U, 0, data.begin(), data.length()); - response.parse(); - printResponse(response); - Serial.println(_F("** End of test packet **")); - Serial.println(); - Serial.println(); + parseFile(filename, data); } } + +#else + parseFile(F("testFile"), testFile); +#endif } void init() From ecfc749a4058ebe6d446905dd718efbea6f846f1 Mon Sep 17 00:00:00 2001 From: mikee47 Date: Wed, 3 Mar 2021 17:21:00 +0000 Subject: [PATCH 20/52] Add `Kind` field to answer to differentiate between answers, name server records and additional records --- Sming/Components/mdns/src/Answer.cpp | 16 ++++ Sming/Components/mdns/src/Response.cpp | 27 ++++--- .../mdns/src/include/Network/Mdns/Answer.h | 19 ++++- samples/Basic_Mdns/app/application.cpp | 73 ++++++++++++------- 4 files changed, 97 insertions(+), 38 deletions(-) diff --git a/Sming/Components/mdns/src/Answer.cpp b/Sming/Components/mdns/src/Answer.cpp index 7cc0dc69b1..2ebb47b84e 100644 --- a/Sming/Components/mdns/src/Answer.cpp +++ b/Sming/Components/mdns/src/Answer.cpp @@ -13,6 +13,22 @@ #include "Packet.h" #include +String toString(mDNS::Answer::Kind kind) +{ + using Kind = mDNS::Answer::Kind; + switch(kind) { + case Kind::answer: + return F("answer"); + case Kind::name: + return F("name"); + case Kind::additional: + return F("additional"); + default: + assert(false); + return nullptr; + } +} + namespace mDNS { ResourceType Answer::getType() const diff --git a/Sming/Components/mdns/src/Response.cpp b/Sming/Components/mdns/src/Response.cpp index c4417e6d3c..36427df604 100644 --- a/Sming/Components/mdns/src/Response.cpp +++ b/Sming/Components/mdns/src/Response.cpp @@ -53,18 +53,27 @@ bool Response::parse() } // List of answers, namespaces and additional records - bool ok{true}; - auto recordCount = answersCount + nsCount + additionalCount; - for(uint16_t i = 0; i < recordCount; i++) { - auto answer = new Answer(*this); - if(!answer->parse(pkt)) { - delete answer; - return false; + auto parseRecords = [&](Answer::Kind kind, uint16_t count) -> bool { + for(uint16_t i = 0; i < count; i++) { + auto answer = new Answer(*this, kind); + if(!answer->parse(pkt)) { + delete answer; + return false; + } + answers.add(answer); + } + return true; + }; + + bool ok = parseRecords(Answer::Kind::answer, answersCount); + if(ok) { + ok = parseRecords(Answer::Kind::name, nsCount); + if(ok) { + ok = parseRecords(Answer::Kind::additional, additionalCount); } - answers.add(answer); } - return ok; + return true; } Answer* Response::operator[](ResourceType type) diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Answer.h b/Sming/Components/mdns/src/include/Network/Mdns/Answer.h index 7ec08ffe62..8dcf0cfd8c 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Answer.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Answer.h @@ -28,12 +28,26 @@ class Answer : public LinkedObjectTemplate using List = LinkedObjectListTemplate; using OwnedList = OwnedLinkedObjectListTemplate; - Answer(Response& response) : response(response) + enum class Kind : uint8_t { + answer, + name, + additional, + }; + + Answer(Response& response, Kind kind) : response(response), kind(kind) { } bool parse(Packet& pkt); + /** + * @brief Identifies what kind of answer this is + */ + Kind getKind() const + { + return kind; + } + /** * @brief Object, domain or zone name */ @@ -93,6 +107,9 @@ class Answer : public LinkedObjectTemplate uint8_t* namePtr; uint16_t recordSize; uint16_t nameLen; + Kind kind; }; } // namespace mDNS + +String toString(mDNS::Answer::Kind kind); diff --git a/samples/Basic_Mdns/app/application.cpp b/samples/Basic_Mdns/app/application.cpp index db39e85612..375b172a28 100644 --- a/samples/Basic_Mdns/app/application.cpp +++ b/samples/Basic_Mdns/app/application.cpp @@ -30,48 +30,60 @@ void savePacket(IpAddress remoteIP, uint16_t remotePort, const uint8_t* data, si } #endif -void printQuestion(mDNS::Question& question) +void print(const String& tag, const String& value) { - Serial.println(F(">> Question")); + String s = " "; + s += tag; + s += ':'; + while(s.length() < 12) { + s += ' '; + } + Serial.print(s); + Serial.print(' '); + Serial.println(value); +} - auto name = question.getName(); - Serial.print(F(" name: ")); - Serial.println(name); +void printHex(const String& tag, uint16_t value) +{ + char buf[10]; + m_snprintf(buf, sizeof(buf), "0x%04x", value); + print(tag, buf); +} - Serial.print(F(" instance: ")); - Serial.println(name.getInstance()); - Serial.print(F(" service: ")); - Serial.println(name.getService()); - Serial.print(F(" domain: ")); - Serial.println(name.getDomain()); +void printBool(const String& tag, bool value) +{ + print(tag, value ? "Y" : "N"); +} - Serial.print(F(" type: ")); +void printQuestion(mDNS::Question& question) +{ + Serial.println(F(">> Question")); + print(F("name"), question.getName()); auto type = question.getType(); - Serial.print(toString(type)); - Serial.print(F(" (0x")); - Serial.print(unsigned(type), HEX); - Serial.println(")"); - - Serial.print(F(" class: 0x")); - Serial.println(question.getClass(), HEX); - Serial.print(F(" ucast: ")); - Serial.println(question.isUnicastResponse()); + print(F("type"), toString(type)); + printHex(F("type"), uint16_t(type)); + printHex(F("class"), question.getClass()); + printBool(F("unicast"), question.isUnicastResponse()); } void printAnswer(mDNS::Answer& answer) { - debug_i(">> name: %s", String(answer.getName()).c_str()); - debug_i(" data: %s", answer.getRecordString().c_str()); - debug_i(" type: %s (0x%04X)", toString(answer.getType()).c_str(), unsigned(answer.getType())); - debug_i(" class: 0x%04x", answer.getClass()); - debug_i(" ttl: %u", answer.getTtl()); - debug_i(" flush: %u", answer.isCachedFlush()); + Serial.print(">> "); + Serial.println(toString(answer.getKind())); + print(F("name"), answer.getName()); + print(F("data"), answer.getRecordString()); + auto type = answer.getType(); + print(F("type"), toString(type)); + printHex(F("type"), uint16_t(type)); + print(F("ttl"), String(answer.getTtl())); + printHex(F("class"), answer.getClass()); + printBool(F("flush"), answer.isCachedFlush()); } void printResponse(mDNS::Response& response) { Serial.println(); - Serial.print(response.isAnswer() ? F("REQUEST") : F("RESPONSE")); + Serial.print(response.isAnswer() ? F("RESPONSE") : F("REQUEST")); auto ip = response.getRemoteIp(); if(uint32_t(ip) != 0) { Serial.print(F(" from ")); @@ -82,6 +94,10 @@ void printResponse(mDNS::Response& response) Serial.println(); } + Serial.print(F("Size: ")); + Serial.print(response.getSize()); + Serial.println(F(" bytes")); + for(auto& question : response.questions) { printQuestion(question); } @@ -145,6 +161,7 @@ void test() { debug_i("sizeof(mDNS::Finder) = %u", sizeof(mDNS::Finder)); debug_i("sizeof(mDNS::Response) = %u", sizeof(mDNS::Response)); + debug_i("sizeof(mDNS::Question) = %u", sizeof(mDNS::Question)); debug_i("sizeof(mDNS::Answer) = %u", sizeof(mDNS::Answer)); debug_i("sizeof(LinkedObject) = %u", sizeof(LinkedObject)); From 024c212313256beacd942f849da39b42a664c044 Mon Sep 17 00:00:00 2001 From: mikee47 Date: Wed, 3 Mar 2021 17:21:35 +0000 Subject: [PATCH 21/52] Reduce object size by storing `Ptr` values instead of memory pointers --- Sming/Components/mdns/src/Answer.cpp | 15 +++++--- Sming/Components/mdns/src/Name.cpp | 4 +- Sming/Components/mdns/src/Question.cpp | 8 ++-- Sming/Components/mdns/src/Resource.cpp | 38 +++++++++---------- .../mdns/src/include/Network/Mdns/Answer.h | 6 ++- .../mdns/src/include/Network/Mdns/Name.h | 4 +- .../mdns/src/include/Network/Mdns/Question.h | 2 +- .../mdns/src/include/Network/Mdns/Resource.h | 4 +- .../mdns/src/include/Network/Mdns/Response.h | 2 + 9 files changed, 46 insertions(+), 37 deletions(-) diff --git a/Sming/Components/mdns/src/Answer.cpp b/Sming/Components/mdns/src/Answer.cpp index 2ebb47b84e..11b6d71efa 100644 --- a/Sming/Components/mdns/src/Answer.cpp +++ b/Sming/Components/mdns/src/Answer.cpp @@ -33,31 +33,31 @@ namespace mDNS { ResourceType Answer::getType() const { - return ResourceType(Packet{namePtr, nameLen}.read16()); + return ResourceType(Packet{response.resolvePointer(namePtr + nameLen)}.read16()); } uint16_t Answer::getClass() const { - auto rrclass = Packet{namePtr, uint16_t(nameLen + 2)}.read16(); + auto rrclass = Packet{response.resolvePointer(namePtr + nameLen + 2)}.read16(); return rrclass & 0x7fff; } bool Answer::isCachedFlush() const { - auto rrclass = Packet{namePtr, uint16_t(nameLen + 2)}.read16(); + auto rrclass = Packet{response.resolvePointer(namePtr + nameLen + 2)}.read16(); return rrclass & 0x8000; } uint32_t Answer::getTtl() const { - return Packet{namePtr, uint16_t(nameLen + 4)}.read32(); + return Packet{response.resolvePointer(namePtr + nameLen + 4)}.read32(); } bool Answer::parse(Packet& pkt) { auto size = response.getSize(); - namePtr = pkt.ptr(); + namePtr = pkt.pos; nameLen = getName().getDataLength(); pkt.skip(nameLen + 8); @@ -72,6 +72,11 @@ bool Answer::parse(Packet& pkt) return true; } +uint8_t* Answer::getRecord() const +{ + return response.resolvePointer(getRecordPtr()); +} + String Answer::getRecordString() const { using namespace Resource; diff --git a/Sming/Components/mdns/src/Name.cpp b/Sming/Components/mdns/src/Name.cpp index 885e41349d..4fd3febf0a 100644 --- a/Sming/Components/mdns/src/Name.cpp +++ b/Sming/Components/mdns/src/Name.cpp @@ -17,7 +17,7 @@ namespace mDNS Name::Info Name::parse() const { Info info{}; - Packet pkt{data}; + Packet pkt{response.resolvePointer(ptr)}; while(true) { if(pkt.peek8() < 0xC0) { ++info.components; @@ -44,7 +44,7 @@ Name::Info Name::parse() const uint16_t Name::read(char* buffer, uint16_t bufSize, uint8_t firstElement, uint8_t count) const { uint16_t pos{0}; - Packet pkt{data}; + Packet pkt{response.resolvePointer(ptr)}; while(count != 0) { if(pkt.peek8() < 0xC0) { auto wordLen = pkt.read8(); diff --git a/Sming/Components/mdns/src/Question.cpp b/Sming/Components/mdns/src/Question.cpp index 575df7f735..d6679b4e74 100644 --- a/Sming/Components/mdns/src/Question.cpp +++ b/Sming/Components/mdns/src/Question.cpp @@ -17,19 +17,19 @@ namespace mDNS { uint16_t Question::getClass() const { - auto qclass = Packet{namePtr, uint16_t(nameLen + 2)}.read16(); + auto qclass = Packet{response.resolvePointer(namePtr + nameLen + 2)}.read16(); return qclass & 0x7fff; } bool Question::isUnicastResponse() const { - auto qclass = Packet{namePtr, uint16_t(nameLen + 2)}.read16(); + auto qclass = Packet{response.resolvePointer(namePtr + nameLen + 2)}.read16(); return qclass & 0x8000; } Resource::Type Question::getType() const { - auto type = Packet{namePtr, nameLen}.read16(); + auto type = Packet{response.resolvePointer(namePtr + nameLen)}.read16(); return Resource::Type(type); } @@ -37,7 +37,7 @@ bool Question::parse(Packet& pkt) { auto size = response.getSize(); - namePtr = pkt.ptr(); + namePtr = pkt.pos; nameLen = getName().getDataLength(); pkt.skip(nameLen + 4); diff --git a/Sming/Components/mdns/src/Resource.cpp b/Sming/Components/mdns/src/Resource.cpp index 1975c1e766..734917a616 100644 --- a/Sming/Components/mdns/src/Resource.cpp +++ b/Sming/Components/mdns/src/Resource.cpp @@ -32,19 +32,19 @@ namespace Resource { /* Record */ -uint8_t* Record::recordPtr() const +uint8_t* Record::getRecord() const { - return answer.getRecordPtr(); + return answer.getRecord(); } -uint16_t Record::recordSize() const +uint16_t Record::getRecordSize() const { return answer.getRecordSize(); } String Record::toString() const { - return makeHexString(recordPtr(), recordSize(), ' '); + return makeHexString(getRecord(), getRecordSize(), ' '); } /* A */ @@ -53,7 +53,7 @@ IpAddress A::getAddress() const { // Keep bytes in network order uint32_t addr; - memcpy(&addr, recordPtr(), sizeof(addr)); + memcpy(&addr, getRecord(), sizeof(addr)); return addr; } @@ -61,7 +61,7 @@ IpAddress A::getAddress() const Name PTR::getName() const { - return Name(answer.getResponse(), recordPtr()); + return Name(answer.getResponse(), answer.getRecordPtr()); } /* TXT */ @@ -69,8 +69,8 @@ Name PTR::getName() const String TXT::toString(const String& sep) const { String s; - Packet pkt{recordPtr()}; - auto size = recordSize(); + Packet pkt{getRecord()}; + auto size = getRecordSize(); while(pkt.pos < size) { auto len = pkt.read8(); if(s) { @@ -84,8 +84,8 @@ String TXT::toString(const String& sep) const uint8_t TXT::count() const { if(mCount == 0) { - Packet pkt{recordPtr()}; - auto size = recordSize(); + Packet pkt{getRecord()}; + auto size = getRecordSize(); while(pkt.pos < size) { auto len = pkt.read8(); pkt.skip(len); @@ -104,8 +104,8 @@ String TXT::operator[](uint8_t index) const String TXT::getValue(const char* name, uint16_t namelen) const { - Packet pkt{recordPtr()}; - auto size = recordSize(); + Packet pkt{getRecord()}; + auto size = getRecordSize(); while(pkt.pos < size) { auto len = pkt.read8(); auto entry = reinterpret_cast(pkt.ptr()); @@ -119,8 +119,8 @@ String TXT::getValue(const char* name, uint16_t namelen) const const char* TXT::get(uint8_t index, uint8_t& len) const { - Packet pkt{recordPtr()}; - auto size = recordSize(); + Packet pkt{getRecord()}; + auto size = getRecordSize(); for(; pkt.pos < size; --index) { len = pkt.read8(); if(index == 0) { @@ -135,29 +135,29 @@ const char* TXT::get(uint8_t index, uint8_t& len) const String AAAA::toString() const { - return makeHexString(recordPtr(), recordSize(), ':'); + return makeHexString(getRecord(), getRecordSize(), ':'); } /* SRV */ uint16_t SRV::getPriority() const { - return Packet{recordPtr()}.read16(); + return Packet{getRecord()}.read16(); } uint16_t SRV::getWeight() const { - return Packet{recordPtr(), 2}.read16(); + return Packet{getRecord(), 2}.read16(); } uint16_t SRV::getPort() const { - return Packet{recordPtr(), 4}.read16(); + return Packet{getRecord(), 4}.read16(); } Name SRV::getHost() const { - return Name(answer.getResponse(), recordPtr() + 6); + return Name(answer.getResponse(), answer.getRecordPtr() + 6); } String SRV::toString(const String& sep) const diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Answer.h b/Sming/Components/mdns/src/include/Network/Mdns/Answer.h index 8dcf0cfd8c..c87a9945ab 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Answer.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Answer.h @@ -86,10 +86,12 @@ class Answer : public LinkedObjectTemplate return response; } + uint8_t* getRecord() const; + /** * @brief Get pointer to Resource Record data */ - uint8_t* getRecordPtr() const + uint16_t getRecordPtr() const { return namePtr + nameLen + 10; } @@ -104,7 +106,7 @@ class Answer : public LinkedObjectTemplate private: Response& response; - uint8_t* namePtr; + uint16_t namePtr; uint16_t recordSize; uint16_t nameLen; Kind kind; diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Name.h b/Sming/Components/mdns/src/include/Network/Mdns/Name.h index 0949a0ef50..dde77cbcc3 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Name.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Name.h @@ -28,7 +28,7 @@ class Name // The mDNS spec says this should never be more than 256 (including trailing '\0'). static constexpr size_t maxLength{256}; - Name(Response& response, uint8_t* data) : response(response), data(data) + Name(Response& response, uint16_t ptr) : response(response), ptr(ptr) { } @@ -67,7 +67,7 @@ class Name String getString(uint8_t firstElement, uint8_t count) const; Response& response; - uint8_t* data; + uint16_t ptr; }; } // namespace mDNS diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Question.h b/Sming/Components/mdns/src/include/Network/Mdns/Question.h index a49352aeae..aec0629fe0 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Question.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Question.h @@ -64,7 +64,7 @@ class Question : public LinkedObjectTemplate private: Response& response; - uint8_t* namePtr; + uint16_t namePtr; uint16_t nameLen; }; diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Resource.h b/Sming/Components/mdns/src/include/Network/Mdns/Resource.h index 43f37c0350..c5d41a7d1f 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Resource.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Resource.h @@ -51,8 +51,8 @@ class Record String toString() const; protected: - uint8_t* recordPtr() const; - uint16_t recordSize() const; + uint8_t* getRecord() const; + uint16_t getRecordSize() const; const Answer& answer; }; diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Response.h b/Sming/Components/mdns/src/include/Network/Mdns/Response.h index 2a246c6d79..84ca9d14bc 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Response.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Response.h @@ -85,6 +85,8 @@ class Response Answer::OwnedList answers; private: + friend class Question; + friend class Answer; friend class Name; uint8_t* resolvePointer(uint16_t pointer) { From 71bab1c0bbe8288528fd1955e9ae9fdda9ab902f Mon Sep 17 00:00:00 2001 From: mikee47 Date: Wed, 3 Mar 2021 20:15:47 +0000 Subject: [PATCH 22/52] Fix LinkedObjectList::count() --- Sming/Core/Data/LinkedObjectList.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Sming/Core/Data/LinkedObjectList.h b/Sming/Core/Data/LinkedObjectList.h index 6ebb5c9963..1d5908f10f 100644 --- a/Sming/Core/Data/LinkedObjectList.h +++ b/Sming/Core/Data/LinkedObjectList.h @@ -100,7 +100,10 @@ template class LinkedObjectListTemplate : public LinkedObj size_t count() const { - return std::count(begin(), end(), true); + size_t n{0}; + for(auto p = mHead; p != nullptr; ++n, p = p->next()) { + } + return n; } bool contains(const ObjectType& object) const From 0dac3972b05a39f02063bec7b61978df0ca77aa2 Mon Sep 17 00:00:00 2001 From: mikee47 Date: Wed, 3 Mar 2021 20:16:21 +0000 Subject: [PATCH 23/52] Refactory Query and add support for multiple questions --- Sming/Components/mdns/src/Finder.cpp | 59 +++----------- Sming/Components/mdns/src/Query.cpp | 77 +++++++++++++++++++ .../mdns/src/include/Network/Mdns/Finder.h | 2 + .../mdns/src/include/Network/Mdns/Query.h | 51 +++++++++++- 4 files changed, 138 insertions(+), 51 deletions(-) create mode 100644 Sming/Components/mdns/src/Query.cpp diff --git a/Sming/Components/mdns/src/Finder.cpp b/Sming/Components/mdns/src/Finder.cpp index 932bd590ab..ad5c9bf966 100644 --- a/Sming/Components/mdns/src/Finder.cpp +++ b/Sming/Components/mdns/src/Finder.cpp @@ -29,65 +29,30 @@ Finder::~Finder() } } -bool Finder::search(const String& hostname, ResourceType type) +bool Finder::search(const String& name, ResourceType type) { - Query query{hostname, type}; + Query query{name, type}; return search(query); } bool Finder::search(const Query& query) { - if(query.name.length() > Name::maxLength - 1) { - return false; - } + Query::List list; + list.add(&query); + return search(list); +} +bool Finder::search(const Query::List& queries) +{ uint8_t buffer[MAX_PACKET_SIZE]; - Packet pkt{buffer}; - - // The first two bytes are the transaction id and they are not used in MDNS - pkt.write16(0); - - // 2 bytes for Flags - pkt.write8(0); // 0b00000000 for Query, 0b10000000 for Answer. - pkt.write8(0); - - // 2 bytes for number of questions - pkt.write16(1); - - // 2 bytes for number of Answer RRs - pkt.write16(0); - - // 2 bytes for Authority PRs - pkt.write16(0); - - // 2 bytes for Additional PRs - pkt.write16(0); - - size_t pos{0}; - auto& name = query.name; - auto namelen = name.length(); - do { - int sep = name.indexOf('.', pos); - auto wordLength = (sep >= 0) ? (sep - pos) : (namelen - pos); - pkt.write8(wordLength); - pkt.write(name.c_str() + pos, wordLength); - pos = sep + 1; - } while(pos > 0); - pkt.write8(0); // End of name. - - // 2 bytes for type - pkt.write16(uint16_t(query.type)); - - // 2 bytes for class - uint16_t qclass = query.klass; - if(query.isUnicastResponse) { - qclass |= 0x8000; + auto len = serialize(queries, buffer, sizeof(buffer)); + if(len == 0) { + return false; } - pkt.write16(qclass); initialise(); out.listen(0); - return out.sendTo(IpAddress(MDNS_IP), MDNS_TARGET_PORT, reinterpret_cast(pkt.data), pkt.pos); + return out.sendTo(IpAddress(MDNS_IP), MDNS_TARGET_PORT, reinterpret_cast(buffer), len); } bool Finder::initialise() diff --git a/Sming/Components/mdns/src/Query.cpp b/Sming/Components/mdns/src/Query.cpp new file mode 100644 index 0000000000..f8b22242c9 --- /dev/null +++ b/Sming/Components/mdns/src/Query.cpp @@ -0,0 +1,77 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * Query.cpp + * + ****/ + +#include "include/Network/Mdns/Query.h" +#include "Packet.h" + +namespace mDNS +{ +uint16_t Query::serialize(uint8_t* buffer) const +{ + if(name.length() > Name::maxLength - 1) { + debug_e("Query name too long: '%s'", name.c_str()); + return 0; + } + + Packet pkt{buffer}; + size_t pos{0}; + auto namelen = name.length(); + do { + int sep = name.indexOf('.', pos); + auto wordLength = (sep >= 0) ? (sep - pos) : (namelen - pos); + pkt.write8(wordLength); + pkt.write(name.c_str() + pos, wordLength); + pos = sep + 1; + } while(pos > 0); + pkt.write8(0); // End of name. + + // 2 bytes for type + pkt.write16(uint16_t(type)); + + // 2 bytes for class + pkt.write16(qclass); + + return pkt.pos; +} + +size_t serialize(const Query::List& list, uint8_t* buffer, size_t bufSize) +{ + Packet pkt{buffer}; + + // The first two bytes are the transaction id and they are not used in MDNS + pkt.write16(0); + + // 2 bytes for Flags and status code + pkt.write16(0); // 0x0000 for Query, 0x8000 for Answer + + // 2 bytes for number of questions + pkt.write16(list.count()); + + // 2 bytes for number of Answer RRs + pkt.write16(0); + + // 2 bytes for Authority PRs + pkt.write16(0); + + // 2 bytes for Additional PRs + pkt.write16(0); + + for(auto& query : list) { + auto len = query.serialize(pkt.ptr()); + if(len == 0) { + return 0; + } + pkt.pos += len; + } + + return pkt.pos; +} + +} // namespace mDNS \ No newline at end of file diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Finder.h b/Sming/Components/mdns/src/include/Network/Mdns/Finder.h index ce7d3ab281..80586f28a0 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Finder.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Finder.h @@ -83,6 +83,8 @@ class Finder : protected UdpConnection */ bool search(const Query& query); + bool search(const Query::List& queries); + protected: void onReceive(pbuf* buf, IpAddress remoteIP, uint16_t remotePort) override; diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Query.h b/Sming/Components/mdns/src/include/Network/Mdns/Query.h index 36074d2d82..1c30aaab45 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Query.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Query.h @@ -11,17 +11,60 @@ #pragma once #include "Resource.h" +#include namespace mDNS { /** - * @brief A single mDNS Query + * @brief A single mDNS question */ -struct Query { +class Query : public LinkedObjectTemplate +{ +public: + using List = LinkedObjectListTemplate; + using OwnedList = OwnedLinkedObjectListTemplate; + + Query(const String& name, ResourceType type = ResourceType::PTR) : name(name), type(type) + { + } + + uint16_t getQClass() const + { + return qclass & 0x7fff; + } + + void setQClass(uint16_t value) + { + qclass = (value & 0x7fff) | (qclass & 0x8000); + } + + bool isUnicastResponse() const + { + return qclass & 0x8000; + } + + void setUnicastResponse(bool state) + { + if(state) { + qclass |= 0x8000; + } else { + qclass &= 0x7fff; + } + } + + bool operator==(const Query& other) + { + return name == other.name && type == other.type && qclass == other.qclass; + } + + uint16_t serialize(uint8_t* buffer) const; + +private: String name; ///< Contains the object, domain or zone name ResourceType type{ResourceType::PTR}; ///< Type of question being asked by client - uint16_t klass{1}; ///< Normally the value 1 for Internet ('IN') - bool isUnicastResponse{false}; + uint16_t qclass{0x0001}; ///< Normally the value 1 for Internet ('IN') }; +size_t serialize(const Query::List& list, uint8_t* buffer, size_t bufSize); + } // namespace mDNS From 6504217f5e64cd7cc9bfbe19f21bde49f3acff9f Mon Sep 17 00:00:00 2001 From: mikee47 Date: Wed, 3 Mar 2021 20:42:59 +0000 Subject: [PATCH 24/52] Add question generate/read to test --- samples/Basic_Mdns/app/application.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/samples/Basic_Mdns/app/application.cpp b/samples/Basic_Mdns/app/application.cpp index 375b172a28..a5667734db 100644 --- a/samples/Basic_Mdns/app/application.cpp +++ b/samples/Basic_Mdns/app/application.cpp @@ -165,6 +165,16 @@ void test() debug_i("sizeof(mDNS::Answer) = %u", sizeof(mDNS::Answer)); debug_i("sizeof(LinkedObject) = %u", sizeof(LinkedObject)); + using namespace mDNS; + Query::OwnedList list; + list.add(new Query{F("_%9832479817234_sming._tcp.local"), mDNS::ResourceType::PTR}); + list.add(new Query{F("_sming._tcp.local"), mDNS::ResourceType::PTR}); + uint8_t buffer[1024]; + auto len = serialize(list, buffer, sizeof(buffer)); + Response response(0U, 0, buffer, len); + response.parse(); + printResponse(response); + #ifdef ARCH_HOST auto& fs = IFS::Host::getFileSystem(); From 80b05a91e8500f70f751c05b29a28f9f95868cbd Mon Sep 17 00:00:00 2001 From: mikee47 Date: Wed, 3 Mar 2021 21:30:58 +0000 Subject: [PATCH 25/52] +app --- samples/Basic_Mdns/app/application.cpp | 45 ++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 6 deletions(-) diff --git a/samples/Basic_Mdns/app/application.cpp b/samples/Basic_Mdns/app/application.cpp index a5667734db..fa133d5216 100644 --- a/samples/Basic_Mdns/app/application.cpp +++ b/samples/Basic_Mdns/app/application.cpp @@ -12,6 +12,8 @@ IMPORT_FSTR(testFile, PROJECT_DIR "/resource/192.168.1.100.mdns") mDNS::Finder finder; +DEFINE_FSTR_LOCAL(fstrSearchInstance, "_googlecast") + #ifdef ARCH_HOST void savePacket(IpAddress remoteIP, uint16_t remotePort, const uint8_t* data, size_t length) { @@ -105,19 +107,50 @@ void printResponse(mDNS::Response& response) for(auto& answer : response.answers) { printAnswer(answer); } +} + +void handleResponse(mDNS::Response& response) +{ + printResponse(response); - auto answer = response[mDNS::ResourceType::TXT]; + // Check if we're interested in this reponse + if(!response.isAnswer()) { + return; + } + auto answer = response[mDNS::ResourceType::PTR]; + if(answer == nullptr) { + return; + } + if(answer->getName().getInstance() != fstrSearchInstance) { + return; + } + + // Extract our required information from the response + struct { + String manufacturerDevice; + String friendlyName; + IpAddress ipaddr; + } info; + + answer = response[mDNS::ResourceType::TXT]; if(answer != nullptr) { mDNS::Resource::TXT txt(*answer); - auto s = txt["md"]; - debug_i("md = '%s'", s.c_str()); + info.manufacturerDevice = txt["md"]; + info.friendlyName = txt["fn"]; } answer = response[mDNS::ResourceType::A]; if(answer != nullptr) { mDNS::Resource::A a(*answer); - debug_i("addr = %s", a.toString().c_str()); + info.ipaddr = a.getAddress(); } + + Serial.print(F("Manufacturer Device = '")); + Serial.print(info.manufacturerDevice); + Serial.print(F("', Friendly Name = '")); + Serial.print(info.friendlyName); + Serial.print(F("', IP Address = ")); + Serial.println(info.ipaddr); } void gotIP(IpAddress ip, IpAddress netmask, IpAddress gateway) @@ -125,14 +158,14 @@ void gotIP(IpAddress ip, IpAddress netmask, IpAddress gateway) Serial.print(F("Connected. Got IP: ")); Serial.println(ip); - finder.onAnswer(printResponse); + finder.onAnswer(handleResponse); #ifdef ARCH_HOST finder.onPacket(savePacket); #endif auto timer = new Timer; timer->initializeMs<10000>(InterruptCallback([]() { - bool ok = finder.search(F("_googlecast._tcp.local")); + bool ok = finder.search(String(fstrSearchInstance) + F("._tcp.local")); debug_i("search(): %s", ok ? "OK" : "FAIL"); })); timer->start(); From 0f87a1f049eaeec19041c4271c28b705214e3665 Mon Sep 17 00:00:00 2001 From: mikee47 Date: Wed, 3 Mar 2021 22:46:54 +0000 Subject: [PATCH 26/52] Building answers --- Sming/Components/mdns/src/Answer.cpp | 27 ++++++++++++ Sming/Components/mdns/src/Request.cpp | 41 +++++++++++++++++++ Sming/Components/mdns/src/Resource.cpp | 40 ++++++++++++++++++ Sming/Components/mdns/src/Response.cpp | 16 ++++++++ .../mdns/src/include/Network/Mdns/Answer.h | 11 +++-- .../mdns/src/include/Network/Mdns/Request.h | 34 +++++++++++++++ .../mdns/src/include/Network/Mdns/Resource.h | 39 +++++++++++++++++- .../mdns/src/include/Network/Mdns/Response.h | 9 +++- samples/Basic_Mdns/app/application.cpp | 36 ++++++++++++---- 9 files changed, 239 insertions(+), 14 deletions(-) create mode 100644 Sming/Components/mdns/src/Request.cpp create mode 100644 Sming/Components/mdns/src/include/Network/Mdns/Request.h diff --git a/Sming/Components/mdns/src/Answer.cpp b/Sming/Components/mdns/src/Answer.cpp index 11b6d71efa..8af352d326 100644 --- a/Sming/Components/mdns/src/Answer.cpp +++ b/Sming/Components/mdns/src/Answer.cpp @@ -98,4 +98,31 @@ String Answer::getRecordString() const } } +uint16_t Answer::init(uint16_t namePtr, const String& name, Resource::Type type, uint16_t rclass, bool flush, + uint32_t ttl) +{ + this->namePtr = namePtr; + nameLen = response.writeName(namePtr, name); + Packet pkt{response.resolvePointer(namePtr), nameLen}; + pkt.write16(uint16_t(type)); // Type + pkt.write16((rclass & 0x7fff) | (flush ? 0x8000 : 0)); // Class + pkt.write32(ttl); // TTL + pkt.write16(0); // Resource Record Length + return pkt.pos; +} + +void Answer::allocate(uint16_t size) +{ + assert(size > recordSize); + message.allocate(size - recordSize); + recordSize = size; + Packet pkt{message.resolvePointer(namePtr + nameLen + 8)}; + pkt.write16(recordSize); +} + +uint16_t Answer::writeName(uint16_t ptr, const String& name) +{ + return response.writeName(getRecordPtr() + ptr, name); +} + } // namespace mDNS diff --git a/Sming/Components/mdns/src/Request.cpp b/Sming/Components/mdns/src/Request.cpp new file mode 100644 index 0000000000..921b067c13 --- /dev/null +++ b/Sming/Components/mdns/src/Request.cpp @@ -0,0 +1,41 @@ +#include "include/Network/Mdns/Request.h" +#include "Packet.h" + +namespace mDNS +{ +Request::Request() : Response(0U, 0, buffer, 0) +{ + Packet pkt{data, 0}; + + // The first two bytes are the transaction id and they are not used in MDNS + pkt.write16(0); + + // 2 bytes for Flags and status code + pkt.write16(0x8000); // 0x0000 for Query, 0x8000 for Answer + + // 2 bytes for number of questions + pkt.write16(0); + + // 2 bytes for number of Answer RRs + pkt.write16(0); + + // 2 bytes for Authority PRs + pkt.write16(0); + + // 2 bytes for Additional PRs + pkt.write16(0); + + size = pkt.pos; +} + +Answer* Request::createAnswer(const String& name, Resource::Type type, uint16_t rclass, bool flush, uint32_t ttl) +{ + auto answer = new Answer(*this, kind); + answers.add(answer); + Packet pkt{data, uint16_t(6 + unsigned(kind) * 2)}; + pkt.write16(pkt.peek16() + 1); + size += answer->init(size, name, type, rclass, flush, ttl); + return answer; +} + +} // namespace mDNS \ No newline at end of file diff --git a/Sming/Components/mdns/src/Resource.cpp b/Sming/Components/mdns/src/Resource.cpp index 734917a616..b0d91cc375 100644 --- a/Sming/Components/mdns/src/Resource.cpp +++ b/Sming/Components/mdns/src/Resource.cpp @@ -57,6 +57,14 @@ IpAddress A::getAddress() const return addr; } +void A::init(IpAddress ipaddr) +{ + uint32_t addr = ipaddr; + Packet pkt{getRecord()}; + pkt.write(&addr, sizeof(addr)); + answer.allocate(pkt.pos); +} + /* PTR */ Name PTR::getName() const @@ -64,6 +72,12 @@ Name PTR::getName() const return Name(answer.getResponse(), answer.getRecordPtr()); } +void PTR::init(const String& name) +{ + auto len = answer.writeName(0, name); + answer.allocate(len); +} + /* TXT */ String TXT::toString(const String& sep) const @@ -131,6 +145,15 @@ const char* TXT::get(uint8_t index, uint8_t& len) const return nullptr; } +void TXT::add(const String& value) +{ + Packet pkt{getRecord(), getRecordSize()}; + auto len = value.length(); + pkt.write8(len); + pkt.write(value.c_str(), len); + answer.allocate(pkt.pos); +} + /* AAAA */ String AAAA::toString() const @@ -138,6 +161,13 @@ String AAAA::toString() const return makeHexString(getRecord(), getRecordSize(), ':'); } +void AAAA::init(Ip6Address addr) +{ + Packet pkt{getRecord()}; + pkt.write(&addr, sizeof(addr)); + answer.allocate(pkt.pos); +} + /* SRV */ uint16_t SRV::getPriority() const @@ -178,5 +208,15 @@ String SRV::toString(const String& sep) const return s; } +void SRV::init(uint16_t priority, uint16_t weight, uint16_t port, const String& host) +{ + Packet pkt{getRecord()}; + pkt.write16(priority); + pkt.write16(weight); + pkt.write16(port); + pkt.pos += answer.writeName(pkt.pos, host); + answer.allocate(pkt.pos); +} + } // namespace Resource } // namespace mDNS diff --git a/Sming/Components/mdns/src/Response.cpp b/Sming/Components/mdns/src/Response.cpp index 36427df604..8e527b9c38 100644 --- a/Sming/Components/mdns/src/Response.cpp +++ b/Sming/Components/mdns/src/Response.cpp @@ -86,4 +86,20 @@ Answer* Response::operator[](ResourceType type) return nullptr; } +uint16_t Response::writeName(uint16_t ptr, const String& name) +{ + Packet pkt{resolvePointer(ptr)}; + size_t pos{0}; + auto namelen = name.length(); + do { + int sep = name.indexOf('.', pos); + auto wordLength = (sep >= 0) ? (sep - pos) : (namelen - pos); + pkt.write8(wordLength); + pkt.write(name.c_str() + pos, wordLength); + pos = sep + 1; + } while(pos > 0); + pkt.write8(0); // End of name. + return pkt.pos; +} + } // namespace mDNS diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Answer.h b/Sming/Components/mdns/src/include/Network/Mdns/Answer.h index c87a9945ab..4fe21e6352 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Answer.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Answer.h @@ -104,11 +104,16 @@ class Answer : public LinkedObjectTemplate return recordSize; } + // Writing + uint16_t init(uint16_t namePtr, const String& name, Resource::Type type, uint16_t rclass, bool flush, uint32_t ttl); + void allocate(uint16_t size); + uint16_t writeName(uint16_t ptr, const String& name); + private: Response& response; - uint16_t namePtr; - uint16_t recordSize; - uint16_t nameLen; + uint16_t namePtr{0}; + uint16_t recordSize{0}; + uint16_t nameLen{0}; Kind kind; }; diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Request.h b/Sming/Components/mdns/src/include/Network/Mdns/Request.h new file mode 100644 index 0000000000..34f0efaa1c --- /dev/null +++ b/Sming/Components/mdns/src/include/Network/Mdns/Request.h @@ -0,0 +1,34 @@ + +#include "Response.h" + +namespace mDNS +{ +class Request : public Response +{ +public: + Request(); + + Answer* createAnswer(const String& name, Resource::Type type, uint16_t rclass = 1, bool flush = false, + uint32_t ttl = 120); + + template Resource addAnswer(const String& name, ParamTypes... params) + { + auto answer = createAnswer(name, Resource::type); + Resource r(*answer); + r.init(params...); + return r; + } + + Answer::Kind nextSection() + { + assert(kind < Answer::Kind::additional); + kind = Answer::Kind(unsigned(kind) + 1); + return kind; + } + +private: + uint8_t buffer[1024]; + Answer::Kind kind{Answer::Kind::answer}; +}; + +} // namespace mDNS diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Resource.h b/Sming/Components/mdns/src/include/Network/Mdns/Resource.h index c5d41a7d1f..aaf8a6a1d2 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Resource.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Resource.h @@ -13,6 +13,10 @@ #include "Name.h" #include +struct Ip6Address { + uint8_t addr[16]; +}; + /** * @brief MDNS resource type identifiers * @@ -44,7 +48,7 @@ enum class Type : uint16_t { class Record { public: - Record(const Answer& answer) : answer(answer) + Record(const Answer& answer) : answer(const_cast(answer)) { } @@ -54,7 +58,7 @@ class Record uint8_t* getRecord() const; uint16_t getRecordSize() const; - const Answer& answer; + Answer& answer; }; /** @@ -63,6 +67,8 @@ class Record class A : public Record { public: + static constexpr Resource::Type type{Resource::Type::A}; + using Record::Record; IpAddress getAddress() const; @@ -71,6 +77,9 @@ class A : public Record { return getAddress().toString(); } + + // Writing + void init(IpAddress ipaddr); }; /** @@ -79,6 +88,8 @@ class A : public Record class PTR : public Record { public: + static constexpr Resource::Type type{Resource::Type::PTR}; + using Record::Record; Name getName() const; @@ -87,6 +98,9 @@ class PTR : public Record { return getName(); } + + // Writing + void init(const String& name); }; /** @@ -95,6 +109,8 @@ class PTR : public Record class HINFO : public Record { public: + static constexpr Resource::Type type{Resource::Type::HINFO}; + using Record::Record; }; @@ -107,6 +123,8 @@ class HINFO : public Record class TXT : public Record { public: + static constexpr Resource::Type type{Resource::Type::TXT}; + using Record::Record; uint8_t count() const; @@ -137,6 +155,13 @@ class TXT : public Record return getValue(name.c_str(), name.length()); } + // Writing + void init() + { + } + + void add(const String& value); + private: const char* get(uint8_t index, uint8_t& len) const; mutable uint8_t mCount{0}; @@ -148,9 +173,14 @@ class TXT : public Record class AAAA : public Record { public: + static constexpr Resource::Type type{Resource::Type::AAAA}; + using Record::Record; String toString() const; + + // Writing + void init(Ip6Address addr); }; /** @@ -159,6 +189,8 @@ class AAAA : public Record class SRV : public Record { public: + static constexpr Resource::Type type{Resource::Type::SRV}; + using Record::Record; uint16_t getPriority() const; @@ -170,6 +202,9 @@ class SRV : public Record Name getHost() const; String toString(const String& sep = "; ") const; + + // Writing + void init(uint16_t priority, uint16_t weight, uint16_t port, const String& host); }; } // namespace Resource diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Response.h b/Sming/Components/mdns/src/include/Network/Mdns/Response.h index 84ca9d14bc..79d4b6de93 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Response.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Response.h @@ -81,10 +81,17 @@ class Response Answer* operator[](ResourceType type); + // Writing + uint16_t writeName(uint16_t ptr, const String& name); + void allocate(uint16_t recordSize) + { + size += recordSize; + } + Question::OwnedList questions; Answer::OwnedList answers; -private: +protected: friend class Question; friend class Answer; friend class Name; diff --git a/samples/Basic_Mdns/app/application.cpp b/samples/Basic_Mdns/app/application.cpp index fa133d5216..dd23d7c277 100644 --- a/samples/Basic_Mdns/app/application.cpp +++ b/samples/Basic_Mdns/app/application.cpp @@ -1,5 +1,6 @@ #include #include +#include #include IMPORT_FSTR(testFile, PROJECT_DIR "/resource/192.168.1.100.mdns") @@ -199,14 +200,33 @@ void test() debug_i("sizeof(LinkedObject) = %u", sizeof(LinkedObject)); using namespace mDNS; - Query::OwnedList list; - list.add(new Query{F("_%9832479817234_sming._tcp.local"), mDNS::ResourceType::PTR}); - list.add(new Query{F("_sming._tcp.local"), mDNS::ResourceType::PTR}); - uint8_t buffer[1024]; - auto len = serialize(list, buffer, sizeof(buffer)); - Response response(0U, 0, buffer, len); - response.parse(); - printResponse(response); + + // Create list of questions, serialise them then decode and print the result + { + Query::OwnedList list; + list.add(new Query{F("_%9832479817234_sming._tcp.local"), mDNS::ResourceType::PTR}); + list.add(new Query{F("_sming._tcp.local"), mDNS::ResourceType::PTR}); + uint8_t buffer[1024]; + auto len = serialize(list, buffer, sizeof(buffer)); + Response response(0U, 0, buffer, len); + response.parse(); + printResponse(response); + } + + // Create response records + { + Request request; + auto ptr = request.addAnswer(F("_chromecast._tcp.local"), F("my.test.name._tcp.local")); + request.nextSection(); + request.nextSection(); + auto a = request.addAnswer(F("my.test.name._tcp.local"), 0x12345678); + auto txt = request.addAnswer(F("my.test.name._tcp.local")); + txt.add("pm=12"); + txt.add("fn=My friendly name"); + auto aaaa = request.addAnswer(F("abc._tcp.local"), Ip6Address()); + auto srv = request.addAnswer(F("xxxxx._tcp.local"), 1, 2, 8080, F("sillyhouse.net")); + printResponse(request); + } #ifdef ARCH_HOST From 374656bd1f93aa03fd666cd189e1f1c8f3933888 Mon Sep 17 00:00:00 2001 From: mikee47 Date: Thu, 4 Mar 2021 13:34:56 +0000 Subject: [PATCH 27/52] Writing questions --- Sming/Components/mdns/src/Question.cpp | 10 ++++++++++ Sming/Components/mdns/src/Request.cpp | 10 ++++++++++ .../mdns/src/include/Network/Mdns/Question.h | 3 +++ .../mdns/src/include/Network/Mdns/Request.h | 7 +++++++ samples/Basic_Mdns/app/application.cpp | 13 ++++++------- 5 files changed, 36 insertions(+), 7 deletions(-) diff --git a/Sming/Components/mdns/src/Question.cpp b/Sming/Components/mdns/src/Question.cpp index d6679b4e74..19eee8d935 100644 --- a/Sming/Components/mdns/src/Question.cpp +++ b/Sming/Components/mdns/src/Question.cpp @@ -50,4 +50,14 @@ bool Question::parse(Packet& pkt) return true; } +uint16_t Question::init(uint16_t namePtr, const String& name) +{ + this->namePtr = namePtr; + nameLen = response.writeName(namePtr, name); + Packet pkt{response.resolvePointer(namePtr), nameLen}; + pkt.write16(0); // Type + pkt.write16(0x0001); // Class + return pkt.pos; +} + } // namespace mDNS diff --git a/Sming/Components/mdns/src/Request.cpp b/Sming/Components/mdns/src/Request.cpp index 921b067c13..9820711f77 100644 --- a/Sming/Components/mdns/src/Request.cpp +++ b/Sming/Components/mdns/src/Request.cpp @@ -28,6 +28,16 @@ Request::Request() : Response(0U, 0, buffer, 0) size = pkt.pos; } +Question* Request::createQuestion(const String& name) +{ + auto question = new Question(*this); + questions.add(question); + Packet pkt{data, 4}; + pkt.write16(questions.count()); + size += question->init(size, name); + return question; +} + Answer* Request::createAnswer(const String& name, Resource::Type type, uint16_t rclass, bool flush, uint32_t ttl) { auto answer = new Answer(*this, kind); diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Question.h b/Sming/Components/mdns/src/include/Network/Mdns/Question.h index aec0629fe0..cede26962b 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Question.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Question.h @@ -62,6 +62,9 @@ class Question : public LinkedObjectTemplate return response; } + // Writing + uint16_t init(uint16_t namePtr, const String& name); + private: Response& response; uint16_t namePtr; diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Request.h b/Sming/Components/mdns/src/include/Network/Mdns/Request.h index 34f0efaa1c..7371213638 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Request.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Request.h @@ -8,6 +8,13 @@ class Request : public Response public: Request(); + Question* createQuestion(const String& name); + + Question* addQuestion(const String& name) + { + return createQuestion(name); + } + Answer* createAnswer(const String& name, Resource::Type type, uint16_t rclass = 1, bool flush = false, uint32_t ttl = 120); diff --git a/samples/Basic_Mdns/app/application.cpp b/samples/Basic_Mdns/app/application.cpp index dd23d7c277..6c730b5431 100644 --- a/samples/Basic_Mdns/app/application.cpp +++ b/samples/Basic_Mdns/app/application.cpp @@ -203,19 +203,18 @@ void test() // Create list of questions, serialise them then decode and print the result { - Query::OwnedList list; - list.add(new Query{F("_%9832479817234_sming._tcp.local"), mDNS::ResourceType::PTR}); - list.add(new Query{F("_sming._tcp.local"), mDNS::ResourceType::PTR}); - uint8_t buffer[1024]; - auto len = serialize(list, buffer, sizeof(buffer)); - Response response(0U, 0, buffer, len); + Request request(Request::Type::query); + request.addQuestion(F("_chromecast._tcp.local")); + request.addQuestion(F("_%9832479817234_sming._tcp.local"), mDNS::ResourceType::PTR); + request.addQuestion(F("_sming._tcp.local"), mDNS::ResourceType::PTR); + Response response(0U, 0, request.getData(), request.getSize()); response.parse(); printResponse(response); } // Create response records { - Request request; + Request request(Request::Type::reply); auto ptr = request.addAnswer(F("_chromecast._tcp.local"), F("my.test.name._tcp.local")); request.nextSection(); request.nextSection(); From efaca9c95b201ab9cad6dc11133c38fe349983df Mon Sep 17 00:00:00 2001 From: mikee47 Date: Thu, 4 Mar 2021 13:07:12 +0000 Subject: [PATCH 28/52] Remove Query --- Sming/Components/mdns/src/Finder.cpp | 23 ++---- Sming/Components/mdns/src/Query.cpp | 77 ------------------- Sming/Components/mdns/src/Question.cpp | 10 ++- Sming/Components/mdns/src/Request.cpp | 8 +- .../mdns/src/include/Network/Mdns/Finder.h | 19 +---- .../mdns/src/include/Network/Mdns/Query.h | 70 ----------------- .../mdns/src/include/Network/Mdns/Question.h | 2 +- .../mdns/src/include/Network/Mdns/Request.h | 19 +++-- .../mdns/src/include/Network/Mdns/Response.h | 7 +- 9 files changed, 42 insertions(+), 193 deletions(-) delete mode 100644 Sming/Components/mdns/src/Query.cpp delete mode 100644 Sming/Components/mdns/src/include/Network/Mdns/Query.h diff --git a/Sming/Components/mdns/src/Finder.cpp b/Sming/Components/mdns/src/Finder.cpp index ad5c9bf966..d3e125fd2f 100644 --- a/Sming/Components/mdns/src/Finder.cpp +++ b/Sming/Components/mdns/src/Finder.cpp @@ -31,28 +31,19 @@ Finder::~Finder() bool Finder::search(const String& name, ResourceType type) { - Query query{name, type}; - return search(query); + Request req(Request::Type::query); + auto question = req.addQuestion(name, type); + return send(req); } -bool Finder::search(const Query& query) +bool Finder::send(Request& request) { - Query::List list; - list.add(&query); - return search(list); -} - -bool Finder::search(const Query::List& queries) -{ - uint8_t buffer[MAX_PACKET_SIZE]; - auto len = serialize(queries, buffer, sizeof(buffer)); - if(len == 0) { - return false; - } + auto buf = reinterpret_cast(request.getData()); + auto len = request.getSize(); initialise(); out.listen(0); - return out.sendTo(IpAddress(MDNS_IP), MDNS_TARGET_PORT, reinterpret_cast(buffer), len); + return out.sendTo(IpAddress(MDNS_IP), MDNS_TARGET_PORT, buf, len); } bool Finder::initialise() diff --git a/Sming/Components/mdns/src/Query.cpp b/Sming/Components/mdns/src/Query.cpp deleted file mode 100644 index f8b22242c9..0000000000 --- a/Sming/Components/mdns/src/Query.cpp +++ /dev/null @@ -1,77 +0,0 @@ -/**** - * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. - * Created 2015 by Skurydin Alexey - * http://github.com/SmingHub/Sming - * All files of the Sming Core are provided under the LGPL v3 license. - * - * Query.cpp - * - ****/ - -#include "include/Network/Mdns/Query.h" -#include "Packet.h" - -namespace mDNS -{ -uint16_t Query::serialize(uint8_t* buffer) const -{ - if(name.length() > Name::maxLength - 1) { - debug_e("Query name too long: '%s'", name.c_str()); - return 0; - } - - Packet pkt{buffer}; - size_t pos{0}; - auto namelen = name.length(); - do { - int sep = name.indexOf('.', pos); - auto wordLength = (sep >= 0) ? (sep - pos) : (namelen - pos); - pkt.write8(wordLength); - pkt.write(name.c_str() + pos, wordLength); - pos = sep + 1; - } while(pos > 0); - pkt.write8(0); // End of name. - - // 2 bytes for type - pkt.write16(uint16_t(type)); - - // 2 bytes for class - pkt.write16(qclass); - - return pkt.pos; -} - -size_t serialize(const Query::List& list, uint8_t* buffer, size_t bufSize) -{ - Packet pkt{buffer}; - - // The first two bytes are the transaction id and they are not used in MDNS - pkt.write16(0); - - // 2 bytes for Flags and status code - pkt.write16(0); // 0x0000 for Query, 0x8000 for Answer - - // 2 bytes for number of questions - pkt.write16(list.count()); - - // 2 bytes for number of Answer RRs - pkt.write16(0); - - // 2 bytes for Authority PRs - pkt.write16(0); - - // 2 bytes for Additional PRs - pkt.write16(0); - - for(auto& query : list) { - auto len = query.serialize(pkt.ptr()); - if(len == 0) { - return 0; - } - pkt.pos += len; - } - - return pkt.pos; -} - -} // namespace mDNS \ No newline at end of file diff --git a/Sming/Components/mdns/src/Question.cpp b/Sming/Components/mdns/src/Question.cpp index 19eee8d935..ea5389c23c 100644 --- a/Sming/Components/mdns/src/Question.cpp +++ b/Sming/Components/mdns/src/Question.cpp @@ -50,13 +50,17 @@ bool Question::parse(Packet& pkt) return true; } -uint16_t Question::init(uint16_t namePtr, const String& name) +uint16_t Question::init(uint16_t namePtr, const String& name, ResourceType type, uint16_t qclass, bool unicast) { this->namePtr = namePtr; nameLen = response.writeName(namePtr, name); Packet pkt{response.resolvePointer(namePtr), nameLen}; - pkt.write16(0); // Type - pkt.write16(0x0001); // Class + pkt.write16(uint16_t(type)); + qclass &= 0x7fff; + if(unicast) { + qclass |= 0x8000; + } + pkt.write16(qclass); return pkt.pos; } diff --git a/Sming/Components/mdns/src/Request.cpp b/Sming/Components/mdns/src/Request.cpp index 9820711f77..9ef4996d13 100644 --- a/Sming/Components/mdns/src/Request.cpp +++ b/Sming/Components/mdns/src/Request.cpp @@ -3,7 +3,7 @@ namespace mDNS { -Request::Request() : Response(0U, 0, buffer, 0) +Request::Request(Type type) : Response(0U, 0, buffer, 0) { Packet pkt{data, 0}; @@ -11,7 +11,7 @@ Request::Request() : Response(0U, 0, buffer, 0) pkt.write16(0); // 2 bytes for Flags and status code - pkt.write16(0x8000); // 0x0000 for Query, 0x8000 for Answer + pkt.write16(type == Type::query ? 0x0000 : 0x8000); // 2 bytes for number of questions pkt.write16(0); @@ -28,13 +28,13 @@ Request::Request() : Response(0U, 0, buffer, 0) size = pkt.pos; } -Question* Request::createQuestion(const String& name) +Question* Request::addQuestion(const String& name, ResourceType type, uint16_t qclass, bool unicast) { auto question = new Question(*this); questions.add(question); Packet pkt{data, 4}; pkt.write16(questions.count()); - size += question->init(size, name); + size += question->init(size, name, type, qclass, unicast); return question; } diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Finder.h b/Sming/Components/mdns/src/include/Network/Mdns/Finder.h index 80586f28a0..ecee59cd66 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Finder.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Finder.h @@ -12,21 +12,13 @@ * * However, code has been essentially rewritten so Sming LGPL v3 license applies. * - * References: - * - * Zero-configuration networking (DNS-SD) https://en.wikipedia.org/wiki/Zero-configuration_networking - * Multicast DNS https://tools.ietf.org/html/rfc6762 - * DNS-Based Service Discovery https://tools.ietf.org/html/rfc6763 - * DNS record types https://en.wikipedia.org/wiki/List_of_DNS_record_types - * */ #pragma once #include #include -#include "Query.h" -#include "Response.h" +#include "Request.h" namespace mDNS { @@ -77,13 +69,10 @@ class Finder : protected UdpConnection bool search(const String& hostname, ResourceType type = ResourceType::PTR); /** - * @brief Send a multicast query request - * @param query Parameters for query - * @retval bool false if parameters failed validation or UDP request could not be sent + * @brief Send an mDNS request containing questions/answers + * @retval bool true if request sent successfully */ - bool search(const Query& query); - - bool search(const Query::List& queries); + bool send(Request& request); protected: void onReceive(pbuf* buf, IpAddress remoteIP, uint16_t remotePort) override; diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Query.h b/Sming/Components/mdns/src/include/Network/Mdns/Query.h deleted file mode 100644 index 1c30aaab45..0000000000 --- a/Sming/Components/mdns/src/include/Network/Mdns/Query.h +++ /dev/null @@ -1,70 +0,0 @@ -/**** - * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. - * Created 2015 by Skurydin Alexey - * http://github.com/SmingHub/Sming - * All files of the Sming Core are provided under the LGPL v3 license. - * - * Query.h - * - ****/ - -#pragma once - -#include "Resource.h" -#include - -namespace mDNS -{ -/** - * @brief A single mDNS question - */ -class Query : public LinkedObjectTemplate -{ -public: - using List = LinkedObjectListTemplate; - using OwnedList = OwnedLinkedObjectListTemplate; - - Query(const String& name, ResourceType type = ResourceType::PTR) : name(name), type(type) - { - } - - uint16_t getQClass() const - { - return qclass & 0x7fff; - } - - void setQClass(uint16_t value) - { - qclass = (value & 0x7fff) | (qclass & 0x8000); - } - - bool isUnicastResponse() const - { - return qclass & 0x8000; - } - - void setUnicastResponse(bool state) - { - if(state) { - qclass |= 0x8000; - } else { - qclass &= 0x7fff; - } - } - - bool operator==(const Query& other) - { - return name == other.name && type == other.type && qclass == other.qclass; - } - - uint16_t serialize(uint8_t* buffer) const; - -private: - String name; ///< Contains the object, domain or zone name - ResourceType type{ResourceType::PTR}; ///< Type of question being asked by client - uint16_t qclass{0x0001}; ///< Normally the value 1 for Internet ('IN') -}; - -size_t serialize(const Query::List& list, uint8_t* buffer, size_t bufSize); - -} // namespace mDNS diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Question.h b/Sming/Components/mdns/src/include/Network/Mdns/Question.h index cede26962b..0b2ec6c6ea 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Question.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Question.h @@ -63,7 +63,7 @@ class Question : public LinkedObjectTemplate } // Writing - uint16_t init(uint16_t namePtr, const String& name); + uint16_t init(uint16_t namePtr, const String& name, ResourceType type, uint16_t qclass, bool unicast); private: Response& response; diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Request.h b/Sming/Components/mdns/src/include/Network/Mdns/Request.h index 7371213638..efb1220618 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Request.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Request.h @@ -1,3 +1,4 @@ +#pragma once #include "Response.h" @@ -6,14 +7,15 @@ namespace mDNS class Request : public Response { public: - Request(); + enum class Type { + query, + reply, + }; - Question* createQuestion(const String& name); + Request(Type type); - Question* addQuestion(const String& name) - { - return createQuestion(name); - } + Question* addQuestion(const String& name, ResourceType type = ResourceType::PTR, uint16_t qclass = 1, + bool unicast = false); Answer* createAnswer(const String& name, Resource::Type type, uint16_t rclass = 1, bool flush = false, uint32_t ttl = 120); @@ -33,6 +35,11 @@ class Request : public Response return kind; } + const uint8_t* getBuffer() const + { + return buffer; + } + private: uint8_t buffer[1024]; Answer::Kind kind{Answer::Kind::answer}; diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Response.h b/Sming/Components/mdns/src/include/Network/Mdns/Response.h index 79d4b6de93..43c3fe7bef 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Response.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Response.h @@ -53,7 +53,7 @@ class Response /** * @brief Check that response contains answers, not queries */ - bool isAnswer() const + bool isReply() const { return data[2] & 0x80; } @@ -74,6 +74,11 @@ class Response return data[3] & 0x0f; } + uint8_t* getData() const + { + return data; + } + uint16_t getSize() const { return size; From 614ab512a6ee2b929dbc3f0c5edb93b8ad7d0b0a Mon Sep 17 00:00:00 2001 From: mikee47 Date: Thu, 4 Mar 2021 13:27:39 +0000 Subject: [PATCH 29/52] Rename `Finder` -> `Server` --- .../mdns/src/{Finder.cpp => Server.cpp} | 17 ++++++++--------- .../include/Network/Mdns/{Finder.h => Server.h} | 12 ++++++------ samples/Basic_Mdns/app/application.cpp | 11 +++++------ 3 files changed, 19 insertions(+), 21 deletions(-) rename Sming/Components/mdns/src/{Finder.cpp => Server.cpp} (81%) rename Sming/Components/mdns/src/include/Network/Mdns/{Finder.h => Server.h} (94%) diff --git a/Sming/Components/mdns/src/Finder.cpp b/Sming/Components/mdns/src/Server.cpp similarity index 81% rename from Sming/Components/mdns/src/Finder.cpp rename to Sming/Components/mdns/src/Server.cpp index d3e125fd2f..b4b63a9542 100644 --- a/Sming/Components/mdns/src/Finder.cpp +++ b/Sming/Components/mdns/src/Server.cpp @@ -4,12 +4,11 @@ * http://github.com/SmingHub/Sming * All files of the Sming Core are provided under the LGPL v3 license. * - * Finder.cpp + * Server.cpp * ****/ -#include "include/Network/Mdns/Finder.h" -#include "include/Network/Mdns/Response.h" +#include "include/Network/Mdns/Server.h" #include "Packet.h" #include @@ -22,21 +21,21 @@ namespace mDNS { -Finder::~Finder() +Server::~Server() { if(initialised) { UdpConnection::leaveMulticastGroup(IpAddress(MDNS_IP)); } } -bool Finder::search(const String& name, ResourceType type) +bool Server::search(const String& name, ResourceType type) { Request req(Request::Type::query); auto question = req.addQuestion(name, type); return send(req); } -bool Finder::send(Request& request) +bool Server::send(Request& request) { auto buf = reinterpret_cast(request.getData()); auto len = request.getSize(); @@ -46,7 +45,7 @@ bool Finder::send(Request& request) return out.sendTo(IpAddress(MDNS_IP), MDNS_TARGET_PORT, buf, len); } -bool Finder::initialise() +bool Server::initialise() { if(initialised) { return true; @@ -71,12 +70,12 @@ bool Finder::initialise() return true; } -void Finder::UdpOut::onReceive(pbuf* buf, IpAddress remoteIP, uint16_t remotePort) +void Server::UdpOut::onReceive(pbuf* buf, IpAddress remoteIP, uint16_t remotePort) { finder.onReceive(buf, remoteIP, remotePort); } -void Finder::onReceive(pbuf* buf, IpAddress remoteIP, uint16_t remotePort) +void Server::onReceive(pbuf* buf, IpAddress remoteIP, uint16_t remotePort) { if(packetCallback) { packetCallback(remoteIP, remotePort, static_cast(buf->payload), buf->len); diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Finder.h b/Sming/Components/mdns/src/include/Network/Mdns/Server.h similarity index 94% rename from Sming/Components/mdns/src/include/Network/Mdns/Finder.h rename to Sming/Components/mdns/src/include/Network/Mdns/Server.h index ecee59cd66..93acaf83c4 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Finder.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Server.h @@ -4,7 +4,7 @@ * http://github.com/SmingHub/Sming * All files of the Sming Core are provided under the LGPL v3 license. * - * Finder.h + * Server.h * * Code originally based on MrDunk's mDNS code distributed under the MIT license. * @@ -25,7 +25,7 @@ namespace mDNS /** * @brief Locates mDNS services by issuing queries */ -class Finder : protected UdpConnection +class Server : protected UdpConnection { public: /** @@ -38,11 +38,11 @@ class Finder : protected UdpConnection */ using PacketDelegate = Delegate; - Finder() : out(*this) + Server() : out(*this) { } - ~Finder(); + ~Server(); /** * @brief Set callback to be invoked for each received response @@ -84,13 +84,13 @@ class Finder : protected UdpConnection class UdpOut : public UdpConnection { public: - UdpOut(Finder& finder) : finder(finder) + UdpOut(Server& finder) : finder(finder) { } protected: void onReceive(pbuf* buf, IpAddress remoteIP, uint16_t remotePort) override; - Finder& finder; + Server& finder; }; bool initialise(); diff --git a/samples/Basic_Mdns/app/application.cpp b/samples/Basic_Mdns/app/application.cpp index 6c730b5431..0b9d5757af 100644 --- a/samples/Basic_Mdns/app/application.cpp +++ b/samples/Basic_Mdns/app/application.cpp @@ -1,6 +1,5 @@ #include -#include -#include +#include #include IMPORT_FSTR(testFile, PROJECT_DIR "/resource/192.168.1.100.mdns") @@ -11,7 +10,7 @@ IMPORT_FSTR(testFile, PROJECT_DIR "/resource/192.168.1.100.mdns") #define WIFI_PWD "PleaseEnterPass" #endif -mDNS::Finder finder; +mDNS::Server server; DEFINE_FSTR_LOCAL(fstrSearchInstance, "_googlecast") @@ -86,7 +85,7 @@ void printAnswer(mDNS::Answer& answer) void printResponse(mDNS::Response& response) { Serial.println(); - Serial.print(response.isAnswer() ? F("RESPONSE") : F("REQUEST")); + Serial.print(response.isReply() ? F("REPLY") : F("QUERY")); auto ip = response.getRemoteIp(); if(uint32_t(ip) != 0) { Serial.print(F(" from ")); @@ -115,7 +114,7 @@ void handleResponse(mDNS::Response& response) printResponse(response); // Check if we're interested in this reponse - if(!response.isAnswer()) { + if(!response.isReply()) { return; } auto answer = response[mDNS::ResourceType::PTR]; @@ -193,7 +192,7 @@ void parseFile(const String& name, const String& data) void test() { - debug_i("sizeof(mDNS::Finder) = %u", sizeof(mDNS::Finder)); + debug_i("sizeof(mDNS::Server) = %u", sizeof(mDNS::Server)); debug_i("sizeof(mDNS::Response) = %u", sizeof(mDNS::Response)); debug_i("sizeof(mDNS::Question) = %u", sizeof(mDNS::Question)); debug_i("sizeof(mDNS::Answer) = %u", sizeof(mDNS::Answer)); From 0921b6549d76e017ef61da0d8d1894ebaff827f0 Mon Sep 17 00:00:00 2001 From: mikee47 Date: Sun, 7 Mar 2021 09:17:07 +0000 Subject: [PATCH 30/52] Rename `Response` -> `Message` --- Sming/Components/mdns/src/Answer.cpp | 20 +++---- .../mdns/src/{Response.cpp => Message.cpp} | 12 ++-- Sming/Components/mdns/src/Name.cpp | 10 ++-- Sming/Components/mdns/src/Question.cpp | 16 +++--- Sming/Components/mdns/src/Request.cpp | 2 +- Sming/Components/mdns/src/Server.cpp | 8 +-- .../mdns/src/include/Network/Mdns/Answer.h | 12 ++-- .../Network/Mdns/{Response.h => Message.h} | 26 ++++++--- .../mdns/src/include/Network/Mdns/Name.h | 6 +- .../mdns/src/include/Network/Mdns/Question.h | 16 +++--- .../mdns/src/include/Network/Mdns/Request.h | 14 +---- .../mdns/src/include/Network/Mdns/Server.h | 19 +++---- samples/Basic_Mdns/app/application.cpp | 56 +++++++++---------- 13 files changed, 107 insertions(+), 110 deletions(-) rename Sming/Components/mdns/src/{Response.cpp => Message.cpp} (90%) rename Sming/Components/mdns/src/include/Network/Mdns/{Response.h => Message.h} (76%) diff --git a/Sming/Components/mdns/src/Answer.cpp b/Sming/Components/mdns/src/Answer.cpp index 8af352d326..4125c30f76 100644 --- a/Sming/Components/mdns/src/Answer.cpp +++ b/Sming/Components/mdns/src/Answer.cpp @@ -9,7 +9,7 @@ ****/ #include "include/Network/Mdns/Answer.h" -#include "include/Network/Mdns/Response.h" +#include "include/Network/Mdns/Message.h" #include "Packet.h" #include @@ -33,29 +33,29 @@ namespace mDNS { ResourceType Answer::getType() const { - return ResourceType(Packet{response.resolvePointer(namePtr + nameLen)}.read16()); + return ResourceType(Packet{message.resolvePointer(namePtr + nameLen)}.read16()); } uint16_t Answer::getClass() const { - auto rrclass = Packet{response.resolvePointer(namePtr + nameLen + 2)}.read16(); + auto rrclass = Packet{message.resolvePointer(namePtr + nameLen + 2)}.read16(); return rrclass & 0x7fff; } bool Answer::isCachedFlush() const { - auto rrclass = Packet{response.resolvePointer(namePtr + nameLen + 2)}.read16(); + auto rrclass = Packet{message.resolvePointer(namePtr + nameLen + 2)}.read16(); return rrclass & 0x8000; } uint32_t Answer::getTtl() const { - return Packet{response.resolvePointer(namePtr + nameLen + 4)}.read32(); + return Packet{message.resolvePointer(namePtr + nameLen + 4)}.read32(); } bool Answer::parse(Packet& pkt) { - auto size = response.getSize(); + auto size = message.getSize(); namePtr = pkt.pos; nameLen = getName().getDataLength(); @@ -74,7 +74,7 @@ bool Answer::parse(Packet& pkt) uint8_t* Answer::getRecord() const { - return response.resolvePointer(getRecordPtr()); + return message.resolvePointer(getRecordPtr()); } String Answer::getRecordString() const @@ -102,8 +102,8 @@ uint16_t Answer::init(uint16_t namePtr, const String& name, Resource::Type type, uint32_t ttl) { this->namePtr = namePtr; - nameLen = response.writeName(namePtr, name); - Packet pkt{response.resolvePointer(namePtr), nameLen}; + nameLen = message.writeName(namePtr, name); + Packet pkt{message.resolvePointer(namePtr), nameLen}; pkt.write16(uint16_t(type)); // Type pkt.write16((rclass & 0x7fff) | (flush ? 0x8000 : 0)); // Class pkt.write32(ttl); // TTL @@ -122,7 +122,7 @@ void Answer::allocate(uint16_t size) uint16_t Answer::writeName(uint16_t ptr, const String& name) { - return response.writeName(getRecordPtr() + ptr, name); + return message.writeName(getRecordPtr() + ptr, name); } } // namespace mDNS diff --git a/Sming/Components/mdns/src/Response.cpp b/Sming/Components/mdns/src/Message.cpp similarity index 90% rename from Sming/Components/mdns/src/Response.cpp rename to Sming/Components/mdns/src/Message.cpp index 8e527b9c38..e9b53e3b45 100644 --- a/Sming/Components/mdns/src/Response.cpp +++ b/Sming/Components/mdns/src/Message.cpp @@ -4,18 +4,18 @@ * http://github.com/SmingHub/Sming * All files of the Sming Core are provided under the LGPL v3 license. * - * Response.cpp + * Message.cpp * ****/ -#include "include/Network/Mdns/Response.h" +#include "include/Network/Mdns/Message.h" #include "Packet.h" #include #include namespace mDNS { -bool Response::parse() +bool Message::parse() { // TODO: If it's truncated we can expect more data soon so we should wait for additional records before deciding whether to respond. // if(isTruncated()) @@ -24,7 +24,7 @@ bool Response::parse() // Non zero Response code implies error. if(getResponseCode() != 0) { - debug_w("Got errored MDNS response"); + debug_w("Got errored MDNS message"); return false; } @@ -76,7 +76,7 @@ bool Response::parse() return true; } -Answer* Response::operator[](ResourceType type) +Answer* Message::operator[](ResourceType type) { for(auto& ans : answers) { if(ans.getType() == type) { @@ -86,7 +86,7 @@ Answer* Response::operator[](ResourceType type) return nullptr; } -uint16_t Response::writeName(uint16_t ptr, const String& name) +uint16_t Message::writeName(uint16_t ptr, const String& name) { Packet pkt{resolvePointer(ptr)}; size_t pos{0}; diff --git a/Sming/Components/mdns/src/Name.cpp b/Sming/Components/mdns/src/Name.cpp index 4fd3febf0a..fb5138a783 100644 --- a/Sming/Components/mdns/src/Name.cpp +++ b/Sming/Components/mdns/src/Name.cpp @@ -9,7 +9,7 @@ ****/ #include "include/Network/Mdns/Name.h" -#include "include/Network/Mdns/Response.h" +#include "include/Network/Mdns/Message.h" #include "Packet.h" namespace mDNS @@ -17,7 +17,7 @@ namespace mDNS Name::Info Name::parse() const { Info info{}; - Packet pkt{response.resolvePointer(ptr)}; + Packet pkt{message.resolvePointer(ptr)}; while(true) { if(pkt.peek8() < 0xC0) { ++info.components; @@ -36,7 +36,7 @@ Name::Info Name::parse() const if(info.dataLength == 0) { info.dataLength = pkt.pos; } - pkt = Packet{response.resolvePointer(pointer)}; + pkt = Packet{message.resolvePointer(pointer)}; } } } @@ -44,7 +44,7 @@ Name::Info Name::parse() const uint16_t Name::read(char* buffer, uint16_t bufSize, uint8_t firstElement, uint8_t count) const { uint16_t pos{0}; - Packet pkt{response.resolvePointer(ptr)}; + Packet pkt{message.resolvePointer(ptr)}; while(count != 0) { if(pkt.peek8() < 0xC0) { auto wordLen = pkt.read8(); @@ -70,7 +70,7 @@ uint16_t Name::read(char* buffer, uint16_t bufSize, uint8_t firstElement, uint8_ } } else { uint16_t pointer = pkt.read16() & 0x3fff; - pkt = Packet{response.resolvePointer(pointer)}; + pkt = Packet{message.resolvePointer(pointer)}; } } return pos; diff --git a/Sming/Components/mdns/src/Question.cpp b/Sming/Components/mdns/src/Question.cpp index ea5389c23c..ac23418fd2 100644 --- a/Sming/Components/mdns/src/Question.cpp +++ b/Sming/Components/mdns/src/Question.cpp @@ -9,7 +9,7 @@ ****/ #include "include/Network/Mdns/Question.h" -#include "include/Network/Mdns/Response.h" +#include "include/Network/Mdns/Message.h" #include "Packet.h" #include @@ -17,25 +17,25 @@ namespace mDNS { uint16_t Question::getClass() const { - auto qclass = Packet{response.resolvePointer(namePtr + nameLen + 2)}.read16(); + auto qclass = Packet{message.resolvePointer(namePtr + nameLen + 2)}.read16(); return qclass & 0x7fff; } -bool Question::isUnicastResponse() const +bool Question::isUnicastReply() const { - auto qclass = Packet{response.resolvePointer(namePtr + nameLen + 2)}.read16(); + auto qclass = Packet{message.resolvePointer(namePtr + nameLen + 2)}.read16(); return qclass & 0x8000; } Resource::Type Question::getType() const { - auto type = Packet{response.resolvePointer(namePtr + nameLen)}.read16(); + auto type = Packet{message.resolvePointer(namePtr + nameLen)}.read16(); return Resource::Type(type); } bool Question::parse(Packet& pkt) { - auto size = response.getSize(); + auto size = message.getSize(); namePtr = pkt.pos; nameLen = getName().getDataLength(); @@ -53,8 +53,8 @@ bool Question::parse(Packet& pkt) uint16_t Question::init(uint16_t namePtr, const String& name, ResourceType type, uint16_t qclass, bool unicast) { this->namePtr = namePtr; - nameLen = response.writeName(namePtr, name); - Packet pkt{response.resolvePointer(namePtr), nameLen}; + nameLen = message.writeName(namePtr, name); + Packet pkt{message.resolvePointer(namePtr), nameLen}; pkt.write16(uint16_t(type)); qclass &= 0x7fff; if(unicast) { diff --git a/Sming/Components/mdns/src/Request.cpp b/Sming/Components/mdns/src/Request.cpp index 9ef4996d13..db2f1fd88a 100644 --- a/Sming/Components/mdns/src/Request.cpp +++ b/Sming/Components/mdns/src/Request.cpp @@ -3,7 +3,7 @@ namespace mDNS { -Request::Request(Type type) : Response(0U, 0, buffer, 0) +Request::Request(Type type) : Message(0U, 0, buffer, 0) { Packet pkt{data, 0}; diff --git a/Sming/Components/mdns/src/Server.cpp b/Sming/Components/mdns/src/Server.cpp index b4b63a9542..5a1f795bd4 100644 --- a/Sming/Components/mdns/src/Server.cpp +++ b/Sming/Components/mdns/src/Server.cpp @@ -81,13 +81,13 @@ void Server::onReceive(pbuf* buf, IpAddress remoteIP, uint16_t remotePort) packetCallback(remoteIP, remotePort, static_cast(buf->payload), buf->len); } - if(!answerCallback) { + if(!messageCallback) { return; } - Response response(remoteIP, remotePort, buf->payload, buf->len); - if(response.parse()) { - answerCallback(response); + Message message(remoteIP, remotePort, buf->payload, buf->len); + if(message.parse()) { + messageCallback(message); } } diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Answer.h b/Sming/Components/mdns/src/include/Network/Mdns/Answer.h index 4fe21e6352..71aa90374c 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Answer.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Answer.h @@ -16,7 +16,7 @@ namespace mDNS { -class Response; +class Message; struct Packet; /** @@ -34,7 +34,7 @@ class Answer : public LinkedObjectTemplate additional, }; - Answer(Response& response, Kind kind) : response(response), kind(kind) + Answer(Message& message, Kind kind) : message(message), kind(kind) { } @@ -53,7 +53,7 @@ class Answer : public LinkedObjectTemplate */ Name getName() const { - return Name(response, namePtr); + return Name(message, namePtr); } /** @@ -81,9 +81,9 @@ class Answer : public LinkedObjectTemplate */ String getRecordString() const; - Response& getResponse() const + Message& getResponse() const { - return response; + return message; } uint8_t* getRecord() const; @@ -110,7 +110,7 @@ class Answer : public LinkedObjectTemplate uint16_t writeName(uint16_t ptr, const String& name); private: - Response& response; + Message& message; uint16_t namePtr{0}; uint16_t recordSize{0}; uint16_t nameLen{0}; diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Response.h b/Sming/Components/mdns/src/include/Network/Mdns/Message.h similarity index 76% rename from Sming/Components/mdns/src/include/Network/Mdns/Response.h rename to Sming/Components/mdns/src/include/Network/Mdns/Message.h index 43c3fe7bef..61fded4f9f 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Response.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Message.h @@ -4,7 +4,7 @@ * http://github.com/SmingHub/Sming * All files of the Sming Core are provided under the LGPL v3 license. * - * Response.h + * Message.h * ****/ @@ -16,19 +16,24 @@ namespace mDNS { /** - * @brief Encapsulates a response packet for flexible introspection + * @brief Encapsulates a message packet for flexible introspection */ -class Response +class Message { public: - Response(IpAddress remoteIp, uint16_t remotePort, void* data, uint16_t size) + enum class Type { + query, + reply, + }; + + Message(IpAddress remoteIp, uint16_t remotePort, void* data, uint16_t size) : remoteIp(remoteIp), remotePort(remotePort), data(static_cast(data)), size(size) { } /** - * @brief Parse response data - * @retval bool true if response parsed successfully, false indicates a problem + * @brief Parse message data + * @retval bool true if message parsed successfully, false indicates a problem * * Does basic validation and builds a list of answers. */ @@ -43,7 +48,7 @@ class Response } /** - * @brief UDP port in response + * @brief UDP port in message */ uint16_t getRemotePort() const { @@ -51,13 +56,18 @@ class Response } /** - * @brief Check that response contains answers, not queries + * @brief Check that message contains answers, not queries */ bool isReply() const { return data[2] & 0x80; } + Type getType() const + { + return isReply() ? Type::reply : Type::query; + } + /** * @brief If set, indicates record is split across multiple packets */ diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Name.h b/Sming/Components/mdns/src/include/Network/Mdns/Name.h index dde77cbcc3..93455c3cf9 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Name.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Name.h @@ -14,7 +14,7 @@ namespace mDNS { -class Response; +class Message; struct Packet; /** @@ -28,7 +28,7 @@ class Name // The mDNS spec says this should never be more than 256 (including trailing '\0'). static constexpr size_t maxLength{256}; - Name(Response& response, uint16_t ptr) : response(response), ptr(ptr) + Name(Message& message, uint16_t ptr) : message(message), ptr(ptr) { } @@ -66,7 +66,7 @@ class Name uint16_t read(char* buffer, uint16_t bufSize, uint8_t firstElement, uint8_t count) const; String getString(uint8_t firstElement, uint8_t count) const; - Response& response; + Message& message; uint16_t ptr; }; diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Question.h b/Sming/Components/mdns/src/include/Network/Mdns/Question.h index 0b2ec6c6ea..789f6ca6e3 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Question.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Question.h @@ -16,7 +16,7 @@ namespace mDNS { -class Response; +class Message; struct Packet; /** @@ -28,7 +28,7 @@ class Question : public LinkedObjectTemplate using List = LinkedObjectListTemplate; using OwnedList = OwnedLinkedObjectListTemplate; - Question(Response& response) : response(response) + Question(Message& message) : message(message) { } @@ -39,7 +39,7 @@ class Question : public LinkedObjectTemplate */ Name getName() const { - return Name(response, namePtr); + return Name(message, namePtr); } /** @@ -53,20 +53,20 @@ class Question : public LinkedObjectTemplate uint16_t getClass() const; /** - * @brief Whether response to this question should be unicast or multicast + * @brief Whether reply should be unicast or multicast */ - bool isUnicastResponse() const; + bool isUnicastReply() const; - Response& getResponse() const + Message& getMessage() const { - return response; + return message; } // Writing uint16_t init(uint16_t namePtr, const String& name, ResourceType type, uint16_t qclass, bool unicast); private: - Response& response; + Message& message; uint16_t namePtr; uint16_t nameLen; }; diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Request.h b/Sming/Components/mdns/src/include/Network/Mdns/Request.h index efb1220618..7b23682a4d 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Request.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Request.h @@ -1,17 +1,12 @@ #pragma once -#include "Response.h" +#include "Message.h" namespace mDNS { -class Request : public Response +class Request : public Message { public: - enum class Type { - query, - reply, - }; - Request(Type type); Question* addQuestion(const String& name, ResourceType type = ResourceType::PTR, uint16_t qclass = 1, @@ -35,11 +30,6 @@ class Request : public Response return kind; } - const uint8_t* getBuffer() const - { - return buffer; - } - private: uint8_t buffer[1024]; Answer::Kind kind{Answer::Kind::answer}; diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Server.h b/Sming/Components/mdns/src/include/Network/Mdns/Server.h index 93acaf83c4..7261691e0b 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Server.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Server.h @@ -29,9 +29,9 @@ class Server : protected UdpConnection { public: /** - * @brief Callback to be invoked for each received response. + * @brief Callback to be invoked for each received message */ - using AnswerDelegate = Delegate; + using MessageDelegate = Delegate; /** * @brief Callback to be invoked with raw data (debugging, etc.) @@ -45,14 +45,11 @@ class Server : protected UdpConnection ~Server(); /** - * @brief Set callback to be invoked for each received response - * - * An mDNS-SD (Multicast-DNS Service Discovery) response contains related answer records. - * The full set of answer records is passed to the callback. + * @brief Set callback to be invoked for each received message */ - void onAnswer(AnswerDelegate callback) + void onMessage(MessageDelegate callback) { - answerCallback = callback; + messageCallback = callback; } void onPacket(PacketDelegate callback) @@ -61,9 +58,9 @@ class Server : protected UdpConnection } /** - * @brief Send a multicast query request + * @brief Send a multicast query * @param hostname Name to find, e.g. "_googlecast._tcp.local" - * @param type + * @param type * @retval bool false if parameters failed validation or UDP request could not be sent */ bool search(const String& hostname, ResourceType type = ResourceType::PTR); @@ -95,7 +92,7 @@ class Server : protected UdpConnection bool initialise(); - AnswerDelegate answerCallback; + MessageDelegate messageCallback; PacketDelegate packetCallback; UdpOut out; bool initialised{false}; diff --git a/samples/Basic_Mdns/app/application.cpp b/samples/Basic_Mdns/app/application.cpp index 0b9d5757af..3e5863c166 100644 --- a/samples/Basic_Mdns/app/application.cpp +++ b/samples/Basic_Mdns/app/application.cpp @@ -65,7 +65,7 @@ void printQuestion(mDNS::Question& question) print(F("type"), toString(type)); printHex(F("type"), uint16_t(type)); printHex(F("class"), question.getClass()); - printBool(F("unicast"), question.isUnicastResponse()); + printBool(F("unicast"), question.isUnicastReply()); } void printAnswer(mDNS::Answer& answer) @@ -82,42 +82,42 @@ void printAnswer(mDNS::Answer& answer) printBool(F("flush"), answer.isCachedFlush()); } -void printResponse(mDNS::Response& response) +void printMessage(mDNS::Message& message) { Serial.println(); - Serial.print(response.isReply() ? F("REPLY") : F("QUERY")); - auto ip = response.getRemoteIp(); + Serial.print(message.isReply() ? F("REPLY") : F("QUERY")); + auto ip = message.getRemoteIp(); if(uint32_t(ip) != 0) { Serial.print(F(" from ")); - Serial.print(response.getRemoteIp().toString()); + Serial.print(message.getRemoteIp().toString()); Serial.print(':'); - Serial.println(response.getRemotePort()); + Serial.println(message.getRemotePort()); } else { Serial.println(); } Serial.print(F("Size: ")); - Serial.print(response.getSize()); + Serial.print(message.getSize()); Serial.println(F(" bytes")); - for(auto& question : response.questions) { + for(auto& question : message.questions) { printQuestion(question); } - for(auto& answer : response.answers) { + for(auto& answer : message.answers) { printAnswer(answer); } } -void handleResponse(mDNS::Response& response) +void handleMessage(mDNS::Message& message) { - printResponse(response); + printMessage(message); // Check if we're interested in this reponse - if(!response.isReply()) { + if(!message.isReply()) { return; } - auto answer = response[mDNS::ResourceType::PTR]; + auto answer = message[mDNS::ResourceType::PTR]; if(answer == nullptr) { return; } @@ -125,21 +125,21 @@ void handleResponse(mDNS::Response& response) return; } - // Extract our required information from the response + // Extract our required information from the message struct { String manufacturerDevice; String friendlyName; IpAddress ipaddr; } info; - answer = response[mDNS::ResourceType::TXT]; + answer = message[mDNS::ResourceType::TXT]; if(answer != nullptr) { mDNS::Resource::TXT txt(*answer); info.manufacturerDevice = txt["md"]; info.friendlyName = txt["fn"]; } - answer = response[mDNS::ResourceType::A]; + answer = message[mDNS::ResourceType::A]; if(answer != nullptr) { mDNS::Resource::A a(*answer); info.ipaddr = a.getAddress(); @@ -158,14 +158,14 @@ void gotIP(IpAddress ip, IpAddress netmask, IpAddress gateway) Serial.print(F("Connected. Got IP: ")); Serial.println(ip); - finder.onAnswer(handleResponse); + server.onMessage(handleMessage); #ifdef ARCH_HOST - finder.onPacket(savePacket); + server.onPacket(savePacket); #endif auto timer = new Timer; timer->initializeMs<10000>(InterruptCallback([]() { - bool ok = finder.search(String(fstrSearchInstance) + F("._tcp.local")); + bool ok = server.search(String(fstrSearchInstance) + F("._tcp.local")); debug_i("search(): %s", ok ? "OK" : "FAIL"); })); timer->start(); @@ -182,9 +182,9 @@ void parseFile(const String& name, const String& data) Serial.print(_F("** Parsing '")); Serial.print(name); Serial.println(_F("' **")); - mDNS::Response response(0U, 0, const_cast(data.begin()), data.length()); - if(response.parse()) { - printResponse(response); + mDNS::Message message(0U, 0, const_cast(data.begin()), data.length()); + if(message.parse()) { + printMessage(message); } Serial.println(_F("** End of test packet **")); Serial.println(); @@ -193,7 +193,7 @@ void parseFile(const String& name, const String& data) void test() { debug_i("sizeof(mDNS::Server) = %u", sizeof(mDNS::Server)); - debug_i("sizeof(mDNS::Response) = %u", sizeof(mDNS::Response)); + debug_i("sizeof(mDNS::Message) = %u", sizeof(mDNS::Message)); debug_i("sizeof(mDNS::Question) = %u", sizeof(mDNS::Question)); debug_i("sizeof(mDNS::Answer) = %u", sizeof(mDNS::Answer)); debug_i("sizeof(LinkedObject) = %u", sizeof(LinkedObject)); @@ -206,12 +206,12 @@ void test() request.addQuestion(F("_chromecast._tcp.local")); request.addQuestion(F("_%9832479817234_sming._tcp.local"), mDNS::ResourceType::PTR); request.addQuestion(F("_sming._tcp.local"), mDNS::ResourceType::PTR); - Response response(0U, 0, request.getData(), request.getSize()); - response.parse(); - printResponse(response); + Message message(0U, 0, request.getData(), request.getSize()); + message.parse(); + printMessage(message); } - // Create response records + // Create message records { Request request(Request::Type::reply); auto ptr = request.addAnswer(F("_chromecast._tcp.local"), F("my.test.name._tcp.local")); @@ -223,7 +223,7 @@ void test() txt.add("fn=My friendly name"); auto aaaa = request.addAnswer(F("abc._tcp.local"), Ip6Address()); auto srv = request.addAnswer(F("xxxxx._tcp.local"), 1, 2, 8080, F("sillyhouse.net")); - printResponse(request); + printMessage(request); } #ifdef ARCH_HOST From acc1ea02d0c936564b944df7822bc17098f5b571 Mon Sep 17 00:00:00 2001 From: mikee47 Date: Thu, 4 Mar 2021 09:13:08 +0000 Subject: [PATCH 31/52] Use message address/port for sending --- Sming/Components/mdns/src/Request.cpp | 2 +- Sming/Components/mdns/src/Server.cpp | 13 +++---------- .../mdns/src/include/Network/Mdns/Message.h | 12 ++++++++++++ .../mdns/src/include/Network/Mdns/Request.h | 14 ++++++++++++-- samples/Basic_Mdns/app/application.cpp | 4 ++-- 5 files changed, 30 insertions(+), 15 deletions(-) diff --git a/Sming/Components/mdns/src/Request.cpp b/Sming/Components/mdns/src/Request.cpp index db2f1fd88a..55d83472d1 100644 --- a/Sming/Components/mdns/src/Request.cpp +++ b/Sming/Components/mdns/src/Request.cpp @@ -3,7 +3,7 @@ namespace mDNS { -Request::Request(Type type) : Message(0U, 0, buffer, 0) +Request::Request(IpAddress remoteIp, uint16_t remotePort, Type type) : Message(remoteIp, remotePort, buffer, 0) { Packet pkt{data, 0}; diff --git a/Sming/Components/mdns/src/Server.cpp b/Sming/Components/mdns/src/Server.cpp index 5a1f795bd4..b23728f14e 100644 --- a/Sming/Components/mdns/src/Server.cpp +++ b/Sming/Components/mdns/src/Server.cpp @@ -12,19 +12,12 @@ #include "Packet.h" #include -#define MDNS_IP 224, 0, 0, 251 -#define MDNS_TARGET_PORT 5353 -#define MDNS_SOURCE_PORT 5353 -#define MDNS_TTL 255 - -#define MAX_PACKET_SIZE 1024 - namespace mDNS { Server::~Server() { if(initialised) { - UdpConnection::leaveMulticastGroup(IpAddress(MDNS_IP)); + UdpConnection::leaveMulticastGroup(MDNS_IP); } } @@ -42,7 +35,7 @@ bool Server::send(Request& request) initialise(); out.listen(0); - return out.sendTo(IpAddress(MDNS_IP), MDNS_TARGET_PORT, buf, len); + return out.sendTo(request.getRemoteIp(), request.getRemotePort(), buf, len); } bool Server::initialise() @@ -53,7 +46,7 @@ bool Server::initialise() auto localIp = WifiStation.getIP(); - if(!joinMulticastGroup(localIp, IpAddress(MDNS_IP))) { + if(!joinMulticastGroup(localIp, MDNS_IP)) { debug_w("[mDNS] joinMulticastGroup() failed"); return false; } diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Message.h b/Sming/Components/mdns/src/include/Network/Mdns/Message.h index 61fded4f9f..0bc25f7264 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Message.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Message.h @@ -15,6 +15,13 @@ namespace mDNS { +constexpr uint32_t MDNS_IP{0xFB0000E0}; // 224.0.0.251 +constexpr uint16_t MDNS_TARGET_PORT{5353}; +constexpr uint16_t MDNS_SOURCE_PORT{5353}; +constexpr uint16_t MDNS_TTL{255}; + +constexpr uint16_t MAX_PACKET_SIZE{1024}; + /** * @brief Encapsulates a message packet for flexible introspection */ @@ -31,6 +38,11 @@ class Message { } + Message(const Message& other) + : Message(other.getRemoteIp(), other.getRemotePort(), other.getData(), other.getSize()) + { + } + /** * @brief Parse message data * @retval bool true if message parsed successfully, false indicates a problem diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Request.h b/Sming/Components/mdns/src/include/Network/Mdns/Request.h index 7b23682a4d..7298e9e4fe 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Request.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Request.h @@ -7,7 +7,17 @@ namespace mDNS class Request : public Message { public: - Request(Type type); + /** + * @brief Create a unicast message + */ + Request(IpAddress remoteIp, uint16_t remotePort, Type type); + + /** + * @brief Create a multicast message + */ + Request(Type type) : Request(MDNS_IP, MDNS_TARGET_PORT, type) + { + } Question* addQuestion(const String& name, ResourceType type = ResourceType::PTR, uint16_t qclass = 1, bool unicast = false); @@ -31,7 +41,7 @@ class Request : public Message } private: - uint8_t buffer[1024]; + uint8_t buffer[MAX_PACKET_SIZE]; Answer::Kind kind{Answer::Kind::answer}; }; diff --git a/samples/Basic_Mdns/app/application.cpp b/samples/Basic_Mdns/app/application.cpp index 3e5863c166..1bc349b534 100644 --- a/samples/Basic_Mdns/app/application.cpp +++ b/samples/Basic_Mdns/app/application.cpp @@ -200,13 +200,13 @@ void test() using namespace mDNS; - // Create list of questions, serialise them then decode and print the result + // Create list of questions, parse the resulting data and print the result { Request request(Request::Type::query); request.addQuestion(F("_chromecast._tcp.local")); request.addQuestion(F("_%9832479817234_sming._tcp.local"), mDNS::ResourceType::PTR); request.addQuestion(F("_sming._tcp.local"), mDNS::ResourceType::PTR); - Message message(0U, 0, request.getData(), request.getSize()); + Message message(request); message.parse(); printMessage(message); } From 09da4e157578935c9e4cc0e54c943bb9ad05ebcc Mon Sep 17 00:00:00 2001 From: mikee47 Date: Thu, 4 Mar 2021 12:49:42 +0000 Subject: [PATCH 32/52] Add global Server instance --- Sming/Components/mdns/src/Server.cpp | 25 ++++++++++++++----- .../mdns/src/include/Network/Mdns/Server.h | 24 +++++++++--------- samples/Basic_Mdns/app/application.cpp | 8 +++--- 3 files changed, 34 insertions(+), 23 deletions(-) diff --git a/Sming/Components/mdns/src/Server.cpp b/Sming/Components/mdns/src/Server.cpp index b23728f14e..7dd186e76a 100644 --- a/Sming/Components/mdns/src/Server.cpp +++ b/Sming/Components/mdns/src/Server.cpp @@ -14,9 +14,11 @@ namespace mDNS { +Server server; + Server::~Server() { - if(initialised) { + if(active) { UdpConnection::leaveMulticastGroup(MDNS_IP); } } @@ -33,14 +35,14 @@ bool Server::send(Request& request) auto buf = reinterpret_cast(request.getData()); auto len = request.getSize(); - initialise(); + begin(); out.listen(0); return out.sendTo(request.getRemoteIp(), request.getRemotePort(), buf, len); } -bool Server::initialise() +bool Server::begin() { - if(initialised) { + if(active) { return true; } @@ -59,13 +61,24 @@ bool Server::initialise() setMulticast(localIp); setMulticastTtl(MDNS_TTL); - initialised = true; + active = true; return true; } +void Server::end() +{ + if(!active) { + return; + } + + close(); + leaveMulticastGroup(MDNS_IP); + active = false; +} + void Server::UdpOut::onReceive(pbuf* buf, IpAddress remoteIP, uint16_t remotePort) { - finder.onReceive(buf, remoteIP, remotePort); + server.onReceive(buf, remoteIP, remotePort); } void Server::onReceive(pbuf* buf, IpAddress remoteIP, uint16_t remotePort) diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Server.h b/Sming/Components/mdns/src/include/Network/Mdns/Server.h index 7261691e0b..03880461ff 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Server.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Server.h @@ -38,12 +38,18 @@ class Server : protected UdpConnection */ using PacketDelegate = Delegate; - Server() : out(*this) + ~Server(); + + bool begin(); + + void end(); + + bool restart() { + end(); + return begin(); } - ~Server(); - /** * @brief Set callback to be invoked for each received message */ @@ -80,22 +86,16 @@ class Server : protected UdpConnection */ class UdpOut : public UdpConnection { - public: - UdpOut(Server& finder) : finder(finder) - { - } - protected: void onReceive(pbuf* buf, IpAddress remoteIP, uint16_t remotePort) override; - Server& finder; }; - bool initialise(); - MessageDelegate messageCallback; PacketDelegate packetCallback; UdpOut out; - bool initialised{false}; + bool active{false}; }; +extern Server server; + } // namespace mDNS diff --git a/samples/Basic_Mdns/app/application.cpp b/samples/Basic_Mdns/app/application.cpp index 1bc349b534..4a1e9fa7c1 100644 --- a/samples/Basic_Mdns/app/application.cpp +++ b/samples/Basic_Mdns/app/application.cpp @@ -10,8 +10,6 @@ IMPORT_FSTR(testFile, PROJECT_DIR "/resource/192.168.1.100.mdns") #define WIFI_PWD "PleaseEnterPass" #endif -mDNS::Server server; - DEFINE_FSTR_LOCAL(fstrSearchInstance, "_googlecast") #ifdef ARCH_HOST @@ -158,14 +156,14 @@ void gotIP(IpAddress ip, IpAddress netmask, IpAddress gateway) Serial.print(F("Connected. Got IP: ")); Serial.println(ip); - server.onMessage(handleMessage); + mDNS::server.onMessage(handleMessage); #ifdef ARCH_HOST - server.onPacket(savePacket); + mDNS::server.onPacket(savePacket); #endif auto timer = new Timer; timer->initializeMs<10000>(InterruptCallback([]() { - bool ok = server.search(String(fstrSearchInstance) + F("._tcp.local")); + bool ok = mDNS::server.search(String(fstrSearchInstance) + F("._tcp.local")); debug_i("search(): %s", ok ? "OK" : "FAIL"); })); timer->start(); From 99baab37a20917ea72604d0b25985e58d6302043 Mon Sep 17 00:00:00 2001 From: mikee47 Date: Thu, 4 Mar 2021 12:21:02 +0000 Subject: [PATCH 33/52] Use test files on Host only --- samples/Basic_Mdns/app/application.cpp | 39 +++++++++++++++----------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/samples/Basic_Mdns/app/application.cpp b/samples/Basic_Mdns/app/application.cpp index 4a1e9fa7c1..77004e6431 100644 --- a/samples/Basic_Mdns/app/application.cpp +++ b/samples/Basic_Mdns/app/application.cpp @@ -2,8 +2,6 @@ #include #include -IMPORT_FSTR(testFile, PROJECT_DIR "/resource/192.168.1.100.mdns") - // If you want, you can define WiFi settings globally in Eclipse Environment Variables #ifndef WIFI_SSID #define WIFI_SSID "PleaseEnterSSID" // Put you SSID and Password here @@ -83,6 +81,8 @@ void printAnswer(mDNS::Answer& answer) void printMessage(mDNS::Message& message) { Serial.println(); + Serial.print(system_get_time()); + Serial.print(' '); Serial.print(message.isReply() ? F("REPLY") : F("QUERY")); auto ip = message.getRemoteIp(); if(uint32_t(ip) != 0) { @@ -174,13 +174,13 @@ void connectFail(const String& ssid, MacAddress bssid, WifiDisconnectReason reas Serial.println(F("I'm NOT CONNECTED!")); } -void parseFile(const String& name, const String& data) +void parseFile(const String& name, const void* data, size_t length) { Serial.println(); Serial.print(_F("** Parsing '")); Serial.print(name); Serial.println(_F("' **")); - mDNS::Message message(0U, 0, const_cast(data.begin()), data.length()); + mDNS::Message message(0U, 0, const_cast(data), length); if(message.parse()) { printMessage(message); } @@ -188,6 +188,16 @@ void parseFile(const String& name, const String& data) Serial.println(); } +void parseFile(const String& name, const String& data) +{ + parseFile(name, data.c_str(), data.length()); +} + +void parseFile(const String& name, const mDNS::Message& message) +{ + parseFile(name, message.getData(), message.getSize()); +} + void test() { debug_i("sizeof(mDNS::Server) = %u", sizeof(mDNS::Server)); @@ -204,26 +214,26 @@ void test() request.addQuestion(F("_chromecast._tcp.local")); request.addQuestion(F("_%9832479817234_sming._tcp.local"), mDNS::ResourceType::PTR); request.addQuestion(F("_sming._tcp.local"), mDNS::ResourceType::PTR); - Message message(request); - message.parse(); - printMessage(message); + parseFile(F("Test questions"), request); } // Create message records { + // Service is "_test", hostname is "sming" Request request(Request::Type::reply); - auto ptr = request.addAnswer(F("_chromecast._tcp.local"), F("my.test.name._tcp.local")); + auto ptr = request.addAnswer(F("_test._tcp.local"), F("sming._test._tcp.local")); request.nextSection(); request.nextSection(); - auto a = request.addAnswer(F("my.test.name._tcp.local"), 0x12345678); - auto txt = request.addAnswer(F("my.test.name._tcp.local")); + auto txt = request.addAnswer(F("sming._test._tcp.local")); txt.add("pm=12"); txt.add("fn=My friendly name"); - auto aaaa = request.addAnswer(F("abc._tcp.local"), Ip6Address()); - auto srv = request.addAnswer(F("xxxxx._tcp.local"), 1, 2, 8080, F("sillyhouse.net")); - printMessage(request); + auto srv = request.addAnswer(F("sming._test._tcp.local"), 1, 2, 8080, F("sming.local")); + auto a = request.addAnswer(F("sming.local"), WifiStation.getIP()); + auto aaaa = request.addAnswer(F("sming.local"), Ip6Address()); + parseFile(F("Test answers"), request); } +// For host, read the resource directory directly as we might want to add other files there #ifdef ARCH_HOST auto& fs = IFS::Host::getFileSystem(); @@ -235,9 +245,6 @@ void test() parseFile(filename, data); } } - -#else - parseFile(F("testFile"), testFile); #endif } From cd7cea818cd09f3ce7ec96950db61241a2ff4e48 Mon Sep 17 00:00:00 2001 From: mikee47 Date: Thu, 4 Mar 2021 12:46:30 +0000 Subject: [PATCH 34/52] Disable AP for MDNS requests to work --- samples/Basic_Mdns/app/application.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/samples/Basic_Mdns/app/application.cpp b/samples/Basic_Mdns/app/application.cpp index 77004e6431..868b3e6b23 100644 --- a/samples/Basic_Mdns/app/application.cpp +++ b/samples/Basic_Mdns/app/application.cpp @@ -256,6 +256,8 @@ void init() test(); // Setup the WIFI connection + // IMPORTANT: MUST disable AP or multicast won't work + WifiAccessPoint.enable(false); WifiStation.enable(true); WifiStation.config(WIFI_SSID, WIFI_PWD); // Put you SSID and Password here From 488ee618082743416dd2642975b1769ad033de3b Mon Sep 17 00:00:00 2001 From: mikee47 Date: Thu, 4 Mar 2021 16:25:22 +0000 Subject: [PATCH 35/52] Make `writeName` a method of Packet --- Sming/Components/mdns/src/Answer.cpp | 9 ++------- Sming/Components/mdns/src/Message.cpp | 16 ---------------- Sming/Components/mdns/src/Packet.h | 17 +++++++++++++++++ Sming/Components/mdns/src/Question.cpp | 5 +++-- Sming/Components/mdns/src/Resource.cpp | 7 ++++--- .../mdns/src/include/Network/Mdns/Answer.h | 1 - .../mdns/src/include/Network/Mdns/Message.h | 1 - 7 files changed, 26 insertions(+), 30 deletions(-) diff --git a/Sming/Components/mdns/src/Answer.cpp b/Sming/Components/mdns/src/Answer.cpp index 4125c30f76..e5803b6250 100644 --- a/Sming/Components/mdns/src/Answer.cpp +++ b/Sming/Components/mdns/src/Answer.cpp @@ -102,8 +102,8 @@ uint16_t Answer::init(uint16_t namePtr, const String& name, Resource::Type type, uint32_t ttl) { this->namePtr = namePtr; - nameLen = message.writeName(namePtr, name); - Packet pkt{message.resolvePointer(namePtr), nameLen}; + Packet pkt{message.resolvePointer(namePtr)}; + pkt.writeName(name); pkt.write16(uint16_t(type)); // Type pkt.write16((rclass & 0x7fff) | (flush ? 0x8000 : 0)); // Class pkt.write32(ttl); // TTL @@ -120,9 +120,4 @@ void Answer::allocate(uint16_t size) pkt.write16(recordSize); } -uint16_t Answer::writeName(uint16_t ptr, const String& name) -{ - return message.writeName(getRecordPtr() + ptr, name); -} - } // namespace mDNS diff --git a/Sming/Components/mdns/src/Message.cpp b/Sming/Components/mdns/src/Message.cpp index e9b53e3b45..4ffba44e7b 100644 --- a/Sming/Components/mdns/src/Message.cpp +++ b/Sming/Components/mdns/src/Message.cpp @@ -86,20 +86,4 @@ Answer* Message::operator[](ResourceType type) return nullptr; } -uint16_t Message::writeName(uint16_t ptr, const String& name) -{ - Packet pkt{resolvePointer(ptr)}; - size_t pos{0}; - auto namelen = name.length(); - do { - int sep = name.indexOf('.', pos); - auto wordLength = (sep >= 0) ? (sep - pos) : (namelen - pos); - pkt.write8(wordLength); - pkt.write(name.c_str() + pos, wordLength); - pos = sep + 1; - } while(pos > 0); - pkt.write8(0); // End of name. - return pkt.pos; -} - } // namespace mDNS diff --git a/Sming/Components/mdns/src/Packet.h b/Sming/Components/mdns/src/Packet.h index 276a8da447..aa5678a20e 100644 --- a/Sming/Components/mdns/src/Packet.h +++ b/Sming/Components/mdns/src/Packet.h @@ -100,6 +100,23 @@ struct Packet { memcpy(ptr(), s, len); pos += len; } + + void writeName(const String& name) + { + size_t namepos{0}; + auto namelen = name.length(); + while(true) { + int sep = name.indexOf('.', namepos); + auto wordLength = (sep >= 0) ? (sep - namepos) : (namelen - namepos); + write8(wordLength); + write(name.c_str() + namepos, wordLength); + if(sep < 0) { + write8(0); // End of name. + break; + } + namepos = sep + 1; + } + } }; } // namespace mDNS \ No newline at end of file diff --git a/Sming/Components/mdns/src/Question.cpp b/Sming/Components/mdns/src/Question.cpp index ac23418fd2..bba172131a 100644 --- a/Sming/Components/mdns/src/Question.cpp +++ b/Sming/Components/mdns/src/Question.cpp @@ -53,8 +53,9 @@ bool Question::parse(Packet& pkt) uint16_t Question::init(uint16_t namePtr, const String& name, ResourceType type, uint16_t qclass, bool unicast) { this->namePtr = namePtr; - nameLen = message.writeName(namePtr, name); - Packet pkt{message.resolvePointer(namePtr), nameLen}; + Packet pkt{message.resolvePointer(namePtr)}; + pkt.writeName(name); + nameLen = pkt.pos; pkt.write16(uint16_t(type)); qclass &= 0x7fff; if(unicast) { diff --git a/Sming/Components/mdns/src/Resource.cpp b/Sming/Components/mdns/src/Resource.cpp index b0d91cc375..cd1abb144b 100644 --- a/Sming/Components/mdns/src/Resource.cpp +++ b/Sming/Components/mdns/src/Resource.cpp @@ -74,8 +74,9 @@ Name PTR::getName() const void PTR::init(const String& name) { - auto len = answer.writeName(0, name); - answer.allocate(len); + Packet pkt{getRecord()}; + pkt.writeName(name); + answer.allocate(pkt.pos); } /* TXT */ @@ -214,7 +215,7 @@ void SRV::init(uint16_t priority, uint16_t weight, uint16_t port, const String& pkt.write16(priority); pkt.write16(weight); pkt.write16(port); - pkt.pos += answer.writeName(pkt.pos, host); + pkt.writeName(host); answer.allocate(pkt.pos); } diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Answer.h b/Sming/Components/mdns/src/include/Network/Mdns/Answer.h index 71aa90374c..b28ee461d3 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Answer.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Answer.h @@ -107,7 +107,6 @@ class Answer : public LinkedObjectTemplate // Writing uint16_t init(uint16_t namePtr, const String& name, Resource::Type type, uint16_t rclass, bool flush, uint32_t ttl); void allocate(uint16_t size); - uint16_t writeName(uint16_t ptr, const String& name); private: Message& message; diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Message.h b/Sming/Components/mdns/src/include/Network/Mdns/Message.h index 0bc25f7264..fce00640cd 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Message.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Message.h @@ -109,7 +109,6 @@ class Message Answer* operator[](ResourceType type); // Writing - uint16_t writeName(uint16_t ptr, const String& name); void allocate(uint16_t recordSize) { size += recordSize; From 40938dff343b94217debe9335a244adb84c305f2 Mon Sep 17 00:00:00 2001 From: mikee47 Date: Thu, 4 Mar 2021 16:25:41 +0000 Subject: [PATCH 36/52] Support name pointer fixups --- Sming/Components/mdns/src/Name.cpp | 16 ++++++++ Sming/Components/mdns/src/Packet.h | 5 +++ .../mdns/src/include/Network/Mdns/Message.h | 9 +++++ .../mdns/src/include/Network/Mdns/Name.h | 7 ++++ .../mdns/src/include/Network/Mdns/Resource.h | 4 +- samples/Basic_Mdns/app/application.cpp | 40 +++++++++++++++++-- 6 files changed, 76 insertions(+), 5 deletions(-) diff --git a/Sming/Components/mdns/src/Name.cpp b/Sming/Components/mdns/src/Name.cpp index fb5138a783..e58ff2f2c3 100644 --- a/Sming/Components/mdns/src/Name.cpp +++ b/Sming/Components/mdns/src/Name.cpp @@ -101,4 +101,20 @@ String Name::getInstance() const return getString(0, info.components - 2); } +bool Name::fixup(const Name& other) +{ + auto info = parse(); + if(info.dataLength < 2) { + // Can't be a pointer, too small + return false; + } + Packet pkt{message.resolvePointer(ptr + info.dataLength - 2)}; + if(pkt.peek8() < 0xC0) { + // Not a pointer + return false; + } + pkt.write16(other.ptr | 0xC000); + return true; +} + } // namespace mDNS diff --git a/Sming/Components/mdns/src/Packet.h b/Sming/Components/mdns/src/Packet.h index aa5678a20e..24eca8abc1 100644 --- a/Sming/Components/mdns/src/Packet.h +++ b/Sming/Components/mdns/src/Packet.h @@ -108,6 +108,11 @@ struct Packet { while(true) { int sep = name.indexOf('.', namepos); auto wordLength = (sep >= 0) ? (sep - namepos) : (namelen - namepos); + if(wordLength == 0) { + // Name ends in a '.' so add a pointer record; will fixup later + write16(0xC000); + break; + } write8(wordLength); write(name.c_str() + namepos, wordLength); if(sep < 0) { diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Message.h b/Sming/Components/mdns/src/include/Network/Mdns/Message.h index fce00640cd..162c500253 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Message.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Message.h @@ -121,6 +121,15 @@ class Message friend class Question; friend class Answer; friend class Name; + + /* + * Resolve a 16-bit 'pointer' to a memory location + * + * Value represents a 16-bit offset from the start of the message data. + * DNS names may contain pointers, but we use this approach internally to improve + * data portability, reduce memory consumption and avoid data duplication during + * message parsing and construction. + */ uint8_t* resolvePointer(uint16_t pointer) { return data + pointer; diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Name.h b/Sming/Components/mdns/src/include/Network/Mdns/Name.h index 93455c3cf9..1dac497400 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Name.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Name.h @@ -55,6 +55,13 @@ class Name String getService() const; String getInstance() const; + /** + * @brief Fixup pointer at end of name to point to another name + * @param other Where to point to + * @retval bool true on success, false if name does not end with a pointer + */ + bool fixup(const Name& other); + private: struct Info { uint16_t dataLength; diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Resource.h b/Sming/Components/mdns/src/include/Network/Mdns/Resource.h index aaf8a6a1d2..fc1f589186 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Resource.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Resource.h @@ -54,11 +54,11 @@ class Record String toString() const; + Answer& answer; + protected: uint8_t* getRecord() const; uint16_t getRecordSize() const; - - Answer& answer; }; /** diff --git a/samples/Basic_Mdns/app/application.cpp b/samples/Basic_Mdns/app/application.cpp index 868b3e6b23..53df47c0b4 100644 --- a/samples/Basic_Mdns/app/application.cpp +++ b/samples/Basic_Mdns/app/application.cpp @@ -219,17 +219,51 @@ void test() // Create message records { - // Service is "_test", hostname is "sming" + /* + * Service is "_test", hostname is "sming" + * + * We also demonstrate here how to set up a name pointer. + */ Request request(Request::Type::reply); - auto ptr = request.addAnswer(F("_test._tcp.local"), F("sming._test._tcp.local")); + + /** + * Anywhere a Name is used, if the final character is '.' then it is stored as a 'pointer', + * a 16-bit offset into the message data. We don't know the value of that pointer yet, so a + * placeholder is inserted. + * + * In this case, we want our PTR record to reference the name of the SRV record which we add later. + */ + auto ptr = request.addAnswer(F("_test._tcp.local"), "."); + + // Move to 'nameserver' records section request.nextSection(); + // Move to 'additional' records section request.nextSection(); - auto txt = request.addAnswer(F("sming._test._tcp.local")); + + /** + * We'll use a pointer for the name here, saves space. + */ + auto txt = request.addAnswer("."); txt.add("pm=12"); txt.add("fn=My friendly name"); auto srv = request.addAnswer(F("sming._test._tcp.local"), 1, 2, 8080, F("sming.local")); + + /** + * Now the SRV record is constructed we can fix our name references. + * The 'fixup' call will essentially replace the final '.' we added above with a pointer to the actual name. + * Note that the call will fail if we didn't append that final '.', since there will be nowhere to write + * the pointer value. + */ + auto serviceName = srv.answer.getName(); + ptr.getName().fixup(serviceName); + txt.answer.getName().fixup(serviceName); + + /** + * Finally, write the address records + */ auto a = request.addAnswer(F("sming.local"), WifiStation.getIP()); auto aaaa = request.addAnswer(F("sming.local"), Ip6Address()); + parseFile(F("Test answers"), request); } From 79b11a7816edf8c60843f3b72962549c1aabacd0 Mon Sep 17 00:00:00 2001 From: mikee47 Date: Thu, 4 Mar 2021 16:13:11 +0000 Subject: [PATCH 37/52] Add with Name --- Sming/Components/mdns/src/Answer.cpp | 15 +++++++++++++++ Sming/Components/mdns/src/Request.cpp | 10 ++++++++++ .../mdns/src/include/Network/Mdns/Answer.h | 1 + .../mdns/src/include/Network/Mdns/Name.h | 5 +++++ .../mdns/src/include/Network/Mdns/Request.h | 8 ++++---- samples/Basic_Mdns/app/application.cpp | 6 ++++-- 6 files changed, 39 insertions(+), 6 deletions(-) diff --git a/Sming/Components/mdns/src/Answer.cpp b/Sming/Components/mdns/src/Answer.cpp index e5803b6250..ed3fdf65bb 100644 --- a/Sming/Components/mdns/src/Answer.cpp +++ b/Sming/Components/mdns/src/Answer.cpp @@ -104,6 +104,21 @@ uint16_t Answer::init(uint16_t namePtr, const String& name, Resource::Type type, this->namePtr = namePtr; Packet pkt{message.resolvePointer(namePtr)}; pkt.writeName(name); + nameLen = pkt.pos; + pkt.write16(uint16_t(type)); // Type + pkt.write16((rclass & 0x7fff) | (flush ? 0x8000 : 0)); // Class + pkt.write32(ttl); // TTL + pkt.write16(0); // Resource Record Length + return pkt.pos; +} + +uint16_t Answer::init(uint16_t namePtr, const Name& name, Resource::Type type, uint16_t rclass, bool flush, + uint32_t ttl) +{ + this->namePtr = namePtr; + Packet pkt{message.resolvePointer(namePtr)}; + pkt.write16(name.getPtr() | 0xC000); + nameLen = pkt.pos; pkt.write16(uint16_t(type)); // Type pkt.write16((rclass & 0x7fff) | (flush ? 0x8000 : 0)); // Class pkt.write32(ttl); // TTL diff --git a/Sming/Components/mdns/src/Request.cpp b/Sming/Components/mdns/src/Request.cpp index 55d83472d1..11fbce4ef1 100644 --- a/Sming/Components/mdns/src/Request.cpp +++ b/Sming/Components/mdns/src/Request.cpp @@ -48,4 +48,14 @@ Answer* Request::createAnswer(const String& name, Resource::Type type, uint16_t return answer; } +Answer* Request::createAnswer(const Name& name, Resource::Type type, uint16_t rclass, bool flush, uint32_t ttl) +{ + auto answer = new Answer(*this, kind); + answers.add(answer); + Packet pkt{data, uint16_t(6 + unsigned(kind) * 2)}; + pkt.write16(pkt.peek16() + 1); + size += answer->init(size, name, type, rclass, flush, ttl); + return answer; +} + } // namespace mDNS \ No newline at end of file diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Answer.h b/Sming/Components/mdns/src/include/Network/Mdns/Answer.h index b28ee461d3..6bac7b0b83 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Answer.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Answer.h @@ -106,6 +106,7 @@ class Answer : public LinkedObjectTemplate // Writing uint16_t init(uint16_t namePtr, const String& name, Resource::Type type, uint16_t rclass, bool flush, uint32_t ttl); + uint16_t init(uint16_t namePtr, const Name& name, Resource::Type type, uint16_t rclass, bool flush, uint32_t ttl); void allocate(uint16_t size); private: diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Name.h b/Sming/Components/mdns/src/include/Network/Mdns/Name.h index 1dac497400..3ffa13ee2b 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Name.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Name.h @@ -55,6 +55,11 @@ class Name String getService() const; String getInstance() const; + uint16_t getPtr() const + { + return ptr; + } + /** * @brief Fixup pointer at end of name to point to another name * @param other Where to point to diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Request.h b/Sming/Components/mdns/src/include/Network/Mdns/Request.h index 7298e9e4fe..fdeec1698c 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Request.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Request.h @@ -22,12 +22,12 @@ class Request : public Message Question* addQuestion(const String& name, ResourceType type = ResourceType::PTR, uint16_t qclass = 1, bool unicast = false); - Answer* createAnswer(const String& name, Resource::Type type, uint16_t rclass = 1, bool flush = false, - uint32_t ttl = 120); + Answer* createAnswer(const String& name, Resource::Type type, uint16_t rclass, bool flush, uint32_t ttl); + Answer* createAnswer(const Name& name, Resource::Type type, uint16_t rclass, bool flush, uint32_t ttl); - template Resource addAnswer(const String& name, ParamTypes... params) + template Resource addAnswer(const N& name, ParamTypes... params) { - auto answer = createAnswer(name, Resource::type); + auto answer = createAnswer(name, Resource::type, 1, false, 120); Resource r(*answer); r.init(params...); return r; diff --git a/samples/Basic_Mdns/app/application.cpp b/samples/Basic_Mdns/app/application.cpp index 53df47c0b4..2ea6a0f28e 100644 --- a/samples/Basic_Mdns/app/application.cpp +++ b/samples/Basic_Mdns/app/application.cpp @@ -260,9 +260,11 @@ void test() /** * Finally, write the address records + * We can re-use the host Name from the SRV record, storing a pointer instead of the full text */ - auto a = request.addAnswer(F("sming.local"), WifiStation.getIP()); - auto aaaa = request.addAnswer(F("sming.local"), Ip6Address()); + auto hostName = srv.getHost(); + auto a = request.addAnswer(hostName, WifiStation.getIP()); + auto aaaa = request.addAnswer(hostName, Ip6Address()); parseFile(F("Test answers"), request); } From 7343dd957568e90dc0d4d2e6419b5343c157b291 Mon Sep 17 00:00:00 2001 From: mikee47 Date: Thu, 4 Mar 2021 08:54:19 +0000 Subject: [PATCH 38/52] Update README --- Sming/Components/mdns/README.rst | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/Sming/Components/mdns/README.rst b/Sming/Components/mdns/README.rst index b5714dd685..e3f6ebe529 100644 --- a/Sming/Components/mdns/README.rst +++ b/Sming/Components/mdns/README.rst @@ -5,14 +5,14 @@ mDNS: Multicast Domain Name System https://en.wikipedia.org/wiki/Multicast_DNS -Sming provides the :cpp:class:`mDNS::Responder` class to allow applications -to advertise themselves on the local network. -Issuing discovery requests is not currently supported. +Responder +--------- +Sming provides the :cpp:class:`mDNS::Responder` class to allow applications +to advertise themselves on the local network. -Using ------ +To use: 1. Add ``COMPONENT_DEPENDS += mdns`` to your application componenent.mk file. 2. Add these lines to your application:: @@ -35,6 +35,18 @@ and call :cpp:func:`mDNS::Responder::addService`. See the :sample:`UdpServer_mDNS` sample application. +Discovery +--------- + +This library also provides support for device discovery using a separate set of classes, +based on the :cpp:class:`mDNS::Server`. +See :sample:`Basic_Mdns` for an example. + +.. note:: + + The mDNS Server and Responder cannot currently be used together. + + Testing ------- @@ -47,6 +59,16 @@ to perform mDNS queries and confirm output is as expected: avahi-browse --all -r +References +---------- + +- Multicast DNS RFC6762 https://tools.ietf.org/html/rfc6762 +- Zero-configuration networking (DNS-SD) https://en.wikipedia.org/wiki/Zero-configuration_networking +- DNS-Based Service Discovery https://tools.ietf.org/html/rfc6763 +- DNS record types https://en.wikipedia.org/wiki/List_of_DNS_record_types +- Domain Names: Implementation and Specification https://tools.ietf.org/html/rfc1035 + + API Documentation ----------------- From 65f392a0c1db564a82e47f6d5183337a423c2e04 Mon Sep 17 00:00:00 2001 From: mikee47 Date: Thu, 4 Mar 2021 17:35:56 +0000 Subject: [PATCH 39/52] Comment headers --- Sming/Components/mdns/src/Request.cpp | 10 ++++++++++ .../Components/mdns/src/include/Network/Mdns/Request.h | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/Sming/Components/mdns/src/Request.cpp b/Sming/Components/mdns/src/Request.cpp index 11fbce4ef1..30b2b77f36 100644 --- a/Sming/Components/mdns/src/Request.cpp +++ b/Sming/Components/mdns/src/Request.cpp @@ -1,3 +1,13 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * Request.cpp + * + ****/ + #include "include/Network/Mdns/Request.h" #include "Packet.h" diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Request.h b/Sming/Components/mdns/src/include/Network/Mdns/Request.h index fdeec1698c..6e495ca56f 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Request.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Request.h @@ -1,3 +1,13 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * Request.h + * + ****/ + #pragma once #include "Message.h" From cb3212f95f12ed189bcc5affdd29dedd9c64c407 Mon Sep 17 00:00:00 2001 From: mikee47 Date: Fri, 5 Mar 2021 08:23:59 +0000 Subject: [PATCH 40/52] Move `Packet.h` (without writeName) into `Core/Data`, implement `NetworkPacket` and `HostPacket` Old compilers require constructor for inherited classes --- Sming/Components/mdns/src/Packet.h | 87 ++---------------------------- Sming/Core/Data/HostPacket.h | 47 ++++++++++++++++ Sming/Core/Data/NetworkPacket.h | 47 ++++++++++++++++ Sming/Core/Data/Packet.h | 78 +++++++++++++++++++++++++++ 4 files changed, 175 insertions(+), 84 deletions(-) create mode 100644 Sming/Core/Data/HostPacket.h create mode 100644 Sming/Core/Data/NetworkPacket.h create mode 100644 Sming/Core/Data/Packet.h diff --git a/Sming/Components/mdns/src/Packet.h b/Sming/Components/mdns/src/Packet.h index 24eca8abc1..0d265bdd05 100644 --- a/Sming/Components/mdns/src/Packet.h +++ b/Sming/Components/mdns/src/Packet.h @@ -10,96 +10,15 @@ #pragma once -#include +#include namespace mDNS { /** * @brief Helper class for reading/writing packet content */ -struct Packet { - uint8_t* data; - mutable uint16_t pos; - - const uint8_t* ptr() const - { - return data + pos; - } - - uint8_t* ptr() - { - return data + pos; - } - - void skip(uint16_t len) const - { - pos += len; - } - - uint8_t peek8() const - { - return data[pos]; - } - - uint8_t read8() const - { - return data[pos++]; - } - - uint16_t peek16() const - { - return (data[pos] << 8) | data[pos + 1]; - } - - uint16_t read16() const - { - return (read8() << 8) | read8(); - } - - uint32_t read32() const - { - return (read16() << 16) | read16(); - } - - void read(void* buffer, uint16_t len) const - { - memcpy(buffer, ptr(), len); - pos += len; - } - - String readString(uint16_t length) const - { - String s; - if(s.setLength(length)) { - read(s.begin(), length); - } else { - pos += length; - } - return s; - } - - void write8(uint8_t value) - { - data[pos++] = value; - } - - void write16(uint16_t value) - { - write8(value >> 8); - write8(value & 0xff); - } - - void write32(uint32_t value) - { - write16(value >> 16); - write16(value & 0xffff); - } - - void write(const void* s, uint16_t len) - { - memcpy(ptr(), s, len); - pos += len; - } +struct Packet : public NetworkPacket { + using NetworkPacket::NetworkPacket; void writeName(const String& name) { diff --git a/Sming/Core/Data/HostPacket.h b/Sming/Core/Data/HostPacket.h new file mode 100644 index 0000000000..975deb7add --- /dev/null +++ b/Sming/Core/Data/HostPacket.h @@ -0,0 +1,47 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * HostPacket.h + * + ****/ + +#pragma once + +#include "Packet.h" + +/** + * @brief Helper class for reading/writing packet content in host byte-order (LSB first) + */ +struct HostPacket : public Packet { + using Packet::Packet; + + uint16_t peek16() const + { + return data[pos] | (data[pos + 1] << 8); + } + + uint16_t read16() const + { + return read8() | (read8() << 8); + } + + uint32_t read32() const + { + return read16() | (read16() << 16); + } + + void write16(uint16_t value) + { + write8(value & 0xff); + write8(value >> 8); + } + + void write32(uint32_t value) + { + write16(value & 0xffff); + write16(value >> 16); + } +}; diff --git a/Sming/Core/Data/NetworkPacket.h b/Sming/Core/Data/NetworkPacket.h new file mode 100644 index 0000000000..e52c516f23 --- /dev/null +++ b/Sming/Core/Data/NetworkPacket.h @@ -0,0 +1,47 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * NetworkPacket.h + * + ****/ + +#pragma once + +#include "Packet.h" + +/** + * @brief Helper class for reading/writing packet content in network byte-order (MSB first) + */ +struct NetworkPacket : public Packet { + using Packet::Packet; + + uint16_t peek16() const + { + return (data[pos] << 8) | data[pos + 1]; + } + + uint16_t read16() const + { + return (read8() << 8) | read8(); + } + + uint32_t read32() const + { + return (read16() << 16) | read16(); + } + + void write16(uint16_t value) + { + write8(value >> 8); + write8(value & 0xff); + } + + void write32(uint32_t value) + { + write16(value >> 16); + write16(value & 0xffff); + } +}; diff --git a/Sming/Core/Data/Packet.h b/Sming/Core/Data/Packet.h new file mode 100644 index 0000000000..fcb74eb114 --- /dev/null +++ b/Sming/Core/Data/Packet.h @@ -0,0 +1,78 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * Packet.h + * + ****/ + +#pragma once + +#include + +/** + * @brief Helper class for reading/writing packet content + */ +struct Packet { + uint8_t* data; + mutable uint16_t pos; + + Packet(void* data, uint16_t pos = 0) : data(static_cast(data)), pos(pos) + { + } + + const uint8_t* ptr() const + { + return data + pos; + } + + uint8_t* ptr() + { + return data + pos; + } + + void skip(uint16_t len) const + { + pos += len; + } + + uint8_t peek8() const + { + return data[pos]; + } + + uint8_t read8() const + { + return data[pos++]; + } + + void read(void* buffer, uint16_t len) const + { + memcpy(buffer, ptr(), len); + pos += len; + } + + String readString(uint16_t length) const + { + String s; + if(s.setLength(length)) { + read(s.begin(), length); + } else { + pos += length; + } + return s; + } + + void write8(uint8_t value) + { + data[pos++] = value; + } + + void write(const void* s, uint16_t len) + { + memcpy(ptr(), s, len); + pos += len; + } +}; From 823ff8d092583bd418dc55fddf152ed7529f88ca Mon Sep 17 00:00:00 2001 From: mikee47 Date: Fri, 5 Mar 2021 09:44:58 +0000 Subject: [PATCH 41/52] Move HostPacket and NetworkPacket into `Packet.h` --- Sming/Components/mdns/src/Packet.h | 2 +- Sming/Core/Data/HostPacket.h | 47 --------------------- Sming/Core/Data/NetworkPacket.h | 47 --------------------- Sming/Core/Data/Packet.h | 68 ++++++++++++++++++++++++++++++ 4 files changed, 69 insertions(+), 95 deletions(-) delete mode 100644 Sming/Core/Data/HostPacket.h delete mode 100644 Sming/Core/Data/NetworkPacket.h diff --git a/Sming/Components/mdns/src/Packet.h b/Sming/Components/mdns/src/Packet.h index 0d265bdd05..fe94eca4e0 100644 --- a/Sming/Components/mdns/src/Packet.h +++ b/Sming/Components/mdns/src/Packet.h @@ -10,7 +10,7 @@ #pragma once -#include +#include namespace mDNS { diff --git a/Sming/Core/Data/HostPacket.h b/Sming/Core/Data/HostPacket.h deleted file mode 100644 index 975deb7add..0000000000 --- a/Sming/Core/Data/HostPacket.h +++ /dev/null @@ -1,47 +0,0 @@ -/**** - * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. - * Created 2015 by Skurydin Alexey - * http://github.com/SmingHub/Sming - * All files of the Sming Core are provided under the LGPL v3 license. - * - * HostPacket.h - * - ****/ - -#pragma once - -#include "Packet.h" - -/** - * @brief Helper class for reading/writing packet content in host byte-order (LSB first) - */ -struct HostPacket : public Packet { - using Packet::Packet; - - uint16_t peek16() const - { - return data[pos] | (data[pos + 1] << 8); - } - - uint16_t read16() const - { - return read8() | (read8() << 8); - } - - uint32_t read32() const - { - return read16() | (read16() << 16); - } - - void write16(uint16_t value) - { - write8(value & 0xff); - write8(value >> 8); - } - - void write32(uint32_t value) - { - write16(value & 0xffff); - write16(value >> 16); - } -}; diff --git a/Sming/Core/Data/NetworkPacket.h b/Sming/Core/Data/NetworkPacket.h deleted file mode 100644 index e52c516f23..0000000000 --- a/Sming/Core/Data/NetworkPacket.h +++ /dev/null @@ -1,47 +0,0 @@ -/**** - * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. - * Created 2015 by Skurydin Alexey - * http://github.com/SmingHub/Sming - * All files of the Sming Core are provided under the LGPL v3 license. - * - * NetworkPacket.h - * - ****/ - -#pragma once - -#include "Packet.h" - -/** - * @brief Helper class for reading/writing packet content in network byte-order (MSB first) - */ -struct NetworkPacket : public Packet { - using Packet::Packet; - - uint16_t peek16() const - { - return (data[pos] << 8) | data[pos + 1]; - } - - uint16_t read16() const - { - return (read8() << 8) | read8(); - } - - uint32_t read32() const - { - return (read16() << 16) | read16(); - } - - void write16(uint16_t value) - { - write8(value >> 8); - write8(value & 0xff); - } - - void write32(uint32_t value) - { - write16(value >> 16); - write16(value & 0xffff); - } -}; diff --git a/Sming/Core/Data/Packet.h b/Sming/Core/Data/Packet.h index fcb74eb114..b37a8ada06 100644 --- a/Sming/Core/Data/Packet.h +++ b/Sming/Core/Data/Packet.h @@ -76,3 +76,71 @@ struct Packet { pos += len; } }; + +/** + * @brief Helper class for reading/writing packet content in network byte-order (MSB first) + */ +struct NetworkPacket : public Packet { + using Packet::Packet; + + uint16_t peek16() const + { + return (data[pos] << 8) | data[pos + 1]; + } + + uint16_t read16() const + { + return (read8() << 8) | read8(); + } + + uint32_t read32() const + { + return (read16() << 16) | read16(); + } + + void write16(uint16_t value) + { + write8(value >> 8); + write8(value & 0xff); + } + + void write32(uint32_t value) + { + write16(value >> 16); + write16(value & 0xffff); + } +}; + +/** + * @brief Helper class for reading/writing packet content in host byte-order (LSB first) + */ +struct HostPacket : public Packet { + using Packet::Packet; + + uint16_t peek16() const + { + return data[pos] | (data[pos + 1] << 8); + } + + uint16_t read16() const + { + return read8() | (read8() << 8); + } + + uint32_t read32() const + { + return read16() | (read16() << 16); + } + + void write16(uint16_t value) + { + write8(value & 0xff); + write8(value >> 8); + } + + void write32(uint32_t value) + { + write16(value & 0xffff); + write16(value >> 16); + } +}; From 944f025ebd1db1d4784621cb34ec7f5efc1ec9ff Mon Sep 17 00:00:00 2001 From: mikee47 Date: Fri, 5 Mar 2021 09:47:06 +0000 Subject: [PATCH 42/52] In Basic_Mdns sample, send initial search immediately then repeat every 30 seconds --- samples/Basic_Mdns/app/application.cpp | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/samples/Basic_Mdns/app/application.cpp b/samples/Basic_Mdns/app/application.cpp index 2ea6a0f28e..c1aca9a748 100644 --- a/samples/Basic_Mdns/app/application.cpp +++ b/samples/Basic_Mdns/app/application.cpp @@ -151,6 +151,18 @@ void handleMessage(mDNS::Message& message) Serial.println(info.ipaddr); } +void sendSearch() +{ + String name(fstrSearchInstance); + name += _F("._tcp.local"); + + // To discover all DNS-SD registered services, use: + // name = F("_services._dns-sd._udp.local"); + + bool ok = mDNS::server.search(name); + debug_i("search('%s'): %s", name.c_str(), ok ? "OK" : "FAIL"); +} + void gotIP(IpAddress ip, IpAddress netmask, IpAddress gateway) { Serial.print(F("Connected. Got IP: ")); @@ -161,11 +173,12 @@ void gotIP(IpAddress ip, IpAddress netmask, IpAddress gateway) mDNS::server.onPacket(savePacket); #endif + // Issue a search now + sendSearch(); + + // Repeat every 30 seconds auto timer = new Timer; - timer->initializeMs<10000>(InterruptCallback([]() { - bool ok = mDNS::server.search(String(fstrSearchInstance) + F("._tcp.local")); - debug_i("search(): %s", ok ? "OK" : "FAIL"); - })); + timer->initializeMs<30000>(sendSearch); timer->start(); } From cb9ff5ae792d7f3930839ba695d21c0a3ee85481 Mon Sep 17 00:00:00 2001 From: mikee47 Date: Fri, 5 Mar 2021 16:24:49 +0000 Subject: [PATCH 43/52] Add mDNS debug module --- Sming/Components/mdns/src/debug.cpp | 88 +++++++++++++++++++ .../mdns/src/include/Network/Mdns/debug.h | 12 +++ samples/Basic_Mdns/app/application.cpp | 84 +----------------- 3 files changed, 103 insertions(+), 81 deletions(-) create mode 100644 Sming/Components/mdns/src/debug.cpp create mode 100644 Sming/Components/mdns/src/include/Network/Mdns/debug.h diff --git a/Sming/Components/mdns/src/debug.cpp b/Sming/Components/mdns/src/debug.cpp new file mode 100644 index 0000000000..4119dedc30 --- /dev/null +++ b/Sming/Components/mdns/src/debug.cpp @@ -0,0 +1,88 @@ +#include "Network/Mdns/debug.h" + +namespace +{ +void print(Print& p, const String& tag, const String& value) +{ + String s = " "; + s += tag; + s += ':'; + while(s.length() < 12) { + s += ' '; + } + p.print(s); + p.print(' '); + p.println(value); +} + +void printHex(Print& p, const String& tag, uint16_t value) +{ + char buf[10]; + m_snprintf(buf, sizeof(buf), "0x%04x", value); + print(p, tag, buf); +} + +void printBool(Print& p, const String& tag, bool value) +{ + print(p, tag, value ? "Y" : "N"); +} + +} // namespace + +namespace mDNS +{ +void printQuestion(Print& p, mDNS::Question& question) +{ + p.println(F(">> Question")); + print(p, F("name"), question.getName()); + auto type = question.getType(); + print(p, F("type"), toString(type)); + printHex(p, F("type"), uint16_t(type)); + printHex(p, F("class"), question.getClass()); + printBool(p, F("unicast"), question.isUnicastReply()); +} + +void printAnswer(Print& p, mDNS::Answer& answer) +{ + p.print(">> "); + p.println(toString(answer.getKind())); + print(p, F("name"), answer.getName()); + print(p, F("data"), answer.getRecordString()); + auto type = answer.getType(); + print(p, F("type"), toString(type)); + printHex(p, F("type"), uint16_t(type)); + print(p, F("ttl"), String(answer.getTtl())); + printHex(p, F("class"), answer.getClass()); + printBool(p, F("flush"), answer.isCachedFlush()); +} + +void printMessage(Print& p, mDNS::Message& message) +{ + p.println(); + p.print(system_get_time()); + p.print(' '); + p.print(message.isReply() ? F("REPLY") : F("QUERY")); + auto ip = message.getRemoteIp(); + if(uint32_t(ip) != 0) { + p.print(F(" from ")); + p.print(message.getRemoteIp().toString()); + p.print(':'); + p.println(message.getRemotePort()); + } else { + p.println(); + } + + p.print(F("Size: ")); + p.print(message.getSize()); + p.println(F(" bytes")); + + for(auto& question : message.questions) { + printQuestion(p, question); + } + + for(auto& answer : message.answers) { + printAnswer(p, answer); + } +} + +} // namespace mDNS diff --git a/Sming/Components/mdns/src/include/Network/Mdns/debug.h b/Sming/Components/mdns/src/include/Network/Mdns/debug.h new file mode 100644 index 0000000000..b29aacbd54 --- /dev/null +++ b/Sming/Components/mdns/src/include/Network/Mdns/debug.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include "Message.h" + +namespace mDNS +{ +void printQuestion(Print& p, Question& question); +void printAnswer(Print& p, Answer& answer); +void printMessage(Print& p, Message& message); + +} // namespace mDNS diff --git a/samples/Basic_Mdns/app/application.cpp b/samples/Basic_Mdns/app/application.cpp index c1aca9a748..10b7710edc 100644 --- a/samples/Basic_Mdns/app/application.cpp +++ b/samples/Basic_Mdns/app/application.cpp @@ -1,5 +1,6 @@ #include #include +#include #include // If you want, you can define WiFi settings globally in Eclipse Environment Variables @@ -28,88 +29,9 @@ void savePacket(IpAddress remoteIP, uint16_t remotePort, const uint8_t* data, si } #endif -void print(const String& tag, const String& value) -{ - String s = " "; - s += tag; - s += ':'; - while(s.length() < 12) { - s += ' '; - } - Serial.print(s); - Serial.print(' '); - Serial.println(value); -} - -void printHex(const String& tag, uint16_t value) -{ - char buf[10]; - m_snprintf(buf, sizeof(buf), "0x%04x", value); - print(tag, buf); -} - -void printBool(const String& tag, bool value) -{ - print(tag, value ? "Y" : "N"); -} - -void printQuestion(mDNS::Question& question) -{ - Serial.println(F(">> Question")); - print(F("name"), question.getName()); - auto type = question.getType(); - print(F("type"), toString(type)); - printHex(F("type"), uint16_t(type)); - printHex(F("class"), question.getClass()); - printBool(F("unicast"), question.isUnicastReply()); -} - -void printAnswer(mDNS::Answer& answer) -{ - Serial.print(">> "); - Serial.println(toString(answer.getKind())); - print(F("name"), answer.getName()); - print(F("data"), answer.getRecordString()); - auto type = answer.getType(); - print(F("type"), toString(type)); - printHex(F("type"), uint16_t(type)); - print(F("ttl"), String(answer.getTtl())); - printHex(F("class"), answer.getClass()); - printBool(F("flush"), answer.isCachedFlush()); -} - -void printMessage(mDNS::Message& message) -{ - Serial.println(); - Serial.print(system_get_time()); - Serial.print(' '); - Serial.print(message.isReply() ? F("REPLY") : F("QUERY")); - auto ip = message.getRemoteIp(); - if(uint32_t(ip) != 0) { - Serial.print(F(" from ")); - Serial.print(message.getRemoteIp().toString()); - Serial.print(':'); - Serial.println(message.getRemotePort()); - } else { - Serial.println(); - } - - Serial.print(F("Size: ")); - Serial.print(message.getSize()); - Serial.println(F(" bytes")); - - for(auto& question : message.questions) { - printQuestion(question); - } - - for(auto& answer : message.answers) { - printAnswer(answer); - } -} - void handleMessage(mDNS::Message& message) { - printMessage(message); + mDNS::printMessage(Serial, message); // Check if we're interested in this reponse if(!message.isReply()) { @@ -195,7 +117,7 @@ void parseFile(const String& name, const void* data, size_t length) Serial.println(_F("' **")); mDNS::Message message(0U, 0, const_cast(data), length); if(message.parse()) { - printMessage(message); + mDNS::printMessage(Serial, message); } Serial.println(_F("** End of test packet **")); Serial.println(); From b31f5614fd8190f9aa3cc94b4712b73587b15414 Mon Sep 17 00:00:00 2001 From: mikee47 Date: Fri, 5 Mar 2021 16:26:03 +0000 Subject: [PATCH 44/52] Use `Message` in Server::send --- Sming/Components/mdns/src/Server.cpp | 10 +++++----- .../Components/mdns/src/include/Network/Mdns/Server.h | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Sming/Components/mdns/src/Server.cpp b/Sming/Components/mdns/src/Server.cpp index 7dd186e76a..0ca242ff3b 100644 --- a/Sming/Components/mdns/src/Server.cpp +++ b/Sming/Components/mdns/src/Server.cpp @@ -26,18 +26,18 @@ Server::~Server() bool Server::search(const String& name, ResourceType type) { Request req(Request::Type::query); - auto question = req.addQuestion(name, type); + req.addQuestion(name, type); return send(req); } -bool Server::send(Request& request) +bool Server::send(Message& message) { - auto buf = reinterpret_cast(request.getData()); - auto len = request.getSize(); + auto buf = reinterpret_cast(message.getData()); + auto len = message.getSize(); begin(); out.listen(0); - return out.sendTo(request.getRemoteIp(), request.getRemotePort(), buf, len); + return out.sendTo(message.getRemoteIp(), message.getRemotePort(), buf, len); } bool Server::begin() diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Server.h b/Sming/Components/mdns/src/include/Network/Mdns/Server.h index 03880461ff..b1a4319b4b 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Server.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Server.h @@ -72,10 +72,10 @@ class Server : protected UdpConnection bool search(const String& hostname, ResourceType type = ResourceType::PTR); /** - * @brief Send an mDNS request containing questions/answers - * @retval bool true if request sent successfully + * @brief Send an mDNS message containing questions/answers + * @retval bool true if message sent successfully */ - bool send(Request& request); + bool send(Message& message); protected: void onReceive(pbuf* buf, IpAddress remoteIP, uint16_t remotePort) override; From 628e1e74fcbf66a4f4a76fb670cfb64c7b1f7e0f Mon Sep 17 00:00:00 2001 From: mikee47 Date: Fri, 5 Mar 2021 20:58:41 +0000 Subject: [PATCH 45/52] Add `Query` and `Reply` classes to simplify code --- Sming/Components/mdns/src/Request.cpp | 2 +- .../mdns/src/include/Network/Mdns/Request.h | 30 ++++++++++++++----- samples/Basic_Mdns/app/application.cpp | 28 ++++++++--------- 3 files changed, 37 insertions(+), 23 deletions(-) diff --git a/Sming/Components/mdns/src/Request.cpp b/Sming/Components/mdns/src/Request.cpp index 30b2b77f36..8637eaaea7 100644 --- a/Sming/Components/mdns/src/Request.cpp +++ b/Sming/Components/mdns/src/Request.cpp @@ -13,7 +13,7 @@ namespace mDNS { -Request::Request(IpAddress remoteIp, uint16_t remotePort, Type type) : Message(remoteIp, remotePort, buffer, 0) +Request::Request(Type type) : Message(MDNS_IP, MDNS_TARGET_PORT, buffer, 0) { Packet pkt{data, 0}; diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Request.h b/Sming/Components/mdns/src/include/Network/Mdns/Request.h index 6e495ca56f..cfa9f7482e 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Request.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Request.h @@ -17,17 +17,10 @@ namespace mDNS class Request : public Message { public: - /** - * @brief Create a unicast message - */ - Request(IpAddress remoteIp, uint16_t remotePort, Type type); - /** * @brief Create a multicast message */ - Request(Type type) : Request(MDNS_IP, MDNS_TARGET_PORT, type) - { - } + Request(Type type); Question* addQuestion(const String& name, ResourceType type = ResourceType::PTR, uint16_t qclass = 1, bool unicast = false); @@ -55,4 +48,25 @@ class Request : public Message Answer::Kind kind{Answer::Kind::answer}; }; +class Query : public Request +{ +public: + Query() : Request(Type::query) + { + } +}; + +class Reply : public Request +{ +public: + Reply(const Question& question) : Request(Type::reply) + { + if(question.isUnicastReply()) { + auto& msg = question.getMessage(); + remoteIp = msg.getRemoteIp(); + remotePort = msg.getRemotePort(); + } + } +}; + } // namespace mDNS diff --git a/samples/Basic_Mdns/app/application.cpp b/samples/Basic_Mdns/app/application.cpp index 10b7710edc..3bc7155ecd 100644 --- a/samples/Basic_Mdns/app/application.cpp +++ b/samples/Basic_Mdns/app/application.cpp @@ -145,11 +145,11 @@ void test() // Create list of questions, parse the resulting data and print the result { - Request request(Request::Type::query); - request.addQuestion(F("_chromecast._tcp.local")); - request.addQuestion(F("_%9832479817234_sming._tcp.local"), mDNS::ResourceType::PTR); - request.addQuestion(F("_sming._tcp.local"), mDNS::ResourceType::PTR); - parseFile(F("Test questions"), request); + Query query; + query.addQuestion(F("_chromecast._tcp.local")); + query.addQuestion(F("_%9832479817234_sming._tcp.local"), mDNS::ResourceType::PTR); + query.addQuestion(F("_sming._tcp.local"), mDNS::ResourceType::PTR); + parseFile(F("Test questions"), query); } // Create message records @@ -159,7 +159,7 @@ void test() * * We also demonstrate here how to set up a name pointer. */ - Request request(Request::Type::reply); + Request reply(Request::Type::reply); /** * Anywhere a Name is used, if the final character is '.' then it is stored as a 'pointer', @@ -168,20 +168,20 @@ void test() * * In this case, we want our PTR record to reference the name of the SRV record which we add later. */ - auto ptr = request.addAnswer(F("_test._tcp.local"), "."); + auto ptr = reply.addAnswer(F("_test._tcp.local"), "."); // Move to 'nameserver' records section - request.nextSection(); + reply.nextSection(); // Move to 'additional' records section - request.nextSection(); + reply.nextSection(); /** * We'll use a pointer for the name here, saves space. */ - auto txt = request.addAnswer("."); + auto txt = reply.addAnswer("."); txt.add("pm=12"); txt.add("fn=My friendly name"); - auto srv = request.addAnswer(F("sming._test._tcp.local"), 1, 2, 8080, F("sming.local")); + auto srv = reply.addAnswer(F("sming._test._tcp.local"), 1, 2, 8080, F("sming.local")); /** * Now the SRV record is constructed we can fix our name references. @@ -198,10 +198,10 @@ void test() * We can re-use the host Name from the SRV record, storing a pointer instead of the full text */ auto hostName = srv.getHost(); - auto a = request.addAnswer(hostName, WifiStation.getIP()); - auto aaaa = request.addAnswer(hostName, Ip6Address()); + auto a = reply.addAnswer(hostName, WifiStation.getIP()); + auto aaaa = reply.addAnswer(hostName, Ip6Address()); - parseFile(F("Test answers"), request); + parseFile(F("Test answers"), reply); } // For host, read the resource directory directly as we might want to add other files there From 5c6efe377d9ea1dfae36190aa31176e21b034380 Mon Sep 17 00:00:00 2001 From: mikee47 Date: Fri, 5 Mar 2021 16:25:21 +0000 Subject: [PATCH 46/52] Add += operator to TXT --- Sming/Components/mdns/src/Resource.cpp | 5 ++--- .../mdns/src/include/Network/Mdns/Resource.h | 19 ++++++++++++++++++- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/Sming/Components/mdns/src/Resource.cpp b/Sming/Components/mdns/src/Resource.cpp index cd1abb144b..b9c7bb0a61 100644 --- a/Sming/Components/mdns/src/Resource.cpp +++ b/Sming/Components/mdns/src/Resource.cpp @@ -146,12 +146,11 @@ const char* TXT::get(uint8_t index, uint8_t& len) const return nullptr; } -void TXT::add(const String& value) +void TXT::add(const char* value, uint16_t len) { Packet pkt{getRecord(), getRecordSize()}; - auto len = value.length(); pkt.write8(len); - pkt.write(value.c_str(), len); + pkt.write(value, len); answer.allocate(pkt.pos); } diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Resource.h b/Sming/Components/mdns/src/include/Network/Mdns/Resource.h index fc1f589186..d58953da4c 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Resource.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Resource.h @@ -160,7 +160,24 @@ class TXT : public Record { } - void add(const String& value); + void add(const char* value, uint16_t len); + + void add(const String& value) + { + add(value.c_str(), value.length()); + } + + TXT& operator+=(const char* value) + { + add(value, strlen(value)); + return *this; + } + + TXT& operator+=(const String& value) + { + add(value); + return *this; + } private: const char* get(uint8_t index, uint8_t& len) const; From 598eaefc8360c2bf8cba63bae88b0e2be1387113 Mon Sep 17 00:00:00 2001 From: mikee47 Date: Fri, 5 Mar 2021 21:15:54 +0000 Subject: [PATCH 47/52] Add `onSend` callback for mDNS server (debugging) --- Sming/Components/mdns/src/Server.cpp | 12 ++++++++++- .../mdns/src/include/Network/Mdns/Server.h | 21 +++++++++++++++++-- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/Sming/Components/mdns/src/Server.cpp b/Sming/Components/mdns/src/Server.cpp index 0ca242ff3b..f4d2497651 100644 --- a/Sming/Components/mdns/src/Server.cpp +++ b/Sming/Components/mdns/src/Server.cpp @@ -32,6 +32,10 @@ bool Server::search(const String& name, ResourceType type) bool Server::send(Message& message) { + if(sendCallback) { + sendCallback(message); + } + auto buf = reinterpret_cast(message.getData()); auto len = message.getSize(); @@ -84,7 +88,9 @@ void Server::UdpOut::onReceive(pbuf* buf, IpAddress remoteIP, uint16_t remotePor void Server::onReceive(pbuf* buf, IpAddress remoteIP, uint16_t remotePort) { if(packetCallback) { - packetCallback(remoteIP, remotePort, static_cast(buf->payload), buf->len); + if(!packetCallback(remoteIP, remotePort, static_cast(buf->payload), buf->len)) { + return; + } } if(!messageCallback) { @@ -93,6 +99,10 @@ void Server::onReceive(pbuf* buf, IpAddress remoteIP, uint16_t remotePort) Message message(remoteIP, remotePort, buf->payload, buf->len); if(message.parse()) { + /* + * TODO: Establish callback chain/queue to support multiple clients. + * If a client returns false then don't pass it any further. + */ messageCallback(message); } } diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Server.h b/Sming/Components/mdns/src/include/Network/Mdns/Server.h index b1a4319b4b..1b848a2fe5 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Server.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Server.h @@ -30,13 +30,15 @@ class Server : protected UdpConnection public: /** * @brief Callback to be invoked for each received message + * @retval bool Depends on operation */ - using MessageDelegate = Delegate; + using MessageDelegate = Delegate; /** * @brief Callback to be invoked with raw data (debugging, etc.) + * @retval bool Depends on operation */ - using PacketDelegate = Delegate; + using PacketDelegate = Delegate; ~Server(); @@ -52,12 +54,26 @@ class Server : protected UdpConnection /** * @brief Set callback to be invoked for each received message + * @param callback Return false from callback to prevent message being passed to other clients */ void onMessage(MessageDelegate callback) { messageCallback = callback; } + /** + * @brief Set callback to be invoked before sending a message + * @param callback Return true from callback to actually send packet + */ + void onSend(MessageDelegate callback) + { + sendCallback = callback; + } + + /** + * @brief Set callback to be invoked for raw received data, before parsing + * @param callback Return true from callback to actually send packet + */ void onPacket(PacketDelegate callback) { packetCallback = callback; @@ -91,6 +107,7 @@ class Server : protected UdpConnection }; MessageDelegate messageCallback; + MessageDelegate sendCallback; PacketDelegate packetCallback; UdpOut out; bool active{false}; From b7a2eda76bc401af4a20e4bd56538c58c7ec178a Mon Sep 17 00:00:00 2001 From: mikee47 Date: Fri, 5 Mar 2021 23:38:43 +0000 Subject: [PATCH 48/52] Update resource types (SOA, ANY) Need ANY, SOA spotted during testing --- Sming/Components/mdns/src/include/Network/Mdns/Resource.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Resource.h b/Sming/Components/mdns/src/include/Network/Mdns/Resource.h index d58953da4c..a78298c262 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Resource.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Resource.h @@ -24,11 +24,13 @@ struct Ip6Address { */ #define MDNS_RESOURCE_TYPE_MAP(XX) \ XX(A, 0x0001, "32-bit IPv4 address") \ + XX(SOA, 0x0006, "Authoritative DNS Zone information") \ XX(PTR, 0x000C, "Pointer to a canonical name") \ XX(HINFO, 0x000D, "Host Information") \ XX(TXT, 0x0010, "Arbitrary human-readable text") \ XX(AAAA, 0x001C, "128-bit IPv6 address") \ - XX(SRV, 0x0021, "Server selection") + XX(SRV, 0x0021, "Server selection") \ + XX(ANY, 0x00FF, "Matches any resource type in query") namespace mDNS { From 3534a5926b988bb4911d69a0366af463ac4e7946 Mon Sep 17 00:00:00 2001 From: mikee47 Date: Fri, 5 Mar 2021 21:16:23 +0000 Subject: [PATCH 49/52] Name matching Split names by identifying location of protocol Validate length in getDataLength() call --- Sming/Components/mdns/src/Answer.cpp | 5 +- Sming/Components/mdns/src/Name.cpp | 197 ++++++++++++------ Sming/Components/mdns/src/Question.cpp | 3 + .../mdns/src/include/Network/Mdns/Message.h | 14 +- .../mdns/src/include/Network/Mdns/Name.h | 94 +++++++-- 5 files changed, 223 insertions(+), 90 deletions(-) diff --git a/Sming/Components/mdns/src/Answer.cpp b/Sming/Components/mdns/src/Answer.cpp index ed3fdf65bb..2d898fc904 100644 --- a/Sming/Components/mdns/src/Answer.cpp +++ b/Sming/Components/mdns/src/Answer.cpp @@ -59,6 +59,9 @@ bool Answer::parse(Packet& pkt) namePtr = pkt.pos; nameLen = getName().getDataLength(); + if(nameLen == 0) { + return false; + } pkt.skip(nameLen + 8); if(pkt.pos > size) { @@ -117,7 +120,7 @@ uint16_t Answer::init(uint16_t namePtr, const Name& name, Resource::Type type, u { this->namePtr = namePtr; Packet pkt{message.resolvePointer(namePtr)}; - pkt.write16(name.getPtr() | 0xC000); + pkt.write16(name.makePointer()); nameLen = pkt.pos; pkt.write16(uint16_t(type)); // Type pkt.write16((rclass & 0x7fff) | (flush ? 0x8000 : 0)); // Class diff --git a/Sming/Components/mdns/src/Name.cpp b/Sming/Components/mdns/src/Name.cpp index e58ff2f2c3..af51e6e1d1 100644 --- a/Sming/Components/mdns/src/Name.cpp +++ b/Sming/Components/mdns/src/Name.cpp @@ -14,106 +14,177 @@ namespace mDNS { -Name::Info Name::parse() const +static constexpr uint32_t PROTO_TCP{0x7063745F}; // "_tcp" +static constexpr uint32_t PROTO_UDP{0x7064755F}; // "_udp" + +/* + * Class for performing operations on a name in segments + */ +class Reader : public Packet +{ +public: + Reader(const Message& message, uint16_t ptr) : Packet{message.resolvePointer(0), ptr} + { + } + + // Return length of next segment, 0 for end of name + uint8_t next(uint8_t previousLength) + { + if(previousLength != 0) { + skip(previousLength); + } + + if(peek8() >= 0xC0) { + pos = read16() & 0x3fff; + } + + auto len = read8(); + if(len >= 0xC0) { + debug_e("INVALID @ %u", pos); + return 0; + } + return len; + } +}; + +uint16_t Name::getDataLength() const { - Info info{}; Packet pkt{message.resolvePointer(ptr)}; while(true) { - if(pkt.peek8() < 0xC0) { - ++info.components; - auto wordLen = pkt.read8(); - info.textLength += wordLen; - pkt.skip(wordLen); - if(pkt.peek8() == 0) { - if(info.dataLength == 0) { - info.dataLength = pkt.pos + 1; - } - return info; - } - ++info.textLength; // separator - } else { - uint16_t pointer = pkt.read16() & 0x3fff; - if(info.dataLength == 0) { - info.dataLength = pkt.pos; - } - pkt = Packet{message.resolvePointer(pointer)}; + if(pkt.peek8() >= 0xC0) { + return pkt.pos + 2; + } + auto wordLen = pkt.read8(); + if(wordLen >= 0xC0) { + // Invalid + debug_e("INVALID @ %u", pkt.pos - 1); + return 0; + } + if(wordLen == 0) { + return pkt.pos; } + pkt.skip(wordLen); } } -uint16_t Name::read(char* buffer, uint16_t bufSize, uint8_t firstElement, uint8_t count) const +uint16_t Name::read(char* buffer, uint16_t bufSize) const { uint16_t pos{0}; - Packet pkt{message.resolvePointer(ptr)}; - while(count != 0) { - if(pkt.peek8() < 0xC0) { - auto wordLen = pkt.read8(); - if(firstElement == 0) { - --count; - if(pos + wordLen > bufSize) { - break; - } - pkt.read(&buffer[pos], wordLen); - pos += wordLen; - } else { - --firstElement; - pkt.skip(wordLen); - } - if(count == 0 || pkt.peek8() == 0) { - break; - } - if(pos >= bufSize) { - break; - } - if(pos != 0) { - buffer[pos++] = '.'; - } - } else { - uint16_t pointer = pkt.read16() & 0x3fff; - pkt = Packet{message.resolvePointer(pointer)}; + Reader reader(message, ptr); + uint8_t len{0}; + while((len = reader.next(len)) != 0) { + if(pos == bufSize) { + break; + } + if(pos != 0) { + buffer[pos++] = '.'; } + if(pos + len > bufSize) { + break; + } + memcpy(&buffer[pos], reader.ptr(), len); + pos += len; } return pos; } -String Name::getString(uint8_t firstElement, uint8_t count) const +String Name::toString() const { char buffer[maxLength]; - auto len = read(buffer, maxLength, firstElement, count); + auto len = read(buffer, maxLength); return String(buffer, len); } -String Name::getDomain() const +Name::ElementPointers Name::parseElements() const { - auto info = parse(); - return getString(info.components - 1, 1); + bool protocolFound{false}; + ElementPointers elem{}; + Reader reader(message, ptr); + uint8_t len{0}; + while((len = reader.next(len)) != 0) { + if(protocolFound) { + elem.domain = reader.pos - 1; + return elem; + } + + elem.service = elem.protocol; + elem.protocol = reader.pos - 1; + + if(len == 4) { + uint32_t w; + memcpy(&w, reader.ptr(), sizeof(w)); + if(w == PROTO_TCP || w == PROTO_UDP) { + protocolFound = true; + } + } + } + + return ElementPointers{}; +} + +Name Name::getDomain() const +{ + return Name(message, parseElements().domain); +} + +Name Name::getProtocol() const +{ + return Name(message, parseElements().protocol); } -String Name::getService() const +Name Name::getService() const { - auto info = parse(); - return getString(info.components - 2, 1); + return Name(message, parseElements().service); } -String Name::getInstance() const +bool Name::equalsIgnoreCase(const char* str, size_t length) const { - auto info = parse(); - return getString(0, info.components - 2); + Reader reader(message, ptr); + uint8_t len{0}; + while((len = reader.next(len)) != 0) { + if(len > length) { + return false; + } + auto c = str[len]; + if(c != '.' && c != '\0') { + return false; + } + if(memicmp(str, reader.ptr(), len) != 0) { + return false; + } + if(c == '\0') { + return len == length; + } + str += len + 1; + length -= len + 1; + } + return true; +} + +uint16_t Name::makePointer() const +{ + auto content = ptr; + Packet pkt{message.resolvePointer(content)}; + if(pkt.peek8() >= 0xC0) { + // Resolve the pointer to data + content = pkt.read16(); + } + return content | 0xC000; } bool Name::fixup(const Name& other) { - auto info = parse(); - if(info.dataLength < 2) { + auto len = getDataLength(); + if(len < 2) { // Can't be a pointer, too small return false; } - Packet pkt{message.resolvePointer(ptr + info.dataLength - 2)}; + Packet pkt{message.resolvePointer(ptr + len - 2)}; if(pkt.peek8() < 0xC0) { // Not a pointer return false; } - pkt.write16(other.ptr | 0xC000); + pkt.write16(other.makePointer()); return true; } diff --git a/Sming/Components/mdns/src/Question.cpp b/Sming/Components/mdns/src/Question.cpp index bba172131a..2c21149f68 100644 --- a/Sming/Components/mdns/src/Question.cpp +++ b/Sming/Components/mdns/src/Question.cpp @@ -39,6 +39,9 @@ bool Question::parse(Packet& pkt) namePtr = pkt.pos; nameLen = getName().getDataLength(); + if(nameLen == 0) { + return false; + } pkt.skip(nameLen + 4); if(pkt.pos > size) { diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Message.h b/Sming/Components/mdns/src/include/Network/Mdns/Message.h index 162c500253..06f602c9e8 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Message.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Message.h @@ -114,14 +114,6 @@ class Message size += recordSize; } - Question::OwnedList questions; - Answer::OwnedList answers; - -protected: - friend class Question; - friend class Answer; - friend class Name; - /* * Resolve a 16-bit 'pointer' to a memory location * @@ -130,11 +122,15 @@ class Message * data portability, reduce memory consumption and avoid data duplication during * message parsing and construction. */ - uint8_t* resolvePointer(uint16_t pointer) + uint8_t* resolvePointer(uint16_t pointer) const { return data + pointer; } + Question::OwnedList questions; + Answer::OwnedList answers; + +protected: IpAddress remoteIp; uint16_t remotePort; uint8_t* data; diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Name.h b/Sming/Components/mdns/src/include/Network/Mdns/Name.h index 3ffa13ee2b..d60ff1b3f2 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Name.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Name.h @@ -20,7 +20,19 @@ struct Packet; /** * @brief Encoded DNS name * - * mDNS-SD names are represented as instance.service.domain + * mDNS-SD names are represented as instance.service.domain. + * See https://tools.ietf.org/html/rfc6763#section-4.1 + * + * Instance names should be friendly and not attempt to be unique. + * See https://tools.ietf.org/html/rfc6763#appendix-D + * + * Example: "UDP Server._http._tcp.local" + * instance: "UDP Server" + * service: "_http._tcp" + * name: "http" + * protocol: Protocol::Tcp + * domain: "local" + * */ class Name { @@ -32,34 +44,82 @@ class Name { } + Message& getMessage() const + { + return message; + } + /** * @brief Get number of bytes occupied by the name * Not the same as the string length because content is encoded. */ - uint16_t getDataLength() const + uint16_t getDataLength() const; + + String toString() const; + + operator String() const { - return parse().dataLength; + return toString(); } - String toString() const + /** + * @brief Get the last element of the name, which must be the domain + * @retval Name + * + * This Result + * ---- ------ + * "UDP Server._http._tcp.local" "local" + * "UDP Server._http._tcp.my.domain.local" "my.domain.local" + */ + Name getDomain() const; + + /** + * @brief Get the service name + * @retval Name + * + * This Result + * ---- ------ + * "UDP Server._http._tcp.local" "_tcp.local" + */ + Name getProtocol() const; + + /** + * @brief Get the service name + * @retval Name + * + * This Result + * ---- ------ + * "UDP Server._http._tcp.local" "_http._tcp.local" + */ + Name getService() const; + + bool equalsIgnoreCase(const char* str, size_t length) const; + + bool equalsIgnoreCase(const String& value) const { - return getString(0, 255); + return equalsIgnoreCase(value.c_str(), value.length()); } - operator String() const + bool operator==(const String& value) const { - return toString(); + return equalsIgnoreCase(value); } - String getDomain() const; - String getService() const; - String getInstance() const; + bool operator!=(const String& value) const + { + return !operator==(value); + } uint16_t getPtr() const { return ptr; } + /** + * @brief Ensure a pointer refers to actual content, not another pointer + */ + uint16_t makePointer() const; + /** * @brief Fixup pointer at end of name to point to another name * @param other Where to point to @@ -68,15 +128,15 @@ class Name bool fixup(const Name& other); private: - struct Info { - uint16_t dataLength; - uint16_t textLength; - uint8_t components; + uint16_t read(char* buffer, uint16_t bufSize) const; + + struct ElementPointers { + uint16_t service; + uint16_t protocol; + uint16_t domain; }; - Info parse() const; - uint16_t read(char* buffer, uint16_t bufSize, uint8_t firstElement, uint8_t count) const; - String getString(uint8_t firstElement, uint8_t count) const; + ElementPointers parseElements() const; Message& message; uint16_t ptr; From 5785f06c40d38e2ab598e0dbe6c8e0d142fd651e Mon Sep 17 00:00:00 2001 From: mikee47 Date: Fri, 5 Mar 2021 23:39:28 +0000 Subject: [PATCH 50/52] Update Basic_Mdns sample --- samples/Basic_Mdns/app/application.cpp | 30 ++++++++++++++++---------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/samples/Basic_Mdns/app/application.cpp b/samples/Basic_Mdns/app/application.cpp index 3bc7155ecd..1dbb32c45f 100644 --- a/samples/Basic_Mdns/app/application.cpp +++ b/samples/Basic_Mdns/app/application.cpp @@ -9,10 +9,10 @@ #define WIFI_PWD "PleaseEnterPass" #endif -DEFINE_FSTR_LOCAL(fstrSearchInstance, "_googlecast") +DEFINE_FSTR_LOCAL(fstrSearchService, "_googlecast._tcp.local") #ifdef ARCH_HOST -void savePacket(IpAddress remoteIP, uint16_t remotePort, const uint8_t* data, size_t length) +bool savePacket(IpAddress remoteIP, uint16_t remotePort, const uint8_t* data, size_t length) { auto& hostfs = IFS::Host::getFileSystem(); String filename; @@ -26,23 +26,26 @@ void savePacket(IpAddress remoteIP, uint16_t remotePort, const uint8_t* data, si filename += ".bin"; filename.replace(':', '-'); hostfs.setContent(filename, data, length); + + return true; } #endif -void handleMessage(mDNS::Message& message) +bool handleMessage(mDNS::Message& message) { mDNS::printMessage(Serial, message); // Check if we're interested in this reponse if(!message.isReply()) { - return; + return false; } auto answer = message[mDNS::ResourceType::PTR]; if(answer == nullptr) { - return; + return false; } - if(answer->getName().getInstance() != fstrSearchInstance) { - return; + if(answer->getName() != fstrSearchService) { + debug_i("Ignoring message"); + return false; } // Extract our required information from the message @@ -71,12 +74,13 @@ void handleMessage(mDNS::Message& message) Serial.print(info.friendlyName); Serial.print(F("', IP Address = ")); Serial.println(info.ipaddr); + + return true; } void sendSearch() { - String name(fstrSearchInstance); - name += _F("._tcp.local"); + String name(fstrSearchService); // To discover all DNS-SD registered services, use: // name = F("_services._dns-sd._udp.local"); @@ -90,6 +94,10 @@ void gotIP(IpAddress ip, IpAddress netmask, IpAddress gateway) Serial.print(F("Connected. Got IP: ")); Serial.println(ip); + mDNS::server.onSend([](mDNS::Message& msg) { + printMessage(Serial, msg); + return true; + }); mDNS::server.onMessage(handleMessage); #ifdef ARCH_HOST mDNS::server.onPacket(savePacket); @@ -198,8 +206,8 @@ void test() * We can re-use the host Name from the SRV record, storing a pointer instead of the full text */ auto hostName = srv.getHost(); - auto a = reply.addAnswer(hostName, WifiStation.getIP()); - auto aaaa = reply.addAnswer(hostName, Ip6Address()); + reply.addAnswer(hostName, WifiStation.getIP()); + reply.addAnswer(hostName, Ip6Address()); parseFile(F("Test answers"), reply); } From fb30f5a2a19d35a09e38a976524947accc65ad6f Mon Sep 17 00:00:00 2001 From: mikee47 Date: Sun, 7 Mar 2021 10:14:22 +0000 Subject: [PATCH 51/52] Add message handler --- Sming/Components/mdns/src/Server.cpp | 16 +++++---- .../mdns/src/include/Network/Mdns/Handler.h | 33 +++++++++++++++++ .../mdns/src/include/Network/Mdns/Server.h | 20 ++++++++--- samples/Basic_Mdns/app/application.cpp | 36 +++++++++++++++---- 4 files changed, 86 insertions(+), 19 deletions(-) create mode 100644 Sming/Components/mdns/src/include/Network/Mdns/Handler.h diff --git a/Sming/Components/mdns/src/Server.cpp b/Sming/Components/mdns/src/Server.cpp index f4d2497651..5657438f22 100644 --- a/Sming/Components/mdns/src/Server.cpp +++ b/Sming/Components/mdns/src/Server.cpp @@ -93,17 +93,19 @@ void Server::onReceive(pbuf* buf, IpAddress remoteIP, uint16_t remotePort) } } - if(!messageCallback) { + if(handlers.isEmpty()) { return; } Message message(remoteIP, remotePort, buf->payload, buf->len); - if(message.parse()) { - /* - * TODO: Establish callback chain/queue to support multiple clients. - * If a client returns false then don't pass it any further. - */ - messageCallback(message); + if(!message.parse()) { + return; + } + + for(auto& handler : handlers) { + if(!handler.onMessage(message)) { + break; + } } } diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Handler.h b/Sming/Components/mdns/src/include/Network/Mdns/Handler.h new file mode 100644 index 0000000000..e853acbf94 --- /dev/null +++ b/Sming/Components/mdns/src/include/Network/Mdns/Handler.h @@ -0,0 +1,33 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * Message.h + * + ****/ + +#pragma once + +#include +#include "Message.h" + +namespace mDNS +{ +/** + * @brief Virtual base class used for chaining message handlers + */ +class Handler : public LinkedObjectTemplate +{ +public: + using List = LinkedObjectListTemplate; + + /** + * @brief Callback to be invoked for each received message + * @retval bool Return true to pass message to other handlers, false to stop + */ + virtual bool onMessage(Message& message) = 0; +}; + +} // namespace mDNS diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Server.h b/Sming/Components/mdns/src/include/Network/Mdns/Server.h index 1b848a2fe5..20262f763b 100644 --- a/Sming/Components/mdns/src/include/Network/Mdns/Server.h +++ b/Sming/Components/mdns/src/include/Network/Mdns/Server.h @@ -19,6 +19,7 @@ #include #include #include "Request.h" +#include "Handler.h" namespace mDNS { @@ -30,13 +31,13 @@ class Server : protected UdpConnection public: /** * @brief Callback to be invoked for each received message - * @retval bool Depends on operation + * @retval bool See `onSend()` */ using MessageDelegate = Delegate; /** * @brief Callback to be invoked with raw data (debugging, etc.) - * @retval bool Depends on operation + * @retval bool See `onPacket()` */ using PacketDelegate = Delegate; @@ -56,9 +57,18 @@ class Server : protected UdpConnection * @brief Set callback to be invoked for each received message * @param callback Return false from callback to prevent message being passed to other clients */ - void onMessage(MessageDelegate callback) + void addHandler(Handler& handler) { - messageCallback = callback; + handlers.add(&handler); + } + + /** + * @brief Remove a message handler + * @note If there are no more handlers then consider setting a timeout and then shutting the server down. + */ + void removeHandler(Handler& handler) + { + handlers.remove(&handler); } /** @@ -106,7 +116,7 @@ class Server : protected UdpConnection void onReceive(pbuf* buf, IpAddress remoteIP, uint16_t remotePort) override; }; - MessageDelegate messageCallback; + Handler::List handlers; MessageDelegate sendCallback; PacketDelegate packetCallback; UdpOut out; diff --git a/samples/Basic_Mdns/app/application.cpp b/samples/Basic_Mdns/app/application.cpp index 1dbb32c45f..c6cd5dad61 100644 --- a/samples/Basic_Mdns/app/application.cpp +++ b/samples/Basic_Mdns/app/application.cpp @@ -9,7 +9,25 @@ #define WIFI_PWD "PleaseEnterPass" #endif -DEFINE_FSTR_LOCAL(fstrSearchService, "_googlecast._tcp.local") +class MessageHandler : public mDNS::Handler +{ +public: + /* + * Set the DNS record name we're interested in. + * Used as a filter in our `onMessage` method. + */ + void setSearchName(const String& name) + { + searchName = name; + } + + bool onMessage(mDNS::Message& message) override; + +private: + String searchName; +}; + +static MessageHandler myMessageHandler; #ifdef ARCH_HOST bool savePacket(IpAddress remoteIP, uint16_t remotePort, const uint8_t* data, size_t length) @@ -31,20 +49,22 @@ bool savePacket(IpAddress remoteIP, uint16_t remotePort, const uint8_t* data, si } #endif -bool handleMessage(mDNS::Message& message) +bool MessageHandler::onMessage(mDNS::Message& message) { mDNS::printMessage(Serial, message); - // Check if we're interested in this reponse + // Check if we're interested in this message if(!message.isReply()) { + debug_i("Ignoring query"); return false; } auto answer = message[mDNS::ResourceType::PTR]; if(answer == nullptr) { + debug_i("Ignoring message: no PTR record"); return false; } - if(answer->getName() != fstrSearchService) { - debug_i("Ignoring message"); + if(answer->getName() != searchName) { + debug_i("Ignoring message: Name doesn't match"); return false; } @@ -80,13 +100,15 @@ bool handleMessage(mDNS::Message& message) void sendSearch() { - String name(fstrSearchService); + String name = F("_googlecast._tcp.local"); // To discover all DNS-SD registered services, use: // name = F("_services._dns-sd._udp.local"); bool ok = mDNS::server.search(name); debug_i("search('%s'): %s", name.c_str(), ok ? "OK" : "FAIL"); + // Tell our handler what we're looking for + myMessageHandler.setSearchName(name); } void gotIP(IpAddress ip, IpAddress netmask, IpAddress gateway) @@ -98,7 +120,7 @@ void gotIP(IpAddress ip, IpAddress netmask, IpAddress gateway) printMessage(Serial, msg); return true; }); - mDNS::server.onMessage(handleMessage); + mDNS::server.addHandler(myMessageHandler); #ifdef ARCH_HOST mDNS::server.onPacket(savePacket); #endif From 50f8714a1e4dc6db6e7ef3262e12068c25a547cf Mon Sep 17 00:00:00 2001 From: mikee47 Date: Sun, 7 Mar 2021 21:39:59 +0000 Subject: [PATCH 52/52] Move mdns out of Components and into MDNS library submodule --- .gitmodules | 4 + Sming/Components/mdns/README.rst | 75 ----- Sming/Components/mdns/component.mk | 14 - Sming/Components/mdns/src/.cs | 0 Sming/Components/mdns/src/Answer.cpp | 141 --------- Sming/Components/mdns/src/Message.cpp | 89 ------ Sming/Components/mdns/src/Name.cpp | 191 ------------- Sming/Components/mdns/src/Packet.h | 46 --- Sming/Components/mdns/src/Question.cpp | 71 ----- Sming/Components/mdns/src/Request.cpp | 71 ----- Sming/Components/mdns/src/Resource.cpp | 222 --------------- Sming/Components/mdns/src/Responder.cpp | 225 --------------- Sming/Components/mdns/src/Server.cpp | 112 -------- Sming/Components/mdns/src/debug.cpp | 88 ------ .../mdns/src/include/Network/Mdns/Answer.h | 122 -------- .../mdns/src/include/Network/Mdns/Handler.h | 33 --- .../mdns/src/include/Network/Mdns/Message.h | 140 --------- .../mdns/src/include/Network/Mdns/Name.h | 145 ---------- .../mdns/src/include/Network/Mdns/Question.h | 74 ----- .../mdns/src/include/Network/Mdns/Request.h | 72 ----- .../mdns/src/include/Network/Mdns/Resource.h | 235 --------------- .../mdns/src/include/Network/Mdns/Responder.h | 58 ---- .../mdns/src/include/Network/Mdns/Server.h | 128 --------- .../mdns/src/include/Network/Mdns/Service.h | 82 ------ .../mdns/src/include/Network/Mdns/debug.h | 12 - Sming/Libraries/MDNS | 1 + samples/Basic_Mdns/.cproject | 151 ---------- samples/Basic_Mdns/.project | 28 -- samples/Basic_Mdns/Makefile | 9 - samples/Basic_Mdns/README.rst | 4 - samples/Basic_Mdns/app/application.cpp | 267 ------------------ samples/Basic_Mdns/component.mk | 1 - .../Basic_Mdns/resource/192.168.1.100.mdns | Bin 497 -> 0 bytes .../Basic_Mdns/resource/192.168.1.103.mdns | Bin 346 -> 0 bytes .../Basic_Mdns/resource/192.168.1.107.mdns | Bin 110 -> 0 bytes samples/UdpServer_mDNS/component.mk | 2 +- 36 files changed, 6 insertions(+), 2907 deletions(-) delete mode 100644 Sming/Components/mdns/README.rst delete mode 100644 Sming/Components/mdns/component.mk delete mode 100644 Sming/Components/mdns/src/.cs delete mode 100644 Sming/Components/mdns/src/Answer.cpp delete mode 100644 Sming/Components/mdns/src/Message.cpp delete mode 100644 Sming/Components/mdns/src/Name.cpp delete mode 100644 Sming/Components/mdns/src/Packet.h delete mode 100644 Sming/Components/mdns/src/Question.cpp delete mode 100644 Sming/Components/mdns/src/Request.cpp delete mode 100644 Sming/Components/mdns/src/Resource.cpp delete mode 100644 Sming/Components/mdns/src/Responder.cpp delete mode 100644 Sming/Components/mdns/src/Server.cpp delete mode 100644 Sming/Components/mdns/src/debug.cpp delete mode 100644 Sming/Components/mdns/src/include/Network/Mdns/Answer.h delete mode 100644 Sming/Components/mdns/src/include/Network/Mdns/Handler.h delete mode 100644 Sming/Components/mdns/src/include/Network/Mdns/Message.h delete mode 100644 Sming/Components/mdns/src/include/Network/Mdns/Name.h delete mode 100644 Sming/Components/mdns/src/include/Network/Mdns/Question.h delete mode 100644 Sming/Components/mdns/src/include/Network/Mdns/Request.h delete mode 100644 Sming/Components/mdns/src/include/Network/Mdns/Resource.h delete mode 100644 Sming/Components/mdns/src/include/Network/Mdns/Responder.h delete mode 100644 Sming/Components/mdns/src/include/Network/Mdns/Server.h delete mode 100644 Sming/Components/mdns/src/include/Network/Mdns/Service.h delete mode 100644 Sming/Components/mdns/src/include/Network/Mdns/debug.h create mode 160000 Sming/Libraries/MDNS delete mode 100644 samples/Basic_Mdns/.cproject delete mode 100644 samples/Basic_Mdns/.project delete mode 100644 samples/Basic_Mdns/Makefile delete mode 100644 samples/Basic_Mdns/README.rst delete mode 100644 samples/Basic_Mdns/app/application.cpp delete mode 100644 samples/Basic_Mdns/component.mk delete mode 100644 samples/Basic_Mdns/resource/192.168.1.100.mdns delete mode 100644 samples/Basic_Mdns/resource/192.168.1.103.mdns delete mode 100644 samples/Basic_Mdns/resource/192.168.1.107.mdns diff --git a/.gitmodules b/.gitmodules index e5ea539ace..d467eddfb4 100644 --- a/.gitmodules +++ b/.gitmodules @@ -211,6 +211,10 @@ path = Sming/Libraries/libsodium/libsodium url = https://github.com/jedisct1/libsodium.git ignore = dirty +[submodule "Libraries.MDNS"] + path = Sming/Libraries/MDNS + url = https://github.com/mikee47/Sming-MDNS + ignore = dirty [submodule "Libraries.modbusino"] path = Sming/Libraries/modbusino/modbusino url = https://github.com/kmihaylov/modbusino.git diff --git a/Sming/Components/mdns/README.rst b/Sming/Components/mdns/README.rst deleted file mode 100644 index e3f6ebe529..0000000000 --- a/Sming/Components/mdns/README.rst +++ /dev/null @@ -1,75 +0,0 @@ -mDNS: Multicast Domain Name System -================================== - -.. highlight:: c++ - -https://en.wikipedia.org/wiki/Multicast_DNS - - -Responder ---------- - -Sming provides the :cpp:class:`mDNS::Responder` class to allow applications -to advertise themselves on the local network. - -To use: - -1. Add ``COMPONENT_DEPENDS += mdns`` to your application componenent.mk file. -2. Add these lines to your application:: - - #include - - static mDNS::Responder responder; - - // Call when IP address has been obtained - void startmDNS() - { - responder.begin(F("myhostname"); - } - -This will advertise the device as ``myhostname.local``. - -To provide a custom service, implement a :cpp:class:`mDNS::Service` class -and call :cpp:func:`mDNS::Responder::addService`. - -See the :sample:`UdpServer_mDNS` sample application. - - -Discovery ---------- - -This library also provides support for device discovery using a separate set of classes, -based on the :cpp:class:`mDNS::Server`. -See :sample:`Basic_Mdns` for an example. - -.. note:: - - The mDNS Server and Responder cannot currently be used together. - - -Testing -------- - -For linux, you can use `avahi `__ -to perform mDNS queries and confirm output is as expected: - -.. code-block:: bash - - sudo apt install avahi - avahi-browse --all -r - - -References ----------- - -- Multicast DNS RFC6762 https://tools.ietf.org/html/rfc6762 -- Zero-configuration networking (DNS-SD) https://en.wikipedia.org/wiki/Zero-configuration_networking -- DNS-Based Service Discovery https://tools.ietf.org/html/rfc6763 -- DNS record types https://en.wikipedia.org/wiki/List_of_DNS_record_types -- Domain Names: Implementation and Specification https://tools.ietf.org/html/rfc1035 - - -API Documentation ------------------ - -.. doxygennamespace:: mDNS diff --git a/Sming/Components/mdns/component.mk b/Sming/Components/mdns/component.mk deleted file mode 100644 index f25ff5d412..0000000000 --- a/Sming/Components/mdns/component.mk +++ /dev/null @@ -1,14 +0,0 @@ -COMPONENT_SRCDIRS := src -COMPONENT_INCDIRS := src/include - -COMPONENT_DOXYGEN_INPUT := src/include - -COMPONENT_VARS += ENABLE_CUSTOM_LWIP - -# If using Espressif LWIP... -ifeq ($(ENABLE_CUSTOM_LWIP),0) -ENABLE_ESPCONN := 1 -GLOBAL_CFLAGS += -DENABLE_ESPCONN -endif - -GLOBAL_CFLAGS += -DENABLE_CUSTOM_LWIP=$(ENABLE_CUSTOM_LWIP) diff --git a/Sming/Components/mdns/src/.cs b/Sming/Components/mdns/src/.cs deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/Sming/Components/mdns/src/Answer.cpp b/Sming/Components/mdns/src/Answer.cpp deleted file mode 100644 index 2d898fc904..0000000000 --- a/Sming/Components/mdns/src/Answer.cpp +++ /dev/null @@ -1,141 +0,0 @@ -/**** - * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. - * Created 2015 by Skurydin Alexey - * http://github.com/SmingHub/Sming - * All files of the Sming Core are provided under the LGPL v3 license. - * - * Answer.cpp - * - ****/ - -#include "include/Network/Mdns/Answer.h" -#include "include/Network/Mdns/Message.h" -#include "Packet.h" -#include - -String toString(mDNS::Answer::Kind kind) -{ - using Kind = mDNS::Answer::Kind; - switch(kind) { - case Kind::answer: - return F("answer"); - case Kind::name: - return F("name"); - case Kind::additional: - return F("additional"); - default: - assert(false); - return nullptr; - } -} - -namespace mDNS -{ -ResourceType Answer::getType() const -{ - return ResourceType(Packet{message.resolvePointer(namePtr + nameLen)}.read16()); -} - -uint16_t Answer::getClass() const -{ - auto rrclass = Packet{message.resolvePointer(namePtr + nameLen + 2)}.read16(); - return rrclass & 0x7fff; -} - -bool Answer::isCachedFlush() const -{ - auto rrclass = Packet{message.resolvePointer(namePtr + nameLen + 2)}.read16(); - return rrclass & 0x8000; -} - -uint32_t Answer::getTtl() const -{ - return Packet{message.resolvePointer(namePtr + nameLen + 4)}.read32(); -} - -bool Answer::parse(Packet& pkt) -{ - auto size = message.getSize(); - - namePtr = pkt.pos; - nameLen = getName().getDataLength(); - if(nameLen == 0) { - return false; - } - pkt.skip(nameLen + 8); - - if(pkt.pos > size) { - debug_e("[MDNS] Answer packet overrun, pos = %u, size = %u", pkt.pos, size); - // Something has gone wrong receiving or parsing the data. - return false; - } - - recordSize = pkt.read16(); - pkt.pos += recordSize; - return true; -} - -uint8_t* Answer::getRecord() const -{ - return message.resolvePointer(getRecordPtr()); -} - -String Answer::getRecordString() const -{ - using namespace Resource; - switch(getType()) { - case Type::A: - return A(*this).toString(); - case Type::PTR: - return PTR(*this).toString(); - case Type::HINFO: - return HINFO(*this).toString(); - case Type::TXT: - return TXT(*this).toString(); - case Type::AAAA: - return AAAA(*this).toString(); - case Type::SRV: - return SRV(*this).toString(); - default: - return Record(*this).toString(); - } -} - -uint16_t Answer::init(uint16_t namePtr, const String& name, Resource::Type type, uint16_t rclass, bool flush, - uint32_t ttl) -{ - this->namePtr = namePtr; - Packet pkt{message.resolvePointer(namePtr)}; - pkt.writeName(name); - nameLen = pkt.pos; - pkt.write16(uint16_t(type)); // Type - pkt.write16((rclass & 0x7fff) | (flush ? 0x8000 : 0)); // Class - pkt.write32(ttl); // TTL - pkt.write16(0); // Resource Record Length - return pkt.pos; -} - -uint16_t Answer::init(uint16_t namePtr, const Name& name, Resource::Type type, uint16_t rclass, bool flush, - uint32_t ttl) -{ - this->namePtr = namePtr; - Packet pkt{message.resolvePointer(namePtr)}; - pkt.write16(name.makePointer()); - nameLen = pkt.pos; - pkt.write16(uint16_t(type)); // Type - pkt.write16((rclass & 0x7fff) | (flush ? 0x8000 : 0)); // Class - pkt.write32(ttl); // TTL - pkt.write16(0); // Resource Record Length - return pkt.pos; -} - -void Answer::allocate(uint16_t size) -{ - assert(size > recordSize); - message.allocate(size - recordSize); - recordSize = size; - Packet pkt{message.resolvePointer(namePtr + nameLen + 8)}; - pkt.write16(recordSize); -} - -} // namespace mDNS diff --git a/Sming/Components/mdns/src/Message.cpp b/Sming/Components/mdns/src/Message.cpp deleted file mode 100644 index 4ffba44e7b..0000000000 --- a/Sming/Components/mdns/src/Message.cpp +++ /dev/null @@ -1,89 +0,0 @@ -/**** - * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. - * Created 2015 by Skurydin Alexey - * http://github.com/SmingHub/Sming - * All files of the Sming Core are provided under the LGPL v3 license. - * - * Message.cpp - * - ****/ - -#include "include/Network/Mdns/Message.h" -#include "Packet.h" -#include -#include - -namespace mDNS -{ -bool Message::parse() -{ - // TODO: If it's truncated we can expect more data soon so we should wait for additional records before deciding whether to respond. - // if(isTruncated()) - // { - // } - - // Non zero Response code implies error. - if(getResponseCode() != 0) { - debug_w("Got errored MDNS message"); - return false; - } - - Packet pkt{data, 4}; - - // Number of incoming queries. - auto questionsCount = pkt.read16(); - - // Number of incoming answers. - auto answersCount = pkt.read16(); - - // Number of incoming Name Server resource records. - auto nsCount = pkt.read16(); - - // Number of incoming Additional resource records. - auto additionalCount = pkt.read16(); - - // List of questions - for(uint16_t i = 0; i < questionsCount; i++) { - auto question = new Question(*this); - if(!question->parse(pkt)) { - delete question; - return false; - } - questions.add(question); - } - - // List of answers, namespaces and additional records - auto parseRecords = [&](Answer::Kind kind, uint16_t count) -> bool { - for(uint16_t i = 0; i < count; i++) { - auto answer = new Answer(*this, kind); - if(!answer->parse(pkt)) { - delete answer; - return false; - } - answers.add(answer); - } - return true; - }; - - bool ok = parseRecords(Answer::Kind::answer, answersCount); - if(ok) { - ok = parseRecords(Answer::Kind::name, nsCount); - if(ok) { - ok = parseRecords(Answer::Kind::additional, additionalCount); - } - } - - return true; -} - -Answer* Message::operator[](ResourceType type) -{ - for(auto& ans : answers) { - if(ans.getType() == type) { - return &ans; - } - } - return nullptr; -} - -} // namespace mDNS diff --git a/Sming/Components/mdns/src/Name.cpp b/Sming/Components/mdns/src/Name.cpp deleted file mode 100644 index af51e6e1d1..0000000000 --- a/Sming/Components/mdns/src/Name.cpp +++ /dev/null @@ -1,191 +0,0 @@ -/**** - * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. - * Created 2015 by Skurydin Alexey - * http://github.com/SmingHub/Sming - * All files of the Sming Core are provided under the LGPL v3 license. - * - * Name.cpp - * - ****/ - -#include "include/Network/Mdns/Name.h" -#include "include/Network/Mdns/Message.h" -#include "Packet.h" - -namespace mDNS -{ -static constexpr uint32_t PROTO_TCP{0x7063745F}; // "_tcp" -static constexpr uint32_t PROTO_UDP{0x7064755F}; // "_udp" - -/* - * Class for performing operations on a name in segments - */ -class Reader : public Packet -{ -public: - Reader(const Message& message, uint16_t ptr) : Packet{message.resolvePointer(0), ptr} - { - } - - // Return length of next segment, 0 for end of name - uint8_t next(uint8_t previousLength) - { - if(previousLength != 0) { - skip(previousLength); - } - - if(peek8() >= 0xC0) { - pos = read16() & 0x3fff; - } - - auto len = read8(); - if(len >= 0xC0) { - debug_e("INVALID @ %u", pos); - return 0; - } - return len; - } -}; - -uint16_t Name::getDataLength() const -{ - Packet pkt{message.resolvePointer(ptr)}; - while(true) { - if(pkt.peek8() >= 0xC0) { - return pkt.pos + 2; - } - auto wordLen = pkt.read8(); - if(wordLen >= 0xC0) { - // Invalid - debug_e("INVALID @ %u", pkt.pos - 1); - return 0; - } - if(wordLen == 0) { - return pkt.pos; - } - pkt.skip(wordLen); - } -} - -uint16_t Name::read(char* buffer, uint16_t bufSize) const -{ - uint16_t pos{0}; - Reader reader(message, ptr); - uint8_t len{0}; - while((len = reader.next(len)) != 0) { - if(pos == bufSize) { - break; - } - if(pos != 0) { - buffer[pos++] = '.'; - } - if(pos + len > bufSize) { - break; - } - memcpy(&buffer[pos], reader.ptr(), len); - pos += len; - } - return pos; -} - -String Name::toString() const -{ - char buffer[maxLength]; - auto len = read(buffer, maxLength); - return String(buffer, len); -} - -Name::ElementPointers Name::parseElements() const -{ - bool protocolFound{false}; - ElementPointers elem{}; - Reader reader(message, ptr); - uint8_t len{0}; - while((len = reader.next(len)) != 0) { - if(protocolFound) { - elem.domain = reader.pos - 1; - return elem; - } - - elem.service = elem.protocol; - elem.protocol = reader.pos - 1; - - if(len == 4) { - uint32_t w; - memcpy(&w, reader.ptr(), sizeof(w)); - if(w == PROTO_TCP || w == PROTO_UDP) { - protocolFound = true; - } - } - } - - return ElementPointers{}; -} - -Name Name::getDomain() const -{ - return Name(message, parseElements().domain); -} - -Name Name::getProtocol() const -{ - return Name(message, parseElements().protocol); -} - -Name Name::getService() const -{ - return Name(message, parseElements().service); -} - -bool Name::equalsIgnoreCase(const char* str, size_t length) const -{ - Reader reader(message, ptr); - uint8_t len{0}; - while((len = reader.next(len)) != 0) { - if(len > length) { - return false; - } - auto c = str[len]; - if(c != '.' && c != '\0') { - return false; - } - if(memicmp(str, reader.ptr(), len) != 0) { - return false; - } - if(c == '\0') { - return len == length; - } - str += len + 1; - length -= len + 1; - } - return true; -} - -uint16_t Name::makePointer() const -{ - auto content = ptr; - Packet pkt{message.resolvePointer(content)}; - if(pkt.peek8() >= 0xC0) { - // Resolve the pointer to data - content = pkt.read16(); - } - return content | 0xC000; -} - -bool Name::fixup(const Name& other) -{ - auto len = getDataLength(); - if(len < 2) { - // Can't be a pointer, too small - return false; - } - Packet pkt{message.resolvePointer(ptr + len - 2)}; - if(pkt.peek8() < 0xC0) { - // Not a pointer - return false; - } - pkt.write16(other.makePointer()); - return true; -} - -} // namespace mDNS diff --git a/Sming/Components/mdns/src/Packet.h b/Sming/Components/mdns/src/Packet.h deleted file mode 100644 index fe94eca4e0..0000000000 --- a/Sming/Components/mdns/src/Packet.h +++ /dev/null @@ -1,46 +0,0 @@ -/**** - * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. - * Created 2015 by Skurydin Alexey - * http://github.com/SmingHub/Sming - * All files of the Sming Core are provided under the LGPL v3 license. - * - * Packet.h - * - ****/ - -#pragma once - -#include - -namespace mDNS -{ -/** - * @brief Helper class for reading/writing packet content - */ -struct Packet : public NetworkPacket { - using NetworkPacket::NetworkPacket; - - void writeName(const String& name) - { - size_t namepos{0}; - auto namelen = name.length(); - while(true) { - int sep = name.indexOf('.', namepos); - auto wordLength = (sep >= 0) ? (sep - namepos) : (namelen - namepos); - if(wordLength == 0) { - // Name ends in a '.' so add a pointer record; will fixup later - write16(0xC000); - break; - } - write8(wordLength); - write(name.c_str() + namepos, wordLength); - if(sep < 0) { - write8(0); // End of name. - break; - } - namepos = sep + 1; - } - } -}; - -} // namespace mDNS \ No newline at end of file diff --git a/Sming/Components/mdns/src/Question.cpp b/Sming/Components/mdns/src/Question.cpp deleted file mode 100644 index 2c21149f68..0000000000 --- a/Sming/Components/mdns/src/Question.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/**** - * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. - * Created 2015 by Skurydin Alexey - * http://github.com/SmingHub/Sming - * All files of the Sming Core are provided under the LGPL v3 license. - * - * Question.cpp - * - ****/ - -#include "include/Network/Mdns/Question.h" -#include "include/Network/Mdns/Message.h" -#include "Packet.h" -#include - -namespace mDNS -{ -uint16_t Question::getClass() const -{ - auto qclass = Packet{message.resolvePointer(namePtr + nameLen + 2)}.read16(); - return qclass & 0x7fff; -} - -bool Question::isUnicastReply() const -{ - auto qclass = Packet{message.resolvePointer(namePtr + nameLen + 2)}.read16(); - return qclass & 0x8000; -} - -Resource::Type Question::getType() const -{ - auto type = Packet{message.resolvePointer(namePtr + nameLen)}.read16(); - return Resource::Type(type); -} - -bool Question::parse(Packet& pkt) -{ - auto size = message.getSize(); - - namePtr = pkt.pos; - nameLen = getName().getDataLength(); - if(nameLen == 0) { - return false; - } - pkt.skip(nameLen + 4); - - if(pkt.pos > size) { - debug_e("[MDNS] Question packet overrun, pos = %u, size = %u", pkt.pos, size); - // Something has gone wrong receiving or parsing the data. - return false; - } - - return true; -} - -uint16_t Question::init(uint16_t namePtr, const String& name, ResourceType type, uint16_t qclass, bool unicast) -{ - this->namePtr = namePtr; - Packet pkt{message.resolvePointer(namePtr)}; - pkt.writeName(name); - nameLen = pkt.pos; - pkt.write16(uint16_t(type)); - qclass &= 0x7fff; - if(unicast) { - qclass |= 0x8000; - } - pkt.write16(qclass); - return pkt.pos; -} - -} // namespace mDNS diff --git a/Sming/Components/mdns/src/Request.cpp b/Sming/Components/mdns/src/Request.cpp deleted file mode 100644 index 8637eaaea7..0000000000 --- a/Sming/Components/mdns/src/Request.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/**** - * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. - * Created 2015 by Skurydin Alexey - * http://github.com/SmingHub/Sming - * All files of the Sming Core are provided under the LGPL v3 license. - * - * Request.cpp - * - ****/ - -#include "include/Network/Mdns/Request.h" -#include "Packet.h" - -namespace mDNS -{ -Request::Request(Type type) : Message(MDNS_IP, MDNS_TARGET_PORT, buffer, 0) -{ - Packet pkt{data, 0}; - - // The first two bytes are the transaction id and they are not used in MDNS - pkt.write16(0); - - // 2 bytes for Flags and status code - pkt.write16(type == Type::query ? 0x0000 : 0x8000); - - // 2 bytes for number of questions - pkt.write16(0); - - // 2 bytes for number of Answer RRs - pkt.write16(0); - - // 2 bytes for Authority PRs - pkt.write16(0); - - // 2 bytes for Additional PRs - pkt.write16(0); - - size = pkt.pos; -} - -Question* Request::addQuestion(const String& name, ResourceType type, uint16_t qclass, bool unicast) -{ - auto question = new Question(*this); - questions.add(question); - Packet pkt{data, 4}; - pkt.write16(questions.count()); - size += question->init(size, name, type, qclass, unicast); - return question; -} - -Answer* Request::createAnswer(const String& name, Resource::Type type, uint16_t rclass, bool flush, uint32_t ttl) -{ - auto answer = new Answer(*this, kind); - answers.add(answer); - Packet pkt{data, uint16_t(6 + unsigned(kind) * 2)}; - pkt.write16(pkt.peek16() + 1); - size += answer->init(size, name, type, rclass, flush, ttl); - return answer; -} - -Answer* Request::createAnswer(const Name& name, Resource::Type type, uint16_t rclass, bool flush, uint32_t ttl) -{ - auto answer = new Answer(*this, kind); - answers.add(answer); - Packet pkt{data, uint16_t(6 + unsigned(kind) * 2)}; - pkt.write16(pkt.peek16() + 1); - size += answer->init(size, name, type, rclass, flush, ttl); - return answer; -} - -} // namespace mDNS \ No newline at end of file diff --git a/Sming/Components/mdns/src/Resource.cpp b/Sming/Components/mdns/src/Resource.cpp deleted file mode 100644 index b9c7bb0a61..0000000000 --- a/Sming/Components/mdns/src/Resource.cpp +++ /dev/null @@ -1,222 +0,0 @@ -/**** - * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. - * Created 2015 by Skurydin Alexey - * http://github.com/SmingHub/Sming - * All files of the Sming Core are provided under the LGPL v3 license. - * - * Resource.cpp - * - ****/ - -#include "include/Network/Mdns/Resource.h" -#include "include/Network/Mdns/Answer.h" -#include "Packet.h" -#include - -String toString(mDNS::ResourceType type) -{ - switch(type) { -#define XX(name, value, desc) \ - case mDNS::ResourceType::name: \ - return F(#name); - MDNS_RESOURCE_TYPE_MAP(XX) -#undef XX - default: - return String(unsigned(type)); - } -} - -namespace mDNS -{ -namespace Resource -{ -/* Record */ - -uint8_t* Record::getRecord() const -{ - return answer.getRecord(); -} - -uint16_t Record::getRecordSize() const -{ - return answer.getRecordSize(); -} - -String Record::toString() const -{ - return makeHexString(getRecord(), getRecordSize(), ' '); -} - -/* A */ - -IpAddress A::getAddress() const -{ - // Keep bytes in network order - uint32_t addr; - memcpy(&addr, getRecord(), sizeof(addr)); - return addr; -} - -void A::init(IpAddress ipaddr) -{ - uint32_t addr = ipaddr; - Packet pkt{getRecord()}; - pkt.write(&addr, sizeof(addr)); - answer.allocate(pkt.pos); -} - -/* PTR */ - -Name PTR::getName() const -{ - return Name(answer.getResponse(), answer.getRecordPtr()); -} - -void PTR::init(const String& name) -{ - Packet pkt{getRecord()}; - pkt.writeName(name); - answer.allocate(pkt.pos); -} - -/* TXT */ - -String TXT::toString(const String& sep) const -{ - String s; - Packet pkt{getRecord()}; - auto size = getRecordSize(); - while(pkt.pos < size) { - auto len = pkt.read8(); - if(s) { - s += sep; - } - s += pkt.readString(len); - } - return s; -} - -uint8_t TXT::count() const -{ - if(mCount == 0) { - Packet pkt{getRecord()}; - auto size = getRecordSize(); - while(pkt.pos < size) { - auto len = pkt.read8(); - pkt.skip(len); - ++mCount; - } - } - return mCount; -} - -String TXT::operator[](uint8_t index) const -{ - uint8_t len; - auto p = get(index, len); - return p ? String(p, len) : nullptr; -} - -String TXT::getValue(const char* name, uint16_t namelen) const -{ - Packet pkt{getRecord()}; - auto size = getRecordSize(); - while(pkt.pos < size) { - auto len = pkt.read8(); - auto entry = reinterpret_cast(pkt.ptr()); - if(len > namelen && entry[namelen] == '=' && memicmp(entry, name, namelen) == 0) { - return String(entry + namelen + 1, len - namelen - 1); - } - pkt.skip(len); - } - return nullptr; -} - -const char* TXT::get(uint8_t index, uint8_t& len) const -{ - Packet pkt{getRecord()}; - auto size = getRecordSize(); - for(; pkt.pos < size; --index) { - len = pkt.read8(); - if(index == 0) { - return reinterpret_cast(pkt.ptr()); - } - pkt.skip(len); - } - return nullptr; -} - -void TXT::add(const char* value, uint16_t len) -{ - Packet pkt{getRecord(), getRecordSize()}; - pkt.write8(len); - pkt.write(value, len); - answer.allocate(pkt.pos); -} - -/* AAAA */ - -String AAAA::toString() const -{ - return makeHexString(getRecord(), getRecordSize(), ':'); -} - -void AAAA::init(Ip6Address addr) -{ - Packet pkt{getRecord()}; - pkt.write(&addr, sizeof(addr)); - answer.allocate(pkt.pos); -} - -/* SRV */ - -uint16_t SRV::getPriority() const -{ - return Packet{getRecord()}.read16(); -} - -uint16_t SRV::getWeight() const -{ - return Packet{getRecord(), 2}.read16(); -} - -uint16_t SRV::getPort() const -{ - return Packet{getRecord(), 4}.read16(); -} - -Name SRV::getHost() const -{ - return Name(answer.getResponse(), answer.getRecordPtr() + 6); -} - -String SRV::toString(const String& sep) const -{ - String s; - s.reserve(32); - s += "p="; - s += getPriority(); - s += sep; - s += "w="; - s += getWeight(); - s += sep; - s += F("port="); - s += getPort(); - s += sep; - s += F("host="); - s += getHost(); - return s; -} - -void SRV::init(uint16_t priority, uint16_t weight, uint16_t port, const String& host) -{ - Packet pkt{getRecord()}; - pkt.write16(priority); - pkt.write16(weight); - pkt.write16(port); - pkt.writeName(host); - answer.allocate(pkt.pos); -} - -} // namespace Resource -} // namespace mDNS diff --git a/Sming/Components/mdns/src/Responder.cpp b/Sming/Components/mdns/src/Responder.cpp deleted file mode 100644 index 12cf5ff2ce..0000000000 --- a/Sming/Components/mdns/src/Responder.cpp +++ /dev/null @@ -1,225 +0,0 @@ -/**** - * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. - * Created 2015 by Skurydin Alexey - * http://github.com/SmingHub/Sming - * All files of the Sming Core are provided under the LGPL v3 license. - * - * Responder.cpp - * - ****/ - -#include "include/Network/Mdns/Responder.h" -#include - -#if MDNS_LWIP >= 0x0200 - -extern "C" { -#ifndef LWIP_MDNS_RESPONDER -#define LWIP_MDNS_RESPONDER 1 -#endif -#include -} - -#ifdef ARCH_ESP8266 -extern struct netif* netif_default_LWIP2; -#define netif_default netif_default_LWIP2 -#endif - -#else - -extern "C" { -#if MDNS_LWIP > 0x0100 -#include -#endif - -// Sets the service name -void mdns_set_name(const char* name); -} - -#include - -#endif - -#define MDNS_TTL_DEFAULT 120 - -namespace mDNS -{ -bool Responder::begin(const String& hostname) -{ -#if MDNS_LWIP >= 0x0200 - - mdns_resp_init(); - -#if MDNS_LWIP >= 0x0202 - err_t err = mdns_resp_add_netif(netif_default, hostname.c_str()); - if(err == ERR_OK) { - System.queueCallback(InterruptCallback([]() { mdns_resp_announce(netif_default); })); - } -#else - err_t err = mdns_resp_add_netif(netif_default, hostname.c_str(), MDNS_TTL_DEFAULT); -#endif - - if(err != ERR_OK) { - debug_e("mdns_resp_add_netif failed, err = %d", err); - return false; - } - -#else - - this->hostname = hostname; - - mdns_info mi{}; - mi.host_name = this->hostname.begin(); - mi.server_name = const_cast("Sming"); - mi.ipAddr = WifiStation.getIP(); - -#ifdef ENABLE_ESPCONN - espconn_mdns_init(&mi); -#else - mdns_init(&mi); -#endif - -#endif - - debug_i("MDNS initialised for '%s'", hostname.c_str()); - return true; -} - -bool Responder::addService(Service& svc) -{ - auto si = svc.getInfo(); - debug_i("MDNS addService '%s'", si.name.c_str()); - -#if MDNS_LWIP >= 0x0200 - - if(svc.id >= 0) { - debug_e("MDNS: Service already in use"); - return false; - } - - auto getSrvTxt = [](mdns_service* service, void* userdata) { - auto txt = static_cast(userdata)->getTxt(); - err_t res = 0; - for(auto s : txt) { - res |= mdns_resp_add_service_txtitem(service, s, strlen(s)); - } - debug_i("mdns add service: res = %d", res); - }; - - String serviceType = String('_') + si.type; -#if MDNS_LWIP >= 0x0202 - auto id = mdns_resp_add_service(netif_default, si.name.c_str(), serviceType.c_str(), mdns_sd_proto(si.protocol), - si.port, getSrvTxt, &svc); -#else - auto id = mdns_resp_add_service(netif_default, si.name.c_str(), serviceType.c_str(), mdns_sd_proto(si.protocol), - si.port, MDNS_TTL_DEFAULT, getSrvTxt, &svc); -#endif - if(id < 0) { - debug_w("mdns_resp_add_service failed, err = %d", id); - return false; - } - - debug_i("mdns_resp_add_service returned %d", id); - - svc.id = id; - -#else - - // Service already registered? - if(service != nullptr) { - debug_e("MDNS: Service already registered"); - return false; - } - - // Initialise text - auto txt = svc.getTxt(); - if(txt.count() > ARRAY_SIZE(mdns_info::txt_data)) { - debug_e("Too many MDNS TXT items"); - return false; - } - - // Have to tear down then re-initialise -#ifdef ENABLE_ESPCONN - espconn_mdns_close(); -#else - mdns_close(); -#endif - - service = &svc; - - svc.type = std::move(si.type); - svc.txt = std::move(txt); - - mdns_info mi{}; - mi.host_name = hostname.begin(); - mi.server_name = svc.type.begin(); - mi.ipAddr = WifiStation.getIP(); - mi.server_port = si.port; - - unsigned i = 0; - for(auto s : svc.txt) { - mi.txt_data[i++] = const_cast(s); - } - -#ifdef ENABLE_ESPCONN - espconn_mdns_init(&mi); -#else - mdns_init(&mi); -#endif - - // We can now set the service name - mdns_set_name(si.name.c_str()); -#endif - - return true; -} - -void Responder::end() -{ -#if MDNS_LWIP >= 0x0200 - - mdns_resp_remove_netif(netif_default); - -#else - -#ifdef ENABLE_ESPCONN - espconn_mdns_close(); -#else - mdns_close(); -#endif - - if(service != nullptr) { - service->type = nullptr; - service->txt = nullptr; - service = nullptr; - } - -#endif - - debug_i("MDNS stopped"); -} - -bool Responder::restart() -{ - debug_i("MDNS restarting..."); - -#if MDNS_LWIP >= 0x0202 - - mdns_resp_restart(netif_default); - return true; - -#elif MDNS_LWIP >= 0x0200 - - mdns_resp_netif_settings_changed(netif_default); - return true; - -#else - - auto svc = service; - end(); - return svc ? addService(*svc) : begin(hostname); - -#endif -} - -} // namespace mDNS diff --git a/Sming/Components/mdns/src/Server.cpp b/Sming/Components/mdns/src/Server.cpp deleted file mode 100644 index 5657438f22..0000000000 --- a/Sming/Components/mdns/src/Server.cpp +++ /dev/null @@ -1,112 +0,0 @@ -/**** - * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. - * Created 2015 by Skurydin Alexey - * http://github.com/SmingHub/Sming - * All files of the Sming Core are provided under the LGPL v3 license. - * - * Server.cpp - * - ****/ - -#include "include/Network/Mdns/Server.h" -#include "Packet.h" -#include - -namespace mDNS -{ -Server server; - -Server::~Server() -{ - if(active) { - UdpConnection::leaveMulticastGroup(MDNS_IP); - } -} - -bool Server::search(const String& name, ResourceType type) -{ - Request req(Request::Type::query); - req.addQuestion(name, type); - return send(req); -} - -bool Server::send(Message& message) -{ - if(sendCallback) { - sendCallback(message); - } - - auto buf = reinterpret_cast(message.getData()); - auto len = message.getSize(); - - begin(); - out.listen(0); - return out.sendTo(message.getRemoteIp(), message.getRemotePort(), buf, len); -} - -bool Server::begin() -{ - if(active) { - return true; - } - - auto localIp = WifiStation.getIP(); - - if(!joinMulticastGroup(localIp, MDNS_IP)) { - debug_w("[mDNS] joinMulticastGroup() failed"); - return false; - } - - if(!listen(MDNS_SOURCE_PORT)) { - debug_e("[mDNS] listen failed"); - return false; - } - - setMulticast(localIp); - setMulticastTtl(MDNS_TTL); - - active = true; - return true; -} - -void Server::end() -{ - if(!active) { - return; - } - - close(); - leaveMulticastGroup(MDNS_IP); - active = false; -} - -void Server::UdpOut::onReceive(pbuf* buf, IpAddress remoteIP, uint16_t remotePort) -{ - server.onReceive(buf, remoteIP, remotePort); -} - -void Server::onReceive(pbuf* buf, IpAddress remoteIP, uint16_t remotePort) -{ - if(packetCallback) { - if(!packetCallback(remoteIP, remotePort, static_cast(buf->payload), buf->len)) { - return; - } - } - - if(handlers.isEmpty()) { - return; - } - - Message message(remoteIP, remotePort, buf->payload, buf->len); - if(!message.parse()) { - return; - } - - for(auto& handler : handlers) { - if(!handler.onMessage(message)) { - break; - } - } -} - -} // namespace mDNS diff --git a/Sming/Components/mdns/src/debug.cpp b/Sming/Components/mdns/src/debug.cpp deleted file mode 100644 index 4119dedc30..0000000000 --- a/Sming/Components/mdns/src/debug.cpp +++ /dev/null @@ -1,88 +0,0 @@ -#include "Network/Mdns/debug.h" - -namespace -{ -void print(Print& p, const String& tag, const String& value) -{ - String s = " "; - s += tag; - s += ':'; - while(s.length() < 12) { - s += ' '; - } - p.print(s); - p.print(' '); - p.println(value); -} - -void printHex(Print& p, const String& tag, uint16_t value) -{ - char buf[10]; - m_snprintf(buf, sizeof(buf), "0x%04x", value); - print(p, tag, buf); -} - -void printBool(Print& p, const String& tag, bool value) -{ - print(p, tag, value ? "Y" : "N"); -} - -} // namespace - -namespace mDNS -{ -void printQuestion(Print& p, mDNS::Question& question) -{ - p.println(F(">> Question")); - print(p, F("name"), question.getName()); - auto type = question.getType(); - print(p, F("type"), toString(type)); - printHex(p, F("type"), uint16_t(type)); - printHex(p, F("class"), question.getClass()); - printBool(p, F("unicast"), question.isUnicastReply()); -} - -void printAnswer(Print& p, mDNS::Answer& answer) -{ - p.print(">> "); - p.println(toString(answer.getKind())); - print(p, F("name"), answer.getName()); - print(p, F("data"), answer.getRecordString()); - auto type = answer.getType(); - print(p, F("type"), toString(type)); - printHex(p, F("type"), uint16_t(type)); - print(p, F("ttl"), String(answer.getTtl())); - printHex(p, F("class"), answer.getClass()); - printBool(p, F("flush"), answer.isCachedFlush()); -} - -void printMessage(Print& p, mDNS::Message& message) -{ - p.println(); - p.print(system_get_time()); - p.print(' '); - p.print(message.isReply() ? F("REPLY") : F("QUERY")); - auto ip = message.getRemoteIp(); - if(uint32_t(ip) != 0) { - p.print(F(" from ")); - p.print(message.getRemoteIp().toString()); - p.print(':'); - p.println(message.getRemotePort()); - } else { - p.println(); - } - - p.print(F("Size: ")); - p.print(message.getSize()); - p.println(F(" bytes")); - - for(auto& question : message.questions) { - printQuestion(p, question); - } - - for(auto& answer : message.answers) { - printAnswer(p, answer); - } -} - -} // namespace mDNS diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Answer.h b/Sming/Components/mdns/src/include/Network/Mdns/Answer.h deleted file mode 100644 index 6bac7b0b83..0000000000 --- a/Sming/Components/mdns/src/include/Network/Mdns/Answer.h +++ /dev/null @@ -1,122 +0,0 @@ -/**** - * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. - * Created 2015 by Skurydin Alexey - * http://github.com/SmingHub/Sming - * All files of the Sming Core are provided under the LGPL v3 license. - * - * Answer.h - * - ****/ - -#pragma once - -#include -#include "Name.h" -#include "Resource.h" - -namespace mDNS -{ -class Message; -struct Packet; - -/** - * @brief A single mDNS Answer - */ -class Answer : public LinkedObjectTemplate -{ -public: - using List = LinkedObjectListTemplate; - using OwnedList = OwnedLinkedObjectListTemplate; - - enum class Kind : uint8_t { - answer, - name, - additional, - }; - - Answer(Message& message, Kind kind) : message(message), kind(kind) - { - } - - bool parse(Packet& pkt); - - /** - * @brief Identifies what kind of answer this is - */ - Kind getKind() const - { - return kind; - } - - /** - * @brief Object, domain or zone name - */ - Name getName() const - { - return Name(message, namePtr); - } - - /** - * @brief ResourceRecord type - */ - Resource::Type getType() const; - - /** - * @brief ResourceRecord Class: Normally the value 1 for Internet (“IN”) - */ - uint16_t getClass() const; - - /** - * @brief Flush cache of records matching this name - */ - bool isCachedFlush() const; - - /** - * @brief ResourceRecord Time To Live: Number of seconds ths should be remembered - */ - uint32_t getTtl() const; - - /** - * @brief Get content of record as string - */ - String getRecordString() const; - - Message& getResponse() const - { - return message; - } - - uint8_t* getRecord() const; - - /** - * @brief Get pointer to Resource Record data - */ - uint16_t getRecordPtr() const - { - return namePtr + nameLen + 10; - } - - /** - * @brief Get size of Resource Record - */ - uint16_t getRecordSize() const - { - return recordSize; - } - - // Writing - uint16_t init(uint16_t namePtr, const String& name, Resource::Type type, uint16_t rclass, bool flush, uint32_t ttl); - uint16_t init(uint16_t namePtr, const Name& name, Resource::Type type, uint16_t rclass, bool flush, uint32_t ttl); - void allocate(uint16_t size); - -private: - Message& message; - uint16_t namePtr{0}; - uint16_t recordSize{0}; - uint16_t nameLen{0}; - Kind kind; -}; - -} // namespace mDNS - -String toString(mDNS::Answer::Kind kind); diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Handler.h b/Sming/Components/mdns/src/include/Network/Mdns/Handler.h deleted file mode 100644 index e853acbf94..0000000000 --- a/Sming/Components/mdns/src/include/Network/Mdns/Handler.h +++ /dev/null @@ -1,33 +0,0 @@ -/**** - * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. - * Created 2015 by Skurydin Alexey - * http://github.com/SmingHub/Sming - * All files of the Sming Core are provided under the LGPL v3 license. - * - * Message.h - * - ****/ - -#pragma once - -#include -#include "Message.h" - -namespace mDNS -{ -/** - * @brief Virtual base class used for chaining message handlers - */ -class Handler : public LinkedObjectTemplate -{ -public: - using List = LinkedObjectListTemplate; - - /** - * @brief Callback to be invoked for each received message - * @retval bool Return true to pass message to other handlers, false to stop - */ - virtual bool onMessage(Message& message) = 0; -}; - -} // namespace mDNS diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Message.h b/Sming/Components/mdns/src/include/Network/Mdns/Message.h deleted file mode 100644 index 06f602c9e8..0000000000 --- a/Sming/Components/mdns/src/include/Network/Mdns/Message.h +++ /dev/null @@ -1,140 +0,0 @@ -/**** - * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. - * Created 2015 by Skurydin Alexey - * http://github.com/SmingHub/Sming - * All files of the Sming Core are provided under the LGPL v3 license. - * - * Message.h - * - ****/ - -#pragma once - -#include "Question.h" -#include "Answer.h" - -namespace mDNS -{ -constexpr uint32_t MDNS_IP{0xFB0000E0}; // 224.0.0.251 -constexpr uint16_t MDNS_TARGET_PORT{5353}; -constexpr uint16_t MDNS_SOURCE_PORT{5353}; -constexpr uint16_t MDNS_TTL{255}; - -constexpr uint16_t MAX_PACKET_SIZE{1024}; - -/** - * @brief Encapsulates a message packet for flexible introspection - */ -class Message -{ -public: - enum class Type { - query, - reply, - }; - - Message(IpAddress remoteIp, uint16_t remotePort, void* data, uint16_t size) - : remoteIp(remoteIp), remotePort(remotePort), data(static_cast(data)), size(size) - { - } - - Message(const Message& other) - : Message(other.getRemoteIp(), other.getRemotePort(), other.getData(), other.getSize()) - { - } - - /** - * @brief Parse message data - * @retval bool true if message parsed successfully, false indicates a problem - * - * Does basic validation and builds a list of answers. - */ - bool parse(); - - /** - * @brief Address of sender from UDP packet - */ - IpAddress getRemoteIp() const - { - return remoteIp; - } - - /** - * @brief UDP port in message - */ - uint16_t getRemotePort() const - { - return remotePort; - } - - /** - * @brief Check that message contains answers, not queries - */ - bool isReply() const - { - return data[2] & 0x80; - } - - Type getType() const - { - return isReply() ? Type::reply : Type::query; - } - - /** - * @brief If set, indicates record is split across multiple packets - */ - bool isTruncated() const - { - return data[2] & 0x02; - } - - /** - * @brief Non-zero indicates error - */ - uint8_t getResponseCode() const - { - return data[3] & 0x0f; - } - - uint8_t* getData() const - { - return data; - } - - uint16_t getSize() const - { - return size; - } - - Answer* operator[](ResourceType type); - - // Writing - void allocate(uint16_t recordSize) - { - size += recordSize; - } - - /* - * Resolve a 16-bit 'pointer' to a memory location - * - * Value represents a 16-bit offset from the start of the message data. - * DNS names may contain pointers, but we use this approach internally to improve - * data portability, reduce memory consumption and avoid data duplication during - * message parsing and construction. - */ - uint8_t* resolvePointer(uint16_t pointer) const - { - return data + pointer; - } - - Question::OwnedList questions; - Answer::OwnedList answers; - -protected: - IpAddress remoteIp; - uint16_t remotePort; - uint8_t* data; - uint16_t size; -}; - -} // namespace mDNS diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Name.h b/Sming/Components/mdns/src/include/Network/Mdns/Name.h deleted file mode 100644 index d60ff1b3f2..0000000000 --- a/Sming/Components/mdns/src/include/Network/Mdns/Name.h +++ /dev/null @@ -1,145 +0,0 @@ -/**** - * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. - * Created 2015 by Skurydin Alexey - * http://github.com/SmingHub/Sming - * All files of the Sming Core are provided under the LGPL v3 license. - * - * Name.h - * - ****/ - -#pragma once - -#include - -namespace mDNS -{ -class Message; -struct Packet; - -/** - * @brief Encoded DNS name - * - * mDNS-SD names are represented as instance.service.domain. - * See https://tools.ietf.org/html/rfc6763#section-4.1 - * - * Instance names should be friendly and not attempt to be unique. - * See https://tools.ietf.org/html/rfc6763#appendix-D - * - * Example: "UDP Server._http._tcp.local" - * instance: "UDP Server" - * service: "_http._tcp" - * name: "http" - * protocol: Protocol::Tcp - * domain: "local" - * - */ -class Name -{ -public: - // The mDNS spec says this should never be more than 256 (including trailing '\0'). - static constexpr size_t maxLength{256}; - - Name(Message& message, uint16_t ptr) : message(message), ptr(ptr) - { - } - - Message& getMessage() const - { - return message; - } - - /** - * @brief Get number of bytes occupied by the name - * Not the same as the string length because content is encoded. - */ - uint16_t getDataLength() const; - - String toString() const; - - operator String() const - { - return toString(); - } - - /** - * @brief Get the last element of the name, which must be the domain - * @retval Name - * - * This Result - * ---- ------ - * "UDP Server._http._tcp.local" "local" - * "UDP Server._http._tcp.my.domain.local" "my.domain.local" - */ - Name getDomain() const; - - /** - * @brief Get the service name - * @retval Name - * - * This Result - * ---- ------ - * "UDP Server._http._tcp.local" "_tcp.local" - */ - Name getProtocol() const; - - /** - * @brief Get the service name - * @retval Name - * - * This Result - * ---- ------ - * "UDP Server._http._tcp.local" "_http._tcp.local" - */ - Name getService() const; - - bool equalsIgnoreCase(const char* str, size_t length) const; - - bool equalsIgnoreCase(const String& value) const - { - return equalsIgnoreCase(value.c_str(), value.length()); - } - - bool operator==(const String& value) const - { - return equalsIgnoreCase(value); - } - - bool operator!=(const String& value) const - { - return !operator==(value); - } - - uint16_t getPtr() const - { - return ptr; - } - - /** - * @brief Ensure a pointer refers to actual content, not another pointer - */ - uint16_t makePointer() const; - - /** - * @brief Fixup pointer at end of name to point to another name - * @param other Where to point to - * @retval bool true on success, false if name does not end with a pointer - */ - bool fixup(const Name& other); - -private: - uint16_t read(char* buffer, uint16_t bufSize) const; - - struct ElementPointers { - uint16_t service; - uint16_t protocol; - uint16_t domain; - }; - - ElementPointers parseElements() const; - - Message& message; - uint16_t ptr; -}; - -} // namespace mDNS diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Question.h b/Sming/Components/mdns/src/include/Network/Mdns/Question.h deleted file mode 100644 index 789f6ca6e3..0000000000 --- a/Sming/Components/mdns/src/include/Network/Mdns/Question.h +++ /dev/null @@ -1,74 +0,0 @@ -/**** - * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. - * Created 2015 by Skurydin Alexey - * http://github.com/SmingHub/Sming - * All files of the Sming Core are provided under the LGPL v3 license. - * - * Question.h - * - ****/ - -#pragma once - -#include -#include "Name.h" -#include "Resource.h" - -namespace mDNS -{ -class Message; -struct Packet; - -/** - * @brief A single mDNS Question - */ -class Question : public LinkedObjectTemplate -{ -public: - using List = LinkedObjectListTemplate; - using OwnedList = OwnedLinkedObjectListTemplate; - - Question(Message& message) : message(message) - { - } - - bool parse(Packet& pkt); - - /** - * @brief Object, domain or zone name - */ - Name getName() const - { - return Name(message, namePtr); - } - - /** - * @brief ResourceRecord type - */ - Resource::Type getType() const; - - /** - * @brief ResourceRecord Class: Normally the value 1 for Internet (“IN”) - */ - uint16_t getClass() const; - - /** - * @brief Whether reply should be unicast or multicast - */ - bool isUnicastReply() const; - - Message& getMessage() const - { - return message; - } - - // Writing - uint16_t init(uint16_t namePtr, const String& name, ResourceType type, uint16_t qclass, bool unicast); - -private: - Message& message; - uint16_t namePtr; - uint16_t nameLen; -}; - -} // namespace mDNS diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Request.h b/Sming/Components/mdns/src/include/Network/Mdns/Request.h deleted file mode 100644 index cfa9f7482e..0000000000 --- a/Sming/Components/mdns/src/include/Network/Mdns/Request.h +++ /dev/null @@ -1,72 +0,0 @@ -/**** - * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. - * Created 2015 by Skurydin Alexey - * http://github.com/SmingHub/Sming - * All files of the Sming Core are provided under the LGPL v3 license. - * - * Request.h - * - ****/ - -#pragma once - -#include "Message.h" - -namespace mDNS -{ -class Request : public Message -{ -public: - /** - * @brief Create a multicast message - */ - Request(Type type); - - Question* addQuestion(const String& name, ResourceType type = ResourceType::PTR, uint16_t qclass = 1, - bool unicast = false); - - Answer* createAnswer(const String& name, Resource::Type type, uint16_t rclass, bool flush, uint32_t ttl); - Answer* createAnswer(const Name& name, Resource::Type type, uint16_t rclass, bool flush, uint32_t ttl); - - template Resource addAnswer(const N& name, ParamTypes... params) - { - auto answer = createAnswer(name, Resource::type, 1, false, 120); - Resource r(*answer); - r.init(params...); - return r; - } - - Answer::Kind nextSection() - { - assert(kind < Answer::Kind::additional); - kind = Answer::Kind(unsigned(kind) + 1); - return kind; - } - -private: - uint8_t buffer[MAX_PACKET_SIZE]; - Answer::Kind kind{Answer::Kind::answer}; -}; - -class Query : public Request -{ -public: - Query() : Request(Type::query) - { - } -}; - -class Reply : public Request -{ -public: - Reply(const Question& question) : Request(Type::reply) - { - if(question.isUnicastReply()) { - auto& msg = question.getMessage(); - remoteIp = msg.getRemoteIp(); - remotePort = msg.getRemotePort(); - } - } -}; - -} // namespace mDNS diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Resource.h b/Sming/Components/mdns/src/include/Network/Mdns/Resource.h deleted file mode 100644 index a78298c262..0000000000 --- a/Sming/Components/mdns/src/include/Network/Mdns/Resource.h +++ /dev/null @@ -1,235 +0,0 @@ -/**** - * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. - * Created 2015 by Skurydin Alexey - * http://github.com/SmingHub/Sming - * All files of the Sming Core are provided under the LGPL v3 license. - * - * Resource.h - * - ****/ - -#pragma once - -#include "Name.h" -#include - -struct Ip6Address { - uint8_t addr[16]; -}; - -/** - * @brief MDNS resource type identifiers - * - * (name, value, description) - */ -#define MDNS_RESOURCE_TYPE_MAP(XX) \ - XX(A, 0x0001, "32-bit IPv4 address") \ - XX(SOA, 0x0006, "Authoritative DNS Zone information") \ - XX(PTR, 0x000C, "Pointer to a canonical name") \ - XX(HINFO, 0x000D, "Host Information") \ - XX(TXT, 0x0010, "Arbitrary human-readable text") \ - XX(AAAA, 0x001C, "128-bit IPv6 address") \ - XX(SRV, 0x0021, "Server selection") \ - XX(ANY, 0x00FF, "Matches any resource type in query") - -namespace mDNS -{ -class Answer; - -namespace Resource -{ -enum class Type : uint16_t { -#define XX(name, value, desc) name = value, - MDNS_RESOURCE_TYPE_MAP(XX) -#undef XX -}; - -/** - * @brief Resource Record with no specific type - */ -class Record -{ -public: - Record(const Answer& answer) : answer(const_cast(answer)) - { - } - - String toString() const; - - Answer& answer; - -protected: - uint8_t* getRecord() const; - uint16_t getRecordSize() const; -}; - -/** - * @brief 'A' record containing IP4 address - */ -class A : public Record -{ -public: - static constexpr Resource::Type type{Resource::Type::A}; - - using Record::Record; - - IpAddress getAddress() const; - - String toString() const - { - return getAddress().toString(); - } - - // Writing - void init(IpAddress ipaddr); -}; - -/** - * @brief 'PTR' record containing pointer to a canonical name - */ -class PTR : public Record -{ -public: - static constexpr Resource::Type type{Resource::Type::PTR}; - - using Record::Record; - - Name getName() const; - - String toString() const - { - return getName(); - } - - // Writing - void init(const String& name); -}; - -/** - * @brief 'HINFO' record containing Host information - */ -class HINFO : public Record -{ -public: - static constexpr Resource::Type type{Resource::Type::HINFO}; - - using Record::Record; -}; - -/** - * @brief 'TXT' record containing attribute list - * - * Originally for arbitrary human-readable text in a DNS record. - * Content is a set of name=value pairs. Value can be binary. - */ -class TXT : public Record -{ -public: - static constexpr Resource::Type type{Resource::Type::TXT}; - - using Record::Record; - - uint8_t count() const; - - String operator[](uint8_t index) const; - - String operator[](const char* name) const - { - return getValue(name); - } - - String operator[](const String& name) const - { - return getValue(name.c_str()); - } - - String toString(const String& sep = "; ") const; - - String getValue(const char* name, uint16_t namelen) const; - - String getValue(const char* name) const - { - return getValue(name, strlen(name)); - } - - String getValue(const String& name) const - { - return getValue(name.c_str(), name.length()); - } - - // Writing - void init() - { - } - - void add(const char* value, uint16_t len); - - void add(const String& value) - { - add(value.c_str(), value.length()); - } - - TXT& operator+=(const char* value) - { - add(value, strlen(value)); - return *this; - } - - TXT& operator+=(const String& value) - { - add(value); - return *this; - } - -private: - const char* get(uint8_t index, uint8_t& len) const; - mutable uint8_t mCount{0}; -}; - -/** - * @brief 'AAAA' record containing 128-bit IPv6 address - */ -class AAAA : public Record -{ -public: - static constexpr Resource::Type type{Resource::Type::AAAA}; - - using Record::Record; - - String toString() const; - - // Writing - void init(Ip6Address addr); -}; - -/** - * @brief 'SRV' Service Locator record - */ -class SRV : public Record -{ -public: - static constexpr Resource::Type type{Resource::Type::SRV}; - - using Record::Record; - - uint16_t getPriority() const; - - uint16_t getWeight() const; - - uint16_t getPort() const; - - Name getHost() const; - - String toString(const String& sep = "; ") const; - - // Writing - void init(uint16_t priority, uint16_t weight, uint16_t port, const String& host); -}; - -} // namespace Resource - -using ResourceType = Resource::Type; - -} // namespace mDNS - -String toString(mDNS::ResourceType type); diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Responder.h b/Sming/Components/mdns/src/include/Network/Mdns/Responder.h deleted file mode 100644 index e1ec7969b7..0000000000 --- a/Sming/Components/mdns/src/include/Network/Mdns/Responder.h +++ /dev/null @@ -1,58 +0,0 @@ -/**** - * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. - * Created 2015 by Skurydin Alexey - * http://github.com/SmingHub/Sming - * All files of the Sming Core are provided under the LGPL v3 license. - * - * Responder.h - * - ****/ - -#pragma once - -#include "Service.h" - -namespace mDNS -{ -class Responder -{ -public: - /** - * @brief Initialise the responder - * @param hostname - * @retval bool true on success - */ - bool begin(const String& hostname); - - /** - * @brief Stop the responder - * - * Must reinitialise the stack again to restart - */ - void end(); - - /** - * @brief Add a service object - * - * Responder doesn't own the object, must remain in scope - * - * @param svc Service object - * @retval bool true on success - */ - bool addService(Service& svc); - - /** - * @brief Restart a running stack - * - * Call when IP address changes, for example. - */ - bool restart(); - -private: -#if LWIP_VERSION_MAJOR == 1 - String hostname; - Service* service = nullptr; -#endif -}; - -} // namespace mDNS diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Server.h b/Sming/Components/mdns/src/include/Network/Mdns/Server.h deleted file mode 100644 index 20262f763b..0000000000 --- a/Sming/Components/mdns/src/include/Network/Mdns/Server.h +++ /dev/null @@ -1,128 +0,0 @@ -/**** - * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. - * Created 2015 by Skurydin Alexey - * http://github.com/SmingHub/Sming - * All files of the Sming Core are provided under the LGPL v3 license. - * - * Server.h - * - * Code originally based on MrDunk's mDNS code distributed under the MIT license. - * - * MIT license: https://github.com/mrdunk/esp8266_mdns/blob/master/LICENCE.txt - * - * However, code has been essentially rewritten so Sming LGPL v3 license applies. - * - */ - -#pragma once - -#include -#include -#include "Request.h" -#include "Handler.h" - -namespace mDNS -{ -/** - * @brief Locates mDNS services by issuing queries - */ -class Server : protected UdpConnection -{ -public: - /** - * @brief Callback to be invoked for each received message - * @retval bool See `onSend()` - */ - using MessageDelegate = Delegate; - - /** - * @brief Callback to be invoked with raw data (debugging, etc.) - * @retval bool See `onPacket()` - */ - using PacketDelegate = Delegate; - - ~Server(); - - bool begin(); - - void end(); - - bool restart() - { - end(); - return begin(); - } - - /** - * @brief Set callback to be invoked for each received message - * @param callback Return false from callback to prevent message being passed to other clients - */ - void addHandler(Handler& handler) - { - handlers.add(&handler); - } - - /** - * @brief Remove a message handler - * @note If there are no more handlers then consider setting a timeout and then shutting the server down. - */ - void removeHandler(Handler& handler) - { - handlers.remove(&handler); - } - - /** - * @brief Set callback to be invoked before sending a message - * @param callback Return true from callback to actually send packet - */ - void onSend(MessageDelegate callback) - { - sendCallback = callback; - } - - /** - * @brief Set callback to be invoked for raw received data, before parsing - * @param callback Return true from callback to actually send packet - */ - void onPacket(PacketDelegate callback) - { - packetCallback = callback; - } - - /** - * @brief Send a multicast query - * @param hostname Name to find, e.g. "_googlecast._tcp.local" - * @param type - * @retval bool false if parameters failed validation or UDP request could not be sent - */ - bool search(const String& hostname, ResourceType type = ResourceType::PTR); - - /** - * @brief Send an mDNS message containing questions/answers - * @retval bool true if message sent successfully - */ - bool send(Message& message); - -protected: - void onReceive(pbuf* buf, IpAddress remoteIP, uint16_t remotePort) override; - -private: - /* - * Need a separate UDP connection for sending requests - */ - class UdpOut : public UdpConnection - { - protected: - void onReceive(pbuf* buf, IpAddress remoteIP, uint16_t remotePort) override; - }; - - Handler::List handlers; - MessageDelegate sendCallback; - PacketDelegate packetCallback; - UdpOut out; - bool active{false}; -}; - -extern Server server; - -} // namespace mDNS diff --git a/Sming/Components/mdns/src/include/Network/Mdns/Service.h b/Sming/Components/mdns/src/include/Network/Mdns/Service.h deleted file mode 100644 index f40d1f4bd9..0000000000 --- a/Sming/Components/mdns/src/include/Network/Mdns/Service.h +++ /dev/null @@ -1,82 +0,0 @@ -/**** - * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. - * Created 2015 by Skurydin Alexey - * http://github.com/SmingHub/Sming - * All files of the Sming Core are provided under the LGPL v3 license. - * - * Service.h - * - ****/ - -#pragma once - -#include -#include -#include - -#if ENABLE_CUSTOM_LWIP == 2 -#ifdef ARCH_ESP8266 -#define MDNS_LWIP 0x0200 -#else -#define MDNS_LWIP 0x0202 -#endif -#elif ENABLE_CUSTOM_LWIP == 1 -#define MDNS_LWIP 0x0101 -#else -#define MDNS_LWIP 0x0100 -#endif - -namespace mDNS -{ -class Responder; - -class Service -{ -public: - enum class Protocol { - Udp /* = DNSSD_PROTO_UCP */, - Tcp /* = DNSSD_PROTO_TCP */, - }; - - /** - * @brief Basic service information - * - * Defaults fine for most cases, just need to set name. - */ - struct Info { - String name = "Sming"; - String type = "http"; - Protocol protocol = Protocol::Tcp; - uint16_t port = 80; - }; - - /** - * @brief Override to obtain service information - */ - virtual Info getInfo() = 0; - - /** - * @brief Override to obtain txt items - * - * LWIP2 calls this each time a TXT reply is created - * Other implementations call it via begin(). - * - * @retval CStringArray List of txt items, e.g. name=value pairs - */ - virtual CStringArray getTxt() - { - return nullptr; - } - -private: - friend class Responder; - -#if MDNS_LWIP >= 0x0200 - int8_t id = -1; -#else - String type; - CStringArray txt; -#endif -}; - -} // namespace mDNS diff --git a/Sming/Components/mdns/src/include/Network/Mdns/debug.h b/Sming/Components/mdns/src/include/Network/Mdns/debug.h deleted file mode 100644 index b29aacbd54..0000000000 --- a/Sming/Components/mdns/src/include/Network/Mdns/debug.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#include -#include "Message.h" - -namespace mDNS -{ -void printQuestion(Print& p, Question& question); -void printAnswer(Print& p, Answer& answer); -void printMessage(Print& p, Message& message); - -} // namespace mDNS diff --git a/Sming/Libraries/MDNS b/Sming/Libraries/MDNS new file mode 160000 index 0000000000..6d4c9801e8 --- /dev/null +++ b/Sming/Libraries/MDNS @@ -0,0 +1 @@ +Subproject commit 6d4c9801e8935fac5d2a4ddb41643929683c183e diff --git a/samples/Basic_Mdns/.cproject b/samples/Basic_Mdns/.cproject deleted file mode 100644 index e1450b6031..0000000000 --- a/samples/Basic_Mdns/.cproject +++ /dev/null @@ -1,151 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - make - -f ${ProjDirPath}/Makefile - all - true - true - true - - - make - -f ${ProjDirPath}/Makefile - clean - true - true - true - - - make - -f ${ProjDirPath}/Makefile - flash - true - true - true - - - make - -f ${ProjDirPath}/Makefile - flashonefile - true - true - true - - - make - -f ${ProjDirPath}/Makefile - flashinit - true - true - true - - - make - -f ${ProjDirPath}/Makefile - flashboot - true - true - true - - - make - -f ${ProjDirPath}/Makefile - rebuild - true - true - true - - - - - - - - - - - - - - - - - - - - diff --git a/samples/Basic_Mdns/.project b/samples/Basic_Mdns/.project deleted file mode 100644 index 301ff6d716..0000000000 --- a/samples/Basic_Mdns/.project +++ /dev/null @@ -1,28 +0,0 @@ - - - Basic_Mdns - - - SmingFramework - - - - org.eclipse.cdt.managedbuilder.core.genmakebuilder - clean,full,incremental, - - - - - org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder - full,incremental, - - - - - - org.eclipse.cdt.core.cnature - org.eclipse.cdt.core.ccnature - org.eclipse.cdt.managedbuilder.core.managedBuildNature - org.eclipse.cdt.managedbuilder.core.ScannerConfigNature - - diff --git a/samples/Basic_Mdns/Makefile b/samples/Basic_Mdns/Makefile deleted file mode 100644 index ff51b6c3a7..0000000000 --- a/samples/Basic_Mdns/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -##################################################################### -#### Please don't change this file. Use component.mk instead #### -##################################################################### - -ifndef SMING_HOME -$(error SMING_HOME is not set: please configure it as an environment variable) -endif - -include $(SMING_HOME)/project.mk diff --git a/samples/Basic_Mdns/README.rst b/samples/Basic_Mdns/README.rst deleted file mode 100644 index d03d2b53c5..0000000000 --- a/samples/Basic_Mdns/README.rst +++ /dev/null @@ -1,4 +0,0 @@ -Basic MDNS -========== - -Demonstrates use of the :component:`MDNS` library to discover local devices. diff --git a/samples/Basic_Mdns/app/application.cpp b/samples/Basic_Mdns/app/application.cpp deleted file mode 100644 index c6cd5dad61..0000000000 --- a/samples/Basic_Mdns/app/application.cpp +++ /dev/null @@ -1,267 +0,0 @@ -#include -#include -#include -#include - -// If you want, you can define WiFi settings globally in Eclipse Environment Variables -#ifndef WIFI_SSID -#define WIFI_SSID "PleaseEnterSSID" // Put you SSID and Password here -#define WIFI_PWD "PleaseEnterPass" -#endif - -class MessageHandler : public mDNS::Handler -{ -public: - /* - * Set the DNS record name we're interested in. - * Used as a filter in our `onMessage` method. - */ - void setSearchName(const String& name) - { - searchName = name; - } - - bool onMessage(mDNS::Message& message) override; - -private: - String searchName; -}; - -static MessageHandler myMessageHandler; - -#ifdef ARCH_HOST -bool savePacket(IpAddress remoteIP, uint16_t remotePort, const uint8_t* data, size_t length) -{ - auto& hostfs = IFS::Host::getFileSystem(); - String filename; - filename = "out/mdns/"; - hostfs.makedirs(filename); - filename += DateTime(SystemClock.now()).toISO8601(); - filename += '-'; - filename += remoteIP.toString(); - filename += '-'; - filename += remotePort; - filename += ".bin"; - filename.replace(':', '-'); - hostfs.setContent(filename, data, length); - - return true; -} -#endif - -bool MessageHandler::onMessage(mDNS::Message& message) -{ - mDNS::printMessage(Serial, message); - - // Check if we're interested in this message - if(!message.isReply()) { - debug_i("Ignoring query"); - return false; - } - auto answer = message[mDNS::ResourceType::PTR]; - if(answer == nullptr) { - debug_i("Ignoring message: no PTR record"); - return false; - } - if(answer->getName() != searchName) { - debug_i("Ignoring message: Name doesn't match"); - return false; - } - - // Extract our required information from the message - struct { - String manufacturerDevice; - String friendlyName; - IpAddress ipaddr; - } info; - - answer = message[mDNS::ResourceType::TXT]; - if(answer != nullptr) { - mDNS::Resource::TXT txt(*answer); - info.manufacturerDevice = txt["md"]; - info.friendlyName = txt["fn"]; - } - - answer = message[mDNS::ResourceType::A]; - if(answer != nullptr) { - mDNS::Resource::A a(*answer); - info.ipaddr = a.getAddress(); - } - - Serial.print(F("Manufacturer Device = '")); - Serial.print(info.manufacturerDevice); - Serial.print(F("', Friendly Name = '")); - Serial.print(info.friendlyName); - Serial.print(F("', IP Address = ")); - Serial.println(info.ipaddr); - - return true; -} - -void sendSearch() -{ - String name = F("_googlecast._tcp.local"); - - // To discover all DNS-SD registered services, use: - // name = F("_services._dns-sd._udp.local"); - - bool ok = mDNS::server.search(name); - debug_i("search('%s'): %s", name.c_str(), ok ? "OK" : "FAIL"); - // Tell our handler what we're looking for - myMessageHandler.setSearchName(name); -} - -void gotIP(IpAddress ip, IpAddress netmask, IpAddress gateway) -{ - Serial.print(F("Connected. Got IP: ")); - Serial.println(ip); - - mDNS::server.onSend([](mDNS::Message& msg) { - printMessage(Serial, msg); - return true; - }); - mDNS::server.addHandler(myMessageHandler); -#ifdef ARCH_HOST - mDNS::server.onPacket(savePacket); -#endif - - // Issue a search now - sendSearch(); - - // Repeat every 30 seconds - auto timer = new Timer; - timer->initializeMs<30000>(sendSearch); - timer->start(); -} - -void connectFail(const String& ssid, MacAddress bssid, WifiDisconnectReason reason) -{ - Serial.println(F("I'm NOT CONNECTED!")); -} - -void parseFile(const String& name, const void* data, size_t length) -{ - Serial.println(); - Serial.print(_F("** Parsing '")); - Serial.print(name); - Serial.println(_F("' **")); - mDNS::Message message(0U, 0, const_cast(data), length); - if(message.parse()) { - mDNS::printMessage(Serial, message); - } - Serial.println(_F("** End of test packet **")); - Serial.println(); -} - -void parseFile(const String& name, const String& data) -{ - parseFile(name, data.c_str(), data.length()); -} - -void parseFile(const String& name, const mDNS::Message& message) -{ - parseFile(name, message.getData(), message.getSize()); -} - -void test() -{ - debug_i("sizeof(mDNS::Server) = %u", sizeof(mDNS::Server)); - debug_i("sizeof(mDNS::Message) = %u", sizeof(mDNS::Message)); - debug_i("sizeof(mDNS::Question) = %u", sizeof(mDNS::Question)); - debug_i("sizeof(mDNS::Answer) = %u", sizeof(mDNS::Answer)); - debug_i("sizeof(LinkedObject) = %u", sizeof(LinkedObject)); - - using namespace mDNS; - - // Create list of questions, parse the resulting data and print the result - { - Query query; - query.addQuestion(F("_chromecast._tcp.local")); - query.addQuestion(F("_%9832479817234_sming._tcp.local"), mDNS::ResourceType::PTR); - query.addQuestion(F("_sming._tcp.local"), mDNS::ResourceType::PTR); - parseFile(F("Test questions"), query); - } - - // Create message records - { - /* - * Service is "_test", hostname is "sming" - * - * We also demonstrate here how to set up a name pointer. - */ - Request reply(Request::Type::reply); - - /** - * Anywhere a Name is used, if the final character is '.' then it is stored as a 'pointer', - * a 16-bit offset into the message data. We don't know the value of that pointer yet, so a - * placeholder is inserted. - * - * In this case, we want our PTR record to reference the name of the SRV record which we add later. - */ - auto ptr = reply.addAnswer(F("_test._tcp.local"), "."); - - // Move to 'nameserver' records section - reply.nextSection(); - // Move to 'additional' records section - reply.nextSection(); - - /** - * We'll use a pointer for the name here, saves space. - */ - auto txt = reply.addAnswer("."); - txt.add("pm=12"); - txt.add("fn=My friendly name"); - auto srv = reply.addAnswer(F("sming._test._tcp.local"), 1, 2, 8080, F("sming.local")); - - /** - * Now the SRV record is constructed we can fix our name references. - * The 'fixup' call will essentially replace the final '.' we added above with a pointer to the actual name. - * Note that the call will fail if we didn't append that final '.', since there will be nowhere to write - * the pointer value. - */ - auto serviceName = srv.answer.getName(); - ptr.getName().fixup(serviceName); - txt.answer.getName().fixup(serviceName); - - /** - * Finally, write the address records - * We can re-use the host Name from the SRV record, storing a pointer instead of the full text - */ - auto hostName = srv.getHost(); - reply.addAnswer(hostName, WifiStation.getIP()); - reply.addAnswer(hostName, Ip6Address()); - - parseFile(F("Test answers"), reply); - } - -// For host, read the resource directory directly as we might want to add other files there -#ifdef ARCH_HOST - - auto& fs = IFS::Host::getFileSystem(); - IFS::Directory dir(&fs); - if(dir.open("resource")) { - while(dir.next()) { - String filename = dir.getDirName() + "/" + String(dir.stat().name); - String data(fs.getContent(filename)); - parseFile(filename, data); - } - } -#endif -} - -void init() -{ - Serial.begin(SERIAL_BAUD_RATE); - Serial.systemDebugOutput(true); // Allow debug print to serial - - test(); - - // Setup the WIFI connection - // IMPORTANT: MUST disable AP or multicast won't work - WifiAccessPoint.enable(false); - WifiStation.enable(true); - WifiStation.config(WIFI_SSID, WIFI_PWD); // Put you SSID and Password here - - WifiEvents.onStationGotIP(gotIP); - WifiEvents.onStationDisconnect(connectFail); -} diff --git a/samples/Basic_Mdns/component.mk b/samples/Basic_Mdns/component.mk deleted file mode 100644 index 1560c918d0..0000000000 --- a/samples/Basic_Mdns/component.mk +++ /dev/null @@ -1 +0,0 @@ -COMPONENT_DEPENDS := mdns diff --git a/samples/Basic_Mdns/resource/192.168.1.100.mdns b/samples/Basic_Mdns/resource/192.168.1.100.mdns deleted file mode 100644 index 7f15eaab348616b1707c6ba0203535ea2b47e6ec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 497 zcmZQzXkh>XMj&S9j!)0ePtQqBPAo2Ai7!bmV9m)-PRwCo-~r25FgR)i2LKCV$lose`Cgc+wK{Rh7vfoXbMVb+3oVc3r5ZcG@~$?dxBG6O~Gev5$q-4-%j4dYObl z5{I5onHO30UJ|qGaXMfQ;vcX9 FkpTP|7_k5V diff --git a/samples/UdpServer_mDNS/component.mk b/samples/UdpServer_mDNS/component.mk index ed2eaeb314..331b317ed9 100644 --- a/samples/UdpServer_mDNS/component.mk +++ b/samples/UdpServer_mDNS/component.mk @@ -1,3 +1,3 @@ HWCONFIG := spiffs SPIFF_FILES := -COMPONENT_DEPENDS += mdns +COMPONENT_DEPENDS += MDNS