Home | History | Annotate | Download | only in net
      1 /*
      2  * Copyright (C) 2015 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.net;
     18 
     19 import android.content.Context;
     20 import android.test.AndroidTestCase;
     21 import android.util.Log;
     22 import libcore.util.HexEncoding;
     23 
     24 import java.io.IOException;
     25 import java.net.DatagramPacket;
     26 import java.net.DatagramSocket;
     27 import java.net.InetAddress;
     28 import java.net.SocketException;
     29 import java.util.Arrays;
     30 
     31 
     32 public class SntpClientTest extends AndroidTestCase {
     33     private static final String TAG = "SntpClientTest";
     34 
     35     private static final int ORIGINATE_TIME_OFFSET = 24;
     36     private static final int TRANSMIT_TIME_OFFSET = 40;
     37 
     38     private static final int NTP_MODE_SERVER = 4;
     39     private static final int NTP_MODE_BROADCAST = 5;
     40 
     41     // From tcpdump (admittedly, an NTPv4 packet):
     42     //
     43     // Server, Leap indicator:  (0), Stratum 2 (secondary reference), poll 6 (64s), precision -20
     44     // Root Delay: 0.005447, Root dispersion: 0.002716, Reference-ID: 221.253.71.41
     45     //   Reference Timestamp:  3653932102.507969856 (2015/10/15 14:08:22)
     46     //   Originator Timestamp: 3653932113.576327741 (2015/10/15 14:08:33)
     47     //   Receive Timestamp:    3653932113.581012725 (2015/10/15 14:08:33)
     48     //   Transmit Timestamp:   3653932113.581012725 (2015/10/15 14:08:33)
     49     //     Originator - Receive Timestamp:  +0.004684958
     50     //     Originator - Transmit Timestamp: +0.004684958
     51     private static final String WORKING_VERSION4 =
     52             "240206ec" +
     53             "00000165" +
     54             "000000b2" +
     55             "ddfd4729" +
     56             "d9ca9446820a5000" +
     57             "d9ca9451938a3771" +
     58             "d9ca945194bd3fff" +
     59             "d9ca945194bd4001";
     60 
     61     private final SntpTestServer mServer = new SntpTestServer();
     62     private final SntpClient mClient = new SntpClient();
     63 
     64     private Network mNetwork;
     65 
     66     @Override
     67     protected void setUp() throws Exception {
     68         super.setUp();
     69         ConnectivityManager mCM = getContext().getSystemService(ConnectivityManager.class);
     70         mNetwork = mCM.getActiveNetwork();
     71     }
     72 
     73     public void testBasicWorkingSntpClientQuery() throws Exception {
     74         mServer.setServerReply(HexEncoding.decode(WORKING_VERSION4.toCharArray(), false));
     75         assertTrue(mClient.requestTime(mServer.getAddress(), mServer.getPort(), 500, mNetwork));
     76         assertEquals(1, mServer.numRequestsReceived());
     77         assertEquals(1, mServer.numRepliesSent());
     78     }
     79 
     80     public void testDnsResolutionFailure() throws Exception {
     81         assertFalse(mClient.requestTime("ntp.server.doesnotexist.example", 5000, mNetwork));
     82     }
     83 
     84     public void testTimeoutFailure() throws Exception {
     85         mServer.clearServerReply();
     86         assertFalse(mClient.requestTime(mServer.getAddress(), mServer.getPort(), 500, mNetwork));
     87         assertEquals(1, mServer.numRequestsReceived());
     88         assertEquals(0, mServer.numRepliesSent());
     89     }
     90 
     91     public void testIgnoreLeapNoSync() throws Exception {
     92         final byte[] reply = HexEncoding.decode(WORKING_VERSION4.toCharArray(), false);
     93         reply[0] |= (byte) 0xc0;
     94         mServer.setServerReply(reply);
     95         assertFalse(mClient.requestTime(mServer.getAddress(), mServer.getPort(), 500, mNetwork));
     96         assertEquals(1, mServer.numRequestsReceived());
     97         assertEquals(1, mServer.numRepliesSent());
     98     }
     99 
    100     public void testAcceptOnlyServerAndBroadcastModes() throws Exception {
    101         final byte[] reply = HexEncoding.decode(WORKING_VERSION4.toCharArray(), false);
    102         for (int i = 0; i <= 7; i++) {
    103             final String logMsg = "mode: " + i;
    104             reply[0] &= (byte) 0xf8;
    105             reply[0] |= (byte) i;
    106             mServer.setServerReply(reply);
    107             final boolean rval = mClient.requestTime(mServer.getAddress(), mServer.getPort(), 500,
    108                     mNetwork);
    109             switch (i) {
    110                 case NTP_MODE_SERVER:
    111                 case NTP_MODE_BROADCAST:
    112                     assertTrue(logMsg, rval);
    113                     break;
    114                 default:
    115                     assertFalse(logMsg, rval);
    116                     break;
    117             }
    118             assertEquals(logMsg, 1, mServer.numRequestsReceived());
    119             assertEquals(logMsg, 1, mServer.numRepliesSent());
    120         }
    121     }
    122 
    123     public void testAcceptableStrataOnly() throws Exception {
    124         final int STRATUM_MIN = 1;
    125         final int STRATUM_MAX = 15;
    126 
    127         final byte[] reply = HexEncoding.decode(WORKING_VERSION4.toCharArray(), false);
    128         for (int i = 0; i < 256; i++) {
    129             final String logMsg = "stratum: " + i;
    130             reply[1] = (byte) i;
    131             mServer.setServerReply(reply);
    132             final boolean rval = mClient.requestTime(mServer.getAddress(), mServer.getPort(), 500,
    133                     mNetwork);
    134             if (STRATUM_MIN <= i && i <= STRATUM_MAX) {
    135                 assertTrue(logMsg, rval);
    136             } else {
    137                 assertFalse(logMsg, rval);
    138             }
    139             assertEquals(logMsg, 1, mServer.numRequestsReceived());
    140             assertEquals(logMsg, 1, mServer.numRepliesSent());
    141         }
    142     }
    143 
    144     public void testZeroTransmitTime() throws Exception {
    145         final byte[] reply = HexEncoding.decode(WORKING_VERSION4.toCharArray(), false);
    146         Arrays.fill(reply, TRANSMIT_TIME_OFFSET, TRANSMIT_TIME_OFFSET + 8, (byte) 0x00);
    147         mServer.setServerReply(reply);
    148         assertFalse(mClient.requestTime(mServer.getAddress(), mServer.getPort(), 500, mNetwork));
    149         assertEquals(1, mServer.numRequestsReceived());
    150         assertEquals(1, mServer.numRepliesSent());
    151     }
    152 
    153 
    154     private static class SntpTestServer {
    155         private final Object mLock = new Object();
    156         private final DatagramSocket mSocket;
    157         private final InetAddress mAddress;
    158         private final int mPort;
    159         private byte[] mReply;
    160         private int mRcvd;
    161         private int mSent;
    162         private Thread mListeningThread;
    163 
    164         public SntpTestServer() {
    165             mSocket = makeSocket();
    166             mAddress = mSocket.getLocalAddress();
    167             mPort = mSocket.getLocalPort();
    168             Log.d(TAG, "testing server listening on (" + mAddress + ", " + mPort + ")");
    169 
    170             mListeningThread = new Thread() {
    171                 public void run() {
    172                     while (true) {
    173                         byte[] buffer = new byte[512];
    174                         DatagramPacket ntpMsg = new DatagramPacket(buffer, buffer.length);
    175                         try {
    176                             mSocket.receive(ntpMsg);
    177                         } catch (IOException e) {
    178                             Log.e(TAG, "datagram receive error: " + e);
    179                             break;
    180                         }
    181                         synchronized (mLock) {
    182                             mRcvd++;
    183                             if (mReply == null) { continue; }
    184                             // Copy transmit timestamp into originate timestamp.
    185                             // TODO: bounds checking.
    186                             System.arraycopy(ntpMsg.getData(), TRANSMIT_TIME_OFFSET,
    187                                              mReply, ORIGINATE_TIME_OFFSET, 8);
    188                             ntpMsg.setData(mReply);
    189                             ntpMsg.setLength(mReply.length);
    190                             try {
    191                                 mSocket.send(ntpMsg);
    192                             } catch (IOException e) {
    193                                 Log.e(TAG, "datagram send error: " + e);
    194                                 break;
    195                             }
    196                             mSent++;
    197                         }
    198                     }
    199                     mSocket.close();
    200                 }
    201             };
    202             mListeningThread.start();
    203         }
    204 
    205         private DatagramSocket makeSocket() {
    206             DatagramSocket socket;
    207             try {
    208                 socket = new DatagramSocket(0, InetAddress.getLoopbackAddress());
    209             } catch (SocketException e) {
    210                 Log.e(TAG, "Failed to create test server socket: " + e);
    211                 return null;
    212             }
    213             return socket;
    214         }
    215 
    216         public void clearServerReply() {
    217             setServerReply(null);
    218         }
    219 
    220         public void setServerReply(byte[] reply) {
    221             synchronized (mLock) {
    222                 mReply = reply;
    223                 mRcvd = 0;
    224                 mSent = 0;
    225             }
    226         }
    227 
    228         public InetAddress getAddress() { return mAddress; }
    229         public int getPort() { return mPort; }
    230         public int numRequestsReceived() { synchronized (mLock) { return mRcvd; } }
    231         public int numRepliesSent() { synchronized (mLock) { return mSent; } }
    232     }
    233 }
    234