Browse Source

webui: add fail query count, add server security status, add error message when audit is disabled.

Nick Peng 1 month ago
parent
commit
25a8e612a8

+ 6 - 6
plugin/smartdns-ui/Cargo.toml

@@ -1,6 +1,6 @@
 [package]
 name = "smartdns-ui"
-version = "0.1.0"
+version = "1.0.0"
 edition = "2021"
 
 [lib]
@@ -11,13 +11,13 @@ ctor = "0.4.3"
 bytes = "1.10.1"
 rusqlite = { version = "0.37.0", features = ["bundled"] }
 hyper = { version = "1.7.0", features = ["full"] }
-hyper-util = { version = "0.1.16", features = ["full"] }
+hyper-util = { version = "0.1.17", features = ["full"] }
 hyper-tungstenite = "0.18.0"
 tokio = { version = "1.47.1", features = ["full"] }
-serde = { version = "1.0.219", features = ["derive"] }
+serde = { version = "1.0.225", features = ["derive"] }
 tokio-rustls = { version = "0.26.2", optional = true}
 rustls-pemfile = { version = "2.2.0", optional = true}
-serde_json = "1.0.143"
+serde_json = "1.0.145"
 http-body-util = "0.1.3"
 getopts = "0.2.24"
 url = "2.5.7"
@@ -27,7 +27,7 @@ futures = "0.3.31"
 socket2 = "0.6.0"
 cfg-if = "1.0.3"
 urlencoding = "2.1.3"
-chrono = "0.4.41"
+chrono = "0.4.42"
 nix = "0.30.1"
 tokio-fd = "0.3.0"
 pbkdf2 = { version = "0.12.2", features = ["simple"] }
@@ -42,7 +42,7 @@ default = ["https"]
 reqwest = {version = "0.12.23", features = ["blocking"]}
 tungstenite = "0.23.0"
 tokio-tungstenite = "0.23.1"
-tempfile = "3.21.0"
+tempfile = "3.22.0"
 
 [build-dependencies]
 bindgen = "0.69.5"

+ 14 - 2
plugin/smartdns-ui/src/data_server.rs

@@ -61,6 +61,7 @@ pub struct OverviewData {
 pub struct MetricsData {
     pub total_query_count: u64,
     pub block_query_count: u64,
+    pub fail_query_count: u64,
     pub avg_query_time: f64,
     pub cache_hit_rate: f64,
     pub cache_number: u64,
@@ -179,7 +180,7 @@ impl DataServerControl {
                 Plugin::smartdns_exit(1);
             }
 
-            dns_log!(LogLevel::INFO, "data server exit.");
+            dns_log!(LogLevel::DEBUG, "data server exit.");
         });
 
         *self.is_run.lock().unwrap() = true;
@@ -474,6 +475,7 @@ impl DataServer {
         let metrics = MetricsData {
             total_query_count: self.stat.get_total_request(),
             block_query_count: self.stat.get_total_blocked_request(),
+            fail_query_count: self.stat.get_total_failed_request(),
             avg_query_time: smartdns::Stats::get_avg_process_time(),
             cache_hit_rate: smartdns::Stats::get_cache_hit_rate(),
             cache_number: smartdns::Plugin::dns_cache_total_num() as u64,
@@ -522,6 +524,10 @@ impl DataServer {
             self.stat.add_total_blocked_request(1);
         }
 
+        if data.reply_code != 0 {
+            self.stat.add_total_failed_request(1);
+        }   
+
         self.db.insert_domain(&list)
     }
 
@@ -532,6 +538,7 @@ impl DataServer {
         let mut domain_data_list = Vec::new();
         let mut client_data_list = Vec::new();
         let mut blocked_num = 0;
+        let mut failed_num = 0;
         let timestamp_now = get_utc_time_ms();
 
         for req in req_list {
@@ -547,6 +554,10 @@ impl DataServer {
                 blocked_num += 1;
             }
 
+            if req.get_rcode() != 0 {
+                failed_num += 1;
+            }
+
             let domain_data = DomainData {
                 id: 0,
                 domain: req.get_domain(),
@@ -589,6 +600,7 @@ impl DataServer {
 
         this.stat.add_total_request(domain_data_list.len() as u64);
         this.stat.add_total_blocked_request(blocked_num as u64);
+        this.stat.add_total_failed_request(failed_num as u64);
 
         dns_log!(
             LogLevel::DEBUG,
@@ -695,7 +707,7 @@ impl DataServer {
         let mut check_timer = tokio::time::interval(Duration::from_secs(60));
         let is_check_timer_running = Arc::new(AtomicBool::new(false));
 
-        dns_log!(LogLevel::INFO, "data server start.");
+        dns_log!(LogLevel::DEBUG, "data server start.");
 
         loop {
             tokio::select! {

+ 58 - 2
plugin/smartdns-ui/src/data_stats.rs

@@ -39,6 +39,7 @@ use tokio::time::{interval_at, Instant};
 struct DataStatsItem {
     total_request: AtomicU64,
     total_blocked_request: AtomicU64,
+    total_failed_request: AtomicU64,
     qps: AtomicU32,
     qps_count: AtomicU32,
     request_dropped: AtomicU64,
@@ -48,6 +49,7 @@ struct DataStatsItem {
 struct DataStatsItem {
     total_request: Arc<Mutex<u64>>,
     total_blocked_request: Arc<Mutex<u64>>,
+    total_failed_request: Arc<Mutex<u64>>,
     qps: AtomicU32,
     qps_count: AtomicU32,
     request_dropped: Arc<Mutex<u64>>,
@@ -59,6 +61,7 @@ impl DataStatsItem {
         let ret = DataStatsItem {
             total_request: 0.into(),
             total_blocked_request: 0.into(),
+            total_failed_request: 0.into(),
             qps: 0.into(),
             qps_count: 0.into(),
             request_dropped: 0.into(),
@@ -67,6 +70,7 @@ impl DataStatsItem {
         let ret = DataStatsItem {
             total_request: Arc::new(Mutex::new(0)),
             total_blocked_request: Arc::new(Mutex::new(0)),
+            total_failed_request: Arc::new(Mutex::new(0)),
             qps: 0.into(),
             qps_count: 0.into(),
             request_dropped: Arc::new(Mutex::new(0)),
@@ -154,6 +158,33 @@ impl DataStatsItem {
         }
     }
 
+    pub fn add_total_failed_request(&self, total: u64) {
+        #[cfg(target_has_atomic = "64")]
+        {
+            self.total_failed_request
+                .fetch_add(total, Ordering::Relaxed);
+        }
+
+        #[cfg(not(target_has_atomic = "64"))]
+        {
+            let mut total_failed_request = self.total_failed_request.lock().unwrap();
+            *total_failed_request += total;
+        }
+    }
+
+    pub fn get_total_failed_request(&self) -> u64 {
+        #[cfg(target_has_atomic = "64")]
+        {
+            return self.total_failed_request.load(Ordering::Relaxed);
+        }
+
+        #[cfg(not(target_has_atomic = "64"))]
+        {
+            let total = self.total_failed_request.lock().unwrap();
+            return *total;
+        }
+    }
+
     #[allow(dead_code)]
     pub fn get_current_hour_total(&self) -> u64 {
         return Stats::get_request_total();
@@ -211,6 +242,14 @@ impl DataStats {
         self.data.add_total_blocked_request(total);
     }
 
+    pub fn get_total_failed_request(&self) -> u64 {
+        return self.data.get_total_failed_request();
+    }
+
+    pub fn add_total_failed_request(&self, total: u64) {
+        self.data.add_total_failed_request(total);
+    }
+
     pub fn get_total_request(&self) -> u64 {
         return self.data.get_total_request();
     }
@@ -291,11 +330,24 @@ impl DataStats {
         if total_blocked_count == 0 {
             let mut parm = DomainListGetParam::new();
             parm.is_blocked = Some(true);
-
+            
             let count = self.db.get_domain_list_count(Some(&parm));
             total_blocked_count = count;
         }
         self.data.add_total_blocked_request(total_blocked_count);
+
+        // load total failed request
+        let mut total_failed_count = 0 as u64;
+        let status_data_total_failed_count = status_data.get("total_failed_request");
+        if status_data_total_failed_count.is_some() {
+            let count = status_data_total_failed_count.unwrap().parse::<u64>();
+            if let Ok(count) = count {
+                total_failed_count = count;
+            } else {
+                total_failed_count = 0;
+            }
+        }
+        self.data.add_total_failed_request(total_failed_count);
         Ok(())
     }
 
@@ -308,6 +360,10 @@ impl DataStats {
             "total_blocked_request",
             self.get_total_blocked_request().to_string().as_str(),
         )?;
+        self.db.set_status_data(
+            "total_failed_request",
+            self.get_total_failed_request().to_string().as_str(),
+        )?;
 
         Ok(())
     }
@@ -331,7 +387,7 @@ impl DataStats {
             .delete_domain_before_timestamp(now - self.conf.read().unwrap().max_log_age_ms as u64);
         if let Err(e) = ret {
             if e.to_string() == "Query returned no rows" {
-                return 
+                return;
             }
 
             dns_log!(

+ 4 - 0
plugin/smartdns-ui/src/data_upstream_server.rs

@@ -30,6 +30,7 @@ pub struct UpstreamServerInfo {
     pub query_success_rate: f64,
     pub avg_time: f64,
     pub status: String,
+    pub security: String,
 }
 
 impl UpstreamServerInfo {
@@ -48,6 +49,8 @@ impl UpstreamServerInfo {
                     "Abnormal"
                 };
 
+                let security_status = server.get_server_security_status();
+
                 servers.push(UpstreamServerInfo {
                     host: server.get_host(),
                     ip: server.get_ip(),
@@ -59,6 +62,7 @@ impl UpstreamServerInfo {
                     query_success_rate: stats.get_success_rate(),
                     avg_time: stats.get_query_avg_time(),
                     status: status.to_string(),
+                    security: security_status.to_string(),
                 });
             });
         Ok(servers)

+ 14 - 0
plugin/smartdns-ui/src/http_api_msg.rs

@@ -440,6 +440,7 @@ pub fn api_msg_gen_upstream_server_list(upstream_server_list: &Vec<UpstreamServe
                         "query_success_rate": x.query_success_rate,
                         "avg_time": x.avg_time,
                         "status": x.status,
+                        "security": x.security,
                     });
                     s
                 })
@@ -510,6 +511,11 @@ pub fn api_msg_parse_upstream_server_list(
             return Err("status not found".into());
         }
 
+        let security = item["security"].as_str();
+        if security.is_none() {
+            return Err("security not found".into());
+        }
+
         upstream_server_list.push(UpstreamServerInfo {
             host: host.unwrap().to_string(),
             ip: ip.unwrap().to_string(),
@@ -521,6 +527,7 @@ pub fn api_msg_parse_upstream_server_list(
             query_success_rate: query_success_rate.unwrap(),
             avg_time: avg_time.unwrap(),
             status: status.unwrap().to_string(),
+            security: security.unwrap().to_string(),
         });
     }
 
@@ -690,6 +697,7 @@ pub fn api_msg_gen_metrics_data(data: &MetricsData) -> String {
     let json_str = json!({
         "total_query_count": data.total_query_count,
         "block_query_count": data.block_query_count,
+        "fail_query_count": data.fail_query_count,
         "avg_query_time": data.avg_query_time,
         "cache_hit_rate": data.cache_hit_rate,
         "cache_number": data.cache_number,
@@ -714,6 +722,11 @@ pub fn api_msg_parse_metrics_data(data: &str) -> Result<MetricsData, Box<dyn Err
         return Err("block_query_count not found".into());
     }
 
+    let fail_query_count = v["fail_query_count"].as_u64();
+    if fail_query_count.is_none() {
+        return Err("fail_query_count not found".into());
+    }
+
     let avg_query_time = v["avg_query_time"].as_f64();
     if avg_query_time.is_none() {
         return Err("avg_query_time not found".into());
@@ -749,6 +762,7 @@ pub fn api_msg_parse_metrics_data(data: &str) -> Result<MetricsData, Box<dyn Err
     Ok(MetricsData {
         total_query_count: total_query_count.unwrap() as u64,
         block_query_count: block_query_count.unwrap() as u64,
+        fail_query_count: fail_query_count.unwrap() as u64,
         avg_query_time: avg_query_time.unwrap(),
         cache_hit_rate: cache_hit_rate.unwrap(),
         cache_number: cache_number.unwrap() as u64,

+ 3 - 7
plugin/smartdns-ui/src/http_server.rs

@@ -123,7 +123,7 @@ impl HttpServerConfig {
             }
         }
 
-        if let Some(username) = data_server.get_server_config("smartdns-ui.username") {
+        if let Some(username) = data_server.get_server_config("smartdns-ui.user") {
             self.username = username;
         }
 
@@ -187,8 +187,6 @@ impl HttpServerControl {
     }
 
     pub fn start_http_server(&self, conf: &HttpServerConfig) -> Result<(), Box<dyn Error>> {
-        dns_log!(LogLevel::INFO, "start smartdns-ui server.");
-
         let inner_clone = Arc::clone(&self.http_server);
         let ret = inner_clone.set_conf(conf);
         if let Err(e) = ret {
@@ -207,7 +205,7 @@ impl HttpServerControl {
                 dns_log!(LogLevel::ERROR, "http server error: {}", e);
                 Plugin::smartdns_exit(1);
             }
-            dns_log!(LogLevel::INFO, "http server exit.");
+            dns_log!(LogLevel::DEBUG, "http server exit.");
         });
 
         tokio::task::block_in_place(|| {
@@ -225,8 +223,6 @@ impl HttpServerControl {
             return;
         }
 
-        dns_log!(LogLevel::INFO, "stop smartdns-ui server.");
-
         self.http_server.stop_http_server();
 
         if let Some(server_thread) = server_thread.take() {
@@ -881,7 +877,7 @@ impl HttpServer {
         let addr = listner.local_addr()?;
 
         *this.local_addr.lock().unwrap() = Some(addr);
-        dns_log!(LogLevel::INFO, "http server listen at {}", url);
+        dns_log!(LogLevel::INFO, "http server listen at {}", addr);
 
         let _ = kickoff_tx.send(0);
         loop {

+ 14 - 0
plugin/smartdns-ui/src/http_server_stream.rs

@@ -18,6 +18,8 @@
 
 use futures::sink::SinkExt;
 use futures::stream::StreamExt;
+use hyper_tungstenite::tungstenite::protocol::frame::coding::CloseCode;
+use hyper_tungstenite::tungstenite::protocol::CloseFrame;
 use std::os::fd::AsRawFd;
 use std::sync::Arc;
 use std::time::Duration;
@@ -182,6 +184,18 @@ pub async fn serve_audit_log_stream(
     let data_server = http_server.get_data_server();
     let mut log_stream = data_server.get_audit_log_stream().await;
 
+    if dns_audit_log_enabled() == false {
+        let reason =
+            "Audit log is not enabled, please set `audit-enable` to `yes` in smartdns config file.";
+
+        let close_msg = CloseFrame {
+            code: CloseCode::Bad(4003),
+            reason: reason.into(),
+        };
+        websocket.send(Message::Close(Some(close_msg))).await?;
+        return Ok(());
+    }
+
     loop {
         tokio::select! {
             msg = log_stream.recv() => {

+ 15 - 1
plugin/smartdns-ui/src/plugin.rs

@@ -36,6 +36,8 @@ pub struct SmartdnsPlugin {
     data_conf: Mutex<DataServerConfig>,
 
     runtime: Arc<Runtime>,
+
+    is_start: Mutex<bool>,
 }
 
 impl SmartdnsPlugin {
@@ -53,6 +55,8 @@ impl SmartdnsPlugin {
             data_server_ctl: Arc::new(DataServerControl::new()),
             data_conf: Mutex::new(DataServerConfig::new()),
             runtime: Arc::new(rt),
+
+            is_start: Mutex::new(false),
         });
 
         plugin.http_server_ctl.set_plugin(plugin.clone());
@@ -110,7 +114,6 @@ impl SmartdnsPlugin {
         if let Some(root) = matches.opt_str("r") {
             http_conf.http_root = root;
         }
-        dns_log!(LogLevel::INFO, "www root: {}", http_conf.http_root);
 
         let mut token_expire = Plugin::dns_conf_plugin_config("smartdns-ui.token-expire");
         if token_expire.is_none() {
@@ -150,6 +153,10 @@ impl SmartdnsPlugin {
     }
 
     pub fn start(&self, args: &Vec<String>) -> Result<(), Box<dyn Error>> {
+        dns_log!(LogLevel::INFO, "start smartdns-ui server.");
+        let mut is_start = self.is_start.lock().unwrap();
+        *is_start = true;
+
         self.parser_args(args)?;
         self.load_config()?;
         self.data_server_ctl
@@ -163,6 +170,13 @@ impl SmartdnsPlugin {
     }
 
     pub fn stop(&self) {
+        let mut is_start = self.is_start.lock().unwrap();
+        if !*is_start {
+            return;
+        }
+        *is_start = false;
+        
+        dns_log!(LogLevel::INFO, "stop smartdns-ui server.");
         self.http_server_ctl.stop_http_server();
         self.data_server_ctl.stop_data_server();
     }

+ 46 - 0
plugin/smartdns-ui/src/smartdns.rs

@@ -160,6 +160,40 @@ impl ToString for DnsServerType {
     }
 }
 
+#[derive(Debug, Clone)]
+pub enum DnsServerTrustStatus {
+    TRUST_UNKNOW,
+    TRUST_NOT_APPLICABLE,
+    TRUST_VERIFY_FAILED,
+    TRUST_INSECURE,
+    TRUST_SECURE,
+}
+
+impl From<u32> for DnsServerTrustStatus {
+    fn from(t: u32) -> DnsServerTrustStatus {
+        match t {
+            0 => DnsServerTrustStatus::TRUST_UNKNOW,
+            1 => DnsServerTrustStatus::TRUST_NOT_APPLICABLE,
+            2 => DnsServerTrustStatus::TRUST_VERIFY_FAILED,
+            3 => DnsServerTrustStatus::TRUST_INSECURE,
+            4 => DnsServerTrustStatus::TRUST_SECURE,
+            _ => DnsServerTrustStatus::TRUST_UNKNOW,
+        }
+    }
+}
+
+impl ToString for DnsServerTrustStatus {
+    fn to_string(&self) -> String {
+        match self {
+            DnsServerTrustStatus::TRUST_UNKNOW => "Unknown".to_string(),
+            DnsServerTrustStatus::TRUST_NOT_APPLICABLE => "Not Applicable".to_string(),
+            DnsServerTrustStatus::TRUST_VERIFY_FAILED => "Verify Failed".to_string(),
+            DnsServerTrustStatus::TRUST_INSECURE => "Insecure".to_string(),
+            DnsServerTrustStatus::TRUST_SECURE => "Secure".to_string(),
+        }
+    }
+}
+
 #[macro_export]
 macro_rules! dns_log {
     ($level:expr, $($arg:tt)*) => {
@@ -185,6 +219,10 @@ pub fn dns_log_get_level() -> LogLevel {
     }
 }
 
+pub fn dns_audit_log_enabled() -> bool {
+    unsafe { smartdns_c::smartdns_plugin_is_audit_enabled() != 0 }
+}
+
 pub fn dns_log_out(level: LogLevel, file: &str, line: u32, message: &str) {
     let filename_only = std::path::Path::new(file)
         .file_name()
@@ -722,6 +760,14 @@ impl DnsUpstreamServer {
         }
     }
 
+    pub fn get_server_security_status(&self) -> DnsServerTrustStatus {
+        unsafe {
+            let t = smartdns_c::dns_client_get_server_security_status(self.server_info)
+                as smartdns_c::dns_server_security_status;
+            DnsServerTrustStatus::from(t)
+        }
+    }
+
     pub fn get_groups(&self) -> Vec<String> {
         let groups = Vec::new();
         groups

+ 1 - 0
src/dns_client/client_mdns.c

@@ -62,6 +62,7 @@ int _dns_client_create_socket_udp_mdns(struct dns_server_info *server_info)
 
 	server_info->fd = fd;
 	server_info->status = DNS_SERVER_STATUS_CONNECTIONLESS;
+	server_info->security_status = DNS_CLIENT_SERVER_SECURITY_NOT_APPLICABLE;
 
 	memset(&event, 0, sizeof(event));
 	event.events = EPOLLIN;

+ 1 - 0
src/dns_client/client_tcp.c

@@ -113,6 +113,7 @@ int _dns_client_create_socket_tcp(struct dns_server_info *server_info)
 
 	server_info->fd = fd;
 	server_info->status = DNS_SERVER_STATUS_CONNECTING;
+	server_info->security_status = DNS_CLIENT_SERVER_SECURITY_NOT_APPLICABLE;
 	server_info->proxy = proxy;
 
 	memset(&event, 0, sizeof(event));

+ 16 - 3
src/dns_client/client_tls.c

@@ -60,7 +60,7 @@ static ssize_t _ssl_write_ext2(struct dns_server_info *server, SSL *ssl, const v
 		return SSL_ERROR_SYSCALL;
 	}
 
-#if defined(OSSL_QUIC1_VERSION) && !defined (OPENSSL_NO_QUIC)
+#if defined(OSSL_QUIC1_VERSION) && !defined(OPENSSL_NO_QUIC)
 	ret = SSL_write_ex2(ssl, buff, num, flags, &written);
 #else
 	ret = SSL_write_ex(ssl, buff, num, &written);
@@ -129,7 +129,7 @@ int _ssl_do_handevent(struct dns_server_info *server)
 		pthread_mutex_unlock(&server->lock);
 		return SSL_ERROR_SYSCALL;
 	}
-#if defined(OSSL_QUIC1_VERSION) && !defined (OPENSSL_NO_QUIC)
+#if defined(OSSL_QUIC1_VERSION) && !defined(OPENSSL_NO_QUIC)
 	err = SSL_handle_events(server->ssl);
 #else
 	err = SSL_ERROR_SYSCALL;
@@ -865,6 +865,7 @@ static int _dns_client_tls_verify(struct dns_server_info *server_info)
 	unsigned char *key_sha256 = NULL;
 	char *spki = NULL;
 	int spki_len = 0;
+	int is_secure = 0;
 
 	if (server_info->ssl == NULL) {
 		return -1;
@@ -886,6 +887,8 @@ static int _dns_client_tls_verify(struct dns_server_info *server_info)
 				 X509_verify_cert_error_string(res));
 			goto errout;
 		}
+
+		is_secure = 1;
 	}
 	pthread_mutex_unlock(&server_info->lock);
 
@@ -955,11 +958,19 @@ static int _dns_client_tls_verify(struct dns_server_info *server_info)
 			goto errout;
 		} else {
 			tlog(TLOG_DEBUG, "server %s cert spki verify succeed", server_info->ip);
+			is_secure = 1;
 		}
 	}
 
 	OPENSSL_free(key_data);
 	X509_free(cert);
+
+	if (is_secure) {
+		server_info->security_status = DNS_CLIENT_SERVER_SECURITY_SECURE;
+	} else {
+		server_info->security_status = DNS_CLIENT_SERVER_SECURITY_INSECURE;
+	}
+
 	return 0;
 
 errout:
@@ -971,6 +982,8 @@ errout:
 		X509_free(cert);
 	}
 
+	server_info->security_status = DNS_CLIENT_SERVER_SECURITY_VERIFY_FAILED;
+
 	return -1;
 }
 
@@ -1049,7 +1062,7 @@ int _dns_client_process_tls(struct dns_server_info *server_info, struct epoll_ev
 
 	if (server_info->type == DNS_SERVER_QUIC || server_info->type == DNS_SERVER_HTTP3) {
 /* QUIC */
-#if defined(OSSL_QUIC1_VERSION) && !defined (OPENSSL_NO_QUIC)
+#if defined(OSSL_QUIC1_VERSION) && !defined(OPENSSL_NO_QUIC)
 		return _dns_client_process_quic(server_info, event, now);
 #else
 		tlog(TLOG_ERROR, "quic/http3 is not supported.");

+ 1 - 0
src/dns_client/client_udp.c

@@ -139,6 +139,7 @@ int _dns_client_create_socket_udp(struct dns_server_info *server_info)
 
 	server_info->fd = fd;
 	server_info->status = DNS_SERVER_STATUS_CONNECTING;
+	server_info->security_status = DNS_CLIENT_SERVER_SECURITY_NOT_APPLICABLE;
 
 	if (connect(fd, &server_info->addr, server_info->ai_addrlen) != 0) {
 		if (errno != EINPROGRESS) {

+ 2 - 0
src/dns_client/dns_client.h

@@ -130,6 +130,8 @@ struct dns_server_info {
 
 	struct dns_server_stats stats;
 	struct list_head conn_stream_list;
+
+	dns_server_security_status security_status;
 };
 
 struct dns_server_pending_group {

+ 12 - 1
src/dns_client/server_info.c

@@ -19,12 +19,12 @@
 #define _GNU_SOURCE
 
 #include "server_info.h"
+#include "client_socket.h"
 #include "client_tls.h"
 #include "conn_stream.h"
 #include "ecs.h"
 #include "group.h"
 #include "pending_server.h"
-#include "client_socket.h"
 
 #include "smartdns/fast_ping.h"
 #include "smartdns/lib/stringutil.h"
@@ -52,6 +52,15 @@ const char *dns_client_get_server_ip(struct dns_server_info *server_info)
 	return server_info->ip;
 }
 
+dns_server_security_status dns_client_get_server_security_status(struct dns_server_info *server_info)
+{
+	if (server_info == NULL) {
+		return DNS_CLIENT_SERVER_SECURITY_UNKNOW;
+	}
+
+	return server_info->security_status;
+}
+
 const char *dns_client_get_server_host(struct dns_server_info *server_info)
 {
 	if (server_info == NULL) {
@@ -413,6 +422,8 @@ int _dns_client_server_add(const char *server_ip, const char *server_host, int p
 	server_info->prohibit = 0;
 	server_info->so_mark = flags->set_mark;
 	server_info->drop_packet_latency_ms = flags->drop_packet_latency_ms;
+	server_info->security_status = DNS_CLIENT_SERVER_SECURITY_UNKNOW;
+
 	atomic_set(&server_info->refcnt, 0);
 	atomic_set(&server_info->is_alive, default_is_alive);
 	INIT_LIST_HEAD(&server_info->check_list);

+ 5 - 0
src/dns_plugin.c

@@ -483,6 +483,11 @@ int smartdns_plugin_log_getlevel(void)
 	return tlog_getlevel();
 }
 
+int smartdns_plugin_is_audit_enabled(void)
+{
+	return dns_conf.audit_enable;
+}
+
 const char *smartdns_plugin_get_config(const char *key)
 {
 	return dns_conf_get_plugin_conf(key);

+ 12 - 3
src/dns_server/answer.c

@@ -240,8 +240,17 @@ static int _dns_server_process_answer_HTTPS(struct dns_rrs *rrs, struct dns_requ
 	struct dns_https_param *p = NULL;
 	int priority = 0;
 	struct dns_request_https *https_svcb;
+	int no_ipv4 = 0;
+	int no_ipv6 = 0;
+	int no_ech = 0;
 	struct dns_https_record_rule *https_record_rule = _dns_server_get_dns_rule(request, DOMAIN_RULE_HTTPS);
 
+	if (https_record_rule) {
+		no_ipv4 = https_record_rule->filter.no_ipv4hint;
+		no_ipv6 = https_record_rule->filter.no_ipv6hint;
+		no_ech = https_record_rule->filter.no_ech;
+	}
+
 	ret = dns_get_HTTPS_svcparm_start(rrs, &p, name, DNS_MAX_CNAME_LEN, &ttl, &priority, target, DNS_MAX_CNAME_LEN);
 	if (ret != 0) {
 		tlog(TLOG_WARN, "get HTTPS svcparm failed");
@@ -279,7 +288,7 @@ static int _dns_server_process_answer_HTTPS(struct dns_rrs *rrs, struct dns_requ
 		} break;
 		case DNS_HTTPS_T_IPV4HINT: {
 			struct dns_rule_address_IPV4 *address_ipv4 = NULL;
-			if (_dns_server_is_return_soa_qtype(request, DNS_T_A) || (https_record_rule && https_record_rule->filter.no_ipv4hint)) {
+			if (_dns_server_is_return_soa_qtype(request, DNS_T_A) || no_ipv4 == 1) {
 				break;
 			}
 
@@ -300,7 +309,7 @@ static int _dns_server_process_answer_HTTPS(struct dns_rrs *rrs, struct dns_requ
 			}
 		} break;
 		case DNS_HTTPS_T_ECH: {
-			if (https_record_rule && https_record_rule->filter.no_ech) {
+			if (no_ech == 1) {
 				break;
 			}
 
@@ -314,7 +323,7 @@ static int _dns_server_process_answer_HTTPS(struct dns_rrs *rrs, struct dns_requ
 		case DNS_HTTPS_T_IPV6HINT: {
 			struct dns_rule_address_IPV6 *address_ipv6 = NULL;
 
-			if (_dns_server_is_return_soa_qtype(request, DNS_T_AAAA) || (https_record_rule && https_record_rule->filter.no_ipv6hint)) {
+			if (_dns_server_is_return_soa_qtype(request, DNS_T_AAAA) || no_ipv6 == 1) {
 				break;
 			}
 

+ 10 - 0
src/include/smartdns/dns_client.h

@@ -57,6 +57,14 @@ typedef enum dns_result_type {
 	DNS_QUERY_END,
 } dns_result_type;
 
+typedef enum dns_server_security_status {
+	DNS_CLIENT_SERVER_SECURITY_UNKNOW = 0,
+	DNS_CLIENT_SERVER_SECURITY_NOT_APPLICABLE = 1,
+	DNS_CLIENT_SERVER_SECURITY_VERIFY_FAILED = 2,
+	DNS_CLIENT_SERVER_SECURITY_INSECURE = 3,
+	DNS_CLIENT_SERVER_SECURITY_SECURE = 4,
+} dns_server_security_status;
+
 #define DNSSERVER_FLAG_BLACKLIST_IP (0x1 << 0)
 #define DNSSERVER_FLAG_WHITELIST_IP (0x1 << 1)
 #define DNSSERVER_FLAG_CHECK_EDNS (0x1 << 2)
@@ -85,6 +93,8 @@ unsigned int dns_client_server_result_flag(struct dns_server_info *server_info);
 
 const char *dns_client_get_server_ip(struct dns_server_info *server_info);
 
+dns_server_security_status dns_client_get_server_security_status(struct dns_server_info *server_info);
+
 const char *dns_client_get_server_host(struct dns_server_info *server_info);
 
 int dns_client_get_server_port(struct dns_server_info *server_info);

+ 2 - 0
src/include/smartdns/dns_plugin.h

@@ -77,6 +77,8 @@ void smartdns_plugin_log_setlevel(smartdns_log_level level);
 
 int smartdns_plugin_log_getlevel(void);
 
+int smartdns_plugin_is_audit_enabled(void);
+
 const char *smartdns_plugin_get_config(const char *key);
 
 void smartdns_plugin_clear_all_config(void);