diff --git a/subsys/net/lib/dns/dns_pack.c b/subsys/net/lib/dns/dns_pack.c index cba6f795d0a84..0964ebb4245c6 100644 --- a/subsys/net/lib/dns/dns_pack.c +++ b/subsys/net/lib/dns/dns_pack.c @@ -6,6 +6,8 @@ #include #include +#include +#include #include "dns_pack.h" @@ -361,10 +363,11 @@ int dns_unpack_response_query(struct dns_msg_t *dns_msg) int dns_copy_qname(uint8_t *buf, uint16_t *len, uint16_t size, struct dns_msg_t *dns_msg, uint16_t pos) { + SYS_BITARRAY_DEFINE(visited, DNS_RESOLVER_MAX_BUF_SIZE); uint16_t msg_size = dns_msg->msg_size; uint8_t *msg = dns_msg->msg; uint16_t lb_size; - int rc = -EINVAL; + int rc = -EINVAL, ret, prev; *len = 0U; @@ -377,7 +380,7 @@ int dns_copy_qname(uint8_t *buf, uint16_t *len, uint16_t size, lb_size = msg[pos]; /* pointer */ - if (lb_size > DNS_LABEL_MAX_SIZE) { + if ((lb_size & NS_CMPRSFLGS) == NS_CMPRSFLGS) { uint8_t mask = DNS_LABEL_MAX_SIZE; if (pos + 1 >= msg_size) { @@ -388,7 +391,21 @@ int dns_copy_qname(uint8_t *buf, uint16_t *len, uint16_t size, /* See: RFC 1035, 4.1.4. Message compression */ pos = ((msg[pos] & mask) << 8) + msg[pos + 1]; + ret = sys_bitarray_test_and_set_bit(&visited, pos, &prev); + if (ret < 0) { + rc = -EINVAL; + break; + } + + if (prev) { + rc = -ELOOP; + break; + } + continue; + } else if (lb_size & NS_CMPRSFLGS) { + rc = -EINVAL; + break; } /* validate that the label (i.e. size + elements), @@ -477,7 +494,7 @@ static int dns_unpack_name(const uint8_t *msg, int maxlen, const uint8_t *src, } while ((val = *curr_src++)) { - if (val & NS_CMPRSFLGS) { + if ((val & NS_CMPRSFLGS) == NS_CMPRSFLGS) { /* Follow pointer */ int pos; diff --git a/subsys/net/lib/dns/resolve.c b/subsys/net/lib/dns/resolve.c index 061f3fdfbd492..39405d23b87c8 100644 --- a/subsys/net/lib/dns/resolve.c +++ b/subsys/net/lib/dns/resolve.c @@ -696,7 +696,8 @@ int dns_validate_msg(struct dns_resolve_context *ctx, ret = dns_unpack_response_header(dns_msg, *dns_id); if (ret < 0) { - ret = DNS_EAI_FAIL; + errno = -ret; + ret = DNS_EAI_SYSTEM; goto quit; } @@ -711,7 +712,8 @@ int dns_validate_msg(struct dns_resolve_context *ctx, ret = dns_unpack_response_query(dns_msg); if (ret < 0) { if (ret == -ENOMEM) { - ret = DNS_EAI_FAIL; + errno = -ret; + ret = DNS_EAI_SYSTEM; goto quit; } @@ -742,7 +744,8 @@ int dns_validate_msg(struct dns_resolve_context *ctx, ret = dns_unpack_answer(dns_msg, answer_ptr, &ttl, &answer_type); if (ret < 0) { - ret = DNS_EAI_FAIL; + errno = -ret; + ret = DNS_EAI_SYSTEM; goto quit; } @@ -809,14 +812,16 @@ int dns_validate_msg(struct dns_resolve_context *ctx, if (dns_msg->response_length < address_size) { /* it seems this is a malformed message */ - ret = DNS_EAI_FAIL; + errno = EMSGSIZE; + ret = DNS_EAI_SYSTEM; goto quit; } if ((dns_msg->response_position + address_size) > dns_msg->msg_size) { /* Too short message */ - ret = DNS_EAI_FAIL; + errno = EMSGSIZE; + ret = DNS_EAI_SYSTEM; goto quit; } @@ -862,6 +867,7 @@ int dns_validate_msg(struct dns_resolve_context *ctx, *query_idx = get_slot_by_id(ctx, *dns_id, *query_hash); if (*query_idx < 0) { + errno = ENOENT; ret = DNS_EAI_SYSTEM; goto quit; } @@ -884,6 +890,7 @@ int dns_validate_msg(struct dns_resolve_context *ctx, net_buf_max_len(dns_cname), dns_msg, pos); if (ret < 0) { + errno = -ret; ret = DNS_EAI_SYSTEM; goto quit; } diff --git a/tests/net/lib/dns_packet/src/main.c b/tests/net/lib/dns_packet/src/main.c index 363bdf3d3b51b..0cb7c4aa09ee3 100644 --- a/tests/net/lib/dns_packet/src/main.c +++ b/tests/net/lib/dns_packet/src/main.c @@ -1287,6 +1287,253 @@ ZTEST(dns_packet, test_dns_invalid_answer) zassert_equal(ret, -EINVAL, "DNS message answer check succeed (%d)", ret); } +static uint8_t recursive_query_resp_ipv4[] = { + /* DNS msg header (12 bytes) */ + 0x74, 0xe1, 0x81, 0x80, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, + + /* Query string (westus2-prod-2.notifications.teams.microsoft.com) + * (length 50) + */ + 0x0e, 0x77, 0x65, 0x73, 0x74, 0x75, 0x73, 0x32, + 0x2d, 0x70, 0x72, 0x6f, 0x64, 0x2d, 0x32, 0x0d, + 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x05, 0x74, 0x65, + 0x61, 0x6d, 0x73, 0x09, 0x6d, 0x69, 0x63, 0x72, + 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x03, 0x63, 0x6f, + 0x6d, 0x00, + + /* Type (2 bytes) */ + 0x00, 0x01, + + /* Class (2 bytes) */ + 0x00, 0x01, + + /* Answer 1 */ + 0xc0, 0x0c, + + /* Answer type (cname) */ + 0x00, 0x05, + + /* Class */ + 0x00, 0x01, + + /* TTL */ + 0x00, 0x00, 0x00, 0x04, + + /* RR data length */ + 0x00, 0x02, + + /* Data */ + 0xc0, 0x4e, /* <--- recursive pointer */ +}; + +NET_BUF_POOL_DEFINE(dns_qname_pool_for_test, 2, 128, 0, NULL); + +ZTEST(dns_packet, test_dns_recursive_query) +{ + static const uint8_t query[] = { + /* Query string */ + 0x0e, 0x77, 0x65, 0x73, 0x74, 0x75, 0x73, 0x32, + 0x2d, 0x70, 0x72, 0x6f, 0x64, 0x2d, 0x32, 0x0d, + 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x05, 0x74, 0x65, + 0x61, 0x6d, 0x73, 0x09, 0x6d, 0x69, 0x63, 0x72, + 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x03, 0x63, 0x6f, + 0x6d, 0x00, + + /* Type */ + 0x00, 0x01, + }; + struct dns_msg_t dns_msg = { 0 }; + uint16_t dns_id = 0; + int query_idx = -1; + uint16_t query_hash = 0; + struct net_buf *dns_cname; + int ret; + + dns_cname = net_buf_alloc(&dns_qname_pool_for_test, dns_ctx.buf_timeout); + zassert_not_null(dns_cname, "Out of mem"); + + dns_msg.msg = recursive_query_resp_ipv4; + dns_msg.msg_size = sizeof(recursive_query_resp_ipv4); + + dns_id = dns_unpack_header_id(dns_msg.msg); + + setup_dns_context(&dns_ctx, 0, dns_id, query, sizeof(query), + DNS_QUERY_TYPE_A); + + ret = dns_validate_msg(&dns_ctx, &dns_msg, &dns_id, &query_idx, + dns_cname, &query_hash); + zassert_true(ret == DNS_EAI_SYSTEM && errno == ELOOP, + "[%s] DNS message was valid (%d / %d)", + "recursive rsp", ret, errno); + + net_buf_unref(dns_cname); +} + +static uint8_t invalid_compression_response_ipv4[] = { + /* DNS msg header (12 bytes) */ + 0x74, 0xe1, 0x81, 0x80, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, + + /* Query string */ + 0x0e, 0x77, 0x65, 0x73, 0x74, 0x75, 0x73, 0x32, + 0x2d, 0x70, 0x72, 0x6f, 0x64, 0x2d, 0x32, 0x0d, + 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x05, 0x74, 0x65, + 0x61, 0x6d, 0x73, 0x09, 0x6d, 0x69, 0x63, 0x72, + 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x03, 0x63, 0x6f, + 0x6d, 0x00, + + /* Type */ + 0x00, 0x01, + + /* Class */ + 0x00, 0x01, + + /* Answer 1 */ + 0xb0, 0x0c, /* <--- invalid compression pointer */ + + /* Answer type (cname) */ + 0x00, 0x05, + + /* Class */ + 0x00, 0x01, + + /* TTL */ + 0x00, 0x00, 0x00, 0x04, + + /* RR data length */ + 0x00, 0x02, + + /* Data */ + 0xc0, 0x0c, +}; + +ZTEST(dns_packet, test_dns_invalid_compress_bits) +{ + static const uint8_t query[] = { + /* Query string */ + 0x0e, 0x77, 0x65, 0x73, 0x74, 0x75, 0x73, 0x32, + 0x2d, 0x70, 0x72, 0x6f, 0x64, 0x2d, 0x32, 0x0d, + 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x05, 0x74, 0x65, + 0x61, 0x6d, 0x73, 0x09, 0x6d, 0x69, 0x63, 0x72, + 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x03, 0x63, 0x6f, + 0x6d, 0x00, + + /* Type */ + 0x00, 0x01, + }; + struct dns_msg_t dns_msg = { 0 }; + uint16_t dns_id = 0; + int query_idx = -1; + uint16_t query_hash = 0; + struct net_buf *dns_cname; + int ret; + + dns_cname = net_buf_alloc(&dns_qname_pool_for_test, dns_ctx.buf_timeout); + zassert_not_null(dns_cname, "Out of mem"); + + dns_msg.msg = invalid_compression_response_ipv4; + dns_msg.msg_size = sizeof(invalid_compression_response_ipv4); + + dns_id = dns_unpack_header_id(dns_msg.msg); + + setup_dns_context(&dns_ctx, 0, dns_id, query, sizeof(query), + DNS_QUERY_TYPE_A); + + ret = dns_validate_msg(&dns_ctx, &dns_msg, &dns_id, &query_idx, + dns_cname, &query_hash); + zassert_true(ret == DNS_EAI_SYSTEM && errno == EINVAL, + "[%s] DNS message was valid (%d / %d)", + "invalid compression rsp", ret, errno); + + net_buf_unref(dns_cname); +} + +static uint8_t invalid_compression_response_cname_ipv4[] = { + /* DNS msg header (12 bytes) */ + 0x74, 0xe1, 0x81, 0x80, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, + + /* Query string */ + 0x0e, 0x77, 0x65, 0x73, 0x74, 0x75, 0x73, 0x32, + 0x2d, 0x70, 0x72, 0x6f, 0x64, 0x2d, 0x32, 0x0d, + 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x05, 0x74, 0x65, + 0x61, 0x6d, 0x73, 0x09, 0x6d, 0x69, 0x63, 0x72, + 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x03, 0x63, 0x6f, + 0x6d, 0x00, + + /* Type */ + 0x00, 0x01, + + /* Class */ + 0x00, 0x01, + + /* Answer 1 */ + 0xc0, 0x0c, + + /* Answer type (cname) */ + 0x00, 0x05, + + /* Class */ + 0x00, 0x01, + + /* TTL */ + 0x00, 0x00, 0x00, 0x04, + + /* RR data length */ + 0x00, 0x02, + + /* Data */ + 0xb0, 0x0c, /* <--- invalid compression pointer */ +}; + +ZTEST(dns_packet, test_dns_invalid_compress_bits_cname) +{ + static const uint8_t query[] = { + /* Query string */ + 0x0e, 0x77, 0x65, 0x73, 0x74, 0x75, 0x73, 0x32, + 0x2d, 0x70, 0x72, 0x6f, 0x64, 0x2d, 0x32, 0x0d, + 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x05, 0x74, 0x65, + 0x61, 0x6d, 0x73, 0x09, 0x6d, 0x69, 0x63, 0x72, + 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x03, 0x63, 0x6f, + 0x6d, 0x00, + + /* Type */ + 0x00, 0x01, + }; + struct dns_msg_t dns_msg = { 0 }; + uint16_t dns_id = 0; + int query_idx = -1; + uint16_t query_hash = 0; + struct net_buf *dns_cname; + int ret; + + dns_cname = net_buf_alloc(&dns_qname_pool_for_test, dns_ctx.buf_timeout); + zassert_not_null(dns_cname, "Out of mem"); + + dns_msg.msg = invalid_compression_response_cname_ipv4; + dns_msg.msg_size = sizeof(invalid_compression_response_cname_ipv4); + + dns_id = dns_unpack_header_id(dns_msg.msg); + + setup_dns_context(&dns_ctx, 0, dns_id, query, sizeof(query), + DNS_QUERY_TYPE_A); + + ret = dns_validate_msg(&dns_ctx, &dns_msg, &dns_id, &query_idx, + dns_cname, &query_hash); + zassert_true(ret == DNS_EAI_SYSTEM && errno == EINVAL, + "[%s] DNS message was valid (%d / %d)", + "invalid compression rsp", ret, errno); + + net_buf_unref(dns_cname); +} + ZTEST_SUITE(dns_packet, NULL, NULL, NULL, NULL, NULL); /* TODO: * 1) add malformed DNS data (mostly done)