[automerger skipped] netlink_listener_test deflake - again... am: e3425844b8 -s ours
am skip reason: Change-Id I5244953c553ac46432af80010e7acb9414926afd with SHA-1 728695794b is in history
Original change: https://android-review.googlesource.com/c/platform/system/netd/+/1168207
Change-Id: Id865823fde3f17f52e29b06bdba1afe1dd7aeea6
diff --git a/Android.bp b/Android.bp
index 114b2e1..01a8e77 100644
--- a/Android.bp
+++ b/Android.bp
@@ -3,6 +3,16 @@
export_include_dirs: ["include"],
}
+cc_library_headers {
+ name: "libnetdbinder_utils_headers",
+ export_include_dirs: ["include/binder_utils"],
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.resolv",
+ ],
+ min_sdk_version: "29",
+}
+
cc_defaults {
name: "netd_defaults",
cflags: [
@@ -10,6 +20,8 @@
"-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",
@@ -17,13 +29,16 @@
"-Wuninitialized",
],
tidy: true,
+ cpp_std: "experimental",
tidy_checks: [
"android-*",
+ "bugprone-*",
"cert-*",
"clang-analyzer-security*",
"google-*",
"misc-*",
"performance-*",
+ "-bugprone-narrowing-conversions", // lots of unsigned -> int conversions
"-cert-err34-c", // TODO: re-enable after removing atoi() and sscanf() calls
"-google-readability-*", // Too pedantic
"-google-runtime-int", // Too many unavoidable warnings due to strtol()
@@ -32,11 +47,12 @@
],
tidy_flags: [
"-warnings-as-errors="
- + "'android-*'"
- + ",'clang-analyzer-security*'"
- + ",'cert-*'"
- + ",'google-*'"
- + ",'performance-*'"
- + ",'misc-*'"
+ + "android-*,"
+ + "bugprone-*,"
+ + "cert-*,"
+ + "clang-analyzer-security*,"
+ + "google-*,"
+ + "misc-*,"
+ + "performance-*"
],
}
diff --git a/OWNERS b/OWNERS
index 107eefc..7d617ec 100644
--- a/OWNERS
+++ b/OWNERS
@@ -4,6 +4,7 @@
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 c8dbf77..27eac94 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -1,5 +1,6 @@
[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 2d3673c..095fa7d 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -1,10 +1,20 @@
{
"presubmit": [
+ { "name": "libnetdbpf_test" },
{ "name": "netd_integration_test" },
{ "name": "netd_unit_test" },
- { "name": "libnetdbpf_test" },
- { "name": "netdutils_test" },
- { "name": "resolv_integration_test" },
- { "name": "resolv_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" }
]
}
diff --git a/apex/Android.bp b/apex/Android.bp
deleted file mode 100644
index c1d33e1..0000000
--- a/apex/Android.bp
+++ /dev/null
@@ -1,41 +0,0 @@
-// 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
deleted file mode 100644
index 9d6455c..0000000
--- a/apex/AndroidManifest.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?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
deleted file mode 100644
index bc97a1b..0000000
--- a/apex/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-chenbruce@google.com
-codewiz@google.com
-martinwu@google.com
-
diff --git a/apex/com.android.resolv.avbpubkey b/apex/com.android.resolv.avbpubkey
deleted file mode 100644
index e0af34c..0000000
--- a/apex/com.android.resolv.avbpubkey
+++ /dev/null
Binary files differ
diff --git a/apex/com.android.resolv.pem b/apex/com.android.resolv.pem
deleted file mode 100644
index 6eb688d..0000000
--- a/apex/com.android.resolv.pem
+++ /dev/null
@@ -1,51 +0,0 @@
------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
deleted file mode 100644
index 6739a4d..0000000
--- a/apex/manifest.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "name": "com.android.resolv",
- "version": 290000000
-}
diff --git a/apex/testcert.pk8 b/apex/testcert.pk8
deleted file mode 100644
index adfc589..0000000
--- a/apex/testcert.pk8
+++ /dev/null
Binary files differ
diff --git a/apex/testcert.x509.pem b/apex/testcert.x509.pem
deleted file mode 100644
index cba30d6..0000000
--- a/apex/testcert.x509.pem
+++ /dev/null
@@ -1,35 +0,0 @@
------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 a393035..8ce924b 100644
--- a/bpf_progs/Android.bp
+++ b/bpf_progs/Android.bp
@@ -14,6 +14,11 @@
// limitations under the License.
//
+cc_library_headers {
+ name: "netd_bpf_progs_headers",
+ export_include_dirs: ["."],
+}
+
//
// bpf kernel programs
//
@@ -42,3 +47,16 @@
"system/netd/libnetdutils/include",
],
}
+
+bpf {
+ name: "offload.o",
+ srcs: ["offload.c"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ include_dirs: [
+ "system/netd/libnetdbpf/include",
+ "system/netd/libnetdutils/include",
+ ],
+}
diff --git a/bpf_progs/bpf_net_helpers.h b/bpf_progs/bpf_net_helpers.h
new file mode 100644
index 0000000..d4dc5f8
--- /dev/null
+++ b/bpf_progs/bpf_net_helpers.h
@@ -0,0 +1,61 @@
+/*
+ * 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 int64_t (*bpf_csum_diff)(__be32* from, __u32 from_size, __be32* to, __u32 to_size,
+ __wsum seed) = (void*)BPF_FUNC_csum_diff;
+
+static int64_t (*bpf_csum_update)(struct __sk_buff* skb, __wsum csum) = (void*)BPF_FUNC_csum_update;
+
+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;
+
+static int (*bpf_skb_change_head)(struct __sk_buff* skb, __u32 head_room,
+ __u64 flags) = (void*)BPF_FUNC_skb_change_head;
+static int (*bpf_skb_adjust_room)(struct __sk_buff* skb, __s32 len_diff, __u32 mode,
+ __u64 flags) = (void*)BPF_FUNC_skb_adjust_room;
+
+// 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 578d244..102ecd9 100644
--- a/bpf_progs/clatd.c
+++ b/bpf_progs/clatd.c
@@ -23,31 +23,16 @@
#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) {
@@ -56,8 +41,6 @@
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;
@@ -75,12 +58,10 @@
if (ntohs(ip6->payload_len) > 0xFFFF - sizeof(struct iphdr)) return TC_ACT_OK;
switch (ip6->nexthdr) {
- 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;
+ 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.
break;
default: // do not know how to handle anything else
@@ -123,20 +104,45 @@
};
// Calculate the IPv4 one's complement checksum of the IPv4 header.
- __u32 sum = 0;
+ __wsum sum4 = 0;
for (int i = 0; i < sizeof(ip) / sizeof(__u16); ++i) {
- sum += ((__u16*)&ip)[i];
+ sum4 += ((__u16*)&ip)[i];
}
- // Note that sum is guaranteed to be non-zero by virtue of ip.version == 4
- sum = (sum & 0xFFFF) + (sum >> 16); // collapse u32 into range 1 .. 0x1FFFE
- sum = (sum & 0xFFFF) + (sum >> 16); // collapse any potential carry into u16
- ip.check = (__u16)~sum; // sum cannot be zero, so this is never 0xFFFF
+ // Note that sum4 is guaranteed to be non-zero by virtue of ip.version == 4
+ sum4 = (sum4 & 0xFFFF) + (sum4 >> 16); // collapse u32 into range 1 .. 0x1FFFE
+ sum4 = (sum4 & 0xFFFF) + (sum4 >> 16); // collapse any potential carry into u16
+ ip.check = (__u16)~sum4; // sum4 cannot be zero, so this is never 0xFFFF
+
+ // Calculate the *negative* IPv6 16-bit one's complement checksum of the IPv6 header.
+ __wsum sum6 = 0;
+ // We'll end up with a non-zero sum due to ip6->version == 6 (which has '0' bits)
+ for (int i = 0; i < sizeof(*ip6) / sizeof(__u16); ++i) {
+ sum6 += ~((__u16*)ip6)[i]; // note the bitwise negation
+ }
// 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.
- if (bpf_skb_change_proto(skb, htons(ETH_P_IP), 0)) return TC_ACT_SHOT;
+ // 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;
+
+ // This takes care of updating the skb->csum field for a CHECKSUM_COMPLETE packet.
+ //
+ // In such a case, skb->csum is a 16-bit one's complement sum of the entire payload,
+ // thus we need to subtract out the ipv6 header's sum, and add in the ipv4 header's sum.
+ // However, by construction of ip.check above the checksum of an ipv4 header is zero.
+ // Thus we only need to subtract the ipv6 header's sum, which is the same as adding
+ // in the sum of the bitwise negation of the ipv6 header.
+ //
+ // bpf_csum_update() always succeeds if the skb is CHECKSUM_COMPLETE and returns an error
+ // (-ENOTSUPP) if it isn't. So we just ignore the return code.
+ //
+ // if (skb->ip_summed == CHECKSUM_COMPLETE)
+ // return (skb->csum = csum_add(skb->csum, csum));
+ // else
+ // return -ENOTSUPP;
+ bpf_csum_update(skb, sum6);
// bpf_skb_change_proto() invalidates all pointers - reload them.
data = (void*)(long)skb->data;
@@ -176,4 +182,125 @@
return nat64(skb, false);
}
-char _license[] SEC("license") = "Apache 2.0";
+DEFINE_BPF_MAP(clat_egress_map, HASH, ClatEgressKey, ClatEgressValue, 16)
+
+SEC("schedcls/egress/clat_ether")
+int sched_cls_egress_clat_ether(struct __sk_buff* skb) {
+ return TC_ACT_OK;
+}
+
+SEC("schedcls/egress/clat_rawip")
+int sched_cls_egress_clat_rawip(struct __sk_buff* skb) {
+ void* data = (void*)(long)skb->data;
+ const void* data_end = (void*)(long)skb->data_end;
+ const struct iphdr* const ip4 = data;
+
+ // Must be meta-ethernet IPv4 frame
+ if (skb->protocol != htons(ETH_P_IP)) return TC_ACT_OK;
+
+ // Must have ipv4 header
+ if (data + sizeof(*ip4) > data_end) return TC_ACT_OK;
+
+ // IP version must be 4
+ if (ip4->version != 4) return TC_ACT_OK;
+
+ // We cannot handle IP options, just standard 20 byte == 5 dword minimal IPv4 header
+ if (ip4->ihl != 5) return TC_ACT_OK;
+
+ // Calculate the IPv4 one's complement checksum of the IPv4 header.
+ __wsum sum4 = 0;
+ for (int i = 0; i < sizeof(*ip4) / sizeof(__u16); ++i) {
+ sum4 += ((__u16*)ip4)[i];
+ }
+ // Note that sum4 is guaranteed to be non-zero by virtue of ip4->version == 4
+ sum4 = (sum4 & 0xFFFF) + (sum4 >> 16); // collapse u32 into range 1 .. 0x1FFFE
+ sum4 = (sum4 & 0xFFFF) + (sum4 >> 16); // collapse any potential carry into u16
+ // for a correct checksum we should get *a* zero, but sum4 must be positive, ie 0xFFFF
+ if (sum4 != 0xFFFF) return TC_ACT_OK;
+
+ // Minimum IPv4 total length is the size of the header
+ if (ntohs(ip4->tot_len) < sizeof(*ip4)) return TC_ACT_OK;
+
+ // We are incapable of dealing with IPv4 fragments
+ if (ip4->frag_off & ~htons(IP_DF)) return TC_ACT_OK;
+
+ switch (ip4->protocol) {
+ 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.
+ break;
+
+ default: // do not know how to handle anything else
+ return TC_ACT_OK;
+ }
+
+ ClatEgressKey k = {
+ .iif = skb->ifindex,
+ .local4.s_addr = ip4->saddr,
+ };
+
+ ClatEgressValue* v = bpf_clat_egress_map_lookup_elem(&k);
+
+ if (!v) return TC_ACT_OK;
+
+ // Translating without redirecting doesn't make sense.
+ if (!v->oif) return TC_ACT_OK;
+
+ // This implementation is currently limited to rawip.
+ if (v->oifIsEthernet) return TC_ACT_OK;
+
+ struct ipv6hdr ip6 = {
+ .version = 6, // __u8:4
+ .priority = ip4->tos >> 4, // __u8:4
+ .flow_lbl = {(ip4->tos & 0xF) << 4, 0, 0}, // __u8[3]
+ .payload_len = htons(ntohs(ip4->tot_len) - 20), // __be16
+ .nexthdr = ip4->protocol, // __u8
+ .hop_limit = ip4->ttl, // __u8
+ .saddr = v->local6, // struct in6_addr
+ .daddr = v->pfx96, // struct in6_addr
+ };
+ ip6.daddr.in6_u.u6_addr32[3] = ip4->daddr;
+
+ // Calculate the IPv6 16-bit one's complement checksum of the IPv6 header.
+ __wsum sum6 = 0;
+ // We'll end up with a non-zero sum due to ip6.version == 6
+ for (int i = 0; i < sizeof(ip6) / sizeof(__u16); ++i) {
+ sum6 += ((__u16*)&ip6)[i];
+ }
+
+ // 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_IPV6), 0)) return TC_ACT_OK;
+
+ // This takes care of updating the skb->csum field for a CHECKSUM_COMPLETE packet.
+ //
+ // In such a case, skb->csum is a 16-bit one's complement sum of the entire payload,
+ // thus we need to subtract out the ipv4 header's sum, and add in the ipv6 header's sum.
+ // However, we've already verified the ipv4 checksum is correct and thus 0.
+ // Thus we only need to add the ipv6 header's sum.
+ //
+ // bpf_csum_update() always succeeds if the skb is CHECKSUM_COMPLETE and returns an error
+ // (-ENOTSUPP) if it isn't. So we just ignore the return code (see above for more details).
+ bpf_csum_update(skb, sum6);
+
+ // bpf_skb_change_proto() invalidates all pointers - reload them.
+ data = (void*)(long)skb->data;
+ data_end = (void*)(long)skb->data_end;
+
+ // I cannot think of any valid way for this error condition to trigger, however I do
+ // believe the explicit check is required to keep the in kernel ebpf verifier happy.
+ if (data + sizeof(ip6) > data_end) return TC_ACT_SHOT;
+
+ // Copy over the new ipv6 header without an ethernet header.
+ *(struct ipv6hdr*)data = ip6;
+
+ // Redirect to non v4-* interface. Tcpdump only sees packet after this redirect.
+ return bpf_redirect(v->oif, 0 /* this is effectively BPF_F_EGRESS */);
+}
+
+LICENSE("Apache 2.0");
+CRITICAL("netd");
diff --git a/bpf_progs/netd.c b/bpf_progs/netd.c
index 8f2863e..f347028 100644
--- a/bpf_progs/netd.c
+++ b/bpf_progs/netd.c
@@ -14,8 +14,271 @@
* limitations under the License.
*/
-#include "netd.h"
+#include <bpf_helpers.h>
#include <linux/bpf.h>
+#include <linux/if.h>
+#include <linux/if_ether.h>
+#include <linux/if_packet.h>
+#include <linux/in.h>
+#include <linux/in6.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/tcp.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include "bpf_net_helpers.h"
+#include "netdbpf/bpf_shared.h"
+
+// This is defined for cgroup bpf filter only.
+#define BPF_DROP_UNLESS_DNS 2
+#define BPF_PASS 1
+#define BPF_DROP 0
+
+// This is used for xt_bpf program only.
+#define BPF_NOMATCH 0
+#define BPF_MATCH 1
+
+#define BPF_EGRESS 0
+#define BPF_INGRESS 1
+
+#define IP_PROTO_OFF offsetof(struct iphdr, protocol)
+#define IPV6_PROTO_OFF offsetof(struct ipv6hdr, nexthdr)
+#define IPPROTO_IHL_OFF 0
+#define TCP_FLAG_OFF 13
+#define RST_OFFSET 2
+
+DEFINE_BPF_MAP_GRO(cookie_tag_map, HASH, uint64_t, UidTagValue, COOKIE_UID_MAP_SIZE,
+ AID_NET_BW_ACCT)
+DEFINE_BPF_MAP_GRO(uid_counterset_map, HASH, uint32_t, uint8_t, UID_COUNTERSET_MAP_SIZE,
+ AID_NET_BW_ACCT)
+DEFINE_BPF_MAP_GRO(app_uid_stats_map, HASH, uint32_t, StatsValue, APP_STATS_MAP_SIZE,
+ AID_NET_BW_STATS)
+DEFINE_BPF_MAP_GRW(stats_map_A, HASH, StatsKey, StatsValue, STATS_MAP_SIZE, AID_NET_BW_STATS)
+DEFINE_BPF_MAP_GRW(stats_map_B, HASH, StatsKey, StatsValue, STATS_MAP_SIZE, AID_NET_BW_STATS)
+DEFINE_BPF_MAP_GRO(iface_stats_map, HASH, uint32_t, StatsValue, IFACE_STATS_MAP_SIZE,
+ AID_NET_BW_STATS)
+DEFINE_BPF_MAP_GRO(configuration_map, HASH, uint32_t, uint8_t, CONFIGURATION_MAP_SIZE,
+ AID_NET_BW_STATS)
+DEFINE_BPF_MAP(uid_owner_map, HASH, uint32_t, UidOwnerValue, UID_OWNER_MAP_SIZE)
+
+/* never actually used from ebpf */
+DEFINE_BPF_MAP_GRO(iface_index_name_map, HASH, uint32_t, IfaceValue, IFACE_INDEX_NAME_MAP_SIZE,
+ AID_NET_BW_STATS)
+
+static __always_inline int is_system_uid(uint32_t uid) {
+ return (uid <= MAX_SYSTEM_UID) && (uid >= MIN_SYSTEM_UID);
+}
+
+/*
+ * Note: this blindly assumes an MTU of 1500, and that packets > MTU are always TCP,
+ * and that TCP is using the Linux default settings with TCP timestamp option enabled
+ * which uses 12 TCP option bytes per frame.
+ *
+ * These are not unreasonable assumptions:
+ *
+ * The internet does not really support MTUs greater than 1500, so most TCP traffic will
+ * be at that MTU, or slightly below it (worst case our upwards adjustment is too small).
+ *
+ * The chance our traffic isn't IP at all is basically zero, so the IP overhead correction
+ * is bound to be needed.
+ *
+ * Furthermore, the likelyhood that we're having to deal with GSO (ie. > MTU) packets that
+ * are not IP/TCP is pretty small (few other things are supported by Linux) and worse case
+ * our extra overhead will be slightly off, but probably still better than assuming none.
+ *
+ * Most servers are also Linux and thus support/default to using TCP timestamp option
+ * (and indeed TCP timestamp option comes from RFC 1323 titled "TCP Extensions for High
+ * Performance" which also defined TCP window scaling and are thus absolutely ancient...).
+ *
+ * All together this should be more correct than if we simply ignored GSO frames
+ * (ie. counted them as single packets with no extra overhead)
+ *
+ * Especially since the number of packets is important for any future clat offload correction.
+ * (which adjusts upward by 20 bytes per packet to account for ipv4 -> ipv6 header conversion)
+ */
+#define DEFINE_UPDATE_STATS(the_stats_map, TypeOfKey) \
+ static __always_inline inline void update_##the_stats_map(struct __sk_buff* skb, \
+ int direction, TypeOfKey* key) { \
+ StatsValue* value = bpf_##the_stats_map##_lookup_elem(key); \
+ if (!value) { \
+ StatsValue newValue = {}; \
+ bpf_##the_stats_map##_update_elem(key, &newValue, BPF_NOEXIST); \
+ value = bpf_##the_stats_map##_lookup_elem(key); \
+ } \
+ if (value) { \
+ const int mtu = 1500; \
+ uint64_t packets = 1; \
+ uint64_t bytes = skb->len; \
+ if (bytes > mtu) { \
+ bool is_ipv6 = (skb->protocol == htons(ETH_P_IPV6)); \
+ int ip_overhead = (is_ipv6 ? sizeof(struct ipv6hdr) : sizeof(struct iphdr)); \
+ int tcp_overhead = ip_overhead + sizeof(struct tcphdr) + 12; \
+ int mss = mtu - tcp_overhead; \
+ uint64_t payload = bytes - tcp_overhead; \
+ packets = (payload + mss - 1) / mss; \
+ bytes = tcp_overhead * packets + payload; \
+ } \
+ if (direction == BPF_EGRESS) { \
+ __sync_fetch_and_add(&value->txPackets, packets); \
+ __sync_fetch_and_add(&value->txBytes, bytes); \
+ } else if (direction == BPF_INGRESS) { \
+ __sync_fetch_and_add(&value->rxPackets, packets); \
+ __sync_fetch_and_add(&value->rxBytes, bytes); \
+ } \
+ } \
+ }
+
+DEFINE_UPDATE_STATS(app_uid_stats_map, uint32_t)
+DEFINE_UPDATE_STATS(iface_stats_map, uint32_t)
+DEFINE_UPDATE_STATS(stats_map_A, StatsKey)
+DEFINE_UPDATE_STATS(stats_map_B, StatsKey)
+
+static inline bool skip_owner_match(struct __sk_buff* skb) {
+ int offset = -1;
+ int ret = 0;
+ if (skb->protocol == htons(ETH_P_IP)) {
+ offset = IP_PROTO_OFF;
+ uint8_t proto, ihl;
+ uint8_t flag;
+ ret = bpf_skb_load_bytes(skb, offset, &proto, 1);
+ if (!ret) {
+ if (proto == IPPROTO_ESP) {
+ return true;
+ } else if (proto == IPPROTO_TCP) {
+ ret = bpf_skb_load_bytes(skb, IPPROTO_IHL_OFF, &ihl, 1);
+ ihl = ihl & 0x0F;
+ ret = bpf_skb_load_bytes(skb, ihl * 4 + TCP_FLAG_OFF, &flag, 1);
+ if (ret == 0 && (flag >> RST_OFFSET & 1)) {
+ return true;
+ }
+ }
+ }
+ } else if (skb->protocol == htons(ETH_P_IPV6)) {
+ offset = IPV6_PROTO_OFF;
+ uint8_t proto;
+ ret = bpf_skb_load_bytes(skb, offset, &proto, 1);
+ if (!ret) {
+ if (proto == IPPROTO_ESP) {
+ return true;
+ } else if (proto == IPPROTO_TCP) {
+ uint8_t flag;
+ ret = bpf_skb_load_bytes(skb, sizeof(struct ipv6hdr) + TCP_FLAG_OFF, &flag, 1);
+ if (ret == 0 && (flag >> RST_OFFSET & 1)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+static __always_inline BpfConfig getConfig(uint32_t configKey) {
+ uint32_t mapSettingKey = configKey;
+ BpfConfig* config = bpf_configuration_map_lookup_elem(&mapSettingKey);
+ if (!config) {
+ // Couldn't read configuration entry. Assume everything is disabled.
+ return DEFAULT_CONFIG;
+ }
+ return *config;
+}
+
+static inline int bpf_owner_match(struct __sk_buff* skb, uint32_t uid, int direction) {
+ if (skip_owner_match(skb)) return BPF_PASS;
+
+ if (is_system_uid(uid)) return BPF_PASS;
+
+ BpfConfig enabledRules = getConfig(UID_RULES_CONFIGURATION_KEY);
+
+ UidOwnerValue* uidEntry = bpf_uid_owner_map_lookup_elem(&uid);
+ uint8_t uidRules = uidEntry ? uidEntry->rule : 0;
+ uint32_t allowed_iif = uidEntry ? uidEntry->iif : 0;
+
+ if (enabledRules) {
+ if ((enabledRules & DOZABLE_MATCH) && !(uidRules & DOZABLE_MATCH)) {
+ return BPF_DROP;
+ }
+ if ((enabledRules & STANDBY_MATCH) && (uidRules & STANDBY_MATCH)) {
+ return BPF_DROP;
+ }
+ if ((enabledRules & POWERSAVE_MATCH) && !(uidRules & POWERSAVE_MATCH)) {
+ return BPF_DROP;
+ }
+ }
+ if (direction == BPF_INGRESS && (uidRules & IIF_MATCH)) {
+ // Drops packets not coming from lo nor the whitelisted interface
+ if (allowed_iif && skb->ifindex != 1 && skb->ifindex != allowed_iif) {
+ return BPF_DROP_UNLESS_DNS;
+ }
+ }
+ return BPF_PASS;
+}
+
+static __always_inline inline void update_stats_with_config(struct __sk_buff* skb, int direction,
+ StatsKey* key, uint8_t selectedMap) {
+ if (selectedMap == SELECT_MAP_A) {
+ update_stats_map_A(skb, direction, key);
+ } else if (selectedMap == SELECT_MAP_B) {
+ update_stats_map_B(skb, direction, key);
+ }
+}
+
+static __always_inline inline int bpf_traffic_account(struct __sk_buff* skb, int direction) {
+ uint32_t sock_uid = bpf_get_socket_uid(skb);
+ // Always allow and never count clat traffic. Only the IPv4 traffic on the stacked
+ // interface is accounted for and subject to usage restrictions.
+ if (sock_uid == AID_CLAT) {
+ return BPF_PASS;
+ }
+
+ int match = bpf_owner_match(skb, sock_uid, direction);
+ if ((direction == BPF_EGRESS) && (match == BPF_DROP)) {
+ // If an outbound packet is going to be dropped, we do not count that
+ // traffic.
+ return match;
+ }
+
+ uint64_t cookie = bpf_get_socket_cookie(skb);
+ UidTagValue* utag = bpf_cookie_tag_map_lookup_elem(&cookie);
+ uint32_t uid, tag;
+ if (utag) {
+ uid = utag->uid;
+ tag = utag->tag;
+ } else {
+ uid = sock_uid;
+ tag = 0;
+ }
+
+// Workaround for secureVPN with VpnIsolation enabled, refer to b/159994981 for details.
+// Keep TAG_SYSTEM_DNS in sync with DnsResolver/include/netd_resolv/resolv.h
+// and TrafficStatsConstants.java
+#define TAG_SYSTEM_DNS 0xFFFFFF82
+ if (tag == TAG_SYSTEM_DNS && uid == AID_DNS) {
+ uid = sock_uid;
+ if (match == BPF_DROP_UNLESS_DNS) match = BPF_PASS;
+ } else {
+ if (match == BPF_DROP_UNLESS_DNS) match = BPF_DROP;
+ }
+
+ StatsKey key = {.uid = uid, .tag = tag, .counterSet = 0, .ifaceIndex = skb->ifindex};
+
+ uint8_t* counterSet = bpf_uid_counterset_map_lookup_elem(&uid);
+ if (counterSet) key.counterSet = (uint32_t)*counterSet;
+
+ uint32_t mapSettingKey = CURRENT_STATS_MAP_CONFIGURATION_KEY;
+ uint8_t* selectedMap = bpf_configuration_map_lookup_elem(&mapSettingKey);
+ if (!selectedMap) {
+ return match;
+ }
+
+ if (key.tag) {
+ update_stats_with_config(skb, direction, &key, *selectedMap);
+ key.tag = 0;
+ }
+
+ update_stats_with_config(skb, direction, &key, *selectedMap);
+ update_app_uid_stats_map(skb, direction, &uid);
+ return match;
+}
SEC("cgroupskb/ingress/stats")
int bpf_cgroup_ingress(struct __sk_buff* skb) {
@@ -27,41 +290,61 @@
return bpf_traffic_account(skb, BPF_EGRESS);
}
-SEC("skfilter/egress/xtbpf")
-int xt_bpf_egress_prog(struct __sk_buff* skb) {
+DEFINE_BPF_PROG("skfilter/egress/xtbpf", AID_ROOT, AID_NET_ADMIN, xt_bpf_egress_prog)
+(struct __sk_buff* skb) {
+ // Clat daemon does not generate new traffic, all its traffic is accounted for already
+ // on the v4-* interfaces (except for the 20 (or 28) extra bytes of IPv6 vs IPv4 overhead,
+ // but that can be corrected for later when merging v4-foo stats into interface foo's).
+ uint32_t sock_uid = bpf_get_socket_uid(skb);
+ if (sock_uid == AID_CLAT) return BPF_NOMATCH;
+
uint32_t key = skb->ifindex;
update_iface_stats_map(skb, BPF_EGRESS, &key);
return BPF_MATCH;
}
-SEC("skfilter/ingress/xtbpf")
-int xt_bpf_ingress_prog(struct __sk_buff* skb) {
+DEFINE_BPF_PROG("skfilter/ingress/xtbpf", AID_ROOT, AID_NET_ADMIN, xt_bpf_ingress_prog)
+(struct __sk_buff* skb) {
+ // Clat daemon traffic is not accounted by virtue of iptables raw prerouting drop rule
+ // (in clat_raw_PREROUTING chain), which triggers before this (in bw_raw_PREROUTING chain).
+ // It will be accounted for on the v4-* clat interface instead.
+ // Keep that in mind when moving this out of iptables xt_bpf and into tc ingress (or xdp).
+
uint32_t key = skb->ifindex;
update_iface_stats_map(skb, BPF_INGRESS, &key);
return BPF_MATCH;
}
-SEC("skfilter/whitelist/xtbpf")
-int xt_bpf_whitelist_prog(struct __sk_buff* skb) {
+DEFINE_BPF_PROG("skfilter/whitelist/xtbpf", AID_ROOT, AID_NET_ADMIN, xt_bpf_whitelist_prog)
+(struct __sk_buff* skb) {
uint32_t sock_uid = bpf_get_socket_uid(skb);
if (is_system_uid(sock_uid)) return BPF_MATCH;
+
+ // 65534 is the overflow 'nobody' uid, usually this being returned means
+ // 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))
+ return BPF_MATCH;
+
UidOwnerValue* whitelistMatch = bpf_uid_owner_map_lookup_elem(&sock_uid);
- if (whitelistMatch) return whitelistMatch->rule & HAPPY_BOX_MATCH;
+ if (whitelistMatch) return whitelistMatch->rule & HAPPY_BOX_MATCH ? BPF_MATCH : BPF_NOMATCH;
return BPF_NOMATCH;
}
-SEC("skfilter/blacklist/xtbpf")
-int xt_bpf_blacklist_prog(struct __sk_buff* skb) {
+DEFINE_BPF_PROG("skfilter/blacklist/xtbpf", AID_ROOT, AID_NET_ADMIN, xt_bpf_blacklist_prog)
+(struct __sk_buff* skb) {
uint32_t sock_uid = bpf_get_socket_uid(skb);
UidOwnerValue* blacklistMatch = bpf_uid_owner_map_lookup_elem(&sock_uid);
- if (blacklistMatch) return blacklistMatch->rule & PENALTY_BOX_MATCH;
+ if (blacklistMatch) return blacklistMatch->rule & PENALTY_BOX_MATCH ? BPF_MATCH : BPF_NOMATCH;
return BPF_NOMATCH;
}
DEFINE_BPF_MAP(uid_permission_map, HASH, uint32_t, uint8_t, UID_OWNER_MAP_SIZE)
-SEC("cgroupsock/inet/create")
-int inet_socket_create(struct bpf_sock* sk) {
+DEFINE_BPF_PROG_KVER("cgroupsock/inet/create", AID_ROOT, AID_ROOT, inet_socket_create,
+ KVER(4, 14, 0))
+(struct bpf_sock* sk) {
uint64_t gid_uid = bpf_get_current_uid_gid();
/*
* A given app is guaranteed to have the same app ID in all the profiles in
@@ -80,4 +363,5 @@
return (*permissions & BPF_PERMISSION_INTERNET) == BPF_PERMISSION_INTERNET;
}
-char _license[] SEC("license") = "Apache 2.0";
+LICENSE("Apache 2.0");
+CRITICAL("netd");
diff --git a/bpf_progs/netd.h b/bpf_progs/netd.h
deleted file mode 100644
index 8be21be..0000000
--- a/bpf_progs/netd.h
+++ /dev/null
@@ -1,245 +0,0 @@
-/*
- * 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 h file together with netd.c is used for compiling the eBPF kernel
- * program.
- */
-
-#include <bpf_helpers.h>
-#include <linux/bpf.h>
-#include <linux/if.h>
-#include <linux/if_ether.h>
-#include <linux/in.h>
-#include <linux/in6.h>
-#include <linux/ip.h>
-#include <linux/ipv6.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include "netdbpf/bpf_shared.h"
-
-typedef struct {
- uint32_t uid;
- uint32_t tag;
-} uid_tag;
-
-typedef struct {
- uint32_t uid;
- uint32_t tag;
- uint32_t counterSet;
- uint32_t ifaceIndex;
-} stats_key;
-
-typedef struct {
- uint64_t rxPackets;
- uint64_t rxBytes;
- uint64_t txPackets;
- uint64_t txBytes;
-} stats_value;
-
-typedef struct {
- char name[IFNAMSIZ];
-} IfaceValue;
-
-// This is defined for cgroup bpf filter only.
-#define BPF_PASS 1
-#define BPF_DROP 0
-
-// This is used for xt_bpf program only.
-#define BPF_NOMATCH 0
-#define BPF_MATCH 1
-
-#define BPF_EGRESS 0
-#define BPF_INGRESS 1
-
-#define IP_PROTO_OFF offsetof(struct iphdr, protocol)
-#define IPV6_PROTO_OFF offsetof(struct ipv6hdr, nexthdr)
-#define IPPROTO_IHL_OFF 0
-#define TCP_FLAG_OFF 13
-#define RST_OFFSET 2
-
-DEFINE_BPF_MAP(cookie_tag_map, HASH, uint64_t, uid_tag, COOKIE_UID_MAP_SIZE)
-DEFINE_BPF_MAP(uid_counterset_map, HASH, uint32_t, uint8_t, UID_COUNTERSET_MAP_SIZE)
-DEFINE_BPF_MAP(app_uid_stats_map, HASH, uint32_t, stats_value, APP_STATS_MAP_SIZE)
-DEFINE_BPF_MAP(stats_map_A, HASH, stats_key, stats_value, STATS_MAP_SIZE)
-DEFINE_BPF_MAP(stats_map_B, HASH, stats_key, stats_value, STATS_MAP_SIZE)
-DEFINE_BPF_MAP(iface_stats_map, HASH, uint32_t, stats_value, IFACE_STATS_MAP_SIZE)
-DEFINE_BPF_MAP(configuration_map, HASH, uint32_t, uint8_t, CONFIGURATION_MAP_SIZE)
-DEFINE_BPF_MAP(uid_owner_map, HASH, uint32_t, UidOwnerValue, UID_OWNER_MAP_SIZE)
-
-/* never actually used from ebpf */
-DEFINE_BPF_MAP_NO_ACCESSORS(iface_index_name_map, HASH, uint32_t, IfaceValue,
- IFACE_INDEX_NAME_MAP_SIZE)
-
-static __always_inline int is_system_uid(uint32_t uid) {
- return (uid <= MAX_SYSTEM_UID) && (uid >= MIN_SYSTEM_UID);
-}
-
-#define DEFINE_UPDATE_STATS(the_stats_map, TypeOfKey) \
- static __always_inline inline void update_##the_stats_map(struct __sk_buff* skb, \
- int direction, TypeOfKey* key) { \
- stats_value* value; \
- value = bpf_##the_stats_map##_lookup_elem(key); \
- if (!value) { \
- stats_value newValue = {}; \
- bpf_##the_stats_map##_update_elem(key, &newValue, BPF_NOEXIST); \
- value = bpf_##the_stats_map##_lookup_elem(key); \
- } \
- if (value) { \
- if (direction == BPF_EGRESS) { \
- __sync_fetch_and_add(&value->txPackets, 1); \
- __sync_fetch_and_add(&value->txBytes, skb->len); \
- } else if (direction == BPF_INGRESS) { \
- __sync_fetch_and_add(&value->rxPackets, 1); \
- __sync_fetch_and_add(&value->rxBytes, skb->len); \
- } \
- } \
- }
-
-DEFINE_UPDATE_STATS(app_uid_stats_map, uint32_t)
-DEFINE_UPDATE_STATS(iface_stats_map, uint32_t)
-DEFINE_UPDATE_STATS(stats_map_A, stats_key)
-DEFINE_UPDATE_STATS(stats_map_B, stats_key)
-
-static inline bool skip_owner_match(struct __sk_buff* skb) {
- int offset = -1;
- int ret = 0;
- if (skb->protocol == ETH_P_IP) {
- offset = IP_PROTO_OFF;
- uint8_t proto, ihl;
- uint16_t flag;
- ret = bpf_skb_load_bytes(skb, offset, &proto, 1);
- if (!ret) {
- if (proto == IPPROTO_ESP) {
- return true;
- } else if (proto == IPPROTO_TCP) {
- ret = bpf_skb_load_bytes(skb, IPPROTO_IHL_OFF, &ihl, 1);
- ihl = ihl & 0x0F;
- ret = bpf_skb_load_bytes(skb, ihl * 4 + TCP_FLAG_OFF, &flag, 1);
- if (ret == 0 && (flag >> RST_OFFSET & 1)) {
- return true;
- }
- }
- }
- } else if (skb->protocol == ETH_P_IPV6) {
- offset = IPV6_PROTO_OFF;
- uint8_t proto;
- ret = bpf_skb_load_bytes(skb, offset, &proto, 1);
- if (!ret) {
- if (proto == IPPROTO_ESP) {
- return true;
- } else if (proto == IPPROTO_TCP) {
- uint16_t flag;
- ret = bpf_skb_load_bytes(skb, sizeof(struct ipv6hdr) + TCP_FLAG_OFF, &flag, 1);
- if (ret == 0 && (flag >> RST_OFFSET & 1)) {
- return true;
- }
- }
- }
- }
- return false;
-}
-
-static __always_inline BpfConfig getConfig(uint32_t configKey) {
- uint32_t mapSettingKey = configKey;
- BpfConfig* config = bpf_configuration_map_lookup_elem(&mapSettingKey);
- if (!config) {
- // Couldn't read configuration entry. Assume everything is disabled.
- return DEFAULT_CONFIG;
- }
- return *config;
-}
-
-static inline int bpf_owner_match(struct __sk_buff* skb, uint32_t uid, int direction) {
- if (skip_owner_match(skb)) return BPF_PASS;
-
- if ((uid <= MAX_SYSTEM_UID) && (uid >= MIN_SYSTEM_UID)) return BPF_PASS;
-
- BpfConfig enabledRules = getConfig(UID_RULES_CONFIGURATION_KEY);
-
- UidOwnerValue* uidEntry = bpf_uid_owner_map_lookup_elem(&uid);
- uint8_t uidRules = uidEntry ? uidEntry->rule : 0;
- uint32_t allowed_iif = uidEntry ? uidEntry->iif : 0;
-
- if (enabledRules) {
- if ((enabledRules & DOZABLE_MATCH) && !(uidRules & DOZABLE_MATCH)) {
- return BPF_DROP;
- }
- if ((enabledRules & STANDBY_MATCH) && (uidRules & STANDBY_MATCH)) {
- return BPF_DROP;
- }
- if ((enabledRules & POWERSAVE_MATCH) && !(uidRules & POWERSAVE_MATCH)) {
- return BPF_DROP;
- }
- }
- if (direction == BPF_INGRESS && (uidRules & IIF_MATCH)) {
- // Drops packets not coming from lo nor the whitelisted interface
- if (allowed_iif && skb->ifindex != 1 && skb->ifindex != allowed_iif) {
- return BPF_DROP;
- }
- }
- return BPF_PASS;
-}
-
-static __always_inline inline void update_stats_with_config(struct __sk_buff* skb, int direction,
- stats_key* key, uint8_t selectedMap) {
- if (selectedMap == SELECT_MAP_A) {
- update_stats_map_A(skb, direction, key);
- } else if (selectedMap == SELECT_MAP_B) {
- update_stats_map_B(skb, direction, key);
- }
-}
-
-static __always_inline inline int bpf_traffic_account(struct __sk_buff* skb, int direction) {
- uint32_t sock_uid = bpf_get_socket_uid(skb);
- int match = bpf_owner_match(skb, sock_uid, direction);
- if ((direction == BPF_EGRESS) && (match == BPF_DROP)) {
- // If an outbound packet is going to be dropped, we do not count that
- // traffic.
- return match;
- }
-
- uint64_t cookie = bpf_get_socket_cookie(skb);
- uid_tag* utag = bpf_cookie_tag_map_lookup_elem(&cookie);
- uint32_t uid, tag;
- if (utag) {
- uid = utag->uid;
- tag = utag->tag;
- } else {
- uid = sock_uid;
- tag = 0;
- }
-
- stats_key key = {.uid = uid, .tag = tag, .counterSet = 0, .ifaceIndex = skb->ifindex};
-
- uint8_t* counterSet = bpf_uid_counterset_map_lookup_elem(&uid);
- if (counterSet) key.counterSet = (uint32_t)*counterSet;
-
- uint32_t mapSettingKey = CURRENT_STATS_MAP_CONFIGURATION_KEY;
- uint8_t* selectedMap = bpf_configuration_map_lookup_elem(&mapSettingKey);
- if (!selectedMap) {
- return match;
- }
-
- if (key.tag) {
- update_stats_with_config(skb, direction, &key, *selectedMap);
- key.tag = 0;
- }
-
- update_stats_with_config(skb, direction, &key, *selectedMap);
- update_app_uid_stats_map(skb, direction, &uid);
- return match;
-}
diff --git a/bpf_progs/offload.c b/bpf_progs/offload.c
new file mode 100644
index 0000000..16dbe1d
--- /dev/null
+++ b/bpf_progs/offload.c
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2020 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 <linux/if.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/pkt_cls.h>
+#include <linux/tcp.h>
+
+#include "bpf_helpers.h"
+#include "bpf_net_helpers.h"
+#include "netdbpf/bpf_shared.h"
+
+DEFINE_BPF_MAP_GRW(tether_ingress_map, HASH, TetherIngressKey, TetherIngressValue, 64,
+ AID_NETWORK_STACK)
+
+// Tethering stats, indexed by upstream interface.
+DEFINE_BPF_MAP_GRW(tether_stats_map, HASH, uint32_t, TetherStatsValue, 16, AID_NETWORK_STACK)
+
+// Tethering data limit, indexed by upstream interface.
+// (tethering allowed when stats[iif].rxBytes + stats[iif].txBytes < limit[iif])
+DEFINE_BPF_MAP_GRW(tether_limit_map, HASH, uint32_t, uint64_t, 16, AID_NETWORK_STACK)
+
+static inline __always_inline int do_forward(struct __sk_buff* skb, bool is_ethernet) {
+ int l2_header_size = is_ethernet ? sizeof(struct ethhdr) : 0;
+ void* data = (void*)(long)skb->data;
+ const void* data_end = (void*)(long)skb->data_end;
+ struct ethhdr* eth = is_ethernet ? data : NULL; // used iff is_ethernet
+ struct ipv6hdr* ip6 = is_ethernet ? (void*)(eth + 1) : data;
+
+ // Must be meta-ethernet IPv6 frame
+ if (skb->protocol != htons(ETH_P_IPV6)) return TC_ACT_OK;
+
+ // Must have (ethernet and) ipv6 header
+ if (data + l2_header_size + sizeof(*ip6) > data_end) return TC_ACT_OK;
+
+ // Ethertype - if present - must be IPv6
+ if (is_ethernet && (eth->h_proto != htons(ETH_P_IPV6))) return TC_ACT_OK;
+
+ // IP version must be 6
+ if (ip6->version != 6) return TC_ACT_OK;
+
+ // Cannot decrement during forward if already zero or would be zero,
+ // Let the kernel's stack handle these cases and generate appropriate ICMP errors.
+ if (ip6->hop_limit <= 1) return TC_ACT_OK;
+
+ // Protect against forwarding packets sourced from ::1 or fe80::/64 or other weirdness.
+ __be32 src32 = ip6->saddr.s6_addr32[0];
+ if (src32 != htonl(0x0064ff9b) && // 64:ff9b:/32 incl. XLAT464 WKP
+ (src32 & htonl(0xe0000000)) != htonl(0x20000000)) // 2000::/3 Global Unicast
+ return TC_ACT_OK;
+
+ TetherIngressKey k = {
+ .iif = skb->ifindex,
+ .neigh6 = ip6->daddr,
+ };
+
+ TetherIngressValue* v = bpf_tether_ingress_map_lookup_elem(&k);
+
+ // If we don't find any offload information then simply let the core stack handle it...
+ if (!v) return TC_ACT_OK;
+
+ uint32_t stat_and_limit_k = skb->ifindex;
+
+ TetherStatsValue* stat_v = bpf_tether_stats_map_lookup_elem(&stat_and_limit_k);
+
+ // If we don't have anywhere to put stats, then abort...
+ if (!stat_v) return TC_ACT_OK;
+
+ uint64_t* limit_v = bpf_tether_limit_map_lookup_elem(&stat_and_limit_k);
+
+ // If we don't have a limit, then abort...
+ if (!limit_v) return TC_ACT_OK;
+
+ // Required IPv6 minimum mtu is 1280, below that not clear what we should do, abort...
+ const int pmtu = v->pmtu;
+ if (pmtu < IPV6_MIN_MTU) return TC_ACT_OK;
+
+ // Approximate handling of TCP/IPv6 overhead for incoming LRO/GRO packets: default
+ // outbound path mtu of 1500 is not necessarily correct, but worst case we simply
+ // undercount, which is still better then not accounting for this overhead at all.
+ // Note: this really shouldn't be device/path mtu at all, but rather should be
+ // derived from this particular connection's mss (ie. from gro segment size).
+ // This would require a much newer kernel with newer ebpf accessors.
+ // (This is also blindly assuming 12 bytes of tcp timestamp option in tcp header)
+ uint64_t packets = 1;
+ uint64_t bytes = skb->len;
+ if (bytes > pmtu) {
+ const int tcp_overhead = sizeof(struct ipv6hdr) + sizeof(struct tcphdr) + 12;
+ const int mss = pmtu - tcp_overhead;
+ const uint64_t payload = bytes - tcp_overhead;
+ packets = (payload + mss - 1) / mss;
+ bytes = tcp_overhead * packets + payload;
+ }
+
+ // Are we past the limit? If so, then abort...
+ // Note: will not overflow since u64 is 936 years even at 5Gbps.
+ // Do not drop here. Offload is just that, whenever we fail to handle
+ // a packet we let the core stack deal with things.
+ // (The core stack needs to handle limits correctly anyway,
+ // since we don't offload all traffic in both directions)
+ if (stat_v->rxBytes + stat_v->txBytes + bytes > *limit_v) return TC_ACT_OK;
+
+ if (!is_ethernet) {
+ is_ethernet = true;
+ l2_header_size = sizeof(struct ethhdr);
+ // Try to inject an ethernet header, and simply return if we fail
+ if (bpf_skb_change_head(skb, l2_header_size, /*flags*/ 0)) {
+ __sync_fetch_and_add(&stat_v->rxErrors, 1);
+ return TC_ACT_OK;
+ }
+
+ // bpf_skb_change_head() invalidates all pointers - reload them
+ data = (void*)(long)skb->data;
+ data_end = (void*)(long)skb->data_end;
+ eth = data;
+ ip6 = (void*)(eth + 1);
+
+ // I do not believe this can ever happen, but keep the verifier happy...
+ if (data + l2_header_size + sizeof(*ip6) > data_end) return TC_ACT_SHOT;
+ };
+
+ // CHECKSUM_COMPLETE is a 16-bit one's complement sum,
+ // thus corrections for it need to be done in 16-byte chunks at even offsets.
+ // IPv6 nexthdr is at offset 6, while hop limit is at offset 7
+ uint8_t old_hl = ip6->hop_limit;
+ --ip6->hop_limit;
+ uint8_t new_hl = ip6->hop_limit;
+
+ // bpf_csum_update() always succeeds if the skb is CHECKSUM_COMPLETE and returns an error
+ // (-ENOTSUPP) if it isn't.
+ bpf_csum_update(skb, 0xFFFF - ntohs(old_hl) + ntohs(new_hl));
+
+ __sync_fetch_and_add(&stat_v->rxPackets, packets);
+ __sync_fetch_and_add(&stat_v->rxBytes, bytes);
+
+ // Overwrite any mac header with the new one
+ *eth = v->macHeader;
+
+ // Redirect to forwarded interface.
+ //
+ // Note that bpf_redirect() cannot fail unless you pass invalid flags.
+ // The redirect actually happens after the ebpf program has already terminated,
+ // and can fail for example for mtu reasons at that point in time, but there's nothing
+ // we can do about it here.
+ return bpf_redirect(v->oif, 0 /* this is effectively BPF_F_EGRESS */);
+}
+
+SEC("schedcls/ingress/tether_ether")
+int sched_cls_ingress_tether_ether(struct __sk_buff* skb) {
+ return do_forward(skb, true);
+}
+
+// Note: section names must be unique to prevent programs from appending to each other,
+// so instead the bpf loader will strip everything past the final $ symbol when actually
+// pinning the program into the filesystem.
+//
+// bpf_skb_change_head() is only present on 4.14+ and 2 trivial kernel patches are needed:
+// ANDROID: net: bpf: Allow TC programs to call BPF_FUNC_skb_change_head
+// ANDROID: net: bpf: permit redirect from ingress L3 to egress L2 devices at near max mtu
+// (the first of those has already been upstreamed)
+//
+// 5.4 kernel support was only added to Android Common Kernel in R,
+// and thus a 5.4 kernel always supports this.
+//
+// Hence, this mandatory (must load successfully) implementation for 5.4+ kernels:
+DEFINE_BPF_PROG_KVER("schedcls/ingress/tether_rawip$5_4", AID_ROOT, AID_ROOT,
+ sched_cls_ingress_tether_rawip_5_4, KVER(5, 4, 0))
+(struct __sk_buff* skb) {
+ return do_forward(skb, false);
+}
+
+// and this identical optional (may fail to load) implementation for [4.14..5.4) patched kernels:
+DEFINE_OPTIONAL_BPF_PROG_KVER_RANGE("schedcls/ingress/tether_rawip$4_14", AID_ROOT, AID_ROOT,
+ sched_cls_ingress_tether_rawip_4_14, KVER(4, 14, 0),
+ KVER(5, 4, 0))
+(struct __sk_buff* skb) {
+ return do_forward(skb, false);
+}
+
+// and define a no-op stub for [4.9,4.14) and unpatched [4.14,5.4) kernels.
+// (if the above real 4.14+ program loaded successfully, then bpfloader will have already pinned
+// it at the same location this one would be pinned at and will thus skip loading this stub)
+DEFINE_BPF_PROG_KVER_RANGE("schedcls/ingress/tether_rawip$stub", AID_ROOT, AID_ROOT,
+ sched_cls_ingress_tether_rawip_stub, KVER_NONE, KVER(5, 4, 0))
+(struct __sk_buff* skb) {
+ return TC_ACT_OK;
+}
+
+LICENSE("Apache 2.0");
+CRITICAL("netd");
diff --git a/client/Android.bp b/client/Android.bp
index 3a6e97d..53afc97 100644
--- a/client/Android.bp
+++ b/client/Android.bp
@@ -19,20 +19,19 @@
"NetdClient.cpp",
],
header_libs: [
+ "dnsproxyd_protocol_headers", // NETID_USE_LOCAL_NAMESERVERS
"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"],
- product_variables: {
- debuggable: {
- cflags: ["-DNETD_CLIENT_DEBUGGABLE_BUILD"],
- }
- }
+ sanitize: {
+ cfi: true,
+ },
}
cc_test {
@@ -43,9 +42,10 @@
defaults: ["netd_defaults"],
test_suites: ["device-tests"],
include_dirs: [
- "system/netd/resolv",
"system/netd/include",
- "system/netd/libnetdutils/include",
+ ],
+ header_libs: [
+ "libnetd_resolv_headers",
],
static_libs: [
"libgmock",
diff --git a/client/FwmarkClient.cpp b/client/FwmarkClient.cpp
index cc4893d..592fe31 100644
--- a/client/FwmarkClient.cpp
+++ b/client/FwmarkClient.cpp
@@ -31,21 +31,11 @@
namespace {
// Env flag to control whether FwmarkClient sends sockets to netd for marking.
-// This can only be disabled in debuggable builds and is meant for kernel testing.
+// This can only be disabled when the process running as root and is meant for kernel testing.
inline constexpr char ANDROID_NO_USE_FWMARK_CLIENT[] = "ANDROID_NO_USE_FWMARK_CLIENT";
const sockaddr_un FWMARK_SERVER_PATH = {AF_UNIX, "/dev/socket/fwmarkd"};
-#if defined(NETD_CLIENT_DEBUGGABLE_BUILD)
-constexpr bool isBuildDebuggable = true;
-#else
-constexpr bool isBuildDebuggable = false;
-#endif
-
-bool isOverriddenBy(const char *name) {
- return isBuildDebuggable && getenv(name);
-}
-
bool commandHasFd(int cmdId) {
return (cmdId != FwmarkCommand::QUERY_USER_ACCESS) &&
(cmdId != FwmarkCommand::SET_COUNTERSET) &&
@@ -55,13 +45,20 @@
} // namespace
bool FwmarkClient::shouldSetFwmark(int family) {
- if (isOverriddenBy(ANDROID_NO_USE_FWMARK_CLIENT)) return false;
- return FwmarkCommand::isSupportedFamily(family);
-}
+ // Checking whether family is supported before checking whether this can be
+ // disabled. Because there are existing processes using AF_LOCAL socket but it
+ // doesn't have permission to call geteuid(). Reference b/135422468.
+ if (!FwmarkCommand::isSupportedFamily(family)) {
+ return false;
+ }
-bool FwmarkClient::shouldReportConnectComplete(int family) {
- if (isOverriddenBy(ANDROID_NO_USE_FWMARK_CLIENT)) return false;
- return shouldSetFwmark(family);
+ // Permit processes running as root to disable marking. This is required, for
+ // example, to run the kernel networking tests.
+ if (getenv(ANDROID_NO_USE_FWMARK_CLIENT) && geteuid() == 0) {
+ return false;
+ }
+
+ return true;
}
FwmarkClient::FwmarkClient() : mChannel(-1) {
diff --git a/client/FwmarkClient.h b/client/FwmarkClient.h
index 31fcbc4..c51688f 100644
--- a/client/FwmarkClient.h
+++ b/client/FwmarkClient.h
@@ -28,10 +28,6 @@
// its SO_MARK set.
static bool shouldSetFwmark(int family);
- // Returns true if an additional call should be made after ON_CONNECT calls, to log extra
- // information like latency and source IP.
- static bool shouldReportConnectComplete(int family);
-
FwmarkClient();
~FwmarkClient();
diff --git a/client/NetdClient.cpp b/client/NetdClient.cpp
index df1ece6..0105a04 100644
--- a/client/NetdClient.cpp
+++ b/client/NetdClient.cpp
@@ -22,6 +22,7 @@
#include <resolv.h>
#include <stdlib.h>
#include <sys/socket.h>
+#include <sys/system_properties.h>
#include <sys/un.h>
#include <unistd.h>
@@ -29,6 +30,7 @@
#include <string>
#include <vector>
+#include <DnsProxydProtocol.h> // NETID_USE_LOCAL_NAMESERVERS
#include <android-base/parseint.h>
#include <android-base/unique_fd.h>
@@ -49,21 +51,49 @@
// Keep this in sync with CMD_BUF_SIZE in FrameworkListener.cpp.
constexpr size_t MAX_CMD_SIZE = 4096;
+// Whether sendto(), sendmsg(), sendmmsg() in libc are shimmed or not. This property is evaluated at
+// process start time and cannot change at runtime on a given device.
+constexpr char PROPERTY_REDIRECT_SOCKET_CALLS[] = "ro.vendor.redirect_socket_calls";
+// Whether some shimmed functions dispatch FwmarkCommand or not. The property can be changed by
+// System Server at runtime. Note: accept4(), socket(), connect() are always shimmed.
+constexpr char PROPERTY_REDIRECT_SOCKET_CALLS_HOOKED[] = "net.redirect_socket_calls.hooked";
std::atomic_uint netIdForProcess(NETID_UNSET);
std::atomic_uint netIdForResolv(NETID_UNSET);
+std::atomic_bool allowNetworkingForProcess(true);
typedef int (*Accept4FunctionType)(int, sockaddr*, socklen_t*, int);
typedef int (*ConnectFunctionType)(int, const sockaddr*, socklen_t);
typedef int (*SocketFunctionType)(int, int, int);
typedef unsigned (*NetIdForResolvFunctionType)(unsigned);
typedef int (*DnsOpenProxyType)();
+typedef int (*SendmmsgFunctionType)(int, const mmsghdr*, unsigned int, int);
+typedef ssize_t (*SendmsgFunctionType)(int, const msghdr*, unsigned int);
+typedef int (*SendtoFunctionType)(int, const void*, size_t, int, const sockaddr*, socklen_t);
// These variables are only modified at startup (when libc.so is loaded) and never afterwards, so
// it's okay that they are read later at runtime without a lock.
Accept4FunctionType libcAccept4 = nullptr;
ConnectFunctionType libcConnect = nullptr;
SocketFunctionType libcSocket = nullptr;
+SendmmsgFunctionType libcSendmmsg = nullptr;
+SendmsgFunctionType libcSendmsg = nullptr;
+SendtoFunctionType libcSendto = nullptr;
+
+static bool propertyValueIsTrue(const char* prop_name) {
+ char prop_value[PROP_VALUE_MAX] = {0};
+ if (__system_property_get(prop_name, prop_value) > 0) {
+ if (strcmp(prop_value, "true") == 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static bool redirectSocketCallsIsTrue() {
+ static bool cached_result = propertyValueIsTrue(PROPERTY_REDIRECT_SOCKET_CALLS);
+ return cached_result;
+}
int checkSocket(int socketFd) {
if (socketFd < 0) {
@@ -120,7 +150,15 @@
const bool shouldSetFwmark = shouldMarkSocket(sockfd, addr);
if (shouldSetFwmark) {
FwmarkCommand command = {FwmarkCommand::ON_CONNECT, 0, 0, 0};
- if (int error = FwmarkClient().send(&command, sockfd, nullptr)) {
+ int error;
+ if (redirectSocketCallsIsTrue()) {
+ FwmarkConnectInfo connectInfo(0, 0, addr);
+ error = FwmarkClient().send(&command, sockfd, &connectInfo);
+ } else {
+ error = FwmarkClient().send(&command, sockfd, nullptr);
+ }
+
+ if (error) {
errno = -error;
return -1;
}
@@ -130,9 +168,9 @@
const int ret = libcConnect(sockfd, addr, addrlen);
// Save errno so it isn't clobbered by sending ON_CONNECT_COMPLETE
const int connectErrno = errno;
- const unsigned latencyMs = lround(s.timeTaken());
+ const auto latencyMs = static_cast<unsigned>(s.timeTakenUs() / 1000);
// Send an ON_CONNECT_COMPLETE command that includes sockaddr and connect latency for reporting
- if (shouldSetFwmark && FwmarkClient::shouldReportConnectComplete(addr->sa_family)) {
+ if (shouldSetFwmark) {
FwmarkConnectInfo connectInfo(ret == 0 ? 0 : connectErrno, latencyMs, addr);
// TODO: get the netId from the socket mark once we have continuous benchmark runs
FwmarkCommand command = {FwmarkCommand::ON_CONNECT_COMPLETE, /* netId (ignored) */ 0,
@@ -145,6 +183,11 @@
}
int netdClientSocket(int domain, int type, int protocol) {
+ // Block creating AF_INET/AF_INET6 socket if networking is not allowed.
+ if (FwmarkCommand::isSupportedFamily(domain) && !allowNetworkingForProcess.load()) {
+ errno = EPERM;
+ return -1;
+ }
int socketFd = libcSocket(domain, type, protocol);
if (socketFd == -1) {
return -1;
@@ -158,6 +201,48 @@
return socketFd;
}
+int netdClientSendmmsg(int sockfd, const mmsghdr* msgs, unsigned int msgcount, int flags) {
+ if (propertyValueIsTrue(PROPERTY_REDIRECT_SOCKET_CALLS_HOOKED) && !checkSocket(sockfd)) {
+ const sockaddr* addr = nullptr;
+ if ((msgcount > 0) && (msgs != nullptr) && (msgs[0].msg_hdr.msg_name != nullptr)) {
+ addr = reinterpret_cast<const sockaddr*>(msgs[0].msg_hdr.msg_name);
+ if ((addr != nullptr) && (FwmarkCommand::isSupportedFamily(addr->sa_family))) {
+ FwmarkConnectInfo sendmmsgInfo(0, 0, addr);
+ FwmarkCommand command = {FwmarkCommand::ON_SENDMMSG, 0, 0, 0};
+ FwmarkClient().send(&command, sockfd, &sendmmsgInfo);
+ }
+ }
+ }
+ return libcSendmmsg(sockfd, msgs, msgcount, flags);
+}
+
+ssize_t netdClientSendmsg(int sockfd, const msghdr* msg, unsigned int flags) {
+ if (propertyValueIsTrue(PROPERTY_REDIRECT_SOCKET_CALLS_HOOKED) && !checkSocket(sockfd)) {
+ const sockaddr* addr = nullptr;
+ if ((msg != nullptr) && (msg->msg_name != nullptr)) {
+ addr = reinterpret_cast<const sockaddr*>(msg->msg_name);
+ if ((addr != nullptr) && (FwmarkCommand::isSupportedFamily(addr->sa_family))) {
+ FwmarkConnectInfo sendmsgInfo(0, 0, addr);
+ FwmarkCommand command = {FwmarkCommand::ON_SENDMSG, 0, 0, 0};
+ FwmarkClient().send(&command, sockfd, &sendmsgInfo);
+ }
+ }
+ }
+ return libcSendmsg(sockfd, msg, flags);
+}
+
+int netdClientSendto(int sockfd, const void* buf, size_t bufsize, int flags, const sockaddr* addr,
+ socklen_t addrlen) {
+ if (propertyValueIsTrue(PROPERTY_REDIRECT_SOCKET_CALLS_HOOKED) && !checkSocket(sockfd)) {
+ if ((addr != nullptr) && (FwmarkCommand::isSupportedFamily(addr->sa_family))) {
+ FwmarkConnectInfo sendtoInfo(0, 0, addr);
+ FwmarkCommand command = {FwmarkCommand::ON_SENDTO, 0, 0, 0};
+ FwmarkClient().send(&command, sockfd, &sendtoInfo);
+ }
+ }
+ return libcSendto(sockfd, buf, bufsize, flags, addr, addrlen);
+}
+
unsigned getNetworkForResolv(unsigned netId) {
if (netId != NETID_UNSET) {
return netId;
@@ -206,6 +291,13 @@
return -1;
}
+ // If networking is not allowed, dns_open_proxy should just fail here.
+ // Then eventually, the DNS related functions in local mode will get
+ // EPERM while creating socket.
+ if (!allowNetworkingForProcess.load()) {
+ errno = EPERM;
+ return -1;
+ }
const auto socketFunc = libcSocket ? libcSocket : socket;
int s = socketFunc(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
if (s == -1) {
@@ -304,34 +396,52 @@
} // namespace
-#define CHECK_SOCKET_IS_MARKABLE(sock) \
- do { \
- int err; \
- if ((err = checkSocket(sock)) != 0) { \
- return err; \
- } \
- } while (false);
+#define CHECK_SOCKET_IS_MARKABLE(sock) \
+ do { \
+ int err = checkSocket(sock); \
+ if (err) return err; \
+ } while (false)
+
+#define HOOK_ON_FUNC(remoteFunc, nativeFunc, localFunc) \
+ do { \
+ if ((remoteFunc) && *(remoteFunc)) { \
+ (nativeFunc) = *(remoteFunc); \
+ *(remoteFunc) = (localFunc); \
+ } \
+ } while (false)
// accept() just calls accept4(..., 0), so there's no need to handle accept() separately.
extern "C" void netdClientInitAccept4(Accept4FunctionType* function) {
- if (function && *function) {
- libcAccept4 = *function;
- *function = netdClientAccept4;
- }
+ HOOK_ON_FUNC(function, libcAccept4, netdClientAccept4);
}
extern "C" void netdClientInitConnect(ConnectFunctionType* function) {
- if (function && *function) {
- libcConnect = *function;
- *function = netdClientConnect;
- }
+ HOOK_ON_FUNC(function, libcConnect, netdClientConnect);
}
extern "C" void netdClientInitSocket(SocketFunctionType* function) {
- if (function && *function) {
- libcSocket = *function;
- *function = netdClientSocket;
+ HOOK_ON_FUNC(function, libcSocket, netdClientSocket);
+}
+
+extern "C" void netdClientInitSendmmsg(SendmmsgFunctionType* function) {
+ if (!propertyValueIsTrue(PROPERTY_REDIRECT_SOCKET_CALLS)) {
+ return;
}
+ HOOK_ON_FUNC(function, libcSendmmsg, netdClientSendmmsg);
+}
+
+extern "C" void netdClientInitSendmsg(SendmsgFunctionType* function) {
+ if (!propertyValueIsTrue(PROPERTY_REDIRECT_SOCKET_CALLS)) {
+ return;
+ }
+ HOOK_ON_FUNC(function, libcSendmsg, netdClientSendmsg);
+}
+
+extern "C" void netdClientInitSendto(SendtoFunctionType* function) {
+ if (!propertyValueIsTrue(PROPERTY_REDIRECT_SOCKET_CALLS)) {
+ return;
+ }
+ HOOK_ON_FUNC(function, libcSendto, netdClientSendto);
}
extern "C" void netdClientInitNetIdForResolv(NetIdForResolvFunctionType* function) {
@@ -378,9 +488,7 @@
}
extern "C" int protectFromVpn(int socketFd) {
- if (socketFd < 0) {
- return -EBADF;
- }
+ CHECK_SOCKET_IS_MARKABLE(socketFd);
FwmarkCommand command = {FwmarkCommand::PROTECT_FROM_VPN, 0, 0, 0};
return FwmarkClient().send(&command, socketFd, nullptr);
}
@@ -498,6 +606,10 @@
close(fd);
}
+extern "C" void setAllowNetworkingForProcess(bool allowNetworking) {
+ allowNetworkingForProcess.store(allowNetworking);
+}
+
extern "C" int getNetworkForDns(unsigned* dnsNetId) {
if (dnsNetId == nullptr) return -EFAULT;
int fd = dns_open_proxy();
diff --git a/client/NetdClientTest.cpp b/client/NetdClientTest.cpp
index 6f2fc09..20f601a 100644
--- a/client/NetdClientTest.cpp
+++ b/client/NetdClientTest.cpp
@@ -31,14 +31,25 @@
// Keep in sync with FrameworkListener.cpp (500, "Command not recognized")
constexpr char NOT_SUPPORT_MSG[] = "500 Command not recognized";
+int openDnsProxyFuncStub() {
+ return -1;
+};
+
+typedef int (*DnsOpenProxyType)();
+typedef int (*SocketFunctionType)(int, int, int);
+
+DnsOpenProxyType openDnsProxyFuncPtr = openDnsProxyFuncStub;
+SocketFunctionType socketFuncPtr = socket;
+
void serverLoop(int dnsProxyFd) {
while (true) {
pollfd fds[1] = {{.fd = dnsProxyFd, .events = POLLIN}};
enum { SERVERFD = 0 };
- const int s = TEMP_FAILURE_RETRY(poll(fds, std::size(fds), -1));
- if (s <= 0) break;
+ int poll_result = TEMP_FAILURE_RETRY(poll(fds, std::size(fds), -1));
+ ASSERT_GT(poll_result, 0);
+ if (fds[SERVERFD].revents & POLLERR) return;
if (fds[SERVERFD].revents & POLLIN) {
char buf[4096];
TEMP_FAILURE_RETRY(read(fds[SERVERFD].fd, &buf, sizeof(buf)));
@@ -48,6 +59,35 @@
}
}
+void expectAllowNetworkingForProcess() {
+ // netdClientSocket
+ android::base::unique_fd ipv4(socketFuncPtr(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0)),
+ ipv6(socketFuncPtr(AF_INET6, SOCK_STREAM | SOCK_CLOEXEC, 0));
+ EXPECT_LE(3, ipv4);
+ EXPECT_LE(3, ipv6);
+
+ // dns_open_proxy
+ android::base::unique_fd dnsproxydSocket(openDnsProxyFuncPtr());
+ EXPECT_LE(3, dnsproxydSocket);
+}
+
+void expectNotAllowNetworkingForProcess() {
+ // netdClientSocket
+ android::base::unique_fd unixSocket(socketFuncPtr(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0));
+ EXPECT_LE(3, unixSocket);
+ android::base::unique_fd ipv4(socketFuncPtr(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0));
+ EXPECT_EQ(-1, ipv4);
+ EXPECT_EQ(errno, EPERM);
+ android::base::unique_fd ipv6(socketFuncPtr(AF_INET6, SOCK_STREAM | SOCK_CLOEXEC, 0));
+ EXPECT_EQ(-1, ipv6);
+ EXPECT_EQ(errno, EPERM);
+
+ // dns_open_proxy
+ android::base::unique_fd dnsproxydSocket(openDnsProxyFuncPtr());
+ EXPECT_EQ(-1, dnsproxydSocket);
+ EXPECT_EQ(errno, EPERM);
+}
+
} // namespace
TEST(NetdClientTest, getNetworkForDnsInternal) {
@@ -73,3 +113,34 @@
unsigned* testNull = nullptr;
EXPECT_EQ(-EFAULT, getNetworkForDns(testNull));
}
+
+TEST(NetdClientTest, protectFromVpnBadFd) {
+ EXPECT_EQ(-EBADF, protectFromVpn(-1));
+}
+
+TEST(NetdClientTest, protectFromVpnUnixStream) {
+ int s = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
+ ASSERT_GE(s, 3);
+ EXPECT_EQ(-EAFNOSUPPORT, protectFromVpn(s));
+ close(s);
+}
+
+TEST(NetdClientTest, protectFromVpnTcp6) {
+ int s = socket(AF_INET6, SOCK_STREAM | SOCK_CLOEXEC, 0);
+ ASSERT_GE(s, 3);
+ EXPECT_EQ(0, protectFromVpn(s));
+ close(s);
+}
+
+TEST(NetdClientTest, setAllowNetworkingForProcess) {
+ netdClientInitDnsOpenProxy(&openDnsProxyFuncPtr);
+ netdClientInitSocket(&socketFuncPtr);
+ // At the beginning, we should be able to use socket since the default setting is allowing.
+ expectAllowNetworkingForProcess();
+ // Disable
+ setAllowNetworkingForProcess(false);
+ expectNotAllowNetworkingForProcess();
+ // Reset
+ setAllowNetworkingForProcess(true);
+ expectAllowNetworkingForProcess();
+}
diff --git a/client/netdclient_priv.h b/client/netdclient_priv.h
index 52952c5..c2fbc53 100644
--- a/client/netdclient_priv.h
+++ b/client/netdclient_priv.h
@@ -19,4 +19,9 @@
int getNetworkForDnsInternal(int fd, unsigned* dnsNetId);
+extern "C" {
+void netdClientInitDnsOpenProxy(int (**DnsOpenProxyType)());
+void netdClientInitSocket(int (**SocketFunctionType)(int, int, int));
+}
+
#endif // NETD_CLIENT_NETD_CLIENT_PRIV_H
diff --git a/include/FwmarkCommand.h b/include/FwmarkCommand.h
index a78542a..83e62d3 100644
--- a/include/FwmarkCommand.h
+++ b/include/FwmarkCommand.h
@@ -31,7 +31,7 @@
sockaddr_in6 sin6;
} addr;
- FwmarkConnectInfo() {}
+ FwmarkConnectInfo() : error(0), latencyMs(0) {}
FwmarkConnectInfo(const int connectErrno, const unsigned latency, const sockaddr* saddr) {
error = connectErrno;
@@ -54,7 +54,7 @@
// TODO: rework this struct into a more flexible data structure such as union or
// a hierarchy class.
struct FwmarkCommand {
- enum {
+ enum CmdId {
ON_ACCEPT,
ON_CONNECT,
SELECT_NETWORK,
@@ -69,6 +69,9 @@
// command.
SET_COUNTERSET,
DELETE_TAGDATA,
+ ON_SENDMMSG,
+ ON_SENDMSG,
+ ON_SENDTO,
} cmdId;
unsigned netId; // used only in the SELECT_NETWORK command; ignored otherwise.
uid_t uid; // used in the SELECT_FOR_USER, QUERY_USER_ACCESS, TAG_SOCKET,
diff --git a/include/NetdClient.h b/include/NetdClient.h
index 0cfc187..cc835ed 100644
--- a/include/NetdClient.h
+++ b/include/NetdClient.h
@@ -21,8 +21,6 @@
#include <sys/cdefs.h>
#include <sys/types.h>
-#define NETID_USE_LOCAL_NAMESERVERS 0x80000000
-
__BEGIN_DECLS
// All functions below that return an int return 0 on success or a negative errno value on failure.
@@ -42,6 +40,8 @@
int queryUserAccess(uid_t uid, unsigned netId);
+void setAllowNetworkingForProcess(bool allowNetworking);
+
int tagSocket(int socketFd, uint32_t tag, uid_t uid);
int untagSocket(int socketFd);
diff --git a/include/BinderUtil.h b/include/binder_utils/BinderUtil.h
similarity index 96%
rename from include/BinderUtil.h
rename to include/binder_utils/BinderUtil.h
index 9333dcd..6b41465 100644
--- a/include/BinderUtil.h
+++ b/include/binder_utils/BinderUtil.h
@@ -14,8 +14,11 @@
* limitations under the License.
*/
-#ifndef NETD_INCLUDE_BINDER_UTIL_H
-#define NETD_INCLUDE_BINDER_UTIL_H
+#pragma once
+
+#include <android-base/stringprintf.h>
+#include <json/value.h>
+#include <json/writer.h>
#ifdef ANDROID_BINDER_STATUS_H
#define IS_BINDER_OK(__ex__) (__ex__ == ::android::binder::Status::EX_NONE)
@@ -104,5 +107,3 @@
::android::base::StringPrintf(" <%.2fms>", logTransaction["duration_ms"].asFloat()));
logFn(output);
}
-
-#endif /* NETD_INCLUDE_BINDER_UTIL_H */
diff --git a/include/NetdPermissions.h b/include/binder_utils/NetdPermissions.h
similarity index 100%
rename from include/NetdPermissions.h
rename to include/binder_utils/NetdPermissions.h
diff --git a/libnetdbpf/Android.bp b/libnetdbpf/Android.bp
index bd25535..72e0fec 100644
--- a/libnetdbpf/Android.bp
+++ b/libnetdbpf/Android.bp
@@ -26,15 +26,18 @@
"libbpf_android",
"liblog",
"libnetdutils",
- "libutils",
],
export_include_dirs: ["include"],
defaults: ["netd_defaults"],
+ sanitize: {
+ cfi: true,
+ },
}
cc_test {
name: "libnetdbpf_test",
test_suites: ["device-tests"],
+ require_root: true,
srcs: [
"BpfNetworkStatsTest.cpp",
],
diff --git a/libnetdbpf/BpfNetworkStats.cpp b/libnetdbpf/BpfNetworkStats.cpp
index fe86cc1..a77f6f9 100644
--- a/libnetdbpf/BpfNetworkStats.cpp
+++ b/libnetdbpf/BpfNetworkStats.cpp
@@ -38,29 +38,27 @@
namespace android {
namespace bpf {
-using netdutils::Status;
+using base::Result;
// The target map for stats reading should be the inactive map, which is oppsite
// from the config value.
static constexpr char const* STATS_MAP_PATH[] = {STATS_MAP_B_PATH, STATS_MAP_A_PATH};
-static constexpr uint32_t BPF_OPEN_FLAGS = BPF_F_RDONLY;
-
int bpfGetUidStatsInternal(uid_t uid, Stats* stats,
const BpfMap<uint32_t, StatsValue>& appUidStatsMap) {
auto statsEntry = appUidStatsMap.readValue(uid);
- if (isOk(statsEntry)) {
+ if (statsEntry.ok()) {
stats->rxPackets = statsEntry.value().rxPackets;
stats->txPackets = statsEntry.value().txPackets;
stats->rxBytes = statsEntry.value().rxBytes;
stats->txBytes = statsEntry.value().txBytes;
}
- return statsEntry.status().code() == ENOENT ? 0 : -statsEntry.status().code();
+ return (statsEntry.ok() || statsEntry.error().code() == ENOENT) ? 0
+ : -statsEntry.error().code();
}
int bpfGetUidStats(uid_t uid, Stats* stats) {
- BpfMap<uint32_t, StatsValue> appUidStatsMap(
- mapRetrieve(APP_UID_STATS_MAP_PATH, BPF_OPEN_FLAGS));
+ BpfMapRO<uint32_t, StatsValue> appUidStatsMap(APP_UID_STATS_MAP_PATH);
if (!appUidStatsMap.isValid()) {
int ret = -errno;
@@ -76,37 +74,40 @@
int64_t unknownIfaceBytesTotal = 0;
stats->tcpRxPackets = -1;
stats->tcpTxPackets = -1;
- const auto processIfaceStats = [iface, stats, &ifaceNameMap, &unknownIfaceBytesTotal]
- (const uint32_t& key,
- const BpfMap<uint32_t, StatsValue>& ifaceStatsMap) {
+ const auto processIfaceStats =
+ [iface, stats, &ifaceNameMap, &unknownIfaceBytesTotal](
+ const uint32_t& key,
+ const BpfMap<uint32_t, StatsValue>& ifaceStatsMap) -> Result<void> {
char ifname[IFNAMSIZ];
if (getIfaceNameFromMap(ifaceNameMap, ifaceStatsMap, key, ifname, key,
&unknownIfaceBytesTotal)) {
- return netdutils::status::ok;
+ return Result<void>();
}
if (!iface || !strcmp(iface, ifname)) {
- StatsValue statsEntry;
- ASSIGN_OR_RETURN(statsEntry, ifaceStatsMap.readValue(key));
- stats->rxPackets += statsEntry.rxPackets;
- stats->txPackets += statsEntry.txPackets;
- stats->rxBytes += statsEntry.rxBytes;
- stats->txBytes += statsEntry.txBytes;
+ Result<StatsValue> statsEntry = ifaceStatsMap.readValue(key);
+ if (!statsEntry.ok()) {
+ return statsEntry.error();
+ }
+ stats->rxPackets += statsEntry.value().rxPackets;
+ stats->txPackets += statsEntry.value().txPackets;
+ stats->rxBytes += statsEntry.value().rxBytes;
+ stats->txBytes += statsEntry.value().txBytes;
}
- return netdutils::status::ok;
+ return Result<void>();
};
- return -ifaceStatsMap.iterate(processIfaceStats).code();
+ auto res = ifaceStatsMap.iterate(processIfaceStats);
+ return res.ok() ? 0 : -res.error().code();
}
int bpfGetIfaceStats(const char* iface, Stats* stats) {
- BpfMap<uint32_t, StatsValue> ifaceStatsMap(mapRetrieve(IFACE_STATS_MAP_PATH, BPF_OPEN_FLAGS));
+ BpfMapRO<uint32_t, StatsValue> ifaceStatsMap(IFACE_STATS_MAP_PATH);
int ret;
if (!ifaceStatsMap.isValid()) {
ret = -errno;
ALOGE("get ifaceStats map fd failed: %s", strerror(errno));
return ret;
}
- BpfMap<uint32_t, IfaceValue> ifaceIndexNameMap(
- mapRetrieve(IFACE_INDEX_NAME_MAP_PATH, BPF_OPEN_FLAGS));
+ BpfMapRO<uint32_t, IfaceValue> ifaceIndexNameMap(IFACE_INDEX_NAME_MAP_PATH);
if (!ifaceIndexNameMap.isValid()) {
ret = -errno;
ALOGE("get ifaceIndexName map fd failed: %s", strerror(errno));
@@ -134,37 +135,39 @@
int limitUid, const BpfMap<StatsKey, StatsValue>& statsMap,
const BpfMap<uint32_t, IfaceValue>& ifaceMap) {
int64_t unknownIfaceBytesTotal = 0;
- const auto processDetailUidStats = [lines, &limitIfaces, &limitTag, &limitUid,
- &unknownIfaceBytesTotal,
- &ifaceMap](const StatsKey& key,
- const BpfMap<StatsKey, StatsValue>& statsMap) {
+ const auto processDetailUidStats =
+ [lines, &limitIfaces, &limitTag, &limitUid, &unknownIfaceBytesTotal, &ifaceMap](
+ const StatsKey& key,
+ const BpfMap<StatsKey, StatsValue>& statsMap) -> Result<void> {
char ifname[IFNAMSIZ];
if (getIfaceNameFromMap(ifaceMap, statsMap, key.ifaceIndex, ifname, key,
&unknownIfaceBytesTotal)) {
- return netdutils::status::ok;
+ return Result<void>();
}
std::string ifnameStr(ifname);
if (limitIfaces.size() > 0 &&
std::find(limitIfaces.begin(), limitIfaces.end(), ifnameStr) == limitIfaces.end()) {
// Nothing matched; skip this line.
- return netdutils::status::ok;
+ return Result<void>();
}
if (limitTag != TAG_ALL && uint32_t(limitTag) != key.tag) {
- return netdutils::status::ok;
+ return Result<void>();
}
if (limitUid != UID_ALL && uint32_t(limitUid) != key.uid) {
- return netdutils::status::ok;
+ return Result<void>();
}
- StatsValue statsEntry;
- ASSIGN_OR_RETURN(statsEntry, statsMap.readValue(key));
- lines->push_back(populateStatsEntry(key, statsEntry, ifname));
- return netdutils::status::ok;
+ Result<StatsValue> statsEntry = statsMap.readValue(key);
+ if (!statsEntry.ok()) {
+ return base::ResultError(statsEntry.error().message(), statsEntry.error().code());
+ }
+ lines->push_back(populateStatsEntry(key, statsEntry.value(), ifname));
+ return Result<void>();
};
- Status res = statsMap.iterate(processDetailUidStats);
- if (!isOk(res)) {
+ Result<void> res = statsMap.iterate(processDetailUidStats);
+ if (!res.ok()) {
ALOGE("failed to iterate per uid Stats map for detail traffic stats: %s",
- strerror(res.code()));
- return -res.code();
+ strerror(res.error().code()));
+ return -res.error().code();
}
// Since eBPF use hash map to record stats, network stats collected from
@@ -183,28 +186,27 @@
int parseBpfNetworkStatsDetail(std::vector<stats_line>* lines,
const std::vector<std::string>& limitIfaces, int limitTag,
int limitUid) {
- BpfMap<uint32_t, IfaceValue> ifaceIndexNameMap(
- mapRetrieve(IFACE_INDEX_NAME_MAP_PATH, BPF_OPEN_FLAGS));
+ BpfMapRO<uint32_t, IfaceValue> ifaceIndexNameMap(IFACE_INDEX_NAME_MAP_PATH);
if (!ifaceIndexNameMap.isValid()) {
int ret = -errno;
ALOGE("get ifaceIndexName map fd failed: %s", strerror(errno));
return ret;
}
- BpfMap<uint32_t, uint8_t> configurationMap(mapRetrieve(CONFIGURATION_MAP_PATH, 0));
+ BpfMapRO<uint32_t, uint8_t> configurationMap(CONFIGURATION_MAP_PATH);
if (!configurationMap.isValid()) {
int ret = -errno;
ALOGE("get configuration map fd failed: %s", strerror(errno));
return ret;
}
auto configuration = configurationMap.readValue(CURRENT_STATS_MAP_CONFIGURATION_KEY);
- if (!isOk(configuration)) {
+ if (!configuration.ok()) {
ALOGE("Cannot read the old configuration from map: %s",
- configuration.status().msg().c_str());
- return -configuration.status().code();
+ configuration.error().message().c_str());
+ return -configuration.error().code();
}
const char* statsMapPath = STATS_MAP_PATH[configuration.value()];
- BpfMap<StatsKey, StatsValue> statsMap(mapRetrieve(statsMapPath, 0));
+ BpfMap<StatsKey, StatsValue> statsMap(statsMapPath);
if (!statsMap.isValid()) {
int ret = -errno;
ALOGE("get stats map fd failed: %s, path: %s", strerror(errno), statsMapPath);
@@ -220,10 +222,10 @@
return ret;
}
- Status res = statsMap.clear();
- if (!isOk(res)) {
- ALOGE("Clean up current stats map failed: %s", strerror(res.code()));
- return -res.code();
+ Result<void> res = statsMap.clear();
+ if (!res.ok()) {
+ ALOGE("Clean up current stats map failed: %s", strerror(res.error().code()));
+ return -res.error().code();
}
return 0;
@@ -238,18 +240,21 @@
const BpfMap<uint32_t, StatsValue>&) {
char ifname[IFNAMSIZ];
if (getIfaceNameFromMap(ifaceMap, statsMap, key, ifname, key, &unknownIfaceBytesTotal)) {
- return netdutils::status::ok;
+ return Result<void>();
}
StatsKey fakeKey = {
- .uid = (uint32_t)UID_ALL, .counterSet = (uint32_t)SET_ALL, .tag = (uint32_t)TAG_NONE};
+ .uid = (uint32_t)UID_ALL,
+ .tag = (uint32_t)TAG_NONE,
+ .counterSet = (uint32_t)SET_ALL,
+ };
lines->push_back(populateStatsEntry(fakeKey, value, ifname));
- return netdutils::status::ok;
+ return Result<void>();
};
- Status res = statsMap.iterateWithValue(processDetailIfaceStats);
- if (!isOk(res)) {
+ Result<void> res = statsMap.iterateWithValue(processDetailIfaceStats);
+ if (!res.ok()) {
ALOGE("failed to iterate per uid Stats map for detail traffic stats: %s",
- strerror(res.code()));
- return -res.code();
+ strerror(res.error().code()));
+ return -res.error().code();
}
groupNetworkStats(lines);
@@ -258,15 +263,14 @@
int parseBpfNetworkStatsDev(std::vector<stats_line>* lines) {
int ret = 0;
- BpfMap<uint32_t, IfaceValue> ifaceIndexNameMap(
- mapRetrieve(IFACE_INDEX_NAME_MAP_PATH, BPF_OPEN_FLAGS));
+ BpfMapRO<uint32_t, IfaceValue> ifaceIndexNameMap(IFACE_INDEX_NAME_MAP_PATH);
if (!ifaceIndexNameMap.isValid()) {
ret = -errno;
ALOGE("get ifaceIndexName map fd failed: %s", strerror(errno));
return ret;
}
- BpfMap<uint32_t, StatsValue> ifaceStatsMap(mapRetrieve(IFACE_STATS_MAP_PATH, BPF_OPEN_FLAGS));
+ BpfMapRO<uint32_t, StatsValue> ifaceStatsMap(IFACE_STATS_MAP_PATH);
if (!ifaceStatsMap.isValid()) {
ret = -errno;
ALOGE("get ifaceStats map fd failed: %s", strerror(errno));
@@ -321,6 +325,8 @@
}
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 8153127..a9581ce 100644
--- a/libnetdbpf/BpfNetworkStatsTest.cpp
+++ b/libnetdbpf/BpfNetworkStatsTest.cpp
@@ -43,6 +43,7 @@
namespace android {
namespace bpf {
+using base::Result;
using base::unique_fd;
constexpr int TEST_MAP_SIZE = 10;
@@ -70,7 +71,7 @@
class BpfNetworkStatsHelperTest : public testing::Test {
protected:
BpfNetworkStatsHelperTest() {}
- BpfMap<uint64_t, UidTag> mFakeCookieTagMap;
+ BpfMap<uint64_t, UidTagValue> mFakeCookieTagMap;
BpfMap<uint32_t, StatsValue> mFakeAppUidStatsMap;
BpfMap<StatsKey, StatsValue> mFakeStatsMap;
BpfMap<uint32_t, IfaceValue> mFakeIfaceIndexNameMap;
@@ -80,31 +81,25 @@
SKIP_IF_BPF_NOT_SUPPORTED;
ASSERT_EQ(0, setrlimitForTest());
- mFakeCookieTagMap = BpfMap<uint64_t, UidTag>(createMap(
- BPF_MAP_TYPE_HASH, sizeof(uint64_t), sizeof(struct UidTag), TEST_MAP_SIZE, 0));
+ mFakeCookieTagMap = BpfMap<uint64_t, UidTagValue>(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, 0);
ASSERT_LE(0, mFakeCookieTagMap.getMap());
- mFakeAppUidStatsMap = BpfMap<uint32_t, StatsValue>(createMap(
- BPF_MAP_TYPE_HASH, sizeof(uint32_t), sizeof(struct StatsValue), TEST_MAP_SIZE, 0));
+ mFakeAppUidStatsMap = BpfMap<uint32_t, StatsValue>(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, 0);
ASSERT_LE(0, mFakeAppUidStatsMap.getMap());
- mFakeStatsMap = BpfMap<StatsKey, StatsValue>(
- createMap(BPF_MAP_TYPE_HASH, sizeof(struct StatsKey), sizeof(struct StatsValue),
- TEST_MAP_SIZE, 0));
+ mFakeStatsMap = BpfMap<StatsKey, StatsValue>(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, 0);
ASSERT_LE(0, mFakeStatsMap.getMap());
- mFakeIfaceIndexNameMap = BpfMap<uint32_t, IfaceValue>(
- createMap(BPF_MAP_TYPE_HASH, sizeof(uint32_t), sizeof(IfaceValue), TEST_MAP_SIZE, 0));
+ mFakeIfaceIndexNameMap = BpfMap<uint32_t, IfaceValue>(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, 0);
ASSERT_LE(0, mFakeIfaceIndexNameMap.getMap());
- mFakeIfaceStatsMap = BpfMap<uint32_t, StatsValue>(createMap(
- BPF_MAP_TYPE_HASH, sizeof(uint32_t), sizeof(struct StatsValue), TEST_MAP_SIZE, 0));
+ mFakeIfaceStatsMap = BpfMap<uint32_t, StatsValue>(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, 0);
ASSERT_LE(0, mFakeIfaceStatsMap.getMap());
}
void expectUidTag(uint64_t cookie, uid_t uid, uint32_t tag) {
auto tagResult = mFakeCookieTagMap.readValue(cookie);
- EXPECT_TRUE(isOk(tagResult));
+ EXPECT_RESULT_OK(tagResult);
EXPECT_EQ(uid, tagResult.value().uid);
EXPECT_EQ(tag, tagResult.value().tag);
}
@@ -113,13 +108,13 @@
StatsValue value, BpfMap<StatsKey, StatsValue>& map) {
StatsKey key = {
.uid = (uint32_t)uid, .tag = tag, .counterSet = counterSet, .ifaceIndex = ifaceIndex};
- EXPECT_TRUE(isOk(map.writeValue(key, value, BPF_ANY)));
+ EXPECT_RESULT_OK(map.writeValue(key, value, BPF_ANY));
}
void updateIfaceMap(const char* ifaceName, uint32_t ifaceIndex) {
IfaceValue iface;
strlcpy(iface.name, ifaceName, IFNAMSIZ);
- EXPECT_TRUE(isOk(mFakeIfaceIndexNameMap.writeValue(ifaceIndex, iface, BPF_ANY)));
+ EXPECT_RESULT_OK(mFakeIfaceIndexNameMap.writeValue(ifaceIndex, iface, BPF_ANY));
}
void expectStatsEqual(const StatsValue& target, const Stats& result) {
@@ -149,29 +144,29 @@
for (int i = 0; i < 5; i++) {
uint64_t cookie = i + 1;
- struct UidTag tag = {.uid = TEST_UID1, .tag = TEST_TAG};
- EXPECT_TRUE(isOk(mFakeCookieTagMap.writeValue(cookie, tag, BPF_ANY)));
+ UidTagValue tag = {.uid = TEST_UID1, .tag = TEST_TAG};
+ EXPECT_RESULT_OK(mFakeCookieTagMap.writeValue(cookie, tag, BPF_ANY));
}
uint64_t curCookie = 0;
auto nextCookie = mFakeCookieTagMap.getNextKey(curCookie);
- EXPECT_TRUE(isOk(nextCookie));
+ EXPECT_RESULT_OK(nextCookie);
uint64_t headOfMap = nextCookie.value();
curCookie = nextCookie.value();
// Find the second entry in the map, then immediately delete it.
nextCookie = mFakeCookieTagMap.getNextKey(curCookie);
- EXPECT_TRUE(isOk(nextCookie));
- EXPECT_TRUE(isOk(mFakeCookieTagMap.deleteValue((nextCookie.value()))));
+ EXPECT_RESULT_OK(nextCookie);
+ EXPECT_RESULT_OK(mFakeCookieTagMap.deleteValue((nextCookie.value())));
// Find the entry that is now immediately after headOfMap, then delete that.
nextCookie = mFakeCookieTagMap.getNextKey(curCookie);
- EXPECT_TRUE(isOk(nextCookie));
- EXPECT_TRUE(isOk(mFakeCookieTagMap.deleteValue((nextCookie.value()))));
+ EXPECT_RESULT_OK(nextCookie);
+ EXPECT_RESULT_OK(mFakeCookieTagMap.deleteValue((nextCookie.value())));
// Attempting to read an entry that has been deleted fails with ENOENT.
curCookie = nextCookie.value();
auto tagResult = mFakeCookieTagMap.readValue(curCookie);
- EXPECT_EQ(ENOENT, tagResult.status().code());
+ EXPECT_EQ(ENOENT, tagResult.error().code());
// Finding the entry after our deleted entry restarts iteration from the beginning of the map.
nextCookie = mFakeCookieTagMap.getNextKey(curCookie);
- EXPECT_TRUE(isOk(nextCookie));
+ EXPECT_RESULT_OK(nextCookie);
EXPECT_EQ(headOfMap, nextCookie.value());
}
@@ -180,19 +175,19 @@
for (int i = 0; i < 5; i++) {
uint64_t cookie = i + 1;
- struct UidTag tag = {.uid = TEST_UID1, .tag = TEST_TAG};
- EXPECT_TRUE(isOk(mFakeCookieTagMap.writeValue(cookie, tag, BPF_ANY)));
+ UidTagValue tag = {.uid = TEST_UID1, .tag = TEST_TAG};
+ EXPECT_RESULT_OK(mFakeCookieTagMap.writeValue(cookie, tag, BPF_ANY));
}
int totalCount = 0;
int totalSum = 0;
- const auto iterateWithoutDeletion = [&totalCount, &totalSum](const uint64_t& key,
- const BpfMap<uint64_t, UidTag>&) {
- EXPECT_GE((uint64_t)5, key);
- totalCount++;
- totalSum += key;
- return netdutils::status::ok;
- };
- EXPECT_TRUE(isOk(mFakeCookieTagMap.iterate(iterateWithoutDeletion)));
+ const auto iterateWithoutDeletion =
+ [&totalCount, &totalSum](const uint64_t& key, const BpfMap<uint64_t, UidTagValue>&) {
+ EXPECT_GE((uint64_t)5, key);
+ totalCount++;
+ totalSum += key;
+ return Result<void>();
+ };
+ EXPECT_RESULT_OK(mFakeCookieTagMap.iterate(iterateWithoutDeletion));
EXPECT_EQ(5, totalCount);
EXPECT_EQ(1 + 2 + 3 + 4 + 5, totalSum);
}
@@ -201,10 +196,10 @@
SKIP_IF_BPF_NOT_SUPPORTED;
StatsValue value1 = {
- .rxBytes = 0,
.rxPackets = 0,
- .txBytes = 0,
+ .rxBytes = 0,
.txPackets = 0,
+ .txBytes = 0,
};
Stats result1 = {};
ASSERT_EQ(0, bpfGetUidStatsInternal(TEST_UID1, &result1, mFakeAppUidStatsMap));
@@ -217,18 +212,20 @@
updateIfaceMap(IFACE_NAME1, IFACE_INDEX1);
updateIfaceMap(IFACE_NAME2, IFACE_INDEX2);
updateIfaceMap(IFACE_NAME3, IFACE_INDEX3);
- StatsValue value1 = {.rxBytes = TEST_BYTES0,
- .rxPackets = TEST_PACKET0,
- .txBytes = TEST_BYTES1,
- .txPackets = TEST_PACKET1,};
- StatsValue value2 = {
- .rxBytes = TEST_BYTES0 * 2,
- .rxPackets = TEST_PACKET0 * 2,
- .txBytes = TEST_BYTES1 * 2,
- .txPackets = TEST_PACKET1 * 2,
+ StatsValue value1 = {
+ .rxPackets = TEST_PACKET0,
+ .rxBytes = TEST_BYTES0,
+ .txPackets = TEST_PACKET1,
+ .txBytes = TEST_BYTES1,
};
- ASSERT_TRUE(isOk(mFakeAppUidStatsMap.writeValue(TEST_UID1, value1, BPF_ANY)));
- ASSERT_TRUE(isOk(mFakeAppUidStatsMap.writeValue(TEST_UID2, value2, BPF_ANY)));
+ StatsValue value2 = {
+ .rxPackets = TEST_PACKET0 * 2,
+ .rxBytes = TEST_BYTES0 * 2,
+ .txPackets = TEST_PACKET1 * 2,
+ .txBytes = TEST_BYTES1 * 2,
+ };
+ ASSERT_RESULT_OK(mFakeAppUidStatsMap.writeValue(TEST_UID1, value1, BPF_ANY));
+ ASSERT_RESULT_OK(mFakeAppUidStatsMap.writeValue(TEST_UID2, value2, BPF_ANY));
Stats result1 = {};
ASSERT_EQ(0, bpfGetUidStatsInternal(TEST_UID1, &result1, mFakeAppUidStatsMap));
expectStatsEqual(value1, result1);
@@ -258,23 +255,23 @@
updateIfaceMap(IFACE_NAME2, IFACE_INDEX2);
updateIfaceMap(IFACE_NAME3, IFACE_INDEX3);
StatsValue value1 = {
- .rxBytes = TEST_BYTES0,
- .rxPackets = TEST_PACKET0,
- .txBytes = TEST_BYTES1,
- .txPackets = TEST_PACKET1,
+ .rxPackets = TEST_PACKET0,
+ .rxBytes = TEST_BYTES0,
+ .txPackets = TEST_PACKET1,
+ .txBytes = TEST_BYTES1,
};
StatsValue value2 = {
- .rxBytes = TEST_BYTES1,
- .rxPackets = TEST_PACKET1,
- .txBytes = TEST_BYTES0,
- .txPackets = TEST_PACKET0,
+ .rxPackets = TEST_PACKET1,
+ .rxBytes = TEST_BYTES1,
+ .txPackets = TEST_PACKET0,
+ .txBytes = TEST_BYTES0,
};
uint32_t ifaceStatsKey = IFACE_INDEX1;
- EXPECT_TRUE(isOk(mFakeIfaceStatsMap.writeValue(ifaceStatsKey, value1, BPF_ANY)));
+ EXPECT_RESULT_OK(mFakeIfaceStatsMap.writeValue(ifaceStatsKey, value1, BPF_ANY));
ifaceStatsKey = IFACE_INDEX2;
- EXPECT_TRUE(isOk(mFakeIfaceStatsMap.writeValue(ifaceStatsKey, value2, BPF_ANY)));
+ EXPECT_RESULT_OK(mFakeIfaceStatsMap.writeValue(ifaceStatsKey, value2, BPF_ANY));
ifaceStatsKey = IFACE_INDEX3;
- EXPECT_TRUE(isOk(mFakeIfaceStatsMap.writeValue(ifaceStatsKey, value1, BPF_ANY)));
+ EXPECT_RESULT_OK(mFakeIfaceStatsMap.writeValue(ifaceStatsKey, value1, BPF_ANY));
Stats result1 = {};
ASSERT_EQ(0, bpfGetIfaceStatsInternal(IFACE_NAME1, &result1, mFakeIfaceStatsMap,
@@ -288,10 +285,10 @@
ASSERT_EQ(0, bpfGetIfaceStatsInternal(NULL, &totalResult, mFakeIfaceStatsMap,
mFakeIfaceIndexNameMap));
StatsValue totalValue = {
- .rxBytes = TEST_BYTES0 * 2 + TEST_BYTES1,
- .rxPackets = TEST_PACKET0 * 2 + TEST_PACKET1,
- .txBytes = TEST_BYTES1 * 2 + TEST_BYTES0,
- .txPackets = TEST_PACKET1 * 2 + TEST_PACKET0,
+ .rxPackets = TEST_PACKET0 * 2 + TEST_PACKET1,
+ .rxBytes = TEST_BYTES0 * 2 + TEST_BYTES1,
+ .txPackets = TEST_PACKET1 * 2 + TEST_PACKET0,
+ .txBytes = TEST_BYTES1 * 2 + TEST_BYTES0,
};
expectStatsEqual(totalValue, totalResult);
}
@@ -301,10 +298,12 @@
updateIfaceMap(IFACE_NAME1, IFACE_INDEX1);
updateIfaceMap(IFACE_NAME2, IFACE_INDEX2);
- StatsValue value1 = {.rxBytes = TEST_BYTES0,
- .rxPackets = TEST_PACKET0,
- .txBytes = TEST_BYTES1,
- .txPackets = TEST_PACKET1,};
+ StatsValue value1 = {
+ .rxPackets = TEST_PACKET0,
+ .rxBytes = TEST_BYTES0,
+ .txPackets = TEST_PACKET1,
+ .txBytes = TEST_BYTES1,
+ };
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,
@@ -336,10 +335,12 @@
updateIfaceMap(IFACE_NAME1, IFACE_INDEX1);
updateIfaceMap(IFACE_NAME2, IFACE_INDEX2);
- StatsValue value1 = {.rxBytes = TEST_BYTES0,
- .rxPackets = TEST_PACKET0,
- .txBytes = TEST_BYTES1,
- .txPackets = TEST_PACKET1,};
+ StatsValue value1 = {
+ .rxPackets = TEST_PACKET0,
+ .rxBytes = TEST_BYTES0,
+ .txPackets = TEST_PACKET1,
+ .txBytes = TEST_BYTES1,
+ };
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);
@@ -370,22 +371,28 @@
SKIP_IF_BPF_NOT_SUPPORTED;
updateIfaceMap(IFACE_NAME1, IFACE_INDEX1);
- StatsValue value1 = {.rxBytes = TEST_BYTES0 * 20,
- .rxPackets = TEST_PACKET0,
- .txBytes = TEST_BYTES1 * 20,
- .txPackets = TEST_PACKET1,};
+ StatsValue value1 = {
+ .rxPackets = TEST_PACKET0,
+ .rxBytes = TEST_BYTES0 * 20,
+ .txPackets = TEST_PACKET1,
+ .txBytes = TEST_BYTES1 * 20,
+ };
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 = {.rxBytes = TEST_BYTES0 * 40,
- .rxPackets = TEST_PACKET0,
- .txBytes = TEST_BYTES1 * 40,
- .txPackets = TEST_PACKET1,};
+ StatsValue value2 = {
+ .rxPackets = TEST_PACKET0,
+ .rxBytes = TEST_BYTES0 * 40,
+ .txPackets = TEST_PACKET1,
+ .txBytes = TEST_BYTES1 * 40,
+ };
populateFakeStats(TEST_UID1, 0, IFACE_INDEX2, TEST_COUNTERSET0, value2, mFakeStatsMap);
- StatsKey curKey = {.uid = TEST_UID1,
- .tag = 0,
- .ifaceIndex = ifaceIndex,
- .counterSet = TEST_COUNTERSET0};
+ StatsKey curKey = {
+ .uid = TEST_UID1,
+ .tag = 0,
+ .counterSet = TEST_COUNTERSET0,
+ .ifaceIndex = ifaceIndex,
+ };
char ifname[IFNAMSIZ];
int64_t unknownIfaceBytesTotal = 0;
ASSERT_EQ(-ENODEV, getIfaceNameFromMap(mFakeIfaceIndexNameMap, mFakeStatsMap, ifaceIndex,
@@ -412,25 +419,25 @@
updateIfaceMap(IFACE_NAME3, IFACE_INDEX3);
updateIfaceMap(LONG_IFACE_NAME, IFACE_INDEX4);
StatsValue value1 = {
- .rxBytes = TEST_BYTES0,
- .rxPackets = TEST_PACKET0,
- .txBytes = TEST_BYTES1,
- .txPackets = TEST_PACKET1,
+ .rxPackets = TEST_PACKET0,
+ .rxBytes = TEST_BYTES0,
+ .txPackets = TEST_PACKET1,
+ .txBytes = TEST_BYTES1,
};
StatsValue value2 = {
- .rxBytes = TEST_BYTES1,
- .rxPackets = TEST_PACKET1,
- .txBytes = TEST_BYTES0,
- .txPackets = TEST_PACKET0,
+ .rxPackets = TEST_PACKET1,
+ .rxBytes = TEST_BYTES1,
+ .txPackets = TEST_PACKET0,
+ .txBytes = TEST_BYTES0,
};
uint32_t ifaceStatsKey = IFACE_INDEX1;
- EXPECT_TRUE(isOk(mFakeIfaceStatsMap.writeValue(ifaceStatsKey, value1, BPF_ANY)));
+ EXPECT_RESULT_OK(mFakeIfaceStatsMap.writeValue(ifaceStatsKey, value1, BPF_ANY));
ifaceStatsKey = IFACE_INDEX2;
- EXPECT_TRUE(isOk(mFakeIfaceStatsMap.writeValue(ifaceStatsKey, value2, BPF_ANY)));
+ EXPECT_RESULT_OK(mFakeIfaceStatsMap.writeValue(ifaceStatsKey, value2, BPF_ANY));
ifaceStatsKey = IFACE_INDEX3;
- EXPECT_TRUE(isOk(mFakeIfaceStatsMap.writeValue(ifaceStatsKey, value1, BPF_ANY)));
+ EXPECT_RESULT_OK(mFakeIfaceStatsMap.writeValue(ifaceStatsKey, value1, BPF_ANY));
ifaceStatsKey = IFACE_INDEX4;
- EXPECT_TRUE(isOk(mFakeIfaceStatsMap.writeValue(ifaceStatsKey, value2, BPF_ANY)));
+ EXPECT_RESULT_OK(mFakeIfaceStatsMap.writeValue(ifaceStatsKey, value2, BPF_ANY));
std::vector<stats_line> lines;
ASSERT_EQ(0,
parseBpfNetworkStatsDevInternal(&lines, mFakeIfaceStatsMap, mFakeIfaceIndexNameMap));
@@ -452,22 +459,22 @@
updateIfaceMap(IFACE_NAME1, IFACE_INDEX3); // Duplicate!
StatsValue value1 = {
- .rxBytes = TEST_BYTES0,
.rxPackets = TEST_PACKET0,
- .txBytes = TEST_BYTES1,
+ .rxBytes = TEST_BYTES0,
.txPackets = TEST_PACKET1,
+ .txBytes = TEST_BYTES1,
};
StatsValue value2 = {
- .rxBytes = TEST_BYTES1,
.rxPackets = TEST_PACKET1,
- .txBytes = TEST_BYTES0,
+ .rxBytes = TEST_BYTES1,
.txPackets = TEST_PACKET0,
+ .txBytes = TEST_BYTES0,
};
StatsValue value3 = {
- .rxBytes = TEST_BYTES0 * 2,
.rxPackets = TEST_PACKET0 * 2,
- .txBytes = TEST_BYTES1 * 2,
+ .rxBytes = TEST_BYTES0 * 2,
.txPackets = TEST_PACKET1 * 2,
+ .txBytes = TEST_BYTES1 * 2,
};
std::vector<stats_line> lines;
@@ -516,13 +523,13 @@
// Perform test on IfaceStats.
uint32_t ifaceStatsKey = IFACE_INDEX2;
- EXPECT_TRUE(isOk(mFakeIfaceStatsMap.writeValue(ifaceStatsKey, value2, BPF_ANY)));
+ EXPECT_RESULT_OK(mFakeIfaceStatsMap.writeValue(ifaceStatsKey, value2, BPF_ANY));
ifaceStatsKey = IFACE_INDEX1;
- EXPECT_TRUE(isOk(mFakeIfaceStatsMap.writeValue(ifaceStatsKey, value1, BPF_ANY)));
+ EXPECT_RESULT_OK(mFakeIfaceStatsMap.writeValue(ifaceStatsKey, value1, BPF_ANY));
// This should be grouped.
ifaceStatsKey = IFACE_INDEX3;
- EXPECT_TRUE(isOk(mFakeIfaceStatsMap.writeValue(ifaceStatsKey, value1, BPF_ANY)));
+ EXPECT_RESULT_OK(mFakeIfaceStatsMap.writeValue(ifaceStatsKey, value1, BPF_ANY));
ASSERT_EQ(0,
parseBpfNetworkStatsDevInternal(&lines, mFakeIfaceStatsMap, mFakeIfaceIndexNameMap));
@@ -541,10 +548,10 @@
updateIfaceMap(IFACE_NAME1, IFACE_INDEX1);
StatsValue value1 = {
- .rxBytes = TEST_BYTES0,
.rxPackets = TEST_PACKET0,
- .txBytes = TEST_BYTES1,
+ .rxBytes = TEST_BYTES0,
.txPackets = TEST_PACKET1,
+ .txBytes = TEST_BYTES1,
};
// Mutate uid, 0 < TEST_UID1 < INT_MAX < INT_MIN < UINT_MAX.
diff --git a/libnetdbpf/include/netdbpf/BpfNetworkStats.h b/libnetdbpf/include/netdbpf/BpfNetworkStats.h
index 503f090..8ab7e25 100644
--- a/libnetdbpf/include/netdbpf/BpfNetworkStats.h
+++ b/libnetdbpf/include/netdbpf/BpfNetworkStats.h
@@ -18,6 +18,7 @@
#define _BPF_NETWORKSTATS_H
#include <bpf/BpfMap.h>
+#include "bpf_shared.h"
namespace android {
namespace bpf {
@@ -55,7 +56,7 @@
bool operator<(const stats_line& lhs, const stats_line& rhs);
// For test only
-int bpfGetUidStatsInternal(uid_t uid, struct Stats* stats,
+int bpfGetUidStatsInternal(uid_t uid, Stats* stats,
const BpfMap<uint32_t, StatsValue>& appUidStatsMap);
// For test only
int bpfGetIfaceStatsInternal(const char* iface, Stats* stats,
@@ -74,7 +75,7 @@
const BpfMap<Key, StatsValue>& statsMap, uint32_t ifaceIndex, char* ifname,
const Key& curKey, int64_t* unknownIfaceBytesTotal) {
auto iface = ifaceMap.readValue(ifaceIndex);
- if (!isOk(iface)) {
+ if (!iface.ok()) {
maybeLogUnknownIface(ifaceIndex, statsMap, curKey, unknownIfaceBytesTotal);
return -ENODEV;
}
@@ -92,7 +93,7 @@
// Are we undercounting enough data to be worth logging?
auto statsEntry = statsMap.readValue(curKey);
- if (!netdutils::isOk(statsEntry)) {
+ if (!statsEntry.ok()) {
// No data is being undercounted.
return;
}
@@ -110,8 +111,8 @@
const BpfMap<uint32_t, StatsValue>& statsMap,
const BpfMap<uint32_t, IfaceValue>& ifaceMap);
-int bpfGetUidStats(uid_t uid, struct Stats* stats);
-int bpfGetIfaceStats(const char* iface, struct Stats* stats);
+int bpfGetUidStats(uid_t uid, Stats* stats);
+int bpfGetIfaceStats(const char* iface, Stats* stats);
int parseBpfNetworkStatsDetail(std::vector<stats_line>* lines,
const std::vector<std::string>& limitIfaces, int limitTag,
int limitUid);
diff --git a/libnetdbpf/include/netdbpf/bpf_shared.h b/libnetdbpf/include/netdbpf/bpf_shared.h
index 66d20ec..6f31879 100644
--- a/libnetdbpf/include/netdbpf/bpf_shared.h
+++ b/libnetdbpf/include/netdbpf/bpf_shared.h
@@ -17,11 +17,44 @@
#ifndef NETDBPF_BPF_SHARED_H
#define NETDBPF_BPF_SHARED_H
+#include <linux/if_ether.h>
#include <linux/in.h>
#include <linux/in6.h>
#include <netdutils/UidConstants.h>
-// const values shared by bpf kernel program bpfloader and netd
+// This header file is shared by eBPF kernel programs and netd
+
+typedef struct {
+ uint32_t uid;
+ uint32_t tag;
+} UidTagValue;
+
+typedef struct {
+ uint32_t uid;
+ uint32_t tag;
+ uint32_t counterSet;
+ uint32_t ifaceIndex;
+} StatsKey;
+
+typedef struct {
+ uint64_t rxPackets;
+ uint64_t rxBytes;
+ uint64_t txPackets;
+ uint64_t txBytes;
+} StatsValue;
+
+typedef struct {
+ char name[IFNAMSIZ];
+} IfaceValue;
+
+typedef struct {
+ uint64_t rxBytes;
+ uint64_t rxPackets;
+ uint64_t txBytes;
+ uint64_t txPackets;
+ uint64_t tcpRxPackets;
+ uint64_t tcpTxPackets;
+} Stats;
// Since we cannot garbage collect the stats map since device boot, we need to make these maps as
// large as possible. The maximum size of number of map entries we can have is depend on the rlimit
@@ -87,7 +120,7 @@
IIF_MATCH = (1 << 5),
};
-enum BpfPemissionMatch {
+enum BpfPermissionMatch {
BPF_PERMISSION_INTERNET = 1 << 2,
BPF_PERMISSION_UPDATE_DEVICE_STATS = 1 << 3,
};
@@ -133,4 +166,59 @@
struct in_addr local4; // The destination IPv4 address
} ClatIngressValue;
+#define CLAT_EGRESS_PROG_RAWIP_NAME "prog_clatd_schedcls_egress_clat_rawip"
+#define CLAT_EGRESS_PROG_ETHER_NAME "prog_clatd_schedcls_egress_clat_ether"
+
+#define CLAT_EGRESS_PROG_RAWIP_PATH BPF_PATH "/" CLAT_EGRESS_PROG_RAWIP_NAME
+#define CLAT_EGRESS_PROG_ETHER_PATH BPF_PATH "/" CLAT_EGRESS_PROG_ETHER_NAME
+
+#define CLAT_EGRESS_MAP_PATH BPF_PATH "/map_clatd_clat_egress_map"
+
+typedef struct {
+ uint32_t iif; // The input interface index
+ struct in_addr local4; // The source IPv4 address
+} ClatEgressKey;
+
+typedef struct {
+ uint32_t oif; // The output interface to redirect to
+ struct in6_addr local6; // The full 128-bits of the source IPv6 address
+ struct in6_addr pfx96; // The destination /96 nat64 prefix, bottom 32 bits must be 0
+ bool oifIsEthernet; // Whether the output interface requires ethernet header
+} ClatEgressValue;
+
+#define TETHER_INGRESS_PROG_RAWIP_NAME "prog_offload_schedcls_ingress_tether_rawip"
+#define TETHER_INGRESS_PROG_ETHER_NAME "prog_offload_schedcls_ingress_tether_ether"
+
+#define TETHER_INGRESS_PROG_RAWIP_PATH BPF_PATH "/" TETHER_INGRESS_PROG_RAWIP_NAME
+#define TETHER_INGRESS_PROG_ETHER_PATH BPF_PATH "/" TETHER_INGRESS_PROG_ETHER_NAME
+
+#define TETHER_INGRESS_MAP_PATH BPF_PATH "/map_offload_tether_ingress_map"
+
+typedef struct {
+ uint32_t iif; // The input interface index
+ struct in6_addr neigh6; // The destination IPv6 address
+} TetherIngressKey;
+
+typedef struct {
+ uint32_t oif; // The output interface to redirect to
+ // For now tethering offload only needs to support downstreams that use 6-byte MAC addresses,
+ // because all downstream types that are currently supported (WiFi, USB, Bluetooth and
+ // Ethernet) have 6-byte MAC addresses.
+ struct ethhdr macHeader; // includes dst/src mac and ethertype
+ uint16_t pmtu; // The maximum L3 output path/route mtu
+} TetherIngressValue;
+
+#define TETHER_STATS_MAP_PATH BPF_PATH "/map_offload_tether_stats_map"
+
+typedef struct {
+ uint64_t rxPackets;
+ uint64_t rxBytes;
+ uint64_t rxErrors;
+ uint64_t txPackets;
+ uint64_t txBytes;
+ uint64_t txErrors;
+} TetherStatsValue;
+
+#define TETHER_LIMIT_MAP_PATH BPF_PATH "/map_offload_tether_limit_map"
+
#endif // NETDBPF_BPF_SHARED_H
diff --git a/libnetdutils/Android.bp b/libnetdutils/Android.bp
index b3fcfc7..00bdc74 100644
--- a/libnetdutils/Android.bp
+++ b/libnetdutils/Android.bp
@@ -25,6 +25,15 @@
"libbase",
],
export_include_dirs: ["include"],
+ sanitize: {
+ cfi: true,
+ },
+
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.resolv",
+ ],
+ min_sdk_version: "29",
}
cc_test {
diff --git a/libnetdutils/AndroidTest.xml b/libnetdutils/AndroidTest.xml
deleted file mode 100644
index dc13361..0000000
--- a/libnetdutils/AndroidTest.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?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/FdTest.cpp b/libnetdutils/FdTest.cpp
index 889c1b7..7080f83 100644
--- a/libnetdutils/FdTest.cpp
+++ b/libnetdutils/FdTest.cpp
@@ -102,6 +102,7 @@
UniqueFd u1(kFd);
{
UniqueFd u2(std::move(u1));
+ // NOLINTNEXTLINE bugprone-use-after-move
EXPECT_FALSE(isWellFormed(u1));
EXPECT_TRUE(isWellFormed(u2));
EXPECT_CALL(mSyscalls, close(kFd)).WillOnce(Return(status::ok));
@@ -115,10 +116,12 @@
UniqueFd u1(kFd);
{
UniqueFd u2 = std::move(u1);
+ // NOLINTNEXTLINE bugprone-use-after-move
EXPECT_FALSE(isWellFormed(u1));
EXPECT_TRUE(isWellFormed(u2));
UniqueFd u3;
u3 = std::move(u2);
+ // NOLINTNEXTLINE bugprone-use-after-move
EXPECT_FALSE(isWellFormed(u2));
EXPECT_TRUE(isWellFormed(u3));
EXPECT_CALL(mSyscalls, close(kFd)).WillOnce(Return(status::ok));
diff --git a/libnetdutils/InternetAddresses.cpp b/libnetdutils/InternetAddresses.cpp
index 944ed91..322f1b1 100644
--- a/libnetdutils/InternetAddresses.cpp
+++ b/libnetdutils/InternetAddresses.cpp
@@ -16,6 +16,8 @@
#include "netdutils/InternetAddresses.h"
+#include <string>
+
#include <android-base/stringprintf.h>
#include <arpa/inet.h>
#include <netdb.h>
@@ -61,9 +63,8 @@
};
addrinfo* res;
const int ret = getaddrinfo(repr.c_str(), nullptr, &hints, &res);
- // TODO: move ScopedAddrinfo into libnetdutils and use it here.
+ ScopedAddrinfo res_cleanup(res);
if (ret != 0) {
- freeaddrinfo(res);
return false;
}
@@ -84,7 +85,6 @@
break;
}
- freeaddrinfo(res);
return rval;
}
@@ -125,6 +125,28 @@
return mData == empty;
}
+bool IPPrefix::forString(const std::string& repr, IPPrefix* prefix) {
+ size_t index = repr.find('/');
+ if (index == std::string::npos) return false;
+
+ // Parse the IP address.
+ IPAddress ip;
+ if (!IPAddress::forString(repr.substr(0, index), &ip)) return false;
+
+ // Parse the prefix length. Can't use base::ParseUint because it accepts non-base 10 input.
+ const char* prefixString = repr.c_str() + index + 1;
+ if (!isdigit(*prefixString)) return false;
+ char* endptr;
+ unsigned long prefixlen = strtoul(prefixString, &endptr, 10);
+ if (*endptr != '\0') return false;
+
+ uint8_t maxlen = (ip.family() == AF_INET) ? 32 : 128;
+ if (prefixlen > maxlen) return false;
+
+ *prefix = IPPrefix(ip, prefixlen);
+ return true;
+}
+
std::string IPPrefix::toString() const noexcept {
return StringPrintf("%s/%d", ip().toString().c_str(), mData.cidrlen);
}
diff --git a/libnetdutils/InternetAddressesTest.cpp b/libnetdutils/InternetAddressesTest.cpp
index 2129ca7..f75fa76 100644
--- a/libnetdutils/InternetAddressesTest.cpp
+++ b/libnetdutils/InternetAddressesTest.cpp
@@ -171,6 +171,43 @@
EXPECT_EQ(IPAddress(FE80_1, 1), IPAddress::forString("fe80::1%lo"));
}
+TEST(IPPrefixTest, forString) {
+ IPPrefix prefix;
+
+ EXPECT_FALSE(IPPrefix::forString("", &prefix));
+ EXPECT_FALSE(IPPrefix::forString("invalid", &prefix));
+ EXPECT_FALSE(IPPrefix::forString("192.0.2.0", &prefix));
+ EXPECT_FALSE(IPPrefix::forString("2001::db8::", &prefix));
+
+ EXPECT_FALSE(IPPrefix::forString("2001:db8::/", &prefix));
+ EXPECT_FALSE(IPPrefix::forString("2001:db8:://32", &prefix));
+ EXPECT_FALSE(IPPrefix::forString("2001:db8::/32z", &prefix));
+ EXPECT_FALSE(IPPrefix::forString("2001:db8::/32/", &prefix));
+ EXPECT_FALSE(IPPrefix::forString("2001:db8::/0x20", &prefix));
+ EXPECT_FALSE(IPPrefix::forString("2001:db8:: /32", &prefix));
+ EXPECT_FALSE(IPPrefix::forString("2001:db8::/ 32", &prefix));
+ EXPECT_FALSE(IPPrefix::forString(" 2001:db8::/32", &prefix));
+ EXPECT_FALSE(IPPrefix::forString("2001:db8::/32 ", &prefix));
+ EXPECT_FALSE(IPPrefix::forString("2001:db8::/+32", &prefix));
+
+ EXPECT_FALSE(IPPrefix::forString("192.0.2.0/33", &prefix));
+ EXPECT_FALSE(IPPrefix::forString("2001:db8::/129", &prefix));
+ EXPECT_FALSE(IPPrefix::forString("192.0.2.0/-1", &prefix));
+ EXPECT_FALSE(IPPrefix::forString("2001:db8::/-1", &prefix));
+
+ EXPECT_TRUE(IPPrefix::forString("2001:db8::/32", &prefix));
+ EXPECT_EQ("2001:db8::/32", prefix.toString());
+ EXPECT_EQ(IPPrefix(IPAddress::forString("2001:db8::"), 32), prefix);
+
+ EXPECT_EQ(IPPrefix(), IPPrefix::forString("invalid"));
+
+ EXPECT_EQ("0.0.0.0/0", IPPrefix::forString("0.0.0.0/0").toString());
+ EXPECT_EQ("::/0", IPPrefix::forString("::/0").toString());
+ EXPECT_EQ("192.0.2.128/25", IPPrefix::forString("192.0.2.131/25").toString());
+ EXPECT_EQ("2001:db8:1:2:3:4:5:4/126",
+ IPPrefix::forString("2001:db8:1:2:3:4:5:6/126").toString());
+}
+
TEST(IPPrefixTest, IPv4Truncation) {
const auto prefixStr = [](int length) -> std::string {
return IPPrefix(IPAddress(IPV4_ONES), length).toString();
diff --git a/libnetdutils/ThreadUtilTest.cpp b/libnetdutils/ThreadUtilTest.cpp
index 2fe63b7..8fad8b8 100644
--- a/libnetdutils/ThreadUtilTest.cpp
+++ b/libnetdutils/ThreadUtilTest.cpp
@@ -14,22 +14,49 @@
* limitations under the License.
*/
+#include <string>
+
+#include <android-base/expected.h>
#include <gtest/gtest.h>
+#include <netdutils/ThreadUtil.h>
-#include "netdutils/ThreadUtil.h"
-
-namespace android {
-namespace netdutils {
+namespace android::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:
- NoopRun() { instanceNum++; }
- ~NoopRun() { instanceNum--; }
+ 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--;
+ }
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;
@@ -44,6 +71,11 @@
// 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;
@@ -61,5 +93,30 @@
EXPECT_EQ(0, NoopRun::instanceNum);
}
-} // namespace netdutils
-} // namespace android
+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
diff --git a/libnetdutils/include/netdutils/BackoffSequence.h b/libnetdutils/include/netdutils/BackoffSequence.h
index 6deb067..a52e72d 100644
--- a/libnetdutils/include/netdutils/BackoffSequence.h
+++ b/libnetdutils/include/netdutils/BackoffSequence.h
@@ -39,7 +39,7 @@
time_type endOfSequenceIndicator{TIME_ZERO};
};
- BackoffSequence() {}
+ BackoffSequence() : BackoffSequence(Parameters{}) {}
BackoffSequence(const BackoffSequence &) = default;
BackoffSequence(BackoffSequence &&) = default;
BackoffSequence& operator=(const BackoffSequence &) = default;
diff --git a/libnetdutils/include/netdutils/InternetAddresses.h b/libnetdutils/include/netdutils/InternetAddresses.h
index 07a6e90..d5cbe2b 100644
--- a/libnetdutils/include/netdutils/InternetAddresses.h
+++ b/libnetdutils/include/netdutils/InternetAddresses.h
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-#ifndef NETDUTILS_INTERNETADDRESSES_H_
-#define NETDUTILS_INTERNETADDRESSES_H_
+#pragma once
+#include <netdb.h>
#include <netinet/in.h>
#include <stdint.h>
#include <cstring>
@@ -104,6 +104,16 @@
} // 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));
}
@@ -185,7 +195,12 @@
class IPPrefix {
public:
- // TODO: "static forString(...)" using NetdConstants' parsePrefix().
+ static bool forString(const std::string& repr, IPPrefix* prefix);
+ static IPPrefix forString(const std::string& repr) {
+ IPPrefix prefix;
+ if (!forString(repr, &prefix)) return IPPrefix();
+ return prefix;
+ }
IPPrefix() = default;
IPPrefix(const IPPrefix&) = default;
@@ -232,6 +247,23 @@
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;
@@ -291,5 +323,3 @@
} // namespace netdutils
} // namespace android
-
-#endif // NETDUTILS_INTERNETADDRESSES_H_
diff --git a/libnetdutils/include/netdutils/NetworkConstants.h b/libnetdutils/include/netdutils/NetworkConstants.h
index 682ceaf..dead9a1 100644
--- a/libnetdutils/include/netdutils/NetworkConstants.h
+++ b/libnetdutils/include/netdutils/NetworkConstants.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef NETDUTILS_NETWORKCONSTANTS_H_
-#define NETDUTILS_NETWORKCONSTANTS_H_
+#pragma once
namespace android {
namespace netdutils {
@@ -26,7 +25,8 @@
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/Status.h b/libnetdutils/include/netdutils/Status.h
index 503eea3..bc347d5 100644
--- a/libnetdutils/include/netdutils/Status.h
+++ b/libnetdutils/include/netdutils/Status.h
@@ -21,6 +21,8 @@
#include <limits>
#include <ostream>
+#include <android-base/result.h>
+
namespace android {
namespace netdutils {
@@ -39,6 +41,10 @@
// Constructs an error Status, |code| must be non-zero.
Status(int code, std::string msg) : mCode(code), mMsg(std::move(msg)) { assert(!ok()); }
+ Status(android::base::Result<void> result)
+ : mCode(result.ok() ? 0 : result.error().code()),
+ mMsg(result.ok() ? "" : result.error().message()) {}
+
int code() const { return mCode; }
bool ok() const { return code() == 0; }
@@ -73,8 +79,9 @@
return status.ok();
}
-// For use only in tests.
-#define EXPECT_OK(status) EXPECT_TRUE((status).ok())
+// For use only in tests. Used for both Status and Status-like objects. See also isOk().
+#define EXPECT_OK(status) EXPECT_TRUE(isOk(status))
+#define ASSERT_OK(status) ASSERT_TRUE(isOk(status))
// Documents that status is expected to be ok. This function may log
// (or assert when running in debug mode) if status has an unexpected value.
diff --git a/libnetdutils/include/netdutils/Stopwatch.h b/libnetdutils/include/netdutils/Stopwatch.h
index 18e2050..e7b4326 100644
--- a/libnetdutils/include/netdutils/Stopwatch.h
+++ b/libnetdutils/include/netdutils/Stopwatch.h
@@ -29,22 +29,12 @@
public:
Stopwatch() : mStart(clock::now()) {}
-
virtual ~Stopwatch() = default;
- float timeTaken() const { return getElapsed(clock::now()); }
-
int64_t timeTakenUs() const { return getElapsedUs(clock::now()); }
-
- float getTimeAndReset() {
+ int64_t getTimeAndResetUs() {
const auto& now = clock::now();
- float elapsed = getElapsed(now);
- mStart = now;
- return elapsed;
- }
- float getTimeAndResetUs() {
- const auto& now = clock::now();
- float elapsed = getElapsedUs(now);
+ int64_t elapsed = getElapsedUs(now);
mStart = now;
return elapsed;
}
@@ -52,10 +42,6 @@
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 2ef97ef..62e6f70 100644
--- a/libnetdutils/include/netdutils/ThreadUtil.h
+++ b/libnetdutils/include/netdutils/ThreadUtil.h
@@ -34,9 +34,22 @@
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/netutils_wrappers/Android.bp b/netutils_wrappers/Android.bp
index 5074071..af7d8c3 100644
--- a/netutils_wrappers/Android.bp
+++ b/netutils_wrappers/Android.bp
@@ -21,6 +21,9 @@
"-Wall",
"-Wextra",
],
+ sanitize: {
+ cfi: true,
+ },
}
cc_test {
diff --git a/resolv/Android.bp b/resolv/Android.bp
deleted file mode 100644
index 06f7f85..0000000
--- a/resolv/Android.bp
+++ /dev/null
@@ -1,220 +0,0 @@
-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"],
- 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",
- ],
- compile_multilib: "both",
- sanitize: {
- address: true,
- recover: ["all"],
- },
-}
-
-cc_test {
- name: "resolv_unit_test",
- test_suites: ["device-tests"],
- defaults: ["netd_defaults"],
- srcs: [
- "dns_tls_test.cpp",
- "libnetd_resolv_test.cpp",
- ],
- shared_libs: [
- "libbase",
- "libcrypto",
- "liblog",
- "libssl",
- ],
- static_libs: [
- "libnetd_resolv",
- "libnetd_test_dnsresponder",
- "libnetdutils",
- "server_configurable_flags",
- ],
-}
diff --git a/resolv/AndroidTest.xml b/resolv/AndroidTest.xml
deleted file mode 100644
index f5715c9..0000000
--- a/resolv/AndroidTest.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<?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
deleted file mode 100644
index 6cd9c2e..0000000
--- a/resolv/Dns64Configuration.cpp
+++ /dev/null
@@ -1,233 +0,0 @@
-/*
- * 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"
-
-namespace android {
-
-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;
- const int status =
- android_getaddrinfofornetcontext(kIPv4OnlyHost, nullptr, &hints, &netcontext, &res);
- 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
deleted file mode 100644
index 58b115e..0000000
--- a/resolv/Dns64Configuration.h
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 63aa331..0000000
--- a/resolv/DnsProxyListener.cpp
+++ /dev/null
@@ -1,1221 +0,0 @@
-/*
- * 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 <list>
-#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_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 reportDnsEvent(int eventType, const android_net_context& netContext, int latencyUs,
- int returnCode, const NetworkDnsEventReported& dnsEvent,
- const std::string& query_name, const std::vector<std::string>& ip_addrs = {},
- int total_ip_addr_count = 0) {
- std::string dnsQueryStats = dnsEvent.dns_query_events().SerializeAsString();
- char const* dnsQueryStatsBytes = dnsQueryStats.c_str();
- stats::BytesField dnsQueryBytesField{dnsQueryStatsBytes, dnsQueryStats.size()};
- android::net::stats::stats_write(android::net::stats::NETWORK_DNS_EVENT_REPORTED, eventType,
- returnCode, latencyUs, dnsEvent.hints_ai_flags(),
- dnsEvent.res_nsend_flags(), dnsEvent.network_type(),
- dnsEvent.private_dns_modes(), dnsQueryBytesField);
-
- 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) {
- 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);
- 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 dnsEvent;
- if (queryLimiter.start(uid)) {
- rv = android_getaddrinfofornetcontext(mHost, mService, mHints, &mNetContext, &result);
- 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);
- const int latencyUs = int(s.timeTakenUs());
-
- 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, dnsEvent,
- 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 dnsEvent;
- if (queryLimiter.start(uid)) {
- nsendAns = resolv_res_nsend(&mNetContext, msg.data(), msgLen, ansBuf.data(), MAXPACKET,
- &arcode, static_cast<ResNsendFlags>(mFlags));
- queryLimiter.finish(uid);
- } else {
- LOG(WARNING) << "ResNSendHandler::run: resnsend: from UID " << uid
- << ", max concurrent queries reached";
- nsendAns = -EBUSY;
- }
-
- const int latencyUs = int(s.timeTakenUs());
-
- // 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), dnsEvent, 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), dnsEvent, 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) {
- // 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);
- 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 dnsEvent;
- if (queryLimiter.start(uid)) {
- rv = android_gethostbynamefornetcontext(mName, mAf, &mNetContext, &hp);
- queryLimiter.finish(uid);
- } else {
- rv = EAI_MEMORY;
- LOG(ERROR) << "GetHostByNameHandler::run: from UID " << uid
- << ", max concurrent queries reached";
- }
-
- doDns64Synthesis(&rv, &hp);
- const int latencyUs = lround(s.timeTakenUs());
- 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, dnsEvent,
- 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) {
- 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);
- 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 dnsEvent;
- if (queryLimiter.start(uid)) {
- rv = android_gethostbyaddrfornetcontext(mAddress, mAddressLen, mAddressFamily,
- &mNetContext, &hp);
- queryLimiter.finish(uid);
- } else {
- rv = EAI_MEMORY;
- LOG(ERROR) << "GetHostByAddrHandler::run: from UID " << uid
- << ", max concurrent queries reached";
- }
-
- doDns64ReverseLookup(&hp);
- const int latencyUs = int(s.timeTakenUs());
-
- 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, dnsEvent,
- (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
deleted file mode 100644
index 423e77f..0000000
--- a/resolv/DnsProxyListener.h
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- * 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 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);
-
- 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);
-
- 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);
-
- 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
deleted file mode 100644
index 092ff3f..0000000
--- a/resolv/DnsResolver.cpp
+++ /dev/null
@@ -1,82 +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.
- */
-
-#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
deleted file mode 100644
index 6d8d2ce..0000000
--- a/resolv/DnsResolver.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 _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
deleted file mode 100644
index 8eefd22..0000000
--- a/resolv/DnsResolverService.cpp
+++ /dev/null
@@ -1,297 +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.
- */
-
-#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
deleted file mode 100644
index 1a1c9e7..0000000
--- a/resolv/DnsResolverService.h
+++ /dev/null
@@ -1,72 +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 _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
deleted file mode 100644
index d9896ad..0000000
--- a/resolv/DnsTlsDispatcher.cpp
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- * 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 "DnsTlsSocketFactory.h"
-
-#include "log/log.h"
-
-namespace android {
-namespace net {
-
-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, unsigned mark,
- const Slice query, const Slice ans, int *resplen) {
- const std::list<DnsTlsServer> orderedServers(getOrderedServerList(tlsServers, mark));
-
- if (orderedServers.empty()) ALOGW("Empty DnsTlsServer list");
-
- DnsTlsTransport::Response code = DnsTlsTransport::Response::internal_error;
- for (const auto& server : orderedServers) {
- code = this->query(server, mark, query, ans, resplen);
- switch (code) {
- // These response codes are valid responses and not expected to
- // change if another server is queried.
- case DnsTlsTransport::Response::success:
- case DnsTlsTransport::Response::limit_error:
- return code;
- break;
- // 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:
- case DnsTlsTransport::Response::internal_error:
- continue;
- break;
- // 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
deleted file mode 100644
index 7a48089..0000000
--- a/resolv/DnsTlsDispatcher.h
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * 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"
-
-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, unsigned mark,
- 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
deleted file mode 100644
index 6e6399c..0000000
--- a/resolv/DnsTlsQueryMap.cpp
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- * 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
deleted file mode 100644
index c5ab023..0000000
--- a/resolv/DnsTlsQueryMap.h
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * 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
deleted file mode 100644
index a97c672..0000000
--- a/resolv/DnsTlsServer.cpp
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 7fc4a35..0000000
--- a/resolv/DnsTlsServer.h
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 8d6dc6b..0000000
--- a/resolv/DnsTlsSessionCache.cpp
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * 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
deleted file mode 100644
index e1a88cf..0000000
--- a/resolv/DnsTlsSessionCache.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * 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
deleted file mode 100644
index a63d221..0000000
--- a/resolv/DnsTlsSocket.cpp
+++ /dev/null
@@ -1,548 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 2940500..0000000
--- a/resolv/DnsTlsSocket.h
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * 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
deleted file mode 100644
index af4011d..0000000
--- a/resolv/DnsTlsSocketFactory.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 0f9042e..0000000
--- a/resolv/DnsTlsTransport.cpp
+++ /dev/null
@@ -1,214 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 6c98fa6..0000000
--- a/resolv/DnsTlsTransport.h
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 0f2800e..0000000
--- a/resolv/IDnsTlsSocket.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * 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
deleted file mode 100644
index a391f59..0000000
--- a/resolv/IDnsTlsSocketFactory.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 980f74a..0000000
--- a/resolv/IDnsTlsSocketObserver.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 0481eda..0000000
--- a/resolv/LockedQueue.h
+++ /dev/null
@@ -1,52 +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 _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
deleted file mode 100644
index 7504f4d..0000000
--- a/resolv/NOTICE
+++ /dev/null
@@ -1,418 +0,0 @@
-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
deleted file mode 100644
index 1a88275..0000000
--- a/resolv/PrivateDnsConfiguration.cpp
+++ /dev/null
@@ -1,315 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 50fb54d..0000000
--- a/resolv/PrivateDnsConfiguration.h
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 8fe4c89..0000000
--- a/resolv/README.md
+++ /dev/null
@@ -1,134 +0,0 @@
-# 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
deleted file mode 100644
index e16ca69..0000000
--- a/resolv/ResolverController.cpp
+++ /dev/null
@@ -1,382 +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.
- */
-
-#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(
- "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(static_cast<PrivateDnsMode>(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
deleted file mode 100644
index 6d08cdb..0000000
--- a/resolv/ResolverController.h
+++ /dev/null
@@ -1,67 +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 _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
deleted file mode 100644
index ccec08b..0000000
--- a/resolv/ResolverEventReporter.cpp
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * 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
deleted file mode 100644
index eb0d9cf..0000000
--- a/resolv/ResolverEventReporter.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 9140af8..0000000
--- a/resolv/ResolverStats.h
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * 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
deleted file mode 100644
index c4b2b6d..0000000
--- a/resolv/aidl/dnsresolver/1/android/net/IDnsResolver.aidl
+++ /dev/null
@@ -1,27 +0,0 @@
-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
deleted file mode 100644
index b808bae..0000000
--- a/resolv/aidl/dnsresolver/1/android/net/ResolverParamsParcel.aidl
+++ /dev/null
@@ -1,15 +0,0 @@
-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
deleted file mode 100644
index 846bbec..0000000
--- a/resolv/aidl/dnsresolver/2/android/net/IDnsResolver.aidl
+++ /dev/null
@@ -1,50 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-// 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
deleted file mode 100644
index a37f938..0000000
--- a/resolv/aidl/dnsresolver/2/android/net/ResolverParamsParcel.aidl
+++ /dev/null
@@ -1,32 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-// 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
deleted file mode 100644
index f6f4092..0000000
--- a/resolv/binder/android/net/IDnsResolver.aidl
+++ /dev/null
@@ -1,164 +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.
- */
-
-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
deleted file mode 100644
index d25880f..0000000
--- a/resolv/binder/android/net/ResolverParamsParcel.aidl
+++ /dev/null
@@ -1,86 +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.
- */
-
-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
deleted file mode 100644
index 4c28c21..0000000
--- a/resolv/dns_responder/Android.bp
+++ /dev/null
@@ -1,24 +0,0 @@
-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
deleted file mode 100644
index 5047255..0000000
--- a/resolv/dns_responder/dns_responder.cpp
+++ /dev/null
@@ -1,994 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 99b601f..0000000
--- a/resolv/dns_responder/dns_responder.h
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 8c71e04..0000000
--- a/resolv/dns_responder/dns_responder_client.cpp
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * 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/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);
-
- 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
deleted file mode 100644
index 17bc961..0000000
--- a/resolv/dns_responder/dns_responder_client.h
+++ /dev/null
@@ -1,106 +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 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"
-
-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
deleted file mode 100644
index 83e36ed..0000000
--- a/resolv/dns_responder/dns_tls_frontend.cpp
+++ /dev/null
@@ -1,421 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 32a2771..0000000
--- a/resolv/dns_responder/dns_tls_frontend.h
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 0bcad30..0000000
--- a/resolv/dns_tls_test.cpp
+++ /dev/null
@@ -1,963 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 0a2de0e..0000000
--- a/resolv/dnsresolver_binder_test.cpp
+++ /dev/null
@@ -1,369 +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.
- *
- * 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);
- }
- 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) {
- 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
deleted file mode 100644
index 1d5038e..0000000
--- a/resolv/getaddrinfo.cpp
+++ /dev/null
@@ -1,1824 +0,0 @@
-/* $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
-
-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*);
-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);
-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,
- };
- return android_getaddrinfofornetcontext(hostname, servname, &hints, &netcontext, result);
-}
-
-int android_getaddrinfofornetcontext(const char* hostname, const char* servname,
- const struct addrinfo* hints,
- const struct android_net_context* netcontext,
- struct addrinfo** res) {
- 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);
-
- 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);
-
- 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) {
- 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);
- }
- 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) {
- 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);
-
- 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
deleted file mode 100644
index cf54cab..0000000
--- a/resolv/getaddrinfo.h
+++ /dev/null
@@ -1,25 +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.
- */
-
-#pragma once
-
-#include "netd_resolv/resolv.h" // struct android_net_context
-
-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**);
diff --git a/resolv/gethnamaddr.cpp b/resolv/gethnamaddr.cpp
deleted file mode 100644
index 1cf0694..0000000
--- a/resolv/gethnamaddr.cpp
+++ /dev/null
@@ -1,944 +0,0 @@
-/* $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"
-
-// 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);
-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);
-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*);
-static int android_gethostbyaddrfornetcontext_proxy(const void* addr, socklen_t len, int af,
- const struct android_net_context* netcontext,
- hostent** hp);
-
-#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) {
- res_setnetcontext(res, netcontext);
- 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) {
- 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);
- 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) {
- return android_gethostbyaddrfornetcontext_real(addr, len, af, hp, hbuf, hbuflen, netcontext);
-}
-
-// 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) {
- 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);
- 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) {
- int error;
- res_state res = res_get_state();
- if (res == NULL) return EAI_MEMORY;
- res_static* rs = res_get_static(); // For thread-safety.
- error = gethostbyname_internal(name, af, res, &rs->host, rs->hostbuf, sizeof(rs->hostbuf),
- netcontext);
- 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) {
- return android_gethostbyaddrfornetcontext_proxy(addr, len, af, netcontext, hp);
-}
-
-static int android_gethostbyaddrfornetcontext_proxy(const void* addr, socklen_t len, int af,
- const struct android_net_context* netcontext,
- hostent** hp) {
- 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);
- 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
deleted file mode 100644
index 27cc1c2..0000000
--- a/resolv/gethnamaddr.h
+++ /dev/null
@@ -1,27 +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.
- */
-
-#pragma once
-
-#include <netdb.h> // struct hostent
-#include "netd_resolv/resolv.h" // struct android_net_context
-
-// This is the entry point for the gethostbyname() family of legacy calls.
-int android_gethostbynamefornetcontext(const char*, int, const android_net_context*, hostent**);
-
-// 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**);
diff --git a/resolv/hostent.h b/resolv/hostent.h
deleted file mode 100644
index 4f6a33b..0000000
--- a/resolv/hostent.h
+++ /dev/null
@@ -1,72 +0,0 @@
-/* $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
deleted file mode 100644
index 93e6287..0000000
--- a/resolv/include/netd_resolv/params.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 3a71110..0000000
--- a/resolv/include/netd_resolv/resolv.h
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 41f56fa..0000000
--- a/resolv/include/netd_resolv/resolv_stub.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 7bfe845..0000000
--- a/resolv/include/netd_resolv/stats.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * 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
deleted file mode 100644
index be193db..0000000
--- a/resolv/libnetd_resolv.map.txt
+++ /dev/null
@@ -1,27 +0,0 @@
-#
-# 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
deleted file mode 100644
index 7fc5669..0000000
--- a/resolv/libnetd_resolv_test.cpp
+++ /dev/null
@@ -1,591 +0,0 @@
-/*
- * 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"
-
-// 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 {
-
-// 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;
- int rv = android_getaddrinfofornetcontext(nullptr /*hostname*/, nullptr /*servname*/,
- nullptr /*hints*/, &mNetcontext, &result);
- 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,
- };
-
- rv = android_getaddrinfofornetcontext("localhost", nullptr /*servname*/, &hints,
- &mNetcontext, &result);
- 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
- };
-
- int rv = android_getaddrinfofornetcontext("localhost", nullptr /*servname*/, &hints,
- &mNetcontext, &result);
- 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,
- };
-
- int rv = android_getaddrinfofornetcontext("localhost", nullptr /*servname*/, &hints,
- &mNetcontext, &result);
- 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;
- int rv = android_getaddrinfofornetcontext("localhost", config.servname, &hints,
- &mNetcontext, &result);
- 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};
- int rv = android_getaddrinfofornetcontext("v4only", nullptr, &hints, &mNetcontext, &result);
- 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};
- int rv =
- android_getaddrinfofornetcontext("sawadee", nullptr, &hints, &mNetcontext, &result);
- 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};
- int rv =
- android_getaddrinfofornetcontext(host_name, nullptr, &hints, &mNetcontext, &result);
- 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};
- int rv = android_getaddrinfofornetcontext("hello", nullptr, &hints, &mNetcontext, &result);
- 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;
- int rv = android_gethostbynamefornetcontext("jiababuei", config.ai_family, &mNetcontext,
- &hp);
- 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;
- int rv = android_gethostbynamefornetcontext("v4only", AF_INET6, &mNetcontext, &hp);
- 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;
- int rv = android_gethostbynamefornetcontext(host_name, AF_INET, &mNetcontext, &hp);
- 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;
- int rv = android_gethostbynamefornetcontext(host_name, AF_INET, &mNetcontext, &hp);
- 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
deleted file mode 100644
index f0ff564..0000000
--- a/resolv/res_cache.cpp
+++ /dev/null
@@ -1,2025 +0,0 @@
-/*
- * 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 <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/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;
-};
-
-// 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);
-}
-
-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;
- 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;
-}
-
-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_comp.cpp b/resolv/res_comp.cpp
deleted file mode 100644
index 98461b3..0000000
--- a/resolv/res_comp.cpp
+++ /dev/null
@@ -1,212 +0,0 @@
-/* $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
deleted file mode 100644
index 5b20a58..0000000
--- a/resolv/res_debug.cpp
+++ /dev/null
@@ -1,540 +0,0 @@
-/* $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_init.cpp b/resolv/res_init.cpp
deleted file mode 100644
index ceb3023..0000000
--- a/resolv/res_init.cpp
+++ /dev/null
@@ -1,397 +0,0 @@
-/* $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) {
- 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;
- }
- }
-}
diff --git a/resolv/res_mkquery.cpp b/resolv/res_mkquery.cpp
deleted file mode 100644
index 2371c7e..0000000
--- a/resolv/res_mkquery.cpp
+++ /dev/null
@@ -1,250 +0,0 @@
-/* $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
deleted file mode 100644
index fbbcc31..0000000
--- a/resolv/res_query.cpp
+++ /dev/null
@@ -1,380 +0,0 @@
-/* $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
deleted file mode 100644
index d89ad7e..0000000
--- a/resolv/res_send.cpp
+++ /dev/null
@@ -1,1307 +0,0 @@
-/* $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 "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"
-
-// TODO: use the namespace something like android::netd_resolv for libnetd_resolv
-using namespace android::net;
-using android::netdutils::Slice;
-
-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);
-
-/* BIONIC-BEGIN: implement source port randomization */
-
-// 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
-
-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);
-}
-
-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;
- cache_status = _resolv_cache_lookup(statp->netid, buf, buflen, ans, anssiz, &anslen, flags);
-
- if (cache_status == RESOLV_CACHE_FOUND) {
- HEADER* hp = (HEADER*)(void*)ans;
- *rcode = hp->rcode;
- 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;
- struct sockaddr* nsap;
- int nsaplen;
- time_t now = 0;
- int delay = 0;
- *rcode = RCODE_INTERNAL_ERROR;
- nsap = get_nsaddr(statp, (size_t) 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;
- }
- }
-
- [[maybe_unused]] static const int niflags = NI_NUMERICHOST | NI_NUMERICSERV;
- [[maybe_unused]] char abuf[NI_MAXHOST];
-
- if (getnameinfo(nsap, (socklen_t)nsaplen, abuf, sizeof(abuf), NULL, 0, niflags) == 0)
- LOG(DEBUG) << __func__ << ": Querying server (# " << ns + 1
- << ") address = " << abuf;
-
- 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);
-
- /*
- * 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);
-
- /* 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;
- }
-}
-
-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;
- const unsigned mark = statp->_mark;
-
- PrivateDnsStatus privateDnsStatus = gPrivateDnsConfiguration.getStatus(netId);
-
- 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, mark, 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) {
- res_state res = res_get_state();
- res_setnetcontext(res, netContext);
- _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
deleted file mode 100644
index 7fc77cb..0000000
--- a/resolv/res_send.h
+++ /dev/null
@@ -1,23 +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.
- */
-
-#pragma once
-
-#include "netd_resolv/resolv.h" // struct android_net_context
-
-// 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);
diff --git a/resolv/res_state.cpp b/resolv/res_state.cpp
deleted file mode 100644
index 26febff..0000000
--- a/resolv/res_state.cpp
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 6ed3398..0000000
--- a/resolv/res_state_ext.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/* $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
deleted file mode 100644
index ed6f084..0000000
--- a/resolv/res_stats.cpp
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 0056076..0000000
--- a/resolv/resolv_cache.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * 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 <vector>
-
-struct __res_state;
-
-/* 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();
-
-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
deleted file mode 100644
index 9fb0d48..0000000
--- a/resolv/resolv_private.h
+++ /dev/null
@@ -1,240 +0,0 @@
-/* $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"
-
-// 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 */
-};
-
-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*);
-
-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);
-
-#endif // NETD_RESOLV_PRIVATE_H
diff --git a/resolv/resolv_static.h b/resolv/resolv_static.h
deleted file mode 100644
index b250892..0000000
--- a/resolv/resolv_static.h
+++ /dev/null
@@ -1,33 +0,0 @@
-#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
deleted file mode 100644
index 84537ca..0000000
--- a/resolv/resolver_test.cpp
+++ /dev/null
@@ -1,3471 +0,0 @@
-/*
- * 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) {
- 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) {
- 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) {
- 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) {
- 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) {
- 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) {
- 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
deleted file mode 100644
index 2885fbc..0000000
--- a/resolv/sethostent.cpp
+++ /dev/null
@@ -1,204 +0,0 @@
-/* $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
deleted file mode 100644
index dd9992c..0000000
--- a/resolv/stats.proto
+++ /dev/null
@@ -1,249 +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.
- */
-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;
-}
-
-// 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 TransportType {
- TT_UNKNOWN = 0;
- TT_UDP = 1;
- TT_TCP = 2;
- TT_DOT = 3;
-}
-
-enum PrivateDnsModes {
- PDM_UNKNOWN = 0;
- PDM_OFF = 1;
- PDM_OPPORTUNISTIC = 2;
- PDM_STRICT = 3;
-}
-
-enum Transport {
- // Indicates this network uses a Cellular transport.
- TRANSPORT_DEFAULT = 0; // TRANSPORT_CELLULAR
- // Indicates this network uses a Wi-Fi transport.
- TRANSPORT_WIFI = 1;
- // Indicates this network uses a Bluetooth transport.
- TRANSPORT_BLUETOOTH = 2;
- // Indicates this network uses an Ethernet transport.
- TRANSPORT_ETHERNET = 3;
- // Indicates this network uses a VPN transport.
- TRANSPORT_VPN = 4;
- // Indicates this network uses a Wi-Fi Aware transport.
- TRANSPORT_WIFI_AWARE = 5;
- // Indicates this network uses a LoWPAN transport.
- TRANSPORT_LOWPAN = 6;
-}
-
-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 TransportType transport = 5;
-
- // Number of DNS query retry times
- optional int32 retry_times = 6;
-
- // Ordinal number of name server.
- optional int32 dns_server_count = 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 Transport 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;
-}
\ No newline at end of file
diff --git a/resolv/tests/BaseTestMetricsListener.cpp b/resolv/tests/BaseTestMetricsListener.cpp
deleted file mode 100644
index 414e383..0000000
--- a/resolv/tests/BaseTestMetricsListener.cpp
+++ /dev/null
@@ -1,80 +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.
- */
-
-#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
deleted file mode 100644
index 326647b..0000000
--- a/resolv/tests/BaseTestMetricsListener.h
+++ /dev/null
@@ -1,104 +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 _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
deleted file mode 100644
index 4c3d36a..0000000
--- a/resolv/tests/TestMetrics.cpp
+++ /dev/null
@@ -1,74 +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.
- */
-
-#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
deleted file mode 100644
index d3a4ad4..0000000
--- a/resolv/tests/TestMetrics.h
+++ /dev/null
@@ -1,58 +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 _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 5129e44..db9ae82 100644
--- a/server/Android.bp
+++ b/server/Android.bp
@@ -1,12 +1,92 @@
-// AIDL interface between netd and services.core
+aidl_interface {
+ name: "netd_aidl_interface",
+ local_include_dir: "binder",
+ srcs: [
+ "binder/android/net/INetd.aidl",
+ // AIDL interface that callers can implement to receive networking events from netd.
+ "binder/android/net/INetdUnsolicitedEventListener.aidl",
+ "binder/android/net/InterfaceConfigurationParcel.aidl",
+ "binder/android/net/MarkMaskParcel.aidl",
+ "binder/android/net/RouteInfoParcel.aidl",
+ "binder/android/net/TetherConfigParcel.aidl",
+ "binder/android/net/TetherOffloadRuleParcel.aidl",
+ "binder/android/net/TetherStatsParcel.aidl",
+ "binder/android/net/UidRangeParcel.aidl",
+ ],
+ backend: {
+ cpp: {
+ gen_log: true,
+ },
+ java: {
+ // TODO: Remove apex_available and restrict visibility to only mainline modules that are
+ // either outside the system server or use jarjar to rename the generated AIDL classes.
+ apex_available: [
+ "//apex_available:platform", // used from services.net
+ "com.android.bluetooth.updatable",
+ "com.android.tethering",
+ "com.android.wifi",
+ ],
+ },
+ },
+ versions: [
+ "1",
+ "2",
+ "3",
+ "4",
+ ],
+}
+
aidl_interface {
name: "netd_event_listener_interface",
local_include_dir: "binder",
srcs: [
"binder/android/net/metrics/INetdEventListener.aidl",
],
- api_dir: "aidl/netdeventlistener",
versions: ["1"],
+ backend: {
+ ndk: {
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.resolv",
+ ],
+ min_sdk_version: "29",
+ },
+ },
+}
+
+aidl_interface {
+ // This interface is for OEM calls to netd and vice versa that do not exist in AOSP.
+ // Those calls cannot be part of INetd.aidl and INetdUnsolicitedEventListener.aidl
+ // because those interfaces are versioned.
+ // These interfaces must never be versioned or OEMs will not be able to change them.
+ name: "oemnetd_aidl_interface",
+ unstable: true,
+ local_include_dir: "binder",
+ srcs: [
+ "binder/com/android/internal/net/IOemNetd.aidl",
+ "binder/com/android/internal/net/IOemNetdUnsolicitedEventListener.aidl",
+ ],
+}
+
+// Convenience build target for the version of the netd stable AIDL interface used by the platform.
+// This exists to ensure that all non-updatable code in the system server uses the same version.
+// Mixing different versions of generated classes results in code non-deterministically(?) using one
+// of the compiled-in versions, and potentially crashing when code compiled against a newer version
+// ends up using a generated class from an older version and calls methods that don't exist.
+// This must be a frozen version on REL builds and can be -unstable on development builds.
+// See http://b/143560726 for an example.
+java_library {
+ name: "netd_aidl_interfaces-platform-java",
+ static_libs: [
+ "netd_aidl_interface-java",
+ "netd_event_listener_interface-java",
+ ],
+ // TODO: remove bluetooth, which doesn't actually use netd at all.
+ apex_available: [
+ "//apex_available:platform", // due to the dependency from services.net
+ "com.android.bluetooth.updatable",
+ ],
+ sdk_version: "system_current",
}
// These are used in netd_integration_test
@@ -18,47 +98,13 @@
"InterfaceController.cpp",
"NetlinkCommands.cpp",
"NetlinkListener.cpp",
+ "OffloadUtils.cpp",
+ "SockDiag.cpp",
"XfrmController.cpp",
"TrafficController.cpp",
],
}
-aidl_interface {
- name: "netd_aidl_interface",
- local_include_dir: "binder",
- srcs: [
- "binder/android/net/INetd.aidl",
- // AIDL interface that callers can implement to receive networking events from netd.
- "binder/android/net/INetdUnsolicitedEventListener.aidl",
- "binder/android/net/InterfaceConfigurationParcel.aidl",
- "binder/android/net/TetherStatsParcel.aidl",
- "binder/android/net/UidRangeParcel.aidl",
- ],
- api_dir: "aidl/netd",
- backend: {
- cpp: {
- gen_log: true,
- },
- },
- versions: [
- "1",
- "2",
- ],
-}
-
-aidl_interface {
- // This interface is for OEM calls to netd and vice versa that do not exist in AOSP.
- // Those calls cannot be part of INetd.aidl and INetdUnsolicitedEventListener.aidl
- // because those interfaces are versioned.
- // These interfaces must never be versioned or OEMs will not be able to change them.
- name: "oemnetd_aidl_interface",
- local_include_dir: "binder",
- srcs: [
- "binder/com/android/internal/net/IOemNetd.aidl",
- "binder/com/android/internal/net/IOemNetdUnsolicitedEventListener.aidl",
- ],
-}
-
// Modules common to both netd and netd_unit_test
cc_library_static {
name: "libnetd_server",
@@ -70,7 +116,6 @@
srcs: [
"BandwidthController.cpp",
"ClatdController.cpp",
- "ClatUtils.cpp",
"Controllers.cpp",
"NetdConstants.cpp",
"FirewallController.cpp",
@@ -81,6 +126,7 @@
"NetlinkCommands.cpp",
"NetlinkListener.cpp",
"NetlinkManager.cpp",
+ "OffloadUtils.cpp",
"RouteController.cpp",
"SockDiag.cpp",
"StrictController.cpp",
@@ -95,18 +141,14 @@
"libbpf_android",
"libbase",
"libbinder",
- "liblogwrap",
"libnetdbpf",
"libnetutils",
"libnetdutils",
"libpcap",
"libqtaguid",
"libssl",
- "netd_aidl_interface-V2-cpp",
- "netd_event_listener_interface-V1-cpp",
- ],
- header_libs: [
- "libnetd_resolv_headers",
+ "netd_aidl_interface-cpp",
+ "netd_event_listener_interface-cpp",
],
aidl: {
export_aidl_headers: true,
@@ -122,6 +164,12 @@
"system/netd/include",
],
init_rc: ["netd.rc"],
+ required: [
+ "bpfloader",
+ "clatd.o",
+ "netd.o",
+ "offload.o",
+ ],
shared_libs: [
"android.system.net.netd@1.0",
"android.system.net.netd@1.1",
@@ -131,11 +179,10 @@
"libcutils",
"libdl",
"libhidlbase",
- "libhidltransport",
"libjsoncpp",
"liblog",
- "liblogwrap",
"libmdnssd",
+ "libnetd_resolv",
"libnetdbpf",
"libnetdutils",
"libnetutils",
@@ -145,16 +192,13 @@
"libselinux",
"libsysutils",
"libutils",
- "netd_aidl_interface-V2-cpp",
- "netd_event_listener_interface-V1-cpp",
+ "netd_aidl_interface-cpp",
+ "netd_event_listener_interface-cpp",
"oemnetd_aidl_interface-cpp",
],
static_libs: [
"libnetd_server",
],
- header_libs: [
- "libnetd_resolv_headers",
- ],
srcs: [
"DummyNetwork.cpp",
"EventReporter.cpp",
@@ -171,11 +215,13 @@
"PhysicalNetwork.cpp",
"PppController.cpp",
"Process.cpp",
- "ResolvStub.cpp",
"VirtualNetwork.cpp",
"main.cpp",
"oem_iptables_hook.cpp",
],
+ sanitize: {
+ cfi: true,
+ },
}
cc_binary {
@@ -195,30 +241,32 @@
"liblog",
"libutils",
"libbinder",
- "dnsresolver_aidl_interface-V2-cpp",
- "netd_aidl_interface-V2-cpp",
+ "dnsresolver_aidl_interface-cpp",
+ "netd_aidl_interface-cpp",
],
srcs: [
"ndc.cpp",
"UidRanges.cpp",
"NdcDispatcher.cpp",
],
+ sanitize: {
+ cfi: true,
+ },
}
cc_test {
name: "netd_unit_test",
defaults: ["netd_defaults"],
test_suites: ["device-tests"],
+ require_root: true,
include_dirs: [
"system/netd/include",
"system/netd/server/binder",
"system/netd/tests",
- "system/core/logwrapper/include",
],
srcs: [
"BandwidthControllerTest.cpp",
"ClatdControllerTest.cpp",
- "ClatUtilsTest.cpp",
"ControllersTest.cpp",
"FirewallControllerTest.cpp",
"IdletimerControllerTest.cpp",
@@ -226,6 +274,7 @@
"IptablesBaseTest.cpp",
"IptablesRestoreControllerTest.cpp",
"NFLogListenerTest.cpp",
+ "OffloadUtilsTest.cpp",
"RouteControllerTest.cpp",
"SockDiagTest.cpp",
"StrictControllerTest.cpp",
@@ -238,6 +287,9 @@
"libgmock",
"libnetd_server",
"libnetd_test_tun_interface",
+ "libqtaguid",
+ "netd_aidl_interface-unstable-cpp",
+ "netd_event_listener_interface-cpp",
],
shared_libs: [
"libbase",
@@ -249,10 +301,7 @@
"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
deleted file mode 100644
index 2501d78..0000000
--- a/server/AndroidTest.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?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 7e9bff7..e1ce56f 100644
--- a/server/BandwidthController.cpp
+++ b/server/BandwidthController.cpp
@@ -48,7 +48,6 @@
#define LOG_TAG "BandwidthController"
#include <cutils/properties.h>
#include <log/log.h>
-#include <logwrap/logwrap.h>
#include <netdutils/Syscalls.h>
#include "BandwidthController.h"
@@ -91,7 +90,7 @@
* Some comments about the rules:
* * Ordering
* - when an interface is marked as costly it should be INSERTED into the INPUT/OUTPUT chains.
- * E.g. "-I bw_INPUT -i rmnet0 --jump costly"
+ * E.g. "-I bw_INPUT -i rmnet0 -j costly"
* - quota'd rules in the costly chain should be before bw_penalty_box lookups.
* - the qtaguid counting is done at the end of the bw_INPUT/bw_OUTPUT user chains.
*
@@ -99,26 +98,26 @@
* - global quota for all costly interfaces uses a single costly chain:
* . initial rules
* iptables -N bw_costly_shared
- * iptables -I bw_INPUT -i iface0 --jump bw_costly_shared
- * iptables -I bw_OUTPUT -o iface0 --jump bw_costly_shared
+ * iptables -I bw_INPUT -i iface0 -j bw_costly_shared
+ * iptables -I bw_OUTPUT -o iface0 -j bw_costly_shared
* iptables -I bw_costly_shared -m quota \! --quota 500000 \
- * --jump REJECT --reject-with icmp-net-prohibited
- * iptables -A bw_costly_shared --jump bw_penalty_box
- * iptables -A bw_penalty_box --jump bw_happy_box
- * iptables -A bw_happy_box --jump bw_data_saver
+ * -j REJECT --reject-with icmp-net-prohibited
+ * iptables -A bw_costly_shared -j bw_penalty_box
+ * iptables -A bw_penalty_box -j bw_happy_box
+ * iptables -A bw_happy_box -j bw_data_saver
*
* . adding a new iface to this, E.g.:
- * iptables -I bw_INPUT -i iface1 --jump bw_costly_shared
- * iptables -I bw_OUTPUT -o iface1 --jump bw_costly_shared
+ * iptables -I bw_INPUT -i iface1 -j bw_costly_shared
+ * iptables -I bw_OUTPUT -o iface1 -j bw_costly_shared
*
* - quota per interface. This is achieve by having "costly" chains per quota.
* E.g. adding a new costly interface iface0 with its own quota:
* iptables -N bw_costly_iface0
- * iptables -I bw_INPUT -i iface0 --jump bw_costly_iface0
- * iptables -I bw_OUTPUT -o iface0 --jump bw_costly_iface0
+ * iptables -I bw_INPUT -i iface0 -j bw_costly_iface0
+ * iptables -I bw_OUTPUT -o iface0 -j bw_costly_iface0
* iptables -A bw_costly_iface0 -m quota \! --quota 500000 \
- * --jump REJECT --reject-with icmp-port-unreachable
- * iptables -A bw_costly_iface0 --jump bw_penalty_box
+ * -j REJECT --reject-with icmp-port-unreachable
+ * iptables -A bw_costly_iface0 -j bw_penalty_box
*
* * Penalty box, happy box and data saver.
* - bw_penalty box is a blacklist of apps that are rejected.
@@ -133,25 +132,25 @@
* - only one bw_penalty_box for all interfaces
* E.g Adding an app:
* iptables -I bw_penalty_box -m owner --uid-owner app_3 \
- * --jump REJECT --reject-with icmp-port-unreachable
+ * -j REJECT --reject-with icmp-port-unreachable
*
* * bw_happy_box handling:
* - The bw_happy_box comes after the penalty box.
* E.g Adding a happy app,
* iptables -I bw_happy_box -m owner --uid-owner app_3 \
- * --jump RETURN
+ * -j RETURN
*
* * bw_data_saver handling:
* - The bw_data_saver comes after the happy box.
* Enable data saver:
- * iptables -R 1 bw_data_saver --jump REJECT --reject-with icmp-port-unreachable
+ * iptables -R 1 bw_data_saver -j REJECT --reject-with icmp-port-unreachable
* Disable data saver:
- * iptables -R 1 bw_data_saver --jump RETURN
+ * iptables -R 1 bw_data_saver -j RETURN
*/
const std::string COMMIT_AND_CLOSE = "COMMIT\n";
const std::string HAPPY_BOX_MATCH_WHITELIST_COMMAND =
- StringPrintf("-I bw_happy_box -m owner --uid-owner %d-%d --jump RETURN", 0, MAX_SYSTEM_UID);
+ StringPrintf("-I bw_happy_box -m owner --uid-owner %d-%d -j RETURN", 0, MAX_SYSTEM_UID);
const std::string BPF_HAPPY_BOX_MATCH_WHITELIST_COMMAND = StringPrintf(
"-I bw_happy_box -m bpf --object-pinned %s -j RETURN", XT_BPF_WHITELIST_PROG_PATH);
const std::string BPF_PENALTY_BOX_MATCH_BLACKLIST_COMMAND = StringPrintf(
@@ -213,8 +212,8 @@
* See go/ipsec-data-accounting for more information.
*/
-const std::vector<std::string> getBasicAccountingCommands(const bool useBpf) {
- const std::vector<std::string> ipt_basic_accounting_commands = {
+std::vector<std::string> getBasicAccountingCommands(const bool useBpf) {
+ std::vector<std::string> ipt_basic_accounting_commands = {
"*filter",
"-A bw_INPUT -j bw_global_alert",
@@ -222,19 +221,27 @@
"-A bw_INPUT -p esp -j RETURN",
StringPrintf("-A bw_INPUT -m mark --mark 0x%x/0x%x -j RETURN", uidBillingMask,
uidBillingMask),
+ // This is ingress application UID xt_qtaguid (pre-ebpf) accounting,
+ // for bpf this is handled out of cgroup hooks instead.
useBpf ? "" : "-A bw_INPUT -m owner --socket-exists",
StringPrintf("-A bw_INPUT -j MARK --or-mark 0x%x", uidBillingMask),
"-A bw_OUTPUT -j bw_global_alert",
// Prevents IPSec double counting (Tunnel mode and Transport mode,
// respectively)
- "-A bw_OUTPUT -o " IPSEC_IFACE_PREFIX "+ -j RETURN",
- "-A bw_OUTPUT -m policy --pol ipsec --dir out -j RETURN",
+ useBpf ? "" : "-A bw_OUTPUT -o " IPSEC_IFACE_PREFIX "+ -j RETURN",
+ useBpf ? "" : "-A bw_OUTPUT -m policy --pol ipsec --dir out -j RETURN",
+ // Don't count clat traffic, as it has already been counted (and subject to
+ // costly / happy_box / data_saver / penalty_box etc. based on the real UID)
+ // on the stacked interface.
+ useBpf ? "" : "-A bw_OUTPUT -m owner --uid-owner clat -j RETURN",
+ // This is egress application UID xt_qtaguid (pre-ebpf) accounting,
+ // for bpf this is handled out of cgroup hooks instead.
useBpf ? "" : "-A bw_OUTPUT -m owner --socket-exists",
- "-A bw_costly_shared --jump bw_penalty_box",
+ "-A bw_costly_shared -j bw_penalty_box",
useBpf ? BPF_PENALTY_BOX_MATCH_BLACKLIST_COMMAND : "",
- "-A bw_penalty_box --jump bw_happy_box", "-A bw_happy_box --jump bw_data_saver",
+ "-A bw_penalty_box -j bw_happy_box", "-A bw_happy_box -j bw_data_saver",
"-A bw_data_saver -j RETURN",
useBpf ? BPF_HAPPY_BOX_MATCH_WHITELIST_COMMAND : HAPPY_BOX_MATCH_WHITELIST_COMMAND,
"COMMIT",
@@ -242,29 +249,42 @@
"*raw",
// Prevents IPSec double counting (Tunnel mode and Transport mode,
// respectively)
- "-A bw_raw_PREROUTING -i " IPSEC_IFACE_PREFIX "+ -j RETURN",
+ ("-A bw_raw_PREROUTING -i " IPSEC_IFACE_PREFIX "+ -j RETURN"),
"-A bw_raw_PREROUTING -m policy --pol ipsec --dir in -j RETURN",
- useBpf ? StringPrintf("-A bw_raw_PREROUTING -m bpf --object-pinned %s",
- XT_BPF_INGRESS_PROG_PATH)
+ // This is ingress interface accounting. There is no need to do anything specific
+ // for 464xlat here, because we only ever account 464xlat traffic on the clat
+ // interface and later correct for overhead (+20 bytes/packet).
+ //
+ // Note: eBPF offloaded packets never hit base interface's ip6tables, and non
+ // offloaded packets (which when using xt_qtaguid means all packets, because
+ // clat eBPF offload does not work on xt_qtaguid devices) are dropped in
+ // clat_raw_PREROUTING.
+ //
+ // Hence we will never double count and additional corrections are not needed.
+ // We can simply take the sum of base and stacked (+20B/pkt) interface counts.
+ useBpf ? "-A bw_raw_PREROUTING -m bpf --object-pinned " XT_BPF_INGRESS_PROG_PATH
: "-A bw_raw_PREROUTING -m owner --socket-exists",
"COMMIT",
"*mangle",
// Prevents IPSec double counting (Tunnel mode and Transport mode,
// respectively)
- "-A bw_mangle_POSTROUTING -o " IPSEC_IFACE_PREFIX "+ -j RETURN",
+ ("-A bw_mangle_POSTROUTING -o " IPSEC_IFACE_PREFIX "+ -j RETURN"),
"-A bw_mangle_POSTROUTING -m policy --pol ipsec --dir out -j RETURN",
- useBpf ? "" : "-A bw_mangle_POSTROUTING -m owner --socket-exists",
- StringPrintf("-A bw_mangle_POSTROUTING -j MARK --set-mark 0x0/0x%x",
- uidBillingMask), // Clear the mark before sending this packet
- useBpf ? StringPrintf("-A bw_mangle_POSTROUTING -m bpf --object-pinned %s",
- XT_BPF_EGRESS_PROG_PATH)
- : "",
+ // Clear the uid billing done (egress) mark before sending this packet
+ StringPrintf("-A bw_mangle_POSTROUTING -j MARK --set-mark 0x0/0x%x", uidBillingMask),
+ // Packets from the clat daemon have already been counted on egress through the
+ // stacked v4-* interface.
+ "-A bw_mangle_POSTROUTING -m owner --uid-owner clat -j RETURN",
+ // This is egress interface accounting: we account 464xlat traffic only on
+ // the clat interface (as offloaded packets never hit base interface's ip6tables)
+ // and later sum base and stacked with overhead (+20B/pkt) in higher layers
+ useBpf ? "-A bw_mangle_POSTROUTING -m bpf --object-pinned " XT_BPF_EGRESS_PROG_PATH
+ : "-A bw_mangle_POSTROUTING -m owner --socket-exists",
COMMIT_AND_CLOSE};
return ipt_basic_accounting_commands;
}
-
std::vector<std::string> toStrVec(int num, const char* const strs[]) {
return std::vector<std::string>(strs, strs + num);
}
@@ -414,15 +434,14 @@
if (it == mSharedQuotaIfaces.end()) {
const int ruleInsertPos = (mGlobalAlertBytes) ? 2 : 1;
std::vector<std::string> cmds = {
- "*filter",
- StringPrintf("-I bw_INPUT %d -i %s --jump %s", ruleInsertPos, iface.c_str(), chain),
- StringPrintf("-I bw_OUTPUT %d -o %s --jump %s", ruleInsertPos, iface.c_str(), chain),
- StringPrintf("-A bw_FORWARD -i %s --jump %s", iface.c_str(), chain),
- StringPrintf("-A bw_FORWARD -o %s --jump %s", iface.c_str(), chain),
+ "*filter",
+ StringPrintf("-I bw_INPUT %d -i %s -j %s", ruleInsertPos, iface.c_str(), chain),
+ StringPrintf("-I bw_OUTPUT %d -o %s -j %s", ruleInsertPos, iface.c_str(), chain),
+ StringPrintf("-A bw_FORWARD -i %s -j %s", iface.c_str(), chain),
+ StringPrintf("-A bw_FORWARD -o %s -j %s", iface.c_str(), chain),
};
if (mSharedQuotaIfaces.empty()) {
- cmds.push_back(StringPrintf("-I %s -m quota2 ! --quota %" PRId64
- " --name %s --jump REJECT",
+ cmds.push_back(StringPrintf("-I %s -m quota2 ! --quota %" PRId64 " --name %s -j REJECT",
chain, maxBytes, cost));
}
cmds.push_back("COMMIT\n");
@@ -465,15 +484,14 @@
}
std::vector<std::string> cmds = {
- "*filter",
- StringPrintf("-D bw_INPUT -i %s --jump %s", iface.c_str(), chain),
- StringPrintf("-D bw_OUTPUT -o %s --jump %s", iface.c_str(), chain),
- StringPrintf("-D bw_FORWARD -i %s --jump %s", iface.c_str(), chain),
- StringPrintf("-D bw_FORWARD -o %s --jump %s", iface.c_str(), chain),
+ "*filter",
+ StringPrintf("-D bw_INPUT -i %s -j %s", iface.c_str(), chain),
+ StringPrintf("-D bw_OUTPUT -o %s -j %s", iface.c_str(), chain),
+ StringPrintf("-D bw_FORWARD -i %s -j %s", iface.c_str(), chain),
+ StringPrintf("-D bw_FORWARD -o %s -j %s", iface.c_str(), chain),
};
if (mSharedQuotaIfaces.size() == 1) {
- cmds.push_back(StringPrintf("-D %s -m quota2 ! --quota %" PRIu64
- " --name %s --jump REJECT",
+ cmds.push_back(StringPrintf("-D %s -m quota2 ! --quota %" PRIu64 " --name %s -j REJECT",
chain, mSharedQuotaBytes, cost));
}
cmds.push_back("COMMIT\n");
@@ -528,18 +546,17 @@
const std::string chain = "bw_costly_" + iface;
const int ruleInsertPos = (mGlobalAlertBytes) ? 2 : 1;
std::vector<std::string> cmds = {
- "*filter",
- StringPrintf(":%s -", chain.c_str()),
- StringPrintf("-A %s -j bw_penalty_box", chain.c_str()),
- StringPrintf("-I bw_INPUT %d -i %s --jump %s", ruleInsertPos, iface.c_str(),
- chain.c_str()),
- StringPrintf("-I bw_OUTPUT %d -o %s --jump %s", ruleInsertPos, iface.c_str(),
- chain.c_str()),
- StringPrintf("-A bw_FORWARD -i %s --jump %s", iface.c_str(), chain.c_str()),
- StringPrintf("-A bw_FORWARD -o %s --jump %s", iface.c_str(), chain.c_str()),
- StringPrintf("-A %s -m quota2 ! --quota %" PRId64 " --name %s --jump REJECT",
- chain.c_str(), maxBytes, cost.c_str()),
- "COMMIT\n",
+ "*filter",
+ StringPrintf(":%s -", chain.c_str()),
+ StringPrintf("-A %s -j bw_penalty_box", chain.c_str()),
+ StringPrintf("-I bw_INPUT %d -i %s -j %s", ruleInsertPos, iface.c_str(), chain.c_str()),
+ StringPrintf("-I bw_OUTPUT %d -o %s -j %s", ruleInsertPos, iface.c_str(),
+ chain.c_str()),
+ StringPrintf("-A bw_FORWARD -i %s -j %s", iface.c_str(), chain.c_str()),
+ StringPrintf("-A bw_FORWARD -o %s -j %s", iface.c_str(), chain.c_str()),
+ StringPrintf("-A %s -m quota2 ! --quota %" PRId64 " --name %s -j REJECT", chain.c_str(),
+ maxBytes, cost.c_str()),
+ "COMMIT\n",
};
if (iptablesRestoreFunction(V4V6, Join(cmds, "\n"), nullptr) != 0) {
ALOGE("Failed set quota rule");
@@ -587,14 +604,14 @@
const std::string chain = "bw_costly_" + iface;
std::vector<std::string> cmds = {
- "*filter",
- StringPrintf("-D bw_INPUT -i %s --jump %s", iface.c_str(), chain.c_str()),
- StringPrintf("-D bw_OUTPUT -o %s --jump %s", iface.c_str(), chain.c_str()),
- StringPrintf("-D bw_FORWARD -i %s --jump %s", iface.c_str(), chain.c_str()),
- StringPrintf("-D bw_FORWARD -o %s --jump %s", iface.c_str(), chain.c_str()),
- StringPrintf("-F %s", chain.c_str()),
- StringPrintf("-X %s", chain.c_str()),
- "COMMIT\n",
+ "*filter",
+ StringPrintf("-D bw_INPUT -i %s -j %s", iface.c_str(), chain.c_str()),
+ StringPrintf("-D bw_OUTPUT -o %s -j %s", iface.c_str(), chain.c_str()),
+ StringPrintf("-D bw_FORWARD -i %s -j %s", iface.c_str(), chain.c_str()),
+ StringPrintf("-D bw_FORWARD -o %s -j %s", iface.c_str(), chain.c_str()),
+ StringPrintf("-F %s", chain.c_str()),
+ StringPrintf("-X %s", chain.c_str()),
+ "COMMIT\n",
};
const int res = iptablesRestoreFunction(V4V6, Join(cmds, "\n"), nullptr);
@@ -860,8 +877,8 @@
case IptJumpNoAdd:
return "";
case IptJumpReject:
- return " --jump REJECT";
+ return " -j REJECT";
case IptJumpReturn:
- return " --jump RETURN";
+ return " -j RETURN";
}
}
diff --git a/server/BandwidthControllerTest.cpp b/server/BandwidthControllerTest.cpp
index 9337ac5..115a0da 100644
--- a/server/BandwidthControllerTest.cpp
+++ b/server/BandwidthControllerTest.cpp
@@ -60,13 +60,14 @@
"-A bw_OUTPUT -j bw_global_alert\n"
"-A bw_OUTPUT -o ipsec+ -j RETURN\n"
"-A bw_OUTPUT -m policy --pol ipsec --dir out -j RETURN\n"
+ "-A bw_OUTPUT -m owner --uid-owner clat -j RETURN\n"
"-A bw_OUTPUT -m owner --socket-exists\n"
- "-A bw_costly_shared --jump bw_penalty_box\n"
+ "-A bw_costly_shared -j bw_penalty_box\n"
"\n"
- "-A bw_penalty_box --jump bw_happy_box\n"
- "-A bw_happy_box --jump bw_data_saver\n"
+ "-A bw_penalty_box -j bw_happy_box\n"
+ "-A bw_happy_box -j bw_data_saver\n"
"-A bw_data_saver -j RETURN\n"
- "-I bw_happy_box -m owner --uid-owner 0-9999 --jump RETURN\n"
+ "-I bw_happy_box -m owner --uid-owner 0-9999 -j RETURN\n"
"COMMIT\n"
"*raw\n"
"-A bw_raw_PREROUTING -i ipsec+ -j RETURN\n"
@@ -76,9 +77,9 @@
"*mangle\n"
"-A bw_mangle_POSTROUTING -o ipsec+ -j RETURN\n"
"-A bw_mangle_POSTROUTING -m policy --pol ipsec --dir out -j RETURN\n"
- "-A bw_mangle_POSTROUTING -m owner --socket-exists\n"
"-A bw_mangle_POSTROUTING -j MARK --set-mark 0x0/0x100000\n"
- "\n"
+ "-A bw_mangle_POSTROUTING -m owner --uid-owner clat -j RETURN\n"
+ "-A bw_mangle_POSTROUTING -m owner --socket-exists\n"
"COMMIT\n";
const std::string ACCOUNT_RULES_WITH_BPF =
@@ -89,14 +90,15 @@
"\n"
"-A bw_INPUT -j MARK --or-mark 0x100000\n"
"-A bw_OUTPUT -j bw_global_alert\n"
- "-A bw_OUTPUT -o ipsec+ -j RETURN\n"
- "-A bw_OUTPUT -m policy --pol ipsec --dir out -j RETURN\n"
"\n"
- "-A bw_costly_shared --jump bw_penalty_box\n" +
+ "\n"
+ "\n"
+ "\n"
+ "-A bw_costly_shared -j bw_penalty_box\n" +
StringPrintf("-I bw_penalty_box -m bpf --object-pinned %s -j REJECT\n",
XT_BPF_BLACKLIST_PROG_PATH) +
- "-A bw_penalty_box --jump bw_happy_box\n"
- "-A bw_happy_box --jump bw_data_saver\n"
+ "-A bw_penalty_box -j bw_happy_box\n"
+ "-A bw_happy_box -j bw_data_saver\n"
"-A bw_data_saver -j RETURN\n" +
StringPrintf("-I bw_happy_box -m bpf --object-pinned %s -j RETURN\n",
XT_BPF_WHITELIST_PROG_PATH) +
@@ -109,8 +111,8 @@
"*mangle\n"
"-A bw_mangle_POSTROUTING -o ipsec+ -j RETURN\n"
"-A bw_mangle_POSTROUTING -m policy --pol ipsec --dir out -j RETURN\n"
- "\n"
- "-A bw_mangle_POSTROUTING -j MARK --set-mark 0x0/0x100000\n" +
+ "-A bw_mangle_POSTROUTING -j MARK --set-mark 0x0/0x100000\n"
+ "-A bw_mangle_POSTROUTING -m owner --uid-owner clat -j RETURN\n" +
StringPrintf("-A bw_mangle_POSTROUTING -m bpf --object-pinned %s\n",
XT_BPF_EGRESS_PROG_PATH) +
"COMMIT\n";
@@ -276,21 +278,21 @@
TEST_F(BandwidthControllerTest, TestEnableDataSaver) {
mBw.enableDataSaver(true);
std::string expected4 =
- "*filter\n"
- ":bw_data_saver -\n"
- "-A bw_data_saver --jump REJECT\n"
- "COMMIT\n";
+ "*filter\n"
+ ":bw_data_saver -\n"
+ "-A bw_data_saver -j REJECT\n"
+ "COMMIT\n";
std::string expected6 =
- "*filter\n"
- ":bw_data_saver -\n"
- "-A bw_data_saver -p icmpv6 --icmpv6-type packet-too-big -j RETURN\n"
- "-A bw_data_saver -p icmpv6 --icmpv6-type router-solicitation -j RETURN\n"
- "-A bw_data_saver -p icmpv6 --icmpv6-type router-advertisement -j RETURN\n"
- "-A bw_data_saver -p icmpv6 --icmpv6-type neighbour-solicitation -j RETURN\n"
- "-A bw_data_saver -p icmpv6 --icmpv6-type neighbour-advertisement -j RETURN\n"
- "-A bw_data_saver -p icmpv6 --icmpv6-type redirect -j RETURN\n"
- "-A bw_data_saver --jump REJECT\n"
- "COMMIT\n";
+ "*filter\n"
+ ":bw_data_saver -\n"
+ "-A bw_data_saver -p icmpv6 --icmpv6-type packet-too-big -j RETURN\n"
+ "-A bw_data_saver -p icmpv6 --icmpv6-type router-solicitation -j RETURN\n"
+ "-A bw_data_saver -p icmpv6 --icmpv6-type router-advertisement -j RETURN\n"
+ "-A bw_data_saver -p icmpv6 --icmpv6-type neighbour-solicitation -j RETURN\n"
+ "-A bw_data_saver -p icmpv6 --icmpv6-type neighbour-advertisement -j RETURN\n"
+ "-A bw_data_saver -p icmpv6 --icmpv6-type redirect -j RETURN\n"
+ "-A bw_data_saver -j REJECT\n"
+ "COMMIT\n";
expectIptablesRestoreCommands({
{V4, expected4},
{V6, expected6},
@@ -298,11 +300,10 @@
mBw.enableDataSaver(false);
std::string expected = {
- "*filter\n"
- ":bw_data_saver -\n"
- "-A bw_data_saver --jump RETURN\n"
- "COMMIT\n"
- };
+ "*filter\n"
+ ":bw_data_saver -\n"
+ "-A bw_data_saver -j RETURN\n"
+ "COMMIT\n"};
expectIptablesRestoreCommands({
{V4, expected},
{V6, expected},
@@ -315,16 +316,16 @@
const char* c_chain = chain.c_str();
const char* c_iface = iface.c_str();
std::vector<std::string> cmds = {
- "*filter",
- StringPrintf(":%s -", c_chain),
- StringPrintf("-A %s -j bw_penalty_box", c_chain),
- StringPrintf("-I bw_INPUT %d -i %s --jump %s", ruleIndex, c_iface, c_chain),
- StringPrintf("-I bw_OUTPUT %d -o %s --jump %s", ruleIndex, c_iface, c_chain),
- StringPrintf("-A bw_FORWARD -i %s --jump %s", c_iface, c_chain),
- StringPrintf("-A bw_FORWARD -o %s --jump %s", c_iface, c_chain),
- StringPrintf("-A %s -m quota2 ! --quota %" PRIu64 " --name %s --jump REJECT", c_chain,
- quota, c_iface),
- "COMMIT\n",
+ "*filter",
+ StringPrintf(":%s -", c_chain),
+ StringPrintf("-A %s -j bw_penalty_box", c_chain),
+ StringPrintf("-I bw_INPUT %d -i %s -j %s", ruleIndex, c_iface, c_chain),
+ StringPrintf("-I bw_OUTPUT %d -o %s -j %s", ruleIndex, c_iface, c_chain),
+ StringPrintf("-A bw_FORWARD -i %s -j %s", c_iface, c_chain),
+ StringPrintf("-A bw_FORWARD -o %s -j %s", c_iface, c_chain),
+ StringPrintf("-A %s -m quota2 ! --quota %" PRIu64 " --name %s -j REJECT", c_chain,
+ quota, c_iface),
+ "COMMIT\n",
};
return {Join(cmds, "\n")};
}
@@ -334,14 +335,14 @@
const char* c_chain = chain.c_str();
const char* c_iface = iface.c_str();
std::vector<std::string> cmds = {
- "*filter",
- StringPrintf("-D bw_INPUT -i %s --jump %s", c_iface, c_chain),
- StringPrintf("-D bw_OUTPUT -o %s --jump %s", c_iface, c_chain),
- StringPrintf("-D bw_FORWARD -i %s --jump %s", c_iface, c_chain),
- StringPrintf("-D bw_FORWARD -o %s --jump %s", c_iface, c_chain),
- StringPrintf("-F %s", c_chain),
- StringPrintf("-X %s", c_chain),
- "COMMIT\n",
+ "*filter",
+ StringPrintf("-D bw_INPUT -i %s -j %s", c_iface, c_chain),
+ StringPrintf("-D bw_OUTPUT -o %s -j %s", c_iface, c_chain),
+ StringPrintf("-D bw_FORWARD -i %s -j %s", c_iface, c_chain),
+ StringPrintf("-D bw_FORWARD -o %s -j %s", c_iface, c_chain),
+ StringPrintf("-F %s", c_chain),
+ StringPrintf("-X %s", c_chain),
+ "COMMIT\n",
};
return {Join(cmds, "\n")};
}
@@ -372,15 +373,15 @@
const char* c_chain = chain.c_str();
const char* c_iface = iface.c_str();
std::vector<std::string> cmds = {
- "*filter",
- StringPrintf("-I bw_INPUT %d -i %s --jump %s", ruleIndex, c_iface, c_chain),
- StringPrintf("-I bw_OUTPUT %d -o %s --jump %s", ruleIndex, c_iface, c_chain),
- StringPrintf("-A bw_FORWARD -i %s --jump %s", c_iface, c_chain),
- StringPrintf("-A bw_FORWARD -o %s --jump %s", c_iface, c_chain),
+ "*filter",
+ StringPrintf("-I bw_INPUT %d -i %s -j %s", ruleIndex, c_iface, c_chain),
+ StringPrintf("-I bw_OUTPUT %d -o %s -j %s", ruleIndex, c_iface, c_chain),
+ StringPrintf("-A bw_FORWARD -i %s -j %s", c_iface, c_chain),
+ StringPrintf("-A bw_FORWARD -o %s -j %s", c_iface, c_chain),
};
if (insertQuota) {
- cmds.push_back(StringPrintf(
- "-I %s -m quota2 ! --quota %" PRIu64 " --name shared --jump REJECT", c_chain, quota));
+ cmds.push_back(StringPrintf("-I %s -m quota2 ! --quota %" PRIu64 " --name shared -j REJECT",
+ c_chain, quota));
}
cmds.push_back("COMMIT\n");
return {Join(cmds, "\n")};
@@ -392,15 +393,15 @@
const char* c_chain = chain.c_str();
const char* c_iface = iface.c_str();
std::vector<std::string> cmds = {
- "*filter",
- StringPrintf("-D bw_INPUT -i %s --jump %s", c_iface, c_chain),
- StringPrintf("-D bw_OUTPUT -o %s --jump %s", c_iface, c_chain),
- StringPrintf("-D bw_FORWARD -i %s --jump %s", c_iface, c_chain),
- StringPrintf("-D bw_FORWARD -o %s --jump %s", c_iface, c_chain),
+ "*filter",
+ StringPrintf("-D bw_INPUT -i %s -j %s", c_iface, c_chain),
+ StringPrintf("-D bw_OUTPUT -o %s -j %s", c_iface, c_chain),
+ StringPrintf("-D bw_FORWARD -i %s -j %s", c_iface, c_chain),
+ StringPrintf("-D bw_FORWARD -o %s -j %s", c_iface, c_chain),
};
if (deleteQuota) {
- cmds.push_back(StringPrintf(
- "-D %s -m quota2 ! --quota %" PRIu64 " --name shared --jump REJECT", c_chain, quota));
+ cmds.push_back(StringPrintf("-D %s -m quota2 ! --quota %" PRIu64 " --name shared -j REJECT",
+ c_chain, quota));
}
cmds.push_back("COMMIT\n");
return {Join(cmds, "\n")};
@@ -515,22 +516,20 @@
std::vector<const char *> appUids = { "1000", "1001", "10012" };
std::vector<std::string> expected = {
- "*filter\n"
- "-I bw_happy_box -m owner --uid-owner 1000 --jump RETURN\n"
- "-I bw_happy_box -m owner --uid-owner 1001 --jump RETURN\n"
- "-I bw_happy_box -m owner --uid-owner 10012 --jump RETURN\n"
- "COMMIT\n"
- };
+ "*filter\n"
+ "-I bw_happy_box -m owner --uid-owner 1000 -j RETURN\n"
+ "-I bw_happy_box -m owner --uid-owner 1001 -j RETURN\n"
+ "-I bw_happy_box -m owner --uid-owner 10012 -j RETURN\n"
+ "COMMIT\n"};
EXPECT_EQ(0, mBw.addNiceApps(appUids.size(), const_cast<char**>(&appUids[0])));
expectIptablesRestoreCommands(expected);
expected = {
- "*filter\n"
- "-D bw_penalty_box -m owner --uid-owner 1000 --jump REJECT\n"
- "-D bw_penalty_box -m owner --uid-owner 1001 --jump REJECT\n"
- "-D bw_penalty_box -m owner --uid-owner 10012 --jump REJECT\n"
- "COMMIT\n"
- };
+ "*filter\n"
+ "-D bw_penalty_box -m owner --uid-owner 1000 -j REJECT\n"
+ "-D bw_penalty_box -m owner --uid-owner 1001 -j REJECT\n"
+ "-D bw_penalty_box -m owner --uid-owner 10012 -j REJECT\n"
+ "COMMIT\n"};
EXPECT_EQ(0, mBw.removeNaughtyApps(appUids.size(), const_cast<char**>(&appUids[0])));
expectIptablesRestoreCommands(expected);
}
diff --git a/server/ClatUtils.h b/server/ClatUtils.h
deleted file mode 100644
index 9f2a28e..0000000
--- a/server/ClatUtils.h
+++ /dev/null
@@ -1,44 +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 _CLAT_UTILS_H
-#define _CLAT_UTILS_H
-
-#include <string>
-
-namespace android {
-namespace net {
-
-int hardwareAddressType(const std::string& interface);
-
-int getClatIngressMapFd(void);
-
-int getClatIngressProgFd(bool with_ethernet_header);
-
-int openNetlinkSocket(void);
-
-int processNetlinkResponse(int fd);
-
-int tcQdiscAddDevClsact(int fd, int ifIndex);
-int tcQdiscReplaceDevClsact(int fd, int ifIndex);
-int tcQdiscDelDevClsact(int fd, int ifIndex);
-
-int tcFilterAddDevBpf(int fd, int ifIndex, int bpfFd, bool ethernet);
-
-} // namespace net
-} // namespace android
-
-#endif
diff --git a/server/ClatUtilsTest.cpp b/server/ClatUtilsTest.cpp
deleted file mode 100644
index 2a869ba..0000000
--- a/server/ClatUtilsTest.cpp
+++ /dev/null
@@ -1,194 +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.
- *
- * ClatUtilsTest.cpp - unit tests for ClatUtils.cpp
- */
-
-#include <gtest/gtest.h>
-
-#include "ClatUtils.h"
-
-#include <linux/if_arp.h>
-#include <stdlib.h>
-#include <sys/wait.h>
-
-#include "bpf/BpfUtils.h"
-#include "netdbpf/bpf_shared.h"
-
-namespace android {
-namespace net {
-
-class ClatUtilsTest : public ::testing::Test {
- public:
- void SetUp() {}
-};
-
-TEST_F(ClatUtilsTest, HardwareAddressTypeOfNonExistingIf) {
- ASSERT_EQ(-ENODEV, hardwareAddressType("not_existing_if"));
-}
-
-TEST_F(ClatUtilsTest, HardwareAddressTypeOfLoopback) {
- ASSERT_EQ(ARPHRD_LOOPBACK, hardwareAddressType("lo"));
-}
-
-// If wireless 'wlan0' interface exists it should be Ethernet.
-TEST_F(ClatUtilsTest, HardwareAddressTypeOfWireless) {
- int type = hardwareAddressType("wlan0");
- if (type == -ENODEV) return;
-
- ASSERT_EQ(ARPHRD_ETHER, type);
-}
-
-// If cellular 'rmnet_data0' interface exists it should
-// *probably* not be Ethernet and instead be RawIp.
-TEST_F(ClatUtilsTest, HardwareAddressTypeOfCellular) {
- int type = hardwareAddressType("rmnet_data0");
- if (type == -ENODEV) return;
-
- ASSERT_NE(ARPHRD_ETHER, type);
-
- // ARPHRD_RAWIP is 530 on some pre-4.14 Qualcomm devices.
- if (type == 530) return;
-
- ASSERT_EQ(ARPHRD_RAWIP, type);
-}
-
-TEST_F(ClatUtilsTest, GetClatMapFd) {
- SKIP_IF_BPF_NOT_SUPPORTED;
-
- int fd = getClatIngressMapFd();
- ASSERT_LE(3, fd); // 0,1,2 - stdin/out/err, thus 3 <= fd
- close(fd);
-}
-
-TEST_F(ClatUtilsTest, GetClatRawIpProgFd) {
- SKIP_IF_BPF_NOT_SUPPORTED;
-
- int fd = getClatIngressProgFd(false);
- ASSERT_LE(3, fd);
- close(fd);
-}
-
-TEST_F(ClatUtilsTest, GetClatEtherProgFd) {
- SKIP_IF_BPF_NOT_SUPPORTED;
-
- int fd = getClatIngressProgFd(true);
- ASSERT_LE(3, fd);
- close(fd);
-}
-
-TEST_F(ClatUtilsTest, TryOpeningNetlinkSocket) {
- int fd = openNetlinkSocket();
- ASSERT_LE(3, fd);
- close(fd);
-}
-
-// The SKIP_IF_BPF_NOT_SUPPORTED macro is effectively a check for 4.9+ kernel
-// combined with a launched on P device. Ie. it's a test for 4.9-P or better.
-
-// NET_SCH_INGRESS is only enabled starting with 4.9-Q and as such we need
-// a separate way to test for this...
-int doKernelSupportsNetSchIngress(void) {
- // NOLINTNEXTLINE(cert-env33-c)
- return system("zcat /proc/config.gz | egrep -q '^CONFIG_NET_SCH_INGRESS=[my]$'");
-}
-
-// NET_CLS_BPF is only enabled starting with 4.9-Q...
-int doKernelSupportsNetClsBpf(void) {
- // NOLINTNEXTLINE(cert-env33-c)
- return system("zcat /proc/config.gz | egrep -q '^CONFIG_NET_CLS_BPF=[my]$'");
-}
-
-// Make sure the above functions actually execute correctly rather than failing
-// due to missing binary or execution failure...
-TEST_F(ClatUtilsTest, KernelSupportsNetFuncs) {
- // Make sure the file is present and readable and decompressable.
- // NOLINTNEXTLINE(cert-env33-c)
- ASSERT_EQ(W_EXITCODE(0, 0), system("zcat /proc/config.gz > /dev/null"));
-
- int v = doKernelSupportsNetSchIngress();
- int w = doKernelSupportsNetClsBpf();
-
- // They should always either return 0 (match) or 1 (no match),
- // anything else is some sort of exec/environment/etc failure.
- if (v != W_EXITCODE(1, 0)) ASSERT_EQ(v, W_EXITCODE(0, 0));
- if (w != W_EXITCODE(1, 0)) ASSERT_EQ(w, W_EXITCODE(0, 0));
-}
-
-// True iff CONFIG_NET_SCH_INGRESS is enabled in /proc/config.gz
-bool kernelSupportsNetSchIngress(void) {
- return doKernelSupportsNetSchIngress() == W_EXITCODE(0, 0);
-}
-
-// True iff CONFIG_NET_CLS_BPF is enabled in /proc/config.gz
-bool kernelSupportsNetClsBpf(void) {
- return doKernelSupportsNetClsBpf() == W_EXITCODE(0, 0);
-}
-
-// See Linux kernel source in include/net/flow.h
-#define LOOPBACK_IFINDEX 1
-
-TEST_F(ClatUtilsTest, AttachReplaceDetachClsactLo) {
- // Technically does not depend on ebpf, but does depend on clsact,
- // and we do not really care if it works on pre-4.9-Q anyway.
- SKIP_IF_BPF_NOT_SUPPORTED;
- if (!kernelSupportsNetSchIngress()) return;
-
- int fd = openNetlinkSocket();
- ASSERT_LE(3, fd);
-
- // This attaches and detaches a configuration-less and thus no-op clsact
- // qdisc to loopback interface (and it takes fractions of a second)
- EXPECT_EQ(0, tcQdiscAddDevClsact(fd, LOOPBACK_IFINDEX));
- EXPECT_EQ(0, tcQdiscReplaceDevClsact(fd, LOOPBACK_IFINDEX));
- EXPECT_EQ(0, tcQdiscDelDevClsact(fd, LOOPBACK_IFINDEX));
- close(fd);
-}
-
-void checkAttachBpfFilterClsactLo(const bool ethernet) {
- // This test requires kernel 4.9-Q or better
- SKIP_IF_BPF_NOT_SUPPORTED;
- if (!kernelSupportsNetSchIngress()) return;
- if (!kernelSupportsNetClsBpf()) return;
-
- int bpf_fd = getClatIngressProgFd(false);
- ASSERT_LE(3, bpf_fd);
-
- int fd = openNetlinkSocket();
- EXPECT_LE(3, fd);
- if (fd >= 0) {
- // This attaches and detaches a clsact plus ebpf program to loopback
- // interface, but it should not affect traffic by virtue of us not
- // actually populating the ebpf control map.
- // Furthermore: it only takes fractions of a second.
- EXPECT_EQ(0, tcQdiscAddDevClsact(fd, LOOPBACK_IFINDEX));
- EXPECT_EQ(0, tcFilterAddDevBpf(fd, LOOPBACK_IFINDEX, bpf_fd, ethernet));
- EXPECT_EQ(0, tcQdiscDelDevClsact(fd, LOOPBACK_IFINDEX));
- close(fd);
- }
-
- close(bpf_fd);
-}
-
-TEST_F(ClatUtilsTest, CheckAttachBpfFilterRawIpClsactLo) {
- checkAttachBpfFilterClsactLo(false);
-}
-
-TEST_F(ClatUtilsTest, CheckAttachBpfFilterEthernetClsactLo) {
- checkAttachBpfFilterClsactLo(true);
-}
-
-} // namespace net
-} // namespace android
diff --git a/server/ClatdController.cpp b/server/ClatdController.cpp
index 5a5566b..90afa3a 100644
--- a/server/ClatdController.cpp
+++ b/server/ClatdController.cpp
@@ -19,7 +19,6 @@
#include <arpa/inet.h>
#include <errno.h>
-#include <linux/if_arp.h>
#include <linux/if_tun.h>
#include <linux/ioctl.h>
#include <net/if.h>
@@ -33,6 +32,7 @@
#include <log/log.h>
#include "ClatdController.h"
+#include "InterfaceController.h"
#include "android-base/properties.h"
#include "android-base/scopeguard.h"
@@ -46,10 +46,10 @@
#include "netutils/checksum.h"
}
-#include "ClatUtils.h"
#include "Fwmark.h"
#include "NetdConstants.h"
#include "NetworkController.h"
+#include "OffloadUtils.h"
#include "netid_client.h"
static const char* kClatdPath = "/system/bin/clatd";
@@ -60,6 +60,7 @@
static const in_addr kV4Addr = {inet_addr(kV4AddrString)};
static const int kV4AddrLen = 29;
+using android::base::Result;
using android::base::StringPrintf;
using android::base::unique_fd;
using android::bpf::BpfMap;
@@ -73,7 +74,7 @@
std::lock_guard guard(mutex);
// TODO: should refactor into separate function for testability
- if (bpf::getBpfSupportLevel() == bpf::BpfLevel::NONE) {
+ if (!bpf::isBpfSupported()) {
ALOGI("Pre-4.9 kernel or pre-P api shipping level - disabling clat ebpf.");
mClatEbpfMode = ClatEbpfDisabled;
return;
@@ -98,37 +99,25 @@
mClatEbpfMode = ClatEbpfMaybe;
}
- int rv = openNetlinkSocket();
+ int rv = getClatEgressMapFd();
if (rv < 0) {
- ALOGE("openNetlinkSocket() failure: %s", strerror(-rv));
+ ALOGE("getClatEgressMapFd() failure: %s", strerror(-rv));
mClatEbpfMode = ClatEbpfDisabled;
return;
}
- mNetlinkFd.reset(rv);
+ mClatEgressMap.reset(rv);
rv = getClatIngressMapFd();
if (rv < 0) {
ALOGE("getClatIngressMapFd() failure: %s", strerror(-rv));
mClatEbpfMode = ClatEbpfDisabled;
- mNetlinkFd.reset(-1);
+ mClatEgressMap.reset(-1);
return;
}
mClatIngressMap.reset(rv);
- int netlinkFd = mNetlinkFd.get();
-
- // TODO: perhaps this initial cleanup should be in its own function?
- const auto del = [&netlinkFd](const ClatIngressKey& key,
- const BpfMap<ClatIngressKey, ClatIngressValue>&) {
- ALOGW("Removing stale clat config on interface %d.", key.iif);
- int rv = tcQdiscDelDevClsact(netlinkFd, key.iif);
- if (rv < 0) ALOGE("tcQdiscDelDevClsact() failure: %s", strerror(-rv));
- return netdutils::status::ok; // keep on going regardless
- };
- auto ret = mClatIngressMap.iterate(del);
- if (!isOk(ret)) ALOGE("mClatIngressMap.iterate() failure: %s", strerror(ret.code()));
- ret = mClatIngressMap.clear();
- if (!isOk(ret)) ALOGE("mClatIngressMap.clear() failure: %s", strerror(ret.code()));
+ mClatEgressMap.clear();
+ mClatIngressMap.clear();
}
bool ClatdController::isIpv4AddressFree(in_addr_t addr) {
@@ -139,7 +128,11 @@
// 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_addr = {addr}, .sin_port = 53};
+ struct sockaddr_in sin = {
+ .sin_family = AF_INET,
+ .sin_port = htons(53),
+ .sin_addr = {addr},
+ };
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) &&
@@ -235,93 +228,145 @@
void ClatdController::maybeStartBpf(const ClatdTracker& tracker) {
if (mClatEbpfMode == ClatEbpfDisabled) return;
- int rv = hardwareAddressType(tracker.iface);
- if (rv < 0) {
- ALOGE("hardwareAddressType(%s[%d]) failure: %s", tracker.iface, tracker.ifIndex,
- strerror(-rv));
+ auto isEthernet = android::net::isEthernet(tracker.iface);
+ if (!isEthernet.ok()) {
+ ALOGE("isEthernet(%s[%d]) failure: %s", tracker.iface, tracker.ifIndex,
+ isEthernet.error().message().c_str());
return;
}
- bool isEthernet;
- switch (rv) {
- case ARPHRD_ETHER:
- isEthernet = true;
- break;
- case ARPHRD_RAWIP: // in Linux 4.14+ rmnet support was upstreamed and this is 519
- case 530: // this is ARPHRD_RAWIP on some Android 4.9 kernels with rmnet
- isEthernet = false;
- break;
- default:
- ALOGE("hardwareAddressType(%s[%d]) returned unknown type %d.", tracker.iface,
- tracker.ifIndex, rv);
- return;
- }
-
- rv = getClatIngressProgFd(isEthernet);
+ // This program will be attached to the v4-* interface which is a TUN and thus always rawip.
+ int rv = getClatEgressProgFd(RAWIP);
if (rv < 0) {
- ALOGE("getClatIngressProgFd(%d) failure: %s", isEthernet, strerror(-rv));
+ ALOGE("getClatEgressProgFd(RAWIP) failure: %s", strerror(-rv));
return;
}
- unique_fd progFd(rv);
+ unique_fd txRawIpProgFd(rv);
- ClatIngressKey key = {
+ rv = getClatIngressProgFd(isEthernet.value());
+ if (rv < 0) {
+ ALOGE("getClatIngressProgFd(%d) failure: %s", isEthernet.value(), strerror(-rv));
+ return;
+ }
+ unique_fd rxProgFd(rv);
+
+ ClatEgressKey txKey = {
+ .iif = tracker.v4ifIndex,
+ .local4 = tracker.v4,
+ };
+ ClatEgressValue txValue = {
+ .oif = tracker.ifIndex,
+ .local6 = tracker.v6,
+ .pfx96 = tracker.pfx96,
+ .oifIsEthernet = isEthernet.value(),
+ };
+
+ auto ret = mClatEgressMap.writeValue(txKey, txValue, BPF_ANY);
+ if (!ret.ok()) {
+ ALOGE("mClatEgressMap.writeValue failure: %s", strerror(ret.error().code()));
+ return;
+ }
+
+ ClatIngressKey rxKey = {
.iif = tracker.ifIndex,
.pfx96 = tracker.pfx96,
.local6 = tracker.v6,
};
- ClatIngressValue value = {
+ ClatIngressValue rxValue = {
// TODO: move all the clat code to eBPF and remove the tun interface entirely.
.oif = tracker.v4ifIndex,
.local4 = tracker.v4,
};
- auto ret = mClatIngressMap.writeValue(key, value, BPF_ANY);
- if (!isOk(ret)) {
- ALOGE("mClatIngress.Map.writeValue failure: %s", strerror(ret.code()));
+ ret = mClatIngressMap.writeValue(rxKey, rxValue, BPF_ANY);
+ if (!ret.ok()) {
+ ALOGE("mClatIngressMap.writeValue failure: %s", strerror(ret.error().code()));
+ ret = mClatEgressMap.deleteValue(txKey);
+ if (!ret.ok())
+ ALOGE("mClatEgressMap.deleteValue failure: %s", strerror(ret.error().code()));
return;
}
- // We do tc setup *after* populating map, so scanning through map
+ // We do tc setup *after* populating the maps, so scanning through them
// can always be used to tell us what needs cleanup.
- rv = tcQdiscAddDevClsact(mNetlinkFd, tracker.ifIndex);
+ // Usually the clsact will be added in RouteController::addInterfaceToPhysicalNetwork.
+ // But clat is started before the v4- interface is added to the network. The clat startup have
+ // to add clsact of v4- tun interface first for adding bpf filter in maybeStartBpf.
+ // TODO: move "qdisc add clsact" of v4- tun interface out from ClatdController.
+ rv = tcQdiscAddDevClsact(tracker.v4ifIndex);
if (rv) {
- ALOGE("tcQdiscAddDevClsact(%d[%s]) failure: %s", tracker.ifIndex, tracker.iface,
+ ALOGE("tcQdiscAddDevClsact(%d[%s]) failure: %s", tracker.v4ifIndex, tracker.v4iface,
strerror(-rv));
- ret = mClatIngressMap.deleteValue(key);
- if (!isOk(ret)) ALOGE("mClatIngressMap.deleteValue failure: %s", strerror(ret.code()));
+ ret = mClatEgressMap.deleteValue(txKey);
+ if (!ret.ok())
+ ALOGE("mClatEgressMap.deleteValue failure: %s", strerror(ret.error().code()));
+ ret = mClatIngressMap.deleteValue(rxKey);
+ if (!ret.ok())
+ ALOGE("mClatIngressMap.deleteValue failure: %s", strerror(ret.error().code()));
return;
}
- rv = tcFilterAddDevBpf(mNetlinkFd, tracker.ifIndex, progFd, isEthernet);
+ rv = tcFilterAddDevEgressClatIpv4(tracker.v4ifIndex, txRawIpProgFd, RAWIP);
if (rv) {
if ((rv == -ENOENT) && (mClatEbpfMode == ClatEbpfMaybe)) {
- ALOGI("tcFilterAddDevBpf(%d[%s], %d): %s", tracker.ifIndex, tracker.iface, isEthernet,
- strerror(-rv));
+ ALOGI("tcFilterAddDevEgressClatIpv4(%d[%s], RAWIP): %s", tracker.v4ifIndex,
+ tracker.v4iface, strerror(-rv));
} else {
- ALOGE("tcFilterAddDevBpf(%d[%s], %d) failure: %s", tracker.ifIndex, tracker.iface,
- isEthernet, strerror(-rv));
+ ALOGE("tcFilterAddDevEgressClatIpv4(%d[%s], RAWIP) failure: %s", tracker.v4ifIndex,
+ tracker.v4iface, strerror(-rv));
}
- rv = tcQdiscDelDevClsact(mNetlinkFd, tracker.ifIndex);
- if (rv)
- ALOGE("tcQdiscDelDevClsact(%d[%s]) failure: %s", tracker.ifIndex, tracker.iface,
- strerror(-rv));
- ret = mClatIngressMap.deleteValue(key);
- if (!isOk(ret)) ALOGE("mClatIngressMap.deleteValue failure: %s", strerror(ret.code()));
+
+ // The v4- interface clsact is not deleted for unwinding error because once it is created
+ // with interface addition, the lifetime is till interface deletion. Moreover, the clsact
+ // has no clat filter now. It should not break anything.
+
+ ret = mClatEgressMap.deleteValue(txKey);
+ if (!ret.ok())
+ ALOGE("mClatEgressMap.deleteValue failure: %s", strerror(ret.error().code()));
+ ret = mClatIngressMap.deleteValue(rxKey);
+ if (!ret.ok())
+ ALOGE("mClatIngressMap.deleteValue failure: %s", strerror(ret.error().code()));
+ return;
+ }
+
+ rv = tcFilterAddDevIngressClatIpv6(tracker.ifIndex, rxProgFd, isEthernet.value());
+ if (rv) {
+ if ((rv == -ENOENT) && (mClatEbpfMode == ClatEbpfMaybe)) {
+ ALOGI("tcFilterAddDevIngressClatIpv6(%d[%s], %d): %s", tracker.ifIndex, tracker.iface,
+ isEthernet.value(), strerror(-rv));
+ } else {
+ ALOGE("tcFilterAddDevIngressClatIpv6(%d[%s], %d) failure: %s", tracker.ifIndex,
+ tracker.iface, isEthernet.value(), strerror(-rv));
+ }
+ rv = tcFilterDelDevEgressClatIpv4(tracker.v4ifIndex);
+ if (rv) {
+ ALOGE("tcFilterDelDevEgressClatIpv4(%d[%s]) failure: %s", tracker.v4ifIndex,
+ tracker.v4iface, strerror(-rv));
+ }
+
+ // The v4- interface clsact is not deleted. See the reason in the error unwinding code of
+ // the egress filter attaching of v4- tun interface.
+
+ ret = mClatEgressMap.deleteValue(txKey);
+ if (!ret.ok())
+ ALOGE("mClatEgressMap.deleteValue failure: %s", strerror(ret.error().code()));
+ ret = mClatIngressMap.deleteValue(rxKey);
+ if (!ret.ok())
+ ALOGE("mClatIngressMap.deleteValue failure: %s", strerror(ret.error().code()));
return;
}
// success
}
-void ClatdController::maybeSetIptablesDropRule(bool add, const char* pfx96Str, const char* v6Str) {
- if (mClatEbpfMode == ClatEbpfDisabled) return;
-
+void ClatdController::setIptablesDropRule(bool add, const char* iface, const char* pfx96Str,
+ const char* v6Str) {
std::string cmd = StringPrintf(
"*raw\n"
- "%s %s -s %s/96 -d %s -j DROP\n"
+ "%s %s -i %s -s %s/96 -d %s -j DROP\n"
"COMMIT\n",
- (add ? "-A" : "-D"), LOCAL_RAW_PREROUTING, pfx96Str, v6Str);
+ (add ? "-A" : "-D"), LOCAL_RAW_PREROUTING, iface, pfx96Str, v6Str);
iptablesRestoreFunction(V6, cmd);
}
@@ -329,24 +374,37 @@
void ClatdController::maybeStopBpf(const ClatdTracker& tracker) {
if (mClatEbpfMode == ClatEbpfDisabled) return;
- // No need to remove filter, since we remove qdisc it is attached to,
- // which automatically removes everything attached to the qdisc.
- int rv = tcQdiscDelDevClsact(mNetlinkFd, tracker.ifIndex);
- if (rv < 0)
- ALOGE("tcQdiscDelDevClsact(%d[%s]) failure: %s", tracker.ifIndex, tracker.iface,
+ int rv = tcFilterDelDevIngressClatIpv6(tracker.ifIndex);
+ if (rv < 0) {
+ ALOGE("tcFilterDelDevIngressClatIpv6(%d[%s]) failure: %s", tracker.ifIndex, tracker.iface,
strerror(-rv));
+ }
- // We cleanup map last, so scanning through map can be used to
+ rv = tcFilterDelDevEgressClatIpv4(tracker.v4ifIndex);
+ if (rv < 0) {
+ ALOGE("tcFilterDelDevEgressClatIpv4(%d[%s]) failure: %s", tracker.v4ifIndex,
+ tracker.v4iface, strerror(-rv));
+ }
+
+ // We cleanup the maps last, so scanning through them can be used to
// determine what still needs cleanup.
- ClatIngressKey key = {
+ ClatEgressKey txKey = {
+ .iif = tracker.v4ifIndex,
+ .local4 = tracker.v4,
+ };
+
+ auto ret = mClatEgressMap.deleteValue(txKey);
+ if (!ret.ok()) ALOGE("mClatEgressMap.deleteValue failure: %s", strerror(ret.error().code()));
+
+ ClatIngressKey rxKey = {
.iif = tracker.ifIndex,
.pfx96 = tracker.pfx96,
.local6 = tracker.v6,
};
- auto ret = mClatIngressMap.deleteValue(key);
- if (!isOk(ret)) ALOGE("mClatIngressMap.deleteValue failure: %s", strerror(ret.code()));
+ ret = mClatIngressMap.deleteValue(rxKey);
+ if (!ret.ok()) ALOGE("mClatIngressMap.deleteValue failure: %s", strerror(ret.error().code()));
}
// Finds the tracker of the clatd running on interface |interface|, or nullptr if clatd has not been
@@ -360,22 +418,19 @@
int ClatdController::ClatdTracker::init(unsigned networkId, const std::string& interface,
const std::string& v4interface,
const std::string& nat64Prefix) {
- netId = networkId;
-
- fwmark.netId = netId;
+ fwmark.netId = networkId;
fwmark.explicitlySelected = true;
fwmark.protectedFromVpn = true;
fwmark.permission = PERMISSION_SYSTEM;
snprintf(fwmarkString, sizeof(fwmarkString), "0x%x", fwmark.intValue);
- snprintf(netIdString, sizeof(netIdString), "%u", netId);
strlcpy(iface, interface.c_str(), sizeof(iface));
ifIndex = if_nametoindex(iface);
strlcpy(v4iface, v4interface.c_str(), sizeof(v4iface));
v4ifIndex = if_nametoindex(v4iface);
- // Pass in everything that clatd needs: interface, a netid to use for DNS lookups, a fwmark for
- // outgoing packets, the NAT64 prefix, and the IPv4 and IPv6 addresses.
+ // Pass in everything that clatd needs: interface, a fwmark for outgoing packets, the NAT64
+ // prefix, and the IPv4 and IPv6 addresses.
// Validate the prefix and strip off the prefix length.
uint8_t family;
uint8_t prefixLen;
@@ -428,7 +483,7 @@
}
// 3. open the tun device in non blocking mode as required by clatd
- int res = open("/dev/tun", O_RDWR | O_NONBLOCK | O_CLOEXEC);
+ int res = open("/dev/net/tun", O_RDWR | O_NONBLOCK | O_CLOEXEC);
if (res == -1) {
res = errno;
ALOGE("open of tun device failed (%s)", strerror(res));
@@ -452,6 +507,10 @@
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);
@@ -477,7 +536,6 @@
// clang-format off
const char* args[] = {progname.c_str(),
"-i", tracker.iface,
- "-n", tracker.netIdString,
"-m", tracker.fwmarkString,
"-p", tracker.pfx96String,
"-4", tracker.v4Str,
@@ -516,8 +574,8 @@
return -res;
}
- // 11. If necessary, add the drop rule for iptables.
- maybeSetIptablesDropRule(true, tracker.pfx96String, tracker.v6Str);
+ // 11. add the drop rule for iptables.
+ setIptablesDropRule(true, tracker.iface, tracker.pfx96String, tracker.v6Str);
// 12. actually perform vfork/dup2/execve
res = posix_spawn(&tracker.pid, kClatdPath, &fa, &attr, (char* const*)args, nullptr);
@@ -552,7 +610,7 @@
kill(tracker->pid, SIGTERM);
waitpid(tracker->pid, nullptr, 0);
- maybeSetIptablesDropRule(false, tracker->pfx96String, tracker->v6Str);
+ setIptablesDropRule(false, tracker->iface, tracker->pfx96String, tracker->v6Str);
mClatdTrackers.erase(interface);
ALOGD("clatd on %s stopped", interface.c_str());
@@ -560,28 +618,39 @@
return 0;
}
-void ClatdController::dump(DumpWriter& dw) {
- std::lock_guard guard(mutex);
+void ClatdController::dumpEgress(DumpWriter& dw) {
+ if (!mClatEgressMap.isValid()) return; // if unsupported just don't dump anything
- ScopedIndent clatdIndent(dw);
- dw.println("ClatdController");
+ ScopedIndent bpfIndent(dw);
+ dw.println("BPF egress map: iif(iface) v4Addr -> v6Addr nat64Prefix oif(iface)");
- {
- ScopedIndent trackerIndent(dw);
- dw.println("Trackers: iif[iface] nat64Prefix v6Addr -> v4Addr v4iif[v4iface] [netId]");
+ ScopedIndent bpfDetailIndent(dw);
+ const auto printClatMap = [&dw](const ClatEgressKey& key, const ClatEgressValue& value,
+ const BpfMap<ClatEgressKey, ClatEgressValue>&) {
+ char iifStr[IFNAMSIZ] = "?";
+ char local4Str[INET_ADDRSTRLEN] = "?";
+ char local6Str[INET6_ADDRSTRLEN] = "?";
+ char pfx96Str[INET6_ADDRSTRLEN] = "?";
+ char oifStr[IFNAMSIZ] = "?";
- ScopedIndent trackerDetailIndent(dw);
- for (const auto& pair : mClatdTrackers) {
- const ClatdTracker& tracker = pair.second;
- dw.println("%u[%s] %s/96 %s -> %s %u[%s] [%u]", tracker.ifIndex, tracker.iface,
- tracker.pfx96String, tracker.v6Str, tracker.v4Str, tracker.v4ifIndex,
- tracker.v4iface, tracker.netId);
- }
+ if_indextoname(key.iif, iifStr);
+ inet_ntop(AF_INET, &key.local4, local4Str, sizeof(local4Str));
+ inet_ntop(AF_INET6, &value.local6, local6Str, sizeof(local6Str));
+ inet_ntop(AF_INET6, &value.pfx96, pfx96Str, sizeof(pfx96Str));
+ if_indextoname(value.oif, oifStr);
+
+ dw.println("%u(%s) %s -> %s %s/96 %u(%s) %s", key.iif, iifStr, local4Str, local6Str,
+ pfx96Str, value.oif, oifStr, value.oifIsEthernet ? "ether" : "rawip");
+ return Result<void>();
+ };
+ auto res = mClatEgressMap.iterateWithValue(printClatMap);
+ if (!res.ok()) {
+ dw.println("Error printing BPF map: %s", res.error().message().c_str());
}
+}
- int mapFd = getClatIngressMapFd();
- if (mapFd < 0) return; // if unsupported just don't dump anything
- BpfMap<ClatIngressKey, ClatIngressValue> configMap(mapFd);
+void ClatdController::dumpIngress(DumpWriter& dw) {
+ if (!mClatIngressMap.isValid()) return; // if unsupported just don't dump anything
ScopedIndent bpfIndent(dw);
dw.println("BPF ingress map: iif(iface) nat64Prefix v6Addr -> v4Addr oif(iface)");
@@ -603,14 +672,38 @@
dw.println("%u(%s) %s/96 %s -> %s %u(%s)", key.iif, iifStr, pfx96Str, local6Str, local4Str,
value.oif, oifStr);
- return netdutils::status::ok;
+ return Result<void>();
};
- auto res = configMap.iterateWithValue(printClatMap);
- if (!isOk(res)) {
- dw.println("Error printing BPF map: %s", res.msg().c_str());
+ auto res = mClatIngressMap.iterateWithValue(printClatMap);
+ if (!res.ok()) {
+ dw.println("Error printing BPF map: %s", res.error().message().c_str());
}
}
+void ClatdController::dumpTrackers(DumpWriter& dw) {
+ ScopedIndent trackerIndent(dw);
+ dw.println("Trackers: iif[iface] nat64Prefix v6Addr -> v4Addr v4iif[v4iface] [fwmark]");
+
+ ScopedIndent trackerDetailIndent(dw);
+ for (const auto& pair : mClatdTrackers) {
+ const ClatdTracker& tracker = pair.second;
+ dw.println("%u[%s] %s/96 %s -> %s %u[%s] [%s]", tracker.ifIndex, tracker.iface,
+ tracker.pfx96String, tracker.v6Str, tracker.v4Str, tracker.v4ifIndex,
+ tracker.v4iface, tracker.fwmarkString);
+ }
+}
+
+void ClatdController::dump(DumpWriter& dw) {
+ std::lock_guard guard(mutex);
+
+ ScopedIndent clatdIndent(dw);
+ dw.println("ClatdController");
+
+ dumpTrackers(dw);
+ dumpIngress(dw);
+ dumpEgress(dw);
+}
+
auto ClatdController::isIpv4AddressFreeFunc = isIpv4AddressFree;
auto ClatdController::iptablesRestoreFunction = execIptablesRestore;
diff --git a/server/ClatdController.h b/server/ClatdController.h
index 8648f17..2c13958 100644
--- a/server/ClatdController.h
+++ b/server/ClatdController.h
@@ -63,8 +63,6 @@
char v4iface[IFNAMSIZ];
Fwmark fwmark;
char fwmarkString[UINT32_STRLEN];
- unsigned netId;
- char netIdString[UINT32_STRLEN];
in_addr v4;
char v4Str[INET_ADDRSTRLEN];
in6_addr v6;
@@ -82,6 +80,10 @@
std::map<std::string, ClatdTracker> mClatdTrackers GUARDED_BY(mutex);
ClatdTracker* getClatdTracker(const std::string& interface) REQUIRES(mutex);
+ void dumpEgress(netdutils::DumpWriter& dw) REQUIRES(mutex);
+ void dumpIngress(netdutils::DumpWriter& dw) REQUIRES(mutex);
+ void dumpTrackers(netdutils::DumpWriter& dw) REQUIRES(mutex);
+
static in_addr_t selectIpv4Address(const in_addr ip, int16_t prefixlen);
static int generateIpv6Address(const char* iface, const in_addr v4, const in6_addr& nat64Prefix,
in6_addr* v6);
@@ -98,12 +100,12 @@
return mClatEbpfMode;
}
- base::unique_fd mNetlinkFd GUARDED_BY(mutex);
+ bpf::BpfMap<ClatEgressKey, ClatEgressValue> mClatEgressMap GUARDED_BY(mutex);
bpf::BpfMap<ClatIngressKey, ClatIngressValue> mClatIngressMap GUARDED_BY(mutex);
void maybeStartBpf(const ClatdTracker& tracker) REQUIRES(mutex);
void maybeStopBpf(const ClatdTracker& tracker) REQUIRES(mutex);
- void maybeSetIptablesDropRule(bool add, const char* pfx96Str, const char* v6Str)
+ void setIptablesDropRule(bool add, const char* iface, const char* pfx96Str, const char* v6Str)
REQUIRES(mutex);
// For testing.
diff --git a/server/ClatdControllerTest.cpp b/server/ClatdControllerTest.cpp
index e90dd1a..dc71a03 100644
--- a/server/ClatdControllerTest.cpp
+++ b/server/ClatdControllerTest.cpp
@@ -70,9 +70,9 @@
protected:
ClatdController mClatdCtrl;
bool isEbpfDisabled() { return mClatdCtrl.getEbpfMode() == ClatdController::ClatEbpfDisabled; }
- void maybeSetIptablesDropRule(bool a, const char* b, const char* c) {
+ void setIptablesDropRule(bool a, const char* b, const char* c, const char* d) {
std::lock_guard guard(mClatdCtrl.mutex);
- return mClatdCtrl.maybeSetIptablesDropRule(a, b, c);
+ return mClatdCtrl.setIptablesDropRule(a, b, c, d);
}
void setIpv4AddressFreeFunc(bool (*func)(in_addr_t)) {
ClatdController::isIpv4AddressFreeFunc = func;
@@ -187,26 +187,22 @@
EXPECT_GE(3210000, onebits);
}
-TEST_F(ClatdControllerTest, AddRemoveIptablesRule) {
- if (isEbpfDisabled()) return;
-
- ExpectedIptablesCommands expected = {
+TEST_F(ClatdControllerTest, AddIptablesRule) {
+ setIptablesDropRule(true, "wlan0", "64:ff9b::", "2001:db8::1:2:3:4");
+ expectIptablesRestoreCommands((ExpectedIptablesCommands){
{V6,
"*raw\n"
- "-A clat_raw_PREROUTING -s 64:ff9b::/96 -d 2001:db8::1:2:3:4 -j DROP\n"
- "COMMIT\n"},
- };
- maybeSetIptablesDropRule(true, "64:ff9b::", "2001:db8::1:2:3:4");
- expectIptablesRestoreCommands(expected);
+ "-A clat_raw_PREROUTING -i wlan0 -s 64:ff9b::/96 -d 2001:db8::1:2:3:4 -j DROP\n"
+ "COMMIT\n"}});
+}
- expected = {
+TEST_F(ClatdControllerTest, RemoveIptablesRule) {
+ setIptablesDropRule(false, "wlan0", "64:ff9b::", "2001:db8::a:b:c:d");
+ expectIptablesRestoreCommands((ExpectedIptablesCommands){
{V6,
"*raw\n"
- "-D clat_raw_PREROUTING -s 64:ff9b::/96 -d 2001:db8::a:b:c:d -j DROP\n"
- "COMMIT\n"},
- };
- maybeSetIptablesDropRule(false, "64:ff9b::", "2001:db8::a:b:c:d");
- expectIptablesRestoreCommands(expected);
+ "-D clat_raw_PREROUTING -i wlan0 -s 64:ff9b::/96 -d 2001:db8::a:b:c:d -j DROP\n"
+ "COMMIT\n"}});
}
} // namespace net
diff --git a/server/Controllers.cpp b/server/Controllers.cpp
index c941a80..5af8a91 100644
--- a/server/Controllers.cpp
+++ b/server/Controllers.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <cinttypes>
#include <regex>
#include <set>
#include <string>
@@ -239,39 +240,39 @@
void Controllers::initIptablesRules() {
Stopwatch s;
initChildChains();
- gLog.info("Creating child chains: %.1fms", s.getTimeAndReset());
+ gLog.info("Creating child chains: %" PRId64 "us", s.getTimeAndResetUs());
// Let each module setup their child chains
setupOemIptablesHook();
- gLog.info("Setting up OEM hooks: %.1fms", s.getTimeAndReset());
+ gLog.info("Setting up OEM hooks: %" PRId64 "us", s.getTimeAndResetUs());
/* When enabled, DROPs all packets except those matching rules. */
firewallCtrl.setupIptablesHooks();
- gLog.info("Setting up FirewallController hooks: %.1fms", s.getTimeAndReset());
+ gLog.info("Setting up FirewallController hooks: %" PRId64 "us", s.getTimeAndResetUs());
/* Does DROPs in FORWARD by default */
tetherCtrl.setupIptablesHooks();
- gLog.info("Setting up TetherController hooks: %.1fms", s.getTimeAndReset());
+ gLog.info("Setting up TetherController hooks: %" PRId64 "us", s.getTimeAndResetUs());
/*
* 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: %.1fms", s.getTimeAndReset());
+ gLog.info("Setting up BandwidthController hooks: %" PRId64 "us", s.getTimeAndResetUs());
/*
* Counts in nat: PREROUTING, POSTROUTING.
* No DROP/REJECT allowed later in netfilter-flow hook order.
*/
idletimerCtrl.setupIptablesHooks();
- gLog.info("Setting up IdletimerController hooks: %.1fms", s.getTimeAndReset());
+ gLog.info("Setting up IdletimerController hooks: %" PRId64 "us", s.getTimeAndResetUs());
/*
* Add rules for detecting IPv6/IPv4 TCP/UDP connections with TLS/DTLS header
*/
strictCtrl.setupIptablesHooks();
- gLog.info("Setting up StrictController hooks: %.1fms", s.getTimeAndReset());
+ gLog.info("Setting up StrictController hooks: %" PRId64 "us", s.getTimeAndResetUs());
}
void Controllers::init() {
@@ -279,28 +280,28 @@
Stopwatch s;
clatdCtrl.init();
- gLog.info("Initializing ClatdController: %.1fms", s.getTimeAndReset());
+ gLog.info("Initializing ClatdController: %" PRId64 "us", s.getTimeAndResetUs());
netdutils::Status tcStatus = trafficCtrl.start();
if (!isOk(tcStatus)) {
gLog.error("Failed to start trafficcontroller: (%s)", toString(tcStatus).c_str());
}
- gLog.info("Initializing traffic control: %.1fms", s.getTimeAndReset());
+ gLog.info("Initializing traffic control: %" PRId64 "us", s.getTimeAndResetUs());
- bandwidthCtrl.setBpfEnabled(trafficCtrl.getBpfLevel() != android::bpf::BpfLevel::NONE);
+ bandwidthCtrl.setBpfEnabled(trafficCtrl.getBpfEnabled());
bandwidthCtrl.enableBandwidthControl();
- gLog.info("Enabling bandwidth control: %.1fms", s.getTimeAndReset());
+ gLog.info("Enabling bandwidth control: %" PRId64 "us", s.getTimeAndResetUs());
if (int ret = RouteController::Init(NetworkController::LOCAL_NET_ID)) {
gLog.error("Failed to initialize RouteController (%s)", strerror(-ret));
}
- gLog.info("Initializing RouteController: %.1fms", s.getTimeAndReset());
+ gLog.info("Initializing RouteController: %" PRId64 "us", s.getTimeAndResetUs());
netdutils::Status xStatus = XfrmController::Init();
if (!isOk(xStatus)) {
gLog.error("Failed to initialize XfrmController (%s)", netdutils::toString(xStatus).c_str());
};
- gLog.info("Initializing XfrmController: %.1fms", s.getTimeAndReset());
+ gLog.info("Initializing XfrmController: %" PRId64 "us", s.getTimeAndResetUs());
}
Controllers* gCtls = nullptr;
diff --git a/server/DummyNetwork.h b/server/DummyNetwork.h
index 32584aa..5823ce5 100644
--- a/server/DummyNetwork.h
+++ b/server/DummyNetwork.h
@@ -14,27 +14,22 @@
* limitations under the License.
*/
-#ifndef NETD_SERVER_DUMMY_NETWORK_H
-#define NETD_SERVER_DUMMY_NETWORK_H
+#pragma once
#include "Network.h"
-namespace android {
-namespace net {
+namespace android::net {
class DummyNetwork : public Network {
-public:
+ public:
static const char* INTERFACE_NAME;
explicit DummyNetwork(unsigned netId);
virtual ~DummyNetwork();
-private:
+ private:
Type getType() const override;
- int addInterface(const std::string& interface) override WARN_UNUSED_RESULT;
- int removeInterface(const std::string& interface) override WARN_UNUSED_RESULT;
+ [[nodiscard]] int addInterface(const std::string& interface) override;
+ [[nodiscard]] int removeInterface(const std::string& interface) override;
};
-} // namespace net
-} // namespace android
-
-#endif // NETD_SERVER_DUMMY_NETWORK_H
+} // namespace android::net
diff --git a/server/FirewallController.cpp b/server/FirewallController.cpp
index 7512c09..3c070ce 100644
--- a/server/FirewallController.cpp
+++ b/server/FirewallController.cpp
@@ -41,7 +41,6 @@
using android::base::Split;
using android::base::StringAppendF;
using android::base::StringPrintf;
-using android::bpf::BpfLevel;
using android::net::gCtls;
namespace {
@@ -54,8 +53,8 @@
// Proc file containing the uid mapping for the user namespace of the current process.
const char kUidMapProcFile[] = "/proc/self/uid_map";
-android::bpf::BpfLevel getBpfOwnerStatus() {
- return gCtls->trafficCtrl.getBpfLevel();
+bool getBpfOwnerStatus() {
+ return gCtls->trafficCtrl.getBpfEnabled();
}
} // namespace
@@ -96,7 +95,7 @@
int FirewallController::setupIptablesHooks(void) {
int res = 0;
mUseBpfOwnerMatch = getBpfOwnerStatus();
- if (mUseBpfOwnerMatch != BpfLevel::NONE) {
+ if (mUseBpfOwnerMatch) {
return res;
}
res |= createChain(LOCAL_DOZABLE, getFirewallType(DOZABLE));
@@ -160,7 +159,7 @@
return res;
}
- if (mUseBpfOwnerMatch != BpfLevel::NONE) {
+ if (mUseBpfOwnerMatch) {
return gCtls->trafficCtrl.toggleUidOwnerMap(chain, enable);
}
@@ -259,7 +258,7 @@
ALOGW("Unknown child chain: %d", chain);
return -EINVAL;
}
- if (mUseBpfOwnerMatch != BpfLevel::NONE) {
+ if (mUseBpfOwnerMatch) {
return gCtls->trafficCtrl.changeUidOwnerRule(chain, uid, rule, firewallType);
}
@@ -347,12 +346,12 @@
int FirewallController::replaceUidChain(
const std::string &name, bool isWhitelist, const std::vector<int32_t>& uids) {
- if (mUseBpfOwnerMatch != BpfLevel::NONE) {
+ if (mUseBpfOwnerMatch) {
return gCtls->trafficCtrl.replaceUidOwnerMap(name, isWhitelist, uids);
- }
- std::string commands4 = makeUidRules(V4, name.c_str(), isWhitelist, uids);
- std::string commands6 = makeUidRules(V6, name.c_str(), isWhitelist, uids);
- return execIptablesRestore(V4, commands4.c_str()) | execIptablesRestore(V6, commands6.c_str());
+ }
+ std::string commands4 = makeUidRules(V4, name.c_str(), isWhitelist, uids);
+ std::string commands6 = makeUidRules(V6, name.c_str(), isWhitelist, uids);
+ return execIptablesRestore(V4, commands4.c_str()) | execIptablesRestore(V6, commands6.c_str());
}
/* static */
diff --git a/server/FirewallController.h b/server/FirewallController.h
index 43da322..620f196 100644
--- a/server/FirewallController.h
+++ b/server/FirewallController.h
@@ -104,7 +104,7 @@
// fails with EPERM. Netd can therefore assumes the max valid uid to be const.
const uid_t mMaxUid;
FirewallType mFirewallType;
- android::bpf::BpfLevel mUseBpfOwnerMatch;
+ bool mUseBpfOwnerMatch;
std::set<std::string> mIfaceRules;
int attachChain(const char*, const char*);
int detachChain(const char*, const char*);
diff --git a/server/FirewallControllerTest.cpp b/server/FirewallControllerTest.cpp
index bd933b4..1b53fb8 100644
--- a/server/FirewallControllerTest.cpp
+++ b/server/FirewallControllerTest.cpp
@@ -42,7 +42,7 @@
// This unit test currently doesn't cover the eBPF owner match case so
// we have to manually turn eBPF support off.
// TODO: find a way to unit test the eBPF code path.
- mFw.mUseBpfOwnerMatch = android::bpf::BpfLevel::NONE;
+ mFw.mUseBpfOwnerMatch = false;
}
FirewallController mFw;
diff --git a/server/FwmarkServer.cpp b/server/FwmarkServer.cpp
index 51d5398..60981e5 100644
--- a/server/FwmarkServer.cpp
+++ b/server/FwmarkServer.cpp
@@ -22,6 +22,9 @@
#include <unistd.h>
#include <utils/String16.h>
+#include <android-base/cmsg.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
#include <binder/IServiceManager.h>
#include <netd_resolv/resolv.h> // NETID_UNSET
@@ -32,6 +35,8 @@
#include "TrafficController.h"
using android::String16;
+using android::base::ReceiveFileDescriptorVector;
+using android::base::unique_fd;
using android::net::metrics::INetdEventListener;
namespace android {
@@ -62,7 +67,9 @@
: SocketListener(SOCKET_NAME, true),
mNetworkController(networkController),
mEventReporter(eventReporter),
- mTrafficCtrl(trafficCtrl) {}
+ mTrafficCtrl(trafficCtrl),
+ mRedirectSocketCalls(
+ android::base::GetBoolProperty("ro.vendor.redirect_socket_calls", false)) {}
bool FwmarkServer::onDataAvailable(SocketClient* client) {
int socketFd = -1;
@@ -81,36 +88,40 @@
return false;
}
+static bool hasDestinationAddress(FwmarkCommand::CmdId cmdId, bool redirectSocketCalls) {
+ if (redirectSocketCalls) {
+ return (cmdId == FwmarkCommand::ON_SENDTO || cmdId == FwmarkCommand::ON_CONNECT ||
+ cmdId == FwmarkCommand::ON_SENDMSG || cmdId == FwmarkCommand::ON_SENDMMSG ||
+ cmdId == FwmarkCommand::ON_CONNECT_COMPLETE);
+ } else {
+ return (cmdId == FwmarkCommand::ON_CONNECT_COMPLETE);
+ }
+}
+
int FwmarkServer::processClient(SocketClient* client, int* socketFd) {
FwmarkCommand command;
FwmarkConnectInfo connectInfo;
- iovec iov[2] = {
- { &command, sizeof(command) },
- { &connectInfo, sizeof(connectInfo) },
- };
- msghdr message;
- memset(&message, 0, sizeof(message));
- message.msg_iov = iov;
- message.msg_iovlen = ARRAY_SIZE(iov);
+ char buf[sizeof(command) + sizeof(connectInfo)];
+ std::vector<unique_fd> received_fds;
+ ssize_t messageLength =
+ ReceiveFileDescriptorVector(client->getSocket(), buf, sizeof(buf), 1, &received_fds);
- union {
- cmsghdr cmh;
- char cmsg[CMSG_SPACE(sizeof(*socketFd))];
- } cmsgu;
-
- memset(cmsgu.cmsg, 0, sizeof(cmsgu.cmsg));
- message.msg_control = cmsgu.cmsg;
- message.msg_controllen = sizeof(cmsgu.cmsg);
-
- int messageLength = TEMP_FAILURE_RETRY(recvmsg(client->getSocket(), &message, MSG_CMSG_CLOEXEC));
- if (messageLength <= 0) {
+ if (messageLength < 0) {
return -errno;
+ } else if (messageLength == 0) {
+ return -ESHUTDOWN;
}
- if (!((command.cmdId != FwmarkCommand::ON_CONNECT_COMPLETE && messageLength == sizeof(command))
- || (command.cmdId == FwmarkCommand::ON_CONNECT_COMPLETE
- && messageLength == sizeof(command) + sizeof(connectInfo)))) {
+ memcpy(&command, buf, sizeof(command));
+ memcpy(&connectInfo, buf + sizeof(command), sizeof(connectInfo));
+
+ size_t expectedLen = sizeof(command);
+ if (hasDestinationAddress(command.cmdId, mRedirectSocketCalls)) {
+ expectedLen += sizeof(connectInfo);
+ }
+
+ if (messageLength != static_cast<ssize_t>(expectedLen)) {
return -EBADMSG;
}
@@ -131,16 +142,16 @@
return mTrafficCtrl->deleteTagData(command.trafficCtrlInfo, command.uid, client->getUid());
}
- cmsghdr* const cmsgh = CMSG_FIRSTHDR(&message);
- if (cmsgh && cmsgh->cmsg_level == SOL_SOCKET && cmsgh->cmsg_type == SCM_RIGHTS &&
- cmsgh->cmsg_len == CMSG_LEN(sizeof(*socketFd))) {
- memcpy(socketFd, CMSG_DATA(cmsgh), sizeof(*socketFd));
- }
-
- if (*socketFd < 0) {
+ if (received_fds.size() != 1) {
+ LOG(ERROR) << "FwmarkServer received " << received_fds.size() << " fds from client?";
+ return -EBADF;
+ } else if (received_fds[0] < 0) {
+ LOG(ERROR) << "FwmarkServer received fd -1 from ReceiveFileDescriptorVector?";
return -EBADF;
}
+ *socketFd = received_fds[0].release();
+
int family;
socklen_t familyLen = sizeof(family);
if (getsockopt(*socketFd, SOL_SOCKET, SO_DOMAIN, &family, &familyLen) == -1) {
@@ -239,6 +250,12 @@
break;
}
+ case FwmarkCommand::ON_SENDMMSG:
+ case FwmarkCommand::ON_SENDMSG:
+ case FwmarkCommand::ON_SENDTO: {
+ return 0;
+ }
+
case FwmarkCommand::SELECT_NETWORK: {
fwmark.netId = command.netId;
if (command.netId == NETID_UNSET) {
diff --git a/server/FwmarkServer.h b/server/FwmarkServer.h
index c17dc0d..32d5791 100644
--- a/server/FwmarkServer.h
+++ b/server/FwmarkServer.h
@@ -43,6 +43,7 @@
NetworkController* const mNetworkController;
EventReporter* mEventReporter;
TrafficController* mTrafficCtrl;
+ bool mRedirectSocketCalls;
};
} // namespace net
diff --git a/server/IdletimerController.cpp b/server/IdletimerController.cpp
index acb8c6a..103e7cd 100644
--- a/server/IdletimerController.cpp
+++ b/server/IdletimerController.cpp
@@ -115,7 +115,6 @@
#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 bad4ba2..7504fb9 100644
--- a/server/InterfaceController.cpp
+++ b/server/InterfaceController.cpp
@@ -30,7 +30,6 @@
#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>
@@ -56,13 +55,6 @@
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";
@@ -290,9 +282,9 @@
switch (mode) {
case INetd::IPV6_ADDR_GEN_MODE_EUI64:
- // Ignore return value. If /proc/.../stable_secret is
+ // Ignore return value. If /proc/.../addr_gen_mode is
// missing we're probably in EUI64 mode already.
- writeValueToPath(ipv6_proc_path, interface.c_str(), "stable_secret", "");
+ writeValueToPath(ipv6_proc_path, interface.c_str(), "addr_gen_mode", "0");
break;
case INetd::IPV6_ADDR_GEN_MODE_STABLE_PRIVACY: {
return enableStablePrivacyAddresses(interface, getProperty, setProperty);
@@ -368,11 +360,13 @@
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);
@@ -505,8 +499,9 @@
}
}
- RETURN_STATUS_IF_IFCERROR(
- ifc_add_address(cfg.ifName.c_str(), cfg.ipv4Addr.c_str(), cfg.prefixLength));
+ if (int ret = ifc_add_address(cfg.ifName.c_str(), cfg.ipv4Addr.c_str(), cfg.prefixLength)) {
+ return statusFromErrno(-ret, "Failed to add addr");
+ }
return ok;
}
diff --git a/server/IptablesRestoreController.cpp b/server/IptablesRestoreController.cpp
index 8339177..10cedfa 100644
--- a/server/IptablesRestoreController.cpp
+++ b/server/IptablesRestoreController.cpp
@@ -258,13 +258,12 @@
return;
}
- ALOGE("iptables error:\n"
- "------- COMMAND -------\n"
- "%s\n"
- "------- ERROR -------\n"
- "%s"
- "----------------------\n",
- command.c_str(), process->errBuf.c_str());
+ ALOGE("iptables error:\n");
+ ALOGE("------- COMMAND -------\n");
+ ALOGE("%s\n", command.c_str());
+ ALOGE("------- ERROR -------\n");
+ ALOGE("%s", process->errBuf.c_str());
+ ALOGE("----------------------\n");
process->errBuf.clear();
}
diff --git a/server/IptablesRestoreControllerTest.cpp b/server/IptablesRestoreControllerTest.cpp
index d01d7ce..20f6183 100644
--- a/server/IptablesRestoreControllerTest.cpp
+++ b/server/IptablesRestoreControllerTest.cpp
@@ -14,14 +14,16 @@
* 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 <gmock/gmock.h>
-#include <gtest/gtest.h>
+#include <cinttypes>
+#include <iostream>
+#include <string>
#define LOG_TAG "IptablesRestoreControllerTest"
#include <android-base/stringprintf.h>
@@ -53,9 +55,7 @@
int mIptablesLock = -1;
std::string mChainName;
- static void SetUpTestCase() {
- blockSigpipe();
- }
+ static void SetUpTestSuite() { 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));
}
- float timeTaken = s.getTimeAndReset();
- fprintf(stderr, " Add/del %d UID rules via restore: %.1fms (%.2fms per operation)\n",
- iterations, timeTaken, timeTaken / 2 / iterations);
+ int64_t timeTaken = s.getTimeAndResetUs();
+ std::cerr << " Add/del " << iterations << " UID rules via restore: " << timeTaken
+ << "us (" << (timeTaken / 2 / iterations) << "us per operation)" << std::endl;
}
}
diff --git a/server/LocalNetwork.h b/server/LocalNetwork.h
index 2aa7900..7a39a9d 100644
--- a/server/LocalNetwork.h
+++ b/server/LocalNetwork.h
@@ -14,13 +14,11 @@
* limitations under the License.
*/
-#ifndef NETD_SERVER_LOCAL_NETWORK_H
-#define NETD_SERVER_LOCAL_NETWORK_H
+#pragma once
#include "Network.h"
-namespace android {
-namespace net {
+namespace android::net {
class LocalNetwork : public Network {
public:
@@ -29,11 +27,8 @@
private:
Type getType() const override;
- int addInterface(const std::string& interface) override WARN_UNUSED_RESULT;
- int removeInterface(const std::string& interface) override WARN_UNUSED_RESULT;
+ [[nodiscard]] int addInterface(const std::string& interface) override;
+ [[nodiscard]] int removeInterface(const std::string& interface) override;
};
-} // namespace net
-} // namespace android
-
-#endif // NETD_SERVER_LOCAL_NETWORK_H
+} // namespace android::net
diff --git a/server/MDnsSdListener.h b/server/MDnsSdListener.h
index 47ddc28..83cf23e 100644
--- a/server/MDnsSdListener.h
+++ b/server/MDnsSdListener.h
@@ -21,6 +21,7 @@
#include <dns_sd.h>
#include <sysutils/FrameworkListener.h>
#include <mutex>
+#include <string>
#include "NetdCommand.h"
@@ -79,6 +80,7 @@
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/NdcDispatcher.cpp b/server/NdcDispatcher.cpp
index 7692a9c..48f7d9f 100644
--- a/server/NdcDispatcher.cpp
+++ b/server/NdcDispatcher.cpp
@@ -50,22 +50,22 @@
using android::base::StringPrintf;
using android::binder::Status;
-#define PARSE_INT_RETURN_IF_FAIL(cli, label, intLabel, errMsg, addErrno) \
- do { \
- if (!android::base::ParseInt(label, &intLabel)) { \
- errno = EINVAL; \
- cli->sendMsg(ResponseCode::OperationFailed, errMsg, addErrno); \
- return 0; \
- } \
+#define PARSE_INT_RETURN_IF_FAIL(cli, label, intLabel, errMsg, addErrno) \
+ do { \
+ if (!android::base::ParseInt((label), &(intLabel))) { \
+ errno = EINVAL; \
+ (cli)->sendMsg(ResponseCode::OperationFailed, (errMsg), (addErrno)); \
+ return 0; \
+ } \
} while (0)
-#define PARSE_UINT_RETURN_IF_FAIL(cli, label, intLabel, errMsg, addErrno) \
- do { \
- if (!android::base::ParseUint(label, &intLabel)) { \
- errno = EINVAL; \
- cli->sendMsg(ResponseCode::OperationFailed, errMsg, addErrno); \
- return 0; \
- } \
+#define PARSE_UINT_RETURN_IF_FAIL(cli, label, intLabel, errMsg, addErrno) \
+ do { \
+ if (!android::base::ParseUint((label), &(intLabel))) { \
+ errno = EINVAL; \
+ (cli)->sendMsg(ResponseCode::OperationFailed, (errMsg), (addErrno)); \
+ return 0; \
+ } \
} while (0)
namespace android {
@@ -271,15 +271,9 @@
true);
return 0;
}
- } else if (!strcmp(flag, "broadcast")) {
- // currently ignored
- } else if (!strcmp(flag, "multicast")) {
- // currently ignored
- } else if (!strcmp(flag, "running")) {
- // currently ignored
- } else if (!strcmp(flag, "loopback")) {
- // currently ignored
- } else if (!strcmp(flag, "point-to-point")) {
+ } else if (!strcmp(flag, "broadcast") || !strcmp(flag, "multicast") ||
+ !strcmp(flag, "running") || !strcmp(flag, "loopback") ||
+ !strcmp(flag, "point-to-point")) {
// currently ignored
} else {
cli->sendMsg(ResponseCode::CommandParameterError, "Flag unsupported", false);
@@ -304,7 +298,7 @@
false);
return 0;
}
- int enable = !strncmp(argv[3], "enable", 7);
+ int enable = !strcmp(argv[3], "enable");
Status status = mNetd->interfaceSetIPv6PrivacyExtensions(std::string(argv[2]), enable);
if (status.isOk()) {
cli->sendMsg(ResponseCode::CommandOkay, "IPv6 privacy extensions changed", false);
@@ -321,7 +315,7 @@
return 0;
}
- int enable = !strncmp(argv[3], "enable", 7);
+ int enable = !strcmp(argv[3], "enable");
Status status = mNetd->interfaceSetEnableIPv6(std::string(argv[2]), enable);
if (status.isOk()) {
cli->sendMsg(ResponseCode::CommandOkay, "IPv6 state changed", false);
@@ -986,7 +980,7 @@
bool add = false;
if (!strcmp(argv[nextArg], "add")) {
add = true;
- } else if (strcmp(argv[nextArg], "remove")) {
+ } else if (strcmp(argv[nextArg], "remove") != 0) {
return syntaxError(cli, "Unknown argument");
}
++nextArg;
@@ -1106,7 +1100,7 @@
return syntaxError(cli, "Missing netId");
}
netId = stringToNetId(argv[3]);
- } else if (strcmp(argv[2], "clear")) {
+ } else if (strcmp(argv[2], "clear") != 0) {
return syntaxError(cli, "Unknown argument");
}
if (Status status = mNetd->networkSetDefault(netId); !status.isOk()) {
@@ -1133,7 +1127,7 @@
return syntaxError(cli, "Unknown permission");
}
nextArg = 5;
- } else if (strcmp(argv[3], "clear")) {
+ } else if (strcmp(argv[3], "clear") != 0) {
return syntaxError(cli, "Unknown argument");
}
if (nextArg == argc) {
diff --git a/server/NetdConstants.cpp b/server/NetdConstants.cpp
index 40f78f6..80282b3 100644
--- a/server/NetdConstants.cpp
+++ b/server/NetdConstants.cpp
@@ -29,7 +29,6 @@
#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 97e75bd..c273e1b 100644
--- a/server/NetdConstants.h
+++ b/server/NetdConstants.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _NETD_CONSTANTS_H
-#define _NETD_CONSTANTS_H
+#pragma once
#include <ifaddrs.h>
#include <netdb.h>
@@ -28,9 +27,6 @@
#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);
@@ -53,24 +49,11 @@
#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) {
@@ -81,8 +64,7 @@
typedef std::unique_ptr<struct ifaddrs, struct IfaddrsDeleter> ScopedIfaddrs;
-namespace android {
-namespace net {
+namespace android::net {
/**
* This lock exists to make NetdNativeService RPCs (which come in on multiple Binder threads)
@@ -92,7 +74,4 @@
*/
extern std::mutex gBigNetdLock;
-} // namespace net
-} // namespace android
-
-#endif // _NETD_CONSTANTS_H
+} // namespace android::net
diff --git a/server/NetdHwService.cpp b/server/NetdHwService.cpp
index b209aca..15855da 100644
--- a/server/NetdHwService.cpp
+++ b/server/NetdHwService.cpp
@@ -108,7 +108,8 @@
RETURN_IF_NOT_OEM_NETWORK(netId);
return toHalStatus(gCtls->netCtrl.addRoute(netId, ifname.c_str(), destination.c_str(),
- maybeNullString(nexthop), false, INVALID_UID));
+ maybeNullString(nexthop), false, INVALID_UID,
+ 0 /* mtu */));
}
Return <StatusCode> NetdHwService::removeRouteFromOemNetwork(
diff --git a/server/NetdHwService.h b/server/NetdHwService.h
index 458c6fa..dd157de 100644
--- a/server/NetdHwService.h
+++ b/server/NetdHwService.h
@@ -27,7 +27,7 @@
using INetdHw = android::system::net::netd::V1_1::INetd;
using StatusCode = android::system::net::netd::V1_1::INetd::StatusCode;
-class NetdHwService : INetdHw {
+class NetdHwService : public INetdHw {
public:
// 1.0
status_t start();
diff --git a/server/NetdNativeService.cpp b/server/NetdNativeService.cpp
index 65d4727..3bf879b 100644
--- a/server/NetdNativeService.cpp
+++ b/server/NetdNativeService.cpp
@@ -30,19 +30,15 @@
#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>
#include <utils/String16.h>
-#include "BinderUtil.h"
#include "Controllers.h"
+#include "Fwmark.h"
#include "InterfaceController.h"
-#include "NetdConstants.h" // SHA256_SIZE
#include "NetdNativeService.h"
-#include "NetdPermissions.h"
#include "OemNetdListener.h"
#include "Permission.h"
#include "Process.h"
@@ -50,10 +46,13 @@
#include "SockDiag.h"
#include "UidRanges.h"
#include "android/net/BnNetd.h"
+#include "binder_utils/BinderUtil.h"
+#include "binder_utils/NetdPermissions.h"
#include "netid_client.h" // NETID_UNSET
using android::base::StringPrintf;
using android::base::WriteStringToFile;
+using android::net::TetherOffloadRuleParcel;
using android::net::TetherStatsParcel;
using android::net::UidRangeParcel;
using android::netdutils::DumpWriter;
@@ -66,22 +65,36 @@
namespace {
const char OPT_SHORT[] = "--short";
+// The input permissions should be equivalent that this function would return ok if any of them is
+// granted.
binder::Status checkAnyPermission(const std::vector<const char*>& permissions) {
pid_t pid = IPCThreadState::self()->getCallingPid();
uid_t uid = IPCThreadState::self()->getCallingUid();
+ // TODO: Do the pure permission check in this function. Have another method
+ // (e.g. checkNetworkStackPermission) to wrap AID_SYSTEM and
+ // AID_NETWORK_STACK uid check.
// 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).
+ // 1. The system server has the NETWORK_STACK permission, which grants access to all the
+ // IPCs in this file.
// 2. AID_SYSTEM always has all permissions. See ActivityManager#checkComponentPermission.
if (uid == AID_SYSTEM) {
return binder::Status::ok();
}
+ // AID_NETWORK_STACK own MAINLINE_NETWORK_STACK permission, don't IPC to system server to check
+ // MAINLINE_NETWORK_STACK permission. Cross-process(netd, networkstack and system server)
+ // deadlock: http://b/149766727
+ if (uid == AID_NETWORK_STACK) {
+ for (const char* permission : permissions) {
+ if (std::strcmp(permission, PERM_MAINLINE_NETWORK_STACK) == 0) {
+ return binder::Status::ok();
+ }
+ }
+ }
for (const char* permission : permissions) {
if (checkPermission(String16(permission), pid, uid)) {
@@ -116,9 +129,6 @@
} \
} 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)
@@ -133,6 +143,14 @@
return binder::Status::fromServiceSpecificError(status.code(), status.msg().c_str());
}
+template <typename T>
+binder::Status asBinderStatus(const base::Result<T> result) {
+ if (result.ok()) return binder::Status::ok();
+
+ return binder::Status::fromServiceSpecificError(result.error().code(),
+ result.error().message().c_str());
+}
+
inline binder::Status statusFromErrcode(int ret) {
if (ret) {
return binder::Status::fromServiceSpecificError(-ret, strerror(-ret));
@@ -210,6 +228,9 @@
gCtls->clatdCtrl.dump(dw);
dw.blankline();
+ gCtls->tetherCtrl.dump(dw);
+ dw.blankline();
+
{
ScopedIndent indentLog(dw);
if (contains(args, String16(OPT_SHORT))) {
@@ -239,7 +260,7 @@
}
binder::Status NetdNativeService::isAlive(bool *alive) {
- NETD_BIG_LOCK_RPC(PERM_CONNECTIVITY_INTERNAL, PERM_MAINLINE_NETWORK_STACK);
+ NETD_BIG_LOCK_RPC(PERM_NETWORK_STACK, PERM_MAINLINE_NETWORK_STACK);
*alive = true;
@@ -248,16 +269,14 @@
binder::Status NetdNativeService::firewallReplaceUidChain(const std::string& chainName,
bool isWhitelist, const std::vector<int32_t>& uids, bool *ret) {
- NETD_LOCKING_RPC(gCtls->firewallCtrl.lock, PERM_CONNECTIVITY_INTERNAL,
- PERM_MAINLINE_NETWORK_STACK);
+ NETD_LOCKING_RPC(gCtls->firewallCtrl.lock, PERM_NETWORK_STACK, PERM_MAINLINE_NETWORK_STACK);
int err = gCtls->firewallCtrl.replaceUidChain(chainName, isWhitelist, uids);
*ret = (err == 0);
return binder::Status::ok();
}
binder::Status NetdNativeService::bandwidthEnableDataSaver(bool enable, bool *ret) {
- NETD_LOCKING_RPC(gCtls->bandwidthCtrl.lock, PERM_CONNECTIVITY_INTERNAL,
- PERM_MAINLINE_NETWORK_STACK);
+ NETD_LOCKING_RPC(gCtls->bandwidthCtrl.lock, PERM_NETWORK_STACK, PERM_MAINLINE_NETWORK_STACK);
int err = gCtls->bandwidthCtrl.enableDataSaver(enable);
*ret = (err == 0);
return binder::Status::ok();
@@ -324,7 +343,7 @@
}
binder::Status NetdNativeService::networkCreatePhysical(int32_t netId, int32_t permission) {
- ENFORCE_INTERNAL_PERMISSIONS();
+ ENFORCE_NETWORK_STACK_PERMISSIONS();
int ret = gCtls->netCtrl.createPhysicalNetwork(netId, convertPermission(permission));
return statusFromErrcode(ret);
}
@@ -343,13 +362,13 @@
}
binder::Status NetdNativeService::networkAddInterface(int32_t netId, const std::string& iface) {
- ENFORCE_INTERNAL_PERMISSIONS();
+ ENFORCE_NETWORK_STACK_PERMISSIONS();
int ret = gCtls->netCtrl.addInterfaceToNetwork(netId, iface.c_str());
return statusFromErrcode(ret);
}
binder::Status NetdNativeService::networkRemoveInterface(int32_t netId, const std::string& iface) {
- ENFORCE_INTERNAL_PERMISSIONS();
+ ENFORCE_NETWORK_STACK_PERMISSIONS();
int ret = gCtls->netCtrl.removeInterfaceFromNetwork(netId, iface.c_str());
return statusFromErrcode(ret);
}
@@ -357,7 +376,7 @@
binder::Status NetdNativeService::networkAddUidRanges(
int32_t netId, const std::vector<UidRangeParcel>& uidRangeArray) {
// NetworkController::addUsersToNetwork is thread-safe.
- ENFORCE_INTERNAL_PERMISSIONS();
+ ENFORCE_NETWORK_STACK_PERMISSIONS();
int ret = gCtls->netCtrl.addUsersToNetwork(netId, UidRanges(uidRangeArray));
return statusFromErrcode(ret);
}
@@ -365,7 +384,7 @@
binder::Status NetdNativeService::networkRemoveUidRanges(
int32_t netId, const std::vector<UidRangeParcel>& uidRangeArray) {
// NetworkController::removeUsersFromNetwork is thread-safe.
- ENFORCE_INTERNAL_PERMISSIONS();
+ ENFORCE_NETWORK_STACK_PERMISSIONS();
int ret = gCtls->netCtrl.removeUsersFromNetwork(netId, UidRanges(uidRangeArray));
return statusFromErrcode(ret);
}
@@ -377,7 +396,7 @@
// the CommandListener "network" command will need to hold this lock too, not just the ones that
// read/modify network internal state (that is sufficient for ::dump() because it doesn't
// look at routes, but it's not enough here).
- NETD_BIG_LOCK_RPC(PERM_CONNECTIVITY_INTERNAL, PERM_MAINLINE_NETWORK_STACK);
+ NETD_BIG_LOCK_RPC(PERM_NETWORK_STACK, PERM_MAINLINE_NETWORK_STACK);
UidRanges uidRanges(uidRangeArray);
int err;
@@ -391,7 +410,7 @@
binder::Status NetdNativeService::socketDestroy(const std::vector<UidRangeParcel>& uids,
const std::vector<int32_t>& skipUids) {
- ENFORCE_INTERNAL_PERMISSIONS();
+ ENFORCE_NETWORK_STACK_PERMISSIONS();
SockDiag sd;
if (!sd.open()) {
@@ -417,6 +436,8 @@
namespace {
+constexpr const int UNUSED_IFINDEX = 0;
+
void tetherAddStatsByInterface(TetherController::TetherStats* tetherStatsParcel,
const TetherController::TetherStats& tetherStats) {
if (tetherStatsParcel->extIface == tetherStats.extIface) {
@@ -434,6 +455,7 @@
result.rxPackets = stats.rxPackets;
result.txBytes = stats.txBytes;
result.txPackets = stats.txPackets;
+ result.ifIndex = UNUSED_IFINDEX;
return result;
}
@@ -480,7 +502,7 @@
binder::Status NetdNativeService::interfaceAddAddress(const std::string &ifName,
const std::string &addrString, int prefixLength) {
- ENFORCE_INTERNAL_PERMISSIONS();
+ ENFORCE_NETWORK_STACK_PERMISSIONS();
const int err = InterfaceController::addAddress(
ifName.c_str(), addrString.c_str(), prefixLength);
if (err != 0) {
@@ -492,7 +514,7 @@
binder::Status NetdNativeService::interfaceDelAddress(const std::string &ifName,
const std::string &addrString, int prefixLength) {
- ENFORCE_INTERNAL_PERMISSIONS();
+ ENFORCE_NETWORK_STACK_PERMISSIONS();
const int err = InterfaceController::delAddress(
ifName.c_str(), addrString.c_str(), prefixLength);
if (err != 0) {
@@ -573,7 +595,6 @@
binder::Status NetdNativeService::ipSecSetEncapSocketOwner(const ParcelFileDescriptor& socket,
int newUid) {
ENFORCE_NETWORK_STACK_PERMISSIONS();
- gLog.log("ipSecSetEncapSocketOwner()");
uid_t callerUid = IPCThreadState::self()->getCallingUid();
return asBinderStatus(
@@ -587,8 +608,7 @@
int32_t inSpi,
int32_t* outSpi) {
// Necessary locking done in IpSecService and kernel
- ENFORCE_INTERNAL_PERMISSIONS();
- gLog.log("ipSecAllocateSpi()");
+ ENFORCE_NETWORK_STACK_PERMISSIONS();
return asBinderStatus(gCtls->xfrmCtrl.ipSecAllocateSpi(
transformId,
sourceAddress,
@@ -606,8 +626,7 @@
const std::vector<uint8_t>& aeadKey, int32_t aeadIcvBits, int32_t encapType,
int32_t encapLocalPort, int32_t encapRemotePort, int32_t interfaceId) {
// Necessary locking done in IpSecService and kernel
- ENFORCE_INTERNAL_PERMISSIONS();
- gLog.log("ipSecAddSecurityAssociation()");
+ ENFORCE_NETWORK_STACK_PERMISSIONS();
return asBinderStatus(gCtls->xfrmCtrl.ipSecAddSecurityAssociation(
transformId, mode, sourceAddress, destinationAddress, underlyingNetId, spi, markValue,
markMask, authAlgo, authKey, authTruncBits, cryptAlgo, cryptKey, cryptTruncBits,
@@ -620,8 +639,7 @@
const std::string& destinationAddress, int32_t spi, int32_t markValue, int32_t markMask,
int32_t interfaceId) {
// Necessary locking done in IpSecService and kernel
- ENFORCE_INTERNAL_PERMISSIONS();
- gLog.log("ipSecDeleteSecurityAssociation()");
+ ENFORCE_NETWORK_STACK_PERMISSIONS();
return asBinderStatus(gCtls->xfrmCtrl.ipSecDeleteSecurityAssociation(
transformId, sourceAddress, destinationAddress, spi, markValue, markMask, interfaceId));
}
@@ -630,8 +648,7 @@
const ParcelFileDescriptor& socket, int32_t transformId, int32_t direction,
const std::string& sourceAddress, const std::string& destinationAddress, int32_t spi) {
// Necessary locking done in IpSecService and kernel
- ENFORCE_INTERNAL_PERMISSIONS();
- gLog.log("ipSecApplyTransportModeTransform()");
+ ENFORCE_NETWORK_STACK_PERMISSIONS();
return asBinderStatus(gCtls->xfrmCtrl.ipSecApplyTransportModeTransform(
socket.get(), transformId, direction, sourceAddress, destinationAddress, spi));
}
@@ -639,8 +656,7 @@
binder::Status NetdNativeService::ipSecRemoveTransportModeTransform(
const ParcelFileDescriptor& socket) {
// Necessary locking done in IpSecService and kernel
- ENFORCE_INTERNAL_PERMISSIONS();
- gLog.log("ipSecRemoveTransportModeTransform()");
+ ENFORCE_NETWORK_STACK_PERMISSIONS();
return asBinderStatus(gCtls->xfrmCtrl.ipSecRemoveTransportModeTransform(socket.get()));
}
@@ -652,7 +668,6 @@
int32_t markMask, int32_t interfaceId) {
// Necessary locking done in IpSecService and kernel
ENFORCE_NETWORK_STACK_PERMISSIONS();
- gLog.log("ipSecAddSecurityPolicy()");
return asBinderStatus(gCtls->xfrmCtrl.ipSecAddSecurityPolicy(
transformId, selAddrFamily, direction, tmplSrcAddress, tmplDstAddress, spi, markValue,
markMask, interfaceId));
@@ -664,7 +679,6 @@
int32_t markValue, int32_t markMask, int32_t interfaceId) {
// Necessary locking done in IpSecService and kernel
ENFORCE_NETWORK_STACK_PERMISSIONS();
- gLog.log("ipSecAddSecurityPolicy()");
return asBinderStatus(gCtls->xfrmCtrl.ipSecUpdateSecurityPolicy(
transformId, selAddrFamily, direction, tmplSrcAddress, tmplDstAddress, spi, markValue,
markMask, interfaceId));
@@ -676,7 +690,6 @@
int32_t markMask, int32_t interfaceId) {
// Necessary locking done in IpSecService and kernel
ENFORCE_NETWORK_STACK_PERMISSIONS();
- gLog.log("ipSecAddSecurityPolicy()");
return asBinderStatus(gCtls->xfrmCtrl.ipSecDeleteSecurityPolicy(
transformId, selAddrFamily, direction, markValue, markMask, interfaceId));
}
@@ -829,6 +842,7 @@
}
namespace {
+
std::string addCurlyBrackets(const std::string& s) {
return "{" + s + "}";
}
@@ -908,11 +922,19 @@
}
binder::Status NetdNativeService::tetherStart(const std::vector<std::string>& dhcpRanges) {
+ TetherConfigParcel config;
+ config.usingLegacyDnsProxy = true;
+ config.dhcpRanges = dhcpRanges;
+ return tetherStartWithConfiguration(config);
+}
+
+binder::Status NetdNativeService::tetherStartWithConfiguration(const TetherConfigParcel& config) {
NETD_LOCKING_RPC(gCtls->tetherCtrl.lock, PERM_NETWORK_STACK, PERM_MAINLINE_NETWORK_STACK);
- if (dhcpRanges.size() % 2 == 1) {
+ if (config.dhcpRanges.size() % 2 == 1) {
return statusFromErrcode(-EINVAL);
}
- int res = gCtls->tetherCtrl.startTethering(dhcpRanges);
+ // TODO: Pass TetherConfigParcel directly.
+ int res = gCtls->tetherCtrl.startTethering(config.usingLegacyDnsProxy, config.dhcpRanges);
return statusFromErrcode(res);
}
@@ -963,6 +985,39 @@
return binder::Status::ok();
}
+binder::Status NetdNativeService::networkAddRouteParcel(int32_t netId,
+ const RouteInfoParcel& route) {
+ // Public methods of NetworkController are thread-safe.
+ ENFORCE_NETWORK_STACK_PERMISSIONS();
+ bool legacy = false;
+ uid_t uid = 0; // UID is only meaningful for legacy routes.
+
+ // convert Parcel to parameters
+ int res = gCtls->netCtrl.addRoute(netId, route.ifName.c_str(), route.destination.c_str(),
+ route.nextHop.empty() ? nullptr : route.nextHop.c_str(),
+ legacy, uid, route.mtu);
+ return statusFromErrcode(res);
+}
+
+binder::Status NetdNativeService::networkUpdateRouteParcel(int32_t netId,
+ const RouteInfoParcel& route) {
+ // Public methods of NetworkController are thread-safe.
+ ENFORCE_NETWORK_STACK_PERMISSIONS();
+ bool legacy = false;
+ uid_t uid = 0; // UID is only meaningful for legacy routes.
+
+ // convert Parcel to parameters
+ int res = gCtls->netCtrl.updateRoute(netId, route.ifName.c_str(), route.destination.c_str(),
+ route.nextHop.empty() ? nullptr : route.nextHop.c_str(),
+ legacy, uid, route.mtu);
+ return statusFromErrcode(res);
+}
+
+binder::Status NetdNativeService::networkRemoveRouteParcel(int32_t netId,
+ const RouteInfoParcel& route) {
+ return networkRemoveRoute(netId, route.ifName, route.destination, route.nextHop);
+}
+
binder::Status NetdNativeService::networkAddRoute(int32_t netId, const std::string& ifName,
const std::string& destination,
const std::string& nextHop) {
@@ -971,7 +1026,7 @@
bool legacy = false;
uid_t uid = 0; // UID is only meaningful for legacy routes.
int res = gCtls->netCtrl.addRoute(netId, ifName.c_str(), destination.c_str(),
- nextHop.empty() ? nullptr : nextHop.c_str(), legacy, uid);
+ nextHop.empty() ? nullptr : nextHop.c_str(), legacy, uid, 0);
return statusFromErrcode(res);
}
@@ -993,7 +1048,7 @@
bool legacy = true;
int res = gCtls->netCtrl.addRoute(netId, ifName.c_str(), destination.c_str(),
nextHop.empty() ? nullptr : nextHop.c_str(), legacy,
- (uid_t) uid);
+ (uid_t)uid, 0);
return statusFromErrcode(res);
}
@@ -1186,5 +1241,76 @@
return binder::Status::ok();
}
+binder::Status NetdNativeService::getFwmarkForNetwork(int32_t netId, MarkMaskParcel* markMask) {
+ ENFORCE_NETWORK_STACK_PERMISSIONS();
+
+ Fwmark fwmark;
+ fwmark.netId = netId;
+ markMask->mask = FWMARK_NET_ID_MASK;
+ markMask->mark = fwmark.intValue;
+ return binder::Status::ok();
+}
+
+binder::Status NetdNativeService::tetherOffloadRuleAdd(const TetherOffloadRuleParcel& rule) {
+ ENFORCE_NETWORK_STACK_PERMISSIONS();
+
+ return asBinderStatus(gCtls->tetherCtrl.addOffloadRule(rule));
+}
+
+binder::Status NetdNativeService::tetherOffloadRuleRemove(const TetherOffloadRuleParcel& rule) {
+ ENFORCE_NETWORK_STACK_PERMISSIONS();
+
+ return asBinderStatus(gCtls->tetherCtrl.removeOffloadRule(rule));
+}
+
+namespace {
+
+constexpr const char UNUSED_IFNAME[] = "";
+
+TetherStatsParcel toTetherStatsParcel(const TetherController::TetherOffloadStats& stats) {
+ TetherStatsParcel result;
+ result.iface = UNUSED_IFNAME;
+ result.rxBytes = stats.rxBytes;
+ result.rxPackets = stats.rxPackets;
+ result.txBytes = stats.txBytes;
+ result.txPackets = stats.txPackets;
+ result.ifIndex = stats.ifIndex;
+ return result;
+}
+
+} // namespace
+
+binder::Status NetdNativeService::tetherOffloadGetStats(
+ std::vector<TetherStatsParcel>* tetherStatsParcelVec) {
+ NETD_LOCKING_RPC(gCtls->tetherCtrl.lock, PERM_NETWORK_STACK, PERM_MAINLINE_NETWORK_STACK);
+
+ tetherStatsParcelVec->clear();
+ const auto& statsList = gCtls->tetherCtrl.getTetherOffloadStats();
+ if (!isOk(statsList)) {
+ return asBinderStatus(statsList);
+ }
+ for (const auto& stats : statsList.value()) {
+ tetherStatsParcelVec->push_back(toTetherStatsParcel(stats));
+ }
+ return binder::Status::ok();
+}
+
+binder::Status NetdNativeService::tetherOffloadSetInterfaceQuota(int ifIndex, int64_t quotaBytes) {
+ NETD_LOCKING_RPC(gCtls->tetherCtrl.lock, PERM_NETWORK_STACK, PERM_MAINLINE_NETWORK_STACK);
+ int res = gCtls->tetherCtrl.setTetherOffloadInterfaceQuota(ifIndex, quotaBytes);
+ return statusFromErrcode(res);
+}
+
+binder::Status NetdNativeService::tetherOffloadGetAndClearStats(
+ int ifIndex, android::net::TetherStatsParcel* tetherStats) {
+ NETD_LOCKING_RPC(gCtls->tetherCtrl.lock, PERM_NETWORK_STACK, PERM_MAINLINE_NETWORK_STACK);
+ const auto& stats = gCtls->tetherCtrl.getAndClearTetherOffloadStats(ifIndex);
+ if (!stats.ok()) {
+ return asBinderStatus(stats);
+ }
+ *tetherStats = toTetherStatsParcel(stats.value());
+ return binder::Status::ok();
+}
+
} // namespace net
} // namespace android
diff --git a/server/NetdNativeService.h b/server/NetdNativeService.h
index 68c9886..7b7f9b3 100644
--- a/server/NetdNativeService.h
+++ b/server/NetdNativeService.h
@@ -76,6 +76,9 @@
const std::vector<UidRangeParcel>& uids) override;
binder::Status networkRejectNonSecureVpn(bool enable,
const std::vector<UidRangeParcel>& uids) override;
+ binder::Status networkAddRouteParcel(int32_t netId, const RouteInfoParcel& route) override;
+ binder::Status networkUpdateRouteParcel(int32_t netId, const RouteInfoParcel& route) override;
+ binder::Status networkRemoveRouteParcel(int32_t netId, const RouteInfoParcel& route) override;
binder::Status networkAddRoute(int32_t netId, const std::string& ifName,
const std::string& destination,
const std::string& nextHop) override;
@@ -120,7 +123,10 @@
binder::Status tetherApplyDnsInterfaces(bool *ret) override;
binder::Status tetherGetStats(
std::vector<android::net::TetherStatsParcel>* tetherStatsVec) override;
+ binder::Status tetherOffloadGetStats(
+ std::vector<android::net::TetherStatsParcel>* tetherStatsVec) override;
binder::Status tetherStart(const std::vector<std::string>& dhcpRanges) override;
+ binder::Status tetherStartWithConfiguration(const TetherConfigParcel& config) override;
binder::Status tetherStop() override;
binder::Status tetherIsEnabled(bool* enabled) override;
binder::Status tetherInterfaceAdd(const std::string& ifName) override;
@@ -128,6 +134,16 @@
binder::Status tetherInterfaceList(std::vector<std::string>* ifList) override;
binder::Status tetherDnsSet(int32_t netId, const std::vector<std::string>& dnsAddrs) override;
binder::Status tetherDnsList(std::vector<std::string>* dnsList) override;
+ binder::Status tetherAddForward(const std::string& intIface,
+ const std::string& extIface) override;
+ binder::Status tetherRemoveForward(const std::string& intIface,
+ const std::string& extIface) override;
+ binder::Status tetherOffloadRuleAdd(const android::net::TetherOffloadRuleParcel& rule) override;
+ binder::Status tetherOffloadRuleRemove(
+ const android::net::TetherOffloadRuleParcel& rule) override;
+ binder::Status tetherOffloadSetInterfaceQuota(int ifIndex, int64_t quotaBytes) override;
+ binder::Status tetherOffloadGetAndClearStats(
+ int ifIndex, android::net::TetherStatsParcel* tetherStats) override;
// Interface-related commands.
binder::Status interfaceAddAddress(const std::string &ifName,
@@ -234,11 +250,6 @@
const std::string& toIface) override;
binder::Status ipfwdRemoveInterfaceForward(const std::string& fromIface,
const std::string& toIface) override;
- // Tether-forward-related commands
- binder::Status tetherAddForward(const std::string& intIface,
- const std::string& extIface) override;
- binder::Status tetherRemoveForward(const std::string& intIface,
- const std::string& extIface) override;
// tcp_mem-config command
binder::Status setTcpRWmemorySize(const std::string& rmemValues,
@@ -248,6 +259,7 @@
const android::sp<android::net::INetdUnsolicitedEventListener>& listener) override;
binder::Status getOemNetd(android::sp<android::IBinder>* listener) override;
+ binder::Status getFwmarkForNetwork(int32_t netId, MarkMaskParcel* markmask);
private:
std::vector<uid_t> intsToUids(const std::vector<int32_t>& intUids);
diff --git a/server/NetlinkCommands.cpp b/server/NetlinkCommands.cpp
index 7af33fe..acefa8e 100644
--- a/server/NetlinkCommands.cpp
+++ b/server/NetlinkCommands.cpp
@@ -65,17 +65,25 @@
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.
-#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) {
+OPTNONE int sendNetlinkRequest(uint16_t action, uint16_t flags, iovec* iov, int iovlen,
+ const NetlinkDumpCallback* callback) {
nlmsghdr nlmsg = {
.nlmsg_type = action,
.nlmsg_flags = flags,
@@ -146,8 +154,8 @@
return 0;
}
-WARN_UNUSED_RESULT int rtNetlinkFlush(uint16_t getAction, uint16_t deleteAction,
- const char *what, const NetlinkDumpFilter& shouldDelete) {
+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);
@@ -201,7 +209,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 ef1ff48..11fb8e0 100644
--- a/server/NetlinkCommands.h
+++ b/server/NetlinkCommands.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef NETD_SERVER_NETLINK_UTIL_H
-#define NETD_SERVER_NETLINK_UTIL_H
+#pragma once
#include <functional>
#include <linux/netlink.h>
@@ -23,13 +22,13 @@
#include "NetdConstants.h"
-namespace android {
-namespace net {
+namespace android::net {
const sockaddr_nl KERNEL_NLADDR = {AF_NETLINK, 0, 0, 0};
const uint16_t NETLINK_REQUEST_FLAGS = NLM_F_REQUEST | NLM_F_ACK;
const uint16_t NETLINK_ROUTE_CREATE_FLAGS = NETLINK_REQUEST_FLAGS | NLM_F_CREATE | NLM_F_EXCL;
+const uint16_t NETLINK_ROUTE_REPLACE_FLAGS = NETLINK_REQUEST_FLAGS | NLM_F_REPLACE;
// Don't create rules with NLM_F_EXCL, because operations such as changing network permissions rely
// on make-before-break. The kernel did not complain about duplicate rules until ~4.9, at which
// point it started returning EEXIST. See for example b/69607866 . We can't just ignore the EEXIST
@@ -44,41 +43,29 @@
typedef std::function<bool(nlmsghdr *)> NetlinkDumpFilter;
// Opens an RTNetlink socket and connects it to the kernel.
-WARN_UNUSED_RESULT int openNetlinkSocket(int protocol);
+[[nodiscard]] 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.
-WARN_UNUSED_RESULT int recvNetlinkAck(int sock);
+[[nodiscard]] 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.
-
-// 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);
+[[nodiscard]] 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|.
-WARN_UNUSED_RESULT int processNetlinkDump(int sock, const NetlinkDumpCallback& callback);
+[[nodiscard]] 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".
-WARN_UNUSED_RESULT int rtNetlinkFlush(uint16_t getAction, uint16_t deleteAction,
- const char *what, const NetlinkDumpFilter& shouldDelete);
+[[nodiscard]] 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 net
-} // namespace android
-
-#endif // NETD_SERVER_NETLINK_UTIL_H
+} // namespace android::net
diff --git a/server/NetlinkManager.cpp b/server/NetlinkManager.cpp
index d014443..e3c1db9 100644
--- a/server/NetlinkManager.cpp
+++ b/server/NetlinkManager.cpp
@@ -17,6 +17,7 @@
#include <errno.h>
#include <stdio.h>
#include <string.h>
+#include <unistd.h>
#include <sys/socket.h>
#include <sys/time.h>
diff --git a/server/Network.h b/server/Network.h
index 69af3ff..8417f34 100644
--- a/server/Network.h
+++ b/server/Network.h
@@ -14,16 +14,14 @@
* limitations under the License.
*/
-#ifndef NETD_SERVER_NETWORK_H
-#define NETD_SERVER_NETWORK_H
+#pragma once
#include "NetdConstants.h"
#include <set>
#include <string>
-namespace android {
-namespace net {
+namespace android::net {
// A Network represents a collection of interfaces participating as a single administrative unit.
class Network {
@@ -47,9 +45,9 @@
const std::set<std::string>& getInterfaces() const;
// These return 0 on success or negative errno on failure.
- 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;
+ [[nodiscard]] virtual int addInterface(const std::string& interface) = 0;
+ [[nodiscard]] virtual int removeInterface(const std::string& interface) = 0;
+ [[nodiscard]] int clearInterfaces();
std::string toString() const;
@@ -60,7 +58,4 @@
std::set<std::string> mInterfaces;
};
-} // namespace net
-} // namespace android
-
-#endif // NETD_SERVER_NETWORK_H
+} // namespace android::net
diff --git a/server/NetworkController.cpp b/server/NetworkController.cpp
index 1e4894f..20ae44b 100644
--- a/server/NetworkController.cpp
+++ b/server/NetworkController.cpp
@@ -30,13 +30,13 @@
#include <android-base/strings.h>
#include <cutils/misc.h> // FIRST_APPLICATION_UID
#include <netd_resolv/resolv.h>
-#include <netd_resolv/resolv_stub.h>
#include "log/log.h"
#include "Controllers.h"
#include "DummyNetwork.h"
#include "Fwmark.h"
#include "LocalNetwork.h"
+#include "OffloadUtils.h"
#include "PhysicalNetwork.h"
#include "RouteController.h"
#include "VirtualNetwork.h"
@@ -47,8 +47,7 @@
using android::netdutils::DumpWriter;
-namespace android {
-namespace net {
+namespace android::net {
namespace {
@@ -67,21 +66,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();
- int modifyFallthrough(unsigned vpnNetId, const std::string& physicalInterface,
- Permission permission, bool add) WARN_UNUSED_RESULT;
+ [[nodiscard]] int modifyFallthrough(unsigned vpnNetId, const std::string& physicalInterface,
+ Permission permission, bool add);
-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;
+ private:
+ [[nodiscard]] int addFallthrough(const std::string& physicalInterface,
+ Permission permission) override;
+ [[nodiscard]] int removeFallthrough(const std::string& physicalInterface,
+ Permission permission) override;
- int modifyFallthrough(const std::string& physicalInterface, Permission permission,
- bool add) WARN_UNUSED_RESULT;
+ [[nodiscard]] int modifyFallthrough(const std::string& physicalInterface, Permission permission,
+ bool add);
NetworkController* const mNetworkController;
};
@@ -143,6 +142,22 @@
mProtectableUsers({AID_VPN}) {
mNetworks[LOCAL_NET_ID] = new LocalNetwork(LOCAL_NET_ID);
mNetworks[DUMMY_NET_ID] = new DummyNetwork(DUMMY_NET_ID);
+
+ // Clear all clsact stubs on all interfaces.
+ // TODO: perhaps only remove the clsact on the interface which is added by
+ // RouteController::addInterfaceToPhysicalNetwork. Currently, the netd only
+ // attach the clsact to the interface for the physical network.
+ if (bpf::isBpfSupported()) {
+ const auto& ifaces = InterfaceController::getIfaceNames();
+ if (isOk(ifaces)) {
+ for (const std::string& iface : ifaces.value()) {
+ if (int ifIndex = if_nametoindex(iface.c_str())) {
+ // Ignore the error because the interface might not have a clsact.
+ tcQdiscDelDevClsact(ifIndex);
+ }
+ }
+ }
+ }
}
unsigned NetworkController::getDefaultNetwork() const {
@@ -214,8 +229,7 @@
// servers (through the default network). Otherwise, the query is guaranteed to fail.
// http://b/29498052
Network *network = getNetworkLocked(*netId);
- if (network && network->getType() == Network::VIRTUAL &&
- !RESOLV_STUB.resolv_has_nameservers(*netId)) {
+ if (network && network->getType() == Network::VIRTUAL && !resolv_has_nameservers(*netId)) {
*netId = mDefaultNetId;
}
} else {
@@ -224,7 +238,7 @@
// them). Otherwise, use the default network's DNS servers.
// TODO: Consider if we should set the explicit bit here.
VirtualNetwork* virtualNetwork = getVirtualNetworkForUserLocked(uid);
- if (virtualNetwork && RESOLV_STUB.resolv_has_nameservers(virtualNetwork->getNetId())) {
+ if (virtualNetwork && resolv_has_nameservers(virtualNetwork->getNetId())) {
*netId = virtualNetwork->getNetId();
} else {
// TODO: return an error instead of silently doing the DNS lookup on the wrong network.
@@ -236,11 +250,6 @@
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 {
@@ -497,12 +506,20 @@
return ret;
}
- int ifIndex = RouteController::getIfIndex(interface);
- if (ifIndex) {
- mIfindexToLastNetId[ifIndex] = netId;
- } else {
- // Cannot happen, since addInterface() above will have failed.
- ALOGE("inconceivable! added interface %s with no index", interface);
+ // Only populate mIfindexToLastNetId for non-local networks, because for these getIfIndex will
+ // return 0. That's fine though, because that map is only used to prevent force-closing sockets
+ // when the same IP address is handed over from one interface to another interface that is in
+ // the same network but not in the same netId (for now this is done only on VPNs). That is not
+ // useful for the local network because IP addresses in the local network are always assigned by
+ // the device itself and never meaningful on any other network.
+ if (netId != LOCAL_NET_ID) {
+ int ifIndex = RouteController::getIfIndex(interface);
+ if (ifIndex) {
+ mIfindexToLastNetId[ifIndex] = netId;
+ } else {
+ // Cannot happen, since addInterface() above will have failed.
+ ALOGE("inconceivable! added interface %s with no index", interface);
+ }
}
return 0;
}
@@ -593,13 +610,18 @@
}
int NetworkController::addRoute(unsigned netId, const char* interface, const char* destination,
- const char* nexthop, bool legacy, uid_t uid) {
- return modifyRoute(netId, interface, destination, nexthop, true, legacy, uid);
+ const char* nexthop, bool legacy, uid_t uid, int mtu) {
+ return modifyRoute(netId, interface, destination, nexthop, ROUTE_ADD, legacy, uid, mtu);
+}
+
+int NetworkController::updateRoute(unsigned netId, const char* interface, const char* destination,
+ const char* nexthop, bool legacy, uid_t uid, int mtu) {
+ return modifyRoute(netId, interface, destination, nexthop, ROUTE_UPDATE, legacy, uid, mtu);
}
int NetworkController::removeRoute(unsigned netId, const char* interface, const char* destination,
const char* nexthop, bool legacy, uid_t uid) {
- return modifyRoute(netId, interface, destination, nexthop, false, legacy, uid);
+ return modifyRoute(netId, interface, destination, nexthop, ROUTE_REMOVE, legacy, uid, 0);
}
void NetworkController::addInterfaceAddress(unsigned ifIndex, const char* address) {
@@ -633,7 +655,7 @@
}
// Then, check for VPN handover condition
if (mIfindexToLastNetId.find(ifindex) == mIfindexToLastNetId.end()) {
- ALOGE("Interface index %u was never in a currently-connected netId", ifindex);
+ ALOGW("Interface index %u was never in a currently-connected non-local netId", ifindex);
return true;
}
unsigned lastNetId = mIfindexToLastNetId[ifindex];
@@ -776,7 +798,8 @@
}
int NetworkController::modifyRoute(unsigned netId, const char* interface, const char* destination,
- const char* nexthop, bool add, bool legacy, uid_t uid) {
+ const char* nexthop, enum RouteOperation op, bool legacy,
+ uid_t uid, int mtu) {
ScopedRLock lock(mRWLock);
if (!isValidNetworkLocked(netId)) {
@@ -806,8 +829,15 @@
tableType = RouteController::INTERFACE;
}
- return add ? RouteController::addRoute(interface, destination, nexthop, tableType) :
- RouteController::removeRoute(interface, destination, nexthop, tableType);
+ switch (op) {
+ case ROUTE_ADD:
+ return RouteController::addRoute(interface, destination, nexthop, tableType, mtu);
+ case ROUTE_UPDATE:
+ return RouteController::updateRoute(interface, destination, nexthop, tableType, mtu);
+ case ROUTE_REMOVE:
+ return RouteController::removeRoute(interface, destination, nexthop, tableType);
+ }
+ return -EINVAL;
}
int NetworkController::modifyFallthroughLocked(unsigned vpnNetId, bool add) {
@@ -850,5 +880,4 @@
}
}
-} // namespace net
-} // namespace android
+} // namespace android::net
diff --git a/server/NetworkController.h b/server/NetworkController.h
index 4a363b3..ff49c02 100644
--- a/server/NetworkController.h
+++ b/server/NetworkController.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef NETD_SERVER_NETWORK_CONTROLLER_H
-#define NETD_SERVER_NETWORK_CONTROLLER_H
+#pragma once
#include <android-base/thread_annotations.h>
#include <android/multinetwork.h>
@@ -37,8 +36,7 @@
struct android_net_context;
-namespace android {
-namespace net {
+namespace android::net {
typedef std::shared_lock<std::shared_mutex> ScopedRLock;
typedef std::lock_guard<std::shared_mutex> ScopedWLock;
@@ -90,47 +88,48 @@
static constexpr int LOCAL_NET_ID = INetd::LOCAL_NET_ID;
static constexpr int DUMMY_NET_ID = 51;
+ // Route mode for modify route
+ enum RouteOperation { ROUTE_ADD, ROUTE_UPDATE, ROUTE_REMOVE };
+
NetworkController();
unsigned getDefaultNetwork() const;
- int setDefaultNetwork(unsigned netId) WARN_UNUSED_RESULT;
+ [[nodiscard]] int setDefaultNetwork(unsigned netId);
- // 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;
- 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 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 addInterfaceToNetwork(unsigned netId, const char* interface) WARN_UNUSED_RESULT;
- int removeInterfaceFromNetwork(unsigned netId, const char* interface) WARN_UNUSED_RESULT;
+ [[nodiscard]] int addInterfaceToNetwork(unsigned netId, const char* interface);
+ [[nodiscard]] int removeInterfaceFromNetwork(unsigned netId, const char* interface);
Permission getPermissionForUser(uid_t uid) const;
void setPermissionForUsers(Permission permission, const std::vector<uid_t>& uids);
int checkUserNetworkAccess(uid_t uid, unsigned netId) const;
- int setPermissionForNetworks(Permission permission,
- const std::vector<unsigned>& netIds) WARN_UNUSED_RESULT;
+ [[nodiscard]] int setPermissionForNetworks(Permission permission,
+ const std::vector<unsigned>& netIds);
- int addUsersToNetwork(unsigned netId, const UidRanges& uidRanges) WARN_UNUSED_RESULT;
- int removeUsersFromNetwork(unsigned netId, const UidRanges& uidRanges) WARN_UNUSED_RESULT;
+ [[nodiscard]] int addUsersToNetwork(unsigned netId, const UidRanges& uidRanges);
+ [[nodiscard]] int removeUsersFromNetwork(unsigned netId, const UidRanges& uidRanges);
// |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.
- 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;
+ [[nodiscard]] int addRoute(unsigned netId, const char* interface, const char* destination,
+ const char* nexthop, bool legacy, uid_t uid, int mtu);
+ [[nodiscard]] int updateRoute(unsigned netId, const char* interface, const char* destination,
+ const char* nexthop, bool legacy, uid_t uid, int mtu);
+ [[nodiscard]] int removeRoute(unsigned netId, const char* interface, const char* destination,
+ const char* nexthop, bool legacy, uid_t uid);
// Notes that the specified address has appeared on the specified interface.
void addInterfaceAddress(unsigned ifIndex, const char* address);
@@ -147,7 +146,12 @@
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;
@@ -156,11 +160,12 @@
VirtualNetwork* getVirtualNetworkForUserLocked(uid_t uid) const;
Permission getPermissionForUserLocked(uid_t uid) const;
int checkUserNetworkAccessLocked(uid_t uid, unsigned netId) const;
- int createPhysicalNetworkLocked(unsigned netId, Permission permission) WARN_UNUSED_RESULT;
+ [[nodiscard]] int createPhysicalNetworkLocked(unsigned netId, Permission permission);
- 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;
+ [[nodiscard]] int modifyRoute(unsigned netId, const char* interface, const char* destination,
+ const char* nexthop, RouteOperation op, bool legacy, uid_t uid,
+ int mtu);
+ [[nodiscard]] int modifyFallthroughLocked(unsigned vpnNetId, bool add);
void updateTcpSocketMonitorPolling();
class DelegateImpl;
@@ -189,7 +194,4 @@
};
-} // namespace net
-} // namespace android
-
-#endif // NETD_SERVER_NETWORK_CONTROLLER_H
+} // namespace android::net
diff --git a/server/ClatUtils.cpp b/server/OffloadUtils.cpp
similarity index 63%
rename from server/ClatUtils.cpp
rename to server/OffloadUtils.cpp
index ed7287a..a743458 100644
--- a/server/ClatUtils.cpp
+++ b/server/OffloadUtils.cpp
@@ -14,31 +14,30 @@
* limitations under the License.
*/
-#include "ClatUtils.h"
+#include "OffloadUtils.h"
#include <arpa/inet.h>
-#include <errno.h>
#include <linux/if.h>
+#include <linux/if_arp.h>
#include <linux/netlink.h>
#include <linux/pkt_cls.h>
#include <linux/pkt_sched.h>
-#include <linux/rtnetlink.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
-#define LOG_TAG "ClatUtils"
+#define LOG_TAG "OffloadUtils"
#include <log/log.h>
#include "NetlinkCommands.h"
#include "android-base/unique_fd.h"
-#include "bpf/BpfUtils.h"
-#include "netdbpf/bpf_shared.h"
namespace android {
namespace net {
+using std::max;
+
int hardwareAddressType(const std::string& interface) {
base::unique_fd ufd(socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0));
@@ -62,19 +61,29 @@
return ifr.ifr_hwaddr.sa_family;
}
-int getClatIngressMapFd(void) {
- const int fd = bpf::bpfFdGet(CLAT_INGRESS_MAP_PATH, 0);
- return (fd == -1) ? -errno : fd;
-}
+base::Result<bool> isEthernet(const std::string& interface) {
+ int rv = hardwareAddressType(interface);
+ if (rv < 0) {
+ errno = -rv;
+ return ErrnoErrorf("Get hardware address type of interface {} failed", interface);
+ }
-int getClatIngressProgFd(bool with_ethernet_header) {
- const int fd = bpf::bpfFdGet(
- with_ethernet_header ? CLAT_INGRESS_PROG_ETHER_PATH : CLAT_INGRESS_PROG_RAWIP_PATH, 0);
- return (fd == -1) ? -errno : fd;
+ switch (rv) {
+ case ARPHRD_ETHER:
+ return true;
+ case ARPHRD_NONE:
+ case ARPHRD_RAWIP: // in Linux 4.14+ rmnet support was upstreamed and this is 519
+ case 530: // this is ARPHRD_RAWIP on some Android 4.9 kernels with rmnet
+ return false;
+ default:
+ errno = EAFNOSUPPORT; // Address family not supported
+ return ErrnoErrorf("Unknown hardware address type {} on interface {}", rv, interface);
+ }
}
// TODO: use //system/netd/server/NetlinkCommands.cpp:openNetlinkSocket(protocol)
-int openNetlinkSocket(void) {
+// and //system/netd/server/SockDiag.cpp:checkError(fd)
+static int sendAndProcessNetlinkResponse(const void* req, int len) {
base::unique_fd fd(socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE));
if (fd == -1) {
const int err = errno;
@@ -82,10 +91,8 @@
return -err;
}
- int rv;
-
- const int on = 1;
- rv = setsockopt(fd, SOL_NETLINK, NETLINK_CAP_ACK, &on, sizeof(on));
+ static constexpr int on = 1;
+ int rv = setsockopt(fd, SOL_NETLINK, NETLINK_CAP_ACK, &on, sizeof(on));
if (rv) ALOGE("setsockopt(fd, SOL_NETLINK, NETLINK_CAP_ACK, %d)", on);
// this is needed to get sane strace netlink parsing, it allocates the pid
@@ -104,18 +111,17 @@
return -err;
}
- return fd.release();
-}
+ rv = send(fd, req, len, 0);
+ if (rv == -1) return -errno;
+ if (rv != len) return -EMSGSIZE;
-// TODO: merge with //system/netd/server/SockDiag.cpp:checkError(fd)
-int processNetlinkResponse(int fd) {
struct {
nlmsghdr h;
nlmsgerr e;
char buf[256];
} resp = {};
- const int rv = recv(fd, &resp, sizeof(resp), MSG_TRUNC);
+ rv = recv(fd, &resp, sizeof(resp), MSG_TRUNC);
if (rv == -1) {
const int err = errno;
@@ -144,14 +150,13 @@
// ADD: nlMsgType=RTM_NEWQDISC nlMsgFlags=NLM_F_EXCL|NLM_F_CREATE
// REPLACE: nlMsgType=RTM_NEWQDISC nlMsgFlags=NLM_F_CREATE|NLM_F_REPLACE
// DEL: nlMsgType=RTM_DELQDISC nlMsgFlags=0
-int doTcQdiscClsact(int fd, int ifIndex, __u16 nlMsgType, __u16 nlMsgFlags) {
+int doTcQdiscClsact(int ifIndex, uint16_t nlMsgType, uint16_t nlMsgFlags) {
// This is the name of the qdisc we are attaching.
// Some hoop jumping to make this compile time constant with known size,
// so that the structure declaration is well defined at compile time.
#define CLSACT "clsact"
- static const char clsact[] = CLSACT;
// sizeof() includes the terminating NULL
-#define ASCIIZ_LEN_CLSACT sizeof(clsact)
+ static constexpr size_t ASCIIZ_LEN_CLSACT = sizeof(CLSACT);
const struct {
nlmsghdr n;
@@ -184,34 +189,15 @@
.str = CLSACT,
},
};
-#undef ASCIIZ_LEN_CLSACT
#undef CLSACT
- const int rv = send(fd, &req, sizeof(req), 0);
- if (rv == -1) return -errno;
- if (rv != sizeof(req)) return -EMSGSIZE;
-
- return processNetlinkResponse(fd);
+ return sendAndProcessNetlinkResponse(&req, sizeof(req));
}
-int tcQdiscAddDevClsact(int fd, int ifIndex) {
- return doTcQdiscClsact(fd, ifIndex, RTM_NEWQDISC, NLM_F_EXCL | NLM_F_CREATE);
-}
-
-int tcQdiscReplaceDevClsact(int fd, int ifIndex) {
- return doTcQdiscClsact(fd, ifIndex, RTM_NEWQDISC, NLM_F_CREATE | NLM_F_REPLACE);
-}
-
-int tcQdiscDelDevClsact(int fd, int ifIndex) {
- return doTcQdiscClsact(fd, ifIndex, RTM_DELQDISC, 0);
-}
-
-// tc filter add dev .. ingress prio 1 protocol ipv6 bpf object-pinned /sys/fs/bpf/... direct-action
-int tcFilterAddDevBpf(int fd, int ifIndex, int bpfFd, bool ethernet) {
- // The priority doesn't matter until we actually start attaching multiple
- // things to the same interface's ingress point.
- const int prio = 1;
-
+// tc filter add dev .. in/egress prio 1 protocol ipv6/ip bpf object-pinned /sys/fs/bpf/...
+// direct-action
+int tcFilterAddDevBpf(int ifIndex, bool ingress, uint16_t prio, uint16_t proto, int bpfFd,
+ bool ethernet) {
// This is the name of the filter we're attaching (ie. this is the 'bpf'
// packet classifier enabled by kernel config option CONFIG_NET_CLS_BPF.
//
@@ -219,9 +205,8 @@
// so that we can define the struct further down the function with the
// field for this sized correctly already during the build.
#define BPF "bpf"
- const char bpf[] = BPF;
// sizeof() includes the terminating NULL
-#define ASCIIZ_LEN_BPF sizeof(bpf)
+ static constexpr size_t ASCIIZ_LEN_BPF = sizeof(BPF);
// This is to replicate program name suffix used by 'tc' Linux cli
// when it attaches programs.
@@ -229,27 +214,61 @@
// This macro expands (from header files) to:
// prog_clatd_schedcls_ingress_clat_rawip:[*fsobj]
- // and is the name of the pinned ebpf program for ARPHRD_RAWIP interfaces.
+ // and is the name of the pinned ingress ebpf program for ARPHRD_RAWIP interfaces.
// (also compatible with anything that has 0 size L2 header)
-#define NAME_RAWIP CLAT_INGRESS_PROG_RAWIP_NAME FSOBJ_SUFFIX
- const char name_rawip[] = NAME_RAWIP;
+ static constexpr char name_clat_rx_rawip[] = CLAT_INGRESS_PROG_RAWIP_NAME FSOBJ_SUFFIX;
// This macro expands (from header files) to:
// prog_clatd_schedcls_ingress_clat_ether:[*fsobj]
- // and is the name of the pinned ebpf program for ARPHRD_ETHER interfaces.
+ // and is the name of the pinned ingress ebpf program for ARPHRD_ETHER interfaces.
// (also compatible with anything that has standard ethernet header)
-#define NAME_ETHER CLAT_INGRESS_PROG_ETHER_NAME FSOBJ_SUFFIX
- const char name_ether[] = NAME_ETHER;
+ static constexpr char name_clat_rx_ether[] = CLAT_INGRESS_PROG_ETHER_NAME FSOBJ_SUFFIX;
- // The actual name we'll use is determined at run time via 'ethernet'
- // boolean. We need to compile time allocate enough space in the struct
+ // This macro expands (from header files) to:
+ // prog_clatd_schedcls_egress_clat_rawip:[*fsobj]
+ // and is the name of the pinned egress ebpf program for ARPHRD_RAWIP interfaces.
+ // (also compatible with anything that has 0 size L2 header)
+ static constexpr char name_clat_tx_rawip[] = CLAT_EGRESS_PROG_RAWIP_NAME FSOBJ_SUFFIX;
+
+ // This macro expands (from header files) to:
+ // prog_clatd_schedcls_egress_clat_ether:[*fsobj]
+ // and is the name of the pinned egress ebpf program for ARPHRD_ETHER interfaces.
+ // (also compatible with anything that has standard ethernet header)
+ static constexpr char name_clat_tx_ether[] = CLAT_EGRESS_PROG_ETHER_NAME FSOBJ_SUFFIX;
+
+ // This macro expands (from header files) to:
+ // prog_offload_schedcls_ingress_tether_rawip:[*fsobj]
+ // and is the name of the pinned ingress ebpf program for ARPHRD_RAWIP interfaces.
+ // (also compatible with anything that has 0 size L2 header)
+ static constexpr char name_tether_rawip[] = TETHER_INGRESS_PROG_RAWIP_NAME FSOBJ_SUFFIX;
+
+ // This macro expands (from header files) to:
+ // prog_offload_schedcls_ingress_tether_ether:[*fsobj]
+ // and is the name of the pinned ingress ebpf program for ARPHRD_ETHER interfaces.
+ // (also compatible with anything that has standard ethernet header)
+ static constexpr char name_tether_ether[] = TETHER_INGRESS_PROG_ETHER_NAME FSOBJ_SUFFIX;
+
+#undef FSOBJ_SUFFIX
+
+ // The actual name we'll use is determined at run time via 'ethernet' and 'ingress'
+ // booleans. We need to compile time allocate enough space in the struct
// hence this macro magic to make sure we have enough space for either
- // possibility. In practice both are actually the same size.
-#define ASCIIZ_MAXLEN_NAME \
- ((sizeof(name_rawip) > sizeof(name_ether)) ? sizeof(name_rawip) : sizeof(name_ether))
+ // possibility. In practice some of these are actually the same size.
+ static constexpr size_t ASCIIZ_MAXLEN_NAME = max({
+ sizeof(name_clat_rx_rawip),
+ sizeof(name_clat_rx_ether),
+ sizeof(name_clat_tx_rawip),
+ sizeof(name_clat_tx_ether),
+ sizeof(name_tether_rawip),
+ sizeof(name_tether_ether),
+ });
- // This is not a compile time constant and is used in strcpy below
-#define NAME (ethernet ? NAME_ETHER : NAME_RAWIP)
+ // These are not compile time constants: 'name' is used in strncpy below
+ const char* const name_clat_rx = ethernet ? name_clat_rx_ether : name_clat_rx_rawip;
+ const char* const name_clat_tx = ethernet ? name_clat_tx_ether : name_clat_tx_rawip;
+ const char* const name_clat = ingress ? name_clat_rx : name_clat_tx;
+ const char* const name_tether = ethernet ? name_tether_ether : name_tether_rawip;
+ const char* const name = (prio == PRIO_TETHER) ? name_tether : name_clat;
struct {
nlmsghdr n;
@@ -285,8 +304,9 @@
.tcm_family = AF_UNSPEC,
.tcm_ifindex = ifIndex,
.tcm_handle = TC_H_UNSPEC,
- .tcm_parent = TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_INGRESS),
- .tcm_info = (prio << 16) | htons(ETH_P_IPV6),
+ .tcm_parent = TC_H_MAKE(TC_H_CLSACT,
+ ingress ? TC_H_MIN_INGRESS : TC_H_MIN_EGRESS),
+ .tcm_info = static_cast<__u32>((prio << 16) | htons(proto)),
},
.kind =
{
@@ -321,7 +341,7 @@
.nla_type = TCA_BPF_NAME,
},
// Visible via 'tc filter show', but
- // is overwritten by strcpy below
+ // is overwritten by strncpy below
.str = "placeholder",
},
.flags =
@@ -335,22 +355,37 @@
},
},
};
-
- strncpy(req.options.name.str, NAME, sizeof(req.options.name.str));
-
-#undef NAME
-#undef ASCIIZ_MAXLEN_NAME
-#undef NAME_ETHER
-#undef NAME_RAWIP
-#undef NAME
-#undef ASCIIZ_LEN_BPF
#undef BPF
- const int rv = send(fd, &req, sizeof(req), 0);
- if (rv == -1) return -errno;
- if (rv != sizeof(req)) return -EMSGSIZE;
+ strncpy(req.options.name.str, name, sizeof(req.options.name.str));
- return processNetlinkResponse(fd);
+ return sendAndProcessNetlinkResponse(&req, sizeof(req));
+}
+
+// tc filter del dev .. in/egress prio .. protocol ..
+int tcFilterDelDev(int ifIndex, bool ingress, uint16_t prio, uint16_t proto) {
+ const struct {
+ nlmsghdr n;
+ tcmsg t;
+ } req = {
+ .n =
+ {
+ .nlmsg_len = sizeof(req),
+ .nlmsg_type = RTM_DELTFILTER,
+ .nlmsg_flags = NETLINK_REQUEST_FLAGS,
+ },
+ .t =
+ {
+ .tcm_family = AF_UNSPEC,
+ .tcm_ifindex = ifIndex,
+ .tcm_handle = TC_H_UNSPEC,
+ .tcm_parent = TC_H_MAKE(TC_H_CLSACT,
+ ingress ? TC_H_MIN_INGRESS : TC_H_MIN_EGRESS),
+ .tcm_info = static_cast<__u32>((prio << 16) | htons(proto)),
+ },
+ };
+
+ return sendAndProcessNetlinkResponse(&req, sizeof(req));
}
} // namespace net
diff --git a/server/OffloadUtils.h b/server/OffloadUtils.h
new file mode 100644
index 0000000..818fd39
--- /dev/null
+++ b/server/OffloadUtils.h
@@ -0,0 +1,146 @@
+/*
+ * 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 <android-base/result.h>
+#include <errno.h>
+#include <linux/if_ether.h>
+#include <linux/rtnetlink.h>
+
+#include <string>
+
+#include "bpf/BpfUtils.h"
+#include "netdbpf/bpf_shared.h"
+
+namespace android {
+namespace net {
+
+// For better code clarity - do not change values - used for booleans like
+// with_ethernet_header or isEthernet.
+constexpr bool RAWIP = false;
+constexpr bool ETHER = true;
+
+// For better code clarity when used for 'bool ingress' parameter.
+constexpr bool EGRESS = false;
+constexpr bool INGRESS = true;
+
+// The priority of clat/tether hooks - smaller is higher priority.
+constexpr uint16_t PRIO_CLAT = 1;
+constexpr uint16_t PRIO_TETHER = 2;
+
+// this returns an ARPHRD_* constant or a -errno
+int hardwareAddressType(const std::string& interface);
+
+base::Result<bool> isEthernet(const std::string& interface);
+
+inline int getClatEgressMapFd(void) {
+ const int fd = bpf::mapRetrieveRW(CLAT_EGRESS_MAP_PATH);
+ return (fd == -1) ? -errno : fd;
+}
+
+inline int getClatEgressProgFd(bool with_ethernet_header) {
+ const int fd = bpf::retrieveProgram(with_ethernet_header ? CLAT_EGRESS_PROG_ETHER_PATH
+ : CLAT_EGRESS_PROG_RAWIP_PATH);
+ return (fd == -1) ? -errno : fd;
+}
+
+inline int getClatIngressMapFd(void) {
+ const int fd = bpf::mapRetrieveRW(CLAT_INGRESS_MAP_PATH);
+ return (fd == -1) ? -errno : fd;
+}
+
+inline int getClatIngressProgFd(bool with_ethernet_header) {
+ const int fd = bpf::retrieveProgram(with_ethernet_header ? CLAT_INGRESS_PROG_ETHER_PATH
+ : CLAT_INGRESS_PROG_RAWIP_PATH);
+ return (fd == -1) ? -errno : fd;
+}
+
+inline int getTetherIngressMapFd(void) {
+ const int fd = bpf::mapRetrieveRW(TETHER_INGRESS_MAP_PATH);
+ return (fd == -1) ? -errno : fd;
+}
+
+inline int getTetherIngressProgFd(bool with_ethernet_header) {
+ const int fd = bpf::retrieveProgram(with_ethernet_header ? TETHER_INGRESS_PROG_ETHER_PATH
+ : TETHER_INGRESS_PROG_RAWIP_PATH);
+ return (fd == -1) ? -errno : fd;
+}
+
+inline int getTetherStatsMapFd(void) {
+ const int fd = bpf::mapRetrieveRW(TETHER_STATS_MAP_PATH);
+ return (fd == -1) ? -errno : fd;
+}
+
+inline int getTetherLimitMapFd(void) {
+ const int fd = bpf::mapRetrieveRW(TETHER_LIMIT_MAP_PATH);
+ return (fd == -1) ? -errno : fd;
+}
+
+int doTcQdiscClsact(int ifIndex, uint16_t nlMsgType, uint16_t nlMsgFlags);
+
+inline int tcQdiscAddDevClsact(int ifIndex) {
+ return doTcQdiscClsact(ifIndex, RTM_NEWQDISC, NLM_F_EXCL | NLM_F_CREATE);
+}
+
+inline int tcQdiscReplaceDevClsact(int ifIndex) {
+ return doTcQdiscClsact(ifIndex, RTM_NEWQDISC, NLM_F_CREATE | NLM_F_REPLACE);
+}
+
+inline int tcQdiscDelDevClsact(int ifIndex) {
+ return doTcQdiscClsact(ifIndex, RTM_DELQDISC, 0);
+}
+
+// tc filter add dev .. in/egress prio 1 protocol ipv6/ip bpf object-pinned /sys/fs/bpf/...
+// direct-action
+int tcFilterAddDevBpf(int ifIndex, bool ingress, uint16_t prio, uint16_t proto, int bpfFd,
+ bool ethernet);
+
+// tc filter add dev .. ingress prio 1 protocol ipv6 bpf object-pinned /sys/fs/bpf/... direct-action
+inline int tcFilterAddDevIngressClatIpv6(int ifIndex, int bpfFd, bool ethernet) {
+ return tcFilterAddDevBpf(ifIndex, INGRESS, PRIO_CLAT, ETH_P_IPV6, bpfFd, ethernet);
+}
+
+// tc filter add dev .. egress prio 1 protocol ip bpf object-pinned /sys/fs/bpf/... direct-action
+inline int tcFilterAddDevEgressClatIpv4(int ifIndex, int bpfFd, bool ethernet) {
+ return tcFilterAddDevBpf(ifIndex, EGRESS, PRIO_CLAT, ETH_P_IP, bpfFd, ethernet);
+}
+
+// tc filter add dev .. ingress prio 2 protocol ipv6 bpf object-pinned /sys/fs/bpf/... direct-action
+inline int tcFilterAddDevIngressTether(int ifIndex, int bpfFd, bool ethernet) {
+ return tcFilterAddDevBpf(ifIndex, INGRESS, PRIO_TETHER, ETH_P_IPV6, bpfFd, ethernet);
+}
+
+// tc filter del dev .. in/egress prio .. protocol ..
+int tcFilterDelDev(int ifIndex, bool ingress, uint16_t prio, uint16_t proto);
+
+// tc filter del dev .. ingress prio 1 protocol ipv6
+inline int tcFilterDelDevIngressClatIpv6(int ifIndex) {
+ return tcFilterDelDev(ifIndex, INGRESS, PRIO_CLAT, ETH_P_IPV6);
+}
+
+// tc filter del dev .. egress prio 1 protocol ip
+inline int tcFilterDelDevEgressClatIpv4(int ifIndex) {
+ return tcFilterDelDev(ifIndex, EGRESS, PRIO_CLAT, ETH_P_IP);
+}
+
+// tc filter del dev .. ingress prio 2 protocol ipv6
+inline int tcFilterDelDevIngressTether(int ifIndex) {
+ return tcFilterDelDev(ifIndex, INGRESS, PRIO_TETHER, ETH_P_IPV6);
+}
+
+} // namespace net
+} // namespace android
diff --git a/server/OffloadUtilsTest.cpp b/server/OffloadUtilsTest.cpp
new file mode 100644
index 0000000..1760c1d
--- /dev/null
+++ b/server/OffloadUtilsTest.cpp
@@ -0,0 +1,332 @@
+/*
+ * 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.
+ *
+ * OffloadUtilsTest.cpp - unit tests for OffloadUtils.cpp
+ */
+
+#include <gtest/gtest.h>
+
+#include "OffloadUtils.h"
+
+#include <linux/if_arp.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+
+#include "bpf/BpfUtils.h"
+#include "netdbpf/bpf_shared.h"
+
+namespace android {
+namespace net {
+
+class OffloadUtilsTest : public ::testing::Test {
+ public:
+ void SetUp() {}
+};
+
+TEST_F(OffloadUtilsTest, HardwareAddressTypeOfNonExistingIf) {
+ ASSERT_EQ(-ENODEV, hardwareAddressType("not_existing_if"));
+}
+
+TEST_F(OffloadUtilsTest, HardwareAddressTypeOfLoopback) {
+ ASSERT_EQ(ARPHRD_LOOPBACK, hardwareAddressType("lo"));
+}
+
+// If wireless 'wlan0' interface exists it should be Ethernet.
+TEST_F(OffloadUtilsTest, HardwareAddressTypeOfWireless) {
+ int type = hardwareAddressType("wlan0");
+ if (type == -ENODEV) return;
+
+ ASSERT_EQ(ARPHRD_ETHER, type);
+}
+
+// If cellular 'rmnet_data0' interface exists it should
+// *probably* not be Ethernet and instead be RawIp.
+TEST_F(OffloadUtilsTest, HardwareAddressTypeOfCellular) {
+ int type = hardwareAddressType("rmnet_data0");
+ if (type == -ENODEV) return;
+
+ ASSERT_NE(ARPHRD_ETHER, type);
+
+ // ARPHRD_RAWIP is 530 on some pre-4.14 Qualcomm devices.
+ if (type == 530) return;
+
+ ASSERT_EQ(ARPHRD_RAWIP, type);
+}
+
+TEST_F(OffloadUtilsTest, IsEthernetOfNonExistingIf) {
+ auto res = isEthernet("not_existing_if");
+ ASSERT_FALSE(res.ok());
+ ASSERT_EQ(ENODEV, res.error().code());
+}
+
+TEST_F(OffloadUtilsTest, IsEthernetOfLoopback) {
+ auto res = isEthernet("lo");
+ ASSERT_FALSE(res.ok());
+ ASSERT_EQ(EAFNOSUPPORT, res.error().code());
+}
+
+// If wireless 'wlan0' interface exists it should be Ethernet.
+// See also HardwareAddressTypeOfWireless.
+TEST_F(OffloadUtilsTest, IsEthernetOfWireless) {
+ auto res = isEthernet("wlan0");
+ if (!res.ok() && res.error().code() == ENODEV) return;
+
+ ASSERT_RESULT_OK(res);
+ ASSERT_TRUE(res.value());
+}
+
+// If cellular 'rmnet_data0' interface exists it should
+// *probably* not be Ethernet and instead be RawIp.
+// See also HardwareAddressTypeOfCellular.
+TEST_F(OffloadUtilsTest, IsEthernetOfCellular) {
+ auto res = isEthernet("rmnet_data0");
+ if (!res.ok() && res.error().code() == ENODEV) return;
+
+ ASSERT_RESULT_OK(res);
+ ASSERT_FALSE(res.value());
+}
+
+TEST_F(OffloadUtilsTest, GetClatEgressMapFd) {
+ SKIP_IF_BPF_NOT_SUPPORTED;
+
+ int fd = getClatEgressMapFd();
+ ASSERT_GE(fd, 3); // 0,1,2 - stdin/out/err, thus fd >= 3
+ EXPECT_EQ(FD_CLOEXEC, fcntl(fd, F_GETFD));
+ close(fd);
+}
+
+TEST_F(OffloadUtilsTest, GetClatEgressRawIpProgFd) {
+ SKIP_IF_BPF_NOT_SUPPORTED;
+
+ int fd = getClatEgressProgFd(RAWIP);
+ ASSERT_GE(fd, 3);
+ EXPECT_EQ(FD_CLOEXEC, fcntl(fd, F_GETFD));
+ close(fd);
+}
+
+TEST_F(OffloadUtilsTest, GetClatEgressEtherProgFd) {
+ SKIP_IF_BPF_NOT_SUPPORTED;
+
+ int fd = getClatEgressProgFd(ETHER);
+ ASSERT_GE(fd, 3);
+ EXPECT_EQ(FD_CLOEXEC, fcntl(fd, F_GETFD));
+ close(fd);
+}
+
+TEST_F(OffloadUtilsTest, GetClatIngressMapFd) {
+ SKIP_IF_BPF_NOT_SUPPORTED;
+
+ int fd = getClatIngressMapFd();
+ ASSERT_GE(fd, 3); // 0,1,2 - stdin/out/err, thus fd >= 3
+ EXPECT_EQ(FD_CLOEXEC, fcntl(fd, F_GETFD));
+ close(fd);
+}
+
+TEST_F(OffloadUtilsTest, GetClatIngressRawIpProgFd) {
+ SKIP_IF_BPF_NOT_SUPPORTED;
+
+ int fd = getClatIngressProgFd(RAWIP);
+ ASSERT_GE(fd, 3);
+ EXPECT_EQ(FD_CLOEXEC, fcntl(fd, F_GETFD));
+ close(fd);
+}
+
+TEST_F(OffloadUtilsTest, GetClatIngressEtherProgFd) {
+ SKIP_IF_BPF_NOT_SUPPORTED;
+
+ int fd = getClatIngressProgFd(ETHER);
+ ASSERT_GE(fd, 3);
+ EXPECT_EQ(FD_CLOEXEC, fcntl(fd, F_GETFD));
+ close(fd);
+}
+
+TEST_F(OffloadUtilsTest, GetTetherIngressMapFd) {
+ SKIP_IF_BPF_NOT_SUPPORTED;
+
+ int fd = getTetherIngressMapFd();
+ ASSERT_GE(fd, 3); // 0,1,2 - stdin/out/err, thus fd >= 3
+ EXPECT_EQ(FD_CLOEXEC, fcntl(fd, F_GETFD));
+ close(fd);
+}
+
+TEST_F(OffloadUtilsTest, GetTetherIngressRawIpProgFd) {
+ // Currently only implementing downstream direction offload.
+ // RX Rawip -> TX Ether requires header adjustments and thus 4.14.
+ SKIP_IF_EXTENDED_BPF_NOT_SUPPORTED;
+
+ int fd = getTetherIngressProgFd(RAWIP);
+ ASSERT_GE(fd, 3);
+ EXPECT_EQ(FD_CLOEXEC, fcntl(fd, F_GETFD));
+ close(fd);
+}
+
+TEST_F(OffloadUtilsTest, GetTetherIngressEtherProgFd) {
+ // Currently only implementing downstream direction offload.
+ // RX Ether -> TX Ether does not require header adjustments
+ SKIP_IF_BPF_NOT_SUPPORTED;
+
+ int fd = getTetherIngressProgFd(ETHER);
+ ASSERT_GE(fd, 3);
+ EXPECT_EQ(FD_CLOEXEC, fcntl(fd, F_GETFD));
+ close(fd);
+}
+
+TEST_F(OffloadUtilsTest, GetTetherStatsMapFd) {
+ SKIP_IF_BPF_NOT_SUPPORTED;
+
+ int fd = getTetherStatsMapFd();
+ ASSERT_GE(fd, 3); // 0,1,2 - stdin/out/err, thus fd >= 3
+ EXPECT_EQ(FD_CLOEXEC, fcntl(fd, F_GETFD));
+ close(fd);
+}
+
+TEST_F(OffloadUtilsTest, GetTetherLimitMapFd) {
+ SKIP_IF_BPF_NOT_SUPPORTED;
+
+ int fd = getTetherLimitMapFd();
+ ASSERT_GE(fd, 3); // 0,1,2 - stdin/out/err, thus fd >= 3
+ EXPECT_EQ(FD_CLOEXEC, fcntl(fd, F_GETFD));
+ close(fd);
+}
+
+// The SKIP_IF_BPF_NOT_SUPPORTED macro is effectively a check for 4.9+ kernel
+// combined with a launched on P device. Ie. it's a test for 4.9-P or better.
+
+// NET_SCH_INGRESS is only enabled starting with 4.9-Q and as such we need
+// a separate way to test for this...
+int doKernelSupportsNetSchIngress(void) {
+ // NOLINTNEXTLINE(cert-env33-c)
+ return system("zcat /proc/config.gz | egrep -q '^CONFIG_NET_SCH_INGRESS=[my]$'");
+}
+
+// NET_CLS_BPF is only enabled starting with 4.9-Q...
+int doKernelSupportsNetClsBpf(void) {
+ // NOLINTNEXTLINE(cert-env33-c)
+ return system("zcat /proc/config.gz | egrep -q '^CONFIG_NET_CLS_BPF=[my]$'");
+}
+
+// Make sure the above functions actually execute correctly rather than failing
+// due to missing binary or execution failure...
+TEST_F(OffloadUtilsTest, KernelSupportsNetFuncs) {
+ // Make sure the file is present and readable and decompressable.
+ // NOLINTNEXTLINE(cert-env33-c)
+ ASSERT_EQ(W_EXITCODE(0, 0), system("zcat /proc/config.gz > /dev/null"));
+
+ int v = doKernelSupportsNetSchIngress();
+ int w = doKernelSupportsNetClsBpf();
+
+ // They should always either return 0 (match) or 1 (no match),
+ // anything else is some sort of exec/environment/etc failure.
+ if (v != W_EXITCODE(1, 0)) ASSERT_EQ(v, W_EXITCODE(0, 0));
+ if (w != W_EXITCODE(1, 0)) ASSERT_EQ(w, W_EXITCODE(0, 0));
+}
+
+// True iff CONFIG_NET_SCH_INGRESS is enabled in /proc/config.gz
+bool kernelSupportsNetSchIngress(void) {
+ return doKernelSupportsNetSchIngress() == W_EXITCODE(0, 0);
+}
+
+// True iff CONFIG_NET_CLS_BPF is enabled in /proc/config.gz
+bool kernelSupportsNetClsBpf(void) {
+ return doKernelSupportsNetClsBpf() == W_EXITCODE(0, 0);
+}
+
+// See Linux kernel source in include/net/flow.h
+#define LOOPBACK_IFINDEX 1
+
+TEST_F(OffloadUtilsTest, AttachReplaceDetachClsactLo) {
+ // Technically does not depend on ebpf, but does depend on clsact,
+ // and we do not really care if it works on pre-4.9-Q anyway.
+ SKIP_IF_BPF_NOT_SUPPORTED;
+ if (!kernelSupportsNetSchIngress()) return;
+
+ // This attaches and detaches a configuration-less and thus no-op clsact
+ // qdisc to loopback interface (and it takes fractions of a second)
+ EXPECT_EQ(0, tcQdiscAddDevClsact(LOOPBACK_IFINDEX));
+ EXPECT_EQ(0, tcQdiscReplaceDevClsact(LOOPBACK_IFINDEX));
+ EXPECT_EQ(0, tcQdiscDelDevClsact(LOOPBACK_IFINDEX));
+ EXPECT_EQ(-EINVAL, tcQdiscDelDevClsact(LOOPBACK_IFINDEX));
+}
+
+static void checkAttachDetachBpfFilterClsactLo(const bool ingress, const bool ethernet) {
+ // This test requires kernel 4.9-Q or better
+ SKIP_IF_BPF_NOT_SUPPORTED;
+ if (!kernelSupportsNetSchIngress()) return;
+ if (!kernelSupportsNetClsBpf()) return;
+
+ const bool extended =
+ (android::bpf::getBpfSupportLevel() >= android::bpf::BpfLevel::EXTENDED_4_14);
+ // Older kernels return EINVAL instead of ENOENT due to lacking proper error propagation...
+ const int errNOENT =
+ (android::bpf::getBpfSupportLevel() >= android::bpf::BpfLevel::EXTENDED_4_19) ? ENOENT
+ : EINVAL;
+
+ int clatBpfFd = ingress ? getClatIngressProgFd(ethernet) : getClatEgressProgFd(ethernet);
+ ASSERT_GE(clatBpfFd, 3);
+
+ int tetherBpfFd = -1;
+ if (extended && ingress) {
+ tetherBpfFd = getTetherIngressProgFd(ethernet);
+ ASSERT_GE(tetherBpfFd, 3);
+ }
+
+ // This attaches and detaches a clsact plus ebpf program to loopback
+ // interface, but it should not affect traffic by virtue of us not
+ // actually populating the ebpf control map.
+ // Furthermore: it only takes fractions of a second.
+ EXPECT_EQ(-EINVAL, tcFilterDelDevIngressClatIpv6(LOOPBACK_IFINDEX));
+ EXPECT_EQ(-EINVAL, tcFilterDelDevEgressClatIpv4(LOOPBACK_IFINDEX));
+ EXPECT_EQ(0, tcQdiscAddDevClsact(LOOPBACK_IFINDEX));
+ EXPECT_EQ(-errNOENT, tcFilterDelDevIngressClatIpv6(LOOPBACK_IFINDEX));
+ EXPECT_EQ(-errNOENT, tcFilterDelDevEgressClatIpv4(LOOPBACK_IFINDEX));
+ if (ingress) {
+ EXPECT_EQ(0, tcFilterAddDevIngressClatIpv6(LOOPBACK_IFINDEX, clatBpfFd, ethernet));
+ if (extended) {
+ EXPECT_EQ(0, tcFilterAddDevIngressTether(LOOPBACK_IFINDEX, tetherBpfFd, ethernet));
+ EXPECT_EQ(0, tcFilterDelDevIngressTether(LOOPBACK_IFINDEX));
+ }
+ EXPECT_EQ(0, tcFilterDelDevIngressClatIpv6(LOOPBACK_IFINDEX));
+ } else {
+ EXPECT_EQ(0, tcFilterAddDevEgressClatIpv4(LOOPBACK_IFINDEX, clatBpfFd, ethernet));
+ EXPECT_EQ(0, tcFilterDelDevEgressClatIpv4(LOOPBACK_IFINDEX));
+ }
+ EXPECT_EQ(-errNOENT, tcFilterDelDevIngressClatIpv6(LOOPBACK_IFINDEX));
+ EXPECT_EQ(-errNOENT, tcFilterDelDevEgressClatIpv4(LOOPBACK_IFINDEX));
+ EXPECT_EQ(0, tcQdiscDelDevClsact(LOOPBACK_IFINDEX));
+ EXPECT_EQ(-EINVAL, tcFilterDelDevIngressClatIpv6(LOOPBACK_IFINDEX));
+ EXPECT_EQ(-EINVAL, tcFilterDelDevEgressClatIpv4(LOOPBACK_IFINDEX));
+
+ if (tetherBpfFd != -1) close(tetherBpfFd);
+ close(clatBpfFd);
+}
+
+TEST_F(OffloadUtilsTest, CheckAttachBpfFilterRawIpClsactEgressLo) {
+ checkAttachDetachBpfFilterClsactLo(EGRESS, RAWIP);
+}
+
+TEST_F(OffloadUtilsTest, CheckAttachBpfFilterEthernetClsactEgressLo) {
+ checkAttachDetachBpfFilterClsactLo(EGRESS, ETHER);
+}
+
+TEST_F(OffloadUtilsTest, CheckAttachBpfFilterRawIpClsactIngressLo) {
+ checkAttachDetachBpfFilterClsactLo(INGRESS, RAWIP);
+}
+
+TEST_F(OffloadUtilsTest, CheckAttachBpfFilterEthernetClsactIngressLo) {
+ checkAttachDetachBpfFilterClsactLo(INGRESS, ETHER);
+}
+
+} // namespace net
+} // namespace android
diff --git a/server/PhysicalNetwork.cpp b/server/PhysicalNetwork.cpp
index 7a1f305..2808fbe 100644
--- a/server/PhysicalNetwork.cpp
+++ b/server/PhysicalNetwork.cpp
@@ -23,13 +23,12 @@
#include "log/log.h"
-namespace android {
-namespace net {
+namespace android::net {
namespace {
-WARN_UNUSED_RESULT int addToDefault(unsigned netId, const std::string& interface,
- Permission permission, PhysicalNetwork::Delegate* delegate) {
+[[nodiscard]] 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;
@@ -40,9 +39,8 @@
return 0;
}
-WARN_UNUSED_RESULT int removeFromDefault(unsigned netId, const std::string& interface,
- Permission permission,
- PhysicalNetwork::Delegate* delegate) {
+[[nodiscard]] 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);
@@ -56,15 +54,13 @@
} // 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;
@@ -92,8 +88,8 @@
// If any of these operations fail, there's no point in logging because RouteController will
// have already logged a message. There's also no point returning an error since there's
// nothing we can do.
- (void) RouteController::addRoute(interface.c_str(), dst, "throw",
- RouteController::INTERFACE);
+ (void)RouteController::addRoute(interface.c_str(), dst, "throw", RouteController::INTERFACE,
+ 0 /* mtu */);
(void) RouteController::removeRoute(interface.c_str(), dst, "throw",
RouteController::INTERFACE);
}
@@ -206,5 +202,4 @@
return 0;
}
-} // namespace net
-} // namespace android
+} // namespace android::net
diff --git a/server/PhysicalNetwork.h b/server/PhysicalNetwork.h
index 89c9443..0720824 100644
--- a/server/PhysicalNetwork.h
+++ b/server/PhysicalNetwork.h
@@ -14,25 +14,23 @@
* limitations under the License.
*/
-#ifndef NETD_SERVER_PHYSICAL_NETWORK_H
-#define NETD_SERVER_PHYSICAL_NETWORK_H
+#pragma once
#include "Network.h"
#include "Permission.h"
-namespace android {
-namespace net {
+namespace android::net {
class PhysicalNetwork : public Network {
-public:
+ public:
class Delegate {
- public:
+ public:
virtual ~Delegate();
- 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;
+ [[nodiscard]] virtual int addFallthrough(const std::string& physicalInterface,
+ Permission permission) = 0;
+ [[nodiscard]] virtual int removeFallthrough(const std::string& physicalInterface,
+ Permission permission) = 0;
};
PhysicalNetwork(unsigned netId, Delegate* delegate);
@@ -40,15 +38,15 @@
// These refer to permissions that apps must have in order to use this network.
Permission getPermission() const;
- int setPermission(Permission permission) WARN_UNUSED_RESULT;
+ [[nodiscard]] int setPermission(Permission permission);
- int addAsDefault() WARN_UNUSED_RESULT;
- int removeAsDefault() WARN_UNUSED_RESULT;
+ [[nodiscard]] int addAsDefault();
+ [[nodiscard]] int removeAsDefault();
-private:
+ private:
Type getType() const override;
- int addInterface(const std::string& interface) override WARN_UNUSED_RESULT;
- int removeInterface(const std::string& interface) override WARN_UNUSED_RESULT;
+ [[nodiscard]] int addInterface(const std::string& interface) override;
+ [[nodiscard]] int removeInterface(const std::string& interface) override;
int destroySocketsLackingPermission(Permission permission);
void invalidateRouteCache(const std::string& interface);
@@ -57,7 +55,4 @@
bool mIsDefault;
};
-} // namespace net
-} // namespace android
-
-#endif // NETD_SERVER_PHYSICAL_NETWORK_H
+} // namespace android::net
diff --git a/server/PppController.cpp b/server/PppController.cpp
index 80a36e9..b80e1a6 100644
--- a/server/PppController.cpp
+++ b/server/PppController.cpp
@@ -14,10 +14,11 @@
* limitations under the License.
*/
-#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
+#include <stdlib.h>
#include <string.h>
+#include <unistd.h>
#include <sys/socket.h>
#include <sys/stat.h>
diff --git a/server/ResolvStub.cpp b/server/ResolvStub.cpp
deleted file mode 100644
index d84572f..0000000
--- a/server/ResolvStub.cpp
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * 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.
- */
-
-#include <string>
-
-#include <dlfcn.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-
-#define LOG_TAG "Netd"
-#include <log/log.h>
-
-#include <netd_resolv/resolv_stub.h>
-
-struct ResolvStub RESOLV_STUB;
-
-inline constexpr char APEX_LIB64_DIR[] = "/apex/com.android.resolv/lib64";
-inline constexpr char APEX_LIB_DIR[] = "/apex/com.android.resolv/lib";
-inline constexpr char LIBNAME[] = "libnetd_resolv.so";
-
-template <typename FunctionType>
-static void resolvStubInitFunction(void* handle, const char* symbol, FunctionType** stubPtr) {
- void* f = dlsym(handle, symbol);
- if (f == nullptr) {
- ALOGE("Can't find symbol %s in %s", symbol, LIBNAME);
- abort();
- }
- *stubPtr = reinterpret_cast<FunctionType*>(f);
-}
-
-int resolv_stub_init() {
- void* netdResolvHandle;
-
- for (const auto& dir : {APEX_LIB64_DIR, APEX_LIB_DIR}) {
- std::string path = std::string(dir) + "/" + LIBNAME;
- netdResolvHandle = dlopen(path.c_str(), RTLD_NOW);
- if (netdResolvHandle != nullptr) {
- ALOGI("Loaded resolver library from %s", path.c_str());
- break;
- }
- ALOGW("dlopen(%s) failed: %s", path.c_str(), strerror(errno));
- }
-
- if (netdResolvHandle == nullptr) {
- ALOGE("Fatal: couldn't open libnetd_resolv");
- abort();
- }
-
-#define STR(x) #x
-#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);
-#undef RESOLV_STUB_LOAD_SYMBOL
-#undef STR
-
- return 0;
-}
diff --git a/server/RouteController.cpp b/server/RouteController.cpp
index 6722372..134bbca 100644
--- a/server/RouteController.cpp
+++ b/server/RouteController.cpp
@@ -33,21 +33,22 @@
#include "Fwmark.h"
#include "NetdConstants.h"
#include "NetlinkCommands.h"
+#include "OffloadUtils.h"
#include "UidRanges.h"
#include <android-base/file.h>
#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include "log/log.h"
-#include "logwrap/logwrap.h"
#include "netid_client.h"
#include "netutils/ifc.h"
+using android::base::StartsWith;
using android::base::StringPrintf;
using android::base::WriteStringToFile;
using android::net::UidRangeParcel;
-namespace android {
-namespace net {
+namespace android::net {
auto RouteController::iptablesRestoreCommandFunction = execIptablesRestoreCommand;
@@ -104,7 +105,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).
-constexpr uint16_t U16_RTA_LENGTH(uint16_t x) {
+static constexpr uint16_t U16_RTA_LENGTH(uint16_t x) {
return RTA_LENGTH(x);
}
@@ -120,16 +121,24 @@
rtattr RTATTR_OIF = { U16_RTA_LENGTH(sizeof(uint32_t)), RTA_OIF };
rtattr RTATTR_PRIO = { U16_RTA_LENGTH(sizeof(uint32_t)), RTA_PRIORITY };
+// One or more nested attributes in the RTA_METRICS attribute.
+rtattr RTATTRX_MTU = { U16_RTA_LENGTH(sizeof(uint32_t)), RTAX_MTU};
+constexpr size_t RTATTRX_MTU_SIZE = RTA_SPACE(sizeof(uint32_t));
+
+// The RTA_METRICS attribute itself.
+constexpr size_t RTATTR_METRICS_SIZE = RTATTRX_MTU_SIZE;
+rtattr RTATTR_METRICS = { U16_RTA_LENGTH(RTATTR_METRICS_SIZE), RTA_METRICS };
+
uint8_t PADDING_BUFFER[RTA_ALIGNTO] = {0, 0, 0, 0};
// END CONSTANTS ----------------------------------------------------------------------------------
-const char *actionName(uint16_t action) {
+static const char* actionName(uint16_t action) {
static const char *ops[4] = {"adding", "deleting", "getting", "???"};
return ops[action % 4];
}
-const char *familyName(uint8_t family) {
+static const char* familyName(uint8_t family) {
switch (family) {
case AF_INET: return "IPv4";
case AF_INET6: return "IPv6";
@@ -139,20 +148,28 @@
// Caller must hold sInterfaceToTableLock.
uint32_t RouteController::getRouteTableForInterfaceLocked(const char* interface) {
- uint32_t index = if_nametoindex(interface);
- 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.
+ // 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()) {
- ALOGE("cannot find interface %s", interface);
+ if (iter != sInterfaceToTable.end()) {
+ return iter->second;
+ }
+
+ uint32_t index = if_nametoindex(interface);
+ if (index == 0) {
+ ALOGE("cannot find interface %s: %s", interface, strerror(errno));
return RT_TABLE_UNSPEC;
}
- return iter->second;
+ index += RouteController::ROUTE_TABLE_OFFSET_FROM_INDEX;
+ sInterfaceToTable[interface] = index;
+ return index;
}
uint32_t RouteController::getIfIndex(const char* interface) {
@@ -164,7 +181,17 @@
return 0;
}
- return iter->second - ROUTE_TABLE_OFFSET_FROM_INDEX;
+ // For interfaces that are not in the local network, the routing table is always the interface
+ // index plus ROUTE_TABLE_OFFSET_FROM_INDEX. But for interfaces in the local network, there's no
+ // way to know the interface index from this table. Return 0 here so callers of this method do
+ // not get confused.
+ // TODO: stop calling this method from any caller that only wants interfaces in client mode.
+ int ifindex = iter->second;
+ if (ifindex == ROUTE_TABLE_LOCAL_NETWORK) {
+ return 0;
+ }
+
+ return ifindex - ROUTE_TABLE_OFFSET_FROM_INDEX;
}
uint32_t RouteController::getRouteTableForInterface(const char* interface) {
@@ -231,9 +258,10 @@
// range (inclusive). Otherwise, the rule matches packets from all UIDs.
//
// Returns 0 on success or negative errno on failure.
-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) {
+[[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) {
// 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);
@@ -317,23 +345,23 @@
return 0;
}
-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) {
+[[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) {
return modifyIpRule(action, priority, FR_ACT_TO_TBL, table, fwmark, mask, iif, oif, uidStart,
uidEnd);
}
-WARN_UNUSED_RESULT int modifyIpRule(uint16_t action, uint32_t priority, uint32_t table,
- uint32_t fwmark, uint32_t mask) {
+[[nodiscard]] static 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.
-WARN_UNUSED_RESULT int modifyIpRoute(uint16_t action, uint32_t table, const char* interface,
- const char* destination, const char* nexthop) {
+int modifyIpRoute(uint16_t action, uint16_t flags, uint32_t table, const char* interface,
+ const char* destination, const char* nexthop, uint32_t mtu) {
// At least the destination must be non-null.
if (!destination) {
ALOGE("null destination");
@@ -392,33 +420,34 @@
// Assemble a rtmsg and put it in an array of iovec structures.
rtmsg route = {
- .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),
+ .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,
};
rtattr rtaDst = { U16_RTA_LENGTH(rawLength), RTA_DST };
rtattr rtaGateway = { U16_RTA_LENGTH(rawLength), RTA_GATEWAY };
iovec iov[] = {
- { nullptr, 0 },
- { &route, sizeof(route) },
- { &RTATTR_TABLE, sizeof(RTATTR_TABLE) },
- { &table, sizeof(table) },
- { &rtaDst, sizeof(rtaDst) },
- { rawAddress, static_cast<size_t>(rawLength) },
- { &RTATTR_OIF, interface != OIF_NONE ? sizeof(RTATTR_OIF) : 0 },
- { &ifindex, interface != OIF_NONE ? sizeof(ifindex) : 0 },
- { &rtaGateway, nexthop ? sizeof(rtaGateway) : 0 },
- { rawNexthop, nexthop ? static_cast<size_t>(rawLength) : 0 },
- { &RTATTR_PRIO, isDefaultThrowRoute ? sizeof(RTATTR_PRIO) : 0 },
- { &PRIO_THROW, isDefaultThrowRoute ? sizeof(PRIO_THROW) : 0 },
+ { nullptr, 0 },
+ { &route, sizeof(route) },
+ { &RTATTR_TABLE, sizeof(RTATTR_TABLE) },
+ { &table, sizeof(table) },
+ { &rtaDst, sizeof(rtaDst) },
+ { rawAddress, static_cast<size_t>(rawLength) },
+ { &RTATTR_OIF, interface != OIF_NONE ? sizeof(RTATTR_OIF) : 0 },
+ { &ifindex, interface != OIF_NONE ? sizeof(ifindex) : 0 },
+ { &rtaGateway, nexthop ? sizeof(rtaGateway) : 0 },
+ { rawNexthop, nexthop ? static_cast<size_t>(rawLength) : 0 },
+ { &RTATTR_METRICS, mtu != 0 ? sizeof(RTATTR_METRICS) : 0 },
+ { &RTATTRX_MTU, mtu != 0 ? sizeof(RTATTRX_MTU) : 0 },
+ { &mtu, mtu != 0 ? sizeof(mtu) : 0 },
+ { &RTATTR_PRIO, isDefaultThrowRoute ? sizeof(RTATTR_PRIO) : 0 },
+ { &PRIO_THROW, isDefaultThrowRoute ? sizeof(PRIO_THROW) : 0 },
};
- uint16_t flags = (action == RTM_NEWROUTE) ? NETLINK_ROUTE_CREATE_FLAGS : NETLINK_REQUEST_FLAGS;
-
// Allow creating multiple link-local routes in the same table, so we can make IPv6
// work on all interfaces in the local_network table.
if (family == AF_INET6 && IN6_IS_ADDR_LINKLOCAL(reinterpret_cast<in6_addr*>(rawAddress))) {
@@ -440,8 +469,8 @@
// replies, SYN-ACKs, etc).
// + Mark sockets that accept connections from this interface so that the connection stays on the
// same interface.
-WARN_UNUSED_RESULT int modifyIncomingPacketMark(unsigned netId, const char* interface,
- Permission permission, bool add) {
+int modifyIncomingPacketMark(unsigned netId, const char* interface, Permission permission,
+ bool add) {
Fwmark fwmark;
fwmark.netId = netId;
@@ -466,7 +495,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.
-WARN_UNUSED_RESULT int modifyVpnOutputToLocalRule(const char* vpnInterface, bool add) {
+[[nodiscard]] static 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);
@@ -477,8 +506,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.
-WARN_UNUSED_RESULT int modifyVpnUidRangeRule(uint32_t table, uid_t uidStart, uid_t uidEnd,
- bool secure, bool add) {
+[[nodiscard]] static int modifyVpnUidRangeRule(uint32_t table, uid_t uidStart, uid_t uidEnd,
+ bool secure, bool add) {
Fwmark fwmark;
Fwmark mask;
@@ -505,8 +534,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.
-WARN_UNUSED_RESULT int modifyVpnSystemPermissionRule(unsigned netId, uint32_t table, bool secure,
- bool add) {
+[[nodiscard]] static int modifyVpnSystemPermissionRule(unsigned netId, uint32_t table, bool secure,
+ bool add) {
Fwmark fwmark;
Fwmark mask;
@@ -529,9 +558,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().
-WARN_UNUSED_RESULT int modifyExplicitNetworkRule(unsigned netId, uint32_t table,
- Permission permission, uid_t uidStart,
- uid_t uidEnd, bool add) {
+[[nodiscard]] static int modifyExplicitNetworkRule(unsigned netId, uint32_t table,
+ Permission permission, uid_t uidStart,
+ uid_t uidEnd, bool add) {
Fwmark fwmark;
Fwmark mask;
@@ -552,9 +581,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).
-WARN_UNUSED_RESULT int modifyOutputInterfaceRules(const char* interface, uint32_t table,
- Permission permission, uid_t uidStart,
- uid_t uidEnd, bool add) {
+[[nodiscard]] static int modifyOutputInterfaceRules(const char* interface, uint32_t table,
+ Permission permission, uid_t uidStart,
+ uid_t uidEnd, bool add) {
Fwmark fwmark;
Fwmark mask;
@@ -581,7 +610,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.
-WARN_UNUSED_RESULT int modifyImplicitNetworkRule(unsigned netId, uint32_t table, bool add) {
+[[nodiscard]] static int modifyImplicitNetworkRule(unsigned netId, uint32_t table, bool add) {
Fwmark fwmark;
Fwmark mask;
@@ -603,9 +632,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.
-WARN_UNUSED_RESULT int RouteController::modifyVpnFallthroughRule(uint16_t action, unsigned vpnNetId,
- const char* physicalInterface,
- Permission permission) {
+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;
@@ -625,7 +654,7 @@
}
// Add rules to allow legacy routes added through the requestRouteToHost() API.
-WARN_UNUSED_RESULT int addLegacyRouteRules() {
+[[nodiscard]] static int addLegacyRouteRules() {
Fwmark fwmark;
Fwmark mask;
@@ -651,7 +680,7 @@
}
// Add rules to lookup the local network when specified explicitly or otherwise.
-WARN_UNUSED_RESULT int addLocalNetworkRules(unsigned localNetId) {
+[[nodiscard]] static int addLocalNetworkRules(unsigned localNetId) {
if (int ret = modifyExplicitNetworkRule(localNetId, ROUTE_TABLE_LOCAL_NETWORK, PERMISSION_NONE,
INVALID_UID, INVALID_UID, ACTION_ADD)) {
return ret;
@@ -672,7 +701,7 @@
const char *interface = DummyNetwork::INTERFACE_NAME;
uint32_t table = getRouteTableForInterface(interface);
if (table == RT_TABLE_UNSPEC) {
- // getRouteTableForInterface has already looged an error.
+ // getRouteTableForInterface has already logged an error.
return -ESRCH;
}
@@ -690,11 +719,13 @@
return ret;
}
- if ((ret = modifyIpRoute(RTM_NEWROUTE, table, interface, "0.0.0.0/0", nullptr))) {
+ if ((ret = modifyIpRoute(RTM_NEWROUTE, NETLINK_ROUTE_CREATE_FLAGS, table, interface,
+ "0.0.0.0/0", nullptr, 0 /* mtu */))) {
return ret;
}
- if ((ret = modifyIpRoute(RTM_NEWROUTE, table, interface, "::/0", nullptr))) {
+ if ((ret = modifyIpRoute(RTM_NEWROUTE, NETLINK_ROUTE_CREATE_FLAGS, table, interface, "::/0",
+ nullptr, 0 /* mtu */))) {
return ret;
}
@@ -705,12 +736,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.
-WARN_UNUSED_RESULT int addUnreachableRule() {
+[[nodiscard]] static 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);
}
-WARN_UNUSED_RESULT int modifyLocalNetwork(unsigned netId, const char* interface, bool add) {
+[[nodiscard]] static int modifyLocalNetwork(unsigned netId, const char* interface, bool add) {
if (int ret = modifyIncomingPacketMark(netId, interface, PERMISSION_NONE, add)) {
return ret;
}
@@ -719,8 +750,8 @@
}
/* static */
-WARN_UNUSED_RESULT int RouteController::modifyPhysicalNetwork(unsigned netId, const char* interface,
- Permission permission, bool add) {
+int RouteController::modifyPhysicalNetwork(unsigned netId, const char* interface,
+ Permission permission, bool add) {
uint32_t table = getRouteTableForInterface(interface);
if (table == RT_TABLE_UNSPEC) {
return -ESRCH;
@@ -764,7 +795,7 @@
return 0;
}
-WARN_UNUSED_RESULT int modifyRejectNonSecureNetworkRule(const UidRanges& uidRanges, bool add) {
+[[nodiscard]] static int modifyRejectNonSecureNetworkRule(const UidRanges& uidRanges, bool add) {
Fwmark fwmark;
Fwmark mask;
fwmark.protectedFromVpn = false;
@@ -781,10 +812,9 @@
return 0;
}
-WARN_UNUSED_RESULT int RouteController::modifyVirtualNetwork(unsigned netId, const char* interface,
- const UidRanges& uidRanges,
- bool secure, bool add,
- bool modifyNonUidBasedRules) {
+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;
@@ -820,8 +850,8 @@
return 0;
}
-WARN_UNUSED_RESULT int RouteController::modifyDefaultNetwork(uint16_t action, const char* interface,
- Permission permission) {
+int RouteController::modifyDefaultNetwork(uint16_t action, const char* interface,
+ Permission permission) {
uint32_t table = getRouteTableForInterface(interface);
if (table == RT_TABLE_UNSPEC) {
return -ESRCH;
@@ -840,9 +870,8 @@
mask.intValue, IIF_LOOPBACK, OIF_NONE, INVALID_UID, INVALID_UID);
}
-WARN_UNUSED_RESULT int RouteController::modifyTetheredNetwork(uint16_t action,
- const char* inputInterface,
- const char* outputInterface) {
+int RouteController::modifyTetheredNetwork(uint16_t action, const char* inputInterface,
+ const char* outputInterface) {
uint32_t table = getRouteTableForInterface(outputInterface);
if (table == RT_TABLE_UNSPEC) {
return -ESRCH;
@@ -854,9 +883,9 @@
// Adds or removes an IPv4 or IPv6 route to the specified table.
// Returns 0 on success or negative errno on failure.
-WARN_UNUSED_RESULT int RouteController::modifyRoute(uint16_t action, const char* interface,
- const char* destination, const char* nexthop,
- TableType tableType) {
+int RouteController::modifyRoute(uint16_t action, uint16_t flags, const char* interface,
+ const char* destination, const char* nexthop, TableType tableType,
+ int mtu) {
uint32_t table;
switch (tableType) {
case RouteController::INTERFACE: {
@@ -880,7 +909,7 @@
}
}
- int ret = modifyIpRoute(action, table, interface, destination, nexthop);
+ int ret = modifyIpRoute(action, flags, table, interface, destination, nexthop, mtu);
// Trying to add a route that already exists shouldn't cause an error.
if (ret && !(action == RTM_NEWROUTE && ret == -EEXIST)) {
return ret;
@@ -889,7 +918,39 @@
return 0;
}
-WARN_UNUSED_RESULT int clearTetheringRules(const char* inputInterface) {
+void maybeModifyQdiscClsact(const char* interface, bool add) {
+ if (!bpf::isBpfSupported()) return;
+
+ // The clsact attaching of v4- tun interface is triggered by ClatdController::maybeStartBpf
+ // because the clat is started before the v4- interface is added to the network and the
+ // clat startup needs to add {in, e}gress filters.
+ // TODO: remove this workaround once v4- tun interface clsact attaching is moved out from
+ // ClatdController::maybeStartBpf.
+ if (StartsWith(interface, "v4-") && add) return;
+
+ // The interface may have already gone away in the delete case.
+ uint32_t ifindex = if_nametoindex(interface);
+ if (!ifindex) {
+ ALOGE("cannot find interface %s", interface);
+ return;
+ }
+
+ if (add) {
+ if (int ret = tcQdiscAddDevClsact(ifindex)) {
+ ALOGE("tcQdiscAddDevClsact(%d[%s]) failure: %s", ifindex, interface, strerror(-ret));
+ return;
+ }
+ } else {
+ if (int ret = tcQdiscDelDevClsact(ifindex)) {
+ ALOGE("tcQdiscDelDevClsact(%d[%s]) failure: %s", ifindex, interface, strerror(-ret));
+ return;
+ }
+ }
+
+ return;
+}
+
+[[nodiscard]] static int clearTetheringRules(const char* inputInterface) {
int ret = 0;
while (ret == 0) {
ret = modifyIpRule(RTM_DELRULE, RULE_PRIORITY_TETHERING, 0, MARK_UNSET, MARK_UNSET,
@@ -911,7 +972,7 @@
return getRtmU32Attribute(nlh, RTA_TABLE);
}
-WARN_UNUSED_RESULT int flushRules() {
+[[nodiscard]] static 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;
@@ -919,7 +980,7 @@
return rtNetlinkFlush(RTM_GETRULE, RTM_DELRULE, "rules", shouldDelete);
}
-WARN_UNUSED_RESULT int RouteController::flushRoutes(uint32_t table) {
+int RouteController::flushRoutes(uint32_t table) {
NetlinkDumpFilter shouldDelete = [table] (nlmsghdr *nlh) {
return getRouteTable(nlh) == table;
};
@@ -928,7 +989,7 @@
}
// Returns 0 on success or negative errno on failure.
-WARN_UNUSED_RESULT int RouteController::flushRoutes(const char* interface) {
+int RouteController::flushRoutes(const char* interface) {
std::lock_guard lock(sInterfaceToTableLock);
uint32_t table = getRouteTableForInterfaceLocked(interface);
@@ -968,11 +1029,21 @@
}
int RouteController::addInterfaceToLocalNetwork(unsigned netId, const char* interface) {
- return modifyLocalNetwork(netId, interface, ACTION_ADD);
+ if (int ret = modifyLocalNetwork(netId, interface, ACTION_ADD)) {
+ return ret;
+ }
+ std::lock_guard lock(sInterfaceToTableLock);
+ sInterfaceToTable[interface] = ROUTE_TABLE_LOCAL_NETWORK;
+ return 0;
}
int RouteController::removeInterfaceFromLocalNetwork(unsigned netId, const char* interface) {
- return modifyLocalNetwork(netId, interface, ACTION_DEL);
+ if (int ret = modifyLocalNetwork(netId, interface, ACTION_DEL)) {
+ return ret;
+ }
+ std::lock_guard lock(sInterfaceToTableLock);
+ sInterfaceToTable.erase(interface);
+ return 0;
}
int RouteController::addInterfaceToPhysicalNetwork(unsigned netId, const char* interface,
@@ -980,6 +1051,7 @@
if (int ret = modifyPhysicalNetwork(netId, interface, permission, ACTION_ADD)) {
return ret;
}
+ maybeModifyQdiscClsact(interface, ACTION_ADD);
updateTableNamesFile();
return 0;
}
@@ -995,6 +1067,7 @@
if (int ret = clearTetheringRules(interface)) {
return ret;
}
+ maybeModifyQdiscClsact(interface, ACTION_DEL);
updateTableNamesFile();
return 0;
}
@@ -1062,13 +1135,21 @@
}
int RouteController::addRoute(const char* interface, const char* destination, const char* nexthop,
- TableType tableType) {
- return modifyRoute(RTM_NEWROUTE, interface, destination, nexthop, tableType);
+ TableType tableType, int mtu) {
+ return modifyRoute(RTM_NEWROUTE, NETLINK_ROUTE_CREATE_FLAGS, interface, destination, nexthop,
+ tableType, mtu);
}
int RouteController::removeRoute(const char* interface, const char* destination,
const char* nexthop, TableType tableType) {
- return modifyRoute(RTM_DELROUTE, interface, destination, nexthop, tableType);
+ return modifyRoute(RTM_DELROUTE, NETLINK_REQUEST_FLAGS, interface, destination, nexthop,
+ tableType, 0);
+}
+
+int RouteController::updateRoute(const char* interface, const char* destination,
+ const char* nexthop, TableType tableType, int mtu) {
+ return modifyRoute(RTM_NEWROUTE, NETLINK_ROUTE_REPLACE_FLAGS, interface, destination, nexthop,
+ tableType, mtu);
}
int RouteController::enableTethering(const char* inputInterface, const char* outputInterface) {
@@ -1094,6 +1175,4 @@
std::mutex RouteController::sInterfaceToTableLock;
std::map<std::string, uint32_t> RouteController::sInterfaceToTable;
-
-} // namespace net
-} // namespace android
+} // namespace android::net
diff --git a/server/RouteController.h b/server/RouteController.h
index d40288b..656fc21 100644
--- a/server/RouteController.h
+++ b/server/RouteController.h
@@ -14,10 +14,9 @@
* limitations under the License.
*/
-#ifndef NETD_SERVER_ROUTE_CONTROLLER_H
-#define NETD_SERVER_ROUTE_CONTROLLER_H
+#pragma once
-#include "NetdConstants.h"
+#include "NetdConstants.h" // IptablesTarget
#include "Permission.h"
#include <android-base/thread_annotations.h>
@@ -27,8 +26,7 @@
#include <map>
#include <mutex>
-namespace android {
-namespace net {
+namespace android::net {
class UidRanges;
@@ -46,7 +44,7 @@
static const char* const LOCAL_MANGLE_INPUT;
- static int Init(unsigned localNetId) WARN_UNUSED_RESULT;
+ [[nodiscard]] static int Init(unsigned localNetId);
// Returns an ifindex given the interface name, by looking up in sInterfaceToTable.
// This is currently only used by NetworkController::addInterfaceToNetwork
@@ -57,55 +55,58 @@
// used to add them.
static uint32_t getIfIndex(const char* interface) EXCLUDES(sInterfaceToTableLock);
- 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 addInterfaceToLocalNetwork(unsigned netId, const char* interface);
+ [[nodiscard]] static int removeInterfaceFromLocalNetwork(unsigned netId, const char* interface);
- 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 addInterfaceToPhysicalNetwork(unsigned netId, const char* interface,
+ Permission permission);
+ [[nodiscard]] static int removeInterfaceFromPhysicalNetwork(unsigned netId,
+ const char* interface,
+ Permission permission);
- 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 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 modifyPhysicalNetworkPermission(unsigned netId, const char* interface,
- Permission oldPermission,
- Permission newPermission) WARN_UNUSED_RESULT;
+ [[nodiscard]] static int modifyPhysicalNetworkPermission(unsigned netId, const char* interface,
+ Permission oldPermission,
+ Permission newPermission);
- 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 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 addUsersToRejectNonSecureNetworkRule(const UidRanges& uidRanges)
- WARN_UNUSED_RESULT;
- static int removeUsersFromRejectNonSecureNetworkRule(const UidRanges& uidRanges)
- WARN_UNUSED_RESULT;
+ [[nodiscard]] static int addUsersToRejectNonSecureNetworkRule(const UidRanges& uidRanges);
+ [[nodiscard]] static int removeUsersFromRejectNonSecureNetworkRule(const UidRanges& uidRanges);
- static int addInterfaceToDefaultNetwork(const char* interface,
- Permission permission) WARN_UNUSED_RESULT;
- static int removeInterfaceFromDefaultNetwork(const char* interface,
- Permission permission) WARN_UNUSED_RESULT;
+ [[nodiscard]] static int addInterfaceToDefaultNetwork(const char* interface,
+ Permission permission);
+ [[nodiscard]] static int removeInterfaceFromDefaultNetwork(const char* interface,
+ Permission permission);
// |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.
- 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 addRoute(const char* interface, const char* destination,
+ const char* nexthop, TableType tableType, int mtu);
+ [[nodiscard]] static int removeRoute(const char* interface, const char* destination,
+ const char* nexthop, TableType tableType);
+ [[nodiscard]] static int updateRoute(const char* interface, const char* destination,
+ const char* nexthop, TableType tableType, int mtu);
- 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 enableTethering(const char* inputInterface,
+ const char* outputInterface);
+ [[nodiscard]] static int disableTethering(const char* inputInterface,
+ const char* outputInterface);
- 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;
+ [[nodiscard]] static int addVirtualNetworkFallthrough(unsigned vpnNetId,
+ const char* physicalInterface,
+ Permission permission);
+ [[nodiscard]] static int removeVirtualNetworkFallthrough(unsigned vpnNetId,
+ const char* physicalInterface,
+ Permission permission);
// For testing.
static int (*iptablesRestoreCommandFunction)(IptablesTarget, const std::string&,
@@ -118,16 +119,17 @@
static std::map<std::string, uint32_t> sInterfaceToTable GUARDED_BY(sInterfaceToTableLock);
static int configureDummyNetwork();
- static int flushRoutes(const char* interface) EXCLUDES(sInterfaceToTableLock);
- static int flushRoutes(uint32_t table);
- static uint32_t getRouteTableForInterfaceLocked(const char *interface)
+ [[nodiscard]] static int flushRoutes(const char* interface) EXCLUDES(sInterfaceToTableLock);
+ [[nodiscard]] 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);
static int modifyPhysicalNetwork(unsigned netId, const char* interface, Permission permission,
bool add);
- static int modifyRoute(uint16_t action, const char* interface, const char* destination,
- const char* nexthop, TableType tableType);
+ static int modifyRoute(uint16_t action, uint16_t flags, const char* interface,
+ const char* destination, const char* nexthop, TableType tableType,
+ int mtu);
static int modifyTetheredNetwork(uint16_t action, const char* inputInterface,
const char* outputInterface);
static int modifyVpnFallthroughRule(uint16_t action, unsigned vpnNetId,
@@ -141,14 +143,11 @@
// 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.
-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;
+[[nodiscard]] int modifyIpRoute(uint16_t action, uint16_t flags, uint32_t table,
+ const char* interface, const char* destination, const char* nexthop,
+ uint32_t mtu);
uint32_t getRulePriority(const nlmsghdr *nlh);
-WARN_UNUSED_RESULT int modifyIncomingPacketMark(unsigned netId, const char* interface,
- Permission permission, bool add);
+[[nodiscard]] int modifyIncomingPacketMark(unsigned netId, const char* interface,
+ Permission permission, bool add);
-} // namespace net
-} // namespace android
-
-#endif // NETD_SERVER_ROUTE_CONTROLLER_H
+} // namespace android::net
diff --git a/server/RouteControllerTest.cpp b/server/RouteControllerTest.cpp
index 20b3618..fed15a3 100644
--- a/server/RouteControllerTest.cpp
+++ b/server/RouteControllerTest.cpp
@@ -79,18 +79,24 @@
static_assert(table2 < RouteController::ROUTE_TABLE_OFFSET_FROM_INDEX,
"Test table2 number too large");
- EXPECT_EQ(0, modifyIpRoute(RTM_NEWROUTE, table1, "lo", "192.0.2.2/32", nullptr));
- EXPECT_EQ(0, modifyIpRoute(RTM_NEWROUTE, table1, "lo", "192.0.2.3/32", nullptr));
- EXPECT_EQ(0, modifyIpRoute(RTM_NEWROUTE, table2, "lo", "192.0.2.4/32", nullptr));
+ EXPECT_EQ(0, modifyIpRoute(RTM_NEWROUTE, NETLINK_ROUTE_CREATE_FLAGS, table1, "lo",
+ "192.0.2.2/32", nullptr, 0 /* mtu */));
+ EXPECT_EQ(0, modifyIpRoute(RTM_NEWROUTE, NETLINK_ROUTE_CREATE_FLAGS, table1, "lo",
+ "192.0.2.3/32", nullptr, 0 /* mtu */));
+ EXPECT_EQ(0, modifyIpRoute(RTM_NEWROUTE, NETLINK_ROUTE_CREATE_FLAGS, table2, "lo",
+ "192.0.2.4/32", nullptr, 0 /* mtu */));
EXPECT_EQ(0, flushRoutes(table1));
EXPECT_EQ(-ESRCH,
- modifyIpRoute(RTM_DELROUTE, table1, "lo", "192.0.2.2/32", nullptr));
+ modifyIpRoute(RTM_DELROUTE, NETLINK_ROUTE_CREATE_FLAGS, table1, "lo", "192.0.2.2/32",
+ nullptr, 0 /* mtu */));
EXPECT_EQ(-ESRCH,
- modifyIpRoute(RTM_DELROUTE, table1, "lo", "192.0.2.3/32", nullptr));
+ modifyIpRoute(RTM_DELROUTE, NETLINK_ROUTE_CREATE_FLAGS, table1, "lo", "192.0.2.3/32",
+ nullptr, 0 /* mtu */));
EXPECT_EQ(0,
- modifyIpRoute(RTM_DELROUTE, table2, "lo", "192.0.2.4/32", nullptr));
+ modifyIpRoute(RTM_DELROUTE, NETLINK_ROUTE_CREATE_FLAGS, table2, "lo", "192.0.2.4/32",
+ nullptr, 0 /* mtu */));
}
TEST_F(RouteControllerTest, TestModifyIncomingPacketMark) {
diff --git a/server/SockDiag.cpp b/server/SockDiag.cpp
index 33c523a..44bda3b 100644
--- a/server/SockDiag.cpp
+++ b/server/SockDiag.cpp
@@ -14,27 +14,29 @@
* 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 <linux/netlink.h>
-#include <linux/sock_diag.h>
-#include <linux/inet_diag.h>
-
-#define LOG_TAG "Netd"
+#include <cinttypes>
#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
@@ -44,6 +46,7 @@
namespace android {
+using netdutils::ScopedAddrinfo;
using netdutils::Stopwatch;
namespace net {
@@ -136,13 +139,11 @@
addrinfo hints = { .ai_flags = AI_NUMERICHOST };
addrinfo *res;
in6_addr mapped = { .s6_addr32 = { 0, 0, htonl(0xffff), 0 } };
- int ret;
// TODO: refactor the netlink parsing code out of system/core, bring it into netd, and stop
// doing string conversions when they're not necessary.
- if ((ret = getaddrinfo(addrstr, nullptr, &hints, &res)) != 0) {
- return -EINVAL;
- }
+ int ret = getaddrinfo(addrstr, nullptr, &hints, &res);
+ if (ret != 0) return -EINVAL;
// So we don't have to call freeaddrinfo on every failure path.
ScopedAddrinfo resP(res);
@@ -323,7 +324,8 @@
}
if (mSocketsDestroyed > 0) {
- ALOGI("Destroyed %d sockets on %s in %.1f ms", mSocketsDestroyed, addrstr, s.timeTaken());
+ ALOGI("Destroyed %d sockets on %s in %" PRId64 "us", mSocketsDestroyed, addrstr,
+ s.timeTakenUs());
}
return mSocketsDestroyed;
@@ -397,7 +399,7 @@
}
if (mSocketsDestroyed > 0) {
- ALOGI("Destroyed %d sockets for UID in %.1f ms", mSocketsDestroyed, s.timeTaken());
+ ALOGI("Destroyed %d sockets for UID in %" PRId64 "us", mSocketsDestroyed, s.timeTakenUs());
}
return 0;
@@ -424,9 +426,9 @@
}
if (mSocketsDestroyed > 0) {
- ALOGI("Destroyed %d sockets for %s skip={%s} in %.1f ms",
- mSocketsDestroyed, uidRanges.toString().c_str(),
- android::base::Join(skipUids, " ").c_str(), s.timeTaken());
+ ALOGI("Destroyed %d sockets for %s skip={%s} in %" PRId64 "us", mSocketsDestroyed,
+ uidRanges.toString().c_str(), android::base::Join(skipUids, " ").c_str(),
+ s.timeTakenUs());
}
return 0;
@@ -495,8 +497,8 @@
};
struct nlattr nla = {
- .nla_type = INET_DIAG_REQ_BYTECODE,
- .nla_len = sizeof(struct nlattr) + bytecodelen,
+ .nla_len = sizeof(struct nlattr) + bytecodelen,
+ .nla_type = INET_DIAG_REQ_BYTECODE,
};
iovec iov[] = {
@@ -517,8 +519,8 @@
}
if (mSocketsDestroyed > 0) {
- ALOGI("Destroyed %d sockets for netId %d permission=%d in %.1f ms",
- mSocketsDestroyed, netId, permission, s.timeTaken());
+ ALOGI("Destroyed %d sockets for netId %d permission=%d in %" PRId64 "us", mSocketsDestroyed,
+ netId, permission, s.timeTakenUs());
}
return 0;
diff --git a/server/SockDiag.h b/server/SockDiag.h
index af96409..745c09e 100644
--- a/server/SockDiag.h
+++ b/server/SockDiag.h
@@ -34,7 +34,6 @@
struct inet_diag_msg;
struct tcp_info;
-class SockDiagTest;
namespace android {
namespace net {
diff --git a/server/SockDiagTest.cpp b/server/SockDiagTest.cpp
index 312f2c8..b79471a 100644
--- a/server/SockDiagTest.cpp
+++ b/server/SockDiagTest.cpp
@@ -199,14 +199,15 @@
inet_diag_msg makeDiagMessage(__u8 family, const sockaddr *src, const sockaddr *dst) {
inet_diag_msg msg = {
- .idiag_family = family,
- .idiag_state = TCP_ESTABLISHED,
- .idiag_uid = AID_APP + 123,
- .idiag_inode = 123456789,
- .id = {
- .idiag_sport = 1234,
- .idiag_dport = 4321,
- }
+ .idiag_family = family,
+ .idiag_state = TCP_ESTABLISHED,
+ .id =
+ {
+ .idiag_sport = 1234,
+ .idiag_dport = 4321,
+ },
+ .idiag_uid = AID_APP + 123,
+ .idiag_inode = 123456789,
};
EXPECT_TRUE(fillDiagAddr(msg.id.idiag_src, src));
EXPECT_TRUE(fillDiagAddr(msg.id.idiag_dst, dst));
diff --git a/server/TcpSocketMonitor.cpp b/server/TcpSocketMonitor.cpp
index f4b505d..c18fe92 100644
--- a/server/TcpSocketMonitor.cpp
+++ b/server/TcpSocketMonitor.cpp
@@ -70,9 +70,10 @@
// Helper macro for reading fields into struct tcp_info and handling different struct tcp_info
// versions in the kernel.
-#define TCPINFO_GET(ptr, fld, len, zero) \
- (((ptr) != nullptr && (offsetof(struct tcp_info, fld) + sizeof((ptr)->fld)) < len) ? \
- (ptr)->fld : zero)
+#define TCPINFO_GET(ptr, fld, len, zero) \
+ (((ptr) != nullptr && (offsetof(struct tcp_info, fld) + sizeof((ptr)->fld)) < (len)) \
+ ? (ptr)->fld \
+ : (zero))
static void tcpInfoPrint(DumpWriter &dw, Fwmark mark, const struct inet_diag_msg *sockinfo,
const struct tcp_info *tcpinfo, uint32_t tcpinfoLen) {
diff --git a/server/TetherController.cpp b/server/TetherController.cpp
index 0c5b6bf..2445fb7 100644
--- a/server/TetherController.cpp
+++ b/server/TetherController.cpp
@@ -36,11 +36,14 @@
#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>
#include <cutils/properties.h>
#include <log/log.h>
+#include <net/if.h>
+#include <netdutils/DumpWriter.h>
#include <netdutils/StatusOr.h>
#include "Controllers.h"
@@ -48,16 +51,25 @@
#include "InterfaceController.h"
#include "NetdConstants.h"
#include "NetworkController.h"
+#include "OffloadUtils.h"
#include "Permission.h"
#include "TetherController.h"
+#include "android/net/TetherOffloadRuleParcel.h"
+
namespace android {
namespace net {
+using android::base::Error;
using android::base::Join;
using android::base::Pipe;
+using android::base::Result;
+using android::base::StringAppendF;
using android::base::StringPrintf;
using android::base::unique_fd;
+using android::net::TetherOffloadRuleParcel;
+using android::netdutils::DumpWriter;
+using android::netdutils::ScopedIndent;
using android::netdutils::statusFromErrno;
using android::netdutils::StatusOr;
@@ -72,6 +84,10 @@
// Chosen to match AID_DNS_TETHER, as made "friendly" by fs_config_generator.py.
constexpr const char kDnsmasqUsername[] = "dns_tether";
+// A value used by interface quota indicates there is no limit.
+// Sync from frameworks/base/core/java/android/net/netstats/provider/NetworkStatsProvider.java
+constexpr int64_t QUOTA_UNLIMITED = -1;
+
bool writeToFile(const char* filename, const char* value) {
int fd = open(filename, O_WRONLY | O_CLOEXEC);
if (fd < 0) {
@@ -116,22 +132,6 @@
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;
@@ -169,6 +169,7 @@
} else {
setIpFwdEnabled();
}
+ maybeInitMaps();
}
bool TetherController::setIpFwdEnabled() {
@@ -201,12 +202,40 @@
return setIpFwdEnabled();
}
+void TetherController::maybeInitMaps() {
+ if (!bpf::isBpfSupported()) return;
+
+ // Open BPF maps, ignoring errors because the device might not support BPF offload.
+ int fd = getTetherIngressMapFd();
+ if (fd >= 0) {
+ mBpfIngressMap.reset(fd);
+ mBpfIngressMap.clear();
+ }
+ fd = getTetherStatsMapFd();
+ if (fd >= 0) {
+ mBpfStatsMap.reset(fd);
+ mBpfStatsMap.clear();
+ }
+ fd = getTetherLimitMapFd();
+ if (fd >= 0) {
+ mBpfLimitMap.reset(fd);
+ mBpfLimitMap.clear();
+ }
+}
+
const std::set<std::string>& TetherController::getIpfwdRequesterList() const {
return mForwardingRequests;
}
-int TetherController::startTethering(int num_addrs, char **dhcp_ranges) {
- if (mDaemonPid != 0) {
+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) {
ALOGE("Tethering already started");
errno = EBUSY;
return -errno;
@@ -245,8 +274,11 @@
kDnsmasqUsername,
};
- // DHCP server will be disabled if num_addrs == 0 and no --dhcp-range is
- // passed.
+ if (!usingLegacyDnsProxy) {
+ argVector.push_back("--port=0");
+ }
+
+ // 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]));
@@ -267,23 +299,33 @@
// dup2 creates fd without CLOEXEC, dnsmasq will receive commands through the
// duplicated fd.
posix_spawn_file_actions_t fa;
- int res = setPosixSpawnFileActionsAddDup2(&fa, pipeRead.get(), STDIN_FILENO);
+ int res = posix_spawn_file_actions_init(&fa);
if (res) {
- ALOGE("posix_spawn set fa failed (%s)", strerror(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));
return -res;
}
posix_spawnattr_t attr;
- res = setPosixSpawnAttrFlags(&attr, POSIX_SPAWN_USEVFORK);
+ res = posix_spawnattr_init(&attr);
if (res) {
- ALOGE("posix_spawn set attr flag failed (%s)", strerror(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));
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;
@@ -291,6 +333,7 @@
mDaemonPid = pid;
mDaemonFd = pipeWrite.release();
configureForTethering(true);
+ mIsTetheringStarted = true;
applyDnsInterfaces();
ALOGD("Tethering services running");
@@ -306,7 +349,8 @@
return addrsCstrVec;
}
-int TetherController::startTethering(const std::vector<std::string>& dhcpRanges) {
+int TetherController::startTethering(bool usingLegacyDnsProxy,
+ const std::vector<std::string>& dhcpRanges) {
struct in_addr v4_addr;
for (const auto& dhcpRange : dhcpRanges) {
if (!inet_aton(dhcpRange.c_str(), &v4_addr)) {
@@ -314,17 +358,23 @@
}
}
auto dhcp_ranges = toCstrVec(dhcpRanges);
- return startTethering(dhcp_ranges.size(), dhcp_ranges.data());
+ return startTethering(usingLegacyDnsProxy, dhcp_ranges.size(), dhcp_ranges.data());
}
int TetherController::stopTethering() {
configureForTethering(false);
- if (mDaemonPid == 0) {
+ if (!mIsTetheringStarted) {
ALOGE("Tethering already stopped");
return 0;
}
+ mIsTetheringStarted = false;
+ // dnsmasq is not started
+ if (mDaemonPid == 0) {
+ return 0;
+ }
+
ALOGD("Stopping tethering services");
kill(mDaemonPid, SIGTERM);
@@ -338,7 +388,7 @@
}
bool TetherController::isTetheringStarted() {
- return (mDaemonPid == 0 ? false : true);
+ return mIsTetheringStarted;
}
// dnsmasq can't parse commands larger than this due to the fixed-size buffer
@@ -556,7 +606,8 @@
}
// add this if we are the first enabled nat for this upstream
- if (!isAnyForwardingEnabledOnUpstream(extIface)) {
+ bool firstDownstreamForThisUpstream = !isAnyForwardingEnabledOnUpstream(extIface);
+ if (firstDownstreamForThisUpstream) {
std::vector<std::string> v4Cmds = {
"*nat",
StringPrintf("-A %s -o %s -j MASQUERADE", LOCAL_NAT_POSTROUTING, extIface),
@@ -582,6 +633,7 @@
return -ENODEV;
}
+ if (firstDownstreamForThisUpstream) maybeStartBpf(extIface);
return 0;
}
@@ -765,12 +817,82 @@
}
setForwardRules(false, intIface, extIface);
- if (!isAnyForwardingPairEnabled()) {
- setDefaults();
- }
+ if (!isAnyForwardingEnabledOnUpstream(extIface)) maybeStopBpf(extIface);
+ if (!isAnyForwardingPairEnabled()) setDefaults();
return 0;
}
+namespace {
+Result<void> validateOffloadRule(const TetherOffloadRuleParcel& rule) {
+ struct ethhdr hdr;
+
+ if (rule.inputInterfaceIndex <= 0) {
+ return Error(ENODEV) << "Invalid input interface " << rule.inputInterfaceIndex;
+ }
+ if (rule.outputInterfaceIndex <= 0) {
+ return Error(ENODEV) << "Invalid output interface " << rule.inputInterfaceIndex;
+ }
+ if (rule.prefixLength != 128) {
+ return Error(EINVAL) << "Prefix length must be 128, not " << rule.prefixLength;
+ }
+ if (rule.destination.size() != sizeof(in6_addr)) {
+ return Error(EAFNOSUPPORT) << "Invalid IP address length " << rule.destination.size();
+ }
+ if (rule.srcL2Address.size() != sizeof(hdr.h_source)) {
+ return Error(ENXIO) << "Invalid L2 src address length " << rule.srcL2Address.size();
+ }
+ if (rule.dstL2Address.size() != sizeof(hdr.h_dest)) {
+ return Error(ENXIO) << "Invalid L2 dst address length " << rule.dstL2Address.size();
+ }
+ if (rule.pmtu < IPV6_MIN_MTU || rule.pmtu > 0xFFFF) {
+ return Error(EINVAL) << "Invalid IPv6 path mtu " << rule.pmtu;
+ }
+ return Result<void>();
+}
+} // namespace
+
+Result<void> TetherController::addOffloadRule(const TetherOffloadRuleParcel& rule) {
+ Result<void> res = validateOffloadRule(rule);
+ if (!res.ok()) return res;
+
+ ethhdr hdr = {
+ .h_proto = htons(ETH_P_IPV6),
+ };
+ memcpy(&hdr.h_dest, rule.dstL2Address.data(), sizeof(hdr.h_dest));
+ memcpy(&hdr.h_source, rule.srcL2Address.data(), sizeof(hdr.h_source));
+
+ // Only downstream supported for now.
+ TetherIngressKey key = {
+ .iif = static_cast<uint32_t>(rule.inputInterfaceIndex),
+ .neigh6 = *(const in6_addr*)rule.destination.data(),
+ };
+
+ TetherIngressValue value = {
+ .oif = static_cast<uint32_t>(rule.outputInterfaceIndex),
+ .macHeader = hdr,
+ .pmtu = static_cast<uint16_t>(rule.pmtu),
+ };
+
+ return mBpfIngressMap.writeValue(key, value, BPF_ANY);
+}
+
+Result<void> TetherController::removeOffloadRule(const TetherOffloadRuleParcel& rule) {
+ Result<void> res = validateOffloadRule(rule);
+ if (!res.ok()) return res;
+
+ TetherIngressKey key = {
+ .iif = static_cast<uint32_t>(rule.inputInterfaceIndex),
+ .neigh6 = *(const in6_addr*)rule.destination.data(),
+ };
+
+ Result<void> ret = mBpfIngressMap.deleteValue(key);
+
+ // Silently return success if the rule did not exist.
+ if (!ret.ok() && ret.error().code() == ENOENT) return {};
+
+ return ret;
+}
+
void TetherController::addStats(TetherStatsList& statsList, const TetherStats& stats) {
for (TetherStats& existing : statsList) {
if (existing.addStatsIfMatch(stats)) {
@@ -906,5 +1028,294 @@
return statsList;
}
+StatusOr<TetherController::TetherOffloadStatsList> TetherController::getTetherOffloadStats() {
+ TetherOffloadStatsList statsList;
+
+ const auto processTetherStats = [&statsList](const uint32_t& key, const TetherStatsValue& value,
+ const BpfMap<uint32_t, TetherStatsValue>&) {
+ statsList.push_back({.ifIndex = static_cast<int>(key),
+ .rxBytes = static_cast<int64_t>(value.rxBytes),
+ .rxPackets = static_cast<int64_t>(value.rxPackets),
+ .txBytes = static_cast<int64_t>(value.txBytes),
+ .txPackets = static_cast<int64_t>(value.txPackets)});
+ return Result<void>();
+ };
+
+ auto ret = mBpfStatsMap.iterateWithValue(processTetherStats);
+ if (!ret.ok()) {
+ // Ignore error to return the remaining tether stats result.
+ ALOGE("Error processing tether stats from BPF maps: %s", ret.error().message().c_str());
+ }
+
+ return statsList;
+}
+
+// Use UINT64_MAX (~0uLL) for unlimited.
+Result<void> TetherController::setBpfLimit(uint32_t ifIndex, uint64_t limit) {
+ // The common case is an update, where the stats already exist,
+ // hence we read first, even though writing with BPF_NOEXIST
+ // first would make the code simpler.
+ uint64_t rxBytes, txBytes;
+ auto statsEntry = mBpfStatsMap.readValue(ifIndex);
+
+ if (statsEntry.ok()) {
+ // Ok, there was a stats entry.
+ rxBytes = statsEntry.value().rxBytes;
+ txBytes = statsEntry.value().txBytes;
+ } else if (statsEntry.error().code() == ENOENT) {
+ // No stats entry - create one with zeroes.
+ TetherStatsValue stats = {};
+ // This function is the *only* thing that can create entries.
+ auto ret = mBpfStatsMap.writeValue(ifIndex, stats, BPF_NOEXIST);
+ if (!ret.ok()) {
+ ALOGE("mBpfStatsMap.writeValue failure: %s", strerror(ret.error().code()));
+ return ret;
+ }
+ rxBytes = 0;
+ txBytes = 0;
+ } else {
+ // Other error while trying to get stats entry.
+ return statsEntry.error();
+ }
+
+ // rxBytes + txBytes won't overflow even at 5gbps for ~936 years.
+ uint64_t newLimit = rxBytes + txBytes + limit;
+
+ // if adding limit (e.g., if limit is UINT64_MAX) caused overflow: clamp to 'infinity'
+ if (newLimit < rxBytes + txBytes) newLimit = ~0uLL;
+
+ auto ret = mBpfLimitMap.writeValue(ifIndex, newLimit, BPF_ANY);
+ if (!ret.ok()) {
+ ALOGE("mBpfLimitMap.writeValue failure: %s", strerror(ret.error().code()));
+ return ret;
+ }
+
+ return {};
+}
+
+void TetherController::maybeStartBpf(const char* extIface) {
+ if (!bpf::isBpfSupported()) return;
+
+ // TODO: perhaps ignore IPv4-only interface because IPv4 traffic downstream is not supported.
+ int ifIndex = if_nametoindex(extIface);
+ if (!ifIndex) {
+ ALOGE("Fail to get index for interface %s", extIface);
+ return;
+ }
+
+ auto isEthernet = android::net::isEthernet(extIface);
+ if (!isEthernet.ok()) {
+ ALOGE("isEthernet(%s[%d]) failure: %s", extIface, ifIndex,
+ isEthernet.error().message().c_str());
+ return;
+ }
+
+ int rv = getTetherIngressProgFd(isEthernet.value());
+ if (rv < 0) {
+ ALOGE("getTetherIngressProgFd(%d) failure: %s", isEthernet.value(), strerror(-rv));
+ return;
+ }
+ unique_fd tetherProgFd(rv);
+
+ rv = tcFilterAddDevIngressTether(ifIndex, tetherProgFd, isEthernet.value());
+ if (rv) {
+ ALOGE("tcFilterAddDevIngressTether(%d[%s], %d) failure: %s", ifIndex, extIface,
+ isEthernet.value(), strerror(-rv));
+ return;
+ }
+}
+
+void TetherController::maybeStopBpf(const char* extIface) {
+ if (!bpf::isBpfSupported()) return;
+
+ // TODO: perhaps ignore IPv4-only interface because IPv4 traffic downstream is not supported.
+ int ifIndex = if_nametoindex(extIface);
+ if (!ifIndex) {
+ ALOGE("Fail to get index for interface %s", extIface);
+ return;
+ }
+
+ int rv = tcFilterDelDevIngressTether(ifIndex);
+ if (rv < 0) {
+ ALOGE("tcFilterDelDevIngressTether(%d[%s]) failure: %s", ifIndex, extIface, strerror(-rv));
+ }
+}
+
+int TetherController::setTetherOffloadInterfaceQuota(int ifIndex, int64_t maxBytes) {
+ if (!mBpfStatsMap.isValid() || !mBpfLimitMap.isValid()) return -ENOTSUP;
+
+ if (ifIndex <= 0) return -ENODEV;
+
+ if (maxBytes < QUOTA_UNLIMITED) {
+ ALOGE("Invalid bytes value. Must be -1 (unlimited) or 0..max_int64.");
+ return -ERANGE;
+ }
+
+ // Note that a value of unlimited quota (-1) indicates simply max_uint64.
+ const auto res = setBpfLimit(static_cast<uint32_t>(ifIndex), static_cast<uint64_t>(maxBytes));
+ if (!res.ok()) {
+ ALOGE("Fail to set quota %" PRId64 " for interface index %d: %s", maxBytes, ifIndex,
+ strerror(res.error().code()));
+ return -res.error().code();
+ }
+
+ return 0;
+}
+
+Result<TetherController::TetherOffloadStats> TetherController::getAndClearTetherOffloadStats(
+ int ifIndex) {
+ if (!mBpfStatsMap.isValid() || !mBpfLimitMap.isValid()) return Error(ENOTSUP);
+
+ if (ifIndex <= 0) {
+ return Error(ENODEV) << "Invalid interface " << ifIndex;
+ }
+
+ // getAndClearTetherOffloadStats is called after all offload rules have already been deleted
+ // for the given upstream interface. Before starting to do cleanup stuff in this function, use
+ // synchronizeKernelRCU to make sure that all the current running eBPF programs are finished
+ // on all CPUs, especially the unfinished packet processing. After synchronizeKernelRCU
+ // returned, we can safely read or delete on the stats map or the limit map.
+ if (int res = bpf::synchronizeKernelRCU()) {
+ // Error log but don't return error. Do as much cleanup as possible.
+ ALOGE("synchronize_rcu() failed: %s", strerror(-res));
+ }
+
+ const auto stats = mBpfStatsMap.readValue(ifIndex);
+ if (!stats.ok()) {
+ return Error(stats.error().code()) << "Fail to get stats for interface index " << ifIndex;
+ }
+
+ auto res = mBpfStatsMap.deleteValue(ifIndex);
+ if (!res.ok()) {
+ return Error(res.error().code()) << "Fail to delete stats for interface index " << ifIndex;
+ }
+
+ res = mBpfLimitMap.deleteValue(ifIndex);
+ if (!res.ok()) {
+ return Error(res.error().code()) << "Fail to delete limit for interface index " << ifIndex;
+ }
+
+ return TetherOffloadStats{.ifIndex = static_cast<int>(ifIndex),
+ .rxBytes = static_cast<int64_t>(stats.value().rxBytes),
+ .rxPackets = static_cast<int64_t>(stats.value().rxPackets),
+ .txBytes = static_cast<int64_t>(stats.value().txBytes),
+ .txPackets = static_cast<int64_t>(stats.value().txPackets)};
+}
+
+void TetherController::dumpIfaces(DumpWriter& dw) {
+ dw.println("Interface pairs:");
+
+ ScopedIndent ifaceIndent(dw);
+ for (const auto& it : mFwdIfaces) {
+ dw.println("%s -> %s %s", it.first.c_str(), it.second.iface.c_str(),
+ (it.second.active ? "ACTIVE" : "DISABLED"));
+ }
+}
+
+namespace {
+
+std::string l2ToString(const uint8_t* addr, size_t len) {
+ std::string str;
+
+ if (len == 0) return str;
+
+ StringAppendF(&str, "%02x", addr[0]);
+ for (size_t i = 1; i < len; i++) {
+ StringAppendF(&str, ":%02x", addr[i]);
+ }
+
+ return str;
+}
+
+} // namespace
+
+void TetherController::dumpBpf(DumpWriter& dw) {
+ if (!mBpfIngressMap.isValid() || !mBpfStatsMap.isValid() || !mBpfLimitMap.isValid()) {
+ dw.println("BPF not supported");
+ return;
+ }
+
+ dw.println("BPF ingress map: iif(iface) v6addr -> oif(iface) srcmac dstmac ethertype [pmtu]");
+ const auto printIngressMap = [&dw](const TetherIngressKey& key, const TetherIngressValue& value,
+ const BpfMap<TetherIngressKey, TetherIngressValue>&) {
+ char addr[INET6_ADDRSTRLEN];
+ std::string src = l2ToString(value.macHeader.h_source, sizeof(value.macHeader.h_source));
+ std::string dst = l2ToString(value.macHeader.h_dest, sizeof(value.macHeader.h_dest));
+ inet_ntop(AF_INET6, &key.neigh6, addr, sizeof(addr));
+
+ char iifStr[IFNAMSIZ] = "?";
+ char oifStr[IFNAMSIZ] = "?";
+ if_indextoname(key.iif, iifStr);
+ if_indextoname(value.oif, oifStr);
+ dw.println("%u(%s) %s -> %u(%s) %s %s %04x [%u]", key.iif, iifStr, addr, value.oif, oifStr,
+ src.c_str(), dst.c_str(), ntohs(value.macHeader.h_proto), value.pmtu);
+
+ return Result<void>();
+ };
+
+ dw.incIndent();
+ auto ret = mBpfIngressMap.iterateWithValue(printIngressMap);
+ if (!ret.ok()) {
+ dw.println("Error printing BPF ingress map: %s", ret.error().message().c_str());
+ }
+ dw.decIndent();
+
+ dw.println("BPF stats (downlink): iif(iface) -> packets bytes errors");
+ const auto printStatsMap = [&dw](const uint32_t& key, const TetherStatsValue& value,
+ const BpfMap<uint32_t, TetherStatsValue>&) {
+ char iifStr[IFNAMSIZ] = "?";
+ if_indextoname(key, iifStr);
+ dw.println("%u(%s) -> %" PRIu64 " %" PRIu64 " %" PRIu64, key, iifStr, value.rxPackets,
+ value.rxBytes, value.rxErrors);
+
+ return Result<void>();
+ };
+
+ dw.incIndent();
+ ret = mBpfStatsMap.iterateWithValue(printStatsMap);
+ if (!ret.ok()) {
+ dw.println("Error printing BPF stats map: %s", ret.error().message().c_str());
+ }
+ dw.decIndent();
+
+ dw.println("BPF limit: iif(iface) -> bytes");
+ const auto printLimitMap = [&dw](const uint32_t& key, const uint64_t& value,
+ const BpfMap<uint32_t, uint64_t>&) {
+ char iifStr[IFNAMSIZ] = "?";
+ if_indextoname(key, iifStr);
+ dw.println("%u(%s) -> %" PRIu64, key, iifStr, value);
+
+ return Result<void>();
+ };
+
+ dw.incIndent();
+ ret = mBpfLimitMap.iterateWithValue(printLimitMap);
+ if (!ret.ok()) {
+ dw.println("Error printing BPF limit map: %s", ret.error().message().c_str());
+ }
+ dw.decIndent();
+}
+
+void TetherController::dump(DumpWriter& dw) {
+ std::lock_guard guard(lock);
+
+ ScopedIndent tetherControllerIndent(dw);
+ dw.println("TetherController");
+ dw.incIndent();
+
+ dw.println("Forwarding requests: " + Join(mForwardingRequests, ' '));
+ if (mDnsNetId != 0) {
+ dw.println(StringPrintf("DNS: netId %d servers [%s]", mDnsNetId,
+ Join(mDnsForwarders, ", ").c_str()));
+ }
+ if (mDaemonPid != 0) {
+ dw.println("dnsmasq PID: %d", mDaemonPid);
+ }
+
+ dumpIfaces(dw);
+ dw.println("");
+ dumpBpf(dw);
+}
+
} // namespace net
} // namespace android
diff --git a/server/TetherController.h b/server/TetherController.h
index 0a04874..bcd2ee6 100644
--- a/server/TetherController.h
+++ b/server/TetherController.h
@@ -21,11 +21,16 @@
#include <set>
#include <string>
+#include <netdutils/DumpWriter.h>
#include <netdutils/StatusOr.h>
#include <sysutils/SocketClient.h>
#include "NetdConstants.h"
+#include "android-base/result.h"
+#include "bpf/BpfMap.h"
+#include "netdbpf/bpf_shared.h"
+#include "android/net/TetherOffloadRuleParcel.h"
namespace android {
namespace net {
@@ -43,6 +48,8 @@
// 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;
@@ -65,6 +72,11 @@
int sendAllState(int daemonFd) const;
} mDnsmasqState{};
+ // BPF maps, initialized by maybeInitMaps.
+ bpf::BpfMap<TetherIngressKey, TetherIngressValue> mBpfIngressMap;
+ bpf::BpfMap<uint32_t, TetherStatsValue> mBpfStatsMap;
+ bpf::BpfMap<uint32_t, uint64_t> mBpfLimitMap;
+
public:
TetherController();
~TetherController() = default;
@@ -73,8 +85,9 @@
bool disableForwarding(const char* requester);
const std::set<std::string>& getIpfwdRequesterList() const;
- int startTethering(int num_addrs, char **dhcp_ranges);
- int startTethering(const std::vector<std::string>& dhcpRanges);
+ //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 stopTethering();
bool isTetheringStarted();
@@ -92,6 +105,11 @@
int disableNat(const char* intIface, const char* extIface);
int setupIptablesHooks();
+ base::Result<void> addOffloadRule(const TetherOffloadRuleParcel& rule);
+ base::Result<void> removeOffloadRule(const TetherOffloadRuleParcel& rule);
+
+ int setTetherOffloadInterfaceQuota(int ifIndex, int64_t maxBytes);
+
class TetherStats {
public:
TetherStats() = default;
@@ -120,9 +138,20 @@
}
};
+ struct TetherOffloadStats {
+ int ifIndex;
+ int64_t rxBytes;
+ int64_t rxPackets;
+ int64_t txBytes;
+ int64_t txPackets;
+ };
+
typedef std::vector<TetherStats> TetherStatsList;
+ typedef std::vector<TetherOffloadStats> TetherOffloadStatsList;
netdutils::StatusOr<TetherStatsList> getTetherStats();
+ netdutils::StatusOr<TetherOffloadStatsList> getTetherOffloadStats();
+ base::Result<TetherOffloadStats> getAndClearTetherOffloadStats(int ifIndex);
/*
* extraProcessingInfo: contains raw parsed data, and error info.
@@ -142,6 +171,10 @@
std::mutex lock;
+ void dump(netdutils::DumpWriter& dw);
+ void dumpIfaces(netdutils::DumpWriter& dw);
+ void dumpBpf(netdutils::DumpWriter& dw);
+
private:
bool setIpFwdEnabled();
std::vector<char*> toCstrVec(const std::vector<std::string>& addrs);
@@ -162,6 +195,11 @@
int setForwardRules(bool set, const char *intIface, const char *extIface);
int setTetherCountingRules(bool add, const char *intIface, const char *extIface);
+ base::Result<void> setBpfLimit(uint32_t ifIndex, uint64_t limit);
+ void maybeInitMaps();
+ void maybeStartBpf(const char* extIface);
+ void maybeStopBpf(const char* extIface);
+
static void addStats(TetherStatsList& statsList, const TetherStats& stats);
// For testing.
diff --git a/server/TetherControllerTest.cpp b/server/TetherControllerTest.cpp
index 309a6d5..db9892f 100644
--- a/server/TetherControllerTest.cpp
+++ b/server/TetherControllerTest.cpp
@@ -20,28 +20,46 @@
#include <vector>
#include <fcntl.h>
-#include <unistd.h>
-#include <sys/types.h>
+#include <inttypes.h>
#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
#include <gtest/gtest.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
+#include <gmock/gmock.h>
#include <netdutils/StatusOr.h>
-#include "TetherController.h"
#include "IptablesBaseTest.h"
+#include "OffloadUtils.h"
+#include "TetherController.h"
using android::base::Join;
using android::base::StringPrintf;
+using android::bpf::BpfMap;
using android::netdutils::StatusOr;
+using ::testing::Contains;
using TetherStats = android::net::TetherController::TetherStats;
using TetherStatsList = android::net::TetherController::TetherStatsList;
+using TetherOffloadStats = android::net::TetherController::TetherOffloadStats;
+using TetherOffloadStatsList = android::net::TetherController::TetherOffloadStatsList;
namespace android {
namespace net {
+constexpr int TEST_MAP_SIZE = 10;
+
+// Comparison for TetherOffloadStats. Need to override operator== because class TetherOffloadStats
+// doesn't have one.
+// TODO: once C++20 is used, use default operator== in TetherOffloadStats and remove the overriding
+// here.
+bool operator==(const TetherOffloadStats& lhs, const TetherOffloadStats& rhs) {
+ return lhs.ifIndex == rhs.ifIndex && lhs.rxBytes == rhs.rxBytes && lhs.txBytes == rhs.txBytes &&
+ lhs.rxPackets == rhs.rxPackets && lhs.txPackets == rhs.txPackets;
+}
+
class TetherControllerTest : public IptablesBaseTest {
public:
TetherControllerTest() {
@@ -50,6 +68,38 @@
protected:
TetherController mTetherCtrl;
+ BpfMap<uint32_t, TetherStatsValue> mFakeTetherStatsMap{BPF_MAP_TYPE_HASH, TEST_MAP_SIZE};
+ BpfMap<uint32_t, uint64_t> mFakeTetherLimitMap{BPF_MAP_TYPE_HASH, TEST_MAP_SIZE};
+
+ void SetUp() {
+ SKIP_IF_BPF_NOT_SUPPORTED;
+
+ ASSERT_TRUE(mFakeTetherStatsMap.isValid());
+ ASSERT_TRUE(mFakeTetherLimitMap.isValid());
+
+ mTetherCtrl.mBpfStatsMap = mFakeTetherStatsMap;
+ ASSERT_TRUE(mTetherCtrl.mBpfStatsMap.isValid());
+ mTetherCtrl.mBpfLimitMap = mFakeTetherLimitMap;
+ ASSERT_TRUE(mTetherCtrl.mBpfLimitMap.isValid());
+ }
+
+ std::string toString(const TetherOffloadStatsList& statsList) {
+ std::string result;
+ for (const auto& stats : statsList) {
+ result += StringPrintf("%d, %" PRId64 ", %" PRId64 ", %" PRId64 ", %" PRId64 "\n",
+ stats.ifIndex, stats.rxBytes, stats.rxPackets, stats.txBytes,
+ stats.txPackets);
+ }
+ return result;
+ }
+
+ void updateMaps(uint32_t ifaceIndex, uint64_t rxBytes, uint64_t rxPackets, uint64_t txBytes,
+ uint64_t txPackets) {
+ // {rx, tx}Errors in |tetherStats| are set zero because getTetherStats doesn't use them.
+ const TetherStatsValue tetherStats = {rxPackets, rxBytes, 0 /*unused*/,
+ txPackets, txBytes, 0 /*unused*/};
+ ASSERT_RESULT_OK(mFakeTetherStatsMap.writeValue(ifaceIndex, tetherStats, BPF_ANY));
+ };
int setDefaults() {
return mTetherCtrl.setDefaults();
@@ -435,5 +485,64 @@
EXPECT_TRUE(std::equal(expectedError.rbegin(), expectedError.rend(), err.rbegin()));
}
+TEST_F(TetherControllerTest, TestTetherOffloadGetStats) {
+ SKIP_IF_BPF_NOT_SUPPORTED;
+
+ updateMaps(101, 100, 10, 200, 20);
+ updateMaps(102, 300, 30, 400, 40);
+ const TetherOffloadStats expected0{101, 100, 10, 200, 20};
+ const TetherOffloadStats expected1{102, 300, 30, 400, 40};
+
+ const StatusOr<TetherOffloadStatsList> result = mTetherCtrl.getTetherOffloadStats();
+ ASSERT_OK(result);
+ const TetherOffloadStatsList& actual = result.value();
+ ASSERT_EQ(2U, actual.size());
+ EXPECT_THAT(actual, Contains(expected0)) << toString(actual);
+ EXPECT_THAT(actual, Contains(expected1)) << toString(actual);
+ clearIptablesRestoreOutput();
+}
+
+TEST_F(TetherControllerTest, TestTetherOffloadSetQuota) {
+ SKIP_IF_BPF_NOT_SUPPORTED;
+
+ const uint32_t ifindex = 100;
+ const uint64_t minQuota = 0;
+ const uint64_t maxQuota = std::numeric_limits<int64_t>::max();
+ const uint64_t infinityQuota = std::numeric_limits<uint64_t>::max();
+
+ // Create a stats entry with zeroes in the first time set limit.
+ ASSERT_EQ(0, mTetherCtrl.setTetherOffloadInterfaceQuota(ifindex, minQuota));
+ const StatusOr<TetherOffloadStatsList> result = mTetherCtrl.getTetherOffloadStats();
+ ASSERT_OK(result);
+ const TetherOffloadStatsList& actual = result.value();
+ ASSERT_EQ(1U, actual.size());
+ EXPECT_THAT(actual, Contains(TetherOffloadStats{ifindex, 0, 0, 0, 0})) << toString(actual);
+
+ // Verify the quota with the boundary {min, max, infinity}.
+ const uint64_t rxBytes = 1000;
+ const uint64_t txBytes = 2000;
+ updateMaps(ifindex, rxBytes, 0 /*unused*/, txBytes, 0 /*unused*/);
+
+ for (const uint64_t quota : {minQuota, maxQuota, infinityQuota}) {
+ ASSERT_EQ(0, mTetherCtrl.setTetherOffloadInterfaceQuota(ifindex, quota));
+ base::Result<uint64_t> result = mFakeTetherLimitMap.readValue(ifindex);
+ ASSERT_RESULT_OK(result);
+
+ const uint64_t expectedQuota =
+ (quota == infinityQuota) ? infinityQuota : quota + rxBytes + txBytes;
+ EXPECT_EQ(expectedQuota, result.value());
+ }
+
+ // The valid range of interface index is 1..max_int64.
+ const uint32_t invalidIfindex = 0;
+ int ret = mTetherCtrl.setTetherOffloadInterfaceQuota(invalidIfindex /*bad*/, infinityQuota);
+ ASSERT_EQ(-ENODEV, ret);
+
+ // The valid range of quota is 0..max_int64 or -1 (unlimited).
+ const uint64_t invalidQuota = std::numeric_limits<int64_t>::min();
+ ret = mTetherCtrl.setTetherOffloadInterfaceQuota(ifindex, invalidQuota /*bad*/);
+ ASSERT_EQ(-ERANGE, ret);
+}
+
} // namespace net
} // namespace android
diff --git a/server/TrafficController.cpp b/server/TrafficController.cpp
index 8dceb5b..3839962 100644
--- a/server/TrafficController.cpp
+++ b/server/TrafficController.cpp
@@ -38,7 +38,6 @@
#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>
@@ -86,12 +85,12 @@
static_assert(STATS_MAP_SIZE - TOTAL_UID_STATS_ENTRIES_LIMIT > 100,
"The limit for stats map is to high, stats data may be lost due to overflow");
-#define FLAG_MSG_TRANS(result, flag, value) \
- do { \
- if (value & flag) { \
- result.append(StringPrintf(" %s", #flag)); \
- value &= ~flag; \
- } \
+#define FLAG_MSG_TRANS(result, flag, value) \
+ do { \
+ if ((value) & (flag)) { \
+ (result).append(" " #flag); \
+ (value) &= ~(flag); \
+ } \
} while (0)
const std::string uidMatchTypeToString(uint8_t match) {
@@ -116,7 +115,7 @@
mPrivilegedUser.find(appId) != mPrivilegedUser.end());
}
-const std::string UidPermissionTypeToString(uint8_t permission) {
+const std::string UidPermissionTypeToString(int permission) {
if (permission == INetd::PERMISSION_NONE) {
return "PERMISSION_NONE";
}
@@ -167,79 +166,43 @@
return listener;
}
-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));
-
- 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;
-}
-
TrafficController::TrafficController()
- : mBpfLevel(getBpfSupportLevel()),
+ : mBpfEnabled(isBpfSupported()),
mPerUidStatsEntriesLimit(PER_UID_STATS_ENTRIES_LIMIT),
mTotalUidStatsEntriesLimit(TOTAL_UID_STATS_ENTRIES_LIMIT) {}
TrafficController::TrafficController(uint32_t perUidLimit, uint32_t totalLimit)
- : mBpfLevel(getBpfSupportLevel()),
+ : mBpfEnabled(isBpfSupported()),
mPerUidStatsEntriesLimit(perUidLimit),
mTotalUidStatsEntriesLimit(totalLimit) {}
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",
- 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", 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", 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", 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", 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", 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",
- false));
RETURN_IF_NOT_OK(mConfigurationMap.init(CONFIGURATION_MAP_PATH));
- RETURN_IF_NOT_OK(changeOwnerAndMode(CONFIGURATION_MAP_PATH, AID_NET_BW_STATS,
- "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", true));
RETURN_IF_NOT_OK(mUidOwnerMap.clear());
RETURN_IF_NOT_OK(mUidPermissionMap.init(UID_PERMISSION_MAP_PATH));
+
return netdutils::status::ok;
}
-static Status attachProgramToCgroup(const char* programPath, const int cgroupFd,
+static Status attachProgramToCgroup(const char* programPath, const unique_fd& cgroupFd,
bpf_attach_type type) {
- unique_fd cgroupProg(bpfFdGet(programPath, 0));
+ unique_fd cgroupProg(retrieveProgram(programPath));
if (cgroupProg == -1) {
int ret = errno;
ALOGE("Failed to get program from %s: %s", programPath, strerror(ret));
@@ -284,7 +247,7 @@
}
Status TrafficController::start() {
- if (mBpfLevel == BpfLevel::NONE) {
+ if (!mBpfEnabled) {
return netdutils::status::ok;
}
@@ -351,14 +314,14 @@
return -EPERM;
}
- if (mBpfLevel == BpfLevel::NONE) {
+ if (!mBpfEnabled) {
if (legacy_tagSocket(sockFd, tag, uid)) return -errno;
return 0;
}
uint64_t sock_cookie = getSocketCookie(sockFd);
if (sock_cookie == NONEXISTENT_COOKIE) return -errno;
- UidTag newKey = {.uid = (uint32_t)uid, .tag = tag};
+ UidTagValue newKey = {.uid = (uint32_t)uid, .tag = tag};
uint32_t totalEntryCount = 0;
uint32_t perUidEntryCount = 0;
@@ -368,18 +331,19 @@
// over the map since when mMutex is hold, system server cannot toggle
// the live stats map and clean it. So nobody can delete entries from the map.
const auto countUidStatsEntries = [uid, &totalEntryCount, &perUidEntryCount](
- const StatsKey& key, BpfMap<StatsKey, StatsValue>&) {
+ const StatsKey& key,
+ const BpfMap<StatsKey, StatsValue>&) {
if (key.uid == uid) {
perUidEntryCount++;
}
totalEntryCount++;
- return netdutils::status::ok;
+ return base::Result<void>();
};
auto configuration = mConfigurationMap.readValue(CURRENT_STATS_MAP_CONFIGURATION_KEY);
- if (!isOk(configuration.status())) {
+ if (!configuration.ok()) {
ALOGE("Failed to get current configuration: %s, fd: %d",
- strerror(configuration.status().code()), mConfigurationMap.getMap().get());
- return -configuration.status().code();
+ strerror(configuration.error().code()), mConfigurationMap.getMap().get());
+ return -configuration.error().code();
}
if (configuration.value() != SELECT_MAP_A && configuration.value() != SELECT_MAP_B) {
ALOGE("unknown configuration value: %d", configuration.value());
@@ -388,11 +352,11 @@
BpfMap<StatsKey, StatsValue>& currentMap =
(configuration.value() == SELECT_MAP_A) ? mStatsMapA : mStatsMapB;
- Status res = currentMap.iterate(countUidStatsEntries);
- if (!isOk(res)) {
+ base::Result<void> res = currentMap.iterate(countUidStatsEntries);
+ if (!res.ok()) {
ALOGE("Failed to count the stats entry in map %d: %s", currentMap.getMap().get(),
- strerror(res.code()));
- return -res.code();
+ strerror(res.error().code()));
+ return -res.error().code();
}
if (totalEntryCount > mTotalUidStatsEntriesLimit ||
@@ -408,27 +372,29 @@
// program in kernel only read this map, and is protected by rcu read lock. It
// should be fine to cocurrently update the map while eBPF program is running.
res = mCookieTagMap.writeValue(sock_cookie, newKey, BPF_ANY);
- if (!isOk(res)) {
- ALOGE("Failed to tag the socket: %s, fd: %d", strerror(res.code()),
+ if (!res.ok()) {
+ ALOGE("Failed to tag the socket: %s, fd: %d", strerror(res.error().code()),
mCookieTagMap.getMap().get());
+ return -res.error().code();
}
- return -res.code();
+ return 0;
}
int TrafficController::untagSocket(int sockFd) {
std::lock_guard guard(mMutex);
- if (mBpfLevel == BpfLevel::NONE) {
+ if (!mBpfEnabled) {
if (legacy_untagSocket(sockFd)) return -errno;
return 0;
}
uint64_t sock_cookie = getSocketCookie(sockFd);
if (sock_cookie == NONEXISTENT_COOKIE) return -errno;
- Status res = mCookieTagMap.deleteValue(sock_cookie);
- if (!isOk(res)) {
- ALOGE("Failed to untag socket: %s\n", strerror(res.code()));
+ base::Result<void> res = mCookieTagMap.deleteValue(sock_cookie);
+ if (!res.ok()) {
+ ALOGE("Failed to untag socket: %s\n", strerror(res.error().code()));
+ return -res.error().code();
}
- return -res.code();
+ return 0;
}
int TrafficController::setCounterSet(int counterSetNum, uid_t uid, uid_t callingUid) {
@@ -437,7 +403,7 @@
std::lock_guard guard(mMutex);
if (!hasUpdateDeviceStatsPermission(callingUid)) return -EPERM;
- if (mBpfLevel == BpfLevel::NONE) {
+ if (!mBpfEnabled) {
if (legacy_setCounterSet(counterSetNum, uid)) return -errno;
return 0;
}
@@ -470,69 +436,72 @@
std::lock_guard guard(mMutex);
if (!hasUpdateDeviceStatsPermission(callingUid)) return -EPERM;
- if (mBpfLevel == BpfLevel::NONE) {
+ if (!mBpfEnabled) {
if (legacy_deleteTagData(tag, uid)) return -errno;
return 0;
}
// First we go through the cookieTagMap to delete the target uid tag combination. Or delete all
// the tags related to the uid if the tag is 0.
- const auto deleteMatchedCookieEntries = [uid, tag](const uint64_t& key, const UidTag& value,
- BpfMap<uint64_t, UidTag>& map) {
+ const auto deleteMatchedCookieEntries = [uid, tag](const uint64_t& key,
+ const UidTagValue& value,
+ BpfMap<uint64_t, UidTagValue>& map) {
if (value.uid == uid && (value.tag == tag || tag == 0)) {
- Status res = map.deleteValue(key);
- if (isOk(res) || (res.code() == ENOENT)) {
- return netdutils::status::ok;
+ auto res = map.deleteValue(key);
+ if (res.ok() || (res.error().code() == ENOENT)) {
+ return base::Result<void>();
}
- ALOGE("Failed to delete data(cookie = %" PRIu64 "): %s\n", key, strerror(res.code()));
+ ALOGE("Failed to delete data(cookie = %" PRIu64 "): %s\n", key,
+ strerror(res.error().code()));
}
// Move forward to next cookie in the map.
- return netdutils::status::ok;
+ return base::Result<void>();
};
- mCookieTagMap.iterateWithValue(deleteMatchedCookieEntries).ignoreError();
+ mCookieTagMap.iterateWithValue(deleteMatchedCookieEntries);
// Now we go through the Tag stats map and delete the data entry with correct uid and tag
// combination. Or all tag stats under that uid if the target tag is 0.
const auto deleteMatchedUidTagEntries = [uid, tag](const StatsKey& key,
BpfMap<StatsKey, StatsValue>& map) {
if (key.uid == uid && (key.tag == tag || tag == 0)) {
- Status res = map.deleteValue(key);
- if (isOk(res) || (res.code() == ENOENT)) {
+ auto res = map.deleteValue(key);
+ if (res.ok() || (res.error().code() == ENOENT)) {
//Entry is deleted, use the current key to get a new nextKey;
- return netdutils::status::ok;
+ return base::Result<void>();
}
ALOGE("Failed to delete data(uid=%u, tag=%u): %s\n", key.uid, key.tag,
- strerror(res.code()));
+ strerror(res.error().code()));
}
- return netdutils::status::ok;
+ return base::Result<void>();
};
- mStatsMapB.iterate(deleteMatchedUidTagEntries).ignoreError();
- mStatsMapA.iterate(deleteMatchedUidTagEntries).ignoreError();
+ mStatsMapB.iterate(deleteMatchedUidTagEntries);
+ mStatsMapA.iterate(deleteMatchedUidTagEntries);
// If the tag is not zero, we already deleted all the data entry required. If tag is 0, we also
// need to delete the stats stored in uidStatsMap and counterSet map.
if (tag != 0) return 0;
- Status res = mUidCounterSetMap.deleteValue(uid);
- if (!isOk(res) && res.code() != ENOENT) {
+ auto res = mUidCounterSetMap.deleteValue(uid);
+ if (!res.ok() && res.error().code() != ENOENT) {
ALOGE("Failed to delete counterSet data(uid=%u, tag=%u): %s\n", uid, tag,
- strerror(res.code()));
+ strerror(res.error().code()));
}
- auto deleteAppUidStatsEntry = [uid](const uint32_t& key, BpfMap<uint32_t, StatsValue>& map) {
+ auto deleteAppUidStatsEntry = [uid](const uint32_t& key,
+ BpfMap<uint32_t, StatsValue>& map) -> base::Result<void> {
if (key == uid) {
- Status res = map.deleteValue(key);
- if (isOk(res) || (res.code() == ENOENT)) {
- return netdutils::status::ok;
+ auto res = map.deleteValue(key);
+ if (res.ok() || (res.error().code() == ENOENT)) {
+ return {};
}
- ALOGE("Failed to delete data(uid=%u): %s", key, strerror(res.code()));
+ ALOGE("Failed to delete data(uid=%u): %s", key, strerror(res.error().code()));
}
- return netdutils::status::ok;
+ return {};
};
- mAppUidStatsMap.iterate(deleteAppUidStatsEntry).ignoreError();
+ mAppUidStatsMap.iterate(deleteAppUidStatsEntry);
return 0;
}
int TrafficController::addInterface(const char* name, uint32_t ifaceIndex) {
- if (mBpfLevel == BpfLevel::NONE) return 0;
+ if (!mBpfEnabled) return 0;
IfaceValue iface;
if (ifaceIndex == 0) {
@@ -577,9 +546,11 @@
Status TrafficController::removeRule(BpfMap<uint32_t, UidOwnerValue>& map, uint32_t uid,
UidOwnerMatchType match) {
auto oldMatch = map.readValue(uid);
- if (isOk(oldMatch)) {
- UidOwnerValue newMatch = {.rule = static_cast<uint8_t>(oldMatch.value().rule & ~match),
- .iif = (match == IIF_MATCH) ? 0 : oldMatch.value().iif};
+ if (oldMatch.ok()) {
+ UidOwnerValue newMatch = {
+ .iif = (match == IIF_MATCH) ? 0 : oldMatch.value().iif,
+ .rule = static_cast<uint8_t>(oldMatch.value().rule & ~match),
+ };
if (newMatch.rule == 0) {
RETURN_IF_NOT_OK(map.deleteValue(uid));
} else {
@@ -600,12 +571,17 @@
return statusFromErrno(EINVAL, "Non-interface match must have zero interface index");
}
auto oldMatch = map.readValue(uid);
- if (isOk(oldMatch)) {
- UidOwnerValue newMatch = {.rule = static_cast<uint8_t>(oldMatch.value().rule | match),
- .iif = iif ? iif : oldMatch.value().iif};
+ if (oldMatch.ok()) {
+ UidOwnerValue newMatch = {
+ .iif = iif ? iif : oldMatch.value().iif,
+ .rule = static_cast<uint8_t>(oldMatch.value().rule | match),
+ };
RETURN_IF_NOT_OK(map.writeValue(uid, newMatch, BPF_ANY));
} else {
- UidOwnerValue newMatch = {.rule = static_cast<uint8_t>(match), .iif = iif};
+ UidOwnerValue newMatch = {
+ .iif = iif,
+ .rule = static_cast<uint8_t>(match),
+ };
RETURN_IF_NOT_OK(map.writeValue(uid, newMatch, BPF_ANY));
}
return netdutils::status::ok;
@@ -642,7 +618,7 @@
int TrafficController::changeUidOwnerRule(ChildChain chain, uid_t uid, FirewallRule rule,
FirewallType type) {
- if (mBpfLevel == BpfLevel::NONE) {
+ if (!mBpfEnabled) {
ALOGE("bpf is not set up, should use iptables rule");
return -ENOSYS;
}
@@ -679,7 +655,7 @@
if (uidSet.find((int32_t) key) == uidSet.end()) {
uidsToDelete.push_back(key);
}
- return netdutils::status::ok;
+ return base::Result<void>();
};
RETURN_IF_NOT_OK(mUidOwnerMap.iterate(getUidsToDelete));
@@ -695,7 +671,7 @@
Status TrafficController::addUidInterfaceRules(const int iif,
const std::vector<int32_t>& uidsToAdd) {
- if (mBpfLevel == BpfLevel::NONE) {
+ if (!mBpfEnabled) {
ALOGW("UID ingress interface filtering not possible without BPF owner match");
return statusFromErrno(EOPNOTSUPP, "eBPF not supported");
}
@@ -714,7 +690,7 @@
}
Status TrafficController::removeUidInterfaceRules(const std::vector<int32_t>& uidsToDelete) {
- if (mBpfLevel == BpfLevel::NONE) {
+ if (!mBpfEnabled) {
ALOGW("UID ingress interface filtering not possible without BPF owner match");
return statusFromErrno(EOPNOTSUPP, "eBPF not supported");
}
@@ -729,17 +705,10 @@
return netdutils::status::ok;
}
-int TrafficController::replaceUidOwnerMap(const std::string& name, bool isWhitelist,
+int TrafficController::replaceUidOwnerMap(const std::string& name, bool isWhitelist __unused,
const std::vector<int32_t>& uids) {
- FirewallRule rule;
- FirewallType type;
- if (isWhitelist) {
- type = WHITELIST;
- rule = ALLOW;
- } else {
- type = BLACKLIST;
- rule = DENY;
- }
+ // FirewallRule rule = isWhitelist ? ALLOW : DENY;
+ // FirewallType type = isWhitelist ? WHITELIST : BLACKLIST;
Status res;
if (!name.compare(FirewallController::LOCAL_DOZABLE)) {
res = replaceRulesInMap(DOZABLE_MATCH, uids);
@@ -762,10 +731,10 @@
std::lock_guard guard(mMutex);
uint32_t key = UID_RULES_CONFIGURATION_KEY;
auto oldConfiguration = mConfigurationMap.readValue(key);
- if (!isOk(oldConfiguration)) {
+ if (!oldConfiguration.ok()) {
ALOGE("Cannot read the old configuration from map: %s",
- oldConfiguration.status().msg().c_str());
- return -oldConfiguration.status().code();
+ oldConfiguration.error().message().c_str());
+ return -oldConfiguration.error().code();
}
Status res;
BpfConfig newConfiguration;
@@ -792,33 +761,33 @@
return -res.code();
}
-BpfLevel TrafficController::getBpfLevel() {
- return mBpfLevel;
+bool TrafficController::getBpfEnabled() {
+ return mBpfEnabled;
}
Status TrafficController::swapActiveStatsMap() {
std::lock_guard guard(mMutex);
- if (mBpfLevel == BpfLevel::NONE) {
+ if (!mBpfEnabled) {
return statusFromErrno(EOPNOTSUPP, "This device doesn't have eBPF support");
}
uint32_t key = CURRENT_STATS_MAP_CONFIGURATION_KEY;
auto oldConfiguration = mConfigurationMap.readValue(key);
- if (!isOk(oldConfiguration)) {
+ if (!oldConfiguration.ok()) {
ALOGE("Cannot read the old configuration from map: %s",
- oldConfiguration.status().msg().c_str());
- return oldConfiguration.status();
+ oldConfiguration.error().message().c_str());
+ return Status(oldConfiguration.error().code(), oldConfiguration.error().message());
}
// Write to the configuration map to inform the kernel eBPF program to switch
// from using one map to the other. Use flag BPF_EXIST here since the map should
// be already populated in initMaps.
uint8_t newConfigure = (oldConfiguration.value() == SELECT_MAP_A) ? SELECT_MAP_B : SELECT_MAP_A;
- Status res = mConfigurationMap.writeValue(CURRENT_STATS_MAP_CONFIGURATION_KEY, newConfigure,
- BPF_EXIST);
- if (!isOk(res)) {
- ALOGE("Failed to toggle the stats map: %s", strerror(res.code()));
+ auto res = mConfigurationMap.writeValue(CURRENT_STATS_MAP_CONFIGURATION_KEY, newConfigure,
+ BPF_EXIST);
+ if (!res.ok()) {
+ ALOGE("Failed to toggle the stats map: %s", strerror(res.error().code()));
return res;
}
// After changing the config, we need to make sure all the current running
@@ -845,9 +814,9 @@
// Clean up all permission information for the related uid if all the
// packages related to it are uninstalled.
mPrivilegedUser.erase(uid);
- if (mBpfLevel > BpfLevel::NONE) {
+ if (mBpfEnabled) {
Status ret = mUidPermissionMap.deleteValue(uid);
- if (!isOk(ret) && ret.code() != ENONET) {
+ if (!isOk(ret) && ret.code() != ENOENT) {
ALOGE("Failed to clean up the permission for %u: %s", uid,
strerror(ret.code()));
}
@@ -866,7 +835,7 @@
}
// Skip the bpf map operation if not supported.
- if (mBpfLevel == BpfLevel::NONE) {
+ if (!mBpfEnabled) {
continue;
}
// The map stores all the permissions that the UID has, except if the only permission
@@ -924,9 +893,10 @@
dw.println("TrafficController");
ScopedIndent indentPreBpfModule(dw);
- dw.println("BPF module status: %s", BpfLevelToString(mBpfLevel).c_str());
+ dw.println("BPF module status: %s", mBpfEnabled ? "enabled" : "disabled");
+ dw.println("BPF support level: %s", BpfLevelToString(getBpfSupportLevel()).c_str());
- if (mBpfLevel == BpfLevel::NONE) {
+ if (!mBpfEnabled) {
return;
}
@@ -974,14 +944,14 @@
// Print CookieTagMap content.
dumpBpfMap("mCookieTagMap", dw, "");
- const auto printCookieTagInfo = [&dw](const uint64_t& key, const UidTag& value,
- const BpfMap<uint64_t, UidTag>&) {
+ const auto printCookieTagInfo = [&dw](const uint64_t& key, const UidTagValue& value,
+ const BpfMap<uint64_t, UidTagValue>&) {
dw.println("cookie=%" PRIu64 " tag=0x%x uid=%u", key, value.tag, value.uid);
- return netdutils::status::ok;
+ return base::Result<void>();
};
- Status res = mCookieTagMap.iterateWithValue(printCookieTagInfo);
- if (!isOk(res)) {
- dw.println("mCookieTagMap print end with error: %s", res.msg().c_str());
+ base::Result<void> res = mCookieTagMap.iterateWithValue(printCookieTagInfo);
+ if (!res.ok()) {
+ dw.println("mCookieTagMap print end with error: %s", res.error().message().c_str());
}
// Print UidCounterSetMap Content
@@ -989,11 +959,11 @@
const auto printUidInfo = [&dw](const uint32_t& key, const uint8_t& value,
const BpfMap<uint32_t, uint8_t>&) {
dw.println("%u %u", key, value);
- return netdutils::status::ok;
+ return base::Result<void>();
};
res = mUidCounterSetMap.iterateWithValue(printUidInfo);
- if (!isOk(res)) {
- dw.println("mUidCounterSetMap print end with error: %s", res.msg().c_str());
+ if (!res.ok()) {
+ dw.println("mUidCounterSetMap print end with error: %s", res.error().message().c_str());
}
// Print AppUidStatsMap content
@@ -1003,11 +973,11 @@
const BpfMap<uint32_t, StatsValue>&) {
dw.println("%u %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64, key, value.rxBytes,
value.rxPackets, value.txBytes, value.txPackets);
- return netdutils::status::ok;
+ return base::Result<void>();
};
res = mAppUidStatsMap.iterateWithValue(printAppUidStatsInfo);
if (!res.ok()) {
- dw.println("mAppUidStatsMap print end with error: %s", res.msg().c_str());
+ dw.println("mAppUidStatsMap print end with error: %s", res.error().message().c_str());
}
// Print uidStatsMap content
@@ -1018,24 +988,24 @@
const BpfMap<StatsKey, StatsValue>&) {
uint32_t ifIndex = key.ifaceIndex;
auto ifname = mIfaceIndexNameMap.readValue(ifIndex);
- if (!isOk(ifname)) {
- strlcpy(ifname.value().name, "unknown", sizeof(IfaceValue));
+ if (!ifname.ok()) {
+ ifname = IfaceValue{"unknown"};
}
dw.println("%u %s 0x%x %u %u %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64, ifIndex,
ifname.value().name, key.tag, key.uid, key.counterSet, value.rxBytes,
value.rxPackets, value.txBytes, value.txPackets);
- return netdutils::status::ok;
+ return base::Result<void>();
};
res = mStatsMapA.iterateWithValue(printStatsInfo);
- if (!isOk(res)) {
- dw.println("mStatsMapA print end with error: %s", res.msg().c_str());
+ if (!res.ok()) {
+ dw.println("mStatsMapA print end with error: %s", res.error().message().c_str());
}
// Print TagStatsMap content.
dumpBpfMap("mStatsMapB", dw, statsHeader);
res = mStatsMapB.iterateWithValue(printStatsInfo);
- if (!isOk(res)) {
- dw.println("mStatsMapB print end with error: %s", res.msg().c_str());
+ if (!res.ok()) {
+ dw.println("mStatsMapB print end with error: %s", res.error().message().c_str());
}
// Print ifaceIndexToNameMap content.
@@ -1044,11 +1014,11 @@
const BpfMap<uint32_t, IfaceValue>&) {
const char* ifname = value.name;
dw.println("ifaceIndex=%u ifaceName=%s", key, ifname);
- return netdutils::status::ok;
+ return base::Result<void>();
};
res = mIfaceIndexNameMap.iterateWithValue(printIfaceNameInfo);
- if (!isOk(res)) {
- dw.println("mIfaceIndexNameMap print end with error: %s", res.msg().c_str());
+ if (!res.ok()) {
+ dw.println("mIfaceIndexNameMap print end with error: %s", res.error().message().c_str());
}
// Print ifaceStatsMap content
@@ -1058,42 +1028,55 @@
const auto printIfaceStatsInfo = [&dw, this](const uint32_t& key, const StatsValue& value,
const BpfMap<uint32_t, StatsValue>&) {
auto ifname = mIfaceIndexNameMap.readValue(key);
- if (!isOk(ifname)) {
- strlcpy(ifname.value().name, "unknown", sizeof(IfaceValue));
+ if (!ifname.ok()) {
+ ifname = IfaceValue{"unknown"};
}
dw.println("%u %s %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64, key, ifname.value().name,
value.rxBytes, value.rxPackets, value.txBytes, value.txPackets);
- return netdutils::status::ok;
+ return base::Result<void>();
};
res = mIfaceStatsMap.iterateWithValue(printIfaceStatsInfo);
- if (!isOk(res)) {
- dw.println("mIfaceStatsMap print end with error: %s", res.msg().c_str());
+ if (!res.ok()) {
+ dw.println("mIfaceStatsMap print end with error: %s", res.error().message().c_str());
}
dw.blankline();
uint32_t key = UID_RULES_CONFIGURATION_KEY;
auto configuration = mConfigurationMap.readValue(key);
- if (isOk(configuration)) {
- dw.println("current ownerMatch configuration: %d", configuration.value());
+ if (configuration.ok()) {
+ dw.println("current ownerMatch configuration: %d%s", configuration.value(),
+ uidMatchTypeToString(configuration.value()).c_str());
} else {
dw.println("mConfigurationMap read ownerMatch configure failed with error: %s",
- configuration.status().msg().c_str());
+ configuration.error().message().c_str());
}
+
key = CURRENT_STATS_MAP_CONFIGURATION_KEY;
configuration = mConfigurationMap.readValue(key);
- if (isOk(configuration)) {
- dw.println("current statsMap configuration: %d", configuration.value());
+ if (configuration.ok()) {
+ 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);
} else {
dw.println("mConfigurationMap read stats map configure failed with error: %s",
- configuration.status().msg().c_str());
+ configuration.error().message().c_str());
}
dumpBpfMap("mUidOwnerMap", dw, "");
const auto printUidMatchInfo = [&dw, this](const uint32_t& key, const UidOwnerValue& value,
const BpfMap<uint32_t, UidOwnerValue>&) {
if (value.rule & IIF_MATCH) {
auto ifname = mIfaceIndexNameMap.readValue(value.iif);
- if (isOk(ifname)) {
+ if (ifname.ok()) {
dw.println("%u %s %s", key, uidMatchTypeToString(value.rule).c_str(),
ifname.value().name);
} else {
@@ -1102,21 +1085,21 @@
} else {
dw.println("%u %s", key, uidMatchTypeToString(value.rule).c_str());
}
- return netdutils::status::ok;
+ return base::Result<void>();
};
res = mUidOwnerMap.iterateWithValue(printUidMatchInfo);
- if (!isOk(res)) {
- dw.println("mUidOwnerMap print end with error: %s", res.msg().c_str());
+ if (!res.ok()) {
+ dw.println("mUidOwnerMap print end with error: %s", res.error().message().c_str());
}
dumpBpfMap("mUidPermissionMap", dw, "");
- const auto printUidPermissionInfo = [&dw](const uint32_t& key, const uint8_t& value,
+ const auto printUidPermissionInfo = [&dw](const uint32_t& key, const int& value,
const BpfMap<uint32_t, uint8_t>&) {
dw.println("%u %s", key, UidPermissionTypeToString(value).c_str());
- return netdutils::status::ok;
+ return base::Result<void>();
};
res = mUidPermissionMap.iterateWithValue(printUidPermissionInfo);
- if (!isOk(res)) {
- dw.println("mUidPermissionMap print end with error: %s", res.msg().c_str());
+ if (!res.ok()) {
+ dw.println("mUidPermissionMap print end with error: %s", res.error().message().c_str());
}
dumpBpfMap("mPrivilegedUser", dw, "");
diff --git a/server/TrafficController.h b/server/TrafficController.h
index 7ec0279..a2539a9 100644
--- a/server/TrafficController.h
+++ b/server/TrafficController.h
@@ -32,10 +32,6 @@
#include "utils/String16.h"
using android::bpf::BpfMap;
-using android::bpf::IfaceValue;
-using android::bpf::StatsKey;
-using android::bpf::StatsValue;
-using android::bpf::UidTag;
namespace android {
namespace net {
@@ -85,7 +81,7 @@
* Check if the current device have the bpf traffic stats accounting service
* running.
*/
- bpf::BpfLevel getBpfLevel();
+ bool getBpfEnabled();
/*
* Swap the stats map config from current active stats map to the idle one.
@@ -136,9 +132,9 @@
* that receives them, then the kernel will drop some of these sockets and we
* won't delete their tags.
* Map Key: uint64_t socket cookie
- * Map Value: struct UidTag, contains a uint32 uid and a uint32 tag.
+ * Map Value: UidTagValue, contains a uint32 uid and a uint32 tag.
*/
- BpfMap<uint64_t, UidTag> mCookieTagMap GUARDED_BY(mMutex);
+ BpfMap<uint64_t, UidTagValue> mCookieTagMap GUARDED_BY(mMutex);
/*
* mUidCounterSetMap: Store the counterSet of a specific uid.
@@ -159,9 +155,9 @@
* mStatsMapA/mStatsMapB: Store the traffic statistics for a specific
* combination of uid, tag, iface and counterSet. These two maps contain
* both tagged and untagged traffic.
- * Map Key: Struct StatsKey contains the uid, tag, counterSet and ifaceIndex
+ * Map Key: StatsKey contains the uid, tag, counterSet and ifaceIndex
* information.
- * Map Value: struct Stats, contains packet count and byte count of each
+ * Map Value: Stats, contains packet count and byte count of each
* transport protocol on egress and ingress direction.
*/
BpfMap<StatsKey, StatsValue> mStatsMapA GUARDED_BY(mMutex);
@@ -213,7 +209,7 @@
netdutils::Status addRule(BpfMap<uint32_t, UidOwnerValue>& map, uint32_t uid,
UidOwnerMatchType match, uint32_t iif = 0) REQUIRES(mMutex);
- bpf::BpfLevel mBpfLevel;
+ bool mBpfEnabled;
// mMutex guards all accesses to mConfigurationMap, mUidOwnerMap, mUidPermissionMap,
// mStatsMapA, mStatsMapB and mPrivilegedUser. It is designed to solve the following
diff --git a/server/TrafficControllerTest.cpp b/server/TrafficControllerTest.cpp
index c97c104..3cde9be 100644
--- a/server/TrafficControllerTest.cpp
+++ b/server/TrafficControllerTest.cpp
@@ -33,7 +33,6 @@
#include <android-base/strings.h>
#include <netdutils/MockSyscalls.h>
-#include "netdutils/StatusOr.h"
#include "FirewallController.h"
#include "TrafficController.h"
@@ -44,8 +43,8 @@
namespace android {
namespace net {
+using base::Result;
using netdutils::isOk;
-using netdutils::StatusOr;
constexpr int TEST_MAP_SIZE = 10;
constexpr int TEST_COOKIE = 1;
@@ -65,7 +64,7 @@
TrafficControllerTest()
: mTc(TEST_PER_UID_STATS_ENTRIES_LIMIT, TEST_TOTAL_UID_STATS_ENTRIES_LIMIT) {}
TrafficController mTc;
- BpfMap<uint64_t, UidTag> mFakeCookieTagMap;
+ BpfMap<uint64_t, UidTagValue> mFakeCookieTagMap;
BpfMap<uint32_t, uint8_t> mFakeUidCounterSetMap;
BpfMap<uint32_t, StatsValue> mFakeAppUidStatsMap;
BpfMap<StatsKey, StatsValue> mFakeStatsMapA;
@@ -78,20 +77,20 @@
SKIP_IF_BPF_NOT_SUPPORTED;
ASSERT_EQ(0, setrlimitForTest());
- mFakeCookieTagMap.reset(createMap(BPF_MAP_TYPE_HASH, sizeof(uint64_t),
- sizeof(struct UidTag), TEST_MAP_SIZE, 0));
+ mFakeCookieTagMap.reset(createMap(BPF_MAP_TYPE_HASH, sizeof(uint64_t), sizeof(UidTagValue),
+ TEST_MAP_SIZE, 0));
ASSERT_VALID(mFakeCookieTagMap);
mFakeUidCounterSetMap.reset(
createMap(BPF_MAP_TYPE_HASH, sizeof(uint32_t), sizeof(uint8_t), TEST_MAP_SIZE, 0));
ASSERT_VALID(mFakeUidCounterSetMap);
- mFakeAppUidStatsMap.reset(createMap(BPF_MAP_TYPE_HASH, sizeof(uint32_t),
- sizeof(struct StatsValue), TEST_MAP_SIZE, 0));
+ mFakeAppUidStatsMap.reset(createMap(BPF_MAP_TYPE_HASH, sizeof(uint32_t), sizeof(StatsValue),
+ TEST_MAP_SIZE, 0));
ASSERT_VALID(mFakeAppUidStatsMap);
- mFakeStatsMapA.reset(createMap(BPF_MAP_TYPE_HASH, sizeof(struct StatsKey),
- sizeof(struct StatsValue), TEST_MAP_SIZE, 0));
+ mFakeStatsMapA.reset(createMap(BPF_MAP_TYPE_HASH, sizeof(StatsKey), sizeof(StatsValue),
+ TEST_MAP_SIZE, 0));
ASSERT_VALID(mFakeStatsMapA);
mFakeConfigurationMap.reset(
@@ -117,8 +116,8 @@
ASSERT_VALID(mTc.mConfigurationMap);
// Always write to stats map A by default.
- ASSERT_TRUE(isOk(mTc.mConfigurationMap.writeValue(CURRENT_STATS_MAP_CONFIGURATION_KEY,
- SELECT_MAP_A, BPF_ANY)));
+ ASSERT_RESULT_OK(mTc.mConfigurationMap.writeValue(CURRENT_STATS_MAP_CONFIGURATION_KEY,
+ SELECT_MAP_A, BPF_ANY));
mTc.mUidOwnerMap.reset(dupFd(mFakeUidOwnerMap.getMap()));
ASSERT_VALID(mTc.mUidOwnerMap);
mTc.mUidPermissionMap.reset(dupFd(mFakeUidPermissionMap.getMap()));
@@ -141,25 +140,25 @@
}
void expectUidTag(uint64_t cookie, uid_t uid, uint32_t tag) {
- StatusOr<UidTag> tagResult = mFakeCookieTagMap.readValue(cookie);
- EXPECT_TRUE(isOk(tagResult));
+ Result<UidTagValue> tagResult = mFakeCookieTagMap.readValue(cookie);
+ ASSERT_RESULT_OK(tagResult);
EXPECT_EQ(uid, tagResult.value().uid);
EXPECT_EQ(tag, tagResult.value().tag);
}
- void expectNoTag(uint64_t cookie) { EXPECT_FALSE(isOk(mFakeCookieTagMap.readValue(cookie))); }
+ void expectNoTag(uint64_t cookie) { EXPECT_FALSE(mFakeCookieTagMap.readValue(cookie).ok()); }
void populateFakeStats(uint64_t cookie, uint32_t uid, uint32_t tag, StatsKey* key) {
- UidTag cookieMapkey = {.uid = (uint32_t)uid, .tag = tag};
- EXPECT_TRUE(isOk(mFakeCookieTagMap.writeValue(cookie, cookieMapkey, BPF_ANY)));
+ UidTagValue cookieMapkey = {.uid = (uint32_t)uid, .tag = tag};
+ EXPECT_RESULT_OK(mFakeCookieTagMap.writeValue(cookie, cookieMapkey, BPF_ANY));
*key = {.uid = uid, .tag = tag, .counterSet = TEST_COUNTERSET, .ifaceIndex = 1};
StatsValue statsMapValue = {.rxPackets = 1, .rxBytes = 100};
uint8_t counterSet = TEST_COUNTERSET;
- EXPECT_TRUE(isOk(mFakeUidCounterSetMap.writeValue(uid, counterSet, BPF_ANY)));
- EXPECT_TRUE(isOk(mFakeStatsMapA.writeValue(*key, statsMapValue, BPF_ANY)));
+ EXPECT_RESULT_OK(mFakeUidCounterSetMap.writeValue(uid, counterSet, BPF_ANY));
+ EXPECT_RESULT_OK(mFakeStatsMapA.writeValue(*key, statsMapValue, BPF_ANY));
key->tag = 0;
- EXPECT_TRUE(isOk(mFakeStatsMapA.writeValue(*key, statsMapValue, BPF_ANY)));
- EXPECT_TRUE(isOk(mFakeAppUidStatsMap.writeValue(uid, statsMapValue, BPF_ANY)));
+ EXPECT_RESULT_OK(mFakeStatsMapA.writeValue(*key, statsMapValue, BPF_ANY));
+ EXPECT_RESULT_OK(mFakeAppUidStatsMap.writeValue(uid, statsMapValue, BPF_ANY));
// put tag information back to statsKey
key->tag = tag;
}
@@ -167,47 +166,47 @@
void checkUidOwnerRuleForChain(ChildChain chain, UidOwnerMatchType match) {
uint32_t uid = TEST_UID;
EXPECT_EQ(0, mTc.changeUidOwnerRule(chain, uid, DENY, BLACKLIST));
- StatusOr<UidOwnerValue> value = mFakeUidOwnerMap.readValue(uid);
- EXPECT_TRUE(isOk(value));
+ Result<UidOwnerValue> value = mFakeUidOwnerMap.readValue(uid);
+ EXPECT_RESULT_OK(value);
EXPECT_TRUE(value.value().rule & match);
uid = TEST_UID2;
EXPECT_EQ(0, mTc.changeUidOwnerRule(chain, uid, ALLOW, WHITELIST));
value = mFakeUidOwnerMap.readValue(uid);
- EXPECT_TRUE(isOk(value));
+ EXPECT_RESULT_OK(value);
EXPECT_TRUE(value.value().rule & match);
EXPECT_EQ(0, mTc.changeUidOwnerRule(chain, uid, DENY, WHITELIST));
value = mFakeUidOwnerMap.readValue(uid);
- EXPECT_FALSE(isOk(value));
- EXPECT_EQ(ENOENT, value.status().code());
+ EXPECT_FALSE(value.ok());
+ EXPECT_EQ(ENOENT, value.error().code());
uid = TEST_UID;
EXPECT_EQ(0, mTc.changeUidOwnerRule(chain, uid, ALLOW, BLACKLIST));
value = mFakeUidOwnerMap.readValue(uid);
- EXPECT_FALSE(isOk(value));
- EXPECT_EQ(ENOENT, value.status().code());
+ EXPECT_FALSE(value.ok());
+ EXPECT_EQ(ENOENT, value.error().code());
uid = TEST_UID3;
EXPECT_EQ(-ENOENT, mTc.changeUidOwnerRule(chain, uid, ALLOW, BLACKLIST));
value = mFakeUidOwnerMap.readValue(uid);
- EXPECT_FALSE(isOk(value));
- EXPECT_EQ(ENOENT, value.status().code());
+ EXPECT_FALSE(value.ok());
+ EXPECT_EQ(ENOENT, value.error().code());
}
void checkEachUidValue(const std::vector<int32_t>& uids, UidOwnerMatchType match) {
for (uint32_t uid : uids) {
- StatusOr<UidOwnerValue> value = mFakeUidOwnerMap.readValue(uid);
- EXPECT_TRUE(isOk(value));
+ Result<UidOwnerValue> value = mFakeUidOwnerMap.readValue(uid);
+ EXPECT_RESULT_OK(value);
EXPECT_TRUE(value.value().rule & match);
}
std::set<uint32_t> uidSet(uids.begin(), uids.end());
const auto checkNoOtherUid = [&uidSet](const int32_t& key,
const BpfMap<uint32_t, UidOwnerValue>&) {
EXPECT_NE(uidSet.end(), uidSet.find(key));
- return netdutils::status::ok;
+ return Result<void>();
};
- EXPECT_TRUE(isOk(mFakeUidOwnerMap.iterate(checkNoOtherUid)));
+ EXPECT_RESULT_OK(mFakeUidOwnerMap.iterate(checkNoOtherUid));
}
void checkUidMapReplace(const std::string& name, const std::vector<int32_t>& uids,
@@ -224,8 +223,8 @@
uint32_t expectedIif) {
for (const std::string& strUid : appStrUids) {
uint32_t uid = stoi(strUid);
- StatusOr<UidOwnerValue> value = mFakeUidOwnerMap.readValue(uid);
- EXPECT_TRUE(isOk(value));
+ Result<UidOwnerValue> value = mFakeUidOwnerMap.readValue(uid);
+ EXPECT_RESULT_OK(value);
EXPECT_EQ(expectedRule, value.value().rule)
<< "Expected rule for UID " << uid << " to be " << expectedRule << ", but was "
<< value.value().rule;
@@ -238,14 +237,14 @@
template <class Key, class Value>
void expectMapEmpty(BpfMap<Key, Value>& map) {
auto isEmpty = map.isEmpty();
- EXPECT_TRUE(isOk(isEmpty));
+ EXPECT_RESULT_OK(isEmpty);
EXPECT_TRUE(isEmpty.value());
}
void expectUidPermissionMapValues(const std::vector<uid_t>& appUids, uint8_t expectedValue) {
for (uid_t uid : appUids) {
- StatusOr<uint8_t> value = mFakeUidPermissionMap.readValue(uid);
- EXPECT_TRUE(isOk(value));
+ Result<uint8_t> value = mFakeUidPermissionMap.readValue(uid);
+ EXPECT_RESULT_OK(value);
EXPECT_EQ(expectedValue, value.value())
<< "Expected value for UID " << uid << " to be " << expectedValue
<< ", but was " << value.value();
@@ -277,24 +276,24 @@
void expectFakeStatsUnchanged(uint64_t cookie, uint32_t tag, uint32_t uid,
StatsKey tagStatsMapKey) {
- StatusOr<UidTag> cookieMapResult = mFakeCookieTagMap.readValue(cookie);
- EXPECT_TRUE(isOk(cookieMapResult));
+ Result<UidTagValue> cookieMapResult = mFakeCookieTagMap.readValue(cookie);
+ EXPECT_RESULT_OK(cookieMapResult);
EXPECT_EQ(uid, cookieMapResult.value().uid);
EXPECT_EQ(tag, cookieMapResult.value().tag);
- StatusOr<uint8_t> counterSetResult = mFakeUidCounterSetMap.readValue(uid);
- EXPECT_TRUE(isOk(counterSetResult));
+ Result<uint8_t> counterSetResult = mFakeUidCounterSetMap.readValue(uid);
+ EXPECT_RESULT_OK(counterSetResult);
EXPECT_EQ(TEST_COUNTERSET, counterSetResult.value());
- StatusOr<StatsValue> statsMapResult = mFakeStatsMapA.readValue(tagStatsMapKey);
- EXPECT_TRUE(isOk(statsMapResult));
+ Result<StatsValue> statsMapResult = mFakeStatsMapA.readValue(tagStatsMapKey);
+ EXPECT_RESULT_OK(statsMapResult);
EXPECT_EQ((uint64_t)1, statsMapResult.value().rxPackets);
EXPECT_EQ((uint64_t)100, statsMapResult.value().rxBytes);
tagStatsMapKey.tag = 0;
statsMapResult = mFakeStatsMapA.readValue(tagStatsMapKey);
- EXPECT_TRUE(isOk(statsMapResult));
+ EXPECT_RESULT_OK(statsMapResult);
EXPECT_EQ((uint64_t)1, statsMapResult.value().rxPackets);
EXPECT_EQ((uint64_t)100, statsMapResult.value().rxBytes);
auto appStatsResult = mFakeAppUidStatsMap.readValue(uid);
- EXPECT_TRUE(isOk(appStatsResult));
+ EXPECT_RESULT_OK(appStatsResult);
EXPECT_EQ((uint64_t)1, appStatsResult.value().rxPackets);
EXPECT_EQ((uint64_t)100, appStatsResult.value().rxBytes);
}
@@ -348,7 +347,7 @@
ASSERT_EQ(0, mTc.untagSocket(v4socket1));
expectNoTag(sockCookie1);
expectUidTag(sockCookie2, TEST_UID, TEST_TAG);
- ASSERT_FALSE(isOk(mFakeCookieTagMap.getNextKey(sockCookie2)));
+ ASSERT_FALSE(mFakeCookieTagMap.getNextKey(sockCookie2).ok());
}
TEST_F(TrafficControllerTest, TestTagSocketV6) {
@@ -442,11 +441,11 @@
addPrivilegedUid(callingUid);
ASSERT_EQ(0, mTc.setCounterSet(TEST_COUNTERSET, TEST_UID, callingUid));
uid_t uid = TEST_UID;
- StatusOr<uint8_t> counterSetResult = mFakeUidCounterSetMap.readValue(uid);
- ASSERT_TRUE(isOk(counterSetResult));
+ Result<uint8_t> counterSetResult = mFakeUidCounterSetMap.readValue(uid);
+ ASSERT_RESULT_OK(counterSetResult);
ASSERT_EQ(TEST_COUNTERSET, counterSetResult.value());
ASSERT_EQ(0, mTc.setCounterSet(DEFAULT_COUNTERSET, TEST_UID, callingUid));
- ASSERT_FALSE(isOk(mFakeUidCounterSetMap.readValue(uid)));
+ ASSERT_FALSE(mFakeUidCounterSetMap.readValue(uid).ok());
expectMapEmpty(mFakeUidCounterSetMap);
}
@@ -455,7 +454,7 @@
ASSERT_EQ(-EPERM, mTc.setCounterSet(TEST_COUNTERSET, TEST_UID, TEST_UID2));
uid_t uid = TEST_UID;
- ASSERT_FALSE(isOk(mFakeUidCounterSetMap.readValue(uid)));
+ ASSERT_FALSE(mFakeUidCounterSetMap.readValue(uid).ok());
expectMapEmpty(mFakeUidCounterSetMap);
}
@@ -466,7 +465,7 @@
addPrivilegedUid(callingUid);
ASSERT_GT(0, mTc.setCounterSet(OVERFLOW_COUNTERSET, TEST_UID, callingUid));
uid_t uid = TEST_UID;
- ASSERT_FALSE(isOk(mFakeUidCounterSetMap.readValue(uid)));
+ ASSERT_FALSE(mFakeUidCounterSetMap.readValue(uid).ok());
expectMapEmpty(mFakeUidCounterSetMap);
}
@@ -494,18 +493,18 @@
StatsKey tagStatsMapKey;
populateFakeStats(cookie, uid, tag, &tagStatsMapKey);
ASSERT_EQ(0, mTc.deleteTagData(TEST_TAG, TEST_UID, callingUid));
- ASSERT_FALSE(isOk(mFakeCookieTagMap.readValue(cookie)));
- StatusOr<uint8_t> counterSetResult = mFakeUidCounterSetMap.readValue(uid);
- ASSERT_TRUE(isOk(counterSetResult));
+ ASSERT_FALSE(mFakeCookieTagMap.readValue(cookie).ok());
+ Result<uint8_t> counterSetResult = mFakeUidCounterSetMap.readValue(uid);
+ ASSERT_RESULT_OK(counterSetResult);
ASSERT_EQ(TEST_COUNTERSET, counterSetResult.value());
- ASSERT_FALSE(isOk(mFakeStatsMapA.readValue(tagStatsMapKey)));
+ ASSERT_FALSE(mFakeStatsMapA.readValue(tagStatsMapKey).ok());
tagStatsMapKey.tag = 0;
- StatusOr<StatsValue> statsMapResult = mFakeStatsMapA.readValue(tagStatsMapKey);
- ASSERT_TRUE(isOk(statsMapResult));
+ Result<StatsValue> statsMapResult = mFakeStatsMapA.readValue(tagStatsMapKey);
+ ASSERT_RESULT_OK(statsMapResult);
ASSERT_EQ((uint64_t)1, statsMapResult.value().rxPackets);
ASSERT_EQ((uint64_t)100, statsMapResult.value().rxBytes);
auto appStatsResult = mFakeAppUidStatsMap.readValue(TEST_UID);
- ASSERT_TRUE(isOk(appStatsResult));
+ ASSERT_RESULT_OK(appStatsResult);
ASSERT_EQ((uint64_t)1, appStatsResult.value().rxPackets);
ASSERT_EQ((uint64_t)100, appStatsResult.value().rxBytes);
}
@@ -521,12 +520,12 @@
StatsKey tagStatsMapKey;
populateFakeStats(cookie, uid, tag, &tagStatsMapKey);
ASSERT_EQ(0, mTc.deleteTagData(0, TEST_UID, callingUid));
- ASSERT_FALSE(isOk(mFakeCookieTagMap.readValue(cookie)));
- ASSERT_FALSE(isOk(mFakeUidCounterSetMap.readValue(uid)));
- ASSERT_FALSE(isOk(mFakeStatsMapA.readValue(tagStatsMapKey)));
+ ASSERT_FALSE(mFakeCookieTagMap.readValue(cookie).ok());
+ ASSERT_FALSE(mFakeUidCounterSetMap.readValue(uid).ok());
+ ASSERT_FALSE(mFakeStatsMapA.readValue(tagStatsMapKey).ok());
tagStatsMapKey.tag = 0;
- ASSERT_FALSE(isOk(mFakeStatsMapA.readValue(tagStatsMapKey)));
- ASSERT_FALSE(isOk(mFakeAppUidStatsMap.readValue(TEST_UID)));
+ ASSERT_FALSE(mFakeStatsMapA.readValue(tagStatsMapKey).ok());
+ ASSERT_FALSE(mFakeAppUidStatsMap.readValue(TEST_UID).ok());
}
TEST_F(TrafficControllerTest, TestDeleteDataWithTwoTags) {
@@ -544,17 +543,17 @@
populateFakeStats(cookie1, uid, tag1, &tagStatsMapKey1);
populateFakeStats(cookie2, uid, tag2, &tagStatsMapKey2);
ASSERT_EQ(0, mTc.deleteTagData(TEST_TAG, TEST_UID, callingUid));
- ASSERT_FALSE(isOk(mFakeCookieTagMap.readValue(cookie1)));
- StatusOr<UidTag> cookieMapResult = mFakeCookieTagMap.readValue(cookie2);
- ASSERT_TRUE(isOk(cookieMapResult));
+ ASSERT_FALSE(mFakeCookieTagMap.readValue(cookie1).ok());
+ Result<UidTagValue> cookieMapResult = mFakeCookieTagMap.readValue(cookie2);
+ ASSERT_RESULT_OK(cookieMapResult);
ASSERT_EQ(TEST_UID, cookieMapResult.value().uid);
ASSERT_EQ(TEST_TAG + 1, cookieMapResult.value().tag);
- StatusOr<uint8_t> counterSetResult = mFakeUidCounterSetMap.readValue(uid);
- ASSERT_TRUE(isOk(counterSetResult));
+ Result<uint8_t> counterSetResult = mFakeUidCounterSetMap.readValue(uid);
+ ASSERT_RESULT_OK(counterSetResult);
ASSERT_EQ(TEST_COUNTERSET, counterSetResult.value());
- ASSERT_FALSE(isOk(mFakeStatsMapA.readValue(tagStatsMapKey1)));
- StatusOr<StatsValue> statsMapResult = mFakeStatsMapA.readValue(tagStatsMapKey2);
- ASSERT_TRUE(isOk(statsMapResult));
+ ASSERT_FALSE(mFakeStatsMapA.readValue(tagStatsMapKey1).ok());
+ Result<StatsValue> statsMapResult = mFakeStatsMapA.readValue(tagStatsMapKey2);
+ ASSERT_RESULT_OK(statsMapResult);
ASSERT_EQ((uint64_t)1, statsMapResult.value().rxPackets);
ASSERT_EQ((uint64_t)100, statsMapResult.value().rxBytes);
}
@@ -577,29 +576,29 @@
// Delete the stats of one of the uid. Check if it is properly collected by
// removedStats.
ASSERT_EQ(0, mTc.deleteTagData(0, uid2, callingUid));
- ASSERT_FALSE(isOk(mFakeCookieTagMap.readValue(cookie2)));
- StatusOr<uint8_t> counterSetResult = mFakeUidCounterSetMap.readValue(uid1);
- ASSERT_TRUE(isOk(counterSetResult));
+ ASSERT_FALSE(mFakeCookieTagMap.readValue(cookie2).ok());
+ Result<uint8_t> counterSetResult = mFakeUidCounterSetMap.readValue(uid1);
+ ASSERT_RESULT_OK(counterSetResult);
ASSERT_EQ(TEST_COUNTERSET, counterSetResult.value());
- ASSERT_FALSE(isOk(mFakeUidCounterSetMap.readValue(uid2)));
- ASSERT_FALSE(isOk(mFakeStatsMapA.readValue(tagStatsMapKey2)));
+ ASSERT_FALSE(mFakeUidCounterSetMap.readValue(uid2).ok());
+ ASSERT_FALSE(mFakeStatsMapA.readValue(tagStatsMapKey2).ok());
tagStatsMapKey2.tag = 0;
- ASSERT_FALSE(isOk(mFakeStatsMapA.readValue(tagStatsMapKey2)));
- ASSERT_FALSE(isOk(mFakeAppUidStatsMap.readValue(uid2)));
+ ASSERT_FALSE(mFakeStatsMapA.readValue(tagStatsMapKey2).ok());
+ ASSERT_FALSE(mFakeAppUidStatsMap.readValue(uid2).ok());
tagStatsMapKey1.tag = 0;
- StatusOr<StatsValue> statsMapResult = mFakeStatsMapA.readValue(tagStatsMapKey1);
- ASSERT_TRUE(isOk(statsMapResult));
+ Result<StatsValue> statsMapResult = mFakeStatsMapA.readValue(tagStatsMapKey1);
+ ASSERT_RESULT_OK(statsMapResult);
ASSERT_EQ((uint64_t)1, statsMapResult.value().rxPackets);
ASSERT_EQ((uint64_t)100, statsMapResult.value().rxBytes);
auto appStatsResult = mFakeAppUidStatsMap.readValue(uid1);
- ASSERT_TRUE(isOk(appStatsResult));
+ ASSERT_RESULT_OK(appStatsResult);
ASSERT_EQ((uint64_t)1, appStatsResult.value().rxPackets);
ASSERT_EQ((uint64_t)100, appStatsResult.value().rxBytes);
// Delete the stats of the other uid.
ASSERT_EQ(0, mTc.deleteTagData(0, uid1, callingUid));
- ASSERT_FALSE(isOk(mFakeStatsMapA.readValue(tagStatsMapKey1)));
- ASSERT_FALSE(isOk(mFakeAppUidStatsMap.readValue(uid1)));
+ ASSERT_FALSE(mFakeStatsMapA.readValue(tagStatsMapKey1).ok());
+ ASSERT_FALSE(mFakeAppUidStatsMap.readValue(uid1).ok());
}
TEST_F(TrafficControllerTest, TestUpdateOwnerMapEntry) {
@@ -607,26 +606,26 @@
uint32_t uid = TEST_UID;
ASSERT_TRUE(isOk(mTc.updateOwnerMapEntry(STANDBY_MATCH, uid, DENY, BLACKLIST)));
- StatusOr<UidOwnerValue> value = mFakeUidOwnerMap.readValue(uid);
- ASSERT_TRUE(isOk(value));
+ Result<UidOwnerValue> value = mFakeUidOwnerMap.readValue(uid);
+ ASSERT_RESULT_OK(value);
ASSERT_TRUE(value.value().rule & STANDBY_MATCH);
ASSERT_TRUE(isOk(mTc.updateOwnerMapEntry(DOZABLE_MATCH, uid, ALLOW, WHITELIST)));
value = mFakeUidOwnerMap.readValue(uid);
- ASSERT_TRUE(isOk(value));
+ ASSERT_RESULT_OK(value);
ASSERT_TRUE(value.value().rule & DOZABLE_MATCH);
ASSERT_TRUE(isOk(mTc.updateOwnerMapEntry(DOZABLE_MATCH, uid, DENY, WHITELIST)));
value = mFakeUidOwnerMap.readValue(uid);
- ASSERT_TRUE(isOk(value));
+ ASSERT_RESULT_OK(value);
ASSERT_FALSE(value.value().rule & DOZABLE_MATCH);
ASSERT_TRUE(isOk(mTc.updateOwnerMapEntry(STANDBY_MATCH, uid, ALLOW, BLACKLIST)));
- ASSERT_FALSE(isOk(mFakeUidOwnerMap.readValue(uid)));
+ ASSERT_FALSE(mFakeUidOwnerMap.readValue(uid).ok());
uid = TEST_UID2;
ASSERT_FALSE(isOk(mTc.updateOwnerMapEntry(STANDBY_MATCH, uid, ALLOW, BLACKLIST)));
- ASSERT_FALSE(isOk(mFakeUidOwnerMap.readValue(uid)));
+ ASSERT_FALSE(mFakeUidOwnerMap.readValue(uid).ok());
}
TEST_F(TrafficControllerTest, TestChangeUidOwnerRule) {
@@ -705,7 +704,7 @@
// Remove the same UIDs from the blacklist and check the map is empty.
ASSERT_TRUE(isOk(mTc.updateUidOwnerMap(appStrUids, BandwidthController::IptJumpReject,
BandwidthController::IptOpDelete)));
- ASSERT_FALSE(isOk(mFakeUidOwnerMap.getFirstKey()));
+ ASSERT_FALSE(mFakeUidOwnerMap.getFirstKey().ok());
}
TEST_F(TrafficControllerTest, TestDeleteWrongMatchSilentlyFails) {
diff --git a/server/VirtualNetwork.h b/server/VirtualNetwork.h
index 69b14d8..20c8dee 100644
--- a/server/VirtualNetwork.h
+++ b/server/VirtualNetwork.h
@@ -14,16 +14,14 @@
* limitations under the License.
*/
-#ifndef NETD_SERVER_VIRTUAL_NETWORK_H
-#define NETD_SERVER_VIRTUAL_NETWORK_H
+#pragma once
#include <set>
#include "Network.h"
#include "UidRanges.h"
-namespace android {
-namespace net {
+namespace android::net {
// A VirtualNetwork may be "secure" or not.
//
@@ -40,15 +38,14 @@
bool isSecure() const;
bool appliesToUser(uid_t uid) const;
- 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;
+ [[nodiscard]] int addUsers(const UidRanges& uidRanges, const std::set<uid_t>& protectableUsers);
+ [[nodiscard]] int removeUsers(const UidRanges& uidRanges,
+ const std::set<uid_t>& protectableUsers);
-private:
+ private:
Type getType() const override;
- int addInterface(const std::string& interface) override WARN_UNUSED_RESULT;
- int removeInterface(const std::string& interface) override WARN_UNUSED_RESULT;
+ [[nodiscard]] int addInterface(const std::string& interface) override;
+ [[nodiscard]] int removeInterface(const std::string& interface) override;
int maybeCloseSockets(bool add, const UidRanges& uidRanges,
const std::set<uid_t>& protectableUsers);
@@ -56,7 +53,4 @@
UidRanges mUidRanges;
};
-} // namespace net
-} // namespace android
-
-#endif // NETD_SERVER_VIRTUAL_NETWORK_H
+} // namespace android::net
diff --git a/server/XfrmController.cpp b/server/XfrmController.cpp
index a57b492..33a2aa2 100644
--- a/server/XfrmController.cpp
+++ b/server/XfrmController.cpp
@@ -53,7 +53,6 @@
#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"
@@ -87,7 +86,7 @@
// Exposed for testing
constexpr uint32_t ALGO_MASK_AEAD_ALL = ~0;
// Exposed for testing
-constexpr uint8_t REPLAY_WINDOW_SIZE = 4;
+constexpr uint8_t REPLAY_WINDOW_SIZE = 32;
namespace {
@@ -1262,7 +1261,7 @@
return sizeof(*usersp);
}
-int XfrmController::fillUserTemplate(const XfrmSpInfo& record, xfrm_user_tmpl* tmpl) {
+void XfrmController::fillUserTemplate(const XfrmSpInfo& record, xfrm_user_tmpl* tmpl) {
tmpl->id.daddr = record.dstAddr;
tmpl->id.spi = record.spi;
tmpl->id.proto = IPPROTO_ESP;
@@ -1278,7 +1277,6 @@
// algos, we should find it and apply it.
// I can't find one.
tmpl->ealgos = ALGO_MASK_CRYPT_ALL; // TODO: if there's a bitmask somewhere...
- return sizeof(xfrm_user_tmpl*);
}
int XfrmController::fillNlAttrUserTemplate(const XfrmSpInfo& record, nlattr_user_tmpl* tmpl) {
diff --git a/server/XfrmController.h b/server/XfrmController.h
index a38bbeb..15eef3d 100644
--- a/server/XfrmController.h
+++ b/server/XfrmController.h
@@ -55,7 +55,8 @@
class XfrmSocket {
public:
- virtual void close() {
+ // called from destructor and thus cannot be virtual
+ void close() {
if (mSock >= 0) {
::close(mSock);
}
@@ -370,7 +371,7 @@
static netdutils::Status deleteSecurityAssociation(const XfrmCommonInfo& record,
const XfrmSocket& sock);
static int fillUserSaId(const XfrmCommonInfo& record, xfrm_usersa_id* said);
- static int fillUserTemplate(const XfrmSpInfo& record, xfrm_user_tmpl* tmpl);
+ static void fillUserTemplate(const XfrmSpInfo& record, xfrm_user_tmpl* tmpl);
static int fillUserSpInfo(const XfrmSpInfo& record, XfrmDirection direction,
xfrm_userpolicy_info* usersp);
diff --git a/server/XfrmControllerTest.cpp b/server/XfrmControllerTest.cpp
index 36af67a..e7f5cfc 100644
--- a/server/XfrmControllerTest.cpp
+++ b/server/XfrmControllerTest.cpp
@@ -19,7 +19,6 @@
#include <cerrno>
#include <cinttypes>
#include <cstdint>
-#include <cstdio>
#include <cstdlib>
#include <set>
#include <vector>
diff --git a/server/aidl_api/netd_aidl_interface/1/.hash b/server/aidl_api/netd_aidl_interface/1/.hash
new file mode 100644
index 0000000..d33e903
--- /dev/null
+++ b/server/aidl_api/netd_aidl_interface/1/.hash
@@ -0,0 +1 @@
+69c2ac134efbb31e9591d7e5c3640fb839e23bdb
diff --git a/server/aidl/netd/1/android/net/INetd.aidl b/server/aidl_api/netd_aidl_interface/1/android/net/INetd.aidl
similarity index 100%
rename from server/aidl/netd/1/android/net/INetd.aidl
rename to server/aidl_api/netd_aidl_interface/1/android/net/INetd.aidl
diff --git a/server/aidl/netd/1/android/net/INetdUnsolicitedEventListener.aidl b/server/aidl_api/netd_aidl_interface/1/android/net/INetdUnsolicitedEventListener.aidl
similarity index 100%
rename from server/aidl/netd/1/android/net/INetdUnsolicitedEventListener.aidl
rename to server/aidl_api/netd_aidl_interface/1/android/net/INetdUnsolicitedEventListener.aidl
diff --git a/server/aidl/netd/1/android/net/InterfaceConfigurationParcel.aidl b/server/aidl_api/netd_aidl_interface/1/android/net/InterfaceConfigurationParcel.aidl
similarity index 100%
rename from server/aidl/netd/1/android/net/InterfaceConfigurationParcel.aidl
rename to server/aidl_api/netd_aidl_interface/1/android/net/InterfaceConfigurationParcel.aidl
diff --git a/server/aidl/netd/1/android/net/TetherStatsParcel.aidl b/server/aidl_api/netd_aidl_interface/1/android/net/TetherStatsParcel.aidl
similarity index 100%
rename from server/aidl/netd/1/android/net/TetherStatsParcel.aidl
rename to server/aidl_api/netd_aidl_interface/1/android/net/TetherStatsParcel.aidl
diff --git a/server/aidl/netd/1/android/net/UidRangeParcel.aidl b/server/aidl_api/netd_aidl_interface/1/android/net/UidRangeParcel.aidl
similarity index 100%
rename from server/aidl/netd/1/android/net/UidRangeParcel.aidl
rename to server/aidl_api/netd_aidl_interface/1/android/net/UidRangeParcel.aidl
diff --git a/server/aidl_api/netd_aidl_interface/2/.hash b/server/aidl_api/netd_aidl_interface/2/.hash
new file mode 100644
index 0000000..5fc5b2d
--- /dev/null
+++ b/server/aidl_api/netd_aidl_interface/2/.hash
@@ -0,0 +1 @@
+e395d63302c47e7d2dac0d503045779029ff598b
diff --git a/server/aidl/netd/2/android/net/INetd.aidl b/server/aidl_api/netd_aidl_interface/2/android/net/INetd.aidl
similarity index 100%
rename from server/aidl/netd/2/android/net/INetd.aidl
rename to server/aidl_api/netd_aidl_interface/2/android/net/INetd.aidl
diff --git a/server/aidl/netd/2/android/net/INetdUnsolicitedEventListener.aidl b/server/aidl_api/netd_aidl_interface/2/android/net/INetdUnsolicitedEventListener.aidl
similarity index 100%
rename from server/aidl/netd/2/android/net/INetdUnsolicitedEventListener.aidl
rename to server/aidl_api/netd_aidl_interface/2/android/net/INetdUnsolicitedEventListener.aidl
diff --git a/server/aidl/netd/2/android/net/InterfaceConfigurationParcel.aidl b/server/aidl_api/netd_aidl_interface/2/android/net/InterfaceConfigurationParcel.aidl
similarity index 100%
rename from server/aidl/netd/2/android/net/InterfaceConfigurationParcel.aidl
rename to server/aidl_api/netd_aidl_interface/2/android/net/InterfaceConfigurationParcel.aidl
diff --git a/server/aidl/netd/2/android/net/TetherStatsParcel.aidl b/server/aidl_api/netd_aidl_interface/2/android/net/TetherStatsParcel.aidl
similarity index 100%
rename from server/aidl/netd/2/android/net/TetherStatsParcel.aidl
rename to server/aidl_api/netd_aidl_interface/2/android/net/TetherStatsParcel.aidl
diff --git a/server/aidl/netd/2/android/net/UidRangeParcel.aidl b/server/aidl_api/netd_aidl_interface/2/android/net/UidRangeParcel.aidl
similarity index 100%
rename from server/aidl/netd/2/android/net/UidRangeParcel.aidl
rename to server/aidl_api/netd_aidl_interface/2/android/net/UidRangeParcel.aidl
diff --git a/server/aidl_api/netd_aidl_interface/3/.hash b/server/aidl_api/netd_aidl_interface/3/.hash
new file mode 100644
index 0000000..59cf708
--- /dev/null
+++ b/server/aidl_api/netd_aidl_interface/3/.hash
@@ -0,0 +1 @@
+e17c1f9b2068b539b22e3a4a447edea3c80aee4b
diff --git a/server/aidl_api/netd_aidl_interface/3/android/net/INetd.aidl b/server/aidl_api/netd_aidl_interface/3/android/net/INetd.aidl
new file mode 100644
index 0000000..135b738
--- /dev/null
+++ b/server/aidl_api/netd_aidl_interface/3/android/net/INetd.aidl
@@ -0,0 +1,161 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a 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;
+/* @hide */
+interface INetd {
+ boolean isAlive();
+ boolean firewallReplaceUidChain(in @utf8InCpp String chainName, boolean isWhitelist, in int[] uids);
+ boolean bandwidthEnableDataSaver(boolean enable);
+ void networkCreatePhysical(int netId, int permission);
+ void networkCreateVpn(int netId, boolean secure);
+ void networkDestroy(int netId);
+ void networkAddInterface(int netId, in @utf8InCpp String iface);
+ void networkRemoveInterface(int netId, in @utf8InCpp String iface);
+ void networkAddUidRanges(int netId, in android.net.UidRangeParcel[] uidRanges);
+ void networkRemoveUidRanges(int netId, in android.net.UidRangeParcel[] uidRanges);
+ void networkRejectNonSecureVpn(boolean add, in android.net.UidRangeParcel[] uidRanges);
+ void socketDestroy(in android.net.UidRangeParcel[] uidRanges, in int[] exemptUids);
+ boolean tetherApplyDnsInterfaces();
+ android.net.TetherStatsParcel[] tetherGetStats();
+ void interfaceAddAddress(in @utf8InCpp String ifName, in @utf8InCpp String addrString, int prefixLength);
+ void interfaceDelAddress(in @utf8InCpp String ifName, in @utf8InCpp String addrString, int prefixLength);
+ @utf8InCpp String getProcSysNet(int ipversion, int which, in @utf8InCpp String ifname, in @utf8InCpp String parameter);
+ void setProcSysNet(int ipversion, int which, in @utf8InCpp String ifname, in @utf8InCpp String parameter, in @utf8InCpp String value);
+ void ipSecSetEncapSocketOwner(in ParcelFileDescriptor socket, int newUid);
+ int ipSecAllocateSpi(int transformId, in @utf8InCpp String sourceAddress, in @utf8InCpp String destinationAddress, int spi);
+ void ipSecAddSecurityAssociation(int transformId, int mode, in @utf8InCpp String sourceAddress, in @utf8InCpp String destinationAddress, int underlyingNetId, int spi, int markValue, int markMask, in @utf8InCpp String authAlgo, in byte[] authKey, in int authTruncBits, in @utf8InCpp String cryptAlgo, in byte[] cryptKey, in int cryptTruncBits, in @utf8InCpp String aeadAlgo, in byte[] aeadKey, in int aeadIcvBits, int encapType, int encapLocalPort, int encapRemotePort, int interfaceId);
+ void ipSecDeleteSecurityAssociation(int transformId, in @utf8InCpp String sourceAddress, in @utf8InCpp String destinationAddress, int spi, int markValue, int markMask, int interfaceId);
+ void ipSecApplyTransportModeTransform(in ParcelFileDescriptor socket, int transformId, int direction, in @utf8InCpp String sourceAddress, in @utf8InCpp String destinationAddress, int spi);
+ void ipSecRemoveTransportModeTransform(in ParcelFileDescriptor socket);
+ void ipSecAddSecurityPolicy(int transformId, int selAddrFamily, int direction, in @utf8InCpp String tmplSrcAddress, in @utf8InCpp String tmplDstAddress, int spi, int markValue, int markMask, int interfaceId);
+ void ipSecUpdateSecurityPolicy(int transformId, int selAddrFamily, int direction, in @utf8InCpp String tmplSrcAddress, in @utf8InCpp String tmplDstAddress, int spi, int markValue, int markMask, int interfaceId);
+ void ipSecDeleteSecurityPolicy(int transformId, int selAddrFamily, int direction, int markValue, int markMask, int interfaceId);
+ void ipSecAddTunnelInterface(in @utf8InCpp String deviceName, in @utf8InCpp String localAddress, in @utf8InCpp String remoteAddress, int iKey, int oKey, int interfaceId);
+ void ipSecUpdateTunnelInterface(in @utf8InCpp String deviceName, in @utf8InCpp String localAddress, in @utf8InCpp String remoteAddress, int iKey, int oKey, int interfaceId);
+ void ipSecRemoveTunnelInterface(in @utf8InCpp String deviceName);
+ void wakeupAddInterface(in @utf8InCpp String ifName, in @utf8InCpp String prefix, int mark, int mask);
+ void wakeupDelInterface(in @utf8InCpp String ifName, in @utf8InCpp String prefix, int mark, int mask);
+ void setIPv6AddrGenMode(in @utf8InCpp String ifName, int mode);
+ void idletimerAddInterface(in @utf8InCpp String ifName, int timeout, in @utf8InCpp String classLabel);
+ void idletimerRemoveInterface(in @utf8InCpp String ifName, int timeout, in @utf8InCpp String classLabel);
+ void strictUidCleartextPenalty(int uid, int policyPenalty);
+ @utf8InCpp String clatdStart(in @utf8InCpp String ifName, in @utf8InCpp String nat64Prefix);
+ void clatdStop(in @utf8InCpp String ifName);
+ boolean ipfwdEnabled();
+ @utf8InCpp String[] ipfwdGetRequesterList();
+ void ipfwdEnableForwarding(in @utf8InCpp String requester);
+ void ipfwdDisableForwarding(in @utf8InCpp String requester);
+ void ipfwdAddInterfaceForward(in @utf8InCpp String fromIface, in @utf8InCpp String toIface);
+ void ipfwdRemoveInterfaceForward(in @utf8InCpp String fromIface, in @utf8InCpp String toIface);
+ void bandwidthSetInterfaceQuota(in @utf8InCpp String ifName, long bytes);
+ void bandwidthRemoveInterfaceQuota(in @utf8InCpp String ifName);
+ void bandwidthSetInterfaceAlert(in @utf8InCpp String ifName, long bytes);
+ void bandwidthRemoveInterfaceAlert(in @utf8InCpp String ifName);
+ void bandwidthSetGlobalAlert(long bytes);
+ void bandwidthAddNaughtyApp(int uid);
+ void bandwidthRemoveNaughtyApp(int uid);
+ void bandwidthAddNiceApp(int uid);
+ void bandwidthRemoveNiceApp(int uid);
+ void tetherStart(in @utf8InCpp String[] dhcpRanges);
+ void tetherStop();
+ boolean tetherIsEnabled();
+ void tetherInterfaceAdd(in @utf8InCpp String ifName);
+ void tetherInterfaceRemove(in @utf8InCpp String ifName);
+ @utf8InCpp String[] tetherInterfaceList();
+ void tetherDnsSet(int netId, in @utf8InCpp String[] dnsAddrs);
+ @utf8InCpp String[] tetherDnsList();
+ void networkAddRoute(int netId, in @utf8InCpp String ifName, in @utf8InCpp String destination, in @utf8InCpp String nextHop);
+ void networkRemoveRoute(int netId, in @utf8InCpp String ifName, in @utf8InCpp String destination, in @utf8InCpp String nextHop);
+ void networkAddLegacyRoute(int netId, in @utf8InCpp String ifName, in @utf8InCpp String destination, in @utf8InCpp String nextHop, int uid);
+ void networkRemoveLegacyRoute(int netId, in @utf8InCpp String ifName, in @utf8InCpp String destination, in @utf8InCpp String nextHop, int uid);
+ int networkGetDefault();
+ void networkSetDefault(int netId);
+ void networkClearDefault();
+ void networkSetPermissionForNetwork(int netId, int permission);
+ void networkSetPermissionForUser(int permission, in int[] uids);
+ void networkClearPermissionForUser(in int[] uids);
+ void trafficSetNetPermForUids(int permission, in int[] uids);
+ void networkSetProtectAllow(int uid);
+ void networkSetProtectDeny(int uid);
+ boolean networkCanProtect(int uid);
+ void firewallSetFirewallType(int firewalltype);
+ void firewallSetInterfaceRule(in @utf8InCpp String ifName, int firewallRule);
+ void firewallSetUidRule(int childChain, int uid, int firewallRule);
+ void firewallEnableChildChain(int childChain, boolean enable);
+ @utf8InCpp String[] interfaceGetList();
+ android.net.InterfaceConfigurationParcel interfaceGetCfg(in @utf8InCpp String ifName);
+ void interfaceSetCfg(in android.net.InterfaceConfigurationParcel cfg);
+ void interfaceSetIPv6PrivacyExtensions(in @utf8InCpp String ifName, boolean enable);
+ void interfaceClearAddrs(in @utf8InCpp String ifName);
+ void interfaceSetEnableIPv6(in @utf8InCpp String ifName, boolean enable);
+ void interfaceSetMtu(in @utf8InCpp String ifName, int mtu);
+ void tetherAddForward(in @utf8InCpp String intIface, in @utf8InCpp String extIface);
+ void tetherRemoveForward(in @utf8InCpp String intIface, in @utf8InCpp String extIface);
+ void setTcpRWmemorySize(in @utf8InCpp String rmemValues, in @utf8InCpp String wmemValues);
+ void registerUnsolicitedEventListener(android.net.INetdUnsolicitedEventListener listener);
+ void firewallAddUidInterfaceRules(in @utf8InCpp String ifName, in int[] uids);
+ void firewallRemoveUidInterfaceRules(in int[] uids);
+ void trafficSwapActiveStatsMap();
+ IBinder getOemNetd();
+ void tetherStartWithConfiguration(in android.net.TetherConfigParcel config);
+ android.net.MarkMaskParcel getFwmarkForNetwork(int netId);
+ void networkAddRouteParcel(int netId, in android.net.RouteInfoParcel routeInfo);
+ void networkUpdateRouteParcel(int netId, in android.net.RouteInfoParcel routeInfo);
+ void networkRemoveRouteParcel(int netId, in android.net.RouteInfoParcel routeInfo);
+ void tetherOffloadRuleAdd(in android.net.TetherOffloadRuleParcel rule);
+ void tetherOffloadRuleRemove(in android.net.TetherOffloadRuleParcel rule);
+ const int IPV4 = 4;
+ const int IPV6 = 6;
+ const int CONF = 1;
+ const int NEIGH = 2;
+ const String IPSEC_INTERFACE_PREFIX = "ipsec";
+ const int IPV6_ADDR_GEN_MODE_EUI64 = 0;
+ const int IPV6_ADDR_GEN_MODE_NONE = 1;
+ const int IPV6_ADDR_GEN_MODE_STABLE_PRIVACY = 2;
+ const int IPV6_ADDR_GEN_MODE_RANDOM = 3;
+ const int IPV6_ADDR_GEN_MODE_DEFAULT = 0;
+ const int PENALTY_POLICY_ACCEPT = 1;
+ const int PENALTY_POLICY_LOG = 2;
+ const int PENALTY_POLICY_REJECT = 3;
+ const int LOCAL_NET_ID = 99;
+ const String NEXTHOP_NONE = "";
+ const String NEXTHOP_UNREACHABLE = "unreachable";
+ const String NEXTHOP_THROW = "throw";
+ const int PERMISSION_NONE = 0;
+ const int PERMISSION_NETWORK = 1;
+ const int PERMISSION_SYSTEM = 2;
+ const int NO_PERMISSIONS = 0;
+ const int PERMISSION_INTERNET = 4;
+ const int PERMISSION_UPDATE_DEVICE_STATS = 8;
+ const int PERMISSION_UNINSTALLED = -1;
+ const int FIREWALL_WHITELIST = 0;
+ const int FIREWALL_BLACKLIST = 1;
+ const int FIREWALL_RULE_ALLOW = 1;
+ const int FIREWALL_RULE_DENY = 2;
+ const int FIREWALL_CHAIN_NONE = 0;
+ const int FIREWALL_CHAIN_DOZABLE = 1;
+ const int FIREWALL_CHAIN_STANDBY = 2;
+ const int FIREWALL_CHAIN_POWERSAVE = 3;
+ const String IF_STATE_UP = "up";
+ const String IF_STATE_DOWN = "down";
+ const String IF_FLAG_BROADCAST = "broadcast";
+ const String IF_FLAG_LOOPBACK = "loopback";
+ const String IF_FLAG_POINTOPOINT = "point-to-point";
+ const String IF_FLAG_RUNNING = "running";
+ const String IF_FLAG_MULTICAST = "multicast";
+}
diff --git a/server/aidl_api/netd_aidl_interface/3/android/net/INetdUnsolicitedEventListener.aidl b/server/aidl_api/netd_aidl_interface/3/android/net/INetdUnsolicitedEventListener.aidl
new file mode 100644
index 0000000..4459363
--- /dev/null
+++ b/server/aidl_api/netd_aidl_interface/3/android/net/INetdUnsolicitedEventListener.aidl
@@ -0,0 +1,32 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a 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;
+/* @hide */
+interface INetdUnsolicitedEventListener {
+ oneway void onInterfaceClassActivityChanged(boolean isActive, int timerLabel, long timestampNs, int uid);
+ oneway void onQuotaLimitReached(@utf8InCpp String alertName, @utf8InCpp String ifName);
+ oneway void onInterfaceDnsServerInfo(@utf8InCpp String ifName, long lifetimeS, in @utf8InCpp String[] servers);
+ oneway void onInterfaceAddressUpdated(@utf8InCpp String addr, @utf8InCpp String ifName, int flags, int scope);
+ oneway void onInterfaceAddressRemoved(@utf8InCpp String addr, @utf8InCpp String ifName, int flags, int scope);
+ oneway void onInterfaceAdded(@utf8InCpp String ifName);
+ oneway void onInterfaceRemoved(@utf8InCpp String ifName);
+ oneway void onInterfaceChanged(@utf8InCpp String ifName, boolean up);
+ oneway void onInterfaceLinkStateChanged(@utf8InCpp String ifName, boolean up);
+ oneway void onRouteChanged(boolean updated, @utf8InCpp String route, @utf8InCpp String gateway, @utf8InCpp String ifName);
+ oneway void onStrictCleartextDetected(int uid, @utf8InCpp String hex);
+}
diff --git a/server/aidl_api/netd_aidl_interface/3/android/net/InterfaceConfigurationParcel.aidl b/server/aidl_api/netd_aidl_interface/3/android/net/InterfaceConfigurationParcel.aidl
new file mode 100644
index 0000000..01e0f95
--- /dev/null
+++ b/server/aidl_api/netd_aidl_interface/3/android/net/InterfaceConfigurationParcel.aidl
@@ -0,0 +1,26 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a 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;
+/* @hide */
+parcelable InterfaceConfigurationParcel {
+ @utf8InCpp String ifName;
+ @utf8InCpp String hwAddr;
+ @utf8InCpp String ipv4Addr;
+ int prefixLength;
+ @utf8InCpp String[] flags;
+}
diff --git a/server/aidl_api/netd_aidl_interface/3/android/net/MarkMaskParcel.aidl b/server/aidl_api/netd_aidl_interface/3/android/net/MarkMaskParcel.aidl
new file mode 100644
index 0000000..62be838
--- /dev/null
+++ b/server/aidl_api/netd_aidl_interface/3/android/net/MarkMaskParcel.aidl
@@ -0,0 +1,23 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a 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;
+/* @hide */
+parcelable MarkMaskParcel {
+ int mark;
+ int mask;
+}
diff --git a/server/aidl_api/netd_aidl_interface/3/android/net/RouteInfoParcel.aidl b/server/aidl_api/netd_aidl_interface/3/android/net/RouteInfoParcel.aidl
new file mode 100644
index 0000000..5e0ee62
--- /dev/null
+++ b/server/aidl_api/netd_aidl_interface/3/android/net/RouteInfoParcel.aidl
@@ -0,0 +1,24 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a 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 RouteInfoParcel {
+ @utf8InCpp String destination;
+ @utf8InCpp String ifName;
+ @utf8InCpp String nextHop;
+ int mtu;
+}
diff --git a/server/aidl_api/netd_aidl_interface/3/android/net/TetherConfigParcel.aidl b/server/aidl_api/netd_aidl_interface/3/android/net/TetherConfigParcel.aidl
new file mode 100644
index 0000000..b136454
--- /dev/null
+++ b/server/aidl_api/netd_aidl_interface/3/android/net/TetherConfigParcel.aidl
@@ -0,0 +1,23 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a 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;
+/* @hide */
+parcelable TetherConfigParcel {
+ boolean usingLegacyDnsProxy;
+ @utf8InCpp String[] dhcpRanges;
+}
diff --git a/server/aidl_api/netd_aidl_interface/3/android/net/TetherOffloadRuleParcel.aidl b/server/aidl_api/netd_aidl_interface/3/android/net/TetherOffloadRuleParcel.aidl
new file mode 100644
index 0000000..3abf0f8
--- /dev/null
+++ b/server/aidl_api/netd_aidl_interface/3/android/net/TetherOffloadRuleParcel.aidl
@@ -0,0 +1,27 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a 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;
+/* @hide */
+parcelable TetherOffloadRuleParcel {
+ int inputInterfaceIndex;
+ int outputInterfaceIndex;
+ byte[] destination;
+ int prefixLength;
+ byte[] srcL2Address;
+ byte[] dstL2Address;
+}
diff --git a/server/aidl_api/netd_aidl_interface/3/android/net/TetherStatsParcel.aidl b/server/aidl_api/netd_aidl_interface/3/android/net/TetherStatsParcel.aidl
new file mode 100644
index 0000000..71ffb9b
--- /dev/null
+++ b/server/aidl_api/netd_aidl_interface/3/android/net/TetherStatsParcel.aidl
@@ -0,0 +1,26 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a 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;
+/* @hide */
+parcelable TetherStatsParcel {
+ @utf8InCpp String iface;
+ long rxBytes;
+ long rxPackets;
+ long txBytes;
+ long txPackets;
+}
diff --git a/server/aidl_api/netd_aidl_interface/3/android/net/UidRangeParcel.aidl b/server/aidl_api/netd_aidl_interface/3/android/net/UidRangeParcel.aidl
new file mode 100644
index 0000000..84ff457
--- /dev/null
+++ b/server/aidl_api/netd_aidl_interface/3/android/net/UidRangeParcel.aidl
@@ -0,0 +1,23 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a 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;
+/* @hide */
+parcelable UidRangeParcel {
+ int start;
+ int stop;
+}
diff --git a/server/aidl_api/netd_aidl_interface/4/.hash b/server/aidl_api/netd_aidl_interface/4/.hash
new file mode 100644
index 0000000..0c3f810
--- /dev/null
+++ b/server/aidl_api/netd_aidl_interface/4/.hash
@@ -0,0 +1 @@
+63adaa5098e4d8621e90c5a84f7cb93505c79311
diff --git a/server/aidl_api/netd_aidl_interface/4/android/net/INetd.aidl b/server/aidl_api/netd_aidl_interface/4/android/net/INetd.aidl
new file mode 100644
index 0000000..47e2931
--- /dev/null
+++ b/server/aidl_api/netd_aidl_interface/4/android/net/INetd.aidl
@@ -0,0 +1,164 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a 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;
+/* @hide */
+interface INetd {
+ boolean isAlive();
+ boolean firewallReplaceUidChain(in @utf8InCpp String chainName, boolean isWhitelist, in int[] uids);
+ boolean bandwidthEnableDataSaver(boolean enable);
+ void networkCreatePhysical(int netId, int permission);
+ void networkCreateVpn(int netId, boolean secure);
+ void networkDestroy(int netId);
+ void networkAddInterface(int netId, in @utf8InCpp String iface);
+ void networkRemoveInterface(int netId, in @utf8InCpp String iface);
+ void networkAddUidRanges(int netId, in android.net.UidRangeParcel[] uidRanges);
+ void networkRemoveUidRanges(int netId, in android.net.UidRangeParcel[] uidRanges);
+ void networkRejectNonSecureVpn(boolean add, in android.net.UidRangeParcel[] uidRanges);
+ void socketDestroy(in android.net.UidRangeParcel[] uidRanges, in int[] exemptUids);
+ boolean tetherApplyDnsInterfaces();
+ android.net.TetherStatsParcel[] tetherGetStats();
+ void interfaceAddAddress(in @utf8InCpp String ifName, in @utf8InCpp String addrString, int prefixLength);
+ void interfaceDelAddress(in @utf8InCpp String ifName, in @utf8InCpp String addrString, int prefixLength);
+ @utf8InCpp String getProcSysNet(int ipversion, int which, in @utf8InCpp String ifname, in @utf8InCpp String parameter);
+ void setProcSysNet(int ipversion, int which, in @utf8InCpp String ifname, in @utf8InCpp String parameter, in @utf8InCpp String value);
+ void ipSecSetEncapSocketOwner(in ParcelFileDescriptor socket, int newUid);
+ int ipSecAllocateSpi(int transformId, in @utf8InCpp String sourceAddress, in @utf8InCpp String destinationAddress, int spi);
+ void ipSecAddSecurityAssociation(int transformId, int mode, in @utf8InCpp String sourceAddress, in @utf8InCpp String destinationAddress, int underlyingNetId, int spi, int markValue, int markMask, in @utf8InCpp String authAlgo, in byte[] authKey, in int authTruncBits, in @utf8InCpp String cryptAlgo, in byte[] cryptKey, in int cryptTruncBits, in @utf8InCpp String aeadAlgo, in byte[] aeadKey, in int aeadIcvBits, int encapType, int encapLocalPort, int encapRemotePort, int interfaceId);
+ void ipSecDeleteSecurityAssociation(int transformId, in @utf8InCpp String sourceAddress, in @utf8InCpp String destinationAddress, int spi, int markValue, int markMask, int interfaceId);
+ void ipSecApplyTransportModeTransform(in ParcelFileDescriptor socket, int transformId, int direction, in @utf8InCpp String sourceAddress, in @utf8InCpp String destinationAddress, int spi);
+ void ipSecRemoveTransportModeTransform(in ParcelFileDescriptor socket);
+ void ipSecAddSecurityPolicy(int transformId, int selAddrFamily, int direction, in @utf8InCpp String tmplSrcAddress, in @utf8InCpp String tmplDstAddress, int spi, int markValue, int markMask, int interfaceId);
+ void ipSecUpdateSecurityPolicy(int transformId, int selAddrFamily, int direction, in @utf8InCpp String tmplSrcAddress, in @utf8InCpp String tmplDstAddress, int spi, int markValue, int markMask, int interfaceId);
+ void ipSecDeleteSecurityPolicy(int transformId, int selAddrFamily, int direction, int markValue, int markMask, int interfaceId);
+ void ipSecAddTunnelInterface(in @utf8InCpp String deviceName, in @utf8InCpp String localAddress, in @utf8InCpp String remoteAddress, int iKey, int oKey, int interfaceId);
+ void ipSecUpdateTunnelInterface(in @utf8InCpp String deviceName, in @utf8InCpp String localAddress, in @utf8InCpp String remoteAddress, int iKey, int oKey, int interfaceId);
+ void ipSecRemoveTunnelInterface(in @utf8InCpp String deviceName);
+ void wakeupAddInterface(in @utf8InCpp String ifName, in @utf8InCpp String prefix, int mark, int mask);
+ void wakeupDelInterface(in @utf8InCpp String ifName, in @utf8InCpp String prefix, int mark, int mask);
+ void setIPv6AddrGenMode(in @utf8InCpp String ifName, int mode);
+ void idletimerAddInterface(in @utf8InCpp String ifName, int timeout, in @utf8InCpp String classLabel);
+ void idletimerRemoveInterface(in @utf8InCpp String ifName, int timeout, in @utf8InCpp String classLabel);
+ void strictUidCleartextPenalty(int uid, int policyPenalty);
+ @utf8InCpp String clatdStart(in @utf8InCpp String ifName, in @utf8InCpp String nat64Prefix);
+ void clatdStop(in @utf8InCpp String ifName);
+ boolean ipfwdEnabled();
+ @utf8InCpp String[] ipfwdGetRequesterList();
+ void ipfwdEnableForwarding(in @utf8InCpp String requester);
+ void ipfwdDisableForwarding(in @utf8InCpp String requester);
+ void ipfwdAddInterfaceForward(in @utf8InCpp String fromIface, in @utf8InCpp String toIface);
+ void ipfwdRemoveInterfaceForward(in @utf8InCpp String fromIface, in @utf8InCpp String toIface);
+ void bandwidthSetInterfaceQuota(in @utf8InCpp String ifName, long bytes);
+ void bandwidthRemoveInterfaceQuota(in @utf8InCpp String ifName);
+ void bandwidthSetInterfaceAlert(in @utf8InCpp String ifName, long bytes);
+ void bandwidthRemoveInterfaceAlert(in @utf8InCpp String ifName);
+ void bandwidthSetGlobalAlert(long bytes);
+ void bandwidthAddNaughtyApp(int uid);
+ void bandwidthRemoveNaughtyApp(int uid);
+ void bandwidthAddNiceApp(int uid);
+ void bandwidthRemoveNiceApp(int uid);
+ void tetherStart(in @utf8InCpp String[] dhcpRanges);
+ void tetherStop();
+ boolean tetherIsEnabled();
+ void tetherInterfaceAdd(in @utf8InCpp String ifName);
+ void tetherInterfaceRemove(in @utf8InCpp String ifName);
+ @utf8InCpp String[] tetherInterfaceList();
+ void tetherDnsSet(int netId, in @utf8InCpp String[] dnsAddrs);
+ @utf8InCpp String[] tetherDnsList();
+ void networkAddRoute(int netId, in @utf8InCpp String ifName, in @utf8InCpp String destination, in @utf8InCpp String nextHop);
+ void networkRemoveRoute(int netId, in @utf8InCpp String ifName, in @utf8InCpp String destination, in @utf8InCpp String nextHop);
+ void networkAddLegacyRoute(int netId, in @utf8InCpp String ifName, in @utf8InCpp String destination, in @utf8InCpp String nextHop, int uid);
+ void networkRemoveLegacyRoute(int netId, in @utf8InCpp String ifName, in @utf8InCpp String destination, in @utf8InCpp String nextHop, int uid);
+ int networkGetDefault();
+ void networkSetDefault(int netId);
+ void networkClearDefault();
+ void networkSetPermissionForNetwork(int netId, int permission);
+ void networkSetPermissionForUser(int permission, in int[] uids);
+ void networkClearPermissionForUser(in int[] uids);
+ void trafficSetNetPermForUids(int permission, in int[] uids);
+ void networkSetProtectAllow(int uid);
+ void networkSetProtectDeny(int uid);
+ boolean networkCanProtect(int uid);
+ void firewallSetFirewallType(int firewalltype);
+ void firewallSetInterfaceRule(in @utf8InCpp String ifName, int firewallRule);
+ void firewallSetUidRule(int childChain, int uid, int firewallRule);
+ void firewallEnableChildChain(int childChain, boolean enable);
+ @utf8InCpp String[] interfaceGetList();
+ android.net.InterfaceConfigurationParcel interfaceGetCfg(in @utf8InCpp String ifName);
+ void interfaceSetCfg(in android.net.InterfaceConfigurationParcel cfg);
+ void interfaceSetIPv6PrivacyExtensions(in @utf8InCpp String ifName, boolean enable);
+ void interfaceClearAddrs(in @utf8InCpp String ifName);
+ void interfaceSetEnableIPv6(in @utf8InCpp String ifName, boolean enable);
+ void interfaceSetMtu(in @utf8InCpp String ifName, int mtu);
+ void tetherAddForward(in @utf8InCpp String intIface, in @utf8InCpp String extIface);
+ void tetherRemoveForward(in @utf8InCpp String intIface, in @utf8InCpp String extIface);
+ void setTcpRWmemorySize(in @utf8InCpp String rmemValues, in @utf8InCpp String wmemValues);
+ void registerUnsolicitedEventListener(android.net.INetdUnsolicitedEventListener listener);
+ void firewallAddUidInterfaceRules(in @utf8InCpp String ifName, in int[] uids);
+ void firewallRemoveUidInterfaceRules(in int[] uids);
+ void trafficSwapActiveStatsMap();
+ IBinder getOemNetd();
+ void tetherStartWithConfiguration(in android.net.TetherConfigParcel config);
+ android.net.MarkMaskParcel getFwmarkForNetwork(int netId);
+ void networkAddRouteParcel(int netId, in android.net.RouteInfoParcel routeInfo);
+ void networkUpdateRouteParcel(int netId, in android.net.RouteInfoParcel routeInfo);
+ void networkRemoveRouteParcel(int netId, in android.net.RouteInfoParcel routeInfo);
+ void tetherOffloadRuleAdd(in android.net.TetherOffloadRuleParcel rule);
+ void tetherOffloadRuleRemove(in android.net.TetherOffloadRuleParcel rule);
+ android.net.TetherStatsParcel[] tetherOffloadGetStats();
+ void tetherOffloadSetInterfaceQuota(int ifIndex, long quotaBytes);
+ android.net.TetherStatsParcel tetherOffloadGetAndClearStats(int ifIndex);
+ const int IPV4 = 4;
+ const int IPV6 = 6;
+ const int CONF = 1;
+ const int NEIGH = 2;
+ const String IPSEC_INTERFACE_PREFIX = "ipsec";
+ const int IPV6_ADDR_GEN_MODE_EUI64 = 0;
+ const int IPV6_ADDR_GEN_MODE_NONE = 1;
+ const int IPV6_ADDR_GEN_MODE_STABLE_PRIVACY = 2;
+ const int IPV6_ADDR_GEN_MODE_RANDOM = 3;
+ const int IPV6_ADDR_GEN_MODE_DEFAULT = 0;
+ const int PENALTY_POLICY_ACCEPT = 1;
+ const int PENALTY_POLICY_LOG = 2;
+ const int PENALTY_POLICY_REJECT = 3;
+ const int LOCAL_NET_ID = 99;
+ const String NEXTHOP_NONE = "";
+ const String NEXTHOP_UNREACHABLE = "unreachable";
+ const String NEXTHOP_THROW = "throw";
+ const int PERMISSION_NONE = 0;
+ const int PERMISSION_NETWORK = 1;
+ const int PERMISSION_SYSTEM = 2;
+ const int NO_PERMISSIONS = 0;
+ const int PERMISSION_INTERNET = 4;
+ const int PERMISSION_UPDATE_DEVICE_STATS = 8;
+ const int PERMISSION_UNINSTALLED = -1;
+ const int FIREWALL_WHITELIST = 0;
+ const int FIREWALL_BLACKLIST = 1;
+ const int FIREWALL_RULE_ALLOW = 1;
+ const int FIREWALL_RULE_DENY = 2;
+ const int FIREWALL_CHAIN_NONE = 0;
+ const int FIREWALL_CHAIN_DOZABLE = 1;
+ const int FIREWALL_CHAIN_STANDBY = 2;
+ const int FIREWALL_CHAIN_POWERSAVE = 3;
+ const String IF_STATE_UP = "up";
+ const String IF_STATE_DOWN = "down";
+ const String IF_FLAG_BROADCAST = "broadcast";
+ const String IF_FLAG_LOOPBACK = "loopback";
+ const String IF_FLAG_POINTOPOINT = "point-to-point";
+ const String IF_FLAG_RUNNING = "running";
+ const String IF_FLAG_MULTICAST = "multicast";
+}
diff --git a/server/aidl_api/netd_aidl_interface/4/android/net/INetdUnsolicitedEventListener.aidl b/server/aidl_api/netd_aidl_interface/4/android/net/INetdUnsolicitedEventListener.aidl
new file mode 100644
index 0000000..4459363
--- /dev/null
+++ b/server/aidl_api/netd_aidl_interface/4/android/net/INetdUnsolicitedEventListener.aidl
@@ -0,0 +1,32 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a 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;
+/* @hide */
+interface INetdUnsolicitedEventListener {
+ oneway void onInterfaceClassActivityChanged(boolean isActive, int timerLabel, long timestampNs, int uid);
+ oneway void onQuotaLimitReached(@utf8InCpp String alertName, @utf8InCpp String ifName);
+ oneway void onInterfaceDnsServerInfo(@utf8InCpp String ifName, long lifetimeS, in @utf8InCpp String[] servers);
+ oneway void onInterfaceAddressUpdated(@utf8InCpp String addr, @utf8InCpp String ifName, int flags, int scope);
+ oneway void onInterfaceAddressRemoved(@utf8InCpp String addr, @utf8InCpp String ifName, int flags, int scope);
+ oneway void onInterfaceAdded(@utf8InCpp String ifName);
+ oneway void onInterfaceRemoved(@utf8InCpp String ifName);
+ oneway void onInterfaceChanged(@utf8InCpp String ifName, boolean up);
+ oneway void onInterfaceLinkStateChanged(@utf8InCpp String ifName, boolean up);
+ oneway void onRouteChanged(boolean updated, @utf8InCpp String route, @utf8InCpp String gateway, @utf8InCpp String ifName);
+ oneway void onStrictCleartextDetected(int uid, @utf8InCpp String hex);
+}
diff --git a/server/aidl_api/netd_aidl_interface/4/android/net/InterfaceConfigurationParcel.aidl b/server/aidl_api/netd_aidl_interface/4/android/net/InterfaceConfigurationParcel.aidl
new file mode 100644
index 0000000..01e0f95
--- /dev/null
+++ b/server/aidl_api/netd_aidl_interface/4/android/net/InterfaceConfigurationParcel.aidl
@@ -0,0 +1,26 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a 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;
+/* @hide */
+parcelable InterfaceConfigurationParcel {
+ @utf8InCpp String ifName;
+ @utf8InCpp String hwAddr;
+ @utf8InCpp String ipv4Addr;
+ int prefixLength;
+ @utf8InCpp String[] flags;
+}
diff --git a/server/aidl_api/netd_aidl_interface/4/android/net/MarkMaskParcel.aidl b/server/aidl_api/netd_aidl_interface/4/android/net/MarkMaskParcel.aidl
new file mode 100644
index 0000000..62be838
--- /dev/null
+++ b/server/aidl_api/netd_aidl_interface/4/android/net/MarkMaskParcel.aidl
@@ -0,0 +1,23 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a 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;
+/* @hide */
+parcelable MarkMaskParcel {
+ int mark;
+ int mask;
+}
diff --git a/server/aidl_api/netd_aidl_interface/4/android/net/RouteInfoParcel.aidl b/server/aidl_api/netd_aidl_interface/4/android/net/RouteInfoParcel.aidl
new file mode 100644
index 0000000..5e0ee62
--- /dev/null
+++ b/server/aidl_api/netd_aidl_interface/4/android/net/RouteInfoParcel.aidl
@@ -0,0 +1,24 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a 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 RouteInfoParcel {
+ @utf8InCpp String destination;
+ @utf8InCpp String ifName;
+ @utf8InCpp String nextHop;
+ int mtu;
+}
diff --git a/server/aidl_api/netd_aidl_interface/4/android/net/TetherConfigParcel.aidl b/server/aidl_api/netd_aidl_interface/4/android/net/TetherConfigParcel.aidl
new file mode 100644
index 0000000..b136454
--- /dev/null
+++ b/server/aidl_api/netd_aidl_interface/4/android/net/TetherConfigParcel.aidl
@@ -0,0 +1,23 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a 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;
+/* @hide */
+parcelable TetherConfigParcel {
+ boolean usingLegacyDnsProxy;
+ @utf8InCpp String[] dhcpRanges;
+}
diff --git a/server/aidl_api/netd_aidl_interface/4/android/net/TetherOffloadRuleParcel.aidl b/server/aidl_api/netd_aidl_interface/4/android/net/TetherOffloadRuleParcel.aidl
new file mode 100644
index 0000000..c9d8458
--- /dev/null
+++ b/server/aidl_api/netd_aidl_interface/4/android/net/TetherOffloadRuleParcel.aidl
@@ -0,0 +1,28 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a 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;
+/* @hide */
+parcelable TetherOffloadRuleParcel {
+ int inputInterfaceIndex;
+ int outputInterfaceIndex;
+ byte[] destination;
+ int prefixLength;
+ byte[] srcL2Address;
+ byte[] dstL2Address;
+ int pmtu = 1500;
+}
diff --git a/server/aidl_api/netd_aidl_interface/4/android/net/TetherStatsParcel.aidl b/server/aidl_api/netd_aidl_interface/4/android/net/TetherStatsParcel.aidl
new file mode 100644
index 0000000..0b0960e
--- /dev/null
+++ b/server/aidl_api/netd_aidl_interface/4/android/net/TetherStatsParcel.aidl
@@ -0,0 +1,27 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a 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;
+/* @hide */
+parcelable TetherStatsParcel {
+ @utf8InCpp String iface;
+ long rxBytes;
+ long rxPackets;
+ long txBytes;
+ long txPackets;
+ int ifIndex = 0;
+}
diff --git a/server/aidl_api/netd_aidl_interface/4/android/net/UidRangeParcel.aidl b/server/aidl_api/netd_aidl_interface/4/android/net/UidRangeParcel.aidl
new file mode 100644
index 0000000..84ff457
--- /dev/null
+++ b/server/aidl_api/netd_aidl_interface/4/android/net/UidRangeParcel.aidl
@@ -0,0 +1,23 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a 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;
+/* @hide */
+parcelable UidRangeParcel {
+ int start;
+ int stop;
+}
diff --git a/server/aidl_api/netd_aidl_interface/current/android/net/INetd.aidl b/server/aidl_api/netd_aidl_interface/current/android/net/INetd.aidl
new file mode 100644
index 0000000..47e2931
--- /dev/null
+++ b/server/aidl_api/netd_aidl_interface/current/android/net/INetd.aidl
@@ -0,0 +1,164 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a 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;
+/* @hide */
+interface INetd {
+ boolean isAlive();
+ boolean firewallReplaceUidChain(in @utf8InCpp String chainName, boolean isWhitelist, in int[] uids);
+ boolean bandwidthEnableDataSaver(boolean enable);
+ void networkCreatePhysical(int netId, int permission);
+ void networkCreateVpn(int netId, boolean secure);
+ void networkDestroy(int netId);
+ void networkAddInterface(int netId, in @utf8InCpp String iface);
+ void networkRemoveInterface(int netId, in @utf8InCpp String iface);
+ void networkAddUidRanges(int netId, in android.net.UidRangeParcel[] uidRanges);
+ void networkRemoveUidRanges(int netId, in android.net.UidRangeParcel[] uidRanges);
+ void networkRejectNonSecureVpn(boolean add, in android.net.UidRangeParcel[] uidRanges);
+ void socketDestroy(in android.net.UidRangeParcel[] uidRanges, in int[] exemptUids);
+ boolean tetherApplyDnsInterfaces();
+ android.net.TetherStatsParcel[] tetherGetStats();
+ void interfaceAddAddress(in @utf8InCpp String ifName, in @utf8InCpp String addrString, int prefixLength);
+ void interfaceDelAddress(in @utf8InCpp String ifName, in @utf8InCpp String addrString, int prefixLength);
+ @utf8InCpp String getProcSysNet(int ipversion, int which, in @utf8InCpp String ifname, in @utf8InCpp String parameter);
+ void setProcSysNet(int ipversion, int which, in @utf8InCpp String ifname, in @utf8InCpp String parameter, in @utf8InCpp String value);
+ void ipSecSetEncapSocketOwner(in ParcelFileDescriptor socket, int newUid);
+ int ipSecAllocateSpi(int transformId, in @utf8InCpp String sourceAddress, in @utf8InCpp String destinationAddress, int spi);
+ void ipSecAddSecurityAssociation(int transformId, int mode, in @utf8InCpp String sourceAddress, in @utf8InCpp String destinationAddress, int underlyingNetId, int spi, int markValue, int markMask, in @utf8InCpp String authAlgo, in byte[] authKey, in int authTruncBits, in @utf8InCpp String cryptAlgo, in byte[] cryptKey, in int cryptTruncBits, in @utf8InCpp String aeadAlgo, in byte[] aeadKey, in int aeadIcvBits, int encapType, int encapLocalPort, int encapRemotePort, int interfaceId);
+ void ipSecDeleteSecurityAssociation(int transformId, in @utf8InCpp String sourceAddress, in @utf8InCpp String destinationAddress, int spi, int markValue, int markMask, int interfaceId);
+ void ipSecApplyTransportModeTransform(in ParcelFileDescriptor socket, int transformId, int direction, in @utf8InCpp String sourceAddress, in @utf8InCpp String destinationAddress, int spi);
+ void ipSecRemoveTransportModeTransform(in ParcelFileDescriptor socket);
+ void ipSecAddSecurityPolicy(int transformId, int selAddrFamily, int direction, in @utf8InCpp String tmplSrcAddress, in @utf8InCpp String tmplDstAddress, int spi, int markValue, int markMask, int interfaceId);
+ void ipSecUpdateSecurityPolicy(int transformId, int selAddrFamily, int direction, in @utf8InCpp String tmplSrcAddress, in @utf8InCpp String tmplDstAddress, int spi, int markValue, int markMask, int interfaceId);
+ void ipSecDeleteSecurityPolicy(int transformId, int selAddrFamily, int direction, int markValue, int markMask, int interfaceId);
+ void ipSecAddTunnelInterface(in @utf8InCpp String deviceName, in @utf8InCpp String localAddress, in @utf8InCpp String remoteAddress, int iKey, int oKey, int interfaceId);
+ void ipSecUpdateTunnelInterface(in @utf8InCpp String deviceName, in @utf8InCpp String localAddress, in @utf8InCpp String remoteAddress, int iKey, int oKey, int interfaceId);
+ void ipSecRemoveTunnelInterface(in @utf8InCpp String deviceName);
+ void wakeupAddInterface(in @utf8InCpp String ifName, in @utf8InCpp String prefix, int mark, int mask);
+ void wakeupDelInterface(in @utf8InCpp String ifName, in @utf8InCpp String prefix, int mark, int mask);
+ void setIPv6AddrGenMode(in @utf8InCpp String ifName, int mode);
+ void idletimerAddInterface(in @utf8InCpp String ifName, int timeout, in @utf8InCpp String classLabel);
+ void idletimerRemoveInterface(in @utf8InCpp String ifName, int timeout, in @utf8InCpp String classLabel);
+ void strictUidCleartextPenalty(int uid, int policyPenalty);
+ @utf8InCpp String clatdStart(in @utf8InCpp String ifName, in @utf8InCpp String nat64Prefix);
+ void clatdStop(in @utf8InCpp String ifName);
+ boolean ipfwdEnabled();
+ @utf8InCpp String[] ipfwdGetRequesterList();
+ void ipfwdEnableForwarding(in @utf8InCpp String requester);
+ void ipfwdDisableForwarding(in @utf8InCpp String requester);
+ void ipfwdAddInterfaceForward(in @utf8InCpp String fromIface, in @utf8InCpp String toIface);
+ void ipfwdRemoveInterfaceForward(in @utf8InCpp String fromIface, in @utf8InCpp String toIface);
+ void bandwidthSetInterfaceQuota(in @utf8InCpp String ifName, long bytes);
+ void bandwidthRemoveInterfaceQuota(in @utf8InCpp String ifName);
+ void bandwidthSetInterfaceAlert(in @utf8InCpp String ifName, long bytes);
+ void bandwidthRemoveInterfaceAlert(in @utf8InCpp String ifName);
+ void bandwidthSetGlobalAlert(long bytes);
+ void bandwidthAddNaughtyApp(int uid);
+ void bandwidthRemoveNaughtyApp(int uid);
+ void bandwidthAddNiceApp(int uid);
+ void bandwidthRemoveNiceApp(int uid);
+ void tetherStart(in @utf8InCpp String[] dhcpRanges);
+ void tetherStop();
+ boolean tetherIsEnabled();
+ void tetherInterfaceAdd(in @utf8InCpp String ifName);
+ void tetherInterfaceRemove(in @utf8InCpp String ifName);
+ @utf8InCpp String[] tetherInterfaceList();
+ void tetherDnsSet(int netId, in @utf8InCpp String[] dnsAddrs);
+ @utf8InCpp String[] tetherDnsList();
+ void networkAddRoute(int netId, in @utf8InCpp String ifName, in @utf8InCpp String destination, in @utf8InCpp String nextHop);
+ void networkRemoveRoute(int netId, in @utf8InCpp String ifName, in @utf8InCpp String destination, in @utf8InCpp String nextHop);
+ void networkAddLegacyRoute(int netId, in @utf8InCpp String ifName, in @utf8InCpp String destination, in @utf8InCpp String nextHop, int uid);
+ void networkRemoveLegacyRoute(int netId, in @utf8InCpp String ifName, in @utf8InCpp String destination, in @utf8InCpp String nextHop, int uid);
+ int networkGetDefault();
+ void networkSetDefault(int netId);
+ void networkClearDefault();
+ void networkSetPermissionForNetwork(int netId, int permission);
+ void networkSetPermissionForUser(int permission, in int[] uids);
+ void networkClearPermissionForUser(in int[] uids);
+ void trafficSetNetPermForUids(int permission, in int[] uids);
+ void networkSetProtectAllow(int uid);
+ void networkSetProtectDeny(int uid);
+ boolean networkCanProtect(int uid);
+ void firewallSetFirewallType(int firewalltype);
+ void firewallSetInterfaceRule(in @utf8InCpp String ifName, int firewallRule);
+ void firewallSetUidRule(int childChain, int uid, int firewallRule);
+ void firewallEnableChildChain(int childChain, boolean enable);
+ @utf8InCpp String[] interfaceGetList();
+ android.net.InterfaceConfigurationParcel interfaceGetCfg(in @utf8InCpp String ifName);
+ void interfaceSetCfg(in android.net.InterfaceConfigurationParcel cfg);
+ void interfaceSetIPv6PrivacyExtensions(in @utf8InCpp String ifName, boolean enable);
+ void interfaceClearAddrs(in @utf8InCpp String ifName);
+ void interfaceSetEnableIPv6(in @utf8InCpp String ifName, boolean enable);
+ void interfaceSetMtu(in @utf8InCpp String ifName, int mtu);
+ void tetherAddForward(in @utf8InCpp String intIface, in @utf8InCpp String extIface);
+ void tetherRemoveForward(in @utf8InCpp String intIface, in @utf8InCpp String extIface);
+ void setTcpRWmemorySize(in @utf8InCpp String rmemValues, in @utf8InCpp String wmemValues);
+ void registerUnsolicitedEventListener(android.net.INetdUnsolicitedEventListener listener);
+ void firewallAddUidInterfaceRules(in @utf8InCpp String ifName, in int[] uids);
+ void firewallRemoveUidInterfaceRules(in int[] uids);
+ void trafficSwapActiveStatsMap();
+ IBinder getOemNetd();
+ void tetherStartWithConfiguration(in android.net.TetherConfigParcel config);
+ android.net.MarkMaskParcel getFwmarkForNetwork(int netId);
+ void networkAddRouteParcel(int netId, in android.net.RouteInfoParcel routeInfo);
+ void networkUpdateRouteParcel(int netId, in android.net.RouteInfoParcel routeInfo);
+ void networkRemoveRouteParcel(int netId, in android.net.RouteInfoParcel routeInfo);
+ void tetherOffloadRuleAdd(in android.net.TetherOffloadRuleParcel rule);
+ void tetherOffloadRuleRemove(in android.net.TetherOffloadRuleParcel rule);
+ android.net.TetherStatsParcel[] tetherOffloadGetStats();
+ void tetherOffloadSetInterfaceQuota(int ifIndex, long quotaBytes);
+ android.net.TetherStatsParcel tetherOffloadGetAndClearStats(int ifIndex);
+ const int IPV4 = 4;
+ const int IPV6 = 6;
+ const int CONF = 1;
+ const int NEIGH = 2;
+ const String IPSEC_INTERFACE_PREFIX = "ipsec";
+ const int IPV6_ADDR_GEN_MODE_EUI64 = 0;
+ const int IPV6_ADDR_GEN_MODE_NONE = 1;
+ const int IPV6_ADDR_GEN_MODE_STABLE_PRIVACY = 2;
+ const int IPV6_ADDR_GEN_MODE_RANDOM = 3;
+ const int IPV6_ADDR_GEN_MODE_DEFAULT = 0;
+ const int PENALTY_POLICY_ACCEPT = 1;
+ const int PENALTY_POLICY_LOG = 2;
+ const int PENALTY_POLICY_REJECT = 3;
+ const int LOCAL_NET_ID = 99;
+ const String NEXTHOP_NONE = "";
+ const String NEXTHOP_UNREACHABLE = "unreachable";
+ const String NEXTHOP_THROW = "throw";
+ const int PERMISSION_NONE = 0;
+ const int PERMISSION_NETWORK = 1;
+ const int PERMISSION_SYSTEM = 2;
+ const int NO_PERMISSIONS = 0;
+ const int PERMISSION_INTERNET = 4;
+ const int PERMISSION_UPDATE_DEVICE_STATS = 8;
+ const int PERMISSION_UNINSTALLED = -1;
+ const int FIREWALL_WHITELIST = 0;
+ const int FIREWALL_BLACKLIST = 1;
+ const int FIREWALL_RULE_ALLOW = 1;
+ const int FIREWALL_RULE_DENY = 2;
+ const int FIREWALL_CHAIN_NONE = 0;
+ const int FIREWALL_CHAIN_DOZABLE = 1;
+ const int FIREWALL_CHAIN_STANDBY = 2;
+ const int FIREWALL_CHAIN_POWERSAVE = 3;
+ const String IF_STATE_UP = "up";
+ const String IF_STATE_DOWN = "down";
+ const String IF_FLAG_BROADCAST = "broadcast";
+ const String IF_FLAG_LOOPBACK = "loopback";
+ const String IF_FLAG_POINTOPOINT = "point-to-point";
+ const String IF_FLAG_RUNNING = "running";
+ const String IF_FLAG_MULTICAST = "multicast";
+}
diff --git a/server/aidl_api/netd_aidl_interface/current/android/net/INetdUnsolicitedEventListener.aidl b/server/aidl_api/netd_aidl_interface/current/android/net/INetdUnsolicitedEventListener.aidl
new file mode 100644
index 0000000..4459363
--- /dev/null
+++ b/server/aidl_api/netd_aidl_interface/current/android/net/INetdUnsolicitedEventListener.aidl
@@ -0,0 +1,32 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a 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;
+/* @hide */
+interface INetdUnsolicitedEventListener {
+ oneway void onInterfaceClassActivityChanged(boolean isActive, int timerLabel, long timestampNs, int uid);
+ oneway void onQuotaLimitReached(@utf8InCpp String alertName, @utf8InCpp String ifName);
+ oneway void onInterfaceDnsServerInfo(@utf8InCpp String ifName, long lifetimeS, in @utf8InCpp String[] servers);
+ oneway void onInterfaceAddressUpdated(@utf8InCpp String addr, @utf8InCpp String ifName, int flags, int scope);
+ oneway void onInterfaceAddressRemoved(@utf8InCpp String addr, @utf8InCpp String ifName, int flags, int scope);
+ oneway void onInterfaceAdded(@utf8InCpp String ifName);
+ oneway void onInterfaceRemoved(@utf8InCpp String ifName);
+ oneway void onInterfaceChanged(@utf8InCpp String ifName, boolean up);
+ oneway void onInterfaceLinkStateChanged(@utf8InCpp String ifName, boolean up);
+ oneway void onRouteChanged(boolean updated, @utf8InCpp String route, @utf8InCpp String gateway, @utf8InCpp String ifName);
+ oneway void onStrictCleartextDetected(int uid, @utf8InCpp String hex);
+}
diff --git a/server/aidl_api/netd_aidl_interface/current/android/net/InterfaceConfigurationParcel.aidl b/server/aidl_api/netd_aidl_interface/current/android/net/InterfaceConfigurationParcel.aidl
new file mode 100644
index 0000000..01e0f95
--- /dev/null
+++ b/server/aidl_api/netd_aidl_interface/current/android/net/InterfaceConfigurationParcel.aidl
@@ -0,0 +1,26 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a 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;
+/* @hide */
+parcelable InterfaceConfigurationParcel {
+ @utf8InCpp String ifName;
+ @utf8InCpp String hwAddr;
+ @utf8InCpp String ipv4Addr;
+ int prefixLength;
+ @utf8InCpp String[] flags;
+}
diff --git a/server/aidl_api/netd_aidl_interface/current/android/net/MarkMaskParcel.aidl b/server/aidl_api/netd_aidl_interface/current/android/net/MarkMaskParcel.aidl
new file mode 100644
index 0000000..62be838
--- /dev/null
+++ b/server/aidl_api/netd_aidl_interface/current/android/net/MarkMaskParcel.aidl
@@ -0,0 +1,23 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a 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;
+/* @hide */
+parcelable MarkMaskParcel {
+ int mark;
+ int mask;
+}
diff --git a/server/aidl_api/netd_aidl_interface/current/android/net/RouteInfoParcel.aidl b/server/aidl_api/netd_aidl_interface/current/android/net/RouteInfoParcel.aidl
new file mode 100644
index 0000000..5e0ee62
--- /dev/null
+++ b/server/aidl_api/netd_aidl_interface/current/android/net/RouteInfoParcel.aidl
@@ -0,0 +1,24 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a 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 RouteInfoParcel {
+ @utf8InCpp String destination;
+ @utf8InCpp String ifName;
+ @utf8InCpp String nextHop;
+ int mtu;
+}
diff --git a/server/aidl_api/netd_aidl_interface/current/android/net/TetherConfigParcel.aidl b/server/aidl_api/netd_aidl_interface/current/android/net/TetherConfigParcel.aidl
new file mode 100644
index 0000000..b136454
--- /dev/null
+++ b/server/aidl_api/netd_aidl_interface/current/android/net/TetherConfigParcel.aidl
@@ -0,0 +1,23 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a 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;
+/* @hide */
+parcelable TetherConfigParcel {
+ boolean usingLegacyDnsProxy;
+ @utf8InCpp String[] dhcpRanges;
+}
diff --git a/server/aidl_api/netd_aidl_interface/current/android/net/TetherOffloadRuleParcel.aidl b/server/aidl_api/netd_aidl_interface/current/android/net/TetherOffloadRuleParcel.aidl
new file mode 100644
index 0000000..c9d8458
--- /dev/null
+++ b/server/aidl_api/netd_aidl_interface/current/android/net/TetherOffloadRuleParcel.aidl
@@ -0,0 +1,28 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a 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;
+/* @hide */
+parcelable TetherOffloadRuleParcel {
+ int inputInterfaceIndex;
+ int outputInterfaceIndex;
+ byte[] destination;
+ int prefixLength;
+ byte[] srcL2Address;
+ byte[] dstL2Address;
+ int pmtu = 1500;
+}
diff --git a/server/aidl_api/netd_aidl_interface/current/android/net/TetherStatsParcel.aidl b/server/aidl_api/netd_aidl_interface/current/android/net/TetherStatsParcel.aidl
new file mode 100644
index 0000000..0b0960e
--- /dev/null
+++ b/server/aidl_api/netd_aidl_interface/current/android/net/TetherStatsParcel.aidl
@@ -0,0 +1,27 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a 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;
+/* @hide */
+parcelable TetherStatsParcel {
+ @utf8InCpp String iface;
+ long rxBytes;
+ long rxPackets;
+ long txBytes;
+ long txPackets;
+ int ifIndex = 0;
+}
diff --git a/server/aidl_api/netd_aidl_interface/current/android/net/UidRangeParcel.aidl b/server/aidl_api/netd_aidl_interface/current/android/net/UidRangeParcel.aidl
new file mode 100644
index 0000000..84ff457
--- /dev/null
+++ b/server/aidl_api/netd_aidl_interface/current/android/net/UidRangeParcel.aidl
@@ -0,0 +1,23 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a 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;
+/* @hide */
+parcelable UidRangeParcel {
+ int start;
+ int stop;
+}
diff --git a/server/aidl_api/netd_event_listener_interface/1/.hash b/server/aidl_api/netd_event_listener_interface/1/.hash
new file mode 100644
index 0000000..f39f730
--- /dev/null
+++ b/server/aidl_api/netd_event_listener_interface/1/.hash
@@ -0,0 +1 @@
+8e27594d285ca7c567d87e8cf74766c27647e02b
diff --git a/server/aidl/netdeventlistener/1/android/net/metrics/INetdEventListener.aidl b/server/aidl_api/netd_event_listener_interface/1/android/net/metrics/INetdEventListener.aidl
similarity index 100%
rename from server/aidl/netdeventlistener/1/android/net/metrics/INetdEventListener.aidl
rename to server/aidl_api/netd_event_listener_interface/1/android/net/metrics/INetdEventListener.aidl
diff --git a/server/aidl_api/netd_event_listener_interface/current/android/net/metrics/INetdEventListener.aidl b/server/aidl_api/netd_event_listener_interface/current/android/net/metrics/INetdEventListener.aidl
new file mode 100644
index 0000000..d71c3f2
--- /dev/null
+++ b/server/aidl_api/netd_event_listener_interface/current/android/net/metrics/INetdEventListener.aidl
@@ -0,0 +1,35 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a 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.metrics;
+/* @hide */
+interface INetdEventListener {
+ oneway void onDnsEvent(int netId, int eventType, int returnCode, int latencyMs, @utf8InCpp String hostname, in @utf8InCpp String[] ipAddresses, int ipAddressesCount, int uid);
+ oneway void onPrivateDnsValidationEvent(int netId, String ipAddress, String hostname, boolean validated);
+ oneway void onConnectEvent(int netId, int error, int latencyMs, String ipAddr, int port, int uid);
+ oneway void onWakeupEvent(String prefix, int uid, int ethertype, int ipNextHeader, in byte[] dstHw, String srcIp, String dstIp, int srcPort, int dstPort, long timestampNs);
+ oneway void onTcpSocketStatsEvent(in int[] networkIds, in int[] sentPackets, in int[] lostPackets, in int[] rttUs, in int[] sentAckDiffMs);
+ oneway void onNat64PrefixEvent(int netId, boolean added, @utf8InCpp String prefixString, int prefixLength);
+ const int EVENT_GETADDRINFO = 1;
+ const int EVENT_GETHOSTBYNAME = 2;
+ const int EVENT_GETHOSTBYADDR = 3;
+ const int EVENT_RES_NSEND = 4;
+ const int REPORTING_LEVEL_NONE = 0;
+ const int REPORTING_LEVEL_METRICS = 1;
+ const int REPORTING_LEVEL_FULL = 2;
+ const int DNS_REPORTED_IP_ADDRESSES_LIMIT = 10;
+}
diff --git a/server/binder/android/net/INetd.aidl b/server/binder/android/net/INetd.aidl
index ec9bbc3..3b221cf 100644
--- a/server/binder/android/net/INetd.aidl
+++ b/server/binder/android/net/INetd.aidl
@@ -18,6 +18,10 @@
import android.net.INetdUnsolicitedEventListener;
import android.net.InterfaceConfigurationParcel;
+import android.net.MarkMaskParcel;
+import android.net.RouteInfoParcel;
+import android.net.TetherConfigParcel;
+import android.net.TetherOffloadRuleParcel;
import android.net.TetherStatsParcel;
import android.net.UidRangeParcel;
@@ -188,7 +192,7 @@
* Return tethering statistics.
*
* @return an array of TetherStatsParcel, where each entry contains the upstream interface
- * name and its tethering statistics.
+ * name and its tethering statistics since netd startup.
* There will only ever be one entry for a given interface.
* @throws ServiceSpecificException in case of failure, with an error code indicating the
* cause of the failure.
@@ -1194,4 +1198,115 @@
* @return a IBinder object, it could be casted to oem specific interface.
*/
IBinder getOemNetd();
+
+ /**
+ * Start tethering with given configuration
+ *
+ * @param config config to start tethering.
+ * @throws ServiceSpecificException in case of failure, with an error code indicating the
+ * cause of the failure.
+ */
+ void tetherStartWithConfiguration(in TetherConfigParcel config);
+
+
+ /**
+ * Get the fwmark and its net id mask for the given network id.
+ *
+ * @param netId the network to get the fwmark and mask for.
+ * @return A MarkMaskParcel of the given network id.
+ */
+ MarkMaskParcel getFwmarkForNetwork(int netId);
+
+ /**
+ * Add a route for specific network
+ *
+ * @param netId the network to add the route to
+ * @param routeInfo parcelable with route information
+ * @throws ServiceSpecificException in case of failure, with an error code indicating the
+ * cause of the failure.
+ */
+ void networkAddRouteParcel(int netId, in android.net.RouteInfoParcel routeInfo);
+
+ /**
+ * Update a route for specific network
+ *
+ * @param routeInfo parcelable with route information
+ * @throws ServiceSpecificException in case of failure, with an error code indicating the
+ * cause of the failure.
+ */
+ void networkUpdateRouteParcel(int netId, in android.net.RouteInfoParcel routeInfo);
+
+ /**
+ * Remove a route for specific network
+ *
+ * @param routeInfo parcelable with route information
+ * @throws ServiceSpecificException in case of failure, with an error code indicating the
+ * cause of the failure.
+ */
+ void networkRemoveRouteParcel(int netId, in android.net.RouteInfoParcel routeInfo);
+
+ /**
+ * Adds a tethering offload rule, or updates it if it already exists.
+ *
+ * Currently, only downstream /128 IPv6 entries are supported. An existing rule will be updated
+ * if the input interface and destination prefix match. Otherwise, a new rule will be created.
+ *
+ * @param rule The rule to add or update.
+ * @throws ServiceSpecificException in case of failure, with an error code indicating the
+ * cause of the failure.
+ */
+ void tetherOffloadRuleAdd(in TetherOffloadRuleParcel rule);
+
+ /**
+ * Deletes a tethering offload rule.
+ *
+ * Currently, only downstream /128 IPv6 entries are supported. An existing rule will be deleted
+ * if the destination IP address and the source interface match. It is not an error if there is
+ * no matching rule to delete.
+ *
+ * @param rule The rule to delete.
+ * @throws ServiceSpecificException in case of failure, with an error code indicating the
+ * cause of the failure.
+ */
+ void tetherOffloadRuleRemove(in TetherOffloadRuleParcel rule);
+
+ /**
+ * Return BPF tethering offload statistics.
+ *
+ * @return an array of TetherStatsParcel's, where each entry contains the upstream interface
+ * index and its tethering statistics since tethering was first started.
+ * There will only ever be one entry for a given interface index.
+ * @throws ServiceSpecificException in case of failure, with an error code indicating the
+ * cause of the failure.
+ */
+ TetherStatsParcel[] tetherOffloadGetStats();
+
+ /**
+ * Set a per-interface quota for tethering offload.
+ *
+ * @param ifIndex Index of upstream interface
+ * @param quotaBytes The quota defined as the number of bytes, starting from zero and counting
+ * from *now*. A value of QUOTA_UNLIMITED (-1) indicates there is no limit.
+ * @throws ServiceSpecificException in case of failure, with an error code indicating the
+ * cause of the failure.
+ */
+ void tetherOffloadSetInterfaceQuota(int ifIndex, long quotaBytes);
+
+ /**
+ * Return BPF tethering offload statistics and clear the stats for a given upstream.
+ *
+ * Must only be called once all offload rules have already been deleted for the given upstream
+ * interface. The existing stats will be fetched and returned. The stats and the limit for the
+ * given upstream interface will be deleted as well.
+ *
+ * The stats and limit for a given upstream interface must be initialized (using
+ * tetherOffloadSetInterfaceQuota) before any offload will occur on that interface.
+ *
+ * @param ifIndex Index of upstream interface.
+ * @return TetherStatsParcel, which contains the given upstream interface index and its
+ * tethering statistics since tethering was first started on that upstream interface.
+ * @throws ServiceSpecificException in case of failure, with an error code indicating the
+ * cause of the failure.
+ */
+ TetherStatsParcel tetherOffloadGetAndClearStats(int ifIndex);
}
diff --git a/resolv/res_debug.h b/server/binder/android/net/MarkMaskParcel.aidl
similarity index 74%
rename from resolv/res_debug.h
rename to server/binder/android/net/MarkMaskParcel.aidl
index 2ba31d4..932b7bf 100644
--- a/resolv/res_debug.h
+++ b/server/binder/android/net/MarkMaskParcel.aidl
@@ -14,8 +14,16 @@
* limitations under the License.
*/
-#pragma once
+package android.net;
-#include <cinttypes>
-
-int resolv_set_log_severity(uint32_t logSeverity);
+/**
+ * Structure that stores a firewall mark and its mask.
+ *
+ * {@hide}
+ */
+parcelable MarkMaskParcel {
+ // The fwmark.
+ int mark;
+ // Net id mask of fwmark.
+ int mask;
+}
diff --git a/server/binder/android/net/RouteInfoParcel.aidl b/server/binder/android/net/RouteInfoParcel.aidl
new file mode 100644
index 0000000..fcc86e3
--- /dev/null
+++ b/server/binder/android/net/RouteInfoParcel.aidl
@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) 2020, 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;
+
+parcelable RouteInfoParcel {
+ // The destination of the route.
+ @utf8InCpp String destination;
+ // The name of interface of the route. This interface should be assigned to the netID.
+ @utf8InCpp String ifName;
+ // The route's next hop address, or one of the NEXTHOP_* constants defined in INetd.aidl.
+ @utf8InCpp String nextHop;
+ // The MTU of the route.
+ int mtu;
+}
diff --git a/server/binder/android/net/TetherConfigParcel.aidl b/server/binder/android/net/TetherConfigParcel.aidl
new file mode 100644
index 0000000..9f371ce
--- /dev/null
+++ b/server/binder/android/net/TetherConfigParcel.aidl
@@ -0,0 +1,32 @@
+/*
+ * 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;
+
+/**
+ * The configuration to start tethering.
+ *
+ * {@hide}
+ */
+parcelable TetherConfigParcel {
+ // Whether to enable or disable legacy DNS proxy server.
+ boolean usingLegacyDnsProxy;
+ // DHCP ranges to set.
+ // dhcpRanges might contain many addresss {addr1, addr2, addr3, addr4...}
+ // Netd splits them into ranges: addr1-addr2, addr3-addr4, etc.
+ // An odd number of addrs will fail.
+ @utf8InCpp String[] dhcpRanges;
+}
diff --git a/server/binder/android/net/TetherOffloadRuleParcel.aidl b/server/binder/android/net/TetherOffloadRuleParcel.aidl
new file mode 100644
index 0000000..c549e61
--- /dev/null
+++ b/server/binder/android/net/TetherOffloadRuleParcel.aidl
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2020 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;
+
+/**
+ * Represents a forwarding rule for tethering offload.
+ *
+ * {@hide}
+ */
+parcelable TetherOffloadRuleParcel {
+ /** The interface index of the input interface. */
+ int inputInterfaceIndex;
+
+ /** The interface index of the output interface. */
+ int outputInterfaceIndex;
+
+ /** The base IP address of the destination prefix as a byte array. */
+ byte[] destination;
+
+ /** The destination prefix length. */
+ int prefixLength;
+
+ /** The source link-layer address. Currently, must be a 6-byte MAC address.*/
+ byte[] srcL2Address;
+
+ /** The destination link-layer address. Currently, must be a 6-byte MAC address. */
+ byte[] dstL2Address;
+
+ /** The outbound path mtu. */
+ int pmtu = 1500;
+}
diff --git a/server/binder/android/net/TetherStatsParcel.aidl b/server/binder/android/net/TetherStatsParcel.aidl
index 25e200c..6bf60a8 100644
--- a/server/binder/android/net/TetherStatsParcel.aidl
+++ b/server/binder/android/net/TetherStatsParcel.aidl
@@ -22,9 +22,34 @@
* {@hide}
*/
parcelable TetherStatsParcel {
+ /**
+ * Parcel representing tethering interface statistics.
+ *
+ * This parcel is used by tetherGetStats, tetherOffloadGetStats and
+ * tetherOffloadGetAndClearStats in INetd.aidl. tetherGetStats uses this parcel to return the
+ * tethering statistics since netd startup and presents the interface via its interface name.
+ * Both tetherOffloadGetStats and tetherOffloadGetAndClearStats use this parcel to return
+ * the tethering statistics since tethering was first started. They present the interface via
+ * its interface index. Note that the interface must be presented by either interface name
+ * |iface| or interface index |ifIndex| in this parcel. The unused interface name is set to
+ * an empty string "" by default and the unused interface index is set to 0 by default.
+ */
+
+ /** The interface name. */
@utf8InCpp String iface;
+
+ /** Total number of received bytes. */
long rxBytes;
+
+ /** Total number of received packets. */
long rxPackets;
+
+ /** Total number of transmitted bytes. */
long txBytes;
+
+ /** Total number of transmitted packets. */
long txPackets;
+
+ /** The interface index. */
+ int ifIndex = 0;
}
diff --git a/server/main.cpp b/server/main.cpp
index b783ce5..4949ff6 100644
--- a/server/main.cpp
+++ b/server/main.cpp
@@ -14,27 +14,27 @@
* limitations under the License.
*/
-#include <chrono>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
#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 <fcntl.h>
-#include <dirent.h>
+#include <chrono>
+#include <cinttypes>
+#include <mutex>
#define LOG_TAG "Netd"
#include "log/log.h"
-#include <android-base/properties.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
+#include <libbpf_android.h>
#include <netdutils/Stopwatch.h>
#include "Controllers.h"
@@ -48,9 +48,9 @@
#include "Process.h"
#include "netd_resolv/resolv.h"
-#include "netd_resolv/resolv_stub.h"
using android::IPCThreadState;
+using android::sp;
using android::status_t;
using android::String16;
using android::net::FwmarkServer;
@@ -82,13 +82,25 @@
gLog.info(std::string(msg));
}
+int tagSocketCallback(int sockFd, uint32_t tag, uid_t uid, pid_t) {
+ // Workaround for secureVPN with VpnIsolation enabled, refer to b/159994981 for details.
+ if (tag == TAG_SYSTEM_DNS) uid = AID_DNS;
+ 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,
- .check_calling_permission = &checkCallingPermissionCallback,
+ .tagSocket = &tagSocketCallback,
+ .evaluate_domain_name = &evaluateDomainNameCallback,
};
- return RESOLV_STUB.resolv_init(callbacks);
+ return resolv_init(&callbacks);
}
} // namespace
@@ -108,14 +120,8 @@
setCloseOnExec(sock);
}
- // Before we start any threads, populate the resolver stub pointers.
- resolv_stub_init();
-
// Make sure BPF programs are loaded before doing anything
- while (!android::base::WaitForProperty("bpf.progs_loaded", "1",
- std::chrono::seconds(5))) {
- ALOGD("netd waited 5s for bpf.progs_loaded, still waiting...");
- }
+ android::bpf::waitForProgsLoaded();
NetlinkManager *nm = NetlinkManager::Instance();
if (nm == nullptr) {
@@ -175,20 +181,18 @@
ALOGE("Unable to start NetdNativeService: %d", ret);
exit(1);
}
- gLog.info("Registering NetdNativeService: %.1fms", subTime.getTimeAndReset());
+ gLog.info("Registering NetdNativeService: %" PRId64 "us", subTime.getTimeAndResetUs());
android::net::process::ScopedPidFile pidFile(PID_FILE_PATH);
- // Now that netd is ready to process commands, advertise service
- // availability for HAL clients.
- NetdHwService mHwSvc;
- if ((ret = mHwSvc.start()) != android::OK) {
+ // Now that netd is ready to process commands, advertise service availability for HAL clients.
+ sp<NetdHwService> mHwSvc(new NetdHwService());
+ if ((ret = mHwSvc->start()) != android::OK) {
ALOGE("Unable to start NetdHwService: %d", ret);
exit(1);
}
- gLog.info("Registering NetdHwService: %.1fms", subTime.getTimeAndReset());
-
- gLog.info("Netd started in %dms", static_cast<int>(s.timeTaken()));
+ gLog.info("Registering NetdHwService: %" PRId64 "us", subTime.getTimeAndResetUs());
+ gLog.info("Netd started in %" PRId64 "us", s.timeTakenUs());
IPCThreadState::self()->joinThreadPool();
diff --git a/server/netd.rc b/server/netd.rc
index 1fc6ab5..e6316c1 100644
--- a/server/netd.rc
+++ b/server/netd.rc
@@ -1,5 +1,6 @@
service netd /system/bin/netd
class main
+ capabilities CHOWN DAC_OVERRIDE DAC_READ_SEARCH FOWNER IPC_LOCK KILL NET_ADMIN NET_BIND_SERVICE NET_RAW SETUID SETGID
socket dnsproxyd stream 0660 root inet
socket mdns stream 0660 root system
socket fwmarkd stream 0660 root inet
diff --git a/server/oem_iptables_hook.cpp b/server/oem_iptables_hook.cpp
index c7f8dbf..39a6285 100644
--- a/server/oem_iptables_hook.cpp
+++ b/server/oem_iptables_hook.cpp
@@ -26,7 +26,6 @@
#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 3c35ceb..3baaa18 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -47,14 +47,31 @@
],
}
+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"],
+ test_suites: [
+ "device-tests",
+ "vts"
+ ],
+ require_root: true,
defaults: ["netd_defaults"],
+ tidy: false, // cuts test build time by almost 1 minute
srcs: [
":netd_integration_test_shared",
"binder_test.cpp",
"bpf_base_test.cpp",
+ "netd_client_test.cpp",
"netd_test.cpp",
"netlink_listener_test.cpp",
],
@@ -75,12 +92,12 @@
"libcap",
"libnetd_test_tun_interface",
"libnetd_test_unsol_service",
+ "libnetd_test_utils",
"libbpf_android",
- "liblogwrap",
"libnetdbpf",
"libnetdutils",
"libqtaguid",
- "netd_aidl_interface-cpp",
+ "netd_aidl_interface-unstable-cpp",
"netd_event_listener_interface-cpp",
"oemnetd_aidl_interface-cpp",
],
diff --git a/tests/AndroidTest.xml b/tests/AndroidTest.xml
deleted file mode 100644
index 7b18f9f..0000000
--- a/tests/AndroidTest.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<?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 c14253d..e1bf67c 100644
--- a/tests/benchmarks/Android.bp
+++ b/tests/benchmarks/Android.bp
@@ -5,17 +5,18 @@
defaults: ["netd_defaults"],
shared_libs: [
"libbase",
- "libbinder",
+ "libbinder_ndk",
"liblog",
"libnetd_client",
"libnetdutils",
+ "libutils",
],
static_libs: [
- "libnetd_test_dnsresponder",
- "libutils",
- "dnsresolver_aidl_interface-cpp",
- "netd_aidl_interface-cpp",
- "netd_event_listener_interface-cpp",
+ "libnetd_test_dnsresponder_ndk",
+ "dnsresolver_aidl_interface-unstable-ndk_platform",
+ "netd_aidl_interface-cpp", // system/netd/server/UidRanges.h
+ "netd_aidl_interface-ndk_platform",
+ "netd_event_listener_interface-ndk_platform",
],
aidl: {
include_dirs: ["system/netd/server/binder"],
@@ -23,10 +24,8 @@
include_dirs: [
"system/netd/include",
"system/netd/client",
- "system/netd/resolv/include",
"system/netd/server",
"system/netd/server/binder",
- "system/netd/resolv/dns_responder",
],
srcs: [
"main.cpp",
@@ -38,14 +37,12 @@
cc_benchmark {
name: "bpf_benchmark",
defaults: ["netd_defaults"],
+ require_root: true,
shared_libs: [
"libbase",
"libbpf_android",
"libnetdutils",
],
- static_libs: [
- "libutils",
- ],
srcs: [
"bpf_benchmark.cpp",
],
diff --git a/tests/benchmarks/AndroidTest.xml b/tests/benchmarks/AndroidTest.xml
deleted file mode 100644
index 7756cc2..0000000
--- a/tests/benchmarks/AndroidTest.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?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/bpf_benchmark.cpp b/tests/benchmarks/bpf_benchmark.cpp
index 2c1ed1e..12aaf95 100644
--- a/tests/benchmarks/bpf_benchmark.cpp
+++ b/tests/benchmarks/bpf_benchmark.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <benchmark/benchmark.h>
@@ -22,6 +23,7 @@
constexpr uint32_t TEST_MAP_SIZE = 10000;
+using android::base::Result;
using android::base::StringPrintf;
using android::bpf::BpfMap;
@@ -33,26 +35,32 @@
BENCHMARK_DEFINE_F(BpfBenchMark, MapWriteNewEntry)(benchmark::State& state) {
for (auto _ : state) {
- expectOk(mBpfTestMap.writeValue(state.range(0), state.range(0), BPF_NOEXIST));
+ // TODO(b/147676069) assert
+ mBpfTestMap.writeValue(state.range(0), state.range(0), BPF_NOEXIST);
}
}
BENCHMARK_DEFINE_F(BpfBenchMark, MapUpdateEntry)(benchmark::State& state) {
for (uint32_t i = 0; i < TEST_MAP_SIZE; i++) {
- expectOk(mBpfTestMap.writeValue(i, i, BPF_NOEXIST));
+ // TODO(b/147676069) assert
+ mBpfTestMap.writeValue(i, i, BPF_NOEXIST);
}
for (auto _ : state) {
- expectOk(mBpfTestMap.writeValue(state.range(0), state.range(0) + 1, BPF_EXIST));
+ // TODO(b/147676069) assert
+ mBpfTestMap.writeValue(state.range(0), state.range(0) + 1, BPF_EXIST);
}
}
BENCHMARK_DEFINE_F(BpfBenchMark, MapDeleteAddEntry)(benchmark::State& state) {
for (uint32_t i = 0; i < TEST_MAP_SIZE; i++) {
- expectOk(mBpfTestMap.writeValue(i, i, BPF_NOEXIST));
+ // TODO(b/147676069) assert
+ mBpfTestMap.writeValue(i, i, BPF_NOEXIST);
}
for (auto _ : state) {
- expectOk(mBpfTestMap.deleteValue(state.range(0)));
- expectOk(mBpfTestMap.writeValue(state.range(0), state.range(0) + 1, BPF_NOEXIST));
+ // TODO(b/147676069) assert
+ mBpfTestMap.deleteValue(state.range(0));
+ // TODO(b/147676069) assert
+ mBpfTestMap.writeValue(state.range(0), state.range(0) + 1, BPF_NOEXIST);
}
}
diff --git a/tests/benchmarks/connect_benchmark.cpp b/tests/benchmarks/connect_benchmark.cpp
index e08fb01..ed90538 100644
--- a/tests/benchmarks/connect_benchmark.cpp
+++ b/tests/benchmarks/connect_benchmark.cpp
@@ -125,8 +125,8 @@
}
if (waitBetweenRuns) {
- latencies[iterations] = stopwatch.timeTaken() * 1e6L;
- state.SetIterationTime(latencies[iterations] / 1e9L);
+ latencies[iterations] = stopwatch.timeTakenUs();
+ state.SetIterationTime(static_cast<double>(latencies[iterations]) / 1.0e6L);
std::this_thread::sleep_for(std::chrono::milliseconds(10));
++iterations;
}
@@ -182,8 +182,8 @@
}
if (waitBetweenRuns) {
- latencies[iterations] = stopwatch.timeTaken() * 1e6L;
- state.SetIterationTime(latencies[iterations] / 1e9L);
+ latencies[iterations] = stopwatch.timeTakenUs();
+ state.SetIterationTime(static_cast<double>(latencies[iterations]) / 1.0e6L);
std::this_thread::sleep_for(std::chrono::milliseconds(10));
++iterations;
}
diff --git a/tests/benchmarks/dns_benchmark.cpp b/tests/benchmarks/dns_benchmark.cpp
index d25f536..b8f626e 100644
--- a/tests/benchmarks/dns_benchmark.cpp
+++ b/tests/benchmarks/dns_benchmark.cpp
@@ -43,8 +43,7 @@
#include <benchmark/benchmark.h>
#include "NetdClient.h"
-#include "dns_responder_client.h"
-#include "netd_resolv/params.h" // MAXNS
+#include "dns_responder_client_ndk.h"
using android::base::StringPrintf;
diff --git a/tests/binder_test.cpp b/tests/binder_test.cpp
index 917bf5d..2a6bb7c 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>
@@ -32,17 +32,22 @@
#include <ifaddrs.h>
#include <linux/if.h>
#include <linux/if_tun.h>
+#include <net/ethernet.h>
#include <net/if.h>
#include <netdb.h>
#include <netinet/in.h>
+#include <netinet/tcp.h>
#include <openssl/base64.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <android-base/file.h>
+#include <android-base/format.h>
#include <android-base/macros.h>
+#include <android-base/scopeguard.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
+#include <android/multinetwork.h>
#include <binder/IPCThreadState.h>
#include <bpf/BpfMap.h>
#include <bpf/BpfUtils.h>
@@ -50,26 +55,27 @@
#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"
#include "InterfaceController.h"
#include "NetdClient.h"
#include "NetdConstants.h"
+#include "NetworkController.h"
+#include "SockDiag.h"
#include "TestUnsolService.h"
#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"
#define RAW_TABLE "raw"
#define MANGLE_TABLE "mangle"
#define FILTER_TABLE "filter"
@@ -83,16 +89,23 @@
using android::String16;
using android::String8;
using android::base::Join;
+using android::base::make_scope_guard;
using android::base::ReadFileToString;
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::MarkMaskParcel;
+using android::net::SockDiag;
+using android::net::TetherOffloadRuleParcel;
using android::net::TetherStatsParcel;
using android::net::TunInterface;
using android::net::UidRangeParcel;
+using android::netdutils::IPAddress;
+using android::netdutils::ScopedAddrinfo;
using android::netdutils::sSyscalls;
using android::netdutils::Stopwatch;
@@ -100,6 +113,7 @@
static const char* IP_RULE_V6 = "-6";
static const int TEST_NETID1 = 65501;
static const int TEST_NETID2 = 65502;
+static const char* DNSMASQ = "dnsmasq";
// Use maximum reserved appId for applications to avoid conflict with existing
// uids.
@@ -111,9 +125,9 @@
static const std::string NO_SOCKET_ALLOW_RULE("! owner UID match 0-4294967294");
static const std::string ESP_ALLOW_RULE("esp");
-class BinderTest : public ::testing::Test {
+class NetdBinderTest : public ::testing::Test {
public:
- BinderTest() {
+ NetdBinderTest() {
sp<IServiceManager> sm = android::defaultServiceManager();
sp<IBinder> binder = sm->getService(String16("netd"));
if (binder != nullptr) {
@@ -149,7 +163,8 @@
sTun2.destroy();
}
- static void fakeRemoteSocketPair(int *clientSocket, int *serverSocket, int *acceptedSocket);
+ static void fakeRemoteSocketPair(unique_fd* clientSocket, unique_fd* serverSocket,
+ unique_fd* acceptedSocket);
void createVpnNetworkWithUid(bool secure, uid_t uid, int vpnNetId = TEST_NETID2,
int fallthroughNetId = TEST_NETID1);
@@ -163,78 +178,27 @@
static TunInterface sTun2;
};
-TunInterface BinderTest::sTun;
-TunInterface BinderTest::sTun2;
+TunInterface NetdBinderTest::sTun;
+TunInterface NetdBinderTest::sTun2;
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());
+ std::cerr << " " << mName << ": " << timeTakenUs() << "us" << std::endl;
}
private:
std::string mName;
};
-TEST_F(BinderTest, IsAlive) {
+TEST_F(NetdBinderTest, IsAlive) {
TimedOperation t("isAlive RPC");
bool isAlive = false;
mNetd->isAlive(&isAlive);
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);
@@ -245,7 +209,7 @@
iptablesRuleExists(IP6TABLES_PATH, chainName, ESP_ALLOW_RULE);
}
-TEST_F(BinderTest, FirewallReplaceUidChain) {
+TEST_F(NetdBinderTest, FirewallReplaceUidChain) {
SKIP_IF_BPF_SUPPORTED;
std::string chainName = StringPrintf("netd_binder_test_%u", arc4random_uniform(10000));
@@ -298,7 +262,7 @@
EXPECT_EQ(false, ret);
}
-TEST_F(BinderTest, IpSecTunnelInterface) {
+TEST_F(NetdBinderTest, IpSecTunnelInterface) {
const struct TestData {
const std::string family;
const std::string deviceName;
@@ -340,8 +304,8 @@
}
}
-TEST_F(BinderTest, IpSecSetEncapSocketOwner) {
- android::base::unique_fd uniqueFd(socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0));
+TEST_F(NetdBinderTest, IpSecSetEncapSocketOwner) {
+ unique_fd uniqueFd(socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0));
android::os::ParcelFileDescriptor sockFd(std::move(uniqueFd));
int sockOptVal = UDP_ENCAP_ESPINUDP;
@@ -367,7 +331,7 @@
#define RETURN_FALSE_IF_NEQ(_expect_, _ret_) \
do { if ((_expect_) != (_ret_)) return false; } while(false)
-bool BinderTest::allocateIpSecResources(bool expectOk, int32_t* spi) {
+bool NetdBinderTest::allocateIpSecResources(bool expectOk, int32_t* spi) {
android::netdutils::Status status = XfrmController::ipSecAllocateSpi(0, "::", "::1", 123, spi);
SCOPED_TRACE(status);
RETURN_FALSE_IF_NEQ(status.ok(), expectOk);
@@ -383,7 +347,7 @@
.ok();
}
-TEST_F(BinderTest, XfrmDualSelectorTunnelModePoliciesV4) {
+TEST_F(NetdBinderTest, XfrmDualSelectorTunnelModePoliciesV4) {
android::binder::Status status;
// Repeat to ensure cleanup and recreation works correctly
@@ -407,7 +371,7 @@
}
}
-TEST_F(BinderTest, XfrmDualSelectorTunnelModePoliciesV6) {
+TEST_F(NetdBinderTest, XfrmDualSelectorTunnelModePoliciesV6) {
binder::Status status;
// Repeat to ensure cleanup and recreation works correctly
@@ -431,7 +395,7 @@
}
}
-TEST_F(BinderTest, XfrmControllerInit) {
+TEST_F(NetdBinderTest, XfrmControllerInit) {
android::netdutils::Status status;
status = XfrmController::Init();
SCOPED_TRACE(status);
@@ -518,7 +482,7 @@
return enabled6;
}
-TEST_F(BinderTest, BandwidthEnableDataSaver) {
+TEST_F(NetdBinderTest, BandwidthEnableDataSaver) {
const int wasEnabled = getDataSaverState();
ASSERT_NE(-1, wasEnabled);
@@ -579,7 +543,7 @@
} // namespace
-TEST_F(BinderTest, NetworkInterfaces) {
+TEST_F(NetdBinderTest, NetworkInterfaces) {
EXPECT_TRUE(mNetd->networkCreatePhysical(TEST_NETID1, INetd::PERMISSION_NONE).isOk());
EXPECT_EQ(EEXIST, mNetd->networkCreatePhysical(TEST_NETID1, INetd::PERMISSION_NONE)
.serviceSpecificErrorCode());
@@ -596,7 +560,7 @@
EXPECT_EQ(ENONET, mNetd->networkDestroy(TEST_NETID1).serviceSpecificErrorCode());
}
-TEST_F(BinderTest, NetworkUidRules) {
+TEST_F(NetdBinderTest, NetworkUidRules) {
const uint32_t RULE_PRIORITY_SECURE_VPN = 12000;
EXPECT_TRUE(mNetd->networkCreateVpn(TEST_NETID1, true).isOk());
@@ -623,27 +587,23 @@
EXPECT_EQ(ENONET, mNetd->networkDestroy(TEST_NETID1).serviceSpecificErrorCode());
}
-TEST_F(BinderTest, NetworkRejectNonSecureVpn) {
+TEST_F(NetdBinderTest, NetworkRejectNonSecureVpn) {
constexpr uint32_t RULE_PRIORITY = 12500;
std::vector<UidRangeParcel> uidRanges = {makeUidRangeParcel(BASE_UID + 150, BASE_UID + 224),
makeUidRangeParcel(BASE_UID + 226, BASE_UID + 300)};
-
- const std::vector<std::string> initialRulesV4 = listIpRules(IP_RULE_V4);
- const std::vector<std::string> initialRulesV6 = listIpRules(IP_RULE_V6);
-
+ // Make sure no rules existed before calling commands.
+ for (auto const& range : uidRanges) {
+ EXPECT_FALSE(ipRuleExistsForRange(RULE_PRIORITY, range, "prohibit"));
+ }
// 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"));
}
@@ -652,15 +612,12 @@
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(int *clientSocket, int *serverSocket, int *acceptedSocket) {
- *serverSocket = socket(AF_INET6, SOCK_STREAM | SOCK_CLOEXEC, 0);
+void NetdBinderTest::fakeRemoteSocketPair(unique_fd* clientSocket, unique_fd* serverSocket,
+ unique_fd* acceptedSocket) {
+ serverSocket->reset(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)));
@@ -668,13 +625,14 @@
ASSERT_EQ(0, getsockname(*serverSocket, (struct sockaddr *) &server6, &addrlen));
ASSERT_EQ(0, listen(*serverSocket, 10));
- *clientSocket = socket(AF_INET6, SOCK_STREAM | SOCK_CLOEXEC, 0);
+ clientSocket->reset(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 = accept4(*serverSocket, (struct sockaddr *) &server6, &addrlen, SOCK_CLOEXEC);
+ acceptedSocket->reset(
+ accept4(*serverSocket, (struct sockaddr*)&server6, &addrlen, SOCK_CLOEXEC));
ASSERT_NE(-1, *acceptedSocket);
ASSERT_EQ(0, memcmp(&client6, &server6, sizeof(client6)));
@@ -701,8 +659,8 @@
EXPECT_EQ(ECONNRESET, err);
}
-TEST_F(BinderTest, SocketDestroy) {
- int clientSocket, serverSocket, acceptedSocket;
+TEST_F(NetdBinderTest, SocketDestroy) {
+ unique_fd clientSocket, serverSocket, acceptedSocket;
ASSERT_NO_FATAL_FAILURE(fakeRemoteSocketPair(&clientSocket, &serverSocket, &acceptedSocket));
// Pick a random UID in the system UID range.
@@ -742,10 +700,6 @@
skipUids.resize(skipUids.size() - 1);
EXPECT_TRUE(mNetd->socketDestroy(uidRanges, skipUids).isOk());
checkSocketpairClosed(clientSocket, acceptedSocket);
-
- close(clientSocket);
- close(serverSocket);
- close(acceptedSocket);
}
namespace {
@@ -851,39 +805,43 @@
} // namespace
-TEST_F(BinderTest, InterfaceAddRemoveAddress) {
+TEST_F(NetdBinderTest, InterfaceAddRemoveAddress) {
static const struct TestData {
const char *addrString;
const int prefixLength;
- const bool expectSuccess;
+ const int expectAddResult;
+ const int expectRemoveResult;
} kTestData[] = {
- { "192.0.2.1", 24, true },
- { "192.0.2.2", 25, true },
- { "192.0.2.3", 32, true },
- { "192.0.2.4", 33, false },
- { "192.not.an.ip", 24, false },
- { "2001:db8::1", 64, true },
- { "2001:db8::2", 65, true },
- { "2001:db8::3", 128, true },
- { "2001:db8::4", 129, false },
- { "foo:bar::bad", 64, false },
+ {"192.0.2.1", 24, 0, 0},
+ {"192.0.2.2", 25, 0, 0},
+ {"192.0.2.3", 32, 0, 0},
+ {"192.0.2.4", 33, EINVAL, EADDRNOTAVAIL},
+ {"192.not.an.ip", 24, EINVAL, EINVAL},
+ {"2001:db8::1", 64, 0, 0},
+ {"2001:db8::2", 65, 0, 0},
+ {"2001:db8::3", 128, 0, 0},
+ {"2001:db8::4", 129, EINVAL, EINVAL},
+ {"foo:bar::bad", 64, EINVAL, EINVAL},
+ {"2001:db8::1/64", 64, EINVAL, EINVAL},
};
for (size_t i = 0; i < std::size(kTestData); i++) {
const auto &td = kTestData[i];
+ SCOPED_TRACE(String8::format("Offending IP address %s/%d", td.addrString, td.prefixLength));
+
// [1.a] Add the address.
binder::Status status = mNetd->interfaceAddAddress(
sTun.name(), td.addrString, td.prefixLength);
- if (td.expectSuccess) {
+ if (td.expectAddResult == 0) {
EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
} else {
ASSERT_EQ(binder::Status::EX_SERVICE_SPECIFIC, status.exceptionCode());
- ASSERT_NE(0, status.serviceSpecificErrorCode());
+ ASSERT_EQ(td.expectAddResult, status.serviceSpecificErrorCode());
}
// [1.b] Verify the addition meets the expectation.
- if (td.expectSuccess) {
+ if (td.expectAddResult == 0) {
EXPECT_TRUE(interfaceHasAddress(sTun.name(), td.addrString, td.prefixLength));
} else {
EXPECT_FALSE(interfaceHasAddress(sTun.name(), td.addrString, -1));
@@ -891,19 +849,32 @@
// [2.a] Try to remove the address. If it was not previously added, removing it fails.
status = mNetd->interfaceDelAddress(sTun.name(), td.addrString, td.prefixLength);
- if (td.expectSuccess) {
+ if (td.expectRemoveResult == 0) {
EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
} else {
ASSERT_EQ(binder::Status::EX_SERVICE_SPECIFIC, status.exceptionCode());
- ASSERT_NE(0, status.serviceSpecificErrorCode());
+ ASSERT_EQ(td.expectRemoveResult, status.serviceSpecificErrorCode());
}
// [2.b] No matter what, the address should not be present.
EXPECT_FALSE(interfaceHasAddress(sTun.name(), td.addrString, -1));
}
+
+ // Check that netlink errors are returned correctly.
+ // We do this by attempting to create an IPv6 address on an interface that has IPv6 disabled,
+ // which returns EACCES.
+ TunInterface tun;
+ ASSERT_EQ(0, tun.init());
+ binder::Status status =
+ mNetd->setProcSysNet(INetd::IPV6, INetd::CONF, tun.name(), "disable_ipv6", "1");
+ ASSERT_TRUE(status.isOk()) << status.exceptionMessage();
+ status = mNetd->interfaceAddAddress(tun.name(), "2001:db8::1", 64);
+ EXPECT_EQ(binder::Status::EX_SERVICE_SPECIFIC, status.exceptionCode());
+ EXPECT_EQ(EACCES, status.serviceSpecificErrorCode());
+ tun.destroy();
}
-TEST_F(BinderTest, GetProcSysNet) {
+TEST_F(NetdBinderTest, GetProcSysNet) {
const char LOOPBACK[] = "lo";
static const struct {
const int ipversion;
@@ -942,7 +913,7 @@
}
}
-TEST_F(BinderTest, SetProcSysNet) {
+TEST_F(NetdBinderTest, SetProcSysNet) {
static const struct {
const int ipversion;
const int which;
@@ -977,7 +948,7 @@
}
}
-TEST_F(BinderTest, GetSetProcSysNet) {
+TEST_F(NetdBinderTest, GetSetProcSysNet) {
const int ipversion = INetd::IPV6;
const int category = INetd::NEIGH;
const std::string& tun = sTun.name();
@@ -1035,7 +1006,7 @@
} // namespace
-TEST_F(BinderTest, TetherGetStats) {
+TEST_F(NetdBinderTest, TetherGetStats) {
expectNoTestCounterRules();
// TODO: fold this into more comprehensive tests once we have binder RPCs for enabling and
@@ -1047,8 +1018,11 @@
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));
- std::string extIface1 = StringPrintf("netdtest_%u", arc4random_uniform(10000));
- std::string extIface2 = 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);
addTetherCounterValues(IPTABLES_PATH, intIface1, extIface1, 123, 111);
addTetherCounterValues(IP6TABLES_PATH, intIface1, extIface1, 456, 10);
@@ -1139,7 +1113,7 @@
} // namespace
-TEST_F(BinderTest, IdletimerAddRemoveInterface) {
+TEST_F(NetdBinderTest, IdletimerAddRemoveInterface) {
// TODO: We will get error in if expectIdletimerInterfaceRuleNotExists if there are the same
// rule in the table. Because we only check the result after calling remove function. We might
// check the actual rule which is removed by our function (maybe compare the results between
@@ -1203,7 +1177,7 @@
} // namespace
-TEST_F(BinderTest, StrictSetUidCleartextPenalty) {
+TEST_F(NetdBinderTest, StrictSetUidCleartextPenalty) {
binder::Status status;
int32_t uid = randomUid();
@@ -1268,7 +1242,7 @@
} // namespace
-TEST_F(BinderTest, ClatdStartStop) {
+TEST_F(NetdBinderTest, ClatdStartStop) {
binder::Status status;
const std::string clatdName = StringPrintf("clatd-%s", sTun.name().c_str());
@@ -1388,7 +1362,7 @@
} // namespace
-TEST_F(BinderTest, TestIpfwdEnableDisableStatusForwarding) {
+TEST_F(NetdBinderTest, TestIpfwdEnableDisableStatusForwarding) {
// Get ipfwd requester list from Netd
std::vector<std::string> requesterList;
binder::Status status = mNetd->ipfwdGetRequesterList(&requesterList);
@@ -1441,23 +1415,20 @@
}
}
-TEST_F(BinderTest, TestIpfwdAddRemoveInterfaceForward) {
- // Add test physical network
- EXPECT_TRUE(
- mNetd->networkCreatePhysical(TEST_NETID1, INetd::PERMISSION_NONE).isOk());
- EXPECT_TRUE(mNetd->networkAddInterface(TEST_NETID1, sTun.name()).isOk());
- EXPECT_TRUE(
- mNetd->networkCreatePhysical(TEST_NETID2, INetd::PERMISSION_NONE).isOk());
- EXPECT_TRUE(mNetd->networkAddInterface(TEST_NETID2, sTun2.name()).isOk());
+TEST_F(NetdBinderTest, TestIpfwdAddRemoveInterfaceForward) {
+ // Add test physical network
+ EXPECT_TRUE(mNetd->networkCreatePhysical(TEST_NETID1, INetd::PERMISSION_NONE).isOk());
+ EXPECT_TRUE(mNetd->networkAddInterface(TEST_NETID1, sTun.name()).isOk());
+ EXPECT_TRUE(mNetd->networkCreatePhysical(TEST_NETID2, INetd::PERMISSION_NONE).isOk());
+ EXPECT_TRUE(mNetd->networkAddInterface(TEST_NETID2, sTun2.name()).isOk());
- binder::Status status =
- mNetd->ipfwdAddInterfaceForward(sTun.name(), sTun2.name());
- EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
- expectIpfwdRuleExists(sTun.name().c_str(), sTun2.name().c_str());
+ binder::Status status = mNetd->ipfwdAddInterfaceForward(sTun.name(), sTun2.name());
+ EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+ expectIpfwdRuleExists(sTun.name().c_str(), sTun2.name().c_str());
- status = mNetd->ipfwdRemoveInterfaceForward(sTun.name(), sTun2.name());
- EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
- expectIpfwdRuleNotExists(sTun.name().c_str(), sTun2.name().c_str());
+ status = mNetd->ipfwdRemoveInterfaceForward(sTun.name(), sTun2.name());
+ EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+ expectIpfwdRuleNotExists(sTun.name().c_str(), sTun2.name().c_str());
}
namespace {
@@ -1577,7 +1548,7 @@
} // namespace
-TEST_F(BinderTest, BandwidthSetRemoveInterfaceQuota) {
+TEST_F(NetdBinderTest, BandwidthSetRemoveInterfaceQuota) {
long testQuotaBytes = 5550;
// Add test physical network
@@ -1596,7 +1567,7 @@
EXPECT_TRUE(mNetd->networkDestroy(TEST_NETID1).isOk());
}
-TEST_F(BinderTest, BandwidthSetRemoveInterfaceAlert) {
+TEST_F(NetdBinderTest, BandwidthSetRemoveInterfaceAlert) {
long testAlertBytes = 373;
// Add test physical network
EXPECT_TRUE(mNetd->networkCreatePhysical(TEST_NETID1, INetd::PERMISSION_NONE).isOk());
@@ -1620,20 +1591,20 @@
EXPECT_TRUE(mNetd->networkDestroy(TEST_NETID1).isOk());
}
-TEST_F(BinderTest, BandwidthSetGlobalAlert) {
- long testAlertBytes = 2097149;
+TEST_F(NetdBinderTest, BandwidthSetGlobalAlert) {
+ int64_t testAlertBytes = 2097200;
binder::Status status = mNetd->bandwidthSetGlobalAlert(testAlertBytes);
EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
expectBandwidthGlobalAlertRuleExists(testAlertBytes);
- testAlertBytes = 2097152;
+ testAlertBytes = 2098230;
status = mNetd->bandwidthSetGlobalAlert(testAlertBytes);
EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
expectBandwidthGlobalAlertRuleExists(testAlertBytes);
}
-TEST_F(BinderTest, BandwidthManipulateSpecialApp) {
+TEST_F(NetdBinderTest, BandwidthManipulateSpecialApp) {
SKIP_IF_BPF_SUPPORTED;
int32_t uid = randomUid();
@@ -1663,42 +1634,53 @@
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) {
+ const std::string& nextHop, const std::string& mtu) {
std::string dstString = (dst == "0.0.0.0/0" || dst == "::/0") ? "default" : dst;
if (!nextHop.empty()) {
dstString += " via " + nextHop;
}
- return dstString + " dev " + ifName;
+ dstString += " dev " + ifName;
+
+ if (!mtu.empty()) {
+ dstString += " proto static";
+ // IPv6 routes report the metric, IPv4 routes report the scope.
+ // TODO: move away from specifying the entire string and use a regexp instead.
+ if (dst.find(':') != std::string::npos) {
+ dstString += " metric 1024";
+ } else {
+ if (nextHop.empty()) {
+ dstString += " scope link";
+ }
+ }
+ dstString += " mtu " + mtu;
+ }
+
+ return dstString;
+}
+
+void expectNetworkRouteExistsWithMtu(const char* ipVersion, const std::string& ifName,
+ const std::string& dst, const std::string& nextHop,
+ const std::string& mtu, const char* table) {
+ std::string routeString = ipRouteString(ifName, dst, nextHop, mtu);
+ EXPECT_TRUE(ipRouteExists(ipVersion, table, ipRouteString(ifName, dst, nextHop, mtu)))
+ << "Couldn't find route to " << dst << ": '" << routeString << "' in table " << table;
}
void expectNetworkRouteExists(const char* ipVersion, const std::string& ifName,
const std::string& dst, const std::string& nextHop,
const char* table) {
- EXPECT_TRUE(ipRouteExists(ipVersion, table, ipRouteString(ifName, dst, nextHop)));
+ expectNetworkRouteExistsWithMtu(ipVersion, ifName, dst, nextHop, "", table);
}
void expectNetworkRouteDoesNotExist(const char* ipVersion, const std::string& ifName,
const std::string& dst, const std::string& nextHop,
const char* table) {
- EXPECT_FALSE(ipRouteExists(ipVersion, table, ipRouteString(ifName, dst, nextHop)));
+ std::string routeString = ipRouteString(ifName, dst, nextHop, "");
+ EXPECT_FALSE(ipRouteExists(ipVersion, table, ipRouteString(ifName, dst, nextHop, "")))
+ << "Found unexpected route " << routeString << " in table " << table;
}
bool ipRuleExists(const char* ipVersion, const std::string& ipRule) {
@@ -1789,7 +1771,7 @@
} // namespace
-TEST_F(BinderTest, NetworkAddRemoveRouteUserPermission) {
+TEST_F(NetdBinderTest, NetworkAddRemoveRouteUserPermission) {
static const struct {
const char* ipVersion;
const char* testDest;
@@ -1930,11 +1912,51 @@
}
}
+ for (size_t i = 0; i < std::size(kTestData); i++) {
+ const auto& td = kTestData[i];
+ int mtu = (i % 2) ? 1480 : 1280;
+
+ android::net::RouteInfoParcel parcel;
+ parcel.ifName = sTun.name();
+ parcel.destination = td.testDest;
+ parcel.nextHop = td.testNextHop;
+ parcel.mtu = mtu;
+ binder::Status status = mNetd->networkAddRouteParcel(TEST_NETID1, parcel);
+ if (td.expectSuccess) {
+ EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+ expectNetworkRouteExistsWithMtu(td.ipVersion, sTun.name(), td.testDest, td.testNextHop,
+ std::to_string(parcel.mtu), sTun.name().c_str());
+ } else {
+ EXPECT_EQ(binder::Status::EX_SERVICE_SPECIFIC, status.exceptionCode());
+ EXPECT_NE(0, status.serviceSpecificErrorCode());
+ }
+
+ parcel.mtu = 1337;
+ status = mNetd->networkUpdateRouteParcel(TEST_NETID1, parcel);
+ if (td.expectSuccess) {
+ EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+ expectNetworkRouteExistsWithMtu(td.ipVersion, sTun.name(), td.testDest, td.testNextHop,
+ std::to_string(parcel.mtu), sTun.name().c_str());
+ } else {
+ EXPECT_EQ(binder::Status::EX_SERVICE_SPECIFIC, status.exceptionCode());
+ EXPECT_NE(0, status.serviceSpecificErrorCode());
+ }
+
+ status = mNetd->networkRemoveRouteParcel(TEST_NETID1, parcel);
+ if (td.expectSuccess) {
+ EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+ expectNetworkRouteDoesNotExist(td.ipVersion, sTun.name(), td.testDest, td.testNextHop,
+ sTun.name().c_str());
+ } else {
+ EXPECT_EQ(binder::Status::EX_SERVICE_SPECIFIC, status.exceptionCode());
+ EXPECT_NE(0, status.serviceSpecificErrorCode());
+ }
+ }
// Remove test physical network
EXPECT_TRUE(mNetd->networkDestroy(TEST_NETID1).isOk());
}
-TEST_F(BinderTest, NetworkPermissionDefault) {
+TEST_F(NetdBinderTest, NetworkPermissionDefault) {
// Add test physical network
EXPECT_TRUE(mNetd->networkCreatePhysical(TEST_NETID1, INetd::PERMISSION_NONE).isOk());
EXPECT_TRUE(mNetd->networkAddInterface(TEST_NETID1, sTun.name()).isOk());
@@ -1971,17 +1993,21 @@
EXPECT_TRUE(mNetd->networkDestroy(TEST_NETID1).isOk());
}
-TEST_F(BinderTest, NetworkSetProtectAllowDeny) {
- const int testUid = randomUid();
- binder::Status status = mNetd->networkSetProtectAllow(testUid);
+TEST_F(NetdBinderTest, NetworkSetProtectAllowDeny) {
+ binder::Status status = mNetd->networkSetProtectAllow(TEST_UID1);
EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
bool ret = false;
- status = mNetd->networkCanProtect(testUid, &ret);
+ status = mNetd->networkCanProtect(TEST_UID1, &ret);
EXPECT_TRUE(ret);
- status = mNetd->networkSetProtectDeny(testUid);
+ status = mNetd->networkSetProtectDeny(TEST_UID1);
EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
- status = mNetd->networkCanProtect(testUid, &ret);
+
+ // 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);
EXPECT_FALSE(ret);
}
@@ -2054,29 +2080,37 @@
} // namespace
-TEST_F(BinderTest, TetherStartStopStatus) {
+TEST_F(NetdBinderTest, TetherStartStopStatus) {
std::vector<std::string> noDhcpRange = {};
- static const char dnsdName[] = "dnsmasq";
+ for (bool usingLegacyDnsProxy : {true, false}) {
+ android::net::TetherConfigParcel config;
+ config.usingLegacyDnsProxy = usingLegacyDnsProxy;
+ config.dhcpRanges = noDhcpRange;
+ binder::Status status = mNetd->tetherStartWithConfiguration(config);
+ EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+ SCOPED_TRACE(StringPrintf("usingLegacyDnsProxy: %d", usingLegacyDnsProxy));
+ if (usingLegacyDnsProxy == true) {
+ expectProcessExists(DNSMASQ);
+ } else {
+ expectProcessDoesNotExist(DNSMASQ);
+ }
- 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(DNSMASQ);
- 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) {
+TEST_F(NetdBinderTest, TetherInterfaceAddRemoveList) {
// TODO: verify if dnsmasq update interface successfully
binder::Status status = mNetd->tetherInterfaceAdd(sTun.name());
@@ -2097,7 +2131,7 @@
expectTetherInterfaceNotExists(ifList, sTun.name());
}
-TEST_F(BinderTest, TetherDnsSetList) {
+TEST_F(NetdBinderTest, TetherDnsSetList) {
// TODO: verify if dnsmasq update dns successfully
std::vector<std::string> testDnsAddrs = {"192.168.1.37", "213.137.100.3",
"fe80::1%" + sTun.name()};
@@ -2113,6 +2147,135 @@
namespace {
+std::vector<IPAddress> findDnsSockets(SockDiag* sd, unsigned numExpected) {
+ std::vector<IPAddress> listenAddrs;
+
+ // Callback lambda that finds all IPv4 sockets with source port 53.
+ auto findDnsSockets = [&](uint8_t /* proto */, const inet_diag_msg* msg) {
+ // Always return false, which means do not destroy this socket.
+ if (msg->id.idiag_sport != htons(53)) return false;
+ IPAddress addr(*(in_addr*)msg->id.idiag_src);
+ listenAddrs.push_back(addr);
+ return false;
+ };
+
+ // There is no way to know if dnsmasq has finished processing the update_interfaces command and
+ // opened listening sockets. So, just spin a few times and return the first list of sockets
+ // that is at least numExpected long.
+ // Pick a relatively large timeout to avoid flaky tests, particularly when running on shared
+ // devices.
+ constexpr int kMaxAttempts = 50;
+ constexpr int kSleepMs = 100;
+ for (int i = 0; i < kMaxAttempts; i++) {
+ listenAddrs.clear();
+ EXPECT_EQ(0, sd->sendDumpRequest(IPPROTO_TCP, AF_INET, 1 << TCP_LISTEN))
+ << "Failed to dump sockets, attempt " << i << " of " << kMaxAttempts;
+ sd->readDiagMsg(IPPROTO_TCP, findDnsSockets);
+ if (listenAddrs.size() >= numExpected) {
+ break;
+ }
+ usleep(kSleepMs * 1000);
+ }
+
+ return listenAddrs;
+}
+
+} // namespace
+
+// Checks that when starting dnsmasq on an interface that no longer exists, it doesn't attempt to
+// start on other interfaces instead.
+TEST_F(NetdBinderTest, TetherDeletedInterface) {
+ // Do this first so we don't need to clean up anything else if it fails.
+ SockDiag sd;
+ ASSERT_TRUE(sd.open()) << "Failed to open SOCK_DIAG socket";
+
+ // Create our own TunInterfaces (so we can delete them without affecting other tests), and add
+ // IP addresses to them. They must be IPv4 because tethering an interface disables and
+ // re-enables IPv6 on the interface, which clears all addresses.
+ TunInterface tun1, tun2;
+ ASSERT_EQ(0, tun1.init());
+ ASSERT_EQ(0, tun2.init());
+
+ // Clean up. It is safe to call TunInterface::destroy multiple times.
+ auto guard = android::base::make_scope_guard([&] {
+ tun1.destroy();
+ tun2.destroy();
+ mNetd->tetherStop();
+ mNetd->tetherInterfaceRemove(tun1.name());
+ mNetd->tetherInterfaceRemove(tun2.name());
+ });
+
+ IPAddress addr1, addr2;
+ ASSERT_TRUE(IPAddress::forString("192.0.2.1", &addr1));
+ ASSERT_TRUE(IPAddress::forString("192.0.2.2", &addr2));
+ EXPECT_EQ(0, tun1.addAddress(addr1.toString(), 32));
+ EXPECT_EQ(0, tun2.addAddress(addr2.toString(), 32));
+
+ // Stop tethering.
+ binder::Status status = mNetd->tetherStop();
+ EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+
+ // Start dnsmasq on an interface that doesn't exist.
+ // First, tether our tun interface...
+ status = mNetd->tetherInterfaceAdd(tun1.name());
+ EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+ expectTetherInterfaceConfigureForIPv6Router(tun1.name());
+
+ // ... then delete it...
+ tun1.destroy();
+
+ // ... then start dnsmasq.
+ android::net::TetherConfigParcel config;
+ config.usingLegacyDnsProxy = true;
+ config.dhcpRanges = {};
+ status = mNetd->tetherStartWithConfiguration(config);
+ EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+
+ // Wait for dnsmasq to start.
+ expectProcessExists(DNSMASQ);
+
+ // Make sure that netd thinks the interface is tethered (even though it doesn't exist).
+ std::vector<std::string> ifList;
+ status = mNetd->tetherInterfaceList(&ifList);
+ EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+ ASSERT_EQ(1U, ifList.size());
+ EXPECT_EQ(tun1.name(), ifList[0]);
+
+ // Give dnsmasq some time to start up.
+ usleep(200 * 1000);
+
+ // Check that dnsmasq is not listening on any IP addresses. It shouldn't, because it was only
+ // told to run on tun1, and tun1 does not exist. Ensure it stays running and doesn't listen on
+ // any IP addresses.
+ std::vector<IPAddress> listenAddrs = findDnsSockets(&sd, 0);
+ EXPECT_EQ(0U, listenAddrs.size()) << "Unexpectedly found IPv4 socket(s) listening on port 53";
+
+ // Now add an interface to dnsmasq and check that we can see the sockets. This confirms that
+ // findDnsSockets is actually able to see sockets when they exist.
+ status = mNetd->tetherInterfaceAdd(tun2.name());
+ EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+
+ in_addr loopback = {htonl(INADDR_LOOPBACK)};
+ listenAddrs = findDnsSockets(&sd, 2);
+ EXPECT_EQ(2U, listenAddrs.size()) << "Expected exactly 2 IPv4 sockets listening on port 53";
+ EXPECT_EQ(1, std::count(listenAddrs.begin(), listenAddrs.end(), addr2));
+ EXPECT_EQ(1, std::count(listenAddrs.begin(), listenAddrs.end(), IPAddress(loopback)));
+
+ // Clean up.
+ status = mNetd->tetherStop();
+ EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+
+ expectProcessDoesNotExist(DNSMASQ);
+
+ status = mNetd->tetherInterfaceRemove(tun1.name());
+ EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+
+ status = mNetd->tetherInterfaceRemove(tun2.name());
+ EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+}
+
+namespace {
+
constexpr char FIREWALL_INPUT[] = "fw_INPUT";
constexpr char FIREWALL_OUTPUT[] = "fw_OUTPUT";
constexpr char FIREWALL_FORWARD[] = "fw_FORWARD";
@@ -2277,7 +2440,7 @@
} // namespace
-TEST_F(BinderTest, FirewallSetFirewallType) {
+TEST_F(NetdBinderTest, FirewallSetFirewallType) {
binder::Status status = mNetd->firewallSetFirewallType(INetd::FIREWALL_WHITELIST);
EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
expectFirewallWhitelistMode();
@@ -2304,7 +2467,7 @@
expectFirewallBlacklistMode();
}
-TEST_F(BinderTest, FirewallSetInterfaceRule) {
+TEST_F(NetdBinderTest, FirewallSetInterfaceRule) {
// setinterfaceRule is not supported in BLACKLIST MODE
binder::Status status = mNetd->firewallSetFirewallType(INetd::FIREWALL_BLACKLIST);
EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
@@ -2330,7 +2493,7 @@
expectFirewallBlacklistMode();
}
-TEST_F(BinderTest, FirewallSetUidRule) {
+TEST_F(NetdBinderTest, FirewallSetUidRule) {
SKIP_IF_BPF_SUPPORTED;
int32_t uid = randomUid();
@@ -2406,7 +2569,7 @@
expectFirewallBlacklistMode();
}
-TEST_F(BinderTest, FirewallEnableDisableChildChains) {
+TEST_F(NetdBinderTest, FirewallEnableDisableChildChains) {
SKIP_IF_BPF_SUPPORTED;
binder::Status status = mNetd->firewallEnableChildChain(INetd::FIREWALL_CHAIN_DOZABLE, true);
@@ -2622,7 +2785,7 @@
} // namespace
-TEST_F(BinderTest, InterfaceList) {
+TEST_F(NetdBinderTest, InterfaceList) {
std::vector<std::string> interfaceListResult;
binder::Status status = mNetd->interfaceGetList(&interfaceListResult);
@@ -2630,7 +2793,7 @@
expectInterfaceList(interfaceListResult);
}
-TEST_F(BinderTest, InterfaceGetCfg) {
+TEST_F(NetdBinderTest, InterfaceGetCfg) {
InterfaceConfigurationParcel interfaceCfgResult;
// Add test physical network
@@ -2646,7 +2809,7 @@
EXPECT_TRUE(mNetd->networkDestroy(TEST_NETID1).isOk());
}
-TEST_F(BinderTest, InterfaceSetCfg) {
+TEST_F(NetdBinderTest, InterfaceSetCfg) {
const std::string testAddr = "192.0.2.3";
const int testPrefixLength = 24;
std::vector<std::string> upFlags = {"up"};
@@ -2673,7 +2836,7 @@
EXPECT_TRUE(mNetd->networkDestroy(TEST_NETID1).isOk());
}
-TEST_F(BinderTest, InterfaceSetIPv6PrivacyExtensions) {
+TEST_F(NetdBinderTest, InterfaceSetIPv6PrivacyExtensions) {
// enable
binder::Status status = mNetd->interfaceSetIPv6PrivacyExtensions(sTun.name(), true);
EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
@@ -2685,7 +2848,7 @@
expectInterfaceIPv6PrivacyExtensions(sTun.name(), false);
}
-TEST_F(BinderTest, InterfaceClearAddr) {
+TEST_F(NetdBinderTest, InterfaceClearAddr) {
const std::string testAddr = "192.0.2.3";
const int testPrefixLength = 24;
std::vector<std::string> noFlags{};
@@ -2707,7 +2870,7 @@
EXPECT_TRUE(mNetd->networkDestroy(TEST_NETID1).isOk());
}
-TEST_F(BinderTest, InterfaceSetEnableIPv6) {
+TEST_F(NetdBinderTest, InterfaceSetEnableIPv6) {
// Add test physical network
EXPECT_TRUE(mNetd->networkCreatePhysical(TEST_NETID1, INetd::PERMISSION_NONE).isOk());
EXPECT_TRUE(mNetd->networkAddInterface(TEST_NETID1, sTun.name()).isOk());
@@ -2726,7 +2889,7 @@
EXPECT_TRUE(mNetd->networkDestroy(TEST_NETID1).isOk());
}
-TEST_F(BinderTest, InterfaceSetMtu) {
+TEST_F(NetdBinderTest, InterfaceSetMtu) {
const int testMtu = 1200;
// Add test physical network
@@ -2826,7 +2989,7 @@
} // namespace
-TEST_F(BinderTest, TetherForwardAddRemove) {
+TEST_F(NetdBinderTest, TetherForwardAddRemove) {
binder::Status status = mNetd->tetherAddForward(sTun.name(), sTun2.name());
EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
expectNatEnable(sTun.name(), sTun2.name());
@@ -2867,7 +3030,7 @@
} // namespace
-TEST_F(BinderTest, TcpBufferSet) {
+TEST_F(NetdBinderTest, TcpBufferSet) {
TripleInt rmemValue = readProcFileToTripleInt(TCP_RMEM_PROC_FILE);
TripleInt testRmemValue{rmemValue[0] + 42, rmemValue[1] + 42, rmemValue[2] + 42};
TripleInt wmemValue = readProcFileToTripleInt(TCP_WMEM_PROC_FILE);
@@ -2880,23 +3043,22 @@
namespace {
void checkUidsInPermissionMap(std::vector<int32_t>& uids, bool exist) {
- android::bpf::BpfMap<uint32_t, uint8_t> uidPermissionMap(
- android::bpf::mapRetrieve(UID_PERMISSION_MAP_PATH, 0));
+ android::bpf::BpfMap<uint32_t, uint8_t> uidPermissionMap(UID_PERMISSION_MAP_PATH);
for (int32_t uid : uids) {
- android::netdutils::StatusOr<uint8_t> permission = uidPermissionMap.readValue(uid);
+ android::base::Result<uint8_t> permission = uidPermissionMap.readValue(uid);
if (exist) {
- EXPECT_TRUE(isOk(permission));
+ ASSERT_RESULT_OK(permission);
EXPECT_EQ(INetd::PERMISSION_NONE, permission.value());
} else {
- EXPECT_FALSE(isOk(permission));
- EXPECT_EQ(ENOENT, permission.status().code());
+ ASSERT_FALSE(permission.ok());
+ EXPECT_EQ(ENOENT, permission.error().code());
}
}
}
} // namespace
-TEST_F(BinderTest, TestInternetPermission) {
+TEST_F(NetdBinderTest, TestInternetPermission) {
SKIP_IF_BPF_NOT_SUPPORTED;
std::vector<int32_t> appUids = {TEST_UID1, TEST_UID2};
@@ -2909,7 +3071,7 @@
checkUidsInPermissionMap(appUids, false);
}
-TEST_F(BinderTest, UnsolEvents) {
+TEST_F(NetdBinderTest, UnsolEvents) {
auto testUnsolService = android::net::TestUnsolService::start();
std::string oldTunName = sTun.name();
std::string newTunName = "unsolTest";
@@ -2948,7 +3110,7 @@
sTun.init();
}
-TEST_F(BinderTest, NDC) {
+TEST_F(NetdBinderTest, NDC) {
struct Command {
const std::string cmdString;
const std::string expectedResult;
@@ -3047,7 +3209,7 @@
EXPECT_TRUE(mNetd->networkDestroy(TEST_NETID1).isOk());
}
-TEST_F(BinderTest, OemNetdRelated) {
+TEST_F(NetdBinderTest, OemNetdRelated) {
sp<IBinder> binder;
binder::Status status = mNetd->getOemNetd(&binder);
EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
@@ -3100,8 +3262,8 @@
}
}
-void BinderTest::createVpnNetworkWithUid(bool secure, uid_t uid, int vpnNetId,
- int fallthroughNetId) {
+void NetdBinderTest::createVpnNetworkWithUid(bool secure, uid_t uid, int vpnNetId,
+ int fallthroughNetId) {
// Re-init sTun* to ensure route rule exists.
sTun.destroy();
sTun.init();
@@ -3166,10 +3328,14 @@
bool sendIPv6PacketFromUid(uid_t uid, const in6_addr& dstAddr, Fwmark* fwmark, int tunFd) {
ScopedUidChange scopedUidChange(uid);
- android::base::unique_fd testSocket(socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0));
+ 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_addr = dstAddr, .sin6_port = 42};
+ const sockaddr_in6 dst6 = {
+ .sin6_family = AF_INET6,
+ .sin6_port = 42,
+ .sin6_addr = dstAddr,
+ };
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));
@@ -3270,16 +3436,320 @@
} // namespace
-TEST_F(BinderTest, SecureVPNFallthrough) {
+TEST_F(NetdBinderTest, SecureVPNFallthrough) {
createVpnNetworkWithUid(true /* secure */, TEST_UID1);
// Get current default network NetId
ASSERT_TRUE(mNetd->networkGetDefault(&mStoredDefaultNetwork).isOk());
expectVpnFallthroughWorks(mNetd.get(), false /* bypassable */, TEST_UID1, sTun, sTun2);
}
-TEST_F(BinderTest, BypassableVPNFallthrough) {
+TEST_F(NetdBinderTest, BypassableVPNFallthrough) {
createVpnNetworkWithUid(false /* secure */, TEST_UID1);
// 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
+}
+
+namespace {
+
+int32_t createIpv6SocketAndCheckMark(int type, const in6_addr& dstAddr) {
+ const sockaddr_in6 dst6 = {
+ .sin6_family = AF_INET6,
+ .sin6_port = 1234,
+ .sin6_addr = dstAddr,
+ };
+ // create non-blocking socket.
+ int sockFd = socket(AF_INET6, type | SOCK_NONBLOCK, 0);
+ EXPECT_NE(-1, sockFd);
+ EXPECT_EQ((type == SOCK_STREAM) ? -1 : 0, connect(sockFd, (sockaddr*)&dst6, sizeof(dst6)));
+
+ // Get socket fwmark.
+ Fwmark fwmark;
+ socklen_t fwmarkLen = sizeof(fwmark.intValue);
+ EXPECT_EQ(0, getsockopt(sockFd, SOL_SOCKET, SO_MARK, &fwmark.intValue, &fwmarkLen));
+ EXPECT_EQ(0, close(sockFd));
+ return fwmark.intValue;
+}
+
+} // namespace
+
+TEST_F(NetdBinderTest, GetFwmarkForNetwork) {
+ // Save current default network.
+ ASSERT_TRUE(mNetd->networkGetDefault(&mStoredDefaultNetwork).isOk());
+
+ in6_addr v6Addr = {
+ {// 2001:db8:cafe::8888
+ .u6_addr8 = {0x20, 0x01, 0x0d, 0xb8, 0xca, 0xfe, 0, 0, 0, 0, 0, 0, 0, 0, 0x88, 0x88}}};
+ // Add test physical network 1 and set as default network.
+ EXPECT_TRUE(mNetd->networkCreatePhysical(TEST_NETID1, INetd::PERMISSION_NONE).isOk());
+ EXPECT_TRUE(mNetd->networkAddInterface(TEST_NETID1, sTun.name()).isOk());
+ EXPECT_TRUE(mNetd->networkAddRoute(TEST_NETID1, sTun.name(), "2001:db8::/32", "").isOk());
+ EXPECT_TRUE(mNetd->networkSetDefault(TEST_NETID1).isOk());
+ // Add test physical network 2
+ EXPECT_TRUE(mNetd->networkCreatePhysical(TEST_NETID2, INetd::PERMISSION_NONE).isOk());
+ EXPECT_TRUE(mNetd->networkAddInterface(TEST_NETID2, sTun2.name()).isOk());
+
+ // Get fwmark for network 1.
+ MarkMaskParcel maskMarkNet1;
+ ASSERT_TRUE(mNetd->getFwmarkForNetwork(TEST_NETID1, &maskMarkNet1).isOk());
+
+ uint32_t fwmarkTcp = createIpv6SocketAndCheckMark(SOCK_STREAM, v6Addr);
+ uint32_t fwmarkUdp = createIpv6SocketAndCheckMark(SOCK_DGRAM, v6Addr);
+ EXPECT_EQ(maskMarkNet1.mark, static_cast<int>(fwmarkTcp & maskMarkNet1.mask));
+ EXPECT_EQ(maskMarkNet1.mark, static_cast<int>(fwmarkUdp & maskMarkNet1.mask));
+
+ // Get fwmark for network 2.
+ MarkMaskParcel maskMarkNet2;
+ ASSERT_TRUE(mNetd->getFwmarkForNetwork(TEST_NETID2, &maskMarkNet2).isOk());
+ EXPECT_NE(maskMarkNet2.mark, static_cast<int>(fwmarkTcp & maskMarkNet2.mask));
+ EXPECT_NE(maskMarkNet2.mark, static_cast<int>(fwmarkUdp & maskMarkNet2.mask));
+
+ // Remove test physical network.
+ EXPECT_TRUE(mNetd->networkDestroy(TEST_NETID2).isOk());
+ EXPECT_TRUE(mNetd->networkDestroy(TEST_NETID1).isOk());
+}
+
+namespace {
+
+TetherOffloadRuleParcel makeTetherOffloadRule(int inputInterfaceIndex, int outputInterfaceIndex,
+ const std::vector<uint8_t>& destination,
+ int prefixLength,
+ const std::vector<uint8_t>& srcL2Address,
+ const std::vector<uint8_t>& dstL2Address, int pmtu) {
+ android::net::TetherOffloadRuleParcel parcel;
+ parcel.inputInterfaceIndex = inputInterfaceIndex;
+ parcel.outputInterfaceIndex = outputInterfaceIndex;
+ parcel.destination = destination;
+ parcel.prefixLength = prefixLength;
+ parcel.srcL2Address = srcL2Address;
+ parcel.dstL2Address = dstL2Address;
+ parcel.pmtu = pmtu;
+ return parcel;
+}
+
+} // namespace
+
+TEST_F(NetdBinderTest, TetherOffloadRule) {
+ SKIP_IF_BPF_NOT_SUPPORTED;
+
+ // TODO: Perhaps verify invalid interface index once the netd handle the error in methods.
+ constexpr uint32_t kIfaceInt = 101;
+ constexpr uint32_t kIfaceExt = 102;
+ constexpr uint32_t kIfaceNonExistent = 103;
+
+ const std::vector<uint8_t> kAddr6 = {0x20, 0x01, 0x0d, 0xb8, 0xca, 0xfe, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x88};
+ const std::vector<uint8_t> kSrcMac = {0x00, 0x00, 0x00, 0x00, 0x00, 0x0a};
+ const std::vector<uint8_t> kDstMac = {0x00, 0x00, 0x00, 0x00, 0x00, 0x0b};
+
+ const std::vector<uint8_t> kInvalidAddr4 = {0xac, 0x0a, 0x0d, 0xb8}; // should be IPv6 address
+ const std::vector<uint8_t> kInvalidMac = {0xde, 0xad, 0xbe, 0xef}; // should be 6-byte length
+
+ // Invalid IP address, add rule
+ TetherOffloadRuleParcel rule = makeTetherOffloadRule(
+ kIfaceExt, kIfaceInt, kInvalidAddr4 /*bad*/, 128, kSrcMac, kDstMac, 1500);
+ auto status = mNetd->tetherOffloadRuleAdd(rule);
+ EXPECT_FALSE(status.isOk());
+ EXPECT_EQ(EAFNOSUPPORT, status.serviceSpecificErrorCode());
+
+ // Invalid source L2 address, add rule
+ rule = makeTetherOffloadRule(kIfaceExt, kIfaceInt, kAddr6, 128, kInvalidMac /*bad*/, kDstMac,
+ 1500);
+ status = mNetd->tetherOffloadRuleAdd(rule);
+ EXPECT_FALSE(status.isOk());
+ EXPECT_EQ(ENXIO, status.serviceSpecificErrorCode());
+
+ // Invalid destination L2 address, add rule
+ rule = makeTetherOffloadRule(kIfaceExt, kIfaceInt, kAddr6, 128, kSrcMac, kInvalidMac /*bad*/,
+ 1500);
+ status = mNetd->tetherOffloadRuleAdd(rule);
+ EXPECT_FALSE(status.isOk());
+ EXPECT_EQ(ENXIO, status.serviceSpecificErrorCode());
+
+ // Invalid IP address, remove rule
+ rule = makeTetherOffloadRule(kIfaceExt, kIfaceInt, kInvalidAddr4 /*bad*/, 128, kSrcMac, kDstMac,
+ 1500);
+ status = mNetd->tetherOffloadRuleRemove(rule);
+ EXPECT_FALSE(status.isOk());
+ EXPECT_EQ(EAFNOSUPPORT, status.serviceSpecificErrorCode());
+
+ // Invalid prefix length
+ rule = makeTetherOffloadRule(kIfaceExt, kIfaceInt, kAddr6, 64 /*bad*/, kSrcMac, kDstMac, 1500);
+ status = mNetd->tetherOffloadRuleAdd(rule);
+ EXPECT_FALSE(status.isOk());
+ EXPECT_EQ(EINVAL, status.serviceSpecificErrorCode());
+ status = mNetd->tetherOffloadRuleRemove(rule);
+ EXPECT_FALSE(status.isOk());
+ EXPECT_EQ(EINVAL, status.serviceSpecificErrorCode());
+
+ // Invalid interface index
+ rule = makeTetherOffloadRule(kIfaceExt, 0, kAddr6, 128, kSrcMac, kDstMac, 1500);
+ status = mNetd->tetherOffloadRuleAdd(rule);
+ EXPECT_FALSE(status.isOk());
+ EXPECT_EQ(ENODEV, status.serviceSpecificErrorCode());
+ rule = makeTetherOffloadRule(0, kIfaceInt, kAddr6, 64, kSrcMac, kDstMac, 1500);
+ status = mNetd->tetherOffloadRuleRemove(rule);
+ EXPECT_FALSE(status.isOk());
+ EXPECT_EQ(ENODEV, status.serviceSpecificErrorCode());
+
+ // Invalid pmtu (too low)
+ rule = makeTetherOffloadRule(kIfaceExt, kIfaceInt, kAddr6, 128, kSrcMac, kDstMac, 1279);
+ status = mNetd->tetherOffloadRuleAdd(rule);
+ EXPECT_FALSE(status.isOk());
+ EXPECT_EQ(EINVAL, status.serviceSpecificErrorCode());
+
+ // Invalid pmtu (too high)
+ rule = makeTetherOffloadRule(kIfaceExt, kIfaceInt, kAddr6, 128, kSrcMac, kDstMac, 65536);
+ status = mNetd->tetherOffloadRuleAdd(rule);
+ EXPECT_FALSE(status.isOk());
+ EXPECT_EQ(EINVAL, status.serviceSpecificErrorCode());
+
+ // Remove non existent rule. Expect that silently return success if the rule did not exist.
+ rule = makeTetherOffloadRule(kIfaceNonExistent, kIfaceInt, kAddr6, 128, kSrcMac, kDstMac, 1500);
+ EXPECT_TRUE(mNetd->tetherOffloadRuleRemove(rule).isOk());
+
+ // Add and remove rule normally.
+ rule = makeTetherOffloadRule(kIfaceExt, kIfaceInt, kAddr6, 128, kSrcMac, kDstMac, 1500);
+ EXPECT_TRUE(mNetd->tetherOffloadRuleAdd(rule).isOk());
+ EXPECT_TRUE(mNetd->tetherOffloadRuleRemove(rule).isOk());
+}
+
+static bool expectPacket(int fd, uint8_t* ipPacket, ssize_t ipLen) {
+ constexpr bool kDebug = false;
+
+ uint8_t buf[ETHER_HDR_LEN + 1500];
+
+ // Wait a bit to ensure that the packet we're interested in has arrived.
+ // TODO: speed this up.
+ usleep(100 * 1000);
+
+ ssize_t bytesRead;
+ ssize_t expectedLen = ipLen + ETHER_HDR_LEN;
+ while ((bytesRead = read(fd, buf, sizeof(buf))) >= 0) {
+ if (kDebug) {
+ std::cerr << fmt::format(
+ "Expected: {:02x}\n Actual: {:02x}\n",
+ fmt::join(ipPacket, ipPacket + ipLen, " "),
+ fmt::join(buf + ETHER_HDR_LEN, buf + ETHER_HDR_LEN + ipLen, " "));
+ }
+
+ if (bytesRead != expectedLen) {
+ continue;
+ }
+
+ if (!memcmp(ipPacket, buf + ETHER_HDR_LEN, ipLen)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+TEST_F(NetdBinderTest, TetherOffloadForwarding) {
+ SKIP_IF_EXTENDED_BPF_NOT_SUPPORTED;
+
+ constexpr const char* kDownstreamPrefix = "2001:db8:2::/64";
+
+ // 1500-byte packet.
+ constexpr unsigned short kPayloadLen = 1500 - sizeof(ipv6hdr);
+ struct packet {
+ ipv6hdr hdr;
+ char data[kPayloadLen];
+ } __attribute__((packed)) pkt = {
+ .hdr =
+ {
+ .version = 6,
+ .payload_len = htons(kPayloadLen),
+ .nexthdr = 59, // No next header.
+ .hop_limit = 64,
+ .saddr = {{{0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}}},
+ .daddr = {{{0x20, 0x01, 0x0d, 0xb8, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x0f, 0x00, 0xca, 0xfe}}},
+ },
+ };
+ ASSERT_EQ(1500U, sizeof(pkt));
+
+ // Use one of the test's tun interfaces as upstream.
+ // It must be part of a network or it will not have the clsact attached.
+ EXPECT_TRUE(mNetd->networkCreatePhysical(TEST_NETID1, INetd::PERMISSION_NONE).isOk());
+ EXPECT_TRUE(mNetd->networkAddInterface(TEST_NETID1, sTun.name()).isOk());
+ int fd1 = sTun.getFdForTesting();
+
+ // Create our own tap as a downstream.
+ TunInterface tap;
+ ASSERT_EQ(0, tap.init(true /* isTap */));
+ ASSERT_LE(tap.name().size(), static_cast<size_t>(IFNAMSIZ));
+ int fd2 = tap.getFdForTesting();
+
+ // Set it to nonblocking so that expectPacket can work.
+ int flags = fcntl(fd2, F_GETFL, 0);
+ fcntl(fd2, F_SETFL, flags | O_NONBLOCK);
+
+ // Downstream interface setup. Add to local network, add directly-connected route, etc.
+ binder::Status status = mNetd->networkAddInterface(INetd::LOCAL_NET_ID, tap.name());
+ EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+ status = mNetd->tetherInterfaceAdd(tap.name());
+ EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+ expectTetherInterfaceConfigureForIPv6Router(tap.name());
+
+ // Can't easily use INetd::NEXTHOP_NONE because it is a String16 constant. Use "" instead.
+ status = mNetd->networkAddRoute(INetd::LOCAL_NET_ID, tap.name(), kDownstreamPrefix, "");
+ EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+
+ // Set up forwarding. All methods take intIface first and extIface second.
+ status = mNetd->tetherAddForward(tap.name(), sTun.name());
+ EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+ status = mNetd->ipfwdAddInterfaceForward(tap.name(), sTun.name());
+ EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+
+ std::vector<uint8_t> kDummyMac = {02, 00, 00, 00, 00, 00};
+ uint8_t* daddr = reinterpret_cast<uint8_t*>(&pkt.hdr.daddr);
+ std::vector<uint8_t> dstAddr(daddr, daddr + sizeof(pkt.hdr.daddr));
+
+ TetherOffloadRuleParcel rule = makeTetherOffloadRule(sTun.ifindex(), tap.ifindex(), dstAddr,
+ 128, kDummyMac, kDummyMac, sizeof(pkt));
+ status = mNetd->tetherOffloadRuleAdd(rule);
+ EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+
+ // Set data limit to one byte less than two packets.
+ // If you get rid of the '- 1' then the second packet will get forwarded
+ // and the EXPECT_FALSE(expectPacket(...)) a dozen lines down will fail.
+ status = mNetd->tetherOffloadSetInterfaceQuota(sTun.ifindex(), sizeof(pkt) * 2 - 1);
+ EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+
+ // Receive a packet on sTun.
+ EXPECT_EQ((ssize_t)sizeof(pkt), write(fd1, &pkt, sizeof(pkt)));
+
+ // Expect a packet identical to pkt, except with a TTL of 63.
+ struct packet pkt2 = pkt;
+ ASSERT_EQ(1500U, sizeof(pkt2));
+ pkt2.hdr.hop_limit = pkt.hdr.hop_limit - 1;
+ EXPECT_TRUE(expectPacket(fd2, (uint8_t*)&pkt2, sizeof(pkt2)));
+
+ // Receive a second packet on sTun.
+ EXPECT_EQ((ssize_t)sizeof(pkt), write(fd1, &pkt, sizeof(pkt)));
+
+ // Should fail to forward due to quota limit.
+ EXPECT_FALSE(expectPacket(fd2, (uint8_t*)&pkt2, sizeof(pkt2)));
+
+ // Clean up.
+ EXPECT_TRUE(mNetd->tetherOffloadRuleRemove(rule).isOk());
+
+ TetherStatsParcel tetherStats;
+ EXPECT_TRUE(mNetd->tetherOffloadGetAndClearStats(sTun.ifindex(), &tetherStats).isOk());
+ EXPECT_EQ("", tetherStats.iface);
+ EXPECT_EQ(static_cast<int64_t>(sizeof(pkt)), tetherStats.rxBytes);
+ EXPECT_EQ(1, tetherStats.rxPackets);
+ EXPECT_EQ(0, tetherStats.txBytes);
+ EXPECT_EQ(0, tetherStats.txPackets);
+ EXPECT_EQ(sTun.ifindex(), tetherStats.ifIndex);
+
+ EXPECT_TRUE(mNetd->ipfwdRemoveInterfaceForward(tap.name(), sTun.name()).isOk());
+ EXPECT_TRUE(mNetd->tetherRemoveForward(tap.name(), sTun.name()).isOk());
+ EXPECT_TRUE(mNetd->networkRemoveRoute(INetd::LOCAL_NET_ID, tap.name(), kDownstreamPrefix, "")
+ .isOk());
+ EXPECT_TRUE(mNetd->tetherInterfaceRemove(tap.name()).isOk());
+ EXPECT_TRUE(mNetd->networkRemoveInterface(INetd::LOCAL_NET_ID, tap.name()).isOk());
+ EXPECT_TRUE(mNetd->networkRemoveInterface(TEST_NETID1, sTun.name()).isOk());
+}
diff --git a/tests/bpf_base_test.cpp b/tests/bpf_base_test.cpp
index 46e4306..f28c5f8 100644
--- a/tests/bpf_base_test.cpp
+++ b/tests/bpf_base_test.cpp
@@ -38,7 +38,7 @@
#include "bpf/BpfUtils.h"
#include "netdbpf/bpf_shared.h"
-using android::netdutils::StatusOr;
+using android::base::Result;
namespace android {
namespace bpf {
@@ -50,15 +50,6 @@
constexpr int TEST_COUNTERSET = 1;
constexpr int DEFAULT_COUNTERSET = 0;
-#define SKIP_IF_EXTENDED_BPF_NOT_SUPPORTED \
- do { \
- if (android::bpf::getBpfSupportLevel() != android::bpf::BpfLevel::EXTENDED) { \
- GTEST_LOG_(INFO) << "This test is skipped since extended bpf feature" \
- << "not supported\n"; \
- return; \
- } \
- } while (0)
-
class BpfBasicTest : public testing::Test {
protected:
BpfBasicTest() {}
@@ -68,7 +59,15 @@
SKIP_IF_BPF_NOT_SUPPORTED;
std::string cg2_path;
+#if 0
+ // This is the correct way to fetch cg2_path, but it occasionally hits ASAN
+ // problems due to memory allocated in non ASAN code being freed later by us
ASSERT_EQ(true, CgroupGetControllerPath(CGROUPV2_CONTROLLER_NAME, &cg2_path));
+#else
+ ASSERT_EQ(true, CgroupGetControllerPath(CGROUPV2_CONTROLLER_NAME, nullptr));
+ // Constant derived from //system/core/libprocessgroup/profiles/cgroups.json
+ cg2_path = "/dev/cg2_bpf";
+#endif
ASSERT_EQ(0, access(cg2_path.c_str(), R_OK));
ASSERT_EQ(0, access((cg2_path + "/cgroup.controllers").c_str(), R_OK));
}
@@ -100,35 +99,35 @@
TEST_F(BpfBasicTest, TestTagSocket) {
SKIP_IF_BPF_NOT_SUPPORTED;
- BpfMap<uint64_t, UidTag> cookieTagMap(mapRetrieve(COOKIE_TAG_MAP_PATH, 0));
+ BpfMap<uint64_t, UidTagValue> cookieTagMap(COOKIE_TAG_MAP_PATH);
ASSERT_LE(0, cookieTagMap.getMap());
int sock = socket(AF_INET6, SOCK_STREAM | SOCK_CLOEXEC, 0);
ASSERT_LE(0, sock);
uint64_t cookie = getSocketCookie(sock);
ASSERT_NE(NONEXISTENT_COOKIE, cookie);
ASSERT_EQ(0, qtaguid_tagSocket(sock, TEST_TAG, TEST_UID));
- StatusOr<UidTag> tagResult = cookieTagMap.readValue(cookie);
- ASSERT_TRUE(isOk(tagResult));
+ Result<UidTagValue> tagResult = cookieTagMap.readValue(cookie);
+ ASSERT_RESULT_OK(tagResult);
ASSERT_EQ(TEST_UID, tagResult.value().uid);
ASSERT_EQ(TEST_TAG, tagResult.value().tag);
ASSERT_EQ(0, qtaguid_untagSocket(sock));
tagResult = cookieTagMap.readValue(cookie);
- ASSERT_FALSE(isOk(tagResult));
- ASSERT_EQ(ENOENT, tagResult.status().code());
+ ASSERT_FALSE(tagResult.ok());
+ ASSERT_EQ(ENOENT, tagResult.error().code());
}
TEST_F(BpfBasicTest, TestCloseSocketWithoutUntag) {
SKIP_IF_BPF_NOT_SUPPORTED;
- BpfMap<uint64_t, UidTag> cookieTagMap(mapRetrieve(COOKIE_TAG_MAP_PATH, 0));
+ BpfMap<uint64_t, UidTagValue> cookieTagMap(COOKIE_TAG_MAP_PATH);
ASSERT_LE(0, cookieTagMap.getMap());
int sock = socket(AF_INET6, SOCK_STREAM | SOCK_CLOEXEC, 0);
ASSERT_LE(0, sock);
uint64_t cookie = getSocketCookie(sock);
ASSERT_NE(NONEXISTENT_COOKIE, cookie);
ASSERT_EQ(0, qtaguid_tagSocket(sock, TEST_TAG, TEST_UID));
- StatusOr<UidTag> tagResult = cookieTagMap.readValue(cookie);
- ASSERT_TRUE(isOk(tagResult));
+ Result<UidTagValue> tagResult = cookieTagMap.readValue(cookie);
+ ASSERT_RESULT_OK(tagResult);
ASSERT_EQ(TEST_UID, tagResult.value().uid);
ASSERT_EQ(TEST_TAG, tagResult.value().tag);
ASSERT_EQ(0, close(sock));
@@ -136,8 +135,8 @@
for (int i = 0; i < 10; i++) {
usleep(5000); // 5ms
tagResult = cookieTagMap.readValue(cookie);
- if (!isOk(tagResult)) {
- ASSERT_EQ(ENOENT, tagResult.status().code());
+ if (!tagResult.ok()) {
+ ASSERT_EQ(ENOENT, tagResult.error().code());
return;
}
}
@@ -147,47 +146,47 @@
TEST_F(BpfBasicTest, TestChangeCounterSet) {
SKIP_IF_BPF_NOT_SUPPORTED;
- BpfMap<uint32_t, uint8_t> uidCounterSetMap(mapRetrieve(UID_COUNTERSET_MAP_PATH, 0));
+ BpfMap<uint32_t, uint8_t> uidCounterSetMap(UID_COUNTERSET_MAP_PATH);
ASSERT_LE(0, uidCounterSetMap.getMap());
ASSERT_EQ(0, qtaguid_setCounterSet(TEST_COUNTERSET, TEST_UID));
uid_t uid = TEST_UID;
- StatusOr<uint8_t> counterSetResult = uidCounterSetMap.readValue(uid);
- ASSERT_TRUE(isOk(counterSetResult));
+ Result<uint8_t> counterSetResult = uidCounterSetMap.readValue(uid);
+ ASSERT_RESULT_OK(counterSetResult);
ASSERT_EQ(TEST_COUNTERSET, counterSetResult.value());
ASSERT_EQ(0, qtaguid_setCounterSet(DEFAULT_COUNTERSET, TEST_UID));
counterSetResult = uidCounterSetMap.readValue(uid);
- ASSERT_FALSE(isOk(counterSetResult));
- ASSERT_EQ(ENOENT, counterSetResult.status().code());
+ ASSERT_FALSE(counterSetResult.ok());
+ ASSERT_EQ(ENOENT, counterSetResult.error().code());
}
TEST_F(BpfBasicTest, TestDeleteTagData) {
SKIP_IF_BPF_NOT_SUPPORTED;
- BpfMap<StatsKey, StatsValue> statsMapA(mapRetrieve(STATS_MAP_A_PATH, 0));
+ BpfMap<StatsKey, StatsValue> statsMapA(STATS_MAP_A_PATH);
ASSERT_LE(0, statsMapA.getMap());
- BpfMap<StatsKey, StatsValue> statsMapB(mapRetrieve(STATS_MAP_B_PATH, 0));
+ BpfMap<StatsKey, StatsValue> statsMapB(STATS_MAP_B_PATH);
ASSERT_LE(0, statsMapB.getMap());
- BpfMap<uint32_t, StatsValue> appUidStatsMap(mapRetrieve(APP_UID_STATS_MAP_PATH, 0));
+ BpfMap<uint32_t, StatsValue> appUidStatsMap(APP_UID_STATS_MAP_PATH);
ASSERT_LE(0, appUidStatsMap.getMap());
StatsKey key = {.uid = TEST_UID, .tag = TEST_TAG, .counterSet = TEST_COUNTERSET,
.ifaceIndex = 1};
StatsValue statsMapValue = {.rxPackets = 1, .rxBytes = 100};
- EXPECT_TRUE(isOk(statsMapB.writeValue(key, statsMapValue, BPF_ANY)));
+ EXPECT_RESULT_OK(statsMapB.writeValue(key, statsMapValue, BPF_ANY));
key.tag = 0;
- EXPECT_TRUE(isOk(statsMapA.writeValue(key, statsMapValue, BPF_ANY)));
- EXPECT_TRUE(isOk(appUidStatsMap.writeValue(TEST_UID, statsMapValue, BPF_ANY)));
+ EXPECT_RESULT_OK(statsMapA.writeValue(key, statsMapValue, BPF_ANY));
+ EXPECT_RESULT_OK(appUidStatsMap.writeValue(TEST_UID, statsMapValue, BPF_ANY));
ASSERT_EQ(0, qtaguid_deleteTagData(0, TEST_UID));
- StatusOr<StatsValue> statsResult = statsMapA.readValue(key);
- ASSERT_FALSE(isOk(statsResult));
- ASSERT_EQ(ENOENT, statsResult.status().code());
+ Result<StatsValue> statsResult = statsMapA.readValue(key);
+ ASSERT_FALSE(statsResult.ok());
+ ASSERT_EQ(ENOENT, statsResult.error().code());
statsResult = appUidStatsMap.readValue(TEST_UID);
- ASSERT_FALSE(isOk(statsResult));
- ASSERT_EQ(ENOENT, statsResult.status().code());
+ ASSERT_FALSE(statsResult.ok());
+ ASSERT_EQ(ENOENT, statsResult.error().code());
key.tag = TEST_TAG;
statsResult = statsMapB.readValue(key);
- ASSERT_FALSE(isOk(statsResult));
- ASSERT_EQ(ENOENT, statsResult.status().code());
+ ASSERT_FALSE(statsResult.ok());
+ ASSERT_EQ(ENOENT, statsResult.error().code());
}
}
diff --git a/tests/netd_client_test.cpp b/tests/netd_client_test.cpp
new file mode 100644
index 0000000..c8af408
--- /dev/null
+++ b/tests/netd_client_test.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2020 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 <netinet/in.h>
+#include <poll.h> /* poll */
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include <android-base/unique_fd.h>
+#include <gtest/gtest.h>
+
+#include "NetdClient.h"
+
+#define SKIP_IF_NO_NETWORK_CONNECTIVITY \
+ do { \
+ if (!checkNetworkConnectivity()) { \
+ GTEST_LOG_(INFO) << "Skip. Required Network Connectivity. \n"; \
+ return; \
+ } \
+ } while (0)
+
+namespace {
+
+constexpr char TEST_DOMAIN[] = "www.google.com";
+
+bool checkNetworkConnectivity() {
+ android::base::unique_fd sock(socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP));
+ if (sock == -1) return false;
+ static const sockaddr_in6 server6 = {
+ .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}};
+ int ret = connect(sock, reinterpret_cast<const sockaddr*>(&server6), sizeof(server6));
+ if (ret == 0) return true;
+ sock.reset(socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP));
+ if (sock == -1) return false;
+ static const sockaddr_in server4 = {
+ .sin_family = AF_INET,
+ .sin_addr.s_addr = __constant_htonl(0x08080808L) // 8.8.8.8
+ };
+ ret = connect(sock, reinterpret_cast<const sockaddr*>(&server4), sizeof(server4));
+ return !ret;
+}
+
+void expectHasNetworking() {
+ // Socket
+ android::base::unique_fd ipv4(socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0)),
+ ipv6(socket(AF_INET6, SOCK_STREAM | SOCK_CLOEXEC, 0));
+ EXPECT_LE(3, ipv4);
+ EXPECT_LE(3, ipv6);
+
+ // DNS
+ addrinfo* result = nullptr;
+ errno = 0;
+ const addrinfo hints = {
+ .ai_family = AF_UNSPEC,
+ .ai_socktype = SOCK_DGRAM,
+ };
+ EXPECT_EQ(0, getaddrinfo(TEST_DOMAIN, nullptr, &hints, &result));
+ EXPECT_EQ(0, errno);
+ freeaddrinfo(result);
+}
+
+void expectNoNetworking() {
+ // Socket
+ android::base::unique_fd unixSocket(socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0));
+ EXPECT_LE(3, unixSocket);
+ android::base::unique_fd ipv4(socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0));
+ EXPECT_EQ(-1, ipv4);
+ EXPECT_EQ(EPERM, errno);
+ android::base::unique_fd ipv6(socket(AF_INET6, SOCK_STREAM | SOCK_CLOEXEC, 0));
+ EXPECT_EQ(-1, ipv6);
+ EXPECT_EQ(EPERM, errno);
+
+ // DNS
+ addrinfo* result = nullptr;
+ errno = 0;
+ const addrinfo hints = {
+ .ai_family = AF_UNSPEC,
+ .ai_socktype = SOCK_DGRAM,
+ };
+ EXPECT_EQ(EAI_NODATA, getaddrinfo(TEST_DOMAIN, nullptr, &hints, &result));
+ EXPECT_EQ(EPERM, errno);
+ freeaddrinfo(result);
+}
+
+} // namespace
+
+TEST(NetdClientIntegrationTest, setAllowNetworkingForProcess) {
+ SKIP_IF_NO_NETWORK_CONNECTIVITY;
+ // At the beginning, we should be able to use socket since the default setting is allowing.
+ expectHasNetworking();
+ // Disable
+ setAllowNetworkingForProcess(false);
+ expectNoNetworking();
+ // Reset
+ setAllowNetworkingForProcess(true);
+ expectHasNetworking();
+}
diff --git a/tests/netd_test.cpp b/tests/netd_test.cpp
index 7e14b01..dd25f96 100644
--- a/tests/netd_test.cpp
+++ b/tests/netd_test.cpp
@@ -7,7 +7,7 @@
*
* http://www.apache.org/licenses/LICENSE-2.0
*
- * Unless requied by applicable law or agreed to in writing, software
+ * 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
@@ -15,11 +15,33 @@
*
*/
+#include <arpa/inet.h>
#include <errno.h>
+#include <linux/if_packet.h>
+#include <linux/if_tun.h>
+#include <net/if.h>
+#include <poll.h>
+#include <sched.h>
#include <sys/capability.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/types.h>
#include <gtest/gtest.h>
+#include <android-base/unique_fd.h>
+
+#define LOG_TAG "NetdTest"
+#include "bpf/BpfMap.h"
+#include "netdbpf/bpf_shared.h"
+
+#include "OffloadUtils.h"
+
+namespace android {
+namespace net {
+
+using base::unique_fd;
+
TEST(NetUtilsWrapperTest, TestFileCapabilities) {
errno = 0;
ASSERT_EQ(NULL, cap_get_file("/system/bin/netutils-wrapper-1.0"));
@@ -33,3 +55,245 @@
ASSERT_EQ(W_EXITCODE(1, 0), system("ls -Z /sys/class/net/*/mtu | egrep -q -v "
"'^u:object_r:sysfs_net:s0 /sys/class/net/'"));
}
+
+// Trivial thread function that simply immediately terminates successfully.
+static int thread(void*) {
+ return 0;
+}
+
+typedef int (*thread_t)(void*);
+
+static void nsTest(int flags, bool success, thread_t newThread) {
+ // We need a minimal stack, but not clear if it will grow up or down,
+ // So allocate 2 pages and give a pointer to the middle.
+ static char stack[PAGE_SIZE * 2];
+ errno = 0;
+ // VFORK: if thread is successfully created, then kernel will wait for it
+ // to terminate before we resume -> hence static stack is safe to reuse.
+ int tid = clone(newThread, &stack[PAGE_SIZE], flags | CLONE_VFORK, NULL);
+ if (success) {
+ ASSERT_EQ(errno, 0);
+ ASSERT_GE(tid, 0);
+ } else {
+ ASSERT_EQ(errno, EINVAL);
+ ASSERT_EQ(tid, -1);
+ }
+}
+
+// Test kernel configuration option CONFIG_NAMESPACES=y
+TEST(NetdNamespaceTest, CheckMountNamespaceSupport) {
+ nsTest(CLONE_NEWNS, true, thread);
+}
+
+// Test kernel configuration option CONFIG_UTS_NS=y
+TEST(NetdNamespaceTest, CheckUTSNamespaceSupport) {
+ nsTest(CLONE_NEWUTS, true, thread);
+}
+
+// Test kernel configuration option CONFIG_NET_NS=y
+TEST(NetdNamespaceTest, CheckNetworkNamespaceSupport) {
+ nsTest(CLONE_NEWNET, true, thread);
+}
+
+// Test kernel configuration option CONFIG_USER_NS=n
+TEST(NetdNamespaceTest, /*DISABLED_*/ CheckNoUserNamespaceSupport) {
+ nsTest(CLONE_NEWUSER, false, thread);
+}
+
+// Test for all of the above
+TEST(NetdNamespaceTest, CheckFullNamespaceSupport) {
+ nsTest(CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWNET, true, thread);
+}
+
+// Test for presence of kernel patch:
+// ANDROID: net: bpf: permit redirect from ingress L3 to egress L2 devices at near max mtu
+// on 4.14+ kernels.
+TEST(NetdBpfTest, testBpfSkbChangeHeadAboveMTU) {
+ SKIP_IF_EXTENDED_BPF_NOT_SUPPORTED;
+
+ constexpr int mtu = 1500;
+
+ errno = 0;
+
+ // Amusingly can't use SIOC... on tun/tap fds.
+ int rv = socket(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0);
+ ASSERT_EQ(errno, 0);
+ ASSERT_GE(rv, 3);
+ unique_fd unixfd(rv);
+
+ rv = open("/dev/net/tun", O_RDWR | O_NONBLOCK);
+ ASSERT_EQ(errno, 0);
+ ASSERT_GE(rv, 3);
+ unique_fd tun(rv);
+
+ rv = open("/dev/net/tun", O_RDWR | O_NONBLOCK);
+ ASSERT_EQ(errno, 0);
+ ASSERT_GE(rv, 3);
+ unique_fd tap(rv);
+
+ struct ifreq tun_ifr = {
+ .ifr_flags = IFF_TUN | IFF_NO_PI,
+ .ifr_name = "tun_bpftest",
+ };
+
+ struct ifreq tap_ifr = {
+ .ifr_flags = IFF_TAP | IFF_NO_PI,
+ .ifr_name = "tap_bpftest",
+ };
+
+ rv = ioctl(tun, TUNSETIFF, &tun_ifr);
+ ASSERT_EQ(errno, 0);
+ ASSERT_EQ(rv, 0);
+
+ rv = ioctl(tap, TUNSETIFF, &tap_ifr);
+ ASSERT_EQ(errno, 0);
+ ASSERT_EQ(rv, 0);
+
+ // prevents kernel from sending us spurious ipv6 packets
+ rv = open("/proc/sys/net/ipv6/conf/tap_bpftest/disable_ipv6", O_WRONLY);
+ ASSERT_EQ(errno, 0);
+ ASSERT_GE(rv, 3);
+ unique_fd f(rv);
+
+ rv = write(f, "1\n", 2);
+ ASSERT_EQ(errno, 0);
+ ASSERT_EQ(rv, 2);
+
+ rv = close(f.release());
+ ASSERT_EQ(errno, 0);
+ ASSERT_EQ(rv, 0);
+
+ int tunif = if_nametoindex(tun_ifr.ifr_name);
+ ASSERT_GE(tunif, 2);
+
+ int tapif = if_nametoindex(tap_ifr.ifr_name);
+ ASSERT_GE(tapif, 2);
+
+ tun_ifr.ifr_mtu = mtu;
+ rv = ioctl(unixfd, SIOCSIFMTU, &tun_ifr);
+ ASSERT_EQ(errno, 0);
+ ASSERT_EQ(rv, 0);
+
+ tap_ifr.ifr_mtu = mtu;
+ rv = ioctl(unixfd, SIOCSIFMTU, &tap_ifr);
+ ASSERT_EQ(errno, 0);
+ ASSERT_EQ(rv, 0);
+
+ rv = ioctl(unixfd, SIOCGIFFLAGS, &tun_ifr);
+ ASSERT_EQ(errno, 0);
+ ASSERT_EQ(rv, 0);
+
+ rv = ioctl(unixfd, SIOCGIFFLAGS, &tap_ifr);
+ ASSERT_EQ(errno, 0);
+ ASSERT_EQ(rv, 0);
+
+ tun_ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
+
+ tap_ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
+
+ rv = ioctl(unixfd, SIOCSIFFLAGS, &tun_ifr);
+ ASSERT_EQ(errno, 0);
+ ASSERT_EQ(rv, 0);
+
+ rv = ioctl(unixfd, SIOCSIFFLAGS, &tap_ifr);
+ ASSERT_EQ(errno, 0);
+ ASSERT_EQ(rv, 0);
+
+ rv = tcQdiscAddDevClsact(tunif);
+ ASSERT_EQ(rv, 0);
+
+ int bpfFd = getTetherIngressProgFd(/* ethernet */ false);
+ ASSERT_EQ(errno, 0);
+ ASSERT_GE(bpfFd, 3);
+
+ rv = tcFilterAddDevIngressTether(tunif, bpfFd, /* ethernet*/ false);
+ ASSERT_EQ(rv, 0);
+
+ bpf::BpfMap<TetherIngressKey, TetherIngressValue> bpfIngressMap;
+ bpf::BpfMap<uint32_t, TetherStatsValue> bpfStatsMap;
+ bpf::BpfMap<uint32_t, uint64_t> bpfLimitMap;
+
+ rv = getTetherIngressMapFd();
+ ASSERT_GE(rv, 3);
+ bpfIngressMap.reset(rv);
+
+ rv = getTetherStatsMapFd();
+ ASSERT_GE(rv, 3);
+ bpfStatsMap.reset(rv);
+
+ rv = getTetherLimitMapFd();
+ ASSERT_GE(rv, 3);
+ bpfLimitMap.reset(rv);
+
+ TetherIngressKey key = {
+ .iif = static_cast<uint32_t>(tunif),
+ //.neigh6 = ,
+ };
+
+ ethhdr hdr = {
+ .h_proto = htons(ETH_P_IPV6),
+ };
+
+ TetherIngressValue value = {
+ .oif = static_cast<uint32_t>(tapif),
+ .macHeader = hdr,
+ .pmtu = mtu,
+ };
+
+#define ASSERT_OK(status) ASSERT_TRUE((status).ok())
+
+ ASSERT_OK(bpfIngressMap.writeValue(key, value, BPF_ANY));
+
+ uint32_t k = tunif;
+ TetherStatsValue stats = {};
+ ASSERT_OK(bpfStatsMap.writeValue(k, stats, BPF_NOEXIST));
+
+ uint64_t limit = ~0uLL;
+ ASSERT_OK(bpfLimitMap.writeValue(k, limit, BPF_NOEXIST));
+
+ // minimal 'acceptable' 40-byte hoplimit 255 IPv6 packet, src ip 2000::
+ uint8_t pkt[mtu] = {
+ 0x60, 0, 0, 0, 0, 40, 0, 255, 0x20,
+ };
+
+ // Iterate over all packet sizes from minimal ipv6 packet to mtu.
+ // Tethering ebpf program should forward the packet from tun to tap interface.
+ // TUN is L3, TAP is L2, so it will add a 14 byte ethernet header.
+ for (int pkt_size = 40; pkt_size <= mtu; ++pkt_size) {
+ rv = write(tun, pkt, pkt_size);
+ ASSERT_EQ(errno, 0);
+ ASSERT_EQ(rv, pkt_size);
+
+ struct pollfd p = {
+ .fd = tap,
+ .events = POLLIN,
+ };
+
+ rv = poll(&p, 1, 1000 /*milliseconds*/);
+ if (rv == 0) {
+ // we hit a timeout at this packet size, log it
+ EXPECT_EQ(pkt_size, -1);
+ // this particular packet size is where it fails without the oneline kernel fix
+ if (pkt_size + ETH_HLEN == mtu + 1) EXPECT_EQ("detected missing kernel patch", "");
+ break;
+ }
+ EXPECT_EQ(errno, 0);
+ EXPECT_EQ(rv, 1);
+ EXPECT_EQ(p.revents, POLLIN);
+
+ // use a buffer 1 byte larger then what we expect so we don't simply get truncated down
+ uint8_t buf[ETH_HLEN + mtu + 1];
+ rv = read(tap, buf, sizeof(buf));
+ EXPECT_EQ(errno, 0);
+ EXPECT_EQ(rv, ETH_HLEN + pkt_size);
+ errno = 0;
+ if (rv < 0) break;
+ }
+
+ ASSERT_OK(bpfIngressMap.deleteValue(key));
+ ASSERT_OK(bpfStatsMap.deleteValue(k));
+ ASSERT_OK(bpfLimitMap.deleteValue(k));
+}
+
+} // namespace net
+} // namespace android
diff --git a/tests/netlink_listener_test.cpp b/tests/netlink_listener_test.cpp
index 94e9524..46394ca 100644
--- a/tests/netlink_listener_test.cpp
+++ b/tests/netlink_listener_test.cpp
@@ -48,8 +48,8 @@
constexpr uint32_t SOCK_CLOSE_WAIT_US = 20 * 1000;
constexpr uint32_t ENOBUFS_POLL_WAIT_US = 10 * 1000;
-using android::netdutils::Status;
-using android::netdutils::statusFromErrno;
+using android::base::Result;
+using android::base::ResultError;
// This test set up a SkDestroyListener that is runing parallel with the production
// SkDestroyListener. The test will create thousands of sockets and tag them on the
@@ -64,41 +64,41 @@
class NetlinkListenerTest : public testing::Test {
protected:
NetlinkListenerTest() {}
- BpfMap<uint64_t, UidTag> mCookieTagMap;
+ BpfMap<uint64_t, UidTagValue> mCookieTagMap;
void SetUp() {
SKIP_IF_BPF_NOT_SUPPORTED;
- mCookieTagMap.reset(android::bpf::mapRetrieve(COOKIE_TAG_MAP_PATH, 0));
+ mCookieTagMap.reset(android::bpf::mapRetrieveRW(COOKIE_TAG_MAP_PATH));
ASSERT_TRUE(mCookieTagMap.isValid());
}
void TearDown() {
SKIP_IF_BPF_NOT_SUPPORTED;
- const auto deleteTestCookieEntries = [](const uint64_t& key, const UidTag& value,
- BpfMap<uint64_t, UidTag>& map) {
+ const auto deleteTestCookieEntries = [](const uint64_t& key, const UidTagValue& value,
+ BpfMap<uint64_t, UidTagValue>& map) {
if ((value.uid == TEST_UID) && (value.tag == TEST_TAG)) {
- Status res = map.deleteValue(key);
- if (isOk(res) || (res.code() == ENOENT)) {
- return android::netdutils::status::ok;
+ Result<void> res = map.deleteValue(key);
+ if (res.ok() || (res.error().code() == ENOENT)) {
+ return Result<void>();
}
ALOGE("Failed to delete data(cookie = %" PRIu64 "): %s\n", key,
- strerror(res.code()));
+ strerror(res.error().code()));
}
// Move forward to next cookie in the map.
- return android::netdutils::status::ok;
+ return Result<void>();
};
- EXPECT_OK(mCookieTagMap.iterateWithValue(deleteTestCookieEntries));
+ EXPECT_RESULT_OK(mCookieTagMap.iterateWithValue(deleteTestCookieEntries));
}
- Status checkNoGarbageTagsExist() {
- const auto checkGarbageTags = [](const uint64_t&, const UidTag& value,
- const BpfMap<uint64_t, UidTag>&) {
+ Result<void> checkNoGarbageTagsExist() {
+ const auto checkGarbageTags = [](const uint64_t&, const UidTagValue& value,
+ const BpfMap<uint64_t, UidTagValue>&) -> Result<void> {
if ((TEST_UID == value.uid) && (TEST_TAG == value.tag)) {
- return statusFromErrno(EUCLEAN, "Closed socket is not untagged");
+ return ResultError("Closed socket is not untagged", EUCLEAN);
}
- return android::netdutils::status::ok;
+ return Result<void>();
};
return mCookieTagMap.iterateWithValue(checkGarbageTags);
}
@@ -145,7 +145,7 @@
usleep(ENOBUFS_POLL_WAIT_US);
EXPECT_EQ(currentErrorCount, rxErrorCount);
} else {
- EXPECT_TRUE(isOk(checkNoGarbageTagsExist()));
+ EXPECT_RESULT_OK(checkNoGarbageTagsExist());
EXPECT_EQ(0, rxErrorCount);
}
}
diff --git a/tests/runtests.sh b/tests/runtests.sh
deleted file mode 100755
index 6f828fc..0000000
--- a/tests/runtests.sh
+++ /dev/null
@@ -1,162 +0,0 @@
-#!/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
new file mode 100644
index 0000000..27b17c5
--- /dev/null
+++ b/tests/test_utils.cpp
@@ -0,0 +1,99 @@
+/*
+ * 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
new file mode 100644
index 0000000..c8cd6ce
--- /dev/null
+++ b/tests/test_utils.h
@@ -0,0 +1,38 @@
+/*
+ * 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 65f1528..93b669f 100644
--- a/tests/tun_interface.cpp
+++ b/tests/tun_interface.cpp
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*
- * tun_interface.cpp - creates tun interfaces for testing purposes
+ * tun_interface.cpp - creates tun or tap interfaces for testing purposes
*/
#include <string>
@@ -40,7 +40,7 @@
#include "tun_interface.h"
-#define TUN_DEV "/dev/tun"
+#define TUN_DEV "/dev/net/tun"
using android::base::StringPrintf;
using android::base::unique_fd;
@@ -48,7 +48,7 @@
namespace android {
namespace net {
-int TunInterface::init(const std::string& ifName) {
+int TunInterface::init(const std::string& ifName, bool isTap) {
// Generate a random ULA address pair.
arc4random_buf(&mSrcAddr, sizeof(mSrcAddr));
mSrcAddr.s6_addr[0] = 0xfd;
@@ -80,8 +80,9 @@
}
mIfName.resize(9);
+ flags = IFF_NO_PI | (isTap ? IFF_TAP : IFF_TUN);
struct ifreq ifr = {
- .ifr_ifru = { .ifru_flags = IFF_TUN },
+ .ifr_ifru = {.ifru_flags = static_cast<short>(flags)},
};
strlcpy(ifr.ifr_name, mIfName.c_str(), sizeof(ifr.ifr_name));
@@ -106,6 +107,7 @@
if (int ret = ifc_enable(ifr.ifr_name)) {
return ret;
}
+
return 0;
}
@@ -137,7 +139,9 @@
timeval timeout = {.tv_usec = 200 * 1000};
if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) == -1) return -errno;
- if (ifc_add_address(mIfName.c_str(), addr.c_str(), prefixlen)) return -errno;
+ if (int ret = ifc_act_on_address(RTM_NEWADDR, mIfName.c_str(), addr.c_str(), prefixlen,
+ /*nodad*/ true))
+ return ret;
int family;
size_t addrlen;
diff --git a/tests/tun_interface.h b/tests/tun_interface.h
index 0af43b1..ae2c330 100644
--- a/tests/tun_interface.h
+++ b/tests/tun_interface.h
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*
- * tun_interface.h - creates tun interfaces for testing purposes
+ * tun_interface.h - creates tun or tap interfaces for testing purposes
*/
#ifndef _SYSTEM_NETD_TESTS_TUN_INTERACE_H
@@ -27,9 +27,14 @@
TunInterface() = default;
~TunInterface() { destroy(); }
- // Creates a tun interface. Returns 0 on success or -errno on failure. Must succeed before it is
- // legal to call any of the other methods in this class.
- int init(const std::string& ifName = "");
+ // Creates a tun or tap interface. Returns 0 on success or -errno on failure. Must succeed
+ // before it is legal to call any of the other methods in this class.
+ int init(const std::string& ifName, bool isTap);
+
+ // Convenience overloads.
+ int init() { return init("", false); }
+ int init(const std::string& ifName) { return init(ifName, false); }
+ int init(bool isTap) { return init("", isTap); }
void destroy();
const std::string& name() const { return mIfName; }