From 562d4548c4083e12d6f9c2c2a90118f37426cabd Mon Sep 17 00:00:00 2001 From: Maciek Grzybowski Date: Tue, 28 Dec 2021 13:42:59 +0100 Subject: [PATCH 1/7] RUMM-1744 Copy & paste Kronos code and tests into SDK --- Datadog/Datadog.xcodeproj/project.pbxproj | 76 +++++++ Sources/Datadog/Kronos/Clock.swift | 102 +++++++++ Sources/Datadog/Kronos/DNSResolver.swift | 81 +++++++ Sources/Datadog/Kronos/Data+Bytes.swift | 91 ++++++++ Sources/Datadog/Kronos/InternetAddress.swift | 108 ++++++++++ .../Datadog/Kronos/NSTimer+ClosureKit.swift | 52 +++++ Sources/Datadog/Kronos/NTPClient.swift | 189 +++++++++++++++++ Sources/Datadog/Kronos/NTPPacket.swift | 200 ++++++++++++++++++ Sources/Datadog/Kronos/NTPProtocol.swift | 128 +++++++++++ Sources/Datadog/Kronos/TimeFreeze.swift | 87 ++++++++ Sources/Datadog/Kronos/TimeStorage.swift | 63 ++++++ .../Datadog/Kronos/ClockTests.swift | 41 ++++ .../Datadog/Kronos/DNSResolverTests.swift | 66 ++++++ .../Datadog/Kronos/NTPClientTests.swift | 50 +++++ .../Datadog/Kronos/NTPPacketTests.swift | 41 ++++ .../Datadog/Kronos/TimeStorageTests.swift | 45 ++++ 16 files changed, 1420 insertions(+) create mode 100644 Sources/Datadog/Kronos/Clock.swift create mode 100644 Sources/Datadog/Kronos/DNSResolver.swift create mode 100644 Sources/Datadog/Kronos/Data+Bytes.swift create mode 100644 Sources/Datadog/Kronos/InternetAddress.swift create mode 100644 Sources/Datadog/Kronos/NSTimer+ClosureKit.swift create mode 100644 Sources/Datadog/Kronos/NTPClient.swift create mode 100644 Sources/Datadog/Kronos/NTPPacket.swift create mode 100644 Sources/Datadog/Kronos/NTPProtocol.swift create mode 100644 Sources/Datadog/Kronos/TimeFreeze.swift create mode 100644 Sources/Datadog/Kronos/TimeStorage.swift create mode 100644 Tests/DatadogTests/Datadog/Kronos/ClockTests.swift create mode 100644 Tests/DatadogTests/Datadog/Kronos/DNSResolverTests.swift create mode 100644 Tests/DatadogTests/Datadog/Kronos/NTPClientTests.swift create mode 100644 Tests/DatadogTests/Datadog/Kronos/NTPPacketTests.swift create mode 100644 Tests/DatadogTests/Datadog/Kronos/TimeStorageTests.swift diff --git a/Datadog/Datadog.xcodeproj/project.pbxproj b/Datadog/Datadog.xcodeproj/project.pbxproj index 6a1408baad..c0831deae1 100644 --- a/Datadog/Datadog.xcodeproj/project.pbxproj +++ b/Datadog/Datadog.xcodeproj/project.pbxproj @@ -396,6 +396,21 @@ 61C5A8A624509FAA00DA608C /* SpanEventEncoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61C5A8A424509FAA00DA608C /* SpanEventEncoder.swift */; }; 61C5A8A724509FAA00DA608C /* SpanEventBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61C5A8A524509FAA00DA608C /* SpanEventBuilder.swift */; }; 61D03BE0273404E700367DE0 /* RUMDataModels+objcTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61D03BDF273404E700367DE0 /* RUMDataModels+objcTests.swift */; }; + 61D3E0D2277B23F1008BE766 /* InternetAddress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61D3E0C8277B23F0008BE766 /* InternetAddress.swift */; }; + 61D3E0D3277B23F1008BE766 /* DNSResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61D3E0C9277B23F0008BE766 /* DNSResolver.swift */; }; + 61D3E0D4277B23F1008BE766 /* TimeStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61D3E0CA277B23F0008BE766 /* TimeStorage.swift */; }; + 61D3E0D5277B23F1008BE766 /* NTPPacket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61D3E0CB277B23F0008BE766 /* NTPPacket.swift */; }; + 61D3E0D6277B23F1008BE766 /* Clock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61D3E0CC277B23F0008BE766 /* Clock.swift */; }; + 61D3E0D7277B23F1008BE766 /* Data+Bytes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61D3E0CD277B23F0008BE766 /* Data+Bytes.swift */; }; + 61D3E0D8277B23F1008BE766 /* NTPClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61D3E0CE277B23F0008BE766 /* NTPClient.swift */; }; + 61D3E0D9277B23F1008BE766 /* NTPProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61D3E0CF277B23F0008BE766 /* NTPProtocol.swift */; }; + 61D3E0DA277B23F1008BE766 /* TimeFreeze.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61D3E0D0277B23F1008BE766 /* TimeFreeze.swift */; }; + 61D3E0DB277B23F1008BE766 /* NSTimer+ClosureKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61D3E0D1277B23F1008BE766 /* NSTimer+ClosureKit.swift */; }; + 61D3E0E3277B3D92008BE766 /* NTPClientTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61D3E0DE277B3D92008BE766 /* NTPClientTests.swift */; }; + 61D3E0E4277B3D92008BE766 /* NTPPacketTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61D3E0DF277B3D92008BE766 /* NTPPacketTests.swift */; }; + 61D3E0E5277B3D92008BE766 /* ClockTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61D3E0E0277B3D92008BE766 /* ClockTests.swift */; }; + 61D3E0E6277B3D92008BE766 /* DNSResolverTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61D3E0E1277B3D92008BE766 /* DNSResolverTests.swift */; }; + 61D3E0E7277B3D92008BE766 /* TimeStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61D3E0E2277B3D92008BE766 /* TimeStorageTests.swift */; }; 61D447E224917F8F00649287 /* DateFormatting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61D447E124917F8F00649287 /* DateFormatting.swift */; }; 61D50C3C2580EEF8006038A3 /* LoggingScenarios.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61D50C3B2580EEF8006038A3 /* LoggingScenarios.swift */; }; 61D50C462580EF19006038A3 /* TracingScenarios.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61D50C452580EF19006038A3 /* TracingScenarios.swift */; }; @@ -1070,6 +1085,21 @@ 61C5A8A424509FAA00DA608C /* SpanEventEncoder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SpanEventEncoder.swift; sourceTree = ""; }; 61C5A8A524509FAA00DA608C /* SpanEventBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SpanEventBuilder.swift; sourceTree = ""; }; 61D03BDF273404E700367DE0 /* RUMDataModels+objcTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RUMDataModels+objcTests.swift"; sourceTree = ""; }; + 61D3E0C8277B23F0008BE766 /* InternetAddress.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InternetAddress.swift; sourceTree = ""; }; + 61D3E0C9277B23F0008BE766 /* DNSResolver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DNSResolver.swift; sourceTree = ""; }; + 61D3E0CA277B23F0008BE766 /* TimeStorage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TimeStorage.swift; sourceTree = ""; }; + 61D3E0CB277B23F0008BE766 /* NTPPacket.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NTPPacket.swift; sourceTree = ""; }; + 61D3E0CC277B23F0008BE766 /* Clock.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Clock.swift; sourceTree = ""; }; + 61D3E0CD277B23F0008BE766 /* Data+Bytes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Data+Bytes.swift"; sourceTree = ""; }; + 61D3E0CE277B23F0008BE766 /* NTPClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NTPClient.swift; sourceTree = ""; }; + 61D3E0CF277B23F0008BE766 /* NTPProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NTPProtocol.swift; sourceTree = ""; }; + 61D3E0D0277B23F1008BE766 /* TimeFreeze.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TimeFreeze.swift; sourceTree = ""; }; + 61D3E0D1277B23F1008BE766 /* NSTimer+ClosureKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSTimer+ClosureKit.swift"; sourceTree = ""; }; + 61D3E0DE277B3D92008BE766 /* NTPClientTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NTPClientTests.swift; sourceTree = ""; }; + 61D3E0DF277B3D92008BE766 /* NTPPacketTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NTPPacketTests.swift; sourceTree = ""; }; + 61D3E0E0277B3D92008BE766 /* ClockTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ClockTests.swift; sourceTree = ""; }; + 61D3E0E1277B3D92008BE766 /* DNSResolverTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DNSResolverTests.swift; sourceTree = ""; }; + 61D3E0E2277B3D92008BE766 /* TimeStorageTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TimeStorageTests.swift; sourceTree = ""; }; 61D447E124917F8F00649287 /* DateFormatting.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DateFormatting.swift; sourceTree = ""; }; 61D50C3B2580EEF8006038A3 /* LoggingScenarios.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoggingScenarios.swift; sourceTree = ""; }; 61D50C452580EF19006038A3 /* TracingScenarios.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TracingScenarios.swift; sourceTree = ""; }; @@ -1449,6 +1479,7 @@ 61216277247D1F2100AC5D67 /* FeaturesIntegration */, 61133BB72423979B00786299 /* Utils */, 61E909E524A24DD3005EA2DE /* OpenTracing */, + 61D3E0C7277B237D008BE766 /* Kronos */, ); name = Datadog; path = ../Sources/Datadog; @@ -1703,6 +1734,7 @@ 61216278247D20D500AC5D67 /* FeaturesIntegration */, 61133C352423990D00786299 /* Utils */, 61BAD46826415FA2001886CA /* OpenTracing */, + 61D3E0DD277B3D6E008BE766 /* Kronos */, ); path = Datadog; sourceTree = ""; @@ -2923,6 +2955,35 @@ path = RUM; sourceTree = ""; }; + 61D3E0C7277B237D008BE766 /* Kronos */ = { + isa = PBXGroup; + children = ( + 61D3E0CC277B23F0008BE766 /* Clock.swift */, + 61D3E0CD277B23F0008BE766 /* Data+Bytes.swift */, + 61D3E0C9277B23F0008BE766 /* DNSResolver.swift */, + 61D3E0C8277B23F0008BE766 /* InternetAddress.swift */, + 61D3E0D1277B23F1008BE766 /* NSTimer+ClosureKit.swift */, + 61D3E0CE277B23F0008BE766 /* NTPClient.swift */, + 61D3E0CB277B23F0008BE766 /* NTPPacket.swift */, + 61D3E0CF277B23F0008BE766 /* NTPProtocol.swift */, + 61D3E0D0277B23F1008BE766 /* TimeFreeze.swift */, + 61D3E0CA277B23F0008BE766 /* TimeStorage.swift */, + ); + path = Kronos; + sourceTree = ""; + }; + 61D3E0DD277B3D6E008BE766 /* Kronos */ = { + isa = PBXGroup; + children = ( + 61D3E0E0277B3D92008BE766 /* ClockTests.swift */, + 61D3E0E1277B3D92008BE766 /* DNSResolverTests.swift */, + 61D3E0DE277B3D92008BE766 /* NTPClientTests.swift */, + 61D3E0DF277B3D92008BE766 /* NTPPacketTests.swift */, + 61D3E0E2277B3D92008BE766 /* TimeStorageTests.swift */, + ); + path = Kronos; + sourceTree = ""; + }; 61D50C3A2580EED5006038A3 /* ManualInstrumentation */ = { isa = PBXGroup; children = ( @@ -3991,8 +4052,10 @@ 61133BDC2423979B00786299 /* Logger.swift in Sources */, 6114FE1625766B310084E372 /* TrackingConsent.swift in Sources */, 61133BD02423979B00786299 /* DateProvider.swift in Sources */, + 61D3E0D2277B23F1008BE766 /* InternetAddress.swift in Sources */, 6156CB8E24DDA1B5008CB2B2 /* RUMContextProvider.swift in Sources */, 61C2C21224C5951400C0321C /* RUMViewScope.swift in Sources */, + 61D3E0D5277B23F1008BE766 /* NTPPacket.swift in Sources */, 61DE332625C826E4008E3EC2 /* CrashReportingFeature.swift in Sources */, 61E36A11254B2280001AD6F2 /* LaunchTimeProvider.swift in Sources */, 614872772485067300E3EBDB /* SpanTagsReducer.swift in Sources */, @@ -4033,6 +4096,7 @@ 619E16E92578E73E00B2516B /* DataMigrator.swift in Sources */, 618DCFD924C7269500589570 /* RUMUUIDGenerator.swift in Sources */, 9EFD112C24B32D29003A1A2B /* FirstPartyURLsFilter.swift in Sources */, + 61D3E0DB277B23F1008BE766 /* NSTimer+ClosureKit.swift in Sources */, 61B0386C2527247B00518F3C /* TaskInterception.swift in Sources */, 61B03892252724D900518F3C /* HTTPHeadersReader.swift in Sources */, 61122ECE25B1B74500F9C7F5 /* SpanSanitizer.swift in Sources */, @@ -4052,6 +4116,7 @@ 613E792F2577B0F900DFCC17 /* Reader.swift in Sources */, 61B0385A2527247000518F3C /* DDURLSessionDelegate.swift in Sources */, 61133BDF2423979B00786299 /* SwiftExtensions.swift in Sources */, + 61D3E0D3277B23F1008BE766 /* DNSResolver.swift in Sources */, 6149FB3A2529D17F00EE387A /* InternalURLsFilter.swift in Sources */, 611529A525E3DD51004F740E /* ValuePublisher.swift in Sources */, 618DCFD724C7265300589570 /* RUMUUID.swift in Sources */, @@ -4063,6 +4128,7 @@ 61C5A88E24509A1F00DA608C /* Tracer.swift in Sources */, 61E909EE24A24DD3005EA2DE /* OTFormat.swift in Sources */, 9E26E6B924C87693000B3270 /* RUMDataModels.swift in Sources */, + 61D3E0D6277B23F1008BE766 /* Clock.swift in Sources */, 613E793B2577B6EE00DFCC17 /* DataReader.swift in Sources */, 614B0A5324EBFE5500A2A780 /* DDRUMMonitor.swift in Sources */, 61133BE32423979B00786299 /* UserInfoProvider.swift in Sources */, @@ -4083,9 +4149,11 @@ 61C5A88A24509A0C00DA608C /* SpanFileOutput.swift in Sources */, 61C3E63524BF1794008053F2 /* Attributes.swift in Sources */, 61133BD32423979B00786299 /* File.swift in Sources */, + 61D3E0D9277B23F1008BE766 /* NTPProtocol.swift in Sources */, 61DE333625C8278A008E3EC2 /* DDCrashReportingPluginType.swift in Sources */, 6112B10225C83D4E00B37771 /* CrashReportingWithLoggingIntegration.swift in Sources */, 6161249E25CAB340009901BE /* CrashContext.swift in Sources */, + 61D3E0DA277B23F1008BE766 /* TimeFreeze.swift in Sources */, 61D447E224917F8F00649287 /* DateFormatting.swift in Sources */, 61133BE72423979B00786299 /* LogUtilityOutputs.swift in Sources */, 61133BDA2423979B00786299 /* RequestBuilder.swift in Sources */, @@ -4094,6 +4162,7 @@ 61ED39D426C2A36B002C0F26 /* DataUploadStatus.swift in Sources */, 61133BE82423979B00786299 /* LogFileOutput.swift in Sources */, 61133BD72423979B00786299 /* DataUploadWorker.swift in Sources */, + 61D3E0D4277B23F1008BE766 /* TimeStorage.swift in Sources */, 61133BD12423979B00786299 /* FilesOrchestrator.swift in Sources */, 61133BCD2423979B00786299 /* NetworkConnectionInfoProvider.swift in Sources */, 61FF282424B8A1C3000B3D9B /* RUMEventFileOutput.swift in Sources */, @@ -4118,6 +4187,7 @@ 61F3CDA72512144600C816E5 /* UIKitRUMViewsPredicate.swift in Sources */, 61133BCE2423979B00786299 /* BatteryStatusProvider.swift in Sources */, 613C6B902768FDDE00870CBF /* Sampler.swift in Sources */, + 61D3E0D8277B23F1008BE766 /* NTPClient.swift in Sources */, 9E9973F1268DF69500D8059B /* VitalInfoSampler.swift in Sources */, 9EA3CA6926775A3500B16871 /* VitalRefreshRateReader.swift in Sources */, E13A880C257922EC004FB174 /* EnvironmentSpanIntegration.swift in Sources */, @@ -4129,6 +4199,7 @@ 61C3E63B24BF1A4B008053F2 /* RUMCommand.swift in Sources */, 61133BE92423979B00786299 /* LogOutput.swift in Sources */, 61C5A88524509A0C00DA608C /* DDNoOps.swift in Sources */, + 61D3E0D7277B23F1008BE766 /* Data+Bytes.swift in Sources */, 61E5332C24B75C51003D6C4E /* RUMFeature.swift in Sources */, 61E5333D24B8791A003D6C4E /* RUMEventEncoder.swift in Sources */, 6156CB9D24E18600008CB2B2 /* TracingWithRUMIntegration.swift in Sources */, @@ -4149,6 +4220,7 @@ 61B0387D252724AB00518F3C /* URLSessionSwizzlerTests.swift in Sources */, 617CD0DD24CEDDD300B0B557 /* RUMUserActionScopeTests.swift in Sources */, D29889C9273413ED00A4D1A9 /* RUMViewsHandlerTests.swift in Sources */, + 61D3E0E3277B3D92008BE766 /* NTPClientTests.swift in Sources */, 61C5A8A024509C1100DA608C /* Casting+Tracing.swift in Sources */, 61133C662423990D00786299 /* LogSanitizerTests.swift in Sources */, 6114FE23257671F00084E372 /* ConsentAwareDataWriterTests.swift in Sources */, @@ -4173,6 +4245,7 @@ 61411B1024EC15AC0012EAB2 /* Casting+RUM.swift in Sources */, 61133C622423990D00786299 /* InternalLoggersTests.swift in Sources */, 61FF283024BC5E2D000B3D9B /* RUMEventFileOutputTests.swift in Sources */, + 61D3E0E7277B3D92008BE766 /* TimeStorageTests.swift in Sources */, 9E986C302677B91400D62490 /* VitalRefreshRateReaderTests.swift in Sources */, 61133C582423990D00786299 /* FileWriterTests.swift in Sources */, 61E917D3246546BF00E6C631 /* TracerConfigurationTests.swift in Sources */, @@ -4196,6 +4269,7 @@ 61D03BE0273404E700367DE0 /* RUMDataModels+objcTests.swift in Sources */, 615C3196251DD5080018781C /* UIKitRUMUserActionsHandlerTests.swift in Sources */, 61E917CF2464270500E6C631 /* CodableValueTests.swift in Sources */, + 61D3E0E4277B3D92008BE766 /* NTPPacketTests.swift in Sources */, 61133C542423990D00786299 /* NetworkConnectionInfoProviderTests.swift in Sources */, 616B668E259CC28E00968EE8 /* DDRUMMonitorTests.swift in Sources */, B3BBBCBC265E71D100943419 /* VitalMemoryReaderTests.swift in Sources */, @@ -4278,8 +4352,10 @@ 611F82032563C66100CB9BDB /* UIKitRUMViewsPredicateTests.swift in Sources */, 61133C652423990D00786299 /* LogBuilderTests.swift in Sources */, 61B5E42B26DFC433000B0A5F /* DDNSURLSessionDelegate+apiTests.m in Sources */, + 61D3E0E5277B3D92008BE766 /* ClockTests.swift in Sources */, 61F8CC092469295500FE2908 /* DatadogConfigurationBuilderTests.swift in Sources */, 61F1A623249B811200075390 /* Encoding.swift in Sources */, + 61D3E0E6277B3D92008BE766 /* DNSResolverTests.swift in Sources */, 6114FE3B25768AA90084E372 /* ConsentProviderTests.swift in Sources */, 61133C642423990D00786299 /* LoggerTests.swift in Sources */, 617B953D24BF4D8F00E6F443 /* RUMMonitorTests.swift in Sources */, diff --git a/Sources/Datadog/Kronos/Clock.swift b/Sources/Datadog/Kronos/Clock.swift new file mode 100644 index 0000000000..fade988687 --- /dev/null +++ b/Sources/Datadog/Kronos/Clock.swift @@ -0,0 +1,102 @@ +import Foundation + +/// Struct that has time + related metadata +public typealias AnnotatedTime = ( + + /// Time that is being annotated + date: Date, + + /// Amount of time that has passed since the last NTP sync; in other words, the NTP response age. + timeSinceLastNtpSync: TimeInterval +) + +/// High level implementation for clock synchronization using NTP. All returned dates use the most accurate +/// synchronization and it's not affected by clock changes. The NTP synchronization implementation has sub- +/// second accuracy but given that Darwin doesn't support microseconds on bootTime, dates don't have sub- +/// second accuracy. +/// +/// Example usage: +/// +/// ```swift +/// Clock.sync { date, offset in +/// print(date) +/// } +/// // (... later on ...) +/// print(Clock.now) +/// ``` +public struct Clock { + private static var stableTime: TimeFreeze? { + didSet { + self.storage.stableTime = self.stableTime + } + } + + /// Determines where the most current stable time is stored. Use TimeStoragePolicy.appGroup to share + /// between your app and an extension. + public static var storage = TimeStorage(storagePolicy: .standard) + + /// The most accurate timestamp that we have so far (nil if no synchronization was done yet) + public static var timestamp: TimeInterval? { + return self.stableTime?.adjustedTimestamp + } + + /// The most accurate date that we have so far (nil if no synchronization was done yet) + public static var now: Date? { + return self.annotatedNow?.date + } + + /// Same as `now` except with analytic metadata about the time + public static var annotatedNow: AnnotatedTime? { + guard let stableTime = self.stableTime else { + return nil + } + + return AnnotatedTime(date: Date(timeIntervalSince1970: stableTime.adjustedTimestamp), + timeSinceLastNtpSync: stableTime.timeSinceLastNtpSync) + } + + /// Syncs the clock using NTP. Note that the full synchronization could take a few seconds. The given + /// closure will be called with the first valid NTP response which accuracy should be good enough for the + /// initial clock adjustment but it might not be the most accurate representation. After calling the + /// closure this method will continue syncing with multiple servers and multiple passes. + /// + /// - parameter pool: NTP pool that will be resolved into multiple NTP servers that will be used for + /// the synchronization. + /// - parameter samples: The number of samples to be acquired from each server (default 4). + /// - parameter completion: A closure that will be called after _all_ the NTP calls are finished. + /// - parameter first: A closure that will be called after the first valid date is calculated. + public static func sync(from pool: String = "time.apple.com", samples: Int = 4, + first: ((Date, TimeInterval) -> Void)? = nil, + completion: ((Date?, TimeInterval?) -> Void)? = nil) + { + self.loadFromDefaults() + + NTPClient().query(pool: pool, numberOfSamples: samples) { offset, done, total in + if let offset = offset { + self.stableTime = TimeFreeze(offset: offset) + + if done == 1, let now = self.now { + first?(now, offset) + } + } + + if done == total { + completion?(self.now, offset) + } + } + } + + /// Resets all state of the monotonic clock. Note that you won't be able to access `now` until you `sync` + /// again. + public static func reset() { + self.stableTime = nil + } + + private static func loadFromDefaults() { + guard let previousStableTime = self.storage.stableTime else { + self.stableTime = nil + return + } + self.stableTime = previousStableTime + } +} diff --git a/Sources/Datadog/Kronos/DNSResolver.swift b/Sources/Datadog/Kronos/DNSResolver.swift new file mode 100644 index 0000000000..d0c2b9fa7a --- /dev/null +++ b/Sources/Datadog/Kronos/DNSResolver.swift @@ -0,0 +1,81 @@ +import Foundation + +private let kCopyNoOperation = unsafeBitCast(0, to: CFAllocatorCopyDescriptionCallBack.self) +private let kDefaultTimeout = 8.0 + +final class DNSResolver { + private var completion: (([InternetAddress]) -> Void)? + private var timer: Timer? + + private init() {} + + /// Performs DNS lookups and calls the given completion with the answers that are returned from the name + /// server(s) that were queried. + /// + /// - parameter host: The host to be looked up. + /// - parameter timeout: The connection timeout. + /// - parameter completion: A completion block that will be called both on failure and success with a list + /// of IPs. + static func resolve(host: String, timeout: TimeInterval = kDefaultTimeout, + completion: @escaping ([InternetAddress]) -> Void) + { + let callback: CFHostClientCallBack = { host, _, _, info in + guard let info = info else { + return + } + let retainedSelf = Unmanaged.fromOpaque(info) + let resolver = retainedSelf.takeUnretainedValue() + resolver.timer?.invalidate() + resolver.timer = nil + + var resolved: DarwinBoolean = false + guard let addresses = CFHostGetAddressing(host, &resolved), resolved.boolValue else { + resolver.completion?([]) + retainedSelf.release() + return + } + + let IPs = (addresses.takeUnretainedValue() as NSArray) + .compactMap { $0 as? NSData } + .compactMap(InternetAddress.init) + + resolver.completion?(IPs) + retainedSelf.release() + } + + let resolver = DNSResolver() + resolver.completion = completion + + let retainedClosure = Unmanaged.passRetained(resolver).toOpaque() + var clientContext = CFHostClientContext(version: 0, info: UnsafeMutableRawPointer(retainedClosure), + retain: nil, release: nil, copyDescription: kCopyNoOperation) + + let hostReference = CFHostCreateWithName(kCFAllocatorDefault, host as CFString).takeUnretainedValue() + resolver.timer = Timer.scheduledTimer(timeInterval: timeout, target: resolver, + selector: #selector(DNSResolver.onTimeout), + userInfo: hostReference, repeats: false) + + CFHostSetClient(hostReference, callback, &clientContext) + CFHostScheduleWithRunLoop(hostReference, CFRunLoopGetMain(), CFRunLoopMode.commonModes.rawValue) + CFHostStartInfoResolution(hostReference, .addresses, nil) + } + + @objc + private func onTimeout() { + defer { + self.completion?([]) + + // Manually release the previously retained self. + Unmanaged.passUnretained(self).release() + } + + guard let userInfo = self.timer?.userInfo else { + return + } + + let hostReference = unsafeBitCast(userInfo as AnyObject, to: CFHost.self) + CFHostCancelInfoResolution(hostReference, .addresses) + CFHostUnscheduleFromRunLoop(hostReference, CFRunLoopGetMain(), CFRunLoopMode.commonModes.rawValue) + CFHostSetClient(hostReference, nil, nil) + } +} diff --git a/Sources/Datadog/Kronos/Data+Bytes.swift b/Sources/Datadog/Kronos/Data+Bytes.swift new file mode 100644 index 0000000000..af6c64ca57 --- /dev/null +++ b/Sources/Datadog/Kronos/Data+Bytes.swift @@ -0,0 +1,91 @@ +import Foundation + +extension Data { + + /// Creates an Data instance based on a hex string (example: "ffff" would be ). + /// + /// - parameter hex: The hex string without any spaces; should only have [0-9A-Fa-f]. + init?(hex: String) { + if hex.count % 2 != 0 { + return nil + } + + let hexArray = Array(hex) + var bytes: [UInt8] = [] + + for index in stride(from: 0, to: hexArray.count, by: 2) { + guard let byte = UInt8("\(hexArray[index])\(hexArray[index + 1])", radix: 16) else { + return nil + } + + bytes.append(byte) + } + + self.init(bytes: bytes, count: bytes.count) + } + + /// Gets one byte from the given index. + /// + /// - parameter index: The index of the byte to be retrieved. Note that this should never be >= length. + /// + /// - returns: The byte located at position `index`. + func getByte(at index: Int) -> Int8 { + let data: Int8 = self.subdata(in: index ..< (index + 1)).withUnsafeBytes { rawPointer in + rawPointer.bindMemory(to: Int8.self).baseAddress!.pointee + } + + return data + } + + /// Gets an unsigned int (32 bits => 4 bytes) from the given index. + /// + /// - parameter index: The index of the uint to be retrieved. Note that this should never be >= length - + /// 3. + /// + /// - returns: The unsigned int located at position `index`. + func getUnsignedInteger(at index: Int, bigEndian: Bool = true) -> UInt32 { + let data: UInt32 = self.subdata(in: index ..< (index + 4)).withUnsafeBytes { rawPointer in + rawPointer.bindMemory(to: UInt32.self).baseAddress!.pointee + } + + return bigEndian ? data.bigEndian : data.littleEndian + } + + /// Gets an unsigned long integer (64 bits => 8 bytes) from the given index. + /// + /// - parameter index: The index of the ulong to be retrieved. Note that this should never be >= length - + /// 7. + /// + /// - returns: The unsigned long integer located at position `index`. + func getUnsignedLong(at index: Int, bigEndian: Bool = true) -> UInt64 { + let data: UInt64 = self.subdata(in: index ..< (index + 8)).withUnsafeBytes { rawPointer in + rawPointer.bindMemory(to: UInt64.self).baseAddress!.pointee + } + + return bigEndian ? data.bigEndian : data.littleEndian + } + + /// Appends the given byte (8 bits) into the receiver Data. + /// + /// - parameter data: The byte to be appended. + mutating func append(byte data: Int8) { + var data = data + self.append(Data(bytes: &data, count: MemoryLayout.size)) + } + + /// Appends the given unsigned integer (32 bits; 4 bytes) into the receiver Data. + /// + /// - parameter data: The unsigned integer to be appended. + mutating func append(unsignedInteger data: UInt32, bigEndian: Bool = true) { + var data = bigEndian ? data.bigEndian : data.littleEndian + self.append(Data(bytes: &data, count: MemoryLayout.size)) + } + + /// Appends the given unsigned long (64 bits; 8 bytes) into the receiver Data. + /// + /// - parameter data: The unsigned long to be appended. + mutating func append(unsignedLong data: UInt64, bigEndian: Bool = true) { + var data = bigEndian ? data.bigEndian : data.littleEndian + self.append(Data(bytes: &data, count: MemoryLayout.size)) + } +} diff --git a/Sources/Datadog/Kronos/InternetAddress.swift b/Sources/Datadog/Kronos/InternetAddress.swift new file mode 100644 index 0000000000..e5a29c3b71 --- /dev/null +++ b/Sources/Datadog/Kronos/InternetAddress.swift @@ -0,0 +1,108 @@ +import Foundation + +/// This enum represents an internet address that can either be IPv4 or IPv6. +/// +/// - IPv6: An Internet Address of type IPv6 (e.g.: '::1'). +/// - IPv4: An Internet Address of type IPv4 (e.g.: '127.0.0.1'). +enum InternetAddress: Hashable { + case ipv6(sockaddr_in6) + case ipv4(sockaddr_in) + + /// Human readable host represetnation (e.g. '192.168.1.1' or 'ab:ab:ab:ab:ab:ab:ab:ab'). + var host: String? { + switch self { + case .ipv6(var address): + var buffer = [CChar](repeating: 0, count: Int(INET6_ADDRSTRLEN)) + inet_ntop(AF_INET6, &address.sin6_addr, &buffer, socklen_t(INET6_ADDRSTRLEN)) + return String(cString: buffer) + + case .ipv4(var address): + var buffer = [CChar](repeating: 0, count: Int(INET_ADDRSTRLEN)) + inet_ntop(AF_INET, &address.sin_addr, &buffer, socklen_t(INET_ADDRSTRLEN)) + return String(cString: buffer) + } + } + + /// The protocol family that should be used on the socket creation for this address. + var family: Int32 { + switch self { + case .ipv4: + return PF_INET + + case .ipv6: + return PF_INET6 + } + } + + func hash(into hasher: inout Hasher) { + hasher.combine(self.host) + } + + init?(dataWithSockAddress data: NSData) { + let storage = sockaddr_storage.from(unsafeDataWithSockAddress: data) + switch Int32(storage.ss_family) { + case AF_INET: + self = storage.withUnsafeAddress { InternetAddress.ipv4($0.pointee) } + + case AF_INET6: + self = storage.withUnsafeAddress { InternetAddress.ipv6($0.pointee) } + + default: + return nil + } + } + + /// Returns the address struct (either sockaddr_in or sockaddr_in6) represented as an CFData. + /// + /// - parameter port: The port number to associate on the address struct. + /// + /// - returns: An address struct wrapped into a CFData type. + func addressData(withPort port: Int) -> CFData { + switch self { + case .ipv6(var address): + address.sin6_port = in_port_t(port).bigEndian + return Data(bytes: &address, count: MemoryLayout.size) as CFData + + case .ipv4(var address): + address.sin_port = in_port_t(port).bigEndian + return Data(bytes: &address, count: MemoryLayout.size) as CFData + } + } +} + +/// Compare InternetAddress(es) by making sure the host representation are equal. +func == (lhs: InternetAddress, rhs: InternetAddress) -> Bool { + return lhs.host == rhs.host +} + +// MARK: - sockaddr_storage helpers + +extension sockaddr_storage { + /// Creates a new storage value from a data type that contains the memory layout of a sockaddr_t. This + /// is used to create sockaddr_storage(s) from some of the CF C functions such as `CFHostGetAddressing`. + /// + /// !!! WARNING: This method is unsafe and assumes the memory layout is of `sockaddr_t`. !!! + /// + /// - parameter data: The data to be interpreted as sockaddr + /// - returns: The newly created sockaddr_storage value + fileprivate static func from(unsafeDataWithSockAddress data: NSData) -> sockaddr_storage { + var storage = sockaddr_storage() + data.getBytes(&storage, length: data.length) + return storage + } + + /// Calls a closure with traditional BSD Sockets address parameters. + /// + /// - parameter body: A closure to call with `self` referenced appropriately for calling + /// BSD Sockets APIs that take an address. + /// + /// - throws: Any error thrown by `body`. + /// + /// - returns: Any result returned by `body`. + fileprivate func withUnsafeAddress(_ body: (_ address: UnsafePointer) -> T) -> T { + var storage = self + return withUnsafePointer(to: &storage) { + $0.withMemoryRebound(to: U.self, capacity: 1) { address in body(address) } + } + } +} diff --git a/Sources/Datadog/Kronos/NSTimer+ClosureKit.swift b/Sources/Datadog/Kronos/NSTimer+ClosureKit.swift new file mode 100644 index 0000000000..9c5d4c64af --- /dev/null +++ b/Sources/Datadog/Kronos/NSTimer+ClosureKit.swift @@ -0,0 +1,52 @@ +import Foundation + +typealias CKTimerHandler = (Timer) -> Void + +/// Simple closure implementation on NSTimer scheduling. +/// +/// Example: +/// +/// ```swift +/// BlockTimer.scheduledTimer(withTimeInterval: 1.0) { timer in +/// print("Did something after 1s!") +/// } +/// ``` +final class BlockTimer: NSObject { + + /// Creates and returns a block-based NSTimer object and schedules it on the current run loop. + /// + /// - parameter interval: The number of seconds between firings of the timer. + /// - parameter repeated: If true, the timer will repeatedly reschedule itself until invalidated. If + /// false, the timer will be invalidated after it fires. + /// - parameter handler: The closure that the NSTimer fires. + /// + /// - returns: A new NSTimer object, configured according to the specified parameters. + class func scheduledTimer(withTimeInterval interval: TimeInterval, repeated: Bool = false, + handler: @escaping CKTimerHandler) -> Timer + { + return Timer.scheduledTimer(timeInterval: interval, target: self, + selector: #selector(BlockTimer.invokeFrom(timer:)), + userInfo: TimerClosureWrapper(handler: handler, repeats: repeated), repeats: repeated) + } + + // MARK: Private methods + + @objc + class private func invokeFrom(timer: Timer) { + if let closureWrapper = timer.userInfo as? TimerClosureWrapper { + closureWrapper.handler(timer) + } + } +} + +// MARK: - Private classes + +private final class TimerClosureWrapper { + fileprivate var handler: CKTimerHandler + private var repeats: Bool + + init(handler: @escaping CKTimerHandler, repeats: Bool) { + self.handler = handler + self.repeats = repeats + } +} diff --git a/Sources/Datadog/Kronos/NTPClient.swift b/Sources/Datadog/Kronos/NTPClient.swift new file mode 100644 index 0000000000..322146eaf6 --- /dev/null +++ b/Sources/Datadog/Kronos/NTPClient.swift @@ -0,0 +1,189 @@ +import Foundation + +private let kDefaultTimeout = 6.0 +private let kDefaultSamples = 4 +private let kMaximumNTPServers = 5 +private let kMaximumResultDispersion = 10.0 + +private typealias ObjCCompletionType = @convention(block) (Data?, TimeInterval) -> Void + +/// Exception raised while sending / receiving NTP packets. +enum NTPNetworkError: Error { + case noValidNTPPacketFound +} + +/// NTP client session. +final class NTPClient { + + /// Query the all ips that resolve from the given pool. + /// + /// - parameter pool: NTP pool that will be resolved into multiple NTP servers. + /// - parameter port: Server NTP port (default 123). + /// - parameter version: NTP version to use (default 3). + /// - parameter numberOfSamples: The number of samples to be acquired from each server (default 4). + /// - parameter maximumServers: The maximum number of servers to be queried (default 5). + /// - parameter timeout: The individual timeout for each of the NTP operations. + /// - parameter completion: A closure that will be response PDU on success or nil on error. + func query(pool: String = "time.apple.com", version: Int8 = 3, port: Int = 123, + numberOfSamples: Int = kDefaultSamples, maximumServers: Int = kMaximumNTPServers, + timeout: CFTimeInterval = kDefaultTimeout, + progress: @escaping (TimeInterval?, Int, Int) -> Void) + { + var servers: [InternetAddress: [NTPPacket]] = [:] + var completed: Int = 0 + + let queryIPAndStoreResult = { (address: InternetAddress, totalQueries: Int) -> Void in + self.query(ip: address, port: port, version: version, timeout: timeout, + numberOfSamples: numberOfSamples) + { packet in + defer { + completed += 1 + + let responses = Array(servers.values) + progress(try? self.offset(from: responses), completed, totalQueries) + } + + guard let PDU = packet else { + return + } + + if servers[address] == nil { + servers[address] = [] + } + + servers[address]?.append(PDU) + } + } + + DNSResolver.resolve(host: pool) { addresses in + if addresses.count == 0 { + return progress(nil, 0, 0) + } + + let totalServers = min(addresses.count, maximumServers) + for address in addresses[0 ..< totalServers] { + queryIPAndStoreResult(address, totalServers * numberOfSamples) + } + } + } + + /// Query the given NTP server for the time exchange. + /// + /// - parameter ip: Server socket address. + /// - parameter port: Server NTP port (default 123). + /// - parameter version: NTP version to use (default 3). + /// - parameter timeout: Timeout on socket operations. + /// - parameter numberOfSamples: The number of samples to be acquired from the server (default 4). + /// - parameter completion: A closure that will be response PDU on success or nil on error. + func query(ip: InternetAddress, port: Int = 123, version: Int8 = 3, + timeout: CFTimeInterval = kDefaultTimeout, numberOfSamples: Int = kDefaultSamples, + completion: @escaping (NTPPacket?) -> Void) + { + var timer: Timer? + let bridgeCallback: ObjCCompletionType = { data, destinationTime in + defer { + // If we still have samples left; we'll keep querying the same server + if numberOfSamples > 1 { + self.query(ip: ip, port: port, version: version, timeout: timeout, + numberOfSamples: numberOfSamples - 1, completion: completion) + } + } + + timer?.invalidate() + guard + let data = data, let PDU = try? NTPPacket(data: data, destinationTime: destinationTime), + PDU.isValidResponse() else + { + completion(nil) + return + } + + completion(PDU) + } + + let callback = unsafeBitCast(bridgeCallback, to: AnyObject.self) + let retainedCallback = Unmanaged.passRetained(callback) + let sourceAndSocket = self.sendAsyncUDPQuery( + to: ip, port: port, timeout: timeout, + completion: UnsafeMutableRawPointer(retainedCallback.toOpaque()) + ) + + timer = BlockTimer.scheduledTimer(withTimeInterval: timeout, repeated: true) { _ in + bridgeCallback(nil, TimeInterval.infinity) + retainedCallback.release() + + if let (source, socket) = sourceAndSocket { + CFSocketInvalidate(socket) + CFRunLoopRemoveSource(CFRunLoopGetMain(), source, CFRunLoopMode.commonModes) + } + } + } + + // MARK: - Private helpers (NTP Calculation) + + private func offset(from responses: [[NTPPacket]]) throws -> TimeInterval { + let now = currentTime() + var bestResponses: [NTPPacket] = [] + for serverResponses in responses { + let filtered = serverResponses + .filter { abs($0.originTime - now) < kMaximumResultDispersion } + .min { $0.delay < $1.delay } + + if let filtered = filtered { + bestResponses.append(filtered) + } + } + + if bestResponses.count == 0 { + throw NTPNetworkError.noValidNTPPacketFound + } + + bestResponses.sort { $0.offset < $1.offset } + return bestResponses[bestResponses.count / 2].offset + } + + // MARK: - Private helpers (CFSocket) + + private func sendAsyncUDPQuery(to ip: InternetAddress, port: Int, timeout: TimeInterval, + completion: UnsafeMutableRawPointer) -> (CFRunLoopSource, CFSocket)? + { + signal(SIGPIPE, SIG_IGN) + + let callback: CFSocketCallBack = { socket, callbackType, _, data, info in + if callbackType == .writeCallBack { + var packet = NTPPacket() + let PDU = packet.prepareToSend() as CFData + CFSocketSendData(socket, nil, PDU, kDefaultTimeout) + return + } + + guard let info = info else { + return + } + + CFSocketInvalidate(socket) + + let destinationTime = currentTime() + let retainedClosure = Unmanaged.fromOpaque(info) + let completion = unsafeBitCast(retainedClosure.takeUnretainedValue(), to: ObjCCompletionType.self) + + let data = unsafeBitCast(data, to: CFData.self) as Data? + completion(data, destinationTime) + retainedClosure.release() + } + + let types = CFSocketCallBackType.dataCallBack.rawValue | CFSocketCallBackType.writeCallBack.rawValue + var context = CFSocketContext(version: 0, info: completion, retain: nil, release: nil, + copyDescription: nil) + guard let socket = CFSocketCreate(nil, ip.family, SOCK_DGRAM, IPPROTO_UDP, types, callback, &context), + CFSocketIsValid(socket) else + { + return nil + } + + let runLoopSource = CFSocketCreateRunLoopSource(kCFAllocatorDefault, socket, 0) + CFRunLoopAddSource(CFRunLoopGetMain(), runLoopSource, CFRunLoopMode.commonModes) + CFSocketConnectToAddress(socket, ip.addressData(withPort: port), timeout) + return (runLoopSource!, socket) + } +} diff --git a/Sources/Datadog/Kronos/NTPPacket.swift b/Sources/Datadog/Kronos/NTPPacket.swift new file mode 100644 index 0000000000..ea7fdcb392 --- /dev/null +++ b/Sources/Datadog/Kronos/NTPPacket.swift @@ -0,0 +1,200 @@ +import Foundation + +/// Delta between system and NTP time +private let kEpochDelta = 2208988800.0 + +/// This is the maximum that we'll tolerate for the client's time vs self.delay +private let kMaximumDelayDifference = 0.1 +private let kMaximumDispersion = 100.0 + +/// Returns the current time in decimal EPOCH timestamp format. +/// +/// - returns: The current time in EPOCH timestamp format. +func currentTime() -> TimeInterval { + var current = timeval() + let systemTimeError = gettimeofday(¤t, nil) != 0 + assert(!systemTimeError, "system clock error: system time unavailable") + + return Double(current.tv_sec) + Double(current.tv_usec) / 1_000_000 +} + +struct NTPPacket { + + /// The leap indicator warning of an impending leap second to be inserted or deleted in the last + /// minute of the current month. + let leap: LeapIndicator + + /// Version Number (VN): This is a three-bit integer indicating the NTP version number, currently 3. + let version: Int8 + + /// The current connection mode. + let mode: Mode + + /// Mode representing the stratum level of the local clock. + let stratum: Stratum + + /// Indicates the maximum interval between successive messages, in seconds to the nearest power of two. + /// The values that normally appear in this field range from 6 to 10, inclusive. + let poll: Int8 + + /// The precision of the local clock, in seconds to the nearest power of two. The values that normally + /// appear in this field range from -6 for mains-frequency clocks to -18 for microsecond clocks found + /// in some workstations. + let precision: Int8 + + /// The total roundtrip delay to the primary reference source, in seconds with fraction point between + /// bits 15 and 16. Note that this variable can take on both positive and negative values, depending on + /// the relative time and frequency errors. The values that normally appear in this field range from + /// negative values of a few milliseconds to positive values of several hundred milliseconds. + let rootDelay: TimeInterval + + /// Total dispersion to the reference clock, in EPOCH. + let rootDispersion: TimeInterval + + /// Server or reference clock. This value is generated based on a reference identifier maintained by IANA. + let clockSource: ClockSource + + /// Time when the system clock was last set or corrected, in EPOCH timestamp format. + let referenceTime: TimeInterval + + /// Time at the client when the request departed for the server, in EPOCH timestamp format. + let originTime: TimeInterval + + /// Time at the server when the request arrived from the client, in EPOCH timestamp format. + let receiveTime: TimeInterval + + /// Time at the server when the response left for the client, in EPOCH timestamp format. + var transmitTime: TimeInterval = 0.0 + + /// Time at the client when the response arrived, in EPOCH timestamp format. + let destinationTime: TimeInterval + + /// NTP protocol package representation. + /// + /// - parameter transmitTime: Packet transmission timestamp. + /// - parameter version: NTP protocol version. + /// - parameter mode: Packet mode (client, server). + init(version: Int8 = 3, mode: Mode = .client) { + self.version = version + self.leap = .noWarning + self.mode = mode + self.stratum = .unspecified + self.poll = 4 + self.precision = -6 + self.rootDelay = 1 + self.rootDispersion = 1 + self.clockSource = .referenceIdentifier(id: 0) + self.referenceTime = -kEpochDelta + self.originTime = -kEpochDelta + self.receiveTime = -kEpochDelta + self.destinationTime = -1 + } + + /// Creates a NTP package based on a network PDU. + /// + /// - parameter data: The PDU received from the NTP call. + /// - parameter destinationTime: The time where the package arrived (client time) in EPOCH format. + /// - throws: NTPParsingError in case of an invalid response. + init(data: Data, destinationTime: TimeInterval) throws { + if data.count < 48 { + throw NTPParsingError.invalidNTPPDU("Invalid PDU length: \(data.count)") + } + + self.leap = LeapIndicator(rawValue: (data.getByte(at: 0) >> 6) & 0b11) ?? .noWarning + self.version = data.getByte(at: 0) >> 3 & 0b111 + self.mode = Mode(rawValue: data.getByte(at: 0) & 0b111) ?? .unknown + self.stratum = Stratum(value: data.getByte(at: 1)) + self.poll = data.getByte(at: 2) + self.precision = data.getByte(at: 3) + self.rootDelay = NTPPacket.intervalFromNTPFormat(data.getUnsignedInteger(at: 4)) + self.rootDispersion = NTPPacket.intervalFromNTPFormat(data.getUnsignedInteger(at: 8)) + self.clockSource = ClockSource(stratum: self.stratum, sourceID: data.getUnsignedInteger(at: 12)) + self.referenceTime = NTPPacket.dateFromNTPFormat(data.getUnsignedLong(at: 16)) + self.originTime = NTPPacket.dateFromNTPFormat(data.getUnsignedLong(at: 24)) + self.receiveTime = NTPPacket.dateFromNTPFormat(data.getUnsignedLong(at: 32)) + self.transmitTime = NTPPacket.dateFromNTPFormat(data.getUnsignedLong(at: 40)) + self.destinationTime = destinationTime + } + + /// Convert this NTPPacket to a buffer that can be sent over a socket. + /// + /// - returns: A bytes buffer representing this packet. + mutating func prepareToSend(transmitTime: TimeInterval? = nil) -> Data { + var data = Data() + data.append(byte: self.leap.rawValue << 6 | self.version << 3 | self.mode.rawValue) + data.append(byte: self.stratum.rawValue) + data.append(byte: self.poll) + data.append(byte: self.precision) + data.append(unsignedInteger: self.intervalToNTPFormat(self.rootDelay)) + data.append(unsignedInteger: self.intervalToNTPFormat(self.rootDispersion)) + data.append(unsignedInteger: self.clockSource.ID) + data.append(unsignedLong: self.dateToNTPFormat(self.referenceTime)) + data.append(unsignedLong: self.dateToNTPFormat(self.originTime)) + data.append(unsignedLong: self.dateToNTPFormat(self.receiveTime)) + + self.transmitTime = transmitTime ?? currentTime() + data.append(unsignedLong: self.dateToNTPFormat(self.transmitTime)) + return data + } + + /// Checks properties to make sure that the received PDU is a valid response that we can use. + /// + /// - returns: A boolean indicating if the response is valid for the given version. + func isValidResponse() -> Bool { + return (self.mode == .server || self.mode == .symmetricPassive) && self.leap != .alarm + && self.stratum != .invalid && self.stratum != .unspecified + && self.rootDispersion < kMaximumDispersion + && abs(currentTime() - self.originTime - self.delay) < kMaximumDelayDifference + } + + // MARK: - Private helpers + + private func dateToNTPFormat(_ time: TimeInterval) -> UInt64 { + let integer = UInt32(time + kEpochDelta) + let decimal = modf(time).1 * 4294967296.0 // 2 ^ 32 + return UInt64(integer) << 32 | UInt64(decimal) + } + + private func intervalToNTPFormat(_ time: TimeInterval) -> UInt32 { + let integer = UInt16(time) + let decimal = modf(time).1 * 65536 // 2 ^ 16 + return UInt32(integer) << 16 | UInt32(decimal) + } + + private static func dateFromNTPFormat(_ time: UInt64) -> TimeInterval { + let integer = Double(time >> 32) + let decimal = Double(time & 0xffffffff) / 4294967296.0 + return integer - kEpochDelta + decimal + } + + private static func intervalFromNTPFormat(_ time: UInt32) -> TimeInterval { + let integer = Double(time >> 16) + let decimal = Double(time & 0xffff) / 65536 + return integer + decimal + } +} + +/// From RFC 2030 (with a correction to the delay math): +/// +/// Timestamp Name ID When Generated +/// ------------------------------------------------------------ +/// Originate Timestamp T1 time request sent by client +/// Receive Timestamp T2 time request received by server +/// Transmit Timestamp T3 time reply sent by server +/// Destination Timestamp T4 time reply received by client +/// +/// The roundtrip delay d and local clock offset t are defined as +/// +/// d = (T4 - T1) - (T3 - T2) t = ((T2 - T1) + (T3 - T4)) / 2. +extension NTPPacket { + + /// Clocks offset in seconds. + var offset: TimeInterval { + return ((self.receiveTime - self.originTime) + (self.transmitTime - self.destinationTime)) / 2.0 + } + + /// Round-trip delay in seconds + var delay: TimeInterval { + return (self.destinationTime - self.originTime) - (self.transmitTime - self.receiveTime) + } +} diff --git a/Sources/Datadog/Kronos/NTPProtocol.swift b/Sources/Datadog/Kronos/NTPProtocol.swift new file mode 100644 index 0000000000..c727012eea --- /dev/null +++ b/Sources/Datadog/Kronos/NTPProtocol.swift @@ -0,0 +1,128 @@ +import Foundation + +/// Exception raised when the received PDU is invalid. +enum NTPParsingError: Error { + case invalidNTPPDU(String) +} + +/// The leap indicator warning of an impending leap second to be inserted or deleted in the last minute of the +/// current month. +enum LeapIndicator: Int8 { + case noWarning, sixtyOneSeconds, fiftyNineSeconds, alarm + + /// Human readable value of the leap warning. + var description: String { + switch self { + case .noWarning: + return "No warning" + case .sixtyOneSeconds: + return "Last minute of the day has 61 seconds" + case .fiftyNineSeconds: + return "Last minute of the day has 59 seconds" + case .alarm: + return "Unknown (clock unsynchronized)" + } + } +} + +/// The connection mode. +enum Mode: Int8 { + case reserved, symmetricActive, symmetricPassive, client, server, broadcast, reservedNTP, unknown +} + +/// Mode representing the stratum level of the clock. +enum Stratum: Int8 { + case unspecified, primary, secondary, invalid + + init(value: Int8) { + switch value { + case 0: + self = .unspecified + + case 1: + self = .primary + + case 0 ..< 15: + self = .secondary + + default: + self = .invalid + } + } +} + +/// Server or reference clock. This value is generated based on the server stratum. +/// +/// - ReferenceClock: Contains the sourceID and the description for the reference clock (stratum 1). +/// - Debug(id): Contains the kiss code for debug purposes (stratum 0). +/// - ReferenceIdentifier(id): The reference identifier of the server (stratum > 1). +enum ClockSource { + case referenceClock(id: UInt32, description: String) + case debug(id: UInt32) + case referenceIdentifier(id: UInt32) + + init(stratum: Stratum, sourceID: UInt32) { + switch stratum { + case .unspecified: + self = .debug(id: sourceID) + + case .primary: + let (id, description) = ClockSource.description(fromID: sourceID) + self = .referenceClock(id: id, description: description) + + case .secondary, .invalid: + self = .referenceIdentifier(id: sourceID) + } + } + + /// The id for the reference clock (IANA, stratum 1), debug (stratum 0) or referenceIdentifier + var ID: UInt32 { + switch self { + case .referenceClock(let id, _): + return id + + case .debug(let id): + return id + + case .referenceIdentifier(let id): + return id + } + } + + private static func description(fromID sourceID: UInt32) -> (UInt32, String) { + let sourceMap: [UInt32: String] = [ + 0x47505300: "Global Position System", + 0x47414c00: "Galileo Positioning System", + 0x50505300: "Generic pulse-per-second", + 0x49524947: "Inter-Range Instrumentation Group", + 0x57575642: "LF Radio WWVB Ft. Collins, CO 60 kHz", + 0x44434600: "LF Radio DCF77 Mainflingen, DE 77.5 kHz", + 0x48424700: "LF Radio HBG Prangins, HB 75 kHz", + 0x4d534600: "LF Radio MSF Anthorn, UK 60 kHz", + 0x4a4a5900: "LF Radio JJY Fukushima, JP 40 kHz, Saga, JP 60 kHz", + 0x4c4f5243: "MF Radio LORAN C station, 100 kHz", + 0x54444600: "MF Radio Allouis, FR 162 kHz", + 0x43485500: "HF Radio CHU Ottawa, Ontario", + 0x57575600: "HF Radio WWV Ft. Collins, CO", + 0x57575648: "HF Radio WWVH Kauai, HI", + 0x4e495354: "NIST telephone modem", + 0x41435453: "ACTS telephone modem", + 0x55534e4f: "USNO telephone modem", + 0x50544200: "European telephone modem", + 0x4c4f434c: "Uncalibrated local clock", + 0x4345534d: "Calibrated Cesium clock", + 0x5242444d: "Calibrated Rubidium clock", + 0x4f4d4547: "OMEGA radio navigation system", + 0x44434e00: "DCN routing protocol", + 0x54535000: "TSP time protocol", + 0x44545300: "Digital Time Service", + 0x41544f4d: "Atomic clock (calibrated)", + 0x564c4600: "VLF radio (OMEGA,, etc.)", + 0x31505053: "External 1 PPS input", + 0x46524545: "(Internal clock)", + 0x494e4954: "(Initialization)", + ] + + return (sourceID, sourceMap[sourceID] ?? "NULL") + } +} diff --git a/Sources/Datadog/Kronos/TimeFreeze.swift b/Sources/Datadog/Kronos/TimeFreeze.swift new file mode 100644 index 0000000000..81ce763957 --- /dev/null +++ b/Sources/Datadog/Kronos/TimeFreeze.swift @@ -0,0 +1,87 @@ +import Foundation + +private let kUptimeKey = "Uptime" +private let kTimestampKey = "Timestamp" +private let kOffsetKey = "Offset" + +struct TimeFreeze { + private let uptime: TimeInterval + private let timestamp: TimeInterval + private let offset: TimeInterval + + /// The stable timestamp adjusted by the most accurate offset known so far. + var adjustedTimestamp: TimeInterval { + return self.offset + self.stableTimestamp + } + + /// The stable timestamp (calculated based on the uptime); note that this doesn't have sub-seconds + /// precision. See `systemUptime()` for more information. + var stableTimestamp: TimeInterval { + return (TimeFreeze.systemUptime() - self.uptime) + self.timestamp + } + + /// Time interval between now and the time the NTP response represented by this TimeFreeze was received. + var timeSinceLastNtpSync: TimeInterval { + return TimeFreeze.systemUptime() - uptime + } + + init(offset: TimeInterval) { + self.offset = offset + self.timestamp = currentTime() + self.uptime = TimeFreeze.systemUptime() + } + + init?(from dictionary: [String: TimeInterval]) { + guard let uptime = dictionary[kUptimeKey], let timestamp = dictionary[kTimestampKey], + let offset = dictionary[kOffsetKey] else + { + return nil + } + + let currentUptime = TimeFreeze.systemUptime() + let currentTimestamp = currentTime() + let currentBoot = currentUptime - currentTimestamp + let previousBoot = uptime - timestamp + if rint(currentBoot) - rint(previousBoot) != 0 { + return nil + } + + self.uptime = uptime + self.timestamp = timestamp + self.offset = offset + } + + /// Convert this TimeFreeze to a dictionary representation. + /// + /// - returns: A dictionary representation. + func toDictionary() -> [String: TimeInterval] { + return [ + kUptimeKey: self.uptime, + kTimestampKey: self.timestamp, + kOffsetKey: self.offset, + ] + } + + /// Returns a high-resolution measurement of system uptime, that continues ticking through device sleep + /// *and* user- or system-generated clock adjustments. This allows for stable differences to be calculated + /// between timestamps. + /// + /// Note: Due to an issue in BSD/darwin, sub-second precision will be lost; + /// see: https://github.com/darwin-on-arm/xnu/blob/master/osfmk/kern/clock.c#L522. + /// + /// - returns: An Int measurement of system uptime in microseconds. + static func systemUptime() -> TimeInterval { + var mib = [CTL_KERN, KERN_BOOTTIME] + var size = MemoryLayout.stride + var bootTime = timeval() + + let bootTimeError = sysctl(&mib, u_int(mib.count), &bootTime, &size, nil, 0) != 0 + assert(!bootTimeError, "system clock error: kernel boot time unavailable") + + let now = currentTime() + let uptime = Double(bootTime.tv_sec) + Double(bootTime.tv_usec) / 1_000_000 + assert(now >= uptime, "inconsistent clock state: system time precedes boot time") + + return now - uptime + } +} diff --git a/Sources/Datadog/Kronos/TimeStorage.swift b/Sources/Datadog/Kronos/TimeStorage.swift new file mode 100644 index 0000000000..082c6a2ba4 --- /dev/null +++ b/Sources/Datadog/Kronos/TimeStorage.swift @@ -0,0 +1,63 @@ +import Foundation + +/// Defines where the user defaults are stored +public enum TimeStoragePolicy { + /// Uses `UserDefaults.Standard` + case standard + /// Attempts to use the specified App Group ID (which is the String) to access shared storage. + case appGroup(String) + + /// Creates an instance + /// + /// - parameter appGroupID: The App Group ID that maps to a shared container for `UserDefaults`. If this + /// is nil, the resulting instance will be `.standard` + public init(appGroupID: String?) { + if let appGroupID = appGroupID { + self = .appGroup(appGroupID) + } else { + self = .standard + } + } +} + +/// Handles saving and retrieving instances of `TimeFreeze` for quick retrieval +public struct TimeStorage { + private var userDefaults: UserDefaults + private let kDefaultsKey = "KronosStableTime" + + /// The most recent stored `TimeFreeze`. Getting retrieves from the UserDefaults defined by the storage + /// policy. Setting sets the value in UserDefaults + var stableTime: TimeFreeze? { + get { + guard let stored = self.userDefaults.value(forKey: kDefaultsKey) as? [String: TimeInterval], + let previousStableTime = TimeFreeze(from: stored) else + { + return nil + } + + return previousStableTime + } + + set { + guard let newFreeze = newValue else { + return + } + + self.userDefaults.set(newFreeze.toDictionary(), forKey: kDefaultsKey) + } + } + + /// Creates an instance + /// + /// - parameter storagePolicy: Defines the storage location of `UserDefaults` + public init(storagePolicy: TimeStoragePolicy) { + switch storagePolicy { + case .standard: + self.userDefaults = .standard + case .appGroup(let groupName): + let sharedDefaults = UserDefaults(suiteName: groupName) + assert(sharedDefaults != nil, "Could not create UserDefaults for group: '\(groupName)'") + self.userDefaults = sharedDefaults ?? .standard + } + } +} diff --git a/Tests/DatadogTests/Datadog/Kronos/ClockTests.swift b/Tests/DatadogTests/Datadog/Kronos/ClockTests.swift new file mode 100644 index 0000000000..cfc7bd1d0b --- /dev/null +++ b/Tests/DatadogTests/Datadog/Kronos/ClockTests.swift @@ -0,0 +1,41 @@ +import XCTest +@testable import Kronos + +final class ClockTests: XCTestCase { + + override func setUp() { + super.setUp() + Clock.reset() + } + + func testFirst() { + let expectation = self.expectation(description: "Clock sync calls first closure") + Clock.sync(first: { date, _ in + XCTAssertNotNil(date) + expectation.fulfill() + }) + + self.waitForExpectations(timeout: 2) + } + + func testLast() { + let expectation = self.expectation(description: "Clock sync calls last closure") + Clock.sync(completion: { date, offset in + XCTAssertNotNil(date) + XCTAssertNotNil(offset) + expectation.fulfill() + }) + + self.waitForExpectations(timeout: 20) + } + + func testBoth() { + let firstExpectation = self.expectation(description: "Clock sync calls first closure") + let lastExpectation = self.expectation(description: "Clock sync calls last closure") + Clock.sync( + first: { _, _ in firstExpectation.fulfill() }, + completion: { _, _ in lastExpectation.fulfill() }) + + self.waitForExpectations(timeout: 20) + } +} diff --git a/Tests/DatadogTests/Datadog/Kronos/DNSResolverTests.swift b/Tests/DatadogTests/Datadog/Kronos/DNSResolverTests.swift new file mode 100644 index 0000000000..3db9564677 --- /dev/null +++ b/Tests/DatadogTests/Datadog/Kronos/DNSResolverTests.swift @@ -0,0 +1,66 @@ +import XCTest +@testable import Kronos + +final class DNSResolverTests: XCTestCase { + + func testResolveOneIP() { + let expectation = self.expectation(description: "Query host's DNS for a single IP") + DNSResolver.resolve(host: "test.com") { addresses in + XCTAssertEqual(addresses.count, 1) + expectation.fulfill() + } + + self.waitForExpectations(timeout: 5) + } + + func testResolveMultipleIP() { + let expectation = self.expectation(description: "Query host's DNS for multiple IPs") + DNSResolver.resolve(host: "pool.ntp.org") { addresses in + XCTAssertGreaterThan(addresses.count, 1) + expectation.fulfill() + } + + self.waitForExpectations(timeout: 5) + } + + func testResolveIPv6() { + let expectation = self.expectation(description: "Query host's DNS that supports IPv6") + DNSResolver.resolve(host: "ipv6friday.org") { addresses in + XCTAssertGreaterThan(addresses.count, 0) + expectation.fulfill() + } + + self.waitForExpectations(timeout: 5) + } + + func testInvalidIP() { + let expectation = self.expectation(description: "Query invalid host's DNS") + DNSResolver.resolve(host: "l33t.h4x") { addresses in + XCTAssertEqual(addresses.count, 0) + expectation.fulfill() + } + + self.waitForExpectations(timeout: 5) + } + + func testTimeout() { + let expectation = self.expectation(description: "DNS times out") + DNSResolver.resolve(host: "ip6.nl", timeout: 0) { addresses in + XCTAssertEqual(addresses.count, 0) + expectation.fulfill() + } + + self.waitForExpectations(timeout: 1.0) + } + + func testTemporaryRunloopHandling() { + let expectation = self.expectation(description: "Query works from async GCD queues") + DispatchQueue(label: "Ephemeral DNS test queue").async { + DNSResolver.resolve(host: "lyft.com") { _ in + expectation.fulfill() + } + } + + self.waitForExpectations(timeout: 5) + } +} diff --git a/Tests/DatadogTests/Datadog/Kronos/NTPClientTests.swift b/Tests/DatadogTests/Datadog/Kronos/NTPClientTests.swift new file mode 100644 index 0000000000..646f5e32f7 --- /dev/null +++ b/Tests/DatadogTests/Datadog/Kronos/NTPClientTests.swift @@ -0,0 +1,50 @@ +import XCTest +@testable import Kronos + +final class NTPClientTests: XCTestCase { + + func testQueryIP() { + let expectation = self.expectation(description: "NTPClient queries single IPs") + + DNSResolver.resolve(host: "time.apple.com") { addresses in + XCTAssertGreaterThan(addresses.count, 0) + + NTPClient().query(ip: addresses.first!, version: 3, numberOfSamples: 1) { PDU in + XCTAssertNotNil(PDU) + + XCTAssertGreaterThanOrEqual(PDU!.version, 3) + XCTAssertTrue(PDU!.isValidResponse()) + + expectation.fulfill() + } + } + + self.waitForExpectations(timeout: 10) + } + + func testQueryPool() { + let expectation = self.expectation(description: "Offset from ref clock to local clock are accurate") + NTPClient().query(pool: "0.pool.ntp.org", numberOfSamples: 1, maximumServers: 1) { offset, _, _ in + XCTAssertNotNil(offset) + + NTPClient().query(pool: "0.pool.ntp.org", numberOfSamples: 1, maximumServers: 1) + { offset2, _, _ in + XCTAssertNotNil(offset2) + XCTAssertLessThan(abs(offset! - offset2!), 0.10) + expectation.fulfill() + } + } + + self.waitForExpectations(timeout: 10) + } + + func testQueryPoolWithIPv6() { + let expectation = self.expectation(description: "NTPClient queries a pool that supports IPv6") + NTPClient().query(pool: "2.pool.ntp.org", numberOfSamples: 1, maximumServers: 1) { offset, _, _ in + XCTAssertNotNil(offset) + expectation.fulfill() + } + + self.waitForExpectations(timeout: 10) + } +} diff --git a/Tests/DatadogTests/Datadog/Kronos/NTPPacketTests.swift b/Tests/DatadogTests/Datadog/Kronos/NTPPacketTests.swift new file mode 100644 index 0000000000..29a7a5d438 --- /dev/null +++ b/Tests/DatadogTests/Datadog/Kronos/NTPPacketTests.swift @@ -0,0 +1,41 @@ +import XCTest +@testable import Kronos + +final class NTPPacketTests: XCTestCase { + func testToData() { + var packet = NTPPacket() + let data = packet.prepareToSend(transmitTime: 1463303662.776552) + XCTAssertEqual(data, Data(hex: "1b0004fa0001000000010000000000000000000000000000" + + "00000000000000000000000000000000dae2bc6ec6cc1c00")!) + } + + func testParseInvalidData() { + let network = Data(hex: "0badface")! + let PDU = try? NTPPacket(data: network, destinationTime: 0) + XCTAssertNil(PDU) + } + + func testParseData() { + let network = Data(hex: "1c0203e90000065700000a68ada2c09cdae2d084a5a76d5fdae2d3354a529000dae2d32b" + + "b38bab46dae2d32bb38d9e00")! + let PDU = try? NTPPacket(data: network, destinationTime: 0) + XCTAssertEqual(PDU?.version, 3) + XCTAssertEqual(PDU?.leap, LeapIndicator.noWarning) + XCTAssertEqual(PDU?.mode, Mode.server) + XCTAssertEqual(PDU?.stratum, Stratum.secondary) + XCTAssertEqual(PDU?.poll, 3) + XCTAssertEqual(PDU?.precision, -23) + } + + func testParseTimeData() { + let network = Data(hex: "1c0203e90000065700000a68ada2c09cdae2d084a5a76d5fdae2d3354a529000dae2d32b" + + "b38bab46dae2d32bb38d9e00")! + let PDU = try? NTPPacket(data: network, destinationTime: 0) + XCTAssertEqual(PDU?.rootDelay, 0.0247650146484375) + XCTAssertEqual(PDU?.rootDispersion, 0.0406494140625) + XCTAssertEqual(PDU?.clockSource.ID, 2913124508) + XCTAssertEqual(PDU?.referenceTime, 1463308804.6470859051) + XCTAssertEqual(PDU?.originTime, 1463309493.2903223038) + XCTAssertEqual(PDU?.receiveTime, 1463309483.7013499737) + } +} diff --git a/Tests/DatadogTests/Datadog/Kronos/TimeStorageTests.swift b/Tests/DatadogTests/Datadog/Kronos/TimeStorageTests.swift new file mode 100644 index 0000000000..667a6bb64a --- /dev/null +++ b/Tests/DatadogTests/Datadog/Kronos/TimeStorageTests.swift @@ -0,0 +1,45 @@ +@testable import Kronos +import XCTest + +class TimeStoragePolicyTests: XCTestCase { + func testInitWithStringGivesAppGroupType() { + let group = TimeStoragePolicy(appGroupID: "com.test.something.mygreatapp") + if case TimeStoragePolicy.appGroup(_) = group { + XCTAssert(true) + } else { + XCTAssert(false) + } + } + + func testInitWithNIlGivesStandardType() { + let group = TimeStoragePolicy(appGroupID: nil) + if case TimeStoragePolicy.standard = group { + XCTAssert(true) + } else { + XCTAssert(false) + } + } +} + +class TimeStorageTests: XCTestCase { + func testStoringAndRetrievingTimeFreeze() { + var storage = TimeStorage(storagePolicy: .standard) + let sampleFreeze = TimeFreeze(offset: 5000.32423) + storage.stableTime = sampleFreeze + + let fromDefaults = storage.stableTime + XCTAssertNotNil(fromDefaults) + XCTAssertEqual(sampleFreeze.toDictionary(), fromDefaults!.toDictionary()) + } + + func testRetrievingTimeFreezeAfterReboot() { + let sampleFreeze = TimeFreeze(offset: 5000.32423) + var storedData = sampleFreeze.toDictionary() + storedData["Uptime"] = storedData["Uptime"]! + 10 + + let beforeRebootFreeze = TimeFreeze(from: sampleFreeze.toDictionary()) + let afterRebootFreeze = TimeFreeze(from: storedData) + XCTAssertNil(afterRebootFreeze) + XCTAssertNotNil(beforeRebootFreeze) + } +} From 11d56029b94b35efed2f1ef5f65c71ce4b2f9554 Mon Sep 17 00:00:00 2001 From: Maciek Grzybowski Date: Tue, 28 Dec 2021 14:22:55 +0100 Subject: [PATCH 2/7] RUMM-1744 Unlink Kronos dependency from the SDK --- Cartfile | 1 - Cartfile.resolved | 1 - Datadog/Datadog.xcodeproj/project.pbxproj | 10 ---------- DatadogSDK.podspec | 1 - DatadogSDK.podspec.src | 1 - Package.swift | 2 -- .../Datadog/Core/System/Time/ServerDateProvider.swift | 1 - .../carthage/CTProject.xcodeproj/project.pbxproj | 4 ---- dependency-manager-tests/carthage/Makefile | 1 - 9 files changed, 22 deletions(-) diff --git a/Cartfile b/Cartfile index 80736e5772..ae7b99f99b 100644 --- a/Cartfile +++ b/Cartfile @@ -1,2 +1 @@ -github "lyft/Kronos" ~> 4.2 github "microsoft/plcrashreporter" ~> 1.10.1 diff --git a/Cartfile.resolved b/Cartfile.resolved index fbec679d38..f04d6c4ebb 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,2 +1 @@ -github "lyft/Kronos" "4.2.1" github "microsoft/plcrashreporter" "1.10.1" diff --git a/Datadog/Datadog.xcodeproj/project.pbxproj b/Datadog/Datadog.xcodeproj/project.pbxproj index c0831deae1..5facab3d71 100644 --- a/Datadog/Datadog.xcodeproj/project.pbxproj +++ b/Datadog/Datadog.xcodeproj/project.pbxproj @@ -305,11 +305,7 @@ 61993657265BB6A6009D7EA8 /* Datadog.framework in ⚙️ Embed Framework Dependencies */ = {isa = PBXBuildFile; fileRef = 61133B82242393DE00786299 /* Datadog.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 6199365A265BB6A6009D7EA8 /* DatadogCrashReporting.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 61B7885425C180CB002675B5 /* DatadogCrashReporting.framework */; }; 6199365B265BB6A6009D7EA8 /* DatadogCrashReporting.framework in ⚙️ Embed Framework Dependencies */ = {isa = PBXBuildFile; fileRef = 61B7885425C180CB002675B5 /* DatadogCrashReporting.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 6199365E265BB6A6009D7EA8 /* Kronos.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9E0542CA25F8EBBE007A3D0B /* Kronos.xcframework */; }; - 6199365F265BB6A6009D7EA8 /* Kronos.xcframework in ⚙️ Embed Framework Dependencies */ = {isa = PBXBuildFile; fileRef = 9E0542CA25F8EBBE007A3D0B /* Kronos.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 61993668265BBEDC009D7EA8 /* E2ETests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61993667265BBEDC009D7EA8 /* E2ETests.swift */; }; - 61993673265BC371009D7EA8 /* Kronos.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9E0542CA25F8EBBE007A3D0B /* Kronos.xcframework */; }; - 61993674265BC371009D7EA8 /* Kronos.xcframework in ⚙️ Embed Framework Dependencies */ = {isa = PBXBuildFile; fileRef = 9E0542CA25F8EBBE007A3D0B /* Kronos.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 619A29F326E64910007D62A3 /* CrashReporter.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 614ED36B260352DC00C8C519 /* CrashReporter.xcframework */; }; 619E16D82577C1CB00B2516B /* DataProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 619E16D72577C1CB00B2516B /* DataProcessor.swift */; }; 619E16E92578E73E00B2516B /* DataMigrator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 619E16E82578E73E00B2516B /* DataMigrator.swift */; }; @@ -499,7 +495,6 @@ 61FF283024BC5E2D000B3D9B /* RUMEventFileOutputTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61FF282F24BC5E2D000B3D9B /* RUMEventFileOutputTests.swift */; }; 61FF416225EE5FF400CE35EC /* CrashReportingWithLoggingIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61FF416125EE5FF400CE35EC /* CrashReportingWithLoggingIntegrationTests.swift */; }; 61FF9A4525AC5DEA001058CC /* RUMViewIdentity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61FF9A4425AC5DEA001058CC /* RUMViewIdentity.swift */; }; - 9E0542CB25F8EBBE007A3D0B /* Kronos.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9E0542CA25F8EBBE007A3D0B /* Kronos.xcframework */; }; 9E26E6B924C87693000B3270 /* RUMDataModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E26E6B824C87693000B3270 /* RUMDataModels.swift */; }; 9E2EF44F2694FA14008A7DAE /* VitalInfoSamplerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E2EF44E2694FA14008A7DAE /* VitalInfoSamplerTests.swift */; }; 9E307C3224C8846D0039607E /* RUMDataModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E26E6B824C87693000B3270 /* RUMDataModels.swift */; }; @@ -669,7 +664,6 @@ dstSubfolderSpec = 10; files = ( 61441C4E24619498003D8BB8 /* Datadog.framework in ⚙️ Embed Framework Dependencies */, - 61993674265BC371009D7EA8 /* Kronos.xcframework in ⚙️ Embed Framework Dependencies */, 614ED39B260357FA00C8C519 /* DatadogCrashReporting.framework in ⚙️ Embed Framework Dependencies */, 61570006246AAE5E00E96950 /* DatadogObjc.framework in ⚙️ Embed Framework Dependencies */, ); @@ -682,7 +676,6 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - 6199365F265BB6A6009D7EA8 /* Kronos.xcframework in ⚙️ Embed Framework Dependencies */, 61993657265BB6A6009D7EA8 /* Datadog.framework in ⚙️ Embed Framework Dependencies */, 6199365B265BB6A6009D7EA8 /* DatadogCrashReporting.framework in ⚙️ Embed Framework Dependencies */, ); @@ -1273,7 +1266,6 @@ buildActionMask = 2147483647; files = ( 61B03ECE274FF01F00EB1AE1 /* SwiftUI.framework in Frameworks */, - 61993673265BC371009D7EA8 /* Kronos.xcframework in Frameworks */, 614ED39A260357FA00C8C519 /* DatadogCrashReporting.framework in Frameworks */, 614ED37C2603533D00C8C519 /* CrashReporter.xcframework in Frameworks */, ); @@ -1301,7 +1293,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 9E0542CB25F8EBBE007A3D0B /* Kronos.xcframework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1318,7 +1309,6 @@ files = ( 61993656265BB6A6009D7EA8 /* Datadog.framework in Frameworks */, 6199365A265BB6A6009D7EA8 /* DatadogCrashReporting.framework in Frameworks */, - 6199365E265BB6A6009D7EA8 /* Kronos.xcframework in Frameworks */, 61993654265BB6A6009D7EA8 /* CrashReporter.xcframework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/DatadogSDK.podspec b/DatadogSDK.podspec index d36c5db582..ac1f469296 100644 --- a/DatadogSDK.podspec +++ b/DatadogSDK.podspec @@ -25,5 +25,4 @@ Pod::Spec.new do |s| s.public_header_files = ["Datadog/TargetSupport/Datadog/Datadog.h", "Sources/_Datadog_Private/include/*.h"] - s.dependency 'Kronos', '~> 4.2' end diff --git a/DatadogSDK.podspec.src b/DatadogSDK.podspec.src index c6be426253..4f4578a4ea 100644 --- a/DatadogSDK.podspec.src +++ b/DatadogSDK.podspec.src @@ -25,5 +25,4 @@ Pod::Spec.new do |s| s.public_header_files = ["Datadog/TargetSupport/Datadog/Datadog.h", "Sources/_Datadog_Private/include/*.h"] - s.dependency 'Kronos', '~> 4.2' end diff --git a/Package.swift b/Package.swift index b613169d67..03baecbbe3 100644 --- a/Package.swift +++ b/Package.swift @@ -35,7 +35,6 @@ let package = Package( ), ], dependencies: [ - .package(name: "Kronos", url: "https://github.com/lyft/Kronos.git", from: "4.2.1"), .package(name: "PLCrashReporter", url: "https://github.com/microsoft/plcrashreporter.git", from: "1.10.0"), ], targets: [ @@ -43,7 +42,6 @@ let package = Package( name: "Datadog", dependencies: [ "_Datadog_Private", - .product(name: "Kronos", package: "Kronos"), ], swiftSettings: [.define("SPM_BUILD")] ), diff --git a/Sources/Datadog/Core/System/Time/ServerDateProvider.swift b/Sources/Datadog/Core/System/Time/ServerDateProvider.swift index 97748d5ef1..683b45bd1d 100644 --- a/Sources/Datadog/Core/System/Time/ServerDateProvider.swift +++ b/Sources/Datadog/Core/System/Time/ServerDateProvider.swift @@ -5,7 +5,6 @@ */ import Foundation -import Kronos /// Abstract the monotonic clock synchronized with the server using NTP. internal protocol ServerDateProvider { diff --git a/dependency-manager-tests/carthage/CTProject.xcodeproj/project.pbxproj b/dependency-manager-tests/carthage/CTProject.xcodeproj/project.pbxproj index bc7d22c723..4a9312c4bd 100644 --- a/dependency-manager-tests/carthage/CTProject.xcodeproj/project.pbxproj +++ b/dependency-manager-tests/carthage/CTProject.xcodeproj/project.pbxproj @@ -14,8 +14,6 @@ 61C36425243752A600C4D4E6 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 61C36423243752A600C4D4E6 /* LaunchScreen.storyboard */; }; 61C36430243752A600C4D4E6 /* CTProjectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61C3642F243752A600C4D4E6 /* CTProjectTests.swift */; }; 61C3643B243752A600C4D4E6 /* CTProjectUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61C3643A243752A600C4D4E6 /* CTProjectUITests.swift */; }; - 9E9D5E8825F90FC6002F12A0 /* Kronos.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9E9D5E8525F90FC6002F12A0 /* Kronos.xcframework */; }; - 9E9D5E8925F90FC6002F12A0 /* Kronos.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9E9D5E8525F90FC6002F12A0 /* Kronos.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 9E9D5E8A25F90FC6002F12A0 /* DatadogObjc.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9E9D5E8625F90FC6002F12A0 /* DatadogObjc.xcframework */; }; 9E9D5E8B25F90FC6002F12A0 /* DatadogObjc.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9E9D5E8625F90FC6002F12A0 /* DatadogObjc.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 9E9D5E8C25F90FC6002F12A0 /* Datadog.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9E9D5E8725F90FC6002F12A0 /* Datadog.xcframework */; }; @@ -52,7 +50,6 @@ 9EF87B8D26B04E1F00998076 /* CrashReporter.xcframework in Embed Frameworks */, 9E9D5E8D25F90FC6002F12A0 /* Datadog.xcframework in Embed Frameworks */, 9E9D5E8B25F90FC6002F12A0 /* DatadogObjc.xcframework in Embed Frameworks */, - 9E9D5E8925F90FC6002F12A0 /* Kronos.xcframework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -90,7 +87,6 @@ 9EF87B8C26B04E1F00998076 /* CrashReporter.xcframework in Frameworks */, 9E9D5E8C25F90FC6002F12A0 /* Datadog.xcframework in Frameworks */, 9E9D5E8A25F90FC6002F12A0 /* DatadogObjc.xcframework in Frameworks */, - 9E9D5E8825F90FC6002F12A0 /* Kronos.xcframework in Frameworks */, 9EF87B8E26B04E1F00998076 /* DatadogCrashReporting.xcframework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/dependency-manager-tests/carthage/Makefile b/dependency-manager-tests/carthage/Makefile index f5542c32e9..bd97559c06 100644 --- a/dependency-manager-tests/carthage/Makefile +++ b/dependency-manager-tests/carthage/Makefile @@ -23,6 +23,5 @@ test: @[ -e "Carthage/Build/Datadog.xcframework" ] && echo "Datadog.xcframework - OK" || { echo "Datadog.xcframework - missing"; false; } @[ -e "Carthage/Build/DatadogObjc.xcframework" ] && echo "DatadogObjc.xcframework - OK" || { echo "DatadogObjc.xcframework - missing"; false; } @[ -e "Carthage/Build/DatadogCrashReporting.xcframework" ] && echo "DatadogCrashReporting.xcframework - OK" || { echo "DatadogCrashReporting.xcframework - missing"; false; } - @[ -e "Carthage/Build/Kronos.xcframework" ] && echo "Kronos.xcframework - OK" || { echo "Kronos.xcframework - missing"; false; } @[ -e "Carthage/Build/CrashReporter.xcframework" ] && echo "CrashReporter.xcframework - OK" || { echo "CrashReporter.xcframework - missing"; false; } @echo "🧪 SUCCEEDED" From 0ec79ae044309a38bba5f560e861a6e124222ea3 Mon Sep 17 00:00:00 2001 From: Maciek Grzybowski Date: Tue, 28 Dec 2021 18:08:22 +0100 Subject: [PATCH 3/7] RUMM-1744 Fix import statement in tests to make it compile --- Tests/DatadogTests/Datadog/Kronos/ClockTests.swift | 2 +- Tests/DatadogTests/Datadog/Kronos/DNSResolverTests.swift | 2 +- Tests/DatadogTests/Datadog/Kronos/NTPClientTests.swift | 2 +- Tests/DatadogTests/Datadog/Kronos/NTPPacketTests.swift | 2 +- Tests/DatadogTests/Datadog/Kronos/TimeStorageTests.swift | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Tests/DatadogTests/Datadog/Kronos/ClockTests.swift b/Tests/DatadogTests/Datadog/Kronos/ClockTests.swift index cfc7bd1d0b..40c79d401e 100644 --- a/Tests/DatadogTests/Datadog/Kronos/ClockTests.swift +++ b/Tests/DatadogTests/Datadog/Kronos/ClockTests.swift @@ -1,5 +1,5 @@ import XCTest -@testable import Kronos +@testable import Datadog final class ClockTests: XCTestCase { diff --git a/Tests/DatadogTests/Datadog/Kronos/DNSResolverTests.swift b/Tests/DatadogTests/Datadog/Kronos/DNSResolverTests.swift index 3db9564677..a98a946ba3 100644 --- a/Tests/DatadogTests/Datadog/Kronos/DNSResolverTests.swift +++ b/Tests/DatadogTests/Datadog/Kronos/DNSResolverTests.swift @@ -1,5 +1,5 @@ import XCTest -@testable import Kronos +@testable import Datadog final class DNSResolverTests: XCTestCase { diff --git a/Tests/DatadogTests/Datadog/Kronos/NTPClientTests.swift b/Tests/DatadogTests/Datadog/Kronos/NTPClientTests.swift index 646f5e32f7..a3a42e57d3 100644 --- a/Tests/DatadogTests/Datadog/Kronos/NTPClientTests.swift +++ b/Tests/DatadogTests/Datadog/Kronos/NTPClientTests.swift @@ -1,5 +1,5 @@ import XCTest -@testable import Kronos +@testable import Datadog final class NTPClientTests: XCTestCase { diff --git a/Tests/DatadogTests/Datadog/Kronos/NTPPacketTests.swift b/Tests/DatadogTests/Datadog/Kronos/NTPPacketTests.swift index 29a7a5d438..f7b7357c0e 100644 --- a/Tests/DatadogTests/Datadog/Kronos/NTPPacketTests.swift +++ b/Tests/DatadogTests/Datadog/Kronos/NTPPacketTests.swift @@ -1,5 +1,5 @@ import XCTest -@testable import Kronos +@testable import Datadog final class NTPPacketTests: XCTestCase { func testToData() { diff --git a/Tests/DatadogTests/Datadog/Kronos/TimeStorageTests.swift b/Tests/DatadogTests/Datadog/Kronos/TimeStorageTests.swift index 667a6bb64a..3b3f3de2bc 100644 --- a/Tests/DatadogTests/Datadog/Kronos/TimeStorageTests.swift +++ b/Tests/DatadogTests/Datadog/Kronos/TimeStorageTests.swift @@ -1,5 +1,5 @@ -@testable import Kronos import XCTest +@testable import Datadog class TimeStoragePolicyTests: XCTestCase { func testInitWithStringGivesAppGroupType() { From 3a2097796e9fa08be658e2141e9caf879db58fd6 Mon Sep 17 00:00:00 2001 From: Maciek Grzybowski Date: Tue, 28 Dec 2021 18:33:04 +0100 Subject: [PATCH 4/7] RUMM-1744 Annotate all Kronos types with "Kronos" prefix and adjust their access control --- Datadog/Datadog.xcodeproj/project.pbxproj | 120 +++++++++--------- .../Core/System/Time/ServerDateProvider.swift | 4 +- .../Kronos/{Clock.swift => KronosClock.swift} | 28 ++-- ...Resolver.swift => KronosDNSResolver.swift} | 14 +- ...ata+Bytes.swift => KronosData+Bytes.swift} | 0 ...ress.swift => KronosInternetAddress.swift} | 8 +- ...t.swift => KronosNSTimer+ClosureKit.swift} | 8 +- ...{NTPClient.swift => KronosNTPClient.swift} | 32 ++--- ...{NTPPacket.swift => KronosNTPPacket.swift} | 44 +++---- ...Protocol.swift => KronosNTPProtocol.swift} | 14 +- ...imeFreeze.swift => KronosTimeFreeze.swift} | 16 +-- ...eStorage.swift => KronosTimeStorage.swift} | 14 +- ...lockTests.swift => KronosClockTests.swift} | 10 +- ...sts.swift => KronosDNSResolverTests.swift} | 14 +- ...Tests.swift => KronosNTPClientTests.swift} | 12 +- ...Tests.swift => KronosNTPPacketTests.swift} | 16 +-- ...sts.swift => KronosTimeStorageTests.swift} | 20 +-- 17 files changed, 187 insertions(+), 187 deletions(-) rename Sources/Datadog/Kronos/{Clock.swift => KronosClock.swift} (81%) rename Sources/Datadog/Kronos/{DNSResolver.swift => KronosDNSResolver.swift} (88%) rename Sources/Datadog/Kronos/{Data+Bytes.swift => KronosData+Bytes.swift} (100%) rename Sources/Datadog/Kronos/{InternetAddress.swift => KronosInternetAddress.swift} (92%) rename Sources/Datadog/Kronos/{NSTimer+ClosureKit.swift => KronosNSTimer+ClosureKit.swift} (86%) rename Sources/Datadog/Kronos/{NTPClient.swift => KronosNTPClient.swift} (85%) rename Sources/Datadog/Kronos/{NTPPacket.swift => KronosNTPPacket.swift} (82%) rename Sources/Datadog/Kronos/{NTPProtocol.swift => KronosNTPProtocol.swift} (92%) rename Sources/Datadog/Kronos/{TimeFreeze.swift => KronosTimeFreeze.swift} (86%) rename Sources/Datadog/Kronos/{TimeStorage.swift => KronosTimeStorage.swift} (83%) rename Tests/DatadogTests/Datadog/Kronos/{ClockTests.swift => KronosClockTests.swift} (83%) rename Tests/DatadogTests/Datadog/Kronos/{DNSResolverTests.swift => KronosDNSResolverTests.swift} (78%) rename Tests/DatadogTests/Datadog/Kronos/{NTPClientTests.swift => KronosNTPClientTests.swift} (68%) rename Tests/DatadogTests/Datadog/Kronos/{NTPPacketTests.swift => KronosNTPPacketTests.swift} (73%) rename Tests/DatadogTests/Datadog/Kronos/{TimeStorageTests.swift => KronosTimeStorageTests.swift} (56%) diff --git a/Datadog/Datadog.xcodeproj/project.pbxproj b/Datadog/Datadog.xcodeproj/project.pbxproj index 5facab3d71..1f719c0f43 100644 --- a/Datadog/Datadog.xcodeproj/project.pbxproj +++ b/Datadog/Datadog.xcodeproj/project.pbxproj @@ -392,21 +392,21 @@ 61C5A8A624509FAA00DA608C /* SpanEventEncoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61C5A8A424509FAA00DA608C /* SpanEventEncoder.swift */; }; 61C5A8A724509FAA00DA608C /* SpanEventBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61C5A8A524509FAA00DA608C /* SpanEventBuilder.swift */; }; 61D03BE0273404E700367DE0 /* RUMDataModels+objcTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61D03BDF273404E700367DE0 /* RUMDataModels+objcTests.swift */; }; - 61D3E0D2277B23F1008BE766 /* InternetAddress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61D3E0C8277B23F0008BE766 /* InternetAddress.swift */; }; - 61D3E0D3277B23F1008BE766 /* DNSResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61D3E0C9277B23F0008BE766 /* DNSResolver.swift */; }; - 61D3E0D4277B23F1008BE766 /* TimeStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61D3E0CA277B23F0008BE766 /* TimeStorage.swift */; }; - 61D3E0D5277B23F1008BE766 /* NTPPacket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61D3E0CB277B23F0008BE766 /* NTPPacket.swift */; }; - 61D3E0D6277B23F1008BE766 /* Clock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61D3E0CC277B23F0008BE766 /* Clock.swift */; }; - 61D3E0D7277B23F1008BE766 /* Data+Bytes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61D3E0CD277B23F0008BE766 /* Data+Bytes.swift */; }; - 61D3E0D8277B23F1008BE766 /* NTPClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61D3E0CE277B23F0008BE766 /* NTPClient.swift */; }; - 61D3E0D9277B23F1008BE766 /* NTPProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61D3E0CF277B23F0008BE766 /* NTPProtocol.swift */; }; - 61D3E0DA277B23F1008BE766 /* TimeFreeze.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61D3E0D0277B23F1008BE766 /* TimeFreeze.swift */; }; - 61D3E0DB277B23F1008BE766 /* NSTimer+ClosureKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61D3E0D1277B23F1008BE766 /* NSTimer+ClosureKit.swift */; }; - 61D3E0E3277B3D92008BE766 /* NTPClientTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61D3E0DE277B3D92008BE766 /* NTPClientTests.swift */; }; - 61D3E0E4277B3D92008BE766 /* NTPPacketTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61D3E0DF277B3D92008BE766 /* NTPPacketTests.swift */; }; - 61D3E0E5277B3D92008BE766 /* ClockTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61D3E0E0277B3D92008BE766 /* ClockTests.swift */; }; - 61D3E0E6277B3D92008BE766 /* DNSResolverTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61D3E0E1277B3D92008BE766 /* DNSResolverTests.swift */; }; - 61D3E0E7277B3D92008BE766 /* TimeStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61D3E0E2277B3D92008BE766 /* TimeStorageTests.swift */; }; + 61D3E0D2277B23F1008BE766 /* KronosInternetAddress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61D3E0C8277B23F0008BE766 /* KronosInternetAddress.swift */; }; + 61D3E0D3277B23F1008BE766 /* KronosDNSResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61D3E0C9277B23F0008BE766 /* KronosDNSResolver.swift */; }; + 61D3E0D4277B23F1008BE766 /* KronosTimeStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61D3E0CA277B23F0008BE766 /* KronosTimeStorage.swift */; }; + 61D3E0D5277B23F1008BE766 /* KronosNTPPacket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61D3E0CB277B23F0008BE766 /* KronosNTPPacket.swift */; }; + 61D3E0D6277B23F1008BE766 /* KronosClock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61D3E0CC277B23F0008BE766 /* KronosClock.swift */; }; + 61D3E0D7277B23F1008BE766 /* KronosData+Bytes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61D3E0CD277B23F0008BE766 /* KronosData+Bytes.swift */; }; + 61D3E0D8277B23F1008BE766 /* KronosNTPClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61D3E0CE277B23F0008BE766 /* KronosNTPClient.swift */; }; + 61D3E0D9277B23F1008BE766 /* KronosNTPProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61D3E0CF277B23F0008BE766 /* KronosNTPProtocol.swift */; }; + 61D3E0DA277B23F1008BE766 /* KronosTimeFreeze.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61D3E0D0277B23F1008BE766 /* KronosTimeFreeze.swift */; }; + 61D3E0DB277B23F1008BE766 /* KronosNSTimer+ClosureKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61D3E0D1277B23F1008BE766 /* KronosNSTimer+ClosureKit.swift */; }; + 61D3E0E3277B3D92008BE766 /* KronosNTPClientTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61D3E0DE277B3D92008BE766 /* KronosNTPClientTests.swift */; }; + 61D3E0E4277B3D92008BE766 /* KronosNTPPacketTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61D3E0DF277B3D92008BE766 /* KronosNTPPacketTests.swift */; }; + 61D3E0E5277B3D92008BE766 /* KronosClockTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61D3E0E0277B3D92008BE766 /* KronosClockTests.swift */; }; + 61D3E0E6277B3D92008BE766 /* KronosDNSResolverTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61D3E0E1277B3D92008BE766 /* KronosDNSResolverTests.swift */; }; + 61D3E0E7277B3D92008BE766 /* KronosTimeStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61D3E0E2277B3D92008BE766 /* KronosTimeStorageTests.swift */; }; 61D447E224917F8F00649287 /* DateFormatting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61D447E124917F8F00649287 /* DateFormatting.swift */; }; 61D50C3C2580EEF8006038A3 /* LoggingScenarios.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61D50C3B2580EEF8006038A3 /* LoggingScenarios.swift */; }; 61D50C462580EF19006038A3 /* TracingScenarios.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61D50C452580EF19006038A3 /* TracingScenarios.swift */; }; @@ -1078,21 +1078,21 @@ 61C5A8A424509FAA00DA608C /* SpanEventEncoder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SpanEventEncoder.swift; sourceTree = ""; }; 61C5A8A524509FAA00DA608C /* SpanEventBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SpanEventBuilder.swift; sourceTree = ""; }; 61D03BDF273404E700367DE0 /* RUMDataModels+objcTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RUMDataModels+objcTests.swift"; sourceTree = ""; }; - 61D3E0C8277B23F0008BE766 /* InternetAddress.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InternetAddress.swift; sourceTree = ""; }; - 61D3E0C9277B23F0008BE766 /* DNSResolver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DNSResolver.swift; sourceTree = ""; }; - 61D3E0CA277B23F0008BE766 /* TimeStorage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TimeStorage.swift; sourceTree = ""; }; - 61D3E0CB277B23F0008BE766 /* NTPPacket.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NTPPacket.swift; sourceTree = ""; }; - 61D3E0CC277B23F0008BE766 /* Clock.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Clock.swift; sourceTree = ""; }; - 61D3E0CD277B23F0008BE766 /* Data+Bytes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Data+Bytes.swift"; sourceTree = ""; }; - 61D3E0CE277B23F0008BE766 /* NTPClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NTPClient.swift; sourceTree = ""; }; - 61D3E0CF277B23F0008BE766 /* NTPProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NTPProtocol.swift; sourceTree = ""; }; - 61D3E0D0277B23F1008BE766 /* TimeFreeze.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TimeFreeze.swift; sourceTree = ""; }; - 61D3E0D1277B23F1008BE766 /* NSTimer+ClosureKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSTimer+ClosureKit.swift"; sourceTree = ""; }; - 61D3E0DE277B3D92008BE766 /* NTPClientTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NTPClientTests.swift; sourceTree = ""; }; - 61D3E0DF277B3D92008BE766 /* NTPPacketTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NTPPacketTests.swift; sourceTree = ""; }; - 61D3E0E0277B3D92008BE766 /* ClockTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ClockTests.swift; sourceTree = ""; }; - 61D3E0E1277B3D92008BE766 /* DNSResolverTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DNSResolverTests.swift; sourceTree = ""; }; - 61D3E0E2277B3D92008BE766 /* TimeStorageTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TimeStorageTests.swift; sourceTree = ""; }; + 61D3E0C8277B23F0008BE766 /* KronosInternetAddress.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KronosInternetAddress.swift; sourceTree = ""; }; + 61D3E0C9277B23F0008BE766 /* KronosDNSResolver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KronosDNSResolver.swift; sourceTree = ""; }; + 61D3E0CA277B23F0008BE766 /* KronosTimeStorage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KronosTimeStorage.swift; sourceTree = ""; }; + 61D3E0CB277B23F0008BE766 /* KronosNTPPacket.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KronosNTPPacket.swift; sourceTree = ""; }; + 61D3E0CC277B23F0008BE766 /* KronosClock.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KronosClock.swift; sourceTree = ""; }; + 61D3E0CD277B23F0008BE766 /* KronosData+Bytes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "KronosData+Bytes.swift"; sourceTree = ""; }; + 61D3E0CE277B23F0008BE766 /* KronosNTPClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KronosNTPClient.swift; sourceTree = ""; }; + 61D3E0CF277B23F0008BE766 /* KronosNTPProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KronosNTPProtocol.swift; sourceTree = ""; }; + 61D3E0D0277B23F1008BE766 /* KronosTimeFreeze.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KronosTimeFreeze.swift; sourceTree = ""; }; + 61D3E0D1277B23F1008BE766 /* KronosNSTimer+ClosureKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "KronosNSTimer+ClosureKit.swift"; sourceTree = ""; }; + 61D3E0DE277B3D92008BE766 /* KronosNTPClientTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KronosNTPClientTests.swift; sourceTree = ""; }; + 61D3E0DF277B3D92008BE766 /* KronosNTPPacketTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KronosNTPPacketTests.swift; sourceTree = ""; }; + 61D3E0E0277B3D92008BE766 /* KronosClockTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KronosClockTests.swift; sourceTree = ""; }; + 61D3E0E1277B3D92008BE766 /* KronosDNSResolverTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KronosDNSResolverTests.swift; sourceTree = ""; }; + 61D3E0E2277B3D92008BE766 /* KronosTimeStorageTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KronosTimeStorageTests.swift; sourceTree = ""; }; 61D447E124917F8F00649287 /* DateFormatting.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DateFormatting.swift; sourceTree = ""; }; 61D50C3B2580EEF8006038A3 /* LoggingScenarios.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoggingScenarios.swift; sourceTree = ""; }; 61D50C452580EF19006038A3 /* TracingScenarios.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TracingScenarios.swift; sourceTree = ""; }; @@ -2948,16 +2948,16 @@ 61D3E0C7277B237D008BE766 /* Kronos */ = { isa = PBXGroup; children = ( - 61D3E0CC277B23F0008BE766 /* Clock.swift */, - 61D3E0CD277B23F0008BE766 /* Data+Bytes.swift */, - 61D3E0C9277B23F0008BE766 /* DNSResolver.swift */, - 61D3E0C8277B23F0008BE766 /* InternetAddress.swift */, - 61D3E0D1277B23F1008BE766 /* NSTimer+ClosureKit.swift */, - 61D3E0CE277B23F0008BE766 /* NTPClient.swift */, - 61D3E0CB277B23F0008BE766 /* NTPPacket.swift */, - 61D3E0CF277B23F0008BE766 /* NTPProtocol.swift */, - 61D3E0D0277B23F1008BE766 /* TimeFreeze.swift */, - 61D3E0CA277B23F0008BE766 /* TimeStorage.swift */, + 61D3E0CC277B23F0008BE766 /* KronosClock.swift */, + 61D3E0CD277B23F0008BE766 /* KronosData+Bytes.swift */, + 61D3E0C9277B23F0008BE766 /* KronosDNSResolver.swift */, + 61D3E0C8277B23F0008BE766 /* KronosInternetAddress.swift */, + 61D3E0D1277B23F1008BE766 /* KronosNSTimer+ClosureKit.swift */, + 61D3E0CE277B23F0008BE766 /* KronosNTPClient.swift */, + 61D3E0CB277B23F0008BE766 /* KronosNTPPacket.swift */, + 61D3E0CF277B23F0008BE766 /* KronosNTPProtocol.swift */, + 61D3E0D0277B23F1008BE766 /* KronosTimeFreeze.swift */, + 61D3E0CA277B23F0008BE766 /* KronosTimeStorage.swift */, ); path = Kronos; sourceTree = ""; @@ -2965,11 +2965,11 @@ 61D3E0DD277B3D6E008BE766 /* Kronos */ = { isa = PBXGroup; children = ( - 61D3E0E0277B3D92008BE766 /* ClockTests.swift */, - 61D3E0E1277B3D92008BE766 /* DNSResolverTests.swift */, - 61D3E0DE277B3D92008BE766 /* NTPClientTests.swift */, - 61D3E0DF277B3D92008BE766 /* NTPPacketTests.swift */, - 61D3E0E2277B3D92008BE766 /* TimeStorageTests.swift */, + 61D3E0E0277B3D92008BE766 /* KronosClockTests.swift */, + 61D3E0E1277B3D92008BE766 /* KronosDNSResolverTests.swift */, + 61D3E0DE277B3D92008BE766 /* KronosNTPClientTests.swift */, + 61D3E0DF277B3D92008BE766 /* KronosNTPPacketTests.swift */, + 61D3E0E2277B3D92008BE766 /* KronosTimeStorageTests.swift */, ); path = Kronos; sourceTree = ""; @@ -4042,10 +4042,10 @@ 61133BDC2423979B00786299 /* Logger.swift in Sources */, 6114FE1625766B310084E372 /* TrackingConsent.swift in Sources */, 61133BD02423979B00786299 /* DateProvider.swift in Sources */, - 61D3E0D2277B23F1008BE766 /* InternetAddress.swift in Sources */, + 61D3E0D2277B23F1008BE766 /* KronosInternetAddress.swift in Sources */, 6156CB8E24DDA1B5008CB2B2 /* RUMContextProvider.swift in Sources */, 61C2C21224C5951400C0321C /* RUMViewScope.swift in Sources */, - 61D3E0D5277B23F1008BE766 /* NTPPacket.swift in Sources */, + 61D3E0D5277B23F1008BE766 /* KronosNTPPacket.swift in Sources */, 61DE332625C826E4008E3EC2 /* CrashReportingFeature.swift in Sources */, 61E36A11254B2280001AD6F2 /* LaunchTimeProvider.swift in Sources */, 614872772485067300E3EBDB /* SpanTagsReducer.swift in Sources */, @@ -4086,7 +4086,7 @@ 619E16E92578E73E00B2516B /* DataMigrator.swift in Sources */, 618DCFD924C7269500589570 /* RUMUUIDGenerator.swift in Sources */, 9EFD112C24B32D29003A1A2B /* FirstPartyURLsFilter.swift in Sources */, - 61D3E0DB277B23F1008BE766 /* NSTimer+ClosureKit.swift in Sources */, + 61D3E0DB277B23F1008BE766 /* KronosNSTimer+ClosureKit.swift in Sources */, 61B0386C2527247B00518F3C /* TaskInterception.swift in Sources */, 61B03892252724D900518F3C /* HTTPHeadersReader.swift in Sources */, 61122ECE25B1B74500F9C7F5 /* SpanSanitizer.swift in Sources */, @@ -4106,7 +4106,7 @@ 613E792F2577B0F900DFCC17 /* Reader.swift in Sources */, 61B0385A2527247000518F3C /* DDURLSessionDelegate.swift in Sources */, 61133BDF2423979B00786299 /* SwiftExtensions.swift in Sources */, - 61D3E0D3277B23F1008BE766 /* DNSResolver.swift in Sources */, + 61D3E0D3277B23F1008BE766 /* KronosDNSResolver.swift in Sources */, 6149FB3A2529D17F00EE387A /* InternalURLsFilter.swift in Sources */, 611529A525E3DD51004F740E /* ValuePublisher.swift in Sources */, 618DCFD724C7265300589570 /* RUMUUID.swift in Sources */, @@ -4118,7 +4118,7 @@ 61C5A88E24509A1F00DA608C /* Tracer.swift in Sources */, 61E909EE24A24DD3005EA2DE /* OTFormat.swift in Sources */, 9E26E6B924C87693000B3270 /* RUMDataModels.swift in Sources */, - 61D3E0D6277B23F1008BE766 /* Clock.swift in Sources */, + 61D3E0D6277B23F1008BE766 /* KronosClock.swift in Sources */, 613E793B2577B6EE00DFCC17 /* DataReader.swift in Sources */, 614B0A5324EBFE5500A2A780 /* DDRUMMonitor.swift in Sources */, 61133BE32423979B00786299 /* UserInfoProvider.swift in Sources */, @@ -4139,11 +4139,11 @@ 61C5A88A24509A0C00DA608C /* SpanFileOutput.swift in Sources */, 61C3E63524BF1794008053F2 /* Attributes.swift in Sources */, 61133BD32423979B00786299 /* File.swift in Sources */, - 61D3E0D9277B23F1008BE766 /* NTPProtocol.swift in Sources */, + 61D3E0D9277B23F1008BE766 /* KronosNTPProtocol.swift in Sources */, 61DE333625C8278A008E3EC2 /* DDCrashReportingPluginType.swift in Sources */, 6112B10225C83D4E00B37771 /* CrashReportingWithLoggingIntegration.swift in Sources */, 6161249E25CAB340009901BE /* CrashContext.swift in Sources */, - 61D3E0DA277B23F1008BE766 /* TimeFreeze.swift in Sources */, + 61D3E0DA277B23F1008BE766 /* KronosTimeFreeze.swift in Sources */, 61D447E224917F8F00649287 /* DateFormatting.swift in Sources */, 61133BE72423979B00786299 /* LogUtilityOutputs.swift in Sources */, 61133BDA2423979B00786299 /* RequestBuilder.swift in Sources */, @@ -4152,7 +4152,7 @@ 61ED39D426C2A36B002C0F26 /* DataUploadStatus.swift in Sources */, 61133BE82423979B00786299 /* LogFileOutput.swift in Sources */, 61133BD72423979B00786299 /* DataUploadWorker.swift in Sources */, - 61D3E0D4277B23F1008BE766 /* TimeStorage.swift in Sources */, + 61D3E0D4277B23F1008BE766 /* KronosTimeStorage.swift in Sources */, 61133BD12423979B00786299 /* FilesOrchestrator.swift in Sources */, 61133BCD2423979B00786299 /* NetworkConnectionInfoProvider.swift in Sources */, 61FF282424B8A1C3000B3D9B /* RUMEventFileOutput.swift in Sources */, @@ -4177,7 +4177,7 @@ 61F3CDA72512144600C816E5 /* UIKitRUMViewsPredicate.swift in Sources */, 61133BCE2423979B00786299 /* BatteryStatusProvider.swift in Sources */, 613C6B902768FDDE00870CBF /* Sampler.swift in Sources */, - 61D3E0D8277B23F1008BE766 /* NTPClient.swift in Sources */, + 61D3E0D8277B23F1008BE766 /* KronosNTPClient.swift in Sources */, 9E9973F1268DF69500D8059B /* VitalInfoSampler.swift in Sources */, 9EA3CA6926775A3500B16871 /* VitalRefreshRateReader.swift in Sources */, E13A880C257922EC004FB174 /* EnvironmentSpanIntegration.swift in Sources */, @@ -4189,7 +4189,7 @@ 61C3E63B24BF1A4B008053F2 /* RUMCommand.swift in Sources */, 61133BE92423979B00786299 /* LogOutput.swift in Sources */, 61C5A88524509A0C00DA608C /* DDNoOps.swift in Sources */, - 61D3E0D7277B23F1008BE766 /* Data+Bytes.swift in Sources */, + 61D3E0D7277B23F1008BE766 /* KronosData+Bytes.swift in Sources */, 61E5332C24B75C51003D6C4E /* RUMFeature.swift in Sources */, 61E5333D24B8791A003D6C4E /* RUMEventEncoder.swift in Sources */, 6156CB9D24E18600008CB2B2 /* TracingWithRUMIntegration.swift in Sources */, @@ -4210,7 +4210,7 @@ 61B0387D252724AB00518F3C /* URLSessionSwizzlerTests.swift in Sources */, 617CD0DD24CEDDD300B0B557 /* RUMUserActionScopeTests.swift in Sources */, D29889C9273413ED00A4D1A9 /* RUMViewsHandlerTests.swift in Sources */, - 61D3E0E3277B3D92008BE766 /* NTPClientTests.swift in Sources */, + 61D3E0E3277B3D92008BE766 /* KronosNTPClientTests.swift in Sources */, 61C5A8A024509C1100DA608C /* Casting+Tracing.swift in Sources */, 61133C662423990D00786299 /* LogSanitizerTests.swift in Sources */, 6114FE23257671F00084E372 /* ConsentAwareDataWriterTests.swift in Sources */, @@ -4235,7 +4235,7 @@ 61411B1024EC15AC0012EAB2 /* Casting+RUM.swift in Sources */, 61133C622423990D00786299 /* InternalLoggersTests.swift in Sources */, 61FF283024BC5E2D000B3D9B /* RUMEventFileOutputTests.swift in Sources */, - 61D3E0E7277B3D92008BE766 /* TimeStorageTests.swift in Sources */, + 61D3E0E7277B3D92008BE766 /* KronosTimeStorageTests.swift in Sources */, 9E986C302677B91400D62490 /* VitalRefreshRateReaderTests.swift in Sources */, 61133C582423990D00786299 /* FileWriterTests.swift in Sources */, 61E917D3246546BF00E6C631 /* TracerConfigurationTests.swift in Sources */, @@ -4259,7 +4259,7 @@ 61D03BE0273404E700367DE0 /* RUMDataModels+objcTests.swift in Sources */, 615C3196251DD5080018781C /* UIKitRUMUserActionsHandlerTests.swift in Sources */, 61E917CF2464270500E6C631 /* CodableValueTests.swift in Sources */, - 61D3E0E4277B3D92008BE766 /* NTPPacketTests.swift in Sources */, + 61D3E0E4277B3D92008BE766 /* KronosNTPPacketTests.swift in Sources */, 61133C542423990D00786299 /* NetworkConnectionInfoProviderTests.swift in Sources */, 616B668E259CC28E00968EE8 /* DDRUMMonitorTests.swift in Sources */, B3BBBCBC265E71D100943419 /* VitalMemoryReaderTests.swift in Sources */, @@ -4342,10 +4342,10 @@ 611F82032563C66100CB9BDB /* UIKitRUMViewsPredicateTests.swift in Sources */, 61133C652423990D00786299 /* LogBuilderTests.swift in Sources */, 61B5E42B26DFC433000B0A5F /* DDNSURLSessionDelegate+apiTests.m in Sources */, - 61D3E0E5277B3D92008BE766 /* ClockTests.swift in Sources */, + 61D3E0E5277B3D92008BE766 /* KronosClockTests.swift in Sources */, 61F8CC092469295500FE2908 /* DatadogConfigurationBuilderTests.swift in Sources */, 61F1A623249B811200075390 /* Encoding.swift in Sources */, - 61D3E0E6277B3D92008BE766 /* DNSResolverTests.swift in Sources */, + 61D3E0E6277B3D92008BE766 /* KronosDNSResolverTests.swift in Sources */, 6114FE3B25768AA90084E372 /* ConsentProviderTests.swift in Sources */, 61133C642423990D00786299 /* LoggerTests.swift in Sources */, 617B953D24BF4D8F00E6F443 /* RUMMonitorTests.swift in Sources */, diff --git a/Sources/Datadog/Core/System/Time/ServerDateProvider.swift b/Sources/Datadog/Core/System/Time/ServerDateProvider.swift index 683b45bd1d..33b90628f4 100644 --- a/Sources/Datadog/Core/System/Time/ServerDateProvider.swift +++ b/Sources/Datadog/Core/System/Time/ServerDateProvider.swift @@ -27,7 +27,7 @@ internal class NTPServerDateProvider: ServerDateProvider { } func synchronize(with pool: String, completion: @escaping (TimeInterval?) -> Void) { - Clock.sync( + KronosClock.sync( from: pool, first: { [weak self] _, offset in self?.publisher.publishAsync(offset) @@ -49,6 +49,6 @@ internal class NTPServerDateProvider: ServerDateProvider { // `Kronos.sync` first loads the previous state from the `UserDefaults` if any. // We can invoke `Clock.now` to retrieve the stored offset. - publisher.publishAsync(Clock.now?.timeIntervalSinceNow) + publisher.publishAsync(KronosClock.now?.timeIntervalSinceNow) } } diff --git a/Sources/Datadog/Kronos/Clock.swift b/Sources/Datadog/Kronos/KronosClock.swift similarity index 81% rename from Sources/Datadog/Kronos/Clock.swift rename to Sources/Datadog/Kronos/KronosClock.swift index fade988687..5e227752cd 100644 --- a/Sources/Datadog/Kronos/Clock.swift +++ b/Sources/Datadog/Kronos/KronosClock.swift @@ -1,7 +1,7 @@ import Foundation /// Struct that has time + related metadata -public typealias AnnotatedTime = ( +internal typealias KronosAnnotatedTime = ( /// Time that is being annotated date: Date, @@ -18,14 +18,14 @@ public typealias AnnotatedTime = ( /// Example usage: /// /// ```swift -/// Clock.sync { date, offset in +/// KronosClock.sync { date, offset in /// print(date) /// } /// // (... later on ...) -/// print(Clock.now) +/// print(KronosClock.now) /// ``` -public struct Clock { - private static var stableTime: TimeFreeze? { +internal struct KronosClock { + private static var stableTime: KronosTimeFreeze? { didSet { self.storage.stableTime = self.stableTime } @@ -33,25 +33,25 @@ public struct Clock { /// Determines where the most current stable time is stored. Use TimeStoragePolicy.appGroup to share /// between your app and an extension. - public static var storage = TimeStorage(storagePolicy: .standard) + static var storage = KronosTimeStorage(storagePolicy: .standard) /// The most accurate timestamp that we have so far (nil if no synchronization was done yet) - public static var timestamp: TimeInterval? { + static var timestamp: TimeInterval? { return self.stableTime?.adjustedTimestamp } /// The most accurate date that we have so far (nil if no synchronization was done yet) - public static var now: Date? { + static var now: Date? { return self.annotatedNow?.date } /// Same as `now` except with analytic metadata about the time - public static var annotatedNow: AnnotatedTime? { + static var annotatedNow: KronosAnnotatedTime? { guard let stableTime = self.stableTime else { return nil } - return AnnotatedTime(date: Date(timeIntervalSince1970: stableTime.adjustedTimestamp), + return KronosAnnotatedTime(date: Date(timeIntervalSince1970: stableTime.adjustedTimestamp), timeSinceLastNtpSync: stableTime.timeSinceLastNtpSync) } @@ -65,15 +65,15 @@ public struct Clock { /// - parameter samples: The number of samples to be acquired from each server (default 4). /// - parameter completion: A closure that will be called after _all_ the NTP calls are finished. /// - parameter first: A closure that will be called after the first valid date is calculated. - public static func sync(from pool: String = "time.apple.com", samples: Int = 4, + static func sync(from pool: String = "time.apple.com", samples: Int = 4, first: ((Date, TimeInterval) -> Void)? = nil, completion: ((Date?, TimeInterval?) -> Void)? = nil) { self.loadFromDefaults() - NTPClient().query(pool: pool, numberOfSamples: samples) { offset, done, total in + KronosNTPClient().query(pool: pool, numberOfSamples: samples) { offset, done, total in if let offset = offset { - self.stableTime = TimeFreeze(offset: offset) + self.stableTime = KronosTimeFreeze(offset: offset) if done == 1, let now = self.now { first?(now, offset) @@ -88,7 +88,7 @@ public struct Clock { /// Resets all state of the monotonic clock. Note that you won't be able to access `now` until you `sync` /// again. - public static func reset() { + static func reset() { self.stableTime = nil } diff --git a/Sources/Datadog/Kronos/DNSResolver.swift b/Sources/Datadog/Kronos/KronosDNSResolver.swift similarity index 88% rename from Sources/Datadog/Kronos/DNSResolver.swift rename to Sources/Datadog/Kronos/KronosDNSResolver.swift index d0c2b9fa7a..1912a464c1 100644 --- a/Sources/Datadog/Kronos/DNSResolver.swift +++ b/Sources/Datadog/Kronos/KronosDNSResolver.swift @@ -3,8 +3,8 @@ import Foundation private let kCopyNoOperation = unsafeBitCast(0, to: CFAllocatorCopyDescriptionCallBack.self) private let kDefaultTimeout = 8.0 -final class DNSResolver { - private var completion: (([InternetAddress]) -> Void)? +internal final class KronosDNSResolver { + private var completion: (([KronosInternetAddress]) -> Void)? private var timer: Timer? private init() {} @@ -17,13 +17,13 @@ final class DNSResolver { /// - parameter completion: A completion block that will be called both on failure and success with a list /// of IPs. static func resolve(host: String, timeout: TimeInterval = kDefaultTimeout, - completion: @escaping ([InternetAddress]) -> Void) + completion: @escaping ([KronosInternetAddress]) -> Void) { let callback: CFHostClientCallBack = { host, _, _, info in guard let info = info else { return } - let retainedSelf = Unmanaged.fromOpaque(info) + let retainedSelf = Unmanaged.fromOpaque(info) let resolver = retainedSelf.takeUnretainedValue() resolver.timer?.invalidate() resolver.timer = nil @@ -37,13 +37,13 @@ final class DNSResolver { let IPs = (addresses.takeUnretainedValue() as NSArray) .compactMap { $0 as? NSData } - .compactMap(InternetAddress.init) + .compactMap(KronosInternetAddress.init) resolver.completion?(IPs) retainedSelf.release() } - let resolver = DNSResolver() + let resolver = KronosDNSResolver() resolver.completion = completion let retainedClosure = Unmanaged.passRetained(resolver).toOpaque() @@ -52,7 +52,7 @@ final class DNSResolver { let hostReference = CFHostCreateWithName(kCFAllocatorDefault, host as CFString).takeUnretainedValue() resolver.timer = Timer.scheduledTimer(timeInterval: timeout, target: resolver, - selector: #selector(DNSResolver.onTimeout), + selector: #selector(KronosDNSResolver.onTimeout), userInfo: hostReference, repeats: false) CFHostSetClient(hostReference, callback, &clientContext) diff --git a/Sources/Datadog/Kronos/Data+Bytes.swift b/Sources/Datadog/Kronos/KronosData+Bytes.swift similarity index 100% rename from Sources/Datadog/Kronos/Data+Bytes.swift rename to Sources/Datadog/Kronos/KronosData+Bytes.swift diff --git a/Sources/Datadog/Kronos/InternetAddress.swift b/Sources/Datadog/Kronos/KronosInternetAddress.swift similarity index 92% rename from Sources/Datadog/Kronos/InternetAddress.swift rename to Sources/Datadog/Kronos/KronosInternetAddress.swift index e5a29c3b71..734c45c29c 100644 --- a/Sources/Datadog/Kronos/InternetAddress.swift +++ b/Sources/Datadog/Kronos/KronosInternetAddress.swift @@ -4,7 +4,7 @@ import Foundation /// /// - IPv6: An Internet Address of type IPv6 (e.g.: '::1'). /// - IPv4: An Internet Address of type IPv4 (e.g.: '127.0.0.1'). -enum InternetAddress: Hashable { +internal enum KronosInternetAddress: Hashable { case ipv6(sockaddr_in6) case ipv4(sockaddr_in) @@ -42,10 +42,10 @@ enum InternetAddress: Hashable { let storage = sockaddr_storage.from(unsafeDataWithSockAddress: data) switch Int32(storage.ss_family) { case AF_INET: - self = storage.withUnsafeAddress { InternetAddress.ipv4($0.pointee) } + self = storage.withUnsafeAddress { KronosInternetAddress.ipv4($0.pointee) } case AF_INET6: - self = storage.withUnsafeAddress { InternetAddress.ipv6($0.pointee) } + self = storage.withUnsafeAddress { KronosInternetAddress.ipv6($0.pointee) } default: return nil @@ -71,7 +71,7 @@ enum InternetAddress: Hashable { } /// Compare InternetAddress(es) by making sure the host representation are equal. -func == (lhs: InternetAddress, rhs: InternetAddress) -> Bool { +internal func == (lhs: KronosInternetAddress, rhs: KronosInternetAddress) -> Bool { return lhs.host == rhs.host } diff --git a/Sources/Datadog/Kronos/NSTimer+ClosureKit.swift b/Sources/Datadog/Kronos/KronosNSTimer+ClosureKit.swift similarity index 86% rename from Sources/Datadog/Kronos/NSTimer+ClosureKit.swift rename to Sources/Datadog/Kronos/KronosNSTimer+ClosureKit.swift index 9c5d4c64af..fef5f2c6d6 100644 --- a/Sources/Datadog/Kronos/NSTimer+ClosureKit.swift +++ b/Sources/Datadog/Kronos/KronosNSTimer+ClosureKit.swift @@ -1,17 +1,17 @@ import Foundation -typealias CKTimerHandler = (Timer) -> Void +internal typealias CKTimerHandler = (Timer) -> Void /// Simple closure implementation on NSTimer scheduling. /// /// Example: /// /// ```swift -/// BlockTimer.scheduledTimer(withTimeInterval: 1.0) { timer in +/// KronosBlockTimer.scheduledTimer(withTimeInterval: 1.0) { timer in /// print("Did something after 1s!") /// } /// ``` -final class BlockTimer: NSObject { +internal final class KronosBlockTimer: NSObject { /// Creates and returns a block-based NSTimer object and schedules it on the current run loop. /// @@ -25,7 +25,7 @@ final class BlockTimer: NSObject { handler: @escaping CKTimerHandler) -> Timer { return Timer.scheduledTimer(timeInterval: interval, target: self, - selector: #selector(BlockTimer.invokeFrom(timer:)), + selector: #selector(KronosBlockTimer.invokeFrom(timer:)), userInfo: TimerClosureWrapper(handler: handler, repeats: repeated), repeats: repeated) } diff --git a/Sources/Datadog/Kronos/NTPClient.swift b/Sources/Datadog/Kronos/KronosNTPClient.swift similarity index 85% rename from Sources/Datadog/Kronos/NTPClient.swift rename to Sources/Datadog/Kronos/KronosNTPClient.swift index 322146eaf6..e0dc7542bd 100644 --- a/Sources/Datadog/Kronos/NTPClient.swift +++ b/Sources/Datadog/Kronos/KronosNTPClient.swift @@ -8,12 +8,12 @@ private let kMaximumResultDispersion = 10.0 private typealias ObjCCompletionType = @convention(block) (Data?, TimeInterval) -> Void /// Exception raised while sending / receiving NTP packets. -enum NTPNetworkError: Error { +internal enum KronosNTPNetworkError: Error { case noValidNTPPacketFound } /// NTP client session. -final class NTPClient { +internal final class KronosNTPClient { /// Query the all ips that resolve from the given pool. /// @@ -29,10 +29,10 @@ final class NTPClient { timeout: CFTimeInterval = kDefaultTimeout, progress: @escaping (TimeInterval?, Int, Int) -> Void) { - var servers: [InternetAddress: [NTPPacket]] = [:] + var servers: [KronosInternetAddress: [KronosNTPPacket]] = [:] var completed: Int = 0 - let queryIPAndStoreResult = { (address: InternetAddress, totalQueries: Int) -> Void in + let queryIPAndStoreResult = { (address: KronosInternetAddress, totalQueries: Int) -> Void in self.query(ip: address, port: port, version: version, timeout: timeout, numberOfSamples: numberOfSamples) { packet in @@ -55,7 +55,7 @@ final class NTPClient { } } - DNSResolver.resolve(host: pool) { addresses in + KronosDNSResolver.resolve(host: pool) { addresses in if addresses.count == 0 { return progress(nil, 0, 0) } @@ -75,9 +75,9 @@ final class NTPClient { /// - parameter timeout: Timeout on socket operations. /// - parameter numberOfSamples: The number of samples to be acquired from the server (default 4). /// - parameter completion: A closure that will be response PDU on success or nil on error. - func query(ip: InternetAddress, port: Int = 123, version: Int8 = 3, + func query(ip: KronosInternetAddress, port: Int = 123, version: Int8 = 3, timeout: CFTimeInterval = kDefaultTimeout, numberOfSamples: Int = kDefaultSamples, - completion: @escaping (NTPPacket?) -> Void) + completion: @escaping (KronosNTPPacket?) -> Void) { var timer: Timer? let bridgeCallback: ObjCCompletionType = { data, destinationTime in @@ -91,7 +91,7 @@ final class NTPClient { timer?.invalidate() guard - let data = data, let PDU = try? NTPPacket(data: data, destinationTime: destinationTime), + let data = data, let PDU = try? KronosNTPPacket(data: data, destinationTime: destinationTime), PDU.isValidResponse() else { completion(nil) @@ -108,7 +108,7 @@ final class NTPClient { completion: UnsafeMutableRawPointer(retainedCallback.toOpaque()) ) - timer = BlockTimer.scheduledTimer(withTimeInterval: timeout, repeated: true) { _ in + timer = KronosBlockTimer.scheduledTimer(withTimeInterval: timeout, repeated: true) { _ in bridgeCallback(nil, TimeInterval.infinity) retainedCallback.release() @@ -121,9 +121,9 @@ final class NTPClient { // MARK: - Private helpers (NTP Calculation) - private func offset(from responses: [[NTPPacket]]) throws -> TimeInterval { - let now = currentTime() - var bestResponses: [NTPPacket] = [] + private func offset(from responses: [[KronosNTPPacket]]) throws -> TimeInterval { + let now = kronosCurrentTime() + var bestResponses: [KronosNTPPacket] = [] for serverResponses in responses { let filtered = serverResponses .filter { abs($0.originTime - now) < kMaximumResultDispersion } @@ -135,7 +135,7 @@ final class NTPClient { } if bestResponses.count == 0 { - throw NTPNetworkError.noValidNTPPacketFound + throw KronosNTPNetworkError.noValidNTPPacketFound } bestResponses.sort { $0.offset < $1.offset } @@ -144,14 +144,14 @@ final class NTPClient { // MARK: - Private helpers (CFSocket) - private func sendAsyncUDPQuery(to ip: InternetAddress, port: Int, timeout: TimeInterval, + private func sendAsyncUDPQuery(to ip: KronosInternetAddress, port: Int, timeout: TimeInterval, completion: UnsafeMutableRawPointer) -> (CFRunLoopSource, CFSocket)? { signal(SIGPIPE, SIG_IGN) let callback: CFSocketCallBack = { socket, callbackType, _, data, info in if callbackType == .writeCallBack { - var packet = NTPPacket() + var packet = KronosNTPPacket() let PDU = packet.prepareToSend() as CFData CFSocketSendData(socket, nil, PDU, kDefaultTimeout) return @@ -163,7 +163,7 @@ final class NTPClient { CFSocketInvalidate(socket) - let destinationTime = currentTime() + let destinationTime = kronosCurrentTime() let retainedClosure = Unmanaged.fromOpaque(info) let completion = unsafeBitCast(retainedClosure.takeUnretainedValue(), to: ObjCCompletionType.self) diff --git a/Sources/Datadog/Kronos/NTPPacket.swift b/Sources/Datadog/Kronos/KronosNTPPacket.swift similarity index 82% rename from Sources/Datadog/Kronos/NTPPacket.swift rename to Sources/Datadog/Kronos/KronosNTPPacket.swift index ea7fdcb392..9e8b647223 100644 --- a/Sources/Datadog/Kronos/NTPPacket.swift +++ b/Sources/Datadog/Kronos/KronosNTPPacket.swift @@ -10,7 +10,7 @@ private let kMaximumDispersion = 100.0 /// Returns the current time in decimal EPOCH timestamp format. /// /// - returns: The current time in EPOCH timestamp format. -func currentTime() -> TimeInterval { +internal func kronosCurrentTime() -> TimeInterval { var current = timeval() let systemTimeError = gettimeofday(¤t, nil) != 0 assert(!systemTimeError, "system clock error: system time unavailable") @@ -18,20 +18,20 @@ func currentTime() -> TimeInterval { return Double(current.tv_sec) + Double(current.tv_usec) / 1_000_000 } -struct NTPPacket { +internal struct KronosNTPPacket { /// The leap indicator warning of an impending leap second to be inserted or deleted in the last /// minute of the current month. - let leap: LeapIndicator + let leap: KronosLeapIndicator /// Version Number (VN): This is a three-bit integer indicating the NTP version number, currently 3. let version: Int8 /// The current connection mode. - let mode: Mode + let mode: KronosMode /// Mode representing the stratum level of the local clock. - let stratum: Stratum + let stratum: KronosStratum /// Indicates the maximum interval between successive messages, in seconds to the nearest power of two. /// The values that normally appear in this field range from 6 to 10, inclusive. @@ -52,7 +52,7 @@ struct NTPPacket { let rootDispersion: TimeInterval /// Server or reference clock. This value is generated based on a reference identifier maintained by IANA. - let clockSource: ClockSource + let clockSource: KronosClockSource /// Time when the system clock was last set or corrected, in EPOCH timestamp format. let referenceTime: TimeInterval @@ -74,7 +74,7 @@ struct NTPPacket { /// - parameter transmitTime: Packet transmission timestamp. /// - parameter version: NTP protocol version. /// - parameter mode: Packet mode (client, server). - init(version: Int8 = 3, mode: Mode = .client) { + init(version: Int8 = 3, mode: KronosMode = .client) { self.version = version self.leap = .noWarning self.mode = mode @@ -94,25 +94,25 @@ struct NTPPacket { /// /// - parameter data: The PDU received from the NTP call. /// - parameter destinationTime: The time where the package arrived (client time) in EPOCH format. - /// - throws: NTPParsingError in case of an invalid response. + /// - throws: KronosNTPParsingError in case of an invalid response. init(data: Data, destinationTime: TimeInterval) throws { if data.count < 48 { - throw NTPParsingError.invalidNTPPDU("Invalid PDU length: \(data.count)") + throw KronosNTPParsingError.invalidNTPPDU("Invalid PDU length: \(data.count)") } - self.leap = LeapIndicator(rawValue: (data.getByte(at: 0) >> 6) & 0b11) ?? .noWarning + self.leap = KronosLeapIndicator(rawValue: (data.getByte(at: 0) >> 6) & 0b11) ?? .noWarning self.version = data.getByte(at: 0) >> 3 & 0b111 - self.mode = Mode(rawValue: data.getByte(at: 0) & 0b111) ?? .unknown - self.stratum = Stratum(value: data.getByte(at: 1)) + self.mode = KronosMode(rawValue: data.getByte(at: 0) & 0b111) ?? .unknown + self.stratum = KronosStratum(value: data.getByte(at: 1)) self.poll = data.getByte(at: 2) self.precision = data.getByte(at: 3) - self.rootDelay = NTPPacket.intervalFromNTPFormat(data.getUnsignedInteger(at: 4)) - self.rootDispersion = NTPPacket.intervalFromNTPFormat(data.getUnsignedInteger(at: 8)) - self.clockSource = ClockSource(stratum: self.stratum, sourceID: data.getUnsignedInteger(at: 12)) - self.referenceTime = NTPPacket.dateFromNTPFormat(data.getUnsignedLong(at: 16)) - self.originTime = NTPPacket.dateFromNTPFormat(data.getUnsignedLong(at: 24)) - self.receiveTime = NTPPacket.dateFromNTPFormat(data.getUnsignedLong(at: 32)) - self.transmitTime = NTPPacket.dateFromNTPFormat(data.getUnsignedLong(at: 40)) + self.rootDelay = KronosNTPPacket.intervalFromNTPFormat(data.getUnsignedInteger(at: 4)) + self.rootDispersion = KronosNTPPacket.intervalFromNTPFormat(data.getUnsignedInteger(at: 8)) + self.clockSource = KronosClockSource(stratum: self.stratum, sourceID: data.getUnsignedInteger(at: 12)) + self.referenceTime = KronosNTPPacket.dateFromNTPFormat(data.getUnsignedLong(at: 16)) + self.originTime = KronosNTPPacket.dateFromNTPFormat(data.getUnsignedLong(at: 24)) + self.receiveTime = KronosNTPPacket.dateFromNTPFormat(data.getUnsignedLong(at: 32)) + self.transmitTime = KronosNTPPacket.dateFromNTPFormat(data.getUnsignedLong(at: 40)) self.destinationTime = destinationTime } @@ -132,7 +132,7 @@ struct NTPPacket { data.append(unsignedLong: self.dateToNTPFormat(self.originTime)) data.append(unsignedLong: self.dateToNTPFormat(self.receiveTime)) - self.transmitTime = transmitTime ?? currentTime() + self.transmitTime = transmitTime ?? kronosCurrentTime() data.append(unsignedLong: self.dateToNTPFormat(self.transmitTime)) return data } @@ -144,7 +144,7 @@ struct NTPPacket { return (self.mode == .server || self.mode == .symmetricPassive) && self.leap != .alarm && self.stratum != .invalid && self.stratum != .unspecified && self.rootDispersion < kMaximumDispersion - && abs(currentTime() - self.originTime - self.delay) < kMaximumDelayDifference + && abs(kronosCurrentTime() - self.originTime - self.delay) < kMaximumDelayDifference } // MARK: - Private helpers @@ -186,7 +186,7 @@ struct NTPPacket { /// The roundtrip delay d and local clock offset t are defined as /// /// d = (T4 - T1) - (T3 - T2) t = ((T2 - T1) + (T3 - T4)) / 2. -extension NTPPacket { +extension KronosNTPPacket { /// Clocks offset in seconds. var offset: TimeInterval { diff --git a/Sources/Datadog/Kronos/NTPProtocol.swift b/Sources/Datadog/Kronos/KronosNTPProtocol.swift similarity index 92% rename from Sources/Datadog/Kronos/NTPProtocol.swift rename to Sources/Datadog/Kronos/KronosNTPProtocol.swift index c727012eea..10441650c1 100644 --- a/Sources/Datadog/Kronos/NTPProtocol.swift +++ b/Sources/Datadog/Kronos/KronosNTPProtocol.swift @@ -1,13 +1,13 @@ import Foundation /// Exception raised when the received PDU is invalid. -enum NTPParsingError: Error { +internal enum KronosNTPParsingError: Error { case invalidNTPPDU(String) } /// The leap indicator warning of an impending leap second to be inserted or deleted in the last minute of the /// current month. -enum LeapIndicator: Int8 { +internal enum KronosLeapIndicator: Int8 { case noWarning, sixtyOneSeconds, fiftyNineSeconds, alarm /// Human readable value of the leap warning. @@ -26,12 +26,12 @@ enum LeapIndicator: Int8 { } /// The connection mode. -enum Mode: Int8 { +internal enum KronosMode: Int8 { case reserved, symmetricActive, symmetricPassive, client, server, broadcast, reservedNTP, unknown } /// Mode representing the stratum level of the clock. -enum Stratum: Int8 { +internal enum KronosStratum: Int8 { case unspecified, primary, secondary, invalid init(value: Int8) { @@ -56,18 +56,18 @@ enum Stratum: Int8 { /// - ReferenceClock: Contains the sourceID and the description for the reference clock (stratum 1). /// - Debug(id): Contains the kiss code for debug purposes (stratum 0). /// - ReferenceIdentifier(id): The reference identifier of the server (stratum > 1). -enum ClockSource { +internal enum KronosClockSource { case referenceClock(id: UInt32, description: String) case debug(id: UInt32) case referenceIdentifier(id: UInt32) - init(stratum: Stratum, sourceID: UInt32) { + init(stratum: KronosStratum, sourceID: UInt32) { switch stratum { case .unspecified: self = .debug(id: sourceID) case .primary: - let (id, description) = ClockSource.description(fromID: sourceID) + let (id, description) = KronosClockSource.description(fromID: sourceID) self = .referenceClock(id: id, description: description) case .secondary, .invalid: diff --git a/Sources/Datadog/Kronos/TimeFreeze.swift b/Sources/Datadog/Kronos/KronosTimeFreeze.swift similarity index 86% rename from Sources/Datadog/Kronos/TimeFreeze.swift rename to Sources/Datadog/Kronos/KronosTimeFreeze.swift index 81ce763957..2699670ac0 100644 --- a/Sources/Datadog/Kronos/TimeFreeze.swift +++ b/Sources/Datadog/Kronos/KronosTimeFreeze.swift @@ -4,7 +4,7 @@ private let kUptimeKey = "Uptime" private let kTimestampKey = "Timestamp" private let kOffsetKey = "Offset" -struct TimeFreeze { +internal struct KronosTimeFreeze { private let uptime: TimeInterval private let timestamp: TimeInterval private let offset: TimeInterval @@ -17,18 +17,18 @@ struct TimeFreeze { /// The stable timestamp (calculated based on the uptime); note that this doesn't have sub-seconds /// precision. See `systemUptime()` for more information. var stableTimestamp: TimeInterval { - return (TimeFreeze.systemUptime() - self.uptime) + self.timestamp + return (KronosTimeFreeze.systemUptime() - self.uptime) + self.timestamp } /// Time interval between now and the time the NTP response represented by this TimeFreeze was received. var timeSinceLastNtpSync: TimeInterval { - return TimeFreeze.systemUptime() - uptime + return KronosTimeFreeze.systemUptime() - uptime } init(offset: TimeInterval) { self.offset = offset - self.timestamp = currentTime() - self.uptime = TimeFreeze.systemUptime() + self.timestamp = kronosCurrentTime() + self.uptime = KronosTimeFreeze.systemUptime() } init?(from dictionary: [String: TimeInterval]) { @@ -38,8 +38,8 @@ struct TimeFreeze { return nil } - let currentUptime = TimeFreeze.systemUptime() - let currentTimestamp = currentTime() + let currentUptime = KronosTimeFreeze.systemUptime() + let currentTimestamp = kronosCurrentTime() let currentBoot = currentUptime - currentTimestamp let previousBoot = uptime - timestamp if rint(currentBoot) - rint(previousBoot) != 0 { @@ -78,7 +78,7 @@ struct TimeFreeze { let bootTimeError = sysctl(&mib, u_int(mib.count), &bootTime, &size, nil, 0) != 0 assert(!bootTimeError, "system clock error: kernel boot time unavailable") - let now = currentTime() + let now = kronosCurrentTime() let uptime = Double(bootTime.tv_sec) + Double(bootTime.tv_usec) / 1_000_000 assert(now >= uptime, "inconsistent clock state: system time precedes boot time") diff --git a/Sources/Datadog/Kronos/TimeStorage.swift b/Sources/Datadog/Kronos/KronosTimeStorage.swift similarity index 83% rename from Sources/Datadog/Kronos/TimeStorage.swift rename to Sources/Datadog/Kronos/KronosTimeStorage.swift index 082c6a2ba4..e2da071e41 100644 --- a/Sources/Datadog/Kronos/TimeStorage.swift +++ b/Sources/Datadog/Kronos/KronosTimeStorage.swift @@ -1,7 +1,7 @@ import Foundation /// Defines where the user defaults are stored -public enum TimeStoragePolicy { +internal enum KronosTimeStoragePolicy { /// Uses `UserDefaults.Standard` case standard /// Attempts to use the specified App Group ID (which is the String) to access shared storage. @@ -11,7 +11,7 @@ public enum TimeStoragePolicy { /// /// - parameter appGroupID: The App Group ID that maps to a shared container for `UserDefaults`. If this /// is nil, the resulting instance will be `.standard` - public init(appGroupID: String?) { + init(appGroupID: String?) { if let appGroupID = appGroupID { self = .appGroup(appGroupID) } else { @@ -20,17 +20,17 @@ public enum TimeStoragePolicy { } } -/// Handles saving and retrieving instances of `TimeFreeze` for quick retrieval -public struct TimeStorage { +/// Handles saving and retrieving instances of `KronosTimeFreeze` for quick retrieval +internal struct KronosTimeStorage { private var userDefaults: UserDefaults private let kDefaultsKey = "KronosStableTime" /// The most recent stored `TimeFreeze`. Getting retrieves from the UserDefaults defined by the storage /// policy. Setting sets the value in UserDefaults - var stableTime: TimeFreeze? { + var stableTime: KronosTimeFreeze? { get { guard let stored = self.userDefaults.value(forKey: kDefaultsKey) as? [String: TimeInterval], - let previousStableTime = TimeFreeze(from: stored) else + let previousStableTime = KronosTimeFreeze(from: stored) else { return nil } @@ -50,7 +50,7 @@ public struct TimeStorage { /// Creates an instance /// /// - parameter storagePolicy: Defines the storage location of `UserDefaults` - public init(storagePolicy: TimeStoragePolicy) { + init(storagePolicy: KronosTimeStoragePolicy) { switch storagePolicy { case .standard: self.userDefaults = .standard diff --git a/Tests/DatadogTests/Datadog/Kronos/ClockTests.swift b/Tests/DatadogTests/Datadog/Kronos/KronosClockTests.swift similarity index 83% rename from Tests/DatadogTests/Datadog/Kronos/ClockTests.swift rename to Tests/DatadogTests/Datadog/Kronos/KronosClockTests.swift index 40c79d401e..f1169b3cc8 100644 --- a/Tests/DatadogTests/Datadog/Kronos/ClockTests.swift +++ b/Tests/DatadogTests/Datadog/Kronos/KronosClockTests.swift @@ -1,16 +1,16 @@ import XCTest @testable import Datadog -final class ClockTests: XCTestCase { +final class KronosClockTests: XCTestCase { override func setUp() { super.setUp() - Clock.reset() + KronosClock.reset() } func testFirst() { let expectation = self.expectation(description: "Clock sync calls first closure") - Clock.sync(first: { date, _ in + KronosClock.sync(first: { date, _ in XCTAssertNotNil(date) expectation.fulfill() }) @@ -20,7 +20,7 @@ final class ClockTests: XCTestCase { func testLast() { let expectation = self.expectation(description: "Clock sync calls last closure") - Clock.sync(completion: { date, offset in + KronosClock.sync(completion: { date, offset in XCTAssertNotNil(date) XCTAssertNotNil(offset) expectation.fulfill() @@ -32,7 +32,7 @@ final class ClockTests: XCTestCase { func testBoth() { let firstExpectation = self.expectation(description: "Clock sync calls first closure") let lastExpectation = self.expectation(description: "Clock sync calls last closure") - Clock.sync( + KronosClock.sync( first: { _, _ in firstExpectation.fulfill() }, completion: { _, _ in lastExpectation.fulfill() }) diff --git a/Tests/DatadogTests/Datadog/Kronos/DNSResolverTests.swift b/Tests/DatadogTests/Datadog/Kronos/KronosDNSResolverTests.swift similarity index 78% rename from Tests/DatadogTests/Datadog/Kronos/DNSResolverTests.swift rename to Tests/DatadogTests/Datadog/Kronos/KronosDNSResolverTests.swift index a98a946ba3..fbfc0dbe7a 100644 --- a/Tests/DatadogTests/Datadog/Kronos/DNSResolverTests.swift +++ b/Tests/DatadogTests/Datadog/Kronos/KronosDNSResolverTests.swift @@ -1,11 +1,11 @@ import XCTest @testable import Datadog -final class DNSResolverTests: XCTestCase { +final class KronosDNSResolverTests: XCTestCase { func testResolveOneIP() { let expectation = self.expectation(description: "Query host's DNS for a single IP") - DNSResolver.resolve(host: "test.com") { addresses in + KronosDNSResolver.resolve(host: "test.com") { addresses in XCTAssertEqual(addresses.count, 1) expectation.fulfill() } @@ -15,7 +15,7 @@ final class DNSResolverTests: XCTestCase { func testResolveMultipleIP() { let expectation = self.expectation(description: "Query host's DNS for multiple IPs") - DNSResolver.resolve(host: "pool.ntp.org") { addresses in + KronosDNSResolver.resolve(host: "pool.ntp.org") { addresses in XCTAssertGreaterThan(addresses.count, 1) expectation.fulfill() } @@ -25,7 +25,7 @@ final class DNSResolverTests: XCTestCase { func testResolveIPv6() { let expectation = self.expectation(description: "Query host's DNS that supports IPv6") - DNSResolver.resolve(host: "ipv6friday.org") { addresses in + KronosDNSResolver.resolve(host: "ipv6friday.org") { addresses in XCTAssertGreaterThan(addresses.count, 0) expectation.fulfill() } @@ -35,7 +35,7 @@ final class DNSResolverTests: XCTestCase { func testInvalidIP() { let expectation = self.expectation(description: "Query invalid host's DNS") - DNSResolver.resolve(host: "l33t.h4x") { addresses in + KronosDNSResolver.resolve(host: "l33t.h4x") { addresses in XCTAssertEqual(addresses.count, 0) expectation.fulfill() } @@ -45,7 +45,7 @@ final class DNSResolverTests: XCTestCase { func testTimeout() { let expectation = self.expectation(description: "DNS times out") - DNSResolver.resolve(host: "ip6.nl", timeout: 0) { addresses in + KronosDNSResolver.resolve(host: "ip6.nl", timeout: 0) { addresses in XCTAssertEqual(addresses.count, 0) expectation.fulfill() } @@ -56,7 +56,7 @@ final class DNSResolverTests: XCTestCase { func testTemporaryRunloopHandling() { let expectation = self.expectation(description: "Query works from async GCD queues") DispatchQueue(label: "Ephemeral DNS test queue").async { - DNSResolver.resolve(host: "lyft.com") { _ in + KronosDNSResolver.resolve(host: "lyft.com") { _ in expectation.fulfill() } } diff --git a/Tests/DatadogTests/Datadog/Kronos/NTPClientTests.swift b/Tests/DatadogTests/Datadog/Kronos/KronosNTPClientTests.swift similarity index 68% rename from Tests/DatadogTests/Datadog/Kronos/NTPClientTests.swift rename to Tests/DatadogTests/Datadog/Kronos/KronosNTPClientTests.swift index a3a42e57d3..18a711989d 100644 --- a/Tests/DatadogTests/Datadog/Kronos/NTPClientTests.swift +++ b/Tests/DatadogTests/Datadog/Kronos/KronosNTPClientTests.swift @@ -1,15 +1,15 @@ import XCTest @testable import Datadog -final class NTPClientTests: XCTestCase { +final class KronosNTPClientTests: XCTestCase { func testQueryIP() { let expectation = self.expectation(description: "NTPClient queries single IPs") - DNSResolver.resolve(host: "time.apple.com") { addresses in + KronosDNSResolver.resolve(host: "time.apple.com") { addresses in XCTAssertGreaterThan(addresses.count, 0) - NTPClient().query(ip: addresses.first!, version: 3, numberOfSamples: 1) { PDU in + KronosNTPClient().query(ip: addresses.first!, version: 3, numberOfSamples: 1) { PDU in XCTAssertNotNil(PDU) XCTAssertGreaterThanOrEqual(PDU!.version, 3) @@ -24,10 +24,10 @@ final class NTPClientTests: XCTestCase { func testQueryPool() { let expectation = self.expectation(description: "Offset from ref clock to local clock are accurate") - NTPClient().query(pool: "0.pool.ntp.org", numberOfSamples: 1, maximumServers: 1) { offset, _, _ in + KronosNTPClient().query(pool: "0.pool.ntp.org", numberOfSamples: 1, maximumServers: 1) { offset, _, _ in XCTAssertNotNil(offset) - NTPClient().query(pool: "0.pool.ntp.org", numberOfSamples: 1, maximumServers: 1) + KronosNTPClient().query(pool: "0.pool.ntp.org", numberOfSamples: 1, maximumServers: 1) { offset2, _, _ in XCTAssertNotNil(offset2) XCTAssertLessThan(abs(offset! - offset2!), 0.10) @@ -40,7 +40,7 @@ final class NTPClientTests: XCTestCase { func testQueryPoolWithIPv6() { let expectation = self.expectation(description: "NTPClient queries a pool that supports IPv6") - NTPClient().query(pool: "2.pool.ntp.org", numberOfSamples: 1, maximumServers: 1) { offset, _, _ in + KronosNTPClient().query(pool: "2.pool.ntp.org", numberOfSamples: 1, maximumServers: 1) { offset, _, _ in XCTAssertNotNil(offset) expectation.fulfill() } diff --git a/Tests/DatadogTests/Datadog/Kronos/NTPPacketTests.swift b/Tests/DatadogTests/Datadog/Kronos/KronosNTPPacketTests.swift similarity index 73% rename from Tests/DatadogTests/Datadog/Kronos/NTPPacketTests.swift rename to Tests/DatadogTests/Datadog/Kronos/KronosNTPPacketTests.swift index f7b7357c0e..e40c0d4401 100644 --- a/Tests/DatadogTests/Datadog/Kronos/NTPPacketTests.swift +++ b/Tests/DatadogTests/Datadog/Kronos/KronosNTPPacketTests.swift @@ -1,9 +1,9 @@ import XCTest @testable import Datadog -final class NTPPacketTests: XCTestCase { +final class KronosNTPPacketTests: XCTestCase { func testToData() { - var packet = NTPPacket() + var packet = KronosNTPPacket() let data = packet.prepareToSend(transmitTime: 1463303662.776552) XCTAssertEqual(data, Data(hex: "1b0004fa0001000000010000000000000000000000000000" + "00000000000000000000000000000000dae2bc6ec6cc1c00")!) @@ -11,18 +11,18 @@ final class NTPPacketTests: XCTestCase { func testParseInvalidData() { let network = Data(hex: "0badface")! - let PDU = try? NTPPacket(data: network, destinationTime: 0) + let PDU = try? KronosNTPPacket(data: network, destinationTime: 0) XCTAssertNil(PDU) } func testParseData() { let network = Data(hex: "1c0203e90000065700000a68ada2c09cdae2d084a5a76d5fdae2d3354a529000dae2d32b" + "b38bab46dae2d32bb38d9e00")! - let PDU = try? NTPPacket(data: network, destinationTime: 0) + let PDU = try? KronosNTPPacket(data: network, destinationTime: 0) XCTAssertEqual(PDU?.version, 3) - XCTAssertEqual(PDU?.leap, LeapIndicator.noWarning) - XCTAssertEqual(PDU?.mode, Mode.server) - XCTAssertEqual(PDU?.stratum, Stratum.secondary) + XCTAssertEqual(PDU?.leap, KronosLeapIndicator.noWarning) + XCTAssertEqual(PDU?.mode, KronosMode.server) + XCTAssertEqual(PDU?.stratum, KronosStratum.secondary) XCTAssertEqual(PDU?.poll, 3) XCTAssertEqual(PDU?.precision, -23) } @@ -30,7 +30,7 @@ final class NTPPacketTests: XCTestCase { func testParseTimeData() { let network = Data(hex: "1c0203e90000065700000a68ada2c09cdae2d084a5a76d5fdae2d3354a529000dae2d32b" + "b38bab46dae2d32bb38d9e00")! - let PDU = try? NTPPacket(data: network, destinationTime: 0) + let PDU = try? KronosNTPPacket(data: network, destinationTime: 0) XCTAssertEqual(PDU?.rootDelay, 0.0247650146484375) XCTAssertEqual(PDU?.rootDispersion, 0.0406494140625) XCTAssertEqual(PDU?.clockSource.ID, 2913124508) diff --git a/Tests/DatadogTests/Datadog/Kronos/TimeStorageTests.swift b/Tests/DatadogTests/Datadog/Kronos/KronosTimeStorageTests.swift similarity index 56% rename from Tests/DatadogTests/Datadog/Kronos/TimeStorageTests.swift rename to Tests/DatadogTests/Datadog/Kronos/KronosTimeStorageTests.swift index 3b3f3de2bc..e4b0ac0b06 100644 --- a/Tests/DatadogTests/Datadog/Kronos/TimeStorageTests.swift +++ b/Tests/DatadogTests/Datadog/Kronos/KronosTimeStorageTests.swift @@ -1,10 +1,10 @@ import XCTest @testable import Datadog -class TimeStoragePolicyTests: XCTestCase { +class KronosTimeStoragePolicyTests: XCTestCase { func testInitWithStringGivesAppGroupType() { - let group = TimeStoragePolicy(appGroupID: "com.test.something.mygreatapp") - if case TimeStoragePolicy.appGroup(_) = group { + let group = KronosTimeStoragePolicy(appGroupID: "com.test.something.mygreatapp") + if case KronosTimeStoragePolicy.appGroup(_) = group { XCTAssert(true) } else { XCTAssert(false) @@ -12,8 +12,8 @@ class TimeStoragePolicyTests: XCTestCase { } func testInitWithNIlGivesStandardType() { - let group = TimeStoragePolicy(appGroupID: nil) - if case TimeStoragePolicy.standard = group { + let group = KronosTimeStoragePolicy(appGroupID: nil) + if case KronosTimeStoragePolicy.standard = group { XCTAssert(true) } else { XCTAssert(false) @@ -23,8 +23,8 @@ class TimeStoragePolicyTests: XCTestCase { class TimeStorageTests: XCTestCase { func testStoringAndRetrievingTimeFreeze() { - var storage = TimeStorage(storagePolicy: .standard) - let sampleFreeze = TimeFreeze(offset: 5000.32423) + var storage = KronosTimeStorage(storagePolicy: .standard) + let sampleFreeze = KronosTimeFreeze(offset: 5000.32423) storage.stableTime = sampleFreeze let fromDefaults = storage.stableTime @@ -33,12 +33,12 @@ class TimeStorageTests: XCTestCase { } func testRetrievingTimeFreezeAfterReboot() { - let sampleFreeze = TimeFreeze(offset: 5000.32423) + let sampleFreeze = KronosTimeFreeze(offset: 5000.32423) var storedData = sampleFreeze.toDictionary() storedData["Uptime"] = storedData["Uptime"]! + 10 - let beforeRebootFreeze = TimeFreeze(from: sampleFreeze.toDictionary()) - let afterRebootFreeze = TimeFreeze(from: storedData) + let beforeRebootFreeze = KronosTimeFreeze(from: sampleFreeze.toDictionary()) + let afterRebootFreeze = KronosTimeFreeze(from: storedData) XCTAssertNil(afterRebootFreeze) XCTAssertNotNil(beforeRebootFreeze) } From 733c0e24c48c3ea2f484d51160610826423e4696 Mon Sep 17 00:00:00 2001 From: Maciek Grzybowski Date: Tue, 28 Dec 2021 18:56:47 +0100 Subject: [PATCH 5/7] RUMM-1744 Adjust Kronos code to pass our linter --- Sources/Datadog/Kronos/KronosClock.swift | 17 +++--- .../Datadog/Kronos/KronosDNSResolver.swift | 27 +++++++--- Sources/Datadog/Kronos/KronosData+Bytes.swift | 9 ++-- .../Kronos/KronosNSTimer+ClosureKit.swift | 19 ++++--- Sources/Datadog/Kronos/KronosNTPClient.swift | 54 ++++++++++--------- Sources/Datadog/Kronos/KronosNTPPacket.swift | 12 ++--- Sources/Datadog/Kronos/KronosTimeFreeze.swift | 6 +-- .../Datadog/Kronos/KronosTimeStorage.swift | 3 +- .../Datadog/Kronos/KronosClockTests.swift | 10 ++-- .../Kronos/KronosDNSResolverTests.swift | 1 - .../Datadog/Kronos/KronosNTPClientTests.swift | 26 ++++----- .../Datadog/Kronos/KronosNTPPacketTests.swift | 14 ++--- .../Kronos/KronosTimeStorageTests.swift | 4 +- 13 files changed, 112 insertions(+), 90 deletions(-) diff --git a/Sources/Datadog/Kronos/KronosClock.swift b/Sources/Datadog/Kronos/KronosClock.swift index 5e227752cd..28c1e0d9d8 100644 --- a/Sources/Datadog/Kronos/KronosClock.swift +++ b/Sources/Datadog/Kronos/KronosClock.swift @@ -2,7 +2,6 @@ import Foundation /// Struct that has time + related metadata internal typealias KronosAnnotatedTime = ( - /// Time that is being annotated date: Date, @@ -51,8 +50,10 @@ internal struct KronosClock { return nil } - return KronosAnnotatedTime(date: Date(timeIntervalSince1970: stableTime.adjustedTimestamp), - timeSinceLastNtpSync: stableTime.timeSinceLastNtpSync) + return KronosAnnotatedTime( + date: Date(timeIntervalSince1970: stableTime.adjustedTimestamp), + timeSinceLastNtpSync: stableTime.timeSinceLastNtpSync + ) } /// Syncs the clock using NTP. Note that the full synchronization could take a few seconds. The given @@ -65,10 +66,12 @@ internal struct KronosClock { /// - parameter samples: The number of samples to be acquired from each server (default 4). /// - parameter completion: A closure that will be called after _all_ the NTP calls are finished. /// - parameter first: A closure that will be called after the first valid date is calculated. - static func sync(from pool: String = "time.apple.com", samples: Int = 4, - first: ((Date, TimeInterval) -> Void)? = nil, - completion: ((Date?, TimeInterval?) -> Void)? = nil) - { + static func sync( + from pool: String = "time.apple.com", + samples: Int = 4, + first: ((Date, TimeInterval) -> Void)? = nil, + completion: ((Date?, TimeInterval?) -> Void)? = nil + ) { self.loadFromDefaults() KronosNTPClient().query(pool: pool, numberOfSamples: samples) { offset, done, total in diff --git a/Sources/Datadog/Kronos/KronosDNSResolver.swift b/Sources/Datadog/Kronos/KronosDNSResolver.swift index 1912a464c1..eda3fb80fd 100644 --- a/Sources/Datadog/Kronos/KronosDNSResolver.swift +++ b/Sources/Datadog/Kronos/KronosDNSResolver.swift @@ -16,9 +16,11 @@ internal final class KronosDNSResolver { /// - parameter timeout: The connection timeout. /// - parameter completion: A completion block that will be called both on failure and success with a list /// of IPs. - static func resolve(host: String, timeout: TimeInterval = kDefaultTimeout, - completion: @escaping ([KronosInternetAddress]) -> Void) - { + static func resolve( + host: String, + timeout: TimeInterval = kDefaultTimeout, + completion: @escaping ([KronosInternetAddress]) -> Void + ) { let callback: CFHostClientCallBack = { host, _, _, info in guard let info = info else { return @@ -47,13 +49,22 @@ internal final class KronosDNSResolver { resolver.completion = completion let retainedClosure = Unmanaged.passRetained(resolver).toOpaque() - var clientContext = CFHostClientContext(version: 0, info: UnsafeMutableRawPointer(retainedClosure), - retain: nil, release: nil, copyDescription: kCopyNoOperation) + var clientContext = CFHostClientContext( + version: 0, + info: UnsafeMutableRawPointer(retainedClosure), + retain: nil, + release: nil, + copyDescription: kCopyNoOperation + ) let hostReference = CFHostCreateWithName(kCFAllocatorDefault, host as CFString).takeUnretainedValue() - resolver.timer = Timer.scheduledTimer(timeInterval: timeout, target: resolver, - selector: #selector(KronosDNSResolver.onTimeout), - userInfo: hostReference, repeats: false) + resolver.timer = Timer.scheduledTimer( + timeInterval: timeout, + target: resolver, + selector: #selector(KronosDNSResolver.onTimeout), + userInfo: hostReference, + repeats: false + ) CFHostSetClient(hostReference, callback, &clientContext) CFHostScheduleWithRunLoop(hostReference, CFRunLoopGetMain(), CFRunLoopMode.commonModes.rawValue) diff --git a/Sources/Datadog/Kronos/KronosData+Bytes.swift b/Sources/Datadog/Kronos/KronosData+Bytes.swift index af6c64ca57..c4a73b27e4 100644 --- a/Sources/Datadog/Kronos/KronosData+Bytes.swift +++ b/Sources/Datadog/Kronos/KronosData+Bytes.swift @@ -1,7 +1,6 @@ import Foundation extension Data { - /// Creates an Data instance based on a hex string (example: "ffff" would be ). /// /// - parameter hex: The hex string without any spaces; should only have [0-9A-Fa-f]. @@ -31,7 +30,7 @@ extension Data { /// - returns: The byte located at position `index`. func getByte(at index: Int) -> Int8 { let data: Int8 = self.subdata(in: index ..< (index + 1)).withUnsafeBytes { rawPointer in - rawPointer.bindMemory(to: Int8.self).baseAddress!.pointee + rawPointer.bindMemory(to: Int8.self).baseAddress!.pointee // swiftlint:disable:this force_unwrapping } return data @@ -44,8 +43,8 @@ extension Data { /// /// - returns: The unsigned int located at position `index`. func getUnsignedInteger(at index: Int, bigEndian: Bool = true) -> UInt32 { - let data: UInt32 = self.subdata(in: index ..< (index + 4)).withUnsafeBytes { rawPointer in - rawPointer.bindMemory(to: UInt32.self).baseAddress!.pointee + let data: UInt32 = self.subdata(in: index ..< (index + 4)).withUnsafeBytes { rawPointer in + rawPointer.bindMemory(to: UInt32.self).baseAddress!.pointee // swiftlint:disable:this force_unwrapping } return bigEndian ? data.bigEndian : data.littleEndian @@ -59,7 +58,7 @@ extension Data { /// - returns: The unsigned long integer located at position `index`. func getUnsignedLong(at index: Int, bigEndian: Bool = true) -> UInt64 { let data: UInt64 = self.subdata(in: index ..< (index + 8)).withUnsafeBytes { rawPointer in - rawPointer.bindMemory(to: UInt64.self).baseAddress!.pointee + rawPointer.bindMemory(to: UInt64.self).baseAddress!.pointee // swiftlint:disable:this force_unwrapping } return bigEndian ? data.bigEndian : data.littleEndian diff --git a/Sources/Datadog/Kronos/KronosNSTimer+ClosureKit.swift b/Sources/Datadog/Kronos/KronosNSTimer+ClosureKit.swift index fef5f2c6d6..38f56ab146 100644 --- a/Sources/Datadog/Kronos/KronosNSTimer+ClosureKit.swift +++ b/Sources/Datadog/Kronos/KronosNSTimer+ClosureKit.swift @@ -12,7 +12,6 @@ internal typealias CKTimerHandler = (Timer) -> Void /// } /// ``` internal final class KronosBlockTimer: NSObject { - /// Creates and returns a block-based NSTimer object and schedules it on the current run loop. /// /// - parameter interval: The number of seconds between firings of the timer. @@ -21,18 +20,24 @@ internal final class KronosBlockTimer: NSObject { /// - parameter handler: The closure that the NSTimer fires. /// /// - returns: A new NSTimer object, configured according to the specified parameters. - class func scheduledTimer(withTimeInterval interval: TimeInterval, repeated: Bool = false, - handler: @escaping CKTimerHandler) -> Timer - { - return Timer.scheduledTimer(timeInterval: interval, target: self, + class func scheduledTimer( + withTimeInterval interval: TimeInterval, + repeated: Bool = false, + handler: @escaping CKTimerHandler + ) -> Timer { + return Timer.scheduledTimer( + timeInterval: interval, + target: self, selector: #selector(KronosBlockTimer.invokeFrom(timer:)), - userInfo: TimerClosureWrapper(handler: handler, repeats: repeated), repeats: repeated) + userInfo: TimerClosureWrapper(handler: handler, repeats: repeated), + repeats: repeated + ) } // MARK: Private methods @objc - class private func invokeFrom(timer: Timer) { + private class func invokeFrom(timer: Timer) { if let closureWrapper = timer.userInfo as? TimerClosureWrapper { closureWrapper.handler(timer) } diff --git a/Sources/Datadog/Kronos/KronosNTPClient.swift b/Sources/Datadog/Kronos/KronosNTPClient.swift index e0dc7542bd..0ac3dc7bb2 100644 --- a/Sources/Datadog/Kronos/KronosNTPClient.swift +++ b/Sources/Datadog/Kronos/KronosNTPClient.swift @@ -14,7 +14,6 @@ internal enum KronosNTPNetworkError: Error { /// NTP client session. internal final class KronosNTPClient { - /// Query the all ips that resolve from the given pool. /// /// - parameter pool: NTP pool that will be resolved into multiple NTP servers. @@ -24,18 +23,20 @@ internal final class KronosNTPClient { /// - parameter maximumServers: The maximum number of servers to be queried (default 5). /// - parameter timeout: The individual timeout for each of the NTP operations. /// - parameter completion: A closure that will be response PDU on success or nil on error. - func query(pool: String = "time.apple.com", version: Int8 = 3, port: Int = 123, - numberOfSamples: Int = kDefaultSamples, maximumServers: Int = kMaximumNTPServers, - timeout: CFTimeInterval = kDefaultTimeout, - progress: @escaping (TimeInterval?, Int, Int) -> Void) - { + func query( + pool: String = "time.apple.com", + version: Int8 = 3, + port: Int = 123, + numberOfSamples: Int = kDefaultSamples, + maximumServers: Int = kMaximumNTPServers, + timeout: CFTimeInterval = kDefaultTimeout, + progress: @escaping (TimeInterval?, Int, Int) -> Void + ) { var servers: [KronosInternetAddress: [KronosNTPPacket]] = [:] var completed: Int = 0 let queryIPAndStoreResult = { (address: KronosInternetAddress, totalQueries: Int) -> Void in - self.query(ip: address, port: port, version: version, timeout: timeout, - numberOfSamples: numberOfSamples) - { packet in + self.query(ip: address, port: port, version: version, timeout: timeout, numberOfSamples: numberOfSamples) { packet in defer { completed += 1 @@ -75,17 +76,20 @@ internal final class KronosNTPClient { /// - parameter timeout: Timeout on socket operations. /// - parameter numberOfSamples: The number of samples to be acquired from the server (default 4). /// - parameter completion: A closure that will be response PDU on success or nil on error. - func query(ip: KronosInternetAddress, port: Int = 123, version: Int8 = 3, - timeout: CFTimeInterval = kDefaultTimeout, numberOfSamples: Int = kDefaultSamples, - completion: @escaping (KronosNTPPacket?) -> Void) - { + func query( + ip: KronosInternetAddress, + port: Int = 123, + version: Int8 = 3, + timeout: CFTimeInterval = kDefaultTimeout, + numberOfSamples: Int = kDefaultSamples, + completion: @escaping (KronosNTPPacket?) -> Void + ) { var timer: Timer? let bridgeCallback: ObjCCompletionType = { data, destinationTime in defer { // If we still have samples left; we'll keep querying the same server if numberOfSamples > 1 { - self.query(ip: ip, port: port, version: version, timeout: timeout, - numberOfSamples: numberOfSamples - 1, completion: completion) + self.query(ip: ip, port: port, version: version, timeout: timeout, numberOfSamples: numberOfSamples - 1, completion: completion) } } @@ -104,8 +108,7 @@ internal final class KronosNTPClient { let callback = unsafeBitCast(bridgeCallback, to: AnyObject.self) let retainedCallback = Unmanaged.passRetained(callback) let sourceAndSocket = self.sendAsyncUDPQuery( - to: ip, port: port, timeout: timeout, - completion: UnsafeMutableRawPointer(retainedCallback.toOpaque()) + to: ip, port: port, timeout: timeout, completion: UnsafeMutableRawPointer(retainedCallback.toOpaque()) ) timer = KronosBlockTimer.scheduledTimer(withTimeInterval: timeout, repeated: true) { _ in @@ -144,9 +147,12 @@ internal final class KronosNTPClient { // MARK: - Private helpers (CFSocket) - private func sendAsyncUDPQuery(to ip: KronosInternetAddress, port: Int, timeout: TimeInterval, - completion: UnsafeMutableRawPointer) -> (CFRunLoopSource, CFSocket)? - { + private func sendAsyncUDPQuery( + to ip: KronosInternetAddress, + port: Int, + timeout: TimeInterval, + completion: UnsafeMutableRawPointer + ) -> (CFRunLoopSource, CFSocket)? { signal(SIGPIPE, SIG_IGN) let callback: CFSocketCallBack = { socket, callbackType, _, data, info in @@ -173,17 +179,15 @@ internal final class KronosNTPClient { } let types = CFSocketCallBackType.dataCallBack.rawValue | CFSocketCallBackType.writeCallBack.rawValue - var context = CFSocketContext(version: 0, info: completion, retain: nil, release: nil, - copyDescription: nil) + var context = CFSocketContext(version: 0, info: completion, retain: nil, release: nil, copyDescription: nil) guard let socket = CFSocketCreate(nil, ip.family, SOCK_DGRAM, IPPROTO_UDP, types, callback, &context), - CFSocketIsValid(socket) else - { + CFSocketIsValid(socket) else { return nil } let runLoopSource = CFSocketCreateRunLoopSource(kCFAllocatorDefault, socket, 0) CFRunLoopAddSource(CFRunLoopGetMain(), runLoopSource, CFRunLoopMode.commonModes) CFSocketConnectToAddress(socket, ip.addressData(withPort: port), timeout) - return (runLoopSource!, socket) + return (runLoopSource!, socket) // swiftlint:disable:this force_unwrapping } } diff --git a/Sources/Datadog/Kronos/KronosNTPPacket.swift b/Sources/Datadog/Kronos/KronosNTPPacket.swift index 9e8b647223..9b21eb1048 100644 --- a/Sources/Datadog/Kronos/KronosNTPPacket.swift +++ b/Sources/Datadog/Kronos/KronosNTPPacket.swift @@ -1,7 +1,7 @@ import Foundation /// Delta between system and NTP time -private let kEpochDelta = 2208988800.0 +private let kEpochDelta = 2_208_988_800.0 /// This is the maximum that we'll tolerate for the client's time vs self.delay private let kMaximumDelayDifference = 0.1 @@ -19,7 +19,6 @@ internal func kronosCurrentTime() -> TimeInterval { } internal struct KronosNTPPacket { - /// The leap indicator warning of an impending leap second to be inserted or deleted in the last /// minute of the current month. let leap: KronosLeapIndicator @@ -151,25 +150,25 @@ internal struct KronosNTPPacket { private func dateToNTPFormat(_ time: TimeInterval) -> UInt64 { let integer = UInt32(time + kEpochDelta) - let decimal = modf(time).1 * 4294967296.0 // 2 ^ 32 + let decimal = modf(time).1 * 4_294_967_296.0 // 2 ^ 32 return UInt64(integer) << 32 | UInt64(decimal) } private func intervalToNTPFormat(_ time: TimeInterval) -> UInt32 { let integer = UInt16(time) - let decimal = modf(time).1 * 65536 // 2 ^ 16 + let decimal = modf(time).1 * 65_536 // 2 ^ 16 return UInt32(integer) << 16 | UInt32(decimal) } private static func dateFromNTPFormat(_ time: UInt64) -> TimeInterval { let integer = Double(time >> 32) - let decimal = Double(time & 0xffffffff) / 4294967296.0 + let decimal = Double(time & 0xffffffff) / 4_294_967_296.0 return integer - kEpochDelta + decimal } private static func intervalFromNTPFormat(_ time: UInt32) -> TimeInterval { let integer = Double(time >> 16) - let decimal = Double(time & 0xffff) / 65536 + let decimal = Double(time & 0xffff) / 65_536 return integer + decimal } } @@ -187,7 +186,6 @@ internal struct KronosNTPPacket { /// /// d = (T4 - T1) - (T3 - T2) t = ((T2 - T1) + (T3 - T4)) / 2. extension KronosNTPPacket { - /// Clocks offset in seconds. var offset: TimeInterval { return ((self.receiveTime - self.originTime) + (self.transmitTime - self.destinationTime)) / 2.0 diff --git a/Sources/Datadog/Kronos/KronosTimeFreeze.swift b/Sources/Datadog/Kronos/KronosTimeFreeze.swift index 2699670ac0..89e9ed67a1 100644 --- a/Sources/Datadog/Kronos/KronosTimeFreeze.swift +++ b/Sources/Datadog/Kronos/KronosTimeFreeze.swift @@ -32,9 +32,9 @@ internal struct KronosTimeFreeze { } init?(from dictionary: [String: TimeInterval]) { - guard let uptime = dictionary[kUptimeKey], let timestamp = dictionary[kTimestampKey], - let offset = dictionary[kOffsetKey] else - { + guard let uptime = dictionary[kUptimeKey], + let timestamp = dictionary[kTimestampKey], + let offset = dictionary[kOffsetKey] else { return nil } diff --git a/Sources/Datadog/Kronos/KronosTimeStorage.swift b/Sources/Datadog/Kronos/KronosTimeStorage.swift index e2da071e41..595eda0469 100644 --- a/Sources/Datadog/Kronos/KronosTimeStorage.swift +++ b/Sources/Datadog/Kronos/KronosTimeStorage.swift @@ -30,8 +30,7 @@ internal struct KronosTimeStorage { var stableTime: KronosTimeFreeze? { get { guard let stored = self.userDefaults.value(forKey: kDefaultsKey) as? [String: TimeInterval], - let previousStableTime = KronosTimeFreeze(from: stored) else - { + let previousStableTime = KronosTimeFreeze(from: stored) else { return nil } diff --git a/Tests/DatadogTests/Datadog/Kronos/KronosClockTests.swift b/Tests/DatadogTests/Datadog/Kronos/KronosClockTests.swift index f1169b3cc8..505802c44e 100644 --- a/Tests/DatadogTests/Datadog/Kronos/KronosClockTests.swift +++ b/Tests/DatadogTests/Datadog/Kronos/KronosClockTests.swift @@ -2,7 +2,6 @@ import XCTest @testable import Datadog final class KronosClockTests: XCTestCase { - override func setUp() { super.setUp() KronosClock.reset() @@ -33,8 +32,13 @@ final class KronosClockTests: XCTestCase { let firstExpectation = self.expectation(description: "Clock sync calls first closure") let lastExpectation = self.expectation(description: "Clock sync calls last closure") KronosClock.sync( - first: { _, _ in firstExpectation.fulfill() }, - completion: { _, _ in lastExpectation.fulfill() }) + first: { _, _ in + firstExpectation.fulfill() + }, + completion: { _, _ in + lastExpectation.fulfill() + } + ) self.waitForExpectations(timeout: 20) } diff --git a/Tests/DatadogTests/Datadog/Kronos/KronosDNSResolverTests.swift b/Tests/DatadogTests/Datadog/Kronos/KronosDNSResolverTests.swift index fbfc0dbe7a..551c352670 100644 --- a/Tests/DatadogTests/Datadog/Kronos/KronosDNSResolverTests.swift +++ b/Tests/DatadogTests/Datadog/Kronos/KronosDNSResolverTests.swift @@ -2,7 +2,6 @@ import XCTest @testable import Datadog final class KronosDNSResolverTests: XCTestCase { - func testResolveOneIP() { let expectation = self.expectation(description: "Query host's DNS for a single IP") KronosDNSResolver.resolve(host: "test.com") { addresses in diff --git a/Tests/DatadogTests/Datadog/Kronos/KronosNTPClientTests.swift b/Tests/DatadogTests/Datadog/Kronos/KronosNTPClientTests.swift index 18a711989d..424f5fe2b8 100644 --- a/Tests/DatadogTests/Datadog/Kronos/KronosNTPClientTests.swift +++ b/Tests/DatadogTests/Datadog/Kronos/KronosNTPClientTests.swift @@ -2,21 +2,21 @@ import XCTest @testable import Datadog final class KronosNTPClientTests: XCTestCase { - func testQueryIP() { let expectation = self.expectation(description: "NTPClient queries single IPs") KronosDNSResolver.resolve(host: "time.apple.com") { addresses in XCTAssertGreaterThan(addresses.count, 0) - KronosNTPClient().query(ip: addresses.first!, version: 3, numberOfSamples: 1) { PDU in - XCTAssertNotNil(PDU) + KronosNTPClient() + .query(ip: addresses.first!, version: 3, numberOfSamples: 1) { PDU in + XCTAssertNotNil(PDU) - XCTAssertGreaterThanOrEqual(PDU!.version, 3) - XCTAssertTrue(PDU!.isValidResponse()) + XCTAssertGreaterThanOrEqual(PDU!.version, 3) + XCTAssertTrue(PDU!.isValidResponse()) - expectation.fulfill() - } + expectation.fulfill() + } } self.waitForExpectations(timeout: 10) @@ -27,12 +27,12 @@ final class KronosNTPClientTests: XCTestCase { KronosNTPClient().query(pool: "0.pool.ntp.org", numberOfSamples: 1, maximumServers: 1) { offset, _, _ in XCTAssertNotNil(offset) - KronosNTPClient().query(pool: "0.pool.ntp.org", numberOfSamples: 1, maximumServers: 1) - { offset2, _, _ in - XCTAssertNotNil(offset2) - XCTAssertLessThan(abs(offset! - offset2!), 0.10) - expectation.fulfill() - } + KronosNTPClient() + .query(pool: "0.pool.ntp.org", numberOfSamples: 1, maximumServers: 1) { offset2, _, _ in + XCTAssertNotNil(offset2) + XCTAssertLessThan(abs(offset! - offset2!), 0.10) + expectation.fulfill() + } } self.waitForExpectations(timeout: 10) diff --git a/Tests/DatadogTests/Datadog/Kronos/KronosNTPPacketTests.swift b/Tests/DatadogTests/Datadog/Kronos/KronosNTPPacketTests.swift index e40c0d4401..a3df6ec562 100644 --- a/Tests/DatadogTests/Datadog/Kronos/KronosNTPPacketTests.swift +++ b/Tests/DatadogTests/Datadog/Kronos/KronosNTPPacketTests.swift @@ -4,7 +4,7 @@ import XCTest final class KronosNTPPacketTests: XCTestCase { func testToData() { var packet = KronosNTPPacket() - let data = packet.prepareToSend(transmitTime: 1463303662.776552) + let data = packet.prepareToSend(transmitTime: 1_463_303_662.776_552) XCTAssertEqual(data, Data(hex: "1b0004fa0001000000010000000000000000000000000000" + "00000000000000000000000000000000dae2bc6ec6cc1c00")!) } @@ -31,11 +31,11 @@ final class KronosNTPPacketTests: XCTestCase { let network = Data(hex: "1c0203e90000065700000a68ada2c09cdae2d084a5a76d5fdae2d3354a529000dae2d32b" + "b38bab46dae2d32bb38d9e00")! let PDU = try? KronosNTPPacket(data: network, destinationTime: 0) - XCTAssertEqual(PDU?.rootDelay, 0.0247650146484375) - XCTAssertEqual(PDU?.rootDispersion, 0.0406494140625) - XCTAssertEqual(PDU?.clockSource.ID, 2913124508) - XCTAssertEqual(PDU?.referenceTime, 1463308804.6470859051) - XCTAssertEqual(PDU?.originTime, 1463309493.2903223038) - XCTAssertEqual(PDU?.receiveTime, 1463309483.7013499737) + XCTAssertEqual(PDU?.rootDelay, 0.024_765_014_648_437_5) + XCTAssertEqual(PDU?.rootDispersion, 0.040_649_414_062_5) + XCTAssertEqual(PDU?.clockSource.ID, 2_913_124_508) + XCTAssertEqual(PDU?.referenceTime, 1_463_308_804.647_085_905_1) + XCTAssertEqual(PDU?.originTime, 1_463_309_493.290_322_303_8) + XCTAssertEqual(PDU?.receiveTime, 1_463_309_483.701_349_973_7) } } diff --git a/Tests/DatadogTests/Datadog/Kronos/KronosTimeStorageTests.swift b/Tests/DatadogTests/Datadog/Kronos/KronosTimeStorageTests.swift index e4b0ac0b06..f832fbc2cf 100644 --- a/Tests/DatadogTests/Datadog/Kronos/KronosTimeStorageTests.swift +++ b/Tests/DatadogTests/Datadog/Kronos/KronosTimeStorageTests.swift @@ -24,7 +24,7 @@ class KronosTimeStoragePolicyTests: XCTestCase { class TimeStorageTests: XCTestCase { func testStoringAndRetrievingTimeFreeze() { var storage = KronosTimeStorage(storagePolicy: .standard) - let sampleFreeze = KronosTimeFreeze(offset: 5000.32423) + let sampleFreeze = KronosTimeFreeze(offset: 5_000.324_23) storage.stableTime = sampleFreeze let fromDefaults = storage.stableTime @@ -33,7 +33,7 @@ class TimeStorageTests: XCTestCase { } func testRetrievingTimeFreezeAfterReboot() { - let sampleFreeze = KronosTimeFreeze(offset: 5000.32423) + let sampleFreeze = KronosTimeFreeze(offset: 5_000.324_23) var storedData = sampleFreeze.toDictionary() storedData["Uptime"] = storedData["Uptime"]! + 10 From aa5279cc6146337f3b077032b901c77478244fcc Mon Sep 17 00:00:00 2001 From: Maciek Grzybowski Date: Wed, 29 Dec 2021 11:20:22 +0100 Subject: [PATCH 6/7] RUMM-1744 Add license header including 'MobileNativeFoundation' copyrights --- Sources/Datadog/Kronos/KronosClock.swift | 9 +++++++++ Sources/Datadog/Kronos/KronosDNSResolver.swift | 9 +++++++++ Sources/Datadog/Kronos/KronosData+Bytes.swift | 9 +++++++++ Sources/Datadog/Kronos/KronosInternetAddress.swift | 9 +++++++++ Sources/Datadog/Kronos/KronosNSTimer+ClosureKit.swift | 9 +++++++++ Sources/Datadog/Kronos/KronosNTPClient.swift | 9 +++++++++ Sources/Datadog/Kronos/KronosNTPPacket.swift | 9 +++++++++ Sources/Datadog/Kronos/KronosNTPProtocol.swift | 9 +++++++++ Sources/Datadog/Kronos/KronosTimeFreeze.swift | 9 +++++++++ Sources/Datadog/Kronos/KronosTimeStorage.swift | 9 +++++++++ Tests/DatadogTests/Datadog/Kronos/KronosClockTests.swift | 9 +++++++++ .../Datadog/Kronos/KronosDNSResolverTests.swift | 9 +++++++++ .../Datadog/Kronos/KronosNTPClientTests.swift | 9 +++++++++ .../Datadog/Kronos/KronosNTPPacketTests.swift | 9 +++++++++ .../Datadog/Kronos/KronosTimeStorageTests.swift | 9 +++++++++ 15 files changed, 135 insertions(+) diff --git a/Sources/Datadog/Kronos/KronosClock.swift b/Sources/Datadog/Kronos/KronosClock.swift index 28c1e0d9d8..e56299831e 100644 --- a/Sources/Datadog/Kronos/KronosClock.swift +++ b/Sources/Datadog/Kronos/KronosClock.swift @@ -1,3 +1,12 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2019-2020 Datadog, Inc. + * + * This file includes software developed by MobileNativeFoundation, https://mobilenativefoundation.org and altered by Datadog. + * Use of this source code is governed by Apache License 2.0 license: https://github.com/MobileNativeFoundation/Kronos/blob/main/LICENSE + */ + import Foundation /// Struct that has time + related metadata diff --git a/Sources/Datadog/Kronos/KronosDNSResolver.swift b/Sources/Datadog/Kronos/KronosDNSResolver.swift index eda3fb80fd..042de51de6 100644 --- a/Sources/Datadog/Kronos/KronosDNSResolver.swift +++ b/Sources/Datadog/Kronos/KronosDNSResolver.swift @@ -1,3 +1,12 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2019-2020 Datadog, Inc. + * + * This file includes software developed by MobileNativeFoundation, https://mobilenativefoundation.org and altered by Datadog. + * Use of this source code is governed by Apache License 2.0 license: https://github.com/MobileNativeFoundation/Kronos/blob/main/LICENSE + */ + import Foundation private let kCopyNoOperation = unsafeBitCast(0, to: CFAllocatorCopyDescriptionCallBack.self) diff --git a/Sources/Datadog/Kronos/KronosData+Bytes.swift b/Sources/Datadog/Kronos/KronosData+Bytes.swift index c4a73b27e4..64b6dace5a 100644 --- a/Sources/Datadog/Kronos/KronosData+Bytes.swift +++ b/Sources/Datadog/Kronos/KronosData+Bytes.swift @@ -1,3 +1,12 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2019-2020 Datadog, Inc. + * + * This file includes software developed by MobileNativeFoundation, https://mobilenativefoundation.org and altered by Datadog. + * Use of this source code is governed by Apache License 2.0 license: https://github.com/MobileNativeFoundation/Kronos/blob/main/LICENSE + */ + import Foundation extension Data { diff --git a/Sources/Datadog/Kronos/KronosInternetAddress.swift b/Sources/Datadog/Kronos/KronosInternetAddress.swift index 734c45c29c..9c912a87ff 100644 --- a/Sources/Datadog/Kronos/KronosInternetAddress.swift +++ b/Sources/Datadog/Kronos/KronosInternetAddress.swift @@ -1,3 +1,12 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2019-2020 Datadog, Inc. + * + * This file includes software developed by MobileNativeFoundation, https://mobilenativefoundation.org and altered by Datadog. + * Use of this source code is governed by Apache License 2.0 license: https://github.com/MobileNativeFoundation/Kronos/blob/main/LICENSE + */ + import Foundation /// This enum represents an internet address that can either be IPv4 or IPv6. diff --git a/Sources/Datadog/Kronos/KronosNSTimer+ClosureKit.swift b/Sources/Datadog/Kronos/KronosNSTimer+ClosureKit.swift index 38f56ab146..903cddb4fd 100644 --- a/Sources/Datadog/Kronos/KronosNSTimer+ClosureKit.swift +++ b/Sources/Datadog/Kronos/KronosNSTimer+ClosureKit.swift @@ -1,3 +1,12 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2019-2020 Datadog, Inc. + * + * This file includes software developed by MobileNativeFoundation, https://mobilenativefoundation.org and altered by Datadog. + * Use of this source code is governed by Apache License 2.0 license: https://github.com/MobileNativeFoundation/Kronos/blob/main/LICENSE + */ + import Foundation internal typealias CKTimerHandler = (Timer) -> Void diff --git a/Sources/Datadog/Kronos/KronosNTPClient.swift b/Sources/Datadog/Kronos/KronosNTPClient.swift index 0ac3dc7bb2..53cdad6cad 100644 --- a/Sources/Datadog/Kronos/KronosNTPClient.swift +++ b/Sources/Datadog/Kronos/KronosNTPClient.swift @@ -1,3 +1,12 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2019-2020 Datadog, Inc. + * + * This file includes software developed by MobileNativeFoundation, https://mobilenativefoundation.org and altered by Datadog. + * Use of this source code is governed by Apache License 2.0 license: https://github.com/MobileNativeFoundation/Kronos/blob/main/LICENSE + */ + import Foundation private let kDefaultTimeout = 6.0 diff --git a/Sources/Datadog/Kronos/KronosNTPPacket.swift b/Sources/Datadog/Kronos/KronosNTPPacket.swift index 9b21eb1048..3b0cb42658 100644 --- a/Sources/Datadog/Kronos/KronosNTPPacket.swift +++ b/Sources/Datadog/Kronos/KronosNTPPacket.swift @@ -1,3 +1,12 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2019-2020 Datadog, Inc. + * + * This file includes software developed by MobileNativeFoundation, https://mobilenativefoundation.org and altered by Datadog. + * Use of this source code is governed by Apache License 2.0 license: https://github.com/MobileNativeFoundation/Kronos/blob/main/LICENSE + */ + import Foundation /// Delta between system and NTP time diff --git a/Sources/Datadog/Kronos/KronosNTPProtocol.swift b/Sources/Datadog/Kronos/KronosNTPProtocol.swift index 10441650c1..45e0b44540 100644 --- a/Sources/Datadog/Kronos/KronosNTPProtocol.swift +++ b/Sources/Datadog/Kronos/KronosNTPProtocol.swift @@ -1,3 +1,12 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2019-2020 Datadog, Inc. + * + * This file includes software developed by MobileNativeFoundation, https://mobilenativefoundation.org and altered by Datadog. + * Use of this source code is governed by Apache License 2.0 license: https://github.com/MobileNativeFoundation/Kronos/blob/main/LICENSE + */ + import Foundation /// Exception raised when the received PDU is invalid. diff --git a/Sources/Datadog/Kronos/KronosTimeFreeze.swift b/Sources/Datadog/Kronos/KronosTimeFreeze.swift index 89e9ed67a1..856796cc98 100644 --- a/Sources/Datadog/Kronos/KronosTimeFreeze.swift +++ b/Sources/Datadog/Kronos/KronosTimeFreeze.swift @@ -1,3 +1,12 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2019-2020 Datadog, Inc. + * + * This file includes software developed by MobileNativeFoundation, https://mobilenativefoundation.org and altered by Datadog. + * Use of this source code is governed by Apache License 2.0 license: https://github.com/MobileNativeFoundation/Kronos/blob/main/LICENSE + */ + import Foundation private let kUptimeKey = "Uptime" diff --git a/Sources/Datadog/Kronos/KronosTimeStorage.swift b/Sources/Datadog/Kronos/KronosTimeStorage.swift index 595eda0469..d633ea1801 100644 --- a/Sources/Datadog/Kronos/KronosTimeStorage.swift +++ b/Sources/Datadog/Kronos/KronosTimeStorage.swift @@ -1,3 +1,12 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2019-2020 Datadog, Inc. + * + * This file includes software developed by MobileNativeFoundation, https://mobilenativefoundation.org and altered by Datadog. + * Use of this source code is governed by Apache License 2.0 license: https://github.com/MobileNativeFoundation/Kronos/blob/main/LICENSE + */ + import Foundation /// Defines where the user defaults are stored diff --git a/Tests/DatadogTests/Datadog/Kronos/KronosClockTests.swift b/Tests/DatadogTests/Datadog/Kronos/KronosClockTests.swift index 505802c44e..9aa9f735d5 100644 --- a/Tests/DatadogTests/Datadog/Kronos/KronosClockTests.swift +++ b/Tests/DatadogTests/Datadog/Kronos/KronosClockTests.swift @@ -1,3 +1,12 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2019-2020 Datadog, Inc. + * + * This file includes software developed by MobileNativeFoundation, https://mobilenativefoundation.org and altered by Datadog. + * Use of this source code is governed by Apache License 2.0 license: https://github.com/MobileNativeFoundation/Kronos/blob/main/LICENSE + */ + import XCTest @testable import Datadog diff --git a/Tests/DatadogTests/Datadog/Kronos/KronosDNSResolverTests.swift b/Tests/DatadogTests/Datadog/Kronos/KronosDNSResolverTests.swift index 551c352670..e7d07f83b1 100644 --- a/Tests/DatadogTests/Datadog/Kronos/KronosDNSResolverTests.swift +++ b/Tests/DatadogTests/Datadog/Kronos/KronosDNSResolverTests.swift @@ -1,3 +1,12 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2019-2020 Datadog, Inc. + * + * This file includes software developed by MobileNativeFoundation, https://mobilenativefoundation.org and altered by Datadog. + * Use of this source code is governed by Apache License 2.0 license: https://github.com/MobileNativeFoundation/Kronos/blob/main/LICENSE + */ + import XCTest @testable import Datadog diff --git a/Tests/DatadogTests/Datadog/Kronos/KronosNTPClientTests.swift b/Tests/DatadogTests/Datadog/Kronos/KronosNTPClientTests.swift index 424f5fe2b8..45df7a964e 100644 --- a/Tests/DatadogTests/Datadog/Kronos/KronosNTPClientTests.swift +++ b/Tests/DatadogTests/Datadog/Kronos/KronosNTPClientTests.swift @@ -1,3 +1,12 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2019-2020 Datadog, Inc. + * + * This file includes software developed by MobileNativeFoundation, https://mobilenativefoundation.org and altered by Datadog. + * Use of this source code is governed by Apache License 2.0 license: https://github.com/MobileNativeFoundation/Kronos/blob/main/LICENSE + */ + import XCTest @testable import Datadog diff --git a/Tests/DatadogTests/Datadog/Kronos/KronosNTPPacketTests.swift b/Tests/DatadogTests/Datadog/Kronos/KronosNTPPacketTests.swift index a3df6ec562..4b4b5d7595 100644 --- a/Tests/DatadogTests/Datadog/Kronos/KronosNTPPacketTests.swift +++ b/Tests/DatadogTests/Datadog/Kronos/KronosNTPPacketTests.swift @@ -1,3 +1,12 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2019-2020 Datadog, Inc. + * + * This file includes software developed by MobileNativeFoundation, https://mobilenativefoundation.org and altered by Datadog. + * Use of this source code is governed by Apache License 2.0 license: https://github.com/MobileNativeFoundation/Kronos/blob/main/LICENSE + */ + import XCTest @testable import Datadog diff --git a/Tests/DatadogTests/Datadog/Kronos/KronosTimeStorageTests.swift b/Tests/DatadogTests/Datadog/Kronos/KronosTimeStorageTests.swift index f832fbc2cf..2d06f14147 100644 --- a/Tests/DatadogTests/Datadog/Kronos/KronosTimeStorageTests.swift +++ b/Tests/DatadogTests/Datadog/Kronos/KronosTimeStorageTests.swift @@ -1,3 +1,12 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2019-2020 Datadog, Inc. + * + * This file includes software developed by MobileNativeFoundation, https://mobilenativefoundation.org and altered by Datadog. + * Use of this source code is governed by Apache License 2.0 license: https://github.com/MobileNativeFoundation/Kronos/blob/main/LICENSE + */ + import XCTest @testable import Datadog From 514e493acf6c11edc022fd8f2accd6c424e90266 Mon Sep 17 00:00:00 2001 From: Maciek Grzybowski Date: Wed, 29 Dec 2021 11:24:03 +0100 Subject: [PATCH 7/7] RUMM-1744 Update LICENSE-3rdparty.csv to include 'MobileNativeFoundation' --- LICENSE-3rdparty.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE-3rdparty.csv b/LICENSE-3rdparty.csv index 586d130941..cc3f578952 100644 --- a/LICENSE-3rdparty.csv +++ b/LICENSE-3rdparty.csv @@ -1,6 +1,6 @@ Component,Origin,License,Copyright import,io.opentracing,MIT,Copyright 2018 LightStep -import,com.Lyft.Kronos,Apache-2.0,Copyright (C) 2016 Lyft Inc. +import,com.Lyft.Kronos,Apache-2.0,Copyright (C) 2016 Lyft Inc. and MobileNativeFoundation import,PLCrashReporter,MIT,Copyright Microsoft Corporation import (tools),https://github.com/jpsim/SourceKitten,MIT,Copyright (c) 2014 JP Simard import (tools),https://github.com/apple/swift-argument-parser,Apache-2.0,(c) 2020 Apple Inc. and the Swift project authors