iOS web browser with a focus on security and privacy
1/*
2 * Endless
3 * Copyright (c) 2014-2015 joshua stein <jcs@jcs.org>
4 *
5 * See LICENSE file for redistribution terms.
6 */
7
8#import "LocalNetworkChecker.h"
9
10#import <netinet/in.h>
11#import <netdb.h>
12#import <ifaddrs.h>
13#import <arpa/inet.h>
14
15@implementation LocalNetworkChecker
16
17static NSArray *localNets;
18static NSArray *local6Nets;
19static NSMutableDictionary *dnsCache;
20
21+ (void)clearCache
22{
23 if (dnsCache) {
24 [dnsCache removeAllObjects];
25 }
26 else {
27 dnsCache = [[NSMutableDictionary alloc] initWithCapacity:20];
28 }
29}
30
31/* iOS has its internal DNS cache, so this does not have to send out another request on the wire */
32+ (NSArray *)addressesForHostname:(NSString *)host {
33 if (!dnsCache) {
34 [[self class] clearCache];
35 }
36
37 id cached = [dnsCache objectForKey:[host lowercaseString]];
38 if (cached != nil) {
39 NSDictionary *dcache = (NSDictionary *)cached;
40 NSDate *t = [dcache objectForKey:@"time"];
41 if ([[NSDate date] timeIntervalSinceDate:t] < 30) {
42 return [dcache objectForKey:@"addresses"];
43 }
44 }
45
46 CFHostRef hostRef = CFHostCreateWithName(kCFAllocatorDefault, (__bridge CFStringRef)host);
47 if (!CFHostStartInfoResolution(hostRef, kCFHostAddresses, nil)) {
48 CFRelease(hostRef);
49 return nil;
50 }
51
52 CFArrayRef addressesRef = CFHostGetAddressing(hostRef, nil);
53 if (addressesRef == nil) {
54 CFRelease(hostRef);
55 return nil;
56 }
57
58 char ipAddress[INET6_ADDRSTRLEN];
59 NSMutableArray *addresses = [NSMutableArray array];
60 CFIndex numAddresses = CFArrayGetCount(addressesRef);
61 for (CFIndex currentIndex = 0; currentIndex < numAddresses; currentIndex++) {
62 struct sockaddr *address = (struct sockaddr *)CFDataGetBytePtr(CFArrayGetValueAtIndex(addressesRef, currentIndex));
63 if (address == nil) {
64 CFRelease(hostRef);
65 return nil;
66 }
67
68 if (getnameinfo(address, address->sa_len, ipAddress, INET6_ADDRSTRLEN, nil, 0, NI_NUMERICHOST) != 0) {
69 CFRelease(hostRef);
70 return nil;
71 }
72
73 [addresses addObject:[NSString stringWithCString:ipAddress encoding:NSASCIIStringEncoding]];
74 }
75 CFRelease(hostRef);
76
77 [dnsCache setValue:@{ @"addresses" : addresses, @"time" : [NSDate date] } forKey:[host lowercaseString]];
78
79 return addresses;
80}
81
82+ (BOOL)isHostOnLocalNet:(NSString *)host
83{
84 if (!localNets) {
85 NSMutableArray *tLocalNets = [[NSMutableArray alloc] init];
86 [@{
87 /* rfc6890 */
88 @"0.0.0.0" : @8,
89 @"10.0.0.0" : @8,
90 @"100.64.0.0" : @10,
91 @"127.0.0.0" : @8,
92 @"169.254.0.0" : @16,
93 @"172.16.0.0" : @12,
94 @"192.0.0.0" : @24,
95 @"192.0.2.0" : @24,
96 @"192.88.99.0" : @24,
97 @"192.168.0.0" : @16,
98 @"198.18.0.0" : @15,
99 @"198.51.100.0" : @24,
100 @"203.0.113.0" : @24,
101 @"224.0.0.0" : @4,
102 @"240.0.0.0" : @4,
103 } enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) {
104 struct in_addr addr;
105
106 if (inet_aton([key UTF8String], &addr) != 0) {
107 uint32_t ip = ntohl(addr.s_addr);
108 int cidr = [(NSNumber *)value intValue];
109 uint32_t last = ip + (uint32_t)pow(2, (32 - cidr)) - 1;
110
111 [tLocalNets addObject:@[ [NSNumber numberWithLongLong:ip], [NSNumber numberWithLongLong:last], key, value ]];
112 } else {
113 NSLog(@"[LocalNetworkChecker] failed parsing IP %@", key);
114 abort();
115 }
116 }];
117
118 localNets = [NSArray arrayWithArray:tLocalNets];
119 }
120
121 if (!local6Nets) {
122 NSMutableArray *tLocal6Nets = [[NSMutableArray alloc] init];
123 [@{
124 /* https://en.wikipedia.org/wiki/Martian_packet#IPv6 */
125 @"::" : @96,
126 @"::1" : @128,
127 @"::ffff:0:0" : @96,
128 @"100::" : @64,
129 @"2001:10::" : @28,
130 @"2001:db8::" : @32,
131 @"fc00::" : @7,
132 @"fe80::" : @10,
133 @"fec0::" : @10,
134 @"ff00::" : @8,
135 @"2001::" : @40,
136 @"2001:0:7f00::" : @40,
137 @"2001:0:a00::" : @40,
138 @"2001:0:a9fe::" : @48,
139 @"2001:0:ac10::" : @44,
140 @"2001:0:c000:200::" : @56,
141 @"2001:0:c000::" : @56,
142 @"2001:0:c0a8::" : @48,
143 @"2001:0:c612::" : @47,
144 @"2001:0:c633:6400::" : @56,
145 @"2001:0:cb00:7100::" : @56,
146 @"2001:0:e000::" : @36,
147 @"2001:0:f000::" : @36,
148 @"2001:0:ffff:ffff::" : @64,
149 @"2002::" : @24,
150 @"2002:7f00::" : @24,
151 @"2002:a00::" : @24,
152 @"2002:a9fe::" : @32,
153 @"2002:ac10::" : @28,
154 @"2002:c000:200::" : @40,
155 @"2002:c000::" : @40,
156 @"2002:c0a8::" : @32,
157 @"2002:c612::" : @31,
158 @"2002:c633:6400::" : @40,
159 @"2002:cb00:7100::" : @40,
160 @"2002:e000::" : @20,
161 @"2002:f000::" : @20,
162 @"2002:ffff:ffff::" : @48,
163 } enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) {
164 struct in6_addr addr;
165 struct in6_addr mask = { 0 }, network = { 0 }, broadcast = { 0 };
166 int i, j;
167
168 if (inet_pton(AF_INET6, [key UTF8String], &addr) != 0) {
169 for (i = [(NSNumber *)value intValue], j = 0; i > 0; i -= 8, j++) {
170 if (i >= 8)
171 mask.s6_addr[j] = 0xff;
172 else
173 mask.s6_addr[j] = (unsigned long)(0xffU << (8 - i));
174 }
175
176 for (i = 0; i < sizeof(struct in6_addr); i++)
177 network.s6_addr[i] = addr.s6_addr[i] & mask.s6_addr[i];
178
179 memcpy(&broadcast, &network, sizeof(struct in6_addr));
180
181 for (i = 0; i < sizeof(struct in6_addr); i++)
182 broadcast.s6_addr[i] |= ~mask.s6_addr[i];
183
184 [tLocal6Nets addObject:@[ [NSValue valueWithBytes:&network objCType:@encode(struct in6_addr)], [NSValue valueWithBytes:&broadcast objCType:@encode(struct in6_addr)], key, value ]];
185 } else {
186 NSLog(@"[LocalNetworkChecker] failed parsing IPv6 IP %@", key);
187 abort();
188 }
189 }];
190
191 local6Nets = [NSArray arrayWithArray:tLocal6Nets];
192 }
193
194 NSArray *ips = [[self class] addressesForHostname:host];
195 if (ips == nil)
196 return NO;
197
198 for (NSString *ip in ips) {
199 struct addrinfo hint, *res = NULL;
200 int ret, family;
201
202 memset(&hint, '\0', sizeof hint);
203
204 hint.ai_family = PF_UNSPEC;
205 hint.ai_flags = AI_NUMERICHOST;
206
207 ret = getaddrinfo([ip UTF8String], NULL, &hint, &res);
208 if (ret) {
209 NSLog(@"[LocalNetworkChecker] DNS returned invalid address \"%@\"", ip);
210 continue;
211 }
212
213 family = res->ai_family;
214 freeaddrinfo(res);
215
216 if (family == AF_INET) {
217 struct in_addr addr;
218
219 if (inet_aton([ip UTF8String], &addr) != 1) {
220 NSLog(@"[LocalNetworkChecker: failed parsing ip \"%@\"", ip);
221 continue;
222 }
223
224 uint32_t uip = ntohl(addr.s_addr);
225
226 for (NSArray *net in localNets) {
227 if (uip >= [((NSNumber *)net[0]) intValue] && uip <= [((NSNumber *)net[1]) intValue]) {
228#ifdef TRACE
229 NSLog(@"[LocalNetworkChecker] ip %@ is in local network %@/%@ (%@-%@)", ip, net[2], net[3], net[0], net[1]);
230#endif
231 return YES;
232 }
233 }
234 }
235 else if (family == AF_INET6) {
236 struct in6_addr addr;
237
238 if (inet_pton(AF_INET6, [ip UTF8String], &addr) != 1) {
239 NSLog(@"[LocalNetworkChecker: failed parsing ipv6 ip \"%@\"", ip);
240 continue;
241 }
242
243 for (NSArray *net in local6Nets) {
244 struct in6_addr network = { 0 }, broadcast = { 0 };
245
246 NSValue *n = [net objectAtIndex:0];
247 NSValue *b = [net objectAtIndex:1];
248 [n getValue:&network];
249 [b getValue:&broadcast];
250
251 for (int i = 0; i <= 16; i += 2) {
252 if (i == 16) {
253#ifdef TRACE
254 NSLog(@"[LocalNetworkChecker] ipv6 ip %@ is in %@/%@", ip, [net objectAtIndex:2], [net objectAtIndex:3]);
255#endif
256 return YES;
257 }
258
259 if (!((int)((addr.s6_addr[i] << 8) + addr.s6_addr[i + 1]) >= (int)((network.s6_addr[i] << 8) + network.s6_addr[i + 1]) &&
260 (int)((addr.s6_addr[i] << 8) + addr.s6_addr[i + 1]) <= (int)((broadcast.s6_addr[i] << 8) + broadcast.s6_addr[i + 1])))
261 break;
262 }
263 }
264 } else {
265 NSLog(@"[LocalNetworkChecker] invalid family %d for %@", family, ip);
266 }
267 }
268
269 return NO;
270}
271
272@end