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

Commit fe453402 authored by Luke Huang's avatar Luke Huang
Browse files

Refactor DoH implementation

Current doh implementation is designed to be totally managed by
DnsResolver, which means we might need bunch of C++ glue code to make
things work.
This goal of this refactoring is to minimize the required C++ glue code
and put most control logic into Rust side.

Test: atest
Bug: 155855709
Change-Id: I9a048f5fe72c4c25ae1b95ddf839f244eda34097
parent 341bdae5
Loading
Loading
Loading
Loading
+6 −3
Original line number Diff line number Diff line
@@ -330,7 +330,8 @@ rust_ffi_static {
    rlibs: [
        "libandroid_logger",
        "libanyhow",
        "liblazy_static",
        "libbase64_rust",
        "libfutures",
        "liblibc",
        "liblog_rust",
        "libquiche",
@@ -365,7 +366,8 @@ rust_test {
    rustlibs: [
        "libandroid_logger",
        "libanyhow",
        "liblazy_static",
        "libbase64_rust",
        "libfutures",
        "liblibc",
        "liblog_rust",
        "libquiche_static",
@@ -386,7 +388,8 @@ rust_ffi_static {
    rlibs: [
        "libandroid_logger",
        "libanyhow",
        "liblazy_static",
        "libbase64_rust",
        "libfutures",
        "liblibc",
        "liblog_rust",
        "libquiche_static",
+48 −13
Original line number Diff line number Diff line
@@ -20,28 +20,63 @@

#pragma once

/* Generated with cbindgen:0.15.0 */
/* Generated with cbindgen:0.17.0 */

#include <stdint.h>
#include <sys/types.h>

/// Context for a running DoH engine and associated thread.
struct DohServer;
/// The return code of doh_query means that there is no answer.
static const ssize_t RESULT_INTERNAL_ERROR = -1;

extern "C" {
/// The return code of doh_query means that query can't be sent.
static const ssize_t 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;

/// Performs static initialization fo the DoH engine.
const char* doh_init();
/// Context for a running DoH engine.
struct DohDispatcher;

using ValidationCallback = void (*)(uint32_t net_id, bool success, const char* ip_addr,
                                    const char* host);

extern "C" {

/// Performs static initialization for the DoH engine.
/// Creates and returns a DoH engine instance.
/// The returned object must be freed with doh_delete().
DohServer* doh_new(const char* url, const char* ip_addr, uint32_t mark, const char* cert_path);
DohDispatcher* doh_dispatcher_new(ValidationCallback ptr);

/// Deletes a DoH engine created by doh_dispatcher_new().
/// # Safety
/// `doh` must be a non-null pointer previously created by `doh_dispatcher_new()`
/// and not yet deleted by `doh_dispatcher_delete()`.
void doh_dispatcher_delete(DohDispatcher* doh);

/// Probes and stores the DoH server with the given configurations.
/// Use the negative errno-style codes as the return value to represent the result.
/// # Safety
/// `doh` must be a non-null pointer previously created by `doh_dispatcher_new()`
/// and not yet deleted by `doh_dispatcher_delete()`.
/// `url`, `domain`, `ip_addr`, `cert_path` are null terminated strings.
int32_t doh_net_new(DohDispatcher* doh, uint32_t net_id, const char* url, const char* domain,
                    const char* ip_addr, uint32_t sk_mark, const char* cert_path,
                    uint64_t timeout_ms);

/// Deletes a DoH engine created by doh_new().
void doh_delete(DohServer* doh);
/// Sends a DNS query via the network associated to the given |net_id| and waits for the response.
/// The return code should be either one of the public constant RESULT_* to indicate the error or
/// the size of the answer.
/// # Safety
/// `doh` must be a non-null pointer previously created by `doh_dispatcher_new()`
/// and not yet deleted by `doh_dispatcher_delete()`.
/// `dns_query` must point to a buffer at least `dns_query_len` in size.
/// `response` must point to a buffer at least `response_len` in size.
ssize_t doh_query(DohDispatcher* doh, uint32_t net_id, uint8_t* dns_query, size_t dns_query_len,
                  uint8_t* response, size_t response_len, uint64_t timeout_ms);

/// Sends a DNS query and waits for the response.
ssize_t doh_query(DohServer* doh, uint8_t* query, size_t query_len, uint8_t* response,
                  size_t response_len);
/// Clears the DoH servers associated with the given |netid|.
/// # Safety
/// `doh` must be a non-null pointer previously created by `doh_dispatcher_new()`
/// and not yet deleted by `doh_dispatcher_delete()`.
void doh_net_delete(DohDispatcher* doh, uint32_t net_id);

}  // extern "C"
+752 −406

File changed.

Preview size limit exceeded, changes collapsed.

+3 −0
Original line number Diff line number Diff line
@@ -304,5 +304,8 @@ cc_test {
        "libring-core",
        "libssl",
    ],
    shared_libs: [
        "libnetd_client",
    ],
    min_sdk_version: "29",
}
+48 −6
Original line number Diff line number Diff line
@@ -16,18 +16,60 @@

#include "doh.h"

#include <chrono>
#include <condition_variable>
#include <mutex>

#include <resolv.h>

#include <NetdClient.h>
#include <gmock/gmock-matchers.h>
#include <gtest/gtest.h>

static const char* GOOGLE_SERVER_IP = "8.8.8.8";
static const int TIMEOUT_MS = 3000;
constexpr int MAXPACKET = (8 * 1024);
constexpr unsigned int MINIMAL_NET_ID = 100;

std::mutex m;
std::condition_variable cv;
unsigned int dnsNetId;

TEST(DoHFFITest, SmokeTest) {
    EXPECT_STREQ(doh_init(), "1.0");
    DohServer* doh = doh_new("https://dns.google/dns-query", "8.8.8.8", 0, "");
    getNetworkForDns(&dnsNetId);
    // To ensure that we have a real network.
    ASSERT_GE(dnsNetId, MINIMAL_NET_ID) << "No available networks";

    auto callback = [](uint32_t netId, bool success, const char* ip_addr, const char* host) {
        EXPECT_EQ(netId, dnsNetId);
        EXPECT_TRUE(success);
        EXPECT_STREQ(ip_addr, GOOGLE_SERVER_IP);
        EXPECT_STREQ(host, "");
        cv.notify_one();
    };
    DohDispatcher* doh = doh_dispatcher_new(callback);
    EXPECT_TRUE(doh != nullptr);

    // www.example.com
    uint8_t query[] = "q80BAAABAAAAAAAAA3d3dwdleGFtcGxlA2NvbQAAAQAB";
    // TODO: Use a local server instead of dns.google.
    // sk_mark doesn't matter here because this test doesn't have permission to set sk_mark.
    // The DNS packet would be sent via default network.
    EXPECT_EQ(doh_net_new(doh, dnsNetId, "https://dns.google/dns-query", /* domain */ "",
                          GOOGLE_SERVER_IP,
                          /* sk_mark */ 0, /* cert_path */ "", TIMEOUT_MS),
              0);
    {
        std::unique_lock<std::mutex> lk(m);
        EXPECT_EQ(cv.wait_for(lk, std::chrono::milliseconds(TIMEOUT_MS)),
                  std::cv_status::no_timeout);
    }

    std::vector<uint8_t> buf(MAXPACKET, 0);
    ssize_t len = res_mkquery(ns_o_query, "www.example.com", ns_c_in, ns_t_aaaa, nullptr, 0,
                              nullptr, buf.data(), MAXPACKET);
    uint8_t answer[8192];
    ssize_t len = doh_query(doh, query, sizeof query, answer, sizeof answer);

    len = doh_query(doh, dnsNetId, buf.data(), len, answer, sizeof answer, TIMEOUT_MS);
    EXPECT_GT(len, 0);
    doh_delete(doh);
    doh_net_delete(doh, dnsNetId);
    doh_dispatcher_delete(doh);
}