Snap for 6439596 from 4db98b94798ea8ca29a89aac579d216239740e0f to qt-aml-tzdata-release
Change-Id: I59007f36d73b0d11760b5eacd128ea339978a7a4
diff --git a/Android.bp b/Android.bp
index 9763730..114b2e1 100644
--- a/Android.bp
+++ b/Android.bp
@@ -10,8 +10,6 @@
"-Werror",
// Override -Wno-error=implicit-fallthrough from soong
"-Werror=implicit-fallthrough",
- "-Werror=sometimes-uninitialized",
- "-Werror=conditional-uninitialized",
"-Wnullable-to-nonnull-conversion",
"-Wsign-compare",
"-Wthread-safety",
diff --git a/OWNERS b/OWNERS
index 7d617ec..107eefc 100644
--- a/OWNERS
+++ b/OWNERS
@@ -4,7 +4,6 @@
jchalard@google.com
lorenzo@google.com
maze@google.com
-nuccachen@google.com
reminv@google.com
satk@google.com
xiaom@google.com
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 27eac94..c8dbf77 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -1,6 +1,5 @@
[Builtin Hooks]
clang_format = true
-commit_msg_test_field = false
[Builtin Hooks Options]
clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 095fa7d..2d3673c 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -1,20 +1,10 @@
{
"presubmit": [
- { "name": "libnetdbpf_test" },
{ "name": "netd_integration_test" },
{ "name": "netd_unit_test" },
- { "name": "netdclient_test" },
- { "name": "netdutils_test" }
- ],
- "postsubmit": [
- { "name": "libnetdbpf_test",
- "keywords": ["netd-device-kernel-4.9", "netd-device-kernel-4.14"]},
- { "name": "netd_integration_test",
- "keywords": ["netd-device-kernel-4.9", "netd-device-kernel-4.14"]},
- { "name": "netd_unit_test",
- "keywords": ["netd-device-kernel-4.9", "netd-device-kernel-4.14"]}
- ],
- "imports": [
- { "path": "packages/modules/DnsResolver" }
+ { "name": "libnetdbpf_test" },
+ { "name": "netdutils_test" },
+ { "name": "resolv_integration_test" },
+ { "name": "resolv_unit_test" }
]
}
diff --git a/apex/Android.bp b/apex/Android.bp
new file mode 100644
index 0000000..c1d33e1
--- /dev/null
+++ b/apex/Android.bp
@@ -0,0 +1,41 @@
+// Copyright (C) 2018 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.
+
+
+apex {
+ name: "com.android.resolv",
+ manifest: "manifest.json",
+ multilib: {
+ first: {
+ native_shared_libs: ["libnetd_resolv"],
+ },
+ },
+ key: "com.android.resolv.key",
+ certificate: ":com.android.resolv.certificate",
+
+ // Use a custom AndroidManifest.xml used for API targeting.
+ androidManifest: "AndroidManifest.xml",
+}
+
+apex_key {
+ name: "com.android.resolv.key",
+ public_key: "com.android.resolv.avbpubkey",
+ private_key: "com.android.resolv.pem",
+}
+
+android_app_certificate {
+ name: "com.android.resolv.certificate",
+ // will use cert.pk8 and cert.x509.pem
+ certificate: "testcert",
+}
diff --git a/apex/AndroidManifest.xml b/apex/AndroidManifest.xml
new file mode 100644
index 0000000..9d6455c
--- /dev/null
+++ b/apex/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2019 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.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.resolv">
+ <!-- APEX does not have classes.dex -->
+ <application android:hasCode="false" />
+ <!--
+ * The interface between the resolver and netd is backwards compatible, so the resolver APEX
+ * can work with any version of netd.
+ -->
+ <uses-sdk
+ android:targetSdkVersion="29"
+ />
+</manifest>
diff --git a/apex/OWNERS b/apex/OWNERS
new file mode 100644
index 0000000..bc97a1b
--- /dev/null
+++ b/apex/OWNERS
@@ -0,0 +1,4 @@
+chenbruce@google.com
+codewiz@google.com
+martinwu@google.com
+
diff --git a/apex/com.android.resolv.avbpubkey b/apex/com.android.resolv.avbpubkey
new file mode 100644
index 0000000..e0af34c
--- /dev/null
+++ b/apex/com.android.resolv.avbpubkey
Binary files differ
diff --git a/apex/com.android.resolv.pem b/apex/com.android.resolv.pem
new file mode 100644
index 0000000..6eb688d
--- /dev/null
+++ b/apex/com.android.resolv.pem
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIJJwIBAAKCAgEAqcW/Lsj7Ftp8jUpWaW82J+uSpIlcs5w/vsIBEPW/nrakXvLG
+GBA1gauamV+2hvQ76CIkXqsYeaBl8jo1JZuF+ek0tEtHnvMCuGWZZmJSni9CWdXa
+GjQf74+HD0LcVYZd07xjW4aR42httOq9S9zGEucQHaTSQ0C0JBRFMUaAva7QJG15
+w0oqogze7TKTxO2aXZ9R1n3STprY+dv5FXcgGNraAZiSZRYc2FDvgE3J1V1PrdV8
+9RZv6133Iz5eINxR/6MTo2yxNxwZ6gsLD72gTX4N7nc9xtbC9WXCi8Uf7b6dhfyf
+zwZfJSKnwHh6EMa1AdIEgSSdIjbbcCP5mFscbvzUWNuAtUn8RWXUFOF5ssWsTMQC
+QtmfAnbVYH3oMxv3HBlDzS4CVMBmIBUZDbtvpPdQYBJPA/l/kU4DVjCv6Kpgx5Z3
+tVXVA9elta6eZ2dXL7aOPeOJS7OwP6nnEZr/7JJ8NRtQNOwRUU1xRcTxkwRmr/2A
+4h2qoYvxCOebqijvobKzB9cCzM5PvQzLA2aF7yohMN24HNNcTaybeBY6yUGd/iaD
+q0xyD8t7XyoEpMs7SOKkVp08G/kpp7hxRWUzADhhYrvMEAIsyIxX5jSTZYtvYLU/
+UMbfpUNE3qQEYNMv6d5+/Psv8EAjQA5mkNiSY/VWWnPkWapha8MAvYA9nTcCAwEA
+AQKCAgBKG8gY5CK3FyjHhGu0GpfJVUvuBwgwX0R0QLu2a4/5+EBIMjHGz0yMDhyu
+VtmWj4aXDlBSAt3sOdhGpKIOiJUzQll/Wl0pRWsqky8jQkhORNLx5CgQyDAoor6S
+Kt2Dd+P9SX8VtOh6HFvHXbDELNtJ5RNj3U7rjJMWLMMHf38zTdwOCrvcQ8PYeUXW
+xP08OXvo4mMW/lWPeoi8iQlyFU4ti1se1zsQZVayFqJ1n387ZEAj4c1qLyEr3RjE
+ibUNIx+oN8eazxeMtveY8rkhZeVT7nKmZebRpW31MTZ7TNFjNpIMqvoXpPBjhX00
+x2KBcDwTT1ooBZOSG+AuU4Xgu+Ts1+6nZKbhnQuxvgslFyDEdtuID7nuXu08D3B2
+OpCtrQt08QS/dLHpcOhSMjkxqH2aITVPQgGWK16E7pcVex/Lapyjr4ETx1GqjpCL
+uKhmvGsrcdUD9fETzV5ZsMBHrnCSlq0XRnWFuGYmxlSIAciNUTMFfy7D/ml++Boe
+S33HwuaJlnUp3iAbIddXXx+zZHBocJ1Iznl1iu8CMdh+79wMOPkcMDRRNZPpMca+
+ukfGRRdrtyPyrWdrv9d+jtORBC3L9Kgg5yCEXe3HZCSLj/9UUrEAAACEw/IuYxy0
+AsDwpHS9WuqvLYbFPL3UQlSlFJWKkgCZwO+K+c2MsVnllWI0AQKCAQEA0NqVa0Jx
+TGxE2EEEgxA63c+rp5Qm2IlKns+tbnWnfI6XW/JiFkDqTVkIcg6F3dVyjEaZnGWa
+MPCORCtOGQ8IyAYQANyza/CW2+i0YvJBSMmCsbo7wUdrYU9krPRLLWYLhue5HdTP
+5+H+lFXmu3o36YhhWwfMflZsV1MOzRF3eIH3FaTgzoXmBQH3CH5y2c3OWjhMrTnz
+51HZFPu3XB3wmqEIyHAsQke68HxgyJ7uBYUmdAIYYMLgV3Jp/ds+QxXSqwYz97tS
+oe6778QLqcn/Q9LtonagDMO/ATSw/0fLt1+8QLdbksDTC4MUoaGKxuiL7sy5PnKd
+72oV75t06Tz26QKCAQEA0BiyFaC3EcZG6KmoZvM+bHGK9gp5tKr0AgERLAVe+M/f
+1NsBxf1HxoHlkudGRo64J+SwM+ZVVh5UPtrb1Le5a3IR9qL++ZfPM1WuvlHz9uBc
+lJLPozboWoUovbfaoKQtuRzzOYBwodwAbY02LNNtM33jqXNUN0MM0L3ZH3FmOglU
+u/4dH+EvgLDpnCV0czMlGSo9I9977Nvy3dQBXjzz9EF+covAhq7skmfwDblTAZR7
+EkmsCmoeEoKRAEtKgAxs0Hgyo2iMIxYy0pdvjGhJe64JQb3EKLjxhYsadnI+S5YI
+QBH9+EX/ZLSii+cIkEeh1lSMk9uT1/Rp4V40sdmfHwKCAQA9oTLUWt/qCEzsb4sw
+TbrBN4XUxpAL979wS6JG5SsOQ1mAxW6SWIeLWYl3drej9Vd+81pi30x3wpSIoyrB
+lEEoHTndt0KuyGkL7YhFZm1DWEtbvLPjXfre1TnFG7WPaPxfy8NBfUn4iCTCivKy
+Qed730WzQgLjCQehYR4N/2h8xAUwet3ns3Vj5ueJtx6XDPcmglSGDYLesLVZTsoV
+wbP9PSXFV+yHhnkwe9NngBaGxHrLYs7kxrzCsT17rpWZ4DexRfxRzxIcPMFyiCxU
+5wmPbw+2kEC8Y4rahTzxp7MCopp/klvQW1wrmnudEnlMJtUcG4dSWSonuutMMFh4
+dwf5AoIBAEX9+HAT1V9yHEmHPvAZooZhDkdBMLxWv6mo6DixOcdgS73RR+BrF2gq
+Kqhlh5qVyFUKs0VRlKRZoSZfAI+kmAYOoQIewrn6mKOAjzzOXctMnXcPhi00e6Ru
+o0xkeXGMpyBbH2fYzolycOZoF6+uEU2/awKEmu/XaokPQec0ghjFKK3ug6dEW3Di
+kECHzIouyqyTK2tUcN8y/5YjB67Fu5wNJ1WpscDbNxDrSq+jBMtEQLze8LG0DZdW
+OSUrLcyx4SuhMg8KTBBFGCUC8G7+aLDj0ZM+G6tCwWGUbvsl49QSi4XZR13pVURv
+CTkbJSM6JLHtUDcvJKP/PlmiEQE83CsCggEAOIlm8zRHeJe3GdZAPIwV8EYCh7rd
+ghRjGIwzjylazzZyeHLQBKaIwNdlgOZG1HqTljdp4ZE0f+Oyh2lGOqQEqWjMBJPp
+yZG1jBCCsJMfTweWxzLhmzV4UUQ+MZQDjZ7uOKGYwN/rkhW30zZMenoZ+PywFhhl
+ldnPsQMBvqS03eCIaQveRkq8S/qA3uDGPjsEPTaYMJgw9/imsP8hW3z+kd7hH76R
+Guk98WHe3wvasV1RNOp5ixwUQgVmFJLrTVX+dfSKLw0XzekkG/1Uis7r2HXCfoaZ
+boHvICyW0kmhnsOVA7mWwByhUav9Ghevm9czVATTIGiA8lIZxFFbvr2KTg==
+-----END RSA PRIVATE KEY-----
diff --git a/apex/manifest.json b/apex/manifest.json
new file mode 100644
index 0000000..f3f5b02
--- /dev/null
+++ b/apex/manifest.json
@@ -0,0 +1,4 @@
+{
+ "name": "com.android.resolv",
+ "version": 299900000
+}
diff --git a/apex/testcert.pk8 b/apex/testcert.pk8
new file mode 100644
index 0000000..adfc589
--- /dev/null
+++ b/apex/testcert.pk8
Binary files differ
diff --git a/apex/testcert.x509.pem b/apex/testcert.x509.pem
new file mode 100644
index 0000000..cba30d6
--- /dev/null
+++ b/apex/testcert.x509.pem
@@ -0,0 +1,35 @@
+-----BEGIN CERTIFICATE-----
+MIIGADCCA+igAwIBAgIJAJiRMVvanGUaMA0GCSqGSIb3DQEBCwUAMIGUMQswCQYD
+VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4g
+VmlldzEQMA4GA1UECgwHQW5kcm9pZDEQMA4GA1UECwwHQW5kcm9pZDEQMA4GA1UE
+AwwHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAe
+Fw0xODEwMzAxMjEzNTFaFw00NjAzMTcxMjEzNTFaMIGUMQswCQYDVQQGEwJVUzET
+MBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzEQMA4G
+A1UECgwHQW5kcm9pZDEQMA4GA1UECwwHQW5kcm9pZDEQMA4GA1UEAwwHQW5kcm9p
+ZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCAiIwDQYJKoZI
+hvcNAQEBBQADggIPADCCAgoCggIBAL3ghKA8Gz9qOORY8gMY4wlB2tCJLDUO2tFG
+LVK1UphtQMp+YEcz/0VQKVV7de7z6V4EMQ5P1HbxHOsjcKn/zXAl4YgFt7b5kZbC
+bpNK4CYHEfho3j6fpYtq5d9q8rIA2kI0uZkkqPy1zXKTl2C2PjOoAnLQRk5xBVQG
+M10/wYsf7yX36mSWoJJwKPp/EzVFpA+hX8HpljeIiZ6CFzKwJdqv9zO/xzfp6NsX
+Tv5EGdkDxmw3qQqKgyl8dLMTZ/2zNfvVOMeZDusEPDF7A/lbU1byLWrKQdCzVb40
+yc7BCSRGYwM29R/byOcgD+lslwKSGzgzNmQXICt1tXz9bSJR8qh4tlAaiRc3ZKBe
+hJWIFGkGtD/cDGtDE5DbNAOz6CdSDdE2XN0Qf0cfN1RHVE6fo2FtFicRRVuFBt8M
+2cbQ7bzmEvtHD6W6dsf120FH7gppXKmnhMx1WazpxR2QltbiYDTy2ZZi4paS/jDB
+fL9gMCWp3Ohg2y74NGfUw5CQWQsDpcki6I7RvwClBCyOV51LHn5LE/nY4DkVrZxk
+Pw0/YrTWz5J5PbdMetTuIunE4ec4lm8nZnh1ET+2MHx2+RoyF5vBs4rp1KHHRaEA
+veD2AfQOWxz7kOG9+akFot7n+QoWEGdwY0mJ9jsO/IITCjv3VbD7o0OoJv1R2AW5
+sK2KQ4PDAgMBAAGjUzBRMB0GA1UdDgQWBBT2EbrayXGhY6VCvSlLtRNyjW9ceDAf
+BgNVHSMEGDAWgBT2EbrayXGhY6VCvSlLtRNyjW9ceDAPBgNVHRMBAf8EBTADAQH/
+MA0GCSqGSIb3DQEBCwUAA4ICAQC7SsWap9zDKmuR0qMUZ6wlualnag0hUG1jZHQP
+t63KO6LmNNMSuXRX60Zcq6WWzgLOyoT4HqHZZ47Jamfb4XQQcnWMMW0tJ3pDtTkz
+dZILBInHJO8QPYI8Du6XWsDLSvMajq6ueBtO3NdcgsNL7eiHf3WoOtajLZxFM94Z
+MESkUQOIsqHolYeTMHLTsuGkX1CK2Zw3Xn18bUSTYwZCHa6mYH00ItUBfetGCnWh
+Y7bth/R15Cc+hocSB7ZsOa/R5kDyDdFDIKrnV5nH5Yd7CryrYC6Ac5UarYrxSJTq
+eKPwqUlJB/tJW/lvdLt8YaURbFGzf/ZqU12zZRafYjmMjcQvfpzMoDSnbvHTA9IR
+ZGO7dwhwykoSaL4/8LWde49xQUq6F2pQBRmEr+7mTzml1MaM5cWEk5emkCMXgLog
+k+c56CAk1EdM1teWik7wR0TIqkkYyYJHTSg61GkXUIXrZJ6iYx2ejDg1+QTPm9rU
+Yr7nP52gVkQuUAX1+xB6wKLSDizQJw8SNiUGXl5+2vwV6+0BI3/CXlQ8I/nRPBC1
+oqOIkRSbE+IF7DP9QvYuNG/3bZZQ8LUVeHxqI5Mq8K2VIJZd95AIwPNMH34SaDGz
+9xjG28Fq4ZkuDP0pCsHM9d2XEwK5PEVS18WW5fJ/QcJKMno4IPTB70ZBBjVzv6Y+
+MYjOrw==
+-----END CERTIFICATE-----
diff --git a/bpf_progs/Android.bp b/bpf_progs/Android.bp
index 7b1f015..a393035 100644
--- a/bpf_progs/Android.bp
+++ b/bpf_progs/Android.bp
@@ -14,11 +14,6 @@
// limitations under the License.
//
-cc_library_headers {
- name: "netd_bpf_progs_headers",
- export_include_dirs: ["."],
-}
-
//
// bpf kernel programs
//
diff --git a/bpf_progs/bpf_net_helpers.h b/bpf_progs/bpf_net_helpers.h
deleted file mode 100644
index 714b7e6e..0000000
--- a/bpf_progs/bpf_net_helpers.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2019 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.
- */
-
-#ifndef NETDBPF_BPF_NET_HELPERS_H
-#define NETDBPF_BPF_NET_HELPERS_H
-
-#include <linux/bpf.h>
-#include <linux/if_packet.h>
-#include <stdbool.h>
-#include <stdint.h>
-
-// this returns 0 iff skb->sk is NULL
-static uint64_t (*bpf_get_socket_cookie)(struct __sk_buff* skb) = (void*)BPF_FUNC_get_socket_cookie;
-
-static uint32_t (*bpf_get_socket_uid)(struct __sk_buff* skb) = (void*)BPF_FUNC_get_socket_uid;
-static int (*bpf_skb_load_bytes)(struct __sk_buff* skb, int off, void* to,
- int len) = (void*)BPF_FUNC_skb_load_bytes;
-
-static int (*bpf_skb_change_proto)(struct __sk_buff* skb, __be16 proto,
- __u64 flags) = (void*)BPF_FUNC_skb_change_proto;
-static int (*bpf_l3_csum_replace)(struct __sk_buff* skb, __u32 offset, __u64 from, __u64 to,
- __u64 flags) = (void*)BPF_FUNC_l3_csum_replace;
-static int (*bpf_l4_csum_replace)(struct __sk_buff* skb, __u32 offset, __u64 from, __u64 to,
- __u64 flags) = (void*)BPF_FUNC_l4_csum_replace;
-static int (*bpf_redirect)(__u32 ifindex, __u64 flags) = (void*)BPF_FUNC_redirect;
-
-// Android only supports little endian architectures
-#define htons(x) (__builtin_constant_p(x) ? ___constant_swab16(x) : __builtin_bswap16(x))
-#define htonl(x) (__builtin_constant_p(x) ? ___constant_swab32(x) : __builtin_bswap32(x))
-#define ntohs(x) htons(x)
-#define ntohl(x) htonl(x)
-
-static inline __always_inline __unused bool is_received_skb(struct __sk_buff* skb) {
- return skb->pkt_type == PACKET_HOST || skb->pkt_type == PACKET_BROADCAST ||
- skb->pkt_type == PACKET_MULTICAST;
-}
-
-#endif // NETDBPF_BPF_NET_HELPERS_H
diff --git a/bpf_progs/clatd.c b/bpf_progs/clatd.c
index fb7acfa..578d244 100644
--- a/bpf_progs/clatd.c
+++ b/bpf_progs/clatd.c
@@ -23,16 +23,31 @@
#include <linux/ipv6.h>
#include <linux/pkt_cls.h>
#include <linux/swab.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
#include <stdbool.h>
#include <stdint.h>
#include "bpf_helpers.h"
-#include "bpf_net_helpers.h"
#include "netdbpf/bpf_shared.h"
+// bionic/libc/kernel/uapi/linux/udp.h:
+// struct __kernel_udphdr {
+// bionic/libc/kernel/tools/defaults.py:
+// # We want to support both BSD and Linux member names in struct udphdr.
+// "udphdr": "__kernel_udphdr",
+// so instead it just doesn't work... ugh.
+#define udphdr __kernel_udphdr
+
// From kernel:include/net/ip.h
#define IP_DF 0x4000 // Flag: "Don't Fragment"
+// Android only supports little endian architectures
+#define htons(x) (__builtin_constant_p(x) ? ___constant_swab16(x) : __builtin_bswap16(x))
+#define htonl(x) (__builtin_constant_p(x) ? ___constant_swab32(x) : __builtin_bswap32(x))
+#define ntohs(x) htons(x)
+#define ntohl(x) htonl(x)
+
DEFINE_BPF_MAP(clat_ingress_map, HASH, ClatIngressKey, ClatIngressValue, 16)
static inline __always_inline int nat64(struct __sk_buff* skb, bool is_ethernet) {
@@ -41,6 +56,8 @@
const void* data_end = (void*)(long)skb->data_end;
const struct ethhdr* const eth = is_ethernet ? data : NULL; // used iff is_ethernet
const struct ipv6hdr* const ip6 = is_ethernet ? (void*)(eth + 1) : data;
+ const struct tcphdr* const tcp = (void*)(ip6 + 1);
+ const struct udphdr* const udp = (void*)(ip6 + 1);
// Must be meta-ethernet IPv6 frame
if (skb->protocol != htons(ETH_P_IPV6)) return TC_ACT_OK;
@@ -58,10 +75,12 @@
if (ntohs(ip6->payload_len) > 0xFFFF - sizeof(struct iphdr)) return TC_ACT_OK;
switch (ip6->nexthdr) {
- case IPPROTO_TCP: // For TCP & UDP the checksum neutrality of the chosen IPv6
- case IPPROTO_UDP: // address means there is no need to update their checksums.
- case IPPROTO_GRE: // We do not need to bother looking at GRE/ESP headers,
- case IPPROTO_ESP: // since there is never a checksum to update.
+ case IPPROTO_TCP: // If TCP, must have 20 byte minimal TCP header
+ if (tcp + 1 > (struct tcphdr*)data_end) return TC_ACT_OK;
+ break;
+
+ case IPPROTO_UDP: // If UDP, must have 8 byte minimal UDP header
+ if (udp + 1 > (struct udphdr*)data_end) return TC_ACT_OK;
break;
default: // do not know how to handle anything else
@@ -116,9 +135,8 @@
// Note that there is no L4 checksum update: we are relying on the checksum neutrality
// of the ipv6 address chosen by netd's ClatdController.
- // Packet mutations begin - point of no return, but if this first modification fails
- // the packet is probably still pristine, so let clatd handle it.
- if (bpf_skb_change_proto(skb, htons(ETH_P_IP), 0)) return TC_ACT_OK;
+ // Packet mutations begin - point of no return.
+ if (bpf_skb_change_proto(skb, htons(ETH_P_IP), 0)) return TC_ACT_SHOT;
// bpf_skb_change_proto() invalidates all pointers - reload them.
data = (void*)(long)skb->data;
diff --git a/bpf_progs/netd.c b/bpf_progs/netd.c
index 3212314..1362be2 100644
--- a/bpf_progs/netd.c
+++ b/bpf_progs/netd.c
@@ -51,7 +51,9 @@
// that skb->sk is NULL during RX (early decap socket lookup failure),
// which commonly happens for incoming packets to an unconnected udp socket.
// Additionally bpf_get_socket_cookie() returns 0 if skb->sk is NULL
- if ((sock_uid == 65534) && !bpf_get_socket_cookie(skb) && is_received_skb(skb))
+ if ((sock_uid == 65534) && !bpf_get_socket_cookie(skb) &&
+ (skb->pkt_type == PACKET_HOST || skb->pkt_type == PACKET_BROADCAST ||
+ skb->pkt_type == PACKET_MULTICAST))
return BPF_MATCH;
UidOwnerValue* whitelistMatch = bpf_uid_owner_map_lookup_elem(&sock_uid);
diff --git a/bpf_progs/netd.h b/bpf_progs/netd.h
index be9aaee..8be21be 100644
--- a/bpf_progs/netd.h
+++ b/bpf_progs/netd.h
@@ -29,7 +29,6 @@
#include <linux/ipv6.h>
#include <stdbool.h>
#include <stdint.h>
-#include "bpf_net_helpers.h"
#include "netdbpf/bpf_shared.h"
typedef struct {
diff --git a/client/Android.bp b/client/Android.bp
index 7b51322..bb52430 100644
--- a/client/Android.bp
+++ b/client/Android.bp
@@ -21,10 +21,10 @@
header_libs: [
"libnetd_client_headers",
"libbase_headers", // for unique_fd.h
- "libnetd_resolv_headers",
],
export_header_lib_headers: ["libnetd_client_headers"],
include_dirs: [
+ "system/netd/resolv",
"system/netd/libnetdutils/include",
],
defaults: ["netd_defaults"],
@@ -38,10 +38,9 @@
defaults: ["netd_defaults"],
test_suites: ["device-tests"],
include_dirs: [
+ "system/netd/resolv",
"system/netd/include",
- ],
- header_libs: [
- "libnetd_resolv_headers",
+ "system/netd/libnetdutils/include",
],
static_libs: [
"libgmock",
diff --git a/client/NetdClient.cpp b/client/NetdClient.cpp
index d5945d0..a7dc0ad 100644
--- a/client/NetdClient.cpp
+++ b/client/NetdClient.cpp
@@ -130,7 +130,7 @@
const int ret = libcConnect(sockfd, addr, addrlen);
// Save errno so it isn't clobbered by sending ON_CONNECT_COMPLETE
const int connectErrno = errno;
- const auto latencyMs = static_cast<unsigned>(s.timeTakenUs() / 1000);
+ const unsigned latencyMs = lround(s.timeTaken());
// Send an ON_CONNECT_COMPLETE command that includes sockaddr and connect latency for reporting
if (shouldSetFwmark) {
FwmarkConnectInfo connectInfo(ret == 0 ? 0 : connectErrno, latencyMs, addr);
diff --git a/client/NetdClientTest.cpp b/client/NetdClientTest.cpp
index b523ccc..6f2fc09 100644
--- a/client/NetdClientTest.cpp
+++ b/client/NetdClientTest.cpp
@@ -36,10 +36,9 @@
pollfd fds[1] = {{.fd = dnsProxyFd, .events = POLLIN}};
enum { SERVERFD = 0 };
- int poll_result = TEMP_FAILURE_RETRY(poll(fds, std::size(fds), -1));
- ASSERT_GT(poll_result, 0);
+ const int s = TEMP_FAILURE_RETRY(poll(fds, std::size(fds), -1));
+ if (s <= 0) break;
- if (fds[SERVERFD].revents & POLLERR) return;
if (fds[SERVERFD].revents & POLLIN) {
char buf[4096];
TEMP_FAILURE_RETRY(read(fds[SERVERFD].fd, &buf, sizeof(buf)));
diff --git a/include/BinderUtil.h b/include/BinderUtil.h
index 6b41465..9333dcd 100644
--- a/include/BinderUtil.h
+++ b/include/BinderUtil.h
@@ -14,11 +14,8 @@
* limitations under the License.
*/
-#pragma once
-
-#include <android-base/stringprintf.h>
-#include <json/value.h>
-#include <json/writer.h>
+#ifndef NETD_INCLUDE_BINDER_UTIL_H
+#define NETD_INCLUDE_BINDER_UTIL_H
#ifdef ANDROID_BINDER_STATUS_H
#define IS_BINDER_OK(__ex__) (__ex__ == ::android::binder::Status::EX_NONE)
@@ -107,3 +104,5 @@
::android::base::StringPrintf(" <%.2fms>", logTransaction["duration_ms"].asFloat()));
logFn(output);
}
+
+#endif /* NETD_INCLUDE_BINDER_UTIL_H */
diff --git a/libnetdbpf/Android.bp b/libnetdbpf/Android.bp
index 574e7c3..b5fefac 100644
--- a/libnetdbpf/Android.bp
+++ b/libnetdbpf/Android.bp
@@ -26,6 +26,7 @@
"libbpf_android",
"liblog",
"libnetdutils",
+ "libutils",
],
export_include_dirs: ["include"],
defaults: ["netd_defaults"],
diff --git a/libnetdbpf/BpfNetworkStats.cpp b/libnetdbpf/BpfNetworkStats.cpp
index 30b43fa..fe86cc1 100644
--- a/libnetdbpf/BpfNetworkStats.cpp
+++ b/libnetdbpf/BpfNetworkStats.cpp
@@ -191,7 +191,7 @@
return ret;
}
- BpfMap<uint32_t, uint8_t> configurationMap(mapRetrieve(CONFIGURATION_MAP_PATH, BPF_OPEN_FLAGS));
+ BpfMap<uint32_t, uint8_t> configurationMap(mapRetrieve(CONFIGURATION_MAP_PATH, 0));
if (!configurationMap.isValid()) {
int ret = -errno;
ALOGE("get configuration map fd failed: %s", strerror(errno));
@@ -241,10 +241,7 @@
return netdutils::status::ok;
}
StatsKey fakeKey = {
- .uid = (uint32_t)UID_ALL,
- .tag = (uint32_t)TAG_NONE,
- .counterSet = (uint32_t)SET_ALL,
- };
+ .uid = (uint32_t)UID_ALL, .counterSet = (uint32_t)SET_ALL, .tag = (uint32_t)TAG_NONE};
lines->push_back(populateStatsEntry(fakeKey, value, ifname));
return netdutils::status::ok;
};
@@ -324,8 +321,6 @@
}
stats_line& stats_line::operator=(const stats_line& rhs) {
- if (this == &rhs) return *this;
-
strlcpy(iface, rhs.iface, sizeof(iface));
uid = rhs.uid;
set = rhs.set;
diff --git a/libnetdbpf/BpfNetworkStatsTest.cpp b/libnetdbpf/BpfNetworkStatsTest.cpp
index 057f2a9..8153127 100644
--- a/libnetdbpf/BpfNetworkStatsTest.cpp
+++ b/libnetdbpf/BpfNetworkStatsTest.cpp
@@ -201,10 +201,10 @@
SKIP_IF_BPF_NOT_SUPPORTED;
StatsValue value1 = {
- .rxPackets = 0,
.rxBytes = 0,
- .txPackets = 0,
+ .rxPackets = 0,
.txBytes = 0,
+ .txPackets = 0,
};
Stats result1 = {};
ASSERT_EQ(0, bpfGetUidStatsInternal(TEST_UID1, &result1, mFakeAppUidStatsMap));
@@ -217,17 +217,15 @@
updateIfaceMap(IFACE_NAME1, IFACE_INDEX1);
updateIfaceMap(IFACE_NAME2, IFACE_INDEX2);
updateIfaceMap(IFACE_NAME3, IFACE_INDEX3);
- StatsValue value1 = {
- .rxPackets = TEST_PACKET0,
- .rxBytes = TEST_BYTES0,
- .txPackets = TEST_PACKET1,
- .txBytes = TEST_BYTES1,
- };
+ StatsValue value1 = {.rxBytes = TEST_BYTES0,
+ .rxPackets = TEST_PACKET0,
+ .txBytes = TEST_BYTES1,
+ .txPackets = TEST_PACKET1,};
StatsValue value2 = {
- .rxPackets = TEST_PACKET0 * 2,
- .rxBytes = TEST_BYTES0 * 2,
- .txPackets = TEST_PACKET1 * 2,
- .txBytes = TEST_BYTES1 * 2,
+ .rxBytes = TEST_BYTES0 * 2,
+ .rxPackets = TEST_PACKET0 * 2,
+ .txBytes = TEST_BYTES1 * 2,
+ .txPackets = TEST_PACKET1 * 2,
};
ASSERT_TRUE(isOk(mFakeAppUidStatsMap.writeValue(TEST_UID1, value1, BPF_ANY)));
ASSERT_TRUE(isOk(mFakeAppUidStatsMap.writeValue(TEST_UID2, value2, BPF_ANY)));
@@ -260,16 +258,16 @@
updateIfaceMap(IFACE_NAME2, IFACE_INDEX2);
updateIfaceMap(IFACE_NAME3, IFACE_INDEX3);
StatsValue value1 = {
- .rxPackets = TEST_PACKET0,
- .rxBytes = TEST_BYTES0,
- .txPackets = TEST_PACKET1,
- .txBytes = TEST_BYTES1,
+ .rxBytes = TEST_BYTES0,
+ .rxPackets = TEST_PACKET0,
+ .txBytes = TEST_BYTES1,
+ .txPackets = TEST_PACKET1,
};
StatsValue value2 = {
- .rxPackets = TEST_PACKET1,
- .rxBytes = TEST_BYTES1,
- .txPackets = TEST_PACKET0,
- .txBytes = TEST_BYTES0,
+ .rxBytes = TEST_BYTES1,
+ .rxPackets = TEST_PACKET1,
+ .txBytes = TEST_BYTES0,
+ .txPackets = TEST_PACKET0,
};
uint32_t ifaceStatsKey = IFACE_INDEX1;
EXPECT_TRUE(isOk(mFakeIfaceStatsMap.writeValue(ifaceStatsKey, value1, BPF_ANY)));
@@ -290,10 +288,10 @@
ASSERT_EQ(0, bpfGetIfaceStatsInternal(NULL, &totalResult, mFakeIfaceStatsMap,
mFakeIfaceIndexNameMap));
StatsValue totalValue = {
- .rxPackets = TEST_PACKET0 * 2 + TEST_PACKET1,
- .rxBytes = TEST_BYTES0 * 2 + TEST_BYTES1,
- .txPackets = TEST_PACKET1 * 2 + TEST_PACKET0,
- .txBytes = TEST_BYTES1 * 2 + TEST_BYTES0,
+ .rxBytes = TEST_BYTES0 * 2 + TEST_BYTES1,
+ .rxPackets = TEST_PACKET0 * 2 + TEST_PACKET1,
+ .txBytes = TEST_BYTES1 * 2 + TEST_BYTES0,
+ .txPackets = TEST_PACKET1 * 2 + TEST_PACKET0,
};
expectStatsEqual(totalValue, totalResult);
}
@@ -303,12 +301,10 @@
updateIfaceMap(IFACE_NAME1, IFACE_INDEX1);
updateIfaceMap(IFACE_NAME2, IFACE_INDEX2);
- StatsValue value1 = {
- .rxPackets = TEST_PACKET0,
- .rxBytes = TEST_BYTES0,
- .txPackets = TEST_PACKET1,
- .txBytes = TEST_BYTES1,
- };
+ StatsValue value1 = {.rxBytes = TEST_BYTES0,
+ .rxPackets = TEST_PACKET0,
+ .txBytes = TEST_BYTES1,
+ .txPackets = TEST_PACKET1,};
populateFakeStats(TEST_UID1, TEST_TAG, IFACE_INDEX1, TEST_COUNTERSET0, value1, mFakeStatsMap);
populateFakeStats(TEST_UID1, TEST_TAG, IFACE_INDEX2, TEST_COUNTERSET0, value1, mFakeStatsMap);
populateFakeStats(TEST_UID1, TEST_TAG + 1, IFACE_INDEX1, TEST_COUNTERSET0, value1,
@@ -340,12 +336,10 @@
updateIfaceMap(IFACE_NAME1, IFACE_INDEX1);
updateIfaceMap(IFACE_NAME2, IFACE_INDEX2);
- StatsValue value1 = {
- .rxPackets = TEST_PACKET0,
- .rxBytes = TEST_BYTES0,
- .txPackets = TEST_PACKET1,
- .txBytes = TEST_BYTES1,
- };
+ StatsValue value1 = {.rxBytes = TEST_BYTES0,
+ .rxPackets = TEST_PACKET0,
+ .txBytes = TEST_BYTES1,
+ .txPackets = TEST_PACKET1,};
populateFakeStats(0, 0, 0, OVERFLOW_COUNTERSET, value1, mFakeStatsMap);
populateFakeStats(TEST_UID1, 0, IFACE_INDEX1, TEST_COUNTERSET0, value1, mFakeStatsMap);
populateFakeStats(TEST_UID1, 0, IFACE_INDEX2, TEST_COUNTERSET0, value1, mFakeStatsMap);
@@ -376,28 +370,22 @@
SKIP_IF_BPF_NOT_SUPPORTED;
updateIfaceMap(IFACE_NAME1, IFACE_INDEX1);
- StatsValue value1 = {
- .rxPackets = TEST_PACKET0,
- .rxBytes = TEST_BYTES0 * 20,
- .txPackets = TEST_PACKET1,
- .txBytes = TEST_BYTES1 * 20,
- };
+ StatsValue value1 = {.rxBytes = TEST_BYTES0 * 20,
+ .rxPackets = TEST_PACKET0,
+ .txBytes = TEST_BYTES1 * 20,
+ .txPackets = TEST_PACKET1,};
uint32_t ifaceIndex = UNKNOWN_IFACE;
populateFakeStats(TEST_UID1, 0, ifaceIndex, TEST_COUNTERSET0, value1, mFakeStatsMap);
populateFakeStats(TEST_UID1, 0, IFACE_INDEX1, TEST_COUNTERSET0, value1, mFakeStatsMap);
- StatsValue value2 = {
- .rxPackets = TEST_PACKET0,
- .rxBytes = TEST_BYTES0 * 40,
- .txPackets = TEST_PACKET1,
- .txBytes = TEST_BYTES1 * 40,
- };
+ StatsValue value2 = {.rxBytes = TEST_BYTES0 * 40,
+ .rxPackets = TEST_PACKET0,
+ .txBytes = TEST_BYTES1 * 40,
+ .txPackets = TEST_PACKET1,};
populateFakeStats(TEST_UID1, 0, IFACE_INDEX2, TEST_COUNTERSET0, value2, mFakeStatsMap);
- StatsKey curKey = {
- .uid = TEST_UID1,
- .tag = 0,
- .counterSet = TEST_COUNTERSET0,
- .ifaceIndex = ifaceIndex,
- };
+ StatsKey curKey = {.uid = TEST_UID1,
+ .tag = 0,
+ .ifaceIndex = ifaceIndex,
+ .counterSet = TEST_COUNTERSET0};
char ifname[IFNAMSIZ];
int64_t unknownIfaceBytesTotal = 0;
ASSERT_EQ(-ENODEV, getIfaceNameFromMap(mFakeIfaceIndexNameMap, mFakeStatsMap, ifaceIndex,
@@ -424,16 +412,16 @@
updateIfaceMap(IFACE_NAME3, IFACE_INDEX3);
updateIfaceMap(LONG_IFACE_NAME, IFACE_INDEX4);
StatsValue value1 = {
- .rxPackets = TEST_PACKET0,
- .rxBytes = TEST_BYTES0,
- .txPackets = TEST_PACKET1,
- .txBytes = TEST_BYTES1,
+ .rxBytes = TEST_BYTES0,
+ .rxPackets = TEST_PACKET0,
+ .txBytes = TEST_BYTES1,
+ .txPackets = TEST_PACKET1,
};
StatsValue value2 = {
- .rxPackets = TEST_PACKET1,
- .rxBytes = TEST_BYTES1,
- .txPackets = TEST_PACKET0,
- .txBytes = TEST_BYTES0,
+ .rxBytes = TEST_BYTES1,
+ .rxPackets = TEST_PACKET1,
+ .txBytes = TEST_BYTES0,
+ .txPackets = TEST_PACKET0,
};
uint32_t ifaceStatsKey = IFACE_INDEX1;
EXPECT_TRUE(isOk(mFakeIfaceStatsMap.writeValue(ifaceStatsKey, value1, BPF_ANY)));
@@ -464,22 +452,22 @@
updateIfaceMap(IFACE_NAME1, IFACE_INDEX3); // Duplicate!
StatsValue value1 = {
- .rxPackets = TEST_PACKET0,
.rxBytes = TEST_BYTES0,
- .txPackets = TEST_PACKET1,
+ .rxPackets = TEST_PACKET0,
.txBytes = TEST_BYTES1,
+ .txPackets = TEST_PACKET1,
};
StatsValue value2 = {
- .rxPackets = TEST_PACKET1,
.rxBytes = TEST_BYTES1,
- .txPackets = TEST_PACKET0,
+ .rxPackets = TEST_PACKET1,
.txBytes = TEST_BYTES0,
+ .txPackets = TEST_PACKET0,
};
StatsValue value3 = {
- .rxPackets = TEST_PACKET0 * 2,
.rxBytes = TEST_BYTES0 * 2,
- .txPackets = TEST_PACKET1 * 2,
+ .rxPackets = TEST_PACKET0 * 2,
.txBytes = TEST_BYTES1 * 2,
+ .txPackets = TEST_PACKET1 * 2,
};
std::vector<stats_line> lines;
@@ -553,10 +541,10 @@
updateIfaceMap(IFACE_NAME1, IFACE_INDEX1);
StatsValue value1 = {
- .rxPackets = TEST_PACKET0,
.rxBytes = TEST_BYTES0,
- .txPackets = TEST_PACKET1,
+ .rxPackets = TEST_PACKET0,
.txBytes = TEST_BYTES1,
+ .txPackets = TEST_PACKET1,
};
// Mutate uid, 0 < TEST_UID1 < INT_MAX < INT_MIN < UINT_MAX.
diff --git a/libnetdbpf/include/netdbpf/bpf_shared.h b/libnetdbpf/include/netdbpf/bpf_shared.h
index 933172a..66d20ec 100644
--- a/libnetdbpf/include/netdbpf/bpf_shared.h
+++ b/libnetdbpf/include/netdbpf/bpf_shared.h
@@ -87,7 +87,7 @@
IIF_MATCH = (1 << 5),
};
-enum BpfPermissionMatch {
+enum BpfPemissionMatch {
BPF_PERMISSION_INTERNET = 1 << 2,
BPF_PERMISSION_UPDATE_DEVICE_STATS = 1 << 3,
};
diff --git a/libnetdutils/AndroidTest.xml b/libnetdutils/AndroidTest.xml
new file mode 100644
index 0000000..dc13361
--- /dev/null
+++ b/libnetdutils/AndroidTest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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.
+-->
+<configuration description="Config for netdutils_test">
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="netdutils_test->/data/local/tmp/netdutils_test" />
+ </target_preparer>
+ <option name="test-suite-tag" value="apct" />
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="netdutils_test" />
+ </test>
+</configuration>
diff --git a/libnetdutils/InternetAddresses.cpp b/libnetdutils/InternetAddresses.cpp
index 78d30b2..944ed91 100644
--- a/libnetdutils/InternetAddresses.cpp
+++ b/libnetdutils/InternetAddresses.cpp
@@ -61,8 +61,9 @@
};
addrinfo* res;
const int ret = getaddrinfo(repr.c_str(), nullptr, &hints, &res);
- ScopedAddrinfo res_cleanup(res);
+ // TODO: move ScopedAddrinfo into libnetdutils and use it here.
if (ret != 0) {
+ freeaddrinfo(res);
return false;
}
@@ -83,6 +84,7 @@
break;
}
+ freeaddrinfo(res);
return rval;
}
diff --git a/libnetdutils/ThreadUtilTest.cpp b/libnetdutils/ThreadUtilTest.cpp
index 8fad8b8..2fe63b7 100644
--- a/libnetdutils/ThreadUtilTest.cpp
+++ b/libnetdutils/ThreadUtilTest.cpp
@@ -14,49 +14,22 @@
* limitations under the License.
*/
-#include <string>
-
-#include <android-base/expected.h>
#include <gtest/gtest.h>
-#include <netdutils/ThreadUtil.h>
-namespace android::netdutils {
+#include "netdutils/ThreadUtil.h"
+
+namespace android {
+namespace netdutils {
namespace {
-android::base::expected<std::string, int> getThreadName() {
- char name[16] = {};
- if (const int ret = pthread_getname_np(pthread_self(), name, sizeof(name)); ret != 0) {
- return android::base::unexpected(ret);
- }
- return std::string(name);
-}
-
class NoopRun {
public:
- explicit NoopRun(const std::string& name = "") : mName(name) { instanceNum++; }
-
- // Destructor happens in the thread.
- ~NoopRun() {
- if (checkName) {
- auto expected = getThreadName();
- EXPECT_TRUE(expected.has_value());
- EXPECT_EQ(mExpectedName, expected.value());
- }
- instanceNum--;
- }
+ NoopRun() { instanceNum++; }
+ ~NoopRun() { instanceNum--; }
void run() {}
- std::string threadName() { return mName; }
-
- // Set the expected thread name which will be used to check if it matches the actual thread
- // name which is returned from the system call. The check will happen in the destructor.
- void setExpectedName(const std::string& expectedName) {
- checkName = true;
- mExpectedName = expectedName;
- }
-
static bool waitForAllReleased(int timeoutMs) {
constexpr int intervalMs = 20;
int limit = timeoutMs / intervalMs;
@@ -71,11 +44,6 @@
// To track how many instances are alive.
static std::atomic<int> instanceNum;
-
- private:
- std::string mName;
- std::string mExpectedName;
- bool checkName = false;
};
std::atomic<int> NoopRun::instanceNum;
@@ -93,30 +61,5 @@
EXPECT_EQ(0, NoopRun::instanceNum);
}
-TEST(ThreadUtilTest, SetThreadName) {
- NoopRun::instanceNum = 0;
-
- // Test thread name empty.
- NoopRun* obj1 = new NoopRun();
- obj1->setExpectedName("");
-
- // Test normal case.
- NoopRun* obj2 = new NoopRun("TestName");
- obj2->setExpectedName("TestName");
-
- // Test thread name too long.
- std::string name("TestNameTooooLong");
- NoopRun* obj3 = new NoopRun(name);
- obj3->setExpectedName(name.substr(0, 15));
-
- // Thread names are examined in their destructors.
- EXPECT_EQ(3, NoopRun::instanceNum);
- threadLaunch(obj1);
- threadLaunch(obj2);
- threadLaunch(obj3);
-
- EXPECT_TRUE(NoopRun::waitForAllReleased(1000));
- EXPECT_EQ(0, NoopRun::instanceNum);
-}
-
-} // namespace android::netdutils
+} // namespace netdutils
+} // namespace android
diff --git a/libnetdutils/include/netdutils/InternetAddresses.h b/libnetdutils/include/netdutils/InternetAddresses.h
index e817b78..07a6e90 100644
--- a/libnetdutils/include/netdutils/InternetAddresses.h
+++ b/libnetdutils/include/netdutils/InternetAddresses.h
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-#pragma once
+#ifndef NETDUTILS_INTERNETADDRESSES_H_
+#define NETDUTILS_INTERNETADDRESSES_H_
-#include <netdb.h>
#include <netinet/in.h>
#include <stdint.h>
#include <cstring>
@@ -104,16 +104,6 @@
} // namespace internal_
-struct AddrinfoDeleter {
- void operator()(struct addrinfo* p) const {
- if (p != nullptr) {
- freeaddrinfo(p);
- }
- }
-};
-
-typedef std::unique_ptr<struct addrinfo, struct AddrinfoDeleter> ScopedAddrinfo;
-
inline bool usesScopedIds(const in6_addr& ipv6) {
return (IN6_IS_ADDR_LINKLOCAL(&ipv6) || IN6_IS_ADDR_MC_LINKLOCAL(&ipv6));
}
@@ -242,23 +232,6 @@
public:
// TODO: static forString
- static IPSockAddr toIPSockAddr(const std::string& repr, in_port_t port) {
- return IPSockAddr(IPAddress::forString(repr), port);
- }
- static IPSockAddr toIPSockAddr(const sockaddr& sa) {
- switch (sa.sa_family) {
- case AF_INET:
- return IPSockAddr(*reinterpret_cast<const sockaddr_in*>(&sa));
- case AF_INET6:
- return IPSockAddr(*reinterpret_cast<const sockaddr_in6*>(&sa));
- default:
- return IPSockAddr();
- }
- }
- static IPSockAddr toIPSockAddr(const sockaddr_storage& ss) {
- return toIPSockAddr(*reinterpret_cast<const sockaddr*>(&ss));
- }
-
IPSockAddr() = default;
IPSockAddr(const IPSockAddr&) = default;
IPSockAddr(IPSockAddr&&) = default;
@@ -318,3 +291,5 @@
} // namespace netdutils
} // namespace android
+
+#endif // NETDUTILS_INTERNETADDRESSES_H_
diff --git a/libnetdutils/include/netdutils/NetworkConstants.h b/libnetdutils/include/netdutils/NetworkConstants.h
index dead9a1..682ceaf 100644
--- a/libnetdutils/include/netdutils/NetworkConstants.h
+++ b/libnetdutils/include/netdutils/NetworkConstants.h
@@ -14,7 +14,8 @@
* limitations under the License.
*/
-#pragma once
+#ifndef NETDUTILS_NETWORKCONSTANTS_H_
+#define NETDUTILS_NETWORKCONSTANTS_H_
namespace android {
namespace netdutils {
@@ -25,8 +26,7 @@
constexpr int IPV6_ADDR_LEN = 16;
constexpr int IPV6_ADDR_BITS = 128;
-// Referred from SHA256_DIGEST_LENGTH in boringssl
-constexpr size_t SHA256_SIZE = 32;
-
} // namespace netdutils
} // namespace android
+
+#endif // NETDUTILS_NETWORKCONSTANTS_H_
diff --git a/libnetdutils/include/netdutils/Stopwatch.h b/libnetdutils/include/netdutils/Stopwatch.h
index e7b4326..18e2050 100644
--- a/libnetdutils/include/netdutils/Stopwatch.h
+++ b/libnetdutils/include/netdutils/Stopwatch.h
@@ -29,12 +29,22 @@
public:
Stopwatch() : mStart(clock::now()) {}
+
virtual ~Stopwatch() = default;
+ float timeTaken() const { return getElapsed(clock::now()); }
+
int64_t timeTakenUs() const { return getElapsedUs(clock::now()); }
- int64_t getTimeAndResetUs() {
+
+ float getTimeAndReset() {
const auto& now = clock::now();
- int64_t elapsed = getElapsedUs(now);
+ float elapsed = getElapsed(now);
+ mStart = now;
+ return elapsed;
+ }
+ float getTimeAndResetUs() {
+ const auto& now = clock::now();
+ float elapsed = getElapsedUs(now);
mStart = now;
return elapsed;
}
@@ -42,6 +52,10 @@
private:
time_point mStart;
+ float getElapsed(const time_point& now) const {
+ using ms = std::chrono::duration<float, std::ratio<1, 1000>>;
+ return (std::chrono::duration_cast<ms>(now - mStart)).count();
+ }
int64_t getElapsedUs(const time_point& now) const {
return (std::chrono::duration_cast<std::chrono::microseconds>(now - mStart)).count();
}
diff --git a/libnetdutils/include/netdutils/ThreadUtil.h b/libnetdutils/include/netdutils/ThreadUtil.h
index 62e6f70..2ef97ef 100644
--- a/libnetdutils/include/netdutils/ThreadUtil.h
+++ b/libnetdutils/include/netdutils/ThreadUtil.h
@@ -34,22 +34,9 @@
pthread_attr_t attr;
};
-inline void setThreadName(std::string name) {
- // MAX_TASK_COMM_LEN=16 is not exported by bionic.
- const size_t MAX_TASK_COMM_LEN = 16;
-
- // Crop name to 16 bytes including the NUL byte, as required by pthread_setname_np()
- if (name.size() >= MAX_TASK_COMM_LEN) name.resize(MAX_TASK_COMM_LEN - 1);
-
- if (int ret = pthread_setname_np(pthread_self(), name.c_str()); ret != 0) {
- LOG(WARNING) << "Unable to set thread name to " << name << ": " << strerror(ret);
- }
-}
-
template <typename T>
inline void* runAndDelete(void* obj) {
std::unique_ptr<T> handler(reinterpret_cast<T*>(obj));
- setThreadName(handler->threadName().c_str());
handler->run();
return nullptr;
}
diff --git a/resolv/Android.bp b/resolv/Android.bp
new file mode 100644
index 0000000..d1bf2e1
--- /dev/null
+++ b/resolv/Android.bp
@@ -0,0 +1,223 @@
+cc_library_headers {
+ name: "libnetd_resolv_headers",
+ export_include_dirs: ["include"],
+}
+
+aidl_interface {
+ name: "dnsresolver_aidl_interface",
+ local_include_dir: "binder",
+ srcs: [
+ "binder/android/net/IDnsResolver.aidl",
+ "binder/android/net/ResolverParamsParcel.aidl",
+ ],
+ imports: [
+ "netd_event_listener_interface",
+ ],
+ backend: {
+ ndk: {
+ gen_log: true,
+ },
+ },
+ api_dir: "aidl/dnsresolver",
+ versions: [
+ "1",
+ "2",
+ ],
+}
+
+cc_test_library {
+ name: "libnetd_test_metrics_listener",
+ defaults: ["netd_defaults"],
+ srcs: [
+ "tests/BaseTestMetricsListener.cpp",
+ "tests/TestMetrics.cpp",
+ ],
+ include_dirs: [
+ "system/netd/include",
+ ],
+ shared_libs: [
+ "libbinder",
+ "libutils",
+ "netd_event_listener_interface-V1-cpp",
+ ],
+}
+
+cc_library {
+ name: "libnetd_resolv",
+ version_script: "libnetd_resolv.map.txt",
+ defaults: ["netd_defaults"],
+ srcs: [
+ "getaddrinfo.cpp",
+ "gethnamaddr.cpp",
+ "sethostent.cpp",
+ "res_cache.cpp",
+ "res_comp.cpp",
+ "res_debug.cpp",
+ "res_init.cpp",
+ "res_mkquery.cpp",
+ "res_query.cpp",
+ "res_send.cpp",
+ "res_state.cpp",
+ "res_stats.cpp",
+ "Dns64Configuration.cpp",
+ "DnsProxyListener.cpp",
+ "DnsResolver.cpp",
+ "DnsResolverService.cpp",
+ "DnsTlsDispatcher.cpp",
+ "DnsTlsQueryMap.cpp",
+ "DnsTlsTransport.cpp",
+ "DnsTlsServer.cpp",
+ "DnsTlsSessionCache.cpp",
+ "DnsTlsSocket.cpp",
+ "PrivateDnsConfiguration.cpp",
+ "ResolverController.cpp",
+ "ResolverEventReporter.cpp",
+ ],
+ // Link everything statically (except for libc) to minimize our dependence
+ // on system ABIs
+ stl: "libc++_static",
+ static_libs: [
+ "libbase",
+ "libcrypto",
+ "libcutils",
+ "libjsoncpp",
+ "liblog",
+ "libnetdutils",
+ "libssl",
+ "libstatslog_resolv",
+ "libstatssocket",
+ "libsysutils",
+ "libutils",
+ "netd_event_listener_interface-ndk_platform",
+ "dnsresolver_aidl_interface-ndk_platform",
+ "server_configurable_flags",
+ "stats_proto",
+ "libprotobuf-cpp-lite",
+ ],
+ shared_libs: [
+ "libbinder_ndk",
+ ],
+ include_dirs: [
+ "system/netd/include",
+ "system/netd/server",
+ ],
+ export_include_dirs: ["include"],
+ // TODO: pie in the sky: make this code clang-tidy clean
+ tidy: false,
+ product_variables: {
+ debuggable: {
+ cppflags: [
+ "-DRESOLV_ALLOW_VERBOSE_LOGGING=1",
+ ],
+ },
+ },
+}
+
+cc_library_static {
+ name: "stats_proto",
+ defaults: ["netd_defaults"],
+ proto: {
+ export_proto_headers: true,
+ type: "lite",
+ },
+ srcs: [
+ "stats.proto",
+ ],
+}
+
+genrule {
+ name: "statslog_resolv.h",
+ tools: ["stats-log-api-gen"],
+ cmd: "$(location stats-log-api-gen) --header $(genDir)/statslog_resolv.h --module resolv --namespace android,net,stats",
+ out: [
+ "statslog_resolv.h",
+ ],
+}
+
+genrule {
+ name: "statslog_resolv.cpp",
+ tools: ["stats-log-api-gen"],
+ cmd: "$(location stats-log-api-gen) --cpp $(genDir)/statslog_resolv.cpp --module resolv --namespace android,net,stats --importHeader statslog_resolv.h",
+ out: [
+ "statslog_resolv.cpp",
+ ],
+}
+
+cc_library_static {
+ name: "libstatslog_resolv",
+ generated_sources: ["statslog_resolv.cpp"],
+ generated_headers: ["statslog_resolv.h"],
+ defaults: ["netd_defaults"],
+ export_generated_headers: ["statslog_resolv.h"],
+ static_libs: [
+ "libcutils",
+ "liblog",
+ "libstatssocket",
+ "libutils",
+ ],
+}
+
+cc_test {
+ name: "resolv_integration_test",
+ test_suites: ["device-tests"],
+ require_root: true,
+ defaults: ["netd_defaults"],
+ srcs: [
+ "dns_responder/dns_responder.cpp",
+ "resolver_test.cpp",
+ "dnsresolver_binder_test.cpp",
+ ],
+ include_dirs: [
+ "system/netd/resolv/include",
+ "system/netd/server",
+ ],
+ shared_libs: [
+ "libbase",
+ "libbinder",
+ "libcrypto",
+ "liblog",
+ "libnetd_client",
+ "libssl",
+ "libutils",
+ ],
+ static_libs: [
+ "libgmock",
+ "libnetd_test_dnsresponder",
+ "libnetd_test_metrics_listener",
+ "libnetd_test_tun_interface",
+ "liblogwrap",
+ "libnetdutils",
+ "netd_aidl_interface-V2-cpp",
+ "netd_event_listener_interface-V1-cpp",
+ "dnsresolver_aidl_interface-V2-cpp",
+ ],
+ tidy: false,
+ compile_multilib: "both",
+}
+
+cc_test {
+ name: "resolv_unit_test",
+ test_suites: ["device-tests"],
+ defaults: ["netd_defaults"],
+ srcs: [
+ "dns_tls_test.cpp",
+ "libnetd_resolv_test.cpp",
+ "res_cache_test.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ "libcrypto",
+ "libcutils",
+ "liblog",
+ "libssl",
+ ],
+ static_libs: [
+ "libgmock",
+ "libnetd_resolv",
+ "libnetd_test_dnsresponder",
+ "libnetdutils",
+ "libprotobuf-cpp-lite",
+ "server_configurable_flags",
+ "stats_proto",
+ ],
+}
diff --git a/resolv/AndroidTest.xml b/resolv/AndroidTest.xml
new file mode 100644
index 0000000..f5715c9
--- /dev/null
+++ b/resolv/AndroidTest.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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 test config file is auto-generated. -->
+<configuration description="Config for resolv_integration_test.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-native" />
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="resolv_integration_test->/data/local/tmp/resolv_integration_test" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="resolv_integration_test" />
+ <!--
+ On 2018-12-12, GetAddrInfoStressTest_Binder_100 suddenly jumped
+ from ~1xs to ~70s runtime in APCT continuous integration, causing
+ resolv_integration_test to flake with the default 60s timeout.
+ We're not sure what caused the regression, but it's not due to a change
+ in the Android image and unlikely to affect users.
+ Just bump the timeout to 120s for now.
+ -->
+ <option name="native-test-timeout" value="120000" />
+ </test>
+</configuration>
diff --git a/resolv/Dns64Configuration.cpp b/resolv/Dns64Configuration.cpp
new file mode 100644
index 0000000..a1dfdca
--- /dev/null
+++ b/resolv/Dns64Configuration.cpp
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#define LOG_TAG "Dns64Configuration"
+#define DBG 0
+
+#include "Dns64Configuration.h"
+
+#include <log/log.h>
+#include <netdb.h>
+#include <thread>
+#include <utility>
+
+#include <arpa/inet.h>
+
+#include "DnsResolver.h"
+#include "NetdConstants.h" // ScopedAddrinfo
+#include "getaddrinfo.h"
+#include "netd_resolv/resolv.h"
+#include "netdutils/BackoffSequence.h"
+#include "netdutils/DumpWriter.h"
+#include "netid_client.h"
+#include "stats.pb.h"
+
+namespace android {
+
+using android::net::NetworkDnsEventReported;
+using netdutils::DumpWriter;
+using netdutils::IPAddress;
+using netdutils::IPPrefix;
+
+namespace net {
+
+const char Dns64Configuration::kIPv4OnlyHost[] = "ipv4only.arpa.";
+const char Dns64Configuration::kIPv4Literal1[] = "192.0.0.170";
+const char Dns64Configuration::kIPv4Literal2[] = "192.0.0.171";
+
+void Dns64Configuration::startPrefixDiscovery(unsigned netId) {
+ std::lock_guard guard(mMutex);
+
+ // TODO: Keep previous prefix for a while
+ // Currently, we remove current prefix, if any, before starting a prefix discovery.
+ // This causes that Netd and framework temporarily forgets DNS64 prefix even the prefix may be
+ // discovered in a short time.
+ removeDns64Config(netId);
+
+ Dns64Config cfg(getNextId(), netId);
+ // Emplace a copy of |cfg| in the map.
+ mDns64Configs.emplace(std::make_pair(netId, cfg));
+
+ // Note that capturing |cfg| in this lambda creates a copy.
+ std::thread discovery_thread([this, cfg] {
+ // Make a mutable copy rather than mark the whole lambda mutable.
+ // No particular reason.
+ Dns64Config evalCfg(cfg);
+
+ auto backoff = netdutils::BackoffSequence<>::Builder()
+ .withInitialRetransmissionTime(std::chrono::seconds(1))
+ .withMaximumRetransmissionTime(std::chrono::seconds(3600))
+ .build();
+
+ while (true) {
+ if (!this->shouldContinueDiscovery(evalCfg)) break;
+
+ android_net_context netcontext{};
+ mGetNetworkContextCallback(evalCfg.netId, 0, &netcontext);
+
+ // Prefix discovery must bypass private DNS because in strict mode
+ // the server generally won't know the NAT64 prefix.
+ netcontext.flags |= NET_CONTEXT_FLAG_USE_LOCAL_NAMESERVERS;
+ if (doRfc7050PrefixDiscovery(netcontext, &evalCfg)) {
+ this->recordDns64Config(evalCfg);
+ break;
+ }
+
+ if (!this->shouldContinueDiscovery(evalCfg)) break;
+
+ if (!backoff.hasNextTimeout()) break;
+ {
+ std::unique_lock<std::mutex> cvGuard(mMutex);
+ // TODO: Consider some chrono math, combined with wait_until()
+ // perhaps, to prevent early re-resolves from the removal of
+ // other netids with IPv6-only nameservers.
+ mCv.wait_for(cvGuard, backoff.getNextTimeout());
+ }
+ }
+ });
+ discovery_thread.detach();
+}
+
+void Dns64Configuration::stopPrefixDiscovery(unsigned netId) {
+ std::lock_guard guard(mMutex);
+ removeDns64Config(netId);
+ mCv.notify_all();
+}
+
+IPPrefix Dns64Configuration::getPrefix64Locked(unsigned netId) const REQUIRES(mMutex) {
+ const auto& iter = mDns64Configs.find(netId);
+ if (iter != mDns64Configs.end()) return iter->second.prefix64;
+
+ return IPPrefix{};
+}
+
+IPPrefix Dns64Configuration::getPrefix64(unsigned netId) const {
+ std::lock_guard guard(mMutex);
+ return getPrefix64Locked(netId);
+}
+
+void Dns64Configuration::dump(DumpWriter& dw, unsigned netId) {
+ static const char kLabel[] = "DNS64 config";
+
+ std::lock_guard guard(mMutex);
+
+ const auto& iter = mDns64Configs.find(netId);
+ if (iter == mDns64Configs.end()) {
+ dw.println("%s: none", kLabel);
+ return;
+ }
+
+ const Dns64Config& cfg = iter->second;
+ if (cfg.prefix64.length() == 0) {
+ dw.println("%s: no prefix yet discovered", kLabel);
+ } else {
+ dw.println("%s: discovered prefix %s", kLabel, cfg.prefix64.toString().c_str());
+ }
+}
+
+// NOTE: The full RFC 7050 DNS64 discovery process is not implemented here.
+// Instead, and more simplistic version of the same thing is done, and it
+// currently assumes the DNS64 prefix is a /96.
+bool Dns64Configuration::doRfc7050PrefixDiscovery(const android_net_context& netcontext,
+ Dns64Config* cfg) {
+ ALOGW("(%u, %u) Detecting NAT64 prefix from DNS...", cfg->netId, cfg->discoveryId);
+
+ const struct addrinfo hints = {
+ .ai_family = AF_INET6,
+ };
+
+ // TODO: Refactor so that netd can get all the regular getaddrinfo handling
+ // that regular apps get. We bypass the UNIX socket connection back to
+ // ourselves, which means we also bypass all the special netcontext flag
+ // handling and the resolver event logging.
+ struct addrinfo* res = nullptr;
+ NetworkDnsEventReported event;
+ const int status = android_getaddrinfofornetcontext(kIPv4OnlyHost, nullptr, &hints, &netcontext,
+ &res, &event);
+ ScopedAddrinfo result(res);
+ if (status != 0) {
+ ALOGW("(%u, %u) plat_prefix/dns(%s) status = %d/%s", cfg->netId, cfg->discoveryId,
+ kIPv4OnlyHost, status, gai_strerror(status));
+ return false;
+ }
+
+ // Use only the first result. If other records are present, possibly
+ // with differing DNS64 prefixes they are ignored. Note that this is a
+ // violation of https://tools.ietf.org/html/rfc7050#section-3
+ //
+ // "A node MUST look through all of the received AAAA resource records
+ // to collect one or more Pref64::/n."
+ //
+ // TODO: Consider remedying this.
+ if (result->ai_family != AF_INET6) {
+ ALOGW("(%u, %u) plat_prefix/unexpected address family: %d", cfg->netId, cfg->discoveryId,
+ result->ai_family);
+ return false;
+ }
+ const IPAddress ipv6(reinterpret_cast<sockaddr_in6*>(result->ai_addr)->sin6_addr);
+ // Only /96 DNS64 prefixes are supported at this time.
+ cfg->prefix64 = IPPrefix(ipv6, 96);
+
+ ALOGW("(%u, %u) Detected NAT64 prefix %s", cfg->netId, cfg->discoveryId,
+ cfg->prefix64.toString().c_str());
+
+ return true;
+}
+
+bool Dns64Configuration::isDiscoveryInProgress(const Dns64Config& cfg) const REQUIRES(mMutex) {
+ const auto& iter = mDns64Configs.find(cfg.netId);
+ if (iter == mDns64Configs.end()) return false;
+
+ const Dns64Config& currentCfg = iter->second;
+ return (currentCfg.discoveryId == cfg.discoveryId);
+}
+
+bool Dns64Configuration::reportNat64PrefixStatus(unsigned netId, bool added, const IPPrefix& pfx) {
+ if (pfx.ip().family() != AF_INET6 || pfx.ip().scope_id() != 0) {
+ ALOGW("Abort to send NAT64 prefix notification. Unexpected NAT64 prefix (%u, %d, %s).",
+ netId, added, pfx.toString().c_str());
+ return false;
+ }
+ Nat64PrefixInfo args = {netId, added, pfx.ip().toString(), (uint8_t)pfx.length()};
+ mPrefixCallback(args);
+ return true;
+}
+
+bool Dns64Configuration::shouldContinueDiscovery(const Dns64Config& cfg) {
+ std::lock_guard guard(mMutex);
+ return isDiscoveryInProgress(cfg);
+}
+
+void Dns64Configuration::removeDns64Config(unsigned netId) REQUIRES(mMutex) {
+ IPPrefix prefix = getPrefix64Locked(netId);
+ mDns64Configs.erase(netId);
+ if (!prefix.isUninitialized()) {
+ reportNat64PrefixStatus(netId, PREFIX_REMOVED, prefix);
+ }
+}
+
+void Dns64Configuration::recordDns64Config(const Dns64Config& cfg) {
+ std::lock_guard guard(mMutex);
+ if (!isDiscoveryInProgress(cfg)) return;
+
+ removeDns64Config(cfg.netId);
+ mDns64Configs.emplace(std::make_pair(cfg.netId, cfg));
+
+ reportNat64PrefixStatus(cfg.netId, PREFIX_ADDED, cfg.prefix64);
+
+ // TODO: consider extending INetdEventListener to report the DNS64 prefix
+ // up to ConnectivityService to potentially include this in LinkProperties.
+}
+
+} // namespace net
+} // namespace android
diff --git a/resolv/Dns64Configuration.h b/resolv/Dns64Configuration.h
new file mode 100644
index 0000000..58b115e
--- /dev/null
+++ b/resolv/Dns64Configuration.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#ifndef DNS_DNS64CONFIGURATION_H_
+#define DNS_DNS64CONFIGURATION_H_
+
+#include <netinet/in.h>
+#include <condition_variable>
+#include <cstdlib>
+#include <mutex>
+#include <unordered_map>
+
+#include <android-base/thread_annotations.h>
+#include "netdutils/DumpWriter.h"
+#include "netdutils/InternetAddresses.h"
+
+struct android_net_context;
+
+namespace android {
+namespace net {
+
+/**
+ * This class handles RFC 7050 -style DNS64 prefix discovery.
+ *
+ * The ResolverController starts DNS64 prefix discovery when it observes a
+ * a network with only IPv6 nameservers. (It stops discovery whenever an IPv4
+ * nameserver is added or the network is deleted.)
+ *
+ * Each time prefix discovery is started, a new discoveryId is generated so
+ * that running resolution threads can notice they are no longer the most
+ * recent resolution attempt. This results in the backoff schedule of resolution
+ * being reset.
+ *
+ * Thread-safety: All public methods in this class MUST be thread-safe.
+ * (In other words: this class handles all its locking privately.)
+ */
+class Dns64Configuration {
+ public:
+ // Simple data struct for passing back packet NAT64 prefix event information to the
+ // Dns64PrefixCallback callback.
+ struct Nat64PrefixInfo {
+ unsigned netId;
+ bool added;
+ std::string prefixString;
+ uint8_t prefixLength;
+ };
+
+ // Callback that is triggered for every NAT64 prefix event.
+ using Nat64PrefixCallback = std::function<void(const Nat64PrefixInfo&)>;
+
+ using GetNetworkContextCallback = std::function<void(uint32_t, uint32_t, android_net_context*)>;
+
+ // Parameters from RFC 7050 section 8.
+ static const char kIPv4OnlyHost[]; // "ipv4only.arpa."
+ static const char kIPv4Literal1[]; // 192.0.0.170
+ static const char kIPv4Literal2[]; // 192.0.0.171
+
+ Dns64Configuration() = delete;
+ Dns64Configuration(GetNetworkContextCallback getNetworkCallback,
+ Nat64PrefixCallback prefixCallback)
+ : mGetNetworkContextCallback(std::move(getNetworkCallback)),
+ mPrefixCallback(std::move(prefixCallback)) {}
+ Dns64Configuration(const Dns64Configuration&) = delete;
+ Dns64Configuration(Dns64Configuration&&) = delete;
+ Dns64Configuration& operator=(const Dns64Configuration&) = delete;
+ Dns64Configuration& operator=(Dns64Configuration&&) = delete;
+
+ void startPrefixDiscovery(unsigned netId);
+ void stopPrefixDiscovery(unsigned netId);
+ netdutils::IPPrefix getPrefix64(unsigned netId) const;
+
+ void dump(netdutils::DumpWriter& dw, unsigned netId);
+
+ private:
+ struct Dns64Config {
+ Dns64Config(unsigned pseudoRandomId, unsigned network)
+ : discoveryId(pseudoRandomId), netId(network) {}
+
+ const unsigned int discoveryId;
+ const unsigned int netId;
+ netdutils::IPPrefix prefix64{};
+ };
+
+ enum { PREFIX_REMOVED, PREFIX_ADDED };
+
+ static bool doRfc7050PrefixDiscovery(const android_net_context& netcontext, Dns64Config* cfg);
+
+ unsigned getNextId() REQUIRES(mMutex) { return mNextId++; }
+ netdutils::IPPrefix getPrefix64Locked(unsigned netId) const REQUIRES(mMutex);
+ bool isDiscoveryInProgress(const Dns64Config& cfg) const REQUIRES(mMutex);
+ bool reportNat64PrefixStatus(unsigned netId, bool added, const netdutils::IPPrefix& pfx)
+ REQUIRES(mMutex);
+
+ bool shouldContinueDiscovery(const Dns64Config& cfg);
+ void recordDns64Config(const Dns64Config& cfg);
+ void removeDns64Config(unsigned netId) REQUIRES(mMutex);
+
+ mutable std::mutex mMutex;
+ std::condition_variable mCv;
+ unsigned int mNextId GUARDED_BY(mMutex);
+ std::unordered_map<unsigned, Dns64Config> mDns64Configs GUARDED_BY(mMutex);
+ const GetNetworkContextCallback mGetNetworkContextCallback;
+ const Nat64PrefixCallback mPrefixCallback;
+};
+
+} // namespace net
+} // namespace android
+
+#endif // DNS_DNS64CONFIGURATION_H_
diff --git a/resolv/DnsProxyListener.cpp b/resolv/DnsProxyListener.cpp
new file mode 100644
index 0000000..8925bc4
--- /dev/null
+++ b/resolv/DnsProxyListener.cpp
@@ -0,0 +1,1259 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#include "DnsProxyListener.h"
+
+#include <arpa/inet.h>
+#include <dirent.h>
+#include <errno.h>
+#include <linux/if.h>
+#include <math.h>
+#include <net/if.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#define LOG_TAG "DnsProxyListener"
+
+#include <algorithm>
+#include <vector>
+
+#include <android-base/stringprintf.h>
+#include <android/multinetwork.h> // ResNsendFlags
+#include <cutils/misc.h> // FIRST_APPLICATION_UID
+#include <netdutils/InternetAddresses.h>
+#include <netdutils/OperationLimiter.h>
+#include <netdutils/ResponseCode.h>
+#include <netdutils/Slice.h>
+#include <netdutils/Stopwatch.h>
+#include <netdutils/ThreadUtil.h>
+#include <private/android_filesystem_config.h> // AID_SYSTEM
+#include <statslog_resolv.h>
+#include <sysutils/SocketClient.h>
+
+#include "DnsResolver.h"
+#include "NetdClient.h" // NETID_USE_LOCAL_NAMESERVERS
+#include "NetdPermissions.h"
+#include "PrivateDnsConfiguration.h"
+#include "ResolverEventReporter.h"
+#include "getaddrinfo.h"
+#include "gethnamaddr.h"
+#include "netd_resolv/stats.h" // RCODE_TIMEOUT
+#include "res_send.h"
+#include "resolv_cache.h"
+#include "resolv_private.h"
+#include "stats.pb.h"
+
+using aidl::android::net::metrics::INetdEventListener;
+using android::net::NetworkDnsEventReported;
+
+namespace android {
+
+using netdutils::ResponseCode;
+using netdutils::Stopwatch;
+
+namespace net {
+namespace {
+
+// Limits the number of outstanding DNS queries by client UID.
+constexpr int MAX_QUERIES_PER_UID = 256;
+
+// Max packet size for answer, sync with getaddrinfo.c
+constexpr int MAXPACKET = 8 * 1024;
+
+android::netdutils::OperationLimiter<uid_t> queryLimiter(MAX_QUERIES_PER_UID);
+
+void logArguments(int argc, char** argv) {
+ if (!WOULD_LOG(VERBOSE)) return;
+ for (int i = 0; i < argc; i++) {
+ LOG(VERBOSE) << __func__ << ": argv[" << i << "]=" << (argv[i] ? argv[i] : "null");
+ }
+}
+
+template<typename T>
+void tryThreadOrError(SocketClient* cli, T* handler) {
+ cli->incRef();
+
+ const int rval = netdutils::threadLaunch(handler);
+ if (rval == 0) {
+ // SocketClient decRef() happens in the handler's run() method.
+ return;
+ }
+
+ char* msg = nullptr;
+ asprintf(&msg, "%s (%d)", strerror(-rval), -rval);
+ cli->sendMsg(ResponseCode::OperationFailed, msg, false);
+ free(msg);
+
+ delete handler;
+ cli->decRef();
+}
+
+bool checkAndClearUseLocalNameserversFlag(unsigned* netid) {
+ if (netid == nullptr || ((*netid) & NETID_USE_LOCAL_NAMESERVERS) == 0) {
+ return false;
+ }
+ *netid = (*netid) & ~NETID_USE_LOCAL_NAMESERVERS;
+ return true;
+}
+
+constexpr bool requestingUseLocalNameservers(unsigned flags) {
+ return (flags & NET_CONTEXT_FLAG_USE_LOCAL_NAMESERVERS) != 0;
+}
+
+inline bool queryingViaTls(unsigned dns_netid) {
+ // TODO: The simpler PrivateDnsStatus should suffice here.
+ ExternalPrivateDnsStatus privateDnsStatus = {PrivateDnsMode::OFF, 0, {}};
+ gPrivateDnsConfiguration.getStatus(dns_netid, &privateDnsStatus);
+ switch (static_cast<PrivateDnsMode>(privateDnsStatus.mode)) {
+ case PrivateDnsMode::OPPORTUNISTIC:
+ for (int i = 0; i < privateDnsStatus.numServers; i++) {
+ if (privateDnsStatus.serverStatus[i].validation == Validation::success) {
+ return true;
+ }
+ }
+ return false;
+ case PrivateDnsMode::STRICT:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool hasPermissionToBypassPrivateDns(uid_t uid) {
+ static_assert(AID_SYSTEM >= 0 && AID_SYSTEM < FIRST_APPLICATION_UID,
+ "Calls from AID_SYSTEM must not result in a permission check to avoid deadlock.");
+ if (uid >= 0 && uid < FIRST_APPLICATION_UID) {
+ return true;
+ }
+
+ for (const char* const permission :
+ {PERM_CONNECTIVITY_USE_RESTRICTED_NETWORKS, PERM_NETWORK_BYPASS_PRIVATE_DNS,
+ PERM_MAINLINE_NETWORK_STACK}) {
+ if (gResNetdCallbacks.check_calling_permission(permission)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void maybeFixupNetContext(android_net_context* ctx) {
+ if (requestingUseLocalNameservers(ctx->flags) && !hasPermissionToBypassPrivateDns(ctx->uid)) {
+ // Not permitted; clear the flag.
+ ctx->flags &= ~NET_CONTEXT_FLAG_USE_LOCAL_NAMESERVERS;
+ }
+
+ if (!requestingUseLocalNameservers(ctx->flags)) {
+ // If we're not explicitly bypassing DNS-over-TLS servers, check whether
+ // DNS-over-TLS is in use as an indicator for when to use more modern
+ // DNS resolution mechanics.
+ if (queryingViaTls(ctx->dns_netid)) {
+ ctx->flags |= NET_CONTEXT_FLAG_USE_EDNS;
+ }
+ }
+}
+
+void addIpAddrWithinLimit(std::vector<std::string>* ip_addrs, const sockaddr* addr,
+ socklen_t addrlen);
+
+int extractResNsendAnswers(const uint8_t* answer, size_t anslen, int ipType,
+ std::vector<std::string>* ip_addrs) {
+ int total_ip_addr_count = 0;
+ ns_msg handle;
+ if (ns_initparse((const uint8_t*) answer, anslen, &handle) < 0) {
+ return 0;
+ }
+ int ancount = ns_msg_count(handle, ns_s_an);
+ ns_rr rr;
+ for (int i = 0; i < ancount; i++) {
+ if (ns_parserr(&handle, ns_s_an, i, &rr) < 0) {
+ continue;
+ }
+ const uint8_t* rdata = ns_rr_rdata(rr);
+ if (ipType == ns_t_a) {
+ sockaddr_in sin = {.sin_family = AF_INET};
+ memcpy(&sin.sin_addr, rdata, sizeof(sin.sin_addr));
+ addIpAddrWithinLimit(ip_addrs, (sockaddr*) &sin, sizeof(sin));
+ total_ip_addr_count++;
+ } else if (ipType == ns_t_aaaa) {
+ sockaddr_in6 sin6 = {.sin6_family = AF_INET6};
+ memcpy(&sin6.sin6_addr, rdata, sizeof(sin6.sin6_addr));
+ addIpAddrWithinLimit(ip_addrs, (sockaddr*) &sin6, sizeof(sin6));
+ total_ip_addr_count++;
+ }
+ }
+
+ return total_ip_addr_count;
+}
+
+int extractGetAddrInfoAnswers(const addrinfo* result, std::vector<std::string>* ip_addrs) {
+ int total_ip_addr_count = 0;
+ if (result == nullptr) {
+ return 0;
+ }
+ for (const addrinfo* ai = result; ai; ai = ai->ai_next) {
+ sockaddr* ai_addr = ai->ai_addr;
+ if (ai_addr) {
+ addIpAddrWithinLimit(ip_addrs, ai_addr, ai->ai_addrlen);
+ total_ip_addr_count++;
+ }
+ }
+ return total_ip_addr_count;
+}
+
+int extractGetHostByNameAnswers(const hostent* hp, std::vector<std::string>* ip_addrs) {
+ int total_ip_addr_count = 0;
+ if (hp == nullptr) {
+ return 0;
+ }
+ if (hp->h_addrtype == AF_INET) {
+ in_addr** list = (in_addr**) hp->h_addr_list;
+ for (int i = 0; list[i] != nullptr; i++) {
+ sockaddr_in sin = {.sin_family = AF_INET, .sin_addr = *list[i]};
+ addIpAddrWithinLimit(ip_addrs, (sockaddr*) &sin, sizeof(sin));
+ total_ip_addr_count++;
+ }
+ } else if (hp->h_addrtype == AF_INET6) {
+ in6_addr** list = (in6_addr**) hp->h_addr_list;
+ for (int i = 0; list[i] != nullptr; i++) {
+ sockaddr_in6 sin6 = {.sin6_family = AF_INET6, .sin6_addr = *list[i]};
+ addIpAddrWithinLimit(ip_addrs, (sockaddr*) &sin6, sizeof(sin6));
+ total_ip_addr_count++;
+ }
+ }
+ return total_ip_addr_count;
+}
+
+int rcodeToAiError(int rcode) {
+ switch (rcode) {
+ case NOERROR:
+ return 0;
+ case RCODE_TIMEOUT:
+ return NETD_RESOLV_TIMEOUT;
+ default:
+ return EAI_NODATA;
+ }
+}
+
+int resNSendToAiError(int err, int rcode) {
+ if (err > 0) {
+ return rcodeToAiError(rcode);
+ }
+ if (err == -ETIMEDOUT) {
+ return NETD_RESOLV_TIMEOUT;
+ }
+ return EAI_SYSTEM;
+}
+
+template <typename IntegralType>
+bool simpleStrtoul(const char* input, IntegralType* output, int base = 10) {
+ char* endPtr;
+ errno = 0;
+ auto result = strtoul(input, &endPtr, base);
+ // Check the length in order to ensure there is no "-" sign
+ if (!*input || *endPtr || (endPtr - input) != static_cast<ptrdiff_t>(strlen(input)) ||
+ (errno == ERANGE && (result == ULONG_MAX))) {
+ return false;
+ }
+ *output = result;
+ return true;
+}
+
+bool setQueryId(uint8_t* msg, size_t msgLen, uint16_t query_id) {
+ if (msgLen < sizeof(HEADER)) {
+ errno = EINVAL;
+ return false;
+ }
+ auto hp = reinterpret_cast<HEADER*>(msg);
+ hp->id = htons(query_id);
+ return true;
+}
+
+bool parseQuery(const uint8_t* msg, size_t msgLen, uint16_t* query_id, int* rr_type,
+ std::string* rr_name) {
+ ns_msg handle;
+ ns_rr rr;
+ if (ns_initparse((const uint8_t*)msg, msgLen, &handle) < 0 ||
+ ns_parserr(&handle, ns_s_qd, 0, &rr) < 0) {
+ return false;
+ }
+ *query_id = ns_msg_id(handle);
+ *rr_name = ns_rr_name(rr);
+ *rr_type = ns_rr_type(rr);
+ return true;
+}
+
+void initDnsEvent(NetworkDnsEventReported* event) {
+ // The value 0 has the special meaning of unset/unknown in Westworld atoms.
+ event->set_hints_ai_flags(-1);
+ event->set_res_nsend_flags(-1);
+}
+
+// Return 0 if the event should not be logged.
+// Otherwise, return subsampling_denom
+uint32_t getDnsEventSubsamplingRate(int netid, int returnCode) {
+ uint32_t subsampling_denom = resolv_cache_get_subsampling_denom(netid, returnCode);
+ if (subsampling_denom == 0) return 0;
+ // Sample the event with a chance of 1 / denom.
+ return (arc4random_uniform(subsampling_denom) == 0) ? subsampling_denom : 0;
+}
+
+void reportDnsEvent(int eventType, const android_net_context& netContext, int latencyUs,
+ int returnCode, NetworkDnsEventReported& event, const std::string& query_name,
+ const std::vector<std::string>& ip_addrs = {}, int total_ip_addr_count = 0) {
+ if (uint32_t rate = getDnsEventSubsamplingRate(netContext.dns_netid, returnCode)) {
+ const std::string& dnsQueryStats = event.dns_query_events().SerializeAsString();
+ stats::BytesField dnsQueryBytesField{dnsQueryStats.c_str(), dnsQueryStats.size()};
+ event.set_return_code(static_cast<ReturnCode>(returnCode));
+ android::net::stats::stats_write(android::net::stats::NETWORK_DNS_EVENT_REPORTED,
+ event.event_type(), event.return_code(),
+ event.latency_micros(), event.hints_ai_flags(),
+ event.res_nsend_flags(), event.network_type(),
+ event.private_dns_modes(), dnsQueryBytesField, rate);
+ }
+
+ const auto& listeners = ResolverEventReporter::getInstance().getListeners();
+ if (listeners.size() == 0) {
+ LOG(ERROR) << __func__
+ << ": DNS event not sent since no INetdEventListener receiver is available.";
+ return;
+ }
+ const int latencyMs = latencyUs / 1000;
+ for (const auto& it : listeners) {
+ it->onDnsEvent(netContext.dns_netid, eventType, returnCode, latencyMs, query_name, ip_addrs,
+ total_ip_addr_count, netContext.uid);
+ }
+}
+
+bool onlyIPv4Answers(const addrinfo* res) {
+ // Null addrinfo pointer isn't checked because the caller doesn't pass null pointer.
+
+ for (const addrinfo* ai = res; ai; ai = ai->ai_next)
+ if (ai->ai_family != AF_INET) return false;
+
+ return true;
+}
+
+bool isSpecialUseIPv4Address(const struct in_addr& ia) {
+ const uint32_t addr = ntohl(ia.s_addr);
+
+ // Only check necessary IP ranges in RFC 5735 section 4
+ return ((addr & 0xff000000) == 0x00000000) || // "This" Network
+ ((addr & 0xff000000) == 0x7f000000) || // Loopback
+ ((addr & 0xffff0000) == 0xa9fe0000) || // Link Local
+ ((addr & 0xf0000000) == 0xe0000000) || // Multicast
+ (addr == INADDR_BROADCAST); // Limited Broadcast
+}
+
+bool isSpecialUseIPv4Address(const struct sockaddr* sa) {
+ if (sa->sa_family != AF_INET) return false;
+
+ return isSpecialUseIPv4Address(((struct sockaddr_in*) sa)->sin_addr);
+}
+
+bool onlyNonSpecialUseIPv4Addresses(struct hostent* hp) {
+ // Null hostent pointer isn't checked because the caller doesn't pass null pointer.
+
+ if (hp->h_addrtype != AF_INET) return false;
+
+ for (int i = 0; hp->h_addr_list[i] != nullptr; i++)
+ if (isSpecialUseIPv4Address(*(struct in_addr*) hp->h_addr_list[i])) return false;
+
+ return true;
+}
+
+bool onlyNonSpecialUseIPv4Addresses(const addrinfo* res) {
+ // Null addrinfo pointer isn't checked because the caller doesn't pass null pointer.
+
+ for (const addrinfo* ai = res; ai; ai = ai->ai_next) {
+ if (ai->ai_family != AF_INET) return false;
+ if (isSpecialUseIPv4Address(ai->ai_addr)) return false;
+ }
+
+ return true;
+}
+
+void logDnsQueryResult(const struct hostent* hp) {
+ if (!WOULD_LOG(DEBUG)) return;
+ if (hp == nullptr) return;
+
+ LOG(DEBUG) << __func__ << ": DNS records:";
+ for (int i = 0; hp->h_addr_list[i] != nullptr; i++) {
+ char ip_addr[INET6_ADDRSTRLEN];
+ if (inet_ntop(hp->h_addrtype, hp->h_addr_list[i], ip_addr, sizeof(ip_addr)) != nullptr) {
+ LOG(DEBUG) << __func__ << ": [" << i << "] " << hp->h_addrtype;
+ } else {
+ PLOG(DEBUG) << __func__ << ": [" << i << "] numeric hostname translation fail";
+ }
+ }
+}
+
+void logDnsQueryResult(const addrinfo* res) {
+ if (!WOULD_LOG(DEBUG)) return;
+ if (res == nullptr) return;
+
+ int i;
+ const addrinfo* ai;
+ LOG(DEBUG) << __func__ << ": DNS records:";
+ for (ai = res, i = 0; ai; ai = ai->ai_next, i++) {
+ if ((ai->ai_family != AF_INET) && (ai->ai_family != AF_INET6)) continue;
+ char ip_addr[INET6_ADDRSTRLEN];
+ int ret = getnameinfo(ai->ai_addr, ai->ai_addrlen, ip_addr, sizeof(ip_addr), nullptr, 0,
+ NI_NUMERICHOST);
+ if (!ret) {
+ LOG(DEBUG) << __func__ << ": [" << i << "] " << ai->ai_flags << " " << ai->ai_family
+ << " " << ai->ai_socktype << " " << ai->ai_protocol;
+ } else {
+ LOG(DEBUG) << __func__ << ": [" << i << "] numeric hostname translation fail " << ret;
+ }
+ }
+}
+
+bool isValidNat64Prefix(const netdutils::IPPrefix prefix) {
+ if (prefix.family() != AF_INET6) {
+ LOG(ERROR) << __func__ << ": Only IPv6 NAT64 prefixes are supported " << prefix.family();
+ return false;
+ }
+ if (prefix.length() != 96) {
+ LOG(ERROR) << __func__ << ": Only /96 NAT64 prefixes are supported " << prefix.length();
+ return false;
+ }
+ return true;
+}
+
+bool synthesizeNat64PrefixWithARecord(const netdutils::IPPrefix& prefix, struct hostent* hp) {
+ if (hp == nullptr) return false;
+ if (!onlyNonSpecialUseIPv4Addresses(hp)) return false;
+ if (!isValidNat64Prefix(prefix)) return false;
+
+ struct sockaddr_storage ss = netdutils::IPSockAddr(prefix.ip());
+ struct sockaddr_in6* v6prefix = (struct sockaddr_in6*) &ss;
+ for (int i = 0; hp->h_addr_list[i] != nullptr; i++) {
+ struct in_addr iaOriginal = *(struct in_addr*) hp->h_addr_list[i];
+ struct in6_addr* ia6 = (struct in6_addr*) hp->h_addr_list[i];
+ memset(ia6, 0, sizeof(struct in6_addr));
+
+ // Synthesize /96 NAT64 prefix in place. The space has reserved by getanswer() and
+ // _hf_gethtbyname2() in system/netd/resolv/gethnamaddr.cpp and
+ // system/netd/resolv/sethostent.cpp.
+ *ia6 = v6prefix->sin6_addr;
+ ia6->s6_addr32[3] = iaOriginal.s_addr;
+
+ if (WOULD_LOG(DEBUG)) {
+ char buf[INET6_ADDRSTRLEN]; // big enough for either IPv4 or IPv6
+ inet_ntop(AF_INET, &iaOriginal.s_addr, buf, sizeof(buf));
+ LOG(DEBUG) << __func__ << ": DNS A record: " << buf;
+ inet_ntop(AF_INET6, &v6prefix->sin6_addr, buf, sizeof(buf));
+ LOG(DEBUG) << __func__ << ": NAT64 prefix: " << buf;
+ inet_ntop(AF_INET6, ia6, buf, sizeof(buf));
+ LOG(DEBUG) << __func__ << ": DNS64 Synthesized AAAA record: " << buf;
+ }
+ }
+ hp->h_addrtype = AF_INET6;
+ hp->h_length = sizeof(in6_addr);
+
+ logDnsQueryResult(hp);
+ return true;
+}
+
+bool synthesizeNat64PrefixWithARecord(const netdutils::IPPrefix& prefix, addrinfo* result) {
+ if (result == nullptr) return false;
+ if (!onlyNonSpecialUseIPv4Addresses(result)) return false;
+ if (!isValidNat64Prefix(prefix)) return false;
+
+ struct sockaddr_storage ss = netdutils::IPSockAddr(prefix.ip());
+ struct sockaddr_in6* v6prefix = (struct sockaddr_in6*) &ss;
+ for (addrinfo* ai = result; ai; ai = ai->ai_next) {
+ struct sockaddr_in sinOriginal = *(struct sockaddr_in*) ai->ai_addr;
+ struct sockaddr_in6* sin6 = (struct sockaddr_in6*) ai->ai_addr;
+ memset(sin6, 0, sizeof(sockaddr_in6));
+
+ // Synthesize /96 NAT64 prefix in place. The space has reserved by get_ai() in
+ // system/netd/resolv/getaddrinfo.cpp.
+ sin6->sin6_addr = v6prefix->sin6_addr;
+ sin6->sin6_addr.s6_addr32[3] = sinOriginal.sin_addr.s_addr;
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = sinOriginal.sin_port;
+ ai->ai_addrlen = sizeof(struct sockaddr_in6);
+ ai->ai_family = AF_INET6;
+
+ if (WOULD_LOG(DEBUG)) {
+ char buf[INET6_ADDRSTRLEN]; // big enough for either IPv4 or IPv6
+ inet_ntop(AF_INET, &sinOriginal.sin_addr.s_addr, buf, sizeof(buf));
+ LOG(DEBUG) << __func__ << ": DNS A record: " << buf;
+ inet_ntop(AF_INET6, &v6prefix->sin6_addr, buf, sizeof(buf));
+ LOG(DEBUG) << __func__ << ": NAT64 prefix: " << buf;
+ inet_ntop(AF_INET6, &sin6->sin6_addr, buf, sizeof(buf));
+ LOG(DEBUG) << __func__ << ": DNS64 Synthesized AAAA record: " << buf;
+ }
+ }
+ logDnsQueryResult(result);
+ return true;
+}
+
+bool getDns64Prefix(unsigned netId, netdutils::IPPrefix* prefix) {
+ return !gDnsResolv->resolverCtrl.getPrefix64(netId, prefix);
+}
+
+} // namespace
+
+DnsProxyListener::DnsProxyListener() : FrameworkListener(SOCKET_NAME) {
+ registerCmd(new GetAddrInfoCmd());
+ registerCmd(new GetHostByAddrCmd());
+ registerCmd(new GetHostByNameCmd());
+ registerCmd(new ResNSendCommand());
+ registerCmd(new GetDnsNetIdCommand());
+}
+
+DnsProxyListener::GetAddrInfoHandler::GetAddrInfoHandler(SocketClient* c, char* host, char* service,
+ addrinfo* hints,
+ const android_net_context& netcontext)
+ : mClient(c), mHost(host), mService(service), mHints(hints), mNetContext(netcontext) {}
+
+DnsProxyListener::GetAddrInfoHandler::~GetAddrInfoHandler() {
+ free(mHost);
+ free(mService);
+ free(mHints);
+}
+
+static bool sendBE32(SocketClient* c, uint32_t data) {
+ uint32_t be_data = htonl(data);
+ return c->sendData(&be_data, sizeof(be_data)) == 0;
+}
+
+// Sends 4 bytes of big-endian length, followed by the data.
+// Returns true on success.
+static bool sendLenAndData(SocketClient* c, const int len, const void* data) {
+ return sendBE32(c, len) && (len == 0 || c->sendData(data, len) == 0);
+}
+
+// Returns true on success
+static bool sendhostent(SocketClient* c, hostent* hp) {
+ bool success = true;
+ int i;
+ if (hp->h_name != nullptr) {
+ success &= sendLenAndData(c, strlen(hp->h_name)+1, hp->h_name);
+ } else {
+ success &= sendLenAndData(c, 0, "") == 0;
+ }
+
+ for (i=0; hp->h_aliases[i] != nullptr; i++) {
+ success &= sendLenAndData(c, strlen(hp->h_aliases[i])+1, hp->h_aliases[i]);
+ }
+ success &= sendLenAndData(c, 0, ""); // null to indicate we're done
+
+ uint32_t buf = htonl(hp->h_addrtype);
+ success &= c->sendData(&buf, sizeof(buf)) == 0;
+
+ buf = htonl(hp->h_length);
+ success &= c->sendData(&buf, sizeof(buf)) == 0;
+
+ for (i=0; hp->h_addr_list[i] != nullptr; i++) {
+ success &= sendLenAndData(c, 16, hp->h_addr_list[i]);
+ }
+ success &= sendLenAndData(c, 0, ""); // null to indicate we're done
+ return success;
+}
+
+static bool sendaddrinfo(SocketClient* c, addrinfo* ai) {
+ // struct addrinfo {
+ // int ai_flags; /* AI_PASSIVE, AI_CANONNAME, AI_NUMERICHOST */
+ // int ai_family; /* PF_xxx */
+ // int ai_socktype; /* SOCK_xxx */
+ // int ai_protocol; /* 0 or IPPROTO_xxx for IPv4 and IPv6 */
+ // socklen_t ai_addrlen; /* length of ai_addr */
+ // char *ai_canonname; /* canonical name for hostname */
+ // struct sockaddr *ai_addr; /* binary address */
+ // struct addrinfo *ai_next; /* next structure in linked list */
+ // };
+
+ // Write the struct piece by piece because we might be a 64-bit netd
+ // talking to a 32-bit process.
+ bool success =
+ sendBE32(c, ai->ai_flags) &&
+ sendBE32(c, ai->ai_family) &&
+ sendBE32(c, ai->ai_socktype) &&
+ sendBE32(c, ai->ai_protocol);
+ if (!success) {
+ return false;
+ }
+
+ // ai_addrlen and ai_addr.
+ if (!sendLenAndData(c, ai->ai_addrlen, ai->ai_addr)) {
+ return false;
+ }
+
+ // strlen(ai_canonname) and ai_canonname.
+ if (!sendLenAndData(c, ai->ai_canonname ? strlen(ai->ai_canonname) + 1 : 0, ai->ai_canonname)) {
+ return false;
+ }
+
+ return true;
+}
+
+void DnsProxyListener::GetAddrInfoHandler::doDns64Synthesis(int32_t* rv, addrinfo** res,
+ NetworkDnsEventReported* event) {
+ if (mHost == nullptr) return;
+
+ const bool ipv6WantedButNoData = (mHints && mHints->ai_family == AF_INET6 && *rv == EAI_NODATA);
+ const bool unspecWantedButNoIPv6 =
+ ((!mHints || mHints->ai_family == AF_UNSPEC) && *rv == 0 && onlyIPv4Answers(*res));
+
+ if (!ipv6WantedButNoData && !unspecWantedButNoIPv6) {
+ return;
+ }
+
+ netdutils::IPPrefix prefix{};
+ if (!getDns64Prefix(mNetContext.dns_netid, &prefix)) {
+ return;
+ }
+
+ if (ipv6WantedButNoData) {
+ // If caller wants IPv6 answers but no data, try to query IPv4 answers for synthesis
+ const uid_t uid = mClient->getUid();
+ if (queryLimiter.start(uid)) {
+ mHints->ai_family = AF_INET;
+ // Don't need to do freeaddrinfo(res) before starting new DNS lookup because previous
+ // DNS lookup is failed with error EAI_NODATA.
+ *rv = android_getaddrinfofornetcontext(mHost, mService, mHints, &mNetContext, res,
+ event);
+ queryLimiter.finish(uid);
+ if (*rv) {
+ *rv = EAI_NODATA; // return original error code
+ return;
+ }
+ } else {
+ LOG(ERROR) << __func__ << ": from UID " << uid << ", max concurrent queries reached";
+ return;
+ }
+ }
+
+ if (!synthesizeNat64PrefixWithARecord(prefix, *res)) {
+ if (ipv6WantedButNoData) {
+ // If caller wants IPv6 answers but no data and failed to synthesize IPv6 answers,
+ // don't return the IPv4 answers.
+ *rv = EAI_NODATA; // return original error code
+ if (*res) {
+ freeaddrinfo(*res);
+ *res = nullptr;
+ }
+ }
+ }
+}
+
+void DnsProxyListener::GetAddrInfoHandler::run() {
+ LOG(DEBUG) << "GetAddrInfoHandler::run: {" << mNetContext.app_netid << " "
+ << mNetContext.app_mark << " " << mNetContext.dns_netid << " "
+ << mNetContext.dns_mark << " " << mNetContext.uid << " " << mNetContext.flags << "}";
+
+ addrinfo* result = nullptr;
+ Stopwatch s;
+ maybeFixupNetContext(&mNetContext);
+ const uid_t uid = mClient->getUid();
+ int32_t rv = 0;
+ NetworkDnsEventReported event;
+ initDnsEvent(&event);
+ if (queryLimiter.start(uid)) {
+ rv = android_getaddrinfofornetcontext(mHost, mService, mHints, &mNetContext, &result,
+ &event);
+ queryLimiter.finish(uid);
+ } else {
+ // Note that this error code is currently not passed down to the client.
+ // android_getaddrinfo_proxy() returns EAI_NODATA on any error.
+ rv = EAI_MEMORY;
+ LOG(ERROR) << "GetAddrInfoHandler::run: from UID " << uid
+ << ", max concurrent queries reached";
+ }
+
+ doDns64Synthesis(&rv, &result, &event);
+ const int32_t latencyUs = saturate_cast<int32_t>(s.timeTakenUs());
+ event.set_latency_micros(latencyUs);
+ event.set_event_type(EVENT_GETADDRINFO);
+ event.set_hints_ai_flags((mHints ? mHints->ai_flags : 0));
+
+ if (rv) {
+ // getaddrinfo failed
+ mClient->sendBinaryMsg(ResponseCode::DnsProxyOperationFailed, &rv, sizeof(rv));
+ } else {
+ bool success = !mClient->sendCode(ResponseCode::DnsProxyQueryResult);
+ addrinfo* ai = result;
+ while (ai && success) {
+ success = sendBE32(mClient, 1) && sendaddrinfo(mClient, ai);
+ ai = ai->ai_next;
+ }
+ success = success && sendBE32(mClient, 0);
+ if (!success) {
+ LOG(WARNING) << "GetAddrInfoHandler::run: Error writing DNS result to client";
+ }
+ }
+ std::vector<std::string> ip_addrs;
+ const int total_ip_addr_count = extractGetAddrInfoAnswers(result, &ip_addrs);
+ reportDnsEvent(INetdEventListener::EVENT_GETADDRINFO, mNetContext, latencyUs, rv, event, mHost,
+ ip_addrs, total_ip_addr_count);
+ freeaddrinfo(result);
+ mClient->decRef();
+}
+
+namespace {
+
+void addIpAddrWithinLimit(std::vector<std::string>* ip_addrs, const sockaddr* addr,
+ socklen_t addrlen) {
+ // ipAddresses array is limited to first INetdEventListener::DNS_REPORTED_IP_ADDRESSES_LIMIT
+ // addresses for A and AAAA. Total count of addresses is provided, to be able to tell whether
+ // some addresses didn't get logged.
+ if (ip_addrs->size() < INetdEventListener::DNS_REPORTED_IP_ADDRESSES_LIMIT) {
+ char ip_addr[INET6_ADDRSTRLEN];
+ if (getnameinfo(addr, addrlen, ip_addr, sizeof(ip_addr), nullptr, 0, NI_NUMERICHOST) == 0) {
+ ip_addrs->push_back(std::string(ip_addr));
+ }
+ }
+}
+
+} // namespace
+
+DnsProxyListener::GetAddrInfoCmd::GetAddrInfoCmd() : FrameworkCommand("getaddrinfo") {}
+
+int DnsProxyListener::GetAddrInfoCmd::runCommand(SocketClient *cli,
+ int argc, char **argv) {
+ logArguments(argc, argv);
+
+ if (argc != 8) {
+ char* msg = nullptr;
+ asprintf( &msg, "Invalid number of arguments to getaddrinfo: %i", argc);
+ LOG(WARNING) << "GetAddrInfoCmd::runCommand: " << (msg ? msg : "null");
+ cli->sendMsg(ResponseCode::CommandParameterError, msg, false);
+ free(msg);
+ return -1;
+ }
+
+ char* name = argv[1];
+ if (strcmp("^", name) == 0) {
+ name = nullptr;
+ } else {
+ name = strdup(name);
+ }
+
+ char* service = argv[2];
+ if (strcmp("^", service) == 0) {
+ service = nullptr;
+ } else {
+ service = strdup(service);
+ }
+
+ addrinfo* hints = nullptr;
+ int ai_flags = strtol(argv[3], nullptr, 10);
+ int ai_family = strtol(argv[4], nullptr, 10);
+ int ai_socktype = strtol(argv[5], nullptr, 10);
+ int ai_protocol = strtol(argv[6], nullptr, 10);
+ unsigned netId = strtoul(argv[7], nullptr, 10);
+ const bool useLocalNameservers = checkAndClearUseLocalNameserversFlag(&netId);
+ const uid_t uid = cli->getUid();
+
+ android_net_context netcontext;
+ gResNetdCallbacks.get_network_context(netId, uid, &netcontext);
+
+ if (useLocalNameservers) {
+ netcontext.flags |= NET_CONTEXT_FLAG_USE_LOCAL_NAMESERVERS;
+ }
+
+ if (ai_flags != -1 || ai_family != -1 ||
+ ai_socktype != -1 || ai_protocol != -1) {
+ hints = (addrinfo*) calloc(1, sizeof(addrinfo));
+ hints->ai_flags = ai_flags;
+ hints->ai_family = ai_family;
+ hints->ai_socktype = ai_socktype;
+ hints->ai_protocol = ai_protocol;
+ }
+
+ DnsProxyListener::GetAddrInfoHandler* handler =
+ new DnsProxyListener::GetAddrInfoHandler(cli, name, service, hints, netcontext);
+ tryThreadOrError(cli, handler);
+ return 0;
+}
+
+/*******************************************************
+ * ResNSendCommand *
+ *******************************************************/
+DnsProxyListener::ResNSendCommand::ResNSendCommand() : FrameworkCommand("resnsend") {}
+
+int DnsProxyListener::ResNSendCommand::runCommand(SocketClient* cli, int argc, char** argv) {
+ logArguments(argc, argv);
+
+ const uid_t uid = cli->getUid();
+ if (argc != 4) {
+ LOG(WARNING) << "ResNSendCommand::runCommand: resnsend: from UID " << uid
+ << ", invalid number of arguments to resnsend: " << argc;
+ sendBE32(cli, -EINVAL);
+ return -1;
+ }
+
+ unsigned netId;
+ if (!simpleStrtoul(argv[1], &netId)) {
+ LOG(WARNING) << "ResNSendCommand::runCommand: resnsend: from UID " << uid
+ << ", invalid netId";
+ sendBE32(cli, -EINVAL);
+ return -1;
+ }
+
+ uint32_t flags;
+ if (!simpleStrtoul(argv[2], &flags)) {
+ LOG(WARNING) << "ResNSendCommand::runCommand: resnsend: from UID " << uid
+ << ", invalid flags";
+ sendBE32(cli, -EINVAL);
+ return -1;
+ }
+
+ const bool useLocalNameservers = checkAndClearUseLocalNameserversFlag(&netId);
+
+ android_net_context netcontext;
+ gResNetdCallbacks.get_network_context(netId, uid, &netcontext);
+
+ if (useLocalNameservers) {
+ netcontext.flags |= NET_CONTEXT_FLAG_USE_LOCAL_NAMESERVERS;
+ }
+
+ DnsProxyListener::ResNSendHandler* handler =
+ new DnsProxyListener::ResNSendHandler(cli, argv[3], flags, netcontext);
+ tryThreadOrError(cli, handler);
+ return 0;
+}
+
+DnsProxyListener::ResNSendHandler::ResNSendHandler(SocketClient* c, std::string msg, uint32_t flags,
+ const android_net_context& netcontext)
+ : mClient(c), mMsg(std::move(msg)), mFlags(flags), mNetContext(netcontext) {}
+
+DnsProxyListener::ResNSendHandler::~ResNSendHandler() {
+ mClient->decRef();
+}
+
+void DnsProxyListener::ResNSendHandler::run() {
+ LOG(DEBUG) << "ResNSendHandler::run: " << mFlags << " / {" << mNetContext.app_netid << " "
+ << mNetContext.app_mark << " " << mNetContext.dns_netid << " "
+ << mNetContext.dns_mark << " " << mNetContext.uid << " " << mNetContext.flags << "}";
+
+ Stopwatch s;
+ maybeFixupNetContext(&mNetContext);
+
+ // Decode
+ std::vector<uint8_t> msg(MAXPACKET, 0);
+
+ // Max length of mMsg is less than 1024 since the CMD_BUF_SIZE in FrameworkListener is 1024
+ int msgLen = b64_pton(mMsg.c_str(), msg.data(), MAXPACKET);
+ if (msgLen == -1) {
+ // Decode fail
+ sendBE32(mClient, -EILSEQ);
+ return;
+ }
+
+ const uid_t uid = mClient->getUid();
+ int rr_type = 0;
+ std::string rr_name;
+ uint16_t original_query_id = 0;
+
+ // TODO: Handle the case which is msg contains more than one query
+ if (!parseQuery(msg.data(), msgLen, &original_query_id, &rr_type, &rr_name) ||
+ !setQueryId(msg.data(), msgLen, arc4random_uniform(65536))) {
+ // If the query couldn't be parsed, block the request.
+ LOG(WARNING) << "ResNSendHandler::run: resnsend: from UID " << uid << ", invalid query";
+ sendBE32(mClient, -EINVAL);
+ return;
+ }
+
+ // Send DNS query
+ std::vector<uint8_t> ansBuf(MAXPACKET, 0);
+ int arcode, nsendAns = -1;
+ NetworkDnsEventReported event;
+ initDnsEvent(&event);
+ if (queryLimiter.start(uid)) {
+ nsendAns = resolv_res_nsend(&mNetContext, msg.data(), msgLen, ansBuf.data(), MAXPACKET,
+ &arcode, static_cast<ResNsendFlags>(mFlags), &event);
+ queryLimiter.finish(uid);
+ } else {
+ LOG(WARNING) << "ResNSendHandler::run: resnsend: from UID " << uid
+ << ", max concurrent queries reached";
+ nsendAns = -EBUSY;
+ }
+
+ const int32_t latencyUs = saturate_cast<int32_t>(s.timeTakenUs());
+ event.set_latency_micros(latencyUs);
+ event.set_event_type(EVENT_RES_NSEND);
+ event.set_res_nsend_flags(static_cast<ResNsendFlags>(mFlags));
+
+ // Fail, send -errno
+ if (nsendAns < 0) {
+ sendBE32(mClient, nsendAns);
+ if (rr_type == ns_t_a || rr_type == ns_t_aaaa) {
+ reportDnsEvent(INetdEventListener::EVENT_RES_NSEND, mNetContext, latencyUs,
+ resNSendToAiError(nsendAns, arcode), event, rr_name);
+ }
+ return;
+ }
+
+ // Send rcode
+ if (!sendBE32(mClient, arcode)) {
+ PLOG(WARNING) << "ResNSendHandler::run: resnsend: failed to send rcode to uid " << uid;
+ return;
+ }
+
+ // Restore query id and send answer
+ if (!setQueryId(ansBuf.data(), nsendAns, original_query_id) ||
+ !sendLenAndData(mClient, nsendAns, ansBuf.data())) {
+ PLOG(WARNING) << "ResNSendHandler::run: resnsend: failed to send answer to uid " << uid;
+ return;
+ }
+
+ if (rr_type == ns_t_a || rr_type == ns_t_aaaa) {
+ std::vector<std::string> ip_addrs;
+ const int total_ip_addr_count =
+ extractResNsendAnswers((uint8_t*) ansBuf.data(), nsendAns, rr_type, &ip_addrs);
+ reportDnsEvent(INetdEventListener::EVENT_RES_NSEND, mNetContext, latencyUs,
+ resNSendToAiError(nsendAns, arcode), event, rr_name, ip_addrs,
+ total_ip_addr_count);
+ }
+}
+
+namespace {
+
+bool sendCodeAndBe32(SocketClient* c, int code, int data) {
+ return !c->sendCode(code) && sendBE32(c, data);
+}
+
+} // namespace
+
+/*******************************************************
+ * GetDnsNetId *
+ *******************************************************/
+DnsProxyListener::GetDnsNetIdCommand::GetDnsNetIdCommand() : FrameworkCommand("getdnsnetid") {}
+
+int DnsProxyListener::GetDnsNetIdCommand::runCommand(SocketClient* cli, int argc, char** argv) {
+ logArguments(argc, argv);
+
+ const uid_t uid = cli->getUid();
+ if (argc != 2) {
+ LOG(WARNING) << "GetDnsNetIdCommand::runCommand: getdnsnetid: from UID " << uid
+ << ", invalid number of arguments to getdnsnetid: " << argc;
+ sendCodeAndBe32(cli, ResponseCode::DnsProxyQueryResult, -EINVAL);
+ return -1;
+ }
+
+ unsigned netId;
+ if (!simpleStrtoul(argv[1], &netId)) {
+ LOG(WARNING) << "GetDnsNetIdCommand::runCommand: getdnsnetid: from UID " << uid
+ << ", invalid netId";
+ sendCodeAndBe32(cli, ResponseCode::DnsProxyQueryResult, -EINVAL);
+ return -1;
+ }
+
+ const bool useLocalNameservers = checkAndClearUseLocalNameserversFlag(&netId);
+ android_net_context netcontext;
+ gResNetdCallbacks.get_network_context(netId, uid, &netcontext);
+
+ if (useLocalNameservers) {
+ netcontext.app_netid |= NETID_USE_LOCAL_NAMESERVERS;
+ }
+
+ return sendCodeAndBe32(cli, ResponseCode::DnsProxyQueryResult, netcontext.app_netid) ? 0 : -1;
+}
+
+/*******************************************************
+ * GetHostByName *
+ *******************************************************/
+DnsProxyListener::GetHostByNameCmd::GetHostByNameCmd() : FrameworkCommand("gethostbyname") {}
+
+int DnsProxyListener::GetHostByNameCmd::runCommand(SocketClient *cli,
+ int argc, char **argv) {
+ logArguments(argc, argv);
+
+ if (argc != 4) {
+ char* msg = nullptr;
+ asprintf(&msg, "Invalid number of arguments to gethostbyname: %i", argc);
+ LOG(WARNING) << "GetHostByNameCmd::runCommand: " << (msg ? msg : "null");
+ cli->sendMsg(ResponseCode::CommandParameterError, msg, false);
+ free(msg);
+ return -1;
+ }
+
+ uid_t uid = cli->getUid();
+ unsigned netId = strtoul(argv[1], nullptr, 10);
+ const bool useLocalNameservers = checkAndClearUseLocalNameserversFlag(&netId);
+ char* name = argv[2];
+ int af = strtol(argv[3], nullptr, 10);
+
+ if (strcmp(name, "^") == 0) {
+ name = nullptr;
+ } else {
+ name = strdup(name);
+ }
+
+ android_net_context netcontext;
+ gResNetdCallbacks.get_network_context(netId, uid, &netcontext);
+
+ if (useLocalNameservers) {
+ netcontext.flags |= NET_CONTEXT_FLAG_USE_LOCAL_NAMESERVERS;
+ }
+
+ DnsProxyListener::GetHostByNameHandler* handler =
+ new DnsProxyListener::GetHostByNameHandler(cli, name, af, netcontext);
+ tryThreadOrError(cli, handler);
+ return 0;
+}
+
+DnsProxyListener::GetHostByNameHandler::GetHostByNameHandler(SocketClient* c, char* name, int af,
+ const android_net_context& netcontext)
+ : mClient(c), mName(name), mAf(af), mNetContext(netcontext) {}
+
+DnsProxyListener::GetHostByNameHandler::~GetHostByNameHandler() {
+ free(mName);
+}
+
+void DnsProxyListener::GetHostByNameHandler::doDns64Synthesis(int32_t* rv, struct hostent** hpp,
+ NetworkDnsEventReported* event) {
+ // Don't have to consider family AF_UNSPEC case because gethostbyname{, 2} only supports
+ // family AF_INET or AF_INET6.
+ const bool ipv6WantedButNoData = (mAf == AF_INET6 && *rv == EAI_NODATA);
+
+ if (!ipv6WantedButNoData) {
+ return;
+ }
+
+ netdutils::IPPrefix prefix{};
+ if (!getDns64Prefix(mNetContext.dns_netid, &prefix)) {
+ return;
+ }
+
+ // If caller wants IPv6 answers but no data, try to query IPv4 answers for synthesis
+ const uid_t uid = mClient->getUid();
+ if (queryLimiter.start(uid)) {
+ *rv = android_gethostbynamefornetcontext(mName, AF_INET, &mNetContext, hpp, event);
+ queryLimiter.finish(uid);
+ if (*rv) {
+ *rv = EAI_NODATA; // return original error code
+ return;
+ }
+ } else {
+ LOG(ERROR) << __func__ << ": from UID " << uid << ", max concurrent queries reached";
+ return;
+ }
+
+ if (!synthesizeNat64PrefixWithARecord(prefix, *hpp)) {
+ // If caller wants IPv6 answers but no data and failed to synthesize IPv4 answers,
+ // don't return the IPv4 answers.
+ *hpp = nullptr;
+ }
+}
+
+void DnsProxyListener::GetHostByNameHandler::run() {
+ Stopwatch s;
+ maybeFixupNetContext(&mNetContext);
+ const uid_t uid = mClient->getUid();
+ hostent* hp = nullptr;
+ int32_t rv = 0;
+ NetworkDnsEventReported event;
+ initDnsEvent(&event);
+ if (queryLimiter.start(uid)) {
+ rv = android_gethostbynamefornetcontext(mName, mAf, &mNetContext, &hp, &event);
+ queryLimiter.finish(uid);
+ } else {
+ rv = EAI_MEMORY;
+ LOG(ERROR) << "GetHostByNameHandler::run: from UID " << uid
+ << ", max concurrent queries reached";
+ }
+
+ doDns64Synthesis(&rv, &hp, &event);
+ const int32_t latencyUs = saturate_cast<int32_t>(s.timeTakenUs());
+ event.set_latency_micros(latencyUs);
+ event.set_event_type(EVENT_GETHOSTBYNAME);
+
+ LOG(DEBUG) << "GetHostByNameHandler::run: errno: " << (hp ? "success" : strerror(errno));
+
+ bool success = true;
+ if (hp) {
+ // hp is not nullptr iff. rv is 0.
+ success = mClient->sendCode(ResponseCode::DnsProxyQueryResult) == 0;
+ success &= sendhostent(mClient, hp);
+ } else {
+ success = mClient->sendBinaryMsg(ResponseCode::DnsProxyOperationFailed, nullptr, 0) == 0;
+ }
+
+ if (!success) {
+ LOG(WARNING) << "GetHostByNameHandler::run: Error writing DNS result to client";
+ }
+
+ std::vector<std::string> ip_addrs;
+ const int total_ip_addr_count = extractGetHostByNameAnswers(hp, &ip_addrs);
+ reportDnsEvent(INetdEventListener::EVENT_GETHOSTBYNAME, mNetContext, latencyUs, rv, event,
+ mName, ip_addrs, total_ip_addr_count);
+ mClient->decRef();
+}
+
+
+/*******************************************************
+ * GetHostByAddr *
+ *******************************************************/
+DnsProxyListener::GetHostByAddrCmd::GetHostByAddrCmd() : FrameworkCommand("gethostbyaddr") {}
+
+int DnsProxyListener::GetHostByAddrCmd::runCommand(SocketClient *cli,
+ int argc, char **argv) {
+ logArguments(argc, argv);
+
+ if (argc != 5) {
+ char* msg = nullptr;
+ asprintf(&msg, "Invalid number of arguments to gethostbyaddr: %i", argc);
+ LOG(WARNING) << "GetHostByAddrCmd::runCommand: " << (msg ? msg : "null");
+ cli->sendMsg(ResponseCode::CommandParameterError, msg, false);
+ free(msg);
+ return -1;
+ }
+
+ char* addrStr = argv[1];
+ int addrLen = strtol(argv[2], nullptr, 10);
+ int addrFamily = strtol(argv[3], nullptr, 10);
+ uid_t uid = cli->getUid();
+ unsigned netId = strtoul(argv[4], nullptr, 10);
+ const bool useLocalNameservers = checkAndClearUseLocalNameserversFlag(&netId);
+
+ void* addr = malloc(sizeof(in6_addr));
+ errno = 0;
+ int result = inet_pton(addrFamily, addrStr, addr);
+ if (result <= 0) {
+ char* msg = nullptr;
+ asprintf(&msg, "inet_pton(\"%s\") failed %s", addrStr, strerror(errno));
+ LOG(WARNING) << "GetHostByAddrCmd::runCommand: " << (msg ? msg : "null");
+ cli->sendMsg(ResponseCode::OperationFailed, msg, false);
+ free(addr);
+ free(msg);
+ return -1;
+ }
+
+ android_net_context netcontext;
+ gResNetdCallbacks.get_network_context(netId, uid, &netcontext);
+
+ if (useLocalNameservers) {
+ netcontext.flags |= NET_CONTEXT_FLAG_USE_LOCAL_NAMESERVERS;
+ }
+
+ DnsProxyListener::GetHostByAddrHandler* handler = new DnsProxyListener::GetHostByAddrHandler(
+ cli, addr, addrLen, addrFamily, netcontext);
+ tryThreadOrError(cli, handler);
+ return 0;
+}
+
+DnsProxyListener::GetHostByAddrHandler::GetHostByAddrHandler(SocketClient* c, void* address,
+ int addressLen, int addressFamily,
+ const android_net_context& netcontext)
+ : mClient(c),
+ mAddress(address),
+ mAddressLen(addressLen),
+ mAddressFamily(addressFamily),
+ mNetContext(netcontext) {}
+
+DnsProxyListener::GetHostByAddrHandler::~GetHostByAddrHandler() {
+ free(mAddress);
+}
+
+void DnsProxyListener::GetHostByAddrHandler::doDns64ReverseLookup(struct hostent** hpp,
+ NetworkDnsEventReported* event) {
+ if (*hpp != nullptr || mAddressFamily != AF_INET6 || !mAddress) {
+ return;
+ }
+
+ netdutils::IPPrefix prefix{};
+ if (!getDns64Prefix(mNetContext.dns_netid, &prefix)) {
+ return;
+ }
+
+ if (!isValidNat64Prefix(prefix)) {
+ return;
+ }
+
+ struct sockaddr_storage ss = netdutils::IPSockAddr(prefix.ip());
+ struct sockaddr_in6* v6prefix = (struct sockaddr_in6*) &ss;
+ struct in6_addr v6addr = *(in6_addr*) mAddress;
+ // Check if address has NAT64 prefix. Only /96 IPv6 NAT64 prefixes are supported
+ if ((v6addr.s6_addr32[0] != v6prefix->sin6_addr.s6_addr32[0]) ||
+ (v6addr.s6_addr32[1] != v6prefix->sin6_addr.s6_addr32[1]) ||
+ (v6addr.s6_addr32[2] != v6prefix->sin6_addr.s6_addr32[2])) {
+ return;
+ }
+
+ const uid_t uid = mClient->getUid();
+ if (queryLimiter.start(uid)) {
+ // Remove NAT64 prefix and do reverse DNS query
+ struct in_addr v4addr = {.s_addr = v6addr.s6_addr32[3]};
+ android_gethostbyaddrfornetcontext(&v4addr, sizeof(v4addr), AF_INET, &mNetContext, hpp,
+ event);
+ queryLimiter.finish(uid);
+ if (*hpp) {
+ // Replace IPv4 address with original queried IPv6 address in place. The space has
+ // reserved by dns_gethtbyaddr() and netbsd_gethostent_r() in
+ // system/netd/resolv/gethnamaddr.cpp.
+ // Note that android_gethostbyaddrfornetcontext returns only one entry in result.
+ memcpy((*hpp)->h_addr_list[0], &v6addr, sizeof(v6addr));
+ (*hpp)->h_addrtype = AF_INET6;
+ (*hpp)->h_length = sizeof(struct in6_addr);
+ }
+ } else {
+ LOG(ERROR) << __func__ << ": from UID " << uid << ", max concurrent queries reached";
+ }
+}
+
+void DnsProxyListener::GetHostByAddrHandler::run() {
+ Stopwatch s;
+ maybeFixupNetContext(&mNetContext);
+ const uid_t uid = mClient->getUid();
+ hostent* hp = nullptr;
+ int32_t rv = 0;
+ NetworkDnsEventReported event;
+ initDnsEvent(&event);
+ if (queryLimiter.start(uid)) {
+ rv = android_gethostbyaddrfornetcontext(mAddress, mAddressLen, mAddressFamily, &mNetContext,
+ &hp, &event);
+ queryLimiter.finish(uid);
+ } else {
+ rv = EAI_MEMORY;
+ LOG(ERROR) << "GetHostByAddrHandler::run: from UID " << uid
+ << ", max concurrent queries reached";
+ }
+
+ doDns64ReverseLookup(&hp, &event);
+ const int32_t latencyUs = saturate_cast<int32_t>(s.timeTakenUs());
+ event.set_latency_micros(latencyUs);
+ event.set_event_type(EVENT_GETHOSTBYADDR);
+
+ LOG(DEBUG) << "GetHostByAddrHandler::run: result: " << (hp ? "success" : gai_strerror(rv));
+
+ bool success = true;
+ if (hp) {
+ success = mClient->sendCode(ResponseCode::DnsProxyQueryResult) == 0;
+ success &= sendhostent(mClient, hp);
+ } else {
+ success = mClient->sendBinaryMsg(ResponseCode::DnsProxyOperationFailed, nullptr, 0) == 0;
+ }
+
+ if (!success) {
+ LOG(WARNING) << "GetHostByAddrHandler::run: Error writing DNS result to client";
+ }
+
+ reportDnsEvent(INetdEventListener::EVENT_GETHOSTBYADDR, mNetContext, latencyUs, rv, event,
+ (hp && hp->h_name) ? hp->h_name : "null", {}, 0);
+ mClient->decRef();
+}
+
+} // namespace net
+} // namespace android
diff --git a/resolv/DnsProxyListener.h b/resolv/DnsProxyListener.h
new file mode 100644
index 0000000..9b71bc5
--- /dev/null
+++ b/resolv/DnsProxyListener.h
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#pragma once
+
+#include <string>
+
+#include <netd_resolv/resolv.h> // android_net_context
+#include <sysutils/FrameworkCommand.h>
+#include <sysutils/FrameworkListener.h>
+
+struct addrinfo;
+struct hostent;
+
+namespace android {
+namespace net {
+
+class NetworkDnsEventReported;
+
+class DnsProxyListener : public FrameworkListener {
+ public:
+ DnsProxyListener();
+ virtual ~DnsProxyListener() {}
+
+ static constexpr const char* SOCKET_NAME = "dnsproxyd";
+
+ private:
+ class GetAddrInfoCmd : public FrameworkCommand {
+ public:
+ GetAddrInfoCmd();
+ virtual ~GetAddrInfoCmd() {}
+ int runCommand(SocketClient* c, int argc, char** argv) override;
+ };
+
+ /* ------ getaddrinfo ------*/
+ class GetAddrInfoHandler {
+ public:
+ // Note: All of host, service, and hints may be NULL
+ GetAddrInfoHandler(SocketClient* c, char* host, char* service, addrinfo* hints,
+ const android_net_context& netcontext);
+ ~GetAddrInfoHandler();
+
+ void run();
+
+ private:
+ void doDns64Synthesis(int32_t* rv, addrinfo** res, NetworkDnsEventReported* event);
+
+ SocketClient* mClient; // ref counted
+ char* mHost; // owned. TODO: convert to std::string.
+ char* mService; // owned. TODO: convert to std::string.
+ addrinfo* mHints; // owned
+ android_net_context mNetContext;
+ };
+
+ /* ------ gethostbyname ------*/
+ class GetHostByNameCmd : public FrameworkCommand {
+ public:
+ GetHostByNameCmd();
+ virtual ~GetHostByNameCmd() {}
+ int runCommand(SocketClient* c, int argc, char** argv) override;
+ };
+
+ class GetHostByNameHandler {
+ public:
+ GetHostByNameHandler(SocketClient* c, char* name, int af,
+ const android_net_context& netcontext);
+ ~GetHostByNameHandler();
+
+ void run();
+
+ private:
+ void doDns64Synthesis(int32_t* rv, hostent** hpp, NetworkDnsEventReported* event);
+
+ SocketClient* mClient; // ref counted
+ char* mName; // owned. TODO: convert to std::string.
+ int mAf;
+ android_net_context mNetContext;
+ };
+
+ /* ------ gethostbyaddr ------*/
+ class GetHostByAddrCmd : public FrameworkCommand {
+ public:
+ GetHostByAddrCmd();
+ virtual ~GetHostByAddrCmd() {}
+ int runCommand(SocketClient* c, int argc, char** argv) override;
+ };
+
+ class GetHostByAddrHandler {
+ public:
+ GetHostByAddrHandler(SocketClient* c, void* address, int addressLen, int addressFamily,
+ const android_net_context& netcontext);
+ ~GetHostByAddrHandler();
+
+ void run();
+
+ private:
+ void doDns64ReverseLookup(hostent** hpp, NetworkDnsEventReported* event);
+
+ SocketClient* mClient; // ref counted
+ void* mAddress; // address to lookup; owned
+ int mAddressLen; // length of address to look up
+ int mAddressFamily; // address family
+ android_net_context mNetContext;
+ };
+
+ /* ------ resnsend ------*/
+ class ResNSendCommand : public FrameworkCommand {
+ public:
+ ResNSendCommand();
+ virtual ~ResNSendCommand() {}
+ int runCommand(SocketClient* c, int argc, char** argv) override;
+ };
+
+ class ResNSendHandler {
+ public:
+ ResNSendHandler(SocketClient* c, std::string msg, uint32_t flags,
+ const android_net_context& netcontext);
+ ~ResNSendHandler();
+
+ void run();
+
+ private:
+ SocketClient* mClient; // ref counted
+ std::string mMsg;
+ uint32_t mFlags;
+ android_net_context mNetContext;
+ };
+
+ /* ------ getdnsnetid ------*/
+ class GetDnsNetIdCommand : public FrameworkCommand {
+ public:
+ GetDnsNetIdCommand();
+ virtual ~GetDnsNetIdCommand() {}
+ int runCommand(SocketClient* c, int argc, char** argv) override;
+ };
+};
+
+} // namespace net
+} // namespace android
diff --git a/resolv/DnsResolver.cpp b/resolv/DnsResolver.cpp
new file mode 100644
index 0000000..092ff3f
--- /dev/null
+++ b/resolv/DnsResolver.cpp
@@ -0,0 +1,82 @@
+/**
+ * Copyright (c) 2019, 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.
+ */
+
+#include "DnsResolver.h"
+
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+
+#include "DnsProxyListener.h"
+#include "DnsResolverService.h"
+#include "netd_resolv/resolv.h"
+#include "res_debug.h"
+
+bool resolv_init(const ResolverNetdCallbacks* callbacks) {
+ android::base::InitLogging(/*argv=*/nullptr);
+ android::base::SetDefaultTag("libnetd_resolv");
+ LOG(INFO) << __func__ << ": Initializing resolver";
+ resolv_set_log_severity(android::base::WARNING);
+
+ android::net::gResNetdCallbacks = *callbacks;
+ android::net::gDnsResolv = android::net::DnsResolver::getInstance();
+ return android::net::gDnsResolv->start();
+}
+
+namespace android {
+namespace net {
+
+namespace {
+
+bool verifyCallbacks() {
+ return gResNetdCallbacks.check_calling_permission && gResNetdCallbacks.get_network_context &&
+ gResNetdCallbacks.log;
+}
+
+} // namespace
+
+DnsResolver* gDnsResolv = nullptr;
+ResolverNetdCallbacks gResNetdCallbacks;
+netdutils::Log gDnsResolverLog("dnsResolver");
+
+DnsResolver* DnsResolver::getInstance() {
+ // Instantiated on first use.
+ static DnsResolver instance;
+ return &instance;
+}
+
+bool DnsResolver::start() {
+ if (!verifyCallbacks()) {
+ LOG(ERROR) << __func__ << ": Callback verification failed";
+ return false;
+ }
+ if (mDnsProxyListener.startListener()) {
+ PLOG(ERROR) << __func__ << ": Unable to start DnsProxyListener";
+ return false;
+ }
+ binder_status_t ret;
+ if ((ret = DnsResolverService::start()) != STATUS_OK) {
+ LOG(ERROR) << __func__ << ": Unable to start DnsResolverService: " << ret;
+ return false;
+ }
+ return true;
+}
+
+int DnsResolver::setLogSeverity(int32_t logSeverity) {
+ return resolv_set_log_severity(logSeverity);
+}
+
+} // namespace net
+} // namespace android
diff --git a/resolv/DnsResolver.h b/resolv/DnsResolver.h
new file mode 100644
index 0000000..6d8d2ce
--- /dev/null
+++ b/resolv/DnsResolver.h
@@ -0,0 +1,51 @@
+/**
+ * Copyright (c) 2019, 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.
+ */
+
+#ifndef _DNS_RESOLVER_H_
+#define _DNS_RESOLVER_H_
+
+#include "DnsProxyListener.h"
+#include "ResolverController.h"
+#include "netd_resolv/resolv.h"
+#include "netdutils/Log.h"
+
+namespace android {
+namespace net {
+
+class DnsResolver {
+ public:
+ static DnsResolver* getInstance();
+ bool start();
+ int setLogSeverity(int32_t logSeverity);
+
+ DnsResolver(DnsResolver const&) = delete;
+ void operator=(DnsResolver const&) = delete;
+
+ ResolverController resolverCtrl;
+
+ private:
+ DnsResolver() {}
+ DnsProxyListener mDnsProxyListener;
+};
+
+extern DnsResolver* gDnsResolv;
+extern ResolverNetdCallbacks gResNetdCallbacks;
+extern netdutils::Log gDnsResolverLog;
+
+} // namespace net
+} // namespace android
+
+#endif // _DNS_RESOLVER_H_
diff --git a/resolv/DnsResolverService.cpp b/resolv/DnsResolverService.cpp
new file mode 100644
index 0000000..8eefd22
--- /dev/null
+++ b/resolv/DnsResolverService.cpp
@@ -0,0 +1,297 @@
+/**
+ * Copyright (c) 2019, 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.
+ */
+
+#define LOG_TAG "DnsResolverService"
+
+#include "DnsResolverService.h"
+
+#include <set>
+#include <vector>
+
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <json/value.h>
+#include <json/writer.h>
+#include <log/log.h>
+#include <netdutils/DumpWriter.h>
+#include <openssl/base64.h>
+#include <private/android_filesystem_config.h> // AID_SYSTEM
+
+#include "BinderUtil.h"
+#include "DnsResolver.h"
+#include "NetdConstants.h" // SHA256_SIZE
+#include "NetdPermissions.h" // PERM_*
+#include "ResolverEventReporter.h"
+#include "resolv_cache.h"
+
+using aidl::android::net::ResolverParamsParcel;
+using android::base::Join;
+using android::base::StringPrintf;
+using android::netdutils::DumpWriter;
+
+namespace android {
+namespace net {
+
+namespace {
+
+#define ENFORCE_ANY_PERMISSION(...) \
+ do { \
+ ::ndk::ScopedAStatus status = checkAnyPermission({__VA_ARGS__}); \
+ if (!status.isOk()) { \
+ return status; \
+ } \
+ } while (0)
+
+#define ENFORCE_INTERNAL_PERMISSIONS() \
+ ENFORCE_ANY_PERMISSION(PERM_CONNECTIVITY_INTERNAL, PERM_MAINLINE_NETWORK_STACK)
+
+#define ENFORCE_NETWORK_STACK_PERMISSIONS() \
+ ENFORCE_ANY_PERMISSION(PERM_NETWORK_STACK, PERM_MAINLINE_NETWORK_STACK)
+
+inline ::ndk::ScopedAStatus statusFromErrcode(int ret) {
+ if (ret) {
+ return ::ndk::ScopedAStatus(
+ AStatus_fromServiceSpecificErrorWithMessage(-ret, strerror(-ret)));
+ }
+ return ::ndk::ScopedAStatus(AStatus_newOk());
+}
+
+} // namespace
+
+DnsResolverService::DnsResolverService() {
+ // register log callback to BnDnsResolver::logFunc
+ BnDnsResolver::logFunc =
+ std::bind(binderCallLogFn, std::placeholders::_1,
+ [](const std::string& msg) { gResNetdCallbacks.log(msg.c_str()); });
+}
+
+binder_status_t DnsResolverService::start() {
+ // TODO: Add disableBackgroundScheduling(true) after libbinder_ndk support it. b/126506010
+ // NetdNativeService does call disableBackgroundScheduling currently, so it is fine now.
+ DnsResolverService* resolverService = new DnsResolverService();
+ binder_status_t status =
+ AServiceManager_addService(resolverService->asBinder().get(), getServiceName());
+ if (status != STATUS_OK) {
+ return status;
+ }
+
+ ABinderProcess_startThreadPool();
+
+ // TODO: register log callback if binder NDK backend support it. b/126501406
+
+ return STATUS_OK;
+}
+
+binder_status_t DnsResolverService::dump(int fd, const char**, uint32_t) {
+ auto dump_permission = checkAnyPermission({PERM_DUMP});
+ if (!dump_permission.isOk()) {
+ return STATUS_PERMISSION_DENIED;
+ }
+
+ // This method does not grab any locks. If individual classes need locking
+ // their dump() methods MUST handle locking appropriately.
+ DumpWriter dw(fd);
+ for (auto netId : resolv_list_caches()) {
+ dw.println("NetId: %u", netId);
+ gDnsResolv->resolverCtrl.dump(dw, netId);
+ dw.blankline();
+ }
+
+ return STATUS_OK;
+}
+
+::ndk::ScopedAStatus DnsResolverService::isAlive(bool* alive) {
+ ENFORCE_INTERNAL_PERMISSIONS();
+
+ *alive = true;
+
+ return ::ndk::ScopedAStatus(AStatus_newOk());
+}
+
+::ndk::ScopedAStatus DnsResolverService::registerEventListener(
+ const std::shared_ptr<aidl::android::net::metrics::INetdEventListener>& listener) {
+ ENFORCE_NETWORK_STACK_PERMISSIONS();
+
+ int res = ResolverEventReporter::getInstance().addListener(listener);
+
+ return statusFromErrcode(res);
+}
+
+::ndk::ScopedAStatus DnsResolverService::checkAnyPermission(
+ const std::vector<const char*>& permissions) {
+ // TODO: Remove callback and move this to unnamed namespace after libbiner_ndk supports
+ // check_permission.
+ if (!gResNetdCallbacks.check_calling_permission) {
+ return ::ndk::ScopedAStatus(AStatus_fromExceptionCodeWithMessage(
+ EX_NULL_POINTER, "check_calling_permission is null"));
+ }
+ pid_t pid = AIBinder_getCallingPid();
+ uid_t uid = AIBinder_getCallingUid();
+
+ // If the caller is the system UID, don't check permissions.
+ // Otherwise, if the system server's binder thread pool is full, and all the threads are
+ // blocked on a thread that's waiting for us to complete, we deadlock. http://b/69389492
+ //
+ // From a security perspective, there is currently no difference, because:
+ // 1. The only permissions we check in netd's binder interface are CONNECTIVITY_INTERNAL
+ // and NETWORK_STACK, which the system server always has (or MAINLINE_NETWORK_STACK, which
+ // is equivalent to having both CONNECTIVITY_INTERNAL and NETWORK_STACK).
+ // 2. AID_SYSTEM always has all permissions. See ActivityManager#checkComponentPermission.
+ if (uid == AID_SYSTEM) {
+ return ::ndk::ScopedAStatus(AStatus_newOk());
+ }
+
+ for (const char* permission : permissions) {
+ if (gResNetdCallbacks.check_calling_permission(permission)) {
+ return ::ndk::ScopedAStatus(AStatus_newOk());
+ }
+ }
+
+ auto err = StringPrintf("UID %d / PID %d does not have any of the following permissions: %s",
+ uid, pid, Join(permissions, ',').c_str());
+ return ::ndk::ScopedAStatus(AStatus_fromExceptionCodeWithMessage(EX_SECURITY, err.c_str()));
+}
+
+namespace {
+
+// Parse a base64 encoded string into a vector of bytes.
+// On failure, return an empty vector.
+static std::vector<uint8_t> parseBase64(const std::string& input) {
+ std::vector<uint8_t> decoded;
+ size_t out_len;
+ if (EVP_DecodedLength(&out_len, input.size()) != 1) {
+ return decoded;
+ }
+ // out_len is now an upper bound on the output length.
+ decoded.resize(out_len);
+ if (EVP_DecodeBase64(decoded.data(), &out_len, decoded.size(),
+ reinterpret_cast<const uint8_t*>(input.data()), input.size()) == 1) {
+ // Possibly shrink the vector if the actual output was smaller than the bound.
+ decoded.resize(out_len);
+ } else {
+ decoded.clear();
+ }
+ if (out_len != SHA256_SIZE) {
+ decoded.clear();
+ }
+ return decoded;
+}
+
+} // namespace
+
+::ndk::ScopedAStatus DnsResolverService::setResolverConfiguration(
+ const ResolverParamsParcel& resolverParams) {
+ // Locking happens in PrivateDnsConfiguration and res_* functions.
+ ENFORCE_INTERNAL_PERMISSIONS();
+ // TODO: Remove this log after AIDL gen_log supporting more types, b/129732660
+ auto entry =
+ gDnsResolverLog.newEntry()
+ .prettyFunction(__PRETTY_FUNCTION__)
+ .args(resolverParams.netId, resolverParams.servers, resolverParams.domains,
+ resolverParams.sampleValiditySeconds, resolverParams.successThreshold,
+ resolverParams.minSamples, resolverParams.maxSamples,
+ resolverParams.baseTimeoutMsec, resolverParams.retryCount,
+ resolverParams.tlsServers, resolverParams.tlsFingerprints);
+
+ std::set<std::vector<uint8_t>> decoded_fingerprints;
+ for (const std::string& fingerprint : resolverParams.tlsFingerprints) {
+ std::vector<uint8_t> decoded = parseBase64(fingerprint);
+ if (decoded.empty()) {
+ return ::ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ EINVAL, "ResolverController error: bad fingerprint"));
+ }
+ decoded_fingerprints.emplace(decoded);
+ }
+
+ int res =
+ gDnsResolv->resolverCtrl.setResolverConfiguration(resolverParams, decoded_fingerprints);
+
+ gResNetdCallbacks.log(entry.returns(res).withAutomaticDuration().toString().c_str());
+
+ return statusFromErrcode(res);
+}
+
+::ndk::ScopedAStatus DnsResolverService::getResolverInfo(
+ int32_t netId, std::vector<std::string>* servers, std::vector<std::string>* domains,
+ std::vector<std::string>* tlsServers, std::vector<int32_t>* params,
+ std::vector<int32_t>* stats, std::vector<int32_t>* wait_for_pending_req_timeout_count) {
+ // Locking happens in PrivateDnsConfiguration and res_* functions.
+ ENFORCE_NETWORK_STACK_PERMISSIONS();
+
+ int res = gDnsResolv->resolverCtrl.getResolverInfo(netId, servers, domains, tlsServers, params,
+ stats, wait_for_pending_req_timeout_count);
+
+ return statusFromErrcode(res);
+}
+
+::ndk::ScopedAStatus DnsResolverService::startPrefix64Discovery(int32_t netId) {
+ // Locking happens in Dns64Configuration.
+ ENFORCE_NETWORK_STACK_PERMISSIONS();
+
+ gDnsResolv->resolverCtrl.startPrefix64Discovery(netId);
+
+ return ::ndk::ScopedAStatus(AStatus_newOk());
+}
+
+::ndk::ScopedAStatus DnsResolverService::stopPrefix64Discovery(int32_t netId) {
+ // Locking happens in Dns64Configuration.
+ ENFORCE_NETWORK_STACK_PERMISSIONS();
+
+ gDnsResolv->resolverCtrl.stopPrefix64Discovery(netId);
+
+ return ::ndk::ScopedAStatus(AStatus_newOk());
+}
+
+::ndk::ScopedAStatus DnsResolverService::getPrefix64(int netId, std::string* stringPrefix) {
+ ENFORCE_NETWORK_STACK_PERMISSIONS();
+
+ netdutils::IPPrefix prefix{};
+ int res = gDnsResolv->resolverCtrl.getPrefix64(netId, &prefix);
+ *stringPrefix = prefix.toString();
+
+ return statusFromErrcode(res);
+}
+
+::ndk::ScopedAStatus DnsResolverService::setLogSeverity(int32_t logSeverity) {
+ ENFORCE_NETWORK_STACK_PERMISSIONS();
+
+ int res = gDnsResolv->setLogSeverity(logSeverity);
+
+ return statusFromErrcode(res);
+}
+
+::ndk::ScopedAStatus DnsResolverService::destroyNetworkCache(int netId) {
+ // Locking happens in res_cache.cpp functions.
+ ENFORCE_NETWORK_STACK_PERMISSIONS();
+
+ gDnsResolv->resolverCtrl.destroyNetworkCache(netId);
+
+ return ::ndk::ScopedAStatus(AStatus_newOk());
+}
+
+::ndk::ScopedAStatus DnsResolverService::createNetworkCache(int netId) {
+ // Locking happens in res_cache.cpp functions.
+ ENFORCE_NETWORK_STACK_PERMISSIONS();
+
+ int res = gDnsResolv->resolverCtrl.createNetworkCache(netId);
+
+ return statusFromErrcode(res);
+}
+
+} // namespace net
+} // namespace android
diff --git a/resolv/DnsResolverService.h b/resolv/DnsResolverService.h
new file mode 100644
index 0000000..1a1c9e7
--- /dev/null
+++ b/resolv/DnsResolverService.h
@@ -0,0 +1,72 @@
+/**
+ * Copyright (c) 2019, 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.
+ */
+
+#ifndef _DNS_RESOLVER_SERVICE_H_
+#define _DNS_RESOLVER_SERVICE_H_
+
+#include <vector>
+
+#include <aidl/android/net/BnDnsResolver.h>
+#include <aidl/android/net/ResolverParamsParcel.h>
+#include <android/binder_ibinder.h>
+
+#include "netd_resolv/resolv.h"
+
+namespace android {
+namespace net {
+
+class DnsResolverService : public aidl::android::net::BnDnsResolver {
+ public:
+ static binder_status_t start();
+ static char const* getServiceName() { return "dnsresolver"; }
+
+ binder_status_t dump(int fd, const char** args, uint32_t numArgs) override;
+
+ ::ndk::ScopedAStatus isAlive(bool* alive) override;
+ ::ndk::ScopedAStatus registerEventListener(
+ const std::shared_ptr<aidl::android::net::metrics::INetdEventListener>& listener)
+ override;
+
+ // Resolver commands.
+ ::ndk::ScopedAStatus setResolverConfiguration(
+ const aidl::android::net::ResolverParamsParcel& resolverParams) override;
+ ::ndk::ScopedAStatus getResolverInfo(
+ int32_t netId, std::vector<std::string>* servers, std::vector<std::string>* domains,
+ std::vector<std::string>* tlsServers, std::vector<int32_t>* params,
+ std::vector<int32_t>* stats,
+ std::vector<int32_t>* wait_for_pending_req_timeout_count) override;
+ ::ndk::ScopedAStatus destroyNetworkCache(int32_t netId) override;
+ ::ndk::ScopedAStatus createNetworkCache(int32_t netId) override;
+
+ // DNS64-related commands
+ ::ndk::ScopedAStatus startPrefix64Discovery(int32_t netId) override;
+ ::ndk::ScopedAStatus stopPrefix64Discovery(int32_t netId) override;
+ // (internal use only)
+ ::ndk::ScopedAStatus getPrefix64(int netId, std::string* stringPrefix) override;
+
+ // Debug log command
+ ::ndk::ScopedAStatus setLogSeverity(int32_t logSeverity) override;
+
+ private:
+ DnsResolverService();
+ // TODO: Remove below items after libbiner_ndk supports check_permission.
+ ::ndk::ScopedAStatus checkAnyPermission(const std::vector<const char*>& permissions);
+};
+
+} // namespace net
+} // namespace android
+
+#endif // _DNS_RESOLVER_SERVICE_H_
diff --git a/resolv/DnsTlsDispatcher.cpp b/resolv/DnsTlsDispatcher.cpp
new file mode 100644
index 0000000..baafa53
--- /dev/null
+++ b/resolv/DnsTlsDispatcher.cpp
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#define LOG_TAG "DnsTlsDispatcher"
+//#define LOG_NDEBUG 0
+
+#include "DnsTlsDispatcher.h"
+#include <netdutils/Stopwatch.h>
+#include "DnsTlsSocketFactory.h"
+#include "resolv_private.h"
+#include "stats.pb.h"
+
+#include "log/log.h"
+
+namespace android {
+namespace net {
+
+using android::netdutils::Stopwatch;
+using netdutils::Slice;
+
+// static
+std::mutex DnsTlsDispatcher::sLock;
+
+DnsTlsDispatcher::DnsTlsDispatcher() {
+ mFactory.reset(new DnsTlsSocketFactory());
+}
+
+std::list<DnsTlsServer> DnsTlsDispatcher::getOrderedServerList(
+ const std::list<DnsTlsServer> &tlsServers, unsigned mark) const {
+ // Our preferred DnsTlsServer order is:
+ // 1) reuse existing IPv6 connections
+ // 2) reuse existing IPv4 connections
+ // 3) establish new IPv6 connections
+ // 4) establish new IPv4 connections
+ std::list<DnsTlsServer> existing6;
+ std::list<DnsTlsServer> existing4;
+ std::list<DnsTlsServer> new6;
+ std::list<DnsTlsServer> new4;
+
+ // Pull out any servers for which we might have existing connections and
+ // place them at the from the list of servers to try.
+ {
+ std::lock_guard guard(sLock);
+
+ for (const auto& tlsServer : tlsServers) {
+ const Key key = std::make_pair(mark, tlsServer);
+ if (mStore.find(key) != mStore.end()) {
+ switch (tlsServer.ss.ss_family) {
+ case AF_INET:
+ existing4.push_back(tlsServer);
+ break;
+ case AF_INET6:
+ existing6.push_back(tlsServer);
+ break;
+ }
+ } else {
+ switch (tlsServer.ss.ss_family) {
+ case AF_INET:
+ new4.push_back(tlsServer);
+ break;
+ case AF_INET6:
+ new6.push_back(tlsServer);
+ break;
+ }
+ }
+ }
+ }
+
+ auto& out = existing6;
+ out.splice(out.cend(), existing4);
+ out.splice(out.cend(), new6);
+ out.splice(out.cend(), new4);
+ return out;
+}
+
+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));
+
+ if (orderedServers.empty()) ALOGW("Empty DnsTlsServer list");
+
+ DnsTlsTransport::Response code = DnsTlsTransport::Response::internal_error;
+ int serverCount = 0;
+ for (const auto& server : orderedServers) {
+ DnsQueryEvent* dnsQueryEvent =
+ statp->event->mutable_dns_query_events()->add_dns_query_event();
+ dnsQueryEvent->set_rcode(NS_R_INTERNAL_ERROR);
+ Stopwatch query_stopwatch;
+ code = this->query(server, statp->_mark, query, ans, resplen);
+
+ dnsQueryEvent->set_latency_micros(saturate_cast<int32_t>(query_stopwatch.timeTakenUs()));
+ dnsQueryEvent->set_dns_server_index(serverCount++);
+ dnsQueryEvent->set_ip_version(ipFamilyToIPVersion(server.ss.ss_family));
+ dnsQueryEvent->set_protocol(PROTO_DOT);
+ dnsQueryEvent->set_type(getQueryType(query.base(), query.size()));
+
+ switch (code) {
+ // These response codes are valid responses and not expected to
+ // change if another server is queried.
+ case DnsTlsTransport::Response::success:
+ dnsQueryEvent->set_rcode(
+ static_cast<NsRcode>(reinterpret_cast<HEADER*>(ans.base())->rcode));
+ [[fallthrough]];
+ case DnsTlsTransport::Response::limit_error:
+ return code;
+ // These response codes might differ when trying other servers, so
+ // keep iterating to see if we can get a different (better) result.
+ case DnsTlsTransport::Response::network_error:
+ // Sync from res_tls_send in res_send.cpp
+ dnsQueryEvent->set_rcode(NS_R_TIMEOUT);
+ [[fallthrough]];
+ case DnsTlsTransport::Response::internal_error:
+ continue;
+ // No "default" statement.
+ }
+ }
+
+ return code;
+}
+
+DnsTlsTransport::Response DnsTlsDispatcher::query(const DnsTlsServer& server, unsigned mark,
+ const Slice query,
+ const Slice ans, int *resplen) {
+ const Key key = std::make_pair(mark, server);
+ 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();
+ }
+ ++xport->useCount;
+ }
+
+ ALOGV("Sending query of length %zu", query.size());
+ auto res = xport->transport.query(query);
+ ALOGV("Awaiting response");
+ const auto& result = res.get();
+ DnsTlsTransport::Response code = result.code;
+ if (code == DnsTlsTransport::Response::success) {
+ if (result.response.size() > ans.size()) {
+ ALOGV("Response too large: %zu > %zu", result.response.size(), ans.size());
+ code = DnsTlsTransport::Response::limit_error;
+ } else {
+ ALOGV("Got response successfully");
+ *resplen = result.response.size();
+ netdutils::copy(ans, netdutils::makeSlice(result.response));
+ }
+ } else {
+ ALOGV("Query failed: %u", (unsigned int) code);
+ }
+
+ auto now = std::chrono::steady_clock::now();
+ {
+ std::lock_guard guard(sLock);
+ --xport->useCount;
+ xport->lastUsed = now;
+ cleanup(now);
+ }
+ return code;
+}
+
+// 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) {
+ // To avoid scanning mStore after every query, return early if a cleanup has been
+ // performed recently.
+ if (now - mLastCleanup < IDLE_TIMEOUT) {
+ return;
+ }
+ for (auto it = mStore.begin(); it != mStore.end();) {
+ auto& s = it->second;
+ if (s->useCount == 0 && now - s->lastUsed > IDLE_TIMEOUT) {
+ it = mStore.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ mLastCleanup = now;
+}
+
+} // end of namespace net
+} // end of namespace android
diff --git a/resolv/DnsTlsDispatcher.h b/resolv/DnsTlsDispatcher.h
new file mode 100644
index 0000000..b8e9968
--- /dev/null
+++ b/resolv/DnsTlsDispatcher.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef _DNS_DNSTLSDISPATCHER_H
+#define _DNS_DNSTLSDISPATCHER_H
+
+#include <list>
+#include <map>
+#include <memory>
+#include <mutex>
+
+#include <android-base/thread_annotations.h>
+#include <netdutils/Slice.h>
+
+#include "DnsTlsServer.h"
+#include "DnsTlsTransport.h"
+#include "IDnsTlsSocketFactory.h"
+#include "resolv_private.h"
+
+namespace android {
+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.
+class DnsTlsDispatcher {
+ public:
+ // Default constructor.
+ DnsTlsDispatcher();
+
+ // Constructor with dependency injection for testing.
+ explicit DnsTlsDispatcher(std::unique_ptr<IDnsTlsSocketFactory> factory)
+ : mFactory(std::move(factory)) {}
+
+ // Enqueues |query| for resolution via the given |tlsServers| on the
+ // network indicated by |mark|; writes the response into |ans|, and stores
+ // the count of bytes written in |resplen|. Returns a success or error code.
+ // The order in which servers from |tlsServers| are queried may not be the
+ // order passed in by the caller.
+ DnsTlsTransport::Response query(const std::list<DnsTlsServer>& tlsServers,
+ res_state _Nonnull statp, const netdutils::Slice query,
+ const netdutils::Slice ans, int* _Nonnull resplen);
+
+ // Given a |query|, sends it to the server on the network indicated by |mark|,
+ // and writes the response into |ans|, and indicates
+ // the number of bytes written in |resplen|. Returns a success or error code.
+ DnsTlsTransport::Response query(const DnsTlsServer& server, unsigned mark,
+ const netdutils::Slice query, const netdutils::Slice ans,
+ int* _Nonnull resplen);
+
+ private:
+ // This lock is static so that it can be used to annotate the Transport struct.
+ // DnsTlsDispatcher is a singleton in practice, so making this static does not change
+ // the locking behavior.
+ static std::mutex sLock;
+
+ // Key = <mark, server>
+ typedef std::pair<unsigned, const DnsTlsServer> Key;
+
+ // 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) {}
+ // 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
+ // destroyed.
+ 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);
+ };
+
+ // 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);
+
+ // 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;
+
+ // Trivial factory for DnsTlsSockets. Dependency injection is only used for testing.
+ std::unique_ptr<IDnsTlsSocketFactory> mFactory;
+};
+
+} // end of namespace net
+} // end of namespace android
+
+#endif // _DNS_DNSTLSDISPATCHER_H
diff --git a/resolv/DnsTlsQueryMap.cpp b/resolv/DnsTlsQueryMap.cpp
new file mode 100644
index 0000000..6e6399c
--- /dev/null
+++ b/resolv/DnsTlsQueryMap.cpp
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#define LOG_TAG "DnsTlsQueryMap"
+//#define LOG_NDEBUG 0
+
+#include "DnsTlsQueryMap.h"
+
+#include "log/log.h"
+
+namespace android {
+namespace net {
+
+std::unique_ptr<DnsTlsQueryMap::QueryFuture> DnsTlsQueryMap::recordQuery(
+ const netdutils::Slice query) {
+ std::lock_guard guard(mLock);
+
+ // Store the query so it can be matched to the response or reissued.
+ if (query.size() < 2) {
+ ALOGW("Query is too short");
+ return nullptr;
+ }
+ int32_t newId = getFreeId();
+ if (newId < 0) {
+ ALOGW("All query IDs are in use");
+ return nullptr;
+ }
+ Query q = { .newId = static_cast<uint16_t>(newId), .query = query };
+ std::map<uint16_t, QueryPromise>::iterator it;
+ bool inserted;
+ std::tie(it, inserted) = mQueries.emplace(newId, q);
+ if (!inserted) {
+ ALOGE("Failed to store pending query");
+ return nullptr;
+ }
+ return std::make_unique<QueryFuture>(q, it->second.result.get_future());
+}
+
+void DnsTlsQueryMap::expire(QueryPromise* p) {
+ Result r = { .code = Response::network_error };
+ p->result.set_value(r);
+}
+
+void DnsTlsQueryMap::markTried(uint16_t newId) {
+ std::lock_guard guard(mLock);
+ auto it = mQueries.find(newId);
+ if (it != mQueries.end()) {
+ it->second.tries++;
+ }
+}
+
+void DnsTlsQueryMap::cleanup() {
+ std::lock_guard guard(mLock);
+ for (auto it = mQueries.begin(); it != mQueries.end();) {
+ auto& p = it->second;
+ if (p.tries >= kMaxTries) {
+ expire(&p);
+ it = mQueries.erase(it);
+ } else {
+ ++it;
+ }
+ }
+}
+
+int32_t DnsTlsQueryMap::getFreeId() {
+ if (mQueries.empty()) {
+ return 0;
+ }
+ uint16_t maxId = mQueries.rbegin()->first;
+ if (maxId < UINT16_MAX) {
+ return maxId + 1;
+ }
+ if (mQueries.size() == UINT16_MAX + 1) {
+ // Map is full.
+ return -1;
+ }
+ // Linear scan.
+ uint16_t nextId = 0;
+ for (auto& pair : mQueries) {
+ uint16_t id = pair.first;
+ if (id != nextId) {
+ // Found a gap.
+ return nextId;
+ }
+ nextId = id + 1;
+ }
+ // Unreachable (but the compiler isn't smart enough to prove it).
+ return -1;
+}
+
+std::vector<DnsTlsQueryMap::Query> DnsTlsQueryMap::getAll() {
+ std::lock_guard guard(mLock);
+ std::vector<DnsTlsQueryMap::Query> queries;
+ for (auto& q : mQueries) {
+ queries.push_back(q.second.query);
+ }
+ return queries;
+}
+
+bool DnsTlsQueryMap::empty() {
+ std::lock_guard guard(mLock);
+ return mQueries.empty();
+}
+
+void DnsTlsQueryMap::clear() {
+ std::lock_guard guard(mLock);
+ for (auto& q : mQueries) {
+ expire(&q.second);
+ }
+ mQueries.clear();
+}
+
+void DnsTlsQueryMap::onResponse(std::vector<uint8_t> response) {
+ ALOGV("Got response of size %zu", response.size());
+ if (response.size() < 2) {
+ ALOGW("Response is too short");
+ return;
+ }
+ uint16_t id = response[0] << 8 | response[1];
+ std::lock_guard guard(mLock);
+ auto it = mQueries.find(id);
+ if (it == mQueries.end()) {
+ ALOGW("Discarding response: unknown ID %d", id);
+ return;
+ }
+ Result r = { .code = Response::success, .response = std::move(response) };
+ // Rewrite ID to match the query
+ const uint8_t* data = it->second.query.query.base();
+ r.response[0] = data[0];
+ r.response[1] = data[1];
+ ALOGV("Sending result to dispatcher");
+ it->second.result.set_value(std::move(r));
+ mQueries.erase(it);
+}
+
+} // end of namespace net
+} // end of namespace android
diff --git a/resolv/DnsTlsQueryMap.h b/resolv/DnsTlsQueryMap.h
new file mode 100644
index 0000000..c5ab023
--- /dev/null
+++ b/resolv/DnsTlsQueryMap.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#ifndef _DNS_DNSTLSQUERYMAP_H
+#define _DNS_DNSTLSQUERYMAP_H
+
+#include <future>
+#include <map>
+#include <mutex>
+#include <vector>
+
+#include <android-base/thread_annotations.h>
+#include <netdutils/Slice.h>
+
+#include "DnsTlsServer.h"
+
+namespace android {
+namespace net {
+
+// Keeps track of queries and responses. This class matches responses with queries.
+// All methods are thread-safe and non-blocking.
+class DnsTlsQueryMap {
+ public:
+ struct Query {
+ // The new ID number assigned to this query.
+ uint16_t newId;
+ // A query that has been passed to recordQuery(), with its original ID number.
+ const netdutils::Slice query;
+ };
+
+ typedef DnsTlsServer::Response Response;
+ typedef DnsTlsServer::Result Result;
+
+ struct QueryFuture {
+ QueryFuture(Query query, std::future<Result> result)
+ : query(query), result(std::move(result)) {}
+ Query query;
+ // A future which will resolve to the result of this query.
+ std::future<Result> result;
+ };
+
+ // Returns an object containing everything needed to complete processing of
+ // this query, or null if the query could not be recorded.
+ std::unique_ptr<QueryFuture> recordQuery(const netdutils::Slice query);
+
+ // Process a response, including a new ID. If the response
+ // is not recognized as matching any query, it will be ignored.
+ void onResponse(std::vector<uint8_t> response);
+
+ // Clear all map contents. This causes all pending queries to resolve with failure.
+ void clear();
+
+ // Get all pending queries. This returns a shallow copy, mostly for thread-safety.
+ std::vector<Query> getAll();
+
+ // Mark a query has having been retried. If the query hits the retry limit, it will
+ // be expired at the next call to cleanup.
+ void markTried(uint16_t newId);
+ void cleanup();
+
+ // Returns true if there are no pending queries.
+ bool empty();
+
+ private:
+ std::mutex mLock;
+
+ struct QueryPromise {
+ QueryPromise(Query query) : query(query) {}
+ Query query;
+ // Number of times the query has been tried. Limited to kMaxTries.
+ int tries = 0;
+ // A promise whose future is returned by recordQuery()
+ // It is fulfilled by onResponse().
+ std::promise<Result> result;
+ };
+
+ // The maximum number of times we will send a query before abandoning it.
+ static constexpr int kMaxTries = 3;
+
+ // Outstanding queries by newId.
+ std::map<uint16_t, QueryPromise> mQueries GUARDED_BY(mLock);
+
+ // Get a "newId" number that is not currently in use. Returns -1 if there are none.
+ int32_t getFreeId() REQUIRES(mLock);
+
+ // Fulfill the result with an error code.
+ static void expire(QueryPromise* _Nonnull p);
+};
+
+} // end of namespace net
+} // end of namespace android
+
+#endif // _DNS_DNSTLSQUERYMAP_H
diff --git a/resolv/DnsTlsServer.cpp b/resolv/DnsTlsServer.cpp
new file mode 100644
index 0000000..a97c672
--- /dev/null
+++ b/resolv/DnsTlsServer.cpp
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include "DnsTlsServer.h"
+
+#include <algorithm>
+
+namespace {
+
+// Returns a tuple of references to the elements of a.
+auto make_tie(const sockaddr_in& a) {
+ return std::tie(a.sin_port, a.sin_addr.s_addr);
+}
+
+// Returns a tuple of references to the elements of a.
+auto make_tie(const sockaddr_in6& a) {
+ // Skip flowinfo, which is not relevant.
+ return std::tie(
+ a.sin6_port,
+ a.sin6_addr,
+ a.sin6_scope_id
+ );
+}
+
+} // namespace
+
+// These binary operators make sockaddr_storage comparable. They need to be
+// in the global namespace so that the std::tuple < and == operators can see them.
+static bool operator <(const in6_addr& x, const in6_addr& y) {
+ return std::lexicographical_compare(
+ std::begin(x.s6_addr), std::end(x.s6_addr),
+ std::begin(y.s6_addr), std::end(y.s6_addr));
+}
+
+static bool operator ==(const in6_addr& x, const in6_addr& y) {
+ return std::equal(
+ std::begin(x.s6_addr), std::end(x.s6_addr),
+ std::begin(y.s6_addr), std::end(y.s6_addr));
+}
+
+static bool operator <(const sockaddr_storage& x, const sockaddr_storage& y) {
+ if (x.ss_family != y.ss_family) {
+ return x.ss_family < y.ss_family;
+ }
+ // Same address family.
+ if (x.ss_family == AF_INET) {
+ const sockaddr_in& x_sin = reinterpret_cast<const sockaddr_in&>(x);
+ const sockaddr_in& y_sin = reinterpret_cast<const sockaddr_in&>(y);
+ return make_tie(x_sin) < make_tie(y_sin);
+ } else if (x.ss_family == AF_INET6) {
+ const sockaddr_in6& x_sin6 = reinterpret_cast<const sockaddr_in6&>(x);
+ const sockaddr_in6& y_sin6 = reinterpret_cast<const sockaddr_in6&>(y);
+ return make_tie(x_sin6) < make_tie(y_sin6);
+ }
+ return false; // Unknown address type. This is an error.
+}
+
+static bool operator ==(const sockaddr_storage& x, const sockaddr_storage& y) {
+ if (x.ss_family != y.ss_family) {
+ return false;
+ }
+ // Same address family.
+ if (x.ss_family == AF_INET) {
+ const sockaddr_in& x_sin = reinterpret_cast<const sockaddr_in&>(x);
+ const sockaddr_in& y_sin = reinterpret_cast<const sockaddr_in&>(y);
+ return make_tie(x_sin) == make_tie(y_sin);
+ } else if (x.ss_family == AF_INET6) {
+ const sockaddr_in6& x_sin6 = reinterpret_cast<const sockaddr_in6&>(x);
+ const sockaddr_in6& y_sin6 = reinterpret_cast<const sockaddr_in6&>(y);
+ return make_tie(x_sin6) == make_tie(y_sin6);
+ }
+ return false; // Unknown address type. This is an error.
+}
+
+namespace android {
+namespace net {
+
+// This comparison ignores ports and fingerprints.
+bool AddressComparator::operator() (const DnsTlsServer& x, const DnsTlsServer& y) const {
+ if (x.ss.ss_family != y.ss.ss_family) {
+ return x.ss.ss_family < y.ss.ss_family;
+ }
+ // Same address family.
+ if (x.ss.ss_family == AF_INET) {
+ const sockaddr_in& x_sin = reinterpret_cast<const sockaddr_in&>(x.ss);
+ const sockaddr_in& y_sin = reinterpret_cast<const sockaddr_in&>(y.ss);
+ return x_sin.sin_addr.s_addr < y_sin.sin_addr.s_addr;
+ } else if (x.ss.ss_family == AF_INET6) {
+ const sockaddr_in6& x_sin6 = reinterpret_cast<const sockaddr_in6&>(x.ss);
+ const sockaddr_in6& y_sin6 = reinterpret_cast<const sockaddr_in6&>(y.ss);
+ return std::tie(x_sin6.sin6_addr, x_sin6.sin6_scope_id) <
+ std::tie(y_sin6.sin6_addr, y_sin6.sin6_scope_id);
+ }
+ return false; // Unknown address type. This is an error.
+}
+
+// Returns a tuple of references to the elements of s.
+auto make_tie(const DnsTlsServer& s) {
+ return std::tie(
+ s.ss,
+ s.name,
+ s.fingerprints,
+ s.protocol
+ );
+}
+
+bool DnsTlsServer::operator <(const DnsTlsServer& other) const {
+ return make_tie(*this) < make_tie(other);
+}
+
+bool DnsTlsServer::operator ==(const DnsTlsServer& other) const {
+ return make_tie(*this) == make_tie(other);
+}
+
+bool DnsTlsServer::wasExplicitlyConfigured() const {
+ return !name.empty() || !fingerprints.empty();
+}
+
+} // namespace net
+} // namespace android
diff --git a/resolv/DnsTlsServer.h b/resolv/DnsTlsServer.h
new file mode 100644
index 0000000..7fc4a35
--- /dev/null
+++ b/resolv/DnsTlsServer.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef _DNS_DNSTLSSERVER_H
+#define _DNS_DNSTLSSERVER_H
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include <netinet/in.h>
+
+#include <netd_resolv/params.h>
+
+namespace android {
+namespace net {
+
+// DnsTlsServer represents a recursive resolver that supports, or may support, a
+// secure protocol.
+struct DnsTlsServer {
+ // Default constructor.
+ DnsTlsServer() {}
+
+ // Allow sockaddr_storage to be promoted to DnsTlsServer automatically.
+ DnsTlsServer(const sockaddr_storage& ss) : ss(ss) {}
+
+ enum class Response : uint8_t { success, network_error, limit_error, internal_error };
+
+ struct Result {
+ Response code;
+ std::vector<uint8_t> response;
+ };
+
+ // The server location, including IP and port.
+ sockaddr_storage ss = {};
+
+ // A set of SHA256 public key fingerprints. If this set is nonempty, the server
+ // must present a self-consistent certificate chain that contains a certificate
+ // whose public key matches one of these fingerprints. Otherwise, the client will
+ // terminate the connection.
+ std::set<std::vector<uint8_t>> fingerprints;
+
+ // The server's hostname. If this string is nonempty, the server must present a
+ // certificate that indicates this name and has a valid chain to a trusted root CA.
+ std::string name;
+
+ // Placeholder. More protocols might be defined in the future.
+ int protocol = IPPROTO_TCP;
+
+ // Exact comparison of DnsTlsServer objects
+ bool operator<(const DnsTlsServer& other) const;
+ bool operator==(const DnsTlsServer& other) const;
+
+ bool wasExplicitlyConfigured() const;
+};
+
+// This comparison only checks the IP address. It ignores ports, names, and fingerprints.
+struct AddressComparator {
+ bool operator()(const DnsTlsServer& x, const DnsTlsServer& y) const;
+};
+
+} // namespace net
+} // namespace android
+
+#endif // _DNS_DNSTLSSERVER_H
diff --git a/resolv/DnsTlsSessionCache.cpp b/resolv/DnsTlsSessionCache.cpp
new file mode 100644
index 0000000..8d6dc6b
--- /dev/null
+++ b/resolv/DnsTlsSessionCache.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include "DnsTlsSessionCache.h"
+
+#define LOG_TAG "DnsTlsSessionCache"
+//#define LOG_NDEBUG 0
+
+#include "log/log.h"
+
+namespace android {
+namespace net {
+
+bool DnsTlsSessionCache::prepareSsl(SSL* ssl) {
+ // Add this cache as the 0-index extra data for the socket.
+ // This is used by newSessionCallback.
+ int ret = SSL_set_ex_data(ssl, 0, this);
+ return ret == 1;
+}
+
+void DnsTlsSessionCache::prepareSslContext(SSL_CTX* ssl_ctx) {
+ SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_CLIENT);
+ SSL_CTX_sess_set_new_cb(ssl_ctx, &DnsTlsSessionCache::newSessionCallback);
+}
+
+// static
+int DnsTlsSessionCache::newSessionCallback(SSL* ssl, SSL_SESSION* session) {
+ if (!ssl || !session) {
+ ALOGE("Null SSL object in new session callback");
+ return 0;
+ }
+ DnsTlsSessionCache* cache = reinterpret_cast<DnsTlsSessionCache*>(
+ SSL_get_ex_data(ssl, 0));
+ if (!cache) {
+ ALOGE("null transport in new session callback");
+ return 0;
+ }
+ ALOGV("Recording session");
+ cache->recordSession(session);
+ return 1; // Increment the refcount of session.
+}
+
+void DnsTlsSessionCache::recordSession(SSL_SESSION* session) {
+ std::lock_guard guard(mLock);
+ mSessions.emplace_front(session);
+ if (mSessions.size() > kMaxSize) {
+ ALOGV("Too many sessions; trimming");
+ mSessions.pop_back();
+ }
+}
+
+bssl::UniquePtr<SSL_SESSION> DnsTlsSessionCache::getSession() {
+ std::lock_guard guard(mLock);
+ if (mSessions.size() == 0) {
+ ALOGV("No known sessions");
+ return nullptr;
+ }
+ bssl::UniquePtr<SSL_SESSION> ret = std::move(mSessions.front());
+ mSessions.pop_front();
+ return ret;
+}
+
+} // end of namespace net
+} // end of namespace android
diff --git a/resolv/DnsTlsSessionCache.h b/resolv/DnsTlsSessionCache.h
new file mode 100644
index 0000000..e1a88cf
--- /dev/null
+++ b/resolv/DnsTlsSessionCache.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#ifndef _DNS_DNSTLSSESSIONCACHE_H
+#define _DNS_DNSTLSSESSIONCACHE_H
+
+#include <deque>
+#include <mutex>
+
+#include <openssl/ssl.h>
+
+#include <android-base/thread_annotations.h>
+
+namespace android {
+namespace net {
+
+// Cache of recently seen SSL_SESSIONs. This is used to support session tickets.
+// This class is thread-safe.
+class DnsTlsSessionCache {
+ public:
+ // Prepare SSL objects to use this session cache. These methods must be called
+ // before making use of either object.
+ void prepareSslContext(SSL_CTX* _Nonnull ssl_ctx);
+ bool prepareSsl(SSL* _Nonnull ssl);
+
+ // Get the most recently discovered session. For TLS 1.3 compatibility and
+ // maximum privacy, each session will only be returned once, so the caller
+ // gains ownership of the session. (Here and throughout,
+ // bssl::UniquePtr<SSL_SESSION> is actually serving as a reference counted
+ // pointer.)
+ bssl::UniquePtr<SSL_SESSION> getSession() EXCLUDES(mLock);
+
+ private:
+ static constexpr size_t kMaxSize = 5;
+ static int newSessionCallback(SSL* _Nullable ssl, SSL_SESSION* _Nullable session);
+
+ std::mutex mLock;
+ void recordSession(SSL_SESSION* _Nullable session) EXCLUDES(mLock);
+
+ // Queue of sessions, from least recently added to most recently.
+ std::deque<bssl::UniquePtr<SSL_SESSION>> mSessions GUARDED_BY(mLock);
+};
+
+} // end of namespace net
+} // end of namespace android
+
+#endif // _DNS_DNSTLSSESSIONCACHE_H
diff --git a/resolv/DnsTlsSocket.cpp b/resolv/DnsTlsSocket.cpp
new file mode 100644
index 0000000..a63d221
--- /dev/null
+++ b/resolv/DnsTlsSocket.cpp
@@ -0,0 +1,548 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#define LOG_TAG "DnsTlsSocket"
+//#define LOG_NDEBUG 0
+
+#include "DnsTlsSocket.h"
+
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+#include <errno.h>
+#include <linux/tcp.h>
+#include <openssl/err.h>
+#include <openssl/sha.h>
+#include <sys/eventfd.h>
+#include <sys/poll.h>
+#include <algorithm>
+
+#include "DnsTlsSessionCache.h"
+#include "IDnsTlsSocketObserver.h"
+
+#include "log/log.h"
+#include "netdutils/SocketOption.h"
+
+namespace android {
+
+using netdutils::enableSockopt;
+using netdutils::enableTcpKeepAlives;
+using netdutils::isOk;
+using netdutils::Slice;
+using netdutils::Status;
+
+namespace net {
+namespace {
+
+constexpr const char kCaCertDir[] = "/system/etc/security/cacerts";
+constexpr size_t SHA256_SIZE = SHA256_DIGEST_LENGTH;
+
+int waitForReading(int fd) {
+ struct pollfd fds = { .fd = fd, .events = POLLIN };
+ const int ret = TEMP_FAILURE_RETRY(poll(&fds, 1, -1));
+ return ret;
+}
+
+int waitForWriting(int fd) {
+ struct pollfd fds = { .fd = fd, .events = POLLOUT };
+ const int ret = TEMP_FAILURE_RETRY(poll(&fds, 1, -1));
+ return ret;
+}
+
+} // namespace
+
+Status DnsTlsSocket::tcpConnect() {
+ ALOGV("%u connecting TCP socket", mMark);
+ int type = SOCK_NONBLOCK | SOCK_CLOEXEC;
+ switch (mServer.protocol) {
+ case IPPROTO_TCP:
+ type |= SOCK_STREAM;
+ break;
+ default:
+ return Status(EPROTONOSUPPORT);
+ }
+
+ mSslFd.reset(socket(mServer.ss.ss_family, type, mServer.protocol));
+ if (mSslFd.get() == -1) {
+ ALOGE("Failed to create socket");
+ return Status(errno);
+ }
+
+ const socklen_t len = sizeof(mMark);
+ if (setsockopt(mSslFd.get(), SOL_SOCKET, SO_MARK, &mMark, len) == -1) {
+ ALOGE("Failed to set socket mark");
+ mSslFd.reset();
+ return Status(errno);
+ }
+
+ const Status tfo = enableSockopt(mSslFd.get(), SOL_TCP, TCP_FASTOPEN_CONNECT);
+ if (!isOk(tfo) && tfo.code() != ENOPROTOOPT) {
+ ALOGI("Failed to enable TFO: %s", tfo.msg().c_str());
+ }
+
+ // Send 5 keepalives, 3 seconds apart, after 15 seconds of inactivity.
+ enableTcpKeepAlives(mSslFd.get(), 15U, 5U, 3U).ignoreError();
+
+ if (connect(mSslFd.get(), reinterpret_cast<const struct sockaddr *>(&mServer.ss),
+ sizeof(mServer.ss)) != 0 &&
+ errno != EINPROGRESS) {
+ ALOGV("Socket failed to connect");
+ mSslFd.reset();
+ return Status(errno);
+ }
+
+ return netdutils::status::ok;
+}
+
+bool getSPKIDigest(const X509* cert, std::vector<uint8_t>* out) {
+ int spki_len = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert), nullptr);
+ unsigned char spki[spki_len];
+ unsigned char* temp = spki;
+ if (spki_len != i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert), &temp)) {
+ ALOGW("SPKI length mismatch");
+ return false;
+ }
+ out->resize(SHA256_SIZE);
+ unsigned int digest_len = 0;
+ int ret = EVP_Digest(spki, spki_len, out->data(), &digest_len, EVP_sha256(), nullptr);
+ if (ret != 1) {
+ ALOGW("Server cert digest extraction failed");
+ return false;
+ }
+ if (digest_len != out->size()) {
+ ALOGW("Wrong digest length: %d", digest_len);
+ return false;
+ }
+ return true;
+}
+
+bool DnsTlsSocket::initialize() {
+ // This method should only be called once, at the beginning, so locking should be
+ // unnecessary. This lock only serves to help catch bugs in code that calls this method.
+ std::lock_guard guard(mLock);
+ if (mSslCtx) {
+ // This is a bug in the caller.
+ return false;
+ }
+ mSslCtx.reset(SSL_CTX_new(TLS_method()));
+ if (!mSslCtx) {
+ return false;
+ }
+
+ // Load system CA certs for hostname verification.
+ //
+ // For discussion of alternative, sustainable approaches see b/71909242.
+ if (SSL_CTX_load_verify_locations(mSslCtx.get(), nullptr, kCaCertDir) != 1) {
+ ALOGE("Failed to load CA cert dir: %s", kCaCertDir);
+ return false;
+ }
+
+ // Enable TLS false start
+ SSL_CTX_set_false_start_allowed_without_alpn(mSslCtx.get(), 1);
+ SSL_CTX_set_mode(mSslCtx.get(), SSL_MODE_ENABLE_FALSE_START);
+
+ // Enable session cache
+ mCache->prepareSslContext(mSslCtx.get());
+
+ // Connect
+ Status status = tcpConnect();
+ if (!status.ok()) {
+ return false;
+ }
+ mSsl = sslConnect(mSslFd.get());
+ if (!mSsl) {
+ return false;
+ }
+
+ mEventFd.reset(eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC));
+
+ // Start the I/O loop.
+ mLoopThread.reset(new std::thread(&DnsTlsSocket::loop, this));
+
+ return true;
+}
+
+bssl::UniquePtr<SSL> DnsTlsSocket::sslConnect(int fd) {
+ if (!mSslCtx) {
+ ALOGE("Internal error: context is null in sslConnect");
+ return nullptr;
+ }
+ if (!SSL_CTX_set_min_proto_version(mSslCtx.get(), TLS1_2_VERSION)) {
+ ALOGE("Failed to set minimum TLS version");
+ return nullptr;
+ }
+
+ bssl::UniquePtr<SSL> ssl(SSL_new(mSslCtx.get()));
+ // This file descriptor is owned by mSslFd, so don't let libssl close it.
+ bssl::UniquePtr<BIO> bio(BIO_new_socket(fd, BIO_NOCLOSE));
+ SSL_set_bio(ssl.get(), bio.get(), bio.get());
+ bio.release();
+
+ if (!mCache->prepareSsl(ssl.get())) {
+ return nullptr;
+ }
+
+ if (!mServer.name.empty()) {
+ if (SSL_set_tlsext_host_name(ssl.get(), mServer.name.c_str()) != 1) {
+ ALOGE("Failed to set SNI to %s", mServer.name.c_str());
+ return nullptr;
+ }
+ X509_VERIFY_PARAM* param = SSL_get0_param(ssl.get());
+ if (X509_VERIFY_PARAM_set1_host(param, mServer.name.data(), mServer.name.size()) != 1) {
+ ALOGE("Failed to set verify host param to %s", mServer.name.c_str());
+ return nullptr;
+ }
+ // This will cause the handshake to fail if certificate verification fails.
+ SSL_set_verify(ssl.get(), SSL_VERIFY_PEER, nullptr);
+ }
+
+ bssl::UniquePtr<SSL_SESSION> session = mCache->getSession();
+ if (session) {
+ ALOGV("Setting session");
+ SSL_set_session(ssl.get(), session.get());
+ } else {
+ ALOGV("No session available");
+ }
+
+ for (;;) {
+ ALOGV("%u Calling SSL_connect", mMark);
+ int ret = SSL_connect(ssl.get());
+ ALOGV("%u SSL_connect returned %d", mMark, ret);
+ if (ret == 1) break; // SSL handshake complete;
+
+ const int ssl_err = SSL_get_error(ssl.get(), ret);
+ switch (ssl_err) {
+ case SSL_ERROR_WANT_READ:
+ if (waitForReading(fd) != 1) {
+ ALOGW("SSL_connect read error: %d", errno);
+ return nullptr;
+ }
+ break;
+ case SSL_ERROR_WANT_WRITE:
+ if (waitForWriting(fd) != 1) {
+ ALOGW("SSL_connect write error");
+ return nullptr;
+ }
+ break;
+ default:
+ ALOGW("SSL_connect error %d, errno=%d", ssl_err, errno);
+ return nullptr;
+ }
+ }
+
+ // TODO: Call SSL_shutdown before discarding the session if validation fails.
+ if (!mServer.fingerprints.empty()) {
+ ALOGV("Checking DNS over TLS fingerprint");
+
+ // We only care that the chain is internally self-consistent, not that
+ // it chains to a trusted root, so we can ignore some kinds of errors.
+ // TODO: Add a CA root verification mode that respects these errors.
+ int verify_result = SSL_get_verify_result(ssl.get());
+ switch (verify_result) {
+ case X509_V_OK:
+ case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
+ case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
+ case X509_V_ERR_CERT_UNTRUSTED:
+ break;
+ default:
+ ALOGW("Invalid certificate chain, error %d", verify_result);
+ return nullptr;
+ }
+
+ STACK_OF(X509) *chain = SSL_get_peer_cert_chain(ssl.get());
+ if (!chain) {
+ ALOGW("Server has null certificate");
+ return nullptr;
+ }
+ // Chain and its contents are owned by ssl, so we don't need to free explicitly.
+ bool matched = false;
+ for (size_t i = 0; i < sk_X509_num(chain); ++i) {
+ // This appears to be O(N^2), but there doesn't seem to be a straightforward
+ // way to walk a STACK_OF nondestructively in linear time.
+ X509* cert = sk_X509_value(chain, i);
+ std::vector<uint8_t> digest;
+ if (!getSPKIDigest(cert, &digest)) {
+ ALOGE("Digest computation failed");
+ return nullptr;
+ }
+
+ if (mServer.fingerprints.count(digest) > 0) {
+ matched = true;
+ break;
+ }
+ }
+
+ if (!matched) {
+ ALOGW("No matching fingerprint");
+ return nullptr;
+ }
+
+ ALOGV("DNS over TLS fingerprint is correct");
+ }
+
+ ALOGV("%u handshake complete", mMark);
+
+ return ssl;
+}
+
+void DnsTlsSocket::sslDisconnect() {
+ if (mSsl) {
+ SSL_shutdown(mSsl.get());
+ mSsl.reset();
+ }
+ mSslFd.reset();
+}
+
+bool DnsTlsSocket::sslWrite(const Slice buffer) {
+ ALOGV("%u Writing %zu bytes", mMark, buffer.size());
+ for (;;) {
+ int ret = SSL_write(mSsl.get(), buffer.base(), buffer.size());
+ if (ret == int(buffer.size())) break; // SSL write complete;
+
+ if (ret < 1) {
+ const int ssl_err = SSL_get_error(mSsl.get(), ret);
+ switch (ssl_err) {
+ case SSL_ERROR_WANT_WRITE:
+ if (waitForWriting(mSslFd.get()) != 1) {
+ ALOGV("SSL_write error");
+ return false;
+ }
+ continue;
+ case 0:
+ break; // SSL write complete;
+ default:
+ ALOGV("SSL_write error %d", ssl_err);
+ return false;
+ }
+ }
+ }
+ ALOGV("%u Wrote %zu bytes", mMark, buffer.size());
+ return true;
+}
+
+void DnsTlsSocket::loop() {
+ std::lock_guard guard(mLock);
+ std::deque<std::vector<uint8_t>> q;
+
+ const int timeout_msecs = DnsTlsSocket::kIdleTimeout.count() * 1000;
+ while (true) {
+ // poll() ignores negative fds
+ struct pollfd fds[2] = { { .fd = -1 }, { .fd = -1 } };
+ enum { SSLFD = 0, EVENTFD = 1 };
+
+ // Always listen for a response from server.
+ fds[SSLFD].fd = mSslFd.get();
+ fds[SSLFD].events = POLLIN;
+
+ // If we have pending queries, wait for space to write one.
+ // Otherwise, listen for new queries.
+ // Note: This blocks the destructor until q is empty, i.e. until all pending
+ // queries are sent or have failed to send.
+ if (!q.empty()) {
+ fds[SSLFD].events |= POLLOUT;
+ } else {
+ fds[EVENTFD].fd = mEventFd.get();
+ fds[EVENTFD].events = POLLIN;
+ }
+
+ const int s = TEMP_FAILURE_RETRY(poll(fds, std::size(fds), timeout_msecs));
+ if (s == 0) {
+ ALOGV("Idle timeout");
+ break;
+ }
+ if (s < 0) {
+ ALOGV("Poll failed: %d", errno);
+ break;
+ }
+ if (fds[SSLFD].revents & (POLLIN | POLLERR | POLLHUP)) {
+ if (!readResponse()) {
+ ALOGV("SSL remote close or read error.");
+ break;
+ }
+ }
+ if (fds[EVENTFD].revents & (POLLIN | POLLERR)) {
+ int64_t num_queries;
+ ssize_t res = read(mEventFd.get(), &num_queries, sizeof(num_queries));
+ if (res < 0) {
+ ALOGW("Error during eventfd read");
+ break;
+ } else if (res == 0) {
+ ALOGW("eventfd closed; disconnecting");
+ break;
+ } else if (res != sizeof(num_queries)) {
+ ALOGE("Int size mismatch: %zd != %zu", res, sizeof(num_queries));
+ break;
+ } else if (num_queries < 0) {
+ ALOGV("Negative eventfd read indicates destructor-initiated shutdown");
+ break;
+ }
+ // Take ownership of all pending queries. (q is always empty here.)
+ mQueue.swap(q);
+ } else if (fds[SSLFD].revents & POLLOUT) {
+ // q cannot be empty here.
+ // Sending the entire queue here would risk a TCP flow control deadlock, so
+ // we only send a single query on each cycle of this loop.
+ // TODO: Coalesce multiple pending queries if there is enough space in the
+ // write buffer.
+ if (!sendQuery(q.front())) {
+ break;
+ }
+ q.pop_front();
+ }
+ }
+ ALOGV("Disconnecting");
+ sslDisconnect();
+ ALOGV("Calling onClosed");
+ mObserver->onClosed();
+ ALOGV("Ending loop");
+}
+
+DnsTlsSocket::~DnsTlsSocket() {
+ ALOGV("Destructor");
+ // This will trigger an orderly shutdown in loop().
+ requestLoopShutdown();
+ {
+ // Wait for the orderly shutdown to complete.
+ std::lock_guard guard(mLock);
+ if (mLoopThread && std::this_thread::get_id() == mLoopThread->get_id()) {
+ ALOGE("Violation of re-entrance precondition");
+ return;
+ }
+ }
+ if (mLoopThread) {
+ ALOGV("Waiting for loop thread to terminate");
+ mLoopThread->join();
+ mLoopThread.reset();
+ }
+ ALOGV("Destructor completed");
+}
+
+bool DnsTlsSocket::query(uint16_t id, const Slice query) {
+ // Compose the entire message in a single buffer, so that it can be
+ // sent as a single TLS record.
+ std::vector<uint8_t> buf(query.size() + 4);
+ // Write 2-byte length
+ uint16_t len = query.size() + 2; // + 2 for the ID.
+ buf[0] = len >> 8;
+ buf[1] = len;
+ // Write 2-byte ID
+ buf[2] = id >> 8;
+ buf[3] = id;
+ // Copy body
+ std::memcpy(buf.data() + 4, query.base(), query.size());
+
+ mQueue.push(std::move(buf));
+ // Increment the mEventFd counter by 1.
+ return incrementEventFd(1);
+}
+
+void DnsTlsSocket::requestLoopShutdown() {
+ if (mEventFd != -1) {
+ // Write a negative number to the eventfd. This triggers an immediate shutdown.
+ incrementEventFd(INT64_MIN);
+ }
+}
+
+bool DnsTlsSocket::incrementEventFd(const int64_t count) {
+ if (mEventFd == -1) {
+ ALOGE("eventfd is not initialized");
+ return false;
+ }
+ ssize_t written = write(mEventFd.get(), &count, sizeof(count));
+ if (written != sizeof(count)) {
+ ALOGE("Failed to increment eventfd by %" PRId64, count);
+ return false;
+ }
+ return true;
+}
+
+// Read exactly len bytes into buffer or fail with an SSL error code
+int DnsTlsSocket::sslRead(const Slice buffer, bool wait) {
+ size_t remaining = buffer.size();
+ while (remaining > 0) {
+ int ret = SSL_read(mSsl.get(), buffer.limit() - remaining, remaining);
+ if (ret == 0) {
+ ALOGW_IF(remaining < buffer.size(), "SSL closed with %zu of %zu bytes remaining",
+ remaining, buffer.size());
+ return SSL_ERROR_ZERO_RETURN;
+ }
+
+ if (ret < 0) {
+ const int ssl_err = SSL_get_error(mSsl.get(), ret);
+ if (wait && ssl_err == SSL_ERROR_WANT_READ) {
+ if (waitForReading(mSslFd.get()) != 1) {
+ ALOGV("Poll failed in sslRead: %d", errno);
+ return SSL_ERROR_SYSCALL;
+ }
+ continue;
+ } else {
+ ALOGV("SSL_read error %d", ssl_err);
+ return ssl_err;
+ }
+ }
+
+ remaining -= ret;
+ wait = true; // Once a read is started, try to finish.
+ }
+ return SSL_ERROR_NONE;
+}
+
+bool DnsTlsSocket::sendQuery(const std::vector<uint8_t>& buf) {
+ if (!sslWrite(netdutils::makeSlice(buf))) {
+ return false;
+ }
+ ALOGV("%u SSL_write complete", mMark);
+ return true;
+}
+
+bool DnsTlsSocket::readResponse() {
+ ALOGV("reading response");
+ uint8_t responseHeader[2];
+ int err = sslRead(Slice(responseHeader, 2), false);
+ if (err == SSL_ERROR_WANT_READ) {
+ ALOGV("Ignoring spurious wakeup from server");
+ return true;
+ }
+ if (err != SSL_ERROR_NONE) {
+ return false;
+ }
+ // Truncate responses larger than MAX_SIZE. This is safe because a DNS packet is
+ // always invalid when truncated, so the response will be treated as an error.
+ constexpr uint16_t MAX_SIZE = 8192;
+ const uint16_t responseSize = (responseHeader[0] << 8) | responseHeader[1];
+ ALOGV("%u Expecting response of size %i", mMark, responseSize);
+ std::vector<uint8_t> response(std::min(responseSize, MAX_SIZE));
+ if (sslRead(netdutils::makeSlice(response), true) != SSL_ERROR_NONE) {
+ ALOGV("%u Failed to read %zu bytes", mMark, response.size());
+ return false;
+ }
+ uint16_t remainingBytes = responseSize - response.size();
+ while (remainingBytes > 0) {
+ constexpr uint16_t CHUNK_SIZE = 2048;
+ std::vector<uint8_t> discard(std::min(remainingBytes, CHUNK_SIZE));
+ if (sslRead(netdutils::makeSlice(discard), true) != SSL_ERROR_NONE) {
+ ALOGV("%u Failed to discard %zu bytes", mMark, discard.size());
+ return false;
+ }
+ remainingBytes -= discard.size();
+ }
+ ALOGV("%u SSL_read complete", mMark);
+
+ mObserver->onResponse(std::move(response));
+ return true;
+}
+
+} // end of namespace net
+} // end of namespace android
diff --git a/resolv/DnsTlsSocket.h b/resolv/DnsTlsSocket.h
new file mode 100644
index 0000000..2940500
--- /dev/null
+++ b/resolv/DnsTlsSocket.h
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#ifndef _DNS_DNSTLSSOCKET_H
+#define _DNS_DNSTLSSOCKET_H
+
+#include <openssl/ssl.h>
+#include <future>
+#include <mutex>
+
+#include <android-base/thread_annotations.h>
+#include <android-base/unique_fd.h>
+#include <netdutils/Slice.h>
+#include <netdutils/Status.h>
+
+#include "DnsTlsServer.h"
+#include "IDnsTlsSocket.h"
+#include "LockedQueue.h"
+
+namespace android {
+namespace net {
+
+class IDnsTlsSocketObserver;
+class DnsTlsSessionCache;
+
+// A class for managing a TLS socket that sends and receives messages in
+// [length][value] format, with a 2-byte length (i.e. DNS-over-TCP format).
+// This class is not aware of query-response pairing or anything else about DNS.
+// For the observer:
+// This class is not re-entrant: the observer is not permitted to wait for a call to query()
+// or the destructor in a callback. Doing so will result in deadlocks.
+// This class may call the observer at any time after initialize(), until the destructor
+// returns (but not after).
+class DnsTlsSocket : public IDnsTlsSocket {
+ public:
+ DnsTlsSocket(const DnsTlsServer& server, unsigned mark,
+ IDnsTlsSocketObserver* _Nonnull observer, DnsTlsSessionCache* _Nonnull cache)
+ : mMark(mark), mServer(server), mObserver(observer), mCache(cache) {}
+ ~DnsTlsSocket();
+
+ // Creates the SSL context for this session and connect. Returns false on failure.
+ // This method should be called after construction and before use of a DnsTlsSocket.
+ // Only call this method once per DnsTlsSocket.
+ bool initialize() EXCLUDES(mLock);
+
+ // Send a query on the provided SSL socket. |query| contains
+ // the body of a query, not including the ID header. This function will typically return before
+ // the query is actually sent. If this function fails, DnsTlsSocketObserver will be
+ // notified that the socket is closed.
+ // Note that success here indicates successful sending, not receipt of a response.
+ // Thread-safe.
+ bool query(uint16_t id, const netdutils::Slice query) override EXCLUDES(mLock);
+
+ private:
+ // Lock to be held by the SSL event loop thread. This is not normally in contention.
+ std::mutex mLock;
+
+ // Forwards queries and receives responses. Blocks until the idle timeout.
+ void loop() EXCLUDES(mLock);
+ std::unique_ptr<std::thread> mLoopThread GUARDED_BY(mLock);
+
+ // On success, sets mSslFd to a socket connected to mAddr (the
+ // connection will likely be in progress if mProtocol is IPPROTO_TCP).
+ // On error, returns the errno.
+ netdutils::Status tcpConnect() REQUIRES(mLock);
+
+ // Connect an SSL session on the provided socket. If connection fails, closing the
+ // socket remains the caller's responsibility.
+ bssl::UniquePtr<SSL> sslConnect(int fd) REQUIRES(mLock);
+
+ // Disconnect the SSL session and close the socket.
+ void sslDisconnect() REQUIRES(mLock);
+
+ // Writes a buffer to the socket.
+ bool sslWrite(const netdutils::Slice buffer) REQUIRES(mLock);
+
+ // Reads exactly the specified number of bytes from the socket, or fails.
+ // Returns SSL_ERROR_NONE on success.
+ // If |wait| is true, then this function always blocks. Otherwise, it
+ // will return SSL_ERROR_WANT_READ if there is no data from the server to read.
+ int sslRead(const netdutils::Slice buffer, bool wait) REQUIRES(mLock);
+
+ bool sendQuery(const std::vector<uint8_t>& buf) REQUIRES(mLock);
+ bool readResponse() REQUIRES(mLock);
+
+ // Similar to query(), this function uses incrementEventFd to send a message to the
+ // loop thread. However, instead of incrementing the counter by one (indicating a
+ // new query), it wraps the counter to negative, which we use to indicate a shutdown
+ // request.
+ void requestLoopShutdown() EXCLUDES(mLock);
+
+ // This function sends a message to the loop thread by incrementing mEventFd.
+ bool incrementEventFd(int64_t count) EXCLUDES(mLock);
+
+ // Queue of pending queries. query() pushes items onto the queue and notifies
+ // the loop thread by incrementing mEventFd. loop() reads items off the queue.
+ LockedQueue<std::vector<uint8_t>> mQueue;
+
+ // eventfd socket used for notifying the SSL thread when queries are ready to send.
+ // This socket acts similarly to an atomic counter, incremented by query() and cleared
+ // by loop(). We have to use a socket because the SSL thread needs to wait in poll()
+ // for input from either a remote server or a query thread. Since eventfd does not have
+ // EOF, we indicate a close request by setting the counter to a negative number.
+ // This file descriptor is opened by initialize(), and closed implicitly after
+ // destruction.
+ base::unique_fd mEventFd;
+
+ // SSL Socket fields.
+ bssl::UniquePtr<SSL_CTX> mSslCtx GUARDED_BY(mLock);
+ base::unique_fd mSslFd GUARDED_BY(mLock);
+ bssl::UniquePtr<SSL> mSsl GUARDED_BY(mLock);
+ static constexpr std::chrono::seconds kIdleTimeout = std::chrono::seconds(20);
+
+ const unsigned mMark; // Socket mark
+ const DnsTlsServer mServer;
+ IDnsTlsSocketObserver* _Nonnull const mObserver;
+ DnsTlsSessionCache* _Nonnull const mCache;
+};
+
+} // end of namespace net
+} // end of namespace android
+
+#endif // _DNS_DNSTLSSOCKET_H
diff --git a/resolv/DnsTlsSocketFactory.h b/resolv/DnsTlsSocketFactory.h
new file mode 100644
index 0000000..af4011d
--- /dev/null
+++ b/resolv/DnsTlsSocketFactory.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#ifndef _DNS_DNSTLSSOCKETFACTORY_H
+#define _DNS_DNSTLSSOCKETFACTORY_H
+
+#include <memory>
+
+#include "DnsTlsSocket.h"
+#include "IDnsTlsSocketFactory.h"
+
+namespace android {
+namespace net {
+
+class IDnsTlsSocketObserver;
+class DnsTlsSessionCache;
+struct DnsTlsServer;
+
+// Trivial RAII factory for DnsTlsSocket. This is owned by DnsTlsDispatcher.
+class DnsTlsSocketFactory : public IDnsTlsSocketFactory {
+ public:
+ std::unique_ptr<IDnsTlsSocket> createDnsTlsSocket(const DnsTlsServer& server, unsigned mark,
+ IDnsTlsSocketObserver* _Nonnull observer,
+ DnsTlsSessionCache* _Nonnull cache) override {
+ auto socket = std::make_unique<DnsTlsSocket>(server, mark, observer, cache);
+ if (!socket->initialize()) {
+ return nullptr;
+ }
+ return std::move(socket);
+ }
+};
+
+} // end of namespace net
+} // end of namespace android
+
+#endif // _DNS_DNSTLSSOCKETFACTORY_H
diff --git a/resolv/DnsTlsTransport.cpp b/resolv/DnsTlsTransport.cpp
new file mode 100644
index 0000000..0f9042e
--- /dev/null
+++ b/resolv/DnsTlsTransport.cpp
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#define LOG_TAG "DnsTlsTransport"
+//#define LOG_NDEBUG 0
+
+#include "DnsTlsTransport.h"
+
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+
+#include "DnsTlsSocketFactory.h"
+#include "IDnsTlsSocketFactory.h"
+
+#include "log/log.h"
+
+namespace android {
+namespace net {
+
+std::future<DnsTlsTransport::Result> DnsTlsTransport::query(const netdutils::Slice query) {
+ std::lock_guard guard(mLock);
+
+ auto record = mQueries.recordQuery(query);
+ if (!record) {
+ return std::async(std::launch::deferred, []{
+ return (Result) { .code = Response::internal_error };
+ });
+ }
+
+ if (!mSocket) {
+ ALOGV("No socket for query. Opening socket and sending.");
+ doConnect();
+ } else {
+ sendQuery(record->query);
+ }
+
+ return std::move(record->result);
+}
+
+bool DnsTlsTransport::sendQuery(const DnsTlsQueryMap::Query q) {
+ // Strip off the ID number and send the new ID instead.
+ bool sent = mSocket->query(q.newId, netdutils::drop(q.query, 2));
+ if (sent) {
+ mQueries.markTried(q.newId);
+ }
+ return sent;
+}
+
+void DnsTlsTransport::doConnect() {
+ ALOGV("Constructing new socket");
+ mSocket = mFactory->createDnsTlsSocket(mServer, mMark, this, &mCache);
+
+ if (mSocket) {
+ auto queries = mQueries.getAll();
+ ALOGV("Initialization succeeded. Reissuing %zu queries.", queries.size());
+ for(auto& q : queries) {
+ if (!sendQuery(q)) {
+ break;
+ }
+ }
+ } else {
+ ALOGV("Initialization failed.");
+ mSocket.reset();
+ ALOGV("Failing all pending queries.");
+ mQueries.clear();
+ }
+}
+
+void DnsTlsTransport::onResponse(std::vector<uint8_t> response) {
+ mQueries.onResponse(std::move(response));
+}
+
+void DnsTlsTransport::onClosed() {
+ std::lock_guard guard(mLock);
+ if (mClosing) {
+ return;
+ }
+ // Move remaining operations to a new thread.
+ // This is necessary because
+ // 1. onClosed is currently running on a thread that blocks mSocket's destructor
+ // 2. doReconnect will call that destructor
+ if (mReconnectThread) {
+ // Complete cleanup of a previous reconnect thread, if present.
+ mReconnectThread->join();
+ // Joining a thread that is trying to acquire mLock, while holding mLock,
+ // looks like it risks a deadlock. However, a deadlock will not occur because
+ // once onClosed is called, it cannot be called again until after doReconnect
+ // acquires mLock.
+ }
+ mReconnectThread.reset(new std::thread(&DnsTlsTransport::doReconnect, this));
+}
+
+void DnsTlsTransport::doReconnect() {
+ std::lock_guard guard(mLock);
+ if (mClosing) {
+ return;
+ }
+ mQueries.cleanup();
+ if (!mQueries.empty()) {
+ ALOGV("Fast reconnect to retry remaining queries");
+ doConnect();
+ } else {
+ ALOGV("No pending queries. Going idle.");
+ mSocket.reset();
+ }
+}
+
+DnsTlsTransport::~DnsTlsTransport() {
+ ALOGV("Destructor");
+ {
+ std::lock_guard guard(mLock);
+ ALOGV("Locked destruction procedure");
+ mQueries.clear();
+ mClosing = true;
+ }
+ // It's possible that a reconnect thread was spawned and waiting for mLock.
+ // It's safe for that thread to run now because mClosing is true (and mQueries is empty),
+ // but we need to wait for it to finish before allowing destruction to proceed.
+ if (mReconnectThread) {
+ ALOGV("Waiting for reconnect thread to terminate");
+ mReconnectThread->join();
+ mReconnectThread.reset();
+ }
+ // Ensure that the socket is destroyed, and can clean up its callback threads,
+ // before any of this object's fields become invalid.
+ mSocket.reset();
+ ALOGV("Destructor completed");
+}
+
+// static
+// TODO: Use this function to preheat the session cache.
+// That may require moving it to DnsTlsDispatcher.
+bool DnsTlsTransport::validate(const DnsTlsServer& server, unsigned netid, uint32_t mark) {
+ ALOGV("Beginning validation on %u", netid);
+ // Generate "<random>-dnsotls-ds.metric.gstatic.com", which we will lookup through |ss| in
+ // order to prove that it is actually a working DNS over TLS server.
+ static const char kDnsSafeChars[] =
+ "abcdefhijklmnopqrstuvwxyz"
+ "ABCDEFHIJKLMNOPQRSTUVWXYZ"
+ "0123456789";
+ const auto c = [](uint8_t rnd) -> uint8_t {
+ return kDnsSafeChars[(rnd % std::size(kDnsSafeChars))];
+ };
+ uint8_t rnd[8];
+ arc4random_buf(rnd, std::size(rnd));
+ // We could try to use res_mkquery() here, but it's basically the same.
+ uint8_t query[] = {
+ rnd[6], rnd[7], // [0-1] query ID
+ 1, 0, // [2-3] flags; query[2] = 1 for recursion desired (RD).
+ 0, 1, // [4-5] QDCOUNT (number of queries)
+ 0, 0, // [6-7] ANCOUNT (number of answers)
+ 0, 0, // [8-9] NSCOUNT (number of name server records)
+ 0, 0, // [10-11] ARCOUNT (number of additional records)
+ 17, c(rnd[0]), c(rnd[1]), c(rnd[2]), c(rnd[3]), c(rnd[4]), c(rnd[5]),
+ '-', 'd', 'n', 's', 'o', 't', 'l', 's', '-', 'd', 's',
+ 6, 'm', 'e', 't', 'r', 'i', 'c',
+ 7, 'g', 's', 't', 'a', 't', 'i', 'c',
+ 3, 'c', 'o', 'm',
+ 0, // null terminator of FQDN (root TLD)
+ 0, ns_t_aaaa, // QTYPE
+ 0, ns_c_in // QCLASS
+ };
+ const int qlen = std::size(query);
+
+ int replylen = 0;
+ DnsTlsSocketFactory factory;
+ DnsTlsTransport transport(server, mark, &factory);
+ auto r = transport.query(netdutils::Slice(query, qlen)).get();
+ if (r.code != Response::success) {
+ ALOGV("query failed");
+ return false;
+ }
+
+ const std::vector<uint8_t>& recvbuf = r.response;
+ if (recvbuf.size() < NS_HFIXEDSZ) {
+ ALOGW("short response: %d", replylen);
+ return false;
+ }
+
+ const int qdcount = (recvbuf[4] << 8) | recvbuf[5];
+ if (qdcount != 1) {
+ ALOGW("reply query count != 1: %d", qdcount);
+ return false;
+ }
+
+ const int ancount = (recvbuf[6] << 8) | recvbuf[7];
+ ALOGV("%u answer count: %d", netid, ancount);
+
+ // TODO: Further validate the response contents (check for valid AAAA record, ...).
+ // Note that currently, integration tests rely on this function accepting a
+ // response with zero records.
+#if 0
+ for (int i = 0; i < resplen; i++) {
+ ALOGD("recvbuf[%d] = %d %c", i, recvbuf[i], recvbuf[i]);
+ }
+#endif
+ return true;
+}
+
+} // end of namespace net
+} // end of namespace android
diff --git a/resolv/DnsTlsTransport.h b/resolv/DnsTlsTransport.h
new file mode 100644
index 0000000..6c98fa6
--- /dev/null
+++ b/resolv/DnsTlsTransport.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef _DNS_DNSTLSTRANSPORT_H
+#define _DNS_DNSTLSTRANSPORT_H
+
+#include <future>
+#include <map>
+#include <mutex>
+#include <vector>
+
+#include <android-base/thread_annotations.h>
+#include <android-base/unique_fd.h>
+#include <netdutils/Slice.h>
+
+#include "DnsTlsQueryMap.h"
+#include "DnsTlsServer.h"
+#include "DnsTlsSessionCache.h"
+#include "IDnsTlsSocket.h"
+#include "IDnsTlsSocketObserver.h"
+
+namespace android {
+namespace net {
+
+class IDnsTlsSocketFactory;
+
+// Manages at most one DnsTlsSocket at a time. This class handles socket lifetime issues,
+// such as reopening the socket and reissuing pending queries.
+class DnsTlsTransport : public IDnsTlsSocketObserver {
+ public:
+ DnsTlsTransport(const DnsTlsServer& server, unsigned mark,
+ IDnsTlsSocketFactory* _Nonnull factory)
+ : mMark(mark), mServer(server), mFactory(factory) {}
+ ~DnsTlsTransport();
+
+ typedef DnsTlsServer::Response Response;
+ typedef DnsTlsServer::Result Result;
+
+ // Given a |query|, this method sends it to the server and returns the result asynchronously.
+ std::future<Result> query(const netdutils::Slice query) EXCLUDES(mLock);
+
+ // Check that a given TLS server is fully working on the specified netid, and has the
+ // provided SHA-256 fingerprint (if nonempty). This function is used in ResolverController
+ // to ensure that we don't enable DNS over TLS on networks where it doesn't actually work.
+ static bool validate(const DnsTlsServer& server, unsigned netid, uint32_t mark);
+
+ // Implement IDnsTlsSocketObserver
+ void onResponse(std::vector<uint8_t> response) override;
+ void onClosed() override EXCLUDES(mLock);
+
+ private:
+ std::mutex mLock;
+
+ DnsTlsSessionCache mCache;
+ DnsTlsQueryMap mQueries;
+
+ const unsigned mMark; // Socket mark
+ const DnsTlsServer mServer;
+ IDnsTlsSocketFactory* _Nonnull const mFactory;
+
+ void doConnect() REQUIRES(mLock);
+
+ // doReconnect is used by onClosed. It runs on the reconnect thread.
+ void doReconnect() EXCLUDES(mLock);
+ std::unique_ptr<std::thread> mReconnectThread GUARDED_BY(mLock);
+
+ // Used to prevent onClosed from starting a reconnect during the destructor.
+ bool mClosing GUARDED_BY(mLock) = false;
+
+ // Sending queries on the socket is thread-safe, but construction/destruction is not.
+ std::unique_ptr<IDnsTlsSocket> mSocket GUARDED_BY(mLock);
+
+ // Send a query to the socket.
+ bool sendQuery(const DnsTlsQueryMap::Query q) REQUIRES(mLock);
+};
+
+} // end of namespace net
+} // end of namespace android
+
+#endif // _DNS_DNSTLSTRANSPORT_H
diff --git a/resolv/IDnsTlsSocket.h b/resolv/IDnsTlsSocket.h
new file mode 100644
index 0000000..0f2800e
--- /dev/null
+++ b/resolv/IDnsTlsSocket.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#ifndef _DNS_IDNSTLSSOCKET_H
+#define _DNS_IDNSTLSSOCKET_H
+
+#include <cstddef>
+#include <cstdint>
+
+#include <netdutils/Slice.h>
+
+namespace android {
+namespace net {
+
+class IDnsTlsSocketObserver;
+class DnsTlsSessionCache;
+
+// A class for managing a TLS socket that sends and receives messages in
+// [length][value] format, with a 2-byte length (i.e. DNS-over-TCP format).
+// This interface is not aware of query-response pairing or anything else about DNS.
+class IDnsTlsSocket {
+ public:
+ virtual ~IDnsTlsSocket(){};
+ // Send a query on the provided SSL socket. |query| contains
+ // the body of a query, not including the ID bytes. This function will typically return before
+ // the query is actually sent. If this function fails, the observer will be
+ // notified that the socket is closed.
+ // Note that a true return value indicates successful sending, not receipt of a response.
+ virtual bool query(uint16_t id, const netdutils::Slice query) = 0;
+};
+
+} // end of namespace net
+} // end of namespace android
+
+#endif // _DNS_IDNSTLSSOCKET_H
diff --git a/resolv/IDnsTlsSocketFactory.h b/resolv/IDnsTlsSocketFactory.h
new file mode 100644
index 0000000..a391f59
--- /dev/null
+++ b/resolv/IDnsTlsSocketFactory.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#ifndef _DNS_IDNSTLSSOCKETFACTORY_H
+#define _DNS_IDNSTLSSOCKETFACTORY_H
+
+#include "IDnsTlsSocket.h"
+
+namespace android {
+namespace net {
+
+class IDnsTlsSocketObserver;
+class DnsTlsSessionCache;
+struct DnsTlsServer;
+
+// Dependency injection interface for DnsTlsSocketFactory.
+// This pattern allows mocking of DnsTlsSocket for tests.
+class IDnsTlsSocketFactory {
+ public:
+ virtual ~IDnsTlsSocketFactory(){};
+ virtual std::unique_ptr<IDnsTlsSocket> createDnsTlsSocket(
+ const DnsTlsServer& server, unsigned mark, IDnsTlsSocketObserver* _Nonnull observer,
+ DnsTlsSessionCache* _Nonnull cache) = 0;
+};
+
+} // end of namespace net
+} // end of namespace android
+
+#endif // _DNS_IDNSTLSSOCKETFACTORY_H
diff --git a/resolv/IDnsTlsSocketObserver.h b/resolv/IDnsTlsSocketObserver.h
new file mode 100644
index 0000000..980f74a
--- /dev/null
+++ b/resolv/IDnsTlsSocketObserver.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#ifndef _DNS_IDNSTLSSOCKETOBSERVER_H
+#define _DNS_IDNSTLSSOCKETOBSERVER_H
+
+namespace android {
+namespace net {
+
+// Interface to listen for DNS query responses on a socket, and to be notified
+// when the socket is closed by the remote peer. This is only implemented by
+// DnsTlsTransport, but it is a separate interface for clarity and to avoid a
+// circular dependency with DnsTlsSocket.
+class IDnsTlsSocketObserver {
+ public:
+ virtual ~IDnsTlsSocketObserver(){};
+ virtual void onResponse(std::vector<uint8_t> response) = 0;
+
+ virtual void onClosed() = 0;
+};
+
+} // namespace net
+} // namespace android
+
+#endif // _DNS_IDNSTLSSOCKETOBSERVER_H
diff --git a/resolv/LockedQueue.h b/resolv/LockedQueue.h
new file mode 100644
index 0000000..0481eda
--- /dev/null
+++ b/resolv/LockedQueue.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#ifndef _DNS_LOCKED_QUEUE_H
+#define _DNS_LOCKED_QUEUE_H
+
+#include <algorithm>
+#include <deque>
+#include <mutex>
+
+#include <android-base/thread_annotations.h>
+
+namespace android {
+namespace net {
+
+template <typename T>
+class LockedQueue {
+ public:
+ // Push an item onto the queue.
+ void push(T item) {
+ std::lock_guard guard(mLock);
+ mQueue.push_front(std::move(item));
+ }
+
+ // Swap out the contents of the queue
+ void swap(std::deque<T>& other) {
+ std::lock_guard guard(mLock);
+ mQueue.swap(other);
+ }
+
+ private:
+ std::mutex mLock;
+ std::deque<T> mQueue GUARDED_BY(mLock);
+};
+
+} // end of namespace net
+} // end of namespace android
+
+#endif // _DNS_LOCKEDQUEUE_H
diff --git a/resolv/NOTICE b/resolv/NOTICE
new file mode 100644
index 0000000..7504f4d
--- /dev/null
+++ b/resolv/NOTICE
@@ -0,0 +1,418 @@
+Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. Neither the name of the project nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+-------------------------------------------------------------------
+
+Copyright (C) 2008 The Android Open Source Project
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+-------------------------------------------------------------------
+
+Copyright (C) 2014 The Android Open Source Project
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+-------------------------------------------------------------------
+
+Copyright (C) 2016 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.
+
+-------------------------------------------------------------------
+
+Copyright (c) 1983, 1987, 1989
+ The Regents of the University of California. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. Neither the name of the University nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+-------------------------------------------------------------------
+
+Copyright (c) 1985
+ The Regents of the University of California. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. All advertising materials mentioning features or use of this software
+ must display the following acknowledgement:
+ This product includes software developed by the University of
+ California, Berkeley and its contributors.
+4. Neither the name of the University nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+-------------------------------------------------------------------
+
+Copyright (c) 1985, 1988, 1993
+ The Regents of the University of California. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. Neither the name of the University nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+Portions Copyright (c) 1993 by Digital Equipment Corporation.
+
+Permission to use, copy, modify, and distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies, and that
+the name of Digital Equipment Corporation not be used in advertising or
+publicity pertaining to distribution of the document or software without
+specific, written prior permission.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
+WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
+CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+-------------------------------------------------------------------
+
+Copyright (c) 1985, 1989, 1993
+ The Regents of the University of California. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. All advertising materials mentioning features or use of this software
+ must display the following acknowledgement:
+ This product includes software developed by the University of
+ California, Berkeley and its contributors.
+4. Neither the name of the University nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+-------------------------------------------------------------------
+
+Copyright (c) 1985, 1993
+ The Regents of the University of California. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. All advertising materials mentioning features or use of this software
+ must display the following acknowledgement:
+ This product includes software developed by the University of
+ California, Berkeley and its contributors.
+4. Neither the name of the University nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+-------------------------------------------------------------------
+
+Copyright (c) 1985, 1993
+ The Regents of the University of California. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. Neither the name of the University nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+-------------------------------------------------------------------
+
+Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+Copyright (c) 1995-1999 by Internet Software Consortium.
+
+Permission to use, copy, modify, and distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+-------------------------------------------------------------------
+
+Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+Copyright (c) 1999 by Internet Software Consortium.
+
+Permission to use, copy, modify, and distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+-------------------------------------------------------------------
+
+Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+Portions Copyright (c) 1996-1999 by Internet Software Consortium.
+
+Permission to use, copy, modify, and distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+-------------------------------------------------------------------
+
+Portions Copyright (C) 2004, 2005, 2008, 2009 Internet Systems Consortium, Inc. ("ISC")
+Portions Copyright (C) 1996-2003 Internet Software Consortium.
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THIS SOFTWARE.
+
+-------------------------------------------------------------------
+
+Portions Copyright (c) 1993 by Digital Equipment Corporation.
+
+Permission to use, copy, modify, and distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies, and that
+the name of Digital Equipment Corporation not be used in advertising or
+publicity pertaining to distribution of the document or software without
+specific, written prior permission.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
+WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
+CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+-------------------------------------------------------------------
+
+Portions Copyright (c) 1995 by International Business Machines, Inc.
+
+International Business Machines, Inc. (hereinafter called IBM) grants
+permission under its copyrights to use, copy, modify, and distribute this
+Software with or without fee, provided that the above copyright notice and
+all paragraphs of this notice appear in all copies, and that the name of IBM
+not be used in connection with the marketing of any product incorporating
+the Software or modifications thereof, without specific, written prior
+permission.
+
+To the extent it has a right to do so, IBM grants an immunity from suit
+under its patents, if any, for the use, sale or manufacture of products to
+the extent that such products are used for performing Domain Name System
+dynamic updates in TCP/IP networks by means of the Software. No immunity is
+granted for any product per se or for any other function of any product.
+
+THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES,
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL,
+DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING
+OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN
+IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+-------------------------------------------------------------------
+
diff --git a/resolv/PrivateDnsConfiguration.cpp b/resolv/PrivateDnsConfiguration.cpp
new file mode 100644
index 0000000..1a88275
--- /dev/null
+++ b/resolv/PrivateDnsConfiguration.cpp
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#define LOG_TAG "PrivateDnsConfiguration"
+#define DBG 0
+
+#include "PrivateDnsConfiguration.h"
+
+#include <log/log.h>
+#include <netdb.h>
+#include <sys/socket.h>
+
+#include "DnsTlsTransport.h"
+#include "ResolverEventReporter.h"
+#include "netd_resolv/resolv.h"
+#include "netdutils/BackoffSequence.h"
+
+namespace android {
+namespace net {
+
+std::string addrToString(const sockaddr_storage* addr) {
+ char out[INET6_ADDRSTRLEN] = {0};
+ getnameinfo((const sockaddr*) addr, sizeof(sockaddr_storage), out, INET6_ADDRSTRLEN, nullptr, 0,
+ NI_NUMERICHOST);
+ return std::string(out);
+}
+
+bool parseServer(const char* server, sockaddr_storage* parsed) {
+ addrinfo hints = {.ai_family = AF_UNSPEC, .ai_flags = AI_NUMERICHOST | AI_NUMERICSERV};
+ addrinfo* res;
+
+ int err = getaddrinfo(server, "853", &hints, &res);
+ if (err != 0) {
+ ALOGW("Failed to parse server address (%s): %s", server, gai_strerror(err));
+ return false;
+ }
+
+ memcpy(parsed, res->ai_addr, res->ai_addrlen);
+ freeaddrinfo(res);
+ return true;
+}
+
+int PrivateDnsConfiguration::set(int32_t netId, uint32_t mark,
+ const std::vector<std::string>& servers, const std::string& name,
+ const std::set<std::vector<uint8_t>>& fingerprints) {
+ if (DBG) {
+ ALOGD("PrivateDnsConfiguration::set(%u, 0x%x, %zu, %s, %zu)", netId, mark, servers.size(),
+ name.c_str(), fingerprints.size());
+ }
+
+ const bool explicitlyConfigured = !name.empty() || !fingerprints.empty();
+
+ // Parse the list of servers that has been passed in
+ std::set<DnsTlsServer> tlsServers;
+ for (size_t i = 0; i < servers.size(); ++i) {
+ sockaddr_storage parsed;
+ if (!parseServer(servers[i].c_str(), &parsed)) {
+ return -EINVAL;
+ }
+ DnsTlsServer server(parsed);
+ server.name = name;
+ server.fingerprints = fingerprints;
+ tlsServers.insert(server);
+ }
+
+ std::lock_guard guard(mPrivateDnsLock);
+ if (explicitlyConfigured) {
+ mPrivateDnsModes[netId] = PrivateDnsMode::STRICT;
+ } else if (!tlsServers.empty()) {
+ mPrivateDnsModes[netId] = PrivateDnsMode::OPPORTUNISTIC;
+ } else {
+ mPrivateDnsModes[netId] = PrivateDnsMode::OFF;
+ mPrivateDnsTransports.erase(netId);
+ return 0;
+ }
+
+ // Create the tracker if it was not present
+ auto netPair = mPrivateDnsTransports.find(netId);
+ if (netPair == mPrivateDnsTransports.end()) {
+ // No TLS tracker yet for this netId.
+ bool added;
+ std::tie(netPair, added) = mPrivateDnsTransports.emplace(netId, PrivateDnsTracker());
+ if (!added) {
+ ALOGE("Memory error while recording private DNS for netId %d", netId);
+ return -ENOMEM;
+ }
+ }
+ auto& tracker = netPair->second;
+
+ // Remove any servers from the tracker that are not in |servers| exactly.
+ for (auto it = tracker.begin(); it != tracker.end();) {
+ if (tlsServers.count(it->first) == 0) {
+ it = tracker.erase(it);
+ } else {
+ ++it;
+ }
+ }
+
+ // Add any new or changed servers to the tracker, and initiate async checks for them.
+ for (const auto& server : tlsServers) {
+ if (needsValidation(tracker, server)) {
+ validatePrivateDnsProvider(server, tracker, netId, mark);
+ }
+ }
+ return 0;
+}
+
+PrivateDnsStatus PrivateDnsConfiguration::getStatus(unsigned netId) {
+ PrivateDnsStatus status{PrivateDnsMode::OFF, {}};
+ std::lock_guard guard(mPrivateDnsLock);
+
+ const auto mode = mPrivateDnsModes.find(netId);
+ if (mode == mPrivateDnsModes.end()) return status;
+ status.mode = mode->second;
+
+ const auto netPair = mPrivateDnsTransports.find(netId);
+ if (netPair != mPrivateDnsTransports.end()) {
+ for (const auto& serverPair : netPair->second) {
+ if (serverPair.second == Validation::success) {
+ status.validatedServers.push_back(serverPair.first);
+ }
+ }
+ }
+
+ return status;
+}
+
+void PrivateDnsConfiguration::getStatus(unsigned netId, ExternalPrivateDnsStatus* status) {
+ std::lock_guard guard(mPrivateDnsLock);
+
+ const auto mode = mPrivateDnsModes.find(netId);
+ if (mode == mPrivateDnsModes.end()) return;
+ status->mode = mode->second;
+
+ const auto netPair = mPrivateDnsTransports.find(netId);
+ if (netPair != mPrivateDnsTransports.end()) {
+ int count = 0;
+ for (const auto& serverPair : netPair->second) {
+ status->serverStatus[count].ss = serverPair.first.ss;
+ status->serverStatus[count].hostname =
+ serverPair.first.name.empty() ? "" : serverPair.first.name.c_str();
+ status->serverStatus[count].validation = serverPair.second;
+ count++;
+ if (count >= MAXNS) break; // Lose the rest
+ }
+ status->numServers = count;
+ }
+}
+
+void PrivateDnsConfiguration::clear(unsigned netId) {
+ if (DBG) {
+ ALOGD("PrivateDnsConfiguration::clear(%u)", netId);
+ }
+ std::lock_guard guard(mPrivateDnsLock);
+ mPrivateDnsModes.erase(netId);
+ mPrivateDnsTransports.erase(netId);
+}
+
+void PrivateDnsConfiguration::validatePrivateDnsProvider(const DnsTlsServer& server,
+ PrivateDnsTracker& tracker, unsigned netId,
+ uint32_t mark) REQUIRES(mPrivateDnsLock) {
+ if (DBG) {
+ ALOGD("validatePrivateDnsProvider(%s, %u)", addrToString(&server.ss).c_str(), netId);
+ }
+
+ tracker[server] = Validation::in_process;
+ if (DBG) {
+ ALOGD("Server %s marked as in_process. Tracker now has size %zu",
+ addrToString(&server.ss).c_str(), tracker.size());
+ }
+ // Note that capturing |server| and |netId| in this lambda create copies.
+ std::thread validate_thread([this, server, netId, mark] {
+ // cat /proc/sys/net/ipv4/tcp_syn_retries yields "6".
+ //
+ // Start with a 1 minute delay and backoff to once per hour.
+ //
+ // Assumptions:
+ // [1] Each TLS validation is ~10KB of certs+handshake+payload.
+ // [2] Network typically provision clients with <=4 nameservers.
+ // [3] Average month has 30 days.
+ //
+ // Each validation pass in a given hour is ~1.2MB of data. And 24
+ // such validation passes per day is about ~30MB per month, in the
+ // worst case. Otherwise, this will cost ~600 SYNs per month
+ // (6 SYNs per ip, 4 ips per validation pass, 24 passes per day).
+ auto backoff = netdutils::BackoffSequence<>::Builder()
+ .withInitialRetransmissionTime(std::chrono::seconds(60))
+ .withMaximumRetransmissionTime(std::chrono::seconds(3600))
+ .build();
+
+ while (true) {
+ // ::validate() is a blocking call that performs network operations.
+ // It can take milliseconds to minutes, up to the SYN retry limit.
+ const bool success = DnsTlsTransport::validate(server, netId, mark);
+ if (DBG) {
+ ALOGD("validateDnsTlsServer returned %d for %s", success,
+ addrToString(&server.ss).c_str());
+ }
+
+ const bool needs_reeval = this->recordPrivateDnsValidation(server, netId, success);
+ if (!needs_reeval) {
+ break;
+ }
+
+ if (backoff.hasNextTimeout()) {
+ std::this_thread::sleep_for(backoff.getNextTimeout());
+ } else {
+ break;
+ }
+ }
+ });
+ validate_thread.detach();
+}
+
+bool PrivateDnsConfiguration::recordPrivateDnsValidation(const DnsTlsServer& server, unsigned netId,
+ bool success) {
+ constexpr bool NEEDS_REEVALUATION = true;
+ constexpr bool DONT_REEVALUATE = false;
+
+ std::lock_guard guard(mPrivateDnsLock);
+
+ auto netPair = mPrivateDnsTransports.find(netId);
+ if (netPair == mPrivateDnsTransports.end()) {
+ ALOGW("netId %u was erased during private DNS validation", netId);
+ return DONT_REEVALUATE;
+ }
+
+ const auto mode = mPrivateDnsModes.find(netId);
+ if (mode == mPrivateDnsModes.end()) {
+ ALOGW("netId %u has no private DNS validation mode", netId);
+ return DONT_REEVALUATE;
+ }
+ const bool modeDoesReevaluation = (mode->second == PrivateDnsMode::STRICT);
+
+ bool reevaluationStatus =
+ (success || !modeDoesReevaluation) ? DONT_REEVALUATE : NEEDS_REEVALUATION;
+
+ auto& tracker = netPair->second;
+ auto serverPair = tracker.find(server);
+ if (serverPair == tracker.end()) {
+ ALOGW("Server %s was removed during private DNS validation",
+ addrToString(&server.ss).c_str());
+ success = false;
+ reevaluationStatus = DONT_REEVALUATE;
+ } else if (!(serverPair->first == server)) {
+ // TODO: It doesn't seem correct to overwrite the tracker entry for
+ // |server| down below in this circumstance... Fix this.
+ ALOGW("Server %s was changed during private DNS validation",
+ addrToString(&server.ss).c_str());
+ success = false;
+ reevaluationStatus = DONT_REEVALUATE;
+ }
+
+ // Send a validation event to NetdEventListenerService.
+ const auto& listeners = ResolverEventReporter::getInstance().getListeners();
+ if (listeners.size() != 0) {
+ for (const auto& it : listeners) {
+ it->onPrivateDnsValidationEvent(netId, addrToString(&server.ss), server.name, success);
+ }
+ if (DBG) {
+ ALOGD("Sent validation %s event on netId %u for %s with hostname %s",
+ success ? "success" : "failure", netId, addrToString(&server.ss).c_str(),
+ server.name.c_str());
+ }
+ } else {
+ ALOGE("Validation event not sent since no INetdEventListener receiver is available.");
+ }
+
+ if (success) {
+ tracker[server] = Validation::success;
+ if (DBG) {
+ ALOGD("Validation succeeded for %s! Tracker now has %zu entries.",
+ addrToString(&server.ss).c_str(), tracker.size());
+ }
+ } else {
+ // Validation failure is expected if a user is on a captive portal.
+ // TODO: Trigger a second validation attempt after captive portal login
+ // succeeds.
+ tracker[server] = (reevaluationStatus == NEEDS_REEVALUATION) ? Validation::in_process
+ : Validation::fail;
+ if (DBG) {
+ ALOGD("Validation failed for %s!", addrToString(&server.ss).c_str());
+ }
+ }
+
+ return reevaluationStatus;
+}
+
+// Start validation for newly added servers as well as any servers that have
+// landed in Validation::fail state. Note that servers that have failed
+// multiple validation attempts but for which there is still a validating
+// thread running are marked as being Validation::in_process.
+bool PrivateDnsConfiguration::needsValidation(const PrivateDnsTracker& tracker,
+ const DnsTlsServer& server) {
+ const auto& iter = tracker.find(server);
+ return (iter == tracker.end()) || (iter->second == Validation::fail);
+}
+
+PrivateDnsConfiguration gPrivateDnsConfiguration;
+
+} // namespace net
+} // namespace android
diff --git a/resolv/PrivateDnsConfiguration.h b/resolv/PrivateDnsConfiguration.h
new file mode 100644
index 0000000..50fb54d
--- /dev/null
+++ b/resolv/PrivateDnsConfiguration.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#ifndef NETD_RESOLV_PRIVATEDNSCONFIGURATION_H
+#define NETD_RESOLV_PRIVATEDNSCONFIGURATION_H
+
+#include <list>
+#include <map>
+#include <mutex>
+#include <vector>
+
+#include <android-base/thread_annotations.h>
+
+#include "DnsTlsServer.h"
+
+namespace android {
+namespace net {
+
+// The DNS over TLS mode on a specific netId.
+enum class PrivateDnsMode : uint8_t { OFF, OPPORTUNISTIC, STRICT };
+
+// Validation status of a DNS over TLS server (on a specific netId).
+enum class Validation : uint8_t { in_process, success, fail, unknown_server, unknown_netid };
+
+struct PrivateDnsStatus {
+ PrivateDnsMode mode;
+ std::list<DnsTlsServer> validatedServers;
+};
+
+// TODO: remove this C-style struct and use PrivateDnsStatus everywhere.
+struct ExternalPrivateDnsStatus {
+ PrivateDnsMode mode;
+ int numServers;
+ struct PrivateDnsInfo {
+ sockaddr_storage ss;
+ const char* hostname;
+ Validation validation;
+ } serverStatus[MAXNS];
+};
+
+class PrivateDnsConfiguration {
+ public:
+ int set(int32_t netId, uint32_t mark, const std::vector<std::string>& servers,
+ const std::string& name, const std::set<std::vector<uint8_t>>& fingerprints);
+
+ PrivateDnsStatus getStatus(unsigned netId);
+
+ // DEPRECATED, use getStatus() above.
+ void getStatus(unsigned netId, ExternalPrivateDnsStatus* status);
+
+ void clear(unsigned netId);
+
+ private:
+ typedef std::map<DnsTlsServer, Validation, AddressComparator> PrivateDnsTracker;
+
+ void validatePrivateDnsProvider(const DnsTlsServer& server, PrivateDnsTracker& tracker,
+ unsigned netId, uint32_t mark) REQUIRES(mPrivateDnsLock);
+
+ bool recordPrivateDnsValidation(const DnsTlsServer& server, unsigned netId, bool success);
+
+ // Start validation for newly added servers as well as any servers that have
+ // landed in Validation::fail state. Note that servers that have failed
+ // multiple validation attempts but for which there is still a validating
+ // thread running are marked as being Validation::in_process.
+ bool needsValidation(const PrivateDnsTracker& tracker, const DnsTlsServer& server);
+
+ std::mutex mPrivateDnsLock;
+ std::map<unsigned, PrivateDnsMode> mPrivateDnsModes GUARDED_BY(mPrivateDnsLock);
+ // Structure for tracking the validation status of servers on a specific netId.
+ // Using the AddressComparator ensures at most one entry per IP address.
+ std::map<unsigned, PrivateDnsTracker> mPrivateDnsTransports GUARDED_BY(mPrivateDnsLock);
+};
+
+extern PrivateDnsConfiguration gPrivateDnsConfiguration;
+
+} // namespace net
+} // namespace android
+
+#endif /* NETD_RESOLV_PRIVATEDNSCONFIGURATION_H */
diff --git a/resolv/README.md b/resolv/README.md
new file mode 100644
index 0000000..8fe4c89
--- /dev/null
+++ b/resolv/README.md
@@ -0,0 +1,134 @@
+# DNS-over-TLS query forwarder design
+
+## Overview
+
+The DNS-over-TLS query forwarder consists of five classes:
+ * `DnsTlsDispatcher`
+ * `DnsTlsTransport`
+ * `DnsTlsQueryMap`
+ * `DnsTlsSessionCache`
+ * `DnsTlsSocket`
+
+`DnsTlsDispatcher` is a singleton class whose `query` method is the DnsTls's
+only public interface. `DnsTlsDispatcher` is just a table holding the
+`DnsTlsTransport` for each server (represented by a `DnsTlsServer` struct) and
+network. `DnsTlsDispatcher` also blocks each query thread, waiting on a
+`std::future` returned by `DnsTlsTransport` that represents the response.
+
+`DnsTlsTransport` sends each query over a `DnsTlsSocket`, opening a
+new one if necessary. It also has to listen for responses from the
+`DnsTlsSocket`, which happen on a different thread.
+`IDnsTlsSocketObserver` is an interface defining how `DnsTlsSocket` returns
+responses to `DnsTlsTransport`.
+
+`DnsTlsQueryMap` and `DnsTlsSessionCache` are helper classes owned by `DnsTlsTransport`.
+`DnsTlsQueryMap` handles ID renumbering and query-response pairing.
+`DnsTlsSessionCache` allows TLS session resumption.
+
+`DnsTlsSocket` interleaves all queries onto a single socket, and reports all
+responses to `DnsTlsTransport` (through the `IDnsTlsObserver` interface). It doesn't
+know anything about which queries correspond to which responses, and does not retain
+state to indicate whether there is an outstanding query.
+
+## Threading
+
+### Overall patterns
+
+For clarity, each of the five classes in this design is thread-safe and holds one lock.
+Classes that spawn a helper thread call `thread::join()` in their destructor to ensure
+that it is cleaned up appropriately.
+
+All the classes here make full use of Clang thread annotations (and also null-pointer
+annotations) to minimize the likelihood of a latent threading bug. The unit tests are
+also heavily threaded to exercise this functionality.
+
+This code creates O(1) threads per socket, and does not create a new thread for each
+query or response. However, DnsProxyListener does create a thread for each query.
+
+### Threading in `DnsTlsSocket`
+
+`DnsTlsSocket` can receive queries on any thread, and send them over a
+"reliable datagram pipe" (`socketpair()` in `SOCK_SEQPACKET` mode).
+The query method writes a struct (containing a pointer to the query) to the pipe
+from its thread, and the loop thread (which owns the SSL socket)
+reads off the other end of the pipe. The pipe doesn't actually have a queue "inside";
+instead, any queueing happens by blocking the query thread until the
+socket thread can read the datagram off the other end.
+
+We need to pass messages between threads using a pipe, and not a condition variable
+or a thread-safe queue, because the socket thread has to be blocked
+in `poll()` waiting for data from the server, but also has to be woken
+up on inputs from the query threads. Therefore, inputs from the query
+threads have to arrive on a socket, so that `poll()` can listen for them.
+(There can only be a single thread because [you can't use different threads
+to read and write in OpenSSL](https://www.openssl.org/blog/blog/2017/02/21/threads/)).
+
+## ID renumbering
+
+`DnsTlsDispatcher` accepts queries that have colliding ID numbers and still sends them on
+a single socket. To avoid confusion at the server, `DnsTlsQueryMap` assigns each
+query a new ID for transmission, records the mapping from input IDs to sent IDs, and
+applies the inverse mapping to responses before returning them to the caller.
+
+`DnsTlsQueryMap` assigns each new query the ID number one greater than the largest
+ID number of an outstanding query. This means that ID numbers are initially sequential
+and usually small. If the largest possible ID number is already in use,
+`DnsTlsQueryMap` will scan the ID space to find an available ID, or fail the query
+if there are no available IDs. Queries will not block waiting for an ID number to
+become available.
+
+## Time constants
+
+`DnsTlsSocket` imposes a 20-second inactivity timeout. A socket that has been idle for
+20 seconds will be closed. This sets the limit of tolerance for slow replies,
+which could happen as a result of malfunctioning authoritative DNS servers.
+If there are any pending queries, `DnsTlsTransport` will retry them.
+
+`DnsTlsQueryMap` imposes a retry limit of 3. `DnsTlsTransport` will retry the query up
+to 3 times before reporting failure to `DnsTlsDispatcher`.
+This limit helps to ensure proper functioning in the case of a recursive resolver that
+is malfunctioning or is flooded with requests that are stalled due to malfunctioning
+authoritative servers.
+
+`DnsTlsDispatcher` maintains a 5-minute timeout. Any `DnsTlsTransport` that has had no
+outstanding queries for 5 minutes will be destroyed at the next query on a different
+transport.
+This sets the limit on how long session tickets will be preserved during idle periods,
+because each `DnsTlsTransport` owns a `DnsTlsSessionCache`. Imposing this timeout
+increases latency on the first query after an idle period, but also helps to avoid
+unbounded memory usage.
+
+`DnsTlsSessionCache` sets a limit of 5 sessions in each cache, expiring the oldest one
+when the limit is reached. However, because the client code does not currently
+reuse sessions more than once, it should not be possible to hit this limit.
+
+## Testing
+
+Unit tests are in `dns_tls_test.cpp`. They cover all the classes except
+`DnsTlsSocket` (which requires `CAP_NET_ADMIN` because it uses `setsockopt(SO_MARK)`) and
+`DnsTlsSessionCache` (which requires integration with libssl). These classes are
+exercised by the integration tests in `../tests/resolv_test.cpp`.
+
+### Dependency Injection
+
+For unit testing, we would like to be able to mock out `DnsTlsSocket`. This is
+particularly required for unit testing of `DnsTlsDispatcher` and `DnsTlsTransport`.
+To make these unit tests possible, this code uses a dependency injection pattern:
+`DnsTlsSocket` is produced by a `DnsTlsSocketFactory`, and both of these have a
+defined interface.
+
+`DnsTlsDispatcher`'s constructor takes an `IDnsTlsSocketFactory`,
+which in production is a `DnsTlsSocketFactory`. However, in unit tests, we can
+substitute a test factory that returns a fake socket, so that the unit tests can
+run without actually connecting over TLS to a test server. (The integration tests
+do actual TLS.)
+
+## Logging
+
+This code uses `ALOGV` throughout for low-priority logging, and does not use
+`ALOGD`. `ALOGV` is disabled by default, unless activated by `#define LOG_NDEBUG 0`.
+(`ALOGD` is not disabled by default, requiring extra measures to avoid spamming the
+system log in production builds.)
+
+## Reference
+ * [BoringSSL API docs](https://commondatastorage.googleapis.com/chromium-boringssl-docs/headers.html)
diff --git a/resolv/ResolverController.cpp b/resolv/ResolverController.cpp
new file mode 100644
index 0000000..ac24259
--- /dev/null
+++ b/resolv/ResolverController.cpp
@@ -0,0 +1,383 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#define LOG_TAG "ResolverController"
+
+#include "ResolverController.h"
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include <netdb.h>
+
+#include <aidl/android/net/IDnsResolver.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+
+#include "Dns64Configuration.h"
+#include "DnsResolver.h"
+#include "Fwmark.h"
+#include "PrivateDnsConfiguration.h"
+#include "ResolverEventReporter.h"
+#include "ResolverStats.h"
+#include "netd_resolv/stats.h"
+#include "resolv_cache.h"
+
+using namespace std::placeholders;
+using aidl::android::net::ResolverParamsParcel;
+
+namespace android {
+
+using netdutils::DumpWriter;
+
+namespace net {
+
+namespace {
+
+std::string addrToString(const sockaddr_storage* addr) {
+ char out[INET6_ADDRSTRLEN] = {0};
+ getnameinfo((const sockaddr*)addr, sizeof(sockaddr_storage), out, INET6_ADDRSTRLEN, nullptr, 0,
+ NI_NUMERICHOST);
+ return std::string(out);
+}
+
+const char* getPrivateDnsModeString(PrivateDnsMode mode) {
+ switch (mode) {
+ case PrivateDnsMode::OFF:
+ return "OFF";
+ case PrivateDnsMode::OPPORTUNISTIC:
+ return "OPPORTUNISTIC";
+ case PrivateDnsMode::STRICT:
+ return "STRICT";
+ }
+}
+
+constexpr const char* validationStatusToString(Validation value) {
+ switch (value) {
+ case Validation::in_process:
+ return "in_process";
+ case Validation::success:
+ return "success";
+ case Validation::fail:
+ return "fail";
+ case Validation::unknown_server:
+ return "unknown_server";
+ case Validation::unknown_netid:
+ return "unknown_netid";
+ default:
+ return "unknown_status";
+ }
+}
+
+void sendNat64PrefixEvent(const Dns64Configuration::Nat64PrefixInfo& args) {
+ const auto& listeners = ResolverEventReporter::getInstance().getListeners();
+ if (listeners.size() == 0) {
+ LOG(ERROR) << __func__ << ": No available listener. dropping NAT64 prefix event";
+ return;
+ }
+ for (const auto& it : listeners) {
+ it->onNat64PrefixEvent(args.netId, args.added, args.prefixString, args.prefixLength);
+ }
+}
+
+int getDnsInfo(unsigned netId, std::vector<std::string>* servers, std::vector<std::string>* domains,
+ res_params* params, std::vector<android::net::ResolverStats>* stats,
+ std::vector<int32_t>* wait_for_pending_req_timeout_count) {
+ using aidl::android::net::IDnsResolver;
+ using android::net::ResolverStats;
+ static_assert(ResolverStats::STATS_SUCCESSES == IDnsResolver::RESOLVER_STATS_SUCCESSES &&
+ ResolverStats::STATS_ERRORS == IDnsResolver::RESOLVER_STATS_ERRORS &&
+ ResolverStats::STATS_TIMEOUTS == IDnsResolver::RESOLVER_STATS_TIMEOUTS &&
+ ResolverStats::STATS_INTERNAL_ERRORS ==
+ IDnsResolver::RESOLVER_STATS_INTERNAL_ERRORS &&
+ ResolverStats::STATS_RTT_AVG == IDnsResolver::RESOLVER_STATS_RTT_AVG &&
+ ResolverStats::STATS_LAST_SAMPLE_TIME ==
+ IDnsResolver::RESOLVER_STATS_LAST_SAMPLE_TIME &&
+ ResolverStats::STATS_USABLE == IDnsResolver::RESOLVER_STATS_USABLE &&
+ ResolverStats::STATS_COUNT == IDnsResolver::RESOLVER_STATS_COUNT,
+ "AIDL and ResolverStats.h out of sync");
+ int nscount = -1;
+ sockaddr_storage res_servers[MAXNS];
+ int dcount = -1;
+ char res_domains[MAXDNSRCH][MAXDNSRCHPATH];
+ res_stats res_stats[MAXNS];
+ servers->clear();
+ domains->clear();
+ *params = res_params{};
+ stats->clear();
+ int res_wait_for_pending_req_timeout_count;
+ int revision_id = android_net_res_stats_get_info_for_net(
+ netId, &nscount, res_servers, &dcount, res_domains, params, res_stats,
+ &res_wait_for_pending_req_timeout_count);
+
+ // If the netId is unknown (which can happen for valid net IDs for which no DNS servers have
+ // yet been configured), there is no revision ID. In this case there is no data to return.
+ if (revision_id < 0) {
+ return 0;
+ }
+
+ // Verify that the returned data is sane.
+ if (nscount < 0 || nscount > MAXNS || dcount < 0 || dcount > MAXDNSRCH) {
+ LOG(ERROR) << __func__ << ": nscount = " << nscount << ", dcount = " << dcount;
+ return -ENOTRECOVERABLE;
+ }
+
+ // Determine which servers are considered usable by the resolver.
+ bool valid_servers[MAXNS];
+ std::fill_n(valid_servers, MAXNS, false);
+ android_net_res_stats_get_usable_servers(params, res_stats, nscount, valid_servers);
+
+ // Convert the server sockaddr structures to std::string.
+ stats->resize(nscount);
+ for (int i = 0; i < nscount; ++i) {
+ char hbuf[NI_MAXHOST];
+ int rv =
+ getnameinfo(reinterpret_cast<const sockaddr*>(&res_servers[i]),
+ sizeof(res_servers[i]), hbuf, sizeof(hbuf), nullptr, 0, NI_NUMERICHOST);
+ std::string server_str;
+ if (rv == 0) {
+ server_str.assign(hbuf);
+ } else {
+ LOG(ERROR) << "getnameinfo() failed for server #" << i << ": " << gai_strerror(rv);
+ server_str.assign("<invalid>");
+ }
+ servers->push_back(std::move(server_str));
+ android::net::ResolverStats& cur_stats = (*stats)[i];
+ android_net_res_stats_aggregate(&res_stats[i], &cur_stats.successes, &cur_stats.errors,
+ &cur_stats.timeouts, &cur_stats.internal_errors,
+ &cur_stats.rtt_avg, &cur_stats.last_sample_time);
+ cur_stats.usable = valid_servers[i];
+ }
+
+ // Convert the stack-allocated search domain strings to std::string.
+ for (int i = 0; i < dcount; ++i) {
+ domains->push_back(res_domains[i]);
+ }
+
+ (*wait_for_pending_req_timeout_count)[0] = res_wait_for_pending_req_timeout_count;
+ return 0;
+}
+
+} // namespace
+
+ResolverController::ResolverController()
+ : mDns64Configuration(
+ [](uint32_t netId, uint32_t uid, android_net_context* netcontext) {
+ gResNetdCallbacks.get_network_context(netId, uid, netcontext);
+ },
+ std::bind(sendNat64PrefixEvent, _1)) {}
+
+void ResolverController::destroyNetworkCache(unsigned netId) {
+ LOG(VERBOSE) << __func__ << ": netId = " << netId;
+
+ resolv_delete_cache_for_net(netId);
+ mDns64Configuration.stopPrefixDiscovery(netId);
+ gPrivateDnsConfiguration.clear(netId);
+}
+
+int ResolverController::createNetworkCache(unsigned netId) {
+ LOG(VERBOSE) << __func__ << ": netId = " << netId;
+
+ return resolv_create_cache_for_net(netId);
+}
+
+int ResolverController::setResolverConfiguration(
+ const ResolverParamsParcel& resolverParams,
+ const std::set<std::vector<uint8_t>>& tlsFingerprints) {
+ using aidl::android::net::IDnsResolver;
+
+ // At private DNS validation time, we only know the netId, so we have to guess/compute the
+ // corresponding socket mark.
+ Fwmark fwmark;
+ fwmark.netId = resolverParams.netId;
+ fwmark.explicitlySelected = true;
+ fwmark.protectedFromVpn = true;
+ fwmark.permission = PERMISSION_SYSTEM;
+
+ // Allow at most MAXNS private DNS servers in a network to prevent too many broken servers.
+ std::vector<std::string> tlsServers = resolverParams.tlsServers;
+ if (tlsServers.size() > MAXNS) {
+ tlsServers.resize(MAXNS);
+ }
+ const int err = gPrivateDnsConfiguration.set(resolverParams.netId, fwmark.intValue, tlsServers,
+ resolverParams.tlsName, tlsFingerprints);
+ if (err != 0) {
+ return err;
+ }
+
+ // Convert network-assigned server list to bionic's format.
+ const size_t serverCount = std::min<size_t>(MAXNS, resolverParams.servers.size());
+ std::vector<const char*> server_ptrs;
+ for (size_t i = 0; i < serverCount; ++i) {
+ server_ptrs.push_back(resolverParams.servers[i].c_str());
+ }
+
+ std::string domains_str = android::base::Join(resolverParams.domains, " ");
+
+ // TODO: Change resolv_set_nameservers_for_net() to use ResolverParamsParcel directly.
+ res_params res_params = {};
+ res_params.sample_validity = resolverParams.sampleValiditySeconds;
+ res_params.success_threshold = resolverParams.successThreshold;
+ res_params.min_samples = resolverParams.minSamples;
+ res_params.max_samples = resolverParams.maxSamples;
+ res_params.base_timeout_msec = resolverParams.baseTimeoutMsec;
+ res_params.retry_count = resolverParams.retryCount;
+
+ LOG(VERBOSE) << "setDnsServers netId = " << resolverParams.netId
+ << ", numservers = " << resolverParams.domains.size();
+
+ return -resolv_set_nameservers_for_net(resolverParams.netId, server_ptrs.data(),
+ server_ptrs.size(), domains_str.c_str(), &res_params);
+}
+
+int ResolverController::getResolverInfo(int32_t netId, std::vector<std::string>* servers,
+ std::vector<std::string>* domains,
+ std::vector<std::string>* tlsServers,
+ std::vector<int32_t>* params, std::vector<int32_t>* stats,
+ std::vector<int32_t>* wait_for_pending_req_timeout_count) {
+ using aidl::android::net::IDnsResolver;
+ using android::net::ResolverStats;
+ res_params res_params;
+ std::vector<ResolverStats> res_stats;
+ int ret = getDnsInfo(netId, servers, domains, &res_params, &res_stats,
+ wait_for_pending_req_timeout_count);
+ if (ret != 0) {
+ return ret;
+ }
+
+ // Serialize the information for binder.
+ ResolverStats::encodeAll(res_stats, stats);
+
+ ExternalPrivateDnsStatus privateDnsStatus = {PrivateDnsMode::OFF, 0, {}};
+ gPrivateDnsConfiguration.getStatus(netId, &privateDnsStatus);
+ for (int i = 0; i < privateDnsStatus.numServers; i++) {
+ std::string tlsServer_str = addrToString(&(privateDnsStatus.serverStatus[i].ss));
+ tlsServers->push_back(std::move(tlsServer_str));
+ }
+
+ params->resize(IDnsResolver::RESOLVER_PARAMS_COUNT);
+ (*params)[IDnsResolver::RESOLVER_PARAMS_SAMPLE_VALIDITY] = res_params.sample_validity;
+ (*params)[IDnsResolver::RESOLVER_PARAMS_SUCCESS_THRESHOLD] = res_params.success_threshold;
+ (*params)[IDnsResolver::RESOLVER_PARAMS_MIN_SAMPLES] = res_params.min_samples;
+ (*params)[IDnsResolver::RESOLVER_PARAMS_MAX_SAMPLES] = res_params.max_samples;
+ (*params)[IDnsResolver::RESOLVER_PARAMS_BASE_TIMEOUT_MSEC] = res_params.base_timeout_msec;
+ (*params)[IDnsResolver::RESOLVER_PARAMS_RETRY_COUNT] = res_params.retry_count;
+ return 0;
+}
+
+void ResolverController::startPrefix64Discovery(int32_t netId) {
+ mDns64Configuration.startPrefixDiscovery(netId);
+}
+
+void ResolverController::stopPrefix64Discovery(int32_t netId) {
+ return mDns64Configuration.stopPrefixDiscovery(netId);
+}
+
+// TODO: use StatusOr<T> to wrap the result.
+int ResolverController::getPrefix64(unsigned netId, netdutils::IPPrefix* prefix) {
+ netdutils::IPPrefix p = mDns64Configuration.getPrefix64(netId);
+ if (p.family() != AF_INET6 || p.length() == 0) {
+ LOG(ERROR) << "No valid NAT64 prefix (" << netId << ", " << p.toString().c_str() << ")";
+
+ return -ENOENT;
+ }
+ *prefix = p;
+ return 0;
+}
+
+void ResolverController::dump(DumpWriter& dw, unsigned netId) {
+ // No lock needed since Bionic's resolver locks all accessed data structures internally.
+ using android::net::ResolverStats;
+ std::vector<std::string> servers;
+ std::vector<std::string> domains;
+ res_params params = {};
+ std::vector<ResolverStats> stats;
+ std::vector<int32_t> wait_for_pending_req_timeout_count(1, 0);
+ time_t now = time(nullptr);
+ int rv = getDnsInfo(netId, &servers, &domains, ¶ms, &stats,
+ &wait_for_pending_req_timeout_count);
+ dw.incIndent();
+ if (rv != 0) {
+ dw.println("getDnsInfo() failed for netid %u", netId);
+ } else {
+ if (servers.empty()) {
+ dw.println("No DNS servers defined");
+ } else {
+ dw.println("DnsEvent subsampling map: " +
+ android::base::Join(resolv_cache_dump_subsampling_map(netId), ' '));
+ dw.println(
+ "DNS servers: # IP (total, successes, errors, timeouts, internal errors, "
+ "RTT avg, last sample)");
+ dw.incIndent();
+ for (size_t i = 0; i < servers.size(); ++i) {
+ if (i < stats.size()) {
+ const ResolverStats& s = stats[i];
+ int total = s.successes + s.errors + s.timeouts + s.internal_errors;
+ if (total > 0) {
+ int time_delta = (s.last_sample_time > 0) ? now - s.last_sample_time : -1;
+ dw.println("%s (%d, %d, %d, %d, %d, %dms, %ds)%s", servers[i].c_str(),
+ total, s.successes, s.errors, s.timeouts, s.internal_errors,
+ s.rtt_avg, time_delta, s.usable ? "" : " BROKEN");
+ } else {
+ dw.println("%s <no data>", servers[i].c_str());
+ }
+ } else {
+ dw.println("%s <no stats>", servers[i].c_str());
+ }
+ }
+ dw.decIndent();
+ }
+ if (domains.empty()) {
+ dw.println("No search domains defined");
+ } else {
+ std::string domains_str = android::base::Join(domains, ", ");
+ dw.println("search domains: %s", domains_str.c_str());
+ }
+ if (params.sample_validity != 0) {
+ dw.println(
+ "DNS parameters: sample validity = %us, success threshold = %u%%, "
+ "samples (min, max) = (%u, %u), base_timeout = %dmsec, retry count = "
+ "%dtimes",
+ params.sample_validity, params.success_threshold, params.min_samples,
+ params.max_samples, params.base_timeout_msec, params.retry_count);
+ }
+
+ mDns64Configuration.dump(dw, netId);
+ ExternalPrivateDnsStatus privateDnsStatus = {PrivateDnsMode::OFF, 0, {}};
+ gPrivateDnsConfiguration.getStatus(netId, &privateDnsStatus);
+ dw.println("Private DNS mode: %s", getPrivateDnsModeString(privateDnsStatus.mode));
+ if (!privateDnsStatus.numServers) {
+ dw.println("No Private DNS servers configured");
+ } else {
+ dw.println("Private DNS configuration (%u entries)", privateDnsStatus.numServers);
+ dw.incIndent();
+ for (int i = 0; i < privateDnsStatus.numServers; i++) {
+ dw.println("%s name{%s} status{%s}",
+ addrToString(&(privateDnsStatus.serverStatus[i].ss)).c_str(),
+ privateDnsStatus.serverStatus[i].hostname,
+ validationStatusToString(static_cast<Validation>(
+ privateDnsStatus.serverStatus[i].validation)));
+ }
+ dw.decIndent();
+ }
+ dw.println("Concurrent DNS query timeout: %d", wait_for_pending_req_timeout_count[0]);
+ }
+ dw.decIndent();
+}
+
+} // namespace net
+} // namespace android
diff --git a/resolv/ResolverController.h b/resolv/ResolverController.h
new file mode 100644
index 0000000..6d08cdb
--- /dev/null
+++ b/resolv/ResolverController.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#ifndef _RESOLVER_CONTROLLER_H_
+#define _RESOLVER_CONTROLLER_H_
+
+#include <list>
+#include <set>
+#include <vector>
+
+#include <aidl/android/net/ResolverParamsParcel.h>
+#include "Dns64Configuration.h"
+#include "netd_resolv/resolv.h"
+#include "netdutils/DumpWriter.h"
+
+struct res_params;
+
+namespace android {
+namespace net {
+
+struct ResolverStats;
+
+class ResolverController {
+ public:
+ ResolverController();
+ ~ResolverController() = default;
+
+ void destroyNetworkCache(unsigned netid);
+ int createNetworkCache(unsigned netid);
+
+ int getPrefix64(unsigned netId, netdutils::IPPrefix* prefix);
+
+ // Binder specific functions, which convert between the ResolverParamsParcel and the
+ // actual data structures, and call setDnsServer() / getDnsInfo() for the actual processing.
+ int setResolverConfiguration(const aidl::android::net::ResolverParamsParcel& resolverParams,
+ const std::set<std::vector<uint8_t>>& tlsFingerprints);
+
+ int getResolverInfo(int32_t netId, std::vector<std::string>* servers,
+ std::vector<std::string>* domains, std::vector<std::string>* tlsServers,
+ std::vector<int32_t>* params, std::vector<int32_t>* stats,
+ std::vector<int32_t>* wait_for_pending_req_timeout_count);
+
+ void startPrefix64Discovery(int32_t netId);
+ void stopPrefix64Discovery(int32_t netId);
+
+ void dump(netdutils::DumpWriter& dw, unsigned netId);
+
+ private:
+ Dns64Configuration mDns64Configuration;
+};
+} // namespace net
+} // namespace android
+
+#endif /* _RESOLVER_CONTROLLER_H_ */
diff --git a/resolv/ResolverEventReporter.cpp b/resolv/ResolverEventReporter.cpp
new file mode 100644
index 0000000..ccec08b
--- /dev/null
+++ b/resolv/ResolverEventReporter.cpp
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+#define LOG_TAG "ResolverEventReporter"
+
+#include "ResolverEventReporter.h"
+
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+
+using aidl::android::net::metrics::INetdEventListener;
+
+ResolverEventReporter& ResolverEventReporter::getInstance() {
+ // It should be initialized only once.
+ static ResolverEventReporter instance;
+
+ // Add framework metrics listener. Because the binder service "netd_listener" may be launched
+ // later than Netd, try to get binder handler in every instance query if any. The framework
+ // metrics listener should be added only once if it has been already added successfully.
+ instance.addDefaultListener();
+
+ return instance;
+}
+
+ResolverEventReporter::ListenerSet ResolverEventReporter::getListeners() const {
+ return getListenersImpl();
+}
+
+int ResolverEventReporter::addListener(const std::shared_ptr<INetdEventListener>& listener) {
+ return addListenerImpl(listener);
+}
+
+// TODO: Consider registering metrics listener from framework and remove this function.
+// Currently, the framework listener "netd_listener" is shared by netd and libnetd_resolv.
+// Consider breaking it into two listeners. Once it has done, may let framework register
+// the listener proactively.
+void ResolverEventReporter::addDefaultListener() {
+ std::lock_guard lock(mMutex);
+
+ static bool added = false;
+ if (added) return;
+
+ // Use the non-blocking call AServiceManager_checkService in order not to delay DNS
+ // lookup threads when the netd_listener service is not ready.
+ ndk::SpAIBinder binder = ndk::SpAIBinder(AServiceManager_checkService("netd_listener"));
+ std::shared_ptr<INetdEventListener> listener = INetdEventListener::fromBinder(binder);
+
+ if (listener == nullptr) return;
+
+ if (!addListenerImplLocked(listener)) added = true;
+}
+
+void ResolverEventReporter::handleBinderDied(const void* who) {
+ std::lock_guard lock(mMutex);
+
+ // Use the raw binder pointer address to be the identification of dead binder. Treat "who"
+ // which passes the raw address of dead binder as an identification only.
+ auto found = std::find_if(mListeners.begin(), mListeners.end(),
+ [=](const auto& it) { return static_cast<void*>(it.get()) == who; });
+
+ if (found != mListeners.end()) mListeners.erase(found);
+}
+
+ResolverEventReporter::ListenerSet ResolverEventReporter::getListenersImpl() const {
+ std::lock_guard lock(mMutex);
+ return mListeners;
+}
+
+int ResolverEventReporter::addListenerImpl(const std::shared_ptr<INetdEventListener>& listener) {
+ std::lock_guard lock(mMutex);
+ return addListenerImplLocked(listener);
+}
+
+int ResolverEventReporter::addListenerImplLocked(
+ const std::shared_ptr<INetdEventListener>& listener) {
+ if (listener == nullptr) {
+ LOG(ERROR) << "The listener should not be null";
+ return -EINVAL;
+ }
+
+ // TODO: Perhaps ignore the listener which comes from the same binder.
+ const auto& it = mListeners.find(listener);
+ if (it != mListeners.end()) {
+ LOG(WARNING) << "The listener was already subscribed";
+ return -EEXIST;
+ }
+
+ static AIBinder_DeathRecipient* deathRecipient = nullptr;
+ if (deathRecipient == nullptr) {
+ // The AIBinder_DeathRecipient object is used to manage all death recipients for multiple
+ // binder objects. It doesn't released because there should have at least one binder object
+ // from framework.
+ // TODO: Considering to remove death recipient for the binder object from framework because
+ // it doesn't need death recipient actually.
+ deathRecipient = AIBinder_DeathRecipient_new([](void* cookie) {
+ ResolverEventReporter::getInstance().handleBinderDied(cookie);
+ });
+ }
+
+ // Pass the raw binder pointer address to be the cookie of the death recipient. While the death
+ // notification is fired, the cookie is used for identifying which binder was died. Because
+ // the NDK binder doesn't pass dead binder pointer to binder death handler, the binder death
+ // handler can't know who was died via wp<IBinder>. The reason for wp<IBinder> is not passed
+ // is that NDK binder can't transform a wp<IBinder> to a wp<AIBinder> in some cases.
+ // See more information in b/128712772.
+ auto binder = listener->asBinder().get();
+ auto cookie = static_cast<void*>(listener.get()); // Used for dead binder identification.
+ binder_status_t status = AIBinder_linkToDeath(binder, deathRecipient, cookie);
+
+ if (STATUS_OK != status) {
+ LOG(ERROR) << "Failed to register death notification for INetdEventListener";
+ return -EAGAIN;
+ }
+
+ mListeners.insert(listener);
+ return 0;
+}
\ No newline at end of file
diff --git a/resolv/ResolverEventReporter.h b/resolv/ResolverEventReporter.h
new file mode 100644
index 0000000..eb0d9cf
--- /dev/null
+++ b/resolv/ResolverEventReporter.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#ifndef NETD_RESOLV_EVENT_REPORTER_H
+#define NETD_RESOLV_EVENT_REPORTER_H
+
+#include <set>
+
+#include <android-base/thread_annotations.h>
+
+#include "aidl/android/net/metrics/INetdEventListener.h"
+
+/*
+ * This class can be used to get the binder reference to the netd events listener service
+ * via stable runtime ABI which is achieved from libbinder_ndk. It also allows that
+ * register an event listener.
+ */
+class ResolverEventReporter {
+ public:
+ ResolverEventReporter(ResolverEventReporter const&) = delete;
+ ResolverEventReporter(ResolverEventReporter&&) = delete;
+ ResolverEventReporter& operator=(ResolverEventReporter const&) = delete;
+ ResolverEventReporter& operator=(ResolverEventReporter&&) = delete;
+
+ using ListenerSet = std::set<std::shared_ptr<aidl::android::net::metrics::INetdEventListener>>;
+
+ // Get the instance of the singleton ResolverEventReporter.
+ static ResolverEventReporter& getInstance();
+
+ // Return the binder from the singleton ResolverEventReporter. This method is threadsafe.
+ ListenerSet getListeners() const;
+
+ // Add the binder to the singleton ResolverEventReporter. This method is threadsafe.
+ int addListener(
+ const std::shared_ptr<aidl::android::net::metrics::INetdEventListener>& listener);
+
+ private:
+ ResolverEventReporter() = default;
+ ~ResolverEventReporter() = default;
+
+ void addDefaultListener() EXCLUDES(mMutex);
+ int addListenerImpl(
+ const std::shared_ptr<aidl::android::net::metrics::INetdEventListener>& listener)
+ EXCLUDES(mMutex);
+ int addListenerImplLocked(
+ const std::shared_ptr<aidl::android::net::metrics::INetdEventListener>& listener)
+ REQUIRES(mMutex);
+ ListenerSet getListenersImpl() const EXCLUDES(mMutex);
+ void handleBinderDied(const void* who) EXCLUDES(mMutex);
+
+ mutable std::mutex mMutex;
+ ListenerSet mListeners GUARDED_BY(mMutex);
+};
+
+#endif // NETD_RESOLV_EVENT_REPORTER_H
diff --git a/resolv/ResolverStats.h b/resolv/ResolverStats.h
new file mode 100644
index 0000000..9140af8
--- /dev/null
+++ b/resolv/ResolverStats.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#ifndef _RESOLVER_STATS_H_
+#define _RESOLVER_STATS_H_
+
+#include <time.h>
+
+namespace android {
+namespace net {
+
+struct ResolverStats {
+ // Offsets into the per-server resolver stats as encoded in vector<int32_t> stats of
+ // getResolverInfo() of Netd's binder interface. The stats are based on data reported by
+ // android_net_res_stats_get_info_for_net(), the usability is calculated by applying
+ // android_net_res_stats_get_usable_servers() to this data.
+ enum ResolverStatsOffsets {
+ STATS_SUCCESSES = 0, // # successes counted for this server
+ STATS_ERRORS, // # errors
+ STATS_TIMEOUTS, // # timeouts
+ STATS_INTERNAL_ERRORS, // # internal errors
+ STATS_RTT_AVG, // average round-trip-time
+ STATS_LAST_SAMPLE_TIME, // time in s when the last sample was recorded
+ STATS_USABLE, // whether the server is considered usable
+ STATS_COUNT // total count of integers in the per-server data
+ };
+
+ int successes{-1};
+ int errors{-1};
+ int timeouts{-1};
+ int internal_errors{-1};
+ int rtt_avg{-1};
+ time_t last_sample_time{0};
+ bool usable{false};
+
+ // Serialize the resolver stats to the end of |out|.
+ void encode(std::vector<int32_t>* out) const;
+
+ // Read the serialized resolverstats starting at |in[ofs]|.
+ ssize_t decode(const std::vector<int32_t>& in, ssize_t ofs);
+
+ // Serialize the contents of |stats| and append them to the end of |out|. Multiple arrays
+ // can be written to the same output vector in sequence, however, the corresponding call
+ // to decodeAll() will return the combined contents in one vector.
+ static void encodeAll(const std::vector<ResolverStats>& stats, std::vector<int32_t>* out);
+
+ // Decodes the serialized ResolverStats from |in| and appends them to stats.
+ static bool decodeAll(const std::vector<int32_t>& in, std::vector<ResolverStats>* stats);
+};
+
+inline void ResolverStats::encode(std::vector<int32_t>* out) const {
+ size_t ofs = out->size();
+ out->resize(ofs + STATS_COUNT);
+ int32_t* cur = &(*out)[ofs];
+ cur[STATS_SUCCESSES] = successes;
+ cur[STATS_ERRORS] = errors;
+ cur[STATS_TIMEOUTS] = timeouts;
+ cur[STATS_INTERNAL_ERRORS] = internal_errors;
+ cur[STATS_RTT_AVG] = rtt_avg;
+ cur[STATS_LAST_SAMPLE_TIME] = last_sample_time;
+ cur[STATS_USABLE] = usable;
+}
+
+// Read the serialized resolverstats starting at |in[ofs]|.
+inline ssize_t ResolverStats::decode(const std::vector<int32_t>& in, ssize_t ofs) {
+ if (ofs < 0 || static_cast<size_t>(ofs) + STATS_COUNT > in.size()) {
+ return -1;
+ }
+ const int32_t* cur = &in[ofs];
+ successes = cur[STATS_SUCCESSES];
+ errors = cur[STATS_ERRORS];
+ timeouts = cur[STATS_TIMEOUTS];
+ internal_errors = cur[STATS_INTERNAL_ERRORS];
+ rtt_avg = cur[STATS_RTT_AVG];
+ last_sample_time = cur[STATS_LAST_SAMPLE_TIME];
+ usable = cur[STATS_USABLE];
+ return ofs + STATS_COUNT;
+}
+
+inline void ResolverStats::encodeAll(const std::vector<ResolverStats>& stats,
+ std::vector<int32_t>* out) {
+ for (const auto& s : stats) {
+ s.encode(out);
+ }
+}
+
+// TODO: Replace with a better representation, e.g. a Parcelable.
+inline bool ResolverStats::decodeAll(const std::vector<int32_t>& in,
+ std::vector<ResolverStats>* stats) {
+ ssize_t size = in.size();
+ if (size % STATS_COUNT) {
+ return false;
+ }
+ stats->resize(size / STATS_COUNT);
+ ssize_t ofs = 0;
+ for (auto& s : *stats) {
+ ofs = s.decode(in, ofs);
+ if (ofs < 0) {
+ return false;
+ }
+ }
+ return true;
+}
+
+} // namespace net
+} // namespace android
+
+#endif /* _RESOLVER_STATS_H_ */
diff --git a/resolv/aidl/dnsresolver/1/android/net/IDnsResolver.aidl b/resolv/aidl/dnsresolver/1/android/net/IDnsResolver.aidl
new file mode 100644
index 0000000..c4b2b6d
--- /dev/null
+++ b/resolv/aidl/dnsresolver/1/android/net/IDnsResolver.aidl
@@ -0,0 +1,27 @@
+package android.net;
+interface IDnsResolver {
+ boolean isAlive();
+ void registerEventListener(android.net.metrics.INetdEventListener listener);
+ void setResolverConfiguration(in android.net.ResolverParamsParcel resolverParams);
+ void getResolverInfo(int netId, out @utf8InCpp String[] servers, out @utf8InCpp String[] domains, out @utf8InCpp String[] tlsServers, out int[] params, out int[] stats, out int[] wait_for_pending_req_timeout_count);
+ void startPrefix64Discovery(int netId);
+ void stopPrefix64Discovery(int netId);
+ @utf8InCpp String getPrefix64(int netId);
+ void createNetworkCache(int netId);
+ void destroyNetworkCache(int netId);
+ const int RESOLVER_PARAMS_SAMPLE_VALIDITY = 0;
+ const int RESOLVER_PARAMS_SUCCESS_THRESHOLD = 1;
+ const int RESOLVER_PARAMS_MIN_SAMPLES = 2;
+ const int RESOLVER_PARAMS_MAX_SAMPLES = 3;
+ const int RESOLVER_PARAMS_BASE_TIMEOUT_MSEC = 4;
+ const int RESOLVER_PARAMS_RETRY_COUNT = 5;
+ const int RESOLVER_PARAMS_COUNT = 6;
+ const int RESOLVER_STATS_SUCCESSES = 0;
+ const int RESOLVER_STATS_ERRORS = 1;
+ const int RESOLVER_STATS_TIMEOUTS = 2;
+ const int RESOLVER_STATS_INTERNAL_ERRORS = 3;
+ const int RESOLVER_STATS_RTT_AVG = 4;
+ const int RESOLVER_STATS_LAST_SAMPLE_TIME = 5;
+ const int RESOLVER_STATS_USABLE = 6;
+ const int RESOLVER_STATS_COUNT = 7;
+}
diff --git a/resolv/aidl/dnsresolver/1/android/net/ResolverParamsParcel.aidl b/resolv/aidl/dnsresolver/1/android/net/ResolverParamsParcel.aidl
new file mode 100644
index 0000000..b808bae
--- /dev/null
+++ b/resolv/aidl/dnsresolver/1/android/net/ResolverParamsParcel.aidl
@@ -0,0 +1,15 @@
+package android.net;
+parcelable ResolverParamsParcel {
+ int netId;
+ int sampleValiditySeconds;
+ int successThreshold;
+ int minSamples;
+ int maxSamples;
+ int baseTimeoutMsec;
+ int retryCount;
+ @utf8InCpp String[] servers;
+ @utf8InCpp String[] domains;
+ @utf8InCpp String tlsName;
+ @utf8InCpp String[] tlsServers;
+ @utf8InCpp String[] tlsFingerprints;
+}
diff --git a/resolv/aidl/dnsresolver/2/android/net/IDnsResolver.aidl b/resolv/aidl/dnsresolver/2/android/net/IDnsResolver.aidl
new file mode 100644
index 0000000..846bbec
--- /dev/null
+++ b/resolv/aidl/dnsresolver/2/android/net/IDnsResolver.aidl
@@ -0,0 +1,50 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not
+// try to edit this file. It looks like you are doing that because you have
+// modified an AIDL interface in a backward-incompatible way, e.g., deleting a
+// function from an interface or a field from a parcelable and it broke the
+// build. That breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+interface IDnsResolver {
+ boolean isAlive();
+ void registerEventListener(android.net.metrics.INetdEventListener listener);
+ void setResolverConfiguration(in android.net.ResolverParamsParcel resolverParams);
+ void getResolverInfo(int netId, out @utf8InCpp String[] servers, out @utf8InCpp String[] domains, out @utf8InCpp String[] tlsServers, out int[] params, out int[] stats, out int[] wait_for_pending_req_timeout_count);
+ void startPrefix64Discovery(int netId);
+ void stopPrefix64Discovery(int netId);
+ @utf8InCpp String getPrefix64(int netId);
+ void createNetworkCache(int netId);
+ void destroyNetworkCache(int netId);
+ void setLogSeverity(int logSeverity);
+ const int RESOLVER_PARAMS_SAMPLE_VALIDITY = 0;
+ const int RESOLVER_PARAMS_SUCCESS_THRESHOLD = 1;
+ const int RESOLVER_PARAMS_MIN_SAMPLES = 2;
+ const int RESOLVER_PARAMS_MAX_SAMPLES = 3;
+ const int RESOLVER_PARAMS_BASE_TIMEOUT_MSEC = 4;
+ const int RESOLVER_PARAMS_RETRY_COUNT = 5;
+ const int RESOLVER_PARAMS_COUNT = 6;
+ const int RESOLVER_STATS_SUCCESSES = 0;
+ const int RESOLVER_STATS_ERRORS = 1;
+ const int RESOLVER_STATS_TIMEOUTS = 2;
+ const int RESOLVER_STATS_INTERNAL_ERRORS = 3;
+ const int RESOLVER_STATS_RTT_AVG = 4;
+ const int RESOLVER_STATS_LAST_SAMPLE_TIME = 5;
+ const int RESOLVER_STATS_USABLE = 6;
+ const int RESOLVER_STATS_COUNT = 7;
+ const int DNS_RESOLVER_LOG_VERBOSE = 0;
+ const int DNS_RESOLVER_LOG_DEBUG = 1;
+ const int DNS_RESOLVER_LOG_INFO = 2;
+ const int DNS_RESOLVER_LOG_WARNING = 3;
+ const int DNS_RESOLVER_LOG_ERROR = 4;
+}
diff --git a/resolv/aidl/dnsresolver/2/android/net/ResolverParamsParcel.aidl b/resolv/aidl/dnsresolver/2/android/net/ResolverParamsParcel.aidl
new file mode 100644
index 0000000..a37f938
--- /dev/null
+++ b/resolv/aidl/dnsresolver/2/android/net/ResolverParamsParcel.aidl
@@ -0,0 +1,32 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not
+// try to edit this file. It looks like you are doing that because you have
+// modified an AIDL interface in a backward-incompatible way, e.g., deleting a
+// function from an interface or a field from a parcelable and it broke the
+// build. That breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+parcelable ResolverParamsParcel {
+ int netId;
+ int sampleValiditySeconds;
+ int successThreshold;
+ int minSamples;
+ int maxSamples;
+ int baseTimeoutMsec;
+ int retryCount;
+ @utf8InCpp String[] servers;
+ @utf8InCpp String[] domains;
+ @utf8InCpp String tlsName;
+ @utf8InCpp String[] tlsServers;
+ @utf8InCpp String[] tlsFingerprints;
+}
diff --git a/resolv/binder/android/net/IDnsResolver.aidl b/resolv/binder/android/net/IDnsResolver.aidl
new file mode 100644
index 0000000..f6f4092
--- /dev/null
+++ b/resolv/binder/android/net/IDnsResolver.aidl
@@ -0,0 +1,164 @@
+/**
+ * Copyright (c) 2019, 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.
+ */
+
+package android.net;
+
+import android.net.ResolverParamsParcel;
+import android.net.metrics.INetdEventListener;
+
+/** {@hide} */
+interface IDnsResolver {
+ /**
+ * Returns true if the service is responding.
+ */
+ boolean isAlive();
+
+ /**
+ * Register event listener
+ * DnsResolver supports multiple event listeners, but only one per unique address of the
+ * binder interface. A newer listener won't be registered if DnsResolver has an old one on
+ * the same address of the binder interface.
+ *
+ * @param listener event listener to register.
+ * @throws ServiceSpecificException in case of failure, with an error code corresponding to the
+ * unix errno.
+ */
+ void registerEventListener(INetdEventListener listener);
+
+ // TODO: Delete these from the public interface
+ // Array indices for resolver parameters.
+ const int RESOLVER_PARAMS_SAMPLE_VALIDITY = 0;
+ const int RESOLVER_PARAMS_SUCCESS_THRESHOLD = 1;
+ const int RESOLVER_PARAMS_MIN_SAMPLES = 2;
+ const int RESOLVER_PARAMS_MAX_SAMPLES = 3;
+ const int RESOLVER_PARAMS_BASE_TIMEOUT_MSEC = 4;
+ const int RESOLVER_PARAMS_RETRY_COUNT = 5;
+ const int RESOLVER_PARAMS_COUNT = 6;
+
+ /**
+ * Sets the name servers, search domains and resolver params for the given network. Flushes the
+ * cache as needed (i.e. when the servers or the number of samples to store changes).
+ *
+ * @param resolverParams the resolver parameters to be wrapped into parcel.
+ * @throws ServiceSpecificException in case of failure, with an error code corresponding to the
+ * unix errno.
+ */
+ void setResolverConfiguration(in ResolverParamsParcel resolverParams);
+
+ // Array indices for resolver stats.
+ const int RESOLVER_STATS_SUCCESSES = 0;
+ const int RESOLVER_STATS_ERRORS = 1;
+ const int RESOLVER_STATS_TIMEOUTS = 2;
+ const int RESOLVER_STATS_INTERNAL_ERRORS = 3;
+ const int RESOLVER_STATS_RTT_AVG = 4;
+ const int RESOLVER_STATS_LAST_SAMPLE_TIME = 5;
+ const int RESOLVER_STATS_USABLE = 6;
+ const int RESOLVER_STATS_COUNT = 7;
+
+ /**
+ * Retrieves the name servers, search domains and resolver stats associated with the given
+ * network ID.
+ *
+ * @param netId the network ID of the network for which information should be retrieved.
+ * @param servers the DNS servers that are currently configured for the network.
+ * @param domains the search domains currently configured.
+ * @param tlsServers the DNS-over-TLS servers that are currently configured for the network.
+ * @param params the resolver parameters configured, i.e. the contents of __res_params in order.
+ * @param stats the stats for each server in the order specified by RESOLVER_STATS_XXX
+ * constants, serialized as an int array. The contents of this array are the number of
+ * <ul>
+ * <li> successes,
+ * <li> errors,
+ * <li> timeouts,
+ * <li> internal errors,
+ * <li> the RTT average,
+ * <li> the time of the last recorded sample,
+ * <li> and an integer indicating whether the server is usable (1) or broken (0).
+ * </ul>
+ * in this order. For example, the timeout counter for server N is stored at position
+ * RESOLVER_STATS_COUNT*N + RESOLVER_STATS_TIMEOUTS
+ * @param wait_for_pending_req_timeout_count an internal counter used to count the number of
+ * timeouts while resolver is handling concurrent DNS queries on the same hostname.
+ * @throws ServiceSpecificException in case of failure, with an error code corresponding to the
+ * unix errno.
+ *
+ * TODO: Consider replacing stats and params with parcelables.
+ */
+ void getResolverInfo(int netId, out @utf8InCpp String[] servers,
+ out @utf8InCpp String[] domains, out @utf8InCpp String[] tlsServers, out int[] params,
+ out int[] stats, out int[] wait_for_pending_req_timeout_count);
+
+ /**
+ * Starts NAT64 prefix discovery on the given network.
+ *
+ * @param netId the netId to start prefix discovery on.
+ */
+ void startPrefix64Discovery(int netId);
+
+ /**
+ * Stops NAT64 prefix discovery on the given network.
+ *
+ * @param netId the netId to stop prefix discovery on.
+ */
+ void stopPrefix64Discovery(int netId);
+
+ /**
+ * Get NAT64 prefix in format Pref64::/n which is described in RFC6147 section 2. This
+ * interface is used for internal test only. Don't use it for other purposes because doing so
+ * would cause race conditions with the NAT64 prefix notifications.
+ *
+ * @param netId the network ID of the network to get the prefix
+ * @return the NAT64 prefix if the query operation was successful
+ * @throws ServiceSpecificException in case of failure, with an error code indicating the
+ * cause of the the failure.
+ *
+ * TODO: Remove this once the tests have been updated to listen for onNat64PrefixEvent.
+ */
+ @utf8InCpp String getPrefix64(int netId);
+
+ /**
+ * Create cache for the given network.
+ *
+ * @param netId the network ID of the network to create.
+ * @throws ServiceSpecificException in case of failure, with an error code indicating the
+ * cause of the failure.
+ */
+ void createNetworkCache(int netId);
+
+ /**
+ * Destroy cache for the given network.
+ *
+ * @param netId the network ID of the network to destroy.
+ */
+ void destroyNetworkCache(int netId);
+
+ // Refer to enum LogSeverity from system/core/base/include/android-base/logging.h
+ const int DNS_RESOLVER_LOG_VERBOSE = 0;
+ const int DNS_RESOLVER_LOG_DEBUG = 1;
+ const int DNS_RESOLVER_LOG_INFO = 2;
+ const int DNS_RESOLVER_LOG_WARNING = 3;
+ const int DNS_RESOLVER_LOG_ERROR = 4;
+
+ /**
+ * Set DNS resolver log severity
+ *
+ * @param logSeverity print log in "VERBOSE", "DEBUG", "INFO", "WARNING", "ERROR".
+ *
+ * @throws ServiceSpecificException in case of failure, with an error code corresponding to the
+ * POSIX errno.
+ */
+ void setLogSeverity(int logSeverity);
+}
diff --git a/resolv/binder/android/net/ResolverParamsParcel.aidl b/resolv/binder/android/net/ResolverParamsParcel.aidl
new file mode 100644
index 0000000..d25880f
--- /dev/null
+++ b/resolv/binder/android/net/ResolverParamsParcel.aidl
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+package android.net;
+
+/**
+ * Configuration for a resolver parameters.
+ *
+ * {@hide}
+ */
+parcelable ResolverParamsParcel {
+ /**
+ * The network ID of the network for which information should be configured.
+ */
+ int netId;
+
+ /**
+ * Sample lifetime in seconds.
+ */
+ int sampleValiditySeconds;
+
+ /**
+ * Use to judge if the server is considered broken.
+ */
+ int successThreshold;
+
+ /**
+ * Min. # samples.
+ */
+ int minSamples;
+
+ /**
+ * Max # samples.
+ */
+ int maxSamples;
+
+ /**
+ * Retransmission interval in milliseconds.
+ */
+ int baseTimeoutMsec;
+
+ /**
+ * Number of retries.
+ */
+ int retryCount;
+
+ /**
+ * The DNS servers to configure for the network.
+ */
+ @utf8InCpp String[] servers;
+
+ /**
+ * The search domains to configure.
+ */
+ @utf8InCpp String[] domains;
+
+ /**
+ * The TLS subject name to require for all servers, or empty if there is none.
+ */
+ @utf8InCpp String tlsName;
+
+ /**
+ * The DNS servers to configure for strict mode Private DNS.
+ */
+ @utf8InCpp String[] tlsServers;
+
+ /**
+ * An array containing TLS public key fingerprints (pins) of which each server must match
+ * at least one, or empty if there are no pinned keys.
+ */
+ // DEPRECATED: remove tlsFingerprints in new code
+ @utf8InCpp String[] tlsFingerprints;
+}
diff --git a/resolv/dns_responder/Android.bp b/resolv/dns_responder/Android.bp
new file mode 100644
index 0000000..4c28c21
--- /dev/null
+++ b/resolv/dns_responder/Android.bp
@@ -0,0 +1,24 @@
+cc_library_static {
+ name: "libnetd_test_dnsresponder",
+ defaults: ["netd_defaults"],
+ shared_libs: [
+ "libbase",
+ "libbinder",
+ "liblog",
+ "libnetd_client",
+ "libnetdutils",
+ "libssl",
+ "dnsresolver_aidl_interface-V2-cpp",
+ "netd_aidl_interface-V2-cpp",
+ ],
+ static_libs: ["libutils"],
+ include_dirs: [
+ "system/netd/server",
+ ],
+ srcs: [
+ "dns_responder.cpp",
+ "dns_responder_client.cpp",
+ "dns_tls_frontend.cpp",
+ ],
+ export_include_dirs: ["."],
+}
diff --git a/resolv/dns_responder/dns_responder.cpp b/resolv/dns_responder/dns_responder.cpp
new file mode 100644
index 0000000..5047255
--- /dev/null
+++ b/resolv/dns_responder/dns_responder.cpp
@@ -0,0 +1,994 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#include "dns_responder.h"
+
+#include <arpa/inet.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/epoll.h>
+#include <sys/eventfd.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <set>
+
+#include <iostream>
+#include <vector>
+
+#define LOG_TAG "DNSResponder"
+#include <android-base/strings.h>
+#include <log/log.h>
+#include <netdutils/SocketOption.h>
+
+#include "NetdConstants.h"
+
+using android::netdutils::enableSockopt;
+
+namespace test {
+
+std::string errno2str() {
+ char error_msg[512] = { 0 };
+ // It actually calls __gnu_strerror_r() which returns the type |char*| rather than |int|.
+ // PLOG is an option though it requires lots of changes from ALOGx() to LOG(x).
+ return strerror_r(errno, error_msg, sizeof(error_msg));
+}
+
+#define APLOGI(fmt, ...) ALOGI(fmt ": [%d] %s", __VA_ARGS__, errno, errno2str().c_str())
+
+#if 0
+#define DBGLOG(fmt, ...) ALOGI(fmt, __VA_ARGS__)
+#else
+#define DBGLOG(fmt, ...)
+#endif
+
+std::string str2hex(const char* buffer, size_t len) {
+ std::string str(len*2, '\0');
+ for (size_t i = 0 ; i < len ; ++i) {
+ static const char* hex = "0123456789ABCDEF";
+ uint8_t c = buffer[i];
+ str[i*2] = hex[c >> 4];
+ str[i*2 + 1] = hex[c & 0x0F];
+ }
+ return str;
+}
+
+std::string addr2str(const sockaddr* sa, socklen_t sa_len) {
+ char host_str[NI_MAXHOST] = { 0 };
+ int rv = getnameinfo(sa, sa_len, host_str, sizeof(host_str), nullptr, 0,
+ NI_NUMERICHOST);
+ if (rv == 0) return std::string(host_str);
+ return std::string();
+}
+
+/* DNS struct helpers */
+
+const char* dnstype2str(unsigned dnstype) {
+ static std::unordered_map<unsigned, const char*> kTypeStrs = {
+ { ns_type::ns_t_a, "A" },
+ { ns_type::ns_t_ns, "NS" },
+ { ns_type::ns_t_md, "MD" },
+ { ns_type::ns_t_mf, "MF" },
+ { ns_type::ns_t_cname, "CNAME" },
+ { ns_type::ns_t_soa, "SOA" },
+ { ns_type::ns_t_mb, "MB" },
+ { ns_type::ns_t_mb, "MG" },
+ { ns_type::ns_t_mr, "MR" },
+ { ns_type::ns_t_null, "NULL" },
+ { ns_type::ns_t_wks, "WKS" },
+ { ns_type::ns_t_ptr, "PTR" },
+ { ns_type::ns_t_hinfo, "HINFO" },
+ { ns_type::ns_t_minfo, "MINFO" },
+ { ns_type::ns_t_mx, "MX" },
+ { ns_type::ns_t_txt, "TXT" },
+ { ns_type::ns_t_rp, "RP" },
+ { ns_type::ns_t_afsdb, "AFSDB" },
+ { ns_type::ns_t_x25, "X25" },
+ { ns_type::ns_t_isdn, "ISDN" },
+ { ns_type::ns_t_rt, "RT" },
+ { ns_type::ns_t_nsap, "NSAP" },
+ { ns_type::ns_t_nsap_ptr, "NSAP-PTR" },
+ { ns_type::ns_t_sig, "SIG" },
+ { ns_type::ns_t_key, "KEY" },
+ { ns_type::ns_t_px, "PX" },
+ { ns_type::ns_t_gpos, "GPOS" },
+ { ns_type::ns_t_aaaa, "AAAA" },
+ { ns_type::ns_t_loc, "LOC" },
+ { ns_type::ns_t_nxt, "NXT" },
+ { ns_type::ns_t_eid, "EID" },
+ { ns_type::ns_t_nimloc, "NIMLOC" },
+ { ns_type::ns_t_srv, "SRV" },
+ { ns_type::ns_t_naptr, "NAPTR" },
+ { ns_type::ns_t_kx, "KX" },
+ { ns_type::ns_t_cert, "CERT" },
+ { ns_type::ns_t_a6, "A6" },
+ { ns_type::ns_t_dname, "DNAME" },
+ { ns_type::ns_t_sink, "SINK" },
+ { ns_type::ns_t_opt, "OPT" },
+ { ns_type::ns_t_apl, "APL" },
+ { ns_type::ns_t_tkey, "TKEY" },
+ { ns_type::ns_t_tsig, "TSIG" },
+ { ns_type::ns_t_ixfr, "IXFR" },
+ { ns_type::ns_t_axfr, "AXFR" },
+ { ns_type::ns_t_mailb, "MAILB" },
+ { ns_type::ns_t_maila, "MAILA" },
+ { ns_type::ns_t_any, "ANY" },
+ { ns_type::ns_t_zxfr, "ZXFR" },
+ };
+ auto it = kTypeStrs.find(dnstype);
+ static const char* kUnknownStr{ "UNKNOWN" };
+ if (it == kTypeStrs.end()) return kUnknownStr;
+ return it->second;
+}
+
+const char* dnsclass2str(unsigned dnsclass) {
+ static std::unordered_map<unsigned, const char*> kClassStrs = {
+ { ns_class::ns_c_in , "Internet" },
+ { 2, "CSNet" },
+ { ns_class::ns_c_chaos, "ChaosNet" },
+ { ns_class::ns_c_hs, "Hesiod" },
+ { ns_class::ns_c_none, "none" },
+ { ns_class::ns_c_any, "any" }
+ };
+ auto it = kClassStrs.find(dnsclass);
+ static const char* kUnknownStr{ "UNKNOWN" };
+ if (it == kClassStrs.end()) return kUnknownStr;
+ return it->second;
+}
+
+struct DNSName {
+ std::string name;
+ const char* read(const char* buffer, const char* buffer_end);
+ char* write(char* buffer, const char* buffer_end) const;
+ const char* toString() const;
+private:
+ const char* parseField(const char* buffer, const char* buffer_end,
+ bool* last);
+};
+
+const char* DNSName::toString() const {
+ return name.c_str();
+}
+
+const char* DNSName::read(const char* buffer, const char* buffer_end) {
+ const char* cur = buffer;
+ bool last = false;
+ do {
+ cur = parseField(cur, buffer_end, &last);
+ if (cur == nullptr) {
+ ALOGI("parsing failed at line %d", __LINE__);
+ return nullptr;
+ }
+ } while (!last);
+ return cur;
+}
+
+char* DNSName::write(char* buffer, const char* buffer_end) const {
+ char* buffer_cur = buffer;
+ for (size_t pos = 0 ; pos < name.size() ; ) {
+ size_t dot_pos = name.find('.', pos);
+ if (dot_pos == std::string::npos) {
+ // Sanity check, should never happen unless parseField is broken.
+ ALOGI("logic error: all names are expected to end with a '.'");
+ return nullptr;
+ }
+ size_t len = dot_pos - pos;
+ if (len >= 256) {
+ ALOGI("name component '%s' is %zu long, but max is 255",
+ name.substr(pos, dot_pos - pos).c_str(), len);
+ return nullptr;
+ }
+ if (buffer_cur + sizeof(uint8_t) + len > buffer_end) {
+ ALOGI("buffer overflow at line %d", __LINE__);
+ return nullptr;
+ }
+ *buffer_cur++ = len;
+ buffer_cur = std::copy(std::next(name.begin(), pos),
+ std::next(name.begin(), dot_pos),
+ buffer_cur);
+ pos = dot_pos + 1;
+ }
+ // Write final zero.
+ *buffer_cur++ = 0;
+ return buffer_cur;
+}
+
+const char* DNSName::parseField(const char* buffer, const char* buffer_end,
+ bool* last) {
+ if (buffer + sizeof(uint8_t) > buffer_end) {
+ ALOGI("parsing failed at line %d", __LINE__);
+ return nullptr;
+ }
+ unsigned field_type = *buffer >> 6;
+ unsigned ofs = *buffer & 0x3F;
+ const char* cur = buffer + sizeof(uint8_t);
+ if (field_type == 0) {
+ // length + name component
+ if (ofs == 0) {
+ *last = true;
+ return cur;
+ }
+ if (cur + ofs > buffer_end) {
+ ALOGI("parsing failed at line %d", __LINE__);
+ return nullptr;
+ }
+ name.append(cur, ofs);
+ name.push_back('.');
+ return cur + ofs;
+ } else if (field_type == 3) {
+ ALOGI("name compression not implemented");
+ return nullptr;
+ }
+ ALOGI("invalid name field type");
+ return nullptr;
+}
+
+struct DNSQuestion {
+ DNSName qname;
+ unsigned qtype;
+ unsigned qclass;
+ const char* read(const char* buffer, const char* buffer_end);
+ char* write(char* buffer, const char* buffer_end) const;
+ std::string toString() const;
+};
+
+const char* DNSQuestion::read(const char* buffer, const char* buffer_end) {
+ const char* cur = qname.read(buffer, buffer_end);
+ if (cur == nullptr) {
+ ALOGI("parsing failed at line %d", __LINE__);
+ return nullptr;
+ }
+ if (cur + 2*sizeof(uint16_t) > buffer_end) {
+ ALOGI("parsing failed at line %d", __LINE__);
+ return nullptr;
+ }
+ qtype = ntohs(*reinterpret_cast<const uint16_t*>(cur));
+ qclass = ntohs(*reinterpret_cast<const uint16_t*>(cur + sizeof(uint16_t)));
+ return cur + 2*sizeof(uint16_t);
+}
+
+char* DNSQuestion::write(char* buffer, const char* buffer_end) const {
+ char* buffer_cur = qname.write(buffer, buffer_end);
+ if (buffer_cur == nullptr) return nullptr;
+ if (buffer_cur + 2*sizeof(uint16_t) > buffer_end) {
+ ALOGI("buffer overflow on line %d", __LINE__);
+ return nullptr;
+ }
+ *reinterpret_cast<uint16_t*>(buffer_cur) = htons(qtype);
+ *reinterpret_cast<uint16_t*>(buffer_cur + sizeof(uint16_t)) =
+ htons(qclass);
+ return buffer_cur + 2*sizeof(uint16_t);
+}
+
+std::string DNSQuestion::toString() const {
+ char buffer[4096];
+ int len = snprintf(buffer, sizeof(buffer), "Q<%s,%s,%s>", qname.toString(),
+ dnstype2str(qtype), dnsclass2str(qclass));
+ return std::string(buffer, len);
+}
+
+struct DNSRecord {
+ DNSName name;
+ unsigned rtype;
+ unsigned rclass;
+ unsigned ttl;
+ std::vector<char> rdata;
+ const char* read(const char* buffer, const char* buffer_end);
+ char* write(char* buffer, const char* buffer_end) const;
+ std::string toString() const;
+private:
+ struct IntFields {
+ uint16_t rtype;
+ uint16_t rclass;
+ uint32_t ttl;
+ uint16_t rdlen;
+ } __attribute__((__packed__));
+
+ const char* readIntFields(const char* buffer, const char* buffer_end,
+ unsigned* rdlen);
+ char* writeIntFields(unsigned rdlen, char* buffer,
+ const char* buffer_end) const;
+};
+
+const char* DNSRecord::read(const char* buffer, const char* buffer_end) {
+ const char* cur = name.read(buffer, buffer_end);
+ if (cur == nullptr) {
+ ALOGI("parsing failed at line %d", __LINE__);
+ return nullptr;
+ }
+ unsigned rdlen = 0;
+ cur = readIntFields(cur, buffer_end, &rdlen);
+ if (cur == nullptr) {
+ ALOGI("parsing failed at line %d", __LINE__);
+ return nullptr;
+ }
+ if (cur + rdlen > buffer_end) {
+ ALOGI("parsing failed at line %d", __LINE__);
+ return nullptr;
+ }
+ rdata.assign(cur, cur + rdlen);
+ return cur + rdlen;
+}
+
+char* DNSRecord::write(char* buffer, const char* buffer_end) const {
+ char* buffer_cur = name.write(buffer, buffer_end);
+ if (buffer_cur == nullptr) return nullptr;
+ buffer_cur = writeIntFields(rdata.size(), buffer_cur, buffer_end);
+ if (buffer_cur == nullptr) return nullptr;
+ if (buffer_cur + rdata.size() > buffer_end) {
+ ALOGI("buffer overflow on line %d", __LINE__);
+ return nullptr;
+ }
+ return std::copy(rdata.begin(), rdata.end(), buffer_cur);
+}
+
+std::string DNSRecord::toString() const {
+ char buffer[4096];
+ int len = snprintf(buffer, sizeof(buffer), "R<%s,%s,%s>", name.toString(),
+ dnstype2str(rtype), dnsclass2str(rclass));
+ return std::string(buffer, len);
+}
+
+const char* DNSRecord::readIntFields(const char* buffer, const char* buffer_end,
+ unsigned* rdlen) {
+ if (buffer + sizeof(IntFields) > buffer_end ) {
+ ALOGI("parsing failed at line %d", __LINE__);
+ return nullptr;
+ }
+ const auto& intfields = *reinterpret_cast<const IntFields*>(buffer);
+ rtype = ntohs(intfields.rtype);
+ rclass = ntohs(intfields.rclass);
+ ttl = ntohl(intfields.ttl);
+ *rdlen = ntohs(intfields.rdlen);
+ return buffer + sizeof(IntFields);
+}
+
+char* DNSRecord::writeIntFields(unsigned rdlen, char* buffer,
+ const char* buffer_end) const {
+ if (buffer + sizeof(IntFields) > buffer_end ) {
+ ALOGI("buffer overflow on line %d", __LINE__);
+ return nullptr;
+ }
+ auto& intfields = *reinterpret_cast<IntFields*>(buffer);
+ intfields.rtype = htons(rtype);
+ intfields.rclass = htons(rclass);
+ intfields.ttl = htonl(ttl);
+ intfields.rdlen = htons(rdlen);
+ return buffer + sizeof(IntFields);
+}
+
+struct DNSHeader {
+ unsigned id;
+ bool ra;
+ uint8_t rcode;
+ bool qr;
+ uint8_t opcode;
+ bool aa;
+ bool tr;
+ bool rd;
+ bool ad;
+ std::vector<DNSQuestion> questions;
+ std::vector<DNSRecord> answers;
+ std::vector<DNSRecord> authorities;
+ std::vector<DNSRecord> additionals;
+ const char* read(const char* buffer, const char* buffer_end);
+ char* write(char* buffer, const char* buffer_end) const;
+ std::string toString() const;
+
+private:
+ struct Header {
+ uint16_t id;
+ uint8_t flags0;
+ uint8_t flags1;
+ uint16_t qdcount;
+ uint16_t ancount;
+ uint16_t nscount;
+ uint16_t arcount;
+ } __attribute__((__packed__));
+
+ const char* readHeader(const char* buffer, const char* buffer_end,
+ unsigned* qdcount, unsigned* ancount,
+ unsigned* nscount, unsigned* arcount);
+};
+
+const char* DNSHeader::read(const char* buffer, const char* buffer_end) {
+ unsigned qdcount;
+ unsigned ancount;
+ unsigned nscount;
+ unsigned arcount;
+ const char* cur = readHeader(buffer, buffer_end, &qdcount, &ancount,
+ &nscount, &arcount);
+ if (cur == nullptr) {
+ ALOGI("parsing failed at line %d", __LINE__);
+ return nullptr;
+ }
+ if (qdcount) {
+ questions.resize(qdcount);
+ for (unsigned i = 0 ; i < qdcount ; ++i) {
+ cur = questions[i].read(cur, buffer_end);
+ if (cur == nullptr) {
+ ALOGI("parsing failed at line %d", __LINE__);
+ return nullptr;
+ }
+ }
+ }
+ if (ancount) {
+ answers.resize(ancount);
+ for (unsigned i = 0 ; i < ancount ; ++i) {
+ cur = answers[i].read(cur, buffer_end);
+ if (cur == nullptr) {
+ ALOGI("parsing failed at line %d", __LINE__);
+ return nullptr;
+ }
+ }
+ }
+ if (nscount) {
+ authorities.resize(nscount);
+ for (unsigned i = 0 ; i < nscount ; ++i) {
+ cur = authorities[i].read(cur, buffer_end);
+ if (cur == nullptr) {
+ ALOGI("parsing failed at line %d", __LINE__);
+ return nullptr;
+ }
+ }
+ }
+ if (arcount) {
+ additionals.resize(arcount);
+ for (unsigned i = 0 ; i < arcount ; ++i) {
+ cur = additionals[i].read(cur, buffer_end);
+ if (cur == nullptr) {
+ ALOGI("parsing failed at line %d", __LINE__);
+ return nullptr;
+ }
+ }
+ }
+ return cur;
+}
+
+char* DNSHeader::write(char* buffer, const char* buffer_end) const {
+ if (buffer + sizeof(Header) > buffer_end) {
+ ALOGI("buffer overflow on line %d", __LINE__);
+ return nullptr;
+ }
+ Header& header = *reinterpret_cast<Header*>(buffer);
+ // bytes 0-1
+ header.id = htons(id);
+ // byte 2: 7:qr, 3-6:opcode, 2:aa, 1:tr, 0:rd
+ header.flags0 = (qr << 7) | (opcode << 3) | (aa << 2) | (tr << 1) | rd;
+ // byte 3: 7:ra, 6:zero, 5:ad, 4:cd, 0-3:rcode
+ // Fake behavior: if the query set the "ad" bit, set it in the response too.
+ // In a real server, this should be set only if the data is authentic and the
+ // query contained an "ad" bit or DNSSEC extensions.
+ header.flags1 = (ad << 5) | rcode;
+ // rest of header
+ header.qdcount = htons(questions.size());
+ header.ancount = htons(answers.size());
+ header.nscount = htons(authorities.size());
+ header.arcount = htons(additionals.size());
+ char* buffer_cur = buffer + sizeof(Header);
+ for (const DNSQuestion& question : questions) {
+ buffer_cur = question.write(buffer_cur, buffer_end);
+ if (buffer_cur == nullptr) return nullptr;
+ }
+ for (const DNSRecord& answer : answers) {
+ buffer_cur = answer.write(buffer_cur, buffer_end);
+ if (buffer_cur == nullptr) return nullptr;
+ }
+ for (const DNSRecord& authority : authorities) {
+ buffer_cur = authority.write(buffer_cur, buffer_end);
+ if (buffer_cur == nullptr) return nullptr;
+ }
+ for (const DNSRecord& additional : additionals) {
+ buffer_cur = additional.write(buffer_cur, buffer_end);
+ if (buffer_cur == nullptr) return nullptr;
+ }
+ return buffer_cur;
+}
+
+std::string DNSHeader::toString() const {
+ // TODO
+ return std::string();
+}
+
+const char* DNSHeader::readHeader(const char* buffer, const char* buffer_end,
+ unsigned* qdcount, unsigned* ancount,
+ unsigned* nscount, unsigned* arcount) {
+ if (buffer + sizeof(Header) > buffer_end)
+ return nullptr;
+ const auto& header = *reinterpret_cast<const Header*>(buffer);
+ // bytes 0-1
+ id = ntohs(header.id);
+ // byte 2: 7:qr, 3-6:opcode, 2:aa, 1:tr, 0:rd
+ qr = header.flags0 >> 7;
+ opcode = (header.flags0 >> 3) & 0x0F;
+ aa = (header.flags0 >> 2) & 1;
+ tr = (header.flags0 >> 1) & 1;
+ rd = header.flags0 & 1;
+ // byte 3: 7:ra, 6:zero, 5:ad, 4:cd, 0-3:rcode
+ ra = header.flags1 >> 7;
+ ad = (header.flags1 >> 5) & 1;
+ rcode = header.flags1 & 0xF;
+ // rest of header
+ *qdcount = ntohs(header.qdcount);
+ *ancount = ntohs(header.ancount);
+ *nscount = ntohs(header.nscount);
+ *arcount = ntohs(header.arcount);
+ return buffer + sizeof(Header);
+}
+
+/* DNS responder */
+
+DNSResponder::DNSResponder(std::string listen_address, std::string listen_service,
+ int poll_timeout_ms, ns_rcode error_rcode)
+ : listen_address_(std::move(listen_address)),
+ listen_service_(std::move(listen_service)),
+ poll_timeout_ms_(poll_timeout_ms),
+ error_rcode_(error_rcode) {}
+
+DNSResponder::~DNSResponder() {
+ stopServer();
+}
+
+void DNSResponder::addMapping(const std::string& name, ns_type type, const std::string& addr) {
+ std::lock_guard lock(mappings_mutex_);
+ auto it = mappings_.find(QueryKey(name, type));
+ if (it != mappings_.end()) {
+ ALOGI("Overwriting mapping for (%s, %s), previous address %s, new "
+ "address %s",
+ name.c_str(), dnstype2str(type), it->second.c_str(), addr.c_str());
+ it->second = addr;
+ return;
+ }
+ mappings_.try_emplace({name, type}, addr);
+}
+
+void DNSResponder::removeMapping(const std::string& name, ns_type type) {
+ std::lock_guard lock(mappings_mutex_);
+ auto it = mappings_.find(QueryKey(name, type));
+ if (it != mappings_.end()) {
+ ALOGI("Cannot remove mapping mapping from (%s, %s), not present", name.c_str(),
+ dnstype2str(type));
+ return;
+ }
+ mappings_.erase(it);
+}
+
+void DNSResponder::setResponseProbability(double response_probability) {
+ response_probability_ = response_probability;
+}
+
+void DNSResponder::setEdns(Edns edns) {
+ edns_ = edns;
+}
+
+bool DNSResponder::running() const {
+ return socket_.get() != -1;
+}
+
+bool DNSResponder::startServer() {
+ if (running()) {
+ ALOGI("server already running");
+ return false;
+ }
+
+ // Set up UDP socket.
+ addrinfo ai_hints{
+ .ai_family = AF_UNSPEC,
+ .ai_socktype = SOCK_DGRAM,
+ .ai_flags = AI_PASSIVE
+ };
+ addrinfo* ai_res = nullptr;
+ int rv = getaddrinfo(listen_address_.c_str(), listen_service_.c_str(),
+ &ai_hints, &ai_res);
+ ScopedAddrinfo ai_res_cleanup(ai_res);
+ if (rv) {
+ ALOGI("getaddrinfo(%s, %s) failed: %s", listen_address_.c_str(),
+ listen_service_.c_str(), gai_strerror(rv));
+ return false;
+ }
+ for (const addrinfo* ai = ai_res ; ai ; ai = ai->ai_next) {
+ socket_.reset(socket(ai->ai_family, ai->ai_socktype | SOCK_NONBLOCK, ai->ai_protocol));
+ if (socket_.get() < 0) {
+ APLOGI("ignore creating socket %d failed", socket_.get());
+ continue;
+ }
+ enableSockopt(socket_.get(), SOL_SOCKET, SO_REUSEPORT).ignoreError();
+ enableSockopt(socket_.get(), SOL_SOCKET, SO_REUSEADDR).ignoreError();
+ std::string host_str = addr2str(ai->ai_addr, ai->ai_addrlen);
+ if (bind(socket_.get(), ai->ai_addr, ai->ai_addrlen)) {
+ APLOGI("failed to bind UDP %s:%s", host_str.c_str(), listen_service_.c_str());
+ continue;
+ }
+ ALOGI("bound to UDP %s:%s", host_str.c_str(), listen_service_.c_str());
+ break;
+ }
+
+ // Set up eventfd socket.
+ event_fd_.reset(eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC));
+ if (event_fd_.get() == -1) {
+ APLOGI("failed to create eventfd %d", event_fd_.get());
+ return false;
+ }
+
+ // Set up epoll socket.
+ epoll_fd_.reset(epoll_create1(EPOLL_CLOEXEC));
+ if (epoll_fd_.get() < 0) {
+ APLOGI("epoll_create1() failed on fd %d", epoll_fd_.get());
+ return false;
+ }
+
+ ALOGI("adding socket %d to epoll", socket_.get());
+ if (!addFd(socket_.get(), EPOLLIN)) {
+ ALOGE("failed to add the socket %d to epoll", socket_.get());
+ return false;
+ }
+ ALOGI("adding eventfd %d to epoll", event_fd_.get());
+ if (!addFd(event_fd_.get(), EPOLLIN)) {
+ ALOGE("failed to add the eventfd %d to epoll", event_fd_.get());
+ return false;
+ }
+
+ {
+ std::lock_guard lock(update_mutex_);
+ handler_thread_ = std::thread(&DNSResponder::requestHandler, this);
+ }
+ ALOGI("server started successfully");
+ return true;
+}
+
+bool DNSResponder::stopServer() {
+ std::lock_guard lock(update_mutex_);
+ if (!running()) {
+ ALOGI("server not running");
+ return false;
+ }
+ ALOGI("stopping server");
+ if (!sendToEventFd()) {
+ return false;
+ }
+ handler_thread_.join();
+ epoll_fd_.reset();
+ socket_.reset();
+ ALOGI("server stopped successfully");
+ return true;
+}
+
+std::vector<std::pair<std::string, ns_type >> DNSResponder::queries() const {
+ std::lock_guard lock(queries_mutex_);
+ return queries_;
+}
+
+std::string DNSResponder::dumpQueries() const {
+ std::lock_guard lock(queries_mutex_);
+ std::string out;
+ for (const auto& q : queries_) {
+ out += "{\"" + q.first + "\", " + std::to_string(q.second) + "} ";
+ }
+ return out;
+}
+
+void DNSResponder::clearQueries() {
+ std::lock_guard lock(queries_mutex_);
+ queries_.clear();
+}
+
+void DNSResponder::requestHandler() {
+ epoll_event evs[EPOLL_MAX_EVENTS];
+ while (true) {
+ int n = epoll_wait(epoll_fd_.get(), evs, EPOLL_MAX_EVENTS, poll_timeout_ms_);
+ if (n == 0) continue;
+ if (n < 0) {
+ APLOGI("epoll_wait() failed, n=%d", n);
+ return;
+ }
+
+ for (int i = 0; i < n; i++) {
+ const int fd = evs[i].data.fd;
+ const uint32_t events = evs[i].events;
+ if (fd == event_fd_.get() && (events & (EPOLLIN | EPOLLERR))) {
+ handleEventFd();
+ return;
+ } else if (fd == socket_.get() && (events & (EPOLLIN | EPOLLERR))) {
+ handleQuery();
+ } else {
+ ALOGW("unexpected epoll events 0x%x on fd %d", events, fd);
+ }
+ }
+ }
+}
+
+bool DNSResponder::handleDNSRequest(const char* buffer, ssize_t len,
+ char* response, size_t* response_len)
+ const {
+ DBGLOG("request: '%s'", str2hex(buffer, len).c_str());
+ const char* buffer_end = buffer + len;
+ DNSHeader header;
+ const char* cur = header.read(buffer, buffer_end);
+ // TODO(imaipi): for now, unparsable messages are silently dropped, fix.
+ if (cur == nullptr) {
+ ALOGI("failed to parse query");
+ return false;
+ }
+ if (header.qr) {
+ ALOGI("response received instead of a query");
+ return false;
+ }
+ if (header.opcode != ns_opcode::ns_o_query) {
+ ALOGI("unsupported request opcode received");
+ return makeErrorResponse(&header, ns_rcode::ns_r_notimpl, response,
+ response_len);
+ }
+ if (header.questions.empty()) {
+ ALOGI("no questions present");
+ return makeErrorResponse(&header, ns_rcode::ns_r_formerr, response,
+ response_len);
+ }
+ if (!header.answers.empty()) {
+ ALOGI("already %zu answers present in query", header.answers.size());
+ return makeErrorResponse(&header, ns_rcode::ns_r_formerr, response,
+ response_len);
+ }
+
+ if (edns_ == Edns::FORMERR_UNCOND) {
+ ALOGI("force to return RCODE FORMERR");
+ return makeErrorResponse(&header, ns_rcode::ns_r_formerr, response, response_len);
+ }
+
+ if (!header.additionals.empty() && edns_ != Edns::ON) {
+ ALOGI("DNS request has an additional section (assumed EDNS). "
+ "Simulating an ancient (pre-EDNS) server, and returning %s",
+ edns_ == Edns::FORMERR_ON_EDNS ? "RCODE FORMERR." : "no response.");
+ if (edns_ == Edns::FORMERR_ON_EDNS) {
+ return makeErrorResponse(&header, ns_rcode::ns_r_formerr, response, response_len);
+ }
+ // No response.
+ return false;
+ }
+ {
+ std::lock_guard lock(queries_mutex_);
+ for (const DNSQuestion& question : header.questions) {
+ queries_.push_back(make_pair(question.qname.name,
+ ns_type(question.qtype)));
+ }
+ }
+
+ // Ignore requests with the preset probability.
+ auto constexpr bound = std::numeric_limits<unsigned>::max();
+ if (arc4random_uniform(bound) > bound * response_probability_) {
+ if (error_rcode_ < 0) {
+ ALOGI("Returning no response");
+ return false;
+ } else {
+ ALOGI("returning RCODE %d in accordance with probability distribution",
+ static_cast<int>(error_rcode_));
+ return makeErrorResponse(&header, error_rcode_, response, response_len);
+ }
+ }
+
+ for (const DNSQuestion& question : header.questions) {
+ if (question.qclass != ns_class::ns_c_in &&
+ question.qclass != ns_class::ns_c_any) {
+ ALOGI("unsupported question class %u", question.qclass);
+ return makeErrorResponse(&header, ns_rcode::ns_r_notimpl, response,
+ response_len);
+ }
+
+ if (!addAnswerRecords(question, &header.answers)) {
+ return makeErrorResponse(&header, ns_rcode::ns_r_servfail, response, response_len);
+ }
+ }
+
+ header.qr = true;
+ char* response_cur = header.write(response, response + *response_len);
+ if (response_cur == nullptr) {
+ return false;
+ }
+ *response_len = response_cur - response;
+ return true;
+}
+
+bool DNSResponder::addAnswerRecords(const DNSQuestion& question,
+ std::vector<DNSRecord>* answers) const {
+ std::lock_guard guard(mappings_mutex_);
+ std::string rname = question.qname.name;
+ std::vector<int> rtypes;
+
+ if (question.qtype == ns_type::ns_t_a || question.qtype == ns_type::ns_t_aaaa)
+ rtypes.push_back(ns_type::ns_t_cname);
+ rtypes.push_back(question.qtype);
+ for (int rtype : rtypes) {
+ std::set<std::string> cnames_Loop;
+ std::unordered_map<QueryKey, std::string, QueryKeyHash>::const_iterator it;
+ while ((it = mappings_.find(QueryKey(rname, rtype))) != mappings_.end()) {
+ if (rtype == ns_type::ns_t_cname) {
+ // When detect CNAME infinite loops by cnames_Loop, it won't save the duplicate one.
+ // As following, the query will stop on loop3 by detecting the same cname.
+ // loop1.{"a.xxx.com", ns_type::ns_t_cname, "b.xxx.com"}(insert in answer record)
+ // loop2.{"b.xxx.com", ns_type::ns_t_cname, "a.xxx.com"}(insert in answer record)
+ // loop3.{"a.xxx.com", ns_type::ns_t_cname, "b.xxx.com"}(When the same cname record
+ // is found in cnames_Loop already, break the query loop.)
+ if (cnames_Loop.find(it->first.name) != cnames_Loop.end()) break;
+ cnames_Loop.insert(it->first.name);
+ }
+ DNSRecord record{
+ .name = {.name = it->first.name},
+ .rtype = it->first.type,
+ .rclass = ns_class::ns_c_in,
+ .ttl = 5, // seconds
+ };
+ fillAnswerRdata(it->second, record);
+ answers->push_back(std::move(record));
+ if (rtype != ns_type::ns_t_cname) break;
+ rname = it->second;
+ }
+ }
+
+ if (answers->size() == 0) {
+ // TODO(imaipi): handle correctly
+ ALOGI("no mapping found for %s %s, lazily refusing to add an answer",
+ question.qname.name.c_str(), dnstype2str(question.qtype));
+ }
+
+ return true;
+}
+
+bool DNSResponder::fillAnswerRdata(const std::string& rdatastr, DNSRecord& record) const {
+ if (record.rtype == ns_type::ns_t_a) {
+ record.rdata.resize(4);
+ if (inet_pton(AF_INET, rdatastr.c_str(), record.rdata.data()) != 1) {
+ ALOGI("inet_pton(AF_INET, %s) failed", rdatastr.c_str());
+ return false;
+ }
+ } else if (record.rtype == ns_type::ns_t_aaaa) {
+ record.rdata.resize(16);
+ if (inet_pton(AF_INET6, rdatastr.c_str(), record.rdata.data()) != 1) {
+ ALOGI("inet_pton(AF_INET6, %s) failed", rdatastr.c_str());
+ return false;
+ }
+ } else if ((record.rtype == ns_type::ns_t_ptr) || (record.rtype == ns_type::ns_t_cname)) {
+ constexpr char delimiter = '.';
+ std::string name = rdatastr;
+ std::vector<char> rdata;
+
+ // Generating PTRDNAME field(section 3.3.12) or CNAME field(section 3.3.1) in rfc1035.
+ // The "name" should be an absolute domain name which ends in a dot.
+ if (name.back() != delimiter) {
+ ALOGI("invalid absolute domain name");
+ return false;
+ }
+ name.pop_back(); // remove the dot in tail
+ for (const std::string& label : android::base::Split(name, {delimiter})) {
+ // The length of label is limited to 63 octets or less. See RFC 1035 section 3.1.
+ if (label.length() == 0 || label.length() > 63) {
+ ALOGI("invalid label length");
+ return false;
+ }
+
+ rdata.push_back(label.length());
+ rdata.insert(rdata.end(), label.begin(), label.end());
+ }
+ rdata.push_back(0); // Length byte of zero terminates the label list
+
+ // The length of domain name is limited to 255 octets or less. See RFC 1035 section 3.1.
+ if (rdata.size() > 255) {
+ ALOGI("invalid name length");
+ return false;
+ }
+ record.rdata = move(rdata);
+ } else {
+ ALOGI("unhandled qtype %s", dnstype2str(record.rtype));
+ return false;
+ }
+ return true;
+}
+
+bool DNSResponder::makeErrorResponse(DNSHeader* header, ns_rcode rcode,
+ char* response, size_t* response_len)
+ const {
+ header->answers.clear();
+ header->authorities.clear();
+ header->additionals.clear();
+ header->rcode = rcode;
+ header->qr = true;
+ char* response_cur = header->write(response, response + *response_len);
+ if (response_cur == nullptr) return false;
+ *response_len = response_cur - response;
+ return true;
+}
+
+void DNSResponder::setDeferredResp(bool deferred_resp) {
+ std::lock_guard<std::mutex> guard(cv_mutex_for_deferred_resp_);
+ deferred_resp_ = deferred_resp;
+ if (!deferred_resp_) {
+ cv_for_deferred_resp_.notify_one();
+ }
+}
+
+bool DNSResponder::addFd(int fd, uint32_t events) {
+ epoll_event ev;
+ ev.events = events;
+ ev.data.fd = fd;
+ if (epoll_ctl(epoll_fd_.get(), EPOLL_CTL_ADD, fd, &ev) < 0) {
+ APLOGI("epoll_ctl() for socket %d failed", fd);
+ return false;
+ }
+ return true;
+}
+
+void DNSResponder::handleQuery() {
+ char buffer[4096];
+ sockaddr_storage sa;
+ socklen_t sa_len = sizeof(sa);
+ ssize_t len;
+ do {
+ len = recvfrom(socket_.get(), buffer, sizeof(buffer), 0, (sockaddr*)&sa, &sa_len);
+ } while (len < 0 && (errno == EAGAIN || errno == EINTR));
+ if (len <= 0) {
+ APLOGI("recvfrom() failed, len=%zu", len);
+ return;
+ }
+ DBGLOG("read %zd bytes", len);
+ std::lock_guard lock(cv_mutex_);
+ char response[4096];
+ size_t response_len = sizeof(response);
+ if (handleDNSRequest(buffer, len, response, &response_len) && response_len > 0) {
+ // place wait_for after handleDNSRequest() so we can check the number of queries in
+ // test case before it got responded.
+ std::unique_lock guard(cv_mutex_for_deferred_resp_);
+ cv_for_deferred_resp_.wait(
+ guard, [this]() REQUIRES(cv_mutex_for_deferred_resp_) { return !deferred_resp_; });
+
+ len = sendto(socket_.get(), response, response_len, 0,
+ reinterpret_cast<const sockaddr*>(&sa), sa_len);
+ std::string host_str = addr2str(reinterpret_cast<const sockaddr*>(&sa), sa_len);
+ if (len > 0) {
+ DBGLOG("sent %zu bytes to %s", len, host_str.c_str());
+ } else {
+ APLOGI("sendto() failed for %s", host_str.c_str());
+ }
+ // Test that the response is actually a correct DNS message.
+ const char* response_end = response + len;
+ DNSHeader header;
+ const char* cur = header.read(response, response_end);
+ if (cur == nullptr) ALOGW("response is flawed");
+ } else {
+ ALOGW("not responding");
+ }
+ cv.notify_one();
+ return;
+}
+
+bool DNSResponder::sendToEventFd() {
+ const uint64_t data = 1;
+ if (const ssize_t rt = write(event_fd_.get(), &data, sizeof(data)); rt != sizeof(data)) {
+ APLOGI("failed to write eventfd, rt=%zd", rt);
+ return false;
+ }
+ return true;
+}
+
+void DNSResponder::handleEventFd() {
+ int64_t data;
+ if (const ssize_t rt = read(event_fd_.get(), &data, sizeof(data)); rt != sizeof(data)) {
+ APLOGI("ignore reading eventfd failed, rt=%zd", rt);
+ }
+}
+
+} // namespace test
diff --git a/resolv/dns_responder/dns_responder.h b/resolv/dns_responder/dns_responder.h
new file mode 100644
index 0000000..99b601f
--- /dev/null
+++ b/resolv/dns_responder/dns_responder.h
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2016 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 requied 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.
+ *
+ */
+
+#ifndef DNS_RESPONDER_H
+#define DNS_RESPONDER_H
+
+#include <arpa/nameser.h>
+
+#include <atomic>
+#include <condition_variable>
+#include <mutex>
+#include <string>
+#include <thread>
+#include <unordered_map>
+#include <vector>
+
+#include <android-base/thread_annotations.h>
+#include "android-base/unique_fd.h"
+
+namespace test {
+
+struct DNSHeader;
+struct DNSQuestion;
+struct DNSRecord;
+
+inline const std::string kDefaultListenAddr = "127.0.0.3";
+inline const std::string kDefaultListenService = "53";
+inline const int kDefaultPollTimoutMillis = -1;
+
+/*
+ * Simple DNS responder, which replies to queries with the registered response
+ * for that type. Class is assumed to be IN. If no response is registered, the
+ * default error response code is returned.
+ */
+class DNSResponder {
+ public:
+ DNSResponder(std::string listen_address = kDefaultListenAddr,
+ std::string listen_service = kDefaultListenService,
+ int poll_timeout_ms = kDefaultPollTimoutMillis,
+ ns_rcode error_rcode = ns_rcode::ns_r_servfail);
+ ~DNSResponder();
+
+ enum class Edns : uint8_t {
+ ON,
+ FORMERR_ON_EDNS, // DNS server not supporting EDNS will reply FORMERR.
+ FORMERR_UNCOND, // DNS server reply FORMERR unconditionally
+ DROP // DNS server not supporting EDNS will not do any response.
+ };
+
+ void addMapping(const std::string& name, ns_type type, const std::string& addr);
+ void removeMapping(const std::string& name, ns_type type);
+ void setResponseProbability(double response_probability);
+ void setEdns(Edns edns);
+ bool running() const;
+ bool startServer();
+ bool stopServer();
+ const std::string& listen_address() const {
+ return listen_address_;
+ }
+ const std::string& listen_service() const {
+ return listen_service_;
+ }
+ std::vector<std::pair<std::string, ns_type>> queries() const;
+ std::string dumpQueries() const;
+ void clearQueries();
+ std::condition_variable& getCv() { return cv; }
+ std::mutex& getCvMutex() { return cv_mutex_; }
+ void setDeferredResp(bool deferred_resp);
+
+ private:
+ // Key used for accessing mappings.
+ struct QueryKey {
+ std::string name;
+ unsigned type;
+
+ QueryKey(std::string n, unsigned t) : name(move(n)), type(t) {}
+ bool operator == (const QueryKey& o) const {
+ return name == o.name && type == o.type;
+ }
+ bool operator < (const QueryKey& o) const {
+ if (name < o.name) return true;
+ if (name > o.name) return false;
+ return type < o.type;
+ }
+ };
+
+ struct QueryKeyHash {
+ size_t operator() (const QueryKey& key) const {
+ return std::hash<std::string>()(key.name) +
+ static_cast<size_t>(key.type);
+ }
+ };
+
+ void requestHandler();
+
+ // Parses and generates a response message for incoming DNS requests.
+ // Returns false to ignore the request, which might be due to either parsing error
+ // or unresponsiveness.
+ bool handleDNSRequest(const char* buffer, ssize_t buffer_len,
+ char* response, size_t* response_len) const;
+
+ bool addAnswerRecords(const DNSQuestion& question, std::vector<DNSRecord>* answers) const;
+
+ bool fillAnswerRdata(const std::string& rdatastr, DNSRecord& record) const;
+
+ bool generateErrorResponse(DNSHeader* header, ns_rcode rcode,
+ char* response, size_t* response_len) const;
+ bool makeErrorResponse(DNSHeader* header, ns_rcode rcode, char* response,
+ size_t* response_len) const;
+
+ // Add a new file descriptor to be polled by the handler thread.
+ bool addFd(int fd, uint32_t events);
+
+ // Read the query sent from the client and send the answer back to the client. It
+ // makes sure the I/O communicated with the client is correct.
+ void handleQuery();
+
+ // Trigger the handler thread to terminate.
+ bool sendToEventFd();
+
+ // Used in the handler thread for the termination signal.
+ void handleEventFd();
+
+ // Address and service to listen on, currently limited to UDP.
+ const std::string listen_address_;
+ const std::string listen_service_;
+ // epoll_wait() timeout in ms.
+ const int poll_timeout_ms_;
+ // Error code to return for requests for an unknown name.
+ const ns_rcode error_rcode_;
+ // Probability that a valid response is being sent instead of being sent
+ // instead of returning error_rcode_.
+ std::atomic<double> response_probability_ = 1.0;
+ // Maximum number of fds for epoll.
+ const int EPOLL_MAX_EVENTS = 2;
+
+ // Control how the DNS server behaves when it receives the requests containing OPT RR.
+ // If it's set Edns::ON, the server can recognize and reply the response; if it's set
+ // Edns::FORMERR_ON_EDNS, the server behaves like an old DNS server that doesn't support EDNS0,
+ // and replying FORMERR; if it's Edns::DROP, the server doesn't support EDNS0 either, and
+ // ignoring the requests.
+ std::atomic<Edns> edns_ = Edns::ON;
+
+ // Mappings from (name, type) to registered response and the
+ // mutex protecting them.
+ std::unordered_map<QueryKey, std::string, QueryKeyHash> mappings_
+ GUARDED_BY(mappings_mutex_);
+ mutable std::mutex mappings_mutex_;
+ // Query names received so far and the corresponding mutex.
+ mutable std::vector<std::pair<std::string, ns_type>> queries_
+ GUARDED_BY(queries_mutex_);
+ mutable std::mutex queries_mutex_;
+ // Socket on which the server is listening.
+ android::base::unique_fd socket_;
+ // File descriptor for epoll.
+ android::base::unique_fd epoll_fd_;
+ // Eventfd used to signal for the handler thread termination.
+ android::base::unique_fd event_fd_;
+ // Thread for handling incoming threads.
+ std::thread handler_thread_ GUARDED_BY(update_mutex_);
+ std::mutex update_mutex_;
+ std::condition_variable cv;
+ std::mutex cv_mutex_;
+
+ std::condition_variable cv_for_deferred_resp_;
+ std::mutex cv_mutex_for_deferred_resp_;
+ bool deferred_resp_ GUARDED_BY(cv_mutex_for_deferred_resp_) = false;
+};
+
+} // namespace test
+
+#endif // DNS_RESPONDER_H
diff --git a/resolv/dns_responder/dns_responder_client.cpp b/resolv/dns_responder/dns_responder_client.cpp
new file mode 100644
index 0000000..3a87c21
--- /dev/null
+++ b/resolv/dns_responder/dns_responder_client.cpp
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#define LOG_TAG "dns_responder_client"
+#include "dns_responder_client.h"
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <utils/Log.h>
+
+// TODO: make this dynamic and stop depending on implementation details.
+#define TEST_OEM_NETWORK "oem29"
+#define TEST_NETID 30
+
+// TODO: move this somewhere shared.
+static const char* ANDROID_DNS_MODE = "ANDROID_DNS_MODE";
+
+using android::base::StringPrintf;
+using android::net::INetd;
+using android::net::ResolverParamsParcel;
+
+void DnsResponderClient::SetupMappings(unsigned num_hosts, const std::vector<std::string>& domains,
+ std::vector<Mapping>* mappings) {
+ mappings->resize(num_hosts * domains.size());
+ auto mappings_it = mappings->begin();
+ for (unsigned i = 0 ; i < num_hosts ; ++i) {
+ for (const auto& domain : domains) {
+ mappings_it->host = StringPrintf("host%u", i);
+ mappings_it->entry = StringPrintf("%s.%s.", mappings_it->host.c_str(),
+ domain.c_str());
+ mappings_it->ip4 = StringPrintf("192.0.2.%u", i%253 + 1);
+ mappings_it->ip6 = StringPrintf("2001:db8::%x", i%65534 + 1);
+ ++mappings_it;
+ }
+ }
+}
+
+// TODO: Use SetResolverConfiguration() with ResolverParamsParcel struct directly.
+// DEPRECATED: Use SetResolverConfiguration() in new code
+static ResolverParamsParcel makeResolverParamsParcel(
+ int netId, const std::vector<int>& params, const std::vector<std::string>& servers,
+ const std::vector<std::string>& domains, const std::string& tlsHostname,
+ const std::vector<std::string>& tlsServers,
+ const std::vector<std::string>& tlsFingerprints) {
+ using android::net::IDnsResolver;
+ ResolverParamsParcel paramsParcel;
+
+ paramsParcel.netId = netId;
+ paramsParcel.sampleValiditySeconds = params[IDnsResolver::RESOLVER_PARAMS_SAMPLE_VALIDITY];
+ paramsParcel.successThreshold = params[IDnsResolver::RESOLVER_PARAMS_SUCCESS_THRESHOLD];
+ paramsParcel.minSamples = params[IDnsResolver::RESOLVER_PARAMS_MIN_SAMPLES];
+ paramsParcel.maxSamples = params[IDnsResolver::RESOLVER_PARAMS_MAX_SAMPLES];
+ if (params.size() > IDnsResolver::RESOLVER_PARAMS_BASE_TIMEOUT_MSEC) {
+ paramsParcel.baseTimeoutMsec = params[IDnsResolver::RESOLVER_PARAMS_BASE_TIMEOUT_MSEC];
+ } else {
+ paramsParcel.baseTimeoutMsec = 0;
+ }
+ if (params.size() > IDnsResolver::RESOLVER_PARAMS_RETRY_COUNT) {
+ paramsParcel.retryCount = params[IDnsResolver::RESOLVER_PARAMS_RETRY_COUNT];
+ } else {
+ paramsParcel.retryCount = 0;
+ }
+ paramsParcel.servers = servers;
+ paramsParcel.domains = domains;
+ paramsParcel.tlsName = tlsHostname;
+ paramsParcel.tlsServers = tlsServers;
+ paramsParcel.tlsFingerprints = tlsFingerprints;
+
+ return paramsParcel;
+}
+
+bool DnsResponderClient::SetResolversForNetwork(const std::vector<std::string>& servers,
+ const std::vector<std::string>& domains, const std::vector<int>& params) {
+ const auto& resolverParams =
+ makeResolverParamsParcel(TEST_NETID, params, servers, domains, "", {}, {});
+ const auto rv = mDnsResolvSrv->setResolverConfiguration(resolverParams);
+ return rv.isOk();
+}
+
+bool DnsResponderClient::SetResolversWithTls(const std::vector<std::string>& servers,
+ const std::vector<std::string>& domains, const std::vector<int>& params,
+ const std::vector<std::string>& tlsServers,
+ const std::string& name, const std::vector<std::string>& fingerprints) {
+ const auto& resolverParams = makeResolverParamsParcel(TEST_NETID, params, servers, domains,
+ name, tlsServers, fingerprints);
+ const auto rv = mDnsResolvSrv->setResolverConfiguration(resolverParams);
+ if (!rv.isOk()) ALOGI("SetResolversWithTls() -> %s", rv.toString8().c_str());
+ return rv.isOk();
+}
+
+void DnsResponderClient::SetupDNSServers(unsigned num_servers, const std::vector<Mapping>& mappings,
+ std::vector<std::unique_ptr<test::DNSResponder>>* dns,
+ std::vector<std::string>* servers) {
+ const char* listen_srv = "53";
+ dns->resize(num_servers);
+ servers->resize(num_servers);
+ for (unsigned i = 0 ; i < num_servers ; ++i) {
+ auto& server = (*servers)[i];
+ auto& d = (*dns)[i];
+ server = StringPrintf("127.0.0.%u", i + 100);
+ d = std::make_unique<test::DNSResponder>(server, listen_srv, 250, ns_rcode::ns_r_servfail);
+ for (const auto& mapping : mappings) {
+ d->addMapping(mapping.entry.c_str(), ns_type::ns_t_a, mapping.ip4.c_str());
+ d->addMapping(mapping.entry.c_str(), ns_type::ns_t_aaaa, mapping.ip6.c_str());
+ }
+ d->startServer();
+ }
+}
+
+int DnsResponderClient::SetupOemNetwork() {
+ mNetdSrv->networkDestroy(TEST_NETID);
+ mDnsResolvSrv->destroyNetworkCache(TEST_NETID);
+ auto ret = mNetdSrv->networkCreatePhysical(TEST_NETID, INetd::PERMISSION_NONE);
+ if (!ret.isOk()) {
+ fprintf(stderr, "Creating physical network %d failed, %s\n", TEST_NETID,
+ ret.toString8().string());
+ return -1;
+ }
+ ret = mDnsResolvSrv->createNetworkCache(TEST_NETID);
+ if (!ret.isOk()) {
+ fprintf(stderr, "Creating network cache %d failed, %s\n", TEST_NETID,
+ ret.toString8().string());
+ return -1;
+ }
+ setNetworkForProcess(TEST_NETID);
+ if ((unsigned)TEST_NETID != getNetworkForProcess()) {
+ return -1;
+ }
+ return TEST_NETID;
+}
+
+void DnsResponderClient::TearDownOemNetwork(int oemNetId) {
+ if (oemNetId != -1) {
+ mNetdSrv->networkDestroy(oemNetId);
+ mDnsResolvSrv->destroyNetworkCache(oemNetId);
+ }
+}
+
+void DnsResponderClient::SetUp() {
+ // binder setup
+ auto binder = android::defaultServiceManager()->getService(android::String16("netd"));
+ mNetdSrv = android::interface_cast<android::net::INetd>(binder);
+ if (mNetdSrv == nullptr) {
+ LOG(FATAL) << "Can't connect to service 'netd'. Missing root privileges? uid=" << getuid();
+ }
+
+ auto resolvBinder =
+ android::defaultServiceManager()->getService(android::String16("dnsresolver"));
+ mDnsResolvSrv = android::interface_cast<android::net::IDnsResolver>(resolvBinder);
+
+ // Ensure resolutions go via proxy.
+ setenv(ANDROID_DNS_MODE, "", 1);
+ mOemNetId = SetupOemNetwork();
+}
+
+void DnsResponderClient::TearDown() {
+ TearDownOemNetwork(mOemNetId);
+}
diff --git a/resolv/dns_responder/dns_responder_client.h b/resolv/dns_responder/dns_responder_client.h
new file mode 100644
index 0000000..678be02
--- /dev/null
+++ b/resolv/dns_responder/dns_responder_client.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2019 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 requied 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.
+ *
+ */
+
+#ifndef DNS_RESPONDER_CLIENT_H
+#define DNS_RESPONDER_CLIENT_H
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <cutils/sockets.h>
+
+#include <private/android_filesystem_config.h>
+#include <utils/StrongPointer.h>
+
+#include "NetdClient.h"
+#include "android/net/IDnsResolver.h"
+#include "android/net/INetd.h"
+#include "binder/IServiceManager.h"
+#include "dns_responder.h"
+
+#define SKIP_IF_RESOLVER_VERSION_NEWER_THAN(service, version) \
+ do { \
+ if ((service)->getInterfaceVersion() > (version)) { \
+ return; \
+ } \
+ } while (0)
+
+inline const std::vector<std::string> kDefaultServers = {"127.0.0.3"};
+inline const std::vector<std::string> kDefaultSearchDomains = {"example.com"};
+inline const std::vector<int> kDefaultParams = {
+ 300, // sample validity in seconds
+ 25, // success threshod in percent
+ 8, 8, // {MIN,MAX}_SAMPLES
+ 1000, // BASE_TIMEOUT_MSEC
+ 2, // retry count
+};
+
+class DnsResponderClient {
+public:
+ struct Mapping {
+ std::string host;
+ std::string entry;
+ std::string ip4;
+ std::string ip6;
+ };
+
+ virtual ~DnsResponderClient() = default;
+
+ static void SetupMappings(unsigned num_hosts, const std::vector<std::string>& domains,
+ std::vector<Mapping>* mappings);
+
+ bool SetResolversForNetwork(const std::vector<std::string>& servers = kDefaultServers,
+ const std::vector<std::string>& domains = kDefaultSearchDomains,
+ const std::vector<int>& params = kDefaultParams);
+
+ bool SetResolversForNetwork(const std::vector<std::string>& servers,
+ const std::vector<std::string>& searchDomains,
+ const std::string& params);
+
+ bool SetResolversWithTls(const std::vector<std::string>& servers,
+ const std::vector<std::string>& searchDomains,
+ const std::vector<int>& params,
+ const std::string& name,
+ const std::vector<std::string>& fingerprints) {
+ // Pass servers as both network-assigned and TLS servers. Tests can
+ // determine on which server and by which protocol queries arrived.
+ return SetResolversWithTls(servers, searchDomains, params,
+ servers, name, fingerprints);
+ }
+
+ bool SetResolversWithTls(const std::vector<std::string>& servers,
+ const std::vector<std::string>& searchDomains,
+ const std::vector<int>& params,
+ const std::vector<std::string>& tlsServers,
+ const std::string& name,
+ const std::vector<std::string>& fingerprints);
+
+ static void SetupDNSServers(unsigned num_servers, const std::vector<Mapping>& mappings,
+ std::vector<std::unique_ptr<test::DNSResponder>>* dns,
+ std::vector<std::string>* servers);
+
+ int SetupOemNetwork();
+
+ void TearDownOemNetwork(int oemNetId);
+
+ virtual void SetUp();
+ virtual void TearDown();
+
+ android::net::IDnsResolver* resolvService() const { return mDnsResolvSrv.get(); }
+ android::net::INetd* netdService() const { return mNetdSrv.get(); }
+
+ private:
+ android::sp<android::net::INetd> mNetdSrv = nullptr;
+ android::sp<android::net::IDnsResolver> mDnsResolvSrv = nullptr;
+ int mOemNetId = -1;
+};
+
+#endif // DNS_RESPONDER_CLIENT_H
diff --git a/resolv/dns_responder/dns_tls_frontend.cpp b/resolv/dns_responder/dns_tls_frontend.cpp
new file mode 100644
index 0000000..83e36ed
--- /dev/null
+++ b/resolv/dns_responder/dns_tls_frontend.cpp
@@ -0,0 +1,421 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include "dns_tls_frontend.h"
+
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/ssl.h>
+#include <sys/eventfd.h>
+#include <sys/poll.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#define LOG_TAG "DnsTlsFrontend"
+#include <log/log.h>
+#include <netdutils/SocketOption.h>
+
+#include "NetdConstants.h" // SHA256_SIZE
+
+using android::netdutils::enableSockopt;
+
+namespace {
+
+// Copied from DnsTlsTransport.
+bool getSPKIDigest(const X509* cert, std::vector<uint8_t>* out) {
+ int spki_len = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert), nullptr);
+ unsigned char spki[spki_len];
+ unsigned char* temp = spki;
+ if (spki_len != i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert), &temp)) {
+ ALOGE("SPKI length mismatch");
+ return false;
+ }
+ out->resize(SHA256_SIZE);
+ unsigned int digest_len = 0;
+ int ret = EVP_Digest(spki, spki_len, out->data(), &digest_len, EVP_sha256(), nullptr);
+ if (ret != 1) {
+ ALOGE("Server cert digest extraction failed");
+ return false;
+ }
+ if (digest_len != out->size()) {
+ ALOGE("Wrong digest length: %d", digest_len);
+ return false;
+ }
+ return true;
+}
+
+std::string errno2str() {
+ char error_msg[512] = { 0 };
+ return strerror_r(errno, error_msg, sizeof(error_msg));
+}
+
+#define APLOGI(fmt, ...) ALOGI(fmt ": [%d] %s", __VA_ARGS__, errno, errno2str().c_str())
+
+std::string addr2str(const sockaddr* sa, socklen_t sa_len) {
+ char host_str[NI_MAXHOST] = { 0 };
+ int rv = getnameinfo(sa, sa_len, host_str, sizeof(host_str), nullptr, 0,
+ NI_NUMERICHOST);
+ if (rv == 0) return std::string(host_str);
+ return std::string();
+}
+
+bssl::UniquePtr<EVP_PKEY> make_private_key() {
+ bssl::UniquePtr<BIGNUM> e(BN_new());
+ if (!e) {
+ ALOGE("BN_new failed");
+ return nullptr;
+ }
+ if (!BN_set_word(e.get(), RSA_F4)) {
+ ALOGE("BN_set_word failed");
+ return nullptr;
+ }
+
+ bssl::UniquePtr<RSA> rsa(RSA_new());
+ if (!rsa) {
+ ALOGE("RSA_new failed");
+ return nullptr;
+ }
+ if (!RSA_generate_key_ex(rsa.get(), 2048, e.get(), nullptr)) {
+ ALOGE("RSA_generate_key_ex failed");
+ return nullptr;
+ }
+
+ bssl::UniquePtr<EVP_PKEY> privkey(EVP_PKEY_new());
+ if (!privkey) {
+ ALOGE("EVP_PKEY_new failed");
+ return nullptr;
+ }
+ if(!EVP_PKEY_assign_RSA(privkey.get(), rsa.get())) {
+ ALOGE("EVP_PKEY_assign_RSA failed");
+ return nullptr;
+ }
+
+ // |rsa| is now owned by |privkey|, so no need to free it.
+ rsa.release();
+ return privkey;
+}
+
+bssl::UniquePtr<X509> make_cert(EVP_PKEY* privkey, EVP_PKEY* parent_key) {
+ bssl::UniquePtr<X509> cert(X509_new());
+ if (!cert) {
+ ALOGE("X509_new failed");
+ return nullptr;
+ }
+
+ ASN1_INTEGER_set(X509_get_serialNumber(cert.get()), 1);
+
+ // Set one hour expiration.
+ X509_gmtime_adj(X509_get_notBefore(cert.get()), 0);
+ X509_gmtime_adj(X509_get_notAfter(cert.get()), 60 * 60);
+
+ X509_set_pubkey(cert.get(), privkey);
+
+ if (!X509_sign(cert.get(), parent_key, EVP_sha256())) {
+ ALOGE("X509_sign failed");
+ return nullptr;
+ }
+
+ return cert;
+}
+
+}
+
+namespace test {
+
+bool DnsTlsFrontend::startServer() {
+ SSL_load_error_strings();
+ OpenSSL_add_ssl_algorithms();
+
+ // reset queries_ to 0 every time startServer called
+ // which would help us easy to check queries_ via calling waitForQueries
+ queries_ = 0;
+
+ ctx_.reset(SSL_CTX_new(TLS_server_method()));
+ if (!ctx_) {
+ ALOGE("SSL context creation failed");
+ return false;
+ }
+
+ SSL_CTX_set_ecdh_auto(ctx_.get(), 1);
+
+ // Make certificate chain
+ std::vector<bssl::UniquePtr<EVP_PKEY>> keys(chain_length_);
+ for (int i = 0; i < chain_length_; ++i) {
+ keys[i] = make_private_key();
+ }
+ std::vector<bssl::UniquePtr<X509>> certs(chain_length_);
+ for (int i = 0; i < chain_length_; ++i) {
+ int next = std::min(i + 1, chain_length_ - 1);
+ certs[i] = make_cert(keys[i].get(), keys[next].get());
+ }
+
+ // Install certificate chain.
+ if (SSL_CTX_use_certificate(ctx_.get(), certs[0].get()) <= 0) {
+ ALOGE("SSL_CTX_use_certificate failed");
+ return false;
+ }
+ if (SSL_CTX_use_PrivateKey(ctx_.get(), keys[0].get()) <= 0 ) {
+ ALOGE("SSL_CTX_use_PrivateKey failed");
+ return false;
+ }
+ for (int i = 1; i < chain_length_; ++i) {
+ if (SSL_CTX_add1_chain_cert(ctx_.get(), certs[i].get()) != 1) {
+ ALOGE("SSL_CTX_add1_chain_cert failed");
+ return false;
+ }
+ }
+
+ // Report the fingerprint of the "middle" cert. For N = 2, this is the root.
+ int fp_index = chain_length_ / 2;
+ if (!getSPKIDigest(certs[fp_index].get(), &fingerprint_)) {
+ ALOGE("getSPKIDigest failed");
+ return false;
+ }
+
+ // Set up TCP server socket for clients.
+ addrinfo frontend_ai_hints{
+ .ai_family = AF_UNSPEC,
+ .ai_socktype = SOCK_STREAM,
+ .ai_flags = AI_PASSIVE
+ };
+ addrinfo* frontend_ai_res = nullptr;
+ int rv = getaddrinfo(listen_address_.c_str(), listen_service_.c_str(),
+ &frontend_ai_hints, &frontend_ai_res);
+ ScopedAddrinfo frontend_ai_res_cleanup(frontend_ai_res);
+ if (rv) {
+ ALOGE("frontend getaddrinfo(%s, %s) failed: %s", listen_address_.c_str(),
+ listen_service_.c_str(), gai_strerror(rv));
+ return false;
+ }
+
+ for (const addrinfo* ai = frontend_ai_res ; ai ; ai = ai->ai_next) {
+ android::base::unique_fd s(socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol));
+ if (s.get() < 0) {
+ APLOGI("ignore creating socket failed %d", s.get());
+ continue;
+ }
+ enableSockopt(s.get(), SOL_SOCKET, SO_REUSEPORT).ignoreError();
+ enableSockopt(s.get(), SOL_SOCKET, SO_REUSEADDR).ignoreError();
+ std::string host_str = addr2str(ai->ai_addr, ai->ai_addrlen);
+ if (bind(s.get(), ai->ai_addr, ai->ai_addrlen)) {
+ APLOGI("failed to bind TCP %s:%s", host_str.c_str(), listen_service_.c_str());
+ continue;
+ }
+ ALOGI("bound to TCP %s:%s", host_str.c_str(), listen_service_.c_str());
+ socket_ = std::move(s);
+ break;
+ }
+
+ if (listen(socket_.get(), 1) < 0) {
+ APLOGI("failed to listen socket %d", socket_.get());
+ return false;
+ }
+
+ // Set up UDP client socket to backend.
+ addrinfo backend_ai_hints{
+ .ai_family = AF_UNSPEC,
+ .ai_socktype = SOCK_DGRAM
+ };
+ addrinfo* backend_ai_res = nullptr;
+ rv = getaddrinfo(backend_address_.c_str(), backend_service_.c_str(),
+ &backend_ai_hints, &backend_ai_res);
+ ScopedAddrinfo backend_ai_res_cleanup(backend_ai_res);
+ if (rv) {
+ ALOGE("backend getaddrinfo(%s, %s) failed: %s", listen_address_.c_str(),
+ listen_service_.c_str(), gai_strerror(rv));
+ return false;
+ }
+ backend_socket_.reset(socket(backend_ai_res->ai_family, backend_ai_res->ai_socktype,
+ backend_ai_res->ai_protocol));
+ if (backend_socket_.get() < 0) {
+ APLOGI("backend socket %d creation failed", backend_socket_.get());
+ return false;
+ }
+
+ // connect() always fails in the test DnsTlsSocketTest.SlowDestructor because of
+ // no backend server. Don't check it.
+ connect(backend_socket_.get(), backend_ai_res->ai_addr, backend_ai_res->ai_addrlen);
+
+ // Set up eventfd socket.
+ event_fd_.reset(eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC));
+ if (event_fd_.get() == -1) {
+ APLOGI("failed to create eventfd %d", event_fd_.get());
+ return false;
+ }
+
+ {
+ std::lock_guard lock(update_mutex_);
+ handler_thread_ = std::thread(&DnsTlsFrontend::requestHandler, this);
+ }
+ ALOGI("server started successfully");
+ return true;
+}
+
+void DnsTlsFrontend::requestHandler() {
+ ALOGD("Request handler started");
+ enum { EVENT_FD = 0, LISTEN_FD = 1 };
+ pollfd fds[2] = {{.fd = event_fd_.get(), .events = POLLIN},
+ {.fd = socket_.get(), .events = POLLIN}};
+
+ while (true) {
+ int poll_code = poll(fds, std::size(fds), -1);
+ if (poll_code <= 0) {
+ APLOGI("Poll failed with error %d", poll_code);
+ break;
+ }
+
+ if (fds[EVENT_FD].revents & (POLLIN | POLLERR)) {
+ handleEventFd();
+ break;
+ }
+ if (fds[LISTEN_FD].revents & (POLLIN | POLLERR)) {
+ sockaddr_storage addr;
+ socklen_t len = sizeof(addr);
+
+ ALOGD("Trying to accept a client");
+ android::base::unique_fd client(
+ accept4(socket_.get(), reinterpret_cast<sockaddr*>(&addr), &len, SOCK_CLOEXEC));
+ if (client.get() < 0) {
+ // Stop
+ APLOGI("failed to accept client socket %d", client.get());
+ break;
+ }
+
+ bssl::UniquePtr<SSL> ssl(SSL_new(ctx_.get()));
+ SSL_set_fd(ssl.get(), client.get());
+
+ ALOGD("Doing SSL handshake");
+ bool success = false;
+ if (SSL_accept(ssl.get()) <= 0) {
+ ALOGI("SSL negotiation failure");
+ } else {
+ ALOGD("SSL handshake complete");
+ success = handleOneRequest(ssl.get());
+ }
+
+ if (success) {
+ // Increment queries_ as late as possible, because it represents
+ // a query that is fully processed, and the response returned to the
+ // client, including cleanup actions.
+ ++queries_;
+ }
+ }
+ }
+ ALOGD("Ending loop");
+}
+
+bool DnsTlsFrontend::handleOneRequest(SSL* ssl) {
+ uint8_t queryHeader[2];
+ if (SSL_read(ssl, &queryHeader, 2) != 2) {
+ ALOGI("Not enough header bytes");
+ return false;
+ }
+ const uint16_t qlen = (queryHeader[0] << 8) | queryHeader[1];
+ uint8_t query[qlen];
+ size_t qbytes = 0;
+ while (qbytes < qlen) {
+ int ret = SSL_read(ssl, query + qbytes, qlen - qbytes);
+ if (ret <= 0) {
+ ALOGI("Error while reading query");
+ return false;
+ }
+ qbytes += ret;
+ }
+ int sent = send(backend_socket_.get(), query, qlen, 0);
+ if (sent != qlen) {
+ ALOGI("Failed to send query");
+ return false;
+ }
+ const int max_size = 4096;
+ uint8_t recv_buffer[max_size];
+ int rlen = recv(backend_socket_.get(), recv_buffer, max_size, 0);
+ if (rlen <= 0) {
+ ALOGI("Failed to receive response");
+ return false;
+ }
+ uint8_t responseHeader[2];
+ responseHeader[0] = rlen >> 8;
+ responseHeader[1] = rlen;
+ if (SSL_write(ssl, responseHeader, 2) != 2) {
+ ALOGI("Failed to write response header");
+ return false;
+ }
+ if (SSL_write(ssl, recv_buffer, rlen) != rlen) {
+ ALOGI("Failed to write response body");
+ return false;
+ }
+ return true;
+}
+
+bool DnsTlsFrontend::stopServer() {
+ std::lock_guard lock(update_mutex_);
+ if (!running()) {
+ ALOGI("server not running");
+ return false;
+ }
+
+ ALOGI("stopping frontend");
+ if (!sendToEventFd()) {
+ return false;
+ }
+ handler_thread_.join();
+ socket_.reset();
+ backend_socket_.reset();
+ event_fd_.reset();
+ ctx_.reset();
+ fingerprint_.clear();
+ ALOGI("frontend stopped successfully");
+ return true;
+}
+
+bool DnsTlsFrontend::waitForQueries(int number, int timeoutMs) const {
+ constexpr int intervalMs = 20;
+ int limit = timeoutMs / intervalMs;
+ for (int count = 0; count <= limit; ++count) {
+ bool done = queries_ >= number;
+ // Always sleep at least one more interval after we are done, to wait for
+ // any immediate post-query actions that the client may take (such as
+ // marking this server as reachable during validation).
+ usleep(intervalMs * 1000);
+ if (done) {
+ // For ensuring that calls have sufficient headroom for slow machines
+ ALOGD("Query arrived in %d/%d of allotted time", count, limit);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool DnsTlsFrontend::sendToEventFd() {
+ const uint64_t data = 1;
+ if (const ssize_t rt = write(event_fd_.get(), &data, sizeof(data)); rt != sizeof(data)) {
+ APLOGI("failed to write eventfd, rt=%zd", rt);
+ return false;
+ }
+ return true;
+}
+
+void DnsTlsFrontend::handleEventFd() {
+ int64_t data;
+ if (const ssize_t rt = read(event_fd_.get(), &data, sizeof(data)); rt != sizeof(data)) {
+ APLOGI("ignore reading eventfd failed, rt=%zd", rt);
+ }
+}
+
+} // namespace test
diff --git a/resolv/dns_responder/dns_tls_frontend.h b/resolv/dns_responder/dns_tls_frontend.h
new file mode 100644
index 0000000..32a2771
--- /dev/null
+++ b/resolv/dns_responder/dns_tls_frontend.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2017 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 requied 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.
+ *
+ */
+
+#ifndef DNS_TLS_FRONTEND_H
+#define DNS_TLS_FRONTEND_H
+
+#include <arpa/nameser.h>
+
+#include <atomic>
+#include <mutex>
+#include <string>
+#include <thread>
+#include <unordered_map>
+#include <vector>
+
+#include <android-base/thread_annotations.h>
+#include <android-base/unique_fd.h>
+#include <openssl/ssl.h>
+
+namespace test {
+
+/*
+ * Simple DNS over TLS reverse proxy that forwards to a UDP backend.
+ * Only handles a single request at a time.
+ */
+class DnsTlsFrontend {
+ public:
+ DnsTlsFrontend(const std::string& listen_address, const std::string& listen_service,
+ const std::string& backend_address, const std::string& backend_service)
+ : listen_address_(listen_address),
+ listen_service_(listen_service),
+ backend_address_(backend_address),
+ backend_service_(backend_service) {}
+ ~DnsTlsFrontend() {
+ stopServer();
+ }
+ const std::string& listen_address() const {
+ return listen_address_;
+ }
+ const std::string& listen_service() const {
+ return listen_service_;
+ }
+ bool running() const {
+ return socket_ != -1;
+ }
+ bool startServer();
+ bool stopServer();
+ int queries() const { return queries_; }
+ void clearQueries() { queries_ = 0; }
+ bool waitForQueries(int number, int timeoutMs) const;
+ void set_chain_length(int length) { chain_length_ = length; }
+ // Represents a fingerprint from the middle of the certificate chain.
+ const std::vector<uint8_t>& fingerprint() const { return fingerprint_; }
+
+ private:
+ void requestHandler();
+ bool handleOneRequest(SSL* ssl);
+
+ // Trigger the handler thread to terminate.
+ bool sendToEventFd();
+
+ // Used in the handler thread for the termination signal.
+ void handleEventFd();
+
+ std::string listen_address_;
+ std::string listen_service_;
+ std::string backend_address_;
+ std::string backend_service_;
+ bssl::UniquePtr<SSL_CTX> ctx_;
+ // Socket on which the server is listening for a TCP connection with a client.
+ android::base::unique_fd socket_;
+ // Socket used to communicate with the backend DNS server.
+ android::base::unique_fd backend_socket_;
+ // Eventfd used to signal for the handler thread termination.
+ android::base::unique_fd event_fd_;
+ std::atomic<int> queries_ = 0;
+ std::thread handler_thread_ GUARDED_BY(update_mutex_);
+ std::mutex update_mutex_;
+ int chain_length_ = 1;
+ std::vector<uint8_t> fingerprint_;
+};
+
+} // namespace test
+
+#endif // DNS_TLS_FRONTEND_H
diff --git a/resolv/dns_tls_test.cpp b/resolv/dns_tls_test.cpp
new file mode 100644
index 0000000..0bcad30
--- /dev/null
+++ b/resolv/dns_tls_test.cpp
@@ -0,0 +1,963 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#define LOG_TAG "dns_tls_test"
+#define LOG_NDEBUG 1 // Set to 0 to enable verbose debug logging
+
+#include <gtest/gtest.h>
+
+#include "DnsTlsDispatcher.h"
+#include "DnsTlsQueryMap.h"
+#include "DnsTlsServer.h"
+#include "DnsTlsSessionCache.h"
+#include "DnsTlsSocket.h"
+#include "DnsTlsTransport.h"
+#include "IDnsTlsSocket.h"
+#include "IDnsTlsSocketFactory.h"
+#include "IDnsTlsSocketObserver.h"
+
+#include "dns_responder/dns_tls_frontend.h"
+
+#include <chrono>
+#include <arpa/inet.h>
+#include <android-base/macros.h>
+#include <netdutils/Slice.h>
+
+#include "log/log.h"
+
+namespace android {
+namespace net {
+
+using netdutils::Slice;
+using netdutils::makeSlice;
+
+typedef std::vector<uint8_t> bytevec;
+
+static void parseServer(const char* server, in_port_t port, sockaddr_storage* parsed) {
+ sockaddr_in* sin = reinterpret_cast<sockaddr_in*>(parsed);
+ if (inet_pton(AF_INET, server, &(sin->sin_addr)) == 1) {
+ // IPv4 parse succeeded, so it's IPv4
+ sin->sin_family = AF_INET;
+ sin->sin_port = htons(port);
+ return;
+ }
+ sockaddr_in6* sin6 = reinterpret_cast<sockaddr_in6*>(parsed);
+ if (inet_pton(AF_INET6, server, &(sin6->sin6_addr)) == 1){
+ // IPv6 parse succeeded, so it's IPv6.
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = htons(port);
+ return;
+ }
+ ALOGE("Failed to parse server address: %s", server);
+}
+
+bytevec FINGERPRINT1 = { 1 };
+bytevec FINGERPRINT2 = { 2 };
+
+std::string SERVERNAME1 = "dns.example.com";
+std::string SERVERNAME2 = "dns.example.org";
+
+// BaseTest just provides constants that are useful for the tests.
+class BaseTest : public ::testing::Test {
+ protected:
+ BaseTest() {
+ parseServer("192.0.2.1", 853, &V4ADDR1);
+ parseServer("192.0.2.2", 853, &V4ADDR2);
+ parseServer("2001:db8::1", 853, &V6ADDR1);
+ parseServer("2001:db8::2", 853, &V6ADDR2);
+
+ SERVER1 = DnsTlsServer(V4ADDR1);
+ SERVER1.fingerprints.insert(FINGERPRINT1);
+ SERVER1.name = SERVERNAME1;
+ }
+
+ sockaddr_storage V4ADDR1;
+ sockaddr_storage V4ADDR2;
+ sockaddr_storage V6ADDR1;
+ sockaddr_storage V6ADDR2;
+
+ DnsTlsServer SERVER1;
+};
+
+bytevec make_query(uint16_t id, size_t size) {
+ bytevec vec(size);
+ vec[0] = id >> 8;
+ vec[1] = id;
+ // Arbitrarily fill the query body with unique data.
+ for (size_t i = 2; i < size; ++i) {
+ vec[i] = id + i;
+ }
+ return vec;
+}
+
+// Query constants
+const unsigned MARK = 123;
+const uint16_t ID = 52;
+const uint16_t SIZE = 22;
+const bytevec QUERY = make_query(ID, SIZE);
+
+template <class T>
+class FakeSocketFactory : public IDnsTlsSocketFactory {
+ public:
+ FakeSocketFactory() {}
+ std::unique_ptr<IDnsTlsSocket> createDnsTlsSocket(
+ const DnsTlsServer& server ATTRIBUTE_UNUSED,
+ unsigned mark ATTRIBUTE_UNUSED,
+ IDnsTlsSocketObserver* observer,
+ DnsTlsSessionCache* cache ATTRIBUTE_UNUSED) override {
+ return std::make_unique<T>(observer);
+ }
+};
+
+bytevec make_echo(uint16_t id, const Slice query) {
+ bytevec response(query.size() + 2);
+ response[0] = id >> 8;
+ response[1] = id;
+ // Echo the query as the fake response.
+ memcpy(response.data() + 2, query.base(), query.size());
+ return response;
+}
+
+// Simplest possible fake server. This just echoes the query as the response.
+class FakeSocketEcho : public IDnsTlsSocket {
+ public:
+ explicit FakeSocketEcho(IDnsTlsSocketObserver* observer) : mObserver(observer) {}
+ bool query(uint16_t id, const Slice query) override {
+ // Return the response immediately (asynchronously).
+ std::thread(&IDnsTlsSocketObserver::onResponse, mObserver, make_echo(id, query)).detach();
+ return true;
+ }
+
+ private:
+ IDnsTlsSocketObserver* const mObserver;
+};
+
+class TransportTest : public BaseTest {};
+
+TEST_F(TransportTest, Query) {
+ FakeSocketFactory<FakeSocketEcho> factory;
+ DnsTlsTransport transport(SERVER1, MARK, &factory);
+ auto r = transport.query(makeSlice(QUERY)).get();
+
+ EXPECT_EQ(DnsTlsTransport::Response::success, r.code);
+ EXPECT_EQ(QUERY, r.response);
+}
+
+// Fake Socket that echoes the observed query ID as the response body.
+class FakeSocketId : public IDnsTlsSocket {
+ public:
+ explicit FakeSocketId(IDnsTlsSocketObserver* observer) : mObserver(observer) {}
+ bool query(uint16_t id, const Slice query ATTRIBUTE_UNUSED) override {
+ // Return the response immediately (asynchronously).
+ bytevec response(4);
+ // Echo the ID in the header to match the response to the query.
+ // This will be overwritten by DnsTlsQueryMap.
+ response[0] = id >> 8;
+ response[1] = id;
+ // Echo the ID in the body, so that the test can verify which ID was used by
+ // DnsTlsQueryMap.
+ response[2] = id >> 8;
+ response[3] = id;
+ std::thread(&IDnsTlsSocketObserver::onResponse, mObserver, response).detach();
+ return true;
+ }
+
+ private:
+ IDnsTlsSocketObserver* const mObserver;
+};
+
+// Test that IDs are properly reused
+TEST_F(TransportTest, IdReuse) {
+ FakeSocketFactory<FakeSocketId> factory;
+ DnsTlsTransport transport(SERVER1, MARK, &factory);
+ for (int i = 0; i < 100; ++i) {
+ // Send a query.
+ std::future<DnsTlsServer::Result> f = transport.query(makeSlice(QUERY));
+ // Wait for the response.
+ DnsTlsServer::Result r = f.get();
+ EXPECT_EQ(DnsTlsTransport::Response::success, r.code);
+
+ // All queries should have an observed ID of zero, because it is returned to the ID pool
+ // after each use.
+ EXPECT_EQ(0, (r.response[2] << 8) | r.response[3]);
+ }
+}
+
+// These queries might be handled in serial or parallel as they race the
+// responses.
+TEST_F(TransportTest, RacingQueries_10000) {
+ FakeSocketFactory<FakeSocketEcho> factory;
+ DnsTlsTransport transport(SERVER1, MARK, &factory);
+ std::vector<std::future<DnsTlsTransport::Result>> results;
+ // Fewer than 65536 queries to avoid ID exhaustion.
+ const int num_queries = 10000;
+ results.reserve(num_queries);
+ for (int i = 0; i < num_queries; ++i) {
+ results.push_back(transport.query(makeSlice(QUERY)));
+ }
+ for (auto& result : results) {
+ auto r = result.get();
+ EXPECT_EQ(DnsTlsTransport::Response::success, r.code);
+ EXPECT_EQ(QUERY, r.response);
+ }
+}
+
+// A server that waits until sDelay queries are queued before responding.
+class FakeSocketDelay : public IDnsTlsSocket {
+ public:
+ explicit FakeSocketDelay(IDnsTlsSocketObserver* observer) : mObserver(observer) {}
+ ~FakeSocketDelay() { std::lock_guard guard(mLock); }
+ static size_t sDelay;
+ static bool sReverse;
+
+ bool query(uint16_t id, const Slice query) override {
+ ALOGV("FakeSocketDelay got query with ID %d", int(id));
+ std::lock_guard guard(mLock);
+ // Check for duplicate IDs.
+ EXPECT_EQ(0U, mIds.count(id));
+ mIds.insert(id);
+
+ // Store response.
+ mResponses.push_back(make_echo(id, query));
+
+ ALOGV("Up to %zu out of %zu queries", mResponses.size(), sDelay);
+ if (mResponses.size() == sDelay) {
+ std::thread(&FakeSocketDelay::sendResponses, this).detach();
+ }
+ return true;
+ }
+
+ private:
+ void sendResponses() {
+ std::lock_guard guard(mLock);
+ if (sReverse) {
+ std::reverse(std::begin(mResponses), std::end(mResponses));
+ }
+ for (auto& response : mResponses) {
+ mObserver->onResponse(response);
+ }
+ mIds.clear();
+ mResponses.clear();
+ }
+
+ std::mutex mLock;
+ IDnsTlsSocketObserver* const mObserver;
+ std::set<uint16_t> mIds GUARDED_BY(mLock);
+ std::vector<bytevec> mResponses GUARDED_BY(mLock);
+};
+
+size_t FakeSocketDelay::sDelay;
+bool FakeSocketDelay::sReverse;
+
+TEST_F(TransportTest, ParallelColliding) {
+ FakeSocketDelay::sDelay = 10;
+ FakeSocketDelay::sReverse = false;
+ FakeSocketFactory<FakeSocketDelay> factory;
+ DnsTlsTransport transport(SERVER1, MARK, &factory);
+ std::vector<std::future<DnsTlsTransport::Result>> results;
+ // Fewer than 65536 queries to avoid ID exhaustion.
+ results.reserve(FakeSocketDelay::sDelay);
+ for (size_t i = 0; i < FakeSocketDelay::sDelay; ++i) {
+ results.push_back(transport.query(makeSlice(QUERY)));
+ }
+ for (auto& result : results) {
+ auto r = result.get();
+ EXPECT_EQ(DnsTlsTransport::Response::success, r.code);
+ EXPECT_EQ(QUERY, r.response);
+ }
+}
+
+TEST_F(TransportTest, ParallelColliding_Max) {
+ FakeSocketDelay::sDelay = 65536;
+ FakeSocketDelay::sReverse = false;
+ FakeSocketFactory<FakeSocketDelay> factory;
+ DnsTlsTransport transport(SERVER1, MARK, &factory);
+ std::vector<std::future<DnsTlsTransport::Result>> results;
+ // Exactly 65536 queries should still be possible in parallel,
+ // even if they all have the same original ID.
+ results.reserve(FakeSocketDelay::sDelay);
+ for (size_t i = 0; i < FakeSocketDelay::sDelay; ++i) {
+ results.push_back(transport.query(makeSlice(QUERY)));
+ }
+ for (auto& result : results) {
+ auto r = result.get();
+ EXPECT_EQ(DnsTlsTransport::Response::success, r.code);
+ EXPECT_EQ(QUERY, r.response);
+ }
+}
+
+TEST_F(TransportTest, ParallelUnique) {
+ FakeSocketDelay::sDelay = 10;
+ FakeSocketDelay::sReverse = false;
+ FakeSocketFactory<FakeSocketDelay> factory;
+ DnsTlsTransport transport(SERVER1, MARK, &factory);
+ std::vector<bytevec> queries(FakeSocketDelay::sDelay);
+ std::vector<std::future<DnsTlsTransport::Result>> results;
+ results.reserve(FakeSocketDelay::sDelay);
+ for (size_t i = 0; i < FakeSocketDelay::sDelay; ++i) {
+ queries[i] = make_query(i, SIZE);
+ results.push_back(transport.query(makeSlice(queries[i])));
+ }
+ for (size_t i = 0 ; i < FakeSocketDelay::sDelay; ++i) {
+ auto r = results[i].get();
+ EXPECT_EQ(DnsTlsTransport::Response::success, r.code);
+ EXPECT_EQ(queries[i], r.response);
+ }
+}
+
+TEST_F(TransportTest, ParallelUnique_Max) {
+ FakeSocketDelay::sDelay = 65536;
+ FakeSocketDelay::sReverse = false;
+ FakeSocketFactory<FakeSocketDelay> factory;
+ DnsTlsTransport transport(SERVER1, MARK, &factory);
+ std::vector<bytevec> queries(FakeSocketDelay::sDelay);
+ std::vector<std::future<DnsTlsTransport::Result>> results;
+ // Exactly 65536 queries should still be possible in parallel,
+ // and they should all be mapped correctly back to the original ID.
+ results.reserve(FakeSocketDelay::sDelay);
+ for (size_t i = 0; i < FakeSocketDelay::sDelay; ++i) {
+ queries[i] = make_query(i, SIZE);
+ results.push_back(transport.query(makeSlice(queries[i])));
+ }
+ for (size_t i = 0 ; i < FakeSocketDelay::sDelay; ++i) {
+ auto r = results[i].get();
+ EXPECT_EQ(DnsTlsTransport::Response::success, r.code);
+ EXPECT_EQ(queries[i], r.response);
+ }
+}
+
+TEST_F(TransportTest, IdExhaustion) {
+ const int num_queries = 65536;
+ // A delay of 65537 is unreachable, because the maximum number
+ // of outstanding queries is 65536.
+ FakeSocketDelay::sDelay = num_queries + 1;
+ FakeSocketDelay::sReverse = false;
+ FakeSocketFactory<FakeSocketDelay> factory;
+ DnsTlsTransport transport(SERVER1, MARK, &factory);
+ std::vector<std::future<DnsTlsTransport::Result>> results;
+ // Issue the maximum number of queries.
+ results.reserve(num_queries);
+ for (int i = 0; i < num_queries; ++i) {
+ results.push_back(transport.query(makeSlice(QUERY)));
+ }
+
+ // The ID space is now full, so subsequent queries should fail immediately.
+ auto r = transport.query(makeSlice(QUERY)).get();
+ EXPECT_EQ(DnsTlsTransport::Response::internal_error, r.code);
+ EXPECT_TRUE(r.response.empty());
+
+ for (auto& result : results) {
+ // All other queries should remain outstanding.
+ EXPECT_EQ(std::future_status::timeout,
+ result.wait_for(std::chrono::duration<int>::zero()));
+ }
+}
+
+// Responses can come back from the server in any order. This should have no
+// effect on Transport's observed behavior.
+TEST_F(TransportTest, ReverseOrder) {
+ FakeSocketDelay::sDelay = 10;
+ FakeSocketDelay::sReverse = true;
+ FakeSocketFactory<FakeSocketDelay> factory;
+ DnsTlsTransport transport(SERVER1, MARK, &factory);
+ std::vector<bytevec> queries(FakeSocketDelay::sDelay);
+ std::vector<std::future<DnsTlsTransport::Result>> results;
+ results.reserve(FakeSocketDelay::sDelay);
+ for (size_t i = 0; i < FakeSocketDelay::sDelay; ++i) {
+ queries[i] = make_query(i, SIZE);
+ results.push_back(transport.query(makeSlice(queries[i])));
+ }
+ for (size_t i = 0 ; i < FakeSocketDelay::sDelay; ++i) {
+ auto r = results[i].get();
+ EXPECT_EQ(DnsTlsTransport::Response::success, r.code);
+ EXPECT_EQ(queries[i], r.response);
+ }
+}
+
+TEST_F(TransportTest, ReverseOrder_Max) {
+ FakeSocketDelay::sDelay = 65536;
+ FakeSocketDelay::sReverse = true;
+ FakeSocketFactory<FakeSocketDelay> factory;
+ DnsTlsTransport transport(SERVER1, MARK, &factory);
+ std::vector<bytevec> queries(FakeSocketDelay::sDelay);
+ std::vector<std::future<DnsTlsTransport::Result>> results;
+ results.reserve(FakeSocketDelay::sDelay);
+ for (size_t i = 0; i < FakeSocketDelay::sDelay; ++i) {
+ queries[i] = make_query(i, SIZE);
+ results.push_back(transport.query(makeSlice(queries[i])));
+ }
+ for (size_t i = 0 ; i < FakeSocketDelay::sDelay; ++i) {
+ auto r = results[i].get();
+ EXPECT_EQ(DnsTlsTransport::Response::success, r.code);
+ EXPECT_EQ(queries[i], r.response);
+ }
+}
+
+// Returning null from the factory indicates a connection failure.
+class NullSocketFactory : public IDnsTlsSocketFactory {
+ public:
+ NullSocketFactory() {}
+ std::unique_ptr<IDnsTlsSocket> createDnsTlsSocket(
+ const DnsTlsServer& server ATTRIBUTE_UNUSED,
+ unsigned mark ATTRIBUTE_UNUSED,
+ IDnsTlsSocketObserver* observer ATTRIBUTE_UNUSED,
+ DnsTlsSessionCache* cache ATTRIBUTE_UNUSED) override {
+ return nullptr;
+ }
+};
+
+TEST_F(TransportTest, ConnectFail) {
+ NullSocketFactory factory;
+ DnsTlsTransport transport(SERVER1, MARK, &factory);
+ auto r = transport.query(makeSlice(QUERY)).get();
+
+ EXPECT_EQ(DnsTlsTransport::Response::network_error, r.code);
+ EXPECT_TRUE(r.response.empty());
+}
+
+// Simulate a socket that connects but then immediately receives a server
+// close notification.
+class FakeSocketClose : public IDnsTlsSocket {
+ public:
+ explicit FakeSocketClose(IDnsTlsSocketObserver* observer)
+ : mCloser(&IDnsTlsSocketObserver::onClosed, observer) {}
+ ~FakeSocketClose() { mCloser.join(); }
+ bool query(uint16_t id ATTRIBUTE_UNUSED,
+ const Slice query ATTRIBUTE_UNUSED) override {
+ return true;
+ }
+
+ private:
+ std::thread mCloser;
+};
+
+TEST_F(TransportTest, CloseRetryFail) {
+ FakeSocketFactory<FakeSocketClose> factory;
+ DnsTlsTransport transport(SERVER1, MARK, &factory);
+ auto r = transport.query(makeSlice(QUERY)).get();
+
+ EXPECT_EQ(DnsTlsTransport::Response::network_error, r.code);
+ EXPECT_TRUE(r.response.empty());
+}
+
+// Simulate a server that occasionally closes the connection and silently
+// drops some queries.
+class FakeSocketLimited : public IDnsTlsSocket {
+ public:
+ static int sLimit; // Number of queries to answer per socket.
+ static size_t sMaxSize; // Silently discard queries greater than this size.
+ explicit FakeSocketLimited(IDnsTlsSocketObserver* observer)
+ : mObserver(observer), mQueries(0) {}
+ ~FakeSocketLimited() {
+ {
+ ALOGV("~FakeSocketLimited acquiring mLock");
+ std::lock_guard guard(mLock);
+ ALOGV("~FakeSocketLimited acquired mLock");
+ for (auto& thread : mThreads) {
+ ALOGV("~FakeSocketLimited joining response thread");
+ thread.join();
+ ALOGV("~FakeSocketLimited joined response thread");
+ }
+ mThreads.clear();
+ }
+
+ if (mCloser) {
+ ALOGV("~FakeSocketLimited joining closer thread");
+ mCloser->join();
+ ALOGV("~FakeSocketLimited joined closer thread");
+ }
+ }
+ bool query(uint16_t id, const Slice query) override {
+ ALOGV("FakeSocketLimited::query acquiring mLock");
+ std::lock_guard guard(mLock);
+ ALOGV("FakeSocketLimited::query acquired mLock");
+ ++mQueries;
+
+ if (mQueries <= sLimit) {
+ ALOGV("size %zu vs. limit of %zu", query.size(), sMaxSize);
+ if (query.size() <= sMaxSize) {
+ // Return the response immediately (asynchronously).
+ mThreads.emplace_back(&IDnsTlsSocketObserver::onResponse, mObserver, make_echo(id, query));
+ }
+ }
+ if (mQueries == sLimit) {
+ mCloser = std::make_unique<std::thread>(&FakeSocketLimited::sendClose, this);
+ }
+ return mQueries <= sLimit;
+ }
+
+ private:
+ void sendClose() {
+ {
+ ALOGV("FakeSocketLimited::sendClose acquiring mLock");
+ std::lock_guard guard(mLock);
+ ALOGV("FakeSocketLimited::sendClose acquired mLock");
+ for (auto& thread : mThreads) {
+ ALOGV("FakeSocketLimited::sendClose joining response thread");
+ thread.join();
+ ALOGV("FakeSocketLimited::sendClose joined response thread");
+ }
+ mThreads.clear();
+ }
+ mObserver->onClosed();
+ }
+ std::mutex mLock;
+ IDnsTlsSocketObserver* const mObserver;
+ int mQueries GUARDED_BY(mLock);
+ std::vector<std::thread> mThreads GUARDED_BY(mLock);
+ std::unique_ptr<std::thread> mCloser GUARDED_BY(mLock);
+};
+
+int FakeSocketLimited::sLimit;
+size_t FakeSocketLimited::sMaxSize;
+
+TEST_F(TransportTest, SilentDrop) {
+ FakeSocketLimited::sLimit = 10; // Close the socket after 10 queries.
+ FakeSocketLimited::sMaxSize = 0; // Silently drop all queries
+ FakeSocketFactory<FakeSocketLimited> factory;
+ DnsTlsTransport transport(SERVER1, MARK, &factory);
+
+ // Queue up 10 queries. They will all be ignored, and after the 10th,
+ // the socket will close. Transport will retry them all, until they
+ // all hit the retry limit and expire.
+ std::vector<std::future<DnsTlsTransport::Result>> results;
+ results.reserve(FakeSocketLimited::sLimit);
+ for (int i = 0; i < FakeSocketLimited::sLimit; ++i) {
+ results.push_back(transport.query(makeSlice(QUERY)));
+ }
+ for (auto& result : results) {
+ auto r = result.get();
+ EXPECT_EQ(DnsTlsTransport::Response::network_error, r.code);
+ EXPECT_TRUE(r.response.empty());
+ }
+}
+
+TEST_F(TransportTest, PartialDrop) {
+ FakeSocketLimited::sLimit = 10; // Close the socket after 10 queries.
+ FakeSocketLimited::sMaxSize = SIZE - 2; // Silently drop "long" queries
+ FakeSocketFactory<FakeSocketLimited> factory;
+ DnsTlsTransport transport(SERVER1, MARK, &factory);
+
+ // Queue up 100 queries, alternating "short" which will be served and "long"
+ // which will be dropped.
+ const int num_queries = 10 * FakeSocketLimited::sLimit;
+ std::vector<bytevec> queries(num_queries);
+ std::vector<std::future<DnsTlsTransport::Result>> results;
+ results.reserve(num_queries);
+ for (int i = 0; i < num_queries; ++i) {
+ queries[i] = make_query(i, SIZE + (i % 2));
+ results.push_back(transport.query(makeSlice(queries[i])));
+ }
+ // Just check the short queries, which are at the even indices.
+ for (int i = 0; i < num_queries; i += 2) {
+ auto r = results[i].get();
+ EXPECT_EQ(DnsTlsTransport::Response::success, r.code);
+ EXPECT_EQ(queries[i], r.response);
+ }
+}
+
+// Simulate a malfunctioning server that injects extra miscellaneous
+// responses to queries that were not asked. This will cause wrong answers but
+// must not crash the Transport.
+class FakeSocketGarbage : public IDnsTlsSocket {
+ public:
+ explicit FakeSocketGarbage(IDnsTlsSocketObserver* observer) : mObserver(observer) {
+ // Inject a garbage event.
+ mThreads.emplace_back(&IDnsTlsSocketObserver::onResponse, mObserver, make_query(ID + 1, SIZE));
+ }
+ ~FakeSocketGarbage() {
+ std::lock_guard guard(mLock);
+ for (auto& thread : mThreads) {
+ thread.join();
+ }
+ }
+ bool query(uint16_t id, const Slice query) override {
+ std::lock_guard guard(mLock);
+ // Return the response twice.
+ auto echo = make_echo(id, query);
+ mThreads.emplace_back(&IDnsTlsSocketObserver::onResponse, mObserver, echo);
+ mThreads.emplace_back(&IDnsTlsSocketObserver::onResponse, mObserver, echo);
+ // Also return some other garbage
+ mThreads.emplace_back(&IDnsTlsSocketObserver::onResponse, mObserver, make_query(id + 1, query.size() + 2));
+ return true;
+ }
+
+ private:
+ std::mutex mLock;
+ std::vector<std::thread> mThreads GUARDED_BY(mLock);
+ IDnsTlsSocketObserver* const mObserver;
+};
+
+TEST_F(TransportTest, IgnoringGarbage) {
+ FakeSocketFactory<FakeSocketGarbage> factory;
+ DnsTlsTransport transport(SERVER1, MARK, &factory);
+ for (int i = 0; i < 10; ++i) {
+ auto r = transport.query(makeSlice(QUERY)).get();
+
+ EXPECT_EQ(DnsTlsTransport::Response::success, r.code);
+ // Don't check the response because this server is malfunctioning.
+ }
+}
+
+// Dispatcher tests
+class DispatcherTest : public BaseTest {};
+
+TEST_F(DispatcherTest, Query) {
+ bytevec ans(4096);
+ int resplen = 0;
+
+ auto factory = std::make_unique<FakeSocketFactory<FakeSocketEcho>>();
+ DnsTlsDispatcher dispatcher(std::move(factory));
+ auto r = dispatcher.query(SERVER1, MARK, makeSlice(QUERY),
+ makeSlice(ans), &resplen);
+
+ EXPECT_EQ(DnsTlsTransport::Response::success, r);
+ EXPECT_EQ(int(QUERY.size()), resplen);
+ ans.resize(resplen);
+ EXPECT_EQ(QUERY, ans);
+}
+
+TEST_F(DispatcherTest, AnswerTooLarge) {
+ bytevec ans(SIZE - 1); // Too small to hold the answer
+ int resplen = 0;
+
+ auto factory = std::make_unique<FakeSocketFactory<FakeSocketEcho>>();
+ DnsTlsDispatcher dispatcher(std::move(factory));
+ auto r = dispatcher.query(SERVER1, MARK, makeSlice(QUERY),
+ makeSlice(ans), &resplen);
+
+ EXPECT_EQ(DnsTlsTransport::Response::limit_error, r);
+}
+
+template<class T>
+class TrackingFakeSocketFactory : public IDnsTlsSocketFactory {
+ public:
+ TrackingFakeSocketFactory() {}
+ std::unique_ptr<IDnsTlsSocket> createDnsTlsSocket(
+ const DnsTlsServer& server,
+ unsigned mark,
+ IDnsTlsSocketObserver* observer,
+ DnsTlsSessionCache* cache ATTRIBUTE_UNUSED) override {
+ std::lock_guard guard(mLock);
+ keys.emplace(mark, server);
+ return std::make_unique<T>(observer);
+ }
+ std::multiset<std::pair<unsigned, DnsTlsServer>> keys;
+
+ private:
+ std::mutex mLock;
+};
+
+TEST_F(DispatcherTest, Dispatching) {
+ FakeSocketDelay::sDelay = 5;
+ FakeSocketDelay::sReverse = true;
+ auto factory = std::make_unique<TrackingFakeSocketFactory<FakeSocketDelay>>();
+ auto* weak_factory = factory.get(); // Valid as long as dispatcher is in scope.
+ DnsTlsDispatcher dispatcher(std::move(factory));
+
+ // Populate a vector of two servers and two socket marks, four combinations
+ // in total.
+ std::vector<std::pair<unsigned, DnsTlsServer>> keys;
+ keys.emplace_back(MARK, SERVER1);
+ keys.emplace_back(MARK + 1, SERVER1);
+ keys.emplace_back(MARK, V4ADDR2);
+ keys.emplace_back(MARK + 1, V4ADDR2);
+
+ // Do several queries on each server. They should all succeed.
+ std::vector<std::thread> threads;
+ for (size_t i = 0; i < FakeSocketDelay::sDelay * keys.size(); ++i) {
+ auto key = keys[i % keys.size()];
+ threads.emplace_back([key, i] (DnsTlsDispatcher* dispatcher) {
+ auto q = make_query(i, SIZE);
+ bytevec ans(4096);
+ int resplen = 0;
+ unsigned mark = key.first;
+ const DnsTlsServer& server = key.second;
+ auto r = dispatcher->query(server, mark, makeSlice(q),
+ makeSlice(ans), &resplen);
+ EXPECT_EQ(DnsTlsTransport::Response::success, r);
+ EXPECT_EQ(int(q.size()), resplen);
+ ans.resize(resplen);
+ EXPECT_EQ(q, ans);
+ }, &dispatcher);
+ }
+ for (auto& thread : threads) {
+ thread.join();
+ }
+ // We expect that the factory created one socket for each key.
+ EXPECT_EQ(keys.size(), weak_factory->keys.size());
+ for (auto& key : keys) {
+ EXPECT_EQ(1U, weak_factory->keys.count(key));
+ }
+}
+
+// Check DnsTlsServer's comparison logic.
+AddressComparator ADDRESS_COMPARATOR;
+bool isAddressEqual(const DnsTlsServer& s1, const DnsTlsServer& s2) {
+ bool cmp1 = ADDRESS_COMPARATOR(s1, s2);
+ bool cmp2 = ADDRESS_COMPARATOR(s2, s1);
+ EXPECT_FALSE(cmp1 && cmp2);
+ return !cmp1 && !cmp2;
+}
+
+void checkUnequal(const DnsTlsServer& s1, const DnsTlsServer& s2) {
+ EXPECT_TRUE(s1 == s1);
+ EXPECT_TRUE(s2 == s2);
+ EXPECT_TRUE(isAddressEqual(s1, s1));
+ EXPECT_TRUE(isAddressEqual(s2, s2));
+
+ EXPECT_TRUE(s1 < s2 ^ s2 < s1);
+ EXPECT_FALSE(s1 == s2);
+ EXPECT_FALSE(s2 == s1);
+}
+
+class ServerTest : public BaseTest {};
+
+TEST_F(ServerTest, IPv4) {
+ checkUnequal(V4ADDR1, V4ADDR2);
+ EXPECT_FALSE(isAddressEqual(V4ADDR1, V4ADDR2));
+}
+
+TEST_F(ServerTest, IPv6) {
+ checkUnequal(V6ADDR1, V6ADDR2);
+ EXPECT_FALSE(isAddressEqual(V6ADDR1, V6ADDR2));
+}
+
+TEST_F(ServerTest, MixedAddressFamily) {
+ checkUnequal(V6ADDR1, V4ADDR1);
+ EXPECT_FALSE(isAddressEqual(V6ADDR1, V4ADDR1));
+}
+
+TEST_F(ServerTest, IPv6ScopeId) {
+ DnsTlsServer s1(V6ADDR1), s2(V6ADDR1);
+ sockaddr_in6* addr1 = reinterpret_cast<sockaddr_in6*>(&s1.ss);
+ addr1->sin6_scope_id = 1;
+ sockaddr_in6* addr2 = reinterpret_cast<sockaddr_in6*>(&s2.ss);
+ addr2->sin6_scope_id = 2;
+ checkUnequal(s1, s2);
+ EXPECT_FALSE(isAddressEqual(s1, s2));
+
+ EXPECT_FALSE(s1.wasExplicitlyConfigured());
+ EXPECT_FALSE(s2.wasExplicitlyConfigured());
+}
+
+TEST_F(ServerTest, IPv6FlowInfo) {
+ DnsTlsServer s1(V6ADDR1), s2(V6ADDR1);
+ sockaddr_in6* addr1 = reinterpret_cast<sockaddr_in6*>(&s1.ss);
+ addr1->sin6_flowinfo = 1;
+ sockaddr_in6* addr2 = reinterpret_cast<sockaddr_in6*>(&s2.ss);
+ addr2->sin6_flowinfo = 2;
+ // All comparisons ignore flowinfo.
+ EXPECT_EQ(s1, s2);
+ EXPECT_TRUE(isAddressEqual(s1, s2));
+
+ EXPECT_FALSE(s1.wasExplicitlyConfigured());
+ EXPECT_FALSE(s2.wasExplicitlyConfigured());
+}
+
+TEST_F(ServerTest, Port) {
+ DnsTlsServer s1, s2;
+ parseServer("192.0.2.1", 853, &s1.ss);
+ parseServer("192.0.2.1", 854, &s2.ss);
+ checkUnequal(s1, s2);
+ EXPECT_TRUE(isAddressEqual(s1, s2));
+
+ DnsTlsServer s3, s4;
+ parseServer("2001:db8::1", 853, &s3.ss);
+ parseServer("2001:db8::1", 852, &s4.ss);
+ checkUnequal(s3, s4);
+ EXPECT_TRUE(isAddressEqual(s3, s4));
+
+ EXPECT_FALSE(s1.wasExplicitlyConfigured());
+ EXPECT_FALSE(s2.wasExplicitlyConfigured());
+}
+
+TEST_F(ServerTest, Name) {
+ DnsTlsServer s1(V4ADDR1), s2(V4ADDR1);
+ s1.name = SERVERNAME1;
+ checkUnequal(s1, s2);
+ s2.name = SERVERNAME2;
+ checkUnequal(s1, s2);
+ EXPECT_TRUE(isAddressEqual(s1, s2));
+
+ EXPECT_TRUE(s1.wasExplicitlyConfigured());
+ EXPECT_TRUE(s2.wasExplicitlyConfigured());
+}
+
+TEST_F(ServerTest, Fingerprint) {
+ DnsTlsServer s1(V4ADDR1), s2(V4ADDR1);
+
+ s1.fingerprints.insert(FINGERPRINT1);
+ checkUnequal(s1, s2);
+ EXPECT_TRUE(isAddressEqual(s1, s2));
+
+ s2.fingerprints.insert(FINGERPRINT2);
+ checkUnequal(s1, s2);
+ EXPECT_TRUE(isAddressEqual(s1, s2));
+
+ s2.fingerprints.insert(FINGERPRINT1);
+ checkUnequal(s1, s2);
+ EXPECT_TRUE(isAddressEqual(s1, s2));
+
+ s1.fingerprints.insert(FINGERPRINT2);
+ EXPECT_EQ(s1, s2);
+ EXPECT_TRUE(isAddressEqual(s1, s2));
+
+ EXPECT_TRUE(s1.wasExplicitlyConfigured());
+ EXPECT_TRUE(s2.wasExplicitlyConfigured());
+}
+
+TEST(QueryMapTest, Basic) {
+ DnsTlsQueryMap map;
+
+ EXPECT_TRUE(map.empty());
+
+ bytevec q0 = make_query(999, SIZE);
+ bytevec q1 = make_query(888, SIZE);
+ bytevec q2 = make_query(777, SIZE);
+
+ auto f0 = map.recordQuery(makeSlice(q0));
+ auto f1 = map.recordQuery(makeSlice(q1));
+ auto f2 = map.recordQuery(makeSlice(q2));
+
+ // Check return values of recordQuery
+ EXPECT_EQ(0, f0->query.newId);
+ EXPECT_EQ(1, f1->query.newId);
+ EXPECT_EQ(2, f2->query.newId);
+
+ // Check side effects of recordQuery
+ EXPECT_FALSE(map.empty());
+
+ auto all = map.getAll();
+ EXPECT_EQ(3U, all.size());
+
+ EXPECT_EQ(0, all[0].newId);
+ EXPECT_EQ(1, all[1].newId);
+ EXPECT_EQ(2, all[2].newId);
+
+ EXPECT_EQ(makeSlice(q0), all[0].query);
+ EXPECT_EQ(makeSlice(q1), all[1].query);
+ EXPECT_EQ(makeSlice(q2), all[2].query);
+
+ bytevec a0 = make_query(0, SIZE);
+ bytevec a1 = make_query(1, SIZE);
+ bytevec a2 = make_query(2, SIZE);
+
+ // Return responses out of order
+ map.onResponse(a2);
+ map.onResponse(a0);
+ map.onResponse(a1);
+
+ EXPECT_TRUE(map.empty());
+
+ auto r0 = f0->result.get();
+ auto r1 = f1->result.get();
+ auto r2 = f2->result.get();
+
+ EXPECT_EQ(DnsTlsQueryMap::Response::success, r0.code);
+ EXPECT_EQ(DnsTlsQueryMap::Response::success, r1.code);
+ EXPECT_EQ(DnsTlsQueryMap::Response::success, r2.code);
+
+ const bytevec& d0 = r0.response;
+ const bytevec& d1 = r1.response;
+ const bytevec& d2 = r2.response;
+
+ // The ID should match the query
+ EXPECT_EQ(999, d0[0] << 8 | d0[1]);
+ EXPECT_EQ(888, d1[0] << 8 | d1[1]);
+ EXPECT_EQ(777, d2[0] << 8 | d2[1]);
+ // The body should match the answer
+ EXPECT_EQ(bytevec(a0.begin() + 2, a0.end()), bytevec(d0.begin() + 2, d0.end()));
+ EXPECT_EQ(bytevec(a1.begin() + 2, a1.end()), bytevec(d1.begin() + 2, d1.end()));
+ EXPECT_EQ(bytevec(a2.begin() + 2, a2.end()), bytevec(d2.begin() + 2, d2.end()));
+}
+
+TEST(QueryMapTest, FillHole) {
+ DnsTlsQueryMap map;
+ std::vector<std::unique_ptr<DnsTlsQueryMap::QueryFuture>> futures(UINT16_MAX + 1);
+ for (uint32_t i = 0; i <= UINT16_MAX; ++i) {
+ futures[i] = map.recordQuery(makeSlice(QUERY));
+ ASSERT_TRUE(futures[i]); // answers[i] should be nonnull.
+ EXPECT_EQ(i, futures[i]->query.newId);
+ }
+
+ // The map should now be full.
+ EXPECT_EQ(size_t(UINT16_MAX + 1), map.getAll().size());
+
+ // Trying to add another query should fail because the map is full.
+ EXPECT_FALSE(map.recordQuery(makeSlice(QUERY)));
+
+ // Send an answer to query 40000
+ auto answer = make_query(40000, SIZE);
+ map.onResponse(answer);
+ auto result = futures[40000]->result.get();
+ EXPECT_EQ(DnsTlsQueryMap::Response::success, result.code);
+ EXPECT_EQ(ID, result.response[0] << 8 | result.response[1]);
+ EXPECT_EQ(bytevec(answer.begin() + 2, answer.end()),
+ bytevec(result.response.begin() + 2, result.response.end()));
+
+ // There should now be room in the map.
+ EXPECT_EQ(size_t(UINT16_MAX), map.getAll().size());
+ auto f = map.recordQuery(makeSlice(QUERY));
+ ASSERT_TRUE(f);
+ EXPECT_EQ(40000, f->query.newId);
+
+ // The map should now be full again.
+ EXPECT_EQ(size_t(UINT16_MAX + 1), map.getAll().size());
+ EXPECT_FALSE(map.recordQuery(makeSlice(QUERY)));
+}
+
+class StubObserver : public IDnsTlsSocketObserver {
+ public:
+ bool closed = false;
+ void onResponse(std::vector<uint8_t>) override {}
+
+ void onClosed() override { closed = true; }
+};
+
+TEST(DnsTlsSocketTest, SlowDestructor) {
+ constexpr char tls_addr[] = "127.0.0.3";
+ constexpr char tls_port[] = "8530"; // High-numbered port so root isn't required.
+ // This test doesn't perform any queries, so the backend address can be invalid.
+ constexpr char backend_addr[] = "192.0.2.1";
+ constexpr char backend_port[] = "1";
+
+ test::DnsTlsFrontend tls(tls_addr, tls_port, backend_addr, backend_port);
+ ASSERT_TRUE(tls.startServer());
+
+ DnsTlsServer server;
+ parseServer(tls_addr, 8530, &server.ss);
+
+ StubObserver observer;
+ ASSERT_FALSE(observer.closed);
+ DnsTlsSessionCache cache;
+ auto socket = std::make_unique<DnsTlsSocket>(server, MARK, &observer, &cache);
+ ASSERT_TRUE(socket->initialize());
+
+ // Test: Time the socket destructor. This should be fast.
+ auto before = std::chrono::steady_clock::now();
+ socket.reset();
+ auto after = std::chrono::steady_clock::now();
+ auto delay = after - before;
+ ALOGV("Shutdown took %lld ns", delay / std::chrono::nanoseconds{1});
+ EXPECT_TRUE(observer.closed);
+ // Shutdown should complete in milliseconds, but if the shutdown signal is lost
+ // it will wait for the timeout, which is expected to take 20seconds.
+ EXPECT_LT(delay, std::chrono::seconds{5});
+}
+
+} // end of namespace net
+} // end of namespace android
diff --git a/resolv/dnsresolver_binder_test.cpp b/resolv/dnsresolver_binder_test.cpp
new file mode 100644
index 0000000..97dea53
--- /dev/null
+++ b/resolv/dnsresolver_binder_test.cpp
@@ -0,0 +1,373 @@
+/*
+ * Copyright 2019 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.
+ *
+ * binder_test.cpp - unit tests for netd binder RPCs.
+ */
+
+#ifdef NDEBUG
+#undef NDEBUG
+#endif
+
+#include <vector>
+
+#include <openssl/base64.h>
+
+#include <android-base/strings.h>
+#include <android/net/IDnsResolver.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <gmock/gmock-matchers.h>
+#include <gtest/gtest.h>
+#include <netdb.h>
+#include <netdutils/Stopwatch.h>
+#include "tests/BaseTestMetricsListener.h"
+#include "tests/TestMetrics.h"
+
+#include "NetdConstants.h" // SHA256_SIZE
+#include "ResolverStats.h"
+#include "dns_responder.h"
+#include "dns_responder_client.h"
+
+namespace binder = android::binder;
+
+using android::IBinder;
+using android::IServiceManager;
+using android::ProcessState;
+using android::sp;
+using android::String16;
+using android::String8;
+using android::net::IDnsResolver;
+using android::net::ResolverParamsParcel;
+using android::net::ResolverStats;
+using android::net::metrics::INetdEventListener;
+using android::net::metrics::TestOnDnsEvent;
+using android::netdutils::Stopwatch;
+
+// TODO: make this dynamic and stop depending on implementation details.
+// Sync from TEST_NETID in dns_responder_client.cpp as resolver_test.cpp does.
+constexpr int TEST_NETID = 30;
+
+class DnsResolverBinderTest : public ::testing::Test {
+ public:
+ DnsResolverBinderTest() {
+ sp<IServiceManager> sm = android::defaultServiceManager();
+ sp<IBinder> binder = sm->getService(String16("dnsresolver"));
+ if (binder != nullptr) {
+ mDnsResolver = android::interface_cast<IDnsResolver>(binder);
+ }
+ // This could happen when the test isn't running as root, or if netd isn't running.
+ assert(nullptr != mDnsResolver.get());
+ // Create cache for test
+ mDnsResolver->createNetworkCache(TEST_NETID);
+ }
+
+ ~DnsResolverBinderTest() {
+ // Destroy cache for test
+ mDnsResolver->destroyNetworkCache(TEST_NETID);
+ }
+
+ protected:
+ sp<IDnsResolver> mDnsResolver;
+};
+
+class TimedOperation : public Stopwatch {
+ public:
+ explicit TimedOperation(const std::string& name) : mName(name) {}
+ virtual ~TimedOperation() { fprintf(stderr, " %s: %6.1f ms\n", mName.c_str(), timeTaken()); }
+
+ private:
+ std::string mName;
+};
+
+namespace {
+
+std::string base64Encode(const std::vector<uint8_t>& input) {
+ size_t out_len;
+ EXPECT_EQ(1, EVP_EncodedLength(&out_len, input.size()));
+ // out_len includes the trailing NULL.
+ uint8_t output_bytes[out_len];
+ EXPECT_EQ(out_len - 1, EVP_EncodeBlock(output_bytes, input.data(), input.size()));
+ return std::string(reinterpret_cast<char*>(output_bytes));
+}
+
+// TODO: Convert tests to ResolverParamsParcel and delete this stub.
+ResolverParamsParcel makeResolverParamsParcel(int netId, const std::vector<int>& params,
+ const std::vector<std::string>& servers,
+ const std::vector<std::string>& domains,
+ const std::string& tlsHostname,
+ const std::vector<std::string>& tlsServers,
+ const std::vector<std::string>& tlsFingerprints) {
+ using android::net::IDnsResolver;
+ ResolverParamsParcel paramsParcel;
+
+ paramsParcel.netId = netId;
+ paramsParcel.sampleValiditySeconds = params[IDnsResolver::RESOLVER_PARAMS_SAMPLE_VALIDITY];
+ paramsParcel.successThreshold = params[IDnsResolver::RESOLVER_PARAMS_SUCCESS_THRESHOLD];
+ paramsParcel.minSamples = params[IDnsResolver::RESOLVER_PARAMS_MIN_SAMPLES];
+ paramsParcel.maxSamples = params[IDnsResolver::RESOLVER_PARAMS_MAX_SAMPLES];
+ if (params.size() > IDnsResolver::RESOLVER_PARAMS_BASE_TIMEOUT_MSEC) {
+ paramsParcel.baseTimeoutMsec = params[IDnsResolver::RESOLVER_PARAMS_BASE_TIMEOUT_MSEC];
+ } else {
+ paramsParcel.baseTimeoutMsec = 0;
+ }
+ if (params.size() > IDnsResolver::RESOLVER_PARAMS_RETRY_COUNT) {
+ paramsParcel.retryCount = params[IDnsResolver::RESOLVER_PARAMS_RETRY_COUNT];
+ } else {
+ paramsParcel.retryCount = 0;
+ }
+ paramsParcel.servers = servers;
+ paramsParcel.domains = domains;
+ paramsParcel.tlsName = tlsHostname;
+ paramsParcel.tlsServers = tlsServers;
+ paramsParcel.tlsFingerprints = tlsFingerprints;
+
+ return paramsParcel;
+}
+
+} // namespace
+
+TEST_F(DnsResolverBinderTest, IsAlive) {
+ TimedOperation t("isAlive RPC");
+ bool isAlive = false;
+ mDnsResolver->isAlive(&isAlive);
+ ASSERT_TRUE(isAlive);
+}
+
+// TODO: Move this test to resolver_test.cpp
+TEST_F(DnsResolverBinderTest, EventListener_onDnsEvent) {
+ // The test configs are used to trigger expected events. The expected results are defined in
+ // expectedResults.
+ static const struct TestConfig {
+ std::string hostname;
+ int returnCode;
+ } testConfigs[] = {
+ {"hi", 0 /*success*/},
+ {"nonexistent", EAI_NODATA},
+ };
+
+ // The expected results define expected event content for test verification.
+ static const std::vector<TestOnDnsEvent::TestResult> expectedResults = {
+ {TEST_NETID, INetdEventListener::EVENT_GETADDRINFO, 0 /*success*/, 1, "hi", "1.2.3.4"},
+ {TEST_NETID, INetdEventListener::EVENT_GETADDRINFO, EAI_NODATA, 0, "nonexistent", ""},
+ };
+
+ // Start the Binder thread pool.
+ // TODO: Consider doing this once if there has another event listener unit test.
+ android::ProcessState::self()->startThreadPool();
+
+ // Setup network.
+ // TODO: Setup device configuration and DNS responser server as resolver test does.
+ // Currently, leave DNS related configuration in this test because only it needs DNS
+ // client-server testing environment.
+ DnsResponderClient dnsClient;
+ dnsClient.SetUp();
+
+ // Setup DNS responder server.
+ constexpr char listen_addr[] = "127.0.0.3";
+ constexpr char listen_srv[] = "53";
+ test::DNSResponder dns(listen_addr, listen_srv, 250, ns_rcode::ns_r_servfail);
+ dns.addMapping("hi.example.com.", ns_type::ns_t_a, "1.2.3.4");
+ ASSERT_TRUE(dns.startServer());
+
+ // Setup DNS configuration.
+ const std::vector<std::string> test_servers = {listen_addr};
+ std::vector<std::string> test_domains = {"example.com"};
+ std::vector<int> test_params = {300 /*sample_validity*/, 25 /*success_threshold*/,
+ 8 /*min_samples*/, 8 /*max_samples*/};
+
+ ASSERT_TRUE(dnsClient.SetResolversForNetwork(test_servers, test_domains, test_params));
+ dns.clearQueries();
+
+ // Register event listener.
+ android::sp<TestOnDnsEvent> testOnDnsEvent = new TestOnDnsEvent(expectedResults);
+ android::binder::Status status = mDnsResolver->registerEventListener(
+ android::interface_cast<INetdEventListener>(testOnDnsEvent));
+ ASSERT_TRUE(status.isOk()) << status.exceptionMessage();
+
+ // DNS queries.
+ // Once all expected events of expectedResults are received by the listener, the unit test will
+ // be notified. Otherwise, notified with a timeout expired failure.
+ auto& cv = testOnDnsEvent->getCv();
+ auto& cvMutex = testOnDnsEvent->getCvMutex();
+ {
+ std::unique_lock lock(cvMutex);
+
+ for (const auto& config : testConfigs) {
+ SCOPED_TRACE(config.hostname);
+
+ addrinfo* result = nullptr;
+ addrinfo hints = {.ai_family = AF_INET, .ai_socktype = SOCK_DGRAM};
+ int status = getaddrinfo(config.hostname.c_str(), nullptr, &hints, &result);
+ EXPECT_EQ(config.returnCode, status);
+
+ if (result) freeaddrinfo(result);
+ }
+
+ // Wait for receiving expected events.
+ EXPECT_EQ(std::cv_status::no_timeout, cv.wait_for(lock, std::chrono::seconds(2)));
+ }
+
+ // Verify that all testcases are passed.
+ EXPECT_TRUE(testOnDnsEvent->isVerified());
+
+ dnsClient.TearDown();
+}
+
+TEST_F(DnsResolverBinderTest, SetResolverConfiguration_Tls) {
+ // Certificate fingerprints are no longer supported by the module.
+ SKIP_IF_RESOLVER_VERSION_NEWER_THAN(mDnsResolver, 2);
+
+ const std::vector<std::string> LOCALLY_ASSIGNED_DNS{"8.8.8.8", "2001:4860:4860::8888"};
+ std::vector<uint8_t> fp(SHA256_SIZE);
+ std::vector<uint8_t> short_fp(1);
+ std::vector<uint8_t> long_fp(SHA256_SIZE + 1);
+ std::vector<std::string> test_domains;
+ std::vector<int> test_params = {300, 25, 8, 8};
+ static const struct TestData {
+ const std::vector<std::string> servers;
+ const std::string tlsName;
+ const std::vector<std::vector<uint8_t>> tlsFingerprints;
+ const int expectedReturnCode;
+ } kTlsTestData[] = {
+ {{"192.0.2.1"}, "", {}, 0},
+ {{"2001:db8::2"}, "host.name", {}, 0},
+ {{"192.0.2.3"}, "@@@@", {fp}, 0},
+ {{"2001:db8::4"}, "", {fp}, 0},
+ {{}, "", {}, 0},
+ {{""}, "", {}, EINVAL},
+ {{"192.0.*.5"}, "", {}, EINVAL},
+ {{"2001:dg8::6"}, "", {}, EINVAL},
+ {{"2001:db8::c"}, "", {short_fp}, EINVAL},
+ {{"192.0.2.12"}, "", {long_fp}, EINVAL},
+ {{"2001:db8::e"}, "", {fp, fp, fp}, 0},
+ {{"192.0.2.14"}, "", {fp, short_fp}, EINVAL},
+ };
+
+ for (size_t i = 0; i < std::size(kTlsTestData); i++) {
+ const auto& td = kTlsTestData[i];
+
+ std::vector<std::string> fingerprints;
+ for (const auto& fingerprint : td.tlsFingerprints) {
+ fingerprints.push_back(base64Encode(fingerprint));
+ }
+ const auto resolverParams =
+ makeResolverParamsParcel(TEST_NETID, test_params, LOCALLY_ASSIGNED_DNS,
+ test_domains, td.tlsName, td.servers, fingerprints);
+ binder::Status status = mDnsResolver->setResolverConfiguration(resolverParams);
+
+ if (td.expectedReturnCode == 0) {
+ SCOPED_TRACE(String8::format("test case %zu should have passed", i));
+ SCOPED_TRACE(status.toString8());
+ EXPECT_EQ(0, status.exceptionCode());
+ } else {
+ SCOPED_TRACE(String8::format("test case %zu should have failed", i));
+ EXPECT_EQ(binder::Status::EX_SERVICE_SPECIFIC, status.exceptionCode());
+ EXPECT_EQ(td.expectedReturnCode, status.serviceSpecificErrorCode());
+ }
+ }
+}
+
+TEST_F(DnsResolverBinderTest, GetResolverInfo) {
+ std::vector<std::string> servers = {"127.0.0.1", "127.0.0.2"};
+ std::vector<std::string> domains = {"example.com"};
+ std::vector<int> testParams = {
+ 300, // sample validity in seconds
+ 25, // success threshod in percent
+ 8, 8, // {MIN,MAX}_SAMPLES
+ 100, // BASE_TIMEOUT_MSEC
+ 2, // retry count
+ };
+ const auto resolverParams =
+ makeResolverParamsParcel(TEST_NETID, testParams, servers, domains, "", {}, {});
+ binder::Status status = mDnsResolver->setResolverConfiguration(resolverParams);
+ EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+
+ std::vector<std::string> res_servers;
+ std::vector<std::string> res_domains;
+ std::vector<std::string> res_tls_servers;
+ std::vector<int32_t> params32;
+ std::vector<int32_t> stats32;
+ std::vector<int32_t> wait_for_pending_req_timeout_count32{0};
+ status = mDnsResolver->getResolverInfo(TEST_NETID, &res_servers, &res_domains, &res_tls_servers,
+ ¶ms32, &stats32,
+ &wait_for_pending_req_timeout_count32);
+
+ EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+ EXPECT_EQ(servers.size(), res_servers.size());
+ EXPECT_EQ(domains.size(), res_domains.size());
+ EXPECT_EQ(0U, res_tls_servers.size());
+ ASSERT_EQ(static_cast<size_t>(IDnsResolver::RESOLVER_PARAMS_COUNT), testParams.size());
+ EXPECT_EQ(testParams[IDnsResolver::RESOLVER_PARAMS_SAMPLE_VALIDITY],
+ params32[IDnsResolver::RESOLVER_PARAMS_SAMPLE_VALIDITY]);
+ EXPECT_EQ(testParams[IDnsResolver::RESOLVER_PARAMS_SUCCESS_THRESHOLD],
+ params32[IDnsResolver::RESOLVER_PARAMS_SUCCESS_THRESHOLD]);
+ EXPECT_EQ(testParams[IDnsResolver::RESOLVER_PARAMS_MIN_SAMPLES],
+ params32[IDnsResolver::RESOLVER_PARAMS_MIN_SAMPLES]);
+ EXPECT_EQ(testParams[IDnsResolver::RESOLVER_PARAMS_MAX_SAMPLES],
+ params32[IDnsResolver::RESOLVER_PARAMS_MAX_SAMPLES]);
+ EXPECT_EQ(testParams[IDnsResolver::RESOLVER_PARAMS_BASE_TIMEOUT_MSEC],
+ params32[IDnsResolver::RESOLVER_PARAMS_BASE_TIMEOUT_MSEC]);
+
+ std::vector<ResolverStats> stats;
+ ResolverStats::decodeAll(stats32, &stats);
+
+ EXPECT_EQ(servers.size(), stats.size());
+
+ EXPECT_THAT(res_servers, testing::UnorderedElementsAreArray(servers));
+ EXPECT_THAT(res_domains, testing::UnorderedElementsAreArray(domains));
+}
+
+TEST_F(DnsResolverBinderTest, CreateDestroyNetworkCache) {
+ // Must not be the same as TEST_NETID
+ const int ANOTHER_TEST_NETID = TEST_NETID + 1;
+
+ // Create a new network cache.
+ EXPECT_TRUE(mDnsResolver->createNetworkCache(ANOTHER_TEST_NETID).isOk());
+
+ // create it again, expect a EEXIST.
+ EXPECT_EQ(EEXIST,
+ mDnsResolver->createNetworkCache(ANOTHER_TEST_NETID).serviceSpecificErrorCode());
+
+ // destroy it.
+ EXPECT_TRUE(mDnsResolver->destroyNetworkCache(ANOTHER_TEST_NETID).isOk());
+
+ // re-create it
+ EXPECT_TRUE(mDnsResolver->createNetworkCache(ANOTHER_TEST_NETID).isOk());
+
+ // destroy it.
+ EXPECT_TRUE(mDnsResolver->destroyNetworkCache(ANOTHER_TEST_NETID).isOk());
+
+ // re-destroy it
+ EXPECT_TRUE(mDnsResolver->destroyNetworkCache(ANOTHER_TEST_NETID).isOk());
+}
+
+TEST_F(DnsResolverBinderTest, setLogSeverity) {
+ // Expect fail
+ EXPECT_EQ(EINVAL, mDnsResolver->setLogSeverity(-1).serviceSpecificErrorCode());
+
+ // Test set different log level
+ EXPECT_TRUE(mDnsResolver->setLogSeverity(IDnsResolver::DNS_RESOLVER_LOG_VERBOSE).isOk());
+
+ EXPECT_TRUE(mDnsResolver->setLogSeverity(IDnsResolver::DNS_RESOLVER_LOG_DEBUG).isOk());
+
+ EXPECT_TRUE(mDnsResolver->setLogSeverity(IDnsResolver::DNS_RESOLVER_LOG_INFO).isOk());
+
+ EXPECT_TRUE(mDnsResolver->setLogSeverity(IDnsResolver::DNS_RESOLVER_LOG_WARNING).isOk());
+
+ EXPECT_TRUE(mDnsResolver->setLogSeverity(IDnsResolver::DNS_RESOLVER_LOG_ERROR).isOk());
+
+ // Set back to default
+ EXPECT_TRUE(mDnsResolver->setLogSeverity(IDnsResolver::DNS_RESOLVER_LOG_WARNING).isOk());
+}
diff --git a/resolv/getaddrinfo.cpp b/resolv/getaddrinfo.cpp
new file mode 100644
index 0000000..bb9ef07
--- /dev/null
+++ b/resolv/getaddrinfo.cpp
@@ -0,0 +1,1832 @@
+/* $NetBSD: getaddrinfo.c,v 1.82 2006/03/25 12:09:40 rpaulo Exp $ */
+/* $KAME: getaddrinfo.c,v 1.29 2000/08/31 17:26:57 itojun Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#define LOG_TAG "getaddrinfo"
+
+#include "getaddrinfo.h"
+
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <net/if.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include <android-base/logging.h>
+
+#include "netd_resolv/resolv.h"
+#include "resolv_cache.h"
+#include "resolv_private.h"
+
+#define ANY 0
+
+using android::net::NetworkDnsEventReported;
+
+const char in_addrany[] = {0, 0, 0, 0};
+const char in_loopback[] = {127, 0, 0, 1};
+const char in6_addrany[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+const char in6_loopback[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1};
+
+const struct afd {
+ int a_af;
+ int a_addrlen;
+ int a_socklen;
+ int a_off;
+ const char* a_addrany;
+ const char* a_loopback;
+ int a_scoped;
+} afdl[] = {
+ {PF_INET6, sizeof(struct in6_addr), sizeof(struct sockaddr_in6),
+ offsetof(struct sockaddr_in6, sin6_addr), in6_addrany, in6_loopback, 1},
+ {PF_INET, sizeof(struct in_addr), sizeof(struct sockaddr_in),
+ offsetof(struct sockaddr_in, sin_addr), in_addrany, in_loopback, 0},
+ {0, 0, 0, 0, NULL, NULL, 0},
+};
+
+struct Explore {
+ int e_af;
+ int e_socktype;
+ int e_protocol;
+ int e_wild;
+#define WILD_AF(ex) ((ex).e_wild & 0x01)
+#define WILD_SOCKTYPE(ex) ((ex).e_wild & 0x02)
+#define WILD_PROTOCOL(ex) ((ex).e_wild & 0x04)
+};
+
+const Explore explore_options[] = {
+ {PF_INET6, SOCK_DGRAM, IPPROTO_UDP, 0x07},
+ {PF_INET6, SOCK_STREAM, IPPROTO_TCP, 0x07},
+ {PF_INET6, SOCK_RAW, ANY, 0x05},
+ {PF_INET, SOCK_DGRAM, IPPROTO_UDP, 0x07},
+ {PF_INET, SOCK_STREAM, IPPROTO_TCP, 0x07},
+ {PF_INET, SOCK_RAW, ANY, 0x05},
+ {PF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, 0x07},
+ {PF_UNSPEC, SOCK_STREAM, IPPROTO_TCP, 0x07},
+ {PF_UNSPEC, SOCK_RAW, ANY, 0x05},
+};
+
+#define PTON_MAX 16
+#define MAXPACKET (8 * 1024)
+
+typedef union {
+ HEADER hdr;
+ u_char buf[MAXPACKET];
+} querybuf;
+
+struct res_target {
+ struct res_target* next;
+ const char* name; /* domain name */
+ int qclass, qtype; /* class and type of query */
+ u_char* answer; /* buffer to put answer */
+ int anslen; /* size of answer buffer */
+ int n; /* result length */
+};
+
+static int str2number(const char*);
+static int explore_fqdn(const struct addrinfo*, const char*, const char*, struct addrinfo**,
+ const struct android_net_context*, NetworkDnsEventReported* event);
+static int explore_null(const struct addrinfo*, const char*, struct addrinfo**);
+static int explore_numeric(const struct addrinfo*, const char*, const char*, struct addrinfo**,
+ const char*);
+static int explore_numeric_scope(const struct addrinfo*, const char*, const char*,
+ struct addrinfo**);
+static int get_canonname(const struct addrinfo*, struct addrinfo*, const char*);
+static struct addrinfo* get_ai(const struct addrinfo*, const struct afd*, const char*);
+static int get_portmatch(const struct addrinfo*, const char*);
+static int get_port(const struct addrinfo*, const char*, int);
+static const struct afd* find_afd(int);
+static int ip6_str2scopeid(const char*, struct sockaddr_in6*, u_int32_t*);
+
+static struct addrinfo* getanswer(const querybuf*, int, const char*, int, const struct addrinfo*,
+ int* herrno);
+static int dns_getaddrinfo(const char* name, const addrinfo* pai,
+ const android_net_context* netcontext, addrinfo** rv,
+ NetworkDnsEventReported* event);
+static void _sethtent(FILE**);
+static void _endhtent(FILE**);
+static struct addrinfo* _gethtent(FILE**, const char*, const struct addrinfo*);
+static bool files_getaddrinfo(const char* name, const addrinfo* pai, addrinfo** res);
+static int _find_src_addr(const struct sockaddr*, struct sockaddr*, unsigned, uid_t);
+
+static int res_queryN(const char* name, res_target* target, res_state res, int* herrno);
+static int res_searchN(const char* name, res_target* target, res_state res, int* herrno);
+static int res_querydomainN(const char* name, const char* domain, res_target* target, res_state res,
+ int* herrno);
+
+const char* const ai_errlist[] = {
+ "Success",
+ "Address family for hostname not supported", /* EAI_ADDRFAMILY */
+ "Temporary failure in name resolution", /* EAI_AGAIN */
+ "Invalid value for ai_flags", /* EAI_BADFLAGS */
+ "Non-recoverable failure in name resolution", /* EAI_FAIL */
+ "ai_family not supported", /* EAI_FAMILY */
+ "Memory allocation failure", /* EAI_MEMORY */
+ "No address associated with hostname", /* EAI_NODATA */
+ "hostname nor servname provided, or not known", /* EAI_NONAME */
+ "servname not supported for ai_socktype", /* EAI_SERVICE */
+ "ai_socktype not supported", /* EAI_SOCKTYPE */
+ "System error returned in errno", /* EAI_SYSTEM */
+ "Invalid value for hints", /* EAI_BADHINTS */
+ "Resolved protocol is unknown", /* EAI_PROTOCOL */
+ "Argument buffer overflow", /* EAI_OVERFLOW */
+ "Unknown error", /* EAI_MAX */
+};
+
+/* XXX macros that make external reference is BAD. */
+
+#define GET_AI(ai, afd, addr) \
+ do { \
+ /* external reference: pai, error, and label free */ \
+ (ai) = get_ai(pai, (afd), (addr)); \
+ if ((ai) == NULL) { \
+ error = EAI_MEMORY; \
+ goto free; \
+ } \
+ } while (0)
+
+#define GET_PORT(ai, serv) \
+ do { \
+ /* external reference: error and label free */ \
+ error = get_port((ai), (serv), 0); \
+ if (error != 0) goto free; \
+ } while (0)
+
+#define MATCH_FAMILY(x, y, w) \
+ ((x) == (y) || ((w) && ((x) == PF_UNSPEC || (y) == PF_UNSPEC)))
+#define MATCH(x, y, w) ((x) == (y) || ((w) && ((x) == ANY || (y) == ANY)))
+
+const char* gai_strerror(int ecode) {
+ if (ecode < 0 || ecode > EAI_MAX) ecode = EAI_MAX;
+ return ai_errlist[ecode];
+}
+
+void freeaddrinfo(struct addrinfo* ai) {
+ while (ai) {
+ struct addrinfo* next = ai->ai_next;
+ if (ai->ai_canonname) free(ai->ai_canonname);
+ // Also frees ai->ai_addr which points to extra space beyond addrinfo
+ free(ai);
+ ai = next;
+ }
+}
+
+static int str2number(const char* p) {
+ char* ep;
+ unsigned long v;
+
+ assert(p != NULL);
+
+ if (*p == '\0') return -1;
+ ep = NULL;
+ errno = 0;
+ v = strtoul(p, &ep, 10);
+ if (errno == 0 && ep && *ep == '\0' && v <= UINT_MAX)
+ return v;
+ else
+ return -1;
+}
+
+/*
+ * The following functions determine whether IPv4 or IPv6 connectivity is
+ * available in order to implement AI_ADDRCONFIG.
+ *
+ * Strictly speaking, AI_ADDRCONFIG should not look at whether connectivity is
+ * available, but whether addresses of the specified family are "configured
+ * on the local system". However, bionic doesn't currently support getifaddrs,
+ * so checking for connectivity is the next best thing.
+ */
+static int have_ipv6(unsigned mark, uid_t uid) {
+ static const struct sockaddr_in6 sin6_test = {
+ .sin6_family = AF_INET6,
+ .sin6_addr.s6_addr = {// 2000::
+ 0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
+ sockaddr_union addr = {.sin6 = sin6_test};
+ return _find_src_addr(&addr.sa, NULL, mark, uid) == 1;
+}
+
+static int have_ipv4(unsigned mark, uid_t uid) {
+ static const struct sockaddr_in sin_test = {
+ .sin_family = AF_INET,
+ .sin_addr.s_addr = __constant_htonl(0x08080808L) // 8.8.8.8
+ };
+ sockaddr_union addr = {.sin = sin_test};
+ return _find_src_addr(&addr.sa, NULL, mark, uid) == 1;
+}
+
+// Internal version of getaddrinfo(), but limited to AI_NUMERICHOST.
+// NOTE: also called by resolv_set_nameservers_for_net().
+int getaddrinfo_numeric(const char* hostname, const char* servname, addrinfo hints,
+ addrinfo** result) {
+ hints.ai_flags = AI_NUMERICHOST;
+ const android_net_context netcontext = {
+ .app_netid = NETID_UNSET,
+ .app_mark = MARK_UNSET,
+ .dns_netid = NETID_UNSET,
+ .dns_mark = MARK_UNSET,
+ .uid = NET_CONTEXT_INVALID_UID,
+ };
+ NetworkDnsEventReported event;
+ return android_getaddrinfofornetcontext(hostname, servname, &hints, &netcontext, result,
+ &event);
+}
+
+int android_getaddrinfofornetcontext(const char* hostname, const char* servname,
+ const struct addrinfo* hints,
+ const struct android_net_context* netcontext,
+ struct addrinfo** res, NetworkDnsEventReported* event) {
+ struct addrinfo sentinel = {};
+ struct addrinfo* cur = &sentinel;
+ int error = 0;
+
+ // hostname is allowed to be nullptr
+ // servname is allowed to be nullptr
+ // hints is allowed to be nullptr
+ assert(res != nullptr);
+ assert(netcontext != nullptr);
+ assert(event != nullptr);
+
+ struct addrinfo ai = {
+ .ai_flags = 0,
+ .ai_family = PF_UNSPEC,
+ .ai_socktype = ANY,
+ .ai_protocol = ANY,
+ .ai_addrlen = 0,
+ .ai_canonname = nullptr,
+ .ai_addr = nullptr,
+ .ai_next = nullptr,
+ };
+
+ do {
+ if (hostname == NULL && servname == NULL) {
+ error = EAI_NONAME;
+ break;
+ }
+ if (hints) {
+ /* error check for hints */
+ if (hints->ai_addrlen || hints->ai_canonname || hints->ai_addr || hints->ai_next) {
+ error = EAI_BADHINTS;
+ break;
+ }
+ if (hints->ai_flags & ~AI_MASK) {
+ error = EAI_BADFLAGS;
+ break;
+ }
+
+ if (!(hints->ai_family == PF_UNSPEC || hints->ai_family == PF_INET ||
+ hints->ai_family == PF_INET6)) {
+ error = EAI_FAMILY;
+ break;
+ }
+
+ ai = *hints;
+
+ /*
+ * if both socktype/protocol are specified, check if they
+ * are meaningful combination.
+ */
+ if (ai.ai_socktype != ANY && ai.ai_protocol != ANY) {
+ for (const Explore& ex : explore_options) {
+ if (ai.ai_family != ex.e_af) continue;
+ if (ex.e_socktype == ANY) continue;
+ if (ex.e_protocol == ANY) continue;
+ if (ai.ai_socktype == ex.e_socktype && ai.ai_protocol != ex.e_protocol) {
+ error = EAI_BADHINTS;
+ break;
+ }
+ }
+ if (error) break;
+ }
+ }
+
+ /*
+ * Check for special cases:
+ * (1) numeric servname is disallowed if socktype/protocol are left unspecified.
+ * (2) servname is disallowed for raw and other inet{,6} sockets.
+ */
+ if (MATCH_FAMILY(ai.ai_family, PF_INET, 1) || MATCH_FAMILY(ai.ai_family, PF_INET6, 1)) {
+ struct addrinfo tmp = ai;
+ if (tmp.ai_family == PF_UNSPEC) {
+ tmp.ai_family = PF_INET6;
+ }
+ error = get_portmatch(&tmp, servname);
+ if (error) break;
+ }
+
+ // NULL hostname, or numeric hostname
+ for (const Explore& ex : explore_options) {
+ /* PF_UNSPEC entries are prepared for DNS queries only */
+ if (ex.e_af == PF_UNSPEC) continue;
+
+ if (!MATCH_FAMILY(ai.ai_family, ex.e_af, WILD_AF(ex))) continue;
+ if (!MATCH(ai.ai_socktype, ex.e_socktype, WILD_SOCKTYPE(ex))) continue;
+ if (!MATCH(ai.ai_protocol, ex.e_protocol, WILD_PROTOCOL(ex))) continue;
+
+ struct addrinfo tmp = ai;
+ if (tmp.ai_family == PF_UNSPEC) tmp.ai_family = ex.e_af;
+ if (tmp.ai_socktype == ANY && ex.e_socktype != ANY) tmp.ai_socktype = ex.e_socktype;
+ if (tmp.ai_protocol == ANY && ex.e_protocol != ANY) tmp.ai_protocol = ex.e_protocol;
+
+ LOG(DEBUG) << __func__ << ": explore_numeric: ai_family=" << tmp.ai_family
+ << " ai_socktype=" << tmp.ai_socktype << " ai_protocol=" << tmp.ai_protocol;
+ if (hostname == nullptr)
+ error = explore_null(&tmp, servname, &cur->ai_next);
+ else
+ error = explore_numeric_scope(&tmp, hostname, servname, &cur->ai_next);
+
+ if (error) break;
+
+ while (cur->ai_next) cur = cur->ai_next;
+ }
+ if (error) break;
+
+ /*
+ * XXX
+ * If numeric representation of AF1 can be interpreted as FQDN
+ * representation of AF2, we need to think again about the code below.
+ */
+ if (sentinel.ai_next) break;
+
+ if (hostname == nullptr) {
+ error = EAI_NODATA;
+ break;
+ }
+ if (ai.ai_flags & AI_NUMERICHOST) {
+ error = EAI_NONAME;
+ break;
+ }
+
+ /*
+ * hostname as alphabetical name.
+ * We would like to prefer AF_INET6 over AF_INET, so we'll make a outer loop by AFs.
+ */
+ for (const Explore& ex : explore_options) {
+ // Require exact match for family field
+ if (ai.ai_family != ex.e_af) continue;
+
+ if (!MATCH(ai.ai_socktype, ex.e_socktype, WILD_SOCKTYPE(ex))) {
+ continue;
+ }
+ if (!MATCH(ai.ai_protocol, ex.e_protocol, WILD_PROTOCOL(ex))) {
+ continue;
+ }
+
+ struct addrinfo tmp = ai;
+ if (tmp.ai_socktype == ANY && ex.e_socktype != ANY) tmp.ai_socktype = ex.e_socktype;
+ if (tmp.ai_protocol == ANY && ex.e_protocol != ANY) tmp.ai_protocol = ex.e_protocol;
+
+ LOG(DEBUG) << __func__ << ": explore_fqdn(): ai_family=" << tmp.ai_family
+ << " ai_socktype=" << tmp.ai_socktype << " ai_protocol=" << tmp.ai_protocol;
+ error = explore_fqdn(&tmp, hostname, servname, &cur->ai_next, netcontext, event);
+
+ while (cur->ai_next) cur = cur->ai_next;
+ }
+
+ if (sentinel.ai_next) {
+ error = 0;
+ } else if (error == 0) {
+ error = EAI_FAIL;
+ }
+ } while (0);
+
+ if (error) {
+ freeaddrinfo(sentinel.ai_next);
+ *res = nullptr;
+ } else {
+ *res = sentinel.ai_next;
+ }
+ return error;
+}
+
+// FQDN hostname, DNS lookup
+static int explore_fqdn(const struct addrinfo* pai, const char* hostname, const char* servname,
+ struct addrinfo** res, const struct android_net_context* netcontext,
+ NetworkDnsEventReported* event) {
+ struct addrinfo* result;
+ int error = 0;
+
+ assert(pai != NULL);
+ /* hostname may be NULL */
+ /* servname may be NULL */
+ assert(res != NULL);
+
+ result = NULL;
+
+ // If the servname does not match socktype/protocol, ignore it.
+ if (get_portmatch(pai, servname) != 0) return 0;
+
+ if (!files_getaddrinfo(hostname, pai, &result)) {
+ error = dns_getaddrinfo(hostname, pai, netcontext, &result, event);
+ }
+ if (!error) {
+ struct addrinfo* cur;
+ for (cur = result; cur; cur = cur->ai_next) {
+ GET_PORT(cur, servname);
+ /* canonname should be filled already */
+ }
+ *res = result;
+ return 0;
+ }
+
+free:
+ freeaddrinfo(result);
+ return error;
+}
+
+/*
+ * hostname == NULL.
+ * passive socket -> anyaddr (0.0.0.0 or ::)
+ * non-passive socket -> localhost (127.0.0.1 or ::1)
+ */
+static int explore_null(const struct addrinfo* pai, const char* servname, struct addrinfo** res) {
+ int s;
+ const struct afd* afd;
+ struct addrinfo* cur;
+ struct addrinfo sentinel;
+ int error;
+
+ LOG(DEBUG) << __func__;
+
+ assert(pai != NULL);
+ /* servname may be NULL */
+ assert(res != NULL);
+
+ *res = NULL;
+ sentinel.ai_next = NULL;
+ cur = &sentinel;
+
+ /*
+ * filter out AFs that are not supported by the kernel
+ * XXX errno?
+ */
+ s = socket(pai->ai_family, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+ if (s < 0) {
+ if (errno != EMFILE) return 0;
+ } else
+ close(s);
+
+ /*
+ * if the servname does not match socktype/protocol, ignore it.
+ */
+ if (get_portmatch(pai, servname) != 0) return 0;
+
+ afd = find_afd(pai->ai_family);
+ if (afd == NULL) return 0;
+
+ if (pai->ai_flags & AI_PASSIVE) {
+ GET_AI(cur->ai_next, afd, afd->a_addrany);
+ GET_PORT(cur->ai_next, servname);
+ } else {
+ GET_AI(cur->ai_next, afd, afd->a_loopback);
+ GET_PORT(cur->ai_next, servname);
+ }
+ cur = cur->ai_next;
+
+ *res = sentinel.ai_next;
+ return 0;
+
+free:
+ freeaddrinfo(sentinel.ai_next);
+ return error;
+}
+
+/*
+ * numeric hostname
+ */
+static int explore_numeric(const struct addrinfo* pai, const char* hostname, const char* servname,
+ struct addrinfo** res, const char* canonname) {
+ const struct afd* afd;
+ struct addrinfo* cur;
+ struct addrinfo sentinel;
+ int error;
+ char pton[PTON_MAX];
+
+ assert(pai != NULL);
+ /* hostname may be NULL */
+ /* servname may be NULL */
+ assert(res != NULL);
+
+ *res = NULL;
+ sentinel.ai_next = NULL;
+ cur = &sentinel;
+
+ /*
+ * if the servname does not match socktype/protocol, ignore it.
+ */
+ if (get_portmatch(pai, servname) != 0) return 0;
+
+ afd = find_afd(pai->ai_family);
+ if (afd == NULL) return 0;
+
+ if (inet_pton(afd->a_af, hostname, pton) == 1) {
+ if (pai->ai_family == afd->a_af || pai->ai_family == PF_UNSPEC /*?*/) {
+ GET_AI(cur->ai_next, afd, pton);
+ GET_PORT(cur->ai_next, servname);
+ if ((pai->ai_flags & AI_CANONNAME)) {
+ /*
+ * Set the numeric address itself as
+ * the canonical name, based on a
+ * clarification in rfc2553bis-03.
+ */
+ error = get_canonname(pai, cur->ai_next, canonname);
+ if (error != 0) {
+ freeaddrinfo(sentinel.ai_next);
+ return error;
+ }
+ }
+ while (cur->ai_next) cur = cur->ai_next;
+ } else
+ return EAI_FAMILY;
+ }
+
+ *res = sentinel.ai_next;
+ return 0;
+
+free:
+ freeaddrinfo(sentinel.ai_next);
+ return error;
+}
+
+/*
+ * numeric hostname with scope
+ */
+static int explore_numeric_scope(const struct addrinfo* pai, const char* hostname,
+ const char* servname, struct addrinfo** res) {
+ const struct afd* afd;
+ struct addrinfo* cur;
+ int error;
+ const char *cp, *scope, *addr;
+ struct sockaddr_in6* sin6;
+
+ LOG(DEBUG) << __func__;
+
+ assert(pai != NULL);
+ /* hostname may be NULL */
+ /* servname may be NULL */
+ assert(res != NULL);
+
+ /*
+ * if the servname does not match socktype/protocol, ignore it.
+ */
+ if (get_portmatch(pai, servname) != 0) return 0;
+
+ afd = find_afd(pai->ai_family);
+ if (afd == NULL) return 0;
+
+ if (!afd->a_scoped) return explore_numeric(pai, hostname, servname, res, hostname);
+
+ cp = strchr(hostname, SCOPE_DELIMITER);
+ if (cp == NULL) return explore_numeric(pai, hostname, servname, res, hostname);
+
+ /*
+ * Handle special case of <scoped_address><delimiter><scope id>
+ */
+ char* hostname2 = strdup(hostname);
+ if (hostname2 == NULL) return EAI_MEMORY;
+ /* terminate at the delimiter */
+ hostname2[cp - hostname] = '\0';
+ addr = hostname2;
+ scope = cp + 1;
+
+ error = explore_numeric(pai, addr, servname, res, hostname);
+ if (error == 0) {
+ u_int32_t scopeid;
+
+ for (cur = *res; cur; cur = cur->ai_next) {
+ if (cur->ai_family != AF_INET6) continue;
+ sin6 = (struct sockaddr_in6*) (void*) cur->ai_addr;
+ if (ip6_str2scopeid(scope, sin6, &scopeid) == -1) {
+ free(hostname2);
+ return (EAI_NODATA); /* XXX: is return OK? */
+ }
+ sin6->sin6_scope_id = scopeid;
+ }
+ }
+
+ free(hostname2);
+
+ return error;
+}
+
+static int get_canonname(const struct addrinfo* pai, struct addrinfo* ai, const char* str) {
+ assert(pai != NULL);
+ assert(ai != NULL);
+ assert(str != NULL);
+
+ if ((pai->ai_flags & AI_CANONNAME) != 0) {
+ ai->ai_canonname = strdup(str);
+ if (ai->ai_canonname == NULL) return EAI_MEMORY;
+ }
+ return 0;
+}
+
+static struct addrinfo* get_ai(const struct addrinfo* pai, const struct afd* afd,
+ const char* addr) {
+ char* p;
+ struct addrinfo* ai;
+
+ assert(pai != NULL);
+ assert(afd != NULL);
+ assert(addr != NULL);
+
+ ai = (struct addrinfo*) malloc(sizeof(struct addrinfo) + sizeof(sockaddr_union));
+ if (ai == NULL) return NULL;
+
+ memcpy(ai, pai, sizeof(struct addrinfo));
+ ai->ai_addr = (struct sockaddr*) (void*) (ai + 1);
+ memset(ai->ai_addr, 0, sizeof(sockaddr_union));
+
+ ai->ai_addrlen = afd->a_socklen;
+ ai->ai_addr->sa_family = ai->ai_family = afd->a_af;
+ p = (char*) (void*) (ai->ai_addr);
+ memcpy(p + afd->a_off, addr, (size_t) afd->a_addrlen);
+ return ai;
+}
+
+static int get_portmatch(const struct addrinfo* ai, const char* servname) {
+ assert(ai != NULL);
+ /* servname may be NULL */
+
+ return get_port(ai, servname, 1);
+}
+
+static int get_port(const struct addrinfo* ai, const char* servname, int matchonly) {
+ const char* proto;
+ struct servent* sp;
+ int port;
+ int allownumeric;
+
+ assert(ai != NULL);
+ /* servname may be NULL */
+
+ if (servname == NULL) return 0;
+ switch (ai->ai_family) {
+ case AF_INET:
+ case AF_INET6:
+ break;
+ default:
+ return 0;
+ }
+
+ switch (ai->ai_socktype) {
+ case SOCK_RAW:
+ return EAI_SERVICE;
+ case SOCK_DGRAM:
+ case SOCK_STREAM:
+ allownumeric = 1;
+ break;
+ case ANY:
+ allownumeric = 1;
+ break;
+ default:
+ return EAI_SOCKTYPE;
+ }
+
+ port = str2number(servname);
+ if (port >= 0) {
+ if (!allownumeric) return EAI_SERVICE;
+ if (port < 0 || port > 65535) return EAI_SERVICE;
+ port = htons(port);
+ } else {
+ if (ai->ai_flags & AI_NUMERICSERV) return EAI_NONAME;
+
+ switch (ai->ai_socktype) {
+ case SOCK_DGRAM:
+ proto = "udp";
+ break;
+ case SOCK_STREAM:
+ proto = "tcp";
+ break;
+ default:
+ proto = NULL;
+ break;
+ }
+
+ if ((sp = getservbyname(servname, proto)) == NULL) return EAI_SERVICE;
+ port = sp->s_port;
+ }
+
+ if (!matchonly) {
+ switch (ai->ai_family) {
+ case AF_INET:
+ ((struct sockaddr_in*) (void*) ai->ai_addr)->sin_port = port;
+ break;
+ case AF_INET6:
+ ((struct sockaddr_in6*) (void*) ai->ai_addr)->sin6_port = port;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static const struct afd* find_afd(int af) {
+ const struct afd* afd;
+
+ if (af == PF_UNSPEC) return NULL;
+ for (afd = afdl; afd->a_af; afd++) {
+ if (afd->a_af == af) return afd;
+ }
+ return NULL;
+}
+
+// Convert a string to a scope identifier.
+static int ip6_str2scopeid(const char* scope, struct sockaddr_in6* sin6, u_int32_t* scopeid) {
+ u_long lscopeid;
+ struct in6_addr* a6;
+ char* ep;
+
+ assert(scope != NULL);
+ assert(sin6 != NULL);
+ assert(scopeid != NULL);
+
+ a6 = &sin6->sin6_addr;
+
+ /* empty scopeid portion is invalid */
+ if (*scope == '\0') return -1;
+
+ if (IN6_IS_ADDR_LINKLOCAL(a6) || IN6_IS_ADDR_MC_LINKLOCAL(a6)) {
+ /*
+ * We currently assume a one-to-one mapping between links
+ * and interfaces, so we simply use interface indices for
+ * like-local scopes.
+ */
+ *scopeid = if_nametoindex(scope);
+ if (*scopeid == 0) goto trynumeric;
+ return 0;
+ }
+
+ /* still unclear about literal, allow numeric only - placeholder */
+ if (IN6_IS_ADDR_SITELOCAL(a6) || IN6_IS_ADDR_MC_SITELOCAL(a6)) goto trynumeric;
+ if (IN6_IS_ADDR_MC_ORGLOCAL(a6))
+ goto trynumeric;
+ else
+ goto trynumeric; /* global */
+
+ /* try to convert to a numeric id as a last resort */
+trynumeric:
+ errno = 0;
+ lscopeid = strtoul(scope, &ep, 10);
+ *scopeid = (u_int32_t)(lscopeid & 0xffffffffUL);
+ if (errno == 0 && ep && *ep == '\0' && *scopeid == lscopeid)
+ return 0;
+ else
+ return -1;
+}
+
+/* code duplicate with gethnamaddr.c */
+
+#define BOUNDED_INCR(x) \
+ do { \
+ BOUNDS_CHECK(cp, x); \
+ cp += (x); \
+ } while (0)
+
+#define BOUNDS_CHECK(ptr, count) \
+ do { \
+ if (eom - (ptr) < (count)) { \
+ *herrno = NO_RECOVERY; \
+ return NULL; \
+ } \
+ } while (0)
+
+static struct addrinfo* getanswer(const querybuf* answer, int anslen, const char* qname, int qtype,
+ const struct addrinfo* pai, int* herrno) {
+ struct addrinfo sentinel = {};
+ struct addrinfo *cur;
+ struct addrinfo ai;
+ const struct afd* afd;
+ char* canonname;
+ const HEADER* hp;
+ const u_char* cp;
+ int n;
+ const u_char* eom;
+ char *bp, *ep;
+ int type, ancount, qdcount;
+ int haveanswer, had_error;
+ char tbuf[MAXDNAME];
+ int (*name_ok)(const char*);
+ char hostbuf[8 * 1024];
+
+ assert(answer != NULL);
+ assert(qname != NULL);
+ assert(pai != NULL);
+
+ cur = &sentinel;
+
+ canonname = NULL;
+ eom = answer->buf + anslen;
+ switch (qtype) {
+ case T_A:
+ case T_AAAA:
+ case T_ANY: /*use T_ANY only for T_A/T_AAAA lookup*/
+ name_ok = res_hnok;
+ break;
+ default:
+ return NULL; /* XXX should be abort(); */
+ }
+ /*
+ * find first satisfactory answer
+ */
+ hp = &answer->hdr;
+ ancount = ntohs(hp->ancount);
+ qdcount = ntohs(hp->qdcount);
+ bp = hostbuf;
+ ep = hostbuf + sizeof hostbuf;
+ cp = answer->buf;
+ BOUNDED_INCR(HFIXEDSZ);
+ if (qdcount != 1) {
+ *herrno = NO_RECOVERY;
+ return (NULL);
+ }
+ n = dn_expand(answer->buf, eom, cp, bp, ep - bp);
+ if ((n < 0) || !(*name_ok)(bp)) {
+ *herrno = NO_RECOVERY;
+ return (NULL);
+ }
+ BOUNDED_INCR(n + QFIXEDSZ);
+ if (qtype == T_A || qtype == T_AAAA || qtype == T_ANY) {
+ /* res_send() has already verified that the query name is the
+ * same as the one we sent; this just gets the expanded name
+ * (i.e., with the succeeding search-domain tacked on).
+ */
+ n = strlen(bp) + 1; /* for the \0 */
+ if (n >= MAXHOSTNAMELEN) {
+ *herrno = NO_RECOVERY;
+ return (NULL);
+ }
+ canonname = bp;
+ bp += n;
+ /* The qname can be abbreviated, but h_name is now absolute. */
+ qname = canonname;
+ }
+ haveanswer = 0;
+ had_error = 0;
+ while (ancount-- > 0 && cp < eom && !had_error) {
+ n = dn_expand(answer->buf, eom, cp, bp, ep - bp);
+ if ((n < 0) || !(*name_ok)(bp)) {
+ had_error++;
+ continue;
+ }
+ cp += n; /* name */
+ BOUNDS_CHECK(cp, 3 * INT16SZ + INT32SZ);
+ type = ntohs(*reinterpret_cast<const uint16_t*>(cp));
+ cp += INT16SZ; /* type */
+ int cl = ntohs(*reinterpret_cast<const uint16_t*>(cp));
+ cp += INT16SZ + INT32SZ; /* class, TTL */
+ n = ntohs(*reinterpret_cast<const uint16_t*>(cp));
+ cp += INT16SZ; /* len */
+ BOUNDS_CHECK(cp, n);
+ if (cl != C_IN) {
+ /* XXX - debug? syslog? */
+ cp += n;
+ continue; /* XXX - had_error++ ? */
+ }
+ if ((qtype == T_A || qtype == T_AAAA || qtype == T_ANY) && type == T_CNAME) {
+ n = dn_expand(answer->buf, eom, cp, tbuf, sizeof tbuf);
+ if ((n < 0) || !(*name_ok)(tbuf)) {
+ had_error++;
+ continue;
+ }
+ cp += n;
+ /* Get canonical name. */
+ n = strlen(tbuf) + 1; /* for the \0 */
+ if (n > ep - bp || n >= MAXHOSTNAMELEN) {
+ had_error++;
+ continue;
+ }
+ strlcpy(bp, tbuf, (size_t)(ep - bp));
+ canonname = bp;
+ bp += n;
+ continue;
+ }
+ if (qtype == T_ANY) {
+ if (!(type == T_A || type == T_AAAA)) {
+ cp += n;
+ continue;
+ }
+ } else if (type != qtype) {
+ if (type != T_KEY && type != T_SIG)
+ LOG(DEBUG) << __func__ << ": asked for \"" << qname << " " << p_class(C_IN) << " "
+ << p_type(qtype) << "\", got type \"" << p_type(type) << "\"";
+ cp += n;
+ continue; /* XXX - had_error++ ? */
+ }
+ switch (type) {
+ case T_A:
+ case T_AAAA:
+ if (strcasecmp(canonname, bp) != 0) {
+ LOG(DEBUG) << __func__ << ": asked for \"" << canonname << "\", got \"" << bp
+ << "\"";
+ cp += n;
+ continue; /* XXX - had_error++ ? */
+ }
+ if (type == T_A && n != INADDRSZ) {
+ cp += n;
+ continue;
+ }
+ if (type == T_AAAA && n != IN6ADDRSZ) {
+ cp += n;
+ continue;
+ }
+ if (type == T_AAAA) {
+ struct in6_addr in6;
+ memcpy(&in6, cp, IN6ADDRSZ);
+ if (IN6_IS_ADDR_V4MAPPED(&in6)) {
+ cp += n;
+ continue;
+ }
+ }
+ if (!haveanswer) {
+ int nn;
+
+ canonname = bp;
+ nn = strlen(bp) + 1; /* for the \0 */
+ bp += nn;
+ }
+
+ /* don't overwrite pai */
+ ai = *pai;
+ ai.ai_family = (type == T_A) ? AF_INET : AF_INET6;
+ afd = find_afd(ai.ai_family);
+ if (afd == NULL) {
+ cp += n;
+ continue;
+ }
+ cur->ai_next = get_ai(&ai, afd, (const char*) cp);
+ if (cur->ai_next == NULL) had_error++;
+ while (cur && cur->ai_next) cur = cur->ai_next;
+ cp += n;
+ break;
+ default:
+ abort();
+ }
+ if (!had_error) haveanswer++;
+ }
+ if (haveanswer) {
+ if (!canonname)
+ (void) get_canonname(pai, sentinel.ai_next, qname);
+ else
+ (void) get_canonname(pai, sentinel.ai_next, canonname);
+ *herrno = NETDB_SUCCESS;
+ return sentinel.ai_next;
+ }
+
+ *herrno = NO_RECOVERY;
+ return NULL;
+}
+
+struct addrinfo_sort_elem {
+ struct addrinfo* ai;
+ int has_src_addr;
+ sockaddr_union src_addr;
+ int original_order;
+};
+
+static int _get_scope(const struct sockaddr* addr) {
+ if (addr->sa_family == AF_INET6) {
+ const struct sockaddr_in6* addr6 = (const struct sockaddr_in6*) addr;
+ if (IN6_IS_ADDR_MULTICAST(&addr6->sin6_addr)) {
+ return IPV6_ADDR_MC_SCOPE(&addr6->sin6_addr);
+ } else if (IN6_IS_ADDR_LOOPBACK(&addr6->sin6_addr) ||
+ IN6_IS_ADDR_LINKLOCAL(&addr6->sin6_addr)) {
+ /*
+ * RFC 4291 section 2.5.3 says loopback is to be treated as having
+ * link-local scope.
+ */
+ return IPV6_ADDR_SCOPE_LINKLOCAL;
+ } else if (IN6_IS_ADDR_SITELOCAL(&addr6->sin6_addr)) {
+ return IPV6_ADDR_SCOPE_SITELOCAL;
+ } else {
+ return IPV6_ADDR_SCOPE_GLOBAL;
+ }
+ } else if (addr->sa_family == AF_INET) {
+ const struct sockaddr_in* addr4 = (const struct sockaddr_in*) addr;
+ unsigned long int na = ntohl(addr4->sin_addr.s_addr);
+
+ if (IN_LOOPBACK(na) || /* 127.0.0.0/8 */
+ (na & 0xffff0000) == 0xa9fe0000) { /* 169.254.0.0/16 */
+ return IPV6_ADDR_SCOPE_LINKLOCAL;
+ } else {
+ /*
+ * RFC 6724 section 3.2. Other IPv4 addresses, including private addresses
+ * and shared addresses (100.64.0.0/10), are assigned global scope.
+ */
+ return IPV6_ADDR_SCOPE_GLOBAL;
+ }
+ } else {
+ /*
+ * This should never happen.
+ * Return a scope with low priority as a last resort.
+ */
+ return IPV6_ADDR_SCOPE_NODELOCAL;
+ }
+}
+
+/* These macros are modelled after the ones in <netinet/in6.h>. */
+
+/* RFC 4380, section 2.6 */
+#define IN6_IS_ADDR_TEREDO(a) \
+ ((*(const uint32_t*) (const void*) (&(a)->s6_addr[0]) == ntohl(0x20010000)))
+
+/* RFC 3056, section 2. */
+#define IN6_IS_ADDR_6TO4(a) (((a)->s6_addr[0] == 0x20) && ((a)->s6_addr[1] == 0x02))
+
+/* 6bone testing address area (3ffe::/16), deprecated in RFC 3701. */
+#define IN6_IS_ADDR_6BONE(a) (((a)->s6_addr[0] == 0x3f) && ((a)->s6_addr[1] == 0xfe))
+
+/*
+ * Get the label for a given IPv4/IPv6 address.
+ * RFC 6724, section 2.1.
+ */
+
+static int _get_label(const struct sockaddr* addr) {
+ if (addr->sa_family == AF_INET) {
+ return 4;
+ } else if (addr->sa_family == AF_INET6) {
+ const struct sockaddr_in6* addr6 = (const struct sockaddr_in6*) addr;
+ if (IN6_IS_ADDR_LOOPBACK(&addr6->sin6_addr)) {
+ return 0;
+ } else if (IN6_IS_ADDR_V4MAPPED(&addr6->sin6_addr)) {
+ return 4;
+ } else if (IN6_IS_ADDR_6TO4(&addr6->sin6_addr)) {
+ return 2;
+ } else if (IN6_IS_ADDR_TEREDO(&addr6->sin6_addr)) {
+ return 5;
+ } else if (IN6_IS_ADDR_ULA(&addr6->sin6_addr)) {
+ return 13;
+ } else if (IN6_IS_ADDR_V4COMPAT(&addr6->sin6_addr)) {
+ return 3;
+ } else if (IN6_IS_ADDR_SITELOCAL(&addr6->sin6_addr)) {
+ return 11;
+ } else if (IN6_IS_ADDR_6BONE(&addr6->sin6_addr)) {
+ return 12;
+ } else {
+ /* All other IPv6 addresses, including global unicast addresses. */
+ return 1;
+ }
+ } else {
+ /*
+ * This should never happen.
+ * Return a semi-random label as a last resort.
+ */
+ return 1;
+ }
+}
+
+/*
+ * Get the precedence for a given IPv4/IPv6 address.
+ * RFC 6724, section 2.1.
+ */
+
+static int _get_precedence(const struct sockaddr* addr) {
+ if (addr->sa_family == AF_INET) {
+ return 35;
+ } else if (addr->sa_family == AF_INET6) {
+ const struct sockaddr_in6* addr6 = (const struct sockaddr_in6*) addr;
+ if (IN6_IS_ADDR_LOOPBACK(&addr6->sin6_addr)) {
+ return 50;
+ } else if (IN6_IS_ADDR_V4MAPPED(&addr6->sin6_addr)) {
+ return 35;
+ } else if (IN6_IS_ADDR_6TO4(&addr6->sin6_addr)) {
+ return 30;
+ } else if (IN6_IS_ADDR_TEREDO(&addr6->sin6_addr)) {
+ return 5;
+ } else if (IN6_IS_ADDR_ULA(&addr6->sin6_addr)) {
+ return 3;
+ } else if (IN6_IS_ADDR_V4COMPAT(&addr6->sin6_addr) ||
+ IN6_IS_ADDR_SITELOCAL(&addr6->sin6_addr) ||
+ IN6_IS_ADDR_6BONE(&addr6->sin6_addr)) {
+ return 1;
+ } else {
+ /* All other IPv6 addresses, including global unicast addresses. */
+ return 40;
+ }
+ } else {
+ return 1;
+ }
+}
+
+/*
+ * Find number of matching initial bits between the two addresses a1 and a2.
+ */
+
+static int _common_prefix_len(const struct in6_addr* a1, const struct in6_addr* a2) {
+ const char* p1 = (const char*) a1;
+ const char* p2 = (const char*) a2;
+ unsigned i;
+
+ for (i = 0; i < sizeof(*a1); ++i) {
+ int x, j;
+
+ if (p1[i] == p2[i]) {
+ continue;
+ }
+ x = p1[i] ^ p2[i];
+ for (j = 0; j < CHAR_BIT; ++j) {
+ if (x & (1 << (CHAR_BIT - 1))) {
+ return i * CHAR_BIT + j;
+ }
+ x <<= 1;
+ }
+ }
+ return sizeof(*a1) * CHAR_BIT;
+}
+
+/*
+ * Compare two source/destination address pairs.
+ * RFC 6724, section 6.
+ */
+
+static int _rfc6724_compare(const void* ptr1, const void* ptr2) {
+ const struct addrinfo_sort_elem* a1 = (const struct addrinfo_sort_elem*) ptr1;
+ const struct addrinfo_sort_elem* a2 = (const struct addrinfo_sort_elem*) ptr2;
+ int scope_src1, scope_dst1, scope_match1;
+ int scope_src2, scope_dst2, scope_match2;
+ int label_src1, label_dst1, label_match1;
+ int label_src2, label_dst2, label_match2;
+ int precedence1, precedence2;
+ int prefixlen1, prefixlen2;
+
+ /* Rule 1: Avoid unusable destinations. */
+ if (a1->has_src_addr != a2->has_src_addr) {
+ return a2->has_src_addr - a1->has_src_addr;
+ }
+
+ /* Rule 2: Prefer matching scope. */
+ scope_src1 = _get_scope(&a1->src_addr.sa);
+ scope_dst1 = _get_scope(a1->ai->ai_addr);
+ scope_match1 = (scope_src1 == scope_dst1);
+
+ scope_src2 = _get_scope(&a2->src_addr.sa);
+ scope_dst2 = _get_scope(a2->ai->ai_addr);
+ scope_match2 = (scope_src2 == scope_dst2);
+
+ if (scope_match1 != scope_match2) {
+ return scope_match2 - scope_match1;
+ }
+
+ /*
+ * Rule 3: Avoid deprecated addresses.
+ * TODO(sesse): We don't currently have a good way of finding this.
+ */
+
+ /*
+ * Rule 4: Prefer home addresses.
+ * TODO(sesse): We don't currently have a good way of finding this.
+ */
+
+ /* Rule 5: Prefer matching label. */
+ label_src1 = _get_label(&a1->src_addr.sa);
+ label_dst1 = _get_label(a1->ai->ai_addr);
+ label_match1 = (label_src1 == label_dst1);
+
+ label_src2 = _get_label(&a2->src_addr.sa);
+ label_dst2 = _get_label(a2->ai->ai_addr);
+ label_match2 = (label_src2 == label_dst2);
+
+ if (label_match1 != label_match2) {
+ return label_match2 - label_match1;
+ }
+
+ /* Rule 6: Prefer higher precedence. */
+ precedence1 = _get_precedence(a1->ai->ai_addr);
+ precedence2 = _get_precedence(a2->ai->ai_addr);
+ if (precedence1 != precedence2) {
+ return precedence2 - precedence1;
+ }
+
+ /*
+ * Rule 7: Prefer native transport.
+ * TODO(sesse): We don't currently have a good way of finding this.
+ */
+
+ /* Rule 8: Prefer smaller scope. */
+ if (scope_dst1 != scope_dst2) {
+ return scope_dst1 - scope_dst2;
+ }
+
+ /*
+ * Rule 9: Use longest matching prefix.
+ * We implement this for IPv6 only, as the rules in RFC 6724 don't seem
+ * to work very well directly applied to IPv4. (glibc uses information from
+ * the routing table for a custom IPv4 implementation here.)
+ */
+ if (a1->has_src_addr && a1->ai->ai_addr->sa_family == AF_INET6 && a2->has_src_addr &&
+ a2->ai->ai_addr->sa_family == AF_INET6) {
+ const struct sockaddr_in6* a1_src = &a1->src_addr.sin6;
+ const struct sockaddr_in6* a1_dst = (const struct sockaddr_in6*) a1->ai->ai_addr;
+ const struct sockaddr_in6* a2_src = &a2->src_addr.sin6;
+ const struct sockaddr_in6* a2_dst = (const struct sockaddr_in6*) a2->ai->ai_addr;
+ prefixlen1 = _common_prefix_len(&a1_src->sin6_addr, &a1_dst->sin6_addr);
+ prefixlen2 = _common_prefix_len(&a2_src->sin6_addr, &a2_dst->sin6_addr);
+ if (prefixlen1 != prefixlen2) {
+ return prefixlen2 - prefixlen1;
+ }
+ }
+
+ /*
+ * Rule 10: Leave the order unchanged.
+ * We need this since qsort() is not necessarily stable.
+ */
+ return a1->original_order - a2->original_order;
+}
+
+/*
+ * Find the source address that will be used if trying to connect to the given
+ * address. src_addr must be large enough to hold a struct sockaddr_in6.
+ *
+ * Returns 1 if a source address was found, 0 if the address is unreachable,
+ * and -1 if a fatal error occurred. If 0 or -1, the contents of src_addr are
+ * undefined.
+ */
+
+static int _find_src_addr(const struct sockaddr* addr, struct sockaddr* src_addr, unsigned mark,
+ uid_t uid) {
+ int sock;
+ int ret;
+ socklen_t len;
+
+ switch (addr->sa_family) {
+ case AF_INET:
+ len = sizeof(struct sockaddr_in);
+ break;
+ case AF_INET6:
+ len = sizeof(struct sockaddr_in6);
+ break;
+ default:
+ /* No known usable source address for non-INET families. */
+ return 0;
+ }
+
+ sock = socket(addr->sa_family, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP);
+ if (sock == -1) {
+ if (errno == EAFNOSUPPORT) {
+ return 0;
+ } else {
+ return -1;
+ }
+ }
+ if (mark != MARK_UNSET && setsockopt(sock, SOL_SOCKET, SO_MARK, &mark, sizeof(mark)) < 0) {
+ close(sock);
+ return 0;
+ }
+ if (uid > 0 && uid != NET_CONTEXT_INVALID_UID && fchown(sock, uid, (gid_t) -1) < 0) {
+ close(sock);
+ return 0;
+ }
+ do {
+ ret = connect(sock, addr, len);
+ } while (ret == -1 && errno == EINTR);
+
+ if (ret == -1) {
+ close(sock);
+ return 0;
+ }
+
+ if (src_addr && getsockname(sock, src_addr, &len) == -1) {
+ close(sock);
+ return -1;
+ }
+ close(sock);
+ return 1;
+}
+
+/*
+ * Sort the linked list starting at sentinel->ai_next in RFC6724 order.
+ * Will leave the list unchanged if an error occurs.
+ */
+
+static void _rfc6724_sort(struct addrinfo* list_sentinel, unsigned mark, uid_t uid) {
+ struct addrinfo* cur;
+ int nelem = 0, i;
+ struct addrinfo_sort_elem* elems;
+
+ cur = list_sentinel->ai_next;
+ while (cur) {
+ ++nelem;
+ cur = cur->ai_next;
+ }
+
+ elems = (struct addrinfo_sort_elem*) malloc(nelem * sizeof(struct addrinfo_sort_elem));
+ if (elems == NULL) {
+ goto error;
+ }
+
+ /*
+ * Convert the linked list to an array that also contains the candidate
+ * source address for each destination address.
+ */
+ for (i = 0, cur = list_sentinel->ai_next; i < nelem; ++i, cur = cur->ai_next) {
+ int has_src_addr;
+ assert(cur != NULL);
+ elems[i].ai = cur;
+ elems[i].original_order = i;
+
+ has_src_addr = _find_src_addr(cur->ai_addr, &elems[i].src_addr.sa, mark, uid);
+ if (has_src_addr == -1) {
+ goto error;
+ }
+ elems[i].has_src_addr = has_src_addr;
+ }
+
+ /* Sort the addresses, and rearrange the linked list so it matches the sorted order. */
+ qsort((void*) elems, nelem, sizeof(struct addrinfo_sort_elem), _rfc6724_compare);
+
+ list_sentinel->ai_next = elems[0].ai;
+ for (i = 0; i < nelem - 1; ++i) {
+ elems[i].ai->ai_next = elems[i + 1].ai;
+ }
+ elems[nelem - 1].ai->ai_next = NULL;
+
+error:
+ free(elems);
+}
+
+static int dns_getaddrinfo(const char* name, const addrinfo* pai,
+ const android_net_context* netcontext, addrinfo** rv,
+ NetworkDnsEventReported* event) {
+ res_target q = {};
+ res_target q2 = {};
+
+ auto buf = std::make_unique<querybuf>();
+ auto buf2 = std::make_unique<querybuf>();
+
+ switch (pai->ai_family) {
+ case AF_UNSPEC: {
+ /* prefer IPv6 */
+ q.name = name;
+ q.qclass = C_IN;
+ q.answer = buf->buf;
+ q.anslen = sizeof(buf->buf);
+ int query_ipv6 = 1, query_ipv4 = 1;
+ if (pai->ai_flags & AI_ADDRCONFIG) {
+ query_ipv6 = have_ipv6(netcontext->app_mark, netcontext->uid);
+ query_ipv4 = have_ipv4(netcontext->app_mark, netcontext->uid);
+ }
+ if (query_ipv6) {
+ q.qtype = T_AAAA;
+ if (query_ipv4) {
+ q.next = &q2;
+ q2.name = name;
+ q2.qclass = C_IN;
+ q2.qtype = T_A;
+ q2.answer = buf2->buf;
+ q2.anslen = sizeof(buf2->buf);
+ }
+ } else if (query_ipv4) {
+ q.qtype = T_A;
+ } else {
+ return EAI_NODATA;
+ }
+ break;
+ }
+ case AF_INET:
+ q.name = name;
+ q.qclass = C_IN;
+ q.qtype = T_A;
+ q.answer = buf->buf;
+ q.anslen = sizeof(buf->buf);
+ break;
+ case AF_INET6:
+ q.name = name;
+ q.qclass = C_IN;
+ q.qtype = T_AAAA;
+ q.answer = buf->buf;
+ q.anslen = sizeof(buf->buf);
+ break;
+ default:
+ return EAI_FAMILY;
+ }
+
+ res_state res = res_get_state();
+ if (!res) return EAI_MEMORY;
+
+ /* this just sets our netid val in the thread private data so we don't have to
+ * modify the api's all the way down to res_send.c's res_nsend. We could
+ * fully populate the thread private data here, but if we get down there
+ * and have a cache hit that would be wasted, so we do the rest there on miss
+ */
+ res_setnetcontext(res, netcontext, event);
+
+ int he;
+ if (res_searchN(name, &q, res, &he) < 0) {
+ // Return h_errno (he) to catch more detailed errors rather than EAI_NODATA.
+ // Note that res_searchN() doesn't set the pair NETDB_INTERNAL and errno.
+ // See also herrnoToAiErrno().
+ return herrnoToAiErrno(he);
+ }
+
+ addrinfo sentinel = {};
+ addrinfo* cur = &sentinel;
+ addrinfo* ai = getanswer(buf.get(), q.n, q.name, q.qtype, pai, &he);
+ if (ai) {
+ cur->ai_next = ai;
+ while (cur && cur->ai_next) cur = cur->ai_next;
+ }
+ if (q.next) {
+ ai = getanswer(buf2.get(), q2.n, q2.name, q2.qtype, pai, &he);
+ if (ai) cur->ai_next = ai;
+ }
+ if (sentinel.ai_next == NULL) {
+ // Note that getanswer() doesn't set the pair NETDB_INTERNAL and errno.
+ // See also herrnoToAiErrno().
+ return herrnoToAiErrno(he);
+ }
+
+ _rfc6724_sort(&sentinel, netcontext->app_mark, netcontext->uid);
+
+ *rv = sentinel.ai_next;
+ return 0;
+}
+
+static void _sethtent(FILE** hostf) {
+ if (!*hostf)
+ *hostf = fopen(_PATH_HOSTS, "re");
+ else
+ rewind(*hostf);
+}
+
+static void _endhtent(FILE** hostf) {
+ if (*hostf) {
+ (void) fclose(*hostf);
+ *hostf = NULL;
+ }
+}
+
+static struct addrinfo* _gethtent(FILE** hostf, const char* name, const struct addrinfo* pai) {
+ char* p;
+ char *cp, *tname, *cname;
+ struct addrinfo *res0, *res;
+ int error;
+ const char* addr;
+ char hostbuf[8 * 1024];
+
+ assert(name != NULL);
+ assert(pai != NULL);
+
+ if (!*hostf && !(*hostf = fopen(_PATH_HOSTS, "re"))) return (NULL);
+again:
+ if (!(p = fgets(hostbuf, sizeof hostbuf, *hostf))) return (NULL);
+ if (*p == '#') goto again;
+ if (!(cp = strpbrk(p, "#\n"))) goto again;
+ *cp = '\0';
+ if (!(cp = strpbrk(p, " \t"))) goto again;
+ *cp++ = '\0';
+ addr = p;
+ /* if this is not something we're looking for, skip it. */
+ cname = NULL;
+ while (cp && *cp) {
+ if (*cp == ' ' || *cp == '\t') {
+ cp++;
+ continue;
+ }
+ if (!cname) cname = cp;
+ tname = cp;
+ if ((cp = strpbrk(cp, " \t")) != NULL) *cp++ = '\0';
+ if (strcasecmp(name, tname) == 0) goto found;
+ }
+ goto again;
+
+found:
+ error = getaddrinfo_numeric(addr, nullptr, *pai, &res0);
+ if (error) goto again;
+ for (res = res0; res; res = res->ai_next) {
+ /* cover it up */
+ res->ai_flags = pai->ai_flags;
+
+ if (pai->ai_flags & AI_CANONNAME) {
+ if (get_canonname(pai, res, cname) != 0) {
+ freeaddrinfo(res0);
+ goto again;
+ }
+ }
+ }
+ return res0;
+}
+
+static bool files_getaddrinfo(const char* name, const addrinfo* pai, addrinfo** res) {
+ struct addrinfo sentinel = {};
+ struct addrinfo *p, *cur;
+ FILE* hostf = NULL;
+
+ cur = &sentinel;
+
+ _sethtent(&hostf);
+ while ((p = _gethtent(&hostf, name, pai)) != NULL) {
+ cur->ai_next = p;
+ while (cur && cur->ai_next) cur = cur->ai_next;
+ }
+ _endhtent(&hostf);
+
+ *res = sentinel.ai_next;
+ return sentinel.ai_next != NULL;
+}
+
+/* resolver logic */
+
+/*
+ * Formulate a normal query, send, and await answer.
+ * Returned answer is placed in supplied buffer "answer".
+ * Perform preliminary check of answer, returning success only
+ * if no error is indicated and the answer count is nonzero.
+ * Return the size of the response on success, -1 on error.
+ * Error number is left in *herrno.
+ *
+ * Caller must parse answer and determine whether it answers the question.
+ */
+static int res_queryN(const char* name, res_target* target, res_state res, int* herrno) {
+ u_char buf[MAXPACKET];
+ HEADER* hp;
+ int n;
+ struct res_target* t;
+ int rcode;
+ int ancount;
+
+ assert(name != NULL);
+ /* XXX: target may be NULL??? */
+
+ rcode = NOERROR;
+ ancount = 0;
+
+ for (t = target; t; t = t->next) {
+ u_char* answer;
+ int anslen;
+
+ hp = (HEADER*) (void*) t->answer;
+ bool retried = false;
+ again:
+ hp->rcode = NOERROR; /* default */
+
+ /* make it easier... */
+ int cl = t->qclass;
+ int type = t->qtype;
+ answer = t->answer;
+ anslen = t->anslen;
+
+ LOG(DEBUG) << __func__ << ": (" << cl << ", " << type << ")";
+
+ n = res_nmkquery(res, QUERY, name, cl, type, NULL, 0, NULL, buf, sizeof(buf));
+ if (n > 0 && (res->options & (RES_USE_EDNS0 | RES_USE_DNSSEC)) != 0 && !retried)
+ n = res_nopt(res, n, buf, sizeof(buf), anslen);
+ if (n <= 0) {
+ LOG(ERROR) << __func__ << ": res_nmkquery failed";
+ *herrno = NO_RECOVERY;
+ return n;
+ }
+
+ n = res_nsend(res, buf, n, answer, anslen, &rcode, 0);
+ if (n < 0 || hp->rcode != NOERROR || ntohs(hp->ancount) == 0) {
+ // Record rcode from DNS response header only if no timeout.
+ // Keep rcode timeout for reporting later if any.
+ if (rcode != RCODE_TIMEOUT) rcode = hp->rcode; /* record most recent error */
+ /* if the query choked with EDNS0, retry without EDNS0 */
+ if ((res->options & (RES_USE_EDNS0 | RES_USE_DNSSEC)) != 0 &&
+ (res->_flags & RES_F_EDNS0ERR) && !retried) {
+ LOG(DEBUG) << __func__ << ": retry without EDNS0";
+ retried = true;
+ goto again;
+ }
+ LOG(DEBUG) << __func__ << ": rcode=" << hp->rcode << ", ancount=" << ntohs(hp->ancount);
+ continue;
+ }
+
+ ancount += ntohs(hp->ancount);
+
+ t->n = n;
+ }
+
+ if (ancount == 0) {
+ switch (rcode) {
+ // Not defined in RFC.
+ case RCODE_TIMEOUT:
+ // DNS metrics monitors DNS query timeout.
+ *herrno = NETD_RESOLV_H_ERRNO_EXT_TIMEOUT; // extended h_errno.
+ break;
+ // Defined in RFC 1035 section 4.1.1.
+ case NXDOMAIN:
+ *herrno = HOST_NOT_FOUND;
+ break;
+ case SERVFAIL:
+ *herrno = TRY_AGAIN;
+ break;
+ case NOERROR:
+ *herrno = NO_DATA;
+ break;
+ case FORMERR:
+ case NOTIMP:
+ case REFUSED:
+ default:
+ *herrno = NO_RECOVERY;
+ break;
+ }
+ return -1;
+ }
+ return ancount;
+}
+
+/*
+ * Formulate a normal query, send, and retrieve answer in supplied buffer.
+ * Return the size of the response on success, -1 on error.
+ * If enabled, implement search rules until answer or unrecoverable failure
+ * is detected. Error code, if any, is left in *herrno.
+ */
+static int res_searchN(const char* name, res_target* target, res_state res, int* herrno) {
+ const char *cp, *const *domain;
+ HEADER* hp;
+ u_int dots;
+ int trailing_dot, ret, saved_herrno;
+ int got_nodata = 0, got_servfail = 0, tried_as_is = 0;
+
+ assert(name != NULL);
+ assert(target != NULL);
+
+ hp = (HEADER*) (void*) target->answer; /*XXX*/
+
+ errno = 0;
+ *herrno = HOST_NOT_FOUND; /* default, if we never query */
+ dots = 0;
+ for (cp = name; *cp; cp++) dots += (*cp == '.');
+ trailing_dot = 0;
+ if (cp > name && *--cp == '.') trailing_dot++;
+
+ /*
+ * If there are dots in the name already, let's just give it a try
+ * 'as is'. The threshold can be set with the "ndots" option.
+ */
+ saved_herrno = -1;
+ if (dots >= res->ndots) {
+ ret = res_querydomainN(name, NULL, target, res, herrno);
+ if (ret > 0) return (ret);
+ saved_herrno = *herrno;
+ tried_as_is++;
+ }
+
+ /*
+ * We do at least one level of search if
+ * - there is no dot and RES_DEFNAME is set, or
+ * - there is at least one dot, there is no trailing dot,
+ * and RES_DNSRCH is set.
+ */
+ if ((!dots && (res->options & RES_DEFNAMES)) ||
+ (dots && !trailing_dot && (res->options & RES_DNSRCH))) {
+ int done = 0;
+
+ /* Unfortunately we need to set stuff up before
+ * the domain stuff is tried. Will have a better
+ * fix after thread pools are used.
+ */
+ _resolv_populate_res_for_net(res);
+
+ for (domain = (const char* const*) res->dnsrch; *domain && !done; domain++) {
+ ret = res_querydomainN(name, *domain, target, res, herrno);
+ if (ret > 0) return ret;
+
+ /*
+ * If no server present, give up.
+ * If name isn't found in this domain,
+ * keep trying higher domains in the search list
+ * (if that's enabled).
+ * On a NO_DATA error, keep trying, otherwise
+ * a wildcard entry of another type could keep us
+ * from finding this entry higher in the domain.
+ * If we get some other error (negative answer or
+ * server failure), then stop searching up,
+ * but try the input name below in case it's
+ * fully-qualified.
+ */
+ if (errno == ECONNREFUSED) {
+ *herrno = TRY_AGAIN;
+ return -1;
+ }
+
+ switch (*herrno) {
+ case NO_DATA:
+ got_nodata++;
+ [[fallthrough]];
+ case HOST_NOT_FOUND:
+ /* keep trying */
+ break;
+ case TRY_AGAIN:
+ if (hp->rcode == SERVFAIL) {
+ /* try next search element, if any */
+ got_servfail++;
+ break;
+ }
+ [[fallthrough]];
+ default:
+ /* anything else implies that we're done */
+ done++;
+ }
+ /*
+ * if we got here for some reason other than DNSRCH,
+ * we only wanted one iteration of the loop, so stop.
+ */
+ if (!(res->options & RES_DNSRCH)) done++;
+ }
+ }
+
+ /*
+ * if we have not already tried the name "as is", do that now.
+ * note that we do this regardless of how many dots were in the
+ * name or whether it ends with a dot.
+ */
+ if (!tried_as_is) {
+ ret = res_querydomainN(name, NULL, target, res, herrno);
+ if (ret > 0) return ret;
+ }
+
+ /*
+ * if we got here, we didn't satisfy the search.
+ * if we did an initial full query, return that query's h_errno
+ * (note that we wouldn't be here if that query had succeeded).
+ * else if we ever got a nodata, send that back as the reason.
+ * else send back meaningless h_errno, that being the one from
+ * the last DNSRCH we did.
+ */
+ if (saved_herrno != -1)
+ *herrno = saved_herrno;
+ else if (got_nodata)
+ *herrno = NO_DATA;
+ else if (got_servfail)
+ *herrno = TRY_AGAIN;
+ return -1;
+}
+
+/*
+ * Perform a call on res_query on the concatenation of name and domain,
+ * removing a trailing dot from name if domain is NULL.
+ */
+static int res_querydomainN(const char* name, const char* domain, res_target* target, res_state res,
+ int* herrno) {
+ char nbuf[MAXDNAME];
+ const char* longname = nbuf;
+ size_t n, d;
+
+ assert(name != NULL);
+
+ if (domain == NULL) {
+ // Check for trailing '.'; copy without '.' if present.
+ n = strlen(name);
+ if (n + 1 > sizeof(nbuf)) {
+ *herrno = NO_RECOVERY;
+ return -1;
+ }
+ if (n > 0 && name[--n] == '.') {
+ strncpy(nbuf, name, n);
+ nbuf[n] = '\0';
+ } else
+ longname = name;
+ } else {
+ n = strlen(name);
+ d = strlen(domain);
+ if (n + 1 + d + 1 > sizeof(nbuf)) {
+ *herrno = NO_RECOVERY;
+ return -1;
+ }
+ snprintf(nbuf, sizeof(nbuf), "%s.%s", name, domain);
+ }
+ return res_queryN(longname, target, res, herrno);
+}
diff --git a/resolv/getaddrinfo.h b/resolv/getaddrinfo.h
new file mode 100644
index 0000000..e8ba5dc
--- /dev/null
+++ b/resolv/getaddrinfo.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#pragma once
+
+#include "netd_resolv/resolv.h" // struct android_net_context
+#include "stats.pb.h"
+
+struct addrinfo;
+
+// This is the DNS proxy entry point for getaddrinfo().
+int android_getaddrinfofornetcontext(const char*, const char*, const addrinfo*,
+ const android_net_context*, addrinfo**,
+ android::net::NetworkDnsEventReported*);
diff --git a/resolv/gethnamaddr.cpp b/resolv/gethnamaddr.cpp
new file mode 100644
index 0000000..a40a7b7
--- /dev/null
+++ b/resolv/gethnamaddr.cpp
@@ -0,0 +1,960 @@
+/* $NetBSD: gethnamaddr.c,v 1.91 2014/06/19 15:08:18 christos Exp $ */
+
+/*
+ * ++Copyright++ 1985, 1988, 1993
+ * -
+ * Copyright (c) 1985, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * -
+ * Portions Copyright (c) 1993 by Digital Equipment Corporation.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies, and that
+ * the name of Digital Equipment Corporation not be used in advertising or
+ * publicity pertaining to distribution of the document or software without
+ * specific, written prior permission.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
+ * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ * -
+ * --Copyright--
+ */
+
+#include "gethnamaddr.h"
+
+#include <android-base/logging.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <functional>
+#include <vector>
+
+#include "hostent.h"
+#include "netd_resolv/resolv.h"
+#include "resolv_cache.h"
+#include "resolv_private.h"
+#include "stats.pb.h"
+
+using android::net::NetworkDnsEventReported;
+
+// NetBSD uses _DIAGASSERT to null-check arguments and the like,
+// but it's clear from the number of mistakes in their assertions
+// that they don't actually test or ship with this.
+#define _DIAGASSERT(e) /* nothing */
+
+// TODO: unify macro ALIGNBYTES and ALIGN for all possible data type alignment of hostent
+// buffer.
+#define ALIGNBYTES (sizeof(uintptr_t) - 1)
+#define ALIGN(p) (((uintptr_t)(p) + ALIGNBYTES) & ~ALIGNBYTES)
+
+#define maybe_ok(res, nm, ok) (((res)->options & RES_NOCHECKNAME) != 0U || (ok)(nm) != 0)
+#define maybe_hnok(res, hn) maybe_ok((res), (hn), res_hnok)
+#define maybe_dnok(res, dn) maybe_ok((res), (dn), res_dnok)
+
+#define MAXPACKET (8 * 1024)
+
+typedef union {
+ HEADER hdr;
+ u_char buf[MAXPACKET];
+} querybuf;
+
+typedef union {
+ int32_t al;
+ char ac;
+} align;
+
+static struct hostent* getanswer(const querybuf*, int, const char*, int, res_state, struct hostent*,
+ char*, size_t, int*);
+static void convert_v4v6_hostent(struct hostent* hp, char** bpp, char* ep,
+ std::function<void(struct hostent* hp)> mapping_param,
+ std::function<void(char* src, char* dst)> mapping_addr);
+static void map_v4v6_address(const char*, char*);
+static void map_v4v6_hostent(struct hostent*, char**, char*);
+static void pad_v4v6_hostent(struct hostent* hp, char** bpp, char* ep);
+static void addrsort(char**, int, res_state);
+
+static int dns_gethtbyaddr(const unsigned char* uaddr, int len, int af,
+ const android_net_context* netcontext, getnamaddr* info,
+ NetworkDnsEventReported* event);
+static int dns_gethtbyname(const char* name, int af, getnamaddr* info);
+
+static int gethostbyname_internal(const char* name, int af, res_state res, hostent* hp, char* hbuf,
+ size_t hbuflen, const android_net_context* netcontext,
+ NetworkDnsEventReported* event);
+static int gethostbyname_internal_real(const char* name, int af, res_state res, hostent* hp,
+ char* buf, size_t buflen);
+static int android_gethostbyaddrfornetcontext_proxy_internal(const void*, socklen_t, int,
+ struct hostent*, char*, size_t,
+ const struct android_net_context*,
+ NetworkDnsEventReported* event);
+static int android_gethostbyaddrfornetcontext_proxy(const void* addr, socklen_t len, int af,
+ const struct android_net_context* netcontext,
+ hostent** hp, NetworkDnsEventReported* event);
+
+#define BOUNDED_INCR(x) \
+ do { \
+ BOUNDS_CHECK(cp, x); \
+ cp += (x); \
+ } while (0)
+
+#define BOUNDS_CHECK(ptr, count) \
+ do { \
+ if (eom - (ptr) < (count)) goto no_recovery; \
+ } while (0)
+
+static struct hostent* getanswer(const querybuf* answer, int anslen, const char* qname, int qtype,
+ res_state res, struct hostent* hent, char* buf, size_t buflen,
+ int* he) {
+ const HEADER* hp;
+ const u_char* cp;
+ int n;
+ size_t qlen;
+ const u_char *eom, *erdata;
+ char *bp, **hap, *ep;
+ int ancount, qdcount;
+ int haveanswer, had_error;
+ int toobig = 0;
+ char tbuf[MAXDNAME];
+ char* addr_ptrs[MAXADDRS];
+ const char* tname;
+ int (*name_ok)(const char*);
+ std::vector<char*> aliases;
+
+ _DIAGASSERT(answer != NULL);
+ _DIAGASSERT(qname != NULL);
+
+ tname = qname;
+ hent->h_name = NULL;
+ eom = answer->buf + anslen;
+ switch (qtype) {
+ case T_A:
+ case T_AAAA:
+ name_ok = res_hnok;
+ break;
+ case T_PTR:
+ name_ok = res_dnok;
+ break;
+ default:
+ *he = NO_RECOVERY;
+ return NULL; /* XXX should be abort(); */
+ }
+
+ /*
+ * find first satisfactory answer
+ */
+ hp = &answer->hdr;
+ ancount = ntohs(hp->ancount);
+ qdcount = ntohs(hp->qdcount);
+ bp = buf;
+ ep = buf + buflen;
+ cp = answer->buf;
+ BOUNDED_INCR(HFIXEDSZ);
+ if (qdcount != 1) goto no_recovery;
+
+ n = dn_expand(answer->buf, eom, cp, bp, (int) (ep - bp));
+ if ((n < 0) || !maybe_ok(res, bp, name_ok)) goto no_recovery;
+
+ BOUNDED_INCR(n + QFIXEDSZ);
+ if (qtype == T_A || qtype == T_AAAA) {
+ /* res_send() has already verified that the query name is the
+ * same as the one we sent; this just gets the expanded name
+ * (i.e., with the succeeding search-domain tacked on).
+ */
+ n = (int) strlen(bp) + 1; /* for the \0 */
+ if (n >= MAXHOSTNAMELEN) goto no_recovery;
+ hent->h_name = bp;
+ bp += n;
+ /* The qname can be abbreviated, but h_name is now absolute. */
+ qname = hent->h_name;
+ }
+ hent->h_addr_list = hap = addr_ptrs;
+ *hap = NULL;
+ haveanswer = 0;
+ had_error = 0;
+ while (ancount-- > 0 && cp < eom && !had_error) {
+ n = dn_expand(answer->buf, eom, cp, bp, (int) (ep - bp));
+ if ((n < 0) || !maybe_ok(res, bp, name_ok)) {
+ had_error++;
+ continue;
+ }
+ cp += n; /* name */
+ BOUNDS_CHECK(cp, 3 * INT16SZ + INT32SZ);
+ int type = ntohs(*reinterpret_cast<const uint16_t*>(cp));
+ cp += INT16SZ; /* type */
+ int cl = ntohs(*reinterpret_cast<const uint16_t*>(cp));
+ cp += INT16SZ + INT32SZ; /* class, TTL */
+ n = ntohs(*reinterpret_cast<const uint16_t*>(cp));
+ cp += INT16SZ; /* len */
+ BOUNDS_CHECK(cp, n);
+ erdata = cp + n;
+ if (cl != C_IN) {
+ /* XXX - debug? syslog? */
+ cp += n;
+ continue; /* XXX - had_error++ ? */
+ }
+ if ((qtype == T_A || qtype == T_AAAA) && type == T_CNAME) {
+ n = dn_expand(answer->buf, eom, cp, tbuf, (int) sizeof tbuf);
+ if ((n < 0) || !maybe_ok(res, tbuf, name_ok)) {
+ had_error++;
+ continue;
+ }
+ cp += n;
+ if (cp != erdata) goto no_recovery;
+ /* Store alias. */
+ aliases.push_back(bp);
+ n = (int) strlen(bp) + 1; /* for the \0 */
+ if (n >= MAXHOSTNAMELEN) {
+ had_error++;
+ continue;
+ }
+ bp += n;
+ /* Get canonical name. */
+ n = (int) strlen(tbuf) + 1; /* for the \0 */
+ if (n > ep - bp || n >= MAXHOSTNAMELEN) {
+ had_error++;
+ continue;
+ }
+ strlcpy(bp, tbuf, (size_t)(ep - bp));
+ hent->h_name = bp;
+ bp += n;
+ continue;
+ }
+ if (qtype == T_PTR && type == T_CNAME) {
+ n = dn_expand(answer->buf, eom, cp, tbuf, (int) sizeof tbuf);
+ if (n < 0 || !maybe_dnok(res, tbuf)) {
+ had_error++;
+ continue;
+ }
+ cp += n;
+ if (cp != erdata) goto no_recovery;
+ /* Get canonical name. */
+ n = (int) strlen(tbuf) + 1; /* for the \0 */
+ if (n > ep - bp || n >= MAXHOSTNAMELEN) {
+ had_error++;
+ continue;
+ }
+ strlcpy(bp, tbuf, (size_t)(ep - bp));
+ tname = bp;
+ bp += n;
+ continue;
+ }
+ if (type != qtype) {
+ if (type != T_KEY && type != T_SIG)
+ LOG(DEBUG) << __func__ << ": asked for \"" << qname << " " << p_class(C_IN) << " "
+ << p_type(qtype) << "\", got type \"" << p_type(type) << "\"";
+ cp += n;
+ continue; /* XXX - had_error++ ? */
+ }
+ switch (type) {
+ case T_PTR:
+ if (strcasecmp(tname, bp) != 0) {
+ LOG(DEBUG) << __func__ << ": asked for \"" << qname << "\", got \"" << bp
+ << "\"";
+ cp += n;
+ continue; /* XXX - had_error++ ? */
+ }
+ n = dn_expand(answer->buf, eom, cp, bp, (int) (ep - bp));
+ if ((n < 0) || !maybe_hnok(res, bp)) {
+ had_error++;
+ break;
+ }
+ cp += n;
+ if (cp != erdata) goto no_recovery;
+ if (!haveanswer)
+ hent->h_name = bp;
+ else
+ aliases.push_back(bp);
+ if (n != -1) {
+ n = (int) strlen(bp) + 1; /* for the \0 */
+ if (n >= MAXHOSTNAMELEN) {
+ had_error++;
+ break;
+ }
+ bp += n;
+ }
+ break;
+ case T_A:
+ case T_AAAA:
+ if (strcasecmp(hent->h_name, bp) != 0) {
+ LOG(DEBUG) << __func__ << ": asked for \"" << hent->h_name << "\", got \"" << bp
+ << "\"";
+ cp += n;
+ continue; /* XXX - had_error++ ? */
+ }
+ if (n != hent->h_length) {
+ cp += n;
+ continue;
+ }
+ if (type == T_AAAA) {
+ struct in6_addr in6;
+ memcpy(&in6, cp, NS_IN6ADDRSZ);
+ if (IN6_IS_ADDR_V4MAPPED(&in6)) {
+ cp += n;
+ continue;
+ }
+ }
+ if (!haveanswer) {
+ int nn;
+
+ hent->h_name = bp;
+ nn = (int) strlen(bp) + 1; /* for the \0 */
+ bp += nn;
+ }
+
+ bp += sizeof(align) - (size_t)((u_long) bp % sizeof(align));
+
+ if (bp + n >= ep) {
+ LOG(DEBUG) << __func__ << ": size (" << n << ") too big";
+ had_error++;
+ continue;
+ }
+ if (hap >= &addr_ptrs[MAXADDRS - 1]) {
+ if (!toobig++) {
+ LOG(DEBUG) << __func__ << ": Too many addresses (" << MAXADDRS << ")";
+ }
+ cp += n;
+ continue;
+ }
+ (void) memcpy(*hap++ = bp, cp, (size_t) n);
+ bp += n;
+ cp += n;
+ if (cp != erdata) goto no_recovery;
+ break;
+ default:
+ abort();
+ }
+ if (!had_error) haveanswer++;
+ }
+ if (haveanswer) {
+ *hap = NULL;
+ /*
+ * Note: we sort even if host can take only one address
+ * in its return structures - should give it the "best"
+ * address in that case, not some random one
+ */
+ if (res->nsort && haveanswer > 1 && qtype == T_A) addrsort(addr_ptrs, haveanswer, res);
+ if (!hent->h_name) {
+ n = (int) strlen(qname) + 1; /* for the \0 */
+ if (n > ep - bp || n >= MAXHOSTNAMELEN) goto no_recovery;
+ strlcpy(bp, qname, (size_t)(ep - bp));
+ hent->h_name = bp;
+ bp += n;
+ }
+ if (res->options & RES_USE_INET6) map_v4v6_hostent(hent, &bp, ep);
+ if (hent->h_addrtype == AF_INET) pad_v4v6_hostent(hent, &bp, ep);
+ goto success;
+ }
+no_recovery:
+ *he = NO_RECOVERY;
+ return NULL;
+success:
+ bp = (char*) ALIGN(bp);
+ aliases.push_back(nullptr);
+ qlen = aliases.size() * sizeof(*hent->h_aliases);
+ if ((size_t)(ep - bp) < qlen) goto nospc;
+ hent->h_aliases = (char**) bp;
+ memcpy(bp, aliases.data(), qlen);
+
+ bp += qlen;
+ n = (int) (hap - addr_ptrs);
+ qlen = (n + 1) * sizeof(*hent->h_addr_list);
+ if ((size_t)(ep - bp) < qlen) goto nospc;
+ hent->h_addr_list = (char**) bp;
+ memcpy(bp, addr_ptrs, qlen);
+ *he = NETDB_SUCCESS;
+ return hent;
+nospc:
+ errno = ENOSPC;
+ *he = NETDB_INTERNAL;
+ return NULL;
+}
+
+static int gethostbyname_internal_real(const char* name, int af, res_state res, hostent* hp,
+ char* buf, size_t buflen) {
+ getnamaddr info;
+ size_t size;
+
+ _DIAGASSERT(name != NULL);
+
+ switch (af) {
+ case AF_INET:
+ size = NS_INADDRSZ;
+ break;
+ case AF_INET6:
+ size = NS_IN6ADDRSZ;
+ break;
+ default:
+ return EAI_FAMILY;
+ }
+ if (buflen < size) goto nospc;
+
+ hp->h_addrtype = af;
+ hp->h_length = (int) size;
+
+ /*
+ * disallow names consisting only of digits/dots, unless
+ * they end in a dot.
+ */
+ if (isdigit((u_char) name[0])) {
+ for (const char* cp = name;; ++cp) {
+ if (!*cp) {
+ if (*--cp == '.') break;
+ /*
+ * All-numeric, no dot at the end.
+ * Fake up a hostent as if we'd actually
+ * done a lookup.
+ */
+ goto fake;
+ }
+ if (!isdigit((u_char) *cp) && *cp != '.') break;
+ }
+ }
+ if ((isxdigit((u_char) name[0]) && strchr(name, ':') != NULL) || name[0] == ':') {
+ for (const char* cp = name;; ++cp) {
+ if (!*cp) {
+ if (*--cp == '.') break;
+ /*
+ * All-IPv6-legal, no dot at the end.
+ * Fake up a hostent as if we'd actually
+ * done a lookup.
+ */
+ goto fake;
+ }
+ if (!isxdigit((u_char) *cp) && *cp != ':' && *cp != '.') break;
+ }
+ }
+
+ info.hp = hp;
+ info.buf = buf;
+ info.buflen = buflen;
+ if (_hf_gethtbyname2(name, af, &info)) {
+ int error = dns_gethtbyname(name, af, &info);
+ if (error != 0) return error;
+ }
+ return 0;
+nospc:
+ return EAI_MEMORY;
+fake:
+ HENT_ARRAY(hp->h_addr_list, 1, buf, buflen);
+ HENT_ARRAY(hp->h_aliases, 0, buf, buflen);
+
+ hp->h_aliases[0] = NULL;
+ if (size > buflen) goto nospc;
+
+ if (inet_pton(af, name, buf) <= 0) {
+ return EAI_NODATA;
+ }
+ hp->h_addr_list[0] = buf;
+ hp->h_addr_list[1] = NULL;
+ buf += size;
+ buflen -= size;
+ HENT_SCOPY(hp->h_name, name, buf, buflen);
+ if (res->options & RES_USE_INET6) map_v4v6_hostent(hp, &buf, buf + buflen);
+ return 0;
+}
+
+// very similar in proxy-ness to android_getaddrinfo_proxy
+static int gethostbyname_internal(const char* name, int af, res_state res, hostent* hp, char* hbuf,
+ size_t hbuflen, const android_net_context* netcontext,
+ NetworkDnsEventReported* event) {
+ res_setnetcontext(res, netcontext, event);
+ return gethostbyname_internal_real(name, af, res, hp, hbuf, hbuflen);
+}
+
+static int android_gethostbyaddrfornetcontext_real(const void* addr, socklen_t len, int af,
+ struct hostent* hp, char* buf, size_t buflen,
+ const struct android_net_context* netcontext,
+ NetworkDnsEventReported* event) {
+ const u_char* uaddr = (const u_char*) addr;
+ socklen_t size;
+ struct getnamaddr info;
+
+ _DIAGASSERT(addr != NULL);
+
+ if (af == AF_INET6 && len == NS_IN6ADDRSZ &&
+ (IN6_IS_ADDR_LINKLOCAL((const struct in6_addr*) addr) ||
+ IN6_IS_ADDR_SITELOCAL((const struct in6_addr*) addr))) {
+ return EAI_NODATA;
+ }
+ if (af == AF_INET6 && len == NS_IN6ADDRSZ &&
+ (IN6_IS_ADDR_V4MAPPED((const struct in6_addr*) addr) ||
+ IN6_IS_ADDR_V4COMPAT((const struct in6_addr*) addr))) {
+ /* Unmap. */
+ uaddr += NS_IN6ADDRSZ - NS_INADDRSZ;
+ addr = uaddr;
+ af = AF_INET;
+ len = NS_INADDRSZ;
+ }
+ switch (af) {
+ case AF_INET:
+ size = NS_INADDRSZ;
+ break;
+ case AF_INET6:
+ size = NS_IN6ADDRSZ;
+ break;
+ default:
+ return EAI_FAMILY;
+ }
+ if (size != len) {
+ // TODO: Consider converting to a private extended EAI_* error code.
+ // Currently, the EAI_* value has no corresponding error code for invalid argument socket
+ // length. In order to not rely on errno, convert the original error code pair, EAI_SYSTEM
+ // and EINVAL, to EAI_FAIL.
+ return EAI_FAIL;
+ }
+ info.hp = hp;
+ info.buf = buf;
+ info.buflen = buflen;
+ if (_hf_gethtbyaddr(uaddr, len, af, &info)) {
+ int error = dns_gethtbyaddr(uaddr, len, af, netcontext, &info, event);
+ if (error != 0) return error;
+ }
+ return 0;
+}
+
+static int android_gethostbyaddrfornetcontext_proxy_internal(
+ const void* addr, socklen_t len, int af, struct hostent* hp, char* hbuf, size_t hbuflen,
+ const struct android_net_context* netcontext, NetworkDnsEventReported* event) {
+ return android_gethostbyaddrfornetcontext_real(addr, len, af, hp, hbuf, hbuflen, netcontext,
+ event);
+}
+
+// TODO: Consider leaving function without returning error code as _gethtent() does because
+// the error code of the caller does not currently return to netd.
+struct hostent* netbsd_gethostent_r(FILE* hf, struct hostent* hent, char* buf, size_t buflen,
+ int* he) {
+ const size_t line_buf_size = sizeof(res_get_static()->hostbuf);
+ char *name;
+ char* cp;
+ int af, len;
+ size_t anum;
+ struct in6_addr host_addr;
+ std::vector<char*> aliases;
+
+ if (hf == NULL) {
+ *he = NETDB_INTERNAL;
+ errno = EINVAL;
+ return NULL;
+ }
+ char* p = NULL;
+
+ /* Allocate a new space to read file lines like upstream does.
+ * To keep reentrancy we cannot use res_get_static()->hostbuf here,
+ * as the buffer may be used to store content for a previous hostent
+ * returned by non-reentrant functions like gethostbyname().
+ */
+ if ((p = (char*) malloc(line_buf_size)) == NULL) {
+ goto nospc;
+ }
+ for (;;) {
+ if (!fgets(p, line_buf_size, hf)) {
+ free(p);
+ *he = HOST_NOT_FOUND;
+ return NULL;
+ }
+ if (*p == '#') {
+ continue;
+ }
+ if (!(cp = strpbrk(p, "#\n"))) {
+ continue;
+ }
+ *cp = '\0';
+ if (!(cp = strpbrk(p, " \t"))) continue;
+ *cp++ = '\0';
+ if (inet_pton(AF_INET6, p, &host_addr) > 0) {
+ af = AF_INET6;
+ len = NS_IN6ADDRSZ;
+ } else {
+ if (inet_pton(AF_INET, p, &host_addr) <= 0) continue;
+
+ res_state res = res_get_state();
+ if (res == NULL) goto nospc;
+ if (res->options & RES_USE_INET6) {
+ map_v4v6_address(buf, buf);
+ af = AF_INET6;
+ len = NS_IN6ADDRSZ;
+ } else {
+ af = AF_INET;
+ len = NS_INADDRSZ;
+ }
+ }
+
+ /* if this is not something we're looking for, skip it. */
+ if (hent->h_addrtype != 0 && hent->h_addrtype != af) continue;
+ if (hent->h_length != 0 && hent->h_length != len) continue;
+
+ while (*cp == ' ' || *cp == '\t') cp++;
+ if ((cp = strpbrk(name = cp, " \t")) != NULL) *cp++ = '\0';
+ while (cp && *cp) {
+ if (*cp == ' ' || *cp == '\t') {
+ cp++;
+ continue;
+ }
+ aliases.push_back(cp);
+ if ((cp = strpbrk(cp, " \t")) != NULL) *cp++ = '\0';
+ }
+ break;
+ }
+ hent->h_length = len;
+ hent->h_addrtype = af;
+ HENT_ARRAY(hent->h_addr_list, 1, buf, buflen);
+ anum = aliases.size();
+ HENT_ARRAY(hent->h_aliases, anum, buf, buflen);
+ HENT_COPY(hent->h_addr_list[0], &host_addr, hent->h_length, buf, buflen);
+ hent->h_addr_list[1] = NULL;
+
+ /* Reserve space for mapping IPv4 address to IPv6 address in place */
+ if (hent->h_addrtype == AF_INET) {
+ HENT_COPY(buf, NAT64_PAD, sizeof(NAT64_PAD), buf, buflen);
+ }
+
+ HENT_SCOPY(hent->h_name, name, buf, buflen);
+ for (size_t i = 0; i < anum; i++) HENT_SCOPY(hent->h_aliases[i], aliases[i], buf, buflen);
+ hent->h_aliases[anum] = NULL;
+ *he = NETDB_SUCCESS;
+ free(p);
+ return hent;
+nospc:
+ free(p);
+ errno = ENOSPC;
+ *he = NETDB_INTERNAL;
+ return NULL;
+}
+
+static void map_v4v6_address(const char* src, char* dst) {
+ u_char* p = (u_char*) dst;
+ char tmp[NS_INADDRSZ];
+ int i;
+
+ _DIAGASSERT(src != NULL);
+ _DIAGASSERT(dst != NULL);
+
+ /* Stash a temporary copy so our caller can update in place. */
+ memcpy(tmp, src, NS_INADDRSZ);
+ /* Mark this ipv6 addr as a mapped ipv4. */
+ for (i = 0; i < 10; i++) *p++ = 0x00;
+ *p++ = 0xff;
+ *p++ = 0xff;
+ /* Retrieve the saved copy and we're done. */
+ memcpy(p, tmp, NS_INADDRSZ);
+}
+
+static void convert_v4v6_hostent(struct hostent* hp, char** bpp, char* ep,
+ std::function<void(struct hostent* hp)> map_param,
+ std::function<void(char* src, char* dst)> map_addr) {
+ _DIAGASSERT(hp != NULL);
+ _DIAGASSERT(bpp != NULL);
+ _DIAGASSERT(ep != NULL);
+
+ if (hp->h_addrtype != AF_INET || hp->h_length != NS_INADDRSZ) return;
+ map_param(hp);
+ for (char** ap = hp->h_addr_list; *ap; ap++) {
+ int i = (int) (sizeof(align) - (size_t)((u_long) *bpp % sizeof(align)));
+
+ if (ep - *bpp < (i + NS_IN6ADDRSZ)) {
+ /* Out of memory. Truncate address list here. XXX */
+ *ap = NULL;
+ return;
+ }
+ *bpp += i;
+ map_addr(*ap, *bpp);
+ *ap = *bpp;
+ *bpp += NS_IN6ADDRSZ;
+ }
+}
+
+static void map_v4v6_hostent(struct hostent* hp, char** bpp, char* ep) {
+ convert_v4v6_hostent(hp, bpp, ep,
+ [](struct hostent* hp) {
+ hp->h_addrtype = AF_INET6;
+ hp->h_length = NS_IN6ADDRSZ;
+ },
+ [](char* src, char* dst) { map_v4v6_address(src, dst); });
+}
+
+/* Reserve space for mapping IPv4 address to IPv6 address in place */
+static void pad_v4v6_hostent(struct hostent* hp, char** bpp, char* ep) {
+ convert_v4v6_hostent(hp, bpp, ep,
+ [](struct hostent* hp) {
+ (void) hp; /* unused */
+ },
+ [](char* src, char* dst) {
+ memcpy(dst, src, NS_INADDRSZ);
+ memcpy(dst + NS_INADDRSZ, NAT64_PAD, sizeof(NAT64_PAD));
+ });
+}
+
+static void addrsort(char** ap, int num, res_state res) {
+ int i, j;
+ char** p;
+ short aval[MAXADDRS];
+ int needsort = 0;
+
+ _DIAGASSERT(ap != NULL);
+
+ p = ap;
+ for (i = 0; i < num; i++, p++) {
+ for (j = 0; (unsigned) j < res->nsort; j++)
+ if (res->sort_list[j].addr.s_addr ==
+ (((struct in_addr*) (void*) (*p))->s_addr & res->sort_list[j].mask))
+ break;
+ aval[i] = j;
+ if (needsort == 0 && i > 0 && j < aval[i - 1]) needsort = i;
+ }
+ if (!needsort) return;
+
+ while (needsort < num) {
+ for (j = needsort - 1; j >= 0; j--) {
+ if (aval[j] > aval[j + 1]) {
+ char* hp;
+
+ i = aval[j];
+ aval[j] = aval[j + 1];
+ aval[j + 1] = i;
+
+ hp = ap[j];
+ ap[j] = ap[j + 1];
+ ap[j + 1] = hp;
+ } else
+ break;
+ }
+ needsort++;
+ }
+}
+
+static int dns_gethtbyname(const char* name, int addr_type, getnamaddr* info) {
+ int n, type;
+ info->hp->h_addrtype = addr_type;
+
+ switch (info->hp->h_addrtype) {
+ case AF_INET:
+ info->hp->h_length = NS_INADDRSZ;
+ type = T_A;
+ break;
+ case AF_INET6:
+ info->hp->h_length = NS_IN6ADDRSZ;
+ type = T_AAAA;
+ break;
+ default:
+ return EAI_FAMILY;
+ }
+ auto buf = std::make_unique<querybuf>();
+
+ res_state res = res_get_state();
+ if (!res) return EAI_MEMORY;
+
+ int he;
+ n = res_nsearch(res, name, C_IN, type, buf->buf, (int)sizeof(buf->buf), &he);
+ if (n < 0) {
+ LOG(DEBUG) << __func__ << ": res_nsearch failed (" << n << ")";
+ // Return h_errno (he) to catch more detailed errors rather than EAI_NODATA.
+ // Note that res_nsearch() doesn't set the pair NETDB_INTERNAL and errno.
+ // See also herrnoToAiErrno().
+ return herrnoToAiErrno(he);
+ }
+ hostent* hp = getanswer(buf.get(), n, name, type, res, info->hp, info->buf, info->buflen, &he);
+ if (hp == NULL) return herrnoToAiErrno(he);
+
+ return 0;
+}
+
+static int dns_gethtbyaddr(const unsigned char* uaddr, int len, int af,
+ const android_net_context* netcontext, getnamaddr* info,
+ NetworkDnsEventReported* event) {
+ char qbuf[MAXDNAME + 1], *qp, *ep;
+ int n;
+ int advance;
+
+ info->hp->h_length = len;
+ info->hp->h_addrtype = af;
+
+ switch (info->hp->h_addrtype) {
+ case AF_INET:
+ (void) snprintf(qbuf, sizeof(qbuf), "%u.%u.%u.%u.in-addr.arpa", (uaddr[3] & 0xff),
+ (uaddr[2] & 0xff), (uaddr[1] & 0xff), (uaddr[0] & 0xff));
+ break;
+
+ case AF_INET6:
+ qp = qbuf;
+ ep = qbuf + sizeof(qbuf) - 1;
+ for (n = NS_IN6ADDRSZ - 1; n >= 0; n--) {
+ advance = snprintf(qp, (size_t)(ep - qp), "%x.%x.", uaddr[n] & 0xf,
+ ((unsigned int) uaddr[n] >> 4) & 0xf);
+ if (advance > 0 && qp + advance < ep)
+ qp += advance;
+ else {
+ // TODO: Consider converting to a private extended EAI_* error code.
+ // Currently, the EAI_* value has no corresponding error code for an internal
+ // out of buffer space. In order to not rely on errno, convert the original
+ // error code EAI_SYSTEM to EAI_MEMORY.
+ return EAI_MEMORY;
+ }
+ }
+ if (strlcat(qbuf, "ip6.arpa", sizeof(qbuf)) >= sizeof(qbuf)) {
+ // TODO: Consider converting to a private extended EAI_* error code.
+ // Currently, the EAI_* value has no corresponding error code for an internal
+ // out of buffer space. In order to not rely on errno, convert the original
+ // error code EAI_SYSTEM to EAI_MEMORY.
+ return EAI_MEMORY;
+ }
+ break;
+ default:
+ return EAI_FAMILY;
+ }
+
+ auto buf = std::make_unique<querybuf>();
+
+ res_state res = res_get_state();
+ if (!res) return EAI_MEMORY;
+
+ res_setnetcontext(res, netcontext, event);
+ int he;
+ n = res_nquery(res, qbuf, C_IN, T_PTR, buf->buf, (int)sizeof(buf->buf), &he);
+ if (n < 0) {
+ LOG(DEBUG) << __func__ << ": res_nquery failed (" << n << ")";
+ // Note that res_nquery() doesn't set the pair NETDB_INTERNAL and errno.
+ // Return h_errno (he) to catch more detailed errors rather than EAI_NODATA.
+ // See also herrnoToAiErrno().
+ return herrnoToAiErrno(he);
+ }
+ hostent* hp = getanswer(buf.get(), n, qbuf, T_PTR, res, info->hp, info->buf, info->buflen, &he);
+ if (hp == NULL) return herrnoToAiErrno(he);
+
+ char* bf = (char*) (hp->h_addr_list + 2);
+ size_t blen = (size_t)(bf - info->buf);
+ if (blen + info->hp->h_length > info->buflen) goto nospc;
+ hp->h_addr_list[0] = bf;
+ hp->h_addr_list[1] = NULL;
+ memcpy(bf, uaddr, (size_t) info->hp->h_length);
+ if (info->hp->h_addrtype == AF_INET && (res->options & RES_USE_INET6)) {
+ if (blen + NS_IN6ADDRSZ > info->buflen) goto nospc;
+ map_v4v6_address(bf, bf);
+ hp->h_addrtype = AF_INET6;
+ hp->h_length = NS_IN6ADDRSZ;
+ }
+
+ /* Reserve enough space for mapping IPv4 address to IPv6 address in place */
+ if (info->hp->h_addrtype == AF_INET) {
+ if (blen + NS_IN6ADDRSZ > info->buflen) goto nospc;
+ // Pad zero to the unused address space
+ memcpy(bf + NS_INADDRSZ, NAT64_PAD, sizeof(NAT64_PAD));
+ }
+
+ return 0;
+
+nospc:
+ return EAI_MEMORY;
+}
+
+/*
+ * Non-reentrant versions.
+ */
+
+int android_gethostbynamefornetcontext(const char* name, int af,
+ const struct android_net_context* netcontext, hostent** hp,
+ NetworkDnsEventReported* event) {
+ assert(event != nullptr);
+
+ res_state res = res_get_state();
+ if (res == NULL) return EAI_MEMORY;
+ res_static* rs = res_get_static(); // For thread-safety.
+ int error;
+ error = gethostbyname_internal(name, af, res, &rs->host, rs->hostbuf, sizeof(rs->hostbuf),
+ netcontext, event);
+ if (error == 0) {
+ *hp = &rs->host;
+ }
+ return error;
+}
+
+int android_gethostbyaddrfornetcontext(const void* addr, socklen_t len, int af,
+ const struct android_net_context* netcontext, hostent** hp,
+ NetworkDnsEventReported* event) {
+ return android_gethostbyaddrfornetcontext_proxy(addr, len, af, netcontext, hp, event);
+}
+
+static int android_gethostbyaddrfornetcontext_proxy(const void* addr, socklen_t len, int af,
+ const struct android_net_context* netcontext,
+ hostent** hp, NetworkDnsEventReported* event) {
+ assert(event != nullptr);
+
+ struct res_static* rs = res_get_static(); // For thread-safety.
+ int error = android_gethostbyaddrfornetcontext_proxy_internal(
+ addr, len, af, &rs->host, rs->hostbuf, sizeof(rs->hostbuf), netcontext, event);
+ if (error == 0) *hp = &rs->host;
+ return error;
+}
+
+int herrnoToAiErrno(int he) {
+ switch (he) {
+ // extended h_errno
+ case NETD_RESOLV_H_ERRNO_EXT_TIMEOUT:
+ return NETD_RESOLV_TIMEOUT;
+ // legacy h_errno
+ case NETDB_SUCCESS:
+ return 0;
+ case HOST_NOT_FOUND: // TODO: Perhaps convert HOST_NOT_FOUND to EAI_NONAME instead
+ case NO_DATA: // NO_ADDRESS
+ return EAI_NODATA;
+ case TRY_AGAIN:
+ return EAI_AGAIN;
+ case NETDB_INTERNAL:
+ // TODO: Remove ENOSPC and call abort() immediately whenever any allocation fails.
+ if (errno == ENOSPC) return EAI_MEMORY;
+ // Theoretically, this should not happen. Leave this here just in case.
+ // Currently, getanswer() of {gethnamaddr, getaddrinfo}.cpp, res_nsearch() and
+ // res_searchN() use this function to convert error code. Only getanswer()
+ // of gethnamaddr.cpp may return the error code pair, herrno NETDB_INTERNAL and
+ // errno ENOSPC, which has already converted to EAI_MEMORY. The remaining functions
+ // don't set the pair herrno and errno.
+ return EAI_SYSTEM; // see errno for detail
+ case NO_RECOVERY:
+ default:
+ return EAI_FAIL; // TODO: Perhaps convert default to EAI_MAX (unknown error) instead
+ }
+}
diff --git a/resolv/gethnamaddr.h b/resolv/gethnamaddr.h
new file mode 100644
index 0000000..bfdb14e
--- /dev/null
+++ b/resolv/gethnamaddr.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#pragma once
+
+#include <netdb.h> // struct hostent
+#include "netd_resolv/resolv.h" // struct android_net_context
+#include "stats.pb.h"
+
+// This is the entry point for the gethostbyname() family of legacy calls.
+int android_gethostbynamefornetcontext(const char*, int, const android_net_context*, hostent**,
+ android::net::NetworkDnsEventReported*);
+
+// This is the entry point for the gethostbyaddr() family of legacy calls.
+int android_gethostbyaddrfornetcontext(const void*, socklen_t, int, const android_net_context*,
+ hostent**, android::net::NetworkDnsEventReported*);
diff --git a/resolv/hostent.h b/resolv/hostent.h
new file mode 100644
index 0000000..4f6a33b
--- /dev/null
+++ b/resolv/hostent.h
@@ -0,0 +1,72 @@
+/* $NetBSD: hostent.h,v 1.2 2013/08/27 09:56:12 christos Exp $ */
+
+/*-
+ * Copyright (c) 2013 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef NETD_RESOLV_HOSTENT_H
+#define NETD_RESOLV_HOSTENT_H
+
+#include <netdb.h>
+#include <stdio.h>
+
+struct getnamaddr {
+ struct hostent* hp;
+ char* buf;
+ size_t buflen;
+};
+
+// /etc/hosts lookup
+int _hf_gethtbyaddr(const unsigned char* uaddr, int len, int af, getnamaddr* info);
+int _hf_gethtbyname2(const char* name, int af, getnamaddr* info);
+hostent* netbsd_gethostent_r(FILE*, struct hostent*, char*, size_t, int*);
+
+// Reserved padding for remapping IPv4 address to NAT64 synthesis IPv6 address
+static const char NAT64_PAD[NS_IN6ADDRSZ - NS_INADDRSZ] = {};
+
+#define HENT_ARRAY(dst, anum, ptr, len) do { \
+ size_t _len = (anum + 1) * sizeof(*dst); \
+ if (_len > len) goto nospc; \
+ dst = (char**) ptr; \
+ ptr += _len; \
+ len -= _len; \
+ } while (0)
+
+#define HENT_COPY(dst, src, slen, ptr, len) do { \
+ if ((size_t) slen > len) goto nospc; \
+ memcpy(ptr, src, (size_t) slen); \
+ dst = ptr; \
+ ptr += slen; \
+ len -= slen; \
+ } while (0)
+
+#define HENT_SCOPY(dst, src, ptr, len) do { \
+ size_t _len = strlen(src) + 1; \
+ HENT_COPY(dst, src, _len, ptr, len); \
+ } while (0)
+
+#endif // NETD_RESOLV_HOSTENT_H
diff --git a/resolv/include/netd_resolv/params.h b/resolv/include/netd_resolv/params.h
new file mode 100644
index 0000000..93e6287
--- /dev/null
+++ b/resolv/include/netd_resolv/params.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#ifndef NETD_RESOLV_PARAMS_H
+#define NETD_RESOLV_PARAMS_H
+
+#include <stdint.h>
+
+#define MAXNS 4 // max # name servers we'll track
+#define MAXDNSRCH 6 // max # domains in search path
+#define MAXDNSRCHPATH 256 // max length of domain search paths
+#define MAXNSSAMPLES 64 // max # samples to store per server
+
+// Per-netid configuration parameters passed from netd to the resolver
+struct res_params {
+ uint16_t sample_validity; // sample lifetime in s
+ // threshold of success / total samples below which a server is considered broken
+ uint8_t success_threshold; // 0: disable, value / 100 otherwise
+ uint8_t min_samples; // min # samples needed for statistics to be considered meaningful
+ uint8_t max_samples; // max # samples taken into account for statistics
+ int base_timeout_msec; // base query retry timeout (if 0, use RES_TIMEOUT)
+ int retry_count; // number of retries
+};
+
+#define LIBNETD_RESOLV_PUBLIC extern "C" [[gnu::visibility("default")]]
+
+#endif // NETD_RESOLV_PARAMS_H
diff --git a/resolv/include/netd_resolv/resolv.h b/resolv/include/netd_resolv/resolv.h
new file mode 100644
index 0000000..3a71110
--- /dev/null
+++ b/resolv/include/netd_resolv/resolv.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "params.h"
+
+#include <netinet/in.h>
+
+typedef union sockaddr_union {
+ struct sockaddr sa;
+ struct sockaddr_in sin;
+ struct sockaddr_in6 sin6;
+} sockaddr_union;
+
+/*
+ * Passing NETID_UNSET as the netId causes system/netd/resolv/DnsProxyListener.cpp to
+ * fill in the appropriate default netId for the query.
+ */
+#define NETID_UNSET 0u
+
+/*
+ * MARK_UNSET represents the default (i.e. unset) value for a socket mark.
+ */
+#define MARK_UNSET 0u
+
+/*
+ * Error code extending EAI_* codes defined in bionic/libc/include/netdb.h.
+ * This error code, including EAI_*, returned from android_getaddrinfofornetcontext()
+ * and android_gethostbynamefornetcontext() are used for DNS metrics.
+ */
+#define NETD_RESOLV_TIMEOUT 255 // consistent with RCODE_TIMEOUT
+
+/*
+ * A struct to capture context relevant to network operations.
+ *
+ * Application and DNS netids/marks can differ from one another under certain
+ * circumstances, notably when a VPN applies to the given uid's traffic but the
+ * VPN network does not have its own DNS servers explicitly provisioned.
+ *
+ * The introduction of per-UID routing means the uid is also an essential part
+ * of the evaluation context. Its proper uninitialized value is
+ * NET_CONTEXT_INVALID_UID.
+ */
+struct android_net_context {
+ unsigned app_netid;
+ unsigned app_mark;
+ unsigned dns_netid;
+ unsigned dns_mark;
+ uid_t uid;
+ unsigned flags;
+};
+
+#define NET_CONTEXT_INVALID_UID ((uid_t) -1)
+#define NET_CONTEXT_FLAG_USE_LOCAL_NAMESERVERS 0x00000001
+#define NET_CONTEXT_FLAG_USE_EDNS 0x00000002
+
+// TODO: investigate having the resolver check permissions itself, either by adding support to
+// libbinder_ndk or by converting IPermissionController into a stable AIDL interface.
+typedef bool (*check_calling_permission_callback)(const char* permission);
+typedef void (*get_network_context_callback)(unsigned netid, uid_t uid,
+ android_net_context* netcontext);
+typedef void (*log_callback)(const char* msg);
+
+/*
+ * Some functions needed by the resolver (e.g. checkCallingPermission()) live in
+ * libraries with no ABI stability guarantees, such as libbinder.so.
+ * As a temporary workaround, we keep these functions in netd and call them via
+ * function pointers.
+ */
+struct ResolverNetdCallbacks {
+ check_calling_permission_callback check_calling_permission;
+ get_network_context_callback get_network_context;
+ log_callback log;
+};
+
+LIBNETD_RESOLV_PUBLIC bool resolv_has_nameservers(unsigned netid);
+
+// Set callbacks and bring DnsResolver up.
+LIBNETD_RESOLV_PUBLIC bool resolv_init(const ResolverNetdCallbacks* callbacks);
diff --git a/resolv/include/netd_resolv/resolv_stub.h b/resolv/include/netd_resolv/resolv_stub.h
new file mode 100644
index 0000000..41f56fa
--- /dev/null
+++ b/resolv/include/netd_resolv/resolv_stub.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef NETD_RESOLV_RESOLV_STUB_H
+#define NETD_RESOLV_RESOLV_STUB_H
+
+#include "resolv.h"
+#include "stats.h"
+
+// Struct containing function pointers for every function exported by libnetd_resolv.
+extern struct ResolvStub {
+ bool (*resolv_has_nameservers)(unsigned netid);
+
+ bool (*resolv_init)(const ResolverNetdCallbacks& callbacks);
+} RESOLV_STUB;
+
+int resolv_stub_init();
+
+#endif // NETD_RESOLV_RESOLV_STUB_H
diff --git a/resolv/include/netd_resolv/stats.h b/resolv/include/netd_resolv/stats.h
new file mode 100644
index 0000000..7bfe845
--- /dev/null
+++ b/resolv/include/netd_resolv/stats.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#ifndef NETD_RES_STATS_H
+#define NETD_RES_STATS_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <sys/socket.h>
+#include <time.h>
+
+#include "params.h"
+
+#define RCODE_INTERNAL_ERROR 254
+#define RCODE_TIMEOUT 255
+
+struct res_sample {
+ time_t at; // time in s at which the sample was recorded
+ uint16_t rtt; // round-trip time in ms
+ uint8_t rcode; // the DNS rcode or RCODE_XXX defined above
+};
+
+// Resolver reachability statistics and run-time parameters.
+struct res_stats {
+ // Stats of the last <sample_count> queries.
+ res_sample samples[MAXNSSAMPLES];
+ // The number of samples stored.
+ uint8_t sample_count;
+ // The next sample to modify.
+ uint8_t sample_next;
+};
+
+// Aggregates the reachability statistics for the given server based on on the stored samples.
+LIBNETD_RESOLV_PUBLIC void android_net_res_stats_aggregate(res_stats* stats, int* successes,
+ int* errors, int* timeouts,
+ int* internal_errors, int* rtt_avg,
+ time_t* last_sample_time);
+
+LIBNETD_RESOLV_PUBLIC int android_net_res_stats_get_info_for_net(
+ unsigned netid, int* nscount, sockaddr_storage servers[MAXNS], int* dcount,
+ char domains[MAXDNSRCH][MAXDNSRCHPATH], res_params* params, res_stats stats[MAXNS],
+ int* wait_for_pending_req_timeout_count);
+
+// Returns an array of bools indicating which servers are considered good
+LIBNETD_RESOLV_PUBLIC int android_net_res_stats_get_usable_servers(const res_params* params,
+ res_stats stats[], int nscount,
+ bool valid_servers[]);
+
+#endif // NETD_RES_STATS_H
diff --git a/resolv/libnetd_resolv.map.txt b/resolv/libnetd_resolv.map.txt
new file mode 100644
index 0000000..be193db
--- /dev/null
+++ b/resolv/libnetd_resolv.map.txt
@@ -0,0 +1,27 @@
+#
+# Copyright (C) 2018 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 lists the entry points visible to applications that use the
+# libnetd_resolv library. Other entry points present in the library won't be
+# usable.
+
+LIBNETD_RESOLV {
+ global:
+ resolv_has_nameservers;
+ resolv_init;
+ local:
+ *;
+};
diff --git a/resolv/libnetd_resolv_test.cpp b/resolv/libnetd_resolv_test.cpp
new file mode 100644
index 0000000..6e4b9ee
--- /dev/null
+++ b/resolv/libnetd_resolv_test.cpp
@@ -0,0 +1,609 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#define LOG_TAG "libnetd_resolv_test"
+
+#include <gtest/gtest.h>
+
+#include <android-base/stringprintf.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include "dns_responder.h"
+#include "getaddrinfo.h"
+#include "gethnamaddr.h"
+#include "resolv_cache.h"
+#include "stats.pb.h"
+
+// TODO: make this dynamic and stop depending on implementation details.
+constexpr unsigned int TEST_NETID = 30;
+
+// Specifying 0 in ai_socktype or ai_protocol of struct addrinfo indicates that any type or
+// protocol can be returned by getaddrinfo().
+constexpr unsigned int ANY = 0;
+
+using android::base::StringPrintf;
+
+namespace android {
+namespace net {
+
+using android::net::NetworkDnsEventReported;
+
+// Minimize class ResolverTest to be class TestBase because class TestBase doesn't need all member
+// functions of class ResolverTest and class DnsResponderClient.
+class TestBase : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ // Create cache for test
+ resolv_create_cache_for_net(TEST_NETID);
+ }
+ void TearDown() override {
+ // Delete cache for test
+ resolv_delete_cache_for_net(TEST_NETID);
+ }
+
+ static std::string ToString(const hostent* he) {
+ if (he == nullptr) return "<null>";
+ char buffer[INET6_ADDRSTRLEN];
+ if (!inet_ntop(he->h_addrtype, he->h_addr_list[0], buffer, sizeof(buffer))) {
+ return "<invalid>";
+ }
+ return buffer;
+ }
+
+ static std::string ToString(const addrinfo* ai) {
+ if (!ai) return "<null>";
+ for (const auto* aip = ai; aip != nullptr; aip = aip->ai_next) {
+ char host[NI_MAXHOST];
+ int rv = getnameinfo(aip->ai_addr, aip->ai_addrlen, host, sizeof(host), nullptr, 0,
+ NI_NUMERICHOST);
+ if (rv != 0) return gai_strerror(rv);
+ return host;
+ }
+ return "<invalid>";
+ }
+
+ size_t GetNumQueries(const test::DNSResponder& dns, const char* name) const {
+ auto queries = dns.queries();
+ size_t found = 0;
+ for (const auto& p : queries) {
+ if (p.first == name) {
+ ++found;
+ }
+ }
+ return found;
+ }
+
+ const char* mDefaultSearchDomains = "example.com";
+ const res_params mDefaultParams_Binder = {
+ .sample_validity = 300,
+ .success_threshold = 25,
+ .min_samples = 8,
+ .max_samples = 8,
+ .base_timeout_msec = 1000,
+ .retry_count = 2,
+ };
+ const android_net_context mNetcontext = {
+ .app_netid = TEST_NETID,
+ .app_mark = MARK_UNSET,
+ .dns_netid = TEST_NETID,
+ .dns_mark = MARK_UNSET,
+ .uid = NET_CONTEXT_INVALID_UID,
+ };
+};
+
+class GetAddrInfoForNetContextTest : public TestBase {};
+class GetHostByNameForNetContextTest : public TestBase {};
+
+TEST_F(GetAddrInfoForNetContextTest, InvalidParameters) {
+ // Both null "netcontext" and null "res" of android_getaddrinfofornetcontext() are not tested
+ // here because they are checked by assert() without returning any error number.
+
+ // Invalid hostname and servname.
+ // Both hostname and servname are null pointers. Expect error number EAI_NONAME.
+ struct addrinfo* result = nullptr;
+ NetworkDnsEventReported event;
+ int rv = android_getaddrinfofornetcontext(nullptr /*hostname*/, nullptr /*servname*/,
+ nullptr /*hints*/, &mNetcontext, &result, &event);
+ EXPECT_EQ(EAI_NONAME, rv);
+ if (result) {
+ freeaddrinfo(result);
+ result = nullptr;
+ }
+
+ // Invalid hints.
+ // These place holders are used to test function call with unrequired parameters.
+ // The content is not important because function call returns error directly if
+ // there have any unrequired parameter.
+ char placeholder_cname[] = "invalid_cname";
+ sockaddr placeholder_addr = {};
+ addrinfo placeholder_next = {};
+ static const struct TestConfig {
+ int ai_flags;
+ socklen_t ai_addrlen;
+ char* ai_canonname;
+ struct sockaddr* ai_addr;
+ struct addrinfo* ai_next;
+ int expected_errorno; // expected result
+
+ std::string asParameters() const {
+ return StringPrintf("0x%x/%u/%s/%p/%p", ai_flags, ai_addrlen,
+ ai_canonname ? ai_canonname : "(null)", (void*) ai_addr,
+ (void*) ai_next);
+ }
+ } testConfigs[]{
+ {0, sizeof(struct in_addr) /*bad*/, nullptr, nullptr, nullptr, EAI_BADHINTS},
+ {0, 0, placeholder_cname /*bad*/, nullptr, nullptr, EAI_BADHINTS},
+ {0, 0, nullptr, &placeholder_addr /*bad*/, nullptr, EAI_BADHINTS},
+ {0, 0, nullptr, nullptr, &placeholder_next /*bad*/, EAI_BADHINTS},
+ {AI_ALL /*bad*/, 0, nullptr, nullptr, nullptr, EAI_BADFLAGS},
+ {AI_V4MAPPED_CFG /*bad*/, 0, nullptr, nullptr, nullptr, EAI_BADFLAGS},
+ {AI_V4MAPPED /*bad*/, 0, nullptr, nullptr, nullptr, EAI_BADFLAGS},
+ {AI_DEFAULT /*bad*/, 0, nullptr, nullptr, nullptr, EAI_BADFLAGS},
+ };
+
+ for (const auto& config : testConfigs) {
+ SCOPED_TRACE(config.asParameters());
+
+ // In current test configuration set, ai_family, ai_protocol and ai_socktype are not
+ // checked because other fields cause hints error check failed first.
+ const struct addrinfo hints = {
+ .ai_flags = config.ai_flags,
+ .ai_family = AF_UNSPEC,
+ .ai_socktype = ANY,
+ .ai_protocol = ANY,
+ .ai_addrlen = config.ai_addrlen,
+ .ai_canonname = config.ai_canonname,
+ .ai_addr = config.ai_addr,
+ .ai_next = config.ai_next,
+ };
+
+ NetworkDnsEventReported event;
+ rv = android_getaddrinfofornetcontext("localhost", nullptr /*servname*/, &hints,
+ &mNetcontext, &result, &event);
+ EXPECT_EQ(config.expected_errorno, rv);
+
+ if (result) {
+ freeaddrinfo(result);
+ result = nullptr;
+ }
+ }
+}
+
+TEST_F(GetAddrInfoForNetContextTest, InvalidParameters_Family) {
+ for (int family = 0; family < AF_MAX; ++family) {
+ if (family == AF_UNSPEC || family == AF_INET || family == AF_INET6) {
+ continue; // skip supported family
+ }
+ SCOPED_TRACE(StringPrintf("family: %d", family));
+
+ struct addrinfo* result = nullptr;
+ const struct addrinfo hints = {
+ .ai_family = family, // unsupported family
+ };
+
+ NetworkDnsEventReported event;
+ int rv = android_getaddrinfofornetcontext("localhost", nullptr /*servname*/, &hints,
+ &mNetcontext, &result, &event);
+ EXPECT_EQ(EAI_FAMILY, rv);
+
+ if (result) freeaddrinfo(result);
+ }
+}
+
+TEST_F(GetAddrInfoForNetContextTest, InvalidParameters_MeaningfulSocktypeAndProtocolCombination) {
+ static const int families[] = {PF_INET, PF_INET6, PF_UNSPEC};
+ // Skip to test socket type SOCK_RAW in meaningful combination (explore_options[]) of
+ // system\netd\resolv\getaddrinfo.cpp. In explore_options[], the socket type SOCK_RAW always
+ // comes with protocol ANY which causes skipping meaningful socktype/protocol combination
+ // check. So it nerver returns error number EAI_BADHINTS which we want to test in this test
+ // case.
+ static const int socktypes[] = {SOCK_STREAM, SOCK_DGRAM};
+
+ // If both socktype/protocol are specified, check non-meaningful combination returns
+ // expected error number EAI_BADHINTS. See meaningful combination in explore_options[] of
+ // system\netd\resolv\getaddrinfo.cpp.
+ for (const auto& family : families) {
+ for (const auto& socktype : socktypes) {
+ for (int protocol = 0; protocol < IPPROTO_MAX; ++protocol) {
+ SCOPED_TRACE(StringPrintf("family: %d, socktype: %d, protocol: %d", family,
+ socktype, protocol));
+
+ // Both socktype/protocol need to be specified.
+ if (!socktype || !protocol) continue;
+
+ // Skip meaningful combination in explore_options[] of
+ // system\netd\resolv\getaddrinfo.cpp.
+ if ((family == AF_INET6 && socktype == SOCK_DGRAM && protocol == IPPROTO_UDP) ||
+ (family == AF_INET6 && socktype == SOCK_STREAM && protocol == IPPROTO_TCP) ||
+ (family == AF_INET && socktype == SOCK_DGRAM && protocol == IPPROTO_UDP) ||
+ (family == AF_INET && socktype == SOCK_STREAM && protocol == IPPROTO_TCP) ||
+ (family == AF_UNSPEC && socktype == SOCK_DGRAM && protocol == IPPROTO_UDP) ||
+ (family == AF_UNSPEC && socktype == SOCK_STREAM && protocol == IPPROTO_TCP)) {
+ continue;
+ }
+
+ struct addrinfo* result = nullptr;
+ const struct addrinfo hints = {
+ .ai_family = family,
+ .ai_protocol = protocol,
+ .ai_socktype = socktype,
+ };
+
+ NetworkDnsEventReported event;
+ int rv = android_getaddrinfofornetcontext("localhost", nullptr /*servname*/, &hints,
+ &mNetcontext, &result, &event);
+ EXPECT_EQ(EAI_BADHINTS, rv);
+
+ if (result) freeaddrinfo(result);
+ }
+ }
+ }
+}
+
+TEST_F(GetAddrInfoForNetContextTest, InvalidParameters_PortNameAndNumber) {
+ constexpr char http_portno[] = "80";
+ constexpr char invalid_portno[] = "65536"; // out of valid port range from 0 to 65535
+ constexpr char http_portname[] = "http";
+ constexpr char invalid_portname[] = "invalid_portname";
+
+ static const struct TestConfig {
+ int ai_flags;
+ int ai_family;
+ int ai_socktype;
+ const char* servname;
+ int expected_errorno; // expected result
+
+ std::string asParameters() const {
+ return StringPrintf("0x%x/%d/%d/%s", ai_flags, ai_family, ai_socktype,
+ servname ? servname : "(null)");
+ }
+ } testConfigs[]{
+ {0, AF_INET, SOCK_RAW /*bad*/, http_portno, EAI_SERVICE},
+ {0, AF_INET6, SOCK_RAW /*bad*/, http_portno, EAI_SERVICE},
+ {0, AF_UNSPEC, SOCK_RAW /*bad*/, http_portno, EAI_SERVICE},
+ {0, AF_INET, SOCK_RDM /*bad*/, http_portno, EAI_SOCKTYPE},
+ {0, AF_INET6, SOCK_RDM /*bad*/, http_portno, EAI_SOCKTYPE},
+ {0, AF_UNSPEC, SOCK_RDM /*bad*/, http_portno, EAI_SOCKTYPE},
+ {0, AF_INET, SOCK_SEQPACKET /*bad*/, http_portno, EAI_SOCKTYPE},
+ {0, AF_INET6, SOCK_SEQPACKET /*bad*/, http_portno, EAI_SOCKTYPE},
+ {0, AF_UNSPEC, SOCK_SEQPACKET /*bad*/, http_portno, EAI_SOCKTYPE},
+ {0, AF_INET, SOCK_DCCP /*bad*/, http_portno, EAI_SOCKTYPE},
+ {0, AF_INET6, SOCK_DCCP /*bad*/, http_portno, EAI_SOCKTYPE},
+ {0, AF_UNSPEC, SOCK_DCCP /*bad*/, http_portno, EAI_SOCKTYPE},
+ {0, AF_INET, SOCK_PACKET /*bad*/, http_portno, EAI_SOCKTYPE},
+ {0, AF_INET6, SOCK_PACKET /*bad*/, http_portno, EAI_SOCKTYPE},
+ {0, AF_UNSPEC, SOCK_PACKET /*bad*/, http_portno, EAI_SOCKTYPE},
+ {0, AF_INET, ANY, invalid_portno /*bad*/, EAI_SERVICE},
+ {0, AF_INET, SOCK_STREAM, invalid_portno /*bad*/, EAI_SERVICE},
+ {0, AF_INET, SOCK_DGRAM, invalid_portno /*bad*/, EAI_SERVICE},
+ {0, AF_INET6, ANY, invalid_portno /*bad*/, EAI_SERVICE},
+ {0, AF_INET6, SOCK_STREAM, invalid_portno /*bad*/, EAI_SERVICE},
+ {0, AF_INET6, SOCK_DGRAM, invalid_portno /*bad*/, EAI_SERVICE},
+ {0, AF_UNSPEC, ANY, invalid_portno /*bad*/, EAI_SERVICE},
+ {0, AF_UNSPEC, SOCK_STREAM, invalid_portno /*bad*/, EAI_SERVICE},
+ {0, AF_UNSPEC, SOCK_DGRAM, invalid_portno /*bad*/, EAI_SERVICE},
+ {AI_NUMERICSERV, AF_INET, ANY, http_portname /*bad*/, EAI_NONAME},
+ {AI_NUMERICSERV, AF_INET, SOCK_STREAM, http_portname /*bad*/, EAI_NONAME},
+ {AI_NUMERICSERV, AF_INET, SOCK_DGRAM, http_portname /*bad*/, EAI_NONAME},
+ {AI_NUMERICSERV, AF_INET6, ANY, http_portname /*bad*/, EAI_NONAME},
+ {AI_NUMERICSERV, AF_INET6, SOCK_STREAM, http_portname /*bad*/, EAI_NONAME},
+ {AI_NUMERICSERV, AF_INET6, SOCK_DGRAM, http_portname /*bad*/, EAI_NONAME},
+ {AI_NUMERICSERV, AF_UNSPEC, ANY, http_portname /*bad*/, EAI_NONAME},
+ {AI_NUMERICSERV, AF_UNSPEC, SOCK_STREAM, http_portname /*bad*/, EAI_NONAME},
+ {AI_NUMERICSERV, AF_UNSPEC, SOCK_DGRAM, http_portname /*bad*/, EAI_NONAME},
+ {0, AF_INET, ANY, invalid_portname /*bad*/, EAI_SERVICE},
+ {0, AF_INET, SOCK_STREAM, invalid_portname /*bad*/, EAI_SERVICE},
+ {0, AF_INET, SOCK_DGRAM, invalid_portname /*bad*/, EAI_SERVICE},
+ {0, AF_INET6, ANY, invalid_portname /*bad*/, EAI_SERVICE},
+ {0, AF_INET6, SOCK_STREAM, invalid_portname /*bad*/, EAI_SERVICE},
+ {0, AF_INET6, SOCK_DGRAM, invalid_portname /*bad*/, EAI_SERVICE},
+ {0, AF_UNSPEC, ANY, invalid_portname /*bad*/, EAI_SERVICE},
+ {0, AF_UNSPEC, SOCK_STREAM, invalid_portname /*bad*/, EAI_SERVICE},
+ {0, AF_UNSPEC, SOCK_DGRAM, invalid_portname /*bad*/, EAI_SERVICE},
+ };
+
+ for (const auto& config : testConfigs) {
+ const std::string testParameters = config.asParameters();
+ SCOPED_TRACE(testParameters);
+
+ const struct addrinfo hints = {
+ .ai_flags = config.ai_flags,
+ .ai_family = config.ai_family,
+ .ai_socktype = config.ai_socktype,
+ };
+
+ struct addrinfo* result = nullptr;
+ NetworkDnsEventReported event;
+ int rv = android_getaddrinfofornetcontext("localhost", config.servname, &hints,
+ &mNetcontext, &result, &event);
+ EXPECT_EQ(config.expected_errorno, rv);
+
+ if (result) freeaddrinfo(result);
+ }
+}
+
+TEST_F(GetAddrInfoForNetContextTest, AlphabeticalHostname_NoData) {
+ constexpr char listen_addr[] = "127.0.0.3";
+ constexpr char listen_srv[] = "53";
+ constexpr char v4_host_name[] = "v4only.example.com.";
+ test::DNSResponder dns(listen_addr, listen_srv, 250, ns_rcode::ns_r_servfail);
+ dns.addMapping(v4_host_name, ns_type::ns_t_a, "1.2.3.3");
+ ASSERT_TRUE(dns.startServer());
+ const char* servers[] = {listen_addr};
+ ASSERT_EQ(0, resolv_set_nameservers_for_net(TEST_NETID, servers,
+ sizeof(servers) / sizeof(servers[0]),
+ mDefaultSearchDomains, &mDefaultParams_Binder));
+ dns.clearQueries();
+
+ // Want AAAA answer but DNS server has A answer only.
+ struct addrinfo* result = nullptr;
+ const addrinfo hints = {.ai_family = AF_INET6};
+ NetworkDnsEventReported event;
+ int rv = android_getaddrinfofornetcontext("v4only", nullptr, &hints, &mNetcontext, &result,
+ &event);
+ EXPECT_LE(1U, GetNumQueries(dns, v4_host_name));
+ EXPECT_TRUE(result == nullptr);
+ EXPECT_EQ(EAI_NODATA, rv);
+
+ if (result) freeaddrinfo(result);
+}
+
+TEST_F(GetAddrInfoForNetContextTest, AlphabeticalHostname) {
+ constexpr char listen_addr[] = "127.0.0.3";
+ constexpr char listen_srv[] = "53";
+ constexpr char host_name[] = "sawadee.example.com.";
+ constexpr char v4addr[] = "1.2.3.4";
+ constexpr char v6addr[] = "::1.2.3.4";
+
+ test::DNSResponder dns(listen_addr, listen_srv, 250, ns_rcode::ns_r_servfail);
+ dns.addMapping(host_name, ns_type::ns_t_a, v4addr);
+ dns.addMapping(host_name, ns_type::ns_t_aaaa, v6addr);
+ ASSERT_TRUE(dns.startServer());
+ const char* servers[] = {listen_addr};
+ ASSERT_EQ(0, resolv_set_nameservers_for_net(TEST_NETID, servers,
+ sizeof(servers) / sizeof(servers[0]),
+ mDefaultSearchDomains, &mDefaultParams_Binder));
+
+ static const struct TestConfig {
+ int ai_family;
+ const std::string expected_addr;
+ } testConfigs[]{
+ {AF_INET, v4addr},
+ {AF_INET6, v6addr},
+ };
+
+ for (const auto& config : testConfigs) {
+ SCOPED_TRACE(StringPrintf("family: %d", config.ai_family));
+ dns.clearQueries();
+
+ struct addrinfo* result = nullptr;
+ const struct addrinfo hints = {.ai_family = config.ai_family};
+ NetworkDnsEventReported event;
+ int rv = android_getaddrinfofornetcontext("sawadee", nullptr, &hints, &mNetcontext, &result,
+ &event);
+ EXPECT_EQ(0, rv);
+ EXPECT_TRUE(result != nullptr);
+ EXPECT_EQ(1U, GetNumQueries(dns, host_name));
+ EXPECT_EQ(config.expected_addr, ToString(result));
+
+ if (result) freeaddrinfo(result);
+ }
+}
+
+TEST_F(GetAddrInfoForNetContextTest, ServerResponseError) {
+ constexpr char listen_addr[] = "127.0.0.3";
+ constexpr char listen_srv[] = "53";
+ constexpr char host_name[] = "hello.example.com.";
+
+ static const struct TestConfig {
+ ns_rcode rcode;
+ int expected_errorno; // expected result
+
+ // Only test failure RCODE [1..5] in RFC 1035 section 4.1.1 and skip successful RCODE 0
+ // which means no error.
+ } testConfigs[]{
+ // clang-format off
+ {ns_rcode::ns_r_formerr, EAI_FAIL},
+ {ns_rcode::ns_r_servfail, EAI_AGAIN},
+ {ns_rcode::ns_r_nxdomain, EAI_NODATA},
+ {ns_rcode::ns_r_notimpl, EAI_FAIL},
+ {ns_rcode::ns_r_refused, EAI_FAIL},
+ // clang-format on
+ };
+
+ for (const auto& config : testConfigs) {
+ SCOPED_TRACE(StringPrintf("rcode: %d", config.rcode));
+
+ test::DNSResponder dns(listen_addr, listen_srv, 250,
+ config.rcode /*response specific rcode*/);
+ dns.addMapping(host_name, ns_type::ns_t_a, "1.2.3.4");
+ dns.setResponseProbability(0.0); // always ignore requests and response preset rcode
+ ASSERT_TRUE(dns.startServer());
+ const char* servers[] = {listen_addr};
+ ASSERT_EQ(0, resolv_set_nameservers_for_net(TEST_NETID, servers,
+ sizeof(servers) / sizeof(servers[0]),
+ mDefaultSearchDomains, &mDefaultParams_Binder));
+
+ struct addrinfo* result = nullptr;
+ const struct addrinfo hints = {.ai_family = AF_UNSPEC};
+ NetworkDnsEventReported event;
+ int rv = android_getaddrinfofornetcontext(host_name, nullptr, &hints, &mNetcontext, &result,
+ &event);
+ EXPECT_EQ(config.expected_errorno, rv);
+
+ if (result) freeaddrinfo(result);
+ }
+}
+
+// TODO: Add private DNS server timeout test.
+TEST_F(GetAddrInfoForNetContextTest, ServerTimeout) {
+ constexpr char listen_addr[] = "127.0.0.3";
+ constexpr char listen_srv[] = "53";
+ constexpr char host_name[] = "hello.example.com.";
+ test::DNSResponder dns(listen_addr, listen_srv, 250, static_cast<ns_rcode>(-1) /*no response*/);
+ dns.addMapping(host_name, ns_type::ns_t_a, "1.2.3.4");
+ dns.setResponseProbability(0.0); // always ignore requests and don't response
+ ASSERT_TRUE(dns.startServer());
+ const char* servers[] = {listen_addr};
+ ASSERT_EQ(0, resolv_set_nameservers_for_net(TEST_NETID, servers,
+ sizeof(servers) / sizeof(servers[0]),
+ mDefaultSearchDomains, &mDefaultParams_Binder));
+
+ struct addrinfo* result = nullptr;
+ const struct addrinfo hints = {.ai_family = AF_UNSPEC};
+ NetworkDnsEventReported event;
+ int rv = android_getaddrinfofornetcontext("hello", nullptr, &hints, &mNetcontext, &result,
+ &event);
+ EXPECT_EQ(NETD_RESOLV_TIMEOUT, rv);
+
+ if (result) freeaddrinfo(result);
+}
+
+TEST_F(GetHostByNameForNetContextTest, AlphabeticalHostname) {
+ constexpr char listen_addr[] = "127.0.0.3";
+ constexpr char listen_srv[] = "53";
+ constexpr char host_name[] = "jiababuei.example.com.";
+ constexpr char v4addr[] = "1.2.3.4";
+ constexpr char v6addr[] = "::1.2.3.4";
+
+ test::DNSResponder dns(listen_addr, listen_srv, 250, ns_rcode::ns_r_servfail);
+ dns.addMapping(host_name, ns_type::ns_t_a, v4addr);
+ dns.addMapping(host_name, ns_type::ns_t_aaaa, v6addr);
+ ASSERT_TRUE(dns.startServer());
+ const char* servers[] = {listen_addr};
+ ASSERT_EQ(0, resolv_set_nameservers_for_net(TEST_NETID, servers,
+ sizeof(servers) / sizeof(servers[0]),
+ mDefaultSearchDomains, &mDefaultParams_Binder));
+
+ static const struct TestConfig {
+ int ai_family;
+ const std::string expected_addr;
+ } testConfigs[]{
+ {AF_INET, v4addr},
+ {AF_INET6, v6addr},
+ };
+
+ for (const auto& config : testConfigs) {
+ SCOPED_TRACE(StringPrintf("family: %d", config.ai_family));
+ dns.clearQueries();
+
+ struct hostent* hp = nullptr;
+ NetworkDnsEventReported event;
+ int rv = android_gethostbynamefornetcontext("jiababuei", config.ai_family, &mNetcontext,
+ &hp, &event);
+ EXPECT_EQ(0, rv);
+ EXPECT_TRUE(hp != nullptr);
+ EXPECT_EQ(1U, GetNumQueries(dns, host_name));
+ EXPECT_EQ(config.expected_addr, ToString(hp));
+ }
+}
+
+TEST_F(GetHostByNameForNetContextTest, NoData) {
+ constexpr char listen_addr[] = "127.0.0.3";
+ constexpr char listen_srv[] = "53";
+ constexpr char v4_host_name[] = "v4only.example.com.";
+ test::DNSResponder dns(listen_addr, listen_srv, 250, ns_rcode::ns_r_servfail);
+ dns.addMapping(v4_host_name, ns_type::ns_t_a, "1.2.3.3");
+ ASSERT_TRUE(dns.startServer());
+ const char* servers[] = {listen_addr};
+ ASSERT_EQ(0, resolv_set_nameservers_for_net(TEST_NETID, servers,
+ sizeof(servers) / sizeof(servers[0]),
+ mDefaultSearchDomains, &mDefaultParams_Binder));
+ dns.clearQueries();
+
+ // Want AAAA answer but DNS server has A answer only.
+ struct hostent* hp = nullptr;
+ NetworkDnsEventReported event;
+ int rv = android_gethostbynamefornetcontext("v4only", AF_INET6, &mNetcontext, &hp, &event);
+ EXPECT_LE(1U, GetNumQueries(dns, v4_host_name));
+ EXPECT_TRUE(hp == nullptr);
+ EXPECT_EQ(EAI_NODATA, rv);
+}
+
+TEST_F(GetHostByNameForNetContextTest, ServerResponseError) {
+ constexpr char listen_addr[] = "127.0.0.3";
+ constexpr char listen_srv[] = "53";
+ constexpr char host_name[] = "hello.example.com.";
+
+ static const struct TestConfig {
+ ns_rcode rcode;
+ int expected_errorno; // expected result
+
+ // Only test failure RCODE [1..5] in RFC 1035 section 4.1.1 and skip successful RCODE 0
+ // which means no error. Note that the return error codes aren't mapped by rcode in the
+ // test case SERVFAIL, NOTIMP and REFUSED. See the comment of res_nsend()
+ // in system\netd\resolv\res_query.cpp for more detail.
+ } testConfigs[]{
+ // clang-format off
+ {ns_rcode::ns_r_formerr, EAI_FAIL},
+ {ns_rcode::ns_r_servfail, EAI_AGAIN}, // Not mapped by rcode.
+ {ns_rcode::ns_r_nxdomain, EAI_NODATA},
+ {ns_rcode::ns_r_notimpl, EAI_AGAIN}, // Not mapped by rcode.
+ {ns_rcode::ns_r_refused, EAI_AGAIN}, // Not mapped by rcode.
+ // clang-format on
+ };
+
+ for (const auto& config : testConfigs) {
+ SCOPED_TRACE(StringPrintf("rcode: %d", config.rcode));
+
+ test::DNSResponder dns(listen_addr, listen_srv, 250,
+ config.rcode /*response specific rcode*/);
+ dns.addMapping(host_name, ns_type::ns_t_a, "1.2.3.4");
+ dns.setResponseProbability(0.0); // always ignore requests and response preset rcode
+ ASSERT_TRUE(dns.startServer());
+ const char* servers[] = {listen_addr};
+ ASSERT_EQ(0, resolv_set_nameservers_for_net(TEST_NETID, servers,
+ sizeof(servers) / sizeof(servers[0]),
+ mDefaultSearchDomains, &mDefaultParams_Binder));
+
+ struct hostent* hp = nullptr;
+ NetworkDnsEventReported event;
+ int rv = android_gethostbynamefornetcontext(host_name, AF_INET, &mNetcontext, &hp, &event);
+ EXPECT_TRUE(hp == nullptr);
+ EXPECT_EQ(config.expected_errorno, rv);
+ }
+}
+
+// TODO: Add private DNS server timeout test.
+TEST_F(GetHostByNameForNetContextTest, ServerTimeout) {
+ constexpr char listen_addr[] = "127.0.0.3";
+ constexpr char listen_srv[] = "53";
+ constexpr char host_name[] = "hello.example.com.";
+ test::DNSResponder dns(listen_addr, listen_srv, 250, static_cast<ns_rcode>(-1) /*no response*/);
+ dns.addMapping(host_name, ns_type::ns_t_a, "1.2.3.4");
+ dns.setResponseProbability(0.0); // always ignore requests and don't response
+ ASSERT_TRUE(dns.startServer());
+ const char* servers[] = {listen_addr};
+ ASSERT_EQ(0, resolv_set_nameservers_for_net(TEST_NETID, servers,
+ sizeof(servers) / sizeof(servers[0]),
+ mDefaultSearchDomains, &mDefaultParams_Binder));
+
+ struct hostent* hp = nullptr;
+ NetworkDnsEventReported event;
+ int rv = android_gethostbynamefornetcontext(host_name, AF_INET, &mNetcontext, &hp, &event);
+ EXPECT_EQ(NETD_RESOLV_TIMEOUT, rv);
+}
+
+// Note that local host file function, files_getaddrinfo(), of android_getaddrinfofornetcontext()
+// is not tested because it only returns a boolean (success or failure) without any error number.
+
+// TODO: Add test NULL hostname, or numeric hostname for android_getaddrinfofornetcontext.
+// TODO: Add test invalid parameters for android_gethostbynamefornetcontext.
+// TODO: Add test for android_gethostbyaddrfornetcontext.
+
+} // end of namespace net
+} // end of namespace android
diff --git a/resolv/res_cache.cpp b/resolv/res_cache.cpp
new file mode 100644
index 0000000..d6f0cd8
--- /dev/null
+++ b/resolv/res_cache.cpp
@@ -0,0 +1,2113 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#define LOG_TAG "res_cache"
+
+#include "resolv_cache.h"
+
+#include <resolv.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <mutex>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+#include <errno.h>
+#include <linux/if.h>
+#include <net/if.h>
+#include <netdb.h>
+
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/thread_annotations.h>
+#include <android/multinetwork.h> // ResNsendFlags
+
+#include <server_configurable_flags/get_flags.h>
+
+#include "res_state_ext.h"
+#include "resolv_private.h"
+
+// NOTE: verbose logging MUST NOT be left enabled in production binaries.
+// It floods logs at high rate, and can leak privacy-sensitive information.
+constexpr bool kDumpData = false;
+
+/* This code implements a small and *simple* DNS resolver cache.
+ *
+ * It is only used to cache DNS answers for a time defined by the smallest TTL
+ * among the answer records in order to reduce DNS traffic. It is not supposed
+ * to be a full DNS cache, since we plan to implement that in the future in a
+ * dedicated process running on the system.
+ *
+ * Note that its design is kept simple very intentionally, i.e.:
+ *
+ * - it takes raw DNS query packet data as input, and returns raw DNS
+ * answer packet data as output
+ *
+ * (this means that two similar queries that encode the DNS name
+ * differently will be treated distinctly).
+ *
+ * the smallest TTL value among the answer records are used as the time
+ * to keep an answer in the cache.
+ *
+ * this is bad, but we absolutely want to avoid parsing the answer packets
+ * (and should be solved by the later full DNS cache process).
+ *
+ * - the implementation is just a (query-data) => (answer-data) hash table
+ * with a trivial least-recently-used expiration policy.
+ *
+ * Doing this keeps the code simple and avoids to deal with a lot of things
+ * that a full DNS cache is expected to do.
+ *
+ * The API is also very simple:
+ *
+ * - the client calls _resolv_cache_get() to obtain a handle to the cache.
+ * this will initialize the cache on first usage. the result can be NULL
+ * if the cache is disabled.
+ *
+ * - the client calls _resolv_cache_lookup() before performing a query
+ *
+ * if the function returns RESOLV_CACHE_FOUND, a copy of the answer data
+ * has been copied into the client-provided answer buffer.
+ *
+ * if the function returns RESOLV_CACHE_NOTFOUND, the client should perform
+ * a request normally, *then* call _resolv_cache_add() to add the received
+ * answer to the cache.
+ *
+ * if the function returns RESOLV_CACHE_UNSUPPORTED, the client should
+ * perform a request normally, and *not* call _resolv_cache_add()
+ *
+ * note that RESOLV_CACHE_UNSUPPORTED is also returned if the answer buffer
+ * is too short to accomodate the cached result.
+ */
+
+/* default number of entries kept in the cache. This value has been
+ * determined by browsing through various sites and counting the number
+ * of corresponding requests. Keep in mind that our framework is currently
+ * performing two requests per name lookup (one for IPv4, the other for IPv6)
+ *
+ * www.google.com 4
+ * www.ysearch.com 6
+ * www.amazon.com 8
+ * www.nytimes.com 22
+ * www.espn.com 28
+ * www.msn.com 28
+ * www.lemonde.fr 35
+ *
+ * (determined in 2009-2-17 from Paris, France, results may vary depending
+ * on location)
+ *
+ * most high-level websites use lots of media/ad servers with different names
+ * but these are generally reused when browsing through the site.
+ *
+ * As such, a value of 64 should be relatively comfortable at the moment.
+ *
+ * ******************************************
+ * * NOTE - this has changed.
+ * * 1) we've added IPv6 support so each dns query results in 2 responses
+ * * 2) we've made this a system-wide cache, so the cost is less (it's not
+ * * duplicated in each process) and the need is greater (more processes
+ * * making different requests).
+ * * Upping by 2x for IPv6
+ * * Upping by another 5x for the centralized nature
+ * *****************************************
+ */
+#define CONFIG_MAX_ENTRIES (64 * 2 * 5)
+
+/** BOUNDED BUFFER FORMATTING **/
+
+/* technical note:
+ *
+ * the following debugging routines are used to append data to a bounded
+ * buffer they take two parameters that are:
+ *
+ * - p : a pointer to the current cursor position in the buffer
+ * this value is initially set to the buffer's address.
+ *
+ * - end : the address of the buffer's limit, i.e. of the first byte
+ * after the buffer. this address should never be touched.
+ *
+ * IMPORTANT: it is assumed that end > buffer_address, i.e.
+ * that the buffer is at least one byte.
+ *
+ * the bprint_x() functions return the new value of 'p' after the data
+ * has been appended, and also ensure the following:
+ *
+ * - the returned value will never be strictly greater than 'end'
+ *
+ * - a return value equal to 'end' means that truncation occurred
+ * (in which case, end[-1] will be set to 0)
+ *
+ * - after returning from a bprint_x() function, the content of the buffer
+ * is always 0-terminated, even in the event of truncation.
+ *
+ * these conventions allow you to call bprint_x() functions multiple times and
+ * only check for truncation at the end of the sequence, as in:
+ *
+ * char buff[1000], *p = buff, *end = p + sizeof(buff);
+ *
+ * p = bprint_c(p, end, '"');
+ * p = bprint_s(p, end, my_string);
+ * p = bprint_c(p, end, '"');
+ *
+ * if (p >= end) {
+ * // buffer was too small
+ * }
+ *
+ * printf( "%s", buff );
+ */
+
+/* Defaults used for initializing res_params */
+
+// If successes * 100 / total_samples is less than this value, the server is considered failing
+#define SUCCESS_THRESHOLD 75
+// Sample validity in seconds. Set to -1 to disable skipping failing servers.
+#define NSSAMPLE_VALIDITY 1800
+
+/* add a char to a bounded buffer */
+static char* bprint_c(char* p, char* end, int c) {
+ if (p < end) {
+ if (p + 1 == end)
+ *p++ = 0;
+ else {
+ *p++ = (char) c;
+ *p = 0;
+ }
+ }
+ return p;
+}
+
+/* add a sequence of bytes to a bounded buffer */
+static char* bprint_b(char* p, char* end, const char* buf, int len) {
+ int avail = end - p;
+
+ if (avail <= 0 || len <= 0) return p;
+
+ if (avail > len) avail = len;
+
+ memcpy(p, buf, avail);
+ p += avail;
+
+ if (p < end)
+ p[0] = 0;
+ else
+ end[-1] = 0;
+
+ return p;
+}
+
+/* add a string to a bounded buffer */
+static char* bprint_s(char* p, char* end, const char* str) {
+ return bprint_b(p, end, str, strlen(str));
+}
+
+/* add a formatted string to a bounded buffer */
+static char* bprint(char* p, char* end, const char* format, ...) {
+ int avail, n;
+ va_list args;
+
+ avail = end - p;
+
+ if (avail <= 0) return p;
+
+ va_start(args, format);
+ n = vsnprintf(p, avail, format, args);
+ va_end(args);
+
+ /* certain C libraries return -1 in case of truncation */
+ if (n < 0 || n > avail) n = avail;
+
+ p += n;
+ /* certain C libraries do not zero-terminate in case of truncation */
+ if (p == end) p[-1] = 0;
+
+ return p;
+}
+
+/* add a hex value to a bounded buffer, up to 8 digits */
+static char* bprint_hex(char* p, char* end, unsigned value, int numDigits) {
+ char text[sizeof(unsigned) * 2];
+ int nn = 0;
+
+ while (numDigits-- > 0) {
+ text[nn++] = "0123456789abcdef"[(value >> (numDigits * 4)) & 15];
+ }
+ return bprint_b(p, end, text, nn);
+}
+
+/* add the hexadecimal dump of some memory area to a bounded buffer */
+static char* bprint_hexdump(char* p, char* end, const uint8_t* data, int datalen) {
+ int lineSize = 16;
+
+ while (datalen > 0) {
+ int avail = datalen;
+ int nn;
+
+ if (avail > lineSize) avail = lineSize;
+
+ for (nn = 0; nn < avail; nn++) {
+ if (nn > 0) p = bprint_c(p, end, ' ');
+ p = bprint_hex(p, end, data[nn], 2);
+ }
+ for (; nn < lineSize; nn++) {
+ p = bprint_s(p, end, " ");
+ }
+ p = bprint_s(p, end, " ");
+
+ for (nn = 0; nn < avail; nn++) {
+ int c = data[nn];
+
+ if (c < 32 || c > 127) c = '.';
+
+ p = bprint_c(p, end, c);
+ }
+ p = bprint_c(p, end, '\n');
+
+ data += avail;
+ datalen -= avail;
+ }
+ return p;
+}
+
+/* dump the content of a query of packet to the log */
+static void dump_bytes(const uint8_t* base, int len) {
+ if (!kDumpData) return;
+
+ char buff[1024];
+ char *p = buff, *end = p + sizeof(buff);
+
+ p = bprint_hexdump(p, end, base, len);
+ LOG(INFO) << __func__ << ": " << buff;
+}
+
+static time_t _time_now(void) {
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+ return tv.tv_sec;
+}
+
+/* reminder: the general format of a DNS packet is the following:
+ *
+ * HEADER (12 bytes)
+ * QUESTION (variable)
+ * ANSWER (variable)
+ * AUTHORITY (variable)
+ * ADDITIONNAL (variable)
+ *
+ * the HEADER is made of:
+ *
+ * ID : 16 : 16-bit unique query identification field
+ *
+ * QR : 1 : set to 0 for queries, and 1 for responses
+ * Opcode : 4 : set to 0 for queries
+ * AA : 1 : set to 0 for queries
+ * TC : 1 : truncation flag, will be set to 0 in queries
+ * RD : 1 : recursion desired
+ *
+ * RA : 1 : recursion available (0 in queries)
+ * Z : 3 : three reserved zero bits
+ * RCODE : 4 : response code (always 0=NOERROR in queries)
+ *
+ * QDCount: 16 : question count
+ * ANCount: 16 : Answer count (0 in queries)
+ * NSCount: 16: Authority Record count (0 in queries)
+ * ARCount: 16: Additionnal Record count (0 in queries)
+ *
+ * the QUESTION is made of QDCount Question Record (QRs)
+ * the ANSWER is made of ANCount RRs
+ * the AUTHORITY is made of NSCount RRs
+ * the ADDITIONNAL is made of ARCount RRs
+ *
+ * Each Question Record (QR) is made of:
+ *
+ * QNAME : variable : Query DNS NAME
+ * TYPE : 16 : type of query (A=1, PTR=12, MX=15, AAAA=28, ALL=255)
+ * CLASS : 16 : class of query (IN=1)
+ *
+ * Each Resource Record (RR) is made of:
+ *
+ * NAME : variable : DNS NAME
+ * TYPE : 16 : type of query (A=1, PTR=12, MX=15, AAAA=28, ALL=255)
+ * CLASS : 16 : class of query (IN=1)
+ * TTL : 32 : seconds to cache this RR (0=none)
+ * RDLENGTH: 16 : size of RDDATA in bytes
+ * RDDATA : variable : RR data (depends on TYPE)
+ *
+ * Each QNAME contains a domain name encoded as a sequence of 'labels'
+ * terminated by a zero. Each label has the following format:
+ *
+ * LEN : 8 : lenght of label (MUST be < 64)
+ * NAME : 8*LEN : label length (must exclude dots)
+ *
+ * A value of 0 in the encoding is interpreted as the 'root' domain and
+ * terminates the encoding. So 'www.android.com' will be encoded as:
+ *
+ * <3>www<7>android<3>com<0>
+ *
+ * Where <n> represents the byte with value 'n'
+ *
+ * Each NAME reflects the QNAME of the question, but has a slightly more
+ * complex encoding in order to provide message compression. This is achieved
+ * by using a 2-byte pointer, with format:
+ *
+ * TYPE : 2 : 0b11 to indicate a pointer, 0b01 and 0b10 are reserved
+ * OFFSET : 14 : offset to another part of the DNS packet
+ *
+ * The offset is relative to the start of the DNS packet and must point
+ * A pointer terminates the encoding.
+ *
+ * The NAME can be encoded in one of the following formats:
+ *
+ * - a sequence of simple labels terminated by 0 (like QNAMEs)
+ * - a single pointer
+ * - a sequence of simple labels terminated by a pointer
+ *
+ * A pointer shall always point to either a pointer of a sequence of
+ * labels (which can themselves be terminated by either a 0 or a pointer)
+ *
+ * The expanded length of a given domain name should not exceed 255 bytes.
+ *
+ * NOTE: we don't parse the answer packets, so don't need to deal with NAME
+ * records, only QNAMEs.
+ */
+
+#define DNS_HEADER_SIZE 12
+
+#define DNS_TYPE_A "\00\01" /* big-endian decimal 1 */
+#define DNS_TYPE_PTR "\00\014" /* big-endian decimal 12 */
+#define DNS_TYPE_MX "\00\017" /* big-endian decimal 15 */
+#define DNS_TYPE_AAAA "\00\034" /* big-endian decimal 28 */
+#define DNS_TYPE_ALL "\00\0377" /* big-endian decimal 255 */
+
+#define DNS_CLASS_IN "\00\01" /* big-endian decimal 1 */
+
+typedef struct {
+ const uint8_t* base;
+ const uint8_t* end;
+ const uint8_t* cursor;
+} DnsPacket;
+
+static void _dnsPacket_init(DnsPacket* packet, const uint8_t* buff, int bufflen) {
+ packet->base = buff;
+ packet->end = buff + bufflen;
+ packet->cursor = buff;
+}
+
+static void _dnsPacket_rewind(DnsPacket* packet) {
+ packet->cursor = packet->base;
+}
+
+static void _dnsPacket_skip(DnsPacket* packet, int count) {
+ const uint8_t* p = packet->cursor + count;
+
+ if (p > packet->end) p = packet->end;
+
+ packet->cursor = p;
+}
+
+static int _dnsPacket_readInt16(DnsPacket* packet) {
+ const uint8_t* p = packet->cursor;
+
+ if (p + 2 > packet->end) return -1;
+
+ packet->cursor = p + 2;
+ return (p[0] << 8) | p[1];
+}
+
+/** QUERY CHECKING **/
+
+/* check bytes in a dns packet. returns 1 on success, 0 on failure.
+ * the cursor is only advanced in the case of success
+ */
+static int _dnsPacket_checkBytes(DnsPacket* packet, int numBytes, const void* bytes) {
+ const uint8_t* p = packet->cursor;
+
+ if (p + numBytes > packet->end) return 0;
+
+ if (memcmp(p, bytes, numBytes) != 0) return 0;
+
+ packet->cursor = p + numBytes;
+ return 1;
+}
+
+/* parse and skip a given QNAME stored in a query packet,
+ * from the current cursor position. returns 1 on success,
+ * or 0 for malformed data.
+ */
+static int _dnsPacket_checkQName(DnsPacket* packet) {
+ const uint8_t* p = packet->cursor;
+ const uint8_t* end = packet->end;
+
+ for (;;) {
+ int c;
+
+ if (p >= end) break;
+
+ c = *p++;
+
+ if (c == 0) {
+ packet->cursor = p;
+ return 1;
+ }
+
+ /* we don't expect label compression in QNAMEs */
+ if (c >= 64) break;
+
+ p += c;
+ /* we rely on the bound check at the start
+ * of the loop here */
+ }
+ /* malformed data */
+ LOG(INFO) << __func__ << ": malformed QNAME";
+ return 0;
+}
+
+/* parse and skip a given QR stored in a packet.
+ * returns 1 on success, and 0 on failure
+ */
+static int _dnsPacket_checkQR(DnsPacket* packet) {
+ if (!_dnsPacket_checkQName(packet)) return 0;
+
+ /* TYPE must be one of the things we support */
+ if (!_dnsPacket_checkBytes(packet, 2, DNS_TYPE_A) &&
+ !_dnsPacket_checkBytes(packet, 2, DNS_TYPE_PTR) &&
+ !_dnsPacket_checkBytes(packet, 2, DNS_TYPE_MX) &&
+ !_dnsPacket_checkBytes(packet, 2, DNS_TYPE_AAAA) &&
+ !_dnsPacket_checkBytes(packet, 2, DNS_TYPE_ALL)) {
+ LOG(INFO) << __func__ << ": unsupported TYPE";
+ return 0;
+ }
+ /* CLASS must be IN */
+ if (!_dnsPacket_checkBytes(packet, 2, DNS_CLASS_IN)) {
+ LOG(INFO) << __func__ << ": unsupported CLASS";
+ return 0;
+ }
+
+ return 1;
+}
+
+/* check the header of a DNS Query packet, return 1 if it is one
+ * type of query we can cache, or 0 otherwise
+ */
+static int _dnsPacket_checkQuery(DnsPacket* packet) {
+ const uint8_t* p = packet->base;
+ int qdCount, anCount, dnCount, arCount;
+
+ if (p + DNS_HEADER_SIZE > packet->end) {
+ LOG(INFO) << __func__ << ": query packet too small";
+ return 0;
+ }
+
+ /* QR must be set to 0, opcode must be 0 and AA must be 0 */
+ /* RA, Z, and RCODE must be 0 */
+ if ((p[2] & 0xFC) != 0 || (p[3] & 0xCF) != 0) {
+ LOG(INFO) << __func__ << ": query packet flags unsupported";
+ return 0;
+ }
+
+ /* Note that we ignore the TC, RD, CD, and AD bits here for the
+ * following reasons:
+ *
+ * - there is no point for a query packet sent to a server
+ * to have the TC bit set, but the implementation might
+ * set the bit in the query buffer for its own needs
+ * between a _resolv_cache_lookup and a
+ * _resolv_cache_add. We should not freak out if this
+ * is the case.
+ *
+ * - we consider that the result from a query might depend on
+ * the RD, AD, and CD bits, so these bits
+ * should be used to differentiate cached result.
+ *
+ * this implies that these bits are checked when hashing or
+ * comparing query packets, but not TC
+ */
+
+ /* ANCOUNT, DNCOUNT and ARCOUNT must be 0 */
+ qdCount = (p[4] << 8) | p[5];
+ anCount = (p[6] << 8) | p[7];
+ dnCount = (p[8] << 8) | p[9];
+ arCount = (p[10] << 8) | p[11];
+
+ if (anCount != 0 || dnCount != 0 || arCount > 1) {
+ LOG(INFO) << __func__ << ": query packet contains non-query records";
+ return 0;
+ }
+
+ if (qdCount == 0) {
+ LOG(INFO) << __func__ << ": query packet doesn't contain query record";
+ return 0;
+ }
+
+ /* Check QDCOUNT QRs */
+ packet->cursor = p + DNS_HEADER_SIZE;
+
+ for (; qdCount > 0; qdCount--)
+ if (!_dnsPacket_checkQR(packet)) return 0;
+
+ return 1;
+}
+
+/** QUERY DEBUGGING **/
+static char* dnsPacket_bprintQName(DnsPacket* packet, char* bp, char* bend) {
+ const uint8_t* p = packet->cursor;
+ const uint8_t* end = packet->end;
+ int first = 1;
+
+ for (;;) {
+ int c;
+
+ if (p >= end) break;
+
+ c = *p++;
+
+ if (c == 0) {
+ packet->cursor = p;
+ return bp;
+ }
+
+ /* we don't expect label compression in QNAMEs */
+ if (c >= 64) break;
+
+ if (first)
+ first = 0;
+ else
+ bp = bprint_c(bp, bend, '.');
+
+ bp = bprint_b(bp, bend, (const char*) p, c);
+
+ p += c;
+ /* we rely on the bound check at the start
+ * of the loop here */
+ }
+ /* malformed data */
+ bp = bprint_s(bp, bend, "<MALFORMED>");
+ return bp;
+}
+
+static char* dnsPacket_bprintQR(DnsPacket* packet, char* p, char* end) {
+#define QQ(x) \
+ { DNS_TYPE_##x, #x }
+ static const struct {
+ const char* typeBytes;
+ const char* typeString;
+ } qTypes[] = {QQ(A), QQ(PTR), QQ(MX), QQ(AAAA), QQ(ALL), {NULL, NULL}};
+ int nn;
+ const char* typeString = NULL;
+
+ /* dump QNAME */
+ p = dnsPacket_bprintQName(packet, p, end);
+
+ /* dump TYPE */
+ p = bprint_s(p, end, " (");
+
+ for (nn = 0; qTypes[nn].typeBytes != NULL; nn++) {
+ if (_dnsPacket_checkBytes(packet, 2, qTypes[nn].typeBytes)) {
+ typeString = qTypes[nn].typeString;
+ break;
+ }
+ }
+
+ if (typeString != NULL)
+ p = bprint_s(p, end, typeString);
+ else {
+ int typeCode = _dnsPacket_readInt16(packet);
+ p = bprint(p, end, "UNKNOWN-%d", typeCode);
+ }
+
+ p = bprint_c(p, end, ')');
+
+ /* skip CLASS */
+ _dnsPacket_skip(packet, 2);
+ return p;
+}
+
+/* this function assumes the packet has already been checked */
+static char* dnsPacket_bprintQuery(DnsPacket* packet, char* p, char* end) {
+ int qdCount;
+
+ if (packet->base[2] & 0x1) {
+ p = bprint_s(p, end, "RECURSIVE ");
+ }
+
+ _dnsPacket_skip(packet, 4);
+ qdCount = _dnsPacket_readInt16(packet);
+ _dnsPacket_skip(packet, 6);
+
+ for (; qdCount > 0; qdCount--) {
+ p = dnsPacket_bprintQR(packet, p, end);
+ }
+ return p;
+}
+
+/** QUERY HASHING SUPPORT
+ **
+ ** THE FOLLOWING CODE ASSUMES THAT THE INPUT PACKET HAS ALREADY
+ ** BEEN SUCCESFULLY CHECKED.
+ **/
+
+/* use 32-bit FNV hash function */
+#define FNV_MULT 16777619U
+#define FNV_BASIS 2166136261U
+
+static unsigned _dnsPacket_hashBytes(DnsPacket* packet, int numBytes, unsigned hash) {
+ const uint8_t* p = packet->cursor;
+ const uint8_t* end = packet->end;
+
+ while (numBytes > 0 && p < end) {
+ hash = hash * FNV_MULT ^ *p++;
+ }
+ packet->cursor = p;
+ return hash;
+}
+
+static unsigned _dnsPacket_hashQName(DnsPacket* packet, unsigned hash) {
+ const uint8_t* p = packet->cursor;
+ const uint8_t* end = packet->end;
+
+ for (;;) {
+ int c;
+
+ if (p >= end) { /* should not happen */
+ LOG(INFO) << __func__ << ": INTERNAL_ERROR: read-overflow";
+ break;
+ }
+
+ c = *p++;
+
+ if (c == 0) break;
+
+ if (c >= 64) {
+ LOG(INFO) << __func__ << ": INTERNAL_ERROR: malformed domain";
+ break;
+ }
+ if (p + c >= end) {
+ LOG(INFO) << __func__ << ": INTERNAL_ERROR: simple label read-overflow";
+ break;
+ }
+ while (c > 0) {
+ hash = hash * FNV_MULT ^ *p++;
+ c -= 1;
+ }
+ }
+ packet->cursor = p;
+ return hash;
+}
+
+static unsigned _dnsPacket_hashQR(DnsPacket* packet, unsigned hash) {
+ hash = _dnsPacket_hashQName(packet, hash);
+ hash = _dnsPacket_hashBytes(packet, 4, hash); /* TYPE and CLASS */
+ return hash;
+}
+
+static unsigned _dnsPacket_hashRR(DnsPacket* packet, unsigned hash) {
+ int rdlength;
+ hash = _dnsPacket_hashQR(packet, hash);
+ hash = _dnsPacket_hashBytes(packet, 4, hash); /* TTL */
+ rdlength = _dnsPacket_readInt16(packet);
+ hash = _dnsPacket_hashBytes(packet, rdlength, hash); /* RDATA */
+ return hash;
+}
+
+static unsigned _dnsPacket_hashQuery(DnsPacket* packet) {
+ unsigned hash = FNV_BASIS;
+ int count, arcount;
+ _dnsPacket_rewind(packet);
+
+ /* ignore the ID */
+ _dnsPacket_skip(packet, 2);
+
+ /* we ignore the TC bit for reasons explained in
+ * _dnsPacket_checkQuery().
+ *
+ * however we hash the RD bit to differentiate
+ * between answers for recursive and non-recursive
+ * queries.
+ */
+ hash = hash * FNV_MULT ^ (packet->base[2] & 1);
+
+ /* mark the first header byte as processed */
+ _dnsPacket_skip(packet, 1);
+
+ /* process the second header byte */
+ hash = _dnsPacket_hashBytes(packet, 1, hash);
+
+ /* read QDCOUNT */
+ count = _dnsPacket_readInt16(packet);
+
+ /* assume: ANcount and NScount are 0 */
+ _dnsPacket_skip(packet, 4);
+
+ /* read ARCOUNT */
+ arcount = _dnsPacket_readInt16(packet);
+
+ /* hash QDCOUNT QRs */
+ for (; count > 0; count--) hash = _dnsPacket_hashQR(packet, hash);
+
+ /* hash ARCOUNT RRs */
+ for (; arcount > 0; arcount--) hash = _dnsPacket_hashRR(packet, hash);
+
+ return hash;
+}
+
+/** QUERY COMPARISON
+ **
+ ** THE FOLLOWING CODE ASSUMES THAT THE INPUT PACKETS HAVE ALREADY
+ ** BEEN SUCCESSFULLY CHECKED.
+ **/
+
+static int _dnsPacket_isEqualDomainName(DnsPacket* pack1, DnsPacket* pack2) {
+ const uint8_t* p1 = pack1->cursor;
+ const uint8_t* end1 = pack1->end;
+ const uint8_t* p2 = pack2->cursor;
+ const uint8_t* end2 = pack2->end;
+
+ for (;;) {
+ int c1, c2;
+
+ if (p1 >= end1 || p2 >= end2) {
+ LOG(INFO) << __func__ << ": INTERNAL_ERROR: read-overflow";
+ break;
+ }
+ c1 = *p1++;
+ c2 = *p2++;
+ if (c1 != c2) break;
+
+ if (c1 == 0) {
+ pack1->cursor = p1;
+ pack2->cursor = p2;
+ return 1;
+ }
+ if (c1 >= 64) {
+ LOG(INFO) << __func__ << ": INTERNAL_ERROR: malformed domain";
+ break;
+ }
+ if ((p1 + c1 > end1) || (p2 + c1 > end2)) {
+ LOG(INFO) << __func__ << ": INTERNAL_ERROR: simple label read-overflow";
+ break;
+ }
+ if (memcmp(p1, p2, c1) != 0) break;
+ p1 += c1;
+ p2 += c1;
+ /* we rely on the bound checks at the start of the loop */
+ }
+ /* not the same, or one is malformed */
+ LOG(INFO) << __func__ << ": different DN";
+ return 0;
+}
+
+static int _dnsPacket_isEqualBytes(DnsPacket* pack1, DnsPacket* pack2, int numBytes) {
+ const uint8_t* p1 = pack1->cursor;
+ const uint8_t* p2 = pack2->cursor;
+
+ if (p1 + numBytes > pack1->end || p2 + numBytes > pack2->end) return 0;
+
+ if (memcmp(p1, p2, numBytes) != 0) return 0;
+
+ pack1->cursor += numBytes;
+ pack2->cursor += numBytes;
+ return 1;
+}
+
+static int _dnsPacket_isEqualQR(DnsPacket* pack1, DnsPacket* pack2) {
+ /* compare domain name encoding + TYPE + CLASS */
+ if (!_dnsPacket_isEqualDomainName(pack1, pack2) ||
+ !_dnsPacket_isEqualBytes(pack1, pack2, 2 + 2))
+ return 0;
+
+ return 1;
+}
+
+static int _dnsPacket_isEqualRR(DnsPacket* pack1, DnsPacket* pack2) {
+ int rdlength1, rdlength2;
+ /* compare query + TTL */
+ if (!_dnsPacket_isEqualQR(pack1, pack2) || !_dnsPacket_isEqualBytes(pack1, pack2, 4)) return 0;
+
+ /* compare RDATA */
+ rdlength1 = _dnsPacket_readInt16(pack1);
+ rdlength2 = _dnsPacket_readInt16(pack2);
+ if (rdlength1 != rdlength2 || !_dnsPacket_isEqualBytes(pack1, pack2, rdlength1)) return 0;
+
+ return 1;
+}
+
+static int _dnsPacket_isEqualQuery(DnsPacket* pack1, DnsPacket* pack2) {
+ int count1, count2, arcount1, arcount2;
+
+ /* compare the headers, ignore most fields */
+ _dnsPacket_rewind(pack1);
+ _dnsPacket_rewind(pack2);
+
+ /* compare RD, ignore TC, see comment in _dnsPacket_checkQuery */
+ if ((pack1->base[2] & 1) != (pack2->base[2] & 1)) {
+ LOG(INFO) << __func__ << ": different RD";
+ return 0;
+ }
+
+ if (pack1->base[3] != pack2->base[3]) {
+ LOG(INFO) << __func__ << ": different CD or AD";
+ return 0;
+ }
+
+ /* mark ID and header bytes as compared */
+ _dnsPacket_skip(pack1, 4);
+ _dnsPacket_skip(pack2, 4);
+
+ /* compare QDCOUNT */
+ count1 = _dnsPacket_readInt16(pack1);
+ count2 = _dnsPacket_readInt16(pack2);
+ if (count1 != count2 || count1 < 0) {
+ LOG(INFO) << __func__ << ": different QDCOUNT";
+ return 0;
+ }
+
+ /* assume: ANcount and NScount are 0 */
+ _dnsPacket_skip(pack1, 4);
+ _dnsPacket_skip(pack2, 4);
+
+ /* compare ARCOUNT */
+ arcount1 = _dnsPacket_readInt16(pack1);
+ arcount2 = _dnsPacket_readInt16(pack2);
+ if (arcount1 != arcount2 || arcount1 < 0) {
+ LOG(INFO) << __func__ << ": different ARCOUNT";
+ return 0;
+ }
+
+ /* compare the QDCOUNT QRs */
+ for (; count1 > 0; count1--) {
+ if (!_dnsPacket_isEqualQR(pack1, pack2)) {
+ LOG(INFO) << __func__ << ": different QR";
+ return 0;
+ }
+ }
+
+ /* compare the ARCOUNT RRs */
+ for (; arcount1 > 0; arcount1--) {
+ if (!_dnsPacket_isEqualRR(pack1, pack2)) {
+ LOG(INFO) << __func__ << ": different additional RR";
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/* cache entry. for simplicity, 'hash' and 'hlink' are inlined in this
+ * structure though they are conceptually part of the hash table.
+ *
+ * similarly, mru_next and mru_prev are part of the global MRU list
+ */
+typedef struct Entry {
+ unsigned int hash; /* hash value */
+ struct Entry* hlink; /* next in collision chain */
+ struct Entry* mru_prev;
+ struct Entry* mru_next;
+
+ const uint8_t* query;
+ int querylen;
+ const uint8_t* answer;
+ int answerlen;
+ time_t expires; /* time_t when the entry isn't valid any more */
+ int id; /* for debugging purpose */
+} Entry;
+
+/*
+ * Find the TTL for a negative DNS result. This is defined as the minimum
+ * of the SOA records TTL and the MINIMUM-TTL field (RFC-2308).
+ *
+ * Return 0 if not found.
+ */
+static u_long answer_getNegativeTTL(ns_msg handle) {
+ int n, nscount;
+ u_long result = 0;
+ ns_rr rr;
+
+ nscount = ns_msg_count(handle, ns_s_ns);
+ for (n = 0; n < nscount; n++) {
+ if ((ns_parserr(&handle, ns_s_ns, n, &rr) == 0) && (ns_rr_type(rr) == ns_t_soa)) {
+ const u_char* rdata = ns_rr_rdata(rr); // find the data
+ const u_char* edata = rdata + ns_rr_rdlen(rr); // add the len to find the end
+ int len;
+ u_long ttl, rec_result = ns_rr_ttl(rr);
+
+ // find the MINIMUM-TTL field from the blob of binary data for this record
+ // skip the server name
+ len = dn_skipname(rdata, edata);
+ if (len == -1) continue; // error skipping
+ rdata += len;
+
+ // skip the admin name
+ len = dn_skipname(rdata, edata);
+ if (len == -1) continue; // error skipping
+ rdata += len;
+
+ if (edata - rdata != 5 * NS_INT32SZ) continue;
+ // skip: serial number + refresh interval + retry interval + expiry
+ rdata += NS_INT32SZ * 4;
+ // finally read the MINIMUM TTL
+ ttl = ntohl(*reinterpret_cast<const uint32_t*>(rdata));
+ if (ttl < rec_result) {
+ rec_result = ttl;
+ }
+ // Now that the record is read successfully, apply the new min TTL
+ if (n == 0 || rec_result < result) {
+ result = rec_result;
+ }
+ }
+ }
+ return result;
+}
+
+/*
+ * Parse the answer records and find the appropriate
+ * smallest TTL among the records. This might be from
+ * the answer records if found or from the SOA record
+ * if it's a negative result.
+ *
+ * The returned TTL is the number of seconds to
+ * keep the answer in the cache.
+ *
+ * In case of parse error zero (0) is returned which
+ * indicates that the answer shall not be cached.
+ */
+static u_long answer_getTTL(const void* answer, int answerlen) {
+ ns_msg handle;
+ int ancount, n;
+ u_long result, ttl;
+ ns_rr rr;
+
+ result = 0;
+ if (ns_initparse((const uint8_t*) answer, answerlen, &handle) >= 0) {
+ // get number of answer records
+ ancount = ns_msg_count(handle, ns_s_an);
+
+ if (ancount == 0) {
+ // a response with no answers? Cache this negative result.
+ result = answer_getNegativeTTL(handle);
+ } else {
+ for (n = 0; n < ancount; n++) {
+ if (ns_parserr(&handle, ns_s_an, n, &rr) == 0) {
+ ttl = ns_rr_ttl(rr);
+ if (n == 0 || ttl < result) {
+ result = ttl;
+ }
+ } else {
+ PLOG(INFO) << __func__ << ": ns_parserr failed ancount no = " << n;
+ }
+ }
+ }
+ } else {
+ PLOG(INFO) << __func__ << ": ns_initparse failed";
+ }
+
+ LOG(INFO) << __func__ << ": TTL = " << result;
+ return result;
+}
+
+static void entry_free(Entry* e) {
+ /* everything is allocated in a single memory block */
+ if (e) {
+ free(e);
+ }
+}
+
+static void entry_mru_remove(Entry* e) {
+ e->mru_prev->mru_next = e->mru_next;
+ e->mru_next->mru_prev = e->mru_prev;
+}
+
+static void entry_mru_add(Entry* e, Entry* list) {
+ Entry* first = list->mru_next;
+
+ e->mru_next = first;
+ e->mru_prev = list;
+
+ list->mru_next = e;
+ first->mru_prev = e;
+}
+
+/* compute the hash of a given entry, this is a hash of most
+ * data in the query (key) */
+static unsigned entry_hash(const Entry* e) {
+ DnsPacket pack[1];
+
+ _dnsPacket_init(pack, e->query, e->querylen);
+ return _dnsPacket_hashQuery(pack);
+}
+
+/* initialize an Entry as a search key, this also checks the input query packet
+ * returns 1 on success, or 0 in case of unsupported/malformed data */
+static int entry_init_key(Entry* e, const void* query, int querylen) {
+ DnsPacket pack[1];
+
+ memset(e, 0, sizeof(*e));
+
+ e->query = (const uint8_t*) query;
+ e->querylen = querylen;
+ e->hash = entry_hash(e);
+
+ _dnsPacket_init(pack, e->query, e->querylen);
+
+ return _dnsPacket_checkQuery(pack);
+}
+
+/* allocate a new entry as a cache node */
+static Entry* entry_alloc(const Entry* init, const void* answer, int answerlen) {
+ Entry* e;
+ int size;
+
+ size = sizeof(*e) + init->querylen + answerlen;
+ e = (Entry*) calloc(size, 1);
+ if (e == NULL) return e;
+
+ e->hash = init->hash;
+ e->query = (const uint8_t*) (e + 1);
+ e->querylen = init->querylen;
+
+ memcpy((char*) e->query, init->query, e->querylen);
+
+ e->answer = e->query + e->querylen;
+ e->answerlen = answerlen;
+
+ memcpy((char*) e->answer, answer, e->answerlen);
+
+ return e;
+}
+
+static int entry_equals(const Entry* e1, const Entry* e2) {
+ DnsPacket pack1[1], pack2[1];
+
+ if (e1->querylen != e2->querylen) {
+ return 0;
+ }
+ _dnsPacket_init(pack1, e1->query, e1->querylen);
+ _dnsPacket_init(pack2, e2->query, e2->querylen);
+
+ return _dnsPacket_isEqualQuery(pack1, pack2);
+}
+
+/* We use a simple hash table with external collision lists
+ * for simplicity, the hash-table fields 'hash' and 'hlink' are
+ * inlined in the Entry structure.
+ */
+
+/* Maximum time for a thread to wait for an pending request */
+constexpr int PENDING_REQUEST_TIMEOUT = 20;
+
+typedef struct resolv_cache {
+ int max_entries;
+ int num_entries;
+ Entry mru_list;
+ int last_id;
+ Entry* entries;
+ struct pending_req_info {
+ unsigned int hash;
+ struct pending_req_info* next;
+ } pending_requests;
+} Cache;
+
+struct resolv_cache_info {
+ unsigned netid;
+ Cache* cache;
+ struct resolv_cache_info* next;
+ int nscount;
+ char* nameservers[MAXNS];
+ struct addrinfo* nsaddrinfo[MAXNS];
+ int revision_id; // # times the nameservers have been replaced
+ res_params params;
+ struct res_stats nsstats[MAXNS];
+ char defdname[MAXDNSRCHPATH];
+ int dnsrch_offset[MAXDNSRCH + 1]; // offsets into defdname
+ int wait_for_pending_req_timeout_count;
+ // Map format: ReturnCode:rate_denom
+ std::unordered_map<int, uint32_t> dns_event_subsampling_map;
+};
+
+// A helper class for the Clang Thread Safety Analysis to deal with
+// std::unique_lock.
+class SCOPED_CAPABILITY ScopedAssumeLocked {
+ public:
+ ScopedAssumeLocked(std::mutex& mutex) ACQUIRE(mutex) {}
+ ~ScopedAssumeLocked() RELEASE() {}
+};
+
+// lock protecting everything in the resolve_cache_info structs (next ptr, etc)
+static std::mutex cache_mutex;
+static std::condition_variable cv;
+
+/* gets cache associated with a network, or NULL if none exists */
+static resolv_cache* find_named_cache_locked(unsigned netid) REQUIRES(cache_mutex);
+static int resolv_create_cache_for_net_locked(unsigned netid) REQUIRES(cache_mutex);
+
+static void cache_flush_pending_requests_locked(struct resolv_cache* cache) {
+ resolv_cache::pending_req_info *ri, *tmp;
+ if (!cache) return;
+
+ ri = cache->pending_requests.next;
+
+ while (ri) {
+ tmp = ri;
+ ri = ri->next;
+ free(tmp);
+ }
+
+ cache->pending_requests.next = NULL;
+ cv.notify_all();
+}
+
+// Return true - if there is a pending request in |cache| matching |key|.
+// Return false - if no pending request is found matching the key. Optionally
+// link a new one if parameter append_if_not_found is true.
+static bool cache_has_pending_request_locked(resolv_cache* cache, const Entry* key,
+ bool append_if_not_found) {
+ if (!cache || !key) return false;
+
+ resolv_cache::pending_req_info* ri = cache->pending_requests.next;
+ resolv_cache::pending_req_info* prev = &cache->pending_requests;
+ while (ri) {
+ if (ri->hash == key->hash) {
+ return true;
+ }
+ prev = ri;
+ ri = ri->next;
+ }
+
+ if (append_if_not_found) {
+ ri = (resolv_cache::pending_req_info*)calloc(1, sizeof(resolv_cache::pending_req_info));
+ if (ri) {
+ ri->hash = key->hash;
+ prev->next = ri;
+ }
+ }
+ return false;
+}
+
+// Notify all threads that the cache entry |key| has become available
+static void _cache_notify_waiting_tid_locked(struct resolv_cache* cache, const Entry* key) {
+ if (!cache || !key) return;
+
+ resolv_cache::pending_req_info* ri = cache->pending_requests.next;
+ resolv_cache::pending_req_info* prev = &cache->pending_requests;
+ while (ri) {
+ if (ri->hash == key->hash) {
+ // remove item from list and destroy
+ prev->next = ri->next;
+ free(ri);
+ cv.notify_all();
+ return;
+ }
+ prev = ri;
+ ri = ri->next;
+ }
+}
+
+void _resolv_cache_query_failed(unsigned netid, const void* query, int querylen, uint32_t flags) {
+ // We should not notify with these flags.
+ if (flags & (ANDROID_RESOLV_NO_CACHE_STORE | ANDROID_RESOLV_NO_CACHE_LOOKUP)) {
+ return;
+ }
+ Entry key[1];
+ Cache* cache;
+
+ if (!entry_init_key(key, query, querylen)) return;
+
+ std::lock_guard guard(cache_mutex);
+
+ cache = find_named_cache_locked(netid);
+
+ if (cache) {
+ _cache_notify_waiting_tid_locked(cache, key);
+ }
+}
+
+static void cache_flush_locked(Cache* cache) {
+ int nn;
+
+ for (nn = 0; nn < cache->max_entries; nn++) {
+ Entry** pnode = (Entry**) &cache->entries[nn];
+
+ while (*pnode != NULL) {
+ Entry* node = *pnode;
+ *pnode = node->hlink;
+ entry_free(node);
+ }
+ }
+
+ // flush pending request
+ cache_flush_pending_requests_locked(cache);
+
+ cache->mru_list.mru_next = cache->mru_list.mru_prev = &cache->mru_list;
+ cache->num_entries = 0;
+ cache->last_id = 0;
+
+ LOG(INFO) << __func__ << ": *** DNS CACHE FLUSHED ***";
+}
+
+static resolv_cache* resolv_cache_create() {
+ struct resolv_cache* cache;
+
+ cache = (struct resolv_cache*) calloc(sizeof(*cache), 1);
+ if (cache) {
+ cache->max_entries = CONFIG_MAX_ENTRIES;
+ cache->entries = (Entry*) calloc(sizeof(*cache->entries), cache->max_entries);
+ if (cache->entries) {
+ cache->mru_list.mru_prev = cache->mru_list.mru_next = &cache->mru_list;
+ LOG(INFO) << __func__ << ": cache created";
+ } else {
+ free(cache);
+ cache = NULL;
+ }
+ }
+ return cache;
+}
+
+static void dump_query(const uint8_t* query, int querylen) {
+ if (!WOULD_LOG(VERBOSE)) return;
+
+ char temp[256], *p = temp, *end = p + sizeof(temp);
+ DnsPacket pack[1];
+
+ _dnsPacket_init(pack, query, querylen);
+ p = dnsPacket_bprintQuery(pack, p, end);
+ LOG(VERBOSE) << __func__ << ": " << temp;
+}
+
+static void cache_dump_mru(Cache* cache) {
+ char temp[512], *p = temp, *end = p + sizeof(temp);
+ Entry* e;
+
+ p = bprint(temp, end, "MRU LIST (%2d): ", cache->num_entries);
+ for (e = cache->mru_list.mru_next; e != &cache->mru_list; e = e->mru_next)
+ p = bprint(p, end, " %d", e->id);
+
+ LOG(INFO) << __func__ << ": " << temp;
+}
+
+/* This function tries to find a key within the hash table
+ * In case of success, it will return a *pointer* to the hashed key.
+ * In case of failure, it will return a *pointer* to NULL
+ *
+ * So, the caller must check '*result' to check for success/failure.
+ *
+ * The main idea is that the result can later be used directly in
+ * calls to _resolv_cache_add or _resolv_cache_remove as the 'lookup'
+ * parameter. This makes the code simpler and avoids re-searching
+ * for the key position in the htable.
+ *
+ * The result of a lookup_p is only valid until you alter the hash
+ * table.
+ */
+static Entry** _cache_lookup_p(Cache* cache, Entry* key) {
+ int index = key->hash % cache->max_entries;
+ Entry** pnode = (Entry**) &cache->entries[index];
+
+ while (*pnode != NULL) {
+ Entry* node = *pnode;
+
+ if (node == NULL) break;
+
+ if (node->hash == key->hash && entry_equals(node, key)) break;
+
+ pnode = &node->hlink;
+ }
+ return pnode;
+}
+
+/* Add a new entry to the hash table. 'lookup' must be the
+ * result of an immediate previous failed _lookup_p() call
+ * (i.e. with *lookup == NULL), and 'e' is the pointer to the
+ * newly created entry
+ */
+static void _cache_add_p(Cache* cache, Entry** lookup, Entry* e) {
+ *lookup = e;
+ e->id = ++cache->last_id;
+ entry_mru_add(e, &cache->mru_list);
+ cache->num_entries += 1;
+
+ LOG(INFO) << __func__ << ": entry " << e->id << " added (count=" << cache->num_entries << ")";
+}
+
+/* Remove an existing entry from the hash table,
+ * 'lookup' must be the result of an immediate previous
+ * and succesful _lookup_p() call.
+ */
+static void _cache_remove_p(Cache* cache, Entry** lookup) {
+ Entry* e = *lookup;
+
+ LOG(INFO) << __func__ << ": entry " << e->id << " removed (count=" << cache->num_entries - 1
+ << ")";
+
+ entry_mru_remove(e);
+ *lookup = e->hlink;
+ entry_free(e);
+ cache->num_entries -= 1;
+}
+
+/* Remove the oldest entry from the hash table.
+ */
+static void _cache_remove_oldest(Cache* cache) {
+ Entry* oldest = cache->mru_list.mru_prev;
+ Entry** lookup = _cache_lookup_p(cache, oldest);
+
+ if (*lookup == NULL) { /* should not happen */
+ LOG(INFO) << __func__ << ": OLDEST NOT IN HTABLE ?";
+ return;
+ }
+ LOG(INFO) << __func__ << ": Cache full - removing oldest";
+ dump_query(oldest->query, oldest->querylen);
+ _cache_remove_p(cache, lookup);
+}
+
+/* Remove all expired entries from the hash table.
+ */
+static void _cache_remove_expired(Cache* cache) {
+ Entry* e;
+ time_t now = _time_now();
+
+ for (e = cache->mru_list.mru_next; e != &cache->mru_list;) {
+ // Entry is old, remove
+ if (now >= e->expires) {
+ Entry** lookup = _cache_lookup_p(cache, e);
+ if (*lookup == NULL) { /* should not happen */
+ LOG(INFO) << __func__ << ": ENTRY NOT IN HTABLE ?";
+ return;
+ }
+ e = e->mru_next;
+ _cache_remove_p(cache, lookup);
+ } else {
+ e = e->mru_next;
+ }
+ }
+}
+
+// gets a resolv_cache_info associated with a network, or NULL if not found
+static resolv_cache_info* find_cache_info_locked(unsigned netid) REQUIRES(cache_mutex);
+
+ResolvCacheStatus _resolv_cache_lookup(unsigned netid, const void* query, int querylen,
+ void* answer, int answersize, int* answerlen,
+ uint32_t flags) {
+ // Skip cache lookup, return RESOLV_CACHE_NOTFOUND directly so that it is
+ // possible to cache the answer of this query.
+ // If ANDROID_RESOLV_NO_CACHE_STORE is set, return RESOLV_CACHE_SKIP to skip possible cache
+ // storing.
+ if (flags & ANDROID_RESOLV_NO_CACHE_LOOKUP) {
+ return flags & ANDROID_RESOLV_NO_CACHE_STORE ? RESOLV_CACHE_SKIP : RESOLV_CACHE_NOTFOUND;
+ }
+ Entry key;
+ Entry** lookup;
+ Entry* e;
+ time_t now;
+ Cache* cache;
+
+ LOG(INFO) << __func__ << ": lookup";
+ dump_query((u_char*) query, querylen);
+
+ /* we don't cache malformed queries */
+ if (!entry_init_key(&key, query, querylen)) {
+ LOG(INFO) << __func__ << ": unsupported query";
+ return RESOLV_CACHE_UNSUPPORTED;
+ }
+ /* lookup cache */
+ std::unique_lock lock(cache_mutex);
+ ScopedAssumeLocked assume_lock(cache_mutex);
+ cache = find_named_cache_locked(netid);
+ if (cache == NULL) {
+ return RESOLV_CACHE_UNSUPPORTED;
+ }
+
+ /* see the description of _lookup_p to understand this.
+ * the function always return a non-NULL pointer.
+ */
+ lookup = _cache_lookup_p(cache, &key);
+ e = *lookup;
+
+ if (e == NULL) {
+ LOG(INFO) << __func__ << ": NOT IN CACHE";
+ // If it is no-cache-store mode, we won't wait for possible query.
+ if (flags & ANDROID_RESOLV_NO_CACHE_STORE) {
+ return RESOLV_CACHE_SKIP;
+ }
+
+ if (!cache_has_pending_request_locked(cache, &key, true)) {
+ return RESOLV_CACHE_NOTFOUND;
+
+ } else {
+ LOG(INFO) << __func__ << ": Waiting for previous request";
+ // wait until (1) timeout OR
+ // (2) cv is notified AND no pending request matching the |key|
+ // (cv notifier should delete pending request before sending notification.)
+ bool ret = cv.wait_for(lock, std::chrono::seconds(PENDING_REQUEST_TIMEOUT),
+ [netid, &cache, &key]() REQUIRES(cache_mutex) {
+ // Must update cache as it could have been deleted
+ cache = find_named_cache_locked(netid);
+ return !cache_has_pending_request_locked(cache, &key, false);
+ });
+ if (!cache) {
+ return RESOLV_CACHE_NOTFOUND;
+ }
+ if (ret == false) {
+ resolv_cache_info* info = find_cache_info_locked(netid);
+ if (info != NULL) {
+ info->wait_for_pending_req_timeout_count++;
+ }
+ }
+ lookup = _cache_lookup_p(cache, &key);
+ e = *lookup;
+ if (e == NULL) {
+ return RESOLV_CACHE_NOTFOUND;
+ }
+ }
+ }
+
+ now = _time_now();
+
+ /* remove stale entries here */
+ if (now >= e->expires) {
+ LOG(INFO) << __func__ << ": NOT IN CACHE (STALE ENTRY " << *lookup << "DISCARDED)";
+ dump_query(e->query, e->querylen);
+ _cache_remove_p(cache, lookup);
+ return RESOLV_CACHE_NOTFOUND;
+ }
+
+ *answerlen = e->answerlen;
+ if (e->answerlen > answersize) {
+ /* NOTE: we return UNSUPPORTED if the answer buffer is too short */
+ LOG(INFO) << __func__ << ": ANSWER TOO LONG";
+ return RESOLV_CACHE_UNSUPPORTED;
+ }
+
+ memcpy(answer, e->answer, e->answerlen);
+
+ /* bump up this entry to the top of the MRU list */
+ if (e != cache->mru_list.mru_next) {
+ entry_mru_remove(e);
+ entry_mru_add(e, &cache->mru_list);
+ }
+
+ LOG(INFO) << __func__ << ": FOUND IN CACHE entry=" << e;
+ return RESOLV_CACHE_FOUND;
+}
+
+void _resolv_cache_add(unsigned netid, const void* query, int querylen, const void* answer,
+ int answerlen) {
+ Entry key[1];
+ Entry* e;
+ Entry** lookup;
+ u_long ttl;
+ Cache* cache = NULL;
+
+ /* don't assume that the query has already been cached
+ */
+ if (!entry_init_key(key, query, querylen)) {
+ LOG(INFO) << __func__ << ": passed invalid query?";
+ return;
+ }
+
+ std::lock_guard guard(cache_mutex);
+
+ cache = find_named_cache_locked(netid);
+ if (cache == NULL) {
+ return;
+ }
+
+ LOG(INFO) << __func__ << ": query:";
+ dump_query((u_char*)query, querylen);
+ res_pquery((u_char*)answer, answerlen);
+ if (kDumpData) {
+ LOG(INFO) << __func__ << ": answer:";
+ dump_bytes((u_char*)answer, answerlen);
+ }
+
+ lookup = _cache_lookup_p(cache, key);
+ e = *lookup;
+
+ // Should only happen on ANDROID_RESOLV_NO_CACHE_LOOKUP
+ if (e != NULL) {
+ LOG(INFO) << __func__ << ": ALREADY IN CACHE (" << e << ") ? IGNORING ADD";
+ _cache_notify_waiting_tid_locked(cache, key);
+ return;
+ }
+
+ if (cache->num_entries >= cache->max_entries) {
+ _cache_remove_expired(cache);
+ if (cache->num_entries >= cache->max_entries) {
+ _cache_remove_oldest(cache);
+ }
+ // TODO: It looks useless, remove below code after having test to prove it.
+ lookup = _cache_lookup_p(cache, key);
+ e = *lookup;
+ if (e != NULL) {
+ LOG(INFO) << __func__ << ": ALREADY IN CACHE (" << e << ") ? IGNORING ADD";
+ _cache_notify_waiting_tid_locked(cache, key);
+ return;
+ }
+ }
+
+ ttl = answer_getTTL(answer, answerlen);
+ if (ttl > 0) {
+ e = entry_alloc(key, answer, answerlen);
+ if (e != NULL) {
+ e->expires = ttl + _time_now();
+ _cache_add_p(cache, lookup, e);
+ }
+ }
+
+ cache_dump_mru(cache);
+ _cache_notify_waiting_tid_locked(cache, key);
+}
+
+// Head of the list of caches.
+static struct resolv_cache_info res_cache_list GUARDED_BY(cache_mutex);
+
+// insert resolv_cache_info into the list of resolv_cache_infos
+static void insert_cache_info_locked(resolv_cache_info* cache_info);
+// creates a resolv_cache_info
+static resolv_cache_info* create_cache_info();
+// empty the nameservers set for the named cache
+static void free_nameservers_locked(resolv_cache_info* cache_info);
+// return 1 if the provided list of name servers differs from the list of name servers
+// currently attached to the provided cache_info
+static int resolv_is_nameservers_equal_locked(resolv_cache_info* cache_info, const char** servers,
+ int numservers);
+// clears the stats samples contained withing the given cache_info
+static void res_cache_clear_stats_locked(resolv_cache_info* cache_info);
+
+// public API for netd to query if name server is set on specific netid
+bool resolv_has_nameservers(unsigned netid) {
+ std::lock_guard guard(cache_mutex);
+ resolv_cache_info* info = find_cache_info_locked(netid);
+ return (info != nullptr) && (info->nscount > 0);
+}
+
+namespace {
+
+// Map format: ReturnCode:rate_denom
+// if the ReturnCode is not associated with any rate_denom, use default
+// Sampling rate varies by return code; events to log are chosen randomly, with a
+// probability proportional to the sampling rate.
+constexpr const char DEFAULT_SUBSAMPLING_MAP[] = "default:1 0:100 7:10";
+
+std::unordered_map<int, uint32_t> resolv_get_dns_event_subsampling_map() {
+ using android::base::ParseInt;
+ using android::base::ParseUint;
+ using android::base::Split;
+ using server_configurable_flags::GetServerConfigurableFlag;
+ std::unordered_map<int, uint32_t> sampling_rate_map{};
+ std::vector<std::string> subsampling_vector =
+ Split(GetServerConfigurableFlag("netd_native", "dns_event_subsample_map",
+ DEFAULT_SUBSAMPLING_MAP),
+ " ");
+ for (const auto& pair : subsampling_vector) {
+ std::vector<std::string> rate_denom = Split(pair, ":");
+ int return_code;
+ uint32_t denom;
+ if (rate_denom.size() != 2) {
+ LOG(ERROR) << __func__ << ": invalid subsampling_pair = " << pair;
+ continue;
+ }
+ if (rate_denom[0] == "default") {
+ return_code = DNSEVENT_SUBSAMPLING_MAP_DEFAULT_KEY;
+ } else if (!ParseInt(rate_denom[0], &return_code)) {
+ LOG(ERROR) << __func__ << ": parse subsampling_pair failed = " << pair;
+ continue;
+ }
+ if (!ParseUint(rate_denom[1], &denom)) {
+ LOG(ERROR) << __func__ << ": parse subsampling_pair failed = " << pair;
+ continue;
+ }
+ sampling_rate_map[return_code] = denom;
+ }
+ return sampling_rate_map;
+}
+
+} // namespace
+
+static int resolv_create_cache_for_net_locked(unsigned netid) {
+ resolv_cache* cache = find_named_cache_locked(netid);
+ // Should not happen
+ if (cache) {
+ LOG(ERROR) << __func__ << ": Cache is already created, netId: " << netid;
+ return -EEXIST;
+ }
+
+ resolv_cache_info* cache_info = create_cache_info();
+ if (!cache_info) return -ENOMEM;
+ cache = resolv_cache_create();
+ if (!cache) {
+ free(cache_info);
+ return -ENOMEM;
+ }
+ cache_info->cache = cache;
+ cache_info->netid = netid;
+ cache_info->dns_event_subsampling_map = resolv_get_dns_event_subsampling_map();
+ insert_cache_info_locked(cache_info);
+
+ return 0;
+}
+
+int resolv_create_cache_for_net(unsigned netid) {
+ std::lock_guard guard(cache_mutex);
+ return resolv_create_cache_for_net_locked(netid);
+}
+
+void resolv_delete_cache_for_net(unsigned netid) {
+ std::lock_guard guard(cache_mutex);
+
+ struct resolv_cache_info* prev_cache_info = &res_cache_list;
+
+ while (prev_cache_info->next) {
+ struct resolv_cache_info* cache_info = prev_cache_info->next;
+
+ if (cache_info->netid == netid) {
+ prev_cache_info->next = cache_info->next;
+ cache_flush_locked(cache_info->cache);
+ free(cache_info->cache->entries);
+ free(cache_info->cache);
+ free_nameservers_locked(cache_info);
+ free(cache_info);
+ break;
+ }
+
+ prev_cache_info = prev_cache_info->next;
+ }
+}
+
+std::vector<unsigned> resolv_list_caches() {
+ std::lock_guard guard(cache_mutex);
+ struct resolv_cache_info* cache_info = res_cache_list.next;
+ std::vector<unsigned> result;
+ while (cache_info) {
+ result.push_back(cache_info->netid);
+ cache_info = cache_info->next;
+ }
+ return result;
+}
+
+static resolv_cache_info* create_cache_info() {
+ return (struct resolv_cache_info*) calloc(sizeof(struct resolv_cache_info), 1);
+}
+
+// TODO: convert this to a simple and efficient C++ container.
+static void insert_cache_info_locked(struct resolv_cache_info* cache_info) {
+ struct resolv_cache_info* last;
+ for (last = &res_cache_list; last->next; last = last->next) {}
+ last->next = cache_info;
+}
+
+static resolv_cache* find_named_cache_locked(unsigned netid) {
+ resolv_cache_info* info = find_cache_info_locked(netid);
+ if (info != NULL) return info->cache;
+ return NULL;
+}
+
+static resolv_cache_info* find_cache_info_locked(unsigned netid) {
+ struct resolv_cache_info* cache_info = res_cache_list.next;
+
+ while (cache_info) {
+ if (cache_info->netid == netid) {
+ break;
+ }
+
+ cache_info = cache_info->next;
+ }
+ return cache_info;
+}
+
+static void resolv_set_default_params(res_params* params) {
+ params->sample_validity = NSSAMPLE_VALIDITY;
+ params->success_threshold = SUCCESS_THRESHOLD;
+ params->min_samples = 0;
+ params->max_samples = 0;
+ params->base_timeout_msec = 0; // 0 = legacy algorithm
+ params->retry_count = 0;
+}
+
+static void resolv_set_experiment_params(res_params* params) {
+ using android::base::ParseInt;
+ using server_configurable_flags::GetServerConfigurableFlag;
+
+ if (params->retry_count == 0) {
+ params->retry_count = RES_DFLRETRY;
+ ParseInt(GetServerConfigurableFlag("netd_native", "retry_count", ""), ¶ms->retry_count);
+ }
+
+ if (params->base_timeout_msec == 0) {
+ params->base_timeout_msec = RES_TIMEOUT;
+ ParseInt(GetServerConfigurableFlag("netd_native", "retransmission_time_interval", ""),
+ ¶ms->base_timeout_msec);
+ }
+}
+
+int resolv_set_nameservers_for_net(unsigned netid, const char** servers, const int numservers,
+ const char* domains, const res_params* params) {
+ char* cp;
+ int* offset;
+ struct addrinfo* nsaddrinfo[MAXNS];
+
+ if (numservers > MAXNS) {
+ LOG(ERROR) << __func__ << ": numservers=" << numservers << ", MAXNS=" << MAXNS;
+ return E2BIG;
+ }
+
+ // Parse the addresses before actually locking or changing any state, in case there is an error.
+ // As a side effect this also reduces the time the lock is kept.
+ char sbuf[NI_MAXSERV];
+ snprintf(sbuf, sizeof(sbuf), "%u", NAMESERVER_PORT);
+ for (int i = 0; i < numservers; i++) {
+ // The addrinfo structures allocated here are freed in free_nameservers_locked().
+ const addrinfo hints = {
+ .ai_family = AF_UNSPEC, .ai_socktype = SOCK_DGRAM, .ai_flags = AI_NUMERICHOST};
+ int rt = getaddrinfo_numeric(servers[i], sbuf, hints, &nsaddrinfo[i]);
+ if (rt != 0) {
+ for (int j = 0; j < i; j++) {
+ freeaddrinfo(nsaddrinfo[j]);
+ }
+ LOG(INFO) << __func__ << ": getaddrinfo_numeric(" << servers[i]
+ << ") = " << gai_strerror(rt);
+ return EINVAL;
+ }
+ }
+
+ std::lock_guard guard(cache_mutex);
+
+ resolv_cache_info* cache_info = find_cache_info_locked(netid);
+
+ if (cache_info == NULL) return ENONET;
+
+ uint8_t old_max_samples = cache_info->params.max_samples;
+ if (params != NULL) {
+ cache_info->params = *params;
+ } else {
+ resolv_set_default_params(&cache_info->params);
+ }
+ resolv_set_experiment_params(&cache_info->params);
+ if (!resolv_is_nameservers_equal_locked(cache_info, servers, numservers)) {
+ // free current before adding new
+ free_nameservers_locked(cache_info);
+ for (int i = 0; i < numservers; i++) {
+ cache_info->nsaddrinfo[i] = nsaddrinfo[i];
+ cache_info->nameservers[i] = strdup(servers[i]);
+ LOG(INFO) << __func__ << ": netid = " << netid << ", addr = " << servers[i];
+ }
+ cache_info->nscount = numservers;
+
+ // Clear the NS statistics because the mapping to nameservers might have changed.
+ res_cache_clear_stats_locked(cache_info);
+
+ // increment the revision id to ensure that sample state is not written back if the
+ // servers change; in theory it would suffice to do so only if the servers or
+ // max_samples actually change, in practice the overhead of checking is higher than the
+ // cost, and overflows are unlikely
+ ++cache_info->revision_id;
+ } else {
+ if (cache_info->params.max_samples != old_max_samples) {
+ // If the maximum number of samples changes, the overhead of keeping the most recent
+ // samples around is not considered worth the effort, so they are cleared instead.
+ // All other parameters do not affect shared state: Changing these parameters does
+ // not invalidate the samples, as they only affect aggregation and the conditions
+ // under which servers are considered usable.
+ res_cache_clear_stats_locked(cache_info);
+ ++cache_info->revision_id;
+ }
+ for (int j = 0; j < numservers; j++) {
+ freeaddrinfo(nsaddrinfo[j]);
+ }
+ }
+
+ // Always update the search paths, since determining whether they actually changed is
+ // complex due to the zero-padding, and probably not worth the effort. Cache-flushing
+ // however is not necessary, since the stored cache entries do contain the domain, not
+ // just the host name.
+ strlcpy(cache_info->defdname, domains, sizeof(cache_info->defdname));
+ if ((cp = strchr(cache_info->defdname, '\n')) != NULL) *cp = '\0';
+ LOG(INFO) << __func__ << ": domains=\"" << cache_info->defdname << "\"";
+
+ cp = cache_info->defdname;
+ offset = cache_info->dnsrch_offset;
+ while (offset < cache_info->dnsrch_offset + MAXDNSRCH) {
+ while (*cp == ' ' || *cp == '\t') /* skip leading white space */
+ cp++;
+ if (*cp == '\0') /* stop if nothing more to do */
+ break;
+ *offset++ = cp - cache_info->defdname; /* record this search domain */
+ while (*cp) { /* zero-terminate it */
+ if (*cp == ' ' || *cp == '\t') {
+ *cp++ = '\0';
+ break;
+ }
+ cp++;
+ }
+ }
+ *offset = -1; /* cache_info->dnsrch_offset has MAXDNSRCH+1 items */
+
+ return 0;
+}
+
+static int resolv_is_nameservers_equal_locked(resolv_cache_info* cache_info, const char** servers,
+ int numservers) {
+ if (cache_info->nscount != numservers) {
+ return 0;
+ }
+
+ // Compare each name server against current name servers.
+ // TODO: this is incorrect if the list of current or previous nameservers
+ // contains duplicates. This does not really matter because the framework
+ // filters out duplicates, but we should probably fix it. It's also
+ // insensitive to the order of the nameservers; we should probably fix that
+ // too.
+ for (int i = 0; i < numservers; i++) {
+ for (int j = 0;; j++) {
+ if (j >= numservers) {
+ return 0;
+ }
+ if (strcmp(cache_info->nameservers[i], servers[j]) == 0) {
+ break;
+ }
+ }
+ }
+
+ return 1;
+}
+
+static void free_nameservers_locked(resolv_cache_info* cache_info) {
+ int i;
+ for (i = 0; i < cache_info->nscount; i++) {
+ free(cache_info->nameservers[i]);
+ cache_info->nameservers[i] = NULL;
+ if (cache_info->nsaddrinfo[i] != NULL) {
+ freeaddrinfo(cache_info->nsaddrinfo[i]);
+ cache_info->nsaddrinfo[i] = NULL;
+ }
+ cache_info->nsstats[i].sample_count = cache_info->nsstats[i].sample_next = 0;
+ }
+ cache_info->nscount = 0;
+ res_cache_clear_stats_locked(cache_info);
+ ++cache_info->revision_id;
+}
+
+void _resolv_populate_res_for_net(res_state statp) {
+ if (statp == NULL) {
+ return;
+ }
+ LOG(INFO) << __func__ << ": netid=" << statp->netid;
+
+ std::lock_guard guard(cache_mutex);
+ resolv_cache_info* info = find_cache_info_locked(statp->netid);
+ if (info != NULL) {
+ int nserv;
+ struct addrinfo* ai;
+ for (nserv = 0; nserv < MAXNS; nserv++) {
+ ai = info->nsaddrinfo[nserv];
+ if (ai == NULL) {
+ break;
+ }
+
+ if ((size_t) ai->ai_addrlen <= sizeof(statp->_u._ext.ext->nsaddrs[0])) {
+ if (statp->_u._ext.ext != NULL) {
+ memcpy(&statp->_u._ext.ext->nsaddrs[nserv], ai->ai_addr, ai->ai_addrlen);
+ statp->nsaddr_list[nserv].sin_family = AF_UNSPEC;
+ } else {
+ if ((size_t) ai->ai_addrlen <= sizeof(statp->nsaddr_list[0])) {
+ memcpy(&statp->nsaddr_list[nserv], ai->ai_addr, ai->ai_addrlen);
+ } else {
+ statp->nsaddr_list[nserv].sin_family = AF_UNSPEC;
+ }
+ }
+ } else {
+ LOG(INFO) << __func__ << ": found too long addrlen";
+ }
+ }
+ statp->nscount = nserv;
+ // now do search domains. Note that we cache the offsets as this code runs alot
+ // but the setting/offset-computer only runs when set/changed
+ // WARNING: Don't use str*cpy() here, this string contains zeroes.
+ memcpy(statp->defdname, info->defdname, sizeof(statp->defdname));
+ char** pp = statp->dnsrch;
+ int* p = info->dnsrch_offset;
+ while (pp < statp->dnsrch + MAXDNSRCH && *p != -1) {
+ *pp++ = &statp->defdname[0] + *p++;
+ }
+ }
+}
+
+/* Resolver reachability statistics. */
+
+static void _res_cache_add_stats_sample_locked(res_stats* stats, const res_sample* sample,
+ int max_samples) {
+ // Note: This function expects max_samples > 0, otherwise a (harmless) modification of the
+ // allocated but supposedly unused memory for samples[0] will happen
+ LOG(INFO) << __func__ << ": adding sample to stats, next = " << unsigned(stats->sample_next)
+ << ", count = " << unsigned(stats->sample_count);
+ stats->samples[stats->sample_next] = *sample;
+ if (stats->sample_count < max_samples) {
+ ++stats->sample_count;
+ }
+ if (++stats->sample_next >= max_samples) {
+ stats->sample_next = 0;
+ }
+}
+
+static void res_cache_clear_stats_locked(resolv_cache_info* cache_info) {
+ if (cache_info) {
+ for (int i = 0; i < MAXNS; ++i) {
+ cache_info->nsstats->sample_count = cache_info->nsstats->sample_next = 0;
+ }
+ }
+}
+
+int android_net_res_stats_get_info_for_net(unsigned netid, int* nscount,
+ struct sockaddr_storage servers[MAXNS], int* dcount,
+ char domains[MAXDNSRCH][MAXDNSRCHPATH],
+ res_params* params, struct res_stats stats[MAXNS],
+ int* wait_for_pending_req_timeout_count) {
+ int revision_id = -1;
+ std::lock_guard guard(cache_mutex);
+
+ resolv_cache_info* info = find_cache_info_locked(netid);
+ if (info) {
+ if (info->nscount > MAXNS) {
+ LOG(INFO) << __func__ << ": nscount " << info->nscount << " > MAXNS " << MAXNS;
+ errno = EFAULT;
+ return -1;
+ }
+ int i;
+ for (i = 0; i < info->nscount; i++) {
+ // Verify that the following assumptions are held, failure indicates corruption:
+ // - getaddrinfo() may never return a sockaddr > sockaddr_storage
+ // - all addresses are valid
+ // - there is only one address per addrinfo thanks to numeric resolution
+ int addrlen = info->nsaddrinfo[i]->ai_addrlen;
+ if (addrlen < (int) sizeof(struct sockaddr) || addrlen > (int) sizeof(servers[0])) {
+ LOG(INFO) << __func__ << ": nsaddrinfo[" << i << "].ai_addrlen == " << addrlen;
+ errno = EMSGSIZE;
+ return -1;
+ }
+ if (info->nsaddrinfo[i]->ai_addr == NULL) {
+ LOG(INFO) << __func__ << ": nsaddrinfo[" << i << "].ai_addr == NULL";
+ errno = ENOENT;
+ return -1;
+ }
+ if (info->nsaddrinfo[i]->ai_next != NULL) {
+ LOG(INFO) << __func__ << ": nsaddrinfo[" << i << "].ai_next != NULL";
+ errno = ENOTUNIQ;
+ return -1;
+ }
+ }
+ *nscount = info->nscount;
+ for (i = 0; i < info->nscount; i++) {
+ memcpy(&servers[i], info->nsaddrinfo[i]->ai_addr, info->nsaddrinfo[i]->ai_addrlen);
+ stats[i] = info->nsstats[i];
+ }
+ for (i = 0; i < MAXDNSRCH; i++) {
+ const char* cur_domain = info->defdname + info->dnsrch_offset[i];
+ // dnsrch_offset[i] can either be -1 or point to an empty string to indicate the end
+ // of the search offsets. Checking for < 0 is not strictly necessary, but safer.
+ // TODO: Pass in a search domain array instead of a string to
+ // resolv_set_nameservers_for_net() and make this double check unnecessary.
+ if (info->dnsrch_offset[i] < 0 ||
+ ((size_t) info->dnsrch_offset[i]) >= sizeof(info->defdname) || !cur_domain[0]) {
+ break;
+ }
+ strlcpy(domains[i], cur_domain, MAXDNSRCHPATH);
+ }
+ *dcount = i;
+ *params = info->params;
+ revision_id = info->revision_id;
+ *wait_for_pending_req_timeout_count = info->wait_for_pending_req_timeout_count;
+ }
+
+ return revision_id;
+}
+
+std::vector<std::string> resolv_cache_dump_subsampling_map(unsigned netid) {
+ using android::base::StringPrintf;
+ std::lock_guard guard(cache_mutex);
+ resolv_cache_info* cache_info = find_cache_info_locked(netid);
+ if (cache_info == nullptr) return {};
+ std::vector<std::string> result;
+ for (const auto& pair : cache_info->dns_event_subsampling_map) {
+ result.push_back(StringPrintf("%s:%d",
+ (pair.first == DNSEVENT_SUBSAMPLING_MAP_DEFAULT_KEY)
+ ? "default"
+ : std::to_string(pair.first).c_str(),
+ pair.second));
+ }
+ return result;
+}
+
+// Decides whether an event should be sampled using a random number generator and
+// a sampling factor derived from the netid and the return code.
+//
+// Returns the subsampling rate if the event should be sampled, or 0 if it should be discarded.
+uint32_t resolv_cache_get_subsampling_denom(unsigned netid, int return_code) {
+ std::lock_guard guard(cache_mutex);
+ resolv_cache_info* cache_info = find_cache_info_locked(netid);
+ if (cache_info == nullptr) return 0; // Don't log anything at all.
+ const auto& subsampling_map = cache_info->dns_event_subsampling_map;
+ auto search_returnCode = subsampling_map.find(return_code);
+ uint32_t denom;
+ if (search_returnCode != subsampling_map.end()) {
+ denom = search_returnCode->second;
+ } else {
+ auto search_default = subsampling_map.find(DNSEVENT_SUBSAMPLING_MAP_DEFAULT_KEY);
+ denom = (search_default == subsampling_map.end()) ? 0 : search_default->second;
+ }
+ return denom;
+}
+
+int resolv_cache_get_resolver_stats(unsigned netid, res_params* params, res_stats stats[MAXNS]) {
+ std::lock_guard guard(cache_mutex);
+ resolv_cache_info* info = find_cache_info_locked(netid);
+ if (info) {
+ memcpy(stats, info->nsstats, sizeof(info->nsstats));
+ *params = info->params;
+ return info->revision_id;
+ }
+
+ return -1;
+}
+
+void _resolv_cache_add_resolver_stats_sample(unsigned netid, int revision_id, int ns,
+ const res_sample* sample, int max_samples) {
+ if (max_samples <= 0) return;
+
+ std::lock_guard guard(cache_mutex);
+ resolv_cache_info* info = find_cache_info_locked(netid);
+
+ if (info && info->revision_id == revision_id) {
+ _res_cache_add_stats_sample_locked(&info->nsstats[ns], sample, max_samples);
+ }
+}
diff --git a/resolv/res_cache_test.cpp b/resolv/res_cache_test.cpp
new file mode 100644
index 0000000..0b09d88
--- /dev/null
+++ b/resolv/res_cache_test.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#include <netdb.h>
+
+#include <cutils/properties.h>
+#include <gmock/gmock-matchers.h>
+#include <gtest/gtest.h>
+
+#include "netd_resolv/stats.h"
+#include "resolv_cache.h"
+
+constexpr int TEST_NETID = 30;
+
+namespace {
+
+constexpr int EAI_OK = 0;
+constexpr char DNS_EVENT_SUBSAMPLING_MAP_FLAG[] =
+ "persist.device_config.netd_native.dns_event_subsample_map";
+
+class ScopedCacheCreate {
+ public:
+ explicit ScopedCacheCreate(unsigned netid, const char* subsampling_map,
+ const char* property = DNS_EVENT_SUBSAMPLING_MAP_FLAG)
+ : mStoredNetId(netid), mStoredProperty(property) {
+ property_get(property, mStoredMap, "");
+ property_set(property, subsampling_map);
+ EXPECT_EQ(0, resolv_create_cache_for_net(netid));
+ }
+ ~ScopedCacheCreate() {
+ resolv_delete_cache_for_net(mStoredNetId);
+ property_set(mStoredProperty, mStoredMap);
+ }
+
+ private:
+ unsigned mStoredNetId;
+ const char* mStoredProperty;
+ char mStoredMap[PROPERTY_VALUE_MAX]{};
+};
+
+} // namespace
+
+TEST(ResolvCacheTest, DnsEventSubsampling) {
+ // Test defaults, default flag is "default:1 0:100 7:10" if no experiment flag is set
+ {
+ ScopedCacheCreate scopedCacheCreate(TEST_NETID, "");
+ EXPECT_EQ(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_NODATA), 10U);
+ EXPECT_EQ(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_OK), 100U);
+ EXPECT_EQ(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_BADFLAGS),
+ 1U); // default
+ EXPECT_THAT(resolv_cache_dump_subsampling_map(TEST_NETID),
+ testing::UnorderedElementsAreArray({"default:1", "0:100", "7:10"}));
+ }
+ // Now change the experiment flag to "0:42 default:666"
+ {
+ ScopedCacheCreate scopedCacheCreate(TEST_NETID, "0:42 default:666");
+ EXPECT_EQ(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_OK), 42U);
+ EXPECT_EQ(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_NODATA),
+ 666U); // default
+ EXPECT_THAT(resolv_cache_dump_subsampling_map(TEST_NETID),
+ testing::UnorderedElementsAreArray({"default:666", "0:42"}));
+ }
+ // Now change the experiment flag to something illegal
+ {
+ ScopedCacheCreate scopedCacheCreate(TEST_NETID, "asvaxx");
+ // 0(disable log) is the default value if experiment flag is invalid.
+ EXPECT_EQ(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_OK), 0U);
+ EXPECT_EQ(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_NODATA), 0U);
+ EXPECT_TRUE(resolv_cache_dump_subsampling_map(TEST_NETID).empty());
+ }
+ // Test negative and zero denom
+ {
+ ScopedCacheCreate scopedCacheCreate(TEST_NETID, "0:-42 default:-666 7:10 10:0");
+ // 0(disable log) is the default value if no valid denom is set
+ EXPECT_EQ(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_OK), 0U);
+ EXPECT_EQ(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_BADFLAGS), 0U);
+ EXPECT_EQ(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_NODATA), 10U);
+ EXPECT_EQ(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_SOCKTYPE), 0U);
+ EXPECT_THAT(resolv_cache_dump_subsampling_map(TEST_NETID),
+ testing::UnorderedElementsAreArray({"7:10", "10:0"}));
+ }
+}
diff --git a/resolv/res_comp.cpp b/resolv/res_comp.cpp
new file mode 100644
index 0000000..98461b3
--- /dev/null
+++ b/resolv/res_comp.cpp
@@ -0,0 +1,212 @@
+/* $NetBSD: res_comp.c,v 1.6 2004/05/22 23:47:09 christos Exp $ */
+
+/*
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Portions Copyright (c) 1993 by Digital Equipment Corporation.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies, and that
+ * the name of Digital Equipment Corporation not be used in advertising or
+ * publicity pertaining to distribution of the document or software without
+ * specific, written prior permission.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
+ * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ */
+
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Portions Copyright (c) 1996-1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <arpa/nameser.h>
+#include <ctype.h>
+#include <netinet/in.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "resolv_private.h"
+
+/*
+ * Expand compressed domain name 'src' to full domain name.
+ * 'msg' is a pointer to the begining of the message,
+ * 'eom' points to the first location after the message,
+ * 'dst' is a pointer to a buffer of size 'dstsiz' for the result.
+ * Return size of compressed name or -1 if there was an error.
+ */
+int dn_expand(const u_char* msg, const u_char* eom, const u_char* src, char* dst, int dstsiz) {
+ int n = ns_name_uncompress(msg, eom, src, dst, (size_t) dstsiz);
+
+ if (n > 0 && dst[0] == '.') dst[0] = '\0';
+ return (n);
+}
+
+/*
+ * Pack domain name 'exp_dn' in presentation form into 'comp_dn'.
+ * Return the size of the compressed name or -1.
+ * 'length' is the size of the array pointed to by 'comp_dn'.
+ */
+int dn_comp(const char* src, u_char* dst, int dstsiz, u_char** dnptrs, u_char** lastdnptr) {
+ return (ns_name_compress(src, dst, (size_t) dstsiz, (const u_char**) dnptrs,
+ (const u_char**) lastdnptr));
+}
+
+/*
+ * Skip over a compressed domain name. Return the size or -1.
+ */
+int dn_skipname(const u_char* ptr, const u_char* eom) {
+ const u_char* saveptr = ptr;
+
+ if (ns_name_skip(&ptr, eom) == -1) return (-1);
+ return (ptr - saveptr);
+}
+
+/*
+ * Verify that a domain name uses an acceptable character set.
+ */
+
+/*
+ * Note the conspicuous absence of ctype macros in these definitions. On
+ * non-ASCII hosts, we can't depend on string literals or ctype macros to
+ * tell us anything about network-format data. The rest of the BIND system
+ * is not careful about this, but for some reason, we're doing it right here.
+ */
+
+/* BIONIC: We also accept underscores in the middle of labels.
+ * This extension is needed to make resolution on some VPN networks
+ * work properly.
+ */
+
+#define PERIOD 0x2e
+#define hyphenchar(c) ((c) == 0x2d)
+#define bslashchar(c) ((c) == 0x5c)
+#define periodchar(c) ((c) == PERIOD)
+#define asterchar(c) ((c) == 0x2a)
+#define alphachar(c) (((c) >= 0x41 && (c) <= 0x5a) || ((c) >= 0x61 && (c) <= 0x7a))
+#define digitchar(c) ((c) >= 0x30 && (c) <= 0x39)
+#define underscorechar(c) ((c) == 0x5f)
+
+#define borderchar(c) (alphachar(c) || digitchar(c))
+#define middlechar(c) (borderchar(c) || hyphenchar(c) || underscorechar(c))
+#define domainchar(c) ((c) > 0x20 && (c) < 0x7f)
+
+int res_hnok(const char* dn) {
+ int pch = PERIOD, ch = *dn++;
+
+ while (ch != '\0') {
+ int nch = *dn++;
+
+ if (periodchar(ch)) {
+ ;
+ } else if (periodchar(pch)) {
+ if (!borderchar(ch)) return (0);
+ } else if (periodchar(nch) || nch == '\0') {
+ if (!borderchar(ch)) return (0);
+ } else {
+ if (!middlechar(ch)) return (0);
+ }
+ pch = ch, ch = nch;
+ }
+ return (1);
+}
+
+/*
+ * hostname-like (A, MX, WKS) owners can have "*" as their first label
+ * but must otherwise be as a host name.
+ */
+int res_ownok(const char* dn) {
+ if (asterchar(dn[0])) {
+ if (periodchar(dn[1])) return (res_hnok(dn + 2));
+ if (dn[1] == '\0') return (1);
+ }
+ return (res_hnok(dn));
+}
+
+/*
+ * SOA RNAMEs and RP RNAMEs can have any printable character in their first
+ * label, but the rest of the name has to look like a host name.
+ */
+int res_mailok(const char* dn) {
+ int ch, escaped = 0;
+
+ /* "." is a valid missing representation */
+ if (*dn == '\0') return (1);
+
+ /* otherwise <label>.<hostname> */
+ while ((ch = *dn++) != '\0') {
+ if (!domainchar(ch)) return (0);
+ if (!escaped && periodchar(ch)) break;
+ if (escaped)
+ escaped = 0;
+ else if (bslashchar(ch))
+ escaped = 1;
+ }
+ if (periodchar(ch)) return (res_hnok(dn));
+ return (0);
+}
+
+/*
+ * This function is quite liberal, since RFC 1034's character sets are only
+ * recommendations.
+ */
+int res_dnok(const char* dn) {
+ int ch;
+
+ while ((ch = *dn++) != '\0')
+ if (!domainchar(ch)) return (0);
+ return (1);
+}
diff --git a/resolv/res_debug.cpp b/resolv/res_debug.cpp
new file mode 100644
index 0000000..5b20a58
--- /dev/null
+++ b/resolv/res_debug.cpp
@@ -0,0 +1,540 @@
+/* $NetBSD: res_debug.c,v 1.13 2012/06/25 22:32:45 abs Exp $ */
+
+/*
+ * Portions Copyright (C) 2004, 2005, 2008, 2009 Internet Systems Consortium, Inc. ("ISC")
+ * Portions Copyright (C) 1996-2003 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Copyright (c) 1985
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Portions Copyright (c) 1993 by Digital Equipment Corporation.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies, and that
+ * the name of Digital Equipment Corporation not be used in advertising or
+ * publicity pertaining to distribution of the document or software without
+ * specific, written prior permission.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
+ * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ */
+
+/*
+ * Portions Copyright (c) 1995 by International Business Machines, Inc.
+ *
+ * International Business Machines, Inc. (hereinafter called IBM) grants
+ * permission under its copyrights to use, copy, modify, and distribute this
+ * Software with or without fee, provided that the above copyright notice and
+ * all paragraphs of this notice appear in all copies, and that the name of IBM
+ * not be used in connection with the marketing of any product incorporating
+ * the Software or modifications thereof, without specific, written prior
+ * permission.
+ *
+ * To the extent it has a right to do so, IBM grants an immunity from suit
+ * under its patents, if any, for the use, sale or manufacture of products to
+ * the extent that such products are used for performing Domain Name System
+ * dynamic updates in TCP/IP networks by means of the Software. No immunity is
+ * granted for any product per se or for any other function of any product.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL,
+ * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN
+ * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#define LOG_TAG "res_debug"
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+#include <netinet/in.h>
+
+#include <aidl/android/net/IDnsResolver.h>
+#include <android-base/logging.h>
+#include <ctype.h>
+#include <errno.h>
+#include <math.h>
+#include <netdb.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <time.h>
+
+#include "resolv_private.h"
+
+// Default to disabling verbose logging unless overridden by Android.bp
+// for debuggable builds.
+//
+// NOTE: Verbose resolver logs could contain PII -- do NOT enable in production builds
+#ifndef RESOLV_ALLOW_VERBOSE_LOGGING
+#define RESOLV_ALLOW_VERBOSE_LOGGING 0
+#endif
+
+struct res_sym {
+ int number; /* Identifying number, like T_MX */
+ const char* name; /* Its symbolic name, like "MX" */
+ const char* humanname; /* Its fun name, like "mail exchanger" */
+};
+
+// add a formatted string to a bounded buffer
+// TODO: convert to std::string
+static char* dbprint(char* p, char* end, const char* format, ...) {
+ int avail, n;
+ va_list args;
+
+ avail = end - p;
+
+ if (avail <= 0) return p;
+
+ va_start(args, format);
+ n = vsnprintf(p, avail, format, args);
+ va_end(args);
+
+ /* certain C libraries return -1 in case of truncation */
+ if (n < 0 || n > avail) n = avail;
+
+ p += n;
+ /* certain C libraries do not zero-terminate in case of truncation */
+ if (p == end) p[-1] = 0;
+
+ return p;
+}
+
+static void do_section(ns_msg* handle, ns_sect section) {
+ int n, rrnum;
+ int buflen = 2048;
+ ns_opcode opcode;
+ ns_rr rr;
+ char temp[2048], *p = temp, *end = p + sizeof(temp);
+
+ /*
+ * Print answer records.
+ */
+
+ char* buf = (char*) malloc((size_t) buflen);
+ if (buf == NULL) {
+ dbprint(p, end, ";; memory allocation failure\n");
+ LOG(VERBOSE) << __func__ << ": " << temp;
+ return;
+ }
+
+ opcode = (ns_opcode) ns_msg_getflag(*handle, ns_f_opcode);
+ rrnum = 0;
+ for (;;) {
+ if (ns_parserr(handle, section, rrnum, &rr)) {
+ if (errno != ENODEV)
+ dbprint(p, end, ";; ns_parserr: %s", strerror(errno));
+ goto cleanup;
+ }
+ if (section == ns_s_qd)
+ dbprint(p, end, ";;\t%s, type = %s, class = %s\n", ns_rr_name(rr),
+ p_type(ns_rr_type(rr)), p_class(ns_rr_class(rr)));
+ else if (section == ns_s_ar && ns_rr_type(rr) == ns_t_opt) {
+ size_t rdatalen, ttl;
+ uint16_t optcode, optlen;
+
+ rdatalen = ns_rr_rdlen(rr);
+ ttl = ns_rr_ttl(rr);
+ dbprint(p, end, "; EDNS: version: %zu, udp=%u, flags=%04zx\n", (ttl >> 16) & 0xff,
+ ns_rr_class(rr), ttl & 0xffff);
+ const u_char* cp = ns_rr_rdata(rr);
+ while (rdatalen <= ns_rr_rdlen(rr) && rdatalen >= 4) {
+ int i;
+
+ GETSHORT(optcode, cp);
+ GETSHORT(optlen, cp);
+
+ if (optcode == NS_OPT_NSID) {
+ p = dbprint(p, end, "; NSID: ");
+ if (optlen == 0) {
+ p = dbprint(p, end, "; NSID\n");
+ } else {
+ p = dbprint(p, end, "; NSID: ");
+ for (i = 0; i < optlen; i++) {
+ p = dbprint(p, end, "%02x ", cp[i]);
+ }
+ p = dbprint(p, end, " (");
+ for (i = 0; i < optlen; i++) {
+ p = dbprint(p, end, "%c", isprint(cp[i]) ? cp[i] : '.');
+ }
+ p = dbprint(p, end, ")\n");
+ }
+ } else {
+ if (optlen == 0) {
+ p = dbprint(p, end, "; OPT=%u\n", optcode);
+ } else {
+ p = dbprint(p, end, "; OPT=%u: ", optcode);
+ for (i = 0; i < optlen; i++) {
+ p = dbprint(p, end, "%02x ", cp[i]);
+ }
+ p = dbprint(p, end, " (");
+ for (i = 0; i < optlen; i++) {
+ p = dbprint(p, end, "%c", isprint(cp[i]) ? cp[i] : '.');
+ }
+ p = dbprint(p, end, ")\n");
+ }
+ }
+ rdatalen -= 4 + optlen;
+ cp += optlen;
+ }
+ } else {
+ n = ns_sprintrr(handle, &rr, NULL, NULL, buf, (u_int) buflen);
+ if (n < 0) {
+ if (errno == ENOSPC) {
+ free(buf);
+ buf = NULL;
+ if (buflen < 131072) {
+ buf = (char*) malloc((size_t)(buflen += 1024));
+ }
+ if (buf == NULL) {
+ p = dbprint(p, end, ";; memory allocation failure\n");
+ LOG(VERBOSE) << __func__ << ": " << temp;
+ return;
+ }
+ continue;
+ }
+ p = dbprint(p, end, ";; ns_sprintrr: %s\n", strerror(errno));
+ goto cleanup;
+ }
+ p = dbprint(p, end, ";; %s\n", buf);
+ }
+ rrnum++;
+ }
+cleanup:
+ free(buf);
+ LOG(VERBOSE) << temp;
+}
+
+/*
+ * Print the contents of a query.
+ * This is intended to be primarily a debugging routine.
+ */
+void res_pquery(const u_char* msg, int len) {
+ if (!WOULD_LOG(VERBOSE)) return;
+
+ ns_msg handle;
+ int qdcount, ancount, nscount, arcount;
+ u_int opcode, rcode, id;
+ char temp[2048], *p = temp, *end = p + sizeof(temp);
+
+ if (ns_initparse(msg, len, &handle) < 0) {
+ dbprint(p, end, ";; ns_initparse: %s\n", strerror(errno));
+ return;
+ }
+ opcode = ns_msg_getflag(handle, ns_f_opcode);
+ rcode = ns_msg_getflag(handle, ns_f_rcode);
+ id = ns_msg_id(handle);
+ qdcount = ns_msg_count(handle, ns_s_qd);
+ ancount = ns_msg_count(handle, ns_s_an);
+ nscount = ns_msg_count(handle, ns_s_ns);
+ arcount = ns_msg_count(handle, ns_s_ar);
+
+ /*
+ * Print header fields.
+ */
+ dbprint(p, end, ";; ->>HEADER<<- opcode: %s, status: %s, id: %d\n", _res_opcodes[opcode],
+ p_rcode((int)rcode), id);
+ p = dbprint(p, end, ";");
+ p = dbprint(p, end, "; flags:");
+ if (ns_msg_getflag(handle, ns_f_qr)) p = dbprint(p, end, " qr");
+ if (ns_msg_getflag(handle, ns_f_aa)) p = dbprint(p, end, " aa");
+ if (ns_msg_getflag(handle, ns_f_tc)) p = dbprint(p, end, " tc");
+ if (ns_msg_getflag(handle, ns_f_rd)) p = dbprint(p, end, " rd");
+ if (ns_msg_getflag(handle, ns_f_ra)) p = dbprint(p, end, " ra");
+ if (ns_msg_getflag(handle, ns_f_z)) p = dbprint(p, end, " ??");
+ if (ns_msg_getflag(handle, ns_f_ad)) p = dbprint(p, end, " ad");
+ if (ns_msg_getflag(handle, ns_f_cd)) p = dbprint(p, end, " cd");
+ p = dbprint(p, end, "; %s: %d", p_section(ns_s_qd, (int)opcode), qdcount);
+ p = dbprint(p, end, ", %s: %d", p_section(ns_s_an, (int)opcode), ancount);
+ p = dbprint(p, end, ", %s: %d", p_section(ns_s_ns, (int)opcode), nscount);
+ p = dbprint(p, end, ", %s: %d", p_section(ns_s_ar, (int)opcode), arcount);
+
+ LOG(VERBOSE) << temp;
+
+ /*
+ * Print the various sections.
+ */
+ do_section(&handle, ns_s_qd);
+ do_section(&handle, ns_s_an);
+ do_section(&handle, ns_s_ns);
+ do_section(&handle, ns_s_ar);
+ if (qdcount == 0 && ancount == 0 && nscount == 0 && arcount == 0) LOG(VERBOSE) << ";;";
+}
+
+/*
+ * Names of RR classes and qclasses. Classes and qclasses are the same, except
+ * that C_ANY is a qclass but not a class. (You can ask for records of class
+ * C_ANY, but you can't have any records of that class in the database.)
+ */
+static const struct res_sym p_class_syms[] = {
+ {C_IN, "IN", (char*) 0}, {C_CHAOS, "CH", (char*) 0}, {C_CHAOS, "CHAOS", (char*) 0},
+ {C_HS, "HS", (char*) 0}, {C_HS, "HESIOD", (char*) 0}, {C_ANY, "ANY", (char*) 0},
+ {C_NONE, "NONE", (char*) 0}, {C_IN, (char*) 0, (char*) 0}};
+
+/*
+ * Names of message sections.
+ */
+static const struct res_sym p_default_section_syms[] = {{ns_s_qd, "QUERY", (char*) 0},
+ {ns_s_an, "ANSWER", (char*) 0},
+ {ns_s_ns, "AUTHORITY", (char*) 0},
+ {ns_s_ar, "ADDITIONAL", (char*) 0},
+ {0, (char*) 0, (char*) 0}};
+
+static const struct res_sym p_update_section_syms[] = {{S_ZONE, "ZONE", (char*) 0},
+ {S_PREREQ, "PREREQUISITE", (char*) 0},
+ {S_UPDATE, "UPDATE", (char*) 0},
+ {S_ADDT, "ADDITIONAL", (char*) 0},
+ {0, (char*) 0, (char*) 0}};
+
+/*
+ * Names of RR types and qtypes. Types and qtypes are the same, except
+ * that T_ANY is a qtype but not a type. (You can ask for records of type
+ * T_ANY, but you can't have any records of that type in the database.)
+ */
+const struct res_sym p_type_syms[] = {
+ {ns_t_a, "A", "address"},
+ {ns_t_ns, "NS", "name server"},
+ {ns_t_md, "MD", "mail destination (deprecated)"},
+ {ns_t_mf, "MF", "mail forwarder (deprecated)"},
+ {ns_t_cname, "CNAME", "canonical name"},
+ {ns_t_soa, "SOA", "start of authority"},
+ {ns_t_mb, "MB", "mailbox"},
+ {ns_t_mg, "MG", "mail group member"},
+ {ns_t_mr, "MR", "mail rename"},
+ {ns_t_null, "NULL", "null"},
+ {ns_t_wks, "WKS", "well-known service (deprecated)"},
+ {ns_t_ptr, "PTR", "domain name pointer"},
+ {ns_t_hinfo, "HINFO", "host information"},
+ {ns_t_minfo, "MINFO", "mailbox information"},
+ {ns_t_mx, "MX", "mail exchanger"},
+ {ns_t_txt, "TXT", "text"},
+ {ns_t_rp, "RP", "responsible person"},
+ {ns_t_afsdb, "AFSDB", "DCE or AFS server"},
+ {ns_t_x25, "X25", "X25 address"},
+ {ns_t_isdn, "ISDN", "ISDN address"},
+ {ns_t_rt, "RT", "router"},
+ {ns_t_nsap, "NSAP", "nsap address"},
+ {ns_t_nsap_ptr, "NSAP_PTR", "domain name pointer"},
+ {ns_t_sig, "SIG", "signature"},
+ {ns_t_key, "KEY", "key"},
+ {ns_t_px, "PX", "mapping information"},
+ {ns_t_gpos, "GPOS", "geographical position (withdrawn)"},
+ {ns_t_aaaa, "AAAA", "IPv6 address"},
+ {ns_t_loc, "LOC", "location"},
+ {ns_t_nxt, "NXT", "next valid name (unimplemented)"},
+ {ns_t_eid, "EID", "endpoint identifier (unimplemented)"},
+ {ns_t_nimloc, "NIMLOC", "NIMROD locator (unimplemented)"},
+ {ns_t_srv, "SRV", "server selection"},
+ {ns_t_atma, "ATMA", "ATM address (unimplemented)"},
+ {ns_t_naptr, "NAPTR", "naptr"},
+ {ns_t_kx, "KX", "key exchange"},
+ {ns_t_cert, "CERT", "certificate"},
+ {ns_t_a6, "A", "IPv6 address (experminental)"},
+ {ns_t_dname, "DNAME", "non-terminal redirection"},
+ {ns_t_opt, "OPT", "opt"},
+ {ns_t_apl, "apl", "apl"},
+ {ns_t_ds, "DS", "delegation signer"},
+ {ns_t_sshfp, "SSFP", "SSH fingerprint"},
+ {ns_t_ipseckey, "IPSECKEY", "IPSEC key"},
+ {ns_t_rrsig, "RRSIG", "rrsig"},
+ {ns_t_nsec, "NSEC", "nsec"},
+ {ns_t_dnskey, "DNSKEY", "DNS key"},
+ {ns_t_dhcid, "DHCID", "dynamic host configuration identifier"},
+ {ns_t_nsec3, "NSEC3", "nsec3"},
+ {ns_t_nsec3param, "NSEC3PARAM", "NSEC3 parameters"},
+ {ns_t_hip, "HIP", "host identity protocol"},
+ {ns_t_spf, "SPF", "sender policy framework"},
+ {ns_t_tkey, "TKEY", "tkey"},
+ {ns_t_tsig, "TSIG", "transaction signature"},
+ {ns_t_ixfr, "IXFR", "incremental zone transfer"},
+ {ns_t_axfr, "AXFR", "zone transfer"},
+ {ns_t_zxfr, "ZXFR", "compressed zone transfer"},
+ {ns_t_mailb, "MAILB", "mailbox-related data (deprecated)"},
+ {ns_t_maila, "MAILA", "mail agent (deprecated)"},
+ {ns_t_naptr, "NAPTR", "URN Naming Authority"},
+ {ns_t_kx, "KX", "Key Exchange"},
+ {ns_t_cert, "CERT", "Certificate"},
+ {ns_t_a6, "A6", "IPv6 Address"},
+ {ns_t_dname, "DNAME", "dname"},
+ {ns_t_sink, "SINK", "Kitchen Sink (experimental)"},
+ {ns_t_opt, "OPT", "EDNS Options"},
+ {ns_t_any, "ANY", "\"any\""},
+ {ns_t_dlv, "DLV", "DNSSEC look-aside validation"},
+ {0, NULL, NULL}};
+
+/*
+ * Names of DNS rcodes.
+ */
+static const struct res_sym p_rcode_syms[] = {{ns_r_noerror, "NOERROR", "no error"},
+ {ns_r_formerr, "FORMERR", "format error"},
+ {ns_r_servfail, "SERVFAIL", "server failed"},
+ {ns_r_nxdomain, "NXDOMAIN", "no such domain name"},
+ {ns_r_notimpl, "NOTIMP", "not implemented"},
+ {ns_r_refused, "REFUSED", "refused"},
+ {ns_r_yxdomain, "YXDOMAIN", "domain name exists"},
+ {ns_r_yxrrset, "YXRRSET", "rrset exists"},
+ {ns_r_nxrrset, "NXRRSET", "rrset doesn't exist"},
+ {ns_r_notauth, "NOTAUTH", "not authoritative"},
+ {ns_r_notzone, "NOTZONE", "Not in zone"},
+ {ns_r_max, "", ""},
+ {ns_r_badsig, "BADSIG", "bad signature"},
+ {ns_r_badkey, "BADKEY", "bad key"},
+ {ns_r_badtime, "BADTIME", "bad time"},
+ {0, NULL, NULL}};
+
+static const char* sym_ntos(const struct res_sym* syms, int number, int* success) {
+ static char unname[20];
+
+ for (; syms->name != 0; syms++) {
+ if (number == syms->number) {
+ if (success) *success = 1;
+ return (syms->name);
+ }
+ }
+
+ snprintf(unname, sizeof(unname), "%d", number); /* XXX nonreentrant */
+ if (success) *success = 0;
+ return (unname);
+}
+
+/*
+ * Return a string for the type.
+ */
+const char* p_type(int type) {
+ int success;
+ const char* result;
+ static char typebuf[20];
+
+ result = sym_ntos(p_type_syms, type, &success);
+ if (success) return (result);
+ if (type < 0 || type > 0xffff) return ("BADTYPE");
+ snprintf(typebuf, sizeof(typebuf), "TYPE%d", type);
+ return (typebuf);
+}
+
+/*
+ * Return a string for the type.
+ */
+const char* p_section(int section, int opcode) {
+ const struct res_sym* symbols;
+
+ switch (opcode) {
+ case ns_o_update:
+ symbols = p_update_section_syms;
+ break;
+ default:
+ symbols = p_default_section_syms;
+ break;
+ }
+ return (sym_ntos(symbols, section, (int*) 0));
+}
+
+/*
+ * Return a mnemonic for class.
+ */
+const char* p_class(int cl) {
+ int success;
+ const char* result;
+ static char classbuf[20];
+
+ result = sym_ntos(p_class_syms, cl, &success);
+ if (success) return (result);
+ if (cl < 0 || cl > 0xffff) return ("BADCLASS");
+ snprintf(classbuf, sizeof(classbuf), "CLASS%d", cl);
+ return (classbuf);
+}
+
+/*
+ * Return a string for the rcode.
+ */
+const char* p_rcode(int rcode) {
+ return (sym_ntos(p_rcode_syms, rcode, (int*) 0));
+}
+
+int resolv_set_log_severity(uint32_t logSeverity) {
+ switch (logSeverity) {
+ case aidl::android::net::IDnsResolver::DNS_RESOLVER_LOG_VERBOSE:
+ logSeverity = android::base::VERBOSE;
+ // *** enable verbose logging only when DBG is set. It prints sensitive data ***
+ if (RESOLV_ALLOW_VERBOSE_LOGGING == false) {
+ logSeverity = android::base::DEBUG;
+ LOG(ERROR) << "Refusing to set VERBOSE logging in non-debuggable build";
+ // TODO: Return EACCES then callers could know if the log
+ // severity is acceptable
+ }
+ break;
+ case aidl::android::net::IDnsResolver::DNS_RESOLVER_LOG_DEBUG:
+ logSeverity = android::base::DEBUG;
+ break;
+ case aidl::android::net::IDnsResolver::DNS_RESOLVER_LOG_INFO:
+ logSeverity = android::base::INFO;
+ break;
+ case aidl::android::net::IDnsResolver::DNS_RESOLVER_LOG_WARNING:
+ logSeverity = android::base::WARNING;
+ break;
+ case aidl::android::net::IDnsResolver::DNS_RESOLVER_LOG_ERROR:
+ logSeverity = android::base::ERROR;
+ break;
+ default:
+ LOG(ERROR) << __func__ << ": invalid log severity: " << logSeverity;
+ return -EINVAL;
+ }
+ android::base::SetMinimumLogSeverity(static_cast<android::base::LogSeverity>(logSeverity));
+ return 0;
+}
diff --git a/resolv/res_debug.h b/resolv/res_debug.h
new file mode 100644
index 0000000..2ba31d4
--- /dev/null
+++ b/resolv/res_debug.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#pragma once
+
+#include <cinttypes>
+
+int resolv_set_log_severity(uint32_t logSeverity);
diff --git a/resolv/res_init.cpp b/resolv/res_init.cpp
new file mode 100644
index 0000000..bdbe162
--- /dev/null
+++ b/resolv/res_init.cpp
@@ -0,0 +1,399 @@
+/* $NetBSD: res_init.c,v 1.8 2006/03/19 03:10:08 christos Exp $ */
+
+/*
+ * Copyright (c) 1985, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Portions Copyright (c) 1993 by Digital Equipment Corporation.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies, and that
+ * the name of Digital Equipment Corporation not be used in advertising or
+ * publicity pertaining to distribution of the document or software without
+ * specific, written prior permission.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
+ * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ */
+
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Portions Copyright (c) 1996-1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define LOG_TAG "res_init"
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/types.h>
+
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+#include <netinet/in.h>
+
+#include <android-base/logging.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "netd_resolv/resolv.h"
+#include "res_state_ext.h"
+#include "resolv_private.h"
+
+
+static void res_setoptions(res_state, const char*, const char*);
+
+/*
+ * Resolver state default settings.
+ */
+
+/*
+ * Set up default settings. If the configuration file exist, the values
+ * there will have precedence. Otherwise, the server address is set to
+ * INADDR_ANY and the default domain name comes from the gethostname().
+ *
+ * An interrim version of this code (BIND 4.9, pre-4.4BSD) used 127.0.0.1
+ * rather than INADDR_ANY ("0.0.0.0") as the default name server address
+ * since it was noted that INADDR_ANY actually meant ``the first interface
+ * you "ifconfig"'d at boot time'' and if this was a SLIP or PPP interface,
+ * it had to be "up" in order for you to reach your own name server. It
+ * was later decided that since the recommended practice is to always
+ * install local static routes through 127.0.0.1 for all your network
+ * interfaces, that we could solve this problem without a code change.
+ *
+ * The configuration file should always be used, since it is the only way
+ * to specify a default domain. If you are running a server on your local
+ * machine, you should say "nameserver 0.0.0.0" or "nameserver 127.0.0.1"
+ * in the configuration file.
+ *
+ * Return 0 if completes successfully, -1 on error
+ */
+int res_ninit(res_state statp) {
+ return res_vinit(statp, 0);
+}
+
+/* This function has to be reachable by res_data.c but not publicly. */
+int res_vinit(res_state statp, int preinit) {
+ char *cp, **pp;
+ char buf[BUFSIZ];
+ int nserv = 0; /* number of nameserver records read from file */
+ int havesearch = 0;
+ int dots;
+ sockaddr_union u[2];
+
+ if ((statp->options & RES_INIT) != 0U) res_ndestroy(statp);
+
+ if (!preinit) {
+ statp->netid = NETID_UNSET;
+ statp->options = RES_DEFAULT;
+ statp->id = arc4random_uniform(65536);
+ statp->_mark = MARK_UNSET;
+ }
+
+ memset(u, 0, sizeof(u));
+ u[nserv].sin.sin_addr.s_addr = INADDR_ANY;
+ u[nserv].sin.sin_family = AF_INET;
+ u[nserv].sin.sin_port = htons(NAMESERVER_PORT);
+ nserv++;
+ statp->nscount = 0;
+ statp->ndots = 1;
+ statp->_vcsock = -1;
+ statp->_flags = 0;
+ statp->_u._ext.nscount = 0;
+ statp->_u._ext.ext = (res_state_ext*) malloc(sizeof(*statp->_u._ext.ext));
+ statp->use_local_nameserver = false;
+ if (statp->_u._ext.ext != NULL) {
+ memset(statp->_u._ext.ext, 0, sizeof(*statp->_u._ext.ext));
+ statp->_u._ext.ext->nsaddrs[0].sin = statp->nsaddr;
+ strcpy(statp->_u._ext.ext->nsuffix, "ip6.arpa");
+ strcpy(statp->_u._ext.ext->nsuffix2, "ip6.int");
+ }
+ statp->nsort = 0;
+ res_setservers(statp, u, nserv);
+
+ if (statp->defdname[0] == 0 && gethostname(buf, sizeof(statp->defdname) - 1) == 0 &&
+ (cp = strchr(buf, '.')) != NULL)
+ strcpy(statp->defdname, cp + 1);
+
+ /* find components of local domain that might be searched */
+ if (havesearch == 0) {
+ pp = statp->dnsrch;
+ *pp++ = statp->defdname;
+ *pp = NULL;
+
+ dots = 0;
+ for (cp = statp->defdname; *cp; cp++) dots += (*cp == '.');
+
+ cp = statp->defdname;
+ while (pp < statp->dnsrch + MAXDFLSRCH) {
+ if (dots < LOCALDOMAINPARTS) break;
+ cp = strchr(cp, '.') + 1; /* we know there is one */
+ *pp++ = cp;
+ dots--;
+ }
+ *pp = NULL;
+ LOG(DEBUG) << __func__ << ": dnsrch list:";
+ for (pp = statp->dnsrch; *pp; pp++) LOG(DEBUG) << "\t" << *pp;
+ }
+
+ if ((cp = getenv("RES_OPTIONS")) != NULL) res_setoptions(statp, cp, "env");
+ if (nserv > 0) {
+ statp->nscount = nserv;
+ statp->options |= RES_INIT;
+ }
+ return (0);
+}
+
+static void res_setoptions(res_state statp, const char* options, const char* source) {
+ const char* cp = options;
+ int i;
+ res_state_ext* ext = statp->_u._ext.ext;
+
+ LOG(DEBUG) << "res_setoptions(\"" << options << "\", \"" << source << "\")...";
+
+ while (*cp) {
+ /* skip leading and inner runs of spaces */
+ while (*cp == ' ' || *cp == '\t') cp++;
+ /* search for and process individual options */
+ if (!strncmp(cp, "ndots:", sizeof("ndots:") - 1)) {
+ i = atoi(cp + sizeof("ndots:") - 1);
+ if (i <= RES_MAXNDOTS)
+ statp->ndots = i;
+ else
+ statp->ndots = RES_MAXNDOTS;
+ LOG(DEBUG) << "\tndots=" << statp->ndots;
+
+ } else if (!strncmp(cp, "debug", sizeof("debug") - 1)) {
+ if (!(statp->options & RES_DEBUG)) {
+ LOG(DEBUG) << "res_setoptions(\"" << options << "\", \"" << source << "\")..";
+ statp->options |= RES_DEBUG;
+ }
+ LOG(DEBUG) << "\tdebug";
+
+ } else if (!strncmp(cp, "no_tld_query", sizeof("no_tld_query") - 1) ||
+ !strncmp(cp, "no-tld-query", sizeof("no-tld-query") - 1)) {
+ statp->options |= RES_NOTLDQUERY;
+ } else if (!strncmp(cp, "inet6", sizeof("inet6") - 1)) {
+ statp->options |= RES_USE_INET6;
+ } else if (!strncmp(cp, "rotate", sizeof("rotate") - 1)) {
+ statp->options |= RES_ROTATE;
+ } else if (!strncmp(cp, "no-check-names", sizeof("no-check-names") - 1)) {
+ statp->options |= RES_NOCHECKNAME;
+ }
+ else if (!strncmp(cp, "edns0", sizeof("edns0") - 1)) {
+ statp->options |= RES_USE_EDNS0;
+ }
+ else if (!strncmp(cp, "dname", sizeof("dname") - 1)) {
+ statp->options |= RES_USE_DNAME;
+ } else if (!strncmp(cp, "nibble:", sizeof("nibble:") - 1)) {
+ if (ext == NULL) goto skip;
+ cp += sizeof("nibble:") - 1;
+ i = MIN(strcspn(cp, " \t"), sizeof(ext->nsuffix) - 1);
+ strncpy(ext->nsuffix, cp, (size_t) i);
+ ext->nsuffix[i] = '\0';
+ } else if (!strncmp(cp, "nibble2:", sizeof("nibble2:") - 1)) {
+ if (ext == NULL) goto skip;
+ cp += sizeof("nibble2:") - 1;
+ i = MIN(strcspn(cp, " \t"), sizeof(ext->nsuffix2) - 1);
+ strncpy(ext->nsuffix2, cp, (size_t) i);
+ ext->nsuffix2[i] = '\0';
+ } else if (!strncmp(cp, "v6revmode:", sizeof("v6revmode:") - 1)) {
+ cp += sizeof("v6revmode:") - 1;
+ /* "nibble" and "bitstring" used to be valid */
+ if (!strncmp(cp, "single", sizeof("single") - 1)) {
+ statp->options |= RES_NO_NIBBLE2;
+ } else if (!strncmp(cp, "both", sizeof("both") - 1)) {
+ statp->options &= ~RES_NO_NIBBLE2;
+ }
+ } else {
+ /* XXX - print a warning here? */
+ }
+ skip:
+ /* skip to next run of spaces */
+ while (*cp && *cp != ' ' && *cp != '\t') cp++;
+ }
+}
+
+/*
+ * This routine is for closing the socket if a virtual circuit is used and
+ * the program wants to close it. This provides support for endhostent()
+ * which expects to close the socket.
+ *
+ * This routine is not expected to be user visible.
+ */
+void res_nclose(res_state statp) {
+ int ns;
+
+ if (statp->_vcsock >= 0) {
+ (void) close(statp->_vcsock);
+ statp->_vcsock = -1;
+ statp->_flags &= ~RES_F_VC;
+ }
+ for (ns = 0; ns < statp->_u._ext.nscount; ns++) {
+ if (statp->_u._ext.nssocks[ns] != -1) {
+ (void) close(statp->_u._ext.nssocks[ns]);
+ statp->_u._ext.nssocks[ns] = -1;
+ }
+ }
+}
+
+void res_ndestroy(res_state statp) {
+ res_nclose(statp);
+ if (statp->_u._ext.ext != NULL) free(statp->_u._ext.ext);
+ statp->options &= ~RES_INIT;
+ statp->_u._ext.ext = NULL;
+}
+
+void res_setservers(res_state statp, const sockaddr_union* set, int cnt) {
+ int i, nserv;
+ size_t size;
+
+ /* close open servers */
+ res_nclose(statp);
+
+ /* cause rtt times to be forgotten */
+ statp->_u._ext.nscount = 0;
+
+ nserv = 0;
+ for (i = 0; i < cnt && nserv < MAXNS; i++) {
+ switch (set->sin.sin_family) {
+ case AF_INET:
+ size = sizeof(set->sin);
+ if (statp->_u._ext.ext)
+ memcpy(&statp->_u._ext.ext->nsaddrs[nserv], &set->sin, size);
+ if (size <= sizeof(statp->nsaddr_list[nserv]))
+ memcpy(&statp->nsaddr_list[nserv], &set->sin, size);
+ else
+ statp->nsaddr_list[nserv].sin_family = 0;
+ nserv++;
+ break;
+
+#ifdef HAS_INET6_STRUCTS
+ case AF_INET6:
+ size = sizeof(set->sin6);
+ if (statp->_u._ext.ext)
+ memcpy(&statp->_u._ext.ext->nsaddrs[nserv], &set->sin6, size);
+ if (size <= sizeof(statp->nsaddr_list[nserv]))
+ memcpy(&statp->nsaddr_list[nserv], &set->sin6, size);
+ else
+ statp->nsaddr_list[nserv].sin_family = 0;
+ nserv++;
+ break;
+#endif
+
+ default:
+ break;
+ }
+ set++;
+ }
+ statp->nscount = nserv;
+}
+
+int res_getservers(res_state statp, sockaddr_union* set, int cnt) {
+ int i;
+ size_t size;
+ uint16_t family;
+
+ for (i = 0; i < statp->nscount && i < cnt; i++) {
+ if (statp->_u._ext.ext)
+ family = statp->_u._ext.ext->nsaddrs[i].sin.sin_family;
+ else
+ family = statp->nsaddr_list[i].sin_family;
+
+ switch (family) {
+ case AF_INET:
+ size = sizeof(set->sin);
+ if (statp->_u._ext.ext)
+ memcpy(&set->sin, &statp->_u._ext.ext->nsaddrs[i], size);
+ else
+ memcpy(&set->sin, &statp->nsaddr_list[i], size);
+ break;
+
+#ifdef HAS_INET6_STRUCTS
+ case AF_INET6:
+ size = sizeof(set->sin6);
+ if (statp->_u._ext.ext)
+ memcpy(&set->sin6, &statp->_u._ext.ext->nsaddrs[i], size);
+ else
+ memcpy(&set->sin6, &statp->nsaddr_list[i], size);
+ break;
+#endif
+
+ default:
+ set->sin.sin_family = 0;
+ break;
+ }
+ set++;
+ }
+ return (statp->nscount);
+}
+
+void res_setnetcontext(res_state statp, const struct android_net_context* netcontext,
+ android::net::NetworkDnsEventReported* _Nonnull event) {
+ if (statp != NULL) {
+ statp->netid = netcontext->dns_netid;
+ statp->_mark = netcontext->dns_mark;
+ if (netcontext->flags & NET_CONTEXT_FLAG_USE_EDNS) {
+ statp->options |= RES_USE_EDNS0 | RES_USE_DNSSEC;
+ }
+ if (netcontext->flags & NET_CONTEXT_FLAG_USE_LOCAL_NAMESERVERS) {
+ statp->use_local_nameserver = true;
+ }
+ statp->event = event;
+ }
+}
diff --git a/resolv/res_mkquery.cpp b/resolv/res_mkquery.cpp
new file mode 100644
index 0000000..2371c7e
--- /dev/null
+++ b/resolv/res_mkquery.cpp
@@ -0,0 +1,250 @@
+/* $NetBSD: res_mkquery.c,v 1.6 2006/01/24 17:40:32 christos Exp $ */
+
+/*
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Portions Copyright (c) 1993 by Digital Equipment Corporation.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies, and that
+ * the name of Digital Equipment Corporation not be used in advertising or
+ * publicity pertaining to distribution of the document or software without
+ * specific, written prior permission.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
+ * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ */
+
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Portions Copyright (c) 1996-1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define LOG_TAG "res_mkquery"
+
+#include <algorithm> // std::min()
+
+#include <arpa/nameser.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include <android-base/logging.h>
+
+#include "resolv_private.h"
+
+// Queries will be padded to a multiple of this length when EDNS0 is active.
+constexpr uint16_t kEdns0Padding = 128;
+
+extern const char* const _res_opcodes[] = {
+ "QUERY", "IQUERY", "CQUERYM", "CQUERYU", /* experimental */
+ "NOTIFY", /* experimental */
+ "UPDATE", "6", "7", "8", "9", "10",
+ "11", "12", "13", "ZONEINIT", "ZONEREF",
+};
+
+/*
+ * Form all types of queries.
+ * Returns the size of the result or -1.
+ */
+int res_nmkquery(res_state statp, int op, /* opcode of query */
+ const char* dname, /* domain name */
+ int cl, int type, /* class and type of query */
+ const u_char* data, /* resource record data */
+ int datalen, /* length of data */
+ const u_char* /*newrr_in*/, /* new rr for modify or append */
+ u_char* buf, /* buffer to put query */
+ int buflen) /* size of buffer */
+{
+ HEADER* hp;
+ u_char *cp, *ep;
+ int n;
+ u_char *dnptrs[20], **dpp, **lastdnptr;
+
+ LOG(DEBUG) << __func__ << ": (" << _res_opcodes[op] << ", " << p_class(cl) << ", "
+ << p_type(type) << ")";
+
+ /*
+ * Initialize header fields.
+ */
+ if ((buf == NULL) || (buflen < HFIXEDSZ)) return (-1);
+ memset(buf, 0, HFIXEDSZ);
+ hp = (HEADER*) (void*) buf;
+ hp->id = htons(arc4random_uniform(65536));
+ hp->opcode = op;
+ hp->rd = (statp->options & RES_RECURSE) != 0U;
+ hp->ad = (statp->options & RES_USE_DNSSEC) != 0U;
+ hp->rcode = NOERROR;
+ cp = buf + HFIXEDSZ;
+ ep = buf + buflen;
+ dpp = dnptrs;
+ *dpp++ = buf;
+ *dpp++ = NULL;
+ lastdnptr = dnptrs + sizeof dnptrs / sizeof dnptrs[0];
+ /*
+ * perform opcode specific processing
+ */
+ switch (op) {
+ case QUERY:
+ [[fallthrough]];
+ case NS_NOTIFY_OP:
+ if (ep - cp < QFIXEDSZ) return (-1);
+ if ((n = dn_comp(dname, cp, ep - cp - QFIXEDSZ, dnptrs, lastdnptr)) < 0) return (-1);
+ cp += n;
+ *reinterpret_cast<uint16_t*>(cp) = htons(type);
+ cp += INT16SZ;
+ *reinterpret_cast<uint16_t*>(cp) = htons(cl);
+ cp += INT16SZ;
+ hp->qdcount = htons(1);
+ if (op == QUERY || data == NULL) break;
+ /*
+ * Make an additional record for completion domain.
+ */
+ if ((ep - cp) < RRFIXEDSZ) return (-1);
+ n = dn_comp((const char*) data, cp, ep - cp - RRFIXEDSZ, dnptrs, lastdnptr);
+ if (n < 0) return (-1);
+ cp += n;
+ *reinterpret_cast<uint16_t*>(cp) = htons(ns_t_null);
+ cp += INT16SZ;
+ *reinterpret_cast<uint16_t*>(cp) = htons(cl);
+ cp += INT16SZ;
+ *reinterpret_cast<uint32_t*>(cp) = htonl(0);
+ cp += INT32SZ;
+ *reinterpret_cast<uint16_t*>(cp) = htons(0);
+ cp += INT16SZ;
+ hp->arcount = htons(1);
+ break;
+
+ case IQUERY:
+ /*
+ * Initialize answer section
+ */
+ if (ep - cp < 1 + RRFIXEDSZ + datalen) return (-1);
+ *cp++ = '\0'; /* no domain name */
+ *reinterpret_cast<uint16_t*>(cp) = htons(type);
+ cp += INT16SZ;
+ *reinterpret_cast<uint16_t*>(cp) = htons(cl);
+ cp += INT16SZ;
+ *reinterpret_cast<uint32_t*>(cp) = htonl(0);
+ cp += INT32SZ;
+ *reinterpret_cast<uint16_t*>(cp) = htons(datalen);
+ cp += INT16SZ;
+ if (datalen) {
+ memcpy(cp, data, (size_t) datalen);
+ cp += datalen;
+ }
+ hp->ancount = htons(1);
+ break;
+
+ default:
+ return (-1);
+ }
+ return (cp - buf);
+}
+
+int res_nopt(res_state statp, int n0, /* current offset in buffer */
+ u_char* buf, /* buffer to put query */
+ int buflen, /* size of buffer */
+ int anslen) /* UDP answer buffer size */
+{
+ HEADER* hp;
+ u_char *cp, *ep;
+ u_int16_t flags = 0;
+
+ LOG(DEBUG) << __func__;
+
+ hp = (HEADER*) (void*) buf;
+ cp = buf + n0;
+ ep = buf + buflen;
+
+ if ((ep - cp) < 1 + RRFIXEDSZ) return (-1);
+
+ *cp++ = 0; /* "." */
+
+ // Attach OPT pseudo-RR, as documented in RFC2671 (EDNS0).
+ *reinterpret_cast<uint16_t*>(cp) = htons(ns_t_opt); /* TYPE */
+ cp += INT16SZ;
+ if (anslen > 0xffff) anslen = 0xffff;
+ *reinterpret_cast<uint16_t*>(cp) = htons(anslen); /* CLASS = UDP payload size */
+ cp += INT16SZ;
+ *cp++ = NOERROR; /* extended RCODE */
+ *cp++ = 0; /* EDNS version */
+ if (statp->options & RES_USE_DNSSEC) {
+ LOG(DEBUG) << __func__ << ": ENDS0 DNSSEC";
+ flags |= NS_OPT_DNSSEC_OK;
+ }
+ *reinterpret_cast<uint16_t*>(cp) = htons(flags);
+ cp += INT16SZ;
+
+ // EDNS0 padding
+ const uint16_t minlen = static_cast<uint16_t>(cp - buf) + 3 * INT16SZ;
+ const uint16_t extra = minlen % kEdns0Padding;
+ uint16_t padlen = (kEdns0Padding - extra) % kEdns0Padding;
+ if (minlen > buflen) {
+ return -1;
+ }
+ padlen = std::min(padlen, static_cast<uint16_t>(buflen - minlen));
+ *reinterpret_cast<uint16_t*>(cp) = htons(padlen + 2 * INT16SZ); /* RDLEN */
+ cp += INT16SZ;
+ *reinterpret_cast<uint16_t*>(cp) = htons(NS_OPT_PADDING); /* OPTION-CODE */
+ cp += INT16SZ;
+ *reinterpret_cast<uint16_t*>(cp) = htons(padlen); /* OPTION-LENGTH */
+ cp += INT16SZ;
+ memset(cp, 0, padlen);
+ cp += padlen;
+
+ hp->arcount = htons(ntohs(hp->arcount) + 1);
+ return (cp - buf);
+}
diff --git a/resolv/res_query.cpp b/resolv/res_query.cpp
new file mode 100644
index 0000000..fbbcc31
--- /dev/null
+++ b/resolv/res_query.cpp
@@ -0,0 +1,380 @@
+/* $NetBSD: res_query.c,v 1.7 2006/01/24 17:41:25 christos Exp $ */
+
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Portions Copyright (c) 1993 by Digital Equipment Corporation.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies, and that
+ * the name of Digital Equipment Corporation not be used in advertising or
+ * publicity pertaining to distribution of the document or software without
+ * specific, written prior permission.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
+ * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ */
+
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Portions Copyright (c) 1996-1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define LOG_TAG "res_query"
+
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+#include <ctype.h>
+#include <errno.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <android-base/logging.h>
+
+#include "resolv_cache.h"
+#include "resolv_private.h"
+
+#if PACKETSZ > 1024
+#define MAXPACKET PACKETSZ
+#else
+#define MAXPACKET 1024
+#endif
+
+/*
+ * Formulate a normal query, send, and await answer.
+ * Returned answer is placed in supplied buffer "answer".
+ * Perform preliminary check of answer, returning success only
+ * if no error is indicated and the answer count is nonzero.
+ * Return the size of the response on success, -1 on error.
+ * Error number is left in *herrno.
+ *
+ * Caller must parse answer and determine whether it answers the question.
+ */
+int res_nquery(res_state statp, const char* name, // domain name
+ int cl, int type, // class and type of query
+ u_char* answer, // buffer to put answer
+ int anslen, // size of answer buffer
+ int* herrno) // legacy and extended h_errno
+ // NETD_RESOLV_H_ERRNO_EXT_*
+{
+ u_char buf[MAXPACKET];
+ HEADER* hp = (HEADER*) (void*) answer;
+ int n;
+ int rcode = NOERROR;
+ bool retried = false;
+
+again:
+ hp->rcode = NOERROR; /* default */
+
+ LOG(DEBUG) << __func__ << ": (" << cl << ", " << type << ")";
+
+ n = res_nmkquery(statp, QUERY, name, cl, type, NULL, 0, NULL, buf, sizeof(buf));
+ if (n > 0 && (statp->options & (RES_USE_EDNS0 | RES_USE_DNSSEC)) != 0U && !retried)
+ n = res_nopt(statp, n, buf, sizeof(buf), anslen);
+ if (n <= 0) {
+ LOG(DEBUG) << __func__ << ": mkquery failed";
+ *herrno = NO_RECOVERY;
+ return n;
+ }
+ n = res_nsend(statp, buf, n, answer, anslen, &rcode, 0);
+ if (n < 0) {
+ /* if the query choked with EDNS0, retry without EDNS0 */
+ if ((statp->options & (RES_USE_EDNS0 | RES_USE_DNSSEC)) != 0U &&
+ (statp->_flags & RES_F_EDNS0ERR) && !retried) {
+ LOG(DEBUG) << __func__ << ": retry without EDNS0";
+ retried = true;
+ goto again;
+ }
+ LOG(DEBUG) << __func__ << ": send error";
+
+ // Note that rcodes SERVFAIL, NOTIMP, REFUSED may cause res_nquery() to return a general
+ // error code EAI_AGAIN, but mapping the error code from rcode as res_queryN() does for
+ // getaddrinfo(). Different rcodes trigger different behaviors:
+ //
+ // - SERVFAIL, NOTIMP, REFUSED
+ // These result in send_dg() returning 0, causing res_nsend() to try the next
+ // nameserver. After all nameservers failed, res_nsend() returns -ETIMEDOUT, causing
+ // res_nquery() to return EAI_AGAIN here regardless of the rcode from the DNS response.
+ //
+ // - NXDOMAIN, FORMERR
+ // These rcodes may cause res_nsend() to return successfully (i.e. the result is a
+ // positive integer). In this case, res_nquery() returns error number by referring
+ // the rcode from the DNS response.
+ switch (rcode) {
+ case RCODE_TIMEOUT: // Not defined in RFC.
+ // DNS metrics monitors DNS query timeout.
+ *herrno = NETD_RESOLV_H_ERRNO_EXT_TIMEOUT; // extended h_errno.
+ break;
+ default:
+ *herrno = TRY_AGAIN;
+ break;
+ }
+ return n;
+ }
+
+ if (hp->rcode != NOERROR || ntohs(hp->ancount) == 0) {
+ LOG(DEBUG) << __func__ << ": rcode = (" << p_rcode(hp->rcode)
+ << "), counts = an:" << ntohs(hp->ancount) << " ns:" << ntohs(hp->nscount)
+ << " ar:" << ntohs(hp->arcount);
+
+ switch (hp->rcode) {
+ case NXDOMAIN:
+ *herrno = HOST_NOT_FOUND;
+ break;
+ case SERVFAIL:
+ *herrno = TRY_AGAIN;
+ break;
+ case NOERROR:
+ *herrno = NO_DATA;
+ break;
+ case FORMERR:
+ case NOTIMP:
+ case REFUSED:
+ default:
+ *herrno = NO_RECOVERY;
+ break;
+ }
+ return -1;
+ }
+ return n;
+}
+
+/*
+ * Formulate a normal query, send, and retrieve answer in supplied buffer.
+ * Return the size of the response on success, -1 on error.
+ * If enabled, implement search rules until answer or unrecoverable failure
+ * is detected. Error code, if any, is left in *herrno.
+ */
+int res_nsearch(res_state statp, const char* name, /* domain name */
+ int cl, int type, /* class and type of query */
+ u_char* answer, /* buffer to put answer */
+ int anslen, /* size of answer */
+ int* herrno) /* legacy and extended
+ h_errno NETD_RESOLV_H_ERRNO_EXT_* */
+{
+ const char *cp, *const *domain;
+ HEADER* hp = (HEADER*) (void*) answer;
+ u_int dots;
+ int trailing_dot, ret, saved_herrno;
+ int got_nodata = 0, got_servfail = 0, root_on_list = 0;
+ int tried_as_is = 0;
+ int searched = 0;
+
+ errno = 0;
+ *herrno = HOST_NOT_FOUND; /* True if we never query. */
+
+ dots = 0;
+ for (cp = name; *cp != '\0'; cp++) dots += (*cp == '.');
+ trailing_dot = 0;
+ if (cp > name && *--cp == '.') trailing_dot++;
+
+ /*
+ * If there are enough dots in the name, let's just give it a
+ * try 'as is'. The threshold can be set with the "ndots" option.
+ * Also, query 'as is', if there is a trailing dot in the name.
+ */
+ saved_herrno = -1;
+ if (dots >= statp->ndots || trailing_dot) {
+ ret = res_nquerydomain(statp, name, NULL, cl, type, answer, anslen, herrno);
+ if (ret > 0 || trailing_dot) return ret;
+ saved_herrno = *herrno;
+ tried_as_is++;
+ }
+
+ /*
+ * We do at least one level of search if
+ * - there is no dot and RES_DEFNAME is set, or
+ * - there is at least one dot, there is no trailing dot,
+ * and RES_DNSRCH is set.
+ */
+ if ((!dots && (statp->options & RES_DEFNAMES) != 0U) ||
+ (dots && !trailing_dot && (statp->options & RES_DNSRCH) != 0U)) {
+ int done = 0;
+
+ /* Unfortunately we need to load network-specific info
+ * (dns servers, search domains) before
+ * the domain stuff is tried. Will have a better
+ * fix after thread pools are used as this will
+ * be loaded once for the thread instead of each
+ * time a query is tried.
+ */
+ _resolv_populate_res_for_net(statp);
+
+ for (domain = (const char* const*) statp->dnsrch; *domain && !done; domain++) {
+ searched = 1;
+
+ if (domain[0][0] == '\0' || (domain[0][0] == '.' && domain[0][1] == '\0'))
+ root_on_list++;
+
+ ret = res_nquerydomain(statp, name, *domain, cl, type, answer, anslen, herrno);
+ if (ret > 0) return ret;
+
+ /*
+ * If no server present, give up.
+ * If name isn't found in this domain,
+ * keep trying higher domains in the search list
+ * (if that's enabled).
+ * On a NO_DATA error, keep trying, otherwise
+ * a wildcard entry of another type could keep us
+ * from finding this entry higher in the domain.
+ * If we get some other error (negative answer or
+ * server failure), then stop searching up,
+ * but try the input name below in case it's
+ * fully-qualified.
+ */
+ if (errno == ECONNREFUSED) {
+ *herrno = TRY_AGAIN;
+ return -1;
+ }
+
+ switch (*herrno) {
+ case NO_DATA:
+ got_nodata++;
+ break;
+ case HOST_NOT_FOUND:
+ /* keep trying */
+ break;
+ case TRY_AGAIN:
+ if (hp->rcode == SERVFAIL) {
+ /* try next search element, if any */
+ got_servfail++;
+ break;
+ }
+ [[fallthrough]];
+ default:
+ /* anything else implies that we're done */
+ done++;
+ }
+
+ /* if we got here for some reason other than DNSRCH,
+ * we only wanted one iteration of the loop, so stop.
+ */
+ if ((statp->options & RES_DNSRCH) == 0U) done++;
+ }
+ }
+
+ /*
+ * If the query has not already been tried as is then try it
+ * unless RES_NOTLDQUERY is set and there were no dots.
+ */
+ if ((dots || !searched || (statp->options & RES_NOTLDQUERY) == 0U) &&
+ !(tried_as_is || root_on_list)) {
+ ret = res_nquerydomain(statp, name, NULL, cl, type, answer, anslen, herrno);
+ if (ret > 0) return ret;
+ }
+
+ /* if we got here, we didn't satisfy the search.
+ * if we did an initial full query, return that query's H_ERRNO
+ * (note that we wouldn't be here if that query had succeeded).
+ * else if we ever got a nodata, send that back as the reason.
+ * else send back meaningless H_ERRNO, that being the one from
+ * the last DNSRCH we did.
+ */
+ if (saved_herrno != -1)
+ *herrno = saved_herrno;
+ else if (got_nodata)
+ *herrno = NO_DATA;
+ else if (got_servfail)
+ *herrno = TRY_AGAIN;
+ return -1;
+}
+
+/*
+ * Perform a call on res_query on the concatenation of name and domain,
+ * removing a trailing dot from name if domain is NULL.
+ */
+int res_nquerydomain(res_state statp, const char* name, const char* domain, int cl,
+ int type, /* class and type of query */
+ u_char* answer, /* buffer to put answer */
+ int anslen, /* size of answer */
+ int* herrno) /* legacy and extended h_errno NETD_RESOLV_H_ERRNO_EXT_* */
+{
+ char nbuf[MAXDNAME];
+ const char* longname = nbuf;
+ int n, d;
+
+ if (domain == NULL) {
+ LOG(DEBUG) << __func__ << ": (null, " << cl << ", " << type << ")";
+ /*
+ * Check for trailing '.';
+ * copy without '.' if present.
+ */
+ n = strlen(name);
+ if (n >= MAXDNAME) {
+ *herrno = NO_RECOVERY;
+ return -1;
+ }
+ n--;
+ if (n >= 0 && name[n] == '.') {
+ strncpy(nbuf, name, (size_t) n);
+ nbuf[n] = '\0';
+ } else
+ longname = name;
+ } else {
+ LOG(DEBUG) << __func__ << ": (" << cl << ", " << type << ")";
+ n = strlen(name);
+ d = strlen(domain);
+ if (n + d + 1 >= MAXDNAME) {
+ *herrno = NO_RECOVERY;
+ return -1;
+ }
+ snprintf(nbuf, sizeof(nbuf), "%s.%s", name, domain);
+ }
+ return res_nquery(statp, longname, cl, type, answer, anslen, herrno);
+}
diff --git a/resolv/res_send.cpp b/resolv/res_send.cpp
new file mode 100644
index 0000000..1ccfc74
--- /dev/null
+++ b/resolv/res_send.cpp
@@ -0,0 +1,1372 @@
+/* $NetBSD: res_send.c,v 1.9 2006/01/24 17:41:25 christos Exp $ */
+
+/*
+ * Copyright (c) 1985, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Portions Copyright (c) 1993 by Digital Equipment Corporation.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies, and that
+ * the name of Digital Equipment Corporation not be used in advertising or
+ * publicity pertaining to distribution of the document or software without
+ * specific, written prior permission.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
+ * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ */
+
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Portions Copyright (c) 1996-1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Send query to name server and wait for reply.
+ */
+
+#define LOG_TAG "res_send"
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+#include <netinet/in.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <android-base/logging.h>
+#include <android/multinetwork.h> // ResNsendFlags
+
+#include <netdutils/Slice.h>
+#include <netdutils/Stopwatch.h>
+#include "DnsTlsDispatcher.h"
+#include "DnsTlsTransport.h"
+#include "PrivateDnsConfiguration.h"
+#include "netd_resolv/resolv.h"
+#include "netd_resolv/stats.h"
+#include "private/android_filesystem_config.h"
+#include "res_state_ext.h"
+#include "resolv_cache.h"
+#include "resolv_private.h"
+#include "stats.pb.h"
+
+// TODO: use the namespace something like android::netd_resolv for libnetd_resolv
+using namespace android::net;
+using android::net::NetworkDnsEventReported;
+using android::netdutils::Slice;
+using android::netdutils::Stopwatch;
+
+static DnsTlsDispatcher sDnsTlsDispatcher;
+
+static int get_salen(const struct sockaddr*);
+static struct sockaddr* get_nsaddr(res_state, size_t);
+static int send_vc(res_state, res_params* params, const u_char*, int, u_char*, int, int*, int,
+ time_t*, int*, int*);
+static int send_dg(res_state, res_params* params, const u_char*, int, u_char*, int, int*, int, int*,
+ int*, time_t*, int*, int*);
+static void Aerror(const res_state, const char*, int, const struct sockaddr*, int);
+static void Perror(const res_state, const char*, int);
+
+static int sock_eq(struct sockaddr*, struct sockaddr*);
+static int connect_with_timeout(int sock, const struct sockaddr* nsap, socklen_t salen,
+ const struct timespec timeout);
+static int retrying_poll(const int sock, short events, const struct timespec* finish);
+static int res_tls_send(res_state, const Slice query, const Slice answer, int* rcode,
+ bool* fallback);
+
+NsType getQueryType(const uint8_t* msg, size_t msgLen) {
+ ns_msg handle;
+ ns_rr rr;
+ if (ns_initparse((const uint8_t*)msg, msgLen, &handle) < 0 ||
+ ns_parserr(&handle, ns_s_qd, 0, &rr) < 0) {
+ return NS_T_INVALID;
+ }
+ return static_cast<NsType>(ns_rr_type(rr));
+}
+
+IpVersion ipFamilyToIPVersion(const int ipFamily) {
+ switch (ipFamily) {
+ case AF_INET:
+ return IV_IPV4;
+ case AF_INET6:
+ return IV_IPV6;
+ default:
+ return IV_UNKNOWN;
+ }
+}
+
+// BEGIN: Code copied from ISC eventlib
+// TODO: move away from this code
+#define BILLION 1000000000
+
+static struct timespec evConsTime(time_t sec, long nsec) {
+ struct timespec x;
+
+ x.tv_sec = sec;
+ x.tv_nsec = nsec;
+ return (x);
+}
+
+static struct timespec evAddTime(struct timespec addend1, struct timespec addend2) {
+ struct timespec x;
+
+ x.tv_sec = addend1.tv_sec + addend2.tv_sec;
+ x.tv_nsec = addend1.tv_nsec + addend2.tv_nsec;
+ if (x.tv_nsec >= BILLION) {
+ x.tv_sec++;
+ x.tv_nsec -= BILLION;
+ }
+ return (x);
+}
+
+static struct timespec evSubTime(struct timespec minuend, struct timespec subtrahend) {
+ struct timespec x;
+
+ x.tv_sec = minuend.tv_sec - subtrahend.tv_sec;
+ if (minuend.tv_nsec >= subtrahend.tv_nsec)
+ x.tv_nsec = minuend.tv_nsec - subtrahend.tv_nsec;
+ else {
+ x.tv_nsec = BILLION - subtrahend.tv_nsec + minuend.tv_nsec;
+ x.tv_sec--;
+ }
+ return (x);
+}
+
+static int evCmpTime(struct timespec a, struct timespec b) {
+#define SGN(x) ((x) < 0 ? (-1) : (x) > 0 ? (1) : (0));
+ time_t s = a.tv_sec - b.tv_sec;
+ long n;
+
+ if (s != 0) return SGN(s);
+
+ n = a.tv_nsec - b.tv_nsec;
+ return SGN(n);
+}
+
+static struct timespec evNowTime(void) {
+ struct timespec tsnow;
+ clock_gettime(CLOCK_REALTIME, &tsnow);
+ return tsnow;
+}
+
+static struct iovec evConsIovec(void* buf, size_t cnt) {
+ struct iovec ret;
+
+ memset(&ret, 0xf5, sizeof ret);
+ ret.iov_base = buf;
+ ret.iov_len = cnt;
+ return ret;
+}
+
+// END: Code copied from ISC eventlib
+
+/* BIONIC-BEGIN: implement source port randomization */
+static int random_bind(int s, int family) {
+ sockaddr_union u;
+ int j;
+ socklen_t slen;
+
+ /* clear all, this also sets the IP4/6 address to 'any' */
+ memset(&u, 0, sizeof u);
+
+ switch (family) {
+ case AF_INET:
+ u.sin.sin_family = family;
+ slen = sizeof u.sin;
+ break;
+ case AF_INET6:
+ u.sin6.sin6_family = family;
+ slen = sizeof u.sin6;
+ break;
+ default:
+ errno = EPROTO;
+ return -1;
+ }
+
+ /* first try to bind to a random source port a few times */
+ for (j = 0; j < 10; j++) {
+ /* find a random port between 1025 .. 65534 */
+ int port = 1025 + (arc4random_uniform(65535 - 1025));
+ if (family == AF_INET)
+ u.sin.sin_port = htons(port);
+ else
+ u.sin6.sin6_port = htons(port);
+
+ if (!bind(s, &u.sa, slen)) return 0;
+ }
+
+ // nothing after 10 attempts, our network table is probably busy
+ // let the system decide which port is best
+ if (family == AF_INET)
+ u.sin.sin_port = 0;
+ else
+ u.sin6.sin6_port = 0;
+
+ return bind(s, &u.sa, slen);
+}
+/* BIONIC-END */
+
+// Disables all nameservers other than selectedServer
+static void res_set_usable_server(int selectedServer, int nscount, bool usable_servers[]) {
+ int usableIndex = 0;
+ for (int ns = 0; ns < nscount; ns++) {
+ if (usable_servers[ns]) ++usableIndex;
+ if (usableIndex != selectedServer) usable_servers[ns] = false;
+ }
+}
+
+/* int
+ * res_isourserver(ina)
+ * looks up "ina" in _res.ns_addr_list[]
+ * returns:
+ * 0 : not found
+ * >0 : found
+ * author:
+ * paul vixie, 29may94
+ */
+static int res_ourserver_p(const res_state statp, const sockaddr* sa) {
+ const sockaddr_in *inp, *srv;
+ const sockaddr_in6 *in6p, *srv6;
+ int ns;
+
+ switch (sa->sa_family) {
+ case AF_INET:
+ inp = (const struct sockaddr_in*) (const void*) sa;
+ for (ns = 0; ns < statp->nscount; ns++) {
+ srv = (struct sockaddr_in*) (void*) get_nsaddr(statp, (size_t) ns);
+ if (srv->sin_family == inp->sin_family && srv->sin_port == inp->sin_port &&
+ (srv->sin_addr.s_addr == INADDR_ANY ||
+ srv->sin_addr.s_addr == inp->sin_addr.s_addr))
+ return 1;
+ }
+ break;
+ case AF_INET6:
+ if (statp->_u._ext.ext == NULL) break;
+ in6p = (const struct sockaddr_in6*) (const void*) sa;
+ for (ns = 0; ns < statp->nscount; ns++) {
+ srv6 = (struct sockaddr_in6*) (void*) get_nsaddr(statp, (size_t) ns);
+ if (srv6->sin6_family == in6p->sin6_family && srv6->sin6_port == in6p->sin6_port &&
+#ifdef HAVE_SIN6_SCOPE_ID
+ (srv6->sin6_scope_id == 0 || srv6->sin6_scope_id == in6p->sin6_scope_id) &&
+#endif
+ (IN6_IS_ADDR_UNSPECIFIED(&srv6->sin6_addr) ||
+ IN6_ARE_ADDR_EQUAL(&srv6->sin6_addr, &in6p->sin6_addr)))
+ return 1;
+ }
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+/* int
+ * res_nameinquery(name, type, cl, buf, eom)
+ * look for (name, type, cl) in the query section of packet (buf, eom)
+ * requires:
+ * buf + HFIXEDSZ <= eom
+ * returns:
+ * -1 : format error
+ * 0 : not found
+ * >0 : found
+ * author:
+ * paul vixie, 29may94
+ */
+int res_nameinquery(const char* name, int type, int cl, const u_char* buf, const u_char* eom) {
+ const u_char* cp = buf + HFIXEDSZ;
+ int qdcount = ntohs(((const HEADER*) (const void*) buf)->qdcount);
+
+ while (qdcount-- > 0) {
+ char tname[MAXDNAME + 1];
+ int n = dn_expand(buf, eom, cp, tname, sizeof tname);
+ if (n < 0) return (-1);
+ cp += n;
+ if (cp + 2 * INT16SZ > eom) return (-1);
+ int ttype = ntohs(*reinterpret_cast<const uint16_t*>(cp));
+ cp += INT16SZ;
+ int tclass = ntohs(*reinterpret_cast<const uint16_t*>(cp));
+ cp += INT16SZ;
+ if (ttype == type && tclass == cl && ns_samename(tname, name) == 1) return (1);
+ }
+ return (0);
+}
+
+/* int
+ * res_queriesmatch(buf1, eom1, buf2, eom2)
+ * is there a 1:1 mapping of (name,type,class)
+ * in (buf1,eom1) and (buf2,eom2)?
+ * returns:
+ * -1 : format error
+ * 0 : not a 1:1 mapping
+ * >0 : is a 1:1 mapping
+ * author:
+ * paul vixie, 29may94
+ */
+int res_queriesmatch(const u_char* buf1, const u_char* eom1, const u_char* buf2,
+ const u_char* eom2) {
+ const u_char* cp = buf1 + HFIXEDSZ;
+ int qdcount = ntohs(((const HEADER*) (const void*) buf1)->qdcount);
+
+ if (buf1 + HFIXEDSZ > eom1 || buf2 + HFIXEDSZ > eom2) return (-1);
+
+ /*
+ * Only header section present in replies to
+ * dynamic update packets.
+ */
+ if ((((const HEADER*) (const void*) buf1)->opcode == ns_o_update) &&
+ (((const HEADER*) (const void*) buf2)->opcode == ns_o_update))
+ return (1);
+
+ if (qdcount != ntohs(((const HEADER*) (const void*) buf2)->qdcount)) return (0);
+ while (qdcount-- > 0) {
+ char tname[MAXDNAME + 1];
+ int n = dn_expand(buf1, eom1, cp, tname, sizeof tname);
+ if (n < 0) return (-1);
+ cp += n;
+ if (cp + 2 * INT16SZ > eom1) return (-1);
+ int ttype = ntohs(*reinterpret_cast<const uint16_t*>(cp));
+ cp += INT16SZ;
+ int tclass = ntohs(*reinterpret_cast<const uint16_t*>(cp));
+ cp += INT16SZ;
+ if (!res_nameinquery(tname, ttype, tclass, buf2, eom2)) return (0);
+ }
+ return (1);
+}
+
+static DnsQueryEvent* addDnsQueryEvent(NetworkDnsEventReported* event) {
+ return event->mutable_dns_query_events()->add_dns_query_event();
+}
+
+int res_nsend(res_state statp, const u_char* buf, int buflen, u_char* ans, int anssiz, int* rcode,
+ uint32_t flags) {
+ int gotsomewhere, terrno, v_circuit, resplen, n;
+ ResolvCacheStatus cache_status = RESOLV_CACHE_UNSUPPORTED;
+
+ if (anssiz < HFIXEDSZ) {
+ // TODO: Remove errno once callers stop using it
+ errno = EINVAL;
+ return -EINVAL;
+ }
+ LOG(DEBUG) << __func__;
+ res_pquery(buf, buflen);
+
+ v_circuit = (statp->options & RES_USEVC) || buflen > PACKETSZ;
+ gotsomewhere = 0;
+ terrno = ETIMEDOUT;
+
+ int anslen = 0;
+ Stopwatch cache_stopwatch;
+ cache_status = _resolv_cache_lookup(statp->netid, buf, buflen, ans, anssiz, &anslen, flags);
+ const int32_t cacheLatencyUs = saturate_cast<int32_t>(cache_stopwatch.timeTakenUs());
+ if (cache_status == RESOLV_CACHE_FOUND) {
+ HEADER* hp = (HEADER*)(void*)ans;
+ *rcode = hp->rcode;
+ DnsQueryEvent* dnsQueryEvent = addDnsQueryEvent(statp->event);
+ dnsQueryEvent->set_latency_micros(cacheLatencyUs);
+ dnsQueryEvent->set_cache_hit(static_cast<CacheStatus>(cache_status));
+ return anslen;
+ } else if (cache_status != RESOLV_CACHE_UNSUPPORTED) {
+ // had a cache miss for a known network, so populate the thread private
+ // data so the normal resolve path can do its thing
+ _resolv_populate_res_for_net(statp);
+ }
+ if (statp->nscount == 0) {
+ // We have no nameservers configured, so there's no point trying.
+ // Tell the cache the query failed, or any retries and anyone else asking the same
+ // question will block for PENDING_REQUEST_TIMEOUT seconds instead of failing fast.
+ _resolv_cache_query_failed(statp->netid, buf, buflen, flags);
+
+ // TODO: Remove errno once callers stop using it
+ errno = ESRCH;
+ return -ESRCH;
+ }
+
+ /*
+ * If the ns_addr_list in the resolver context has changed, then
+ * invalidate our cached copy and the associated timing data.
+ */
+ if (statp->_u._ext.nscount != 0) {
+ int needclose = 0;
+ struct sockaddr_storage peer;
+ socklen_t peerlen;
+
+ if (statp->_u._ext.nscount != statp->nscount) {
+ needclose++;
+ } else {
+ for (int ns = 0; ns < statp->nscount; ns++) {
+ if (statp->nsaddr_list[ns].sin_family &&
+ !sock_eq((struct sockaddr*) (void*) &statp->nsaddr_list[ns],
+ (struct sockaddr*) (void*) &statp->_u._ext.ext->nsaddrs[ns])) {
+ needclose++;
+ break;
+ }
+
+ if (statp->_u._ext.nssocks[ns] == -1) continue;
+ peerlen = sizeof(peer);
+ if (getpeername(statp->_u._ext.nssocks[ns], (struct sockaddr*) (void*) &peer,
+ &peerlen) < 0) {
+ needclose++;
+ break;
+ }
+ if (!sock_eq((struct sockaddr*) (void*) &peer, get_nsaddr(statp, (size_t) ns))) {
+ needclose++;
+ break;
+ }
+ }
+ }
+ if (needclose) {
+ res_nclose(statp);
+ statp->_u._ext.nscount = 0;
+ }
+ }
+
+ /*
+ * Maybe initialize our private copy of the ns_addr_list.
+ */
+ if (statp->_u._ext.nscount == 0) {
+ for (int ns = 0; ns < statp->nscount; ns++) {
+ statp->_u._ext.nstimes[ns] = RES_MAXTIME;
+ statp->_u._ext.nssocks[ns] = -1;
+ if (!statp->nsaddr_list[ns].sin_family) continue;
+ statp->_u._ext.ext->nsaddrs[ns].sin = statp->nsaddr_list[ns];
+ }
+ statp->_u._ext.nscount = statp->nscount;
+ }
+
+ /*
+ * Some resolvers want to even out the load on their nameservers.
+ * Note that RES_BLAST overrides RES_ROTATE.
+ */
+ if ((statp->options & RES_ROTATE) != 0U && (statp->options & RES_BLAST) == 0U) {
+ sockaddr_union inu;
+ struct sockaddr_in ina;
+ int lastns = statp->nscount - 1;
+ int fd;
+ u_int16_t nstime;
+
+ if (statp->_u._ext.ext != NULL) inu = statp->_u._ext.ext->nsaddrs[0];
+ ina = statp->nsaddr_list[0];
+ fd = statp->_u._ext.nssocks[0];
+ nstime = statp->_u._ext.nstimes[0];
+ for (int ns = 0; ns < lastns; ns++) {
+ if (statp->_u._ext.ext != NULL)
+ statp->_u._ext.ext->nsaddrs[ns] = statp->_u._ext.ext->nsaddrs[ns + 1];
+ statp->nsaddr_list[ns] = statp->nsaddr_list[ns + 1];
+ statp->_u._ext.nssocks[ns] = statp->_u._ext.nssocks[ns + 1];
+ statp->_u._ext.nstimes[ns] = statp->_u._ext.nstimes[ns + 1];
+ }
+ if (statp->_u._ext.ext != NULL) statp->_u._ext.ext->nsaddrs[lastns] = inu;
+ statp->nsaddr_list[lastns] = ina;
+ statp->_u._ext.nssocks[lastns] = fd;
+ statp->_u._ext.nstimes[lastns] = nstime;
+ }
+
+ res_stats stats[MAXNS];
+ res_params params;
+ int revision_id = resolv_cache_get_resolver_stats(statp->netid, ¶ms, stats);
+ if (revision_id < 0) {
+ // TODO: Remove errno once callers stop using it
+ errno = ESRCH;
+ return -ESRCH;
+ }
+ bool usable_servers[MAXNS];
+ int usableServersCount = android_net_res_stats_get_usable_servers(
+ ¶ms, stats, statp->nscount, usable_servers);
+
+ if ((flags & ANDROID_RESOLV_NO_RETRY) && usableServersCount > 1) {
+ auto hp = reinterpret_cast<const HEADER*>(buf);
+
+ // Select a random server based on the query id
+ int selectedServer = (hp->id % usableServersCount) + 1;
+ res_set_usable_server(selectedServer, statp->nscount, usable_servers);
+ }
+
+ /*
+ * Send request, RETRY times, or until successful.
+ */
+ int retryTimes = (flags & ANDROID_RESOLV_NO_RETRY) ? 1 : params.retry_count;
+
+ for (int attempt = 0; attempt < retryTimes; ++attempt) {
+
+ for (int ns = 0; ns < statp->nscount; ns++) {
+ if (!usable_servers[ns]) continue;
+ int nsaplen;
+ time_t now = 0;
+ int delay = 0;
+ *rcode = RCODE_INTERNAL_ERROR;
+ const sockaddr* nsap = get_nsaddr(statp, ns);
+ nsaplen = get_salen(nsap);
+
+ same_ns:
+ // TODO: Since we expect there is only one DNS server being queried here while this
+ // function tries to query all of private DNS servers. Consider moving it to other
+ // reasonable place. In addition, maybe add stats for private DNS.
+ if (!statp->use_local_nameserver) {
+ bool fallback = false;
+ resplen = res_tls_send(statp, Slice(const_cast<u_char*>(buf), buflen),
+ Slice(ans, anssiz), rcode, &fallback);
+ if (resplen > 0) {
+ if (cache_status == RESOLV_CACHE_NOTFOUND) {
+ _resolv_cache_add(statp->netid, buf, buflen, ans, resplen);
+ }
+ return resplen;
+ }
+ if (!fallback) {
+ _resolv_cache_query_failed(statp->netid, buf, buflen, flags);
+ res_nclose(statp);
+ return -terrno;
+ }
+ }
+
+ static const int niflags = NI_NUMERICHOST | NI_NUMERICSERV;
+ char abuf[NI_MAXHOST];
+ DnsQueryEvent* dnsQueryEvent = addDnsQueryEvent(statp->event);
+ dnsQueryEvent->set_cache_hit(static_cast<CacheStatus>(cache_status));
+
+ if (getnameinfo(nsap, (socklen_t)nsaplen, abuf, sizeof(abuf), NULL, 0, niflags) == 0)
+ LOG(DEBUG) << __func__ << ": Querying server (# " << ns + 1
+ << ") address = " << abuf;
+
+ Stopwatch query_stopwatch;
+ if (v_circuit) {
+ /* Use VC; at most one attempt per server. */
+ bool shouldRecordStats = (attempt == 0);
+ attempt = retryTimes;
+
+ n = send_vc(statp, ¶ms, buf, buflen, ans, anssiz, &terrno, ns, &now, rcode,
+ &delay);
+
+ dnsQueryEvent->set_latency_micros(
+ saturate_cast<int32_t>(query_stopwatch.timeTakenUs()));
+ dnsQueryEvent->set_dns_server_index(ns);
+ dnsQueryEvent->set_ip_version(ipFamilyToIPVersion(nsap->sa_family));
+ dnsQueryEvent->set_retry_times(attempt);
+ dnsQueryEvent->set_rcode(static_cast<NsRcode>(*rcode));
+ dnsQueryEvent->set_protocol(PROTO_TCP);
+ dnsQueryEvent->set_type(getQueryType(buf, buflen));
+
+ /*
+ * Only record stats the first time we try a query. This ensures that
+ * queries that deterministically fail (e.g., a name that always returns
+ * SERVFAIL or times out) do not unduly affect the stats.
+ */
+ if (shouldRecordStats) {
+ res_sample sample;
+ _res_stats_set_sample(&sample, now, *rcode, delay);
+ _resolv_cache_add_resolver_stats_sample(statp->netid, revision_id, ns, &sample,
+ params.max_samples);
+ }
+
+ LOG(INFO) << __func__ << ": used send_vc " << n;
+
+ if (n < 0) {
+ _resolv_cache_query_failed(statp->netid, buf, buflen, flags);
+ res_nclose(statp);
+ return -terrno;
+ };
+ if (n == 0) goto next_ns;
+ resplen = n;
+ } else {
+ /* Use datagrams. */
+ LOG(INFO) << __func__ << ": using send_dg";
+
+ n = send_dg(statp, ¶ms, buf, buflen, ans, anssiz, &terrno, ns, &v_circuit,
+ &gotsomewhere, &now, rcode, &delay);
+
+ dnsQueryEvent->set_latency_micros(
+ saturate_cast<int32_t>(query_stopwatch.timeTakenUs()));
+ dnsQueryEvent->set_dns_server_index(ns);
+ dnsQueryEvent->set_ip_version(ipFamilyToIPVersion(nsap->sa_family));
+ dnsQueryEvent->set_retry_times(attempt);
+ dnsQueryEvent->set_rcode(static_cast<NsRcode>(*rcode));
+ dnsQueryEvent->set_protocol(PROTO_UDP);
+ dnsQueryEvent->set_type(getQueryType(buf, buflen));
+
+ /* Only record stats the first time we try a query. See above. */
+ if (attempt == 0) {
+ res_sample sample;
+ _res_stats_set_sample(&sample, now, *rcode, delay);
+ _resolv_cache_add_resolver_stats_sample(statp->netid, revision_id, ns, &sample,
+ params.max_samples);
+ }
+
+ LOG(INFO) << __func__ << ": used send_dg " << n;
+
+ if (n < 0) {
+ _resolv_cache_query_failed(statp->netid, buf, buflen, flags);
+ res_nclose(statp);
+ return -terrno;
+ };
+ if (n == 0) goto next_ns;
+ if (v_circuit) goto same_ns;
+ resplen = n;
+ }
+
+ LOG(DEBUG) << __func__ << ": got answer:";
+ res_pquery(ans, (resplen > anssiz) ? anssiz : resplen);
+
+ if (cache_status == RESOLV_CACHE_NOTFOUND) {
+ _resolv_cache_add(statp->netid, buf, buflen, ans, resplen);
+ }
+ /*
+ * If we have temporarily opened a virtual circuit,
+ * or if we haven't been asked to keep a socket open,
+ * close the socket.
+ */
+ if ((v_circuit && (statp->options & RES_USEVC) == 0U) ||
+ (statp->options & RES_STAYOPEN) == 0U) {
+ res_nclose(statp);
+ }
+ return (resplen);
+ next_ns:;
+ } // for each ns
+ } // for each retry
+ res_nclose(statp);
+ if (!v_circuit) {
+ if (!gotsomewhere) {
+ // TODO: Remove errno once callers stop using it
+ errno = ECONNREFUSED; /* no nameservers found */
+ terrno = ECONNREFUSED;
+ } else {
+ // TODO: Remove errno once callers stop using it
+ errno = ETIMEDOUT; /* no answer obtained */
+ terrno = ETIMEDOUT;
+ }
+ } else {
+ errno = terrno;
+ }
+ _resolv_cache_query_failed(statp->netid, buf, buflen, flags);
+ return -terrno;
+}
+
+/* Private */
+
+static int get_salen(const struct sockaddr* sa) {
+ if (sa->sa_family == AF_INET)
+ return (sizeof(struct sockaddr_in));
+ else if (sa->sa_family == AF_INET6)
+ return (sizeof(struct sockaddr_in6));
+ else
+ return (0); /* unknown, die on connect */
+}
+
+/*
+ * pick appropriate nsaddr_list for use. see res_init() for initialization.
+ */
+static struct sockaddr* get_nsaddr(res_state statp, size_t n) {
+ if (!statp->nsaddr_list[n].sin_family && statp->_u._ext.ext) {
+ /*
+ * - statp->_u._ext.ext->nsaddrs[n] holds an address that is larger
+ * than struct sockaddr, and
+ * - user code did not update statp->nsaddr_list[n].
+ */
+ return (struct sockaddr*) (void*) &statp->_u._ext.ext->nsaddrs[n];
+ } else {
+ /*
+ * - user code updated statp->nsaddr_list[n], or
+ * - statp->nsaddr_list[n] has the same content as
+ * statp->_u._ext.ext->nsaddrs[n].
+ */
+ return (struct sockaddr*) (void*) &statp->nsaddr_list[n];
+ }
+}
+
+static struct timespec get_timeout(const res_state statp, const res_params* params, const int ns) {
+ int msec;
+ // Legacy algorithm which scales the timeout by nameserver number.
+ // For instance, with 4 nameservers: 5s, 2.5s, 5s, 10s
+ // This has no effect with 1 or 2 nameservers
+ msec = params->base_timeout_msec << ns;
+ if (ns > 0) {
+ msec /= statp->nscount;
+ }
+ // For safety, don't allow OEMs and experiments to configure a timeout shorter than 1s.
+ if (msec < 1000) {
+ msec = 1000; // Use at least 1000ms
+ }
+ LOG(INFO) << __func__ << ": using timeout of " << msec << " msec";
+
+ struct timespec result;
+ result.tv_sec = msec / 1000;
+ result.tv_nsec = (msec % 1000) * 1000000;
+ return result;
+}
+
+static int send_vc(res_state statp, res_params* params, const u_char* buf, int buflen, u_char* ans,
+ int anssiz, int* terrno, int ns, time_t* at, int* rcode, int* delay) {
+ *at = time(NULL);
+ *delay = 0;
+ const HEADER* hp = (const HEADER*) (const void*) buf;
+ HEADER* anhp = (HEADER*) (void*) ans;
+ struct sockaddr* nsap;
+ int nsaplen;
+ int truncating, connreset, n;
+ struct iovec iov[2];
+ u_char* cp;
+
+ LOG(INFO) << __func__ << ": using send_vc";
+
+ nsap = get_nsaddr(statp, (size_t) ns);
+ nsaplen = get_salen(nsap);
+
+ connreset = 0;
+same_ns:
+ truncating = 0;
+
+ struct timespec now = evNowTime();
+
+ /* Are we still talking to whom we want to talk to? */
+ if (statp->_vcsock >= 0 && (statp->_flags & RES_F_VC) != 0) {
+ struct sockaddr_storage peer;
+ socklen_t size = sizeof peer;
+ unsigned old_mark;
+ socklen_t mark_size = sizeof(old_mark);
+ if (getpeername(statp->_vcsock, (struct sockaddr*) (void*) &peer, &size) < 0 ||
+ !sock_eq((struct sockaddr*) (void*) &peer, nsap) ||
+ getsockopt(statp->_vcsock, SOL_SOCKET, SO_MARK, &old_mark, &mark_size) < 0 ||
+ old_mark != statp->_mark) {
+ res_nclose(statp);
+ statp->_flags &= ~RES_F_VC;
+ }
+ }
+
+ if (statp->_vcsock < 0 || (statp->_flags & RES_F_VC) == 0) {
+ if (statp->_vcsock >= 0) res_nclose(statp);
+
+ statp->_vcsock = socket(nsap->sa_family, SOCK_STREAM | SOCK_CLOEXEC, 0);
+ if (statp->_vcsock < 0) {
+ switch (errno) {
+ case EPROTONOSUPPORT:
+ case EPFNOSUPPORT:
+ case EAFNOSUPPORT:
+ Perror(statp, "socket(vc)", errno);
+ return 0;
+ default:
+ *terrno = errno;
+ Perror(statp, "socket(vc)", errno);
+ return -1;
+ }
+ }
+ fchown(statp->_vcsock, AID_DNS, -1);
+ if (statp->_mark != MARK_UNSET) {
+ if (setsockopt(statp->_vcsock, SOL_SOCKET, SO_MARK, &statp->_mark,
+ sizeof(statp->_mark)) < 0) {
+ *terrno = errno;
+ Perror(statp, "setsockopt", errno);
+ return -1;
+ }
+ }
+ errno = 0;
+ if (random_bind(statp->_vcsock, nsap->sa_family) < 0) {
+ *terrno = errno;
+ Aerror(statp, "bind/vc", errno, nsap, nsaplen);
+ res_nclose(statp);
+ return (0);
+ }
+ if (connect_with_timeout(statp->_vcsock, nsap, (socklen_t) nsaplen,
+ get_timeout(statp, params, ns)) < 0) {
+ *terrno = errno;
+ Aerror(statp, "connect/vc", errno, nsap, nsaplen);
+ res_nclose(statp);
+ /*
+ * The way connect_with_timeout() is implemented prevents us from reliably
+ * determining whether this was really a timeout or e.g. ECONNREFUSED. Since
+ * currently both cases are handled in the same way, there is no need to
+ * change this (yet). If we ever need to reliably distinguish between these
+ * cases, both connect_with_timeout() and retrying_poll() need to be
+ * modified, though.
+ */
+ *rcode = RCODE_TIMEOUT;
+ return (0);
+ }
+ statp->_flags |= RES_F_VC;
+ }
+
+ /*
+ * Send length & message
+ */
+ uint16_t len = htons(static_cast<uint16_t>(buflen));
+ iov[0] = evConsIovec(&len, INT16SZ);
+ iov[1] = evConsIovec((void*) buf, (size_t) buflen);
+ if (writev(statp->_vcsock, iov, 2) != (INT16SZ + buflen)) {
+ *terrno = errno;
+ Perror(statp, "write failed", errno);
+ res_nclose(statp);
+ return (0);
+ }
+ /*
+ * Receive length & response
+ */
+read_len:
+ cp = ans;
+ len = INT16SZ;
+ while ((n = read(statp->_vcsock, (char*) cp, (size_t) len)) > 0) {
+ cp += n;
+ if ((len -= n) == 0) break;
+ }
+ if (n <= 0) {
+ *terrno = errno;
+ Perror(statp, "read failed", errno);
+ res_nclose(statp);
+ /*
+ * A long running process might get its TCP
+ * connection reset if the remote server was
+ * restarted. Requery the server instead of
+ * trying a new one. When there is only one
+ * server, this means that a query might work
+ * instead of failing. We only allow one reset
+ * per query to prevent looping.
+ */
+ if (*terrno == ECONNRESET && !connreset) {
+ connreset = 1;
+ res_nclose(statp);
+ goto same_ns;
+ }
+ res_nclose(statp);
+ return (0);
+ }
+ uint16_t resplen = ntohs(*reinterpret_cast<const uint16_t*>(ans));
+ if (resplen > anssiz) {
+ LOG(DEBUG) << __func__ << ": response truncated";
+ truncating = 1;
+ len = anssiz;
+ } else
+ len = resplen;
+ if (len < HFIXEDSZ) {
+ /*
+ * Undersized message.
+ */
+ LOG(DEBUG) << __func__ << ": undersized: " << len;
+ *terrno = EMSGSIZE;
+ res_nclose(statp);
+ return (0);
+ }
+ cp = ans;
+ while (len != 0 && (n = read(statp->_vcsock, (char*) cp, (size_t) len)) > 0) {
+ cp += n;
+ len -= n;
+ }
+ if (n <= 0) {
+ *terrno = errno;
+ Perror(statp, "read(vc)", errno);
+ res_nclose(statp);
+ return (0);
+ }
+
+ if (truncating) {
+ /*
+ * Flush rest of answer so connection stays in synch.
+ */
+ anhp->tc = 1;
+ len = resplen - anssiz;
+ while (len != 0) {
+ char junk[PACKETSZ];
+
+ n = read(statp->_vcsock, junk, (len > sizeof junk) ? sizeof junk : len);
+ if (n > 0)
+ len -= n;
+ else
+ break;
+ }
+ }
+ /*
+ * If the calling application has bailed out of
+ * a previous call and failed to arrange to have
+ * the circuit closed or the server has got
+ * itself confused, then drop the packet and
+ * wait for the correct one.
+ */
+ if (hp->id != anhp->id) {
+ LOG(DEBUG) << __func__ << ": ld answer (unexpected):";
+ res_pquery(ans, (resplen > anssiz) ? anssiz : resplen);
+ goto read_len;
+ }
+
+ /*
+ * All is well, or the error is fatal. Signal that the
+ * next nameserver ought not be tried.
+ */
+ if (resplen > 0) {
+ struct timespec done = evNowTime();
+ *delay = _res_stats_calculate_rtt(&done, &now);
+ *rcode = anhp->rcode;
+ }
+ return (resplen);
+}
+
+/* return -1 on error (errno set), 0 on success */
+static int connect_with_timeout(int sock, const struct sockaddr* nsap, socklen_t salen,
+ const struct timespec timeout) {
+ int res, origflags;
+
+ origflags = fcntl(sock, F_GETFL, 0);
+ fcntl(sock, F_SETFL, origflags | O_NONBLOCK);
+
+ res = connect(sock, nsap, salen);
+ if (res < 0 && errno != EINPROGRESS) {
+ res = -1;
+ goto done;
+ }
+ if (res != 0) {
+ struct timespec now = evNowTime();
+ struct timespec finish = evAddTime(now, timeout);
+ LOG(INFO) << __func__ << ": " << sock << " send_vc";
+ res = retrying_poll(sock, POLLIN | POLLOUT, &finish);
+ if (res <= 0) {
+ res = -1;
+ }
+ }
+done:
+ fcntl(sock, F_SETFL, origflags);
+ LOG(INFO) << __func__ << ": " << sock << " connect_with_const timeout returning " << res;
+ return res;
+}
+
+static int retrying_poll(const int sock, const short events, const struct timespec* finish) {
+ struct timespec now, timeout;
+
+retry:
+ LOG(INFO) << __func__ << ": " << sock << " retrying_poll";
+
+ now = evNowTime();
+ if (evCmpTime(*finish, now) > 0)
+ timeout = evSubTime(*finish, now);
+ else
+ timeout = evConsTime(0L, 0L);
+ struct pollfd fds = {.fd = sock, .events = events};
+ int n = ppoll(&fds, 1, &timeout, /*sigmask=*/NULL);
+ if (n == 0) {
+ LOG(INFO) << __func__ << ": " << sock << "retrying_poll timeout";
+ errno = ETIMEDOUT;
+ return 0;
+ }
+ if (n < 0) {
+ if (errno == EINTR) goto retry;
+ PLOG(INFO) << __func__ << ": " << sock << " retrying_poll failed";
+ return n;
+ }
+ if (fds.revents & (POLLIN | POLLOUT | POLLERR)) {
+ int error;
+ socklen_t len = sizeof(error);
+ if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &error, &len) < 0 || error) {
+ errno = error;
+ PLOG(INFO) << __func__ << ": " << sock << " retrying_poll getsockopt failed";
+ return -1;
+ }
+ }
+ LOG(INFO) << __func__ << ": " << sock << " retrying_poll returning " << n;
+ return n;
+}
+
+static int send_dg(res_state statp, res_params* params, const u_char* buf, int buflen, u_char* ans,
+ int anssiz, int* terrno, int ns, int* v_circuit, int* gotsomewhere, time_t* at,
+ int* rcode, int* delay) {
+ *at = time(NULL);
+ *delay = 0;
+ const HEADER* hp = (const HEADER*) (const void*) buf;
+ HEADER* anhp = (HEADER*) (void*) ans;
+ const struct sockaddr* nsap;
+ int nsaplen;
+ struct timespec now, timeout, finish, done;
+ struct sockaddr_storage from;
+ socklen_t fromlen;
+ int resplen, n, s;
+
+ nsap = get_nsaddr(statp, (size_t) ns);
+ nsaplen = get_salen(nsap);
+ if (statp->_u._ext.nssocks[ns] == -1) {
+ statp->_u._ext.nssocks[ns] = socket(nsap->sa_family, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+ if (statp->_u._ext.nssocks[ns] < 0) {
+ switch (errno) {
+ case EPROTONOSUPPORT:
+ case EPFNOSUPPORT:
+ case EAFNOSUPPORT:
+ Perror(statp, "socket(dg)", errno);
+ return (0);
+ default:
+ *terrno = errno;
+ Perror(statp, "socket(dg)", errno);
+ return (-1);
+ }
+ }
+
+ fchown(statp->_u._ext.nssocks[ns], AID_DNS, -1);
+ if (statp->_mark != MARK_UNSET) {
+ if (setsockopt(statp->_u._ext.nssocks[ns], SOL_SOCKET, SO_MARK, &(statp->_mark),
+ sizeof(statp->_mark)) < 0) {
+ res_nclose(statp);
+ return -1;
+ }
+ }
+#ifndef CANNOT_CONNECT_DGRAM
+ /*
+ * On a 4.3BSD+ machine (client and server,
+ * actually), sending to a nameserver datagram
+ * port with no nameserver will cause an
+ * ICMP port unreachable message to be returned.
+ * If our datagram socket is "connected" to the
+ * server, we get an ECONNREFUSED error on the next
+ * socket operation, and select returns if the
+ * error message is received. We can thus detect
+ * the absence of a nameserver without timing out.
+ */
+ if (random_bind(statp->_u._ext.nssocks[ns], nsap->sa_family) < 0) {
+ Aerror(statp, "bind(dg)", errno, nsap, nsaplen);
+ res_nclose(statp);
+ return (0);
+ }
+ if (connect(statp->_u._ext.nssocks[ns], nsap, (socklen_t) nsaplen) < 0) {
+ Aerror(statp, "connect(dg)", errno, nsap, nsaplen);
+ res_nclose(statp);
+ return (0);
+ }
+#endif /* !CANNOT_CONNECT_DGRAM */
+ LOG(DEBUG) << __func__ << ": new DG socket";
+ }
+ s = statp->_u._ext.nssocks[ns];
+#ifndef CANNOT_CONNECT_DGRAM
+ if (send(s, (const char*) buf, (size_t) buflen, 0) != buflen) {
+ Perror(statp, "send", errno);
+ res_nclose(statp);
+ return 0;
+ }
+#else /* !CANNOT_CONNECT_DGRAM */
+ if (sendto(s, (const char*) buf, buflen, 0, nsap, nsaplen) != buflen) {
+ Aerror(statp, "sendto", errno, nsap, nsaplen);
+ res_nclose(statp);
+ return 0;
+ }
+#endif /* !CANNOT_CONNECT_DGRAM */
+
+ // Wait for reply.
+ timeout = get_timeout(statp, params, ns);
+ now = evNowTime();
+ finish = evAddTime(now, timeout);
+retry:
+ n = retrying_poll(s, POLLIN, &finish);
+
+ if (n == 0) {
+ *rcode = RCODE_TIMEOUT;
+ LOG(DEBUG) << __func__ << ": timeout";
+ *gotsomewhere = 1;
+ return 0;
+ }
+ if (n < 0) {
+ Perror(statp, "poll", errno);
+ res_nclose(statp);
+ return 0;
+ }
+ errno = 0;
+ fromlen = sizeof(from);
+ resplen = recvfrom(s, (char*) ans, (size_t) anssiz, 0, (struct sockaddr*) (void*) &from,
+ &fromlen);
+ if (resplen <= 0) {
+ Perror(statp, "recvfrom", errno);
+ res_nclose(statp);
+ return 0;
+ }
+ *gotsomewhere = 1;
+ if (resplen < HFIXEDSZ) {
+ /*
+ * Undersized message.
+ */
+ LOG(DEBUG) << __func__ << ": undersized: " << resplen;
+ *terrno = EMSGSIZE;
+ res_nclose(statp);
+ return 0;
+ }
+ if (hp->id != anhp->id) {
+ /*
+ * response from old query, ignore it.
+ * XXX - potential security hazard could
+ * be detected here.
+ */
+ LOG(DEBUG) << __func__ << ": old answer:";
+ res_pquery(ans, (resplen > anssiz) ? anssiz : resplen);
+ goto retry;
+ }
+ if (!(statp->options & RES_INSECURE1) &&
+ !res_ourserver_p(statp, (struct sockaddr*) (void*) &from)) {
+ /*
+ * response from wrong server? ignore it.
+ * XXX - potential security hazard could
+ * be detected here.
+ */
+ LOG(DEBUG) << __func__ << ": not our server:";
+ res_pquery(ans, (resplen > anssiz) ? anssiz : resplen);
+ goto retry;
+ }
+ if (anhp->rcode == FORMERR && (statp->options & RES_USE_EDNS0) != 0U) {
+ /*
+ * Do not retry if the server do not understand EDNS0.
+ * The case has to be captured here, as FORMERR packet do not
+ * carry query section, hence res_queriesmatch() returns 0.
+ */
+ LOG(DEBUG) << __func__ << ": server rejected query with EDNS0:";
+ res_pquery(ans, (resplen > anssiz) ? anssiz : resplen);
+ /* record the error */
+ statp->_flags |= RES_F_EDNS0ERR;
+ res_nclose(statp);
+ return 0;
+ }
+ if (!(statp->options & RES_INSECURE2) &&
+ !res_queriesmatch(buf, buf + buflen, ans, ans + anssiz)) {
+ /*
+ * response contains wrong query? ignore it.
+ * XXX - potential security hazard could
+ * be detected here.
+ */
+ LOG(DEBUG) << __func__ << ": wrong query name:";
+ res_pquery(ans, (resplen > anssiz) ? anssiz : resplen);
+ goto retry;
+ }
+ done = evNowTime();
+ *delay = _res_stats_calculate_rtt(&done, &now);
+ if (anhp->rcode == SERVFAIL || anhp->rcode == NOTIMP || anhp->rcode == REFUSED) {
+ LOG(DEBUG) << __func__ << ": server rejected query:";
+ res_pquery(ans, (resplen > anssiz) ? anssiz : resplen);
+ res_nclose(statp);
+ *rcode = anhp->rcode;
+ return 0;
+ }
+ if (!(statp->options & RES_IGNTC) && anhp->tc) {
+ /*
+ * To get the rest of answer,
+ * use TCP with same server.
+ */
+ LOG(DEBUG) << __func__ << ": truncated answer";
+ *v_circuit = 1;
+ res_nclose(statp);
+ return 1;
+ }
+ /*
+ * All is well, or the error is fatal. Signal that the
+ * next nameserver ought not be tried.
+ */
+ if (resplen > 0) {
+ *rcode = anhp->rcode;
+ }
+ return resplen;
+}
+
+static void Aerror(const res_state statp, const char* string, int error,
+ const struct sockaddr* address, int alen) {
+ const int save = errno;
+ char hbuf[NI_MAXHOST];
+ char sbuf[NI_MAXSERV];
+ constexpr int niflags = NI_NUMERICHOST | NI_NUMERICSERV;
+
+ if ((statp->options & RES_DEBUG) != 0U) {
+ if (getnameinfo(address, (socklen_t) alen, hbuf, sizeof(hbuf), sbuf, sizeof(sbuf),
+ niflags)) {
+ strncpy(hbuf, "?", sizeof(hbuf) - 1);
+ hbuf[sizeof(hbuf) - 1] = '\0';
+ strncpy(sbuf, "?", sizeof(sbuf) - 1);
+ sbuf[sizeof(sbuf) - 1] = '\0';
+ }
+ LOG(DEBUG) << __func__ << ": " << string << " ([" << hbuf << "]." << sbuf
+ << "): " << strerror(error);
+ }
+ errno = save;
+}
+
+static void Perror(const res_state statp, const char* string, int error) {
+ if ((statp->options & RES_DEBUG) != 0U) {
+ LOG(DEBUG) << __func__ << ": " << string << ": " << strerror(error);
+ }
+}
+
+static int sock_eq(struct sockaddr* a, struct sockaddr* b) {
+ struct sockaddr_in *a4, *b4;
+ struct sockaddr_in6 *a6, *b6;
+
+ if (a->sa_family != b->sa_family) return 0;
+ switch (a->sa_family) {
+ case AF_INET:
+ a4 = (struct sockaddr_in*) (void*) a;
+ b4 = (struct sockaddr_in*) (void*) b;
+ return a4->sin_port == b4->sin_port && a4->sin_addr.s_addr == b4->sin_addr.s_addr;
+ case AF_INET6:
+ a6 = (struct sockaddr_in6*) (void*) a;
+ b6 = (struct sockaddr_in6*) (void*) b;
+ return a6->sin6_port == b6->sin6_port &&
+#ifdef HAVE_SIN6_SCOPE_ID
+ a6->sin6_scope_id == b6->sin6_scope_id &&
+#endif
+ IN6_ARE_ADDR_EQUAL(&a6->sin6_addr, &b6->sin6_addr);
+ default:
+ return 0;
+ }
+}
+
+PrivateDnsModes convertEnumType(PrivateDnsMode privateDnsmode) {
+ switch (privateDnsmode) {
+ case PrivateDnsMode::OFF:
+ return PrivateDnsModes::PDM_OFF;
+ case PrivateDnsMode::OPPORTUNISTIC:
+ return PrivateDnsModes::PDM_OPPORTUNISTIC;
+ case PrivateDnsMode::STRICT:
+ return PrivateDnsModes::PDM_STRICT;
+ }
+ return PrivateDnsModes::PDM_UNKNOWN;
+}
+
+static int res_tls_send(res_state statp, const Slice query, const Slice answer, int* rcode,
+ bool* fallback) {
+ int resplen = 0;
+ const unsigned netId = statp->netid;
+
+ PrivateDnsStatus privateDnsStatus = gPrivateDnsConfiguration.getStatus(netId);
+ statp->event->set_private_dns_modes(convertEnumType(privateDnsStatus.mode));
+
+ if (privateDnsStatus.mode == PrivateDnsMode::OFF) {
+ *fallback = true;
+ return -1;
+ }
+
+ if (privateDnsStatus.validatedServers.empty()) {
+ if (privateDnsStatus.mode == PrivateDnsMode::OPPORTUNISTIC) {
+ *fallback = true;
+ return -1;
+ } else {
+ // Sleep and iterate some small number of times checking for the
+ // arrival of resolved and validated server IP addresses, instead
+ // of returning an immediate error.
+ // This is needed because as soon as a network becomes the default network, apps will
+ // send DNS queries on that network. If no servers have yet validated, and we do not
+ // block those queries, they would immediately fail, causing application-visible errors.
+ // Note that this can happen even before the network validates, since an unvalidated
+ // network can become the default network if no validated networks are available.
+ //
+ // TODO: see if there is a better way to address this problem, such as buffering the
+ // queries in a queue or only blocking queries for the first few seconds after a default
+ // network change.
+ for (int i = 0; i < 42; i++) {
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
+ if (!gPrivateDnsConfiguration.getStatus(netId).validatedServers.empty()) {
+ privateDnsStatus = gPrivateDnsConfiguration.getStatus(netId);
+ break;
+ }
+ }
+ if (privateDnsStatus.validatedServers.empty()) {
+ return -1;
+ }
+ }
+ }
+
+ LOG(INFO) << __func__ << ": performing query over TLS";
+
+ const auto response = sDnsTlsDispatcher.query(privateDnsStatus.validatedServers, statp, query,
+ answer, &resplen);
+
+ LOG(INFO) << __func__ << ": TLS query result: " << static_cast<int>(response);
+
+ if (privateDnsStatus.mode == PrivateDnsMode::OPPORTUNISTIC) {
+ // In opportunistic mode, handle falling back to cleartext in some
+ // cases (DNS shouldn't fail if a validated opportunistic mode server
+ // becomes unreachable for some reason).
+ switch (response) {
+ case DnsTlsTransport::Response::success:
+ *rcode = reinterpret_cast<HEADER*>(answer.base())->rcode;
+ return resplen;
+ case DnsTlsTransport::Response::network_error:
+ // No need to set the error timeout here since it will fallback to UDP.
+ case DnsTlsTransport::Response::internal_error:
+ // Note: this will cause cleartext queries to be emitted, with
+ // all of the EDNS0 goodness enabled. Fingers crossed. :-/
+ *fallback = true;
+ [[fallthrough]];
+ default:
+ return -1;
+ }
+ } else {
+ // Strict mode
+ switch (response) {
+ case DnsTlsTransport::Response::success:
+ *rcode = reinterpret_cast<HEADER*>(answer.base())->rcode;
+ return resplen;
+ case DnsTlsTransport::Response::network_error:
+ // This case happens when the query stored in DnsTlsTransport is expired since
+ // either 1) the query has been tried for 3 times but no response or 2) fail to
+ // establish the connection with the server.
+ *rcode = RCODE_TIMEOUT;
+ [[fallthrough]];
+ default:
+ return -1;
+ }
+ }
+}
+
+int resolv_res_nsend(const android_net_context* netContext, const uint8_t* msg, int msgLen,
+ uint8_t* ans, int ansLen, int* rcode, uint32_t flags,
+ NetworkDnsEventReported* event) {
+ assert(event != nullptr);
+ res_state res = res_get_state();
+ res_setnetcontext(res, netContext, event);
+ _resolv_populate_res_for_net(res);
+ *rcode = NOERROR;
+ return res_nsend(res, msg, msgLen, ans, ansLen, rcode, flags);
+}
diff --git a/resolv/res_send.h b/resolv/res_send.h
new file mode 100644
index 0000000..fb80160
--- /dev/null
+++ b/resolv/res_send.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#pragma once
+
+#include "netd_resolv/resolv.h" // struct android_net_context
+#include "stats.pb.h"
+
+// Query dns with raw msg
+int resolv_res_nsend(const android_net_context* netContext, const uint8_t* msg, int msgLen,
+ uint8_t* ans, int ansLen, int* rcode, uint32_t flags,
+ android::net::NetworkDnsEventReported* event);
diff --git a/resolv/res_state.cpp b/resolv/res_state.cpp
new file mode 100644
index 0000000..26febff
--- /dev/null
+++ b/resolv/res_state.cpp
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#define LOG_TAG "res_state"
+
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+#include <netdb.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h> /* for gettid() */
+
+#include <android-base/logging.h>
+
+#include "resolv_cache.h"
+#include "resolv_private.h"
+
+typedef struct {
+ // TODO: Have one __res_state per network so we don't have to repopulate frequently.
+ struct __res_state _nres[1];
+ struct res_static _rstatic[1];
+} _res_thread;
+
+static _res_thread* res_thread_alloc(void) {
+ _res_thread* rt = (_res_thread*) calloc(1, sizeof(*rt));
+
+ if (rt) {
+ memset(rt->_rstatic, 0, sizeof rt->_rstatic);
+ }
+ return rt;
+}
+
+static void res_static_done(struct res_static* rs) {
+ /* fortunately, there is nothing to do here, since the
+ * points in h_addr_ptrs and host_aliases should all
+ * point to 'hostbuf'
+ */
+ if (rs->hostf) { /* should not happen in theory, but just be safe */
+ fclose(rs->hostf);
+ rs->hostf = NULL;
+ }
+ free(rs->servent.s_aliases);
+}
+
+static void res_thread_free(void* _rt) {
+ _res_thread* rt = (_res_thread*) _rt;
+
+ LOG(VERBOSE) << __func__ << ": rt=" << rt << " for thread=" << gettid();
+
+ res_static_done(rt->_rstatic);
+ res_ndestroy(rt->_nres);
+ free(rt);
+}
+
+static pthread_key_t _res_key;
+
+__attribute__((constructor)) static void __res_key_init() {
+ pthread_key_create(&_res_key, res_thread_free);
+}
+
+static _res_thread* res_thread_get(void) {
+ _res_thread* rt = (_res_thread*) pthread_getspecific(_res_key);
+ if (rt != NULL) {
+ return rt;
+ }
+
+ /* It is the first time this function is called in this thread,
+ * we need to create a new thread-specific DNS resolver state. */
+ rt = res_thread_alloc();
+ if (rt == NULL) {
+ return NULL;
+ }
+ pthread_setspecific(_res_key, rt);
+
+ /* Reset the state, note that res_ninit() can now properly reset
+ * an existing state without leaking memory.
+ */
+ LOG(VERBOSE) << __func__ << ": tid=" << gettid() << ", rt=" << rt
+ << " setting DNS state (options=" << rt->_nres->options << ")";
+ if (res_ninit(rt->_nres) < 0) {
+ /* This should not happen */
+ LOG(VERBOSE) << __func__ << ": tid=" << gettid() << " rt=" << rt
+ << ", res_ninit() returned < 0";
+ res_thread_free(rt);
+ pthread_setspecific(_res_key, NULL);
+ return NULL;
+ }
+ return rt;
+}
+
+struct __res_state _nres;
+
+res_state res_get_state(void) {
+ _res_thread* rt = res_thread_get();
+
+ return rt ? rt->_nres : NULL;
+}
+
+res_static* res_get_static(void) {
+ _res_thread* rt = res_thread_get();
+
+ return rt ? rt->_rstatic : NULL;
+}
diff --git a/resolv/res_state_ext.h b/resolv/res_state_ext.h
new file mode 100644
index 0000000..6ed3398
--- /dev/null
+++ b/resolv/res_state_ext.h
@@ -0,0 +1,22 @@
+/* $NetBSD: res_private.h,v 1.1.1.1 2004/05/20 17:18:54 christos Exp $ */
+
+#ifndef NETD_RES_STATE_EXT_H
+#define NETD_RES_STATE_EXT_H
+
+#include "resolv_private.h"
+
+// TODO: consider inlining into res_state
+struct res_state_ext {
+ sockaddr_union nsaddrs[MAXNS];
+ struct sort_list {
+ int af;
+ union {
+ struct in_addr ina;
+ struct in6_addr in6a;
+ } addr, mask;
+ } sort_list[MAXRESOLVSORT];
+ char nsuffix[64];
+ char nsuffix2[64];
+};
+
+#endif // NETD_RES_STATE_EXT_H
diff --git a/resolv/res_stats.cpp b/resolv/res_stats.cpp
new file mode 100644
index 0000000..ed6f084
--- /dev/null
+++ b/resolv/res_stats.cpp
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+#define LOG_TAG "res_stats"
+
+#include <arpa/nameser.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include <android-base/logging.h>
+
+#include "netd_resolv/stats.h"
+
+
+// Calculate the round-trip-time from start time t0 and end time t1.
+int _res_stats_calculate_rtt(const timespec* t1, const timespec* t0) {
+ // Divide ns by one million to get ms, multiply s by thousand to get ms (obvious)
+ long ms0 = t0->tv_sec * 1000 + t0->tv_nsec / 1000000;
+ long ms1 = t1->tv_sec * 1000 + t1->tv_nsec / 1000000;
+ return (int) (ms1 - ms0);
+}
+
+// Create a sample for calculating server reachability statistics.
+void _res_stats_set_sample(res_sample* sample, time_t now, int rcode, int rtt) {
+ LOG(INFO) << __func__ << ": rcode = " << rcode << ", sec = " << rtt;
+ sample->at = now;
+ sample->rcode = rcode;
+ sample->rtt = rtt;
+}
+
+/* Clears all stored samples for the given server. */
+void _res_stats_clear_samples(res_stats* stats) {
+ stats->sample_count = stats->sample_next = 0;
+}
+
+/* Aggregates the reachability statistics for the given server based on on the stored samples. */
+void android_net_res_stats_aggregate(res_stats* stats, int* successes, int* errors, int* timeouts,
+ int* internal_errors, int* rtt_avg, time_t* last_sample_time) {
+ int s = 0; // successes
+ int e = 0; // errors
+ int t = 0; // timouts
+ int ie = 0; // internal errors
+ long rtt_sum = 0;
+ time_t last = 0;
+ int rtt_count = 0;
+ for (int i = 0; i < stats->sample_count; ++i) {
+ // Treat everything as an error that the code in send_dg() already considers a
+ // rejection by the server, i.e. SERVFAIL, NOTIMP and REFUSED. Assume that NXDOMAIN
+ // and NOTAUTH can actually occur for user queries. NOERROR with empty answer section
+ // is not treated as an error here either. FORMERR seems to sometimes be returned by
+ // some versions of BIND in response to DNSSEC or EDNS0. Whether to treat such responses
+ // as an indication of a broken server is unclear, though. For now treat such responses,
+ // as well as unknown codes as errors.
+ switch (stats->samples[i].rcode) {
+ case NOERROR:
+ case NOTAUTH:
+ case NXDOMAIN:
+ ++s;
+ rtt_sum += stats->samples[i].rtt;
+ ++rtt_count;
+ break;
+ case RCODE_TIMEOUT:
+ ++t;
+ break;
+ case RCODE_INTERNAL_ERROR:
+ ++ie;
+ break;
+ case SERVFAIL:
+ case NOTIMP:
+ case REFUSED:
+ default:
+ ++e;
+ break;
+ }
+ }
+ *successes = s;
+ *errors = e;
+ *timeouts = t;
+ *internal_errors = ie;
+ /* If there was at least one successful sample, calculate average RTT. */
+ if (rtt_count) {
+ *rtt_avg = rtt_sum / rtt_count;
+ } else {
+ *rtt_avg = -1;
+ }
+ /* If we had at least one sample, populate last sample time. */
+ if (stats->sample_count > 0) {
+ if (stats->sample_next > 0) {
+ last = stats->samples[stats->sample_next - 1].at;
+ } else {
+ last = stats->samples[stats->sample_count - 1].at;
+ }
+ }
+ *last_sample_time = last;
+}
+
+// Returns true if the server is considered usable, i.e. if the success rate is not lower than the
+// threshold for the stored stored samples. If not enough samples are stored, the server is
+// considered usable.
+static bool res_stats_usable_server(const res_params* params, res_stats* stats) {
+ int successes = -1;
+ int errors = -1;
+ int timeouts = -1;
+ int internal_errors = -1;
+ int rtt_avg = -1;
+ time_t last_sample_time = 0;
+ android_net_res_stats_aggregate(stats, &successes, &errors, &timeouts, &internal_errors,
+ &rtt_avg, &last_sample_time);
+ if (successes >= 0 && errors >= 0 && timeouts >= 0) {
+ int total = successes + errors + timeouts;
+ LOG(INFO) << __func__ << ": NS stats: S " << successes << " + E " << errors << " + T "
+ << timeouts << " + I " << internal_errors << " = " << total
+ << ", rtt = " << rtt_avg << ", min_samples = " << unsigned(params->min_samples);
+ if (total >= params->min_samples && (errors > 0 || timeouts > 0)) {
+ int success_rate = successes * 100 / total;
+ LOG(INFO) << __func__ << ": success rate " << success_rate;
+ if (success_rate < params->success_threshold) {
+ time_t now = time(NULL);
+ if (now - last_sample_time > params->sample_validity) {
+ // Note: It might be worth considering to expire old servers after their expiry
+ // date has been reached, however the code for returning the ring buffer to its
+ // previous non-circular state would induce additional complexity.
+ LOG(INFO) << __func__ << ": samples stale, retrying server";
+ _res_stats_clear_samples(stats);
+ } else {
+ LOG(INFO) << __func__ << ": too many resolution errors, ignoring server";
+ return 0;
+ }
+ }
+ }
+ }
+ return 1;
+}
+
+int android_net_res_stats_get_usable_servers(const res_params* params, res_stats stats[],
+ int nscount, bool usable_servers[]) {
+ unsigned usable_servers_found = 0;
+ for (int ns = 0; ns < nscount; ns++) {
+ bool usable = res_stats_usable_server(params, &stats[ns]);
+ if (usable) {
+ ++usable_servers_found;
+ }
+ usable_servers[ns] = usable;
+ }
+ // If there are no usable servers, consider all of them usable.
+ // TODO: Explore other possibilities, such as enabling only the best N servers, etc.
+ if (usable_servers_found == 0) {
+ for (int ns = 0; ns < nscount; ns++) {
+ usable_servers[ns] = true;
+ }
+ }
+ return (usable_servers_found == 0) ? nscount : usable_servers_found;
+}
diff --git a/resolv/resolv_cache.h b/resolv/resolv_cache.h
new file mode 100644
index 0000000..df21fb3
--- /dev/null
+++ b/resolv/resolv_cache.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "netd_resolv/resolv.h"
+
+#include <stddef.h>
+
+#include <unordered_map>
+#include <vector>
+
+struct __res_state;
+
+constexpr int DNSEVENT_SUBSAMPLING_MAP_DEFAULT_KEY = -1;
+
+/* sets the name server addresses to the provided res_state structure. The
+ * name servers are retrieved from the cache which is associated
+ * with the network to which the res_state structure is associated */
+void _resolv_populate_res_for_net(struct __res_state* statp);
+
+std::vector<unsigned> resolv_list_caches();
+
+std::vector<std::string> resolv_cache_dump_subsampling_map(unsigned netid);
+uint32_t resolv_cache_get_subsampling_denom(unsigned netid, int return_code);
+
+typedef enum {
+ RESOLV_CACHE_UNSUPPORTED, /* the cache can't handle that kind of queries */
+ /* or the answer buffer is too small */
+ RESOLV_CACHE_NOTFOUND, /* the cache doesn't know about this query */
+ RESOLV_CACHE_FOUND, /* the cache found the answer */
+ RESOLV_CACHE_SKIP /* Don't do anything on cache */
+} ResolvCacheStatus;
+
+ResolvCacheStatus _resolv_cache_lookup(unsigned netid, const void* query, int querylen,
+ void* answer, int answersize, int* answerlen,
+ uint32_t flags);
+
+/* add a (query,answer) to the cache, only call if _resolv_cache_lookup
+ * did return RESOLV_CACHE_NOTFOUND
+ */
+void _resolv_cache_add(unsigned netid, const void* query, int querylen, const void* answer,
+ int answerlen);
+
+/* Notify the cache a request failed */
+void _resolv_cache_query_failed(unsigned netid, const void* query, int querylen, uint32_t flags);
+
+// Sets name servers for a given network.
+int resolv_set_nameservers_for_net(unsigned netid, const char** servers, int numservers,
+ const char* domains, const res_params* params);
+
+// Creates the cache associated with the given network.
+int resolv_create_cache_for_net(unsigned netid);
+
+// Deletes the cache associated with the given network.
+void resolv_delete_cache_for_net(unsigned netid);
diff --git a/resolv/resolv_private.h b/resolv/resolv_private.h
new file mode 100644
index 0000000..3ecb6c1
--- /dev/null
+++ b/resolv/resolv_private.h
@@ -0,0 +1,255 @@
+/* $NetBSD: resolv.h,v 1.31 2005/12/26 19:01:47 perry Exp $ */
+
+/*
+ * Copyright (c) 1983, 1987, 1989
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Portions Copyright (c) 1996-1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * @(#)resolv.h 8.1 (Berkeley) 6/2/93
+ * Id: resolv.h,v 1.7.2.11.4.2 2004/06/25 00:41:05 marka Exp
+ */
+
+#ifndef NETD_RESOLV_PRIVATE_H
+#define NETD_RESOLV_PRIVATE_H
+
+#include <android-base/logging.h>
+#include <net/if.h>
+#include <resolv.h>
+#include <time.h>
+#include <string>
+
+#include "netd_resolv/params.h"
+#include "netd_resolv/resolv.h"
+#include "netd_resolv/stats.h"
+#include "resolv_static.h"
+#include "stats.pb.h"
+
+// Linux defines MAXHOSTNAMELEN as 64, while the domain name limit in
+// RFC 1034 and RFC 1035 is 255 octets.
+#ifdef MAXHOSTNAMELEN
+#undef MAXHOSTNAMELEN
+#endif
+#define MAXHOSTNAMELEN 256
+
+/*
+ * Global defines and variables for resolver stub.
+ */
+#define MAXDFLSRCH 3 /* # default domain levels to try */
+#define LOCALDOMAINPARTS 2 /* min levels in name that is "local" */
+
+#define RES_TIMEOUT 5000 /* min. milliseconds between retries */
+#define MAXRESOLVSORT 10 /* number of net to sort on */
+#define RES_MAXNDOTS 15 /* should reflect bit field size */
+#define RES_DFLRETRY 2 /* Default #/tries. */
+#define RES_MAXTIME 65535 /* Infinity, in milliseconds. */
+
+struct res_state_ext;
+
+struct __res_state {
+ unsigned netid; /* NetId: cache key and socket mark */
+ u_long options; /* option flags - see below. */
+ int nscount; /* number of name srvers */
+ struct sockaddr_in nsaddr_list[MAXNS]; /* address of name server */
+#define nsaddr nsaddr_list[0] /* for backward compatibility */
+ u_short id; /* current message id */
+ char* dnsrch[MAXDNSRCH + 1]; /* components of domain to search */
+ char defdname[256]; /* default domain (deprecated) */
+ unsigned ndots : 4; /* threshold for initial abs. query */
+ unsigned nsort : 4; /* number of elements in sort_list[] */
+ char unused[3];
+ struct {
+ struct in_addr addr;
+ uint32_t mask;
+ } sort_list[MAXRESOLVSORT];
+ unsigned _mark; /* If non-0 SET_MARK to _mark on all request sockets */
+ int _vcsock; /* PRIVATE: for res_send VC i/o */
+ u_int _flags; /* PRIVATE: see below */
+ u_int _pad; /* make _u 64 bit aligned */
+ union {
+ /* On an 32-bit arch this means 512b total. */
+ char pad[72 - 4 * sizeof(int) - 2 * sizeof(void*)];
+ struct {
+ uint16_t nscount;
+ uint16_t nstimes[MAXNS]; /* ms. */
+ int nssocks[MAXNS];
+ struct res_state_ext* ext; /* extention for IPv6 */
+ } _ext;
+ } _u;
+ struct res_static rstatic[1];
+ bool use_local_nameserver; /* DNS-over-TLS bypass */
+ android::net::NetworkDnsEventReported* event;
+};
+
+typedef struct __res_state* res_state;
+
+/* Retrieve a local copy of the stats for the given netid. The buffer must have space for
+ * MAXNS __resolver_stats. Returns the revision id of the resolvers used.
+ */
+int resolv_cache_get_resolver_stats(unsigned netid, res_params* params, res_stats stats[MAXNS]);
+
+/* Add a sample to the shared struct for the given netid and server, provided that the
+ * revision_id of the stored servers has not changed.
+ */
+void _resolv_cache_add_resolver_stats_sample(unsigned netid, int revision_id, int ns,
+ const res_sample* sample, int max_samples);
+
+// Calculate the round-trip-time from start time t0 and end time t1.
+int _res_stats_calculate_rtt(const timespec* t1, const timespec* t0);
+
+// Create a sample for calculating server reachability statistics.
+void _res_stats_set_sample(res_sample* sample, time_t now, int rcode, int rtt);
+
+/* End of stats related definitions */
+
+// Flags for res_state->_flags
+#define RES_F_VC 0x00000001 // socket is TCP
+#define RES_F_EDNS0ERR 0x00000004 // EDNS0 caused errors
+
+/*
+ * Resolver options (keep these in synch with res_debug.c, please)
+ */
+#define RES_INIT 0x00000001 /* address initialized */
+#define RES_DEBUG 0x00000002 /* print debug messages */
+#define RES_AAONLY 0x00000004 /* authoritative answers only (!IMPL)*/
+#define RES_USEVC 0x00000008 /* use virtual circuit */
+#define RES_PRIMARY 0x00000010 /* query primary server only (!IMPL) */
+#define RES_IGNTC 0x00000020 /* ignore trucation errors */
+#define RES_RECURSE 0x00000040 /* recursion desired */
+#define RES_DEFNAMES 0x00000080 /* use default domain name */
+#define RES_STAYOPEN 0x00000100 /* Keep TCP socket open */
+#define RES_DNSRCH 0x00000200 /* search up local domain tree */
+#define RES_INSECURE1 0x00000400 /* type 1 security disabled */
+#define RES_INSECURE2 0x00000800 /* type 2 security disabled */
+#define RES_USE_INET6 0x00002000 /* use/map IPv6 in gethostbyname() */
+#define RES_ROTATE 0x00004000 /* rotate ns list after each query */
+#define RES_NOCHECKNAME 0x00008000 /* do not check names for sanity. */
+#define RES_KEEPTSIG 0x00010000 /* do not strip TSIG records */
+#define RES_BLAST 0x00020000 /* blast all recursive servers */
+#define RES_NOTLDQUERY 0x00100000 /* don't unqualified name as a tld */
+#define RES_USE_DNSSEC 0x00200000 /* use DNSSEC using OK bit in OPT */
+/* #define RES_DEBUG2 0x00400000 */ /* nslookup internal */
+/* KAME extensions: use higher bit to avoid conflict with ISC use */
+#define RES_USE_DNAME 0x10000000 /* use DNAME */
+#define RES_USE_EDNS0 0x40000000 /* use EDNS0 if configured */
+#define RES_NO_NIBBLE2 0x80000000 /* disable alternate nibble lookup */
+
+#define RES_DEFAULT (RES_RECURSE | RES_DEFNAMES | RES_DNSRCH | RES_NO_NIBBLE2)
+
+
+/*
+ * Error code extending h_errno codes defined in bionic/libc/include/netdb.h.
+ *
+ * This error code, including legacy h_errno, is returned from res_nquery(), res_nsearch(),
+ * res_nquerydomain(), res_queryN(), res_searchN() and res_querydomainN() for DNS metrics.
+ *
+ * TODO: Consider mapping legacy and extended h_errno into a unified resolver error code mapping.
+ */
+#define NETD_RESOLV_H_ERRNO_EXT_TIMEOUT RCODE_TIMEOUT
+
+extern const char* const _res_opcodes[];
+
+/* Things involving an internal (static) resolver context. */
+struct __res_state* res_get_state(void);
+
+int res_hnok(const char*);
+int res_ownok(const char*);
+int res_mailok(const char*);
+int res_dnok(const char*);
+int dn_skipname(const u_char*, const u_char*);
+void putlong(uint32_t, u_char*);
+void putshort(uint16_t, u_char*);
+
+// Thread-unsafe functions returning pointers to static buffers :-(
+// TODO: switch all res_debug to std::string
+const char* p_class(int);
+const char* p_type(int);
+const char* p_rcode(int);
+const char* p_section(int, int);
+
+int res_nameinquery(const char*, int, int, const u_char*, const u_char*);
+int res_queriesmatch(const u_char*, const u_char*, const u_char*, const u_char*);
+/* Things involving a resolver context. */
+int res_ninit(res_state);
+void res_pquery(const u_char*, int);
+
+int res_nquery(res_state, const char*, int, int, u_char*, int, int*);
+int res_nsearch(res_state, const char*, int, int, u_char*, int, int*);
+int res_nquerydomain(res_state, const char*, const char*, int, int, u_char*, int, int*);
+int res_nmkquery(res_state, int, const char*, int, int, const u_char*, int, const u_char*, u_char*,
+ int);
+int res_nsend(res_state, const u_char*, int, u_char*, int, int*, uint32_t);
+void res_nclose(res_state);
+int res_nopt(res_state, int, u_char*, int, int);
+int res_vinit(res_state, int);
+void res_ndestroy(res_state);
+void res_setservers(res_state, const sockaddr_union*, int);
+int res_getservers(res_state, sockaddr_union*, int);
+
+struct android_net_context; /* forward */
+void res_setnetcontext(res_state, const struct android_net_context*,
+ android::net::NetworkDnsEventReported* event);
+
+int getaddrinfo_numeric(const char* hostname, const char* servname, addrinfo hints,
+ addrinfo** result);
+
+// Helper function for converting h_errno to the error codes visible to netd
+int herrnoToAiErrno(int herrno);
+
+// switch resolver log severity
+android::base::LogSeverity logSeverityStrToEnum(const std::string& logSeverityStr);
+
+template <typename Dest>
+Dest saturate_cast(int64_t x) {
+ using DestLimits = std::numeric_limits<Dest>;
+ if (x > DestLimits::max()) return DestLimits::max();
+ if (x < DestLimits::min()) return DestLimits::min();
+ return static_cast<Dest>(x);
+}
+
+android::net::NsType getQueryType(const uint8_t* msg, size_t msgLen);
+
+android::net::IpVersion ipFamilyToIPVersion(int ipFamily);
+
+#endif // NETD_RESOLV_PRIVATE_H
diff --git a/resolv/resolv_static.h b/resolv/resolv_static.h
new file mode 100644
index 0000000..b250892
--- /dev/null
+++ b/resolv/resolv_static.h
@@ -0,0 +1,33 @@
+#ifndef NETD_RESOLV_STATIC_H
+#define NETD_RESOLV_STATIC_H
+
+#include <netdb.h>
+#include <stdio.h>
+
+/* this structure contains all the variables that were declared
+ * 'static' in the original NetBSD resolver code.
+ *
+ * this caused vast amounts of crashes and memory corruptions
+ * when the resolver was being used by multiple threads.
+ *
+ * (note: the OpenBSD/FreeBSD resolver has similar 'issues')
+ */
+
+#define MAXALIASES 35
+#define MAXADDRS 35
+
+struct res_static {
+ char* h_addr_ptrs[MAXADDRS + 1];
+ char* host_aliases[MAXALIASES];
+ char hostbuf[8 * 1024];
+ u_int32_t host_addr[16 / sizeof(u_int32_t)]; /* IPv4 or IPv6 */
+ FILE* hostf;
+ int stayopen;
+ const char* servent_ptr;
+ struct servent servent;
+ struct hostent host;
+};
+
+res_static* res_get_static();
+
+#endif // NETD_RESOLV_STATIC_H
diff --git a/resolv/resolver_test.cpp b/resolv/resolver_test.cpp
new file mode 100644
index 0000000..b135b16
--- /dev/null
+++ b/resolv/resolver_test.cpp
@@ -0,0 +1,3489 @@
+/*
+ * Copyright (C) 2016 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 requied 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.
+ *
+ */
+
+#define LOG_TAG "resolv_integration_test"
+
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <poll.h> /* poll */
+#include <resolv.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <chrono>
+#include <iterator>
+#include <numeric>
+#include <thread>
+
+#include <android-base/parseint.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+#include <android/multinetwork.h> // ResNsendFlags
+#include <cutils/sockets.h>
+#include <gmock/gmock-matchers.h>
+#include <gtest/gtest.h>
+#include <openssl/base64.h>
+#include <private/android_filesystem_config.h>
+#include <utils/Log.h>
+
+#include "NetdClient.h"
+#include "netid_client.h" // NETID_UNSET
+#include "netd_resolv/params.h" // MAX_NS
+
+#include "dns_responder/dns_responder.h"
+#include "dns_responder/dns_responder_client.h"
+#include "dns_responder/dns_tls_frontend.h"
+
+#include "NetdConstants.h"
+#include "ResolverStats.h"
+
+#include "android/net/IDnsResolver.h"
+#include "binder/IServiceManager.h"
+#include "netdutils/ResponseCode.h"
+#include "netdutils/SocketOption.h"
+
+// TODO: make this dynamic and stop depending on implementation details.
+constexpr int TEST_NETID = 30;
+// Valid VPN netId range is 100 ~ 65535
+constexpr int TEST_VPN_NETID = 65502;
+constexpr int MAXPACKET = (8 * 1024);
+
+// Semi-public Bionic hook used by the NDK (frameworks/base/native/android/net.c)
+// Tested here for convenience.
+extern "C" int android_getaddrinfofornet(const char* hostname, const char* servname,
+ const addrinfo* hints, unsigned netid, unsigned mark,
+ struct addrinfo** result);
+
+using android::base::ParseInt;
+using android::base::StringPrintf;
+using android::base::unique_fd;
+using android::net::ResolverStats;
+using android::netdutils::enableSockopt;
+using android::netdutils::ResponseCode;
+
+// TODO: move into libnetdutils?
+namespace {
+ScopedAddrinfo safe_getaddrinfo(const char* node, const char* service,
+ const struct addrinfo* hints) {
+ addrinfo* result = nullptr;
+ if (getaddrinfo(node, service, hints, &result) != 0) {
+ result = nullptr; // Should already be the case, but...
+ }
+ return ScopedAddrinfo(result);
+}
+} // namespace
+
+class ResolverTest : public ::testing::Test {
+ protected:
+ struct DnsRecord {
+ std::string host_name; // host name
+ ns_type type; // record type
+ std::string addr; // ipv4/v6 address
+ };
+
+ void SetUp() { mDnsClient.SetUp(); }
+ void TearDown() {
+ mDnsClient.TearDown();
+ }
+
+ bool GetResolverInfo(std::vector<std::string>* servers, std::vector<std::string>* domains,
+ std::vector<std::string>* tlsServers, res_params* params,
+ std::vector<ResolverStats>* stats,
+ int* wait_for_pending_req_timeout_count) {
+ using android::net::IDnsResolver;
+ std::vector<int32_t> params32;
+ std::vector<int32_t> stats32;
+ std::vector<int32_t> wait_for_pending_req_timeout_count32{0};
+ auto rv = mDnsClient.resolvService()->getResolverInfo(
+ TEST_NETID, servers, domains, tlsServers, ¶ms32, &stats32,
+ &wait_for_pending_req_timeout_count32);
+
+ if (!rv.isOk() ||
+ params32.size() != static_cast<size_t>(IDnsResolver::RESOLVER_PARAMS_COUNT)) {
+ return false;
+ }
+ *params = res_params{
+ .sample_validity = static_cast<uint16_t>(
+ params32[IDnsResolver::RESOLVER_PARAMS_SAMPLE_VALIDITY]),
+ .success_threshold = static_cast<uint8_t>(
+ params32[IDnsResolver::RESOLVER_PARAMS_SUCCESS_THRESHOLD]),
+ .min_samples =
+ static_cast<uint8_t>(params32[IDnsResolver::RESOLVER_PARAMS_MIN_SAMPLES]),
+ .max_samples =
+ static_cast<uint8_t>(params32[IDnsResolver::RESOLVER_PARAMS_MAX_SAMPLES]),
+ .base_timeout_msec = params32[IDnsResolver::RESOLVER_PARAMS_BASE_TIMEOUT_MSEC],
+ .retry_count = params32[IDnsResolver::RESOLVER_PARAMS_RETRY_COUNT],
+ };
+ *wait_for_pending_req_timeout_count = wait_for_pending_req_timeout_count32[0];
+ return ResolverStats::decodeAll(stats32, stats);
+ }
+
+ static std::string ToString(const hostent* he) {
+ if (he == nullptr) return "<null>";
+ char buffer[INET6_ADDRSTRLEN];
+ if (!inet_ntop(he->h_addrtype, he->h_addr_list[0], buffer, sizeof(buffer))) {
+ return "<invalid>";
+ }
+ return buffer;
+ }
+
+ static std::string ToString(const addrinfo* ai) {
+ if (!ai)
+ return "<null>";
+ for (const auto* aip = ai ; aip != nullptr ; aip = aip->ai_next) {
+ char host[NI_MAXHOST];
+ int rv = getnameinfo(aip->ai_addr, aip->ai_addrlen, host, sizeof(host), nullptr, 0,
+ NI_NUMERICHOST);
+ if (rv != 0)
+ return gai_strerror(rv);
+ return host;
+ }
+ return "<invalid>";
+ }
+
+ static std::string ToString(const ScopedAddrinfo& ai) { return ToString(ai.get()); }
+
+ static std::vector<std::string> ToStrings(const addrinfo* ai) {
+ std::vector<std::string> hosts;
+ if (!ai) {
+ hosts.push_back("<null>");
+ return hosts;
+ }
+ for (const auto* aip = ai; aip != nullptr; aip = aip->ai_next) {
+ char host[NI_MAXHOST];
+ int rv = getnameinfo(aip->ai_addr, aip->ai_addrlen, host, sizeof(host), nullptr, 0,
+ NI_NUMERICHOST);
+ if (rv != 0) {
+ hosts.clear();
+ hosts.push_back(gai_strerror(rv));
+ return hosts;
+ } else {
+ hosts.push_back(host);
+ }
+ }
+ if (hosts.empty()) hosts.push_back("<invalid>");
+ return hosts;
+ }
+
+ static std::vector<std::string> ToStrings(const ScopedAddrinfo& ai) {
+ return ToStrings(ai.get());
+ }
+
+ size_t GetNumQueries(const test::DNSResponder& dns, const char* name) const {
+ auto queries = dns.queries();
+ size_t found = 0;
+ for (const auto& p : queries) {
+ if (p.first == name) {
+ ++found;
+ }
+ }
+ return found;
+ }
+
+ size_t GetNumQueriesForType(const test::DNSResponder& dns, ns_type type,
+ const char* name) const {
+ auto queries = dns.queries();
+ size_t found = 0;
+ for (const auto& p : queries) {
+ if (p.second == type && p.first == name) {
+ ++found;
+ }
+ }
+ return found;
+ }
+
+ bool WaitForPrefix64Detected(int netId, int timeoutMs) {
+ constexpr int intervalMs = 2;
+ const int limit = timeoutMs / intervalMs;
+ for (int count = 0; count <= limit; ++count) {
+ std::string prefix;
+ auto rv = mDnsClient.resolvService()->getPrefix64(netId, &prefix);
+ if (rv.isOk()) {
+ return true;
+ }
+ usleep(intervalMs * 1000);
+ }
+ return false;
+ }
+
+ void RunGetAddrInfoStressTest_Binder(unsigned num_hosts, unsigned num_threads,
+ unsigned num_queries) {
+ std::vector<std::string> domains = { "example.com" };
+ std::vector<std::unique_ptr<test::DNSResponder>> dns;
+ std::vector<std::string> servers;
+ std::vector<DnsResponderClient::DnsResponderClient::Mapping> mappings;
+ ASSERT_NO_FATAL_FAILURE(mDnsClient.SetupMappings(num_hosts, domains, &mappings));
+ ASSERT_NO_FATAL_FAILURE(mDnsClient.SetupDNSServers(MAXNS, mappings, &dns, &servers));
+
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork(servers, domains, kDefaultParams));
+
+ auto t0 = std::chrono::steady_clock::now();
+ std::vector<std::thread> threads(num_threads);
+ for (std::thread& thread : threads) {
+ thread = std::thread([&mappings, num_queries]() {
+ for (unsigned i = 0 ; i < num_queries ; ++i) {
+ uint32_t ofs = arc4random_uniform(mappings.size());
+ auto& mapping = mappings[ofs];
+ addrinfo* result = nullptr;
+ int rv = getaddrinfo(mapping.host.c_str(), nullptr, nullptr, &result);
+ EXPECT_EQ(0, rv) << "error [" << rv << "] " << gai_strerror(rv);
+ if (rv == 0) {
+ std::string result_str = ToString(result);
+ EXPECT_TRUE(result_str == mapping.ip4 || result_str == mapping.ip6)
+ << "result='" << result_str << "', ip4='" << mapping.ip4
+ << "', ip6='" << mapping.ip6;
+ }
+ if (result) {
+ freeaddrinfo(result);
+ result = nullptr;
+ }
+ }
+ });
+ }
+
+ for (std::thread& thread : threads) {
+ thread.join();
+ }
+ auto t1 = std::chrono::steady_clock::now();
+ ALOGI("%u hosts, %u threads, %u queries, %Es", num_hosts, num_threads, num_queries,
+ std::chrono::duration<double>(t1 - t0).count());
+
+ std::vector<std::string> res_servers;
+ std::vector<std::string> res_domains;
+ std::vector<std::string> res_tls_servers;
+ res_params res_params;
+ std::vector<ResolverStats> res_stats;
+ int wait_for_pending_req_timeout_count;
+ ASSERT_TRUE(GetResolverInfo(&res_servers, &res_domains, &res_tls_servers, &res_params,
+ &res_stats, &wait_for_pending_req_timeout_count));
+ EXPECT_EQ(0, wait_for_pending_req_timeout_count);
+ }
+
+ void StartDns(test::DNSResponder& dns, const std::vector<DnsRecord>& records) {
+ for (const auto& r : records) {
+ dns.addMapping(r.host_name, r.type, r.addr);
+ }
+
+ ASSERT_TRUE(dns.startServer());
+ dns.clearQueries();
+ }
+
+ DnsResponderClient mDnsClient;
+
+ static constexpr char kLocalHost[] = "localhost";
+ static constexpr char kLocalHostAddr[] = "127.0.0.1";
+ static constexpr char kIp6LocalHost[] = "ip6-localhost";
+ static constexpr char kIp6LocalHostAddr[] = "::1";
+ static constexpr char kHelloExampleCom[] = "hello.example.com.";
+};
+
+TEST_F(ResolverTest, GetHostByName) {
+ constexpr char nonexistent_host_name[] = "nonexistent.example.com.";
+
+ test::DNSResponder dns;
+ StartDns(dns, {{kHelloExampleCom, ns_type::ns_t_a, "1.2.3.3"}});
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork());
+
+ const hostent* result;
+ result = gethostbyname("nonexistent");
+ EXPECT_EQ(1U, GetNumQueriesForType(dns, ns_type::ns_t_a, nonexistent_host_name));
+ ASSERT_TRUE(result == nullptr);
+ ASSERT_EQ(HOST_NOT_FOUND, h_errno);
+
+ dns.clearQueries();
+ result = gethostbyname("hello");
+ EXPECT_EQ(1U, GetNumQueriesForType(dns, ns_type::ns_t_a, kHelloExampleCom));
+ ASSERT_FALSE(result == nullptr);
+ ASSERT_EQ(4, result->h_length);
+ ASSERT_FALSE(result->h_addr_list[0] == nullptr);
+ EXPECT_EQ("1.2.3.3", ToString(result));
+ EXPECT_TRUE(result->h_addr_list[1] == nullptr);
+}
+
+TEST_F(ResolverTest, GetHostByName_cnames) {
+ constexpr char host_name[] = "host.example.com.";
+ size_t cnamecount = 0;
+ test::DNSResponder dns;
+
+ const std::vector<DnsRecord> records = {
+ {kHelloExampleCom, ns_type::ns_t_cname, "a.example.com."},
+ {"a.example.com.", ns_type::ns_t_cname, "b.example.com."},
+ {"b.example.com.", ns_type::ns_t_cname, "c.example.com."},
+ {"c.example.com.", ns_type::ns_t_cname, "d.example.com."},
+ {"d.example.com.", ns_type::ns_t_cname, "e.example.com."},
+ {"e.example.com.", ns_type::ns_t_cname, host_name},
+ {host_name, ns_type::ns_t_a, "1.2.3.3"},
+ {host_name, ns_type::ns_t_aaaa, "2001:db8::42"},
+ };
+ StartDns(dns, records);
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork());
+
+ // using gethostbyname2() to resolve ipv4 hello.example.com. to 1.2.3.3
+ // Ensure the v4 address and cnames are correct
+ const hostent* result;
+ result = gethostbyname2("hello", AF_INET);
+ ASSERT_FALSE(result == nullptr);
+
+ for (int i = 0; result != nullptr && result->h_aliases[i] != nullptr; i++) {
+ std::string domain_name = records[i].host_name.substr(0, records[i].host_name.size() - 1);
+ EXPECT_EQ(result->h_aliases[i], domain_name);
+ cnamecount++;
+ }
+ // The size of "Non-cname type" record in DNS records is 2
+ ASSERT_EQ(cnamecount, records.size() - 2);
+ ASSERT_EQ(4, result->h_length);
+ ASSERT_FALSE(result->h_addr_list[0] == nullptr);
+ EXPECT_EQ("1.2.3.3", ToString(result));
+ EXPECT_TRUE(result->h_addr_list[1] == nullptr);
+ EXPECT_EQ(1U, dns.queries().size()) << dns.dumpQueries();
+
+ // using gethostbyname2() to resolve ipv6 hello.example.com. to 2001:db8::42
+ // Ensure the v6 address and cnames are correct
+ cnamecount = 0;
+ dns.clearQueries();
+ result = gethostbyname2("hello", AF_INET6);
+ for (unsigned i = 0; result != nullptr && result->h_aliases[i] != nullptr; i++) {
+ std::string domain_name = records[i].host_name.substr(0, records[i].host_name.size() - 1);
+ EXPECT_EQ(result->h_aliases[i], domain_name);
+ cnamecount++;
+ }
+ // The size of "Non-cname type" DNS record in records is 2
+ ASSERT_EQ(cnamecount, records.size() - 2);
+ ASSERT_FALSE(result == nullptr);
+ ASSERT_EQ(16, result->h_length);
+ ASSERT_FALSE(result->h_addr_list[0] == nullptr);
+ EXPECT_EQ("2001:db8::42", ToString(result));
+ EXPECT_TRUE(result->h_addr_list[1] == nullptr);
+}
+
+TEST_F(ResolverTest, GetHostByName_cnamesInfiniteLoop) {
+ test::DNSResponder dns;
+ const std::vector<DnsRecord> records = {
+ {kHelloExampleCom, ns_type::ns_t_cname, "a.example.com."},
+ {"a.example.com.", ns_type::ns_t_cname, kHelloExampleCom},
+ };
+ StartDns(dns, records);
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork());
+
+ const hostent* result;
+ result = gethostbyname2("hello", AF_INET);
+ ASSERT_TRUE(result == nullptr);
+
+ dns.clearQueries();
+ result = gethostbyname2("hello", AF_INET6);
+ ASSERT_TRUE(result == nullptr);
+}
+
+TEST_F(ResolverTest, GetHostByName_localhost) {
+ constexpr char name_camelcase[] = "LocalHost";
+ constexpr char name_ip6_dot[] = "ip6-localhost.";
+ constexpr char name_ip6_fqdn[] = "ip6-localhost.example.com.";
+
+ // Add a dummy nameserver which shouldn't receive any queries
+ test::DNSResponder dns;
+ StartDns(dns, {});
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork());
+
+ // Expect no DNS queries; localhost is resolved via /etc/hosts
+ const hostent* result = gethostbyname(kLocalHost);
+ EXPECT_TRUE(dns.queries().empty()) << dns.dumpQueries();
+ ASSERT_FALSE(result == nullptr);
+ ASSERT_EQ(4, result->h_length);
+ ASSERT_FALSE(result->h_addr_list[0] == nullptr);
+ EXPECT_EQ(kLocalHostAddr, ToString(result));
+ EXPECT_TRUE(result->h_addr_list[1] == nullptr);
+
+ // Ensure the hosts file resolver ignores case of hostnames
+ result = gethostbyname(name_camelcase);
+ EXPECT_TRUE(dns.queries().empty()) << dns.dumpQueries();
+ ASSERT_FALSE(result == nullptr);
+ ASSERT_EQ(4, result->h_length);
+ ASSERT_FALSE(result->h_addr_list[0] == nullptr);
+ EXPECT_EQ(kLocalHostAddr, ToString(result));
+ EXPECT_TRUE(result->h_addr_list[1] == nullptr);
+
+ // The hosts file also contains ip6-localhost, but gethostbyname() won't
+ // return it unless the RES_USE_INET6 option is set. This would be easy to
+ // change, but there's no point in changing the legacy behavior; new code
+ // should be calling getaddrinfo() anyway.
+ // So we check the legacy behavior, which results in amusing A-record
+ // lookups for ip6-localhost, with and without search domains appended.
+ dns.clearQueries();
+ result = gethostbyname(kIp6LocalHost);
+ EXPECT_EQ(2U, dns.queries().size()) << dns.dumpQueries();
+ EXPECT_EQ(1U, GetNumQueriesForType(dns, ns_type::ns_t_a, name_ip6_dot)) << dns.dumpQueries();
+ EXPECT_EQ(1U, GetNumQueriesForType(dns, ns_type::ns_t_a, name_ip6_fqdn)) << dns.dumpQueries();
+ ASSERT_TRUE(result == nullptr);
+
+ // Finally, use gethostbyname2() to resolve ip6-localhost to ::1 from
+ // the hosts file.
+ dns.clearQueries();
+ result = gethostbyname2(kIp6LocalHost, AF_INET6);
+ EXPECT_TRUE(dns.queries().empty()) << dns.dumpQueries();
+ ASSERT_FALSE(result == nullptr);
+ ASSERT_EQ(16, result->h_length);
+ ASSERT_FALSE(result->h_addr_list[0] == nullptr);
+ EXPECT_EQ(kIp6LocalHostAddr, ToString(result));
+ EXPECT_TRUE(result->h_addr_list[1] == nullptr);
+}
+
+TEST_F(ResolverTest, GetHostByName_numeric) {
+ // Add a dummy nameserver which shouldn't receive any queries
+ test::DNSResponder dns;
+ StartDns(dns, {});
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork());
+
+ // Numeric v4 address: expect no DNS queries
+ constexpr char numeric_v4[] = "192.168.0.1";
+ const hostent* result = gethostbyname(numeric_v4);
+ EXPECT_EQ(0U, dns.queries().size());
+ ASSERT_FALSE(result == nullptr);
+ ASSERT_EQ(4, result->h_length); // v4
+ ASSERT_FALSE(result->h_addr_list[0] == nullptr);
+ EXPECT_EQ(numeric_v4, ToString(result));
+ EXPECT_TRUE(result->h_addr_list[1] == nullptr);
+
+ // gethostbyname() recognizes a v6 address, and fails with no DNS queries
+ constexpr char numeric_v6[] = "2001:db8::42";
+ dns.clearQueries();
+ result = gethostbyname(numeric_v6);
+ EXPECT_EQ(0U, dns.queries().size());
+ EXPECT_TRUE(result == nullptr);
+
+ // Numeric v6 address with gethostbyname2(): succeeds with no DNS queries
+ dns.clearQueries();
+ result = gethostbyname2(numeric_v6, AF_INET6);
+ EXPECT_EQ(0U, dns.queries().size());
+ ASSERT_FALSE(result == nullptr);
+ ASSERT_EQ(16, result->h_length); // v6
+ ASSERT_FALSE(result->h_addr_list[0] == nullptr);
+ EXPECT_EQ(numeric_v6, ToString(result));
+ EXPECT_TRUE(result->h_addr_list[1] == nullptr);
+
+ // Numeric v6 address with scope work with getaddrinfo(),
+ // but gethostbyname2() does not understand them; it issues two dns
+ // queries, then fails. This hardly ever happens, there's no point
+ // in fixing this. This test simply verifies the current (bogus)
+ // behavior to avoid further regressions (like crashes, or leaks).
+ constexpr char numeric_v6_scope[] = "fe80::1%lo";
+ dns.clearQueries();
+ result = gethostbyname2(numeric_v6_scope, AF_INET6);
+ EXPECT_EQ(2U, dns.queries().size()); // OUCH!
+ ASSERT_TRUE(result == nullptr);
+}
+
+TEST_F(ResolverTest, BinderSerialization) {
+ using android::net::IDnsResolver;
+ std::vector<int> params_offsets = {
+ IDnsResolver::RESOLVER_PARAMS_SAMPLE_VALIDITY,
+ IDnsResolver::RESOLVER_PARAMS_SUCCESS_THRESHOLD,
+ IDnsResolver::RESOLVER_PARAMS_MIN_SAMPLES,
+ IDnsResolver::RESOLVER_PARAMS_MAX_SAMPLES,
+ IDnsResolver::RESOLVER_PARAMS_BASE_TIMEOUT_MSEC,
+ IDnsResolver::RESOLVER_PARAMS_RETRY_COUNT,
+ };
+ const int size = static_cast<int>(params_offsets.size());
+ EXPECT_EQ(size, IDnsResolver::RESOLVER_PARAMS_COUNT);
+ std::sort(params_offsets.begin(), params_offsets.end());
+ for (int i = 0; i < size; ++i) {
+ EXPECT_EQ(params_offsets[i], i);
+ }
+}
+
+TEST_F(ResolverTest, GetHostByName_Binder) {
+ using android::net::IDnsResolver;
+
+ std::vector<std::string> domains = { "example.com" };
+ std::vector<std::unique_ptr<test::DNSResponder>> dns;
+ std::vector<std::string> servers;
+ std::vector<DnsResponderClient::Mapping> mappings;
+ ASSERT_NO_FATAL_FAILURE(mDnsClient.SetupMappings(1, domains, &mappings));
+ ASSERT_NO_FATAL_FAILURE(mDnsClient.SetupDNSServers(4, mappings, &dns, &servers));
+ ASSERT_EQ(1U, mappings.size());
+ const DnsResponderClient::Mapping& mapping = mappings[0];
+
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork(servers, domains, kDefaultParams));
+
+ const hostent* result = gethostbyname(mapping.host.c_str());
+ const size_t total_queries =
+ std::accumulate(dns.begin(), dns.end(), 0, [this, &mapping](size_t total, auto& d) {
+ return total + GetNumQueriesForType(*d, ns_type::ns_t_a, mapping.entry.c_str());
+ });
+
+ EXPECT_LE(1U, total_queries);
+ ASSERT_FALSE(result == nullptr);
+ ASSERT_EQ(4, result->h_length);
+ ASSERT_FALSE(result->h_addr_list[0] == nullptr);
+ EXPECT_EQ(mapping.ip4, ToString(result));
+ EXPECT_TRUE(result->h_addr_list[1] == nullptr);
+
+ std::vector<std::string> res_servers;
+ std::vector<std::string> res_domains;
+ std::vector<std::string> res_tls_servers;
+ res_params res_params;
+ std::vector<ResolverStats> res_stats;
+ int wait_for_pending_req_timeout_count;
+ ASSERT_TRUE(GetResolverInfo(&res_servers, &res_domains, &res_tls_servers, &res_params,
+ &res_stats, &wait_for_pending_req_timeout_count));
+ EXPECT_EQ(servers.size(), res_servers.size());
+ EXPECT_EQ(domains.size(), res_domains.size());
+ EXPECT_EQ(0U, res_tls_servers.size());
+ ASSERT_EQ(static_cast<size_t>(IDnsResolver::RESOLVER_PARAMS_COUNT), kDefaultParams.size());
+ EXPECT_EQ(kDefaultParams[IDnsResolver::RESOLVER_PARAMS_SAMPLE_VALIDITY],
+ res_params.sample_validity);
+ EXPECT_EQ(kDefaultParams[IDnsResolver::RESOLVER_PARAMS_SUCCESS_THRESHOLD],
+ res_params.success_threshold);
+ EXPECT_EQ(kDefaultParams[IDnsResolver::RESOLVER_PARAMS_MIN_SAMPLES], res_params.min_samples);
+ EXPECT_EQ(kDefaultParams[IDnsResolver::RESOLVER_PARAMS_MAX_SAMPLES], res_params.max_samples);
+ EXPECT_EQ(kDefaultParams[IDnsResolver::RESOLVER_PARAMS_BASE_TIMEOUT_MSEC],
+ res_params.base_timeout_msec);
+ EXPECT_EQ(servers.size(), res_stats.size());
+
+ EXPECT_THAT(res_servers, testing::UnorderedElementsAreArray(servers));
+ EXPECT_THAT(res_domains, testing::UnorderedElementsAreArray(domains));
+}
+
+TEST_F(ResolverTest, GetAddrInfo) {
+ constexpr char listen_addr[] = "127.0.0.4";
+ constexpr char listen_addr2[] = "127.0.0.5";
+ constexpr char host_name[] = "howdy.example.com.";
+
+ const std::vector<DnsRecord> records = {
+ {host_name, ns_type::ns_t_a, "1.2.3.4"},
+ {host_name, ns_type::ns_t_aaaa, "::1.2.3.4"},
+ };
+ test::DNSResponder dns(listen_addr);
+ test::DNSResponder dns2(listen_addr2);
+ StartDns(dns, records);
+ StartDns(dns2, records);
+
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork({listen_addr}));
+ dns.clearQueries();
+ dns2.clearQueries();
+
+ ScopedAddrinfo result = safe_getaddrinfo("howdy", nullptr, nullptr);
+ EXPECT_TRUE(result != nullptr);
+ size_t found = GetNumQueries(dns, host_name);
+ EXPECT_LE(1U, found);
+ // Could be A or AAAA
+ std::string result_str = ToString(result);
+ EXPECT_TRUE(result_str == "1.2.3.4" || result_str == "::1.2.3.4")
+ << ", result_str='" << result_str << "'";
+
+ // Verify that the name is cached.
+ size_t old_found = found;
+ result = safe_getaddrinfo("howdy", nullptr, nullptr);
+ EXPECT_TRUE(result != nullptr);
+ found = GetNumQueries(dns, host_name);
+ EXPECT_LE(1U, found);
+ EXPECT_EQ(old_found, found);
+ result_str = ToString(result);
+ EXPECT_TRUE(result_str == "1.2.3.4" || result_str == "::1.2.3.4")
+ << result_str;
+
+ // Change the DNS resolver, ensure that queries are still cached.
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork({listen_addr2}));
+ dns.clearQueries();
+ dns2.clearQueries();
+
+ result = safe_getaddrinfo("howdy", nullptr, nullptr);
+ EXPECT_TRUE(result != nullptr);
+ found = GetNumQueries(dns, host_name);
+ size_t found2 = GetNumQueries(dns2, host_name);
+ EXPECT_EQ(0U, found);
+ EXPECT_LE(0U, found2);
+
+ // Could be A or AAAA
+ result_str = ToString(result);
+ EXPECT_TRUE(result_str == "1.2.3.4" || result_str == "::1.2.3.4")
+ << ", result_str='" << result_str << "'";
+}
+
+TEST_F(ResolverTest, GetAddrInfoV4) {
+ test::DNSResponder dns;
+ StartDns(dns, {{kHelloExampleCom, ns_type::ns_t_a, "1.2.3.5"}});
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork());
+
+ const addrinfo hints = {.ai_family = AF_INET};
+ ScopedAddrinfo result = safe_getaddrinfo("hello", nullptr, &hints);
+ EXPECT_TRUE(result != nullptr);
+ EXPECT_EQ(1U, GetNumQueries(dns, kHelloExampleCom));
+ EXPECT_EQ("1.2.3.5", ToString(result));
+}
+
+TEST_F(ResolverTest, GetAddrInfo_localhost) {
+ // Add a dummy nameserver which shouldn't receive any queries
+ test::DNSResponder dns;
+ StartDns(dns, {});
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork());
+
+ ScopedAddrinfo result = safe_getaddrinfo(kLocalHost, nullptr, nullptr);
+ EXPECT_TRUE(result != nullptr);
+ // Expect no DNS queries; localhost is resolved via /etc/hosts
+ EXPECT_TRUE(dns.queries().empty()) << dns.dumpQueries();
+ EXPECT_EQ(kLocalHostAddr, ToString(result));
+
+ result = safe_getaddrinfo(kIp6LocalHost, nullptr, nullptr);
+ EXPECT_TRUE(result != nullptr);
+ // Expect no DNS queries; ip6-localhost is resolved via /etc/hosts
+ EXPECT_TRUE(dns.queries().empty()) << dns.dumpQueries();
+ EXPECT_EQ(kIp6LocalHostAddr, ToString(result));
+}
+
+// Verify if the resolver correctly handle multiple queries simultaneously
+// step 1: set dns server#1 into deferred responding mode.
+// step 2: thread#1 query "hello.example.com." --> resolver send query to server#1.
+// step 3: thread#2 query "hello.example.com." --> resolver hold the request and wait for
+// response of previous pending query sent by thread#1.
+// step 4: thread#3 query "konbanha.example.com." --> resolver send query to server#3. Server
+// respond to resolver immediately.
+// step 5: check if server#1 get 1 query by thread#1, server#2 get 0 query, server#3 get 1 query.
+// step 6: resume dns server#1 to respond dns query in step#2.
+// step 7: thread#1 and #2 should get returned from DNS query after step#6. Also, check the
+// number of queries in server#2 is 0 to ensure thread#2 does not wake up unexpectedly
+// before signaled by thread#1.
+TEST_F(ResolverTest, GetAddrInfoV4_deferred_resp) {
+ const char* listen_addr1 = "127.0.0.9";
+ const char* listen_addr2 = "127.0.0.10";
+ const char* listen_addr3 = "127.0.0.11";
+ const char* listen_srv = "53";
+ const char* host_name_deferred = "hello.example.com.";
+ const char* host_name_normal = "konbanha.example.com.";
+ test::DNSResponder dns1(listen_addr1, listen_srv, 250, ns_rcode::ns_r_servfail);
+ test::DNSResponder dns2(listen_addr2, listen_srv, 250, ns_rcode::ns_r_servfail);
+ test::DNSResponder dns3(listen_addr3, listen_srv, 250, ns_rcode::ns_r_servfail);
+ dns1.addMapping(host_name_deferred, ns_type::ns_t_a, "1.2.3.4");
+ dns2.addMapping(host_name_deferred, ns_type::ns_t_a, "1.2.3.4");
+ dns3.addMapping(host_name_normal, ns_type::ns_t_a, "1.2.3.5");
+ ASSERT_TRUE(dns1.startServer());
+ ASSERT_TRUE(dns2.startServer());
+ ASSERT_TRUE(dns3.startServer());
+ const std::vector<std::string> servers_for_t1 = {listen_addr1};
+ const std::vector<std::string> servers_for_t2 = {listen_addr2};
+ const std::vector<std::string> servers_for_t3 = {listen_addr3};
+ addrinfo hints = {.ai_family = AF_INET};
+ const std::vector<int> params = {300, 25, 8, 8, 5000};
+ bool t3_task_done = false;
+
+ dns1.setDeferredResp(true);
+ std::thread t1([&, this]() {
+ ASSERT_TRUE(
+ mDnsClient.SetResolversForNetwork(servers_for_t1, kDefaultSearchDomains, params));
+ ScopedAddrinfo result = safe_getaddrinfo(host_name_deferred, nullptr, &hints);
+ // t3's dns query should got returned first
+ EXPECT_TRUE(t3_task_done);
+ EXPECT_EQ(1U, GetNumQueries(dns1, host_name_deferred));
+ EXPECT_TRUE(result != nullptr);
+ EXPECT_EQ("1.2.3.4", ToString(result));
+ });
+
+ // ensuring t1 and t2 handler functions are processed in order
+ usleep(100 * 1000);
+ std::thread t2([&, this]() {
+ ASSERT_TRUE(
+ mDnsClient.SetResolversForNetwork(servers_for_t2, kDefaultSearchDomains, params));
+ ScopedAddrinfo result = safe_getaddrinfo(host_name_deferred, nullptr, &hints);
+ EXPECT_TRUE(t3_task_done);
+ EXPECT_EQ(0U, GetNumQueries(dns2, host_name_deferred));
+ EXPECT_TRUE(result != nullptr);
+ EXPECT_EQ("1.2.3.4", ToString(result));
+
+ std::vector<std::string> res_servers;
+ std::vector<std::string> res_domains;
+ std::vector<std::string> res_tls_servers;
+ res_params res_params;
+ std::vector<ResolverStats> res_stats;
+ int wait_for_pending_req_timeout_count;
+ ASSERT_TRUE(GetResolverInfo(&res_servers, &res_domains, &res_tls_servers, &res_params,
+ &res_stats, &wait_for_pending_req_timeout_count));
+ EXPECT_EQ(0, wait_for_pending_req_timeout_count);
+ });
+
+ // ensuring t2 and t3 handler functions are processed in order
+ usleep(100 * 1000);
+ std::thread t3([&, this]() {
+ ASSERT_TRUE(
+ mDnsClient.SetResolversForNetwork(servers_for_t3, kDefaultSearchDomains, params));
+ ScopedAddrinfo result = safe_getaddrinfo(host_name_normal, nullptr, &hints);
+ EXPECT_EQ(1U, GetNumQueries(dns1, host_name_deferred));
+ EXPECT_EQ(0U, GetNumQueries(dns2, host_name_deferred));
+ EXPECT_EQ(1U, GetNumQueries(dns3, host_name_normal));
+ EXPECT_TRUE(result != nullptr);
+ EXPECT_EQ("1.2.3.5", ToString(result));
+
+ t3_task_done = true;
+ dns1.setDeferredResp(false);
+ });
+ t3.join();
+ t1.join();
+ t2.join();
+}
+
+TEST_F(ResolverTest, GetAddrInfo_cnames) {
+ constexpr char host_name[] = "host.example.com.";
+ test::DNSResponder dns;
+ const std::vector<DnsRecord> records = {
+ {kHelloExampleCom, ns_type::ns_t_cname, "a.example.com."},
+ {"a.example.com.", ns_type::ns_t_cname, "b.example.com."},
+ {"b.example.com.", ns_type::ns_t_cname, "c.example.com."},
+ {"c.example.com.", ns_type::ns_t_cname, "d.example.com."},
+ {"d.example.com.", ns_type::ns_t_cname, "e.example.com."},
+ {"e.example.com.", ns_type::ns_t_cname, host_name},
+ {host_name, ns_type::ns_t_a, "1.2.3.3"},
+ {host_name, ns_type::ns_t_aaaa, "2001:db8::42"},
+ };
+ StartDns(dns, records);
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork());
+
+ addrinfo hints = {.ai_family = AF_INET};
+ ScopedAddrinfo result = safe_getaddrinfo("hello", nullptr, &hints);
+ EXPECT_TRUE(result != nullptr);
+ EXPECT_EQ("1.2.3.3", ToString(result));
+
+ dns.clearQueries();
+ hints = {.ai_family = AF_INET6};
+ result = safe_getaddrinfo("hello", nullptr, &hints);
+ EXPECT_TRUE(result != nullptr);
+ EXPECT_EQ("2001:db8::42", ToString(result));
+}
+
+TEST_F(ResolverTest, GetAddrInfo_cnamesNoIpAddress) {
+ test::DNSResponder dns;
+ const std::vector<DnsRecord> records = {
+ {kHelloExampleCom, ns_type::ns_t_cname, "a.example.com."},
+ };
+ StartDns(dns, records);
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork());
+
+ addrinfo hints = {.ai_family = AF_INET};
+ ScopedAddrinfo result = safe_getaddrinfo("hello", nullptr, &hints);
+ EXPECT_TRUE(result == nullptr);
+
+ dns.clearQueries();
+ hints = {.ai_family = AF_INET6};
+ result = safe_getaddrinfo("hello", nullptr, &hints);
+ EXPECT_TRUE(result == nullptr);
+}
+
+TEST_F(ResolverTest, GetAddrInfo_cnamesIllegalRdata) {
+ test::DNSResponder dns;
+ const std::vector<DnsRecord> records = {
+ {kHelloExampleCom, ns_type::ns_t_cname, ".!#?"},
+ };
+ StartDns(dns, records);
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork());
+
+ addrinfo hints = {.ai_family = AF_INET};
+ ScopedAddrinfo result = safe_getaddrinfo("hello", nullptr, &hints);
+ EXPECT_TRUE(result == nullptr);
+
+ dns.clearQueries();
+ hints = {.ai_family = AF_INET6};
+ result = safe_getaddrinfo("hello", nullptr, &hints);
+ EXPECT_TRUE(result == nullptr);
+}
+
+TEST_F(ResolverTest, MultidomainResolution) {
+ constexpr char host_name[] = "nihao.example2.com.";
+ std::vector<std::string> searchDomains = { "example1.com", "example2.com", "example3.com" };
+
+ test::DNSResponder dns("127.0.0.6");
+ StartDns(dns, {{host_name, ns_type::ns_t_a, "1.2.3.3"}});
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork({"127.0.0.6"}, searchDomains));
+
+ const hostent* result = gethostbyname("nihao");
+
+ EXPECT_EQ(1U, GetNumQueriesForType(dns, ns_type::ns_t_a, host_name));
+ ASSERT_FALSE(result == nullptr);
+ ASSERT_EQ(4, result->h_length);
+ ASSERT_FALSE(result->h_addr_list[0] == nullptr);
+ EXPECT_EQ("1.2.3.3", ToString(result));
+ EXPECT_TRUE(result->h_addr_list[1] == nullptr);
+}
+
+TEST_F(ResolverTest, GetAddrInfoV6_numeric) {
+ constexpr char host_name[] = "ohayou.example.com.";
+ constexpr char numeric_addr[] = "fe80::1%lo";
+
+ test::DNSResponder dns;
+ dns.setResponseProbability(0.0);
+ StartDns(dns, {{host_name, ns_type::ns_t_aaaa, "2001:db8::5"}});
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork());
+
+ addrinfo hints = {.ai_family = AF_INET6};
+ ScopedAddrinfo result = safe_getaddrinfo(numeric_addr, nullptr, &hints);
+ EXPECT_TRUE(result != nullptr);
+ EXPECT_EQ(numeric_addr, ToString(result));
+ EXPECT_TRUE(dns.queries().empty()); // Ensure no DNS queries were sent out
+
+ // Now try a non-numeric hostname query with the AI_NUMERICHOST flag set.
+ // We should fail without sending out a DNS query.
+ hints.ai_flags |= AI_NUMERICHOST;
+ result = safe_getaddrinfo(host_name, nullptr, &hints);
+ EXPECT_TRUE(result == nullptr);
+ EXPECT_TRUE(dns.queries().empty()); // Ensure no DNS queries were sent out
+}
+
+TEST_F(ResolverTest, GetAddrInfoV6_failing) {
+ constexpr char listen_addr0[] = "127.0.0.7";
+ constexpr char listen_addr1[] = "127.0.0.8";
+ const char* host_name = "ohayou.example.com.";
+
+ test::DNSResponder dns0(listen_addr0);
+ test::DNSResponder dns1(listen_addr1);
+ dns0.setResponseProbability(0.0);
+ StartDns(dns0, {{host_name, ns_type::ns_t_aaaa, "2001:db8::5"}});
+ StartDns(dns1, {{host_name, ns_type::ns_t_aaaa, "2001:db8::6"}});
+
+ std::vector<std::string> servers = { listen_addr0, listen_addr1 };
+ // <sample validity in s> <success threshold in percent> <min samples> <max samples>
+ int sample_count = 8;
+ const std::vector<int> params = { 300, 25, sample_count, sample_count };
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork(servers, kDefaultSearchDomains, params));
+
+ // Repeatedly perform resolutions for non-existing domains until MAXNSSAMPLES resolutions have
+ // reached the dns0, which is set to fail. No more requests should then arrive at that server
+ // for the next sample_lifetime seconds.
+ // TODO: This approach is implementation-dependent, change once metrics reporting is available.
+ const addrinfo hints = {.ai_family = AF_INET6};
+ for (int i = 0; i < sample_count; ++i) {
+ std::string domain = StringPrintf("nonexistent%d", i);
+ ScopedAddrinfo result = safe_getaddrinfo(domain.c_str(), nullptr, &hints);
+ }
+ // Due to 100% errors for all possible samples, the server should be ignored from now on and
+ // only the second one used for all following queries, until NSSAMPLE_VALIDITY is reached.
+ dns0.clearQueries();
+ dns1.clearQueries();
+ ScopedAddrinfo result = safe_getaddrinfo("ohayou", nullptr, &hints);
+ EXPECT_TRUE(result != nullptr);
+ EXPECT_EQ(0U, GetNumQueries(dns0, host_name));
+ EXPECT_EQ(1U, GetNumQueries(dns1, host_name));
+}
+
+TEST_F(ResolverTest, GetAddrInfoV6_nonresponsive) {
+ constexpr char listen_addr0[] = "127.0.0.7";
+ constexpr char listen_addr1[] = "127.0.0.8";
+ constexpr char listen_srv[] = "53";
+ constexpr char host_name1[] = "ohayou.example.com.";
+ constexpr char host_name2[] = "ciao.example.com.";
+ const std::vector<DnsRecord> records0 = {
+ {host_name1, ns_type::ns_t_aaaa, "2001:db8::5"},
+ {host_name2, ns_type::ns_t_aaaa, "2001:db8::5"},
+ };
+ const std::vector<DnsRecord> records1 = {
+ {host_name1, ns_type::ns_t_aaaa, "2001:db8::6"},
+ {host_name2, ns_type::ns_t_aaaa, "2001:db8::6"},
+ };
+
+ // dns0 does not respond with 100% probability, while
+ // dns1 responds normally, at least initially.
+ test::DNSResponder dns0(listen_addr0, listen_srv, 250, static_cast<ns_rcode>(-1));
+ test::DNSResponder dns1(listen_addr1, listen_srv, 250, static_cast<ns_rcode>(-1));
+ dns0.setResponseProbability(0.0);
+ StartDns(dns0, records0);
+ StartDns(dns1, records1);
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork({listen_addr0, listen_addr1}));
+
+ const addrinfo hints = {.ai_family = AF_INET6};
+
+ // dns0 will ignore the request, and we'll fallback to dns1 after the first
+ // retry.
+ ScopedAddrinfo result = safe_getaddrinfo(host_name1, nullptr, &hints);
+ EXPECT_TRUE(result != nullptr);
+ EXPECT_EQ(1U, GetNumQueries(dns0, host_name1));
+ EXPECT_EQ(1U, GetNumQueries(dns1, host_name1));
+
+ // Now make dns1 also ignore 100% requests... The resolve should alternate
+ // retries between the nameservers and fail after 4 attempts.
+ dns1.setResponseProbability(0.0);
+ addrinfo* result2 = nullptr;
+ EXPECT_EQ(EAI_NODATA, getaddrinfo(host_name2, nullptr, &hints, &result2));
+ EXPECT_EQ(nullptr, result2);
+ EXPECT_EQ(4U, GetNumQueries(dns0, host_name2));
+ EXPECT_EQ(4U, GetNumQueries(dns1, host_name2));
+}
+
+TEST_F(ResolverTest, GetAddrInfoV6_concurrent) {
+ constexpr char listen_addr0[] = "127.0.0.9";
+ constexpr char listen_addr1[] = "127.0.0.10";
+ constexpr char listen_addr2[] = "127.0.0.11";
+ constexpr char host_name[] = "konbanha.example.com.";
+
+ test::DNSResponder dns0(listen_addr0);
+ test::DNSResponder dns1(listen_addr1);
+ test::DNSResponder dns2(listen_addr2);
+ StartDns(dns0, {{host_name, ns_type::ns_t_aaaa, "2001:db8::5"}});
+ StartDns(dns1, {{host_name, ns_type::ns_t_aaaa, "2001:db8::6"}});
+ StartDns(dns2, {{host_name, ns_type::ns_t_aaaa, "2001:db8::7"}});
+
+ const std::vector<std::string> servers = { listen_addr0, listen_addr1, listen_addr2 };
+ std::vector<std::thread> threads(10);
+ for (std::thread& thread : threads) {
+ thread = std::thread([this, &servers]() {
+ unsigned delay = arc4random_uniform(1*1000*1000); // <= 1s
+ usleep(delay);
+ std::vector<std::string> serverSubset;
+ for (const auto& server : servers) {
+ if (arc4random_uniform(2)) {
+ serverSubset.push_back(server);
+ }
+ }
+ if (serverSubset.empty()) serverSubset = servers;
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork(serverSubset));
+ const addrinfo hints = {.ai_family = AF_INET6};
+ addrinfo* result = nullptr;
+ int rv = getaddrinfo("konbanha", nullptr, &hints, &result);
+ EXPECT_EQ(0, rv) << "error [" << rv << "] " << gai_strerror(rv);
+ if (result) {
+ freeaddrinfo(result);
+ result = nullptr;
+ }
+ });
+ }
+ for (std::thread& thread : threads) {
+ thread.join();
+ }
+
+ std::vector<std::string> res_servers;
+ std::vector<std::string> res_domains;
+ std::vector<std::string> res_tls_servers;
+ res_params res_params;
+ std::vector<ResolverStats> res_stats;
+ int wait_for_pending_req_timeout_count;
+ ASSERT_TRUE(GetResolverInfo(&res_servers, &res_domains, &res_tls_servers, &res_params,
+ &res_stats, &wait_for_pending_req_timeout_count));
+ EXPECT_EQ(0, wait_for_pending_req_timeout_count);
+}
+
+TEST_F(ResolverTest, GetAddrInfoStressTest_Binder_100) {
+ const unsigned num_hosts = 100;
+ const unsigned num_threads = 100;
+ const unsigned num_queries = 100;
+ ASSERT_NO_FATAL_FAILURE(RunGetAddrInfoStressTest_Binder(num_hosts, num_threads, num_queries));
+}
+
+TEST_F(ResolverTest, GetAddrInfoStressTest_Binder_100000) {
+ const unsigned num_hosts = 100000;
+ const unsigned num_threads = 100;
+ const unsigned num_queries = 100;
+ ASSERT_NO_FATAL_FAILURE(RunGetAddrInfoStressTest_Binder(num_hosts, num_threads, num_queries));
+}
+
+TEST_F(ResolverTest, EmptySetup) {
+ using android::net::IDnsResolver;
+ std::vector<std::string> servers;
+ std::vector<std::string> domains;
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork(servers, domains));
+ std::vector<std::string> res_servers;
+ std::vector<std::string> res_domains;
+ std::vector<std::string> res_tls_servers;
+ res_params res_params;
+ std::vector<ResolverStats> res_stats;
+ int wait_for_pending_req_timeout_count;
+ ASSERT_TRUE(GetResolverInfo(&res_servers, &res_domains, &res_tls_servers, &res_params,
+ &res_stats, &wait_for_pending_req_timeout_count));
+ EXPECT_EQ(0U, res_servers.size());
+ EXPECT_EQ(0U, res_domains.size());
+ EXPECT_EQ(0U, res_tls_servers.size());
+ ASSERT_EQ(static_cast<size_t>(IDnsResolver::RESOLVER_PARAMS_COUNT), kDefaultParams.size());
+ EXPECT_EQ(kDefaultParams[IDnsResolver::RESOLVER_PARAMS_SAMPLE_VALIDITY],
+ res_params.sample_validity);
+ EXPECT_EQ(kDefaultParams[IDnsResolver::RESOLVER_PARAMS_SUCCESS_THRESHOLD],
+ res_params.success_threshold);
+ EXPECT_EQ(kDefaultParams[IDnsResolver::RESOLVER_PARAMS_MIN_SAMPLES], res_params.min_samples);
+ EXPECT_EQ(kDefaultParams[IDnsResolver::RESOLVER_PARAMS_MAX_SAMPLES], res_params.max_samples);
+ EXPECT_EQ(kDefaultParams[IDnsResolver::RESOLVER_PARAMS_BASE_TIMEOUT_MSEC],
+ res_params.base_timeout_msec);
+ EXPECT_EQ(kDefaultParams[IDnsResolver::RESOLVER_PARAMS_RETRY_COUNT], res_params.retry_count);
+}
+
+TEST_F(ResolverTest, SearchPathChange) {
+ constexpr char listen_addr[] = "127.0.0.13";
+ constexpr char host_name1[] = "test13.domain1.org.";
+ constexpr char host_name2[] = "test13.domain2.org.";
+ std::vector<std::string> servers = { listen_addr };
+ std::vector<std::string> domains = { "domain1.org" };
+
+ const std::vector<DnsRecord> records = {
+ {host_name1, ns_type::ns_t_aaaa, "2001:db8::13"},
+ {host_name2, ns_type::ns_t_aaaa, "2001:db8::1:13"},
+ };
+ test::DNSResponder dns(listen_addr);
+ StartDns(dns, records);
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork(servers, domains));
+
+ const addrinfo hints = {.ai_family = AF_INET6};
+ ScopedAddrinfo result = safe_getaddrinfo("test13", nullptr, &hints);
+ EXPECT_TRUE(result != nullptr);
+ EXPECT_EQ(1U, dns.queries().size());
+ EXPECT_EQ(1U, GetNumQueries(dns, host_name1));
+ EXPECT_EQ("2001:db8::13", ToString(result));
+
+ // Test that changing the domain search path on its own works.
+ domains = { "domain2.org" };
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork(servers, domains));
+ dns.clearQueries();
+
+ result = safe_getaddrinfo("test13", nullptr, &hints);
+ EXPECT_TRUE(result != nullptr);
+ EXPECT_EQ(1U, dns.queries().size());
+ EXPECT_EQ(1U, GetNumQueries(dns, host_name2));
+ EXPECT_EQ("2001:db8::1:13", ToString(result));
+}
+
+static std::string base64Encode(const std::vector<uint8_t>& input) {
+ size_t out_len;
+ EXPECT_EQ(1, EVP_EncodedLength(&out_len, input.size()));
+ // out_len includes the trailing NULL.
+ uint8_t output_bytes[out_len];
+ EXPECT_EQ(out_len - 1, EVP_EncodeBlock(output_bytes, input.data(), input.size()));
+ return std::string(reinterpret_cast<char*>(output_bytes));
+}
+
+// If we move this function to dns_responder_client, it will complicate the dependency need of
+// dns_tls_frontend.h.
+static void setupTlsServers(const std::vector<std::string>& servers,
+ std::vector<std::unique_ptr<test::DnsTlsFrontend>>* tls,
+ std::vector<std::string>* fingerprints) {
+ constexpr char listen_udp[] = "53";
+ constexpr char listen_tls[] = "853";
+
+ for (const auto& server : servers) {
+ auto t = std::make_unique<test::DnsTlsFrontend>(server, listen_tls, server, listen_udp);
+ t = std::make_unique<test::DnsTlsFrontend>(server, listen_tls, server, listen_udp);
+ t->startServer();
+ fingerprints->push_back(base64Encode(t->fingerprint()));
+ tls->push_back(std::move(t));
+ }
+}
+
+TEST_F(ResolverTest, MaxServerPrune_Binder) {
+ std::vector<std::string> domains;
+ std::vector<std::unique_ptr<test::DNSResponder>> dns;
+ std::vector<std::unique_ptr<test::DnsTlsFrontend>> tls;
+ std::vector<std::string> servers;
+ std::vector<std::string> fingerprints;
+ std::vector<DnsResponderClient::Mapping> mappings;
+
+ for (unsigned i = 0; i < MAXDNSRCH + 1; i++) {
+ domains.push_back(StringPrintf("example%u.com", i));
+ }
+ ASSERT_NO_FATAL_FAILURE(mDnsClient.SetupMappings(1, domains, &mappings));
+ ASSERT_NO_FATAL_FAILURE(mDnsClient.SetupDNSServers(MAXNS + 1, mappings, &dns, &servers));
+ ASSERT_NO_FATAL_FAILURE(setupTlsServers(servers, &tls, &fingerprints));
+
+ ASSERT_TRUE(mDnsClient.SetResolversWithTls(servers, domains, kDefaultParams, "", fingerprints));
+
+ // If the private DNS validation hasn't completed yet before backend DNS servers stop,
+ // TLS servers will get stuck in handleOneRequest(), which causes this test stuck in
+ // ~DnsTlsFrontend() because the TLS server loop threads can't be terminated.
+ // So, wait for private DNS validation done before stopping backend DNS servers.
+ for (int i = 0; i < MAXNS; i++) {
+ ALOGI("Waiting for private DNS validation on %s.", tls[i]->listen_address().c_str());
+ EXPECT_TRUE(tls[i]->waitForQueries(1, 5000));
+ ALOGI("private DNS validation on %s done.", tls[i]->listen_address().c_str());
+ }
+
+ std::vector<std::string> res_servers;
+ std::vector<std::string> res_domains;
+ std::vector<std::string> res_tls_servers;
+ res_params res_params;
+ std::vector<ResolverStats> res_stats;
+ int wait_for_pending_req_timeout_count;
+ ASSERT_TRUE(GetResolverInfo(&res_servers, &res_domains, &res_tls_servers, &res_params,
+ &res_stats, &wait_for_pending_req_timeout_count));
+
+ // Check the size of the stats and its contents.
+ EXPECT_EQ(static_cast<size_t>(MAXNS), res_servers.size());
+ EXPECT_EQ(static_cast<size_t>(MAXNS), res_tls_servers.size());
+ EXPECT_EQ(static_cast<size_t>(MAXDNSRCH), res_domains.size());
+ EXPECT_TRUE(std::equal(servers.begin(), servers.begin() + MAXNS, res_servers.begin()));
+ EXPECT_TRUE(std::equal(servers.begin(), servers.begin() + MAXNS, res_tls_servers.begin()));
+ EXPECT_TRUE(std::equal(domains.begin(), domains.begin() + MAXDNSRCH, res_domains.begin()));
+}
+
+TEST_F(ResolverTest, ResolverStats) {
+ constexpr char listen_addr1[] = "127.0.0.4";
+ constexpr char listen_addr2[] = "127.0.0.5";
+ constexpr char listen_addr3[] = "127.0.0.6";
+
+ // Set server 1 timeout.
+ test::DNSResponder dns1(listen_addr1, "53", 250, static_cast<ns_rcode>(-1));
+ dns1.setResponseProbability(0.0);
+ ASSERT_TRUE(dns1.startServer());
+
+ // Set server 2 responding server failure.
+ test::DNSResponder dns2(listen_addr2);
+ dns2.setResponseProbability(0.0);
+ ASSERT_TRUE(dns2.startServer());
+
+ // Set server 3 workable.
+ test::DNSResponder dns3(listen_addr3);
+ dns3.addMapping(kHelloExampleCom, ns_type::ns_t_a, "1.2.3.4");
+ ASSERT_TRUE(dns3.startServer());
+
+ std::vector<std::string> servers = {listen_addr1, listen_addr2, listen_addr3};
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork(servers));
+
+ dns3.clearQueries();
+ const addrinfo hints = {.ai_family = AF_INET, .ai_socktype = SOCK_DGRAM};
+ ScopedAddrinfo result = safe_getaddrinfo("hello", nullptr, &hints);
+ size_t found = GetNumQueries(dns3, kHelloExampleCom);
+ EXPECT_LE(1U, found);
+ std::string result_str = ToString(result);
+ EXPECT_TRUE(result_str == "1.2.3.4") << ", result_str='" << result_str << "'";
+
+ std::vector<std::string> res_servers;
+ std::vector<std::string> res_domains;
+ std::vector<std::string> res_tls_servers;
+ res_params res_params;
+ std::vector<ResolverStats> res_stats;
+ int wait_for_pending_req_timeout_count;
+ ASSERT_TRUE(GetResolverInfo(&res_servers, &res_domains, &res_tls_servers, &res_params,
+ &res_stats, &wait_for_pending_req_timeout_count));
+
+ EXPECT_EQ(1, res_stats[0].timeouts);
+ EXPECT_EQ(1, res_stats[1].errors);
+ EXPECT_EQ(1, res_stats[2].successes);
+}
+
+// Test what happens if the specified TLS server is nonexistent.
+TEST_F(ResolverTest, GetHostByName_TlsMissing) {
+ constexpr char listen_addr[] = "127.0.0.3";
+ constexpr char host_name[] = "tlsmissing.example.com.";
+
+ test::DNSResponder dns;
+ StartDns(dns, {{host_name, ns_type::ns_t_a, "1.2.3.3"}});
+ std::vector<std::string> servers = { listen_addr };
+
+ // There's nothing listening on this address, so validation will either fail or
+ /// hang. Either way, queries will continue to flow to the DNSResponder.
+ ASSERT_TRUE(
+ mDnsClient.SetResolversWithTls(servers, kDefaultSearchDomains, kDefaultParams, "", {}));
+
+ const hostent* result;
+
+ result = gethostbyname("tlsmissing");
+ ASSERT_FALSE(result == nullptr);
+ EXPECT_EQ("1.2.3.3", ToString(result));
+
+ // Clear TLS bit.
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork());
+}
+
+// Test what happens if the specified TLS server replies with garbage.
+TEST_F(ResolverTest, GetHostByName_TlsBroken) {
+ constexpr char listen_addr[] = "127.0.0.3";
+ constexpr char host_name1[] = "tlsbroken1.example.com.";
+ constexpr char host_name2[] = "tlsbroken2.example.com.";
+ const std::vector<DnsRecord> records = {
+ {host_name1, ns_type::ns_t_a, "1.2.3.1"},
+ {host_name2, ns_type::ns_t_a, "1.2.3.2"},
+ };
+
+ test::DNSResponder dns;
+ StartDns(dns, records);
+ std::vector<std::string> servers = { listen_addr };
+
+ // Bind the specified private DNS socket but don't respond to any client sockets yet.
+ int s = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP);
+ ASSERT_TRUE(s >= 0);
+ struct sockaddr_in tlsServer = {
+ .sin_family = AF_INET,
+ .sin_port = htons(853),
+ };
+ ASSERT_TRUE(inet_pton(AF_INET, listen_addr, &tlsServer.sin_addr));
+ ASSERT_TRUE(enableSockopt(s, SOL_SOCKET, SO_REUSEPORT).ok());
+ ASSERT_TRUE(enableSockopt(s, SOL_SOCKET, SO_REUSEADDR).ok());
+ ASSERT_FALSE(bind(s, reinterpret_cast<struct sockaddr*>(&tlsServer), sizeof(tlsServer)));
+ ASSERT_FALSE(listen(s, 1));
+
+ // Trigger TLS validation.
+ ASSERT_TRUE(
+ mDnsClient.SetResolversWithTls(servers, kDefaultSearchDomains, kDefaultParams, "", {}));
+
+ struct sockaddr_storage cliaddr;
+ socklen_t sin_size = sizeof(cliaddr);
+ int new_fd = accept4(s, reinterpret_cast<struct sockaddr *>(&cliaddr), &sin_size, SOCK_CLOEXEC);
+ ASSERT_TRUE(new_fd > 0);
+
+ // We've received the new file descriptor but not written to it or closed, so the
+ // validation is still pending. Queries should still flow correctly because the
+ // server is not used until validation succeeds.
+ const hostent* result;
+ result = gethostbyname("tlsbroken1");
+ ASSERT_FALSE(result == nullptr);
+ EXPECT_EQ("1.2.3.1", ToString(result));
+
+ // Now we cause the validation to fail.
+ std::string garbage = "definitely not a valid TLS ServerHello";
+ write(new_fd, garbage.data(), garbage.size());
+ close(new_fd);
+
+ // Validation failure shouldn't interfere with lookups, because lookups won't be sent
+ // to the TLS server unless validation succeeds.
+ result = gethostbyname("tlsbroken2");
+ ASSERT_FALSE(result == nullptr);
+ EXPECT_EQ("1.2.3.2", ToString(result));
+
+ // Clear TLS bit.
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork());
+ close(s);
+}
+
+TEST_F(ResolverTest, GetHostByName_Tls) {
+ constexpr char listen_addr[] = "127.0.0.3";
+ constexpr char listen_udp[] = "53";
+ constexpr char listen_tls[] = "853";
+ constexpr char host_name1[] = "tls1.example.com.";
+ constexpr char host_name2[] = "tls2.example.com.";
+ constexpr char host_name3[] = "tls3.example.com.";
+ const std::vector<DnsRecord> records = {
+ {host_name1, ns_type::ns_t_a, "1.2.3.1"},
+ {host_name2, ns_type::ns_t_a, "1.2.3.2"},
+ {host_name3, ns_type::ns_t_a, "1.2.3.3"},
+ };
+
+ test::DNSResponder dns;
+ StartDns(dns, records);
+ std::vector<std::string> servers = { listen_addr };
+
+ test::DnsTlsFrontend tls(listen_addr, listen_tls, listen_addr, listen_udp);
+ ASSERT_TRUE(tls.startServer());
+ ASSERT_TRUE(
+ mDnsClient.SetResolversWithTls(servers, kDefaultSearchDomains, kDefaultParams, "", {}));
+
+ const hostent* result;
+
+ // Wait for validation to complete.
+ EXPECT_TRUE(tls.waitForQueries(1, 5000));
+
+ result = gethostbyname("tls1");
+ ASSERT_FALSE(result == nullptr);
+ EXPECT_EQ("1.2.3.1", ToString(result));
+
+ // Wait for query to get counted.
+ EXPECT_TRUE(tls.waitForQueries(2, 5000));
+
+ // Stop the TLS server. Since we're in opportunistic mode, queries will
+ // fall back to the locally-assigned (clear text) nameservers.
+ tls.stopServer();
+
+ dns.clearQueries();
+ result = gethostbyname("tls2");
+ EXPECT_FALSE(result == nullptr);
+ EXPECT_EQ("1.2.3.2", ToString(result));
+ const auto queries = dns.queries();
+ EXPECT_EQ(1U, queries.size());
+ EXPECT_EQ("tls2.example.com.", queries[0].first);
+ EXPECT_EQ(ns_t_a, queries[0].second);
+
+ // Reset the resolvers without enabling TLS. Queries should still be routed
+ // to the UDP endpoint.
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork());
+
+ result = gethostbyname("tls3");
+ ASSERT_FALSE(result == nullptr);
+ EXPECT_EQ("1.2.3.3", ToString(result));
+}
+
+TEST_F(ResolverTest, GetHostByName_TlsFingerprint) {
+ // Certificate fingerprints are no longer supported by the module.
+ SKIP_IF_RESOLVER_VERSION_NEWER_THAN(mDnsClient.resolvService(), 2);
+
+ constexpr char listen_addr[] = "127.0.0.3";
+ constexpr char listen_udp[] = "53";
+ constexpr char listen_tls[] = "853";
+ test::DNSResponder dns;
+ ASSERT_TRUE(dns.startServer());
+ for (int chain_length = 1; chain_length <= 3; ++chain_length) {
+ std::string host_name = StringPrintf("tlsfingerprint%d.example.com.", chain_length);
+ dns.addMapping(host_name, ns_type::ns_t_a, "1.2.3.1");
+ std::vector<std::string> servers = { listen_addr };
+
+ test::DnsTlsFrontend tls(listen_addr, listen_tls, listen_addr, listen_udp);
+ tls.set_chain_length(chain_length);
+ ASSERT_TRUE(tls.startServer());
+ ASSERT_TRUE(mDnsClient.SetResolversWithTls(servers, kDefaultSearchDomains, kDefaultParams,
+ "", {base64Encode(tls.fingerprint())}));
+
+ const hostent* result;
+
+ // Wait for validation to complete.
+ EXPECT_TRUE(tls.waitForQueries(1, 5000));
+
+ result = gethostbyname(StringPrintf("tlsfingerprint%d", chain_length).c_str());
+ EXPECT_FALSE(result == nullptr);
+ if (result) {
+ EXPECT_EQ("1.2.3.1", ToString(result));
+
+ // Wait for query to get counted.
+ EXPECT_TRUE(tls.waitForQueries(2, 5000));
+ }
+
+ // Clear TLS bit to ensure revalidation.
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork());
+ tls.stopServer();
+ }
+}
+
+TEST_F(ResolverTest, GetHostByName_BadTlsFingerprint) {
+ // Certificate fingerprints are no longer supported by the module.
+ SKIP_IF_RESOLVER_VERSION_NEWER_THAN(mDnsClient.resolvService(), 2);
+
+ constexpr char listen_addr[] = "127.0.0.3";
+ constexpr char listen_udp[] = "53";
+ constexpr char listen_tls[] = "853";
+ constexpr char host_name[] = "badtlsfingerprint.example.com.";
+
+ test::DNSResponder dns;
+ StartDns(dns, {{host_name, ns_type::ns_t_a, "1.2.3.1"}});
+ std::vector<std::string> servers = { listen_addr };
+
+ test::DnsTlsFrontend tls(listen_addr, listen_tls, listen_addr, listen_udp);
+ ASSERT_TRUE(tls.startServer());
+ std::vector<uint8_t> bad_fingerprint = tls.fingerprint();
+ bad_fingerprint[5] += 1; // Corrupt the fingerprint.
+ ASSERT_TRUE(mDnsClient.SetResolversWithTls(servers, kDefaultSearchDomains, kDefaultParams, "",
+ {base64Encode(bad_fingerprint)}));
+
+ // The initial validation should fail at the fingerprint check before
+ // issuing a query.
+ EXPECT_FALSE(tls.waitForQueries(1, 500));
+
+ // A fingerprint was provided and failed to match, so the query should fail.
+ EXPECT_EQ(nullptr, gethostbyname("badtlsfingerprint"));
+
+ // Clear TLS bit.
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork());
+}
+
+// Test that we can pass two different fingerprints, and connection succeeds as long as
+// at least one of them matches the server.
+TEST_F(ResolverTest, GetHostByName_TwoTlsFingerprints) {
+ constexpr char listen_addr[] = "127.0.0.3";
+ constexpr char listen_udp[] = "53";
+ constexpr char listen_tls[] = "853";
+ constexpr char host_name[] = "twotlsfingerprints.example.com.";
+
+ test::DNSResponder dns;
+ StartDns(dns, {{host_name, ns_type::ns_t_a, "1.2.3.1"}});
+ std::vector<std::string> servers = { listen_addr };
+
+ test::DnsTlsFrontend tls(listen_addr, listen_tls, listen_addr, listen_udp);
+ ASSERT_TRUE(tls.startServer());
+ std::vector<uint8_t> bad_fingerprint = tls.fingerprint();
+ bad_fingerprint[5] += 1; // Corrupt the fingerprint.
+ ASSERT_TRUE(mDnsClient.SetResolversWithTls(
+ servers, kDefaultSearchDomains, kDefaultParams, "",
+ {base64Encode(bad_fingerprint), base64Encode(tls.fingerprint())}));
+
+ const hostent* result;
+
+ // Wait for validation to complete.
+ EXPECT_TRUE(tls.waitForQueries(1, 5000));
+
+ result = gethostbyname("twotlsfingerprints");
+ ASSERT_FALSE(result == nullptr);
+ EXPECT_EQ("1.2.3.1", ToString(result));
+
+ // Wait for query to get counted.
+ EXPECT_TRUE(tls.waitForQueries(2, 5000));
+
+ // Clear TLS bit.
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork());
+}
+
+TEST_F(ResolverTest, GetHostByName_TlsFingerprintGoesBad) {
+ // Certificate fingerprints are no longer supported by the module.
+ SKIP_IF_RESOLVER_VERSION_NEWER_THAN(mDnsClient.resolvService(), 2);
+
+ constexpr char listen_addr[] = "127.0.0.3";
+ constexpr char listen_udp[] = "53";
+ constexpr char listen_tls[] = "853";
+ constexpr char host_name1[] = "tlsfingerprintgoesbad1.example.com.";
+ constexpr char host_name2[] = "tlsfingerprintgoesbad2.example.com.";
+ const std::vector<DnsRecord> records = {
+ {host_name1, ns_type::ns_t_a, "1.2.3.1"},
+ {host_name2, ns_type::ns_t_a, "1.2.3.2"},
+ };
+
+ test::DNSResponder dns;
+ StartDns(dns, records);
+ std::vector<std::string> servers = { listen_addr };
+
+ test::DnsTlsFrontend tls(listen_addr, listen_tls, listen_addr, listen_udp);
+ ASSERT_TRUE(tls.startServer());
+ ASSERT_TRUE(mDnsClient.SetResolversWithTls(servers, kDefaultSearchDomains, kDefaultParams, "",
+ {base64Encode(tls.fingerprint())}));
+
+ const hostent* result;
+
+ // Wait for validation to complete.
+ EXPECT_TRUE(tls.waitForQueries(1, 5000));
+
+ result = gethostbyname("tlsfingerprintgoesbad1");
+ ASSERT_FALSE(result == nullptr);
+ EXPECT_EQ("1.2.3.1", ToString(result));
+
+ // Wait for query to get counted.
+ EXPECT_TRUE(tls.waitForQueries(2, 5000));
+
+ // Restart the TLS server. This will generate a new certificate whose fingerprint
+ // no longer matches the stored fingerprint.
+ tls.stopServer();
+ tls.startServer();
+
+ result = gethostbyname("tlsfingerprintgoesbad2");
+ ASSERT_TRUE(result == nullptr);
+ EXPECT_EQ(HOST_NOT_FOUND, h_errno);
+
+ // Clear TLS bit.
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork());
+}
+
+TEST_F(ResolverTest, GetHostByName_TlsFailover) {
+ constexpr char listen_addr1[] = "127.0.0.3";
+ constexpr char listen_addr2[] = "127.0.0.4";
+ constexpr char listen_udp[] = "53";
+ constexpr char listen_tls[] = "853";
+ constexpr char host_name1[] = "tlsfailover1.example.com.";
+ constexpr char host_name2[] = "tlsfailover2.example.com.";
+ const std::vector<DnsRecord> records1 = {
+ {host_name1, ns_type::ns_t_a, "1.2.3.1"},
+ {host_name2, ns_type::ns_t_a, "1.2.3.2"},
+ };
+ const std::vector<DnsRecord> records2 = {
+ {host_name1, ns_type::ns_t_a, "1.2.3.3"},
+ {host_name2, ns_type::ns_t_a, "1.2.3.4"},
+ };
+
+ test::DNSResponder dns1(listen_addr1);
+ test::DNSResponder dns2(listen_addr2);
+ StartDns(dns1, records1);
+ StartDns(dns2, records2);
+
+ std::vector<std::string> servers = { listen_addr1, listen_addr2 };
+
+ test::DnsTlsFrontend tls1(listen_addr1, listen_tls, listen_addr1, listen_udp);
+ test::DnsTlsFrontend tls2(listen_addr2, listen_tls, listen_addr2, listen_udp);
+ ASSERT_TRUE(tls1.startServer());
+ ASSERT_TRUE(tls2.startServer());
+ ASSERT_TRUE(mDnsClient.SetResolversWithTls(
+ servers, kDefaultSearchDomains, kDefaultParams, "",
+ {base64Encode(tls1.fingerprint()), base64Encode(tls2.fingerprint())}));
+
+ const hostent* result;
+
+ // Wait for validation to complete.
+ EXPECT_TRUE(tls1.waitForQueries(1, 5000));
+ EXPECT_TRUE(tls2.waitForQueries(1, 5000));
+
+ result = gethostbyname("tlsfailover1");
+ ASSERT_FALSE(result == nullptr);
+ EXPECT_EQ("1.2.3.1", ToString(result));
+
+ // Wait for query to get counted.
+ EXPECT_TRUE(tls1.waitForQueries(2, 5000));
+ // No new queries should have reached tls2.
+ EXPECT_EQ(1, tls2.queries());
+
+ // Stop tls1. Subsequent queries should attempt to reach tls1, fail, and retry to tls2.
+ tls1.stopServer();
+
+ result = gethostbyname("tlsfailover2");
+ EXPECT_EQ("1.2.3.4", ToString(result));
+
+ // Wait for query to get counted.
+ EXPECT_TRUE(tls2.waitForQueries(2, 5000));
+
+ // No additional queries should have reached the insecure servers.
+ EXPECT_EQ(2U, dns1.queries().size());
+ EXPECT_EQ(2U, dns2.queries().size());
+
+ // Clear TLS bit.
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork(servers));
+}
+
+TEST_F(ResolverTest, GetHostByName_BadTlsName) {
+ constexpr char listen_addr[] = "127.0.0.3";
+ constexpr char listen_udp[] = "53";
+ constexpr char listen_tls[] = "853";
+ constexpr char host_name[] = "badtlsname.example.com.";
+
+ test::DNSResponder dns;
+ StartDns(dns, {{host_name, ns_type::ns_t_a, "1.2.3.1"}});
+ std::vector<std::string> servers = { listen_addr };
+
+ test::DnsTlsFrontend tls(listen_addr, listen_tls, listen_addr, listen_udp);
+ ASSERT_TRUE(tls.startServer());
+ ASSERT_TRUE(mDnsClient.SetResolversWithTls(servers, kDefaultSearchDomains, kDefaultParams,
+ "www.example.com", {}));
+
+ // The TLS server's certificate doesn't chain to a known CA, and a nonempty name was specified,
+ // so the client should fail the TLS handshake before ever issuing a query.
+ EXPECT_FALSE(tls.waitForQueries(1, 500));
+
+ // The query should fail hard, because a name was specified.
+ EXPECT_EQ(nullptr, gethostbyname("badtlsname"));
+
+ // Clear TLS bit.
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork());
+}
+
+TEST_F(ResolverTest, GetAddrInfo_Tls) {
+ constexpr char listen_addr[] = "127.0.0.3";
+ constexpr char listen_udp[] = "53";
+ constexpr char listen_tls[] = "853";
+ constexpr char host_name[] = "addrinfotls.example.com.";
+ const std::vector<DnsRecord> records = {
+ {host_name, ns_type::ns_t_a, "1.2.3.4"},
+ {host_name, ns_type::ns_t_aaaa, "::1.2.3.4"},
+ };
+
+ test::DNSResponder dns;
+ StartDns(dns, records);
+ std::vector<std::string> servers = { listen_addr };
+
+ test::DnsTlsFrontend tls(listen_addr, listen_tls, listen_addr, listen_udp);
+ ASSERT_TRUE(tls.startServer());
+ ASSERT_TRUE(mDnsClient.SetResolversWithTls(servers, kDefaultSearchDomains, kDefaultParams, "",
+ {base64Encode(tls.fingerprint())}));
+
+ // Wait for validation to complete.
+ EXPECT_TRUE(tls.waitForQueries(1, 5000));
+
+ dns.clearQueries();
+ ScopedAddrinfo result = safe_getaddrinfo("addrinfotls", nullptr, nullptr);
+ EXPECT_TRUE(result != nullptr);
+ size_t found = GetNumQueries(dns, host_name);
+ EXPECT_LE(1U, found);
+ // Could be A or AAAA
+ std::string result_str = ToString(result);
+ EXPECT_TRUE(result_str == "1.2.3.4" || result_str == "::1.2.3.4")
+ << ", result_str='" << result_str << "'";
+ // Wait for both A and AAAA queries to get counted.
+ EXPECT_TRUE(tls.waitForQueries(3, 5000));
+
+ // Clear TLS bit.
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork());
+}
+
+TEST_F(ResolverTest, TlsBypass) {
+ // Certificate fingerprints are no longer supported by the module.
+ SKIP_IF_RESOLVER_VERSION_NEWER_THAN(mDnsClient.resolvService(), 2);
+
+ const char OFF[] = "off";
+ const char OPPORTUNISTIC[] = "opportunistic";
+ const char STRICT[] = "strict";
+
+ const char GETHOSTBYNAME[] = "gethostbyname";
+ const char GETADDRINFO[] = "getaddrinfo";
+ const char GETADDRINFOFORNET[] = "getaddrinfofornet";
+
+ const unsigned BYPASS_NETID = NETID_USE_LOCAL_NAMESERVERS | TEST_NETID;
+
+ const std::vector<uint8_t> NOOP_FINGERPRINT(SHA256_SIZE, 0U);
+
+ const char ADDR4[] = "192.0.2.1";
+ const char ADDR6[] = "2001:db8::1";
+
+ const char cleartext_addr[] = "127.0.0.53";
+ const char cleartext_port[] = "53";
+ const char tls_port[] = "853";
+ const std::vector<std::string> servers = { cleartext_addr };
+
+ test::DNSResponder dns(cleartext_addr);
+ ASSERT_TRUE(dns.startServer());
+
+ test::DnsTlsFrontend tls(cleartext_addr, tls_port, cleartext_addr, cleartext_port);
+
+ struct TestConfig {
+ const std::string mode;
+ const bool withWorkingTLS;
+ const std::string method;
+
+ std::string asHostName() const {
+ return StringPrintf("%s.%s.%s.",
+ mode.c_str(),
+ withWorkingTLS ? "tlsOn" : "tlsOff",
+ method.c_str());
+ }
+ } testConfigs[]{
+ {OFF, false, GETHOSTBYNAME},
+ {OPPORTUNISTIC, false, GETHOSTBYNAME},
+ {STRICT, false, GETHOSTBYNAME},
+ {OFF, true, GETHOSTBYNAME},
+ {OPPORTUNISTIC, true, GETHOSTBYNAME},
+ {STRICT, true, GETHOSTBYNAME},
+ {OFF, false, GETADDRINFO},
+ {OPPORTUNISTIC, false, GETADDRINFO},
+ {STRICT, false, GETADDRINFO},
+ {OFF, true, GETADDRINFO},
+ {OPPORTUNISTIC, true, GETADDRINFO},
+ {STRICT, true, GETADDRINFO},
+ {OFF, false, GETADDRINFOFORNET},
+ {OPPORTUNISTIC, false, GETADDRINFOFORNET},
+ {STRICT, false, GETADDRINFOFORNET},
+ {OFF, true, GETADDRINFOFORNET},
+ {OPPORTUNISTIC, true, GETADDRINFOFORNET},
+ {STRICT, true, GETADDRINFOFORNET},
+ };
+
+ for (const auto& config : testConfigs) {
+ const std::string testHostName = config.asHostName();
+ SCOPED_TRACE(testHostName);
+
+ // Don't tempt test bugs due to caching.
+ const char* host_name = testHostName.c_str();
+ dns.addMapping(host_name, ns_type::ns_t_a, ADDR4);
+ dns.addMapping(host_name, ns_type::ns_t_aaaa, ADDR6);
+
+ if (config.withWorkingTLS) ASSERT_TRUE(tls.startServer());
+
+ if (config.mode == OFF) {
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork(servers, kDefaultSearchDomains,
+ kDefaultParams));
+ } else if (config.mode == OPPORTUNISTIC) {
+ ASSERT_TRUE(mDnsClient.SetResolversWithTls(servers, kDefaultSearchDomains,
+ kDefaultParams, "", {}));
+ // Wait for validation to complete.
+ if (config.withWorkingTLS) EXPECT_TRUE(tls.waitForQueries(1, 5000));
+ } else if (config.mode == STRICT) {
+ // We use the existence of fingerprints to trigger strict mode,
+ // rather than hostname validation.
+ const auto& fingerprint =
+ (config.withWorkingTLS) ? tls.fingerprint() : NOOP_FINGERPRINT;
+ ASSERT_TRUE(mDnsClient.SetResolversWithTls(servers, kDefaultSearchDomains,
+ kDefaultParams, "",
+ {base64Encode(fingerprint)}));
+ // Wait for validation to complete.
+ if (config.withWorkingTLS) EXPECT_TRUE(tls.waitForQueries(1, 5000));
+ } else {
+ FAIL() << "Unsupported Private DNS mode: " << config.mode;
+ }
+
+ const int tlsQueriesBefore = tls.queries();
+
+ const hostent* h_result = nullptr;
+ ScopedAddrinfo ai_result;
+
+ if (config.method == GETHOSTBYNAME) {
+ ASSERT_EQ(0, setNetworkForResolv(BYPASS_NETID));
+ h_result = gethostbyname(host_name);
+
+ EXPECT_EQ(1U, GetNumQueriesForType(dns, ns_type::ns_t_a, host_name));
+ ASSERT_FALSE(h_result == nullptr);
+ ASSERT_EQ(4, h_result->h_length);
+ ASSERT_FALSE(h_result->h_addr_list[0] == nullptr);
+ EXPECT_EQ(ADDR4, ToString(h_result));
+ EXPECT_TRUE(h_result->h_addr_list[1] == nullptr);
+ } else if (config.method == GETADDRINFO) {
+ ASSERT_EQ(0, setNetworkForResolv(BYPASS_NETID));
+ ai_result = safe_getaddrinfo(host_name, nullptr, nullptr);
+ EXPECT_TRUE(ai_result != nullptr);
+
+ EXPECT_LE(1U, GetNumQueries(dns, host_name));
+ // Could be A or AAAA
+ const std::string result_str = ToString(ai_result);
+ EXPECT_TRUE(result_str == ADDR4 || result_str == ADDR6)
+ << ", result_str='" << result_str << "'";
+ } else if (config.method == GETADDRINFOFORNET) {
+ addrinfo* raw_ai_result = nullptr;
+ EXPECT_EQ(0, android_getaddrinfofornet(host_name, /*servname=*/nullptr,
+ /*hints=*/nullptr, BYPASS_NETID, MARK_UNSET,
+ &raw_ai_result));
+ ai_result.reset(raw_ai_result);
+
+ EXPECT_LE(1U, GetNumQueries(dns, host_name));
+ // Could be A or AAAA
+ const std::string result_str = ToString(ai_result);
+ EXPECT_TRUE(result_str == ADDR4 || result_str == ADDR6)
+ << ", result_str='" << result_str << "'";
+ } else {
+ FAIL() << "Unsupported query method: " << config.method;
+ }
+
+ const int tlsQueriesAfter = tls.queries();
+ EXPECT_EQ(0, tlsQueriesAfter - tlsQueriesBefore);
+
+ // Clear per-process resolv netid.
+ ASSERT_EQ(0, setNetworkForResolv(NETID_UNSET));
+ tls.stopServer();
+ dns.clearQueries();
+ }
+}
+
+TEST_F(ResolverTest, StrictMode_NoTlsServers) {
+ // Certificate fingerprints are no longer supported by the module.
+ SKIP_IF_RESOLVER_VERSION_NEWER_THAN(mDnsClient.resolvService(), 2);
+
+ const std::vector<uint8_t> NOOP_FINGERPRINT(SHA256_SIZE, 0U);
+ constexpr char cleartext_addr[] = "127.0.0.53";
+ const std::vector<std::string> servers = { cleartext_addr };
+ constexpr char host_name[] = "strictmode.notlsips.example.com.";
+ const std::vector<DnsRecord> records = {
+ {host_name, ns_type::ns_t_a, "1.2.3.4"},
+ {host_name, ns_type::ns_t_aaaa, "::1.2.3.4"},
+ };
+
+ test::DNSResponder dns(cleartext_addr);
+ StartDns(dns, records);
+
+ ASSERT_TRUE(mDnsClient.SetResolversWithTls(servers, kDefaultSearchDomains, kDefaultParams, {},
+ "", {base64Encode(NOOP_FINGERPRINT)}));
+
+ addrinfo* ai_result = nullptr;
+ EXPECT_NE(0, getaddrinfo(host_name, nullptr, nullptr, &ai_result));
+ EXPECT_EQ(0U, GetNumQueries(dns, host_name));
+}
+
+namespace {
+
+int getAsyncResponse(int fd, int* rcode, uint8_t* buf, int bufLen) {
+ struct pollfd wait_fd[1];
+ wait_fd[0].fd = fd;
+ wait_fd[0].events = POLLIN;
+ short revents;
+ int ret;
+
+ ret = poll(wait_fd, 1, -1);
+ revents = wait_fd[0].revents;
+ if (revents & POLLIN) {
+ int n = resNetworkResult(fd, rcode, buf, bufLen);
+ // Verify that resNetworkResult() closed the fd
+ char dummy;
+ EXPECT_EQ(-1, read(fd, &dummy, sizeof dummy));
+ EXPECT_EQ(EBADF, errno);
+ return n;
+ }
+ return -1;
+}
+
+std::string toString(uint8_t* buf, int bufLen, int ipType) {
+ ns_msg handle;
+ int ancount, n = 0;
+ ns_rr rr;
+
+ if (ns_initparse((const uint8_t*) buf, bufLen, &handle) >= 0) {
+ ancount = ns_msg_count(handle, ns_s_an);
+ if (ns_parserr(&handle, ns_s_an, n, &rr) == 0) {
+ const uint8_t* rdata = ns_rr_rdata(rr);
+ char buffer[INET6_ADDRSTRLEN];
+ if (inet_ntop(ipType, (const char*) rdata, buffer, sizeof(buffer))) {
+ return buffer;
+ }
+ }
+ }
+ return "";
+}
+
+int dns_open_proxy() {
+ int s = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
+ if (s == -1) {
+ return -1;
+ }
+ const int one = 1;
+ setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
+
+ static const struct sockaddr_un proxy_addr = {
+ .sun_family = AF_UNIX,
+ .sun_path = "/dev/socket/dnsproxyd",
+ };
+
+ if (TEMP_FAILURE_RETRY(connect(s, (const struct sockaddr*) &proxy_addr, sizeof(proxy_addr))) !=
+ 0) {
+ close(s);
+ return -1;
+ }
+
+ return s;
+}
+
+void expectAnswersValid(int fd, int ipType, const std::string& expectedAnswer) {
+ int rcode = -1;
+ uint8_t buf[MAXPACKET] = {};
+
+ int res = getAsyncResponse(fd, &rcode, buf, MAXPACKET);
+ EXPECT_GT(res, 0);
+ EXPECT_EQ(expectedAnswer, toString(buf, res, ipType));
+}
+
+void expectAnswersNotValid(int fd, int expectedErrno) {
+ int rcode = -1;
+ uint8_t buf[MAXPACKET] = {};
+
+ int res = getAsyncResponse(fd, &rcode, buf, MAXPACKET);
+ EXPECT_EQ(expectedErrno, res);
+}
+
+} // namespace
+
+TEST_F(ResolverTest, Async_NormalQueryV4V6) {
+ constexpr char listen_addr[] = "127.0.0.4";
+ constexpr char host_name[] = "howdy.example.com.";
+ const std::vector<DnsRecord> records = {
+ {host_name, ns_type::ns_t_a, "1.2.3.4"},
+ {host_name, ns_type::ns_t_aaaa, "::1.2.3.4"},
+ };
+
+ test::DNSResponder dns(listen_addr);
+ StartDns(dns, records);
+ std::vector<std::string> servers = {listen_addr};
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork(servers));
+
+ int fd1 = resNetworkQuery(TEST_NETID, "howdy.example.com", ns_c_in, ns_t_a, 0);
+ int fd2 = resNetworkQuery(TEST_NETID, "howdy.example.com", ns_c_in, ns_t_aaaa, 0);
+ EXPECT_TRUE(fd1 != -1);
+ EXPECT_TRUE(fd2 != -1);
+
+ uint8_t buf[MAXPACKET] = {};
+ int rcode;
+ int res = getAsyncResponse(fd2, &rcode, buf, MAXPACKET);
+ EXPECT_GT(res, 0);
+ EXPECT_EQ("::1.2.3.4", toString(buf, res, AF_INET6));
+
+ res = getAsyncResponse(fd1, &rcode, buf, MAXPACKET);
+ EXPECT_GT(res, 0);
+ EXPECT_EQ("1.2.3.4", toString(buf, res, AF_INET));
+
+ EXPECT_EQ(2U, GetNumQueries(dns, host_name));
+
+ // Re-query verify cache works
+ fd1 = resNetworkQuery(TEST_NETID, "howdy.example.com", ns_c_in, ns_t_a, 0);
+ fd2 = resNetworkQuery(TEST_NETID, "howdy.example.com", ns_c_in, ns_t_aaaa, 0);
+
+ EXPECT_TRUE(fd1 != -1);
+ EXPECT_TRUE(fd2 != -1);
+
+ res = getAsyncResponse(fd2, &rcode, buf, MAXPACKET);
+ EXPECT_GT(res, 0);
+ EXPECT_EQ("::1.2.3.4", toString(buf, res, AF_INET6));
+
+ res = getAsyncResponse(fd1, &rcode, buf, MAXPACKET);
+ EXPECT_GT(res, 0);
+ EXPECT_EQ("1.2.3.4", toString(buf, res, AF_INET));
+
+ EXPECT_EQ(2U, GetNumQueries(dns, host_name));
+}
+
+TEST_F(ResolverTest, Async_BadQuery) {
+ constexpr char listen_addr[] = "127.0.0.4";
+ constexpr char host_name[] = "howdy.example.com.";
+ const std::vector<DnsRecord> records = {
+ {host_name, ns_type::ns_t_a, "1.2.3.4"},
+ {host_name, ns_type::ns_t_aaaa, "::1.2.3.4"},
+ };
+
+ test::DNSResponder dns(listen_addr);
+ StartDns(dns, records);
+ std::vector<std::string> servers = {listen_addr};
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork(servers));
+
+ static struct {
+ int fd;
+ const char* dname;
+ const int queryType;
+ const int expectRcode;
+ } kTestData[] = {
+ {-1, "", ns_t_aaaa, 0},
+ {-1, "as65ass46", ns_t_aaaa, 0},
+ {-1, "454564564564", ns_t_aaaa, 0},
+ {-1, "h645235", ns_t_a, 0},
+ {-1, "www.google.com", ns_t_a, 0},
+ };
+
+ for (auto& td : kTestData) {
+ SCOPED_TRACE(td.dname);
+ td.fd = resNetworkQuery(TEST_NETID, td.dname, ns_c_in, td.queryType, 0);
+ EXPECT_TRUE(td.fd != -1);
+ }
+
+ // dns_responder return empty resp(packet only contains query part) with no error currently
+ for (const auto& td : kTestData) {
+ uint8_t buf[MAXPACKET] = {};
+ int rcode;
+ SCOPED_TRACE(td.dname);
+ int res = getAsyncResponse(td.fd, &rcode, buf, MAXPACKET);
+ EXPECT_GT(res, 0);
+ EXPECT_EQ(rcode, td.expectRcode);
+ }
+}
+
+TEST_F(ResolverTest, Async_EmptyAnswer) {
+ constexpr char listen_addr[] = "127.0.0.4";
+ constexpr char host_name[] = "howdy.example.com.";
+ const std::vector<DnsRecord> records = {
+ {host_name, ns_type::ns_t_a, "1.2.3.4"},
+ {host_name, ns_type::ns_t_aaaa, "::1.2.3.4"},
+ };
+
+ test::DNSResponder dns(listen_addr);
+ StartDns(dns, records);
+ std::vector<std::string> servers = {listen_addr};
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork(servers));
+
+ // TODO: Disable retry to make this test explicit.
+ auto& cv = dns.getCv();
+ auto& cvMutex = dns.getCvMutex();
+ int fd1;
+ // Wait on the condition variable to ensure that the DNS server has handled our first query.
+ {
+ std::unique_lock lk(cvMutex);
+ fd1 = resNetworkQuery(TEST_NETID, "howdy.example.com", ns_c_in, ns_t_aaaa, 0);
+ EXPECT_TRUE(fd1 != -1);
+ EXPECT_EQ(std::cv_status::no_timeout, cv.wait_for(lk, std::chrono::seconds(1)));
+ }
+
+ dns.setResponseProbability(0.0);
+
+ int fd2 = resNetworkQuery(TEST_NETID, "howdy.example.com", ns_c_in, ns_t_a, 0);
+ EXPECT_TRUE(fd2 != -1);
+
+ int fd3 = resNetworkQuery(TEST_NETID, "howdy.example.com", ns_c_in, ns_t_a, 0);
+ EXPECT_TRUE(fd3 != -1);
+
+ uint8_t buf[MAXPACKET] = {};
+ int rcode;
+
+ // expect no response
+ int res = getAsyncResponse(fd3, &rcode, buf, MAXPACKET);
+ EXPECT_EQ(-ETIMEDOUT, res);
+
+ // expect no response
+ memset(buf, 0, MAXPACKET);
+ res = getAsyncResponse(fd2, &rcode, buf, MAXPACKET);
+ EXPECT_EQ(-ETIMEDOUT, res);
+
+ dns.setResponseProbability(1.0);
+
+ int fd4 = resNetworkQuery(TEST_NETID, "howdy.example.com", ns_c_in, ns_t_a, 0);
+ EXPECT_TRUE(fd4 != -1);
+
+ memset(buf, 0, MAXPACKET);
+ res = getAsyncResponse(fd4, &rcode, buf, MAXPACKET);
+ EXPECT_GT(res, 0);
+ EXPECT_EQ("1.2.3.4", toString(buf, res, AF_INET));
+
+ memset(buf, 0, MAXPACKET);
+ res = getAsyncResponse(fd1, &rcode, buf, MAXPACKET);
+ EXPECT_GT(res, 0);
+ EXPECT_EQ("::1.2.3.4", toString(buf, res, AF_INET6));
+}
+
+TEST_F(ResolverTest, Async_MalformedQuery) {
+ constexpr char listen_addr[] = "127.0.0.4";
+ constexpr char host_name[] = "howdy.example.com.";
+ const std::vector<DnsRecord> records = {
+ {host_name, ns_type::ns_t_a, "1.2.3.4"},
+ {host_name, ns_type::ns_t_aaaa, "::1.2.3.4"},
+ };
+
+ test::DNSResponder dns(listen_addr);
+ StartDns(dns, records);
+ std::vector<std::string> servers = {listen_addr};
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork(servers));
+
+ int fd = dns_open_proxy();
+ EXPECT_TRUE(fd > 0);
+
+ const std::string badMsg = "16-52512#";
+ static const struct {
+ const std::string cmd;
+ const int expectErr;
+ } kTestData[] = {
+ // Too few arguments
+ {"resnsend " + badMsg + '\0', -EINVAL},
+ // Bad netId
+ {"resnsend badnetId 0 " + badMsg + '\0', -EINVAL},
+ // Bad raw data
+ {"resnsend " + std::to_string(TEST_NETID) + " 0 " + badMsg + '\0', -EILSEQ},
+ };
+
+ for (unsigned int i = 0; i < std::size(kTestData); i++) {
+ auto& td = kTestData[i];
+ SCOPED_TRACE(td.cmd);
+ ssize_t rc = TEMP_FAILURE_RETRY(write(fd, td.cmd.c_str(), td.cmd.size()));
+ EXPECT_EQ(rc, static_cast<ssize_t>(td.cmd.size()));
+
+ int32_t tmp;
+ rc = TEMP_FAILURE_RETRY(read(fd, &tmp, sizeof(tmp)));
+ EXPECT_TRUE(rc > 0);
+ EXPECT_EQ(static_cast<int>(ntohl(tmp)), td.expectErr);
+ }
+ // Normal query with answer buffer
+ // This is raw data of query "howdy.example.com" type 1 class 1
+ std::string query = "81sBAAABAAAAAAAABWhvd2R5B2V4YW1wbGUDY29tAAABAAE=";
+ std::string cmd = "resnsend " + std::to_string(TEST_NETID) + " 0 " + query + '\0';
+ ssize_t rc = TEMP_FAILURE_RETRY(write(fd, cmd.c_str(), cmd.size()));
+ EXPECT_EQ(rc, static_cast<ssize_t>(cmd.size()));
+
+ uint8_t smallBuf[1] = {};
+ int rcode;
+ rc = getAsyncResponse(fd, &rcode, smallBuf, 1);
+ EXPECT_EQ(-EMSGSIZE, rc);
+
+ // Do the normal test with large buffer again
+ fd = dns_open_proxy();
+ EXPECT_TRUE(fd > 0);
+ rc = TEMP_FAILURE_RETRY(write(fd, cmd.c_str(), cmd.size()));
+ EXPECT_EQ(rc, static_cast<ssize_t>(cmd.size()));
+ uint8_t buf[MAXPACKET] = {};
+ rc = getAsyncResponse(fd, &rcode, buf, MAXPACKET);
+ EXPECT_EQ("1.2.3.4", toString(buf, rc, AF_INET));
+}
+
+TEST_F(ResolverTest, Async_CacheFlags) {
+ constexpr char listen_addr[] = "127.0.0.4";
+ constexpr char host_name[] = "howdy.example.com.";
+ constexpr char another_host_name[] = "howdy.example2.com.";
+ const std::vector<DnsRecord> records = {
+ {host_name, ns_type::ns_t_a, "1.2.3.4"},
+ {host_name, ns_type::ns_t_aaaa, "::1.2.3.4"},
+ {another_host_name, ns_type::ns_t_a, "1.2.3.5"},
+ {another_host_name, ns_type::ns_t_aaaa, "::1.2.3.5"},
+ };
+
+ test::DNSResponder dns(listen_addr);
+ StartDns(dns, records);
+ std::vector<std::string> servers = {listen_addr};
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork(servers));
+
+ // ANDROID_RESOLV_NO_CACHE_STORE
+ int fd1 = resNetworkQuery(TEST_NETID, "howdy.example.com", ns_c_in, ns_t_a,
+ ANDROID_RESOLV_NO_CACHE_STORE);
+ EXPECT_TRUE(fd1 != -1);
+ int fd2 = resNetworkQuery(TEST_NETID, "howdy.example.com", ns_c_in, ns_t_a,
+ ANDROID_RESOLV_NO_CACHE_STORE);
+ EXPECT_TRUE(fd2 != -1);
+ int fd3 = resNetworkQuery(TEST_NETID, "howdy.example.com", ns_c_in, ns_t_a,
+ ANDROID_RESOLV_NO_CACHE_STORE);
+ EXPECT_TRUE(fd3 != -1);
+
+ expectAnswersValid(fd3, AF_INET, "1.2.3.4");
+ expectAnswersValid(fd2, AF_INET, "1.2.3.4");
+ expectAnswersValid(fd1, AF_INET, "1.2.3.4");
+
+ // No cache exists, expect 3 queries
+ EXPECT_EQ(3U, GetNumQueries(dns, host_name));
+
+ // Re-query and cache
+ fd1 = resNetworkQuery(TEST_NETID, "howdy.example.com", ns_c_in, ns_t_a, 0);
+
+ EXPECT_TRUE(fd1 != -1);
+
+ expectAnswersValid(fd1, AF_INET, "1.2.3.4");
+
+ // Now we have cache, expect 4 queries
+ EXPECT_EQ(4U, GetNumQueries(dns, host_name));
+
+ // ANDROID_RESOLV_NO_CACHE_LOOKUP
+ fd1 = resNetworkQuery(TEST_NETID, "howdy.example.com", ns_c_in, ns_t_a,
+ ANDROID_RESOLV_NO_CACHE_LOOKUP);
+ fd2 = resNetworkQuery(TEST_NETID, "howdy.example.com", ns_c_in, ns_t_a,
+ ANDROID_RESOLV_NO_CACHE_LOOKUP);
+
+ EXPECT_TRUE(fd1 != -1);
+ EXPECT_TRUE(fd2 != -1);
+
+ expectAnswersValid(fd2, AF_INET, "1.2.3.4");
+ expectAnswersValid(fd1, AF_INET, "1.2.3.4");
+
+ // Skip cache, expect 6 queries
+ EXPECT_EQ(6U, GetNumQueries(dns, host_name));
+
+ // Re-query verify cache works
+ fd1 = resNetworkQuery(TEST_NETID, "howdy.example.com", ns_c_in, ns_t_a,
+ ANDROID_RESOLV_NO_CACHE_STORE);
+ EXPECT_TRUE(fd1 != -1);
+ expectAnswersValid(fd1, AF_INET, "1.2.3.4");
+
+ // Cache hits, expect still 6 queries
+ EXPECT_EQ(6U, GetNumQueries(dns, host_name));
+
+ // Start to verify if ANDROID_RESOLV_NO_CACHE_LOOKUP does write response into cache
+ dns.clearQueries();
+
+ fd1 = resNetworkQuery(TEST_NETID, "howdy.example.com", ns_c_in, ns_t_aaaa,
+ ANDROID_RESOLV_NO_CACHE_LOOKUP);
+ fd2 = resNetworkQuery(TEST_NETID, "howdy.example.com", ns_c_in, ns_t_aaaa,
+ ANDROID_RESOLV_NO_CACHE_LOOKUP);
+
+ EXPECT_TRUE(fd1 != -1);
+ EXPECT_TRUE(fd2 != -1);
+
+ expectAnswersValid(fd2, AF_INET6, "::1.2.3.4");
+ expectAnswersValid(fd1, AF_INET6, "::1.2.3.4");
+
+ // Skip cache, expect 2 queries
+ EXPECT_EQ(2U, GetNumQueries(dns, host_name));
+
+ // Re-query without flags
+ fd1 = resNetworkQuery(TEST_NETID, "howdy.example.com", ns_c_in, ns_t_aaaa, 0);
+ fd2 = resNetworkQuery(TEST_NETID, "howdy.example.com", ns_c_in, ns_t_aaaa, 0);
+
+ EXPECT_TRUE(fd1 != -1);
+ EXPECT_TRUE(fd2 != -1);
+
+ expectAnswersValid(fd2, AF_INET6, "::1.2.3.4");
+ expectAnswersValid(fd1, AF_INET6, "::1.2.3.4");
+
+ // Cache hits, expect still 2 queries
+ EXPECT_EQ(2U, GetNumQueries(dns, host_name));
+
+ // Test both ANDROID_RESOLV_NO_CACHE_STORE and ANDROID_RESOLV_NO_CACHE_LOOKUP are set
+ dns.clearQueries();
+
+ // Make sure that the cache of "howdy.example2.com" exists.
+ fd1 = resNetworkQuery(TEST_NETID, "howdy.example2.com", ns_c_in, ns_t_aaaa, 0);
+ EXPECT_TRUE(fd1 != -1);
+ expectAnswersValid(fd1, AF_INET6, "::1.2.3.5");
+ EXPECT_EQ(1U, GetNumQueries(dns, another_host_name));
+
+ // Re-query with testFlags
+ const int testFlag = ANDROID_RESOLV_NO_CACHE_STORE | ANDROID_RESOLV_NO_CACHE_LOOKUP;
+ fd1 = resNetworkQuery(TEST_NETID, "howdy.example2.com", ns_c_in, ns_t_aaaa, testFlag);
+ EXPECT_TRUE(fd1 != -1);
+ expectAnswersValid(fd1, AF_INET6, "::1.2.3.5");
+ // Expect cache lookup is skipped.
+ EXPECT_EQ(2U, GetNumQueries(dns, another_host_name));
+
+ // Do another query with testFlags
+ fd1 = resNetworkQuery(TEST_NETID, "howdy.example2.com", ns_c_in, ns_t_a, testFlag);
+ EXPECT_TRUE(fd1 != -1);
+ expectAnswersValid(fd1, AF_INET, "1.2.3.5");
+ // Expect cache lookup is skipped.
+ EXPECT_EQ(3U, GetNumQueries(dns, another_host_name));
+
+ // Re-query with no flags
+ fd1 = resNetworkQuery(TEST_NETID, "howdy.example2.com", ns_c_in, ns_t_a, 0);
+ EXPECT_TRUE(fd1 != -1);
+ expectAnswersValid(fd1, AF_INET, "1.2.3.5");
+ // Expect no cache hit because cache storing is also skipped in previous query.
+ EXPECT_EQ(4U, GetNumQueries(dns, another_host_name));
+}
+
+TEST_F(ResolverTest, Async_NoRetryFlag) {
+ constexpr char listen_addr0[] = "127.0.0.4";
+ constexpr char listen_addr1[] = "127.0.0.6";
+ constexpr char host_name[] = "howdy.example.com.";
+ const std::vector<DnsRecord> records = {
+ {host_name, ns_type::ns_t_a, "1.2.3.4"},
+ {host_name, ns_type::ns_t_aaaa, "::1.2.3.4"},
+ };
+
+ test::DNSResponder dns0(listen_addr0);
+ test::DNSResponder dns1(listen_addr1);
+ StartDns(dns0, records);
+ StartDns(dns1, records);
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork({listen_addr0, listen_addr1}));
+
+ dns0.clearQueries();
+ dns1.clearQueries();
+
+ dns0.setResponseProbability(0.0);
+ dns1.setResponseProbability(0.0);
+
+ int fd1 = resNetworkQuery(TEST_NETID, "howdy.example.com", ns_c_in, ns_t_a,
+ ANDROID_RESOLV_NO_RETRY);
+ EXPECT_TRUE(fd1 != -1);
+
+ int fd2 = resNetworkQuery(TEST_NETID, "howdy.example.com", ns_c_in, ns_t_aaaa,
+ ANDROID_RESOLV_NO_RETRY);
+ EXPECT_TRUE(fd2 != -1);
+
+ // expect no response
+ expectAnswersNotValid(fd1, -ETIMEDOUT);
+ expectAnswersNotValid(fd2, -ETIMEDOUT);
+
+ // No retry case, expect total 2 queries. The server is selected randomly.
+ EXPECT_EQ(2U, GetNumQueries(dns0, host_name) + GetNumQueries(dns1, host_name));
+
+ dns0.clearQueries();
+ dns1.clearQueries();
+
+ fd1 = resNetworkQuery(TEST_NETID, "howdy.example.com", ns_c_in, ns_t_a, 0);
+ EXPECT_TRUE(fd1 != -1);
+
+ fd2 = resNetworkQuery(TEST_NETID, "howdy.example.com", ns_c_in, ns_t_aaaa, 0);
+ EXPECT_TRUE(fd2 != -1);
+
+ // expect no response
+ expectAnswersNotValid(fd1, -ETIMEDOUT);
+ expectAnswersNotValid(fd2, -ETIMEDOUT);
+
+ // Retry case, expect 4 queries
+ EXPECT_EQ(4U, GetNumQueries(dns0, host_name));
+ EXPECT_EQ(4U, GetNumQueries(dns1, host_name));
+}
+
+TEST_F(ResolverTest, Async_VerifyQueryID) {
+ constexpr char listen_addr[] = "127.0.0.4";
+ constexpr char host_name[] = "howdy.example.com.";
+ const std::vector<DnsRecord> records = {
+ {host_name, ns_type::ns_t_a, "1.2.3.4"},
+ {host_name, ns_type::ns_t_aaaa, "::1.2.3.4"},
+ };
+
+ test::DNSResponder dns(listen_addr);
+ StartDns(dns, records);
+ std::vector<std::string> servers = {listen_addr};
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork(servers));
+
+ const uint8_t queryBuf1[] = {
+ /* Header */
+ 0x55, 0x66, /* Transaction ID */
+ 0x01, 0x00, /* Flags */
+ 0x00, 0x01, /* Questions */
+ 0x00, 0x00, /* Answer RRs */
+ 0x00, 0x00, /* Authority RRs */
+ 0x00, 0x00, /* Additional RRs */
+ /* Queries */
+ 0x05, 0x68, 0x6f, 0x77, 0x64, 0x79, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65,
+ 0x03, 0x63, 0x6f, 0x6d, 0x00, /* Name */
+ 0x00, 0x01, /* Type */
+ 0x00, 0x01 /* Class */
+ };
+
+ int fd = resNetworkSend(TEST_NETID, queryBuf1, sizeof(queryBuf1), 0);
+ EXPECT_TRUE(fd != -1);
+
+ uint8_t buf[MAXPACKET] = {};
+ int rcode;
+
+ int res = getAsyncResponse(fd, &rcode, buf, MAXPACKET);
+ EXPECT_GT(res, 0);
+ EXPECT_EQ("1.2.3.4", toString(buf, res, AF_INET));
+
+ auto hp = reinterpret_cast<HEADER*>(buf);
+ EXPECT_EQ(21862U, htons(hp->id));
+
+ EXPECT_EQ(1U, GetNumQueries(dns, host_name));
+
+ const uint8_t queryBuf2[] = {
+ /* Header */
+ 0x00, 0x53, /* Transaction ID */
+ 0x01, 0x00, /* Flags */
+ 0x00, 0x01, /* Questions */
+ 0x00, 0x00, /* Answer RRs */
+ 0x00, 0x00, /* Authority RRs */
+ 0x00, 0x00, /* Additional RRs */
+ /* Queries */
+ 0x05, 0x68, 0x6f, 0x77, 0x64, 0x79, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65,
+ 0x03, 0x63, 0x6f, 0x6d, 0x00, /* Name */
+ 0x00, 0x01, /* Type */
+ 0x00, 0x01 /* Class */
+ };
+
+ // Re-query verify cache works and query id is correct
+ fd = resNetworkSend(TEST_NETID, queryBuf2, sizeof(queryBuf2), 0);
+
+ EXPECT_TRUE(fd != -1);
+
+ res = getAsyncResponse(fd, &rcode, buf, MAXPACKET);
+ EXPECT_GT(res, 0);
+ EXPECT_EQ("1.2.3.4", toString(buf, res, AF_INET));
+
+ EXPECT_EQ(0x0053U, htons(hp->id));
+
+ EXPECT_EQ(1U, GetNumQueries(dns, host_name));
+}
+
+// This test checks that the resolver should not generate the request containing OPT RR when using
+// cleartext DNS. If we query the DNS server not supporting EDNS0 and it reponds with
+// FORMERR_ON_EDNS, we will fallback to no EDNS0 and try again. If the server does no response, we
+// won't retry so that we get no answer.
+TEST_F(ResolverTest, BrokenEdns) {
+ // Certificate fingerprints are no longer supported by the module.
+ SKIP_IF_RESOLVER_VERSION_NEWER_THAN(mDnsClient.resolvService(), 2);
+
+ typedef test::DNSResponder::Edns Edns;
+ enum ExpectResult { EXPECT_FAILURE, EXPECT_SUCCESS };
+
+ const char OFF[] = "off";
+ const char OPPORTUNISTIC_UDP[] = "opportunistic_udp";
+ const char OPPORTUNISTIC_TLS[] = "opportunistic_tls";
+ const char STRICT[] = "strict";
+ const char GETHOSTBYNAME[] = "gethostbyname";
+ const char GETADDRINFO[] = "getaddrinfo";
+ const std::vector<uint8_t> NOOP_FINGERPRINT(SHA256_SIZE, 0U);
+ const char ADDR4[] = "192.0.2.1";
+ const char CLEARTEXT_ADDR[] = "127.0.0.53";
+ const char CLEARTEXT_PORT[] = "53";
+ const char TLS_PORT[] = "853";
+ const std::vector<std::string> servers = { CLEARTEXT_ADDR };
+
+ test::DNSResponder dns(CLEARTEXT_ADDR, CLEARTEXT_PORT, 250, ns_rcode::ns_r_servfail);
+ ASSERT_TRUE(dns.startServer());
+
+ test::DnsTlsFrontend tls(CLEARTEXT_ADDR, TLS_PORT, CLEARTEXT_ADDR, CLEARTEXT_PORT);
+
+ static const struct TestConfig {
+ std::string mode;
+ std::string method;
+ Edns edns;
+ ExpectResult expectResult;
+
+ std::string asHostName() const {
+ const char* ednsString;
+ switch (edns) {
+ case Edns::ON:
+ ednsString = "ednsOn";
+ break;
+ case Edns::FORMERR_ON_EDNS:
+ ednsString = "ednsFormerr";
+ break;
+ case Edns::DROP:
+ ednsString = "ednsDrop";
+ break;
+ default:
+ ednsString = "";
+ break;
+ }
+ return StringPrintf("%s.%s.%s.", mode.c_str(), method.c_str(), ednsString);
+ }
+ } testConfigs[] = {
+ // In OPPORTUNISTIC_TLS, we get no answer if the DNS server supports TLS but not EDNS0.
+ // Could such server exist? if so, we might need to fallback to query cleartext DNS.
+ // Another thing is that {OPPORTUNISTIC_TLS, Edns::DROP} and {STRICT, Edns::DROP} are
+ // commented out since TLS timeout is not configurable.
+ // TODO: Uncomment them after TLS timeout is configurable.
+ {OFF, GETHOSTBYNAME, Edns::ON, EXPECT_SUCCESS},
+ {OPPORTUNISTIC_UDP, GETHOSTBYNAME, Edns::ON, EXPECT_SUCCESS},
+ {OPPORTUNISTIC_TLS, GETHOSTBYNAME, Edns::ON, EXPECT_SUCCESS},
+ {STRICT, GETHOSTBYNAME, Edns::ON, EXPECT_SUCCESS},
+ {OFF, GETHOSTBYNAME, Edns::FORMERR_ON_EDNS, EXPECT_SUCCESS},
+ {OPPORTUNISTIC_UDP, GETHOSTBYNAME, Edns::FORMERR_ON_EDNS, EXPECT_SUCCESS},
+ {OPPORTUNISTIC_TLS, GETHOSTBYNAME, Edns::FORMERR_ON_EDNS, EXPECT_FAILURE},
+ {STRICT, GETHOSTBYNAME, Edns::FORMERR_ON_EDNS, EXPECT_FAILURE},
+ {OFF, GETHOSTBYNAME, Edns::DROP, EXPECT_SUCCESS},
+ {OPPORTUNISTIC_UDP, GETHOSTBYNAME, Edns::DROP, EXPECT_SUCCESS},
+ //{OPPORTUNISTIC_TLS, GETHOSTBYNAME, Edns::DROP, EXPECT_FAILURE},
+ //{STRICT, GETHOSTBYNAME, Edns::DROP, EXPECT_FAILURE},
+ {OFF, GETADDRINFO, Edns::ON, EXPECT_SUCCESS},
+ {OPPORTUNISTIC_UDP, GETADDRINFO, Edns::ON, EXPECT_SUCCESS},
+ {OPPORTUNISTIC_TLS, GETADDRINFO, Edns::ON, EXPECT_SUCCESS},
+ {STRICT, GETADDRINFO, Edns::ON, EXPECT_SUCCESS},
+ {OFF, GETADDRINFO, Edns::FORMERR_ON_EDNS, EXPECT_SUCCESS},
+ {OPPORTUNISTIC_UDP, GETADDRINFO, Edns::FORMERR_ON_EDNS, EXPECT_SUCCESS},
+ {OPPORTUNISTIC_TLS, GETADDRINFO, Edns::FORMERR_ON_EDNS, EXPECT_FAILURE},
+ {STRICT, GETADDRINFO, Edns::FORMERR_ON_EDNS, EXPECT_FAILURE},
+ {OFF, GETADDRINFO, Edns::DROP, EXPECT_SUCCESS},
+ {OPPORTUNISTIC_UDP, GETADDRINFO, Edns::DROP, EXPECT_SUCCESS},
+ //{OPPORTUNISTIC_TLS, GETADDRINFO, Edns::DROP, EXPECT_FAILURE},
+ //{STRICT, GETADDRINFO, Edns::DROP, EXPECT_FAILURE},
+ };
+
+ for (const auto& config : testConfigs) {
+ const std::string testHostName = config.asHostName();
+ SCOPED_TRACE(testHostName);
+
+ const char* host_name = testHostName.c_str();
+ dns.addMapping(host_name, ns_type::ns_t_a, ADDR4);
+ dns.setEdns(config.edns);
+
+ if (config.mode == OFF) {
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork(servers));
+ } else if (config.mode == OPPORTUNISTIC_UDP) {
+ ASSERT_TRUE(mDnsClient.SetResolversWithTls(servers, kDefaultSearchDomains,
+ kDefaultParams, "", {}));
+ } else if (config.mode == OPPORTUNISTIC_TLS) {
+ ASSERT_TRUE(tls.startServer());
+ ASSERT_TRUE(mDnsClient.SetResolversWithTls(servers, kDefaultSearchDomains,
+ kDefaultParams, "", {}));
+ // Wait for validation to complete.
+ EXPECT_TRUE(tls.waitForQueries(1, 5000));
+ } else if (config.mode == STRICT) {
+ ASSERT_TRUE(tls.startServer());
+ ASSERT_TRUE(mDnsClient.SetResolversWithTls(servers, kDefaultSearchDomains,
+ kDefaultParams, "",
+ {base64Encode(tls.fingerprint())}));
+ // Wait for validation to complete.
+ EXPECT_TRUE(tls.waitForQueries(1, 5000));
+ }
+
+ if (config.method == GETHOSTBYNAME) {
+ const hostent* h_result = gethostbyname(host_name);
+ if (config.expectResult == EXPECT_SUCCESS) {
+ EXPECT_LE(1U, GetNumQueries(dns, host_name));
+ ASSERT_TRUE(h_result != nullptr);
+ ASSERT_EQ(4, h_result->h_length);
+ ASSERT_FALSE(h_result->h_addr_list[0] == nullptr);
+ EXPECT_EQ(ADDR4, ToString(h_result));
+ EXPECT_TRUE(h_result->h_addr_list[1] == nullptr);
+ } else {
+ EXPECT_EQ(0U, GetNumQueriesForType(dns, ns_type::ns_t_a, host_name));
+ ASSERT_TRUE(h_result == nullptr);
+ ASSERT_EQ(HOST_NOT_FOUND, h_errno);
+ }
+ } else if (config.method == GETADDRINFO) {
+ ScopedAddrinfo ai_result;
+ addrinfo hints = {.ai_family = AF_INET, .ai_socktype = SOCK_DGRAM};
+ ai_result = safe_getaddrinfo(host_name, nullptr, &hints);
+ if (config.expectResult == EXPECT_SUCCESS) {
+ EXPECT_TRUE(ai_result != nullptr);
+ EXPECT_EQ(1U, GetNumQueries(dns, host_name));
+ const std::string result_str = ToString(ai_result);
+ EXPECT_EQ(ADDR4, result_str);
+ } else {
+ EXPECT_TRUE(ai_result == nullptr);
+ EXPECT_EQ(0U, GetNumQueries(dns, host_name));
+ }
+ } else {
+ FAIL() << "Unsupported query method: " << config.method;
+ }
+
+ tls.stopServer();
+ dns.clearQueries();
+ }
+}
+
+// DNS-over-TLS validation success, but server does not respond to TLS query after a while.
+// Resolver should have a reasonable number of retries instead of spinning forever. We don't have
+// an efficient way to know if resolver is stuck in an infinite loop. However, test case will be
+// failed due to timeout.
+TEST_F(ResolverTest, UnstableTls) {
+ const char CLEARTEXT_ADDR[] = "127.0.0.53";
+ const char CLEARTEXT_PORT[] = "53";
+ const char TLS_PORT[] = "853";
+ const char* host_name1 = "nonexistent1.example.com.";
+ const char* host_name2 = "nonexistent2.example.com.";
+ const std::vector<std::string> servers = {CLEARTEXT_ADDR};
+
+ test::DNSResponder dns(CLEARTEXT_ADDR, CLEARTEXT_PORT, 250, ns_rcode::ns_r_servfail);
+ ASSERT_TRUE(dns.startServer());
+ dns.setEdns(test::DNSResponder::Edns::FORMERR_ON_EDNS);
+ test::DnsTlsFrontend tls(CLEARTEXT_ADDR, TLS_PORT, CLEARTEXT_ADDR, CLEARTEXT_PORT);
+ ASSERT_TRUE(tls.startServer());
+ ASSERT_TRUE(
+ mDnsClient.SetResolversWithTls(servers, kDefaultSearchDomains, kDefaultParams, "", {}));
+ // Wait for validation complete.
+ EXPECT_TRUE(tls.waitForQueries(1, 5000));
+ // Shutdown TLS server to get an error. It's similar to no response case but without waiting.
+ tls.stopServer();
+
+ const hostent* h_result = gethostbyname(host_name1);
+ EXPECT_EQ(1U, GetNumQueries(dns, host_name1));
+ ASSERT_TRUE(h_result == nullptr);
+ ASSERT_EQ(HOST_NOT_FOUND, h_errno);
+
+ addrinfo hints = {.ai_family = AF_INET, .ai_socktype = SOCK_DGRAM};
+ ScopedAddrinfo ai_result = safe_getaddrinfo(host_name2, nullptr, &hints);
+ EXPECT_TRUE(ai_result == nullptr);
+ EXPECT_EQ(1U, GetNumQueries(dns, host_name2));
+}
+
+// DNS-over-TLS validation success, but server does not respond to TLS query after a while.
+// Moreover, server responds RCODE=FORMERR even on non-EDNS query.
+TEST_F(ResolverTest, BogusDnsServer) {
+ const char CLEARTEXT_ADDR[] = "127.0.0.53";
+ const char CLEARTEXT_PORT[] = "53";
+ const char TLS_PORT[] = "853";
+ const char* host_name1 = "nonexistent1.example.com.";
+ const char* host_name2 = "nonexistent2.example.com.";
+ const std::vector<std::string> servers = {CLEARTEXT_ADDR};
+
+ test::DNSResponder dns(CLEARTEXT_ADDR, CLEARTEXT_PORT, 250, ns_rcode::ns_r_servfail);
+ ASSERT_TRUE(dns.startServer());
+ test::DnsTlsFrontend tls(CLEARTEXT_ADDR, TLS_PORT, CLEARTEXT_ADDR, CLEARTEXT_PORT);
+ ASSERT_TRUE(tls.startServer());
+ ASSERT_TRUE(
+ mDnsClient.SetResolversWithTls(servers, kDefaultSearchDomains, kDefaultParams, "", {}));
+ // Wait for validation complete.
+ EXPECT_TRUE(tls.waitForQueries(1, 5000));
+ // Shutdown TLS server to get an error. It's similar to no response case but without waiting.
+ tls.stopServer();
+ dns.setEdns(test::DNSResponder::Edns::FORMERR_UNCOND);
+
+ const hostent* h_result = gethostbyname(host_name1);
+ EXPECT_EQ(0U, GetNumQueries(dns, host_name1));
+ ASSERT_TRUE(h_result == nullptr);
+ ASSERT_EQ(HOST_NOT_FOUND, h_errno);
+
+ addrinfo hints = {.ai_family = AF_INET, .ai_socktype = SOCK_DGRAM};
+ ScopedAddrinfo ai_result = safe_getaddrinfo(host_name2, nullptr, &hints);
+ EXPECT_TRUE(ai_result == nullptr);
+ EXPECT_EQ(0U, GetNumQueries(dns, host_name2));
+}
+
+TEST_F(ResolverTest, GetAddrInfo_Dns64Synthesize) {
+ constexpr char listen_addr[] = "::1";
+ constexpr char dns64_name[] = "ipv4only.arpa.";
+ constexpr char host_name[] = "v4only.example.com.";
+ const std::vector<DnsRecord> records = {
+ {dns64_name, ns_type::ns_t_aaaa, "64:ff9b::192.0.0.170"},
+ {host_name, ns_type::ns_t_a, "1.2.3.4"},
+ };
+
+ test::DNSResponder dns(listen_addr);
+ StartDns(dns, records);
+
+ std::vector<std::string> servers = {listen_addr};
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork(servers));
+
+ // Start NAT64 prefix discovery and wait for it to complete.
+ EXPECT_TRUE(mDnsClient.resolvService()->startPrefix64Discovery(TEST_NETID).isOk());
+ EXPECT_TRUE(WaitForPrefix64Detected(TEST_NETID, 1000));
+
+ // hints are necessary in order to let netd know which type of addresses the caller is
+ // interested in.
+ const addrinfo hints = {.ai_family = AF_UNSPEC};
+ ScopedAddrinfo result = safe_getaddrinfo("v4only", nullptr, &hints);
+ EXPECT_TRUE(result != nullptr);
+ // TODO: BUG: there should only be two queries, one AAAA (which returns no records) and one A
+ // (which returns 1.2.3.4). But there is an extra AAAA.
+ EXPECT_EQ(3U, GetNumQueries(dns, host_name));
+
+ std::string result_str = ToString(result);
+ EXPECT_EQ(result_str, "64:ff9b::102:304");
+
+ // Stopping NAT64 prefix discovery disables synthesis.
+ EXPECT_TRUE(mDnsClient.resolvService()->stopPrefix64Discovery(TEST_NETID).isOk());
+ EXPECT_FALSE(WaitForPrefix64Detected(TEST_NETID, 300));
+
+ dns.clearQueries();
+
+ result = safe_getaddrinfo("v4only", nullptr, &hints);
+ EXPECT_TRUE(result != nullptr);
+ // TODO: BUG: there should only be one query, an AAAA (which returns no records), because the
+ // A is already cached. But there is an extra AAAA.
+ EXPECT_EQ(2U, GetNumQueries(dns, host_name));
+
+ result_str = ToString(result);
+ EXPECT_EQ(result_str, "1.2.3.4");
+}
+
+TEST_F(ResolverTest, GetAddrInfo_Dns64QuerySpecified) {
+ constexpr char listen_addr[] = "::1";
+ constexpr char dns64_name[] = "ipv4only.arpa.";
+ constexpr char host_name[] = "v4only.example.com.";
+ const std::vector<DnsRecord> records = {
+ {dns64_name, ns_type::ns_t_aaaa, "64:ff9b::192.0.0.170"},
+ {host_name, ns_type::ns_t_a, "1.2.3.4"},
+ };
+
+ test::DNSResponder dns(listen_addr);
+ StartDns(dns, records);
+ const std::vector<std::string> servers = {listen_addr};
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork(servers));
+
+ // Start NAT64 prefix discovery and wait for it to complete.
+ EXPECT_TRUE(mDnsClient.resolvService()->startPrefix64Discovery(TEST_NETID).isOk());
+ EXPECT_TRUE(WaitForPrefix64Detected(TEST_NETID, 1000));
+
+ // Ensure to synthesize AAAA if AF_INET6 is specified, and not to synthesize AAAA
+ // in AF_INET case.
+ addrinfo hints;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET6;
+ ScopedAddrinfo result = safe_getaddrinfo("v4only", nullptr, &hints);
+ EXPECT_TRUE(result != nullptr);
+ std::string result_str = ToString(result);
+ EXPECT_EQ(result_str, "64:ff9b::102:304");
+
+ hints.ai_family = AF_INET;
+ result = safe_getaddrinfo("v4only", nullptr, &hints);
+ EXPECT_TRUE(result != nullptr);
+ EXPECT_LE(2U, GetNumQueries(dns, host_name));
+ result_str = ToString(result);
+ EXPECT_EQ(result_str, "1.2.3.4");
+}
+
+TEST_F(ResolverTest, GetAddrInfo_Dns64QueryUnspecifiedV6) {
+ constexpr char listen_addr[] = "::1";
+ constexpr char dns64_name[] = "ipv4only.arpa.";
+ constexpr char host_name[] = "v4v6.example.com.";
+ const std::vector<DnsRecord> records = {
+ {dns64_name, ns_type::ns_t_aaaa, "64:ff9b::192.0.0.170"},
+ {host_name, ns_type::ns_t_a, "1.2.3.4"},
+ {host_name, ns_type::ns_t_aaaa, "2001:db8::1.2.3.4"},
+ };
+
+ test::DNSResponder dns(listen_addr);
+ StartDns(dns, records);
+ const std::vector<std::string> servers = {listen_addr};
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork(servers));
+
+ // Start NAT64 prefix discovery and wait for it to complete.
+ EXPECT_TRUE(mDnsClient.resolvService()->startPrefix64Discovery(TEST_NETID).isOk());
+ EXPECT_TRUE(WaitForPrefix64Detected(TEST_NETID, 1000));
+
+ const addrinfo hints = {.ai_family = AF_UNSPEC};
+ ScopedAddrinfo result = safe_getaddrinfo("v4v6", nullptr, &hints);
+ EXPECT_TRUE(result != nullptr);
+ EXPECT_LE(2U, GetNumQueries(dns, host_name));
+
+ // In AF_UNSPEC case, do not synthesize AAAA if there's at least one AAAA answer.
+ const std::vector<std::string> result_strs = ToStrings(result);
+ for (const auto& str : result_strs) {
+ EXPECT_TRUE(str == "1.2.3.4" || str == "2001:db8::102:304")
+ << ", result_str='" << str << "'";
+ }
+}
+
+TEST_F(ResolverTest, GetAddrInfo_Dns64QueryUnspecifiedNoV6) {
+ constexpr char listen_addr[] = "::1";
+ constexpr char dns64_name[] = "ipv4only.arpa.";
+ constexpr char host_name[] = "v4v6.example.com.";
+ const std::vector<DnsRecord> records = {
+ {dns64_name, ns_type::ns_t_aaaa, "64:ff9b::192.0.0.170"},
+ {host_name, ns_type::ns_t_a, "1.2.3.4"},
+ };
+
+ test::DNSResponder dns(listen_addr);
+ StartDns(dns, records);
+ const std::vector<std::string> servers = {listen_addr};
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork(servers));
+
+ // Start NAT64 prefix discovery and wait for it to complete.
+ EXPECT_TRUE(mDnsClient.resolvService()->startPrefix64Discovery(TEST_NETID).isOk());
+ EXPECT_TRUE(WaitForPrefix64Detected(TEST_NETID, 1000));
+
+ const addrinfo hints = {.ai_family = AF_UNSPEC};
+ ScopedAddrinfo result = safe_getaddrinfo("v4v6", nullptr, &hints);
+ EXPECT_TRUE(result != nullptr);
+ EXPECT_LE(2U, GetNumQueries(dns, host_name));
+
+ // In AF_UNSPEC case, synthesize AAAA if there's no AAAA answer.
+ std::string result_str = ToString(result);
+ EXPECT_EQ(result_str, "64:ff9b::102:304");
+}
+
+TEST_F(ResolverTest, GetAddrInfo_Dns64QuerySpecialUseIPv4Addresses) {
+ constexpr char THIS_NETWORK[] = "this_network";
+ constexpr char LOOPBACK[] = "loopback";
+ constexpr char LINK_LOCAL[] = "link_local";
+ constexpr char MULTICAST[] = "multicast";
+ constexpr char LIMITED_BROADCAST[] = "limited_broadcast";
+
+ constexpr char ADDR_THIS_NETWORK[] = "0.0.0.1";
+ constexpr char ADDR_LOOPBACK[] = "127.0.0.1";
+ constexpr char ADDR_LINK_LOCAL[] = "169.254.0.1";
+ constexpr char ADDR_MULTICAST[] = "224.0.0.1";
+ constexpr char ADDR_LIMITED_BROADCAST[] = "255.255.255.255";
+
+ constexpr char listen_addr[] = "::1";
+ constexpr char dns64_name[] = "ipv4only.arpa.";
+
+ test::DNSResponder dns(listen_addr);
+ StartDns(dns, {{dns64_name, ns_type::ns_t_aaaa, "64:ff9b::"}});
+ const std::vector<std::string> servers = {listen_addr};
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork(servers));
+
+ // Start NAT64 prefix discovery and wait for it to complete.
+ EXPECT_TRUE(mDnsClient.resolvService()->startPrefix64Discovery(TEST_NETID).isOk());
+ EXPECT_TRUE(WaitForPrefix64Detected(TEST_NETID, 1000));
+
+ static const struct TestConfig {
+ std::string name;
+ std::string addr;
+
+ std::string asHostName() const { return StringPrintf("%s.example.com.", name.c_str()); }
+ } testConfigs[]{
+ {THIS_NETWORK, ADDR_THIS_NETWORK},
+ {LOOPBACK, ADDR_LOOPBACK},
+ {LINK_LOCAL, ADDR_LINK_LOCAL},
+ {MULTICAST, ADDR_MULTICAST},
+ {LIMITED_BROADCAST, ADDR_LIMITED_BROADCAST}
+ };
+
+ for (const auto& config : testConfigs) {
+ const std::string testHostName = config.asHostName();
+ SCOPED_TRACE(testHostName);
+
+ const char* host_name = testHostName.c_str();
+ dns.addMapping(host_name, ns_type::ns_t_a, config.addr.c_str());
+
+ addrinfo hints;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET6;
+ ScopedAddrinfo result = safe_getaddrinfo(config.name.c_str(), nullptr, &hints);
+ // In AF_INET6 case, don't return IPv4 answers
+ EXPECT_TRUE(result == nullptr);
+ EXPECT_LE(2U, GetNumQueries(dns, host_name));
+ dns.clearQueries();
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ result = safe_getaddrinfo(config.name.c_str(), nullptr, &hints);
+ EXPECT_TRUE(result != nullptr);
+ // Expect IPv6 query only. IPv4 answer has been cached in previous query.
+ EXPECT_LE(1U, GetNumQueries(dns, host_name));
+ // In AF_UNSPEC case, don't synthesize special use IPv4 address.
+ std::string result_str = ToString(result);
+ EXPECT_EQ(result_str, config.addr.c_str());
+ dns.clearQueries();
+ }
+}
+
+TEST_F(ResolverTest, GetAddrInfo_Dns64QueryWithNullArgumentHints) {
+ constexpr char listen_addr[] = "::1";
+ constexpr char dns64_name[] = "ipv4only.arpa.";
+ constexpr char host_name[] = "v4only.example.com.";
+ constexpr char host_name2[] = "v4v6.example.com.";
+ const std::vector<DnsRecord> records = {
+ {dns64_name, ns_type::ns_t_aaaa, "64:ff9b::192.0.0.170"},
+ {host_name, ns_type::ns_t_a, "1.2.3.4"},
+ {host_name2, ns_type::ns_t_a, "1.2.3.4"},
+ {host_name2, ns_type::ns_t_aaaa, "2001:db8::1.2.3.4"},
+ };
+
+ test::DNSResponder dns(listen_addr);
+ StartDns(dns, records);
+ const std::vector<std::string> servers = {listen_addr};
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork(servers));
+
+ // Start NAT64 prefix discovery and wait for it to complete.
+ EXPECT_TRUE(mDnsClient.resolvService()->startPrefix64Discovery(TEST_NETID).isOk());
+ EXPECT_TRUE(WaitForPrefix64Detected(TEST_NETID, 1000));
+
+ // Assign argument hints of getaddrinfo() as null is equivalent to set ai_family AF_UNSPEC.
+ // In AF_UNSPEC case, synthesize AAAA if there has A answer only.
+ ScopedAddrinfo result = safe_getaddrinfo("v4only", nullptr, nullptr);
+ EXPECT_TRUE(result != nullptr);
+ EXPECT_LE(2U, GetNumQueries(dns, host_name));
+ std::string result_str = ToString(result);
+ EXPECT_EQ(result_str, "64:ff9b::102:304");
+ dns.clearQueries();
+
+ // In AF_UNSPEC case, do not synthesize AAAA if there's at least one AAAA answer.
+ result = safe_getaddrinfo("v4v6", nullptr, nullptr);
+ EXPECT_TRUE(result != nullptr);
+ EXPECT_LE(2U, GetNumQueries(dns, host_name2));
+ std::vector<std::string> result_strs = ToStrings(result);
+ for (const auto& str : result_strs) {
+ EXPECT_TRUE(str == "1.2.3.4" || str == "2001:db8::102:304")
+ << ", result_str='" << str << "'";
+ }
+}
+
+TEST_F(ResolverTest, GetAddrInfo_Dns64QueryNullArgumentNode) {
+ constexpr char ADDR_ANYADDR_V4[] = "0.0.0.0";
+ constexpr char ADDR_ANYADDR_V6[] = "::";
+ constexpr char ADDR_LOCALHOST_V4[] = "127.0.0.1";
+ constexpr char ADDR_LOCALHOST_V6[] = "::1";
+
+ constexpr char PORT_NAME_HTTP[] = "http";
+ constexpr char PORT_NUMBER_HTTP[] = "80";
+
+ constexpr char listen_addr[] = "::1";
+ constexpr char dns64_name[] = "ipv4only.arpa.";
+
+ test::DNSResponder dns(listen_addr);
+ StartDns(dns, {{dns64_name, ns_type::ns_t_aaaa, "64:ff9b::"}});
+ const std::vector<std::string> servers = {listen_addr};
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork(servers));
+
+ // Start NAT64 prefix discovery and wait for it to complete.
+ EXPECT_TRUE(mDnsClient.resolvService()->startPrefix64Discovery(TEST_NETID).isOk());
+ EXPECT_TRUE(WaitForPrefix64Detected(TEST_NETID, 1000));
+
+ // If node is null, return address is listed by libc/getaddrinfo.c as follows.
+ // - passive socket -> anyaddr (0.0.0.0 or ::)
+ // - non-passive socket -> localhost (127.0.0.1 or ::1)
+ static const struct TestConfig {
+ int flag;
+ std::string addr_v4;
+ std::string addr_v6;
+
+ std::string asParameters() const {
+ return StringPrintf("flag=%d, addr_v4=%s, addr_v6=%s", flag, addr_v4.c_str(),
+ addr_v6.c_str());
+ }
+ } testConfigs[]{
+ {0 /* non-passive */, ADDR_LOCALHOST_V4, ADDR_LOCALHOST_V6},
+ {AI_PASSIVE, ADDR_ANYADDR_V4, ADDR_ANYADDR_V6}
+ };
+
+ for (const auto& config : testConfigs) {
+ SCOPED_TRACE(config.asParameters());
+
+ addrinfo hints = {
+ .ai_family = AF_UNSPEC, // any address family
+ .ai_socktype = 0, // any type
+ .ai_protocol = 0, // any protocol
+ .ai_flags = config.flag,
+ };
+
+ // Assign hostname as null and service as port name.
+ ScopedAddrinfo result = safe_getaddrinfo(nullptr, PORT_NAME_HTTP, &hints);
+ ASSERT_TRUE(result != nullptr);
+
+ // Can't be synthesized because it should not get into Netd.
+ std::vector<std::string> result_strs = ToStrings(result);
+ for (const auto& str : result_strs) {
+ EXPECT_TRUE(str == config.addr_v4 || str == config.addr_v6)
+ << ", result_str='" << str << "'";
+ }
+
+ // Assign hostname as null and service as numeric port number.
+ hints.ai_flags = config.flag | AI_NUMERICSERV;
+ result = safe_getaddrinfo(nullptr, PORT_NUMBER_HTTP, &hints);
+ ASSERT_TRUE(result != nullptr);
+
+ // Can't be synthesized because it should not get into Netd.
+ result_strs = ToStrings(result);
+ for (const auto& str : result_strs) {
+ EXPECT_TRUE(str == config.addr_v4 || str == config.addr_v6)
+ << ", result_str='" << str << "'";
+ }
+ }
+}
+
+TEST_F(ResolverTest, GetHostByAddr_ReverseDnsQueryWithHavingNat64Prefix) {
+ struct hostent* result = nullptr;
+ struct in_addr v4addr;
+ struct in6_addr v6addr;
+
+ constexpr char listen_addr[] = "::1";
+ constexpr char dns64_name[] = "ipv4only.arpa.";
+ constexpr char ptr_name[] = "v4v6.example.com.";
+ // PTR record for IPv4 address 1.2.3.4
+ constexpr char ptr_addr_v4[] = "4.3.2.1.in-addr.arpa.";
+ // PTR record for IPv6 address 2001:db8::102:304
+ constexpr char ptr_addr_v6[] =
+ "4.0.3.0.2.0.1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.";
+ const std::vector<DnsRecord> records = {
+ {dns64_name, ns_type::ns_t_aaaa, "64:ff9b::192.0.0.170"},
+ {ptr_addr_v4, ns_type::ns_t_ptr, ptr_name},
+ {ptr_addr_v6, ns_type::ns_t_ptr, ptr_name},
+ };
+
+ test::DNSResponder dns(listen_addr);
+ StartDns(dns, records);
+ const std::vector<std::string> servers = {listen_addr};
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork(servers));
+
+ // Start NAT64 prefix discovery and wait for it to complete.
+ EXPECT_TRUE(mDnsClient.resolvService()->startPrefix64Discovery(TEST_NETID).isOk());
+ EXPECT_TRUE(WaitForPrefix64Detected(TEST_NETID, 1000));
+
+ // Reverse IPv4 DNS query. Prefix should have no effect on it.
+ inet_pton(AF_INET, "1.2.3.4", &v4addr);
+ result = gethostbyaddr(&v4addr, sizeof(v4addr), AF_INET);
+ ASSERT_TRUE(result != nullptr);
+ std::string result_str = result->h_name ? result->h_name : "null";
+ EXPECT_EQ(result_str, "v4v6.example.com");
+
+ // Reverse IPv6 DNS query. Prefix should have no effect on it.
+ inet_pton(AF_INET6, "2001:db8::102:304", &v6addr);
+ result = gethostbyaddr(&v6addr, sizeof(v6addr), AF_INET6);
+ ASSERT_TRUE(result != nullptr);
+ result_str = result->h_name ? result->h_name : "null";
+ EXPECT_EQ(result_str, "v4v6.example.com");
+}
+
+TEST_F(ResolverTest, GetHostByAddr_ReverseDns64Query) {
+ constexpr char listen_addr[] = "::1";
+ constexpr char dns64_name[] = "ipv4only.arpa.";
+ constexpr char ptr_name[] = "v4only.example.com.";
+ // PTR record for IPv4 address 1.2.3.4
+ constexpr char ptr_addr_v4[] = "4.3.2.1.in-addr.arpa.";
+ // PTR record for IPv6 address 64:ff9b::1.2.3.4
+ constexpr char ptr_addr_v6_nomapping[] =
+ "4.0.3.0.2.0.1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.b.9.f.f.4.6.0.0.ip6.arpa.";
+ constexpr char ptr_name_v6_synthesis[] = "v6synthesis.example.com.";
+ // PTR record for IPv6 address 64:ff9b::5.6.7.8
+ constexpr char ptr_addr_v6_synthesis[] =
+ "8.0.7.0.6.0.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.b.9.f.f.4.6.0.0.ip6.arpa.";
+ const std::vector<DnsRecord> records = {
+ {dns64_name, ns_type::ns_t_aaaa, "64:ff9b::192.0.0.170"},
+ {ptr_addr_v4, ns_type::ns_t_ptr, ptr_name},
+ {ptr_addr_v6_synthesis, ns_type::ns_t_ptr, ptr_name_v6_synthesis},
+ };
+
+ test::DNSResponder dns(listen_addr);
+ StartDns(dns, records);
+ // "ptr_addr_v6_nomapping" is not mapped in DNS server
+ const std::vector<std::string> servers = {listen_addr};
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork(servers));
+
+ // Start NAT64 prefix discovery and wait for it to complete.
+ EXPECT_TRUE(mDnsClient.resolvService()->startPrefix64Discovery(TEST_NETID).isOk());
+ EXPECT_TRUE(WaitForPrefix64Detected(TEST_NETID, 1000));
+
+ // Synthesized PTR record doesn't exist on DNS server
+ // Reverse IPv6 DNS64 query while DNS server doesn't have an answer for synthesized address.
+ // After querying synthesized address failed, expect that prefix is removed from IPv6
+ // synthesized address and do reverse IPv4 query instead.
+ struct in6_addr v6addr;
+ inet_pton(AF_INET6, "64:ff9b::1.2.3.4", &v6addr);
+ struct hostent* result = gethostbyaddr(&v6addr, sizeof(v6addr), AF_INET6);
+ ASSERT_TRUE(result != nullptr);
+ EXPECT_LE(1U, GetNumQueries(dns, ptr_addr_v6_nomapping)); // PTR record not exist
+ EXPECT_LE(1U, GetNumQueries(dns, ptr_addr_v4)); // PTR record exist
+ std::string result_str = result->h_name ? result->h_name : "null";
+ EXPECT_EQ(result_str, "v4only.example.com");
+ // Check that return address has been mapped from IPv4 to IPv6 address because Netd
+ // removes NAT64 prefix and does IPv4 DNS reverse lookup in this case. Then, Netd
+ // fakes the return IPv4 address as original queried IPv6 address.
+ result_str = ToString(result);
+ EXPECT_EQ(result_str, "64:ff9b::102:304");
+ dns.clearQueries();
+
+ // Synthesized PTR record exists on DNS server
+ // Reverse IPv6 DNS64 query while DNS server has an answer for synthesized address.
+ // Expect to Netd pass through synthesized address for DNS queries.
+ inet_pton(AF_INET6, "64:ff9b::5.6.7.8", &v6addr);
+ result = gethostbyaddr(&v6addr, sizeof(v6addr), AF_INET6);
+ ASSERT_TRUE(result != nullptr);
+ EXPECT_LE(1U, GetNumQueries(dns, ptr_addr_v6_synthesis));
+ result_str = result->h_name ? result->h_name : "null";
+ EXPECT_EQ(result_str, "v6synthesis.example.com");
+}
+
+TEST_F(ResolverTest, GetHostByAddr_ReverseDns64QueryFromHostFile) {
+ constexpr char dns64_name[] = "ipv4only.arpa.";
+ constexpr char host_name[] = "localhost";
+ // The address is synthesized by prefix64:localhost.
+ constexpr char host_addr[] = "64:ff9b::7f00:1";
+ constexpr char listen_addr[] = "::1";
+
+ test::DNSResponder dns(listen_addr);
+ StartDns(dns, {{dns64_name, ns_type::ns_t_aaaa, "64:ff9b::192.0.0.170"}});
+ const std::vector<std::string> servers = {listen_addr};
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork(servers));
+
+ // Start NAT64 prefix discovery and wait for it to complete.
+ EXPECT_TRUE(mDnsClient.resolvService()->startPrefix64Discovery(TEST_NETID).isOk());
+ EXPECT_TRUE(WaitForPrefix64Detected(TEST_NETID, 1000));
+
+ // Using synthesized "localhost" address to be a trick for resolving host name
+ // from host file /etc/hosts and "localhost" is the only name in /etc/hosts. Note that this is
+ // not realistic: the code never synthesizes AAAA records for addresses in 127.0.0.0/8.
+ struct in6_addr v6addr;
+ inet_pton(AF_INET6, host_addr, &v6addr);
+ struct hostent* result = gethostbyaddr(&v6addr, sizeof(v6addr), AF_INET6);
+ ASSERT_TRUE(result != nullptr);
+ // Expect no DNS queries; localhost is resolved via /etc/hosts.
+ EXPECT_EQ(0U, GetNumQueries(dns, host_name));
+
+ ASSERT_EQ(sizeof(in6_addr), (unsigned) result->h_length);
+ ASSERT_EQ(AF_INET6, result->h_addrtype);
+ std::string result_str = ToString(result);
+ EXPECT_EQ(result_str, host_addr);
+ result_str = result->h_name ? result->h_name : "null";
+ EXPECT_EQ(result_str, host_name);
+}
+
+TEST_F(ResolverTest, GetNameInfo_ReverseDnsQueryWithHavingNat64Prefix) {
+ constexpr char listen_addr[] = "::1";
+ constexpr char dns64_name[] = "ipv4only.arpa.";
+ constexpr char ptr_name[] = "v4v6.example.com.";
+ // PTR record for IPv4 address 1.2.3.4
+ constexpr char ptr_addr_v4[] = "4.3.2.1.in-addr.arpa.";
+ // PTR record for IPv6 address 2001:db8::102:304
+ constexpr char ptr_addr_v6[] =
+ "4.0.3.0.2.0.1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.";
+ const std::vector<DnsRecord> records = {
+ {dns64_name, ns_type::ns_t_aaaa, "64:ff9b::192.0.0.170"},
+ {ptr_addr_v4, ns_type::ns_t_ptr, ptr_name},
+ {ptr_addr_v6, ns_type::ns_t_ptr, ptr_name},
+ };
+
+ test::DNSResponder dns(listen_addr);
+ StartDns(dns, records);
+ const std::vector<std::string> servers = {listen_addr};
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork(servers));
+
+ // Start NAT64 prefix discovery and wait for it to complete.
+ EXPECT_TRUE(mDnsClient.resolvService()->startPrefix64Discovery(TEST_NETID).isOk());
+ EXPECT_TRUE(WaitForPrefix64Detected(TEST_NETID, 1000));
+
+ static const struct TestConfig {
+ int flag;
+ int family;
+ std::string addr;
+ std::string host;
+
+ std::string asParameters() const {
+ return StringPrintf("flag=%d, family=%d, addr=%s, host=%s", flag, family, addr.c_str(),
+ host.c_str());
+ }
+ } testConfigs[]{
+ {NI_NAMEREQD, AF_INET, "1.2.3.4", "v4v6.example.com"},
+ {NI_NUMERICHOST, AF_INET, "1.2.3.4", "1.2.3.4"},
+ {0, AF_INET, "1.2.3.4", "v4v6.example.com"},
+ {0, AF_INET, "5.6.7.8", "5.6.7.8"}, // unmapped
+ {NI_NAMEREQD, AF_INET6, "2001:db8::102:304", "v4v6.example.com"},
+ {NI_NUMERICHOST, AF_INET6, "2001:db8::102:304", "2001:db8::102:304"},
+ {0, AF_INET6, "2001:db8::102:304", "v4v6.example.com"},
+ {0, AF_INET6, "2001:db8::506:708", "2001:db8::506:708"}, // unmapped
+ };
+
+ // Reverse IPv4/IPv6 DNS query. Prefix should have no effect on it.
+ for (const auto& config : testConfigs) {
+ SCOPED_TRACE(config.asParameters());
+
+ int rv;
+ char host[NI_MAXHOST];
+ struct sockaddr_in sin;
+ struct sockaddr_in6 sin6;
+ if (config.family == AF_INET) {
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ inet_pton(AF_INET, config.addr.c_str(), &sin.sin_addr);
+ rv = getnameinfo((const struct sockaddr*) &sin, sizeof(sin), host, sizeof(host),
+ nullptr, 0, config.flag);
+ if (config.flag == NI_NAMEREQD) EXPECT_LE(1U, GetNumQueries(dns, ptr_addr_v4));
+ } else if (config.family == AF_INET6) {
+ memset(&sin6, 0, sizeof(sin6));
+ sin6.sin6_family = AF_INET6;
+ inet_pton(AF_INET6, config.addr.c_str(), &sin6.sin6_addr);
+ rv = getnameinfo((const struct sockaddr*) &sin6, sizeof(sin6), host, sizeof(host),
+ nullptr, 0, config.flag);
+ if (config.flag == NI_NAMEREQD) EXPECT_LE(1U, GetNumQueries(dns, ptr_addr_v6));
+ }
+ ASSERT_EQ(0, rv);
+ std::string result_str = host;
+ EXPECT_EQ(result_str, config.host);
+ dns.clearQueries();
+ }
+}
+
+TEST_F(ResolverTest, GetNameInfo_ReverseDns64Query) {
+ constexpr char listen_addr[] = "::1";
+ constexpr char dns64_name[] = "ipv4only.arpa.";
+ constexpr char ptr_name[] = "v4only.example.com.";
+ // PTR record for IPv4 address 1.2.3.4
+ constexpr char ptr_addr_v4[] = "4.3.2.1.in-addr.arpa.";
+ // PTR record for IPv6 address 64:ff9b::1.2.3.4
+ constexpr char ptr_addr_v6_nomapping[] =
+ "4.0.3.0.2.0.1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.b.9.f.f.4.6.0.0.ip6.arpa.";
+ constexpr char ptr_name_v6_synthesis[] = "v6synthesis.example.com.";
+ // PTR record for IPv6 address 64:ff9b::5.6.7.8
+ constexpr char ptr_addr_v6_synthesis[] =
+ "8.0.7.0.6.0.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.b.9.f.f.4.6.0.0.ip6.arpa.";
+ const std::vector<DnsRecord> records = {
+ {dns64_name, ns_type::ns_t_aaaa, "64:ff9b::192.0.0.170"},
+ {ptr_addr_v4, ns_type::ns_t_ptr, ptr_name},
+ {ptr_addr_v6_synthesis, ns_type::ns_t_ptr, ptr_name_v6_synthesis},
+ };
+
+ test::DNSResponder dns(listen_addr);
+ StartDns(dns, records);
+ const std::vector<std::string> servers = {listen_addr};
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork(servers));
+
+ // Start NAT64 prefix discovery and wait for it to complete.
+ EXPECT_TRUE(mDnsClient.resolvService()->startPrefix64Discovery(TEST_NETID).isOk());
+ EXPECT_TRUE(WaitForPrefix64Detected(TEST_NETID, 1000));
+
+ static const struct TestConfig {
+ bool hasSynthesizedPtrRecord;
+ int flag;
+ std::string addr;
+ std::string host;
+
+ std::string asParameters() const {
+ return StringPrintf("hasSynthesizedPtrRecord=%d, flag=%d, addr=%s, host=%s",
+ hasSynthesizedPtrRecord, flag, addr.c_str(), host.c_str());
+ }
+ } testConfigs[]{
+ {false, NI_NAMEREQD, "64:ff9b::102:304", "v4only.example.com"},
+ {false, NI_NUMERICHOST, "64:ff9b::102:304", "64:ff9b::102:304"},
+ {false, 0, "64:ff9b::102:304", "v4only.example.com"},
+ {true, NI_NAMEREQD, "64:ff9b::506:708", "v6synthesis.example.com"},
+ {true, NI_NUMERICHOST, "64:ff9b::506:708", "64:ff9b::506:708"},
+ {true, 0, "64:ff9b::506:708", "v6synthesis.example.com"}
+ };
+
+ // hasSynthesizedPtrRecord = false
+ // Synthesized PTR record doesn't exist on DNS server
+ // Reverse IPv6 DNS64 query while DNS server doesn't have an answer for synthesized address.
+ // After querying synthesized address failed, expect that prefix is removed from IPv6
+ // synthesized address and do reverse IPv4 query instead.
+ //
+ // hasSynthesizedPtrRecord = true
+ // Synthesized PTR record exists on DNS server
+ // Reverse IPv6 DNS64 query while DNS server has an answer for synthesized address.
+ // Expect to just pass through synthesized address for DNS queries.
+ for (const auto& config : testConfigs) {
+ SCOPED_TRACE(config.asParameters());
+
+ char host[NI_MAXHOST];
+ struct sockaddr_in6 sin6;
+ memset(&sin6, 0, sizeof(sin6));
+ sin6.sin6_family = AF_INET6;
+ inet_pton(AF_INET6, config.addr.c_str(), &sin6.sin6_addr);
+ int rv = getnameinfo((const struct sockaddr*) &sin6, sizeof(sin6), host, sizeof(host),
+ nullptr, 0, config.flag);
+ ASSERT_EQ(0, rv);
+ if (config.flag == NI_NAMEREQD) {
+ if (config.hasSynthesizedPtrRecord) {
+ EXPECT_LE(1U, GetNumQueries(dns, ptr_addr_v6_synthesis));
+ } else {
+ EXPECT_LE(1U, GetNumQueries(dns, ptr_addr_v6_nomapping)); // PTR record not exist.
+ EXPECT_LE(1U, GetNumQueries(dns, ptr_addr_v4)); // PTR record exist.
+ }
+ }
+ std::string result_str = host;
+ EXPECT_EQ(result_str, config.host);
+ dns.clearQueries();
+ }
+}
+
+TEST_F(ResolverTest, GetNameInfo_ReverseDns64QueryFromHostFile) {
+ constexpr char dns64_name[] = "ipv4only.arpa.";
+ constexpr char host_name[] = "localhost";
+ // The address is synthesized by prefix64:localhost.
+ constexpr char host_addr[] = "64:ff9b::7f00:1";
+ constexpr char listen_addr[] = "::1";
+
+ test::DNSResponder dns(listen_addr);
+ StartDns(dns, {{dns64_name, ns_type::ns_t_aaaa, "64:ff9b::192.0.0.170"}});
+ const std::vector<std::string> servers = {listen_addr};
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork(servers));
+
+ // Start NAT64 prefix discovery and wait for it to complete.
+ EXPECT_TRUE(mDnsClient.resolvService()->startPrefix64Discovery(TEST_NETID).isOk());
+ EXPECT_TRUE(WaitForPrefix64Detected(TEST_NETID, 1000));
+
+ // Using synthesized "localhost" address to be a trick for resolving host name
+ // from host file /etc/hosts and "localhost" is the only name in /etc/hosts. Note that this is
+ // not realistic: the code never synthesizes AAAA records for addresses in 127.0.0.0/8.
+ char host[NI_MAXHOST];
+ struct sockaddr_in6 sin6 = {.sin6_family = AF_INET6};
+ inet_pton(AF_INET6, host_addr, &sin6.sin6_addr);
+ int rv = getnameinfo((const struct sockaddr*) &sin6, sizeof(sin6), host, sizeof(host), nullptr,
+ 0, NI_NAMEREQD);
+ ASSERT_EQ(0, rv);
+ // Expect no DNS queries; localhost is resolved via /etc/hosts.
+ EXPECT_EQ(0U, GetNumQueries(dns, host_name));
+
+ std::string result_str = host;
+ EXPECT_EQ(result_str, host_name);
+}
+
+TEST_F(ResolverTest, GetHostByName2_Dns64Synthesize) {
+ constexpr char listen_addr[] = "::1";
+ constexpr char dns64_name[] = "ipv4only.arpa.";
+ constexpr char host_name[] = "ipv4only.example.com.";
+ const std::vector<DnsRecord> records = {
+ {dns64_name, ns_type::ns_t_aaaa, "64:ff9b::192.0.0.170"},
+ {host_name, ns_type::ns_t_a, "1.2.3.4"},
+ };
+
+ test::DNSResponder dns(listen_addr);
+ StartDns(dns, records);
+ const std::vector<std::string> servers = {listen_addr};
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork(servers));
+
+ // Start NAT64 prefix discovery and wait for it to complete.
+ EXPECT_TRUE(mDnsClient.resolvService()->startPrefix64Discovery(TEST_NETID).isOk());
+ EXPECT_TRUE(WaitForPrefix64Detected(TEST_NETID, 1000));
+
+ // Query an IPv4-only hostname. Expect that gets a synthesized address.
+ struct hostent* result = gethostbyname2("ipv4only", AF_INET6);
+ ASSERT_TRUE(result != nullptr);
+ EXPECT_LE(1U, GetNumQueries(dns, host_name));
+ std::string result_str = ToString(result);
+ EXPECT_EQ(result_str, "64:ff9b::102:304");
+}
+
+TEST_F(ResolverTest, GetHostByName2_DnsQueryWithHavingNat64Prefix) {
+ constexpr char dns64_name[] = "ipv4only.arpa.";
+ constexpr char host_name[] = "v4v6.example.com.";
+ constexpr char listen_addr[] = "::1";
+ const std::vector<DnsRecord> records = {
+ {dns64_name, ns_type::ns_t_aaaa, "64:ff9b::192.0.0.170"},
+ {host_name, ns_type::ns_t_a, "1.2.3.4"},
+ {host_name, ns_type::ns_t_aaaa, "2001:db8::1.2.3.4"},
+ };
+
+ test::DNSResponder dns(listen_addr);
+ StartDns(dns, records);
+ const std::vector<std::string> servers = {listen_addr};
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork(servers));
+
+ // Start NAT64 prefix discovery and wait for it to complete.
+ EXPECT_TRUE(mDnsClient.resolvService()->startPrefix64Discovery(TEST_NETID).isOk());
+ EXPECT_TRUE(WaitForPrefix64Detected(TEST_NETID, 1000));
+
+ // IPv4 DNS query. Prefix should have no effect on it.
+ struct hostent* result = gethostbyname2("v4v6", AF_INET);
+ ASSERT_TRUE(result != nullptr);
+ EXPECT_LE(1U, GetNumQueries(dns, host_name));
+ std::string result_str = ToString(result);
+ EXPECT_EQ(result_str, "1.2.3.4");
+ dns.clearQueries();
+
+ // IPv6 DNS query. Prefix should have no effect on it.
+ result = gethostbyname2("v4v6", AF_INET6);
+ ASSERT_TRUE(result != nullptr);
+ EXPECT_LE(1U, GetNumQueries(dns, host_name));
+ result_str = ToString(result);
+ EXPECT_EQ(result_str, "2001:db8::102:304");
+}
+
+TEST_F(ResolverTest, GetHostByName2_Dns64QuerySpecialUseIPv4Addresses) {
+ constexpr char THIS_NETWORK[] = "this_network";
+ constexpr char LOOPBACK[] = "loopback";
+ constexpr char LINK_LOCAL[] = "link_local";
+ constexpr char MULTICAST[] = "multicast";
+ constexpr char LIMITED_BROADCAST[] = "limited_broadcast";
+
+ constexpr char ADDR_THIS_NETWORK[] = "0.0.0.1";
+ constexpr char ADDR_LOOPBACK[] = "127.0.0.1";
+ constexpr char ADDR_LINK_LOCAL[] = "169.254.0.1";
+ constexpr char ADDR_MULTICAST[] = "224.0.0.1";
+ constexpr char ADDR_LIMITED_BROADCAST[] = "255.255.255.255";
+
+ constexpr char listen_addr[] = "::1";
+ constexpr char dns64_name[] = "ipv4only.arpa.";
+
+ test::DNSResponder dns(listen_addr);
+ StartDns(dns, {{dns64_name, ns_type::ns_t_aaaa, "64:ff9b::"}});
+ const std::vector<std::string> servers = {listen_addr};
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork(servers));
+
+ // Start NAT64 prefix discovery and wait for it to complete.
+ EXPECT_TRUE(mDnsClient.resolvService()->startPrefix64Discovery(TEST_NETID).isOk());
+ EXPECT_TRUE(WaitForPrefix64Detected(TEST_NETID, 1000));
+
+ static const struct TestConfig {
+ std::string name;
+ std::string addr;
+
+ std::string asHostName() const {
+ return StringPrintf("%s.example.com.",
+ name.c_str());
+ }
+ } testConfigs[]{
+ {THIS_NETWORK, ADDR_THIS_NETWORK},
+ {LOOPBACK, ADDR_LOOPBACK},
+ {LINK_LOCAL, ADDR_LINK_LOCAL},
+ {MULTICAST, ADDR_MULTICAST},
+ {LIMITED_BROADCAST, ADDR_LIMITED_BROADCAST}
+ };
+
+ for (const auto& config : testConfigs) {
+ const std::string testHostName = config.asHostName();
+ SCOPED_TRACE(testHostName);
+
+ const char* host_name = testHostName.c_str();
+ dns.addMapping(host_name, ns_type::ns_t_a, config.addr.c_str());
+
+ struct hostent* result = gethostbyname2(config.name.c_str(), AF_INET6);
+ EXPECT_LE(1U, GetNumQueries(dns, host_name));
+
+ // In AF_INET6 case, don't synthesize special use IPv4 address.
+ // Expect to have no answer
+ EXPECT_EQ(nullptr, result);
+
+ dns.clearQueries();
+ }
+}
+
+TEST_F(ResolverTest, PrefixDiscoveryBypassTls) {
+ constexpr char listen_addr[] = "::1";
+ constexpr char cleartext_port[] = "53";
+ constexpr char tls_port[] = "853";
+ constexpr char dns64_name[] = "ipv4only.arpa.";
+ const std::vector<std::string> servers = {listen_addr};
+
+ test::DNSResponder dns(listen_addr);
+ StartDns(dns, {{dns64_name, ns_type::ns_t_aaaa, "64:ff9b::192.0.0.170"}});
+ test::DnsTlsFrontend tls(listen_addr, tls_port, listen_addr, cleartext_port);
+ ASSERT_TRUE(tls.startServer());
+
+ // Setup OPPORTUNISTIC mode and wait for the validation complete.
+ ASSERT_TRUE(
+ mDnsClient.SetResolversWithTls(servers, kDefaultSearchDomains, kDefaultParams, "", {}));
+ EXPECT_TRUE(tls.waitForQueries(1, 5000));
+ tls.clearQueries();
+
+ // Start NAT64 prefix discovery and wait for it complete.
+ EXPECT_TRUE(mDnsClient.resolvService()->startPrefix64Discovery(TEST_NETID).isOk());
+ EXPECT_TRUE(WaitForPrefix64Detected(TEST_NETID, 1000));
+
+ // Verify it bypassed TLS even though there's a TLS server available.
+ EXPECT_EQ(0, tls.queries());
+ EXPECT_EQ(1U, GetNumQueries(dns, dns64_name));
+
+ // Restart the testing network to reset the cache.
+ mDnsClient.TearDown();
+ mDnsClient.SetUp();
+ dns.clearQueries();
+
+ // Setup STRICT mode and wait for the validation complete.
+ ASSERT_TRUE(mDnsClient.SetResolversWithTls(servers, kDefaultSearchDomains, kDefaultParams, "",
+ {base64Encode(tls.fingerprint())}));
+ EXPECT_TRUE(tls.waitForQueries(1, 5000));
+ tls.clearQueries();
+
+ // Start NAT64 prefix discovery and wait for it to complete.
+ EXPECT_TRUE(mDnsClient.resolvService()->startPrefix64Discovery(TEST_NETID).isOk());
+ EXPECT_TRUE(WaitForPrefix64Detected(TEST_NETID, 1000));
+
+ // Verify it bypassed TLS despite STRICT mode.
+ EXPECT_EQ(0, tls.queries());
+ EXPECT_EQ(1U, GetNumQueries(dns, dns64_name));
+}
+
+namespace {
+
+class ScopedSetNetworkForProcess {
+ public:
+ explicit ScopedSetNetworkForProcess(unsigned netId) {
+ mStoredNetId = getNetworkForProcess();
+ if (netId == mStoredNetId) return;
+ EXPECT_EQ(0, setNetworkForProcess(netId));
+ }
+ ~ScopedSetNetworkForProcess() { EXPECT_EQ(0, setNetworkForProcess(mStoredNetId)); }
+
+ private:
+ unsigned mStoredNetId;
+};
+
+class ScopedSetNetworkForResolv {
+ public:
+ explicit ScopedSetNetworkForResolv(unsigned netId) { EXPECT_EQ(0, setNetworkForResolv(netId)); }
+ ~ScopedSetNetworkForResolv() { EXPECT_EQ(0, setNetworkForResolv(NETID_UNSET)); }
+};
+
+void sendCommand(int fd, const std::string& cmd) {
+ ssize_t rc = TEMP_FAILURE_RETRY(write(fd, cmd.c_str(), cmd.size() + 1));
+ EXPECT_EQ(rc, static_cast<ssize_t>(cmd.size() + 1));
+}
+
+int32_t readBE32(int fd) {
+ int32_t tmp;
+ int n = TEMP_FAILURE_RETRY(read(fd, &tmp, sizeof(tmp)));
+ EXPECT_TRUE(n > 0);
+ return ntohl(tmp);
+}
+
+int readResponseCode(int fd) {
+ char buf[4];
+ int n = TEMP_FAILURE_RETRY(read(fd, &buf, sizeof(buf)));
+ EXPECT_TRUE(n > 0);
+ // The format of response code is that 4 bytes for the code & null.
+ buf[3] = '\0';
+ int result;
+ EXPECT_TRUE(ParseInt(buf, &result));
+ return result;
+}
+
+bool checkAndClearUseLocalNameserversFlag(unsigned* netid) {
+ if (netid == nullptr || ((*netid) & NETID_USE_LOCAL_NAMESERVERS) == 0) {
+ return false;
+ }
+ *netid = (*netid) & ~NETID_USE_LOCAL_NAMESERVERS;
+ return true;
+}
+
+android::net::UidRangeParcel makeUidRangeParcel(int start, int stop) {
+ android::net::UidRangeParcel res;
+ res.start = start;
+ res.stop = stop;
+
+ return res;
+}
+
+void expectNetIdWithLocalNameserversFlag(unsigned netId) {
+ unsigned dnsNetId = 0;
+ EXPECT_EQ(0, getNetworkForDns(&dnsNetId));
+ EXPECT_TRUE(checkAndClearUseLocalNameserversFlag(&dnsNetId));
+ EXPECT_EQ(netId, static_cast<unsigned>(dnsNetId));
+}
+
+void expectDnsNetIdEquals(unsigned netId) {
+ unsigned dnsNetId = 0;
+ EXPECT_EQ(0, getNetworkForDns(&dnsNetId));
+ EXPECT_EQ(netId, static_cast<unsigned>(dnsNetId));
+}
+
+void expectDnsNetIdIsDefaultNetwork(android::net::INetd* netdService) {
+ int currentNetid;
+ EXPECT_TRUE(netdService->networkGetDefault(¤tNetid).isOk());
+ expectDnsNetIdEquals(currentNetid);
+}
+
+void expectDnsNetIdWithVpn(android::net::INetd* netdService, unsigned vpnNetId,
+ unsigned expectedNetId) {
+ EXPECT_TRUE(netdService->networkCreateVpn(vpnNetId, false /* secure */).isOk());
+ uid_t uid = getuid();
+ // Add uid to VPN
+ EXPECT_TRUE(netdService->networkAddUidRanges(vpnNetId, {makeUidRangeParcel(uid, uid)}).isOk());
+ expectDnsNetIdEquals(expectedNetId);
+ EXPECT_TRUE(netdService->networkDestroy(vpnNetId).isOk());
+}
+
+} // namespace
+
+TEST_F(ResolverTest, getDnsNetId) {
+ // We've called setNetworkForProcess in SetupOemNetwork, so reset to default first.
+ setNetworkForProcess(NETID_UNSET);
+
+ expectDnsNetIdIsDefaultNetwork(mDnsClient.netdService());
+ expectDnsNetIdWithVpn(mDnsClient.netdService(), TEST_VPN_NETID, TEST_VPN_NETID);
+
+ // Test with setNetworkForProcess
+ {
+ ScopedSetNetworkForProcess scopedSetNetworkForProcess(TEST_NETID);
+ expectDnsNetIdEquals(TEST_NETID);
+ }
+
+ // Test with setNetworkForProcess with NETID_USE_LOCAL_NAMESERVERS
+ {
+ ScopedSetNetworkForProcess scopedSetNetworkForProcess(TEST_NETID |
+ NETID_USE_LOCAL_NAMESERVERS);
+ expectNetIdWithLocalNameserversFlag(TEST_NETID);
+ }
+
+ // Test with setNetworkForResolv
+ {
+ ScopedSetNetworkForResolv scopedSetNetworkForResolv(TEST_NETID);
+ expectDnsNetIdEquals(TEST_NETID);
+ }
+
+ // Test with setNetworkForResolv with NETID_USE_LOCAL_NAMESERVERS
+ {
+ ScopedSetNetworkForResolv scopedSetNetworkForResolv(TEST_NETID |
+ NETID_USE_LOCAL_NAMESERVERS);
+ expectNetIdWithLocalNameserversFlag(TEST_NETID);
+ }
+
+ // Test with setNetworkForResolv under bypassable vpn
+ {
+ ScopedSetNetworkForResolv scopedSetNetworkForResolv(TEST_NETID);
+ expectDnsNetIdWithVpn(mDnsClient.netdService(), TEST_VPN_NETID, TEST_NETID);
+ }
+
+ // Create socket connected to DnsProxyListener
+ int fd = dns_open_proxy();
+ EXPECT_TRUE(fd > 0);
+ unique_fd ufd(fd);
+
+ // Test command with wrong netId
+ sendCommand(fd, "getdnsnetid abc");
+ EXPECT_EQ(ResponseCode::DnsProxyQueryResult, readResponseCode(fd));
+ EXPECT_EQ(-EINVAL, readBE32(fd));
+
+ // Test unsupported command
+ sendCommand(fd, "getdnsnetidNotSupported");
+ // Keep in sync with FrameworkListener.cpp (500, "Command not recognized")
+ EXPECT_EQ(500, readResponseCode(fd));
+}
diff --git a/resolv/sethostent.cpp b/resolv/sethostent.cpp
new file mode 100644
index 0000000..2885fbc
--- /dev/null
+++ b/resolv/sethostent.cpp
@@ -0,0 +1,204 @@
+/* $NetBSD: sethostent.c,v 1.20 2014/03/17 13:24:23 christos Exp $ */
+
+/*
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+#include <assert.h>
+#include <errno.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <resolv.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/param.h>
+
+#include "hostent.h"
+#include "resolv_private.h"
+
+#define ALIGNBYTES (sizeof(uintptr_t) - 1)
+#define ALIGN(p) (((uintptr_t)(p) + ALIGNBYTES) & ~ALIGNBYTES)
+
+static void sethostent_r(FILE** hf) {
+ if (!*hf)
+ *hf = fopen(_PATH_HOSTS, "re");
+ else
+ rewind(*hf);
+}
+
+static void endhostent_r(FILE** hf) {
+ if (*hf) {
+ (void) fclose(*hf);
+ *hf = NULL;
+ }
+}
+
+// TODO: Consider returning a boolean result as files_getaddrinfo() does because the error code
+// does not currently return to netd.
+int _hf_gethtbyname2(const char* name, int af, getnamaddr* info) {
+ struct hostent *hp, hent;
+ char *buf, *ptr;
+ size_t len, num, i;
+ char* aliases[MAXALIASES];
+ char* addr_ptrs[MAXADDRS];
+
+ FILE* hf = NULL;
+ sethostent_r(&hf);
+ if (hf == NULL) {
+ // TODO: Consider converting to a private extended EAI_* error code.
+ // Currently, the EAI_* value has no corresponding error code for invalid argument socket
+ // length. In order to not rely on errno, convert the original error code pair, EAI_SYSTEM
+ // and EINVAL, to EAI_FAIL.
+ return EAI_FAIL;
+ }
+
+ if ((ptr = buf = (char*) malloc(len = info->buflen)) == NULL) {
+ return EAI_MEMORY;
+ }
+
+ hent.h_name = NULL;
+ hent.h_addrtype = 0;
+ hent.h_length = 0;
+
+ size_t anum = 0;
+ for (num = 0; num < MAXADDRS; /**/) {
+ info->hp->h_addrtype = af;
+ info->hp->h_length = 0;
+
+ int he;
+ hp = netbsd_gethostent_r(hf, info->hp, info->buf, info->buflen, &he);
+ if (hp == NULL) {
+ if (he == NETDB_INTERNAL && errno == ENOSPC) {
+ goto nospc; // glibc compatibility.
+ }
+ break;
+ }
+
+ if (strcasecmp(hp->h_name, name) != 0) {
+ char** cp;
+ for (cp = hp->h_aliases; *cp != NULL; cp++)
+ if (strcasecmp(*cp, name) == 0) break;
+ // NOTE: does not increment num
+ if (*cp == NULL) continue;
+ }
+
+ if (num == 0) {
+ hent.h_addrtype = hp->h_addrtype;
+ hent.h_length = hp->h_length;
+
+ HENT_SCOPY(hent.h_name, hp->h_name, ptr, len);
+ for (anum = 0; hp->h_aliases[anum]; anum++) {
+ if (anum >= MAXALIASES) goto nospc;
+ HENT_SCOPY(aliases[anum], hp->h_aliases[anum], ptr, len);
+ }
+ ptr = (char*) ALIGN(ptr);
+ if ((size_t)(ptr - buf) >= info->buflen) goto nospc;
+ }
+
+ if (num >= MAXADDRS) goto nospc;
+ HENT_COPY(addr_ptrs[num], hp->h_addr_list[0], hp->h_length, ptr, len);
+
+ num++;
+ }
+ endhostent_r(&hf);
+
+ if (num == 0) {
+ free(buf);
+ // TODO: Perhaps convert HOST_NOT_FOUND to EAI_NONAME instead.
+ // The original return error number is h_errno HOST_NOT_FOUND which was converted to
+ // EAI_NODATA.
+ return EAI_NODATA;
+ }
+
+ hp = info->hp;
+ ptr = info->buf;
+ len = info->buflen;
+
+ hp->h_addrtype = hent.h_addrtype;
+ hp->h_length = hent.h_length;
+
+ HENT_ARRAY(hp->h_aliases, anum, ptr, len);
+ HENT_ARRAY(hp->h_addr_list, num, ptr, len);
+
+ for (i = 0; i < num; i++) {
+ HENT_COPY(hp->h_addr_list[i], addr_ptrs[i], hp->h_length, ptr, len);
+
+ // reserve space for mapping IPv4 address to IPv6 address in place
+ if (hp->h_addrtype == AF_INET) {
+ HENT_COPY(ptr, NAT64_PAD, sizeof(NAT64_PAD), ptr, len);
+ }
+ }
+ hp->h_addr_list[num] = NULL;
+
+ HENT_SCOPY(hp->h_name, hent.h_name, ptr, len);
+
+ for (i = 0; i < anum; i++) {
+ HENT_SCOPY(hp->h_aliases[i], aliases[i], ptr, len);
+ }
+ hp->h_aliases[anum] = NULL;
+
+ free(buf);
+ return 0;
+nospc:
+ free(buf);
+ return EAI_MEMORY;
+}
+
+// TODO: Consider returning a boolean result as files_getaddrinfo() does because the error code
+// does not currently return to netd.
+int _hf_gethtbyaddr(const unsigned char* uaddr, int len, int af, getnamaddr* info) {
+ info->hp->h_length = len;
+ info->hp->h_addrtype = af;
+
+ FILE* hf = NULL;
+ sethostent_r(&hf);
+ if (hf == NULL) {
+ // TODO: Consider converting to a private extended EAI_* error code.
+ // Currently, the EAI_* value has no corresponding error code for invalid argument socket
+ // length. In order to not rely on errno, convert the original error code pair, EAI_SYSTEM
+ // and EINVAL, to EAI_FAIL.
+ return EAI_FAIL;
+ }
+ struct hostent* hp;
+ int he;
+ while ((hp = netbsd_gethostent_r(hf, info->hp, info->buf, info->buflen, &he)) != NULL)
+ if (!memcmp(hp->h_addr_list[0], uaddr, (size_t) hp->h_length)) break;
+ endhostent_r(&hf);
+
+ if (hp == NULL) {
+ if (errno == ENOSPC) return EAI_MEMORY; // glibc compatibility.
+ // TODO: Perhaps convert HOST_NOT_FOUND to EAI_NONAME instead.
+ // The original return error number is h_errno HOST_NOT_FOUND which was converted to
+ // EAI_NODATA.
+ return EAI_NODATA;
+ }
+ return 0;
+}
diff --git a/resolv/stats.proto b/resolv/stats.proto
new file mode 100644
index 0000000..60e2071
--- /dev/null
+++ b/resolv/stats.proto
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+syntax = "proto2";
+option optimize_for = LITE_RUNTIME;
+package android.net;
+
+enum EventType {
+ EVENT_UNKNOWN = 0;
+ EVENT_GETADDRINFO = 1;
+ EVENT_GETHOSTBYNAME = 2;
+ EVENT_GETHOSTBYADDR = 3;
+ EVENT_RES_NSEND = 4;
+}
+
+// The return value of the DNS resolver for each DNS lookups.
+// bionic/libc/include/netdb.h
+// system/netd/resolv/include/netd_resolv/resolv.h
+enum ReturnCode {
+ RC_EAI_NO_ERROR = 0;
+ RC_EAI_ADDRFAMILY = 1;
+ RC_EAI_AGAIN = 2;
+ RC_EAI_BADFLAGS = 3;
+ RC_EAI_FAIL = 4;
+ RC_EAI_FAMILY = 5;
+ RC_EAI_MEMORY = 6;
+ RC_EAI_NODATA = 7;
+ RC_EAI_NONAME = 8;
+ RC_EAI_SERVICE = 9;
+ RC_EAI_SOCKTYPE = 10;
+ RC_EAI_SYSTEM = 11;
+ RC_EAI_BADHINTS = 12;
+ RC_EAI_PROTOCOL = 13;
+ RC_EAI_OVERFLOW = 14;
+ RC_RESOLV_TIMEOUT = 255;
+ RC_EAI_MAX = 256;
+}
+
+enum NsRcode {
+ NS_R_NO_ERROR = 0; // No error occurred.
+ NS_R_FORMERR = 1; // Format error.
+ NS_R_SERVFAIL = 2; // Server failure.
+ NS_R_NXDOMAIN = 3; // Name error.
+ NS_R_NOTIMPL = 4; // Unimplemented.
+ NS_R_REFUSED = 5; // Operation refused.
+ // these are for BIND_UPDATE
+ NS_R_YXDOMAIN = 6; // Name exists
+ NS_R_YXRRSET = 7; // RRset exists
+ NS_R_NXRRSET = 8; // RRset does not exist
+ NS_R_NOTAUTH = 9; // Not authoritative for zone
+ NS_R_NOTZONE = 10; // Zone of record different from zone section
+ NS_R_MAX = 11;
+ // The following are EDNS extended rcodes
+ NS_R_BADVERS = 16;
+ // The following are TSIG errors
+ // NS_R_BADSIG = 16,
+ NS_R_BADKEY = 17;
+ NS_R_BADTIME = 18;
+ NS_R_INTERNAL_ERROR = 254;
+ NS_R_TIMEOUT = 255;
+}
+
+// Currently defined type values for resources and queries.
+enum NsType {
+ NS_T_INVALID = 0; // Cookie.
+ NS_T_A = 1; // Host address.
+ NS_T_NS = 2; // Authoritative server.
+ NS_T_MD = 3; // Mail destination.
+ NS_T_MF = 4; // Mail forwarder.
+ NS_T_CNAME = 5; // Canonical name.
+ NS_T_SOA = 6; // Start of authority zone.
+ NS_T_MB = 7; // Mailbox domain name.
+ NS_T_MG = 8; // Mail group member.
+ NS_T_MR = 9; // Mail rename name.
+ NS_T_NULL = 10; // Null resource record.
+ NS_T_WKS = 11; // Well known service.
+ NS_T_PTR = 12; // Domain name pointer.
+ NS_T_HINFO = 13; // Host information.
+ NS_T_MINFO = 14; // Mailbox information.
+ NS_T_MX = 15; // Mail routing information.
+ NS_T_TXT = 16; // Text strings.
+ NS_T_RP = 17; // Responsible person.
+ NS_T_AFSDB = 18; // AFS cell database.
+ NS_T_X25 = 19; // X_25 calling address.
+ NS_T_ISDN = 20; // ISDN calling address.
+ NS_T_RT = 21; // Router.
+ NS_T_NSAP = 22; // NSAP address.
+ NS_T_NSAP_PTR = 23; // Reverse NSAP lookup (deprecated).
+ NS_T_SIG = 24; // Security signature.
+ NS_T_KEY = 25; // Security key.
+ NS_T_PX = 26; // X.400 mail mapping.
+ NS_T_GPOS = 27; // Geographical position (withdrawn).
+ NS_T_AAAA = 28; // IPv6 Address.
+ NS_T_LOC = 29; // Location Information.
+ NS_T_NXT = 30; // Next domain (security).
+ NS_T_EID = 31; // Endpoint identifier.
+ NS_T_NIMLOC = 32; // Nimrod Locator.
+ NS_T_SRV = 33; // Server Selection.
+ NS_T_ATMA = 34; // ATM Address
+ NS_T_NAPTR = 35; // Naming Authority PoinTeR
+ NS_T_KX = 36; // Key Exchange
+ NS_T_CERT = 37; // Certification record
+ NS_T_A6 = 38; // IPv6 address (experimental)
+ NS_T_DNAME = 39; // Non-terminal DNAME
+ NS_T_SINK = 40; // Kitchen sink (experimentatl)
+ NS_T_OPT = 41; // EDNS0 option (meta-RR)
+ NS_T_APL = 42; // Address prefix list (RFC 3123)
+ NS_T_DS = 43; // Delegation Signer
+ NS_T_SSHFP = 44; // SSH Fingerprint
+ NS_T_IPSECKEY = 45; // IPSEC Key
+ NS_T_RRSIG = 46; // RRset Signature
+ NS_T_NSEC = 47; // Negative security
+ NS_T_DNSKEY = 48; // DNS Key
+ NS_T_DHCID = 49; // Dynamic host configuratin identifier
+ NS_T_NSEC3 = 50; // Negative security type 3
+ NS_T_NSEC3PARAM = 51; // Negative security type 3 parameters
+ NS_T_HIP = 55; // Host Identity Protocol
+ NS_T_SPF = 99; // Sender Policy Framework
+ NS_T_TKEY = 249; // Transaction key
+ NS_T_TSIG = 250; // Transaction signature.
+ NS_T_IXFR = 251; // Incremental zone transfer.
+ NS_T_AXFR = 252; // Transfer zone of authority.
+ NS_T_MAILB = 253; // Transfer mailbox records.
+ NS_T_MAILA = 254; // Transfer mail agent records.
+ NS_T_ANY = 255; // Wildcard match.
+ NS_T_ZXFR = 256; // BIND-specific, nonstandard.
+ NS_T_DLV = 32769; // DNSSEC look-aside validatation.
+ NS_T_MAX = 65536;
+}
+
+enum IpVersion {
+ IV_UNKNOWN = 0;
+ IV_IPV4 = 1;
+ IV_IPV6 = 2;
+}
+
+enum Protocol {
+ PROTO_UNKNOWN = 0;
+ PROTO_UDP = 1;
+ PROTO_TCP = 2;
+ PROTO_DOT = 3;
+}
+
+enum PrivateDnsModes {
+ PDM_UNKNOWN = 0;
+ PDM_OFF = 1;
+ PDM_OPPORTUNISTIC = 2;
+ PDM_STRICT = 3;
+}
+
+enum NetworkType {
+ NT_UNKNOWN = 0;
+ // Indicates this network uses a Cellular transport.
+ NT_CELLULAR = 1;
+ // Indicates this network uses a Wi-Fi transport.
+ NT_WIFI = 2;
+ // Indicates this network uses a Bluetooth transport.
+ NT_BLUETOOTH = 3;
+ // Indicates this network uses an Ethernet transport.
+ NT_ETHERNET = 4;
+ // Indicates this network uses a VPN transport.
+ NT_VPN = 5;
+ // Indicates this network uses a Wi-Fi Aware transport.
+ NT_WIFI_AWARE = 6;
+ // Indicates this network uses a LoWPAN transport.
+ NT_LOWPAN = 7;
+}
+
+enum CacheStatus{
+ // the cache can't handle that kind of queries.
+ // or the answer buffer is too small.
+ CS_UNSUPPORTED = 0;
+ // the cache doesn't know about this query.
+ CS_NOTFOUND = 1;
+ // the cache found the answer.
+ CS_FOUND = 2;
+ // Don't do anything on cache.
+ CS_SKIP = 3;
+}
+
+message DnsQueryEvent {
+ optional NsRcode rcode = 1;
+
+ optional NsType type = 2;
+
+ optional CacheStatus cache_hit = 3;
+
+ optional IpVersion ip_version = 4;
+
+ optional Protocol protocol = 5;
+
+ // Number of DNS query retry times
+ optional int32 retry_times = 6;
+
+ // Ordinal number of name server.
+ optional int32 dns_server_index = 7;
+
+ // Used only by TCP and DOT. True for new connections.
+ optional bool connected = 8;
+
+ optional int32 latency_micros = 9;
+}
+
+message DnsQueryEvents {
+ repeated DnsQueryEvent dns_query_event = 1;
+}
+
+/**
+ * Logs a DNS lookup operation initiated by the system resolver on behalf of an application
+ * invoking native APIs such as getaddrinfo() or Java APIs such as Network#getAllByName().
+ *
+ * The NetworkDnsEventReported message represents the entire lookup operation, which may
+ * result one or more queries to the recursive DNS resolvers. Those are individually logged
+ * in DnsQueryEvents to enable computing error rates and network latency and timeouts
+ * broken up by query type, transport, network interface, etc.
+ */
+message NetworkDnsEventReported {
+ optional EventType event_type = 1;
+
+ optional ReturnCode return_code = 2;
+
+ // The latency in microseconds of the entire DNS lookup operation.
+ optional int32 latency_micros = 3;
+
+ // Only valid for event_type = EVENT_GETADDRINFO.
+ optional int32 hints_ai_flags = 4;
+
+ // Flags passed to android_res_nsend() defined in multinetwork.h
+ // Only valid for event_type = EVENT_RESNSEND.
+ optional int32 res_nsend_flags = 5;
+
+ optional NetworkType network_type = 6;
+
+ // The DNS over TLS mode on a specific netId.
+ optional PrivateDnsModes private_dns_modes = 7;
+
+ // Additional pass-through fields opaque to statsd.
+ // The DNS resolver Mainline module can add new fields here without requiring an OS update.
+ optional DnsQueryEvents dns_query_events = 8;
+
+ // The sample rate of DNS stats (to statsd) is 1/sampling_rate_denom.
+ optional int32 sampling_rate_denom = 9;
+}
diff --git a/resolv/tests/BaseTestMetricsListener.cpp b/resolv/tests/BaseTestMetricsListener.cpp
new file mode 100644
index 0000000..414e383
--- /dev/null
+++ b/resolv/tests/BaseTestMetricsListener.cpp
@@ -0,0 +1,80 @@
+/**
+ * Copyright (c) 2019, 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.
+ */
+
+#include "BaseTestMetricsListener.h"
+
+namespace android {
+namespace net {
+namespace metrics {
+
+void BaseTestMetricsListener::notify() {
+ std::lock_guard lock(mCvMutex);
+ mCv.notify_one();
+}
+
+void BaseTestMetricsListener::setVerified(EventFlag event) {
+ mVerified |= event;
+}
+
+android::binder::Status BaseTestMetricsListener::onDnsEvent(
+ int32_t /*netId*/, int32_t /*eventType*/, int32_t /*returnCode*/, int32_t /*latencyMs*/,
+ const std::string& /*hostname*/, const ::std::vector<std::string>& /*ipAddresses*/,
+ int32_t /*ipAddressesCount*/, int32_t /*uid*/) {
+ // default no-op
+ return android::binder::Status::ok();
+};
+
+android::binder::Status BaseTestMetricsListener::onPrivateDnsValidationEvent(
+ int32_t /*netId*/, const ::android::String16& /*ipAddress*/,
+ const ::android::String16& /*hostname*/, bool /*validated*/) {
+ // default no-op
+ return android::binder::Status::ok();
+};
+
+android::binder::Status BaseTestMetricsListener::onConnectEvent(
+ int32_t /*netId*/, int32_t /*error*/, int32_t /*latencyMs*/,
+ const ::android::String16& /*ipAddr*/, int32_t /*port*/, int32_t /*uid*/) {
+ // default no-op
+ return android::binder::Status::ok();
+};
+
+android::binder::Status BaseTestMetricsListener::onWakeupEvent(
+ const ::android::String16& /*prefix*/, int32_t /*uid*/, int32_t /*ethertype*/,
+ int32_t /*ipNextHeader*/, const ::std::vector<uint8_t>& /*dstHw*/,
+ const ::android::String16& /*srcIp*/, const ::android::String16& /*dstIp*/,
+ int32_t /*srcPort*/, int32_t /*dstPort*/, int64_t /*timestampNs*/) {
+ // default no-op
+ return android::binder::Status::ok();
+};
+
+android::binder::Status BaseTestMetricsListener::onTcpSocketStatsEvent(
+ const ::std::vector<int32_t>& /*networkIds*/, const ::std::vector<int32_t>& /*sentPackets*/,
+ const ::std::vector<int32_t>& /*lostPackets*/, const ::std::vector<int32_t>& /*rttUs*/,
+ const ::std::vector<int32_t>& /*sentAckDiffMs*/) {
+ // default no-op
+ return android::binder::Status::ok();
+};
+
+android::binder::Status BaseTestMetricsListener::onNat64PrefixEvent(
+ int32_t /*netId*/, bool /*added*/, const ::std::string& /*prefixString*/,
+ int32_t /*prefixLength*/) {
+ // default no-op
+ return android::binder::Status::ok();
+};
+
+} // namespace metrics
+} // namespace net
+} // namespace android
\ No newline at end of file
diff --git a/resolv/tests/BaseTestMetricsListener.h b/resolv/tests/BaseTestMetricsListener.h
new file mode 100644
index 0000000..326647b
--- /dev/null
+++ b/resolv/tests/BaseTestMetricsListener.h
@@ -0,0 +1,104 @@
+/**
+ * Copyright (c) 2019, 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.
+ */
+
+#ifndef _BASE_TEST_METRICS_LISTENER_H_
+#define _BASE_TEST_METRICS_LISTENER_H_
+
+#include <string>
+#include <vector>
+
+#include <binder/BinderService.h>
+
+#include "android/net/metrics/BnNetdEventListener.h"
+
+enum EventFlag : uint32_t {
+ onDnsEvent = 1 << 0,
+ onPrivateDnsValidationEvent = 1 << 1,
+ onConnectEvent = 1 << 2,
+ onWakeupEvent = 1 << 3,
+ onTcpSocketStatsEvent = 1 << 4,
+ onNat64PrefixEvent = 1 << 5,
+};
+
+namespace android {
+namespace net {
+namespace metrics {
+
+class BaseTestMetricsListener : public BnNetdEventListener {
+ public:
+ BaseTestMetricsListener() = default;
+ ~BaseTestMetricsListener() = default;
+
+ // Returns TRUE if the verification was successful. Otherwise, returns FALSE.
+ virtual bool isVerified() = 0;
+
+ std::condition_variable& getCv() { return mCv; }
+ std::mutex& getCvMutex() { return mCvMutex; }
+
+ android::binder::Status onDnsEvent(int32_t /*netId*/, int32_t /*eventType*/,
+ int32_t /*returnCode*/, int32_t /*latencyMs*/,
+ const std::string& /*hostname*/,
+ const ::std::vector<std::string>& /*ipAddresses*/,
+ int32_t /*ipAddressesCount*/, int32_t /*uid*/) override;
+ android::binder::Status onPrivateDnsValidationEvent(int32_t /*netId*/,
+ const ::android::String16& /*ipAddress*/,
+ const ::android::String16& /*hostname*/,
+ bool /*validated*/) override;
+ android::binder::Status onConnectEvent(int32_t /*netId*/, int32_t /*error*/,
+ int32_t /*latencyMs*/,
+ const ::android::String16& /*ipAddr*/, int32_t /*port*/,
+ int32_t /*uid*/) override;
+ android::binder::Status onWakeupEvent(const ::android::String16& /*prefix*/, int32_t /*uid*/,
+ int32_t /*ethertype*/, int32_t /*ipNextHeader*/,
+ const ::std::vector<uint8_t>& /*dstHw*/,
+ const ::android::String16& /*srcIp*/,
+ const ::android::String16& /*dstIp*/, int32_t /*srcPort*/,
+ int32_t /*dstPort*/, int64_t /*timestampNs*/) override;
+ android::binder::Status onTcpSocketStatsEvent(
+ const ::std::vector<int32_t>& /*networkIds*/,
+ const ::std::vector<int32_t>& /*sentPackets*/,
+ const ::std::vector<int32_t>& /*lostPackets*/, const ::std::vector<int32_t>& /*rttUs*/,
+ const ::std::vector<int32_t>& /*sentAckDiffMs*/) override;
+ android::binder::Status onNat64PrefixEvent(int32_t /*netId*/, bool /*added*/,
+ const ::std::string& /*prefixString*/,
+ int32_t /*prefixLength*/) override;
+
+ private:
+ // The verified event(s) as a bitwise-OR combination of enum EventFlag flags.
+ uint32_t mVerified{};
+
+ // This lock prevents racing condition between signaling thread(s) and waiting thread(s).
+ std::mutex mCvMutex;
+
+ // Condition variable signaled when notify() is called.
+ std::condition_variable mCv;
+
+ protected:
+ // Notify who is waiting for test results. See also mCvMutex and mCv.
+ void notify();
+
+ // Get current verified event(s).
+ uint32_t getVerified() const { return mVerified; }
+
+ // Set the specific event as verified if its verification was successful.
+ void setVerified(EventFlag event);
+};
+
+} // namespace metrics
+} // namespace net
+} // namespace android
+
+#endif // _BASE_TEST_METRICS_LISTENER_H_
diff --git a/resolv/tests/TestMetrics.cpp b/resolv/tests/TestMetrics.cpp
new file mode 100644
index 0000000..4c3d36a
--- /dev/null
+++ b/resolv/tests/TestMetrics.cpp
@@ -0,0 +1,74 @@
+/**
+ * Copyright (c) 2019, 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.
+ */
+
+#include <netdb.h>
+
+#include "TestMetrics.h"
+
+namespace android {
+namespace net {
+namespace metrics {
+
+android::binder::Status TestOnDnsEvent::onDnsEvent(int32_t netId, int32_t eventType,
+ int32_t returnCode, int32_t /*latencyMs*/,
+ const std::string& hostname,
+ const ::std::vector<std::string>& ipAddresses,
+ int32_t ipAddressesCount, int32_t /*uid*/) {
+ // A bitwise-OR combination of all expected test cases.
+ // Currently, the maximum number of test case is 32 because a 32-bits bitwise-OR combination
+ // is used for checking and recording verified test cases.
+ static const uint32_t kExpectedTests = (1 << mResults.size()) - 1;
+
+ // A bitwise-OR combination for recording verified test cases.
+ static uint32_t verifiedTests = 0;
+
+ for (size_t i = 0; i < mResults.size(); ++i) {
+ // Generate bitwise flag to trace which testcase is running.
+ const uint32_t currentTestFlag = 1 << i;
+
+ // Ignore verified testcase.
+ if (verifiedTests & currentTestFlag) continue;
+
+ // Verify expected event content.
+ auto& result = mResults[i];
+ if (netId != result.netId) continue;
+ if (eventType != result.eventType) continue;
+ if (hostname != result.hostname) continue;
+ if (returnCode != result.returnCode) continue;
+ if (ipAddressesCount != result.ipAddressesCount) continue;
+ if (result.returnCode == 0 /*success*/) {
+ // Only verify first address.
+ // TODO: Check all addresses.
+ if (ipAddresses.empty() || ipAddresses.front() != result.ipAddress) continue;
+ }
+
+ // Record current testcase as verified.
+ verifiedTests |= currentTestFlag;
+ break;
+ }
+
+ // All testcases of onDnsEvent are verified. Notify who was waiting for test results.
+ if (verifiedTests == kExpectedTests) {
+ setVerified(EventFlag::onDnsEvent);
+ notify();
+ }
+
+ return android::binder::Status::ok();
+};
+
+} // namespace metrics
+} // namespace net
+} // namespace android
\ No newline at end of file
diff --git a/resolv/tests/TestMetrics.h b/resolv/tests/TestMetrics.h
new file mode 100644
index 0000000..d3a4ad4
--- /dev/null
+++ b/resolv/tests/TestMetrics.h
@@ -0,0 +1,58 @@
+/**
+ * Copyright (c) 2019, 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.
+ */
+
+#ifndef _TEST_METRICS_H_
+#define _TEST_METRICS_H_
+
+#include "BaseTestMetricsListener.h"
+
+namespace android {
+namespace net {
+namespace metrics {
+
+class TestOnDnsEvent : public BaseTestMetricsListener {
+ public:
+ // Both latencyMs and uid are not verified. No special reason.
+ struct TestResult {
+ int netId;
+ int eventType;
+ int returnCode;
+ int ipAddressesCount;
+ std::string hostname;
+ std::string ipAddress; // Check first address only.
+ };
+
+ TestOnDnsEvent() = delete;
+ TestOnDnsEvent(const std::vector<TestResult>& results) : mResults(results){};
+
+ // BaseTestMetricsListener::isVerified() override.
+ bool isVerified() override { return (getVerified() & EventFlag::onDnsEvent) != 0; }
+
+ // Override for testing verification.
+ android::binder::Status onDnsEvent(int32_t netId, int32_t eventType, int32_t returnCode,
+ int32_t /*latencyMs*/, const std::string& hostname,
+ const std::vector<std::string>& ipAddresses,
+ int32_t ipAddressesCount, int32_t /*uid*/) override;
+
+ private:
+ const std::vector<TestResult>& mResults; // Expected results for test verification.
+};
+
+} // namespace metrics
+} // namespace net
+} // namespace android
+
+#endif // _TEST_METRICS_H_
diff --git a/server/Android.bp b/server/Android.bp
index eba80bc..c7f2984 100644
--- a/server/Android.bp
+++ b/server/Android.bp
@@ -95,14 +95,15 @@
"libbpf_android",
"libbase",
"libbinder",
+ "liblogwrap",
"libnetdbpf",
"libnetutils",
"libnetdutils",
"libpcap",
"libqtaguid",
"libssl",
- "netd_aidl_interface-cpp",
- "netd_event_listener_interface-cpp",
+ "netd_aidl_interface-V2-cpp",
+ "netd_event_listener_interface-V1-cpp",
],
header_libs: [
"libnetd_resolv_headers",
@@ -130,8 +131,10 @@
"libcutils",
"libdl",
"libhidlbase",
+ "libhidltransport",
"libjsoncpp",
"liblog",
+ "liblogwrap",
"libmdnssd",
"libnetdbpf",
"libnetdutils",
@@ -142,8 +145,8 @@
"libselinux",
"libsysutils",
"libutils",
- "netd_aidl_interface-unstable-cpp",
- "netd_event_listener_interface-cpp",
+ "netd_aidl_interface-V2-cpp",
+ "netd_event_listener_interface-V1-cpp",
"oemnetd_aidl_interface-cpp",
],
static_libs: [
@@ -192,8 +195,8 @@
"liblog",
"libutils",
"libbinder",
- "dnsresolver_aidl_interface-cpp",
- "netd_aidl_interface-cpp",
+ "dnsresolver_aidl_interface-V2-cpp",
+ "netd_aidl_interface-V2-cpp",
],
srcs: [
"ndc.cpp",
@@ -211,6 +214,7 @@
"system/netd/include",
"system/netd/server/binder",
"system/netd/tests",
+ "system/core/logwrapper/include",
],
srcs: [
"BandwidthControllerTest.cpp",
@@ -235,9 +239,6 @@
"libgmock",
"libnetd_server",
"libnetd_test_tun_interface",
- "libqtaguid",
- "netd_aidl_interface-cpp",
- "netd_event_listener_interface-cpp",
],
shared_libs: [
"libbase",
@@ -249,7 +250,10 @@
"libnetdbpf",
"libnetdutils",
"libnetutils",
+ "libqtaguid",
"libsysutils",
"libutils",
+ "netd_aidl_interface-V2-cpp",
+ "netd_event_listener_interface-V1-cpp",
],
}
diff --git a/server/AndroidTest.xml b/server/AndroidTest.xml
new file mode 100644
index 0000000..2501d78
--- /dev/null
+++ b/server/AndroidTest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+<configuration description="Config for netd_unit_test">
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="netd_unit_test->/data/local/tmp/netd_unit_test" />
+ </target_preparer>
+ <option name="test-suite-tag" value="apct" />
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="netd_unit_test" />
+ </test>
+</configuration>
diff --git a/server/BandwidthController.cpp b/server/BandwidthController.cpp
index 3efc8a3..7e9bff7 100644
--- a/server/BandwidthController.cpp
+++ b/server/BandwidthController.cpp
@@ -48,6 +48,7 @@
#define LOG_TAG "BandwidthController"
#include <cutils/properties.h>
#include <log/log.h>
+#include <logwrap/logwrap.h>
#include <netdutils/Syscalls.h>
#include "BandwidthController.h"
diff --git a/server/ClatdController.cpp b/server/ClatdController.cpp
index cf0ab96..5a5566b 100644
--- a/server/ClatdController.cpp
+++ b/server/ClatdController.cpp
@@ -33,7 +33,6 @@
#include <log/log.h>
#include "ClatdController.h"
-#include "InterfaceController.h"
#include "android-base/properties.h"
#include "android-base/scopeguard.h"
@@ -140,11 +139,7 @@
// Attempt to connect to the address. If the connection succeeds and getsockname returns the
// same then the address is already assigned to the system and we can't use it.
- struct sockaddr_in sin = {
- .sin_family = AF_INET,
- .sin_port = htons(53),
- .sin_addr = {addr},
- };
+ struct sockaddr_in sin = {.sin_family = AF_INET, .sin_addr = {addr}, .sin_port = 53};
socklen_t len = sizeof(sin);
bool inuse = connect(s, (struct sockaddr*)&sin, sizeof(sin)) == 0 &&
getsockname(s, (struct sockaddr*)&sin, &len) == 0 && (size_t)len >= sizeof(sin) &&
@@ -457,10 +452,6 @@
return -res;
}
- // disable IPv6 on it - failing to do so is not a critical error
- res = InterfaceController::setEnableIPv6(v4interface.c_str(), 0);
- if (res) ALOGE("setEnableIPv6 %s failed (%s)", v4interface.c_str(), strerror(res));
-
// 5. initialize tracker object
ClatdTracker tracker;
int ret = tracker.init(networkId, interface, v4interface, nat64Prefix);
diff --git a/server/Controllers.cpp b/server/Controllers.cpp
index f844544..c941a80 100644
--- a/server/Controllers.cpp
+++ b/server/Controllers.cpp
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-#include <cinttypes>
#include <regex>
#include <set>
#include <string>
@@ -240,39 +239,39 @@
void Controllers::initIptablesRules() {
Stopwatch s;
initChildChains();
- gLog.info("Creating child chains: %" PRId64 "us", s.getTimeAndResetUs());
+ gLog.info("Creating child chains: %.1fms", s.getTimeAndReset());
// Let each module setup their child chains
setupOemIptablesHook();
- gLog.info("Setting up OEM hooks: %" PRId64 "us", s.getTimeAndResetUs());
+ gLog.info("Setting up OEM hooks: %.1fms", s.getTimeAndReset());
/* When enabled, DROPs all packets except those matching rules. */
firewallCtrl.setupIptablesHooks();
- gLog.info("Setting up FirewallController hooks: %" PRId64 "us", s.getTimeAndResetUs());
+ gLog.info("Setting up FirewallController hooks: %.1fms", s.getTimeAndReset());
/* Does DROPs in FORWARD by default */
tetherCtrl.setupIptablesHooks();
- gLog.info("Setting up TetherController hooks: %" PRId64 "us", s.getTimeAndResetUs());
+ gLog.info("Setting up TetherController hooks: %.1fms", s.getTimeAndReset());
/*
* Does REJECT in INPUT, OUTPUT. Does counting also.
* No DROP/REJECT allowed later in netfilter-flow hook order.
*/
bandwidthCtrl.setupIptablesHooks();
- gLog.info("Setting up BandwidthController hooks: %" PRId64 "us", s.getTimeAndResetUs());
+ gLog.info("Setting up BandwidthController hooks: %.1fms", s.getTimeAndReset());
/*
* Counts in nat: PREROUTING, POSTROUTING.
* No DROP/REJECT allowed later in netfilter-flow hook order.
*/
idletimerCtrl.setupIptablesHooks();
- gLog.info("Setting up IdletimerController hooks: %" PRId64 "us", s.getTimeAndResetUs());
+ gLog.info("Setting up IdletimerController hooks: %.1fms", s.getTimeAndReset());
/*
* Add rules for detecting IPv6/IPv4 TCP/UDP connections with TLS/DTLS header
*/
strictCtrl.setupIptablesHooks();
- gLog.info("Setting up StrictController hooks: %" PRId64 "us", s.getTimeAndResetUs());
+ gLog.info("Setting up StrictController hooks: %.1fms", s.getTimeAndReset());
}
void Controllers::init() {
@@ -280,28 +279,28 @@
Stopwatch s;
clatdCtrl.init();
- gLog.info("Initializing ClatdController: %" PRId64 "us", s.getTimeAndResetUs());
+ gLog.info("Initializing ClatdController: %.1fms", s.getTimeAndReset());
netdutils::Status tcStatus = trafficCtrl.start();
if (!isOk(tcStatus)) {
gLog.error("Failed to start trafficcontroller: (%s)", toString(tcStatus).c_str());
}
- gLog.info("Initializing traffic control: %" PRId64 "us", s.getTimeAndResetUs());
+ gLog.info("Initializing traffic control: %.1fms", s.getTimeAndReset());
bandwidthCtrl.setBpfEnabled(trafficCtrl.getBpfLevel() != android::bpf::BpfLevel::NONE);
bandwidthCtrl.enableBandwidthControl();
- gLog.info("Enabling bandwidth control: %" PRId64 "us", s.getTimeAndResetUs());
+ gLog.info("Enabling bandwidth control: %.1fms", s.getTimeAndReset());
if (int ret = RouteController::Init(NetworkController::LOCAL_NET_ID)) {
gLog.error("Failed to initialize RouteController (%s)", strerror(-ret));
}
- gLog.info("Initializing RouteController: %" PRId64 "us", s.getTimeAndResetUs());
+ gLog.info("Initializing RouteController: %.1fms", s.getTimeAndReset());
netdutils::Status xStatus = XfrmController::Init();
if (!isOk(xStatus)) {
gLog.error("Failed to initialize XfrmController (%s)", netdutils::toString(xStatus).c_str());
};
- gLog.info("Initializing XfrmController: %" PRId64 "us", s.getTimeAndResetUs());
+ gLog.info("Initializing XfrmController: %.1fms", s.getTimeAndReset());
}
Controllers* gCtls = nullptr;
diff --git a/server/DummyNetwork.h b/server/DummyNetwork.h
index 5823ce5..32584aa 100644
--- a/server/DummyNetwork.h
+++ b/server/DummyNetwork.h
@@ -14,22 +14,27 @@
* limitations under the License.
*/
-#pragma once
+#ifndef NETD_SERVER_DUMMY_NETWORK_H
+#define NETD_SERVER_DUMMY_NETWORK_H
#include "Network.h"
-namespace android::net {
+namespace android {
+namespace net {
class DummyNetwork : public Network {
- public:
+public:
static const char* INTERFACE_NAME;
explicit DummyNetwork(unsigned netId);
virtual ~DummyNetwork();
- private:
+private:
Type getType() const override;
- [[nodiscard]] int addInterface(const std::string& interface) override;
- [[nodiscard]] int removeInterface(const std::string& interface) override;
+ int addInterface(const std::string& interface) override WARN_UNUSED_RESULT;
+ int removeInterface(const std::string& interface) override WARN_UNUSED_RESULT;
};
-} // namespace android::net
+} // namespace net
+} // namespace android
+
+#endif // NETD_SERVER_DUMMY_NETWORK_H
diff --git a/server/IdletimerController.cpp b/server/IdletimerController.cpp
index 103e7cd..acb8c6a 100644
--- a/server/IdletimerController.cpp
+++ b/server/IdletimerController.cpp
@@ -115,6 +115,7 @@
#define LOG_TAG "IdletimerController"
#include <log/log.h>
+#include <logwrap/logwrap.h>
#include "IdletimerController.h"
#include "NetdConstants.h"
diff --git a/server/InterfaceController.cpp b/server/InterfaceController.cpp
index 7504fb9..bad4ba2 100644
--- a/server/InterfaceController.cpp
+++ b/server/InterfaceController.cpp
@@ -30,6 +30,7 @@
#include <android-base/strings.h>
#include <linux/if_ether.h>
#include <log/log.h>
+#include <logwrap/logwrap.h>
#include <netutils/ifc.h>
#include <android/net/INetd.h>
@@ -55,6 +56,13 @@
using android::netdutils::toString;
using android::netdutils::status::ok;
+#define RETURN_STATUS_IF_IFCERROR(exp) \
+ do { \
+ if ((exp) == -1) { \
+ return statusFromErrno(errno, "Failed to add addr"); \
+ } \
+ } while (0);
+
namespace {
const char ipv4_proc_path[] = "/proc/sys/net/ipv4/conf";
@@ -282,9 +290,9 @@
switch (mode) {
case INetd::IPV6_ADDR_GEN_MODE_EUI64:
- // Ignore return value. If /proc/.../addr_gen_mode is
+ // Ignore return value. If /proc/.../stable_secret is
// missing we're probably in EUI64 mode already.
- writeValueToPath(ipv6_proc_path, interface.c_str(), "addr_gen_mode", "0");
+ writeValueToPath(ipv6_proc_path, interface.c_str(), "stable_secret", "");
break;
case INetd::IPV6_ADDR_GEN_MODE_STABLE_PRIVACY: {
return enableStablePrivacyAddresses(interface, getProperty, setProperty);
@@ -360,13 +368,11 @@
return writeValueToPath(sys_net_path, interface, "mtu", mtu);
}
-// Returns zero on success and negative errno on failure.
int InterfaceController::addAddress(const char *interface,
const char *addrString, int prefixLength) {
return ifc_add_address(interface, addrString, prefixLength);
}
-// Returns zero on success and negative errno on failure.
int InterfaceController::delAddress(const char *interface,
const char *addrString, int prefixLength) {
return ifc_del_address(interface, addrString, prefixLength);
@@ -499,9 +505,8 @@
}
}
- if (int ret = ifc_add_address(cfg.ifName.c_str(), cfg.ipv4Addr.c_str(), cfg.prefixLength)) {
- return statusFromErrno(-ret, "Failed to add addr");
- }
+ RETURN_STATUS_IF_IFCERROR(
+ ifc_add_address(cfg.ifName.c_str(), cfg.ipv4Addr.c_str(), cfg.prefixLength));
return ok;
}
diff --git a/server/IptablesRestoreControllerTest.cpp b/server/IptablesRestoreControllerTest.cpp
index 20f6183..d01d7ce 100644
--- a/server/IptablesRestoreControllerTest.cpp
+++ b/server/IptablesRestoreControllerTest.cpp
@@ -14,16 +14,14 @@
* limitations under the License.
*/
+#include <string>
#include <fcntl.h>
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
#include <sys/file.h>
#include <sys/socket.h>
#include <sys/un.h>
-#include <cinttypes>
-#include <iostream>
-#include <string>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
#define LOG_TAG "IptablesRestoreControllerTest"
#include <android-base/stringprintf.h>
@@ -55,7 +53,9 @@
int mIptablesLock = -1;
std::string mChainName;
- static void SetUpTestSuite() { blockSigpipe(); }
+ static void SetUpTestCase() {
+ blockSigpipe();
+ }
void SetUp() {
ASSERT_EQ(0, createTestChain());
@@ -269,9 +269,9 @@
EXPECT_EQ(0, con.execute(V4V6, IPTABLES_RESTORE_ADD, nullptr));
EXPECT_EQ(0, con.execute(V4V6, IPTABLES_RESTORE_DEL, nullptr));
}
- int64_t timeTaken = s.getTimeAndResetUs();
- std::cerr << " Add/del " << iterations << " UID rules via restore: " << timeTaken
- << "us (" << (timeTaken / 2 / iterations) << "us per operation)" << std::endl;
+ float timeTaken = s.getTimeAndReset();
+ fprintf(stderr, " Add/del %d UID rules via restore: %.1fms (%.2fms per operation)\n",
+ iterations, timeTaken, timeTaken / 2 / iterations);
}
}
diff --git a/server/LocalNetwork.h b/server/LocalNetwork.h
index 7a39a9d..2aa7900 100644
--- a/server/LocalNetwork.h
+++ b/server/LocalNetwork.h
@@ -14,11 +14,13 @@
* limitations under the License.
*/
-#pragma once
+#ifndef NETD_SERVER_LOCAL_NETWORK_H
+#define NETD_SERVER_LOCAL_NETWORK_H
#include "Network.h"
-namespace android::net {
+namespace android {
+namespace net {
class LocalNetwork : public Network {
public:
@@ -27,8 +29,11 @@
private:
Type getType() const override;
- [[nodiscard]] int addInterface(const std::string& interface) override;
- [[nodiscard]] int removeInterface(const std::string& interface) override;
+ int addInterface(const std::string& interface) override WARN_UNUSED_RESULT;
+ int removeInterface(const std::string& interface) override WARN_UNUSED_RESULT;
};
-} // namespace android::net
+} // namespace net
+} // namespace android
+
+#endif // NETD_SERVER_LOCAL_NETWORK_H
diff --git a/server/MDnsSdListener.h b/server/MDnsSdListener.h
index 83cf23e..47ddc28 100644
--- a/server/MDnsSdListener.h
+++ b/server/MDnsSdListener.h
@@ -21,7 +21,6 @@
#include <dns_sd.h>
#include <sysutils/FrameworkListener.h>
#include <mutex>
-#include <string>
#include "NetdCommand.h"
@@ -80,7 +79,6 @@
int stopService();
void run();
void deallocateServiceRef(DNSServiceRef* ref);
- std::string threadName() { return std::string("MDnsSdMonitor"); }
private:
int rescan(); // returns the number of elements in the poll
diff --git a/server/NetdConstants.cpp b/server/NetdConstants.cpp
index 80282b3..40f78f6 100644
--- a/server/NetdConstants.cpp
+++ b/server/NetdConstants.cpp
@@ -29,6 +29,7 @@
#include <android-base/stringprintf.h>
#include <cutils/sockets.h>
#include <log/log.h>
+#include <logwrap/logwrap.h>
#include "Controllers.h"
#include "NetdConstants.h"
diff --git a/server/NetdConstants.h b/server/NetdConstants.h
index c273e1b..97e75bd 100644
--- a/server/NetdConstants.h
+++ b/server/NetdConstants.h
@@ -14,7 +14,8 @@
* limitations under the License.
*/
-#pragma once
+#ifndef _NETD_CONSTANTS_H
+#define _NETD_CONSTANTS_H
#include <ifaddrs.h>
#include <netdb.h>
@@ -27,6 +28,9 @@
#include <netdutils/UidConstants.h>
#include <private/android_filesystem_config.h>
+// Referred from SHA256_DIGEST_LENGTH in boringssl
+constexpr size_t SHA256_SIZE = 32;
+
enum IptablesTarget { V4, V6, V4V6 };
int execIptablesRestore(IptablesTarget target, const std::string& commands);
@@ -49,11 +53,24 @@
#define UINT32_HEX_STRLEN sizeof("0x12345678")
#define IPSEC_IFACE_PREFIX "ipsec"
+#define WARN_UNUSED_RESULT __attribute__((__warn_unused_result__))
+
const uid_t INVALID_UID = static_cast<uid_t>(-1);
constexpr char TCP_RMEM_PROC_FILE[] = "/proc/sys/net/ipv4/tcp_rmem";
constexpr char TCP_WMEM_PROC_FILE[] = "/proc/sys/net/ipv4/tcp_wmem";
+struct AddrinfoDeleter {
+ void operator()(struct addrinfo* p) const {
+ if (p != nullptr) {
+ freeaddrinfo(p);
+ }
+ }
+};
+
+typedef std::unique_ptr<struct addrinfo, struct AddrinfoDeleter> ScopedAddrinfo;
+
+
struct IfaddrsDeleter {
void operator()(struct ifaddrs *p) const {
if (p != nullptr) {
@@ -64,7 +81,8 @@
typedef std::unique_ptr<struct ifaddrs, struct IfaddrsDeleter> ScopedIfaddrs;
-namespace android::net {
+namespace android {
+namespace net {
/**
* This lock exists to make NetdNativeService RPCs (which come in on multiple Binder threads)
@@ -74,4 +92,7 @@
*/
extern std::mutex gBigNetdLock;
-} // namespace android::net
+} // namespace net
+} // namespace android
+
+#endif // _NETD_CONSTANTS_H
diff --git a/server/NetdNativeService.cpp b/server/NetdNativeService.cpp
index 9aec5e2..65d4727 100644
--- a/server/NetdNativeService.cpp
+++ b/server/NetdNativeService.cpp
@@ -30,6 +30,8 @@
#include <binder/IServiceManager.h>
#include <binder/Status.h>
#include <cutils/properties.h>
+#include <json/value.h>
+#include <json/writer.h>
#include <log/log.h>
#include <netdutils/DumpWriter.h>
#include <utils/Errors.h>
@@ -38,6 +40,7 @@
#include "BinderUtil.h"
#include "Controllers.h"
#include "InterfaceController.h"
+#include "NetdConstants.h" // SHA256_SIZE
#include "NetdNativeService.h"
#include "NetdPermissions.h"
#include "OemNetdListener.h"
@@ -905,16 +908,11 @@
}
binder::Status NetdNativeService::tetherStart(const std::vector<std::string>& dhcpRanges) {
- return tetherStartWithConfiguration(true, dhcpRanges);
-}
-
-binder::Status NetdNativeService::tetherStartWithConfiguration(
- bool usingLegacyDnsProxy, const std::vector<std::string>& dhcpRanges) {
NETD_LOCKING_RPC(gCtls->tetherCtrl.lock, PERM_NETWORK_STACK, PERM_MAINLINE_NETWORK_STACK);
if (dhcpRanges.size() % 2 == 1) {
return statusFromErrcode(-EINVAL);
}
- int res = gCtls->tetherCtrl.startTethering(usingLegacyDnsProxy, dhcpRanges);
+ int res = gCtls->tetherCtrl.startTethering(dhcpRanges);
return statusFromErrcode(res);
}
diff --git a/server/NetdNativeService.h b/server/NetdNativeService.h
index 8f9c40f..68c9886 100644
--- a/server/NetdNativeService.h
+++ b/server/NetdNativeService.h
@@ -121,8 +121,6 @@
binder::Status tetherGetStats(
std::vector<android::net::TetherStatsParcel>* tetherStatsVec) override;
binder::Status tetherStart(const std::vector<std::string>& dhcpRanges) override;
- binder::Status tetherStartWithConfiguration(
- bool usingLegacyDnsProxy, const std::vector<std::string>& dhcpRanges) override;
binder::Status tetherStop() override;
binder::Status tetherIsEnabled(bool* enabled) override;
binder::Status tetherInterfaceAdd(const std::string& ifName) override;
diff --git a/server/NetlinkCommands.cpp b/server/NetlinkCommands.cpp
index acefa8e..7af33fe 100644
--- a/server/NetlinkCommands.cpp
+++ b/server/NetlinkCommands.cpp
@@ -65,25 +65,17 @@
return response.err.error; // Netlink errors are negative errno.
}
-// Disable optimizations in ASan build.
-// ASan reports an out-of-bounds 32-bit(!) access in the first loop of the function (over iov[]).
-// TODO: verify if this bug is still present.
-#ifdef __clang__
-#if __has_feature(address_sanitizer)
-#define OPTNONE [[clang::optnone]]
-#endif
-#endif
-
-#ifndef OPTNONE
-#define OPTNONE /* nop */
-#endif
-
// Sends a netlink request and possibly expects an ack.
// |iov| is an array of struct iovec that contains the netlink message payload.
// The netlink header is generated by this function based on |action| and |flags|.
// Returns -errno if there was an error or if the kernel reported an error.
-OPTNONE int sendNetlinkRequest(uint16_t action, uint16_t flags, iovec* iov, int iovlen,
- const NetlinkDumpCallback* callback) {
+#ifdef __clang__
+#if __has_feature(address_sanitizer)
+__attribute__((optnone))
+#endif
+#endif
+WARN_UNUSED_RESULT int sendNetlinkRequest(uint16_t action, uint16_t flags, iovec* iov, int iovlen,
+ const NetlinkDumpCallback *callback) {
nlmsghdr nlmsg = {
.nlmsg_type = action,
.nlmsg_flags = flags,
@@ -154,8 +146,8 @@
return 0;
}
-int rtNetlinkFlush(uint16_t getAction, uint16_t deleteAction, const char* what,
- const NetlinkDumpFilter& shouldDelete) {
+WARN_UNUSED_RESULT int rtNetlinkFlush(uint16_t getAction, uint16_t deleteAction,
+ const char *what, const NetlinkDumpFilter& shouldDelete) {
// RTM_GETxxx is always RTM_DELxxx + 1, see <linux/rtnetlink.h>.
if (getAction != deleteAction + 1) {
ALOGE("Unknown flush type getAction=%d deleteAction=%d", getAction, deleteAction);
@@ -209,7 +201,7 @@
return ret;
}
-uint32_t getRtmU32Attribute(const nlmsghdr* nlh, int attribute) {
+uint32_t getRtmU32Attribute(const nlmsghdr *nlh, int attribute) {
uint32_t rta_len = RTM_PAYLOAD(nlh);
rtmsg *msg = reinterpret_cast<rtmsg *>(NLMSG_DATA(nlh));
rtattr *rta = reinterpret_cast<rtattr *> RTM_RTA(msg);
diff --git a/server/NetlinkCommands.h b/server/NetlinkCommands.h
index c07194d..ef1ff48 100644
--- a/server/NetlinkCommands.h
+++ b/server/NetlinkCommands.h
@@ -14,7 +14,8 @@
* limitations under the License.
*/
-#pragma once
+#ifndef NETD_SERVER_NETLINK_UTIL_H
+#define NETD_SERVER_NETLINK_UTIL_H
#include <functional>
#include <linux/netlink.h>
@@ -22,7 +23,8 @@
#include "NetdConstants.h"
-namespace android::net {
+namespace android {
+namespace net {
const sockaddr_nl KERNEL_NLADDR = {AF_NETLINK, 0, 0, 0};
@@ -42,29 +44,41 @@
typedef std::function<bool(nlmsghdr *)> NetlinkDumpFilter;
// Opens an RTNetlink socket and connects it to the kernel.
-[[nodiscard]] int openNetlinkSocket(int protocol);
+WARN_UNUSED_RESULT int openNetlinkSocket(int protocol);
// Receives a netlink ACK. Returns 0 if the command succeeded or negative errno if the command
// failed or receiving the ACK failed.
-[[nodiscard]] int recvNetlinkAck(int sock);
+WARN_UNUSED_RESULT int recvNetlinkAck(int sock);
// Sends a netlink request and possibly expects an ACK. The first element of iov should be null and
// will be set to the netlink message headerheader. The subsequent elements are the contents of the
// request.
-[[nodiscard]] int sendNetlinkRequest(uint16_t action, uint16_t flags, iovec* iov, int iovlen,
- const NetlinkDumpCallback* callback);
+
+// Disable optimizations in ASan build.
+// ASan reports an out-of-bounds 32-bit(!) access in the first loop of the
+// function (over iov[]).
+#ifdef __clang__
+#if __has_feature(address_sanitizer)
+__attribute__((optnone))
+#endif
+#endif
+WARN_UNUSED_RESULT int sendNetlinkRequest(uint16_t action, uint16_t flags, iovec* iov, int iovlen,
+ const NetlinkDumpCallback *callback);
// Processes a netlink dump, passing every message to the specified |callback|.
-[[nodiscard]] int processNetlinkDump(int sock, const NetlinkDumpCallback& callback);
+WARN_UNUSED_RESULT int processNetlinkDump(int sock, const NetlinkDumpCallback& callback);
// Flushes netlink objects that take an rtmsg structure (FIB rules, routes...). |getAction| and
// |deleteAction| specify the netlink message types, e.g., RTM_GETRULE and RTM_DELRULE.
// |shouldDelete| specifies whether a given object should be deleted or not. |what| is a
// human-readable name for the objects being flushed, e.g. "rules".
-[[nodiscard]] int rtNetlinkFlush(uint16_t getAction, uint16_t deleteAction, const char* what,
- const NetlinkDumpFilter& shouldDelete);
+WARN_UNUSED_RESULT int rtNetlinkFlush(uint16_t getAction, uint16_t deleteAction,
+ const char *what, const NetlinkDumpFilter& shouldDelete);
// Returns the value of the specific __u32 attribute, or 0 if the attribute was not present.
uint32_t getRtmU32Attribute(const nlmsghdr *nlh, int attribute);
-} // namespace android::net
+} // namespace net
+} // namespace android
+
+#endif // NETD_SERVER_NETLINK_UTIL_H
diff --git a/server/Network.h b/server/Network.h
index 8417f34..69af3ff 100644
--- a/server/Network.h
+++ b/server/Network.h
@@ -14,14 +14,16 @@
* limitations under the License.
*/
-#pragma once
+#ifndef NETD_SERVER_NETWORK_H
+#define NETD_SERVER_NETWORK_H
#include "NetdConstants.h"
#include <set>
#include <string>
-namespace android::net {
+namespace android {
+namespace net {
// A Network represents a collection of interfaces participating as a single administrative unit.
class Network {
@@ -45,9 +47,9 @@
const std::set<std::string>& getInterfaces() const;
// These return 0 on success or negative errno on failure.
- [[nodiscard]] virtual int addInterface(const std::string& interface) = 0;
- [[nodiscard]] virtual int removeInterface(const std::string& interface) = 0;
- [[nodiscard]] int clearInterfaces();
+ virtual int addInterface(const std::string& interface) WARN_UNUSED_RESULT = 0;
+ virtual int removeInterface(const std::string& interface) WARN_UNUSED_RESULT = 0;
+ int clearInterfaces() WARN_UNUSED_RESULT;
std::string toString() const;
@@ -58,4 +60,7 @@
std::set<std::string> mInterfaces;
};
-} // namespace android::net
+} // namespace net
+} // namespace android
+
+#endif // NETD_SERVER_NETWORK_H
diff --git a/server/NetworkController.cpp b/server/NetworkController.cpp
index e490a9f..1e4894f 100644
--- a/server/NetworkController.cpp
+++ b/server/NetworkController.cpp
@@ -47,7 +47,8 @@
using android::netdutils::DumpWriter;
-namespace android::net {
+namespace android {
+namespace net {
namespace {
@@ -66,21 +67,21 @@
// setPermissionForNetworks).
// TODO: use std::mutex and GUARDED_BY instead of manual inspection.
class NetworkController::DelegateImpl : public PhysicalNetwork::Delegate {
- public:
+public:
explicit DelegateImpl(NetworkController* networkController);
virtual ~DelegateImpl();
- [[nodiscard]] int modifyFallthrough(unsigned vpnNetId, const std::string& physicalInterface,
- Permission permission, bool add);
+ int modifyFallthrough(unsigned vpnNetId, const std::string& physicalInterface,
+ Permission permission, bool add) WARN_UNUSED_RESULT;
- private:
- [[nodiscard]] int addFallthrough(const std::string& physicalInterface,
- Permission permission) override;
- [[nodiscard]] int removeFallthrough(const std::string& physicalInterface,
- Permission permission) override;
+private:
+ int addFallthrough(const std::string& physicalInterface,
+ Permission permission) override WARN_UNUSED_RESULT;
+ int removeFallthrough(const std::string& physicalInterface,
+ Permission permission) override WARN_UNUSED_RESULT;
- [[nodiscard]] int modifyFallthrough(const std::string& physicalInterface, Permission permission,
- bool add);
+ int modifyFallthrough(const std::string& physicalInterface, Permission permission,
+ bool add) WARN_UNUSED_RESULT;
NetworkController* const mNetworkController;
};
@@ -235,6 +236,11 @@
return fwmark.intValue;
}
+uint32_t NetworkController::getNetworkForDns(unsigned* netId, uid_t uid) const {
+ ScopedRLock lock(mRWLock);
+ return getNetworkForDnsLocked(netId, uid);
+}
+
// Returns the NetId that a given UID would use if no network is explicitly selected. Specifically,
// the VPN that applies to the UID if any; otherwise, the default network.
unsigned NetworkController::getNetworkForUser(uid_t uid) const {
@@ -844,4 +850,5 @@
}
}
-} // namespace android::net
+} // namespace net
+} // namespace android
diff --git a/server/NetworkController.h b/server/NetworkController.h
index e0abde0..4a363b3 100644
--- a/server/NetworkController.h
+++ b/server/NetworkController.h
@@ -14,7 +14,8 @@
* limitations under the License.
*/
-#pragma once
+#ifndef NETD_SERVER_NETWORK_CONTROLLER_H
+#define NETD_SERVER_NETWORK_CONTROLLER_H
#include <android-base/thread_annotations.h>
#include <android/multinetwork.h>
@@ -36,7 +37,8 @@
struct android_net_context;
-namespace android::net {
+namespace android {
+namespace net {
typedef std::shared_lock<std::shared_mutex> ScopedRLock;
typedef std::lock_guard<std::shared_mutex> ScopedWLock;
@@ -91,40 +93,44 @@
NetworkController();
unsigned getDefaultNetwork() const;
- [[nodiscard]] int setDefaultNetwork(unsigned netId);
+ int setDefaultNetwork(unsigned netId) WARN_UNUSED_RESULT;
+ // Sets |*netId| to an appropriate NetId to use for DNS for the given user. Call with |*netId|
+ // set to a non-NETID_UNSET value if the user already has indicated a preference. Returns the
+ // fwmark value to set on the socket when performing the DNS request.
+ uint32_t getNetworkForDns(unsigned* netId, uid_t uid) const;
unsigned getNetworkForUser(uid_t uid) const;
unsigned getNetworkForConnect(uid_t uid) const;
void getNetworkContext(unsigned netId, uid_t uid, struct android_net_context* netcontext) const;
unsigned getNetworkForInterface(const char* interface) const;
bool isVirtualNetwork(unsigned netId) const;
- [[nodiscard]] int createPhysicalNetwork(unsigned netId, Permission permission);
- [[nodiscard]] int createPhysicalOemNetwork(Permission permission, unsigned* netId);
- [[nodiscard]] int createVirtualNetwork(unsigned netId, bool secure);
- [[nodiscard]] int destroyNetwork(unsigned netId);
+ int createPhysicalNetwork(unsigned netId, Permission permission) WARN_UNUSED_RESULT;
+ int createPhysicalOemNetwork(Permission permission, unsigned *netId) WARN_UNUSED_RESULT;
+ int createVirtualNetwork(unsigned netId, bool secure) WARN_UNUSED_RESULT;
+ int destroyNetwork(unsigned netId) WARN_UNUSED_RESULT;
- [[nodiscard]] int addInterfaceToNetwork(unsigned netId, const char* interface);
- [[nodiscard]] int removeInterfaceFromNetwork(unsigned netId, const char* interface);
+ int addInterfaceToNetwork(unsigned netId, const char* interface) WARN_UNUSED_RESULT;
+ int removeInterfaceFromNetwork(unsigned netId, const char* interface) WARN_UNUSED_RESULT;
Permission getPermissionForUser(uid_t uid) const;
void setPermissionForUsers(Permission permission, const std::vector<uid_t>& uids);
int checkUserNetworkAccess(uid_t uid, unsigned netId) const;
- [[nodiscard]] int setPermissionForNetworks(Permission permission,
- const std::vector<unsigned>& netIds);
+ int setPermissionForNetworks(Permission permission,
+ const std::vector<unsigned>& netIds) WARN_UNUSED_RESULT;
- [[nodiscard]] int addUsersToNetwork(unsigned netId, const UidRanges& uidRanges);
- [[nodiscard]] int removeUsersFromNetwork(unsigned netId, const UidRanges& uidRanges);
+ int addUsersToNetwork(unsigned netId, const UidRanges& uidRanges) WARN_UNUSED_RESULT;
+ int removeUsersFromNetwork(unsigned netId, const UidRanges& uidRanges) WARN_UNUSED_RESULT;
// |nexthop| can be NULL (to indicate a directly-connected route), "unreachable" (to indicate a
// route that's blocked), "throw" (to indicate the lack of a match), or a regular IP address.
//
// Routes are added to tables determined by the interface, so only |interface| is actually used.
// |netId| is given only to sanity check that the interface has the correct netId.
- [[nodiscard]] int addRoute(unsigned netId, const char* interface, const char* destination,
- const char* nexthop, bool legacy, uid_t uid);
- [[nodiscard]] int removeRoute(unsigned netId, const char* interface, const char* destination,
- const char* nexthop, bool legacy, uid_t uid);
+ int addRoute(unsigned netId, const char* interface, const char* destination,
+ const char* nexthop, bool legacy, uid_t uid) WARN_UNUSED_RESULT;
+ int removeRoute(unsigned netId, const char* interface, const char* destination,
+ const char* nexthop, bool legacy, uid_t uid) WARN_UNUSED_RESULT;
// Notes that the specified address has appeared on the specified interface.
void addInterfaceAddress(unsigned ifIndex, const char* address);
@@ -141,12 +147,7 @@
private:
bool isValidNetworkLocked(unsigned netId) const;
Network* getNetworkLocked(unsigned netId) const;
-
- // Sets |*netId| to an appropriate NetId to use for DNS for the given user. Call with |*netId|
- // set to a non-NETID_UNSET value if the user already has indicated a preference. Returns the
- // fwmark value to set on the socket when performing the DNS request.
uint32_t getNetworkForDnsLocked(unsigned* netId, uid_t uid) const;
-
unsigned getNetworkForUserLocked(uid_t uid) const;
unsigned getNetworkForConnectLocked(uid_t uid) const;
unsigned getNetworkForInterfaceLocked(const char* interface) const;
@@ -155,11 +156,11 @@
VirtualNetwork* getVirtualNetworkForUserLocked(uid_t uid) const;
Permission getPermissionForUserLocked(uid_t uid) const;
int checkUserNetworkAccessLocked(uid_t uid, unsigned netId) const;
- [[nodiscard]] int createPhysicalNetworkLocked(unsigned netId, Permission permission);
+ int createPhysicalNetworkLocked(unsigned netId, Permission permission) WARN_UNUSED_RESULT;
- [[nodiscard]] int modifyRoute(unsigned netId, const char* interface, const char* destination,
- const char* nexthop, bool add, bool legacy, uid_t uid);
- [[nodiscard]] int modifyFallthroughLocked(unsigned vpnNetId, bool add);
+ int modifyRoute(unsigned netId, const char* interface, const char* destination,
+ const char* nexthop, bool add, bool legacy, uid_t uid) WARN_UNUSED_RESULT;
+ int modifyFallthroughLocked(unsigned vpnNetId, bool add) WARN_UNUSED_RESULT;
void updateTcpSocketMonitorPolling();
class DelegateImpl;
@@ -188,4 +189,7 @@
};
-} // namespace android::net
+} // namespace net
+} // namespace android
+
+#endif // NETD_SERVER_NETWORK_CONTROLLER_H
diff --git a/server/PhysicalNetwork.cpp b/server/PhysicalNetwork.cpp
index 56f4ac3..7a1f305 100644
--- a/server/PhysicalNetwork.cpp
+++ b/server/PhysicalNetwork.cpp
@@ -23,12 +23,13 @@
#include "log/log.h"
-namespace android::net {
+namespace android {
+namespace net {
namespace {
-[[nodiscard]] int addToDefault(unsigned netId, const std::string& interface, Permission permission,
- PhysicalNetwork::Delegate* delegate) {
+WARN_UNUSED_RESULT int addToDefault(unsigned netId, const std::string& interface,
+ Permission permission, PhysicalNetwork::Delegate* delegate) {
if (int ret = RouteController::addInterfaceToDefaultNetwork(interface.c_str(), permission)) {
ALOGE("failed to add interface %s to default netId %u", interface.c_str(), netId);
return ret;
@@ -39,8 +40,9 @@
return 0;
}
-[[nodiscard]] int removeFromDefault(unsigned netId, const std::string& interface,
- Permission permission, PhysicalNetwork::Delegate* delegate) {
+WARN_UNUSED_RESULT int removeFromDefault(unsigned netId, const std::string& interface,
+ Permission permission,
+ PhysicalNetwork::Delegate* delegate) {
if (int ret = RouteController::removeInterfaceFromDefaultNetwork(interface.c_str(),
permission)) {
ALOGE("failed to remove interface %s from default netId %u", interface.c_str(), netId);
@@ -54,13 +56,15 @@
} // namespace
-PhysicalNetwork::Delegate::~Delegate() {}
+PhysicalNetwork::Delegate::~Delegate() {
+}
PhysicalNetwork::PhysicalNetwork(unsigned netId, PhysicalNetwork::Delegate* delegate) :
Network(netId), mDelegate(delegate), mPermission(PERMISSION_NONE), mIsDefault(false) {
}
-PhysicalNetwork::~PhysicalNetwork() {}
+PhysicalNetwork::~PhysicalNetwork() {
+}
Permission PhysicalNetwork::getPermission() const {
return mPermission;
@@ -202,4 +206,5 @@
return 0;
}
-} // namespace android::net
+} // namespace net
+} // namespace android
diff --git a/server/PhysicalNetwork.h b/server/PhysicalNetwork.h
index 0720824..89c9443 100644
--- a/server/PhysicalNetwork.h
+++ b/server/PhysicalNetwork.h
@@ -14,23 +14,25 @@
* limitations under the License.
*/
-#pragma once
+#ifndef NETD_SERVER_PHYSICAL_NETWORK_H
+#define NETD_SERVER_PHYSICAL_NETWORK_H
#include "Network.h"
#include "Permission.h"
-namespace android::net {
+namespace android {
+namespace net {
class PhysicalNetwork : public Network {
- public:
+public:
class Delegate {
- public:
+ public:
virtual ~Delegate();
- [[nodiscard]] virtual int addFallthrough(const std::string& physicalInterface,
- Permission permission) = 0;
- [[nodiscard]] virtual int removeFallthrough(const std::string& physicalInterface,
- Permission permission) = 0;
+ virtual int addFallthrough(const std::string& physicalInterface,
+ Permission permission) WARN_UNUSED_RESULT = 0;
+ virtual int removeFallthrough(const std::string& physicalInterface,
+ Permission permission) WARN_UNUSED_RESULT = 0;
};
PhysicalNetwork(unsigned netId, Delegate* delegate);
@@ -38,15 +40,15 @@
// These refer to permissions that apps must have in order to use this network.
Permission getPermission() const;
- [[nodiscard]] int setPermission(Permission permission);
+ int setPermission(Permission permission) WARN_UNUSED_RESULT;
- [[nodiscard]] int addAsDefault();
- [[nodiscard]] int removeAsDefault();
+ int addAsDefault() WARN_UNUSED_RESULT;
+ int removeAsDefault() WARN_UNUSED_RESULT;
- private:
+private:
Type getType() const override;
- [[nodiscard]] int addInterface(const std::string& interface) override;
- [[nodiscard]] int removeInterface(const std::string& interface) override;
+ int addInterface(const std::string& interface) override WARN_UNUSED_RESULT;
+ int removeInterface(const std::string& interface) override WARN_UNUSED_RESULT;
int destroySocketsLackingPermission(Permission permission);
void invalidateRouteCache(const std::string& interface);
@@ -55,4 +57,7 @@
bool mIsDefault;
};
-} // namespace android::net
+} // namespace net
+} // namespace android
+
+#endif // NETD_SERVER_PHYSICAL_NETWORK_H
diff --git a/server/ResolvStub.cpp b/server/ResolvStub.cpp
index 2fb6625..5928790 100644
--- a/server/ResolvStub.cpp
+++ b/server/ResolvStub.cpp
@@ -55,7 +55,7 @@
}
int resolv_stub_init() {
- void* netdResolvHandle = nullptr;
+ void* netdResolvHandle;
for (const auto& dir : {APEX_LIB64_DIR, APEX_LIB_DIR}) {
std::string path = std::string(dir) + "/" + LIBNAME;
@@ -64,7 +64,7 @@
ALOGI("Loaded resolver library from %s", path.c_str());
break;
}
- ALOGW("dlopen(%s) failed: %s", path.c_str(), strerror(errno));
+ ALOGW("dlopen(%s) failed: %s", path.c_str(), dlerror());
}
if (netdResolvHandle == nullptr) {
@@ -76,7 +76,6 @@
#define RESOLV_STUB_LOAD_SYMBOL(x) resolvStubInitFunction(netdResolvHandle, STR(x), &RESOLV_STUB.x)
RESOLV_STUB_LOAD_SYMBOL(resolv_has_nameservers);
RESOLV_STUB_LOAD_SYMBOL(resolv_init);
- RESOLV_STUB_LOAD_SYMBOL(resolv_gethostbyaddr_from_cache);
#undef RESOLV_STUB_LOAD_SYMBOL
#undef STR
diff --git a/server/RouteController.cpp b/server/RouteController.cpp
index a62ed6b..6722372 100644
--- a/server/RouteController.cpp
+++ b/server/RouteController.cpp
@@ -38,6 +38,7 @@
#include <android-base/file.h>
#include <android-base/stringprintf.h>
#include "log/log.h"
+#include "logwrap/logwrap.h"
#include "netid_client.h"
#include "netutils/ifc.h"
@@ -45,7 +46,8 @@
using android::base::WriteStringToFile;
using android::net::UidRangeParcel;
-namespace android::net {
+namespace android {
+namespace net {
auto RouteController::iptablesRestoreCommandFunction = execIptablesRestoreCommand;
@@ -102,7 +104,7 @@
// Avoids "non-constant-expression cannot be narrowed from type 'unsigned int' to 'unsigned short'"
// warnings when using RTA_LENGTH(x) inside static initializers (even when x is already uint16_t).
-static constexpr uint16_t U16_RTA_LENGTH(uint16_t x) {
+constexpr uint16_t U16_RTA_LENGTH(uint16_t x) {
return RTA_LENGTH(x);
}
@@ -122,12 +124,12 @@
// END CONSTANTS ----------------------------------------------------------------------------------
-static const char* actionName(uint16_t action) {
+const char *actionName(uint16_t action) {
static const char *ops[4] = {"adding", "deleting", "getting", "???"};
return ops[action % 4];
}
-static const char* familyName(uint8_t family) {
+const char *familyName(uint8_t family) {
switch (family) {
case AF_INET: return "IPv4";
case AF_INET6: return "IPv6";
@@ -137,28 +139,20 @@
// Caller must hold sInterfaceToTableLock.
uint32_t RouteController::getRouteTableForInterfaceLocked(const char* interface) {
- // If we already know the routing table for this interface name, use it.
- // This ensures we can remove rules and routes for an interface that has been removed,
- // or has been removed and re-added with a different interface index.
- //
- // The caller is responsible for ensuring that an interface is never added to a network
- // until it has been removed from any network it was previously in. This ensures that
- // if the same interface disconnects and then reconnects with a different interface ID
- // when the reconnect happens the interface will not be in the map, and the code will
- // determine the new routing table from the interface ID, below.
- auto iter = sInterfaceToTable.find(interface);
- if (iter != sInterfaceToTable.end()) {
- return iter->second;
- }
-
uint32_t index = if_nametoindex(interface);
- if (index == 0) {
+ if (index) {
+ index += RouteController::ROUTE_TABLE_OFFSET_FROM_INDEX;
+ sInterfaceToTable[interface] = index;
+ return index;
+ }
+ // If the interface goes away if_nametoindex() will return 0 but we still need to know
+ // the index so we can remove the rules and routes.
+ auto iter = sInterfaceToTable.find(interface);
+ if (iter == sInterfaceToTable.end()) {
ALOGE("cannot find interface %s", interface);
return RT_TABLE_UNSPEC;
}
- index += RouteController::ROUTE_TABLE_OFFSET_FROM_INDEX;
- sInterfaceToTable[interface] = index;
- return index;
+ return iter->second;
}
uint32_t RouteController::getIfIndex(const char* interface) {
@@ -237,10 +231,9 @@
// range (inclusive). Otherwise, the rule matches packets from all UIDs.
//
// Returns 0 on success or negative errno on failure.
-[[nodiscard]] static int modifyIpRule(uint16_t action, uint32_t priority, uint8_t ruleType,
- uint32_t table, uint32_t fwmark, uint32_t mask,
- const char* iif, const char* oif, uid_t uidStart,
- uid_t uidEnd) {
+WARN_UNUSED_RESULT int modifyIpRule(uint16_t action, uint32_t priority, uint8_t ruleType,
+ uint32_t table, uint32_t fwmark, uint32_t mask, const char* iif,
+ const char* oif, uid_t uidStart, uid_t uidEnd) {
// Ensure that if you set a bit in the fwmark, it's not being ignored by the mask.
if (fwmark & ~mask) {
ALOGE("mask 0x%x does not select all the bits set in fwmark 0x%x", mask, fwmark);
@@ -324,23 +317,23 @@
return 0;
}
-[[nodiscard]] static int modifyIpRule(uint16_t action, uint32_t priority, uint32_t table,
- uint32_t fwmark, uint32_t mask, const char* iif,
- const char* oif, uid_t uidStart, uid_t uidEnd) {
+WARN_UNUSED_RESULT int modifyIpRule(uint16_t action, uint32_t priority, uint32_t table,
+ uint32_t fwmark, uint32_t mask, const char* iif,
+ const char* oif, uid_t uidStart, uid_t uidEnd) {
return modifyIpRule(action, priority, FR_ACT_TO_TBL, table, fwmark, mask, iif, oif, uidStart,
uidEnd);
}
-[[nodiscard]] static int modifyIpRule(uint16_t action, uint32_t priority, uint32_t table,
- uint32_t fwmark, uint32_t mask) {
+WARN_UNUSED_RESULT int modifyIpRule(uint16_t action, uint32_t priority, uint32_t table,
+ uint32_t fwmark, uint32_t mask) {
return modifyIpRule(action, priority, table, fwmark, mask, IIF_NONE, OIF_NONE, INVALID_UID,
INVALID_UID);
}
// Adds or deletes an IPv4 or IPv6 route.
// Returns 0 on success or negative errno on failure.
-int modifyIpRoute(uint16_t action, uint32_t table, const char* interface, const char* destination,
- const char* nexthop) {
+WARN_UNUSED_RESULT int modifyIpRoute(uint16_t action, uint32_t table, const char* interface,
+ const char* destination, const char* nexthop) {
// At least the destination must be non-null.
if (!destination) {
ALOGE("null destination");
@@ -399,11 +392,11 @@
// Assemble a rtmsg and put it in an array of iovec structures.
rtmsg route = {
- .rtm_family = family,
- .rtm_dst_len = prefixLength,
- .rtm_protocol = RTPROT_STATIC,
- .rtm_scope = static_cast<uint8_t>(nexthop ? RT_SCOPE_UNIVERSE : RT_SCOPE_LINK),
- .rtm_type = type,
+ .rtm_protocol = RTPROT_STATIC,
+ .rtm_type = type,
+ .rtm_family = family,
+ .rtm_dst_len = prefixLength,
+ .rtm_scope = static_cast<uint8_t>(nexthop ? RT_SCOPE_UNIVERSE : RT_SCOPE_LINK),
};
rtattr rtaDst = { U16_RTA_LENGTH(rawLength), RTA_DST };
@@ -447,8 +440,8 @@
// replies, SYN-ACKs, etc).
// + Mark sockets that accept connections from this interface so that the connection stays on the
// same interface.
-int modifyIncomingPacketMark(unsigned netId, const char* interface, Permission permission,
- bool add) {
+WARN_UNUSED_RESULT int modifyIncomingPacketMark(unsigned netId, const char* interface,
+ Permission permission, bool add) {
Fwmark fwmark;
fwmark.netId = netId;
@@ -473,7 +466,7 @@
//
// When a VPN is in effect, packets from the local network to upstream networks are forwarded into
// the VPN's tunnel interface. When the VPN forwards the responses, they emerge out of the tunnel.
-[[nodiscard]] static int modifyVpnOutputToLocalRule(const char* vpnInterface, bool add) {
+WARN_UNUSED_RESULT int modifyVpnOutputToLocalRule(const char* vpnInterface, bool add) {
return modifyIpRule(add ? RTM_NEWRULE : RTM_DELRULE, RULE_PRIORITY_VPN_OUTPUT_TO_LOCAL,
ROUTE_TABLE_LOCAL_NETWORK, MARK_UNSET, MARK_UNSET, vpnInterface, OIF_NONE,
INVALID_UID, INVALID_UID);
@@ -484,8 +477,8 @@
// Notice that this rule doesn't use the netId. I.e., no matter what netId the user's socket may
// have, if they are subject to this VPN, their traffic has to go through it. Allows the traffic to
// bypass the VPN if the protectedFromVpn bit is set.
-[[nodiscard]] static int modifyVpnUidRangeRule(uint32_t table, uid_t uidStart, uid_t uidEnd,
- bool secure, bool add) {
+WARN_UNUSED_RESULT int modifyVpnUidRangeRule(uint32_t table, uid_t uidStart, uid_t uidEnd,
+ bool secure, bool add) {
Fwmark fwmark;
Fwmark mask;
@@ -512,8 +505,8 @@
//
// This is needed for DnsProxyListener to correctly resolve a request for a user who is in the
// target set, but where the DnsProxyListener itself is not.
-[[nodiscard]] static int modifyVpnSystemPermissionRule(unsigned netId, uint32_t table, bool secure,
- bool add) {
+WARN_UNUSED_RESULT int modifyVpnSystemPermissionRule(unsigned netId, uint32_t table, bool secure,
+ bool add) {
Fwmark fwmark;
Fwmark mask;
@@ -536,9 +529,9 @@
// Even though we check permissions at the time we set a netId into the fwmark of a socket, we need
// to check it again in the rules here, because a network's permissions may have been updated via
// modifyNetworkPermission().
-[[nodiscard]] static int modifyExplicitNetworkRule(unsigned netId, uint32_t table,
- Permission permission, uid_t uidStart,
- uid_t uidEnd, bool add) {
+WARN_UNUSED_RESULT int modifyExplicitNetworkRule(unsigned netId, uint32_t table,
+ Permission permission, uid_t uidStart,
+ uid_t uidEnd, bool add) {
Fwmark fwmark;
Fwmark mask;
@@ -559,9 +552,9 @@
//
// Supports apps that use SO_BINDTODEVICE or IP_PKTINFO options and the kernel that already knows
// the outgoing interface (typically for link-local communications).
-[[nodiscard]] static int modifyOutputInterfaceRules(const char* interface, uint32_t table,
- Permission permission, uid_t uidStart,
- uid_t uidEnd, bool add) {
+WARN_UNUSED_RESULT int modifyOutputInterfaceRules(const char* interface, uint32_t table,
+ Permission permission, uid_t uidStart,
+ uid_t uidEnd, bool add) {
Fwmark fwmark;
Fwmark mask;
@@ -588,7 +581,7 @@
// This is for sockets that have not explicitly requested a particular network, but have been
// bound to one when they called connect(). This ensures that sockets connected on a particular
// network stay on that network even if the default network changes.
-[[nodiscard]] static int modifyImplicitNetworkRule(unsigned netId, uint32_t table, bool add) {
+WARN_UNUSED_RESULT int modifyImplicitNetworkRule(unsigned netId, uint32_t table, bool add) {
Fwmark fwmark;
Fwmark mask;
@@ -610,9 +603,9 @@
//
// If a packet with a VPN's netId doesn't find a route in the VPN's routing table, it's allowed to
// go over the default network, provided it has the permissions required by the default network.
-int RouteController::modifyVpnFallthroughRule(uint16_t action, unsigned vpnNetId,
- const char* physicalInterface,
- Permission permission) {
+WARN_UNUSED_RESULT int RouteController::modifyVpnFallthroughRule(uint16_t action, unsigned vpnNetId,
+ const char* physicalInterface,
+ Permission permission) {
uint32_t table = getRouteTableForInterface(physicalInterface);
if (table == RT_TABLE_UNSPEC) {
return -ESRCH;
@@ -632,7 +625,7 @@
}
// Add rules to allow legacy routes added through the requestRouteToHost() API.
-[[nodiscard]] static int addLegacyRouteRules() {
+WARN_UNUSED_RESULT int addLegacyRouteRules() {
Fwmark fwmark;
Fwmark mask;
@@ -658,7 +651,7 @@
}
// Add rules to lookup the local network when specified explicitly or otherwise.
-[[nodiscard]] static int addLocalNetworkRules(unsigned localNetId) {
+WARN_UNUSED_RESULT int addLocalNetworkRules(unsigned localNetId) {
if (int ret = modifyExplicitNetworkRule(localNetId, ROUTE_TABLE_LOCAL_NETWORK, PERMISSION_NONE,
INVALID_UID, INVALID_UID, ACTION_ADD)) {
return ret;
@@ -712,12 +705,12 @@
// relying on the kernel-default "from all lookup main" rule at priority 32766 is not intended
// behaviour. We do flush the kernel-default rules at startup, but having an explicit unreachable
// rule will hopefully make things even clearer.
-[[nodiscard]] static int addUnreachableRule() {
+WARN_UNUSED_RESULT int addUnreachableRule() {
return modifyIpRule(RTM_NEWRULE, RULE_PRIORITY_UNREACHABLE, FR_ACT_UNREACHABLE, RT_TABLE_UNSPEC,
MARK_UNSET, MARK_UNSET, IIF_NONE, OIF_NONE, INVALID_UID, INVALID_UID);
}
-[[nodiscard]] static int modifyLocalNetwork(unsigned netId, const char* interface, bool add) {
+WARN_UNUSED_RESULT int modifyLocalNetwork(unsigned netId, const char* interface, bool add) {
if (int ret = modifyIncomingPacketMark(netId, interface, PERMISSION_NONE, add)) {
return ret;
}
@@ -726,8 +719,8 @@
}
/* static */
-int RouteController::modifyPhysicalNetwork(unsigned netId, const char* interface,
- Permission permission, bool add) {
+WARN_UNUSED_RESULT int RouteController::modifyPhysicalNetwork(unsigned netId, const char* interface,
+ Permission permission, bool add) {
uint32_t table = getRouteTableForInterface(interface);
if (table == RT_TABLE_UNSPEC) {
return -ESRCH;
@@ -771,7 +764,7 @@
return 0;
}
-[[nodiscard]] static int modifyRejectNonSecureNetworkRule(const UidRanges& uidRanges, bool add) {
+WARN_UNUSED_RESULT int modifyRejectNonSecureNetworkRule(const UidRanges& uidRanges, bool add) {
Fwmark fwmark;
Fwmark mask;
fwmark.protectedFromVpn = false;
@@ -788,9 +781,10 @@
return 0;
}
-int RouteController::modifyVirtualNetwork(unsigned netId, const char* interface,
- const UidRanges& uidRanges, bool secure, bool add,
- bool modifyNonUidBasedRules) {
+WARN_UNUSED_RESULT int RouteController::modifyVirtualNetwork(unsigned netId, const char* interface,
+ const UidRanges& uidRanges,
+ bool secure, bool add,
+ bool modifyNonUidBasedRules) {
uint32_t table = getRouteTableForInterface(interface);
if (table == RT_TABLE_UNSPEC) {
return -ESRCH;
@@ -826,8 +820,8 @@
return 0;
}
-int RouteController::modifyDefaultNetwork(uint16_t action, const char* interface,
- Permission permission) {
+WARN_UNUSED_RESULT int RouteController::modifyDefaultNetwork(uint16_t action, const char* interface,
+ Permission permission) {
uint32_t table = getRouteTableForInterface(interface);
if (table == RT_TABLE_UNSPEC) {
return -ESRCH;
@@ -846,8 +840,9 @@
mask.intValue, IIF_LOOPBACK, OIF_NONE, INVALID_UID, INVALID_UID);
}
-int RouteController::modifyTetheredNetwork(uint16_t action, const char* inputInterface,
- const char* outputInterface) {
+WARN_UNUSED_RESULT int RouteController::modifyTetheredNetwork(uint16_t action,
+ const char* inputInterface,
+ const char* outputInterface) {
uint32_t table = getRouteTableForInterface(outputInterface);
if (table == RT_TABLE_UNSPEC) {
return -ESRCH;
@@ -859,8 +854,9 @@
// Adds or removes an IPv4 or IPv6 route to the specified table.
// Returns 0 on success or negative errno on failure.
-int RouteController::modifyRoute(uint16_t action, const char* interface, const char* destination,
- const char* nexthop, TableType tableType) {
+WARN_UNUSED_RESULT int RouteController::modifyRoute(uint16_t action, const char* interface,
+ const char* destination, const char* nexthop,
+ TableType tableType) {
uint32_t table;
switch (tableType) {
case RouteController::INTERFACE: {
@@ -893,7 +889,7 @@
return 0;
}
-[[nodiscard]] static int clearTetheringRules(const char* inputInterface) {
+WARN_UNUSED_RESULT int clearTetheringRules(const char* inputInterface) {
int ret = 0;
while (ret == 0) {
ret = modifyIpRule(RTM_DELRULE, RULE_PRIORITY_TETHERING, 0, MARK_UNSET, MARK_UNSET,
@@ -915,7 +911,7 @@
return getRtmU32Attribute(nlh, RTA_TABLE);
}
-[[nodiscard]] static int flushRules() {
+WARN_UNUSED_RESULT int flushRules() {
NetlinkDumpFilter shouldDelete = [] (nlmsghdr *nlh) {
// Don't touch rules at priority 0 because by default they are used for local input.
return getRulePriority(nlh) != 0;
@@ -923,7 +919,7 @@
return rtNetlinkFlush(RTM_GETRULE, RTM_DELRULE, "rules", shouldDelete);
}
-int RouteController::flushRoutes(uint32_t table) {
+WARN_UNUSED_RESULT int RouteController::flushRoutes(uint32_t table) {
NetlinkDumpFilter shouldDelete = [table] (nlmsghdr *nlh) {
return getRouteTable(nlh) == table;
};
@@ -932,7 +928,7 @@
}
// Returns 0 on success or negative errno on failure.
-int RouteController::flushRoutes(const char* interface) {
+WARN_UNUSED_RESULT int RouteController::flushRoutes(const char* interface) {
std::lock_guard lock(sInterfaceToTableLock);
uint32_t table = getRouteTableForInterfaceLocked(interface);
@@ -1098,4 +1094,6 @@
std::mutex RouteController::sInterfaceToTableLock;
std::map<std::string, uint32_t> RouteController::sInterfaceToTable;
-} // namespace android::net
+
+} // namespace net
+} // namespace android
diff --git a/server/RouteController.h b/server/RouteController.h
index e36320f..d40288b 100644
--- a/server/RouteController.h
+++ b/server/RouteController.h
@@ -14,9 +14,10 @@
* limitations under the License.
*/
-#pragma once
+#ifndef NETD_SERVER_ROUTE_CONTROLLER_H
+#define NETD_SERVER_ROUTE_CONTROLLER_H
-#include "NetdConstants.h" // IptablesTarget
+#include "NetdConstants.h"
#include "Permission.h"
#include <android-base/thread_annotations.h>
@@ -26,7 +27,8 @@
#include <map>
#include <mutex>
-namespace android::net {
+namespace android {
+namespace net {
class UidRanges;
@@ -44,7 +46,7 @@
static const char* const LOCAL_MANGLE_INPUT;
- [[nodiscard]] static int Init(unsigned localNetId);
+ static int Init(unsigned localNetId) WARN_UNUSED_RESULT;
// Returns an ifindex given the interface name, by looking up in sInterfaceToTable.
// This is currently only used by NetworkController::addInterfaceToNetwork
@@ -55,56 +57,55 @@
// used to add them.
static uint32_t getIfIndex(const char* interface) EXCLUDES(sInterfaceToTableLock);
- [[nodiscard]] static int addInterfaceToLocalNetwork(unsigned netId, const char* interface);
- [[nodiscard]] static int removeInterfaceFromLocalNetwork(unsigned netId, const char* interface);
+ static int addInterfaceToLocalNetwork(unsigned netId, const char* interface) WARN_UNUSED_RESULT;
+ static int removeInterfaceFromLocalNetwork(unsigned netId,
+ const char* interface) WARN_UNUSED_RESULT;
- [[nodiscard]] static int addInterfaceToPhysicalNetwork(unsigned netId, const char* interface,
- Permission permission);
- [[nodiscard]] static int removeInterfaceFromPhysicalNetwork(unsigned netId,
- const char* interface,
- Permission permission);
+ static int addInterfaceToPhysicalNetwork(unsigned netId, const char* interface,
+ Permission permission) WARN_UNUSED_RESULT;
+ static int removeInterfaceFromPhysicalNetwork(unsigned netId, const char* interface,
+ Permission permission) WARN_UNUSED_RESULT;
- [[nodiscard]] static int addInterfaceToVirtualNetwork(unsigned netId, const char* interface,
- bool secure, const UidRanges& uidRanges);
- [[nodiscard]] static int removeInterfaceFromVirtualNetwork(unsigned netId,
- const char* interface, bool secure,
- const UidRanges& uidRanges);
+ static int addInterfaceToVirtualNetwork(unsigned netId, const char* interface, bool secure,
+ const UidRanges& uidRanges) WARN_UNUSED_RESULT;
+ static int removeInterfaceFromVirtualNetwork(unsigned netId, const char* interface, bool secure,
+ const UidRanges& uidRanges) WARN_UNUSED_RESULT;
- [[nodiscard]] static int modifyPhysicalNetworkPermission(unsigned netId, const char* interface,
- Permission oldPermission,
- Permission newPermission);
+ static int modifyPhysicalNetworkPermission(unsigned netId, const char* interface,
+ Permission oldPermission,
+ Permission newPermission) WARN_UNUSED_RESULT;
- [[nodiscard]] static int addUsersToVirtualNetwork(unsigned netId, const char* interface,
- bool secure, const UidRanges& uidRanges);
- [[nodiscard]] static int removeUsersFromVirtualNetwork(unsigned netId, const char* interface,
- bool secure, const UidRanges& uidRanges);
+ static int addUsersToVirtualNetwork(unsigned netId, const char* interface, bool secure,
+ const UidRanges& uidRanges) WARN_UNUSED_RESULT;
+ static int removeUsersFromVirtualNetwork(unsigned netId, const char* interface, bool secure,
+ const UidRanges& uidRanges) WARN_UNUSED_RESULT;
- [[nodiscard]] static int addUsersToRejectNonSecureNetworkRule(const UidRanges& uidRanges);
- [[nodiscard]] static int removeUsersFromRejectNonSecureNetworkRule(const UidRanges& uidRanges);
+ static int addUsersToRejectNonSecureNetworkRule(const UidRanges& uidRanges)
+ WARN_UNUSED_RESULT;
+ static int removeUsersFromRejectNonSecureNetworkRule(const UidRanges& uidRanges)
+ WARN_UNUSED_RESULT;
- [[nodiscard]] static int addInterfaceToDefaultNetwork(const char* interface,
- Permission permission);
- [[nodiscard]] static int removeInterfaceFromDefaultNetwork(const char* interface,
- Permission permission);
+ static int addInterfaceToDefaultNetwork(const char* interface,
+ Permission permission) WARN_UNUSED_RESULT;
+ static int removeInterfaceFromDefaultNetwork(const char* interface,
+ Permission permission) WARN_UNUSED_RESULT;
// |nexthop| can be NULL (to indicate a directly-connected route), "unreachable" (to indicate a
// route that's blocked), "throw" (to indicate the lack of a match), or a regular IP address.
- [[nodiscard]] static int addRoute(const char* interface, const char* destination,
- const char* nexthop, TableType tableType);
- [[nodiscard]] static int removeRoute(const char* interface, const char* destination,
- const char* nexthop, TableType tableType);
+ static int addRoute(const char* interface, const char* destination, const char* nexthop,
+ TableType tableType) WARN_UNUSED_RESULT;
+ static int removeRoute(const char* interface, const char* destination, const char* nexthop,
+ TableType tableType) WARN_UNUSED_RESULT;
- [[nodiscard]] static int enableTethering(const char* inputInterface,
- const char* outputInterface);
- [[nodiscard]] static int disableTethering(const char* inputInterface,
- const char* outputInterface);
+ static int enableTethering(const char* inputInterface,
+ const char* outputInterface) WARN_UNUSED_RESULT;
+ static int disableTethering(const char* inputInterface,
+ const char* outputInterface) WARN_UNUSED_RESULT;
- [[nodiscard]] static int addVirtualNetworkFallthrough(unsigned vpnNetId,
- const char* physicalInterface,
- Permission permission);
- [[nodiscard]] static int removeVirtualNetworkFallthrough(unsigned vpnNetId,
- const char* physicalInterface,
- Permission permission);
+ static int addVirtualNetworkFallthrough(unsigned vpnNetId, const char* physicalInterface,
+ Permission permission) WARN_UNUSED_RESULT;
+ static int removeVirtualNetworkFallthrough(unsigned vpnNetId, const char* physicalInterface,
+ Permission permission) WARN_UNUSED_RESULT;
// For testing.
static int (*iptablesRestoreCommandFunction)(IptablesTarget, const std::string&,
@@ -117,9 +118,9 @@
static std::map<std::string, uint32_t> sInterfaceToTable GUARDED_BY(sInterfaceToTableLock);
static int configureDummyNetwork();
- [[nodiscard]] static int flushRoutes(const char* interface) EXCLUDES(sInterfaceToTableLock);
- [[nodiscard]] static int flushRoutes(uint32_t table);
- static uint32_t getRouteTableForInterfaceLocked(const char* interface)
+ static int flushRoutes(const char* interface) EXCLUDES(sInterfaceToTableLock);
+ static int flushRoutes(uint32_t table);
+ static uint32_t getRouteTableForInterfaceLocked(const char *interface)
REQUIRES(sInterfaceToTableLock);
static uint32_t getRouteTableForInterface(const char *interface) EXCLUDES(sInterfaceToTableLock);
static int modifyDefaultNetwork(uint16_t action, const char* interface, Permission permission);
@@ -140,10 +141,14 @@
// Public because they are called by by RouteControllerTest.cpp.
// TODO: come up with a scheme of unit testing this code that does not rely on making all its
// functions public.
-[[nodiscard]] int modifyIpRoute(uint16_t action, uint32_t table, const char* interface,
- const char* destination, const char* nexthop);
+int modifyIpRoute(uint16_t action, uint32_t table, const char* interface, const char* destination,
+ const char* nexthop) WARN_UNUSED_RESULT;
+int flushRoutes(uint32_t table) WARN_UNUSED_RESULT;
uint32_t getRulePriority(const nlmsghdr *nlh);
-[[nodiscard]] int modifyIncomingPacketMark(unsigned netId, const char* interface,
- Permission permission, bool add);
+WARN_UNUSED_RESULT int modifyIncomingPacketMark(unsigned netId, const char* interface,
+ Permission permission, bool add);
-} // namespace android::net
+} // namespace net
+} // namespace android
+
+#endif // NETD_SERVER_ROUTE_CONTROLLER_H
diff --git a/server/SockDiag.cpp b/server/SockDiag.cpp
index 3e1f384..33c523a 100644
--- a/server/SockDiag.cpp
+++ b/server/SockDiag.cpp
@@ -14,29 +14,27 @@
* limitations under the License.
*/
-#define LOG_TAG "Netd"
-
-#include "SockDiag.h"
-
#include <errno.h>
-#include <linux/inet_diag.h>
-#include <linux/netlink.h>
-#include <linux/sock_diag.h>
#include <netdb.h>
+#include <string.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
-#include <string.h>
#include <sys/socket.h>
#include <sys/uio.h>
-#include <cinttypes>
+#include <linux/netlink.h>
+#include <linux/sock_diag.h>
+#include <linux/inet_diag.h>
+
+#define LOG_TAG "Netd"
#include <android-base/strings.h>
#include <log/log.h>
-#include <netdutils/InternetAddresses.h>
#include <netdutils/Stopwatch.h>
+#include "NetdConstants.h"
#include "Permission.h"
+#include "SockDiag.h"
#ifndef SOCK_DESTROY
#define SOCK_DESTROY 21
@@ -46,7 +44,6 @@
namespace android {
-using netdutils::ScopedAddrinfo;
using netdutils::Stopwatch;
namespace net {
@@ -326,8 +323,7 @@
}
if (mSocketsDestroyed > 0) {
- ALOGI("Destroyed %d sockets on %s in %" PRId64 "us", mSocketsDestroyed, addrstr,
- s.timeTakenUs());
+ ALOGI("Destroyed %d sockets on %s in %.1f ms", mSocketsDestroyed, addrstr, s.timeTaken());
}
return mSocketsDestroyed;
@@ -401,7 +397,7 @@
}
if (mSocketsDestroyed > 0) {
- ALOGI("Destroyed %d sockets for UID in %" PRId64 "us", mSocketsDestroyed, s.timeTakenUs());
+ ALOGI("Destroyed %d sockets for UID in %.1f ms", mSocketsDestroyed, s.timeTaken());
}
return 0;
@@ -428,9 +424,9 @@
}
if (mSocketsDestroyed > 0) {
- ALOGI("Destroyed %d sockets for %s skip={%s} in %" PRId64 "us", mSocketsDestroyed,
- uidRanges.toString().c_str(), android::base::Join(skipUids, " ").c_str(),
- s.timeTakenUs());
+ ALOGI("Destroyed %d sockets for %s skip={%s} in %.1f ms",
+ mSocketsDestroyed, uidRanges.toString().c_str(),
+ android::base::Join(skipUids, " ").c_str(), s.timeTaken());
}
return 0;
@@ -499,8 +495,8 @@
};
struct nlattr nla = {
- .nla_len = sizeof(struct nlattr) + bytecodelen,
- .nla_type = INET_DIAG_REQ_BYTECODE,
+ .nla_type = INET_DIAG_REQ_BYTECODE,
+ .nla_len = sizeof(struct nlattr) + bytecodelen,
};
iovec iov[] = {
@@ -521,8 +517,8 @@
}
if (mSocketsDestroyed > 0) {
- ALOGI("Destroyed %d sockets for netId %d permission=%d in %" PRId64 "us", mSocketsDestroyed,
- netId, permission, s.timeTakenUs());
+ ALOGI("Destroyed %d sockets for netId %d permission=%d in %.1f ms",
+ mSocketsDestroyed, netId, permission, s.timeTaken());
}
return 0;
diff --git a/server/SockDiagTest.cpp b/server/SockDiagTest.cpp
index b79471a..312f2c8 100644
--- a/server/SockDiagTest.cpp
+++ b/server/SockDiagTest.cpp
@@ -199,15 +199,14 @@
inet_diag_msg makeDiagMessage(__u8 family, const sockaddr *src, const sockaddr *dst) {
inet_diag_msg msg = {
- .idiag_family = family,
- .idiag_state = TCP_ESTABLISHED,
- .id =
- {
- .idiag_sport = 1234,
- .idiag_dport = 4321,
- },
- .idiag_uid = AID_APP + 123,
- .idiag_inode = 123456789,
+ .idiag_family = family,
+ .idiag_state = TCP_ESTABLISHED,
+ .idiag_uid = AID_APP + 123,
+ .idiag_inode = 123456789,
+ .id = {
+ .idiag_sport = 1234,
+ .idiag_dport = 4321,
+ }
};
EXPECT_TRUE(fillDiagAddr(msg.id.idiag_src, src));
EXPECT_TRUE(fillDiagAddr(msg.id.idiag_dst, dst));
diff --git a/server/TetherController.cpp b/server/TetherController.cpp
index c987d63..0c5b6bf 100644
--- a/server/TetherController.cpp
+++ b/server/TetherController.cpp
@@ -36,7 +36,6 @@
#include <vector>
#define LOG_TAG "TetherController"
-#include <android-base/scopeguard.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
@@ -117,6 +116,22 @@
return !strcmp(BP_TOOLS_MODE, bootmode);
}
+int setPosixSpawnFileActionsAddDup2(posix_spawn_file_actions_t* fa, int fd, int new_fd) {
+ int res = posix_spawn_file_actions_init(fa);
+ if (res) {
+ return res;
+ }
+ return posix_spawn_file_actions_adddup2(fa, fd, new_fd);
+}
+
+int setPosixSpawnAttrFlags(posix_spawnattr_t* attr, short flags) {
+ int res = posix_spawnattr_init(attr);
+ if (res) {
+ return res;
+ }
+ return posix_spawnattr_setflags(attr, flags);
+}
+
} // namespace
auto TetherController::iptablesRestoreFunction = execIptablesRestoreWithOutput;
@@ -190,15 +205,8 @@
return mForwardingRequests;
}
-int TetherController::startTethering(bool usingLegacyDnsProxy, int num_addrs, char** dhcp_ranges) {
- if (!usingLegacyDnsProxy && num_addrs == 0) {
- // Both DHCP and DnsProxy are disabled, we don't need to start dnsmasq
- configureForTethering(true);
- mIsTetheringStarted = true;
- return 0;
- }
-
- if (mIsTetheringStarted) {
+int TetherController::startTethering(int num_addrs, char **dhcp_ranges) {
+ if (mDaemonPid != 0) {
ALOGE("Tethering already started");
errno = EBUSY;
return -errno;
@@ -237,11 +245,8 @@
kDnsmasqUsername,
};
- if (!usingLegacyDnsProxy) {
- argVector.push_back("--port=0");
- }
-
- // DHCP server will be disabled if num_addrs == 0 and no --dhcp-range is passed.
+ // DHCP server will be disabled if num_addrs == 0 and no --dhcp-range is
+ // passed.
for (int addrIndex = 0; addrIndex < num_addrs; addrIndex += 2) {
argVector.push_back(StringPrintf("--dhcp-range=%s,%s,1h", dhcp_ranges[addrIndex],
dhcp_ranges[addrIndex + 1]));
@@ -262,33 +267,23 @@
// dup2 creates fd without CLOEXEC, dnsmasq will receive commands through the
// duplicated fd.
posix_spawn_file_actions_t fa;
- int res = posix_spawn_file_actions_init(&fa);
+ int res = setPosixSpawnFileActionsAddDup2(&fa, pipeRead.get(), STDIN_FILENO);
if (res) {
- ALOGE("posix_spawn_file_actions_init failed (%s)", strerror(res));
- return -res;
- }
- const android::base::ScopeGuard faGuard = [&] { posix_spawn_file_actions_destroy(&fa); };
- res = posix_spawn_file_actions_adddup2(&fa, pipeRead.get(), STDIN_FILENO);
- if (res) {
- ALOGE("posix_spawn_file_actions_adddup2 failed (%s)", strerror(res));
+ ALOGE("posix_spawn set fa failed (%s)", strerror(res));
return -res;
}
posix_spawnattr_t attr;
- res = posix_spawnattr_init(&attr);
+ res = setPosixSpawnAttrFlags(&attr, POSIX_SPAWN_USEVFORK);
if (res) {
- ALOGE("posix_spawnattr_init failed (%s)", strerror(res));
- return -res;
- }
- const android::base::ScopeGuard attrGuard = [&] { posix_spawnattr_destroy(&attr); };
- res = posix_spawnattr_setflags(&attr, POSIX_SPAWN_USEVFORK);
- if (res) {
- ALOGE("posix_spawnattr_setflags failed (%s)", strerror(res));
+ ALOGE("posix_spawn set attr flag failed (%s)", strerror(res));
return -res;
}
pid_t pid;
res = posix_spawn(&pid, args[0], &fa, &attr, &args[0], nullptr);
+ posix_spawnattr_destroy(&attr);
+ posix_spawn_file_actions_destroy(&fa);
if (res) {
ALOGE("posix_spawn failed (%s)", strerror(res));
return -res;
@@ -296,7 +291,6 @@
mDaemonPid = pid;
mDaemonFd = pipeWrite.release();
configureForTethering(true);
- mIsTetheringStarted = true;
applyDnsInterfaces();
ALOGD("Tethering services running");
@@ -312,8 +306,7 @@
return addrsCstrVec;
}
-int TetherController::startTethering(bool usingLegacyDnsProxy,
- const std::vector<std::string>& dhcpRanges) {
+int TetherController::startTethering(const std::vector<std::string>& dhcpRanges) {
struct in_addr v4_addr;
for (const auto& dhcpRange : dhcpRanges) {
if (!inet_aton(dhcpRange.c_str(), &v4_addr)) {
@@ -321,20 +314,14 @@
}
}
auto dhcp_ranges = toCstrVec(dhcpRanges);
- return startTethering(usingLegacyDnsProxy, dhcp_ranges.size(), dhcp_ranges.data());
+ return startTethering(dhcp_ranges.size(), dhcp_ranges.data());
}
int TetherController::stopTethering() {
configureForTethering(false);
- if (!mIsTetheringStarted) {
- ALOGE("Tethering already stopped");
- return 0;
- }
-
- mIsTetheringStarted = false;
- // dnsmasq is not started
if (mDaemonPid == 0) {
+ ALOGE("Tethering already stopped");
return 0;
}
@@ -351,7 +338,7 @@
}
bool TetherController::isTetheringStarted() {
- return mIsTetheringStarted;
+ return (mDaemonPid == 0 ? false : true);
}
// dnsmasq can't parse commands larger than this due to the fixed-size buffer
diff --git a/server/TetherController.h b/server/TetherController.h
index 7f8ba06..0a04874 100644
--- a/server/TetherController.h
+++ b/server/TetherController.h
@@ -43,8 +43,6 @@
// some point since the controller was initialized.
std::multimap<std::string, ForwardingDownstream> mFwdIfaces;
- bool mIsTetheringStarted = false;
-
// NetId to use for forwarded DNS queries. This may not be the default
// network, e.g., in the case where we are tethering to a DUN APN.
unsigned mDnsNetId = 0;
@@ -75,9 +73,8 @@
bool disableForwarding(const char* requester);
const std::set<std::string>& getIpfwdRequesterList() const;
- //TODO: Clean up the overload function
- int startTethering(bool isLegacyDnsProxy, int num_addrs, char** dhcp_ranges);
- int startTethering(bool isLegacyDnsProxy, const std::vector<std::string>& dhcpRanges);
+ int startTethering(int num_addrs, char **dhcp_ranges);
+ int startTethering(const std::vector<std::string>& dhcpRanges);
int stopTethering();
bool isTetheringStarted();
diff --git a/server/TrafficController.cpp b/server/TrafficController.cpp
index 86f1f53..8dceb5b 100644
--- a/server/TrafficController.cpp
+++ b/server/TrafficController.cpp
@@ -38,6 +38,7 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
+#include <logwrap/logwrap.h>
#include <netdutils/StatusOr.h>
#include <netdutils/Misc.h>
@@ -77,7 +78,6 @@
// Otherwise, apps would be able to avoid data usage accounting entirely by filling up the
// map with tagged traffic entries.
constexpr int TOTAL_UID_STATS_ENTRIES_LIMIT = STATS_MAP_SIZE * 0.9;
-constexpr mode_t S_NONE = 0;
static_assert(BPF_PERMISSION_INTERNET == INetd::PERMISSION_INTERNET,
"Mismatch between BPF and AIDL permissions: PERMISSION_INTERNET");
@@ -116,7 +116,7 @@
mPrivilegedUser.find(appId) != mPrivilegedUser.end());
}
-const std::string UidPermissionTypeToString(int permission) {
+const std::string UidPermissionTypeToString(uint8_t permission) {
if (permission == INetd::PERMISSION_NONE) {
return "PERMISSION_NONE";
}
@@ -167,16 +167,19 @@
return listener;
}
-Status changeOwnerAndMode(const char* path, gid_t group, const char* debugName, mode_t groupMode) {
+Status changeOwnerAndMode(const char* path, gid_t group, const char* debugName, bool netdOnly) {
int ret = chown(path, AID_ROOT, group);
if (ret != 0) return statusFromErrno(errno, StringPrintf("change %s group failed", debugName));
- // Ensure groupMode only contains group bits.
- groupMode &= S_IRGRP | S_IWGRP;
-
- // chmod doesn't by itself grant permission to all processes in that group to
- // read/write the bpf map. They still need correct sepolicy.
- ret = chmod(path, S_IRUSR | S_IWUSR | groupMode);
+ if (netdOnly) {
+ ret = chmod(path, S_IRWXU);
+ } else {
+ // Allow both netd and system server to obtain map fd from the path.
+ // chmod doesn't grant permission to all processes in that group to
+ // read/write the bpf map. They still need correct sepolicy to
+ // read/write the map.
+ ret = chmod(path, S_IRWXU | S_IRGRP | S_IWGRP);
+ }
if (ret != 0) return statusFromErrno(errno, StringPrintf("change %s mode failed", debugName));
return netdutils::status::ok;
}
@@ -194,56 +197,43 @@
Status TrafficController::initMaps() {
std::lock_guard guard(mMutex);
RETURN_IF_NOT_OK(mCookieTagMap.init(COOKIE_TAG_MAP_PATH));
- RETURN_IF_NOT_OK(
- changeOwnerAndMode(COOKIE_TAG_MAP_PATH, AID_NET_BW_ACCT, "CookieTagMap", S_IRGRP));
+ RETURN_IF_NOT_OK(changeOwnerAndMode(COOKIE_TAG_MAP_PATH, AID_NET_BW_ACCT, "CookieTagMap",
+ false));
RETURN_IF_NOT_OK(mUidCounterSetMap.init(UID_COUNTERSET_MAP_PATH));
RETURN_IF_NOT_OK(changeOwnerAndMode(UID_COUNTERSET_MAP_PATH, AID_NET_BW_ACCT,
- "UidCounterSetMap", S_IRGRP));
+ "UidCounterSetMap", false));
RETURN_IF_NOT_OK(mAppUidStatsMap.init(APP_UID_STATS_MAP_PATH));
- RETURN_IF_NOT_OK(changeOwnerAndMode(APP_UID_STATS_MAP_PATH, AID_NET_BW_STATS, "AppUidStatsMap",
- S_IRGRP));
+ RETURN_IF_NOT_OK(
+ changeOwnerAndMode(APP_UID_STATS_MAP_PATH, AID_NET_BW_STATS, "AppUidStatsMap", false));
RETURN_IF_NOT_OK(mStatsMapA.init(STATS_MAP_A_PATH));
- RETURN_IF_NOT_OK(
- changeOwnerAndMode(STATS_MAP_A_PATH, AID_NET_BW_STATS, "StatsMapA", S_IRGRP | S_IWGRP));
+ RETURN_IF_NOT_OK(changeOwnerAndMode(STATS_MAP_A_PATH, AID_NET_BW_STATS, "StatsMapA", false));
RETURN_IF_NOT_OK(mStatsMapB.init(STATS_MAP_B_PATH));
- RETURN_IF_NOT_OK(
- changeOwnerAndMode(STATS_MAP_B_PATH, AID_NET_BW_STATS, "StatsMapB", S_IRGRP | S_IWGRP));
+ RETURN_IF_NOT_OK(changeOwnerAndMode(STATS_MAP_B_PATH, AID_NET_BW_STATS, "StatsMapB", false));
RETURN_IF_NOT_OK(mIfaceIndexNameMap.init(IFACE_INDEX_NAME_MAP_PATH));
RETURN_IF_NOT_OK(changeOwnerAndMode(IFACE_INDEX_NAME_MAP_PATH, AID_NET_BW_STATS,
- "IfaceIndexNameMap", S_IRGRP));
+ "IfaceIndexNameMap", false));
RETURN_IF_NOT_OK(mIfaceStatsMap.init(IFACE_STATS_MAP_PATH));
- RETURN_IF_NOT_OK(
- changeOwnerAndMode(IFACE_STATS_MAP_PATH, AID_NET_BW_STATS, "IfaceStatsMap", S_IRGRP));
+ RETURN_IF_NOT_OK(changeOwnerAndMode(IFACE_STATS_MAP_PATH, AID_NET_BW_STATS, "IfaceStatsMap",
+ false));
RETURN_IF_NOT_OK(mConfigurationMap.init(CONFIGURATION_MAP_PATH));
RETURN_IF_NOT_OK(changeOwnerAndMode(CONFIGURATION_MAP_PATH, AID_NET_BW_STATS,
- "ConfigurationMap", S_IRGRP));
+ "ConfigurationMap", false));
RETURN_IF_NOT_OK(
mConfigurationMap.writeValue(UID_RULES_CONFIGURATION_KEY, DEFAULT_CONFIG, BPF_ANY));
RETURN_IF_NOT_OK(mConfigurationMap.writeValue(CURRENT_STATS_MAP_CONFIGURATION_KEY, SELECT_MAP_A,
BPF_ANY));
RETURN_IF_NOT_OK(mUidOwnerMap.init(UID_OWNER_MAP_PATH));
- RETURN_IF_NOT_OK(changeOwnerAndMode(UID_OWNER_MAP_PATH, AID_ROOT, "UidOwnerMap", S_NONE));
+ RETURN_IF_NOT_OK(changeOwnerAndMode(UID_OWNER_MAP_PATH, AID_ROOT, "UidOwnerMap", true));
RETURN_IF_NOT_OK(mUidOwnerMap.clear());
RETURN_IF_NOT_OK(mUidPermissionMap.init(UID_PERMISSION_MAP_PATH));
-
- // The programs must be readable to process that modify iptables rules
- RETURN_IF_NOT_OK(changeOwnerAndMode(XT_BPF_EGRESS_PROG_PATH, AID_NET_ADMIN,
- "XtFilterEgressProgram", S_IRGRP));
- RETURN_IF_NOT_OK(changeOwnerAndMode(XT_BPF_INGRESS_PROG_PATH, AID_NET_ADMIN,
- "XtFilterIngressProgram", S_IRGRP));
- RETURN_IF_NOT_OK(changeOwnerAndMode(XT_BPF_WHITELIST_PROG_PATH, AID_NET_ADMIN,
- "XtWhitelistProgram", S_IRGRP));
- RETURN_IF_NOT_OK(changeOwnerAndMode(XT_BPF_BLACKLIST_PROG_PATH, AID_NET_ADMIN,
- "XtBlacklistProgram", S_IRGRP));
-
return netdutils::status::ok;
}
@@ -588,10 +578,8 @@
UidOwnerMatchType match) {
auto oldMatch = map.readValue(uid);
if (isOk(oldMatch)) {
- UidOwnerValue newMatch = {
- .iif = (match == IIF_MATCH) ? 0 : oldMatch.value().iif,
- .rule = static_cast<uint8_t>(oldMatch.value().rule & ~match),
- };
+ UidOwnerValue newMatch = {.rule = static_cast<uint8_t>(oldMatch.value().rule & ~match),
+ .iif = (match == IIF_MATCH) ? 0 : oldMatch.value().iif};
if (newMatch.rule == 0) {
RETURN_IF_NOT_OK(map.deleteValue(uid));
} else {
@@ -613,16 +601,11 @@
}
auto oldMatch = map.readValue(uid);
if (isOk(oldMatch)) {
- UidOwnerValue newMatch = {
- .iif = iif ? iif : oldMatch.value().iif,
- .rule = static_cast<uint8_t>(oldMatch.value().rule | match),
- };
+ UidOwnerValue newMatch = {.rule = static_cast<uint8_t>(oldMatch.value().rule | match),
+ .iif = iif ? iif : oldMatch.value().iif};
RETURN_IF_NOT_OK(map.writeValue(uid, newMatch, BPF_ANY));
} else {
- UidOwnerValue newMatch = {
- .iif = iif,
- .rule = static_cast<uint8_t>(match),
- };
+ UidOwnerValue newMatch = {.rule = static_cast<uint8_t>(match), .iif = iif};
RETURN_IF_NOT_OK(map.writeValue(uid, newMatch, BPF_ANY));
}
return netdutils::status::ok;
@@ -864,7 +847,7 @@
mPrivilegedUser.erase(uid);
if (mBpfLevel > BpfLevel::NONE) {
Status ret = mUidPermissionMap.deleteValue(uid);
- if (!isOk(ret) && ret.code() != ENOENT) {
+ if (!isOk(ret) && ret.code() != ENONET) {
ALOGE("Failed to clean up the permission for %u: %s", uid,
strerror(ret.code()));
}
@@ -1092,28 +1075,15 @@
uint32_t key = UID_RULES_CONFIGURATION_KEY;
auto configuration = mConfigurationMap.readValue(key);
if (isOk(configuration)) {
- dw.println("current ownerMatch configuration: %d%s", configuration.value(),
- uidMatchTypeToString(configuration.value()).c_str());
+ dw.println("current ownerMatch configuration: %d", configuration.value());
} else {
dw.println("mConfigurationMap read ownerMatch configure failed with error: %s",
configuration.status().msg().c_str());
}
-
key = CURRENT_STATS_MAP_CONFIGURATION_KEY;
configuration = mConfigurationMap.readValue(key);
if (isOk(configuration)) {
- const char* statsMapDescription = "???";
- switch (configuration.value()) {
- case SELECT_MAP_A:
- statsMapDescription = "SELECT_MAP_A";
- break;
- case SELECT_MAP_B:
- statsMapDescription = "SELECT_MAP_B";
- break;
- // No default clause, so if we ever add a third map, this code will fail to build.
- }
- dw.println("current statsMap configuration: %d %s", configuration.value(),
- statsMapDescription);
+ dw.println("current statsMap configuration: %d", configuration.value());
} else {
dw.println("mConfigurationMap read stats map configure failed with error: %s",
configuration.status().msg().c_str());
@@ -1139,7 +1109,7 @@
dw.println("mUidOwnerMap print end with error: %s", res.msg().c_str());
}
dumpBpfMap("mUidPermissionMap", dw, "");
- const auto printUidPermissionInfo = [&dw](const uint32_t& key, const int& value,
+ const auto printUidPermissionInfo = [&dw](const uint32_t& key, const uint8_t& value,
const BpfMap<uint32_t, uint8_t>&) {
dw.println("%u %s", key, UidPermissionTypeToString(value).c_str());
return netdutils::status::ok;
diff --git a/server/VirtualNetwork.h b/server/VirtualNetwork.h
index 20c8dee..69b14d8 100644
--- a/server/VirtualNetwork.h
+++ b/server/VirtualNetwork.h
@@ -14,14 +14,16 @@
* limitations under the License.
*/
-#pragma once
+#ifndef NETD_SERVER_VIRTUAL_NETWORK_H
+#define NETD_SERVER_VIRTUAL_NETWORK_H
#include <set>
#include "Network.h"
#include "UidRanges.h"
-namespace android::net {
+namespace android {
+namespace net {
// A VirtualNetwork may be "secure" or not.
//
@@ -38,14 +40,15 @@
bool isSecure() const;
bool appliesToUser(uid_t uid) const;
- [[nodiscard]] int addUsers(const UidRanges& uidRanges, const std::set<uid_t>& protectableUsers);
- [[nodiscard]] int removeUsers(const UidRanges& uidRanges,
- const std::set<uid_t>& protectableUsers);
+ int addUsers(const UidRanges& uidRanges,
+ const std::set<uid_t>& protectableUsers) WARN_UNUSED_RESULT;
+ int removeUsers(const UidRanges& uidRanges,
+ const std::set<uid_t>& protectableUsers) WARN_UNUSED_RESULT;
- private:
+private:
Type getType() const override;
- [[nodiscard]] int addInterface(const std::string& interface) override;
- [[nodiscard]] int removeInterface(const std::string& interface) override;
+ int addInterface(const std::string& interface) override WARN_UNUSED_RESULT;
+ int removeInterface(const std::string& interface) override WARN_UNUSED_RESULT;
int maybeCloseSockets(bool add, const UidRanges& uidRanges,
const std::set<uid_t>& protectableUsers);
@@ -53,4 +56,7 @@
UidRanges mUidRanges;
};
-} // namespace android::net
+} // namespace net
+} // namespace android
+
+#endif // NETD_SERVER_VIRTUAL_NETWORK_H
diff --git a/server/XfrmController.cpp b/server/XfrmController.cpp
index 199638c..a57b492 100644
--- a/server/XfrmController.cpp
+++ b/server/XfrmController.cpp
@@ -53,6 +53,7 @@
#include <cutils/properties.h>
#include <log/log.h>
#include <log/log_properties.h>
+#include <logwrap/logwrap.h>
#include "Fwmark.h"
#include "InterfaceController.h"
#include "NetdConstants.h"
diff --git a/server/XfrmControllerTest.cpp b/server/XfrmControllerTest.cpp
index e7f5cfc..36af67a 100644
--- a/server/XfrmControllerTest.cpp
+++ b/server/XfrmControllerTest.cpp
@@ -19,6 +19,7 @@
#include <cerrno>
#include <cinttypes>
#include <cstdint>
+#include <cstdio>
#include <cstdlib>
#include <set>
#include <vector>
diff --git a/server/binder/android/net/INetd.aidl b/server/binder/android/net/INetd.aidl
index fdea52e..ec9bbc3 100644
--- a/server/binder/android/net/INetd.aidl
+++ b/server/binder/android/net/INetd.aidl
@@ -1194,18 +1194,4 @@
* @return a IBinder object, it could be casted to oem specific interface.
*/
IBinder getOemNetd();
-
- /**
- * Start tethering with given configuration
- *
- * @param usingLegacyDnsProxy, whether to enable or disable legacy DNS proxy server.
- * @param dhcpRanges dhcp ranges to set.
- * dhcpRanges might contain many addresss {addr1, addr2, aadr3, addr4...}
- * Netd splits them into ranges: addr1-addr2, addr3-addr4, etc.
- * An odd number of addrs will fail.
- * @throws ServiceSpecificException in case of failure, with an error code indicating the
- * cause of the failure.
- */
- void tetherStartWithConfiguration(
- boolean usingLegacyDnsProxy, in @utf8InCpp String[] dhcpRanges);
}
diff --git a/server/main.cpp b/server/main.cpp
index d8ed96f..b783ce5 100644
--- a/server/main.cpp
+++ b/server/main.cpp
@@ -14,19 +14,19 @@
* limitations under the License.
*/
-#include <dirent.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <signal.h>
+#include <chrono>
#include <stdio.h>
#include <stdlib.h>
+#include <signal.h>
+#include <errno.h>
#include <string.h>
+#include <mutex>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
-#include <chrono>
-#include <cinttypes>
-#include <mutex>
+
+#include <fcntl.h>
+#include <dirent.h>
#define LOG_TAG "Netd"
@@ -82,21 +82,11 @@
gLog.info(std::string(msg));
}
-int tagSocketCallback(int sockFd, uint32_t tag, uid_t uid, pid_t) {
- return gCtls->trafficCtrl.tagSocket(sockFd, tag, uid, geteuid());
-}
-
-bool evaluateDomainNameCallback(const android_net_context&, const char* /*name*/) {
- return true;
-}
-
bool initDnsResolver() {
ResolverNetdCallbacks callbacks = {
- .check_calling_permission = &checkCallingPermissionCallback,
.get_network_context = &getNetworkContextCallback,
.log = &logCallback,
- .tagSocket = &tagSocketCallback,
- .evaluate_domain_name = &evaluateDomainNameCallback,
+ .check_calling_permission = &checkCallingPermissionCallback,
};
return RESOLV_STUB.resolv_init(callbacks);
}
@@ -185,7 +175,7 @@
ALOGE("Unable to start NetdNativeService: %d", ret);
exit(1);
}
- gLog.info("Registering NetdNativeService: %" PRId64 "us", subTime.getTimeAndResetUs());
+ gLog.info("Registering NetdNativeService: %.1fms", subTime.getTimeAndReset());
android::net::process::ScopedPidFile pidFile(PID_FILE_PATH);
@@ -196,8 +186,9 @@
ALOGE("Unable to start NetdHwService: %d", ret);
exit(1);
}
- gLog.info("Registering NetdHwService: %" PRId64 "us", subTime.getTimeAndResetUs());
- gLog.info("Netd started in %" PRId64 "us", s.timeTakenUs());
+ gLog.info("Registering NetdHwService: %.1fms", subTime.getTimeAndReset());
+
+ gLog.info("Netd started in %dms", static_cast<int>(s.timeTaken()));
IPCThreadState::self()->joinThreadPool();
diff --git a/server/oem_iptables_hook.cpp b/server/oem_iptables_hook.cpp
index 39a6285..c7f8dbf 100644
--- a/server/oem_iptables_hook.cpp
+++ b/server/oem_iptables_hook.cpp
@@ -26,6 +26,7 @@
#define LOG_TAG "OemIptablesHook"
#include <log/log.h>
+#include <logwrap/logwrap.h>
#include "NetdConstants.h"
namespace {
diff --git a/tests/Android.bp b/tests/Android.bp
index 1ac413f..4237b25 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -47,23 +47,11 @@
],
}
-cc_test_library {
- name: "libnetd_test_utils",
- srcs: [
- "test_utils.cpp"
- ],
- export_include_dirs: ["."],
- shared_libs: [
- "libbase",
- ],
-}
-
cc_test {
name: "netd_integration_test",
test_suites: ["device-tests"],
require_root: true,
defaults: ["netd_defaults"],
- tidy: false, // cuts test build time by almost 1 minute
srcs: [
":netd_integration_test_shared",
"binder_test.cpp",
@@ -88,12 +76,12 @@
"libcap",
"libnetd_test_tun_interface",
"libnetd_test_unsol_service",
- "libnetd_test_utils",
"libbpf_android",
+ "liblogwrap",
"libnetdbpf",
"libnetdutils",
"libqtaguid",
- "netd_aidl_interface-unstable-cpp",
+ "netd_aidl_interface-cpp",
"netd_event_listener_interface-cpp",
"oemnetd_aidl_interface-cpp",
],
diff --git a/tests/AndroidTest.xml b/tests/AndroidTest.xml
new file mode 100644
index 0000000..7b18f9f
--- /dev/null
+++ b/tests/AndroidTest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+<!--
+ To run test manually:
+ make -j netd_integration_test && \
+ tradefed.sh run template/local_min -\-template:map test=netd_integration_test
+
+ For a list of options, see:
+ tools/tradefederation/core/src/com/android/tradefed/testtype/GTest.java
+-->
+<configuration description="Config for netd_integration_test">
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="netd_integration_test->/data/local/tmp/netd_integration_test" />
+ </target_preparer>
+ <option name="test-suite-tag" value="apct" />
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="netd_integration_test" />
+ </test>
+</configuration>
diff --git a/tests/benchmarks/Android.bp b/tests/benchmarks/Android.bp
index 59cac96..c14253d 100644
--- a/tests/benchmarks/Android.bp
+++ b/tests/benchmarks/Android.bp
@@ -9,10 +9,10 @@
"liblog",
"libnetd_client",
"libnetdutils",
- "libutils",
],
static_libs: [
"libnetd_test_dnsresponder",
+ "libutils",
"dnsresolver_aidl_interface-cpp",
"netd_aidl_interface-cpp",
"netd_event_listener_interface-cpp",
@@ -23,11 +23,10 @@
include_dirs: [
"system/netd/include",
"system/netd/client",
+ "system/netd/resolv/include",
"system/netd/server",
"system/netd/server/binder",
- ],
- header_libs: [
- "libnetd_resolv_headers",
+ "system/netd/resolv/dns_responder",
],
srcs: [
"main.cpp",
@@ -44,6 +43,9 @@
"libbpf_android",
"libnetdutils",
],
+ static_libs: [
+ "libutils",
+ ],
srcs: [
"bpf_benchmark.cpp",
],
diff --git a/tests/benchmarks/AndroidTest.xml b/tests/benchmarks/AndroidTest.xml
new file mode 100644
index 0000000..7756cc2
--- /dev/null
+++ b/tests/benchmarks/AndroidTest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+<configuration description="Config for netd_benchmark">
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="netd_benchmark->/data/local/tmp/netd_benchmark" />
+ </target_preparer>
+ <option name="test-suite-tag" value="apct" />
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="netd_benchmark" />
+ </test>
+</configuration>
diff --git a/tests/benchmarks/connect_benchmark.cpp b/tests/benchmarks/connect_benchmark.cpp
index ed90538..e08fb01 100644
--- a/tests/benchmarks/connect_benchmark.cpp
+++ b/tests/benchmarks/connect_benchmark.cpp
@@ -125,8 +125,8 @@
}
if (waitBetweenRuns) {
- latencies[iterations] = stopwatch.timeTakenUs();
- state.SetIterationTime(static_cast<double>(latencies[iterations]) / 1.0e6L);
+ latencies[iterations] = stopwatch.timeTaken() * 1e6L;
+ state.SetIterationTime(latencies[iterations] / 1e9L);
std::this_thread::sleep_for(std::chrono::milliseconds(10));
++iterations;
}
@@ -182,8 +182,8 @@
}
if (waitBetweenRuns) {
- latencies[iterations] = stopwatch.timeTakenUs();
- state.SetIterationTime(static_cast<double>(latencies[iterations]) / 1.0e6L);
+ latencies[iterations] = stopwatch.timeTaken() * 1e6L;
+ state.SetIterationTime(latencies[iterations] / 1e9L);
std::this_thread::sleep_for(std::chrono::milliseconds(10));
++iterations;
}
diff --git a/tests/binder_test.cpp b/tests/binder_test.cpp
index 7be8e55..917bf5d 100644
--- a/tests/binder_test.cpp
+++ b/tests/binder_test.cpp
@@ -21,8 +21,8 @@
#include <cinttypes>
#include <condition_variable>
#include <cstdint>
+#include <cstdio>
#include <cstdlib>
-#include <iostream>
#include <mutex>
#include <set>
#include <vector>
@@ -50,6 +50,7 @@
#include <com/android/internal/net/IOemNetd.h>
#include <cutils/multiuser.h>
#include <gtest/gtest.h>
+#include <logwrap/logwrap.h>
#include <netdbpf/bpf_shared.h>
#include <netutils/ifc.h>
#include "Fwmark.h"
@@ -60,13 +61,12 @@
#include "XfrmController.h"
#include "android/net/INetd.h"
#include "binder/IServiceManager.h"
-#include "netdutils/InternetAddresses.h"
#include "netdutils/Stopwatch.h"
#include "netdutils/Syscalls.h"
#include "netid_client.h" // NETID_UNSET
-#include "test_utils.h"
#include "tun_interface.h"
+#define IP_PATH "/system/bin/ip"
#define IP6TABLES_PATH "/system/bin/ip6tables"
#define IPTABLES_PATH "/system/bin/iptables"
#define TUN_DEV "/dev/tun"
@@ -87,14 +87,12 @@
using android::base::StartsWith;
using android::base::StringPrintf;
using android::base::Trim;
-using android::base::unique_fd;
using android::net::INetd;
using android::net::InterfaceConfigurationParcel;
using android::net::InterfaceController;
using android::net::TetherStatsParcel;
using android::net::TunInterface;
using android::net::UidRangeParcel;
-using android::netdutils::ScopedAddrinfo;
using android::netdutils::sSyscalls;
using android::netdutils::Stopwatch;
@@ -151,8 +149,7 @@
sTun2.destroy();
}
- static void fakeRemoteSocketPair(unique_fd* clientSocket, unique_fd* serverSocket,
- unique_fd* acceptedSocket);
+ static void fakeRemoteSocketPair(int *clientSocket, int *serverSocket, int *acceptedSocket);
void createVpnNetworkWithUid(bool secure, uid_t uid, int vpnNetId = TEST_NETID2,
int fallthroughNetId = TEST_NETID1);
@@ -173,7 +170,7 @@
public:
explicit TimedOperation(const std::string &name): mName(name) {}
virtual ~TimedOperation() {
- std::cerr << " " << mName << ": " << timeTakenUs() << "us" << std::endl;
+ fprintf(stderr, " %s: %6.1f ms\n", mName.c_str(), timeTaken());
}
private:
@@ -187,6 +184,57 @@
ASSERT_TRUE(isAlive);
}
+static int randomUid() {
+ return 100000 * arc4random_uniform(7) + 10000 + arc4random_uniform(5000);
+}
+
+static std::vector<std::string> runCommand(const std::string& command) {
+ std::vector<std::string> lines;
+ FILE *f = popen(command.c_str(), "r"); // NOLINT(cert-env33-c)
+ if (f == nullptr) {
+ perror("popen");
+ return lines;
+ }
+
+ char *line = nullptr;
+ size_t bufsize = 0;
+ ssize_t linelen = 0;
+ while ((linelen = getline(&line, &bufsize, f)) >= 0) {
+ lines.push_back(std::string(line, linelen));
+ free(line);
+ line = nullptr;
+ }
+
+ pclose(f);
+ return lines;
+}
+
+static std::vector<std::string> listIpRules(const char *ipVersion) {
+ std::string command = StringPrintf("%s %s rule list", IP_PATH, ipVersion);
+ return runCommand(command);
+}
+
+static std::vector<std::string> listIptablesRule(const char *binary, const char *chainName) {
+ std::string command = StringPrintf("%s -w -n -L %s", binary, chainName);
+ return runCommand(command);
+}
+
+static int iptablesRuleLineLength(const char *binary, const char *chainName) {
+ return listIptablesRule(binary, chainName).size();
+}
+
+static bool iptablesRuleExists(const char *binary,
+ const char *chainName,
+ const std::string& expectedRule) {
+ std::vector<std::string> rules = listIptablesRule(binary, chainName);
+ for (const auto& rule : rules) {
+ if(rule.find(expectedRule) != std::string::npos) {
+ return true;
+ }
+ }
+ return false;
+}
+
static bool iptablesNoSocketAllowRuleExists(const char *chainName){
return iptablesRuleExists(IPTABLES_PATH, chainName, NO_SOCKET_ALLOW_RULE) &&
iptablesRuleExists(IP6TABLES_PATH, chainName, NO_SOCKET_ALLOW_RULE);
@@ -293,7 +341,7 @@
}
TEST_F(BinderTest, IpSecSetEncapSocketOwner) {
- unique_fd uniqueFd(socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0));
+ android::base::unique_fd uniqueFd(socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0));
android::os::ParcelFileDescriptor sockFd(std::move(uniqueFd));
int sockOptVal = UDP_ENCAP_ESPINUDP;
@@ -580,18 +628,22 @@
std::vector<UidRangeParcel> uidRanges = {makeUidRangeParcel(BASE_UID + 150, BASE_UID + 224),
makeUidRangeParcel(BASE_UID + 226, BASE_UID + 300)};
- // Make sure no rules existed before calling commands.
- for (auto const& range : uidRanges) {
- EXPECT_FALSE(ipRuleExistsForRange(RULE_PRIORITY, range, "prohibit"));
- }
+
+ const std::vector<std::string> initialRulesV4 = listIpRules(IP_RULE_V4);
+ const std::vector<std::string> initialRulesV6 = listIpRules(IP_RULE_V6);
+
// Create two valid rules.
ASSERT_TRUE(mNetd->networkRejectNonSecureVpn(true, uidRanges).isOk());
+ EXPECT_EQ(initialRulesV4.size() + 2, listIpRules(IP_RULE_V4).size());
+ EXPECT_EQ(initialRulesV6.size() + 2, listIpRules(IP_RULE_V6).size());
for (auto const& range : uidRanges) {
EXPECT_TRUE(ipRuleExistsForRange(RULE_PRIORITY, range, "prohibit"));
}
// Remove the rules.
ASSERT_TRUE(mNetd->networkRejectNonSecureVpn(false, uidRanges).isOk());
+ EXPECT_EQ(initialRulesV4.size(), listIpRules(IP_RULE_V4).size());
+ EXPECT_EQ(initialRulesV6.size(), listIpRules(IP_RULE_V6).size());
for (auto const& range : uidRanges) {
EXPECT_FALSE(ipRuleExistsForRange(RULE_PRIORITY, range, "prohibit"));
}
@@ -600,12 +652,15 @@
binder::Status status = mNetd->networkRejectNonSecureVpn(false, uidRanges);
ASSERT_EQ(binder::Status::EX_SERVICE_SPECIFIC, status.exceptionCode());
EXPECT_EQ(ENOENT, status.serviceSpecificErrorCode());
+
+ // All rules should be the same as before.
+ EXPECT_EQ(initialRulesV4, listIpRules(IP_RULE_V4));
+ EXPECT_EQ(initialRulesV6, listIpRules(IP_RULE_V6));
}
// Create a socket pair that isLoopbackSocket won't think is local.
-void BinderTest::fakeRemoteSocketPair(unique_fd* clientSocket, unique_fd* serverSocket,
- unique_fd* acceptedSocket) {
- serverSocket->reset(socket(AF_INET6, SOCK_STREAM | SOCK_CLOEXEC, 0));
+void BinderTest::fakeRemoteSocketPair(int *clientSocket, int *serverSocket, int *acceptedSocket) {
+ *serverSocket = socket(AF_INET6, SOCK_STREAM | SOCK_CLOEXEC, 0);
struct sockaddr_in6 server6 = { .sin6_family = AF_INET6, .sin6_addr = sTun.dstAddr() };
ASSERT_EQ(0, bind(*serverSocket, (struct sockaddr *) &server6, sizeof(server6)));
@@ -613,14 +668,13 @@
ASSERT_EQ(0, getsockname(*serverSocket, (struct sockaddr *) &server6, &addrlen));
ASSERT_EQ(0, listen(*serverSocket, 10));
- clientSocket->reset(socket(AF_INET6, SOCK_STREAM | SOCK_CLOEXEC, 0));
+ *clientSocket = socket(AF_INET6, SOCK_STREAM | SOCK_CLOEXEC, 0);
struct sockaddr_in6 client6 = { .sin6_family = AF_INET6, .sin6_addr = sTun.srcAddr() };
ASSERT_EQ(0, bind(*clientSocket, (struct sockaddr *) &client6, sizeof(client6)));
ASSERT_EQ(0, connect(*clientSocket, (struct sockaddr *) &server6, sizeof(server6)));
ASSERT_EQ(0, getsockname(*clientSocket, (struct sockaddr *) &client6, &addrlen));
- acceptedSocket->reset(
- accept4(*serverSocket, (struct sockaddr*)&server6, &addrlen, SOCK_CLOEXEC));
+ *acceptedSocket = accept4(*serverSocket, (struct sockaddr *) &server6, &addrlen, SOCK_CLOEXEC);
ASSERT_NE(-1, *acceptedSocket);
ASSERT_EQ(0, memcmp(&client6, &server6, sizeof(client6)));
@@ -648,7 +702,7 @@
}
TEST_F(BinderTest, SocketDestroy) {
- unique_fd clientSocket, serverSocket, acceptedSocket;
+ int clientSocket, serverSocket, acceptedSocket;
ASSERT_NO_FATAL_FAILURE(fakeRemoteSocketPair(&clientSocket, &serverSocket, &acceptedSocket));
// Pick a random UID in the system UID range.
@@ -688,6 +742,10 @@
skipUids.resize(skipUids.size() - 1);
EXPECT_TRUE(mNetd->socketDestroy(uidRanges, skipUids).isOk());
checkSocketpairClosed(clientSocket, acceptedSocket);
+
+ close(clientSocket);
+ close(serverSocket);
+ close(acceptedSocket);
}
namespace {
@@ -989,11 +1047,8 @@
std::string intIface1 = StringPrintf("netdtest_%u", arc4random_uniform(10000));
std::string intIface2 = StringPrintf("netdtest_%u", arc4random_uniform(10000));
std::string intIface3 = StringPrintf("netdtest_%u", arc4random_uniform(10000));
-
- // Ensure we won't use the same interface name, otherwise the test will fail.
- u_int32_t rNumber = arc4random_uniform(10000);
- std::string extIface1 = StringPrintf("netdtest_%u", rNumber);
- std::string extIface2 = StringPrintf("netdtest_%u", rNumber + 1);
+ std::string extIface1 = StringPrintf("netdtest_%u", arc4random_uniform(10000));
+ std::string extIface2 = StringPrintf("netdtest_%u", arc4random_uniform(10000));
addTetherCounterValues(IPTABLES_PATH, intIface1, extIface1, 123, 111);
addTetherCounterValues(IP6TABLES_PATH, intIface1, extIface1, 456, 10);
@@ -1566,13 +1621,13 @@
}
TEST_F(BinderTest, BandwidthSetGlobalAlert) {
- int64_t testAlertBytes = 2097200;
+ long testAlertBytes = 2097149;
binder::Status status = mNetd->bandwidthSetGlobalAlert(testAlertBytes);
EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
expectBandwidthGlobalAlertRuleExists(testAlertBytes);
- testAlertBytes = 2098230;
+ testAlertBytes = 2097152;
status = mNetd->bandwidthSetGlobalAlert(testAlertBytes);
EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
expectBandwidthGlobalAlertRuleExists(testAlertBytes);
@@ -1608,6 +1663,21 @@
namespace {
+std::vector<std::string> listIpRoutes(const char* ipVersion, const char* table) {
+ std::string command = StringPrintf("%s %s route ls table %s", IP_PATH, ipVersion, table);
+ return runCommand(command);
+}
+
+bool ipRouteExists(const char* ipVersion, const char* table, const std::string& ipRoute) {
+ std::vector<std::string> routes = listIpRoutes(ipVersion, table);
+ for (const auto& route : routes) {
+ if (route.find(ipRoute) != std::string::npos) {
+ return true;
+ }
+ }
+ return false;
+}
+
std::string ipRouteString(const std::string& ifName, const std::string& dst,
const std::string& nextHop) {
std::string dstString = (dst == "0.0.0.0/0" || dst == "::/0") ? "default" : dst;
@@ -1902,20 +1972,16 @@
}
TEST_F(BinderTest, NetworkSetProtectAllowDeny) {
- binder::Status status = mNetd->networkSetProtectAllow(TEST_UID1);
+ const int testUid = randomUid();
+ binder::Status status = mNetd->networkSetProtectAllow(testUid);
EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
bool ret = false;
- status = mNetd->networkCanProtect(TEST_UID1, &ret);
+ status = mNetd->networkCanProtect(testUid, &ret);
EXPECT_TRUE(ret);
- status = mNetd->networkSetProtectDeny(TEST_UID1);
+ status = mNetd->networkSetProtectDeny(testUid);
EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
-
- // Clear uid permission before calling networkCanProtect to ensure
- // the call won't be affected by uid permission.
- EXPECT_TRUE(mNetd->networkClearPermissionForUser({TEST_UID1}).isOk());
-
- status = mNetd->networkCanProtect(TEST_UID1, &ret);
+ status = mNetd->networkCanProtect(testUid, &ret);
EXPECT_FALSE(ret);
}
@@ -1992,30 +2058,22 @@
std::vector<std::string> noDhcpRange = {};
static const char dnsdName[] = "dnsmasq";
- for (bool usingLegacyDnsProxy : {true, false}) {
- binder::Status status =
- mNetd->tetherStartWithConfiguration(usingLegacyDnsProxy, noDhcpRange);
- EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
- SCOPED_TRACE(StringPrintf("usingLegacyDnsProxy: %d", usingLegacyDnsProxy));
- if (usingLegacyDnsProxy == true) {
- expectProcessExists(dnsdName);
- } else {
- expectProcessDoesNotExist(dnsdName);
- }
+ binder::Status status = mNetd->tetherStart(noDhcpRange);
+ EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+ expectProcessExists(dnsdName);
- bool tetherEnabled;
- status = mNetd->tetherIsEnabled(&tetherEnabled);
- EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
- EXPECT_TRUE(tetherEnabled);
+ bool tetherEnabled;
+ status = mNetd->tetherIsEnabled(&tetherEnabled);
+ EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+ EXPECT_TRUE(tetherEnabled);
- status = mNetd->tetherStop();
- EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
- expectProcessDoesNotExist(dnsdName);
+ status = mNetd->tetherStop();
+ EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+ expectProcessDoesNotExist(dnsdName);
- status = mNetd->tetherIsEnabled(&tetherEnabled);
- EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
- EXPECT_FALSE(tetherEnabled);
- }
+ status = mNetd->tetherIsEnabled(&tetherEnabled);
+ EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+ EXPECT_FALSE(tetherEnabled);
}
TEST_F(BinderTest, TetherInterfaceAddRemoveList) {
@@ -3108,14 +3166,10 @@
bool sendIPv6PacketFromUid(uid_t uid, const in6_addr& dstAddr, Fwmark* fwmark, int tunFd) {
ScopedUidChange scopedUidChange(uid);
- unique_fd testSocket(socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0));
+ android::base::unique_fd testSocket(socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0));
if (testSocket < 0) return false;
- const sockaddr_in6 dst6 = {
- .sin6_family = AF_INET6,
- .sin6_port = 42,
- .sin6_addr = dstAddr,
- };
+ const sockaddr_in6 dst6 = {.sin6_family = AF_INET6, .sin6_addr = dstAddr, .sin6_port = 42};
int res = connect(testSocket, (sockaddr*)&dst6, sizeof(dst6));
socklen_t fwmarkLen = sizeof(fwmark->intValue);
EXPECT_NE(-1, getsockopt(testSocket, SOL_SOCKET, SO_MARK, &(fwmark->intValue), &fwmarkLen));
@@ -3228,4 +3282,4 @@
// Get current default network NetId
ASSERT_TRUE(mNetd->networkGetDefault(&mStoredDefaultNetwork).isOk());
expectVpnFallthroughWorks(mNetd.get(), true /* bypassable */, TEST_UID1, sTun, sTun2);
-}
+}
\ No newline at end of file
diff --git a/tests/netd_test.cpp b/tests/netd_test.cpp
index 9a6e69c..7e14b01 100644
--- a/tests/netd_test.cpp
+++ b/tests/netd_test.cpp
@@ -7,7 +7,7 @@
*
* http://www.apache.org/licenses/LICENSE-2.0
*
- * Unless required by applicable law or agreed to in writing, software
+ * Unless requied 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
diff --git a/tests/netlink_listener_test.cpp b/tests/netlink_listener_test.cpp
index 8c9b622..b39182c 100644
--- a/tests/netlink_listener_test.cpp
+++ b/tests/netlink_listener_test.cpp
@@ -150,11 +150,8 @@
checkMassiveSocketDestroy(100, false);
}
-// Disabled because flaky on blueline-userdebug; this test relies on the main thread
-// winning a race against the NetlinkListener::run() thread. There's no way to ensure
-// things will be scheduled the same way across all architectures and test environments.
-TEST_F(NetlinkListenerTest, DISABLED_TestSkDestroyError) {
+TEST_F(NetlinkListenerTest, TestSkDestroyError) {
SKIP_IF_BPF_NOT_SUPPORTED;
- checkMassiveSocketDestroy(50000, true);
+ checkMassiveSocketDestroy(20000, true);
}
diff --git a/tests/runtests.sh b/tests/runtests.sh
new file mode 100755
index 0000000..6f828fc
--- /dev/null
+++ b/tests/runtests.sh
@@ -0,0 +1,162 @@
+#!/usr/bin/env bash
+
+set -e
+set -u
+
+readonly DEFAULT_TESTS="
+ netdutils_test
+ netd_unit_test
+ netd_integration_test
+ resolv_integration_test
+ resolv_unit_test
+"
+
+readonly EXTENDED_TESTS="
+ netd_benchmark
+"
+
+readonly TEST_DEVICE_PATH="/data/local/tmp"
+
+REPO_TOP=""
+SOONG_BIN=""
+DEBUG=""
+
+function logToStdErr() {
+ echo "$1" >&2
+}
+
+function testAndSetRepoTop() {
+ if [[ -n "$1" && -d "$1/.repo" ]]; then
+ REPO_TOP="$1"
+ SOONG_BIN="$REPO_TOP/build/soong/soong_ui.bash"
+ return 0
+ fi
+ return 1
+}
+
+function gotoRepoTop() {
+ if testAndSetRepoTop "$ANDROID_BUILD_TOP"; then
+ return
+ fi
+
+ while ! testAndSetRepoTop "$PWD"; do
+ if [[ "$PWD" == "/" ]]; then
+ break
+ fi
+ cd ..
+ done
+}
+
+function runCmd() {
+ local cmd="$@"
+ echo "#"
+ echo "# $cmd"
+ $DEBUG $cmd
+ return $?
+}
+
+function runOneTest() {
+ local test="$1"
+ echo
+ echo "###"
+ echo "# $test"
+ echo "###"
+
+ local prefix="$ANDROID_TARGET_OUT_TESTCASES/$test/$TARGET_ARCH/$test"
+ for bits in '' 32 64; do
+ local testbin="${prefix}${bits}"
+ if [ -f "$testbin" ]; then
+ runCmd adb push "$testbin" "$TEST_DEVICE_PATH/$test" &&
+ runCmd adb shell "$TEST_DEVICE_PATH/$test" &&
+ runCmd adb shell "rm $TEST_DEVICE_PATH/$test"
+ return $?
+ fi
+ done
+
+ logToStdErr "Couldn't find test binary '$prefix'"
+ return 1
+}
+
+function main() {
+ if [ ! -v ANDROID_BUILD_TOP ]; then
+ logToStdErr "You need to source and lunch before you can use this script"
+ return 1
+ fi
+ gotoRepoTop
+ if ! testAndSetRepoTop "$REPO_TOP"; then
+ logToStdErr "Could not find useful top of repo directory"
+ return 1
+ fi
+ logToStdErr "Using REPO_TOP=$REPO_TOP"
+
+ TARGET_ARCH=$("$SOONG_BIN" --dumpvar-mode TARGET_ARCH)
+ if [ -z "$TARGET_ARCH" ]; then
+ logToStdErr "Could not determine TARGET_ARCH"
+ return 1
+ fi
+
+ local test_regex=""
+ while [[ $# -gt 0 ]]; do
+ case "$1" in
+ "-n")
+ DEBUG=echo
+ shift
+ ;;
+ *)
+ # Allow us to do things like "runtests.sh integration", etc.
+ test_regex="$1"
+ shift
+ ;;
+ esac
+ done
+
+ # Try becoming root, in case the tests will be run on a device
+ # with a userdebug build. Failure to become root is not fatal
+ # for all (in fact, not even most) tests so don't exit on error.
+ runCmd adb root || logToStdErr "WARNING: unable to 'adb root'"
+
+ local failures=0
+ local skipped=0
+
+ # Find out which tests to run.
+ local tests=""
+ for testName in $DEFAULT_TESTS; do
+ if [[ -z "$test_regex" || "$testName" =~ "$test_regex" ]]; then
+ tests="$tests $testName"
+ else
+ logToStdErr "Skipping $testName"
+ skipped=$((skipped + 1))
+ fi
+ done
+
+ # If something has been specified, also check the extended tests for
+ # a possible match (i.e. in order to run benchmarks, etc).
+ if [[ -n "$test_regex" ]]; then
+ for testName in $EXTENDED_TESTS; do
+ if [[ "$testName" =~ "$test_regex" ]]; then
+ tests="$tests $testName"
+ fi
+ done
+ fi
+
+ if [[ -z "$tests" ]]; then
+ logToStdErr "No tests matched"
+ return 1
+ fi
+
+ # Build all the specified tests.
+ runCmd "$SOONG_BIN" --make-mode "$tests" || return $?
+
+ # Run all the specified tests.
+ for testName in $tests; do
+ runOneTest "$testName" || failures=$((failures + 1))
+ done
+
+ echo "Number of tests failing: $failures"
+ [[ $skipped -gt 0 ]] && echo "Number of tests skipped: $skipped"
+ return $failures
+}
+
+
+main "$@"
+exit $?
diff --git a/tests/test_utils.cpp b/tests/test_utils.cpp
deleted file mode 100644
index 27b17c5..0000000
--- a/tests/test_utils.cpp
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright 2019 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.
- *
- * test_utils.cpp - miscellaneous unit test utilities.
- */
-
-#include <cstdio>
-#include <string>
-#include <vector>
-
-#include <android-base/stringprintf.h>
-
-#include "test_utils.h"
-
-#define IP_PATH "/system/bin/ip"
-
-using android::base::StringPrintf;
-
-int randomUid() {
- // Pick a random UID consisting of:
- // - Random user profile (0 - 6)
- // - Random app ID starting from 12000 (FIRST_APPLICATION_UID + 2000). This ensures no conflicts
- // with existing app UIDs unless the user has installed more than 2000 apps, and is still less
- // than LAST_APPLICATION_UID (19999).
- return 100000 * arc4random_uniform(7) + 12000 + arc4random_uniform(3000);
-}
-
-std::vector<std::string> runCommand(const std::string& command) {
- std::vector<std::string> lines;
- FILE* f = popen(command.c_str(), "r"); // NOLINT(cert-env33-c)
- if (f == nullptr) {
- perror("popen");
- return lines;
- }
-
- char* line = nullptr;
- size_t bufsize = 0;
- ssize_t linelen = 0;
- while ((linelen = getline(&line, &bufsize, f)) >= 0) {
- lines.push_back(std::string(line, linelen));
- free(line);
- line = nullptr;
- }
-
- pclose(f);
- return lines;
-}
-
-std::vector<std::string> listIpRules(const char* ipVersion) {
- std::string command = StringPrintf("%s %s rule list", IP_PATH, ipVersion);
- return runCommand(command);
-}
-
-std::vector<std::string> listIptablesRule(const char* binary, const char* chainName) {
- std::string command = StringPrintf("%s -w -n -L %s", binary, chainName);
- return runCommand(command);
-}
-
-int iptablesRuleLineLength(const char* binary, const char* chainName) {
- return listIptablesRule(binary, chainName).size();
-}
-
-bool iptablesRuleExists(const char* binary, const char* chainName,
- const std::string& expectedRule) {
- std::vector<std::string> rules = listIptablesRule(binary, chainName);
- for (const auto& rule : rules) {
- if (rule.find(expectedRule) != std::string::npos) {
- return true;
- }
- }
- return false;
-}
-
-std::vector<std::string> listIpRoutes(const char* ipVersion, const char* table) {
- std::string command = StringPrintf("%s %s route ls table %s", IP_PATH, ipVersion, table);
- return runCommand(command);
-}
-
-bool ipRouteExists(const char* ipVersion, const char* table, const std::string& ipRoute) {
- std::vector<std::string> routes = listIpRoutes(ipVersion, table);
- for (const auto& route : routes) {
- if (route.find(ipRoute) != std::string::npos) {
- return true;
- }
- }
- return false;
-}
diff --git a/tests/test_utils.h b/tests/test_utils.h
deleted file mode 100644
index c8cd6ce..0000000
--- a/tests/test_utils.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright 2019 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.
- *
- * test_utils.h - miscellaneous unit test utilities.
- */
-
-#pragma once
-
-#include <string>
-#include <vector>
-
-int randomUid();
-
-std::vector<std::string> runCommand(const std::string& command);
-
-std::vector<std::string> listIpRules(const char* ipVersion);
-
-std::vector<std::string> listIptablesRule(const char* binary, const char* chainName);
-
-int iptablesRuleLineLength(const char* binary, const char* chainName);
-
-bool iptablesRuleExists(const char* binary, const char* chainName, const std::string& expectedRule);
-
-std::vector<std::string> listIpRoutes(const char* ipVersion, const char* table);
-
-bool ipRouteExists(const char* ipVersion, const char* table, const std::string& ipRoute);
diff --git a/tests/tun_interface.cpp b/tests/tun_interface.cpp
index e412ffd..65f1528 100644
--- a/tests/tun_interface.cpp
+++ b/tests/tun_interface.cpp
@@ -137,7 +137,7 @@
timeval timeout = {.tv_usec = 200 * 1000};
if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) == -1) return -errno;
- if (int ret = ifc_add_address(mIfName.c_str(), addr.c_str(), prefixlen)) return ret;
+ if (ifc_add_address(mIfName.c_str(), addr.c_str(), prefixlen)) return -errno;
int family;
size_t addrlen;