@@ -5,6 +5,7 @@ Version 1.1
5
5
Copyright (c) 2013 Tony DiCola ([email protected] )
6
6
ESP8266 port (c) 2015 Ivan Grokhotkov ([email protected] )
7
7
MDNS-SD Suport 2015 Hristo Gochkov
8
+ Extended MDNS-SD support 2016 Lars Englund ([email protected] )
8
9
9
10
10
11
License (MIT license):
@@ -31,6 +32,7 @@ License (MIT license):
31
32
// Important RFC's for reference:
32
33
// - DNS request and response: http://www.ietf.org/rfc/rfc1035.txt
33
34
// - Multicast DNS: http://www.ietf.org/rfc/rfc6762.txt
35
+ // - MDNS-SD: https://tools.ietf.org/html/rfc6763
34
36
35
37
#define LWIP_OPEN_SRC
36
38
@@ -99,13 +101,44 @@ struct MDNSTxt{
99
101
String _txt;
100
102
};
101
103
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
+ };
102
115
103
116
104
117
MDNSResponder::MDNSResponder () : _conn(0 ) {
105
118
_services = 0 ;
106
119
_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 ;
107
141
}
108
- MDNSResponder::~MDNSResponder () {}
109
142
110
143
bool MDNSResponder::begin (const char * hostname){
111
144
// Open the MDNS socket if it isn't already open.
@@ -221,6 +254,130 @@ void MDNSResponder::addService(char *name, char *proto, uint16_t port){
221
254
222
255
}
223
256
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
+
224
381
MDNSTxt * MDNSResponder::_getServiceTxt (char *name, char *proto){
225
382
MDNSService* servicePtr;
226
383
for (servicePtr = _services; servicePtr; servicePtr = servicePtr->_next ) {
@@ -297,7 +454,176 @@ void MDNSResponder::_parsePacket(){
297
454
298
455
for (i=0 ; i<6 ; i++) packetHeader[i] = _conn_read16 ();
299
456
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
+
301
627
_conn->flush ();
302
628
return ;
303
629
}
0 commit comments