iOS web browser with a focus on security and privacy
at master 272 lines 7.5 kB view raw
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