浏览代码

feature: support fallback dns server.

Nick Peng 7 月之前
父节点
当前提交
c19db1a519
共有 8 个文件被更改,包括 83 次插入2 次删除
  1. 1 0
      etc/smartdns/smartdns.conf
  2. 14 0
      src/dns_client.c
  3. 1 0
      src/dns_client.h
  4. 5 0
      src/dns_conf.c
  5. 1 0
      src/dns_conf.h
  6. 1 1
      src/dns_server.c
  7. 1 0
      src/smartdns.c
  8. 59 1
      test/cases/test-server.cc

+ 1 - 0
etc/smartdns/smartdns.conf

@@ -219,6 +219,7 @@ log-level info
 #   -subnet [ip/subnet]: set edns client subnet.
 #   -host-ip [ip]: set dns server host ip.
 #   -interface [interface]: set dns server interface.
+#   -fallback: set as fallback dns server.
 # server 8.8.8.8 -blacklist-ip -check-edns -group g1 -group g2
 # server tls://dns.google:853 
 # server https://dns.google/dns-query

+ 14 - 0
src/dns_client.c

@@ -3861,10 +3861,24 @@ static int _dns_client_send_packet(struct dns_query_struct *query, void *packet,
 			prohibit_time = 5;
 		}
 
+		/* fallback group exists, use fallback group */
+		if (atomic_read(&query->retry_count) == 1) {
+			struct dns_server_group *fallback_server_group = _dns_client_get_group("fallback");
+			if (fallback_server_group != NULL) {
+				query->server_group = fallback_server_group;
+			}
+		}
+
 		pthread_mutex_lock(&client.server_list_lock);
 		list_for_each_entry_safe(group_member, tmp, &query->server_group->head, list)
 		{
 			server_info = group_member->server;
+
+			/* skip fallback server for first query */
+			if (server_info->flags.fallback && atomic_read(&query->retry_count) == DNS_QUERY_RETRY && i == 0) {
+				continue;
+			}
+
 			if (server_info->prohibit) {
 				if (server_info->is_already_prohibit == 0) {
 					server_info->is_already_prohibit = 1;

+ 1 - 0
src/dns_client.h

@@ -143,6 +143,7 @@ struct client_dns_server_flags {
 	long long set_mark;
 	int tcp_keepalive;
 	int drop_packet_latency_ms;
+	int fallback;
 
 	char proxyname[DNS_MAX_CNAME_LEN];
 	char ifname[DNS_SERVER_IFNAME_LEN];

+ 5 - 0
src/dns_conf.c

@@ -943,6 +943,7 @@ static int _config_server(int argc, char *argv[], dns_server_type_t type, int de
 		{"tls-host-verify", required_argument, NULL, 262 }, /* verify tls hostname */
 		{"tcp-keepalive", required_argument, NULL, 263}, /* tcp keepalive */
 		{"subnet-all-query-types", no_argument, NULL, 264}, /* send subnent for all query types.*/
+		{"fallback", no_argument, NULL, 265}, /* fallback */
 		{NULL, no_argument, NULL, 0}
 	};
 	/* clang-format on */
@@ -1114,6 +1115,10 @@ static int _config_server(int argc, char *argv[], dns_server_type_t type, int de
 			server->subnet_all_query_types = 1;
 			break;
 		}
+		case 265: {
+			server->fallback = 1;
+			break;
+		}
 		default:
 			if (optind > optind_last) {
 				tlog(TLOG_WARN, "unknown server option: %s at '%s:%d'.", argv[optind - 1], conf_get_conf_file(),

+ 1 - 0
src/dns_conf.h

@@ -387,6 +387,7 @@ struct dns_servers {
 	long long set_mark;
 	unsigned int drop_packet_latency_ms;
 	int tcp_keepalive;
+	int fallback;
 	int subnet_all_query_types;
 	char skip_check_cert;
 	char spki[DNS_MAX_SPKI_LEN];

+ 1 - 1
src/dns_server.c

@@ -6815,7 +6815,7 @@ static int _dns_server_query_dualstack(struct dns_request *request)
 	ret = _dns_server_do_query(request_dualstack, 0);
 	if (ret != 0) {
 		request->request_wait--;
-		tlog(TLOG_ERROR, "do query %s type %d failed.\n", request->domain, qtype);
+		tlog(TLOG_DEBUG, "do query %s type %d failed.\n", request->domain, qtype);
 		goto errout;
 	}
 

+ 1 - 0
src/smartdns.c

@@ -234,6 +234,7 @@ static int _smartdns_prepare_server_flags(struct client_dns_server_flags *flags,
 	flags->drop_packet_latency_ms = server->drop_packet_latency_ms;
 	flags->tcp_keepalive = server->tcp_keepalive;
 	flags->subnet_all_query_types = server->subnet_all_query_types;
+	flags->fallback = server->fallback;
 	safe_strncpy(flags->proxyname, server->proxyname, sizeof(flags->proxyname));
 	safe_strncpy(flags->ifname, server->ifname, sizeof(flags->ifname));
 	if (server->ipv4_ecs.enable) {

+ 59 - 1
test/cases/test-server.cc

@@ -272,4 +272,62 @@ dualstack-ip-selection no
 	ASSERT_EQ(client.GetAnswerNum(), 0);
 	EXPECT_EQ(client.GetStatus(), "REFUSED");
 	EXPECT_LT(client.GetQueryTime(), 100);
-}
+}
+
+TEST_F(Server, fallback)
+{
+	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) {
+			return smartdns::SERVER_REQUEST_SOA;
+		}
+		smartdns::MockServer::AddIP(request, request->domain.c_str(), "1.2.3.4", 611);
+		return smartdns::SERVER_REQUEST_OK;
+	});
+
+	server.MockPing(PING_TYPE_ICMP, "2001::", 128, 10000);
+	server.Start(R"""(bind [::]:60053
+bind-tcp [::]:60053
+server 127.0.0.1:61053 -fallback
+server 127.0.0.1:61054
+)""");
+	smartdns::Client client;
+	ASSERT_TRUE(client.Query("a.com", 60053));
+	std::cout << client.GetResult() << std::endl;
+	ASSERT_EQ(client.GetAnswerNum(), 1);
+	EXPECT_GE(client.GetQueryTime(), 1000);
+	EXPECT_EQ(client.GetStatus(), "NOERROR");
+	EXPECT_EQ(client.GetAnswer()[0].GetName(), "a.com");
+	EXPECT_EQ(client.GetAnswer()[0].GetData(), "1.2.3.4");
+}
+
+TEST_F(Server, fallback_group)
+{
+	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) {
+			return smartdns::SERVER_REQUEST_SOA;
+		}
+		smartdns::MockServer::AddIP(request, request->domain.c_str(), "1.2.3.4", 611);
+		return smartdns::SERVER_REQUEST_OK;
+	});
+
+	server.MockPing(PING_TYPE_ICMP, "2001::", 128, 10000);
+	server.Start(R"""(bind [::]:60053
+bind-tcp [::]:60053
+server 127.0.0.1:61053 -e -group fallback
+server 127.0.0.1:61054
+)""");
+	smartdns::Client client;
+	ASSERT_TRUE(client.Query("a.com", 60053));
+	std::cout << client.GetResult() << std::endl;
+	ASSERT_EQ(client.GetAnswerNum(), 1);
+	EXPECT_GE(client.GetQueryTime(), 1000);
+	EXPECT_EQ(client.GetStatus(), "NOERROR");
+	EXPECT_EQ(client.GetAnswer()[0].GetName(), "a.com");
+	EXPECT_EQ(client.GetAnswer()[0].GetData(), "1.2.3.4");
+}