From 24b5784f027b04ccdb7d34b89a87e204b97bc22b Mon Sep 17 00:00:00 2001
From: "Kevin P. Fleming" <kevin@km6g.us>
Date: Tue, 7 Feb 2023 05:24:23 -0500
Subject: [PATCH 1/2] Generate distinct log messages for regex vs. IP
 blacklisting.

When an icon will not be downloaded due to matching a configured
blacklist, ensure that the log message indicates the type of blacklist
that was matched.
---
 src/api/icons.rs | 30 ++++++++++++++++++++----------
 1 file changed, 20 insertions(+), 10 deletions(-)

diff --git a/src/api/icons.rs b/src/api/icons.rs
index 23d122f1..f1b1ee70 100644
--- a/src/api/icons.rs
+++ b/src/api/icons.rs
@@ -79,7 +79,7 @@ async fn icon_redirect(domain: &str, template: &str) -> Option<Redirect> {
         return None;
     }
 
-    if is_domain_blacklisted(domain).await {
+    if check_domain_blacklist_reason(domain).await.is_some() {
         return None;
     }
 
@@ -258,9 +258,15 @@ mod tests {
     }
 }
 
+#[derive(Debug, Clone)]
+enum DomainBlacklistReason {
+    Regex,
+    IP,
+}
+
 use cached::proc_macro::cached;
-#[cached(key = "String", convert = r#"{ domain.to_string() }"#, size = 16, time = 60)]
-async fn is_domain_blacklisted(domain: &str) -> bool {
+#[cached(key = "String", convert = r#"{ domain.to_string() }"#, size = 16, time = 60, option = true)]
+async fn check_domain_blacklist_reason(domain: &str) -> Option<DomainBlacklistReason> {
     // First check the blacklist regex if there is a match.
     // This prevents the blocked domain(s) from being leaked via a DNS lookup.
     if let Some(blacklist) = CONFIG.icon_blacklist_regex() {
@@ -284,7 +290,7 @@ async fn is_domain_blacklisted(domain: &str) -> bool {
 
         if is_match {
             debug!("Blacklisted domain: {} matched ICON_BLACKLIST_REGEX", domain);
-            return true;
+            return Some(DomainBlacklistReason::Regex);
         }
     }
 
@@ -293,13 +299,13 @@ async fn is_domain_blacklisted(domain: &str) -> bool {
             for addr in s {
                 if !is_global(addr.ip()) {
                     debug!("IP {} for domain '{}' is not a global IP!", addr.ip(), domain);
-                    return true;
+                    return Some(DomainBlacklistReason::IP);
                 }
             }
         }
     }
 
-    false
+    None
 }
 
 async fn get_icon(domain: &str) -> Option<(Vec<u8>, String)> {
@@ -564,8 +570,10 @@ async fn get_page(url: &str) -> Result<Response, Error> {
 }
 
 async fn get_page_with_referer(url: &str, referer: &str) -> Result<Response, Error> {
-    if is_domain_blacklisted(url::Url::parse(url).unwrap().host_str().unwrap_or_default()).await {
-        warn!("Favicon '{}' resolves to a blacklisted domain or IP!", url);
+    match check_domain_blacklist_reason(url::Url::parse(url).unwrap().host_str().unwrap_or_default()).await {
+        Some(DomainBlacklistReason::Regex) => warn!("Favicon '{}' is from a blacklisted domain!", url),
+        Some(DomainBlacklistReason::IP) => warn!("Favicon '{}' is hosted on a non-global IP!", url),
+        None => (),
     }
 
     let mut client = CLIENT.get(url);
@@ -659,8 +667,10 @@ fn parse_sizes(sizes: &str) -> (u16, u16) {
 }
 
 async fn download_icon(domain: &str) -> Result<(Bytes, Option<&str>), Error> {
-    if is_domain_blacklisted(domain).await {
-        err_silent!("Domain is blacklisted", domain)
+    match check_domain_blacklist_reason(domain).await {
+        Some(DomainBlacklistReason::Regex) => err_silent!("Domain is blacklisted", domain),
+        Some(DomainBlacklistReason::IP) => err_silent!("Host resolves to a non-global IP", domain),
+        None => (),
     }
 
     let icon_result = get_icon_url(domain).await?;

From 6741b2590709957dffcd16d7a04b4ebdbaa92c1d Mon Sep 17 00:00:00 2001
From: "Kevin P. Fleming" <kevin@km6g.us>
Date: Tue, 7 Feb 2023 05:54:06 -0500
Subject: [PATCH 2/2] Ensure that all results from
 check_domain_blacklist_reason are cached.

---
 src/api/icons.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/api/icons.rs b/src/api/icons.rs
index f1b1ee70..9bff0162 100644
--- a/src/api/icons.rs
+++ b/src/api/icons.rs
@@ -265,7 +265,7 @@ enum DomainBlacklistReason {
 }
 
 use cached::proc_macro::cached;
-#[cached(key = "String", convert = r#"{ domain.to_string() }"#, size = 16, time = 60, option = true)]
+#[cached(key = "String", convert = r#"{ domain.to_string() }"#, size = 16, time = 60)]
 async fn check_domain_blacklist_reason(domain: &str) -> Option<DomainBlacklistReason> {
     // First check the blacklist regex if there is a match.
     // This prevents the blocked domain(s) from being leaked via a DNS lookup.