From 1d73321b0dbc04da413528c31146e296a273eb4a Mon Sep 17 00:00:00 2001 From: Roman Priesol Date: Sat, 10 Oct 2026 12:16:40 +0200 Subject: [PATCH] LEAmDNS: announce IPv6 addresses (AAAA) and use the IPv6 transport Answer AAAA (and ANY) questions with one record per valid IPv6 address of the interface, add them next to the A record in answers, additional records and announcements, and re-announce when the IPv6 address count changes (SLAAC usually completes after DHCPv4). Join ff02::fb, listen dual stack and send every multicast message to both 224.0.0.251 and ff02::fb, each only when the interface has an address of that family. Without an IPv4 address no A or IPv4 PTR record is written. The new MDNS_IP6_RESPONDER (on with LWIP_IPV6) covers the responder only; the unfinished MDNS_IP6_SUPPORT query side stays off. See #9068 --- libraries/ESP8266mDNS/src/LEAmDNS.cpp | 43 +++--- libraries/ESP8266mDNS/src/LEAmDNS.h | 18 ++- libraries/ESP8266mDNS/src/LEAmDNS_Control.cpp | 15 +- libraries/ESP8266mDNS/src/LEAmDNS_Helpers.cpp | 4 + .../ESP8266mDNS/src/LEAmDNS_Transfer.cpp | 129 ++++++++++++------ 5 files changed, 148 insertions(+), 61 deletions(-) diff --git a/libraries/ESP8266mDNS/src/LEAmDNS.cpp b/libraries/ESP8266mDNS/src/LEAmDNS.cpp index 75862644f5..589f86f90b 100644 --- a/libraries/ESP8266mDNS/src/LEAmDNS.cpp +++ b/libraries/ESP8266mDNS/src/LEAmDNS.cpp @@ -29,6 +29,9 @@ #include "LEAmDNS_Priv.h" #include #include +#if LWIP_IPV6 +#include +#endif #include // should be defined at build time @@ -1298,9 +1301,13 @@ namespace MDNSImplementation // Join multicast group(s) for (netif* pNetIf = netif_list; pNetIf; pNetIf = pNetIf->next) { - if (netif_is_up(pNetIf) && IPAddress(pNetIf->ip_addr).isSet()) + if (!netif_is_up(pNetIf)) { + continue; + } #ifdef MDNS_IP4_SUPPORT + if (IPAddress(pNetIf->ip_addr).isSet()) + { ip_addr_t multicast_addr_V4 = DNS_MQUERY_IPV4_GROUP_INIT; if (!(pNetIf->flags & NETIF_FLAG_IGMP)) { @@ -1326,21 +1333,22 @@ namespace MDNSImplementation ": %s) FAILED!\n"), NETIFID_VAL(pNetIf), IPAddress(multicast_addr_V4).toString().c_str());); } + } #endif - -#ifdef MDNS_IPV6_SUPPORT - ip_addr_t multicast_addr_V6 = DNS_MQUERY_IPV6_GROUP_INIT; - bResult - = ((bResult) - && (ERR_OK == mld6_joingroup_netif(pNetIf, ip_2_ip6(&multicast_addr_V6)))); - DEBUG_EX_ERR_IF( - !bResult, - DEBUG_OUTPUT.printf_P( - PSTR("[MDNSResponder] _createHost: mld6_joingroup_netif (" NETIFID_STR - ") FAILED!\n"), - NETIFID_VAL(pNetIf))); -#endif +#if defined(MDNS_IP6_SUPPORT) || defined(MDNS_IP6_RESPONDER) + ip_addr_t multicast_addr_V6 = DNS_MQUERY_IPV6_GROUP_INIT; + if (ERR_OK == mld6_joingroup_netif(pNetIf, ip_2_ip6(&multicast_addr_V6))) + { + bResult = true; + } + else + { + DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _createHost: mld6_joingroup_netif (" NETIFID_STR + ") FAILED!\n"), + NETIFID_VAL(pNetIf));); } +#endif } return bResult; } @@ -1354,19 +1362,20 @@ namespace MDNSImplementation for (netif* pNetIf = netif_list; pNetIf; pNetIf = pNetIf->next) { - if (netif_is_up(pNetIf) && IPAddress(pNetIf->ip_addr).isSet()) + if (netif_is_up(pNetIf)) { bResult = true; // Leave multicast group(s) #ifdef MDNS_IP4_SUPPORT ip_addr_t multicast_addr_V4 = DNS_MQUERY_IPV4_GROUP_INIT; - if (ERR_OK != igmp_leavegroup_netif(pNetIf, ip_2_ip4(&multicast_addr_V4))) + if ((IPAddress(pNetIf->ip_addr).isSet()) + && (ERR_OK != igmp_leavegroup_netif(pNetIf, ip_2_ip4(&multicast_addr_V4)))) { DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("\n"));); } #endif -#ifdef MDNS_IPV6_SUPPORT +#if defined(MDNS_IP6_SUPPORT) || defined(MDNS_IP6_RESPONDER) ip_addr_t multicast_addr_V6 = DNS_MQUERY_IPV6_GROUP_INIT; if (ERR_OK != mld6_leavegroup_netif( diff --git a/libraries/ESP8266mDNS/src/LEAmDNS.h b/libraries/ESP8266mDNS/src/LEAmDNS.h index 3fc4dd98da..75d985ad29 100644 --- a/libraries/ESP8266mDNS/src/LEAmDNS.h +++ b/libraries/ESP8266mDNS/src/LEAmDNS.h @@ -135,12 +135,14 @@ namespace MDNSImplementation #define MDNS_IP4_SUPPORT #if LWIP_IPV6 //#define MDNS_IP6_SUPPORT +// Answer AAAA questions and announce the IPv6 addresses (over the IPv4 transport) +#define MDNS_IP6_RESPONDER #endif #ifdef MDNS_IP4_SUPPORT #define MDNS_IP4_SIZE 4 #endif -#ifdef MDNS_IP6_SUPPORT +#if defined(MDNS_IP6_SUPPORT) || defined(MDNS_IP6_RESPONDER) #define MDNS_IP6_SIZE 16 #endif /* @@ -1173,8 +1175,11 @@ namespace MDNSImplementation stcMDNSServiceQuery* m_pServiceQueries; MDNSDynamicServiceTxtCallbackFunc m_fnServiceTxtCallback; stcProbeInformation m_HostProbeInformation; - bool m_bLwipCb; - bool m_bRestarting; +#ifdef MDNS_IP6_RESPONDER + uint8_t m_u8IP6AddressCount = 0; // re-announce when it changes (SLAAC comes after DHCP) +#endif + bool m_bLwipCb; + bool m_bRestarting; /** CONTROL **/ /* MAINTENANCE */ @@ -1220,7 +1225,7 @@ namespace MDNSImplementation /* SENDING */ bool _sendMDNSMessage(stcMDNSSendParameter& p_SendParameter); bool _sendMDNSMessage_Multicast(MDNSResponder::stcMDNSSendParameter& p_rSendParameter); - bool _prepareMDNSMessage(stcMDNSSendParameter& p_SendParameter, IPAddress p_IPAddress); + bool _prepareMDNSMessage(stcMDNSSendParameter& p_SendParameter, const netif* p_pNetIf); bool _sendMDNSServiceQuery(const stcMDNSServiceQuery& p_ServiceQuery); bool _sendMDNSQuery(const stcMDNS_RRDomain& p_QueryDomain, uint16_t p_u16QueryType, stcMDNSServiceQuery::stcAnswer* p_pKnownAnswers = 0); @@ -1316,8 +1321,11 @@ namespace MDNSImplementation stcMDNSSendParameter& p_rSendParameter); bool _writeMDNSAnswer_TXT(stcMDNSService& p_rService, stcMDNSSendParameter& p_rSendParameter); +#if defined(MDNS_IP6_SUPPORT) || defined(MDNS_IP6_RESPONDER) + static uint8_t _ip6AddressCount(const netif* p_pNetIf); + bool _writeMDNSAnswer_AAAA(const netif* p_pNetIf, stcMDNSSendParameter& p_rSendParameter); +#endif #ifdef MDNS_IP6_SUPPORT - bool _writeMDNSAnswer_AAAA(IPAddress p_IPAddress, stcMDNSSendParameter& p_rSendParameter); bool _writeMDNSAnswer_PTR_IP6(IPAddress p_IPAddress, stcMDNSSendParameter& p_rSendParameter); #endif diff --git a/libraries/ESP8266mDNS/src/LEAmDNS_Control.cpp b/libraries/ESP8266mDNS/src/LEAmDNS_Control.cpp index 2cc32b053b..954fae3fbc 100644 --- a/libraries/ESP8266mDNS/src/LEAmDNS_Control.cpp +++ b/libraries/ESP8266mDNS/src/LEAmDNS_Control.cpp @@ -86,6 +86,16 @@ namespace MDNSImplementation { bResult = _updateProbeStatus() && // Probing _checkServiceQueryCache(); // Service query cache check +#ifdef MDNS_IP6_RESPONDER + // IP6 addresses (SLAAC) usually come after the IP4 one, announce them once they do + const uint8_t u8IP6Count = _ip6AddressCount(0); + if ((ProbingStatus_Done == m_HostProbeInformation.m_ProbingStatus) + && (u8IP6Count != m_u8IP6AddressCount)) + { + m_u8IP6AddressCount = u8IP6Count; + _announce(true, true); // Services too, in an IP6 only net nothing went out yet + } +#endif } return bResult; } @@ -1778,6 +1788,9 @@ namespace MDNSImplementation sendParameter.m_u8HostReplyMask |= ContentFlag_A; // A answer sendParameter.m_u8HostReplyMask |= ContentFlag_PTR_IP4; // PTR_IP4 answer #endif +#ifdef MDNS_IP6_RESPONDER + sendParameter.m_u8HostReplyMask |= ContentFlag_AAAA; // AAAA answer +#endif #ifdef MDNS_IP6_SUPPORT sendParameter.m_u8HostReplyMask |= ContentFlag_AAAA; // AAAA answer sendParameter.m_u8HostReplyMask |= ContentFlag_PTR_IP6; // PTR_IP6 answer @@ -2258,7 +2271,7 @@ namespace MDNSImplementation u8ReplyMask |= ContentFlag_A; } #endif -#ifdef MDNS_IP6_SUPPORT +#if defined(MDNS_IP6_SUPPORT) || defined(MDNS_IP6_RESPONDER) if ((DNS_RRTYPE_AAAA == p_RRHeader.m_Attributes.m_u16Type) || (DNS_RRTYPE_ANY == p_RRHeader.m_Attributes.m_u16Type)) { diff --git a/libraries/ESP8266mDNS/src/LEAmDNS_Helpers.cpp b/libraries/ESP8266mDNS/src/LEAmDNS_Helpers.cpp index 2d12f9042e..8723c446ce 100644 --- a/libraries/ESP8266mDNS/src/LEAmDNS_Helpers.cpp +++ b/libraries/ESP8266mDNS/src/LEAmDNS_Helpers.cpp @@ -180,7 +180,11 @@ namespace MDNSImplementation m_pUDPContext = new UdpContext; m_pUDPContext->ref(); +#ifdef MDNS_IP6_RESPONDER + if (m_pUDPContext->listen(IP_ANY_TYPE, DNS_MQUERY_PORT)) // IP4 and IP6 +#else if (m_pUDPContext->listen(IP4_ADDR_ANY, DNS_MQUERY_PORT)) +#endif { m_pUDPContext->setMulticastTTL(MDNS_MULTICAST_TTL); m_pUDPContext->onRx(std::bind(&MDNSResponder::_callProcess, this)); diff --git a/libraries/ESP8266mDNS/src/LEAmDNS_Transfer.cpp b/libraries/ESP8266mDNS/src/LEAmDNS_Transfer.cpp index ed58f1b86f..a09a8930ae 100644 --- a/libraries/ESP8266mDNS/src/LEAmDNS_Transfer.cpp +++ b/libraries/ESP8266mDNS/src/LEAmDNS_Transfer.cpp @@ -87,10 +87,9 @@ namespace MDNSImplementation }); IPAddress ipRemote; ipRemote = m_pUDPContext->getRemoteAddress(); - bResult - = ((_prepareMDNSMessage(p_rSendParameter, m_pUDPContext->getInputNetif()->ip_addr)) - && (m_pUDPContext->sendTimeout(ipRemote, m_pUDPContext->getRemotePort(), - MDNS_UDPCONTEXT_TIMEOUT))); + bResult = ((_prepareMDNSMessage(p_rSendParameter, m_pUDPContext->getInputNetif())) + && (m_pUDPContext->sendTimeout(ipRemote, m_pUDPContext->getRemotePort(), + MDNS_UDPCONTEXT_TIMEOUT))); } else // Multicast response { @@ -122,25 +121,33 @@ namespace MDNSImplementation for (netif* pNetIf = netif_list; pNetIf; pNetIf = pNetIf->next) { - if (netif_is_up(pNetIf) && IPAddress(pNetIf->ip_addr).isSet()) + if (!netif_is_up(pNetIf)) { - IPAddress fromIPAddress; - // fromIPAddress = _getResponseMulticastInterface(); - fromIPAddress = pNetIf->ip_addr; - m_pUDPContext->setMulticastInterface(fromIPAddress); + continue; + } + m_pUDPContext->setMulticastInterface(pNetIf); + // One message per address family, both carry all the host's addresses + const ip_addr_t toMulticastAddresses[] = { #ifdef MDNS_IP4_SUPPORT - IPAddress toMulticastAddress(DNS_MQUERY_IPV4_GROUP_INIT); + DNS_MQUERY_IPV4_GROUP_INIT, #endif -#ifdef MDNS_IP6_SUPPORT - // TODO: set multicast address - IPAddress toMulticastAddress(DNS_MQUERY_IPV6_GROUP_INIT); +#if defined(MDNS_IP6_SUPPORT) || defined(MDNS_IP6_RESPONDER) + DNS_MQUERY_IPV6_GROUP_INIT, #endif + }; + for (const ip_addr_t& toMulticastAddress : toMulticastAddresses) + { + if (IP_IS_V4(&toMulticastAddress) ? !IPAddress(pNetIf->ip_addr).isSet() + : !_ip6AddressCount(pNetIf)) + { + continue; // No address of this family to send from + } DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P( PSTR("[MDNSResponder] _sendMDNSMessage_Multicast: Will send to '%s'.\n"), - toMulticastAddress.toString().c_str());); - bResult = ((_prepareMDNSMessage(p_rSendParameter, fromIPAddress)) - && (m_pUDPContext->sendTimeout(toMulticastAddress, DNS_MQUERY_PORT, + IPAddress(toMulticastAddress).toString().c_str());); + bResult = ((_prepareMDNSMessage(p_rSendParameter, pNetIf)) + && (m_pUDPContext->sendTimeout(&toMulticastAddress, DNS_MQUERY_PORT, MDNS_UDPCONTEXT_TIMEOUT))); DEBUG_EX_ERR(if (!bResult) { @@ -162,12 +169,18 @@ namespace MDNSImplementation */ bool MDNSResponder::_prepareMDNSMessage(MDNSResponder::stcMDNSSendParameter& p_rSendParameter, - IPAddress p_IPAddress) + const netif* p_pNetIf) { DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage\n"));); bool bResult = true; p_rSendParameter.clearCachedNames(); // Need to remove cached names, p_SendParameter might // have been used before on other interface + const IPAddress p_IPAddress(p_pNetIf ? IPAddress(p_pNetIf->ip_addr) : IPAddress()); + const uint8_t u8IP4Count = (p_IPAddress.isSet() ? 1 : 0); // No A in an IP6 only net +#if defined(MDNS_IP6_SUPPORT) || defined(MDNS_IP6_RESPONDER) + const netif* pNetIf = p_pNetIf; + const uint8_t u8IP6Count = _ip6AddressCount(pNetIf); +#endif // Prepare header; count answers stcMDNS_MsgHeader msgHeader(p_rSendParameter.m_u16ID, p_rSendParameter.m_bResponse, 0, @@ -221,7 +234,7 @@ namespace MDNSImplementation // Answers and authoritative answers #ifdef MDNS_IP4_SUPPORT - if ((bResult) && (p_rSendParameter.m_u8HostReplyMask & ContentFlag_A)) + if ((bResult) && (u8IP4Count) && (p_rSendParameter.m_u8HostReplyMask & ContentFlag_A)) { ((Sequence_Count == sequence) ? ++ru16Answers @@ -229,7 +242,8 @@ namespace MDNSImplementation DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P( PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_A(A) FAILED!\n"));); } - if ((bResult) && (p_rSendParameter.m_u8HostReplyMask & ContentFlag_PTR_IP4)) + if ((bResult) && (u8IP4Count) + && (p_rSendParameter.m_u8HostReplyMask & ContentFlag_PTR_IP4)) { ((Sequence_Count == sequence) ? ++ru16Answers @@ -238,15 +252,17 @@ namespace MDNSImplementation "[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_PTR_IP4 FAILED!\n"));); } #endif -#ifdef MDNS_IP6_SUPPORT +#if defined(MDNS_IP6_SUPPORT) || defined(MDNS_IP6_RESPONDER) if ((bResult) && (p_rSendParameter.m_u8HostReplyMask & ContentFlag_AAAA)) { ((Sequence_Count == sequence) - ? ++ru16Answers - : (bResult = _writeMDNSAnswer_AAAA(p_IPAddress, p_rSendParameter))); + ? ru16Answers += u8IP6Count + : (bResult = _writeMDNSAnswer_AAAA(pNetIf, p_rSendParameter))); DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR( "[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_AAAA(A) FAILED!\n"));); } +#endif +#ifdef MDNS_IP6_SUPPORT if ((bResult) && (p_rSendParameter.m_u8HostReplyMask & ContentFlag_PTR_IP6)) { ((Sequence_Count == sequence) @@ -302,7 +318,7 @@ namespace MDNSImplementation #ifdef MDNS_IP4_SUPPORT bool bNeedsAdditionalAnswerA = false; #endif -#ifdef MDNS_IP6_SUPPORT +#if defined(MDNS_IP6_SUPPORT) || defined(MDNS_IP6_RESPONDER) bool bNeedsAdditionalAnswerAAAA = false; #endif for (stcMDNSService* pService = m_pServices; ((bResult) && (pService)); @@ -345,7 +361,7 @@ namespace MDNSImplementation bNeedsAdditionalAnswerA = true; } #endif -#ifdef MDNS_IP6_SUPPORT +#if defined(MDNS_IP6_SUPPORT) || defined(MDNS_IP6_RESPONDER) if ((bResult) && (!(p_rSendParameter.m_u8HostReplyMask & ContentFlag_AAAA))) // Add IP6 address @@ -358,7 +374,7 @@ namespace MDNSImplementation // Answer A needed? #ifdef MDNS_IP4_SUPPORT - if ((bResult) && (bNeedsAdditionalAnswerA)) + if ((bResult) && (u8IP4Count) && (bNeedsAdditionalAnswerA)) { ((Sequence_Count == sequence) ? ++msgHeader.m_u16ARCount @@ -367,13 +383,13 @@ namespace MDNSImplementation PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_A(B) FAILED!\n"));); } #endif -#ifdef MDNS_IP6_SUPPORT +#if defined(MDNS_IP6_SUPPORT) || defined(MDNS_IP6_RESPONDER) // Answer AAAA needed? if ((bResult) && (bNeedsAdditionalAnswerAAAA)) { ((Sequence_Count == sequence) - ? ++msgHeader.m_u16ARCount - : (bResult = _writeMDNSAnswer_AAAA(p_IPAddress, p_rSendParameter))); + ? msgHeader.m_u16ARCount += u8IP6Count + : (bResult = _writeMDNSAnswer_AAAA(pNetIf, p_rSendParameter))); DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR( "[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_AAAA(B) FAILED!\n"));); } @@ -1748,17 +1764,39 @@ namespace MDNSImplementation return bResult; } -#ifdef MDNS_IP6_SUPPORT +#if defined(MDNS_IP6_SUPPORT) || defined(MDNS_IP6_RESPONDER) + /* + MDNSResponder::_ip6AddressCount + + Number of valid IP6 addresses of the given netif (all netifs that are up, if 0) + */ + uint8_t MDNSResponder::_ip6AddressCount(const netif* p_pNetIf) + { + uint8_t u8Count = 0; + for (const netif* pNetIf = (p_pNetIf ? p_pNetIf : netif_list); pNetIf; + pNetIf = (p_pNetIf ? 0 : pNetIf->next)) + { + if (netif_is_up(pNetIf)) + { + for (int i = 0; i < LWIP_IPV6_NUM_ADDRESSES; ++i) + { + u8Count += (ip6_addr_isvalid(netif_ip6_addr_state(pNetIf, i)) ? 1 : 0); + } + } + } + return u8Count; + } + /* MDNSResponder::_writeMDNSAnswer_AAAA - Write a MDNS AAAA answer to the UDP output buffer. + Write a MDNS AAAA answer for each valid IP6 address of the netif to the UDP output buffer. See: '_writeMDNSAnswer_A' eg. esp8266.local AAAA 0x8001 120 16 xxxx::xx http://www.zytrax.com/books/dns/ch8/aaaa.html */ - bool MDNSResponder::_writeMDNSAnswer_AAAA(IPAddress p_IPAddress, + bool MDNSResponder::_writeMDNSAnswer_AAAA(const netif* p_pNetIf, MDNSResponder::stcMDNSSendParameter& p_rSendParameter) { DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_AAAA\n"));); @@ -1766,19 +1804,34 @@ namespace MDNSImplementation stcMDNS_RRAttributes attributes(DNS_RRTYPE_AAAA, ((p_rSendParameter.m_bCacheFlush ? 0x8000 : 0) | DNS_RRCLASS_IN)); // Cache flush? & INternet - bool bResult - = ((_writeMDNSHostDomain(m_pcHostname, false, p_rSendParameter)) && // esp8266.local - (_writeMDNSRRAttributes(attributes, p_rSendParameter)) && // TYPE & CLASS - (_write32((p_rSendParameter.m_bUnannounce ? 0 : MDNS_HOST_TTL), p_rSendParameter)) - && // TTL - (_write16(MDNS_IP6_SIZE, p_rSendParameter)) && // RDLength - (false /*TODO: IP6 version of: _udpAppendBuffer((uint32_t)p_IPAddress, MDNS_IP4_SIZE)*/)); // RData + bool bResult = true; + for (int i = 0; ((bResult) && (p_pNetIf) && (i < LWIP_IPV6_NUM_ADDRESSES)); ++i) + { + if (!ip6_addr_isvalid(netif_ip6_addr_state(p_pNetIf, i))) + { + continue; + } + bResult = ((_writeMDNSHostDomain(m_pcHostname, false, p_rSendParameter)) + && // esp8266.local + (_writeMDNSRRAttributes(attributes, p_rSendParameter)) && // TYPE & CLASS + (_write32((p_rSendParameter.m_bUnannounce ? 0 : MDNS_HOST_TTL), + p_rSendParameter)) + && // TTL + (_write16(MDNS_IP6_SIZE, p_rSendParameter)) && // RDLength + (_udpAppendBuffer((const unsigned char*)netif_ip6_addr(p_pNetIf, i)->addr, + MDNS_IP6_SIZE)) + && // RData (network order) + (p_rSendParameter.shiftOffset(MDNS_IP6_SIZE))); + } DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_AAAA: FAILED!\n")); }); return bResult; } +#endif + +#ifdef MDNS_IP6_SUPPORT /* MDNSResponder::_writeMDNSAnswer_PTR_IP6