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

Commit e97e69a1 authored by android-build-team Robot's avatar android-build-team Robot
Browse files

Snap for 7357916 from 683b5252 to...

Snap for 7357916 from 683b5252 to mainline-captiveportallogin-release

Change-Id: I5852f51c5f7aecde443dc2bc1ff8fb829cdfca92
parents aed9935a 683b5252
Loading
Loading
Loading
Loading
+35 −1
Original line number Diff line number Diff line
@@ -281,13 +281,22 @@ cc_library_static {
    export_generated_headers: ["statslog_resolv.h"],
    static_libs: [
        "libcutils",
        "libgtest_prod", // Used by libstatspush_compat
        "libstatspush_compat",
    ],
    header_libs: [
        "libgtest_prod_headers", // Used by libstatspush_compat
    ],
    apex_available: ["com.android.resolv"],
    min_sdk_version: "29",
}

filegroup {
    name: "resolv_rust_test_config_template",
    srcs: [
        "resolv_rust_test_config_template.xml",
    ],
}

filegroup {
    name: "resolv_test_config_template",
    srcs: [
@@ -359,3 +368,28 @@ filegroup {
//     ],
//     min_sdk_version: "29",
// }

// rust_test {
//     name: "doh_unit_test",
//     enabled: support_rust_toolchain,
//     crate_name: "doh",
//     srcs: ["doh.rs"],
//     edition: "2018",
//     test_suites: ["general-tests"],
//     auto_gen_config: true,
//     // 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",
//         "liblazy_static",
//         "liblibc",
//         "liblog_rust",
//         "libquiche",
//         "libring",
//         "libtokio",
//         "liburl",
//     ],
//     min_sdk_version: "29",
// }
+136 −20
Original line number Diff line number Diff line
@@ -21,6 +21,8 @@
#include <netdutils/Stopwatch.h>

#include "DnsTlsSocketFactory.h"
#include "Experiments.h"
#include "PrivateDnsConfiguration.h"
#include "resolv_cache.h"
#include "resolv_private.h"
#include "stats.pb.h"
@@ -46,8 +48,8 @@ DnsTlsDispatcher& DnsTlsDispatcher::getInstance() {
    return instance;
}

std::list<DnsTlsServer> DnsTlsDispatcher::getOrderedServerList(
        const std::list<DnsTlsServer> &tlsServers, unsigned mark) const {
std::list<DnsTlsServer> DnsTlsDispatcher::getOrderedAndUsableServerList(
        const std::list<DnsTlsServer>& tlsServers, unsigned netId, unsigned mark) {
    // Our preferred DnsTlsServer order is:
    //     1) reuse existing IPv6 connections
    //     2) reuse existing IPv4 connections
@@ -65,7 +67,16 @@ std::list<DnsTlsServer> DnsTlsDispatcher::getOrderedServerList(

        for (const auto& tlsServer : tlsServers) {
            const Key key = std::make_pair(mark, tlsServer);
            if (mStore.find(key) != mStore.end()) {
            if (const Transport* xport = getTransport(key); xport != nullptr) {
                // DoT revalidation specific feature.
                if (!xport->usable()) {
                    // Don't use this xport. It will be removed after timeout
                    // (IDLE_TIMEOUT minutes).
                    LOG(DEBUG) << "Skip using DoT server " << tlsServer.toIpString() << " on "
                               << netId;
                    continue;
                }

                switch (tlsServer.ss.ss_family) {
                    case AF_INET:
                        existing4.push_back(tlsServer);
@@ -97,19 +108,21 @@ std::list<DnsTlsServer> DnsTlsDispatcher::getOrderedServerList(
DnsTlsTransport::Response DnsTlsDispatcher::query(const std::list<DnsTlsServer>& tlsServers,
                                                  res_state statp, const Slice query,
                                                  const Slice ans, int* resplen) {
    const std::list<DnsTlsServer> orderedServers(getOrderedServerList(tlsServers, statp->_mark));
    const std::list<DnsTlsServer> servers(
            getOrderedAndUsableServerList(tlsServers, statp->netid, statp->_mark));

    if (orderedServers.empty()) LOG(WARNING) << "Empty DnsTlsServer list";
    if (servers.empty()) LOG(WARNING) << "No usable DnsTlsServers";

    DnsTlsTransport::Response code = DnsTlsTransport::Response::internal_error;
    int serverCount = 0;
    for (const auto& server : orderedServers) {
    for (const auto& server : servers) {
        DnsQueryEvent* dnsQueryEvent =
                statp->event->mutable_dns_query_events()->add_dns_query_event();

        bool connectTriggered = false;
        Stopwatch queryStopwatch;
        code = this->query(server, statp->_mark, query, ans, resplen, &connectTriggered);
        code = this->query(server, statp->netid, statp->_mark, query, ans, resplen,
                           &connectTriggered);

        dnsQueryEvent->set_latency_micros(saturate_cast<int32_t>(queryStopwatch.timeTakenUs()));
        dnsQueryEvent->set_dns_server_index(serverCount++);
@@ -148,9 +161,9 @@ DnsTlsTransport::Response DnsTlsDispatcher::query(const std::list<DnsTlsServer>&
    return code;
}

DnsTlsTransport::Response DnsTlsDispatcher::query(const DnsTlsServer& server, unsigned mark,
                                                  const Slice query, const Slice ans, int* resplen,
                                                  bool* connectTriggered) {
DnsTlsTransport::Response DnsTlsDispatcher::query(const DnsTlsServer& server, unsigned netId,
                                                  unsigned mark, const Slice query, const Slice ans,
                                                  int* resplen, bool* connectTriggered) {
    // TODO: This can cause the resolver to create multiple connections to the same DoT server
    // merely due to different mark, such as the bit explicitlySelected unset.
    // See if we can save them and just create one connection for one DoT server.
@@ -158,12 +171,8 @@ DnsTlsTransport::Response DnsTlsDispatcher::query(const DnsTlsServer& server, un
    Transport* xport;
    {
        std::lock_guard guard(sLock);
        auto it = mStore.find(key);
        if (it == mStore.end()) {
            xport = new Transport(server, mark, mFactory.get());
            mStore[key].reset(xport);
        } else {
            xport = it->second.get();
        if (xport = getTransport(key); xport == nullptr) {
            xport = addTransport(server, mark);
        }
        ++xport->useCount;
    }
@@ -173,10 +182,7 @@ DnsTlsTransport::Response DnsTlsDispatcher::query(const DnsTlsServer& server, un
    // stuck, this function also gets blocked.
    const int connectCounter = xport->transport.getConnectCounter();

    LOG(DEBUG) << "Sending query of length " << query.size();
    auto res = xport->transport.query(query);
    LOG(DEBUG) << "Awaiting response";
    const auto& result = res.get();
    const auto& result = queryInternal(*xport, query);
    *connectTriggered = (xport->transport.getConnectCounter() > connectCounter);

    DnsTlsTransport::Response code = result.code;
@@ -198,11 +204,55 @@ DnsTlsTransport::Response DnsTlsDispatcher::query(const DnsTlsServer& server, un
        std::lock_guard guard(sLock);
        --xport->useCount;
        xport->lastUsed = now;

        // DoT revalidation specific feature.
        if (xport->checkRevalidationNecessary(code)) {
            // Even if the revalidation passes, it doesn't guarantee that DoT queries
            // to the xport can stop failing because revalidation creates a new connection
            // to probe while the xport still uses an existing connection. So far, there isn't
            // a feasible way to force the xport to disconnect the connection. If the case
            // happens, the xport will be marked as unusable and DoT queries won't be sent to
            // it anymore. Eventually, after IDLE_TIMEOUT, the xport will be destroyed, and
            // a new xport will be created.
            const auto result =
                    PrivateDnsConfiguration::getInstance().requestValidation(netId, server, mark);
            LOG(WARNING) << "Requested validation for " << server.toIpString() << " with mark 0x"
                         << std::hex << mark << ", "
                         << (result.ok() ? "succeeded" : "failed: " + result.error().message());
        }

        cleanup(now);
    }
    return code;
}

DnsTlsTransport::Result DnsTlsDispatcher::queryInternal(Transport& xport,
                                                        const netdutils::Slice query) {
    LOG(DEBUG) << "Sending query of length " << query.size();

    // If dot_async_handshake is not set, the call might block in some cases; otherwise,
    // the call should return very soon.
    auto res = xport.transport.query(query);
    LOG(DEBUG) << "Awaiting response";

    if (xport.timeout().count() == -1) {
        // Infinite timeout.
        return res.get();
    }

    const auto status = res.wait_for(xport.timeout());
    if (status == std::future_status::timeout) {
        // TODO(b/186613628): notify the Transport to remove this query.
        LOG(WARNING) << "DoT query timed out after " << xport.timeout().count() << " ms";
        return DnsTlsTransport::Result{
                .code = DnsTlsTransport::Response::network_error,
                .response = {},
        };
    }

    return res.get();
}

// This timeout effectively controls how long to keep SSL session tickets.
static constexpr std::chrono::minutes IDLE_TIMEOUT(5);
void DnsTlsDispatcher::cleanup(std::chrono::time_point<std::chrono::steady_clock> now) {
@@ -222,5 +272,71 @@ void DnsTlsDispatcher::cleanup(std::chrono::time_point<std::chrono::steady_clock
    mLastCleanup = now;
}

DnsTlsDispatcher::Transport* DnsTlsDispatcher::addTransport(const DnsTlsServer& server,
                                                            unsigned mark) {
    const Key key = std::make_pair(mark, server);
    Transport* ret = getTransport(key);
    if (ret != nullptr) return ret;

    const Experiments* const instance = Experiments::getInstance();
    int triggerThr =
            instance->getFlag("dot_revalidation_threshold", Transport::kDotRevalidationThreshold);
    int unusableThr = instance->getFlag("dot_xport_unusable_threshold",
                                        Transport::kDotXportUnusableThreshold);
    int queryTimeout = instance->getFlag("dot_query_timeout_ms", Transport::kDotQueryTimeoutMs);

    // Check and adjust the parameters if they are improperly set.
    bool revalidationEnabled = false;
    const bool isForOpportunisticMode = server.name.empty();
    if (triggerThr > 0 && unusableThr > 0 && isForOpportunisticMode) {
        revalidationEnabled = true;
    } else {
        triggerThr = -1;
        unusableThr = -1;
    }
    if (queryTimeout < 0) {
        queryTimeout = -1;
    } else if (queryTimeout < 1000) {
        queryTimeout = 1000;
    }

    ret = new Transport(server, mark, mFactory.get(), revalidationEnabled, triggerThr, unusableThr,
                        queryTimeout);
    LOG(DEBUG) << "Transport is initialized with { " << triggerThr << ", " << unusableThr << ", "
               << queryTimeout << "ms }"
               << " for server { " << server.toIpString() << "/" << server.name << " }";

    mStore[key].reset(ret);

    return ret;
}

DnsTlsDispatcher::Transport* DnsTlsDispatcher::getTransport(const Key& key) {
    auto it = mStore.find(key);
    return (it == mStore.end() ? nullptr : it->second.get());
}

bool DnsTlsDispatcher::Transport::checkRevalidationNecessary(DnsTlsTransport::Response code) {
    if (!revalidationEnabled) return false;

    if (code == DnsTlsTransport::Response::network_error) {
        continuousfailureCount++;
    } else {
        continuousfailureCount = 0;
    }

    // triggerThreshold must be greater than 0 because the value of revalidationEnabled is true.
    if (usable() && continuousfailureCount == triggerThreshold) {
        return true;
    }
    return false;
}

bool DnsTlsDispatcher::Transport::usable() const {
    if (!revalidationEnabled) return true;

    return continuousfailureCount < unusableThreshold;
}

}  // end of namespace net
}  // end of namespace android
+57 −7
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ namespace net {

// This is a singleton class that manages the collection of active DnsTlsTransports.
// Queries made here are dispatched to an existing or newly constructed DnsTlsTransport.
// TODO: PrivateDnsValidationObserver is not implemented in this class. Remove it.
class DnsTlsDispatcher : public PrivateDnsValidationObserver {
  public:
    // Constructor with dependency injection for testing.
@@ -57,7 +58,7 @@ class DnsTlsDispatcher : public PrivateDnsValidationObserver {
    // and writes the response into |ans|, and indicates the number of bytes written in |resplen|.
    // If the whole procedure above triggers (or experiences) any new connection, |connectTriggered|
    // is set. Returns a success or error code.
    DnsTlsTransport::Response query(const DnsTlsServer& server, unsigned mark,
    DnsTlsTransport::Response query(const DnsTlsServer& server, unsigned netId, unsigned mark,
                                    const netdutils::Slice query, const netdutils::Slice ans,
                                    int* _Nonnull resplen, bool* _Nonnull connectTriggered);

@@ -78,8 +79,13 @@ class DnsTlsDispatcher : public PrivateDnsValidationObserver {
    // Transport is a thin wrapper around DnsTlsTransport, adding reference counting and
    // usage monitoring so we can expire idle sessions from the cache.
    struct Transport {
        Transport(const DnsTlsServer& server, unsigned mark, IDnsTlsSocketFactory* _Nonnull factory)
            : transport(server, mark, factory) {}
        Transport(const DnsTlsServer& server, unsigned mark, IDnsTlsSocketFactory* _Nonnull factory,
                  bool revalidationEnabled, int triggerThr, int unusableThr, int timeout)
            : transport(server, mark, factory),
              revalidationEnabled(revalidationEnabled),
              triggerThreshold(triggerThr),
              unusableThreshold(unusableThr),
              mTimeout(timeout) {}
        // DnsTlsTransport is thread-safe, so it doesn't need to be guarded.
        DnsTlsTransport transport;
        // This use counter and timestamp are used to ensure that only idle sessions are
@@ -87,24 +93,68 @@ class DnsTlsDispatcher : public PrivateDnsValidationObserver {
        int useCount GUARDED_BY(sLock) = 0;
        // lastUsed is only guaranteed to be meaningful after useCount is decremented to zero.
        std::chrono::time_point<std::chrono::steady_clock> lastUsed GUARDED_BY(sLock);

        // If DoT revalidation is disabled, it returns true; otherwise, it returns
        // whether or not this Transport is usable.
        bool usable() const REQUIRES(sLock);

        bool checkRevalidationNecessary(DnsTlsTransport::Response code) REQUIRES(sLock);

        std::chrono::milliseconds timeout() const { return mTimeout; }

        static constexpr int kDotRevalidationThreshold = -1;
        static constexpr int kDotXportUnusableThreshold = -1;
        static constexpr int kDotQueryTimeoutMs = -1;

      private:
        // Used to track if this Transport is usable.
        int continuousfailureCount GUARDED_BY(sLock) = 0;

        // Used to indicate whether DoT revalidation is enabled for this Transport.
        // The value is set to true only if:
        //    1. both triggerThreshold and unusableThreshold are  positive values.
        //    2. private DNS mode is opportunistic.
        const bool revalidationEnabled;

        // The number of continuous failures to trigger a validation. It takes effect when DoT
        // revalidation is on. If the value is not a positive value, DoT revalidation is disabled.
        // Note that it must be at least 10, or it breaks ConnectTlsServerTimeout_ConcurrentQueries
        // test.
        const int triggerThreshold;

        // The threshold to determine if this Transport is considered unusable.
        // If continuousfailureCount reaches this value, this Transport is no longer used. It
        // takes effect when DoT revalidation is on. If the value is not a positive value, DoT
        // revalidation is disabled.
        const int unusableThreshold;

        // The time to await a future (the result of a DNS request) from the DnsTlsTransport
        // of this Transport.
        // To set an infinite timeout, assign the value to -1.
        const std::chrono::milliseconds mTimeout;
    };

    Transport* _Nullable addTransport(const DnsTlsServer& server, unsigned mark) REQUIRES(sLock);
    Transport* _Nullable getTransport(const Key& key) REQUIRES(sLock);

    // Cache of reusable DnsTlsTransports.  Transports stay in cache as long as
    // they are in use and for a few minutes after.
    // The key is a (netid, server) pair.  The netid is first for lexicographic comparison speed.
    std::map<Key, std::unique_ptr<Transport>> mStore GUARDED_BY(sLock);

    // The last time we did a cleanup.  For efficiency, we only perform a cleanup once every
    // few minutes.
    std::chrono::time_point<std::chrono::steady_clock> mLastCleanup GUARDED_BY(sLock);

    DnsTlsTransport::Result queryInternal(Transport& transport, const netdutils::Slice query)
            EXCLUDES(sLock);

    // Drop any cache entries whose useCount is zero and which have not been used recently.
    // This function performs a linear scan of mStore.
    void cleanup(std::chrono::time_point<std::chrono::steady_clock> now) REQUIRES(sLock);

    // Return a sorted list of DnsTlsServers in preference order.
    std::list<DnsTlsServer> getOrderedServerList(const std::list<DnsTlsServer>& tlsServers,
                                                 unsigned mark) const;
    // Return a sorted list of usable DnsTlsServers in preference order.
    std::list<DnsTlsServer> getOrderedAndUsableServerList(const std::list<DnsTlsServer>& tlsServers,
                                                          unsigned netId, unsigned mark);

    // Trivial factory for DnsTlsSockets.  Dependency injection is only used for testing.
    std::unique_ptr<IDnsTlsSocketFactory> mFactory;
+4 −3
Original line number Diff line number Diff line
@@ -51,7 +51,8 @@ class Experiments {
    static constexpr const char* const kExperimentFlagKeyList[] = {
            "keep_listening_udp",   "parallel_lookup_release",    "parallel_lookup_sleep_time",
            "sort_nameservers",     "dot_async_handshake",        "dot_connect_timeout_ms",
            "dot_maxtries",
            "dot_maxtries",         "dot_revalidation_threshold", "dot_xport_unusable_threshold",
            "dot_query_timeout_ms",
    };
    // This value is used in updateInternal as the default value if any flags can't be found.
    static constexpr int kFlagIntDefault = INT_MIN;
+52 −33

File changed.

Preview size limit exceeded, changes collapsed.

Loading