@@ -100,6 +100,7 @@ bool mg_dns_parse(const uint8_t *buf, size_t len, struct mg_dns_message *dm) {
100100 const struct mg_dns_header * h = (struct mg_dns_header * ) buf ;
101101 struct mg_dns_rr rr ;
102102 size_t i , n , num_answers , ofs = sizeof (* h );
103+ bool is_response ;
103104 memset (dm , 0 , sizeof (* dm ));
104105
105106 if (len < sizeof (* h )) return 0 ; // Too small, headers dont fit
@@ -110,12 +111,22 @@ bool mg_dns_parse(const uint8_t *buf, size_t len, struct mg_dns_message *dm) {
110111 num_answers = 10 ; // Sanity cap
111112 }
112113 dm -> txnid = mg_ntohs (h -> txnid );
114+ is_response = mg_ntohs (h -> flags ) & 0x8000 ;
113115
114116 for (i = 0 ; i < mg_ntohs (h -> num_questions ); i ++ ) {
115117 if ((n = mg_dns_parse_rr (buf , len , ofs , true, & rr )) == 0 ) return false;
116118 // MG_INFO(("Q %lu %lu %hu/%hu", ofs, n, rr.atype, rr.aclass));
119+ mg_dns_parse_name (buf , len , ofs , dm -> name , sizeof (dm -> name ));
117120 ofs += n ;
118121 }
122+
123+ if (!is_response ) {
124+ // For queries, there is no need to parse the answers. In this way,
125+ // we also ensure the domain name (dm->name) is parsed from
126+ // the question field.
127+ return true;
128+ }
129+
119130 for (i = 0 ; i < num_answers ; i ++ ) {
120131 if ((n = mg_dns_parse_rr (buf , len , ofs , false, & rr )) == 0 ) return false;
121132 // MG_INFO(("A -- %lu %lu %hu/%hu %s", ofs, n, rr.atype, rr.aclass,
@@ -265,3 +276,72 @@ void mg_resolve(struct mg_connection *c, const char *url) {
265276 mg_sendnsreq (c , & host , c -> mgr -> dnstimeout , dns , c -> mgr -> use_dns6 );
266277 }
267278}
279+
280+ static const uint8_t mdns_answer [] = {
281+ 0 , 1 , // 2 bytes - record type, A
282+ 0 , 1 , // 2 bytes - address class, INET
283+ 0 , 0 , 0 , 120 , // 4 bytes - TTL
284+ 0 , 4 // 2 bytes - address length
285+ };
286+
287+ static void mdns_cb (struct mg_connection * c , int ev , void * ev_data ) {
288+ if (ev == MG_EV_READ ) {
289+ struct mg_dns_header * qh = (struct mg_dns_header * ) c -> recv .buf ;
290+ if (c -> recv .len > 12 && (qh -> flags & mg_htons (0xF800 )) == 0 ) {
291+ // flags -> !resp, opcode=0 => query; ignore other opcodes and responses
292+ struct mg_dns_rr rr ; // Parse first question, offset 12 is header size
293+ size_t n = mg_dns_parse_rr (c -> recv .buf , c -> recv .len , 12 , true, & rr );
294+ MG_VERBOSE (("mDNS request parsed, result=%d" , (int ) n ));
295+ if (n > 0 ) {
296+ // RFC-6762 Appendix C, RFC2181 11: m(n + 1-63), max 255 + 0x0
297+ // buf and h declared here to ease future expansion to DNS-SD
298+ uint8_t buf [sizeof (struct mg_dns_header ) + 256 + sizeof (mdns_answer ) + 4 ];
299+ struct mg_dns_header * h = (struct mg_dns_header * ) buf ;
300+ char local_name [63 + 7 ]; // name label + '.' + local label + '\0'
301+ uint8_t name_len = (uint8_t ) strlen ((char * )c -> fn_data );
302+ struct mg_dns_message dm ;
303+ bool unicast = (rr .aclass & MG_BIT (15 )) != 0 ; // QU
304+ // uint16_t q = mg_ntohs(qh->num_questions);
305+ rr .aclass &= (uint16_t ) ~MG_BIT (15 ); // remove "QU" (unicast response)
306+ qh -> num_questions = mg_htons (1 ); // parser sanity
307+ mg_dns_parse (c -> recv .buf , c -> recv .len , & dm );
308+ if (name_len > (sizeof (local_name ) - 7 )) // leave room for .local\0
309+ name_len = sizeof (local_name ) - 7 ;
310+ memcpy (local_name , c -> fn_data , name_len );
311+ strcpy (local_name + name_len , ".local" ); // ensure proper name.local\0
312+ if (strcmp (local_name , dm .name ) == 0 ) {
313+ uint8_t * p = & buf [sizeof (* h )];
314+ memset (h , 0 , sizeof (* h )); // clear header
315+ h -> txnid = unicast ? qh -> txnid : 0 ; // RFC-6762 18.1
316+ // RFC-6762 6: 0 questions, 1 Answer, 0 Auth, 0 Additional RRs
317+ h -> num_answers = mg_htons (1 ); // only one answer
318+ h -> flags = mg_htons (0x8400 ); // Authoritative response
319+ * p ++ = name_len ; // label 1
320+ memcpy (p , c -> fn_data , name_len ), p += name_len ;
321+ * p ++ = 5 ; // label 2
322+ memcpy (p , "local" , 5 ), p += 5 ;
323+ * p ++ = 0 ; // no more labels
324+ memcpy (p , mdns_answer , sizeof (mdns_answer )), p += sizeof (mdns_answer );
325+ #if MG_ENABLE_TCPIP
326+ memcpy (p , & c -> mgr -> ifp -> ip , 4 ), p += 4 ;
327+ #else
328+ memcpy (p , c -> data , 4 ), p += 4 ;
329+ #endif
330+ if (!unicast ) memcpy (& c -> rem , & c -> loc , sizeof (c -> rem ));
331+ mg_send (c , buf , (size_t )(p - buf )); // And send it!
332+ MG_DEBUG (("mDNS %c response sent" , unicast ? 'U' : 'M' ));
333+ }
334+ }
335+ }
336+ mg_iobuf_del (& c -> recv , 0 , c -> recv .len );
337+ }
338+ (void ) ev_data ;
339+ }
340+
341+ void mg_multicast_add (struct mg_connection * c , char * ip );
342+ struct mg_connection * mg_mdns_listen (struct mg_mgr * mgr , char * name ) {
343+ struct mg_connection * c =
344+ mg_listen (mgr , "udp://224.0.0.251:5353" , mdns_cb , name );
345+ if (c != NULL ) mg_multicast_add (c , (char * )"224.0.0.251" );
346+ return c ;
347+ }
0 commit comments