1
0
mirror of https://github.com/esp8266/Arduino.git synced 2025-09-06 19:08:12 +03:00

Merge pull request #1741 from larsenglund/master

Extended mDNS-SD support (sending queries and receiving answers)
This commit is contained in:
Ivan Grokhotkov
2016-03-10 12:53:18 +03:00
3 changed files with 423 additions and 2 deletions

View File

@@ -5,6 +5,7 @@ Version 1.1
Copyright (c) 2013 Tony DiCola (tony@tonydicola.com) Copyright (c) 2013 Tony DiCola (tony@tonydicola.com)
ESP8266 port (c) 2015 Ivan Grokhotkov (ivan@esp8266.com) ESP8266 port (c) 2015 Ivan Grokhotkov (ivan@esp8266.com)
MDNS-SD Suport 2015 Hristo Gochkov MDNS-SD Suport 2015 Hristo Gochkov
Extended MDNS-SD support 2016 Lars Englund (lars.englund@gmail.com)
License (MIT license): License (MIT license):
@@ -31,6 +32,7 @@ License (MIT license):
// Important RFC's for reference: // Important RFC's for reference:
// - DNS request and response: http://www.ietf.org/rfc/rfc1035.txt // - DNS request and response: http://www.ietf.org/rfc/rfc1035.txt
// - Multicast DNS: http://www.ietf.org/rfc/rfc6762.txt // - Multicast DNS: http://www.ietf.org/rfc/rfc6762.txt
// - MDNS-SD: https://tools.ietf.org/html/rfc6763
#define LWIP_OPEN_SRC #define LWIP_OPEN_SRC
@@ -99,13 +101,44 @@ struct MDNSTxt{
String _txt; String _txt;
}; };
struct MDNSAnswer {
MDNSAnswer* next;
uint8_t ip[4];
uint16_t port;
char *hostname;
};
struct MDNSQuery {
char _service[32];
char _proto[4];
};
MDNSResponder::MDNSResponder() : _conn(0) { MDNSResponder::MDNSResponder() : _conn(0) {
_services = 0; _services = 0;
_instanceName = ""; _instanceName = "";
_answers = 0;
_query = 0;
_newQuery = false;
_waitingForAnswers = false;
}
MDNSResponder::~MDNSResponder() {
if (_query != 0) {
os_free(_query);
_query = 0;
}
// Clear answer list
MDNSAnswer *answer;
int numAnswers = _getNumAnswers();
for (int n = numAnswers - 1; n >= 0; n--) {
answer = _getAnswerFromIdx(n);
os_free(answer->hostname);
os_free(answer);
answer = 0;
}
_answers = 0;
} }
MDNSResponder::~MDNSResponder() {}
bool MDNSResponder::begin(const char* hostname){ bool MDNSResponder::begin(const char* hostname){
// Open the MDNS socket if it isn't already open. // Open the MDNS socket if it isn't already open.
@@ -221,6 +254,130 @@ void MDNSResponder::addService(char *name, char *proto, uint16_t port){
} }
int MDNSResponder::queryService(char *service, char *proto) {
#ifdef MDNS_DEBUG_TX
Serial.printf("queryService %s %s\n", service, proto);
#endif
if (_query != 0) {
os_free(_query);
_query = 0;
}
_query = (struct MDNSQuery*)(os_malloc(sizeof(struct MDNSQuery)));
os_strcpy(_query->_service, service);
os_strcpy(_query->_proto, proto);
_newQuery = true;
char underscore[] = "_";
// build service name with _
char serviceName[os_strlen(service) + 2];
os_strcpy(serviceName, underscore);
os_strcat(serviceName, service);
size_t serviceNameLen = os_strlen(serviceName);
//build proto name with _
char protoName[5];
os_strcpy(protoName, underscore);
os_strcat(protoName, proto);
size_t protoNameLen = 4;
//local string
char localName[] = "local";
size_t localNameLen = 5;
//terminator
char terminator[] = "\0";
// Only supports sending one PTR query
uint8_t questionCount = 1;
// Write the header
_conn->flush();
uint8_t head[12] = {
0x00, 0x00, //ID = 0
0x00, 0x00, //Flags = response + authoritative answer
0x00, questionCount, //Question count
0x00, 0x00, //Answer count
0x00, 0x00, //Name server records
0x00, 0x00 //Additional records
};
_conn->append(reinterpret_cast<const char*>(head), 12);
// Only supports sending one PTR query
// Send the Name field (eg. "_http._tcp.local")
_conn->append(reinterpret_cast<const char*>(&serviceNameLen), 1); // lenght of "_" + service
_conn->append(reinterpret_cast<const char*>(serviceName), serviceNameLen); // "_" + service
_conn->append(reinterpret_cast<const char*>(&protoNameLen), 1); // lenght of "_" + proto
_conn->append(reinterpret_cast<const char*>(protoName), protoNameLen); // "_" + proto
_conn->append(reinterpret_cast<const char*>(&localNameLen), 1); // lenght of "local"
_conn->append(reinterpret_cast<const char*>(localName), localNameLen); // "local"
_conn->append(reinterpret_cast<const char*>(&terminator), 1); // terminator
//Send the type and class
uint8_t ptrAttrs[4] = {
0x00, 0x0c, //PTR record query
0x00, 0x01 //Class IN
};
_conn->append(reinterpret_cast<const char*>(ptrAttrs), 4);
_waitingForAnswers = true;
_conn->send();
#ifdef MDNS_DEBUG_TX
Serial.println("Waiting for answers..");
#endif
delay(1000);
_waitingForAnswers = false;
return _getNumAnswers();
}
String MDNSResponder::hostname(int idx) {
MDNSAnswer *answer = _getAnswerFromIdx(idx);
if (answer == 0) {
return String();
}
return answer->hostname;
}
IPAddress MDNSResponder::IP(int idx) {
MDNSAnswer *answer = _getAnswerFromIdx(idx);
if (answer == 0) {
return IPAddress();
}
return IPAddress(answer->ip);
}
uint16_t MDNSResponder::port(int idx) {
MDNSAnswer *answer = _getAnswerFromIdx(idx);
if (answer == 0) {
return 0;
}
return answer->port;
}
MDNSAnswer* MDNSResponder::_getAnswerFromIdx(int idx) {
MDNSAnswer *answer = _answers;
while (answer != 0 && idx-- > 0) {
answer = answer->next;
}
if (idx > 0) {
return 0;
}
return answer;
}
int MDNSResponder::_getNumAnswers() {
int numAnswers = 0;
MDNSAnswer *answer = _answers;
while (answer != 0) {
numAnswers++;
answer = answer->next;
}
return numAnswers;
}
MDNSTxt * MDNSResponder::_getServiceTxt(char *name, char *proto){ MDNSTxt * MDNSResponder::_getServiceTxt(char *name, char *proto){
MDNSService* servicePtr; MDNSService* servicePtr;
for (servicePtr = _services; servicePtr; servicePtr = servicePtr->_next) { for (servicePtr = _services; servicePtr; servicePtr = servicePtr->_next) {
@@ -297,7 +454,176 @@ void MDNSResponder::_parsePacket(){
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) { // Read answers
#ifdef MDNS_DEBUG_RX
Serial.printf("Reading answers RX: REQ, ID:%u, Q:%u, A:%u, NS:%u, ADD:%u\n", packetHeader[0], packetHeader[2], packetHeader[3], packetHeader[4], packetHeader[5]);
#endif
if (!_waitingForAnswers) {
#ifdef MDNS_DEBUG_RX
Serial.println("Not expecting any answers right now, returning");
#endif
_conn->flush();
return;
}
int numAnswers = packetHeader[3];
// Assume that the PTR answer always comes first and that it is always accompanied by a TXT, SRV and A answer in the same packet.
if (numAnswers != 4) {
#ifdef MDNS_DEBUG_RX
Serial.println("Expected a packet with 4 answers, returning");
#endif
_conn->flush();
return;
}
uint8_t tmp8;
uint16_t answerPort = 0;
uint8_t answerIp[4] = { 0,0,0,0 };
char answerHostName[255];
bool serviceMatch = false;
MDNSAnswer *answer;
uint8_t partsCollected = 0;
// Clear answer list
if (_newQuery) {
int numAnswers = _getNumAnswers();
for (int n = numAnswers - 1; n >= 0; n--) {
answer = _getAnswerFromIdx(n);
os_free(answer->hostname);
os_free(answer);
answer = 0;
}
_answers = 0;
_newQuery = false;
}
while (numAnswers--) {
// Read name
do {
tmp8 = _conn_read8();
if (tmp8 & 0xC0) { // Compressed pointer (not supported)
tmp8 = _conn_read8();
break;
}
if (tmp8 == 0x00) { // <20>nd of name
break;
}
_conn_readS(serviceName, tmp8);
serviceName[tmp8] = '\0';
#ifdef MDNS_DEBUG_RX
Serial.printf(" %d ", tmp8);
for (int n = 0; n < tmp8; n++) {
Serial.printf("%02x ", serviceName[n]);
}
Serial.println();
#endif
if (serviceName[0] == '_') {
if (strcmp(&serviceName[1], _query->_service) == 0) {
serviceMatch = true;
#ifdef MDNS_DEBUG_RX
Serial.printf("found matching service: %s\n", _query->_service);
#endif
}
}
} while (true);
uint16_t answerType = _conn_read16(); // Read type
uint16_t answerClass = _conn_read16(); // Read class
uint32_t answerTtl = _conn_read32(); // Read ttl
uint16_t answerRdlength = _conn_read16(); // Read rdlength
#ifdef MDNS_DEBUG_RX
Serial.printf("type: %04x rdlength: %d\n", answerType, answerRdlength);
#endif
if (answerType == MDNS_TYPE_PTR) {
partsCollected |= 0x01;
_conn_readS(hostName, answerRdlength); // Read rdata
#ifdef MDNS_DEBUG_RX
for (int n = 0; n < answerRdlength; n++) {
Serial.printf("%02x ", hostName[n]);
}
Serial.println();
#endif
}
if (answerType == MDNS_TYPE_TXT) {
partsCollected |= 0x02;
_conn_readS(hostName, answerRdlength); // Read rdata
#ifdef MDNS_DEBUG_RX
for (int n = 0; n < answerRdlength; n++) {
Serial.printf("%02x ", hostName[n]);
}
Serial.println();
#endif
}
if (answerType == MDNS_TYPE_SRV) {
partsCollected |= 0x04;
uint16_t answerPrio = _conn_read16(); // Read priority
uint16_t answerWeight = _conn_read16(); // Read weight
answerPort = _conn_read16(); // Read port
// Read hostname
tmp8 = _conn_read8();
if (tmp8 & 0xC0) { // Compressed pointer (not supported)
Serial.println("Skipping compressed pointer");
tmp8 = _conn_read8();
}
else {
_conn_readS(answerHostName, tmp8);
answerHostName[tmp8] = '\0';
#ifdef MDNS_DEBUG_RX
Serial.printf(" %d ", tmp8);
for (int n = 0; n < tmp8; n++) {
Serial.printf("%02x ", answerHostName[n]);
}
Serial.printf("\n%s\n", answerHostName);
#endif
if (answerRdlength - (6 + 1 + tmp8) > 0) { // Skip any remaining rdata
_conn_readS(hostName, answerRdlength - (6 + 1 + tmp8));
}
}
}
if (answerType == MDNS_TYPE_A) {
partsCollected |= 0x08;
for (int i = 0; i < 4; i++) {
answerIp[i] = _conn_read8();
}
}
if ((partsCollected == 0x0F) && serviceMatch) {
#ifdef MDNS_DEBUG_RX
Serial.println("All answers parsed, adding to _answers list..");
#endif
// Add new answer to answer list
if (_answers == 0) {
_answers = (struct MDNSAnswer*)(os_malloc(sizeof(struct MDNSAnswer)));
answer = _answers;
}
else {
answer = _answers;
while (answer->next != 0) {
answer = _answers->next;
}
answer->next = (struct MDNSAnswer*)(os_malloc(sizeof(struct MDNSAnswer)));
answer = answer->next;
}
answer->next = 0;
answer->hostname = 0;
// Populate new answer
answer->port = answerPort;
for (int i = 0; i < 4; i++) {
answer->ip[i] = answerIp[i];
}
answer->hostname = (char *)os_malloc(strlen(answerHostName) + 1);
os_strcpy(answer->hostname, answerHostName);
}
}
_conn->flush(); _conn->flush();
return; return;
} }

View File

@@ -3,6 +3,7 @@ ESP8266 Multicast DNS (port of CC3000 Multicast DNS library)
Version 1.1 Version 1.1
Copyright (c) 2013 Tony DiCola (tony@tonydicola.com) Copyright (c) 2013 Tony DiCola (tony@tonydicola.com)
ESP8266 port (c) 2015 Ivan Grokhotkov (ivan@esp8266.com) ESP8266 port (c) 2015 Ivan Grokhotkov (ivan@esp8266.com)
Extended MDNS-SD support 2016 Lars Englund (lars.englund@gmail.com)
This is a simple implementation of multicast DNS query support for an Arduino This is a simple implementation of multicast DNS query support for an Arduino
running on ESP8266 chip. Only support for resolving address queries is currently running on ESP8266 chip. Only support for resolving address queries is currently
@@ -54,6 +55,7 @@ class UdpContext;
struct MDNSService; struct MDNSService;
struct MDNSTxt; struct MDNSTxt;
struct MDNSAnswer;
class MDNSResponder { class MDNSResponder {
public: public:
@@ -82,6 +84,17 @@ public:
addServiceTxt(name.c_str(), proto.c_str(), key.c_str(), value.c_str()); addServiceTxt(name.c_str(), proto.c_str(), key.c_str(), value.c_str());
} }
int queryService(char *service, char *proto);
int queryService(const char *service, const char *proto){
return queryService((char *)service, (char *)proto);
}
int queryService(String service, String proto){
return queryService(service.c_str(), proto.c_str());
}
String hostname(int idx);
IPAddress IP(int idx);
uint16_t port(int idx);
void enableArduino(uint16_t port, bool auth=false); void enableArduino(uint16_t port, bool auth=false);
void setInstanceName(String name); void setInstanceName(String name);
@@ -97,6 +110,11 @@ private:
UdpContext* _conn; UdpContext* _conn;
String _hostName; String _hostName;
String _instanceName; String _instanceName;
struct MDNSAnswer * _answers;
struct MDNSQuery * _query;
bool _newQuery;
bool _waitingForAnswers;
uint32_t _getOurIp(); uint32_t _getOurIp();
uint16_t _getServicePort(char *service, char *proto); uint16_t _getServicePort(char *service, char *proto);
@@ -105,6 +123,8 @@ private:
void _parsePacket(); void _parsePacket();
void _reply(uint8_t replyMask, char * service, char *proto, uint16_t port); void _reply(uint8_t replyMask, char * service, char *proto, uint16_t port);
size_t advertiseServices(); // advertise all hosted services size_t advertiseServices(); // advertise all hosted services
MDNSAnswer* _getAnswerFromIdx(int idx);
int _getNumAnswers();
}; };
extern MDNSResponder MDNS; extern MDNSResponder MDNS;

View File

@@ -0,0 +1,75 @@
/*
ESP8266 mDNS-SD responder and query sample
This is an example of announcing and finding services.
Instructions:
- Update WiFi SSID and password as necessary.
- Flash the sketch to two ESP8266 boards
- The last one powered on should now find the other.
*/
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
const char* ssid = "...";
const char* password = "...";
char hostString[16] = {0};
void setup() {
Serial.begin(115200);
delay(100);
Serial.println("\r\nsetup()");
sprintf(hostString, "ESP_%06X", ESP.getChipId());
Serial.print("Hostname: ");
Serial.println(hostString);
WiFi.hostname(hostString);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(250);
Serial.print(".");
}
Serial.println("");
Serial.print("Connected to ");
Serial.println(ssid);
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
if (!MDNS.begin(hostString)) {
Serial.println("Error setting up MDNS responder!");
}
Serial.println("mDNS responder started");
MDNS.addService("esp", "tcp", 8080); // Announce esp tcp service on port 8080
Serial.println("Sending mDNS query");
int n = MDNS.queryService("esp", "tcp"); // Send out query for esp tcp services
Serial.println("mDNS query done");
if (n == 0) {
Serial.println("no services found");
}
else {
Serial.print(n);
Serial.println(" service(s) found");
for (int i = 0; i < n; ++i) {
// Print details for each service found
Serial.print(i + 1);
Serial.print(": ");
Serial.print(MDNS.hostname(i));
Serial.print(" (");
Serial.print(MDNS.IP(i));
Serial.print(":");
Serial.print(MDNS.port(i));
Serial.println(")");
}
}
Serial.println();
Serial.println("loop() next");
}
void loop() {
// put your main code here, to run repeatedly:
}