]> git.saurik.com Git - apple/configd.git/blob - sctest/SCTestReachability.m
configd-963.30.1.tar.gz
[apple/configd.git] / sctest / SCTestReachability.m
1 /*
2 * Copyright (c) 2016, 2017 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 #import "SCTest.h"
25 #import "SCTestUtils.h"
26 #import <arpa/inet.h>
27 #import <NetworkExtension/NEPolicySession.h>
28 #import <network_information.h>
29
30 #define REACHABILITY_TEST_HOSTNAME "apple.com"
31
32 @interface SCTestReachability : SCTest
33 @property SCNetworkReachabilityRef target;
34 @property dispatch_queue_t callbackQ;
35 @end
36
37 @implementation SCTestReachability
38
39 + (NSString *)command
40 {
41 return @"reachability";
42 }
43
44 + (NSString *)commandDescription
45 {
46 return @"Tests the SCNetworkReachability code path";
47 }
48
49 - (instancetype)initWithOptions:(NSDictionary *)options
50 {
51 self = [super initWithOptions:options];
52 if (self) {
53 NSMutableDictionary *reachOptions;
54 if (self.options[kSCTestReachabilityInterface] != nil) {
55 reachOptions = [[NSMutableDictionary alloc] init];
56 [reachOptions setObject:self.options[kSCTestReachabilityInterface] forKey:(__bridge NSString *)kSCNetworkReachabilityOptionInterface];
57 if (self.options[kSCTestReachabilityHost] != nil) {
58 [reachOptions setObject:self.options[kSCTestReachabilityHost] forKey:(__bridge NSString *)kSCNetworkReachabilityOptionNodeName];
59 } else if (self.options[kSCTestReachabilityAddress] != nil) {
60 NSData *data;
61 NSString *addressString = self.options[kSCTestReachabilityAddress];
62 struct sockaddr *sa = _SC_string_to_sockaddr(addressString.UTF8String, AF_INET, NULL, 0);
63 if (sa == NULL) {
64 sa = _SC_string_to_sockaddr(addressString.UTF8String, AF_INET6, NULL, 0);
65 if (sa == NULL) {
66 SCTestLog("Invalid address");
67 ERR_EXIT;
68 }
69 }
70 data = [NSData dataWithBytes:sa length:(sa->sa_family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6)];
71 [reachOptions setObject:data forKey:(__bridge NSString *)kSCNetworkReachabilityOptionRemoteAddress];
72 }
73
74
75 _target = SCNetworkReachabilityCreateWithOptions(kCFAllocatorDefault, (__bridge CFDictionaryRef)reachOptions);
76 } else if (self.options[kSCTestReachabilityHost]) {
77 NSString *host = self.options[kSCTestReachabilityHost];
78 _target = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, host.UTF8String);
79 } else if (self.options[kSCTestReachabilityAddress]) {
80 NSString *addressString = self.options[kSCTestReachabilityAddress];
81 struct sockaddr *sa = _SC_string_to_sockaddr(addressString.UTF8String, AF_INET, NULL, 0);
82 if (sa == NULL) {
83 sa = _SC_string_to_sockaddr(addressString.UTF8String, AF_INET6, NULL, 0);
84 if (sa == NULL) {
85 SCTestLog("Invalid address");
86 ERR_EXIT;
87 }
88 }
89 _target = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, sa);
90 }
91 }
92 return self;
93 }
94
95 - (void)dealloc
96 {
97 if (self.target != NULL) {
98 CFRelease(self.target);
99 self.target = NULL;
100 }
101 }
102
103 void
104 myReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void *info)
105 {
106 #pragma unused(target)
107 #pragma unused(info)
108 struct tm tm_now;
109 struct timeval tv_now;
110 (void)gettimeofday(&tv_now, NULL);
111 (void)localtime_r(&tv_now.tv_sec, &tm_now);
112 SCTestLog("%2d:%02d:%02d.%03d Reachability changed: %#x", tm_now.tm_hour, tm_now.tm_min, tm_now.tm_sec, tv_now.tv_usec / 1000, flags);
113 }
114
115 - (void)start
116 {
117 if (self.options[kSCTestReachabilityHost] != nil && self.options[kSCTestReachabilityAddress] != nil) {
118 SCTestLog("Please specify either a host or address");
119 ERR_EXIT;
120 }
121
122 if (self.options[kSCTestReachabilityWatch]) {
123 self.callbackQ = dispatch_queue_create("SCTestReachability callback queue", NULL);
124 SCNetworkReachabilitySetCallback(self.target, myReachabilityCallback, NULL);
125 SCNetworkReachabilitySetDispatchQueue(self.target, self.callbackQ);
126 } else {
127 SCNetworkReachabilityFlags flags;
128 SCNetworkReachabilityGetFlags(self.target, &flags);
129 SCTestLog("Reachability: %#x", flags);
130 [self cleanupAndExitWithErrorCode:0];
131 }
132 }
133
134 - (void)cleanupAndExitWithErrorCode:(int)error
135 {
136 [super cleanupAndExitWithErrorCode:error];
137 }
138
139 - (NSString *)primaryInterfaceName
140 {
141 const char *name;
142 NSString *nsName;
143 nwi_ifstate_t ifstate;
144 nwi_state_t nwi;
145
146 nwi = nwi_state_copy();
147 ifstate = nwi_state_get_first_ifstate(nwi, AF_INET);
148 if (ifstate == NULL) {
149 ifstate = nwi_state_get_first_ifstate(nwi, AF_INET6);
150 if (ifstate == NULL) {
151 // May be we are in airplane mode
152 SCTestLog("No primary interface found");
153 return nil;
154 }
155 }
156
157 name = nwi_ifstate_get_ifname(ifstate);
158 nsName = [NSString stringWithCString:name encoding:NSUTF8StringEncoding];
159 nwi_state_release(nwi);
160
161 return nsName;
162 }
163
164 - (BOOL)setup
165 {
166 return YES;
167 }
168
169 - (BOOL)unitTest
170 {
171 if(![self setup]) {
172 return NO;
173 }
174
175 BOOL allUnitTestsPassed = YES;
176
177 allUnitTestsPassed &= [self unitTestBasicReachabilityCheck];
178 allUnitTestsPassed &= [self unitTestReachabilityWithPolicy];
179 allUnitTestsPassed &= [self unitTestScopedReachabilityWithPolicy];
180
181 if(![self tearDown]) {
182 return NO;
183 }
184
185 return allUnitTestsPassed;
186 }
187
188 - (BOOL)unitTestBasicReachabilityCheck
189 {
190 SCNetworkReachabilityFlags flags;
191 SCTestReachability *test;
192
193 test = [[SCTestReachability alloc] initWithOptions:self.options];
194 if ([test primaryInterfaceName] == nil) {
195 return YES;
196 }
197
198 if (test.target == NULL) {
199 NSDictionary *options = @{kSCTestReachabilityHost:@REACHABILITY_TEST_HOSTNAME};
200 test = [[SCTestReachability alloc] initWithOptions:options];
201 }
202
203
204 SCNetworkReachabilityGetFlags(test.target, &flags);
205 if (flags == 0) {
206 SCTestLog("Reachability reported not reachable");
207 return NO;
208 }
209
210 SCTestLog("Verified that basic reachability check succeeds");
211 return YES;
212 }
213
214 - (BOOL)unitTestReachabilityWithPolicy
215 {
216 NEPolicyCondition *condition1;
217 NEPolicyCondition *condition2;
218 SCNetworkReachabilityFlags flags;
219 BOOL ok;
220 NEPolicy *policy;
221 NSUInteger policyID;
222 NEPolicyResult *result;
223 NEPolicySession *session;
224 SCTestReachability *test;
225
226 test = [[SCTestReachability alloc] initWithOptions:self.options];
227 if ([test primaryInterfaceName] == nil) {
228 return YES;
229 }
230
231 if (test.target == NULL) {
232 NSDictionary *options = @{kSCTestReachabilityHost:@REACHABILITY_TEST_HOSTNAME};
233 test = [[SCTestReachability alloc] initWithOptions:options];
234 }
235
236 SCNetworkReachabilityGetFlags(test.target, &flags);
237 if (flags == 0) {
238 SCTestLog("Reachability reported not reachable");
239 return NO;
240 }
241
242 session = [[NEPolicySession alloc] init];
243 if (session == nil) {
244 SCTestLog("Failed to create NEPolicySession");
245 return NO;
246 }
247
248 result = [NEPolicyResult drop];
249 condition1 = [NEPolicyCondition allInterfaces];
250 condition2 = [NEPolicyCondition effectivePID:getpid()];
251 policy = [[NEPolicy alloc ] initWithOrder:10 result:result conditions:@[condition1, condition2]];
252 policyID = [session addPolicy:policy];
253 if (policyID == 0) {
254 SCTestLog("Failed to add policy");
255 return NO;
256 }
257
258 ok = [session apply];
259 if (!ok) {
260 SCTestLog("Failed to apply policy");
261 return NO;
262 }
263
264 SCNetworkReachabilityGetFlags(test.target, &flags);
265 if (flags != 0) {
266 SCTestLog("Reachability reported as reachable, in presence of a drop policy");
267 return NO;
268 }
269
270 ok = [session removeAllPolicies];
271 if (!ok) {
272 SCTestLog("Failed to remove policies from session");
273 return NO;
274 }
275
276 ok = [session apply];
277 if (!ok) {
278 SCTestLog("Failed to apply policy");
279 return NO;
280 }
281
282 SCNetworkReachabilityGetFlags(test.target, &flags);
283 if (flags == 0) {
284 SCTestLog("Reachability reported as not reachable, in absence of a drop policy");
285 return NO;
286 }
287
288 SCTestLog("Verified that SCNetworkReachability reports reachability corresponding to the NECP Policies");
289 return YES;
290 }
291
292 - (BOOL)unitTestScopedReachabilityWithPolicy
293 {
294 NEPolicyCondition *condition1;
295 NEPolicyCondition *condition2;
296 SCNetworkReachabilityFlags flags;
297 BOOL ok;
298 NEPolicy *policy;
299 NSUInteger policyID;
300 NSString *primaryInterface;
301 NEPolicyResult *result;
302 NEPolicySession *session;
303 SCTestReachability *test;
304
305 test = [[SCTestReachability alloc] initWithOptions:self.options];
306 primaryInterface = [test primaryInterfaceName];
307 if (primaryInterface == nil) {
308 return YES;
309 }
310
311 if (test.target == NULL) {
312 NSDictionary *options = @{kSCTestReachabilityHost:@REACHABILITY_TEST_HOSTNAME,
313 kSCTestReachabilityInterface:primaryInterface};
314 test = [[SCTestReachability alloc] initWithOptions:options];
315 }
316
317 SCNetworkReachabilityGetFlags(test.target, &flags);
318 if (flags == 0) {
319 SCTestLog("Reachability reported not reachable");
320 return NO;
321 }
322
323 session = [[NEPolicySession alloc] init];
324 if (session == nil) {
325 SCTestLog("Failed to create NEPolicySession");
326 return NO;
327 }
328
329 result = [NEPolicyResult drop];
330 condition1 = [NEPolicyCondition scopedInterface:primaryInterface];
331 condition2 = [NEPolicyCondition effectivePID:getpid()];
332 policy = [[NEPolicy alloc ] initWithOrder:10 result:result conditions:@[condition1, condition2]];
333 policyID = [session addPolicy:policy];
334 if (policyID == 0) {
335 SCTestLog("Failed to add policy");
336 return NO;
337 }
338
339 ok = [session apply];
340 if (!ok) {
341 SCTestLog("Failed to apply policy");
342 return NO;
343 }
344
345 SCNetworkReachabilityGetFlags(test.target, &flags);
346 if (flags != 0) {
347 SCTestLog("Reachability reported as reachable, in presence of a drop policy");
348 return NO;
349 }
350
351 ok = [session removeAllPolicies];
352 if (!ok) {
353 SCTestLog("Failed to remove policies from session");
354 return NO;
355 }
356
357 ok = [session apply];
358 if (!ok) {
359 SCTestLog("Failed to apply policy");
360 return NO;
361 }
362
363 SCNetworkReachabilityGetFlags(test.target, &flags);
364 if (flags == 0) {
365 SCTestLog("Reachability reported as not reachable, in absence of a drop policy");
366 return NO;
367 }
368
369 SCTestLog("Verified that SCNetworkReachability for scoped queries report reachability corresponding to the NECP Policies");
370 return YES;
371 }
372
373 - (BOOL)tearDown
374 {
375 return YES;
376 }
377
378 @end