diff options
| author | Emīls <emils@mullvad.net> | 2020-11-04 17:47:40 +0000 |
|---|---|---|
| committer | Emīls <emils@mullvad.net> | 2020-11-05 12:28:55 +0000 |
| commit | 8a5c92363c19d79b25a8da5a443a529737112b76 (patch) | |
| tree | 8ca47d68e68fad3dba467cc3b0354dfde709d1dd | |
| parent | bc353efc368c32d9957d700885f75b3ad3a3e5dd (diff) | |
| download | mullvadvpn-8a5c92363c19d79b25a8da5a443a529737112b76.tar.xz mullvadvpn-8a5c92363c19d79b25a8da5a443a529737112b76.zip | |
Prefer systemd-resolved over NM if it's working
| -rw-r--r-- | CHANGELOG.md | 2 | ||||
| -rw-r--r-- | talpid-core/src/dns/linux/network_manager.rs | 74 | ||||
| -rw-r--r-- | talpid-core/src/dns/linux/systemd_resolved.rs | 9 |
3 files changed, 75 insertions, 10 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f6d2b365a..38cb16bd10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -80,6 +80,8 @@ Line wrap the file at 100 chars. Th - Stop reconnecting when using WireGuard and NetworkManager. - Apply DNS config quicker when managing DNS via NetworkManager. - Fix routing rules sometimes being duplicated. +- When NetworkManager is managing /etc/resolv.conf but ultimately using systemd-resolved, use + systemd-resolved directly to manage DNS. ### Security diff --git a/talpid-core/src/dns/linux/network_manager.rs b/talpid-core/src/dns/linux/network_manager.rs index e2695e5678..20e69f03d4 100644 --- a/talpid-core/src/dns/linux/network_manager.rs +++ b/talpid-core/src/dns/linux/network_manager.rs @@ -127,10 +127,7 @@ impl NetworkManager { _ => (), }; - - let expected_resolv_conf = "/var/run/NetworkManager/resolv.conf"; - let actual_resolv_conf = "/etc/resolv.conf"; - if !eq_file_content(&expected_resolv_conf, &actual_resolv_conf) { + if !verify_etc_resolv_conf_contents() { log::debug!("/etc/resolv.conf differs from reference resolv.conf, therefore NM is not managing DNS"); return Err(Error::NetworkManagerNotManagingDns); } @@ -412,6 +409,69 @@ impl NetworkManager { } } +/// Given a DBus connection, verify that NM is up and running and capable of managing DNS via +/// systemd-resolved. This includes verifying that NM is managing DNS via systemd-resolved and is +/// controlling /etc/resolv.conf +pub fn is_nm_managing_via_resolved(connection: &Connection) -> bool { + let check_nm = || -> Result<bool> { + let dns_mode: String = connection + .with_path(NM_BUS, NM_DNS_MANAGER_PATH, RPC_TIMEOUT_MS) + .get(NM_DNS_MANAGER, DNS_MODE_KEY) + .map_err(Error::Dbus)?; + if &dns_mode != "systemd-resolved" { + return Ok(false); + } + + + let rc_management_mode: String = connection + .with_path(NM_BUS, NM_DNS_MANAGER_PATH, RPC_TIMEOUT_MS) + .get(NM_DNS_MANAGER, RC_MANAGEMENT_MODE_KEY) + .map_err(Error::TooOldNetworkManager)?; + + match rc_management_mode.as_str() { + // /etc/resolv.conf is managed via executing a command, can't verify that works, have + // to assume it does + "resolvconf" | "netconfig" => Ok(true), + + "symlink" | "none" | "file" => { + let reload = + Message::new_method_call(NM_BUS, NM_OBJECT_PATH, NM_TOP_OBJECT, "Reload") + .map_err(Error::DbusMethodCall)? + .append1(0x02u32); + let _ = connection + .send_with_reply_and_block(reload, RPC_TIMEOUT_MS) + .map_err(Error::Dbus)?; + let result = verify_etc_resolv_conf_contents(); + Ok(result) + } + + // NM doesn't manage DNS at all + "unmanaged" => Ok(false), + unknown_rc_mode => { + log::error!("Unknown resolvconf management mode - {}", unknown_rc_mode); + Ok(false) + } + } + }; + match check_nm() { + Ok(result) => result, + Err(err) => { + log::error!( + "Failed to check if NM is managing DNS via systemd-resolved: {}", + err + ); + false + } + } +} + +// Verify that the contents of /etc/resolv.conf match what NM expectes them to be. +fn verify_etc_resolv_conf_contents() -> bool { + let expected_resolv_conf = "/var/run/NetworkManager/resolv.conf"; + let actual_resolv_conf = "/etc/resolv.conf"; + eq_file_content(&expected_resolv_conf, &actual_resolv_conf) +} + fn device_is_ready(device_state: u32) -> bool { /// Any state above `NM_DEVICE_STATE_IP_CONFIG` is considered to be an OK state to change the /// DNS config. For the enums, see https://developer.gnome.org/NetworkManager/stable/nm-dbus-types.html#NMDeviceState @@ -439,11 +499,11 @@ fn eq_file_content<P: AsRef<Path>>(a: &P, b: &P) -> bool { } }; - file_a + !file_a .lines() .zip(file_b.lines()) - .all(|(a, b)| match (a, b) { - (Ok(a), Ok(b)) => a == b, + .any(|(a, b)| match (a, b) { + (Ok(a), Ok(b)) => a != b, _ => false, }) } diff --git a/talpid-core/src/dns/linux/systemd_resolved.rs b/talpid-core/src/dns/linux/systemd_resolved.rs index bacd416382..6abdd4990e 100644 --- a/talpid-core/src/dns/linux/systemd_resolved.rs +++ b/talpid-core/src/dns/linux/systemd_resolved.rs @@ -94,16 +94,19 @@ impl SystemdResolved { }; systemd_resolved.ensure_resolved_exists()?; - Self::ensure_resolv_conf_is_resolved_symlink()?; + if !super::network_manager::is_nm_managing_via_resolved( + &systemd_resolved.dbus_connection, + ) { + Self::ensure_resolv_conf_is_resolved_symlink()?; + } Ok(systemd_resolved) })(); match &result { - Err(Error::NoSystemdResolved(_)) => (), + Ok(_) | Err(Error::NoSystemdResolved(_)) => (), Err(error) => { log::error!("systemd-resolved is not being used because: {}", error); } - Ok(_) => (), } |
