解决了语音上传到问题,接下来要解决下载播放问题

This commit is contained in:
joywayer
2025-06-15 12:36:47 +08:00
parent bba3ed1cb4
commit c11fc62bf1
513 changed files with 31197 additions and 2969 deletions

8
Podfile Normal file
View File

@@ -0,0 +1,8 @@
source 'https://mirrors.tuna.tsinghua.edu.cn/git/CocoaPods/Specs.git'
platform :ios, '9.0'
target 'msext' do
# 添加七牛云SDK
pod 'Qiniu', '~> 8.0'
end

20
Podfile.lock Normal file
View File

@@ -0,0 +1,20 @@
PODS:
- HappyDNS (1.0.4)
- Qiniu (8.8.1):
- HappyDNS (~> 1.0.4)
DEPENDENCIES:
- Qiniu (~> 8.0)
SPEC REPOS:
https://mirrors.tuna.tsinghua.edu.cn/git/CocoaPods/Specs.git:
- HappyDNS
- Qiniu
SPEC CHECKSUMS:
HappyDNS: aefbd28cdcda93cffac64013dfe3342a2f87ed0a
Qiniu: 37131e52fdf43fe70da0c89dd0df3c9ce25bb74b
PODFILE CHECKSUM: 4aa3a53aaead366c96efe2b5f93ec0e4c4468bc0
COCOAPODS: 1.15.2

View File

@@ -0,0 +1,36 @@
//
// QNDnsError.h
// Doh
//
// Created by yangsen on 2021/7/20.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
extern const int kQNDomainHijackingCode;
extern const int kQNDomainNotOwnCode;
extern const int kQNDomainSeverError;
extern const int kQNDnsMethodErrorCode;
extern const int kQNDnsInvalidParamCode;
extern const int kQNDnsResponseBadTypeCode;
extern const int kQNDnsResponseBadClassCode;
extern const int kQNDnsResponseFormatCode;
#define kQNDnsErrorDomain @"qiniu.dns"
@interface QNDnsError : NSObject
+ (NSError *)error:(int)code desc:(NSString *)desc;
@end
#define kQNDnsMethodError(description) [QNDnsError error:kQNDnsMethodErrorCode desc:description]
#define kQNDnsInvalidParamError(description) [QNDnsError error:kQNDnsInvalidParamCode desc:description]
#define kQNDnsResponseBadTypeError(description) [QNDnsError error:kQNDnsResponseBadTypeCode desc:description]
#define kQNDnsResponseBadClassError(description) [QNDnsError error:kQNDnsResponseBadClassCode desc:description]
#define kQNDnsResponseFormatError(description) [QNDnsError error:kQNDnsResponseFormatCode desc:description]
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,27 @@
//
// QNDnsError.m
// Doh
//
// Created by yangsen on 2021/7/20.
//
#import "QNDnsError.h"
const int kQNDomainHijackingCode = -7001;
const int kQNDomainNotOwnCode = -7002;
const int kQNDomainSeverError = -7003;
const int kQNDnsMethodErrorCode = -7010;
const int kQNDnsInvalidParamCode = -7021;
const int kQNDnsResponseBadTypeCode = -7022;
const int kQNDnsResponseBadClassCode = -7023;
const int kQNDnsResponseFormatCode = -7024;
@implementation QNDnsError
+ (NSError *)error:(int)code desc:(NSString *)desc {
return [NSError errorWithDomain:kQNDnsErrorDomain code:code userInfo:@{@"user_info" : desc ?: @"nil"}];
}
@end

View File

@@ -0,0 +1,174 @@
//
// QNDnsManager.h
// HappyDNS
//
// Created by bailong on 15/6/23.
// Copyright (c) 2015年 Qiniu Cloud Storage. All rights reserved.
//
#import "QNRecord.h"
@class QNNetworkInfo;
@class QNDomain;
/**
* getaddrinfo 回调上层的函数
*
* @param host 请求的域名
* @return ip 列表
*/
typedef NSArray<NSString *> * (^QNGetAddrInfoCallback)(NSString *host);
/**
* ip status 回调上层的函数
*
* @param ip 请求的IP
* @param code 错误码
* @param ms 消耗时间
*/
typedef void (^QNIpStatusCallback)(NSString *ip, int code, int ms);
/**
* 外部 Record 排序接口
*/
@protocol QNRecordSorter <NSObject>
/**
* 排序方法
*
* @param ips 传入的IP列表
*
* @return 返回排序好的IP 列表
*/
- (NSArray<QNRecord *> *)sort:(NSArray<QNRecord *> *)ips;
@end
/**
* DNS请求客户端集成了cache管理
*/
@interface QNDnsManager : NSObject
/// 查询失败时抛出错误信息回调
@property(nonatomic, copy)void(^queryErrorHandler)(NSError *error, NSString *host);
/**
* 解析域名
*
* @param domain 域名
*
* @return QNRecord列表 QNRecord.value即为host
*/
- (NSArray <QNRecord *> *)queryRecords:(NSString *)domain;
/**
* 解析域名使用Domain对象进行详细约定
*
* @param domain 配置了一些domain 参数的 domain 对象
*
* @return IP 列表
*/
- (NSArray <QNRecord *> *)queryRecordsWithDomain:(QNDomain *)domain;
/**
* 通知网络发生变化
*
* @param netInfo 网络信息
*/
- (void)onNetworkChange:(QNNetworkInfo *)netInfo;
/**
* Dns client 初始化
*
* @param resolvers 解析服务器列表
* @param netInfo 当前网络信息
*
* @return DnsManager
*/
- (instancetype)init:(NSArray *)resolvers networkInfo:(QNNetworkInfo *)netInfo;
/**
* Dns client 初始化
*
* @param resolvers 解析服务器列表
* @param netInfo 当前网络信息
* @param sorter 外部排序函数
*
* @return DnsManager
*/
- (instancetype)init:(NSArray *)resolvers networkInfo:(QNNetworkInfo *)netInfo sorter:(id<QNRecordSorter>)sorter;
/**
* 内置 Hosts 解析
*
* @param domain 域名
* @param ipv4 对应IPv4 ip
*
* @return 当前Dnsmanager, 为了链式调用
*/
- (instancetype)putHosts:(NSString *)domain ipv4:(NSString *)ipv4;
/**
* 内置 Hosts 解析
*
* @param domain 域名
* @param ip 对应IP
* @param type ip 类别kQNTypeA / kQNTypeAAAA
* @param provider 网络运营商
*
* @return 当前Dnsmanager, 为了链式调用
*/
- (instancetype)putHosts:(NSString *)domain ip:(NSString *)ip type:(int)type provider:(int)provider;
/**
* 内置 Hosts 解析
*
* @param domain 域名
* @param record 对应 record 记录
* @param provider 网络运营商
*
* @return 当前Dnsmanager, 为了链式调用
*/
- (instancetype)putHosts:(NSString *)domain record:(QNRecord *)record provider:(int)provider;
/**
* 设置底层 getaddrinfo 使用的回调
*
* @param block 回调的代码块
*/
+ (void)setGetAddrInfoBlock:(QNGetAddrInfoCallback)block;
/**
* 设置底层 getaddrinfo 回调使用的dnsmanager
*
* @param dns 回调用的dnsmanager
*/
+ (void)setDnsManagerForGetAddrInfo:(QNDnsManager *)dns;
/**
* 设置底层 业务统计 如connect 回调使用的Callback
*
* @param block 回调返回该IP状态
*/
+ (void)setIpStatusCallback:(QNIpStatusCallback)block;
/**
* 根据时区判断是否要设置httpDns
*/
+ (BOOL)needHttpDns;
@end
/**
* DnsManager 的 URL 辅助类
*/
@interface QNDnsManager (NSURL)
/**
* 使用URL 进行请求
*
* @param url 请求的Url
*
* @return 返回IP 替换过的url
*/
- (NSURL *)queryAndReplaceWithIP:(NSURL *)url;
@end

View File

@@ -0,0 +1,322 @@
//
// QNDnsManager.m
// HappyDNS
//
// Created by bailong on 15/6/23.
// Copyright (c) 2015 Qiniu Cloud Storage. All rights reserved.
//
#import "QNDnsManager.h"
#import "QNDomain.h"
#import "QNHosts.h"
#import "QNIP.h"
#import "QNLruCache.h"
#import "QNNetworkInfo.h"
#import "QNRecord.h"
#import "QNResolverDelegate.h"
#include "QNGetAddrInfo.h"
@interface QNDnsManager ()
@property (nonatomic, strong) QNLruCache *cache;
@property (atomic) QNNetworkInfo *curNetwork;
@property (nonatomic) NSArray *resolvers;
@property (atomic) UInt32 resolverStatus;
@property (nonatomic, strong) QNHosts *hosts;
@property (nonatomic, strong) id<QNRecordSorter> sorter;
@end
//static inline BOOL bits_isSet(UInt32 v, int index) {
// return (v & (1 << index)) != 0;
//}
static inline UInt32 bits_set(UInt32 v, int bitIndex) {
return v |= (1 << bitIndex);
}
static inline UInt32 bits_leadingZeros(UInt32 x) {
UInt32 y;
int n = 32;
y = x >> 16;
if (y != 0) {
n = n - 16;
x = y;
}
y = x >> 8;
if (y != 0) {
n = n - 8;
x = y;
}
y = x >> 4;
if (y != 0) {
n = n - 4;
x = y;
}
y = x >> 2;
if (y != 0) {
n = n - 2;
x = y;
}
y = x >> 1;
if (y != 0) {
return n - 2;
}
return n - x;
}
static NSMutableArray *trimCname(NSArray *records) {
NSMutableArray *array = [[NSMutableArray alloc] init];
for (QNRecord *r in records) {
if (r.type == kQNTypeA || r.type == kQNTypeAAAA) {
[array addObject:r];
}
}
return array;
}
static NSArray<NSString *> *records2Ips(NSArray<QNRecord *> *records) {
NSMutableArray *array = [[NSMutableArray alloc] init];
for (QNRecord *r in records) {
if (r.value && r.value.length > 0) {
[array addObject:r.value];
}
}
return [array copy];
}
static NSArray<QNRecord *> * filterInvalidRecords(NSArray *records) {
NSMutableArray *array = [[NSMutableArray alloc] init];
long long timestamp = [[NSDate date] timeIntervalSince1970];
for (QNRecord *r in records) {
if (r.value && r.value.length > 0 && ![r expired:timestamp]) {
[array addObject:r];
}
}
return [array copy];
}
@interface DummySorter : NSObject <QNRecordSorter>
@end
@implementation DummySorter
//sorted already
- (NSArray *)sort:(NSArray *)ips {
return ips;
}
@end
@implementation QNDnsManager
- (NSArray <QNRecord *> *)queryRecords:(NSString *)domain {
return [self queryRecordsWithDomain:[[QNDomain alloc] init:domain]];
}
- (NSArray <QNRecord *> *)queryRecordsWithDomain:(QNDomain *)domain{
if (domain == nil) {
return nil;
}
if ([QNIP mayBeIpV4:domain.domain]) {
QNRecord *record = [[QNRecord alloc] init:domain.domain ttl:kQNRecordForeverTTL type:kQNTypeA source:QNRecordSourceUnknown];
return [NSArray arrayWithObject:record];
}
NSArray<QNRecord *> *records = [self queryInternalWithDomain:domain];
return [_sorter sort:records];
}
- (NSArray <QNRecord *> *)queryInternalWithDomain:(QNDomain *)domain {
if (domain.hostsFirst) {
NSArray <QNRecord *> *result = [_hosts query:domain networkInfo:_curNetwork];
result = filterInvalidRecords(result);
if (result.count > 0) {
return [result copy];
}
}
if ([_curNetwork isEqualToInfo:[QNNetworkInfo normal]] && [QNNetworkInfo isNetworkChanged]) {
@synchronized(_cache) {
[_cache removeAllObjects];
}
_resolverStatus = 0;
} else {
@synchronized(_cache) {
NSArray *result = [_cache objectForKey:domain.domain];
result = filterInvalidRecords(result);
if (result.count > 0) {
return [result copy];
}
}
}
NSArray *records = nil;
NSError *error = nil;
int firstOk = 32 - bits_leadingZeros(_resolverStatus);
for (int i = 0; i < _resolvers.count; i++) {
int pos = (firstOk + i) % _resolvers.count;
id<QNResolverDelegate> resolver = [_resolvers objectAtIndex:pos];
QNNetworkInfo *previousNetwork = _curNetwork;
NSString *previousIp = [QNNetworkInfo getIp];
records = [resolver query:domain networkInfo:previousNetwork error:&error];
if (error != nil) {
NSError *tmp = error;
error = nil;
if (tmp.code == kQNDomainNotOwnCode) {
continue;
}
if (self.queryErrorHandler) {
self.queryErrorHandler(error, domain.domain);
}
}
if (records == nil || records.count == 0) {
if (_curNetwork == previousNetwork && [previousIp isEqualToString:[QNNetworkInfo getIp]]) {
_resolverStatus = bits_set(_resolverStatus, pos);
}
} else {
NSMutableArray *result = trimCname(records);
if (_curNetwork == previousNetwork && [previousIp isEqualToString:[QNNetworkInfo getIp]]) {
@synchronized(_cache) {
[_cache setObject:[result copy] forKey:domain.domain];
}
}
return [result copy];
}
}
if (!domain.hostsFirst) {
return [_hosts query:domain networkInfo:_curNetwork];
}
return nil;
}
- (instancetype)init:(NSArray *)resolvers networkInfo:(QNNetworkInfo *)netInfo {
return [self init:resolvers networkInfo:netInfo sorter:nil];
}
- (instancetype)init:(NSArray *)resolvers networkInfo:(QNNetworkInfo *)netInfo sorter:(id<QNRecordSorter>)sorter {
if (self = [super init]) {
_cache = [[QNLruCache alloc] init:1024];
_curNetwork = netInfo;
_resolvers = [[NSArray alloc] initWithArray:resolvers];
_hosts = [[QNHosts alloc] init];
if (sorter == nil) {
_sorter = [[DummySorter alloc] init];
} else {
_sorter = sorter;
}
}
return self;
}
- (void)onNetworkChange:(QNNetworkInfo *)netInfo {
@synchronized(_cache) {
[_cache removeAllObjects];
}
_curNetwork = netInfo;
}
- (instancetype)putHosts:(NSString *)domain ipv4:(NSString *)ipv4 {
return [self putHosts:domain ip:ipv4 type:kQNTypeA provider:kQNISP_GENERAL];
}
- (instancetype)putHosts:(NSString *)domain ip:(NSString *)ip type:(int)type provider:(int)provider {
return [self putHosts:domain record:[[QNRecord alloc] init:ip ttl:kQNRecordForeverTTL type:type source:QNRecordSourceCustom] provider:provider];
}
- (instancetype)putHosts:(NSString *)domain record:(QNRecord *)record provider:(int)provider {
QNRecord *recordNew = [[QNRecord alloc] init:record.value ttl:record.ttl type:record.type timeStamp:record.timeStamp server:record.server source:QNRecordSourceCustom];
[_hosts put:domain record:recordNew provider:provider];
return self;
}
- (NSURL *)queryAndReplaceWithIP:(NSURL *)url {
NSURLComponents *urlComponents = [[NSURLComponents alloc] initWithURL:url resolvingAgainstBaseURL:YES];
if (!urlComponents) {
return nil;
}
NSString *host = urlComponents.host;
NSArray<QNRecord *> *records = [self queryRecords:host];
NSURL *URL = nil;
if (records && records.firstObject) {
urlComponents.host = [QNIP ipHost:records.firstObject.value];
}
URL = urlComponents.URL;
return URL;
}
static QNGetAddrInfoCallback getAddrInfoCallback = nil;
static qn_ips_ret *dns_callback_internal(const char *host) {
if (getAddrInfoCallback == nil) {
return NULL;
}
NSString *s = [[NSString alloc] initWithUTF8String:host];
if (s == nil) {
return NULL;
}
NSArray *ips = getAddrInfoCallback(s);
if (ips == nil) {
return NULL;
}
qn_ips_ret *ret = calloc(sizeof(char *), ips.count + 1);
for (int i = 0; i < ips.count; i++) {
NSString *ip = ips[i];
char *ip2 = strdup([ip cStringUsingEncoding:NSUTF8StringEncoding]);
ret->ips[i] = ip2;
}
return ret;
}
static qn_ips_ret *dns_callback(const char *host) {
qn_ips_ret *ret = dns_callback_internal(host);
if (ret == NULL) {
//only for compatible
qn_ips_ret *ret = calloc(sizeof(char *), 2);
ret->ips[0] = strdup(host);
}
return ret;
}
static QNIpStatusCallback ipStatusCallback = nil;
static void ip_status_callback(const char *ip, int code, int time_ms) {
if (ipStatusCallback == nil) {
return;
}
NSString *s = [[NSString alloc] initWithUTF8String:ip];
if (s == nil) {
return;
}
ipStatusCallback(s, code, time_ms);
}
+ (void)setGetAddrInfoBlock:(QNGetAddrInfoCallback)block {
if ([QNIP isIpV6FullySupported] || ![QNIP isV6]) {
getAddrInfoCallback = block;
qn_set_dns_callback(dns_callback);
}
}
+ (void)setDnsManagerForGetAddrInfo:(QNDnsManager *)dns {
[QNDnsManager setGetAddrInfoBlock:^NSArray<NSString *> *(NSString *host) {
NSArray *records = [dns queryRecords:host];
return records2Ips(records);
}];
}
+ (void)setIpStatusCallback:(QNIpStatusCallback)block {
ipStatusCallback = block;
qn_set_ip_report_callback(ip_status_callback);
}
+ (BOOL)needHttpDns {
NSTimeZone *timeZone = [NSTimeZone localTimeZone];
NSString *tzName = [timeZone name];
return [tzName isEqual:@"Asia/Shanghai"] || [tzName isEqual:@"Asia/Chongqing"] || [tzName isEqual:@"Asia/Harbin"] || [tzName isEqual:@"Asia/Urumqi"];
}
@end

27
Pods/HappyDNS/HappyDNS/Common/QNDomain.h generated Normal file
View File

@@ -0,0 +1,27 @@
//
// QNDomain.h
// HappyDNS
//
// Created by bailong on 15/6/23.
// Copyright (c) 2015年 Qiniu Cloud Storage. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface QNDomain : NSObject
@property (nonatomic, strong, readonly) NSString *domain;
// 用来判断劫持
@property (nonatomic, readonly) BOOL hasCname;
// 用来判断劫持
@property (nonatomic, readonly) int maxTtl;
@property (nonatomic, readonly) BOOL hostsFirst;
- (instancetype)init:(NSString *)domain;
- (instancetype)init:(NSString *)domain hostsFirst:(BOOL)hostsFirst hasCname:(BOOL)hasCname;
- (instancetype)init:(NSString *)domain hostsFirst:(BOOL)hostsFirst hasCname:(BOOL)hasCname maxTtl:(int)maxTtl;
@end

30
Pods/HappyDNS/HappyDNS/Common/QNDomain.m generated Normal file
View File

@@ -0,0 +1,30 @@
//
// QNDomain.m
// HappyDNS
//
// Created by bailong on 15/6/23.
// Copyright (c) 2015 Qiniu Cloud Storage. All rights reserved.
//
#import "QNDomain.h"
@implementation QNDomain
- (instancetype)init:(NSString *)domain {
return [self init:domain hostsFirst:NO hasCname:NO maxTtl:0];
}
- (instancetype)init:(NSString *)domain hostsFirst:(BOOL)hostsFirst hasCname:(BOOL)hasCname {
return [self init:domain hostsFirst:hostsFirst hasCname:hasCname maxTtl:0];
}
- (instancetype)init:(NSString *)domain hostsFirst:(BOOL)hostsFirst hasCname:(BOOL)hasCname maxTtl:(int)maxTtl {
if (self = [super init]) {
_domain = domain;
_hasCname = hasCname;
_maxTtl = maxTtl;
_hostsFirst = hostsFirst;
}
return self;
}
@end

View File

@@ -0,0 +1,23 @@
//
// QNLruCache.h
// HappyDNS
//
// Created by bailong on 16/7/5.
// Copyright © 2016年 Qiniu Cloud Storage. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface QNLruCache : NSObject
- (instancetype)init:(NSUInteger)limit;
- (void)removeAllObjects;
- (void)removeObjectForKey:(NSString *)key;
- (id)objectForKey:(NSString *)key;
- (void)setObject:(id)obj forKey:(NSString *)key;
@end

View File

@@ -0,0 +1,90 @@
//
// QNLruCache.m
// HappyDNS
//
// Created by bailong on 16/7/5.
// Copyright © 2016 Qiniu Cloud Storage. All rights reserved.
//
#import "QNLruCache.h"
@interface QNLruCache ()
@property (nonatomic, readonly) NSUInteger limit;
@property (nonatomic, readonly) NSMutableDictionary* cache;
@property (nonatomic, readonly) NSMutableArray* list;
@end
@interface _QNElement : NSObject
@property (nonatomic, readonly, strong) NSString* key;
@property (nonatomic, strong) id obj;
- (instancetype)initObject:(id)obj forKey:(NSString*)key;
@end
@implementation _QNElement
- (instancetype)initObject:(id)obj forKey:(NSString*)key {
if (self = [super init]) {
_key = key;
_obj = obj;
}
return self;
}
@end
@implementation QNLruCache
- (instancetype)init:(NSUInteger)limit {
if (self = [super init]) {
_limit = limit;
_cache = [NSMutableDictionary new];
_list = [NSMutableArray new];
}
return self;
}
- (void)removeAllObjects {
[_cache removeAllObjects];
[_list removeAllObjects];
}
- (void)removeObjectForKey:(NSString*)key {
_QNElement* obj = [_cache objectForKey:key];
if (obj == nil) {
return;
}
[_cache removeObjectForKey:key];
[_list removeObjectIdenticalTo:obj];
}
- (id)objectForKey:(NSString*)key {
_QNElement* obj = [_cache objectForKey:key];
if (obj != nil) {
[_list removeObjectIdenticalTo:obj];
[_list insertObject:obj atIndex:0];
}
return obj.obj;
}
- (void)setObject:(id)obj forKey:(NSString*)key {
_QNElement* old = [_cache objectForKey:key];
if (old) {
old.obj = obj;
[_list removeObjectIdenticalTo:old];
[_list insertObject:old atIndex:0];
return;
} else if (_list.count == _limit) {
old = [_list lastObject];
[_list removeLastObject];
[_cache removeObjectForKey:old.key];
}
_QNElement* newElement = [[_QNElement alloc] initObject:obj forKey:key];
[_cache setObject:newElement forKey:key];
[_list insertObject:newElement atIndex:0];
}
@end

View File

@@ -0,0 +1,42 @@
//
// QNNetworkInfo.h
// HappyDNS
//
// Created by bailong on 15/6/25.
// Copyright (c) 2015年 Qiniu Cloud Storage. All rights reserved.
//
#import <Foundation/Foundation.h>
extern const int kQNNO_NETWORK;
extern const int kQNWIFI;
extern const int kQNMOBILE;
extern const int kQNISP_GENERAL;
extern const int kQNISP_CTC;
extern const int kQNISP_DIANXIN;
extern const int kQNISP_CNC;
extern const int kQNISP_LIANTONG;
extern const int kQNISP_CMCC;
extern const int kQNISP_YIDONG;
extern const int kQNISP_OTHER;
@interface QNNetworkInfo : NSObject
@property (nonatomic, readonly) int networkConnection;
@property (nonatomic, readonly) int provider;
- (instancetype)init:(int)connecton provider:(int)provider;
- (BOOL)isEqual:(id)other;
- (BOOL)isEqualToInfo:(QNNetworkInfo *)info;
+ (instancetype)noNet;
+ (instancetype)normal;
+ (BOOL)isNetworkChanged;
+ (NSString *)getIp;
@end

View File

@@ -0,0 +1,86 @@
//
// QNNetworkInfo.m
// HappyDNS
//
// Created by bailong on 15/6/25.
// Copyright (c) 2015 Qiniu Cloud Storage. All rights reserved.
//
#import <arpa/inet.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>
#import "QNIP.h"
#import "QNNetworkInfo.h"
const int kQNNO_NETWORK = -1;
const int kQNWIFI = 1;
const int kQNMOBILE = 2;
const int kQNISP_GENERAL = 0;
const int kQNISP_CTC = 1;
const int kQNISP_DIANXIN = kQNISP_CTC;
const int kQNISP_CNC = 2;
const int kQNISP_LIANTONG = kQNISP_CNC;
const int kQNISP_CMCC = 3;
const int kQNISP_YIDONG = kQNISP_CMCC;
const int kQNISP_OTHER = 999;
#define IPLength 64
static char previousIp[IPLength] = {0};
static NSString *lock = @"";
@implementation QNNetworkInfo
- (instancetype)init:(int)connecton provider:(int)provider {
if (self = [super init]) {
_networkConnection = connecton;
_provider = provider;
}
return self;
}
+ (instancetype)noNet {
return [[QNNetworkInfo alloc] init:kQNNO_NETWORK provider:kQNISP_GENERAL];
}
+ (instancetype)normal {
return [[QNNetworkInfo alloc] init:kQNISP_GENERAL provider:kQNISP_GENERAL];
}
- (BOOL)isEqualToInfo:(QNNetworkInfo *)info {
if (self == info)
return YES;
return self.provider == info.provider && self.networkConnection == info.networkConnection;
}
- (BOOL)isEqual:(id)other {
if (other == self)
return YES;
if (!other || ![other isKindOfClass:[self class]])
return NO;
return [self isEqualToInfo:other];
}
+ (BOOL)isNetworkChanged {
@synchronized(lock) {
char local[IPLength] = {0};
int err = qn_localIp(local, sizeof(local));
if (err != 0) {
return YES;
}
if (memcmp(previousIp, local, sizeof(local)) != 0) {
memcpy(previousIp, local, sizeof(local));
return YES;
}
return NO;
}
}
+ (NSString *)getIp {
return [QNIP local];
}
@end

69
Pods/HappyDNS/HappyDNS/Common/QNRecord.h generated Normal file
View File

@@ -0,0 +1,69 @@
//
// QNRecord.h
// HappyDNS
//
// Created by bailong on 15/6/23.
// Copyright (c) 2015年 Qiniu Cloud Storage. All rights reserved.
//
#import <Foundation/Foundation.h>
/**
* A 记录
*/
extern const int kQNTypeA;
/**
* AAAA 记录
*/
extern const int kQNTypeAAAA;
/**
* Cname 记录
*/
extern const int kQNTypeCname;
/**
* Txt 记录
*/
extern const int kQNTypeTXT;
/**
* 永久有效的 ttl
*/
extern const int kQNRecordForeverTTL;
typedef NS_ENUM(NSUInteger, QNRecordSource) {
QNRecordSourceUnknown,
QNRecordSourceCustom,
QNRecordSourceDnspodEnterprise,
QNRecordSourceSystem,
QNRecordSourceUdp,
QNRecordSourceDoh,
};
@interface QNRecord : NSObject
@property (nonatomic, copy, readonly) NSString *value;
@property (nonatomic, copy, readonly) NSString *server;
@property (nonatomic, readonly) int ttl;
@property (nonatomic, readonly) int type;
@property (nonatomic, readonly) long long timeStamp;
@property (nonatomic, readonly) QNRecordSource source;
- (instancetype)init:(NSString *)value
ttl:(int)ttl
type:(int)type
source:(QNRecordSource)source;
- (instancetype)init:(NSString *)value
ttl:(int)ttl
type:(int)type
timeStamp:(long long)timeStamp
server:(NSString *)server
source:(QNRecordSource)source;
- (BOOL)expired:(long long)time;
@end

60
Pods/HappyDNS/HappyDNS/Common/QNRecord.m generated Normal file
View File

@@ -0,0 +1,60 @@
//
// QNRecord.m
// HappyDNS
//
// Created by bailong on 15/6/23.
// Copyright (c) 2015 Qiniu Cloud Storage. All rights reserved.
//
#import "QNRecord.h"
const int kQNTypeA = 1;
const int kQNTypeAAAA = 28;
const int kQNTypeCname = 5;
const int kQNTypeTXT = 16;
const int kQNRecordForeverTTL = -1;
@implementation QNRecord
- (instancetype)init:(NSString *)value
ttl:(int)ttl
type:(int)type
source:(QNRecordSource)source {
if (self = [super init]) {
_value = value;
_type = type;
_ttl = ttl;
_source = source;
_timeStamp = [[NSDate date] timeIntervalSince1970];
}
return self;
}
- (instancetype)init:(NSString *)value
ttl:(int)ttl
type:(int)type
timeStamp:(long long)timeStamp
server:(NSString *)server
source:(QNRecordSource)source {
if (self = [super init]) {
_value = value;
_type = type;
_ttl = ttl;
_server = server;
_source = source;
_timeStamp = timeStamp;
}
return self;
}
- (BOOL)expired:(long long)time {
if (_ttl == kQNRecordForeverTTL) {
return false;
}
return time > _timeStamp + _ttl;
}
- (NSString *)description {
return [NSString stringWithFormat:@"value:%@, ttl:%d, timestamp:%lld, type:%d server:%@ source:%lu", _value, _ttl, _timeStamp, _type, _server, (unsigned long)_source];
}
@end

View File

@@ -0,0 +1,19 @@
//
// QNResolverDelegate.h
// HappyDNS
//
// Created by bailong on 15/6/23.
// Copyright (c) 2015年 Qiniu Cloud Storage. All rights reserved.
//
#import "QNDnsError.h"
#define QN_DNS_DEFAULT_TIMEOUT 20 //seconds
@class QNDomain;
@class QNNetworkInfo;
@protocol QNResolverDelegate <NSObject>
- (NSArray *)query:(QNDomain *)domain networkInfo:(QNNetworkInfo *)netInfo error:(NSError **)error;
@end

15
Pods/HappyDNS/HappyDNS/Dns/QNDnsDefine.h generated Normal file
View File

@@ -0,0 +1,15 @@
//
// QNDnsDefine.h
// Doh
//
// Created by yangsen on 2021/7/20.
//
#import <Foundation/Foundation.h>
typedef NS_ENUM(NSInteger, QNDnsOpCode) {
QNDnsOpCodeQuery = 0, // 标准查询
QNDnsOpCodeIQuery = 1, // 反向查询
QNDnsOpCodeStatus = 2, // DNS状态请求
QNDnsOpCodeUpdate = 5, // DNS域更新请求
};

View File

@@ -0,0 +1,41 @@
//
// QNDnsMessage.h
// Doh
//
// Created by yangsen on 2021/7/20.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface QNDnsMessage : NSObject
/**
* 16位的消息ID标示一次正常的交互该ID由消息请求者设置消息响应者回复请求时带上该ID。最大0xFFFF65536
*/
@property(nonatomic, assign, readonly)int messageId;
/**
* 请求类型,目前有三类值:
* 0 QUERY, 标准查询
* 1 IQUERY, 反向查询
* 2 STATUS, DNS状态请求
* 5 UPDATE, DNS域更新请求
*/
@property(nonatomic, assign, readonly)int opCode;
/**
* 是否递归查询。如果该位被设置为1则收到请求的域名服务器会递归查询域名
* 注: 该位为1域名服务器不一定会做递归查询这取决于域名服务器是否支持递归查询。
*/
@property(nonatomic, assign, readonly)int rd;
/**
* 在响应消息中清除并设置。表示该DNS域名服务器是否支持递归查询。
*/
@property(nonatomic, assign, readonly)int ra;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,12 @@
//
// QNDnsMessage.m
// Doh
//
// Created by yangsen on 2021/7/20.
//
#import "QNDnsMessage.h"
@implementation QNDnsMessage
@end

View File

@@ -0,0 +1,43 @@
//
// DnsQuestion.h
// Doh
//
// Created by yangsen on 2021/7/16.
//
#import "QNDnsDefine.h"
#import "QNDnsMessage.h"
NS_ASSUME_NONNULL_BEGIN
@interface QNDnsRequest : QNDnsMessage
@property(nonatomic, assign, readonly)int recordType;
@property(nonatomic, copy, readonly)NSString *host;
/// 构造函数
/// @param messageId 请求 id
/// @param recordType 记录类型
/// @param host 需要进行 Dns 解析的 host
+ (instancetype)request:(int)messageId
recordType:(int)recordType
host:(NSString *)host;
/// 构造函数
/// @param messageId 请求 id
/// @param opCode 请求类型
/// @param rd 是否递归查询。如果该位被设置为1则收到请求的域名服务器会递归查询域名
/// 注: 该位为1域名服务器不一定会做递归查询这取决于域名服务器是否支持递归查询。
/// @param recordType 记录类型
/// @param host 需要进行 Dns 解析的 host
+ (instancetype)request:(int)messageId
opCode:(QNDnsOpCode)opCode
rd:(int)rd
recordType:(int)recordType
host:(NSString *)host;
- (NSData *)toDnsQuestionData:(NSError **)error;
@end
NS_ASSUME_NONNULL_END

118
Pods/HappyDNS/HappyDNS/Dns/QNDnsRequest.m generated Normal file
View File

@@ -0,0 +1,118 @@
//
// DnsQuestion.m
// Doh
//
// Created by yangsen on 2021/7/16.
//
#import "QNRecord.h"
#import "NSData+QNRW.h"
#import "QNDnsError.h"
#import "QNDnsRequest.h"
@interface QNDnsRequest()
@property(nonatomic, assign)int messageId;
@property(nonatomic, assign)QNDnsOpCode opCode;
@property(nonatomic, assign)int rd;
@property(nonatomic, assign)int recordType;
@property(nonatomic, copy)NSString *host;
@end
@implementation QNDnsRequest
@synthesize messageId;
@synthesize opCode;
@synthesize rd;
+ (instancetype)request:(int)messageId
recordType:(int)recordType
host:(NSString *)host {
return [self request:messageId opCode:QNDnsOpCodeQuery rd:1 recordType:recordType host:host];
}
+ (instancetype)request:(int)messageId
opCode:(QNDnsOpCode)opCode
rd:(int)rd
recordType:(int)recordType
host:(NSString *)host {
QNDnsRequest *request = [[QNDnsRequest alloc] init];
request.messageId = messageId;
request.opCode = opCode;
request.rd = rd;
request.recordType = recordType;
request.host = host;
return request;
}
- (NSData *)toDnsQuestionData:(NSError *__autoreleasing _Nullable *)error {
if (self.host == nil || self.host.length == 0) {
[self copyError:kQNDnsInvalidParamError(@"host can not empty") toErrorPoint:error];
return nil;
}
if (self.opCode != QNDnsOpCodeQuery &&
self.opCode != QNDnsOpCodeIQuery &&
self.opCode != QNDnsOpCodeStatus &&
self.opCode != QNDnsOpCodeUpdate) {
[self copyError:kQNDnsInvalidParamError(@"opCode is not valid") toErrorPoint:error];
return nil;
}
if (self.rd != 0 && self.rd != 1) {
[self copyError:kQNDnsInvalidParamError(@"rd is not valid") toErrorPoint:error];
return nil;
}
if (self.recordType != kQNTypeA &&
self.recordType != kQNTypeCname &&
self.recordType != kQNTypeTXT &&
self.recordType != kQNTypeAAAA) {
[self copyError:kQNDnsInvalidParamError(@"recordType is not valid") toErrorPoint:error];
return nil;
}
NSMutableData *data = [NSMutableData data];
[data qn_appendBigEndianInt16:self.messageId]; // 16 bit id
// |00|01|02|03|04|05|06|07|
// |QR| OPCODE |AA|TC|RD|
[data qn_appendInt8:(self.opCode<<3) + self.rd];
// |00|01|02|03|04|05|06|07|
// |RA|r1|r2|r3| RCODE |
[data qn_appendInt8:0x00];
[data qn_appendInt8:0x00];
[data qn_appendInt8:0x01]; // QDCOUNT (number of entries in the question section)
[data qn_appendInt8:0x00];
[data qn_appendInt8:0x00]; // ANCOUNT
[data qn_appendInt8:0x00];
[data qn_appendInt8:0x00]; // NSCOUNT
[data qn_appendInt8:0x00];
[data qn_appendInt8:0x00]; // ARCOUNT
NSArray *hostParts = [self.host componentsSeparatedByString:@"."];
for (NSString *part in hostParts) {
if (part.length > 63) {
return nil;
}
[data qn_appendInt8:part.length];
[data qn_appendString:part usingEncoding:NSUTF8StringEncoding];
}
[data qn_appendInt8:0x00]; /* terminating zero */
[data qn_appendInt8:0x00];
[data qn_appendInt8:self.recordType];
[data qn_appendInt8:0x00];
[data qn_appendInt8:0x01]; /* IN - "the Internet" */
return data;
}
- (void)copyError:(NSError *)error toErrorPoint:(NSError **)errorPoint {
if (errorPoint != nil) {
*errorPoint = error;
}
}
- (NSString *)description {
return [NSString stringWithFormat:@"messageId:%d opcode:%ld rd:%d ra:%d type:%ld", self.messageId, (long)self.opCode, self.rd, self.ra, (long)self.recordType];
}
@end

View File

@@ -0,0 +1,33 @@
//
// QNDnsResolver.h
// HappyDNS
//
// Created by yangsen on 2021/7/28.
// Copyright © 2021 Qiniu Cloud Storage. All rights reserved.
//
#import "QNDnsDefine.h"
#import "QNRecord.h"
#import "QNResolverDelegate.h"
NS_ASSUME_NONNULL_BEGIN
@class QNDnsResponse;
// 抽象对象,不能直接使用,使用其子类
@interface QNDnsResolver : NSObject <QNResolverDelegate>
@property(nonatomic, assign, readonly)int recordType;
@property(nonatomic, assign, readonly)int timeout;
@property(nonatomic, copy, readonly)NSArray *servers;
// 抽象方法,子类实现
- (void)request:(NSString *)server
host:(NSString *)host
recordType:(int)recordType
complete:(void(^)(QNDnsResponse *response, NSError *error))complete;
@end
NS_ASSUME_NONNULL_END

129
Pods/HappyDNS/HappyDNS/Dns/QNDnsResolver.m generated Normal file
View File

@@ -0,0 +1,129 @@
//
// QNDnsResolver.m
// HappyDNS
//
// Created by yangsen on 2021/7/28.
// Copyright © 2021 Qiniu Cloud Storage. All rights reserved.
//
#import "QNRecord.h"
#import "QNDomain.h"
#import "QNDnsError.h"
#import "QNDnsResponse.h"
#import "QNDnsResolver.h"
@interface QNDnsResolver()
@property(nonatomic, strong)dispatch_queue_t timerQueue;
@end
@implementation QNDnsResolver
+ (dispatch_queue_t)timeoutQueue {
static dispatch_once_t onceToken;
static dispatch_queue_t timerQueue;
dispatch_once(&onceToken, ^{
timerQueue = dispatch_queue_create("com.happyDns.timeoutQueue", DISPATCH_QUEUE_CONCURRENT);
});
return timerQueue;
}
- (NSArray *)query:(QNDomain *)domain networkInfo:(QNNetworkInfo *)netInfo error:(NSError *__autoreleasing *)error {
NSError *err = nil;
QNDnsResponse *response = [self lookupHost:domain.domain error:&err];
if (err != nil) {
*error = err;
return @[];
}
NSMutableArray *records = [NSMutableArray array];
for (QNRecord *record in response.answerArray) {
if (record.type == kQNTypeA || record.type == kQNTypeAAAA || record.type == kQNTypeCname) {
[records addObject:record];
}
}
return [records copy];
}
- (QNDnsResponse *)lookupHost:(NSString *)host error:(NSError *__autoreleasing _Nullable *)error {
//
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
__block NSError *errorP = nil;
__block QNDnsResponse *dnsResponse = nil;
[self request:host recordType:self.recordType complete:^(QNDnsResponse *response, NSError *err) {
errorP = err;
dnsResponse = response;
dispatch_semaphore_signal(semaphore);
}];
dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, self.timeout * NSEC_PER_SEC));
if (error != NULL) {
*error = errorP;
}
return dnsResponse;
}
- (void)request:(NSString *)host
recordType:(int)recordType
complete:(void(^)(QNDnsResponse *response, NSError *error))complete {
if (complete == nil) {
return;
}
if (self.servers == nil || self.servers.count == 0) {
complete(nil, kQNDnsInvalidParamError(@"server can not empty"));
return;
}
NSLock *locker = [[NSLock alloc] init];
__block BOOL hasCallBack = false;
__block BOOL completeCount = 0;
//
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(self.timeout * NSEC_PER_SEC)), [QNDnsResolver timeoutQueue], ^{
BOOL shouldCallBack = false;
[locker lock];
if (!hasCallBack) {
shouldCallBack = true;
hasCallBack = true;
}
[locker unlock];
if (shouldCallBack) {
NSString *error = [NSString stringWithFormat:@"resolver timeout for server:%@ host:%@",[self.servers description], host];
complete(nil, kQNDnsInvalidParamError(error));
}
});
for (NSString *server in self.servers) {
[self request:server host:host recordType:recordType complete:^(QNDnsResponse *response, NSError *error) {
BOOL shouldCallBack = false;
[locker lock];
completeCount++;
if (completeCount == self.servers.count || (response != nil && response.rCode == 0 && !hasCallBack)) {
shouldCallBack = true;
hasCallBack = true;
}
[locker unlock];
if (shouldCallBack) {
complete(response, error);
}
}];
}
}
- (void)request:(NSString *)server
host:(NSString *)host
recordType:(int)recordType
complete:(void(^)(QNDnsResponse *response, NSError *error))complete {
if (complete != nil) {
complete(nil, kQNDnsMethodError(@"use sub class of QNDnsResolver"));
}
}
@end

View File

@@ -0,0 +1,46 @@
//
// DnsRecord.h
// Doh
//
// Created by yangsen on 2021/7/16.
//
#import "QNRecord.h"
#import "QNDnsRequest.h"
NS_ASSUME_NONNULL_BEGIN
@interface QNDnsResponse : QNDnsMessage
@property(nonatomic, assign, readonly)NSInteger timestamp;
@property(nonatomic, assign, readonly)QNRecordSource source;
@property(nonatomic, copy, readonly)NSString *server;
@property(nonatomic, strong, readonly)QNDnsRequest *request;
@property(nonatomic, strong, readonly)NSData *recordData;
/**
* 响应该消息的域名服务器是该域中的权威域名服务器。因为Answer Section中可能会有很多域名
*/
@property(nonatomic, assign, readonly)int aa;
/**
* 响应消息的类型:
* 0 成功的响应
* 1 格式错误--域名服务器无法解析请求,因为请求消息格式错误
* 2 服务器错误--域名服务器因为内部错误无法解析该请求
* 3 名字错误-- 只在权威域名服务器的响应消息中有效,标示请求中请求的域不存在
* 4 Not Implemented--域名服务器不支持请求的类型
* 5 Refused -- 域名服务器因为策略的原因拒绝执行请求的操作。例如域名服务器不会为特定的请求者返回查询结果,或者域名服务器不会为特定的请求返回特定的数据
*/
@property(nonatomic, assign, readonly)int rCode;
@property(nonatomic, copy, readonly)NSArray <QNRecord *> *answerArray;
@property(nonatomic, copy, readonly)NSArray <QNRecord *> *authorityArray;
@property(nonatomic, copy, readonly)NSArray <QNRecord *> *additionalArray;
+ (instancetype)dnsResponse:(NSString *)server source:(QNRecordSource)source request:(QNDnsRequest *)request dnsRecordData:(NSData *)recordData error:(NSError **)error;
@end
NS_ASSUME_NONNULL_END

350
Pods/HappyDNS/HappyDNS/Dns/QNDnsResponse.m generated Normal file
View File

@@ -0,0 +1,350 @@
//
// DnsRecord.m
// Doh
//
// Created by yangsen on 2021/7/16.
//
#import "QNRecord.h"
#import "QNDnsError.h"
#import "NSData+QNRW.h"
#import "QNDnsResponse.h"
/// DNS index name
@interface QNDnsRecordName : NSObject
@property(nonatomic, assign)NSInteger skipLength;
@property(nonatomic, copy)NSString *name;
@end
@implementation QNDnsRecordName
@end
@interface QNDnsRecordResource : NSObject
@property(nonatomic, copy)NSString *name;
@property(nonatomic, assign)int count;
@property(nonatomic, assign)int from;
@property(nonatomic, assign)int length;
@property(nonatomic, strong)NSMutableArray *records;
@end
@implementation QNDnsRecordResource
+ (instancetype)resource:(NSString *)name count:(int)count from:(int)from {
QNDnsRecordResource *resource = [[QNDnsRecordResource alloc] init];
resource.name = name;
resource.count = count;
resource.from = from;
resource.length = 0;
resource.records = [NSMutableArray array];
return resource;
}
@end
@interface QNDnsResponse()
@property(nonatomic, assign)NSInteger timestamp;
@property(nonatomic, assign)QNRecordSource source;
@property(nonatomic, copy)NSString *server;
@property(nonatomic, strong)QNDnsRequest *request;
@property(nonatomic, strong)NSData *recordData;
@property(nonatomic, assign)int messageId;
@property(nonatomic, assign)QNDnsOpCode opCode;
@property(nonatomic, assign)int aa;
@property(nonatomic, assign)int ra;
@property(nonatomic, assign)int rd;
@property(nonatomic, assign)int rCode;
@property(nonatomic, copy)NSArray <QNRecord *> *answerArray;
@property(nonatomic, copy)NSArray <QNRecord *> *authorityArray;
@property(nonatomic, copy)NSArray <QNRecord *> *additionalArray;
@end
@implementation QNDnsResponse
@synthesize messageId;
@synthesize opCode;
@synthesize aa;
@synthesize ra;
@synthesize rd;
@synthesize rCode;
+ (instancetype)dnsResponse:(NSString *)server source:(QNRecordSource)source request:(QNDnsRequest *)request dnsRecordData:(NSData *)recordData error:(NSError *__autoreleasing _Nullable *)error {
QNDnsResponse *record = [[QNDnsResponse alloc] init];
record.server = server;
record.source = source;
record.request = request;
record.recordData = recordData;
record.timestamp = [[NSDate date] timeIntervalSince1970];
NSError *err = nil;
[record parse:&err];
if (error != nil) {
*error = err;
}
return record;
}
- (void)parse:(NSError **)error {
if (self.recordData.length < 12) {
[self copyError:kQNDnsResponseFormatError(@"response data too small") toErrorPoint:error];
return;
}
// Header
[self parseHeader:error];
if (error != nil && *error != nil) {
return;
}
// Question
int index = [self parseQuestion:error];
if (error != nil && *error != nil) {
return;
}
// Answer
QNDnsRecordResource *answer = [QNDnsRecordResource resource:@"answer"
count:[self.recordData qn_readBigEndianInt16:6]
from:index];
[self parseResourceRecord:answer error:error];
if (error != nil && *error != nil) {
return;
}
index += answer.length;
self.answerArray = [answer.records copy];
// Authority
QNDnsRecordResource *authority = [QNDnsRecordResource resource:@"authority"
count:[self.recordData qn_readBigEndianInt16:8]
from:index];
[self parseResourceRecord:authority error:error];
if (error != nil && *error != nil) {
return;
}
index += authority.length;
self.authorityArray = [authority.records copy];
// Additional
QNDnsRecordResource *additional = [QNDnsRecordResource resource:@"additional"
count:[self.recordData qn_readBigEndianInt16:10]
from:index];
[self parseResourceRecord:additional error:error];
if (error != nil && *error != nil) {
return;
}
self.additionalArray = [additional.records copy];
}
- (void)parseHeader:(NSError **)error {
self.messageId = [self.recordData qn_readBigEndianInt16:0];
// question id
if (self.messageId != self.request.messageId) {
[self copyError:kQNDnsResponseFormatError(@"question id error") toErrorPoint:error];
return;
}
// |00|01|02|03|04|05|06|07|
// |QR| OPCODE |AA|TC|RD|
int field0 = [self.recordData qn_readInt8:2];
int qr = [self.recordData qn_readInt8:2] & 0x80;
// dns
if (qr == 0) {
[self copyError:kQNDnsResponseFormatError(@"not a response data") toErrorPoint:error];
return;
}
self.opCode = (field0 >> 3) & 0x07;
self.aa = (field0 >> 2) & 0x01;
self.rd = field0 & 0x01;
// |00|01|02|03|04|05|06|07|
// |RA|r1|r2|r3| RCODE |
int field1 = [self.recordData qn_readInt8:3];
self.ra = (field1 >> 7) & 0x1;
self.rCode = field1 & 0x0F;
}
- (int)parseQuestion:(NSError **)error {
int index = 12;
int qdCount = [self.recordData qn_readBigEndianInt16:4];
while (qdCount) {
QNDnsRecordName *recordName = [self getNameFrom:index];
if (recordName == nil) {
[self copyError:kQNDnsResponseFormatError(@"read Question error") toErrorPoint:error];
return -1;
}
if (self.recordData.length < (index + recordName.skipLength + 4)) {
[self copyError:kQNDnsResponseFormatError(@"read Question error: out of range") toErrorPoint:error];
return -1;
}
index += recordName.skipLength + 4;
qdCount --;
}
return index;
}
- (void)parseResourceRecord:(QNDnsRecordResource *)resource error:(NSError **)error {
int index = resource.from;
int count = resource.count;
while (count) {
QNDnsRecordName *recordName = [self getNameFrom:index];
if (recordName == nil) {
NSString *errorDesc = [NSString stringWithFormat:@"read %@ error", resource.name];
[self copyError:kQNDnsResponseFormatError(errorDesc) toErrorPoint:error];
return;
}
index += recordName.skipLength;
if (self.recordData.length < (index + 2)) {
NSString *errorDesc = [NSString stringWithFormat:@"read %@ error: out of range", resource.name];
[self copyError:kQNDnsResponseFormatError(errorDesc) toErrorPoint:error];
return;
}
int type = [self.recordData qn_readBigEndianInt16:index];
index += 2;
if (self.recordData.length < (index + 2)) {
NSString *errorDesc = [NSString stringWithFormat:@"%@ read Answer error: out of range", resource.name];
[self copyError:kQNDnsResponseFormatError(errorDesc) toErrorPoint:error];
return;
}
int class = [self.recordData qn_readBigEndianInt16:index];
index += 2;
if (self.recordData.length < (index + 4)) {
NSString *errorDesc = [NSString stringWithFormat:@"%@ read Answer error: out of range", resource.name];
[self copyError:kQNDnsResponseFormatError(errorDesc) toErrorPoint:error];
return;
}
int ttl = [self.recordData qn_readBigEndianInt32:index];
index += 4;
if (self.recordData.length < (index + 2)) {
NSString *errorDesc = [NSString stringWithFormat:@"%@ read Answer error: out of range", resource.name];
[self copyError:kQNDnsResponseFormatError(errorDesc) toErrorPoint:error];
return;
}
int rdLength = [self.recordData qn_readBigEndianInt16:index];
index += 2;
if (self.recordData.length < (index + rdLength)) {
NSString *errorDesc = [NSString stringWithFormat:@"%@ read Answer error: out of range", resource.name];
[self copyError:kQNDnsResponseFormatError(errorDesc) toErrorPoint:error];
return;
}
NSString *value = [self readData:type range:NSMakeRange(index, rdLength)];
if (class == 0x01 && (type == kQNTypeCname || type == self.request.recordType)) {
QNRecord *record = [[QNRecord alloc] init:[value copy] ttl:ttl type:type timeStamp:self.timestamp server:self.server source:self.source];
[resource.records addObject:record];
}
index += rdLength;
count --;
}
resource.length = index - resource.from;
}
- (QNDnsRecordName *)getNameFrom:(NSInteger)fromIndex {
NSInteger partLength = 0;
NSInteger index = fromIndex;
NSMutableString *name = [NSMutableString string];
QNDnsRecordName *recordName = [[QNDnsRecordName alloc] init];
int maxLoop = 128;
do {
if (index >= self.recordData.length) {
return nil;
}
partLength = [self.recordData qn_readInt8:index];
if ((partLength & 0xc0) == 0xc0) {
// name pointer
if((index + 1) >= self.recordData.length) {
return nil;
}
if (recordName.skipLength < 1) {
recordName.skipLength = index + 2 - fromIndex;
}
index = (partLength & 0x3f) << 8 | [self.recordData qn_readInt8:index + 1];
continue;
} else if((partLength & 0xc0) > 0) {
return nil;
} else {
index++;
}
if (partLength > 0) {
if (name.length > 0) {
[name appendString:@"."];
}
if (index + partLength > self.recordData.length) {
return nil;
}
NSData *nameData = [self.recordData subdataWithRange:NSMakeRange(index, partLength)];
[name appendString:[[NSString alloc] initWithData:nameData encoding:NSUTF8StringEncoding]];
index += partLength;
}
} while (partLength && --maxLoop);
recordName.name = name;
if (recordName.skipLength < 1) {
recordName.skipLength = index - fromIndex;
}
return recordName;
}
- (NSString *)readData:(int)recordType range:(NSRange)range {
NSString *dataString = nil;
NSData *dataValue = [self.recordData subdataWithRange:range];
if (recordType == kQNTypeA) {
if (dataValue.length == 4) {
dataString = [NSString stringWithFormat:@"%d.%d.%d.%d", [dataValue qn_readInt8:0], [dataValue qn_readInt8:1], [dataValue qn_readInt8:2], [dataValue qn_readInt8:3]];
}
} else if (recordType == kQNTypeAAAA) {
if (dataValue.length == 16) {
NSMutableString *ipv6 = [NSMutableString string];
for (int i=0; i<16; i+=2) {
[ipv6 appendFormat:@"%@%02x%02x",(i?@":":@""), [dataValue qn_readInt8:i], [dataValue qn_readInt8:i+1]];
}
dataString = [ipv6 copy];
}
} else if (recordType == kQNTypeCname) {
if (dataValue.length > 1) {
QNDnsRecordName *name = [self getNameFrom:range.location];
dataString = [name.name copy];
}
} else if (recordType == kQNTypeTXT) {
if (dataValue.length > 1) {
dataString = [[NSString alloc] initWithData:[dataValue subdataWithRange:NSMakeRange(1, dataValue.length - 1)] encoding:NSUTF8StringEncoding];
}
}
return dataString;
}
- (void)copyError:(NSError *)error toErrorPoint:(NSError **)errorPoint {
if (errorPoint != nil) {
*errorPoint = error;
}
}
- (NSString *)description {
return [NSString stringWithFormat:@"{messageId:%d, rd:%d, ra:%d, aa:%d, rCode:%d, server:%@, request:%@, answerArray:%@, authorityArray:%@, additionalArray:%@}", self.messageId, self.rd, self.ra, self.aa, self.rCode, self.server, self.request, self.answerArray, self.authorityArray, self.additionalArray];
}
@end

View File

@@ -0,0 +1,47 @@
//
// QNDnsServer.h
// Doh
//
// Created by yangsen on 2021/7/20.
//
#import "QNDnsResolver.h"
NS_ASSUME_NONNULL_BEGIN
@interface QNDnsUdpResolver : QNDnsResolver
/// 构造函数
/// @param serverIP 指定 dns local server1. eg:114.114.114.114
+ (instancetype)resolverWithServerIP:(NSString *)serverIP;
/// 构造函数
/// @param serverIP 指定 dns local server1. eg:114.114.114.114
/// @param recordType 记录类型 egkQNTypeA
/// @param timeout 超时时间
+ (instancetype)resolverWithServerIP:(NSString *)serverIP
recordType:(int)recordType
timeout:(int)timeout;
/// 构造函数
/// @param serverIPs 指定多个 dns local server同时进行 dns 解析,当第一个有效数据返回时结束,或均为解析到数据时结束. eg:@[@"8.8.8.8"]
/// @param recordType 记录类型 egkQNTypeA
/// @param timeout 超时时间
+ (instancetype)resolverWithServerIPs:(NSArray <NSString *> *)serverIPs
recordType:(int)recordType
timeout:(int)timeout;
/// 构造函数
/// @param serverIPs 指定多个 dns local server同时进行 dns 解析,当第一个有效数据返回时结束,或均为解析到数据时结束. eg:@[@"8.8.8.8"]
/// @param recordType 记录类型 egkQNTypeA
/// @param queue 多个 udp 包所在的 queue
/// @param timeout 超时时间
+ (instancetype)resolverWithServerIPs:(NSArray <NSString *> *)serverIPs
recordType:(int)recordType
queue:(dispatch_queue_t _Nullable)queue
timeout:(int)timeout;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,214 @@
//
// QNDnsServer.m
// Doh
//
// Created by yangsen on 2021/7/20.
//
#import "QNRecord.h"
#import "QNDomain.h"
#import "QNDnsError.h"
#import "QNDnsResponse.h"
#import "QNDnsUdpResolver.h"
#import "QNAsyncUdpSocket.h"
@interface QNDnsFlow : NSObject
@property(nonatomic, assign)long flowId;
@property(nonatomic, copy)NSString *server;
@property(nonatomic, strong)QNDnsRequest *dnsRequest;
@property(nonatomic, strong)QNAsyncUdpSocket *socket;
@property(nonatomic, copy)void(^complete)(QNDnsResponse *response, NSError *error);
@end
@implementation QNDnsFlow
@end
#define kDnsPort 53
@interface QNDnsUdpResolver()<QNAsyncUdpSocketDelegate>
@property(nonatomic, assign)int recordType;
@property(nonatomic, assign)int timeout;
@property(nonatomic, copy)NSArray *servers;
@property(nonatomic, strong)dispatch_queue_t queue;
@property(nonatomic, strong)NSMutableDictionary *flows;
@end
@implementation QNDnsUdpResolver
@synthesize recordType;
@synthesize timeout;
@synthesize servers;
+ (instancetype)resolverWithServerIP:(NSString *)serverIP {
return [self resolverWithServerIP:serverIP recordType:kQNTypeA timeout:QN_DNS_DEFAULT_TIMEOUT];
}
+ (instancetype)resolverWithServerIP:(NSString *)serverIP
recordType:(int)recordType
timeout:(int)timeout {
return [self resolverWithServerIPs:serverIP ? @[serverIP] : @[] recordType:recordType timeout:timeout];
}
+ (instancetype)resolverWithServerIPs:(NSArray <NSString *> *)serverIPs
recordType:(int)recordType
timeout:(int)timeout {
return [self resolverWithServerIPs:serverIPs recordType:recordType queue:nil timeout:timeout];
}
+ (instancetype)resolverWithServerIPs:(NSArray <NSString *> *)servers
recordType:(int)recordType
queue:(dispatch_queue_t _Nullable)queue
timeout:(int)timeout {
QNDnsUdpResolver *resolver = [[self alloc] init];
resolver.recordType = recordType;
resolver.servers = [servers copy] ?: @[];
resolver.timeout = timeout;
resolver.queue = queue;
return resolver;
}
+ (dispatch_queue_t)defaultQueue {
static dispatch_once_t onceToken;
static dispatch_queue_t timerQueue;
dispatch_once(&onceToken, ^{
timerQueue = dispatch_queue_create("com.qiniu.dns.udp.queue", DISPATCH_QUEUE_CONCURRENT);
});
return timerQueue;
}
- (dispatch_queue_t)queue {
if (_queue == nil) {
_queue = [QNDnsUdpResolver defaultQueue];
}
return _queue;
}
- (NSMutableDictionary *)flows {
if (_flows == nil) {
_flows = [NSMutableDictionary dictionary];
}
return _flows;
}
- (void)request:(NSString *)server
host:(NSString *)host
recordType:(int)recordType
complete:(void(^)(QNDnsResponse *response, NSError *error))complete {
if (complete == nil) {
return;
}
int messageId = arc4random()%(0xFFFF);
QNDnsRequest *dnsRequest = [QNDnsRequest request:messageId recordType:recordType host:host];
NSError *error = nil;
NSData *requestData = [dnsRequest toDnsQuestionData:&error];
if (error) {
complete(nil, error);
return;
}
QNAsyncUdpSocket *socket = [[QNAsyncUdpSocket alloc] initWithDelegate:self delegateQueue:self.queue];
//
[socket bindToPort:0 error: &error];
if (error) {
complete(nil, error);
return;
}
[socket beginReceiving:&error];
if (error) {
complete(nil, error);
return;
}
QNDnsFlow *flow = [[QNDnsFlow alloc] init];
flow.flowId = [socket hash];
flow.server = server;
flow.dnsRequest = dnsRequest;
flow.socket = socket;
flow.complete = complete;
[self setFlow:flow withId:flow.flowId];
[socket sendData:requestData toHost:server port:kDnsPort withTimeout:self.timeout tag:flow.flowId];
}
- (void)udpSocketComplete:(QNAsyncUdpSocket *)sock data:(NSData *)data error:(NSError * _Nullable)error {
[sock close];
QNDnsFlow *flow = [self getFlowWithId:[sock hash]];
if (!flow) {
return;
}
[self removeFlowWithId:flow.flowId];
if (error != nil) {
flow.complete(nil, error);
} else if (data != nil) {
NSError *err = nil;
QNDnsResponse *response = [QNDnsResponse dnsResponse:flow.server source:QNRecordSourceUdp request:flow.dnsRequest dnsRecordData:data error:&err];
flow.complete(response, err);
} else {
flow.complete(nil, nil);
}
}
//MARK: -- QNAsyncUdpSocketDelegate
- (void)udpSocket:(QNAsyncUdpSocket *)sock didConnectToAddress:(NSData *)address {
}
- (void)udpSocket:(QNAsyncUdpSocket *)sock didNotConnect:(NSError * _Nullable)error {
[self udpSocketComplete:sock data:nil error:error];
}
- (void)udpSocket:(QNAsyncUdpSocket *)sock didSendDataWithTag:(long)tag {
}
- (void)udpSocket:(QNAsyncUdpSocket *)sock didNotSendDataWithTag:(long)tag dueToError:(NSError * _Nullable)error {
[self udpSocketComplete:sock data:nil error:error];
}
- (void)udpSocket:(QNAsyncUdpSocket *)sock
didReceiveData:(NSData *)data
fromAddress:(NSData *)address
withFilterContext:(nullable id)filterContext {
[self udpSocketComplete:sock data:data error:nil];
}
- (void)udpSocketDidClose:(QNAsyncUdpSocket *)sock withError:(NSError * _Nullable)error {
[self udpSocketComplete:sock data:nil error:error];
}
//MARK: flows
- (QNDnsFlow *)getFlowWithId:(long)flowId {
NSString *key = [NSString stringWithFormat:@"%ld", flowId];
QNDnsFlow *flow = nil;
@synchronized (self) {
flow = self.flows[key];
}
return flow;
}
- (BOOL)setFlow:(QNDnsFlow *)flow withId:(long)flowId {
if (flow == nil) {
return false;
}
NSString *key = [NSString stringWithFormat:@"%ld", flowId];
@synchronized (self) {
self.flows[key] = flow;
}
return true;
}
- (void)removeFlowWithId:(long)flowId {
NSString *key = [NSString stringWithFormat:@"%ld", flowId];
@synchronized (self) {
[self.flows removeObjectForKey:key];
}
}
@end

View File

@@ -0,0 +1,38 @@
//
// Doh.h
// Doh
//
// Created by yangsen on 2021/7/15.
//
#import "QNDnsResolver.h"
#import "QNDnsDefine.h"
NS_ASSUME_NONNULL_BEGIN
@interface QNDohResolver : QNDnsResolver
/// 构造函数
/// @param server 指定 dns server url。 eg:https://dns.google/dns-query
+ (instancetype)resolverWithServer:(NSString *)server;
/// 构造函数
/// @param server 指定 dns server url。 eg:https://dns.google/dns-query
/// @param recordType 记录类型 egkQNTypeA
/// @param timeout 超时时间
+ (instancetype)resolverWithServer:(NSString *)server
recordType:(int)recordType
timeout:(int)timeout;
/// 构造函数
/// @param servers 指定多个 dns server url同时进行 dns 解析,当第一个有效数据返回时结束,或均为解析到数据时结束
/// eg:https://dns.google/dns-query
/// @param recordType 记录类型 egkQNTypeA
/// @param timeout 超时时间
+ (instancetype)resolverWithServers:(NSArray <NSString *> *)servers
recordType:(int)recordType
timeout:(int)timeout;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,85 @@
//
// Doh.m
// Doh
//
// Created by yangsen on 2021/7/15.
//
#import "QNDnsResponse.h"
#import "QNDohResolver.h"
@interface QNDohResolver()
@property(nonatomic, assign)int recordType;
@property(nonatomic, assign)int timeout;
@property(nonatomic, copy)NSArray *servers;
@end
@implementation QNDohResolver
@synthesize recordType;
@synthesize timeout;
@synthesize servers;
+ (instancetype)resolverWithServer:(NSString *)server {
return [self resolverWithServer:server recordType:kQNTypeA timeout:QN_DNS_DEFAULT_TIMEOUT];
}
+ (instancetype)resolverWithServer:(NSString *)server
recordType:(int)recordType
timeout:(int)timeout {
return [self resolverWithServers:server ? @[server] : @[] recordType:recordType timeout:timeout];
}
+ (instancetype)resolverWithServers:(NSArray <NSString *> *)servers
recordType:(int)recordType
timeout:(int)timeout {
QNDohResolver *resolver = [[self alloc] init];
resolver.recordType = recordType;
resolver.servers = [servers copy] ?: @[];
resolver.timeout = timeout;
return resolver;
}
- (void)request:(NSString *)server
host:(NSString *)host
recordType:(int)recordType
complete:(void(^)(QNDnsResponse *response, NSError *error))complete {
if (complete == nil) {
return;
}
if (host == nil || host.length == 0) {
complete(nil, kQNDnsInvalidParamError(@"host can not empty"));
return;
}
int messageId = arc4random()%(0xFFFF);
QNDnsRequest *dnsRequest = [QNDnsRequest request:messageId recordType:recordType host:host];
NSError *error = nil;
NSData *requestData = [dnsRequest toDnsQuestionData:&error];
if (error) {
complete(nil, error);
return;
}
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:server]];
request.HTTPMethod = @"POST";
request.HTTPBody = requestData;
request.timeoutInterval = self.timeout;
[request addValue:@"application/dns-message" forHTTPHeaderField:@"Content-Type"];
[request addValue:@"application/dns-message" forHTTPHeaderField:@"Accept"];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (error) {
complete(nil, error);
} else if (data) {
QNDnsResponse *dnsResponse = [QNDnsResponse dnsResponse:server source:QNRecordSourceDoh request:dnsRequest dnsRecordData:data error:nil];
complete(dnsResponse, nil);
} else {
complete(nil, nil);
}
}];
[task resume];
}
@end

25
Pods/HappyDNS/HappyDNS/HappyDNS.h generated Normal file
View File

@@ -0,0 +1,25 @@
//
// HappyDNS.h
// HappyDNS
//
// Created by bailong on 15/6/24.
// Copyright (c) 2015年 Qiniu Cloud Storage. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "QNDnsError.h"
#import "QNDnsManager.h"
#import "QNDnspodEnterprise.h"
#import "QNDomain.h"
#import "QNDnsResolver.h"
#import "QNDnsUdpResolver.h"
#import "QNDohResolver.h"
#import "QNDnsDefine.h"
#import "QNHijackingDetectWrapper.h"
#import "QNIP.h"
#import "QNNetworkInfo.h"
#import "QNRecord.h"
#import "QNResolver.h"
#import "QNResolverDelegate.h"
#import "QNGetAddrInfo.h"

View File

@@ -0,0 +1,30 @@
//
// QNDnspodEnterprise.h
// HappyDNS
//
// Created by bailong on 15/7/31.
// Copyright (c) 2015年 Qiniu Cloud Storage. All rights reserved.
//
#import "QNResolverDelegate.h"
#import <Foundation/Foundation.h>
extern const int kQN_ENCRYPT_FAILED;
extern const int kQN_DECRYPT_FAILED;
@interface QNDnspodEnterprise : NSObject <QNResolverDelegate>
- (instancetype)initWithId:(NSString *)userId
key:(NSString *)key;
- (instancetype)initWithId:(NSString *)userId
key:(NSString *)key
server:(NSString *)server;
- (instancetype)initWithId:(NSString *)userId
key:(NSString *)key
server:(NSString *)server
timeout:(NSUInteger)time;
- (NSArray *)query:(QNDomain *)domain networkInfo:(QNNetworkInfo *)netInfo error:(NSError *__autoreleasing *)error;
@end

View File

@@ -0,0 +1,141 @@
//
// QNDnspodEnterprise.m
// HappyDNS
//
// Created by bailong on 15/6/23.
// Copyright (c) 2015 Qiniu Cloud Storage. All rights reserved.
//
#import "QNDnspodEnterprise.h"
#import <CommonCrypto/CommonCryptor.h>
#import "QNDes.h"
#import "QNDomain.h"
#import "QNHex.h"
#import "QNIP.h"
#import "QNRecord.h"
const int kQN_ENCRYPT_FAILED = -10001;
const int kQN_DECRYPT_FAILED = -10002;
@interface QNDnspodEnterprise ()
@property (readonly, strong) NSString *server;
@property (nonatomic, strong) NSString *userId;
@property (nonatomic, strong) QNDes *des;
@property (nonatomic) NSUInteger timeout;
@end
@implementation QNDnspodEnterprise
- (instancetype)initWithId:(NSString *)userId
key:(NSString *)key {
return [self initWithId:userId key:key server:@"119.29.29.98"];
}
- (instancetype)initWithId:(NSString *)userId
key:(NSString *)key
server:(NSString *)server {
return [self initWithId:userId key:key server:@"119.29.29.98" timeout:QN_DNS_DEFAULT_TIMEOUT];
}
- (instancetype)initWithId:(NSString *)userId
key:(NSString *)key
server:(NSString *)server
timeout:(NSUInteger)time {
if (self = [super init]) {
_server = server;
_userId = userId;
_des = [[QNDes alloc] init:[key dataUsingEncoding:NSUTF8StringEncoding]];
_timeout = time;
}
return self;
}
- (NSString *)encrypt:(NSString *)domain {
NSData *data = [_des encrypt:[domain dataUsingEncoding:NSUTF8StringEncoding]];
if (data == nil) {
return nil;
}
NSString *str = [QNHex encodeHexData:data];
return str;
}
- (NSString *)decrypt:(NSData *)raw {
NSData *enc = [QNHex decodeHexString:[[NSString alloc] initWithData:raw
encoding:NSUTF8StringEncoding]];
if (enc == nil) {
return nil;
}
NSData *data = [_des decrpyt:enc];
if (data == nil) {
return nil;
}
return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
}
- (NSArray *)query:(QNDomain *)domain networkInfo:(QNNetworkInfo *)netInfo error:(NSError *__autoreleasing *)error {
NSString *encrypt = [self encrypt:domain.domain];
if (encrypt == nil) {
if (error != nil) {
*error = [[NSError alloc] initWithDomain:domain.domain code:kQN_ENCRYPT_FAILED userInfo:nil];
}
return nil;
}
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
__block NSData *data = nil;
__block NSError *httpError = nil;
__block NSHTTPURLResponse *response = nil;
NSString *url = [NSString stringWithFormat:@"http://%@/d?ttl=1&dn=%@&id=%@", [QNIP ipHost:_server], encrypt, _userId];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:url] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:_timeout];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionTask *task = [session dataTaskWithRequest:urlRequest completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable resp, NSError * _Nullable error) {
data = data;
httpError = error;
response = (NSHTTPURLResponse *)resp;
dispatch_semaphore_signal(semaphore);
}];
[task resume];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
if (httpError != nil) {
if (error != nil) {
*error = httpError;
}
return nil;
}
if (response.statusCode != 200) {
return nil;
}
NSString *raw = [self decrypt:data];
if (raw == nil) {
if (error != nil) {
*error = [[NSError alloc] initWithDomain:domain.domain code:kQN_DECRYPT_FAILED userInfo:nil];
}
return nil;
}
NSArray *ip1 = [raw componentsSeparatedByString:@","];
if (ip1.count != 2) {
return nil;
}
NSString *ttlStr = [ip1 objectAtIndex:1];
int ttl = [ttlStr intValue];
if (ttl <= 0) {
return nil;
}
NSString *ips = [ip1 objectAtIndex:0];
NSArray *ipArray = [ips componentsSeparatedByString:@";"];
NSMutableArray *ret = [[NSMutableArray alloc] initWithCapacity:ipArray.count];
for (int i = 0; i < ipArray.count; i++) {
QNRecord *record = [[QNRecord alloc] init:[ipArray objectAtIndex:i] ttl:ttl type:kQNTypeA source:QNRecordSourceDnspodEnterprise];
[ret addObject:record];
}
return ret;
}
@end

View File

@@ -0,0 +1,17 @@
//
// QNHijackingDetectWrapper.h
// HappyDNS
//
// Created by bailong on 15/7/16.
// Copyright (c) 2015年 Qiniu Cloud Storage. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "QNResolverDelegate.h"
@class QNResolver;
@interface QNHijackingDetectWrapper : NSObject <QNResolverDelegate>
- (NSArray *)query:(QNDomain *)domain networkInfo:(QNNetworkInfo *)netInfo error:(NSError *__autoreleasing *)error;
- (instancetype)initWithResolver:(QNResolver *)resolver;
@end

View File

@@ -0,0 +1,51 @@
//
// QNHijackingDetectWrapper.m
// HappyDNS
//
// Created by bailong on 15/7/16.
// Copyright (c) 2015 Qiniu Cloud Storage. All rights reserved.
//
#import "QNHijackingDetectWrapper.h"
#import "QNDomain.h"
#import "QNRecord.h"
#import "QNResolver.h"
@interface QNHijackingDetectWrapper ()
@property (nonatomic, readonly) QNResolver *resolver;
@end
@implementation QNHijackingDetectWrapper
- (NSArray *)query:(QNDomain *)domain networkInfo:(QNNetworkInfo *)netInfo error:(NSError *__autoreleasing *)error {
NSArray *result = [_resolver query:domain networkInfo:netInfo error:error];
if (((!domain.hasCname) && domain.maxTtl == 0) || result == nil || result.count == 0) {
return result;
}
BOOL hasCname = NO;
BOOL outOfTtl = NO;
for (int i = 0; i < result.count; i++) {
QNRecord *record = [result objectAtIndex:i];
if (record.type == kQNTypeCname) {
hasCname = YES;
}
if (domain.maxTtl > 0 && record.type == kQNTypeA && record.ttl > domain.maxTtl) {
outOfTtl = YES;
}
}
if ((domain.hasCname && !hasCname) || outOfTtl) {
if (error != nil) {
*error = [[NSError alloc] initWithDomain:domain.domain code:kQNDomainHijackingCode userInfo:nil];
}
return nil;
}
return result;
}
- (instancetype)initWithResolver:(QNResolver *)resolver {
if (self = [super init]) {
_resolver = resolver;
}
return self;
}
@end

20
Pods/HappyDNS/HappyDNS/Local/QNHosts.h generated Normal file
View File

@@ -0,0 +1,20 @@
//
// QNHosts.h
// HappyDNS
//
// Created by bailong on 15/6/23.
// Copyright (c) 2015年 Qiniu Cloud Storage. All rights reserved.
//
#import "QNRecord.h"
#import "QNResolverDelegate.h"
#import <Foundation/Foundation.h>
@interface QNHosts : NSObject
- (NSArray <QNRecord *> *)query:(QNDomain *)domain networkInfo:(QNNetworkInfo *)netInfo;
- (void)put:(NSString *)domain record:(QNRecord *)record;
- (void)put:(NSString *)domain record:(QNRecord *)record provider:(int)provider;
@end

111
Pods/HappyDNS/HappyDNS/Local/QNHosts.m generated Normal file
View File

@@ -0,0 +1,111 @@
//
// QNHosts.m
// HappyDNS
//
// Created by bailong on 15/6/23.
// Copyright (c) 2015 Qiniu Cloud Storage. All rights reserved.
//
#import "QNHosts.h"
#import "QNDomain.h"
#import "QNNetworkInfo.h"
@interface QNHostsValue : NSObject
@property (nonatomic, copy, readonly) QNRecord *record;
@property (readonly) int provider;
@end
@implementation QNHostsValue
- (instancetype)init:(QNRecord *)record provider:(int)provider {
if (self = [super init]) {
_record = record;
_provider = provider;
}
return self;
}
@end
static NSArray<QNHostsValue *> *filter(NSArray *input, int provider) {
NSMutableArray<QNHostsValue *> *normal = [[NSMutableArray alloc] initWithCapacity:input.count];
NSMutableArray<QNHostsValue *> *special = [[NSMutableArray alloc] init];
for (QNHostsValue *v in input) {
if (v.provider == kQNISP_GENERAL) {
[normal addObject:v];
} else if (provider == v.provider) {
[special addObject:v];
}
}
if (special.count != 0) {
return special;
}
return [normal copy];
}
@interface QNHosts ()
@property (nonatomic) NSMutableDictionary *dict;
@end
@implementation QNHosts
- (NSArray <QNRecord *> *)query:(QNDomain *)domain networkInfo:(QNNetworkInfo *)netInfo {
NSMutableArray *x;
@synchronized(_dict) {
x = [_dict objectForKey:domain.domain];
}
if (x == nil || x.count == 0) {
return nil;
}
@synchronized (_dict) {
if (x.count >= 2) {
QNHostsValue *first = [x firstObject];
[x removeObjectAtIndex:0];
[x addObject:first];
}
}
NSArray <QNHostsValue *> *values = filter([x copy], netInfo.provider);
return [self toRecords:values];
}
- (NSArray <QNRecord *> *)toRecords:(NSArray <QNHostsValue *> *)values {
if (values == nil) {
return nil;
}
NSMutableArray<QNRecord *> *records = [NSMutableArray array];
for (QNHostsValue *value in values) {
if (value.record != nil && value.record.value != nil) {
[records addObject:value.record];
}
}
return [records copy];
}
- (void)put:(NSString *)domain record:(QNRecord *)record {
[self put:domain record:record provider:kQNISP_GENERAL];
}
- (void)put:(NSString *)domain record:(QNRecord *)record provider:(int)provider {
QNHostsValue *v = [[QNHostsValue alloc] init:record provider:provider];
@synchronized(_dict) {
NSMutableArray *x = [_dict objectForKey:domain];
if (x == nil) {
x = [[NSMutableArray alloc] init];
}
[x addObject:v];
[_dict setObject:x forKey:domain];
}
}
- (instancetype)init {
if (self = [super init]) {
_dict = [[NSMutableDictionary alloc] init];
}
return self;
}
@end

View File

@@ -0,0 +1,16 @@
//
// QNResolv.h
// HappyDNS
//
// Created by bailong on 16/5/28.
// Copyright © 2016年 Qiniu Cloud Storage. All rights reserved.
//
#ifndef QNResolv_h
#define QNResolv_h
extern BOOL isV6(NSString *address);
extern int setup_dns_server(void *res, NSString *dns_server, NSUInteger timeout);
#endif /* QNResolv_h */

View File

@@ -0,0 +1,84 @@
//
// QNResolvUtil.m
// HappyDNS
//
// Created by bailong on 16/5/28.
// Copyright © 2016 Qiniu Cloud Storage. All rights reserved.
//
#import <Foundation/Foundation.h>
#include <arpa/inet.h>
#include <resolv.h>
#include <string.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <unistd.h>
#import "QNIP.h"
#if __IPHONE_OS_VERSION_MIN_REQUIRED
#import <MobileCoreServices/MobileCoreServices.h>
#import <UIKit/UIKit.h>
#endif
BOOL isV6(NSString *address) {
return strchr(address.UTF8String, ':') != NULL;
}
int setup_dns_server(void *_res_state, NSString *dns_server, NSUInteger timeout) {
res_state res = (res_state)_res_state;
int r = res_ninit(res);
if (r != 0) {
return r;
}
res->retrans = (int)timeout;
res->retry = 1;
if (dns_server == nil) {
return 0;
}
res->options |= RES_IGNTC;
union res_sockaddr_union server = {0};
struct addrinfo hints = {0}, *ai = NULL;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
int ret = getaddrinfo(dns_server.UTF8String, "53", &hints, &ai);
if (ret != 0) {
return ret;
}
int family = ai->ai_family;
if (family == AF_INET6) {
((struct sockaddr_in6 *)ai->ai_addr)->sin6_port = htons(53);
server.sin6 = *((struct sockaddr_in6 *)ai->ai_addr);
} else {
server.sin = *((struct sockaddr_in *)ai->ai_addr);
}
if (![QNIP isIpV6FullySupported] && family == AF_INET) {
if ([QNIP isV6]) {
freeaddrinfo(ai);
ai = NULL;
bzero(&hints, (size_t)0);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
char buf[64] = {0};
qn_nat64(buf, sizeof(buf), (uint32_t)server.sin.sin_addr.s_addr);
int ret = getaddrinfo(buf, "53", &hints, &ai);
if (ret != 0) {
return -1;
}
((struct sockaddr_in6 *)ai->ai_addr)->sin6_port = htons(53);
server.sin6 = *((struct sockaddr_in6 *)ai->ai_addr);
}
}
freeaddrinfo(ai);
res_setservers(res, &server, 1);
return 0;
}

View File

@@ -0,0 +1,25 @@
//
// QNResolver.h
// HappyDNS
//
// Created by bailong on 15/6/23.
// Copyright (c) 2015年 Qiniu Cloud Storage. All rights reserved.
//
#import "QNResolverDelegate.h"
#import <Foundation/Foundation.h>
@interface QNResolver : NSObject <QNResolverDelegate>
- (NSArray *)query:(QNDomain *)domain networkInfo:(QNNetworkInfo *)netInfo error:(NSError *__autoreleasing *)error;
// @deprecated typo
- (instancetype)initWithAddres:(NSString *)address DEPRECATED_ATTRIBUTE;
- (instancetype)initWithAddress:(NSString *)address;
- (instancetype)initWithAddress:(NSString *)address
timeout:(NSUInteger)time;
+ (instancetype)systemResolver;
+ (NSString *)systemDnsServer;
@end

153
Pods/HappyDNS/HappyDNS/Local/QNResolver.m generated Normal file
View File

@@ -0,0 +1,153 @@
//
// QNResolver.m
// HappyDNS
//
// Created by bailong on 15/6/23.
// Copyright (c) 2015 Qiniu Cloud Storage. All rights reserved.
//
#include <arpa/inet.h>
#include <resolv.h>
#include <string.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <unistd.h>
#import "QNDomain.h"
#import "QNIP.h"
#import "QNRecord.h"
#import "QNResolver.h"
#import "QNResolvUtil.h"
#if __IPHONE_OS_VERSION_MIN_REQUIRED
#import <MobileCoreServices/MobileCoreServices.h>
#import <UIKit/UIKit.h>
#endif
@interface QNResolver ()
@property (nonatomic, readonly, strong) NSString *address;
@property (nonatomic, readonly) NSUInteger timeout;
@end
static NSArray *query_ip_v4(res_state res, const char *host) {
u_char answer[2000];
int len = res_nquery(res, host, ns_c_in, ns_t_a, answer, sizeof(answer));
ns_msg handle;
ns_initparse(answer, len, &handle);
int count = ns_msg_count(handle, ns_s_an);
if (count <= 0) {
res_ndestroy(res);
return nil;
}
NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:count];
char buf[32];
char cnameBuf[NS_MAXDNAME];
memset(cnameBuf, 0, sizeof(cnameBuf));
for (int i = 0; i < count; i++) {
ns_rr rr;
if (ns_parserr(&handle, ns_s_an, i, &rr) != 0) {
res_ndestroy(res);
return nil;
}
int t = ns_rr_type(rr);
int ttl = ns_rr_ttl(rr);
NSString *val;
if (t == ns_t_a) {
const char *p = inet_ntop(AF_INET, ns_rr_rdata(rr), buf, sizeof(buf));
val = [NSString stringWithUTF8String:p];
} else if (t == ns_t_cname) {
int x = ns_name_uncompress(answer, &(answer[len]), ns_rr_rdata(rr), cnameBuf, sizeof(cnameBuf));
if (x <= 0) {
continue;
}
val = [NSString stringWithUTF8String:cnameBuf];
memset(cnameBuf, 0, sizeof(cnameBuf));
} else {
continue;
}
QNRecord *record = [[QNRecord alloc] init:val ttl:ttl type:t source:QNRecordSourceSystem];
[array addObject:record];
}
res_ndestroy(res);
return array;
}
@implementation QNResolver
- (instancetype)initWithAddres:(NSString *)address {
return [self initWithAddress:address];
}
- (instancetype)initWithAddress:(NSString *)address {
return [self initWithAddress:address timeout:QN_DNS_DEFAULT_TIMEOUT];
}
- (instancetype)initWithAddress:(NSString *)address
timeout:(NSUInteger)time {
if (self = [super init]) {
_address = address;
_timeout = time;
}
return self;
}
- (NSArray *)query:(QNDomain *)domain networkInfo:(QNNetworkInfo *)netInfo error:(NSError *__autoreleasing *)error {
struct __res_state res;
int r = setup_dns_server(&res, _address, _timeout);
if (r != 0) {
if (error != nil) {
*error = [[NSError alloc] initWithDomain:@"qiniu.dns" code:kQNDomainSeverError userInfo:nil];
}
return nil;
}
NSArray *ret = query_ip_v4(&res, [domain.domain cStringUsingEncoding:NSUTF8StringEncoding]);
if (ret != nil && ret.count != 0) {
return ret;
}
if (error != nil) {
*error = [[NSError alloc] initWithDomain:@"qiniu.dns" code:NSURLErrorDNSLookupFailed userInfo:nil];
}
return nil;
}
+ (instancetype)systemResolver {
return [[QNResolver alloc] initWithAddress:nil];
}
+ (NSString *)systemDnsServer {
struct __res_state res;
int r = res_ninit(&res);
if (r != 0) {
return nil;
}
union res_sockaddr_union server[MAXNS] = {0};
r = res_getservers(&res, server, MAXNS);
res_ndestroy(&res);
if (r <= 0) {
return nil;
}
int family = server[0].sin.sin_family;
char buf[64] = {0};
const void *addr;
if (family == AF_INET6) {
addr = &server[0].sin6.sin6_addr;
} else {
addr = &server[0].sin.sin_addr;
}
const char *p = inet_ntop(family, addr, buf, sizeof(buf));
if (p == NULL) {
return nil;
}
return [NSString stringWithUTF8String:p];
}
@end

View File

@@ -0,0 +1,23 @@
//
// QNTxtResolver.h
// HappyDNS
//
// Created by bailong on 16/1/5.
// Copyright © 2016年 Qiniu Cloud Storage. All rights reserved.
//
#import "QNResolverDelegate.h"
#import <Foundation/Foundation.h>
@interface QNTxtResolver : NSObject <QNResolverDelegate>
- (NSArray *)query:(QNDomain *)domain networkInfo:(QNNetworkInfo *)netInfo error:(NSError *__autoreleasing *)error;
/**
* 根据服务器地址进行初始化
*
* @param address DNS 服务器地址nil 表示系统的
*/
- (instancetype)initWithAddress:(NSString *)address;
- (instancetype)initWithAddress:(NSString *)address timeout:(NSUInteger)time;
@end

View File

@@ -0,0 +1,109 @@
//
// QNTxtResolver.m
// HappyDNS
//
// Created by bailong on 16/1/5.
// Copyright © 2016 Qiniu Cloud Storage. All rights reserved.
//
#import "QNTxtResolver.h"
#include <arpa/inet.h>
#include <resolv.h>
#include <string.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <unistd.h>
#import "QNDomain.h"
#import "QNRecord.h"
#import "QNResolvUtil.h"
#import "QNResolver.h"
@interface QNTxtResolver ()
@property (nonatomic, readonly, strong) NSString *address;
@property (nonatomic, readonly) NSUInteger timeout;
@end
static NSArray *query_ip(res_state res, const char *host) {
u_char answer[1500];
int len = res_nquery(res, host, ns_c_in, ns_t_txt, answer, sizeof(answer));
ns_msg handle;
ns_initparse(answer, len, &handle);
int count = ns_msg_count(handle, ns_s_an);
if (count != 1) {
res_ndestroy(res);
return nil;
}
char txtbuf[256];
memset(txtbuf, 0, sizeof(txtbuf));
ns_rr rr;
if (ns_parserr(&handle, ns_s_an, 0, &rr) != 0) {
res_ndestroy(res);
return nil;
}
int t = ns_rr_type(rr);
int ttl = ns_rr_ttl(rr);
int rdlen = ns_rr_rdlen(rr);
if (rdlen <= 1 + 7) {
res_ndestroy(res);
return nil;
}
NSString *val;
if (t == ns_t_txt) {
memcpy(txtbuf, ns_rr_rdata(rr) + 1, rdlen - 1);
val = [NSString stringWithUTF8String:txtbuf];
} else {
res_ndestroy(res);
return nil;
}
NSArray *ipArray = [val componentsSeparatedByString:@","];
NSMutableArray *ret = [[NSMutableArray alloc] initWithCapacity:ipArray.count];
for (int i = 0; i < ipArray.count; i++) {
QNRecord *record = [[QNRecord alloc] init:[ipArray objectAtIndex:i] ttl:ttl type:kQNTypeA source:QNRecordSourceSystem];
[ret addObject:record];
}
res_ndestroy(res);
return ret;
}
@implementation QNTxtResolver
- (instancetype)initWithAddress:(NSString *)address {
return [self initWithAddress:address timeout:QN_DNS_DEFAULT_TIMEOUT];
}
- (instancetype)initWithAddress:(NSString *)address timeout:(NSUInteger)time {
if (self = [super init]) {
_address = address;
_timeout = time;
}
return self;
}
- (NSArray *)query:(QNDomain *)domain networkInfo:(QNNetworkInfo *)netInfo error:(NSError *__autoreleasing *)error {
struct __res_state res;
int r = setup_dns_server(&res, _address, _timeout);
if (r != 0) {
if (error != nil) {
*error = [[NSError alloc] initWithDomain:@"qiniu.dns" code:kQNDomainSeverError userInfo:nil];
}
return nil;
}
NSArray *ret = query_ip(&res, [domain.domain cStringUsingEncoding:NSUTF8StringEncoding]);
if (ret == nil && error != nil) {
*error = [[NSError alloc] initWithDomain:@"qiniu.dns" code:NSURLErrorDNSLookupFailed userInfo:nil];
}
return ret;
}
@end

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSPrivacyTracking</key>
<false/>
<key>NSPrivacyAccessedAPITypes</key>
<array/>
<key>NSPrivacyTrackingDomains</key>
<array/>
<key>NSPrivacyCollectedDataTypes</key>
<array/>
</dict>
</plist>

View File

@@ -0,0 +1,75 @@
//
// NSMutableData+QNWriter.h
// Doh
//
// Created by yangsen on 2021/7/16.
//
#import <Foundation/Foundation.h>
@interface NSData (QNReader)
//MARK: 读数据
- (uint8_t)qn_readInt8:(NSInteger)from;
- (uint16_t)qn_readLittleEndianInt16:(NSInteger)from;
- (uint32_t)qn_readLittleEndianInt32:(NSInteger)from;
- (uint64_t)qn_readLittleEndianInt64:(NSInteger)from;
- (uint16_t)qn_readBigEndianInt16:(NSInteger)from;
- (uint32_t)qn_readBigEndianInt32:(NSInteger)from;
- (uint64_t)qn_readBigEndianInt64:(NSInteger)from;
- (int8_t)qn_readSInt8:(NSInteger)from;
- (int16_t)qn_readLittleEndianSInt16:(NSInteger)from;
- (int32_t)qn_readLittleEndianSInt32:(NSInteger)from;
- (int64_t)qn_readLittleEndianSInt64:(NSInteger)from;
- (int16_t)qn_readBigEndianSInt16:(NSInteger)from;
- (int32_t)qn_readBigEndianSInt32:(NSInteger)from;
- (int64_t)qn_readBigEndianSInt64:(NSInteger)from;
- (float)qn_readFloat:(NSInteger)from;
- (double)qn_readDouble:(NSInteger)from;
- (float)qn_readSwappedFloat:(NSInteger)from;
- (double)qn_readSwappedDouble:(NSInteger)from;
- (NSString *)qn_readString:(NSRange)range usingEncoding:(NSStringEncoding)encoding;
@end
@interface NSMutableData (QNWriter)
//MARK: 写数据
- (void)qn_appendInt8:(uint8_t)value;
- (void)qn_appendLittleEndianInt16:(uint16_t)value;
- (void)qn_appendLittleEndianInt32:(uint32_t)value;
- (void)qn_appendLittleEndianInt64:(uint64_t)value;
- (void)qn_appendBigEndianInt16:(uint16_t)value;
- (void)qn_appendBigEndianInt32:(uint32_t)value;
- (void)qn_appendBigEndianInt64:(uint64_t)value;
- (void)qn_appendSInt8:(int8_t)value;
- (void)qn_appendLittleEndianSInt16:(int16_t)value;
- (void)qn_appendLittleEndianSInt32:(int32_t)value;
- (void)qn_appendLittleEndianSInt64:(int64_t)value;
- (void)qn_appendBigEndianSInt16:(int16_t)value;
- (void)qn_appendBigEndianSInt32:(int32_t)value;
- (void)qn_appendBigEndianSInt64:(int64_t)value;
// These methods append floating point values depending on the architecture of your processor
// they're usually not appropriate for network transmission
- (void)qn_appendFloat:(float)value;
- (void)qn_appendDouble:(double)value;
- (void)qn_appendSwappedFloat:(float)value;
- (void)qn_appendSwappedDouble:(double)value;
- (void)qn_appendString:(NSString *)value usingEncoding:(NSStringEncoding)encoding;
@end

151
Pods/HappyDNS/HappyDNS/Util/NSData+QNRW.m generated Normal file
View File

@@ -0,0 +1,151 @@
//
// NSMutableData+QNWriter.m
// Doh
//
// Created by yangsen on 2021/7/16.
//
#import "NSData+QNRW.h"
@implementation NSData (QNReader)
//MARK:
- (uint8_t)qn_readInt8:(NSInteger)from {
uint8_t value = 0;
[[self subdataWithRange:NSMakeRange(from, sizeof(value))] getBytes:&value length:sizeof(value)];
return value;
}
#define READ_METHOD(endian, size) \
- (uint ## size ## _t)qn_read ## endian ## EndianInt ## size:(NSInteger)from { \
uint ## size ## _t value = 0; \
[[self subdataWithRange:NSMakeRange(from, sizeof(value))] getBytes:&value length:sizeof(value)]; \
value = CFSwapInt ## size ## HostTo ## endian(value); \
return value; \
}
READ_METHOD(Little, 16)
READ_METHOD(Little, 32)
READ_METHOD(Little, 64)
READ_METHOD(Big, 16)
READ_METHOD(Big, 32)
READ_METHOD(Big, 64)
#undef READ_METHOD
- (int8_t)qn_readSInt8:(NSInteger)from {
int8_t value = 0;
[[self subdataWithRange:NSMakeRange(from, 8)] getBytes:&value length:sizeof(value)];
return value;
}
#define READ_METHOD(endian, size) \
- (int ## size ## _t)qn_read ## endian ## EndianSInt ## size:(NSInteger)from { \
int ## size ## _t value = 0; \
[[self subdataWithRange:NSMakeRange(from, sizeof(value))] getBytes:&value length:sizeof(value)]; \
value = CFSwapInt ## size ## HostTo ## endian(value); \
return value; \
}
READ_METHOD(Little, 16)
READ_METHOD(Little, 32)
READ_METHOD(Little, 64)
READ_METHOD(Big, 16)
READ_METHOD(Big, 32)
READ_METHOD(Big, 64)
#undef READ_METHOD
- (float)qn_readFloat:(NSInteger)from {
float value = 0;
[[self subdataWithRange:NSMakeRange(from, sizeof(value))] getBytes:&value length:sizeof(value)];
return value;
}
- (double)qn_readDouble:(NSInteger)from {
double value = 0;
[[self subdataWithRange:NSMakeRange(from, sizeof(value))] getBytes:&value length:sizeof(value)];
return value;
}
- (float)qn_readSwappedFloat:(NSInteger)from {
CFSwappedFloat32 value;
[[self subdataWithRange:NSMakeRange(from, sizeof(value))] getBytes:&value length:sizeof(value)];
return CFConvertFloatSwappedToHost(value);
}
- (double)qn_readSwappedDouble:(NSInteger)from {
CFSwappedFloat64 value;
[[self subdataWithRange:NSMakeRange(from, sizeof(value))] getBytes:&value length:sizeof(value)];
return CFConvertDoubleSwappedToHost(value);
}
- (NSString *)qn_readString:(NSRange)range usingEncoding:(NSStringEncoding)encoding {
NSData *value = [self subdataWithRange:range];
return [[NSString alloc] initWithData:value encoding:encoding];
}
@end
@implementation NSMutableData (QNWriter)
- (void)qn_appendInt8:(uint8_t)value {
[self appendBytes:&value length:sizeof(value)];
}
#define APPEND_METHOD(endian, size) \
- (void)qn_append ## endian ## EndianInt ## size:(uint ## size ## _t)value { \
value = CFSwapInt ## size ## HostTo ## endian(value); \
[self appendBytes:&value length:sizeof(value)]; \
}
APPEND_METHOD(Little, 16)
APPEND_METHOD(Little, 32)
APPEND_METHOD(Little, 64)
APPEND_METHOD(Big, 16)
APPEND_METHOD(Big, 32)
APPEND_METHOD(Big, 64)
#undef APPEND_METHOD
- (void)qn_appendSInt8:(int8_t)value {
[self qn_appendInt8:*(int8_t *)&value];
}
#define APPEND_METHOD(endian, size) \
- (void)qn_append ## endian ## EndianSInt ## size:(int ## size ## _t)value { \
[self qn_append ## endian ## EndianInt ## size:*(uint ## size ## _t *)&value]; \
}
APPEND_METHOD(Little, 16)
APPEND_METHOD(Little, 32)
APPEND_METHOD(Little, 64)
APPEND_METHOD(Big, 16)
APPEND_METHOD(Big, 32)
APPEND_METHOD(Big, 64)
#undef APPEND_METHOD
- (void)qn_appendFloat:(float)value {
[self appendBytes:&value length:sizeof(value)];
}
- (void)qn_appendDouble:(double)value {
[self appendBytes:&value length:sizeof(value)];
}
- (void)qn_appendSwappedFloat:(float)value {
CFSwappedFloat32 v = CFConvertFloatHostToSwapped(value);
[self appendBytes:&v length:sizeof(value)];
}
- (void)qn_appendSwappedDouble:(double)value {
CFSwappedFloat64 v = CFConvertDoubleHostToSwapped(value);
[self appendBytes:&v length:sizeof(value)];
}
- (void)qn_appendString:(NSString *)value usingEncoding:(NSStringEncoding)encoding {
[self appendData:[value dataUsingEncoding:encoding]];
}
@end

File diff suppressed because it is too large Load Diff

5517
Pods/HappyDNS/HappyDNS/Util/QNAsyncUdpSocket.m generated Executable file

File diff suppressed because it is too large Load Diff

22
Pods/HappyDNS/HappyDNS/Util/QNDes.h generated Normal file
View File

@@ -0,0 +1,22 @@
//
// QNDes.h
// HappyDNS
//
// Created by bailong on 15/8/1.
// Copyright (c) 2015年 Qiniu Cloud Storage. All rights reserved.
//
#import <Foundation/Foundation.h>
extern const int kQN_ENCRYPT_FAILED;
extern const int kQN_DECRYPT_FAILED;
@interface QNDes : NSObject
- (NSData *)encrypt:(NSData *)input;
- (NSData *)decrpyt:(NSData *)input;
- (instancetype)init:(NSData *)key;
@end

92
Pods/HappyDNS/HappyDNS/Util/QNDes.m generated Normal file
View File

@@ -0,0 +1,92 @@
//
// QNDes.m
// HappyDNS
//
// Created by bailong on 15/8/1.
// Copyright (c) 2015 Qiniu Cloud Storage. All rights reserved.
//
#import <CommonCrypto/CommonCryptor.h>
#import "QNDes.h"
@interface QNDes ()
@property (nonatomic, strong) NSData *key;
@end
@implementation QNDes
- (NSData *)encrypt:(NSData *)data {
const void *input = data.bytes;
size_t inputSize = data.length;
size_t bufferSize = (inputSize + kCCBlockSize3DES) & ~(kCCBlockSize3DES - 1);
uint8_t *buffer = malloc(bufferSize * sizeof(uint8_t));
memset((void *)buffer, 0x0, bufferSize);
size_t movedBytes = 0;
const void *vkey = _key.bytes;
CCCryptorStatus ccStatus = CCCrypt(kCCEncrypt,
kCCAlgorithmDES,
kCCOptionECBMode | kCCOptionPKCS7Padding,
vkey,
kCCKeySizeDES,
NULL,
input,
inputSize,
(void *)buffer,
bufferSize,
&movedBytes);
if (ccStatus != kCCSuccess) {
NSLog(@"error code %d", ccStatus);
free(buffer);
return nil;
}
NSData *encrypted = [NSData dataWithBytes:(const void *)buffer length:(NSUInteger)movedBytes];
free(buffer);
return encrypted;
}
- (NSData *)decrpyt:(NSData *)raw {
const void *input = raw.bytes;
size_t inputSize = raw.length;
size_t bufferSize = 1024;
uint8_t *buffer = malloc(bufferSize * sizeof(uint8_t));
memset((void *)buffer, 0x0, bufferSize);
size_t movedBytes = 0;
const void *vkey = _key.bytes;
CCCryptorStatus ccStatus = CCCrypt(kCCDecrypt,
kCCAlgorithmDES,
kCCOptionECBMode | kCCOptionPKCS7Padding,
vkey,
kCCKeySizeDES,
NULL,
input,
inputSize,
(void *)buffer,
bufferSize,
&movedBytes);
if (ccStatus != kCCSuccess) {
NSLog(@"error code %d", ccStatus);
free(buffer);
return nil;
}
NSData *decrypted = [NSData dataWithBytes:(const void *)buffer length:(NSUInteger)movedBytes];
free(buffer);
return decrypted;
}
- (instancetype)init:(NSData *)key {
if (self = [super init]) {
_key = key;
}
return self;
}
@end

View File

@@ -0,0 +1,40 @@
//
// QNGetAddrInfo.h
// HappyDNS
//
// Created by bailong on 16/7/19.
// Copyright © 2016年 Qiniu Cloud Storage. All rights reserved.
//
#ifndef QNGetAddrInfo_h
#define QNGetAddrInfo_h
#ifdef __cplusplus
extern "C" {
#endif
typedef struct qn_ips_ret {
char *ips[1];
} qn_ips_ret;
typedef qn_ips_ret *(*qn_dns_callback)(const char *host);
typedef void (*qn_ip_report_callback)(const char *ip, int code, int time_ms);
extern void qn_free_ips_ret(qn_ips_ret *ip_list);
extern int qn_getaddrinfo(const char *hostname, const char *servname, const struct addrinfo *hints, struct addrinfo **res);
extern void qn_freeaddrinfo(struct addrinfo *ai);
extern void qn_set_dns_callback(qn_dns_callback cb);
extern void qn_set_ip_report_callback(qn_ip_report_callback cb);
extern void qn_ip_report(const struct addrinfo *info, int code, int time_ms);
#ifdef __cplusplus
};
#endif
#endif /* QNGetAddrInfo_h */

View File

@@ -0,0 +1,147 @@
//
// QNGetAddrInfo.c
// HappyDNS
//
// Created by bailong on 16/7/19.
// Copyright © 2016 Qiniu Cloud Storage. All rights reserved.
//
#include <arpa/inet.h>
#include <netdb.h>
#include <stdlib.h>
#include <string.h>
#include "QNGetAddrInfo.h"
//fast judge domain or ip, not verify ip right.
static int isIp(const char* domain) {
size_t l = strlen(domain);
if (l > 15 || l < 7) {
return 0;
}
for (const char* p = domain; p < domain + l; p++) {
if ((*p < '0' || *p > '9') && *p != '.') {
return 0;
}
}
return 1;
}
static struct addrinfo* addrinfo_clone(struct addrinfo* addr) {
struct addrinfo* ai;
ai = (struct addrinfo*)calloc(sizeof(struct addrinfo) + addr->ai_addrlen, 1);
if (ai) {
memcpy(ai, addr, sizeof(struct addrinfo));
ai->ai_addr = (struct sockaddr*)(ai + 1);
memcpy(ai->ai_addr, addr->ai_addr, addr->ai_addrlen);
if (addr->ai_canonname) {
ai->ai_canonname = strdup(addr->ai_canonname);
}
ai->ai_next = NULL;
}
return ai;
}
static void append_addrinfo(struct addrinfo** head, struct addrinfo* addr) {
if (*head == NULL) {
*head = addr;
return;
}
struct addrinfo* ai = *head;
while (ai->ai_next != NULL) {
ai = ai->ai_next;
}
ai->ai_next = addr;
}
void qn_free_ips_ret(qn_ips_ret* ip_list) {
if (ip_list == NULL) {
return;
}
char** p = ip_list->ips;
while (*p != NULL) {
free(*p);
p++;
}
free(ip_list);
}
static qn_dns_callback dns_callback = NULL;
int qn_getaddrinfo(const char* hostname, const char* servname, const struct addrinfo* hints, struct addrinfo** res) {
if (dns_callback == NULL || hostname == NULL || isIp(hostname)) {
return getaddrinfo(hostname, servname, hints, res);
}
qn_ips_ret* ret = dns_callback(hostname);
if (ret == NULL) {
return EAI_NODATA;
}
if (ret->ips[0] == NULL) {
qn_free_ips_ret(ret);
return EAI_NODATA;
}
int i;
struct addrinfo* ai = NULL;
struct addrinfo* store = NULL;
int r = 0;
for (i = 0; ret->ips[i] != NULL; i++) {
r = getaddrinfo(ret->ips[i], servname, hints, &ai);
if (r != 0) {
break;
}
struct addrinfo* temp = ai;
ai = addrinfo_clone(ai);
append_addrinfo(&store, ai);
freeaddrinfo(temp);
ai = NULL;
}
qn_free_ips_ret(ret);
if (r != 0) {
qn_freeaddrinfo(store);
return r;
}
*res = store;
return 0;
}
void qn_freeaddrinfo(struct addrinfo* ai) {
if (ai == NULL) {
return;
}
struct addrinfo* next;
do {
next = ai->ai_next;
if (ai->ai_canonname)
free(ai->ai_canonname);
/* no need to free(ai->ai_addr) */
free(ai);
ai = next;
} while (ai);
}
void qn_set_dns_callback(qn_dns_callback cb) {
dns_callback = cb;
}
static qn_ip_report_callback ip_report_cb = NULL;
void qn_set_ip_report_callback(qn_ip_report_callback cb) {
ip_report_cb = cb;
}
void qn_ip_report(const struct addrinfo* info, int code, int time_ms) {
if (ip_report_cb == NULL || info == NULL) {
return;
}
char ip_str_buf[32] = {0};
const char* c_ip;
if (info->ai_family == AF_INET6) {
c_ip = inet_ntop(info->ai_family, &((struct sockaddr_in6*)(info->ai_addr))->sin6_addr, ip_str_buf, sizeof(ip_str_buf));
} else {
c_ip = inet_ntop(info->ai_family, &((struct sockaddr_in*)(info->ai_addr))->sin_addr, ip_str_buf, sizeof(ip_str_buf));
}
if (c_ip == NULL) {
c_ip = "";
}
ip_report_cb(c_ip, code, time_ms);
}

21
Pods/HappyDNS/HappyDNS/Util/QNHex.h generated Normal file
View File

@@ -0,0 +1,21 @@
//
// QNHex.h
// HappyDNS
//
// Created by bailong on 15/7/31.
// Copyright (c) 2015年 Qiniu Cloud Storage. All rights reserved.
//
#import <Foundation/Foundation.h>
char *qn_encodeHexData(char *output_buf, const char *data, int data_size, BOOL up);
@interface QNHex : NSObject
+ (NSString *)encodeHexData:(NSData *)data;
+ (NSString *)encodeHexString:(NSString *)str;
+ (NSData *)decodeHexString:(NSString *)hex;
+ (NSString *)decodeHexToString:(NSString *)hex;
@end

108
Pods/HappyDNS/HappyDNS/Util/QNHex.m generated Normal file
View File

@@ -0,0 +1,108 @@
//
// QNHex.m
// HappyDNS
//
// Created by bailong on 15/7/31.
// Copyright (c) 2015 Qiniu Cloud Storage. All rights reserved.
//
#import "QNHex.h"
static char DIGITS_LOWER[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
static char DIGITS_UPPER[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
static int hexDigit(char c) {
int result = -1;
if ('0' <= c && c <= '9') {
result = c - '0';
} else if ('a' <= c && c <= 'f') {
result = 10 + (c - 'a');
} else if ('A' <= c && c <= 'F') {
result = 10 + (c - 'A');
}
return result;
}
static char *decodeHex(const char *data, int size) {
if ((size & 0x01) != 0) {
return NULL;
}
char *output = malloc(size / 2);
int outLimit = 0;
for (int i = 0, j = 0; j < size; i++) {
int f = hexDigit(data[j]);
if (f < 0) {
outLimit = 1;
break;
}
j++;
int f2 = hexDigit(data[j]);
if (f2 < 0) {
outLimit = 1;
break;
}
f = (f << 4) | f2;
j++;
output[i] = (char)(f & 0xff);
}
if (outLimit) {
free(output);
return NULL;
}
return output;
}
static char *encodeHexInternal(char *output_buf, const char *data, int size, char hexTable[]) {
for (int i = 0, j = 0; i < size; i++) {
output_buf[j++] = hexTable[((0XF0 & data[i]) >> 4) & 0X0F];
output_buf[j++] = hexTable[((0X0F & data[i])) & 0X0F];
}
return output_buf;
}
static char *encodeHex(const char *data, int size, char hexTable[]) {
char *output = malloc(size * 2);
return encodeHexInternal(output, data, size, hexTable);
}
char *qn_encodeHexData(char *buff, const char *data, int data_size, BOOL up) {
char *hexTable = DIGITS_UPPER;
if (!up) {
hexTable = DIGITS_LOWER;
}
return encodeHexInternal(buff, data, data_size, hexTable);
}
@implementation QNHex
+ (NSString *)encodeHexData:(NSData *)data {
char *e = encodeHex(data.bytes, (int)data.length, DIGITS_UPPER);
NSString *str = [[NSString alloc] initWithBytes:e length:data.length * 2 encoding:NSASCIIStringEncoding];
free(e);
return str;
}
+ (NSString *)encodeHexString:(NSString *)str {
return [QNHex encodeHexData:[str dataUsingEncoding:NSUTF8StringEncoding]];
}
+ (NSData *)decodeHexString:(NSString *)hex {
char *d = decodeHex(hex.UTF8String, (int)hex.length);
if (d == NULL) {
return nil;
}
NSData *data = [NSData dataWithBytes:d length:hex.length / 2];
free(d);
return data;
}
+ (NSString *)decodeHexToString:(NSString *)hex {
NSData *data = [QNHex decodeHexString:hex];
if (data == nil) {
return nil;
}
return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
}
@end

31
Pods/HappyDNS/HappyDNS/Util/QNIP.h generated Normal file
View File

@@ -0,0 +1,31 @@
//
// QNIPV6.h
// HappyDNS
//
// Created by bailong on 16/5/25.
// Copyright © 2016年 Qiniu Cloud Storage. All rights reserved.
//
#import <Foundation/Foundation.h>
extern int qn_localIp(char *buf, int buf_size);
extern void qn_nat64(char *buf, int buf_size, uint32_t ipv4_addr);
@interface QNIP : NSObject
+ (BOOL)isV6;
+ (NSString *)adaptiveIp:(NSString *)ipv4;
+ (NSString *)local;
// ipv6 in url like http://[::xxx]/
+ (NSString *)ipHost:(NSString *)ip;
+ (NSString *)nat64:(NSString *)ip;
+ (BOOL)isIpV6FullySupported;
+ (BOOL)mayBeIpV4:(NSString *)domain;
@end

240
Pods/HappyDNS/HappyDNS/Util/QNIP.m generated Normal file
View File

@@ -0,0 +1,240 @@
//
// QNIPV6.m
// HappyDNS
//
// Created by bailong on 16/5/25.
// Copyright © 2016 Qiniu Cloud Storage. All rights reserved.
//
#import <arpa/inet.h>
#import <netdb.h>
#import <netinet/in.h>
#import <unistd.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#import "QNHex.h"
#import "QNIP.h"
#if __IPHONE_OS_VERSION_MIN_REQUIRED
#import <MobileCoreServices/MobileCoreServices.h>
#import <UIKit/UIKit.h>
#endif
#ifndef kQNDnsDefaultIPv4IP
#define kQNDnsDefaultIPv4IP "114.114.114.114"
#endif
#ifndef kQNDnsDefaultIPv6IP
#define kQNDnsDefaultIPv6IP "2400:3200::1"
#endif
void qn_nat64(char *buf, int buf_size, uint32_t ipv4addr) {
bzero(buf, buf_size);
//nat 4 to ipv6
const char *p = (const char *)&ipv4addr;
const char prefix[] = "64:ff9b::";
memcpy(buf, prefix, sizeof(prefix));
char *phex = buf + sizeof(prefix) - 1;
qn_encodeHexData(phex, p, 2, false);
if (*phex == '0') {
memmove(phex, phex + 1, 3);
phex += 3;
} else {
phex += 4;
}
*phex = ':';
phex++;
qn_encodeHexData(phex, p + 2, 2, false);
if (*phex == '0') {
memmove(phex, phex + 1, 3);
phex[3] = 0;
}
}
int qn_local_ip_internal(char *buf, int buf_size, const char *t_ip) {
struct addrinfo hints = {0}, *ai;
int err = 0;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM;
int ret = getaddrinfo(t_ip, "53", &hints, &ai);
if (ret != 0) {
err = errno;
return err;
}
int family = ai->ai_family;
int sock = socket(family, ai->ai_socktype, 0);
if (sock < 0) {
err = errno;
freeaddrinfo(ai);
return err;
}
//fix getaddrinfo bug in ipv4 to ipv6
if (ai->ai_family == AF_INET6) {
((struct sockaddr_in6 *)ai->ai_addr)->sin6_port = htons(53);
}
err = connect(sock, ai->ai_addr, ai->ai_addrlen);
freeaddrinfo(ai);
if (err < 0) {
close(sock);
err = errno;
return err;
}
uint32_t localAddress[16] = {0};
socklen_t addressLength = sizeof(localAddress);
err = getsockname(sock, (struct sockaddr *)&localAddress, &addressLength);
close(sock);
if (err != 0) {
return err;
}
void *addr;
if (family == AF_INET6) {
addr = &((struct sockaddr_in6 *)&localAddress)->sin6_addr;
} else {
addr = &((struct sockaddr_in *)&localAddress)->sin_addr;
}
const char *ip = inet_ntop(family, addr, buf, buf_size);
if (ip == nil) {
return -1;
}
return 0;
}
int qn_localIp(char *buf, int buf_size) {
int ret = qn_local_ip_internal(buf, buf_size, kQNDnsDefaultIPv4IP);
if (ret != 0) {
#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
if (![QNIP isIpV6FullySupported]) {
ret = qn_local_ip_internal(buf, buf_size, kQNDnsDefaultIPv6IP);
}
#endif
}
return ret;
}
@implementation QNIP
+ (BOOL)isV6 {
struct addrinfo hints = {0}, *ai;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_NUMERICHOST;
int ret = getaddrinfo(kQNDnsDefaultIPv4IP, "http", &hints, &ai);
if (ret != 0) {
return NO;
}
int family = ai->ai_family;
freeaddrinfo(ai);
BOOL result = family == AF_INET6;
#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
if (![QNIP isIpV6FullySupported] && !ret) {
char buf[64] = {0};
ret = qn_local_ip_internal(buf, sizeof(buf), kQNDnsDefaultIPv6IP);
if (strchr(buf, ':') != NULL) {
result = YES;
}
}
#endif
return result;
}
+ (NSString *)adaptiveIp:(NSString *)ipv4 {
struct addrinfo hints = {0}, *ai;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
int ret = getaddrinfo(ipv4.UTF8String, "http", &hints, &ai);
if (ret != 0) {
return nil;
}
int family = ai->ai_family;
void *addr;
if (family == AF_INET6) {
addr = &((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr;
} else {
addr = &((struct sockaddr_in *)ai->ai_addr)->sin_addr;
}
char buf[64] = {0};
const char *ip = inet_ntop(family, addr, buf, sizeof(buf));
#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
if (![QNIP isIpV6FullySupported] && family == AF_INET) {
char buf2[64] = {0};
ret = qn_local_ip_internal(buf2, sizeof(buf2), kQNDnsDefaultIPv6IP);
if (strchr(buf2, ':') != NULL) {
bzero(buf, sizeof(buf));
qn_nat64(buf, sizeof(buf), *((uint32_t *)addr));
}
}
#endif
freeaddrinfo(ai);
return [NSString stringWithUTF8String:ip];
}
+ (NSString *)local {
char buf[64] = {0};
int err = qn_localIp(buf, sizeof(buf));
if (err != 0) {
return nil;
}
return [NSString stringWithUTF8String:buf];
}
+ (NSString *)ipHost:(NSString *)ip {
NSRange range = [ip rangeOfString:@":"];
if (range.location != NSNotFound) {
return [NSString stringWithFormat:@"[%@]", ip];
}
return ip;
}
+ (NSString *)nat64:(NSString *)ip {
struct in_addr s = {0};
inet_pton(AF_INET, ip.UTF8String, (void *)&s);
char buf[64] = {0};
qn_nat64(buf, sizeof(buf), (uint32_t)s.s_addr);
return [NSString stringWithUTF8String:buf];
}
+ (BOOL)isIpV6FullySupported {
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED)
float sysVersion = [[[UIDevice currentDevice] systemVersion] floatValue];
if (sysVersion < 9.0) {
return NO;
}
#else
NSOperatingSystemVersion sysVersion = [[NSProcessInfo processInfo] operatingSystemVersion];
if (sysVersion.majorVersion < 10) {
return NO;
} else if (sysVersion.majorVersion == 10) {
return sysVersion.minorVersion >= 11;
}
#endif
return YES;
}
+ (BOOL)mayBeIpV4:(NSString *)domain {
NSUInteger l = domain.length;
if (l > 15 || l < 7) {
return NO;
}
const char *str = domain.UTF8String;
if (str == nil) {
return NO;
}
for (const char *p = str; p < str + l; p++) {
if ((*p < '0' || *p > '9') && *p != '.') {
return NO;
}
}
return YES;
}
@end

15
Pods/HappyDNS/HappyDNS/Util/QNMD5.h generated Normal file
View File

@@ -0,0 +1,15 @@
//
// QNMD5.h
// HappyDNS_Mac
//
// Created by 何昊宇 on 2018/4/25.
// Copyright © 2018年 Qiniu Cloud Storage. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface QNMD5 : NSObject
+(NSString *)MD5:(NSString *)string;
@end

28
Pods/HappyDNS/HappyDNS/Util/QNMD5.m generated Normal file
View File

@@ -0,0 +1,28 @@
//
// QNMD5.m
// HappyDNS_Mac
//
// Created by on 2018/4/25.
// Copyright © 2018 Qiniu Cloud Storage. All rights reserved.
//
#import "QNMD5.h"
#import <CommonCrypto/CommonDigest.h>
@implementation QNMD5
+ (NSString *)MD5:(NSString *)string{
const char* input = [string UTF8String];
unsigned char result[CC_MD5_DIGEST_LENGTH];
CC_MD5(input, (CC_LONG)strlen(input), result);
NSMutableString *digest = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH * 2];
for (NSInteger i = 0; i < CC_MD5_DIGEST_LENGTH; i++) {
[digest appendFormat:@"%02x", result[i]];
}
return digest;
}
@end

22
Pods/HappyDNS/LICENSE generated Normal file
View File

@@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2014-2016 Qiniu, Ltd.<sdk@qiniu.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

122
Pods/HappyDNS/README.md generated Normal file
View File

@@ -0,0 +1,122 @@
# Happy DNS for Objective-C
[![@qiniu on weibo](http://img.shields.io/badge/weibo-%40qiniutek-blue.svg)](http://weibo.com/qiniutek)
[![LICENSE](https://img.shields.io/github/license/qiniu/happy-dns-objc.svg)](https://github.com/qiniu/happy-dns-objc/blob/master/LICENSE)
[![Build Status](https://travis-ci.org/qiniu/happy-dns-objc.svg?branch=master)](https://travis-ci.org/qiniu/happy-dns-objc)
[![GitHub release](https://img.shields.io/github/v/tag/qiniu/happy-dns-objc.svg?label=release)](https://github.com/qiniu/happy-dns-objc/releases)
[![codecov](https://codecov.io/gh/qiniu/happy-dns-objc/branch/master/graph/badge.svg)](https://codecov.io/gh/qiniu/happy-dns-objc)
![Platform](http://img.shields.io/cocoapods/p/HappyDNS.svg)
## 用途
调用系统底层Dns解析库可以使用 114 等第三方 dns 解析,可以使用 Doh 协议的 Dns 解析方案,也可以集成 dnspod 等 httpdns。另外也有丰富的 hosts 域名配置。
## 安装
通过 CocoaPods
```ruby
pod "HappyDNS"
```
通过 Swift Package Manager (Xcode 11+)
```
App 对接:
File -> Swift Packages -> Add Package Dependency输入 HappyDNS 库链接,选择相应版本即可
库链接: https://github.com/qiniu/happy-dns-objc
库对接:
let package = Package(
dependencies: [
.package(url: "https://github.com/qiniu/happy-dns-objc", from: "1.0.4")
],
// ...
)
```
## 运行环境
## 使用方法
返回 IP 列表
```
NSMutableArray *array = [[NSMutableArray alloc] init];
[array addObject:[QNResolver systemResolver]];
[array addObject:[[QNResolver alloc] initWithAddress:@"119.29.29.29"]];
[array addObject:[QNDohResolver resolverWithServer:@"https://dns.alidns.com/dns-query"]];
QNDnsManager *dns = [[QNDnsManager alloc] init:array networkInfo:[QNNetworkInfo normal]];
NSArray <QNRecord *> *records = [dns queryRecords:@"www.qiniu.com"];
```
url 请求返回一个IP 替换URL 里的domain
```
NSMutableArray *array = [[NSMutableArray alloc] init];
[array addObject:[QNResolver systemResolver]];
[array addObject:[[QNResolver alloc] initWithAddress:@"119.29.29.29"]];
QNDnsManager *dns = [[QNDnsManager alloc] init:array networkInfo:[QNNetworkInfo normal]];
NSURL *u = [[NSURL alloc] initWithString:@"rtmp://www.qiniu.com/abc?q=1"];
NSURL *u2 = [dns queryAndReplaceWithIP:u];
```
* 兼容 getaddrinfo, 方便底层 C 代码接入
```
static QNDnsManager *dns = nil;
dns = [[QNDnsManager alloc] init:@[ [QNResolver systemResolver] ] networkInfo:nil];
[QNDnsManager setGetAddrInfoBlock:^NSArray *(NSString *host) {
return [dns query:host];
}];
struct addrinfo hints = {0};
struct addrinfo *ai = NULL;
int x = qn_getaddrinfo(host, "http", &hints, &ai);
qn_freeaddrinfo(ai); // 也可以用系统的freeaddrinfo, 代码一致,不过最好用这个
```
### 运行测试
``` bash
$ xctool -workspace HappyDNS.xcworkspace -scheme "HappyDNS_Mac" -sdk macosx -configuration Release test -test-sdk macosx
```
### 指定测试
可以在单元测试上修改,熟悉使用
``` bash
```
## 常见问题
- 如果碰到其他编译错误,请参考 CocoaPods 的 [troubleshooting](http://guides.cocoapods.org/using/troubleshooting.html)
- httpdns 在**ios8** 时不支持 nat64 模式下 IP 直接访问url原因是 NSUrlConnection 不支持。无论是用http://119.29.29.29/d 还是http://[64:ff9b::771d:1d1d]/d 都不行此时可以使用localdns方式。
- 如果软件有国外的使用情况时,建议初始化程序采取这样的方式
```Objective-C
QNDnsManager *dns;
if([QNDnsManager needHttpDns]){
NSMutableArray *array = [[NSMutableArray alloc] init];
[array addObject:[[QNResolver alloc] initWithAddress:@"119.29.29.29"]];
[array addObject:[QNResolver systemResolver]];
dns = [[QNDnsManager alloc] init:array networkInfo:[QNNetworkInfo normal]];
}else{
NSMutableArray *array = [[NSMutableArray alloc] init];
[array addObject:[QNResolver systemResolver]];
[array addObject:[[QNResolver alloc] initWithAddress:@"114.114.114.114"]];
dns = [[QNDnsManager alloc] init:array networkInfo:[QNNetworkInfo normal]];
}
```
## 代码贡献
详情参考[代码提交指南](https://github.com/qiniu/happy-dns-objc/blob/master/CONTRIBUTING.md)。
## 贡献记录
- [所有贡献者](https://github.com/qiniu/happy-dns-objc/contributors)
## 联系我们
- 如果有什么问题,可以到问答社区提问,[问答社区](http://qiniu.segmentfault.com/)
- 如果发现了bug 欢迎提交 [issue](https://github.com/qiniu/happy-dns-objc/issues)
- 如果有功能需求,欢迎提交 [issue](https://github.com/qiniu/happy-dns-objc/issues)
- 如果要提交代码,欢迎提交 pull request
- 欢迎关注我们的[微信](http://www.qiniu.com/#weixin) [微博](http://weibo.com/qiniutek),及时获取动态信息。
## 代码许可
The MIT License (MIT).详情见 [License文件](https://github.com/qiniu/happy-dns-objc/blob/master/LICENSE).

1
Pods/Headers/Private/HappyDNS/HappyDNS.h generated Symbolic link
View File

@@ -0,0 +1 @@
../../../HappyDNS/HappyDNS/HappyDNS.h

View File

@@ -0,0 +1 @@
../../../HappyDNS/HappyDNS/Util/NSData+QNRW.h

View File

@@ -0,0 +1 @@
../../../HappyDNS/HappyDNS/Util/QNAsyncUdpSocket.h

1
Pods/Headers/Private/HappyDNS/QNDes.h generated Symbolic link
View File

@@ -0,0 +1 @@
../../../HappyDNS/HappyDNS/Util/QNDes.h

View File

@@ -0,0 +1 @@
../../../HappyDNS/HappyDNS/Dns/QNDnsDefine.h

1
Pods/Headers/Private/HappyDNS/QNDnsError.h generated Symbolic link
View File

@@ -0,0 +1 @@
../../../HappyDNS/HappyDNS/Common/QNDnsError.h

View File

@@ -0,0 +1 @@
../../../HappyDNS/HappyDNS/Common/QNDnsManager.h

View File

@@ -0,0 +1 @@
../../../HappyDNS/HappyDNS/Dns/QNDnsMessage.h

View File

@@ -0,0 +1 @@
../../../HappyDNS/HappyDNS/Dns/QNDnsRequest.h

View File

@@ -0,0 +1 @@
../../../HappyDNS/HappyDNS/Dns/QNDnsResolver.h

View File

@@ -0,0 +1 @@
../../../HappyDNS/HappyDNS/Dns/QNDnsResponse.h

View File

@@ -0,0 +1 @@
../../../HappyDNS/HappyDNS/Dns/QNDnsUdpResolver.h

View File

@@ -0,0 +1 @@
../../../HappyDNS/HappyDNS/Http/QNDnspodEnterprise.h

View File

@@ -0,0 +1 @@
../../../HappyDNS/HappyDNS/Dns/QNDohResolver.h

1
Pods/Headers/Private/HappyDNS/QNDomain.h generated Symbolic link
View File

@@ -0,0 +1 @@
../../../HappyDNS/HappyDNS/Common/QNDomain.h

View File

@@ -0,0 +1 @@
../../../HappyDNS/HappyDNS/Util/QNGetAddrInfo.h

1
Pods/Headers/Private/HappyDNS/QNHex.h generated Symbolic link
View File

@@ -0,0 +1 @@
../../../HappyDNS/HappyDNS/Util/QNHex.h

View File

@@ -0,0 +1 @@
../../../HappyDNS/HappyDNS/Local/QNHijackingDetectWrapper.h

1
Pods/Headers/Private/HappyDNS/QNHosts.h generated Symbolic link
View File

@@ -0,0 +1 @@
../../../HappyDNS/HappyDNS/Local/QNHosts.h

1
Pods/Headers/Private/HappyDNS/QNIP.h generated Symbolic link
View File

@@ -0,0 +1 @@
../../../HappyDNS/HappyDNS/Util/QNIP.h

1
Pods/Headers/Private/HappyDNS/QNLruCache.h generated Symbolic link
View File

@@ -0,0 +1 @@
../../../HappyDNS/HappyDNS/Common/QNLruCache.h

1
Pods/Headers/Private/HappyDNS/QNMD5.h generated Symbolic link
View File

@@ -0,0 +1 @@
../../../HappyDNS/HappyDNS/Util/QNMD5.h

View File

@@ -0,0 +1 @@
../../../HappyDNS/HappyDNS/Common/QNNetworkInfo.h

1
Pods/Headers/Private/HappyDNS/QNRecord.h generated Symbolic link
View File

@@ -0,0 +1 @@
../../../HappyDNS/HappyDNS/Common/QNRecord.h

View File

@@ -0,0 +1 @@
../../../HappyDNS/HappyDNS/Local/QNResolvUtil.h

1
Pods/Headers/Private/HappyDNS/QNResolver.h generated Symbolic link
View File

@@ -0,0 +1 @@
../../../HappyDNS/HappyDNS/Local/QNResolver.h

View File

@@ -0,0 +1 @@
../../../HappyDNS/HappyDNS/Common/QNResolverDelegate.h

View File

@@ -0,0 +1 @@
../../../HappyDNS/HappyDNS/Local/QNTxtResolver.h

1
Pods/Headers/Private/Qiniu/NSData+QNGZip.h generated Symbolic link
View File

@@ -0,0 +1 @@
../../../Qiniu/QiniuSDK/Utils/NSData+QNGZip.h

1
Pods/Headers/Private/Qiniu/NSData+QNMD5.h generated Symbolic link
View File

@@ -0,0 +1 @@
../../../Qiniu/QiniuSDK/Utils/NSData+QNMD5.h

View File

@@ -0,0 +1 @@
../../../Qiniu/QiniuSDK/Utils/NSObject+QNSwizzle.h

View File

@@ -0,0 +1 @@
../../../Qiniu/QiniuSDK/Http/Request/HttpClient/CFNetwork/NSURLRequest+QNRequest.h

1
Pods/Headers/Private/Qiniu/QNALAssetFile.h generated Symbolic link
View File

@@ -0,0 +1 @@
../../../Qiniu/QiniuSDK/Utils/QNALAssetFile.h

1
Pods/Headers/Private/Qiniu/QNAsyncRun.h generated Symbolic link
View File

@@ -0,0 +1 @@
../../../Qiniu/QiniuSDK/Utils/QNAsyncRun.h

1
Pods/Headers/Private/Qiniu/QNAutoZone.h generated Symbolic link
View File

@@ -0,0 +1 @@
../../../Qiniu/QiniuSDK/Common/QNAutoZone.h

1
Pods/Headers/Private/Qiniu/QNBaseUpload.h generated Symbolic link
View File

@@ -0,0 +1 @@
../../../Qiniu/QiniuSDK/Storage/QNBaseUpload.h

View File

@@ -0,0 +1 @@
../../../Qiniu/QiniuSDK/Http/Request/HttpClient/CFNetwork/QNCFHttpClient.h

View File

@@ -0,0 +1 @@
../../../Qiniu/QiniuSDK/Http/Request/HttpClient/CFNetwork/QNCFHttpClientInner.h

View File

@@ -0,0 +1 @@
../../../Qiniu/QiniuSDK/Http/Request/HttpClient/CFNetwork/QNCFHttpThreadPool.h

1
Pods/Headers/Private/Qiniu/QNCache.h generated Symbolic link
View File

@@ -0,0 +1 @@
../../../Qiniu/QiniuSDK/Utils/QNCache.h

View File

@@ -0,0 +1 @@
../../../Qiniu/QiniuSDK/Storage/QNConcurrentResumeUpload.h

1
Pods/Headers/Private/Qiniu/QNConfig.h generated Symbolic link
View File

@@ -0,0 +1 @@
../../../Qiniu/QiniuSDK/Common/QNConfig.h

Some files were not shown because too many files have changed in this diff Show More