1 /* 2 * Copyright (C) 2008 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.compat.annotation.UnsupportedAppUsage; 20 import android.net.sntp.Duration64; 21 import android.net.sntp.Timestamp64; 22 import android.os.SystemClock; 23 import android.util.Log; 24 import android.util.Slog; 25 26 import com.android.internal.annotations.VisibleForTesting; 27 import com.android.internal.util.TrafficStatsConstants; 28 29 import java.net.DatagramPacket; 30 import java.net.DatagramSocket; 31 import java.net.InetAddress; 32 import java.net.UnknownHostException; 33 import java.security.NoSuchAlgorithmException; 34 import java.security.SecureRandom; 35 import java.time.Duration; 36 import java.time.Instant; 37 import java.util.Objects; 38 import java.util.Random; 39 import java.util.function.Supplier; 40 41 /** 42 * {@hide} 43 * 44 * Simple SNTP client class for retrieving network time. 45 * 46 * Sample usage: 47 * <pre>SntpClient client = new SntpClient(); 48 * if (client.requestTime("time.foo.com")) { 49 * long now = client.getNtpTime() + SystemClock.elapsedRealtime() - client.getNtpTimeReference(); 50 * } 51 * </pre> 52 */ 53 public class SntpClient { 54 private static final String TAG = "SntpClient"; 55 private static final boolean DBG = true; 56 57 private static final int REFERENCE_TIME_OFFSET = 16; 58 private static final int ORIGINATE_TIME_OFFSET = 24; 59 private static final int RECEIVE_TIME_OFFSET = 32; 60 private static final int TRANSMIT_TIME_OFFSET = 40; 61 private static final int NTP_PACKET_SIZE = 48; 62 63 public static final int STANDARD_NTP_PORT = 123; 64 private static final int NTP_MODE_CLIENT = 3; 65 private static final int NTP_MODE_SERVER = 4; 66 private static final int NTP_MODE_BROADCAST = 5; 67 private static final int NTP_VERSION = 3; 68 69 private static final int NTP_LEAP_NOSYNC = 3; 70 private static final int NTP_STRATUM_DEATH = 0; 71 private static final int NTP_STRATUM_MAX = 15; 72 73 // The source of the current system clock time, replaceable for testing. 74 private final Supplier<Instant> mSystemTimeSupplier; 75 76 private final Random mRandom; 77 78 // The last offset calculated from an NTP server response 79 private long mClockOffset; 80 81 // The last system time computed from an NTP server response 82 private long mNtpTime; 83 84 // The value of SystemClock.elapsedRealtime() corresponding to mNtpTime / mClockOffset 85 private long mNtpTimeReference; 86 87 // The round trip (network) time in milliseconds 88 private long mRoundTripTime; 89 90 private static class InvalidServerReplyException extends Exception { InvalidServerReplyException(String message)91 public InvalidServerReplyException(String message) { 92 super(message); 93 } 94 } 95 96 @UnsupportedAppUsage SntpClient()97 public SntpClient() { 98 this(Instant::now, defaultRandom()); 99 } 100 101 @VisibleForTesting SntpClient(Supplier<Instant> systemTimeSupplier, Random random)102 public SntpClient(Supplier<Instant> systemTimeSupplier, Random random) { 103 mSystemTimeSupplier = Objects.requireNonNull(systemTimeSupplier); 104 mRandom = Objects.requireNonNull(random); 105 } 106 107 /** 108 * Sends an SNTP request to the given host and processes the response. 109 * 110 * @param host host name of the server. 111 * @param port port of the server. 112 * @param timeout network timeout in milliseconds. the timeout doesn't include the DNS lookup 113 * time, and it applies to each individual query to the resolved addresses of 114 * the NTP server. 115 * @param network network over which to send the request. 116 * @return true if the transaction was successful. 117 */ requestTime(String host, int port, int timeout, Network network)118 public boolean requestTime(String host, int port, int timeout, Network network) { 119 final Network networkForResolv = network.getPrivateDnsBypassingCopy(); 120 try { 121 final InetAddress[] addresses = networkForResolv.getAllByName(host); 122 for (int i = 0; i < addresses.length; i++) { 123 if (requestTime(addresses[i], port, timeout, networkForResolv)) { 124 return true; 125 } 126 } 127 } catch (UnknownHostException e) { 128 Log.w(TAG, "Unknown host: " + host); 129 EventLogTags.writeNtpFailure(host, e.toString()); 130 } 131 132 if (DBG) Log.d(TAG, "request time failed"); 133 return false; 134 } 135 requestTime(InetAddress address, int port, int timeout, Network network)136 public boolean requestTime(InetAddress address, int port, int timeout, Network network) { 137 DatagramSocket socket = null; 138 final int oldTag = TrafficStats.getAndSetThreadStatsTag( 139 TrafficStatsConstants.TAG_SYSTEM_NTP); 140 try { 141 socket = new DatagramSocket(); 142 network.bindSocket(socket); 143 socket.setSoTimeout(timeout); 144 byte[] buffer = new byte[NTP_PACKET_SIZE]; 145 DatagramPacket request = new DatagramPacket(buffer, buffer.length, address, port); 146 147 // set mode = 3 (client) and version = 3 148 // mode is in low 3 bits of first byte 149 // version is in bits 3-5 of first byte 150 buffer[0] = NTP_MODE_CLIENT | (NTP_VERSION << 3); 151 152 // get current time and write it to the request packet 153 final Instant requestTime = mSystemTimeSupplier.get(); 154 final Timestamp64 requestTimestamp = Timestamp64.fromInstant(requestTime); 155 156 final Timestamp64 randomizedRequestTimestamp = 157 requestTimestamp.randomizeSubMillis(mRandom); 158 final long requestTicks = SystemClock.elapsedRealtime(); 159 writeTimeStamp(buffer, TRANSMIT_TIME_OFFSET, randomizedRequestTimestamp); 160 161 socket.send(request); 162 163 // read the response 164 DatagramPacket response = new DatagramPacket(buffer, buffer.length); 165 socket.receive(response); 166 final long responseTicks = SystemClock.elapsedRealtime(); 167 final Instant responseTime = requestTime.plusMillis(responseTicks - requestTicks); 168 final Timestamp64 responseTimestamp = Timestamp64.fromInstant(responseTime); 169 170 // extract the results 171 final byte leap = (byte) ((buffer[0] >> 6) & 0x3); 172 final byte mode = (byte) (buffer[0] & 0x7); 173 final int stratum = (int) (buffer[1] & 0xff); 174 final Timestamp64 referenceTimestamp = readTimeStamp(buffer, REFERENCE_TIME_OFFSET); 175 final Timestamp64 originateTimestamp = readTimeStamp(buffer, ORIGINATE_TIME_OFFSET); 176 final Timestamp64 receiveTimestamp = readTimeStamp(buffer, RECEIVE_TIME_OFFSET); 177 final Timestamp64 transmitTimestamp = readTimeStamp(buffer, TRANSMIT_TIME_OFFSET); 178 179 /* Do validation according to RFC */ 180 checkValidServerReply(leap, mode, stratum, transmitTimestamp, referenceTimestamp, 181 randomizedRequestTimestamp, originateTimestamp); 182 183 long totalTransactionDurationMillis = responseTicks - requestTicks; 184 long serverDurationMillis = 185 Duration64.between(receiveTimestamp, transmitTimestamp).toDuration().toMillis(); 186 long roundTripTimeMillis = totalTransactionDurationMillis - serverDurationMillis; 187 188 Duration clockOffsetDuration = calculateClockOffset(requestTimestamp, 189 receiveTimestamp, transmitTimestamp, responseTimestamp); 190 long clockOffsetMillis = clockOffsetDuration.toMillis(); 191 192 EventLogTags.writeNtpSuccess( 193 address.toString(), roundTripTimeMillis, clockOffsetMillis); 194 if (DBG) { 195 Log.d(TAG, "round trip: " + roundTripTimeMillis + "ms, " 196 + "clock offset: " + clockOffsetMillis + "ms"); 197 } 198 199 // save our results - use the times on this side of the network latency 200 // (response rather than request time) 201 mClockOffset = clockOffsetMillis; 202 mNtpTime = responseTime.plus(clockOffsetDuration).toEpochMilli(); 203 mNtpTimeReference = responseTicks; 204 mRoundTripTime = roundTripTimeMillis; 205 } catch (Exception e) { 206 EventLogTags.writeNtpFailure(address.toString(), e.toString()); 207 if (DBG) Log.d(TAG, "request time failed: " + e); 208 return false; 209 } finally { 210 if (socket != null) { 211 socket.close(); 212 } 213 TrafficStats.setThreadStatsTag(oldTag); 214 } 215 216 return true; 217 } 218 219 /** Performs the NTP clock offset calculation. */ 220 @VisibleForTesting calculateClockOffset(Timestamp64 clientRequestTimestamp, Timestamp64 serverReceiveTimestamp, Timestamp64 serverTransmitTimestamp, Timestamp64 clientResponseTimestamp)221 public static Duration calculateClockOffset(Timestamp64 clientRequestTimestamp, 222 Timestamp64 serverReceiveTimestamp, Timestamp64 serverTransmitTimestamp, 223 Timestamp64 clientResponseTimestamp) { 224 // According to RFC4330: 225 // t is the system clock offset (the adjustment we are trying to find) 226 // t = ((T2 - T1) + (T3 - T4)) / 2 227 // 228 // Which is: 229 // t = (([server]receiveTimestamp - [client]requestTimestamp) 230 // + ([server]transmitTimestamp - [client]responseTimestamp)) / 2 231 // 232 // See the NTP spec and tests: the numeric types used are deliberate: 233 // + Duration64.between() uses 64-bit arithmetic (32-bit for the seconds). 234 // + plus() / dividedBy() use Duration, which isn't the double precision floating point 235 // used in NTPv4, but is good enough. 236 return Duration64.between(clientRequestTimestamp, serverReceiveTimestamp) 237 .plus(Duration64.between(clientResponseTimestamp, serverTransmitTimestamp)) 238 .dividedBy(2); 239 } 240 241 @Deprecated 242 @UnsupportedAppUsage requestTime(String host, int timeout)243 public boolean requestTime(String host, int timeout) { 244 Log.w(TAG, "Shame on you for calling the hidden API requestTime()!"); 245 return false; 246 } 247 248 /** 249 * Returns the offset calculated to apply to the client clock to arrive at {@link #getNtpTime()} 250 */ 251 @VisibleForTesting getClockOffset()252 public long getClockOffset() { 253 return mClockOffset; 254 } 255 256 /** 257 * Returns the time computed from the NTP transaction. 258 * 259 * @return time value computed from NTP server response. 260 */ 261 @UnsupportedAppUsage getNtpTime()262 public long getNtpTime() { 263 return mNtpTime; 264 } 265 266 /** 267 * Returns the reference clock value (value of SystemClock.elapsedRealtime()) 268 * corresponding to the NTP time. 269 * 270 * @return reference clock corresponding to the NTP time. 271 */ 272 @UnsupportedAppUsage getNtpTimeReference()273 public long getNtpTimeReference() { 274 return mNtpTimeReference; 275 } 276 277 /** 278 * Returns the round trip time of the NTP transaction 279 * 280 * @return round trip time in milliseconds. 281 */ 282 @UnsupportedAppUsage getRoundTripTime()283 public long getRoundTripTime() { 284 return mRoundTripTime; 285 } 286 checkValidServerReply( byte leap, byte mode, int stratum, Timestamp64 transmitTimestamp, Timestamp64 referenceTimestamp, Timestamp64 randomizedRequestTimestamp, Timestamp64 originateTimestamp)287 private static void checkValidServerReply( 288 byte leap, byte mode, int stratum, Timestamp64 transmitTimestamp, 289 Timestamp64 referenceTimestamp, Timestamp64 randomizedRequestTimestamp, 290 Timestamp64 originateTimestamp) throws InvalidServerReplyException { 291 if (leap == NTP_LEAP_NOSYNC) { 292 throw new InvalidServerReplyException("unsynchronized server"); 293 } 294 if ((mode != NTP_MODE_SERVER) && (mode != NTP_MODE_BROADCAST)) { 295 throw new InvalidServerReplyException("untrusted mode: " + mode); 296 } 297 if ((stratum == NTP_STRATUM_DEATH) || (stratum > NTP_STRATUM_MAX)) { 298 throw new InvalidServerReplyException("untrusted stratum: " + stratum); 299 } 300 if (!randomizedRequestTimestamp.equals(originateTimestamp)) { 301 throw new InvalidServerReplyException( 302 "originateTimestamp != randomizedRequestTimestamp"); 303 } 304 if (transmitTimestamp.equals(Timestamp64.ZERO)) { 305 throw new InvalidServerReplyException("zero transmitTimestamp"); 306 } 307 if (referenceTimestamp.equals(Timestamp64.ZERO)) { 308 throw new InvalidServerReplyException("zero referenceTimestamp"); 309 } 310 } 311 312 /** 313 * Reads an unsigned 32 bit big endian number from the given offset in the buffer. 314 */ readUnsigned32(byte[] buffer, int offset)315 private long readUnsigned32(byte[] buffer, int offset) { 316 int i0 = buffer[offset++] & 0xFF; 317 int i1 = buffer[offset++] & 0xFF; 318 int i2 = buffer[offset++] & 0xFF; 319 int i3 = buffer[offset] & 0xFF; 320 321 int bits = (i0 << 24) | (i1 << 16) | (i2 << 8) | i3; 322 return bits & 0xFFFF_FFFFL; 323 } 324 325 /** 326 * Reads the NTP time stamp from the given offset in the buffer. 327 */ readTimeStamp(byte[] buffer, int offset)328 private Timestamp64 readTimeStamp(byte[] buffer, int offset) { 329 long seconds = readUnsigned32(buffer, offset); 330 int fractionBits = (int) readUnsigned32(buffer, offset + 4); 331 return Timestamp64.fromComponents(seconds, fractionBits); 332 } 333 334 /** 335 * Writes the NTP time stamp at the given offset in the buffer. 336 */ writeTimeStamp(byte[] buffer, int offset, Timestamp64 timestamp)337 private void writeTimeStamp(byte[] buffer, int offset, Timestamp64 timestamp) { 338 long seconds = timestamp.getEraSeconds(); 339 // write seconds in big endian format 340 buffer[offset++] = (byte) (seconds >>> 24); 341 buffer[offset++] = (byte) (seconds >>> 16); 342 buffer[offset++] = (byte) (seconds >>> 8); 343 buffer[offset++] = (byte) (seconds); 344 345 int fractionBits = timestamp.getFractionBits(); 346 // write fraction in big endian format 347 buffer[offset++] = (byte) (fractionBits >>> 24); 348 buffer[offset++] = (byte) (fractionBits >>> 16); 349 buffer[offset++] = (byte) (fractionBits >>> 8); 350 buffer[offset] = (byte) (fractionBits); 351 } 352 defaultRandom()353 private static Random defaultRandom() { 354 Random random; 355 try { 356 random = SecureRandom.getInstanceStrong(); 357 } catch (NoSuchAlgorithmException e) { 358 // This should never happen. 359 Slog.wtf(TAG, "Unable to access SecureRandom", e); 360 random = new Random(System.currentTimeMillis()); 361 } 362 return random; 363 } 364 } 365