Skip to content

Commit 20d8ce1

Browse files
committed
dns: add support for legacy unicast responses
* do not ignore legacy unicast queries, i.e., respond to them * comply with RFC 6762 Section 6.7 and repeat the query ID and the question in the response * for legacy unicast queries, aggregate the answers in one response packet instead of sending multiple packets
1 parent 2b28094 commit 20d8ce1

6 files changed

Lines changed: 131 additions & 51 deletions

File tree

announce.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,9 @@ announce_timer(struct uloop_timeout *timeout)
6565
/* Fall through */
6666

6767
case STATE_ANNOUNCE:
68-
dns_reply_a(iface, NULL, announce_ttl, NULL);
69-
dns_reply_a_additional(iface, NULL, announce_ttl);
70-
service_announce_services(iface, NULL, announce_ttl);
68+
dns_reply_a(iface, NULL, announce_ttl, NULL, false);
69+
dns_reply_a_additional(iface, NULL, announce_ttl, false);
70+
service_announce_services(iface, NULL, announce_ttl, false);
7171
uloop_timeout_set(timeout, announce_ttl * 800);
7272
break;
7373
}

dns.c

Lines changed: 100 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,9 @@ void dns_packet_answer(const char *name, int type, const uint8_t *rdata, uint16_
157157
pkt.h.flags |= cpu_to_be16(0x8400);
158158

159159
a = dns_packet_record_add(sizeof(*a) + rdlength, name);
160+
if (!a)
161+
return;
162+
160163
memset(a, 0, sizeof(*a));
161164
a->type = cpu_to_be16(type);
162165
a->class = cpu_to_be16(1);
@@ -266,7 +269,7 @@ void dns_query(const char *name, uint16_t type)
266269
}
267270

268271
void
269-
dns_reply_a(struct interface *iface, struct sockaddr *to, int ttl, const char *hostname)
272+
dns_reply_a(struct interface *iface, struct sockaddr *to, int ttl, const char *hostname, bool append)
270273
{
271274
struct ifaddrs *ifap, *ifa;
272275
struct sockaddr_in *sa;
@@ -277,7 +280,9 @@ dns_reply_a(struct interface *iface, struct sockaddr *to, int ttl, const char *h
277280

278281
getifaddrs(&ifap);
279282

280-
dns_packet_init();
283+
if (!append)
284+
dns_packet_init();
285+
281286
for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
282287
if (strcmp(ifa->ifa_name, iface->name))
283288
continue;
@@ -292,16 +297,17 @@ dns_reply_a(struct interface *iface, struct sockaddr *to, int ttl, const char *h
292297
}
293298
freeifaddrs(ifap);
294299

295-
dns_packet_send(iface, to, 0, 0);
300+
if(!append)
301+
dns_packet_send(iface, to, 0, 0);
296302
}
297303

298304
void
299-
dns_reply_a_additional(struct interface *iface, struct sockaddr *to, int ttl)
305+
dns_reply_a_additional(struct interface *iface, struct sockaddr *to, int ttl, bool append)
300306
{
301307
struct hostname *h;
302308

303309
vlist_for_each_element(&hostnames, h, node)
304-
dns_reply_a(iface, to, ttl, h->hostname);
310+
dns_reply_a(iface, to, ttl, h->hostname, append);
305311
}
306312

307313
static int
@@ -484,7 +490,8 @@ match_ip_addresses(char *reverse_ip, char *intf_ip)
484490
}
485491

486492
static void
487-
dns_reply_reverse_ip6_mapping(struct interface *iface, struct sockaddr *to, int ttl, char *name, char *reverse_ip)
493+
dns_reply_reverse_ip6_mapping(struct interface *iface, struct sockaddr *to, int ttl, char *name, char *reverse_ip,
494+
bool append)
488495
{
489496
struct ifaddrs *ifap, *ifa;
490497
struct sockaddr_in6 *sa6;
@@ -494,7 +501,10 @@ dns_reply_reverse_ip6_mapping(struct interface *iface, struct sockaddr *to, int
494501
int len;
495502

496503
getifaddrs(&ifap);
497-
dns_packet_init();
504+
505+
if (!append)
506+
dns_packet_init();
507+
498508
for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
499509
if (strcmp(ifa->ifa_name, iface->name))
500510
continue;
@@ -513,13 +523,16 @@ dns_reply_reverse_ip6_mapping(struct interface *iface, struct sockaddr *to, int
513523
}
514524
}
515525
}
516-
dns_packet_send(iface, to, 0, 0);
526+
527+
if (!append)
528+
dns_packet_send(iface, to, 0, 0);
517529

518530
freeifaddrs(ifap);
519531
}
520532

521533
static void
522-
dns_reply_reverse_ip4_mapping(struct interface *iface, struct sockaddr *to, int ttl, char *name, char *reverse_ip)
534+
dns_reply_reverse_ip4_mapping(struct interface *iface, struct sockaddr *to, int ttl, char *name, char *reverse_ip,
535+
bool append)
523536
{
524537
struct ifaddrs *ifap, *ifa;
525538
struct sockaddr_in *sa;
@@ -529,7 +542,10 @@ dns_reply_reverse_ip4_mapping(struct interface *iface, struct sockaddr *to, int
529542
int len;
530543

531544
getifaddrs(&ifap);
532-
dns_packet_init();
545+
546+
if (!append)
547+
dns_packet_init();
548+
533549
for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
534550
if (strcmp(ifa->ifa_name, iface->name))
535551
continue;
@@ -548,7 +564,8 @@ dns_reply_reverse_ip4_mapping(struct interface *iface, struct sockaddr *to, int
548564
}
549565
}
550566
}
551-
dns_packet_send(iface, to, 0, 0);
567+
if (!append)
568+
dns_packet_send(iface, to, 0, 0);
552569

553570
freeifaddrs(ifap);
554571
}
@@ -572,7 +589,7 @@ is_reverse_dns_query(const char *name, const char *suffix)
572589
}
573590

574591
static void
575-
parse_question(struct interface *iface, struct sockaddr *from, char *name, struct dns_question *q)
592+
parse_question(struct interface *iface, struct sockaddr *from, char *name, struct dns_question *q, bool append)
576593
{
577594
int is_unicast = (q->class & CLASS_UNICAST) != 0;
578595
struct sockaddr *to = NULL;
@@ -581,19 +598,22 @@ parse_question(struct interface *iface, struct sockaddr *from, char *name, struc
581598

582599
/* TODO: Multicast if more than one quarter of TTL has passed */
583600
if (is_unicast) {
584-
to = from;
585-
if (interface_multicast(iface))
586-
iface = interface_get(iface->name, iface->type | SOCKTYPE_BIT_UNICAST);
601+
/* if append is true we have already done this */
602+
if (!append) {
603+
to = from;
604+
if (interface_multicast(iface))
605+
iface = interface_get(iface->name, iface->type | SOCKTYPE_BIT_UNICAST);
606+
}
587607
}
588608

589609
DBG(1, "Q -> %s %s\n", dns_type_string(q->type), name);
590610

591611
switch (q->type) {
592612
case TYPE_ANY:
593613
if (!strcasecmp(name, mdns_hostname_local)) {
594-
dns_reply_a(iface, to, announce_ttl, NULL);
595-
dns_reply_a_additional(iface, to, announce_ttl);
596-
service_reply(iface, to, NULL, NULL, announce_ttl, is_unicast);
614+
dns_reply_a(iface, to, announce_ttl, NULL, append);
615+
dns_reply_a_additional(iface, to, announce_ttl, append);
616+
service_reply(iface, to, NULL, NULL, announce_ttl, is_unicast, append);
597617
}
598618
break;
599619

@@ -603,7 +623,7 @@ parse_question(struct interface *iface, struct sockaddr *from, char *name, struc
603623
char name_buf[256];
604624
strcpy(name_buf, name);
605625
*host = '\0';
606-
dns_reply_reverse_ip4_mapping(iface, to, announce_ttl, name_buf, name);
626+
dns_reply_reverse_ip4_mapping(iface, to, announce_ttl, name_buf, name, append);
607627
break;
608628
}
609629

@@ -612,22 +632,22 @@ parse_question(struct interface *iface, struct sockaddr *from, char *name, struc
612632
char name_buf6[256];
613633
strcpy(name_buf6, name);
614634
*host6 = '\0';
615-
dns_reply_reverse_ip6_mapping(iface, to, announce_ttl, name_buf6, name);
635+
dns_reply_reverse_ip6_mapping(iface, to, announce_ttl, name_buf6, name, append);
616636
break;
617637
}
618638

619639
if (!strcasecmp(name, C_DNS_SD)) {
620-
service_announce_services(iface, to, announce_ttl);
640+
service_announce_services(iface, to, announce_ttl, append);
621641
} else {
622642
if (name[0] == '_') {
623-
service_reply(iface, to, NULL, name, announce_ttl, is_unicast);
643+
service_reply(iface, to, NULL, name, announce_ttl, is_unicast, append);
624644
} else {
625645
/* First dot separates instance name from the rest */
626646
char *dot = strchr(name, '.');
627647

628648
if (dot) {
629649
*dot = '\0';
630-
service_reply(iface, to, name, dot + 1, announce_ttl, is_unicast);
650+
service_reply(iface, to, name, dot + 1, announce_ttl, is_unicast, append);
631651
*dot = '.';
632652
}
633653
}
@@ -640,34 +660,81 @@ parse_question(struct interface *iface, struct sockaddr *from, char *name, struc
640660
if (host)
641661
*host = '\0';
642662
if (!strcasecmp(umdns_host_label, name)) {
643-
dns_reply_a(iface, to, announce_ttl, NULL);
663+
dns_reply_a(iface, to, announce_ttl, NULL, append);
644664
} else {
645665
if (host)
646666
*host = '.';
647667
vlist_for_each_element(&hostnames, h, node)
648668
if (!strcasecmp(h->hostname, name))
649-
dns_reply_a(iface, to, announce_ttl, h->hostname);
669+
dns_reply_a(iface, to, announce_ttl, h->hostname, append);
650670
}
651671
break;
652672
};
653673
}
654674

675+
static void
676+
dns_append_questions(uint8_t *orig_buffer, int orig_len)
677+
{
678+
/* Construct original question section */
679+
const struct dns_header *orig_h;
680+
uint8_t *ptr = orig_buffer;
681+
int len = orig_len;
682+
683+
orig_h = dns_consume_header(&ptr, &len);
684+
if (orig_h) {
685+
pkt.h.id = cpu_to_be16(orig_h->id);
686+
687+
uint16_t q_count = be16_to_cpu(orig_h->questions);
688+
while (q_count-- > 0 && len > 0) {
689+
char *qname = dns_consume_name(orig_buffer, orig_len, &ptr, &len);
690+
if (!qname || len < (int)sizeof(struct dns_question))
691+
break;
692+
693+
struct dns_question *q = dns_consume_question(&ptr, &len);
694+
if (!q)
695+
break;
696+
697+
dns_packet_question(qname, q->type);
698+
}
699+
}
700+
}
701+
655702
void
656703
dns_handle_packet(struct interface *iface, struct sockaddr *from, uint16_t port, uint8_t *buffer, int len)
657704
{
658705
struct dns_header *h;
659706
uint8_t *b = buffer;
660707
int rlen = len;
708+
uint8_t orig_buffer[len];
709+
struct sockaddr *to = NULL;
710+
bool append = false;
711+
712+
/* make a copy of the original buffer since it might be needed to construct the answer
713+
* in case the query is received from a one-shot multicast dns querier */
714+
memcpy(orig_buffer, buffer, len);
661715

662716
h = dns_consume_header(&b, &rlen);
663717
if (!h) {
664718
fprintf(stderr, "dropping: bad header\n");
665719
return;
666720
}
667721

668-
if (h->questions && !interface_multicast(iface) && port != MCAST_PORT)
669-
/* silently drop unicast questions that dont originate from port 5353 */
670-
return;
722+
/* legacy querier */
723+
if (port != MCAST_PORT) {
724+
/* aggregate answers and send, instead of sending separately */
725+
append = true;
726+
727+
/* packet construction starts here */
728+
dns_packet_init();
729+
730+
/* add original questions, as required by unicast DNS RFC */
731+
dns_append_questions(orig_buffer, len);
732+
733+
/* to return a unicast response */
734+
to = from;
735+
if (interface_multicast(iface))
736+
iface = interface_get(iface->name, iface->type | SOCKTYPE_BIT_UNICAST);
737+
}
671738

672739
while (h->questions-- > 0) {
673740
char *name = dns_consume_name(buffer, len, &b, &rlen);
@@ -685,9 +752,13 @@ dns_handle_packet(struct interface *iface, struct sockaddr *from, uint16_t port,
685752
}
686753

687754
if (!(h->flags & FLAG_RESPONSE))
688-
parse_question(iface, from, name, q);
755+
parse_question(iface, from, name, q, append);
689756
}
690757

758+
/* if append is true, then answers have only been appended to the packet, not sent, so we do that here */
759+
if (append && pkt.h.answers > 0)
760+
dns_packet_send(iface, to, 0, 0);
761+
691762
if (!(h->flags & FLAG_RESPONSE))
692763
return;
693764

dns.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,8 @@ void dns_query(const char *name, uint16_t type);
8282

8383
void dns_send_question(struct interface *iface, struct sockaddr *to,
8484
const char *question, int type, int multicast);
85-
void dns_reply_a(struct interface *iface, struct sockaddr *to, int ttl, const char *hostname);
86-
void dns_reply_a_additional(struct interface *iface, struct sockaddr *to, int ttl);
85+
void dns_reply_a(struct interface *iface, struct sockaddr *to, int ttl, const char *hostname, bool append);
86+
void dns_reply_a_additional(struct interface *iface, struct sockaddr *to, int ttl, bool append);
8787
const char* dns_type_string(uint16_t type);
8888
void dns_handle_packet(struct interface *iface, struct sockaddr *s, uint16_t port, uint8_t *buf, int len);
8989

interface.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -666,9 +666,9 @@ void interface_shutdown(void)
666666

667667
vlist_for_each_element(&interfaces, iface, node)
668668
if (interface_multicast(iface)) {
669-
dns_reply_a(iface, NULL, 0, NULL);
670-
dns_reply_a_additional(iface, NULL, 0);
671-
service_announce_services(iface, NULL, 0);
669+
dns_reply_a(iface, NULL, 0, NULL, false);
670+
dns_reply_a_additional(iface, NULL, 0, false);
671+
service_announce_services(iface, NULL, 0, false);
672672
}
673673

674674
for (size_t i = 0; i < ARRAY_SIZE(ufd); i++) {

0 commit comments

Comments
 (0)