From 6b021946aa09e7bd5d8720101e101d9e50eaa8bb Mon Sep 17 00:00:00 2001 From: Nick Peng Date: Wed, 5 Jul 2023 00:08:29 +0800 Subject: [PATCH] conf: support prefix wildcard match. --- src/dns_client.c | 23 +++- src/dns_conf.c | 36 +++++- src/dns_conf.h | 7 +- src/dns_server.c | 33 +++-- src/smartdns.c | 4 +- test/cases/test-rule.cc | 262 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 347 insertions(+), 18 deletions(-) create mode 100644 test/cases/test-rule.cc diff --git a/src/dns_client.c b/src/dns_client.c index 407b86a..894175f 100644 --- a/src/dns_client.c +++ b/src/dns_client.c @@ -3372,6 +3372,8 @@ static int _dns_client_setup_server_packet(struct dns_server_info *server_info, struct dns_packet *packet = (struct dns_packet *)packet_buff; struct dns_head head; int encode_len = 0; + int repack = 0; + int hitchhiking = 0; *packet_data = default_packet; *packet_data_len = default_packet_len; @@ -3381,12 +3383,20 @@ static int _dns_client_setup_server_packet(struct dns_server_info *server_info, return 0; } - if (server_info->ecs_ipv4.enable == false && query->qtype == DNS_T_A) { - /* no need to encode packet */ - return 0; + if (server_info->ecs_ipv4.enable == true && query->qtype == DNS_T_A) { + repack = 1; } - if (server_info->ecs_ipv6.enable == false && query->qtype == DNS_T_AAAA) { + if (server_info->ecs_ipv6.enable == true && query->qtype == DNS_T_AAAA) { + repack = 1; + } + + if ((server_info->flags.server_flag & SERVER_FLAG_HITCHHIKING) != 0) { + hitchhiking = 1; + repack = 1; + } + + if (repack == 0) { /* no need to encode packet */ return 0; } @@ -3406,6 +3416,11 @@ static int _dns_client_setup_server_packet(struct dns_server_info *server_info, return -1; } + if (hitchhiking != 0 && dns_add_domain(packet, "-", query->qtype, DNS_C_IN) != 0) { + tlog(TLOG_ERROR, "add domain to packet failed."); + return -1; + } + /* add question */ if (dns_add_domain(packet, query->domain, query->qtype, DNS_C_IN) != 0) { tlog(TLOG_ERROR, "add domain to packet failed."); diff --git a/src/dns_conf.c b/src/dns_conf.c index efb96c1..343ef91 100644 --- a/src/dns_conf.c +++ b/src/dns_conf.c @@ -161,6 +161,8 @@ char dns_conf_user[DNS_CONF_USERNAME_LEN]; int dns_save_fail_packet; char dns_save_fail_packet_dir[DNS_MAX_PATH]; char dns_resolv_file[DNS_MAX_PATH]; +int dns_no_pidfile; +int dns_no_daemon; /* ECS */ struct dns_edns_client_subnet dns_conf_ipv4_ecs; @@ -508,6 +510,7 @@ static int _config_server(int argc, char *argv[], dns_server_type_t type, int de {"set-mark", required_argument, NULL, 254}, /* set mark */ {"bootstrap-dns", no_argument, NULL, 255}, /* set as bootstrap dns */ {"subnet", required_argument, NULL, 256}, /* set subnet */ + {"hitchhiking", no_argument, NULL, 257}, /* hitchhiking */ {NULL, no_argument, NULL, 0} }; /* clang-format on */ @@ -647,6 +650,9 @@ static int _config_server(int argc, char *argv[], dns_server_type_t type, int de _conf_client_subnet(optarg, &server->ipv4_ecs, &server->ipv6_ecs); break; } + case 257: { + server_flag |= SERVER_FLAG_HITCHHIKING; + } default: break; } @@ -841,6 +847,8 @@ static int _config_domain_rule_add(const char *domain, enum domain_rule type, vo char domain_key[DNS_MAX_CONF_CNAME_LEN]; int len = 0; + int sub_rule_only = 0; + int root_rule_only = 0; /* Reverse string, for suffix match */ len = strlen(domain); @@ -849,6 +857,11 @@ static int _config_domain_rule_add(const char *domain, enum domain_rule type, vo goto errout; } + if (len <= 0) { + tlog(TLOG_ERROR, "domain name %s too short", domain); + goto errout; + } + if (strncmp(domain, "domain-set:", sizeof("domain-set:") - 1) == 0) { struct dns_set_rule_add_callback_args args; args.type = type; @@ -858,8 +871,23 @@ static int _config_domain_rule_add(const char *domain, enum domain_rule type, vo } reverse_string(domain_key, domain, len, 1); - domain_key[len] = '.'; - len++; + if (domain[0] == '*') { + /* prefix wildcard */ + len--; + if (domain[1] == '.') { + sub_rule_only = 1; + } + } else if (domain[0] == '-') { + /* root match only */ + len--; + if (domain[1] == '.') { + root_rule_only = 1; + } + } else { + /* suffix match */ + domain_key[len] = '.'; + len++; + } domain_key[len] = 0; if (type >= DOMAIN_RULE_MAX) { @@ -884,6 +912,8 @@ static int _config_domain_rule_add(const char *domain, enum domain_rule type, vo } domain_rule->rules[type] = rule; + domain_rule->sub_rule_only = sub_rule_only; + domain_rule->root_rule_only = root_rule_only; _dns_rule_get(rule); /* update domain rule */ @@ -3517,6 +3547,8 @@ static struct config_item _config_item[] = { CONF_STRING("ca-path", (char *)&dns_conf_ca_path, DNS_MAX_PATH), CONF_STRING("user", (char *)&dns_conf_user, sizeof(dns_conf_user)), CONF_YESNO("debug-save-fail-packet", &dns_save_fail_packet), + CONF_YESNO("no-pidfile", &dns_no_pidfile), + CONF_YESNO("no-daemon", &dns_no_daemon), CONF_STRING("resolv-file", (char *)&dns_resolv_file, sizeof(dns_resolv_file)), CONF_STRING("debug-save-fail-packet-dir", (char *)&dns_save_fail_packet_dir, sizeof(dns_save_fail_packet_dir)), CONF_CUSTOM("conf-file", config_additional_file, NULL), diff --git a/src/dns_conf.h b/src/dns_conf.h index 497ee15..e8d62bb 100644 --- a/src/dns_conf.h +++ b/src/dns_conf.h @@ -119,6 +119,7 @@ typedef enum { #define DOMAIN_FLAG_NO_CACHE (1 << 17) #define SERVER_FLAG_EXCLUDE_DEFAULT (1 << 0) +#define SERVER_FLAG_HITCHHIKING (1 << 1) #define BIND_FLAG_NO_RULE_ADDR (1 << 0) #define BIND_FLAG_NO_RULE_NAMESERVER (1 << 1) @@ -217,8 +218,9 @@ extern struct dns_nftset_names dns_conf_nftset_no_speed; struct dns_domain_rule { struct dns_rule head; + unsigned char sub_rule_only : 1; + unsigned char root_rule_only : 1; struct dns_rule *rules[DOMAIN_RULE_MAX]; - int is_sub_rule[DOMAIN_RULE_MAX]; }; struct dns_nameserver_rule { @@ -517,6 +519,9 @@ extern int dns_save_fail_packet; extern char dns_save_fail_packet_dir[DNS_MAX_PATH]; extern char dns_resolv_file[DNS_MAX_PATH]; +extern int dns_no_pidfile; +extern int dns_no_daemon; + void dns_server_load_exit(void); int dns_server_load_conf(const char *file); diff --git a/src/dns_server.c b/src/dns_server.c index 0214578..6778cb0 100644 --- a/src/dns_server.c +++ b/src/dns_server.c @@ -216,6 +216,11 @@ struct dns_request_pending_list { struct hlist_node node; }; +struct dns_request_domain_rule { + struct dns_rule *rules[DOMAIN_RULE_MAX]; + int is_sub_rule[DOMAIN_RULE_MAX]; +}; + typedef DNS_CHILD_POST_RESULT (*child_request_callback)(struct dns_request *request, struct dns_request *child_request, int is_first_resp); @@ -303,7 +308,7 @@ struct dns_request { atomic_t ip_map_num; DECLARE_HASHTABLE(ip_map, 4); - struct dns_domain_rule domain_rule; + struct dns_request_domain_rule domain_rule; int skip_domain_rule; struct dns_domain_check_orders *check_order_list; int check_order; @@ -710,7 +715,7 @@ static void _dns_server_audit_log(struct dns_server_post_context *context) return; } - for (j = 1; j < DNS_RRS_END && context->packet; j++) { + for (j = 1; j < DNS_RRS_OPT && context->packet; j++) { rrs = dns_get_rrs_start(context->packet, j, &rr_count); for (i = 0; i < rr_count && rrs && left_len > 0; i++, rrs = dns_get_rrs_next(context->packet, rrs)) { switch (rrs->type) { @@ -1341,7 +1346,7 @@ static int _dns_cache_cname_packet(struct dns_server_post_context *context) return -1; } - for (j = 1; j < DNS_RRS_END && context->packet; j++) { + for (j = 1; j < DNS_RRS_OPT && context->packet; j++) { rrs = dns_get_rrs_start(context->packet, j, &rr_count); for (i = 0; i < rr_count && rrs; i++, rrs = dns_get_rrs_next(context->packet, rrs)) { switch (rrs->type) { @@ -1699,7 +1704,7 @@ static int _dns_server_setup_ipset_nftset_packet(struct dns_server_post_context timeout_value = _dns_server_get_conf_ttl(request, 0) * 3; } - for (j = 1; j < DNS_RRS_END; j++) { + for (j = 1; j < DNS_RRS_OPT; j++) { rrs = dns_get_rrs_start(context->packet, j, &rr_count); for (i = 0; i < rr_count && rrs; i++, rrs = dns_get_rrs_next(context->packet, rrs)) { switch (rrs->type) { @@ -2993,7 +2998,7 @@ static int _dns_server_process_answer(struct dns_request *request, const char *d request->rcode = packet->head.rcode; } - for (j = 1; j < DNS_RRS_END; j++) { + for (j = 1; j < DNS_RRS_OPT; j++) { rrs = dns_get_rrs_start(packet, j, &rr_count); for (i = 0; i < rr_count && rrs; i++, rrs = dns_get_rrs_next(packet, rrs)) { switch (rrs->type) { @@ -3057,7 +3062,7 @@ static int _dns_server_process_answer(struct dns_request *request, const char *d } } break; default: - tlog(TLOG_DEBUG, "%s, qtype: %d", name, rrs->type); + tlog(TLOG_DEBUG, "%s, qtype: %d, rrstype = %d", name, rrs->type, j); break; } } @@ -3093,7 +3098,7 @@ static int _dns_server_passthrough_rule_check(struct dns_request *request, const request->rcode = packet->head.rcode; } - for (j = 1; j < DNS_RRS_END; j++) { + for (j = 1; j < DNS_RRS_OPT; j++) { rrs = dns_get_rrs_start(packet, j, &rr_count); for (i = 0; i < rr_count && rrs; i++, rrs = dns_get_rrs_next(packet, rrs)) { switch (rrs->type) { @@ -3219,7 +3224,7 @@ static int _dns_server_get_answer(struct dns_server_post_context *context) struct dns_request *request = context->request; struct dns_packet *packet = context->packet; - for (j = 1; j < DNS_RRS_END; j++) { + for (j = 1; j < DNS_RRS_OPT; j++) { rrs = dns_get_rrs_start(packet, j, &rr_count); for (i = 0; i < rr_count && rrs; i++, rrs = dns_get_rrs_next(packet, rrs)) { switch (rrs->type) { @@ -3925,6 +3930,16 @@ static int _dns_server_get_rules(unsigned char *key, uint32_t key_len, int is_su return 0; } + /* only subkey rule */ + if (domain_rule->sub_rule_only == 1 && is_subkey == 0) { + return 0; + } + + /* only root key rule */ + if (domain_rule->root_rule_only == 1 && is_subkey == 1) { + return 0; + } + for (i = 0; i < DOMAIN_RULE_MAX; i++) { if (domain_rule->rules[i] == NULL) { continue; @@ -4331,7 +4346,7 @@ static int _dns_server_process_cname_pre(struct dns_request *request) { struct dns_cname_rule *cname = NULL; struct dns_rule_flags *rule_flag = NULL; - struct dns_domain_rule domain_rule; + struct dns_request_domain_rule domain_rule; if (_dns_server_has_bind_flag(request, BIND_FLAG_NO_RULE_CNAME) == 0) { return 0; diff --git a/src/smartdns.c b/src/smartdns.c index 68ebbd1..3c986f2 100644 --- a/src/smartdns.c +++ b/src/smartdns.c @@ -810,7 +810,7 @@ int main(int argc, char *argv[]) goto errout; } - if (is_foreground == 0) { + if (is_foreground == 0 && dns_no_daemon == 0) { daemon_ret = run_daemon(); if (daemon_ret < 0) { char buff[4096]; @@ -831,7 +831,7 @@ int main(int argc, char *argv[]) _reg_signal(); } - if (strncmp(pid_file, "-", 2) != 0 && create_pid_file(pid_file) != 0) { + if (strncmp(pid_file, "-", 2) != 0 && dns_no_pidfile == 0 && create_pid_file(pid_file) != 0) { ret = -2; goto errout; } diff --git a/test/cases/test-rule.cc b/test/cases/test-rule.cc new file mode 100644 index 0000000..0a50080 --- /dev/null +++ b/test/cases/test-rule.cc @@ -0,0 +1,262 @@ +/************************************************************************* + * + * Copyright (C) 2018-2023 Ruilin Peng (Nick) . + * + * smartdns is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * smartdns is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "client.h" +#include "dns.h" +#include "include/utils.h" +#include "server.h" +#include "util.h" +#include "gtest/gtest.h" +#include + +class Rule : public ::testing::Test +{ + protected: + virtual void SetUp() {} + virtual void TearDown() {} +}; + +TEST_F(Rule, Match) +{ + smartdns::MockServer server_upstream; + smartdns::Server server; + + server_upstream.Start("udp://0.0.0.0:61053", [&](struct smartdns::ServerRequestContext *request) { + if (request->qtype == DNS_T_A) { + smartdns::MockServer::AddIP(request, request->domain.c_str(), "1.2.3.4", 700); + return smartdns::SERVER_REQUEST_OK; + } else if (request->qtype == DNS_T_AAAA) { + smartdns::MockServer::AddIP(request, request->domain.c_str(), "64:ff9b::102:304", 700); + return smartdns::SERVER_REQUEST_OK; + } + return smartdns::SERVER_REQUEST_SOA; + }); + + server.Start(R"""(bind [::]:60053 +server 127.0.0.1:61053 +log-num 0 +log-console yes +log-level debug +speed-check-mode none +address /a.com/5.6.7.8 +cache-persist no)"""); + smartdns::Client client; + ASSERT_TRUE(client.Query("a.com A", 60053)); + std::cout << client.GetResult() << std::endl; + ASSERT_EQ(client.GetAnswerNum(), 1); + EXPECT_EQ(client.GetStatus(), "NOERROR"); + EXPECT_EQ(client.GetAnswer()[0].GetName(), "a.com"); + EXPECT_EQ(client.GetAnswer()[0].GetTTL(), 600); + EXPECT_EQ(client.GetAnswer()[0].GetType(), "A"); + EXPECT_EQ(client.GetAnswer()[0].GetData(), "5.6.7.8"); + + ASSERT_TRUE(client.Query("a.a.com A", 60053)); + std::cout << client.GetResult() << std::endl; + ASSERT_EQ(client.GetAnswerNum(), 1); + EXPECT_EQ(client.GetStatus(), "NOERROR"); + EXPECT_EQ(client.GetAnswer()[0].GetName(), "a.a.com"); + EXPECT_EQ(client.GetAnswer()[0].GetTTL(), 600); + EXPECT_EQ(client.GetAnswer()[0].GetType(), "A"); + EXPECT_EQ(client.GetAnswer()[0].GetData(), "5.6.7.8"); + + ASSERT_TRUE(client.Query("aa.com A", 60053)); + std::cout << client.GetResult() << std::endl; + ASSERT_EQ(client.GetAnswerNum(), 1); + EXPECT_EQ(client.GetStatus(), "NOERROR"); + EXPECT_EQ(client.GetAnswer()[0].GetName(), "aa.com"); + EXPECT_EQ(client.GetAnswer()[0].GetTTL(), 700); + EXPECT_EQ(client.GetAnswer()[0].GetType(), "A"); + EXPECT_EQ(client.GetAnswer()[0].GetData(), "1.2.3.4"); +} + +TEST_F(Rule, PrefixWildcardMatch) +{ + smartdns::MockServer server_upstream; + smartdns::Server server; + + server_upstream.Start("udp://0.0.0.0:61053", [&](struct smartdns::ServerRequestContext *request) { + if (request->qtype == DNS_T_A) { + smartdns::MockServer::AddIP(request, request->domain.c_str(), "1.2.3.4", 700); + return smartdns::SERVER_REQUEST_OK; + } else if (request->qtype == DNS_T_AAAA) { + smartdns::MockServer::AddIP(request, request->domain.c_str(), "64:ff9b::102:304", 700); + return smartdns::SERVER_REQUEST_OK; + } + return smartdns::SERVER_REQUEST_SOA; + }); + + server.Start(R"""(bind [::]:60053 +server 127.0.0.1:61053 +log-num 0 +log-console yes +log-level debug +speed-check-mode none +address /*a.com/5.6.7.8 +cache-persist no)"""); + smartdns::Client client; + ASSERT_TRUE(client.Query("a.com A", 60053)); + std::cout << client.GetResult() << std::endl; + ASSERT_EQ(client.GetAnswerNum(), 1); + EXPECT_EQ(client.GetStatus(), "NOERROR"); + EXPECT_EQ(client.GetAnswer()[0].GetName(), "a.com"); + EXPECT_EQ(client.GetAnswer()[0].GetTTL(), 600); + EXPECT_EQ(client.GetAnswer()[0].GetType(), "A"); + EXPECT_EQ(client.GetAnswer()[0].GetData(), "5.6.7.8"); + + ASSERT_TRUE(client.Query("a.a.com A", 60053)); + std::cout << client.GetResult() << std::endl; + ASSERT_EQ(client.GetAnswerNum(), 1); + EXPECT_EQ(client.GetStatus(), "NOERROR"); + EXPECT_EQ(client.GetAnswer()[0].GetName(), "a.a.com"); + EXPECT_EQ(client.GetAnswer()[0].GetTTL(), 600); + EXPECT_EQ(client.GetAnswer()[0].GetType(), "A"); + EXPECT_EQ(client.GetAnswer()[0].GetData(), "5.6.7.8"); + + ASSERT_TRUE(client.Query("aa.com A", 60053)); + std::cout << client.GetResult() << std::endl; + ASSERT_EQ(client.GetAnswerNum(), 1); + EXPECT_EQ(client.GetStatus(), "NOERROR"); + EXPECT_EQ(client.GetAnswer()[0].GetName(), "aa.com"); + EXPECT_EQ(client.GetAnswer()[0].GetTTL(), 600); + EXPECT_EQ(client.GetAnswer()[0].GetType(), "A"); + EXPECT_EQ(client.GetAnswer()[0].GetData(), "5.6.7.8"); + + ASSERT_TRUE(client.Query("ab.com A", 60053)); + std::cout << client.GetResult() << std::endl; + ASSERT_EQ(client.GetAnswerNum(), 1); + EXPECT_EQ(client.GetStatus(), "NOERROR"); + EXPECT_EQ(client.GetAnswer()[0].GetName(), "ab.com"); + EXPECT_EQ(client.GetAnswer()[0].GetTTL(), 700); + EXPECT_EQ(client.GetAnswer()[0].GetType(), "A"); + EXPECT_EQ(client.GetAnswer()[0].GetData(), "1.2.3.4"); +} + +TEST_F(Rule, SubDomainMatchOnly) +{ + smartdns::MockServer server_upstream; + smartdns::Server server; + + server_upstream.Start("udp://0.0.0.0:61053", [&](struct smartdns::ServerRequestContext *request) { + if (request->qtype == DNS_T_A) { + smartdns::MockServer::AddIP(request, request->domain.c_str(), "1.2.3.4", 700); + return smartdns::SERVER_REQUEST_OK; + } else if (request->qtype == DNS_T_AAAA) { + smartdns::MockServer::AddIP(request, request->domain.c_str(), "64:ff9b::102:304", 700); + return smartdns::SERVER_REQUEST_OK; + } + return smartdns::SERVER_REQUEST_SOA; + }); + + server.Start(R"""(bind [::]:60053 +server 127.0.0.1:61053 +log-num 0 +log-console yes +log-level debug +speed-check-mode none +address /*.a.com/5.6.7.8 +cache-persist no)"""); + smartdns::Client client; + ASSERT_TRUE(client.Query("a.com A", 60053)); + std::cout << client.GetResult() << std::endl; + ASSERT_EQ(client.GetAnswerNum(), 1); + EXPECT_EQ(client.GetStatus(), "NOERROR"); + EXPECT_EQ(client.GetAnswer()[0].GetName(), "a.com"); + EXPECT_EQ(client.GetAnswer()[0].GetTTL(), 700); + EXPECT_EQ(client.GetAnswer()[0].GetType(), "A"); + EXPECT_EQ(client.GetAnswer()[0].GetData(), "1.2.3.4"); + + ASSERT_TRUE(client.Query("a.a.com A", 60053)); + std::cout << client.GetResult() << std::endl; + ASSERT_EQ(client.GetAnswerNum(), 1); + EXPECT_EQ(client.GetStatus(), "NOERROR"); + EXPECT_EQ(client.GetAnswer()[0].GetName(), "a.a.com"); + EXPECT_EQ(client.GetAnswer()[0].GetTTL(), 600); + EXPECT_EQ(client.GetAnswer()[0].GetType(), "A"); + EXPECT_EQ(client.GetAnswer()[0].GetData(), "5.6.7.8"); + + ASSERT_TRUE(client.Query("aa.com A", 60053)); + std::cout << client.GetResult() << std::endl; + ASSERT_EQ(client.GetAnswerNum(), 1); + EXPECT_EQ(client.GetStatus(), "NOERROR"); + EXPECT_EQ(client.GetAnswer()[0].GetName(), "aa.com"); + EXPECT_EQ(client.GetAnswer()[0].GetTTL(), 700); + EXPECT_EQ(client.GetAnswer()[0].GetType(), "A"); + EXPECT_EQ(client.GetAnswer()[0].GetData(), "1.2.3.4"); +} + +TEST_F(Rule, RootDomainMatchOnly) +{ + smartdns::MockServer server_upstream; + smartdns::Server server; + + server_upstream.Start("udp://0.0.0.0:61053", [&](struct smartdns::ServerRequestContext *request) { + if (request->qtype == DNS_T_A) { + smartdns::MockServer::AddIP(request, request->domain.c_str(), "1.2.3.4", 700); + return smartdns::SERVER_REQUEST_OK; + } else if (request->qtype == DNS_T_AAAA) { + smartdns::MockServer::AddIP(request, request->domain.c_str(), "64:ff9b::102:304", 700); + return smartdns::SERVER_REQUEST_OK; + } + return smartdns::SERVER_REQUEST_SOA; + }); + + server.Start(R"""(bind [::]:60053 +server 127.0.0.1:61053 +log-num 0 +log-console yes +log-level debug +speed-check-mode none +address /-.a.com/5.6.7.8 +cache-persist no)"""); + smartdns::Client client; + ASSERT_TRUE(client.Query("a.com A", 60053)); + std::cout << client.GetResult() << std::endl; + ASSERT_EQ(client.GetAnswerNum(), 1); + EXPECT_EQ(client.GetStatus(), "NOERROR"); + EXPECT_EQ(client.GetAnswer()[0].GetName(), "a.com"); + EXPECT_EQ(client.GetAnswer()[0].GetTTL(), 600); + EXPECT_EQ(client.GetAnswer()[0].GetType(), "A"); + EXPECT_EQ(client.GetAnswer()[0].GetData(), "5.6.7.8"); + + ASSERT_TRUE(client.Query("a.a.com A", 60053)); + std::cout << client.GetResult() << std::endl; + ASSERT_EQ(client.GetAnswerNum(), 1); + EXPECT_EQ(client.GetStatus(), "NOERROR"); + EXPECT_EQ(client.GetAnswer()[0].GetName(), "a.a.com"); + EXPECT_EQ(client.GetAnswer()[0].GetTTL(), 700); + EXPECT_EQ(client.GetAnswer()[0].GetType(), "A"); + EXPECT_EQ(client.GetAnswer()[0].GetData(), "1.2.3.4"); + + ASSERT_TRUE(client.Query("b.a.com A", 60053)); + std::cout << client.GetResult() << std::endl; + ASSERT_EQ(client.GetAnswerNum(), 1); + EXPECT_EQ(client.GetStatus(), "NOERROR"); + EXPECT_EQ(client.GetAnswer()[0].GetName(), "b.a.com"); + EXPECT_EQ(client.GetAnswer()[0].GetTTL(), 700); + EXPECT_EQ(client.GetAnswer()[0].GetType(), "A"); + EXPECT_EQ(client.GetAnswer()[0].GetData(), "1.2.3.4"); + + ASSERT_TRUE(client.Query("ba.com A", 60053)); + std::cout << client.GetResult() << std::endl; + ASSERT_EQ(client.GetAnswerNum(), 1); + EXPECT_EQ(client.GetStatus(), "NOERROR"); + EXPECT_EQ(client.GetAnswer()[0].GetName(), "ba.com"); + EXPECT_EQ(client.GetAnswer()[0].GetTTL(), 700); + EXPECT_EQ(client.GetAnswer()[0].GetType(), "A"); + EXPECT_EQ(client.GetAnswer()[0].GetData(), "1.2.3.4"); +} \ No newline at end of file