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