* 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
[email protected]
https://lists.connman.net/mailman/listinfo/connman