Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mdnsd: add handling for legacy queries #12

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions announce.c
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,9 @@ announce_timer(struct uloop_timeout *timeout)
/* Fall through */

case STATE_ANNOUNCE:
dns_reply_a(iface, NULL, announce_ttl, NULL);
dns_reply_a(iface, NULL, announce_ttl, NULL, NULL, 0);
dns_reply_a_additional(iface, NULL, announce_ttl);
service_announce_services(iface, NULL, announce_ttl);
service_announce_services(iface, NULL, announce_ttl, NULL, 0);
uloop_timeout_set(timeout, announce_ttl * 800);
break;
}
Expand Down
81 changes: 62 additions & 19 deletions dns.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@ static char name_buffer[MAX_NAME_LEN + 1];
static char dns_buffer[MAX_NAME_LEN];
static struct blob_buf ans_buf;

static struct dns_header*
dns_consume_header(uint8_t **data, int *len);
static char *
dns_consume_name(const uint8_t *base, int blen, uint8_t **data, int *len);
static struct dns_question*
dns_consume_question(uint8_t **data, int *len);

const char*
dns_type_string(uint16_t type)
{
Expand Down Expand Up @@ -139,27 +146,51 @@ dns_add_answer(int type, const uint8_t *rdata, uint16_t rdlength, int ttl)
}

void
dns_send_answer(struct interface *iface, struct sockaddr *to, const char *answer)
dns_send_answer(struct interface *iface, struct sockaddr *to, const char *answer, uint8_t *orig_buffer, int orig_len)
{
uint8_t buffer[256];
struct blob_attr *attr;
struct dns_header h = { 0 };
struct iovec *iov;
int answer_len, rem;
int n_iov = 0;
uint16_t q_cnt = 0;
struct dns_header *orig_h;
uint8_t *b = orig_buffer;
int rlen = orig_len;

if (!dns_answer_cnt)
return;

if (orig_buffer != NULL) {
orig_h = dns_consume_header(&b, &rlen);
if (!orig_h) {
fprintf(stderr, "dropping: bad header\n");
return;
}

q_cnt = orig_h->questions;
h.questions = cpu_to_be16(orig_h->questions);
h.id = cpu_to_be16(orig_h->id);
}
h.answers = cpu_to_be16(dns_answer_cnt);
h.flags = cpu_to_be16(0x8400);

iov = alloca(sizeof(struct iovec) * ((dns_answer_cnt * 2) + 1));
iov = alloca(sizeof(struct iovec) * ((dns_answer_cnt * 2) + 1 + q_cnt));

iov[n_iov].iov_base = &h;
iov[n_iov].iov_len = sizeof(struct dns_header);
n_iov++;

/* if the answer is in reply to a question, the copy the question in answer
* so that the dns format is correct, as per section 6.7 of rfc 6762 */
if (orig_buffer != NULL) {
/* after consuming the header above, b now points to query section */
iov[n_iov].iov_base = b;
iov[n_iov].iov_len = rlen;
n_iov++;
}

answer_len = dn_comp(answer, buffer, sizeof(buffer), NULL, NULL);
if (answer_len < 1)
return;
Expand All @@ -178,12 +209,13 @@ dns_send_answer(struct interface *iface, struct sockaddr *to, const char *answer
DBG(1, "A <- %s %s\n", dns_type_string(be16_to_cpu(a->type)), answer);
}


if (interface_send_packet(iface, to, iov, n_iov) < 0)
perror("failed to send answer");
}

void
dns_reply_a(struct interface *iface, struct sockaddr *to, int ttl, const char *hostname)
dns_reply_a(struct interface *iface, struct sockaddr *to, int ttl, const char *hostname, uint8_t *orig_buffer, int orig_len)
{
struct ifaddrs *ifap, *ifa;
struct sockaddr_in *sa;
Expand All @@ -204,7 +236,7 @@ dns_reply_a(struct interface *iface, struct sockaddr *to, int ttl, const char *h
dns_add_answer(TYPE_AAAA, (uint8_t *) &sa6->sin6_addr, 16, ttl);
}
}
dns_send_answer(iface, to, hostname ? hostname : mdns_hostname_local);
dns_send_answer(iface, to, hostname ? hostname : mdns_hostname_local, orig_buffer, orig_len);

freeifaddrs(ifap);
}
Expand All @@ -215,7 +247,7 @@ dns_reply_a_additional(struct interface *iface, struct sockaddr *to, int ttl)
struct hostname *h;

vlist_for_each_element(&hostnames, h, node)
dns_reply_a(iface, to, ttl, h->hostname);
dns_reply_a(iface, to, ttl, h->hostname, NULL, 0);
}

static int
Expand Down Expand Up @@ -355,44 +387,54 @@ static int parse_answer(struct interface *iface, struct sockaddr *from,
}

static void
parse_question(struct interface *iface, struct sockaddr *from, char *name, struct dns_question *q)
parse_question(struct interface *iface, struct sockaddr *from, char *name, struct dns_question *q,
uint8_t *orig_buffer, int orig_len, uint16_t port)
{
struct sockaddr *to = NULL;
char *host;

/* TODO: Multicast if more than one quarter of TTL has passed */
if (q->class & CLASS_UNICAST) {
if ((q->class & CLASS_UNICAST) || port != MCAST_PORT) {
/* As per rfc 6762 section 6.7, if source port is not multicast port,
* then the response should be unicast udp to the source port */
to = from;
if (interface_multicast(iface))
iface = interface_get(iface->name, iface->type | SOCKTYPE_BIT_UNICAST);
} else {
/* if the query is from multicast port, no need for original buffer
* while responding as per rfc 6762, section 6:
* Multicast DNS responses MUST NOT contain any questions in the
* Question Section. */
orig_buffer = NULL;
orig_len = 0;
}

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

switch (q->type) {
case TYPE_ANY:
if (!strcmp(name, mdns_hostname_local)) {
dns_reply_a(iface, to, announce_ttl, NULL);
dns_reply_a(iface, to, announce_ttl, NULL, orig_buffer, orig_len);
dns_reply_a_additional(iface, to, announce_ttl);
service_reply(iface, to, NULL, NULL, announce_ttl);
service_reply(iface, to, NULL, NULL, announce_ttl, orig_buffer, orig_len);
}
break;

case TYPE_PTR:
if (!strcmp(name, C_DNS_SD)) {
dns_reply_a(iface, to, announce_ttl, NULL);
dns_reply_a(iface, to, announce_ttl, NULL, orig_buffer, orig_len);
dns_reply_a_additional(iface, to, announce_ttl);
service_announce_services(iface, to, announce_ttl);
service_announce_services(iface, to, announce_ttl, orig_buffer, orig_len);
} else {
if (name[0] == '_') {
service_reply(iface, to, NULL, name, announce_ttl);
service_reply(iface, to, NULL, name, announce_ttl, orig_buffer, orig_len);
} else {
/* First dot separates instance name from the rest */
char *dot = strchr(name, '.');

if (dot) {
*dot = '\0';
service_reply(iface, to, name, dot + 1, announce_ttl);
service_reply(iface, to, name, dot + 1, announce_ttl, orig_buffer, orig_len);
*dot = '.';
}
}
Expand All @@ -405,7 +447,7 @@ parse_question(struct interface *iface, struct sockaddr *from, char *name, struc
if (host)
*host = '\0';
if (!strcmp(umdns_host_label, name))
dns_reply_a(iface, to, announce_ttl, NULL);
dns_reply_a(iface, to, announce_ttl, NULL, orig_buffer, orig_len);
break;
};
}
Expand All @@ -416,17 +458,18 @@ dns_handle_packet(struct interface *iface, struct sockaddr *from, uint16_t port,
struct dns_header *h;
uint8_t *b = buffer;
int rlen = len;
uint8_t orig_buffer[len];

/* make a copy of the original buffer since it might be needed to construct the answer
* in case the query is received from a one-shot multicast dns querier */
memcpy(orig_buffer, buffer, len);

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

if (h->questions && !interface_multicast(iface) && port != MCAST_PORT)
/* silently drop unicast questions that dont originate from port 5353 */
return;

while (h->questions-- > 0) {
char *name = dns_consume_name(buffer, len, &b, &rlen);
struct dns_question *q;
Expand All @@ -443,7 +486,7 @@ dns_handle_packet(struct interface *iface, struct sockaddr *from, uint16_t port,
}

if (!(h->flags & FLAG_RESPONSE))
parse_question(iface, from, name, q);
parse_question(iface, from, name, q, orig_buffer, len, port);
}

if (!(h->flags & FLAG_RESPONSE))
Expand Down
4 changes: 2 additions & 2 deletions dns.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,8 @@ void dns_send_question(struct interface *iface, struct sockaddr *to,
const char *question, int type, int multicast);
void dns_init_answer(void);
void dns_add_answer(int type, const uint8_t *rdata, uint16_t rdlength, int ttl);
void dns_send_answer(struct interface *iface, struct sockaddr *to, const char *answer);
void dns_reply_a(struct interface *iface, struct sockaddr *to, int ttl, const char *hostname);
void dns_send_answer(struct interface *iface, struct sockaddr *to, const char *answer, uint8_t *orig_buffer, int orig_len);
void dns_reply_a(struct interface *iface, struct sockaddr *to, int ttl, const char *hostname, uint8_t *orig_buffer, int orig_len);
void dns_reply_a_additional(struct interface *iface, struct sockaddr *to, int ttl);
const char* dns_type_string(uint16_t type);
void dns_handle_packet(struct interface *iface, struct sockaddr *s, uint16_t port, uint8_t *buf, int len);
Expand Down
5 changes: 3 additions & 2 deletions interface.c
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ interface_send_packet4(struct interface *iface, struct sockaddr_in *to, struct i
if (to)
fprintf(stderr, "Ignoring IPv4 address for multicast interface\n");
} else {
a.sin_port = to->sin_port;
a.sin_addr.s_addr = to->sin_addr.s_addr;
}

Expand Down Expand Up @@ -652,9 +653,9 @@ void interface_shutdown(void)

vlist_for_each_element(&interfaces, iface, node)
if (interface_multicast(iface)) {
dns_reply_a(iface, NULL, 0, NULL);
dns_reply_a(iface, NULL, 0, NULL, NULL, 0);
dns_reply_a_additional(iface, NULL, 0);
service_announce_services(iface, NULL, 0);
service_announce_services(iface, NULL, 0, NULL, 0);
}

for (size_t i = 0; i < ARRAY_SIZE(ufd); i++) {
Expand Down
26 changes: 14 additions & 12 deletions service.c
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,8 @@ service_timeout(struct service *s)
}

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

dns_init_answer();
service_add_ptr(service_instance_name(s), ttl);
dns_send_answer(iface, to, service);
dns_send_answer(iface, to, service, orig_buffer, orig_len);

dns_init_answer();
service_add_srv(s, ttl);
if (s->txt && s->txt_len)
dns_add_answer(TYPE_TXT, (uint8_t *) s->txt, s->txt_len, ttl);
dns_send_answer(iface, to, host);
dns_send_answer(iface, to, host, orig_buffer, orig_len);
}

void
service_reply(struct interface *iface, struct sockaddr *to, const char *instance, const char *service_domain, int ttl)
service_reply(struct interface *iface, struct sockaddr *to, const char *instance, const char *service_domain, int ttl,
uint8_t *orig_buffer, int orig_len)
{
struct service *s;

Expand All @@ -173,12 +175,12 @@ service_reply(struct interface *iface, struct sockaddr *to, const char *instance
continue;
if (service_domain && strcmp(s->service, service_domain))
continue;
service_reply_single(iface, to, s, ttl, 0);
service_reply_single(iface, to, s, ttl, 0, orig_buffer, orig_len);
}
}

void
service_announce_services(struct interface *iface, struct sockaddr *to, int ttl)
service_announce_services(struct interface *iface, struct sockaddr *to, int ttl, uint8_t *orig_buffer, int orig_len)
{
struct service *s;

Expand All @@ -187,9 +189,9 @@ service_announce_services(struct interface *iface, struct sockaddr *to, int ttl)
if (ttl) {
dns_init_answer();
service_add_ptr(s->service, ttl);
dns_send_answer(iface, to, C_DNS_SD);
dns_send_answer(iface, to, C_DNS_SD, orig_buffer, orig_len);
}
service_reply_single(iface, to, s, ttl, 0);
service_reply_single(iface, to, s, ttl, 0, NULL, 0);
}
}

Expand All @@ -205,15 +207,15 @@ service_update(struct vlist_tree *tree, struct vlist_node *node_new,
if (service_init_announce)
vlist_for_each_element(&interfaces, iface, node) {
s->t = 0;
service_reply_single(iface, NULL, s, announce_ttl, 1);
service_reply_single(iface, NULL, s, announce_ttl, 1, NULL, 0);
}
return;
}

s = container_of(node_old, struct service, node);
if (!node_new && service_init_announce)
vlist_for_each_element(&interfaces, iface, node)
service_reply_single(iface, NULL, s, 0, 1);
service_reply_single(iface, NULL, s, 0, 1, NULL, 0);
free(s);
}

Expand All @@ -227,14 +229,14 @@ hostname_update(struct vlist_tree *tree, struct vlist_node *node_new,
if (!node_old) {
h = container_of(node_new, struct hostname, node);
vlist_for_each_element(&interfaces, iface, node)
dns_reply_a(iface, NULL, announce_ttl, h->hostname);
dns_reply_a(iface, NULL, announce_ttl, h->hostname, NULL, 0);
return;
}

h = container_of(node_old, struct hostname, node);
if (!node_new)
vlist_for_each_element(&interfaces, iface, node)
dns_reply_a(iface, NULL, 0, h->hostname);
dns_reply_a(iface, NULL, 0, h->hostname, NULL, 0);

free(h);
}
Expand Down
5 changes: 3 additions & 2 deletions service.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ extern struct vlist_tree hostnames;

extern void service_init(int announce);
extern void service_cleanup(void);
extern void service_reply(struct interface *iface, struct sockaddr *to, const char *instance, const char *service_domain, int ttl);
extern void service_announce_services(struct interface *iface, struct sockaddr *to, int ttl);
extern void service_reply(struct interface *iface, struct sockaddr *to, const char *instance, const char *service_domain, int ttl,
uint8_t *orig_buffer, int orig_len);
extern void service_announce_services(struct interface *iface, struct sockaddr *to, int ttl, uint8_t *orig_buffer, int orig_len);

#endif