├── .gitignore ├── LICENSE ├── PPDNSMapping ├── PPDNSMapping.h ├── PPDNSMappingManager.h ├── PPDNSMappingManager.m ├── PPDNSReporter.h ├── PPDNSReporter.m ├── PPDomainRequestResult.h ├── PPDomainRequestResult.m ├── PPIPValidator.h ├── PPIPValidator.m ├── model │ ├── PPMappingNode.h │ ├── PPMappingNode.m │ ├── PPResolvedUrl.h │ └── PPResolvedUrl.m └── util │ ├── PPMappingUtil.h │ ├── PPMappingUtil.m │ ├── PPWeakTimer.h │ └── PPWeakTimer.m ├── PPDNSMappingDemo.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcuserdata │ └── gaofeng.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ ├── PPDNSMappingDemo.xcscheme │ └── xcschememanagement.plist ├── PPDNSMappingDemo ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Info.plist ├── ViewController.h ├── ViewController.m ├── defaultMapping.txt └── main.m ├── PPDNSMappingDemoTests ├── Info.plist └── PPDNSMappingDemoTests.m ├── PPDNSMappingDemoUITests ├── Info.plist └── PPDNSMappingDemoUITests.m └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata 19 | 20 | ## Other 21 | *.xccheckout 22 | *.moved-aside 23 | *.xcuserstate 24 | *.xcscmblueprint 25 | 26 | ## Obj-C/Swift specific 27 | *.hmap 28 | *.ipa 29 | 30 | # CocoaPods 31 | # 32 | # We recommend against adding the Pods directory to your .gitignore. However 33 | # you should judge for yourself, the pros and cons are mentioned at: 34 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 35 | # 36 | # Pods/ 37 | 38 | # Carthage 39 | # 40 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 41 | # Carthage/Checkouts 42 | 43 | Carthage/Build 44 | 45 | # fastlane 46 | # 47 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 48 | # screenshots whenever they are needed. 49 | # For more information about the recommended setup visit: 50 | # https://github.com/fastlane/fastlane/blob/master/docs/Gitignore.md 51 | 52 | fastlane/report.xml 53 | fastlane/screenshots 54 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 gao feng 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /PPDNSMapping/PPDNSMapping.h: -------------------------------------------------------------------------------- 1 | // 2 | // PPDNSMapping.h 3 | // PPDNSMappingDemo 4 | // 5 | // Created by gao feng on 16/1/19. 6 | // Copyright © 2016年 gao feng. All rights reserved. 7 | // 8 | 9 | #ifndef PPDNSMapping_h 10 | #define PPDNSMapping_h 11 | 12 | #import "PPDNSMappingManager.h" 13 | #import "PPDomainRequestResult.h" 14 | #import "PPResolvedUrl.h" 15 | 16 | #endif /* PPDNSMapping_h */ 17 | -------------------------------------------------------------------------------- /PPDNSMapping/PPDNSMappingManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // PPDNSMappingManager.h 3 | // PPDNSMappingDemo 4 | // 5 | // Created by gao feng on 16/1/19. 6 | // Copyright © 2016年 gao feng. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class PPResolvedUrl; 12 | 13 | #define Notif_DomainRequestResult @"Notif_DomainRequestResult" //result from application layer 14 | #define Notif_IPValidateResult @"Notif_IPValidateResult" //result from ipvalidator 15 | 16 | @interface PPDNSMappingManager : NSObject 17 | 18 | @property (nonatomic, strong) NSString* diskSavePath; 19 | 20 | + (instancetype)sharedInstance; 21 | 22 | //local file with default mapping 23 | - (void)setDefaultLocalMapping:(NSString*)filePath; 24 | 25 | //read mapping from previous server response 26 | - (void)readMappingFromDisk; 27 | 28 | //interval should be larger than 3s 29 | - (void)startSyncTimerWithInterval:(NSTimeInterval)interval withSrcUrl:(NSString*)srcUrl; 30 | 31 | //resolve url 32 | - (PPResolvedUrl*)resolveUrl:(NSURL*)originalUrl; 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /PPDNSMapping/PPDNSMappingManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // PPDNSMappingManager.m 3 | // PPDNSMappingDemo 4 | // 5 | // Created by gao feng on 16/1/19. 6 | // Copyright © 2016年 gao feng. All rights reserved. 7 | // 8 | 9 | #import "PPDNSMappingManager.h" 10 | #import "PPWeakTimer.h" 11 | #import "Reachability.h" 12 | #import "PPDomainRequestResult.h" 13 | #import "PPResolvedUrl.h" 14 | #import "PPMappingUtil.h" 15 | #import "PPMappingNode.h" 16 | #import 17 | #import "PPIPValidator.h" 18 | #import "PPDNSReporter.h" 19 | 20 | #define kMappingFileName @"dns_mapping" 21 | #define kPreviousMappingFileName @"dns_mapping_previous" 22 | 23 | @interface PPDNSMappingManager () 24 | 25 | //default dns mapping, latest copy 26 | @property (nonatomic, strong) NSDictionary* dnsMapping; 27 | //previous dns mapping, previous copy 28 | @property (nonatomic, strong) NSDictionary* previousDNSMapping; 29 | //real mapping source, with health checking 30 | @property (nonatomic, strong) NSMutableDictionary* mappingSource; 31 | 32 | 33 | //timer to sync mapping file from server 34 | @property (nonatomic, strong) NSTimer* syncTimer; 35 | 36 | //server url to retrieve mapping data 37 | @property (nonatomic, strong) NSString* srcUrl; 38 | 39 | @end 40 | 41 | @implementation PPDNSMappingManager 42 | { 43 | dispatch_semaphore_t _sema; 44 | time_t _lastSyncTime; 45 | } 46 | 47 | + (instancetype)sharedInstance 48 | { 49 | static PPDNSMappingManager* instance; 50 | static dispatch_once_t onceToken; 51 | dispatch_once(&onceToken, ^{ 52 | instance = [PPDNSMappingManager new]; 53 | }); 54 | return instance; 55 | } 56 | 57 | - (instancetype)init 58 | { 59 | self = [super init]; 60 | if (self) { 61 | 62 | _sema = dispatch_semaphore_create(1); 63 | _lastSyncTime = 0; 64 | 65 | self.dnsMapping = @{}; 66 | self.previousDNSMapping = @{}; 67 | self.mappingSource = @{}.mutableCopy; 68 | 69 | //observe events 70 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(detectDomainRequestResult:) name:Notif_DomainRequestResult object:nil]; 71 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(detectIPValidatorResult:) name:Notif_IPValidateResult object:nil]; 72 | } 73 | return self; 74 | } 75 | 76 | - (PPResolvedUrl*)resolveUrl:(NSURL*)originalUrl 77 | { 78 | if (originalUrl == nil || originalUrl.absoluteString.length == 0) { 79 | NSLog(@"empty original url."); 80 | return nil; 81 | } 82 | 83 | PPResolvedUrl* resolvedEntry = nil; 84 | 85 | //retrieve resolved url from our mapping 86 | NSString* urlKey = [PPMappingUtil getUrlMappingKey:originalUrl]; 87 | NSString* mappedUrl = [self getMappingEntry:urlKey withFailLimit:0 withMappingDic:self.dnsMapping]; 88 | if (mappedUrl.length == 0) { 89 | mappedUrl = [self getMappingEntry:urlKey withFailLimit:0 withMappingDic:self.previousDNSMapping]; 90 | } 91 | 92 | resolvedEntry = [PPResolvedUrl new]; 93 | resolvedEntry.resolvedHost = [NSURL URLWithString:urlKey].host; 94 | if (mappedUrl.length != 0) { 95 | resolvedEntry.resolvedUrl = [originalUrl.absoluteString stringByReplacingOccurrencesOfString:urlKey withString:mappedUrl]; 96 | } 97 | else 98 | { 99 | resolvedEntry.resolvedUrl = originalUrl.absoluteString; 100 | } 101 | 102 | return resolvedEntry; 103 | } 104 | 105 | - (NSString*)getMappingEntry:(NSString*)urlKey withFailLimit:(int)limit withMappingDic:(NSDictionary*)mappingDic 106 | { 107 | NSString* mappedUrl = nil; 108 | NSURL* tmpUrl = [NSURL URLWithString:urlKey]; 109 | 110 | BOOL shouldSkipMapping = false; 111 | 112 | //if it's ip address already, no need to do mapping 113 | if ([PPMappingUtil isIPAddress:tmpUrl.host]) { 114 | shouldSkipMapping = true; 115 | } 116 | 117 | dispatch_semaphore_wait(_sema, DISPATCH_TIME_FOREVER); 118 | 119 | NSArray* mappingUrls = [mappingDic objectForKey:urlKey]; 120 | //no mapping found, skip 121 | if (mappingUrls.count == 0) { 122 | shouldSkipMapping = true; 123 | } 124 | 125 | //do mapping 126 | if (shouldSkipMapping == false) { 127 | for (NSString* source in mappingUrls) { 128 | PPMappingNode* node = [_mappingSource objectForKey:source]; 129 | if (node && node.requestFailedCount <= limit) { 130 | mappedUrl = node.mappedUrl; 131 | break; 132 | } 133 | } 134 | } 135 | 136 | dispatch_semaphore_signal(_sema); 137 | 138 | return mappedUrl; 139 | } 140 | 141 | #pragma mark- initial config 142 | - (void)setDefaultLocalMapping:(NSString*)filePath 143 | { 144 | NSData* mappingData = [NSData dataWithContentsOfFile:filePath]; 145 | 146 | if (mappingData.length == 0) { 147 | return; 148 | } 149 | 150 | NSDictionary* mappingDic = [NSJSONSerialization JSONObjectWithData:mappingData options:NSJSONReadingMutableContainers error:nil]; 151 | [self parseDNSMapping:mappingDic]; 152 | } 153 | 154 | - (void)parseDNSMapping:(NSDictionary*)mappingDic 155 | { 156 | if (mappingDic == nil) { 157 | NSLog(@"mapping is nil, err"); 158 | return; 159 | } 160 | 161 | dispatch_semaphore_wait(_sema, DISPATCH_TIME_FOREVER); 162 | NSDictionary* prevMapping = [NSDictionary dictionaryWithDictionary:self.dnsMapping]; 163 | NSDictionary* newMapping = [NSDictionary dictionaryWithDictionary:mappingDic]; 164 | dispatch_semaphore_signal(_sema); 165 | 166 | [self buildMappingData:newMapping withPreviousMapping:prevMapping]; 167 | } 168 | 169 | 170 | #pragma mark- sync mapping logic 171 | - (void)syncMappingWithServer 172 | { 173 | if ([NSThread isMainThread]) { 174 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 175 | [self syncMappingWithServer]; 176 | }); 177 | return; 178 | } 179 | NSAssert([NSThread isMainThread] == false, @"run this from worker thread"); 180 | 181 | //do frequency check, ignore requests within 3 seconds 182 | time_t now = time(0); 183 | if (now - _lastSyncTime < 3) { 184 | return; 185 | } 186 | _lastSyncTime = now; 187 | 188 | //retrieve mapping data from server 189 | NSMutableURLRequest* req = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:_srcUrl] cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:10]; 190 | NSURLResponse* response; 191 | NSError* error = nil; 192 | 193 | NSData* resultData = [NSURLConnection sendSynchronousRequest:req returningResponse:&response error:&error]; 194 | if(resultData.length == 0) 195 | { 196 | NSLog(@"empty response, err"); 197 | return; 198 | } 199 | 200 | NSDictionary* mappingDic = [NSJSONSerialization JSONObjectWithData:resultData options:NSJSONReadingMutableContainers error:nil]; 201 | [self parseDNSMapping:mappingDic]; 202 | 203 | //save mapping to disk 204 | [self saveMappingToDisk]; 205 | } 206 | 207 | - (void)startSyncTimerWithInterval:(NSTimeInterval)interval withSrcUrl:(NSString*)srcUrl 208 | { 209 | NSAssert([NSThread isMainThread], @"must run in main thread"); 210 | 211 | if (interval < 3) { 212 | return; 213 | } 214 | 215 | self.srcUrl = srcUrl; 216 | __weak __typeof(self) wself = self; 217 | self.syncTimer = [PPWeakTimer scheduledTimerWithTimeInterval:interval target:wself selector:@selector(syncMappingWithServer) userInfo:nil repeats:true]; 218 | [self.syncTimer fire]; 219 | } 220 | 221 | #pragma mark- detect mapping events 222 | - (void)detectDomainRequestResult:(NSNotification*)notif 223 | { 224 | PPDomainRequestResult* result = notif.object; 225 | if (result.resultUrl == nil) { 226 | return; 227 | } 228 | 229 | if (result.status == PPDomainRequestSuccess) { 230 | //happy news 231 | } 232 | else if(result.status == PPDomainRequestFail) 233 | { 234 | //ignore result from network lost 235 | NetworkStatus status = [[Reachability reachabilityForInternetConnection] currentReachabilityStatus]; 236 | if (status == NotReachable) { 237 | return; 238 | } 239 | 240 | NSString* urlKey = [PPMappingUtil getUrlMappingKey:result.resultUrl]; 241 | 242 | dispatch_semaphore_wait(_sema, DISPATCH_TIME_FOREVER); 243 | PPMappingNode* node = [self.mappingSource objectForKey:urlKey]; 244 | if (node != nil) { 245 | //increase fail count 246 | node.requestFailedCount ++; 247 | } 248 | dispatch_semaphore_signal(_sema); 249 | 250 | //validate this ip again with validator 251 | [[PPIPValidator sharedInstance] validateIP:result.resultUrl.absoluteString]; 252 | } 253 | } 254 | 255 | 256 | - (void)detectIPValidatorResult:(NSNotification*)notif 257 | { 258 | PPDomainRequestResult* result = notif.object; 259 | if (result.resultUrl == nil) { 260 | return; 261 | } 262 | NSString* urlKey = [PPMappingUtil getUrlMappingKey:result.resultUrl]; 263 | 264 | if (result.status == PPDomainRequestSuccess) { 265 | dispatch_semaphore_wait(_sema, DISPATCH_TIME_FOREVER); 266 | PPMappingNode* node = [self.mappingSource objectForKey:urlKey]; 267 | if (node != nil) { 268 | //reset fail count 269 | node.requestFailedCount = 0; 270 | } 271 | dispatch_semaphore_signal(_sema); 272 | } 273 | else if(result.status == PPDomainRequestFail) 274 | { 275 | dispatch_semaphore_wait(_sema, DISPATCH_TIME_FOREVER); 276 | PPMappingNode* node = [self.mappingSource objectForKey:urlKey]; 277 | dispatch_semaphore_signal(_sema); 278 | 279 | //validator failed too, report to server 280 | [[PPDNSReporter sharedInstance] reportFailedUrl:result.resultUrl.absoluteString withFailedCount:node.requestFailedCount]; 281 | } 282 | } 283 | 284 | #pragma mark- mapping file reading and writting 285 | - (void)saveMappingToDisk 286 | { 287 | if (self.dnsMapping) { 288 | NSString* allotdnsmappath = [NSHomeDirectory() stringByAppendingPathComponent:kMappingFileName]; 289 | [NSKeyedArchiver archiveRootObject:self.dnsMapping toFile:allotdnsmappath]; 290 | } 291 | 292 | if (self.previousDNSMapping) { 293 | NSString* lastallotdnsmap = [NSHomeDirectory() stringByAppendingPathComponent:kPreviousMappingFileName]; 294 | [NSKeyedArchiver archiveRootObject:self.previousDNSMapping toFile:lastallotdnsmap]; 295 | } 296 | } 297 | 298 | - (void)readMappingFromDisk 299 | { 300 | NSString* mappingPath = [NSHomeDirectory() stringByAppendingPathComponent:kMappingFileName]; 301 | NSMutableDictionary* mappingDic = [NSKeyedUnarchiver unarchiveObjectWithFile:mappingPath]; 302 | 303 | NSString* preMappingPath = [NSHomeDirectory() stringByAppendingPathComponent:kPreviousMappingFileName]; 304 | NSMutableDictionary* prevMappingDic = [NSKeyedUnarchiver unarchiveObjectWithFile:preMappingPath]; 305 | 306 | [self buildMappingData:mappingDic withPreviousMapping:prevMappingDic]; 307 | } 308 | 309 | #pragma mark- build mapping data 310 | - (void)buildMappingData:(NSDictionary*)mapping withPreviousMapping:(NSDictionary*)preMapping 311 | { 312 | dispatch_semaphore_wait(_sema, DISPATCH_TIME_FOREVER); 313 | 314 | if (mapping) { 315 | self.dnsMapping = [NSDictionary dictionaryWithDictionary:mapping]; 316 | } 317 | if (preMapping) { 318 | self.previousDNSMapping = [NSDictionary dictionaryWithDictionary:preMapping]; 319 | } 320 | 321 | //build mapping source 322 | if (self.dnsMapping == nil || self.dnsMapping.allKeys.count == 0) { 323 | return; 324 | } 325 | 326 | [self.mappingSource removeAllObjects]; 327 | 328 | [self buildMappingSourceFromDic:self.previousDNSMapping]; 329 | [self buildMappingSourceFromDic:self.dnsMapping]; 330 | 331 | dispatch_semaphore_signal(_sema); 332 | } 333 | 334 | - (void)buildMappingSourceFromDic:(NSDictionary*)dic 335 | { 336 | if (dic == nil) { 337 | return; 338 | } 339 | [dic enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) { 340 | NSArray* sourceList = obj; 341 | for (NSString* source in sourceList) { 342 | 343 | PPMappingNode* node = [PPMappingNode new]; 344 | node.mappedUrl = source; 345 | node.requestFailedCount = 0; 346 | 347 | [self.mappingSource setObject:node forKey:source]; 348 | } 349 | }]; 350 | } 351 | 352 | @end 353 | -------------------------------------------------------------------------------- /PPDNSMapping/PPDNSReporter.h: -------------------------------------------------------------------------------- 1 | // 2 | // PPDNSReporter.h 3 | // PPDNSMappingDemo 4 | // 5 | // Created by gao feng on 16/1/19. 6 | // Copyright © 2016年 gao feng. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface PPDNSReporter : NSObject 12 | 13 | + (PPDNSReporter*)sharedInstance; 14 | 15 | - (void)reportFailedUrl:(NSString*)url withFailedCount:(int64_t)count; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /PPDNSMapping/PPDNSReporter.m: -------------------------------------------------------------------------------- 1 | // 2 | // PPDNSReporter.m 3 | // PPDNSMappingDemo 4 | // 5 | // Created by gao feng on 16/1/19. 6 | // Copyright © 2016年 gao feng. All rights reserved. 7 | // 8 | 9 | #import "PPDNSReporter.h" 10 | 11 | @implementation PPDNSReporter 12 | 13 | + (PPDNSReporter*)sharedInstance 14 | { 15 | static PPDNSReporter* instance = nil; 16 | static dispatch_once_t onceToken; 17 | dispatch_once(&onceToken, ^{ 18 | instance = [PPDNSReporter new]; 19 | }); 20 | return instance; 21 | } 22 | 23 | - (void)reportFailedUrl:(NSString*)url withFailedCount:(int64_t)count 24 | { 25 | NSLog(@"mapped url:%@ failed with count:%llu", url, count); 26 | } 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /PPDNSMapping/PPDomainRequestResult.h: -------------------------------------------------------------------------------- 1 | // 2 | // PPDomainRequestResult.h 3 | // PPDNSMappingDemo 4 | // 5 | // Created by gao feng on 16/1/19. 6 | // Copyright © 2016年 gao feng. All rights reserved. 7 | // 8 | 9 | /** 10 | application layer module use this model to tell mapping engine, whether mapped url is healthy or not. 11 | **/ 12 | 13 | #import 14 | 15 | typedef enum : NSUInteger { 16 | PPDomainRequestSuccess = 0, 17 | PPDomainRequestFail, 18 | } PPDomainRequestResultStatus; 19 | 20 | @interface PPDomainRequestResult : NSObject 21 | 22 | @property (nonatomic, strong) NSURL* resultUrl; 23 | @property (nonatomic, assign) PPDomainRequestResultStatus status; 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /PPDNSMapping/PPDomainRequestResult.m: -------------------------------------------------------------------------------- 1 | // 2 | // PPDomainRequestResult.m 3 | // PPDNSMappingDemo 4 | // 5 | // Created by gao feng on 16/1/19. 6 | // Copyright © 2016年 gao feng. All rights reserved. 7 | // 8 | 9 | #import "PPDomainRequestResult.h" 10 | 11 | @implementation PPDomainRequestResult 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /PPDNSMapping/PPIPValidator.h: -------------------------------------------------------------------------------- 1 | // 2 | // PPIPValidator.h 3 | // PPDNSMappingDemo 4 | // 5 | // Created by gao feng on 16/1/19. 6 | // Copyright © 2016年 gao feng. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface PPIPValidator : NSObject 12 | 13 | + (instancetype)sharedInstance; 14 | 15 | - (void)validateIP:(NSString*)ip; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /PPDNSMapping/PPIPValidator.m: -------------------------------------------------------------------------------- 1 | // 2 | // PPIPValidator.m 3 | // PPDNSMappingDemo 4 | // 5 | // Created by gao feng on 16/1/19. 6 | // Copyright © 2016年 gao feng. All rights reserved. 7 | // 8 | 9 | #import "PPIPValidator.h" 10 | #import "PPDNSMappingManager.h" 11 | #import "PPDomainRequestResult.h" 12 | 13 | #define kMaxValidateRetry 10 14 | #define kValidateInterval 3 15 | 16 | @interface PPValidatingNode : NSObject 17 | @property (nonatomic, strong) NSString* url; 18 | @property (nonatomic, assign) int failedCount; 19 | @end 20 | @implementation PPValidatingNode 21 | @end 22 | 23 | 24 | @interface PPIPValidator () 25 | @property (nonatomic, strong) NSMutableDictionary* validatingMap; 26 | @end 27 | 28 | @implementation PPIPValidator 29 | { 30 | dispatch_semaphore_t _sema; 31 | } 32 | 33 | + (instancetype)sharedInstance 34 | { 35 | static PPIPValidator* instance; 36 | static dispatch_once_t onceToken; 37 | dispatch_once(&onceToken, ^{ 38 | instance = [PPIPValidator new]; 39 | }); 40 | return instance; 41 | } 42 | 43 | - (instancetype)init 44 | { 45 | self = [super init]; 46 | if (self) { 47 | _sema = dispatch_semaphore_create(1); 48 | 49 | self.validatingMap = @{}.mutableCopy; 50 | } 51 | return self; 52 | } 53 | 54 | - (void)validateIP:(NSString*)ipUrl 55 | { 56 | @synchronized(self) 57 | { 58 | dispatch_semaphore_wait(_sema, DISPATCH_TIME_FOREVER); 59 | PPValidatingNode* node = [self.validatingMap objectForKey:ipUrl]; 60 | dispatch_semaphore_signal(_sema); 61 | 62 | if (!node) { 63 | node = [PPValidatingNode new]; 64 | node.url = ipUrl; 65 | node.failedCount = 1; 66 | 67 | dispatch_semaphore_wait(_sema, DISPATCH_TIME_FOREVER); 68 | [self.validatingMap setObject:node forKey:ipUrl]; 69 | dispatch_semaphore_signal(_sema); 70 | 71 | //start validating in 3 seconds 72 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(kValidateInterval * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 73 | [self checkServerStatus:ipUrl]; 74 | }); 75 | 76 | } 77 | else 78 | { 79 | node.failedCount ++; 80 | } 81 | } 82 | } 83 | 84 | - (void)checkServerStatus:(NSString*)httpUrl 85 | { 86 | //try connecting server 87 | NSMutableURLRequest* req = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:httpUrl] cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:10]; 88 | NSURLResponse* response; 89 | NSError* error = nil; 90 | [NSURLConnection sendSynchronousRequest:req returningResponse:&response error:&error]; 91 | 92 | if(error == nil) 93 | { 94 | PPDomainRequestResult* result = [PPDomainRequestResult new]; 95 | result.resultUrl = [NSURL URLWithString:httpUrl]; 96 | result.status = PPDomainRequestSuccess; 97 | [[NSNotificationCenter defaultCenter] postNotificationName:Notif_IPValidateResult object:result]; 98 | } 99 | else 100 | { 101 | dispatch_semaphore_wait(_sema, DISPATCH_TIME_FOREVER); 102 | 103 | PPValidatingNode* node = [self.validatingMap objectForKey:httpUrl]; 104 | if (node) { 105 | node.failedCount ++; 106 | if (node.failedCount >= kMaxValidateRetry) { 107 | PPDomainRequestResult* result = [PPDomainRequestResult new]; 108 | result.resultUrl = [NSURL URLWithString:httpUrl]; 109 | result.status = PPDomainRequestFail; 110 | [[NSNotificationCenter defaultCenter] postNotificationName:Notif_IPValidateResult object:result]; 111 | } 112 | else 113 | { 114 | //keep trying 115 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(kValidateInterval * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 116 | [self checkServerStatus:httpUrl]; 117 | }); 118 | 119 | } 120 | } 121 | 122 | dispatch_semaphore_signal(_sema); 123 | } 124 | 125 | } 126 | 127 | @end 128 | -------------------------------------------------------------------------------- /PPDNSMapping/model/PPMappingNode.h: -------------------------------------------------------------------------------- 1 | // 2 | // PPMappingNode.h 3 | // PPDNSMappingDemo 4 | // 5 | // Created by gao feng on 16/1/21. 6 | // Copyright © 2016年 gao feng. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface PPMappingNode : NSObject 12 | 13 | @property (nonatomic, strong) NSString* mappedUrl; //ip or domain 14 | @property (nonatomic, assign) int requestFailedCount; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /PPDNSMapping/model/PPMappingNode.m: -------------------------------------------------------------------------------- 1 | // 2 | // PPMappingNode.m 3 | // PPDNSMappingDemo 4 | // 5 | // Created by gao feng on 16/1/21. 6 | // Copyright © 2016年 gao feng. All rights reserved. 7 | // 8 | 9 | #import "PPMappingNode.h" 10 | 11 | @implementation PPMappingNode 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /PPDNSMapping/model/PPResolvedUrl.h: -------------------------------------------------------------------------------- 1 | // 2 | // PPResolvedUrl.h 3 | // PPDNSMappingDemo 4 | // 5 | // Created by gao feng on 16/1/20. 6 | // Copyright © 2016年 gao feng. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface PPResolvedUrl : NSObject 12 | 13 | @property (nonatomic, strong) NSString* resolvedUrl; 14 | @property (nonatomic, strong) NSString* resolvedHost; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /PPDNSMapping/model/PPResolvedUrl.m: -------------------------------------------------------------------------------- 1 | // 2 | // PPResolvedUrl.m 3 | // PPDNSMappingDemo 4 | // 5 | // Created by gao feng on 16/1/20. 6 | // Copyright © 2016年 gao feng. All rights reserved. 7 | // 8 | 9 | #import "PPResolvedUrl.h" 10 | 11 | @implementation PPResolvedUrl 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /PPDNSMapping/util/PPMappingUtil.h: -------------------------------------------------------------------------------- 1 | // 2 | // PPMappingUtil.h 3 | // PPDNSMappingDemo 4 | // 5 | // Created by gao feng on 16/1/20. 6 | // Copyright © 2016年 gao feng. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface PPMappingUtil : NSObject 12 | 13 | + (NSString*)getUrlMappingKey:(NSURL*)url; 14 | 15 | + (BOOL)isIPAddress:(NSString*)address; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /PPDNSMapping/util/PPMappingUtil.m: -------------------------------------------------------------------------------- 1 | // 2 | // PPMappingUtil.m 3 | // PPDNSMappingDemo 4 | // 5 | // Created by gao feng on 16/1/20. 6 | // Copyright © 2016年 gao feng. All rights reserved. 7 | // 8 | 9 | #import "PPMappingUtil.h" 10 | 11 | @implementation PPMappingUtil 12 | 13 | + (NSString*)getUrlMappingKey:(NSURL*)url 14 | { 15 | /** since we will be only modifying scheme, host, port. the key should be formatted like: scheme://host:port 16 | excluding path, parameter, query, fragament. **/ 17 | 18 | NSString* port = @""; 19 | if (url.port != nil) { 20 | port = [NSString stringWithFormat:@":%d", url.port.intValue]; 21 | } 22 | NSString* urlKey = [NSString stringWithFormat:@"%@://%@%@", url.scheme, url.host, port]; 23 | 24 | return urlKey; 25 | } 26 | 27 | + (BOOL)isIPAddress:(NSString*)address 28 | { 29 | NSString *urlRegEx =@"^([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\." 30 | "([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\." 31 | "([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\." 32 | "([01]?\\d\\d?|2[0-4]\\d|25[0-5])$"; 33 | 34 | NSPredicate *urlTest = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", urlRegEx]; 35 | return [urlTest evaluateWithObject:address]; 36 | } 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /PPDNSMapping/util/PPWeakTimer.h: -------------------------------------------------------------------------------- 1 | // 2 | // PPWeakTimer.h 3 | // PPDNSMappingDemo 4 | // 5 | // Created by gao feng on 16/1/19. 6 | // Copyright © 2016年 gao feng. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef void (^PPTimerHandler)(id userInfo); 12 | 13 | @interface PPWeakTimer : NSObject 14 | 15 | + (NSTimer*)scheduledTimerWithTimeInterval:(NSTimeInterval)interval 16 | target:(id)aTarget 17 | selector:(SEL)aSelector 18 | userInfo:(id)userInfo 19 | repeats:(BOOL)repeats; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /PPDNSMapping/util/PPWeakTimer.m: -------------------------------------------------------------------------------- 1 | // 2 | // PPWeakTimer.m 3 | // PPDNSMappingDemo 4 | // 5 | // Created by gao feng on 16/1/19. 6 | // Copyright © 2016年 gao feng. All rights reserved. 7 | // 8 | 9 | #import "PPWeakTimer.h" 10 | 11 | @interface PPWeakTimer() 12 | @property (nonatomic, weak) id target; 13 | @property (nonatomic, assign) SEL selector; 14 | @property (nonatomic, weak) NSTimer* timer; 15 | @end 16 | 17 | @implementation PPWeakTimer 18 | 19 | - (void)timerDidFire:(NSTimer *)timer { 20 | if(self.target) 21 | { 22 | #pragma clang diagnostic push 23 | #pragma clang diagnostic ignored "-Warc-performSelector-leaks" 24 | [self.target performSelector:self.selector withObject:timer.userInfo]; 25 | #pragma clang diagnostic pop 26 | } 27 | else { 28 | [self.timer invalidate]; 29 | } 30 | } 31 | 32 | + (NSTimer*)scheduledTimerWithTimeInterval:(NSTimeInterval)interval 33 | target:(id)aTarget 34 | selector:(SEL)aSelector 35 | userInfo:(id)userInfo 36 | repeats:(BOOL)repeats { 37 | PPWeakTimer* timerTarget = [[PPWeakTimer alloc] init]; 38 | timerTarget.target = aTarget; 39 | timerTarget.selector = aSelector; 40 | timerTarget.timer = [NSTimer scheduledTimerWithTimeInterval:interval 41 | target:timerTarget 42 | selector:@selector(timerDidFire:) 43 | userInfo:userInfo 44 | repeats:repeats]; 45 | return timerTarget.timer; 46 | } 47 | 48 | @end 49 | -------------------------------------------------------------------------------- /PPDNSMappingDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 672F116C1C4E1EC3001E1694 /* PPWeakTimer.m in Sources */ = {isa = PBXBuildFile; fileRef = 672F116B1C4E1EC3001E1694 /* PPWeakTimer.m */; }; 11 | 672F116F1C4E2A46001E1694 /* PPDomainRequestResult.m in Sources */ = {isa = PBXBuildFile; fileRef = 672F116E1C4E2A46001E1694 /* PPDomainRequestResult.m */; }; 12 | 672F11771C4E2D93001E1694 /* Reachability.m in Sources */ = {isa = PBXBuildFile; fileRef = 672F11751C4E2D93001E1694 /* Reachability.m */; }; 13 | 672F11801C4E2FBC001E1694 /* PPIPValidator.m in Sources */ = {isa = PBXBuildFile; fileRef = 672F117F1C4E2FBC001E1694 /* PPIPValidator.m */; }; 14 | 672F11831C4E61CB001E1694 /* PPDNSReporter.m in Sources */ = {isa = PBXBuildFile; fileRef = 672F11821C4E61CB001E1694 /* PPDNSReporter.m */; }; 15 | 672F11851C4E633A001E1694 /* defaultMapping.txt in Resources */ = {isa = PBXBuildFile; fileRef = 672F11841C4E633A001E1694 /* defaultMapping.txt */; }; 16 | 672F11881C4F2C2E001E1694 /* PPResolvedUrl.m in Sources */ = {isa = PBXBuildFile; fileRef = 672F11871C4F2C2E001E1694 /* PPResolvedUrl.m */; }; 17 | 672F118B1C4F3951001E1694 /* PPMappingUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = 672F118A1C4F3951001E1694 /* PPMappingUtil.m */; }; 18 | 672F118E1C50803C001E1694 /* PPMappingNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 672F118D1C50803C001E1694 /* PPMappingNode.m */; }; 19 | 67B8A53D1C4E172D00DE1CE4 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 67B8A53C1C4E172D00DE1CE4 /* main.m */; }; 20 | 67B8A5401C4E172D00DE1CE4 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 67B8A53F1C4E172D00DE1CE4 /* AppDelegate.m */; }; 21 | 67B8A5431C4E172D00DE1CE4 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 67B8A5421C4E172D00DE1CE4 /* ViewController.m */; }; 22 | 67B8A5461C4E172D00DE1CE4 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 67B8A5441C4E172D00DE1CE4 /* Main.storyboard */; }; 23 | 67B8A5481C4E172D00DE1CE4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 67B8A5471C4E172D00DE1CE4 /* Assets.xcassets */; }; 24 | 67B8A54B1C4E172D00DE1CE4 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 67B8A5491C4E172D00DE1CE4 /* LaunchScreen.storyboard */; }; 25 | 67B8A5561C4E172D00DE1CE4 /* PPDNSMappingDemoTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 67B8A5551C4E172D00DE1CE4 /* PPDNSMappingDemoTests.m */; }; 26 | 67B8A5611C4E172D00DE1CE4 /* PPDNSMappingDemoUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = 67B8A5601C4E172D00DE1CE4 /* PPDNSMappingDemoUITests.m */; }; 27 | 67B8A5721C4E17F400DE1CE4 /* PPDNSMappingManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 67B8A5711C4E17F400DE1CE4 /* PPDNSMappingManager.m */; }; 28 | /* End PBXBuildFile section */ 29 | 30 | /* Begin PBXContainerItemProxy section */ 31 | 67B8A5521C4E172D00DE1CE4 /* PBXContainerItemProxy */ = { 32 | isa = PBXContainerItemProxy; 33 | containerPortal = 67B8A5301C4E172D00DE1CE4 /* Project object */; 34 | proxyType = 1; 35 | remoteGlobalIDString = 67B8A5371C4E172D00DE1CE4; 36 | remoteInfo = PPDNSMappingDemo; 37 | }; 38 | 67B8A55D1C4E172D00DE1CE4 /* PBXContainerItemProxy */ = { 39 | isa = PBXContainerItemProxy; 40 | containerPortal = 67B8A5301C4E172D00DE1CE4 /* Project object */; 41 | proxyType = 1; 42 | remoteGlobalIDString = 67B8A5371C4E172D00DE1CE4; 43 | remoteInfo = PPDNSMappingDemo; 44 | }; 45 | /* End PBXContainerItemProxy section */ 46 | 47 | /* Begin PBXFileReference section */ 48 | 672F116A1C4E1EC3001E1694 /* PPWeakTimer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PPWeakTimer.h; sourceTree = ""; }; 49 | 672F116B1C4E1EC3001E1694 /* PPWeakTimer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PPWeakTimer.m; sourceTree = ""; }; 50 | 672F116D1C4E2A46001E1694 /* PPDomainRequestResult.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PPDomainRequestResult.h; path = ../PPDomainRequestResult.h; sourceTree = ""; }; 51 | 672F116E1C4E2A46001E1694 /* PPDomainRequestResult.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PPDomainRequestResult.m; path = ../PPDomainRequestResult.m; sourceTree = ""; }; 52 | 672F11741C4E2D93001E1694 /* Reachability.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Reachability.h; sourceTree = ""; }; 53 | 672F11751C4E2D93001E1694 /* Reachability.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Reachability.m; sourceTree = ""; }; 54 | 672F117E1C4E2FBC001E1694 /* PPIPValidator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PPIPValidator.h; sourceTree = ""; }; 55 | 672F117F1C4E2FBC001E1694 /* PPIPValidator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PPIPValidator.m; sourceTree = ""; }; 56 | 672F11811C4E61CB001E1694 /* PPDNSReporter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PPDNSReporter.h; sourceTree = ""; }; 57 | 672F11821C4E61CB001E1694 /* PPDNSReporter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PPDNSReporter.m; sourceTree = ""; }; 58 | 672F11841C4E633A001E1694 /* defaultMapping.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = defaultMapping.txt; sourceTree = ""; }; 59 | 672F11861C4F2C2E001E1694 /* PPResolvedUrl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PPResolvedUrl.h; sourceTree = ""; }; 60 | 672F11871C4F2C2E001E1694 /* PPResolvedUrl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PPResolvedUrl.m; sourceTree = ""; }; 61 | 672F11891C4F3951001E1694 /* PPMappingUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PPMappingUtil.h; sourceTree = ""; }; 62 | 672F118A1C4F3951001E1694 /* PPMappingUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PPMappingUtil.m; sourceTree = ""; }; 63 | 672F118C1C50803C001E1694 /* PPMappingNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PPMappingNode.h; sourceTree = ""; }; 64 | 672F118D1C50803C001E1694 /* PPMappingNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PPMappingNode.m; sourceTree = ""; }; 65 | 67B8A5381C4E172D00DE1CE4 /* PPDNSMappingDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PPDNSMappingDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 66 | 67B8A53C1C4E172D00DE1CE4 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 67 | 67B8A53E1C4E172D00DE1CE4 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 68 | 67B8A53F1C4E172D00DE1CE4 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 69 | 67B8A5411C4E172D00DE1CE4 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 70 | 67B8A5421C4E172D00DE1CE4 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 71 | 67B8A5451C4E172D00DE1CE4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 72 | 67B8A5471C4E172D00DE1CE4 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 73 | 67B8A54A1C4E172D00DE1CE4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 74 | 67B8A54C1C4E172D00DE1CE4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 75 | 67B8A5511C4E172D00DE1CE4 /* PPDNSMappingDemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PPDNSMappingDemoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 76 | 67B8A5551C4E172D00DE1CE4 /* PPDNSMappingDemoTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PPDNSMappingDemoTests.m; sourceTree = ""; }; 77 | 67B8A5571C4E172D00DE1CE4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 78 | 67B8A55C1C4E172D00DE1CE4 /* PPDNSMappingDemoUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PPDNSMappingDemoUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 79 | 67B8A5601C4E172D00DE1CE4 /* PPDNSMappingDemoUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PPDNSMappingDemoUITests.m; sourceTree = ""; }; 80 | 67B8A5621C4E172D00DE1CE4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 81 | 67B8A56F1C4E178800DE1CE4 /* PPDNSMapping.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PPDNSMapping.h; sourceTree = ""; }; 82 | 67B8A5701C4E17F400DE1CE4 /* PPDNSMappingManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PPDNSMappingManager.h; sourceTree = ""; }; 83 | 67B8A5711C4E17F400DE1CE4 /* PPDNSMappingManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PPDNSMappingManager.m; sourceTree = ""; }; 84 | /* End PBXFileReference section */ 85 | 86 | /* Begin PBXFrameworksBuildPhase section */ 87 | 67B8A5351C4E172D00DE1CE4 /* Frameworks */ = { 88 | isa = PBXFrameworksBuildPhase; 89 | buildActionMask = 2147483647; 90 | files = ( 91 | ); 92 | runOnlyForDeploymentPostprocessing = 0; 93 | }; 94 | 67B8A54E1C4E172D00DE1CE4 /* Frameworks */ = { 95 | isa = PBXFrameworksBuildPhase; 96 | buildActionMask = 2147483647; 97 | files = ( 98 | ); 99 | runOnlyForDeploymentPostprocessing = 0; 100 | }; 101 | 67B8A5591C4E172D00DE1CE4 /* Frameworks */ = { 102 | isa = PBXFrameworksBuildPhase; 103 | buildActionMask = 2147483647; 104 | files = ( 105 | ); 106 | runOnlyForDeploymentPostprocessing = 0; 107 | }; 108 | /* End PBXFrameworksBuildPhase section */ 109 | 110 | /* Begin PBXGroup section */ 111 | 672F11691C4E1DAF001E1694 /* util */ = { 112 | isa = PBXGroup; 113 | children = ( 114 | 672F116A1C4E1EC3001E1694 /* PPWeakTimer.h */, 115 | 672F116B1C4E1EC3001E1694 /* PPWeakTimer.m */, 116 | 672F11891C4F3951001E1694 /* PPMappingUtil.h */, 117 | 672F118A1C4F3951001E1694 /* PPMappingUtil.m */, 118 | ); 119 | path = util; 120 | sourceTree = ""; 121 | }; 122 | 672F11701C4E2B68001E1694 /* model */ = { 123 | isa = PBXGroup; 124 | children = ( 125 | 672F116D1C4E2A46001E1694 /* PPDomainRequestResult.h */, 126 | 672F116E1C4E2A46001E1694 /* PPDomainRequestResult.m */, 127 | 672F11861C4F2C2E001E1694 /* PPResolvedUrl.h */, 128 | 672F11871C4F2C2E001E1694 /* PPResolvedUrl.m */, 129 | 672F118C1C50803C001E1694 /* PPMappingNode.h */, 130 | 672F118D1C50803C001E1694 /* PPMappingNode.m */, 131 | ); 132 | path = model; 133 | sourceTree = ""; 134 | }; 135 | 672F11711C4E2C9F001E1694 /* 3rdParty */ = { 136 | isa = PBXGroup; 137 | children = ( 138 | 672F11721C4E2D93001E1694 /* Reachability */, 139 | ); 140 | path = 3rdParty; 141 | sourceTree = ""; 142 | }; 143 | 672F11721C4E2D93001E1694 /* Reachability */ = { 144 | isa = PBXGroup; 145 | children = ( 146 | 672F11741C4E2D93001E1694 /* Reachability.h */, 147 | 672F11751C4E2D93001E1694 /* Reachability.m */, 148 | ); 149 | path = Reachability; 150 | sourceTree = ""; 151 | }; 152 | 67B8A52F1C4E172D00DE1CE4 = { 153 | isa = PBXGroup; 154 | children = ( 155 | 67B8A56E1C4E175B00DE1CE4 /* PPDNSMapping */, 156 | 67B8A53A1C4E172D00DE1CE4 /* PPDNSMappingDemo */, 157 | 67B8A5541C4E172D00DE1CE4 /* PPDNSMappingDemoTests */, 158 | 67B8A55F1C4E172D00DE1CE4 /* PPDNSMappingDemoUITests */, 159 | 67B8A5391C4E172D00DE1CE4 /* Products */, 160 | ); 161 | sourceTree = ""; 162 | }; 163 | 67B8A5391C4E172D00DE1CE4 /* Products */ = { 164 | isa = PBXGroup; 165 | children = ( 166 | 67B8A5381C4E172D00DE1CE4 /* PPDNSMappingDemo.app */, 167 | 67B8A5511C4E172D00DE1CE4 /* PPDNSMappingDemoTests.xctest */, 168 | 67B8A55C1C4E172D00DE1CE4 /* PPDNSMappingDemoUITests.xctest */, 169 | ); 170 | name = Products; 171 | sourceTree = ""; 172 | }; 173 | 67B8A53A1C4E172D00DE1CE4 /* PPDNSMappingDemo */ = { 174 | isa = PBXGroup; 175 | children = ( 176 | 67B8A53E1C4E172D00DE1CE4 /* AppDelegate.h */, 177 | 67B8A53F1C4E172D00DE1CE4 /* AppDelegate.m */, 178 | 67B8A5411C4E172D00DE1CE4 /* ViewController.h */, 179 | 67B8A5421C4E172D00DE1CE4 /* ViewController.m */, 180 | 67B8A5441C4E172D00DE1CE4 /* Main.storyboard */, 181 | 67B8A5471C4E172D00DE1CE4 /* Assets.xcassets */, 182 | 67B8A5491C4E172D00DE1CE4 /* LaunchScreen.storyboard */, 183 | 67B8A54C1C4E172D00DE1CE4 /* Info.plist */, 184 | 67B8A53B1C4E172D00DE1CE4 /* Supporting Files */, 185 | 672F11841C4E633A001E1694 /* defaultMapping.txt */, 186 | ); 187 | path = PPDNSMappingDemo; 188 | sourceTree = ""; 189 | }; 190 | 67B8A53B1C4E172D00DE1CE4 /* Supporting Files */ = { 191 | isa = PBXGroup; 192 | children = ( 193 | 67B8A53C1C4E172D00DE1CE4 /* main.m */, 194 | ); 195 | name = "Supporting Files"; 196 | sourceTree = ""; 197 | }; 198 | 67B8A5541C4E172D00DE1CE4 /* PPDNSMappingDemoTests */ = { 199 | isa = PBXGroup; 200 | children = ( 201 | 67B8A5551C4E172D00DE1CE4 /* PPDNSMappingDemoTests.m */, 202 | 67B8A5571C4E172D00DE1CE4 /* Info.plist */, 203 | ); 204 | path = PPDNSMappingDemoTests; 205 | sourceTree = ""; 206 | }; 207 | 67B8A55F1C4E172D00DE1CE4 /* PPDNSMappingDemoUITests */ = { 208 | isa = PBXGroup; 209 | children = ( 210 | 67B8A5601C4E172D00DE1CE4 /* PPDNSMappingDemoUITests.m */, 211 | 67B8A5621C4E172D00DE1CE4 /* Info.plist */, 212 | ); 213 | path = PPDNSMappingDemoUITests; 214 | sourceTree = ""; 215 | }; 216 | 67B8A56E1C4E175B00DE1CE4 /* PPDNSMapping */ = { 217 | isa = PBXGroup; 218 | children = ( 219 | 67B8A56F1C4E178800DE1CE4 /* PPDNSMapping.h */, 220 | 67B8A5701C4E17F400DE1CE4 /* PPDNSMappingManager.h */, 221 | 67B8A5711C4E17F400DE1CE4 /* PPDNSMappingManager.m */, 222 | 672F117E1C4E2FBC001E1694 /* PPIPValidator.h */, 223 | 672F117F1C4E2FBC001E1694 /* PPIPValidator.m */, 224 | 672F11811C4E61CB001E1694 /* PPDNSReporter.h */, 225 | 672F11821C4E61CB001E1694 /* PPDNSReporter.m */, 226 | 672F11701C4E2B68001E1694 /* model */, 227 | 672F11691C4E1DAF001E1694 /* util */, 228 | 672F11711C4E2C9F001E1694 /* 3rdParty */, 229 | ); 230 | path = PPDNSMapping; 231 | sourceTree = ""; 232 | }; 233 | /* End PBXGroup section */ 234 | 235 | /* Begin PBXNativeTarget section */ 236 | 67B8A5371C4E172D00DE1CE4 /* PPDNSMappingDemo */ = { 237 | isa = PBXNativeTarget; 238 | buildConfigurationList = 67B8A5651C4E172D00DE1CE4 /* Build configuration list for PBXNativeTarget "PPDNSMappingDemo" */; 239 | buildPhases = ( 240 | 67B8A5341C4E172D00DE1CE4 /* Sources */, 241 | 67B8A5351C4E172D00DE1CE4 /* Frameworks */, 242 | 67B8A5361C4E172D00DE1CE4 /* Resources */, 243 | ); 244 | buildRules = ( 245 | ); 246 | dependencies = ( 247 | ); 248 | name = PPDNSMappingDemo; 249 | productName = PPDNSMappingDemo; 250 | productReference = 67B8A5381C4E172D00DE1CE4 /* PPDNSMappingDemo.app */; 251 | productType = "com.apple.product-type.application"; 252 | }; 253 | 67B8A5501C4E172D00DE1CE4 /* PPDNSMappingDemoTests */ = { 254 | isa = PBXNativeTarget; 255 | buildConfigurationList = 67B8A5681C4E172D00DE1CE4 /* Build configuration list for PBXNativeTarget "PPDNSMappingDemoTests" */; 256 | buildPhases = ( 257 | 67B8A54D1C4E172D00DE1CE4 /* Sources */, 258 | 67B8A54E1C4E172D00DE1CE4 /* Frameworks */, 259 | 67B8A54F1C4E172D00DE1CE4 /* Resources */, 260 | ); 261 | buildRules = ( 262 | ); 263 | dependencies = ( 264 | 67B8A5531C4E172D00DE1CE4 /* PBXTargetDependency */, 265 | ); 266 | name = PPDNSMappingDemoTests; 267 | productName = PPDNSMappingDemoTests; 268 | productReference = 67B8A5511C4E172D00DE1CE4 /* PPDNSMappingDemoTests.xctest */; 269 | productType = "com.apple.product-type.bundle.unit-test"; 270 | }; 271 | 67B8A55B1C4E172D00DE1CE4 /* PPDNSMappingDemoUITests */ = { 272 | isa = PBXNativeTarget; 273 | buildConfigurationList = 67B8A56B1C4E172D00DE1CE4 /* Build configuration list for PBXNativeTarget "PPDNSMappingDemoUITests" */; 274 | buildPhases = ( 275 | 67B8A5581C4E172D00DE1CE4 /* Sources */, 276 | 67B8A5591C4E172D00DE1CE4 /* Frameworks */, 277 | 67B8A55A1C4E172D00DE1CE4 /* Resources */, 278 | ); 279 | buildRules = ( 280 | ); 281 | dependencies = ( 282 | 67B8A55E1C4E172D00DE1CE4 /* PBXTargetDependency */, 283 | ); 284 | name = PPDNSMappingDemoUITests; 285 | productName = PPDNSMappingDemoUITests; 286 | productReference = 67B8A55C1C4E172D00DE1CE4 /* PPDNSMappingDemoUITests.xctest */; 287 | productType = "com.apple.product-type.bundle.ui-testing"; 288 | }; 289 | /* End PBXNativeTarget section */ 290 | 291 | /* Begin PBXProject section */ 292 | 67B8A5301C4E172D00DE1CE4 /* Project object */ = { 293 | isa = PBXProject; 294 | attributes = { 295 | LastUpgradeCheck = 0720; 296 | ORGANIZATIONNAME = "gao feng"; 297 | TargetAttributes = { 298 | 67B8A5371C4E172D00DE1CE4 = { 299 | CreatedOnToolsVersion = 7.2; 300 | }; 301 | 67B8A5501C4E172D00DE1CE4 = { 302 | CreatedOnToolsVersion = 7.2; 303 | TestTargetID = 67B8A5371C4E172D00DE1CE4; 304 | }; 305 | 67B8A55B1C4E172D00DE1CE4 = { 306 | CreatedOnToolsVersion = 7.2; 307 | TestTargetID = 67B8A5371C4E172D00DE1CE4; 308 | }; 309 | }; 310 | }; 311 | buildConfigurationList = 67B8A5331C4E172D00DE1CE4 /* Build configuration list for PBXProject "PPDNSMappingDemo" */; 312 | compatibilityVersion = "Xcode 3.2"; 313 | developmentRegion = English; 314 | hasScannedForEncodings = 0; 315 | knownRegions = ( 316 | en, 317 | Base, 318 | ); 319 | mainGroup = 67B8A52F1C4E172D00DE1CE4; 320 | productRefGroup = 67B8A5391C4E172D00DE1CE4 /* Products */; 321 | projectDirPath = ""; 322 | projectRoot = ""; 323 | targets = ( 324 | 67B8A5371C4E172D00DE1CE4 /* PPDNSMappingDemo */, 325 | 67B8A5501C4E172D00DE1CE4 /* PPDNSMappingDemoTests */, 326 | 67B8A55B1C4E172D00DE1CE4 /* PPDNSMappingDemoUITests */, 327 | ); 328 | }; 329 | /* End PBXProject section */ 330 | 331 | /* Begin PBXResourcesBuildPhase section */ 332 | 67B8A5361C4E172D00DE1CE4 /* Resources */ = { 333 | isa = PBXResourcesBuildPhase; 334 | buildActionMask = 2147483647; 335 | files = ( 336 | 67B8A54B1C4E172D00DE1CE4 /* LaunchScreen.storyboard in Resources */, 337 | 67B8A5481C4E172D00DE1CE4 /* Assets.xcassets in Resources */, 338 | 67B8A5461C4E172D00DE1CE4 /* Main.storyboard in Resources */, 339 | 672F11851C4E633A001E1694 /* defaultMapping.txt in Resources */, 340 | ); 341 | runOnlyForDeploymentPostprocessing = 0; 342 | }; 343 | 67B8A54F1C4E172D00DE1CE4 /* Resources */ = { 344 | isa = PBXResourcesBuildPhase; 345 | buildActionMask = 2147483647; 346 | files = ( 347 | ); 348 | runOnlyForDeploymentPostprocessing = 0; 349 | }; 350 | 67B8A55A1C4E172D00DE1CE4 /* Resources */ = { 351 | isa = PBXResourcesBuildPhase; 352 | buildActionMask = 2147483647; 353 | files = ( 354 | ); 355 | runOnlyForDeploymentPostprocessing = 0; 356 | }; 357 | /* End PBXResourcesBuildPhase section */ 358 | 359 | /* Begin PBXSourcesBuildPhase section */ 360 | 67B8A5341C4E172D00DE1CE4 /* Sources */ = { 361 | isa = PBXSourcesBuildPhase; 362 | buildActionMask = 2147483647; 363 | files = ( 364 | 672F118E1C50803C001E1694 /* PPMappingNode.m in Sources */, 365 | 672F116F1C4E2A46001E1694 /* PPDomainRequestResult.m in Sources */, 366 | 672F11881C4F2C2E001E1694 /* PPResolvedUrl.m in Sources */, 367 | 67B8A5721C4E17F400DE1CE4 /* PPDNSMappingManager.m in Sources */, 368 | 67B8A5431C4E172D00DE1CE4 /* ViewController.m in Sources */, 369 | 672F11771C4E2D93001E1694 /* Reachability.m in Sources */, 370 | 67B8A5401C4E172D00DE1CE4 /* AppDelegate.m in Sources */, 371 | 672F116C1C4E1EC3001E1694 /* PPWeakTimer.m in Sources */, 372 | 67B8A53D1C4E172D00DE1CE4 /* main.m in Sources */, 373 | 672F11801C4E2FBC001E1694 /* PPIPValidator.m in Sources */, 374 | 672F11831C4E61CB001E1694 /* PPDNSReporter.m in Sources */, 375 | 672F118B1C4F3951001E1694 /* PPMappingUtil.m in Sources */, 376 | ); 377 | runOnlyForDeploymentPostprocessing = 0; 378 | }; 379 | 67B8A54D1C4E172D00DE1CE4 /* Sources */ = { 380 | isa = PBXSourcesBuildPhase; 381 | buildActionMask = 2147483647; 382 | files = ( 383 | 67B8A5561C4E172D00DE1CE4 /* PPDNSMappingDemoTests.m in Sources */, 384 | ); 385 | runOnlyForDeploymentPostprocessing = 0; 386 | }; 387 | 67B8A5581C4E172D00DE1CE4 /* Sources */ = { 388 | isa = PBXSourcesBuildPhase; 389 | buildActionMask = 2147483647; 390 | files = ( 391 | 67B8A5611C4E172D00DE1CE4 /* PPDNSMappingDemoUITests.m in Sources */, 392 | ); 393 | runOnlyForDeploymentPostprocessing = 0; 394 | }; 395 | /* End PBXSourcesBuildPhase section */ 396 | 397 | /* Begin PBXTargetDependency section */ 398 | 67B8A5531C4E172D00DE1CE4 /* PBXTargetDependency */ = { 399 | isa = PBXTargetDependency; 400 | target = 67B8A5371C4E172D00DE1CE4 /* PPDNSMappingDemo */; 401 | targetProxy = 67B8A5521C4E172D00DE1CE4 /* PBXContainerItemProxy */; 402 | }; 403 | 67B8A55E1C4E172D00DE1CE4 /* PBXTargetDependency */ = { 404 | isa = PBXTargetDependency; 405 | target = 67B8A5371C4E172D00DE1CE4 /* PPDNSMappingDemo */; 406 | targetProxy = 67B8A55D1C4E172D00DE1CE4 /* PBXContainerItemProxy */; 407 | }; 408 | /* End PBXTargetDependency section */ 409 | 410 | /* Begin PBXVariantGroup section */ 411 | 67B8A5441C4E172D00DE1CE4 /* Main.storyboard */ = { 412 | isa = PBXVariantGroup; 413 | children = ( 414 | 67B8A5451C4E172D00DE1CE4 /* Base */, 415 | ); 416 | name = Main.storyboard; 417 | sourceTree = ""; 418 | }; 419 | 67B8A5491C4E172D00DE1CE4 /* LaunchScreen.storyboard */ = { 420 | isa = PBXVariantGroup; 421 | children = ( 422 | 67B8A54A1C4E172D00DE1CE4 /* Base */, 423 | ); 424 | name = LaunchScreen.storyboard; 425 | sourceTree = ""; 426 | }; 427 | /* End PBXVariantGroup section */ 428 | 429 | /* Begin XCBuildConfiguration section */ 430 | 67B8A5631C4E172D00DE1CE4 /* Debug */ = { 431 | isa = XCBuildConfiguration; 432 | buildSettings = { 433 | ALWAYS_SEARCH_USER_PATHS = NO; 434 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 435 | CLANG_CXX_LIBRARY = "libc++"; 436 | CLANG_ENABLE_MODULES = YES; 437 | CLANG_ENABLE_OBJC_ARC = YES; 438 | CLANG_WARN_BOOL_CONVERSION = YES; 439 | CLANG_WARN_CONSTANT_CONVERSION = YES; 440 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 441 | CLANG_WARN_EMPTY_BODY = YES; 442 | CLANG_WARN_ENUM_CONVERSION = YES; 443 | CLANG_WARN_INT_CONVERSION = YES; 444 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 445 | CLANG_WARN_UNREACHABLE_CODE = YES; 446 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 447 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 448 | COPY_PHASE_STRIP = NO; 449 | DEBUG_INFORMATION_FORMAT = dwarf; 450 | ENABLE_STRICT_OBJC_MSGSEND = YES; 451 | ENABLE_TESTABILITY = YES; 452 | GCC_C_LANGUAGE_STANDARD = gnu99; 453 | GCC_DYNAMIC_NO_PIC = NO; 454 | GCC_NO_COMMON_BLOCKS = YES; 455 | GCC_OPTIMIZATION_LEVEL = 0; 456 | GCC_PREPROCESSOR_DEFINITIONS = ( 457 | "DEBUG=1", 458 | "$(inherited)", 459 | ); 460 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 461 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 462 | GCC_WARN_UNDECLARED_SELECTOR = YES; 463 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 464 | GCC_WARN_UNUSED_FUNCTION = YES; 465 | GCC_WARN_UNUSED_VARIABLE = YES; 466 | IPHONEOS_DEPLOYMENT_TARGET = 9.2; 467 | MTL_ENABLE_DEBUG_INFO = YES; 468 | ONLY_ACTIVE_ARCH = YES; 469 | SDKROOT = iphoneos; 470 | TARGETED_DEVICE_FAMILY = "1,2"; 471 | }; 472 | name = Debug; 473 | }; 474 | 67B8A5641C4E172D00DE1CE4 /* Release */ = { 475 | isa = XCBuildConfiguration; 476 | buildSettings = { 477 | ALWAYS_SEARCH_USER_PATHS = NO; 478 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 479 | CLANG_CXX_LIBRARY = "libc++"; 480 | CLANG_ENABLE_MODULES = YES; 481 | CLANG_ENABLE_OBJC_ARC = YES; 482 | CLANG_WARN_BOOL_CONVERSION = YES; 483 | CLANG_WARN_CONSTANT_CONVERSION = YES; 484 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 485 | CLANG_WARN_EMPTY_BODY = YES; 486 | CLANG_WARN_ENUM_CONVERSION = YES; 487 | CLANG_WARN_INT_CONVERSION = YES; 488 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 489 | CLANG_WARN_UNREACHABLE_CODE = YES; 490 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 491 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 492 | COPY_PHASE_STRIP = NO; 493 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 494 | ENABLE_NS_ASSERTIONS = NO; 495 | ENABLE_STRICT_OBJC_MSGSEND = YES; 496 | GCC_C_LANGUAGE_STANDARD = gnu99; 497 | GCC_NO_COMMON_BLOCKS = YES; 498 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 499 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 500 | GCC_WARN_UNDECLARED_SELECTOR = YES; 501 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 502 | GCC_WARN_UNUSED_FUNCTION = YES; 503 | GCC_WARN_UNUSED_VARIABLE = YES; 504 | IPHONEOS_DEPLOYMENT_TARGET = 9.2; 505 | MTL_ENABLE_DEBUG_INFO = NO; 506 | SDKROOT = iphoneos; 507 | TARGETED_DEVICE_FAMILY = "1,2"; 508 | VALIDATE_PRODUCT = YES; 509 | }; 510 | name = Release; 511 | }; 512 | 67B8A5661C4E172D00DE1CE4 /* Debug */ = { 513 | isa = XCBuildConfiguration; 514 | buildSettings = { 515 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 516 | INFOPLIST_FILE = PPDNSMappingDemo/Info.plist; 517 | IPHONEOS_DEPLOYMENT_TARGET = 7.1; 518 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 519 | PRODUCT_BUNDLE_IDENTIFIER = com.music4kid.PPDNSMappingDemo; 520 | PRODUCT_NAME = "$(TARGET_NAME)"; 521 | }; 522 | name = Debug; 523 | }; 524 | 67B8A5671C4E172D00DE1CE4 /* Release */ = { 525 | isa = XCBuildConfiguration; 526 | buildSettings = { 527 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 528 | INFOPLIST_FILE = PPDNSMappingDemo/Info.plist; 529 | IPHONEOS_DEPLOYMENT_TARGET = 7.1; 530 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 531 | PRODUCT_BUNDLE_IDENTIFIER = com.music4kid.PPDNSMappingDemo; 532 | PRODUCT_NAME = "$(TARGET_NAME)"; 533 | }; 534 | name = Release; 535 | }; 536 | 67B8A5691C4E172D00DE1CE4 /* Debug */ = { 537 | isa = XCBuildConfiguration; 538 | buildSettings = { 539 | BUNDLE_LOADER = "$(TEST_HOST)"; 540 | INFOPLIST_FILE = PPDNSMappingDemoTests/Info.plist; 541 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 542 | PRODUCT_BUNDLE_IDENTIFIER = com.music4kid.PPDNSMappingDemoTests; 543 | PRODUCT_NAME = "$(TARGET_NAME)"; 544 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/PPDNSMappingDemo.app/PPDNSMappingDemo"; 545 | }; 546 | name = Debug; 547 | }; 548 | 67B8A56A1C4E172D00DE1CE4 /* Release */ = { 549 | isa = XCBuildConfiguration; 550 | buildSettings = { 551 | BUNDLE_LOADER = "$(TEST_HOST)"; 552 | INFOPLIST_FILE = PPDNSMappingDemoTests/Info.plist; 553 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 554 | PRODUCT_BUNDLE_IDENTIFIER = com.music4kid.PPDNSMappingDemoTests; 555 | PRODUCT_NAME = "$(TARGET_NAME)"; 556 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/PPDNSMappingDemo.app/PPDNSMappingDemo"; 557 | }; 558 | name = Release; 559 | }; 560 | 67B8A56C1C4E172D00DE1CE4 /* Debug */ = { 561 | isa = XCBuildConfiguration; 562 | buildSettings = { 563 | INFOPLIST_FILE = PPDNSMappingDemoUITests/Info.plist; 564 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 565 | PRODUCT_BUNDLE_IDENTIFIER = com.music4kid.PPDNSMappingDemoUITests; 566 | PRODUCT_NAME = "$(TARGET_NAME)"; 567 | TEST_TARGET_NAME = PPDNSMappingDemo; 568 | USES_XCTRUNNER = YES; 569 | }; 570 | name = Debug; 571 | }; 572 | 67B8A56D1C4E172D00DE1CE4 /* Release */ = { 573 | isa = XCBuildConfiguration; 574 | buildSettings = { 575 | INFOPLIST_FILE = PPDNSMappingDemoUITests/Info.plist; 576 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 577 | PRODUCT_BUNDLE_IDENTIFIER = com.music4kid.PPDNSMappingDemoUITests; 578 | PRODUCT_NAME = "$(TARGET_NAME)"; 579 | TEST_TARGET_NAME = PPDNSMappingDemo; 580 | USES_XCTRUNNER = YES; 581 | }; 582 | name = Release; 583 | }; 584 | /* End XCBuildConfiguration section */ 585 | 586 | /* Begin XCConfigurationList section */ 587 | 67B8A5331C4E172D00DE1CE4 /* Build configuration list for PBXProject "PPDNSMappingDemo" */ = { 588 | isa = XCConfigurationList; 589 | buildConfigurations = ( 590 | 67B8A5631C4E172D00DE1CE4 /* Debug */, 591 | 67B8A5641C4E172D00DE1CE4 /* Release */, 592 | ); 593 | defaultConfigurationIsVisible = 0; 594 | defaultConfigurationName = Release; 595 | }; 596 | 67B8A5651C4E172D00DE1CE4 /* Build configuration list for PBXNativeTarget "PPDNSMappingDemo" */ = { 597 | isa = XCConfigurationList; 598 | buildConfigurations = ( 599 | 67B8A5661C4E172D00DE1CE4 /* Debug */, 600 | 67B8A5671C4E172D00DE1CE4 /* Release */, 601 | ); 602 | defaultConfigurationIsVisible = 0; 603 | defaultConfigurationName = Release; 604 | }; 605 | 67B8A5681C4E172D00DE1CE4 /* Build configuration list for PBXNativeTarget "PPDNSMappingDemoTests" */ = { 606 | isa = XCConfigurationList; 607 | buildConfigurations = ( 608 | 67B8A5691C4E172D00DE1CE4 /* Debug */, 609 | 67B8A56A1C4E172D00DE1CE4 /* Release */, 610 | ); 611 | defaultConfigurationIsVisible = 0; 612 | defaultConfigurationName = Release; 613 | }; 614 | 67B8A56B1C4E172D00DE1CE4 /* Build configuration list for PBXNativeTarget "PPDNSMappingDemoUITests" */ = { 615 | isa = XCConfigurationList; 616 | buildConfigurations = ( 617 | 67B8A56C1C4E172D00DE1CE4 /* Debug */, 618 | 67B8A56D1C4E172D00DE1CE4 /* Release */, 619 | ); 620 | defaultConfigurationIsVisible = 0; 621 | defaultConfigurationName = Release; 622 | }; 623 | /* End XCConfigurationList section */ 624 | }; 625 | rootObject = 67B8A5301C4E172D00DE1CE4 /* Project object */; 626 | } 627 | -------------------------------------------------------------------------------- /PPDNSMappingDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /PPDNSMappingDemo.xcodeproj/xcuserdata/gaofeng.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 20 | 21 | 22 | 24 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /PPDNSMappingDemo.xcodeproj/xcuserdata/gaofeng.xcuserdatad/xcschemes/PPDNSMappingDemo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 43 | 49 | 50 | 51 | 52 | 53 | 59 | 60 | 61 | 62 | 63 | 64 | 74 | 76 | 82 | 83 | 84 | 85 | 86 | 87 | 93 | 95 | 101 | 102 | 103 | 104 | 106 | 107 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /PPDNSMappingDemo.xcodeproj/xcuserdata/gaofeng.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | PPDNSMappingDemo.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 67B8A5371C4E172D00DE1CE4 16 | 17 | primary 18 | 19 | 20 | 67B8A5501C4E172D00DE1CE4 21 | 22 | primary 23 | 24 | 25 | 67B8A55B1C4E172D00DE1CE4 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /PPDNSMappingDemo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // PPDNSMappingDemo 4 | // 5 | // Created by gao feng on 16/1/19. 6 | // Copyright © 2016年 gao feng. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /PPDNSMappingDemo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // PPDNSMappingDemo 4 | // 5 | // Created by gao feng on 16/1/19. 6 | // Copyright © 2016年 gao feng. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | - (void)applicationWillResignActive:(UIApplication *)application { 24 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 25 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 26 | } 27 | 28 | - (void)applicationDidEnterBackground:(UIApplication *)application { 29 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 30 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 31 | } 32 | 33 | - (void)applicationWillEnterForeground:(UIApplication *)application { 34 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 35 | } 36 | 37 | - (void)applicationDidBecomeActive:(UIApplication *)application { 38 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 39 | } 40 | 41 | - (void)applicationWillTerminate:(UIApplication *)application { 42 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /PPDNSMappingDemo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /PPDNSMappingDemo/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /PPDNSMappingDemo/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /PPDNSMappingDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /PPDNSMappingDemo/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // PPDNSMappingDemo 4 | // 5 | // Created by gao feng on 16/1/19. 6 | // Copyright © 2016年 gao feng. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /PPDNSMappingDemo/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // PPDNSMappingDemo 4 | // 5 | // Created by gao feng on 16/1/19. 6 | // Copyright © 2016年 gao feng. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "PPDNSMapping.h" 11 | 12 | @interface ViewController () 13 | 14 | @end 15 | 16 | @implementation ViewController 17 | 18 | - (void)viewDidLoad { 19 | [super viewDidLoad]; 20 | // Do any additional setup after loading the view, typically from a nib. 21 | 22 | __block NSString* mapping1; 23 | __block NSString* mapping2; 24 | __block NSString* mapping3; 25 | 26 | NSString* filePath = [[NSBundle mainBundle] pathForResource:@"defaultMapping" ofType:@"txt"]; 27 | [[PPDNSMappingManager sharedInstance] setDefaultLocalMapping:filePath]; 28 | 29 | // [[PPDNSMappingManager sharedInstance] readMappingFromDisk]; 30 | 31 | //test mapping sync 32 | // NSString* syncUrl = @"https://10.43.111.25/testapp/mapping"; 33 | // [[PPDNSMappingManager sharedInstance] startSyncTimerWithInterval:5 withSrcUrl:syncUrl]; 34 | 35 | //do mapping 36 | NSString* testUrl = @"http://avatar.testapp.com:80/test;params?a=b#fragment"; 37 | PPResolvedUrl* resolved = [[PPDNSMappingManager sharedInstance] resolveUrl:[NSURL URLWithString:testUrl]]; 38 | NSLog(@"resolved url:%@ host:%@", resolved.resolvedUrl, resolved.resolvedHost); 39 | mapping1 = resolved.resolvedUrl; 40 | NSAssert(mapping1 != nil, @"valid mapping expected"); 41 | 42 | //simulate request fail 43 | PPDomainRequestResult* result = [PPDomainRequestResult new]; 44 | result.resultUrl = [NSURL URLWithString:resolved.resolvedUrl]; 45 | result.status = PPDomainRequestFail; 46 | [[NSNotificationCenter defaultCenter] postNotificationName:Notif_DomainRequestResult object:result]; 47 | 48 | //try mapping in 1 seconds 49 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 50 | PPResolvedUrl* resolved = [[PPDNSMappingManager sharedInstance] resolveUrl:[NSURL URLWithString:testUrl]]; 51 | NSLog(@"resolved url again:%@ host:%@", resolved.resolvedUrl, resolved.resolvedHost); 52 | mapping2 = resolved.resolvedUrl; 53 | 54 | NSAssert([mapping1 isEqualToString:mapping2] == false, @"different mapping expected."); 55 | 56 | 57 | //simulate request fail again 58 | PPDomainRequestResult* result = [PPDomainRequestResult new]; 59 | result.resultUrl = [NSURL URLWithString:resolved.resolvedUrl]; 60 | result.status = PPDomainRequestFail; 61 | [[NSNotificationCenter defaultCenter] postNotificationName:Notif_DomainRequestResult object:result]; 62 | 63 | 64 | //try mapping in 1 seconds 65 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 66 | PPResolvedUrl* resolved = [[PPDNSMappingManager sharedInstance] resolveUrl:[NSURL URLWithString:testUrl]]; 67 | NSLog(@"resolved url again:%@ host:%@", resolved.resolvedUrl, resolved.resolvedHost); 68 | mapping3 = resolved.resolvedUrl; 69 | 70 | NSAssert([mapping3 isEqualToString:testUrl], @"no mapping expected."); 71 | }); 72 | }); 73 | } 74 | 75 | - (void)didReceiveMemoryWarning { 76 | [super didReceiveMemoryWarning]; 77 | // Dispose of any resources that can be recreated. 78 | } 79 | 80 | @end 81 | -------------------------------------------------------------------------------- /PPDNSMappingDemo/defaultMapping.txt: -------------------------------------------------------------------------------- 1 | {"http://test.testapp.com":["https://test2.testapp.com"],"http://avatar.testapp.com:80":["http://10.54.23.182","http://10.54.23.174:80"]} -------------------------------------------------------------------------------- /PPDNSMappingDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // PPDNSMappingDemo 4 | // 5 | // Created by gao feng on 16/1/19. 6 | // Copyright © 2016年 gao feng. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /PPDNSMappingDemoTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /PPDNSMappingDemoTests/PPDNSMappingDemoTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // PPDNSMappingDemoTests.m 3 | // PPDNSMappingDemoTests 4 | // 5 | // Created by gao feng on 16/1/19. 6 | // Copyright © 2016年 gao feng. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface PPDNSMappingDemoTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation PPDNSMappingDemoTests 16 | 17 | - (void)setUp { 18 | [super setUp]; 19 | // Put setup code here. This method is called before the invocation of each test method in the class. 20 | } 21 | 22 | - (void)tearDown { 23 | // Put teardown code here. This method is called after the invocation of each test method in the class. 24 | [super tearDown]; 25 | } 26 | 27 | - (void)testExample { 28 | // This is an example of a functional test case. 29 | // Use XCTAssert and related functions to verify your tests produce the correct results. 30 | } 31 | 32 | - (void)testPerformanceExample { 33 | // This is an example of a performance test case. 34 | [self measureBlock:^{ 35 | // Put the code you want to measure the time of here. 36 | }]; 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /PPDNSMappingDemoUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /PPDNSMappingDemoUITests/PPDNSMappingDemoUITests.m: -------------------------------------------------------------------------------- 1 | // 2 | // PPDNSMappingDemoUITests.m 3 | // PPDNSMappingDemoUITests 4 | // 5 | // Created by gao feng on 16/1/19. 6 | // Copyright © 2016年 gao feng. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface PPDNSMappingDemoUITests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation PPDNSMappingDemoUITests 16 | 17 | - (void)setUp { 18 | [super setUp]; 19 | 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | 22 | // In UI tests it is usually best to stop immediately when a failure occurs. 23 | self.continueAfterFailure = NO; 24 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 25 | [[[XCUIApplication alloc] init] launch]; 26 | 27 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 28 | } 29 | 30 | - (void)tearDown { 31 | // Put teardown code here. This method is called after the invocation of each test method in the class. 32 | [super tearDown]; 33 | } 34 | 35 | - (void)testExample { 36 | // Use recording to get started writing UI tests. 37 | // Use XCTAssert and related functions to verify your tests produce the correct results. 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 绝大多数网络请求的第一步都是DNS解析,解析请求根据当时网络情况不同,各平台的DNS缓存策略差异等因素,对移动端app整体网络性能会产生或大或小的影响。移动端app网络性能优化涉及到很多方面,DNS映射只是其中一环,也是十分重要的一环,因为它带来的好处不仅仅是降低网络请求的延迟。 2 | 3 | #### 降低DNS请求带来的延迟 4 | 客户端app的请求第一步都是DNS解析,但由于cache的存在使得大部分的解析请求并不会产生任何延迟。各品台都有自己的cache过期策略。像iOS系统一般是24小时之后会过期,还有进入飞行模式再切回来,开关机,重置网络设置等也会导致DNS cache的清除。所以一般情况下用户在第二天打开你的app都会经历一次完整的DNS解析请求,网络情况差的时候会明显增加应用请求的总耗时。如果能直接跳过DNS解析这一步,当然能提升网络性能了。 5 | 6 | #### 预防DNS劫持 7 | DNS劫持指的是改变DNS请求的返回结果,将目的ip指向另一个地址。一般有两种方式,一是通过病毒的方式改变本机配置的DNS服务器地址,而是通过攻击正常DNS服务器而改变其行为。不管是哪种方式,都会影响app本身的业务请求。如果遇到恶意的攻击还会衍生出各种安全问题。客户端自己做DNS与ip地址的映射就跨过了解析,让劫持者无从下手。 8 | 9 | #### 服务器动态部署 10 | DNS映射实际是模拟了DNS请求的解析行为。如果客户端将自己的位置信息诸如ip地址,国家码等加入映射文件的请求参数当中,服务器就可以根据客户端所处的位置不同,下发距离其物理位置最近的server ip地址,从而减小整体网络请求的延迟,实现一定程度的服务器动态部署。 11 | 12 | #### 如何设计自己的DNS映射机制? 13 | DNS解析请求简单来说,无非是输入一个域名,输出一个ip地址。做自己的映射机制也就是客户端本地维护这样一个映射文件,只不过这个映射文件需要能从服务器更新,还要做一些容错处理。我们先从这几个基本要求出发制定下面几个必须满足的需求。 14 | 15 | * 一个打包到app包里面的默认映射文件,这样可以避免第一次去服务器取配置文件带来的延迟。 16 | * 有一个定时器能每隔一段时间从服务器获取最新的映射,并覆盖本地。 17 | * 每次取到最新的映射文件之后,同时把上一次的映射文件保存起来作为替补,一旦出现线上配置失误不至于导致请求无法处理。 18 | * 如果映射文件不能处理域名,要能回滚使用默认的DNS解析服务。 19 | * 如果一个映射过后的ip持续导致请求失败,应该能从机制上保证这个ip地址不再使用。也就是需要一个无效映射淘汰机制。 20 | * 无效的ip地址能及时上报到服务器,及时发现问题更新映射文件。 21 | 22 | 基于这些基本需求,可以做出如下简单的设计: 23 | 24 | 25 | 大致有3个角色,mapper,validator,reporter。各自职责如下: 26 | 27 | **mapper** 28 | 29 | mapper是和外部交互的部分,主要负责在输入domain的情况下输出ip,同时还要检测来自应用层请求成功和失败的信息。失败的情况下要将失败的ip进行进一步的检测,以确定是否真的是ip地址无效,如果无效则进行上报。mapper还要负责从mapper文件的更新机制。 30 | 31 | **validator** 32 | 33 | validator在接受到请求失败的ip时,要负责对这个ip做进一步的有效性检测。检测规则的强弱可自己定制。但一般来说流程在后台线程使用这个地址做多次连接尝试。如果失败则告诉mapper这个地址确实无效。如果成功则表明这个地址有效,很有可能只是当时的网络环境导致了请求的失败。 34 | 35 | **reporter** 36 | 37 | reporter主要负责告诉server整个mapping机制的健康状况。在出现某个ip经过validator检测依然失败的情况下,要及时的告诉server出问题的ip。很有可能出现了某个服务器故障或者映射文件的配置失误等等。 38 | 39 | 40 | 41 | 在上面这些基础需求之外,还可以根据自身的业务特点及技术条件做一些深度定制。比如从服务器定期更新映射文件,可以改成socket长链接通道在需要更新时push,或者利用http2.0的server push机制。还有上报机制,除了上报错误的ip地址映射之外,还可以对请求的总量,成功率,映射成功率等数据进行侦测。 42 | 43 | 44 | #### PPDNSMapping 45 | 46 | 47 | --------------------------------------------------------------------------------