Loading Android.bp +15 −36 Original line number Diff line number Diff line Loading @@ -326,24 +326,25 @@ filegroup { ], } rust_ffi_static { name: "libdoh_ffi", crate_name: "doh", srcs: ["doh/doh.rs"], edition: "2018", rlibs: [ doh_rust_deps = [ "libandroid_logger", "libanyhow", "libbase64_rust", "libfutures", "liblibc", "liblog_rust", "libquiche", "libring", "libtokio", "liburl", ], ] rust_ffi_static { name: "libdoh_ffi", crate_name: "doh", srcs: ["doh/doh.rs"], edition: "2018", rlibs: doh_rust_deps + ["libquiche"], prefer_rlib: true, shared_libs: [ Loading @@ -368,18 +369,7 @@ rust_test { // Used to enable root permission for the test. // TODO: remove after 'require_root' is supported in rust_test. test_config_template: ":resolv_rust_test_config_template", rustlibs: [ "libandroid_logger", "libanyhow", "libbase64_rust", "libfutures", "liblibc", "liblog_rust", "libquiche_static", "libring", "libtokio", "liburl", ], rustlibs: doh_rust_deps + ["libquiche_static"], min_sdk_version: "29", } Loading @@ -390,18 +380,7 @@ rust_ffi_static { srcs: ["doh/doh.rs"], edition: "2018", rlibs: [ "libandroid_logger", "libanyhow", "libbase64_rust", "libfutures", "liblibc", "liblog_rust", "libquiche_static", "libring", "libtokio", "liburl", ], rlibs: doh_rust_deps + ["libquiche_static"], prefer_rlib: true, // TODO(b/194022174), for unit tests to run on the Android 10 platform, // libunwind must be statically linked. Loading DnsResolver.cpp +1 −1 Original line number Diff line number Diff line Loading @@ -33,7 +33,7 @@ bool resolv_init(const ResolverNetdCallbacks* callbacks) { // TODO(b/170539625): restore log level to WARNING after clarifying flaky tests. const bool isDebug = isUserDebugBuild(); resolv_set_log_severity(isDebug ? android::base::DEBUG : android::base::WARNING); doh_init_logger(isDebug ? LOG_LEVEL_DEBUG : LOG_LEVEL_WARN); doh_init_logger(isDebug ? DOH_LOG_LEVEL_DEBUG : DOH_LOG_LEVEL_WARN); using android::net::gApiLevel; gApiLevel = getApiLevel(); using android::net::gResNetdCallbacks; Loading PrivateDnsConfiguration.cpp +1 −1 Original line number Diff line number Diff line Loading @@ -510,7 +510,7 @@ ssize_t PrivateDnsConfiguration::dohQuery(unsigned netId, const Slice query, con { std::lock_guard guard(mPrivateDnsLock); // It's safe because mDohDispatcher won't be deleted after initializing. if (mDohDispatcher == nullptr) return RESULT_CAN_NOT_SEND; if (mDohDispatcher == nullptr) return DOH_RESULT_CAN_NOT_SEND; } return doh_query(mDohDispatcher, netId, query.base(), query.size(), answer.base(), answer.size(), timeoutMs); Loading doh.h +11 −8 Original line number Diff line number Diff line Loading @@ -26,28 +26,28 @@ #include <sys/types.h> /// The return code of doh_query means that there is no answer. static const ssize_t RESULT_INTERNAL_ERROR = -1; static const ssize_t DOH_RESULT_INTERNAL_ERROR = -1; /// The return code of doh_query means that query can't be sent. static const ssize_t RESULT_CAN_NOT_SEND = -2; static const ssize_t DOH_RESULT_CAN_NOT_SEND = -2; /// The return code of doh_query to indicate that the query timed out. static const ssize_t RESULT_TIMEOUT = -255; static const ssize_t DOH_RESULT_TIMEOUT = -255; /// The error log level. static const uint32_t LOG_LEVEL_ERROR = 0; static const uint32_t DOH_LOG_LEVEL_ERROR = 0; /// The warning log level. static const uint32_t LOG_LEVEL_WARN = 1; static const uint32_t DOH_LOG_LEVEL_WARN = 1; /// The info log level. static const uint32_t LOG_LEVEL_INFO = 2; static const uint32_t DOH_LOG_LEVEL_INFO = 2; /// The debug log level. static const uint32_t LOG_LEVEL_DEBUG = 3; static const uint32_t DOH_LOG_LEVEL_DEBUG = 3; /// The trace log level. static const uint32_t LOG_LEVEL_TRACE = 4; static const uint32_t DOH_LOG_LEVEL_TRACE = 4; /// Context for a running DoH engine. struct DohDispatcher; Loading @@ -60,9 +60,12 @@ using TagSocketCallback = void (*)(int32_t sock); extern "C" { /// Performs static initialization for android logger. /// If an invalid level is passed, defaults to logging errors only. /// If called more than once, it will have no effect on subsequent calls. void doh_init_logger(uint32_t level); /// Set the log level. /// If an invalid level is passed, defaults to logging errors only. void doh_set_log_level(uint32_t level); /// Performs the initialization for the DoH engine. Loading doh/boot_time.rs 0 → 100644 +206 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ //! This module provides a time hack to work around the broken `Instant` type in the standard //! library. //! //! `BootTime` looks like `Instant`, but represents `CLOCK_BOOTTIME` instead of `CLOCK_MONOTONIC`. //! This means the clock increments correctly during suspend. pub use std::time::Duration; use std::io; use futures::future::pending; use std::convert::TryInto; use std::fmt; use std::future::Future; use std::os::unix::io::{AsRawFd, RawFd}; use tokio::io::unix::AsyncFd; use tokio::select; /// Represents a moment in time, with differences including time spent in suspend. Only valid for /// a single boot - numbers from different boots are incomparable. #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct BootTime { d: Duration, } // Return an error with the same structure as tokio::time::timeout to facilitate migration off it, // and hopefully some day back to it. /// Error returned by timeout #[derive(Debug, PartialEq)] pub struct Elapsed(()); impl fmt::Display for Elapsed { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { "deadline has elapsed".fmt(fmt) } } impl std::error::Error for Elapsed {} impl BootTime { /// Gets a `BootTime` representing the current moment in time. pub fn now() -> BootTime { let mut t = libc::timespec { tv_sec: 0, tv_nsec: 0 }; // # Safety // clock_gettime's only action will be to possibly write to the pointer provided, // and no borrows exist from that object other than the &mut used to construct the pointer // itself. if unsafe { libc::clock_gettime(libc::CLOCK_BOOTTIME, &mut t as *mut libc::timespec) } != 0 { panic!( "libc::clock_gettime(libc::CLOCK_BOOTTIME) failed: {:?}", io::Error::last_os_error() ); } BootTime { d: Duration::new(t.tv_sec as u64, t.tv_nsec as u32) } } /// Determines how long has elapsed since the provided `BootTime`. pub fn elapsed(&self) -> Duration { BootTime::now().checked_duration_since(*self).unwrap() } /// Add a specified time delta to a moment in time. If this would overflow the representation, /// returns `None`. pub fn checked_add(&self, duration: Duration) -> Option<BootTime> { Some(BootTime { d: self.d.checked_add(duration)? }) } /// Finds the difference from an earlier point in time. If the provided time is later, returns /// `None`. pub fn checked_duration_since(&self, earlier: BootTime) -> Option<Duration> { self.d.checked_sub(earlier.d) } } struct TimerFd(RawFd); impl Drop for TimerFd { fn drop(&mut self) { // # Safety // The fd is owned by the TimerFd struct, and no memory access occurs as a result of this // call. unsafe { libc::close(self.0); } } } impl AsRawFd for TimerFd { fn as_raw_fd(&self) -> RawFd { self.0 } } impl TimerFd { fn create() -> io::Result<Self> { // # Unsafe // This libc call will either give us back a file descriptor or fail, it does not act on // memory or resources. let raw = unsafe { libc::timerfd_create(libc::CLOCK_BOOTTIME, libc::TFD_NONBLOCK | libc::TFD_CLOEXEC) }; if raw < 0 { return Err(io::Error::last_os_error()); } Ok(Self(raw)) } fn set(&self, duration: Duration) { let timer = libc::itimerspec { it_interval: libc::timespec { tv_sec: 0, tv_nsec: 0 }, it_value: libc::timespec { tv_sec: duration.as_secs().try_into().unwrap(), tv_nsec: duration.subsec_nanos().try_into().unwrap(), }, }; // # Unsafe // We own `timer` and there are no borrows to it other than the pointer we pass to // timerfd_settime. timerfd_settime is explicitly documented to handle a null output // parameter for its fourth argument by not filling out the output. The fd passed in at // self.0 is owned by the `TimerFd` struct, so we aren't breaking anyone else's invariants. if unsafe { libc::timerfd_settime(self.0, 0, &timer, std::ptr::null_mut()) } != 0 { panic!("timerfd_settime failed: {:?}", io::Error::last_os_error()); } } } /// Runs the provided future until completion or `duration` has passed on the `CLOCK_BOOTTIME` /// clock. In the event of a timeout, returns the elapsed time as an error. pub async fn timeout<T>(duration: Duration, future: impl Future<Output = T>) -> Result<T, Elapsed> { // Ideally, all timeouts in a runtime would share a timerfd. That will be much more // straightforwards to implement when moving this functionality into `tokio`. // The failure conditions for this are rare (see `man 2 timerfd_create`) and the caller would // not be able to do much in response to them. When integrated into tokio, this would be called // during runtime setup. let timer_fd = TimerFd::create().unwrap(); timer_fd.set(duration); let async_fd = AsyncFd::new(timer_fd).unwrap(); select! { v = future => Ok(v), _ = async_fd.readable() => Err(Elapsed(())), } } /// Provides a future which will complete once the provided duration has passed, as measured by the /// `CLOCK_BOOTTIME` clock. pub async fn sleep(duration: Duration) { assert!(timeout(duration, pending::<()>()).await.is_err()); } #[test] fn monotonic_smoke() { for _ in 0..1000 { // If BootTime is not monotonic, .elapsed() will panic on the unwrap. BootTime::now().elapsed(); } } #[test] fn round_trip() { use std::thread::sleep; for _ in 0..10 { let start = BootTime::now(); sleep(Duration::from_millis(1)); let end = BootTime::now(); let delta = end.checked_duration_since(start).unwrap(); assert_eq!(start.checked_add(delta).unwrap(), end); } } #[tokio::test] async fn timeout_drift() { let delta = Duration::from_millis(20); for _ in 0..10 { let start = BootTime::now(); assert!(timeout(delta, pending::<()>()).await.is_err()); let taken = start.elapsed(); let drift = if taken > delta { taken - delta } else { delta - taken }; assert!(drift < Duration::from_millis(5)); } for _ in 0..10 { let start = BootTime::now(); sleep(delta).await; let taken = start.elapsed(); let drift = if taken > delta { taken - delta } else { delta - taken }; assert!(drift < Duration::from_millis(5)); } } Loading
Android.bp +15 −36 Original line number Diff line number Diff line Loading @@ -326,24 +326,25 @@ filegroup { ], } rust_ffi_static { name: "libdoh_ffi", crate_name: "doh", srcs: ["doh/doh.rs"], edition: "2018", rlibs: [ doh_rust_deps = [ "libandroid_logger", "libanyhow", "libbase64_rust", "libfutures", "liblibc", "liblog_rust", "libquiche", "libring", "libtokio", "liburl", ], ] rust_ffi_static { name: "libdoh_ffi", crate_name: "doh", srcs: ["doh/doh.rs"], edition: "2018", rlibs: doh_rust_deps + ["libquiche"], prefer_rlib: true, shared_libs: [ Loading @@ -368,18 +369,7 @@ rust_test { // Used to enable root permission for the test. // TODO: remove after 'require_root' is supported in rust_test. test_config_template: ":resolv_rust_test_config_template", rustlibs: [ "libandroid_logger", "libanyhow", "libbase64_rust", "libfutures", "liblibc", "liblog_rust", "libquiche_static", "libring", "libtokio", "liburl", ], rustlibs: doh_rust_deps + ["libquiche_static"], min_sdk_version: "29", } Loading @@ -390,18 +380,7 @@ rust_ffi_static { srcs: ["doh/doh.rs"], edition: "2018", rlibs: [ "libandroid_logger", "libanyhow", "libbase64_rust", "libfutures", "liblibc", "liblog_rust", "libquiche_static", "libring", "libtokio", "liburl", ], rlibs: doh_rust_deps + ["libquiche_static"], prefer_rlib: true, // TODO(b/194022174), for unit tests to run on the Android 10 platform, // libunwind must be statically linked. Loading
DnsResolver.cpp +1 −1 Original line number Diff line number Diff line Loading @@ -33,7 +33,7 @@ bool resolv_init(const ResolverNetdCallbacks* callbacks) { // TODO(b/170539625): restore log level to WARNING after clarifying flaky tests. const bool isDebug = isUserDebugBuild(); resolv_set_log_severity(isDebug ? android::base::DEBUG : android::base::WARNING); doh_init_logger(isDebug ? LOG_LEVEL_DEBUG : LOG_LEVEL_WARN); doh_init_logger(isDebug ? DOH_LOG_LEVEL_DEBUG : DOH_LOG_LEVEL_WARN); using android::net::gApiLevel; gApiLevel = getApiLevel(); using android::net::gResNetdCallbacks; Loading
PrivateDnsConfiguration.cpp +1 −1 Original line number Diff line number Diff line Loading @@ -510,7 +510,7 @@ ssize_t PrivateDnsConfiguration::dohQuery(unsigned netId, const Slice query, con { std::lock_guard guard(mPrivateDnsLock); // It's safe because mDohDispatcher won't be deleted after initializing. if (mDohDispatcher == nullptr) return RESULT_CAN_NOT_SEND; if (mDohDispatcher == nullptr) return DOH_RESULT_CAN_NOT_SEND; } return doh_query(mDohDispatcher, netId, query.base(), query.size(), answer.base(), answer.size(), timeoutMs); Loading
doh.h +11 −8 Original line number Diff line number Diff line Loading @@ -26,28 +26,28 @@ #include <sys/types.h> /// The return code of doh_query means that there is no answer. static const ssize_t RESULT_INTERNAL_ERROR = -1; static const ssize_t DOH_RESULT_INTERNAL_ERROR = -1; /// The return code of doh_query means that query can't be sent. static const ssize_t RESULT_CAN_NOT_SEND = -2; static const ssize_t DOH_RESULT_CAN_NOT_SEND = -2; /// The return code of doh_query to indicate that the query timed out. static const ssize_t RESULT_TIMEOUT = -255; static const ssize_t DOH_RESULT_TIMEOUT = -255; /// The error log level. static const uint32_t LOG_LEVEL_ERROR = 0; static const uint32_t DOH_LOG_LEVEL_ERROR = 0; /// The warning log level. static const uint32_t LOG_LEVEL_WARN = 1; static const uint32_t DOH_LOG_LEVEL_WARN = 1; /// The info log level. static const uint32_t LOG_LEVEL_INFO = 2; static const uint32_t DOH_LOG_LEVEL_INFO = 2; /// The debug log level. static const uint32_t LOG_LEVEL_DEBUG = 3; static const uint32_t DOH_LOG_LEVEL_DEBUG = 3; /// The trace log level. static const uint32_t LOG_LEVEL_TRACE = 4; static const uint32_t DOH_LOG_LEVEL_TRACE = 4; /// Context for a running DoH engine. struct DohDispatcher; Loading @@ -60,9 +60,12 @@ using TagSocketCallback = void (*)(int32_t sock); extern "C" { /// Performs static initialization for android logger. /// If an invalid level is passed, defaults to logging errors only. /// If called more than once, it will have no effect on subsequent calls. void doh_init_logger(uint32_t level); /// Set the log level. /// If an invalid level is passed, defaults to logging errors only. void doh_set_log_level(uint32_t level); /// Performs the initialization for the DoH engine. Loading
doh/boot_time.rs 0 → 100644 +206 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ //! This module provides a time hack to work around the broken `Instant` type in the standard //! library. //! //! `BootTime` looks like `Instant`, but represents `CLOCK_BOOTTIME` instead of `CLOCK_MONOTONIC`. //! This means the clock increments correctly during suspend. pub use std::time::Duration; use std::io; use futures::future::pending; use std::convert::TryInto; use std::fmt; use std::future::Future; use std::os::unix::io::{AsRawFd, RawFd}; use tokio::io::unix::AsyncFd; use tokio::select; /// Represents a moment in time, with differences including time spent in suspend. Only valid for /// a single boot - numbers from different boots are incomparable. #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct BootTime { d: Duration, } // Return an error with the same structure as tokio::time::timeout to facilitate migration off it, // and hopefully some day back to it. /// Error returned by timeout #[derive(Debug, PartialEq)] pub struct Elapsed(()); impl fmt::Display for Elapsed { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { "deadline has elapsed".fmt(fmt) } } impl std::error::Error for Elapsed {} impl BootTime { /// Gets a `BootTime` representing the current moment in time. pub fn now() -> BootTime { let mut t = libc::timespec { tv_sec: 0, tv_nsec: 0 }; // # Safety // clock_gettime's only action will be to possibly write to the pointer provided, // and no borrows exist from that object other than the &mut used to construct the pointer // itself. if unsafe { libc::clock_gettime(libc::CLOCK_BOOTTIME, &mut t as *mut libc::timespec) } != 0 { panic!( "libc::clock_gettime(libc::CLOCK_BOOTTIME) failed: {:?}", io::Error::last_os_error() ); } BootTime { d: Duration::new(t.tv_sec as u64, t.tv_nsec as u32) } } /// Determines how long has elapsed since the provided `BootTime`. pub fn elapsed(&self) -> Duration { BootTime::now().checked_duration_since(*self).unwrap() } /// Add a specified time delta to a moment in time. If this would overflow the representation, /// returns `None`. pub fn checked_add(&self, duration: Duration) -> Option<BootTime> { Some(BootTime { d: self.d.checked_add(duration)? }) } /// Finds the difference from an earlier point in time. If the provided time is later, returns /// `None`. pub fn checked_duration_since(&self, earlier: BootTime) -> Option<Duration> { self.d.checked_sub(earlier.d) } } struct TimerFd(RawFd); impl Drop for TimerFd { fn drop(&mut self) { // # Safety // The fd is owned by the TimerFd struct, and no memory access occurs as a result of this // call. unsafe { libc::close(self.0); } } } impl AsRawFd for TimerFd { fn as_raw_fd(&self) -> RawFd { self.0 } } impl TimerFd { fn create() -> io::Result<Self> { // # Unsafe // This libc call will either give us back a file descriptor or fail, it does not act on // memory or resources. let raw = unsafe { libc::timerfd_create(libc::CLOCK_BOOTTIME, libc::TFD_NONBLOCK | libc::TFD_CLOEXEC) }; if raw < 0 { return Err(io::Error::last_os_error()); } Ok(Self(raw)) } fn set(&self, duration: Duration) { let timer = libc::itimerspec { it_interval: libc::timespec { tv_sec: 0, tv_nsec: 0 }, it_value: libc::timespec { tv_sec: duration.as_secs().try_into().unwrap(), tv_nsec: duration.subsec_nanos().try_into().unwrap(), }, }; // # Unsafe // We own `timer` and there are no borrows to it other than the pointer we pass to // timerfd_settime. timerfd_settime is explicitly documented to handle a null output // parameter for its fourth argument by not filling out the output. The fd passed in at // self.0 is owned by the `TimerFd` struct, so we aren't breaking anyone else's invariants. if unsafe { libc::timerfd_settime(self.0, 0, &timer, std::ptr::null_mut()) } != 0 { panic!("timerfd_settime failed: {:?}", io::Error::last_os_error()); } } } /// Runs the provided future until completion or `duration` has passed on the `CLOCK_BOOTTIME` /// clock. In the event of a timeout, returns the elapsed time as an error. pub async fn timeout<T>(duration: Duration, future: impl Future<Output = T>) -> Result<T, Elapsed> { // Ideally, all timeouts in a runtime would share a timerfd. That will be much more // straightforwards to implement when moving this functionality into `tokio`. // The failure conditions for this are rare (see `man 2 timerfd_create`) and the caller would // not be able to do much in response to them. When integrated into tokio, this would be called // during runtime setup. let timer_fd = TimerFd::create().unwrap(); timer_fd.set(duration); let async_fd = AsyncFd::new(timer_fd).unwrap(); select! { v = future => Ok(v), _ = async_fd.readable() => Err(Elapsed(())), } } /// Provides a future which will complete once the provided duration has passed, as measured by the /// `CLOCK_BOOTTIME` clock. pub async fn sleep(duration: Duration) { assert!(timeout(duration, pending::<()>()).await.is_err()); } #[test] fn monotonic_smoke() { for _ in 0..1000 { // If BootTime is not monotonic, .elapsed() will panic on the unwrap. BootTime::now().elapsed(); } } #[test] fn round_trip() { use std::thread::sleep; for _ in 0..10 { let start = BootTime::now(); sleep(Duration::from_millis(1)); let end = BootTime::now(); let delta = end.checked_duration_since(start).unwrap(); assert_eq!(start.checked_add(delta).unwrap(), end); } } #[tokio::test] async fn timeout_drift() { let delta = Duration::from_millis(20); for _ in 0..10 { let start = BootTime::now(); assert!(timeout(delta, pending::<()>()).await.is_err()); let taken = start.elapsed(); let drift = if taken > delta { taken - delta } else { delta - taken }; assert!(drift < Duration::from_millis(5)); } for _ in 0..10 { let start = BootTime::now(); sleep(delta).await; let taken = start.elapsed(); let drift = if taken > delta { taken - delta } else { delta - taken }; assert!(drift < Duration::from_millis(5)); } }