1
0
mirror of https://github.com/esp8266/Arduino.git synced 2025-04-21 10:26:06 +03:00

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.
This commit is contained in:
Ivan Grokhotkov 2015-08-10 16:14:26 +03:00
parent 208ae95979
commit 6cf18ed1a2
3 changed files with 58 additions and 55 deletions

View File

@ -290,7 +290,6 @@ When listening to multicast packets, replace `udp.begin(port)` with
`udp.beginMulticast(WiFi.localIP(), multicast_ip_addr, port)`. `udp.beginMulticast(WiFi.localIP(), multicast_ip_addr, port)`.
You can use `udp.destinationIP()` to tell whether the packet received was You can use `udp.destinationIP()` to tell whether the packet received was
sent to the multicast or unicast address. 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. `WiFiServer`, `WiFiClient`, and `WiFiUDP` behave mostly the same way as with WiFi shield library.
Four samples are provided for this 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) ## 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. 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. See attached example for details.
## SSDP responder (ESP8266SSDP) ## SSDP responder (ESP8266SSDP)

View File

@ -36,7 +36,7 @@ License (MIT license):
#include "ESP8266mDNS.h" #include "ESP8266mDNS.h"
#include <functional> #include <functional>
#include "debug.h" #include "debug.h"
extern "C" { extern "C" {
@ -88,28 +88,28 @@ static const int MDNS_PORT = 5353;
MDNSResponder::MDNSResponder() : _conn(0) { _services = 0; } MDNSResponder::MDNSResponder() : _conn(0) { _services = 0; }
MDNSResponder::~MDNSResponder() {} MDNSResponder::~MDNSResponder() {}
bool MDNSResponder::begin(const char* domain){ bool MDNSResponder::begin(const char* domain){
// Open the MDNS socket if it isn't already open. // Open the MDNS socket if it isn't already open.
size_t n = strlen(domain); size_t n = strlen(domain);
if (n > 255) { // Can only handle domains that are 255 chars in length. if (n > 255) { // Can only handle domains that are 255 chars in length.
return false; return false;
} }
// Copy in domain characters as lowercase // Copy in domain characters as lowercase
for (int i = 0; i < n; ++i) for (int i = 0; i < n; ++i)
_hostName[i] = tolower(domain[i]); _hostName[i] = tolower(domain[i]);
_hostName[n] = '\0'; _hostName[n] = '\0';
os_strcpy(_boardName, ARDUINO_BOARD); os_strcpy(_boardName, ARDUINO_BOARD);
// Open the MDNS socket if it isn't already open. // Open the MDNS socket if it isn't already open.
if (!_conn) { if (!_conn) {
uint32_t ourIp = _getOurIp(); uint32_t ourIp = _getOurIp();
if(ourIp == 0){ if(ourIp == 0){
return false; return false;
} }
ip_addr_t ifaddr; ip_addr_t ifaddr;
ifaddr.addr = ourIp; ifaddr.addr = ourIp;
ip_addr_t multicast_addr; ip_addr_t multicast_addr;
@ -135,7 +135,7 @@ bool MDNSResponder::begin(const char* domain){
void MDNSResponder::update() { void MDNSResponder::update() {
if (!_conn->next()) { if (!_conn->next()) {
return; return;
} }
_parsePacket(); _parsePacket();
} }
@ -163,15 +163,21 @@ uint16_t MDNSResponder::_getServicePort(char *name, char *proto){
} }
uint32_t MDNSResponder::_getOurIp(){ uint32_t MDNSResponder::_getOurIp(){
if(wifi_get_opmode() & STATION_MODE){ int mode = wifi_get_opmode();
if(mode & STATION_MODE){
struct ip_info staIpInfo; struct ip_info staIpInfo;
wifi_get_ip_info(STATION_IF, &staIpInfo); wifi_get_ip_info(STATION_IF, &staIpInfo);
return staIpInfo.ip.addr; 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 #ifdef MDNS_DEBUG_ERR
os_printf("ERR_NO_LOCAL_IP\n"); os_printf("ERR_NO_LOCAL_IP\n");
#endif #endif
return 0; return 0;
}
} }
void MDNSResponder::_parsePacket(){ void MDNSResponder::_parsePacket(){
@ -180,39 +186,39 @@ void MDNSResponder::_parsePacket(){
bool serviceParsed = false; bool serviceParsed = false;
bool protoParsed = false; bool protoParsed = false;
bool localParsed = false; bool localParsed = false;
char hostName[255]; char hostName[255];
uint8_t hostNameLen; uint8_t hostNameLen;
char serviceName[32]; char serviceName[32];
uint8_t serviceNameLen; uint8_t serviceNameLen;
uint16_t servicePort; uint16_t servicePort;
char protoName[32]; char protoName[32];
uint8_t protoNameLen; uint8_t protoNameLen;
uint16_t packetHeader[6]; uint16_t packetHeader[6];
for(i=0; i<6; i++) packetHeader[i] = _conn_read16(); for(i=0; i<6; i++) packetHeader[i] = _conn_read16();
if((packetHeader[1] & 0x8000) != 0){ //not parsing responses yet if((packetHeader[1] & 0x8000) != 0){ //not parsing responses yet
_conn->flush(); _conn->flush();
return; return;
} }
// PARSE REQUEST NAME // PARSE REQUEST NAME
hostNameLen = _conn_read8(); hostNameLen = _conn_read8();
_conn_readS(hostName, hostNameLen); _conn_readS(hostName, hostNameLen);
hostName[hostNameLen] = '\0'; hostName[hostNameLen] = '\0';
if(hostName[0] == '_'){ if(hostName[0] == '_'){
serviceParsed = true; serviceParsed = true;
memcpy(serviceName, hostName+1, hostNameLen); memcpy(serviceName, hostName+1, hostNameLen);
serviceNameLen = hostNameLen-1; serviceNameLen = hostNameLen-1;
hostNameLen = 0; hostNameLen = 0;
} }
if(hostNameLen > 0 && strcmp(_hostName, hostName) != 0){ if(hostNameLen > 0 && strcmp(_hostName, hostName) != 0){
#ifdef MDNS_DEBUG_ERR #ifdef MDNS_DEBUG_ERR
os_printf("ERR_NO_HOST: %s\n", hostName); os_printf("ERR_NO_HOST: %s\n", hostName);
@ -220,12 +226,12 @@ void MDNSResponder::_parsePacket(){
_conn->flush(); _conn->flush();
return; return;
} }
if(!serviceParsed){ if(!serviceParsed){
serviceNameLen = _conn_read8(); serviceNameLen = _conn_read8();
_conn_readS(serviceName, serviceNameLen); _conn_readS(serviceName, serviceNameLen);
serviceName[serviceNameLen] = '\0'; serviceName[serviceNameLen] = '\0';
if(serviceName[0] == '_'){ if(serviceName[0] == '_'){
memcpy(serviceName, serviceName+1, serviceNameLen); memcpy(serviceName, serviceName+1, serviceNameLen);
serviceNameLen--; serviceNameLen--;
@ -253,7 +259,7 @@ void MDNSResponder::_parsePacket(){
return; return;
} }
} }
if(!protoParsed){ if(!protoParsed){
protoNameLen = _conn_read8(); protoNameLen = _conn_read8();
_conn_readS(protoName, protoNameLen); _conn_readS(protoName, protoNameLen);
@ -270,7 +276,7 @@ void MDNSResponder::_parsePacket(){
return; return;
} }
} }
if(!localParsed){ if(!localParsed){
char localName[32]; char localName[32];
uint8_t localNameLen = _conn_read8(); uint8_t localNameLen = _conn_read8();
@ -287,7 +293,7 @@ void MDNSResponder::_parsePacket(){
return; return;
} }
} }
if(serviceNameLen > 0 && protoNameLen > 0){ if(serviceNameLen > 0 && protoNameLen > 0){
servicePort = _getServicePort(serviceName, protoName); servicePort = _getServicePort(serviceName, protoName);
if(servicePort == 0){ if(servicePort == 0){
@ -304,16 +310,16 @@ void MDNSResponder::_parsePacket(){
_conn->flush(); _conn->flush();
return; return;
} }
// RESPOND // RESPOND
#ifdef MDNS_DEBUG_RX #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]); 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 #endif
uint16_t currentType; uint16_t currentType;
uint16_t currentClass; uint16_t currentClass;
int numQuestions = packetHeader[2]; int numQuestions = packetHeader[2];
if(numQuestions > 4) numQuestions = 4; if(numQuestions > 4) numQuestions = 4;
uint16_t questions[4]; uint16_t questions[4];
@ -326,21 +332,21 @@ void MDNSResponder::_parsePacket(){
} }
currentClass = _conn_read16(); currentClass = _conn_read16();
if(currentClass & MDNS_CLASS_IN) questions[question++] = currentType; if(currentClass & MDNS_CLASS_IN) questions[question++] = currentType;
if(numQuestions > 0){ if(numQuestions > 0){
if(_conn_read16() != 0xC00C){//new question but for another host/service if(_conn_read16() != 0xC00C){//new question but for another host/service
_conn->flush(); _conn->flush();
numQuestions = 0; numQuestions = 0;
} }
} }
#ifdef MDNS_DEBUG_RX #ifdef MDNS_DEBUG_RX
os_printf("REQ: "); os_printf("REQ: ");
if(hostNameLen > 0) os_printf("%s.", hostName); if(hostNameLen > 0) os_printf("%s.", hostName);
if(serviceNameLen > 0) os_printf("_%s.", serviceName); if(serviceNameLen > 0) os_printf("_%s.", serviceName);
if(protoNameLen > 0) os_printf("_%s.", protoName); if(protoNameLen > 0) os_printf("_%s.", protoName);
os_printf("local. "); os_printf("local. ");
if(currentType == MDNS_TYPE_AAAA) os_printf(" AAAA "); if(currentType == MDNS_TYPE_AAAA) os_printf(" AAAA ");
else if(currentType == MDNS_TYPE_A) os_printf(" A "); else if(currentType == MDNS_TYPE_A) os_printf(" A ");
else if(currentType == MDNS_TYPE_PTR) os_printf(" PTR "); else if(currentType == MDNS_TYPE_PTR) os_printf(" PTR ");
@ -351,7 +357,7 @@ void MDNSResponder::_parsePacket(){
if(currentClass == MDNS_CLASS_IN) os_printf(" IN "); if(currentClass == MDNS_CLASS_IN) os_printf(" IN ");
else if(currentClass == MDNS_CLASS_IN_FLUSH_CACHE) os_printf(" IN[F] "); else if(currentClass == MDNS_CLASS_IN_FLUSH_CACHE) os_printf(" IN[F] ");
else os_printf(" 0x%04X ", currentClass); else os_printf(" 0x%04X ", currentClass);
os_printf("\n"); os_printf("\n");
#endif #endif
} }
@ -362,7 +368,7 @@ void MDNSResponder::_parsePacket(){
else if(questions[i] == MDNS_TYPE_TXT) responseMask |= 0x4; else if(questions[i] == MDNS_TYPE_TXT) responseMask |= 0x4;
else if(questions[i] == MDNS_TYPE_PTR) responseMask |= 0xF; else if(questions[i] == MDNS_TYPE_PTR) responseMask |= 0xF;
} }
return _reply(responseMask, (serviceName), (protoName), servicePort); 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){ void MDNSResponder::_reply(uint8_t replyMask, char * service, char *proto, uint16_t port){
int i; int i;
if(replyMask == 0) return; if(replyMask == 0) return;
#ifdef MDNS_DEBUG_TX #ifdef MDNS_DEBUG_TX
os_printf("TX: mask:%01X, service:%s, proto:%s, port:%u\n", replyMask, service, proto, port); os_printf("TX: mask:%01X, service:%s, proto:%s, port:%u\n", replyMask, service, proto, port);
#endif #endif
char nameLen = os_strlen(_hostName); char nameLen = os_strlen(_hostName);
size_t serviceLen = os_strlen(service); size_t serviceLen = os_strlen(service);
uint8_t answerCount = 0; uint8_t answerCount = 0;
for(i=0;i<4;i++){ for(i=0;i<4;i++){
if(replyMask & (1 << i)) answerCount++; if(replyMask & (1 << i)) answerCount++;
@ -394,12 +400,12 @@ void MDNSResponder::_reply(uint8_t replyMask, char * service, char *proto, uint1
0x00, 0x00, //Additional records 0x00, 0x00, //Additional records
}; };
_conn->append(reinterpret_cast<const char*>(head), 12); _conn->append(reinterpret_cast<const char*>(head), 12);
if((replyMask & 0x8) == 0){ if((replyMask & 0x8) == 0){
_conn->append(reinterpret_cast<const char*>(&nameLen), 1); _conn->append(reinterpret_cast<const char*>(&nameLen), 1);
_conn->append(reinterpret_cast<const char*>(_hostName), nameLen); _conn->append(reinterpret_cast<const char*>(_hostName), nameLen);
} }
if(replyMask & 0xE){ if(replyMask & 0xE){
uint8_t servHead[2] = {(uint8_t)(serviceLen+1), '_'}; uint8_t servHead[2] = {(uint8_t)(serviceLen+1), '_'};
uint8_t protoHead[2] = {0x4, '_'}; uint8_t protoHead[2] = {0x4, '_'};
@ -408,14 +414,14 @@ void MDNSResponder::_reply(uint8_t replyMask, char * service, char *proto, uint1
_conn->append(reinterpret_cast<const char*>(protoHead), 2); _conn->append(reinterpret_cast<const char*>(protoHead), 2);
_conn->append(reinterpret_cast<const char*>(proto), 3); _conn->append(reinterpret_cast<const char*>(proto), 3);
} }
uint8_t local[7] = { uint8_t local[7] = {
0x05, //strlen(_local) 0x05, //strlen(_local)
0x6C, 0x6F, 0x63, 0x61, 0x6C, //local 0x6C, 0x6F, 0x63, 0x61, 0x6C, //local
0x00, //End of domain 0x00, //End of domain
}; };
_conn->append(reinterpret_cast<const char*>(local), 7); _conn->append(reinterpret_cast<const char*>(local), 7);
// PTR Response // PTR Response
if(replyMask & 0x8){ if(replyMask & 0x8){
uint8_t ptr[10] = { 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}; uint8_t ptrTail[2] = {0xC0, 0x0C};
_conn->append(reinterpret_cast<const char*>(ptrTail), 2); _conn->append(reinterpret_cast<const char*>(ptrTail), 2);
} }
// TXT Response // TXT Response
if(replyMask & 0x4){ if(replyMask & 0x4){
if(replyMask & 0x8){//send the name if(replyMask & 0x8){//send the name
uint8_t txtHead[2] = {0xC0, (uint8_t)(36 + serviceLen)}; uint8_t txtHead[2] = {0xC0, (uint8_t)(36 + serviceLen)};
_conn->append(reinterpret_cast<const char*>(txtHead), 2); _conn->append(reinterpret_cast<const char*>(txtHead), 2);
} }
uint8_t boardNameLen = os_strlen(_boardName); uint8_t boardNameLen = os_strlen(_boardName);
uint8_t txt[24] = { uint8_t txt[24] = {
0x00, 0x10, //Type TXT 0x00, 0x10, //Type TXT
0x80, 0x01, //Class IN, with cache flush 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<const char*>(txt), 17); _conn->append(reinterpret_cast<const char*>(txt), 17);
_conn->append(reinterpret_cast<const char*>(_boardName), boardNameLen); _conn->append(reinterpret_cast<const char*>(_boardName), boardNameLen);
} }
// SRV Response // SRV Response
if(replyMask & 0x2){ if(replyMask & 0x2){
if(replyMask & 0xC){//send the name 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; srvHead[1] = 36 + serviceLen;
_conn->append(reinterpret_cast<const char*>(srvHead), 2); _conn->append(reinterpret_cast<const char*>(srvHead), 2);
} }
uint8_t srv[16] = { uint8_t srv[16] = {
0x00, 0x21, //Type SRV 0x00, 0x21, //Type SRV
0x80, 0x01, //Class IN, with cache flush 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; srvTail[1] = 19 + serviceLen;
_conn->append(reinterpret_cast<const char*>(srvTail), 2); _conn->append(reinterpret_cast<const char*>(srvTail), 2);
} }
// A Response // A Response
if(replyMask & 0x1){ if(replyMask & 0x1){
uint32_t ip = _getOurIp(); uint32_t ip = _getOurIp();
@ -488,7 +494,7 @@ void MDNSResponder::_reply(uint8_t replyMask, char * service, char *proto, uint1
_conn->append(reinterpret_cast<const char*>(_hostName), nameLen); _conn->append(reinterpret_cast<const char*>(_hostName), nameLen);
_conn->append(reinterpret_cast<const char*>(local), 7); _conn->append(reinterpret_cast<const char*>(local), 7);
} }
uint8_t aaa[14] = { uint8_t aaa[14] = {
0x00, 0x01, //TYPE A 0x00, 0x01, //TYPE A
0x80, 0x01, //Class IN, with cache flush 0x80, 0x01, //Class IN, with cache flush

View File

@ -13,10 +13,9 @@ Requirements:
Usage: Usage:
- Include the ESP8266 Multicast DNS library in the sketch. - 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 - 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 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) Adafruit CC3000 class instance. Optionally provide a time to live (in seconds)
for the DNS record--the default is 1 hour. for the DNS record--the default is 1 hour.
- Call the update method in each iteration of the sketch's loop function. - Call the update method in each iteration of the sketch's loop function.
@ -70,7 +69,7 @@ public:
return begin(hostName); return begin(hostName);
} }
void update(); void update();
void addService(char *service, char *proto, uint16_t port); void addService(char *service, char *proto, uint16_t port);
void addService(const char *service, const char *proto, uint16_t port){ void addService(const char *service, const char *proto, uint16_t port){
addService((char *)service, (char *)proto, port); addService((char *)service, (char *)proto, port);
@ -78,7 +77,7 @@ public:
void addService(String service, String proto, uint16_t port){ void addService(String service, String proto, uint16_t port){
addService(service.c_str(), proto.c_str(), port); addService(service.c_str(), proto.c_str(), port);
} }
private: private:
struct MDNSService * _services; struct MDNSService * _services;
UdpContext* _conn; UdpContext* _conn;