@@ -43,6 +43,13 @@ static char name_buffer[MAX_NAME_LEN + 1];
4343static char dns_buffer [MAX_NAME_LEN ];
4444static struct blob_buf ans_buf ;
4545
46+ static struct dns_header *
47+ dns_consume_header (uint8_t * * data , int * len );
48+ static char *
49+ dns_consume_name (const uint8_t * base , int blen , uint8_t * * data , int * len );
50+ static struct dns_question *
51+ dns_consume_question (uint8_t * * data , int * len );
52+
4653const char *
4754dns_type_string (uint16_t type )
4855{
@@ -139,27 +146,51 @@ dns_add_answer(int type, const uint8_t *rdata, uint16_t rdlength, int ttl)
139146}
140147
141148void
142- dns_send_answer (struct interface * iface , struct sockaddr * to , const char * answer )
149+ dns_send_answer (struct interface * iface , struct sockaddr * to , const char * answer , uint8_t * orig_buffer , int orig_len )
143150{
144151 uint8_t buffer [256 ];
145152 struct blob_attr * attr ;
146153 struct dns_header h = { 0 };
147154 struct iovec * iov ;
148155 int answer_len , rem ;
149156 int n_iov = 0 ;
157+ uint16_t q_cnt = 0 ;
158+ struct dns_header * orig_h ;
159+ uint8_t * b = orig_buffer ;
160+ int rlen = orig_len ;
150161
151162 if (!dns_answer_cnt )
152163 return ;
153164
165+ if (orig_buffer != NULL ) {
166+ orig_h = dns_consume_header (& b , & rlen );
167+ if (!orig_h ) {
168+ fprintf (stderr , "dropping: bad header\n" );
169+ return ;
170+ }
171+
172+ q_cnt = orig_h -> questions ;
173+ h .questions = cpu_to_be16 (orig_h -> questions );
174+ h .id = cpu_to_be16 (orig_h -> id );
175+ }
154176 h .answers = cpu_to_be16 (dns_answer_cnt );
155177 h .flags = cpu_to_be16 (0x8400 );
156178
157- iov = alloca (sizeof (struct iovec ) * ((dns_answer_cnt * 2 ) + 1 ));
179+ iov = alloca (sizeof (struct iovec ) * ((dns_answer_cnt * 2 ) + 1 + q_cnt ));
158180
159181 iov [n_iov ].iov_base = & h ;
160182 iov [n_iov ].iov_len = sizeof (struct dns_header );
161183 n_iov ++ ;
162184
185+ /* if the answer is in reply to a question, the copy the question in answer
186+ * so that the dns format is correct, as per section 6.7 of rfc 6762 */
187+ if (orig_buffer != NULL ) {
188+ /* after consuming the header above, b now points to query section */
189+ iov [n_iov ].iov_base = b ;
190+ iov [n_iov ].iov_len = rlen ;
191+ n_iov ++ ;
192+ }
193+
163194 answer_len = dn_comp (answer , buffer , sizeof (buffer ), NULL , NULL );
164195 if (answer_len < 1 )
165196 return ;
@@ -178,12 +209,13 @@ dns_send_answer(struct interface *iface, struct sockaddr *to, const char *answer
178209 DBG (1 , "A <- %s %s\n" , dns_type_string (be16_to_cpu (a -> type )), answer );
179210 }
180211
212+
181213 if (interface_send_packet (iface , to , iov , n_iov ) < 0 )
182214 perror ("failed to send answer" );
183215}
184216
185217void
186- dns_reply_a (struct interface * iface , struct sockaddr * to , int ttl , const char * hostname )
218+ dns_reply_a (struct interface * iface , struct sockaddr * to , int ttl , const char * hostname , uint8_t * orig_buffer , int orig_len )
187219{
188220 struct ifaddrs * ifap , * ifa ;
189221 struct sockaddr_in * sa ;
@@ -204,7 +236,7 @@ dns_reply_a(struct interface *iface, struct sockaddr *to, int ttl, const char *h
204236 dns_add_answer (TYPE_AAAA , (uint8_t * ) & sa6 -> sin6_addr , 16 , ttl );
205237 }
206238 }
207- dns_send_answer (iface , to , hostname ? hostname : mdns_hostname_local );
239+ dns_send_answer (iface , to , hostname ? hostname : mdns_hostname_local , orig_buffer , orig_len );
208240
209241 freeifaddrs (ifap );
210242}
@@ -215,7 +247,7 @@ dns_reply_a_additional(struct interface *iface, struct sockaddr *to, int ttl)
215247 struct hostname * h ;
216248
217249 vlist_for_each_element (& hostnames , h , node )
218- dns_reply_a (iface , to , ttl , h -> hostname );
250+ dns_reply_a (iface , to , ttl , h -> hostname , NULL , 0 );
219251}
220252
221253static int
@@ -355,44 +387,54 @@ static int parse_answer(struct interface *iface, struct sockaddr *from,
355387}
356388
357389static void
358- parse_question (struct interface * iface , struct sockaddr * from , char * name , struct dns_question * q )
390+ parse_question (struct interface * iface , struct sockaddr * from , char * name , struct dns_question * q ,
391+ uint8_t * orig_buffer , int orig_len , uint16_t port )
359392{
360393 struct sockaddr * to = NULL ;
361394 char * host ;
362395
363396 /* TODO: Multicast if more than one quarter of TTL has passed */
364- if (q -> class & CLASS_UNICAST ) {
397+ if ((q -> class & CLASS_UNICAST ) || port != MCAST_PORT ) {
398+ /* As per rfc 6762 section 6.7, if source port is not multicast port,
399+ * then the response should be unicast udp to the source port */
365400 to = from ;
366401 if (interface_multicast (iface ))
367402 iface = interface_get (iface -> name , iface -> type | SOCKTYPE_BIT_UNICAST );
403+ } else {
404+ /* if the query is from multicast port, no need for original buffer
405+ * while responding as per rfc 6762, section 6:
406+ * Multicast DNS responses MUST NOT contain any questions in the
407+ * Question Section. */
408+ orig_buffer = NULL ;
409+ orig_len = 0 ;
368410 }
369411
370412 DBG (1 , "Q -> %s %s\n" , dns_type_string (q -> type ), name );
371413
372414 switch (q -> type ) {
373415 case TYPE_ANY :
374416 if (!strcmp (name , mdns_hostname_local )) {
375- dns_reply_a (iface , to , announce_ttl , NULL );
417+ dns_reply_a (iface , to , announce_ttl , NULL , orig_buffer , orig_len );
376418 dns_reply_a_additional (iface , to , announce_ttl );
377- service_reply (iface , to , NULL , NULL , announce_ttl );
419+ service_reply (iface , to , NULL , NULL , announce_ttl , orig_buffer , orig_len );
378420 }
379421 break ;
380422
381423 case TYPE_PTR :
382424 if (!strcmp (name , C_DNS_SD )) {
383- dns_reply_a (iface , to , announce_ttl , NULL );
425+ dns_reply_a (iface , to , announce_ttl , NULL , orig_buffer , orig_len );
384426 dns_reply_a_additional (iface , to , announce_ttl );
385- service_announce_services (iface , to , announce_ttl );
427+ service_announce_services (iface , to , announce_ttl , orig_buffer , orig_len );
386428 } else {
387429 if (name [0 ] == '_' ) {
388- service_reply (iface , to , NULL , name , announce_ttl );
430+ service_reply (iface , to , NULL , name , announce_ttl , orig_buffer , orig_len );
389431 } else {
390432 /* First dot separates instance name from the rest */
391433 char * dot = strchr (name , '.' );
392434
393435 if (dot ) {
394436 * dot = '\0' ;
395- service_reply (iface , to , name , dot + 1 , announce_ttl );
437+ service_reply (iface , to , name , dot + 1 , announce_ttl , orig_buffer , orig_len );
396438 * dot = '.' ;
397439 }
398440 }
@@ -405,7 +447,7 @@ parse_question(struct interface *iface, struct sockaddr *from, char *name, struc
405447 if (host )
406448 * host = '\0' ;
407449 if (!strcmp (umdns_host_label , name ))
408- dns_reply_a (iface , to , announce_ttl , NULL );
450+ dns_reply_a (iface , to , announce_ttl , NULL , orig_buffer , orig_len );
409451 break ;
410452 };
411453}
@@ -416,17 +458,18 @@ dns_handle_packet(struct interface *iface, struct sockaddr *from, uint16_t port,
416458 struct dns_header * h ;
417459 uint8_t * b = buffer ;
418460 int rlen = len ;
461+ uint8_t orig_buffer [len ];
462+
463+ /* make a copy of the original buffer since it might be needed to construct the answer
464+ * in case the query is received from a one-shot multicast dns querier */
465+ memcpy (orig_buffer , buffer , len );
419466
420467 h = dns_consume_header (& b , & rlen );
421468 if (!h ) {
422469 fprintf (stderr , "dropping: bad header\n" );
423470 return ;
424471 }
425472
426- if (h -> questions && !interface_multicast (iface ) && port != MCAST_PORT )
427- /* silently drop unicast questions that dont originate from port 5353 */
428- return ;
429-
430473 while (h -> questions -- > 0 ) {
431474 char * name = dns_consume_name (buffer , len , & b , & rlen );
432475 struct dns_question * q ;
@@ -443,7 +486,7 @@ dns_handle_packet(struct interface *iface, struct sockaddr *from, uint16_t port,
443486 }
444487
445488 if (!(h -> flags & FLAG_RESPONSE ))
446- parse_question (iface , from , name , q );
489+ parse_question (iface , from , name , q , orig_buffer , len , port );
447490 }
448491
449492 if (!(h -> flags & FLAG_RESPONSE ))
0 commit comments