/* * LEAmDNS.h * (c) 2018, LaborEtArs * * Version 0.9 beta * * Some notes (from LaborEtArs, 2018): * Essentially, this is an rewrite of the original EPS8266 Multicast DNS code (ESP8266mDNS). * The target of this rewrite was to keep the existing interface as stable as possible while * adding and extending the supported set of mDNS features. * A lot of the additions were basicly taken from Erik Ekman's lwIP mdns app code. * * Supported mDNS features (in some cases somewhat limited): * - Presenting a DNS-SD service to interested observers, eg. a http server by presenting _http._tcp service * - Support for multi-level compressed names in input; in output only a very simple one-leven full-name compression is implemented * - Probing host and service domains for uniqueness in the local network * - Tiebreaking while probing is supportet in a very minimalistic way (the 'higher' IP address wins the tiebreak) * - Announcing available services after successful probing * - Using fixed service TXT items or * - Using dynamic service TXT items for presented services (via callback) * - Remove services (and un-announcing them to the observers by sending goodbye-messages) * - Static queries for DNS-SD services (creating a fixed answer set after a certain timeout period) * - Dynamic queries for DNS-SD services with cached and updated answers and user notifications * * * Usage: * In most cases, this implementation should work as a 'drop-in' replacement for the original * ESP8266 Multicast DNS code. Adjustments to the existing code would only be needed, if some * of the new features should be used. * * For presenting services: * In 'setup()': * Install a callback for the probing of host (and service) domains via 'MDNS.setProbeResultCallback(probeResultCallback, &userData);' * Register DNS-SD services with 'MDNSResponder::hMDNSService hService = MDNS.addService("MyESP", "http", "tcp", 5000);' * (Install additional callbacks for the probing of these service domains via 'MDNS.setServiceProbeResultCallback(hService, probeResultCallback, &userData);') * Add service TXT items with 'MDNS.addServiceTxt(hService, "c#", "1");' or by installing a service TXT callback * using 'MDNS.setDynamicServiceTxtCallback(dynamicServiceTxtCallback, &userData);' or service specific * 'MDNS.setDynamicServiceTxtCallback(hService, dynamicServiceTxtCallback, &userData);' * Call MDNS.begin("MyHostname"); * * In 'probeResultCallback(MDNSResponder* p_MDNSResponder, const char* p_pcDomain, MDNSResponder:hMDNSService p_hService, bool p_bProbeResult, void* p_pUserdata)': * Check the probe result and update the host or service domain name if the probe failed * * In 'dynamicServiceTxtCallback(MDNSResponder* p_MDNSResponder, const hMDNSService p_hService, void* p_pUserdata)': * Add dynamic TXT items by calling 'MDNS.addDynamicServiceTxt(p_hService, "c#", "1");' * * In loop(): * Call 'MDNS.update();' * * * For querying services: * Static: * Call 'uint32_t u32AnswerCount = MDNS.queryService("http", "tcp");' * Iterate answers by: 'for (uint32_t u=0; u // for UdpContext.h #include "WiFiUdp.h" #include "lwip/udp.h" #include "debug.h" #include "include/UdpContext.h" #include #include #include #include "ESP8266WiFi.h" namespace esp8266 { /** * LEAmDNS */ namespace MDNSImplementation { //this should be defined at build time #ifndef ARDUINO_BOARD #define ARDUINO_BOARD "generic" #endif #define MDNS_IP4_SUPPORT //#define MDNS_IP6_SUPPORT #ifdef MDNS_IP4_SUPPORT #define MDNS_IP4_SIZE 4 #endif #ifdef MDNS_IP6_SUPPORT #define MDNS_IP6_SIZE 16 #endif /* * Maximum length for all service txts for one service */ #define MDNS_SERVICE_TXT_MAXLENGTH 1300 /* * Maximum length for a full domain name eg. MyESP._http._tcp.local */ #define MDNS_DOMAIN_MAXLENGTH 256 /* * Maximum length of on label in a domain name (length info fits into 6 bits) */ #define MDNS_DOMAIN_LABEL_MAXLENGTH 63 /* * Maximum length of a service name eg. http */ #define MDNS_SERVICE_NAME_LENGTH 15 /* * Maximum length of a service protocol name eg. tcp */ #define MDNS_SERVICE_PROTOCOL_LENGTH 3 /* * Default timeout for static service queries */ #define MDNS_QUERYSERVICES_WAIT_TIME 1000 /** * MDNSResponder */ class MDNSResponder { public: /* INTERFACE */ MDNSResponder(void); virtual ~MDNSResponder(void); // Start the MDNS responder by setting the default hostname // Later call MDNS::update() in every 'loop' to run the process loop // (probing, announcing, responding, ...) // if interfaceAddress is not specified, default interface is STA, or AP when STA is not set bool begin(const char* p_pcHostname, const IPAddress& p_IPAddress = INADDR_ANY, uint32_t p_u32TTL = 120 /*ignored*/); bool begin(const String& p_strHostname, const IPAddress& p_IPAddress = INADDR_ANY, uint32_t p_u32TTL = 120 /*ignored*/) {return begin(p_strHostname.c_str(), p_IPAddress, p_u32TTL);} // Finish MDNS processing bool close(void); // for esp32 compatability bool end(void); // Change hostname (probing is restarted) bool setHostname(const char* p_pcHostname); // for compatibility... bool setHostname(String p_strHostname); /** * hMDNSService (opaque handle to access the service) */ typedef const void* hMDNSService; // Add a new service to the MDNS responder. If no name (instance name) is given (p_pcName = 0) // the current hostname is used. If the hostname is changed later, the instance names for // these 'auto-named' services are changed to the new name also (and probing is restarted). // The usual '_' before p_pcService (eg. http) and protocol (eg. tcp) may be given. hMDNSService addService(const char* p_pcName, const char* p_pcService, const char* p_pcProtocol, uint16_t p_u16Port); // Removes a service from the MDNS responder bool removeService(const hMDNSService p_hService); bool removeService(const char* p_pcInstanceName, const char* p_pcServiceName, const char* p_pcProtocol); // for compatibility... bool addService(String p_strServiceName, String p_strProtocol, uint16_t p_u16Port); // Change the services instance name (and restart probing). bool setServiceName(const hMDNSService p_hService, const char* p_pcInstanceName); //for compatibility //Warning: this has the side effect of changing the hostname. //TODO: implement instancename different from hostname void setInstanceName(const char* p_pcHostname) {setHostname(p_pcHostname);} // for esp32 compatibilty void setInstanceName(const String& s_pcHostname) {setInstanceName(s_pcHostname.c_str());} /** * hMDNSTxt (opaque handle to access the TXT items) */ typedef void* hMDNSTxt; // Add a (static) MDNS TXT item ('key' = 'value') to the service hMDNSTxt addServiceTxt(const hMDNSService p_hService, const char* p_pcKey, const char* p_pcValue); hMDNSTxt addServiceTxt(const hMDNSService p_hService, const char* p_pcKey, uint32_t p_u32Value); hMDNSTxt addServiceTxt(const hMDNSService p_hService, const char* p_pcKey, uint16_t p_u16Value); hMDNSTxt addServiceTxt(const hMDNSService p_hService, const char* p_pcKey, uint8_t p_u8Value); hMDNSTxt addServiceTxt(const hMDNSService p_hService, const char* p_pcKey, int32_t p_i32Value); hMDNSTxt addServiceTxt(const hMDNSService p_hService, const char* p_pcKey, int16_t p_i16Value); hMDNSTxt addServiceTxt(const hMDNSService p_hService, const char* p_pcKey, int8_t p_i8Value); // Remove an existing (static) MDNS TXT item from the service bool removeServiceTxt(const hMDNSService p_hService, const hMDNSTxt p_hTxt); bool removeServiceTxt(const hMDNSService p_hService, const char* p_pcKey); bool removeServiceTxt(const char* p_pcinstanceName, const char* p_pcServiceName, const char* p_pcProtocol, const char* p_pcKey); // for compatibility... bool addServiceTxt(const char* p_pcService, const char* p_pcProtocol, const char* p_pcKey, const char* p_pcValue); bool addServiceTxt(String p_strService, String p_strProtocol, String p_strKey, String p_strValue); /** * MDNSDynamicServiceTxtCallbackFn * Callback function for dynamic MDNS TXT items */ typedef std::function MDNSDynamicServiceTxtCallbackFunc; // Set a global callback for dynamic MDNS TXT items. The callback function is called // every time, a TXT item is needed for one of the installed services. bool setDynamicServiceTxtCallback(MDNSDynamicServiceTxtCallbackFunc p_fnCallback); // Set a service specific callback for dynamic MDNS TXT items. The callback function // is called every time, a TXT item is needed for the given service. bool setDynamicServiceTxtCallback(const hMDNSService p_hService, MDNSDynamicServiceTxtCallbackFunc p_fnCallback); // Add a (dynamic) MDNS TXT item ('key' = 'value') to the service // Dynamic TXT items are removed right after one-time use. So they need to be added // every time the value s needed (via callback). hMDNSTxt addDynamicServiceTxt(hMDNSService p_hService, const char* p_pcKey, const char* p_pcValue); hMDNSTxt addDynamicServiceTxt(hMDNSService p_hService, const char* p_pcKey, uint32_t p_u32Value); hMDNSTxt addDynamicServiceTxt(hMDNSService p_hService, const char* p_pcKey, uint16_t p_u16Value); hMDNSTxt addDynamicServiceTxt(hMDNSService p_hService, const char* p_pcKey, uint8_t p_u8Value); hMDNSTxt addDynamicServiceTxt(hMDNSService p_hService, const char* p_pcKey, int32_t p_i32Value); hMDNSTxt addDynamicServiceTxt(hMDNSService p_hService, const char* p_pcKey, int16_t p_i16Value); hMDNSTxt addDynamicServiceTxt(hMDNSService p_hService, const char* p_pcKey, int8_t p_i8Value); // Perform a (static) service query. The function returns after p_u16Timeout milliseconds // The answers (the number of received answers is returned) can be retrieved by calling // - answerHostname (or hostname) // - answerIP (or IP) // - answerPort (or port) uint32_t queryService(const char* p_pcService, const char* p_pcProtocol, const uint16_t p_u16Timeout = MDNS_QUERYSERVICES_WAIT_TIME); bool removeQuery(void); // for compatibility... uint32_t queryService(String p_strService, String p_strProtocol); const char* answerHostname(const uint32_t p_u32AnswerIndex); IPAddress answerIP(const uint32_t p_u32AnswerIndex); uint16_t answerPort(const uint32_t p_u32AnswerIndex); // for compatibility... String hostname(const uint32_t p_u32AnswerIndex); IPAddress IP(const uint32_t p_u32AnswerIndex); uint16_t port(const uint32_t p_u32AnswerIndex); /** * hMDNSServiceQuery (opaque handle to access dynamic service queries) */ typedef const void* hMDNSServiceQuery; /** * enuServiceQueryAnswerType */ typedef enum _enuServiceQueryAnswerType { ServiceQueryAnswerType_ServiceDomain = (1 << 0), // Service instance name ServiceQueryAnswerType_HostDomainAndPort = (1 << 1), // Host domain and service port ServiceQueryAnswerType_Txts = (1 << 2), // TXT items #ifdef MDNS_IP4_SUPPORT ServiceQueryAnswerType_IP4Address = (1 << 3), // IP4 address #endif #ifdef MDNS_IP6_SUPPORT ServiceQueryAnswerType_IP6Address = (1 << 4), // IP6 address #endif } enuServiceQueryAnswerType; enum class AnswerType : uint32_t { Unknown = 0, ServiceDomain = ServiceQueryAnswerType_ServiceDomain, HostDomainAndPort = ServiceQueryAnswerType_HostDomainAndPort, Txt = ServiceQueryAnswerType_Txts, #ifdef MDNS_IP4_SUPPORT IP4Address = ServiceQueryAnswerType_IP4Address, #endif #ifdef MDNS_IP6_SUPPORT IP6Address = ServiceQueryAnswerType_IP6Address, #endif }; /** * MDNSServiceQueryCallbackFn * Callback function for received answers for dynamic service queries */ struct MDNSServiceInfo; // forward declaration typedef std::function MDNSServiceQueryCallbackFunc; // Install a dynamic service query. For every received answer (part) the given callback // function is called. The query will be updated every time, the TTL for an answer // has timed-out. // The answers can also be retrieved by calling // - answerCount // - answerServiceDomain // - hasAnswerHostDomain/answerHostDomain // - hasAnswerIP4Address/answerIP4Address // - hasAnswerIP6Address/answerIP6Address // - hasAnswerPort/answerPort // - hasAnswerTxts/answerTxts hMDNSServiceQuery installServiceQuery(const char* p_pcService, const char* p_pcProtocol, MDNSServiceQueryCallbackFunc p_fnCallback); // Remove a dynamic service query bool removeServiceQuery(hMDNSServiceQuery p_hServiceQuery); uint32_t answerCount(const hMDNSServiceQuery p_hServiceQuery); std::vector answerInfo (const MDNSResponder::hMDNSServiceQuery p_hServiceQuery); const char* answerServiceDomain(const hMDNSServiceQuery p_hServiceQuery, const uint32_t p_u32AnswerIndex); bool hasAnswerHostDomain(const hMDNSServiceQuery p_hServiceQuery, const uint32_t p_u32AnswerIndex); const char* answerHostDomain(const hMDNSServiceQuery p_hServiceQuery, const uint32_t p_u32AnswerIndex); #ifdef MDNS_IP4_SUPPORT bool hasAnswerIP4Address(const hMDNSServiceQuery p_hServiceQuery, const uint32_t p_u32AnswerIndex); uint32_t answerIP4AddressCount(const hMDNSServiceQuery p_hServiceQuery, const uint32_t p_u32AnswerIndex); IPAddress answerIP4Address(const hMDNSServiceQuery p_hServiceQuery, const uint32_t p_u32AnswerIndex, const uint32_t p_u32AddressIndex); #endif #ifdef MDNS_IP6_SUPPORT bool hasAnswerIP6Address(const hMDNSServiceQuery p_hServiceQuery, const uint32_t p_u32AnswerIndex); uint32_t answerIP6AddressCount(const hMDNSServiceQuery p_hServiceQuery, const uint32_t p_u32AnswerIndex); IPAddress answerIP6Address(const hMDNSServiceQuery p_hServiceQuery, const uint32_t p_u32AnswerIndex, const uint32_t p_u32AddressIndex); #endif bool hasAnswerPort(const hMDNSServiceQuery p_hServiceQuery, const uint32_t p_u32AnswerIndex); uint16_t answerPort(const hMDNSServiceQuery p_hServiceQuery, const uint32_t p_u32AnswerIndex); bool hasAnswerTxts(const hMDNSServiceQuery p_hServiceQuery, const uint32_t p_u32AnswerIndex); // Get the TXT items as a ';'-separated string const char* answerTxts(const hMDNSServiceQuery p_hServiceQuery, const uint32_t p_u32AnswerIndex); /** * MDNSProbeResultCallbackFn * Callback function for (host and service domain) probe results */ typedef std::function MDNSHostProbeFn; typedef std::function MDNSHostProbeFn1; typedef std::function MDNSServiceProbeFn; typedef std::function MDNSServiceProbeFn1; // Set a global callback function for host and service probe results // The callback function is called, when the probing for the host domain // (or a service domain, which hasn't got a service specific callback) // Succeeds or fails. // In case of failure, the failed domain name should be changed. bool setHostProbeResultCallback(MDNSHostProbeFn p_fnCallback); bool setHostProbeResultCallback(MDNSHostProbeFn1 p_fnCallback); // Set a service specific probe result callback bool setServiceProbeResultCallback(const MDNSResponder::hMDNSService p_hService, MDNSServiceProbeFn p_fnCallback); bool setServiceProbeResultCallback(const MDNSResponder::hMDNSService p_hService, MDNSServiceProbeFn1 p_fnCallback); // Application should call this whenever AP is configured/disabled bool notifyAPChange(void); // 'update' should be called in every 'loop' to run the MDNS processing bool update(void); // 'announce' can be called every time, the configuration of some service // changes. Mainly, this would be changed content of TXT items. bool announce(void); // Enable OTA update hMDNSService enableArduino(uint16_t p_u16Port, bool p_bAuthUpload = false); // Domain name helper static bool indexDomain(char*& p_rpcDomain, const char* p_pcDivider = "-", const char* p_pcDefaultDomain = 0); /** STRUCTS **/ public: /** * MDNSServiceInfo, used in application callbacks */ struct MDNSServiceInfo { MDNSServiceInfo(MDNSResponder& p_pM,MDNSResponder::hMDNSServiceQuery p_hS,uint32_t p_u32A) : p_pMDNSResponder(p_pM), p_hServiceQuery(p_hS), p_u32AnswerIndex(p_u32A) {}; struct CompareKey { bool operator()(char const *a, char const *b) const { return strcmp(a, b) < 0; } }; using KeyValueMap = std::map; protected: MDNSResponder& p_pMDNSResponder; MDNSResponder::hMDNSServiceQuery p_hServiceQuery; uint32_t p_u32AnswerIndex; KeyValueMap keyValueMap; public: const char* serviceDomain(){ return p_pMDNSResponder.answerServiceDomain(p_hServiceQuery, p_u32AnswerIndex); }; bool hostDomainAvailable() { return (p_pMDNSResponder.hasAnswerHostDomain(p_hServiceQuery, p_u32AnswerIndex)); } const char* hostDomain(){ return (hostDomainAvailable()) ? p_pMDNSResponder.answerHostDomain(p_hServiceQuery, p_u32AnswerIndex) : nullptr; }; bool hostPortAvailable() { return (p_pMDNSResponder.hasAnswerPort(p_hServiceQuery, p_u32AnswerIndex)); } uint16_t hostPort(){ return (hostPortAvailable()) ? p_pMDNSResponder.answerPort(p_hServiceQuery, p_u32AnswerIndex) : 0; }; bool IP4AddressAvailable() { return (p_pMDNSResponder.hasAnswerIP4Address(p_hServiceQuery,p_u32AnswerIndex )); } std::vector IP4Adresses(){ std::vector internalIP; if (IP4AddressAvailable()) { uint16_t cntIP4Adress = p_pMDNSResponder.answerIP4AddressCount(p_hServiceQuery, p_u32AnswerIndex); for (uint32_t u2 = 0; u2 < cntIP4Adress; ++u2) { internalIP.emplace_back(p_pMDNSResponder.answerIP4Address(p_hServiceQuery, p_u32AnswerIndex, u2)); } } return internalIP; }; bool txtAvailable() { return (p_pMDNSResponder.hasAnswerTxts(p_hServiceQuery, p_u32AnswerIndex)); } const char* strKeyValue (){ return (txtAvailable()) ? p_pMDNSResponder.answerTxts(p_hServiceQuery, p_u32AnswerIndex) : nullptr; }; const KeyValueMap& keyValues() { if (txtAvailable() && keyValueMap.size() == 0) { for (auto kv = p_pMDNSResponder._answerKeyValue(p_hServiceQuery, p_u32AnswerIndex);kv != nullptr;kv = kv->m_pNext) { keyValueMap.emplace(std::pair(kv->m_pcKey,kv->m_pcValue)); } } return keyValueMap; } const char* value(const char* key) { char* result = nullptr; for (stcMDNSServiceTxt* pTxt=p_pMDNSResponder._answerKeyValue(p_hServiceQuery, p_u32AnswerIndex); pTxt; pTxt=pTxt->m_pNext) { if ((key) && (0 == strcmp(pTxt->m_pcKey, key))) { result = pTxt->m_pcValue; break; } } return result; } }; protected: /** * stcMDNSServiceTxt */ struct stcMDNSServiceTxt { stcMDNSServiceTxt* m_pNext; char* m_pcKey; char* m_pcValue; bool m_bTemp; stcMDNSServiceTxt(const char* p_pcKey = 0, const char* p_pcValue = 0, bool p_bTemp = false); stcMDNSServiceTxt(const stcMDNSServiceTxt& p_Other); ~stcMDNSServiceTxt(void); stcMDNSServiceTxt& operator=(const stcMDNSServiceTxt& p_Other); bool clear(void); char* allocKey(size_t p_stLength); bool setKey(const char* p_pcKey, size_t p_stLength); bool setKey(const char* p_pcKey); bool releaseKey(void); char* allocValue(size_t p_stLength); bool setValue(const char* p_pcValue, size_t p_stLength); bool setValue(const char* p_pcValue); bool releaseValue(void); bool set(const char* p_pcKey, const char* p_pcValue, bool p_bTemp = false); bool update(const char* p_pcValue); size_t length(void) const; }; /** * stcMDNSTxts */ struct stcMDNSServiceTxts { stcMDNSServiceTxt* m_pTxts; stcMDNSServiceTxts(void); stcMDNSServiceTxts(const stcMDNSServiceTxts& p_Other); ~stcMDNSServiceTxts(void); stcMDNSServiceTxts& operator=(const stcMDNSServiceTxts& p_Other); bool clear(void); bool add(stcMDNSServiceTxt* p_pTxt); bool remove(stcMDNSServiceTxt* p_pTxt); bool removeTempTxts(void); stcMDNSServiceTxt* find(const char* p_pcKey); const stcMDNSServiceTxt* find(const char* p_pcKey) const; stcMDNSServiceTxt* find(const stcMDNSServiceTxt* p_pTxt); uint16_t length(void) const; size_t c_strLength(void) const; bool c_str(char* p_pcBuffer); size_t bufferLength(void) const; bool buffer(char* p_pcBuffer); bool compare(const stcMDNSServiceTxts& p_Other) const; bool operator==(const stcMDNSServiceTxts& p_Other) const; bool operator!=(const stcMDNSServiceTxts& p_Other) const; }; /** * enuContentFlags */ typedef enum _enuContentFlags { // Host ContentFlag_A = 0x01, ContentFlag_PTR_IP4 = 0x02, ContentFlag_PTR_IP6 = 0x04, ContentFlag_AAAA = 0x08, // Service ContentFlag_PTR_TYPE = 0x10, ContentFlag_PTR_NAME = 0x20, ContentFlag_TXT = 0x40, ContentFlag_SRV = 0x80, } enuContentFlags; /** * stcMDNS_MsgHeader */ struct stcMDNS_MsgHeader { uint16_t m_u16ID; // Identifier bool m_1bQR : 1; // Query/Response flag unsigned char m_4bOpcode : 4; // Operation code bool m_1bAA : 1; // Authoritative Answer flag bool m_1bTC : 1; // Truncation flag bool m_1bRD : 1; // Recursion desired bool m_1bRA : 1; // Recursion available unsigned char m_3bZ : 3; // Zero unsigned char m_4bRCode : 4; // Response code uint16_t m_u16QDCount; // Question count uint16_t m_u16ANCount; // Answer count uint16_t m_u16NSCount; // Authority Record count uint16_t m_u16ARCount; // Additional Record count stcMDNS_MsgHeader(uint16_t p_u16ID = 0, bool p_bQR = false, unsigned char p_ucOpcode = 0, bool p_bAA = false, bool p_bTC = false, bool p_bRD = false, bool p_bRA = false, unsigned char p_ucRCode = 0, uint16_t p_u16QDCount = 0, uint16_t p_u16ANCount = 0, uint16_t p_u16NSCount = 0, uint16_t p_u16ARCount = 0); }; /** * stcMDNS_RRDomain */ struct stcMDNS_RRDomain { char m_acName[MDNS_DOMAIN_MAXLENGTH]; // Encoded domain name uint16_t m_u16NameLength; // Length (incl. '\0') stcMDNS_RRDomain(void); stcMDNS_RRDomain(const stcMDNS_RRDomain& p_Other); stcMDNS_RRDomain& operator=(const stcMDNS_RRDomain& p_Other); bool clear(void); bool addLabel(const char* p_pcLabel, bool p_bPrependUnderline = false); bool compare(const stcMDNS_RRDomain& p_Other) const; bool operator==(const stcMDNS_RRDomain& p_Other) const; bool operator!=(const stcMDNS_RRDomain& p_Other) const; bool operator>(const stcMDNS_RRDomain& p_Other) const; size_t c_strLength(void) const; bool c_str(char* p_pcBuffer); }; /** * stcMDNS_RRAttributes */ struct stcMDNS_RRAttributes { uint16_t m_u16Type; // Type uint16_t m_u16Class; // Class, nearly always 'IN' stcMDNS_RRAttributes(uint16_t p_u16Type = 0, uint16_t p_u16Class = 1 /*DNS_RRCLASS_IN Internet*/); stcMDNS_RRAttributes(const stcMDNS_RRAttributes& p_Other); stcMDNS_RRAttributes& operator=(const stcMDNS_RRAttributes& p_Other); }; /** * stcMDNS_RRHeader */ struct stcMDNS_RRHeader { stcMDNS_RRDomain m_Domain; stcMDNS_RRAttributes m_Attributes; stcMDNS_RRHeader(void); stcMDNS_RRHeader(const stcMDNS_RRHeader& p_Other); stcMDNS_RRHeader& operator=(const stcMDNS_RRHeader& p_Other); bool clear(void); }; /** * stcMDNS_RRQuestion */ struct stcMDNS_RRQuestion { stcMDNS_RRQuestion* m_pNext; stcMDNS_RRHeader m_Header; bool m_bUnicast; // Unicast reply requested stcMDNS_RRQuestion(void); }; /** * enuAnswerType */ typedef enum _enuAnswerType { AnswerType_A, AnswerType_PTR, AnswerType_TXT, AnswerType_AAAA, AnswerType_SRV, AnswerType_Generic } enuAnswerType; /** * stcMDNS_RRAnswer */ struct stcMDNS_RRAnswer { stcMDNS_RRAnswer* m_pNext; const enuAnswerType m_AnswerType; stcMDNS_RRHeader m_Header; bool m_bCacheFlush; // Cache flush command bit uint32_t m_u32TTL; // Validity time in seconds virtual ~stcMDNS_RRAnswer(void); enuAnswerType answerType(void) const; bool clear(void); protected: stcMDNS_RRAnswer(enuAnswerType p_AnswerType, const stcMDNS_RRHeader& p_Header, uint32_t p_u32TTL); }; #ifdef MDNS_IP4_SUPPORT /** * stcMDNS_RRAnswerA */ struct stcMDNS_RRAnswerA : public stcMDNS_RRAnswer { IPAddress m_IPAddress; stcMDNS_RRAnswerA(const stcMDNS_RRHeader& p_Header, uint32_t p_u32TTL); ~stcMDNS_RRAnswerA(void); bool clear(void); }; #endif /** * stcMDNS_RRAnswerPTR */ struct stcMDNS_RRAnswerPTR : public stcMDNS_RRAnswer { stcMDNS_RRDomain m_PTRDomain; stcMDNS_RRAnswerPTR(const stcMDNS_RRHeader& p_Header, uint32_t p_u32TTL); ~stcMDNS_RRAnswerPTR(void); bool clear(void); }; /** * stcMDNS_RRAnswerTXT */ struct stcMDNS_RRAnswerTXT : public stcMDNS_RRAnswer { stcMDNSServiceTxts m_Txts; stcMDNS_RRAnswerTXT(const stcMDNS_RRHeader& p_Header, uint32_t p_u32TTL); ~stcMDNS_RRAnswerTXT(void); bool clear(void); }; #ifdef MDNS_IP6_SUPPORT /** * stcMDNS_RRAnswerAAAA */ struct stcMDNS_RRAnswerAAAA : public stcMDNS_RRAnswer { //TODO: IP6Address m_IPAddress; stcMDNS_RRAnswerAAAA(const stcMDNS_RRHeader& p_Header, uint32_t p_u32TTL); ~stcMDNS_RRAnswerAAAA(void); bool clear(void); }; #endif /** * stcMDNS_RRAnswerSRV */ struct stcMDNS_RRAnswerSRV : public stcMDNS_RRAnswer { uint16_t m_u16Priority; uint16_t m_u16Weight; uint16_t m_u16Port; stcMDNS_RRDomain m_SRVDomain; stcMDNS_RRAnswerSRV(const stcMDNS_RRHeader& p_Header, uint32_t p_u32TTL); ~stcMDNS_RRAnswerSRV(void); bool clear(void); }; /** * stcMDNS_RRAnswerGeneric */ struct stcMDNS_RRAnswerGeneric : public stcMDNS_RRAnswer { uint16_t m_u16RDLength; // Length of variable answer uint8_t* m_pu8RDData; // Offset of start of variable answer in packet stcMDNS_RRAnswerGeneric(const stcMDNS_RRHeader& p_Header, uint32_t p_u32TTL); ~stcMDNS_RRAnswerGeneric(void); bool clear(void); }; /** * enuProbingStatus */ typedef enum _enuProbingStatus { ProbingStatus_WaitingForData, ProbingStatus_ReadyToStart, ProbingStatus_InProgress, ProbingStatus_Done } enuProbingStatus; /** * stcProbeInformation */ struct stcProbeInformation { enuProbingStatus m_ProbingStatus; uint8_t m_u8SentCount; // Used for probes and announcements esp8266::polledTimeout::oneShotMs m_Timeout; // Used for probes and announcements //clsMDNSTimeFlag m_TimeFlag; // Used for probes and announcements bool m_bConflict; bool m_bTiebreakNeeded; MDNSHostProbeFn m_fnHostProbeResultCallback; MDNSServiceProbeFn m_fnServiceProbeResultCallback; stcProbeInformation(void); bool clear(bool p_bClearUserdata = false); }; /** * stcMDNSService */ struct stcMDNSService { stcMDNSService* m_pNext; char* m_pcName; bool m_bAutoName; // Name was set automatically to hostname (if no name was supplied) char* m_pcService; char* m_pcProtocol; uint16_t m_u16Port; uint8_t m_u8ReplyMask; stcMDNSServiceTxts m_Txts; MDNSDynamicServiceTxtCallbackFunc m_fnTxtCallback; stcProbeInformation m_ProbeInformation; stcMDNSService(const char* p_pcName = 0, const char* p_pcService = 0, const char* p_pcProtocol = 0); ~stcMDNSService(void); bool setName(const char* p_pcName); bool releaseName(void); bool setService(const char* p_pcService); bool releaseService(void); bool setProtocol(const char* p_pcProtocol); bool releaseProtocol(void); }; /** * stcMDNSServiceQuery */ struct stcMDNSServiceQuery { /** * stcAnswer */ struct stcAnswer { /** * stcTTL */ struct stcTTL { /** * timeoutLevel_t */ typedef uint8_t timeoutLevel_t; /** * TIMEOUTLEVELs */ const timeoutLevel_t TIMEOUTLEVEL_UNSET = 0; const timeoutLevel_t TIMEOUTLEVEL_BASE = 80; const timeoutLevel_t TIMEOUTLEVEL_INTERVAL = 5; const timeoutLevel_t TIMEOUTLEVEL_FINAL = 100; uint32_t m_u32TTL; esp8266::polledTimeout::oneShotMs m_TTLTimeout; timeoutLevel_t m_timeoutLevel; stcTTL(void); bool set(uint32_t p_u32TTL); bool flagged(void); bool restart(void); bool prepareDeletion(void); bool finalTimeoutLevel(void) const; unsigned long timeout(void) const; }; #ifdef MDNS_IP4_SUPPORT /** * stcIP4Address */ struct stcIP4Address { stcIP4Address* m_pNext; IPAddress m_IPAddress; stcTTL m_TTL; stcIP4Address(IPAddress p_IPAddress, uint32_t p_u32TTL = 0); }; #endif #ifdef MDNS_IP6_SUPPORT /** * stcIP6Address */ struct stcIP6Address { stcIP6Address* m_pNext; IP6Address m_IPAddress; stcTTL m_TTL; stcIP6Address(IPAddress p_IPAddress, uint32_t p_u32TTL = 0); }; #endif stcAnswer* m_pNext; // The service domain is the first 'answer' (from PTR answer, using service and protocol) to be set // Defines the key for additional answer, like host domain, etc. stcMDNS_RRDomain m_ServiceDomain; // 1. level answer (PTR), eg. MyESP._http._tcp.local char* m_pcServiceDomain; stcTTL m_TTLServiceDomain; stcMDNS_RRDomain m_HostDomain; // 2. level answer (SRV, using service domain), eg. esp8266.local char* m_pcHostDomain; uint16_t m_u16Port; // 2. level answer (SRV, using service domain), eg. 5000 stcTTL m_TTLHostDomainAndPort; stcMDNSServiceTxts m_Txts; // 2. level answer (TXT, using service domain), eg. c#=1 char* m_pcTxts; stcTTL m_TTLTxts; #ifdef MDNS_IP4_SUPPORT stcIP4Address* m_pIP4Addresses; // 3. level answer (A, using host domain), eg. 123.456.789.012 #endif #ifdef MDNS_IP6_SUPPORT stcIP6Address* m_pIP6Addresses; // 3. level answer (AAAA, using host domain), eg. 1234::09 #endif uint32_t m_u32ContentFlags; stcAnswer(void); ~stcAnswer(void); bool clear(void); char* allocServiceDomain(size_t p_stLength); bool releaseServiceDomain(void); char* allocHostDomain(size_t p_stLength); bool releaseHostDomain(void); char* allocTxts(size_t p_stLength); bool releaseTxts(void); #ifdef MDNS_IP4_SUPPORT bool releaseIP4Addresses(void); bool addIP4Address(stcIP4Address* p_pIP4Address); bool removeIP4Address(stcIP4Address* p_pIP4Address); const stcIP4Address* findIP4Address(const IPAddress& p_IPAddress) const; stcIP4Address* findIP4Address(const IPAddress& p_IPAddress); uint32_t IP4AddressCount(void) const; const stcIP4Address* IP4AddressAtIndex(uint32_t p_u32Index) const; stcIP4Address* IP4AddressAtIndex(uint32_t p_u32Index); #endif #ifdef MDNS_IP6_SUPPORT bool releaseIP6Addresses(void); bool addIP6Address(stcIP6Address* p_pIP6Address); bool removeIP6Address(stcIP6Address* p_pIP6Address); const stcIP6Address* findIP6Address(const IPAddress& p_IPAddress) const; stcIP6Address* findIP6Address(const IPAddress& p_IPAddress); uint32_t IP6AddressCount(void) const; const stcIP6Address* IP6AddressAtIndex(uint32_t p_u32Index) const; stcIP6Address* IP6AddressAtIndex(uint32_t p_u32Index); #endif }; stcMDNSServiceQuery* m_pNext; stcMDNS_RRDomain m_ServiceTypeDomain; // eg. _http._tcp.local MDNSServiceQueryCallbackFunc m_fnCallback; bool m_bLegacyQuery; uint8_t m_u8SentCount; esp8266::polledTimeout::oneShotMs m_ResendTimeout; bool m_bAwaitingAnswers; stcAnswer* m_pAnswers; stcMDNSServiceQuery(void); ~stcMDNSServiceQuery(void); bool clear(void); uint32_t answerCount(void) const; const stcAnswer* answerAtIndex(uint32_t p_u32Index) const; stcAnswer* answerAtIndex(uint32_t p_u32Index); uint32_t indexOfAnswer(const stcAnswer* p_pAnswer) const; bool addAnswer(stcAnswer* p_pAnswer); bool removeAnswer(stcAnswer* p_pAnswer); stcAnswer* findAnswerForServiceDomain(const stcMDNS_RRDomain& p_ServiceDomain); stcAnswer* findAnswerForHostDomain(const stcMDNS_RRDomain& p_HostDomain); }; /** * stcMDNSSendParameter */ struct stcMDNSSendParameter { protected: /** * stcDomainCacheItem */ struct stcDomainCacheItem { stcDomainCacheItem* m_pNext; const void* m_pHostnameOrService; // Opaque id for host or service domain (pointer) bool m_bAdditionalData; // Opaque flag for special info (service domain included) uint16_t m_u16Offset; // Offset in UDP output buffer stcDomainCacheItem(const void* p_pHostnameOrService, bool p_bAdditionalData, uint32_t p_u16Offset); }; public: uint16_t m_u16ID; // Query ID (used only in lagacy queries) stcMDNS_RRQuestion* m_pQuestions; // A list of queries uint8_t m_u8HostReplyMask; // Flags for reply components/answers bool m_bLegacyQuery; // Flag: Legacy query bool m_bResponse; // Flag: Response to a query bool m_bAuthorative; // Flag: Authorative (owner) response bool m_bCacheFlush; // Flag: Clients should flush their caches bool m_bUnicast; // Flag: Unicast response bool m_bUnannounce; // Flag: Unannounce service uint16_t m_u16Offset; // Current offset in UDP write buffer (mainly for domain cache) stcDomainCacheItem* m_pDomainCacheItems; // Cached host and service domains stcMDNSSendParameter(void); ~stcMDNSSendParameter(void); bool clear(void); bool shiftOffset(uint16_t p_u16Shift); bool addDomainCacheItem(const void* p_pHostnameOrService, bool p_bAdditionalData, uint16_t p_u16Offset); uint16_t findCachedDomainOffset(const void* p_pHostnameOrService, bool p_bAdditionalData) const; }; // Instance variables stcMDNSService* m_pServices; UdpContext* m_pUDPContext; char* m_pcHostname; stcMDNSServiceQuery* m_pServiceQueries; WiFiEventHandler m_DisconnectedHandler; WiFiEventHandler m_GotIPHandler; MDNSDynamicServiceTxtCallbackFunc m_fnServiceTxtCallback; bool m_bPassivModeEnabled; stcProbeInformation m_HostProbeInformation; IPAddress m_IPAddress; /** CONTROL **/ /* MAINTENANCE */ bool _process(bool p_bUserContext); bool _restart(void); /* RECEIVING */ bool _parseMessage(void); bool _parseQuery(const stcMDNS_MsgHeader& p_Header); bool _parseResponse(const stcMDNS_MsgHeader& p_Header); bool _processAnswers(const stcMDNS_RRAnswer* p_pPTRAnswers); bool _processPTRAnswer(const stcMDNS_RRAnswerPTR* p_pPTRAnswer, bool& p_rbFoundNewKeyAnswer); bool _processSRVAnswer(const stcMDNS_RRAnswerSRV* p_pSRVAnswer, bool& p_rbFoundNewKeyAnswer); bool _processTXTAnswer(const stcMDNS_RRAnswerTXT* p_pTXTAnswer); #ifdef MDNS_IP4_SUPPORT bool _processAAnswer(const stcMDNS_RRAnswerA* p_pAAnswer); #endif #ifdef MDNS_IP6_SUPPORT bool _processAAAAAnswer(const stcMDNS_RRAnswerAAAA* p_pAAAAAnswer); #endif /* PROBING */ bool _updateProbeStatus(void); bool _resetProbeStatus(bool p_bRestart = true); bool _hasProbesWaitingForAnswers(void) const; bool _sendHostProbe(void); bool _sendServiceProbe(stcMDNSService& p_rService); bool _cancelProbingForHost(void); bool _cancelProbingForService(stcMDNSService& p_rService); /* ANNOUNCE */ bool _announce(bool p_bAnnounce, bool p_bIncludeServices); bool _announceService(stcMDNSService& p_rService, bool p_bAnnounce = true); /* SERVICE QUERY CACHE */ bool _hasServiceQueriesWaitingForAnswers(void) const; bool _checkServiceQueryCache(void); /** TRANSFER **/ /* SENDING */ bool _sendMDNSMessage(stcMDNSSendParameter& p_SendParameter); bool _sendMDNSMessage_Multicast(MDNSResponder::stcMDNSSendParameter& p_rSendParameter); bool _prepareMDNSMessage(stcMDNSSendParameter& p_SendParameter, IPAddress p_IPAddress); bool _sendMDNSServiceQuery(const stcMDNSServiceQuery& p_ServiceQuery); bool _sendMDNSQuery(const stcMDNS_RRDomain& p_QueryDomain, uint16_t p_u16QueryType, stcMDNSServiceQuery::stcAnswer* p_pKnownAnswers = 0); const IPAddress& _getResponseMulticastInterface() const { return m_IPAddress; } uint8_t _replyMaskForHost(const stcMDNS_RRHeader& p_RRHeader, bool* p_pbFullNameMatch = 0) const; uint8_t _replyMaskForService(const stcMDNS_RRHeader& p_RRHeader, const stcMDNSService& p_Service, bool* p_pbFullNameMatch = 0) const; /* RESOURCE RECORD */ bool _readRRQuestion(stcMDNS_RRQuestion& p_rQuestion); bool _readRRAnswer(stcMDNS_RRAnswer*& p_rpAnswer); #ifdef MDNS_IP4_SUPPORT bool _readRRAnswerA(stcMDNS_RRAnswerA& p_rRRAnswerA, uint16_t p_u16RDLength); #endif bool _readRRAnswerPTR(stcMDNS_RRAnswerPTR& p_rRRAnswerPTR, uint16_t p_u16RDLength); bool _readRRAnswerTXT(stcMDNS_RRAnswerTXT& p_rRRAnswerTXT, uint16_t p_u16RDLength); #ifdef MDNS_IP6_SUPPORT bool _readRRAnswerAAAA(stcMDNS_RRAnswerAAAA& p_rRRAnswerAAAA, uint16_t p_u16RDLength); #endif bool _readRRAnswerSRV(stcMDNS_RRAnswerSRV& p_rRRAnswerSRV, uint16_t p_u16RDLength); bool _readRRAnswerGeneric(stcMDNS_RRAnswerGeneric& p_rRRAnswerGeneric, uint16_t p_u16RDLength); bool _readRRHeader(stcMDNS_RRHeader& p_rHeader); bool _readRRDomain(stcMDNS_RRDomain& p_rRRDomain); bool _readRRDomain_Loop(stcMDNS_RRDomain& p_rRRDomain, uint8_t p_u8Depth); bool _readRRAttributes(stcMDNS_RRAttributes& p_rAttributes); /* DOMAIN NAMES */ bool _buildDomainForHost(const char* p_pcHostname, stcMDNS_RRDomain& p_rHostDomain) const; bool _buildDomainForDNSSD(stcMDNS_RRDomain& p_rDNSSDDomain) const; bool _buildDomainForService(const stcMDNSService& p_Service, bool p_bIncludeName, stcMDNS_RRDomain& p_rServiceDomain) const; bool _buildDomainForService(const char* p_pcService, const char* p_pcProtocol, stcMDNS_RRDomain& p_rServiceDomain) const; #ifdef MDNS_IP4_SUPPORT bool _buildDomainForReverseIP4(IPAddress p_IP4Address, stcMDNS_RRDomain& p_rReverseIP4Domain) const; #endif #ifdef MDNS_IP6_SUPPORT bool _buildDomainForReverseIP6(IPAddress p_IP4Address, stcMDNS_RRDomain& p_rReverseIP6Domain) const; #endif /* UDP */ bool _udpReadBuffer(unsigned char* p_pBuffer, size_t p_stLength); bool _udpRead8(uint8_t& p_ru8Value); bool _udpRead16(uint16_t& p_ru16Value); bool _udpRead32(uint32_t& p_ru32Value); bool _udpAppendBuffer(const unsigned char* p_pcBuffer, size_t p_stLength); bool _udpAppend8(uint8_t p_u8Value); bool _udpAppend16(uint16_t p_u16Value); bool _udpAppend32(uint32_t p_u32Value); #if not defined ESP_8266_MDNS_INCLUDE || defined DEBUG_ESP_MDNS_RESPONDER bool _udpDump(bool p_bMovePointer = false); bool _udpDump(unsigned p_uOffset, unsigned p_uLength); #endif /* READ/WRITE MDNS STRUCTS */ bool _readMDNSMsgHeader(stcMDNS_MsgHeader& p_rMsgHeader); bool _write8(uint8_t p_u8Value, stcMDNSSendParameter& p_rSendParameter); bool _write16(uint16_t p_u16Value, stcMDNSSendParameter& p_rSendParameter); bool _write32(uint32_t p_u32Value, stcMDNSSendParameter& p_rSendParameter); bool _writeMDNSMsgHeader(const stcMDNS_MsgHeader& p_MsgHeader, stcMDNSSendParameter& p_rSendParameter); bool _writeMDNSRRAttributes(const stcMDNS_RRAttributes& p_Attributes, stcMDNSSendParameter& p_rSendParameter); bool _writeMDNSRRDomain(const stcMDNS_RRDomain& p_Domain, stcMDNSSendParameter& p_rSendParameter); bool _writeMDNSHostDomain(const char* m_pcHostname, bool p_bPrependRDLength, stcMDNSSendParameter& p_rSendParameter); bool _writeMDNSServiceDomain(const stcMDNSService& p_Service, bool p_bIncludeName, bool p_bPrependRDLength, stcMDNSSendParameter& p_rSendParameter); bool _writeMDNSQuestion(stcMDNS_RRQuestion& p_Question, stcMDNSSendParameter& p_rSendParameter); #ifdef MDNS_IP4_SUPPORT bool _writeMDNSAnswer_A(IPAddress p_IPAddress, stcMDNSSendParameter& p_rSendParameter); bool _writeMDNSAnswer_PTR_IP4(IPAddress p_IPAddress, stcMDNSSendParameter& p_rSendParameter); #endif bool _writeMDNSAnswer_PTR_TYPE(stcMDNSService& p_rService, stcMDNSSendParameter& p_rSendParameter); bool _writeMDNSAnswer_PTR_NAME(stcMDNSService& p_rService, stcMDNSSendParameter& p_rSendParameter); bool _writeMDNSAnswer_TXT(stcMDNSService& p_rService, stcMDNSSendParameter& p_rSendParameter); #ifdef MDNS_IP6_SUPPORT bool _writeMDNSAnswer_AAAA(IPAddress p_IPAddress, stcMDNSSendParameter& p_rSendParameter); bool _writeMDNSAnswer_PTR_IP6(IPAddress p_IPAddress, stcMDNSSendParameter& p_rSendParameter); #endif bool _writeMDNSAnswer_SRV(stcMDNSService& p_rService, stcMDNSSendParameter& p_rSendParameter); /** HELPERS **/ /* UDP CONTEXT */ bool _callProcess(void); bool _allocUDPContext(void); bool _releaseUDPContext(void); /* SERVICE QUERY */ stcMDNSServiceQuery* _allocServiceQuery(void); bool _removeServiceQuery(stcMDNSServiceQuery* p_pServiceQuery); bool _removeLegacyServiceQuery(void); stcMDNSServiceQuery* _findServiceQuery(hMDNSServiceQuery p_hServiceQuery); stcMDNSServiceQuery* _findLegacyServiceQuery(void); bool _releaseServiceQueries(void); stcMDNSServiceQuery* _findNextServiceQueryByServiceType(const stcMDNS_RRDomain& p_ServiceDomain, const stcMDNSServiceQuery* p_pPrevServiceQuery); /* HOSTNAME */ bool _setHostname(const char* p_pcHostname); bool _releaseHostname(void); /* SERVICE */ stcMDNSService* _allocService(const char* p_pcName, const char* p_pcService, const char* p_pcProtocol, uint16_t p_u16Port); bool _releaseService(stcMDNSService* p_pService); bool _releaseServices(void); stcMDNSService* _findService(const char* p_pcName, const char* p_pcService, const char* p_pcProtocol); stcMDNSService* _findService(const hMDNSService p_hService); size_t _countServices(void) const; /* SERVICE TXT */ stcMDNSServiceTxt* _allocServiceTxt(stcMDNSService* p_pService, const char* p_pcKey, const char* p_pcValue, bool p_bTemp); bool _releaseServiceTxt(stcMDNSService* p_pService, stcMDNSServiceTxt* p_pTxt); stcMDNSServiceTxt* _updateServiceTxt(stcMDNSService* p_pService, stcMDNSServiceTxt* p_pTxt, const char* p_pcValue, bool p_bTemp); stcMDNSServiceTxt* _findServiceTxt(stcMDNSService* p_pService, const char* p_pcKey); stcMDNSServiceTxt* _findServiceTxt(stcMDNSService* p_pService, const hMDNSTxt p_hTxt); stcMDNSServiceTxt* _addServiceTxt(stcMDNSService* p_pService, const char* p_pcKey, const char* p_pcValue, bool p_bTemp); stcMDNSServiceTxt* _answerKeyValue(const hMDNSServiceQuery p_hServiceQuery, const uint32_t p_u32AnswerIndex); bool _collectServiceTxts(stcMDNSService& p_rService); bool _releaseTempServiceTxts(stcMDNSService& p_rService); const stcMDNSServiceTxt* _serviceTxts(const char* p_pcName, const char* p_pcService, const char* p_pcProtocol); /* MISC */ #if not defined ESP_8266_MDNS_INCLUDE || defined DEBUG_ESP_MDNS_RESPONDER bool _printRRDomain(const stcMDNS_RRDomain& p_rRRDomain) const; bool _printRRAnswer(const MDNSResponder::stcMDNS_RRAnswer& p_RRAnswer) const; #endif }; }// namespace MDNSImplementation }// namespace esp8266 #endif // MDNS_H