阿里sdk
This commit is contained in:
28
EdgeHttpDNS/sdk/ios/AlicloudHttpDNS/Utils/HttpDnsLocker.h
Normal file
28
EdgeHttpDNS/sdk/ios/AlicloudHttpDNS/Utils/HttpDnsLocker.h
Normal file
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// HttpDnsLocker.h
|
||||
// AlicloudHttpDNS
|
||||
//
|
||||
// Created by 王贇 on 2023/8/16.
|
||||
// Copyright © 2023 alibaba-inc.com. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef HttpDnsLocker_h
|
||||
#define HttpDnsLocker_h
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "HttpdnsRequest.h"
|
||||
|
||||
@interface HttpDnsLocker : NSObject
|
||||
|
||||
+ (instancetype)sharedInstance;
|
||||
|
||||
- (void)lock:(NSString *)host queryType:(HttpdnsQueryIPType)queryType;
|
||||
|
||||
- (BOOL)tryLock:(NSString *)host queryType:(HttpdnsQueryIPType)queryType;
|
||||
|
||||
- (void)unlock:(NSString *)host queryType:(HttpdnsQueryIPType)queryType;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
#endif /* HttpDnsLocker_h */
|
||||
91
EdgeHttpDNS/sdk/ios/AlicloudHttpDNS/Utils/HttpDnsLocker.m
Normal file
91
EdgeHttpDNS/sdk/ios/AlicloudHttpDNS/Utils/HttpDnsLocker.m
Normal file
@@ -0,0 +1,91 @@
|
||||
//
|
||||
// HttpDnsLocker.m
|
||||
// AlicloudHttpDNS
|
||||
//
|
||||
// Created by 王贇 on 2023/8/16.
|
||||
// Copyright © 2023 alibaba-inc.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "HttpDnsLocker.h"
|
||||
#import "HttpdnsService.h"
|
||||
|
||||
@implementation HttpDnsLocker {
|
||||
NSMutableDictionary<NSString*, NSLock*> *_v4LockMap;
|
||||
NSMutableDictionary<NSString*, NSLock*> *_v6LockMap;
|
||||
NSMutableDictionary<NSString*, NSLock*> *_v4v6LockMap;
|
||||
}
|
||||
|
||||
+ (instancetype)sharedInstance {
|
||||
static HttpDnsLocker *locker = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
locker = [[HttpDnsLocker alloc] init];
|
||||
});
|
||||
return locker;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
if (self = [super init]) {
|
||||
_v4LockMap = [NSMutableDictionary dictionary];
|
||||
_v6LockMap = [NSMutableDictionary dictionary];
|
||||
_v4v6LockMap = [NSMutableDictionary dictionary];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)lock:(NSString *)host queryType:(HttpdnsQueryIPType)queryIpType {
|
||||
NSLock *condition = [self getLock:host queryType:queryIpType];
|
||||
|
||||
if (condition) {
|
||||
[condition lock];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)tryLock:(NSString *)host queryType:(HttpdnsQueryIPType)queryType {
|
||||
NSLock *condition = [self getLock:host queryType:queryType];
|
||||
|
||||
if (condition) {
|
||||
return [condition tryLock];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)unlock:(NSString *)host queryType:(HttpdnsQueryIPType)queryType {
|
||||
NSLock *condition = [self getLock:host queryType:queryType];
|
||||
if (condition) {
|
||||
[condition unlock];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSLock *)getLock:(NSString *)host queryType:(HttpdnsQueryIPType)queryType {
|
||||
if (queryType == HttpdnsQueryIPTypeIpv4) {
|
||||
@synchronized (_v4LockMap) {
|
||||
NSLock *lock = [_v4LockMap objectForKey:host];
|
||||
if (!lock) {
|
||||
lock = [[NSLock alloc] init];
|
||||
[_v4LockMap setObject:lock forKey:host];
|
||||
}
|
||||
return lock;
|
||||
}
|
||||
} else if (queryType == HttpdnsQueryIPTypeIpv6) {
|
||||
@synchronized (_v6LockMap) {
|
||||
NSLock *condition = [_v6LockMap objectForKey:host];
|
||||
if (!condition) {
|
||||
condition = [[NSLock alloc] init];
|
||||
[_v6LockMap setObject:condition forKey:host];
|
||||
}
|
||||
return condition;
|
||||
}
|
||||
} else {
|
||||
@synchronized (_v4v6LockMap) {
|
||||
NSLock *condition = [_v4v6LockMap objectForKey:host];
|
||||
if (!condition) {
|
||||
condition = [[NSLock alloc] init];
|
||||
[_v4v6LockMap setObject:condition forKey:host];
|
||||
}
|
||||
return condition;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,38 @@
|
||||
//
|
||||
// HttpdnsHostObjectInMemoryCache.h
|
||||
// AlicloudHttpDNS
|
||||
//
|
||||
// Created by xuyecan on 2024/9/28.
|
||||
// Copyright © 2024 alibaba-inc.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "HttpdnsHostObject.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
// 这个字典在HTTPDNS中只用于存储HttpdnsHostObject对象,这个对象是整个框架的核心对象,用于缓存和处理域名解析结果
|
||||
// 通常从缓存中获得这个对象之后,会根据不同场景改变一些字段的值,而且很可能发生在不同线程中
|
||||
// 而不同线程从缓存中直接读取共享对象的话,很有可能发生线程竞争的情况,多线程访问某个对象的同一个字段,在swift环境有较高概率发生crash
|
||||
// 因此,除了确保字典操作的线程安全,拿出对象的时候,也直接copy一个复制对象返回(HttpdnsHostObject对象实现了NSCopying协议)
|
||||
@interface HttpdnsHostObjectInMemoryCache : NSObject
|
||||
|
||||
- (void)setHostObject:(HttpdnsHostObject *)object forCacheKey:(NSString *)key;
|
||||
|
||||
- (HttpdnsHostObject *)getHostObjectByCacheKey:(NSString *)key;
|
||||
|
||||
- (HttpdnsHostObject *)getHostObjectByCacheKey:(NSString *)key createIfNotExists:(HttpdnsHostObject *(^)(void))objectProducer;
|
||||
|
||||
- (void)updateQualityForCacheKey:(NSString *)key forIp:(NSString *)ip withConnectedRT:(NSInteger)connectedRT;
|
||||
|
||||
- (void)removeHostObjectByCacheKey:(NSString *)key;
|
||||
|
||||
- (void)removeAllHostObjects;
|
||||
|
||||
- (NSInteger)count;
|
||||
|
||||
- (NSArray *)allCacheKeys;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,94 @@
|
||||
//
|
||||
// HttpdnsHostObjectInMemoryCache.m
|
||||
// AlicloudHttpDNS
|
||||
//
|
||||
// Created by xuyecan on 2024/9/28.
|
||||
// Copyright © 2024 alibaba-inc.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "HttpdnsHostObjectInMemoryCache.h"
|
||||
|
||||
@interface HttpdnsHostObjectInMemoryCache ()
|
||||
|
||||
@property (nonatomic, strong) NSMutableDictionary<NSString *, HttpdnsHostObject *> *cacheDict;
|
||||
@property (nonatomic, strong) NSLock *lock;
|
||||
|
||||
@end
|
||||
|
||||
@implementation HttpdnsHostObjectInMemoryCache
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_cacheDict = [NSMutableDictionary dictionary];
|
||||
_lock = [[NSLock alloc] init];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setHostObject:(HttpdnsHostObject *)object forCacheKey:(NSString *)key {
|
||||
[_lock lock];
|
||||
_cacheDict[key] = object;
|
||||
[_lock unlock];
|
||||
}
|
||||
|
||||
- (HttpdnsHostObject *)getHostObjectByCacheKey:(NSString *)key {
|
||||
[_lock lock];
|
||||
@try {
|
||||
HttpdnsHostObject *object = _cacheDict[key];
|
||||
return [object copy];
|
||||
} @finally {
|
||||
[_lock unlock];
|
||||
}
|
||||
}
|
||||
|
||||
- (HttpdnsHostObject *)getHostObjectByCacheKey:(NSString *)key createIfNotExists:(HttpdnsHostObject *(^)(void))objectProducer {
|
||||
[_lock lock];
|
||||
HttpdnsHostObject *object = _cacheDict[key];
|
||||
@try {
|
||||
if (!object) {
|
||||
object = objectProducer();
|
||||
_cacheDict[key] = object;
|
||||
}
|
||||
return [object copy];
|
||||
} @finally {
|
||||
[_lock unlock];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updateQualityForCacheKey:(NSString *)key forIp:(NSString *)ip withConnectedRT:(NSInteger)connectedRT {
|
||||
[_lock lock];
|
||||
HttpdnsHostObject *object = _cacheDict[key];
|
||||
if (object) {
|
||||
[object updateConnectedRT:connectedRT forIP:ip];
|
||||
}
|
||||
[_lock unlock];
|
||||
}
|
||||
|
||||
- (void)removeHostObjectByCacheKey:(NSString *)key {
|
||||
[_lock lock];
|
||||
[_cacheDict removeObjectForKey:key];
|
||||
[_lock unlock];
|
||||
}
|
||||
|
||||
- (void)removeAllHostObjects {
|
||||
[_lock lock];
|
||||
[_cacheDict removeAllObjects];
|
||||
[_lock unlock];
|
||||
}
|
||||
|
||||
- (NSInteger)count {
|
||||
[_lock lock];
|
||||
NSInteger count = _cacheDict.count;
|
||||
[_lock unlock];
|
||||
return count;
|
||||
}
|
||||
|
||||
- (NSArray *)allCacheKeys {
|
||||
[_lock lock];
|
||||
NSArray *keys = [_cacheDict allKeys];
|
||||
[_lock unlock];
|
||||
return keys;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,96 @@
|
||||
//
|
||||
// HttpdnsIPQualityDetector.h
|
||||
// AlicloudHttpDNS
|
||||
//
|
||||
// Created by xuyecan on 2025/3/13.
|
||||
// Copyright © 2025 alibaba-inc.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* IP质量检测回调
|
||||
* @param cacheKey 缓存键
|
||||
* @param ip IP地址
|
||||
* @param costTime 连接耗时(毫秒),-1表示连接失败
|
||||
*/
|
||||
typedef void(^HttpdnsIPQualityCallback)(NSString *cacheKey, NSString *ip, NSInteger costTime);
|
||||
|
||||
@interface HttpdnsIPQualityDetector : NSObject
|
||||
|
||||
/**
|
||||
* 单例方法
|
||||
*/
|
||||
+ (instancetype)sharedInstance;
|
||||
|
||||
/**
|
||||
* 获取当前等待队列中的任务数量
|
||||
*/
|
||||
- (NSUInteger)pendingTasksCount;
|
||||
|
||||
/**
|
||||
* 调度一个IP连接质量检测任务,不会阻塞当前线程
|
||||
* @param cacheKey 缓存键,通常是域名
|
||||
* @param ip 要检测的IP地址
|
||||
* @param port 连接端口,如果为nil则默认使用80
|
||||
* @param callback 检测完成后的回调
|
||||
*/
|
||||
- (void)scheduleIPQualityDetection:(NSString *)cacheKey
|
||||
ip:(NSString *)ip
|
||||
port:(nullable NSNumber *)port
|
||||
callback:(HttpdnsIPQualityCallback)callback;
|
||||
|
||||
#pragma mark - Methods exposed for testing
|
||||
|
||||
/**
|
||||
* 执行IP连接质量检测
|
||||
* @param cacheKey 缓存键,通常是域名
|
||||
* @param ip 要检测的IP地址
|
||||
* @param port 连接端口,如果为nil则默认使用80
|
||||
* @param callback 检测完成后的回调
|
||||
* @note 此方法主要用于测试
|
||||
*/
|
||||
- (void)executeDetection:(NSString *)cacheKey
|
||||
ip:(NSString *)ip
|
||||
port:(nullable NSNumber *)port
|
||||
callback:(HttpdnsIPQualityCallback)callback;
|
||||
|
||||
/**
|
||||
* 建立TCP连接并测量连接时间
|
||||
* @param ip 要连接的IP地址
|
||||
* @param port 连接端口
|
||||
* @return 连接耗时(毫秒),-1表示连接失败
|
||||
* @note 此方法主要用于测试
|
||||
*/
|
||||
- (NSInteger)tcpConnectToIP:(NSString *)ip port:(int)port;
|
||||
|
||||
/**
|
||||
* 添加待处理任务
|
||||
* @param cacheKey 缓存键,通常是域名
|
||||
* @param ip 要检测的IP地址
|
||||
* @param port 连接端口
|
||||
* @param callback 检测完成后的回调
|
||||
* @note 此方法主要用于测试
|
||||
*/
|
||||
- (void)addPendingTask:(NSString *)cacheKey
|
||||
ip:(NSString *)ip
|
||||
port:(nullable NSNumber *)port
|
||||
callback:(HttpdnsIPQualityCallback)callback;
|
||||
|
||||
/**
|
||||
* 处理待处理任务队列
|
||||
* @note 此方法主要用于测试
|
||||
*/
|
||||
- (void)processPendingTasksIfNeeded;
|
||||
|
||||
/**
|
||||
* 处理所有待处理任务
|
||||
* @note 此方法主要用于测试
|
||||
*/
|
||||
- (void)processPendingTasks;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,291 @@
|
||||
//
|
||||
// HttpdnsIPQualityDetector.m
|
||||
// AlicloudHttpDNS
|
||||
//
|
||||
// Created by xuyecan on 2025/3/13.
|
||||
// Copyright © 2025 alibaba-inc.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "HttpdnsIPQualityDetector.h"
|
||||
#import <sys/socket.h>
|
||||
#import <netinet/in.h>
|
||||
#import <arpa/inet.h>
|
||||
#import <netdb.h>
|
||||
#import <unistd.h>
|
||||
#import <sys/time.h>
|
||||
#import <fcntl.h>
|
||||
#import <errno.h>
|
||||
#import "HttpdnsLog_Internal.h"
|
||||
#import "HttpdnsUtil.h"
|
||||
|
||||
// 定义任务类,替代之前的结构体,确保正确的内存管理
|
||||
@interface HttpdnsDetectionTask : NSObject
|
||||
@property (nonatomic, copy) NSString *cacheKey;
|
||||
@property (nonatomic, copy) NSString *ip;
|
||||
@property (nonatomic, strong) NSNumber *port;
|
||||
@property (nonatomic, copy) HttpdnsIPQualityCallback callback;
|
||||
@end
|
||||
|
||||
@implementation HttpdnsDetectionTask
|
||||
@end
|
||||
|
||||
@interface HttpdnsIPQualityDetector ()
|
||||
|
||||
@property (nonatomic, strong) dispatch_queue_t detectQueue;
|
||||
@property (nonatomic, strong) dispatch_semaphore_t concurrencySemaphore;
|
||||
@property (nonatomic, strong) NSMutableArray<HttpdnsDetectionTask *> *pendingTasks;
|
||||
@property (nonatomic, strong) NSLock *pendingTasksLock;
|
||||
@property (nonatomic, assign) BOOL isProcessingPendingTasks;
|
||||
|
||||
/**
|
||||
* 最大并发检测数量,默认为10
|
||||
*/
|
||||
@property (nonatomic, assign) NSUInteger maxConcurrentDetections;
|
||||
|
||||
@end
|
||||
|
||||
@implementation HttpdnsIPQualityDetector
|
||||
|
||||
+ (instancetype)sharedInstance {
|
||||
static HttpdnsIPQualityDetector *instance = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
instance = [[HttpdnsIPQualityDetector alloc] init];
|
||||
});
|
||||
return instance;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_detectQueue = dispatch_queue_create("com.aliyun.httpdns.ipqualitydetector", DISPATCH_QUEUE_CONCURRENT);
|
||||
_maxConcurrentDetections = 10;
|
||||
_concurrencySemaphore = dispatch_semaphore_create(_maxConcurrentDetections);
|
||||
_pendingTasks = [NSMutableArray array];
|
||||
_pendingTasksLock = [[NSLock alloc] init];
|
||||
_isProcessingPendingTasks = NO;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)scheduleIPQualityDetection:(NSString *)cacheKey
|
||||
ip:(NSString *)ip
|
||||
port:(NSNumber *)port
|
||||
callback:(HttpdnsIPQualityCallback)callback {
|
||||
if (!cacheKey || !ip || !callback) {
|
||||
HttpdnsLogDebug("IPQualityDetector invalid parameters for detection: cacheKey=%@, ip=%@", cacheKey, ip);
|
||||
return;
|
||||
}
|
||||
|
||||
// 尝试获取信号量,如果获取不到,说明已达到最大并发数
|
||||
if (dispatch_semaphore_wait(_concurrencySemaphore, DISPATCH_TIME_NOW) != 0) {
|
||||
// 将任务加入等待队列
|
||||
[self addPendingTask:cacheKey ip:ip port:port callback:callback];
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取到信号量,可以执行检测
|
||||
[self executeDetection:cacheKey ip:ip port:port callback:callback];
|
||||
}
|
||||
|
||||
- (void)addPendingTask:(NSString *)cacheKey ip:(NSString *)ip port:(NSNumber *)port callback:(HttpdnsIPQualityCallback)callback {
|
||||
// 创建任务对象,ARC会自动管理内存
|
||||
HttpdnsDetectionTask *task = [[HttpdnsDetectionTask alloc] init];
|
||||
task.cacheKey = cacheKey;
|
||||
task.ip = ip;
|
||||
task.port = port;
|
||||
task.callback = callback;
|
||||
|
||||
// 加锁添加任务
|
||||
[_pendingTasksLock lock];
|
||||
[_pendingTasks addObject:task];
|
||||
[_pendingTasksLock unlock];
|
||||
|
||||
// 如果没有正在处理等待队列,则开始处理
|
||||
[self processPendingTasksIfNeeded];
|
||||
}
|
||||
|
||||
- (NSUInteger)pendingTasksCount {
|
||||
[_pendingTasksLock lock];
|
||||
NSUInteger count = _pendingTasks.count;
|
||||
[_pendingTasksLock unlock];
|
||||
return count;
|
||||
}
|
||||
|
||||
- (void)processPendingTasksIfNeeded {
|
||||
[_pendingTasksLock lock];
|
||||
BOOL shouldProcess = !_isProcessingPendingTasks && _pendingTasks.count > 0;
|
||||
if (shouldProcess) {
|
||||
_isProcessingPendingTasks = YES;
|
||||
}
|
||||
[_pendingTasksLock unlock];
|
||||
|
||||
if (shouldProcess) {
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
[self processPendingTasks];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
- (void)processPendingTasks {
|
||||
while (1) {
|
||||
// 尝试获取信号量
|
||||
if (dispatch_semaphore_wait(_concurrencySemaphore, DISPATCH_TIME_NOW) != 0) {
|
||||
// 无法获取信号量,等待一段时间后重试
|
||||
[NSThread sleepForTimeInterval:0.1];
|
||||
continue;
|
||||
}
|
||||
|
||||
// 获取到信号量,取出一个等待任务
|
||||
HttpdnsDetectionTask *task = nil;
|
||||
|
||||
[_pendingTasksLock lock];
|
||||
if (_pendingTasks.count > 0) {
|
||||
task = _pendingTasks.firstObject;
|
||||
[_pendingTasks removeObjectAtIndex:0];
|
||||
} else {
|
||||
// 没有等待任务了,结束处理
|
||||
_isProcessingPendingTasks = NO;
|
||||
[_pendingTasksLock unlock];
|
||||
// 释放多余的信号量
|
||||
dispatch_semaphore_signal(_concurrencySemaphore);
|
||||
break;
|
||||
}
|
||||
[_pendingTasksLock unlock];
|
||||
|
||||
// 执行任务
|
||||
[self executeDetection:task.cacheKey ip:task.ip port:task.port callback:task.callback];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)executeDetection:(NSString *)cacheKey ip:(NSString *)ip port:(NSNumber *)port callback:(HttpdnsIPQualityCallback)callback {
|
||||
// 创建强引用以确保在异步操作期间对象不会被释放
|
||||
HttpdnsIPQualityCallback strongCallback = [callback copy];
|
||||
|
||||
// 使用后台队列进行检测,避免阻塞主线程
|
||||
dispatch_async(self.detectQueue, ^{
|
||||
NSInteger costTime = [self tcpConnectToIP:ip port:port ? [port intValue] : 80];
|
||||
|
||||
// 在后台线程回调结果
|
||||
dispatch_async(dispatch_get_global_queue(0, 0), ^{
|
||||
strongCallback(cacheKey, ip, costTime);
|
||||
|
||||
// 释放信号量,允许执行下一个任务
|
||||
dispatch_semaphore_signal(self->_concurrencySemaphore);
|
||||
|
||||
// 检查是否有等待的任务需要处理
|
||||
[self processPendingTasksIfNeeded];
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
- (NSInteger)tcpConnectToIP:(NSString *)ip port:(int)port {
|
||||
if (!ip || port <= 0) {
|
||||
return -1;
|
||||
}
|
||||
int socketFd;
|
||||
struct sockaddr_in serverAddr;
|
||||
struct sockaddr_in6 serverAddr6;
|
||||
void *serverAddrPtr;
|
||||
socklen_t serverAddrLen;
|
||||
BOOL isIPv6 = [HttpdnsUtil isIPv6Address:ip];
|
||||
BOOL isIpv4 = [HttpdnsUtil isIPv4Address:ip];
|
||||
|
||||
// 创建socket
|
||||
if (isIPv6) {
|
||||
socketFd = socket(AF_INET6, SOCK_STREAM, 0);
|
||||
if (socketFd < 0) {
|
||||
HttpdnsLogDebug("IPQualityDetector failed to create IPv6 socket: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&serverAddr6, 0, sizeof(serverAddr6));
|
||||
serverAddr6.sin6_family = AF_INET6;
|
||||
serverAddr6.sin6_port = htons(port);
|
||||
inet_pton(AF_INET6, [ip UTF8String], &serverAddr6.sin6_addr);
|
||||
|
||||
serverAddrPtr = &serverAddr6;
|
||||
serverAddrLen = sizeof(serverAddr6);
|
||||
} else if (isIpv4) {
|
||||
socketFd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (socketFd < 0) {
|
||||
HttpdnsLogDebug("IPQualityDetector failed to create IPv4 socket: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&serverAddr, 0, sizeof(serverAddr));
|
||||
serverAddr.sin_family = AF_INET;
|
||||
serverAddr.sin_port = htons(port);
|
||||
inet_pton(AF_INET, [ip UTF8String], &serverAddr.sin_addr);
|
||||
|
||||
serverAddrPtr = &serverAddr;
|
||||
serverAddrLen = sizeof(serverAddr);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 设置非阻塞模式
|
||||
int flags = fcntl(socketFd, F_GETFL, 0);
|
||||
fcntl(socketFd, F_SETFL, flags | O_NONBLOCK);
|
||||
|
||||
// 开始计时
|
||||
struct timeval startTime, endTime;
|
||||
gettimeofday(&startTime, NULL);
|
||||
|
||||
// 尝试连接
|
||||
int connectResult = connect(socketFd, serverAddrPtr, serverAddrLen);
|
||||
|
||||
if (connectResult < 0) {
|
||||
if (errno == EINPROGRESS) {
|
||||
// 连接正在进行中,使用select等待
|
||||
fd_set fdSet;
|
||||
struct timeval timeout;
|
||||
|
||||
FD_ZERO(&fdSet);
|
||||
FD_SET(socketFd, &fdSet);
|
||||
|
||||
// 设置超时时间为2秒
|
||||
// 更长的超时时间不是很有必要,因为建连超过2秒的IP,已经没有优选必要了
|
||||
timeout.tv_sec = 2;
|
||||
timeout.tv_usec = 0;
|
||||
|
||||
int selectResult = select(socketFd + 1, NULL, &fdSet, NULL, &timeout);
|
||||
|
||||
if (selectResult <= 0) {
|
||||
// 超时或错误
|
||||
HttpdnsLogDebug("IPQualityDetector connection to %@ timed out or error: %s", ip, strerror(errno));
|
||||
close(socketFd);
|
||||
return -1;
|
||||
} else {
|
||||
// 检查连接是否成功
|
||||
int error;
|
||||
socklen_t errorLen = sizeof(error);
|
||||
if (getsockopt(socketFd, SOL_SOCKET, SO_ERROR, &error, &errorLen) < 0 || error != 0) {
|
||||
HttpdnsLogDebug("IPQualityDetector connection to %@ failed after select: %s", ip, strerror(error));
|
||||
close(socketFd);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 其他错误
|
||||
HttpdnsLogDebug("IPQualityDetector connection to %@ failed: %s", ip, strerror(errno));
|
||||
close(socketFd);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// 结束计时
|
||||
gettimeofday(&endTime, NULL);
|
||||
|
||||
// 关闭socket
|
||||
close(socketFd);
|
||||
|
||||
// 计算耗时(毫秒)
|
||||
long seconds = endTime.tv_sec - startTime.tv_sec;
|
||||
long microseconds = endTime.tv_usec - startTime.tv_usec;
|
||||
NSInteger costTime = (seconds * 1000) + (microseconds / 1000);
|
||||
|
||||
return costTime;
|
||||
}
|
||||
|
||||
@end
|
||||
106
EdgeHttpDNS/sdk/ios/AlicloudHttpDNS/Utils/HttpdnsReachability.h
Normal file
106
EdgeHttpDNS/sdk/ios/AlicloudHttpDNS/Utils/HttpdnsReachability.h
Normal file
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
Copyright (c) 2011, Tony Million.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. 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.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <SystemConfiguration/SystemConfiguration.h>
|
||||
|
||||
//! Project version number for MacOSReachability.
|
||||
FOUNDATION_EXPORT double ReachabilityVersionNumber;
|
||||
|
||||
//! Project version string for MacOSReachability.
|
||||
FOUNDATION_EXPORT const unsigned char ReachabilityVersionString[];
|
||||
|
||||
/**
|
||||
* Create NS_ENUM macro if it does not exist on the targeted version of iOS or OS X.
|
||||
*
|
||||
* @see http://nshipster.com/ns_enum-ns_options/
|
||||
**/
|
||||
#ifndef NS_ENUM
|
||||
#define NS_ENUM(_type, _name) enum _name : _type _name; enum _name : _type
|
||||
#endif
|
||||
|
||||
extern NSString *const kHttpdnsReachabilityChangedNotification;
|
||||
|
||||
typedef NS_ENUM(NSInteger, HttpdnsNetworkStatus) {
|
||||
// Apple NetworkStatus Compatible Names.
|
||||
HttpdnsNotReachable = 0,
|
||||
HttpdnsReachableViaWiFi = 1,
|
||||
HttpdnsReachableVia2G = 2,
|
||||
HttpdnsReachableVia3G = 3,
|
||||
HttpdnsReachableVia4G = 4,
|
||||
HttpdnsReachableVia5G = 5
|
||||
};
|
||||
|
||||
@class HttpdnsReachability;
|
||||
|
||||
typedef void (^NetworkReachable)(HttpdnsReachability * reachability);
|
||||
typedef void (^NetworkUnreachable)(HttpdnsReachability * reachability);
|
||||
typedef void (^NetworkReachability)(HttpdnsReachability * reachability, SCNetworkConnectionFlags flags);
|
||||
|
||||
|
||||
@interface HttpdnsReachability : NSObject
|
||||
|
||||
@property (nonatomic, copy) NetworkReachable reachableBlock;
|
||||
@property (nonatomic, copy) NetworkUnreachable unreachableBlock;
|
||||
@property (nonatomic, copy) NetworkReachability reachabilityBlock;
|
||||
|
||||
@property (nonatomic, assign) BOOL reachableOnWWAN;
|
||||
|
||||
+(instancetype)sharedInstance;
|
||||
+(instancetype)reachabilityWithHostname:(NSString*)hostname;
|
||||
// This is identical to the function above, but is here to maintain
|
||||
//compatibility with Apples original code. (see .m)
|
||||
+(instancetype)reachabilityWithHostName:(NSString*)hostname;
|
||||
+(instancetype)reachabilityForInternetConnection;
|
||||
+(instancetype)reachabilityWithAddress:(void *)hostAddress;
|
||||
+(instancetype)reachabilityForLocalWiFi;
|
||||
+(instancetype)reachabilityWithURL:(NSURL*)url;
|
||||
|
||||
-(instancetype)initWithReachabilityRef:(SCNetworkReachabilityRef)ref;
|
||||
|
||||
-(BOOL)startNotifier;
|
||||
-(void)stopNotifier;
|
||||
|
||||
-(BOOL)isReachable;
|
||||
-(BOOL)isReachableViaWWAN;
|
||||
-(BOOL)isReachableViaWiFi;
|
||||
|
||||
// WWAN may be available, but not active until a connection has been established.
|
||||
// WiFi may require a connection for VPN on Demand.
|
||||
-(BOOL)isConnectionRequired; // Identical DDG variant.
|
||||
-(BOOL)connectionRequired; // Apple's routine.
|
||||
// Dynamic, on demand connection?
|
||||
-(BOOL)isConnectionOnDemand;
|
||||
// Is user intervention required?
|
||||
-(BOOL)isInterventionRequired;
|
||||
|
||||
-(HttpdnsNetworkStatus)currentReachabilityStatus;
|
||||
-(SCNetworkReachabilityFlags)reachabilityFlags;
|
||||
-(NSString*)currentReachabilityString;
|
||||
-(NSString*)currentReachabilityFlags;
|
||||
|
||||
@end
|
||||
559
EdgeHttpDNS/sdk/ios/AlicloudHttpDNS/Utils/HttpdnsReachability.m
Normal file
559
EdgeHttpDNS/sdk/ios/AlicloudHttpDNS/Utils/HttpdnsReachability.m
Normal file
@@ -0,0 +1,559 @@
|
||||
/*
|
||||
Copyright (c) 2011, Tony Million.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. 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.
|
||||
*/
|
||||
|
||||
#import "HttpdnsReachability.h"
|
||||
|
||||
#import <sys/socket.h>
|
||||
#import <netinet/in.h>
|
||||
#import <netinet6/in6.h>
|
||||
#import <arpa/inet.h>
|
||||
#import <ifaddrs.h>
|
||||
#import <netdb.h>
|
||||
#import <CoreTelephony/CTTelephonyNetworkInfo.h>
|
||||
|
||||
NSString *const kHttpdnsReachabilityChangedNotification = @"kHttpdnsReachabilityChangedNotification";
|
||||
|
||||
static CTTelephonyNetworkInfo *networkInfo;
|
||||
|
||||
@interface HttpdnsReachability ()
|
||||
|
||||
@property (nonatomic, assign) SCNetworkReachabilityRef reachabilityRef;
|
||||
@property (nonatomic, strong) dispatch_queue_t reachabilitySerialQueue;
|
||||
@property (nonatomic, strong) id reachabilityObject;
|
||||
|
||||
-(void)reachabilityChanged:(SCNetworkReachabilityFlags)flags;
|
||||
-(BOOL)isReachableWithFlags:(SCNetworkReachabilityFlags)flags;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
static NSString *reachabilityFlags(SCNetworkReachabilityFlags flags)
|
||||
{
|
||||
return [NSString stringWithFormat:@"%c%c %c%c%c%c%c%c%c",
|
||||
#if TARGET_OS_IPHONE
|
||||
(flags & kSCNetworkReachabilityFlagsIsWWAN) ? 'W' : '-',
|
||||
#else
|
||||
'X',
|
||||
#endif
|
||||
(flags & kSCNetworkReachabilityFlagsReachable) ? 'R' : '-',
|
||||
(flags & kSCNetworkReachabilityFlagsConnectionRequired) ? 'c' : '-',
|
||||
(flags & kSCNetworkReachabilityFlagsTransientConnection) ? 't' : '-',
|
||||
(flags & kSCNetworkReachabilityFlagsInterventionRequired) ? 'i' : '-',
|
||||
(flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) ? 'C' : '-',
|
||||
(flags & kSCNetworkReachabilityFlagsConnectionOnDemand) ? 'D' : '-',
|
||||
(flags & kSCNetworkReachabilityFlagsIsLocalAddress) ? 'l' : '-',
|
||||
(flags & kSCNetworkReachabilityFlagsIsDirect) ? 'd' : '-'];
|
||||
}
|
||||
|
||||
// Start listening for reachability notifications on the current run loop
|
||||
static void TMReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info)
|
||||
{
|
||||
#pragma unused (target)
|
||||
|
||||
HttpdnsReachability *reachability = ((__bridge HttpdnsReachability*)info);
|
||||
|
||||
// We probably don't need an autoreleasepool here, as GCD docs state each queue has its own autorelease pool,
|
||||
// but what the heck eh?
|
||||
@autoreleasepool
|
||||
{
|
||||
[reachability reachabilityChanged:flags];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@implementation HttpdnsReachability
|
||||
|
||||
#pragma mark - Class Constructor Methods
|
||||
+(instancetype)sharedInstance
|
||||
{
|
||||
static HttpdnsReachability *instance;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
networkInfo = [[CTTelephonyNetworkInfo alloc] init];
|
||||
instance = [HttpdnsReachability reachabilityWithHostname:@"www.taobao.com"];
|
||||
});
|
||||
return instance;
|
||||
}
|
||||
|
||||
+(instancetype)reachabilityWithHostName:(NSString*)hostname
|
||||
{
|
||||
return [HttpdnsReachability reachabilityWithHostname:hostname];
|
||||
}
|
||||
|
||||
+(instancetype)reachabilityWithHostname:(NSString*)hostname
|
||||
{
|
||||
SCNetworkReachabilityRef ref = SCNetworkReachabilityCreateWithName(NULL, [hostname UTF8String]);
|
||||
if (ref)
|
||||
{
|
||||
id reachability = [[self alloc] initWithReachabilityRef:ref];
|
||||
return reachability;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
+(instancetype)reachabilityWithAddress:(void *)hostAddress
|
||||
{
|
||||
SCNetworkReachabilityRef ref = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr*)hostAddress);
|
||||
if (ref)
|
||||
{
|
||||
id reachability = [[self alloc] initWithReachabilityRef:ref];
|
||||
|
||||
return reachability;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
+(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);
|
||||
|
||||
return [self reachabilityWithAddress:&localWifiAddress];
|
||||
}
|
||||
|
||||
+(instancetype)reachabilityWithURL:(NSURL*)url
|
||||
{
|
||||
id reachability;
|
||||
|
||||
NSString *host = url.host;
|
||||
BOOL isIpAddress = [self isIpAddress:host];
|
||||
|
||||
if (isIpAddress)
|
||||
{
|
||||
NSNumber *port = url.port ?: [url.scheme isEqualToString:@"https"] ? @(443) : @(80);
|
||||
|
||||
struct sockaddr_in address;
|
||||
address.sin_len = sizeof(address);
|
||||
address.sin_family = AF_INET;
|
||||
address.sin_port = htons([port intValue]);
|
||||
address.sin_addr.s_addr = inet_addr([host UTF8String]);
|
||||
|
||||
reachability = [self reachabilityWithAddress:&address];
|
||||
}
|
||||
else
|
||||
{
|
||||
reachability = [self reachabilityWithHostname:host];
|
||||
}
|
||||
|
||||
return reachability;
|
||||
}
|
||||
|
||||
+(BOOL)isIpAddress:(NSString*)host
|
||||
{
|
||||
struct in_addr pin;
|
||||
return 1 == inet_aton([host UTF8String], &pin);
|
||||
}
|
||||
|
||||
|
||||
// Initialization methods
|
||||
|
||||
-(instancetype)initWithReachabilityRef:(SCNetworkReachabilityRef)ref
|
||||
{
|
||||
self = [super init];
|
||||
if (self != nil)
|
||||
{
|
||||
self.reachableOnWWAN = YES;
|
||||
self.reachabilityRef = ref;
|
||||
|
||||
// We need to create a serial queue.
|
||||
// We allocate this once for the lifetime of the notifier.
|
||||
|
||||
self.reachabilitySerialQueue = dispatch_queue_create("com.tonymillion.reachability", NULL);
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
-(void)dealloc
|
||||
{
|
||||
[self stopNotifier];
|
||||
|
||||
if(self.reachabilityRef)
|
||||
{
|
||||
CFRelease(self.reachabilityRef);
|
||||
self.reachabilityRef = nil;
|
||||
}
|
||||
|
||||
self.reachableBlock = nil;
|
||||
self.unreachableBlock = nil;
|
||||
self.reachabilityBlock = nil;
|
||||
self.reachabilitySerialQueue = nil;
|
||||
}
|
||||
|
||||
#pragma mark - Notifier Methods
|
||||
|
||||
// Notifier
|
||||
// NOTE: This uses GCD to trigger the blocks - they *WILL NOT* be called on THE MAIN THREAD
|
||||
// - In other words DO NOT DO ANY UI UPDATES IN THE BLOCKS.
|
||||
// INSTEAD USE dispatch_async(dispatch_get_main_queue(), ^{UISTUFF}) (or dispatch_sync if you want)
|
||||
|
||||
-(BOOL)startNotifier
|
||||
{
|
||||
// allow start notifier to be called multiple times
|
||||
if(self.reachabilityObject && (self.reachabilityObject == self))
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
||||
SCNetworkReachabilityContext context = { 0, NULL, NULL, NULL, NULL };
|
||||
context.info = (__bridge void *)self;
|
||||
|
||||
if(SCNetworkReachabilitySetCallback(self.reachabilityRef, TMReachabilityCallback, &context))
|
||||
{
|
||||
// Set it as our reachability queue, which will retain the queue
|
||||
if(SCNetworkReachabilitySetDispatchQueue(self.reachabilityRef, self.reachabilitySerialQueue))
|
||||
{
|
||||
// this should do a retain on ourself, so as long as we're in notifier mode we shouldn't disappear out from under ourselves
|
||||
// woah
|
||||
self.reachabilityObject = self;
|
||||
return YES;
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef DEBUG
|
||||
NSLog(@"SCNetworkReachabilitySetDispatchQueue() failed: %s", SCErrorString(SCError()));
|
||||
#endif
|
||||
|
||||
// UH OH - FAILURE - stop any callbacks!
|
||||
SCNetworkReachabilitySetCallback(self.reachabilityRef, NULL, NULL);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef DEBUG
|
||||
NSLog(@"SCNetworkReachabilitySetCallback() failed: %s", SCErrorString(SCError()));
|
||||
#endif
|
||||
}
|
||||
|
||||
// if we get here we fail at the internet
|
||||
self.reachabilityObject = nil;
|
||||
return NO;
|
||||
}
|
||||
|
||||
-(void)stopNotifier
|
||||
{
|
||||
// First stop, any callbacks!
|
||||
SCNetworkReachabilitySetCallback(self.reachabilityRef, NULL, NULL);
|
||||
|
||||
// Unregister target from the GCD serial dispatch queue.
|
||||
SCNetworkReachabilitySetDispatchQueue(self.reachabilityRef, NULL);
|
||||
|
||||
self.reachabilityObject = nil;
|
||||
}
|
||||
|
||||
#pragma mark - reachability tests
|
||||
|
||||
// This is for the case where you flick the airplane mode;
|
||||
// you end up getting something like this:
|
||||
//Reachability: WR ct-----
|
||||
//Reachability: -- -------
|
||||
//Reachability: WR ct-----
|
||||
//Reachability: -- -------
|
||||
// We treat this as 4 UNREACHABLE triggers - really apple should do better than this
|
||||
|
||||
#define testcase (kSCNetworkReachabilityFlagsConnectionRequired | kSCNetworkReachabilityFlagsTransientConnection)
|
||||
|
||||
-(BOOL)isReachableWithFlags:(SCNetworkReachabilityFlags)flags
|
||||
{
|
||||
BOOL connectionUP = YES;
|
||||
|
||||
if(!(flags & kSCNetworkReachabilityFlagsReachable))
|
||||
connectionUP = NO;
|
||||
|
||||
if( (flags & testcase) == testcase )
|
||||
connectionUP = NO;
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
if(flags & kSCNetworkReachabilityFlagsIsWWAN)
|
||||
{
|
||||
// We're on 3G.
|
||||
if(!self.reachableOnWWAN)
|
||||
{
|
||||
// We don't want to connect when on 3G.
|
||||
connectionUP = NO;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return connectionUP;
|
||||
}
|
||||
|
||||
-(BOOL)isReachable
|
||||
{
|
||||
SCNetworkReachabilityFlags flags;
|
||||
|
||||
if(!SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags))
|
||||
return NO;
|
||||
|
||||
return [self isReachableWithFlags:flags];
|
||||
}
|
||||
|
||||
-(BOOL)isReachableViaWWAN
|
||||
{
|
||||
#if TARGET_OS_IPHONE
|
||||
SCNetworkReachabilityFlags flags = 0;
|
||||
|
||||
if(SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags))
|
||||
{
|
||||
// Check we're REACHABLE
|
||||
if(flags & kSCNetworkReachabilityFlagsReachable)
|
||||
{
|
||||
// Now, check we're on WWAN
|
||||
if(flags & kSCNetworkReachabilityFlagsIsWWAN)
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
-(BOOL)isReachableViaWiFi
|
||||
{
|
||||
SCNetworkReachabilityFlags flags = 0;
|
||||
|
||||
if(SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags))
|
||||
{
|
||||
// Check we're reachable
|
||||
if((flags & kSCNetworkReachabilityFlagsReachable))
|
||||
{
|
||||
#if TARGET_OS_IPHONE
|
||||
// Check we're NOT on WWAN
|
||||
if((flags & kSCNetworkReachabilityFlagsIsWWAN))
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
#endif
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
|
||||
// WWAN may be available, but not active until a connection has been established.
|
||||
// WiFi may require a connection for VPN on Demand.
|
||||
-(BOOL)isConnectionRequired
|
||||
{
|
||||
return [self connectionRequired];
|
||||
}
|
||||
|
||||
-(BOOL)connectionRequired
|
||||
{
|
||||
SCNetworkReachabilityFlags flags;
|
||||
|
||||
if(SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags))
|
||||
{
|
||||
return (flags & kSCNetworkReachabilityFlagsConnectionRequired);
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
// Dynamic, on demand connection?
|
||||
-(BOOL)isConnectionOnDemand
|
||||
{
|
||||
SCNetworkReachabilityFlags flags;
|
||||
|
||||
if (SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags))
|
||||
{
|
||||
return ((flags & kSCNetworkReachabilityFlagsConnectionRequired) &&
|
||||
(flags & (kSCNetworkReachabilityFlagsConnectionOnTraffic | kSCNetworkReachabilityFlagsConnectionOnDemand)));
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
// Is user intervention required?
|
||||
-(BOOL)isInterventionRequired
|
||||
{
|
||||
SCNetworkReachabilityFlags flags;
|
||||
|
||||
if (SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags))
|
||||
{
|
||||
return ((flags & kSCNetworkReachabilityFlagsConnectionRequired) &&
|
||||
(flags & kSCNetworkReachabilityFlagsInterventionRequired));
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - reachability status stuff
|
||||
|
||||
-(HttpdnsNetworkStatus)currentReachabilityStatus
|
||||
{
|
||||
if([self isReachable])
|
||||
{
|
||||
if([self isReachableViaWiFi])
|
||||
{
|
||||
return HttpdnsReachableViaWiFi;
|
||||
}
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
NSString *nettype = networkInfo.currentRadioAccessTechnology;
|
||||
if (nettype)
|
||||
{
|
||||
if ([CTRadioAccessTechnologyGPRS isEqualToString:nettype] ||
|
||||
[CTRadioAccessTechnologyEdge isEqualToString:nettype] ||
|
||||
[CTRadioAccessTechnologyCDMA1x isEqualToString:nettype])
|
||||
{
|
||||
return HttpdnsReachableVia2G;
|
||||
}
|
||||
else if ([CTRadioAccessTechnologyLTE isEqualToString:nettype])
|
||||
{
|
||||
return HttpdnsReachableVia4G;
|
||||
}
|
||||
else if ([CTRadioAccessTechnologyWCDMA isEqualToString:nettype] ||
|
||||
[CTRadioAccessTechnologyHSDPA isEqualToString:nettype] ||
|
||||
[CTRadioAccessTechnologyHSUPA isEqualToString:nettype] ||
|
||||
[CTRadioAccessTechnologyCDMAEVDORev0 isEqualToString:nettype] ||
|
||||
[CTRadioAccessTechnologyCDMAEVDORevA isEqualToString:nettype] ||
|
||||
[CTRadioAccessTechnologyCDMAEVDORevB isEqualToString:nettype] ||
|
||||
[CTRadioAccessTechnologyeHRPD isEqualToString:nettype])
|
||||
{
|
||||
return HttpdnsReachableVia3G;
|
||||
}
|
||||
else
|
||||
{
|
||||
return HttpdnsReachableVia5G;
|
||||
}
|
||||
}
|
||||
// 默认以使用最广泛的4G兜底
|
||||
return HttpdnsReachableVia4G;
|
||||
#endif
|
||||
}
|
||||
|
||||
return HttpdnsNotReachable;
|
||||
}
|
||||
|
||||
-(SCNetworkReachabilityFlags)reachabilityFlags
|
||||
{
|
||||
SCNetworkReachabilityFlags flags = 0;
|
||||
|
||||
if(SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags))
|
||||
{
|
||||
return flags;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
-(NSString*)currentReachabilityString
|
||||
{
|
||||
HttpdnsNetworkStatus temp = [self currentReachabilityStatus];
|
||||
|
||||
if(temp == HttpdnsReachableVia2G)
|
||||
{
|
||||
return NSLocalizedString(@"2G", @"");
|
||||
}
|
||||
if(temp == HttpdnsReachableVia3G)
|
||||
{
|
||||
return NSLocalizedString(@"3G", @"");
|
||||
}
|
||||
if(temp == HttpdnsReachableVia4G)
|
||||
{
|
||||
return NSLocalizedString(@"4G", @"");
|
||||
}
|
||||
if(temp == HttpdnsReachableVia5G)
|
||||
{
|
||||
return NSLocalizedString(@"5G", @"");
|
||||
}
|
||||
if (temp == HttpdnsReachableViaWiFi)
|
||||
{
|
||||
// 旧版本上报的wifi标识为全小写,要保持兼容
|
||||
return NSLocalizedString(@"wifi", @"");
|
||||
}
|
||||
return NSLocalizedString(@"unknown", @"");
|
||||
}
|
||||
|
||||
-(NSString*)currentReachabilityFlags
|
||||
{
|
||||
return reachabilityFlags([self reachabilityFlags]);
|
||||
}
|
||||
|
||||
#pragma mark - Callback function calls this method
|
||||
|
||||
-(void)reachabilityChanged:(SCNetworkReachabilityFlags)flags
|
||||
{
|
||||
if([self isReachableWithFlags:flags])
|
||||
{
|
||||
if(self.reachableBlock)
|
||||
{
|
||||
self.reachableBlock(self);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(self.unreachableBlock)
|
||||
{
|
||||
self.unreachableBlock(self);
|
||||
}
|
||||
}
|
||||
|
||||
if(self.reachabilityBlock)
|
||||
{
|
||||
self.reachabilityBlock(self, flags);
|
||||
}
|
||||
|
||||
// this makes sure the change notification happens on the MAIN THREAD
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:kHttpdnsReachabilityChangedNotification
|
||||
object:self];
|
||||
});
|
||||
}
|
||||
|
||||
#pragma mark - Debug Description
|
||||
|
||||
- (NSString *) description
|
||||
{
|
||||
NSString *description = [NSString stringWithFormat:@"<%@: %p (%@)>",
|
||||
NSStringFromClass([self class]), self, [self currentReachabilityFlags]];
|
||||
return description;
|
||||
}
|
||||
|
||||
@end
|
||||
81
EdgeHttpDNS/sdk/ios/AlicloudHttpDNS/Utils/HttpdnsUtil.h
Normal file
81
EdgeHttpDNS/sdk/ios/AlicloudHttpDNS/Utils/HttpdnsUtil.h
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@class HttpDnsService;
|
||||
|
||||
@class HttpdnsHostObject;
|
||||
|
||||
@interface HttpdnsUtil : NSObject
|
||||
|
||||
+ (BOOL)isIPv4Address:(NSString *)addr;
|
||||
|
||||
+ (BOOL)isIPv6Address:(NSString *)addr;
|
||||
|
||||
+ (BOOL)isAnIP:(NSString *)candidate;
|
||||
|
||||
+ (BOOL)isAHost:(NSString *)host;
|
||||
|
||||
+ (void)warnMainThreadIfNecessary;
|
||||
|
||||
+ (NSDictionary *)getValidDictionaryFromJson:(id)jsonValue;
|
||||
|
||||
+ (BOOL)isEmptyArray:(NSArray *)inputArr;
|
||||
|
||||
+ (BOOL)isNotEmptyArray:(NSArray *)inputArr;
|
||||
|
||||
+ (BOOL)isEmptyString:(NSString *)inputStr;
|
||||
|
||||
+ (BOOL)isNotEmptyString:(NSString *)inputStr;
|
||||
|
||||
+ (BOOL)isValidJSON:(id)JSON;
|
||||
|
||||
+ (BOOL)isEmptyDictionary:(NSDictionary *)inputDict;
|
||||
|
||||
+ (BOOL)isNotEmptyDictionary:(NSDictionary *)inputDict;
|
||||
|
||||
+ (NSArray *)joinArrays:(NSArray *)array1 withArray:(NSArray *)array2;
|
||||
|
||||
+ (NSString *)getMD5StringFrom:(NSString *)originString;
|
||||
|
||||
+ (NSString *)URLEncodedString:(NSString *)str;
|
||||
|
||||
+ (NSString *)generateSessionID;
|
||||
|
||||
+ (NSString *)generateUserAgent;
|
||||
|
||||
+ (NSData *)encryptDataAESCBC:(NSData *)plaintext
|
||||
withKey:(NSData *)key
|
||||
error:(NSError **)error;
|
||||
|
||||
+ (NSData *)decryptDataAESCBC:(NSData *)ciphertext
|
||||
withKey:(NSData *)key
|
||||
error:(NSError **)error;
|
||||
|
||||
+ (NSString *)hexStringFromData:(NSData *)data;
|
||||
|
||||
+ (NSData *)dataFromHexString:(NSString *)hexString;
|
||||
|
||||
+ (NSString *)hmacSha256:(NSString *)data key:(NSString *)key;
|
||||
|
||||
+ (void)processCustomTTL:(HttpdnsHostObject *)hostObject forHost:(NSString *)host;
|
||||
+ (void)processCustomTTL:(HttpdnsHostObject *)hostObject forHost:(NSString *)host service:(HttpDnsService *)service;
|
||||
|
||||
@end
|
||||
471
EdgeHttpDNS/sdk/ios/AlicloudHttpDNS/Utils/HttpdnsUtil.m
Normal file
471
EdgeHttpDNS/sdk/ios/AlicloudHttpDNS/Utils/HttpdnsUtil.m
Normal file
@@ -0,0 +1,471 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "HttpdnsUtil.h"
|
||||
#import "HttpdnsLog_Internal.h"
|
||||
#import "CommonCrypto/CommonCrypto.h"
|
||||
#import "arpa/inet.h"
|
||||
#import "HttpdnsPublicConstant.h"
|
||||
#import "httpdnsReachability.h"
|
||||
#import "HttpdnsInternalConstant.h"
|
||||
#import "HttpdnsHostObject.h"
|
||||
#import "HttpdnsService.h"
|
||||
|
||||
@implementation HttpdnsUtil
|
||||
|
||||
+ (BOOL)isIPv4Address:(NSString *)addr {
|
||||
if (!addr) {
|
||||
return NO;
|
||||
}
|
||||
struct in_addr dst;
|
||||
return inet_pton(AF_INET, [addr UTF8String], &(dst.s_addr)) == 1;
|
||||
}
|
||||
|
||||
+ (BOOL)isIPv6Address:(NSString *)addr {
|
||||
if (!addr) {
|
||||
return NO;
|
||||
}
|
||||
struct in6_addr dst6;
|
||||
return inet_pton(AF_INET6, [addr UTF8String], &dst6) == 1;
|
||||
}
|
||||
|
||||
+ (BOOL)isAnIP:(NSString *)candidate {
|
||||
if ([self isIPv4Address:candidate]) {
|
||||
return YES;
|
||||
}
|
||||
if ([self isIPv6Address:candidate]) {
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
+ (BOOL)isAHost:(NSString *)host {
|
||||
static dispatch_once_t once_token;
|
||||
static NSRegularExpression *hostExpression = nil;
|
||||
dispatch_once(&once_token, ^{
|
||||
hostExpression = [[NSRegularExpression alloc] initWithPattern:@"^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\\-]*[A-Za-z0-9])$" options:NSRegularExpressionCaseInsensitive error:nil];
|
||||
});
|
||||
|
||||
if (!host.length) {
|
||||
return NO;
|
||||
}
|
||||
NSTextCheckingResult *checkResult = [hostExpression firstMatchInString:host options:0 range:NSMakeRange(0, [host length])];
|
||||
if (checkResult.range.length == [host length]) {
|
||||
return YES;
|
||||
} else {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
+ (void)warnMainThreadIfNecessary {
|
||||
if ([NSThread isMainThread]) {
|
||||
HttpdnsLogDebug("Warning: A long-running Paas operation is being executed on the main thread.");
|
||||
}
|
||||
}
|
||||
|
||||
+ (NSDictionary *)getValidDictionaryFromJson:(id)jsonValue {
|
||||
NSDictionary *dictionaryValueFromJson = nil;
|
||||
if ([jsonValue isKindOfClass:[NSDictionary class]]) {
|
||||
if ([(NSDictionary *)jsonValue allKeys].count > 0) {
|
||||
NSMutableDictionary *mutableDict = [NSMutableDictionary dictionaryWithDictionary:jsonValue];
|
||||
@try {
|
||||
[self removeNSNullValueFromDictionary:mutableDict];
|
||||
} @catch (NSException *exception) {}
|
||||
dictionaryValueFromJson = [jsonValue copy];
|
||||
}
|
||||
}
|
||||
return dictionaryValueFromJson;
|
||||
}
|
||||
|
||||
+ (void)removeNSNullValueFromArray:(NSMutableArray *)array {
|
||||
NSMutableArray *objToRemove = nil;
|
||||
NSMutableIndexSet *indexToReplace = [[NSMutableIndexSet alloc] init];
|
||||
NSMutableArray *objForReplace = [[NSMutableArray alloc] init];
|
||||
for (int i = 0; i < array.count; ++i) {
|
||||
id value = [array objectAtIndex:i];
|
||||
if ([value isKindOfClass:[NSNull class]]) {
|
||||
if (!objToRemove) {
|
||||
objToRemove = [[NSMutableArray alloc] init];
|
||||
}
|
||||
[objToRemove addObject:value];
|
||||
} else if ([value isKindOfClass:[NSDictionary class]]) {
|
||||
NSMutableDictionary *mutableDict = [NSMutableDictionary dictionaryWithDictionary:value];
|
||||
[self removeNSNullValueFromDictionary:mutableDict];
|
||||
[indexToReplace addIndex:i];
|
||||
[objForReplace addObject:mutableDict];
|
||||
} else if ([value isKindOfClass:[NSArray class]]) {
|
||||
NSMutableArray *v = [value mutableCopy];
|
||||
[self removeNSNullValueFromArray:v];
|
||||
[indexToReplace addIndex:i];
|
||||
[objForReplace addObject:v];
|
||||
}
|
||||
}
|
||||
[array replaceObjectsAtIndexes:indexToReplace withObjects:objForReplace];
|
||||
if (objToRemove) {
|
||||
[array removeObjectsInArray:objToRemove];
|
||||
}
|
||||
}
|
||||
|
||||
+ (void)removeNSNullValueFromDictionary:(NSMutableDictionary *)dict {
|
||||
for (id key in [dict allKeys]) {
|
||||
id value = [dict objectForKey:key];
|
||||
if ([value isKindOfClass:[NSNull class]]) {
|
||||
[dict removeObjectForKey:key];
|
||||
} else if ([value isKindOfClass:[NSDictionary class]]) {
|
||||
NSMutableDictionary *mutableDict = [NSMutableDictionary dictionaryWithDictionary:value];
|
||||
[self removeNSNullValueFromDictionary:mutableDict];
|
||||
[dict setObject:mutableDict forKey:key];
|
||||
} else if ([value isKindOfClass:[NSArray class]]) {
|
||||
NSMutableArray *v = [value mutableCopy];
|
||||
[self removeNSNullValueFromArray:v];
|
||||
[dict setObject:v forKey:key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+ (BOOL)isEmptyArray:(NSArray *)inputArr {
|
||||
if (!inputArr) {
|
||||
return YES;
|
||||
}
|
||||
return [inputArr count] == 0;
|
||||
}
|
||||
|
||||
+ (BOOL)isNotEmptyArray:(NSArray *)inputArr {
|
||||
return ![self isEmptyArray:inputArr];
|
||||
}
|
||||
|
||||
+ (BOOL)isEmptyString:(NSString *)inputStr {
|
||||
if (!inputStr) {
|
||||
return YES;
|
||||
}
|
||||
return [inputStr length] == 0;
|
||||
}
|
||||
|
||||
+ (BOOL)isNotEmptyString:(NSString *)inputStr {
|
||||
return ![self isEmptyString:inputStr];
|
||||
}
|
||||
|
||||
+ (BOOL)isValidJSON:(id)JSON {
|
||||
BOOL isValid;
|
||||
@try {
|
||||
isValid = ([JSON isKindOfClass:[NSDictionary class]] || [JSON isKindOfClass:[NSArray class]]);
|
||||
} @catch (NSException *ignore) {
|
||||
}
|
||||
return isValid;
|
||||
}
|
||||
|
||||
+ (BOOL)isEmptyDictionary:(NSDictionary *)inputDict {
|
||||
if (!inputDict) {
|
||||
return YES;
|
||||
}
|
||||
return [inputDict count] == 0;
|
||||
}
|
||||
|
||||
+ (BOOL)isNotEmptyDictionary:(NSDictionary *)inputDict {
|
||||
return ![self isEmptyDictionary:inputDict];
|
||||
}
|
||||
|
||||
+ (NSArray *)joinArrays:(NSArray *)array1 withArray:(NSArray *)array2 {
|
||||
NSMutableArray *resultArray = [array1 mutableCopy];
|
||||
[resultArray addObjectsFromArray:array2];
|
||||
return [resultArray copy];
|
||||
}
|
||||
|
||||
+ (id)convertJsonDataToObject:(NSData *)jsonData {
|
||||
if (jsonData) {
|
||||
NSError *error;
|
||||
id jsonObj = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:&error];
|
||||
if (!error) {
|
||||
return jsonObj;
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (NSString *)getMD5StringFrom:(NSString *)originString {
|
||||
const char * pointer = [originString UTF8String];
|
||||
unsigned char md5Buffer[CC_MD5_DIGEST_LENGTH];
|
||||
|
||||
CC_MD5(pointer, (CC_LONG)strlen(pointer), md5Buffer);
|
||||
|
||||
NSMutableString *string = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH * 2];
|
||||
for (int i = 0; i < CC_MD5_DIGEST_LENGTH; i++)
|
||||
[string appendFormat:@"%02x",md5Buffer[i]];
|
||||
|
||||
return [string copy];
|
||||
}
|
||||
|
||||
+ (NSString *)URLEncodedString:(NSString *)str {
|
||||
if (str) {
|
||||
return [str stringByAddingPercentEncodingWithAllowedCharacters:
|
||||
[NSCharacterSet characterSetWithCharactersInString:@"!*'();:@&=+$,/?%#[]\""].invertedSet];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
/**
|
||||
生成sessionId
|
||||
App打开生命周期只生成一次,不做持久化
|
||||
sessionId为12位,采用base62编码
|
||||
*/
|
||||
+ (NSString *)generateSessionID {
|
||||
static NSString *sessionId = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
NSString *alphabet = @"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
NSUInteger length = alphabet.length;
|
||||
if (![HttpdnsUtil isNotEmptyString:sessionId]) {
|
||||
NSMutableString *mSessionId = [NSMutableString string];
|
||||
for (int i = 0; i < 12; i++) {
|
||||
[mSessionId appendFormat:@"%@", [alphabet substringWithRange:NSMakeRange(arc4random() % length, 1)]];
|
||||
}
|
||||
sessionId = [mSessionId copy];
|
||||
}
|
||||
});
|
||||
return sessionId;
|
||||
}
|
||||
|
||||
+ (NSString *)generateUserAgent {
|
||||
UIDevice *device = [UIDevice currentDevice];
|
||||
NSString *systemName = [device systemName];
|
||||
NSString *systemVersion = [device systemVersion];
|
||||
NSString *model = [device model];
|
||||
|
||||
NSString *userAgent = [NSString stringWithFormat:@"HttpdnsSDK/%@ (%@; iOS %@; %@)", HTTPDNS_IOS_SDK_VERSION, model, systemVersion, systemName];
|
||||
|
||||
return userAgent;
|
||||
}
|
||||
|
||||
+ (NSData *)encryptDataAESCBC:(NSData *)plaintext
|
||||
withKey:(NSData *)key
|
||||
error:(NSError **)error {
|
||||
// 检查输入参数
|
||||
if (plaintext == nil || [plaintext length] == 0 || key == nil || [key length] != kCCKeySizeAES128) {
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:ALICLOUD_HTTPDNS_ERROR_DOMAIN
|
||||
code:ALICLOUD_HTTPDNS_ENCRYPT_INVALID_PARAMS_ERROR_CODE
|
||||
userInfo:@{NSLocalizedDescriptionKey: @"Invalid input parameters"}];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
// 为CBC模式生成128bit(16字节)的随机IV
|
||||
NSMutableData *iv = [NSMutableData dataWithLength:kCCBlockSizeAES128];
|
||||
int result = SecRandomCopyBytes(kSecRandomDefault, kCCBlockSizeAES128, iv.mutableBytes);
|
||||
if (result != 0) {
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:ALICLOUD_HTTPDNS_ERROR_DOMAIN
|
||||
code:ALICLOUD_HTTPDNS_ENCRYPT_RANDOM_IV_ERROR_CODE
|
||||
userInfo:@{NSLocalizedDescriptionKey: @"Failed to generate random IV"}];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
// 计算加密后的数据长度 (可能需要填充)
|
||||
size_t bufferSize = [plaintext length] + kCCBlockSizeAES128;
|
||||
size_t encryptedSize = 0;
|
||||
|
||||
// 创建输出缓冲区
|
||||
NSMutableData *cipherData = [NSMutableData dataWithLength:bufferSize];
|
||||
|
||||
// 执行加密
|
||||
// AES中PKCS5Padding与PKCS7Padding相同
|
||||
CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt,
|
||||
kCCAlgorithmAES,
|
||||
kCCOptionPKCS7Padding,
|
||||
[key bytes],
|
||||
[key length],
|
||||
[iv bytes],
|
||||
[plaintext bytes],
|
||||
[plaintext length],
|
||||
[cipherData mutableBytes],
|
||||
[cipherData length],
|
||||
&encryptedSize);
|
||||
|
||||
if (cryptStatus != kCCSuccess) {
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:ALICLOUD_HTTPDNS_ERROR_DOMAIN
|
||||
code:ALICLOUD_HTTPDNS_ENCRYPT_FAILED_ERROR_CODE
|
||||
userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Encryption failed with status: %d", cryptStatus]}];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
// 调整加密数据的长度,只保留实际加密内容
|
||||
[cipherData setLength:encryptedSize];
|
||||
|
||||
// 将IV和加密数据合并在一起
|
||||
NSMutableData *resultData = [NSMutableData dataWithData:iv];
|
||||
[resultData appendData:cipherData];
|
||||
|
||||
return resultData;
|
||||
}
|
||||
|
||||
+ (NSString *)hexStringFromData:(NSData *)data {
|
||||
if (!data || data.length == 0) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSMutableString *hexString = [NSMutableString stringWithCapacity:data.length * 2];
|
||||
const unsigned char *bytes = data.bytes;
|
||||
|
||||
for (NSInteger i = 0; i < data.length; i++) {
|
||||
[hexString appendFormat:@"%02x", bytes[i]];
|
||||
}
|
||||
|
||||
return [hexString copy];
|
||||
}
|
||||
|
||||
+ (NSData *)dataFromHexString:(NSString *)hexString {
|
||||
if (!hexString || hexString.length == 0) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
// 移除可能存在的空格
|
||||
NSString *cleanedString = [hexString stringByReplacingOccurrencesOfString:@" " withString:@""];
|
||||
|
||||
// 确保字符串长度为偶数
|
||||
if (cleanedString.length % 2 != 0) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSMutableData *data = [NSMutableData dataWithCapacity:cleanedString.length / 2];
|
||||
for (NSUInteger i = 0; i < cleanedString.length; i += 2) {
|
||||
NSString *byteString = [cleanedString substringWithRange:NSMakeRange(i, 2)];
|
||||
NSScanner *scanner = [NSScanner scannerWithString:byteString];
|
||||
|
||||
unsigned int byteValue;
|
||||
if (![scanner scanHexInt:&byteValue]) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
uint8_t byte = (uint8_t)byteValue;
|
||||
[data appendBytes:&byte length:1];
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
+ (NSString *)hmacSha256:(NSString *)data key:(NSString *)key {
|
||||
if (!data || !key) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
// 将十六进制密钥转换为NSData
|
||||
NSData *keyData = [self dataFromHexString:key];
|
||||
if (!keyData) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
// 数据转换
|
||||
NSData *dataToSign = [data dataUsingEncoding:NSUTF8StringEncoding];
|
||||
|
||||
// 计算HMAC
|
||||
uint8_t digestBytes[CC_SHA256_DIGEST_LENGTH];
|
||||
CCHmac(kCCHmacAlgSHA256, keyData.bytes, keyData.length, dataToSign.bytes, dataToSign.length, digestBytes);
|
||||
|
||||
// 创建结果数据
|
||||
NSData *hmacData = [NSData dataWithBytes:digestBytes length:CC_SHA256_DIGEST_LENGTH];
|
||||
|
||||
// 转换为十六进制字符串
|
||||
return [self hexStringFromData:hmacData];
|
||||
}
|
||||
|
||||
+ (NSData *)decryptDataAESCBC:(NSData *)ciphertext
|
||||
withKey:(NSData *)key
|
||||
error:(NSError **)error {
|
||||
// 检查输入参数
|
||||
if (ciphertext == nil || [ciphertext length] <= kCCBlockSizeAES128 || key == nil || [key length] != kCCKeySizeAES128) {
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:ALICLOUD_HTTPDNS_ERROR_DOMAIN
|
||||
code:ALICLOUD_HTTPDNS_ENCRYPT_INVALID_PARAMS_ERROR_CODE
|
||||
userInfo:@{NSLocalizedDescriptionKey: @"Invalid input parameters for decryption"}];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
// 提取IV(前16字节)和实际的密文
|
||||
NSData *iv = [ciphertext subdataWithRange:NSMakeRange(0, kCCBlockSizeAES128)];
|
||||
NSData *actualCiphertext = [ciphertext subdataWithRange:NSMakeRange(kCCBlockSizeAES128, ciphertext.length - kCCBlockSizeAES128)];
|
||||
|
||||
// 计算解密后可能的缓冲区大小
|
||||
size_t bufferSize = actualCiphertext.length + kCCBlockSizeAES128;
|
||||
size_t decryptedSize = 0;
|
||||
|
||||
// 创建输出缓冲区
|
||||
NSMutableData *decryptedData = [NSMutableData dataWithLength:bufferSize];
|
||||
|
||||
// 执行解密
|
||||
CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt,
|
||||
kCCAlgorithmAES,
|
||||
kCCOptionPKCS7Padding,
|
||||
[key bytes],
|
||||
[key length],
|
||||
[iv bytes],
|
||||
[actualCiphertext bytes],
|
||||
[actualCiphertext length],
|
||||
[decryptedData mutableBytes],
|
||||
[decryptedData length],
|
||||
&decryptedSize);
|
||||
|
||||
if (cryptStatus != kCCSuccess) {
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:ALICLOUD_HTTPDNS_ERROR_DOMAIN
|
||||
code:ALICLOUD_HTTPDNS_ENCRYPT_FAILED_ERROR_CODE
|
||||
userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Decryption failed with status: %d", cryptStatus]}];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
// 调整解密数据的长度,只保留实际解密内容
|
||||
[decryptedData setLength:decryptedSize];
|
||||
|
||||
return decryptedData;
|
||||
}
|
||||
|
||||
+ (void)processCustomTTL:(HttpdnsHostObject *)hostObject forHost:(NSString *)host {
|
||||
[self processCustomTTL:hostObject forHost:host service:[HttpDnsService sharedInstance]];
|
||||
}
|
||||
|
||||
+ (void)processCustomTTL:(HttpdnsHostObject *)hostObject forHost:(NSString *)host service:(HttpDnsService *)service {
|
||||
if (!hostObject || !host) {
|
||||
return;
|
||||
}
|
||||
|
||||
HttpDnsService *dnsService = service ?: [HttpDnsService sharedInstance];
|
||||
|
||||
if (dnsService.ttlDelegate && [dnsService.ttlDelegate respondsToSelector:@selector(httpdnsHost:ipType:ttl:)]) {
|
||||
if ([self isNotEmptyArray:[hostObject getV4Ips]]) {
|
||||
int64_t customV4TTL = [dnsService.ttlDelegate httpdnsHost:host ipType:AlicloudHttpDNS_IPTypeV4 ttl:hostObject.v4ttl];
|
||||
if (customV4TTL > 0) {
|
||||
hostObject.v4ttl = customV4TTL;
|
||||
}
|
||||
}
|
||||
|
||||
if ([self isNotEmptyArray:[hostObject getV6Ips]]) {
|
||||
int64_t customV6TTL = [dnsService.ttlDelegate httpdnsHost:host ipType:AlicloudHttpDNS_IPTypeV6 ttl:hostObject.v6ttl];
|
||||
if (customV6TTL > 0) {
|
||||
hostObject.v6ttl = customV6TTL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user