Skip to content

Commit bb45f8d

Browse files
committed
mdnsd: add handling for legacy queries
Following are taken care of: - in the reply to a legacy query, send unicast udp to same port from which the packet is received. - copy the query from question message in reply. - use the same transaction id as in question.
1 parent e91ed40 commit bb45f8d

6 files changed

Lines changed: 86 additions & 39 deletions

File tree

announce.c

Lines changed: 2 additions & 2 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);
68+
dns_reply_a(iface, NULL, announce_ttl, NULL, NULL, 0);
6969
dns_reply_a_additional(iface, NULL, announce_ttl);
70-
service_announce_services(iface, NULL, announce_ttl);
70+
service_announce_services(iface, NULL, announce_ttl, NULL, 0);
7171
uloop_timeout_set(timeout, announce_ttl * 800);
7272
break;
7373
}

dns.c

Lines changed: 62 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,13 @@ static char name_buffer[MAX_NAME_LEN + 1];
4343
static char dns_buffer[MAX_NAME_LEN];
4444
static 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+
4653
const char*
4754
dns_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

141148
void
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

185217
void
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

221253
static int
@@ -355,44 +387,54 @@ static int parse_answer(struct interface *iface, struct sockaddr *from,
355387
}
356388

357389
static 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))

dns.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,8 @@ void dns_send_question(struct interface *iface, struct sockaddr *to,
7777
const char *question, int type, int multicast);
7878
void dns_init_answer(void);
7979
void dns_add_answer(int type, const uint8_t *rdata, uint16_t rdlength, int ttl);
80-
void dns_send_answer(struct interface *iface, struct sockaddr *to, const char *answer);
81-
void dns_reply_a(struct interface *iface, struct sockaddr *to, int ttl, const char *hostname);
80+
void dns_send_answer(struct interface *iface, struct sockaddr *to, const char *answer, uint8_t *orig_buffer, int orig_len);
81+
void dns_reply_a(struct interface *iface, struct sockaddr *to, int ttl, const char *hostname, uint8_t *orig_buffer, int orig_len);
8282
void dns_reply_a_additional(struct interface *iface, struct sockaddr *to, int ttl);
8383
const char* dns_type_string(uint16_t type);
8484
void dns_handle_packet(struct interface *iface, struct sockaddr *s, uint16_t port, uint8_t *buf, int len);

interface.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ interface_send_packet4(struct interface *iface, struct sockaddr_in *to, struct i
8383
if (to)
8484
fprintf(stderr, "Ignoring IPv4 address for multicast interface\n");
8585
} else {
86+
a.sin_port = to->sin_port;
8687
a.sin_addr.s_addr = to->sin_addr.s_addr;
8788
}
8889

@@ -652,9 +653,9 @@ void interface_shutdown(void)
652653

653654
vlist_for_each_element(&interfaces, iface, node)
654655
if (interface_multicast(iface)) {
655-
dns_reply_a(iface, NULL, 0, NULL);
656+
dns_reply_a(iface, NULL, 0, NULL, NULL, 0);
656657
dns_reply_a_additional(iface, NULL, 0);
657-
service_announce_services(iface, NULL, 0);
658+
service_announce_services(iface, NULL, 0, NULL, 0);
658659
}
659660

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

service.c

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,8 @@ service_timeout(struct service *s)
138138
}
139139

140140
static void
141-
service_reply_single(struct interface *iface, struct sockaddr *to, struct service *s, int ttl, int force)
141+
service_reply_single(struct interface *iface, struct sockaddr *to, struct service *s, int ttl, int force,
142+
uint8_t *orig_buffer, int orig_len)
142143
{
143144
const char *host = service_instance_name(s);
144145
char *service = strstr(host, "._");
@@ -154,17 +155,18 @@ service_reply_single(struct interface *iface, struct sockaddr *to, struct servic
154155

155156
dns_init_answer();
156157
service_add_ptr(service_instance_name(s), ttl);
157-
dns_send_answer(iface, to, service);
158+
dns_send_answer(iface, to, service, orig_buffer, orig_len);
158159

159160
dns_init_answer();
160161
service_add_srv(s, ttl);
161162
if (s->txt && s->txt_len)
162163
dns_add_answer(TYPE_TXT, (uint8_t *) s->txt, s->txt_len, ttl);
163-
dns_send_answer(iface, to, host);
164+
dns_send_answer(iface, to, host, orig_buffer, orig_len);
164165
}
165166

166167
void
167-
service_reply(struct interface *iface, struct sockaddr *to, const char *instance, const char *service_domain, int ttl)
168+
service_reply(struct interface *iface, struct sockaddr *to, const char *instance, const char *service_domain, int ttl,
169+
uint8_t *orig_buffer, int orig_len)
168170
{
169171
struct service *s;
170172

@@ -173,12 +175,12 @@ service_reply(struct interface *iface, struct sockaddr *to, const char *instance
173175
continue;
174176
if (service_domain && strcmp(s->service, service_domain))
175177
continue;
176-
service_reply_single(iface, to, s, ttl, 0);
178+
service_reply_single(iface, to, s, ttl, 0, orig_buffer, orig_len);
177179
}
178180
}
179181

180182
void
181-
service_announce_services(struct interface *iface, struct sockaddr *to, int ttl)
183+
service_announce_services(struct interface *iface, struct sockaddr *to, int ttl, uint8_t *orig_buffer, int orig_len)
182184
{
183185
struct service *s;
184186

@@ -187,9 +189,9 @@ service_announce_services(struct interface *iface, struct sockaddr *to, int ttl)
187189
if (ttl) {
188190
dns_init_answer();
189191
service_add_ptr(s->service, ttl);
190-
dns_send_answer(iface, to, C_DNS_SD);
192+
dns_send_answer(iface, to, C_DNS_SD, orig_buffer, orig_len);
191193
}
192-
service_reply_single(iface, to, s, ttl, 0);
194+
service_reply_single(iface, to, s, ttl, 0, NULL, 0);
193195
}
194196
}
195197

@@ -205,15 +207,15 @@ service_update(struct vlist_tree *tree, struct vlist_node *node_new,
205207
if (service_init_announce)
206208
vlist_for_each_element(&interfaces, iface, node) {
207209
s->t = 0;
208-
service_reply_single(iface, NULL, s, announce_ttl, 1);
210+
service_reply_single(iface, NULL, s, announce_ttl, 1, NULL, 0);
209211
}
210212
return;
211213
}
212214

213215
s = container_of(node_old, struct service, node);
214216
if (!node_new && service_init_announce)
215217
vlist_for_each_element(&interfaces, iface, node)
216-
service_reply_single(iface, NULL, s, 0, 1);
218+
service_reply_single(iface, NULL, s, 0, 1, NULL, 0);
217219
free(s);
218220
}
219221

@@ -227,14 +229,14 @@ hostname_update(struct vlist_tree *tree, struct vlist_node *node_new,
227229
if (!node_old) {
228230
h = container_of(node_new, struct hostname, node);
229231
vlist_for_each_element(&interfaces, iface, node)
230-
dns_reply_a(iface, NULL, announce_ttl, h->hostname);
232+
dns_reply_a(iface, NULL, announce_ttl, h->hostname, NULL, 0);
231233
return;
232234
}
233235

234236
h = container_of(node_old, struct hostname, node);
235237
if (!node_new)
236238
vlist_for_each_element(&interfaces, iface, node)
237-
dns_reply_a(iface, NULL, 0, h->hostname);
239+
dns_reply_a(iface, NULL, 0, h->hostname, NULL, 0);
238240

239241
free(h);
240242
}

service.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ extern struct vlist_tree hostnames;
2323

2424
extern void service_init(int announce);
2525
extern void service_cleanup(void);
26-
extern void service_reply(struct interface *iface, struct sockaddr *to, const char *instance, const char *service_domain, int ttl);
27-
extern void service_announce_services(struct interface *iface, struct sockaddr *to, int ttl);
26+
extern void service_reply(struct interface *iface, struct sockaddr *to, const char *instance, const char *service_domain, int ttl,
27+
uint8_t *orig_buffer, int orig_len);
28+
extern void service_announce_services(struct interface *iface, struct sockaddr *to, int ttl, uint8_t *orig_buffer, int orig_len);
2829

2930
#endif

0 commit comments

Comments
 (0)