dns-cache: support cache when speed check is disabled

This commit is contained in:
Nick Peng
2020-08-23 20:12:50 +08:00
parent 98429e88f1
commit aee19be262
5 changed files with 443 additions and 183 deletions

View File

@@ -137,6 +137,8 @@ struct dns_request {
atomic_t refcnt;
struct dns_server_conn_head *conn;
uint32_t server_flags;
/* dns request list */
struct list_head list;
@@ -206,7 +208,7 @@ static struct dns_server server;
static tlog_log *dns_audit;
static int _dns_server_prefetch_request(char *domain, dns_type_t qtype);
static int _dns_server_prefetch_request(char *domain, dns_type_t qtype, uint32_t server_flags);
static int _dns_server_forward_request(unsigned char *inpacket, int inpacket_len)
{
@@ -216,17 +218,27 @@ static int _dns_server_forward_request(unsigned char *inpacket, int inpacket_len
static int _dns_server_has_bind_flag(struct dns_request *request, uint32_t flag)
{
if (request->conn == NULL) {
return -1;
}
if (request->conn->server_flags & flag) {
if (request->server_flags & flag) {
return 0;
}
return -1;
}
static int _dns_server_get_conf_ttl(int ttl)
{
if (dns_conf_rr_ttl > 0) {
return dns_conf_rr_ttl;
}
if (dns_conf_rr_ttl_max > 0 && ttl > dns_conf_rr_ttl_max) {
ttl = dns_conf_rr_ttl_max;
} else if (dns_conf_rr_ttl_min > 0 && ttl < dns_conf_rr_ttl_min) {
ttl = dns_conf_rr_ttl_min;
}
return ttl;
}
static int _dns_server_epoll_ctl(struct dns_server_conn_head *head, int op, uint32_t events)
{
struct epoll_event event;
@@ -692,10 +704,47 @@ static int _dns_setup_ipset(struct dns_request *request)
return ret;
}
static int _dns_server_request_update_cache(struct dns_request *request, dns_type_t qtype,
struct dns_cache_data *cache_data)
{
int ttl;
int speed = 0;
if (qtype == DNS_T_A) {
ttl = _dns_server_get_conf_ttl(request->ttl_v4);
speed = request->ping_ttl_v4;
} else if (qtype == DNS_T_AAAA) {
ttl = _dns_server_get_conf_ttl(request->ttl_v6);
speed = request->ping_ttl_v6;
} else {
goto errout;
}
/* if doing prefetch, update cache only */
if (request->prefetch) {
if (dns_cache_replace(request->domain, ttl, qtype, speed, cache_data) != 0) {
goto errout;
}
} else {
/* insert result to cache */
if (dns_cache_insert(request->domain, ttl, qtype, speed, cache_data) != 0) {
goto errout;
}
}
return 0;
errout:
if (cache_data) {
dns_cache_data_free(cache_data);
}
return -1;
}
static int _dns_server_request_complete_A(struct dns_request *request)
{
char *cname = NULL;
int cname_ttl = 0;
struct dns_cache_data *cache_data = NULL;
if (request->has_cname) {
cname = request->cname;
@@ -718,23 +767,32 @@ static int _dns_server_request_complete_A(struct dns_request *request)
return 0;
}
/* if doing prefetch, update cache only */
if (request->prefetch) {
dns_cache_replace(request->domain, cname, cname_ttl, request->ttl_v4, DNS_T_A, request->ipv4_addr, DNS_RR_A_LEN,
request->ping_ttl_v4);
} else {
/* insert result to cache */
dns_cache_insert(request->domain, cname, cname_ttl, request->ttl_v4, DNS_T_A, request->ipv4_addr, DNS_RR_A_LEN,
request->ping_ttl_v4);
cache_data = dns_cache_new_data_addr(request->server_flags, cname, cname_ttl, request->ipv4_addr, DNS_RR_A_LEN);
if (cache_data == NULL) {
goto errout;
}
if (_dns_server_request_update_cache(request, DNS_T_A, cache_data) != 0) {
goto errout;
}
return 0;
errout:
if (cache_data) {
dns_cache_data_free(cache_data);
cache_data = NULL;
}
return -1;
}
static int _dns_server_request_complete_AAAA(struct dns_request *request)
{
int ret = -1;
char *cname = NULL;
int cname_ttl = 0;
struct dns_cache_data *cache_data = NULL;
if (request->has_cname) {
cname = request->cname;
@@ -756,13 +814,14 @@ static int _dns_server_request_complete_AAAA(struct dns_request *request)
/* if doing prefetch, update cache only */
if (_dns_server_has_bind_flag(request, BIND_FLAG_NO_CACHE) != 0) {
if (request->prefetch) {
dns_cache_replace(request->domain, cname, cname_ttl, request->ttl_v6, DNS_T_AAAA, request->ipv6_addr,
DNS_RR_AAAA_LEN, request->ping_ttl_v6);
} else {
/* insert result to cache */
dns_cache_insert(request->domain, cname, cname_ttl, request->ttl_v6, DNS_T_AAAA, request->ipv6_addr,
DNS_RR_AAAA_LEN, request->ping_ttl_v6);
cache_data =
dns_cache_new_data_addr(request->server_flags, cname, cname_ttl, request->ipv6_addr, DNS_T_AAAA);
if (cache_data == NULL) {
goto errout;
}
if (_dns_server_request_update_cache(request, DNS_T_AAAA, cache_data) != 0) {
goto errout;
}
}
@@ -778,21 +837,25 @@ static int _dns_server_request_complete_AAAA(struct dns_request *request)
request->ping_ttl_v6 < 0) {
tlog(TLOG_DEBUG, "Force IPV4 perfered.");
if (_dns_server_has_bind_flag(request, BIND_FLAG_NO_CACHE) != 0) {
if (request->prefetch) {
dns_cache_replace(request->domain, cname, cname_ttl, request->ttl_v4, DNS_T_A, request->ipv4_addr,
DNS_RR_A_LEN, request->ping_ttl_v4);
} else {
dns_cache_insert(request->domain, cname, cname_ttl, request->ttl_v4, DNS_T_A, request->ipv4_addr,
DNS_RR_A_LEN, request->ping_ttl_v4);
cache_data =
dns_cache_new_data_addr(request->server_flags, cname, cname_ttl, request->ipv4_addr, DNS_T_A);
if (cache_data == NULL) {
goto errout;
}
if (_dns_server_request_update_cache(request, DNS_T_A, cache_data) != 0) {
goto errout;
}
}
if (request->dualstack_selection) {
if (_dns_server_reply_SOA(DNS_RC_NOERROR, request) != 0) {
return -1;
ret = -1;
goto errout;
}
return 1;
ret = 1;
goto errout;
}
}
}
@@ -800,6 +863,14 @@ static int _dns_server_request_complete_AAAA(struct dns_request *request)
request->has_ipv4 = 0;
return 0;
errout:
if (cache_data == NULL) {
dns_cache_data_free(cache_data);
cache_data = NULL;
}
return ret;
}
static int _dns_server_request_complete(struct dns_request *request)
@@ -1196,20 +1267,6 @@ static int _dns_ip_address_check_add(struct dns_request *request, unsigned char
return 0;
}
static int _dns_server_get_conf_ttl(int ttl)
{
if (dns_conf_rr_ttl > 0) {
return dns_conf_rr_ttl;
}
if (dns_conf_rr_ttl_max > 0 && ttl > dns_conf_rr_ttl_max) {
ttl = dns_conf_rr_ttl_max;
} else if (dns_conf_rr_ttl_min > 0 && ttl < dns_conf_rr_ttl_min) {
ttl = dns_conf_rr_ttl_min;
}
return ttl;
}
static int _dns_server_ip_rule_check(struct dns_request *request, unsigned char *addr, int addr_len,
dns_type_t addr_type, int result_flag)
{
@@ -1709,8 +1766,7 @@ static int _dns_server_get_answer(struct dns_request *request, struct dns_packet
return 0;
}
static int _dns_server_reply_passthrouth(struct dns_request *request, struct dns_packet *packet,
unsigned char *inpacket, int inpacket_len)
static int _dns_server_setup_ipset_packet(struct dns_request *request, struct dns_packet *packet)
{
int ttl;
char name[DNS_MAX_CNAME_LEN] = {0};
@@ -1720,44 +1776,21 @@ static int _dns_server_reply_passthrouth(struct dns_request *request, struct dns
struct dns_rrs *rrs = NULL;
struct dns_ipset_rule *ipset_rule = NULL;
struct dns_rule_flags *rule_flags = NULL;
int ret = 0;
if (atomic_inc_return(&request->notified) != 1) {
return 0;
}
if (request->result_callback) {
_dns_server_get_answer(request, packet);
_dns_result_callback(request);
}
if (request->conn == NULL) {
return 0;
}
/* When passthrough, modify the id to be the id of the client request. */
dns_server_update_reply_packet_id(request, inpacket, inpacket_len);
ret = _dns_reply_inpacket(request, inpacket, inpacket_len);
if (packet->head.rcode != DNS_RC_NOERROR && packet->head.rcode != DNS_RC_NXDOMAIN) {
return ret;
}
if (_dns_server_has_bind_flag(request, BIND_FLAG_NO_RULE_IPSET) == 0) {
return ret;
return -1;
}
/* check ipset rule */
rule_flags = request->domain_rule.rules[DOMAIN_RULE_FLAGS];
if (rule_flags) {
if (rule_flags->flags & DOMAIN_FLAG_IPSET_IGNORE) {
return ret;
return -1;
}
}
ipset_rule = request->domain_rule.rules[DOMAIN_RULE_IPSET];
if (ipset_rule == NULL) {
return ret;
return -1;
}
for (j = 1; j < DNS_RRS_END; j++) {
@@ -1778,8 +1811,8 @@ static int _dns_server_reply_passthrouth(struct dns_request *request, struct dns
/* add IPV4 to ipset */
ipset_add(ipset_rule->ipsetname, addr, DNS_RR_A_LEN, request->ttl_v4 * 2);
tlog(TLOG_DEBUG, "IPSET-MATCH-PASSTHROUTH: domain: %s, ipset: %s, IP: %d.%d.%d.%d",
request->domain, ipset_rule->ipsetname, addr[0], addr[1], addr[2], addr[3]);
tlog(TLOG_DEBUG, "IPSET-MATCH-PASSTHROUTH: domain: %s, ipset: %s, IP: %d.%d.%d.%d", request->domain,
ipset_rule->ipsetname, addr[0], addr[1], addr[2], addr[3]);
} break;
case DNS_T_AAAA: {
unsigned char addr[16];
@@ -1797,9 +1830,11 @@ static int _dns_server_reply_passthrouth(struct dns_request *request, struct dns
ipset_add(ipset_rule->ipsetname, addr, DNS_RR_AAAA_LEN, request->ttl_v6 * 2);
}
tlog(TLOG_DEBUG, "IPSET-MATCH-PASSTHROUTH: domain: %s, ipset: %s, IP: %.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x",
request->domain, ipset_rule->ipsetname, addr[0], addr[1], addr[2], addr[3], addr[4], addr[5], addr[6], addr[7], addr[8],
addr[9], addr[10], addr[11], addr[12], addr[13], addr[14], addr[15]);
tlog(TLOG_DEBUG,
"IPSET-MATCH-PASSTHROUTH: domain: %s, ipset: %s, IP: "
"%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x",
request->domain, ipset_rule->ipsetname, addr[0], addr[1], addr[2], addr[3], addr[4], addr[5],
addr[6], addr[7], addr[8], addr[9], addr[10], addr[11], addr[12], addr[13], addr[14], addr[15]);
} break;
default:
break;
@@ -1807,7 +1842,48 @@ static int _dns_server_reply_passthrouth(struct dns_request *request, struct dns
}
}
return ret;
return 0;
}
static int _dns_server_reply_passthrouth(struct dns_request *request, struct dns_packet *packet,
unsigned char *inpacket, int inpacket_len)
{
int ret = 0;
if (atomic_inc_return(&request->notified) != 1) {
return 0;
}
_dns_server_get_answer(request, packet);
if (request->result_callback) {
_dns_result_callback(request);
}
if (request->conn == NULL) {
return 0;
}
/* When passthrough, modify the id to be the id of the client request. */
dns_server_update_reply_packet_id(request, inpacket, inpacket_len);
ret = _dns_reply_inpacket(request, inpacket, inpacket_len);
if (packet->head.rcode != DNS_RC_NOERROR && packet->head.rcode != DNS_RC_NXDOMAIN) {
return ret;
}
if (_dns_server_has_bind_flag(request, BIND_FLAG_NO_CACHE) != 0 &&
(request->qtype == DNS_T_AAAA || request->qtype == DNS_T_A)) {
struct dns_cache_data *cache_packet = dns_cache_new_data_packet(request->server_flags, inpacket, inpacket_len);
if (cache_packet == NULL) {
return ret;
}
if (_dns_server_request_update_cache(request, request->qtype, cache_packet) != 0) {
tlog(TLOG_WARN, "update packet cache failed.");
}
}
return _dns_server_setup_ipset_packet(request, packet);
}
static int dns_server_resolve_callback(char *domain, dns_result_type rtype, unsigned int result_flag,
@@ -1949,7 +2025,6 @@ static void _dns_server_log_rule(const char *domain, enum domain_rule rule_type,
int rule_key_len)
{
char rule_name[DNS_MAX_CNAME_LEN];
if (rule_key_len <= 0) {
return;
}
@@ -2184,6 +2259,118 @@ static void _dns_server_process_speed_check_rule(struct dns_request *request)
request->check_order_list = check_order;
}
static int _dns_server_process_cache_addr(struct dns_request *request, struct dns_cache *dns_cache)
{
struct dns_cache_addr *cache_addr = (struct dns_cache_addr *)dns_cache_get_data(dns_cache);
if (cache_addr->head.cache_type != CACHE_TYPE_ADDR) {
goto errout;
}
/* Cache hits, returning results in the cache */
switch (request->qtype) {
case DNS_T_A:
memcpy(request->ipv4_addr, cache_addr->addr_data.ipv4_addr, DNS_RR_A_LEN);
request->ttl_v4 = dns_cache_get_ttl(dns_cache);
request->has_ipv4 = 1;
break;
case DNS_T_AAAA:
memcpy(request->ipv6_addr, cache_addr->addr_data.ipv6_addr, DNS_RR_AAAA_LEN);
request->ttl_v6 = dns_cache_get_ttl(dns_cache);
request->has_ipv6 = 1;
break;
default:
goto errout;
break;
}
if (cache_addr->addr_data.cname[0] != 0) {
safe_strncpy(request->cname, cache_addr->addr_data.cname, DNS_MAX_CNAME_LEN);
request->has_cname = 1;
request->ttl_cname = cache_addr->addr_data.cname_ttl;
}
request->rcode = DNS_RC_NOERROR;
_dns_result_callback(request);
if (request->prefetch == 0) {
_dns_reply(request);
}
return 0;
errout:
return -1;
}
static int _dns_server_process_cache_packet(struct dns_request *request, struct dns_cache *dns_cache)
{
struct dns_cache_packet *cache_packet = (struct dns_cache_packet *)dns_cache_get_data(dns_cache);
if (cache_packet->head.cache_type != CACHE_TYPE_PACKET) {
goto errout;
}
if (dns_cache->qtype != request->qtype) {
goto errout;
}
if (atomic_inc_return(&request->notified) != 1) {
return 0;
}
if (request->result_callback) {
unsigned char packet_buff[DNS_PACKSIZE];
struct dns_packet *packet = (struct dns_packet *)packet_buff;
if (dns_decode(packet, DNS_PACKSIZE, cache_packet->data, cache_packet->head.size) != 0) {
goto errout;
}
_dns_server_get_answer(request, packet);
_dns_result_callback(request);
}
if (request->conn == NULL) {
return 0;
}
/* When passthrough, modify the id to be the id of the client request. */
dns_server_update_reply_packet_id(request, cache_packet->data, cache_packet->head.size);
return _dns_reply_inpacket(request, cache_packet->data, cache_packet->head.size);
errout:
return -1;
}
static int _dns_server_process_cache_data(struct dns_request *request, struct dns_cache *dns_cache)
{
enum CACHE_TYPE cache_type = CACHE_TYPE_NONE;
int ret = -1;
cache_type = dns_cache_data_type(dns_cache->cache_data);
switch (cache_type) {
case CACHE_TYPE_ADDR:
ret = _dns_server_process_cache_addr(request, dns_cache);
if (ret != 0) {
goto out;
}
break;
case CACHE_TYPE_PACKET:
ret = _dns_server_process_cache_packet(request, dns_cache);
if (ret != 0) {
goto out;
}
break;
default:
goto out;
break;
}
return 0;
out:
return -1;
}
static int _dns_server_process_cache(struct dns_request *request)
{
struct dns_cache *dns_cache = NULL;
@@ -2201,7 +2388,11 @@ static int _dns_server_process_cache(struct dns_request *request)
if (dns_cache_A) {
tlog(TLOG_DEBUG, "No IPV6 Found, Force IPV4 perfered.");
if (dns_cache_get_ttl(dns_cache_A) == 0) {
_dns_server_prefetch_request(request->domain, request->qtype);
uint32_t server_flags = request->server_flags;
if (request->conn == NULL) {
server_flags = dns_cache_get_cache_flag(dns_cache_A->cache_data);
}
_dns_server_prefetch_request(request->domain, request->qtype, server_flags);
}
ret = _dns_server_reply_SOA(DNS_RC_NOERROR, request);
goto out;
@@ -2226,42 +2417,18 @@ static int _dns_server_process_cache(struct dns_request *request)
}
}
/* Cache hits, returning results in the cache */
switch (request->qtype) {
case DNS_T_A:
memcpy(request->ipv4_addr, dns_cache->ipv4_addr, DNS_RR_A_LEN);
request->ttl_v4 = dns_cache_get_ttl(dns_cache);
request->has_ipv4 = 1;
break;
case DNS_T_AAAA:
memcpy(request->ipv6_addr, dns_cache->ipv6_addr, DNS_RR_AAAA_LEN);
request->ttl_v6 = dns_cache_get_ttl(dns_cache);
request->has_ipv6 = 1;
break;
default:
ret = _dns_server_process_cache_data(request, dns_cache);
if (ret != 0) {
goto out;
break;
}
if (dns_cache->cname[0] != 0) {
safe_strncpy(request->cname, dns_cache->cname, DNS_MAX_CNAME_LEN);
request->has_cname = 1;
request->ttl_cname = dns_cache->cname_ttl;
}
request->rcode = DNS_RC_NOERROR;
_dns_result_callback(request);
if (request->prefetch == 0) {
_dns_reply(request);
}
ret = 0;
out_update_cache:
if (dns_cache_get_ttl(dns_cache) == 0) {
_dns_server_prefetch_request(request->domain, request->qtype);
uint32_t server_flags = request->server_flags;
if (request->conn == NULL) {
server_flags = dns_cache_get_cache_flag(dns_cache_A->cache_data);
}
_dns_server_prefetch_request(request->domain, request->qtype, server_flags);
} else {
dns_cache_update(dns_cache);
}
@@ -2282,6 +2449,7 @@ out:
static void _dns_server_request_set_client(struct dns_request *request, struct dns_server_conn_head *conn)
{
request->conn = conn;
request->server_flags = conn->server_flags;
_dns_server_conn_get(conn);
}
@@ -2551,7 +2719,7 @@ errout:
return ret;
}
static int _dns_server_prefetch_request(char *domain, dns_type_t qtype)
static int _dns_server_prefetch_request(char *domain, dns_type_t qtype, uint32_t server_flags)
{
int ret = -1;
struct dns_request *request = NULL;
@@ -2562,6 +2730,7 @@ static int _dns_server_prefetch_request(char *domain, dns_type_t qtype)
goto errout;
}
request->server_flags = server_flags;
_dns_server_request_set_enable_prefetch(request);
ret = _dns_server_do_query(request, domain, qtype);
if (ret != 0) {
@@ -2579,7 +2748,7 @@ errout:
return ret;
}
int dns_server_query(char *domain, int qtype, dns_result_callback callback, void *user_ptr)
int dns_server_query(char *domain, int qtype, uint32_t server_flags, dns_result_callback callback, void *user_ptr)
{
int ret = -1;
struct dns_request *request = NULL;
@@ -2590,6 +2759,7 @@ int dns_server_query(char *domain, int qtype, dns_result_callback callback, void
goto errout;
}
request->server_flags = server_flags;
_dns_server_request_set_callback(request, callback, user_ptr);
ret = _dns_server_do_query(request, domain, qtype);
if (ret != 0) {
@@ -2986,7 +3156,8 @@ static void _dns_server_prefetch_domain(struct dns_cache *dns_cache)
/* start prefetch domain */
tlog(TLOG_DEBUG, "prefetch by cache %s, qtype %d, ttl %d, hitnum %d", dns_cache->domain, dns_cache->qtype,
dns_cache->ttl, hitnum);
if (_dns_server_prefetch_request(dns_cache->domain, dns_cache->qtype) != 0) {
if (_dns_server_prefetch_request(dns_cache->domain, dns_cache->qtype,
dns_cache_get_cache_flag(dns_cache->cache_data)) != 0) {
tlog(TLOG_ERROR, "prefetch domain %s, qtype %d, failed.", dns_cache->domain, dns_cache->qtype);
}
}