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
5 changes: 5 additions & 0 deletions i18n/en/cachyos_hello.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ dot-tooltip = Encrypt DNS queries using TLS for improved privacy (requires serve
enable-doh = Enable DNS over HTTPS (DoH)
doh-tooltip = Encrypt DNS queries using HTTPS via blocky local proxy (requires server support, installs blocky)
doh-blocky-install-failed = Failed to install blocky for DoH support!
enable-doq = Enable DNS over QUIC (DoQ)
doq-tooltip = Encrypt DNS queries using QUIC via blocky local proxy (requires server support, installs blocky)
doq-blocky-install-failed = Failed to install blocky for DoQ support!
test-latency = Test Latency of Selected Server
test-latency-tooltip = Measure network latency to the selected DNS server
best-server = Select Best Server by Latency
Expand All @@ -54,6 +57,8 @@ custom-dns-invalid = Please enter at least an IPv4 or IPv6 address
custom-dns-invalid-hostname = Invalid DoT hostname
custom-dns-doh-url = DoH URL (for DNS over HTTPS):
custom-dns-doh-url-required = Please enter a valid DoH URL starting with https://
custom-dns-doq-endpoint = DoQ endpoint (for DNS over QUIC):
custom-dns-doq-endpoint-required = Please enter a valid DoQ endpoint starting with quic: or quic://
dns-check-hint = After applying, verify your DNS provider at
dns-server-changed = DNS server was successfully changed!
dns-server-failed = Failed to set DNS server!
Expand Down
24 changes: 16 additions & 8 deletions src/actions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ pub fn change_dns_server(
}

pub fn reset_dns_server(conn_name: &str, dialog_tx: Sender<DialogMessage>) {
// Stop blocky if it was running (DoH mode)
// Stop blocky if it was running (DoH/DoQ mode)
stop_blocky();

let result = (|| -> anyhow::Result<()> {
Expand Down Expand Up @@ -185,24 +185,31 @@ pub fn reset_dns_server(conn_name: &str, dialog_tx: Sender<DialogMessage>) {
}
}

/// Set DNS to use `DoH` via blocky local proxy.
/// Set DNS to use an encrypted upstream via blocky local proxy.
/// Installs blocky if needed, writes its config, starts the service, and points NM to 127.0.0.1.
pub fn change_dns_server_doh(
#[allow(clippy::too_many_arguments)]
pub fn change_dns_server_blocky(
callback: RunCmdCallback,
conn_name: &str,
doh_url: &str,
mode: dns::BlockyMode,
upstream: &str,
bootstrap_ipv4: &str,
bootstrap_ipv6: &str,
dot_hostname: Option<&str>,
dialog_tx: Sender<DialogMessage>,
) {
let install_failed_msg = match mode {
dns::BlockyMode::Doh => fl!("doh-blocky-install-failed"),
dns::BlockyMode::Doq => fl!("doq-blocky-install-failed"),
};
Comment thread
ptr1337 marked this conversation as resolved.

// 1. Install blocky if not present
if !utils::is_alpm_pkg_installed("blocky") {
const ALPM_PACKAGE_NAMES: [&str; 1] = ["blocky"];
install_needed_packages(
callback,
&ALPM_PACKAGE_NAMES,
fl!("doh-blocky-install-failed"),
install_failed_msg,
Action::SetDnsServer,
dialog_tx.clone(),
);
Expand All @@ -212,7 +219,8 @@ pub fn change_dns_server_doh(
}

// 2. Generate and write blocky config
let config = dns::generate_blocky_config(doh_url, bootstrap_ipv4, bootstrap_ipv6, dot_hostname);
let config =
dns::generate_blocky_config(upstream, bootstrap_ipv4, bootstrap_ipv6, dot_hostname);

let write_result = (|| -> anyhow::Result<()> {
let mut tmp = tempfile::NamedTempFile::new()?;
Expand Down Expand Up @@ -272,13 +280,13 @@ pub fn change_dns_server_doh(
}
}

/// Stop blocky if it's running (used during reset or when switching away from `DoH`).
/// Stop blocky if it's running (used during reset or when switching away from encrypted DNS).
pub fn stop_blocky() {
let _ = systemd_units::systemd_stop(dns::BLOCKY_SERVICE, Scope::System);
let _ = systemd_units::systemd_disable(&[dns::BLOCKY_SERVICE], Scope::System);
}

/// Returns true if blocky is currently active.
/// Returns true if blocky encrypted DNS proxy is currently active.
pub fn is_blocky_active() -> bool {
systemd_units::systemd_is_active(dns::BLOCKY_SERVICE, Scope::System).unwrap_or(false)
}
Expand Down
197 changes: 134 additions & 63 deletions src/cli_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,51 +75,108 @@ pub fn handle_tweak_command(action: TweakAction) -> Result<()> {
}
}

fn blocky_mode_label(mode: dns::BlockyMode) -> &'static str {
match mode {
dns::BlockyMode::Doh => "DoH",
dns::BlockyMode::Doq => "DoQ",
}
}

fn apply_preset_blocky(
connection: &str,
server_name: &str,
server_addr: &dns::DnsEntry,
mode: dns::BlockyMode,
tx: async_channel::Sender<crate::ui::DialogMessage>,
) {
if let Some(upstream) = dns::get_blocky_upstream(server_name, mode) {
println!(
"Setting DNS for '{}' to '{}' ({} enabled via blocky)...",
connection.cyan(),
server_name.cyan(),
blocky_mode_label(mode),
);
actions::change_dns_server_blocky(
crate::cli::run_command,
connection,
mode,
upstream,
server_addr.0,
server_addr.1,
server_addr.2,
tx,
);
} else {
println!(
"{}: DNS over {} is not supported by '{}'.",
"Warning".yellow(),
blocky_mode_label(mode),
server_name
);
println!("Setting DNS without {}...", blocky_mode_label(mode));
actions::change_dns_server(
connection,
server_addr.0,
server_addr.1,
false,
server_addr.2.unwrap_or(""),
tx,
);
}
}

fn apply_custom_blocky(
connection: &str,
upstream: &str,
ipv4: &str,
ipv6: &str,
dot_hostname: &str,
mode: dns::BlockyMode,
tx: async_channel::Sender<crate::ui::DialogMessage>,
) {
let dot_suffix = if dot_hostname.is_empty() {
String::new()
} else {
format!(" DoT bootstrap={dot_hostname}")
};
println!(
"Setting custom {} DNS for '{}': upstream='{}' (bootstrap: IPv4='{}' IPv6='{}'{})...",
blocky_mode_label(mode),
connection.cyan(),
upstream.cyan(),
if ipv4.is_empty() { "(none)" } else { ipv4 },
if ipv6.is_empty() { "(none)" } else { ipv6 },
dot_suffix,
);
let dot_host = if dot_hostname.is_empty() { None } else { Some(dot_hostname) };
actions::change_dns_server_blocky(
crate::cli::run_command,
connection,
mode,
upstream,
ipv4,
ipv6,
dot_host,
tx,
);
}

pub fn handle_dns_command(action: DnsAction) -> Result<()> {
let (tx, rx) = async_channel::unbounded();

match action {
DnsAction::Set { connection, server, dot, doh } => {
DnsAction::Set { connection, server, dot, doh, doq } => {
let server_name = server.as_str();
let server_addr = dns::G_DNS_SERVERS.get(server_name).unwrap();

if doh {
// DoH mode via blocky
let doh_url = dns::get_doh_url(server_name);
if let Some(url) = doh_url {
println!(
"Setting DNS for '{}' to '{}' (DoH enabled via blocky)...",
connection.cyan(),
server_name.cyan(),
);
actions::change_dns_server_doh(
crate::cli::run_command,
&connection,
url,
server_addr.0,
server_addr.1,
server_addr.2,
tx,
);
} else {
println!(
"{}: DNS over HTTPS is not supported by '{}'.",
"Warning".yellow(),
server_name
);
println!("Setting DNS without DoH...");
let dot_hostname = server_addr.2.unwrap_or("");
actions::change_dns_server(
&connection,
server_addr.0,
server_addr.1,
false,
dot_hostname,
tx,
);
}
if let Some(mode) = match (doh, doq) {
(true, _) => Some(dns::BlockyMode::Doh),
(_, true) => Some(dns::BlockyMode::Doq),
_ => None,
} {
apply_preset_blocky(&connection, server_name, server_addr, mode, tx);
} else {
// Stop blocky if switching away from DoH
// Stop blocky if switching away from encrypted DNS
actions::stop_blocky();
let dot_supported = server_addr.2.is_some();

Expand Down Expand Up @@ -151,7 +208,17 @@ pub fn handle_dns_command(action: DnsAction) -> Result<()> {
);
}
},
DnsAction::SetCustom { connection, ipv4, ipv6, dot, dot_hostname, doh, doh_url } => {
DnsAction::SetCustom {
connection,
ipv4,
ipv6,
dot,
dot_hostname,
doh,
doh_url,
doq,
doq_endpoint,
} => {
if ipv4.is_empty() && ipv6.is_empty() {
eprintln!("{}: At least one of --ipv4 or --ipv6 must be provided.", "Error".red());
std::process::exit(1);
Expand All @@ -162,36 +229,38 @@ pub fn handle_dns_command(action: DnsAction) -> Result<()> {
}

if doh {
if doh_url.is_empty() || !doh_url.starts_with("https://") {
if !dns::is_valid_doh_url(&doh_url) {
eprintln!("{}: --doh-url must be a valid https:// URL.", "Error".red());
std::process::exit(1);
}
println!(
"Setting custom DoH DNS for '{}': URL='{}' (bootstrap: IPv4='{}' \
IPv6='{}'{})...",
connection.cyan(),
doh_url.cyan(),
if ipv4.is_empty() { "(none)" } else { &ipv4 },
if ipv6.is_empty() { "(none)" } else { &ipv6 },
if dot_hostname.is_empty() {
String::new()
} else {
format!(" DoT bootstrap={dot_hostname}")
},
);
let dot_host =
if dot_hostname.is_empty() { None } else { Some(dot_hostname.as_str()) };
actions::change_dns_server_doh(
crate::cli::run_command,
apply_custom_blocky(
&connection,
&doh_url,
&ipv4,
&ipv6,
dot_host,
&dot_hostname,
dns::BlockyMode::Doh,
tx,
);
} else if doq {
if !dns::is_valid_doq_endpoint(&doq_endpoint) {
eprintln!(
"{}: --doq-endpoint must start with quic: or quic://.",
"Error".red()
);
std::process::exit(1);
}
apply_custom_blocky(
&connection,
&doq_endpoint,
&ipv4,
&ipv6,
&dot_hostname,
dns::BlockyMode::Doq,
tx,
);
} else {
// Stop blocky if switching away from DoH
// Stop blocky if switching away from encrypted DNS
actions::stop_blocky();
let dot_label = if dot { " (DoT enabled)" } else { "" };
println!(
Expand Down Expand Up @@ -231,15 +300,17 @@ pub fn handle_dns_command(action: DnsAction) -> Result<()> {
Some(host) => format!(" [DoT: {host}]"),
None => String::new(),
};
let doh_info = match dns::get_doh_url(name) {
Some(url) => format!(" [DoH: {url}]"),
None => String::new(),
};
let doh_info = dns::get_blocky_upstream(name, dns::BlockyMode::Doh)
.map(|url| format!(" [DoH: {url}]"))
.unwrap_or_default();
let doq_info = dns::get_blocky_upstream(name, dns::BlockyMode::Doq)
.map(|endpoint| format!(" [DoQ: {endpoint}]"))
.unwrap_or_default();
let region_info = match dns::G_DNS_SERVER_INFO.get(name) {
Some(info) => format!(" ({} - {})", info.region, info.homepage),
None => String::new(),
};
println!("- {name}{dot_info}{doh_info}{region_info}");
println!("- {name}{dot_info}{doh_info}{doq_info}{region_info}");
}
},
DnsAction::TestLatency => {
Expand Down
Loading
Loading