@@ -5,6 +5,7 @@ Version 1.1
55Copyright (c) 2013 Tony DiCola (tony@tonydicola.com)
66ESP8266 port (c) 2015 Ivan Grokhotkov (ivan@esp8266.com)
77MDNS-SD Suport 2015 Hristo Gochkov
8+ Extended MDNS-SD support 2016 Lars Englund (lars.englund@gmail.com)
89
910
1011License (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
104117MDNSResponder::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
110143bool 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+
224381MDNSTxt * 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