From 6cf18ed1a2221215da1adc0d4ad76be2250e4c25 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Mon, 10 Aug 2015 16:14:26 +0300 Subject: [PATCH] Multicast in SoftAP mode (#96) SDK 1.3 fixed a bug which caused multicast UDP to fail on SoftAP interface. Update MDNSResponder and documentation appropriately. --- doc/reference.md | 2 - libraries/ESP8266mDNS/ESP8266mDNS.cpp | 102 ++++++++++++++------------ libraries/ESP8266mDNS/ESP8266mDNS.h | 9 +-- 3 files changed, 58 insertions(+), 55 deletions(-) diff --git a/doc/reference.md b/doc/reference.md index eca1f31e6..18c38d02b 100644 --- a/doc/reference.md +++ b/doc/reference.md @@ -290,7 +290,6 @@ When listening to multicast packets, replace `udp.begin(port)` with `udp.beginMulticast(WiFi.localIP(), multicast_ip_addr, port)`. You can use `udp.destinationIP()` to tell whether the packet received was sent to the multicast or unicast address. -Also note that multicast doesn't work on softAP interface. `WiFiServer`, `WiFiClient`, and `WiFiUDP` behave mostly the same way as with WiFi shield library. Four samples are provided for this library. @@ -373,7 +372,6 @@ instead of the one that comes with this package. ## mDNS and DNS-SD responder (ESP8266mDNS library) Allows the sketch to respond to multicast DNS queries for domain names like "foo.local", and DNS-SD (service dicovery) queries. -Currently the library only works on STA interface, AP interface is not supported. See attached example for details. ## SSDP responder (ESP8266SSDP) diff --git a/libraries/ESP8266mDNS/ESP8266mDNS.cpp b/libraries/ESP8266mDNS/ESP8266mDNS.cpp index 6e50313a4..0767d14e0 100644 --- a/libraries/ESP8266mDNS/ESP8266mDNS.cpp +++ b/libraries/ESP8266mDNS/ESP8266mDNS.cpp @@ -36,7 +36,7 @@ License (MIT license): #include "ESP8266mDNS.h" #include - + #include "debug.h" extern "C" { @@ -88,28 +88,28 @@ static const int MDNS_PORT = 5353; MDNSResponder::MDNSResponder() : _conn(0) { _services = 0; } MDNSResponder::~MDNSResponder() {} -bool MDNSResponder::begin(const char* domain){ +bool MDNSResponder::begin(const char* domain){ // Open the MDNS socket if it isn't already open. - + size_t n = strlen(domain); if (n > 255) { // Can only handle domains that are 255 chars in length. return false; } - + // Copy in domain characters as lowercase for (int i = 0; i < n; ++i) _hostName[i] = tolower(domain[i]); _hostName[n] = '\0'; - + os_strcpy(_boardName, ARDUINO_BOARD); - + // Open the MDNS socket if it isn't already open. if (!_conn) { uint32_t ourIp = _getOurIp(); if(ourIp == 0){ return false; } - + ip_addr_t ifaddr; ifaddr.addr = ourIp; ip_addr_t multicast_addr; @@ -135,7 +135,7 @@ bool MDNSResponder::begin(const char* domain){ void MDNSResponder::update() { if (!_conn->next()) { - return; + return; } _parsePacket(); } @@ -163,15 +163,21 @@ uint16_t MDNSResponder::_getServicePort(char *name, char *proto){ } uint32_t MDNSResponder::_getOurIp(){ - if(wifi_get_opmode() & STATION_MODE){ + int mode = wifi_get_opmode(); + if(mode & STATION_MODE){ struct ip_info staIpInfo; wifi_get_ip_info(STATION_IF, &staIpInfo); return staIpInfo.ip.addr; - } + } else if (mode & SOFTAP_MODE) { + struct ip_info staIpInfo; + wifi_get_ip_info(SOFTAP_IF, &staIpInfo); + return staIpInfo.ip.addr; + } else { #ifdef MDNS_DEBUG_ERR os_printf("ERR_NO_LOCAL_IP\n"); #endif - return 0; + return 0; + } } void MDNSResponder::_parsePacket(){ @@ -180,39 +186,39 @@ void MDNSResponder::_parsePacket(){ bool serviceParsed = false; bool protoParsed = false; bool localParsed = false; - + char hostName[255]; uint8_t hostNameLen; - + char serviceName[32]; uint8_t serviceNameLen; uint16_t servicePort; - + char protoName[32]; uint8_t protoNameLen; - + uint16_t packetHeader[6]; - + for(i=0; i<6; i++) packetHeader[i] = _conn_read16(); if((packetHeader[1] & 0x8000) != 0){ //not parsing responses yet _conn->flush(); return; } - + // PARSE REQUEST NAME - + hostNameLen = _conn_read8(); _conn_readS(hostName, hostNameLen); hostName[hostNameLen] = '\0'; - + if(hostName[0] == '_'){ serviceParsed = true; memcpy(serviceName, hostName+1, hostNameLen); serviceNameLen = hostNameLen-1; hostNameLen = 0; } - + if(hostNameLen > 0 && strcmp(_hostName, hostName) != 0){ #ifdef MDNS_DEBUG_ERR os_printf("ERR_NO_HOST: %s\n", hostName); @@ -220,12 +226,12 @@ void MDNSResponder::_parsePacket(){ _conn->flush(); return; } - + if(!serviceParsed){ serviceNameLen = _conn_read8(); _conn_readS(serviceName, serviceNameLen); serviceName[serviceNameLen] = '\0'; - + if(serviceName[0] == '_'){ memcpy(serviceName, serviceName+1, serviceNameLen); serviceNameLen--; @@ -253,7 +259,7 @@ void MDNSResponder::_parsePacket(){ return; } } - + if(!protoParsed){ protoNameLen = _conn_read8(); _conn_readS(protoName, protoNameLen); @@ -270,7 +276,7 @@ void MDNSResponder::_parsePacket(){ return; } } - + if(!localParsed){ char localName[32]; uint8_t localNameLen = _conn_read8(); @@ -287,7 +293,7 @@ void MDNSResponder::_parsePacket(){ return; } } - + if(serviceNameLen > 0 && protoNameLen > 0){ servicePort = _getServicePort(serviceName, protoName); if(servicePort == 0){ @@ -304,16 +310,16 @@ void MDNSResponder::_parsePacket(){ _conn->flush(); return; } - + // RESPOND - + #ifdef MDNS_DEBUG_RX os_printf("RX: REQ, ID:%u, Q:%u, A:%u, NS:%u, ADD:%u\n", packetHeader[0], packetHeader[2], packetHeader[3], packetHeader[4], packetHeader[5]); #endif uint16_t currentType; uint16_t currentClass; - + int numQuestions = packetHeader[2]; if(numQuestions > 4) numQuestions = 4; uint16_t questions[4]; @@ -326,21 +332,21 @@ void MDNSResponder::_parsePacket(){ } currentClass = _conn_read16(); if(currentClass & MDNS_CLASS_IN) questions[question++] = currentType; - + if(numQuestions > 0){ if(_conn_read16() != 0xC00C){//new question but for another host/service _conn->flush(); numQuestions = 0; } } - + #ifdef MDNS_DEBUG_RX os_printf("REQ: "); if(hostNameLen > 0) os_printf("%s.", hostName); if(serviceNameLen > 0) os_printf("_%s.", serviceName); if(protoNameLen > 0) os_printf("_%s.", protoName); os_printf("local. "); - + if(currentType == MDNS_TYPE_AAAA) os_printf(" AAAA "); else if(currentType == MDNS_TYPE_A) os_printf(" A "); else if(currentType == MDNS_TYPE_PTR) os_printf(" PTR "); @@ -351,7 +357,7 @@ void MDNSResponder::_parsePacket(){ if(currentClass == MDNS_CLASS_IN) os_printf(" IN "); else if(currentClass == MDNS_CLASS_IN_FLUSH_CACHE) os_printf(" IN[F] "); else os_printf(" 0x%04X ", currentClass); - + os_printf("\n"); #endif } @@ -362,7 +368,7 @@ void MDNSResponder::_parsePacket(){ else if(questions[i] == MDNS_TYPE_TXT) responseMask |= 0x4; else if(questions[i] == MDNS_TYPE_PTR) responseMask |= 0xF; } - + return _reply(responseMask, (serviceName), (protoName), servicePort); } @@ -371,14 +377,14 @@ void MDNSResponder::_parsePacket(){ void MDNSResponder::_reply(uint8_t replyMask, char * service, char *proto, uint16_t port){ int i; if(replyMask == 0) return; - + #ifdef MDNS_DEBUG_TX os_printf("TX: mask:%01X, service:%s, proto:%s, port:%u\n", replyMask, service, proto, port); #endif - + char nameLen = os_strlen(_hostName); size_t serviceLen = os_strlen(service); - + uint8_t answerCount = 0; for(i=0;i<4;i++){ if(replyMask & (1 << i)) answerCount++; @@ -394,12 +400,12 @@ void MDNSResponder::_reply(uint8_t replyMask, char * service, char *proto, uint1 0x00, 0x00, //Additional records }; _conn->append(reinterpret_cast(head), 12); - + if((replyMask & 0x8) == 0){ _conn->append(reinterpret_cast(&nameLen), 1); _conn->append(reinterpret_cast(_hostName), nameLen); } - + if(replyMask & 0xE){ uint8_t servHead[2] = {(uint8_t)(serviceLen+1), '_'}; uint8_t protoHead[2] = {0x4, '_'}; @@ -408,14 +414,14 @@ void MDNSResponder::_reply(uint8_t replyMask, char * service, char *proto, uint1 _conn->append(reinterpret_cast(protoHead), 2); _conn->append(reinterpret_cast(proto), 3); } - + uint8_t local[7] = { 0x05, //strlen(_local) 0x6C, 0x6F, 0x63, 0x61, 0x6C, //local 0x00, //End of domain }; _conn->append(reinterpret_cast(local), 7); - + // PTR Response if(replyMask & 0x8){ uint8_t ptr[10] = { @@ -430,16 +436,16 @@ void MDNSResponder::_reply(uint8_t replyMask, char * service, char *proto, uint1 uint8_t ptrTail[2] = {0xC0, 0x0C}; _conn->append(reinterpret_cast(ptrTail), 2); } - + // TXT Response if(replyMask & 0x4){ if(replyMask & 0x8){//send the name uint8_t txtHead[2] = {0xC0, (uint8_t)(36 + serviceLen)}; _conn->append(reinterpret_cast(txtHead), 2); } - + uint8_t boardNameLen = os_strlen(_boardName); - + uint8_t txt[24] = { 0x00, 0x10, //Type TXT 0x80, 0x01, //Class IN, with cache flush @@ -450,9 +456,9 @@ void MDNSResponder::_reply(uint8_t replyMask, char * service, char *proto, uint1 }; _conn->append(reinterpret_cast(txt), 17); _conn->append(reinterpret_cast(_boardName), boardNameLen); - + } - + // SRV Response if(replyMask & 0x2){ if(replyMask & 0xC){//send the name @@ -461,7 +467,7 @@ void MDNSResponder::_reply(uint8_t replyMask, char * service, char *proto, uint1 srvHead[1] = 36 + serviceLen; _conn->append(reinterpret_cast(srvHead), 2); } - + uint8_t srv[16] = { 0x00, 0x21, //Type SRV 0x80, 0x01, //Class IN, with cache flush @@ -479,7 +485,7 @@ void MDNSResponder::_reply(uint8_t replyMask, char * service, char *proto, uint1 srvTail[1] = 19 + serviceLen; _conn->append(reinterpret_cast(srvTail), 2); } - + // A Response if(replyMask & 0x1){ uint32_t ip = _getOurIp(); @@ -488,7 +494,7 @@ void MDNSResponder::_reply(uint8_t replyMask, char * service, char *proto, uint1 _conn->append(reinterpret_cast(_hostName), nameLen); _conn->append(reinterpret_cast(local), 7); } - + uint8_t aaa[14] = { 0x00, 0x01, //TYPE A 0x80, 0x01, //Class IN, with cache flush diff --git a/libraries/ESP8266mDNS/ESP8266mDNS.h b/libraries/ESP8266mDNS/ESP8266mDNS.h index 4a8fa6ad1..a2fd17d24 100644 --- a/libraries/ESP8266mDNS/ESP8266mDNS.h +++ b/libraries/ESP8266mDNS/ESP8266mDNS.h @@ -13,10 +13,9 @@ Requirements: Usage: - Include the ESP8266 Multicast DNS library in the sketch. -- Create an instance of the MDNSResponder class. - Call the begin method in the sketch's setup and provide a domain name (without - the '.local' suffix, i.e. just provide 'foo' to resolve 'foo.local'), and the - Adafruit CC3000 class instance. Optionally provide a time to live (in seconds) + the '.local' suffix, i.e. just provide 'foo' to resolve 'foo.local'), and the + Adafruit CC3000 class instance. Optionally provide a time to live (in seconds) for the DNS record--the default is 1 hour. - Call the update method in each iteration of the sketch's loop function. @@ -70,7 +69,7 @@ public: return begin(hostName); } void update(); - + void addService(char *service, char *proto, uint16_t port); void addService(const char *service, const char *proto, uint16_t port){ addService((char *)service, (char *)proto, port); @@ -78,7 +77,7 @@ public: void addService(String service, String proto, uint16_t port){ addService(service.c_str(), proto.c_str(), port); } - + private: struct MDNSService * _services; UdpContext* _conn;