diff --git a/.gitignore b/.gitignore index 60bf083a1b..081006fbf7 100644 --- a/.gitignore +++ b/.gitignore @@ -54,4 +54,5 @@ CHANGES.dist c-ares-*.tar.gz.asc ares_parse_mx_reply.pdf /[0-9]*.patch +.dirstamp build diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a3b5194c78..18cf2d02ac 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -13,3 +13,32 @@ To improve the chances of the c-ares maintainers responding to your request: - Also send an email to the mailing list at `c-ares@lists.haxx.se` describing your change. - To follow any associated discussion, please subscribe to the [mailing list](http://lists.haxx.se/listinfo/c-ares). + +To build locally with asan: +``` +export BUILD_TYPE=asan +export CFLAGS=-fsanitize=address +export CXXFLAGS=-fsanitize=address +export LDFLAGS=-fsanitize=address +autoreconf -fi +./configure --disable-symbol-hiding --enable-expose-statics --enable-maintainer-mode --enable-debug +make +ASAN_OPTIONS=detect_leaks=1 test/arestest --gtest_filter='-*.Live*' +for file in $(ls test/fuzzinput); do echo "$file"; test/aresfuzz "test/fuzzinput/$file"; done +``` + +To build locally with ubsan, same instructions as asan but switch out BUILD_TYPE, CFLAGS, CXXFLAGS, and LDFLAGS: +``` +export BUILD_TYPE=ubsan +export CFLAGS=-fsanitize=undefined -fno-sanitize-recover +export CXXFLAGS=-fsanitize=undefined -fno-sanitize-recover +export LDFLAGS=CFLAGS=-fsanitize=undefined +``` + +To build locally with lsan, same instructions as asan but switch out BUILD_TYPE, CFLAGS, CXXFLAGS, and LDFLAGS: +``` +export BUILD_TYPE=lsan +export CFLAGS=-fsanitize=leak +export CXXFLAGS=-fsanitize=leak +export LDFLAGS=-fsanitize=leak +``` diff --git a/aminclude_static.am b/aminclude_static.am new file mode 100644 index 0000000000..6ee9c15b43 --- /dev/null +++ b/aminclude_static.am @@ -0,0 +1,126 @@ + +# aminclude_static.am generated automatically by Autoconf +# from AX_AM_MACROS_STATIC on Sun Jan 2 13:41:56 EST 2022 + + +# Code coverage +# +# Optional: +# - CODE_COVERAGE_DIRECTORY: Top-level directory for code coverage reporting. +# Multiple directories may be specified, separated by whitespace. +# (Default: $(top_builddir)) +# - CODE_COVERAGE_OUTPUT_FILE: Filename and path for the .info file generated +# by lcov for code coverage. (Default: +# $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage.info) +# - CODE_COVERAGE_OUTPUT_DIRECTORY: Directory for generated code coverage +# reports to be created. (Default: +# $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage) +# - CODE_COVERAGE_BRANCH_COVERAGE: Set to 1 to enforce branch coverage, +# set to 0 to disable it and leave empty to stay with the default. +# (Default: empty) +# - CODE_COVERAGE_LCOV_SHOPTS_DEFAULT: Extra options shared between both lcov +# instances. (Default: based on ) +# - CODE_COVERAGE_LCOV_SHOPTS: Extra options to shared between both lcov +# instances. (Default: ) +# - CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH: --gcov-tool pathtogcov +# - CODE_COVERAGE_LCOV_OPTIONS_DEFAULT: Extra options to pass to the +# collecting lcov instance. (Default: ) +# - CODE_COVERAGE_LCOV_OPTIONS: Extra options to pass to the collecting lcov +# instance. (Default: ) +# - CODE_COVERAGE_LCOV_RMOPTS_DEFAULT: Extra options to pass to the filtering +# lcov instance. (Default: empty) +# - CODE_COVERAGE_LCOV_RMOPTS: Extra options to pass to the filtering lcov +# instance. (Default: ) +# - CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT: Extra options to pass to the +# genhtml instance. (Default: based on ) +# - CODE_COVERAGE_GENHTML_OPTIONS: Extra options to pass to the genhtml +# instance. (Default: ) +# - CODE_COVERAGE_IGNORE_PATTERN: Extra glob pattern of files to ignore +# +# The generated report will be titled using the $(PACKAGE_NAME) and +# $(PACKAGE_VERSION). In order to add the current git hash to the title, +# use the git-version-gen script, available online. +# Optional variables +# run only on top dir +if CODE_COVERAGE_ENABLED + ifeq ($(abs_builddir), $(abs_top_builddir)) +CODE_COVERAGE_DIRECTORY ?= $(top_builddir) +CODE_COVERAGE_OUTPUT_FILE ?= $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage.info +CODE_COVERAGE_OUTPUT_DIRECTORY ?= $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage + +CODE_COVERAGE_BRANCH_COVERAGE ?= +CODE_COVERAGE_LCOV_SHOPTS_DEFAULT ?= $(if $(CODE_COVERAGE_BRANCH_COVERAGE),--rc lcov_branch_coverage=$(CODE_COVERAGE_BRANCH_COVERAGE)) +CODE_COVERAGE_LCOV_SHOPTS ?= $(CODE_COVERAGE_LCOV_SHOPTS_DEFAULT) +CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH ?= --gcov-tool "$(GCOV)" +CODE_COVERAGE_LCOV_OPTIONS_DEFAULT ?= $(CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH) +CODE_COVERAGE_LCOV_OPTIONS ?= $(CODE_COVERAGE_LCOV_OPTIONS_DEFAULT) +CODE_COVERAGE_LCOV_RMOPTS_DEFAULT ?= +CODE_COVERAGE_LCOV_RMOPTS ?= $(CODE_COVERAGE_LCOV_RMOPTS_DEFAULT) +CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT ?=$(if $(CODE_COVERAGE_BRANCH_COVERAGE),--rc genhtml_branch_coverage=$(CODE_COVERAGE_BRANCH_COVERAGE)) +CODE_COVERAGE_GENHTML_OPTIONS ?= $(CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT) +CODE_COVERAGE_IGNORE_PATTERN ?= + +GITIGNOREFILES := $(GITIGNOREFILES) $(CODE_COVERAGE_OUTPUT_FILE) $(CODE_COVERAGE_OUTPUT_DIRECTORY) +code_coverage_v_lcov_cap = $(code_coverage_v_lcov_cap_$(V)) +code_coverage_v_lcov_cap_ = $(code_coverage_v_lcov_cap_$(AM_DEFAULT_VERBOSITY)) +code_coverage_v_lcov_cap_0 = @echo " LCOV --capture" $(CODE_COVERAGE_OUTPUT_FILE); +code_coverage_v_lcov_ign = $(code_coverage_v_lcov_ign_$(V)) +code_coverage_v_lcov_ign_ = $(code_coverage_v_lcov_ign_$(AM_DEFAULT_VERBOSITY)) +code_coverage_v_lcov_ign_0 = @echo " LCOV --remove /tmp/*" $(CODE_COVERAGE_IGNORE_PATTERN); +code_coverage_v_genhtml = $(code_coverage_v_genhtml_$(V)) +code_coverage_v_genhtml_ = $(code_coverage_v_genhtml_$(AM_DEFAULT_VERBOSITY)) +code_coverage_v_genhtml_0 = @echo " GEN " "$(CODE_COVERAGE_OUTPUT_DIRECTORY)"; +code_coverage_quiet = $(code_coverage_quiet_$(V)) +code_coverage_quiet_ = $(code_coverage_quiet_$(AM_DEFAULT_VERBOSITY)) +code_coverage_quiet_0 = --quiet + +# sanitizes the test-name: replaces with underscores: dashes and dots +code_coverage_sanitize = $(subst -,_,$(subst .,_,$(1))) + +# Use recursive makes in order to ignore errors during check +check-code-coverage: + -$(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -k check + $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) code-coverage-capture + +# Capture code coverage data +code-coverage-capture: code-coverage-capture-hook + $(code_coverage_v_lcov_cap)$(LCOV) $(code_coverage_quiet) $(addprefix --directory ,$(CODE_COVERAGE_DIRECTORY)) --capture --output-file "$(CODE_COVERAGE_OUTPUT_FILE).tmp" --test-name "$(call code_coverage_sanitize,$(PACKAGE_NAME)-$(PACKAGE_VERSION))" --no-checksum --compat-libtool $(CODE_COVERAGE_LCOV_SHOPTS) $(CODE_COVERAGE_LCOV_OPTIONS) + $(code_coverage_v_lcov_ign)$(LCOV) $(code_coverage_quiet) $(addprefix --directory ,$(CODE_COVERAGE_DIRECTORY)) --remove "$(CODE_COVERAGE_OUTPUT_FILE).tmp" "/tmp/*" $(CODE_COVERAGE_IGNORE_PATTERN) --output-file "$(CODE_COVERAGE_OUTPUT_FILE)" $(CODE_COVERAGE_LCOV_SHOPTS) $(CODE_COVERAGE_LCOV_RMOPTS) + -@rm -f "$(CODE_COVERAGE_OUTPUT_FILE).tmp" + $(code_coverage_v_genhtml)LANG=C $(GENHTML) $(code_coverage_quiet) $(addprefix --prefix ,$(CODE_COVERAGE_DIRECTORY)) --output-directory "$(CODE_COVERAGE_OUTPUT_DIRECTORY)" --title "$(PACKAGE_NAME)-$(PACKAGE_VERSION) Code Coverage" --legend --show-details "$(CODE_COVERAGE_OUTPUT_FILE)" $(CODE_COVERAGE_GENHTML_OPTIONS) + @echo "file://$(abs_builddir)/$(CODE_COVERAGE_OUTPUT_DIRECTORY)/index.html" + +code-coverage-clean: + -$(LCOV) --directory $(top_builddir) -z + -rm -rf "$(CODE_COVERAGE_OUTPUT_FILE)" "$(CODE_COVERAGE_OUTPUT_FILE).tmp" "$(CODE_COVERAGE_OUTPUT_DIRECTORY)" + -find . \( -name "*.gcda" -o -name "*.gcno" -o -name "*.gcov" \) -delete + +code-coverage-dist-clean: + +AM_DISTCHECK_CONFIGURE_FLAGS := $(AM_DISTCHECK_CONFIGURE_FLAGS) --disable-code-coverage + else # ifneq ($(abs_builddir), $(abs_top_builddir)) +check-code-coverage: + +code-coverage-capture: code-coverage-capture-hook + +code-coverage-clean: + +code-coverage-dist-clean: + endif # ifeq ($(abs_builddir), $(abs_top_builddir)) +else #! CODE_COVERAGE_ENABLED +# Use recursive makes in order to ignore errors during check +check-code-coverage: + @echo "Need to reconfigure with --enable-code-coverage" +# Capture code coverage data +code-coverage-capture: code-coverage-capture-hook + @echo "Need to reconfigure with --enable-code-coverage" + +code-coverage-clean: + +code-coverage-dist-clean: + +endif #CODE_COVERAGE_ENABLED +# Hook rule executed before code-coverage-capture, overridable by the user +code-coverage-capture-hook: + +.PHONY: check-code-coverage code-coverage-capture code-coverage-dist-clean code-coverage-clean code-coverage-capture-hook diff --git a/include/ares.h b/include/ares.h index cf8a8553fe..42e8377317 100644 --- a/include/ares.h +++ b/include/ares.h @@ -537,6 +537,18 @@ struct ares_caa_reply { size_t length; /* length excludes null termination */ }; +struct cares_caa_reply; + +typedef struct cares_caa_reply cares_caa_reply; + +struct cares_ptr_reply; + +typedef struct cares_ptr_reply cares_ptr_reply; + +struct cares_ns_reply; + +typedef struct cares_ns_reply cares_ns_reply; + struct ares_srv_reply { struct ares_srv_reply *next; char *host; @@ -545,12 +557,28 @@ struct ares_srv_reply { unsigned short port; }; +struct cares_srv_reply; + +typedef struct cares_srv_reply cares_srv_reply; + +struct cares_srv_reply_container; + +typedef struct cares_srv_reply_container cares_srv_reply_container; + +struct cares_host_reply; + +typedef struct cares_host_reply cares_host_reply; + struct ares_mx_reply { struct ares_mx_reply *next; char *host; unsigned short priority; }; +struct cares_mx_reply; + +typedef struct cares_mx_reply cares_mx_reply; + struct ares_txt_reply { struct ares_txt_reply *next; unsigned char *txt; @@ -568,6 +596,10 @@ struct ares_txt_ext { unsigned char record_start; }; +struct cares_txt_reply; + +typedef struct cares_txt_reply cares_txt_reply; + struct ares_naptr_reply { struct ares_naptr_reply *next; unsigned char *flags; @@ -578,6 +610,10 @@ struct ares_naptr_reply { unsigned short preference; }; +struct cares_naptr_reply; + +typedef struct cares_naptr_reply cares_naptr_reply; + struct ares_soa_reply { char *nsname; char *hostmaster; @@ -588,12 +624,16 @@ struct ares_soa_reply { unsigned int minttl; }; +struct cares_soa_reply; + +typedef struct cares_soa_reply cares_soa_reply; + struct ares_uri_reply { struct ares_uri_reply *next; unsigned short priority; unsigned short weight; char *uri; - int ttl; + unsigned int ttl; }; /* @@ -656,8 +696,33 @@ CARES_EXTERN int ares_parse_aaaa_reply(const unsigned char *abuf, int *naddrttls); CARES_EXTERN int ares_parse_caa_reply(const unsigned char* abuf, - int alen, - struct ares_caa_reply** caa_out); + int alen, + struct ares_caa_reply** caa_out); + +CARES_EXTERN int cares_parse_caa_reply(const unsigned char* abuf, + int alen, + struct cares_caa_reply** caa_out); + +CARES_EXTERN const cares_caa_reply* +cares_caa_reply_get_next(const cares_caa_reply* caa_reply); + +CARES_EXTERN int +cares_caa_reply_get_critical(const cares_caa_reply* caa_reply); + +CARES_EXTERN const unsigned char* +cares_caa_reply_get_property(const cares_caa_reply* caa_reply); + +CARES_EXTERN size_t +cares_caa_reply_get_plength(const cares_caa_reply* caa_reply); + +CARES_EXTERN const unsigned char* +cares_caa_reply_get_value(const cares_caa_reply* caa_reply); + +CARES_EXTERN size_t +cares_caa_reply_get_length(const cares_caa_reply* caa_reply); + +CARES_EXTERN unsigned int cares_caa_reply_get_ttl(const cares_caa_reply* + caa_reply); CARES_EXTERN int ares_parse_ptr_reply(const unsigned char *abuf, int alen, @@ -666,17 +731,99 @@ CARES_EXTERN int ares_parse_ptr_reply(const unsigned char *abuf, int family, struct hostent **host); +CARES_EXTERN int cares_parse_ptr_reply(const unsigned char *abuf, + int alen, + struct cares_ptr_reply **ptr_out); + +CARES_EXTERN const cares_ptr_reply* +cares_ptr_reply_get_next(const cares_ptr_reply* ptr_reply); + +CARES_EXTERN const char* +cares_ptr_reply_get_host(const cares_ptr_reply* ptr_reply); + +CARES_EXTERN unsigned int cares_ptr_reply_get_ttl(const cares_ptr_reply* + ptr_reply); + CARES_EXTERN int ares_parse_ns_reply(const unsigned char *abuf, int alen, struct hostent **host); +CARES_EXTERN int cares_parse_ns_reply(const unsigned char *abuf, + int alen, + struct cares_ns_reply **ns_out); + +CARES_EXTERN const cares_ns_reply* +cares_ns_reply_get_next(const cares_ns_reply* ns_reply); + +CARES_EXTERN const char* +cares_ns_reply_get_host(const cares_ns_reply* ns_reply); + +CARES_EXTERN unsigned int cares_ns_reply_get_ttl(const cares_ns_reply* + ns_reply); + CARES_EXTERN int ares_parse_srv_reply(const unsigned char* abuf, int alen, struct ares_srv_reply** srv_out); +CARES_EXTERN int cares_parse_srv_reply(const unsigned char* abuf, + int alen, + struct cares_srv_reply_container** srv_out); + +CARES_EXTERN const cares_srv_reply* +cares_srv_reply_container_get_first(const cares_srv_reply_container* container); + +CARES_EXTERN const cares_srv_reply* +cares_srv_reply_container_get_next(cares_srv_reply_container* container); + +CARES_EXTERN const cares_srv_reply* +cares_srv_reply_container_get_last(const cares_srv_reply_container* container); + +CARES_EXTERN unsigned int +cares_srv_reply_container_get_count(const cares_srv_reply_container* container); + +CARES_EXTERN unsigned int +cares_srv_reply_container_get_curr(const cares_srv_reply_container* container); + +CARES_EXTERN int +cares_srv_reply_container_at_end(const cares_srv_reply_container* container); + +CARES_EXTERN void +cares_srv_reply_container_reset(cares_srv_reply_container* container); + +CARES_EXTERN const char* +cares_srv_reply_get_host(const cares_srv_reply* srv_reply); + +CARES_EXTERN unsigned short +cares_srv_reply_get_priority(const cares_srv_reply* srv_reply); + +CARES_EXTERN unsigned short +cares_srv_reply_get_weight(const cares_srv_reply* srv_reply); + +CARES_EXTERN unsigned short +cares_srv_reply_get_port(const cares_srv_reply* srv_reply); + +CARES_EXTERN unsigned int cares_srv_reply_get_ttl(const cares_srv_reply* + srv_reply); + CARES_EXTERN int ares_parse_mx_reply(const unsigned char* abuf, + int alen, + struct ares_mx_reply** mx_out); + +CARES_EXTERN int cares_parse_mx_reply(const unsigned char* abuf, int alen, - struct ares_mx_reply** mx_out); + struct cares_mx_reply** mx_out); + +CARES_EXTERN const cares_mx_reply* +cares_mx_reply_get_next(const cares_mx_reply* mx_reply); + +CARES_EXTERN const char* +cares_mx_reply_get_host(const cares_mx_reply* mx_reply); + +CARES_EXTERN unsigned short +cares_mx_reply_get_priority(const cares_mx_reply* mx_reply); + +CARES_EXTERN unsigned int cares_mx_reply_get_ttl(const cares_mx_reply* + mx_reply); CARES_EXTERN int ares_parse_txt_reply(const unsigned char* abuf, int alen, @@ -686,13 +833,88 @@ CARES_EXTERN int ares_parse_txt_reply_ext(const unsigned char* abuf, int alen, struct ares_txt_ext** txt_out); +CARES_EXTERN int cares_parse_txt_reply(const unsigned char* abuf, + int alen, + struct cares_txt_reply** txt_out); + +CARES_EXTERN const cares_txt_reply* +cares_txt_reply_get_next(const cares_txt_reply* txt_reply); + +CARES_EXTERN const unsigned char* +cares_txt_reply_get_txt(const cares_txt_reply* txt_reply); + +CARES_EXTERN size_t +cares_txt_reply_get_length(const cares_txt_reply* txt_reply); + +CARES_EXTERN unsigned char +cares_txt_reply_get_record_start(const cares_txt_reply* txt_reply); + +CARES_EXTERN unsigned int cares_txt_reply_get_ttl(const cares_txt_reply* + txt_reply); + CARES_EXTERN int ares_parse_naptr_reply(const unsigned char* abuf, int alen, struct ares_naptr_reply** naptr_out); +CARES_EXTERN int cares_parse_naptr_reply(const unsigned char* abuf, + int alen, + struct cares_naptr_reply** naptr_out); + +CARES_EXTERN const cares_naptr_reply* +cares_naptr_reply_get_next(const cares_naptr_reply* naptr_reply); + +CARES_EXTERN const unsigned char* +cares_naptr_reply_get_flags(const cares_naptr_reply* naptr_reply); + +CARES_EXTERN const unsigned char* +cares_naptr_reply_get_service(const cares_naptr_reply* naptr_reply); + +CARES_EXTERN const unsigned char* +cares_naptr_reply_get_regexp(const cares_naptr_reply* naptr_reply); + +CARES_EXTERN const char* +cares_naptr_reply_get_replacement(const cares_naptr_reply* naptr_reply); + +CARES_EXTERN unsigned short +cares_naptr_reply_get_order(const cares_naptr_reply* naptr_reply); + +CARES_EXTERN unsigned short +cares_naptr_reply_get_preference(const cares_naptr_reply* naptr_reply); + +CARES_EXTERN unsigned int cares_naptr_reply_get_ttl(const cares_naptr_reply* + naptr_reply); + CARES_EXTERN int ares_parse_soa_reply(const unsigned char* abuf, - int alen, - struct ares_soa_reply** soa_out); + int alen, + struct ares_soa_reply** soa_out); + +CARES_EXTERN int cares_parse_soa_reply(const unsigned char* abuf, + int alen, + struct cares_soa_reply** soa_out); + +CARES_EXTERN const char* +cares_soa_reply_get_nsname(const cares_soa_reply* soa_reply); + +CARES_EXTERN const char* +cares_soa_reply_get_hostmaster(const cares_soa_reply* soa_reply); + +CARES_EXTERN unsigned int +cares_soa_reply_get_serial(const cares_soa_reply* soa_reply); + +CARES_EXTERN unsigned int +cares_soa_reply_get_refresh(const cares_soa_reply* soa_reply); + +CARES_EXTERN unsigned int +cares_soa_reply_get_retry(const cares_soa_reply* soa_reply); + +CARES_EXTERN unsigned int +cares_soa_reply_get_expire(const cares_soa_reply* soa_reply); + +CARES_EXTERN unsigned int +cares_soa_reply_get_minttl(const cares_soa_reply* soa_reply); + +CARES_EXTERN unsigned int cares_soa_reply_get_ttl(const cares_soa_reply* + soa_reply); CARES_EXTERN int ares_parse_uri_reply(const unsigned char* abuf, int alen, @@ -747,6 +969,8 @@ CARES_EXTERN const char *ares_inet_ntop(int af, const void *src, char *dst, CARES_EXTERN int ares_inet_pton(int af, const char *src, void *dst); +CARES_EXTERN void cares_free_container(void *containerptr); + #ifdef __cplusplus } diff --git a/src/lib/Makefile.inc b/src/lib/Makefile.inc index a3b060c289..eddeaa63e7 100644 --- a/src/lib/Makefile.inc +++ b/src/lib/Makefile.inc @@ -34,21 +34,31 @@ CSOURCES = ares__addrinfo2hostent.c \ ares_parse_a_reply.c \ ares_parse_aaaa_reply.c \ ares_parse_caa_reply.c \ + cares_parse_caa_reply.c \ ares_parse_mx_reply.c \ + cares_parse_mx_reply.c \ ares_parse_naptr_reply.c \ + cares_parse_naptr_reply.c \ ares_parse_ns_reply.c \ + cares_parse_ns_reply.c \ ares_parse_ptr_reply.c \ + cares_parse_ptr_reply.c \ ares_parse_soa_reply.c \ + cares_parse_soa_reply.c \ ares_parse_srv_reply.c \ + cares_parse_srv_reply.c \ ares_parse_txt_reply.c \ + cares_parse_txt_reply.c \ ares_parse_uri_reply.c \ ares_platform.c \ ares_process.c \ ares_query.c \ + cares_reply.c \ ares_search.c \ ares_send.c \ ares_strcasecmp.c \ ares_strdup.c \ + cares_memdup.c \ ares_strerror.c \ ares_strsplit.c \ ares_timeout.c \ @@ -57,6 +67,8 @@ CSOURCES = ares__addrinfo2hostent.c \ bitncmp.c \ inet_net_pton.c \ inet_ntop.c \ + cares_container.c \ + cares_free_container.c \ windows_port.c HHEADERS = ares_android.h \ @@ -72,10 +84,12 @@ HHEADERS = ares_android.h \ ares_private.h \ ares_strcasecmp.h \ ares_strdup.h \ + cares_memdup.h \ ares_strsplit.h \ ares_writev.h \ bitncmp.h \ ares_setup.h \ + cares_free_container.h \ setup_once.h diff --git a/src/lib/ares_data.c b/src/lib/ares_data.c index 69dff06689..b41e215442 100644 --- a/src/lib/ares_data.c +++ b/src/lib/ares_data.c @@ -23,7 +23,6 @@ #include "ares_data.h" #include "ares_private.h" - /* ** ares_free_data() - c-ares external API function. ** @@ -34,12 +33,14 @@ ** function is: ** ** ares_get_servers() +** cares_parse_srv_reply() ** ares_parse_srv_reply() ** ares_parse_txt_reply() */ void ares_free_data(void *dataptr) { + while (dataptr != NULL) { struct ares_data *ptr; void *next_data = NULL; @@ -56,12 +57,15 @@ void ares_free_data(void *dataptr) # pragma warning(pop) #endif - if (ptr->mark != ARES_DATATYPE_MARK) + if (ptr->mark != ARES_DATATYPE_MARK) { return; + } + switch (ptr->type) { case ARES_DATATYPE_MX_REPLY: + case CARES_DATATYPE_MX_REPLY: if (ptr->data.mx_reply.next) next_data = ptr->data.mx_reply.next; @@ -74,7 +78,17 @@ void ares_free_data(void *dataptr) if (ptr->data.srv_reply.next) next_data = ptr->data.srv_reply.next; if (ptr->data.srv_reply.host) + { ares_free(ptr->data.srv_reply.host); + } + break; + + case CARES_DATATYPE_SRV_REPLY: + + if (ptr->data.csrv_reply.host) + { + ares_free(ptr->data.csrv_reply.host); + } break; case ARES_DATATYPE_URI_REPLY: @@ -87,6 +101,7 @@ void ares_free_data(void *dataptr) case ARES_DATATYPE_TXT_REPLY: case ARES_DATATYPE_TXT_EXT: + case CARES_DATATYPE_TXT_REPLY: if (ptr->data.txt_reply.next) next_data = ptr->data.txt_reply.next; @@ -107,6 +122,7 @@ void ares_free_data(void *dataptr) break; case ARES_DATATYPE_NAPTR_REPLY: + case CARES_DATATYPE_NAPTR_REPLY: if (ptr->data.naptr_reply.next) next_data = ptr->data.naptr_reply.next; @@ -121,6 +137,7 @@ void ares_free_data(void *dataptr) break; case ARES_DATATYPE_SOA_REPLY: + case CARES_DATATYPE_SOA_REPLY: if (ptr->data.soa_reply.nsname) ares_free(ptr->data.soa_reply.nsname); if (ptr->data.soa_reply.hostmaster) @@ -128,7 +145,7 @@ void ares_free_data(void *dataptr) break; case ARES_DATATYPE_CAA_REPLY: - + case CARES_DATATYPE_CAA_REPLY: if (ptr->data.caa_reply.next) next_data = ptr->data.caa_reply.next; if (ptr->data.caa_reply.property) @@ -137,10 +154,27 @@ void ares_free_data(void *dataptr) ares_free(ptr->data.caa_reply.value); break; + case CARES_DATATYPE_PTR_REPLY: + + if (ptr->data.cptr_reply.next) + next_data = ptr->data.cptr_reply.next; + if (ptr->data.cptr_reply.host) + ares_free(ptr->data.cptr_reply.host); + break; + + case CARES_DATATYPE_NS_REPLY: + + if (ptr->data.cns_reply.next) + next_data = ptr->data.cns_reply.next; + if (ptr->data.cns_reply.host) + { + ares_free(ptr->data.cns_reply.host); + } + break; + default: return; } - ares_free(ptr); dataptr = next_data; } @@ -168,12 +202,24 @@ void *ares_malloc_data(ares_datatype type) switch (type) { + case CARES_DATATYPE_MX_REPLY: + ptr->data.cmx_reply.ttl = 0; + /* FALLTHROUGH */ + case ARES_DATATYPE_MX_REPLY: ptr->data.mx_reply.next = NULL; ptr->data.mx_reply.host = NULL; ptr->data.mx_reply.priority = 0; break; + case CARES_DATATYPE_SRV_REPLY: + ptr->data.csrv_reply.host = NULL; + ptr->data.csrv_reply.priority = 0; + ptr->data.csrv_reply.weight = 0; + ptr->data.csrv_reply.port = 0; + ptr->data.csrv_reply.ttl = 0; + break; + case ARES_DATATYPE_SRV_REPLY: ptr->data.srv_reply.next = NULL; ptr->data.srv_reply.host = NULL; @@ -190,6 +236,10 @@ void *ares_malloc_data(ares_datatype type) ptr->data.uri_reply.ttl = 0; break; + case CARES_DATATYPE_TXT_REPLY: + ptr->data.ctxt_reply.ttl = 0; + /* FALLTHROUGH */ + case ARES_DATATYPE_TXT_EXT: ptr->data.txt_ext.record_start = 0; /* FALLTHROUGH */ @@ -200,6 +250,10 @@ void *ares_malloc_data(ares_datatype type) ptr->data.txt_reply.length = 0; break; + case CARES_DATATYPE_CAA_REPLY: + ptr->data.ccaa_reply.ttl = 0; + /* FALLTHROUGH */ + case ARES_DATATYPE_CAA_REPLY: ptr->data.caa_reply.next = NULL; ptr->data.caa_reply.plength = 0; @@ -212,7 +266,7 @@ void *ares_malloc_data(ares_datatype type) ptr->data.addr_node.next = NULL; ptr->data.addr_node.family = 0; memset(&ptr->data.addr_node.addrV6, 0, - sizeof(ptr->data.addr_node.addrV6)); + sizeof(ptr->data.addr_node.addrV6)); break; case ARES_DATATYPE_ADDR_PORT_NODE: @@ -221,9 +275,13 @@ void *ares_malloc_data(ares_datatype type) ptr->data.addr_port_node.udp_port = 0; ptr->data.addr_port_node.tcp_port = 0; memset(&ptr->data.addr_port_node.addrV6, 0, - sizeof(ptr->data.addr_port_node.addrV6)); + sizeof(ptr->data.addr_port_node.addrV6)); break; + case CARES_DATATYPE_NAPTR_REPLY: + ptr->data.cnaptr_reply.ttl = 0; + /* FALLTHROUGH */ + case ARES_DATATYPE_NAPTR_REPLY: ptr->data.naptr_reply.next = NULL; ptr->data.naptr_reply.flags = NULL; @@ -234,6 +292,10 @@ void *ares_malloc_data(ares_datatype type) ptr->data.naptr_reply.preference = 0; break; + case CARES_DATATYPE_SOA_REPLY: + ptr->data.csoa_reply.ttl = 0; + /* FALLTHROUGH */ + case ARES_DATATYPE_SOA_REPLY: ptr->data.soa_reply.nsname = NULL; ptr->data.soa_reply.hostmaster = NULL; @@ -242,7 +304,20 @@ void *ares_malloc_data(ares_datatype type) ptr->data.soa_reply.retry = 0; ptr->data.soa_reply.expire = 0; ptr->data.soa_reply.minttl = 0; - break; + break; + + case CARES_DATATYPE_PTR_REPLY: + ptr->data.cptr_reply.next = NULL; + ptr->data.cptr_reply.host = NULL; + ptr->data.cptr_reply.ttl = 0; + break; + + case CARES_DATATYPE_NS_REPLY: + ptr->data.cns_reply.next = NULL; + ptr->data.cns_reply.host = NULL; + ptr->data.cns_reply.ttl = 0; + break; + default: ares_free(ptr); diff --git a/src/lib/ares_data.h b/src/lib/ares_data.h index 6b9dd9f8f7..ba225e5ed9 100644 --- a/src/lib/ares_data.h +++ b/src/lib/ares_data.h @@ -14,15 +14,21 @@ * without express or implied warranty. */ +#include "ares_private.h" typedef enum { ARES_DATATYPE_UNKNOWN = 1, /* unknown data type - introduced in 1.7.0 */ ARES_DATATYPE_SRV_REPLY, /* struct ares_srv_reply - introduced in 1.7.0 */ + CARES_DATATYPE_SRV_REPLY, /* struct cares_srv_reply */ ARES_DATATYPE_TXT_REPLY, /* struct ares_txt_reply - introduced in 1.7.0 */ ARES_DATATYPE_TXT_EXT, /* struct ares_txt_ext - introduced in 1.11.0 */ + CARES_DATATYPE_TXT_REPLY, ARES_DATATYPE_ADDR_NODE, /* struct ares_addr_node - introduced in 1.7.1 */ ARES_DATATYPE_MX_REPLY, /* struct ares_mx_reply - introduced in 1.7.2 */ + CARES_DATATYPE_MX_REPLY, ARES_DATATYPE_NAPTR_REPLY,/* struct ares_naptr_reply - introduced in 1.7.6 */ + CARES_DATATYPE_NAPTR_REPLY, ARES_DATATYPE_SOA_REPLY, /* struct ares_soa_reply - introduced in 1.9.0 */ + CARES_DATATYPE_SOA_REPLY, ARES_DATATYPE_URI_REPLY, /* struct ares_uri_reply */ #if 0 ARES_DATATYPE_ADDR6TTL, /* struct ares_addrttl */ @@ -32,6 +38,9 @@ typedef enum { #endif ARES_DATATYPE_ADDR_PORT_NODE, /* struct ares_addr_port_node - introduced in 1.11.0 */ ARES_DATATYPE_CAA_REPLY, /* struct ares_caa_reply - introduced in 1.17 */ + CARES_DATATYPE_CAA_REPLY, + CARES_DATATYPE_PTR_REPLY, + CARES_DATATYPE_NS_REPLY, ARES_DATATYPE_LAST /* not used - introduced in 1.7.0 */ } ares_datatype; @@ -61,13 +70,21 @@ struct ares_data { union { struct ares_txt_reply txt_reply; struct ares_txt_ext txt_ext; + struct cares_txt_reply ctxt_reply; struct ares_srv_reply srv_reply; + struct cares_srv_reply csrv_reply; struct ares_addr_node addr_node; struct ares_addr_port_node addr_port_node; struct ares_mx_reply mx_reply; + struct cares_mx_reply cmx_reply; struct ares_naptr_reply naptr_reply; + struct cares_naptr_reply cnaptr_reply; struct ares_soa_reply soa_reply; + struct cares_soa_reply csoa_reply; struct ares_caa_reply caa_reply; + struct cares_caa_reply ccaa_reply; + struct cares_ptr_reply cptr_reply; + struct cares_ns_reply cns_reply; struct ares_uri_reply uri_reply; } data; }; diff --git a/src/lib/ares_parse_caa_reply.c b/src/lib/ares_parse_caa_reply.c index f6d4d3c61f..ec5e5bb83d 100644 --- a/src/lib/ares_parse_caa_reply.c +++ b/src/lib/ares_parse_caa_reply.c @@ -36,163 +36,88 @@ #include "ares_dns.h" #include "ares_data.h" #include "ares_private.h" +#include "cares_memdup.h" int ares_parse_caa_reply (const unsigned char *abuf, int alen, struct ares_caa_reply **caa_out) { - unsigned int qdcount, ancount, i; - const unsigned char *aptr; - const unsigned char *strptr; - int status, rr_type, rr_class, rr_len; - long len; - char *hostname = NULL, *rr_name = NULL; + int status; struct ares_caa_reply *caa_head = NULL; struct ares_caa_reply *caa_last = NULL; struct ares_caa_reply *caa_curr; + const cares_caa_reply *ccaa_curr = NULL; + cares_caa_reply *ccaa_out = NULL; /* Set *caa_out to NULL for all failure cases. */ *caa_out = NULL; - /* Give up if abuf doesn't have room for a header. */ - if (alen < HFIXEDSZ) - return ARES_EBADRESP; - - /* Fetch the question and answer count from the header. */ - qdcount = DNS_HEADER_QDCOUNT (abuf); - ancount = DNS_HEADER_ANCOUNT (abuf); - if (qdcount != 1) - return ARES_EBADRESP; - if (ancount == 0) - return ARES_ENODATA; - - /* Expand the name from the question, and skip past the question. */ - aptr = abuf + HFIXEDSZ; - status = ares_expand_name (aptr, abuf, alen, &hostname, &len); + status = cares_parse_caa_reply(abuf, alen, &ccaa_out); + if (status != ARES_SUCCESS) + { + if (ccaa_out) + ares_free_data(ccaa_out); return status; - - if (aptr + len + QFIXEDSZ > abuf + alen) + } + + /* iterate through the cares_caa_reply list and + * create a new ares_caa_reply */ + for(ccaa_curr = ccaa_out; ccaa_curr; + ccaa_curr = cares_caa_reply_get_next(ccaa_curr)) + { + const unsigned char* property; + const unsigned char* value; + caa_curr = ares_malloc_data(ARES_DATATYPE_CAA_REPLY); + if (!caa_curr) { - ares_free (hostname); - return ARES_EBADRESP; + status = ARES_ENOMEM; + break; } - aptr += len + QFIXEDSZ; - - /* Examine each answer resource record (RR) in turn. */ - for (i = 0; i < ancount; i++) + if (caa_last) { - /* Decode the RR up to the data field. */ - status = ares_expand_name (aptr, abuf, alen, &rr_name, &len); - if (status != ARES_SUCCESS) - { - break; - } - aptr += len; - if (aptr + RRFIXEDSZ > abuf + alen) - { - status = ARES_EBADRESP; - break; - } - rr_type = DNS_RR_TYPE (aptr); - rr_class = DNS_RR_CLASS (aptr); - rr_len = DNS_RR_LEN (aptr); - aptr += RRFIXEDSZ; - if (aptr + rr_len > abuf + alen) - { - status = ARES_EBADRESP; - break; - } - - /* Check if we are really looking at a CAA record */ - if ((rr_class == C_IN || rr_class == C_CHAOS) && rr_type == T_CAA) - { - strptr = aptr; - - /* Allocate storage for this CAA answer appending it to the list */ - caa_curr = ares_malloc_data(ARES_DATATYPE_CAA_REPLY); - if (!caa_curr) - { - status = ARES_ENOMEM; - break; - } - if (caa_last) - { - caa_last->next = caa_curr; - } - else - { - caa_head = caa_curr; - } - caa_last = caa_curr; - if (rr_len < 2) - { - status = ARES_EBADRESP; - break; - } - caa_curr->critical = (int)*strptr++; - caa_curr->plength = (int)*strptr++; - if (caa_curr->plength <= 0 || (int)caa_curr->plength >= rr_len - 2) - { - status = ARES_EBADRESP; - break; - } - caa_curr->property = ares_malloc (caa_curr->plength + 1/* Including null byte */); - if (caa_curr->property == NULL) - { - status = ARES_ENOMEM; - break; - } - memcpy ((char *) caa_curr->property, strptr, caa_curr->plength); - /* Make sure we NULL-terminate */ - caa_curr->property[caa_curr->plength] = 0; - strptr += caa_curr->plength; - - caa_curr->length = rr_len - caa_curr->plength - 2; - if (caa_curr->length <= 0) - { - status = ARES_EBADRESP; - break; - } - caa_curr->value = ares_malloc (caa_curr->length + 1/* Including null byte */); - if (caa_curr->value == NULL) - { - status = ARES_ENOMEM; - break; - } - memcpy ((char *) caa_curr->value, strptr, caa_curr->length); - /* Make sure we NULL-terminate */ - caa_curr->value[caa_curr->length] = 0; - } - - /* Propagate any failures */ - if (status != ARES_SUCCESS) - { - break; - } - - /* Don't lose memory in the next iteration */ - ares_free (rr_name); - rr_name = NULL; - - /* Move on to the next record */ - aptr += rr_len; + caa_last->next = caa_curr; + } + else { + caa_head = caa_curr; + } + caa_last = caa_curr; + + /* fill in the ares_caa_reply fields */ + caa_curr->critical = cares_caa_reply_get_critical(ccaa_curr); + + property = cares_caa_reply_get_property(ccaa_curr); + caa_curr->property = cares_memdup(property, cares_caa_reply_get_plength(ccaa_curr)); + if (!caa_curr->property) { + status = ARES_ENOMEM; + break; } - if (hostname) - ares_free (hostname); - if (rr_name) - ares_free (rr_name); + caa_curr->plength = cares_caa_reply_get_plength(ccaa_curr); + + value = cares_caa_reply_get_value(ccaa_curr); + caa_curr->value = cares_memdup(value, cares_caa_reply_get_length(ccaa_curr)); + if (!caa_curr->value) { + status = ARES_ENOMEM; + break; + } + + caa_curr->length = cares_caa_reply_get_length(ccaa_curr); + } + + if (ccaa_out) + { + ares_free_data(ccaa_out); + } /* clean up on error */ if (status != ARES_SUCCESS) - { - if (caa_head) - ares_free_data (caa_head); - return status; - } + { + if (caa_head) + ares_free_data(caa_head); + return status; + } - /* everything looks fine, return the data */ *caa_out = caa_head; return ARES_SUCCESS; diff --git a/src/lib/ares_parse_mx_reply.c b/src/lib/ares_parse_mx_reply.c index a497f55873..0c088f2469 100644 --- a/src/lib/ares_parse_mx_reply.c +++ b/src/lib/ares_parse_mx_reply.c @@ -1,6 +1,5 @@ /* Copyright 1998 by the Massachusetts Institute of Technology. - * Copyright (C) 2010 Jeremy Lal * * Permission to use, copy, modify, and distribute this * software and its documentation for any purpose and without @@ -16,6 +15,7 @@ */ #include "ares_setup.h" +#include "ares_strdup.h" #ifdef HAVE_NETINET_IN_H # include @@ -36,126 +36,77 @@ int ares_parse_mx_reply (const unsigned char *abuf, int alen, - struct ares_mx_reply **mx_out) + struct ares_mx_reply **mx_out) { - unsigned int qdcount, ancount, i; - const unsigned char *aptr, *vptr; - int status, rr_type, rr_class, rr_len; - long len; - char *hostname = NULL, *rr_name = NULL; + + /* call cares_parse_mx_reply, iterate through the result and + * create a linked list of struct ares_mx_reply to return */ + + int status; + char* newhost; struct ares_mx_reply *mx_head = NULL; + struct ares_mx_reply *mx_curr = NULL; struct ares_mx_reply *mx_last = NULL; - struct ares_mx_reply *mx_curr; + const cares_mx_reply *cmx_curr = NULL; + cares_mx_reply *cmx_out = NULL; /* Set *mx_out to NULL for all failure cases. */ *mx_out = NULL; - /* Give up if abuf doesn't have room for a header. */ - if (alen < HFIXEDSZ) - return ARES_EBADRESP; - - /* Fetch the question and answer count from the header. */ - qdcount = DNS_HEADER_QDCOUNT (abuf); - ancount = DNS_HEADER_ANCOUNT (abuf); - if (qdcount != 1) - return ARES_EBADRESP; - if (ancount == 0) - return ARES_ENODATA; - - /* Expand the name from the question, and skip past the question. */ - aptr = abuf + HFIXEDSZ; - status = ares_expand_name (aptr, abuf, alen, &hostname, &len); + status = cares_parse_mx_reply(abuf, alen, &cmx_out); + + /* clean up on error */ if (status != ARES_SUCCESS) + { + if (cmx_out) + ares_free_data (cmx_out); return status; - - if (aptr + len + QFIXEDSZ > abuf + alen) + } + + /* iterate through the cares_mx_reply list and + * create a new ares_mx_reply */ + for (cmx_curr = cmx_out; cmx_curr; + cmx_curr = cares_mx_reply_get_next(cmx_curr)) + { + mx_curr = ares_malloc_data(ARES_DATATYPE_MX_REPLY); + if (!mx_curr) { - ares_free (hostname); - return ARES_EBADRESP; + status = ARES_ENOMEM; + break; } - aptr += len + QFIXEDSZ; - - /* Examine each answer resource record (RR) in turn. */ - for (i = 0; i < ancount; i++) + if (mx_last) + { + mx_last->next = mx_curr; + } + else { - /* Decode the RR up to the data field. */ - status = ares_expand_name (aptr, abuf, alen, &rr_name, &len); - if (status != ARES_SUCCESS) - { - break; - } - aptr += len; - if (aptr + RRFIXEDSZ > abuf + alen) - { - status = ARES_EBADRESP; - break; - } - rr_type = DNS_RR_TYPE (aptr); - rr_class = DNS_RR_CLASS (aptr); - rr_len = DNS_RR_LEN (aptr); - aptr += RRFIXEDSZ; - if (aptr + rr_len > abuf + alen) - { - status = ARES_EBADRESP; - break; - } - - /* Check if we are really looking at a MX record */ - if (rr_class == C_IN && rr_type == T_MX) - { - /* parse the MX record itself */ - if (rr_len < 2) - { - status = ARES_EBADRESP; - break; - } - - /* Allocate storage for this MX answer appending it to the list */ - mx_curr = ares_malloc_data(ARES_DATATYPE_MX_REPLY); - if (!mx_curr) - { - status = ARES_ENOMEM; - break; - } - if (mx_last) - { - mx_last->next = mx_curr; - } - else - { - mx_head = mx_curr; - } - mx_last = mx_curr; - - vptr = aptr; - mx_curr->priority = DNS__16BIT(vptr); - vptr += sizeof(unsigned short); - - status = ares_expand_name (vptr, abuf, alen, &mx_curr->host, &len); - if (status != ARES_SUCCESS) - break; - } - - /* Don't lose memory in the next iteration */ - ares_free (rr_name); - rr_name = NULL; - - /* Move on to the next record */ - aptr += rr_len; + mx_head = mx_curr; + } + mx_last = mx_curr; + + /* copy the host to newhost so we can free cmx_out */ + newhost = ares_strdup(cares_mx_reply_get_host(cmx_curr)); + if (!newhost) { + status = ARES_ENOMEM; + break; } - if (hostname) - ares_free (hostname); - if (rr_name) - ares_free (rr_name); + mx_curr->host = newhost; + mx_curr->priority = cares_mx_reply_get_priority(cmx_curr); + } + + if (cmx_out) + { + ares_free_data (cmx_out); + } /* clean up on error */ if (status != ARES_SUCCESS) - { - if (mx_head) - ares_free_data (mx_head); - return status; - } + { + if (mx_head) + ares_free_data (mx_head); + return status; + } /* everything looks fine, return the data */ *mx_out = mx_head; diff --git a/src/lib/ares_parse_naptr_reply.c b/src/lib/ares_parse_naptr_reply.c index dd984c0fea..0bc027be39 100644 --- a/src/lib/ares_parse_naptr_reply.c +++ b/src/lib/ares_parse_naptr_reply.c @@ -33,150 +33,107 @@ #include "ares_dns.h" #include "ares_data.h" #include "ares_private.h" +#include "cares_memdup.h" int ares_parse_naptr_reply (const unsigned char *abuf, int alen, struct ares_naptr_reply **naptr_out) { - unsigned int qdcount, ancount, i; - const unsigned char *aptr, *vptr; - int status, rr_type, rr_class, rr_len; - long len; - char *hostname = NULL, *rr_name = NULL; + int status; + const unsigned char* flags = NULL; + const unsigned char* service = NULL; + const unsigned char* regexp = NULL; + char* replacement = NULL; + unsigned long len; struct ares_naptr_reply *naptr_head = NULL; struct ares_naptr_reply *naptr_last = NULL; struct ares_naptr_reply *naptr_curr; + const cares_naptr_reply *cnaptr_curr = NULL; + cares_naptr_reply *cnaptr_out = NULL; /* Set *naptr_out to NULL for all failure cases. */ *naptr_out = NULL; - /* Give up if abuf doesn't have room for a header. */ - if (alen < HFIXEDSZ) - return ARES_EBADRESP; - - /* Fetch the question and answer count from the header. */ - qdcount = DNS_HEADER_QDCOUNT (abuf); - ancount = DNS_HEADER_ANCOUNT (abuf); - if (qdcount != 1) - return ARES_EBADRESP; - if (ancount == 0) - return ARES_ENODATA; - - /* Expand the name from the question, and skip past the question. */ - aptr = abuf + HFIXEDSZ; - status = ares_expand_name (aptr, abuf, alen, &hostname, &len); + status = cares_parse_naptr_reply(abuf, alen, &cnaptr_out); + if (status != ARES_SUCCESS) + { + if (cnaptr_out) + ares_free_data(cnaptr_out); return status; - - if (aptr + len + QFIXEDSZ > abuf + alen) + } + + /* iterate through the cares_naptr_reply list and + * create a new ares_naptr_reply */ + for(cnaptr_curr = cnaptr_out; cnaptr_curr; + cnaptr_curr = cares_naptr_reply_get_next(cnaptr_curr)) + { + naptr_curr = ares_malloc_data(ARES_DATATYPE_NAPTR_REPLY); + if (!naptr_curr) { - ares_free (hostname); - return ARES_EBADRESP; + status = ARES_ENOMEM; + break; } - aptr += len + QFIXEDSZ; - - /* Examine each answer resource record (RR) in turn. */ - for (i = 0; i < ancount; i++) + if (naptr_last) { - /* Decode the RR up to the data field. */ - status = ares_expand_name (aptr, abuf, alen, &rr_name, &len); - if (status != ARES_SUCCESS) - { - break; - } - aptr += len; - if (aptr + RRFIXEDSZ > abuf + alen) - { - status = ARES_EBADRESP; - break; - } - rr_type = DNS_RR_TYPE (aptr); - rr_class = DNS_RR_CLASS (aptr); - rr_len = DNS_RR_LEN (aptr); - aptr += RRFIXEDSZ; - if (aptr + rr_len > abuf + alen) - { - status = ARES_EBADRESP; - break; - } - - /* Check if we are really looking at a NAPTR record */ - if (rr_class == C_IN && rr_type == T_NAPTR) - { - /* parse the NAPTR record itself */ - - /* RR must contain at least 7 bytes = 2 x int16 + 3 x name */ - if (rr_len < 7) - { - status = ARES_EBADRESP; - break; - } - - /* Allocate storage for this NAPTR answer appending it to the list */ - naptr_curr = ares_malloc_data(ARES_DATATYPE_NAPTR_REPLY); - if (!naptr_curr) - { - status = ARES_ENOMEM; - break; - } - if (naptr_last) - { - naptr_last->next = naptr_curr; - } - else - { - naptr_head = naptr_curr; - } - naptr_last = naptr_curr; - - vptr = aptr; - naptr_curr->order = DNS__16BIT(vptr); - vptr += sizeof(unsigned short); - naptr_curr->preference = DNS__16BIT(vptr); - vptr += sizeof(unsigned short); - - status = ares_expand_string(vptr, abuf, alen, &naptr_curr->flags, &len); - if (status != ARES_SUCCESS) - break; - vptr += len; - - status = ares_expand_string(vptr, abuf, alen, &naptr_curr->service, &len); - if (status != ARES_SUCCESS) - break; - vptr += len; - - status = ares_expand_string(vptr, abuf, alen, &naptr_curr->regexp, &len); - if (status != ARES_SUCCESS) - break; - vptr += len; - - status = ares_expand_name(vptr, abuf, alen, &naptr_curr->replacement, &len); - if (status != ARES_SUCCESS) - break; - } - - /* Don't lose memory in the next iteration */ - ares_free (rr_name); - rr_name = NULL; - - /* Move on to the next record */ - aptr += rr_len; + naptr_last->next = naptr_curr; + } + else { + naptr_head = naptr_curr; + } + naptr_last = naptr_curr; + + /* fill in the ares_naptr_reply fields */ + naptr_curr->order = cares_naptr_reply_get_order(cnaptr_curr); + naptr_curr->preference = cares_naptr_reply_get_preference(cnaptr_curr); + + flags = cares_naptr_reply_get_flags(cnaptr_curr); + len = strlen((char *)flags); + naptr_curr->flags = cares_memdup(flags, len); + if (!naptr_curr->flags) { + status = ARES_ENOMEM; + break; + } + + service = cares_naptr_reply_get_service(cnaptr_curr); + len = strlen((char *)service); + naptr_curr->service = cares_memdup(service, len); + if (!naptr_curr->service) { + status = ARES_ENOMEM; + break; } - if (hostname) - ares_free (hostname); - if (rr_name) - ares_free (rr_name); + regexp = cares_naptr_reply_get_regexp(cnaptr_curr); + len = strlen((char *)regexp); + naptr_curr->regexp = cares_memdup(regexp, len); + if (!naptr_curr->regexp) { + status = ARES_ENOMEM; + break; + } + + replacement = ares_strdup( + cares_naptr_reply_get_replacement(cnaptr_curr)); + if (!replacement) { + status = ARES_ENOMEM; + break; + } + + naptr_curr->replacement = replacement; + } + + if (cnaptr_out) + { + ares_free_data(cnaptr_out); + } /* clean up on error */ if (status != ARES_SUCCESS) - { - if (naptr_head) - ares_free_data (naptr_head); - return status; - } + { + if (naptr_head) + ares_free_data(naptr_head); + return status; + } - /* everything looks fine, return the data */ *naptr_out = naptr_head; return ARES_SUCCESS; diff --git a/src/lib/ares_parse_ns_reply.c b/src/lib/ares_parse_ns_reply.c index 47d12994c9..720487f8f1 100644 --- a/src/lib/ares_parse_ns_reply.c +++ b/src/lib/ares_parse_ns_reply.c @@ -39,139 +39,115 @@ int ares_parse_ns_reply( const unsigned char* abuf, int alen, struct hostent** host ) { - unsigned int qdcount, ancount; - int status, i, rr_type, rr_class, rr_len; - int nameservers_num; - long len; + struct hostent *hostent = NULL; + char *hname = NULL; const unsigned char *aptr; - char* hostname, *rr_name, *rr_data, **nameservers; - struct hostent *hostent; + int status, i; + long len; + int alias_alloc = 2; + cares_ns_reply* ns_out = NULL; /* Set *host to NULL for all failure cases. */ *host = NULL; - /* Give up if abuf doesn't have room for a header. */ - if ( alen < HFIXEDSZ ) - return ARES_EBADRESP; - - /* Fetch the question and answer count from the header. */ - qdcount = DNS_HEADER_QDCOUNT( abuf ); - ancount = DNS_HEADER_ANCOUNT( abuf ); - if ( qdcount != 1 ) - return ARES_EBADRESP; - - /* Expand the name from the question, and skip past the question. */ - aptr = abuf + HFIXEDSZ; - status = ares__expand_name_for_response( aptr, abuf, alen, &hostname, &len, 0); - if ( status != ARES_SUCCESS ) - return status; - if ( aptr + len + QFIXEDSZ > abuf + alen ) - { - ares_free( hostname ); - return ARES_EBADRESP; - } - aptr += len + QFIXEDSZ; + status = cares_parse_ns_reply(abuf, alen, &ns_out); - /* Allocate nameservers array; ancount gives an upper bound */ - nameservers = ares_malloc( ( ancount + 1 ) * sizeof( char * ) ); - if ( !nameservers ) + /* clean up on error */ + if (status != ARES_SUCCESS) { - ares_free( hostname ); - return ARES_ENOMEM; + if (ns_out) + ares_free_data(ns_out); + return status; } - nameservers_num = 0; - /* Examine each answer resource record (RR) in turn. */ - for ( i = 0; i < ( int ) ancount; i++ ) + aptr = abuf + HFIXEDSZ; + status = ares__expand_name_for_response(aptr, abuf, alen, &hname, &len, 0); + if (status != ARES_SUCCESS) { - /* Decode the RR up to the data field. */ - status = ares__expand_name_for_response( aptr, abuf, alen, &rr_name, &len, 0); - if ( status != ARES_SUCCESS ) - break; - aptr += len; - if ( aptr + RRFIXEDSZ > abuf + alen ) - { - status = ARES_EBADRESP; - ares_free(rr_name); - break; - } - rr_type = DNS_RR_TYPE( aptr ); - rr_class = DNS_RR_CLASS( aptr ); - rr_len = DNS_RR_LEN( aptr ); - aptr += RRFIXEDSZ; - if (aptr + rr_len > abuf + alen) - { - ares_free(rr_name); - status = ARES_EBADRESP; - break; - } - - if ( rr_class == C_IN && rr_type == T_NS ) - { - /* Decode the RR data and add it to the nameservers list */ - status = ares__expand_name_for_response( aptr, abuf, alen, &rr_data, - &len, 1); - if ( status != ARES_SUCCESS ) - { - ares_free(rr_name); - break; - } - - nameservers[nameservers_num] = ares_malloc(strlen(rr_data)+1); - - if (nameservers[nameservers_num]==NULL) - { - ares_free(rr_name); - ares_free(rr_data); - status=ARES_ENOMEM; - break; - } - strcpy(nameservers[nameservers_num],rr_data); - ares_free(rr_data); - - nameservers_num++; - } - - ares_free( rr_name ); - - aptr += rr_len; - if ( aptr > abuf + alen ) - { /* LCOV_EXCL_START: already checked above */ - status = ARES_EBADRESP; - break; - } /* LCOV_EXCL_STOP */ + ares_free_data(ns_out); + return status; } - if ( status == ARES_SUCCESS && nameservers_num == 0 ) + /* We got our answer. Allocate memory to build the host entry. */ + hostent = ares_malloc(sizeof(*hostent)); + if (hostent) { - status = ARES_ENODATA; - } - if ( status == ARES_SUCCESS ) - { - /* We got our answer. Allocate memory to build the host entry. */ - nameservers[nameservers_num] = NULL; - hostent = ares_malloc( sizeof( struct hostent ) ); - if ( hostent ) + hostent->h_addr_list = ares_malloc(sizeof(char *)); + if (hostent->h_addr_list) { - hostent->h_addr_list = ares_malloc( 1 * sizeof( char * ) ); - if ( hostent->h_addr_list ) + hostent->h_aliases = ares_malloc(alias_alloc * sizeof (char *)); + if (hostent->h_aliases) { /* Fill in the hostent and return successfully. */ - hostent->h_name = hostname; - hostent->h_aliases = nameservers; + hostent->h_name = hname; + + /* iterate through the linked list of cares_ns_reply + and build the h_aliases array. */ + i = 0; + for (const cares_ns_reply* ns_curr=ns_out; ns_curr; + ns_curr = cares_ns_reply_get_next(ns_curr)) + { + if (alias_alloc > 2) + { + char** ptr; + ptr = ares_realloc(hostent->h_aliases, + alias_alloc * sizeof(char *)); + if (!ptr) + { + status = ARES_ENOMEM; + if (ns_out) + ares_free_data(ns_out); + + for (int j = 0; j < i; ++j) + { + if (hostent->h_aliases[j]) + ares_free(hostent->h_aliases[j]); + } + ares_free(hostent->h_name); + ares_free(hostent->h_aliases); + ares_free(hostent->h_addr_list); + ares_free(hostent); + return status; + } + hostent->h_aliases = ptr; + } + hostent->h_aliases[i] = ares_strdup(cares_ns_reply_get_host(ns_curr)); + if (!hostent->h_aliases[i]) { + status = ARES_ENOMEM; + if (ns_out) + ares_free_data(ns_out); + + for (int j = 0; j < i; ++j) + { + if (hostent->h_aliases[j]) + ares_free(hostent->h_aliases[j]); + } + ares_free(hostent->h_name); + ares_free(hostent->h_aliases); + ares_free(hostent->h_addr_list); + ares_free(hostent); + return status; + } + i++; + alias_alloc++; + } + hostent->h_aliases[i] = NULL; hostent->h_addrtype = AF_INET; hostent->h_length = sizeof( struct in_addr ); hostent->h_addr_list[0] = NULL; *host = hostent; + if (ns_out) + ares_free_data(ns_out); + return ARES_SUCCESS; } - ares_free( hostent ); + ares_free(hostent->h_addr_list); } - status = ARES_ENOMEM; + ares_free(hostent); } - for ( i = 0; i < nameservers_num; i++ ) - ares_free( nameservers[i] ); - ares_free( nameservers ); - ares_free( hostname ); - return status; + ares_free(hname); + if (ns_out) + ares_free_data(ns_out); + + return ARES_ENOMEM; } diff --git a/src/lib/ares_parse_ptr_reply.c b/src/lib/ares_parse_ptr_reply.c index ae78edf195..5f1ed2f820 100644 --- a/src/lib/ares_parse_ptr_reply.c +++ b/src/lib/ares_parse_ptr_reply.c @@ -15,6 +15,7 @@ */ #include "ares_setup.h" +#include "ares_strdup.h" #ifdef HAVE_NETINET_IN_H # include @@ -37,192 +38,156 @@ int ares_parse_ptr_reply(const unsigned char *abuf, int alen, const void *addr, int addrlen, int family, struct hostent **host) { - unsigned int qdcount, ancount; - int status, i, rr_type, rr_class, rr_len; - long len; - const unsigned char *aptr; - char *ptrname, *hostname, *rr_name, *rr_data; struct hostent *hostent = NULL; - int aliascnt = 0; - int alias_alloc = 8; - char ** aliases; - size_t rr_data_len; + int status, i; + int alias_alloc = 2; + cares_ptr_reply* ptr_out = NULL; /* Set *host to NULL for all failure cases. */ *host = NULL; - /* Give up if abuf doesn't have room for a header. */ - if (alen < HFIXEDSZ) - return ARES_EBADRESP; + status = cares_parse_ptr_reply(abuf, alen, &ptr_out); - /* Fetch the question and answer count from the header. */ - qdcount = DNS_HEADER_QDCOUNT(abuf); - ancount = DNS_HEADER_ANCOUNT(abuf); - if (qdcount != 1) - return ARES_EBADRESP; - - /* Expand the name from the question, and skip past the question. */ - aptr = abuf + HFIXEDSZ; - status = ares__expand_name_for_response(aptr, abuf, alen, &ptrname, &len, 0); + /* clean up on error */ if (status != ARES_SUCCESS) + { + if (ptr_out) + ares_free_data(ptr_out); return status; - if (aptr + len + QFIXEDSZ > abuf + alen) - { - ares_free(ptrname); - return ARES_EBADRESP; - } - aptr += len + QFIXEDSZ; - - /* Examine each answer resource record (RR) in turn. */ - hostname = NULL; - aliases = ares_malloc(alias_alloc * sizeof(char *)); - if (!aliases) - { - ares_free(ptrname); - return ARES_ENOMEM; - } - for (i = 0; i < (int)ancount; i++) + } + + /* We got our answer. Allocate memory to build the host entry. */ + hostent = ares_malloc(sizeof(*hostent)); + if (hostent) + { + hostent->h_addr_list = ares_malloc(2 * sizeof(char *)); + if (hostent->h_addr_list) { - /* Decode the RR up to the data field. */ - status = ares__expand_name_for_response(aptr, abuf, alen, &rr_name, &len, 0); - if (status != ARES_SUCCESS) - break; - aptr += len; - if (aptr + RRFIXEDSZ > abuf + alen) - { - ares_free(rr_name); - status = ARES_EBADRESP; - break; - } - rr_type = DNS_RR_TYPE(aptr); - rr_class = DNS_RR_CLASS(aptr); - rr_len = DNS_RR_LEN(aptr); - aptr += RRFIXEDSZ; - if (aptr + rr_len > abuf + alen) + if (addr && addrlen) + { + hostent->h_addr_list[0] = ares_malloc(addrlen); + if (!hostent->h_addr_list[0]) { - ares_free(rr_name); - status = ARES_EBADRESP; - break; + status = ARES_ENOMEM; + if (ptr_out) + ares_free_data(ptr_out); + ares_free(hostent->h_addr_list); + ares_free(hostent); + return status; } - - if (rr_class == C_IN && rr_type == T_PTR - && strcasecmp(rr_name, ptrname) == 0) + } else { + hostent->h_addr_list[0] = NULL; + } + hostent->h_aliases = ares_malloc(alias_alloc * sizeof (char *)); + if (hostent->h_aliases) + { + /* Fill in the hostent and return successfully. */ + hostent->h_name = NULL; + + /* iterate through the linked list of cares_ptr_reply + and build the h_aliases array. */ + i = 0; + for (const cares_ptr_reply* ptr_curr=ptr_out; ptr_curr; + ptr_curr = cares_ptr_reply_get_next(ptr_curr)) { - /* Decode the RR data and set hostname to it. */ - status = ares__expand_name_for_response(aptr, abuf, alen, &rr_data, - &len, 1); - if (status != ARES_SUCCESS) + if (!cares_ptr_reply_get_next(ptr_curr)) + { + hostent->h_name = ares_strdup(cares_ptr_reply_get_host(ptr_curr)); + if (!hostent->h_name) { - ares_free(rr_name); - break; - } - if (hostname) - ares_free(hostname); - hostname = rr_data; - rr_data_len = strlen(rr_data)+1; - aliases[aliascnt] = ares_malloc(rr_data_len * sizeof(char)); - if (!aliases[aliascnt]) - { - ares_free(rr_name); status = ARES_ENOMEM; - break; + if (ptr_out) + ares_free_data(ptr_out); + + for (int j = 0; j < i; ++j) + { + if (hostent->h_aliases[j]) + { + ares_free(hostent->h_aliases[j]); + } + } + if (hostent->h_aliases) + ares_free(hostent->h_aliases); + if (hostent->h_addr_list[0]) + ares_free(hostent->h_addr_list[0]); + ares_free(hostent->h_addr_list); + ares_free(hostent); + return status; } - strncpy(aliases[aliascnt], rr_data, rr_data_len); - aliascnt++; - if (aliascnt >= alias_alloc) { - char **ptr; - alias_alloc *= 2; - ptr = ares_realloc(aliases, alias_alloc * sizeof(char *)); - if(!ptr) { - ares_free(rr_name); + } + + if (alias_alloc > 2) + { + char** ptr; + ptr = ares_realloc(hostent->h_aliases, + alias_alloc * sizeof(char *)); + if (!ptr) + { status = ARES_ENOMEM; - break; + if (ptr_out) + ares_free_data(ptr_out); + + for (int j = 0; j < i; ++j) + { + if (hostent->h_aliases[j]) + { + ares_free(hostent->h_aliases[j]); + } + } + if (hostent->h_name) + ares_free(hostent->h_name); + ares_free(hostent->h_aliases); + if (hostent->h_addr_list[0]) + ares_free(hostent->h_addr_list[0]); + ares_free(hostent->h_addr_list); + ares_free(hostent); + return status; } - aliases = ptr; + hostent->h_aliases = ptr; } - } + hostent->h_aliases[i] = ares_strdup(cares_ptr_reply_get_host(ptr_curr)); + if (!hostent->h_aliases[i]) { + status = ARES_ENOMEM; + if (ptr_out) + ares_free_data(ptr_out); - if (rr_class == C_IN && rr_type == T_CNAME) - { - /* Decode the RR data and replace ptrname with it. */ - status = ares__expand_name_for_response(aptr, abuf, alen, &rr_data, - &len, 1); - if (status != ARES_SUCCESS) + for (int j = 0; j < i; ++j) { - ares_free(rr_name); - break; + if (hostent->h_aliases[j]) + ares_free(hostent->h_aliases[j]); } - ares_free(ptrname); - ptrname = rr_data; + if (hostent->h_name) + ares_free(hostent->h_name); + ares_free(hostent->h_aliases); + if (hostent->h_addr_list[0]) + ares_free(hostent->h_addr_list[0]); + ares_free(hostent->h_addr_list); + ares_free(hostent); + return status; + } + i++; + alias_alloc++; } - - ares_free(rr_name); - aptr += rr_len; - if (aptr > abuf + alen) - { /* LCOV_EXCL_START: already checked above */ - status = ARES_EBADRESP; - break; - } /* LCOV_EXCL_STOP */ - } - - if (status == ARES_SUCCESS && !hostname) - status = ARES_ENODATA; - if (status == ARES_SUCCESS) - { - /* If we don't reach the end, we must have failed due to out of memory */ - status = ARES_ENOMEM; - - /* We got our answer. Allocate memory to build the host entry. */ - hostent = ares_malloc(sizeof(*hostent)); - if (!hostent) - goto fail; - - /* If we don't memset here, cleanups may fail */ - memset(hostent, 0, sizeof(*hostent)); - - hostent->h_addr_list = ares_malloc(2 * sizeof(char *)); - if (!hostent->h_addr_list) - goto fail; - - - if (addr && addrlen) { - hostent->h_addr_list[0] = ares_malloc(addrlen); - if (!hostent->h_addr_list[0]) - goto fail; - } else { - hostent->h_addr_list[0] = NULL; + hostent->h_aliases[i] = NULL; + hostent->h_addrtype = aresx_sitoss(family); + hostent->h_length = aresx_sitoss(addrlen); + if (addr && addrlen) + memcpy(hostent->h_addr_list[0], addr, addrlen); + hostent->h_addr_list[1] = NULL; + *host = hostent; + if (ptr_out) + ares_free_data(ptr_out); + + return ARES_SUCCESS; } - - hostent->h_aliases = ares_malloc((aliascnt+1) * sizeof (char *)); - if (!hostent->h_aliases) - goto fail; - - /* Fill in the hostent and return successfully. */ - hostent->h_name = hostname; - for (i=0 ; ih_aliases[i] = aliases[i]; - hostent->h_aliases[aliascnt] = NULL; - hostent->h_addrtype = aresx_sitoss(family); - hostent->h_length = aresx_sitoss(addrlen); - if (addr && addrlen) - memcpy(hostent->h_addr_list[0], addr, addrlen); - hostent->h_addr_list[1] = NULL; - *host = hostent; - ares_free(aliases); - ares_free(ptrname); - - return ARES_SUCCESS; + if (hostent->h_addr_list[0]) + ares_free(hostent->h_addr_list[0]); + ares_free(hostent->h_addr_list); } + ares_free(hostent); + } + if (ptr_out) + ares_free_data(ptr_out); -fail: - ares_free_hostent(hostent); - - for (i=0 ; i abuf + alen) - goto failed; - aptr += QFIXEDSZ; - - /* qclass of SOA with multiple answers */ - if (qclass == T_SOA && ancount > 1) - goto failed; - - /* examine all the records, break and return if found soa */ - for (i = 0; i < ancount; i++) { - rr_name = NULL; - status = ares__expand_name_for_response (aptr, abuf, alen, &rr_name, &len, 0); - if (status != ARES_SUCCESS) - { - ares_free(rr_name); - goto failed_stat; - } + if (csoa_out) + ares_free_data (csoa_out); + return status; + } + soa = ares_malloc_data(ARES_DATATYPE_SOA_REPLY); - aptr += len; - if ( aptr + RRFIXEDSZ > abuf + alen ) + nsname = ares_strdup(cares_soa_reply_get_nsname(csoa_out)); + if (!nsname) + { + status = ARES_ENOMEM; + if (soa) { - ares_free(rr_name); - status = ARES_EBADRESP; - goto failed_stat; + ares_free_data (soa); } - rr_type = DNS_RR_TYPE( aptr ); - rr_class = DNS_RR_CLASS( aptr ); - rr_len = DNS_RR_LEN( aptr ); - aptr += RRFIXEDSZ; - if (aptr + rr_len > abuf + alen) - { - ares_free(rr_name); - status = ARES_EBADRESP; - goto failed_stat; - } - if ( rr_class == C_IN && rr_type == T_SOA ) + if (csoa_out) { - /* allocate result struct */ - soa = ares_malloc_data(ARES_DATATYPE_SOA_REPLY); - if (!soa) - { - ares_free(rr_name); - status = ARES_ENOMEM; - goto failed_stat; - } - - /* nsname */ - status = ares__expand_name_for_response(aptr, abuf, alen, &soa->nsname, - &len, 0); - if (status != ARES_SUCCESS) - { - ares_free(rr_name); - goto failed_stat; - } - aptr += len; - - /* hostmaster */ - status = ares__expand_name_for_response(aptr, abuf, alen, - &soa->hostmaster, &len, 0); - if (status != ARES_SUCCESS) - { - ares_free(rr_name); - goto failed_stat; - } - aptr += len; - - /* integer fields */ - if (aptr + 5 * 4 > abuf + alen) - { - ares_free(rr_name); - goto failed; - } - soa->serial = DNS__32BIT(aptr + 0 * 4); - soa->refresh = DNS__32BIT(aptr + 1 * 4); - soa->retry = DNS__32BIT(aptr + 2 * 4); - soa->expire = DNS__32BIT(aptr + 3 * 4); - soa->minttl = DNS__32BIT(aptr + 4 * 4); - - ares_free(qname); - ares_free(rr_name); - - *soa_out = soa; + ares_free_data (csoa_out); + } + return status; + } + soa->nsname = nsname; - return ARES_SUCCESS; + hostmaster = ares_strdup(cares_soa_reply_get_hostmaster(csoa_out)); + if (!hostmaster) + { + status = ARES_ENOMEM; + if (soa) + { + ares_free_data (soa); + } + if (csoa_out) + { + ares_free_data (csoa_out); } - aptr += rr_len; + return status; + } + soa->hostmaster = hostmaster; - ares_free(rr_name); + soa->serial = cares_soa_reply_get_serial(csoa_out); + soa->refresh = cares_soa_reply_get_refresh(csoa_out); + soa->retry = cares_soa_reply_get_retry(csoa_out); + soa->expire = cares_soa_reply_get_expire(csoa_out); + soa->minttl = cares_soa_reply_get_minttl(csoa_out); - if (aptr > abuf + alen) - goto failed_stat; + if (csoa_out) + { + ares_free_data (csoa_out); } - /* no SOA record found */ - status = ARES_EBADRESP; - goto failed_stat; -failed: - status = ARES_EBADRESP; + /* everything looks fine, return the data */ + *soa_out = soa; -failed_stat: - if (soa) - ares_free_data(soa); - if (qname) - ares_free(qname); - return status; + return ARES_SUCCESS; } diff --git a/src/lib/ares_parse_srv_reply.c b/src/lib/ares_parse_srv_reply.c index 0d8f4d2098..bb819e7f62 100644 --- a/src/lib/ares_parse_srv_reply.c +++ b/src/lib/ares_parse_srv_reply.c @@ -16,6 +16,7 @@ */ #include "ares_setup.h" +#include "ares_strdup.h" #ifdef HAVE_NETINET_IN_H # include @@ -38,128 +39,80 @@ int ares_parse_srv_reply (const unsigned char *abuf, int alen, struct ares_srv_reply **srv_out) { - unsigned int qdcount, ancount, i; - const unsigned char *aptr, *vptr; - int status, rr_type, rr_class, rr_len; - long len; - char *hostname = NULL, *rr_name = NULL; + + /* call cares_parse_srv_reply, iterate through the result and + * create a linked list of struct ares_srv_reply to return */ + + int status; + char* newhost = NULL; struct ares_srv_reply *srv_head = NULL; + struct ares_srv_reply *srv_curr = NULL; struct ares_srv_reply *srv_last = NULL; - struct ares_srv_reply *srv_curr; + const cares_srv_reply *csrv_curr = NULL; + cares_srv_reply_container *csrv_out = NULL; /* Set *srv_out to NULL for all failure cases. */ *srv_out = NULL; - /* Give up if abuf doesn't have room for a header. */ - if (alen < HFIXEDSZ) - return ARES_EBADRESP; - - /* Fetch the question and answer count from the header. */ - qdcount = DNS_HEADER_QDCOUNT (abuf); - ancount = DNS_HEADER_ANCOUNT (abuf); - if (qdcount != 1) - return ARES_EBADRESP; - if (ancount == 0) - return ARES_ENODATA; - - /* Expand the name from the question, and skip past the question. */ - aptr = abuf + HFIXEDSZ; - status = ares_expand_name (aptr, abuf, alen, &hostname, &len); + status = cares_parse_srv_reply(abuf, alen, &csrv_out); + + /* clean up on error */ if (status != ARES_SUCCESS) + { + if (csrv_out) + { + cares_free_container(csrv_out); + } return status; - - if (aptr + len + QFIXEDSZ > abuf + alen) + } + + /* iterate through the cares_srv_reply_container and + * create a new ares_srv_reply */ + for (csrv_curr = cares_srv_reply_container_get_first(csrv_out); + !cares_srv_reply_container_at_end(csrv_out); + csrv_curr = cares_srv_reply_container_get_next(csrv_out)) + { + srv_curr = ares_malloc_data(ARES_DATATYPE_SRV_REPLY); + if (!srv_curr) { - ares_free (hostname); - return ARES_EBADRESP; + status = ARES_ENOMEM; + break; } - aptr += len + QFIXEDSZ; - - /* Examine each answer resource record (RR) in turn. */ - for (i = 0; i < ancount; i++) + if (srv_last) { - /* Decode the RR up to the data field. */ - status = ares_expand_name (aptr, abuf, alen, &rr_name, &len); - if (status != ARES_SUCCESS) - { - break; - } - aptr += len; - if (aptr + RRFIXEDSZ > abuf + alen) - { - status = ARES_EBADRESP; - break; - } - rr_type = DNS_RR_TYPE (aptr); - rr_class = DNS_RR_CLASS (aptr); - rr_len = DNS_RR_LEN (aptr); - aptr += RRFIXEDSZ; - if (aptr + rr_len > abuf + alen) - { - status = ARES_EBADRESP; - break; - } - - /* Check if we are really looking at a SRV record */ - if (rr_class == C_IN && rr_type == T_SRV) - { - /* parse the SRV record itself */ - if (rr_len < 6) - { - status = ARES_EBADRESP; - break; - } - - /* Allocate storage for this SRV answer appending it to the list */ - srv_curr = ares_malloc_data(ARES_DATATYPE_SRV_REPLY); - if (!srv_curr) - { - status = ARES_ENOMEM; - break; - } - if (srv_last) - { - srv_last->next = srv_curr; - } - else - { - srv_head = srv_curr; - } - srv_last = srv_curr; - - vptr = aptr; - srv_curr->priority = DNS__16BIT(vptr); - vptr += sizeof(unsigned short); - srv_curr->weight = DNS__16BIT(vptr); - vptr += sizeof(unsigned short); - srv_curr->port = DNS__16BIT(vptr); - vptr += sizeof(unsigned short); - - status = ares_expand_name (vptr, abuf, alen, &srv_curr->host, &len); - if (status != ARES_SUCCESS) - break; - } - - /* Don't lose memory in the next iteration */ - ares_free (rr_name); - rr_name = NULL; - - /* Move on to the next record */ - aptr += rr_len; + srv_last->next = srv_curr; + } + else + { + srv_head = srv_curr; + } + srv_last = srv_curr; + + /* copy the host to newhost so we can free csrv_out */ + newhost = ares_strdup(cares_srv_reply_get_host(csrv_curr)); + if (!newhost) { + status = ARES_ENOMEM; + break; } - if (hostname) - ares_free (hostname); - if (rr_name) - ares_free (rr_name); + srv_curr->host = newhost; + srv_curr->priority = cares_srv_reply_get_priority(csrv_curr); + srv_curr->weight = cares_srv_reply_get_weight(csrv_curr); + srv_curr->port = cares_srv_reply_get_port(csrv_curr); + } + + if (csrv_out) + { + cares_free_container(csrv_out); + } /* clean up on error */ if (status != ARES_SUCCESS) - { - if (srv_head) - ares_free_data (srv_head); - return status; - } + { + if (srv_head) + ares_free_data (srv_head); + return status; + } /* everything looks fine, return the data */ *srv_out = srv_head; diff --git a/src/lib/ares_parse_txt_reply.c b/src/lib/ares_parse_txt_reply.c index 6848a092bb..7ac5059ad5 100644 --- a/src/lib/ares_parse_txt_reply.c +++ b/src/lib/ares_parse_txt_reply.c @@ -37,160 +37,83 @@ #include "ares_dns.h" #include "ares_data.h" #include "ares_private.h" +#include "cares_memdup.h" static int ares__parse_txt_reply (const unsigned char *abuf, int alen, int ex, void **txt_out) { - size_t substr_len; - unsigned int qdcount, ancount, i; - const unsigned char *aptr; - const unsigned char *strptr; - int status, rr_type, rr_class, rr_len; - long len; - char *hostname = NULL, *rr_name = NULL; + /* call cares_parse_txt_reply, iterate through the result and + * create a linked list of struct ares_txt_reply to return */ + + int status; + const unsigned char* txt = NULL; struct ares_txt_ext *txt_head = NULL; + struct ares_txt_ext *txt_curr = NULL; struct ares_txt_ext *txt_last = NULL; - struct ares_txt_ext *txt_curr; + const cares_txt_reply *ctxt_curr = NULL; + cares_txt_reply *ctxt_out = NULL; /* Set *txt_out to NULL for all failure cases. */ *txt_out = NULL; - /* Give up if abuf doesn't have room for a header. */ - if (alen < HFIXEDSZ) - return ARES_EBADRESP; - - /* Fetch the question and answer count from the header. */ - qdcount = DNS_HEADER_QDCOUNT (abuf); - ancount = DNS_HEADER_ANCOUNT (abuf); - if (qdcount != 1) - return ARES_EBADRESP; - if (ancount == 0) - return ARES_ENODATA; - - /* Expand the name from the question, and skip past the question. */ - aptr = abuf + HFIXEDSZ; - status = ares_expand_name (aptr, abuf, alen, &hostname, &len); + status = cares_parse_txt_reply(abuf, alen, &ctxt_out); + + /* clean up on error */ if (status != ARES_SUCCESS) + { + if (ctxt_out) + ares_free_data (ctxt_out); return status; - - if (aptr + len + QFIXEDSZ > abuf + alen) + } + + /* iterate through the cares_txt_reply list and + * create a new ares_txt_reply */ + for (ctxt_curr = ctxt_out; ctxt_curr; + ctxt_curr = cares_txt_reply_get_next(ctxt_curr)) + { + txt_curr = ares_malloc_data(ex ? ARES_DATATYPE_TXT_EXT : + ARES_DATATYPE_TXT_REPLY); + if (!txt_curr) { - ares_free (hostname); - return ARES_EBADRESP; + status = ARES_ENOMEM; + break; } - aptr += len + QFIXEDSZ; - - /* Examine each answer resource record (RR) in turn. */ - for (i = 0; i < ancount; i++) + if (txt_last) + { + txt_last->next = txt_curr; + } + else { - /* Decode the RR up to the data field. */ - status = ares_expand_name (aptr, abuf, alen, &rr_name, &len); - if (status != ARES_SUCCESS) - { - break; - } - aptr += len; - if (aptr + RRFIXEDSZ > abuf + alen) - { - status = ARES_EBADRESP; - break; - } - rr_type = DNS_RR_TYPE (aptr); - rr_class = DNS_RR_CLASS (aptr); - rr_len = DNS_RR_LEN (aptr); - aptr += RRFIXEDSZ; - if (aptr + rr_len > abuf + alen) - { - status = ARES_EBADRESP; - break; - } - - /* Check if we are really looking at a TXT record */ - if ((rr_class == C_IN || rr_class == C_CHAOS) && rr_type == T_TXT) - { - /* - * There may be multiple substrings in a single TXT record. Each - * substring may be up to 255 characters in length, with a - * "length byte" indicating the size of the substring payload. - * RDATA contains both the length-bytes and payloads of all - * substrings contained therein. - */ - - strptr = aptr; - while (strptr < (aptr + rr_len)) - { - substr_len = (unsigned char)*strptr; - if (strptr + substr_len + 1 > aptr + rr_len) - { - status = ARES_EBADRESP; - break; - } - - /* Allocate storage for this TXT answer appending it to the list */ - txt_curr = ares_malloc_data(ex ? ARES_DATATYPE_TXT_EXT : - ARES_DATATYPE_TXT_REPLY); - if (!txt_curr) - { - status = ARES_ENOMEM; - break; - } - if (txt_last) - { - txt_last->next = txt_curr; - } - else - { - txt_head = txt_curr; - } - txt_last = txt_curr; - - if (ex) - txt_curr->record_start = (strptr == aptr); - txt_curr->length = substr_len; - txt_curr->txt = ares_malloc (substr_len + 1/* Including null byte */); - if (txt_curr->txt == NULL) - { - status = ARES_ENOMEM; - break; - } - - ++strptr; - memcpy ((char *) txt_curr->txt, strptr, substr_len); - - /* Make sure we NULL-terminate */ - txt_curr->txt[substr_len] = 0; - - strptr += substr_len; - } - } - - /* Propagate any failures */ - if (status != ARES_SUCCESS) - { - break; - } - - /* Don't lose memory in the next iteration */ - ares_free (rr_name); - rr_name = NULL; - - /* Move on to the next record */ - aptr += rr_len; + txt_head = txt_curr; } + txt_last = txt_curr; - if (hostname) - ares_free (hostname); - if (rr_name) - ares_free (rr_name); + txt = cares_txt_reply_get_txt(ctxt_curr); + txt_curr->txt = cares_memdup(txt, cares_txt_reply_get_length(ctxt_curr)); + if (!txt_curr->txt) { + status = ARES_ENOMEM; + break; + } + + txt_curr->length = cares_txt_reply_get_length(ctxt_curr); + if (ex) + txt_curr->record_start = cares_txt_reply_get_record_start(ctxt_curr); + + } + + if (ctxt_out) + { + ares_free_data (ctxt_out); + } /* clean up on error */ if (status != ARES_SUCCESS) - { - if (txt_head) - ares_free_data (txt_head); - return status; - } + { + if (txt_head) + ares_free_data (txt_head); + return status; + } /* everything looks fine, return the data */ *txt_out = txt_head; diff --git a/src/lib/ares_parse_uri_reply.c b/src/lib/ares_parse_uri_reply.c index d79b5c4d85..199f500cd2 100644 --- a/src/lib/ares_parse_uri_reply.c +++ b/src/lib/ares_parse_uri_reply.c @@ -45,7 +45,8 @@ ares_parse_uri_reply (const unsigned char *abuf, int alen, { unsigned int qdcount, ancount, i; const unsigned char *aptr, *vptr; - int status, rr_type, rr_class, rr_len, rr_ttl; + int status, rr_type, rr_class, rr_len; + unsigned int rr_ttl; long len; char *uri_str = NULL, *rr_name = NULL; struct ares_uri_reply *uri_head = NULL; diff --git a/src/lib/ares_private.h b/src/lib/ares_private.h index 60d69e08b7..be5a297e75 100644 --- a/src/lib/ares_private.h +++ b/src/lib/ares_private.h @@ -434,4 +434,224 @@ int ares__connect_socket(ares_channel channel, (c)->sock_state_cb((c)->sock_state_cb_data, (s), (r), (w)); \ } WHILE_FALSE +/* Private definition of c-ares reply structs so that c-ares users + * only have access to the type definition in ares.h and must use + * accessor functions to interact with the structs. This way, + * cares reply structs can be modified without breaking ABI. + */ + struct cares_caa_reply { + cares_caa_reply *next; + int critical; + unsigned char *property; + size_t plength; /* plength excludes null termination */ + unsigned char *value; + size_t length; /* length excludes null termination */ + unsigned int ttl; +}; + +struct cares_ptr_reply { + cares_ptr_reply *next; + char *host; + unsigned int ttl; +}; + +struct cares_ns_reply { + cares_ns_reply *next; + char *host; + unsigned int ttl; +}; + +struct cares_srv_reply { +// cares_srv_reply *next; + char *host; + unsigned short priority; + unsigned short weight; + unsigned short port; + unsigned int ttl; +}; + +struct cares_srv_reply_container { + cares_srv_reply **replies; + unsigned int curr; + unsigned int count; +}; + +struct cares_mx_reply { + cares_mx_reply *next; + char *host; + unsigned short priority; + unsigned int ttl; +}; + +struct cares_txt_reply { + cares_txt_reply *next; + unsigned char *txt; + size_t length; + /* 1 - if start of new record + * 0 - if a chunk in the same record */ + unsigned char record_start; + unsigned int ttl; +}; + +struct cares_naptr_reply { + cares_naptr_reply *next; + unsigned char *flags; + unsigned char *service; + unsigned char *regexp; + char *replacement; + unsigned short order; + unsigned short preference; + unsigned int ttl; +}; + +struct cares_soa_reply { + char *nsname; + char *hostmaster; + unsigned int serial; + unsigned int refresh; + unsigned int retry; + unsigned int expire; + unsigned int minttl; + unsigned int ttl; +}; + +/* Private declaration of c-ares reply setters */ +// void cares_srv_reply_set_next(cares_srv_reply* srv_reply, +// cares_srv_reply* next); + +void cares_srv_reply_set_host(cares_srv_reply* srv_reply, char* host); + +void cares_srv_reply_set_priority(cares_srv_reply* srv_reply, + const unsigned short priority); + +void cares_srv_reply_set_weight(cares_srv_reply* srv_reply, + const unsigned short weight); + +void cares_srv_reply_set_port(cares_srv_reply* srv_reply, + const unsigned short port); + +void cares_srv_reply_set_ttl(cares_srv_reply* srv_reply, + const unsigned int ttl); + +void cares_srv_reply_container_set_replies(cares_srv_reply_container* container, + cares_srv_reply** replies); + +void cares_srv_reply_container_set_curr(cares_srv_reply_container* container, + const unsigned int index); + +void cares_srv_reply_container_set_count(cares_srv_reply_container* container, + const unsigned int count); + +void cares_caa_reply_set_next(cares_caa_reply* caa_reply, + cares_caa_reply* next); + +void cares_caa_reply_set_critical(cares_caa_reply* caa_reply, + const int critical); + +void cares_caa_reply_set_property(cares_caa_reply* caa_reply, + unsigned char* property); + +void cares_caa_reply_set_plength(cares_caa_reply* caa_reply, + const size_t plength); + +void cares_caa_reply_set_value(cares_caa_reply* caa_reply, + unsigned char* value); + +void cares_caa_reply_set_length(cares_caa_reply* caa_reply, + const size_t length); + +void cares_caa_reply_set_ttl(cares_caa_reply* caa_reply, + const unsigned int ttl); + +void cares_ptr_reply_set_next(cares_ptr_reply* ptr_reply, + cares_ptr_reply* next); + +void cares_ptr_reply_set_host(cares_ptr_reply* ptr_reply, char* host); + +void cares_ptr_reply_set_ttl(cares_ptr_reply* ptr_reply, + const unsigned int ttl); + +void cares_ns_reply_set_next(cares_ns_reply* ns_reply, + cares_ns_reply* next); + +void cares_ns_reply_set_host(cares_ns_reply* ns_reply, char* host); + +void cares_ns_reply_set_ttl(cares_ns_reply* ns_reply, + const unsigned int ttl); + +void cares_mx_reply_set_next(cares_mx_reply* mx_reply, + cares_mx_reply* next); + +void cares_mx_reply_set_host(cares_mx_reply* mx_reply, char *host); + +void cares_mx_reply_set_priority(cares_mx_reply* mx_reply, + const unsigned short priority); + +void cares_mx_reply_set_ttl(cares_mx_reply* mx_reply, + const unsigned int ttl); + +void cares_txt_reply_set_next(cares_txt_reply* txt_reply, + cares_txt_reply* next); + +void cares_txt_reply_set_txt(cares_txt_reply* txt_reply, + unsigned char* txt); + +void cares_txt_reply_set_length(cares_txt_reply* txt_reply, + const size_t length); + +void cares_txt_reply_set_record_start(cares_txt_reply* txt_reply, + const unsigned char record_start); + +void cares_txt_reply_set_ttl(cares_txt_reply* txt_reply, + const unsigned int ttl); + +void cares_naptr_reply_set_next(cares_naptr_reply* naptr_reply, + cares_naptr_reply* next); + +void cares_naptr_reply_set_flags(cares_naptr_reply* naptr_reply, + unsigned char* flags); + +void cares_naptr_reply_set_service(cares_naptr_reply* naptr_reply, + unsigned char* service); + +void cares_naptr_reply_set_regexp(cares_naptr_reply* naptr_reply, + unsigned char* regexp); + +void cares_naptr_reply_set_replacement(cares_naptr_reply* naptr_reply, + char* replacement); + +void cares_naptr_reply_set_order(cares_naptr_reply* naptr_reply, + const unsigned short order); + +void cares_naptr_reply_set_preference(cares_naptr_reply* naptr_reply, + const unsigned short preference); + +void cares_naptr_reply_set_ttl(cares_naptr_reply* naptr_reply, + const unsigned int ttl); + +void cares_soa_reply_set_nsname(cares_soa_reply* soa_reply, char* nsname); + +void cares_soa_reply_set_hostmaster(cares_soa_reply* soa_reply, + char* hostmaster); + +void cares_soa_reply_set_serial(cares_soa_reply* soa_reply, + const unsigned int serial); + +void cares_soa_reply_set_refresh(cares_soa_reply* soa_reply, + const unsigned int refresh); + +void cares_soa_reply_set_retry(cares_soa_reply* soa_reply, + const unsigned int retry); + +void cares_soa_reply_set_expire(cares_soa_reply* soa_reply, + const unsigned int expire); + +void cares_soa_reply_set_minttl(cares_soa_reply* soa_reply, + const unsigned int minttl); + +void cares_soa_reply_set_ttl(cares_soa_reply* soa_reply, + const unsigned int ttl); + + + #endif /* __ARES_PRIVATE_H */ diff --git a/src/lib/cares_container.c b/src/lib/cares_container.c new file mode 100644 index 0000000000..245546917f --- /dev/null +++ b/src/lib/cares_container.c @@ -0,0 +1,95 @@ +/* Copyright (C) 2021 by Kyle Evans + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of M.I.T. not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. M.I.T. makes no representations about the + * suitability of this software for any purpose. It is provided "as is" + * without express or implied warranty. + */ + +#include "ares_setup.h" +#include "ares.h" +#include "ares_private.h" + + +const cares_srv_reply* +cares_srv_reply_container_get_first(const cares_srv_reply_container* container) +{ + return container->replies[0]; +} + +const cares_srv_reply* +cares_srv_reply_container_get_next(cares_srv_reply_container* container) +{ + if (cares_srv_reply_container_get_count(container) == 0) + { + return container->replies[0]; + } + + if (container->curr == cares_srv_reply_container_get_count(container)) + { + return container->replies[cares_srv_reply_container_get_count(container) - 1]; + } + + cares_srv_reply_container_set_curr(container, + cares_srv_reply_container_get_curr(container) + 1); + + return container->replies[container->curr]; +} + +const cares_srv_reply* +cares_srv_reply_container_get_last(const cares_srv_reply_container* container) +{ + if (cares_srv_reply_container_get_count(container) == 0) + { + return container->replies[0]; + } + + return container->replies[cares_srv_reply_container_get_count(container) - 1]; +} + +unsigned int +cares_srv_reply_container_get_count(const cares_srv_reply_container* container) +{ + return container->count; +} + +unsigned int +cares_srv_reply_container_get_curr(const cares_srv_reply_container* container) +{ + return container->curr; +} + +int +cares_srv_reply_container_at_end(const cares_srv_reply_container* container) +{ + return cares_srv_reply_container_get_curr(container) >= + cares_srv_reply_container_get_count(container); +} + +void cares_srv_reply_container_set_replies(cares_srv_reply_container* container, + cares_srv_reply** replies) +{ + container->replies = replies; +} + +void cares_srv_reply_container_set_curr(cares_srv_reply_container* container, + const unsigned int index) +{ + container->curr = index; +} + +void cares_srv_reply_container_set_count(cares_srv_reply_container* container, + const unsigned int count) +{ + container->count = count; +} + +void cares_srv_reply_container_reset(cares_srv_reply_container* container) +{ + cares_srv_reply_container_set_curr(container, 0); +} diff --git a/src/lib/cares_free_container.c b/src/lib/cares_free_container.c new file mode 100644 index 0000000000..312f2b6b5a --- /dev/null +++ b/src/lib/cares_free_container.c @@ -0,0 +1,94 @@ +/* Copyright (C) 2021 by Kyle Evans + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of M.I.T. not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. M.I.T. makes no representations about the + * suitability of this software for any purpose. It is provided "as is" + * without express or implied warranty. + */ + +#include +#include "ares_setup.h" +#include "ares.h" +#include "ares_private.h" +#include "cares_free_container.h" +#include "ares_data.h" + +void cares_free_container(void *containerptr) +{ + if (containerptr == NULL) { + return; + } + + struct cares_container *ptr; + unsigned int count = 0; + + ptr = (void *)((char *)containerptr - offsetof(struct cares_container, container)); + + if (ptr->mark != ARES_DATATYPE_MARK) + return; + + switch (ptr->type) + { + case CARES_CONTAINER_SRV_REPLY_CONTAINER: + count = ptr->container.srv_container.count; + break; + + default: + break; + } + + for (unsigned int i = 0; i < count; ++i) + { + switch (ptr->type) + { + case CARES_CONTAINER_SRV_REPLY_CONTAINER: + if (ptr->container.srv_container.replies[i]) + { + ares_free_data(ptr->container.srv_container.replies[i]); + } + + if (i == count - 1) + { + ares_free(ptr->container.srv_container.replies); + } + break; + + default: + break; + } + } + + ares_free(ptr); +} + +void *cares_malloc_container(cares_container_type type) +{ + struct cares_container *ptr; + + ptr = ares_malloc(sizeof(struct cares_container)); + if (!ptr) + return NULL; + + switch (type) + { + case CARES_CONTAINER_SRV_REPLY_CONTAINER: + ptr->container.srv_container.replies = NULL; + ptr->container.srv_container.curr = 0; + ptr->container.srv_container.count = 0; + break; + + default: + ares_free(ptr); + return NULL; + } + + ptr->mark = ARES_DATATYPE_MARK; + ptr->type = type; + + return &ptr->container; +} diff --git a/src/lib/cares_free_container.h b/src/lib/cares_free_container.h new file mode 100644 index 0000000000..fd158232f5 --- /dev/null +++ b/src/lib/cares_free_container.h @@ -0,0 +1,30 @@ +/* Copyright (C) 2021 by Kyle Evans + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of M.I.T. not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. M.I.T. makes no representations about the + * suitability of this software for any purpose. It is provided "as is" + * without express or implied warranty. + */ + +#include "ares_private.h" + +typedef enum { + CARES_CONTAINER_SRV_REPLY_CONTAINER, + CARES_CONTAINER_LAST /* not used */ +} cares_container_type; + + +struct cares_container { + cares_container_type type; /* Actual data type identifier. */ + unsigned int mark; /* Private ares_data signature. */ + union { + struct cares_srv_reply_container srv_container; + } container; +}; + +void *cares_malloc_container(cares_container_type type); diff --git a/src/lib/cares_memdup.c b/src/lib/cares_memdup.c new file mode 100644 index 0000000000..2aceea21e8 --- /dev/null +++ b/src/lib/cares_memdup.c @@ -0,0 +1,33 @@ +/* Copyright (C) 2021 by Kyle Evans + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of M.I.T. not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. M.I.T. makes no representations about the + * suitability of this software for any purpose. It is provided "as is" + * without express or implied warranty. + */ + +#include "ares_setup.h" +#include "ares.h" +#include "cares_memdup.h" +#include "ares_private.h" +#include "string.h" + +unsigned char* cares_memdup(const unsigned char* data, size_t sz) +{ + unsigned char* data2; + data2 = ares_malloc(sz + 1); + if (!data2) + { + return (unsigned char*)NULL; + } + memcpy(data2, data, sz); + /* Make sure we NULL-terminate */ + data2[sz] = 0; + + return data2; +} diff --git a/src/lib/cares_memdup.h b/src/lib/cares_memdup.h new file mode 100644 index 0000000000..03e6e7b65b --- /dev/null +++ b/src/lib/cares_memdup.h @@ -0,0 +1,19 @@ +#ifndef HEADER_CARES_MEMDUP_H +#define HEADER_CARES_MEMDUP_H + +/* Copyright (C) 2021 by Kyle Evans + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of M.I.T. not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. M.I.T. makes no representations about the + * suitability of this software for any purpose. It is provided "as is" + * without express or implied warranty. + */ + +unsigned char* cares_memdup(const unsigned char* data, size_t sz); + +#endif /* HEADER_CARES_MEMDUP_H */ diff --git a/src/lib/cares_parse_caa_reply.c b/src/lib/cares_parse_caa_reply.c new file mode 100644 index 0000000000..febf10b3ff --- /dev/null +++ b/src/lib/cares_parse_caa_reply.c @@ -0,0 +1,205 @@ + +/* Copyright 2020 by + * + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting + * documentation, and that the name of M.I.T. not be used in + * advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" + * without express or implied warranty. + */ + +#include "ares_setup.h" + +#ifdef HAVE_NETINET_IN_H +# include +#endif +#ifdef HAVE_NETDB_H +# include +#endif +#ifdef HAVE_ARPA_INET_H +# include +#endif + +#include "ares_nameser.h" + +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "ares.h" +#include "ares_dns.h" +#include "ares_data.h" +#include "ares_private.h" +#include "cares_memdup.h" + +int +cares_parse_caa_reply (const unsigned char *abuf, int alen, + cares_caa_reply **caa_out) +{ + unsigned int qdcount, ancount, i; + const unsigned char *aptr; + const unsigned char *strptr; + int status, rr_type, rr_class, rr_len; + unsigned int rr_ttl; + long len; + char *hostname = NULL, *rr_name = NULL; + cares_caa_reply *caa_head = NULL; + cares_caa_reply *caa_last = NULL; + cares_caa_reply *caa_curr; + + /* Set *caa_out to NULL for all failure cases. */ + *caa_out = NULL; + + /* Give up if abuf doesn't have room for a header. */ + if (alen < HFIXEDSZ) + return ARES_EBADRESP; + + /* Fetch the question and answer count from the header. */ + qdcount = DNS_HEADER_QDCOUNT (abuf); + ancount = DNS_HEADER_ANCOUNT (abuf); + if (qdcount != 1) + return ARES_EBADRESP; + if (ancount == 0) + return ARES_ENODATA; + + /* Expand the name from the question, and skip past the question. */ + aptr = abuf + HFIXEDSZ; + status = ares_expand_name (aptr, abuf, alen, &hostname, &len); + if (status != ARES_SUCCESS) + return status; + + if (aptr + len + QFIXEDSZ > abuf + alen) + { + ares_free (hostname); + return ARES_EBADRESP; + } + aptr += len + QFIXEDSZ; + + /* Examine each answer resource record (RR) in turn. */ + for (i = 0; i < ancount; i++) + { + /* Decode the RR up to the data field. */ + status = ares_expand_name (aptr, abuf, alen, &rr_name, &len); + if (status != ARES_SUCCESS) + { + break; + } + aptr += len; + if (aptr + RRFIXEDSZ > abuf + alen) + { + status = ARES_EBADRESP; + break; + } + rr_type = DNS_RR_TYPE (aptr); + rr_class = DNS_RR_CLASS (aptr); + rr_ttl = DNS_RR_TTL (aptr); + rr_len = DNS_RR_LEN (aptr); + aptr += RRFIXEDSZ; + if (aptr + rr_len > abuf + alen) + { + status = ARES_EBADRESP; + break; + } + + /* Check if we are really looking at a CAA record */ + if ((rr_class == C_IN || rr_class == C_CHAOS) && rr_type == T_CAA) + { + unsigned char* property; + unsigned char* value; + strptr = aptr; + + /* Allocate storage for this CAA answer appending it to the list */ + caa_curr = ares_malloc_data(CARES_DATATYPE_CAA_REPLY); + if (!caa_curr) + { + status = ARES_ENOMEM; + break; + } + if (caa_last) + { + cares_caa_reply_set_next(caa_last, caa_curr); + } + else + { + caa_head = caa_curr; + } + caa_last = caa_curr; + if (rr_len < 2) + { + status = ARES_EBADRESP; + break; + } + cares_caa_reply_set_critical(caa_curr, (int)*strptr++); + cares_caa_reply_set_plength(caa_curr, (int)*strptr++); + if (cares_caa_reply_get_plength(caa_curr) <= 0 || (int)cares_caa_reply_get_plength(caa_curr) >= rr_len - 2) + { + status = ARES_EBADRESP; + break; + } + property = cares_memdup((unsigned char*) strptr, cares_caa_reply_get_plength(caa_curr)); + if (!property) { + status = ARES_ENOMEM; + break; + } + cares_caa_reply_set_property(caa_curr, property); + strptr += cares_caa_reply_get_plength(caa_curr); + + cares_caa_reply_set_length(caa_curr, rr_len - cares_caa_reply_get_plength(caa_curr) - 2); + if (cares_caa_reply_get_length(caa_curr) <= 0) + { + status = ARES_EBADRESP; + break; + } + value = cares_memdup((unsigned char*) strptr, cares_caa_reply_get_length(caa_curr)); + if (!value) { + status = ARES_ENOMEM; + break; + } + cares_caa_reply_set_value(caa_curr, value); + cares_caa_reply_set_ttl(caa_curr, rr_ttl); + } + else if (rr_type != T_CNAME) + { + /* wrong record type */ + status = ARES_ENODATA; + break; + } + + /* Propagate any failures */ + if (status != ARES_SUCCESS) + { + break; + } + + /* Don't lose memory in the next iteration */ + ares_free (rr_name); + rr_name = NULL; + + /* Move on to the next record */ + aptr += rr_len; + } + + if (hostname) + ares_free (hostname); + if (rr_name) + ares_free (rr_name); + + /* clean up on error */ + if (status != ARES_SUCCESS) + { + if (caa_head) + ares_free_data (caa_head); + return status; + } + + /* everything looks fine, return the data */ + *caa_out = caa_head; + + return ARES_SUCCESS; +} diff --git a/src/lib/cares_parse_mx_reply.c b/src/lib/cares_parse_mx_reply.c new file mode 100644 index 0000000000..640e44e0cc --- /dev/null +++ b/src/lib/cares_parse_mx_reply.c @@ -0,0 +1,177 @@ + +/* Copyright 1998 by the Massachusetts Institute of Technology. + * Copyright (C) 2010 Jeremy Lal + * + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting + * documentation, and that the name of M.I.T. not be used in + * advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" + * without express or implied warranty. + */ + +#include "ares_setup.h" + +#ifdef HAVE_NETINET_IN_H +# include +#endif +#ifdef HAVE_NETDB_H +# include +#endif +#ifdef HAVE_ARPA_INET_H +# include +#endif + +#include "ares_nameser.h" + +#include "ares.h" +#include "ares_dns.h" +#include "ares_data.h" +#include "ares_private.h" + +int +cares_parse_mx_reply (const unsigned char *abuf, int alen, + cares_mx_reply **mx_out) +{ + unsigned int qdcount, ancount, i; + const unsigned char *aptr, *vptr; + char* mx_host = NULL; + int status, rr_type, rr_class, rr_len; + unsigned int rr_ttl; + long len; + char *hostname = NULL, *rr_name = NULL; + cares_mx_reply *mx_head = NULL; + cares_mx_reply *mx_last = NULL; + cares_mx_reply *mx_curr; + + /* Set *mx_out to NULL for all failure cases. */ + *mx_out = NULL; + + /* Give up if abuf doesn't have room for a header. */ + if (alen < HFIXEDSZ) + return ARES_EBADRESP; + + /* Fetch the question and answer count from the header. */ + qdcount = DNS_HEADER_QDCOUNT (abuf); + ancount = DNS_HEADER_ANCOUNT (abuf); + if (qdcount != 1) + return ARES_EBADRESP; + if (ancount == 0) + return ARES_ENODATA; + + /* Expand the name from the question, and skip past the question. */ + aptr = abuf + HFIXEDSZ; + status = ares_expand_name (aptr, abuf, alen, &hostname, &len); + if (status != ARES_SUCCESS) + return status; + + if (aptr + len + QFIXEDSZ > abuf + alen) + { + ares_free (hostname); + return ARES_EBADRESP; + } + aptr += len + QFIXEDSZ; + + /* Examine each answer resource record (RR) in turn. */ + for (i = 0; i < ancount; i++) + { + /* Decode the RR up to the data field. */ + status = ares_expand_name (aptr, abuf, alen, &rr_name, &len); + if (status != ARES_SUCCESS) + { + break; + } + aptr += len; + if (aptr + RRFIXEDSZ > abuf + alen) + { + status = ARES_EBADRESP; + break; + } + rr_type = DNS_RR_TYPE (aptr); + rr_class = DNS_RR_CLASS (aptr); + rr_ttl = DNS_RR_TTL(aptr); + rr_len = DNS_RR_LEN (aptr); + aptr += RRFIXEDSZ; + if (aptr + rr_len > abuf + alen) + { + status = ARES_EBADRESP; + break; + } + + /* Check if we are really looking at a MX record */ + if (rr_class == C_IN && rr_type == T_MX) + { + + /* parse the MX record itself */ + if (rr_len < 2) + { + status = ARES_EBADRESP; + break; + } + + /* Allocate storage for this MX answer appending it to the list */ + mx_curr = ares_malloc_data(CARES_DATATYPE_MX_REPLY); + if (!mx_curr) + { + status = ARES_ENOMEM; + break; + } + if (mx_last) + { + cares_mx_reply_set_next(mx_last, mx_curr); + } + else + { + mx_head = mx_curr; + } + mx_last = mx_curr; + + vptr = aptr; + cares_mx_reply_set_priority(mx_curr, DNS__16BIT(vptr)); + cares_mx_reply_set_ttl(mx_curr, rr_ttl); + vptr += sizeof(unsigned short); + + status = ares_expand_name (vptr, abuf, alen, &mx_host, &len); + if (status != ARES_SUCCESS) + break; + + cares_mx_reply_set_host(mx_curr, mx_host); + } + else if (rr_type != T_CNAME) + { + /* wrong record type */ + status = ARES_ENODATA; + break; + } + + /* Don't lose memory in the next iteration */ + ares_free (rr_name); + rr_name = NULL; + + /* Move on to the next record */ + aptr += rr_len; + } + + if (hostname) + ares_free (hostname); + if (rr_name) + ares_free (rr_name); + + /* clean up on error */ + if (status != ARES_SUCCESS) + { + if (mx_head) + ares_free_data (mx_head); + return status; + } + + /* everything looks fine, return the data */ + *mx_out = mx_head; + + return ARES_SUCCESS; +} diff --git a/src/lib/cares_parse_naptr_reply.c b/src/lib/cares_parse_naptr_reply.c new file mode 100644 index 0000000000..ff3f301ffa --- /dev/null +++ b/src/lib/cares_parse_naptr_reply.c @@ -0,0 +1,200 @@ + +/* Copyright 1998 by the Massachusetts Institute of Technology. + * Copyright (C) 2009 by Jakub Hrozek + * + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting + * documentation, and that the name of M.I.T. not be used in + * advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" + * without express or implied warranty. + */ + +#include "ares_setup.h" + +#ifdef HAVE_NETINET_IN_H +# include +#endif +#ifdef HAVE_NETDB_H +# include +#endif +#ifdef HAVE_ARPA_INET_H +# include +#endif + +#include "ares_nameser.h" + +#include "ares.h" +#include "ares_dns.h" +#include "ares_data.h" +#include "ares_private.h" + +int +cares_parse_naptr_reply (const unsigned char *abuf, int alen, + cares_naptr_reply **naptr_out) +{ + unsigned int qdcount, ancount, i; + const unsigned char *aptr, *vptr; + int status, rr_type, rr_class, rr_len; + unsigned int rr_ttl; + long len; + char *hostname = NULL, *rr_name = NULL; + unsigned char* flags = NULL; + unsigned char* service = NULL; + unsigned char* regexp = NULL; + char* replacement = NULL; + cares_naptr_reply *naptr_head = NULL; + cares_naptr_reply *naptr_last = NULL; + cares_naptr_reply *naptr_curr; + + /* Set *naptr_out to NULL for all failure cases. */ + *naptr_out = NULL; + + /* Give up if abuf doesn't have room for a header. */ + if (alen < HFIXEDSZ) + return ARES_EBADRESP; + + /* Fetch the question and answer count from the header. */ + qdcount = DNS_HEADER_QDCOUNT (abuf); + ancount = DNS_HEADER_ANCOUNT (abuf); + if (qdcount != 1) + return ARES_EBADRESP; + if (ancount == 0) + return ARES_ENODATA; + + /* Expand the name from the question, and skip past the question. */ + aptr = abuf + HFIXEDSZ; + status = ares_expand_name (aptr, abuf, alen, &hostname, &len); + if (status != ARES_SUCCESS) + return status; + + if (aptr + len + QFIXEDSZ > abuf + alen) + { + ares_free (hostname); + return ARES_EBADRESP; + } + aptr += len + QFIXEDSZ; + + /* Examine each answer resource record (RR) in turn. */ + for (i = 0; i < ancount; i++) + { + /* Decode the RR up to the data field. */ + status = ares_expand_name (aptr, abuf, alen, &rr_name, &len); + if (status != ARES_SUCCESS) + { + break; + } + aptr += len; + if (aptr + RRFIXEDSZ > abuf + alen) + { + status = ARES_EBADRESP; + break; + } + rr_type = DNS_RR_TYPE (aptr); + rr_class = DNS_RR_CLASS (aptr); + rr_ttl = DNS_RR_TTL(aptr); + rr_len = DNS_RR_LEN (aptr); + aptr += RRFIXEDSZ; + if (aptr + rr_len > abuf + alen) + { + status = ARES_EBADRESP; + break; + } + + /* Check if we are really looking at a NAPTR record */ + if (rr_class == C_IN && rr_type == T_NAPTR) + { + /* parse the NAPTR record itself */ + + /* RR must contain at least 7 bytes = 2 x int16 + 3 x name */ + if (rr_len < 7) + { + status = ARES_EBADRESP; + break; + } + + /* Allocate storage for this NAPTR answer appending it to the list */ + naptr_curr = ares_malloc_data(CARES_DATATYPE_NAPTR_REPLY); + if (!naptr_curr) + { + status = ARES_ENOMEM; + break; + } + if (naptr_last) + { + cares_naptr_reply_set_next(naptr_last, naptr_curr); + } + else + { + naptr_head = naptr_curr; + } + naptr_last = naptr_curr; + + vptr = aptr; + cares_naptr_reply_set_order(naptr_curr, DNS__16BIT(vptr)); + vptr += sizeof(unsigned short); + cares_naptr_reply_set_preference(naptr_curr, DNS__16BIT(vptr)); + vptr += sizeof(unsigned short); + cares_naptr_reply_set_ttl(naptr_curr, rr_ttl); + + status = ares_expand_string(vptr, abuf, alen, &flags, &len); + if (status != ARES_SUCCESS) + break; + vptr += len; + cares_naptr_reply_set_flags(naptr_curr, flags); + + status = ares_expand_string(vptr, abuf, alen, &service, &len); + if (status != ARES_SUCCESS) + break; + vptr += len; + cares_naptr_reply_set_service(naptr_curr, service); + + status = ares_expand_string(vptr, abuf, alen, ®exp, &len); + if (status != ARES_SUCCESS) + break; + vptr += len; + cares_naptr_reply_set_regexp(naptr_curr, regexp); + + status = ares_expand_name(vptr, abuf, alen, &replacement, &len); + if (status != ARES_SUCCESS) + break; + cares_naptr_reply_set_replacement(naptr_curr, replacement); + } + else if (rr_type != T_CNAME) + { + /* wrong record type */ + status = ARES_ENODATA; + break; + } + + /* Don't lose memory in the next iteration */ + ares_free (rr_name); + rr_name = NULL; + + /* Move on to the next record */ + aptr += rr_len; + } + + if (hostname) + ares_free (hostname); + if (rr_name) + ares_free (rr_name); + + /* clean up on error */ + if (status != ARES_SUCCESS) + { + if (naptr_head) + ares_free_data (naptr_head); + return status; + } + + /* everything looks fine, return the data */ + *naptr_out = naptr_head; + + return ARES_SUCCESS; +} diff --git a/src/lib/cares_parse_ns_reply.c b/src/lib/cares_parse_ns_reply.c new file mode 100644 index 0000000000..87280fdbe6 --- /dev/null +++ b/src/lib/cares_parse_ns_reply.c @@ -0,0 +1,171 @@ + +/* Copyright 1998 by the Massachusetts Institute of Technology. + * + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting + * documentation, and that the name of M.I.T. not be used in + * advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" + * without express or implied warranty. + */ + +#include "ares_setup.h" + +#ifdef HAVE_NETINET_IN_H +# include +#endif +#ifdef HAVE_NETDB_H +# include +#endif + +#include "ares_nameser.h" + +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "ares.h" +#include "ares_dns.h" +#include "ares_nowarn.h" +#include "ares_data.h" +#include "ares_private.h" + +int cares_parse_ns_reply(const unsigned char *abuf, int alen, + cares_ns_reply **ns_out) +{ + unsigned int qdcount, ancount; + int status, i, rr_type, rr_class, rr_len; + unsigned int rr_ttl; + long len; + const unsigned char *aptr; + char *nsname = NULL, *rr_name = NULL; + char* ns_host = NULL; + cares_ns_reply *ns_head = NULL; + cares_ns_reply *ns_last = NULL; + cares_ns_reply *ns_curr = NULL; + + /* Set *ns_out to NULL for all failure cases. */ + *ns_out = NULL; + + /* Give up if abuf doesn't have room for a header. */ + if (alen < HFIXEDSZ) + return ARES_EBADRESP; + + /* Fetch the question and answer count from the header. */ + qdcount = DNS_HEADER_QDCOUNT(abuf); + ancount = DNS_HEADER_ANCOUNT(abuf); + if (qdcount != 1) + return ARES_EBADRESP; + if (ancount == 0) + return ARES_ENODATA; + + /* Expand the name from the question, and skip past the question. */ + aptr = abuf + HFIXEDSZ; + status = ares__expand_name_for_response(aptr, abuf, alen, &nsname, &len, 0); + if (status != ARES_SUCCESS) + return status; + if (aptr + len + QFIXEDSZ > abuf + alen) + { + ares_free(nsname); + return ARES_EBADRESP; + } + aptr += len + QFIXEDSZ; + + /* Examine each answer resource record (RR) in turn. */ + for (i = 0; i < (int)ancount; i++) + { + /* Decode the RR up to the data field. */ + status = ares__expand_name_for_response(aptr, abuf, alen, &rr_name, &len, 0); + if (status != ARES_SUCCESS) + break; + aptr += len; + if (aptr + RRFIXEDSZ > abuf + alen) + { + status = ARES_EBADRESP; + break; + } + rr_type = DNS_RR_TYPE(aptr); + rr_class = DNS_RR_CLASS(aptr); + rr_ttl = DNS_RR_TTL(aptr); + rr_len = DNS_RR_LEN(aptr); + aptr += RRFIXEDSZ; + if (aptr + rr_len > abuf + alen) + { + status = ARES_EBADRESP; + break; + } + + if (rr_class == C_IN && rr_type == T_NS) + { + /* Allocate storage for this NS answer appending it to the list */ + ns_curr = ares_malloc_data(CARES_DATATYPE_NS_REPLY); + if (!ns_curr) + { + status = ARES_ENOMEM; + break; + } + if (ns_last) + { + cares_ns_reply_set_next(ns_last, ns_curr); + } + else + { + ns_head = ns_curr; + } + ns_last = ns_curr; + + /* Decode the RR data and set hostname to it. */ + status = ares__expand_name_for_response(aptr, abuf, alen, &ns_host, + &len, 1); + if (status != ARES_SUCCESS) + { + break; + } + + cares_ns_reply_set_host(ns_curr, ns_host); + cares_ns_reply_set_ttl(ns_curr, rr_ttl); + } + else if (rr_type != T_CNAME) + { + /* wrong record type */ + status = ARES_ENODATA; + break; + } + + /* Don't lose memory in the next iteration */ + ares_free(rr_name); + rr_name = NULL; + + /* Move on to the next record */ + aptr += rr_len; + if (aptr > abuf + alen) + { /* LCOV_EXCL_START: already checked above */ + status = ARES_EBADRESP; + break; + } /* LCOV_EXCL_STOP */ + } + + if (nsname) + ares_free(nsname); + if (rr_name) + ares_free(rr_name); + + /* clean up on error */ + if (status != ARES_SUCCESS) + { + if (ns_head) + { + ares_free_data(ns_head); + } + return status; + } + + /* everything looks fine, return the data */ + *ns_out = ns_head; + return ARES_SUCCESS; +} diff --git a/src/lib/cares_parse_ptr_reply.c b/src/lib/cares_parse_ptr_reply.c new file mode 100644 index 0000000000..2a05338e61 --- /dev/null +++ b/src/lib/cares_parse_ptr_reply.c @@ -0,0 +1,188 @@ + +/* Copyright 1998 by the Massachusetts Institute of Technology. + * + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting + * documentation, and that the name of M.I.T. not be used in + * advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" + * without express or implied warranty. + */ + +#include "ares_setup.h" + +#ifdef HAVE_NETINET_IN_H +# include +#endif +#ifdef HAVE_NETDB_H +# include +#endif + +#include "ares_nameser.h" + +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "ares.h" +#include "ares_dns.h" +#include "ares_nowarn.h" +#include "ares_data.h" +#include "ares_private.h" + +int cares_parse_ptr_reply(const unsigned char *abuf, int alen, + cares_ptr_reply **ptr_out) +{ + unsigned int qdcount, ancount; + int status, i, rr_type, rr_class, rr_len; + unsigned int rr_ttl; + long len; + const unsigned char *aptr; + char *ptrname = NULL, *rr_name = NULL; + char* ptr_host = NULL; + cares_ptr_reply *ptr_head = NULL; + cares_ptr_reply *ptr_last = NULL; + cares_ptr_reply *ptr_curr; + + /* Set *ptr_out to NULL for all failure cases. */ + *ptr_out = NULL; + + /* Give up if abuf doesn't have room for a header. */ + if (alen < HFIXEDSZ) + return ARES_EBADRESP; + + /* Fetch the question and answer count from the header. */ + qdcount = DNS_HEADER_QDCOUNT(abuf); + ancount = DNS_HEADER_ANCOUNT(abuf); + if (qdcount != 1) + return ARES_EBADRESP; + if (ancount == 0) + return ARES_ENODATA; + + /* Expand the name from the question, and skip past the question. */ + aptr = abuf + HFIXEDSZ; + status = ares__expand_name_for_response(aptr, abuf, alen, &ptrname, &len, 0); + if (status != ARES_SUCCESS) + return status; + if (aptr + len + QFIXEDSZ > abuf + alen) + { + ares_free(ptrname); + return ARES_EBADRESP; + } + aptr += len + QFIXEDSZ; + + /* Examine each answer resource record (RR) in turn. */ + for (i = 0; i < (int)ancount; i++) + { + /* Decode the RR up to the data field. */ + status = ares__expand_name_for_response(aptr, abuf, alen, &rr_name, &len, 0); + if (status != ARES_SUCCESS) + break; + aptr += len; + if (aptr + RRFIXEDSZ > abuf + alen) + { + status = ARES_EBADRESP; + break; + } + rr_type = DNS_RR_TYPE(aptr); + rr_class = DNS_RR_CLASS(aptr); + rr_ttl = DNS_RR_TTL(aptr); + rr_len = DNS_RR_LEN(aptr); + aptr += RRFIXEDSZ; + if (aptr + rr_len > abuf + alen) + { + status = ARES_EBADRESP; + break; + } + + if (rr_class == C_IN && rr_type == T_PTR) + { + if (strcasecmp(rr_name, ptrname) != 0) + { + /* question and answer don't match */ + status = ARES_ENODATA; + break; + } + /* Allocate storage for this PTR answer appending it to the list */ + ptr_curr = ares_malloc_data(CARES_DATATYPE_PTR_REPLY); + if (!ptr_curr) + { + status = ARES_ENOMEM; + break; + } + if (ptr_last) + { + cares_ptr_reply_set_next(ptr_last, ptr_curr); + } + else + { + ptr_head = ptr_curr; + } + ptr_last = ptr_curr; + + /* Decode the RR data and set hostname to it. */ + status = ares__expand_name_for_response(aptr, abuf, alen, &ptr_host, + &len, 1); + if (status != ARES_SUCCESS) + { + break; + } + + cares_ptr_reply_set_host(ptr_curr, ptr_host); + cares_ptr_reply_set_ttl(ptr_curr, rr_ttl); + } + else if (rr_class == C_IN && rr_type == T_CNAME) + { + /* Decode the RR data and replace ptrname with it. */ + ares_free(ptrname); + ptrname = NULL; + status = ares__expand_name_for_response(aptr, abuf, alen, &ptrname, + &len, 1); + if (status != ARES_SUCCESS) + { + break; + } + } + else + { + /* wrong record type */ + status = ARES_ENODATA; + break; + } + + /* Don't lose memory in the next iteration */ + ares_free(rr_name); + rr_name = NULL; + + /* Move on to the next record */ + aptr += rr_len; + if (aptr > abuf + alen) + { /* LCOV_EXCL_START: already checked above */ + status = ARES_EBADRESP; + break; + } /* LCOV_EXCL_STOP */ + } + if (ptrname) + ares_free(ptrname); + if (rr_name) + ares_free(rr_name); + + /* clean up on error */ + if (status != ARES_SUCCESS) + { + if (ptr_head) + { + ares_free_data (ptr_head); + } + return status; + } + + /* everything looks fine, return the data */ + *ptr_out = ptr_head; + return ARES_SUCCESS; +} diff --git a/src/lib/cares_parse_soa_reply.c b/src/lib/cares_parse_soa_reply.c new file mode 100644 index 0000000000..d38becfca2 --- /dev/null +++ b/src/lib/cares_parse_soa_reply.c @@ -0,0 +1,194 @@ + +/* Copyright 1998 by the Massachusetts Institute of Technology. + * Copyright (C) 2012 Marko Kreen + * + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting + * documentation, and that the name of M.I.T. not be used in + * advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" + * without express or implied warranty. + */ + +#include "ares_setup.h" + +#ifdef HAVE_NETINET_IN_H +# include +#endif +#ifdef HAVE_NETDB_H +# include +#endif +#ifdef HAVE_ARPA_INET_H +# include +#endif + +#include "ares_nameser.h" + +#include "ares.h" +#include "ares_dns.h" +#include "ares_data.h" +#include "ares_private.h" + +int +cares_parse_soa_reply(const unsigned char *abuf, int alen, + cares_soa_reply **soa_out) +{ + const unsigned char *aptr; + char* nsname = NULL; + char* hostmaster = NULL; + long len; + char *qname = NULL, *rr_name = NULL; + cares_soa_reply *soa = NULL; + int qdcount, ancount, qclass; + int status, i, rr_type, rr_class, rr_len; + unsigned int rr_ttl; + + if (alen < HFIXEDSZ) + return ARES_EBADRESP; + + /* parse message header */ + qdcount = DNS_HEADER_QDCOUNT(abuf); + ancount = DNS_HEADER_ANCOUNT(abuf); + + if (qdcount != 1) + return ARES_EBADRESP; + if (ancount == 0) + return ARES_EBADRESP; + + aptr = abuf + HFIXEDSZ; + + /* query name */ + status = ares__expand_name_for_response(aptr, abuf, alen, &qname, &len, 0); + if (status != ARES_SUCCESS) + return status; + + if (alen <= len + HFIXEDSZ + 1) + { + ares_free(qname); + return ARES_EBADRESP; + } + aptr += len; + + qclass = DNS_QUESTION_TYPE(aptr); + + /* skip qtype & qclass */ + if (aptr + QFIXEDSZ > abuf + alen) + { + ares_free(qname); + return ARES_EBADRESP; + } + aptr += QFIXEDSZ; + + /* qclass of SOA with multiple answers */ + if (qclass == T_SOA && ancount > 1) + { + ares_free(qname); + return ARES_EBADRESP; + } + + /* examine all the records, break and return if found soa */ + for (i = 0; i < ancount; i++) + { + rr_name = NULL; + status = ares__expand_name_for_response (aptr, abuf, alen, &rr_name, &len, 0); + if (status != ARES_SUCCESS) + { + break; + } + + aptr += len; + if ( aptr + RRFIXEDSZ > abuf + alen ) + { + status = ARES_EBADRESP; + break; + } + rr_type = DNS_RR_TYPE( aptr ); + rr_class = DNS_RR_CLASS( aptr ); + rr_ttl = DNS_RR_TTL(aptr); + rr_len = DNS_RR_LEN( aptr ); + aptr += RRFIXEDSZ; + if (aptr + rr_len > abuf + alen) + { + status = ARES_EBADRESP; + break; + } + if ( rr_class == C_IN && rr_type == T_SOA ) + { + /* allocate result struct */ + soa = ares_malloc_data(CARES_DATATYPE_SOA_REPLY); + if (!soa) + { + status = ARES_ENOMEM; + break; + } + + /* nsname */ + status = ares__expand_name_for_response(aptr, abuf, alen, &nsname, + &len, 0); + if (status != ARES_SUCCESS) + break; + cares_soa_reply_set_nsname(soa, nsname); + aptr += len; + + /* hostmaster */ + status = ares__expand_name_for_response(aptr, abuf, alen, + &hostmaster, &len, 0); + if (status != ARES_SUCCESS) + break; + cares_soa_reply_set_hostmaster(soa, hostmaster); + aptr += len; + + /* integer fields */ + if (aptr + 5 * 4 > abuf + alen) + { + status = ARES_EBADRESP; + break; + } + cares_soa_reply_set_serial(soa, DNS__32BIT(aptr + 0 * 4)); + cares_soa_reply_set_refresh(soa, DNS__32BIT(aptr + 1 * 4)); + cares_soa_reply_set_retry(soa, DNS__32BIT(aptr + 2 * 4)); + cares_soa_reply_set_expire(soa, DNS__32BIT(aptr + 3 * 4)); + cares_soa_reply_set_minttl(soa, DNS__32BIT(aptr + 4 * 4)); + cares_soa_reply_set_ttl(soa, rr_ttl); + break; + } + aptr += rr_len; + + ares_free(rr_name); + rr_name = NULL; + + if (aptr > abuf + alen) + { + status = ARES_EBADRESP; + break; + } + } + + if (status == ARES_SUCCESS && !soa) + { + /* no SOA record found */ + status = ARES_EBADRESP; + } + + if (qname) + ares_free(qname); + if(rr_name) + ares_free(rr_name); + + if (status != ARES_SUCCESS) + { + /* no SOA record found */ + if (soa) + ares_free_data(soa); + return status; + } + + *soa_out = soa; + + return ARES_SUCCESS; +} diff --git a/src/lib/cares_parse_srv_reply.c b/src/lib/cares_parse_srv_reply.c new file mode 100644 index 0000000000..5948f16b97 --- /dev/null +++ b/src/lib/cares_parse_srv_reply.c @@ -0,0 +1,217 @@ + +/* Copyright 1998 by the Massachusetts Institute of Technology. + * Copyright (C) 2009 by Jakub Hrozek + * + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting + * documentation, and that the name of M.I.T. not be used in + * advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" + * without express or implied warranty. + */ + +#include "ares_setup.h" + +#ifdef HAVE_NETINET_IN_H +# include +#endif +#ifdef HAVE_NETDB_H +# include +#endif +#ifdef HAVE_ARPA_INET_H +# include +#endif + +#include "ares_nameser.h" + +#include "ares.h" +#include "ares_dns.h" +#include "ares_data.h" +#include "ares_private.h" +#include "cares_free_container.h" + +int +cares_parse_srv_reply (const unsigned char *abuf, int alen, + cares_srv_reply_container **srv_out) +{ + unsigned int qdcount, ancount, i, count = 0; + const unsigned char *aptr, *vptr; + int status, rr_type, rr_class, rr_len; + unsigned int rr_ttl; + long len; + char *hostname = NULL, *rr_name = NULL; + char* srv_host = NULL; + // cares_srv_reply *srv_head = NULL; + // cares_srv_reply *srv_last = NULL; + cares_srv_reply *srv_curr; + cares_srv_reply **srv_replies = NULL; + + /* Set *srv_out to NULL for all failure cases. */ + *srv_out = NULL; + + /* Give up if abuf doesn't have room for a header. */ + if (alen < HFIXEDSZ) + { + return ARES_EBADRESP; + } + + /* Fetch the question and answer count from the header. */ + qdcount = DNS_HEADER_QDCOUNT (abuf); + ancount = DNS_HEADER_ANCOUNT (abuf); + if (qdcount != 1) + { + return ARES_EBADRESP; + } + if (ancount == 0) + { + return ARES_ENODATA; + } + + /* Expand the name from the question, and skip past the question. */ + aptr = abuf + HFIXEDSZ; + status = ares_expand_name (aptr, abuf, alen, &hostname, &len); + if (status != ARES_SUCCESS) + { + if (hostname) + { + ares_free(hostname); + } + return status; + } + + if (aptr + len + QFIXEDSZ > abuf + alen) + { + ares_free (hostname); + return ARES_EBADRESP; + } + aptr += len + QFIXEDSZ; + + srv_replies = ares_malloc(ancount * sizeof(**srv_replies)); + if (srv_replies == NULL) + { + ares_free (hostname); + return ARES_ENOMEM; + } + + for (i = 0; i < ancount; ++i) + { + srv_replies[i] = NULL; + } + + /* Examine each answer resource record (RR) in turn. */ + for (i = 0; i < ancount; i++) + { + /* Decode the RR up to the data field. */ + status = ares_expand_name (aptr, abuf, alen, &rr_name, &len); + if (status != ARES_SUCCESS) + { + break; + } + aptr += len; + if (aptr + RRFIXEDSZ > abuf + alen) + { + status = ARES_EBADRESP; + break; + } + rr_type = DNS_RR_TYPE (aptr); + rr_class = DNS_RR_CLASS (aptr); + rr_ttl = DNS_RR_TTL (aptr); + rr_len = DNS_RR_LEN (aptr); + aptr += RRFIXEDSZ; + if (aptr + rr_len > abuf + alen) + { + status = ARES_EBADRESP; + break; + } + + /* Check if we are really looking at a SRV record */ + if (rr_class == C_IN && rr_type == T_SRV) + { + /* parse the SRV record itself */ + if (rr_len < 6) + { + status = ARES_EBADRESP; + break; + } + + /* Allocate storage for this SRV answer appending it to the list */ + srv_curr = ares_malloc_data(CARES_DATATYPE_SRV_REPLY); + if (!srv_curr) + { + status = ARES_ENOMEM; + break; + } + + vptr = aptr; + cares_srv_reply_set_priority(srv_curr, DNS__16BIT(vptr)); + vptr += sizeof(unsigned short); + cares_srv_reply_set_weight(srv_curr, DNS__16BIT(vptr)); + vptr += sizeof(unsigned short); + cares_srv_reply_set_port(srv_curr, DNS__16BIT(vptr)); + vptr += sizeof(unsigned short); + cares_srv_reply_set_ttl(srv_curr, rr_ttl); + + status = ares_expand_name (vptr, abuf, alen, &srv_host, &len); + if (status != ARES_SUCCESS) + break; + cares_srv_reply_set_host(srv_curr, srv_host); + srv_replies[count] = srv_curr; + count++; + } + else if (rr_type != T_CNAME) + { + /* wrong record type */ + status = ARES_ENODATA; + break; + } + + /* Don't lose memory in the next iteration */ + ares_free (rr_name); + rr_name = NULL; + + /* Move on to the next record */ + aptr += rr_len; + } + if (hostname) + ares_free (hostname); + if (rr_name) + ares_free (rr_name); + + *srv_out = cares_malloc_container(CARES_CONTAINER_SRV_REPLY_CONTAINER); + if (*srv_out == NULL) + status = ARES_ENOMEM; + + if (count == 0 && status == ARES_SUCCESS) + status = ARES_ENODATA; + + /* clean up on error */ + if (status != ARES_SUCCESS) + { + if (srv_replies) + { + for(i = 0; i < count; ++i) { + if (srv_replies[i]) + { + ares_free_data(srv_replies[i]); + } + + } + ares_free(srv_replies); + } + if (srv_host) + { + ares_free(srv_host); + } + return status; + } + + /* everything looks fine, return the data */ + (*srv_out)->replies = srv_replies; + cares_srv_reply_container_set_count(*srv_out, count); + return ARES_SUCCESS; +} diff --git a/src/lib/cares_parse_txt_reply.c b/src/lib/cares_parse_txt_reply.c new file mode 100644 index 0000000000..6fd81e1c8f --- /dev/null +++ b/src/lib/cares_parse_txt_reply.c @@ -0,0 +1,205 @@ + +/* Copyright 1998 by the Massachusetts Institute of Technology. + * Copyright (C) 2009 by Jakub Hrozek + * + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting + * documentation, and that the name of M.I.T. not be used in + * advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" + * without express or implied warranty. + */ + +#include "ares_setup.h" + +#ifdef HAVE_NETINET_IN_H +# include +#endif +#ifdef HAVE_NETDB_H +# include +#endif +#ifdef HAVE_ARPA_INET_H +# include +#endif + +#include "ares_nameser.h" + +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "ares.h" +#include "ares_dns.h" +#include "ares_data.h" +#include "ares_private.h" +#include "cares_memdup.h" + +int cares_parse_txt_reply (const unsigned char *abuf, int alen, + cares_txt_reply **txt_out) +{ + size_t substr_len; + unsigned int qdcount, ancount, i; + const unsigned char *aptr; + const unsigned char *strptr; + unsigned char* txt = NULL; + int status, rr_type, rr_class, rr_len; + unsigned int rr_ttl; + long len; + char *hostname = NULL, *rr_name = NULL; + cares_txt_reply *txt_head = NULL; + cares_txt_reply *txt_last = NULL; + cares_txt_reply *txt_curr; + + /* Set *txt_out to NULL for all failure cases. */ + *txt_out = NULL; + + /* Give up if abuf doesn't have room for a header. */ + if (alen < HFIXEDSZ) + return ARES_EBADRESP; + + /* Fetch the question and answer count from the header. */ + qdcount = DNS_HEADER_QDCOUNT (abuf); + ancount = DNS_HEADER_ANCOUNT (abuf); + if (qdcount != 1) + return ARES_EBADRESP; + if (ancount == 0) + return ARES_ENODATA; + + /* Expand the name from the question, and skip past the question. */ + aptr = abuf + HFIXEDSZ; + status = ares_expand_name (aptr, abuf, alen, &hostname, &len); + if (status != ARES_SUCCESS) + return status; + + if (aptr + len + QFIXEDSZ > abuf + alen) + { + ares_free (hostname); + return ARES_EBADRESP; + } + aptr += len + QFIXEDSZ; + + /* Examine each answer resource record (RR) in turn. */ + for (i = 0; i < ancount; i++) + { + /* Decode the RR up to the data field. */ + status = ares_expand_name (aptr, abuf, alen, &rr_name, &len); + if (status != ARES_SUCCESS) + { + break; + } + aptr += len; + if (aptr + RRFIXEDSZ > abuf + alen) + { + status = ARES_EBADRESP; + break; + } + rr_type = DNS_RR_TYPE (aptr); + rr_class = DNS_RR_CLASS (aptr); + rr_ttl = DNS_RR_TTL(aptr); + rr_len = DNS_RR_LEN (aptr); + aptr += RRFIXEDSZ; + if (aptr + rr_len > abuf + alen) + { + status = ARES_EBADRESP; + break; + } + + /* Check if we are really looking at a TXT record */ + if ((rr_class == C_IN || rr_class == C_CHAOS) && rr_type == T_TXT) + { + /* + * There may be multiple substrings in a single TXT record. Each + * substring may be up to 255 characters in length, with a + * "length byte" indicating the size of the substring payload. + * RDATA contains both the length-bytes and payloads of all + * substrings contained therein. + */ + + strptr = aptr; + while (strptr < (aptr + rr_len)) + { + substr_len = (unsigned char)*strptr; + if (strptr + substr_len + 1 > aptr + rr_len) + { + status = ARES_EBADRESP; + break; + } + + /* Allocate storage for this TXT answer appending it to the list */ + txt_curr = ares_malloc_data(CARES_DATATYPE_TXT_REPLY); + if (!txt_curr) + { + status = ARES_ENOMEM; + break; + } + if (txt_last) + { + cares_txt_reply_set_next(txt_last, txt_curr); + } + else + { + txt_head = txt_curr; + } + txt_last = txt_curr; + + + cares_txt_reply_set_record_start(txt_curr, (strptr == aptr)); + cares_txt_reply_set_length(txt_curr, substr_len); + cares_txt_reply_set_ttl(txt_curr, rr_ttl); + + ++strptr; + txt = cares_memdup((unsigned char*) strptr, cares_txt_reply_get_length(txt_curr)); + if (!txt) { + status = ARES_ENOMEM; + break; + } + + cares_txt_reply_set_txt(txt_curr, txt); + + strptr += substr_len; + } + } + else if (rr_type != T_CNAME) + { + /* wrong record type */ + status = ARES_ENODATA; + break; + } + + /* Propagate any failures */ + if (status != ARES_SUCCESS) + { + break; + } + + /* Don't lose memory in the next iteration */ + ares_free (rr_name); + rr_name = NULL; + + /* Move on to the next record */ + aptr += rr_len; + } + + if (hostname) + ares_free (hostname); + if (rr_name) + ares_free (rr_name); + + /* clean up on error */ + if (status != ARES_SUCCESS) + { + if (txt_head) + ares_free_data (txt_head); + return status; + } + + /* everything looks fine, return the data */ + *txt_out = txt_head; + + return ARES_SUCCESS; +} diff --git a/src/lib/cares_reply.c b/src/lib/cares_reply.c new file mode 100644 index 0000000000..7ee41727a0 --- /dev/null +++ b/src/lib/cares_reply.c @@ -0,0 +1,534 @@ +/* Copyright (C) 2021 by Kyle Evans + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of M.I.T. not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. M.I.T. makes no representations about the + * suitability of this software for any purpose. It is provided "as is" + * without express or implied warranty. + */ + +#include "ares_setup.h" +#include "ares.h" +#include "ares_private.h" +#include "string.h" + + +// const cares_srv_reply* +// cares_srv_reply_get_next(const cares_srv_reply* srv_reply) +// { +// return srv_reply->next; +// } + +const char* cares_srv_reply_get_host(const cares_srv_reply* srv_reply) +{ + return srv_reply->host; +} + +unsigned short +cares_srv_reply_get_priority(const cares_srv_reply* srv_reply) +{ + return srv_reply->priority; +} + +unsigned short +cares_srv_reply_get_weight(const cares_srv_reply* srv_reply) +{ + return srv_reply->weight; +} + +unsigned short +cares_srv_reply_get_port(const cares_srv_reply* srv_reply) +{ + return srv_reply->port; +} + +unsigned int cares_srv_reply_get_ttl(const cares_srv_reply* srv_reply) +{ + return srv_reply->ttl; +} + +// void cares_srv_reply_set_next(cares_srv_reply* srv_reply, +// cares_srv_reply* next) +// { +// srv_reply->next = next; +// } + +void cares_srv_reply_set_host(cares_srv_reply* srv_reply, char* host) +{ + srv_reply->host = host; +} + +void cares_srv_reply_set_priority(cares_srv_reply* srv_reply, + const unsigned short priority) +{ + srv_reply->priority = priority; +} + +void cares_srv_reply_set_weight(cares_srv_reply* srv_reply, + const unsigned short weight) +{ + srv_reply->weight = weight; +} + +void cares_srv_reply_set_port(cares_srv_reply* srv_reply, + const unsigned short port) +{ + srv_reply->port = port; +} + +void cares_srv_reply_set_ttl(cares_srv_reply* srv_reply, + const unsigned int ttl) +{ + srv_reply->ttl = ttl; +} + + + +const cares_caa_reply* +cares_caa_reply_get_next(const cares_caa_reply* caa_reply) +{ + return caa_reply->next; +} + +int +cares_caa_reply_get_critical(const cares_caa_reply* caa_reply) +{ + return caa_reply->critical; +} + +const unsigned char* +cares_caa_reply_get_property(const cares_caa_reply* caa_reply) +{ + return caa_reply->property; +} + +size_t +cares_caa_reply_get_plength(const cares_caa_reply* caa_reply) +{ + return caa_reply->plength; +} + +const unsigned char* +cares_caa_reply_get_value(const cares_caa_reply* caa_reply) +{ + return caa_reply->value; +} + +size_t +cares_caa_reply_get_length(const cares_caa_reply* caa_reply) +{ + return caa_reply->length; +} + +unsigned int cares_caa_reply_get_ttl(const cares_caa_reply* caa_reply) +{ + return caa_reply->ttl; +} + +void cares_caa_reply_set_next(cares_caa_reply* caa_reply, + cares_caa_reply* next) +{ + caa_reply->next = next; +} + +void cares_caa_reply_set_critical(cares_caa_reply* caa_reply, + const int critical) +{ + caa_reply->critical = critical; +} + +void cares_caa_reply_set_property(cares_caa_reply* caa_reply, + unsigned char* property) +{ + caa_reply->property = property; +} + +void cares_caa_reply_set_plength(cares_caa_reply* caa_reply, + const size_t plength) +{ + caa_reply->plength = plength; +} + +void cares_caa_reply_set_value(cares_caa_reply* caa_reply, + unsigned char* value) +{ + caa_reply->value = value; +} + +void cares_caa_reply_set_length(cares_caa_reply* caa_reply, + const size_t length) +{ + caa_reply->length = length; +} + +void cares_caa_reply_set_ttl(cares_caa_reply* caa_reply, + const unsigned int ttl) +{ + caa_reply->ttl = ttl; +} + +const cares_ptr_reply* +cares_ptr_reply_get_next(const cares_ptr_reply* ptr_reply) +{ + return ptr_reply->next; +} + +const char* +cares_ptr_reply_get_host(const cares_ptr_reply* ptr_reply) +{ + return ptr_reply->host; +} + +unsigned int cares_ptr_reply_get_ttl(const cares_ptr_reply* ptr_reply) +{ + return ptr_reply->ttl; +} + +void cares_ptr_reply_set_next(cares_ptr_reply* ptr_reply, + cares_ptr_reply* next) +{ + ptr_reply->next = next; +} + +void cares_ptr_reply_set_host(cares_ptr_reply* ptr_reply, char* host) +{ + ptr_reply->host = host; +} + +void cares_ptr_reply_set_ttl(cares_ptr_reply* ptr_reply, + const unsigned int ttl) +{ + ptr_reply->ttl = ttl; +} + +const cares_ns_reply* +cares_ns_reply_get_next(const cares_ns_reply* ns_reply) +{ + return ns_reply->next; +} + +const char* +cares_ns_reply_get_host(const cares_ns_reply* ns_reply) +{ + return ns_reply->host; +} + +unsigned int cares_ns_reply_get_ttl(const cares_ns_reply* ns_reply) +{ + return ns_reply->ttl; +} + +void cares_ns_reply_set_next(cares_ns_reply* ns_reply, + cares_ns_reply* next) +{ + ns_reply->next = next; +} + +void cares_ns_reply_set_host(cares_ns_reply* ns_reply, char* host) +{ + ns_reply->host = host; +} + +void cares_ns_reply_set_ttl(cares_ns_reply* ns_reply, + const unsigned int ttl) +{ + ns_reply->ttl = ttl; +} + +const cares_mx_reply* +cares_mx_reply_get_next(const cares_mx_reply* mx_reply) +{ + return mx_reply->next; +} + +const char* +cares_mx_reply_get_host(const cares_mx_reply* mx_reply) +{ + return mx_reply->host; +} + +unsigned short +cares_mx_reply_get_priority(const cares_mx_reply* mx_reply) +{ + return mx_reply->priority; +} + +unsigned int cares_mx_reply_get_ttl(const cares_mx_reply* mx_reply) +{ + return mx_reply->ttl; +} + +void cares_mx_reply_set_next(cares_mx_reply* mx_reply, + cares_mx_reply* next) +{ + mx_reply->next = next; +} + +void cares_mx_reply_set_host(cares_mx_reply* mx_reply, char *host) +{ + mx_reply->host = host; +} + +void cares_mx_reply_set_priority(cares_mx_reply* mx_reply, + const unsigned short priority) +{ + mx_reply->priority = priority; +} + +void cares_mx_reply_set_ttl(cares_mx_reply* mx_reply, + const unsigned int ttl) +{ + mx_reply->ttl = ttl; +} + +const cares_txt_reply* +cares_txt_reply_get_next(const cares_txt_reply* txt_reply) +{ + return txt_reply->next; +} + +const unsigned char* +cares_txt_reply_get_txt(const cares_txt_reply* txt_reply) +{ + return txt_reply->txt; +} + +size_t +cares_txt_reply_get_length(const cares_txt_reply* txt_reply) +{ + return txt_reply->length; +} + +unsigned char +cares_txt_reply_get_record_start(const cares_txt_reply* txt_reply) +{ + return txt_reply->record_start; +} + +unsigned int cares_txt_reply_get_ttl(const cares_txt_reply* txt_reply) +{ + return txt_reply->ttl; +} + +void cares_txt_reply_set_next(cares_txt_reply* txt_reply, + cares_txt_reply* next) +{ + txt_reply->next = next; +} + +void cares_txt_reply_set_txt(cares_txt_reply* txt_reply, + unsigned char* txt) +{ + txt_reply->txt = txt; +} + +void cares_txt_reply_set_length(cares_txt_reply* txt_reply, + const size_t length) +{ + txt_reply->length = length; +} + +void cares_txt_reply_set_record_start(cares_txt_reply* txt_reply, + const unsigned char record_start) +{ + txt_reply->record_start = record_start; +} + +void cares_txt_reply_set_ttl(cares_txt_reply* txt_reply, + const unsigned int ttl) +{ + txt_reply->ttl = ttl; +} + +const cares_naptr_reply* +cares_naptr_reply_get_next(const cares_naptr_reply* naptr_reply) +{ + return naptr_reply->next; +} + +const unsigned char* +cares_naptr_reply_get_flags(const cares_naptr_reply* naptr_reply) +{ + return naptr_reply->flags; +} + +const unsigned char* +cares_naptr_reply_get_service(const cares_naptr_reply* naptr_reply) +{ + return naptr_reply->service; +} + +const unsigned char* +cares_naptr_reply_get_regexp(const cares_naptr_reply* naptr_reply) +{ + return naptr_reply->regexp; +} + +const char* +cares_naptr_reply_get_replacement(const cares_naptr_reply* naptr_reply) +{ + return naptr_reply->replacement; +} + +unsigned short +cares_naptr_reply_get_order(const cares_naptr_reply* naptr_reply) +{ + return naptr_reply->order; +} + +unsigned short +cares_naptr_reply_get_preference(const cares_naptr_reply* naptr_reply) +{ + return naptr_reply->preference; +} + +unsigned int cares_naptr_reply_get_ttl(const cares_naptr_reply* naptr_reply) +{ + return naptr_reply->ttl; +} + +void cares_naptr_reply_set_next(cares_naptr_reply* naptr_reply, + cares_naptr_reply* next) +{ + naptr_reply->next = next; +} + +void cares_naptr_reply_set_flags(cares_naptr_reply* naptr_reply, + unsigned char* flags) +{ + naptr_reply->flags = flags; +} + +void cares_naptr_reply_set_service(cares_naptr_reply* naptr_reply, + unsigned char* service) +{ + naptr_reply->service = service; +} + +void cares_naptr_reply_set_regexp(cares_naptr_reply* naptr_reply, + unsigned char* regexp) +{ + naptr_reply->regexp = regexp; +} + +void cares_naptr_reply_set_replacement(cares_naptr_reply* naptr_reply, + char* replacement) +{ + naptr_reply->replacement = replacement; +} + +void cares_naptr_reply_set_order(cares_naptr_reply* naptr_reply, + const unsigned short order) +{ + naptr_reply->order = order; +} + +void cares_naptr_reply_set_preference(cares_naptr_reply* naptr_reply, + const unsigned short preference) +{ + naptr_reply->preference = preference; +} + +void cares_naptr_reply_set_ttl(cares_naptr_reply* naptr_reply, + const unsigned int ttl) +{ + naptr_reply->ttl = ttl; +} + +const char* +cares_soa_reply_get_nsname(const cares_soa_reply* soa_reply) +{ + return soa_reply->nsname; +} + +const char* +cares_soa_reply_get_hostmaster(const cares_soa_reply* soa_reply) +{ + return soa_reply->hostmaster; +} + +unsigned int +cares_soa_reply_get_serial(const cares_soa_reply* soa_reply) +{ + return soa_reply->serial; +} + +unsigned int +cares_soa_reply_get_refresh(const cares_soa_reply* soa_reply) +{ + return soa_reply->refresh; +} + +unsigned int +cares_soa_reply_get_retry(const cares_soa_reply* soa_reply) +{ + return soa_reply->retry; +} + +unsigned int +cares_soa_reply_get_expire(const cares_soa_reply* soa_reply) +{ + return soa_reply->expire; +} + +unsigned int +cares_soa_reply_get_minttl(const cares_soa_reply* soa_reply) +{ + return soa_reply->minttl; +} + +unsigned int cares_soa_reply_get_ttl(const cares_soa_reply* soa_reply) +{ + return soa_reply->ttl; +} + +void cares_soa_reply_set_nsname(cares_soa_reply* soa_reply, char* nsname) +{ + soa_reply->nsname = nsname; +} + +void cares_soa_reply_set_hostmaster(cares_soa_reply* soa_reply, + char* hostmaster) +{ + soa_reply->hostmaster = hostmaster; +} + +void cares_soa_reply_set_serial(cares_soa_reply* soa_reply, + const unsigned int serial) +{ + soa_reply->serial = serial; +} + +void cares_soa_reply_set_refresh(cares_soa_reply* soa_reply, + const unsigned int refresh) +{ + soa_reply->refresh = refresh; +} + +void cares_soa_reply_set_retry(cares_soa_reply* soa_reply, + const unsigned int retry) +{ + soa_reply->retry = retry; +} + +void cares_soa_reply_set_expire(cares_soa_reply* soa_reply, + const unsigned int expire) +{ + soa_reply->expire = expire; +} + +void cares_soa_reply_set_minttl(cares_soa_reply* soa_reply, + const unsigned int minttl) +{ + soa_reply->minttl = minttl; +} + +void cares_soa_reply_set_ttl(cares_soa_reply* soa_reply, + const unsigned int ttl) +{ + soa_reply->ttl = ttl; +} diff --git a/test/aminclude_static.am b/test/aminclude_static.am new file mode 100644 index 0000000000..0a943e225b --- /dev/null +++ b/test/aminclude_static.am @@ -0,0 +1,126 @@ + +# aminclude_static.am generated automatically by Autoconf +# from AX_AM_MACROS_STATIC on Sun Jan 2 13:43:16 EST 2022 + + +# Code coverage +# +# Optional: +# - CODE_COVERAGE_DIRECTORY: Top-level directory for code coverage reporting. +# Multiple directories may be specified, separated by whitespace. +# (Default: $(top_builddir)) +# - CODE_COVERAGE_OUTPUT_FILE: Filename and path for the .info file generated +# by lcov for code coverage. (Default: +# $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage.info) +# - CODE_COVERAGE_OUTPUT_DIRECTORY: Directory for generated code coverage +# reports to be created. (Default: +# $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage) +# - CODE_COVERAGE_BRANCH_COVERAGE: Set to 1 to enforce branch coverage, +# set to 0 to disable it and leave empty to stay with the default. +# (Default: empty) +# - CODE_COVERAGE_LCOV_SHOPTS_DEFAULT: Extra options shared between both lcov +# instances. (Default: based on ) +# - CODE_COVERAGE_LCOV_SHOPTS: Extra options to shared between both lcov +# instances. (Default: ) +# - CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH: --gcov-tool pathtogcov +# - CODE_COVERAGE_LCOV_OPTIONS_DEFAULT: Extra options to pass to the +# collecting lcov instance. (Default: ) +# - CODE_COVERAGE_LCOV_OPTIONS: Extra options to pass to the collecting lcov +# instance. (Default: ) +# - CODE_COVERAGE_LCOV_RMOPTS_DEFAULT: Extra options to pass to the filtering +# lcov instance. (Default: empty) +# - CODE_COVERAGE_LCOV_RMOPTS: Extra options to pass to the filtering lcov +# instance. (Default: ) +# - CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT: Extra options to pass to the +# genhtml instance. (Default: based on ) +# - CODE_COVERAGE_GENHTML_OPTIONS: Extra options to pass to the genhtml +# instance. (Default: ) +# - CODE_COVERAGE_IGNORE_PATTERN: Extra glob pattern of files to ignore +# +# The generated report will be titled using the $(PACKAGE_NAME) and +# $(PACKAGE_VERSION). In order to add the current git hash to the title, +# use the git-version-gen script, available online. +# Optional variables +# run only on top dir +if CODE_COVERAGE_ENABLED + ifeq ($(abs_builddir), $(abs_top_builddir)) +CODE_COVERAGE_DIRECTORY ?= $(top_builddir) +CODE_COVERAGE_OUTPUT_FILE ?= $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage.info +CODE_COVERAGE_OUTPUT_DIRECTORY ?= $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage + +CODE_COVERAGE_BRANCH_COVERAGE ?= +CODE_COVERAGE_LCOV_SHOPTS_DEFAULT ?= $(if $(CODE_COVERAGE_BRANCH_COVERAGE),--rc lcov_branch_coverage=$(CODE_COVERAGE_BRANCH_COVERAGE)) +CODE_COVERAGE_LCOV_SHOPTS ?= $(CODE_COVERAGE_LCOV_SHOPTS_DEFAULT) +CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH ?= --gcov-tool "$(GCOV)" +CODE_COVERAGE_LCOV_OPTIONS_DEFAULT ?= $(CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH) +CODE_COVERAGE_LCOV_OPTIONS ?= $(CODE_COVERAGE_LCOV_OPTIONS_DEFAULT) +CODE_COVERAGE_LCOV_RMOPTS_DEFAULT ?= +CODE_COVERAGE_LCOV_RMOPTS ?= $(CODE_COVERAGE_LCOV_RMOPTS_DEFAULT) +CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT ?=$(if $(CODE_COVERAGE_BRANCH_COVERAGE),--rc genhtml_branch_coverage=$(CODE_COVERAGE_BRANCH_COVERAGE)) +CODE_COVERAGE_GENHTML_OPTIONS ?= $(CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT) +CODE_COVERAGE_IGNORE_PATTERN ?= + +GITIGNOREFILES := $(GITIGNOREFILES) $(CODE_COVERAGE_OUTPUT_FILE) $(CODE_COVERAGE_OUTPUT_DIRECTORY) +code_coverage_v_lcov_cap = $(code_coverage_v_lcov_cap_$(V)) +code_coverage_v_lcov_cap_ = $(code_coverage_v_lcov_cap_$(AM_DEFAULT_VERBOSITY)) +code_coverage_v_lcov_cap_0 = @echo " LCOV --capture" $(CODE_COVERAGE_OUTPUT_FILE); +code_coverage_v_lcov_ign = $(code_coverage_v_lcov_ign_$(V)) +code_coverage_v_lcov_ign_ = $(code_coverage_v_lcov_ign_$(AM_DEFAULT_VERBOSITY)) +code_coverage_v_lcov_ign_0 = @echo " LCOV --remove /tmp/*" $(CODE_COVERAGE_IGNORE_PATTERN); +code_coverage_v_genhtml = $(code_coverage_v_genhtml_$(V)) +code_coverage_v_genhtml_ = $(code_coverage_v_genhtml_$(AM_DEFAULT_VERBOSITY)) +code_coverage_v_genhtml_0 = @echo " GEN " "$(CODE_COVERAGE_OUTPUT_DIRECTORY)"; +code_coverage_quiet = $(code_coverage_quiet_$(V)) +code_coverage_quiet_ = $(code_coverage_quiet_$(AM_DEFAULT_VERBOSITY)) +code_coverage_quiet_0 = --quiet + +# sanitizes the test-name: replaces with underscores: dashes and dots +code_coverage_sanitize = $(subst -,_,$(subst .,_,$(1))) + +# Use recursive makes in order to ignore errors during check +check-code-coverage: + -$(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -k check + $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) code-coverage-capture + +# Capture code coverage data +code-coverage-capture: code-coverage-capture-hook + $(code_coverage_v_lcov_cap)$(LCOV) $(code_coverage_quiet) $(addprefix --directory ,$(CODE_COVERAGE_DIRECTORY)) --capture --output-file "$(CODE_COVERAGE_OUTPUT_FILE).tmp" --test-name "$(call code_coverage_sanitize,$(PACKAGE_NAME)-$(PACKAGE_VERSION))" --no-checksum --compat-libtool $(CODE_COVERAGE_LCOV_SHOPTS) $(CODE_COVERAGE_LCOV_OPTIONS) + $(code_coverage_v_lcov_ign)$(LCOV) $(code_coverage_quiet) $(addprefix --directory ,$(CODE_COVERAGE_DIRECTORY)) --remove "$(CODE_COVERAGE_OUTPUT_FILE).tmp" "/tmp/*" $(CODE_COVERAGE_IGNORE_PATTERN) --output-file "$(CODE_COVERAGE_OUTPUT_FILE)" $(CODE_COVERAGE_LCOV_SHOPTS) $(CODE_COVERAGE_LCOV_RMOPTS) + -@rm -f "$(CODE_COVERAGE_OUTPUT_FILE).tmp" + $(code_coverage_v_genhtml)LANG=C $(GENHTML) $(code_coverage_quiet) $(addprefix --prefix ,$(CODE_COVERAGE_DIRECTORY)) --output-directory "$(CODE_COVERAGE_OUTPUT_DIRECTORY)" --title "$(PACKAGE_NAME)-$(PACKAGE_VERSION) Code Coverage" --legend --show-details "$(CODE_COVERAGE_OUTPUT_FILE)" $(CODE_COVERAGE_GENHTML_OPTIONS) + @echo "file://$(abs_builddir)/$(CODE_COVERAGE_OUTPUT_DIRECTORY)/index.html" + +code-coverage-clean: + -$(LCOV) --directory $(top_builddir) -z + -rm -rf "$(CODE_COVERAGE_OUTPUT_FILE)" "$(CODE_COVERAGE_OUTPUT_FILE).tmp" "$(CODE_COVERAGE_OUTPUT_DIRECTORY)" + -find . \( -name "*.gcda" -o -name "*.gcno" -o -name "*.gcov" \) -delete + +code-coverage-dist-clean: + +AM_DISTCHECK_CONFIGURE_FLAGS := $(AM_DISTCHECK_CONFIGURE_FLAGS) --disable-code-coverage + else # ifneq ($(abs_builddir), $(abs_top_builddir)) +check-code-coverage: + +code-coverage-capture: code-coverage-capture-hook + +code-coverage-clean: + +code-coverage-dist-clean: + endif # ifeq ($(abs_builddir), $(abs_top_builddir)) +else #! CODE_COVERAGE_ENABLED +# Use recursive makes in order to ignore errors during check +check-code-coverage: + @echo "Need to reconfigure with --enable-code-coverage" +# Capture code coverage data +code-coverage-capture: code-coverage-capture-hook + @echo "Need to reconfigure with --enable-code-coverage" + +code-coverage-clean: + +code-coverage-dist-clean: + +endif #CODE_COVERAGE_ENABLED +# Hook rule executed before code-coverage-capture, overridable by the user +code-coverage-capture-hook: + +.PHONY: check-code-coverage code-coverage-capture code-coverage-dist-clean code-coverage-clean code-coverage-capture-hook diff --git a/test/ares-test-init.cc b/test/ares-test-init.cc index ff6c6c6e02..d519c9f2a9 100644 --- a/test/ares-test-init.cc +++ b/test/ares-test-init.cc @@ -505,6 +505,7 @@ CONTAINED_TEST_F(LibraryTest, ContainerResolvConfNotReadable, MakeUnreadable hide("/etc/resolv.conf"); // Unavailable /etc/resolv.conf falls back to defaults EXPECT_EQ(ARES_SUCCESS, ares_init(&channel)); + ares_destroy(channel); return HasFailure(); } CONTAINED_TEST_F(LibraryTest, ContainerNsswitchConfNotReadable, diff --git a/test/ares-test-parse-caa.cc b/test/ares-test-parse-caa.cc index 99903edf71..aab12dcc40 100644 --- a/test/ares-test-parse-caa.cc +++ b/test/ares-test-parse-caa.cc @@ -33,12 +33,38 @@ TEST_F(LibraryTest, ParseCaaReplyMultipleOK) { ares_free_data(caa); } +TEST_F(LibraryTest, ParseCCaaReplyMultipleOK) { + std::vector data = { + 0x27, 0x86, 0x81, 0x80, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x09, 0x77, 0x69, 0x6B, // '............wik + 0x69, 0x70, 0x65, 0x64, 0x69, 0x61, 0x03, 0x6F, 0x72, 0x67, 0x00, 0x01, 0x01, 0x00, 0x01, 0xC0, // ipedia.org...... + 0x0C, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x02, 0x23, 0x00, 0x15, 0x00, 0x05, 0x69, 0x73, 0x73, // ........#....iss + 0x75, 0x65, 0x67, 0x6C, 0x6F, 0x62, 0x61, 0x6C, 0x73, 0x69, 0x67, 0x6E, 0x2E, 0x63, 0x6F, 0x6D, // ueglobalsign.com + 0xC0, 0x0C, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x02, 0x23, 0x00, 0x13, 0x00, 0x05, 0x69, 0x73, // .........#....is + 0x73, 0x75, 0x65, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2E, 0x63, 0x6F, 0x6D, 0xC0, // suedigicert.com. + 0x0C, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x02, 0x23, 0x00, 0x16, 0x00, 0x05, 0x69, 0x73, 0x73, // ........#....iss + 0x75, 0x65, 0x6C, 0x65, 0x74, 0x73, 0x65, 0x6E, 0x63, 0x72, 0x79, 0x70, 0x74, 0x2E, 0x6F, 0x72, // ueletsencrypt.or + 0x67, 0xC0, 0x0C, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x02, 0x23, 0x00, 0x25, 0x00, 0x05, 0x69, // g.........#.%..i + 0x6F, 0x64, 0x65, 0x66, 0x6D, 0x61, 0x69, 0x6C, 0x74, 0x6F, 0x3A, 0x64, 0x6E, 0x73, 0x2D, 0x61, // odefmailto:dns-a + 0x64, 0x6D, 0x69, 0x6E, 0x40, 0x77, 0x69, 0x6B, 0x69, 0x6D, 0x65, 0x64, 0x69, 0x61, 0x2E, 0x6F, // dmin@wikimedia.o + 0x72, 0x67 // rg + }; + + cares_caa_reply* caa = nullptr; + EXPECT_EQ(ARES_SUCCESS, cares_parse_caa_reply(data.data(), data.size(), &caa)); + ASSERT_NE(nullptr, caa); + ASSERT_NE(nullptr, cares_caa_reply_get_next(caa)); + ASSERT_NE(nullptr, cares_caa_reply_get_next(cares_caa_reply_get_next(caa))); + ASSERT_NE(nullptr, cares_caa_reply_get_next(cares_caa_reply_get_next(cares_caa_reply_get_next(caa)))); + + ares_free_data(caa); +} + TEST_F(LibraryTest, ParseCaaReplySingleOK) { std::vector data = { - 0x27, 0x86, 0x81, 0x80, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x06, 0x67, 0x6F, 0x6F, // '............goo - 0x67, 0x6C, 0x65, 0x03, 0x63, 0x6F, 0x6D, 0x00, 0x01, 0x01, 0x00, 0x01, 0xC0, 0x0C, 0x01, 0x01, // gle.com......... - 0x00, 0x01, 0x00, 0x01, 0x43, 0xBE, 0x00, 0x0F, 0x00, 0x05, 0x69, 0x73, 0x73, 0x75, 0x65, 0x70, // ....C.....issuep - 0x6B, 0x69, 0x2E, 0x67, 0x6F, 0x6F, 0x67 // ki.goog + 0x27, 0x86, 0x81, 0x80, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x06, 0x67, 0x6F, 0x6F, // '............goo + 0x67, 0x6C, 0x65, 0x03, 0x63, 0x6F, 0x6D, 0x00, 0x01, 0x01, 0x00, 0x01, 0xC0, 0x0C, 0x01, 0x01, // gle.com......... + 0x00, 0x01, 0x00, 0x01, 0x43, 0xBE, 0x00, 0x0F, 0x00, 0x05, 0x69, 0x73, 0x73, 0x75, 0x65, 0x70, // ....C.....issuep + 0x6B, 0x69, 0x2E, 0x67, 0x6F, 0x6F, 0x67 // ki.goog }; struct ares_caa_reply* caa = nullptr; @@ -54,12 +80,34 @@ TEST_F(LibraryTest, ParseCaaReplySingleOK) { ares_free_data(caa); } +TEST_F(LibraryTest, ParseCCaaReplySingleOK) { + std::vector data = { + 0x27, 0x86, 0x81, 0x80, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x06, 0x67, 0x6F, 0x6F, // '............goo + 0x67, 0x6C, 0x65, 0x03, 0x63, 0x6F, 0x6D, 0x00, 0x01, 0x01, 0x00, 0x01, 0xC0, 0x0C, 0x01, 0x01, // gle.com......... + 0x00, 0x01, 0x00, 0x01, 0x43, 0xBE, 0x00, 0x0F, 0x00, 0x05, 0x69, 0x73, 0x73, 0x75, 0x65, 0x70, // ....C.....issuep + 0x6B, 0x69, 0x2E, 0x67, 0x6F, 0x6F, 0x67 // ki.goog + }; + + cares_caa_reply* caa = nullptr; + EXPECT_EQ(ARES_SUCCESS, cares_parse_caa_reply(data.data(), data.size(), &caa)); + ASSERT_NE(nullptr, caa); + + EXPECT_EQ(cares_caa_reply_get_critical(caa), 0); + EXPECT_EQ(cares_caa_reply_get_plength(caa), 5); + EXPECT_STREQ((char *)cares_caa_reply_get_property(caa), "issue"); + EXPECT_EQ(cares_caa_reply_get_length(caa), 8); + EXPECT_STREQ((char *)cares_caa_reply_get_value(caa), "pki.goog"); + EXPECT_EQ(cares_caa_reply_get_ttl(caa), 82878); + + ares_free_data(caa); +} + TEST_F(LibraryTest, ParseCaaBogusReply1) { std::vector data = { - 0x27, 0x86, 0x81, 0x80, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x06, 0x67, 0x6F, 0x6F, // '............goo - 0x67, 0x6C, 0x65, 0x03, 0x63, 0x6F, 0x6D, 0x00, 0x01, 0x01, 0x00, 0x01, 0xC0, 0x0C, 0x01, 0x01, // gle.com......... - 0x00, 0x01, 0x00, 0x01, 0x43, 0xBE, 0x00, 0x0F, 0x00, 0x00, 0x69, 0x73, 0x73, 0x75, 0x65, 0x70, // ....C.....issuep - 0x6B, 0x69, 0x2E, 0x67, 0x6F, 0x6F, 0x67 // ki.goog + 0x27, 0x86, 0x81, 0x80, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x06, 0x67, 0x6F, 0x6F, // '............goo + 0x67, 0x6C, 0x65, 0x03, 0x63, 0x6F, 0x6D, 0x00, 0x01, 0x01, 0x00, 0x01, 0xC0, 0x0C, 0x01, 0x01, // gle.com......... + 0x00, 0x01, 0x00, 0x01, 0x43, 0xBE, 0x00, 0x0F, 0x00, 0x00, 0x69, 0x73, 0x73, 0x75, 0x65, 0x70, // ....C.....issuep + 0x6B, 0x69, 0x2E, 0x67, 0x6F, 0x6F, 0x67 // ki.goog }; struct ares_caa_reply* caa = nullptr; @@ -67,12 +115,25 @@ TEST_F(LibraryTest, ParseCaaBogusReply1) { ASSERT_EQ(nullptr, caa); } +TEST_F(LibraryTest, ParseCCaaBogusReply1) { + std::vector data = { + 0x27, 0x86, 0x81, 0x80, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x06, 0x67, 0x6F, 0x6F, // '............goo + 0x67, 0x6C, 0x65, 0x03, 0x63, 0x6F, 0x6D, 0x00, 0x01, 0x01, 0x00, 0x01, 0xC0, 0x0C, 0x01, 0x01, // gle.com......... + 0x00, 0x01, 0x00, 0x01, 0x43, 0xBE, 0x00, 0x0F, 0x00, 0x00, 0x69, 0x73, 0x73, 0x75, 0x65, 0x70, // ....C.....issuep + 0x6B, 0x69, 0x2E, 0x67, 0x6F, 0x6F, 0x67 // ki.goog + }; + + cares_caa_reply* caa = nullptr; + EXPECT_EQ(ARES_EBADRESP, cares_parse_caa_reply(data.data(), data.size(), &caa)); + ASSERT_EQ(nullptr, caa); +} + TEST_F(LibraryTest, ParseCaaBogusReply2) { std::vector data = { - 0x27, 0x86, 0x81, 0x80, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x06, 0x67, 0x6F, 0x6F, // '............goo - 0x67, 0x6C, 0x65, 0x03, 0x63, 0x6F, 0x6D, 0x00, 0x01, 0x01, 0x00, 0x01, 0xC0, 0x0C, 0x01, 0x01, // gle.com......... - 0x00, 0x01, 0x00, 0x01, 0x43, 0xBE, 0x00, 0x0F, 0x00, 0x0e, 0x69, 0x73, 0x73, 0x75, 0x65, 0x70, // ....C.....issuep - 0x6B, 0x69, 0x2E, 0x67, 0x6F, 0x6F, 0x67 // ki.goog + 0x27, 0x86, 0x81, 0x80, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x06, 0x67, 0x6F, 0x6F, // '............goo + 0x67, 0x6C, 0x65, 0x03, 0x63, 0x6F, 0x6D, 0x00, 0x01, 0x01, 0x00, 0x01, 0xC0, 0x0C, 0x01, 0x01, // gle.com......... + 0x00, 0x01, 0x00, 0x01, 0x43, 0xBE, 0x00, 0x0F, 0x00, 0x0e, 0x69, 0x73, 0x73, 0x75, 0x65, 0x70, // ....C.....issuep + 0x6B, 0x69, 0x2E, 0x67, 0x6F, 0x6F, 0x67 // ki.goog }; struct ares_caa_reply* caa = nullptr; @@ -80,12 +141,25 @@ TEST_F(LibraryTest, ParseCaaBogusReply2) { ASSERT_EQ(nullptr, caa); } +TEST_F(LibraryTest, ParseCCaaBogusReply2) { + std::vector data = { + 0x27, 0x86, 0x81, 0x80, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x06, 0x67, 0x6F, 0x6F, // '............goo + 0x67, 0x6C, 0x65, 0x03, 0x63, 0x6F, 0x6D, 0x00, 0x01, 0x01, 0x00, 0x01, 0xC0, 0x0C, 0x01, 0x01, // gle.com......... + 0x00, 0x01, 0x00, 0x01, 0x43, 0xBE, 0x00, 0x0F, 0x00, 0x0e, 0x69, 0x73, 0x73, 0x75, 0x65, 0x70, // ....C.....issuep + 0x6B, 0x69, 0x2E, 0x67, 0x6F, 0x6F, 0x67 // ki.goog + }; + + cares_caa_reply* caa = nullptr; + EXPECT_EQ(ARES_EBADRESP, cares_parse_caa_reply(data.data(), data.size(), &caa)); + ASSERT_EQ(nullptr, caa); +} + TEST_F(LibraryTest, ParseCaaBogusReply3) { std::vector data = { - 0x27, 0x86, 0x81, 0x80, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x06, 0x67, 0x6F, 0x6F, // '............goo - 0x67, 0x6C, 0x65, 0x03, 0x63, 0x6F, 0x6D, 0x00, 0x01, 0x01, 0x00, 0x01, 0xC0, 0x0C, 0x01, 0x01, // gle.com......... - 0x00, 0x01, 0x00, 0x01, 0x43, 0xBE, 0x00, 0x10, 0x00, 0x05, 0x69, 0x73, 0x73, 0x75, 0x65, 0x70, // ....C.....issuep - 0x6B, 0x69, 0x2E, 0x67, 0x6F, 0x6F, 0x67 // ki.goog + 0x27, 0x86, 0x81, 0x80, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x06, 0x67, 0x6F, 0x6F, // '............goo + 0x67, 0x6C, 0x65, 0x03, 0x63, 0x6F, 0x6D, 0x00, 0x01, 0x01, 0x00, 0x01, 0xC0, 0x0C, 0x01, 0x01, // gle.com......... + 0x00, 0x01, 0x00, 0x01, 0x43, 0xBE, 0x00, 0x10, 0x00, 0x05, 0x69, 0x73, 0x73, 0x75, 0x65, 0x70, // ....C.....issuep + 0x6B, 0x69, 0x2E, 0x67, 0x6F, 0x6F, 0x67 // ki.goog }; struct ares_caa_reply* caa = nullptr; @@ -93,15 +167,28 @@ TEST_F(LibraryTest, ParseCaaBogusReply3) { ASSERT_EQ(nullptr, caa); } +TEST_F(LibraryTest, ParseCCaaBogusReply3) { + std::vector data = { + 0x27, 0x86, 0x81, 0x80, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x06, 0x67, 0x6F, 0x6F, // '............goo + 0x67, 0x6C, 0x65, 0x03, 0x63, 0x6F, 0x6D, 0x00, 0x01, 0x01, 0x00, 0x01, 0xC0, 0x0C, 0x01, 0x01, // gle.com......... + 0x00, 0x01, 0x00, 0x01, 0x43, 0xBE, 0x00, 0x10, 0x00, 0x05, 0x69, 0x73, 0x73, 0x75, 0x65, 0x70, // ....C.....issuep + 0x6B, 0x69, 0x2E, 0x67, 0x6F, 0x6F, 0x67 // ki.goog + }; + + cares_caa_reply* caa = nullptr; + EXPECT_EQ(ARES_EBADRESP, cares_parse_caa_reply(data.data(), data.size(), &caa)); + ASSERT_EQ(nullptr, caa); +} + TEST_F(LibraryTest, ParseCaaEmptyReply) { std::vector data = { - 0x27, 0x86, 0x81, 0x80, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x09, 0x77, 0x69, 0x6B, // '............wik - 0x69, 0x70, 0x65, 0x64, 0x69, 0x61, 0x02, 0x64, 0x65, 0x00, 0x01, 0x01, 0x00, 0x01, 0xC0, 0x0C, // ipedia.de....... - 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x02, 0x58, 0x00, 0x3B, 0x04, 0x6E, 0x73, 0x38, 0x31, 0x0D, // .......X.;.ns81. - 0x64, 0x6F, 0x6D, 0x61, 0x69, 0x6E, 0x63, 0x6F, 0x6E, 0x74, 0x72, 0x6F, 0x6C, 0x03, 0x63, 0x6F, // domaincontrol.co - 0x6D, 0x00, 0x03, 0x64, 0x6E, 0x73, 0x05, 0x6A, 0x6F, 0x6D, 0x61, 0x78, 0x03, 0x6E, 0x65, 0x74, // m..dns.jomax.net - 0x00, 0x78, 0x67, 0xFE, 0x34, 0x00, 0x00, 0x70, 0x80, 0x00, 0x00, 0x1C, 0x20, 0x00, 0x09, 0x3A, // .xg.4..p.... ..: - 0x80, 0x00, 0x00, 0x02, 0x58 // ....X + 0x27, 0x86, 0x81, 0x80, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x09, 0x77, 0x69, 0x6B, // '............wik + 0x69, 0x70, 0x65, 0x64, 0x69, 0x61, 0x02, 0x64, 0x65, 0x00, 0x01, 0x01, 0x00, 0x01, 0xC0, 0x0C, // ipedia.de....... + 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x02, 0x58, 0x00, 0x3B, 0x04, 0x6E, 0x73, 0x38, 0x31, 0x0D, // .......X.;.ns81. + 0x64, 0x6F, 0x6D, 0x61, 0x69, 0x6E, 0x63, 0x6F, 0x6E, 0x74, 0x72, 0x6F, 0x6C, 0x03, 0x63, 0x6F, // domaincontrol.co + 0x6D, 0x00, 0x03, 0x64, 0x6E, 0x73, 0x05, 0x6A, 0x6F, 0x6D, 0x61, 0x78, 0x03, 0x6E, 0x65, 0x74, // m..dns.jomax.net + 0x00, 0x78, 0x67, 0xFE, 0x34, 0x00, 0x00, 0x70, 0x80, 0x00, 0x00, 0x1C, 0x20, 0x00, 0x09, 0x3A, // .xg.4..p.... ..: + 0x80, 0x00, 0x00, 0x02, 0x58 // ....X }; struct ares_caa_reply* caa = nullptr; @@ -109,5 +196,21 @@ TEST_F(LibraryTest, ParseCaaEmptyReply) { ASSERT_EQ(nullptr, caa); } +TEST_F(LibraryTest, ParseCCaaEmptyReply) { + std::vector data = { + 0x27, 0x86, 0x81, 0x80, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x09, 0x77, 0x69, 0x6B, // '............wik + 0x69, 0x70, 0x65, 0x64, 0x69, 0x61, 0x02, 0x64, 0x65, 0x00, 0x01, 0x01, 0x00, 0x01, 0xC0, 0x0C, // ipedia.de....... + 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x02, 0x58, 0x00, 0x3B, 0x04, 0x6E, 0x73, 0x38, 0x31, 0x0D, // .......X.;.ns81. + 0x64, 0x6F, 0x6D, 0x61, 0x69, 0x6E, 0x63, 0x6F, 0x6E, 0x74, 0x72, 0x6F, 0x6C, 0x03, 0x63, 0x6F, // domaincontrol.co + 0x6D, 0x00, 0x03, 0x64, 0x6E, 0x73, 0x05, 0x6A, 0x6F, 0x6D, 0x61, 0x78, 0x03, 0x6E, 0x65, 0x74, // m..dns.jomax.net + 0x00, 0x78, 0x67, 0xFE, 0x34, 0x00, 0x00, 0x70, 0x80, 0x00, 0x00, 0x1C, 0x20, 0x00, 0x09, 0x3A, // .xg.4..p.... ..: + 0x80, 0x00, 0x00, 0x02, 0x58 // ....X + }; + + cares_caa_reply* caa = nullptr; + EXPECT_EQ(ARES_ENODATA, cares_parse_caa_reply(data.data(), data.size(), &caa)); + ASSERT_EQ(nullptr, caa); +} + } // namespace test } // namespace ares diff --git a/test/ares-test-parse-mx.cc b/test/ares-test-parse-mx.cc index db8fa89404..cc8998e35f 100644 --- a/test/ares-test-parse-mx.cc +++ b/test/ares-test-parse-mx.cc @@ -30,6 +30,31 @@ TEST_F(LibraryTest, ParseMxReplyOK) { ares_free_data(mx); } +TEST_F(LibraryTest, ParseCMxReplyOK) { + DNSPacket pkt; + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("example.com", T_MX)) + .add_answer(new DNSMxRR("example.com", 100, 100, "mx1.example.com")) + .add_answer(new DNSMxRR("example.com", 100, 200, "mx2.example.com")); + std::vector data = pkt.data(); + + cares_mx_reply* mx = nullptr; + EXPECT_EQ(ARES_SUCCESS, cares_parse_mx_reply(data.data(), data.size(), &mx)); + ASSERT_NE(nullptr, mx); + EXPECT_EQ("mx1.example.com", std::string(cares_mx_reply_get_host(mx))); + EXPECT_EQ(100, cares_mx_reply_get_priority(mx)); + EXPECT_EQ(100, cares_mx_reply_get_ttl(mx)); + + const cares_mx_reply* mx2 = cares_mx_reply_get_next(mx); + ASSERT_NE(nullptr, mx2); + EXPECT_EQ("mx2.example.com", std::string(cares_mx_reply_get_host(mx2))); + EXPECT_EQ(200, cares_mx_reply_get_priority(mx2)); + EXPECT_EQ(100, cares_mx_reply_get_ttl(mx2)); + EXPECT_EQ(nullptr, cares_mx_reply_get_next(mx2)); + + ares_free_data(mx); +} + TEST_F(LibraryTest, ParseMxReplyMalformed) { std::vector data = { 0x12, 0x34, // qid @@ -61,6 +86,37 @@ TEST_F(LibraryTest, ParseMxReplyMalformed) { ASSERT_EQ(nullptr, mx); } +TEST_F(LibraryTest, ParseCMxReplyMalformed) { + std::vector data = { + 0x12, 0x34, // qid + 0x84, // response + query + AA + not-TC + not-RD + 0x00, // not-RA + not-Z + not-AD + not-CD + rc=NoError + 0x00, 0x01, // num questions + 0x00, 0x01, // num answer RRs + 0x00, 0x00, // num authority RRs + 0x00, 0x00, // num additional RRs + // Question + 0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e', + 0x03, 'c', 'o', 'm', + 0x00, + 0x00, 0x0F, // type MX + 0x00, 0x01, // class IN + // Answer 1 + 0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e', + 0x03, 'c', 'o', 'm', + 0x00, + 0x00, 0x0F, // RR type + 0x00, 0x01, // class IN + 0x01, 0x02, 0x03, 0x04, // TTL + 0x00, 0x01, // rdata length -- too short + 0x02, + }; + + cares_mx_reply* mx = nullptr; + EXPECT_EQ(ARES_EBADRESP, cares_parse_mx_reply(data.data(), data.size(), &mx)); + ASSERT_EQ(nullptr, mx); +} + TEST_F(LibraryTest, ParseMxReplyErrors) { DNSPacket pkt; @@ -96,11 +152,10 @@ TEST_F(LibraryTest, ParseMxReplyErrors) { pkt.add_question(new DNSQuestion("example.com", T_MX)); // Wrong sort of answer. - // TODO(drysdale): check if this should be ARES_ENODATA? pkt.answers_.clear(); pkt.add_answer(new DNSSrvRR("example.abc.def.com", 180, 0, 10, 8160, "example.abc.def.com")); data = pkt.data(); - EXPECT_EQ(ARES_SUCCESS, ares_parse_mx_reply(data.data(), data.size(), &mx)); + EXPECT_EQ(ARES_ENODATA, ares_parse_mx_reply(data.data(), data.size(), &mx)); EXPECT_EQ(nullptr, mx); pkt.answers_.clear(); pkt.add_answer(new DNSMxRR("example.com", 100, 100, "mx1.example.com")); @@ -121,6 +176,64 @@ TEST_F(LibraryTest, ParseMxReplyErrors) { } } +TEST_F(LibraryTest, ParseCMxReplyErrors) { + DNSPacket pkt; + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("example.com", T_MX)) + .add_answer(new DNSMxRR("example.com", 100, 100, "mx1.example.com")); + std::vector data; + cares_mx_reply* mx = nullptr; + + // No question. + pkt.questions_.clear(); + data = pkt.data(); + EXPECT_EQ(ARES_EBADRESP, cares_parse_mx_reply(data.data(), data.size(), &mx)); + EXPECT_EQ(nullptr, mx); + pkt.add_question(new DNSQuestion("example.com", T_MX)); + +#ifdef DISABLED + // Question != answer + pkt.questions_.clear(); + pkt.add_question(new DNSQuestion("Axample.com", T_MX)); + data = pkt.data(); + EXPECT_EQ(ARES_EBADRESP, cares_parse_mx_reply(data.data(), data.size(), &mx)); + pkt.questions_.clear(); + pkt.add_question(new DNSQuestion("example.com", T_MX)); +#endif + + // Two questions. + pkt.add_question(new DNSQuestion("example.com", T_MX)); + data = pkt.data(); + EXPECT_EQ(ARES_EBADRESP, cares_parse_mx_reply(data.data(), data.size(), &mx)); + EXPECT_EQ(nullptr, mx); + pkt.questions_.clear(); + pkt.add_question(new DNSQuestion("example.com", T_MX)); + + // Wrong sort of answer. + pkt.answers_.clear(); + pkt.add_answer(new DNSSrvRR("example.abc.def.com", 180, 0, 10, 8160, "example.abc.def.com")); + data = pkt.data(); + EXPECT_EQ(ARES_ENODATA, cares_parse_mx_reply(data.data(), data.size(), &mx)); + EXPECT_EQ(nullptr, mx); + pkt.answers_.clear(); + pkt.add_answer(new DNSMxRR("example.com", 100, 100, "mx1.example.com")); + + // No answer. + pkt.answers_.clear(); + data = pkt.data(); + EXPECT_EQ(ARES_ENODATA, cares_parse_mx_reply(data.data(), data.size(), &mx)); + EXPECT_EQ(nullptr, mx); + pkt.add_answer(new DNSMxRR("example.com", 100, 100, "mx1.example.com")); + + // Truncated packets. + data = pkt.data(); + for (size_t len = 1; len < data.size(); len++) { + int rc = cares_parse_mx_reply(data.data(), len, &mx); + EXPECT_EQ(nullptr, mx); + EXPECT_TRUE(rc == ARES_EBADRESP || rc == ARES_EBADNAME); + } +} + TEST_F(LibraryTest, ParseMxReplyAllocFail) { DNSPacket pkt; pkt.set_qid(0x1234).set_response().set_aa() diff --git a/test/ares-test-parse-naptr.cc b/test/ares-test-parse-naptr.cc index aa1a2a5029..40d3f980ee 100644 --- a/test/ares-test-parse-naptr.cc +++ b/test/ares-test-parse-naptr.cc @@ -40,6 +40,41 @@ TEST_F(LibraryTest, ParseNaptrReplyOK) { ares_free_data(naptr); } +TEST_F(LibraryTest, ParseCNaptrReplyOK) { + DNSPacket pkt; + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("example.com", T_NAPTR)) + .add_answer(new DNSNaptrRR("example.com", 100, + 10, 20, "SP", "service", "regexp", "replace")) + .add_answer(new DNSNaptrRR("example.com", 0x0010, + 11, 21, "SP", "service2", "regexp2", "replace2")); + std::vector data = pkt.data(); + + cares_naptr_reply* naptr = nullptr; + EXPECT_EQ(ARES_SUCCESS, cares_parse_naptr_reply(data.data(), data.size(), &naptr)); + ASSERT_NE(nullptr, naptr); + EXPECT_EQ("SP", std::string((char*)cares_naptr_reply_get_flags(naptr))); + EXPECT_EQ("service", std::string((char*)cares_naptr_reply_get_service(naptr))); + EXPECT_EQ("regexp", std::string((char*)cares_naptr_reply_get_regexp(naptr))); + EXPECT_EQ("replace", std::string((char*)cares_naptr_reply_get_replacement(naptr))); + EXPECT_EQ(10, cares_naptr_reply_get_order(naptr)); + EXPECT_EQ(20, cares_naptr_reply_get_preference(naptr)); + EXPECT_EQ(100, cares_naptr_reply_get_ttl(naptr)); + + const cares_naptr_reply* naptr2 = cares_naptr_reply_get_next(naptr); + ASSERT_NE(nullptr, naptr2); + EXPECT_EQ("SP", std::string((char*)cares_naptr_reply_get_flags(naptr2))); + EXPECT_EQ("service2", std::string((char*)cares_naptr_reply_get_service(naptr2))); + EXPECT_EQ("regexp2", std::string((char*)cares_naptr_reply_get_regexp(naptr2))); + EXPECT_EQ("replace2", std::string((char*)cares_naptr_reply_get_replacement(naptr2))); + EXPECT_EQ(11, cares_naptr_reply_get_order(naptr2)); + EXPECT_EQ(21, cares_naptr_reply_get_preference(naptr2)); + EXPECT_EQ(nullptr, cares_naptr_reply_get_next(naptr2)); + EXPECT_EQ(16, cares_naptr_reply_get_ttl(naptr2)); + + ares_free_data(naptr); +} + TEST_F(LibraryTest, ParseNaptrReplyErrors) { DNSPacket pkt; pkt.set_qid(0x1234).set_response().set_aa() @@ -76,7 +111,7 @@ TEST_F(LibraryTest, ParseNaptrReplyErrors) { pkt.answers_.clear(); pkt.add_answer(new DNSMxRR("example.com", 100, 100, "mx1.example.com")); data = pkt.data(); - EXPECT_EQ(ARES_SUCCESS, ares_parse_naptr_reply(data.data(), data.size(), &naptr)); + EXPECT_EQ(ARES_ENODATA, ares_parse_naptr_reply(data.data(), data.size(), &naptr)); EXPECT_EQ(nullptr, naptr); pkt.answers_.clear(); pkt.add_answer(new DNSNaptrRR("example.com", 100, @@ -97,6 +132,63 @@ TEST_F(LibraryTest, ParseNaptrReplyErrors) { } } +TEST_F(LibraryTest, ParseCNaptrReplyErrors) { + DNSPacket pkt; + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("example.com", T_NAPTR)) + .add_answer(new DNSNaptrRR("example.com", 100, + 10, 20, "SP", "service", "regexp", "replace")); + std::vector data; + cares_naptr_reply* naptr = nullptr; + + // No question. + pkt.questions_.clear(); + data = pkt.data(); + EXPECT_EQ(ARES_EBADRESP, cares_parse_naptr_reply(data.data(), data.size(), &naptr)); + pkt.add_question(new DNSQuestion("example.com", T_NAPTR)); + +#ifdef DISABLED + // Question != answer + pkt.questions_.clear(); + pkt.add_question(new DNSQuestion("Axample.com", T_NAPTR)); + data = pkt.data(); + EXPECT_EQ(ARES_ENODATA, cares_parse_naptr_reply(data.data(), data.size(), &naptr)); + pkt.questions_.clear(); + pkt.add_question(new DNSQuestion("example.com", T_NAPTR)); +#endif + + // Two questions + pkt.add_question(new DNSQuestion("example.com", T_NAPTR)); + data = pkt.data(); + EXPECT_EQ(ARES_EBADRESP, cares_parse_naptr_reply(data.data(), data.size(), &naptr)); + pkt.questions_.clear(); + pkt.add_question(new DNSQuestion("example.com", T_NAPTR)); + + // Wrong sort of answer. + pkt.answers_.clear(); + pkt.add_answer(new DNSMxRR("example.com", 100, 100, "mx1.example.com")); + data = pkt.data(); + EXPECT_EQ(ARES_ENODATA, cares_parse_naptr_reply(data.data(), data.size(), &naptr)); + EXPECT_EQ(nullptr, naptr); + pkt.answers_.clear(); + pkt.add_answer(new DNSNaptrRR("example.com", 100, + 10, 20, "SP", "service", "regexp", "replace")); + + // No answer. + pkt.answers_.clear(); + data = pkt.data(); + EXPECT_EQ(ARES_ENODATA, cares_parse_naptr_reply(data.data(), data.size(), &naptr)); + pkt.add_answer(new DNSNaptrRR("example.com", 100, + 10, 20, "SP", "service", "regexp", "replace")); + + // Truncated packets. + data = pkt.data(); + for (size_t len = 1; len < data.size(); len++) { + int rc = cares_parse_naptr_reply(data.data(), len, &naptr); + EXPECT_TRUE(rc == ARES_EBADRESP || rc == ARES_EBADNAME); + } +} + TEST_F(LibraryTest, ParseNaptrReplyTooShort) { std::vector data = { 0x12, 0x34, // qid @@ -126,6 +218,35 @@ TEST_F(LibraryTest, ParseNaptrReplyTooShort) { EXPECT_EQ(ARES_EBADRESP, ares_parse_naptr_reply(data.data(), data.size(), &naptr)); } +TEST_F(LibraryTest, ParseCNaptrReplyTooShort) { + std::vector data = { + 0x12, 0x34, // qid + 0x84, // response + query + AA + not-TC + not-RD + 0x00, // not-RA + not-Z + not-AD + not-CD + rc=NoError + 0x00, 0x01, // num questions + 0x00, 0x01, // num answer RRs + 0x00, 0x00, // num authority RRs + 0x00, 0x00, // num additional RRs + // Question + 0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e', + 0x03, 'c', 'o', 'm', + 0x00, + 0x00, 0x23, // type NAPTR + 0x00, 0x01, // class IN + // Answer 1 + 0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e', + 0x03, 'c', 'o', 'm', + 0x00, + 0x00, 0x23, // RR type + 0x00, 0x01, // class IN + 0x01, 0x02, 0x03, 0x04, // TTL + 0x00, 0x01, // rdata length + 0x00, // Too short: expect 2 x int16 and 3 x name (min 1 byte each) + }; + cares_naptr_reply* naptr = nullptr; + EXPECT_EQ(ARES_EBADRESP, cares_parse_naptr_reply(data.data(), data.size(), &naptr)); +} + TEST_F(LibraryTest, ParseNaptrReplyAllocFail) { DNSPacket pkt; pkt.set_qid(0x1234).set_response().set_aa() @@ -144,5 +265,23 @@ TEST_F(LibraryTest, ParseNaptrReplyAllocFail) { } } +TEST_F(LibraryTest, ParseCNaptrReplyAllocFail) { + DNSPacket pkt; + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("example.com", T_NAPTR)) + .add_answer(new DNSNaptrRR("example.com", 100, + 10, 20, "SP", "service", "regexp", "replace")) + .add_answer(new DNSNaptrRR("example.com", 0x0010, + 11, 21, "SP", "service2", "regexp2", "replace2")); + std::vector data = pkt.data(); + cares_naptr_reply* naptr = nullptr; + + for (int ii = 1; ii <= 13; ii++) { + ClearFails(); + SetAllocFail(ii); + EXPECT_EQ(ARES_ENOMEM, cares_parse_naptr_reply(data.data(), data.size(), &naptr)); + } +} + } // namespace test } // namespace ares diff --git a/test/ares-test-parse-ns.cc b/test/ares-test-parse-ns.cc index 316492174c..8ef284d701 100644 --- a/test/ares-test-parse-ns.cc +++ b/test/ares-test-parse-ns.cc @@ -23,6 +23,21 @@ TEST_F(LibraryTest, ParseNsReplyOK) { ares_free_hostent(host); } +TEST_F(LibraryTest, ParseCNsReplyOK) { + DNSPacket pkt; + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("example.com", T_NS)) + .add_answer(new DNSNsRR("example.com", 100, "ns.example.com")); + std::vector data = pkt.data(); + + cares_ns_reply* ns = nullptr; + EXPECT_EQ(ARES_SUCCESS, cares_parse_ns_reply(data.data(), data.size(), &ns)); + ASSERT_NE(nullptr, ns); + EXPECT_EQ("ns.example.com", std::string(cares_ns_reply_get_host(ns))); + EXPECT_EQ(100, cares_ns_reply_get_ttl(ns)); + ares_free_data(ns); +} + TEST_F(LibraryTest, ParseNsReplyMultiple) { DNSPacket pkt; pkt.set_qid(10501).set_response().set_rd().set_ra() @@ -46,6 +61,38 @@ TEST_F(LibraryTest, ParseNsReplyMultiple) { ares_free_hostent(host); } +TEST_F(LibraryTest, ParseCNsReplyMultiple) { + DNSPacket pkt; + pkt.set_qid(10501).set_response().set_rd().set_ra() + .add_question(new DNSQuestion("google.com", T_NS)) + .add_answer(new DNSNsRR("google.com", 59, "ns1.google.com")) + .add_answer(new DNSNsRR("google.com", 59, "ns2.google.com")) + .add_answer(new DNSNsRR("google.com", 59, "ns3.google.com")) + .add_answer(new DNSNsRR("google.com", 59, "ns4.google.com")) + .add_additional(new DNSARR("ns4.google.com", 247, {216,239,38,10})) + .add_additional(new DNSARR("ns2.google.com", 247, {216,239,34,10})) + .add_additional(new DNSARR("ns1.google.com", 247, {216,239,32,10})) + .add_additional(new DNSARR("ns3.google.com", 247, {216,239,36,10})); + std::vector data = pkt.data(); + + cares_ns_reply* ns0 = nullptr; + EXPECT_EQ(ARES_SUCCESS, cares_parse_ns_reply(data.data(), data.size(), &ns0)); + ASSERT_NE(nullptr, ns0); + const cares_ns_reply* ns = ns0; + EXPECT_EQ("ns1.google.com", std::string(cares_ns_reply_get_host(ns))); + EXPECT_EQ(59, cares_ns_reply_get_ttl(ns)); + ns = cares_ns_reply_get_next(ns); + EXPECT_EQ("ns2.google.com", std::string(cares_ns_reply_get_host(ns))); + EXPECT_EQ(59, cares_ns_reply_get_ttl(ns)); + ns = cares_ns_reply_get_next(ns); + EXPECT_EQ("ns3.google.com", std::string(cares_ns_reply_get_host(ns))); + EXPECT_EQ(59, cares_ns_reply_get_ttl(ns)); + ns = cares_ns_reply_get_next(ns); + EXPECT_EQ("ns4.google.com", std::string(cares_ns_reply_get_host(ns))); + EXPECT_EQ(59, cares_ns_reply_get_ttl(ns)); + ares_free_data(ns0); +} + TEST_F(LibraryTest, ParseNsReplyErrors) { DNSPacket pkt; pkt.set_qid(0x1234).set_response().set_aa() @@ -98,6 +145,58 @@ TEST_F(LibraryTest, ParseNsReplyErrors) { } } +TEST_F(LibraryTest, ParseCNsReplyErrors) { + DNSPacket pkt; + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("example.com", T_NS)) + .add_answer(new DNSNsRR("example.com", 100, "ns.example.com")); + std::vector data; + cares_ns_reply* ns = nullptr; + + // No question. + pkt.questions_.clear(); + data = pkt.data(); + EXPECT_EQ(ARES_EBADRESP, cares_parse_ns_reply(data.data(), data.size(), &ns)); + pkt.add_question(new DNSQuestion("example.com", T_NS)); + +#ifdef DISABLED + // Question != answer + pkt.questions_.clear(); + pkt.add_question(new DNSQuestion("Axample.com", T_NS)); + data = pkt.data(); + EXPECT_EQ(ARES_ENODATA, cares_parse_ns_reply(data.data(), data.size(), &ns)); + pkt.questions_.clear(); + pkt.add_question(new DNSQuestion("example.com", T_NS)); +#endif + + // Two questions. + pkt.add_question(new DNSQuestion("example.com", T_NS)); + data = pkt.data(); + EXPECT_EQ(ARES_EBADRESP, cares_parse_ns_reply(data.data(), data.size(), &ns)); + pkt.questions_.clear(); + pkt.add_question(new DNSQuestion("example.com", T_NS)); + + // Wrong sort of answer. + pkt.answers_.clear(); + pkt.add_answer(new DNSMxRR("example.com", 100, 100, "mx1.example.com")); + data = pkt.data(); + EXPECT_EQ(ARES_ENODATA, cares_parse_ns_reply(data.data(), data.size(), &ns)); + pkt.answers_.clear(); + pkt.add_answer(new DNSNsRR("example.com", 100, "ns.example.com")); + + // No answer. + pkt.answers_.clear(); + data = pkt.data(); + EXPECT_EQ(ARES_ENODATA, cares_parse_ns_reply(data.data(), data.size(), &ns)); + pkt.add_answer(new DNSNsRR("example.com", 100, "ns.example.com")); + + // Truncated packets. + data = pkt.data(); + for (size_t len = 1; len < data.size(); len++) { + EXPECT_EQ(ARES_EBADRESP, cares_parse_ns_reply(data.data(), len, &ns)); + } +} + TEST_F(LibraryTest, ParseNsReplyAllocFail) { DNSPacket pkt; pkt.set_qid(0x1234).set_response().set_aa() @@ -114,6 +213,21 @@ TEST_F(LibraryTest, ParseNsReplyAllocFail) { } } +TEST_F(LibraryTest, ParseCNsReplyAllocFail) { + DNSPacket pkt; + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("example.com", T_NS)) + .add_answer(new DNSCnameRR("example.com", 300, "c.example.com")) + .add_answer(new DNSNsRR("c.example.com", 100, "ns.example.com")); + std::vector data = pkt.data(); + cares_ns_reply* ns = nullptr; + + for (int ii = 1; ii <= 5; ii++) { + ClearFails(); + SetAllocFail(ii); + EXPECT_EQ(ARES_ENOMEM, cares_parse_ns_reply(data.data(), data.size(), &ns)) << ii; + } +} } // namespace test } // namespace ares diff --git a/test/ares-test-parse-ptr.cc b/test/ares-test-parse-ptr.cc index 6fdaf3ea5d..afdcc5f3d5 100644 --- a/test/ares-test-parse-ptr.cc +++ b/test/ares-test-parse-ptr.cc @@ -25,6 +25,22 @@ TEST_F(LibraryTest, ParsePtrReplyOK) { ares_free_hostent(host); } +TEST_F(LibraryTest, ParseCPtrReplyOK) { + DNSPacket pkt; + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("64.48.32.16.in-addr.arpa", T_PTR)) + .add_answer(new DNSPtrRR("64.48.32.16.in-addr.arpa", 100, "other.com")); + std::vector data = pkt.data(); + + cares_ptr_reply *ptr = nullptr; + EXPECT_EQ(ARES_SUCCESS, cares_parse_ptr_reply(data.data(), data.size(), &ptr)); + ASSERT_NE(nullptr, ptr); + + EXPECT_EQ("other.com", std::string(cares_ptr_reply_get_host(ptr))); + EXPECT_EQ(100, cares_ptr_reply_get_ttl(ptr)); + ares_free_data(ptr); +} + TEST_F(LibraryTest, ParsePtrReplyCname) { byte addrv4[4] = {0x10, 0x20, 0x30, 0x40}; DNSPacket pkt; @@ -44,6 +60,23 @@ TEST_F(LibraryTest, ParsePtrReplyCname) { ares_free_hostent(host); } +TEST_F(LibraryTest, ParseCPtrReplyCname) { + DNSPacket pkt; + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("64.48.32.16.in-addr.arpa", T_PTR)) + .add_answer(new DNSCnameRR("64.48.32.16.in-addr.arpa", 50, "64.48.32.8.in-addr.arpa")) + .add_answer(new DNSPtrRR("64.48.32.8.in-addr.arpa", 100, "other.com")); + std::vector data = pkt.data(); + + cares_ptr_reply *ptr = nullptr; + EXPECT_EQ(ARES_SUCCESS, cares_parse_ptr_reply(data.data(), data.size(), &ptr)); + ASSERT_NE(nullptr, ptr); + + EXPECT_EQ("other.com", std::string(cares_ptr_reply_get_host(ptr))); + EXPECT_EQ(100, cares_ptr_reply_get_ttl(ptr)); + ares_free_data(ptr); +} + struct DNSMalformedCnameRR : public DNSCnameRR { DNSMalformedCnameRR(const std::string& name, int ttl, const std::string& other) @@ -74,6 +107,19 @@ TEST_F(LibraryTest, ParsePtrReplyMalformedCname) { ASSERT_EQ(nullptr, host); } +TEST_F(LibraryTest, ParseCPtrReplyMalformedCname) { + DNSPacket pkt; + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("64.48.32.16.in-addr.arpa", T_PTR)) + .add_answer(new DNSMalformedCnameRR("64.48.32.16.in-addr.arpa", 50, "64.48.32.8.in-addr.arpa")) + .add_answer(new DNSPtrRR("64.48.32.8.in-addr.arpa", 100, "other.com")); + std::vector data = pkt.data(); + + cares_ptr_reply *ptr = nullptr; + EXPECT_EQ(ARES_EBADRESP, cares_parse_ptr_reply(data.data(), data.size(), &ptr)); + ASSERT_EQ(nullptr, ptr); +} + TEST_F(LibraryTest, ParseManyPtrReply) { byte addrv4[4] = {0x10, 0x20, 0x30, 0x40}; DNSPacket pkt; @@ -98,6 +144,28 @@ TEST_F(LibraryTest, ParseManyPtrReply) { ares_free_hostent(host); } +TEST_F(LibraryTest, ParseManyCPtrReply) { + DNSPacket pkt; + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("64.48.32.16.in-addr.arpa", T_PTR)) + .add_answer(new DNSPtrRR("64.48.32.16.in-addr.arpa", 100, "main.com")) + .add_answer(new DNSPtrRR("64.48.32.16.in-addr.arpa", 100, "other1.com")) + .add_answer(new DNSPtrRR("64.48.32.16.in-addr.arpa", 100, "other2.com")) + .add_answer(new DNSPtrRR("64.48.32.16.in-addr.arpa", 100, "other3.com")) + .add_answer(new DNSPtrRR("64.48.32.16.in-addr.arpa", 100, "other4.com")) + .add_answer(new DNSPtrRR("64.48.32.16.in-addr.arpa", 100, "other5.com")) + .add_answer(new DNSPtrRR("64.48.32.16.in-addr.arpa", 100, "other6.com")) + .add_answer(new DNSPtrRR("64.48.32.16.in-addr.arpa", 100, "other7.com")) + .add_answer(new DNSPtrRR("64.48.32.16.in-addr.arpa", 100, "other8.com")) + .add_answer(new DNSPtrRR("64.48.32.16.in-addr.arpa", 100, "other9.com")); + std::vector data = pkt.data(); + + cares_ptr_reply *ptr = nullptr; + EXPECT_EQ(ARES_SUCCESS, cares_parse_ptr_reply(data.data(), data.size(), &ptr)); + ASSERT_NE(nullptr, ptr); + ares_free_data(ptr); +} + TEST_F(LibraryTest, ParsePtrReplyAdditional) { byte addrv4[4] = {0x10, 0x20, 0x30, 0x40}; DNSPacket pkt; @@ -122,6 +190,27 @@ TEST_F(LibraryTest, ParsePtrReplyAdditional) { ares_free_hostent(host); } +TEST_F(LibraryTest, ParseCPtrReplyAdditional) { + DNSPacket pkt; + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("64.48.32.16.in-addr.arpa", T_PTR)) + .add_answer(new DNSPtrRR("64.48.32.16.in-addr.arpa", 55, "other.com")) + .add_auth(new DNSNsRR("16.in-addr.arpa", 234, "ns1.other.com")) + .add_auth(new DNSNsRR("16.in-addr.arpa", 234, "bb.ns2.other.com")) + .add_auth(new DNSNsRR("16.in-addr.arpa", 234, "ns3.other.com")) + .add_additional(new DNSARR("ns1.other.com", 229, {10,20,30,41})) + .add_additional(new DNSARR("bb.ns2.other.com", 229, {10,20,30,42})) + .add_additional(new DNSARR("ns3.other.com", 229, {10,20,30,43})); + std::vector data = pkt.data(); + + cares_ptr_reply *ptr = nullptr; + EXPECT_EQ(ARES_SUCCESS, cares_parse_ptr_reply(data.data(), data.size(), &ptr)); + ASSERT_NE(nullptr, ptr); + EXPECT_EQ("other.com", std::string(cares_ptr_reply_get_host(ptr))); + EXPECT_EQ(55, cares_ptr_reply_get_ttl(ptr)); + ares_free_data(ptr); +} + TEST_F(LibraryTest, ParsePtrReplyErrors) { byte addrv4[4] = {0x10, 0x20, 0x30, 0x40}; DNSPacket pkt; @@ -193,6 +282,69 @@ TEST_F(LibraryTest, ParsePtrReplyErrors) { } } +TEST_F(LibraryTest, ParseCPtrReplyErrors) { + DNSPacket pkt; + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("64.48.32.16.in-addr.arpa", T_PTR)) + .add_answer(new DNSPtrRR("64.48.32.16.in-addr.arpa", 100, "other.com")); + std::vector data; + cares_ptr_reply *ptr = nullptr; + + // No question. + pkt.questions_.clear(); + data = pkt.data(); + EXPECT_EQ(ARES_EBADRESP, cares_parse_ptr_reply(data.data(), data.size(), &ptr)); + pkt.add_question(new DNSQuestion("64.48.32.16.in-addr.arpa", T_PTR)); + + // Question != answer + pkt.questions_.clear(); + pkt.add_question(new DNSQuestion("99.48.32.16.in-addr.arpa", T_PTR)); + data = pkt.data(); + EXPECT_EQ(ARES_ENODATA, cares_parse_ptr_reply(data.data(), data.size(), &ptr)); + EXPECT_EQ(nullptr, ptr); + pkt.questions_.clear(); + pkt.add_question(new DNSQuestion("64.48.32.16.in-addr.arpa", T_PTR)); + + // Two questions. + pkt.add_question(new DNSQuestion("64.48.32.16.in-addr.arpa", T_PTR)); + data = pkt.data(); + EXPECT_EQ(ARES_EBADRESP, cares_parse_ptr_reply(data.data(), data.size(), &ptr)); + EXPECT_EQ(nullptr, ptr); + pkt.questions_.clear(); + pkt.add_question(new DNSQuestion("64.48.32.16.in-addr.arpa", T_PTR)); + + // Wrong sort of answer. + pkt.answers_.clear(); + pkt.add_answer(new DNSMxRR("example.com", 100, 100, "mx1.example.com")); + data = pkt.data(); + EXPECT_EQ(ARES_ENODATA, cares_parse_ptr_reply(data.data(), data.size(), &ptr)); + EXPECT_EQ(nullptr, ptr); + pkt.answers_.clear(); + pkt.add_answer(new DNSPtrRR("64.48.32.16.in-addr.arpa", 100, "other.com")); + + // No answer. + pkt.answers_.clear(); + data = pkt.data(); + EXPECT_EQ(ARES_ENODATA, cares_parse_ptr_reply(data.data(), data.size(), &ptr)); + EXPECT_EQ(nullptr, ptr); + pkt.add_answer(new DNSPtrRR("64.48.32.16.in-addr.arpa", 100, "other.com")); + + // Truncated packets. + data = pkt.data(); + for (size_t len = 1; len < data.size(); len++) { + EXPECT_EQ(ARES_EBADRESP, cares_parse_ptr_reply(data.data(), len, &ptr)); + EXPECT_EQ(nullptr, ptr); + } + + // Truncated packets with CNAME. + pkt.add_answer(new DNSCnameRR("64.48.32.16.in-addr.arpa", 50, "64.48.32.8.in-addr.arpa")); + data = pkt.data(); + for (size_t len = 1; len < data.size(); len++) { + EXPECT_EQ(ARES_EBADRESP, cares_parse_ptr_reply(data.data(), len, &ptr)); + EXPECT_EQ(nullptr, ptr); + } +} + TEST_F(LibraryTest, ParsePtrReplyAllocFailSome) { byte addrv4[4] = {0x10, 0x20, 0x30, 0x40}; DNSPacket pkt; @@ -213,6 +365,24 @@ TEST_F(LibraryTest, ParsePtrReplyAllocFailSome) { } } +TEST_F(LibraryTest, ParseCPtrReplyAllocFailSome) { + DNSPacket pkt; + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("64.48.32.16.in-addr.arpa", T_PTR)) + .add_answer(new DNSPtrRR("64.48.32.16.in-addr.arpa", 100, "main.com")) + .add_answer(new DNSPtrRR("64.48.32.16.in-addr.arpa", 100, "other1.com")) + .add_answer(new DNSPtrRR("64.48.32.16.in-addr.arpa", 100, "other2.com")) + .add_answer(new DNSPtrRR("64.48.32.16.in-addr.arpa", 100, "other3.com")); + std::vector data = pkt.data(); + cares_ptr_reply *ptr = nullptr; + + for (int ii = 1; ii <= 13; ii++) { + ClearFails(); + SetAllocFail(ii); + EXPECT_EQ(ARES_ENOMEM, cares_parse_ptr_reply(data.data(), data.size(), &ptr)) << ii; + } +} + TEST_F(LibraryTest, ParsePtrReplyAllocFailMany) { byte addrv4[4] = {0x10, 0x20, 0x30, 0x40}; DNSPacket pkt; @@ -244,6 +414,35 @@ TEST_F(LibraryTest, ParsePtrReplyAllocFailMany) { } } +TEST_F(LibraryTest, ParseCPtrReplyAllocFailMany) { + DNSPacket pkt; + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("64.48.32.16.in-addr.arpa", T_PTR)) + .add_answer(new DNSPtrRR("64.48.32.16.in-addr.arpa", 100, "main.com")) + .add_answer(new DNSPtrRR("64.48.32.16.in-addr.arpa", 100, "other1.com")) + .add_answer(new DNSPtrRR("64.48.32.16.in-addr.arpa", 100, "other2.com")) + .add_answer(new DNSPtrRR("64.48.32.16.in-addr.arpa", 100, "other3.com")) + .add_answer(new DNSPtrRR("64.48.32.16.in-addr.arpa", 100, "other4.com")) + .add_answer(new DNSPtrRR("64.48.32.16.in-addr.arpa", 100, "other5.com")) + .add_answer(new DNSPtrRR("64.48.32.16.in-addr.arpa", 100, "other6.com")) + .add_answer(new DNSPtrRR("64.48.32.16.in-addr.arpa", 100, "other7.com")) + .add_answer(new DNSPtrRR("64.48.32.16.in-addr.arpa", 100, "other8.com")) + .add_answer(new DNSPtrRR("64.48.32.16.in-addr.arpa", 100, "other9.com")); + std::vector data = pkt.data(); + cares_ptr_reply *ptr = nullptr; + + for (int ii = 1; ii <= 63; ii++) { + ClearFails(); + SetAllocFail(ii); + int rc = cares_parse_ptr_reply(data.data(), data.size(), &ptr); + if (rc != ARES_ENOMEM) { + EXPECT_EQ(ARES_SUCCESS, rc); + ares_free_data(ptr); + ptr = nullptr; + } + } +} + } // namespace test } // namespace ares diff --git a/test/ares-test-parse-soa-any.cc b/test/ares-test-parse-soa-any.cc index 700073c5c8..14b5f569a5 100644 --- a/test/ares-test-parse-soa-any.cc +++ b/test/ares-test-parse-soa-any.cc @@ -32,6 +32,32 @@ TEST_F(LibraryTest, ParseSoaAnyReplyOK) { ares_free_data(soa); } +TEST_F(LibraryTest, ParseCSoaAnyReplyOK) { + DNSPacket pkt; + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("example.com", T_ANY))\ + .add_answer(new DNSARR("example.com", 0x01020304, {2,3,4,5})) + .add_answer(new DNSMxRR("example.com", 100, 100, "mx1.example.com")) + .add_answer(new DNSMxRR("example.com", 100, 200, "mx2.example.com")) + .add_answer(new DNSSoaRR("example.com", 100, + "soa1.example.com", "fred.example.com", + 1, 2, 3, 4, 5)); + std::vector data = pkt.data(); + + cares_soa_reply* soa = nullptr; + EXPECT_EQ(ARES_SUCCESS, cares_parse_soa_reply(data.data(), data.size(), &soa)); + ASSERT_NE(nullptr, soa); + EXPECT_EQ("soa1.example.com", std::string(cares_soa_reply_get_nsname(soa))); + EXPECT_EQ("fred.example.com", std::string(cares_soa_reply_get_hostmaster(soa))); + EXPECT_EQ(1, cares_soa_reply_get_serial(soa)); + EXPECT_EQ(2, cares_soa_reply_get_refresh(soa)); + EXPECT_EQ(3, cares_soa_reply_get_retry(soa)); + EXPECT_EQ(4, cares_soa_reply_get_expire(soa)); + EXPECT_EQ(5, cares_soa_reply_get_minttl(soa)); + EXPECT_EQ(100, cares_soa_reply_get_ttl(soa)); + ares_free_data(soa); +} + TEST_F(LibraryTest, ParseSoaAnyReplyErrors) { DNSPacket pkt; pkt.set_qid(0x1234).set_response().set_aa() @@ -90,6 +116,64 @@ TEST_F(LibraryTest, ParseSoaAnyReplyErrors) { } } +TEST_F(LibraryTest, ParseCSoaAnyReplyErrors) { + DNSPacket pkt; + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("example.com", T_ANY)) + .add_answer(new DNSSoaRR("example.com", 100, + "soa1.example.com", "fred.example.com", + 1, 2, 3, 4, 5)); + std::vector data; + cares_soa_reply* soa = nullptr; + + // No question. + pkt.questions_.clear(); + data = pkt.data(); + EXPECT_EQ(ARES_EBADRESP, cares_parse_soa_reply(data.data(), data.size(), &soa)); + pkt.add_question(new DNSQuestion("example.com", T_ANY)); + +#ifdef DISABLED + // Question != answer + pkt.questions_.clear(); + pkt.add_question(new DNSQuestion("Axample.com", T_ANY)); + data = pkt.data(); + EXPECT_EQ(ARES_EBADRESP, cares_parse_soa_reply(data.data(), data.size(), &soa)); + pkt.questions_.clear(); + pkt.add_question(new DNSQuestion("example.com", T_ANY)); +#endif + + // Two questions + pkt.add_question(new DNSQuestion("example.com", T_ANY)); + data = pkt.data(); + EXPECT_EQ(ARES_EBADRESP, cares_parse_soa_reply(data.data(), data.size(), &soa)); + pkt.questions_.clear(); + pkt.add_question(new DNSQuestion("example.com", T_ANY)); + + // Wrong sort of answer. + pkt.answers_.clear(); + pkt.add_answer(new DNSMxRR("example.com", 100, 100, "mx1.example.com")); + data = pkt.data(); + EXPECT_EQ(ARES_EBADRESP, cares_parse_soa_reply(data.data(), data.size(), &soa)); + pkt.answers_.clear(); + pkt.add_answer(new DNSSoaRR("example.com", 100, + "soa1.example.com", "fred.example.com", + 1, 2, 3, 4, 5)); + + // No answer. + pkt.answers_.clear(); + data = pkt.data(); + EXPECT_EQ(ARES_EBADRESP, cares_parse_soa_reply(data.data(), data.size(), &soa)); + pkt.add_answer(new DNSSoaRR("example.com", 100, + "soa1.example.com", "fred.example.com", + 1, 2, 3, 4, 5)); + + // Truncated packets. + data = pkt.data(); + for (size_t len = 1; len < data.size(); len++) { + EXPECT_EQ(ARES_EBADRESP, cares_parse_soa_reply(data.data(), len, &soa)); + } +} + TEST_F(LibraryTest, ParseSoaAnyReplyAllocFail) { DNSPacket pkt; pkt.set_qid(0x1234).set_response().set_aa() @@ -107,5 +191,22 @@ TEST_F(LibraryTest, ParseSoaAnyReplyAllocFail) { } } +TEST_F(LibraryTest, ParseCSoaAnyReplyAllocFail) { + DNSPacket pkt; + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("example.com", T_ANY)) + .add_answer(new DNSSoaRR("example.com", 100, + "soa1.example.com", "fred.example.com", + 1, 2, 3, 4, 5)); + std::vector data = pkt.data(); + cares_soa_reply* soa = nullptr; + + for (int ii = 1; ii <= 5; ii++) { + ClearFails(); + SetAllocFail(ii); + EXPECT_EQ(ARES_ENOMEM, cares_parse_soa_reply(data.data(), data.size(), &soa)) << ii; + } +} + } // namespace test } // namespace ares diff --git a/test/ares-test-parse-soa.cc b/test/ares-test-parse-soa.cc index 22a78e5f46..00a947bf52 100644 --- a/test/ares-test-parse-soa.cc +++ b/test/ares-test-parse-soa.cc @@ -29,6 +29,29 @@ TEST_F(LibraryTest, ParseSoaReplyOK) { ares_free_data(soa); } +TEST_F(LibraryTest, ParseCSoaReplyOK) { + DNSPacket pkt; + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("example.com", T_SOA)) + .add_answer(new DNSSoaRR("example.com", 100, + "soa1.example.com", "fred.example.com", + 1, 2, 3, 4, 5)); + std::vector data = pkt.data(); + + cares_soa_reply* soa = nullptr; + EXPECT_EQ(ARES_SUCCESS, cares_parse_soa_reply(data.data(), data.size(), &soa)); + ASSERT_NE(nullptr, soa); + EXPECT_EQ("soa1.example.com", std::string(cares_soa_reply_get_nsname(soa))); + EXPECT_EQ("fred.example.com", std::string(cares_soa_reply_get_hostmaster(soa))); + EXPECT_EQ(1, cares_soa_reply_get_serial(soa)); + EXPECT_EQ(2, cares_soa_reply_get_refresh(soa)); + EXPECT_EQ(3, cares_soa_reply_get_retry(soa)); + EXPECT_EQ(4, cares_soa_reply_get_expire(soa)); + EXPECT_EQ(5, cares_soa_reply_get_minttl(soa)); + EXPECT_EQ(100, cares_soa_reply_get_ttl(soa)); + ares_free_data(soa); +} + TEST_F(LibraryTest, ParseSoaReplyErrors) { DNSPacket pkt; pkt.set_qid(0x1234).set_response().set_aa() @@ -87,6 +110,64 @@ TEST_F(LibraryTest, ParseSoaReplyErrors) { } } +TEST_F(LibraryTest, ParseCSoaReplyErrors) { + DNSPacket pkt; + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("example.com", T_SOA)) + .add_answer(new DNSSoaRR("example.com", 100, + "soa1.example.com", "fred.example.com", + 1, 2, 3, 4, 5)); + std::vector data; + cares_soa_reply* soa = nullptr; + + // No question. + pkt.questions_.clear(); + data = pkt.data(); + EXPECT_EQ(ARES_EBADRESP, cares_parse_soa_reply(data.data(), data.size(), &soa)); + pkt.add_question(new DNSQuestion("example.com", T_SOA)); + +#ifdef DISABLED + // Question != answer + pkt.questions_.clear(); + pkt.add_question(new DNSQuestion("Axample.com", T_SOA)); + data = pkt.data(); + EXPECT_EQ(ARES_EBADRESP, cares_parse_soa_reply(data.data(), data.size(), &soa)); + pkt.questions_.clear(); + pkt.add_question(new DNSQuestion("example.com", T_SOA)); +#endif + + // Two questions + pkt.add_question(new DNSQuestion("example.com", T_SOA)); + data = pkt.data(); + EXPECT_EQ(ARES_EBADRESP, cares_parse_soa_reply(data.data(), data.size(), &soa)); + pkt.questions_.clear(); + pkt.add_question(new DNSQuestion("example.com", T_SOA)); + + // Wrong sort of answer. + pkt.answers_.clear(); + pkt.add_answer(new DNSMxRR("example.com", 100, 100, "mx1.example.com")); + data = pkt.data(); + EXPECT_EQ(ARES_EBADRESP, cares_parse_soa_reply(data.data(), data.size(), &soa)); + pkt.answers_.clear(); + pkt.add_answer(new DNSSoaRR("example.com", 100, + "soa1.example.com", "fred.example.com", + 1, 2, 3, 4, 5)); + + // No answer. + pkt.answers_.clear(); + data = pkt.data(); + EXPECT_EQ(ARES_EBADRESP, cares_parse_soa_reply(data.data(), data.size(), &soa)); + pkt.add_answer(new DNSSoaRR("example.com", 100, + "soa1.example.com", "fred.example.com", + 1, 2, 3, 4, 5)); + + // Truncated packets. + data = pkt.data(); + for (size_t len = 1; len < data.size(); len++) { + EXPECT_EQ(ARES_EBADRESP, cares_parse_soa_reply(data.data(), len, &soa)); + } +} + TEST_F(LibraryTest, ParseSoaReplyAllocFail) { DNSPacket pkt; pkt.set_qid(0x1234).set_response().set_aa() @@ -104,5 +185,22 @@ TEST_F(LibraryTest, ParseSoaReplyAllocFail) { } } +TEST_F(LibraryTest, ParseCSoaReplyAllocFail) { + DNSPacket pkt; + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("example.com", T_SOA)) + .add_answer(new DNSSoaRR("example.com", 100, + "soa1.example.com", "fred.example.com", + 1, 2, 3, 4, 5)); + std::vector data = pkt.data(); + cares_soa_reply* soa = nullptr; + + for (int ii = 1; ii <= 5; ii++) { + ClearFails(); + SetAllocFail(ii); + EXPECT_EQ(ARES_ENOMEM, cares_parse_soa_reply(data.data(), data.size(), &soa)) << ii; + } +} + } // namespace test } // namespace ares diff --git a/test/ares-test-parse-srv.cc b/test/ares-test-parse-srv.cc index b004759801..aafa5423e9 100644 --- a/test/ares-test-parse-srv.cc +++ b/test/ares-test-parse-srv.cc @@ -35,6 +35,35 @@ TEST_F(LibraryTest, ParseSrvReplyOK) { ares_free_data(srv); } +// TEST_F(LibraryTest, ParseCSrvReplyOK) { +// DNSPacket pkt; +// pkt.set_qid(0x1234).set_response().set_aa() +// .add_question(new DNSQuestion("example.com", T_SRV)) +// .add_answer(new DNSSrvRR("example.com", 100, 10, 20, 30, "srv.example.com")) +// .add_answer(new DNSSrvRR("example.com", 100, 11, 21, 31, "srv2.example.com")); +// std::vector data = pkt.data(); + +// cares_srv_reply* srv = nullptr; +// EXPECT_EQ(ARES_SUCCESS, cares_parse_srv_reply(data.data(), data.size(), &srv)); +// ASSERT_NE(nullptr, srv); + +// EXPECT_EQ("srv.example.com", std::string(cares_srv_reply_get_host(srv))); +// EXPECT_EQ(10, cares_srv_reply_get_priority(srv)); +// EXPECT_EQ(20, cares_srv_reply_get_weight(srv)); +// EXPECT_EQ(30, cares_srv_reply_get_port(srv)); +// EXPECT_EQ(100, cares_srv_reply_get_ttl(srv)); + +// const cares_srv_reply* srv2 = cares_srv_reply_get_next(srv); +// ASSERT_NE(nullptr, srv2); +// EXPECT_EQ("srv2.example.com", std::string(cares_srv_reply_get_host(srv2))); +// EXPECT_EQ(11, cares_srv_reply_get_priority(srv2)); +// EXPECT_EQ(21, cares_srv_reply_get_weight(srv2)); +// EXPECT_EQ(31, cares_srv_reply_get_port(srv2)); +// EXPECT_EQ(100, cares_srv_reply_get_ttl(srv2)); +// EXPECT_EQ(nullptr, cares_srv_reply_get_next(srv2)); +// ares_free_data(srv); +// } + TEST_F(LibraryTest, ParseSrvReplySingle) { DNSPacket pkt; pkt.set_qid(0x1234).set_response().set_aa() @@ -62,6 +91,34 @@ TEST_F(LibraryTest, ParseSrvReplySingle) { ares_free_data(srv); } +// TEST_F(LibraryTest, ParseCSrvReplySingle) { +// DNSPacket pkt; +// pkt.set_qid(0x1234).set_response().set_aa() +// .add_question(new DNSQuestion("example.abc.def.com", T_SRV)) +// .add_answer(new DNSSrvRR("example.abc.def.com", 180, 0, 10, 8160, "example.abc.def.com")) +// .add_auth(new DNSNsRR("abc.def.com", 44, "else1.where.com")) +// .add_auth(new DNSNsRR("abc.def.com", 44, "else2.where.com")) +// .add_auth(new DNSNsRR("abc.def.com", 44, "else3.where.com")) +// .add_auth(new DNSNsRR("abc.def.com", 44, "else4.where.com")) +// .add_auth(new DNSNsRR("abc.def.com", 44, "else5.where.com")) +// .add_additional(new DNSARR("else2.where.com", 42, {172,19,0,1})) +// .add_additional(new DNSARR("else5.where.com", 42, {172,19,0,2})); +// std::vector data = pkt.data(); + +// cares_srv_reply* srv = nullptr; +// EXPECT_EQ(ARES_SUCCESS, cares_parse_srv_reply(data.data(), data.size(), &srv)); +// ASSERT_NE(nullptr, srv); + +// EXPECT_EQ("example.abc.def.com", std::string(cares_srv_reply_get_host(srv))); +// EXPECT_EQ(0, cares_srv_reply_get_priority(srv)); +// EXPECT_EQ(10, cares_srv_reply_get_weight(srv)); +// EXPECT_EQ(8160, cares_srv_reply_get_port(srv)); +// EXPECT_EQ(180, cares_srv_reply_get_ttl(srv)); +// EXPECT_EQ(nullptr, cares_srv_reply_get_next(srv)); + +// ares_free_data(srv); +// } + TEST_F(LibraryTest, ParseSrvReplyMalformed) { std::vector data = { 0x12, 0x34, // qid @@ -93,6 +150,37 @@ TEST_F(LibraryTest, ParseSrvReplyMalformed) { ASSERT_EQ(nullptr, srv); } +// TEST_F(LibraryTest, ParseCSrvReplyMalformed) { +// std::vector data = { +// 0x12, 0x34, // qid +// 0x84, // response + query + AA + not-TC + not-RD +// 0x00, // not-RA + not-Z + not-AD + not-CD + rc=NoError +// 0x00, 0x01, // num questions +// 0x00, 0x01, // num answer RRs +// 0x00, 0x00, // num authority RRs +// 0x00, 0x00, // num additional RRs +// // Question +// 0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e', +// 0x03, 'c', 'o', 'm', +// 0x00, +// 0x00, 0x21, // type SRV +// 0x00, 0x01, // class IN +// // Answer 1 +// 0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e', +// 0x03, 'c', 'o', 'm', +// 0x00, +// 0x00, 0x21, // RR type +// 0x00, 0x01, // class IN +// 0x01, 0x02, 0x03, 0x04, // TTL +// 0x00, 0x04, // rdata length -- too short +// 0x02, 0x03, 0x04, 0x05, +// }; + +// cares_srv_reply* srv = nullptr; +// EXPECT_EQ(ARES_EBADRESP, cares_parse_srv_reply(data.data(), data.size(), &srv)); +// ASSERT_EQ(nullptr, srv); +// } + TEST_F(LibraryTest, ParseSrvReplyMultiple) { DNSPacket pkt; pkt.set_qid(0x1234).set_response().set_ra().set_rd() @@ -139,6 +227,55 @@ TEST_F(LibraryTest, ParseSrvReplyMultiple) { ares_free_data(srv0); } +// TEST_F(LibraryTest, ParseCSrvReplyMultiple) { +// DNSPacket pkt; +// pkt.set_qid(0x1234).set_response().set_ra().set_rd() +// .add_question(new DNSQuestion("srv.example.com", T_SRV)) +// .add_answer(new DNSSrvRR("srv.example.com", 300, 0, 5, 6789, "a1.srv.example.com")) +// .add_answer(new DNSSrvRR("srv.example.com", 300, 0, 5, 4567, "a2.srv.example.com")) +// .add_answer(new DNSSrvRR("srv.example.com", 300, 0, 5, 5678, "a3.srv.example.com")) +// .add_auth(new DNSNsRR("example.com", 300, "ns1.example.com")) +// .add_auth(new DNSNsRR("example.com", 300, "ns2.example.com")) +// .add_auth(new DNSNsRR("example.com", 300, "ns3.example.com")) +// .add_additional(new DNSARR("a1.srv.example.com", 300, {172,19,1,1})) +// .add_additional(new DNSARR("a2.srv.example.com", 300, {172,19,1,2})) +// .add_additional(new DNSARR("a3.srv.example.com", 300, {172,19,1,3})) +// .add_additional(new DNSARR("n1.example.com", 300, {172,19,0,1})) +// .add_additional(new DNSARR("n2.example.com", 300, {172,19,0,2})) +// .add_additional(new DNSARR("n3.example.com", 300, {172,19,0,3})); +// std::vector data = pkt.data(); + +// cares_srv_reply* srv0 = nullptr; +// EXPECT_EQ(ARES_SUCCESS, cares_parse_srv_reply(data.data(), data.size(), &srv0)); +// ASSERT_NE(nullptr, srv0); +// const cares_srv_reply* srv = srv0; + +// EXPECT_EQ("a1.srv.example.com", std::string(cares_srv_reply_get_host(srv))); +// EXPECT_EQ(0, cares_srv_reply_get_priority(srv)); +// EXPECT_EQ(5, cares_srv_reply_get_weight(srv)); +// EXPECT_EQ(6789, cares_srv_reply_get_port(srv)); +// EXPECT_EQ(300, cares_srv_reply_get_ttl(srv)); +// EXPECT_NE(nullptr, cares_srv_reply_get_next(srv)); +// srv = cares_srv_reply_get_next(srv); + +// EXPECT_EQ("a2.srv.example.com", std::string(cares_srv_reply_get_host(srv))); +// EXPECT_EQ(0, cares_srv_reply_get_priority(srv)); +// EXPECT_EQ(5, cares_srv_reply_get_weight(srv)); +// EXPECT_EQ(4567, cares_srv_reply_get_port(srv)); +// EXPECT_EQ(300, cares_srv_reply_get_ttl(srv)); +// EXPECT_NE(nullptr, cares_srv_reply_get_next(srv)); +// srv = cares_srv_reply_get_next(srv); + +// EXPECT_EQ("a3.srv.example.com", std::string(cares_srv_reply_get_host(srv))); +// EXPECT_EQ(0, cares_srv_reply_get_priority(srv)); +// EXPECT_EQ(5, cares_srv_reply_get_weight(srv)); +// EXPECT_EQ(5678, cares_srv_reply_get_port(srv)); +// EXPECT_EQ(300, cares_srv_reply_get_ttl(srv)); +// EXPECT_EQ(nullptr, cares_srv_reply_get_next(srv)); + +// ares_free_data(srv0); +// } + TEST_F(LibraryTest, ParseSrvReplyCname) { DNSPacket pkt; pkt.set_qid(0x1234).set_response().set_aa() @@ -167,6 +304,35 @@ TEST_F(LibraryTest, ParseSrvReplyCname) { ares_free_data(srv); } +// TEST_F(LibraryTest, ParseCSrvReplyCname) { +// DNSPacket pkt; +// pkt.set_qid(0x1234).set_response().set_aa() +// .add_question(new DNSQuestion("example.abc.def.com", T_SRV)) +// .add_answer(new DNSCnameRR("example.abc.def.com", 300, "cname.abc.def.com")) +// .add_answer(new DNSSrvRR("cname.abc.def.com", 300, 0, 10, 1234, "srv.abc.def.com")) +// .add_auth(new DNSNsRR("abc.def.com", 44, "else1.where.com")) +// .add_auth(new DNSNsRR("abc.def.com", 44, "else2.where.com")) +// .add_auth(new DNSNsRR("abc.def.com", 44, "else3.where.com")) +// .add_additional(new DNSARR("example.abc.def.com", 300, {172,19,0,1})) +// .add_additional(new DNSARR("else1.where.com", 42, {172,19,0,1})) +// .add_additional(new DNSARR("else2.where.com", 42, {172,19,0,2})) +// .add_additional(new DNSARR("else3.where.com", 42, {172,19,0,3})); +// std::vector data = pkt.data(); + +// cares_srv_reply* srv = nullptr; +// EXPECT_EQ(ARES_SUCCESS, cares_parse_srv_reply(data.data(), data.size(), &srv)); +// ASSERT_NE(nullptr, srv); + +// EXPECT_EQ("srv.abc.def.com", std::string(cares_srv_reply_get_host(srv))); +// EXPECT_EQ(0, cares_srv_reply_get_priority(srv)); +// EXPECT_EQ(10, cares_srv_reply_get_weight(srv)); +// EXPECT_EQ(1234, cares_srv_reply_get_port(srv)); +// EXPECT_EQ(300, cares_srv_reply_get_ttl(srv)); +// EXPECT_EQ(nullptr, cares_srv_reply_get_next(srv)); + +// ares_free_data(srv); +// } + TEST_F(LibraryTest, ParseSrvReplyCnameMultiple) { DNSPacket pkt; pkt.set_qid(0x1234).set_response().set_ra().set_rd() @@ -214,6 +380,56 @@ TEST_F(LibraryTest, ParseSrvReplyCnameMultiple) { ares_free_data(srv0); } +// TEST_F(LibraryTest, ParseCSrvReplyCnameMultiple) { +// DNSPacket pkt; +// pkt.set_qid(0x1234).set_response().set_ra().set_rd() +// .add_question(new DNSQuestion("query.example.com", T_SRV)) +// .add_answer(new DNSCnameRR("query.example.com", 300, "srv.example.com")) +// .add_answer(new DNSSrvRR("srv.example.com", 300, 0, 5, 6789, "a1.srv.example.com")) +// .add_answer(new DNSSrvRR("srv.example.com", 300, 0, 5, 4567, "a2.srv.example.com")) +// .add_answer(new DNSSrvRR("srv.example.com", 300, 0, 5, 5678, "a3.srv.example.com")) +// .add_auth(new DNSNsRR("example.com", 300, "ns1.example.com")) +// .add_auth(new DNSNsRR("example.com", 300, "ns2.example.com")) +// .add_auth(new DNSNsRR("example.com", 300, "ns3.example.com")) +// .add_additional(new DNSARR("a1.srv.example.com", 300, {172,19,1,1})) +// .add_additional(new DNSARR("a2.srv.example.com", 300, {172,19,1,2})) +// .add_additional(new DNSARR("a3.srv.example.com", 300, {172,19,1,3})) +// .add_additional(new DNSARR("n1.example.com", 300, {172,19,0,1})) +// .add_additional(new DNSARR("n2.example.com", 300, {172,19,0,2})) +// .add_additional(new DNSARR("n3.example.com", 300, {172,19,0,3})); +// std::vector data = pkt.data(); + +// cares_srv_reply* srv0 = nullptr; +// EXPECT_EQ(ARES_SUCCESS, cares_parse_srv_reply(data.data(), data.size(), &srv0)); +// ASSERT_NE(nullptr, srv0); +// const cares_srv_reply* srv = srv0; + +// EXPECT_EQ("a1.srv.example.com", std::string(cares_srv_reply_get_host(srv))); +// EXPECT_EQ(0, cares_srv_reply_get_priority(srv)); +// EXPECT_EQ(5, cares_srv_reply_get_weight(srv)); +// EXPECT_EQ(6789, cares_srv_reply_get_port(srv)); +// EXPECT_EQ(300, cares_srv_reply_get_ttl(srv)); +// EXPECT_NE(nullptr, cares_srv_reply_get_next(srv)); +// srv = cares_srv_reply_get_next(srv); + +// EXPECT_EQ("a2.srv.example.com", std::string(cares_srv_reply_get_host(srv))); +// EXPECT_EQ(0, cares_srv_reply_get_priority(srv)); +// EXPECT_EQ(5, cares_srv_reply_get_weight(srv)); +// EXPECT_EQ(4567, cares_srv_reply_get_port(srv)); +// EXPECT_EQ(300, cares_srv_reply_get_ttl(srv)); +// EXPECT_NE(nullptr, cares_srv_reply_get_next(srv)); +// srv = cares_srv_reply_get_next(srv); + +// EXPECT_EQ("a3.srv.example.com", std::string(cares_srv_reply_get_host(srv))); +// EXPECT_EQ(0, cares_srv_reply_get_priority(srv)); +// EXPECT_EQ(5, cares_srv_reply_get_weight(srv)); +// EXPECT_EQ(5678, cares_srv_reply_get_port(srv)); +// EXPECT_EQ(300, cares_srv_reply_get_ttl(srv)); +// EXPECT_EQ(nullptr, cares_srv_reply_get_next(srv)); + +// ares_free_data(srv0); +// } + TEST_F(LibraryTest, ParseSrvReplyErrors) { DNSPacket pkt; pkt.set_qid(0x1234).set_response().set_aa() @@ -227,7 +443,6 @@ TEST_F(LibraryTest, ParseSrvReplyErrors) { data = pkt.data(); EXPECT_EQ(ARES_EBADRESP, ares_parse_srv_reply(data.data(), data.size(), &srv)); pkt.add_question(new DNSQuestion("example.abc.def.com", T_SRV)); - #ifdef DISABLED // Question != answer pkt.questions_.clear(); @@ -237,29 +452,25 @@ TEST_F(LibraryTest, ParseSrvReplyErrors) { pkt.questions_.clear(); pkt.add_question(new DNSQuestion("example.com", T_SRV)); #endif - // Two questions. pkt.add_question(new DNSQuestion("example.abc.def.com", T_SRV)); data = pkt.data(); EXPECT_EQ(ARES_EBADRESP, ares_parse_srv_reply(data.data(), data.size(), &srv)); pkt.questions_.clear(); pkt.add_question(new DNSQuestion("64.48.32.16.in-addr.arpa", T_PTR)); - // Wrong sort of answer. pkt.answers_.clear(); pkt.add_answer(new DNSMxRR("example.com", 100, 100, "mx1.example.com")); data = pkt.data(); - EXPECT_EQ(ARES_SUCCESS, ares_parse_srv_reply(data.data(), data.size(), &srv)); + EXPECT_EQ(ARES_ENODATA, ares_parse_srv_reply(data.data(), data.size(), &srv)); EXPECT_EQ(nullptr, srv); pkt.answers_.clear(); pkt.add_answer(new DNSSrvRR("example.abc.def.com", 180, 0, 10, 8160, "example.abc.def.com")); - // No answer. pkt.answers_.clear(); data = pkt.data(); EXPECT_EQ(ARES_ENODATA, ares_parse_srv_reply(data.data(), data.size(), &srv)); pkt.add_answer(new DNSSrvRR("example.abc.def.com", 180, 0, 10, 8160, "example.abc.def.com")); - // Truncated packets. data = pkt.data(); for (size_t len = 1; len < data.size(); len++) { @@ -268,6 +479,60 @@ TEST_F(LibraryTest, ParseSrvReplyErrors) { } } +// TEST_F(LibraryTest, ParseCSrvReplyErrors) { +// DNSPacket pkt; +// pkt.set_qid(0x1234).set_response().set_aa() +// .add_question(new DNSQuestion("example.abc.def.com", T_SRV)) +// .add_answer(new DNSSrvRR("example.abc.def.com", 180, 0, 10, 8160, "example.abc.def.com")); +// std::vector data; +// cares_srv_reply* srv = nullptr; + +// // No question. +// pkt.questions_.clear(); +// data = pkt.data(); +// EXPECT_EQ(ARES_EBADRESP, cares_parse_srv_reply(data.data(), data.size(), &srv)); +// pkt.add_question(new DNSQuestion("example.abc.def.com", T_SRV)); + +// #ifdef DISABLED +// // Question != answer +// pkt.questions_.clear(); +// pkt.add_question(new DNSQuestion("Axample.com", T_SRV)); +// data = pkt.data(); +// EXPECT_EQ(ARES_ENODATA, cares_parse_srv_reply(data.data(), data.size(), &srv)); +// pkt.questions_.clear(); +// pkt.add_question(new DNSQuestion("example.com", T_SRV)); +// #endif + +// // Two questions. +// pkt.add_question(new DNSQuestion("example.abc.def.com", T_SRV)); +// data = pkt.data(); +// EXPECT_EQ(ARES_EBADRESP, cares_parse_srv_reply(data.data(), data.size(), &srv)); +// pkt.questions_.clear(); +// pkt.add_question(new DNSQuestion("64.48.32.16.in-addr.arpa", T_PTR)); + +// // Wrong sort of answer. +// pkt.answers_.clear(); +// pkt.add_answer(new DNSMxRR("example.com", 100, 100, "mx1.example.com")); +// data = pkt.data(); +// EXPECT_EQ(ARES_ENODATA, cares_parse_srv_reply(data.data(), data.size(), &srv)); +// EXPECT_EQ(nullptr, srv); +// pkt.answers_.clear(); +// pkt.add_answer(new DNSSrvRR("example.abc.def.com", 180, 0, 10, 8160, "example.abc.def.com")); + +// // No answer. +// pkt.answers_.clear(); +// data = pkt.data(); +// EXPECT_EQ(ARES_ENODATA, cares_parse_srv_reply(data.data(), data.size(), &srv)); +// pkt.add_answer(new DNSSrvRR("example.abc.def.com", 180, 0, 10, 8160, "example.abc.def.com")); + +// // Truncated packets. +// data = pkt.data(); +// for (size_t len = 1; len < data.size(); len++) { +// int rc = cares_parse_srv_reply(data.data(), len, &srv); +// EXPECT_TRUE(rc == ARES_EBADRESP || rc == ARES_EBADNAME); +// } +// } + TEST_F(LibraryTest, ParseSrvReplyAllocFail) { DNSPacket pkt; pkt.set_qid(0x1234).set_response().set_aa() @@ -284,5 +549,21 @@ TEST_F(LibraryTest, ParseSrvReplyAllocFail) { } } +// TEST_F(LibraryTest, ParseCSrvReplyAllocFail) { +// DNSPacket pkt; +// pkt.set_qid(0x1234).set_response().set_aa() +// .add_question(new DNSQuestion("example.abc.def.com", T_SRV)) +// .add_answer(new DNSCnameRR("example.com", 300, "c.example.com")) +// .add_answer(new DNSSrvRR("example.abc.def.com", 180, 0, 10, 8160, "example.abc.def.com")); +// std::vector data = pkt.data(); +// cares_srv_reply* srv = nullptr; + +// for (int ii = 1; ii <= 5; ii++) { +// ClearFails(); +// SetAllocFail(ii); +// EXPECT_EQ(ARES_ENOMEM, cares_parse_srv_reply(data.data(), data.size(), &srv)) << ii; +// } +// } + } // namespace test } // namespace ares diff --git a/test/ares-test-parse-txt.cc b/test/ares-test-parse-txt.cc index b33fb2de0c..2008792760 100644 --- a/test/ares-test-parse-txt.cc +++ b/test/ares-test-parse-txt.cc @@ -72,6 +72,43 @@ TEST_F(LibraryTest, ParseTxtExtReplyOK) { ares_free_data(txt); } +TEST_F(LibraryTest, ParseCTxtReplyOK) { + DNSPacket pkt; + std::string expected1 = "txt1.example.com"; + std::string expected2a = "txt2a"; + std::string expected2b("ABC\0ABC", 7); + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("example.com", T_MX)) + .add_answer(new DNSTxtRR("example.com", 100, {expected1})) + .add_answer(new DNSTxtRR("example.com", 100, {expected2a, expected2b})); + std::vector data = pkt.data(); + + cares_txt_reply* txt = nullptr; + EXPECT_EQ(ARES_SUCCESS, cares_parse_txt_reply(data.data(), data.size(), &txt)); + ASSERT_NE(nullptr, txt); + EXPECT_EQ(std::vector(expected1.data(), expected1.data() + expected1.size()), + std::vector(cares_txt_reply_get_txt(txt), + cares_txt_reply_get_txt(txt) + cares_txt_reply_get_length(txt))); + EXPECT_EQ(100, cares_txt_reply_get_ttl(txt)); + + const cares_txt_reply* txt2 = cares_txt_reply_get_next(txt); + ASSERT_NE(nullptr, txt2); + EXPECT_EQ(std::vector(expected2a.data(), expected2a.data() + expected2a.size()), + std::vector(cares_txt_reply_get_txt(txt2), + cares_txt_reply_get_txt(txt2) + cares_txt_reply_get_length(txt2))); + EXPECT_EQ(100, cares_txt_reply_get_ttl(txt2)); + + const cares_txt_reply* txt3 = cares_txt_reply_get_next(txt2); + ASSERT_NE(nullptr, txt3); + EXPECT_EQ(std::vector(expected2b.data(), expected2b.data() + expected2b.size()), + std::vector(cares_txt_reply_get_txt(txt3), + cares_txt_reply_get_txt(txt3) + cares_txt_reply_get_length(txt3))); + EXPECT_EQ(nullptr, cares_txt_reply_get_next(txt3)); + EXPECT_EQ(100, cares_txt_reply_get_ttl(txt3)); + + ares_free_data(txt); +} + TEST_F(LibraryTest, ParseTxtMalformedReply1) { std::vector data = { 0x12, 0x34, // qid @@ -103,6 +140,37 @@ TEST_F(LibraryTest, ParseTxtMalformedReply1) { ASSERT_EQ(nullptr, txt); } +TEST_F(LibraryTest, ParseCTxtMalformedReply1) { + std::vector data = { + 0x12, 0x34, // qid + 0x84, // response + query + AA + not-TC + not-RD + 0x00, // not-RA + not-Z + not-AD + not-CD + rc=NoError + 0x00, 0x01, // num questions + 0x00, 0x01, // num answer RRs + 0x00, 0x00, // num authority RRs + 0x00, 0x00, // num additional RRs + // Question + 0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e', + 0x03, 'c', 'o', 'm', + 0x00, + 0x00, 0x10, // type TXT + 0x00, 0x01, // class IN + // Answer 1 + 0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e', + 0x03, 'c', 'o', 'm', + 0x00, + 0x00, 0x10, // RR type + 0x00, 0x01, // class IN + 0x01, 0x02, 0x03, 0x04, // TTL + 0x00, 0x03, // rdata length + 0x12, 'a', 'b', // invalid length + }; + + cares_txt_reply* txt = nullptr; + EXPECT_EQ(ARES_EBADRESP, cares_parse_txt_reply(data.data(), data.size(), &txt)); + ASSERT_EQ(nullptr, txt); +} + TEST_F(LibraryTest, ParseTxtMalformedReply2) { std::vector data = { 0x12, 0x34, // qid @@ -131,6 +199,34 @@ TEST_F(LibraryTest, ParseTxtMalformedReply2) { ASSERT_EQ(nullptr, txt); } +TEST_F(LibraryTest, ParseCTxtMalformedReply2) { + std::vector data = { + 0x12, 0x34, // qid + 0x84, // response + query + AA + not-TC + not-RD + 0x00, // not-RA + not-Z + not-AD + not-CD + rc=NoError + 0x00, 0x01, // num questions + 0x00, 0x01, // num answer RRs + 0x00, 0x00, // num authority RRs + 0x00, 0x00, // num additional RRs + // Question + 0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e', + 0x03, 'c', 'o', 'm', + 0x00, + 0x00, 0x10, // type TXT + 0x00, 0x01, // class IN + // Answer 1 + 0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e', + 0x03, 'c', 'o', 'm', + 0x00, + 0x00, 0x10, // RR type + // truncated + }; + + cares_txt_reply* txt = nullptr; + EXPECT_EQ(ARES_EBADRESP, cares_parse_txt_reply(data.data(), data.size(), &txt)); + ASSERT_EQ(nullptr, txt); +} + TEST_F(LibraryTest, ParseTxtMalformedReply3) { std::vector data = { 0x12, 0x34, // qid @@ -162,6 +258,37 @@ TEST_F(LibraryTest, ParseTxtMalformedReply3) { ASSERT_EQ(nullptr, txt); } +TEST_F(LibraryTest, ParseCTxtMalformedReply3) { + std::vector data = { + 0x12, 0x34, // qid + 0x84, // response + query + AA + not-TC + not-RD + 0x00, // not-RA + not-Z + not-AD + not-CD + rc=NoError + 0x00, 0x01, // num questions + 0x00, 0x01, // num answer RRs + 0x00, 0x00, // num authority RRs + 0x00, 0x00, // num additional RRs + // Question + 0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e', + 0x03, 'c', 'o', 'm', + 0x00, + 0x00, 0x10, // type TXT + 0x00, 0x01, // class IN + // Answer 1 + 0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e', + 0x03, 'c', 'o', 'm', + 0x00, + 0x00, 0x10, // RR type + 0x00, 0x01, // class IN + 0x01, 0x02, 0x03, 0x04, // TTL + 0x00, 0x13, // rdata length INVALID + 0x02, 'a', 'b', + }; + + cares_txt_reply* txt = nullptr; + EXPECT_EQ(ARES_EBADRESP, cares_parse_txt_reply(data.data(), data.size(), &txt)); + ASSERT_EQ(nullptr, txt); +} + TEST_F(LibraryTest, ParseTxtMalformedReply4) { std::vector data = { 0x12, 0x34, // qid @@ -184,6 +311,28 @@ TEST_F(LibraryTest, ParseTxtMalformedReply4) { ASSERT_EQ(nullptr, txt); } +TEST_F(LibraryTest, ParseCTxtMalformedReply4) { + std::vector data = { + 0x12, 0x34, // qid + 0x84, // response + query + AA + not-TC + not-RD + 0x00, // not-RA + not-Z + not-AD + not-CD + rc=NoError + 0x00, 0x01, // num questions + 0x00, 0x01, // num answer RRs + 0x00, 0x00, // num authority RRs + 0x00, 0x00, // num additional RRs + // Question + 0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e', + 0x03, 'c', 'o', 'm', + 0x00, + 0x00, 0x10, // type TXT + 0x00, // TRUNCATED + }; + + cares_txt_reply* txt = nullptr; + EXPECT_EQ(ARES_EBADRESP, cares_parse_txt_reply(data.data(), data.size(), &txt)); + ASSERT_EQ(nullptr, txt); +} + TEST_F(LibraryTest, ParseTxtReplyErrors) { DNSPacket pkt; std::string expected1 = "txt1.example.com"; @@ -240,6 +389,62 @@ TEST_F(LibraryTest, ParseTxtReplyErrors) { } } +TEST_F(LibraryTest, ParseCTxtReplyErrors) { + DNSPacket pkt; + std::string expected1 = "txt1.example.com"; + std::string expected2a = "txt2a"; + std::string expected2b = "txt2b"; + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("example.com", T_MX)) + .add_answer(new DNSTxtRR("example.com", 100, {expected1})) + .add_answer(new DNSTxtRR("example.com", 100, {expected1})) + .add_answer(new DNSTxtRR("example.com", 100, {expected2a, expected2b})); + std::vector data = pkt.data(); + cares_txt_reply* txt = nullptr; + + // No question. + pkt.questions_.clear(); + data = pkt.data(); + txt = nullptr; + EXPECT_EQ(ARES_EBADRESP, cares_parse_txt_reply(data.data(), data.size(), &txt)); + EXPECT_EQ(nullptr, txt); + pkt.add_question(new DNSQuestion("example.com", T_MX)); + +#ifdef DISABLED + // Question != answer + pkt.questions_.clear(); + pkt.add_question(new DNSQuestion("Axample.com", T_TXT)); + data = pkt.data(); + EXPECT_EQ(ARES_ENODATA, cares_parse_txt_reply(data.data(), data.size(), &txt)); + pkt.questions_.clear(); + pkt.add_question(new DNSQuestion("example.com", T_TXT)); +#endif + + // Two questions. + pkt.add_question(new DNSQuestion("example.com", T_MX)); + data = pkt.data(); + txt = nullptr; + EXPECT_EQ(ARES_EBADRESP, cares_parse_txt_reply(data.data(), data.size(), &txt)); + EXPECT_EQ(nullptr, txt); + pkt.questions_.clear(); + pkt.add_question(new DNSQuestion("example.com", T_MX)); + + // No answer. + pkt.answers_.clear(); + data = pkt.data(); + txt = nullptr; + EXPECT_EQ(ARES_ENODATA, cares_parse_txt_reply(data.data(), data.size(), &txt)); + EXPECT_EQ(nullptr, txt); + pkt.add_answer(new DNSTxtRR("example.com", 100, {expected1})); + + // Truncated packets. + for (size_t len = 1; len < data.size(); len++) { + txt = nullptr; + EXPECT_NE(ARES_SUCCESS, cares_parse_txt_reply(data.data(), len, &txt)); + EXPECT_EQ(nullptr, txt); + } +} + TEST_F(LibraryTest, ParseTxtReplyAllocFail) { DNSPacket pkt; std::string expected1 = "txt1.example.com"; @@ -261,6 +466,27 @@ TEST_F(LibraryTest, ParseTxtReplyAllocFail) { } } +TEST_F(LibraryTest, ParseCTxtReplyAllocFail) { + DNSPacket pkt; + std::string expected1 = "txt1.example.com"; + std::string expected2a = "txt2a"; + std::string expected2b = "txt2b"; + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("example.com", T_MX)) + .add_answer(new DNSCnameRR("example.com", 300, "c.example.com")) + .add_answer(new DNSTxtRR("c.example.com", 100, {expected1})) + .add_answer(new DNSTxtRR("c.example.com", 100, {expected1})) + .add_answer(new DNSTxtRR("c.example.com", 100, {expected2a, expected2b})); + std::vector data = pkt.data(); + cares_txt_reply* txt = nullptr; + + for (int ii = 1; ii <= 13; ii++) { + ClearFails(); + SetAllocFail(ii); + EXPECT_EQ(ARES_ENOMEM, cares_parse_txt_reply(data.data(), data.size(), &txt)) << ii; + } +} + } // namespace test } // namespace ares