Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 61 additions & 57 deletions src/dns.rs → src/dns/darwin.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,78 @@
use std::collections::HashMap;
use std::io::Error;
use std::process::Command;
use super::DNSManagerTrait;

pub struct DNSManager {
service_dns: HashMap<String, String>,
service_dns_search: HashMap<String, String>,
}

impl DNSManager {
pub fn new() -> DNSManager {
impl DNSManagerTrait for DNSManager {
fn new() -> DNSManager {
DNSManager {
service_dns: HashMap::new(),
service_dns_search: HashMap::new(),
}
}

fn set_dns(&mut self, dns_servers: Vec<&str>, dns_search: Vec<&str>) -> Result<(), Error> {
if dns_servers.is_empty() {
return Ok(());
}
match self.collect_new_service_dns() {
Err(e) => return Err(e),
_ => {}
}

for service in self.service_dns.keys() {
Command::new("networksetup")
.arg("-setdnsservers")
.arg(service)
.args(&dns_servers)
.status()?;

if !dns_search.is_empty() {
Command::new("networksetup")
.arg("-setsearchdomains")
.arg(service)
.args(&dns_search)
.status()?;
}
log::debug!("DNS seted for {} with {}", service, dns_servers.join(","));
}

Ok(())
}

fn restore_dns(&self) -> Result<(), Error> {
for (service, dns) in &self.service_dns {
Command::new("networksetup")
.arg("-setdnsservers")
.arg(service)
.args(dns.lines())
.status()?;

log::debug!("DNS server reseted for {} with {}", service, dns);
}
for (service, search_domain) in &self.service_dns_search {
Command::new("networksetup")
.arg("-setsearchdomains")
.arg(service)
.args(search_domain.lines())
.status()?;
log::debug!(
"DNS search domain reseted for {} with {}",
service,
search_domain
)
}
log::debug!("DNS reseted");
Ok(())
}
}

impl DNSManager {
fn collect_new_service_dns(&mut self) -> Result<(), Error> {
let output = Command::new("networksetup")
.arg("-listallnetworkservices")
Expand Down Expand Up @@ -75,58 +133,4 @@ impl DNSManager {
}
Ok(())
}

pub fn set_dns(&mut self, dns_servers: Vec<&str>, dns_search: Vec<&str>) -> Result<(), Error> {
if dns_servers.is_empty() {
return Ok(());
}
match self.collect_new_service_dns() {
Err(e) => return Err(e),
_ => {}
}
for service in self.service_dns.keys() {
Command::new("networksetup")
.arg("-setdnsservers")
.arg(service)
.args(&dns_servers)
.status()?;

if !dns_search.is_empty() {
Command::new("networksetup")
.arg("-setsearchdomains")
.arg(service)
.args(&dns_search)
.status()?;
}
log::debug!("DNS seted for {} with {}", service, dns_servers.join(","));
}

Ok(())
}

pub fn restore_dns(&self) -> Result<(), Error> {
for (service, dns) in &self.service_dns {
Command::new("networksetup")
.arg("-setdnsservers")
.arg(service)
.args(dns.lines())
.status()?;

log::debug!("DNS server reseted for {} with {}", service, dns);
}
for (service, search_domain) in &self.service_dns_search {
Command::new("networksetup")
.arg("-setsearchdomains")
.arg(service)
.args(search_domain.lines())
.status()?;
log::debug!(
"DNS search domain reseted for {} with {}",
service,
search_domain
)
}
log::debug!("DNS reseted");
Ok(())
}
}
}
106 changes: 106 additions & 0 deletions src/dns/linux.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
use std::io::Error;
use std::process::Command;
use super::DNSManagerTrait;

pub struct DNSManager {
interface: String,
original_dns: Option<String>,
original_search: Option<String>,
}

impl DNSManagerTrait for DNSManager {
fn new() -> DNSManager {
DNSManager {
interface: String::new(),
original_dns: None,
original_search: None,
}
}

fn set_dns(&mut self, dns_servers: Vec<&str>, dns_search: Vec<&str>) -> Result<(), Error> {
if dns_servers.is_empty() || self.interface.is_empty() {
return Ok(());
}

// Store current DNS settings before changing them
let status = Command::new("resolvectl")
.arg("status")
.arg(&self.interface)
.output()?;
let output = String::from_utf8_lossy(&status.stdout);

// Parse and store original DNS servers and search domains
for line in output.lines() {
if line.contains("DNS Servers:") {
self.original_dns = Some(line.split(':').nth(1).unwrap_or("").trim().to_string());
} else if line.contains("DNS Domain:") {
self.original_search = Some(line.split(':').nth(1).unwrap_or("").trim().to_string());
}
}

// Set new DNS servers
Command::new("resolvectl")
.arg("dns")
.arg(&self.interface)
.args(dns_servers.clone())
.status()?;

// Set new search domains if provided
if !dns_search.is_empty() {
Command::new("resolvectl")
.arg("domain")
.arg(&self.interface)
.args(dns_search)
.status()?;
}

log::debug!(
"DNS set for interface {} with servers: {}",
self.interface,
dns_servers.join(",")
);

Ok(())
}

fn restore_dns(&self) -> Result<(), Error> {
if self.interface.is_empty() {
return Ok(());
}

// Restore original DNS servers if they were saved
if let Some(dns) = &self.original_dns {
if !dns.is_empty() {
Command::new("resolvectl")
.arg("dns")
.arg(&self.interface)
.args(dns.split_whitespace())
.status()?;
}
}

// Restore original search domains if they were saved
if let Some(search) = &self.original_search {
if !search.is_empty() {
Command::new("resolvectl")
.arg("domain")
.arg(&self.interface)
.args(search.split_whitespace())
.status()?;
}
}

log::debug!("DNS settings restored for interface {}", self.interface);
Ok(())
}
}

impl DNSManager {
pub fn with_interface(interface: String) -> Self {
DNSManager {
interface,
original_dns: None,
original_search: None,
}
}
}
22 changes: 22 additions & 0 deletions src/dns/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use std::io::Error;

#[cfg(target_os = "macos")]
mod darwin;
#[cfg(target_os = "macos")]
pub use darwin::DNSManager;

#[cfg(target_os = "linux")]
mod linux;
#[cfg(target_os = "linux")]
pub use linux::DNSManager;

#[cfg(target_os = "windows")]
mod win;
#[cfg(target_os = "windows")]
pub use win::DNSManager;

pub trait DNSManagerTrait {
fn new() -> Self where Self: Sized;
fn set_dns(&mut self, dns_servers: Vec<&str>, dns_search: Vec<&str>) -> Result<(), Error>;
fn restore_dns(&self) -> Result<(), Error>;
}
114 changes: 114 additions & 0 deletions src/dns/win.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
use super::DNSManagerTrait;
use std::io::Error;
use std::process::Command;

pub struct DNSManager {
interface_name: String,
original_dns: Option<Vec<String>>,
}

impl DNSManagerTrait for DNSManager {
fn new() -> Self {
Self {
interface_name: String::new(),
original_dns: None,
}
}

fn set_dns(&mut self, dns_servers: Vec<&str>, dns_search: Vec<&str>) -> Result<(), Error> {
if !dns_search.is_empty() {
log::warn!("DNS search domains are not supported on Windows");
}

// First, backup current DNS settings
let output = Command::new("netsh")
.args(["interface", "ipv4", "show", "dns", &self.interface_name])
.output()?;

if output.status.success() {
self.original_dns = Some(Vec::new());
}

// First, clear any existing DNS servers
let status = Command::new("netsh")
.args([
"interface",
"ipv4",
"set",
"dnsservers",
&self.interface_name,
"source=static",
"address=none",
])
.status()?;

if !status.success() {
return Err(Error::new(
std::io::ErrorKind::Other,
"Failed to clear existing DNS servers",
));
}

// Set new DNS servers
for (i, &dns) in dns_servers.iter().enumerate() {
let server_type = if i == 0 {
"source=static"
} else {
"source=static validate=no"
};

let status = Command::new("netsh")
.args([
"interface",
"ipv4",
"add",
"dnsserver",
&self.interface_name,
&format!("addr={}", dns),
server_type,
])
.status()?;

if !status.success() {
return Err(Error::new(
std::io::ErrorKind::Other,
format!("Failed to set DNS server {}", dns),
));
}
}

Ok(())
}

fn restore_dns(&self) -> Result<(), Error> {
// Reset DNS servers to DHCP
let status = Command::new("netsh")
.args([
"interface",
"ipv4",
"set",
"dnsservers",
&self.interface_name,
"source=dhcp",
])
.status()?;

if !status.success() {
return Err(Error::new(
std::io::ErrorKind::Other,
"Failed to restore DNS settings",
));
}

Ok(())
}
}

impl DNSManager {
pub fn with_interface(interface_name: String) -> Self {
Self {
interface_name,
original_dns: None,
}
}
}
Loading