Merge "EthernetTetheringTest: testTetherUdpV4Dns"
This commit is contained in:
@@ -24,7 +24,9 @@ import static android.net.InetAddresses.parseNumericAddress;
|
||||
import static android.net.TetheringManager.CONNECTIVITY_SCOPE_GLOBAL;
|
||||
import static android.net.TetheringManager.CONNECTIVITY_SCOPE_LOCAL;
|
||||
import static android.net.TetheringManager.TETHERING_ETHERNET;
|
||||
import static android.net.TetheringTester.TestDnsPacket;
|
||||
import static android.net.TetheringTester.isExpectedIcmpv6Packet;
|
||||
import static android.net.TetheringTester.isExpectedUdpDnsPacket;
|
||||
import static android.net.TetheringTester.isExpectedUdpPacket;
|
||||
import static android.system.OsConstants.IPPROTO_IP;
|
||||
import static android.system.OsConstants.IPPROTO_IPV6;
|
||||
@@ -81,7 +83,9 @@ import com.android.net.module.util.bpf.Tether4Key;
|
||||
import com.android.net.module.util.bpf.Tether4Value;
|
||||
import com.android.net.module.util.bpf.TetherStatsKey;
|
||||
import com.android.net.module.util.bpf.TetherStatsValue;
|
||||
import com.android.net.module.util.structs.Ipv4Header;
|
||||
import com.android.net.module.util.structs.Ipv6Header;
|
||||
import com.android.net.module.util.structs.UdpHeader;
|
||||
import com.android.testutils.DevSdkIgnoreRule;
|
||||
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
|
||||
import com.android.testutils.DeviceInfoUtils;
|
||||
@@ -156,6 +160,8 @@ public class EthernetTetheringTest {
|
||||
private static final ByteBuffer TEST_REACHABILITY_PAYLOAD =
|
||||
ByteBuffer.wrap(new byte[] { (byte) 0x55, (byte) 0xaa });
|
||||
|
||||
private static final short DNS_PORT = 53;
|
||||
|
||||
private static final String DUMPSYS_TETHERING_RAWMAP_ARG = "bpfRawMap";
|
||||
private static final String DUMPSYS_RAWMAP_ARG_STATS = "--stats";
|
||||
private static final String DUMPSYS_RAWMAP_ARG_UPSTREAM4 = "--upstream4";
|
||||
@@ -165,6 +171,66 @@ public class EthernetTetheringTest {
|
||||
private static final int VERSION_TRAFFICCLASS_FLOWLABEL = 0x60000000;
|
||||
private static final short HOP_LIMIT = 0x40;
|
||||
|
||||
// TODO: use class DnsPacket to build DNS query and reply message once DnsPacket supports
|
||||
// building packet for given arguments.
|
||||
private static final ByteBuffer DNS_QUERY = ByteBuffer.wrap(new byte[] {
|
||||
// scapy.DNS(
|
||||
// id=0xbeef,
|
||||
// qr=0,
|
||||
// qd=scapy.DNSQR(qname="hello.example.com"))
|
||||
//
|
||||
/* Header */
|
||||
(byte) 0xbe, (byte) 0xef, /* Transaction ID: 0xbeef */
|
||||
(byte) 0x01, (byte) 0x00, /* Flags: rd */
|
||||
(byte) 0x00, (byte) 0x01, /* Questions: 1 */
|
||||
(byte) 0x00, (byte) 0x00, /* Answer RRs: 0 */
|
||||
(byte) 0x00, (byte) 0x00, /* Authority RRs: 0 */
|
||||
(byte) 0x00, (byte) 0x00, /* Additional RRs: 0 */
|
||||
/* Queries */
|
||||
(byte) 0x05, (byte) 0x68, (byte) 0x65, (byte) 0x6c,
|
||||
(byte) 0x6c, (byte) 0x6f, (byte) 0x07, (byte) 0x65,
|
||||
(byte) 0x78, (byte) 0x61, (byte) 0x6d, (byte) 0x70,
|
||||
(byte) 0x6c, (byte) 0x65, (byte) 0x03, (byte) 0x63,
|
||||
(byte) 0x6f, (byte) 0x6d, (byte) 0x00, /* Name: hello.example.com */
|
||||
(byte) 0x00, (byte) 0x01, /* Type: A */
|
||||
(byte) 0x00, (byte) 0x01 /* Class: IN */
|
||||
});
|
||||
|
||||
private static final byte[] DNS_REPLY = new byte[] {
|
||||
// scapy.DNS(
|
||||
// id=0,
|
||||
// qr=1,
|
||||
// qd=scapy.DNSQR(qname="hello.example.com"),
|
||||
// an=scapy.DNSRR(rrname="hello.example.com", rdata='1.2.3.4'))
|
||||
//
|
||||
/* Header */
|
||||
(byte) 0x00, (byte) 0x00, /* Transaction ID: 0x0, must be updated by dns query id */
|
||||
(byte) 0x81, (byte) 0x00, /* Flags: qr rd */
|
||||
(byte) 0x00, (byte) 0x01, /* Questions: 1 */
|
||||
(byte) 0x00, (byte) 0x01, /* Answer RRs: 1 */
|
||||
(byte) 0x00, (byte) 0x00, /* Authority RRs: 0 */
|
||||
(byte) 0x00, (byte) 0x00, /* Additional RRs: 0 */
|
||||
/* Queries */
|
||||
(byte) 0x05, (byte) 0x68, (byte) 0x65, (byte) 0x6c,
|
||||
(byte) 0x6c, (byte) 0x6f, (byte) 0x07, (byte) 0x65,
|
||||
(byte) 0x78, (byte) 0x61, (byte) 0x6d, (byte) 0x70,
|
||||
(byte) 0x6c, (byte) 0x65, (byte) 0x03, (byte) 0x63,
|
||||
(byte) 0x6f, (byte) 0x6d, (byte) 0x00, /* Name: hello.example.com */
|
||||
(byte) 0x00, (byte) 0x01, /* Type: A */
|
||||
(byte) 0x00, (byte) 0x01, /* Class: IN */
|
||||
/* Answers */
|
||||
(byte) 0x05, (byte) 0x68, (byte) 0x65, (byte) 0x6c,
|
||||
(byte) 0x6c, (byte) 0x6f, (byte) 0x07, (byte) 0x65,
|
||||
(byte) 0x78, (byte) 0x61, (byte) 0x6d, (byte) 0x70,
|
||||
(byte) 0x6c, (byte) 0x65, (byte) 0x03, (byte) 0x63,
|
||||
(byte) 0x6f, (byte) 0x6d, (byte) 0x00, /* Name: hello.example.com */
|
||||
(byte) 0x00, (byte) 0x01, /* Type: A */
|
||||
(byte) 0x00, (byte) 0x01, /* Class: IN */
|
||||
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, /* Time to live: 0 */
|
||||
(byte) 0x00, (byte) 0x04, /* Data length: 4 */
|
||||
(byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04 /* Address: 1.2.3.4 */
|
||||
};
|
||||
|
||||
private final Context mContext = InstrumentationRegistry.getContext();
|
||||
private final EthernetManager mEm = mContext.getSystemService(EthernetManager.class);
|
||||
private final TetheringManager mTm = mContext.getSystemService(TetheringManager.class);
|
||||
@@ -1371,6 +1437,94 @@ public class EthernetTetheringTest {
|
||||
runClatUdpTest();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private ByteBuffer buildDnsReplyMessageById(short id) {
|
||||
byte[] replyMessage = Arrays.copyOf(DNS_REPLY, DNS_REPLY.length);
|
||||
// Assign transaction id of reply message pattern with a given DNS transaction id.
|
||||
replyMessage[0] = (byte) ((id >> 8) & 0xff);
|
||||
replyMessage[1] = (byte) (id & 0xff);
|
||||
Log.d(TAG, "Built DNS reply: " + dumpHexString(replyMessage));
|
||||
|
||||
return ByteBuffer.wrap(replyMessage);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private void sendDownloadPacketDnsV4(@NonNull final Inet4Address srcIp,
|
||||
@NonNull final Inet4Address dstIp, short srcPort, short dstPort, short dnsId,
|
||||
@NonNull final TetheringTester tester) throws Exception {
|
||||
// DNS response transaction id must be copied from DNS query. Used by the requester
|
||||
// to match up replies to outstanding queries. See RFC 1035 section 4.1.1.
|
||||
final ByteBuffer dnsReplyMessage = buildDnsReplyMessageById(dnsId);
|
||||
final ByteBuffer testPacket = buildUdpPacket((InetAddress) srcIp,
|
||||
(InetAddress) dstIp, srcPort, dstPort, dnsReplyMessage);
|
||||
|
||||
tester.verifyDownload(testPacket, p -> {
|
||||
Log.d(TAG, "Packet in downstream: " + dumpHexString(p));
|
||||
return isExpectedUdpDnsPacket(p, true /* hasEther */, true /* isIpv4 */,
|
||||
dnsReplyMessage);
|
||||
});
|
||||
}
|
||||
|
||||
// Send IPv4 UDP DNS packet and return the forwarded DNS packet on upstream.
|
||||
@NonNull
|
||||
private byte[] sendUploadPacketDnsV4(@NonNull final MacAddress srcMac,
|
||||
@NonNull final MacAddress dstMac, @NonNull final Inet4Address srcIp,
|
||||
@NonNull final Inet4Address dstIp, short srcPort, short dstPort,
|
||||
@NonNull final TetheringTester tester) throws Exception {
|
||||
final ByteBuffer testPacket = buildUdpPacket(srcMac, dstMac, srcIp, dstIp,
|
||||
srcPort, dstPort, DNS_QUERY);
|
||||
|
||||
return tester.verifyUpload(testPacket, p -> {
|
||||
Log.d(TAG, "Packet in upstream: " + dumpHexString(p));
|
||||
return isExpectedUdpDnsPacket(p, false /* hasEther */, true /* isIpv4 */,
|
||||
DNS_QUERY);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTetherUdpV4Dns() throws Exception {
|
||||
final TetheringTester tester = initTetheringTester(toList(TEST_IP4_ADDR),
|
||||
toList(TEST_IP4_DNS));
|
||||
final TetheredDevice tethered = tester.createTetheredDevice(TEST_MAC, false /* hasIpv6 */);
|
||||
|
||||
// TODO: remove the connectivity verification for upstream connected notification race.
|
||||
// See the same reason in runUdp4Test().
|
||||
probeV4TetheringConnectivity(tester, tethered, false /* is4To6 */);
|
||||
|
||||
// [1] Send DNS query.
|
||||
// tethered device --> downstream --> dnsmasq forwarding --> upstream --> DNS server
|
||||
//
|
||||
// Need to extract DNS transaction id and source port from dnsmasq forwarded DNS query
|
||||
// packet. dnsmasq forwarding creats new query which means UDP source port and DNS
|
||||
// transaction id are changed from original sent DNS query. See forward_query() in
|
||||
// external/dnsmasq/src/forward.c. Note that #TetheringTester.isExpectedUdpDnsPacket
|
||||
// guarantees that |forwardedQueryPacket| is a valid DNS packet. So we can parse it as DNS
|
||||
// packet.
|
||||
final MacAddress srcMac = tethered.macAddr;
|
||||
final MacAddress dstMac = tethered.routerMacAddr;
|
||||
final Inet4Address clientIp = tethered.ipv4Addr;
|
||||
final Inet4Address gatewayIp = tethered.ipv4Gatway;
|
||||
final byte[] forwardedQueryPacket = sendUploadPacketDnsV4(srcMac, dstMac, clientIp,
|
||||
gatewayIp, LOCAL_PORT, DNS_PORT, tester);
|
||||
final ByteBuffer buf = ByteBuffer.wrap(forwardedQueryPacket);
|
||||
Struct.parse(Ipv4Header.class, buf);
|
||||
final UdpHeader udpHeader = Struct.parse(UdpHeader.class, buf);
|
||||
final TestDnsPacket dnsQuery = TestDnsPacket.getTestDnsPacket(buf);
|
||||
assertNotNull(dnsQuery);
|
||||
Log.d(TAG, "Forwarded UDP source port: " + udpHeader.srcPort + ", DNS query id: "
|
||||
+ dnsQuery.getHeader().id);
|
||||
|
||||
// [2] Send DNS reply.
|
||||
// DNS server --> upstream --> dnsmasq forwarding --> downstream --> tethered device
|
||||
//
|
||||
// DNS reply transaction id must be copied from DNS query. Used by the requester to match
|
||||
// up replies to outstanding queries. See RFC 1035 section 4.1.1.
|
||||
final Inet4Address remoteIp = (Inet4Address) TEST_IP4_DNS;
|
||||
final Inet4Address tetheringUpstreamIp = (Inet4Address) TEST_IP4_ADDR.getAddress();
|
||||
sendDownloadPacketDnsV4(remoteIp, tetheringUpstreamIp, DNS_PORT,
|
||||
(short) udpHeader.srcPort, (short) dnsQuery.getHeader().id, tester);
|
||||
}
|
||||
|
||||
private <T> List<T> toList(T... array) {
|
||||
return Arrays.asList(array);
|
||||
}
|
||||
|
||||
@@ -20,6 +20,11 @@ import static android.net.InetAddresses.parseNumericAddress;
|
||||
import static android.system.OsConstants.IPPROTO_ICMPV6;
|
||||
import static android.system.OsConstants.IPPROTO_UDP;
|
||||
|
||||
import static com.android.net.module.util.DnsPacket.ANSECTION;
|
||||
import static com.android.net.module.util.DnsPacket.ARSECTION;
|
||||
import static com.android.net.module.util.DnsPacket.NSSECTION;
|
||||
import static com.android.net.module.util.DnsPacket.QDSECTION;
|
||||
import static com.android.net.module.util.HexDump.dumpHexString;
|
||||
import static com.android.net.module.util.NetworkStackConstants.ARP_REPLY;
|
||||
import static com.android.net.module.util.NetworkStackConstants.ARP_REQUEST;
|
||||
import static com.android.net.module.util.NetworkStackConstants.ETHER_ADDR_LEN;
|
||||
@@ -41,12 +46,14 @@ import static org.junit.Assert.fail;
|
||||
import android.net.dhcp.DhcpAckPacket;
|
||||
import android.net.dhcp.DhcpOfferPacket;
|
||||
import android.net.dhcp.DhcpPacket;
|
||||
import android.text.TextUtils;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.net.module.util.DnsPacket;
|
||||
import com.android.net.module.util.Ipv6Utils;
|
||||
import com.android.net.module.util.Struct;
|
||||
import com.android.net.module.util.structs.EthernetHeader;
|
||||
@@ -124,12 +131,14 @@ public final class TetheringTester {
|
||||
public final MacAddress macAddr;
|
||||
public final MacAddress routerMacAddr;
|
||||
public final Inet4Address ipv4Addr;
|
||||
public final Inet4Address ipv4Gatway;
|
||||
public final Inet6Address ipv6Addr;
|
||||
|
||||
private TetheredDevice(MacAddress mac, boolean hasIpv6) throws Exception {
|
||||
macAddr = mac;
|
||||
DhcpResults dhcpResults = runDhcp(macAddr.toByteArray());
|
||||
ipv4Addr = (Inet4Address) dhcpResults.ipAddress.getAddress();
|
||||
ipv4Gatway = (Inet4Address) dhcpResults.gateway;
|
||||
routerMacAddr = getRouterMacAddressFromArp(ipv4Addr, macAddr,
|
||||
dhcpResults.serverAddress);
|
||||
ipv6Addr = hasIpv6 ? runSlaac(macAddr, routerMacAddr) : null;
|
||||
@@ -386,8 +395,8 @@ public final class TetheringTester {
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isExpectedUdpPacket(@NonNull final byte[] rawPacket, boolean hasEth,
|
||||
boolean isIpv4, @NonNull final ByteBuffer payload) {
|
||||
private static boolean isExpectedUdpPacket(@NonNull final byte[] rawPacket, boolean hasEth,
|
||||
boolean isIpv4, Predicate<ByteBuffer> payloadVerifier) {
|
||||
final ByteBuffer buf = ByteBuffer.wrap(rawPacket);
|
||||
try {
|
||||
if (hasEth && !hasExpectedEtherHeader(buf, isIpv4)) return false;
|
||||
@@ -395,15 +404,178 @@ public final class TetheringTester {
|
||||
if (!hasExpectedIpHeader(buf, isIpv4, IPPROTO_UDP)) return false;
|
||||
|
||||
if (Struct.parse(UdpHeader.class, buf) == null) return false;
|
||||
|
||||
if (!payloadVerifier.test(buf)) return false;
|
||||
} catch (Exception e) {
|
||||
// Parsing packet fail means it is not udp packet.
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (buf.remaining() != payload.limit()) return false;
|
||||
// Returns remaining bytes in the ByteBuffer in a new byte array of the right size. The
|
||||
// ByteBuffer will be empty upon return. Used to avoid lint warning.
|
||||
// See https://errorprone.info/bugpattern/ByteBufferBackingArray
|
||||
private static byte[] getRemaining(final ByteBuffer buf) {
|
||||
final byte[] bytes = new byte[buf.remaining()];
|
||||
buf.get(bytes);
|
||||
Log.d(TAG, "Get remaining bytes: " + dumpHexString(bytes));
|
||||
return bytes;
|
||||
}
|
||||
|
||||
return Arrays.equals(Arrays.copyOfRange(buf.array(), buf.position(), buf.limit()),
|
||||
payload.array());
|
||||
// |expectedPayload| is copied as read-only because the caller may reuse it.
|
||||
public static boolean isExpectedUdpPacket(@NonNull final byte[] rawPacket, boolean hasEth,
|
||||
boolean isIpv4, @NonNull final ByteBuffer expectedPayload) {
|
||||
return isExpectedUdpPacket(rawPacket, hasEth, isIpv4, p -> {
|
||||
if (p.remaining() != expectedPayload.limit()) return false;
|
||||
|
||||
return Arrays.equals(getRemaining(p), getRemaining(
|
||||
expectedPayload.asReadOnlyBuffer()));
|
||||
});
|
||||
}
|
||||
|
||||
// |expectedPayload| is copied as read-only because the caller may reuse it.
|
||||
// See hasExpectedDnsMessage.
|
||||
public static boolean isExpectedUdpDnsPacket(@NonNull final byte[] rawPacket, boolean hasEth,
|
||||
boolean isIpv4, @NonNull final ByteBuffer expectedPayload) {
|
||||
return isExpectedUdpPacket(rawPacket, hasEth, isIpv4, p -> {
|
||||
return hasExpectedDnsMessage(p, expectedPayload);
|
||||
});
|
||||
}
|
||||
|
||||
public static class TestDnsPacket extends DnsPacket {
|
||||
TestDnsPacket(byte[] data) throws DnsPacket.ParseException {
|
||||
super(data);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static TestDnsPacket getTestDnsPacket(final ByteBuffer buf) {
|
||||
try {
|
||||
// The ByteBuffer will be empty upon return.
|
||||
return new TestDnsPacket(getRemaining(buf));
|
||||
} catch (DnsPacket.ParseException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public DnsHeader getHeader() {
|
||||
return mHeader;
|
||||
}
|
||||
|
||||
public List<DnsRecord> getRecordList(int secType) {
|
||||
return mRecords[secType];
|
||||
}
|
||||
|
||||
public int getANCount() {
|
||||
return mHeader.getRecordCount(ANSECTION);
|
||||
}
|
||||
|
||||
public int getQDCount() {
|
||||
return mHeader.getRecordCount(QDSECTION);
|
||||
}
|
||||
|
||||
public int getNSCount() {
|
||||
return mHeader.getRecordCount(NSSECTION);
|
||||
}
|
||||
|
||||
public int getARCount() {
|
||||
return mHeader.getRecordCount(ARSECTION);
|
||||
}
|
||||
|
||||
private boolean isRecordsEquals(int type, @NonNull final TestDnsPacket other) {
|
||||
List<DnsRecord> records = getRecordList(type);
|
||||
List<DnsRecord> otherRecords = other.getRecordList(type);
|
||||
|
||||
if (records.size() != otherRecords.size()) return false;
|
||||
|
||||
// Expect that two compared resource records are in the same order. For current tests
|
||||
// in EthernetTetheringTest, it is okay because dnsmasq doesn't reorder the forwarded
|
||||
// resource records.
|
||||
// TODO: consider allowing that compare records out of order.
|
||||
for (int i = 0; i < records.size(); i++) {
|
||||
// TODO: use DnsRecord.equals once aosp/1387135 is merged.
|
||||
if (!TextUtils.equals(records.get(i).dName, otherRecords.get(i).dName)
|
||||
|| records.get(i).nsType != otherRecords.get(i).nsType
|
||||
|| records.get(i).nsClass != otherRecords.get(i).nsClass
|
||||
|| records.get(i).ttl != otherRecords.get(i).ttl
|
||||
|| !Arrays.equals(records.get(i).getRR(), otherRecords.get(i).getRR())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isQDRecordsEquals(@NonNull final TestDnsPacket other) {
|
||||
return isRecordsEquals(QDSECTION, other);
|
||||
}
|
||||
|
||||
public boolean isANRecordsEquals(@NonNull final TestDnsPacket other) {
|
||||
return isRecordsEquals(ANSECTION, other);
|
||||
}
|
||||
}
|
||||
|
||||
// The ByteBuffer |actual| will be empty upon return. The ByteBuffer |excepted| will be copied
|
||||
// as read-only because the caller may reuse it.
|
||||
private static boolean hasExpectedDnsMessage(@NonNull final ByteBuffer actual,
|
||||
@NonNull final ByteBuffer excepted) {
|
||||
// Forwarded DNS message is extracted from remaining received packet buffer which has
|
||||
// already parsed ethernet header, if any, IP header and UDP header.
|
||||
final TestDnsPacket forwardedDns = TestDnsPacket.getTestDnsPacket(actual);
|
||||
if (forwardedDns == null) return false;
|
||||
|
||||
// Original DNS message is the payload of the sending test UDP packet. It is used to check
|
||||
// that the forwarded DNS query and reply have corresponding contents.
|
||||
final TestDnsPacket originalDns = TestDnsPacket.getTestDnsPacket(
|
||||
excepted.asReadOnlyBuffer());
|
||||
assertNotNull(originalDns);
|
||||
|
||||
// Compare original DNS message which is sent to dnsmasq and forwarded DNS message which
|
||||
// is forwarded by dnsmasq. The original message and forwarded message may be not identical
|
||||
// because dnsmasq may change the header flags or even recreate the DNS query message and
|
||||
// so on. We only simple check on forwarded packet and monitor if test will be broken by
|
||||
// vendor dnsmasq customization. See forward_query() in external/dnsmasq/src/forward.c.
|
||||
//
|
||||
// DNS message format. See rfc1035 section 4.1.
|
||||
// +---------------------+
|
||||
// | Header |
|
||||
// +---------------------+
|
||||
// | Question | the question for the name server
|
||||
// +---------------------+
|
||||
// | Answer | RRs answering the question
|
||||
// +---------------------+
|
||||
// | Authority | RRs pointing toward an authority
|
||||
// +---------------------+
|
||||
// | Additional | RRs holding additional information
|
||||
// +---------------------+
|
||||
|
||||
// [1] Header section. See rfc1035 section 4.1.1.
|
||||
// Verify QR flag bit, QDCOUNT, ANCOUNT, NSCOUNT, ARCOUNT.
|
||||
if (originalDns.getHeader().isResponse() != forwardedDns.getHeader().isResponse()) {
|
||||
return false;
|
||||
}
|
||||
if (originalDns.getQDCount() != forwardedDns.getQDCount()) return false;
|
||||
if (originalDns.getANCount() != forwardedDns.getANCount()) return false;
|
||||
if (originalDns.getNSCount() != forwardedDns.getNSCount()) return false;
|
||||
if (originalDns.getARCount() != forwardedDns.getARCount()) return false;
|
||||
|
||||
// [2] Question section. See rfc1035 section 4.1.2.
|
||||
// Question section has at least one entry either DNS query or DNS reply.
|
||||
if (forwardedDns.getRecordList(QDSECTION).isEmpty()) return false;
|
||||
// Expect that original and forwarded message have the same question records (usually 1).
|
||||
if (!originalDns.isQDRecordsEquals(forwardedDns)) return false;
|
||||
|
||||
// [3] Answer section. See rfc1035 section 4.1.3.
|
||||
if (forwardedDns.getHeader().isResponse()) {
|
||||
// DNS reply has at least have one answer in our tests.
|
||||
// See EthernetTetheringTest#testTetherUdpV4Dns.
|
||||
if (forwardedDns.getRecordList(ANSECTION).isEmpty()) return false;
|
||||
// Expect that original and forwarded message have the same answer records.
|
||||
if (!originalDns.isANRecordsEquals(forwardedDns)) return false;
|
||||
}
|
||||
|
||||
// Ignore checking {Authority, Additional} sections because they are not tested
|
||||
// in EthernetTetheringTest.
|
||||
return true;
|
||||
}
|
||||
|
||||
private void sendUploadPacket(ByteBuffer packet) throws Exception {
|
||||
|
||||
Reference in New Issue
Block a user