feature: Simple add dns64 support.
This commit is contained in:
@@ -120,6 +120,9 @@ rtt min/avg/max/mdev = 5.954/6.133/6.313/0.195 ms
|
||||
1. **支持 IPv4、IPv6 双栈**
|
||||
支持 IPv4 和 IPV 6网络,支持查询 A 和 AAAA 记录,支持双栈 IP 速度优化,并支持完全禁用 IPv6 AAAA 解析。
|
||||
|
||||
1. **支持DNS64**
|
||||
支持DNS64转换。
|
||||
|
||||
1. **高性能、占用资源少**
|
||||
多线程异步 IO 模式,cache 缓存查询结果。
|
||||
|
||||
@@ -603,6 +606,7 @@ entware|ipkg update<br />ipkg install smartdns|软件源路径:<https://bin.en
|
||||
| response-mode | 首次查询响应模式 | first-ping |模式:[first-ping\|fastest-ip\|fastest-response]<br /> [first-ping]: 最快ping响应地址模式,DNS上游最快查询时延+ping时延最短,查询等待与链接体验最佳;<br />[fastest-ip]: 最快IP地址模式,查询到的所有IP地址中ping最短的IP。需等待IP测速; <br />[fastest-response]: 最快响应的DNS结果,DNS查询等待时间最短,返回的IP地址可能不是最快。| response-mode first-ping |
|
||||
| address | 指定域名 IP 地址 | 无 | address /domain/[ip\|-\|-4\|-6\|#\|#4\|#6] <br />- 表示忽略 <br /># 表示返回 SOA <br />4 表示 IPv4 <br />6 表示 IPv6 | address /www.example.com/1.2.3.4 |
|
||||
| cname | 指定域名别名 | 无 | cname /domain/target <br />- 表示忽略 <br />指定对应域名的cname | cname /www.example.com/cdn.example.com |
|
||||
| dns64 | DNS64转换 | 无 | dns64 ip-prefix/mask <br /> ipv6前缀和掩码 | dns64 64:ff9b::/96 |
|
||||
| nameserver | 指定域名使用 server 组解析 | 无 | nameserver /domain/[group\|-], group 为组名,- 表示忽略此规则,配套 server 中的 -group 参数使用 | nameserver /www.example.com/office |
|
||||
| ipset | 域名 ipset | 无 | ipset /domain/[ipset\|-\|#[4\|6]:[ipset\|-][,#[4\|6]:[ipset\|-]]],-表示忽略 | ipset /www.example.com/#4:dns4,#6:- <br />ipset /www.example.com/dns |
|
||||
| ipset-timeout | 设置 ipset 超时功能启用 | no | [yes\|no] | ipset-timeout yes |
|
||||
|
||||
@@ -115,6 +115,9 @@ From the comparison, smartdns found the fastest IP address to visit www.baidu.co
|
||||
1. **Support IPV4, IPV6 dual stack**
|
||||
Support IPV4, IPV6 network, support query A, AAAA record, dual-stack IP selection, and filter IPV6 AAAA record.
|
||||
|
||||
1. **DNS64**
|
||||
Support DNS64 translation.
|
||||
|
||||
1. **High performance, low resource consumption**
|
||||
Multi-threaded asynchronous IO mode, cache cache query results.
|
||||
|
||||
@@ -566,6 +569,7 @@ Note: Merlin firmware is derived from ASUS firmware and can theoretically be use
|
||||
|response-mode|First query response mode|first-ping|Mode: [first-ping\|fastest-ip\|fastest-response]<br /> [first-ping]: The fastest dns + ping response mode, DNS query delay + ping delay is the shortest;<br />[fastest-ip]: The fastest IP address mode, return the fastest ip address, may take some time to test speed. <br />[fastest-response]: The fastest response DNS result mode, the DNS query waiting time is the shortest. | response-mode first-ping |
|
||||
|address|Domain IP address|None|address /domain/[ip\|-\|-4\|-6\|#\|#4\|#6], `-` for ignore, `#` for return SOA, `4` for IPV4, `6` for IPV6| address /www.example.com/1.2.3.4
|
||||
|cname|set cname to domain| None | cname /domain/target <br />- for ignore <br />set cname to domain. | cname /www.example.com/cdn.example.com |
|
||||
|dns64|dns64 translation | None | dns64 ip-prefix/mask <br /> ipv6 prefix and mask. | dns64 64:ff9b::/96 |
|
||||
|nameserver|To query domain with specific server group|None|nameserver /domain/[group\|-], `group` is the group name, `-` means ignore this rule, use the `-group` parameter in the related server|nameserver /www.example.com/office
|
||||
|ipset|Domain IPSet|None|ipset /domain/[ipset\|-\|#[4\|6]:[ipset\|-][,#[4\|6]:[ipset\|-]]], `-` for ignore|ipset /www.example.com/#4:dns4,#6:-
|
||||
|ipset-timeout|ipset timeout enable|no|[yes\|no]|ipset-timeout yes
|
||||
|
||||
@@ -105,7 +105,7 @@ force-qtype-SOA 65
|
||||
# dualstack-ip-selection-threshold [num] (0~1000)
|
||||
# dualstack-ip-allow-force-AAAA [yes|no]
|
||||
# dualstack-ip-selection [yes|no]
|
||||
# dualstack-ip-selection yes
|
||||
# dualstack-ip-selection no
|
||||
|
||||
# edns client subnet
|
||||
# edns-client-subnet [ip/subnet]
|
||||
@@ -229,6 +229,10 @@ log-level info
|
||||
# specific cname to domain
|
||||
# cname /domain/target
|
||||
|
||||
# enalbe DNS64 feature
|
||||
# dns64 [ip/subnet]
|
||||
# dns64 64:ff9b::/96
|
||||
|
||||
# enable ipset timeout by ttl feature
|
||||
# ipset-timeout [yes]
|
||||
|
||||
|
||||
@@ -61,6 +61,9 @@ static time_t dns_conf_dnsmasq_lease_file_time;
|
||||
struct dns_hosts_table dns_hosts_table;
|
||||
int dns_hosts_record_num;
|
||||
|
||||
/* DNS64 */
|
||||
struct dns_dns64 dns_conf_dns_dns64;
|
||||
|
||||
/* server ip/port */
|
||||
struct dns_bind_ip dns_conf_bind_ip[DNS_MAX_BIND_IP];
|
||||
int dns_conf_bind_ip_num = 0;
|
||||
@@ -1690,6 +1693,43 @@ static int _config_speed_check_mode(void *data, int argc, char *argv[])
|
||||
return _config_speed_check_mode_parser(&dns_conf_check_orders, mode);
|
||||
}
|
||||
|
||||
static int _config_dns64(void *data, int argc, char *argv[])
|
||||
{
|
||||
prefix_t prefix;
|
||||
char *subnet = NULL;
|
||||
const char *errmsg = NULL;
|
||||
void *p = NULL;
|
||||
|
||||
if (argc <= 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
subnet = argv[1];
|
||||
|
||||
p = prefix_pton(subnet, -1, &prefix, &errmsg);
|
||||
if (p == NULL) {
|
||||
goto errout;
|
||||
}
|
||||
|
||||
if (prefix.family != AF_INET6) {
|
||||
tlog(TLOG_ERROR, "dns64 subnet %s is not ipv6", subnet);
|
||||
goto errout;
|
||||
}
|
||||
|
||||
if (prefix.bitlen <= 0 || prefix.bitlen > 96) {
|
||||
tlog(TLOG_ERROR, "dns64 subnet %s is not valid", subnet);
|
||||
goto errout;
|
||||
}
|
||||
|
||||
memcpy(&dns_conf_dns_dns64.prefix, &prefix.add.sin6.s6_addr, sizeof(dns_conf_dns_dns64.prefix));
|
||||
dns_conf_dns_dns64.prefix_len = prefix.bitlen;
|
||||
|
||||
return 0;
|
||||
|
||||
errout:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int _config_bind_ip(int argc, char *argv[], DNS_BIND_TYPE type)
|
||||
{
|
||||
int index = dns_conf_bind_ip_num;
|
||||
@@ -3021,6 +3061,7 @@ static struct config_item _config_item[] = {
|
||||
CONF_YESNO("dualstack-ip-selection", &dns_conf_dualstack_ip_selection),
|
||||
CONF_YESNO("dualstack-ip-allow-force-AAAA", &dns_conf_dualstack_ip_allow_force_AAAA),
|
||||
CONF_INT("dualstack-ip-selection-threshold", &dns_conf_dualstack_ip_selection_threshold, 0, 1000),
|
||||
CONF_CUSTOM("dns64", _config_dns64, NULL),
|
||||
CONF_CUSTOM("log-level", _config_log_level, NULL),
|
||||
CONF_STRING("log-file", (char *)dns_conf_log_file, DNS_MAX_PATH),
|
||||
CONF_SIZE("log-size", &dns_conf_log_size, 0, 1024 * 1024 * 1024),
|
||||
|
||||
@@ -393,6 +393,13 @@ struct dns_set_rule_flags_callback_args {
|
||||
int is_clear_flag;
|
||||
};
|
||||
|
||||
struct dns_dns64 {
|
||||
unsigned char prefix[DNS_RR_AAAA_LEN];
|
||||
uint32_t prefix_len;
|
||||
};
|
||||
|
||||
extern struct dns_dns64 dns_conf_dns_dns64;
|
||||
|
||||
extern struct dns_bind_ip dns_conf_bind_ip[DNS_MAX_BIND_IP];
|
||||
extern int dns_conf_bind_ip_num;
|
||||
|
||||
|
||||
179
src/dns_server.c
179
src/dns_server.c
@@ -77,6 +77,13 @@ typedef enum {
|
||||
DNS_CONN_TYPE_TLS_CLIENT,
|
||||
} DNS_CONN_TYPE;
|
||||
|
||||
typedef enum DNS_CHILD_POST_RESULT {
|
||||
DNS_CHILD_POST_SUCCESS = 0,
|
||||
DNS_CHILD_POST_FAIL,
|
||||
DNS_CHILD_POST_SKIP,
|
||||
DNS_CHILD_POST_NO_RESPONSE,
|
||||
} DNS_CHILD_POST_RESULT;
|
||||
|
||||
struct rule_walk_args {
|
||||
void *args;
|
||||
unsigned char *key[DOMAIN_RULE_MAX];
|
||||
@@ -166,7 +173,8 @@ struct dns_request_pending_list {
|
||||
struct hlist_node node;
|
||||
};
|
||||
|
||||
typedef int (*child_request_callback)(struct dns_request *request, struct dns_request *child_request);
|
||||
typedef DNS_CHILD_POST_RESULT (*child_request_callback)(struct dns_request *request, struct dns_request *child_request,
|
||||
int is_first_resp);
|
||||
|
||||
struct dns_request {
|
||||
atomic_t refcnt;
|
||||
@@ -219,7 +227,6 @@ struct dns_request {
|
||||
int ping_time;
|
||||
int ip_ttl;
|
||||
unsigned char ip_addr[DNS_RR_AAAA_LEN];
|
||||
int ip_addr_len;
|
||||
|
||||
struct dns_soa soa;
|
||||
int has_soa;
|
||||
@@ -1587,7 +1594,7 @@ static int _dns_result_child_post(struct dns_server_post_context *context)
|
||||
{
|
||||
struct dns_request *request = context->request;
|
||||
struct dns_request *parent_request = request->parent_request;
|
||||
int ret = 0;
|
||||
DNS_CHILD_POST_RESULT child_ret = DNS_CHILD_POST_FAIL;
|
||||
|
||||
/* not a child request */
|
||||
if (parent_request == NULL) {
|
||||
@@ -1595,10 +1602,11 @@ static int _dns_result_child_post(struct dns_server_post_context *context)
|
||||
}
|
||||
|
||||
if (request->child_callback) {
|
||||
ret = request->child_callback(parent_request, request);
|
||||
int is_first_resp = context->no_release_parent;
|
||||
child_ret = request->child_callback(parent_request, request, is_first_resp);
|
||||
}
|
||||
|
||||
if (context->do_reply == 1) {
|
||||
if (context->do_reply == 1 && child_ret == DNS_CHILD_POST_SUCCESS) {
|
||||
struct dns_server_post_context parent_context;
|
||||
_dns_server_post_context_init(&parent_context, parent_request);
|
||||
parent_context.do_cache = context->do_cache;
|
||||
@@ -1612,17 +1620,21 @@ static int _dns_result_child_post(struct dns_server_post_context *context)
|
||||
parent_context.no_release_parent = context->no_release_parent;
|
||||
|
||||
_dns_request_post(&parent_context);
|
||||
ret = _dns_server_reply_all_pending_list(parent_request, &parent_context);
|
||||
_dns_server_reply_all_pending_list(parent_request, &parent_context);
|
||||
}
|
||||
|
||||
if (context->no_release_parent == 0) {
|
||||
tlog(TLOG_INFO, "query %s with cname %s done", parent_request->domain, request->domain);
|
||||
tlog(TLOG_INFO, "query %s with child %s done", parent_request->domain, request->domain);
|
||||
request->parent_request = NULL;
|
||||
parent_request->request_wait--;
|
||||
_dns_server_request_release(parent_request);
|
||||
}
|
||||
|
||||
return ret;
|
||||
if (child_ret == DNS_CHILD_POST_FAIL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _dns_request_post(struct dns_server_post_context *context)
|
||||
@@ -2787,6 +2799,12 @@ static int _dns_server_process_answer(struct dns_request *request, const char *d
|
||||
"%d, minimum: %d",
|
||||
domain, request->qtype, request->soa.mname, request->soa.rname, request->soa.serial,
|
||||
request->soa.refresh, request->soa.retry, request->soa.expire, request->soa.minimum);
|
||||
|
||||
/* if DNS64 enabled, skip check SOA. */
|
||||
if (request->qtype == DNS_T_AAAA && dns_conf_dns_dns64.prefix_len > 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
int soa_num = atomic_inc_return(&request->soa_num);
|
||||
if ((soa_num >= (dns_server_num() / 3) + 1 || soa_num > 4) && atomic_read(&request->ip_map_num) <= 0) {
|
||||
request->ip_ttl = ttl;
|
||||
@@ -3103,6 +3121,7 @@ static void _dns_server_query_end(struct dns_request *request)
|
||||
{
|
||||
int ip_num = 0;
|
||||
int request_wait = 0;
|
||||
|
||||
pthread_mutex_lock(&request->ip_map_lock);
|
||||
ip_num = atomic_read(&request->ip_map_num);
|
||||
/* if adblock ip address exist */
|
||||
@@ -3838,6 +3857,10 @@ static struct dns_request *_dns_server_new_child_request(struct dns_request *req
|
||||
child_request->prefetch_expired_domain = request->prefetch_expired_domain;
|
||||
child_request->child_callback = child_callback;
|
||||
child_request->parent_request = request;
|
||||
if (request->has_ecs) {
|
||||
memcpy(&child_request->ecs, &request->ecs, sizeof(child_request->ecs));
|
||||
child_request->has_ecs = request->has_ecs;
|
||||
}
|
||||
_dns_server_request_get(request);
|
||||
/* reference count is 1 hold by parent request */
|
||||
request->child_request = child_request;
|
||||
@@ -3919,12 +3942,13 @@ static int _dns_server_request_copy(struct dns_request *request, struct dns_requ
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _dns_server_process_cname_callback(struct dns_request *request, struct dns_request *child_request)
|
||||
static DNS_CHILD_POST_RESULT _dns_server_process_cname_callback(struct dns_request *request,
|
||||
struct dns_request *child_request, int is_first_resp)
|
||||
{
|
||||
_dns_server_request_copy(request, child_request);
|
||||
safe_strncpy(request->cname, child_request->domain, sizeof(request->cname));
|
||||
|
||||
return 0;
|
||||
return DNS_CHILD_POST_SUCCESS;
|
||||
}
|
||||
|
||||
static int _dns_server_process_cname(struct dns_request *request)
|
||||
@@ -3988,6 +4012,137 @@ errout:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static enum DNS_CHILD_POST_RESULT
|
||||
_dns_server_process_dns64_callback(struct dns_request *request, struct dns_request *child_request, int is_first_resp)
|
||||
{
|
||||
unsigned long bucket = 0;
|
||||
struct dns_ip_address *addr_map = NULL;
|
||||
struct hlist_node *tmp = NULL;
|
||||
uint32_t key = 0;
|
||||
int addr_len = 0;
|
||||
|
||||
if (request->has_ip == 1) {
|
||||
if (memcmp(request->ip_addr, dns_conf_dns_dns64.prefix, 12) != 0) {
|
||||
return DNS_CHILD_POST_SKIP;
|
||||
}
|
||||
}
|
||||
|
||||
if (child_request->qtype != DNS_T_A) {
|
||||
return DNS_CHILD_POST_FAIL;
|
||||
}
|
||||
|
||||
if (child_request->has_ip == 0) {
|
||||
if (child_request->has_soa) {
|
||||
memcpy(&request->soa, &child_request->soa, sizeof(struct dns_soa));
|
||||
request->has_soa = 1;
|
||||
return DNS_CHILD_POST_SUCCESS;
|
||||
}
|
||||
|
||||
if (request->has_soa == 0) {
|
||||
_dns_server_setup_soa(request);
|
||||
request->has_soa = 1;
|
||||
}
|
||||
return DNS_CHILD_POST_FAIL;
|
||||
}
|
||||
|
||||
memcpy(request->ip_addr, dns_conf_dns_dns64.prefix, 16);
|
||||
memcpy(request->ip_addr + 12, child_request->ip_addr, 4);
|
||||
request->ip_ttl = child_request->ip_ttl;
|
||||
request->has_ip = 1;
|
||||
request->has_soa = 0;
|
||||
|
||||
request->rcode = child_request->rcode;
|
||||
pthread_mutex_lock(&request->ip_map_lock);
|
||||
hash_for_each_safe(request->ip_map, bucket, tmp, addr_map, node)
|
||||
{
|
||||
hash_del(&addr_map->node);
|
||||
free(addr_map);
|
||||
}
|
||||
pthread_mutex_unlock(&request->ip_map_lock);
|
||||
|
||||
pthread_mutex_lock(&child_request->ip_map_lock);
|
||||
hash_for_each_safe(child_request->ip_map, bucket, tmp, addr_map, node)
|
||||
{
|
||||
struct dns_ip_address *new_addr_map = NULL;
|
||||
|
||||
if (addr_map->addr_type == DNS_T_A) {
|
||||
addr_len = DNS_RR_A_LEN;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
new_addr_map = malloc(sizeof(struct dns_ip_address));
|
||||
if (new_addr_map == NULL) {
|
||||
tlog(TLOG_ERROR, "malloc failed.\n");
|
||||
pthread_mutex_unlock(&child_request->ip_map_lock);
|
||||
return DNS_CHILD_POST_FAIL;
|
||||
}
|
||||
|
||||
new_addr_map->addr_type = DNS_T_AAAA;
|
||||
addr_len = DNS_RR_AAAA_LEN;
|
||||
memcpy(new_addr_map->ip_addr, dns_conf_dns_dns64.prefix, 16);
|
||||
memcpy(new_addr_map->ip_addr + 12, addr_map->ip_addr, 4);
|
||||
|
||||
new_addr_map->ping_time = addr_map->ping_time;
|
||||
key = jhash(new_addr_map->ip_addr, addr_len, 0);
|
||||
key = jhash(&new_addr_map->addr_type, sizeof(new_addr_map->addr_type), key);
|
||||
pthread_mutex_lock(&request->ip_map_lock);
|
||||
hash_add(request->ip_map, &new_addr_map->node, key);
|
||||
pthread_mutex_unlock(&request->ip_map_lock);
|
||||
}
|
||||
pthread_mutex_unlock(&child_request->ip_map_lock);
|
||||
|
||||
if (request->dualstack_selection == 1) {
|
||||
return DNS_CHILD_POST_NO_RESPONSE;
|
||||
}
|
||||
|
||||
return DNS_CHILD_POST_SUCCESS;
|
||||
}
|
||||
|
||||
static int _dns_server_process_dns64(struct dns_request *request)
|
||||
{
|
||||
if (request->qtype != DNS_T_AAAA) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (dns_conf_dns_dns64.prefix_len <= 0) {
|
||||
/* no dns64 prefix, no need to do dns64 */
|
||||
return 0;
|
||||
}
|
||||
|
||||
tlog(TLOG_DEBUG, "query %s with dns64", request->domain);
|
||||
|
||||
struct dns_request *child_request = _dns_server_new_child_request(request, _dns_server_process_dns64_callback);
|
||||
if (child_request == NULL) {
|
||||
tlog(TLOG_ERROR, "malloc failed.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
child_request->qtype = DNS_T_A;
|
||||
child_request->qclass = request->qclass;
|
||||
safe_strncpy(child_request->domain, request->domain, sizeof(child_request->domain));
|
||||
|
||||
request->request_wait++;
|
||||
int ret = _dns_server_do_query(child_request, 0);
|
||||
if (ret != 0) {
|
||||
request->request_wait--;
|
||||
tlog(TLOG_ERROR, "do query %s type %d failed.\n", request->domain, request->qtype);
|
||||
goto errout;
|
||||
}
|
||||
|
||||
_dns_server_request_release_complete(child_request, 0);
|
||||
return 1;
|
||||
|
||||
errout:
|
||||
|
||||
if (child_request) {
|
||||
request->child_request = NULL;
|
||||
_dns_server_request_release(child_request);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int _dns_server_qtype_soa(struct dns_request *request)
|
||||
{
|
||||
struct dns_qtype_soa_list *soa_list = NULL;
|
||||
@@ -4694,6 +4849,10 @@ static int _dns_server_do_query(struct dns_request *request, int skip_notify_eve
|
||||
/* When the dual stack ip preference is enabled, both A and AAAA records are requested. */
|
||||
_dns_server_query_dualstack(request);
|
||||
|
||||
if (_dns_server_process_dns64(request) != 0) {
|
||||
goto clean_exit;
|
||||
}
|
||||
|
||||
clean_exit:
|
||||
return 0;
|
||||
errout:
|
||||
|
||||
Reference in New Issue
Block a user