69 lines
2.2 KiB
Swift
69 lines
2.2 KiB
Swift
//
|
|
// NTPResponse.swift
|
|
// TrueTime
|
|
//
|
|
// Created by Michael Sanders on 10/14/16.
|
|
// Copyright © 2016 Instacart. All rights reserved.
|
|
//
|
|
|
|
import Foundation
|
|
|
|
struct NTPResponse {
|
|
let packet: ntp_packet_t
|
|
let responseTime: Int64
|
|
let receiveTime: timeval
|
|
init?(packet: ntp_packet_t, responseTime: Int64, receiveTime: timeval = .now()) {
|
|
self.packet = packet
|
|
self.responseTime = responseTime
|
|
self.receiveTime = receiveTime
|
|
guard isValidResponse else { return nil }
|
|
}
|
|
|
|
// See https://en.wikipedia.org/wiki/Network_Time_Protocol#Clock_synchronization_algorithm
|
|
var offset: Int64 {
|
|
let T = offsetValues
|
|
return ((T[1] - T[0]) + (T[2] - T[3])) / 2
|
|
}
|
|
|
|
var delay: Int64 {
|
|
let T = offsetValues
|
|
return (T[3] - T[0]) - (T[2] - T[1])
|
|
}
|
|
|
|
var networkDate: Date {
|
|
let interval = TimeInterval(milliseconds: responseTime + offset)
|
|
return Date(timeIntervalSince1970: interval)
|
|
}
|
|
}
|
|
|
|
func bestTime(fromResponses times: [[FrozenNetworkTime]]) -> FrozenNetworkTime? {
|
|
let bestTimes = times.map { serverTimes -> FrozenNetworkTime? in
|
|
serverTimes.min { $0.serverResponse.delay < $1.serverResponse.delay }
|
|
}.compactMap { $0 }.sorted { $0.serverResponse.offset < $1.serverResponse.offset }
|
|
|
|
return bestTimes.isEmpty ? nil : bestTimes[bestTimes.count / 2]
|
|
}
|
|
|
|
private extension NTPResponse {
|
|
var isValidResponse: Bool {
|
|
return packet.stratum > 0 && packet.stratum < 16 &&
|
|
packet.root_delay.durationInMilliseconds < maxRootDispersion &&
|
|
packet.root_dispersion.durationInMilliseconds < maxRootDispersion &&
|
|
packet.client_mode == ntpModeServer &&
|
|
packet.leap_indicator != leapIndicatorUnknown &&
|
|
abs(receiveTime.milliseconds - packet.originate_time.milliseconds - delay) < maxDelayDelta
|
|
}
|
|
|
|
var offsetValues: [Int64] {
|
|
return [packet.originate_time.milliseconds,
|
|
packet.receive_time.milliseconds,
|
|
packet.transmit_time.milliseconds,
|
|
responseTime]
|
|
}
|
|
}
|
|
|
|
private let maxRootDispersion: Int64 = 100
|
|
private let maxDelayDelta: Int64 = 100
|
|
private let ntpModeServer: UInt8 = 4
|
|
private let leapIndicatorUnknown: UInt8 = 3
|