diff options
| author | 2019-05-28 05:38:49 +0000 | |
|---|---|---|
| committer | 2019-05-28 05:38:49 +0000 | |
| commit | 0edeaa365e044e3160e6e8d06e15d64124af3652 (patch) | |
| tree | 8934c0142cb487e8cd0aecc3b1f8198bfe37ae25 | |
| parent | a80dd06e1768be4b48af0f83842a43189b5a0019 (diff) | |
| parent | ad7c294c475d235c6d91fa75f97d7c22fc29855e (diff) | |
Merge changes from topic "am-851f19c8d7d34bb48accd48868aae501-qt-dev" into qt-dev
* changes:
Remove broken test testRfc6724Sort
Add Rfc6724 style sort for DnsResolver and fix potential bug
| -rw-r--r-- | core/java/android/net/DnsResolver.java | 106 | ||||
| -rw-r--r-- | core/java/android/net/NetworkUtils.java | 7 | ||||
| -rw-r--r-- | core/java/android/net/util/DnsUtils.java | 376 | ||||
| -rw-r--r-- | core/jni/android_net_NetUtils.cpp | 27 | ||||
| -rw-r--r-- | tests/net/java/android/net/util/DnsUtilsTest.java | 202 |
5 files changed, 646 insertions, 72 deletions
diff --git a/core/java/android/net/DnsResolver.java b/core/java/android/net/DnsResolver.java index 68826cbeb845..4b2b4c35b292 100644 --- a/core/java/android/net/DnsResolver.java +++ b/core/java/android/net/DnsResolver.java @@ -16,16 +16,17 @@ package android.net; +import static android.net.NetworkUtils.getDnsNetId; import static android.net.NetworkUtils.resNetworkCancel; import static android.net.NetworkUtils.resNetworkQuery; import static android.net.NetworkUtils.resNetworkResult; import static android.net.NetworkUtils.resNetworkSend; +import static android.net.util.DnsUtils.haveIpv4; +import static android.net.util.DnsUtils.haveIpv6; +import static android.net.util.DnsUtils.rfc6724Sort; import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_ERROR; import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT; -import static android.system.OsConstants.AF_INET; -import static android.system.OsConstants.AF_INET6; -import static android.system.OsConstants.IPPROTO_UDP; -import static android.system.OsConstants.SOCK_DGRAM; +import static android.system.OsConstants.ENONET; import android.annotation.CallbackExecutor; import android.annotation.IntDef; @@ -34,18 +35,12 @@ import android.annotation.Nullable; import android.os.CancellationSignal; import android.os.Looper; import android.system.ErrnoException; -import android.system.Os; import android.util.Log; -import libcore.io.IoUtils; - import java.io.FileDescriptor; -import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.SocketAddress; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.List; @@ -196,8 +191,8 @@ public final class DnsResolver { final Object lock = new Object(); final FileDescriptor queryfd; try { - queryfd = resNetworkSend((network != null - ? network.getNetIdForResolv() : NETID_UNSET), query, query.length, flags); + queryfd = resNetworkSend((network != null) + ? network.getNetIdForResolv() : NETID_UNSET, query, query.length, flags); } catch (ErrnoException e) { executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e))); return; @@ -237,8 +232,8 @@ public final class DnsResolver { final Object lock = new Object(); final FileDescriptor queryfd; try { - queryfd = resNetworkQuery((network != null - ? network.getNetIdForResolv() : NETID_UNSET), domain, nsClass, nsType, flags); + queryfd = resNetworkQuery((network != null) + ? network.getNetIdForResolv() : NETID_UNSET, domain, nsClass, nsType, flags); } catch (ErrnoException e) { executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e))); return; @@ -252,14 +247,16 @@ public final class DnsResolver { private class InetAddressAnswerAccumulator implements Callback<byte[]> { private final List<InetAddress> mAllAnswers; + private final Network mNetwork; private int mRcode; private DnsException mDnsException; private final Callback<? super List<InetAddress>> mUserCallback; private final int mTargetAnswerCount; private int mReceivedAnswerCount = 0; - InetAddressAnswerAccumulator(int size, + InetAddressAnswerAccumulator(@NonNull Network network, int size, @NonNull Callback<? super List<InetAddress>> callback) { + mNetwork = network; mTargetAnswerCount = size; mAllAnswers = new ArrayList<>(); mUserCallback = callback; @@ -280,8 +277,7 @@ public final class DnsResolver { private void maybeReportAnswer() { if (++mReceivedAnswerCount != mTargetAnswerCount) return; if (mAllAnswers.isEmpty() && maybeReportError()) return; - // TODO: Do RFC6724 sort. - mUserCallback.onAnswer(mAllAnswers, mRcode); + mUserCallback.onAnswer(rfc6724Sort(mNetwork, mAllAnswers), mRcode); } @Override @@ -308,7 +304,7 @@ public final class DnsResolver { /** * Send a DNS query with the specified name on a network with both IPv4 and IPv6, - * get back a set of InetAddresses asynchronously. + * get back a set of InetAddresses with rfc6724 sorting style asynchronously. * * This method will examine the connection ability on given network, and query IPv4 * and IPv6 if connection is available. @@ -335,8 +331,23 @@ public final class DnsResolver { return; } final Object lock = new Object(); - final boolean queryIpv6 = haveIpv6(network); - final boolean queryIpv4 = haveIpv4(network); + final Network queryNetwork; + try { + queryNetwork = (network != null) ? network : new Network(getDnsNetId()); + } catch (ErrnoException e) { + executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e))); + return; + } + final boolean queryIpv6 = haveIpv6(queryNetwork); + final boolean queryIpv4 = haveIpv4(queryNetwork); + + // This can only happen if queryIpv4 and queryIpv6 are both false. + // This almost certainly means that queryNetwork does not exist or no longer exists. + if (!queryIpv6 && !queryIpv4) { + executor.execute(() -> callback.onError( + new DnsException(ERROR_SYSTEM, new ErrnoException("resNetworkQuery", ENONET)))); + return; + } final FileDescriptor v4fd; final FileDescriptor v6fd; @@ -345,9 +356,8 @@ public final class DnsResolver { if (queryIpv6) { try { - v6fd = resNetworkQuery((network != null - ? network.getNetIdForResolv() : NETID_UNSET), - domain, CLASS_IN, TYPE_AAAA, flags); + v6fd = resNetworkQuery(queryNetwork.getNetIdForResolv(), domain, CLASS_IN, + TYPE_AAAA, flags); } catch (ErrnoException e) { executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e))); return; @@ -355,7 +365,6 @@ public final class DnsResolver { queryCount++; } else v6fd = null; - // TODO: Use device flag to control the sleep time. // Avoiding gateways drop packets if queries are sent too close together try { Thread.sleep(SLEEP_TIME_MS); @@ -365,9 +374,8 @@ public final class DnsResolver { if (queryIpv4) { try { - v4fd = resNetworkQuery((network != null - ? network.getNetIdForResolv() : NETID_UNSET), - domain, CLASS_IN, TYPE_A, flags); + v4fd = resNetworkQuery(queryNetwork.getNetIdForResolv(), domain, CLASS_IN, TYPE_A, + flags); } catch (ErrnoException e) { if (queryIpv6) resNetworkCancel(v6fd); // Closes fd, marks it invalid. executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e))); @@ -377,7 +385,7 @@ public final class DnsResolver { } else v4fd = null; final InetAddressAnswerAccumulator accumulator = - new InetAddressAnswerAccumulator(queryCount, callback); + new InetAddressAnswerAccumulator(queryNetwork, queryCount, callback); synchronized (lock) { if (queryIpv6) { @@ -398,7 +406,7 @@ public final class DnsResolver { /** * Send a DNS query with the specified name and query type, get back a set of - * InetAddresses asynchronously. + * InetAddresses with rfc6724 sorting style asynchronously. * * The answer will be provided asynchronously through the provided {@link Callback}. * @@ -423,15 +431,17 @@ public final class DnsResolver { } final Object lock = new Object(); final FileDescriptor queryfd; + final Network queryNetwork; try { - queryfd = resNetworkQuery((network != null - ? network.getNetIdForResolv() : NETID_UNSET), domain, CLASS_IN, nsType, flags); + queryNetwork = (network != null) ? network : new Network(getDnsNetId()); + queryfd = resNetworkQuery(queryNetwork.getNetIdForResolv(), domain, CLASS_IN, nsType, + flags); } catch (ErrnoException e) { executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e))); return; } final InetAddressAnswerAccumulator accumulator = - new InetAddressAnswerAccumulator(1, callback); + new InetAddressAnswerAccumulator(queryNetwork, 1, callback); synchronized (lock) { registerFDListener(executor, queryfd, accumulator, cancellationSignal, lock); if (cancellationSignal == null) return; @@ -500,38 +510,6 @@ public final class DnsResolver { }); } - // These two functions match the behaviour of have_ipv4 and have_ipv6 in the native resolver. - private boolean haveIpv4(@Nullable Network network) { - final SocketAddress addrIpv4 = - new InetSocketAddress(InetAddresses.parseNumericAddress("8.8.8.8"), 0); - return checkConnectivity(network, AF_INET, addrIpv4); - } - - private boolean haveIpv6(@Nullable Network network) { - final SocketAddress addrIpv6 = - new InetSocketAddress(InetAddresses.parseNumericAddress("2000::"), 0); - return checkConnectivity(network, AF_INET6, addrIpv6); - } - - private boolean checkConnectivity(@Nullable Network network, - int domain, @NonNull SocketAddress addr) { - final FileDescriptor socket; - try { - socket = Os.socket(domain, SOCK_DGRAM, IPPROTO_UDP); - } catch (ErrnoException e) { - return false; - } - try { - if (network != null) network.bindSocket(socket); - Os.connect(socket, addr); - } catch (IOException | ErrnoException e) { - return false; - } finally { - IoUtils.closeQuietly(socket); - } - return true; - } - private static class DnsAddressAnswer extends DnsPacket { private static final String TAG = "DnsResolver.DnsAddressAnswer"; private static final boolean DBG = false; diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java index c06a13269d83..a640f83ea5d2 100644 --- a/core/java/android/net/NetworkUtils.java +++ b/core/java/android/net/NetworkUtils.java @@ -157,6 +157,13 @@ public class NetworkUtils { public static native void resNetworkCancel(FileDescriptor fd); /** + * DNS resolver series jni method. + * Attempts to get netid of network which resolver will + * use if no network is explicitly selected. + */ + public static native int getDnsNetId() throws ErrnoException; + + /** * Get the tcp repair window associated with the {@code fd}. * * @param fd the tcp socket's {@link FileDescriptor}. diff --git a/core/java/android/net/util/DnsUtils.java b/core/java/android/net/util/DnsUtils.java new file mode 100644 index 000000000000..e6abd5059027 --- /dev/null +++ b/core/java/android/net/util/DnsUtils.java @@ -0,0 +1,376 @@ +/* + * 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.util; + +import static android.system.OsConstants.AF_INET; +import static android.system.OsConstants.AF_INET6; +import static android.system.OsConstants.IPPROTO_UDP; +import static android.system.OsConstants.SOCK_DGRAM; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.net.InetAddresses; +import android.net.Network; +import android.system.ErrnoException; +import android.system.Os; +import android.util.Log; + +import com.android.internal.util.BitUtils; + +import libcore.io.IoUtils; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +/** + * @hide + */ +public class DnsUtils { + private static final String TAG = "DnsUtils"; + private static final int CHAR_BIT = 8; + public static final int IPV6_ADDR_SCOPE_NODELOCAL = 0x01; + public static final int IPV6_ADDR_SCOPE_LINKLOCAL = 0x02; + public static final int IPV6_ADDR_SCOPE_SITELOCAL = 0x05; + public static final int IPV6_ADDR_SCOPE_GLOBAL = 0x0e; + private static final Comparator<SortableAddress> sRfc6724Comparator = new Rfc6724Comparator(); + + /** + * Comparator to sort SortableAddress in Rfc6724 style. + */ + public static class Rfc6724Comparator implements Comparator<SortableAddress> { + // This function matches the behaviour of _rfc6724_compare in the native resolver. + @Override + public int compare(SortableAddress span1, SortableAddress span2) { + // Rule 1: Avoid unusable destinations. + if (span1.hasSrcAddr != span2.hasSrcAddr) { + return span2.hasSrcAddr - span1.hasSrcAddr; + } + + // Rule 2: Prefer matching scope. + if (span1.scopeMatch != span2.scopeMatch) { + return span2.scopeMatch - span1.scopeMatch; + } + + // TODO: Implement rule 3: Avoid deprecated addresses. + // TODO: Implement rule 4: Prefer home addresses. + + // Rule 5: Prefer matching label. + if (span1.labelMatch != span2.labelMatch) { + return span2.labelMatch - span1.labelMatch; + } + + // Rule 6: Prefer higher precedence. + if (span1.precedence != span2.precedence) { + return span2.precedence - span1.precedence; + } + + // TODO: Implement rule 7: Prefer native transport. + + // Rule 8: Prefer smaller scope. + if (span1.scope != span2.scope) { + return span1.scope - span2.scope; + } + + // Rule 9: Use longest matching prefix. IPv6 only. + if (span1.prefixMatchLen != span2.prefixMatchLen) { + return span2.prefixMatchLen - span1.prefixMatchLen; + } + + // Rule 10: Leave the order unchanged. Collections.sort is a stable sort. + return 0; + } + } + + /** + * Class used to sort with RFC 6724 + */ + public static class SortableAddress { + public final int label; + public final int labelMatch; + public final int scope; + public final int scopeMatch; + public final int precedence; + public final int prefixMatchLen; + public final int hasSrcAddr; + public final InetAddress address; + + public SortableAddress(@NonNull InetAddress addr, @Nullable InetAddress srcAddr) { + address = addr; + hasSrcAddr = (srcAddr != null) ? 1 : 0; + label = findLabel(addr); + scope = findScope(addr); + precedence = findPrecedence(addr); + labelMatch = ((srcAddr != null) && (label == findLabel(srcAddr))) ? 1 : 0; + scopeMatch = ((srcAddr != null) && (scope == findScope(srcAddr))) ? 1 : 0; + if (isIpv6Address(addr) && isIpv6Address(srcAddr)) { + prefixMatchLen = compareIpv6PrefixMatchLen(srcAddr, addr); + } else { + prefixMatchLen = 0; + } + } + } + + /** + * Sort the given address list in RFC6724 order. + * Will leave the list unchanged if an error occurs. + * + * This function matches the behaviour of _rfc6724_sort in the native resolver. + */ + public static @NonNull List<InetAddress> rfc6724Sort(@Nullable Network network, + @NonNull List<InetAddress> answers) { + List<SortableAddress> sortableAnswerList = new ArrayList<>(); + answers.forEach(addr -> sortableAnswerList.add( + new SortableAddress(addr, findSrcAddress(network, addr)))); + + Collections.sort(sortableAnswerList, sRfc6724Comparator); + + final List<InetAddress> sortedAnswers = new ArrayList<>(); + sortableAnswerList.forEach(ans -> sortedAnswers.add(ans.address)); + + return sortedAnswers; + } + + private static @Nullable InetAddress findSrcAddress(@Nullable Network network, + @NonNull InetAddress addr) { + final int domain; + if (isIpv4Address(addr)) { + domain = AF_INET; + } else if (isIpv6Address(addr)) { + domain = AF_INET6; + } else { + return null; + } + final FileDescriptor socket; + try { + socket = Os.socket(domain, SOCK_DGRAM, IPPROTO_UDP); + } catch (ErrnoException e) { + Log.e(TAG, "findSrcAddress:" + e.toString()); + return null; + } + try { + if (network != null) network.bindSocket(socket); + Os.connect(socket, new InetSocketAddress(addr, 0)); + return ((InetSocketAddress) Os.getsockname(socket)).getAddress(); + } catch (IOException | ErrnoException e) { + return null; + } finally { + IoUtils.closeQuietly(socket); + } + } + + /** + * Get the label for a given IPv4/IPv6 address. + * RFC 6724, section 2.1. + * + * Note that Java will return an IPv4-mapped address as an IPv4 address. + */ + private static int findLabel(@NonNull InetAddress addr) { + if (isIpv4Address(addr)) { + return 4; + } else if (isIpv6Address(addr)) { + if (addr.isLoopbackAddress()) { + return 0; + } else if (isIpv6Address6To4(addr)) { + return 2; + } else if (isIpv6AddressTeredo(addr)) { + return 5; + } else if (isIpv6AddressULA(addr)) { + return 13; + } else if (((Inet6Address) addr).isIPv4CompatibleAddress()) { + return 3; + } else if (addr.isSiteLocalAddress()) { + return 11; + } else if (isIpv6Address6Bone(addr)) { + return 12; + } else { + // All other IPv6 addresses, including global unicast addresses. + return 1; + } + } else { + // This should never happen. + return 1; + } + } + + private static boolean isIpv6Address(@Nullable InetAddress addr) { + return addr instanceof Inet6Address; + } + + private static boolean isIpv4Address(@Nullable InetAddress addr) { + return addr instanceof Inet4Address; + } + + private static boolean isIpv6Address6To4(@NonNull InetAddress addr) { + if (!isIpv6Address(addr)) return false; + final byte[] byteAddr = addr.getAddress(); + return byteAddr[0] == 0x20 && byteAddr[1] == 0x02; + } + + private static boolean isIpv6AddressTeredo(@NonNull InetAddress addr) { + if (!isIpv6Address(addr)) return false; + final byte[] byteAddr = addr.getAddress(); + return byteAddr[0] == 0x20 && byteAddr[1] == 0x01 && byteAddr[2] == 0x00 + && byteAddr[3] == 0x00; + } + + private static boolean isIpv6AddressULA(@NonNull InetAddress addr) { + return isIpv6Address(addr) && (addr.getAddress()[0] & 0xfe) == 0xfc; + } + + private static boolean isIpv6Address6Bone(@NonNull InetAddress addr) { + if (!isIpv6Address(addr)) return false; + final byte[] byteAddr = addr.getAddress(); + return byteAddr[0] == 0x3f && byteAddr[1] == (byte) 0xfe; + } + + private static int getIpv6MulticastScope(@NonNull InetAddress addr) { + return !isIpv6Address(addr) ? 0 : (addr.getAddress()[1] & 0x0f); + } + + private static int findScope(@NonNull InetAddress addr) { + if (isIpv6Address(addr)) { + if (addr.isMulticastAddress()) { + return getIpv6MulticastScope(addr); + } else if (addr.isLoopbackAddress() || addr.isLinkLocalAddress()) { + /** + * RFC 4291 section 2.5.3 says loopback is to be treated as having + * link-local scope. + */ + return IPV6_ADDR_SCOPE_LINKLOCAL; + } else if (addr.isSiteLocalAddress()) { + return IPV6_ADDR_SCOPE_SITELOCAL; + } else { + return IPV6_ADDR_SCOPE_GLOBAL; + } + } else if (isIpv4Address(addr)) { + if (addr.isLoopbackAddress() || addr.isLinkLocalAddress()) { + 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; + } + } + + /** + * Get the precedence for a given IPv4/IPv6 address. + * RFC 6724, section 2.1. + * + * Note that Java will return an IPv4-mapped address as an IPv4 address. + */ + private static int findPrecedence(@NonNull InetAddress addr) { + if (isIpv4Address(addr)) { + return 35; + } else if (isIpv6Address(addr)) { + if (addr.isLoopbackAddress()) { + return 50; + } else if (isIpv6Address6To4(addr)) { + return 30; + } else if (isIpv6AddressTeredo(addr)) { + return 5; + } else if (isIpv6AddressULA(addr)) { + return 3; + } else if (((Inet6Address) addr).isIPv4CompatibleAddress() || addr.isSiteLocalAddress() + || isIpv6Address6Bone(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. + */ + private static int compareIpv6PrefixMatchLen(@NonNull InetAddress srcAddr, + @NonNull InetAddress dstAddr) { + final byte[] srcByte = srcAddr.getAddress(); + final byte[] dstByte = dstAddr.getAddress(); + + // This should never happen. + if (srcByte.length != dstByte.length) return 0; + + for (int i = 0; i < dstByte.length; ++i) { + if (srcByte[i] == dstByte[i]) { + continue; + } + int x = BitUtils.uint8(srcByte[i]) ^ BitUtils.uint8(dstByte[i]); + return i * CHAR_BIT + (Integer.numberOfLeadingZeros(x) - 24); // Java ints are 32 bits + } + return dstByte.length * CHAR_BIT; + } + + /** + * Check if given network has Ipv4 capability + * This function matches the behaviour of have_ipv4 in the native resolver. + */ + public static boolean haveIpv4(@Nullable Network network) { + final SocketAddress addrIpv4 = + new InetSocketAddress(InetAddresses.parseNumericAddress("8.8.8.8"), 0); + return checkConnectivity(network, AF_INET, addrIpv4); + } + + /** + * Check if given network has Ipv6 capability + * This function matches the behaviour of have_ipv6 in the native resolver. + */ + public static boolean haveIpv6(@Nullable Network network) { + final SocketAddress addrIpv6 = + new InetSocketAddress(InetAddresses.parseNumericAddress("2000::"), 0); + return checkConnectivity(network, AF_INET6, addrIpv6); + } + + private static boolean checkConnectivity(@Nullable Network network, + int domain, @NonNull SocketAddress addr) { + final FileDescriptor socket; + try { + socket = Os.socket(domain, SOCK_DGRAM, IPPROTO_UDP); + } catch (ErrnoException e) { + return false; + } + try { + if (network != null) network.bindSocket(socket); + Os.connect(socket, addr); + } catch (IOException | ErrnoException e) { + return false; + } finally { + IoUtils.closeQuietly(socket); + } + return true; + } +} diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp index c5fc9b3628df..00e0e3a74d70 100644 --- a/core/jni/android_net_NetUtils.cpp +++ b/core/jni/android_net_NetUtils.cpp @@ -18,26 +18,27 @@ #include <vector> -#include "jni.h" -#include <nativehelper/JNIHelp.h> -#include <nativehelper/ScopedLocalRef.h> -#include "NetdClient.h" -#include <utils/misc.h> -#include <android_runtime/AndroidRuntime.h> -#include <utils/Log.h> #include <arpa/inet.h> -#include <net/if.h> #include <linux/filter.h> #include <linux/if_arp.h> #include <linux/tcp.h> +#include <net/if.h> #include <netinet/ether.h> #include <netinet/icmp6.h> #include <netinet/ip.h> #include <netinet/ip6.h> #include <netinet/udp.h> + +#include <android_runtime/AndroidRuntime.h> #include <cutils/properties.h> +#include <utils/misc.h> +#include <utils/Log.h> +#include <nativehelper/JNIHelp.h> +#include <nativehelper/ScopedLocalRef.h> +#include "NetdClient.h" #include "core_jni_helpers.h" +#include "jni.h" extern "C" { int ifc_enable(const char *ifname); @@ -303,6 +304,15 @@ static void android_net_utils_resNetworkCancel(JNIEnv *env, jobject thiz, jobjec jniSetFileDescriptorOfFD(env, javaFd, -1); } +static jint android_net_utils_getDnsNetId(JNIEnv *env, jobject thiz) { + int dnsNetId = getNetworkForDns(); + if (dnsNetId < 0) { + throwErrnoException(env, "getDnsNetId", -dnsNetId); + } + + return dnsNetId; +} + static jobject android_net_utils_getTcpRepairWindow(JNIEnv *env, jobject thiz, jobject javaFd) { if (javaFd == NULL) { jniThrowNullPointerException(env, NULL); @@ -359,6 +369,7 @@ static const JNINativeMethod gNetworkUtilMethods[] = { { "resNetworkQuery", "(ILjava/lang/String;III)Ljava/io/FileDescriptor;", (void*) android_net_utils_resNetworkQuery }, { "resNetworkResult", "(Ljava/io/FileDescriptor;)Landroid/net/DnsResolver$DnsResponse;", (void*) android_net_utils_resNetworkResult }, { "resNetworkCancel", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_resNetworkCancel }, + { "getDnsNetId", "()I", (void*) android_net_utils_getDnsNetId }, }; int register_android_net_NetworkUtils(JNIEnv* env) diff --git a/tests/net/java/android/net/util/DnsUtilsTest.java b/tests/net/java/android/net/util/DnsUtilsTest.java new file mode 100644 index 000000000000..42e340bbcba9 --- /dev/null +++ b/tests/net/java/android/net/util/DnsUtilsTest.java @@ -0,0 +1,202 @@ +/* + * 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.util; + +import static android.net.util.DnsUtils.IPV6_ADDR_SCOPE_GLOBAL; +import static android.net.util.DnsUtils.IPV6_ADDR_SCOPE_LINKLOCAL; +import static android.net.util.DnsUtils.IPV6_ADDR_SCOPE_SITELOCAL; + +import static org.junit.Assert.assertEquals; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.net.InetAddresses; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.net.InetAddress; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class DnsUtilsTest { + private InetAddress stringToAddress(@NonNull String addr) { + return InetAddresses.parseNumericAddress(addr); + } + + private DnsUtils.SortableAddress makeSortableAddress(@NonNull String addr) { + return makeSortableAddress(addr, null); + } + + private DnsUtils.SortableAddress makeSortableAddress(@NonNull String addr, + @Nullable String srcAddr) { + return new DnsUtils.SortableAddress(stringToAddress(addr), + srcAddr != null ? stringToAddress(srcAddr) : null); + } + + @Test + public void testRfc6724Comparator() { + final List<DnsUtils.SortableAddress> test = Arrays.asList( + makeSortableAddress("216.58.200.36"), // Ipv4 + makeSortableAddress("2404:6800:4008:801::2004"), // global + makeSortableAddress("::1"), // loop back + makeSortableAddress("fe80::c46f:1cff:fe04:39b4"), // link local + makeSortableAddress("::ffff:192.168.95.3"), // IPv4-mapped IPv6 + makeSortableAddress("2001::47c1"), // teredo tunneling + makeSortableAddress("::216.58.200.36"), // IPv4-compatible + makeSortableAddress("3ffe::1234:5678")); // 6bone + + final List<InetAddress> expected = Arrays.asList( + stringToAddress("::1"), // loop back + stringToAddress("fe80::c46f:1cff:fe04:39b4"), // link local + stringToAddress("2404:6800:4008:801::2004"), // global + stringToAddress("216.58.200.36"), // Ipv4 + stringToAddress("::ffff:192.168.95.3"), // IPv4-mapped IPv6 + stringToAddress("2001::47c1"), // teredo tunneling + stringToAddress("::216.58.200.36"), // IPv4-compatible + stringToAddress("3ffe::1234:5678")); // 6bone + + Collections.sort(test, new DnsUtils.Rfc6724Comparator()); + + for (int i = 0; i < test.size(); ++i) { + assertEquals(test.get(i).address, expected.get(i)); + } + + // TODO: add more combinations + } + + @Test + public void testV4SortableAddress() { + // Test V4 address + DnsUtils.SortableAddress test = makeSortableAddress("216.58.200.36"); + assertEquals(test.hasSrcAddr, 0); + assertEquals(test.prefixMatchLen, 0); + assertEquals(test.address, stringToAddress("216.58.200.36")); + assertEquals(test.labelMatch, 0); + assertEquals(test.scopeMatch, 0); + assertEquals(test.scope, IPV6_ADDR_SCOPE_GLOBAL); + assertEquals(test.label, 4); + assertEquals(test.precedence, 35); + + // Test V4 loopback address with the same source address + test = makeSortableAddress("127.1.2.3", "127.1.2.3"); + assertEquals(test.hasSrcAddr, 1); + assertEquals(test.prefixMatchLen, 0); + assertEquals(test.address, stringToAddress("127.1.2.3")); + assertEquals(test.labelMatch, 1); + assertEquals(test.scopeMatch, 1); + assertEquals(test.scope, IPV6_ADDR_SCOPE_LINKLOCAL); + assertEquals(test.label, 4); + assertEquals(test.precedence, 35); + } + + @Test + public void testV6SortableAddress() { + // Test global address + DnsUtils.SortableAddress test = makeSortableAddress("2404:6800:4008:801::2004"); + assertEquals(test.address, stringToAddress("2404:6800:4008:801::2004")); + assertEquals(test.scope, IPV6_ADDR_SCOPE_GLOBAL); + assertEquals(test.label, 1); + assertEquals(test.precedence, 40); + + // Test global address with global source address + test = makeSortableAddress("2404:6800:4008:801::2004", + "2401:fa00:fc:fd00:6d6c:7199:b8e7:41d6"); + assertEquals(test.address, stringToAddress("2404:6800:4008:801::2004")); + assertEquals(test.hasSrcAddr, 1); + assertEquals(test.scope, IPV6_ADDR_SCOPE_GLOBAL); + assertEquals(test.labelMatch, 1); + assertEquals(test.scopeMatch, 1); + assertEquals(test.label, 1); + assertEquals(test.precedence, 40); + assertEquals(test.prefixMatchLen, 13); + + // Test global address with linklocal source address + test = makeSortableAddress("2404:6800:4008:801::2004", "fe80::c46f:1cff:fe04:39b4"); + assertEquals(test.hasSrcAddr, 1); + assertEquals(test.scope, IPV6_ADDR_SCOPE_GLOBAL); + assertEquals(test.labelMatch, 1); + assertEquals(test.scopeMatch, 0); + assertEquals(test.label, 1); + assertEquals(test.precedence, 40); + assertEquals(test.prefixMatchLen, 0); + + // Test loopback address with the same source address + test = makeSortableAddress("::1", "::1"); + assertEquals(test.hasSrcAddr, 1); + assertEquals(test.prefixMatchLen, 16 * 8); + assertEquals(test.labelMatch, 1); + assertEquals(test.scopeMatch, 1); + assertEquals(test.scope, IPV6_ADDR_SCOPE_LINKLOCAL); + assertEquals(test.label, 0); + assertEquals(test.precedence, 50); + + // Test linklocal address + test = makeSortableAddress("fe80::c46f:1cff:fe04:39b4"); + assertEquals(test.scope, IPV6_ADDR_SCOPE_LINKLOCAL); + assertEquals(test.label, 1); + assertEquals(test.precedence, 40); + + // Test linklocal address + test = makeSortableAddress("fe80::"); + assertEquals(test.scope, IPV6_ADDR_SCOPE_LINKLOCAL); + assertEquals(test.label, 1); + assertEquals(test.precedence, 40); + + // Test 6to4 address + test = makeSortableAddress("2002:c000:0204::"); + assertEquals(test.scope, IPV6_ADDR_SCOPE_GLOBAL); + assertEquals(test.label, 2); + assertEquals(test.precedence, 30); + + // Test unique local address + test = makeSortableAddress("fc00::c000:13ab"); + assertEquals(test.scope, IPV6_ADDR_SCOPE_GLOBAL); + assertEquals(test.label, 13); + assertEquals(test.precedence, 3); + + // Test teredo tunneling address + test = makeSortableAddress("2001::47c1"); + assertEquals(test.scope, IPV6_ADDR_SCOPE_GLOBAL); + assertEquals(test.label, 5); + assertEquals(test.precedence, 5); + + // Test IPv4-compatible addresses + test = makeSortableAddress("::216.58.200.36"); + assertEquals(test.scope, IPV6_ADDR_SCOPE_GLOBAL); + assertEquals(test.label, 3); + assertEquals(test.precedence, 1); + + // Test site-local address + test = makeSortableAddress("fec0::cafe:3ab2"); + assertEquals(test.scope, IPV6_ADDR_SCOPE_SITELOCAL); + assertEquals(test.label, 11); + assertEquals(test.precedence, 1); + + // Test 6bone address + test = makeSortableAddress("3ffe::1234:5678"); + assertEquals(test.scope, IPV6_ADDR_SCOPE_GLOBAL); + assertEquals(test.label, 12); + assertEquals(test.precedence, 1); + } +} |