DnsPinger.java revision b1ef292b3d0c2b4b4c77bb7b442df8e73d1fbb5e
1bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy/*
2bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy * Copyright (C) 2011 The Android Open Source Project
3bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy *
4bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy * Licensed under the Apache License, Version 2.0 (the "License");
5bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy * you may not use this file except in compliance with the License.
6bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy * You may obtain a copy of the License at
7bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy *
8bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy *      http://www.apache.org/licenses/LICENSE-2.0
9bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy *
10bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy * Unless required by applicable law or agreed to in writing, software
11bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy * distributed under the License is distributed on an "AS IS" BASIS,
12bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy * See the License for the specific language governing permissions and
14bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy * limitations under the License.
15bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy */
16bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
17bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levypackage com.android.server;
18bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
19bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levyimport android.content.ContentResolver;
20bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levyimport android.content.Context;
21bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levyimport android.net.ConnectivityManager;
22bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levyimport android.net.LinkProperties;
23bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levyimport android.os.SystemClock;
24bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levyimport android.util.Slog;
25bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
26bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levyimport java.net.DatagramPacket;
27bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levyimport java.net.DatagramSocket;
28bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levyimport java.net.InetAddress;
29bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levyimport java.net.SocketTimeoutException;
30bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levyimport java.util.Collection;
31bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levyimport java.util.Random;
32bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
33bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy/**
34bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy * Performs a simple DNS "ping" by sending a "server status" query packet to the
35bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy * DNS server. As long as the server replies, we consider it a success.
36bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy * <p>
37bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy * We do not use a simple hostname lookup because that could be cached and the
38bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy * API may not differentiate between a time out and a failure lookup (which we
39bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy * really care about).
40bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy * <p>
41b1ef292b3d0c2b4b4c77bb7b442df8e73d1fbb5eIsaac Levy * TODO : More general API.  Socket does not bind to specified connection type
42bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy * TODO : Choice of DNS query location - current looks up www.android.com
43bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy *
44bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy * @hide
45bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy */
46bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levypublic final class DnsPinger {
47bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy    private static final boolean V = true;
48bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
49bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy    /** Number of bytes for the query */
50bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy    private static final int DNS_QUERY_BASE_SIZE = 33;
51bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
52bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy    /** The DNS port */
53bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy    private static final int DNS_PORT = 53;
54bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
55bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy    /** Used to generate IDs */
56bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy    private static Random sRandom = new Random();
57bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
58bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy    private ConnectivityManager mConnectivityManager = null;
59bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy    private ContentResolver mContentResolver;
60bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy    private Context mContext;
61b1ef292b3d0c2b4b4c77bb7b442df8e73d1fbb5eIsaac Levy    private int mConnectionType;
62bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
63bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy    private String TAG;
64bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
65b1ef292b3d0c2b4b4c77bb7b442df8e73d1fbb5eIsaac Levy
66b1ef292b3d0c2b4b4c77bb7b442df8e73d1fbb5eIsaac Levy    /**
67b1ef292b3d0c2b4b4c77bb7b442df8e73d1fbb5eIsaac Levy     * @param connectionType The connection type from @link {@link ConnectivityManager}
68b1ef292b3d0c2b4b4c77bb7b442df8e73d1fbb5eIsaac Levy     */
69b1ef292b3d0c2b4b4c77bb7b442df8e73d1fbb5eIsaac Levy    public DnsPinger(String TAG, Context context, int connectionType) {
70bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy        mContext = context;
71bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy        mContentResolver = context.getContentResolver();
72b1ef292b3d0c2b4b4c77bb7b442df8e73d1fbb5eIsaac Levy        mConnectionType = connectionType;
73bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy        this.TAG = TAG;
74bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy    }
75bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
76bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy    /**
77b1ef292b3d0c2b4b4c77bb7b442df8e73d1fbb5eIsaac Levy     * @return The first DNS in the link properties of the specified connection type
78bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy     */
79bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy    public InetAddress getDns() {
80b1ef292b3d0c2b4b4c77bb7b442df8e73d1fbb5eIsaac Levy        LinkProperties linkProperties = getCurLinkProperties();
81bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy        if (linkProperties == null)
82bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy            return null;
83bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
84bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy        Collection<InetAddress> dnses = linkProperties.getDnses();
85bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy        if (dnses == null || dnses.size() == 0)
86bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy            return null;
87bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
88bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy        return dnses.iterator().next();
89bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy    }
90bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
91b1ef292b3d0c2b4b4c77bb7b442df8e73d1fbb5eIsaac Levy    private LinkProperties getCurLinkProperties() {
92b1ef292b3d0c2b4b4c77bb7b442df8e73d1fbb5eIsaac Levy        if (mConnectivityManager == null) {
93b1ef292b3d0c2b4b4c77bb7b442df8e73d1fbb5eIsaac Levy            mConnectivityManager = (ConnectivityManager) mContext.getSystemService(
94b1ef292b3d0c2b4b4c77bb7b442df8e73d1fbb5eIsaac Levy                    Context.CONNECTIVITY_SERVICE);
95b1ef292b3d0c2b4b4c77bb7b442df8e73d1fbb5eIsaac Levy        }
96b1ef292b3d0c2b4b4c77bb7b442df8e73d1fbb5eIsaac Levy        return mConnectivityManager.getLinkProperties(mConnectionType);
97b1ef292b3d0c2b4b4c77bb7b442df8e73d1fbb5eIsaac Levy    }
98b1ef292b3d0c2b4b4c77bb7b442df8e73d1fbb5eIsaac Levy
99bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy    /**
100bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy     * @return time to response. Negative value on error.
101bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy     */
102bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy    public long pingDns(InetAddress dnsAddress, int timeout) {
103bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy        DatagramSocket socket = null;
104bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy        try {
105bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy            socket = new DatagramSocket();
106bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
107bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy            // Set some socket properties
108bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy            socket.setSoTimeout(timeout);
109bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
110bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy            byte[] buf = new byte[DNS_QUERY_BASE_SIZE];
111bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy            fillQuery(buf);
112bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
113bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy            // Send the DNS query
114bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
115bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy            DatagramPacket packet = new DatagramPacket(buf,
116bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy                    buf.length, dnsAddress, DNS_PORT);
117bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy            long start = SystemClock.elapsedRealtime();
118bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy            socket.send(packet);
119bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
120bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy            // Wait for reply (blocks for the above timeout)
121bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy            DatagramPacket replyPacket = new DatagramPacket(buf, buf.length);
122bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy            socket.receive(replyPacket);
123bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
124bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy            // If a timeout occurred, an exception would have been thrown. We
125bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy            // got a reply!
126bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy            return SystemClock.elapsedRealtime() - start;
127bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
128bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy        } catch (SocketTimeoutException e) {
129bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy            // Squelch this exception.
130bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy            return -1;
131bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy        } catch (Exception e) {
132bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy            if (V) {
133bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy                Slog.v(TAG, "DnsPinger.pingDns got socket exception: ", e);
134bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy            }
135bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy            return -2;
136bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy        } finally {
137bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy            if (socket != null) {
138bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy                socket.close();
139bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy            }
140bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy        }
141bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
142bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy    }
143bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
144bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy    private static void fillQuery(byte[] buf) {
145bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
146bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy        /*
147bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy         * See RFC2929 (though the bit tables in there are misleading for us.
148bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy         * For example, the recursion desired bit is the 0th bit for us, but
149bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy         * looking there it would appear as the 7th bit of the byte
150bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy         */
151bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
152bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy        // Make sure it's all zeroed out
153bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy        for (int i = 0; i < buf.length; i++)
154bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy            buf[i] = 0;
155bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
156bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy        // Form a query for www.android.com
157bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
158bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy        // [0-1] bytes are an ID, generate random ID for this query
159bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy        buf[0] = (byte) sRandom.nextInt(256);
160bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy        buf[1] = (byte) sRandom.nextInt(256);
161bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
162bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy        // [2-3] bytes are for flags.
163bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy        buf[2] = 1; // Recursion desired
164bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
165bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy        // [4-5] bytes are for the query count
166bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy        buf[5] = 1; // One query
167bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
168bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy        // [6-7] [8-9] [10-11] are all counts of other fields we don't use
169bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
170bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy        // [12-15] for www
171bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy        writeString(buf, 12, "www");
172bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
173bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy        // [16-23] for android
174bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy        writeString(buf, 16, "android");
175bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
176bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy        // [24-27] for com
177bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy        writeString(buf, 24, "com");
178bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
179bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy        // [29-30] bytes are for QTYPE, set to 1
180bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy        buf[30] = 1;
181bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
182bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy        // [31-32] bytes are for QCLASS, set to 1
183bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy        buf[32] = 1;
184bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy    }
185bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
186bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy    private static void writeString(byte[] buf, int startPos, String string) {
187bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy        int pos = startPos;
188bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
189bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy        // Write the length first
190bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy        buf[pos++] = (byte) string.length();
191bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy        for (int i = 0; i < string.length(); i++) {
192bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy            buf[pos++] = (byte) string.charAt(i);
193bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy        }
194bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy    }
195bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy}
196