Merge "Add Rfc6724 style sort for DnsResolver and fix potential bug" am: 7a1133eb44
am: d352f4ca85 Change-Id: Ic356f42b46a3ad170fb01f22d9b3e0b7bf1585d4
This commit is contained in:
@@ -16,16 +16,17 @@
|
|||||||
|
|
||||||
package android.net;
|
package android.net;
|
||||||
|
|
||||||
|
import static android.net.NetworkUtils.getDnsNetId;
|
||||||
import static android.net.NetworkUtils.resNetworkCancel;
|
import static android.net.NetworkUtils.resNetworkCancel;
|
||||||
import static android.net.NetworkUtils.resNetworkQuery;
|
import static android.net.NetworkUtils.resNetworkQuery;
|
||||||
import static android.net.NetworkUtils.resNetworkResult;
|
import static android.net.NetworkUtils.resNetworkResult;
|
||||||
import static android.net.NetworkUtils.resNetworkSend;
|
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_ERROR;
|
||||||
import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT;
|
import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT;
|
||||||
import static android.system.OsConstants.AF_INET;
|
import static android.system.OsConstants.ENONET;
|
||||||
import static android.system.OsConstants.AF_INET6;
|
|
||||||
import static android.system.OsConstants.IPPROTO_UDP;
|
|
||||||
import static android.system.OsConstants.SOCK_DGRAM;
|
|
||||||
|
|
||||||
import android.annotation.CallbackExecutor;
|
import android.annotation.CallbackExecutor;
|
||||||
import android.annotation.IntDef;
|
import android.annotation.IntDef;
|
||||||
@@ -34,18 +35,12 @@ import android.annotation.Nullable;
|
|||||||
import android.os.CancellationSignal;
|
import android.os.CancellationSignal;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.system.ErrnoException;
|
import android.system.ErrnoException;
|
||||||
import android.system.Os;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import libcore.io.IoUtils;
|
|
||||||
|
|
||||||
import java.io.FileDescriptor;
|
import java.io.FileDescriptor;
|
||||||
import java.io.IOException;
|
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.InetSocketAddress;
|
|
||||||
import java.net.SocketAddress;
|
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -196,8 +191,8 @@ public final class DnsResolver {
|
|||||||
final Object lock = new Object();
|
final Object lock = new Object();
|
||||||
final FileDescriptor queryfd;
|
final FileDescriptor queryfd;
|
||||||
try {
|
try {
|
||||||
queryfd = resNetworkSend((network != null
|
queryfd = resNetworkSend((network != null)
|
||||||
? network.getNetIdForResolv() : NETID_UNSET), query, query.length, flags);
|
? network.getNetIdForResolv() : NETID_UNSET, query, query.length, flags);
|
||||||
} catch (ErrnoException e) {
|
} catch (ErrnoException e) {
|
||||||
executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e)));
|
executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e)));
|
||||||
return;
|
return;
|
||||||
@@ -237,8 +232,8 @@ public final class DnsResolver {
|
|||||||
final Object lock = new Object();
|
final Object lock = new Object();
|
||||||
final FileDescriptor queryfd;
|
final FileDescriptor queryfd;
|
||||||
try {
|
try {
|
||||||
queryfd = resNetworkQuery((network != null
|
queryfd = resNetworkQuery((network != null)
|
||||||
? network.getNetIdForResolv() : NETID_UNSET), domain, nsClass, nsType, flags);
|
? network.getNetIdForResolv() : NETID_UNSET, domain, nsClass, nsType, flags);
|
||||||
} catch (ErrnoException e) {
|
} catch (ErrnoException e) {
|
||||||
executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e)));
|
executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e)));
|
||||||
return;
|
return;
|
||||||
@@ -252,14 +247,16 @@ public final class DnsResolver {
|
|||||||
|
|
||||||
private class InetAddressAnswerAccumulator implements Callback<byte[]> {
|
private class InetAddressAnswerAccumulator implements Callback<byte[]> {
|
||||||
private final List<InetAddress> mAllAnswers;
|
private final List<InetAddress> mAllAnswers;
|
||||||
|
private final Network mNetwork;
|
||||||
private int mRcode;
|
private int mRcode;
|
||||||
private DnsException mDnsException;
|
private DnsException mDnsException;
|
||||||
private final Callback<? super List<InetAddress>> mUserCallback;
|
private final Callback<? super List<InetAddress>> mUserCallback;
|
||||||
private final int mTargetAnswerCount;
|
private final int mTargetAnswerCount;
|
||||||
private int mReceivedAnswerCount = 0;
|
private int mReceivedAnswerCount = 0;
|
||||||
|
|
||||||
InetAddressAnswerAccumulator(int size,
|
InetAddressAnswerAccumulator(@NonNull Network network, int size,
|
||||||
@NonNull Callback<? super List<InetAddress>> callback) {
|
@NonNull Callback<? super List<InetAddress>> callback) {
|
||||||
|
mNetwork = network;
|
||||||
mTargetAnswerCount = size;
|
mTargetAnswerCount = size;
|
||||||
mAllAnswers = new ArrayList<>();
|
mAllAnswers = new ArrayList<>();
|
||||||
mUserCallback = callback;
|
mUserCallback = callback;
|
||||||
@@ -280,8 +277,7 @@ public final class DnsResolver {
|
|||||||
private void maybeReportAnswer() {
|
private void maybeReportAnswer() {
|
||||||
if (++mReceivedAnswerCount != mTargetAnswerCount) return;
|
if (++mReceivedAnswerCount != mTargetAnswerCount) return;
|
||||||
if (mAllAnswers.isEmpty() && maybeReportError()) return;
|
if (mAllAnswers.isEmpty() && maybeReportError()) return;
|
||||||
// TODO: Do RFC6724 sort.
|
mUserCallback.onAnswer(rfc6724Sort(mNetwork, mAllAnswers), mRcode);
|
||||||
mUserCallback.onAnswer(mAllAnswers, mRcode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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,
|
* 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
|
* This method will examine the connection ability on given network, and query IPv4
|
||||||
* and IPv6 if connection is available.
|
* and IPv6 if connection is available.
|
||||||
@@ -335,8 +331,23 @@ public final class DnsResolver {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final Object lock = new Object();
|
final Object lock = new Object();
|
||||||
final boolean queryIpv6 = haveIpv6(network);
|
final Network queryNetwork;
|
||||||
final boolean queryIpv4 = haveIpv4(network);
|
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 v4fd;
|
||||||
final FileDescriptor v6fd;
|
final FileDescriptor v6fd;
|
||||||
@@ -345,9 +356,8 @@ public final class DnsResolver {
|
|||||||
|
|
||||||
if (queryIpv6) {
|
if (queryIpv6) {
|
||||||
try {
|
try {
|
||||||
v6fd = resNetworkQuery((network != null
|
v6fd = resNetworkQuery(queryNetwork.getNetIdForResolv(), domain, CLASS_IN,
|
||||||
? network.getNetIdForResolv() : NETID_UNSET),
|
TYPE_AAAA, flags);
|
||||||
domain, CLASS_IN, TYPE_AAAA, flags);
|
|
||||||
} catch (ErrnoException e) {
|
} catch (ErrnoException e) {
|
||||||
executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e)));
|
executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e)));
|
||||||
return;
|
return;
|
||||||
@@ -355,7 +365,6 @@ public final class DnsResolver {
|
|||||||
queryCount++;
|
queryCount++;
|
||||||
} else v6fd = null;
|
} else v6fd = null;
|
||||||
|
|
||||||
// TODO: Use device flag to control the sleep time.
|
|
||||||
// Avoiding gateways drop packets if queries are sent too close together
|
// Avoiding gateways drop packets if queries are sent too close together
|
||||||
try {
|
try {
|
||||||
Thread.sleep(SLEEP_TIME_MS);
|
Thread.sleep(SLEEP_TIME_MS);
|
||||||
@@ -365,9 +374,8 @@ public final class DnsResolver {
|
|||||||
|
|
||||||
if (queryIpv4) {
|
if (queryIpv4) {
|
||||||
try {
|
try {
|
||||||
v4fd = resNetworkQuery((network != null
|
v4fd = resNetworkQuery(queryNetwork.getNetIdForResolv(), domain, CLASS_IN, TYPE_A,
|
||||||
? network.getNetIdForResolv() : NETID_UNSET),
|
flags);
|
||||||
domain, CLASS_IN, TYPE_A, flags);
|
|
||||||
} catch (ErrnoException e) {
|
} catch (ErrnoException e) {
|
||||||
if (queryIpv6) resNetworkCancel(v6fd); // Closes fd, marks it invalid.
|
if (queryIpv6) resNetworkCancel(v6fd); // Closes fd, marks it invalid.
|
||||||
executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e)));
|
executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e)));
|
||||||
@@ -377,7 +385,7 @@ public final class DnsResolver {
|
|||||||
} else v4fd = null;
|
} else v4fd = null;
|
||||||
|
|
||||||
final InetAddressAnswerAccumulator accumulator =
|
final InetAddressAnswerAccumulator accumulator =
|
||||||
new InetAddressAnswerAccumulator(queryCount, callback);
|
new InetAddressAnswerAccumulator(queryNetwork, queryCount, callback);
|
||||||
|
|
||||||
synchronized (lock) {
|
synchronized (lock) {
|
||||||
if (queryIpv6) {
|
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
|
* 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}.
|
* 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 Object lock = new Object();
|
||||||
final FileDescriptor queryfd;
|
final FileDescriptor queryfd;
|
||||||
|
final Network queryNetwork;
|
||||||
try {
|
try {
|
||||||
queryfd = resNetworkQuery((network != null
|
queryNetwork = (network != null) ? network : new Network(getDnsNetId());
|
||||||
? network.getNetIdForResolv() : NETID_UNSET), domain, CLASS_IN, nsType, flags);
|
queryfd = resNetworkQuery(queryNetwork.getNetIdForResolv(), domain, CLASS_IN, nsType,
|
||||||
|
flags);
|
||||||
} catch (ErrnoException e) {
|
} catch (ErrnoException e) {
|
||||||
executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e)));
|
executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e)));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final InetAddressAnswerAccumulator accumulator =
|
final InetAddressAnswerAccumulator accumulator =
|
||||||
new InetAddressAnswerAccumulator(1, callback);
|
new InetAddressAnswerAccumulator(queryNetwork, 1, callback);
|
||||||
synchronized (lock) {
|
synchronized (lock) {
|
||||||
registerFDListener(executor, queryfd, accumulator, cancellationSignal, lock);
|
registerFDListener(executor, queryfd, accumulator, cancellationSignal, lock);
|
||||||
if (cancellationSignal == null) return;
|
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 class DnsAddressAnswer extends DnsPacket {
|
||||||
private static final String TAG = "DnsResolver.DnsAddressAnswer";
|
private static final String TAG = "DnsResolver.DnsAddressAnswer";
|
||||||
private static final boolean DBG = false;
|
private static final boolean DBG = false;
|
||||||
|
|||||||
@@ -156,6 +156,13 @@ public class NetworkUtils {
|
|||||||
*/
|
*/
|
||||||
public static native void resNetworkCancel(FileDescriptor fd);
|
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}.
|
* Get the tcp repair window associated with the {@code fd}.
|
||||||
*
|
*
|
||||||
|
|||||||
376
core/java/android/net/util/DnsUtils.java
Normal file
376
core/java/android/net/util/DnsUtils.java
Normal file
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,26 +18,27 @@
|
|||||||
|
|
||||||
#include <vector>
|
#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 <arpa/inet.h>
|
||||||
#include <net/if.h>
|
|
||||||
#include <linux/filter.h>
|
#include <linux/filter.h>
|
||||||
#include <linux/if_arp.h>
|
#include <linux/if_arp.h>
|
||||||
#include <linux/tcp.h>
|
#include <linux/tcp.h>
|
||||||
|
#include <net/if.h>
|
||||||
#include <netinet/ether.h>
|
#include <netinet/ether.h>
|
||||||
#include <netinet/icmp6.h>
|
#include <netinet/icmp6.h>
|
||||||
#include <netinet/ip.h>
|
#include <netinet/ip.h>
|
||||||
#include <netinet/ip6.h>
|
#include <netinet/ip6.h>
|
||||||
#include <netinet/udp.h>
|
#include <netinet/udp.h>
|
||||||
#include <cutils/properties.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 "core_jni_helpers.h"
|
||||||
|
#include "jni.h"
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
int ifc_enable(const char *ifname);
|
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);
|
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) {
|
static jobject android_net_utils_getTcpRepairWindow(JNIEnv *env, jobject thiz, jobject javaFd) {
|
||||||
if (javaFd == NULL) {
|
if (javaFd == NULL) {
|
||||||
jniThrowNullPointerException(env, 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 },
|
{ "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 },
|
{ "resNetworkResult", "(Ljava/io/FileDescriptor;)Landroid/net/DnsResolver$DnsResponse;", (void*) android_net_utils_resNetworkResult },
|
||||||
{ "resNetworkCancel", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_resNetworkCancel },
|
{ "resNetworkCancel", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_resNetworkCancel },
|
||||||
|
{ "getDnsNetId", "()I", (void*) android_net_utils_getDnsNetId },
|
||||||
};
|
};
|
||||||
|
|
||||||
int register_android_net_NetworkUtils(JNIEnv* env)
|
int register_android_net_NetworkUtils(JNIEnv* env)
|
||||||
|
|||||||
221
tests/net/java/android/net/util/DnsUtilsTest.java
Normal file
221
tests/net/java/android/net/util/DnsUtilsTest.java
Normal file
@@ -0,0 +1,221 @@
|
|||||||
|
/*
|
||||||
|
* 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 android.net.util.DnsUtils.rfc6724Sort;
|
||||||
|
|
||||||
|
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 testRfc6724Sort() {
|
||||||
|
final List<InetAddress> testAddresses = Arrays.asList(
|
||||||
|
stringToAddress("172.217.24.14"),
|
||||||
|
stringToAddress("216.58.200.46"),
|
||||||
|
stringToAddress("2404:6800:4008:802::200e"));
|
||||||
|
|
||||||
|
final List<InetAddress> expected = Arrays.asList(
|
||||||
|
stringToAddress("2404:6800:4008:802::200e"),
|
||||||
|
stringToAddress("172.217.24.14"),
|
||||||
|
stringToAddress("216.58.200.46"));
|
||||||
|
|
||||||
|
final List<InetAddress> result = rfc6724Sort(null, testAddresses);
|
||||||
|
|
||||||
|
assertEquals(result.size(), testAddresses.size());
|
||||||
|
assertEquals(result, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user