Revert "[ST02] Add methods for synthesizing DNS packets"
Revert "[ST02.1] Add TYPE_CNAME constant to DnsResolver" Revert submission 1387135-st02 Reason for revert: Usage of TYPE_CNAME in a library shared among modules does not interact well with current module release process Reverted Changes: Ib5616c65c:[ST02.2] Use the getters of DnsHeader I5e58f99b0:[ST02.1] Add TYPE_CNAME constant to DnsResolver I0c1547cbc:[ST02] Add methods for synthesizing DNS packets Change-Id: I9f8de47c9ba9fb4db7cf3695966f222f68a9a290
This commit is contained in:
@@ -96,10 +96,7 @@ java_library {
|
||||
visibility: [
|
||||
"//packages/services/Iwlan:__subpackages__",
|
||||
],
|
||||
libs: [
|
||||
"framework-annotations-lib",
|
||||
"framework-connectivity.stubs.module_lib",
|
||||
],
|
||||
libs: ["framework-annotations-lib"],
|
||||
}
|
||||
|
||||
filegroup {
|
||||
|
||||
@@ -16,35 +16,15 @@
|
||||
|
||||
package com.android.net.module.util;
|
||||
|
||||
import static android.net.DnsResolver.TYPE_A;
|
||||
import static android.net.DnsResolver.TYPE_AAAA;
|
||||
import static android.net.DnsResolver.TYPE_CNAME;
|
||||
|
||||
import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
|
||||
import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
|
||||
import static com.android.net.module.util.DnsPacketUtils.DnsRecordParser.domainNameToLabels;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.net.module.util.DnsPacketUtils.DnsRecordParser;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.net.InetAddress;
|
||||
import java.nio.BufferUnderflowException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Defines basic data for DNS protocol based on RFC 1035.
|
||||
@@ -70,29 +50,13 @@ public abstract class DnsPacket {
|
||||
}
|
||||
|
||||
/**
|
||||
* DNS header for DNS protocol based on RFC 1035 section 4.1.1.
|
||||
*
|
||||
* 1 1 1 1 1 1
|
||||
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
|
||||
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
* | ID |
|
||||
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
* |QR| Opcode |AA|TC|RD|RA| Z | RCODE |
|
||||
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
* | QDCOUNT |
|
||||
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
* | ANCOUNT |
|
||||
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
* | NSCOUNT |
|
||||
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
* | ARCOUNT |
|
||||
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
* DNS header for DNS protocol based on RFC 1035.
|
||||
*/
|
||||
public static class DnsHeader {
|
||||
public class DnsHeader {
|
||||
private static final String TAG = "DnsHeader";
|
||||
private static final int SIZE_IN_BYTES = 12;
|
||||
private final int mId;
|
||||
private final int mFlags;
|
||||
public final int id;
|
||||
public final int flags;
|
||||
public final int rcode;
|
||||
private final int[] mRecordCount;
|
||||
|
||||
/* If this bit in the 'flags' field is set to 0, the DNS message corresponding to this
|
||||
@@ -109,11 +73,10 @@ public abstract class DnsPacket {
|
||||
* advanced to the end of the DNS header record.
|
||||
* This is meant to chain with other methods reading a DNS response in sequence.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public DnsHeader(@NonNull ByteBuffer buf) throws BufferUnderflowException {
|
||||
Objects.requireNonNull(buf);
|
||||
mId = Short.toUnsignedInt(buf.getShort());
|
||||
mFlags = Short.toUnsignedInt(buf.getShort());
|
||||
DnsHeader(@NonNull ByteBuffer buf) throws BufferUnderflowException {
|
||||
id = Short.toUnsignedInt(buf.getShort());
|
||||
flags = Short.toUnsignedInt(buf.getShort());
|
||||
rcode = flags & 0xF;
|
||||
mRecordCount = new int[NUM_SECTIONS];
|
||||
for (int i = 0; i < NUM_SECTIONS; ++i) {
|
||||
mRecordCount[i] = Short.toUnsignedInt(buf.getShort());
|
||||
@@ -125,23 +88,7 @@ public abstract class DnsPacket {
|
||||
* RFC 1035 Section 4.1.1.
|
||||
*/
|
||||
public boolean isResponse() {
|
||||
return (mFlags & (1 << FLAGS_SECTION_QR_BIT)) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new DnsHeader from specified parameters.
|
||||
*
|
||||
* This constructor only builds the question and answer sections. Authority
|
||||
* and additional sections are not supported. Useful when synthesizing dns
|
||||
* responses from query or reply packets.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public DnsHeader(int id, int flags, int qdcount, int ancount) {
|
||||
this.mId = id;
|
||||
this.mFlags = flags;
|
||||
mRecordCount = new int[NUM_SECTIONS];
|
||||
mRecordCount[QDSECTION] = qdcount;
|
||||
mRecordCount[ANSECTION] = ancount;
|
||||
return (flags & (1 << FLAGS_SECTION_QR_BIT)) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -150,103 +97,15 @@ public abstract class DnsPacket {
|
||||
public int getRecordCount(int type) {
|
||||
return mRecordCount[type];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get flags of this instance.
|
||||
*/
|
||||
public int getFlags() {
|
||||
return mFlags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get id of this instance.
|
||||
*/
|
||||
public int getId() {
|
||||
return mId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DnsHeader{" + "id=" + mId + ", flags=" + mFlags
|
||||
+ ", recordCounts=" + Arrays.toString(mRecordCount) + '}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o.getClass() != getClass()) return false;
|
||||
final DnsHeader other = (DnsHeader) o;
|
||||
return mId == other.mId
|
||||
&& mFlags == other.mFlags
|
||||
&& Arrays.equals(mRecordCount, other.mRecordCount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 31 * mId + 37 * mFlags + Arrays.hashCode(mRecordCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get DnsHeader as byte array.
|
||||
*/
|
||||
@NonNull
|
||||
public byte[] getBytes() {
|
||||
// TODO: if this is called often, optimize the ByteBuffer out and write to the
|
||||
// array directly.
|
||||
final ByteBuffer buf = ByteBuffer.allocate(SIZE_IN_BYTES);
|
||||
buf.putShort((short) mId);
|
||||
buf.putShort((short) mFlags);
|
||||
for (int i = 0; i < NUM_SECTIONS; ++i) {
|
||||
buf.putShort((short) mRecordCount[i]);
|
||||
}
|
||||
return buf.array();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Superclass for DNS questions and DNS resource records.
|
||||
*
|
||||
* DNS questions (No TTL/RDLENGTH/RDATA) based on RFC 1035 section 4.1.2.
|
||||
* 1 1 1 1 1 1
|
||||
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
|
||||
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
* | |
|
||||
* / QNAME /
|
||||
* / /
|
||||
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
* | QTYPE |
|
||||
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
* | QCLASS |
|
||||
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
*
|
||||
* DNS resource records (With TTL/RDLENGTH/RDATA) based on RFC 1035 section 4.1.3.
|
||||
* 1 1 1 1 1 1
|
||||
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
|
||||
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
* | |
|
||||
* / /
|
||||
* / NAME /
|
||||
* | |
|
||||
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
* | TYPE |
|
||||
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
* | CLASS |
|
||||
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
* | TTL |
|
||||
* | |
|
||||
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
* | RDLENGTH |
|
||||
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
|
||||
* / RDATA /
|
||||
* / /
|
||||
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
* DNS questions (No TTL/RDATA)
|
||||
* DNS resource records (With TTL/RDATA)
|
||||
*/
|
||||
// TODO: Make DnsResourceRecord and DnsQuestion subclasses of DnsRecord, and construct
|
||||
// corresponding object from factory methods.
|
||||
public static class DnsRecord {
|
||||
// Refer to RFC 1035 section 2.3.4 for MAXNAMESIZE.
|
||||
// NAME_NORMAL and NAME_COMPRESSION are used for checking name compression,
|
||||
// refer to rfc 1035 section 4.1.4.
|
||||
public class DnsRecord {
|
||||
private static final int MAXNAMESIZE = 255;
|
||||
public static final int NAME_NORMAL = 0;
|
||||
public static final int NAME_COMPRESSION = 0xC0;
|
||||
@@ -258,31 +117,22 @@ public abstract class DnsPacket {
|
||||
public final int nsClass;
|
||||
public final long ttl;
|
||||
private final byte[] mRdata;
|
||||
/**
|
||||
* Type of this DNS record.
|
||||
*/
|
||||
@RecordType
|
||||
public final int rType;
|
||||
|
||||
/**
|
||||
* Create a new DnsRecord from a positioned ByteBuffer.
|
||||
*
|
||||
* Reads the passed ByteBuffer from its current position and decodes a DNS record.
|
||||
* When this constructor returns, the reading position of the ByteBuffer has been
|
||||
* advanced to the end of the DNS resource record.
|
||||
* advanced to the end of the DNS header record.
|
||||
* This is meant to chain with other methods reading a DNS response in sequence.
|
||||
*
|
||||
* @param rType Type of the record.
|
||||
* @param buf ByteBuffer input of record, must be in network byte order
|
||||
* (which is the default).
|
||||
*/
|
||||
@VisibleForTesting(visibility = PACKAGE)
|
||||
public DnsRecord(@RecordType int rType, @NonNull ByteBuffer buf)
|
||||
DnsRecord(int recordType, @NonNull ByteBuffer buf)
|
||||
throws BufferUnderflowException, ParseException {
|
||||
Objects.requireNonNull(buf);
|
||||
this.rType = rType;
|
||||
dName = DnsRecordParser.parseName(buf, 0 /* Parse depth */,
|
||||
true /* isNameCompressionSupported */);
|
||||
/* isNameCompressionSupported= */ true);
|
||||
if (dName.length() > MAXNAMESIZE) {
|
||||
throw new ParseException(
|
||||
"Parse name fail, name size is too long: " + dName.length());
|
||||
@@ -290,7 +140,7 @@ public abstract class DnsPacket {
|
||||
nsType = Short.toUnsignedInt(buf.getShort());
|
||||
nsClass = Short.toUnsignedInt(buf.getShort());
|
||||
|
||||
if (rType != QDSECTION) {
|
||||
if (recordType != QDSECTION) {
|
||||
ttl = Integer.toUnsignedLong(buf.getInt());
|
||||
final int length = Short.toUnsignedInt(buf.getShort());
|
||||
mRdata = new byte[length];
|
||||
@@ -301,95 +151,6 @@ public abstract class DnsPacket {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make an A or AAAA record based on the specified parameters.
|
||||
*
|
||||
* @param rType Type of the record, can be {@link #ANSECTION}, {@link #ARSECTION}
|
||||
* or {@link #NSSECTION}.
|
||||
* @param dName Domain name of the record.
|
||||
* @param nsClass Class of the record. See RFC 1035 section 3.2.4.
|
||||
* @param ttl time interval (in seconds) that the resource record may be
|
||||
* cached before it should be discarded. Zero values are
|
||||
* interpreted to mean that the RR can only be used for the
|
||||
* transaction in progress, and should not be cached.
|
||||
* @param address Instance of {@link InetAddress}
|
||||
* @return A record if the {@code address} is an IPv4 address, or AAAA record if the
|
||||
* {@code address} is an IPv6 address.
|
||||
*/
|
||||
public static DnsRecord makeAOrAAAARecord(int rType, @NonNull String dName,
|
||||
int nsClass, long ttl, @NonNull InetAddress address) throws IOException {
|
||||
final int nsType = (address.getAddress().length == 4) ? TYPE_A : TYPE_AAAA;
|
||||
return new DnsRecord(rType, dName, nsType, nsClass, ttl, address, null /* rDataStr */);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make an CNAME record based on the specified parameters.
|
||||
*
|
||||
* @param rType Type of the record, can be {@link #ANSECTION}, {@link #ARSECTION}
|
||||
* or {@link #NSSECTION}.
|
||||
* @param dName Domain name of the record.
|
||||
* @param nsClass Class of the record. See RFC 1035 section 3.2.4.
|
||||
* @param ttl time interval (in seconds) that the resource record may be
|
||||
* cached before it should be discarded. Zero values are
|
||||
* interpreted to mean that the RR can only be used for the
|
||||
* transaction in progress, and should not be cached.
|
||||
* @param domainName Canonical name of the {@code dName}.
|
||||
* @return A record if the {@code address} is an IPv4 address, or AAAA record if the
|
||||
* {@code address} is an IPv6 address.
|
||||
*/
|
||||
public static DnsRecord makeCNameRecord(int rType, @NonNull String dName, int nsClass,
|
||||
long ttl, @NonNull String domainName) throws IOException {
|
||||
return new DnsRecord(rType, dName, TYPE_CNAME, nsClass, ttl, null /* address */,
|
||||
domainName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a DNS question based on the specified parameters.
|
||||
*/
|
||||
public static DnsRecord makeQuestion(@NonNull String dName, int nsType, int nsClass) {
|
||||
return new DnsRecord(dName, nsType, nsClass);
|
||||
}
|
||||
|
||||
private static String requireHostName(@NonNull String name) {
|
||||
if (!DnsRecordParser.isHostName(name)) {
|
||||
throw new IllegalArgumentException("Expected domain name but got " + name);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new query DnsRecord from specified parameters, useful when synthesizing
|
||||
* dns response.
|
||||
*/
|
||||
private DnsRecord(@NonNull String dName, int nsType, int nsClass) {
|
||||
this.rType = QDSECTION;
|
||||
this.dName = requireHostName(dName);
|
||||
this.nsType = nsType;
|
||||
this.nsClass = nsClass;
|
||||
mRdata = null;
|
||||
this.ttl = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new CNAME/A/AAAA DnsRecord from specified parameters.
|
||||
*
|
||||
* @param address The address only used when synthesizing A or AAAA record.
|
||||
* @param rDataStr The alias of the domain, only used when synthesizing CNAME record.
|
||||
*/
|
||||
private DnsRecord(@RecordType int rType, @NonNull String dName, int nsType, int nsClass,
|
||||
long ttl, @Nullable InetAddress address, @Nullable String rDataStr)
|
||||
throws IOException {
|
||||
this.rType = rType;
|
||||
this.dName = requireHostName(dName);
|
||||
this.nsType = nsType;
|
||||
this.nsClass = nsClass;
|
||||
if (rType < 0 || rType >= NUM_SECTIONS || rType == QDSECTION) {
|
||||
throw new IllegalArgumentException("Unexpected record type: " + rType);
|
||||
}
|
||||
mRdata = nsType == TYPE_CNAME ? domainNameToLabels(rDataStr) : address.getAddress();
|
||||
this.ttl = ttl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a copy of rdata.
|
||||
*/
|
||||
@@ -398,84 +159,13 @@ public abstract class DnsPacket {
|
||||
return (mRdata == null) ? null : mRdata.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get DnsRecord as byte array.
|
||||
*/
|
||||
@NonNull
|
||||
public byte[] getBytes() throws IOException {
|
||||
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
final DataOutputStream dos = new DataOutputStream(baos);
|
||||
dos.write(domainNameToLabels(dName));
|
||||
dos.writeShort(nsType);
|
||||
dos.writeShort(nsClass);
|
||||
if (rType != QDSECTION) {
|
||||
dos.writeInt((int) ttl);
|
||||
if (mRdata == null) {
|
||||
dos.writeShort(0);
|
||||
} else {
|
||||
dos.writeShort(mRdata.length);
|
||||
dos.write(mRdata);
|
||||
}
|
||||
}
|
||||
return baos.toByteArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o.getClass() != getClass()) return false;
|
||||
final DnsRecord other = (DnsRecord) o;
|
||||
return rType == other.rType
|
||||
&& nsType == other.nsType
|
||||
&& nsClass == other.nsClass
|
||||
&& ttl == other.ttl
|
||||
&& TextUtils.equals(dName, other.dName)
|
||||
&& Arrays.equals(mRdata, other.mRdata);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 31 * Objects.hash(dName)
|
||||
+ 37 * ((int) (ttl & 0xFFFFFFFF))
|
||||
+ 41 * ((int) (ttl >> 32))
|
||||
+ 43 * nsType
|
||||
+ 47 * nsClass
|
||||
+ 53 * rType
|
||||
+ Arrays.hashCode(mRdata);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DnsRecord{"
|
||||
+ "rType=" + rType
|
||||
+ ", dName='" + dName + '\''
|
||||
+ ", nsType=" + nsType
|
||||
+ ", nsClass=" + nsClass
|
||||
+ ", ttl=" + ttl
|
||||
+ ", mRdata=" + Arrays.toString(mRdata)
|
||||
+ '}';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Header section types, refer to RFC 1035 section 4.1.1.
|
||||
*/
|
||||
public static final int QDSECTION = 0;
|
||||
public static final int ANSECTION = 1;
|
||||
public static final int NSSECTION = 2;
|
||||
public static final int ARSECTION = 3;
|
||||
@VisibleForTesting(visibility = PRIVATE)
|
||||
static final int NUM_SECTIONS = ARSECTION + 1;
|
||||
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef(value = {
|
||||
QDSECTION,
|
||||
ANSECTION,
|
||||
NSSECTION,
|
||||
ARSECTION,
|
||||
})
|
||||
public @interface RecordType {}
|
||||
|
||||
private static final int NUM_SECTIONS = ARSECTION + 1;
|
||||
|
||||
private static final String TAG = DnsPacket.class.getSimpleName();
|
||||
|
||||
@@ -499,7 +189,9 @@ public abstract class DnsPacket {
|
||||
|
||||
for (int i = 0; i < NUM_SECTIONS; ++i) {
|
||||
final int count = mHeader.getRecordCount(i);
|
||||
mRecords[i] = new ArrayList(count);
|
||||
if (count > 0) {
|
||||
mRecords[i] = new ArrayList(count);
|
||||
}
|
||||
for (int j = 0; j < count; ++j) {
|
||||
try {
|
||||
mRecords[i].add(new DnsRecord(i, buffer));
|
||||
@@ -509,61 +201,4 @@ public abstract class DnsPacket {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link #DnsPacket} from specified parameters.
|
||||
*
|
||||
* Note that authority records section and additional records section is not supported.
|
||||
*/
|
||||
protected DnsPacket(@NonNull DnsHeader header, @NonNull List<DnsRecord> qd,
|
||||
@NonNull List<DnsRecord> an) {
|
||||
mHeader = Objects.requireNonNull(header);
|
||||
mRecords = new List[NUM_SECTIONS];
|
||||
mRecords[QDSECTION] = Collections.unmodifiableList(new ArrayList<>(qd));
|
||||
mRecords[ANSECTION] = Collections.unmodifiableList(new ArrayList<>(an));
|
||||
mRecords[NSSECTION] = new ArrayList<>();
|
||||
mRecords[ARSECTION] = new ArrayList<>();
|
||||
for (int i = 0; i < NUM_SECTIONS; i++) {
|
||||
if (mHeader.mRecordCount[i] != mRecords[i].size()) {
|
||||
throw new IllegalArgumentException("Record count mismatch: expected "
|
||||
+ mHeader.mRecordCount[i] + " but was " + mRecords[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get DnsPacket as byte array.
|
||||
*/
|
||||
public @NonNull byte[] getBytes() throws IOException {
|
||||
final ByteArrayOutputStream buf = new ByteArrayOutputStream();
|
||||
buf.write(mHeader.getBytes());
|
||||
|
||||
for (int i = 0; i < NUM_SECTIONS; ++i) {
|
||||
for (final DnsRecord record : mRecords[i]) {
|
||||
buf.write(record.getBytes());
|
||||
}
|
||||
}
|
||||
return buf.toByteArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DnsPacket{" + "header=" + mHeader + ", records='" + Arrays.toString(mRecords) + '}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o.getClass() != getClass()) return false;
|
||||
final DnsPacket other = (DnsPacket) o;
|
||||
return Objects.equals(mHeader, other.mHeader)
|
||||
&& Arrays.deepEquals(mRecords, other.mRecords);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = Objects.hash(mHeader);
|
||||
result = 31 * result + Arrays.hashCode(mRecords);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,19 +20,10 @@ import static com.android.net.module.util.DnsPacket.DnsRecord.NAME_COMPRESSION;
|
||||
import static com.android.net.module.util.DnsPacket.DnsRecord.NAME_NORMAL;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.net.InetAddresses;
|
||||
import android.net.ParseException;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Patterns;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.BufferUnderflowException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.FieldPosition;
|
||||
|
||||
@@ -47,7 +38,6 @@ public final class DnsPacketUtils {
|
||||
*/
|
||||
public static class DnsRecordParser {
|
||||
private static final int MAXLABELSIZE = 63;
|
||||
private static final int MAXNAMESIZE = 255;
|
||||
private static final int MAXLABELCOUNT = 128;
|
||||
|
||||
private static final DecimalFormat sByteFormat = new DecimalFormat();
|
||||
@@ -58,8 +48,7 @@ public final class DnsPacketUtils {
|
||||
*
|
||||
* <p>Follows the same conversion rules of the native code (ns_name.c in libc).
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static String labelToString(@NonNull byte[] label) {
|
||||
private static String labelToString(@NonNull byte[] label) {
|
||||
final StringBuffer sb = new StringBuffer();
|
||||
|
||||
for (int i = 0; i < label.length; ++i) {
|
||||
@@ -82,52 +71,6 @@ public final class DnsPacketUtils {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts domain name to labels according to RFC 1035.
|
||||
*
|
||||
* @param name Domain name as String that needs to be converted to labels.
|
||||
* @return An encoded byte array that is constructed out of labels,
|
||||
* and ends with zero-length label.
|
||||
* @throws ParseException if failed to parse the given domain name or
|
||||
* IOException if failed to output labels.
|
||||
*/
|
||||
public static @NonNull byte[] domainNameToLabels(@NonNull String name) throws
|
||||
IOException, ParseException {
|
||||
if (name.length() > MAXNAMESIZE) {
|
||||
throw new ParseException("Domain name exceeds max length: " + name.length());
|
||||
}
|
||||
if (!isHostName(name)) {
|
||||
throw new ParseException("Failed to parse domain name: " + name);
|
||||
}
|
||||
final ByteArrayOutputStream buf = new ByteArrayOutputStream();
|
||||
final String[] labels = name.split("\\.");
|
||||
for (final String label : labels) {
|
||||
if (label.length() > MAXLABELSIZE) {
|
||||
throw new ParseException("label is too long: " + label);
|
||||
}
|
||||
buf.write(label.length());
|
||||
// Encode as UTF-8 as suggested in RFC 6055 section 3.
|
||||
buf.write(label.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
buf.write(0x00); // end with zero-length label
|
||||
return buf.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the input is a valid hostname based on rfc 1035 section 3.3.
|
||||
*
|
||||
* @param hostName the target host name.
|
||||
* @return true if the input is a valid hostname.
|
||||
*/
|
||||
public static boolean isHostName(@Nullable String hostName) {
|
||||
// TODO: Use {@code Patterns.HOST_NAME} if available.
|
||||
// Patterns.DOMAIN_NAME accepts host names or IP addresses, so reject
|
||||
// IP addresses.
|
||||
return hostName != null
|
||||
&& Patterns.DOMAIN_NAME.matcher(hostName).matches()
|
||||
&& !InetAddresses.isNumericAddress(hostName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the domain / target name of a DNS record.
|
||||
*
|
||||
|
||||
@@ -16,45 +16,26 @@
|
||||
|
||||
package com.android.net.module.util;
|
||||
|
||||
import static android.net.DnsResolver.CLASS_IN;
|
||||
import static android.net.DnsResolver.TYPE_A;
|
||||
import static android.net.DnsResolver.TYPE_AAAA;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import libcore.net.InetAddressUtils;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.BufferUnderflowException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class DnsPacketTest {
|
||||
private static final int TEST_DNS_PACKET_ID = 0x7722;
|
||||
private static final int TEST_DNS_PACKET_FLAGS = 0x8180;
|
||||
|
||||
private void assertHeaderParses(DnsPacket.DnsHeader header, int id, int flag,
|
||||
int qCount, int aCount, int nsCount, int arCount) {
|
||||
assertEquals(header.getId(), id);
|
||||
assertEquals(header.getFlags(), flag);
|
||||
assertEquals(header.id, id);
|
||||
assertEquals(header.flags, flag);
|
||||
assertEquals(header.getRecordCount(DnsPacket.QDSECTION), qCount);
|
||||
assertEquals(header.getRecordCount(DnsPacket.ANSECTION), aCount);
|
||||
assertEquals(header.getRecordCount(DnsPacket.NSSECTION), nsCount);
|
||||
@@ -70,16 +51,11 @@ public class DnsPacketTest {
|
||||
assertTrue(Arrays.equals(record.getRR(), rr));
|
||||
}
|
||||
|
||||
static class TestDnsPacket extends DnsPacket {
|
||||
class TestDnsPacket extends DnsPacket {
|
||||
TestDnsPacket(byte[] data) throws DnsPacket.ParseException {
|
||||
super(data);
|
||||
}
|
||||
|
||||
TestDnsPacket(@NonNull DnsHeader header, @Nullable ArrayList<DnsRecord> qd,
|
||||
@Nullable ArrayList<DnsRecord> an) {
|
||||
super(header, qd, an);
|
||||
}
|
||||
|
||||
public DnsHeader getHeader() {
|
||||
return mHeader;
|
||||
}
|
||||
@@ -180,247 +156,4 @@ public class DnsPacketTest {
|
||||
new byte[]{ 0x24, 0x04, 0x68, 0x00, 0x40, 0x05, 0x08, 0x0d,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x04 });
|
||||
}
|
||||
|
||||
/** Verifies that the synthesized {@link DnsPacket.DnsHeader} can be parsed correctly. */
|
||||
@Test
|
||||
public void testDnsHeaderSynthesize() {
|
||||
final DnsPacket.DnsHeader testHeader = new DnsPacket.DnsHeader(TEST_DNS_PACKET_ID,
|
||||
TEST_DNS_PACKET_FLAGS, 3 /* qcount */, 5 /* ancount */);
|
||||
final DnsPacket.DnsHeader actualHeader = new DnsPacket.DnsHeader(
|
||||
ByteBuffer.wrap(testHeader.getBytes()));
|
||||
assertEquals(testHeader, actualHeader);
|
||||
}
|
||||
|
||||
/** Verifies that the synthesized {@link DnsPacket.DnsRecord} can be parsed correctly. */
|
||||
@Test
|
||||
public void testDnsRecordSynthesize() throws IOException {
|
||||
assertDnsRecordRoundTrip(
|
||||
DnsPacket.DnsRecord.makeAOrAAAARecord(DnsPacket.ANSECTION,
|
||||
"test.com", CLASS_IN, 5 /* ttl */,
|
||||
InetAddressUtils.parseNumericAddress("abcd::fedc")));
|
||||
assertDnsRecordRoundTrip(DnsPacket.DnsRecord.makeQuestion("test.com", TYPE_AAAA, CLASS_IN));
|
||||
assertDnsRecordRoundTrip(DnsPacket.DnsRecord.makeCNameRecord(DnsPacket.ANSECTION,
|
||||
"test.com", CLASS_IN, 0 /* ttl */, "example.com"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies ttl/rData error handling when parsing
|
||||
* {@link DnsPacket.DnsRecord} from bytes.
|
||||
*/
|
||||
@Test
|
||||
public void testDnsRecordTTLRDataErrorHandling() throws IOException {
|
||||
// Verify the constructor ignore ttl/rData of questions even if they are supplied.
|
||||
final byte[] qdWithTTLRData = new byte[]{
|
||||
0x03, 0x77, 0x77, 0x77, 0x06, 0x67, 0x6F, 0x6F, 0x67, 0x6c, 0x65,
|
||||
0x03, 0x63, 0x6f, 0x6d, 0x00, /* Name */
|
||||
0x00, 0x00, /* Type */
|
||||
0x00, 0x01, /* Class */
|
||||
0x00, 0x00, 0x01, 0x2b, /* TTL */
|
||||
0x00, 0x04, /* Data length */
|
||||
(byte) 0xac, (byte) 0xd9, (byte) 0xa1, (byte) 0x84 /* Address */};
|
||||
final DnsPacket.DnsRecord questionsFromBytes =
|
||||
new DnsPacket.DnsRecord(DnsPacket.QDSECTION, ByteBuffer.wrap(qdWithTTLRData));
|
||||
assertEquals(0, questionsFromBytes.ttl);
|
||||
assertNull(questionsFromBytes.getRR());
|
||||
|
||||
// Verify ANSECTION must have rData when constructing.
|
||||
final byte[] anWithoutTTLRData = new byte[]{
|
||||
0x03, 0x77, 0x77, 0x77, 0x06, 0x67, 0x6F, 0x6F, 0x67, 0x6c, 0x65,
|
||||
0x03, 0x63, 0x6f, 0x6d, 0x00, /* Name */
|
||||
0x00, 0x01, /* Type */
|
||||
0x00, 0x01, /* Class */};
|
||||
assertThrows(BufferUnderflowException.class, () ->
|
||||
new DnsPacket.DnsRecord(DnsPacket.ANSECTION, ByteBuffer.wrap(anWithoutTTLRData)));
|
||||
}
|
||||
|
||||
private void assertDnsRecordRoundTrip(DnsPacket.DnsRecord before)
|
||||
throws IOException {
|
||||
final DnsPacket.DnsRecord after = new DnsPacket.DnsRecord(before.rType,
|
||||
ByteBuffer.wrap(before.getBytes()));
|
||||
assertEquals(after, before);
|
||||
}
|
||||
|
||||
/** Verifies that the synthesized {@link DnsPacket} can be parsed correctly. */
|
||||
@Test
|
||||
public void testDnsPacketSynthesize() throws IOException {
|
||||
// Ipv4 dns response packet generated by scapy:
|
||||
// dns_r = scapy.DNS(
|
||||
// id=0xbeef,
|
||||
// qr=1,
|
||||
// qd=scapy.DNSQR(qname="hello.example.com"),
|
||||
// an=scapy.DNSRR(rrname="hello.example.com", type="CNAME", rdata='test.com') /
|
||||
// scapy.DNSRR(rrname="hello.example.com", rdata='1.2.3.4'))
|
||||
// scapy.hexdump(dns_r)
|
||||
// dns_r.show2()
|
||||
// Note that since the synthesizing does not support name compression yet, the domain
|
||||
// name of the sample need to be uncompressed when generating.
|
||||
final byte[] v4BlobUncompressed = new byte[]{
|
||||
/* Header */
|
||||
(byte) 0xbe, (byte) 0xef, /* Transaction ID */
|
||||
(byte) 0x81, 0x00, /* Flags */
|
||||
0x00, 0x01, /* Questions */
|
||||
0x00, 0x02, /* Answer RRs */
|
||||
0x00, 0x00, /* Authority RRs */
|
||||
0x00, 0x00, /* Additional RRs */
|
||||
/* Queries */
|
||||
0x05, 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x07, 0x65, 0x78, 0x61,
|
||||
0x6D, 0x70, 0x6C, 0x65, 0x03, 0x63, 0x6F, 0x6D, 0x00, /* Name: hello.example.com */
|
||||
0x00, 0x01, /* Type */
|
||||
0x00, 0x01, /* Class */
|
||||
/* Answers */
|
||||
0x05, 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x07, 0x65, 0x78, 0x61,
|
||||
0x6D, 0x70, 0x6C, 0x65, 0x03, 0x63, 0x6F, 0x6D, 0x00, /* Name: hello.example.com */
|
||||
0x00, 0x05, /* Type */
|
||||
0x00, 0x01, /* Class */
|
||||
0x00, 0x00, 0x00, 0x00, /* TTL */
|
||||
0x00, 0x0A, /* Data length */
|
||||
0x04, 0x74, 0x65, 0x73, 0x74, 0x03, 0x63, 0x6F, 0x6D, 0x00, /* Alias: test.com */
|
||||
0x05, 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x07, 0x65, 0x78, 0x61,
|
||||
0x6D, 0x70, 0x6C, 0x65, 0x03, 0x63, 0x6F, 0x6D, 0x00, /* Name: hello.example.com */
|
||||
0x00, 0x01, /* Type */
|
||||
0x00, 0x01, /* Class */
|
||||
0x00, 0x00, 0x00, 0x00, /* TTL */
|
||||
0x00, 0x04, /* Data length */
|
||||
0x01, 0x02, 0x03, 0x04, /* Address: 1.2.3.4 */
|
||||
};
|
||||
|
||||
// Forge one via constructors.
|
||||
final DnsPacket.DnsHeader testHeader = new DnsPacket.DnsHeader(0xbeef,
|
||||
0x8100, 1 /* qcount */, 2 /* ancount */);
|
||||
final ArrayList<DnsPacket.DnsRecord> qlist = new ArrayList<>();
|
||||
final ArrayList<DnsPacket.DnsRecord> alist = new ArrayList<>();
|
||||
qlist.add(DnsPacket.DnsRecord.makeQuestion(
|
||||
"hello.example.com", TYPE_A, CLASS_IN));
|
||||
alist.add(DnsPacket.DnsRecord.makeCNameRecord(
|
||||
DnsPacket.ANSECTION, "hello.example.com", CLASS_IN, 0 /* ttl */, "test.com"));
|
||||
alist.add(DnsPacket.DnsRecord.makeAOrAAAARecord(
|
||||
DnsPacket.ANSECTION, "hello.example.com", CLASS_IN, 0 /* ttl */,
|
||||
InetAddressUtils.parseNumericAddress("1.2.3.4")));
|
||||
final TestDnsPacket testPacket = new TestDnsPacket(testHeader, qlist, alist);
|
||||
|
||||
// Assert content equals in both ways.
|
||||
assertTrue(Arrays.equals(v4BlobUncompressed, testPacket.getBytes()));
|
||||
assertEquals(new TestDnsPacket(v4BlobUncompressed), testPacket);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDnsPacketSynthesize_recordCountMismatch() throws IOException {
|
||||
final DnsPacket.DnsHeader testHeader = new DnsPacket.DnsHeader(0xbeef,
|
||||
0x8100, 1 /* qcount */, 1 /* ancount */);
|
||||
final ArrayList<DnsPacket.DnsRecord> qlist = new ArrayList<>();
|
||||
final ArrayList<DnsPacket.DnsRecord> alist = new ArrayList<>();
|
||||
qlist.add(DnsPacket.DnsRecord.makeQuestion(
|
||||
"hello.example.com", TYPE_A, CLASS_IN));
|
||||
|
||||
// Assert throws if the supplied answer records fewer than the declared count.
|
||||
assertThrows(IllegalArgumentException.class, () ->
|
||||
new TestDnsPacket(testHeader, qlist, alist));
|
||||
|
||||
// Assert throws if the supplied answer records more than the declared count.
|
||||
alist.add(DnsPacket.DnsRecord.makeCNameRecord(
|
||||
DnsPacket.ANSECTION, "hello.example.com", CLASS_IN, 0 /* ttl */, "test.com"));
|
||||
alist.add(DnsPacket.DnsRecord.makeAOrAAAARecord(
|
||||
DnsPacket.ANSECTION, "hello.example.com", CLASS_IN, 0 /* ttl */,
|
||||
InetAddressUtils.parseNumericAddress("1.2.3.4")));
|
||||
assertThrows(IllegalArgumentException.class, () ->
|
||||
new TestDnsPacket(testHeader, qlist, alist));
|
||||
|
||||
// Assert counts matched if the byte buffer still has data when parsing ended.
|
||||
final byte[] blobTooMuchData = new byte[]{
|
||||
/* Header */
|
||||
(byte) 0xbe, (byte) 0xef, /* Transaction ID */
|
||||
(byte) 0x81, 0x00, /* Flags */
|
||||
0x00, 0x00, /* Questions */
|
||||
0x00, 0x00, /* Answer RRs */
|
||||
0x00, 0x00, /* Authority RRs */
|
||||
0x00, 0x00, /* Additional RRs */
|
||||
/* Queries */
|
||||
0x05, 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x07, 0x65, 0x78, 0x61,
|
||||
0x6D, 0x70, 0x6C, 0x65, 0x03, 0x63, 0x6F, 0x6D, 0x00, /* Name */
|
||||
0x00, 0x01, /* Type */
|
||||
0x00, 0x01, /* Class */
|
||||
};
|
||||
final TestDnsPacket packetFromTooMuchData = new TestDnsPacket(blobTooMuchData);
|
||||
for (int i = 0; i < DnsPacket.NUM_SECTIONS; i++) {
|
||||
assertEquals(0, packetFromTooMuchData.getRecordList(i).size());
|
||||
assertEquals(0, packetFromTooMuchData.getHeader().getRecordCount(i));
|
||||
}
|
||||
|
||||
// Assert throws if the byte buffer ended when expecting more records.
|
||||
final byte[] blobNotEnoughData = new byte[]{
|
||||
/* Header */
|
||||
(byte) 0xbe, (byte) 0xef, /* Transaction ID */
|
||||
(byte) 0x81, 0x00, /* Flags */
|
||||
0x00, 0x01, /* Questions */
|
||||
0x00, 0x02, /* Answer RRs */
|
||||
0x00, 0x00, /* Authority RRs */
|
||||
0x00, 0x00, /* Additional RRs */
|
||||
/* Queries */
|
||||
0x05, 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x07, 0x65, 0x78, 0x61,
|
||||
0x6D, 0x70, 0x6C, 0x65, 0x03, 0x63, 0x6F, 0x6D, 0x00, /* Name */
|
||||
0x00, 0x01, /* Type */
|
||||
0x00, 0x01, /* Class */
|
||||
/* Answers */
|
||||
0x05, 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x07, 0x65, 0x78, 0x61,
|
||||
0x6D, 0x70, 0x6C, 0x65, 0x03, 0x63, 0x6F, 0x6D, 0x00, /* Name */
|
||||
0x00, 0x01, /* Type */
|
||||
0x00, 0x01, /* Class */
|
||||
0x00, 0x00, 0x00, 0x00, /* TTL */
|
||||
0x00, 0x04, /* Data length */
|
||||
0x01, 0x02, 0x03, 0x04, /* Address */
|
||||
};
|
||||
assertThrows(DnsPacket.ParseException.class, () -> new TestDnsPacket(blobNotEnoughData));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEqualsAndHashCode() throws IOException {
|
||||
// Verify DnsHeader equals and hashCode.
|
||||
final DnsPacket.DnsHeader testHeader = new DnsPacket.DnsHeader(TEST_DNS_PACKET_ID,
|
||||
TEST_DNS_PACKET_FLAGS, 1 /* qcount */, 1 /* ancount */);
|
||||
final DnsPacket.DnsHeader emptyHeader = new DnsPacket.DnsHeader(TEST_DNS_PACKET_ID + 1,
|
||||
TEST_DNS_PACKET_FLAGS + 0x08, 0 /* qcount */, 0 /* ancount */);
|
||||
final DnsPacket.DnsHeader headerFromBytes =
|
||||
new DnsPacket.DnsHeader(ByteBuffer.wrap(testHeader.getBytes()));
|
||||
assertEquals(testHeader, headerFromBytes);
|
||||
assertEquals(testHeader.hashCode(), headerFromBytes.hashCode());
|
||||
assertNotEquals(testHeader, emptyHeader);
|
||||
assertNotEquals(testHeader.hashCode(), emptyHeader.hashCode());
|
||||
assertNotEquals(headerFromBytes, emptyHeader);
|
||||
assertNotEquals(headerFromBytes.hashCode(), emptyHeader.hashCode());
|
||||
|
||||
// Verify DnsRecord equals and hashCode.
|
||||
final DnsPacket.DnsRecord testQuestion = DnsPacket.DnsRecord.makeQuestion(
|
||||
"test.com", TYPE_AAAA, CLASS_IN);
|
||||
final DnsPacket.DnsRecord testAnswer = DnsPacket.DnsRecord.makeCNameRecord(
|
||||
DnsPacket.ANSECTION, "test.com", CLASS_IN, 9, "www.test.com");
|
||||
final DnsPacket.DnsRecord questionFromBytes = new DnsPacket.DnsRecord(DnsPacket.QDSECTION,
|
||||
ByteBuffer.wrap(testQuestion.getBytes()));
|
||||
assertEquals(testQuestion, questionFromBytes);
|
||||
assertEquals(testQuestion.hashCode(), questionFromBytes.hashCode());
|
||||
assertNotEquals(testQuestion, testAnswer);
|
||||
assertNotEquals(testQuestion.hashCode(), testAnswer.hashCode());
|
||||
assertNotEquals(questionFromBytes, testAnswer);
|
||||
assertNotEquals(questionFromBytes.hashCode(), testAnswer.hashCode());
|
||||
|
||||
// Verify DnsPacket equals and hashCode.
|
||||
final ArrayList<DnsPacket.DnsRecord> qlist = new ArrayList<>();
|
||||
final ArrayList<DnsPacket.DnsRecord> alist = new ArrayList<>();
|
||||
qlist.add(testQuestion);
|
||||
alist.add(testAnswer);
|
||||
final TestDnsPacket testPacket = new TestDnsPacket(testHeader, qlist, alist);
|
||||
final TestDnsPacket emptyPacket = new TestDnsPacket(
|
||||
emptyHeader, new ArrayList<>(), new ArrayList<>());
|
||||
final TestDnsPacket packetFromBytes = new TestDnsPacket(testPacket.getBytes());
|
||||
assertEquals(testPacket, packetFromBytes);
|
||||
assertEquals(testPacket.hashCode(), packetFromBytes.hashCode());
|
||||
assertNotEquals(testPacket, emptyPacket);
|
||||
assertNotEquals(testPacket.hashCode(), emptyPacket.hashCode());
|
||||
assertNotEquals(packetFromBytes, emptyPacket);
|
||||
assertNotEquals(packetFromBytes.hashCode(), emptyPacket.hashCode());
|
||||
|
||||
// Verify DnsPacket with empty list.
|
||||
final TestDnsPacket emptyPacketFromBytes = new TestDnsPacket(emptyPacket.getBytes());
|
||||
assertEquals(emptyPacket, emptyPacketFromBytes);
|
||||
assertEquals(emptyPacket.hashCode(), emptyPacketFromBytes.hashCode());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,20 +18,12 @@ package com.android.net.module.util;
|
||||
|
||||
import static com.android.net.module.util.DnsPacketUtils.DnsRecordParser;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
|
||||
import android.net.ParseException;
|
||||
import android.os.Build;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.testutils.DevSdkIgnoreRule;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@@ -40,8 +32,6 @@ import java.nio.ByteBuffer;
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class DnsPacketUtilsTest {
|
||||
@Rule
|
||||
public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule();
|
||||
|
||||
/**
|
||||
* Verifies that the compressed NAME field in the answer section of the DNS message is parsed
|
||||
@@ -126,31 +116,4 @@ public class DnsPacketUtilsTest {
|
||||
notNameCompressedBuf, /* depth= */ 0, /* isNameCompressionSupported= */ false);
|
||||
assertEquals(domainName, "www.google.com");
|
||||
}
|
||||
|
||||
// Skip test on R- devices since ParseException only available on S+ devices.
|
||||
@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
|
||||
@Test
|
||||
public void testDomainNameToLabels() throws Exception {
|
||||
assertArrayEquals(
|
||||
new byte[]{3, 'w', 'w', 'w', 6, 'g', 'o', 'o', 'g', 'l', 'e', 3, 'c', 'o', 'm', 0},
|
||||
DnsRecordParser.domainNameToLabels("www.google.com"));
|
||||
assertThrows(ParseException.class, () ->
|
||||
DnsRecordParser.domainNameToLabels("aaa."));
|
||||
assertThrows(ParseException.class, () ->
|
||||
DnsRecordParser.domainNameToLabels("aaa"));
|
||||
assertThrows(ParseException.class, () ->
|
||||
DnsRecordParser.domainNameToLabels("."));
|
||||
assertThrows(ParseException.class, () ->
|
||||
DnsRecordParser.domainNameToLabels(""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsHostName() {
|
||||
Assert.assertTrue(DnsRecordParser.isHostName("www.google.com"));
|
||||
Assert.assertFalse(DnsRecordParser.isHostName("com"));
|
||||
Assert.assertFalse(DnsRecordParser.isHostName("1.2.3.4"));
|
||||
Assert.assertFalse(DnsRecordParser.isHostName("1234::5678"));
|
||||
Assert.assertFalse(DnsRecordParser.isHostName(null));
|
||||
Assert.assertFalse(DnsRecordParser.isHostName(""));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user