* Avoid corruption of response to a query that includes "additional" records (e.g. OPT). * Provide an A record response to only an A record query. --- src/dnsproxy.c | 115 ++++++++++++++++++++++++++++++++++++++++++------------- src/technology.c | 2 +- 2 files changed, 90 insertions(+), 27 deletions(-)
diff --git a/src/dnsproxy.c b/src/dnsproxy.c index f72b5cc..45ecd69 100644 --- a/src/dnsproxy.c +++ b/src/dnsproxy.c @@ -76,6 +76,10 @@ struct domain_hdr { #else #error "Unknown byte order" #endif +struct dns_query_part { + uint16_t qtype; + uint16_t qclass; +} __attribute__ ((packed)); struct a_dns_answer { uint16_t ptr; uint16_t type; @@ -367,6 +371,39 @@ static int dns_name_length(unsigned char *buf) return strlen((char *)buf); } +static int dns_name_field_length(unsigned char *buf, int max_len) +{ + int len = 0; + + for (;;) { + if (len == max_len) + return -EINVAL; + if ((buf[0] & NS_CMPRSFLGS) == NS_CMPRSFLGS) { + /* compressed name */ + len += 2; + if (len > max_len) + return -EINVAL; + return len; + } + if (buf[0] == 0) + return len + 1; + len += buf[0] + 1; + buf += buf[0] + 1; + } +} + +static int dns_query_length(unsigned char *buf, int max_len) +{ + int len = 0; + len = dns_name_field_length(buf, max_len); + if (len < 0) + return len; + len += sizeof(struct dns_query_part); + if (len > max_len) + return -EINVAL; + return len; +} + static void update_cached_ttl(unsigned char *buf, int len, int new_ttl) { unsigned char *c; @@ -516,8 +553,13 @@ static void send_response_A(int sk, unsigned char *req, int len, const struct sockaddr *to, socklen_t tolen, int protocol) { - struct domain_hdr *hdr; - struct a_dns_answer* ans; + struct domain_hdr *req_hdr; + struct domain_hdr *new_hdr; + struct a_dns_answer *ans; + char *new_buf; + unsigned char *req_query; + struct dns_query_part *req_query_part; + int query_len, response_len; int err, offset = protocol_offset(protocol); DBG("sk %d", sk); @@ -525,36 +567,57 @@ static void send_response_A(int sk, unsigned char *req, int len, if (offset < 0) return; - if (len < 12) + if (len < offset + sizeof(struct domain_hdr)) return; - - size_t new_len = len+sizeof(struct a_dns_answer) ; - char* new_buf ; - new_buf = g_malloc(new_len) ; - memcpy(new_buf, req, len) ; - hdr = (void *) (new_buf + offset); + req_hdr = (void *) (req + offset); - DBG("id 0x%04x qr %d opcode %d", hdr->id, hdr->qr, hdr->opcode); + DBG("id 0x%04x qr %d opcode %d", req_hdr->id, req_hdr->qr, req_hdr->opcode); - hdr->qr = 1; - hdr->rcode = ns_r_noerror; + if (ntohs(req_hdr->qdcount) == 0) + return; - hdr->ancount = htons(1); - hdr->nscount = 0; - hdr->arcount = 0; + req_query = req + offset + sizeof(struct domain_hdr); + query_len = dns_query_length(req_query, len - offset - sizeof(struct domain_hdr)); + if (query_len < 0) + return; + req_query_part = (void *) (req_query + query_len - sizeof(struct dns_query_part)); + + size_t new_len = len + sizeof(struct a_dns_answer); + new_buf = g_malloc(new_len); + + /* Copy header */ + response_len = offset + sizeof(struct domain_hdr); + memcpy(new_buf, req, response_len); + + new_hdr = (void *) (new_buf + offset); + + new_hdr->qr = 1; + new_hdr->rcode = ns_r_noerror; + + new_hdr->ancount = 0; + new_hdr->nscount = 0; + new_hdr->arcount = 0; + + /* Copy query */ + memcpy(new_buf + response_len, req_query, query_len); + response_len += query_len; + + if (ntohs(req_query_part->qtype) == ns_t_a && + ntohs(req_query_part->qclass) == ns_c_in) { + /* Add an A answer */ + new_hdr->ancount = htons(1); + ans = (void *) (new_buf + response_len); + ans->ptr = htons(0xc00c) ;/* 11....1100 -> ptr to 12th octet = query */ + ans->type = htons(ns_t_a); + ans->class = htons(ns_c_in); + ans->ttl = 0; + ans->rdlength = htons(4); + ans->ip = htonl(ip); + response_len += sizeof(struct a_dns_answer); + } - /* Create A answer */ - /* Other attributes */ - ans = (void *) (new_buf + len); - ans->ptr = htons(0xc00c) ;/* 11....1100 -> ptr to 12th octet = query */ - ans->type = htons(ns_t_a); - ans->class = htons(ns_c_in); - ans->ttl = 0; - ans->rdlength = htons(4); - ans->ip = htonl(ip); - - err = sendto(sk, new_buf, new_len, MSG_NOSIGNAL, to, tolen); + err = sendto(sk, new_buf, response_len, MSG_NOSIGNAL, to, tolen); g_free(new_buf) ; if (err < 0) { connman_error("Failed to send DNS response to %d: %s", diff --git a/src/technology.c b/src/technology.c index c6475b8..1197df5 100644 --- a/src/technology.c +++ b/src/technology.c @@ -431,7 +431,7 @@ static void technology_load(struct connman_technology *technology) identifier, "Tethering.Passphrase", NULL); technology->tethering_captive = g_key_file_get_boolean(keyfile, - identifier, "Tethering.Captive", false); + identifier, "Tethering.Captive", NULL); done: g_free(identifier); -- 2.1.4 _______________________________________________ connman mailing list connman@connman.net https://lists.connman.net/mailman/listinfo/connman