/
NetworkClock.m
217 lines (177 loc) Β· 15.4 KB
/
NetworkClock.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
/*ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β NetworkClock.m β
β β
β Created by Gavin Eadie on Oct17/10 ... Copyright 2010-14 Ramsay Consulting. All rights reserved. β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ*/
#import <arpa/inet.h>
#import "NetworkClock.h"
#import "ntp-log.h"
@interface NetworkClock () {
NSMutableArray * timeAssociations;
NSArray * sortDescriptors;
NSSortDescriptor * dispersionSortDescriptor;
dispatch_queue_t associationDelegateQueue;
}
@end
#pragma mark -
#pragma mark N E T W O R K β’ C L O C K
/*ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β NetworkClock is a singleton class which will provide the best estimate of the difference in time β
β between the device's system clock and the time returned by a collection of time servers. β
β β
β The method <networkTime> returns an NSDate with the network time. β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ*/
@implementation NetworkClock
+ (instancetype) sharedNetworkClock {
static id sharedNetworkClockInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedNetworkClockInstance = [[self alloc] init];
});
return sharedNetworkClockInstance;
}
/*ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Return the offset to network-derived UTC. β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ*/
- (NSTimeInterval) networkOffset {
if ([timeAssociations count] == 0) return 0.0;
NSArray * sortedArray = [timeAssociations sortedArrayUsingDescriptors:sortDescriptors];
double timeInterval = 0.0;
short usefulCount = 0;
for (NetAssociation * timeAssociation in sortedArray) {
if (timeAssociation.active) {
if (timeAssociation.trusty) {
usefulCount++;
timeInterval = timeInterval + timeAssociation.offset;
// NSLog(@"[%@]: %f (%d)", timeAssociation.server, timeAssociation.offset*1000.0, usefulCount);
}
else {
NSLog(@"Clockβ’Drop: [%@]", timeAssociation.server);
if ([timeAssociations count] > 8) {
[timeAssociations removeObject:timeAssociation];
[timeAssociation finish];
}
}
if (usefulCount == 8) break; // use 8 best dispersions
}
}
if (usefulCount > 0) {
timeInterval = timeInterval / usefulCount;
// NSLog(@"timeIntervalSinceDeviceTime: %f (%d)", timeInterval*1000.0, usefulCount);
}
return timeInterval;
}
/*ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Return the device clock time adjusted for the offset to network-derived UTC. β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ*/
- (NSDate *) networkTime {
return [[NSDate date] dateByAddingTimeInterval:[self networkOffset]];
}
- (instancetype) init {
if (self = [super init]) {
/*ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Prepare a sort-descriptor to sort associations based on their dispersion, and then create an β
β array of empty associations to use ... β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ*/
sortDescriptors = @[[[NSSortDescriptor alloc] initWithKey:@"dispersion" ascending:YES]];
timeAssociations = [NSMutableArray arrayWithCapacity:100];
/*ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β .. and fill that array with the time hosts obtained from "ntp.hosts" .. β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ*/
[[[NSOperationQueue alloc] init] addOperation:[[NSInvocationOperation alloc]
initWithTarget:self
selector:@selector(createAssociations)
object:nil]];
}
return self;
}
/*ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Use the following time servers or, if it exists, read the "ntp.hosts" file from the application β
β resources and derive all the IP addresses referred to, remove any duplicates and create an β
β 'association' (individual host client) for each one. β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ*/
- (void) createAssociations {
NSArray * ntpDomains;
NSString * filePath = [[NSBundle mainBundle] pathForResource:@"ntp.hosts" ofType:@""];
if (nil == filePath) {
ntpDomains = @[@"0.pool.ntp.org",
@"0.uk.pool.ntp.org",
@"0.us.pool.ntp.org",
@"asia.pool.ntp.org",
@"europe.pool.ntp.org",
@"north-america.pool.ntp.org",
@"south-america.pool.ntp.org",
@"oceania.pool.ntp.org",
@"africa.pool.ntp.org"];
}
else {
NSString * fileData = [[NSString alloc] initWithData:[[NSFileManager defaultManager]
contentsAtPath:filePath]
encoding:NSUTF8StringEncoding];
ntpDomains = [fileData componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
}
/*ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β for each NTP service domain name in the 'ntp.hosts' file : "0.pool.ntp.org" etc ... β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ*/
NSMutableSet * hostAddresses = [NSMutableSet setWithCapacity:100];
for (NSString * ntpDomainName in ntpDomains) {
if ([ntpDomainName length] == 0 ||
[ntpDomainName characterAtIndex:0] == ' ' ||
[ntpDomainName characterAtIndex:0] == '#') {
continue;
}
/*ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β ... resolve the IP address of the named host : "0.pool.ntp.org" --> [123.45.67.89], ... β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ*/
CFHostRef ntpHostName = CFHostCreateWithName (nil, (__bridge CFStringRef)ntpDomainName);
if (nil == ntpHostName) {
NTP_Logging(@"CFHostCreateWithName <nil> for %@", ntpDomainName);
continue; // couldn't create 'host object' ...
}
CFStreamError nameError;
if (!CFHostStartInfoResolution (ntpHostName, kCFHostAddresses, &nameError)) {
NTP_Logging(@"CFHostStartInfoResolution error %i for %@", (int)nameError.error, ntpDomainName);
CFRelease(ntpHostName);
continue; // couldn't start resolution ...
}
Boolean nameFound;
CFArrayRef ntpHostAddrs = CFHostGetAddressing (ntpHostName, &nameFound);
if (!nameFound) {
NTP_Logging(@"CFHostGetAddressing: %@ NOT resolved", ntpHostName);
CFRelease(ntpHostName);
continue; // resolution failed ...
}
if (ntpHostAddrs == nil) {
NTP_Logging(@"CFHostGetAddressing: no addresses resolved for %@", ntpHostName);
CFRelease(ntpHostName);
continue; // NO addresses were resolved ...
}
/*ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β for each (sockaddr structure wrapped by a CFDataRef/NSData *) associated with the hostname, β
β drop the IP address string into a Set to remove duplicates. β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ*/
for (NSData * ntpHost in (__bridge NSArray *)ntpHostAddrs) {
[hostAddresses addObject:[GCDAsyncUdpSocket hostFromAddress:ntpHost]];
}
CFRelease(ntpHostName);
}
NTP_Logging(@"%@", hostAddresses); // all the addresses resolved
/*ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β ... now start one 'association' (network clock server) for each address. β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ*/
for (NSString * server in hostAddresses) {
NetAssociation * timeAssociation = [[NetAssociation alloc] initWithServerName:server];
[timeAssociations addObject:timeAssociation];
[timeAssociation enable]; // starts are randomized internally
}
}
/*ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Stop all the individual ntp clients associations .. β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ*/
- (void) finishAssociations {
for (NetAssociation * timeAssociation in timeAssociations) [timeAssociation finish];
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
#pragma mark -
#pragma mark I n t e r n a l β’ M e t h o d s
@end