Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a4dba473 authored by Matthew Maurer's avatar Matthew Maurer Committed by Automerger Merge Worker
Browse files

Merge changes I4fd4c57a,I719965ff,I91a8a7f4 am: 971821fb am: 2b11d35d

Original change: https://android-review.googlesource.com/c/platform/packages/modules/DnsResolver/+/1845781

Change-Id: I566e3a408213368daa4e295fa70e997125158460
parents a2fccc24 2b11d35d
Loading
Loading
Loading
Loading
+15 −36
Original line number Diff line number Diff line
@@ -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: [
@@ -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",
}

@@ -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.
+1 −1
Original line number Diff line number Diff line
@@ -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;
+1 −1
Original line number Diff line number Diff line
@@ -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);
+11 −8
Original line number Diff line number Diff line
@@ -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;
@@ -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.

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