Răsfoiți Sursa

fix and refactor default nameserver selection on windows

Zoritle 4 ani în urmă
părinte
comite
8e90376889
1 a modificat fișierele cu 63 adăugiri și 22 ștergeri
  1. 63 22
      src/resolve.rs

+ 63 - 22
src/resolve.rs

@@ -1,17 +1,18 @@
 //! Specifying the address of the DNS server to send requests to.
 
-use std::io;
+use std::{
+    io,
+    net::{IpAddr, UdpSocket},
+};
 
 use log::*;
 
 use dns::Labels;
 
-
 /// A **resolver** knows the address of the server we should
 /// send DNS requests to, and the search list for name lookup.
 #[derive(PartialEq, Debug)]
 pub struct Resolver {
-
     /// The address of the name server.
     pub nameserver: String,
 
@@ -20,19 +21,24 @@ pub struct Resolver {
 }
 
 impl Resolver {
-
     /// Returns a resolver with the specified nameserver and an empty
     /// search list.
     pub fn specified(nameserver: String) -> Self {
         let search_list = Vec::new();
-        Self { nameserver, search_list }
+        Self {
+            nameserver,
+            search_list,
+        }
     }
 
     /// Returns a resolver that is default for the system.
     pub fn system_default() -> Self {
         let (nameserver_opt, search_list) = system_nameservers().expect("Failed to get nameserver");
         let nameserver = nameserver_opt.expect("No nameserver found");
-        Self { nameserver, search_list }
+        Self {
+            nameserver,
+            search_list,
+        }
     }
 
     /// Returns a nameserver that queries should be sent to.
@@ -59,15 +65,14 @@ impl Resolver {
     }
 }
 
-
 /// Looks up the system default nameserver on Unix, by querying
 /// `/etc/resolv.conf` and returning the first line that specifies one.
 /// Returns an error if there’s a problem reading the file, or `None` if no
 /// nameserver is specified in the file.
 #[cfg(unix)]
 fn system_nameservers() -> io::Result<(Option<String>, Vec<String>)> {
-    use std::io::{BufRead, BufReader};
     use std::fs::File;
+    use std::io::{BufRead, BufReader};
 
     let f = File::open("/etc/resolv.conf")?;
     let reader = BufReader::new(f);
@@ -83,7 +88,7 @@ fn system_nameservers() -> io::Result<(Option<String>, Vec<String>)> {
 
             match ip {
                 Ok(_ip) => nameservers.push(nameserver_str.into()),
-                Err(e)  => warn!("Failed to parse nameserver line {:?}: {}", line, e),
+                Err(e) => warn!("Failed to parse nameserver line {:?}: {}", line, e),
             }
         }
 
@@ -96,7 +101,6 @@ fn system_nameservers() -> io::Result<(Option<String>, Vec<String>)> {
     Ok((nameservers.first().cloned(), search_list))
 }
 
-
 /// Looks up the system default nameserver on Windows, by iterating through
 /// the list of network adapters and returning the first nameserver it finds.
 #[cfg(windows)]
@@ -108,24 +112,47 @@ fn system_nameservers() -> io::Result<(Option<String>, Vec<String>)> {
             return Ok((None, Vec::new()));
         }
     };
-
-    for adapter in adapters.iter().filter(|a| {
+    // According to the specification, prefer ipv6 by default.
+    // TODO: add control flag to select an ip family.
+    #[allow(dead_code)]
+    #[derive(Debug, PartialEq)]
+    enum ForceIPFamily {
+        V4,
+        V6,
+        None,
+    }
+    let force_ip_family: ForceIPFamily = ForceIPFamily::None;
+    let ip = match force_ip_family {
+        ForceIPFamily::V4 => get_ipv4().ok(),
+        ForceIPFamily::V6 => get_ipv6().ok(),
+        ForceIPFamily::None => get_ipv6().or(get_ipv4()).ok(),
+    };
+    let active_adapters = adapters.iter().filter(|a| {
         a.oper_status() == ipconfig::OperStatus::IfOperStatusUp && !a.gateways().is_empty()
-    }) {
-        for dns_server in adapter.dns_servers().iter() {
-            // TODO: This will need to be changed for IPv6 support.
-            if dns_server.is_ipv4() {
-                debug!("Found first nameserver {:?}", dns_server);
-                return Ok((Some(dns_server.to_string()), Vec::new()));
-            }
-        }
+    });
+    if let Some(dns_server) = active_adapters
+        .clone()
+        .find(|a| ip.map(|ip| a.ip_addresses().contains(&ip)).unwrap_or(false))
+        .map(|a| a.dns_servers().first())
+        .flatten()
+    {
+        debug!("Found first nameserver {:?}", dns_server);
+        // TODO: Implement dns suffix search list on Windows
+        return Ok((Some(dns_server.to_string()), Vec::new()));
+    }
+    // Fallback
+    if let Some(dns_server) = active_adapters
+        .flat_map(|a| a.dns_servers())
+        .find(|d| (d.is_ipv4() && force_ip_family != ForceIPFamily::V6) || d.is_ipv6())
+    {
+        debug!("Found first fallback nameserver {:?}", dns_server);
+        return Ok((Some(dns_server.to_string()), Vec::new()));
     }
 
     warn!("No nameservers available");
-    return Ok((None, Vec::new()))
+    return Ok((None, Vec::new()));
 }
 
-
 /// The fall-back system default nameserver determinator that is not very
 /// determined as it returns nothing without actually checking anything.
 #[cfg(all(not(unix), not(windows)))]
@@ -133,3 +160,17 @@ fn system_nameservers() -> io::Result<(Option<String>, Vec<String>)> {
     warn!("Unable to fetch default nameservers on this platform.");
     Ok((None, Vec::new()))
 }
+// get the IP of the Network adaptor that is used to access the Internet
+// https://stackoverflow.com/questions/24661022/getting-ip-adress-associated-to-real-hardware-ethernet-controller-in-windows-c
+fn get_ipv4() -> io::Result<IpAddr> {
+    let s = UdpSocket::bind("0.0.0.0:0")?;
+    s.connect("8.8.8.8:53")?;
+    let addr = s.local_addr()?;
+    Ok(addr.ip())
+}
+fn get_ipv6() -> io::Result<IpAddr> {
+    let s = UdpSocket::bind("[::1]:0")?;
+    s.connect("[2001:4860:4860::8888]:53")?;
+    let addr = s.local_addr()?;
+    Ok(addr.ip())
+}