Skip to content

Commit 302366d

Browse files
committed
Merge pull request esp8266#1741 from larsenglund/master
Extended mDNS-SD support (sending queries and receiving answers)
2 parents 99b1469 + 609c84b commit 302366d

File tree

3 files changed

+423
-2
lines changed

3 files changed

+423
-2
lines changed

libraries/ESP8266mDNS/ESP8266mDNS.cpp

Lines changed: 328 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Version 1.1
55
Copyright (c) 2013 Tony DiCola ([email protected])
66
ESP8266 port (c) 2015 Ivan Grokhotkov ([email protected])
77
MDNS-SD Suport 2015 Hristo Gochkov
8+
Extended MDNS-SD support 2016 Lars Englund ([email protected])
89
910
1011
License (MIT license):
@@ -31,6 +32,7 @@ License (MIT license):
3132
// Important RFC's for reference:
3233
// - DNS request and response: http://www.ietf.org/rfc/rfc1035.txt
3334
// - Multicast DNS: http://www.ietf.org/rfc/rfc6762.txt
35+
// - MDNS-SD: https://tools.ietf.org/html/rfc6763
3436

3537
#define LWIP_OPEN_SRC
3638

@@ -99,13 +101,44 @@ struct MDNSTxt{
99101
String _txt;
100102
};
101103

104+
struct MDNSAnswer {
105+
MDNSAnswer* next;
106+
uint8_t ip[4];
107+
uint16_t port;
108+
char *hostname;
109+
};
110+
111+
struct MDNSQuery {
112+
char _service[32];
113+
char _proto[4];
114+
};
102115

103116

104117
MDNSResponder::MDNSResponder() : _conn(0) {
105118
_services = 0;
106119
_instanceName = "";
120+
_answers = 0;
121+
_query = 0;
122+
_newQuery = false;
123+
_waitingForAnswers = false;
124+
}
125+
MDNSResponder::~MDNSResponder() {
126+
if (_query != 0) {
127+
os_free(_query);
128+
_query = 0;
129+
}
130+
131+
// Clear answer list
132+
MDNSAnswer *answer;
133+
int numAnswers = _getNumAnswers();
134+
for (int n = numAnswers - 1; n >= 0; n--) {
135+
answer = _getAnswerFromIdx(n);
136+
os_free(answer->hostname);
137+
os_free(answer);
138+
answer = 0;
139+
}
140+
_answers = 0;
107141
}
108-
MDNSResponder::~MDNSResponder() {}
109142

110143
bool MDNSResponder::begin(const char* hostname){
111144
// Open the MDNS socket if it isn't already open.
@@ -221,6 +254,130 @@ void MDNSResponder::addService(char *name, char *proto, uint16_t port){
221254

222255
}
223256

257+
int MDNSResponder::queryService(char *service, char *proto) {
258+
#ifdef MDNS_DEBUG_TX
259+
Serial.printf("queryService %s %s\n", service, proto);
260+
#endif
261+
262+
if (_query != 0) {
263+
os_free(_query);
264+
_query = 0;
265+
}
266+
_query = (struct MDNSQuery*)(os_malloc(sizeof(struct MDNSQuery)));
267+
os_strcpy(_query->_service, service);
268+
os_strcpy(_query->_proto, proto);
269+
_newQuery = true;
270+
271+
char underscore[] = "_";
272+
273+
// build service name with _
274+
char serviceName[os_strlen(service) + 2];
275+
os_strcpy(serviceName, underscore);
276+
os_strcat(serviceName, service);
277+
size_t serviceNameLen = os_strlen(serviceName);
278+
279+
//build proto name with _
280+
char protoName[5];
281+
os_strcpy(protoName, underscore);
282+
os_strcat(protoName, proto);
283+
size_t protoNameLen = 4;
284+
285+
//local string
286+
char localName[] = "local";
287+
size_t localNameLen = 5;
288+
289+
//terminator
290+
char terminator[] = "\0";
291+
292+
// Only supports sending one PTR query
293+
uint8_t questionCount = 1;
294+
295+
// Write the header
296+
_conn->flush();
297+
uint8_t head[12] = {
298+
0x00, 0x00, //ID = 0
299+
0x00, 0x00, //Flags = response + authoritative answer
300+
0x00, questionCount, //Question count
301+
0x00, 0x00, //Answer count
302+
0x00, 0x00, //Name server records
303+
0x00, 0x00 //Additional records
304+
};
305+
_conn->append(reinterpret_cast<const char*>(head), 12);
306+
307+
// Only supports sending one PTR query
308+
// Send the Name field (eg. "_http._tcp.local")
309+
_conn->append(reinterpret_cast<const char*>(&serviceNameLen), 1); // lenght of "_" + service
310+
_conn->append(reinterpret_cast<const char*>(serviceName), serviceNameLen); // "_" + service
311+
_conn->append(reinterpret_cast<const char*>(&protoNameLen), 1); // lenght of "_" + proto
312+
_conn->append(reinterpret_cast<const char*>(protoName), protoNameLen); // "_" + proto
313+
_conn->append(reinterpret_cast<const char*>(&localNameLen), 1); // lenght of "local"
314+
_conn->append(reinterpret_cast<const char*>(localName), localNameLen); // "local"
315+
_conn->append(reinterpret_cast<const char*>(&terminator), 1); // terminator
316+
317+
//Send the type and class
318+
uint8_t ptrAttrs[4] = {
319+
0x00, 0x0c, //PTR record query
320+
0x00, 0x01 //Class IN
321+
};
322+
_conn->append(reinterpret_cast<const char*>(ptrAttrs), 4);
323+
_waitingForAnswers = true;
324+
_conn->send();
325+
326+
#ifdef MDNS_DEBUG_TX
327+
Serial.println("Waiting for answers..");
328+
#endif
329+
delay(1000);
330+
331+
_waitingForAnswers = false;
332+
333+
return _getNumAnswers();
334+
}
335+
336+
String MDNSResponder::hostname(int idx) {
337+
MDNSAnswer *answer = _getAnswerFromIdx(idx);
338+
if (answer == 0) {
339+
return String();
340+
}
341+
return answer->hostname;
342+
}
343+
344+
IPAddress MDNSResponder::IP(int idx) {
345+
MDNSAnswer *answer = _getAnswerFromIdx(idx);
346+
if (answer == 0) {
347+
return IPAddress();
348+
}
349+
return IPAddress(answer->ip);
350+
}
351+
352+
uint16_t MDNSResponder::port(int idx) {
353+
MDNSAnswer *answer = _getAnswerFromIdx(idx);
354+
if (answer == 0) {
355+
return 0;
356+
}
357+
return answer->port;
358+
}
359+
360+
MDNSAnswer* MDNSResponder::_getAnswerFromIdx(int idx) {
361+
MDNSAnswer *answer = _answers;
362+
while (answer != 0 && idx-- > 0) {
363+
answer = answer->next;
364+
}
365+
if (idx > 0) {
366+
return 0;
367+
}
368+
return answer;
369+
}
370+
371+
int MDNSResponder::_getNumAnswers() {
372+
int numAnswers = 0;
373+
MDNSAnswer *answer = _answers;
374+
while (answer != 0) {
375+
numAnswers++;
376+
answer = answer->next;
377+
}
378+
return numAnswers;
379+
}
380+
224381
MDNSTxt * MDNSResponder::_getServiceTxt(char *name, char *proto){
225382
MDNSService* servicePtr;
226383
for (servicePtr = _services; servicePtr; servicePtr = servicePtr->_next) {
@@ -297,7 +454,176 @@ void MDNSResponder::_parsePacket(){
297454

298455
for(i=0; i<6; i++) packetHeader[i] = _conn_read16();
299456

300-
if((packetHeader[1] & 0x8000) != 0){ //not parsing responses yet
457+
if ((packetHeader[1] & 0x8000) != 0) { // Read answers
458+
#ifdef MDNS_DEBUG_RX
459+
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]);
460+
#endif
461+
462+
if (!_waitingForAnswers) {
463+
#ifdef MDNS_DEBUG_RX
464+
Serial.println("Not expecting any answers right now, returning");
465+
#endif
466+
_conn->flush();
467+
return;
468+
}
469+
470+
int numAnswers = packetHeader[3];
471+
// 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.
472+
if (numAnswers != 4) {
473+
#ifdef MDNS_DEBUG_RX
474+
Serial.println("Expected a packet with 4 answers, returning");
475+
#endif
476+
_conn->flush();
477+
return;
478+
}
479+
480+
uint8_t tmp8;
481+
uint16_t answerPort = 0;
482+
uint8_t answerIp[4] = { 0,0,0,0 };
483+
char answerHostName[255];
484+
bool serviceMatch = false;
485+
MDNSAnswer *answer;
486+
uint8_t partsCollected = 0;
487+
488+
// Clear answer list
489+
if (_newQuery) {
490+
int numAnswers = _getNumAnswers();
491+
for (int n = numAnswers - 1; n >= 0; n--) {
492+
answer = _getAnswerFromIdx(n);
493+
os_free(answer->hostname);
494+
os_free(answer);
495+
answer = 0;
496+
}
497+
_answers = 0;
498+
_newQuery = false;
499+
}
500+
501+
while (numAnswers--) {
502+
// Read name
503+
do {
504+
tmp8 = _conn_read8();
505+
if (tmp8 & 0xC0) { // Compressed pointer (not supported)
506+
tmp8 = _conn_read8();
507+
break;
508+
}
509+
if (tmp8 == 0x00) { // Énd of name
510+
break;
511+
}
512+
_conn_readS(serviceName, tmp8);
513+
serviceName[tmp8] = '\0';
514+
#ifdef MDNS_DEBUG_RX
515+
Serial.printf(" %d ", tmp8);
516+
for (int n = 0; n < tmp8; n++) {
517+
Serial.printf("%02x ", serviceName[n]);
518+
}
519+
Serial.println();
520+
#endif
521+
if (serviceName[0] == '_') {
522+
if (strcmp(&serviceName[1], _query->_service) == 0) {
523+
serviceMatch = true;
524+
#ifdef MDNS_DEBUG_RX
525+
Serial.printf("found matching service: %s\n", _query->_service);
526+
#endif
527+
}
528+
}
529+
} while (true);
530+
531+
uint16_t answerType = _conn_read16(); // Read type
532+
uint16_t answerClass = _conn_read16(); // Read class
533+
uint32_t answerTtl = _conn_read32(); // Read ttl
534+
uint16_t answerRdlength = _conn_read16(); // Read rdlength
535+
536+
#ifdef MDNS_DEBUG_RX
537+
Serial.printf("type: %04x rdlength: %d\n", answerType, answerRdlength);
538+
#endif
539+
540+
if (answerType == MDNS_TYPE_PTR) {
541+
partsCollected |= 0x01;
542+
_conn_readS(hostName, answerRdlength); // Read rdata
543+
#ifdef MDNS_DEBUG_RX
544+
for (int n = 0; n < answerRdlength; n++) {
545+
Serial.printf("%02x ", hostName[n]);
546+
}
547+
Serial.println();
548+
#endif
549+
}
550+
551+
if (answerType == MDNS_TYPE_TXT) {
552+
partsCollected |= 0x02;
553+
_conn_readS(hostName, answerRdlength); // Read rdata
554+
#ifdef MDNS_DEBUG_RX
555+
for (int n = 0; n < answerRdlength; n++) {
556+
Serial.printf("%02x ", hostName[n]);
557+
}
558+
Serial.println();
559+
#endif
560+
}
561+
562+
if (answerType == MDNS_TYPE_SRV) {
563+
partsCollected |= 0x04;
564+
uint16_t answerPrio = _conn_read16(); // Read priority
565+
uint16_t answerWeight = _conn_read16(); // Read weight
566+
answerPort = _conn_read16(); // Read port
567+
568+
// Read hostname
569+
tmp8 = _conn_read8();
570+
if (tmp8 & 0xC0) { // Compressed pointer (not supported)
571+
Serial.println("Skipping compressed pointer");
572+
tmp8 = _conn_read8();
573+
}
574+
else {
575+
_conn_readS(answerHostName, tmp8);
576+
answerHostName[tmp8] = '\0';
577+
#ifdef MDNS_DEBUG_RX
578+
Serial.printf(" %d ", tmp8);
579+
for (int n = 0; n < tmp8; n++) {
580+
Serial.printf("%02x ", answerHostName[n]);
581+
}
582+
Serial.printf("\n%s\n", answerHostName);
583+
#endif
584+
if (answerRdlength - (6 + 1 + tmp8) > 0) { // Skip any remaining rdata
585+
_conn_readS(hostName, answerRdlength - (6 + 1 + tmp8));
586+
}
587+
}
588+
}
589+
590+
if (answerType == MDNS_TYPE_A) {
591+
partsCollected |= 0x08;
592+
for (int i = 0; i < 4; i++) {
593+
answerIp[i] = _conn_read8();
594+
}
595+
}
596+
597+
if ((partsCollected == 0x0F) && serviceMatch) {
598+
#ifdef MDNS_DEBUG_RX
599+
Serial.println("All answers parsed, adding to _answers list..");
600+
#endif
601+
// Add new answer to answer list
602+
if (_answers == 0) {
603+
_answers = (struct MDNSAnswer*)(os_malloc(sizeof(struct MDNSAnswer)));
604+
answer = _answers;
605+
}
606+
else {
607+
answer = _answers;
608+
while (answer->next != 0) {
609+
answer = _answers->next;
610+
}
611+
answer->next = (struct MDNSAnswer*)(os_malloc(sizeof(struct MDNSAnswer)));
612+
answer = answer->next;
613+
}
614+
answer->next = 0;
615+
answer->hostname = 0;
616+
617+
// Populate new answer
618+
answer->port = answerPort;
619+
for (int i = 0; i < 4; i++) {
620+
answer->ip[i] = answerIp[i];
621+
}
622+
answer->hostname = (char *)os_malloc(strlen(answerHostName) + 1);
623+
os_strcpy(answer->hostname, answerHostName);
624+
}
625+
}
626+
301627
_conn->flush();
302628
return;
303629
}

0 commit comments

Comments
 (0)