feature: Simple add dns64 support.
This commit is contained in:
@@ -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