From f2f4ecebd4ef66bb7371bce47fc1d8383903baa5 Mon Sep 17 00:00:00 2001 From: Henry Aidan Leta Date: Thu, 7 Jul 2022 11:04:42 -0400 Subject: [PATCH 1/9] first imp for dns reverse lookup name parsing and resolution --- src/include/ndpi_typedefs.h | 1 + src/lib/protocols/dns.c | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/src/include/ndpi_typedefs.h b/src/include/ndpi_typedefs.h index 8ff0d92e9c0..78ec1c3e4cc 100644 --- a/src/include/ndpi_typedefs.h +++ b/src/include/ndpi_typedefs.h @@ -1222,6 +1222,7 @@ struct ndpi_flow_struct { u_int8_t num_queries, num_answers, reply_code, is_query; u_int16_t query_type, query_class, rsp_type; u_int32_t answer_ttl; + char answer_domain[32]; ndpi_ip_addr_t rsp_addr; /* The first address in a DNS response packet */ } dns; diff --git a/src/lib/protocols/dns.c b/src/lib/protocols/dns.c index 51eb20eb570..13e84b4b9c5 100644 --- a/src/lib/protocols/dns.c +++ b/src/lib/protocols/dns.c @@ -315,6 +315,28 @@ static int search_valid_dns(struct ndpi_detection_module_struct *ndpi_struct, )) { memcpy(&flow->protos.dns.rsp_addr, packet->payload + x, data_len); } + else if (rsp_type == 0x0c) + { + // reverse dns lookup responses can have an address section and a domain name section + // since we already have the address from the query we just need the domain name + int section_len = packet->payload[x]; // get 1st segment len + if (packet->payload[x+1] >= 0x30 && packet->payload[x+1] <= 0x39) + { + x += section_len +1; // skip segment len + address field + data_len -= section_len + 1; // adjust data_len for copying + } + // if block to remove the next segment length from the front of the string + if (data_len > 0) + { + x++; + data_len--; + } + + if (data_len > 32) + memcpy(&flow->protos.dns.answer_domain, packet->payload + x, 32); + else + memcpy(&flow->protos.dns.answer_domain, packet->payload + x, data_len); + } } } From b0bd6661d1d348dc7fec77f2f6acc169b0efa8c7 Mon Sep 17 00:00:00 2001 From: Henry Aidan Leta Date: Thu, 7 Jul 2022 11:18:16 -0400 Subject: [PATCH 2/9] indenting changes --- src/lib/protocols/dns.c | 44 ++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/lib/protocols/dns.c b/src/lib/protocols/dns.c index 13e84b4b9c5..57f6345f7d9 100644 --- a/src/lib/protocols/dns.c +++ b/src/lib/protocols/dns.c @@ -315,28 +315,28 @@ static int search_valid_dns(struct ndpi_detection_module_struct *ndpi_struct, )) { memcpy(&flow->protos.dns.rsp_addr, packet->payload + x, data_len); } - else if (rsp_type == 0x0c) - { - // reverse dns lookup responses can have an address section and a domain name section - // since we already have the address from the query we just need the domain name - int section_len = packet->payload[x]; // get 1st segment len - if (packet->payload[x+1] >= 0x30 && packet->payload[x+1] <= 0x39) - { - x += section_len +1; // skip segment len + address field - data_len -= section_len + 1; // adjust data_len for copying - } - // if block to remove the next segment length from the front of the string - if (data_len > 0) - { - x++; - data_len--; - } - - if (data_len > 32) - memcpy(&flow->protos.dns.answer_domain, packet->payload + x, 32); - else - memcpy(&flow->protos.dns.answer_domain, packet->payload + x, data_len); - } + else if (rsp_type == 0x0c) + { + // reverse dns lookup responses can have an address section and a domain name section + // since we already have the address from the query we just need the domain name + int section_len = packet->payload[x]; // get 1st segment len + if (packet->payload[x+1] >= 0x30 && packet->payload[x+1] <= 0x39) + { + x += section_len +1; // skip segment len + address field + data_len -= section_len + 1; // adjust data_len for copying + } + // if block to remove the next segment length from the front of the string + if (data_len > 0) + { + x++; + data_len--; + } + + if (data_len > 32) + memcpy(&flow->protos.dns.answer_domain, packet->payload + x, 32); + else + memcpy(&flow->protos.dns.answer_domain, packet->payload + x, data_len); + } } } From 781466b296450ac809b440f9dedb790009f89771 Mon Sep 17 00:00:00 2001 From: Henry Aidan Leta Date: Thu, 7 Jul 2022 11:19:49 -0400 Subject: [PATCH 3/9] indenting changes --- src/lib/protocols/dns.c | 44 ++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/lib/protocols/dns.c b/src/lib/protocols/dns.c index 57f6345f7d9..29667cce0ad 100644 --- a/src/lib/protocols/dns.c +++ b/src/lib/protocols/dns.c @@ -315,28 +315,28 @@ static int search_valid_dns(struct ndpi_detection_module_struct *ndpi_struct, )) { memcpy(&flow->protos.dns.rsp_addr, packet->payload + x, data_len); } - else if (rsp_type == 0x0c) - { - // reverse dns lookup responses can have an address section and a domain name section - // since we already have the address from the query we just need the domain name - int section_len = packet->payload[x]; // get 1st segment len - if (packet->payload[x+1] >= 0x30 && packet->payload[x+1] <= 0x39) - { - x += section_len +1; // skip segment len + address field - data_len -= section_len + 1; // adjust data_len for copying - } - // if block to remove the next segment length from the front of the string - if (data_len > 0) - { - x++; - data_len--; - } - - if (data_len > 32) - memcpy(&flow->protos.dns.answer_domain, packet->payload + x, 32); - else - memcpy(&flow->protos.dns.answer_domain, packet->payload + x, data_len); - } + else if (rsp_type == 0x0c) + { + // reverse dns lookup responses can have an address section and a domain name section + // since we already have the address from the query we just need the domain name + int section_len = packet->payload[x]; // get 1st segment len + if (packet->payload[x+1] >= 0x30 && packet->payload[x+1] <= 0x39) + { + x += section_len +1; // skip segment len + address field + data_len -= section_len + 1; // adjust data_len for copying + } + // if block to remove the next segment length from the front of the string + if (data_len > 0) + { + x++; + data_len--; + } + + if (data_len > 32) + memcpy(&flow->protos.dns.answer_domain, packet->payload + x, 32); + else + memcpy(&flow->protos.dns.answer_domain, packet->payload + x, data_len); + } } } From 142c32b9792418c2c84823c97baaac0304dbe5b3 Mon Sep 17 00:00:00 2001 From: Henry Aidan Leta Date: Thu, 7 Jul 2022 11:24:08 -0400 Subject: [PATCH 4/9] comments for clarity --- src/lib/protocols/dns.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/lib/protocols/dns.c b/src/lib/protocols/dns.c index 29667cce0ad..4c937593ae7 100644 --- a/src/lib/protocols/dns.c +++ b/src/lib/protocols/dns.c @@ -320,6 +320,8 @@ static int search_valid_dns(struct ndpi_detection_module_struct *ndpi_struct, // reverse dns lookup responses can have an address section and a domain name section // since we already have the address from the query we just need the domain name int section_len = packet->payload[x]; // get 1st segment len + // if first char is number this is an address + // number can't be first char in domain name if (packet->payload[x+1] >= 0x30 && packet->payload[x+1] <= 0x39) { x += section_len +1; // skip segment len + address field @@ -332,6 +334,7 @@ static int search_valid_dns(struct ndpi_detection_module_struct *ndpi_struct, data_len--; } + // copy domain name to field if (data_len > 32) memcpy(&flow->protos.dns.answer_domain, packet->payload + x, 32); else From 2e8304c5203f824d75bb65fd5cedd69124dab962 Mon Sep 17 00:00:00 2001 From: Henry Aidan Leta Date: Fri, 8 Jul 2022 10:30:41 -0400 Subject: [PATCH 5/9] fix potential buffer overflow case and ensure answer_domain is null termed --- src/lib/protocols/dns.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/protocols/dns.c b/src/lib/protocols/dns.c index 4c937593ae7..c033e161540 100644 --- a/src/lib/protocols/dns.c +++ b/src/lib/protocols/dns.c @@ -324,7 +324,7 @@ static int search_valid_dns(struct ndpi_detection_module_struct *ndpi_struct, // number can't be first char in domain name if (packet->payload[x+1] >= 0x30 && packet->payload[x+1] <= 0x39) { - x += section_len +1; // skip segment len + address field + x += section_len + 1; // skip segment len + address field data_len -= section_len + 1; // adjust data_len for copying } // if block to remove the next segment length from the front of the string @@ -335,8 +335,8 @@ static int search_valid_dns(struct ndpi_detection_module_struct *ndpi_struct, } // copy domain name to field - if (data_len > 32) - memcpy(&flow->protos.dns.answer_domain, packet->payload + x, 32); + if (data_len >= 32) + memcpy(&flow->protos.dns.answer_domain, packet->payload + x, sizeof(flow->protos.dns.answer_domain)-1); // make sure buf is null terminated else memcpy(&flow->protos.dns.answer_domain, packet->payload + x, data_len); } From bcb4f1160a83cc83456ff151c0dd8fda015d221f Mon Sep 17 00:00:00 2001 From: Henry Aidan Leta Date: Fri, 8 Jul 2022 10:37:02 -0400 Subject: [PATCH 6/9] readability change --- src/lib/protocols/dns.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/protocols/dns.c b/src/lib/protocols/dns.c index c033e161540..c9229a02092 100644 --- a/src/lib/protocols/dns.c +++ b/src/lib/protocols/dns.c @@ -335,7 +335,7 @@ static int search_valid_dns(struct ndpi_detection_module_struct *ndpi_struct, } // copy domain name to field - if (data_len >= 32) + if (data_len >= sizeof(flow->protos.dns.answer_domain)) memcpy(&flow->protos.dns.answer_domain, packet->payload + x, sizeof(flow->protos.dns.answer_domain)-1); // make sure buf is null terminated else memcpy(&flow->protos.dns.answer_domain, packet->payload + x, data_len); From 5f97d2e080ccff89366d4644fad1e546d672932f Mon Sep 17 00:00:00 2001 From: Henry Aidan Leta Date: Tue, 12 Jul 2022 14:13:31 -0400 Subject: [PATCH 7/9] parsing to properly handle the first dns answer domain name --- src/lib/protocols/dns.c | 43 +++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/src/lib/protocols/dns.c b/src/lib/protocols/dns.c index c9229a02092..f50a8d6f440 100644 --- a/src/lib/protocols/dns.c +++ b/src/lib/protocols/dns.c @@ -317,28 +317,33 @@ static int search_valid_dns(struct ndpi_detection_module_struct *ndpi_struct, } else if (rsp_type == 0x0c) { - // reverse dns lookup responses can have an address section and a domain name section + + // reverse dns lookup responses can have an address label as well as additional domain name labels // since we already have the address from the query we just need the domain name - int section_len = packet->payload[x]; // get 1st segment len - // if first char is number this is an address - // number can't be first char in domain name - if (packet->payload[x+1] >= 0x30 && packet->payload[x+1] <= 0x39) + int an_index = 0; + while((packet->payload[x] != '.') && (packet->payload[x] != '\0')) { - x += section_len + 1; // skip segment len + address field - data_len -= section_len + 1; // adjust data_len for copying + int label_len = packet->payload[x]; // get 1st label len + + // if first char is number this is an address + // number can't be first char in domain name + if (packet->payload[x+1] >= 0x30 && packet->payload[x+1] <= 0x39) + { + x += label_len + 1; // skip label len + address field + } + else + { + if (an_index + label_len < (int)(sizeof(flow->protos.dns.answer_domain)-1)) + { + strncat(flow->protos.dns.answer_domain, (char *)packet->payload + x + 1, label_len); // copy label section into string field + an_index += label_len; // increment index + flow->protos.dns.answer_domain[an_index] = '.'; // delimeter between label sections + an_index++; // increment index for delimeter + x += label_len + 1; // skip label len + address field + } + } } - // if block to remove the next segment length from the front of the string - if (data_len > 0) - { - x++; - data_len--; - } - - // copy domain name to field - if (data_len >= sizeof(flow->protos.dns.answer_domain)) - memcpy(&flow->protos.dns.answer_domain, packet->payload + x, sizeof(flow->protos.dns.answer_domain)-1); // make sure buf is null terminated - else - memcpy(&flow->protos.dns.answer_domain, packet->payload + x, data_len); + flow->protos.dns.answer_domain[an_index - 1] = '\0'; // remove trailing '.' char and replace with null term } } } From 2c30d1a91d039e18ad2fbe5f467ae0cde4c99688 Mon Sep 17 00:00:00 2001 From: Henry Aidan Leta Date: Tue, 12 Jul 2022 15:48:48 -0400 Subject: [PATCH 8/9] extend size of domain buffer to have enouh space for the largest of domain names --- src/include/ndpi_typedefs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/include/ndpi_typedefs.h b/src/include/ndpi_typedefs.h index 78ec1c3e4cc..a92eaa0ef09 100644 --- a/src/include/ndpi_typedefs.h +++ b/src/include/ndpi_typedefs.h @@ -1222,7 +1222,7 @@ struct ndpi_flow_struct { u_int8_t num_queries, num_answers, reply_code, is_query; u_int16_t query_type, query_class, rsp_type; u_int32_t answer_ttl; - char answer_domain[32]; + char answer_domain[256]; ndpi_ip_addr_t rsp_addr; /* The first address in a DNS response packet */ } dns; From 836bc260d84781876cf02ef0729fbcd3fabb7452 Mon Sep 17 00:00:00 2001 From: Henry Aidan Leta Date: Thu, 14 Jul 2022 13:16:12 -0400 Subject: [PATCH 9/9] change if case to make sure reverse lookup parsing wont iterate past the end of the first answer if there is only one --- src/lib/protocols/dns.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/lib/protocols/dns.c b/src/lib/protocols/dns.c index f50a8d6f440..15221cfc598 100644 --- a/src/lib/protocols/dns.c +++ b/src/lib/protocols/dns.c @@ -320,14 +320,17 @@ static int search_valid_dns(struct ndpi_detection_module_struct *ndpi_struct, // reverse dns lookup responses can have an address label as well as additional domain name labels // since we already have the address from the query we just need the domain name + // we only process the first answer and grab its domain name int an_index = 0; - while((packet->payload[x] != '.') && (packet->payload[x] != '\0')) + // make sure to exit loop if x is a 00 octet or x exceeds the packet payload length or the length octet has first 2 bits set (domain name compressed) + while((packet->payload[x] != '\0') && (x < packet->payload_packet_len) && ((packet->payload[x] & 0xC0) != 0)) { int label_len = packet->payload[x]; // get 1st label len - + // if first char is number this is an address // number can't be first char in domain name - if (packet->payload[x+1] >= 0x30 && packet->payload[x+1] <= 0x39) + // we only want to check the first label for an ip address + if (packet->payload[x+1] >= 0x30 && packet->payload[x+1] <= 0x39 && an_index == 0) { x += label_len + 1; // skip label len + address field } @@ -341,6 +344,8 @@ static int search_valid_dns(struct ndpi_detection_module_struct *ndpi_struct, an_index++; // increment index for delimeter x += label_len + 1; // skip label len + address field } + else + break; } } flow->protos.dns.answer_domain[an_index - 1] = '\0'; // remove trailing '.' char and replace with null term