* 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

Reply via email to