@@ -262,6 +262,24 @@ static time_t round_down_ttl(time_t end_time, int ttl)
return end_time;
}
+static
+char* find_compressed_pos(char* start, char* end)
+{
+ char* ptr = start;
+ while (ptr <= end) {
+
+ // check string end point
+ if ('\0' == *ptr) return NULL;
+
+ // check compressed signal
+ if (0xc0 == (*ptr & 0xc0)) return ptr;
+
+ ptr++;
+ }
+ DBG("ERR : reach the end position : start %p end %p", start, end);
+ return NULL;
+}
+
static struct request_data *find_request(guint16 id)
{
GSList *list;
@@ -528,6 +546,61 @@ static void destroy_request_data(struct request_data *req)
g_free(req);
}
+static bool udp_request_refuse(struct request_data* req,
+ gpointer request, gpointer name)
+{
+ struct sockaddr *sa;
+ int sk;
+
+ if (!req) {
+ connman_error("%s: ERR : req is NULL", __func__);
+ return false;
+ }
+
+ DBG("id 0x%04x", req->srcid);
+
+ if (req->protocol == IPPROTO_UDP) {
+ if (NULL != req->request) {
+ g_free(req->request);
+ req->request = NULL;
+ }
+ req->request = g_malloc(req->request_len);
+ if (NULL == req->request) {
+ connman_error("%s: ERR : no free memory", __func__);
+ return false;
+ }
+ req->name = g_strdup(name);
+ memcpy(req->request, request, req->request_len);
+ sk = get_req_udp_socket(req);
+ sa = &req->sa;
+ } else {
+ connman_error("%s: ERR : not udp request", __func__);
+ return false;
+ }
+
+ /*
+ * There was no reply from server at all.
+ */
+ struct domain_hdr *hdr;
+
+ hdr = (void *)(req->request + protocol_offset(req->protocol));
+ hdr->id = req->srcid;
+
+ if (sk >= 0)
+ send_response(sk, req->request, req->request_len,
+ sa, req->sa_len, req->protocol);
+
+ req->timeout = 0;
+ g_free(req->resp);
+ g_free(req->request);
+ g_free(req->name);
+ /* to avoid double free req */
+ //g_free(req);
+
+ return true;
+}
+
+
static gboolean request_timeout(gpointer user_data)
{
struct request_data *req = user_data;
@@ -836,7 +909,7 @@ static struct cache_entry *cache_check(gpointer request, int *qtype, int proto)
static int get_name(int counter,
unsigned char *pkt, unsigned char *start, unsigned char *max,
unsigned char *output, int output_max, int *output_len,
- unsigned char **end, char *name, int *name_len)
+ unsigned char **end, char *name, size_t max_name, int *name_len)
{
unsigned char *p;
@@ -857,7 +930,7 @@ static int get_name(int counter,
return get_name(counter + 1, pkt, pkt + offset, max,
output, output_max, output_len, end,
- name, name_len);
+ name, max_name, name_len);
} else {
unsigned label_len = *p;
@@ -867,6 +940,9 @@ static int get_name(int counter,
if (*output_len > output_max)
return -ENOBUFS;
+ if ((*name_len + 1 + label_len + 1) > max_name)
+ return -ENOBUFS;
+
/*
* We need the original name in order to check
* if this answer is the correct one.
@@ -898,14 +974,14 @@ static int parse_rr(unsigned char *buf, unsigned char *start,
unsigned char *response, unsigned int *response_size,
uint16_t *type, uint16_t *class, int *ttl, int *rdlen,
unsigned char **end,
- char *name)
+ char *name, size_t max_name)
{
struct domain_rr *rr;
int err, offset;
int name_len = 0, output_len = 0, max_rsp = *response_size;
err = get_name(0, buf, start, max, response, max_rsp,
- &output_len, end, name, &name_len);
+ &output_len, end, name, max_name, &name_len);
if (err < 0)
return err;
@@ -1031,7 +1107,8 @@ static int parse_response(unsigned char *buf, int buflen,
memset(rsp, 0, sizeof(rsp));
ret = parse_rr(buf, ptr, buf + buflen, rsp, &rsp_len,
- type, class, ttl, &rdlen, &next, name);
+ type, class, ttl, &rdlen, &next, name,
+ sizeof(name) - 1);
if (ret != 0) {
err = ret;
goto out;
@@ -1097,7 +1174,7 @@ static int parse_response(unsigned char *buf, int buflen,
*/
ret = get_name(0, buf, next - rdlen, buf + buflen,
rsp, rsp_len, &output_len, &end,
- name, &name_len);
+ name, sizeof(name) - 1, &name_len);
if (ret != 0) {
/* just ignore the error at this point */
ptr = next;
@@ -1719,12 +1796,90 @@ static char *convert_label(char *start, char *end, char *ptr, char *uptr,
{
int pos, comp_pos;
char name[NS_MAXLABEL];
+ memset(name, 0, NS_MAXLABEL);
- pos = dn_expand((u_char *)start, (u_char *)end, (u_char *)ptr,
- name, NS_MAXLABEL);
- if (pos < 0) {
- DBG("uncompress error [%d/%s]", errno, strerror(errno));
- goto out;
+ char* compressed_ptr = find_compressed_pos(ptr, end);
+ if (NULL == compressed_ptr) {
+ /*
+ * The whole name is not compressed :
+ *
+ * --------------------------------------------------------------
+ * | <host len> | a | . | d | o | m | a | i | n | . | c | o | m |
+ * --------------------------------------------------------------
+ * \_____ dn_expand ___/
+ * \ /
+ * -------------------------------------------------
+ * | a | . | d | o | m | a | i | n | . | c | o | m |
+ * -------------------------------------------------
+ */
+ int ptr_len = strlen(ptr);
+ if (ptr_len > 0) {
+ // remove length part of domain name
+ pos = dn_expand((u_char *)start, (u_char *)end, (u_char *)ptr,
+ name, NS_MAXLABEL);
+ if (pos < 0) {
+ DBG("uncompress error for uncompressed name [%d/%s]", errno, strerror(errno));
+ goto out;
+ }
+ } else {
+ DBG("WARN : ptr length is 0");
+ pos = 1;
+ name[0] = '\0';
+ }
+ } else if (compressed_ptr == ptr) {
+ /*
+ * The whole name is compressed :
+ *
+ * ----------------
+ * | 0xc0 | <pos> |
+ * ----------------
+ * _______________/ \______________
+ * / dn_expand \
+ * -------------------------------------------------
+ * | a | . | d | o | m | a | i | n | . | c | o | m |
+ * -------------------------------------------------
+ */
+ pos = dn_expand((u_char *)start, (u_char *)end, (u_char *)ptr,
+ name, NS_MAXLABEL);
+ if (pos < 0) {
+ DBG("uncompress error for whole name compressed [%d/%s]", errno, strerror(errno));
+ goto out;
+ }
+ } else {
+ /*
+ * Part of the name is compressed :
+ *
+ * -------------------------------------------------------------
+ * | <host len> | a | . | d | o | m | a | i | n | 0xc0 | <pos> |
+ * -------------------------------------------------------------
+ * \_____ dn_expand ___/
+ * \ /
+ * -------------------------------------------------
+ * | a | . | d | o | m | a | i | n | . | c | o | m |
+ * -------------------------------------------------
+ */
+ if (compressed_ptr < ptr || (compressed_ptr - ptr) >= NS_MAXLABEL) {
+ DBG("ERR : too long name : compressed_ptr(%p) ptr(%p)", compressed_ptr, ptr);
+ goto out;
+ }
+
+ int uncompressed_len = compressed_ptr - ptr;
+ if (uncompressed_len <= 0) {
+ DBG("ERR : uncompressed_len %d", uncompressed_len);
+ goto out;
+ }
+ memcpy(name, ptr + 1, uncompressed_len - 1);
+
+ name[uncompressed_len - 1] = '.'; // add '.' between host and domain
+
+ char* name_ptr = &name[uncompressed_len];
+ pos = dn_expand((u_char *)start, (u_char *)end, (u_char *)compressed_ptr,
+ name_ptr, NS_MAXLABEL);
+ if (pos < 0) {
+ DBG("ERR : uncompress error for part compressed [%d/%s]", errno, strerror(errno));
+ goto out;
+ }
+ pos += uncompressed_len;
}
/*
@@ -1737,6 +1892,7 @@ static char *convert_label(char *start, char *end, char *ptr, char *uptr,
goto out;
}
+ DBG("pos %d comp_pos %d", pos, comp_pos);
*used_comp = pos;
*used_uncomp = comp_pos;
@@ -1752,9 +1908,10 @@ static char *uncompress(int16_t field_count, char *start, char *end,
{
char *uptr = *uncompressed_ptr; /* position in result buffer */
- DBG("count %d ptr %p end %p uptr %p", field_count, ptr, end, uptr);
+ DBG("count %d ptr %p start %p end %p uptr %p", field_count, ptr, start, end, uptr);
while (field_count-- > 0 && ptr < end) {
+ DBG("field_count %d", field_count);
int dlen; /* data field length */
int ulen; /* uncompress length */
int pos; /* position in compressed string */
@@ -1762,9 +1919,13 @@ static char *uncompress(int16_t field_count, char *start, char *end,
uint16_t dns_type, dns_class;
int comp_pos;
+ memset(name, 0, NS_MAXLABEL);
+
if (!convert_label(start, end, ptr, name, NS_MAXLABEL,
- &pos, &comp_pos))
+ &pos, &comp_pos)) {
+ DBG("ERR : convert failed");
goto out;
+ }
/*
* Copy the uncompressed resource record, type, class and \0 to
@@ -1772,10 +1933,16 @@ static char *uncompress(int16_t field_count, char *start, char *end,
*/
ulen = strlen(name);
- strncpy(uptr, name, uncomp_len - (uptr - uncompressed));
+ if (ulen <= 0) {
+ DBG("WARN : name is NULL");
+ pos = 1;
+ ulen = 0;
+ } else {
+ strncpy(uptr, name, uncomp_len - (uptr - uncompressed));
- DBG("pos %d ulen %d left %d name %s", pos, ulen,
- (int)(uncomp_len - (uptr - uncompressed)), uptr);
+ DBG("pos %d ulen %d left %d ", pos, ulen,
+ (int)(uncomp_len - (uptr - uncompressed)));
+ }
uptr += ulen;
*uptr++ = '\0';
@@ -1791,8 +1958,10 @@ static char *uncompress(int16_t field_count, char *start, char *end,
dns_type = uptr[0] << 8 | uptr[1];
dns_class = uptr[2] << 8 | uptr[3];
- if (dns_class != ns_c_in)
+ if (dns_class != ns_c_in) {
+ DBG("ERR : dns_class %d", dns_class);
goto out;
+ }
ptr += NS_RRFIXEDSZ;
uptr += NS_RRFIXEDSZ;
@@ -1801,8 +1970,13 @@ static char *uncompress(int16_t field_count, char *start, char *end,
* Then the variable portion of the result (data length).
* Typically this portion is also compressed
* so we need to uncompress it also when necessary.
+ *
+ * RFC 1035 : Standard RRs
+ * CNAME RDATA, HINFO RDATA, MX RDATA, NS RDATA,
+ * PTR RDATA, SOA RDATA, TXT RDATA
*/
- if (dns_type == ns_t_cname) {
+ DBG("dns_type %d", dns_type);
+ if (dns_type == ns_t_cname || dns_type == ns_t_ns) {
if (!convert_label(start, end, ptr, uptr,
uncomp_len - (uptr - uncompressed),
&pos, &comp_pos))
@@ -1861,11 +2035,15 @@ static char *uncompress(int16_t field_count, char *start, char *end,
ptr += 20;
total_len += 20;
+ DBG("total_len = %d", total_len);
+
/*
* Finally fix the length of the data part
*/
len_ptr[0] = total_len << 8;
len_ptr[1] = total_len & 0xff;
+ } else {
+ DBG(" ERR : not support dns_type(%d) !!", dns_type);
}
*uncompressed_ptr = uptr;
@@ -1882,6 +2060,8 @@ static int strip_domains(char *name, char *answers, int maxlen)
uint16_t data_len;
int name_len = strlen(name);
char *ptr, *start = answers, *end = answers + maxlen;
+ DBG("start %p end %p : answers %p maxlen %d name_len %d",
+ start, end, answers, maxlen, name_len);
while (maxlen > 0) {
ptr = strstr(answers, name);
@@ -1898,19 +2078,26 @@ static int strip_domains(char *name, char *answers, int maxlen)
end -= domain_len;
maxlen -= domain_len;
}
+ } else {
+ DBG("WARN : not found domains");
+ ptr = answers;
}
answers += strlen(answers) + 1;
answers += 2 + 2 + 4; /* skip type, class and ttl fields */
data_len = answers[0] << 8 | answers[1];
+ DBG("data_len = %d", data_len);
answers += 2; /* skip the length field */
- if (answers + data_len > end)
+ if (answers + data_len > end) {
+ DBG("ERR: answers %p data_len %d end %p", answers, data_len, end);
return -EINVAL;
+ }
answers += data_len;
maxlen -= answers - ptr;
+ DBG("answers %p ptr %p maxlen = %d", answers, ptr, maxlen);
}
return end - start;
@@ -2022,6 +2209,7 @@ static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol,
memcpy(uptr, ptr, len);
uptr[len] = '\0'; /* host termination */
+ DBG("host name is %s", uptr);
uptr += len + 1;
/*
@@ -2050,22 +2238,28 @@ static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol,
(char *)reply + offset, eom,
ptr, uncompressed, NS_MAXDNAME,
&uptr);
- if (ptr == NULL)
+ if (ptr == NULL) {
+ DBG("ERR : parse answer section failed");
goto out;
+ }
ptr = uncompress(ntohs(hdr->nscount),
(char *)reply + offset, eom,
ptr, uncompressed, NS_MAXDNAME,
&uptr);
- if (ptr == NULL)
+ if (ptr == NULL) {
+ DBG("ERR : parse authority section failed");
goto out;
+ }
ptr = uncompress(ntohs(hdr->arcount),
(char *)reply + offset, eom,
ptr, uncompressed, NS_MAXDNAME,
&uptr);
- if (ptr == NULL)
+ if (ptr == NULL) {
+ DBG("ERR : parse attached section failed");
goto out;
+ }
/*
* The uncompressed buffer now contains almost
@@ -2077,10 +2271,11 @@ static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol,
* domain part. Note that glibc getaddrinfo()
* works differently and accepts FQDN in answer
*/
+ DBG("uptr %p ptr %p answers %p", uptr, ptr, answers);
new_len = strip_domains(uncompressed, answers,
uptr - answers);
if (new_len < 0) {
- DBG("Corrupted packet");
+ DBG("Corrupted packet %d", new_len);
return -EINVAL;
}
@@ -2123,8 +2318,10 @@ static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol,
out:
if (req->numresp < req->numserv) {
if (hdr->rcode > ns_r_noerror) {
+ DBG("WARN : response has error");
return -EINVAL;
} else if (hdr->ancount == 0 && req->append_domain) {
+ DBG("WARN : no answer section");
return -EINVAL;
}
}
@@ -2134,6 +2331,7 @@ out:
if (protocol == IPPROTO_UDP) {
sk = get_req_udp_socket(req);
if (sk < 0) {
+ DBG("ERR : socket error");
errno = -EIO;
err = -EIO;
} else
@@ -2621,6 +2819,7 @@ static bool resolv(struct request_data *req,
gpointer request, gpointer name)
{
GSList *list;
+ bool has_useful_server = false;
for (list = server_list; list; list = list->next) {
struct server_data *data = list->data;
@@ -2641,11 +2840,17 @@ static bool resolv(struct request_data *req,
continue;
}
}
+ has_useful_server = true;
if (ns_resolv(data, req, request, name) > 0)
return true;
}
+ if (false == has_useful_server) {
+ connman_warn("%s: WARN : no useful dns server, try to refuse request", __func__);
+ udp_request_refuse(req, request, name);
+ return true;
+ }
return false;
}
From: Denis Kenzior <denis.kenzior@getcruise.com> --- src/dnsproxy.c | 251 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 228 insertions(+), 23 deletions(-)