NetworkDiagnostics.java revision 379747a4040118130ca35947aef6942b2cd1cd67
1379747a4040118130ca35947aef6942b2cd1cd67Erik Kline/*
2379747a4040118130ca35947aef6942b2cd1cd67Erik Kline * Copyright (C) 2015 The Android Open Source Project
3379747a4040118130ca35947aef6942b2cd1cd67Erik Kline *
4379747a4040118130ca35947aef6942b2cd1cd67Erik Kline * Licensed under the Apache License, Version 2.0 (the "License");
5379747a4040118130ca35947aef6942b2cd1cd67Erik Kline * you may not use this file except in compliance with the License.
6379747a4040118130ca35947aef6942b2cd1cd67Erik Kline * You may obtain a copy of the License at
7379747a4040118130ca35947aef6942b2cd1cd67Erik Kline *
8379747a4040118130ca35947aef6942b2cd1cd67Erik Kline *      http://www.apache.org/licenses/LICENSE-2.0
9379747a4040118130ca35947aef6942b2cd1cd67Erik Kline *
10379747a4040118130ca35947aef6942b2cd1cd67Erik Kline * Unless required by applicable law or agreed to in writing, software
11379747a4040118130ca35947aef6942b2cd1cd67Erik Kline * distributed under the License is distributed on an "AS IS" BASIS,
12379747a4040118130ca35947aef6942b2cd1cd67Erik Kline * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13379747a4040118130ca35947aef6942b2cd1cd67Erik Kline * See the License for the specific language governing permissions and
14379747a4040118130ca35947aef6942b2cd1cd67Erik Kline * limitations under the License.
15379747a4040118130ca35947aef6942b2cd1cd67Erik Kline */
16379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
17379747a4040118130ca35947aef6942b2cd1cd67Erik Klinepackage com.android.server.connectivity;
18379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
19379747a4040118130ca35947aef6942b2cd1cd67Erik Klineimport static android.system.OsConstants.*;
20379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
21379747a4040118130ca35947aef6942b2cd1cd67Erik Klineimport android.net.LinkProperties;
22379747a4040118130ca35947aef6942b2cd1cd67Erik Klineimport android.net.Network;
23379747a4040118130ca35947aef6942b2cd1cd67Erik Klineimport android.net.RouteInfo;
24379747a4040118130ca35947aef6942b2cd1cd67Erik Klineimport android.os.SystemClock;
25379747a4040118130ca35947aef6942b2cd1cd67Erik Klineimport android.system.ErrnoException;
26379747a4040118130ca35947aef6942b2cd1cd67Erik Klineimport android.system.Os;
27379747a4040118130ca35947aef6942b2cd1cd67Erik Klineimport android.system.StructTimeval;
28379747a4040118130ca35947aef6942b2cd1cd67Erik Klineimport android.text.TextUtils;
29379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
30379747a4040118130ca35947aef6942b2cd1cd67Erik Klineimport com.android.internal.util.IndentingPrintWriter;
31379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
32379747a4040118130ca35947aef6942b2cd1cd67Erik Klineimport java.io.Closeable;
33379747a4040118130ca35947aef6942b2cd1cd67Erik Klineimport java.io.FileDescriptor;
34379747a4040118130ca35947aef6942b2cd1cd67Erik Klineimport java.io.InterruptedIOException;
35379747a4040118130ca35947aef6942b2cd1cd67Erik Klineimport java.io.IOException;
36379747a4040118130ca35947aef6942b2cd1cd67Erik Klineimport java.net.Inet4Address;
37379747a4040118130ca35947aef6942b2cd1cd67Erik Klineimport java.net.Inet6Address;
38379747a4040118130ca35947aef6942b2cd1cd67Erik Klineimport java.net.InetAddress;
39379747a4040118130ca35947aef6942b2cd1cd67Erik Klineimport java.net.InetSocketAddress;
40379747a4040118130ca35947aef6942b2cd1cd67Erik Klineimport java.net.NetworkInterface;
41379747a4040118130ca35947aef6942b2cd1cd67Erik Klineimport java.net.SocketAddress;
42379747a4040118130ca35947aef6942b2cd1cd67Erik Klineimport java.net.SocketException;
43379747a4040118130ca35947aef6942b2cd1cd67Erik Klineimport java.net.UnknownHostException;
44379747a4040118130ca35947aef6942b2cd1cd67Erik Klineimport java.nio.ByteBuffer;
45379747a4040118130ca35947aef6942b2cd1cd67Erik Klineimport java.nio.charset.StandardCharsets;
46379747a4040118130ca35947aef6942b2cd1cd67Erik Klineimport java.util.concurrent.CountDownLatch;
47379747a4040118130ca35947aef6942b2cd1cd67Erik Klineimport java.util.concurrent.TimeUnit;
48379747a4040118130ca35947aef6942b2cd1cd67Erik Klineimport java.util.Arrays;
49379747a4040118130ca35947aef6942b2cd1cd67Erik Klineimport java.util.HashMap;
50379747a4040118130ca35947aef6942b2cd1cd67Erik Klineimport java.util.Map;
51379747a4040118130ca35947aef6942b2cd1cd67Erik Klineimport java.util.Random;
52379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
53379747a4040118130ca35947aef6942b2cd1cd67Erik Klineimport libcore.io.IoUtils;
54379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
55379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
56379747a4040118130ca35947aef6942b2cd1cd67Erik Kline/**
57379747a4040118130ca35947aef6942b2cd1cd67Erik Kline * NetworkDiagnostics
58379747a4040118130ca35947aef6942b2cd1cd67Erik Kline *
59379747a4040118130ca35947aef6942b2cd1cd67Erik Kline * A simple class to diagnose network connectivity fundamentals.  Current
60379747a4040118130ca35947aef6942b2cd1cd67Erik Kline * checks performed are:
61379747a4040118130ca35947aef6942b2cd1cd67Erik Kline *     - ICMPv4/v6 echo requests for all routers
62379747a4040118130ca35947aef6942b2cd1cd67Erik Kline *     - ICMPv4/v6 echo requests for all DNS servers
63379747a4040118130ca35947aef6942b2cd1cd67Erik Kline *     - DNS UDP queries to all DNS servers
64379747a4040118130ca35947aef6942b2cd1cd67Erik Kline *
65379747a4040118130ca35947aef6942b2cd1cd67Erik Kline * Currently unimplemented checks include:
66379747a4040118130ca35947aef6942b2cd1cd67Erik Kline *     - report ARP/ND data about on-link neighbors
67379747a4040118130ca35947aef6942b2cd1cd67Erik Kline *     - DNS TCP queries to all DNS servers
68379747a4040118130ca35947aef6942b2cd1cd67Erik Kline *     - HTTP DIRECT and PROXY checks
69379747a4040118130ca35947aef6942b2cd1cd67Erik Kline *     - port 443 blocking/TLS intercept checks
70379747a4040118130ca35947aef6942b2cd1cd67Erik Kline *     - QUIC reachability checks
71379747a4040118130ca35947aef6942b2cd1cd67Erik Kline *     - MTU checks
72379747a4040118130ca35947aef6942b2cd1cd67Erik Kline *
73379747a4040118130ca35947aef6942b2cd1cd67Erik Kline * The supplied timeout bounds the entire diagnostic process.  Each specific
74379747a4040118130ca35947aef6942b2cd1cd67Erik Kline * check class must implement this upper bound on measurements in whichever
75379747a4040118130ca35947aef6942b2cd1cd67Erik Kline * manner is most appropriate and effective.
76379747a4040118130ca35947aef6942b2cd1cd67Erik Kline *
77379747a4040118130ca35947aef6942b2cd1cd67Erik Kline * @hide
78379747a4040118130ca35947aef6942b2cd1cd67Erik Kline */
79379747a4040118130ca35947aef6942b2cd1cd67Erik Klinepublic class NetworkDiagnostics {
80379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    private static final String TAG = "NetworkDiagnostics";
81379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
82379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    // For brevity elsewhere.
83379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    private static final long now() {
84379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        return SystemClock.elapsedRealtime();
85379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    }
86379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
87379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    // Values from RFC 1035 section 4.1.1, names from <arpa/nameser.h>.
88379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    // Should be a member of DnsUdpCheck, but "compiler says no".
89379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    public static enum DnsResponseCode { NOERROR, FORMERR, SERVFAIL, NXDOMAIN, NOTIMP, REFUSED };
90379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
91379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    private final Network mNetwork;
92379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    private final LinkProperties mLinkProperties;
93379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    private final Integer mInterfaceIndex;
94379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
95379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    private final long mTimeoutMs;
96379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    private final long mStartTime;
97379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    private final long mDeadlineTime;
98379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
99379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    // A counter, initialized to the total number of measurements,
100379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    // so callers can wait for completion.
101379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    private final CountDownLatch mCountDownLatch;
102379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
103379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    private class Measurement {
104379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        private static final String SUCCEEDED = "SUCCEEDED";
105379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        private static final String FAILED = "FAILED";
106379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
107379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        // TODO: Refactor to make these private for better encapsulation.
108379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        public String description = "";
109379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        public long startTime;
110379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        public long finishTime;
111379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        public String result = "";
112379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        public Thread thread;
113379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
114379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        public void recordSuccess(String msg) {
115379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            maybeFixupTimes();
116379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            if (mCountDownLatch != null) {
117379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                mCountDownLatch.countDown();
118379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            }
119379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            result = SUCCEEDED + ": " + msg;
120379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        }
121379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
122379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        public void recordFailure(String msg) {
123379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            maybeFixupTimes();
124379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            if (mCountDownLatch != null) {
125379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                mCountDownLatch.countDown();
126379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            }
127379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            result = FAILED + ": " + msg;
128379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        }
129379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
130379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        private void maybeFixupTimes() {
131379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            // Allows the caller to just set success/failure and not worry
132379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            // about also setting the correct finishing time.
133379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            if (finishTime == 0) { finishTime = now(); }
134379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
135379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            // In cases where, for example, a failure has occurred before the
136379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            // measurement even began, fixup the start time to reflect as much.
137379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            if (startTime == 0) { startTime = finishTime; }
138379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        }
139379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
140379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        @Override
141379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        public String toString() {
142379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            return description + ": " + result + " (" + (finishTime - startTime) + "ms)";
143379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        }
144379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    }
145379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
146379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    private final Map<InetAddress, Measurement> mIcmpChecks = new HashMap<>();
147379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    private final Map<InetAddress, Measurement> mDnsUdpChecks = new HashMap<>();
148379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    private final String mDescription;
149379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
150379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
151379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    public NetworkDiagnostics(Network network, LinkProperties lp, long timeoutMs) {
152379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        mNetwork = network;
153379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        mLinkProperties = lp;
154379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        mInterfaceIndex = getInterfaceIndex(mLinkProperties.getInterfaceName());
155379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        mTimeoutMs = timeoutMs;
156379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        mStartTime = now();
157379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        mDeadlineTime = mStartTime + mTimeoutMs;
158379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
159379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        for (RouteInfo route : mLinkProperties.getRoutes()) {
160379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            if (route.hasGateway()) {
161379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                prepareIcmpMeasurement(route.getGateway());
162379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            }
163379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        }
164379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        for (InetAddress nameserver : mLinkProperties.getDnsServers()) {
165379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                prepareIcmpMeasurement(nameserver);
166379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                prepareDnsMeasurement(nameserver);
167379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        }
168379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
169379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        mCountDownLatch = new CountDownLatch(totalMeasurementCount());
170379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
171379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        startMeasurements();
172379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
173379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        mDescription = "ifaces{" + TextUtils.join(",", mLinkProperties.getAllInterfaceNames()) + "}"
174379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                + " index{" + mInterfaceIndex + "}"
175379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                + " network{" + mNetwork + "}"
176379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                + " nethandle{" + mNetwork.getNetworkHandle() + "}";
177379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    }
178379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
179379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    private static Integer getInterfaceIndex(String ifname) {
180379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        try {
181379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            NetworkInterface ni = NetworkInterface.getByName(ifname);
182379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            return ni.getIndex();
183379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        } catch (NullPointerException | SocketException e) {
184379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            return null;
185379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        }
186379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    }
187379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
188379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    private void prepareIcmpMeasurement(InetAddress target) {
189379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        if (!mIcmpChecks.containsKey(target)) {
190379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            Measurement measurement = new Measurement();
191379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            measurement.thread = new Thread(new IcmpCheck(target, measurement));
192379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            mIcmpChecks.put(target, measurement);
193379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        }
194379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    }
195379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
196379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    private void prepareDnsMeasurement(InetAddress target) {
197379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        if (!mDnsUdpChecks.containsKey(target)) {
198379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            Measurement measurement = new Measurement();
199379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            measurement.thread = new Thread(new DnsUdpCheck(target, measurement));
200379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            mDnsUdpChecks.put(target, measurement);
201379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        }
202379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    }
203379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
204379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    private int totalMeasurementCount() {
205379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        return mIcmpChecks.size() + mDnsUdpChecks.size();
206379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    }
207379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
208379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    private void startMeasurements() {
209379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        for (Measurement measurement : mIcmpChecks.values()) {
210379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            measurement.thread.start();
211379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        }
212379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        for (Measurement measurement : mDnsUdpChecks.values()) {
213379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            measurement.thread.start();
214379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        }
215379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    }
216379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
217379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    public void waitForMeasurements() {
218379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        try {
219379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            mCountDownLatch.await(mDeadlineTime - now(), TimeUnit.MILLISECONDS);
220379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        } catch (InterruptedException ignored) {}
221379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    }
222379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
223379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    public void dump(IndentingPrintWriter pw) {
224379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        pw.println(TAG + ":" + mDescription);
225379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        final long unfinished = mCountDownLatch.getCount();
226379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        if (unfinished > 0) {
227379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            // This can't happen unless a caller forgets to call waitForMeasurements()
228379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            // or a measurement isn't implemented to correctly honor the timeout.
229379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            pw.println("WARNING: countdown wait incomplete: "
230379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                    + unfinished + " unfinished measurements");
231379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        }
232379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
233379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        pw.increaseIndent();
234379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        for (Map.Entry<InetAddress, Measurement> entry : mIcmpChecks.entrySet()) {
235379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            if (entry.getKey() instanceof Inet4Address) {
236379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                pw.println(entry.getValue().toString());
237379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            }
238379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        }
239379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        for (Map.Entry<InetAddress, Measurement> entry : mIcmpChecks.entrySet()) {
240379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            if (entry.getKey() instanceof Inet6Address) {
241379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                pw.println(entry.getValue().toString());
242379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            }
243379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        }
244379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        for (Map.Entry<InetAddress, Measurement> entry : mDnsUdpChecks.entrySet()) {
245379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            if (entry.getKey() instanceof Inet4Address) {
246379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                pw.println(entry.getValue().toString());
247379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            }
248379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        }
249379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        for (Map.Entry<InetAddress, Measurement> entry : mDnsUdpChecks.entrySet()) {
250379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            if (entry.getKey() instanceof Inet6Address) {
251379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                pw.println(entry.getValue().toString());
252379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            }
253379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        }
254379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        pw.decreaseIndent();
255379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    }
256379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
257379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
258379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    private class SimpleSocketCheck implements Closeable {
259379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        protected final InetAddress mTarget;
260379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        protected final int mAddressFamily;
261379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        protected final Measurement mMeasurement;
262379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        protected FileDescriptor mFileDescriptor;
263379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        protected SocketAddress mSocketAddress;
264379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
265379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        protected SimpleSocketCheck(InetAddress target, Measurement measurement) {
266379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            mMeasurement = measurement;
267379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
268379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            if (target instanceof Inet6Address) {
269379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                Inet6Address targetWithScopeId = null;
270379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                if (target.isLinkLocalAddress() && mInterfaceIndex != null) {
271379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                    try {
272379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                        targetWithScopeId = Inet6Address.getByAddress(
273379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                                null, target.getAddress(), mInterfaceIndex);
274379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                    } catch (UnknownHostException e) {
275379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                        mMeasurement.recordFailure(e.toString());
276379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                    }
277379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                }
278379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                mTarget = (targetWithScopeId != null) ? targetWithScopeId : target;
279379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                mAddressFamily = AF_INET6;
280379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            } else {
281379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                mTarget = target;
282379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                mAddressFamily = AF_INET;
283379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            }
284379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        }
285379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
286379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        protected void setupSocket(
287379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                int sockType, int protocol, long writeTimeout, long readTimeout, int dstPort)
288379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                throws ErrnoException, IOException {
289379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            mFileDescriptor = Os.socket(mAddressFamily, sockType, protocol);
290379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            // Setting SNDTIMEO is purely for defensive purposes.
291379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            Os.setsockoptTimeval(mFileDescriptor,
292379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                    SOL_SOCKET, SO_SNDTIMEO, StructTimeval.fromMillis(writeTimeout));
293379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            Os.setsockoptTimeval(mFileDescriptor,
294379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                    SOL_SOCKET, SO_RCVTIMEO, StructTimeval.fromMillis(readTimeout));
295379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            // TODO: Use IP_RECVERR/IPV6_RECVERR, pending OsContants availability.
296379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            mNetwork.bindSocket(mFileDescriptor);
297379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            Os.connect(mFileDescriptor, mTarget, dstPort);
298379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            mSocketAddress = Os.getsockname(mFileDescriptor);
299379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        }
300379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
301379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        protected String getSocketAddressString() {
302379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            // The default toString() implementation is not the prettiest.
303379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            InetSocketAddress inetSockAddr = (InetSocketAddress) mSocketAddress;
304379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            InetAddress localAddr = inetSockAddr.getAddress();
305379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            return String.format(
306379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                    (localAddr instanceof Inet6Address ? "[%s]:%d" : "%s:%d"),
307379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                    localAddr.getHostAddress(), inetSockAddr.getPort());
308379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        }
309379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
310379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        @Override
311379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        public void close() {
312379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            IoUtils.closeQuietly(mFileDescriptor);
313379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        }
314379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    }
315379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
316379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
317379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    private class IcmpCheck extends SimpleSocketCheck implements Runnable {
318379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        private static final int TIMEOUT_SEND = 100;
319379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        private static final int TIMEOUT_RECV = 300;
320379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        private static final int ICMPV4_ECHO_REQUEST = 8;
321379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        private static final int ICMPV6_ECHO_REQUEST = 128;
322379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        private static final int PACKET_BUFSIZE = 512;
323379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        private final int mProtocol;
324379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        private final int mIcmpType;
325379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
326379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        public IcmpCheck(InetAddress target, Measurement measurement) {
327379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            super(target, measurement);
328379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
329379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            if (mAddressFamily == AF_INET6) {
330379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                mProtocol = IPPROTO_ICMPV6;
331379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                mIcmpType = ICMPV6_ECHO_REQUEST;
332379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                mMeasurement.description = "ICMPv6";
333379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            } else {
334379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                mProtocol = IPPROTO_ICMP;
335379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                mIcmpType = ICMPV4_ECHO_REQUEST;
336379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                mMeasurement.description = "ICMPv4";
337379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            }
338379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
339379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            mMeasurement.description += " dst{" + mTarget.getHostAddress() + "}";
340379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        }
341379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
342379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        @Override
343379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        public void run() {
344379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            // Check if this measurement has already failed during setup.
345379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            if (mMeasurement.finishTime > 0) {
346379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                // If the measurement failed during construction it didn't
347379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                // decrement the countdown latch; do so here.
348379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                mCountDownLatch.countDown();
349379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                return;
350379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            }
351379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
352379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            try {
353379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                setupSocket(SOCK_DGRAM, mProtocol, TIMEOUT_SEND, TIMEOUT_RECV, 0);
354379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            } catch (ErrnoException | IOException e) {
355379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                mMeasurement.recordFailure(e.toString());
356379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                return;
357379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            }
358379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            mMeasurement.description += " src{" + getSocketAddressString() + "}";
359379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
360379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            // Build a trivial ICMP packet.
361379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            final byte[] icmpPacket = {
362379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                    (byte) mIcmpType, 0, 0, 0, 0, 0, 0, 0  // ICMP header
363379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            };
364379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
365379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            int count = 0;
366379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            mMeasurement.startTime = now();
367379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            while (now() < mDeadlineTime - (TIMEOUT_SEND + TIMEOUT_RECV)) {
368379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                count++;
369379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                icmpPacket[icmpPacket.length - 1] = (byte) count;
370379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                try {
371379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                    Os.write(mFileDescriptor, icmpPacket, 0, icmpPacket.length);
372379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                } catch (ErrnoException | InterruptedIOException e) {
373379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                    mMeasurement.recordFailure(e.toString());
374379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                    break;
375379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                }
376379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
377379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                try {
378379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                    ByteBuffer reply = ByteBuffer.allocate(PACKET_BUFSIZE);
379379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                    Os.read(mFileDescriptor, reply);
380379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                    // TODO: send a few pings back to back to guesstimate packet loss.
381379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                    mMeasurement.recordSuccess("1/" + count);
382379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                    break;
383379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                } catch (ErrnoException | InterruptedIOException e) {
384379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                    continue;
385379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                }
386379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            }
387379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            if (mMeasurement.finishTime == 0) {
388379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                mMeasurement.recordFailure("0/" + count);
389379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            }
390379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
391379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            close();
392379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        }
393379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    }
394379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
395379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
396379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    private class DnsUdpCheck extends SimpleSocketCheck implements Runnable {
397379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        private static final int TIMEOUT_SEND = 100;
398379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        private static final int TIMEOUT_RECV = 500;
399379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        private static final int DNS_SERVER_PORT = 53;
400379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        private static final int RR_TYPE_A = 1;
401379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        private static final int RR_TYPE_AAAA = 28;
402379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        private static final int PACKET_BUFSIZE = 512;
403379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
404379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        private final Random mRandom = new Random();
405379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
406379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        // Should be static, but the compiler mocks our puny, human attempts at reason.
407379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        private String responseCodeStr(int rcode) {
408379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            try {
409379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                return DnsResponseCode.values()[rcode].toString();
410379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            } catch (IndexOutOfBoundsException e) {
411379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                return String.valueOf(rcode);
412379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            }
413379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        }
414379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
415379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        private final int mQueryType;
416379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
417379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        public DnsUdpCheck(InetAddress target, Measurement measurement) {
418379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            super(target, measurement);
419379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
420379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            // TODO: Ideally, query the target for both types regardless of address family.
421379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            if (mAddressFamily == AF_INET6) {
422379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                mQueryType = RR_TYPE_AAAA;
423379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            } else {
424379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                mQueryType = RR_TYPE_A;
425379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            }
426379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
427379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            mMeasurement.description = "DNS UDP dst{" + mTarget.getHostAddress() + "}";
428379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        }
429379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
430379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        @Override
431379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        public void run() {
432379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            // Check if this measurement has already failed during setup.
433379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            if (mMeasurement.finishTime > 0) {
434379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                // If the measurement failed during construction it didn't
435379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                // decrement the countdown latch; do so here.
436379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                mCountDownLatch.countDown();
437379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                return;
438379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            }
439379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
440379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            try {
441379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                setupSocket(SOCK_DGRAM, IPPROTO_UDP, TIMEOUT_SEND, TIMEOUT_RECV, DNS_SERVER_PORT);
442379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            } catch (ErrnoException | IOException e) {
443379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                mMeasurement.recordFailure(e.toString());
444379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                return;
445379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            }
446379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            mMeasurement.description += " src{" + getSocketAddressString() + "}";
447379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
448379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            // This needs to be fixed length so it can be dropped into the pre-canned packet.
449379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            final String sixRandomDigits =
450379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                    Integer.valueOf(mRandom.nextInt(900000) + 100000).toString();
451379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            mMeasurement.description += " qtype{" + mQueryType + "}"
452379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                    + " qname{" + sixRandomDigits + "-android-ds.metric.gstatic.com}";
453379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
454379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            // Build a trivial DNS packet.
455379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            final byte[] dnsPacket = getDnsQueryPacket(sixRandomDigits);
456379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
457379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            int count = 0;
458379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            mMeasurement.startTime = now();
459379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            while (now() < mDeadlineTime - (TIMEOUT_RECV + TIMEOUT_RECV)) {
460379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                count++;
461379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                try {
462379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                    Os.write(mFileDescriptor, dnsPacket, 0, dnsPacket.length);
463379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                } catch (ErrnoException | InterruptedIOException e) {
464379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                    mMeasurement.recordFailure(e.toString());
465379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                    break;
466379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                }
467379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
468379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                try {
469379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                    ByteBuffer reply = ByteBuffer.allocate(PACKET_BUFSIZE);
470379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                    Os.read(mFileDescriptor, reply);
471379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                    // TODO: more correct and detailed evaluation of the response,
472379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                    // possibly adding the returned IP address(es) to the output.
473379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                    final String rcodeStr = (reply.limit() > 3)
474379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                            ? " " + responseCodeStr((int) (reply.get(3)) & 0x0f)
475379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                            : "";
476379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                    mMeasurement.recordSuccess("1/" + count + rcodeStr);
477379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                    break;
478379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                } catch (ErrnoException | InterruptedIOException e) {
479379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                    continue;
480379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                }
481379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            }
482379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            if (mMeasurement.finishTime == 0) {
483379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                mMeasurement.recordFailure("0/" + count);
484379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            }
485379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
486379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            close();
487379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        }
488379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
489379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        private byte[] getDnsQueryPacket(String sixRandomDigits) {
490379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            byte[] rnd = sixRandomDigits.getBytes(StandardCharsets.US_ASCII);
491379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            return new byte[] {
492379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                (byte) mRandom.nextInt(), (byte) mRandom.nextInt(),  // [0-1]   query ID
493379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                1, 0,  // [2-3]   flags; byte[2] = 1 for recursion desired (RD).
494379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                0, 1,  // [4-5]   QDCOUNT (number of queries)
495379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                0, 0,  // [6-7]   ANCOUNT (number of answers)
496379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                0, 0,  // [8-9]   NSCOUNT (number of name server records)
497379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                0, 0,  // [10-11] ARCOUNT (number of additional records)
498379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                17, rnd[0], rnd[1], rnd[2], rnd[3], rnd[4], rnd[5],
499379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                        '-', 'a', 'n', 'd', 'r', 'o', 'i', 'd', '-', 'd', 's',
500379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                6, 'm', 'e', 't', 'r', 'i', 'c',
501379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                7, 'g', 's', 't', 'a', 't', 'i', 'c',
502379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                3, 'c', 'o', 'm',
503379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                0,  // null terminator of FQDN (root TLD)
504379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                0, (byte) mQueryType,  // QTYPE
505379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                0, 1  // QCLASS, set to 1 = IN (Internet)
506379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            };
507379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        }
508379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    }
509379747a4040118130ca35947aef6942b2cd1cd67Erik Kline}
510