summaryrefslogtreecommitdiffstats
path: root/netwerk/dns/PlatformDNSAndroid.cpp
blob: 171797b93820261f88f033a973938108cbf7cbec (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=4 sw=2 sts=2 et cin: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "GetAddrInfo.h"
#include "mozilla/net/DNSPacket.h"
#include "nsIDNSService.h"
#include "mozilla/Maybe.h"
#include "mozilla/Atomics.h"
#include "mozilla/StaticPrefs_network.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <resolv.h>
#include <poll.h>
#include <dlfcn.h>
#include <android/multinetwork.h>

namespace mozilla::net {

// The first call to ResolveHTTPSRecordImpl will load the library
// and function pointers.
static Atomic<bool> sLibLoading{false};

// https://developer.android.com/ndk/reference/group/networking#android_res_nquery
// The function android_res_nquery is defined in <android/multinetwork.h>
typedef int (*android_res_nquery_ptr)(net_handle_t network, const char* dname,
                                      int ns_class, int ns_type,
                                      uint32_t flags);
static Atomic<android_res_nquery_ptr> sAndroidResNQuery;

#define LOG(msg, ...) \
  MOZ_LOG(gGetAddrInfoLog, LogLevel::Debug, ("[DNS]: " msg, ##__VA_ARGS__))

nsresult ResolveHTTPSRecordImpl(const nsACString& aHost, uint16_t aFlags,
                                TypeRecordResultType& aResult, uint32_t& aTTL) {
  DNSPacket packet;
  nsAutoCString host(aHost);
  nsAutoCString cname;
  nsresult rv;

  if (xpc::IsInAutomation() &&
      !StaticPrefs::network_dns_native_https_query_in_automation()) {
    return NS_ERROR_UNKNOWN_HOST;
  }

  if (!sLibLoading.exchange(true)) {
    // We're the first call here, load the library and symbols.
    void* handle = dlopen("libandroid.so", RTLD_LAZY | RTLD_LOCAL);
    if (!handle) {
      LOG("Error loading libandroid_net %s", dlerror());
      return NS_ERROR_UNKNOWN_HOST;
    }

    auto x = dlsym(handle, "android_res_nquery");
    if (!x) {
      LOG("No android_res_nquery symbol");
    }

    sAndroidResNQuery = (android_res_nquery_ptr)x;
  }

  if (!sAndroidResNQuery) {
    LOG("nquery not loaded");
    // The library hasn't been loaded yet.
    return NS_ERROR_UNKNOWN_HOST;
  }

  LOG("resolving %s\n", host.get());
  // Perform the query
  rv = packet.FillBuffer(
      [&](unsigned char response[DNSPacket::MAX_SIZE]) -> int {
        int fd = 0;
        auto closeSocket = MakeScopeExit([&] {
          if (fd > 0) {
            close(fd);
          }
        });
        uint32_t flags = 0;
        if (aFlags & nsIDNSService::RESOLVE_BYPASS_CACHE) {
          flags = ANDROID_RESOLV_NO_CACHE_LOOKUP;
        }
        fd = sAndroidResNQuery(0, host.get(), ns_c_in,
                               nsIDNSService::RESOLVE_TYPE_HTTPSSVC, flags);

        if (fd < 0) {
          LOG("DNS query failed");
          return fd;
        }

        struct pollfd fds;
        fds.fd = fd;
        fds.events = POLLIN;  // Wait for read events

        // Wait for an event on the file descriptor
        int ret = poll(&fds, 1,
                       StaticPrefs::network_dns_native_https_timeout_android());
        if (ret <= 0) {
          LOG("poll failed %d", ret);
          return -1;
        }

        ssize_t len = recv(fd, response, DNSPacket::MAX_SIZE - 1, 0);
        if (len <= 8) {
          LOG("size too small %zd", len);
          return len < 0 ? len : -1;
        }

        // The first 8 bytes are UDP header.
        // XXX: we should consider avoiding this move somehow.
        for (int i = 0; i < len - 8; i++) {
          response[i] = response[i + 8];
        }

        return len - 8;
      });
  if (NS_FAILED(rv)) {
    LOG("failed rv");
    return rv;
  }
  packet.SetNativePacket(true);

  int32_t loopCount = 64;
  while (loopCount > 0 && aResult.is<Nothing>()) {
    loopCount--;
    DOHresp resp;
    nsClassHashtable<nsCStringHashKey, DOHresp> additionalRecords;
    rv = packet.Decode(host, TRRTYPE_HTTPSSVC, cname, true, resp, aResult,
                       additionalRecords, aTTL);
    if (NS_FAILED(rv)) {
      LOG("Decode failed %x", static_cast<uint32_t>(rv));
      return rv;
    }
    if (!cname.IsEmpty() && aResult.is<Nothing>()) {
      host = cname;
      cname.Truncate();
      continue;
    }
  }

  if (aResult.is<Nothing>()) {
    LOG("Result is nothing");
    // The call succeeded, but no HTTPS records were found.
    return NS_ERROR_UNKNOWN_HOST;
  }

  return NS_OK;
}

void DNSThreadShutdown() {}

}  // namespace mozilla::net