Full Code of CNSRE/HTTPDNSLib-for-iOS for AI

master c2c4cd657b1b cached
51 files
202.9 KB
58.0k tokens
3 symbols
1 requests
Download .txt
Showing preview only (225K chars total). Download the full file or copy to clipboard to get everything.
Repository: CNSRE/HTTPDNSLib-for-iOS
Branch: master
Commit: c2c4cd657b1b
Files: 51
Total size: 202.9 KB

Directory structure:
gitextract_sdz2bv6d/

├── .gitignore
├── LICENSE
├── README.md
└── src/
    ├── DNSCache/
    │   ├── HttpDns/
    │   │   ├── WBDNSHttpDnsManager.h
    │   │   └── WBDNSHttpDnsManager.m
    │   ├── LogManager/
    │   │   ├── WBDNSLogFile.h
    │   │   ├── WBDNSLogFile.m
    │   │   ├── WBDNSLogManager.h
    │   │   ├── WBDNSLogManager.m
    │   │   ├── WBDNSLogger.h
    │   │   └── WBDNSLogger.m
    │   ├── Models/
    │   │   ├── WBDNSConfig.h
    │   │   ├── WBDNSConfig.m
    │   │   ├── WBDNSDomainModel.h
    │   │   ├── WBDNSDomainModel.m
    │   │   ├── WBDNSHttpDnsPack.h
    │   │   ├── WBDNSHttpDnsPack.m
    │   │   ├── WBDNSIP.h
    │   │   ├── WBDNSIP.m
    │   │   ├── WBDNSIpModel.h
    │   │   ├── WBDNSIpModel.m
    │   │   ├── WBDNSMemeryCache.h
    │   │   ├── WBDNSMemeryCache.m
    │   │   └── WBDNSModel.h
    │   ├── QueryManager/
    │   │   ├── WBDNSQueryManager.h
    │   │   └── WBDNSQueryManager.m
    │   ├── SortManager/
    │   │   ├── WBDNSSortManager.h
    │   │   └── WBDNSSortManager.m
    │   ├── SpeedTest/
    │   │   ├── WBDNSSpeedTestManager.h
    │   │   ├── WBDNSSpeedTestManager.m
    │   │   ├── WBDNSTCPSpeedTester.h
    │   │   └── WBDNSTCPSpeedTester.m
    │   ├── Tools/
    │   │   ├── WBDNSTools.h
    │   │   ├── WBDNSTools.m
    │   │   ├── WBDNSWeakTimer.h
    │   │   └── WBDNSWeakTimer.m
    │   ├── WBDNSCache.h
    │   ├── WBDNSCache.m
    │   ├── WBDNSConfigManager.h
    │   ├── WBDNSConfigManager.m
    │   ├── WBDNSNetworkManager.h
    │   ├── WBDNSNetworkManager.m
    │   ├── WBDNSReachability.h
    │   ├── WBDNSReachability.m
    │   ├── WBDNSSpeedTester.h
    │   └── cache/
    │       ├── WBDNSCacheManager.h
    │       ├── WBDNSCacheManager.m
    │       ├── WBDNSDBManager.h
    │       └── WBDNSDBManager.m
    └── DNSCache.xcodeproj/
        ├── project.pbxproj
        └── project.xcworkspace/
            └── contents.xcworkspacedata

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
# Xcode
#
build/
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata
*.xccheckout
*.moved-aside
DerivedData
*.hmap
*.ipa
*.xcuserstate

# CocoaPods
#
# We recommend against adding the Pods directory to your .gitignore. However
# you should judge for yourself, the pros and cons are mentioned at:
# http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control
#
#Pods/


================================================
FILE: LICENSE
================================================
Copyright (c) 2015, 渣浪研发中心技术保障部移动端保障团队
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.



================================================
FILE: README.md
================================================
# HTTPDNSLib-for-iOS
DNSCache库使用说明书

目前我们的服务器还不能支持所有的域名,所以代码里有白名单来限制域名。现在还不建议大家用在商用项目里,仅做学习交流用。

1. 导入LibDnsCache.a, WBDNSCache.h. (如果愿意,也可以将DNSCache整个工程导入)

2. 在Targets-》Build Phases-》LinkBinaryWithLibraries 加入libDNSCache.a.  httpDNSLib依赖libsqlite3.dylib, SystemConfiguration.framework, CoreTelephony.framework. 请同时加入以上依赖库。

3. 确定Targets-》BuildingSetting-》SearchPaths-》Library Search Path 可以搜索到正确的库文件。
注意,库分为模拟器版本和真机版本,请确定自己导入的是正确的版本,或者库路径查找 能首先查到正确的版本,有时候能找到两个版本,系统会已第一个找到的版本为准,导致link错误。

4。建议在AppDelegate里(也就是尽可能早的时候)初始化 WBDNSCache库。
设置AppKey和版本,用于请求对应版本的配置参数
以下只是一个示例,如果需要从sina服务器拉取配置,需要申请自己的AppKey,否则请手动修改代码获取自己的配置。
[WBDNSCache setAppkey:@"ed3e6e90975f52876cd9d74a8e9e05d8" version:@"0.1"];
设置配置参数服务器的URL
[WBDNSCache setConfigServerUrl:@"http://api.weibo.cn/2/httpdns/config"];
初始化库,期间会从参数服务器请求配置参数
[[WBDNSCache sharedInstance] initialize];

5.建议初始化后延时调用 预请求域名对应IP,提前从服务器拉取域名对应IP
[[WBDNSCache sharedInstance]preloadDomains:@[@"http://ww4.sinaimg.cn", @"http://api.weibo.cn/"]];

6.然后就可以在任何地方调用
[[WBDNSCache sharedInstance] getDomainServerIpFromURL:url]
获取转换后Url 和 需要设置的host值。
这个函数拿到的是一个WBDNSDomainInfo 对象数组,一般来说 取第一个就可以了。
WBDNSDomainInfo.id 暂时没用。
WBDNSDomainInfo.url 已经替换好的URL, 客户端可以直接用它 请求资源。
WBDNSDomainInfo.host 客户端需要将这个host设置到HTTP的请求头里。 如果Host为@“” 代表不需要设置Host
以AFNetworking举例
[manager.requestSerializer setValue:WBDNSDomainInfo.host forHTTPHeaderField:@"Host"];


================================================
FILE: src/DNSCache/HttpDns/WBDNSHttpDnsManager.h
================================================
//
//  HttpDnsManager.h
//  DNSCache
//
//  Created by Robert Yang on 15/8/4.
//  Copyright (c) 2015年 Weibo. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "WBDNSModel.h"
#import "WBDNSCache.h"
@protocol WBDNSHttpDnsProtocol

- (void)requestHttpDnsByDomain:(NSString *)domain completionHandler:(void(^)(WBDNSHttpDnsPack *))completionHandler;

@end

@interface WBDNSHttpDnsManager : NSObject <WBDNSHttpDnsProtocol>

@end


================================================
FILE: src/DNSCache/HttpDns/WBDNSHttpDnsManager.m
================================================
//
//  HttpDnsManager.m
//  DNSCache
//
//  Created by Robert Yang on 15/8/4.
//  Copyright (c) 2015年 Weibo. All rights reserved.
//

#import "WBDNSHttpDnsManager.h"
#import "WBDNSNetworkManager.h"
#import "WBDNSConfig.h"
#import "WBDNSConfigManager.h"
#import "WBDNSLogManager.h"

@implementation WBDNSHttpDnsManager

- (void)requestHttpDnsByDomain:(NSString *)domain completionHandler:(void(^)(WBDNSHttpDnsPack *))completionHandler {
    NSString* urlPrefix = [[WBDNSConfigManager sharedInstance] getServerUrl];
    if (urlPrefix == nil) {
        NSLog(@"ERROR:%s:%d urlPrefix is nil.", __FUNCTION__, __LINE__);
        return;
    }
    NSURL *url;
    if ([urlPrefix hasPrefix:@"http://"]||[urlPrefix hasPrefix:@"https://"]) {
        url = [NSURL URLWithString:[NSString stringWithFormat:@"%@/dns?domain=%@", urlPrefix, domain]];
    }
    else {
        url = [NSURL URLWithString:[NSString stringWithFormat:@"http://%@/dns?domain=%@",  urlPrefix, domain]];
    }
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    NSURLSessionConfiguration *config = [NSURLSessionConfiguration ephemeralSessionConfiguration];
    
    NSURLSession *session = [NSURLSession sessionWithConfiguration:config];
    session.sessionDescription = urlPrefix;
    NSURLSessionDataTask* task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        if (!error) {
            //NSString* str = [[NSString alloc] initWithData:data  encoding:NSUTF8StringEncoding];
            NSDictionary* jsonDic = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:nil];
            if (jsonDic) {
                WBDNSHttpDnsPack* dnsPack = [WBDNSHttpDnsPack generateInstanceFromDic:jsonDic];
                dnsPack.localhostSp = [WBDNSNetworkManager sharedInstance].currentSpTypeString;
                
                [WBDNSLogManager log:WBDNS_LOG_TYPE_INFO action:WBDNS_LOG_ACTION_INFO_PACK body:[dnsPack toDictionary] samplingRate:[WBDNSConfigManager sharedInstance].config.logSamplingRate];
                
                //检测服务器返回的运营商SP 是否正确
                if ([WBDNSNetworkManager sharedInstance].networkType == WBDNS_NETWORK_TYPE_MOBILE) {
                    
                    NSInteger deviceSpCode = [dnsPack.device_sp integerValue];
                    if (![dnsPack.localhostSp isEqualToString:[WBDNSTools serviceProviderTypeToString:(int)deviceSpCode]]) {
                        [WBDNSLogManager log:WBDNS_LOG_TYPE_ERROR action:WBDNS_LOG_ACTION_ERR_SPINFO body:[dnsPack toDictionary]];
                    }
                }
                
                if (completionHandler) {
                    completionHandler(dnsPack);
                }
            }
            else {
                NSString* str = [[NSString alloc] initWithData:data  encoding:NSUTF8StringEncoding];
                NSLog(@"ERROR:%s:%d request ip from sina dns failed reason:response data is not a valid json. \n response:\n%@",__FUNCTION__,__LINE__,str);
                if (completionHandler) {
                    completionHandler(nil);
                }
            }
            
        }
        else {
            if (completionHandler) {
                completionHandler(nil);
            }
            
            [[WBDNSConfigManager sharedInstance] setServerUrlFailedTimes:session.sessionDescription];
            NSLog(@"ERROR:%s:%d request ip from sina dns failed reason:%@.",__FUNCTION__,__LINE__, error.description);
            
        }
    }];
    [task resume];
    [session finishTasksAndInvalidate];
}

@end


================================================
FILE: src/DNSCache/LogManager/WBDNSLogFile.h
================================================
//
//  WBDNSLogFile.h
//  DNSCache
//
//  Created by Robert Yang on 15/8/27.
//  Copyright (c) 2015年 Weibo. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface WBDNSLogFile : NSObject

@property (strong, nonatomic) NSString *filePath;

@property (strong, nonatomic) NSString *fileName;

@property (strong, nonatomic) NSDate *createDate;

@property (strong, nonatomic) NSDate *modifyDate;

@property (assign, nonatomic) NSInteger fileSize;

- (instancetype)initWithPath:(NSString *)filePath;

@end


================================================
FILE: src/DNSCache/LogManager/WBDNSLogFile.m
================================================
//
//  WBDNSLogFile.m
//  DNSCache
//
//  Created by Robert Yang on 15/8/27.
//  Copyright (c) 2015年 Weibo. All rights reserved.
//

#import "WBDNSLogFile.h"

@implementation WBDNSLogFile

- (instancetype)initWithPath:(NSString *)filePath {
    NSDictionary* fileInfo = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil];
    if (fileInfo == nil) {
        NSLog(@"ERROR:%s:%d: filePath(%@) is invalid.",__func__,__LINE__, filePath);
        return nil;
    }
    
    if ((self = [super init])) {
        _filePath = [filePath copy];
        _fileName = [filePath lastPathComponent];
        _createDate = fileInfo[NSFileCreationDate];
        _modifyDate = fileInfo[NSFileModificationDate];
        _fileSize = [fileInfo[NSFileSize] integerValue];
        
    }
    return self;
}

@end


================================================
FILE: src/DNSCache/LogManager/WBDNSLogManager.h
================================================
//
//  WBDNSLogManager.h
//  DNSCache
//
//  Created by Robert Yang on 15/8/27.
//  Copyright (c) 2015年 Weibo. All rights reserved.
//

#import <Foundation/Foundation.h>

typedef enum WBDNS_LOG_TYPE
{
    WBDNS_LOG_TYPE_ERROR = 1,
    WBDNS_LOG_TYPE_INFO =2,
    WBDNS_LOG_TYPE_SPEED = 3
} WBDNS_LOG_TYPE;

#define WBDNS_LOG_ACTION_INFO_DOMAIN  @"httpdns_domaininfo"
#define WBDNS_LOG_ACTION_INFO_PACK  @"httpdns_packinfo"
#define WBDNS_LOG_ACTION_INFO_CONFIG  @"httpdns_configinfo"
#define WBDNS_LOG_ACTION_ERR_SPINFO  @"httpdns_errspinfo"
#define WBDNS_LOG_ACTION_ERR_DOMAININFO  @"httpdns_errdomaininfo"

@interface WBDNSLogManager : NSObject

- (void)uploadLogFiles;

+ (instancetype)sharedInstance;

+ (void)log:(int)type action:(NSString*)action body:(NSDictionary*)body;

+ (void)log:(int)type action:(NSString*)action body:(NSDictionary*)body samplingRate:(int)samplingRate;

@end


================================================
FILE: src/DNSCache/LogManager/WBDNSLogManager.m
================================================
//
//  WBDNSLogManager.m
//  DNSCache
//
//  Created by Robert Yang on 15/8/27.
//  Copyright (c) 2015年 Weibo. All rights reserved.
//

#import "WBDNSLogManager.h"
#import "WBDNSLogger.h"
#import "WBDNSConfigManager.h"
#import "WBDNSTools.h"
#import "WBDNSLogFile.h"

@implementation WBDNSLogManager {
    WBDNSLogger *_logger;
    NSString *_appVersion;
}

+ (instancetype)sharedInstance {
    static WBDNSLogManager* sharedInstance;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[WBDNSLogManager alloc]init];
    });
    return sharedInstance;
}

- (instancetype)init {
    if ((self = [super init])) {
        _logger = [WBDNSLogger sharedInstance];
        NSString *notificationName = @"UIApplicationWillTerminateNotification";
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(applicationWillTerminate)
                                                     name:notificationName
                                                   object:nil];
    }
    
    return self;
}

- (void)applicationWillTerminate {
    [_logger rollLogFileNow];
}

- (void)dealloc {
    [[NSNotificationCenter defaultCenter]removeObserver:self];
}

- (void)uploadLogFiles {
    [_logger uploadLogFiles];
}

- (void)removeLogFile:(WBDNSLogFile *)logFile {
    [_logger removeFile:logFile];
}

- (NSString *)generateJsonStrByLogType:(int)type action:(NSString *)action body:(NSDictionary *)body {
    NSDictionary* dic = @{@"type":@(type),
                          @"action":action,
                          @"content":body,
                          @"versionName":[WBDNSConfigManager getAppVersion],
                          @"did":@"iOSCan'tGetDid",
                          @"appkey":[WBDNSConfigManager getAppkey],
                          @"timestamp":[[WBDNSTools sharedInstance] stringFromDate:[NSDate date]]
                          };
    NSData * jsonData = [NSJSONSerialization dataWithJSONObject:dic options:0 error:nil];
    NSString * myString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
    return myString;
}


+ (void)log:(int)type action:(NSString *)action body:(NSDictionary *)body {
    [[WBDNSLogManager sharedInstance] writeLog:type action:action body:body];
}

+ (void)log:(int)type action:(NSString *)action body:(NSDictionary *) body samplingRate:(int)samplingRate {
    [[WBDNSLogManager sharedInstance] writeLog:type action:action body:body samplingRate:samplingRate];
}


- (void)writeLog:(int)type action:(NSString *)action body:(NSDictionary *) body {
    [self writeLog:type action:action body:body samplingRate:1];
}

- (void)writeLog:(int)type action:(NSString *)action body:(NSDictionary *)body samplingRate:(int) samplingRate {
    if (samplingRate <= 0) {
        return;
    }
    //安卓版本对采样率的定义是,采样率是50 就代表50次取一次。ios版本的理解是 50%,这里适配安卓的理解。
    float androidSamplingRate = 1.0f/samplingRate;
    float temp = ((float)(arc4random()%1000000))/1000000 ;
    if (temp > androidSamplingRate) {
        return;
    }
    
    NSString* message = [self generateJsonStrByLogType:type action:action body:body];
    NSString* newMessage = [NSString stringWithFormat:@"%@,", message];
    [_logger logMessage:newMessage];
}

@end


================================================
FILE: src/DNSCache/LogManager/WBDNSLogger.h
================================================
//
//  WBDNSLogger.h
//  DNSCache
//
//  Created by Robert Yang on 15/8/27.
//  Copyright (c) 2015年 Weibo. All rights reserved.
//

#import <Foundation/Foundation.h>
@class WBDNSLogFile;
@interface WBDNSLogger : NSObject

+ (instancetype)sharedInstance;

- (void)logMessage:(NSString *)Message;

- (NSArray *)unsortedLogFiles;

- (void)removeFile:(WBDNSLogFile *)logFile;

- (void)uploadLogFiles;

- (void)rollLogFileNow;

@end


================================================
FILE: src/DNSCache/LogManager/WBDNSLogger.m
================================================
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2015, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,
// with or without modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
//   this list of conditions and the following disclaimer.
//
// * Neither the name of Deusty nor the names of its contributors may be used
//   to endorse or promote products derived from this software without specific
//   prior written permission of Deusty, LLC.
//
//  updated by Robert Yang on 15/8/27.
//  Copyright (c) 2015年 Weibo. All rights reserved.
//

#import "WBDNSLogger.h"
#import "WBDNSLogFile.h"
#import "WBDNSConfigManager.h"
#import "WBDNSTools.h"
#import <UIKit/UIKit.h>

NSUInteger const WBDNS_DEFAULT_MAX_LOG_FILE_SIZE = 512*1024; //512KB

NSUInteger const WBDNS_DEFAULT_MAX_LOG_FILE_NUM = 10;

@implementation WBDNSLogger
{
    NSUInteger _maxFileSize;
    NSUInteger _maxFileNum;
    NSString *_logDirectory;
    NSDateFormatter *_dateFormatter;
    NSFileHandle *_currentFileHandle;
    WBDNSLogFile *_currentLogFile;
    dispatch_queue_t _loggerQueue;
    dispatch_source_t _currentLogFileVnode;
    NSMutableDictionary *_runningUploadTask;
}


+ (instancetype)sharedInstance {
    static WBDNSLogger* sharedInstance;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[WBDNSLogger alloc]init];
    });
    return sharedInstance;
}

- (instancetype)init {
    return [self initWithLogDirectory:nil];
}

- (void)dealloc {
    if (_currentFileHandle) {
        [_currentFileHandle synchronizeFile];
        [_currentFileHandle closeFile];
        _currentFileHandle = nil;
    }
    
    if (_currentLogFileVnode) {
        dispatch_source_cancel(_currentLogFileVnode);
        _currentLogFileVnode = NULL;
    }
}

- (instancetype)initWithLogDirectory:(NSString *)logDirectory {
    if ((self = [super init])) {
        _maxFileNum = WBDNS_DEFAULT_MAX_LOG_FILE_NUM;
        _maxFileSize = WBDNS_DEFAULT_MAX_LOG_FILE_SIZE;
        
        if (logDirectory) {
            _logDirectory = [logDirectory copy];
        } else {
            _logDirectory = [[self defaultLogDirectory] copy];
        }
        
        _dateFormatter = [[NSDateFormatter alloc]init];
        [_dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss Z"];
        _loggerQueue = dispatch_queue_create([@"com.weibo.dnscache.fileLogger" UTF8String], NULL);
        _runningUploadTask = [NSMutableDictionary dictionary];
        [self getLogDirectory];
    }
    
    return self;
}

- (NSString *)defaultLogDirectory {
    
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
    NSString *baseDir = paths.firstObject;
    NSString *logDirectory = [baseDir stringByAppendingPathComponent:@"WBDNSLogs"];
    
    return logDirectory;
}

- (NSString *) getLogDirectory {
    if (![[NSFileManager defaultManager] fileExistsAtPath:_logDirectory]) {
        NSError *err = nil;
        
        if (![[NSFileManager defaultManager] createDirectoryAtPath:_logDirectory
                                       withIntermediateDirectories:YES
                                                        attributes:nil
                                                             error:&err]) {
            NSLog(@"ERROR:%s:%d Error creating logDirectory: %@",__func__,__LINE__, err);
        }
    }
    return _logDirectory;
}


- (void)logMessage:(NSString*)message {
    if (message == nil) {
        NSLog(@"ERROR:%s:%d Error message is nil",__func__,__LINE__);
        return;
    }
    NSData *logData = [message dataUsingEncoding:NSUTF8StringEncoding];
    dispatch_async(_loggerQueue, ^{
        @try {
            [[self currentLogFileHandle] writeData:logData];
            [self maybeRollLogFileDueToSize];
        }
        @catch (NSException *exception) {
            NSLog(@"ERROR:%s:%d writeing data to file failed.",__func__,__LINE__);
        }
    });
}

- (NSFileHandle *)currentLogFileHandle {
    if (_currentFileHandle == nil) {
        NSString *logFilePath = [[self currentLogFile] filePath];
        _currentFileHandle = [NSFileHandle fileHandleForWritingAtPath:logFilePath];
        [_currentFileHandle seekToEndOfFile];
        
        if (_currentFileHandle) {
            // Here we are monitoring the log file. In case if it would be deleted ormoved
            // somewhere we want to roll it and use a new one.
            _currentLogFileVnode = dispatch_source_create(
                                                          DISPATCH_SOURCE_TYPE_VNODE,
                                                          [_currentFileHandle fileDescriptor],
                                                          DISPATCH_VNODE_DELETE | DISPATCH_VNODE_RENAME,
                                                          _loggerQueue
                                                          );
            
            dispatch_source_set_event_handler(_currentLogFileVnode, ^{ @autoreleasepool {
                [self rollLogFileNow];
            } });
            
            dispatch_resume(_currentLogFileVnode);
        }
    }
    return _currentFileHandle;
}

-(WBDNSLogFile *) currentLogFile {
    if (_currentLogFile == nil) {
        NSArray *sortedLogFiles = [self sortedLogFiles];
        
        if ([sortedLogFiles count] > 0) {
            WBDNSLogFile* recentFile = [sortedLogFiles firstObject];
            if (recentFile.fileSize < WBDNS_DEFAULT_MAX_LOG_FILE_SIZE) {
                _currentLogFile = recentFile;
            }
        }
        
        if (_currentLogFile == nil) {
            NSString *currentLogFilePath = [self createNewLogFile];
            _currentLogFile = [[WBDNSLogFile alloc] initWithPath:currentLogFilePath];
        }
    }
    
    return _currentLogFile;
}

- (void)rollLogFileNow {
    if (_currentFileHandle == nil) {
        return;
    }
    
    [_currentFileHandle synchronizeFile];
    [_currentFileHandle closeFile];
    _currentFileHandle = nil;
    _currentLogFile = nil;
    
    if (_currentLogFileVnode) {
        dispatch_source_cancel(_currentLogFileVnode);
        _currentLogFileVnode = NULL;
    }
}



- (void)maybeRollLogFileDueToSize {
    // This method is called from logMessage.
    // Keep it FAST.
    // Note: Use direct access to maximumFileSize variable.
    // We specifically wrote our own getter/setter method to allow us to do this (for performance reasons).
    
    if (_maxFileNum > 0) {
        unsigned long long fileSize = [_currentFileHandle offsetInFile];
        
        if (fileSize >= _maxFileSize) {
            [self rollLogFileNow];
        }
    }
}



- (NSString *)newLogFileName {
    
    NSString *formattedDate = [_dateFormatter stringFromDate:[NSDate date]];
    
    return [NSString stringWithFormat:@"WBDNS %@.log", formattedDate];
}

/**
 * Generates a new unique log file path, and creates the corresponding log file.
 **/
- (NSString *)createNewLogFile {
    NSString *fileName = [self newLogFileName];
    NSString *logsDirectory = [self getLogDirectory];
    
    NSUInteger attempt = 1;
    
    do {
        NSString *actualFileName = fileName;
        
        if (attempt > 1) {
            NSString *extension = [actualFileName pathExtension];
            
            actualFileName = [actualFileName stringByDeletingPathExtension];
            actualFileName = [actualFileName stringByAppendingFormat:@" %lu", (unsigned long)attempt];
            
            if (extension.length) {
                actualFileName = [actualFileName stringByAppendingPathExtension:extension];
            }
        }
        
        NSString *filePath = [logsDirectory stringByAppendingPathComponent:actualFileName];
        
        if (![[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
            
            NSDictionary *attributes = nil;
            [[NSFileManager defaultManager] createFileAtPath:filePath contents:nil attributes:attributes];
            
            // Since we just created a new log file, we may need to delete some old log files
            [self deleteOldLogFiles];
            
            return filePath;
        } else {
            attempt++;
        }
    } while (YES);
}

- (void)deleteOldLogFiles {
    NSArray *sortedLogFileInfos = [self sortedLogFiles];
    NSUInteger firstIndexToDelete = NSNotFound;
    
    if (_maxFileNum) {
        if (firstIndexToDelete == NSNotFound) {
            firstIndexToDelete = _maxFileNum;
        } else {
            firstIndexToDelete = MIN(firstIndexToDelete, _maxFileNum);
        }
    }
    
    if (firstIndexToDelete != NSNotFound) {
        // removing all logfiles starting with firstIndexToDelete
        for (NSUInteger i = firstIndexToDelete; i < sortedLogFileInfos.count; i++) {
            WBDNSLogFile *logFileInfo = sortedLogFileInfos[i];
            [[NSFileManager defaultManager] removeItemAtPath:logFileInfo.filePath error:nil];
        }
    }
}

-(NSArray *)sortedLogFiles {
    NSArray* sortedLogFiles = [[self unsortedLogFiles]sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
        NSDate *date1 = [obj1 createDate];
        NSDate *date2 = [obj2 createDate];
        
        NSComparisonResult result = [date1 compare:date2];
        
        if (result == NSOrderedAscending) {
            return NSOrderedDescending;
        }
        
        if (result == NSOrderedDescending) {
            return NSOrderedAscending;
        }
        
        return NSOrderedSame;
        
    }];
    return sortedLogFiles;
}

- (void)uploadLogFiles {
    dispatch_async(_loggerQueue, ^{
        [self rollLogFileNow];
        NSArray* array = [self unsortedLogFiles];
        for (WBDNSLogFile* logFile in array) {
            [self uploadLogFile:logFile];
        }
    });
}

- (void)uploadLogFile:(WBDNSLogFile *)logFile {
    dispatch_async(_loggerQueue, ^{
        
        if (logFile == nil) {
            NSLog(@"ERROR:%s:%d input logFile is nil.",__func__, __LINE__);
            return;
        }
        
        if (logFile.filePath == nil) {
            NSLog(@"ERROR:%s:%d input logFile.filePath is nil.",__func__, __LINE__);
            return;
        }
        
        if (_runningUploadTask[logFile.filePath]) {
            return;
        }
        
        NSString * filePath = logFile.filePath;
        
        if (![[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
            NSLog(@"WARNING:%s:%d input logFile is not exist.",__func__, __LINE__);
            return;
        }
        
        [_runningUploadTask setObject:@"NonNull" forKey:filePath];
        if ([filePath isEqualToString:_currentLogFile.filePath]) {
            [self rollLogFileNow];
        }
        NSString* urlPrefix = [[WBDNSConfigManager sharedInstance] getLogServerUrl];
        
        NSString *identifierForVendor = [[UIDevice currentDevice].identifierForVendor UUIDString];
        NSString *secureCode = [WBDNSTools md5:[NSString stringWithFormat:@"%@%@",identifierForVendor,@"iheRFsFhLE9h9TRHVRLLBD6eS9ccQdLe"]];
        NSString* urlString = [NSString stringWithFormat:@"%@?c=httpdns&did=%@&s=%@",urlPrefix,identifierForVendor, [secureCode lowercaseString]];
        NSURL* url = [NSURL URLWithString:urlString];
        NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData timeoutInterval:60];
        
        [request setHTTPMethod:@"POST"];
        
//        NSString* boundary = @"1342578690";
//        NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; charset=utf-8;boundary=%@", boundary];
        NSString *contentType = @"application/x-www-form-urlencoded";
        [request setValue:contentType forHTTPHeaderField:@"Content-Type"];
        NSData* data = [self getUploadbody2:[NSData dataWithContentsOfFile:logFile.filePath]];
        
        NSURLSession* session = [NSURLSession sharedSession];
        session.sessionDescription = urlPrefix;
        NSURLSessionUploadTask* dataTask = [session uploadTaskWithRequest:request fromData:data completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
            dispatch_async(_loggerQueue, ^{
                NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response;
                if (httpResponse.statusCode == 200) {
                    //This function need to be executed in _loggerQueue, as it may conflict with other file operation.
                    [self removeFile:logFile];
                }
                else
                {
                    [[WBDNSConfigManager sharedInstance]setServerUrlFailedTimes:session.sessionDescription];
                    NSLog(@"ERROR:%s:%d: upload file failed. reason:%@", __func__,__LINE__, error.localizedDescription);
                }
                [_runningUploadTask removeObjectForKey:filePath];
            });
        }];
        [dataTask resume];
        [session finishTasksAndInvalidate];
    });
    
}

- (NSData *)getUploadBody:(NSString *)boundary fileData:(NSData *)fileData {
    NSMutableData *postData = [NSMutableData data];
    [postData appendData: [[NSString stringWithFormat:@"--%@\r\n", boundary]
                           dataUsingEncoding:NSUTF8StringEncoding]];//开始标志
    
    [postData appendData: [[NSString stringWithFormat: @"Content-Disposition: form-data; name=\"File1\";filename=\"httpdns.log\"\r\nContent-type: application/octet-stream\r\n\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];//name是页面文件的参数,type是文件类型
    [postData appendData:fileData];// 文件数据
    [postData appendData: [[NSString stringWithFormat:@"\r\n--%@--\r\n",  boundary]dataUsingEncoding:NSUTF8StringEncoding]];// 文件结束标志
    
    return postData;
}

- (NSData *) getUploadbody2:(NSData *)fileData {
    NSMutableData *postData = [NSMutableData data];
    [postData appendData: [@"log=[" dataUsingEncoding:NSUTF8StringEncoding]];//开始标志
    NSMutableString* fileString = [[NSMutableString alloc] initWithData:fileData encoding:NSUTF8StringEncoding];
    [fileString deleteCharactersInRange:NSMakeRange(fileString.length -1, 1)];
    [postData appendData:[fileString dataUsingEncoding:NSUTF8StringEncoding] ];// 文件数据
    [postData appendData: [@"]" dataUsingEncoding:NSUTF8StringEncoding]];// 文件结束标志
    
    return postData;
}



- (NSArray *)unsortedLogFiles {
    NSArray *unsortedLogFilePaths = [self unsortedLogFilePaths];
    
    NSMutableArray *unsortedLogFiles = [NSMutableArray arrayWithCapacity:[unsortedLogFilePaths count]];
    
    for (NSString *filePath in unsortedLogFilePaths) {
        WBDNSLogFile *logFileInfo = [[WBDNSLogFile alloc] initWithPath:filePath];
        if (logFileInfo) {
            [unsortedLogFiles addObject:logFileInfo];
        }
    }
    
    return unsortedLogFiles;
}


- (NSArray *)unsortedLogFilePaths {
    NSString* logsDirectory = [self getLogDirectory];
    NSArray *fileNames = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:logsDirectory error:nil];
    NSMutableArray *unsortedLogFilePaths = [NSMutableArray arrayWithCapacity:[fileNames count]];
    
    for (NSString* fileName in fileNames) {
        NSString *filePath = [logsDirectory stringByAppendingPathComponent:fileName];
        
        [unsortedLogFilePaths addObject:filePath];
    }
    return unsortedLogFilePaths;
}


- (void)removeFile:(WBDNSLogFile *)logFile {
    if (logFile == nil || logFile.filePath == nil) {
        NSLog(@"ERROR:%s:%d input logFile is nil.",__func__, __LINE__);
        return;
    }
    
    if ([logFile.filePath isEqualToString:_currentLogFile.filePath]) {
        [self rollLogFileNow];
    }
    NSError* error;
    [[NSFileManager defaultManager]removeItemAtPath:logFile.filePath error:&error];
    if (error) {
        NSLog(@"ERROR:%s:%d remove logFile failed, reason:%@.",__func__, __LINE__, error.localizedDescription);
    }
    
}

@end


================================================
FILE: src/DNSCache/Models/WBDNSConfig.h
================================================
//
//  WBDNSConfig.h
//  DNSCache
//
//  Created by Robert Yang on 15/9/1.
//  Copyright (c) 2015年 Weibo. All rights reserved.
//

#import <Foundation/Foundation.h>

#define WBDNSMinInterval 30

@interface WBDNSConfig : NSObject<NSCopying>
/**
 *  是否允许从HttpDns Server 或者cache 获取ip。 YES 允许, NO 将对所有请求返回nil。
 */
@property (atomic, assign) BOOL enableHttpDnsCache;
/**
 *  概率性记录某条日志的抽样率
 */
@property (atomic, assign) int logSamplingRate;

/**
 *  上传HTTP Cache Log的间隔周期。单位是秒,但是服务器下发的是毫秒。
 */
@property (atomic, assign) int uploadLogInterval;
/**
 *  对缓存域名的ip测速的间隔周期。单位是秒,但是服务器下发的是毫秒。
 */
@property (atomic, assign) int speedTestInterval;

/**
 *  刷新缓存内域名对应ip的间隔周期。单位是秒,但是服务器下发的是毫秒。
 */
@property (atomic, assign) int refreshDomainIpInterval;

/**
 *  SDK可以从多种途径获取域名对应Ip,这个变量控制是否从Sina HTTP DNS server获取ip. YES 代表允许,NO代表不允许。
 */
@property (atomic, assign) BOOL enableRequestFromSinaHttpDnsServer;

/**
 *  允许SDK根据本地获取信息 修改server返回的Domain对应IP的优先级。
 */
@property (atomic, assign) BOOL enableSDKUpdateServerIpOrder;

/**
 *  SDK计算IP列表中一个ip的优先级时,测速所占权重。
 */
@property (atomic, assign) int  speedTestFactorWeight;

/**
 *  SDK计算IP列表中一个ip的优先级时,Server返回优先级所占权重。
 */
@property (atomic, assign) int  serverSuggestionFactorWeight;

/**
 *  目前服务器支持的domain列表。里面存储NSString 对象,代表一个支持的domain。
 */
@property (atomic, strong) NSArray *supportedDomainList;

/**
 *  Sina http dns server的地址。会返回多个,默认使用第一个,其它作为备用。用NSString对象表示。
 */
@property (atomic, strong) NSArray *httpDnsServerUrlList;

@end


================================================
FILE: src/DNSCache/Models/WBDNSConfig.m
================================================
//
//  WBDNSConfig.m
//  DNSCache
//
//  Created by Robert Yang on 15/9/1.
//  Copyright (c) 2015年 Weibo. All rights reserved.
//

#import "WBDNSConfig.h"
#import "WBDNSTools.h"
@implementation WBDNSConfig

-(instancetype)init {
    if (self = [super init]) {
        self.enableHttpDnsCache = YES;
        self.logSamplingRate = 50;
        self.uploadLogInterval = 3600;
        self.speedTestInterval = 60;
        self.refreshDomainIpInterval = 60;
        self.enableRequestFromSinaHttpDnsServer = YES;
        self.enableSDKUpdateServerIpOrder = YES;
        self.speedTestFactorWeight = 50;
        self.serverSuggestionFactorWeight = 50;
        self.supportedDomainList = @[
                                     @"ww1.sinaimg.cn",
                                     @"ww2.sinaimg.cn",
                                     @"ww3.sinaimg.cn",
                                     @"ww4.sinaimg.cn",
                                     @"api.weibo.cn"
                                     ];
        self.httpDnsServerUrlList = @[
                                      @"http://dns.weibo.cn",
                                      @"http://202.108.7.232",
                                      @"http://221.179.190.246",
                                      @"http://58.63.236.228"
                                      ];
    }
    return self;
}

- (instancetype)copyWithZone:(NSZone *)zone {
    
    WBDNSConfig *newConfig = [[[self class] allocWithZone:zone] init];
    newConfig.enableHttpDnsCache = self.enableHttpDnsCache;
    newConfig.logSamplingRate = self.logSamplingRate;
    newConfig.uploadLogInterval = self.uploadLogInterval;
    newConfig.speedTestInterval = self.speedTestInterval;
    newConfig.refreshDomainIpInterval = self.refreshDomainIpInterval;
    newConfig.enableRequestFromSinaHttpDnsServer = self.enableRequestFromSinaHttpDnsServer;
    newConfig.enableSDKUpdateServerIpOrder = self.enableSDKUpdateServerIpOrder;
    newConfig.speedTestFactorWeight = self.speedTestFactorWeight;
    newConfig.serverSuggestionFactorWeight = self.serverSuggestionFactorWeight;
    newConfig.supportedDomainList = [NSMutableArray arrayWithArray:self.supportedDomainList];
    newConfig.httpDnsServerUrlList = [NSMutableArray arrayWithArray:self.httpDnsServerUrlList];
    return newConfig;
}

-(BOOL)isEqual:(id)object {
    if (![object isKindOfClass:[WBDNSConfig class]]) {
        return NO;
    }
    WBDNSConfig *other = (WBDNSConfig *)object;
    if (self.enableHttpDnsCache != other.enableHttpDnsCache ) {
        return NO;
    }
    
    if (self.enableSDKUpdateServerIpOrder != other.enableSDKUpdateServerIpOrder) {
        return NO;
    }
    
    if (self.enableRequestFromSinaHttpDnsServer != other.enableRequestFromSinaHttpDnsServer) {
        return NO;
    }
    
    if (self.logSamplingRate != other.logSamplingRate) {
        return NO;
    }
    
    if (self.uploadLogInterval != other.uploadLogInterval) {
        return NO;
    }
    
    if (self.speedTestInterval != other.speedTestInterval) {
        return NO;
    }
    
    if (self.refreshDomainIpInterval != other.refreshDomainIpInterval) {
        return NO;
    }
    
    if (self.speedTestFactorWeight != other.speedTestFactorWeight) {
        return NO;
    }
    
    if (self.serverSuggestionFactorWeight != other.serverSuggestionFactorWeight) {
        return NO;
    }
    
    if (![WBDNSTools isStringArray:self.supportedDomainList equalToStringArray2:other.supportedDomainList]) {
        return NO;
    }
    
    if (![WBDNSTools isStringArray:self.httpDnsServerUrlList equalToStringArray2:other.httpDnsServerUrlList]){
        return NO;
    }
    
    return YES;
}

@end


================================================
FILE: src/DNSCache/Models/WBDNSDomainModel.h
================================================
//
//  DomainModel.h
//  DNSCache
//
//  Created by Robert Yang on 15/7/28.
//  Copyright (c) 2015年 Weibo. All rights reserved.
//

#import <Foundation/Foundation.h>

#define WBDNS_LOCAL_DNS_ID -1

/**
 *
 * 项目名称: DNSCache <br>
 * 类名称: DomainModel <br>
 * 类描述: 域名数据模型 - 对应domain表 <br>
 * 创建人: Robert Yang <br>
 * 创建时间: 2015-7-28 下午5:04:01 <br>
 *
 * 修改人:  <br>
 * 修改时间:  <br>
 * 修改备注: <br>
 *
 * @version V1.0
 */
@interface WBDNSDomainModel : NSObject<NSCopying>
/**
 * 自增id <br>
 *
 * 该字段映射类 {@link com.sina.util.dnscache.cache.DBConstants } DOMAIN_COLUMN_ID 字段 <br>
 */
@property (nonatomic, assign) int id;

/**
 * 域名 <br>
 *
 * 该字段映射类 {@link com.sina.util.dnscache.cache.DBConstants } DOMAIN_COLUMN_DOMAIN 字段 <br>
 */
@property (nonatomic, strong) NSString *domain;

/**
 * 运营商 <br>
 *
 * 该字段映射类 {@link com.sina.util.dnscache.cache.DBConstants } DOMAIN_COLUMN_SP 字段 <br>
 */
@property (nonatomic, strong) NSString *sp;

/**
 * 域名过期时间 <br>
 *
 * 该字段映射类 {@link com.sina.util.dnscache.cache.DBConstants } DOMAIN_COLUMN_TTL 字段 <br>
 */
@property (nonatomic, strong) NSString *ttl;

/**
 * 域名最后查询时间 <br>
 *
 * 该字段映射类 {@link com.sina.util.dnscache.cache.DBConstants } DOMAIN_COLUMN_TIME 字段 <br>
 */
@property (nonatomic, strong) NSString *time;

/**
 * 域名关联的ip数组 <br>
 */
@property (nonatomic, strong) NSMutableArray *ipModelArray;

- (NSArray *)serverIpArray;

- (NSString *)toJson;

- (NSDictionary *)toDictionary;

@end


================================================
FILE: src/DNSCache/Models/WBDNSDomainModel.m
================================================
//
//  DomainModel.m
//  DNSCache
//
//  Created by Robert Yang on 15/7/28.
//  Copyright (c) 2015年 Weibo. All rights reserved.
//

#import "WBDNSDomainModel.h"
#import "WBDNSIpModel.h"
@implementation WBDNSDomainModel

- (instancetype)init {
    if (self = [super init]) {
        _id = -2;
        _domain = @"";
        _sp = @"";
        _ttl = @"";
        _time = @"";
        self.ipModelArray = [NSMutableArray array];
    }
    return self;
}

- (void)dealloc {
    self.ipModelArray = nil;
}

- (NSArray *)serverIpArray {
    NSMutableArray* serverIpArray = [NSMutableArray array];
    for (WBDNSIpModel* ipModel in self.ipModelArray) {
        [serverIpArray addObject:ipModel.ip];
    }
    return serverIpArray;
}

- (instancetype)copyWithZone:(NSZone *)zone {
    WBDNSDomainModel* newModel = [[WBDNSDomainModel allocWithZone:zone]init];
    newModel.id = self.id;
    newModel.domain = self.domain;
    newModel.sp = self.sp;
    newModel.ttl = self.ttl;
    newModel.time = self.time;
    
    for (WBDNSIpModel* ipModel in self.ipModelArray) {
        [newModel.ipModelArray addObject:[ipModel copy]];
    }
    return newModel;
}

- (NSString *) description {
    NSMutableString* string = [NSMutableString stringWithFormat:@"域名ID = %i, 域名 = %@, 运营商ID = %@, 域名过期时间 = %@, 域名最后查询时间 = %@,\n", _id, _domain, _sp, _ttl, _time];
    for (WBDNSIpModel* ipModel in self.ipModelArray) {
        [string appendString:@"--------Ip 列表---------\n"];
        [string appendString:[ipModel description]];
    }
    return string;
}

-(NSString*) toJson {
    NSMutableArray* ipModelJasonArray = [NSMutableArray array];
    for(int i = 0; i< self.ipModelArray.count; i++) {
        [ipModelJasonArray addObject:[self.ipModelArray[i] toDictionary]];
    }
        
    NSDictionary* dic = @{@"id":@(_id),
                          @"domain":_domain,
                          @"sp":_sp,
                          @"ttl":_ttl,
                          @"sp":_sp,
                          @"time":_time,
                          @"ipModelArr":ipModelJasonArray,
                          };
    NSData * jsonData = [NSJSONSerialization dataWithJSONObject:dic options:0 error:nil];
    NSString * myString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
    return myString;
}

- (NSDictionary *)toDictionary {
    NSMutableArray* ipModelJasonArray = [NSMutableArray array];
    for(int i = 0; i< self.ipModelArray.count; i++) {
        [ipModelJasonArray addObject:[self.ipModelArray[i] toDictionary]];
    }
    
    NSDictionary* dic = @{@"id":@(_id),
                          @"domain":_domain,
                          @"sp":_sp,
                          @"ttl":_ttl,
                          @"sp":_sp,
                          @"time":_time,
                          @"ipModelArr":ipModelJasonArray,
                          };
    return dic;
}

@end


================================================
FILE: src/DNSCache/Models/WBDNSHttpDnsPack.h
================================================
//
//  HttpDnsPack.h
//  DNSCache
//
//  Created by Robert Yang on 15/7/28.
//  Copyright (c) 2015年 Weibo. All rights reserved.
//

#import <Foundation/Foundation.h>

/**
 *
 * 项目名称: DNSCache
 * 类名称: HttpDnsPack
 * 类描述: 将httpdns返回的数据封装一层,方便日后httpdns接口改动不影响数据库模型。 并且该接口还会标识httpdns错误之后的一些信息用来上报
 * 创建人: Robert Yang
 * 创建时间: 2015-7-30 上午11:20:11
 *
 * 修改人:
 * 修改时间:
 * 修改备注:
 *
 * @version V1.0
 */
@interface WBDNSHttpDnsPack : NSObject
/**
 * httpdns 接口返回字段 域名信息
 */
@property (nonatomic, strong) NSString *domain;

/**
 * httpdns 接口返回字段 请求的设备ip(也可能是sp的出口ip)
 */
@property (nonatomic, strong) NSString *device_ip;

/**
 * httpdns 接口返回字段 请求的设备sp运营商
 */
@property (nonatomic, strong) NSString *device_sp;

/**
 * httpdns 接口返回的a记录。(目前不包含cname别名信息)
 */
@property (nonatomic, strong) NSMutableArray *dns;

/**
 * 本机识别的sp运营商,手机卡下运营商正常,wifi下为ssid名字`
 */
@property (nonatomic, strong) NSString *localhostSp;

+ (WBDNSHttpDnsPack *)generateInstanceFromDic:(NSDictionary *)dic;

- (NSDictionary *)toDictionary;

@end


================================================
FILE: src/DNSCache/Models/WBDNSHttpDnsPack.m
================================================
//
//  HttpDnsPack.m
//  DNSCache
//
//  Created by Robert Yang on 15/7/28.
//  Copyright (c) 2015年 Weibo. All rights reserved.
//

#import "WBDNSHttpDnsPack.h"
#import "WBDNSModel.h"
#import "WBDNSCache.h"
@implementation WBDNSHttpDnsPack

- (instancetype)init {
    if (self = [super init]) {
    }
    return self;
}

+ (WBDNSHttpDnsPack *)generateInstanceFromDic:(NSDictionary *)dic {
    if (dic == nil) {
        return nil;
    }
    WBDNSHttpDnsPack* dnsPack = [[WBDNSHttpDnsPack alloc]init];
    dnsPack.domain = dic[@"domain"];
    dnsPack.device_ip = dic[@"device_ip"];
    dnsPack.device_sp = dic[@"device_sp"];
    NSArray* dns = dic[@"dns"];
    if (dns && [dns isKindOfClass:[NSArray class]]) {
        dnsPack.dns = [NSMutableArray array];
    }
    for (NSDictionary* tempIP in dns) {
        WBDNSIP* ip = [[WBDNSIP alloc]init];
        ip.ip = tempIP[@"ip"];
        ip.ttl = tempIP[@"ttl"];
        ip.priority = tempIP[@"priority"];
        [dnsPack.dns addObject:ip];
    }
    return dnsPack;
}

- (NSString *)description {
    NSMutableString* string = [NSMutableString stringWithFormat:@"域名 = %@, 最终请求IP = %@, 服务器识别运营商 = %@, 本地识别运营商或SSID = %@", _domain, _device_ip, _device_sp, _localhostSp];
    return string;
}

- (NSDictionary *)toDictionary {
    NSMutableArray* dnsIpJasonArray = [NSMutableArray array];
    for(int i = 0; i< self.dns.count; i++)
    {
        [dnsIpJasonArray addObject:[self.dns[i] toDictionary]];
    }
    
    NSDictionary* dic = @{@"domain":_domain,
                          @"device_ip":_device_ip,
                          @"device_sp":_device_sp,
                          @"dns":dnsIpJasonArray,
                          };
    return dic;
}

@end


================================================
FILE: src/DNSCache/Models/WBDNSIP.h
================================================
//
//  IP.h
//  DNSCache
//
//  Created by Robert Yang on 15/7/28.
//  Copyright (c) 2015年 Weibo. All rights reserved.
//

#import <Foundation/Foundation.h>

/**
 * A记录相关字段信息
 */
@interface WBDNSIP : NSObject

/**
 * A记录IP
 */
@property (nonatomic, strong) NSString *ip;

/**
 * 域名A记录过期时间
 */
@property (nonatomic, strong) NSString *ttl;

/**
 * 服务器推荐使用的A记录 级别从0-10
 */
@property (nonatomic, strong) NSString *priority;

@end


================================================
FILE: src/DNSCache/Models/WBDNSIP.m
================================================
//
//  IP.m
//  DNSCache
//
//  Created by Robert Yang on 15/7/28.
//  Copyright (c) 2015年 Weibo. All rights reserved.
//

#import "WBDNSIP.h"

@implementation WBDNSIP

- (instancetype) init {
    if (self = [super init]) {
    }
    return self;
}

- (NSString *)description {
    NSMutableString *string = [NSMutableString stringWithFormat:@"服务器ip = %@, 过期时间 = %@, 优先级 = %@", _ip, _ttl, _priority];
    return string;
}

- (NSDictionary *)toDictionary {
    NSDictionary* dic = @{@"ip":_ip,
                          @"ttl":_ttl,
                          @"priority":_priority,
                          };
    return dic;
}

@end


================================================
FILE: src/DNSCache/Models/WBDNSIpModel.h
================================================
//
//  IpModel.h
//  DNSCache
//
//  Created by Robert Yang on 15/7/28.
//  Copyright (c) 2015年 Weibo. All rights reserved.
//

#import <Foundation/Foundation.h>

/**
 *
 * 项目名称: DNSCache <br>
 * 类名称: IpModel <br>
 * 类描述: ip数据模型 - 对应ip表 <br>
 * 创建人: Robert Yang <br>
 * 创建时间: 2015-7-26 下午5:23:06 <br>
 *
 * 修改人:  <br>
 * 修改时间:  <br>
 * 修改备注:  <br>
 *
 * @version V1.0
 */
@interface WBDNSIpModel : NSObject <NSCopying>
/**
 * 自增id <br>
 *
 * 该字段映射 WBDNS_IP_COLUMN_ID 字段 <br>
 */
@property (nonatomic, assign) int id;

/**
 * domain id 关联id
 *
 * 该字段映射 WBDNS_IP_COLUMN_DOMAIN_ID 字段 <br>
 */
@property (nonatomic, assign) int d_id;

/**
 * 服务器ip地址
 *
 * 该字段映射 WBDNS_IP_COLUMN_PORT 字段 <br>
 */
@property (nonatomic, strong) NSString *ip;

/**
 * ip服务器对应的端口
 *
 * 该字段映射 WBDNS_IP_COLUMN_PORT 字段 <br>
 */
@property (nonatomic, assign) int port;

/**
 * ip服务器对应的sp运营商
 *
 * 该字段映射 WBDNS_IP_COLUMN_SP 字段 <br>
 */
@property (nonatomic, strong) NSString *sp;

/**
 * ip过期时间
 *
 * 该字段映射 WBDNS_IP_COLUMN_TTL 字段 <br>
 */
@property (nonatomic, strong) NSString *ttl;

/**
 * ip服务器优先级-排序算法策略使用
 *
 * 该字段映射 WBDNS_IP_COLUMN_PRIORITY 字段 <br>
 */
@property (nonatomic, strong) NSString *priority;

/**
 *  访问ip服务器的往返时延
 *
 * 该字段映射类 WBDNS_IP_COLUMN_RTT 字段
 */
@property (nonatomic, strong) NSString *rtt;

/**
 * ip服务器链接产生的成功数
 *
 * 该字段映射 WBDNS_IP_COLUMN_SUCCESS_NUM 字段 <br>
 */
@property (nonatomic, strong) NSString *success_num;

/**
 * ip服务器链接产生的错误数
 *
 * 该字段映射 WBDNS_IP_COLUMN_ERR_NUM 字段 <br>
 */
@property (nonatomic, strong) NSString *err_num;

/**
 * ip服务器最后成功链接时间
 *
 * 该字段映射 WBDNS_IP_COLUMN_FINALLY_SUCCESS_TIME 字段 <br>
 */
@property (nonatomic, strong) NSString *finally_success_time;


/**
 * ip服务器最后失败链接时间
 *
 * 该字段映射WBDNS_IP_COLUMN_FINALLY_FAIL_TIME 字段 <br>
 */
@property (nonatomic, strong) NSString *finally_fail_time;

/**
 *  此IP记录从服务器的更新时间
 *  该字段映射WBDNS_IP_COLUMN_FINALLY_UPDATE_TIME 字段 <br>
 */
@property (nonatomic, strong) NSString* finally_update_time;

/**
 * 评估体系 评分分值
 */
@property (nonatomic, assign) NSInteger grade;

/**
 *  转成用json表示的对象。
 *
 *  @return json字符串
 */
- (NSString*)toJson;


/**
 *  转成用词典表示的对象
 *
 *  @return 用词典表示的对象
 */
- (NSDictionary*)toDictionary;

@end


================================================
FILE: src/DNSCache/Models/WBDNSIpModel.m
================================================
//
//  IpModel.m
//  DNSCache
//
//  Created by Robert Yang on 15/7/28.
//  Copyright (c) 2015年 Weibo. All rights reserved.
//

#import "WBDNSIpModel.h"

@implementation WBDNSIpModel

- (instancetype) init {
    if (self = [super init]) {
        _id = -2;
        _d_id = -2;
        _ip = @"";
        _port = 80;
        _sp = @"";
        _ttl = @"";
        _priority = @"";
        _rtt = @"";
        _success_num = 0;
        _err_num = 0;
        _finally_success_time = @"";
        _finally_fail_time = @"";
        _finally_update_time = @"";
        _grade = 0;
        
    }
    return self;
}

- (instancetype)copyWithZone:(NSZone *)zone {
    WBDNSIpModel* newModel = [[WBDNSIpModel allocWithZone:zone]init];
    newModel.id = self.id;
    newModel.d_id = self.d_id;
    newModel.ip = self.ip;
    newModel.port = self.port;
    newModel.sp = self.sp;
    newModel.ttl = self.ttl;
    newModel.priority = self.priority;
    newModel.rtt = self.rtt;
    newModel.success_num = self.success_num;
    newModel.err_num = self.err_num;
    newModel.finally_success_time = self.finally_success_time;
    newModel.finally_fail_time = self.finally_fail_time;
    newModel.finally_update_time = self.finally_update_time;
    newModel.grade = self.grade;
    return newModel;
}

- (NSString *)description {
    NSMutableString* string = [NSMutableString stringWithFormat:@"服务器id = %ld\n域名ID索引 = %i\n服务器ip = %@\n服务器端口 = %i\n运营商 = %@\n过期时间 = %@\n优先级 = %@\n往返时延 = %@\n历史成功次数 = %@\n历史失败次数 = %@\n最后一次访问成功时间 = %@\n最后一次访问失败时间 = %@\n最后一次更新时间 = %@\n服务器评分 = %ld\n", (long)_id, _d_id, _ip, _port, _sp, _ttl, _priority,_rtt, _success_num, _err_num, _finally_success_time, _finally_fail_time, _finally_update_time, (long)_grade];
    return string;
}


- (NSString*)toJson {
    NSDictionary* dic = @{@"id":@(_id),
                          @"d_id":@(_d_id),
                          @"ip":_ip,
                          @"port":@(_port),
                          @"sp":_sp,
                          @"ttl":_ttl,
                          @"priority":_priority,
                          @"success_num":_success_num,
                          @"err_num":_err_num,
                          @"finally_success_time":_finally_success_time,
                          @"finally_fail_time":_finally_fail_time
                          };
    NSData * jsonData = [NSJSONSerialization dataWithJSONObject:dic options:0 error:nil];
    NSString * myString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
    return myString;
}

- (NSDictionary*)toDictionary {
    NSDictionary *dic = @{@"id":@(_id),
                          @"d_id":@(_d_id),
                          @"ip":_ip,
                          @"port":@(_port),
                          @"sp":_sp,
                          @"ttl":_ttl,
                          @"priority":_priority,
                          @"success_num":_success_num,
                          @"err_num":_err_num,
                          @"finally_success_time":_finally_success_time,
                          @"finally_fail_time":_finally_fail_time
                          };
    return dic;
}

@end


================================================
FILE: src/DNSCache/Models/WBDNSMemeryCache.h
================================================
//
//  WBDNSMemeryCache.h
//  DNSCache
//
//  Created by Robert Yang on 15/8/10.
//  Copyright (c) 2015年 Weibo. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "WBDNSDomainModel.h"
@interface WBDNSMemeryCache : NSMutableDictionary

+ (instancetype)sharedInstance;
- (WBDNSDomainModel *)getModelByKeyUrl:(NSString*)keyUrl;
- (void)addModel:(WBDNSDomainModel *)model keyUrl:(NSString*)keyUrl;
- (void)updateModel:(WBDNSDomainModel *)model keyUrl:(NSString*)keyUrl;
- (NSArray *)getExpireDnsCache;
- (void)removeAllModels;
- (NSArray *)getAllModels;

@end


================================================
FILE: src/DNSCache/Models/WBDNSMemeryCache.m
================================================
//
//  WBDNSMemeryCache.m
//  DNSCache
//
//  Created by Robert Yang on 15/8/10.
//  Copyright (c) 2015年 Weibo. All rights reserved.
//

#import "WBDNSMemeryCache.h"
#import "WBDNSQueryManager.h"
#import "WBDNSConfigManager.h"
#import "WBDNSTools.h"

@implementation WBDNSMemeryCache
{
    NSMutableDictionary* _dic;
}

+ (instancetype)sharedInstance {
    static WBDNSMemeryCache* sharedInstance;
    static dispatch_once_t predicate;
    dispatch_once(&predicate, ^{
        sharedInstance = [[WBDNSMemeryCache alloc]init];
    });
    return sharedInstance;
}

- (instancetype)init {
    if (self = [super init]) {
        _dic = [[NSMutableDictionary alloc]init];
    }
    return self;
}


- (WBDNSDomainModel *)getModelByKeyUrl:(NSString *)keyUrl {
    @synchronized(self) {
        return _dic[keyUrl];
    }
}

- (void)removeAllModels {
    @synchronized(self) {
        [_dic removeAllObjects];
    }
}

- (NSArray *)getAllModels {
    @synchronized(self) {
        return [_dic allValues];
    }
}

- (void)addModel:(WBDNSDomainModel *)model keyUrl:(NSString *)keyUrl {
    @synchronized(self) {
        WBDNSDomainModel* existModel = _dic[keyUrl];
        if (existModel) {
            [_dic removeObjectForKey:keyUrl];
            [_dic setObject:model forKey:keyUrl];
        }
        else {
            [_dic setObject:model forKey:keyUrl];
        }
    }
}


//本函数只用于更新从本地dns获取的ip, 从httpserver 获取的ip 不应该用此函数更新。
- (void)updateModel:(WBDNSDomainModel *)model keyUrl:(NSString *)keyUrl
{
    @synchronized(self) {
        WBDNSDomainModel* existModel = _dic[keyUrl];
        if (existModel) {
            existModel.id = model.id;
            existModel.domain = model.domain;
            existModel.sp = model.sp;
            existModel.ttl = model.ttl;
            existModel.time = model.time;
            
            NSMutableArray *newIpArray= [NSMutableArray array];
            
            //更新在新老Model里的ip,不更新测速数据;。删除存在着老model里的,但是不在新Model里的ip。
            for (WBDNSIpModel *ipModel in existModel.ipModelArray) {
                WBDNSIpModel *newIpModel = [self findIp:ipModel.ip inIPArray:model.ipModelArray];
                if (newIpModel) {
                    ipModel.id = newIpModel.id;
                    ipModel.d_id = newIpModel.d_id;
                    ipModel.ttl = newIpModel.ttl;
                    ipModel.sp = newIpModel.sp;
                    ipModel.finally_update_time = newIpModel.finally_update_time;
                    [newIpArray addObject:ipModel];
                }
            }
            
            //把新Model里存在 但不再老model里的ip 加进来。
            for (WBDNSIpModel *ipModel in model.ipModelArray) {
                WBDNSIpModel *newIpModel = [self findIp:ipModel.ip inIPArray:newIpArray];
                if (newIpModel == nil) {
                    [newIpArray addObject:ipModel];
                }
            }
            
            existModel.ipModelArray = newIpArray;
        }
        else {
            [_dic setObject:model forKey:keyUrl];
        }
    }
}

- (WBDNSIpModel *)findIp:(NSString *)ip inIPArray:(NSArray *)ipArray
{
    for (WBDNSIpModel* ipModel in ipArray) {
        if ([ipModel.ip isEqualToString:ip]) {
            return ipModel;
        }
    }
    return nil;
}

- (NSArray *)getExpireDnsCache
{
    @synchronized(self) {
        NSMutableArray *domainList = [NSMutableArray array];
        for (WBDNSDomainModel *tempModel in [_dic allValues] ) {
            if ([WBDNSTools isDomainModelExpired:tempModel expireDuration:0]) {
                [domainList addObject:tempModel];
            }
        }
        return domainList;
    }
}

@end


================================================
FILE: src/DNSCache/Models/WBDNSModel.h
================================================
//
//  WBDNSModel.h
//  DNSCache
//
//  Created by Robert Yang on 15/8/3.
//  Copyright (c) 2015年 Weibo. All rights reserved.
//

#ifndef DNSCache_Model_h
#define DNSCache_Model_h
#import "WBDNSDomainModel.h"
#import "WBDNSIpModel.h"
#import "WBDNSHttpDnsPack.h"
#import "WBDNSIP.h"
#endif


================================================
FILE: src/DNSCache/QueryManager/WBDNSQueryManager.h
================================================
//
//  QueryManager.h
//  DNSCache
//
//  Created by Robert Yang on 15/8/4.
//  Copyright (c) 2015年 Weibo. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "WBDNSModel.h"
#import "WBDNSCacheManager.h"

@protocol WBDNSQueryManagerProtocol <NSObject>

- (WBDNSDomainModel *)queryDomainIp:(NSString *)sp host:(NSString *)host;

@end

@interface WBDNSQueryManager : NSObject <WBDNSQueryManagerProtocol>

- (instancetype)initWithDnsCacheManager:(id<WBDNSCacheProtocol>)DnsCacheManager;

@end


================================================
FILE: src/DNSCache/QueryManager/WBDNSQueryManager.m
================================================
//
//  QueryManager.m
//  DNSCache
//
//  Created by Robert Yang on 15/8/4.
//  Copyright (c) 2015年 Weibo. All rights reserved.
//

#import "WBDNSQueryManager.h"
#import "WBDNSConfig.h"
#import "WBDNSConfigManager.h"
#import "WBDNSSortManager.h"
#import <netdb.h>
#import <sys/socket.h>

@implementation WBDNSQueryManager
{
    id<WBDNSCacheProtocol> _manager;
}

- (instancetype)initWithDnsCacheManager:(id<WBDNSCacheProtocol>)dnsCacheManager
{
    if (self = [super init]) {
        _manager = dnsCacheManager;
    }
    return  self;
}

- (WBDNSDomainModel *)queryDomainIp:(NSString *)sp host:(NSString *)host
{
    WBDNSDomainModel* domainModel = nil;
    
    if ([[WBDNSConfigManager sharedInstance] isSupportedDomain:host] && [WBDNSConfigManager sharedInstance].config.enableRequestFromSinaHttpDnsServer) {
        domainModel = [[_manager getDnsCache:sp url:host] copy];
        if ([WBDNSConfigManager sharedInstance].config.enableSDKUpdateServerIpOrder) {
            domainModel = [WBDNSSortManager sortIpArrayOfModel:domainModel];
        }
    }
    
    if (domainModel == nil || domainModel.ipModelArray == nil || domainModel.ipModelArray.count == 0) {
        struct addrinfo hints;
        memset(&hints, 0, sizeof(hints));
        hints.ai_family = PF_INET;        // PF_INET if you want only IPv4 addresses
        hints.ai_protocol = IPPROTO_TCP;
        
        struct addrinfo *addrs, *addr;
        
        getaddrinfo([host UTF8String], NULL, &hints, &addrs);
        NSMutableArray* ipStringArray = [NSMutableArray array];
        for (addr = addrs; addr; addr = addr->ai_next) {
            char host[NI_MAXHOST];
            getnameinfo(addr->ai_addr, addr->ai_addrlen, host, sizeof(host), NULL, 0, NI_NUMERICHOST);
            [ipStringArray addObject:[NSString stringWithUTF8String:host]];
        }
        freeaddrinfo(addrs);

        NSDateFormatter* formatter = [[NSDateFormatter alloc]init];
        [formatter setDateFormat:[WBDNSTools dateFormatter]];
        
        domainModel = [[WBDNSDomainModel alloc]init];
        domainModel.id = WBDNS_LOCAL_DNS_ID;
        domainModel.domain = host;
        domainModel.sp = sp;
        domainModel.ttl = @"60";
        domainModel.time = [formatter stringFromDate:[NSDate date]];
        
        for (NSString* ip in ipStringArray) {
            WBDNSIpModel* model = [[WBDNSIpModel alloc]init];
            model.id = WBDNS_LOCAL_DNS_ID;
            model.d_id = WBDNS_LOCAL_DNS_ID;
            model.ip = ip;
            model.port = 80;
            model.sp = sp;
            model.ttl = @"60";
            model.priority = @"";
            model.rtt = @"0";
            model.success_num = @"0";
            model.err_num = @"0";
            model.finally_fail_time = [[WBDNSTools sharedInstance]stringFromDate:[NSDate dateWithTimeIntervalSince1970:0]];
            model.finally_success_time = [[WBDNSTools sharedInstance]stringFromDate:[NSDate dateWithTimeIntervalSince1970:0]];
            model.finally_update_time = [formatter stringFromDate:[NSDate date]];
            
            [domainModel.ipModelArray addObject:model];
        }
        
        if ([[WBDNSConfigManager sharedInstance] isSupportedDomain:host] && [WBDNSConfigManager sharedInstance].config.enableRequestFromSinaHttpDnsServer) {
            [_manager updateMemoryCache:host model:domainModel];
        }
    }
    return domainModel;
}

@end


================================================
FILE: src/DNSCache/SortManager/WBDNSSortManager.h
================================================
//
//  WBDNSSortManager.h
//  DNSCache
//
//  Created by Robert Yang on 15/8/24.
//  Copyright (c) 2015年 Weibo. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "WBDNSModel.h"

@interface WBDNSSortManager : NSObject

+ (WBDNSDomainModel *)sortIpArrayOfModel:(WBDNSDomainModel*)model;

@end


================================================
FILE: src/DNSCache/SortManager/WBDNSSortManager.m
================================================
//
//  WBDNSSortManager.m
//  DNSCache
//
//  Created by Robert Yang on 15/8/24.
//  Copyright (c) 2015年 Weibo. All rights reserved.
//

#import "WBDNSSortManager.h"
#import "WBDNSTools.h"
#import "WBDNSConfigManager.h"
#import "WBDNSConfig.h"

@implementation WBDNSSortManager

+ (WBDNSDomainModel *)sortIpArrayOfModel:(WBDNSDomainModel *)model {
    if (model == nil || model.ipModelArray == nil || model.ipModelArray.count == 0) {
        return model;
    }
    
    NSMutableArray* sortedIpArray = [NSMutableArray array];
    NSMutableArray* speedTestedIpArray = [NSMutableArray array];
    NSMutableArray* speedNotTestedIpArray = [NSMutableArray array];
    for (WBDNSIpModel* ip in model.ipModelArray) {
        if (ip.finally_success_time && ip.finally_fail_time) {
            NSDate* successTime = [[WBDNSTools sharedInstance] dateFromString:ip.finally_success_time];
            NSDate* failedTime = [[WBDNSTools sharedInstance] dateFromString:ip.finally_fail_time];
            
            //时间异常,认定测速数据不合法,分到未测速组。
            if (successTime == nil && failedTime == nil) {
                NSLog(@"ERROR:%s:%d SuccessTime(%@) or FailedTime (%@) is invalid.", __func__,__LINE__,ip.finally_success_time,ip.finally_fail_time);
                [speedNotTestedIpArray addObject:ip];
                continue;
            }
            
            int speedTestExpireTime = 2* [WBDNSConfigManager sharedInstance].config.speedTestInterval;
            
            if ((![WBDNSTools isTestTimeExpired:successTime expiredTime:speedTestExpireTime])&&(![WBDNSTools isTestTimeExpired:failedTime expiredTime:speedTestExpireTime])) {
                //成功时间 晚于失败时间,说明测速成功。
                if ([successTime compare:failedTime] != NSOrderedDescending) {
                    [speedTestedIpArray addObject:ip];
                    continue;
                }
                //测速失败,本条记录从ip列表中去除,不返回给用户。
                else {
                    continue;
                }
            }
            else if(![WBDNSTools isTestTimeExpired:successTime expiredTime:speedTestExpireTime]) {
                //测速成功,加入到测速成功分组
                [speedTestedIpArray addObject:ip];
                continue;
            }
            else if(![WBDNSTools isTestTimeExpired:failedTime expiredTime:speedTestExpireTime]) {
                //测速失败,本条记录从ip列表中去除,不返回给用户。
                continue;
            }
            else {
                //测速数据过期,分到未测速分组
                [speedNotTestedIpArray addObject:ip];
                continue;
            }
        }
        //时间异常,认定测速数据不合法,分到未测速组。
        else {
            NSLog(@"ERROR:%s:%d SuccessTime(%@) or FailedTime (%@) is invalid.", __func__,__LINE__,ip.finally_success_time,ip.finally_fail_time);
            [speedNotTestedIpArray addObject:ip];
            continue;
        }
    
    }
    
    [speedTestedIpArray sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {
        WBDNSIpModel* ip1 = obj1;
        WBDNSIpModel* ip2 = obj2;
        int rtt1 = [ip1.rtt intValue];
        int rtt2 = [ip2.rtt intValue];
        if (rtt1 > rtt2) {
            return NSOrderedDescending;
        }
        else if(rtt1 < rtt2) {
            return NSOrderedAscending;
        }
        else {
            return NSOrderedSame;
        }
    }];
    
    [sortedIpArray addObjectsFromArray:speedTestedIpArray];
    [sortedIpArray addObjectsFromArray:speedNotTestedIpArray];
    model.ipModelArray = sortedIpArray;
    return model;
}

@end


================================================
FILE: src/DNSCache/SpeedTest/WBDNSSpeedTestManager.h
================================================
//
//  SpeedTestManager.h
//  DNSCache
//
//  Created by Robert Yang on 15/8/19.
//  Copyright (c) 2015年 Weibo. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "WBDNSModel.h"
@interface WBDNSSpeedTestManager : NSObject
- (void)testSpeedOfIpArrayOfDomain:(WBDNSDomainModel *)domain;
+ (instancetype)sharedInstance;
@end


================================================
FILE: src/DNSCache/SpeedTest/WBDNSSpeedTestManager.m
================================================
//
//  SpeedTestManager.m
//  DNSCache
//
//  Created by Robert Yang on 15/8/19.
//  Copyright (c) 2015年 Weibo. All rights reserved.
//

#import "WBDNSSpeedTestManager.h"
#import "WBDNSSpeedTester.h"
#import "WBDNSTCPSpeedTester.h"
#import "WBDNSModel.h"
#import "WBDNSCacheManager.h"
@implementation WBDNSSpeedTestManager
{
    id<WBDNSSpeedTester> _speedTester;
}

+ (instancetype)sharedInstance {
    static WBDNSSpeedTestManager* sharedInstance = nil;
    static dispatch_once_t predicate;
    dispatch_once(&predicate, ^{
        sharedInstance = [[WBDNSSpeedTestManager alloc]init];
    });
    return sharedInstance;
}

- (instancetype)init {
    self = [super init];
    if (self) {
        _speedTester = [[WBDNSTCPSpeedTester alloc]init];
    }
    return self;
}

-(void)testSpeedOfIpArrayOfDomain:(WBDNSDomainModel *)domain
{
    if (domain.ipModelArray == nil) {
        NSLog(@"ERROR:%s:%d domian or domain.ipModelArray is nil.",__func__,__LINE__);
        return;
    }
    
    if (domain.ipModelArray.count == 0) {
        return;
    }
    
    for (WBDNSIpModel *ip in domain.ipModelArray) {

        int rtt = [_speedTester testSpeedOf:ip.ip];
        if (rtt < WBDNS_SOCKET_CONNECT_TIMEOUT_RTT) {
            ip.success_num = [NSString stringWithFormat:@"%d", [ip.success_num intValue] +1];
            ip.finally_success_time = [[WBDNSTools sharedInstance]stringFromDate:[NSDate date]];
        }
        else
        {
            ip.err_num = [NSString stringWithFormat:@"%d", [ip.err_num intValue] +1];
            ip.finally_fail_time = [[WBDNSTools sharedInstance]stringFromDate:[NSDate date]];
        }
        ip.rtt = [NSString stringWithFormat:@"%d",rtt];
    }
    
    [[WBDNSCacheManager sharedInstance]updateIpModelSpeedInfoInCacheAndDB:domain];
}

@end


================================================
FILE: src/DNSCache/SpeedTest/WBDNSTCPSpeedTester.h
================================================
//
//  TCPSpeedTester.h
//  DNSCache
//
//  Created by Robert Yang on 15/8/19.
//  Copyright (c) 2015年 Weibo. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "WBDNSSpeedTester.h"

#define WBDNS_SOCKET_CONNECT_TIMEOUT 10 //单位秒
#define WBDNS_SOCKET_CONNECT_TIMEOUT_RTT 600000//10分钟 单位毫秒

@interface WBDNSTCPSpeedTester : NSObject <WBDNSSpeedTester>

@end


================================================
FILE: src/DNSCache/SpeedTest/WBDNSTCPSpeedTester.m
================================================
//
//  TCPSpeedTester.m
//  DNSCache
//
//  Created by Robert Yang on 15/8/19.
//  Copyright (c) 2015年 Weibo. All rights reserved.
//

#import "WBDNSTCPSpeedTester.h"
#import "WBDNSTools.h"
#import <sys/socket.h>
#import <netinet/in.h>
#import <fcntl.h>
#import <arpa/inet.h>
#import <netdb.h>

@implementation WBDNSTCPSpeedTester 

-(NSString*) getHostByName:(NSString*) url {
    struct addrinfo hints;
    memset(&hints, 0, sizeof(hints));
    hints.ai_family = PF_UNSPEC;        // PF_INET if you want only IPv4 addresses
    hints.ai_protocol = IPPROTO_TCP;
    
    struct addrinfo *addrs, *addr;
    
    getaddrinfo([url UTF8String], NULL, &hints, &addrs);
    NSMutableArray* ipStringArray = [NSMutableArray array];
    for (addr = addrs; addr; addr = addr->ai_next) {
        
        char host[NI_MAXHOST];
        getnameinfo(addr->ai_addr, addr->ai_addrlen, host, sizeof(host), NULL, 0, NI_NUMERICHOST);
        //            printf("%s\n", host);
        [ipStringArray addObject:[NSString stringWithUTF8String:host]];
    }
    freeaddrinfo(addrs);
    if (ipStringArray.count > 0) {
        return ipStringArray.firstObject;
    }
    else {
        return nil;
    }
}

/**
 *  本测速函数,使用linux socket connect 和select函数实现的。 基于以下原理
 *  1. 即使套接口是非阻塞的。如果连接的服务器在同一台主机上,那么在调用connect 建立连接时,连接通常会立即建立成功,我们必须处理这种情况。
 *  2. 源自Berkeley的实现(和Posix.1g)有两条与select 和非阻塞IO相关的规则:
 *     A. 当连接建立成功时,套接口描述符变成可写;
 *     B. 当连接出错时,套接口描述符变成既可读又可写。
 *  @param ip 用于测速对Ip,应该是IPv4格式。
 *
 *  @return 测速结果,单位时毫秒,WBDNS_SOCKET_CONNECT_TIMEOUT_RTT 代表超时。
 */
-(int) testSpeedOf:(NSString *)ip {
    NSString* oldIp = ip;
    if (![WBDNSTools isIpV4Address:ip]) {
        ip = [self getHostByName:ip];
        if (ip == nil) {
            NSLog(@"ERROR:%s:%d, params(%@) is invalid.",__FUNCTION__,__LINE__, oldIp);
            return 0;
        }
        
    }
    
    float rtt = 0.0;
    int s = 0;
    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(80);
    saddr.sin_addr.s_addr = inet_addr([ip UTF8String]);
    //saddr.sin_addr.s_addr = inet_addr("1.1.1.123");
    if( (s=socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        NSLog(@"ERROR:%s:%d, create socket failed.",__FUNCTION__,__LINE__);
        return 0;
    }

    NSDate* startTime = [NSDate date];
    NSDate* endTime;
    //为了设置connect超时 把socket设置称为非阻塞
    int flags = fcntl(s, F_GETFL,0);
    fcntl(s,F_SETFL, flags | O_NONBLOCK);
    int i = connect(s,(struct sockaddr*)&saddr, sizeof(saddr));
    if(i ==0) {
        //建立连接成功,返回rtt时间。 因为connect是非阻塞,所以这个时间就是一个函数执行的时间,毫秒级,没必要再测速了。
        close(s);
        return 1;
    }
    
    struct timeval tv;
    fd_set myset;
    int valopt;
    socklen_t lon;
    tv.tv_sec = WBDNS_SOCKET_CONNECT_TIMEOUT;
    tv.tv_usec = 0;
    FD_ZERO(&myset);
    FD_SET(s, &myset);
    
    int j = select(s+1, NULL, &myset, NULL, &tv);
    if (j > 0) {
        lon = sizeof(int);
        getsockopt(s, SOL_SOCKET, SO_ERROR, (void*)(&valopt), &lon);
        if (valopt) {
            NSLog(@"ERROR:%s:%d, select function error.",__FUNCTION__,__LINE__);
            rtt = 0;
        }
        else {
            endTime = [NSDate date];
            rtt = [endTime timeIntervalSinceDate:startTime] * 1000;
        }
    }
    else if (j == 0) {
        NSLog(@"INFO:%s:%d, test rtt of (%@) timeout.",__FUNCTION__,__LINE__, oldIp);
        rtt = WBDNS_SOCKET_CONNECT_TIMEOUT_RTT;
    }
    else {
        NSLog(@"ERROR:%s:%d, select function error.",__FUNCTION__,__LINE__);
        rtt = 0;
    }
    
    close(s);
    return rtt;
}

@end


================================================
FILE: src/DNSCache/Tools/WBDNSTools.h
================================================
//
//  Tools.h
//  DNSCache
//
//  Created by Robert Yang on 15/8/3.
//  Copyright (c) 2015年 Weibo. All rights reserved.
//

#import <Foundation/Foundation.h>
@class WBDNSDomainModel;
@class WBDNSIpModel;
@interface WBDNSTools : NSObject

+ (instancetype)sharedInstance;

- (NSDate *)dateFromString:(NSString *)s;

- (NSString *)stringFromDate:(NSDate *)date;
//非标准的md5算法,然后从 md5 获取 1 , 5 , 2 , 10 , 17 , 9 , 25 , 27 位(从0开始)的字符按顺序拼成一个8位长度的字符串 s
+ (NSString *) md5:(NSString *)str;

+ (NSString *)dateFormatter;

+ (NSString *)getIpUrlFromDomainUrl:(NSString *)domainUrl host:(NSString *)host ip:(NSString *) ip;

+ (NSString *)getHostNameOfUrl:(NSString *)urlString;

+ (NSString *)networkTypeToString:(int)networkType;

+ (NSString *)serviceProviderTypeToString:(int)spType;

+ (BOOL)isDomainModelExpired:(WBDNSDomainModel *)domainModel expireDuration:(long) duration;

+ (BOOL)isIpV4Address:(NSString *)url;

+ (BOOL)isIpRecordExpired:(WBDNSIpModel *)ipModel expireDuration:(long) duration;

+ (BOOL)isTestTimeExpired:(NSDate *)time expiredTime:(int) expiredTime;

+ (BOOL)isPureInt:(NSString *)string;

+ (BOOL)isStringArray:(NSArray *)arr1 equalToStringArray2:(NSArray *) arr2;

+ (BOOL)findString:(NSString *)str inStringArray:(NSArray *) stringArray;
@end


================================================
FILE: src/DNSCache/Tools/WBDNSTools.m
================================================
//
//  Tools.m
//  DNSCache
//
//  Created by Robert Yang on 15/8/3.
//  Copyright (c) 2015年 Weibo. All rights reserved.
//

#import "WBDNSTools.h"
#import "WBDNSNetworkManager.h"
#import <CommonCrypto/CommonDigest.h>
@implementation WBDNSTools
{
    NSDateFormatter *_dateFormat;
}

+ (instancetype)sharedInstance {
    static WBDNSTools* sharedInsance;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInsance = [[WBDNSTools alloc]init];
    });
    return sharedInsance;
}

- (instancetype)init {
    if (self = [super init]) {
        _dateFormat = [[NSDateFormatter alloc]init];
        [_dateFormat setDateFormat:[WBDNSTools dateFormatter]];
    }
    return self;
}

- (NSDate *)dateFromString:(NSString *)s {
    return [_dateFormat dateFromString:s];
}

- (NSString *)stringFromDate:(NSDate *)date {
    return [_dateFormat stringFromDate:date];
}

+ (NSString *)dateFormatter
{
    return @"yyyy-MM-dd HH:mm:ss Z";
}

+ (NSString *)getIpUrlFromDomainUrl:(NSString *)domainUrl host:(NSString *)host ip:(NSString *)ip
{
    if (domainUrl == nil) {
        NSLog(@"ERROR:%s:%d failed reason:domainUrl is nil.",__FUNCTION__,__LINE__);
        return domainUrl;
    }
    
    if (host == nil) {
        NSLog(@"ERROR:%s:%d failed reason:host is nil.",__FUNCTION__,__LINE__);
        return domainUrl;
    }
    
    if (ip == nil) {
        NSLog(@"ERROR:%s:%d failed reason:ip is nil.",__FUNCTION__,__LINE__);
        return domainUrl;
    }
    
    NSRange range = [domainUrl rangeOfString:host];
    if (range.length != host.length) {
        NSLog(@"ERROR:%s:%d failed reason:can't find %@ in %@.",__FUNCTION__,__LINE__, host, domainUrl);
        return domainUrl;
    }
    NSString *ipUrl = [domainUrl stringByReplacingCharactersInRange:range withString:ip];
    return ipUrl;
}

+ (NSString *) md5:(NSString *)str
{
     const char *cStr = [str UTF8String];
     unsigned char result[16];
     CC_MD5( cStr, (CC_LONG)strlen(cStr), result );
     NSString* orginalMd5 = [NSString stringWithFormat:@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
                                          result[0], result[1], result[2], result[3],
                                           result[4], result[5], result[6], result[7],
                                           result[8], result[9], result[10], result[11],
                                           result[12], result[13], result[14], result[15]
            ];
    return [NSString stringWithFormat:@"%c%c%c%c%c%c%c%c",
            [orginalMd5 characterAtIndex:1],
            [orginalMd5 characterAtIndex:5],
            [orginalMd5 characterAtIndex:2],
            [orginalMd5 characterAtIndex:10],
            [orginalMd5 characterAtIndex:17],
            [orginalMd5 characterAtIndex:9],
            [orginalMd5 characterAtIndex:25],
            [orginalMd5 characterAtIndex:27]];
}


+ (NSString *)getHostNameOfUrl:(NSString *)urlString
{
    NSURL* url = [NSURL URLWithString:urlString];
    if (url == nil) {
        NSLog(@"ERROR:%s:%d failed reason: %@ is invaild url.",__FUNCTION__,__LINE__, urlString);
    }
    
    return  url.host;
}

+ (NSString *)networkTypeToString:(int)networkType
{
    NSString* networkTypeString;
    
    switch (networkType) {
        case WBDNS_NETWORK_TYPE_UNCONNECTED:
            networkTypeString = @"NoNetwork";
            break;
        case WBDNS_NETWORK_TYPE_UNKNOWN:
            networkTypeString = @"UnknownNetwork";
            break;
        case WBDNS_NETWORK_TYPE_WIFI:
            networkTypeString = @"WifiNetwork";
            break;
        case WBDNS_NETWORK_TYPE_MOBILE:
            networkTypeString = @"OperatorNetwork";
            break;
        default:
            networkTypeString = @"UnknownNetwork";
            break;
    }
    return networkTypeString;
}

+ (NSString *)serviceProviderTypeToString:(int)spType
{
    NSString *spTypeString;
    
    switch (spType) {
        case WBDNS_MOBILE_UNKNOWN:
            spTypeString = @"UnknownSP";
            break;
        case WBDNS_MOBILE_TELCOM:
            spTypeString = @"ChinaTelecom";
            break;
        case WBDNS_MOBILE_UNICOM:
            spTypeString = @"ChinaUnicom";
            break;
        case WBDNS_MOBILE_CHINAMOBILE:
            spTypeString = @"ChinaMobile";
            break;
        default:
            spTypeString = @"UnknowNetwork";
            break;
    }
    return spTypeString;
}

+ (BOOL)isDomainModelExpired:(WBDNSDomainModel *)domainModel expireDuration:(long)duration
{
    NSDateFormatter* formatter = [[NSDateFormatter alloc]init];
    [formatter setDateFormat:[WBDNSTools dateFormatter]];
    NSDate* date = [formatter dateFromString:domainModel.time];
    int ttl = [domainModel.ttl intValue];
    NSDate* currentDate = [NSDate date];
    NSTimeInterval inteval = [currentDate timeIntervalSinceDate:date];
    if (inteval > ttl + duration) {
        return YES;
    }
    else {
        return NO;
    }
}

+ (BOOL)isIpRecordExpired:(WBDNSIpModel *)ipModel expireDuration:(long)duration
{
    NSDateFormatter* formatter = [[NSDateFormatter alloc]init];
    [formatter setDateFormat:[WBDNSTools dateFormatter]];
    NSDate* date = [formatter dateFromString:ipModel.finally_update_time];
    int ttl = [ipModel.ttl intValue];
    NSDate* currentDate = [NSDate date];
    NSTimeInterval inteval = [currentDate timeIntervalSinceDate:date];
    if (inteval > ttl + duration) {
        return YES;
    }
    else {
        return NO;
    }
}

+ (BOOL)isTestTimeExpired:(NSDate *)time expiredTime:(int)expiredTime
{
    //容错处理,如果间隔小于10秒,设为10秒。
    if (expiredTime < 10) {
        expiredTime = 10;
    }
    
    if ([time compare:[NSDate date]] == NSOrderedDescending) {
        NSLog(@"ERROR:%s:%d time(%@) is invalid.", __func__,__LINE__, time);
        return YES;
    }
    else {
        NSTimeInterval interval = [[NSDate date] timeIntervalSinceDate:time];
        if (interval < expiredTime) {
            return NO;
        }
        else {
            return YES;
        }
    }
}

+ (BOOL)isPureInt:(NSString *)string {
    
    NSScanner* scan = [NSScanner scannerWithString:string];
    
    int val;
    
    return[scan scanInt:&val] && [scan isAtEnd];
    
}

+ (BOOL)isIpV4Address:(NSString *) url {
    NSString *regex = @"^(2[0-4][0-9]|25[0-5]|1[0-9][0-9]|[1-9]?[0-9])(\\.(2[0-4][0-9]|25[0-5]|1[0-9][0-9]|[1-9]?[0-9])){3}$";
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", regex];
    return [predicate evaluateWithObject:url];
}

+ (BOOL)isStringArray:(NSArray *)arr1 equalToStringArray2:(NSArray *)arr2 {
    if (arr1 == nil && arr2 == nil) {
        return YES;
    }
    else if(arr1 == nil) {
        if (arr2.count == 0) {
            return YES;
        }
        else {
            return NO;
        }
    }
    else if(arr2 == nil) {
        if (arr1.count == 0) {
            return YES;
        }
        else {
            return NO;
        }
    }
    
    if (arr1.count != arr2.count) {
        return NO;
    }
    
    for (NSString* item1 in arr1) {
        if (![self findString:item1 inStringArray:arr2]) {
            return NO;
        }
    }
    
    for (NSString* item2 in arr2) {
        if (![self findString:item2 inStringArray:arr1]) {
            return NO;
        }
    }
    return YES;
}

+ (BOOL)findString:(NSString *)str inStringArray:(NSArray *)stringArray {
    for (NSString* temStr in stringArray) {
        if([str isEqualToString:temStr]) {
            return YES;
        }
    }
    return NO;
}

@end


================================================
FILE: src/DNSCache/Tools/WBDNSWeakTimer.h
================================================
//
//  WBDNSWeakTimer.h
//  DNSCache
//
//  Created by Robert Yang on 15/8/17.
//  Copyright (c) 2015年 Weibo. All rights reserved.
//

#import <Foundation/Foundation.h>

typedef void (^WBDNSTimerHandler)(id userInfo);

@interface WBDNSWeakTimer : NSObject
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)interval
                                      target:(id)aTarget
                                    selector:(SEL)aSelector
                                    userInfo:(id)userInfo
                                     repeats:(BOOL)repeats;

+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)interval
                                      block:(WBDNSTimerHandler)block
                                   userInfo:(id)userInfo
                                    repeats:(BOOL)repeats;
@end


================================================
FILE: src/DNSCache/Tools/WBDNSWeakTimer.m
================================================
//
//  WBDNSWeakTimer.m
//  DNSCache
//
//  Created by Robert Yang on 15/8/17.
//  Copyright (c) 2015年 Weibo. All rights reserved.
//

#import "WBDNSWeakTimer.h"

@interface WBDNSWeakTimerTarget : NSObject

@property (nonatomic, weak) id target;
@property (nonatomic, assign) SEL selector;
@property (nonatomic, weak) NSTimer* timer;

@end

@implementation WBDNSWeakTimerTarget

- (void)fire:(NSTimer *)timer {
    if(self.target) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
        [self.target performSelector:self.selector withObject:timer.userInfo afterDelay:0.0f];
#pragma clang diagnostic pop
    } else {
        [self.timer invalidate];
    }
}

@end

@implementation WBDNSWeakTimer

+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)interval
                                     target:(id)aTarget
                                   selector:(SEL)aSelector
                                   userInfo:(id)userInfo
                                    repeats:(BOOL)repeats {
    WBDNSWeakTimerTarget *timerTarget = [[WBDNSWeakTimerTarget alloc] init];
    timerTarget.target = aTarget;
    timerTarget.selector = aSelector;
    timerTarget.timer = [NSTimer scheduledTimerWithTimeInterval:interval
                                                         target:timerTarget
                                                       selector:@selector(fire:)
                                                       userInfo:userInfo
                                                        repeats:repeats];
    return timerTarget.timer;
}

+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)interval
                                      block:(WBDNSTimerHandler)block
                                   userInfo:(id)userInfo
                                    repeats:(BOOL)repeats {
    NSMutableArray *userInfoArray = [NSMutableArray arrayWithObject:[block copy]];
    if (userInfo != nil) {
        [userInfoArray addObject:userInfo];
    }
    return [self scheduledTimerWithTimeInterval:interval
                                         target:self
                                       selector:@selector(timerBlockInvoke:)
                                       userInfo:[userInfoArray copy]
                                        repeats:repeats];
}

+ (void)timerBlockInvoke:(NSArray*)userInfo {
    WBDNSTimerHandler block = userInfo[0];
    id info = nil;
    if (userInfo.count == 2) {
        info = userInfo[1];
    }
    
    if (block) {
        block(info);
    }
}
@end


================================================
FILE: src/DNSCache/WBDNSCache.h
================================================
//
//  DNSCache.h
//  DNSCache
//
//  Created by Robert Yang on 15/7/29.
//  Copyright (c) 2015年 Weibo. All rights reserved.
//

#import <Foundation/Foundation.h>

/**
 *  用于封装转换后URL的模型
 */
@interface WBDNSDomainInfo : NSObject

/**
 *  url 的唯一编号,目前没有使用。
 */
@property (nonatomic, strong) NSString *id;
/**
 *  转换后的URL
 */
@property (nonatomic, strong) NSString *url;

/**
 *  需要设置到Http head 中的主机头, 当host为空或者nil时,代表不设置host。
 */
@property (nonatomic, strong) NSString *host;

/**
 *  根据参数,生成一个WBDNSDomainInfo模型。
 *
 *  @param id   编号
 *  @param url  网址
 *  @param host 主机名
 *
 *  @return 生成的WBDNSDomainInfo模型。
 */
- (instancetype)initWithId:(NSString *)id url:(NSString *)url host:(NSString *)host;

@end


@interface WBDNSCache : NSObject
/**
 *  获取WBDNSCache的全局唯一对象。
 *
 *  @return WBDNSCache的全局唯一对象。
 */
+ (instancetype)sharedInstance;

/**
 *  从Http DNS server获取 转换后的URL。
 *
 *  @param urlString 原始的URL 注意Url必须已http:// 开头 否则SDK无法识别是否是合法的域名。
 *
 *  @return 转换后的直接使用的URL。是一个数组对象,里面有一个或多个WBDNSDomainInfo对象, nil 当没有网络且缓存数据过期,或者服务器配置禁止HTTPDNS服务的时候,返回nil。
 */
- (NSArray *)getDomainServerIpFromURL:(NSString *)urlString;

/**
 *  SDK 工作的原理是当用户请求domain 对应ip时,如果本地缓存没有数据,先取从本地dns获取Ip 返回给用户,同时向Sina http dns服务器请求,请求成功之后,存入缓存,下次用户请求便获得sina http dns 的IP,所以如果用户知道哪些domain后面会使用,那么可以提前从Http服务器请求,那么下次请求的时候,就会直接取到缓存中的sina http dns 返回的ip。
 *
 *  @param domainsArray 提前请求到domain数组,里面存储的是NSString类型的 域名
 */
- (void)preloadDomains:(NSArray *)domainsArray;


/**
 *  用于初始化整个全局唯一对象,仅程序初始化时调用一次。
 */
- (void)initialize;

/**
 *  设置应用程序在SinaDNS 服务器注册的app标识符和版本号。请在初始化函数调用前设置。
 *
 *  @param appKey  在新浪DNS服务器注册的 app标识符
 *  @param version 在新浪DNS服务器注册的 app版本号。
 */
+ (void)setAppkey:(NSString *)appKey version:(NSString *)version;


/**
 *  WBDNSCache SDK 需要从配置服务器获取需要的各种配置参数,如dns服务器地址,上传log服务器地址,网址支持白名单等参数。
 *  需要调用方设置配置服务器的地址,请在初始化函数前设置。
 *
 *  @param url WBDDSNCacheSDK的配置服务器地址
 */
+ (void)setConfigServerUrl:(NSString*)url;

@end


================================================
FILE: src/DNSCache/WBDNSCache.m
================================================
//
//  DNSCache.m
//  DNSCache
//
//  Created by Robert Yang on 15/7/29.
//  Copyright (c) 2015年 Weibo. All rights reserved.
//

#import "WBDNSCache.h"
#import "WBDNSModel.h"
#import "WBDNSNetworkManager.h"
#import "WBDNSQueryManager.h"
#import "WBDNSHttpDnsManager.h"
#import "WBDNSConfig.h"
#import "WBDNSConfigManager.h"
#import "WBDNSSpeedTestManager.h"
#import "WBDNSLogManager.h"
#import "WBDNSLogManager.h"
#import "WBDNSWeakTimer.h"

static NSString* WBDNSLastUploadLogTime = @"WBDNSLastUploadLogTime";

@implementation WBDNSDomainInfo

- (instancetype)initWithId:(NSString *)id url:(NSString *)url host:(NSString *)host {
    if (self = [super init]) {
        self.id = id;
        self.url = url;
        self.host = host;
    }
    return self;
}

+ (WBDNSDomainInfo *)generateDomainInfoByServerIp:(NSString *)serverIp url:(NSString *) url host:(NSString *) host {
    url = [WBDNSTools getIpUrlFromDomainUrl:url host:host ip:serverIp];
    return [[WBDNSDomainInfo alloc]initWithId:@"" url:url host:host];
}

+ (NSArray *)generateDomainInfoArrayByServerIpArray:(NSArray *)serverIpArray url:(NSString *) url host:(NSString *)host {
    NSMutableArray* resultArray = [NSMutableArray array];
    for (NSString* serverIp in serverIpArray) {
        WBDNSDomainInfo* domainInfo = [self generateDomainInfoByServerIp:serverIp url:url host:host];
        [resultArray addObject:domainInfo];
    }
    return resultArray;
}


- (NSString *)description {
    NSString* description = [NSString stringWithFormat:@"DomainInfo: id = %@, url = %@, host = %@", _id, _url, _host];
    return description;
}

@end


@interface WBDNSCache()
{
    WBDNSCacheManager *_dnsCacheManager;
    WBDNSQueryManager *_queryManager;
    WBDNSNetworkManager *_netWorkManager;
    WBDNSHttpDnsManager *_httpDnsManager;
    WBDNSConfigManager *_dnsCacheConfig;
    NSMutableDictionary *_runningTask;
    NSMutableDictionary *_runningSpeedTestTask;
    NSOperationQueue *_taskQueue;
    NSTimer *_timer;
    NSTimer *_testSpeedTimer;
}
@end

@implementation WBDNSCache

+ (instancetype)sharedInstance {
    static  WBDNSCache* sharedInstance = nil;
    static dispatch_once_t predicate;
    dispatch_once(&predicate, ^{
        sharedInstance = [[WBDNSCache alloc]init];
    });
    return sharedInstance;
}

- (void)initialize {
    //为了让客户端调用sharedInstance时 感觉有初始化这个动作。
}

- (instancetype)init {
    if (self = [super init]) {
        //其它模块都需要依赖config模块,所以config模块先初始化好一些。
        _dnsCacheConfig = [WBDNSConfigManager sharedInstance];
        _dnsCacheManager = [WBDNSCacheManager sharedInstance];
        _queryManager = [[WBDNSQueryManager alloc]initWithDnsCacheManager:_dnsCacheManager];
        _netWorkManager = [WBDNSNetworkManager sharedInstance];
        [_netWorkManager setDnsCacheManager:_dnsCacheManager];
        _httpDnsManager = [[WBDNSHttpDnsManager alloc]init];
        _runningTask = [[NSMutableDictionary alloc]init];
        _taskQueue = [[NSOperationQueue alloc]init];
        _runningSpeedTestTask = [[NSMutableDictionary alloc]init];
        
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(configDataChanged) name:WBDNSConfigDataChangeNotification object:nil];

        _timer = [WBDNSWeakTimer scheduledTimerWithTimeInterval:_dnsCacheConfig.config.refreshDomainIpInterval target:self selector:@selector(processPeriodicTask) userInfo:nil repeats:YES];
    }
    return self;
}

- (void)dealloc {
    _dnsCacheManager = nil;
    _queryManager = nil;
    _netWorkManager = nil;
    _httpDnsManager = nil;
    
    [_timer invalidate];
    _timer = nil;
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}


+ (void)setAppkey:(NSString *)appKey version:(NSString *)version {
    [WBDNSConfigManager setAppkey:appKey version:version];
}

+ (void)setConfigServerUrl:(NSString *) url {
    [WBDNSConfigManager setConfigServerUrl:url];
}

- (void)configDataChanged {
    dispatch_async(dispatch_get_main_queue(), ^{
        if ([_timer isValid]) {
            [_timer invalidate];
            _timer = nil;
        }
        
        _timer = [WBDNSWeakTimer scheduledTimerWithTimeInterval:_dnsCacheConfig.config.refreshDomainIpInterval target:self selector:@selector(processPeriodicTask) userInfo:nil repeats:YES];
    });
}

- (void)preloadDomains:(NSArray *)domainsArray {
    for (NSString* domain in domainsArray) {
        NSString* host;
        if ([domain hasPrefix:@"http://"]) {
            NSURL* url = [NSURL URLWithString:domain];
            host = url.host;
        } else {
            host = domain;
        }
        [self checkUpdate:host needSpeedTest:YES];
    }
}

- (NSArray *)getDomainServerIpFromURL:(NSString *)urlString {
    if (!_dnsCacheConfig.config.enableHttpDnsCache) {
        return nil;
    }
    
    NSURL* url = [NSURL URLWithString:urlString];
    
    if (url.host == nil) {
        NSLog(@"ERROR:%s:%d get host from (%@) failed ",__FUNCTION__,__LINE__, urlString);
        return nil;
    }
    
    if ([WBDNSTools isIpV4Address:url.host] || ![[WBDNSConfigManager sharedInstance] isSupportedDomain:url.host]) {
        NSArray* domainInfoArray = @[[[WBDNSDomainInfo alloc]initWithId:@"1" url:urlString host:@""]];
        return domainInfoArray;
    }
    
    WBDNSDomainModel* domainModel = [_queryManager queryDomainIp:_netWorkManager.currentSpTypeString host:url.host];
    
    if (domainModel == nil) {
        if ([[WBDNSConfigManager sharedInstance] isSupportedDomain:url.host] && [WBDNSConfigManager sharedInstance].config.enableRequestFromSinaHttpDnsServer) {
            [self checkUpdate:url.host needSpeedTest:YES];
        }
        return nil;
    }
    
    if (domainModel.id == WBDNS_LOCAL_DNS_ID) {
        if ([[WBDNSConfigManager sharedInstance] isSupportedDomain:url.host] && [WBDNSConfigManager sharedInstance].config.enableRequestFromSinaHttpDnsServer) {
            [self checkUpdate:url.host  needSpeedTest:YES];
        }
    }
    
    [WBDNSLogManager log:WBDNS_LOG_TYPE_INFO action:WBDNS_LOG_ACTION_INFO_DOMAIN body:[domainModel toDictionary] samplingRate:[WBDNSConfigManager sharedInstance].config.logSamplingRate];

    NSArray *domainInfoArray = [WBDNSDomainInfo generateDomainInfoArrayByServerIpArray:[domainModel serverIpArray]  url:urlString host:url.host];

    return domainInfoArray;
}


#pragma mark- 定时任务 -刷新域名对应IP

- (void)processPeriodicTask {
    if ([WBDNSConfigManager sharedInstance].config.enableRequestFromSinaHttpDnsServer == NO) {
        return;
    }
    
    if ([WBDNSNetworkManager sharedInstance].networkType == WBDNS_NETWORK_TYPE_UNCONNECTED
        || [WBDNSNetworkManager sharedInstance].networkType == WBDNS_NETWORK_TYPE_UNKNOWN) {
        return;
    }
    
    NSArray* expiredDomainInfos = [_dnsCacheManager getExpireDnsCache];
    for (WBDNSDomainModel* model in expiredDomainInfos) {
        [self checkUpdate:model.domain needSpeedTest:NO];
    }
    
    [self testSpeed];
    
    [self uploadLogs];
}

- (void)checkUpdate:(NSString *)domain needSpeedTest:(BOOL)needSpeedTest {
    if ([WBDNSNetworkManager sharedInstance].networkType == WBDNS_NETWORK_TYPE_UNCONNECTED
        || [WBDNSNetworkManager sharedInstance].networkType == WBDNS_NETWORK_TYPE_UNKNOWN) {
        return;
    }
    
    if (domain == nil) {
        return;
    }
    
    NSBlockOperation* operation = _runningTask[domain];
    if (operation == nil) {
        NSBlockOperation* operation = [NSBlockOperation blockOperationWithBlock:^{
            [_httpDnsManager requestHttpDnsByDomain:domain completionHandler:^(WBDNSHttpDnsPack * dnsPack) {
                WBDNSDomainModel* newModel;
                if(dnsPack != nil)
                {
                    newModel = [_dnsCacheManager insertDnsCache:dnsPack];
                    if (needSpeedTest) {
                        [self testSpeedOfModel:[newModel copy]];
                    }
                }
                
                [_runningTask performSelectorOnMainThread:@selector(removeObjectForKey:) withObject:domain waitUntilDone:NO];
            }];
        }];
        [_runningTask setObject:operation forKey:domain];
        [_taskQueue addOperation:operation];
    }
}

#pragma mark- 定时任务 -对域名IP进行测速

- (void)testSpeed {
    NSArray* modelArray = [_dnsCacheManager getAllModels];
    for (WBDNSDomainModel* model in modelArray) {
        
        if (![self needSpeedTestForDomain:model]) {
            continue;
        }
        
        [self testSpeedOfModel:[model copy]];
    }
}

- (void)testSpeedOfModel:(WBDNSDomainModel *)model {
    if ([WBDNSNetworkManager sharedInstance].networkType == WBDNS_NETWORK_TYPE_UNCONNECTED
        || [WBDNSNetworkManager sharedInstance].networkType == WBDNS_NETWORK_TYPE_UNKNOWN) {
        return;
    }
    
    if (model == nil || model.ipModelArray == nil || model.ipModelArray.count == 0) {
        NSLog(@"WARNING:%s:%d test ip of domain is %@ or ipArray is nil.",__func__,__LINE__,model.domain);
        return;
    }
    
    if(_runningSpeedTestTask[model.domain] == nil) {
        NSBlockOperation* operation = [NSBlockOperation blockOperationWithBlock:^{
            [[WBDNSSpeedTestManager sharedInstance]testSpeedOfIpArrayOfDomain:model];
            [_runningSpeedTestTask performSelectorOnMainThread:@selector(removeObjectForKey:) withObject:model.domain waitUntilDone:NO];
        }];
        [_runningSpeedTestTask setObject:operation forKey:model.domain];
        [_taskQueue addOperation:operation];
    }
}

- (BOOL)needSpeedTestForDomain:(WBDNSDomainModel *)model {
    if(model == nil || model.ipModelArray == nil  || model.ipModelArray.count == 0) {
        NSLog(@"ERROR:%s:%d model(%@) is invalid.",__func__,__LINE__, model.domain);
        return NO;
    }
    for (WBDNSIpModel* ip in model.ipModelArray) {
        NSDate* successTime = [[WBDNSTools sharedInstance] dateFromString:ip.finally_success_time];
        NSDate* failedTime = [[WBDNSTools sharedInstance] dateFromString:ip.finally_fail_time];
        NSDate* lastSpeedTestTime;
        //时间异常,需要测速。
        if (successTime == nil || failedTime == nil) {
            NSLog(@"ERROR:%s:%d SuccessTime(%@) or FailedTime (%@) is invalid.", __func__,__LINE__,ip.finally_success_time,ip.finally_fail_time);
            return YES;
        }
        
        if ([successTime compare:failedTime] == NSOrderedAscending) {
            lastSpeedTestTime = failedTime;
        } else {
            lastSpeedTestTime = successTime;
        }
        
        if ([WBDNSTools isTestTimeExpired:lastSpeedTestTime expiredTime:[WBDNSConfigManager sharedInstance].config.speedTestInterval]) {
            return YES;
        }
    }
    return NO;
}


#pragma mark- 定时任务 -上传日志
- (void)uploadLogs {
    if ([WBDNSNetworkManager sharedInstance].networkType != WBDNS_NETWORK_TYPE_WIFI) {
        return;
    }
    //周期判断
    NSDate* lastUploadTime = [[NSUserDefaults standardUserDefaults] objectForKey:WBDNSLastUploadLogTime];
    if (lastUploadTime != nil && [lastUploadTime compare:[NSDate date]] != NSOrderedDescending) {
        NSDate *shouldUploadTime = [lastUploadTime initWithTimeInterval:[WBDNSConfigManager sharedInstance].config.uploadLogInterval sinceDate:lastUploadTime];
        if ([[NSDate date]compare:shouldUploadTime] == NSOrderedAscending) {
            return;
        }
    }
    [[NSUserDefaults standardUserDefaults] setObject:[NSDate date] forKey:WBDNSLastUploadLogTime];
    [[NSUserDefaults standardUserDefaults]synchronize];
    
    [[WBDNSLogManager sharedInstance]uploadLogFiles];
}

@end


================================================
FILE: src/DNSCache/WBDNSConfigManager.h
================================================
//
//  WBDNSCacheConfig.h
//  DNSCache
//
//  Created by Robert Yang on 15/8/13.
//  Copyright (c) 2015年 Weibo. All rights reserved.
//

#import <Foundation/Foundation.h>

#define WBDNSConfigDataChangeNotification @"WBDNSConfigDataChangeNotification"
@class WBDNSConfig;
@interface WBDNSConfigManager : NSObject

@property (nonatomic, strong, getter=getConfig) WBDNSConfig *config;

/**
 *  获取WBDNSConfigManager的全局唯一实例。
 *
 *  @return WBDNSConfigManager的全局唯一实例。
 */
+ (WBDNSConfigManager *)sharedInstance;

/**
 *  获取WBDNSConfigManager的全局唯一参数配置
 *
 *  @return WBDNSConfigManager的全局唯一参数配置
 */
+ (WBDNSConfig *)sharedConfig;

/**
 *  设置使用DNSCache SDK的app 的唯一标识符和版本号,用于请求对应的参数配置文件。
 *  App应在Sina http dns server网站配置此标识符合版本对应的参数配置列表。
 *
 *  @param appKey  App申请的唯一标识符
 *  @param version App的版本号。
 */
+ (void)setAppkey:(NSString *)appKey version:(NSString *)version;

/**
 *  设置配置下发服务器的网址。
 *
 *  @param url 配置下发服务器的网址。
 */
+ (void)setConfigServerUrl:(NSString *)url;

/**
 * 设置上传Log服务器的地址
 *
 *  @param url 上传Log服务器的地址。
 */
+ (void)setLogServerUrl:(NSString *)url;

/**
 *  判断一个domain是否在服务器的支持列表中。
 *
 *  @param domain 待查询的domain
 *
 *  @return YES 代表服务器支持查询,NO代表不支持。
 */
- (BOOL)isSupportedDomain:(NSString *)domain;

/**
 *  获取设置的App key.
 *
 *  @return App key
 */
+ (NSString *)getAppkey;

/**
 *  获取设置的App version。
 *
 *  @return App Version
 */
+ (NSString *)getAppVersion;

/**
 *  获取推荐的服务器Url
 *
 *  @return 推荐的服务器Url
 */
- (NSString *)getServerUrl;

/**
 *  获取Log服务器Url
 *
 *  @return Log服务器Url
 */
- (NSString *)getLogServerUrl;

/**
 *  当使用服务器推荐URL失败的时候,调用此函数,通知这个URL调用失败了一次。
 *
 *  @param url 访问失败的URL
 */
- (void)setServerUrlFailedTimes:(NSString *) url;

@end



================================================
FILE: src/DNSCache/WBDNSConfigManager.m
================================================
//
//  WBDNSCacheConfig.m
//  DNSCache
//
//  Created by Robert Yang on 15/8/13.
//  Copyright (c) 2015年 Weibo. All rights reserved.
//

#import "WBDNSConfigManager.h"
#import "WBDNSTools.h"
#import "WBDNSNetworkManager.h"
#import "WBDNSConfig.h"
#import <UIKit/UIKit.h>
#define WBDNS_CONFIG_FILE_NAME @"WBDNSCacheConfigFile.json"

@implementation WBDNSConfigManager
{
    BOOL _getConfigDataSuccessfully;
    BOOL _isRequestingConfigDataFromServer;
    NSArray *_httpDnsServerUrlList;
    NSMutableDictionary *_httpDnsServerUrlFailedTimesDic;
    dispatch_queue_t _serverUrlOperationQueue;
}

static NSString* WBDNSCacheConfigServerUrl = @"";
static NSString* WBDNSCacheLogServerUrl = @"";
static NSString* WBDNS_APPKEY = @"";
static NSString* WBDNS_APP_VERSION = @"";

+ (WBDNSConfigManager *)sharedInstance {
    static WBDNSConfigManager* sharedInstance;
    static dispatch_once_t predicate;
    dispatch_once(&predicate, ^{
        sharedInstance = [[WBDNSConfigManager alloc]init];
    });
    return sharedInstance;
}

+ (WBDNSConfig *)sharedConfig {
    return [WBDNSConfigManager sharedInstance].config;
}

- (instancetype)init {
    if (self = [super init]) {
        _config = [[WBDNSConfig alloc]init];
        
        _serverUrlOperationQueue = dispatch_queue_create("com.sina.weibo.dnscache.serverUrlQueue", DISPATCH_QUEUE_SERIAL);
        
        if ([self isExistFile]) {
            [self readDataFromConfigFile];
        }
        else {
            [self saveConfigToJasonFile];
        }
        dispatch_sync(_serverUrlOperationQueue, ^{
            _httpDnsServerUrlList = [NSArray arrayWithArray:_config.httpDnsServerUrlList];
            _httpDnsServerUrlFailedTimesDic = [NSMutableDictionary dictionary];
            for (NSString* url in _config.httpDnsServerUrlList) {
                [_httpDnsServerUrlFailedTimesDic setObject:@(0) forKey:url];
            }
        });
        _getConfigDataSuccessfully = NO;
        _isRequestingConfigDataFromServer = NO;
        [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(networkStatusChanged:) name:(WBDNSNetworkStatusChangeNotification) object:nil];
        
        dispatch_async(dispatch_get_main_queue(), ^{
            [self requestingConfigDataFromServer];
        });
    }
    
    return self;
}

- (void)dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

- (WBDNSConfig *)getConfig {
    return _config;
}

- (void)networkStatusChanged:(NSNotification*)notif {
    WBDNSNetworkStatus status = [notif.object intValue];
    
    if (status == WBDNS_NETWORK_TYPE_MOBILE || status == WBDNS_NETWORK_TYPE_WIFI) {
        if (!_getConfigDataSuccessfully && !_isRequestingConfigDataFromServer) {
            dispatch_async(dispatch_get_main_queue(), ^{
                [self requestingConfigDataFromServer];
            });
        }
    }
}

- (BOOL)isExistFile {
    NSFileManager* fm = [[NSFileManager alloc] init];
    return [fm fileExistsAtPath:[self getFilePath]];
}

- (NSString *)getFilePath {
    //get db path
    NSArray *documentsPaths=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomainMask  , YES);
    NSString *databaseFilePath=[[documentsPaths objectAtIndex:0] stringByAppendingPathComponent:WBDNS_CONFIG_FILE_NAME];
    return databaseFilePath ;
}

- (void)saveConfigToJasonFile {
    @synchronized(self) {
        if (_config == nil) {
            NSLog(@"ERROR:%s:%d: _config is nil.",__func__,__LINE__);
            return;
        }
        NSDictionary* dic = @{@"HTTPDNS_LOG_SAMPLE_RATE":[NSString stringWithFormat:@"%d", _config.logSamplingRate],
                              @"HTTPDNS_SWITCH": _config.enableHttpDnsCache?@"1":@"0",
                              @"SCHEDULE_LOG_INTERVAL":[NSString stringWithFormat:@"%d", _config.uploadLogInterval*1000],
                              @"SCHEDULE_SPEED_INTERVAL":[NSString stringWithFormat:@"%d", _config.speedTestInterval*1000],
                              @"SCHEDULE_TIMER_INTERVAL":[NSString stringWithFormat:@"%d", _config.refreshDomainIpInterval*1000],
                              @"IS_MY_HTTP_SERVER":_config.enableRequestFromSinaHttpDnsServer?@"1":@"0",
                              @"IS_SORT":_config.enableSDKUpdateServerIpOrder?@"1":@"0",
                              @"SPEEDTEST_PLUGIN_NUM":[NSString stringWithFormat:@"%d", _config.speedTestFactorWeight],
                              @"PRIORITY_PLUGIN_NUM":[NSString stringWithFormat:@"%d", _config.serverSuggestionFactorWeight],
                              @"DOMAIN_SUPPORT_LIST":_config.supportedDomainList,
                              @"HTTPDNS_SERVER_API": _config.httpDnsServerUrlList
                              };
        NSData * jsonData = [NSJSONSerialization dataWithJSONObject:dic options:NSJSONWritingPrettyPrinted error:nil];
        BOOL result = [jsonData writeToFile:[self getFilePath] atomically:YES];
        if (!result) {
            NSLog(@"ERROR:%s:%d write to file failed.", __FUNCTION__, __LINE__);
        }
    }
}

- (void)requestingConfigDataFromServer {
    if (WBDNS_APP_VERSION.length == 0 || WBDNS_APPKEY.length == 0) {
        NSLog(@"ERROR:%s:%d WBDNS_APP_VERSION or WBDNS_APPKEY is not set.", __FUNCTION__, __LINE__);
        return;
    }
    
    if ([WBDNSNetworkManager sharedInstance].networkType == WBDNS_NETWORK_TYPE_UNCONNECTED || [WBDNSNetworkManager sharedInstance].networkType == WBDNS_NETWORK_TYPE_UNKNOWN) {
        return;
    }
    _isRequestingConfigDataFromServer = YES;
    NSString *identifierForVendor = [[UIDevice currentDevice].identifierForVendor UUIDString];
    NSString *secureCode = [WBDNSTools md5:[NSString stringWithFormat:@"%@%@",identifierForVendor,@"iheRFsFhLE9h9TRHVRLLBD6eS9ccQdLe"]];
    NSString* urlString = [NSString stringWithFormat:@"%@?k=%@&v=%@&c=httpdns&did=%@&s=%@",WBDNSCacheConfigServerUrl, WBDNS_APPKEY, WBDNS_APP_VERSION,identifierForVendor, [secureCode lowercaseString]];
    NSURL* url = [NSURL URLWithString:urlString];
    NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:url];
    [request setHTTPMethod:@"GET"];
    NSURLSessionConfiguration *config = [NSURLSessionConfiguration ephemeralSessionConfiguration];
    NSURLSession* session = [NSURLSession sessionWithConfiguration:config];
    NSURLSessionDataTask* task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
            if (!error) {
                NSDictionary* dic = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
                if (dic == nil) {
                    NSLog(@"ERROR:%s:%d deserialize response data failed.", __FUNCTION__, __LINE__);
                    return;
                }
                WBDNSConfig* oldConfig = [_config copy];
                //这个函数是线程安全的。
                [self readDataFromDic:dic];
                if (![oldConfig isEqual:_config]) {
                    [self saveConfigToJasonFile];
                    [[NSNotificationCenter defaultCenter] postNotificationName:WBDNSConfigDataChangeNotification object:nil];
                    [self updateServerUrlList];
                }
                _getConfigDataSuccessfully = YES;
            }
            else {
                _getConfigDataSuccessfully = NO;
                NSLog(@"ERROR:%s:%d request config data from server. failed reason:%@.",__FUNCTION__,__LINE__, error.description);
            }
            _isRequestingConfigDataFromServer = NO;
    }];
    [task resume];
    [session finishTasksAndInvalidate];
}

+ (void)setAppkey:(NSString *)appKey version:(NSString *)version {
    WBDNS_APPKEY = appKey;
    WBDNS_APP_VERSION = version;
}

+ (NSString *)getAppkey {
    return WBDNS_APPKEY;
}

+ (NSString *)getAppVersion {
    return WBDNS_APP_VERSION;
}

+ (void)setConfigServerUrl:(NSString *)url {
    if ([NSURL URLWithString:url] != nil) {
        WBDNSCacheConfigServerUrl = url;
    }
    else
    {
        NSLog(@"ERROR:%s:%d url:(%@) is invalid.", __FUNCTION__, __LINE__, url);
    }
}

+ (void)setLogServerUrl:(NSString *)url {
    if ([NSURL URLWithString:url] != nil) {
        WBDNSCacheLogServerUrl = url;
    }
    else
    {
        NSLog(@"ERROR:%s:%d url:(%@) is invalid.", __FUNCTION__, __LINE__, url);
    }
}



- (BOOL)isSupportedDomain:(NSString*)domain {
    NSArray* supportedDomainList = _config.supportedDomainList;
    //如果域名支持列表为空,那么默认支持所有的域名。
    if (supportedDomainList == nil || supportedDomainList.count == 0) {
        return YES;
    }
    
    for (NSString* temStr in supportedDomainList) {
        if([domain hasSuffix:temStr])
        {
            return YES;
        }
    }
    return NO;
}

- (void)readDataFromDic:(NSDictionary*)dic {
    if (dic == nil) {
        return;
    }
    
    if (_config == nil) {
        _config = [[WBDNSConfig alloc]init];
    }
    
    NSString *httpDnsSwitch = dic[@"HTTPDNS_SWITCH"];
    if ([httpDnsSwitch isEqualToString:@"1"]) {
        _config.enableHttpDnsCache = YES;
    }
    else if([httpDnsSwitch isEqualToString:@"0"]) {
        
        _config.enableHttpDnsCache = NO;
    }
    else {
        NSLog(@"WARNING%s:%d HTTPDNS_SWITCH:%@ is invalid.",__func__, __LINE__, httpDnsSwitch);
    }
    
    NSString *enableSDKUpdateServerIpOrderString = dic[@"IS_SORT"];
    if ([enableSDKUpdateServerIpOrderString isEqualToString:@"1"]) {
        _config.enableSDKUpdateServerIpOrder = YES;
    }
    else if([enableSDKUpdateServerIpOrderString isEqualToString:@"0"]) {
        
        _config.enableSDKUpdateServerIpOrder = NO;
    }
    else {
        NSLog(@"WARNING%s:%d IS_SORT:%@ is invalid.",__func__, __LINE__, enableSDKUpdateServerIpOrderString);
    }
    
    NSString *enableSinaHttpServer = dic[@"IS_MY_HTTP_SERVER"];
    if ([enableSinaHttpServer isEqualToString:@"1"]) {
        _config.enableRequestFromSinaHttpDnsServer = YES;
    }
    else if ([enableSinaHttpServer isEqualToString:@"0"]) {
        
        _config.enableRequestFromSinaHttpDnsServer = NO;
    }
    else {
        NSLog(@"WARNING%s:%d IS_MY_HTTP_SERVER:%@ is invalid.",__func__, __LINE__, enableSinaHttpServer);
    }
    
    
    NSString* logSamplingRate = dic[@"HTTPDNS_LOG_SAMPLE_RATE"];
    if (logSamplingRate && [WBDNSTools isPureInt:logSamplingRate]){
        _config.logSamplingRate = [logSamplingRate intValue];
    }
    else {
        NSLog(@"WARNING%s:%d SCHEDULE_LOG_INTERVAL:%@ is invalid.",__func__, __LINE__, logSamplingRate);
    }
    
    NSString* uploadLogInterval = dic[@"SCHEDULE_LOG_INTERVAL"];
    if (uploadLogInterval && [WBDNSTools isPureInt:uploadLogInterval]) {
        int logInterval = [uploadLogInterval intValue]/1000;
        if (logInterval > WBDNSMinInterval) {
            _config.uploadLogInterval = logInterval;
        }
        else {
            _config.uploadLogInterval = WBDNSMinInterval;
            NSLog(@"WARNING%s:%d SCHEDULE_LOG_INTERVAL:%@ is too short. use %d .",__func__, __LINE__, uploadLogInterval, WBDNSMinInterval);
        }
    }
    else {
        NSLog(@"WARNING%s:%d SCHEDULE_LOG_INTERVAL:%@ is invalid.",__func__, __LINE__, uploadLogInterval);
    }
    
    NSString *speedTestInterval = dic[@"SCHEDULE_SPEED_INTERVAL"];
    
    if (speedTestInterval && [WBDNSTools isPureInt:speedTestInterval]) {
        int speedInterval =[speedTestInterval intValue]/1000;
        
        if (speedInterval > WBDNSMinInterval) {
            _config.speedTestInterval = speedInterval;
        }
        else {
            _config.speedTestInterval = WBDNSMinInterval;
            NSLog(@"WARNING%s:%d SCHEDULE_SPEED_INTERVAL:%@ is too short. use %d .",__func__, __LINE__, speedTestInterval, WBDNSMinInterval);
        }
        
    }
    else {
        NSLog(@"WARNING%s:%d SCHEDULE_SPEED_INTERVAL:%@ is invalid.",__func__, __LINE__, speedTestInterval);
    }
    
    NSString* refreshDomainIpInterval = dic[@"SCHEDULE_TIMER_INTERVAL"];
    if (refreshDomainIpInterval && [WBDNSTools isPureInt:refreshDomainIpInterval]) {
        int refreshInterval = [refreshDomainIpInterval intValue]/1000;
        if (refreshInterval > WBDNSMinInterval) {
            _config.refreshDomainIpInterval = refreshInterval;
        }
        else {
            _config.refreshDomainIpInterval = WBDNSMinInterval;
            NSLog(@"WARNING%s:%d SCHEDULE_TIMER_INTERVAL:%@ is too short. use %d .",__func__, __LINE__, refreshDomainIpInterval, WBDNSMinInterval);
        }
        
    }
    else {
        NSLog(@"WARNING%s:%d SCHEDULE_TIMER_INTERVAL:%@ is invalid.",__func__, __LINE__, refreshDomainIpInterval);
    }
    
    NSString* speedTestFactorWeightString = dic[@"SPEEDTEST_PLUGIN_NUM"];
    if (speedTestFactorWeightString && [WBDNSTools isPureInt:speedTestFactorWeightString]) {
        _config.speedTestFactorWeight = [speedTestFactorWeightString intValue];
    }
    else {
        NSLog(@"WARNING%s:%d SPEEDTEST_PLUGIN_NUM:%@ is invalid.",__func__, __LINE__, speedTestFactorWeightString);
    }
    
//    NSString* serverSuggestionFactorWeightString = dic[@"PRIORITY_PLUGIN_NUM"];
//    if (serverSuggestionFactorWeightString && [WBDNSTools isPureInt:serverSuggestionFactorWeightString]){
//        _config.serverSuggestionFactorWeight = [serverSuggestionFactorWeightString intValue];
//    }
//    else {
//        NSLog(@"WARNING%s:%d PRIORITY_PLUGIN_NUM:%@ is invalid.",__func__, __LINE__, serverSuggestionFactorWeightString);
//    }
    
    NSArray* supportedDomainArray = dic[@"DOMAIN_SUPPORT_LIST"];
    if (supportedDomainArray) {
        _config.supportedDomainList = [NSArray arrayWithArray:supportedDomainArray];
    }
    
    NSArray* httpDnsServerUrlArray = dic[@"HTTPDNS_SERVER_API"];
    if (httpDnsServerUrlArray) {
        _config.httpDnsServerUrlList = [NSArray arrayWithArray:httpDnsServerUrlArray];
    }
}

- (void)readDataFromConfigFile {
    @synchronized(self) {
        NSData* fileData = [NSData dataWithContentsOfFile:[self getFilePath]];
        NSDictionary* dic = [NSJSONSerialization JSONObjectWithData:fileData options:kNilOptions error:nil];
        [self readDataFromDic:dic];
    }
}

- (NSString *) getServerUrl {
    __block NSString* serverUrl;
    dispatch_sync(_serverUrlOperationQueue, ^{
        serverUrl = [_httpDnsServerUrlList firstObject];
    });
    return serverUrl;
    
}

- (NSString *) getLogServerUrl {
    return WBDNSCacheLogServerUrl;
}

-(void) updateServerUrlList {
    dispatch_async(_serverUrlOperationQueue, ^{
        
        for (NSString* url in _httpDnsServerUrlList) {
            if (![WBDNSTools findString:url inStringArray:_config.httpDnsServerUrlList]) {
                [_httpDnsServerUrlFailedTimesDic removeObjectForKey:url];
            }
        }
        
        for (NSString* url in _config.httpDnsServerUrlList) {
            if (![WBDNSTools findString:url inStringArray:_httpDnsServerUrlList]) {
                [_httpDnsServerUrlFailedTimesDic setObject:@(0) forKey:url];
            }
        }
        
        _httpDnsServerUrlList = [NSArray arrayWithArray:_config.httpDnsServerUrlList];
        _httpDnsServerUrlList = [_httpDnsServerUrlList sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
            int obj1FailTimes = [_httpDnsServerUrlFailedTimesDic[obj1] intValue];
            int obj2FailTimes = [_httpDnsServerUrlFailedTimesDic[obj2] intValue];
            
            if(obj1FailTimes > obj2FailTimes) {
                return NSOrderedDescending;
            }
            else if(obj1FailTimes < obj2FailTimes) {
                return NSOrderedAscending;
            }
            else {
                return NSOrderedSame;
            }
        }];
    });
}

- (void)setServerUrlFailedTimes:(NSString *)url {
    
    dispatch_async(_serverUrlOperationQueue, ^{
        
        if (url == nil) {
            NSLog(@"ERROR:%s:%d: serverUrl is nil.",__FUNCTION__,__LINE__);
            return;
        }
        
        NSNumber* failedTimes = _httpDnsServerUrlFailedTimesDic[url];
        if (failedTimes) {
            _httpDnsServerUrlFailedTimesDic[url] = @(failedTimes.intValue+1);
        }
        else {
            NSLog(@"ERROR:%s:%d: url(%@) is not found in dic.",__FUNCTION__,__LINE__, url);
            return;
        }
        
        _httpDnsServerUrlList = [[_httpDnsServerUrlList sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
            int obj1FailTimes = [_httpDnsServerUrlFailedTimesDic[obj1] intValue];
            int obj2FailTimes = [_httpDnsServerUrlFailedTimesDic[obj2] intValue];
            
            if(obj1FailTimes > obj2FailTimes) {
                return NSOrderedDescending;
            }
            else if(obj1FailTimes < obj2FailTimes) {
                return NSOrderedAscending;
            }
            else {
                return NSOrderedSame;
            }
        }] mutableCopy];
    });
}

@end


================================================
FILE: src/DNSCache/WBDNSNetworkManager.h
================================================
//
//  NetworkManager.h
//  DNSCache
//
//  Created by Robert Yang on 15/8/4.
//  Copyright (c) 2015年 Weibo. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "WBDNSReachability.h"
#import "WBDNSCacheManager.h"

/**
 * 无网络
 */
#define WBDNS_NETWORK_TYPE_UNCONNECTED -1
/**
 * 未知网络
 */
#define WBDNS_NETWORK_TYPE_UNKNOWN 0
/**
 * WIFI网络
 */
#define WBDNS_NETWORK_TYPE_WIFI 1
/**
 * 运营商网络
 */
#define WBDNS_NETWORK_TYPE_MOBILE 2


/**
 * 未知运营商
 */
#define WBDNS_MOBILE_UNKNOWN 0  // 未知运营商
/**
 * mobile-中国电信
 */
#define WBDNS_MOBILE_TELCOM 3  // 中国电信
/**
 * mobile-中国联通
 */
#define WBDNS_MOBILE_UNICOM 5 // 中国联通
/**
 * mobile-中国移动
 */
#define WBDNS_MOBILE_CHINAMOBILE 4 // 中国移动

static NSString* WBDNSNetworkStatusChangeNotification = @"WBDNSNetworkStatusChangeNotification";

@interface WBDNSNetworkManager : NSObject

@property (atomic, strong) NSString *lastSpTypeString;
@property (atomic, strong) NSString *currentSpTypeString;
@property (atomic, assign) int networkType;
@property (atomic, strong) NSString *networkTypeString;

+ (instancetype)sharedInstance;
- (void)setDnsCacheManager:(id<WBDNSCacheProtocol>)dnsCacheManager;

@end


================================================
FILE: src/DNSCache/WBDNSNetworkManager.m
================================================
//
//  NetworkManager.m
//  DNSCache
//
//  Created by Robert Yang on 15/8/4.
//  Copyright (c) 2015年 Weibo. All rights reserved.
//

#import "WBDNSNetworkManager.h"
#import "WBDNSCacheManager.h"
#import <CoreTelephony/CTCarrier.h>
#import <CoreTelephony/CTTelephonyNetworkInfo.h>
#import <SystemConfiguration/CaptiveNetwork.h>
@implementation WBDNSNetworkManager
{
    id<WBDNSCacheProtocol> _manager;
    WBDNSReachability *_reachability;
    WBDNSNetworkManager *_sharedInstance;
    
}

+ (instancetype)sharedInstance {
    static  WBDNSNetworkManager* sharedInstance = nil;
    static dispatch_once_t predicate;
    dispatch_once(&predicate, ^{
        sharedInstance = [[WBDNSNetworkManager alloc]init];
    });
    return sharedInstance;
}

- (void)setDnsCacheManager:(id<WBDNSCacheProtocol>)dnsCacheManager {
    _manager = dnsCacheManager;
}

- (instancetype)init {
    if (self = [super init]) {
        _reachability = [WBDNSReachability reachabilityForInternetConnection];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reachabilityChanged:) name:kWBDNSReachabilityChangedNotification object:nil];
        [_reachability startNotifier];
        self.networkType = WBDNS_NETWORK_TYPE_UNKNOWN;
        self.networkTypeString = [WBDNSTools networkTypeToString:self.networkType];
        self.lastSpTypeString = @"UnknownSP";
        self.currentSpTypeString = @"UnkownSP";
        [self refreshNetworkInfo:_reachability];
    }
    return  self;
}

- (void)reachabilityChanged:(NSNotification *)note {
    WBDNSReachability * reach = [note object];
    [self refreshNetworkInfo:reach];
    WBDNSNetworkStatus status = [reach currentReachabilityStatus];
    [[NSNotificationCenter defaultCenter]postNotificationName:WBDNSNetworkStatusChangeNotification object:@(status)];
}

- (void)refreshNetworkInfo:(WBDNSReachability *)reach {
    if (reach == nil) {
        reach = _reachability;
    }
    
    WBDNSNetworkStatus status = [reach currentReachabilityStatus];
    switch (status) {
        case WBDNSNotReachable:
            self.networkType = WBDNS_NETWORK_TYPE_UNCONNECTED;
            self.networkTypeString = [WBDNSTools networkTypeToString:self.networkType];
            break;
        case WBDNSReachableViaWiFi:
        {
            self.networkType = WBDNS_NETWORK_TYPE_WIFI;
            self.networkTypeString = [WBDNSTools networkTypeToString:self.networkType];
            if((self.currentSpTypeString = [self fetchSSIDName]) == nil) {
                self.currentSpTypeString = @"UnkownWifiSP";
            }
            
            if (self.currentSpTypeString != self.lastSpTypeString) {
                [_manager clearMemoryCache];
            }
            self.lastSpTypeString = self.currentSpTypeString;
            break;
        }
        case WBDNSReachableViaWWAN:
        {
            self.networkType = WBDNS_NETWORK_TYPE_MOBILE;
            self.networkTypeString = [WBDNSTools networkTypeToString:self.networkType];
            CTTelephonyNetworkInfo *info = [[CTTelephonyNetworkInfo alloc] init];
            CTCarrier *carrier = [info subscriberCellularProvider];
            self.currentSpTypeString = [self getSPNameFromCode:carrier.mobileNetworkCode];
            NSLog(@"%@, %@", carrier.carrierName, carrier.mobileNetworkCode);
            
            if (self.currentSpTypeString != self.lastSpTypeString) {
                [_manager clearMemoryCache];
            }
            self.lastSpTypeString = self.currentSpTypeString;
            break;
        }
        default:
            self.networkType = WBDNS_NETWORK_TYPE_UNKNOWN;
            self.networkTypeString = [WBDNSTools networkTypeToString:self.networkType];
            NSLog(@"ERROR:%s:%d this condition should not appear.", __func__, __LINE__);
            break;
    }
    
}

- (NSArray *)chinaMobileNetworkCode {
    return @[@"00",@"02",@"07",@"20"];
}

- (NSArray *)chinaUnicomNetworkCode {
    return @[@"01",@"06"];
}

- (NSArray *)chinaTelecomNetworkCode {
    return @[@"03",@"05"];
}

- (NSString *)getSPNameFromCode:(NSString *)networkCode {
    if([[self chinaMobileNetworkCode] containsObject:networkCode]) {
        return @"ChinaMobile";
    }
    else if([[self chinaUnicomNetworkCode] containsObject:networkCode]) {
        return @"ChinaUnicom";
    }
    else if([[self chinaTelecomNetworkCode] containsObject:networkCode]) {
        return @"ChinaTelecom";
    }
    else {
        return @"UnknowSP";
    }
}

- (NSString *)fetchSSIDName {
    return [self fetchSSIDInfo][@"SSID"];
}

- (NSDictionary *)fetchSSIDInfo {
    NSArray *interfaceNames = CFBridgingRelease(CNCopySupportedInterfaces());
    
    NSDictionary *SSIDInfo;
    for (NSString *interfaceName in interfaceNames) {
        SSIDInfo = CFBridgingRelease(
                                     CNCopyCurrentNetworkInfo((__bridge CFStringRef)interfaceName));
        //NSLog(@"%s: %@ => %@", __func__, interfaceName, SSIDInfo);
        
        BOOL isNotEmpty = (SSIDInfo.count > 0);
        if (isNotEmpty) {
            break;
        }
    }
    return SSIDInfo;
}

- (void)dealloc {
    [_reachability stopNotifier];
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

@end


================================================
FILE: src/DNSCache/WBDNSReachability.h
================================================
/*
     File: Reachability.h
 Abstract: Basic demonstration of how to use the SystemConfiguration Reachablity APIs.
  Version: 3.5
 
 Disclaimer: IMPORTANT:  This Apple software is supplied to you by Apple
 Inc. ("Apple") in consideration of your agreement to the following
 terms, and your use, installation, modification or redistribution of
 this Apple software constitutes acceptance of these terms.  If you do
 not agree with these terms, please do not use, install, modify or
 redistribute this Apple software.
 
 In consideration of your agreement to abide by the following terms, and
 subject to these terms, Apple grants you a personal, non-exclusive
 license, under Apple's copyrights in this original Apple software (the
 "Apple Software"), to use, reproduce, modify and redistribute the Apple
 Software, with or without modifications, in source and/or binary forms;
 provided that if you redistribute the Apple Software in its entirety and
 without modifications, you must retain this notice and the following
 text and disclaimers in all such redistributions of the Apple Software.
 Neither the name, trademarks, service marks or logos of Apple Inc. may
 be used to endorse or promote products derived from the Apple Software
 without specific prior written permission from Apple.  Except as
 expressly stated in this notice, no other rights or licenses, express or
 implied, are granted by Apple herein, including but not limited to any
 patent rights that may be infringed by your derivative works or by other
 works in which the Apple Software may be incorporated.
 
 The Apple Software is provided by Apple on an "AS IS" basis.  APPLE
 MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
 THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
 FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
 OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
 
 IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
 OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
 MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
 AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
 STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
 POSSIBILITY OF SUCH DAMAGE.
 
 Copyright (C) 2014 Apple Inc. All Rights Reserved.
 
 */

#import <Foundation/Foundation.h>
#import <SystemConfiguration/SystemConfiguration.h>
#import <netinet/in.h>


typedef enum : NSInteger {
	WBDNSNotReachable = 0,
	WBDNSReachableViaWiFi,
	WBDNSReachableViaWWAN
} WBDNSNetworkStatus;


extern NSString *kWBDNSReachabilityChangedNotification;


@interface WBDNSReachability : NSObject

/*!
 * Use to check the reachability of a given host name.
 */
+ (instancetype)reachabilityWithHostName:(NSString *)hostName;

/*!
 * Use to check the reachability of a given IP address.
 */
+ (instancetype)reachabilityWithAddress:(const struct sockaddr_in *)hostAddress;

/*!
 * Checks whether the default route is available. Should be used by applications that do not connect to a particular host.
 */
+ (instancetype)reachabilityForInternetConnection;

/*!
 * Checks whether a local WiFi connection is available.
 */
+ (instancetype)reachabilityForLocalWiFi;

/*!
 * Start listening for reachability notifications on the current run loop.
 */
- (BOOL)startNotifier;

/*!
 * Stop listening for reachability notifications on the current run loop.
 */
- (void)stopNotifier;


/**
 *  Get current reachability status.
 *
 *  @return current reachability status.
 */
- (WBDNSNetworkStatus)currentReachabilityStatus;

/*!
 * WWAN may be available, but not active until a connection has been established. WiFi may require a connection for VPN on Demand.
 */
- (BOOL)connectionRequired;

@end




================================================
FILE: src/DNSCache/WBDNSReachability.m
================================================
/*
     File: Reachability.m
 Abstract: Basic demonstration of how to use the SystemConfiguration Reachablity APIs.
  Version: 3.5
 
 Disclaimer: IMPORTANT:  This Apple software is supplied to you by Apple
 Inc. ("Apple") in consideration of your agreement to the following
 terms, and your use, installation, modification or redistribution of
 this Apple software constitutes acceptance of these terms.  If you do
 not agree with these terms, please do not use, install, modify or
 redistribute this Apple software.
 
 In consideration of your agreement to abide by the following terms, and
 subject to these terms, Apple grants you a personal, non-exclusive
 license, under Apple's copyrights in this original Apple software (the
 "Apple Software"), to use, reproduce, modify and redistribute the Apple
 Software, with or without modifications, in source and/or binary forms;
 provided that if you redistribute the Apple Software in its entirety and
 without modifications, you must retain this notice and the following
 text and disclaimers in all such redistributions of the Apple Software.
 Neither the name, trademarks, service marks or logos of Apple Inc. may
 be used to endorse or promote products derived from the Apple Software
 without specific prior written permission from Apple.  Except as
 expressly stated in this notice, no other rights or licenses, express or
 implied, are granted by Apple herein, including but not limited to any
 patent rights that may be infringed by your derivative works or by other
 works in which the Apple Software may be incorporated.
 
 The Apple Software is provided by Apple on an "AS IS" basis.  APPLE
 MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
 THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
 FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
 OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
 
 IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
 OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
 MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
 AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
 STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
 POSSIBILITY OF SUCH DAMAGE.
 
 Copyright (C) 2014 Apple Inc. All Rights Reserved.
 
 */

#import <arpa/inet.h>
#import <ifaddrs.h>
#import <netdb.h>
#import <sys/socket.h>

#import <CoreFoundation/CoreFoundation.h>

#import "WBDNSReachability.h"


NSString *kWBDNSReachabilityChangedNotification = @"kWBDNSNetworkReachabilityChangedNotification";


#pragma mark - Supporting functions

#define kWBDNSShouldPrintReachabilityFlags 0

static void WBDNSPrintReachabilityFlags(SCNetworkReachabilityFlags flags, const char* comment)
{
#if kWBDNSShouldPrintReachabilityFlags

    NSLog(@"Reachability Flag Status: %c%c %c%c%c%c%c%c%c %s\n",
          (flags & kSCNetworkReachabilityFlagsIsWWAN)				? 'W' : '-',
          (flags & kSCNetworkReachabilityFlagsReachable)            ? 'R' : '-',

          (flags & kSCNetworkReachabilityFlagsTransientConnection)  ? 't' : '-',
          (flags & kSCNetworkReachabilityFlagsConnectionRequired)   ? 'c' : '-',
          (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic)  ? 'C' : '-',
          (flags & kSCNetworkReachabilityFlagsInterventionRequired) ? 'i' : '-',
          (flags & kSCNetworkReachabilityFlagsConnectionOnDemand)   ? 'D' : '-',
          (flags & kSCNetworkReachabilityFlagsIsLocalAddress)       ? 'l' : '-',
          (flags & kSCNetworkReachabilityFlagsIsDirect)             ? 'd' : '-',
          comment
          );
#endif
}


static void WBDNSReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info)
{
#pragma unused (target, flags)
	NSCAssert(info != NULL, @"info was NULL in ReachabilityCallback");
	NSCAssert([(__bridge NSObject*) info isKindOfClass: [WBDNSReachability class]], @"info was wrong class in ReachabilityCallback");

    WBDNSReachability* noteObject = (__bridge WBDNSReachability *)info;
    // Post a notification to notify the client that the network reachability changed.
    [[NSNotificationCenter defaultCenter] postNotificationName: kWBDNSReachabilityChangedNotification object: noteObject];
}


#pragma mark - Reachability implementation

@implementation WBDNSReachability
{
	BOOL _alwaysReturnLocalWiFiStatus; //default is NO
	SCNetworkReachabilityRef _reachabilityRef;
}

+ (instancetype)reachabilityWithHostName:(NSString *)hostName {
	WBDNSReachability* returnValue = NULL;
	SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(NULL, [hostName UTF8String]);
	if (reachability != NULL) {
		returnValue= [[self alloc] init];
		if (returnValue != NULL) {
			returnValue->_reachabilityRef = reachability;
			returnValue->_alwaysReturnLocalWiFiStatus = NO;
		}
	}
	return returnValue;
}


+ (instancetype)reachabilityWithAddress:(const struct sockaddr_in *)hostAddress {
	SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)hostAddress);

	WBDNSReachability* returnValue = NULL;

	if (reachability != NULL) {
		returnValue = [[self alloc] init];
		if (returnValue != NULL) {
			returnValue->_reachabilityRef = reachability;
			returnValue->_alwaysReturnLocalWiFiStatus = NO;
		}
	}

	return returnValue;
}



+ (instancetype)reachabilityForInternetConnection {
	struct sockaddr_in zeroAddress;
	bzero(&zeroAddress, sizeof(zeroAddress));
	zeroAddress.sin_len = sizeof(zeroAddress);
	zeroAddress.sin_family = AF_INET;
    
	return [self reachabilityWithAddress:&zeroAddress];
}


+ (instancetype)reachabilityForLocalWiFi {
	struct sockaddr_in localWifiAddress;
	bzero(&localWifiAddress, sizeof(localWifiAddress));
	localWifiAddress.sin_len = sizeof(localWifiAddress);
	localWifiAddress.sin_family = AF_INET;

	// IN_LINKLOCALNETNUM is defined in <netinet/in.h> as 169.254.0.0.
	localWifiAddress.sin_addr.s_addr = htonl(IN_LINKLOCALNETNUM);

	WBDNSReachability* returnValue = [self reachabilityWithAddress: &localWifiAddress];
	if (returnValue != NULL) {
		returnValue->_alwaysReturnLocalWiFiStatus = YES;
	}
    
	return returnValue;
}


#pragma mark - Start and stop notifier

- (BOOL)startNotifier {
	BOOL returnValue = NO;
	SCNetworkReachabilityContext context = {0, (__bridge void *)(self), NULL, NULL, NULL};

	if (SCNetworkReachabilitySetCallback(_reachabilityRef, WBDNSReachabilityCallback, &context)) {
		if (SCNetworkReachabilityScheduleWithRunLoop(_reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode)) {
			returnValue = YES;
		}
	}
    
	return returnValue;
}


- (void)stopNotifier {
	if (_reachabilityRef != NULL) {
		SCNetworkReachabilityUnscheduleFromRunLoop(_reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
	}
}


- (void)dealloc {
	[self stopNotifier];
	if (_reachabilityRef != NULL) {
		CFRelease(_reachabilityRef);
	}
}


#pragma mark - Network Flag Handling

- (WBDNSNetworkStatus)localWiFiStatusForFlags:(SCNetworkReachabilityFlags)flags {
	WBDNSPrintReachabilityFlags(flags, "localWiFiStatusForFlags");
	WBDNSNetworkStatus returnValue = WBDNSNotReachable;

	if ((flags & kSCNetworkReachabilityFlagsReachable) && (flags & kSCNetworkReachabilityFlagsIsDirect)) {
		returnValue = WBDNSReachableViaWiFi;
	}
    
	return returnValue;
}


- (WBDNSNetworkStatus)networkStatusForFlags:(SCNetworkReachabilityFlags)flags {
	WBDNSPrintReachabilityFlags(flags, "networkStatusForFlags");
	if ((flags & kSCNetworkReachabilityFlagsReachable) == 0) {
		// The target host is not reachable.
		return WBDNSNotReachable;
	}

    WBDNSNetworkStatus returnValue = WBDNSNotReachable;

	if ((flags & kSCNetworkReachabilityFlagsConnectionRequired) == 0) {
		/*
         If the target host is reachable and no connection is required then we'll assume (for now) that you're on Wi-Fi...
         */
		returnValue = WBDNSReachableViaWiFi;
	}

	if ((((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) ||
        (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0)) {
        /*
         ... and the connection is on-demand (or on-traffic) if the calling application is using the CFSocketStream or higher APIs...
         */

        if ((flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0) {
            /*
             ... and no [user] intervention is needed...
             */
            returnValue = WBDNSReachableViaWiFi;
        }
    }

	if ((flags & kSCNetworkReachabilityFlagsIsWWAN) == kSCNetworkReachabilityFlagsIsWWAN) {
		/*
         ... but WWAN connections are OK if the calling application is using the CFNetwork APIs.
         */
		returnValue = WBDNSReachableViaWWAN;
	}
    
	return returnValue;
}


- (BOOL)connectionRequired {
	NSAssert(_reachabilityRef != NULL, @"connectionRequired called with NULL reachabilityRef");
	SCNetworkReachabilityFlags flags;

	if (SCNetworkReachabilityGetFlags(_reachabilityRef, &flags)) {
		return (flags & kSCNetworkReachabilityFlagsConnectionRequired);
	}

    return NO;
}


- (WBDNSNetworkStatus)currentReachabilityStatus {
	NSAssert(_reachabilityRef != NULL, @"currentNetworkStatus called with NULL SCNetworkReachabilityRef");
	WBDNSNetworkStatus returnValue = WBDNSNotReachable;
	SCNetworkReachabilityFlags flags;
    
	if (SCNetworkReachabilityGetFlags(_reachabilityRef, &flags)) {
		if (_alwaysReturnLocalWiFiStatus) {
			returnValue = [self localWiFiStatusForFlags:flags];
		}
		else {
			returnValue = [self networkStatusForFlags:flags];
		}
	}
    
	return returnValue;
}

@end


================================================
FILE: src/DNSCache/WBDNSSpeedTester.h
================================================
//
//  WBDNSSpeedTester.h
//  DNSCache
//
//  Created by Robert Yang on 15/8/19.
//  Copyright (c) 2015年 Weibo. All rights reserved.
//

@protocol WBDNSSpeedTester <NSObject>

-(int) testSpeedOf:(NSString *)ip;

@end



================================================
FILE: src/DNSCache/cache/WBDNSCacheManager.h
================================================
//
//  DNSCacheManager.h
//  DNSCache
//
//  Created by Robert Yang on 15/8/3.
//  Copyright (c) 2015年 Weibo. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "WBDNSDBManager.h"
#import "WBDNSTools.h"
#import "WBDNSCache.h"
@protocol WBDNSCacheProtocol <NSObject>

- (void)addMemoryCache:(NSString *)url model:(WBDNSDomainModel *)model;
- (WBDNSDomainModel *)getDnsCache:(NSString *)sp url:(NSString *)url;
- (WBDNSDomainModel *)insertDnsCache:(WBDNSHttpDnsPack *) dnsPack;
- (NSArray *)getExpireDnsCache;
- (NSArray *)getAllModels;
- (BOOL)updateIpModelSpeedInfoInCacheAndDB:(WBDNSDomainModel *)model;

//本函数只用于更新从本地dns获取的ip, 从httpserver 获取的ip 不应该用此函数更新。
- (void)updateMemoryCache:(NSString *)domain model:(WBDNSDomainModel *)model;

- (void)clear;
- (void)clearDB;
- (void)clearMemoryCache;


@end

@interface WBDNSCacheManager : NSObject<WBDNSCacheProtocol>

+(instancetype) sharedInstance;

- (NSArray *)getAllModelsFromDB;

@end


================================================
FILE: src/DNSCache/cache/WBDNSCacheManager.m
================================================
//
//  DNSCacheManager.m
//  DNSCache
//
//  Created by Robert Yang on 15/8/3.
//  Copyright (c) 2015年 Weibo. All rights reserved.
//

#import "WBDNSCacheManager.h"
#import "WBDNSMemeryCache.h"
#import "WBDNSConfig.h"
#import "WBDNSConfigManager.h"
#import "WBDNSQueryManager.h"
#import "WBDNSNetworkManager.h"
#import "WBDNSTools.h"

@interface WBDNSCacheManager()
{
    WBDNSDBManager *_dbManager;
    WBDNSMemeryCache *_cacheDic;
}

@end

@implementation WBDNSCacheManager

+ (instancetype)sharedInstance {
    static  WBDNSCacheManager *sharedInstance = nil;
    static dispatch_once_t predicate;
    dispatch_once(&predicate, ^{
        sharedInstance = [[WBDNSCacheManager alloc]init];
    });
    return sharedInstance;
}

- (id)init {
    if (self = [super init]) {
        _dbManager = [[WBDNSDBManager alloc]init];
        _cacheDic = [WBDNSMemeryCache sharedInstance];
    }
    return  self;
}

- (NSArray *)getExpireDnsCache {
    @synchronized(self) {
        return [_cacheDic getExpireDnsCache];
    }
}

- (NSArray *)getAllModels {
    @synchronized(self) {
        return [_cacheDic getAllModels];
    }
}

- (NSArray *)getAllModelsFromDB {
    @synchronized(self) {
        return [_dbManager queryAllDomainInfoWithIpArray:YES containsExpiredIp:YES hasDbOpen:NO];
    }
}

- (void)addMemoryCache:(NSString *)url model:(WBDNSDomainModel *)model {
    @synchronized(self) {
        if (model == nil) {
            NSLog(@"ERROR:%s:%d model is nil.", __func__, __LINE__);
            return;
        }
        if (model.ipModelArray == nil || model.ipModelArray.count == 0) {
            NSLog(@"ERROR:%s:%d model.ipModelArray is nil.", __func__, __LINE__);
            return;
        }
        
        for (WBDNSIpModel *ipModel in model.ipModelArray) {
            if (ipModel == nil) {
                NSLog(@"WARNING:%s an ipModel in model.ipModelArray is nil.", __func__);
                return;
            }
        }
        
        [_cacheDic addModel:model keyUrl:url];
        return;
    }
}

//本函数只用于更新从本地dns获取的ip, 从httpserver 获取的ip 不应该用此函数更新。
- (void)updateMemoryCache:(NSString *)domain model:(WBDNSDomainModel *)model
{
    @synchronized(self) {
        if (model == nil) {
            NSLog(@"ERROR:%s:%d model is nil.", __func__, __LINE__);
            return;
        }
        if (model.ipModelArray == nil || model.ipModelArray.count == 0) {
            NSLog(@"ERROR:%s:%d model.ipModelArray is nil.", __func__, __LINE__);
            return;
        }
        
        [_cacheDic updateModel:model keyUrl:domain];
        return;
    }
}

- (WBDNSDomainModel *)getDnsCache:(NSString *)sp url:(NSString *)url {
    //这个锁是防止 多个线程重复的 从数据库取数据插入缓存。
    @synchronized(self) {
        WBDNSDomainModel *model = [_cacheDic getModelByKeyUrl:url];
        NSString* source = @"cache";
        if (model == nil) {
            model = [_dbManager queryDomainInfoWithIPArray:url sp:sp containsExpiredIp:NO hasDbOpen:NO];
            source = @"database";
        }
        
        if (model != nil) {
            if ([WBDNSTools isDomainModelExpired:model expireDuration:[WBDNSConfigManager sharedInstance].config.refreshDomainIpInterval]) {
                NSLog(@"INFO:%s:%d model(domain:%@) from %@ is expired.", __func__, __LINE__, model.domain, source);
                model = nil;
            } else if (model.ipModelArray == nil || model.ipModelArray.count == 0) {
                NSLog(@"INFO:%s:%d model(domain:%@) from %@ all ip are expired.", __func__, __LINE__, model.domain, source);
                model = nil;
            } else if(![model.sp isEqualToString:sp]) {
                //只是一个容错判断,一般不会走到这里。
                NSLog(@"INFO:%s:%d model(domain:%@) from %@, sp is not current sp.", __func__, __LINE__, model.domain, source);
                model = nil;
            } else {
                //把数据库里的有效model 存到缓存里。
                if ([source isEqualToString:@"database"]) {
                    [self addMemoryCache:url model:model];
                }
            }
        }
        return model;
    }
}

- (WBDNSDomainModel *)insertDnsCache:(WBDNSHttpDnsPack *) dnsPack {
    @synchronized(self) {
        WBDNSDomainModel* model = [[WBDNSDomainModel alloc]init];
        model.domain = dnsPack.domain;
        model.sp = dnsPack.localhostSp;
        
        model.time = [[WBDNSTools sharedInstance] stringFromDate:[NSDate date]];
        
        int t = 120;
        for (WBDNSIP* tempIp in dnsPack.dns) {
            WBDNSIpModel* ipModel = [[WBDNSIpModel alloc]init];
            ipModel.d_id = -1;//需要在后面刷新:updateDomainModelWithIpArray
            ipModel.ip = tempIp.ip;
            ipModel.ttl = tempIp.ttl;
            ipModel.priority = tempIp.priority;
            ipModel.rtt = @"0";
            ipModel.port = 80;
            ipModel.sp = model.sp;
            ipModel.success_num = @"0";
            ipModel.err_num = @"0";
            ipModel.finally_success_time = [[WBDNSTools sharedInstance] stringFromDate:[NSDate dateWithTimeIntervalSince1970:0]];
            ipModel.finally_fail_time = [[WBDNSTools sharedInstance] stringFromDate:[NSDate dateWithTimeIntervalSince1970:0]];
            ipModel.finally_update_time = [[WBDNSTools sharedInstance] stringFromDate:[NSDate date]];
            [model.ipModelArray addObject:ipModel];
            
            if ([ipModel.ttl intValue] < t) {
                t = [ipModel.ttl intValue];
            }
        }
        
        model.ttl = [NSString stringWithFormat:@"%i", t];
        
        if (model.ipModelArray != nil && model.ipModelArray.count > 0) {
            model = [_dbManager updateDomainModelWithIpArray:model];
            [self addMemoryCache:model.domain model:model];
        }
        return model;
    }
}

- (WBDNSIpModel *)findIpModel:(WBDNSIpModel *)ipModel inArray:(NSArray *)array {
    for (WBDNSIpModel* ip in array) {
        if ([ip.sp isEqualToString:ipModel.sp] && [ip.ip isEqualToString:ipModel.ip]) {
            return ip;
        }
    }
    return nil;
}

- (void)updateCachedIpModelSpeedInfo:(WBDNSDomainModel *)model {
    @synchronized(self) {
        if(![model.sp isEqualToString:[WBDNSNetworkManager sharedInstance].currentSpTypeString]) {
            NSLog(@"INFO:%s:%d network switched,don't set speed test data to cache.", __func__, __LINE__);
            return;
        }
        
        WBDNSDomainModel* existModel = [_cacheDic getModelByKeyUrl:model.domain];
        if (existModel == nil) {
            return;
        }
        
        for (WBDNSIpModel* ipModel in model.ipModelArray) {
            WBDNSIpModel* existCachedIpModel = [self findIpModel:ipModel inArray:existModel.ipModelArray];
            if (existCachedIpModel) {
                NSDate* succDate = [[WBDNSTools sharedInstance]dateFromString:ipModel.finally_success_time];
                NSDate* failDate = [[WBDNSTools sharedInstance]dateFromString:ipModel.finally_fail_time];
                NSComparisonResult result = [succDate compare:failDate];
                if (result == NSOrderedDescending) {
                    existCachedIpModel.success_num =[NSString stringWithFormat:@"%d", [existCachedIpModel.success_num intValue] +1];
                    existCachedIpModel.finally_success_time = ipModel.finally_success_time;
                } else {
                    existCachedIpModel.err_num =[NSString stringWithFormat:@"%d", [existCachedIpModel.err_num intValue] +1];
                    existCachedIpModel.finally_fail_time = ipModel.finally_fail_time;
                }
                
                existCachedIpModel.rtt = ipModel.rtt;
            }
        }
    }
}

- (void)updateDBSavedIpModelSpeedInfo:(WBDNSDomainModel *)model {
    for (WBDNSIpModel* ipModel in model.ipModelArray) {
        
        WBDNSIpModel* existModel = [_dbManager queryIpModel:ipModel.ip sp:ipModel.sp domainId:model.id hasDbOpen:NO];
        if (existModel) {
            
            NSDate* succDate = [[WBDNSTools sharedInstance]dateFromString:ipModel.finally_success_time];
            NSDate* failDate = [[WBDNSTools sharedInstance]dateFromString:ipModel.finally_fail_time];
            NSComparisonResult result = [succDate compare:failDate];
            if (result == NSOrderedDescending) {
                existModel.success_num =[NSString stringWithFormat:@"%d", [existModel.success_num intValue] +1];
                existModel.finally_success_time = ipModel.finally_success_time;
            } else {
                existModel.err_num =[NSString stringWithFormat:@"%d", [existModel.err_num intValue] +1];
                existModel.finally_fail_time = ipModel.finally_fail_time;
            }
            
            existModel.rtt = ipModel.rtt;
            
            [_dbManager updateIpModel:existModel hasDbOpen:NO];
        }
    }
}

- (BOOL)updateIpModelSpeedInfoInCacheAndDB:(WBDNSDomainModel *)model {
    @synchronized(self) {
        if (model == nil || model.ipModelArray.count == 0) {
            NSLog(@"INFO:%s:%d  input params is nil.", __func__, __LINE__);
            return NO;
        }

        [self updateCachedIpModelSpeedInfo:model];
        
        [self updateDBSavedIpModelSpeedInfo:model];
        
        return YES;
    }
}

- (void)clear {
    @synchronized(self) {
        [_dbManager clear];
        [self clearMemoryCache];
    }
}

- (void)clearDB {
    @synchronized(self) {
        [_dbManager clear];
    }
}

- (void)clearMemoryCache {
    @synchronized(self) {
        [_cacheDic removeAllModels];
    }
}

@end


================================================
FILE: src/DNSCache/cache/WBDNSDBManager.h
================================================
//
//  DBManager.h
//  DNSCache
//
//  Created by Robert Yang on 15/7/28.
//  Copyright (c) 2015年 Weibo. All rights reserved.
//

#import <Foundation/Foundation.h>
#import <sqlite3.h>
#import "WBDNSModel.h"
@interface WBDNSDBManager : NSObject

- (WBDNSDomainModel*)queryDomainInfoWithIPArray:(NSString *)domain sp:(NSString *)sp containsExpiredIp:(BOOL)containsExpiredIp hasDbOpen:(BOOL)hasDbOpen;

- (WBDNSDomainModel *)updateDomainModelWithIpArray:(WBDNSDomainModel *) model;

- (WBDNSIpModel *)queryIpModel:(NSString *)severIp sp:(NSString *)sp domainId:(int)d_id hasDbOpen:(BOOL)hasDbOpen;

- (NSArray *)queryAllDomainInfoWithIpArray:(BOOL)withIPArray containsExpiredIp:(BOOL)containsExpiredIp hasDbOpen:(BOOL)hasDbOpen;

- (BOOL)updateIpModel:(WBDNSIpModel *)model hasDbOpen:(BOOL)hasDbOpen;

- (void)clear;

@end


================================================
FILE: src/DNSCache/cache/WBDNSDBManager.m
================================================
//
//  DBManager.m
//  DNSCache
//
//  Created by Robert Yang on 15/7/28.
//  Copyright (c) 2015年 Weibo. All rights reserved.
//

#import "WBDNSDBManager.h"
#import "WBDNSTools.h"
#import "WBDNSConfig.h"
#import "WBDNSConfigManager.h"

#define WB_DNSCache_DB_Version 1
#define WB_DNSCache_DB_NAME @"dns_ip_info.db"

/**
 *  创建db_info table的Sql语句
 */
static NSString *WBDNS_CREATE_DB_INFO_TABLE_SQL= @"create table if not exists db_info (c_key text primary key ,c_value text)";


/**
 * domain表名称、列名定义
 */
static NSString *WBDNS_TABLE_NAME_DOMAIN = @"domain";
/**
 * domain 自增id
 */
static NSString *WBDNS_DOMAIN_COLUMN_ID = @"id";
/**
 * 域名
 */
static NSString *WBDNS_DOMAIN_COLUMN_DOMAIN = @"domain";
/**
 * 运营商
 */
static NSString *WBDNS_DOMAIN_COLUMN_SP = @"sp";
/**
 * 域名过期时间
 */
static NSString *WBDNS_DOMAIN_COLUMN_TTL = @"ttl";
/**
 * 最后查询时间
 */
static NSString *WBDNS_DOMAIN_COLUMN_TIME = @"time";

/**
 *  创建domain table的Sql语句
 */
static NSString *WBDNS_CREATE_DOMAIN_TABLE_SQL= @"create table if not exists domain (id INTEGER PRIMARY KEY, domain TEXT, sp TEXT, ttl TEXT, time TEXT)";

/**
 * ip表名称、列名定义
 */
static NSString *WBDNS_TABLE_NAME_IP = @"ip";
/**
 * ip 自增id
 */
static NSString *WBDNS_IP_COLUMN_ID = @"id";
/**
 * domain 关联id
 */
static NSString *WBDNS_IP_COLUMN_DOMAIN_ID = @"d_id";
/**
 * 服务器 ip地址
 */
static NSString *WBDNS_IP_COLUMN_IP = @"ip";
/**
 * ip服务器对应的端口
 */
static NSString *WBDNS_IP_COLUMN_PORT = @"port";
/**
 * ip服务器对应的sp运营商
 */
static NSString *WBDNS_IP_COLUMN_SP = @"sp";
/**
 * ip服务器对应域名过期时间
 */
static NSString *WBDNS_IP_COLUMN_TTL = @"ttl";
/**
 * ip服务器优先级-排序算法策略使用
 */
static NSString *WBDNS_IP_COLUMN_PRIORITY = @"priority";
/**
 *  ip服务器访问延时时间(可用ping或http发送空包实现)。单位ms
 */
static NSString *WBDNS_IP_COLUMN_RTT = @"rtt";
/**
 * 最后测速下行速度值
 */
static NSString *WBDNS_IP_COLUMN_FINALLY_SPEED = @"finally_speed";
/**
 * ip服务器链接产生的成功数
 */
static NSString *WBDNS_IP_COLUMN_SUCCESS_NUM = @"success_num";
/**
 * ip服务器链接产生的错误数
 */
static NSString *WBDNS_IP_COLUMN_ERR_NUM = @"err_num";
/**
 * ip服务器最后成功链接时间
 */
static NSString *WBDNS_IP_COLUMN_FINALLY_SUCCESS_TIME = @"finally_success_time";

/**
 * ip服务器最后失败链接时间
 */
static NSString *WBDNS_IP_COLUMN_FINALLY_FAIL_TIME = @"finally_fail_time";

/**
 *  此IP记录从服务器的更新时间
 */
static NSString *WBDNS_IP_COLUMN_FINALLY_UPDATE_TIME = @"finally_update_time";
/**
 *  创建ip table的Sql语句
 */
static NSString *WBDNS_CREATE_IP_TABLE_SQL= @"create table if not exists ip (id INTEGER PRIMARY KEY, d_id INTEGER, ip TEXT, port INTEGER, sp TEXT, ttl TEXT, priority TEXT, rtt TEXT, success_num TEXT, err_num TEXT, finally_success_time TEXT, finally_fail_time TEXT, finally_update_time TEXT)";

@implementation WBDNSDBManager
{
    sqlite3* db;
}

- (id)init {
    if(self = [super init]) {
        //检查是否存在数据库文件
        if (![self isExistDB]) {
            //不存在,则创建
            [self createDB];
            [self updateDB:0];
        }else {
            //若存在,检测数据库版本,则进行升级,
            char* info=NULL;
            [self getDBInfoValueWithKey:"db_version" value:&info];
            if(info == NULL) {
                NSLog(@"ERROR: DB is invalid, no db_version found.");
                return self;
            }
            int existDBVersion= atoi(info);
            free (info);
            
            [self updateDB:existDBVersion];
        }
    }
    return self;
}

- (void)updateDB:(int)currentDBVersion
{
    @synchronized(self) {
        //升级数据库。若第一次创建,则从0开始升级。顺序升级,因此不可以有break
        switch (currentDBVersion) {
            case 0:
                //第一次,新建并初始化各表
                [self createTable:WBDNS_CREATE_DB_INFO_TABLE_SQL];
                [self setDBInfoValueWithKey:"db_version" value:"1"];
                [self createTable:WBDNS_CREATE_DOMAIN_TABLE_SQL];
                [self createTable:WBDNS_CREATE_IP_TABLE_SQL];
            default:
                break;
        }
    }
}

- (BOOL)createDB {
    @synchronized(self) {
        int ret = sqlite3_open([[self getFilePath] UTF8String], &db);//打开数据库,数据库不存在则创建
        if (SQLITE_OK == ret) {//创建成功
            sqlite3_close(db);//关闭
            return YES;
        } else {
            NSLog(@"ERROR:%s:%d failed reason:%s.",__FUNCTION__,__LINE__,sqlite3_errmsg(db));
            return NO;//创建失败
        }
    }
}

- (NSString *)getFilePath {
    NSArray *documentsPaths=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomainMask  , YES);
    NSString *databaseFilePath=[[documentsPaths objectAtIndex:0] stringByAppendingPathComponent:WB_DNSCache_DB_NAME];
    return databaseFilePath ;
}

- (BOOL)isExistDB {
    NSFileManager* fm = [[NSFileManager alloc] init];
    return [fm fileExistsAtPath:[self getFilePath]];
}

- (BOOL)createTable:(NSString *)creteSql {
    return [self execSql:creteSql hasDbOpen:NO];
}


- (BOOL)execSql:(NSString *)createSql hasDbOpen:(BOOL)hasDbOpen {
    @synchronized(self) {
        BOOL ret = YES;
        char* err;
        const char* sql = [createSql UTF8String];//创建表语句
        if (sql==NULL) {
            NSLog(@"ERROR:%s:%d createSql is nil.",__FUNCTION__,__LINE__);
            return NO;
        }
        if (!hasDbOpen) {
            if (SQLITE_OK != sqlite3_open([[self getFilePath] UTF8String], &db)){
                NSLog(@"ERROR:%s:%d DB open failed reason:%s.",__FUNCTION__,__LINE__,sqlite3_errmsg(db));
                return NO;
            }
        }
        
        if (SQLITE_OK == sqlite3_exec(db, sql, NULL, NULL, &err)) {//执行创建表语句成功ß
            ret = YES;
        } else {
            //创建表失败
            NSLog(@"ERROR:%s:%d sql execute failed reason:%s.",__FUNCTION__,__LINE__,sqlite3_errmsg(db));
            ret = NO;
        }
        
        if (!hasDbOpen) {
            sqlite3_close(db);
        }
        return  ret;
    }
}

- (void)clear {
    @synchronized(self) {
        if (SQLITE_OK != sqlite3_open([[self getFilePath] UTF8String], &db)) {//打开数据库
            NSLog(@"ERROR:%s:%d db open failed reason:%s.",__FUNCTION__,__LINE__,sqlite3_errmsg(db));
            return;
        }
        
        NSString* sql = [NSString stringWithFormat:@"delete from %@", WBDNS_TABLE_NAME_DOMAIN];
        sqlite3_stmt* stmt;//
        int result =sqlite3_prepare_v2(db, [sql UTF8String], -1, &stmt, nil);
        if (result==SQLITE_OK) {
            if (SQLITE_DONE != sqlite3_step(stmt)) {
                NSLog(@"ERROR:%s:%d sql execute failed reason:%s.",__FUNCTION__,__LINE__,sqlite3_errmsg(db));
            }
            
            sqlite3_finalize(stmt);
        } else {
            NSLog(@"ERROR:%s:%d sql prepared failed reason:%s.",__FUNCTION__,__LINE__,sqlite3_errmsg(db));
        }
        
        sql = [NSString stringWithFormat:@"delete from %@", WBDNS_TABLE_NAME_IP];
        result =sqlite3_prepare_v2(db, [sql UTF8String], -1, &stmt, nil);
        if (result==SQLITE_OK) {
            if (SQLITE_DONE != sqlite3_step(stmt)) {
                NSLog(@"ERROR:%s:%d sql execute failed reason:%s.",__FUNCTION__,__LINE__,sqlite3_errmsg(db));
            }
            
            sqlite3_finalize(stmt);
        } else {
            NSLog(@"ERROR:%s:%d sql prepared failed reason:%s.",__FUNCTION__,__LINE__,sqlite3_errmsg(db));
        }
        
        sqlite3_close(db);
    }
}

#pragma mark-  table db_info manage
- (void)getDBInfoValueWithKey:(const char *)key value:(char **)value {
    @synchronized(self) {
        if (SQLITE_OK != sqlite3_open([[self getFilePath] UTF8String] , &db)) {
            NSLog(@"%s:%d db open error..reason:%s.",__FUNCTION__,__LINE__,sqlite3_errmsg(db));
            return ;
        }
        const char* sql = "select * from db_info where c_key =?";//查询语句
        sqlite3_stmt* stmt;
        
        int error = sqlite3_prepare_v2(db, sql, -1, &stmt, 0);
        if (error==SQLITE_OK) {//准备
            sqlite3_bind_text(stmt, 1,key, -1, NULL);
        } else {
            NSLog(@"ERROR%s:%d query error.. %d reason:%s\n",__FUNCTION__,__LINE__,error, sqlite3_errmsg(db));
            sqlite3_finalize(stmt);
            sqlite3_close(db);
            return;
        }
        
        if( SQLITE_ROW == sqlite3_step(stmt) ) {//执行
            char* v= (char*)sqlite3_column_text(stmt, 1);
            *value = strdup(v);
        }
        sqlite3_finalize(stmt);
        sqlite3_close(db);
    }
}

- (BOOL)setDBInfoValueWithKey:(const char*)key value:(const char*)value {
    
    @synchronized(self) {
        char* info=NULL;
        [self getDBInfoValueWithKey:key value:&info];
        if (info!= NULL) {
            //存在,则更新
            [self updateDBInfoValueWithKey:key value:value];
        }else {
            //不存在,插入
            [self insertDBInfoValueWithKey:key value:value];
        }
        free(info);
        return YES;
    }
}

- (BOOL)insertDBInfoValueWithKey:(const char*)key value:(const char*)value {
    @synchronized(self) {
        int ret = 0;
        if (SQLITE_OK != sqlite3_open([[self getFilePath] UTF8String], &db)) {
            return NO;
        }
        const char* sql = "insert into db_info(c_key,c_value) values(?,?);";
        sqlite3_stmt* stmt;//
        int result =sqlite3_prepare_v2(db, sql, -1, &stmt, nil);
        
        if (result==SQLITE_OK) {//准备语句
            sqlite3_bind_text(stmt, 1, key, -1, NULL);//绑定参数
            sqlite3_bind_text(stmt, 2, value, -1, NULL);
        } else {
            NSLog(@"ERROR:%s\n",sqlite3_errmsg(db));
            sqlite3_finalize(stmt);
            sqlite3_close(db);
            return NO;
        }
        if (SQLITE_DONE == (ret = sqlite3_step(stmt))) {//执行查询
            sqlite3_finalize(stmt);
            sqlite3_close(db);
            return YES;
        } else {
            sqlite3_finalize(stmt);
            sqlite3_close(db);
            return NO;
        }
    }
}


- (BOOL)updateDBInfoValueWithKey:(const char*)key value:(const char*)value {
    @synchronized(self) {
        int ret = 0;
        if (SQLITE_OK != sqlite3_open([[self getFilePath] UTF8String], &db)) {
            NSLog(@"ERROR%s:%d query db open error. reason:%s\n",__FUNCTION__,__LINE__, sqlite3_errmsg(db));
            return NO;
        }
        const char* sql = "update db_info set c_value = ? where c_key = ?;";
        sqlite3_stmt* stmt;//
        int result =sqlite3_prepare_v2(db, sql, -1, &stmt, nil);
        if (result==SQLITE_OK) {//准备语句
            sqlite3_bind_text(stmt, 1, value, -1, NULL);
            sqlite3_bind_text(stmt, 2, key, -1, NULL);
        } else {
            NSLog(@"ERROR:%s\n",sqlite3_errmsg(db));
            sqlite3_finalize(stmt);
            sqlite3_close(db);
            return NO;
        }
        ret = sqlite3_step(stmt);
        if (SQLITE_DONE ==ret ) {//执行查询
            sqlite3_finalize(stmt);
            sqlite3_close(db);
            return YES;
        } else {
            NSLog(@"ERROR:%s\n",sqlite3_errmsg(db));
            sqlite3_finalize(stmt);
            sqlite3_close(db);
            return NO;
        }
    }
}

#pragma mark- Domain db manage



- (WBDNSDomainModel *)queryDomainInfo:(NSString *)domain sp:(NSString *)sp withIpArray:(BOOL)withIPArray containsExpiredIp:(BOOL)containsExpiredIp hasDbOpen:(BOOL)hasDbOpen {
    if (domain == nil) {
        NSLog(@"ERROR %s:%d domain is nil.", __func__,__LINE__);
    }
    
    if (sp == nil) {
        NSLog(@"ERROR %s:%d sp is nil.", __func__,__LINE__);
    }
    @synchronized(self) {
        NSMutableArray* result = [[NSMutableArray alloc]init];
        if (!hasDbOpen) {
            if (SQLITE_OK != sqlite3_open([[self getFilePath] UTF8String], &db)) {
                NSLog(@"ERROR%s:%d db open error. reason:%s\n",__FUNCTION__,__LINE__, sqlite3_errmsg(db));
                return nil;
            }
        }
        
        NSString *sql = [NSString stringWithFormat:@"SELECT * FROM %@ WHERE %@ = ? AND %@ = ?;", WBDNS_TABLE_NAME_DOMAIN, WBDNS_DOMAIN_COLUMN_DOMAIN, WBDNS_DOMAIN_COLUMN_SP];
        sqlite3_stmt *stmt;
        if (sqlite3_prepare_v2(db, [sql cStringUsingEncoding:NSUTF8StringEncoding], -1, &stmt, nil) ==SQLITE_OK) {//准备
            sqlite3_bind_text(stmt, 1,[domain UTF8String], -1, NULL);
            sqlite3_bind_text(stmt, 2,[sp UTF8String], -1, NULL);
        } else {
            NSLog(@"ERROR%s:%d statments prepared fail. reason:%s\n",__FUNCTION__,__LINE__, sqlite3_errmsg(db));
            sqlite3_finalize(stmt);
            if (!hasDbOpen) {
                sqlite3_close(db);
            }
            return nil;
        }
        
        while (SQLITE_ROW == sqlite3_step(stmt)) {
            WBDNSDomainModel* model = [[WBDNSDomainModel alloc]init];
            model.id = sqlite3_column_int(stmt, 0);
            if (sqlite3_column_text(stmt, 1) != NULL) {
                model.domain = [NSString stringWithUTF8String:(char*)sqlite3_column_text(stmt, 1)];
            }
            if (sqlite3_column_text(stmt, 2) != NULL) {
                model.sp = [NSString stringWithUTF8String:(char*)sqlite3_column_text(stmt, 2)];
            }
            if (sqlite3_column_text(stmt, 3) != NULL) {
                model.ttl = [NSString stringWithUTF8String:(char*)sqlite3_column_text(stmt, 3)];
            }
            if (sqlite3_column_text(stmt, 4) != NULL) {
                model.time = [NSString stringWithUTF8String:(char*)sqlite3_column_text(stmt, 4)];
            }
            
            if (withIPArray) {
                model.ipModelArray = [self queryIpModelArray:model containsExpiredIp:containsExpiredIp  hasDbOpen:YES];
            }
            
            [result addObject:model];
        }
        sqlite3_finalize(stmt);
        if (!hasDbOpen) {
            sqlite3_close(db);
        }
        
        if (result.count > 1) {
            NSLog(@"ERROR%s:%d more than one domain Model in database.\n",__FUNCTION__,__LINE__);
        }
        
        return  [result firstObject];
    }
}


- (NSArray *)queryAllDomainInfoWithIpArray:(BOOL)withIPArray containsExpiredIp:(BOOL)containsExpiredIp hasDbOpen:(BOOL)hasDbOpen {
   
    @synchronized(self) {
        NSMutableArray* result = [[NSMutableArray alloc]init];
        if (!hasDbOpen) {
            if (SQLITE_OK != sqlite3_open([[self getFilePath] UTF8String], &db)) {
                NSLog(@"ERROR%s:%d db open error. reason:%s\n",__FUNCTION__,__LINE__, sqlite3_errmsg(db));
                return nil;
            }
        }
        
        NSString *sql = [NSString stringWithFormat:@"SELECT * FROM %@", WBDNS_TABLE_NAME_DOMAIN];
        sqlite3_stmt *stmt;
        if (sqlite3_prepare_v2(db, [sql cStringUsingEncoding:NSUTF8StringEncoding], -1, &stmt, nil) ==SQLITE_OK) {//准备
           
        } else {
            NSLog(@"ERROR%s:%d statments prepared fail. reason:%s\n",__FUNCTION__,__LINE__, sqlite3_errmsg(db));
            sqlite3_finalize(stmt);
            if (!hasDbOpen) {
                sqlite3_close(db);
            }
            return nil;
        }
        
        while (SQLITE_ROW == sqlite3_step(stmt)) {
            WBDNSDomainModel* model = [[WBDNSDomainModel alloc]init];
            model.id = sqlite3_column_int(stmt, 0);
            if (sqlite3_column_text(stmt, 1) != NULL) {
                model.domain = [NSString stringWithUTF8String:(char*)sqlite3_column_text(stmt, 1)];
            }
            if (sqlite3_column_text(stmt, 2) != NULL) {
                model.sp = [NSString stringWithUTF8String:(char*)sqlite3_column_text(stmt, 2)];
            }
            if (sqlite3_column_text(stmt, 3) != NULL) {
                model.ttl = [NSString stringWithUTF8String:(char*)sqlite3_column_text(stmt, 3)];
            }
            if (sqlite3_column_text(stmt, 4) != NULL) {
                model.time = [NSString stringWithUTF8String:(char*)sqlite3_column_text(stmt, 4)];
            }
            
            if (withIPArray) {
                model.ipModelArray = [self queryIpModelArray:model containsExpiredIp:containsExpiredIp  hasDbOpen:YES];
            }
            
            [result addObject:model];
        }
        sqlite3_finalize(stmt);
        if (!hasDbOpen) {
            sqlite3_close(db);
        }
        
        return result;
    }
}

- (WBDNSDomainModel *)queryDomainInfoWithIPArray:(NSString *)domain sp:(NSString *)sp containsExpiredIp:(BOOL)containsExpiredIp hasDbOpen:(BOOL)hasDbOpen {
    return [self queryDomainInfo:domain sp:sp withIpArray:YES containsExpiredIp:containsExpiredIp hasDbOpen:hasDbOpen];
}

- (WBDNSDomainModel *)queryDomainInfoWithoutIPArray:(NSString *)domain sp:(NSString *)sp hasDbOpen:(BOOL)hasDbOpen {
    return [self queryDomainInfo:domain sp:sp withIpArray:NO containsExpiredIp:NO hasDbOpen:hasDbOpen];
}

- (void)deleteDomainInfo:(WBDNSDomainModel *)model  hasDbOpen:(BOOL)hasDbOpen {
    @synchronized(self) {
        if (!hasDbOpen) {
            if (SQLITE_OK != sqlite3_open([[self getFilePath] UTF8String], &db)) {
                NSLog(@"ERROR %s:%d reason:%s", __func__,__LINE__, sqlite3_errmsg(db));
                return;
            }
        }
        
        
        NSString* sql = [NSString stringWithFormat:@"DELETE FROM %@ WHERE %@ = ?", WBDNS_TABLE_NAME_DOMAIN, WBDNS_DOMAIN_COLUMN_ID];
        sqlite3_stmt* stmt;//
        int result =sqlite3_prepare_v2(db, [sql UTF8String], -1, &stmt, nil);
        
        if (result==SQLITE_OK) {//准备语句
            sqlite3_bind_int(stmt, 1, model.id);
        } else {
            NSLog(@"ERROR %s:%d reason:%s", __func__,__LINE__, sqlite3_errmsg(db));
            sqlite3_finalize(stmt);
            if (!hasDbOpen) {
                sqlite3_close(db);
            }
            return;
        }
        if (SQLITE_DONE != sqlite3_step(stmt)) {
            NSLog(@"ERROR %s:%d reason:%s", __func__,__LINE__, sqlite3_errmsg(db));
        }
        sqlite3_finalize(stmt);
        if (!hasDbOpen) {
            sqlite3_close(db);
        }
    }
}

- (void)deleteDomainInfoArray:(NSArray *)domainInfoArr hasDbOpen:(BOOL) hasDbOpen {
    @synchronized(self) {
        for (WBDNSDomainModel* model in domainInfoArr) {
            if ([model isKindOfClass:[WBDNSDomainModel class]]) {
                [self deleteDomainInfo:model hasDbOpen:hasDbOpen];
            }
            else {
                NSLog(@"deleteDomainInfoArray: invaild model.\n");
            }
        }
    }
}

- (WBDNSDomainModel *)updateDomainModel:(WBDNSDomainModel *)model byDomain:(NSString *)domain sp:(NSString *)sp hasDbOpen:(BOOL)hasDbOpen {
    @synchronized(self) {
        if (!hasDbOpen) {
            if (SQLITE_OK != sqlite3_open([[self getFilePath] UTF8String], &db)) {
                NSLog(@"ERROR %s:%d Open database fail\n", __func__,__LINE__);
                return nil;
            }
        }
        
        WBDNSDomainModel* existModel = [self queryDomainInfoWithoutIPArray:domain sp:sp hasDbOpen:YES];
        sqlite3_stmt* stmt;
        if (existModel != nil) {
            NSString* sql = [NSString stringWithFormat:@"UPDATE %@ SET %@ = ? , %@ = ?, %@ = ?, %@ =? where %@ = ? AND %@ = ?",WBDNS_TABLE_NAME_DOMAIN, WBDNS_DOMAIN_COLUMN_DOMAIN, WBDNS_DOMAIN_COLUMN_SP,  WBDNS_DOMAIN_COLUMN_TTL, WBDNS_DOMAIN_COLUMN_TIME,WBDNS_DOMAIN_COLUMN_DOMAIN, WBDNS_DOMAIN_COLUMN_SP];
            sqlite3_stmt* stmt;//
            int result =sqlite3_prepare_v2(db, [sql UTF8String], -1, &stmt, nil);
            if (result==SQLITE_OK) {//准备语句
                sqlite3_bind_text(stmt, 1, [model.domain UTF8String], -1, NULL);
                sqlite3_bind_text(stmt, 2, [model.sp UTF8String], -1, NULL);
                sqlite3_bind_text(stmt, 3, [model.ttl UTF8String], -1, NULL);
                sqlite3_bind_text(stmt, 4, [model.time UTF8String], -1, NULL);
                sqlite3_bind_text(stmt, 5, [model.domain UTF8String], -1, NULL);
                sqlite3_bind_text(stmt, 6, [model.sp UTF8String], -1, NULL);
            } else {
                if (!hasDbOpen) {
                    sqlite3_close(db);
                }
                NSLog(@"ERROR %s:%d reason %s", __func__,__LINE__, sqlite3_errmsg(db));
                return nil;
            }
            if (SQLITE_DONE != sqlite3_step(stmt)) {//执行查询
                NSLog(@"ERROR %s:%d reason %s", __func__,__LINE__, sqlite3_errmsg(db));
                sqlite3_finalize(stmt);
                if (!hasDbOpen) {
                    sqlite3_close(db);
                }
            }
            
            sqlite3_finalize(stmt);
            if (!hasDbOpen) {
                sqlite3_close(db);
            }
        } else {
            
            NSString* sql = [NSString stringWithFormat:@"insert into %@ (%@,%@,%@,%@) values (?, ?, ?, ?)", WBDNS_TABLE_NAME_DOMAIN, WBDNS_DOMAIN_COLUMN_DOMAIN, WBDNS_DOMAIN_COLUMN_SP, WBDNS_DOMAIN_COLUMN_TTL, WBDNS_DOMAIN_COLUMN_TIME];
            
            int result =sqlite3_prepare_v2(db, [sql UTF8String], -1, &stmt, nil);
            if (result==SQLITE_OK) {//准备语句
                sqlite3_bind_text(stmt, 1, [model.domain UTF8String], -1, NULL);
                sqlite3_bind_text(stmt, 2, [model.sp UTF8String], -1, NULL);
                sqlite3_bind_text(stmt, 3, [model.ttl UTF8String], -1, NULL);
                sqlite3_bind_text(stmt, 4, [model.time UTF8String], -1, NULL);
            } else {
                NSLog(@"ERROR %s:%d reason:%s", __func__,__LINE__, sqlite3_errmsg(db));
                if (!hasDbOpen) {
                    sqlite3_close(db);
                }
                return nil;
            }
            
            if (SQLITE_DONE != sqlite3_step(stmt)) {//执行查询
                NSLog(@"ERROR %s:%d reason:%s", __func__,__LINE__, sqlite3_errmsg(db));
                sqlite3_finalize(stmt);
                if (!hasDbOpen) {
                    sqlite3_close(db);
                }
                return nil;
            }
            
            sqlite3_finalize(stmt);
        }
        
        WBDNSDomainModel* newModel = [self queryDomainInfoWithoutIPArray:domain sp:sp hasDbOpen:YES];;
        if (!hasDbOpen) {
            sqlite3_close(db);
        }
        return newModel;
    }
}

- (WBDNSDomainModel *)updateDomainModelWithIpArray:(WBDNSDomainModel *)model {
    @synchronized(self) {
        if (SQLITE_OK != sqlite3_open([[self getFilePath] UTF8String], &db)) {//打开数据库
            NSLog(@"ERROR %s:%d reason:%s", __func__,__LINE__, sqlite3_errmsg(db));
            return nil;
        }
        
        WBDNSDomainModel* newModel = [self updateDomainModel:model byDomain:model.domain sp:model.sp
                                                   hasDbOpen:YES];
        //是否需要把旧的IP 置为过期。这个函数只会返回server下发的ip,即使不把老的IP置为过期,也会在几秒钟后过期。
        //数据库里服务器以前下发的IP 只会在缓存没数据时读取,基本上都是程序启动时,一般情况下 都已经过期。
        NSMutableArray* tempIpArray = [[NSMutableArray alloc]init];
        for (int i = 0; i < model.ipModelArray.count; i++) {
            WBDNSIpModel* tempIp = model.ipModelArray[i];
            tempIp.d_id = newModel.id;
            WBDNSIpModel* ipModel = [self queryIpModel:tempIp.ip sp:model.sp domainId:newModel.id hasDbOpen:YES];
            if (ipModel == nil) {
                BOOL result = [self insertIpModel:tempIp hasDbOpen:YES];
                if (result == NO) {
                     NSLog(@"WARNING:%s:%d insert tempIp failed.", __func__,__LINE__);
                }
            } else {
                ipModel.d_id = tempIp.d_id;
                ipModel.port = tempIp.port;
                ipModel.sp = tempIp.sp;
                ipModel.ttl = tempIp.ttl;
                ipModel.priority = tempIp.priority;
                //重新请求ip 不更新rtt, 成功次数,失败此处,成功时间,和失败时间
                //ipModel.rtt = [[tempIp rtt] intValue] == 0 ? ipModel.rtt : tempIp.rtt;
                //ipModel.success_num = [NSString stringWithFormat:@"%i",[[tempIp success_num] intValue] + [[ipModel success_num]intValue]] ;
                //ipModel.err_num = [NSString stringWithFormat:@"%i",[[tempIp err_num] intValue] + [[ipModel err_num]intValue]] ;
                
                //ipModel.finally_success_time = tempIp.finally_success_time;
                //ipModel.finally_fail_time = tempIp.finally_fail_time;
                ipModel.finally_update_time = tempIp.finally_update_time;
                [self updateIpModel:ipModel hasDbOpen:YES];
            }
            
            if (ipModel == nil) {
                ipModel = tempIp;
            }
            [tempIpArray addObject:ipModel];
        }
        model.ipModelArray = tempIpArray;
        model.id = newModel.id;
        sqlite3_close(db);
        return model;
    }
}

#pragma mark- ip db manage

- (NSMutableArray *)queryIpModelArray:(WBDNSDomainModel *)domain containsExpiredIp:(BOOL)containsExpiredIp hasDbOpen:(BOOL)hasDbOpen {
    @synchronized(self) {
        if (domain.sp == nil) {
            NSLog(@"ERROR %s:%d domain.sp is nil.", __func__,__LINE__);
        }
        
        if (domain.id <= 0) {
            NSLog(@"ERROR %s:%d domain.id(%d) is invaild.", __func__,__LINE__, domain.id);
        }
        
        NSMutableArray* result = [[NSMutableArray alloc]init];
        
        if (!hasDbOpen) {
            if (SQLITE_OK != sqlite3_open([[self getFilePath] UTF8String], &db)){
                NSLog(@"ERROR %s:%d reason:%s", __func__,__LINE__, sqlite3_errmsg(db));
                return nil;
            }
        }
        
        
        NSString* sql = [NSString stringWithFormat:@"SELECT * FROM %@ WHERE %@ = ? AND %@ = ?;", WBDNS_TABLE_NAME_IP, WBDNS_IP_COLUMN_DOMAIN_ID, WBDNS_IP_COLUMN_SP];
        sqlite3_stmt* stmt;
        if (sqlite3_prepare_v2(db, [sql cStringUsingEncoding:NSUTF8StringEncoding], -1, &stmt, nil) ==SQLITE_OK) {//准备
            sqlite3_bind_int(stmt, 1, domain.id);
            sqlite3_bind_text(stmt, 2, [domain.sp UTF8String], -1, NULL);
        } else {
            NSLog(@"ERROR %s:%d reason:%s", __func__,__LINE__, sqlite3_errmsg(db));
            if (!hasDbOpen) {
                sqlite3_close(db);
            }
            return nil;
        }
        
        while (SQLITE_ROW == sqlite3_step(stmt)) {
            WBDNSIpModel* model = [[WBDNSIpModel alloc]init];
            model.id = sqlite3_column_int(stmt, 0);
            model.d_id = sqlite3_column_int(stmt, 1);
            if (sqlite3_column_text(stmt, 2) != NULL) {
                model.ip = [NSString stringWithUTF8String:(char*)sqlite3_column_text(stmt, 2)];
            }
            model.port = sqlite3_column_int(stmt, 3);
            if (sqlite3_column_text(stmt, 4) != NULL) {
                model.sp = [NSString stringWithUTF8String:(char*)sqlite3_column_text(stmt, 4)];
            }
            if (sqlite3_column_text(stmt, 5) != NULL) {
                model.ttl = [NSString stringWithUTF8String:(char*)sqlite3_column_text(stmt, 5)];
            }
            if (sqlite3_column_text(stmt, 6) != NULL) {
                model.priority = [NSString stringWithUTF8String:(char*)sqlite3_column_text(stmt, 6)];
            }
            if (sqlite3_column_text(stmt, 7) != NULL) {
                model.rtt = [NSString stringWithUTF8String:(char*)sqlite3_column_text(stmt, 7)];
            }
            if (sqlite3_column_text(stmt, 8) != NULL) {
                model.success_num = [NSString stringWithUTF8String:(char*)sqlite3_column_text(stmt, 8)];
            }
            if (sqlite3_column_text(stmt, 9) != NULL) {
                model.err_num = [NSString stringWithUTF8String:(char*)sqlite3_column_text(stmt, 9)];
            }
            if (sqlite3_column_text(stmt, 10) != NULL) {
                model.finally_success_time = [NSString stringWithUTF8String:(char*)sqlite3_column_text(stmt, 10)];
            }
            if (sqlite3_column_text(stmt, 11) != NULL) {
                model.finally_fail_time = [NSString stringWithUTF8String:(char*)sqlite3_column_text(stmt, 11)];
            }
            if (sqlite3_column_text(stmt, 12) != NULL) {
                model.finally_update_time = [NSString stringWithUTF8String:(char*)sqlite3_column_text(stmt, 12)];
            }
            
            if (containsExpiredIp) {
                [result addObject:model];
            } else {
                if (![WBDNSTools isIpRecordExpired:model expireDuration:[WBDNSConfigManager sharedInstance].config.refreshDomainIpInterval + 15]) {
                    [result addObject:model];
                }
            }
            
        }
        sqlite3_finalize(stmt);
        if (!hasDbOpen) {
            sqlite3_close(db);
        }
        return  result;
    }
}

- (WBDNSIpModel *)queryIpModel:(NSString *)severIp sp:(NSString *)sp domainId:(int)d_id hasDbOpen:(BOOL)hasDbOpen {
    @synchronized(self) {
        NSMutableArray* result = [[NSMutableArray alloc]init];
        if (!hasDbOpen) {
            if (SQLITE_OK != sqlite3_open([[self getFilePath] UTF8String], &db)){
                NSLog(@"ERROR %s:%d reason:%s", __func__,__LINE__, sqlite3_errmsg(db));
                return nil;
            }
        }
        
        NSString* sql = [NSString stringWithFormat:@"SELECT * FROM %@ WHERE %@ = ? AND %@ = ? AND %@ = ?;", WBDNS_TABLE_NAME_IP,WBDNS_IP_COLUMN_DOMAIN_ID, WBDNS_IP_COLUMN_IP, WBDNS_IP_COLUMN_SP];
        sqlite3_stmt* stmt;
        if (sqlite3_prepare_v2(db, [sql cStringUsingEncoding:NSUTF8StringEncoding], -1, &stmt, nil) ==SQLITE_OK) {//准备
            sqlite3_bind_int(stmt, 1, d_id);
            sqlite3_bind_text(stmt, 2,[severIp UTF8String], -1, NULL);
            sqlite3_bind_text(stmt, 3,[sp UTF8String], -1, NULL);
        } else {
            NSLog(@"ERROR %s:%d reason:%s", __func__,__LINE__, sqlite3_errmsg(db));
            if (!hasDbOpen) {
                sqlite3_close(db);
            }
            return nil;
        }
        
        while (SQLITE_ROW == sqlite3_step(stmt)) {
            WBDNSIpModel* model = [[WBDNSIpModel alloc]init];
            model.id = sqlite3_column_int(stmt, 0);
            model.d_id = sqlite3_column_int(stmt, 1);
            if (sqlite3_column_text(stmt, 2) != NULL) {
                model.ip = [NSString stringWithUTF8String:(char*)sqlite3_column_text(stmt, 2)];
            }
            model.port = sqlite3_column_int(stmt, 3);
            if (sqlite3_column_text(stmt, 4) != NULL) {
                model.sp = [NSString stringWithUTF8String:(char*)sqlite3_column_text(stmt, 4)];
            }
            if (sqlite3_column_text(stmt, 5) != NULL) {
                model.ttl = [NSString stringWithUTF8String:(char*)sqlite3_column_text(stmt, 5)];
            }
            if (sqlite3_column_text(stmt, 6) != NULL) {
                model.priority = [NSString stringWithUTF8String:(char*)sqlite3_column_text(stmt, 6)];
            }
            if (sqlite3_column_text(stmt, 7) != NULL) {
                model.rtt = [NSString stringWithUTF8String:(char*)sqlite3_column_text(stmt, 7)];
            }
            if (sqlite3_column_text(stmt, 8) != NULL) {
                model.success_num = [NSString stringWithUTF8String:(char*)sqlite3_column_text(stmt, 8)];
            }
            if (sqlite3_column_text(stmt, 9) != NULL) {
                model.err_num = [NSString stringWithUTF8String:(char*)sqlite3_column_text(stmt, 9)];
            }
            if (sqlite3_column_text(stmt, 10) != NULL) {
                model.finally_success_time = [NSString stringWithUTF8String:(char*)sqlite3_column_text(stmt, 10)];
            }
            if (sqlite3_column_text(stmt, 11) != NULL) {
                model.finally_fail_time = [NSString stringWithUTF8String:(char*)sqlite3_column_text(stmt, 11)];
            }
            if (sqlite3_column_text(stmt, 12) != NULL) {
                model.finally_update_time = [NSString stringWithUTF8String:(char*)sqlite3_column_text(stmt, 12)];
            }
            [result addObject:model];
        }
        sqlite3_finalize(stmt);
        
        if (result.count >= 1) {
            for (int i = 1; i< result.count; i++) {
                NSLog(@"ERROR:%s:%d more than one ip record for this ip(%@) sp (%@)and domain id(%d)",__func__,__LINE__, severIp,sp, d_id);
                [self deleteIpModel:result[i] hasDbOpen:YES];
            }
            if (!hasDbOpen) {
                sqlite3_close(db);
            }
            return  result[0];
        } else {
            if (!hasDbOpen) {
                sqlite3_close(db);
            }
            return nil;
        }
    }
}

- (BOOL)deleteIpModel:(WBDNSIpModel *)model hasDbOpen:(BOOL)hasDbOpen {
    @synchronized(self) {
        int ret = YES;
        if (!hasDbOpen) {
            if (SQLITE_OK != sqlite3_open([[self getFilePath] UTF8String], &db)) {//打开数据库
                NSLog(@"ERROR %s:%d reason:%s", __func__,__LINE__, sqlite3_errmsg(db));
                return NO;
            }
        }
        
        NSString* sql = [NSString stringWithFormat:@"delete from %@ where %@ = ?", WBDNS_TABLE_NAME_IP, WBDNS_IP_COLUMN_ID];
        sqlite3_stmt* stmt;//
        int result =sqlite3_prepare_v2(db, [sql UTF8String], -1, &stmt, nil);
        if (result==SQLITE_OK) {//准备语句
            sqlite3_bind_int(stmt, 1, model.id);
        } else {
            NSLog(@"ERROR %s:%d reason:%s", __func__,__LINE__, sqlite3_errmsg(db));
            if (!hasDbOpen) {
                sqlite3_close(db);
            }
            return NO;
        }
        if (SQLITE_DONE != sqlite3_step(stmt)) {//执行查询
            NSLog(@"ERROR %s:%d reason:%s", __func__,__LINE__, sqlite3_errmsg(db));
            ret =  NO;
        }
        sqlite3_finalize(stmt);
        if (!hasDbOpen) {
            sqlite3_close(db);
        }
        return  ret;
    }
}

- (BOOL)updateIpModel:(WBDNSIpModel *) model hasDbOpen:(BOOL)hasDbOpen {
    @synchronized(self) {
        BOOL ret = YES;
        if (model == nil) {
            NSLog(@"ERROR %s:model is nil", __func__);
            return NO;
        }
        if (!hasDbOpen) {
            if (SQLITE_OK != sqlite3_open([[self getFilePath] UTF8String], &db)) {//打开数据库
                NSLog(@"ERROR %s:%s", __func__, sqlite3_errmsg(db));
                return NO;
            }
        }
        NSString* sql = [NSString stringWithFormat:@"UPDATE %@ SET %@ = ? , %@ = ?, %@ = ?,%@ = ?,%@ = ?,%@ = ?,%@ = ?,%@ = ?, %@ =?, %@ =?, %@ =?, %@ =? where %@ = ?",WBDNS_TABLE_NAME_IP, WBDNS_IP_COLUMN_DOMAIN_ID, WBDNS_IP_COLUMN_IP, WBDNS_IP_COLUMN_PORT, WBDNS_IP_COLUMN_SP, WBDNS_IP_COLUMN_TTL, WBDNS_IP_COLUMN_PRIORITY, WBDNS_IP_COLUMN_RTT, WBDNS_IP_COLUMN_SUCCESS_NUM, WBDNS_IP_COLUMN_ERR_NUM, WBDNS_IP_COLUMN_FINALLY_SUCCESS_TIME,WBDNS_IP_COLUMN_FINALLY_FAIL_TIME, WBDNS_IP_COLUMN_FINALLY_UPDATE_TIME, WBDNS_IP_COLUMN_ID];
        sqlite3_stmt* stmt;//
        int result =sqlite3_prepare_v2(db, [sql UTF8String], -1, &stmt, nil);
        if (result==SQLITE_OK) {//准备语句
            sqlite3_bind_int(stmt, 1, model.d_id);
            sqlite3_bind_text(stmt, 2, [model.ip UTF8String], -1, NULL);
            sqlite3_bind_int(stmt, 3, model.port);
            sqlite3_bind_text(stmt, 4, [model.sp UTF8String], -1, NULL);
            sqlite3_bind_text(stmt, 5, [model.priority UTF8String], -1, NULL);
            sqlite3_bind_text(stmt, 6, [model.ttl UTF8String], -1, NULL);
            sqlite3_bind_text(stmt, 7, [model.rtt UTF8String], -1, NULL);
            sqlite3_bind_text(stmt, 8, [model.success_num UTF8String], -1, NULL);
            sqlite3_bind_text(stmt, 9, [model.err_num UTF8String], -1, NULL);
            sqlite3_bind_text(stmt, 10, [model.finally_success_time UTF8String], -1, NULL);
            sqlite3_bind_text(stmt, 11, [model.finally_fail_time UTF8String], -1, NULL);
            sqlite3_bind_text(stmt, 12, [model.finally_update_time UTF8String], -1, NULL);
            sqlite3_bind_int(stmt, 13, model.id);
        } else {
            NSLog(@"ERROR %s:%d reason:%s", __func__,__LINE__, sqlite3_errmsg(db));
            if (!hasDbOpen) {
                sqlite3_close(db);
            }
            return NO;
        }
        if (SQLITE_DONE != sqlite3_step(stmt)) {//执行查询
            NSLog(@"ERROR %s:%d reason:%s", __func__,__LINE__, sqlite3_errmsg(db));
            ret = NO;
        }
        sqlite3_finalize(stmt);
        if (!hasDbOpen) {
            sqlite3_close(db);
        }
        return ret;
    }
}

- (BOOL)insertIpModel:(WBDNSIpModel *) ipModel hasDbOpen:(BOOL)hasDbOpen {
    @synchronized(self) {
        BOOL ret = YES;
        
        if (ipModel == nil) {
            NSLog(@"ERROR %s:%d model is nil", __func__, __LINE__);
            return NO;
        }
        if (!hasDbOpen) {
            if (SQLITE_OK != sqlite3_open([[self getFilePath] UTF8String], &db)){//打开数据库
                NSLog(@"ERROR %s:%d reason:%s", __func__,__LINE__, sqlite3_errmsg(db));
                return NO;
            }
        }
        NSString* sql = [NSString stringWithFormat:@"insert into %@(%@,%@,%@,%@,%@,%@,%@,%@,%@,%@,%@,%@) values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);", WBDNS_TABLE_NAME_IP, WBDNS_IP_COLUMN_DOMAIN_ID, WBDNS_IP_COLUMN_IP, WBDNS_IP_COLUMN_PORT, WBDNS_IP_COLUMN_SP, WBDNS_IP_COLUMN_TTL, WBDNS_IP_COLUMN_PRIORITY, WBDNS_IP_COLUMN_RTT, WBDNS_IP_COLUMN_SUCCESS_NUM, WBDNS_IP_COLUMN_ERR_NUM, WBDNS_IP_COLUMN_FINALLY_SUCCESS_TIME, WBDNS_IP_COLUMN_FINALLY_FAIL_TIME, WBDNS_IP_COLUMN_FINALLY_UPDATE_TIME];
        sqlite3_stmt* stmt;
        int result =sqlite3_prepare_v2(db, [sql UTF8String], -1, &stmt, nil);
        if (result==SQLITE_OK) {//准备语句
            sqlite3_bind_int(stmt, 1, ipModel.d_id);
            sqlite3_bind_text(stmt, 2, [ipModel.ip UTF8String], -1, NULL);
            sqlite3_bind_int(stmt, 3, ipModel.port);
            sqlite3_bind_text(stmt, 4, [ipModel.sp UTF8String], -1, NULL);
            sqlite3_bind_text(stmt, 5, [ipModel.ttl UTF8String], -1, NULL);
            sqlite3_bind_text(stmt, 6, [ipModel.priority UTF8String], -1, NULL);
            sqlite3_bind_text(stmt, 7, [ipModel.rtt UTF8String], -1, NULL);
            sqlite3_bind_text(stmt, 8, [ipModel.success_num UTF8String], -1, NULL);
            sqlite3_bind_text(stmt, 9, [ipModel.err_num UTF8String], -1, NULL);
            sqlite3_bind_text(stmt, 10, [ipModel.finally_success_time UTF8String], -1, NULL);
            sqlite3_bind_text(stmt, 11, [ipModel.finally_fail_time UTF8String], -1, NULL);
            sqlite3_bind_text(stmt, 12, [ipModel.finally_update_time UTF8String], -1, NULL);
            
        } else {
            NSLog(@"ERROR %s:%d reason:%s", __func__,__LINE__, sqlite3_errmsg(db));
            if (!hasDbOpen) {
                sqlite3_close(db);
            }
            return NO;
        }
        
        if (SQLITE_DONE != sqlite3_step(stmt)) {//执行查询
            NSLog(@"ERROR %s:%d reason:%s", __func__,__LINE__, sqlite3_errmsg(db));;
            ret = NO;
        }
        sqlite3_finalize(stmt);
        if (!hasDbOpen) {
            sqlite3_close(db);
        }
        return ret;
    }

}

@end


================================================
FILE: src/DNSCache.xcodeproj/project.pbxproj
================================================
// !$*UTF8*$!
{
	archiveVersion = 1;
	classes = {
	};
	objectVersion = 46;
	objects = {

/* Begin PBXBuildFile section */
		9A34ECB21B687F6700AC4526 /* WBDNSCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 9A34ECB11B687F6700AC4526 /* WBDNSCache.m */; };
		9A77C2701B9585D400826070 /* WBDNSConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 9A77C26F1B9585D400826070 /* WBDNSConfig.m */; };
		9A8F50DF1B7C412D009451C9 /* WBDNSConfigManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 9A8F50DE1B7C412D009451C9 /* WBDNSConfigManager.m */; };
		9AA389901B678B7F0054EE87 /* WBDNSDBManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 9AA3898F1B678B7F0054EE87 /* WBDNSDBManager.m */; };
		9AAF51041B6F551E0073954E /* WBDNSCacheManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 9AAF51031B6F551E0073954E /* WBDNSCacheManager.m */; };
		9AAF51081B6F78440073954E /* WBDNSTools.m in Sources */ = {isa = PBXBuildFile; fileRef = 9AAF51071B6F78440073954E /* WBDNSTools.m */; };
		9AD4139A1B71EC7A004B2658 /* WBDNSReachability.m in Sources */ = {isa = PBXBuildFile; fileRef = 9AD413991B71EC7A004B2658 /* WBDNSReachability.m */; };
		9AD775561B708E1800904927 /* WBDNSHttpDnsManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 9AD775551B708E1800904927 /* WBDNSHttpDnsManager.m */; };
		9AD775641B70ABD400904927 /* WBDNSDomainModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 9AD7755C1B70ABD400904927 /* WBDNSDomainModel.m */; };
		9AD775671B70ABD400904927 /* WBDNSIpModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 9AD775621B70ABD400904927 /* WBDNSIpModel.m */; };
		9AD7756A1B70AC8300904927 /* WBDNSQueryManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 9AD775691B70AC8300904927 /* WBDNSQueryManager.m */; };
		9AD7756D1B70C34100904927 /* WBDNSNetworkManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 9AD7756C1B70C34100904927 /* WBDNSNetworkManager.m */; };
		9AD9C0691B8EE15B0027EFD5 /* WBDNSLogManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 9AD9C0681B8EE15B0027EFD5 /* WBDNSLogManager.m */; };
		9AD9C06C1B8EE1BE0027EFD5 /* WBDNSLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = 9AD9C06B1B8EE1BE0027EFD5 /* WBDNSLogger.m */; };
		9AD9C06F1B8EEF8E0027EFD5 /* WBDNSLogFile.m in Sources */ = {isa = PBXBuildFile; fileRef = 9AD9C06E1B8EEF8E0027EFD5 /* WBDNSLogFile.m */; };
		9AE0E6361B8AEEF300018625 /* WBDNSSortManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 9AE0E6351B8AEEF300018625 /* WBDNSSortManager.m */; };
		9AE96CE81B78857000AF20F9 /* WBDNSMemeryCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 9AE96CE71B78857000AF20F9 /* WBDNSMemeryCache.m */; };
		9AF00BBD1B84704B00B60534 /* WBDNSTCPSpeedTester.m in Sources */ = {isa = PBXBuildFile; fileRef = 9AF00BBC1B84704B00B60534 /* WBDNSTCPSpeedTester.m */; };
		9AF00BC01B84772F00B60534 /* WBDNSSpeedTestManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 9AF00BBF1B84772F00B60534 /* WBDNSSpeedTestManager.m */; };
		9AF42C301B7460560073EA07 /* WBDNSHttpDnsPack.m in Sources */ = {isa = PBXBuildFile; fileRef = 9AF42C2D1B7460560073EA07 /* WBDNSHttpDnsPack.m */; };
		9AF42C311B7460560073EA07 /* WBDNSIP.m in Sources */ = {isa = PBXBuildFile; fileRef = 9AF42C2F1B7460560073EA07 /* WBDNSIP.m */; };
		9AF5B0FF1B81A5F4006BB9B6 /* WBDNSWeakTimer.m in Sources */ = {isa = PBXBuildFile; fileRef = 9AF5B0FE1B81A5F4006BB9B6 /* WBDNSWeakTimer.m */; };
/* End PBXBuildFile section */

/* Begin PBXCopyFilesBuildPhase section */
		9AB30FF21B6745AC00D90E87 /* CopyFiles */ = {
			isa = PBXCopyFilesBuildPhase;
			buildActionMask = 2147483647;
			dstPath = "include/$(PRODUCT_NAME)";
			dstSubfolderSpec = 16;
			files = (
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
/* End PBXCopyFilesBuildPhase section */

/* Begin PBXFileReference section */
		9A34EC6E1B686BF200AC4526 /* libsqlite3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libsqlite3.dylib; path = usr/lib/libsqlite3.dylib; sourceTree = SDKROOT; };
		9A34ECB01B687F6700AC4526 /* WBDNSCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WBDNSCache.h; sourceTree = "<group>"; };
		9A34ECB11B687F6700AC4526 /* WBDNSCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WBDNSCache.m; sourceTree = "<group>"; };
		9A77C26E1B9585D400826070 /* WBDNSConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WBDNSConfig.h; path = DNSCache/Models/WBDNSConfig.h; sourceTree = "<group>"; };
		9A77C26F1B9585D400826070 /* WBDNSConfig.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WBDNSConfig.m; path = DNSCache/Models/WBDNSConfig.m; sourceTree = "<group>"; };
		9A8F50DD1B7C412D009451C9 /* WBDNSConfigManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WBDNSConfigManager.h; path = DNSCache/WBDNSConfigManager.h; sourceTree = "<group>"; };
		9A8F50DE1B7C412D009451C9 /* WBDNSConfigManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WBDNSConfigManager.m; path = DNSCache/WBDNSConfigManager.m; sourceTree = "<group>"; };
		9AA3898E1B678B7F0054EE87 /* WBDNSDBManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WBDNSDBManager.h; path = DNSCache/cache/WBDNSDBManager.h; sourceTree = "<group>"; };
		9AA3898F1B678B7F0054EE87 /* WBDNSDBManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WBDNSDBManager.m; path = DNSCache/cache/WBDNSDBManager.m; sourceTree = "<group>"; };
		9AAF51021B6F551E0073954E /* WBDNSCacheManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WBDNSCacheManager.h; path = DNSCache/cache/WBDNSCacheManager.h; sourceTree = "<group>"; };
		9AAF51031B6F551E0073954E /* WBDNSCacheManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WBDNSCacheManager.m; path = DNSCache/cache/WBDNSCacheManager.m; sourceTree = "<group>"; };
		9AAF51061B6F78440073954E /* WBDNSTools.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WBDNSTools.h; path = Tools/WBDNSTools.h; sourceTree = "<group>"; };
		9AAF51071B6F78440073954E /* WBDNSTools.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WBDNSTools.m; path = Tools/WBDNSTools.m; sourceTree = "<group>"; };
		9AB30FF41B6745AC00D90E87 /* libDNSCache.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libDNSCache.a; sourceTree = BUILT_PRODUCTS_DIR; };
		9AD413981B71EC7A004B2658 /* WBDNSReachability.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WBDNSReachability.h; path = DNSCache/WBDNSReachability.h; sourceTree = "<group>"; };
		9AD413991B71EC7A004B2658 /* WBDNSReachability.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WBDNSReachability.m; path = DNSCache/WBDNSReachability.m; sourceTree = "<group>"; };
		9AD775541B708E1800904927 /* WBDNSHttpDnsManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WBDNSHttpDnsManager.h; path = HttpDns/WBDNSHttpDnsManager.h; sourceTree = "<group>"; };
		9AD775551B708E1800904927 /* WBDNSHttpDnsManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WBDNSHttpDnsManager.m; path = HttpDns/WBDNSHttpDnsManager.m; sourceTree = "<group>"; };
		9AD7755B1B70ABD400904927 /* WBDNSDomainModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WBDNSDomainModel.h; path = Models/WBDNSDomainModel.h; sourceTree = "<group>"; };
		9AD7755C1B70ABD400904927 /* WBDNSDomainModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WBDNSDomainModel.m; path = Models/WBDNSDomainModel.m; sourceTree = "<group>"; };
		9AD775611B70ABD400904927 /* WBDNSIpModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WBDNSIpModel.h; path = Models/WBDNSIpModel.h; sourceTree = "<group>"; };
		9AD775621B70ABD400904927 /* WBDNSIpModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WBDNSIpModel.m; path = Models/WBDNSIpModel.m; sourceTree = "<group>"; };
		9AD775631B70ABD400904927 /* WBDNSModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WBDNSModel.h; path = Models/WBDNSModel.h; sourceTree = "<group>"; };
		9AD775681B70AC8300904927 /* WBDNSQueryManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WBDNSQueryManager.h; path = QueryManager/WBDNSQueryManager.h; sourceTree = "<group>"; };
		9AD775691B70AC8300904927 /* WBDNSQueryManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WBDNSQueryManager.m; path = QueryManager/WBDNSQueryManager.m; sourceTree = "<group>"; };
		9AD7756B1B70C34100904927 /* WBDNSNetworkManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WBDNSNetworkManager.h; path = DNSCache/WBDNSNetworkManager.h; sourceTree = "<group>"; };
		9AD7756C1B70C34100904927 /* WBDNSNetworkManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WBDNSNetworkManager.m; path = DNSCache/WBDNSNetworkManager.m; sourceTree = "<group>"; };
		9AD9C0671B8EE15B0027EFD5 /* WBDNSLogManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WBDNSLogManager.h; path = LogManager/WBDNSLogManager.h; sourceTree = "<group>"; };
		9AD9C0681B8EE15B0027EFD5 /* WBDNSLogManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WBDNSLogManager.m; path = LogManager/WBDNSLogManager.m; sourceTree = "<group>"; };
		9AD9C06A1B8EE1BE0027EFD5 /* WBDNSLogger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WBDNSLogger.h; path = LogManager/WBDNSLogger.h; sourceTree = "<group>"; };
		9AD9C06B1B8EE1BE0027EFD5 /* WBDNSLogger.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WBDNSLogger.m; path = LogManager/WBDNSLogger.m; sourceTree = "<group>"; };
		9AD9C06D1B8EEF8E0027EFD5 /* WBDNSLogFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WBDNSLogFile.h; path = LogManager/WBDNSLogFile.h; sourceTree = "<group>"; };
		9AD9C06E1B8EEF8E0027EFD5 /* WBDNSLogFile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WBDNSLogFile.m; path = LogManager/WBDNSLogFile.m; sourceTree = "<group>"; };
		9AE0E6341B8AEEF300018625 /* WBDNSSortManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WBDNSSortManager.h; path = SortManager/WBDNSSortManager.h; sourceTree = "<group>"; };
		9AE0E6351B8AEEF300018625 /* WBDNSSortManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WBDNSSortManager.m; path = SortManager/WBDNSSortManager.m; sourceTree = "<group>"; };
		9AE96CE61B78857000AF20F9 /* WBDNSMemeryCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WBDNSMemeryCache.h; path = Models/WBDNSMemeryCache.h; sourceTree = "<group>"; };
		9AE96CE71B78857000AF20F9 /* WBDNSMemeryCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WBDNSMemeryCache.m; path = Models/WBDNSMemeryCache.m; sourceTree = "<group>"; };
		9AF00BBA1B846FAE00B60534 /* WBDNSSpeedTester.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WBDNSSpeedTester.h; sourceTree = "<group>"; };
		9AF00BBB1B84704B00B60534 /* WBDNSTCPSpeedTester.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WBDNSTCPSpeedTester.h; path = SpeedTest/WBDNSTCPSpeed
Download .txt
gitextract_sdz2bv6d/

├── .gitignore
├── LICENSE
├── README.md
└── src/
    ├── DNSCache/
    │   ├── HttpDns/
    │   │   ├── WBDNSHttpDnsManager.h
    │   │   └── WBDNSHttpDnsManager.m
    │   ├── LogManager/
    │   │   ├── WBDNSLogFile.h
    │   │   ├── WBDNSLogFile.m
    │   │   ├── WBDNSLogManager.h
    │   │   ├── WBDNSLogManager.m
    │   │   ├── WBDNSLogger.h
    │   │   └── WBDNSLogger.m
    │   ├── Models/
    │   │   ├── WBDNSConfig.h
    │   │   ├── WBDNSConfig.m
    │   │   ├── WBDNSDomainModel.h
    │   │   ├── WBDNSDomainModel.m
    │   │   ├── WBDNSHttpDnsPack.h
    │   │   ├── WBDNSHttpDnsPack.m
    │   │   ├── WBDNSIP.h
    │   │   ├── WBDNSIP.m
    │   │   ├── WBDNSIpModel.h
    │   │   ├── WBDNSIpModel.m
    │   │   ├── WBDNSMemeryCache.h
    │   │   ├── WBDNSMemeryCache.m
    │   │   └── WBDNSModel.h
    │   ├── QueryManager/
    │   │   ├── WBDNSQueryManager.h
    │   │   └── WBDNSQueryManager.m
    │   ├── SortManager/
    │   │   ├── WBDNSSortManager.h
    │   │   └── WBDNSSortManager.m
    │   ├── SpeedTest/
    │   │   ├── WBDNSSpeedTestManager.h
    │   │   ├── WBDNSSpeedTestManager.m
    │   │   ├── WBDNSTCPSpeedTester.h
    │   │   └── WBDNSTCPSpeedTester.m
    │   ├── Tools/
    │   │   ├── WBDNSTools.h
    │   │   ├── WBDNSTools.m
    │   │   ├── WBDNSWeakTimer.h
    │   │   └── WBDNSWeakTimer.m
    │   ├── WBDNSCache.h
    │   ├── WBDNSCache.m
    │   ├── WBDNSConfigManager.h
    │   ├── WBDNSConfigManager.m
    │   ├── WBDNSNetworkManager.h
    │   ├── WBDNSNetworkManager.m
    │   ├── WBDNSReachability.h
    │   ├── WBDNSReachability.m
    │   ├── WBDNSSpeedTester.h
    │   └── cache/
    │       ├── WBDNSCacheManager.h
    │       ├── WBDNSCacheManager.m
    │       ├── WBDNSDBManager.h
    │       └── WBDNSDBManager.m
    └── DNSCache.xcodeproj/
        ├── project.pbxproj
        └── project.xcworkspace/
            └── contents.xcworkspacedata
Download .txt
SYMBOL INDEX (3 symbols across 2 files)

FILE: src/DNSCache/LogManager/WBDNSLogManager.h
  type WBDNS_LOG_TYPE (line 11) | typedef enum WBDNS_LOG_TYPE

FILE: src/DNSCache/WBDNSReachability.h
  type WBDNSNetworkStatus (line 53) | typedef enum : NSInteger {
  type sockaddr_in (line 73) | struct sockaddr_in
Condensed preview — 51 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (227K chars).
[
  {
    "path": ".gitignore",
    "chars": 494,
    "preview": "# Xcode\n#\nbuild/\n*.pbxuser\n!default.pbxuser\n*.mode1v3\n!default.mode1v3\n*.mode2v3\n!default.mode2v3\n*.perspectivev3\n!defau"
  },
  {
    "path": "LICENSE",
    "chars": 1302,
    "preview": "Copyright (c) 2015, 渣浪研发中心技术保障部移动端保障团队\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or "
  },
  {
    "path": "README.md",
    "chars": 1371,
    "preview": "# HTTPDNSLib-for-iOS\nDNSCache库使用说明书\n\n目前我们的服务器还不能支持所有的域名,所以代码里有白名单来限制域名。现在还不建议大家用在商用项目里,仅做学习交流用。\n\n1. 导入LibDnsCache.a, WBD"
  },
  {
    "path": "src/DNSCache/HttpDns/WBDNSHttpDnsManager.h",
    "chars": 440,
    "preview": "//\n//  HttpDnsManager.h\n//  DNSCache\n//\n//  Created by Robert Yang on 15/8/4.\n//  Copyright (c) 2015年 Weibo. All rights "
  },
  {
    "path": "src/DNSCache/HttpDns/WBDNSHttpDnsManager.m",
    "chars": 3627,
    "preview": "//\n//  HttpDnsManager.m\n//  DNSCache\n//\n//  Created by Robert Yang on 15/8/4.\n//  Copyright (c) 2015年 Weibo. All rights "
  },
  {
    "path": "src/DNSCache/LogManager/WBDNSLogFile.h",
    "chars": 516,
    "preview": "//\n//  WBDNSLogFile.h\n//  DNSCache\n//\n//  Created by Robert Yang on 15/8/27.\n//  Copyright (c) 2015年 Weibo. All rights r"
  },
  {
    "path": "src/DNSCache/LogManager/WBDNSLogFile.m",
    "chars": 814,
    "preview": "//\n//  WBDNSLogFile.m\n//  DNSCache\n//\n//  Created by Robert Yang on 15/8/27.\n//  Copyright (c) 2015年 Weibo. All rights r"
  },
  {
    "path": "src/DNSCache/LogManager/WBDNSLogManager.h",
    "chars": 889,
    "preview": "//\n//  WBDNSLogManager.h\n//  DNSCache\n//\n//  Created by Robert Yang on 15/8/27.\n//  Copyright (c) 2015年 Weibo. All right"
  },
  {
    "path": "src/DNSCache/LogManager/WBDNSLogManager.m",
    "chars": 3302,
    "preview": "//\n//  WBDNSLogManager.m\n//  DNSCache\n//\n//  Created by Robert Yang on 15/8/27.\n//  Copyright (c) 2015年 Weibo. All right"
  },
  {
    "path": "src/DNSCache/LogManager/WBDNSLogger.h",
    "chars": 428,
    "preview": "//\n//  WBDNSLogger.h\n//  DNSCache\n//\n//  Created by Robert Yang on 15/8/27.\n//  Copyright (c) 2015年 Weibo. All rights re"
  },
  {
    "path": "src/DNSCache/LogManager/WBDNSLogger.m",
    "chars": 16081,
    "preview": "// Software License Agreement (BSD License)\n//\n// Copyright (c) 2010-2015, Deusty, LLC\n// All rights reserved.\n//\n// Red"
  },
  {
    "path": "src/DNSCache/Models/WBDNSConfig.h",
    "chars": 1471,
    "preview": "//\n//  WBDNSConfig.h\n//  DNSCache\n//\n//  Created by Robert Yang on 15/9/1.\n//  Copyright (c) 2015年 Weibo. All rights res"
  },
  {
    "path": "src/DNSCache/Models/WBDNSConfig.m",
    "chars": 3691,
    "preview": "//\n//  WBDNSConfig.m\n//  DNSCache\n//\n//  Created by Robert Yang on 15/9/1.\n//  Copyright (c) 2015年 Weibo. All rights res"
  },
  {
    "path": "src/DNSCache/Models/WBDNSDomainModel.h",
    "chars": 1422,
    "preview": "//\n//  DomainModel.h\n//  DNSCache\n//\n//  Created by Robert Yang on 15/7/28.\n//  Copyright (c) 2015年 Weibo. All rights re"
  },
  {
    "path": "src/DNSCache/Models/WBDNSDomainModel.m",
    "chars": 2893,
    "preview": "//\n//  DomainModel.m\n//  DNSCache\n//\n//  Created by Robert Yang on 15/7/28.\n//  Copyright (c) 2015年 Weibo. All rights re"
  },
  {
    "path": "src/DNSCache/Models/WBDNSHttpDnsPack.h",
    "chars": 1006,
    "preview": "//\n//  HttpDnsPack.h\n//  DNSCache\n//\n//  Created by Robert Yang on 15/7/28.\n//  Copyright (c) 2015年 Weibo. All rights re"
  },
  {
    "path": "src/DNSCache/Models/WBDNSHttpDnsPack.m",
    "chars": 1709,
    "preview": "//\n//  HttpDnsPack.m\n//  DNSCache\n//\n//  Created by Robert Yang on 15/7/28.\n//  Copyright (c) 2015年 Weibo. All rights re"
  },
  {
    "path": "src/DNSCache/Models/WBDNSIP.h",
    "chars": 426,
    "preview": "//\n//  IP.h\n//  DNSCache\n//\n//  Created by Robert Yang on 15/7/28.\n//  Copyright (c) 2015年 Weibo. All rights reserved.\n/"
  },
  {
    "path": "src/DNSCache/Models/WBDNSIP.m",
    "chars": 634,
    "preview": "//\n//  IP.m\n//  DNSCache\n//\n//  Created by Robert Yang on 15/7/28.\n//  Copyright (c) 2015年 Weibo. All rights reserved.\n/"
  },
  {
    "path": "src/DNSCache/Models/WBDNSIpModel.h",
    "chars": 2182,
    "preview": "//\n//  IpModel.h\n//  DNSCache\n//\n//  Created by Robert Yang on 15/7/28.\n//  Copyright (c) 2015年 Weibo. All rights reserv"
  },
  {
    "path": "src/DNSCache/Models/WBDNSIpModel.m",
    "chars": 3157,
    "preview": "//\n//  IpModel.m\n//  DNSCache\n//\n//  Created by Robert Yang on 15/7/28.\n//  Copyright (c) 2015年 Weibo. All rights reserv"
  },
  {
    "path": "src/DNSCache/Models/WBDNSMemeryCache.h",
    "chars": 572,
    "preview": "//\n//  WBDNSMemeryCache.h\n//  DNSCache\n//\n//  Created by Robert Yang on 15/8/10.\n//  Copyright (c) 2015年 Weibo. All righ"
  },
  {
    "path": "src/DNSCache/Models/WBDNSMemeryCache.m",
    "chars": 3633,
    "preview": "//\n//  WBDNSMemeryCache.m\n//  DNSCache\n//\n//  Created by Robert Yang on 15/8/10.\n//  Copyright (c) 2015年 Weibo. All righ"
  },
  {
    "path": "src/DNSCache/Models/WBDNSModel.h",
    "chars": 290,
    "preview": "//\n//  WBDNSModel.h\n//  DNSCache\n//\n//  Created by Robert Yang on 15/8/3.\n//  Copyright (c) 2015年 Weibo. All rights rese"
  },
  {
    "path": "src/DNSCache/QueryManager/WBDNSQueryManager.h",
    "chars": 505,
    "preview": "//\n//  QueryManager.h\n//  DNSCache\n//\n//  Created by Robert Yang on 15/8/4.\n//  Copyright (c) 2015年 Weibo. All rights re"
  },
  {
    "path": "src/DNSCache/QueryManager/WBDNSQueryManager.m",
    "chars": 3411,
    "preview": "//\n//  QueryManager.m\n//  DNSCache\n//\n//  Created by Robert Yang on 15/8/4.\n//  Copyright (c) 2015年 Weibo. All rights re"
  },
  {
    "path": "src/DNSCache/SortManager/WBDNSSortManager.h",
    "chars": 308,
    "preview": "//\n//  WBDNSSortManager.h\n//  DNSCache\n//\n//  Created by Robert Yang on 15/8/24.\n//  Copyright (c) 2015年 Weibo. All righ"
  },
  {
    "path": "src/DNSCache/SortManager/WBDNSSortManager.m",
    "chars": 3485,
    "preview": "//\n//  WBDNSSortManager.m\n//  DNSCache\n//\n//  Created by Robert Yang on 15/8/24.\n//  Copyright (c) 2015年 Weibo. All righ"
  },
  {
    "path": "src/DNSCache/SpeedTest/WBDNSSpeedTestManager.h",
    "chars": 338,
    "preview": "//\n//  SpeedTestManager.h\n//  DNSCache\n//\n//  Created by Robert Yang on 15/8/19.\n//  Copyright (c) 2015年 Weibo. All righ"
  },
  {
    "path": "src/DNSCache/SpeedTest/WBDNSSpeedTestManager.m",
    "chars": 1790,
    "preview": "//\n//  SpeedTestManager.m\n//  DNSCache\n//\n//  Created by Robert Yang on 15/8/19.\n//  Copyright (c) 2015年 Weibo. All righ"
  },
  {
    "path": "src/DNSCache/SpeedTest/WBDNSTCPSpeedTester.h",
    "chars": 372,
    "preview": "//\n//  TCPSpeedTester.h\n//  DNSCache\n//\n//  Created by Robert Yang on 15/8/19.\n//  Copyright (c) 2015年 Weibo. All rights"
  },
  {
    "path": "src/DNSCache/SpeedTest/WBDNSTCPSpeedTester.m",
    "chars": 3555,
    "preview": "//\n//  TCPSpeedTester.m\n//  DNSCache\n//\n//  Created by Robert Yang on 15/8/19.\n//  Copyright (c) 2015年 Weibo. All rights"
  },
  {
    "path": "src/DNSCache/Tools/WBDNSTools.h",
    "chars": 1262,
    "preview": "//\n//  Tools.h\n//  DNSCache\n//\n//  Created by Robert Yang on 15/8/3.\n//  Copyright (c) 2015年 Weibo. All rights reserved."
  },
  {
    "path": "src/DNSCache/Tools/WBDNSTools.m",
    "chars": 7591,
    "preview": "//\n//  Tools.m\n//  DNSCache\n//\n//  Created by Robert Yang on 15/8/3.\n//  Copyright (c) 2015年 Weibo. All rights reserved."
  },
  {
    "path": "src/DNSCache/Tools/WBDNSWeakTimer.h",
    "chars": 820,
    "preview": "//\n//  WBDNSWeakTimer.h\n//  DNSCache\n//\n//  Created by Robert Yang on 15/8/17.\n//  Copyright (c) 2015年 Weibo. All rights"
  },
  {
    "path": "src/DNSCache/Tools/WBDNSWeakTimer.m",
    "chars": 2564,
    "preview": "//\n//  WBDNSWeakTimer.m\n//  DNSCache\n//\n//  Created by Robert Yang on 15/8/17.\n//  Copyright (c) 2015年 Weibo. All rights"
  },
  {
    "path": "src/DNSCache/WBDNSCache.h",
    "chars": 1912,
    "preview": "//\n//  DNSCache.h\n//  DNSCache\n//\n//  Created by Robert Yang on 15/7/29.\n//  Copyright (c) 2015年 Weibo. All rights reser"
  },
  {
    "path": "src/DNSCache/WBDNSCache.m",
    "chars": 11535,
    "preview": "//\n//  DNSCache.m\n//  DNSCache\n//\n//  Created by Robert Yang on 15/7/29.\n//  Copyright (c) 2015年 Weibo. All rights reser"
  },
  {
    "path": "src/DNSCache/WBDNSConfigManager.h",
    "chars": 1671,
    "preview": "//\n//  WBDNSCacheConfig.h\n//  DNSCache\n//\n//  Created by Robert Yang on 15/8/13.\n//  Copyright (c) 2015年 Weibo. All righ"
  },
  {
    "path": "src/DNSCache/WBDNSConfigManager.m",
    "chars": 16909,
    "preview": "//\n//  WBDNSCacheConfig.m\n//  DNSCache\n//\n//  Created by Robert Yang on 15/8/13.\n//  Copyright (c) 2015年 Weibo. All righ"
  },
  {
    "path": "src/DNSCache/WBDNSNetworkManager.h",
    "chars": 1155,
    "preview": "//\n//  NetworkManager.h\n//  DNSCache\n//\n//  Created by Robert Yang on 15/8/4.\n//  Copyright (c) 2015年 Weibo. All rights "
  },
  {
    "path": "src/DNSCache/WBDNSNetworkManager.m",
    "chars": 5232,
    "preview": "//\n//  NetworkManager.m\n//  DNSCache\n//\n//  Created by Robert Yang on 15/8/4.\n//  Copyright (c) 2015年 Weibo. All rights "
  },
  {
    "path": "src/DNSCache/WBDNSReachability.h",
    "chars": 3930,
    "preview": "/*\n     File: Reachability.h\n Abstract: Basic demonstration of how to use the SystemConfiguration Reachablity APIs.\n  Ve"
  },
  {
    "path": "src/DNSCache/WBDNSReachability.m",
    "chars": 9746,
    "preview": "/*\n     File: Reachability.m\n Abstract: Basic demonstration of how to use the SystemConfiguration Reachablity APIs.\n  Ve"
  },
  {
    "path": "src/DNSCache/WBDNSSpeedTester.h",
    "chars": 218,
    "preview": "//\n//  WBDNSSpeedTester.h\n//  DNSCache\n//\n//  Created by Robert Yang on 15/8/19.\n//  Copyright (c) 2015年 Weibo. All righ"
  },
  {
    "path": "src/DNSCache/cache/WBDNSCacheManager.h",
    "chars": 952,
    "preview": "//\n//  DNSCacheManager.h\n//  DNSCache\n//\n//  Created by Robert Yang on 15/8/3.\n//  Copyright (c) 2015年 Weibo. All rights"
  },
  {
    "path": "src/DNSCache/cache/WBDNSCacheManager.m",
    "chars": 9522,
    "preview": "//\n//  DNSCacheManager.m\n//  DNSCache\n//\n//  Created by Robert Yang on 15/8/3.\n//  Copyright (c) 2015年 Weibo. All rights"
  },
  {
    "path": "src/DNSCache/cache/WBDNSDBManager.h",
    "chars": 820,
    "preview": "//\n//  DBManager.h\n//  DNSCache\n//\n//  Created by Robert Yang on 15/7/28.\n//  Copyright (c) 2015年 Weibo. All rights rese"
  },
  {
    "path": "src/DNSCache/cache/WBDNSDBManager.m",
    "chars": 38741,
    "preview": "//\n//  DBManager.m\n//  DNSCache\n//\n//  Created by Robert Yang on 15/7/28.\n//  Copyright (c) 2015年 Weibo. All rights rese"
  },
  {
    "path": "src/DNSCache.xcodeproj/project.pbxproj",
    "chars": 26889,
    "preview": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 46;\n\tobjects = {\n\n/* Begin PBXBuildFile section *"
  },
  {
    "path": "src/DNSCache.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
    "chars": 153,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:DNSCache.xcodep"
  }
]

About this extraction

This page contains the full source code of the CNSRE/HTTPDNSLib-for-iOS GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 51 files (202.9 KB), approximately 58.0k tokens, and a symbol index with 3 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!