/************************************************************************* * * Copyright (C) 2018-2023 Ruilin Peng (Nick) . * * smartdns is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * smartdns is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include "dns_server.h" #include "atomic.h" #include "dns.h" #include "dns_cache.h" #include "dns_client.h" #include "dns_conf.h" #include "fast_ping.h" #include "hashtable.h" #include "list.h" #include "nftset.h" #include "tlog.h" #include "util.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DNS_MAX_EVENTS 256 #define IPV6_READY_CHECK_TIME 180 #define DNS_SERVER_TMOUT_TTL (5 * 60) #define DNS_SERVER_FAIL_TTL (60) #define DNS_SERVER_SOA_TTL (30) #define DNS_SERVER_ADDR_TTL (60) #define DNS_CONN_BUFF_SIZE 4096 #define DNS_REQUEST_MAX_TIMEOUT 950 #define DNS_PING_TIMEOUT (DNS_REQUEST_MAX_TIMEOUT) #define DNS_PING_CHECK_INTERVAL (250) #define DNS_PING_SECOND_TIMEOUT (DNS_REQUEST_MAX_TIMEOUT - DNS_PING_CHECK_INTERVAL) #define SOCKET_IP_TOS (IPTOS_LOWDELAY | IPTOS_RELIABILITY) #define SOCKET_PRIORITY (6) #define CACHE_AUTO_ENABLE_SIZE (1024 * 1024 * 128) #define EXPIRED_DOMAIN_PREFETCH_TIME (3600 * 8) #define DNS_MAX_DOMAIN_REFETCH_NUM 64 #define PREFETCH_FLAGS_NO_DUALSTACK (1 << 0) #define PREFETCH_FLAGS_EXPIRED (1 << 1) #define RECV_ERROR_AGAIN 1 #define RECV_ERROR_OK 0 #define RECV_ERROR_FAIL (-1) #define RECV_ERROR_CLOSE (-2) #define RECV_ERROR_INVALID_PACKET (-3) typedef enum { DNS_CONN_TYPE_UDP_SERVER = 0, DNS_CONN_TYPE_TCP_SERVER, DNS_CONN_TYPE_TCP_CLIENT, DNS_CONN_TYPE_TLS_SERVER, DNS_CONN_TYPE_TLS_CLIENT, DNS_CONN_TYPE_HTTPS_SERVER, DNS_CONN_TYPE_HTTPS_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]; uint32_t key_len[DOMAIN_RULE_MAX]; }; struct dns_conn_buf { char buf[DNS_CONN_BUFF_SIZE]; int buffsize; int size; }; struct dns_server_conn_head { DNS_CONN_TYPE type; int fd; struct list_head list; time_t last_request_time; atomic_t refcnt; const char *dns_group; uint32_t server_flags; struct nftset_ipset_rules *ipset_nftset_rule; }; struct dns_server_post_context { unsigned char inpacket_buff[DNS_IN_PACKSIZE]; unsigned char *inpacket; int inpacket_maxlen; int inpacket_len; unsigned char packet_buff[DNS_PACKSIZE]; unsigned int packet_maxlen; struct dns_request *request; struct dns_packet *packet; int ip_num; const unsigned char *ip_addr[MAX_IP_NUM]; dns_type_t qtype; int do_cache; int do_reply; int do_ipset; int do_log_result; int reply_ttl; int cache_ttl; int no_check_add_ip; int do_audit; int do_force_soa; int skip_notify_count; int select_all_best_ip; int no_release_parent; }; typedef enum dns_server_client_status { DNS_SERVER_CLIENT_STATUS_INIT = 0, DNS_SERVER_CLIENT_STATUS_CONNECTING, DNS_SERVER_CLIENT_STATUS_CONNECTIONLESS, DNS_SERVER_CLIENT_STATUS_CONNECTED, DNS_SERVER_CLIENT_STATUS_DISCONNECTED, } dns_server_client_status; struct dns_server_conn_udp { struct dns_server_conn_head head; socklen_t addr_len; struct sockaddr_storage addr; }; struct dns_server_conn_tcp_server { struct dns_server_conn_head head; }; struct dns_server_conn_tls_server { struct dns_server_conn_head head; SSL_CTX *ssl_ctx; }; struct dns_server_conn_tcp_client { struct dns_server_conn_head head; struct dns_conn_buf recvbuff; struct dns_conn_buf sndbuff; socklen_t addr_len; struct sockaddr_storage addr; socklen_t localaddr_len; struct sockaddr_storage localaddr; dns_server_client_status status; }; struct dns_server_conn_tls_client { struct dns_server_conn_head head; struct dns_conn_buf recvbuff; struct dns_conn_buf sndbuff; socklen_t addr_len; struct sockaddr_storage addr; socklen_t localaddr_len; struct sockaddr_storage localaddr; dns_server_client_status status; SSL *ssl; pthread_mutex_t ssl_lock; }; /* ip address lists of domain */ struct dns_ip_address { struct hlist_node node; int hitnum; unsigned long recv_tick; int ping_time; dns_type_t addr_type; char cname[DNS_MAX_CNAME_LEN]; unsigned char ip_addr[DNS_RR_AAAA_LEN]; }; struct dns_request_pending_list { pthread_mutex_t request_list_lock; unsigned short qtype; char domain[DNS_MAX_CNAME_LEN]; uint32_t server_flags; char dns_group_name[DNS_GROUP_NAME_LEN]; struct list_head request_list; struct hlist_node node; }; struct dns_request_domain_rule { struct dns_rule *rules[DOMAIN_RULE_MAX]; int is_sub_rule[DOMAIN_RULE_MAX]; }; 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; struct dns_server_conn_head *conn; uint32_t server_flags; char dns_group_name[DNS_GROUP_NAME_LEN]; /* dns request list */ struct list_head list; struct list_head pending_list; /* dns request timeout check list */ struct list_head check_list; /* dns query */ char domain[DNS_MAX_CNAME_LEN]; dns_type_t qtype; int qclass; unsigned long send_tick; unsigned short id; unsigned short rcode; unsigned short ss_family; char remote_server_fail; char skip_qtype_soa; socklen_t addr_len; union { struct sockaddr_in in; struct sockaddr_in6 in6; struct sockaddr addr; }; struct sockaddr_storage localaddr; int has_ecs; struct dns_opt_ecs ecs; dns_result_callback result_callback; void *user_ptr; int has_ping_result; int has_ping_tcp; int has_ptr; char ptr_hostname[DNS_MAX_CNAME_LEN]; int has_cname; char cname[DNS_MAX_CNAME_LEN]; int ttl_cname; int has_ip; int ping_time; int ip_ttl; unsigned char ip_addr[DNS_RR_AAAA_LEN]; struct dns_soa soa; int has_soa; int force_soa; atomic_t notified; atomic_t do_callback; atomic_t adblock; atomic_t soa_num; /* send original raw packet to server/client like proxy */ int passthrough; int request_wait; int prefetch; int prefetch_flags; int dualstack_selection; int dualstack_selection_force_soa; int dualstack_selection_query; int dualstack_selection_ping_time; int dualstack_selection_has_ip; struct dns_request *dualstack_request; int no_serve_expired; pthread_mutex_t ip_map_lock; struct dns_request *child_request; struct dns_request *parent_request; child_request_callback child_callback; atomic_t ip_map_num; DECLARE_HASHTABLE(ip_map, 4); struct dns_request_domain_rule domain_rule; int skip_domain_rule; struct dns_domain_check_orders *check_order_list; int check_order; enum response_mode_type response_mode; struct dns_request_pending_list *request_pending_list; int no_select_possible_ip; int no_cache_cname; int no_cache; int no_ipalias; int has_cname_loop; }; /* dns server data */ struct dns_server { atomic_t run; int epoll_fd; int event_fd; struct list_head conn_list; pid_t cache_save_pid; time_t cache_save_time; /* dns request list */ pthread_mutex_t request_list_lock; struct list_head request_list; DECLARE_HASHTABLE(request_pending, 4); pthread_mutex_t request_pending_lock; }; static struct dns_server server; static tlog_log *dns_audit; static int is_ipv6_ready; static int _dns_server_prefetch_request(char *domain, dns_type_t qtype, struct dns_server_query_option *server_query_option, int prefetch_flags); static int _dns_server_get_answer(struct dns_server_post_context *context); static void _dns_server_request_get(struct dns_request *request); static void _dns_server_request_release(struct dns_request *request); static void _dns_server_request_release_complete(struct dns_request *request, int do_complete); static int _dns_server_request_complete(struct dns_request *request); static int _dns_server_reply_passthrough(struct dns_server_post_context *context); static int _dns_server_do_query(struct dns_request *request, int skip_notify_event); static int _dns_request_post(struct dns_server_post_context *context); static int _dns_server_reply_all_pending_list(struct dns_request *request, struct dns_server_post_context *context); static void *_dns_server_get_dns_rule(struct dns_request *request, enum domain_rule rule); static const char *_dns_server_get_request_groupname(struct dns_request *request); static int _dns_server_tcp_socket_send(struct dns_server_conn_tcp_client *tcp_client, void *data, int data_len); static int _dns_server_cache_save(int check_lock); int dns_is_ipv6_ready(void) { return is_ipv6_ready; } static void _dns_server_wakeup_thread(void) { uint64_t u = 1; int unused __attribute__((unused)); unused = write(server.event_fd, &u, sizeof(u)); } static int _dns_server_forward_request(unsigned char *inpacket, int inpacket_len) { return -1; } static int _dns_server_has_bind_flag(struct dns_request *request, uint32_t flag) { if (request->server_flags & flag) { return 0; } return -1; } static void *_dns_server_get_bind_ipset_nftset_rule(struct dns_request *request, enum domain_rule type) { if (request->conn == NULL) { return NULL; } if (request->conn->ipset_nftset_rule == NULL) { return NULL; } switch (type) { case DOMAIN_RULE_IPSET: return request->conn->ipset_nftset_rule->ipset; case DOMAIN_RULE_IPSET_IPV4: return request->conn->ipset_nftset_rule->ipset_ip; case DOMAIN_RULE_IPSET_IPV6: return request->conn->ipset_nftset_rule->ipset_ip6; case DOMAIN_RULE_NFTSET_IP: return request->conn->ipset_nftset_rule->nftset_ip; case DOMAIN_RULE_NFTSET_IP6: return request->conn->ipset_nftset_rule->nftset_ip6; default: break; } return NULL; } static int _dns_server_get_reply_ttl(struct dns_request *request, int ttl) { int reply_ttl = ttl; if ((request->passthrough == 0 || request->passthrough == 2) && dns_conf_cachesize > 0 && request->check_order_list->orders[0].type != DOMAIN_CHECK_NONE) { reply_ttl = dns_conf_serve_expired_reply_ttl; if (reply_ttl < 2) { reply_ttl = 2; } } return reply_ttl; } static int _dns_server_get_conf_ttl(struct dns_request *request, int ttl) { int rr_ttl = dns_conf_rr_ttl; int rr_ttl_min = dns_conf_rr_ttl_min; int rr_ttl_max = dns_conf_rr_ttl_max; struct dns_ttl_rule *ttl_rule = _dns_server_get_dns_rule(request, DOMAIN_RULE_TTL); if (ttl_rule != NULL) { if (ttl_rule->ttl > 0) { rr_ttl = ttl_rule->ttl; } /* make domain rule ttl high priority */ if (ttl_rule->ttl_min > 0) { rr_ttl_min = ttl_rule->ttl_min; if (dns_conf_rr_ttl_max <= rr_ttl_min && dns_conf_rr_ttl_max > 0) { rr_ttl_max = rr_ttl_min; } } if (ttl_rule->ttl_max > 0) { rr_ttl_max = ttl_rule->ttl_max; if (dns_conf_rr_ttl_min >= rr_ttl_max && dns_conf_rr_ttl_min > 0 && ttl_rule->ttl_min <= 0) { rr_ttl_min = rr_ttl_max; } } } if (rr_ttl > 0) { return rr_ttl; } /* make rr_ttl_min first priority */ if (rr_ttl_max < rr_ttl_min && rr_ttl_max > 0) { rr_ttl_max = rr_ttl_min; } if (rr_ttl_max > 0 && ttl >= rr_ttl_max) { ttl = rr_ttl_max; } else if (rr_ttl_min > 0 && ttl <= rr_ttl_min) { ttl = 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; memset(&event, 0, sizeof(event)); event.events = events; event.data.ptr = head; if (epoll_ctl(server.epoll_fd, op, head->fd, &event) != 0) { return -1; } return 0; } static void *_dns_server_get_dns_rule(struct dns_request *request, enum domain_rule rule) { if (rule >= DOMAIN_RULE_MAX || request == NULL) { return NULL; } return request->domain_rule.rules[rule]; } static int _dns_server_is_dns_rule_extract_match(struct dns_request *request, enum domain_rule rule) { if (rule >= DOMAIN_RULE_MAX || request == NULL) { return 0; } return request->domain_rule.is_sub_rule[rule] == 0; } static int _dns_server_is_dns64_request(struct dns_request *request) { if (request->qtype != DNS_T_AAAA) { return 0; } if (request->dualstack_selection_query == 1) { return 0; } if (dns_conf_dns_dns64.prefix_len <= 0) { return 0; } return 1; } static void _dns_server_set_dualstack_selection(struct dns_request *request) { struct dns_rule_flags *rule_flag = NULL; if (request->dualstack_selection_query || is_ipv6_ready == 0) { request->dualstack_selection = 0; return; } if ((request->prefetch_flags & PREFETCH_FLAGS_NO_DUALSTACK) != 0 || (request->prefetch_flags & PREFETCH_FLAGS_EXPIRED) != 0) { request->dualstack_selection = 0; return; } rule_flag = _dns_server_get_dns_rule(request, DOMAIN_RULE_FLAGS); if (rule_flag) { if (rule_flag->flags & DOMAIN_FLAG_DUALSTACK_SELECT) { request->dualstack_selection = 1; return; } if (rule_flag->is_flag_set & DOMAIN_FLAG_DUALSTACK_SELECT) { request->dualstack_selection = 0; return; } } if (_dns_server_has_bind_flag(request, BIND_FLAG_NO_DUALSTACK_SELECTION) == 0) { request->dualstack_selection = 0; return; } request->dualstack_selection = dns_conf_dualstack_ip_selection; } static int _dns_server_is_return_soa_qtype(struct dns_request *request, dns_type_t qtype) { struct dns_rule_flags *rule_flag = NULL; unsigned int flags = 0; if (_dns_server_has_bind_flag(request, BIND_FLAG_NO_RULE_SOA) == 0) { /* when both has no rule SOA and force AAAA soa, force AAAA soa has high priority */ if (qtype == DNS_T_AAAA && _dns_server_has_bind_flag(request, BIND_FLAG_FORCE_AAAA_SOA) == 0) { return 1; } return 0; } rule_flag = _dns_server_get_dns_rule(request, DOMAIN_RULE_FLAGS); if (rule_flag) { flags = rule_flag->flags; if (flags & DOMAIN_FLAG_ADDR_SOA) { return 1; } if (flags & DOMAIN_FLAG_ADDR_IGN) { request->skip_qtype_soa = 1; return 0; } switch (qtype) { case DNS_T_A: if (flags & DOMAIN_FLAG_ADDR_IPV4_SOA) { return 1; } if (flags & DOMAIN_FLAG_ADDR_IPV4_IGN) { request->skip_qtype_soa = 1; return 0; } break; case DNS_T_AAAA: if (flags & DOMAIN_FLAG_ADDR_IPV6_SOA) { return 1; } if (flags & DOMAIN_FLAG_ADDR_IPV6_IGN) { request->skip_qtype_soa = 1; return 0; } break; default: break; } } if (qtype == DNS_T_AAAA) { if (_dns_server_has_bind_flag(request, BIND_FLAG_FORCE_AAAA_SOA) == 0 || dns_conf_force_AAAA_SOA == 1) { return 1; } } return 0; } static int _dns_server_is_return_soa(struct dns_request *request) { return _dns_server_is_return_soa_qtype(request, request->qtype); } static void _dns_server_post_context_init(struct dns_server_post_context *context, struct dns_request *request) { memset(context, 0, sizeof(*context)); context->packet = (struct dns_packet *)(context->packet_buff); context->packet_maxlen = sizeof(context->packet_buff); context->inpacket = (unsigned char *)(context->inpacket_buff); context->inpacket_maxlen = sizeof(context->inpacket_buff); context->qtype = request->qtype; context->request = request; } static void _dns_server_context_add_ip(struct dns_server_post_context *context, const unsigned char *ip_addr) { if (context->ip_num < MAX_IP_NUM) { context->ip_addr[context->ip_num] = ip_addr; } context->ip_num++; } static void _dns_server_post_context_init_from(struct dns_server_post_context *context, struct dns_request *request, struct dns_packet *packet, unsigned char *inpacket, int inpacket_len) { memset(context, 0, sizeof(*context)); context->packet = packet; context->packet_maxlen = sizeof(context->packet_buff); context->inpacket = inpacket; context->inpacket_len = inpacket_len; context->inpacket_maxlen = sizeof(context->inpacket); context->qtype = request->qtype; context->request = request; } static struct dns_ip_address *_dns_ip_address_get(struct dns_request *request, unsigned char *addr, dns_type_t addr_type) { uint32_t key = 0; struct dns_ip_address *addr_map = NULL; struct dns_ip_address *addr_tmp = NULL; int addr_len = 0; if (addr_type == DNS_T_A) { addr_len = DNS_RR_A_LEN; } else if (addr_type == DNS_T_AAAA) { addr_len = DNS_RR_AAAA_LEN; } else { return NULL; } /* store the ip address and the number of hits */ key = jhash(addr, addr_len, 0); key = jhash(&addr_type, sizeof(addr_type), key); pthread_mutex_lock(&request->ip_map_lock); hash_for_each_possible(request->ip_map, addr_tmp, node, key) { if (addr_type != addr_tmp->addr_type) { continue; } if (memcmp(addr_tmp->ip_addr, addr, addr_len) != 0) { continue; } addr_map = addr_tmp; break; } pthread_mutex_unlock(&request->ip_map_lock); return addr_map; } static void _dns_server_audit_log(struct dns_server_post_context *context) { char req_host[MAX_IP_LEN]; char req_result[1024] = {0}; char *ip_msg = req_result; char req_time[MAX_IP_LEN]; struct tlog_time tm; int i = 0; int j = 0; int rr_count = 0; struct dns_rrs *rrs = NULL; char name[DNS_MAX_CNAME_LEN] = {0}; int ttl = 0; int len = 0; int left_len = sizeof(req_result); int total_len = 0; int ip_num = 0; struct dns_request *request = context->request; int has_soa = request->has_soa; if (dns_audit == NULL || !dns_conf_audit_enable || context->do_audit == 0) { return; } if (request->conn == NULL) { return; } for (j = 1; j < DNS_RRS_OPT && context->packet; j++) { rrs = dns_get_rrs_start(context->packet, j, &rr_count); for (i = 0; i < rr_count && rrs && left_len > 0; i++, rrs = dns_get_rrs_next(context->packet, rrs)) { switch (rrs->type) { case DNS_T_A: { unsigned char ipv4_addr[4]; if (dns_get_A(rrs, name, DNS_MAX_CNAME_LEN, &ttl, ipv4_addr) != 0) { continue; } if (strncmp(name, request->domain, DNS_MAX_CNAME_LEN - 1) != 0 && strncmp(name, request->cname, DNS_MAX_CNAME_LEN - 1) != 0) { continue; } const char *fmt = "%d.%d.%d.%d"; if (ip_num > 0) { fmt = ", %d.%d.%d.%d"; } len = snprintf(ip_msg + total_len, left_len, fmt, ipv4_addr[0], ipv4_addr[1], ipv4_addr[2], ipv4_addr[3]); ip_num++; has_soa = 0; } break; case DNS_T_AAAA: { unsigned char ipv6_addr[16]; if (dns_get_AAAA(rrs, name, DNS_MAX_CNAME_LEN, &ttl, ipv6_addr) != 0) { continue; } if (strncmp(name, request->domain, DNS_MAX_CNAME_LEN - 1) != 0 && strncmp(name, request->cname, DNS_MAX_CNAME_LEN - 1) != 0) { continue; } const char *fmt = "%s"; if (ip_num > 0) { fmt = ", %s"; } req_host[0] = '\0'; inet_ntop(AF_INET6, ipv6_addr, req_host, sizeof(req_host)); len = snprintf(ip_msg + total_len, left_len, fmt, req_host); ip_num++; has_soa = 0; } break; case DNS_T_SOA: { if (ip_num == 0) { has_soa = 1; } } break; default: continue; } if (len < 0 || len >= left_len) { left_len = 0; break; } left_len -= len; total_len += len; } } if (has_soa && ip_num == 0) { if (!dns_conf_audit_log_SOA) { return; } if (request->dualstack_selection_force_soa) { snprintf(req_result, left_len, "dualstack soa"); } else { snprintf(req_result, left_len, "soa"); } } get_host_by_addr(req_host, sizeof(req_host), &request->addr); tlog_localtime(&tm); if (req_host[0] == '\0') { safe_strncpy(req_host, "API", MAX_IP_LEN); } snprintf(req_time, sizeof(req_time), "[%.4d-%.2d-%.2d %.2d:%.2d:%.2d,%.3d]", tm.year, tm.mon, tm.mday, tm.hour, tm.min, tm.sec, tm.usec / 1000); tlog_printf(dns_audit, "%s %s query %s, type %d, time %lums, speed: %.1fms, result %s\n", req_time, req_host, request->domain, request->qtype, get_tick_count() - request->send_tick, ((float)request->ping_time) / 10, req_result); } static void _dns_rrs_result_log(struct dns_server_post_context *context, struct dns_ip_address *addr_map) { struct dns_request *request = context->request; if (context->do_log_result == 0 || addr_map == NULL) { return; } if (addr_map->addr_type == DNS_T_A) { tlog(TLOG_INFO, "result: %s, id: %d, index: %d, rtt: %.1f ms, %d.%d.%d.%d", request->domain, request->id, context->ip_num, ((float)addr_map->ping_time) / 10, addr_map->ip_addr[0], addr_map->ip_addr[1], addr_map->ip_addr[2], addr_map->ip_addr[3]); } else if (addr_map->addr_type == DNS_T_AAAA) { tlog(TLOG_INFO, "result: %s, id: %d, index: %d, rtt: %.1f ms, " "%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x", request->domain, request->id, context->ip_num, ((float)addr_map->ping_time) / 10, addr_map->ip_addr[0], addr_map->ip_addr[1], addr_map->ip_addr[2], addr_map->ip_addr[3], addr_map->ip_addr[4], addr_map->ip_addr[5], addr_map->ip_addr[6], addr_map->ip_addr[7], addr_map->ip_addr[8], addr_map->ip_addr[9], addr_map->ip_addr[10], addr_map->ip_addr[11], addr_map->ip_addr[12], addr_map->ip_addr[13], addr_map->ip_addr[14], addr_map->ip_addr[15]); } } static int _dns_rrs_add_all_best_ip(struct dns_server_post_context *context) { struct dns_ip_address *addr_map = NULL; struct dns_ip_address *added_ip_addr = NULL; struct hlist_node *tmp = NULL; struct dns_request *request = context->request; unsigned long bucket = 0; char *domain = NULL; int ret = 0; int ignore_speed = 0; int maxhit = 0; if (context->select_all_best_ip == 0 || context->ip_num >= dns_conf_max_reply_ip_num) { return 0; } domain = request->domain; /* add CNAME record */ if (request->has_cname) { domain = request->cname; } /* add fasted ip address at first place of dns RR */ if (request->has_ip) { added_ip_addr = _dns_ip_address_get(request, request->ip_addr, request->qtype); _dns_rrs_result_log(context, added_ip_addr); } if (request->passthrough == 2) { ignore_speed = 1; } while (true) { pthread_mutex_lock(&request->ip_map_lock); hash_for_each_safe(request->ip_map, bucket, tmp, addr_map, node) { if (context->ip_num >= dns_conf_max_reply_ip_num) { break; } if (context->qtype != addr_map->addr_type) { continue; } if (addr_map == added_ip_addr) { continue; } if (addr_map->hitnum > maxhit) { maxhit = addr_map->hitnum; } if (addr_map->ping_time < 0 && ignore_speed == 0) { continue; } if (addr_map->hitnum < maxhit && ignore_speed == 1) { continue; } /* if ping time is larger than 5ms, check again. */ if (addr_map->ping_time - request->ping_time >= 50) { int ttl_range = request->ping_time + request->ping_time / 10 + 5; if ((ttl_range < addr_map->ping_time) && addr_map->ping_time >= 100 && ignore_speed == 0) { continue; } } _dns_server_context_add_ip(context, addr_map->ip_addr); if (addr_map->addr_type == DNS_T_A) { ret |= dns_add_A(context->packet, DNS_RRS_AN, domain, request->ip_ttl, addr_map->ip_addr); } else if (addr_map->addr_type == DNS_T_AAAA) { ret |= dns_add_AAAA(context->packet, DNS_RRS_AN, domain, request->ip_ttl, addr_map->ip_addr); } _dns_rrs_result_log(context, addr_map); } pthread_mutex_unlock(&request->ip_map_lock); if (context->ip_num <= 0 && ignore_speed == 0) { ignore_speed = 1; } else { break; } } return ret; } static void _dns_server_setup_soa(struct dns_request *request) { struct dns_soa *soa = NULL; soa = &request->soa; safe_strncpy(soa->mname, "a.gtld-servers.net", DNS_MAX_CNAME_LEN); safe_strncpy(soa->rname, "nstld.verisign-grs.com", DNS_MAX_CNAME_LEN); soa->serial = 1800; soa->refresh = 1800; soa->retry = 900; soa->expire = 604800; soa->minimum = 86400; } static int _dns_add_rrs(struct dns_server_post_context *context) { struct dns_request *request = context->request; int ret = 0; int has_soa = request->has_soa; char *domain = request->domain; if (request->has_ptr) { /* add PTR record */ ret = dns_add_PTR(context->packet, DNS_RRS_AN, request->domain, request->ip_ttl, request->ptr_hostname); } /* add CNAME record */ if (request->has_cname && context->do_force_soa == 0) { ret |= dns_add_CNAME(context->packet, DNS_RRS_AN, request->domain, request->ttl_cname, request->cname); domain = request->cname; } /* add A record */ if (request->has_ip && context->do_force_soa == 0) { _dns_server_context_add_ip(context, request->ip_addr); if (context->qtype == DNS_T_A) { ret |= dns_add_A(context->packet, DNS_RRS_AN, domain, request->ip_ttl, request->ip_addr); tlog(TLOG_DEBUG, "result: %s, rtt: %.1f ms, %d.%d.%d.%d", request->domain, ((float)request->ping_time) / 10, request->ip_addr[0], request->ip_addr[1], request->ip_addr[2], request->ip_addr[3]); } /* add AAAA record */ if (context->qtype == DNS_T_AAAA) { ret |= dns_add_AAAA(context->packet, DNS_RRS_AN, domain, request->ip_ttl, request->ip_addr); tlog(TLOG_DEBUG, "result: %s, rtt: %.1f ms, " "%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x", request->domain, ((float)request->ping_time) / 10, request->ip_addr[0], request->ip_addr[1], request->ip_addr[2], request->ip_addr[3], request->ip_addr[4], request->ip_addr[5], request->ip_addr[6], request->ip_addr[7], request->ip_addr[8], request->ip_addr[9], request->ip_addr[10], request->ip_addr[11], request->ip_addr[12], request->ip_addr[13], request->ip_addr[14], request->ip_addr[15]); } } if (context->do_force_soa == 0) { ret |= _dns_rrs_add_all_best_ip(context); } if (context->qtype == DNS_T_A || context->qtype == DNS_T_AAAA) { if (context->ip_num > 0) { has_soa = 0; } } /* add SOA record */ if (has_soa) { ret |= dns_add_SOA(context->packet, DNS_RRS_NS, domain, request->ip_ttl, &request->soa); tlog(TLOG_DEBUG, "result: %s, qtype: %d, return SOA", request->domain, context->qtype); } else if (context->do_force_soa == 1) { _dns_server_setup_soa(request); ret |= dns_add_SOA(context->packet, DNS_RRS_NS, domain, request->ip_ttl, &request->soa); } if (request->has_ecs) { ret |= dns_add_OPT_ECS(context->packet, &request->ecs); } if (request->rcode != DNS_RC_NOERROR) { tlog(TLOG_INFO, "result: %s, qtype: %d, rtcode: %d", domain, context->qtype, request->rcode); } return ret; } static int _dns_setup_dns_packet(struct dns_server_post_context *context) { struct dns_head head; struct dns_request *request = context->request; int ret = 0; memset(&head, 0, sizeof(head)); head.id = request->id; head.qr = DNS_QR_ANSWER; head.opcode = DNS_OP_QUERY; head.rd = 1; head.ra = 1; head.aa = 0; head.tc = 0; head.rcode = request->rcode; /* init a new DNS packet */ ret = dns_packet_init(context->packet, context->packet_maxlen, &head); if (ret != 0) { return -1; } /* add request domain */ ret = dns_add_domain(context->packet, request->domain, context->qtype, request->qclass); if (ret != 0) { return -1; } /* add RECORDs */ ret = _dns_add_rrs(context); if (ret != 0) { return -1; } return 0; } static int _dns_setup_dns_raw_packet(struct dns_server_post_context *context) { /* encode to binary data */ int encode_len = dns_encode(context->inpacket, context->inpacket_maxlen, context->packet); if (encode_len <= 0) { tlog(TLOG_DEBUG, "encode raw packet failed for %s", context->request->domain); return -1; } context->inpacket_len = encode_len; return 0; } static void _dns_server_conn_release(struct dns_server_conn_head *conn) { if (conn == NULL) { return; } int refcnt = atomic_dec_return(&conn->refcnt); if (refcnt) { if (refcnt < 0) { BUG("BUG: refcnt is %d, type = %d", refcnt, conn->type); } return; } if (conn->fd > 0) { close(conn->fd); conn->fd = -1; } if (conn->type == DNS_CONN_TYPE_TLS_CLIENT || conn->type == DNS_CONN_TYPE_HTTPS_CLIENT) { struct dns_server_conn_tls_client *tls_client = (struct dns_server_conn_tls_client *)conn; if (tls_client->ssl != NULL) { SSL_free(tls_client->ssl); tls_client->ssl = NULL; } pthread_mutex_destroy(&tls_client->ssl_lock); } else if (conn->type == DNS_CONN_TYPE_TLS_SERVER) { struct dns_server_conn_tls_server *tls_server = (struct dns_server_conn_tls_server *)conn; if (tls_server->ssl_ctx != NULL) { SSL_CTX_free(tls_server->ssl_ctx); tls_server->ssl_ctx = NULL; } } list_del_init(&conn->list); free(conn); } static void _dns_server_conn_get(struct dns_server_conn_head *conn) { if (conn == NULL) { return; } if (atomic_inc_return(&conn->refcnt) <= 0) { BUG("BUG: client ref is invalid."); } } static int _dns_server_reply_tcp_to_buffer(struct dns_server_conn_tcp_client *tcpclient, void *packet, int len) { if ((int)sizeof(tcpclient->sndbuff.buf) - tcpclient->sndbuff.size < len) { return -1; } memcpy(tcpclient->sndbuff.buf + tcpclient->sndbuff.size, packet, len); tcpclient->sndbuff.size += len; if (_dns_server_epoll_ctl(&tcpclient->head, EPOLL_CTL_MOD, EPOLLIN | EPOLLOUT) != 0) { tlog(TLOG_ERROR, "epoll ctl failed."); return -1; } return 0; } static int _dns_server_reply_tcp(struct dns_request *request, struct dns_server_conn_tcp_client *tcpclient, void *packet, unsigned short len) { int send_len = 0; unsigned char inpacket_data[DNS_IN_PACKSIZE]; unsigned char *inpacket = inpacket_data; if (len > sizeof(inpacket_data) - 2) { tlog(TLOG_ERROR, "packet size is invalid."); return -1; } /* TCP query format * | len (short) | dns query data | */ *((unsigned short *)(inpacket)) = htons(len); memcpy(inpacket + 2, packet, len); len += 2; send_len = _dns_server_tcp_socket_send(tcpclient, inpacket, len); if (send_len < 0) { if (errno == EAGAIN) { /* save data to buffer, and retry when EPOLLOUT is available */ return _dns_server_reply_tcp_to_buffer(tcpclient, inpacket, len); } return -1; } else if (send_len < len) { /* save remain data to buffer, and retry when EPOLLOUT is available */ return _dns_server_reply_tcp_to_buffer(tcpclient, inpacket + send_len, len - send_len); } return 0; } static int _dns_server_reply_udp(struct dns_request *request, struct dns_server_conn_udp *udpserver, unsigned char *inpacket, int inpacket_len) { int send_len = 0; struct iovec iovec[1]; struct msghdr msg; struct cmsghdr *cmsg; char msg_control[64]; if (atomic_read(&server.run) == 0 || inpacket == NULL || inpacket_len <= 0) { return -1; } iovec[0].iov_base = inpacket; iovec[0].iov_len = inpacket_len; memset(msg_control, 0, sizeof(msg_control)); msg.msg_iov = iovec; msg.msg_iovlen = 1; msg.msg_control = msg_control; msg.msg_controllen = sizeof(msg_control); msg.msg_flags = 0; msg.msg_name = &request->addr; msg.msg_namelen = request->addr_len; cmsg = CMSG_FIRSTHDR(&msg); if (request->localaddr.ss_family == AF_INET) { struct sockaddr_in *s4 = (struct sockaddr_in *)&request->localaddr; cmsg->cmsg_level = SOL_IP; cmsg->cmsg_type = IP_PKTINFO; cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); msg.msg_controllen = CMSG_SPACE(sizeof(struct in_pktinfo)); struct in_pktinfo *pktinfo = (struct in_pktinfo *)CMSG_DATA(cmsg); memset(pktinfo, 0, sizeof(*pktinfo)); pktinfo->ipi_spec_dst = s4->sin_addr; } else if (request->localaddr.ss_family == AF_INET6) { struct sockaddr_in6 *s6 = (struct sockaddr_in6 *)&request->localaddr; cmsg->cmsg_level = IPPROTO_IPV6; cmsg->cmsg_type = IPV6_PKTINFO; cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); msg.msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo)); struct in6_pktinfo *pktinfo = (struct in6_pktinfo *)CMSG_DATA(cmsg); memset(pktinfo, 0, sizeof(*pktinfo)); pktinfo->ipi6_addr = s6->sin6_addr; } else { goto use_send; } send_len = sendmsg(udpserver->head.fd, &msg, 0); if (send_len == inpacket_len) { return 0; } use_send: send_len = sendto(udpserver->head.fd, inpacket, inpacket_len, 0, &request->addr, request->addr_len); if (send_len != inpacket_len) { tlog(TLOG_DEBUG, "send failed, %s", strerror(errno)); return -1; } return 0; } static int _dns_reply_inpacket(struct dns_request *request, unsigned char *inpacket, int inpacket_len) { struct dns_server_conn_head *conn = request->conn; int ret = 0; if (conn == NULL) { tlog(TLOG_ERROR, "client is invalid, domain: %s", request->domain); return -1; } if (conn->type == DNS_CONN_TYPE_UDP_SERVER) { ret = _dns_server_reply_udp(request, (struct dns_server_conn_udp *)conn, inpacket, inpacket_len); } else if (conn->type == DNS_CONN_TYPE_TCP_CLIENT) { ret = _dns_server_reply_tcp(request, (struct dns_server_conn_tcp_client *)conn, inpacket, inpacket_len); } else if (conn->type == DNS_CONN_TYPE_TLS_CLIENT) { ret = _dns_server_reply_tcp(request, (struct dns_server_conn_tcp_client *)conn, inpacket, inpacket_len); } else { ret = -1; } return ret; } static inline int _dns_server_expired_cache_ttl(struct dns_cache *cache) { return cache->info.insert_time + cache->info.ttl + dns_conf_serve_expired_ttl - time(NULL); } static int _dns_cache_is_specify_packet(int qtype) { switch (qtype) { case DNS_T_PTR: case DNS_T_HTTPS: case DNS_T_TXT: case DNS_T_SRV: break; default: return -1; break; } return 0; } static int _dns_server_get_cache_timeout(struct dns_request *request, struct dns_cache_key *cache_key, int ttl) { int timeout = 0; if (request->rcode != DNS_RC_NOERROR) { return ttl + 1; } if (dns_conf_prefetch && _dns_cache_is_specify_packet(request->qtype) != 0) { if (dns_conf_serve_expired) { timeout = dns_conf_serve_expired_prefetch_time; if (timeout == 0) { timeout = dns_conf_serve_expired_ttl / 2; if (timeout == 0 || timeout > EXPIRED_DOMAIN_PREFETCH_TIME) { timeout = EXPIRED_DOMAIN_PREFETCH_TIME; } } if ((request->prefetch_flags & PREFETCH_FLAGS_EXPIRED) == 0) { timeout += ttl; } else if (cache_key != NULL) { struct dns_cache *old_cache = dns_cache_lookup(cache_key); if (old_cache) { time_t next_ttl = _dns_server_expired_cache_ttl(old_cache) - old_cache->info.ttl + ttl; if (next_ttl < timeout) { timeout = next_ttl; } dns_cache_release(old_cache); } } } else { timeout = ttl - 3; } } else { timeout = ttl; if (dns_conf_serve_expired) { timeout += dns_conf_serve_expired_ttl; } } if (timeout <= 0) { timeout = 1; } return timeout; } static int _dns_server_request_update_cache(struct dns_request *request, int speed, dns_type_t qtype, struct dns_cache_data *cache_data, int cache_ttl) { int ttl = 0; int ret = -1; if (qtype != DNS_T_A && qtype != DNS_T_AAAA) { goto errout; } if (cache_ttl > 0) { ttl = cache_ttl; } else { ttl = _dns_server_get_conf_ttl(request, request->ip_ttl); } tlog(TLOG_DEBUG, "cache %s qtype: %d ttl: %d\n", request->domain, qtype, ttl); /* if doing prefetch, update cache only */ struct dns_cache_key cache_key; cache_key.dns_group_name = request->dns_group_name; cache_key.domain = request->domain; cache_key.qtype = request->qtype; cache_key.query_flag = request->server_flags; if (request->prefetch) { if (dns_cache_replace(&cache_key, request->rcode, ttl, speed, _dns_server_get_cache_timeout(request, &cache_key, ttl), !(request->prefetch_flags & PREFETCH_FLAGS_EXPIRED), cache_data) != 0) { ret = 0; goto errout; } } else { /* insert result to cache */ if (dns_cache_insert(&cache_key, request->rcode, ttl, speed, _dns_server_get_cache_timeout(request, NULL, ttl), cache_data) != 0) { ret = -1; goto errout; } } return 0; errout: if (cache_data) { dns_cache_data_put(cache_data); } return ret; } static int _dns_cache_cname_packet(struct dns_server_post_context *context) { struct dns_packet *packet = context->packet; struct dns_packet *cname_packet = NULL; int ret = -1; int i = 0; int j = 0; int rr_count = 0; int ttl = 0; int speed = 0; unsigned char packet_buff[DNS_PACKSIZE]; unsigned char inpacket_buff[DNS_IN_PACKSIZE]; int inpacket_len = 0; struct dns_cache_data *cache_packet = NULL; struct dns_rrs *rrs = NULL; char name[DNS_MAX_CNAME_LEN] = {0}; cname_packet = (struct dns_packet *)packet_buff; int has_result = 0; struct dns_request *request = context->request; if (request->has_cname == 0 || request->no_cache_cname == 1 || request->no_cache == 1) { return 0; } /* init a new DNS packet */ ret = dns_packet_init(cname_packet, DNS_PACKSIZE, &packet->head); if (ret != 0) { return -1; } /* add request domain */ ret = dns_add_domain(cname_packet, request->cname, context->qtype, DNS_C_IN); if (ret != 0) { return -1; } for (j = 1; j < DNS_RRS_OPT && context->packet; j++) { rrs = dns_get_rrs_start(context->packet, j, &rr_count); for (i = 0; i < rr_count && rrs; i++, rrs = dns_get_rrs_next(context->packet, rrs)) { switch (rrs->type) { case DNS_T_A: { unsigned char ipv4_addr[4]; if (dns_get_A(rrs, name, DNS_MAX_CNAME_LEN, &ttl, ipv4_addr) != 0) { continue; } if (strncmp(request->cname, name, DNS_MAX_CNAME_LEN - 1) != 0) { continue; } ret = dns_add_A(cname_packet, DNS_RRS_AN, request->cname, ttl, ipv4_addr); if (ret != 0) { return -1; } has_result = 1; } break; case DNS_T_AAAA: { unsigned char ipv6_addr[16]; if (dns_get_AAAA(rrs, name, DNS_MAX_CNAME_LEN, &ttl, ipv6_addr) != 0) { continue; } if (strncmp(request->cname, name, DNS_MAX_CNAME_LEN - 1) != 0) { continue; } ret = dns_add_AAAA(cname_packet, DNS_RRS_AN, request->cname, ttl, ipv6_addr); if (ret != 0) { return -1; } has_result = 1; } break; case DNS_T_SOA: { struct dns_soa soa; if (dns_get_SOA(rrs, name, DNS_MAX_CNAME_LEN, &ttl, &soa) != 0) { continue; } ret = dns_add_SOA(cname_packet, DNS_RRS_AN, request->cname, ttl, &soa); if (ret != 0) { return -1; } has_result = 1; break; } default: continue; } } } if (has_result == 0) { return 0; } inpacket_len = dns_encode(inpacket_buff, DNS_IN_PACKSIZE, cname_packet); if (inpacket_len <= 0) { return -1; } if (context->qtype != DNS_T_A && context->qtype != DNS_T_AAAA) { return -1; } cache_packet = dns_cache_new_data_packet(inpacket_buff, inpacket_len); if (cache_packet == NULL) { goto errout; } ttl = _dns_server_get_conf_ttl(request, request->ip_ttl); speed = request->ping_time; tlog(TLOG_DEBUG, "Cache CNAME: %s, qtype: %d, speed: %d", request->cname, request->qtype, speed); /* if doing prefetch, update cache only */ struct dns_cache_key cache_key; cache_key.dns_group_name = request->dns_group_name; cache_key.domain = request->cname; cache_key.qtype = context->qtype; cache_key.query_flag = request->server_flags; if (request->prefetch) { if (dns_cache_replace(&cache_key, request->rcode, ttl, speed, _dns_server_get_cache_timeout(request, &cache_key, ttl), !(request->prefetch_flags & PREFETCH_FLAGS_EXPIRED), cache_packet) != 0) { ret = 0; goto errout; } } else { /* insert result to cache */ if (dns_cache_insert(&cache_key, request->rcode, ttl, speed, _dns_server_get_cache_timeout(request, NULL, ttl), cache_packet) != 0) { ret = -1; goto errout; } } return 0; errout: if (cache_packet) { dns_cache_data_put((struct dns_cache_data *)cache_packet); } return ret; } static int _dns_cache_packet(struct dns_server_post_context *context) { struct dns_request *request = context->request; int ret = -1; struct dns_cache_data *cache_packet = dns_cache_new_data_packet(context->inpacket, context->inpacket_len); if (cache_packet == NULL) { goto errout; } /* if doing prefetch, update cache only */ struct dns_cache_key cache_key; cache_key.dns_group_name = request->dns_group_name; cache_key.domain = request->domain; cache_key.qtype = context->qtype; cache_key.query_flag = request->server_flags; if (request->prefetch) { if (dns_cache_replace(&cache_key, request->rcode, request->ip_ttl, -1, _dns_server_get_cache_timeout(request, &cache_key, request->ip_ttl), !(request->prefetch_flags & PREFETCH_FLAGS_EXPIRED), cache_packet) != 0) { ret = 0; goto errout; } } else { /* insert result to cache */ if (dns_cache_insert(&cache_key, request->rcode, request->ip_ttl, -1, _dns_server_get_cache_timeout(request, NULL, request->ip_ttl), cache_packet) != 0) { ret = -1; goto errout; } } return 0; errout: if (cache_packet) { dns_cache_data_put((struct dns_cache_data *)cache_packet); } return ret; } static int _dns_result_callback(struct dns_server_post_context *context) { struct dns_result result; char ip[DNS_MAX_CNAME_LEN]; unsigned int ping_time = -1; struct dns_request *request = context->request; if (request->result_callback == NULL) { return 0; } if (atomic_inc_return(&request->do_callback) != 1) { return 0; } ip[0] = 0; memset(&result, 0, sizeof(result)); ping_time = request->ping_time; result.domain = request->domain; result.rtcode = request->rcode; result.addr_type = request->qtype; result.ip = ip; result.has_soa = request->has_soa | context->do_force_soa; result.ping_time = ping_time; result.ip_num = 0; if (request->has_ip != 0 && context->do_force_soa == 0) { for (int i = 0; i < context->ip_num && i < MAX_IP_NUM; i++) { result.ip_addr[i] = context->ip_addr[i]; result.ip_num++; } if (request->qtype == DNS_T_A) { snprintf(ip, sizeof(ip), "%d.%d.%d.%d", request->ip_addr[0], request->ip_addr[1], request->ip_addr[2], request->ip_addr[3]); } else if (request->qtype == DNS_T_AAAA) { snprintf(ip, sizeof(ip), "%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x", request->ip_addr[0], request->ip_addr[1], request->ip_addr[2], request->ip_addr[3], request->ip_addr[4], request->ip_addr[5], request->ip_addr[6], request->ip_addr[7], request->ip_addr[8], request->ip_addr[9], request->ip_addr[10], request->ip_addr[11], request->ip_addr[12], request->ip_addr[13], request->ip_addr[14], request->ip_addr[15]); } } return request->result_callback(&result, request->user_ptr); } static int _dns_cache_specify_packet(struct dns_server_post_context *context) { if (_dns_cache_is_specify_packet(context->qtype) != 0) { return 0; } return _dns_cache_packet(context); } static int _dns_cache_reply_packet(struct dns_server_post_context *context) { struct dns_request *request = context->request; int speed = -1; if (context->do_cache == 0 || request->no_cache == 1) { return 0; } if (context->packet->head.rcode == DNS_RC_SERVFAIL || context->packet->head.rcode == DNS_RC_NXDOMAIN || context->packet->head.rcode == DNS_RC_NOTIMP) { context->reply_ttl = DNS_SERVER_FAIL_TTL; /* Do not cache record if cannot connect to remote */ if (request->remote_server_fail == 0 && context->packet->head.rcode == DNS_RC_SERVFAIL) { return 0; } if (context->packet->head.rcode == DNS_RC_NOTIMP) { return 0; } return _dns_cache_packet(context); } if (context->qtype != DNS_T_AAAA && context->qtype != DNS_T_A) { return _dns_cache_specify_packet(context); } struct dns_cache_data *cache_packet = dns_cache_new_data_packet(context->inpacket, context->inpacket_len); if (cache_packet == NULL) { return -1; } speed = request->ping_time; if (context->do_force_soa) { speed = -1; } if (_dns_server_request_update_cache(request, speed, context->qtype, cache_packet, context->cache_ttl) != 0) { tlog(TLOG_WARN, "update packet cache failed."); } _dns_cache_cname_packet(context); return 0; } static int _dns_server_setup_ipset_nftset_packet(struct dns_server_post_context *context) { int ttl = 0; struct dns_request *request = context->request; char name[DNS_MAX_CNAME_LEN] = {0}; int rr_count = 0; int timeout_value = 0; int i = 0; int j = 0; struct dns_rrs *rrs = NULL; struct dns_ipset_rule *rule = NULL; struct dns_ipset_rule *ipset_rule = NULL; struct dns_ipset_rule *ipset_rule_v4 = NULL; struct dns_ipset_rule *ipset_rule_v6 = NULL; struct dns_nftset_rule *nftset_ip = NULL; struct dns_nftset_rule *nftset_ip6 = NULL; struct dns_rule_flags *rule_flags = NULL; int check_no_speed_rule = 0; if (_dns_server_has_bind_flag(request, BIND_FLAG_NO_RULE_IPSET) == 0) { return 0; } if (context->do_ipset == 0) { return 0; } if (context->ip_num <= 0) { return 0; } if (request->ping_time < 0 && request->has_ip > 0 && request->passthrough == 0) { check_no_speed_rule = 1; } /* check ipset rule */ rule_flags = _dns_server_get_dns_rule(request, DOMAIN_RULE_FLAGS); if (!rule_flags || (rule_flags->flags & DOMAIN_FLAG_IPSET_IGN) == 0) { ipset_rule = _dns_server_get_dns_rule(request, DOMAIN_RULE_IPSET); if (ipset_rule == NULL) { ipset_rule = _dns_server_get_bind_ipset_nftset_rule(request, DOMAIN_RULE_IPSET); } } if (!rule_flags || (rule_flags->flags & DOMAIN_FLAG_IPSET_IPV4_IGN) == 0) { ipset_rule_v4 = _dns_server_get_dns_rule(request, DOMAIN_RULE_IPSET_IPV4); if (ipset_rule_v4 == NULL) { ipset_rule_v4 = _dns_server_get_bind_ipset_nftset_rule(request, DOMAIN_RULE_IPSET_IPV4); } if (ipset_rule == NULL && check_no_speed_rule && dns_conf_ipset_no_speed.ipv4_enable) { ipset_rule_v4 = &dns_conf_ipset_no_speed.ipv4; } } if (!rule_flags || (rule_flags->flags & DOMAIN_FLAG_IPSET_IPV6_IGN) == 0) { ipset_rule_v6 = _dns_server_get_dns_rule(request, DOMAIN_RULE_IPSET_IPV6); if (ipset_rule_v6 == NULL) { ipset_rule_v6 = _dns_server_get_bind_ipset_nftset_rule(request, DOMAIN_RULE_IPSET_IPV6); } if (ipset_rule_v6 == NULL && check_no_speed_rule && dns_conf_ipset_no_speed.ipv6_enable) { ipset_rule_v6 = &dns_conf_ipset_no_speed.ipv6; } } if (!rule_flags || (rule_flags->flags & DOMAIN_FLAG_NFTSET_IP_IGN) == 0) { nftset_ip = _dns_server_get_dns_rule(request, DOMAIN_RULE_NFTSET_IP); if (nftset_ip == NULL) { nftset_ip = _dns_server_get_bind_ipset_nftset_rule(request, DOMAIN_RULE_NFTSET_IP); } if (nftset_ip == NULL && check_no_speed_rule && dns_conf_nftset_no_speed.ip_enable) { nftset_ip = &dns_conf_nftset_no_speed.ip; } } if (!rule_flags || (rule_flags->flags & DOMAIN_FLAG_NFTSET_IP6_IGN) == 0) { nftset_ip6 = _dns_server_get_dns_rule(request, DOMAIN_RULE_NFTSET_IP6); if (nftset_ip6 == NULL) { nftset_ip6 = _dns_server_get_bind_ipset_nftset_rule(request, DOMAIN_RULE_NFTSET_IP6); } if (nftset_ip6 == NULL && check_no_speed_rule && dns_conf_nftset_no_speed.ip6_enable) { nftset_ip6 = &dns_conf_nftset_no_speed.ip6; } } if (!(ipset_rule || ipset_rule_v4 || ipset_rule_v6 || nftset_ip || nftset_ip6)) { return 0; } timeout_value = request->ip_ttl * 3; if (timeout_value == 0) { timeout_value = _dns_server_get_conf_ttl(request, 0) * 3; } for (j = 1; j < DNS_RRS_OPT; j++) { rrs = dns_get_rrs_start(context->packet, j, &rr_count); for (i = 0; i < rr_count && rrs; i++, rrs = dns_get_rrs_next(context->packet, rrs)) { switch (rrs->type) { case DNS_T_A: { unsigned char addr[4]; if (context->qtype != DNS_T_A) { break; } /* get A result */ dns_get_A(rrs, name, DNS_MAX_CNAME_LEN, &ttl, addr); rule = ipset_rule_v4 ? ipset_rule_v4 : ipset_rule; if (rule != NULL) { /* add IPV4 to ipset */ tlog(TLOG_DEBUG, "IPSET-MATCH: domain: %s, ipset: %s, IP: %d.%d.%d.%d", request->domain, rule->ipsetname, addr[0], addr[1], addr[2], addr[3]); ipset_add(rule->ipsetname, addr, DNS_RR_A_LEN, timeout_value); } if (nftset_ip != NULL) { /* add IPV4 to ipset */ tlog(TLOG_DEBUG, "NFTSET-MATCH: domain: %s, nftset: %s %s %s, IP: %d.%d.%d.%d", request->domain, nftset_ip->familyname, nftset_ip->nfttablename, nftset_ip->nftsetname, addr[0], addr[1], addr[2], addr[3]); nftset_add(nftset_ip->familyname, nftset_ip->nfttablename, nftset_ip->nftsetname, addr, DNS_RR_A_LEN, timeout_value); } } break; case DNS_T_AAAA: { unsigned char addr[16]; if (context->qtype != DNS_T_AAAA) { /* ignore non-matched query type */ break; } dns_get_AAAA(rrs, name, DNS_MAX_CNAME_LEN, &ttl, addr); rule = ipset_rule_v6 ? ipset_rule_v6 : ipset_rule; if (rule != NULL) { tlog(TLOG_DEBUG, "IPSET-MATCH: domain: %s, ipset: %s, IP: " "%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x", request->domain, 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]); ipset_add(rule->ipsetname, addr, DNS_RR_AAAA_LEN, timeout_value); } if (nftset_ip6 != NULL) { /* add IPV6 to ipset */ tlog(TLOG_DEBUG, "NFTSET-MATCH: domain: %s, nftset: %s %s %s, IP: " "%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x", request->domain, nftset_ip6->familyname, nftset_ip6->nfttablename, nftset_ip6->nftsetname, 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]); nftset_add(nftset_ip6->familyname, nftset_ip6->nfttablename, nftset_ip6->nftsetname, addr, DNS_RR_AAAA_LEN, timeout_value); } } break; default: break; } } } return 0; } 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; DNS_CHILD_POST_RESULT child_ret = DNS_CHILD_POST_FAIL; /* not a child request */ if (parent_request == NULL) { return 0; } if (request->child_callback) { int is_first_resp = context->no_release_parent; child_ret = request->child_callback(parent_request, request, is_first_resp); } 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; parent_context.do_ipset = context->do_ipset; parent_context.do_force_soa = context->do_force_soa; parent_context.do_audit = context->do_audit; parent_context.do_reply = context->do_reply; parent_context.reply_ttl = context->reply_ttl; parent_context.cache_ttl = context->cache_ttl; parent_context.skip_notify_count = context->skip_notify_count; parent_context.select_all_best_ip = 1; parent_context.no_release_parent = context->no_release_parent; _dns_request_post(&parent_context); _dns_server_reply_all_pending_list(parent_request, &parent_context); } if (context->no_release_parent == 0) { tlog(TLOG_DEBUG, "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); } if (child_ret == DNS_CHILD_POST_FAIL) { return -1; } return 0; } static int _dns_request_update_id_ttl(struct dns_server_post_context *context) { int ttl = context->reply_ttl; struct dns_request *request = context->request; if (dns_conf_rr_ttl_reply_max > 0) { if (request->ip_ttl > dns_conf_rr_ttl_reply_max && ttl == 0) { ttl = request->ip_ttl; } if (ttl > dns_conf_rr_ttl_reply_max) { ttl = dns_conf_rr_ttl_reply_max; } if (ttl == 0) { ttl = dns_conf_rr_ttl_reply_max; } } if (ttl == 0) { ttl = request->ip_ttl; if (ttl == 0) { ttl = _dns_server_get_conf_ttl(request, ttl); } } struct dns_update_param param; param.id = request->id; param.cname_ttl = ttl; param.ip_ttl = ttl; if (dns_packet_update(context->inpacket, context->inpacket_len, ¶m) != 0) { tlog(TLOG_ERROR, "update packet info failed."); return -1; } return 0; } static int _dns_request_post(struct dns_server_post_context *context) { struct dns_request *request = context->request; char clientip[DNS_MAX_CNAME_LEN] = {0}; int ret = 0; tlog(TLOG_DEBUG, "reply %s qtype: %d, rcode: %d, reply: %d", request->domain, request->qtype, context->packet->head.rcode, context->do_reply); /* init a new DNS packet */ ret = _dns_setup_dns_packet(context); if (ret != 0) { tlog(TLOG_ERROR, "setup dns packet failed."); return -1; } ret = _dns_setup_dns_raw_packet(context); if (ret != 0) { tlog(TLOG_ERROR, "set dns raw packet failed."); return -1; } /* cache reply packet */ ret = _dns_cache_reply_packet(context); if (ret != 0) { tlog(TLOG_WARN, "cache packet for %s failed.", request->domain); } /* setup ipset */ _dns_server_setup_ipset_nftset_packet(context); /* reply child request */ _dns_result_child_post(context); if (context->do_reply == 0) { return 0; } if (context->skip_notify_count == 0) { if (atomic_inc_return(&request->notified) != 1) { tlog(TLOG_DEBUG, "skip reply %s %d", request->domain, request->qtype); return 0; } } /* log audit log */ _dns_server_audit_log(context); /* reply API callback */ _dns_result_callback(context); if (request->conn == NULL) { return 0; } ret = _dns_request_update_id_ttl(context); if (ret != 0) { tlog(TLOG_ERROR, "update packet ttl failed."); return -1; } tlog(TLOG_INFO, "result: %s, client: %s, qtype: %d, id: %d, group: %s, time: %lums", request->domain, get_host_by_addr(clientip, sizeof(clientip), (struct sockaddr *)&request->addr), request->qtype, request->id, request->dns_group_name[0] != '\0' ? request->dns_group_name : "default", get_tick_count() - request->send_tick); ret = _dns_reply_inpacket(request, context->inpacket, context->inpacket_len); if (ret != 0) { tlog(TLOG_DEBUG, "reply raw packet to client failed."); return -1; } return 0; } static int _dns_server_reply_SOA(int rcode, struct dns_request *request) { /* return SOA record */ request->rcode = rcode; if (request->ip_ttl <= 0) { request->ip_ttl = DNS_SERVER_SOA_TTL; } _dns_server_setup_soa(request); struct dns_server_post_context context; _dns_server_post_context_init(&context, request); context.do_audit = 1; context.do_reply = 1; context.do_force_soa = 1; _dns_request_post(&context); return 0; } static int _dns_server_reply_all_pending_list(struct dns_request *request, struct dns_server_post_context *context) { struct dns_request_pending_list *pending_list = NULL; struct dns_request *req = NULL; struct dns_request *tmp = NULL; int ret = 0; if (request->request_pending_list == NULL) { return 0; } pthread_mutex_lock(&server.request_pending_lock); pending_list = request->request_pending_list; request->request_pending_list = NULL; hlist_del_init(&pending_list->node); pthread_mutex_unlock(&server.request_pending_lock); pthread_mutex_lock(&pending_list->request_list_lock); list_del_init(&request->pending_list); list_for_each_entry_safe(req, tmp, &(pending_list->request_list), pending_list) { struct dns_server_post_context context_pending; _dns_server_post_context_init_from(&context_pending, req, context->packet, context->inpacket, context->inpacket_len); req->dualstack_selection = request->dualstack_selection; req->dualstack_selection_query = request->dualstack_selection_query; req->dualstack_selection_force_soa = request->dualstack_selection_force_soa; req->dualstack_selection_has_ip = request->dualstack_selection_has_ip; req->dualstack_selection_ping_time = request->dualstack_selection_ping_time; req->ping_time = request->ping_time; _dns_server_get_answer(&context_pending); context_pending.do_cache = 0; context_pending.do_audit = context->do_audit; context_pending.do_reply = context->do_reply; context_pending.do_force_soa = context->do_force_soa; context_pending.do_ipset = 0; context_pending.reply_ttl = request->ip_ttl; context_pending.no_release_parent = 0; _dns_server_reply_passthrough(&context_pending); req->request_pending_list = NULL; list_del_init(&req->pending_list); _dns_server_request_release_complete(req, 0); } pthread_mutex_unlock(&pending_list->request_list_lock); free(pending_list); return ret; } static void _dns_server_check_complete_dualstack(struct dns_request *request, struct dns_request *dualstack_request) { if (dualstack_request == NULL || request == NULL) { return; } if (dualstack_request->qtype == DNS_T_A && dns_conf_dualstack_ip_allow_force_AAAA == 0) { return; } if (dualstack_request->ping_time > 0) { return; } if (dualstack_request->dualstack_selection_query == 1) { return; } if (request->ping_time <= (dns_conf_dualstack_ip_selection_threshold * 10)) { return; } dualstack_request->dualstack_selection_has_ip = request->has_ip; dualstack_request->dualstack_selection_ping_time = request->ping_time; dualstack_request->dualstack_selection_force_soa = 1; _dns_server_request_complete(dualstack_request); } static int _dns_server_force_dualstack(struct dns_request *request) { /* for dualstack request as first pending request, check if need to choose another request*/ if (request->dualstack_request) { struct dns_request *dualstack_request = request->dualstack_request; request->dualstack_selection_has_ip = dualstack_request->has_ip; request->dualstack_selection_ping_time = dualstack_request->ping_time; request->dualstack_selection = 1; /* if another request still waiting for ping, force complete another request */ _dns_server_check_complete_dualstack(request, dualstack_request); } if (request->dualstack_selection_ping_time < 0 || request->dualstack_selection == 0) { return -1; } if (request->has_soa || request->rcode != DNS_RC_NOERROR) { return -1; } if (request->dualstack_selection_has_ip == 0) { return -1; } if (request->ping_time > 0) { if (request->dualstack_selection_ping_time + (dns_conf_dualstack_ip_selection_threshold * 10) > request->ping_time) { return -1; } } if (request->qtype == DNS_T_A && dns_conf_dualstack_ip_allow_force_AAAA == 0) { return -1; } /* if ipv4 is fasting than ipv6, add ipv4 to cache, and return SOA for AAAA request */ tlog(TLOG_INFO, "result: %s, qtype: %d, force %s preferred, id: %d, time1: %d, time2: %d", request->domain, request->qtype, request->qtype == DNS_T_AAAA ? "IPv4" : "IPv6", request->id, request->ping_time, request->dualstack_selection_ping_time); request->dualstack_selection_force_soa = 1; return 0; } static int _dns_server_request_complete_with_all_IPs(struct dns_request *request, int with_all_ips) { int ttl = 0; struct dns_server_post_context context; if (request->rcode == DNS_RC_SERVFAIL || request->rcode == DNS_RC_NXDOMAIN) { ttl = DNS_SERVER_FAIL_TTL; } if (request->prefetch == 1) { return 0; } if (atomic_inc_return(&request->notified) != 1) { return 0; } if (request->has_ip != 0 && request->passthrough == 0) { request->has_soa = 0; if (request->has_ping_result == 0 && request->ip_ttl > DNS_SERVER_TMOUT_TTL) { request->ip_ttl = DNS_SERVER_TMOUT_TTL; } ttl = request->ip_ttl; } if (_dns_server_force_dualstack(request) == 0) { goto out; } if (request->has_soa) { tlog(TLOG_INFO, "result: %s, qtype: %d, SOA", request->domain, request->qtype); } else { if (request->qtype == DNS_T_A) { tlog(TLOG_INFO, "result: %s, qtype: %d, rtt: %.1f ms, %d.%d.%d.%d", request->domain, request->qtype, ((float)request->ping_time) / 10, request->ip_addr[0], request->ip_addr[1], request->ip_addr[2], request->ip_addr[3]); } else if (request->qtype == DNS_T_AAAA) { tlog(TLOG_INFO, "result: %s, qtype: %d, rtt: %.1f ms, " "%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x", request->domain, request->qtype, ((float)request->ping_time) / 10, request->ip_addr[0], request->ip_addr[1], request->ip_addr[2], request->ip_addr[3], request->ip_addr[4], request->ip_addr[5], request->ip_addr[6], request->ip_addr[7], request->ip_addr[8], request->ip_addr[9], request->ip_addr[10], request->ip_addr[11], request->ip_addr[12], request->ip_addr[13], request->ip_addr[14], request->ip_addr[15]); } } out: _dns_server_post_context_init(&context, request); context.do_cache = 1; context.do_ipset = 1; context.do_force_soa = request->dualstack_selection_force_soa | request->force_soa; context.do_audit = 1; context.do_reply = 1; context.reply_ttl = _dns_server_get_reply_ttl(request, ttl); context.skip_notify_count = 1; context.select_all_best_ip = with_all_ips; context.no_release_parent = 1; _dns_request_post(&context); return _dns_server_reply_all_pending_list(request, &context); } static int _dns_server_request_complete(struct dns_request *request) { return _dns_server_request_complete_with_all_IPs(request, 0); } static int _dns_ip_address_check_add(struct dns_request *request, char *cname, unsigned char *addr, dns_type_t addr_type, int ping_time, struct dns_ip_address **out_addr_map) { uint32_t key = 0; struct dns_ip_address *addr_map = NULL; int addr_len = 0; if (ping_time == 0) { ping_time = -1; } if (addr_type == DNS_T_A) { addr_len = DNS_RR_A_LEN; } else if (addr_type == DNS_T_AAAA) { addr_len = DNS_RR_AAAA_LEN; } else { return -1; } /* store the ip address and the number of hits */ key = jhash(addr, addr_len, 0); key = jhash(&addr_type, sizeof(addr_type), key); pthread_mutex_lock(&request->ip_map_lock); hash_for_each_possible(request->ip_map, addr_map, node, key) { if (addr_map->addr_type != addr_type) { continue; } if (memcmp(addr_map->ip_addr, addr, addr_len) != 0) { continue; } addr_map->hitnum++; addr_map->recv_tick = get_tick_count(); pthread_mutex_unlock(&request->ip_map_lock); return -1; } atomic_inc(&request->ip_map_num); addr_map = malloc(sizeof(*addr_map)); if (addr_map == NULL) { pthread_mutex_unlock(&request->ip_map_lock); tlog(TLOG_ERROR, "malloc addr map failed"); return -1; } memset(addr_map, 0, sizeof(*addr_map)); addr_map->addr_type = addr_type; addr_map->hitnum = 1; addr_map->recv_tick = get_tick_count(); addr_map->ping_time = ping_time; memcpy(addr_map->ip_addr, addr, addr_len); if (dns_conf_force_no_cname == 0) { safe_strncpy(addr_map->cname, cname, DNS_MAX_CNAME_LEN); } hash_add(request->ip_map, &addr_map->node, key); pthread_mutex_unlock(&request->ip_map_lock); if (out_addr_map != NULL) { *out_addr_map = addr_map; } return 0; } static void _dns_server_request_remove_all(void) { struct dns_request *request = NULL; struct dns_request *tmp = NULL; LIST_HEAD(remove_list); pthread_mutex_lock(&server.request_list_lock); list_for_each_entry_safe(request, tmp, &server.request_list, list) { list_add_tail(&request->check_list, &remove_list); _dns_server_request_get(request); } pthread_mutex_unlock(&server.request_list_lock); list_for_each_entry_safe(request, tmp, &remove_list, check_list) { _dns_server_request_complete(request); _dns_server_request_release(request); } } static void _dns_server_select_possible_ipaddress(struct dns_request *request) { int maxhit = 0; unsigned long bucket = 0; unsigned long max_recv_tick = 0; struct dns_ip_address *addr_map = NULL; struct dns_ip_address *maxhit_addr_map = NULL; struct dns_ip_address *last_recv_addr_map = NULL; struct dns_ip_address *selected_addr_map = NULL; struct hlist_node *tmp = NULL; if (atomic_read(&request->notified) > 0) { return; } if (request->no_select_possible_ip != 0) { return; } if (request->ping_time > 0) { return; } /* Return the most likely correct IP address */ /* Returns the IP with the most hits, or the last returned record is considered to be the most likely correct. */ pthread_mutex_lock(&request->ip_map_lock); hash_for_each_safe(request->ip_map, bucket, tmp, addr_map, node) { if (addr_map->addr_type != request->qtype) { continue; } if (addr_map->recv_tick - request->send_tick > max_recv_tick) { max_recv_tick = addr_map->recv_tick - request->send_tick; last_recv_addr_map = addr_map; } if (addr_map->hitnum > maxhit) { maxhit = addr_map->hitnum; maxhit_addr_map = addr_map; } } pthread_mutex_unlock(&request->ip_map_lock); if (maxhit_addr_map && maxhit > 1) { selected_addr_map = maxhit_addr_map; } else if (last_recv_addr_map) { selected_addr_map = last_recv_addr_map; } if (selected_addr_map == NULL) { return; } tlog(TLOG_DEBUG, "select best ip address, %s", request->domain); switch (request->qtype) { case DNS_T_A: { memcpy(request->ip_addr, selected_addr_map->ip_addr, DNS_RR_A_LEN); request->ip_ttl = dns_conf_rr_ttl_min > 0 ? dns_conf_rr_ttl_min : DNS_SERVER_TMOUT_TTL; tlog(TLOG_DEBUG, "possible result: %s, rcode: %d, hitnum: %d, %d.%d.%d.%d", request->domain, request->rcode, selected_addr_map->hitnum, request->ip_addr[0], request->ip_addr[1], request->ip_addr[2], request->ip_addr[3]); } break; case DNS_T_AAAA: { memcpy(request->ip_addr, selected_addr_map->ip_addr, DNS_RR_AAAA_LEN); request->ip_ttl = dns_conf_rr_ttl_min > 0 ? dns_conf_rr_ttl_min : DNS_SERVER_TMOUT_TTL; tlog(TLOG_DEBUG, "possible result: %s, rcode: %d, hitnum: %d, " "%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x", request->domain, request->rcode, selected_addr_map->hitnum, request->ip_addr[0], request->ip_addr[1], request->ip_addr[2], request->ip_addr[3], request->ip_addr[4], request->ip_addr[5], request->ip_addr[6], request->ip_addr[7], request->ip_addr[8], request->ip_addr[9], request->ip_addr[10], request->ip_addr[11], request->ip_addr[12], request->ip_addr[13], request->ip_addr[14], request->ip_addr[15]); } break; default: break; } } static void _dns_server_delete_request(struct dns_request *request) { if (atomic_read(&request->notified) == 0) { _dns_server_request_complete(request); } if (request->conn) { _dns_server_conn_release(request->conn); } pthread_mutex_destroy(&request->ip_map_lock); memset(request, 0, sizeof(*request)); free(request); } static void _dns_server_complete_with_multi_ipaddress(struct dns_request *request) { struct dns_server_post_context context; int do_reply = 0; if (atomic_read(&request->ip_map_num) > 0) { request->has_soa = 0; } if (atomic_inc_return(&request->notified) == 1) { do_reply = 1; _dns_server_force_dualstack(request); } if (request->passthrough && do_reply == 0) { return; } _dns_server_post_context_init(&context, request); context.do_cache = 1; context.do_ipset = 1; context.do_reply = do_reply; context.do_log_result = 1; context.select_all_best_ip = 1; context.skip_notify_count = 1; context.do_force_soa = request->dualstack_selection_force_soa | request->force_soa; _dns_request_post(&context); _dns_server_reply_all_pending_list(request, &context); } static void _dns_server_request_release_complete(struct dns_request *request, int do_complete) { struct dns_ip_address *addr_map = NULL; struct hlist_node *tmp = NULL; unsigned long bucket = 0; pthread_mutex_lock(&server.request_list_lock); int refcnt = atomic_dec_return(&request->refcnt); if (refcnt) { pthread_mutex_unlock(&server.request_list_lock); if (refcnt < 0) { BUG("BUG: refcnt is %d, domain %s, qtype %d", refcnt, request->domain, request->qtype); } return; } list_del_init(&request->list); list_del_init(&request->check_list); pthread_mutex_unlock(&server.request_list_lock); pthread_mutex_lock(&server.request_pending_lock); list_del_init(&request->pending_list); pthread_mutex_unlock(&server.request_pending_lock); if (do_complete) { /* Select max hit ip address, and return to client */ _dns_server_select_possible_ipaddress(request); _dns_server_complete_with_multi_ipaddress(request); } if (request->parent_request != NULL) { _dns_server_request_release(request->parent_request); request->parent_request = NULL; } 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); _dns_server_delete_request(request); } static void _dns_server_request_release(struct dns_request *request) { _dns_server_request_release_complete(request, 1); } static void _dns_server_request_get(struct dns_request *request) { if (atomic_inc_return(&request->refcnt) <= 0) { BUG("BUG: request ref is invalid, %s", request->domain); } } static int _dns_server_set_to_pending_list(struct dns_request *request) { struct dns_request_pending_list *pending_list = NULL; struct dns_request_pending_list *pending_list_tmp = NULL; uint32_t key = 0; int ret = -1; if (request->qtype != DNS_T_A && request->qtype != DNS_T_AAAA) { return ret; } key = hash_string(request->domain); key = hash_string_initval(request->dns_group_name, key); key = jhash(&(request->qtype), sizeof(request->qtype), key); key = jhash(&(request->server_flags), sizeof(request->server_flags), key); pthread_mutex_lock(&server.request_pending_lock); hash_for_each_possible(server.request_pending, pending_list_tmp, node, key) { if (request->qtype != pending_list_tmp->qtype) { continue; } if (request->server_flags != pending_list_tmp->server_flags) { continue; } if (strcmp(request->dns_group_name, pending_list_tmp->dns_group_name) != 0) { continue; } if (strncmp(request->domain, pending_list_tmp->domain, DNS_MAX_CNAME_LEN) != 0) { continue; } pending_list = pending_list_tmp; break; } if (pending_list == NULL) { pending_list = malloc(sizeof(*pending_list)); if (pending_list == NULL) { ret = -1; goto out; } memset(pending_list, 0, sizeof(*pending_list)); pthread_mutex_init(&pending_list->request_list_lock, NULL); INIT_LIST_HEAD(&pending_list->request_list); INIT_HLIST_NODE(&pending_list->node); pending_list->qtype = request->qtype; pending_list->server_flags = request->server_flags; safe_strncpy(pending_list->domain, request->domain, DNS_MAX_CNAME_LEN); safe_strncpy(pending_list->dns_group_name, request->dns_group_name, DNS_GROUP_NAME_LEN); hash_add(server.request_pending, &pending_list->node, key); request->request_pending_list = pending_list; } else { ret = 0; } if (ret == 0) { _dns_server_request_get(request); } list_add_tail(&request->pending_list, &pending_list->request_list); out: pthread_mutex_unlock(&server.request_pending_lock); return ret; } static struct dns_request *_dns_server_new_request(void) { struct dns_request *request = NULL; request = malloc(sizeof(*request)); if (request == NULL) { tlog(TLOG_ERROR, "malloc request failed.\n"); goto errout; } memset(request, 0, sizeof(*request)); pthread_mutex_init(&request->ip_map_lock, NULL); atomic_set(&request->adblock, 0); atomic_set(&request->soa_num, 0); atomic_set(&request->ip_map_num, 0); atomic_set(&request->refcnt, 0); atomic_set(&request->notified, 0); atomic_set(&request->do_callback, 0); request->ping_time = -1; request->prefetch = 0; request->dualstack_selection = dns_conf_dualstack_ip_selection; request->dualstack_selection_ping_time = -1; request->rcode = DNS_RC_SERVFAIL; request->conn = NULL; request->qclass = DNS_C_IN; request->result_callback = NULL; request->check_order_list = &dns_conf_check_orders; request->response_mode = dns_conf_response_mode; INIT_LIST_HEAD(&request->list); INIT_LIST_HEAD(&request->pending_list); INIT_LIST_HEAD(&request->check_list); hash_init(request->ip_map); _dns_server_request_get(request); return request; errout: return NULL; } static void _dns_server_ping_result(struct ping_host_struct *ping_host, const char *host, FAST_PING_RESULT result, struct sockaddr *addr, socklen_t addr_len, int seqno, int ttl, struct timeval *tv, int error, void *userptr) { struct dns_request *request = userptr; int may_complete = 0; int threshold = 100; struct dns_ip_address *addr_map = NULL; int last_rtt = request->ping_time; if (request == NULL) { return; } if (result == PING_RESULT_END) { _dns_server_request_release(request); fast_ping_stop(ping_host); return; } else if (result == PING_RESULT_TIMEOUT) { tlog(TLOG_DEBUG, "ping %s timeout", host); goto out; return; } else if (result == PING_RESULT_ERROR) { if (addr->sa_family != AF_INET6) { return; } if (is_ipv6_ready) { if (error == EADDRNOTAVAIL || errno == EACCES) { is_ipv6_ready = 0; tlog(TLOG_ERROR, "IPV6 is not ready, disable all ipv6 feature, recheck after %ds", IPV6_READY_CHECK_TIME); } } return; } int rtt = tv->tv_sec * 10000 + tv->tv_usec / 100; if (result == PING_RESULT_RESPONSE) { tlog(TLOG_DEBUG, "from %s: seq=%d time=%d, lasttime=%d id=%d", host, seqno, rtt, last_rtt, request->id); } else { tlog(TLOG_DEBUG, "from %s: seq=%d timeout, id=%d", host, seqno, request->id); } switch (addr->sa_family) { case AF_INET: { struct sockaddr_in *addr_in = NULL; addr_in = (struct sockaddr_in *)addr; addr_map = _dns_ip_address_get(request, (unsigned char *)&addr_in->sin_addr.s_addr, DNS_T_A); if (addr_map) { addr_map->ping_time = rtt; } if (request->ping_time > rtt || request->ping_time == -1) { memcpy(request->ip_addr, &addr_in->sin_addr.s_addr, 4); request->ping_time = rtt; request->has_cname = 0; request->has_ip = 1; if (addr_map && addr_map->cname[0] != 0) { request->has_cname = 1; safe_strncpy(request->cname, addr_map->cname, DNS_MAX_CNAME_LEN); } else { request->has_cname = 0; } } if (request->qtype == DNS_T_AAAA && request->dualstack_selection) { if (request->ping_time < 0 && request->has_soa == 0) { return; } } if (request->qtype == DNS_T_A) { request->has_ping_result = 1; } } break; case AF_INET6: { struct sockaddr_in6 *addr_in6 = NULL; addr_in6 = (struct sockaddr_in6 *)addr; if (IN6_IS_ADDR_V4MAPPED(&addr_in6->sin6_addr)) { addr_map = _dns_ip_address_get(request, addr_in6->sin6_addr.s6_addr + 12, DNS_T_A); if (addr_map) { addr_map->ping_time = rtt; } if (request->ping_time > rtt || request->ping_time == -1) { request->ping_time = rtt; request->has_cname = 0; request->has_ip = 1; memcpy(request->ip_addr, addr_in6->sin6_addr.s6_addr + 12, 4); if (addr_map && addr_map->cname[0] != 0) { request->has_cname = 1; safe_strncpy(request->cname, addr_map->cname, DNS_MAX_CNAME_LEN); } else { request->has_cname = 0; } } if (request->qtype == DNS_T_A) { request->has_ping_result = 1; } } else { addr_map = _dns_ip_address_get(request, addr_in6->sin6_addr.s6_addr, DNS_T_AAAA); if (addr_map) { addr_map->ping_time = rtt; } if (request->ping_time > rtt || request->ping_time == -1) { request->ping_time = rtt; request->has_cname = 0; request->has_ip = 1; memcpy(request->ip_addr, addr_in6->sin6_addr.s6_addr, 16); if (addr_map && addr_map->cname[0] != 0) { request->has_cname = 1; safe_strncpy(request->cname, addr_map->cname, DNS_MAX_CNAME_LEN); } else { request->has_cname = 0; } } if (request->qtype == DNS_T_AAAA) { request->has_ping_result = 1; } } } break; default: break; } out: /* If the ping delay is less than the threshold, the result is returned */ if (request->ping_time > 0) { if (request->ping_time < threshold) { may_complete = 1; } else if (request->ping_time < (int)(get_tick_count() - request->send_tick) * 8) { may_complete = 1; } } /* Get first ping result */ if (request->response_mode == DNS_RESPONSE_MODE_FIRST_PING_IP && last_rtt == -1 && request->ping_time > 0) { may_complete = 1; } if (may_complete && request->has_ping_result == 1) { _dns_server_request_complete(request); } } static int _dns_server_ping(struct dns_request *request, PING_TYPE type, char *ip, int timeout) { if (fast_ping_start(type, ip, 1, 0, timeout, _dns_server_ping_result, request) == NULL) { return -1; } return 0; } static int _dns_server_check_speed(struct dns_request *request, char *ip) { char tcp_ip[DNS_MAX_CNAME_LEN] = {0}; int port = 80; int type = DOMAIN_CHECK_NONE; int order = request->check_order; int ping_timeout = DNS_PING_TIMEOUT; unsigned long now = get_tick_count(); if (order >= DOMAIN_CHECK_NUM || request->check_order_list == NULL) { return -1; } if (request->passthrough) { return -1; } ping_timeout = ping_timeout - (now - request->send_tick); if (ping_timeout > DNS_PING_TIMEOUT) { ping_timeout = DNS_PING_TIMEOUT; } else if (ping_timeout < 200) { ping_timeout = 200; } port = request->check_order_list->orders[order].tcp_port; type = request->check_order_list->orders[order].type; switch (type) { case DOMAIN_CHECK_ICMP: tlog(TLOG_DEBUG, "ping %s with icmp, order: %d, timeout: %d", ip, order, ping_timeout); return _dns_server_ping(request, PING_TYPE_ICMP, ip, ping_timeout); break; case DOMAIN_CHECK_TCP: snprintf(tcp_ip, sizeof(tcp_ip), "%s:%d", ip, port); tlog(TLOG_DEBUG, "ping %s with tcp, order: %d, timeout: %d", tcp_ip, order, ping_timeout); return _dns_server_ping(request, PING_TYPE_TCP, tcp_ip, ping_timeout); break; default: break; } return -1; } static struct dns_ip_rules *_dns_server_ip_rule_get(struct dns_request *request, unsigned char *addr, int addr_len, dns_type_t addr_type) { prefix_t prefix; radix_node_t *node = NULL; struct dns_ip_rules *rule = NULL; /* Match IP address rules */ if (prefix_from_blob(addr, addr_len, addr_len * 8, &prefix) == NULL) { return NULL; } switch (prefix.family) { case AF_INET: node = radix_search_best(dns_conf_address_rule.ipv4, &prefix); break; case AF_INET6: node = radix_search_best(dns_conf_address_rule.ipv6, &prefix); break; default: break; } if (node == NULL) { return NULL; } if (node->data == NULL) { return NULL; } rule = node->data; return rule; } static int _dns_server_ip_rule_check(struct dns_request *request, struct dns_ip_rules *ip_rules, int result_flag) { struct ip_rule_flags *rule_flags = NULL; if (ip_rules == NULL) { goto rule_not_found; } rule_flags = container_of(ip_rules->rules[IP_RULE_FLAGS], struct ip_rule_flags, head); if (rule_flags != NULL) { if (rule_flags->flags & IP_RULE_FLAG_BOGUS) { request->rcode = DNS_RC_NXDOMAIN; request->has_soa = 1; request->force_soa = 1; _dns_server_setup_soa(request); goto nxdomain; } /* blacklist-ip */ if (rule_flags->flags & IP_RULE_FLAG_BLACKLIST) { if (result_flag & DNSSERVER_FLAG_BLACKLIST_IP) { goto match; } } /* ignore-ip */ if (rule_flags->flags & IP_RULE_FLAG_IP_IGNORE) { goto skip; } } if (ip_rules->rules[IP_RULE_ALIAS] != NULL) { goto match; } rule_not_found: if (result_flag & DNSSERVER_FLAG_WHITELIST_IP) { if (rule_flags == NULL) { goto skip; } if (!(rule_flags->flags & IP_RULE_FLAG_WHITELIST)) { goto skip; } } return -1; skip: return -2; nxdomain: return -3; match: if (request->rcode == DNS_RC_SERVFAIL) { request->rcode = DNS_RC_NXDOMAIN; } return 0; } static int _dns_server_process_ip_alias(struct dns_request *request, struct dns_iplist_ip_addresses *alias, unsigned char **paddrs, int *paddr_num, int max_paddr_num, int addr_len) { int addr_num = 0; if (alias == 0) { return 0; } if (request == NULL) { return -1; } if (alias->ipaddr_num <= 0) { return 0; } for (int i = 0; i < alias->ipaddr_num && i < max_paddr_num; i++) { if (alias->ipaddr[i].addr_len != addr_len) { continue; } paddrs[i] = alias->ipaddr[i].addr; addr_num++; } *paddr_num = addr_num; return 0; } static int _dns_server_process_ip_rule(struct dns_request *request, unsigned char *addr, int addr_len, dns_type_t addr_type, int result_flag, struct dns_iplist_ip_addresses **alias) { struct dns_ip_rules *ip_rules = NULL; int ret = 0; ip_rules = _dns_server_ip_rule_get(request, addr, addr_len, addr_type); ret = _dns_server_ip_rule_check(request, ip_rules, result_flag); if (ret != 0) { return ret; } if (ip_rules->rules[IP_RULE_ALIAS] && alias != NULL) { if (request->no_ipalias == 0) { struct ip_rule_alias *rule = container_of(ip_rules->rules[IP_RULE_ALIAS], struct ip_rule_alias, head); *alias = &rule->ip_alias; if (alias == NULL) { return 0; } } /* need process ip alias */ return -1; } return 0; } static int _dns_server_is_adblock_ipv6(const unsigned char addr[16]) { int i = 0; for (i = 0; i < 15; i++) { if (addr[i]) { return -1; } } if (addr[15] == 0 || addr[15] == 1) { return 0; } return -1; } static int _dns_server_process_answer_A(struct dns_rrs *rrs, struct dns_request *request, const char *domain, char *cname, unsigned int result_flag) { int ttl = 0; int ip_check_result = 0; unsigned char addr[4]; unsigned char *paddrs[MAX_IP_NUM]; int paddr_num = 0; char name[DNS_MAX_CNAME_LEN] = {0}; char ip[DNS_MAX_CNAME_LEN] = {0}; struct dns_iplist_ip_addresses *alias = NULL; if (request->qtype != DNS_T_A) { /* ignore non-matched query type */ if (request->dualstack_selection == 0) { return 0; } } /* get A result */ dns_get_A(rrs, name, DNS_MAX_CNAME_LEN, &ttl, addr); paddrs[paddr_num] = addr; paddr_num = 1; tlog(TLOG_DEBUG, "domain: %s TTL: %d IP: %d.%d.%d.%d", name, ttl, addr[0], addr[1], addr[2], addr[3]); /* if domain is not match */ if (strncmp(name, domain, DNS_MAX_CNAME_LEN) != 0 && strncmp(cname, name, DNS_MAX_CNAME_LEN) != 0) { return -1; } /* ip rule check */ ip_check_result = _dns_server_process_ip_rule(request, addr, 4, DNS_T_A, result_flag, &alias); if (ip_check_result == 0) { /* match */ return -1; } else if (ip_check_result == -2 || ip_check_result == -3) { /* skip, nxdomain */ return ip_check_result; } int ret = _dns_server_process_ip_alias(request, alias, paddrs, &paddr_num, MAX_IP_NUM, DNS_RR_A_LEN); if (ret != 0) { return ret; } for (int i = 0; i < paddr_num; i++) { unsigned char *paddr = paddrs[i]; if (atomic_read(&request->ip_map_num) == 0) { request->has_ip = 1; memcpy(request->ip_addr, paddr, DNS_RR_A_LEN); request->ip_ttl = _dns_server_get_conf_ttl(request, ttl); if (cname[0] != 0 && request->has_cname == 0 && dns_conf_force_no_cname == 0) { request->has_cname = 1; safe_strncpy(request->cname, cname, DNS_MAX_CNAME_LEN); } } else { if (ttl < request->ip_ttl) { request->ip_ttl = _dns_server_get_conf_ttl(request, ttl); } } /* Ad blocking result */ if (paddr[0] == 0 || paddr[0] == 127) { /* If half of the servers return the same result, then ignore this address */ if (atomic_inc_return(&request->adblock) <= (dns_server_alive_num() / 2 + dns_server_alive_num() % 2)) { request->rcode = DNS_RC_NOERROR; return -1; } } /* add this ip to request */ if (_dns_ip_address_check_add(request, cname, paddr, DNS_T_A, 0, NULL) != 0) { return -1; } snprintf(ip, sizeof(ip), "%d.%d.%d.%d", paddr[0], paddr[1], paddr[2], paddr[3]); /* start ping */ _dns_server_request_get(request); if (_dns_server_check_speed(request, ip) != 0) { _dns_server_request_release(request); } } return 0; } static int _dns_server_process_answer_AAAA(struct dns_rrs *rrs, struct dns_request *request, const char *domain, char *cname, unsigned int result_flag) { unsigned char addr[16]; unsigned char *paddrs[MAX_IP_NUM]; int paddr_num = 0; char name[DNS_MAX_CNAME_LEN] = {0}; char ip[DNS_MAX_CNAME_LEN] = {0}; int ttl = 0; int ip_check_result = 0; struct dns_iplist_ip_addresses *alias = NULL; if (request->qtype != DNS_T_AAAA) { /* ignore non-matched query type */ return -1; } dns_get_AAAA(rrs, name, DNS_MAX_CNAME_LEN, &ttl, addr); paddrs[paddr_num] = addr; paddr_num = 1; tlog(TLOG_DEBUG, "domain: %s TTL: %d IP: %.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x", name, ttl, 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]); /* if domain is not match */ if (strncmp(name, domain, DNS_MAX_CNAME_LEN) != 0 && strncmp(cname, name, DNS_MAX_CNAME_LEN) != 0) { return -1; } ip_check_result = _dns_server_process_ip_rule(request, addr, 16, DNS_T_AAAA, result_flag, &alias); if (ip_check_result == 0) { /* match */ return -1; } else if (ip_check_result == -2 || ip_check_result == -3) { /* skip, nxdomain */ return ip_check_result; } int ret = _dns_server_process_ip_alias(request, alias, paddrs, &paddr_num, MAX_IP_NUM, DNS_RR_AAAA_LEN); if (ret != 0) { return ret; } for (int i = 0; i < paddr_num; i++) { unsigned char *paddr = paddrs[i]; if (atomic_read(&request->ip_map_num) == 0) { request->has_ip = 1; memcpy(request->ip_addr, paddr, DNS_RR_AAAA_LEN); request->ip_ttl = _dns_server_get_conf_ttl(request, ttl); if (cname[0] != 0 && request->has_cname == 0 && dns_conf_force_no_cname == 0) { request->has_cname = 1; safe_strncpy(request->cname, cname, DNS_MAX_CNAME_LEN); } } else { if (ttl < request->ip_ttl) { request->ip_ttl = _dns_server_get_conf_ttl(request, ttl); } } /* Ad blocking result */ if (_dns_server_is_adblock_ipv6(paddr) == 0) { /* If half of the servers return the same result, then ignore this address */ if (atomic_inc_return(&request->adblock) <= (dns_server_alive_num() / 2 + dns_server_alive_num() % 2)) { request->rcode = DNS_RC_NOERROR; return -1; } } /* add this ip to request */ if (_dns_ip_address_check_add(request, cname, paddr, DNS_T_AAAA, 0, NULL) != 0) { return -1; } snprintf(ip, sizeof(ip), "[%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x]", paddr[0], paddr[1], paddr[2], paddr[3], paddr[4], paddr[5], paddr[6], paddr[7], paddr[8], paddr[9], paddr[10], paddr[11], paddr[12], paddr[13], paddr[14], paddr[15]); /* start ping */ _dns_server_request_get(request); if (_dns_server_check_speed(request, ip) != 0) { _dns_server_request_release(request); } } return 0; } static int _dns_server_process_answer(struct dns_request *request, const char *domain, struct dns_packet *packet, unsigned int result_flag) { int ttl = 0; char name[DNS_MAX_CNAME_LEN] = {0}; char cname[DNS_MAX_CNAME_LEN] = {0}; int rr_count = 0; int i = 0; int j = 0; struct dns_rrs *rrs = NULL; int ret = 0; if (packet->head.rcode != DNS_RC_NOERROR && packet->head.rcode != DNS_RC_NXDOMAIN) { if (request->rcode == DNS_RC_SERVFAIL) { request->rcode = packet->head.rcode; request->remote_server_fail = 1; } tlog(TLOG_DEBUG, "inquery failed, %s, rcode = %d, id = %d\n", domain, packet->head.rcode, packet->head.id); return -1; } request->remote_server_fail = 0; if (request->rcode == DNS_RC_SERVFAIL) { request->rcode = packet->head.rcode; } for (j = 1; j < DNS_RRS_OPT; j++) { rrs = dns_get_rrs_start(packet, j, &rr_count); for (i = 0; i < rr_count && rrs; i++, rrs = dns_get_rrs_next(packet, rrs)) { switch (rrs->type) { case DNS_T_A: { ret = _dns_server_process_answer_A(rrs, request, domain, cname, result_flag); if (ret == -1) { break; } else if (ret == -2) { continue; } else if (ret == -3) { return -1; } request->rcode = packet->head.rcode; } break; case DNS_T_AAAA: { ret = _dns_server_process_answer_AAAA(rrs, request, domain, cname, result_flag); if (ret == -1) { break; } else if (ret == -2) { continue; } else if (ret == -3) { return -1; } request->rcode = packet->head.rcode; } break; case DNS_T_NS: { char nsname[DNS_MAX_CNAME_LEN]; dns_get_CNAME(rrs, name, DNS_MAX_CNAME_LEN, &ttl, nsname, DNS_MAX_CNAME_LEN); tlog(TLOG_DEBUG, "NS: %s ttl: %d nsname: %s\n", name, ttl, nsname); } break; case DNS_T_CNAME: { char domain_name[DNS_MAX_CNAME_LEN] = {0}; char domain_cname[DNS_MAX_CNAME_LEN] = {0}; dns_get_CNAME(rrs, domain_name, DNS_MAX_CNAME_LEN, &ttl, domain_cname, DNS_MAX_CNAME_LEN); if (strncmp(domain_name, request->domain, DNS_MAX_CNAME_LEN - 1) != 0 && strncmp(domain_name, cname, DNS_MAX_CNAME_LEN - 1) != 0) { continue; } safe_strncpy(cname, domain_cname, DNS_MAX_CNAME_LEN); request->ttl_cname = _dns_server_get_conf_ttl(request, ttl); tlog(TLOG_DEBUG, "name: %s ttl: %d cname: %s\n", domain_name, ttl, cname); } break; case DNS_T_SOA: { /* if DNS64 enabled, skip check SOA. */ if (_dns_server_is_dns64_request(request)) { break; } request->has_soa = 1; if (request->rcode != DNS_RC_NOERROR) { request->rcode = packet->head.rcode; } dns_get_SOA(rrs, name, 128, &ttl, &request->soa); tlog(TLOG_DEBUG, "domain: %s, qtype: %d, SOA: mname: %s, rname: %s, serial: %d, refresh: %d, retry: %d, expire: " "%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); int soa_num = atomic_inc_return(&request->soa_num); if ((soa_num >= (dns_server_alive_num() / 3) + 1 || soa_num > 4) && atomic_read(&request->ip_map_num) <= 0) { request->ip_ttl = ttl; _dns_server_request_complete(request); } } break; default: tlog(TLOG_DEBUG, "%s, qtype: %d, rrstype = %d", name, rrs->type, j); break; } } } return 0; } static int _dns_server_passthrough_rule_check(struct dns_request *request, const char *domain, struct dns_packet *packet, unsigned int result_flag, int *pttl) { int ttl = 0; char name[DNS_MAX_CNAME_LEN] = {0}; char cname[DNS_MAX_CNAME_LEN]; int rr_count = 0; int i = 0; int j = 0; struct dns_rrs *rrs = NULL; int ip_check_result = 0; if (packet->head.rcode != DNS_RC_NOERROR && packet->head.rcode != DNS_RC_NXDOMAIN) { if (request->rcode == DNS_RC_SERVFAIL) { request->rcode = packet->head.rcode; request->remote_server_fail = 1; } tlog(TLOG_DEBUG, "inquery failed, %s, rcode = %d, id = %d\n", domain, packet->head.rcode, packet->head.id); return 0; } request->remote_server_fail = 0; if (request->rcode == DNS_RC_SERVFAIL) { request->rcode = packet->head.rcode; } for (j = 1; j < DNS_RRS_OPT; j++) { rrs = dns_get_rrs_start(packet, j, &rr_count); for (i = 0; i < rr_count && rrs; i++, rrs = dns_get_rrs_next(packet, rrs)) { switch (rrs->type) { case DNS_T_A: { unsigned char addr[4]; int ttl_tmp = 0; if (request->qtype != DNS_T_A) { /* ignore non-matched query type */ if (request->dualstack_selection == 0) { break; } } _dns_server_request_get(request); /* get A result */ dns_get_A(rrs, name, DNS_MAX_CNAME_LEN, &ttl_tmp, addr); /* if domain is not match */ if (strncmp(name, domain, DNS_MAX_CNAME_LEN) != 0 && strncmp(cname, name, DNS_MAX_CNAME_LEN) != 0) { _dns_server_request_release(request); continue; } tlog(TLOG_DEBUG, "domain: %s TTL: %d IP: %d.%d.%d.%d", name, ttl_tmp, addr[0], addr[1], addr[2], addr[3]); /* ip rule check */ ip_check_result = _dns_server_process_ip_rule(request, addr, 4, DNS_T_A, result_flag, NULL); if (ip_check_result == 0 || ip_check_result == -2 || ip_check_result == -3) { /* match, skip, nxdomain */ _dns_server_request_release(request); return 0; } /* Ad blocking result */ if (addr[0] == 0 || addr[0] == 127) { /* If half of the servers return the same result, then ignore this address */ if (atomic_read(&request->adblock) <= (dns_server_alive_num() / 2 + dns_server_alive_num() % 2)) { _dns_server_request_release(request); return 0; } } ttl = ttl_tmp; _dns_server_request_release(request); } break; case DNS_T_AAAA: { unsigned char addr[16]; int ttl_tmp = 0; if (request->qtype != DNS_T_AAAA) { /* ignore non-matched query type */ break; } _dns_server_request_get(request); dns_get_AAAA(rrs, name, DNS_MAX_CNAME_LEN, &ttl_tmp, addr); /* if domain is not match */ if (strncmp(name, domain, DNS_MAX_CNAME_LEN) != 0 && strncmp(cname, name, DNS_MAX_CNAME_LEN) != 0) { _dns_server_request_release(request); continue; } tlog(TLOG_DEBUG, "domain: %s TTL: %d IP: %.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x", name, ttl_tmp, 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]); ip_check_result = _dns_server_process_ip_rule(request, addr, 16, DNS_T_AAAA, result_flag, NULL); if (ip_check_result == 0 || ip_check_result == -2 || ip_check_result == -3) { /* match, skip, nxdomain */ _dns_server_request_release(request); return 0; } /* Ad blocking result */ if (_dns_server_is_adblock_ipv6(addr) == 0) { /* If half of the servers return the same result, then ignore this address */ if (atomic_read(&request->adblock) <= (dns_server_alive_num() / 2 + dns_server_alive_num() % 2)) { _dns_server_request_release(request); return 0; } } ttl = ttl_tmp; _dns_server_request_release(request); } break; case DNS_T_CNAME: { dns_get_CNAME(rrs, name, DNS_MAX_CNAME_LEN, &ttl, cname, DNS_MAX_CNAME_LEN); } break; default: if (ttl == 0) { /* Get TTL */ char tmpname[DNS_MAX_CNAME_LEN]; char tmpbuf[DNS_MAX_CNAME_LEN]; dns_get_CNAME(rrs, tmpname, DNS_MAX_CNAME_LEN, &ttl, tmpbuf, DNS_MAX_CNAME_LEN); if (request->ip_ttl == 0) { request->ip_ttl = ttl; } } break; } } } *pttl = ttl; return -1; } static int _dns_server_get_answer(struct dns_server_post_context *context) { int i = 0; int j = 0; int ttl = 0; struct dns_rrs *rrs = NULL; int rr_count = 0; struct dns_request *request = context->request; struct dns_packet *packet = context->packet; for (j = 1; j < DNS_RRS_OPT; j++) { rrs = dns_get_rrs_start(packet, j, &rr_count); for (i = 0; i < rr_count && rrs; i++, rrs = dns_get_rrs_next(packet, rrs)) { switch (rrs->type) { case DNS_T_A: { unsigned char addr[4]; char name[DNS_MAX_CNAME_LEN] = {0}; struct dns_ip_address *addr_map = NULL; if (request->qtype != DNS_T_A) { continue; } /* get A result */ dns_get_A(rrs, name, DNS_MAX_CNAME_LEN, &ttl, addr); if (strncmp(name, request->domain, DNS_MAX_CNAME_LEN - 1) != 0 && strncmp(name, request->cname, DNS_MAX_CNAME_LEN - 1) != 0) { continue; } if (context->no_check_add_ip == 0 && _dns_ip_address_check_add(request, name, addr, DNS_T_A, request->ping_time, &addr_map) != 0) { continue; } _dns_server_context_add_ip(context, addr_map->ip_addr); if (request->has_ip == 1) { continue; } memcpy(request->ip_addr, addr, DNS_RR_A_LEN); /* add this ip to request */ request->ip_ttl = _dns_server_get_conf_ttl(request, ttl); request->has_ip = 1; request->rcode = packet->head.rcode; } break; case DNS_T_AAAA: { unsigned char addr[16]; char name[DNS_MAX_CNAME_LEN] = {0}; struct dns_ip_address *addr_map = NULL; if (request->qtype != DNS_T_AAAA) { /* ignore non-matched query type */ continue; } dns_get_AAAA(rrs, name, DNS_MAX_CNAME_LEN, &ttl, addr); if (strncmp(name, request->domain, DNS_MAX_CNAME_LEN - 1) != 0 && strncmp(name, request->cname, DNS_MAX_CNAME_LEN - 1) != 0) { continue; } if (context->no_check_add_ip == 0 && _dns_ip_address_check_add(request, name, addr, DNS_T_AAAA, request->ping_time, &addr_map) != 0) { continue; } _dns_server_context_add_ip(context, addr_map->ip_addr); if (request->has_ip == 1) { continue; } memcpy(request->ip_addr, addr, DNS_RR_AAAA_LEN); request->ip_ttl = _dns_server_get_conf_ttl(request, ttl); request->has_ip = 1; request->rcode = packet->head.rcode; } break; case DNS_T_NS: { char cname[DNS_MAX_CNAME_LEN]; char name[DNS_MAX_CNAME_LEN] = {0}; dns_get_CNAME(rrs, name, DNS_MAX_CNAME_LEN, &ttl, cname, DNS_MAX_CNAME_LEN); tlog(TLOG_DEBUG, "NS: %s, ttl: %d, cname: %s\n", name, ttl, cname); } break; case DNS_T_CNAME: { char cname[DNS_MAX_CNAME_LEN]; char name[DNS_MAX_CNAME_LEN] = {0}; if (dns_conf_force_no_cname) { continue; } dns_get_CNAME(rrs, name, DNS_MAX_CNAME_LEN, &ttl, cname, DNS_MAX_CNAME_LEN); tlog(TLOG_DEBUG, "name: %s, ttl: %d, cname: %s\n", name, ttl, cname); if (strncmp(name, request->domain, DNS_MAX_CNAME_LEN - 1) != 0 && strncmp(name, request->cname, DNS_MAX_CNAME_LEN - 1) != 0) { continue; } safe_strncpy(request->cname, cname, DNS_MAX_CNAME_LEN); request->ttl_cname = _dns_server_get_conf_ttl(request, ttl); request->has_cname = 1; } break; case DNS_T_SOA: { char name[DNS_MAX_CNAME_LEN] = {0}; request->has_soa = 1; if (request->rcode != DNS_RC_NOERROR) { request->rcode = packet->head.rcode; } dns_get_SOA(rrs, name, 128, &ttl, &request->soa); tlog(TLOG_DEBUG, "domain: %s, qtype: %d, SOA: mname: %s, rname: %s, serial: %d, refresh: %d, retry: %d, expire: " "%d, minimum: %d", request->domain, request->qtype, request->soa.mname, request->soa.rname, request->soa.serial, request->soa.refresh, request->soa.retry, request->soa.expire, request->soa.minimum); } break; default: break; } } } return 0; } static int _dns_server_reply_passthrough(struct dns_server_post_context *context) { struct dns_request *request = context->request; if (atomic_inc_return(&request->notified) != 1) { return 0; } _dns_server_get_answer(context); _dns_cache_reply_packet(context); if (_dns_server_setup_ipset_nftset_packet(context) != 0) { tlog(TLOG_DEBUG, "setup ipset failed."); } _dns_result_callback(context); _dns_server_audit_log(context); /* reply child request */ _dns_result_child_post(context); if (request->conn && context->do_reply == 1) { char clientip[DNS_MAX_CNAME_LEN] = {0}; /* When passthrough, modify the id to be the id of the client request. */ int ret = _dns_request_update_id_ttl(context); if (ret != 0) { tlog(TLOG_ERROR, "update packet ttl failed."); return -1; } _dns_reply_inpacket(request, context->inpacket, context->inpacket_len); tlog(TLOG_INFO, "result: %s, client: %s, qtype: %d, id: %d, group: %s, time: %lums", request->domain, get_host_by_addr(clientip, sizeof(clientip), (struct sockaddr *)&request->addr), request->qtype, request->id, request->dns_group_name[0] != '\0' ? request->dns_group_name : "default", get_tick_count() - request->send_tick); } return _dns_server_reply_all_pending_list(request, context); } 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); request_wait = request->request_wait; request->request_wait--; pthread_mutex_unlock(&request->ip_map_lock); /* Not need to wait check result if only has one ip address */ if (ip_num <= 1 && request_wait == 1) { if (request->dualstack_selection_query == 1) { if ((dns_conf_ipset_no_speed.ipv4_enable || dns_conf_nftset_no_speed.ip_enable || dns_conf_ipset_no_speed.ipv6_enable || dns_conf_nftset_no_speed.ip6_enable) && dns_conf_dns_dns64.prefix_len == 0) { /* if speed check fail enabled, we need reply quickly, otherwise wait for ping result.*/ _dns_server_request_complete(request); } goto out; } if (request->dualstack_selection_has_ip && request->dualstack_selection_ping_time > 0) { goto out; } request->has_ping_result = 1; _dns_server_request_complete(request); } out: _dns_server_request_release(request); } static int dns_server_dualstack_callback(const struct dns_result *result, void *user_ptr) { struct dns_request *request = (struct dns_request *)user_ptr; tlog(TLOG_DEBUG, "dualstack result: domain: %s, ip: %s, type: %d, ping: %d, rcode: %d", result->domain, result->ip, result->addr_type, result->ping_time, result->rtcode); if (request == NULL) { return -1; } if (result->rtcode == DNS_RC_NOERROR && result->ip[0] != 0) { request->dualstack_selection_has_ip = 1; } request->dualstack_selection_ping_time = result->ping_time; _dns_server_query_end(request); return 0; } static void _dns_server_passthrough_may_complete(struct dns_request *request) { const unsigned char *addr; if (request->passthrough != 2) { return; } if (request->has_ip == 0 && request->has_soa == 0) { return; } if (request->qtype == DNS_T_A && request->has_ip == 1) { /* Ad blocking result */ addr = request->ip_addr; if (addr[0] == 0 || addr[0] == 127) { /* If half of the servers return the same result, then ignore this address */ if (atomic_read(&request->adblock) <= (dns_server_alive_num() / 2 + dns_server_alive_num() % 2)) { return; } } } if (request->qtype == DNS_T_AAAA && request->has_ip == 1) { addr = request->ip_addr; if (_dns_server_is_adblock_ipv6(addr) == 0) { /* If half of the servers return the same result, then ignore this address */ if (atomic_read(&request->adblock) <= (dns_server_alive_num() / 2 + dns_server_alive_num() % 2)) { return; } } } _dns_server_request_complete_with_all_IPs(request, 1); } static int dns_server_resolve_callback(const char *domain, dns_result_type rtype, struct dns_server_info *server_info, struct dns_packet *packet, unsigned char *inpacket, int inpacket_len, void *user_ptr) { struct dns_request *request = user_ptr; int ret = 0; unsigned long result_flag = dns_client_server_result_flag(server_info); if (request == NULL) { return -1; } if (rtype == DNS_QUERY_RESULT) { tlog(TLOG_DEBUG, "query result from server %s:%d, type: %d", dns_client_get_server_ip(server_info), dns_client_get_server_port(server_info), dns_client_get_server_type(server_info)); if (request->passthrough == 1 && atomic_read(&request->notified) == 0) { struct dns_server_post_context context; int ttl = 0; ret = _dns_server_passthrough_rule_check(request, domain, packet, result_flag, &ttl); if (ret == 0) { return 0; } ttl = _dns_server_get_conf_ttl(request, ttl); _dns_server_post_context_init_from(&context, request, packet, inpacket, inpacket_len); context.do_cache = 1; context.do_audit = 1; context.do_reply = 1; context.do_ipset = 1; context.reply_ttl = ttl; return _dns_server_reply_passthrough(&context); } if (request->prefetch == 0 && request->response_mode == DNS_RESPONSE_MODE_FASTEST_RESPONSE && atomic_read(&request->notified) == 0) { struct dns_server_post_context context; int ttl = 0; ret = _dns_server_passthrough_rule_check(request, domain, packet, result_flag, &ttl); if (ret != 0) { _dns_server_post_context_init_from(&context, request, packet, inpacket, inpacket_len); context.do_cache = 1; context.do_audit = 1; context.do_reply = 1; context.do_ipset = 1; context.reply_ttl = _dns_server_get_reply_ttl(request, ttl); context.cache_ttl = _dns_server_get_conf_ttl(request, ttl); request->ip_ttl = context.cache_ttl; context.no_check_add_ip = 1; _dns_server_reply_passthrough(&context); request->cname[0] = 0; request->has_ip = 0; request->has_cname = 0; request->has_ping_result = 0; request->has_soa = 0; request->has_ptr = 0; request->ping_time = -1; request->ip_ttl = 0; } } _dns_server_process_answer(request, domain, packet, result_flag); _dns_server_passthrough_may_complete(request); return 0; } else if (rtype == DNS_QUERY_ERR) { tlog(TLOG_ERROR, "request failed, %s", domain); return -1; } else { _dns_server_query_end(request); } return 0; } static int _dns_server_get_inet_by_addr(struct sockaddr_storage *localaddr, struct sockaddr_storage *addr, int family) { struct ifaddrs *ifaddr = NULL; struct ifaddrs *ifa = NULL; char ethname[16] = {0}; if (getifaddrs(&ifaddr) == -1) { return -1; } for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { if (ifa->ifa_addr == NULL) { continue; } if (localaddr->ss_family != ifa->ifa_addr->sa_family) { continue; } switch (ifa->ifa_addr->sa_family) { case AF_INET: { struct sockaddr_in *addr_in_1 = NULL; struct sockaddr_in *addr_in_2 = NULL; addr_in_1 = (struct sockaddr_in *)ifa->ifa_addr; addr_in_2 = (struct sockaddr_in *)localaddr; if (memcmp(&(addr_in_1->sin_addr.s_addr), &(addr_in_2->sin_addr.s_addr), 4) != 0) { continue; } } break; case AF_INET6: { struct sockaddr_in6 *addr_in6_1 = NULL; struct sockaddr_in6 *addr_in6_2 = NULL; addr_in6_1 = (struct sockaddr_in6 *)ifa->ifa_addr; addr_in6_2 = (struct sockaddr_in6 *)localaddr; if (IN6_IS_ADDR_V4MAPPED(&addr_in6_1->sin6_addr)) { unsigned char *addr1 = addr_in6_1->sin6_addr.s6_addr + 12; unsigned char *addr2 = addr_in6_2->sin6_addr.s6_addr + 12; if (memcmp(addr1, addr2, 4) != 0) { continue; } } else { unsigned char *addr1 = addr_in6_1->sin6_addr.s6_addr; unsigned char *addr2 = addr_in6_2->sin6_addr.s6_addr; if (memcmp(addr1, addr2, 16) != 0) { continue; } } } break; default: continue; break; } safe_strncpy(ethname, ifa->ifa_name, sizeof(ethname)); break; } if (ethname[0] == '\0') { goto errout; } for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { if (ifa->ifa_addr == NULL) { continue; } if (ifa->ifa_addr->sa_family != family) { continue; } if (strncmp(ethname, ifa->ifa_name, sizeof(ethname)) != 0) { continue; } if (family == AF_INET) { memcpy(addr, ifa->ifa_addr, sizeof(struct sockaddr_in)); } else if (family == AF_INET6) { memcpy(addr, ifa->ifa_addr, sizeof(struct sockaddr_in6)); } break; } if (ifa == NULL) { goto errout; } freeifaddrs(ifaddr); return 0; errout: if (ifaddr) { freeifaddrs(ifaddr); } return -1; } static int _dns_server_reply_request_eth_ip(struct dns_request *request) { struct sockaddr_in *addr_in = NULL; struct sockaddr_in6 *addr_in6 = NULL; struct sockaddr_storage *localaddr = NULL; struct sockaddr_storage localaddr_buff; localaddr = &request->localaddr; /* address /domain/ rule */ switch (request->qtype) { case DNS_T_A: if (localaddr->ss_family != AF_INET) { if (_dns_server_get_inet_by_addr(localaddr, &localaddr_buff, AF_INET) != 0) { _dns_server_reply_SOA(DNS_RC_NOERROR, request); return 0; } localaddr = &localaddr_buff; } addr_in = (struct sockaddr_in *)localaddr; memcpy(request->ip_addr, &addr_in->sin_addr.s_addr, DNS_RR_A_LEN); break; case DNS_T_AAAA: if (localaddr->ss_family != AF_INET6) { if (_dns_server_get_inet_by_addr(localaddr, &localaddr_buff, AF_INET6) != 0) { _dns_server_reply_SOA(DNS_RC_NOERROR, request); return 0; } localaddr = &localaddr_buff; } addr_in6 = (struct sockaddr_in6 *)localaddr; memcpy(request->ip_addr, &addr_in6->sin6_addr.s6_addr, DNS_RR_AAAA_LEN); break; default: goto out; break; } request->rcode = DNS_RC_NOERROR; request->ip_ttl = dns_conf_local_ttl; request->has_ip = 1; struct dns_server_post_context context; _dns_server_post_context_init(&context, request); context.do_reply = 1; _dns_request_post(&context); return 0; out: return -1; } static int _dns_server_process_ptrs(struct dns_request *request) { uint32_t key = 0; struct dns_ptr *ptr = NULL; struct dns_ptr *ptr_tmp = NULL; key = hash_string(request->domain); hash_for_each_possible(dns_ptr_table.ptr, ptr_tmp, node, key) { if (strncmp(ptr_tmp->ptr_domain, request->domain, DNS_MAX_PTR_LEN) != 0) { continue; } ptr = ptr_tmp; break; } if (ptr == NULL) { goto errout; } request->has_ptr = 1; safe_strncpy(request->ptr_hostname, ptr->hostname, DNS_MAX_CNAME_LEN); return 0; errout: return -1; } static int _dns_server_process_local_ptr(struct dns_request *request) { struct ifaddrs *ifaddr = NULL; struct ifaddrs *ifa = NULL; unsigned char *addr = NULL; char reverse_addr[DNS_MAX_CNAME_LEN] = {0}; int found = 0; if (getifaddrs(&ifaddr) == -1) { return -1; } /* Get the NIC IP and match it. If the match is successful, return the host name. */ for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { if (ifa->ifa_addr == NULL) { continue; } switch (ifa->ifa_addr->sa_family) { case AF_INET: { struct sockaddr_in *addr_in = NULL; addr_in = (struct sockaddr_in *)ifa->ifa_addr; addr = (unsigned char *)&(addr_in->sin_addr.s_addr); snprintf(reverse_addr, sizeof(reverse_addr), "%d.%d.%d.%d.in-addr.arpa", addr[3], addr[2], addr[1], addr[0]); } break; case AF_INET6: { struct sockaddr_in6 *addr_in6 = NULL; addr_in6 = (struct sockaddr_in6 *)ifa->ifa_addr; if (IN6_IS_ADDR_V4MAPPED(&addr_in6->sin6_addr)) { addr = addr_in6->sin6_addr.s6_addr + 12; snprintf(reverse_addr, sizeof(reverse_addr), "%d.%d.%d.%d.in-addr.arpa", addr[3], addr[2], addr[1], addr[0]); } else { addr = addr_in6->sin6_addr.s6_addr; snprintf(reverse_addr, sizeof(reverse_addr), "%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x." "%x.ip6.arpa", addr[15] & 0xF, (addr[15] >> 4) & 0xF, addr[14] & 0xF, (addr[14] >> 4) & 0xF, addr[13] & 0xF, (addr[13] >> 4) & 0xF, addr[12] & 0xF, (addr[12] >> 4) & 0xF, addr[11] & 0xF, (addr[11] >> 4) & 0xF, addr[10] & 0xF, (addr[10] >> 4) & 0xF, addr[9] & 0xF, (addr[9] >> 4) & 0xF, addr[8] & 0xF, (addr[8] >> 4) & 0xF, addr[7] & 0xF, (addr[7] >> 4) & 0xF, addr[6] & 0xF, (addr[6] >> 4) & 0xF, addr[5] & 0xF, (addr[5] >> 4) & 0xF, addr[4] & 0xF, (addr[4] >> 4) & 0xF, addr[3] & 0xF, (addr[3] >> 4) & 0xF, addr[2] & 0xF, (addr[2] >> 4) & 0xF, addr[1] & 0xF, (addr[1] >> 4) & 0xF, addr[0] & 0xF, (addr[0] >> 4) & 0xF); } } break; default: continue; break; } if (strncmp(request->domain, reverse_addr, DNS_MAX_CNAME_LEN) == 0) { found = 1; break; } } /* Determine if the smartdns service is in effect. */ if (strncmp(request->domain, "0.0.0.0.in-addr.arpa", DNS_MAX_CNAME_LEN - 1) == 0) { found = 1; } /* Determine if the smartdns service is in effect. */ if (found == 0 && strncmp(request->domain, "smartdns", sizeof("smartdns")) == 0) { found = 1; } if (found == 0) { goto errout; } char full_hostname[DNS_MAX_CNAME_LEN]; if (dns_conf_server_name[0] == 0) { char hostname[DNS_MAX_CNAME_LEN]; char domainname[DNS_MAX_CNAME_LEN]; /* get local domain name */ if (getdomainname(domainname, DNS_MAX_CNAME_LEN - 1) == 0) { /* check domain is valid */ if (strncmp(domainname, "(none)", DNS_MAX_CNAME_LEN - 1) == 0) { domainname[0] = '\0'; } } if (gethostname(hostname, DNS_MAX_CNAME_LEN - 1) == 0) { /* check hostname is valid */ if (strncmp(hostname, "(none)", DNS_MAX_CNAME_LEN - 1) == 0) { hostname[0] = '\0'; } } if (hostname[0] != '\0' && domainname[0] != '\0') { snprintf(full_hostname, sizeof(full_hostname), "%.64s.%.128s", hostname, domainname); } else if (hostname[0] != '\0') { safe_strncpy(full_hostname, hostname, DNS_MAX_CNAME_LEN); } else { safe_strncpy(full_hostname, "smartdns", DNS_MAX_CNAME_LEN); } } else { /* return configured server name */ safe_strncpy(full_hostname, dns_conf_server_name, DNS_MAX_CNAME_LEN); } request->has_ptr = 1; safe_strncpy(request->ptr_hostname, full_hostname, DNS_MAX_CNAME_LEN); freeifaddrs(ifaddr); return 0; errout: if (ifaddr) { freeifaddrs(ifaddr); } return -1; } static int _dns_server_get_local_ttl(struct dns_request *request) { struct dns_ttl_rule *ttl_rule; /* get domain rule flag */ ttl_rule = _dns_server_get_dns_rule(request, DOMAIN_RULE_TTL); if (ttl_rule != NULL) { if (ttl_rule->ttl > 0) { return ttl_rule->ttl; } } if (dns_conf_local_ttl > 0) { return dns_conf_local_ttl; } if (dns_conf_rr_ttl > 0) { return dns_conf_rr_ttl; } if (dns_conf_rr_ttl_min > 0) { return dns_conf_rr_ttl_min; } return DNS_SERVER_ADDR_TTL; } static int _dns_server_process_private_ptr(struct dns_request *request) { int a, b, c, d; int ret = sscanf(request->domain, "%d.%d.%d.%d.in-addr.arpa", &a, &b, &c, &d); if (ret != 4) { return -1; } if (d == 10 || (d == 172 && c >= 16 && c <= 31) || (d == 192 && c == 168)) { request->has_soa = 1; _dns_server_setup_soa(request); return 0; } return -1; } static int _dns_server_process_ptr(struct dns_request *request) { if (_dns_server_process_ptrs(request) == 0) { goto reply_exit; } if (_dns_server_process_local_ptr(request) == 0) { goto reply_exit; } if (_dns_server_process_private_ptr(request) == 0) { goto reply_exit; } return -1; reply_exit: request->rcode = DNS_RC_NOERROR; request->ip_ttl = _dns_server_get_local_ttl(request); struct dns_server_post_context context; _dns_server_post_context_init(&context, request); context.do_reply = 1; context.do_audit = 0; context.do_cache = 1; _dns_request_post(&context); return 0; } static int _dns_server_process_DDR(struct dns_request *request) { return _dns_server_reply_SOA(DNS_RC_NOERROR, request); } static int _dns_server_process_srv(struct dns_request *request) { if (strncmp("_dns.resolver.arpa", request->domain, DNS_MAX_CNAME_LEN) == 0) { return _dns_server_process_DDR(request); } return -1; } static void _dns_server_log_rule(const char *domain, enum domain_rule rule_type, unsigned char *rule_key, int rule_key_len) { char rule_name[DNS_MAX_CNAME_LEN]; if (rule_key_len <= 0) { return; } reverse_string(rule_name, (char *)rule_key, rule_key_len, 1); rule_name[rule_key_len] = 0; tlog(TLOG_INFO, "RULE-MATCH, type: %d, domain: %s, rule: %s", rule_type, domain, rule_name); } static void _dns_server_update_rule_by_flags(struct dns_request *request) { struct dns_rule_flags *rule_flag = (struct dns_rule_flags *)request->domain_rule.rules[0]; unsigned int flags = 0; if (rule_flag == NULL) { return; } flags = rule_flag->flags; if (flags & DOMAIN_FLAG_ADDR_IGN) { request->domain_rule.rules[DOMAIN_RULE_ADDRESS_IPV4] = NULL; request->domain_rule.rules[DOMAIN_RULE_ADDRESS_IPV6] = NULL; } if (flags & DOMAIN_FLAG_ADDR_IPV4_IGN) { request->domain_rule.rules[DOMAIN_RULE_ADDRESS_IPV4] = NULL; } if (flags & DOMAIN_FLAG_ADDR_IPV6_IGN) { request->domain_rule.rules[DOMAIN_RULE_ADDRESS_IPV6] = NULL; } if (flags & DOMAIN_FLAG_IPSET_IGN) { request->domain_rule.rules[DOMAIN_RULE_IPSET] = NULL; } if (flags & DOMAIN_FLAG_IPSET_IPV4_IGN) { request->domain_rule.rules[DOMAIN_RULE_IPSET_IPV4] = NULL; } if (flags & DOMAIN_FLAG_IPSET_IPV6_IGN) { request->domain_rule.rules[DOMAIN_RULE_IPSET_IPV6] = NULL; } if (flags & DOMAIN_FLAG_NFTSET_IP_IGN || flags & DOMAIN_FLAG_NFTSET_INET_IGN) { request->domain_rule.rules[DOMAIN_RULE_NFTSET_IP] = NULL; } if (flags & DOMAIN_FLAG_NFTSET_IP6_IGN || flags & DOMAIN_FLAG_NFTSET_INET_IGN) { request->domain_rule.rules[DOMAIN_RULE_NFTSET_IP6] = NULL; } if (flags & DOMAIN_FLAG_NAMESERVER_IGNORE) { request->domain_rule.rules[DOMAIN_RULE_NAMESERVER] = NULL; } } static int _dns_server_get_rules(unsigned char *key, uint32_t key_len, int is_subkey, void *value, void *arg) { struct rule_walk_args *walk_args = arg; struct dns_request *request = walk_args->args; struct dns_domain_rule *domain_rule = value; int i = 0; if (domain_rule == NULL) { return 0; } if (domain_rule->sub_rule_only != domain_rule->root_rule_only) { /* only subkey rule */ if (domain_rule->sub_rule_only == 1 && is_subkey == 0) { return 0; } /* only root key rule */ if (domain_rule->root_rule_only == 1 && is_subkey == 1) { return 0; } } for (i = 0; i < DOMAIN_RULE_MAX; i++) { if (domain_rule->rules[i] == NULL) { continue; } request->domain_rule.rules[i] = domain_rule->rules[i]; request->domain_rule.is_sub_rule[i] = is_subkey; walk_args->key[i] = key; walk_args->key_len[i] = key_len; } /* update rules by flags */ _dns_server_update_rule_by_flags(request); return 0; } static void _dns_server_get_domain_rule_by_domain(struct dns_request *request, const char *domain, int out_log) { int domain_len = 0; char domain_key[DNS_MAX_CNAME_LEN]; int matched_key_len = DNS_MAX_CNAME_LEN; unsigned char matched_key[DNS_MAX_CNAME_LEN]; struct rule_walk_args walk_args; int i = 0; if (request->skip_domain_rule != 0) { return; } memset(&walk_args, 0, sizeof(walk_args)); walk_args.args = request; /* reverse domain string */ domain_len = strlen(domain); if (domain_len >= (int)sizeof(domain_key) - 2) { return; } reverse_string(domain_key, domain, domain_len, 1); domain_key[domain_len] = '.'; domain_len++; domain_key[domain_len] = 0; /* find domain rule */ art_substring_walk(&dns_conf_domain_rule, (unsigned char *)domain_key, domain_len, _dns_server_get_rules, &walk_args); if (likely(dns_conf_log_level > TLOG_DEBUG)) { return; } /* output log rule */ for (i = 0; i < DOMAIN_RULE_MAX; i++) { if (walk_args.key[i] == NULL) { continue; } matched_key_len = walk_args.key_len[i]; if (walk_args.key_len[i] >= sizeof(matched_key)) { continue; } memcpy(matched_key, walk_args.key[i], walk_args.key_len[i]); matched_key_len--; matched_key[matched_key_len] = 0; if (out_log != 0) { _dns_server_log_rule(request->domain, i, matched_key, matched_key_len); } } request->skip_domain_rule = 1; } static void _dns_server_get_domain_rule(struct dns_request *request) { _dns_server_get_domain_rule_by_domain(request, request->domain, 1); } static int _dns_server_pre_process_server_flags(struct dns_request *request) { if (_dns_server_has_bind_flag(request, BIND_FLAG_NO_CACHE) == 0) { request->no_cache = 1; } if (_dns_server_has_bind_flag(request, BIND_FLAG_NO_IP_ALIAS) == 0) { request->no_ipalias = 1; } return -1; } static int _dns_server_pre_process_rule_flags(struct dns_request *request) { struct dns_rule_flags *rule_flag = NULL; unsigned int flags = 0; int rcode = DNS_RC_NOERROR; /* get domain rule flag */ rule_flag = _dns_server_get_dns_rule(request, DOMAIN_RULE_FLAGS); if (rule_flag == NULL) { if (_dns_server_is_return_soa(request)) { goto soa; } goto out; } flags = rule_flag->flags; if (flags & DOMAIN_FLAG_NO_SERVE_EXPIRED) { request->no_serve_expired = 1; } if (flags & DOMAIN_FLAG_NO_CACHE) { request->no_cache = 1; } if (flags & DOMAIN_FLAG_NO_IPALIAS) { request->no_ipalias = 1; } if (flags & DOMAIN_FLAG_ADDR_IGN) { /* ignore this domain */ goto skip_soa_out; } /* return specific type of address */ switch (request->qtype) { case DNS_T_A: if (flags & DOMAIN_FLAG_ADDR_IPV4_IGN) { /* ignore this domain for A request */ goto skip_soa_out; } if (request->domain_rule.rules[DOMAIN_RULE_ADDRESS_IPV4] != NULL) { goto skip_soa_out; } if (_dns_server_is_return_soa(request)) { /* return SOA for A request */ if (_dns_server_is_return_soa_qtype(request, DNS_T_AAAA)) { rcode = DNS_RC_NXDOMAIN; } goto soa; } break; case DNS_T_AAAA: if (flags & DOMAIN_FLAG_ADDR_IPV6_IGN) { /* ignore this domain for A request */ goto skip_soa_out; } if (request->domain_rule.rules[DOMAIN_RULE_ADDRESS_IPV6] != NULL) { goto skip_soa_out; } if (_dns_server_is_return_soa(request)) { /* return SOA for A request */ if (_dns_server_is_return_soa_qtype(request, DNS_T_A)) { rcode = DNS_RC_NXDOMAIN; } goto soa; } if (flags & DOMAIN_FLAG_ADDR_IPV4_SOA && request->dualstack_selection) { /* if IPV4 return SOA and dualstack-selection enabled, set request dualstack disable */ request->dualstack_selection = 0; } break; default: goto out; break; } if (_dns_server_is_return_soa(request)) { goto soa; } skip_soa_out: request->skip_qtype_soa = 1; out: return -1; soa: /* return SOA */ _dns_server_reply_SOA(rcode, request); return 0; } static int _dns_server_address_generate_order(int orders[], int order_num, int max_order_count) { int i = 0; int j = 0; int k = 0; unsigned int seed = time(NULL); for (i = 0; i < order_num && i < max_order_count; i++) { orders[i] = i; } for (i = 0; i < order_num && max_order_count; i++) { k = rand_r(&seed) % order_num; j = rand_r(&seed) % order_num; if (j == k) { continue; } int temp = orders[j]; orders[j] = orders[k]; orders[k] = temp; } return 0; } static int _dns_server_process_address(struct dns_request *request) { struct dns_rule_address_IPV4 *address_ipv4 = NULL; struct dns_rule_address_IPV6 *address_ipv6 = NULL; int orders[DNS_MAX_REPLY_IP_NUM]; if (_dns_server_has_bind_flag(request, BIND_FLAG_NO_RULE_ADDR) == 0) { goto errout; } /* address /domain/ rule */ switch (request->qtype) { case DNS_T_A: if (request->domain_rule.rules[DOMAIN_RULE_ADDRESS_IPV4] == NULL) { goto errout; } address_ipv4 = _dns_server_get_dns_rule(request, DOMAIN_RULE_ADDRESS_IPV4); if (address_ipv4 == NULL) { goto errout; } _dns_server_address_generate_order(orders, address_ipv4->addr_num, DNS_MAX_REPLY_IP_NUM); memcpy(request->ip_addr, address_ipv4->ipv4_addr[orders[0]], DNS_RR_A_LEN); for (int i = 1; i < address_ipv4->addr_num; i++) { int index = orders[i]; if (index >= address_ipv4->addr_num) { continue; } _dns_ip_address_check_add(request, request->cname, address_ipv4->ipv4_addr[index], DNS_T_A, 1, NULL); } break; case DNS_T_AAAA: if (request->domain_rule.rules[DOMAIN_RULE_ADDRESS_IPV6] == NULL) { goto errout; } address_ipv6 = _dns_server_get_dns_rule(request, DOMAIN_RULE_ADDRESS_IPV6); if (address_ipv6 == NULL) { goto errout; } _dns_server_address_generate_order(orders, address_ipv6->addr_num, DNS_MAX_REPLY_IP_NUM); memcpy(request->ip_addr, address_ipv6->ipv6_addr[orders[0]], DNS_RR_AAAA_LEN); for (int i = 1; i < address_ipv6->addr_num; i++) { int index = orders[i]; if (index >= address_ipv6->addr_num) { continue; } _dns_ip_address_check_add(request, request->cname, address_ipv6->ipv6_addr[index], DNS_T_AAAA, 1, NULL); } break; default: goto errout; break; } request->rcode = DNS_RC_NOERROR; request->ip_ttl = _dns_server_get_local_ttl(request); request->has_ip = 1; struct dns_server_post_context context; _dns_server_post_context_init(&context, request); context.do_reply = 1; context.do_audit = 1; context.do_ipset = 1; context.select_all_best_ip = 1; _dns_request_post(&context); return 0; errout: return -1; } static struct dns_request *_dns_server_new_child_request(struct dns_request *request, const char *domain, dns_type_t qtype, child_request_callback child_callback) { struct dns_request *child_request = NULL; child_request = _dns_server_new_request(); if (child_request == NULL) { tlog(TLOG_ERROR, "malloc failed.\n"); goto errout; } child_request->server_flags = request->server_flags; safe_strncpy(child_request->dns_group_name, request->dns_group_name, sizeof(request->dns_group_name)); safe_strncpy(child_request->domain, domain, sizeof(child_request->domain)); child_request->prefetch = request->prefetch; child_request->prefetch_flags = request->prefetch_flags; child_request->child_callback = child_callback; child_request->parent_request = request; child_request->qtype = qtype; child_request->qclass = request->qclass; 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; _dns_server_get_domain_rule(child_request); return child_request; errout: if (child_request) { _dns_server_request_release(child_request); } return NULL; } static int _dns_server_request_copy(struct dns_request *request, struct dns_request *from) { unsigned long bucket = 0; struct dns_ip_address *addr_map = NULL; struct hlist_node *tmp = NULL; uint32_t key = 0; int addr_len = 0; request->rcode = from->rcode; if (from->has_ip) { request->has_ip = 1; request->ip_ttl = _dns_server_get_conf_ttl(request, from->ip_ttl); request->ping_time = from->ping_time; memcpy(request->ip_addr, from->ip_addr, sizeof(request->ip_addr)); } if (from->has_cname) { request->has_cname = 1; request->ttl_cname = from->ttl_cname; safe_strncpy(request->cname, from->cname, sizeof(request->cname)); } if (from->has_soa) { request->has_soa = 1; memcpy(&request->soa, &from->soa, sizeof(request->soa)); } 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(&from->ip_map_lock); hash_for_each_safe(from->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 if (addr_map->addr_type == DNS_T_AAAA) { addr_len = DNS_RR_AAAA_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(&from->ip_map_lock); return -1; } memcpy(new_addr_map, addr_map, sizeof(struct dns_ip_address)); new_addr_map->ping_time = addr_map->ping_time; key = jhash(new_addr_map->ip_addr, addr_len, 0); key = jhash(&addr_map->addr_type, sizeof(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(&from->ip_map_lock); return 0; } 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); if (child_request->rcode == DNS_RC_NOERROR && dns_conf_force_no_cname == 0 && child_request->has_soa == 0) { safe_strncpy(request->cname, child_request->domain, sizeof(request->cname)); request->has_cname = 1; request->ttl_cname = _dns_server_get_conf_ttl(request, child_request->ip_ttl); } return DNS_CHILD_POST_SUCCESS; } static int _dns_server_process_cname_pre(struct dns_request *request) { struct dns_cname_rule *cname = NULL; struct dns_rule_flags *rule_flag = NULL; struct dns_request_domain_rule domain_rule; if (_dns_server_has_bind_flag(request, BIND_FLAG_NO_RULE_CNAME) == 0) { return 0; } if (request->has_cname_loop == 1) { return 0; } /* get domain rule flag */ rule_flag = _dns_server_get_dns_rule(request, DOMAIN_RULE_FLAGS); if (rule_flag != NULL) { if (rule_flag->flags & DOMAIN_FLAG_CNAME_IGN) { return 0; } } cname = _dns_server_get_dns_rule(request, DOMAIN_RULE_CNAME); if (cname == NULL) { return 0; } request->skip_domain_rule = 0; /* copy child rules */ memcpy(&domain_rule, &request->domain_rule, sizeof(domain_rule)); memset(&request->domain_rule, 0, sizeof(request->domain_rule)); _dns_server_get_domain_rule_by_domain(request, cname->cname, 0); request->domain_rule.rules[DOMAIN_RULE_CNAME] = domain_rule.rules[DOMAIN_RULE_CNAME]; request->domain_rule.is_sub_rule[DOMAIN_RULE_CNAME] = domain_rule.is_sub_rule[DOMAIN_RULE_CNAME]; request->no_select_possible_ip = 1; request->no_cache_cname = 1; safe_strncpy(request->cname, cname->cname, sizeof(request->cname)); return 0; } static int _dns_server_process_cname(struct dns_request *request) { struct dns_cname_rule *cname = NULL; const char *child_group_name = NULL; int ret = 0; struct dns_rule_flags *rule_flag = NULL; if (_dns_server_has_bind_flag(request, BIND_FLAG_NO_RULE_CNAME) == 0) { return 0; } if (request->has_cname_loop == 1) { return 0; } /* get domain rule flag */ rule_flag = _dns_server_get_dns_rule(request, DOMAIN_RULE_FLAGS); if (rule_flag != NULL) { if (rule_flag->flags & DOMAIN_FLAG_CNAME_IGN) { return 0; } } cname = _dns_server_get_dns_rule(request, DOMAIN_RULE_CNAME); if (cname == NULL) { return 0; } tlog(TLOG_INFO, "query %s with cname %s", request->domain, cname->cname); struct dns_request *child_request = _dns_server_new_child_request(request, cname->cname, request->qtype, _dns_server_process_cname_callback); if (child_request == NULL) { tlog(TLOG_ERROR, "malloc failed.\n"); return -1; } /* check cname rule loop */ struct dns_request *check_request = child_request->parent_request; struct dns_cname_rule *child_cname = _dns_server_get_dns_rule(child_request, DOMAIN_RULE_CNAME); /* sub domain rule*/ if (child_cname != NULL && strncmp(child_request->domain, child_cname->cname, DNS_MAX_CNAME_LEN) == 0) { child_request->domain_rule.rules[DOMAIN_RULE_CNAME] = NULL; child_request->has_cname_loop = 1; } /* loop rule */ while (check_request != NULL && child_cname != NULL) { struct dns_cname_rule *check_cname = _dns_server_get_dns_rule(check_request, DOMAIN_RULE_CNAME); if (check_cname == NULL) { break; } if (strstr(child_request->domain, check_request->domain) != NULL && check_request != child_request->parent_request) { child_request->domain_rule.rules[DOMAIN_RULE_CNAME] = NULL; child_request->has_cname_loop = 1; break; } check_request = check_request->parent_request; } child_group_name = _dns_server_get_request_groupname(child_request); if (child_group_name) { /* reset dns group and setup child request domain group again when do query.*/ child_request->dns_group_name[0] = '\0'; } request->request_wait++; 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 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_cname == 1) { safe_strncpy(request->cname, child_request->cname, sizeof(request->cname)); request->has_cname = 1; request->ttl_cname = child_request->ttl_cname; } 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; } memset(new_addr_map, 0, sizeof(struct dns_ip_address)); 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 (_dns_server_is_dns64_request(request) == 0) { return 0; } tlog(TLOG_DEBUG, "query %s with dns64", request->domain); struct dns_request *child_request = _dns_server_new_child_request(request, request->domain, DNS_T_A, _dns_server_process_dns64_callback); if (child_request == NULL) { tlog(TLOG_ERROR, "malloc failed.\n"); return -1; } 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) { if (request->skip_qtype_soa || dns_qtype_soa_table == NULL) { return -1; } if (request->qtype >= 0 && request->qtype <= MAX_QTYPE_NUM) { int offset = request->qtype / 8; int bit = request->qtype % 8; if ((dns_qtype_soa_table[offset] & (1 << bit)) == 0) { return -1; } } _dns_server_reply_SOA(DNS_RC_NOERROR, request); tlog(TLOG_DEBUG, "force qtype %d soa", request->qtype); return 0; } static void _dns_server_process_speed_rule(struct dns_request *request) { struct dns_domain_check_orders *check_order = NULL; struct dns_response_mode_rule *response_mode = NULL; /* get speed check mode */ check_order = _dns_server_get_dns_rule(request, DOMAIN_RULE_CHECKSPEED); if (check_order != NULL) { request->check_order_list = check_order; } /* get response mode */ response_mode = _dns_server_get_dns_rule(request, DOMAIN_RULE_RESPONSE_MODE); if (response_mode != NULL) { request->response_mode = response_mode->mode; } } static int _dns_server_get_expired_ttl_reply(struct dns_cache *dns_cache) { int ttl = dns_cache_get_ttl(dns_cache); if (ttl > 0) { return ttl; } return dns_conf_serve_expired_reply_ttl; } static int _dns_server_process_cache_packet(struct dns_request *request, struct dns_cache *dns_cache) { int ret = -1; struct dns_cache_packet *cache_packet = (struct dns_cache_packet *)dns_cache_get_data(dns_cache); if (cache_packet == NULL) { goto out; } int do_ipset = (dns_cache_get_ttl(dns_cache) == 0); if (dns_cache_is_visited(dns_cache) == 0) { do_ipset = 1; } if (dns_cache->info.qtype != request->qtype) { goto out; } struct dns_server_post_context context; _dns_server_post_context_init(&context, request); context.inpacket = cache_packet->data; context.inpacket_len = cache_packet->head.size; request->ping_time = dns_cache->info.speed; if (dns_decode(context.packet, context.packet_maxlen, cache_packet->data, cache_packet->head.size) != 0) { tlog(TLOG_ERROR, "decode cache failed, %d, %d", context.packet_maxlen, context.inpacket_len); goto out; } request->rcode = context.packet->head.rcode; context.do_cache = 0; context.do_ipset = do_ipset; context.do_audit = 1; context.do_reply = 1; context.reply_ttl = _dns_server_get_expired_ttl_reply(dns_cache); ret = _dns_server_reply_passthrough(&context); out: if (cache_packet) { dns_cache_data_put((struct dns_cache_data *)cache_packet); } return ret; } static int _dns_server_process_cache_data(struct dns_request *request, struct dns_cache *dns_cache) { int ret = -1; request->ping_time = dns_cache->info.speed; ret = _dns_server_process_cache_packet(request, dns_cache); if (ret != 0) { goto out; } return 0; out: return -1; } static int _dns_server_process_cache(struct dns_request *request) { struct dns_cache *dns_cache = NULL; struct dns_cache *dualstack_dns_cache = NULL; int ret = -1; if (_dns_server_has_bind_flag(request, BIND_FLAG_NO_CACHE) == 0) { goto out; } struct dns_cache_key cache_key; cache_key.dns_group_name = request->dns_group_name; cache_key.domain = request->domain; cache_key.qtype = request->qtype; cache_key.query_flag = request->server_flags; dns_cache = dns_cache_lookup(&cache_key); if (dns_cache == NULL) { goto out; } if (request->qtype != dns_cache->info.qtype) { goto out; } if (request->qtype == DNS_T_A && dns_conf_dualstack_ip_allow_force_AAAA == 0) { goto reply_cache; } if (request->qtype != DNS_T_A && request->qtype != DNS_T_AAAA) { goto reply_cache; } if (request->dualstack_selection) { int dualstack_qtype = 0; if (request->qtype == DNS_T_A) { dualstack_qtype = DNS_T_AAAA; } else if (request->qtype == DNS_T_AAAA) { dualstack_qtype = DNS_T_A; } else { goto reply_cache; } cache_key.qtype = dualstack_qtype; dualstack_dns_cache = dns_cache_lookup(&cache_key); if (dualstack_dns_cache == NULL && request->cname[0] != '\0') { cache_key.domain = request->cname; dualstack_dns_cache = dns_cache_lookup(&cache_key); } if (dualstack_dns_cache && (dualstack_dns_cache->info.speed > 0)) { if ((dualstack_dns_cache->info.speed + (dns_conf_dualstack_ip_selection_threshold * 10)) < dns_cache->info.speed || dns_cache->info.speed < 0) { tlog(TLOG_DEBUG, "cache result: %s, qtype: %d, force %s preferred, id: %d, time1: %d, time2: %d", request->domain, request->qtype, request->qtype == DNS_T_AAAA ? "IPv4" : "IPv6", request->id, dns_cache->info.speed, dualstack_dns_cache->info.speed); request->ip_ttl = _dns_server_get_expired_ttl_reply(dualstack_dns_cache); ret = _dns_server_reply_SOA(DNS_RC_NOERROR, request); goto out_update_cache; } } } reply_cache: if (dns_cache_get_ttl(dns_cache) <= 0 && request->no_serve_expired == 1) { goto out; } ret = _dns_server_process_cache_data(request, dns_cache); if (ret != 0) { goto out; } out_update_cache: if (dns_cache_get_ttl(dns_cache) == 0) { struct dns_server_query_option dns_query_options; dns_query_options.server_flags = request->server_flags; dns_query_options.dns_group_name = request->dns_group_name; if (request->conn == NULL) { dns_query_options.server_flags = dns_cache_get_query_flag(dns_cache); dns_query_options.dns_group_name = dns_cache_get_dns_group_name(dns_cache); } dns_query_options.ecs_enable_flag = 0; if (request->has_ecs) { dns_query_options.ecs_enable_flag |= DNS_QUEY_OPTION_ECS_DNS; memcpy(&dns_query_options.ecs_dns, &request->ecs, sizeof(dns_query_options.ecs_dns)); } _dns_server_prefetch_request(request->domain, request->qtype, &dns_query_options, 0); } else { dns_cache_update(dns_cache); } out: if (dns_cache) { dns_cache_release(dns_cache); } if (dualstack_dns_cache) { dns_cache_release(dualstack_dns_cache); dualstack_dns_cache = NULL; } return ret; } void dns_server_check_ipv6_ready(void) { static int do_get_conf = 0; static int is_icmp_check_set; static int is_tcp_check_set; int i = 0; if (do_get_conf == 0) { for (i = 0; i < DOMAIN_CHECK_NUM; i++) { if (dns_conf_check_orders.orders[i].type == DOMAIN_CHECK_ICMP) { is_icmp_check_set = 1; } if (dns_conf_check_orders.orders[i].type == DOMAIN_CHECK_TCP) { is_tcp_check_set = 1; } } if (is_icmp_check_set == 0) { tlog(TLOG_INFO, "ICMP ping is disabled, no ipv6 icmp check feature"); } do_get_conf = 1; } if (is_icmp_check_set) { struct ping_host_struct *check_ping = fast_ping_start(PING_TYPE_ICMP, "2001::", 1, 0, 100, NULL, NULL); if (check_ping) { fast_ping_stop(check_ping); is_ipv6_ready = 1; return; } if (errno == EADDRNOTAVAIL) { is_ipv6_ready = 0; return; } } if (is_tcp_check_set) { struct ping_host_struct *check_ping = fast_ping_start(PING_TYPE_TCP, "2001::", 1, 0, 100, NULL, NULL); if (check_ping) { fast_ping_stop(check_ping); is_ipv6_ready = 1; return; } if (errno == EADDRNOTAVAIL) { is_ipv6_ready = 0; return; } } } 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); } static void _dns_server_request_set_id(struct dns_request *request, unsigned short id) { request->id = id; } static int _dns_server_request_set_client_addr(struct dns_request *request, struct sockaddr_storage *from, socklen_t from_len) { switch (from->ss_family) { case AF_INET: memcpy(&request->in, from, from_len); request->addr_len = from_len; break; case AF_INET6: memcpy(&request->in6, from, from_len); request->addr_len = from_len; break; default: return -1; break; } return 0; } static void _dns_server_request_set_callback(struct dns_request *request, dns_result_callback callback, void *user_ptr) { request->result_callback = callback; request->user_ptr = user_ptr; } static int _dns_server_process_smartdns_domain(struct dns_request *request) { struct dns_rule_flags *rule_flag = NULL; unsigned int flags = 0; /* get domain rule flag */ rule_flag = _dns_server_get_dns_rule(request, DOMAIN_RULE_FLAGS); if (rule_flag == NULL) { return -1; } if (_dns_server_is_dns_rule_extract_match(request, DOMAIN_RULE_FLAGS) == 0) { return -1; } flags = rule_flag->flags; if (!(flags & DOMAIN_FLAG_SMARTDNS_DOMAIN)) { return -1; } return _dns_server_reply_request_eth_ip(request); } static int _dns_server_process_ptr_query(struct dns_request *request) { if (request->qtype != DNS_T_PTR) { return -1; } if (_dns_server_process_ptr(request) == 0) { return 0; } request->passthrough = 1; return -1; } static int _dns_server_process_special_query(struct dns_request *request) { int ret = 0; switch (request->qtype) { case DNS_T_PTR: break; case DNS_T_SVCB: ret = _dns_server_process_srv(request); if (ret == 0) { goto clean_exit; } else { /* pass to upstream server */ request->passthrough = 1; } break; case DNS_T_A: break; case DNS_T_AAAA: break; default: tlog(TLOG_DEBUG, "unsupported qtype: %d, domain: %s", request->qtype, request->domain); request->passthrough = 1; /* pass request to upstream server */ break; } return -1; clean_exit: return 0; } static const char *_dns_server_get_request_groupname(struct dns_request *request) { if (_dns_server_has_bind_flag(request, BIND_FLAG_NO_RULE_NAMESERVER) == 0) { return NULL; } /* Get the nameserver rule */ if (request->domain_rule.rules[DOMAIN_RULE_NAMESERVER]) { struct dns_nameserver_rule *nameserver_rule = _dns_server_get_dns_rule(request, DOMAIN_RULE_NAMESERVER); return nameserver_rule->group_name; } return NULL; } static void _dns_server_check_set_passthrough(struct dns_request *request) { if (request->check_order_list->orders[0].type == DOMAIN_CHECK_NONE) { request->passthrough = 1; } if (_dns_server_has_bind_flag(request, BIND_FLAG_NO_SPEED_CHECK) == 0) { request->passthrough = 1; } if (is_ipv6_ready == 0 && request->qtype == DNS_T_AAAA) { request->passthrough = 1; } if (request->passthrough == 1) { request->dualstack_selection = 0; } if (request->passthrough == 1 && (request->qtype == DNS_T_A || request->qtype == DNS_T_AAAA)) { request->passthrough = 2; } } static int _dns_server_process_host(struct dns_request *request) { uint32_t key = 0; struct dns_hosts *host = NULL; struct dns_hosts *host_tmp = NULL; int dns_type = request->qtype; char hostname_lower[DNS_MAX_CNAME_LEN]; if (dns_hosts_record_num <= 0) { return -1; } key = hash_string(to_lower_case(hostname_lower, request->domain, DNS_MAX_CNAME_LEN)); key = jhash(&dns_type, sizeof(dns_type), key); hash_for_each_possible(dns_hosts_table.hosts, host_tmp, node, key) { if (host_tmp->dns_type != dns_type) { continue; } if (strncmp(host_tmp->domain, hostname_lower, DNS_MAX_CNAME_LEN) != 0) { continue; } host = host_tmp; break; } if (host == NULL) { return -1; } if (host->is_soa) { request->has_soa = 1; return _dns_server_reply_SOA(DNS_RC_NOERROR, request); } switch (request->qtype) { case DNS_T_A: memcpy(request->ip_addr, host->ipv4_addr, DNS_RR_A_LEN); break; case DNS_T_AAAA: memcpy(request->ip_addr, host->ipv6_addr, DNS_RR_AAAA_LEN); break; default: goto errout; break; } request->rcode = DNS_RC_NOERROR; request->ip_ttl = dns_conf_local_ttl; request->has_ip = 1; struct dns_server_post_context context; _dns_server_post_context_init(&context, request); context.do_reply = 1; context.do_audit = 1; _dns_request_post(&context); return 0; errout: return -1; } static int _dns_server_setup_query_option(struct dns_request *request, struct dns_query_options *options) { options->enable_flag = 0; if (request->has_ecs) { memcpy(&options->ecs_dns, &request->ecs, sizeof(options->ecs_dns)); options->enable_flag |= DNS_QUEY_OPTION_ECS_DNS; } return 0; } static int _dns_server_query_dualstack(struct dns_request *request) { int ret = -1; struct dns_request *request_dualstack = NULL; dns_type_t qtype = request->qtype; if (request->dualstack_selection == 0) { return 0; } if (qtype == DNS_T_A) { qtype = DNS_T_AAAA; } else if (qtype == DNS_T_AAAA) { qtype = DNS_T_A; } else { return 0; } request_dualstack = _dns_server_new_request(); if (request_dualstack == NULL) { tlog(TLOG_ERROR, "malloc failed.\n"); goto errout; } request_dualstack->server_flags = request->server_flags; safe_strncpy(request_dualstack->dns_group_name, request->dns_group_name, sizeof(request->dns_group_name)); safe_strncpy(request_dualstack->domain, request->domain, sizeof(request->domain)); request_dualstack->qtype = qtype; request_dualstack->dualstack_selection_query = 1; request_dualstack->has_cname_loop = request->has_cname_loop; request_dualstack->prefetch = request->prefetch; request_dualstack->prefetch_flags = request->prefetch_flags; _dns_server_request_get(request); request_dualstack->dualstack_request = request; _dns_server_request_set_callback(request_dualstack, dns_server_dualstack_callback, request); request->request_wait++; ret = _dns_server_do_query(request_dualstack, 0); if (ret != 0) { request->request_wait--; tlog(TLOG_ERROR, "do query %s type %d failed.\n", request->domain, qtype); goto errout; } _dns_server_request_release(request_dualstack); return ret; errout: if (request_dualstack) { _dns_server_request_set_callback(request_dualstack, NULL, NULL); _dns_server_request_release(request_dualstack); } _dns_server_request_release(request); return ret; } static int _dns_server_do_query(struct dns_request *request, int skip_notify_event) { int ret = -1; const char *group_name = NULL; const char *dns_group = NULL; struct dns_query_options options; if (request->conn) { dns_group = request->conn->dns_group; } request->send_tick = get_tick_count(); /* lookup domain rule */ _dns_server_get_domain_rule(request); group_name = request->dns_group_name; if (request->dns_group_name[0] == '\0') { group_name = _dns_server_get_request_groupname(request); if (group_name == NULL) { group_name = dns_group; } safe_strncpy(request->dns_group_name, group_name, DNS_GROUP_NAME_LEN); } if (_dns_server_process_cname_pre(request) != 0) { goto errout; } _dns_server_set_dualstack_selection(request); if (_dns_server_process_special_query(request) == 0) { goto clean_exit; } if (_dns_server_pre_process_server_flags(request) == 0) { goto clean_exit; } /* process domain flag */ if (_dns_server_pre_process_rule_flags(request) == 0) { goto clean_exit; } /* process domain address */ if (_dns_server_process_address(request) == 0) { goto clean_exit; } if (_dns_server_process_smartdns_domain(request) == 0) { goto clean_exit; } if (_dns_server_process_host(request) == 0) { goto clean_exit; } /* process qtype soa */ if (_dns_server_qtype_soa(request) == 0) { goto clean_exit; } /* process speed check rule */ _dns_server_process_speed_rule(request); /* check and set passthrough */ _dns_server_check_set_passthrough(request); /* process cache */ if (request->prefetch == 0 && request->dualstack_selection_query == 0) { if (_dns_server_process_cache(request) == 0) { goto clean_exit; } } /* process ptr */ if (_dns_server_process_ptr_query(request) == 0) { goto clean_exit; } ret = _dns_server_set_to_pending_list(request); if (ret == 0) { goto clean_exit; } if (_dns_server_process_cname(request) != 0) { goto clean_exit; } // setup options _dns_server_setup_query_option(request, &options); pthread_mutex_lock(&server.request_list_lock); if (list_empty(&server.request_list) && skip_notify_event == 1) { _dns_server_wakeup_thread(); } list_add_tail(&request->list, &server.request_list); pthread_mutex_unlock(&server.request_list_lock); // Get reference for DNS query request->request_wait++; _dns_server_request_get(request); if (dns_client_query(request->domain, request->qtype, dns_server_resolve_callback, request, group_name, &options) != 0) { request->request_wait--; _dns_server_request_release(request); tlog(TLOG_DEBUG, "send dns request failed."); goto errout; } /* 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: request = NULL; return ret; } static int _dns_server_check_request_supported(struct dns_request *request, struct dns_packet *packet) { if (request->qclass != DNS_C_IN) { return -1; } if (packet->head.opcode != DNS_OP_QUERY) { return -1; } return 0; } static int _dns_server_parser_request(struct dns_request *request, struct dns_packet *packet) { struct dns_rrs *rrs = NULL; int rr_count = 0; int i = 0; int ret = 0; int qclass = 0; int qtype = DNS_T_ALL; char domain[DNS_MAX_CNAME_LEN]; if (packet->head.qr != DNS_QR_QUERY) { goto errout; } /* get request domain and request qtype */ rrs = dns_get_rrs_start(packet, DNS_RRS_QD, &rr_count); if (rr_count > 1 || rr_count <= 0) { goto errout; } for (i = 0; i < rr_count && rrs; i++, rrs = dns_get_rrs_next(packet, rrs)) { ret = dns_get_domain(rrs, domain, sizeof(domain), &qtype, &qclass); if (ret != 0) { goto errout; } // Only support one question. safe_strncpy(request->domain, domain, sizeof(request->domain)); request->qtype = qtype; break; } request->qclass = qclass; if (_dns_server_check_request_supported(request, packet) != 0) { goto errout; } /* get request opts */ rr_count = 0; rrs = dns_get_rrs_start(packet, DNS_RRS_OPT, &rr_count); if (rr_count <= 0) { return 0; } for (i = 0; i < rr_count && rrs; i++, rrs = dns_get_rrs_next(packet, rrs)) { ret = dns_get_OPT_ECS(rrs, NULL, NULL, &request->ecs); if (ret != 0) { continue; } request->has_ecs = 1; break; } return 0; errout: request->rcode = DNS_RC_NOTIMP; return -1; } static int _dns_server_recv(struct dns_server_conn_head *conn, unsigned char *inpacket, int inpacket_len, struct sockaddr_storage *local, socklen_t local_len, struct sockaddr_storage *from, socklen_t from_len) { int decode_len = 0; int ret = -1; unsigned char packet_buff[DNS_PACKSIZE]; char name[DNS_MAX_CNAME_LEN]; struct dns_packet *packet = (struct dns_packet *)packet_buff; struct dns_request *request = NULL; /* decode packet */ tlog(TLOG_DEBUG, "recv query packet from %s, len = %d, type = %d", get_host_by_addr(name, sizeof(name), (struct sockaddr *)from), inpacket_len, conn->type); decode_len = dns_decode(packet, DNS_PACKSIZE, inpacket, inpacket_len); if (decode_len < 0) { tlog(TLOG_DEBUG, "decode failed.\n"); ret = RECV_ERROR_INVALID_PACKET; if (dns_save_fail_packet) { dns_packet_save(dns_save_fail_packet_dir, "server", name, inpacket, inpacket_len); } goto errout; } tlog(TLOG_DEBUG, "request qdcount = %d, ancount = %d, nscount = %d, nrcount = %d, len = %d, id = %d, tc = %d, rd = %d, ra = " "%d, rcode = %d\n", packet->head.qdcount, packet->head.ancount, packet->head.nscount, packet->head.nrcount, inpacket_len, packet->head.id, packet->head.tc, packet->head.rd, packet->head.ra, packet->head.rcode); request = _dns_server_new_request(); if (request == NULL) { tlog(TLOG_ERROR, "malloc failed.\n"); goto errout; } memcpy(&request->localaddr, local, local_len); _dns_server_request_set_client(request, conn); _dns_server_request_set_client_addr(request, from, from_len); _dns_server_request_set_id(request, packet->head.id); if (_dns_server_parser_request(request, packet) != 0) { tlog(TLOG_DEBUG, "parser request failed."); ret = RECV_ERROR_INVALID_PACKET; goto errout; } tlog(TLOG_DEBUG, "query %s from %s, qtype: %d, id: %d\n", request->domain, name, request->qtype, request->id); ret = _dns_server_do_query(request, 1); if (ret != 0) { tlog(TLOG_DEBUG, "do query %s failed.\n", request->domain); goto errout; } _dns_server_request_release_complete(request, 0); return ret; errout: if (request) { _dns_server_forward_request(inpacket, inpacket_len); _dns_server_request_release(request); } return ret; } static int _dns_server_setup_server_query_options(struct dns_request *request, struct dns_server_query_option *server_query_option) { if (server_query_option == NULL) { return 0; } request->server_flags = server_query_option->server_flags; if (server_query_option->dns_group_name) { safe_strncpy(request->dns_group_name, server_query_option->dns_group_name, DNS_GROUP_NAME_LEN); } if (server_query_option->ecs_enable_flag & DNS_QUEY_OPTION_ECS_DNS) { request->has_ecs = 1; memcpy(&request->ecs, &server_query_option->ecs_dns, sizeof(request->ecs)); } return 0; } static int _dns_server_prefetch_request(char *domain, dns_type_t qtype, struct dns_server_query_option *server_query_option, int prefetch_flag) { int ret = -1; struct dns_request *request = NULL; request = _dns_server_new_request(); if (request == NULL) { tlog(TLOG_ERROR, "malloc failed.\n"); goto errout; } request->prefetch = 1; request->prefetch_flags = prefetch_flag; safe_strncpy(request->domain, domain, sizeof(request->domain)); request->qtype = qtype; _dns_server_setup_server_query_options(request, server_query_option); ret = _dns_server_do_query(request, 0); if (ret != 0) { tlog(TLOG_DEBUG, "prefetch do query %s failed.\n", request->domain); goto errout; } _dns_server_request_release(request); return ret; errout: if (request) { _dns_server_request_release(request); } return ret; } int dns_server_query(const char *domain, int qtype, struct dns_server_query_option *server_query_option, dns_result_callback callback, void *user_ptr) { int ret = -1; struct dns_request *request = NULL; request = _dns_server_new_request(); if (request == NULL) { tlog(TLOG_ERROR, "malloc failed.\n"); goto errout; } safe_strncpy(request->domain, domain, sizeof(request->domain)); request->qtype = qtype; _dns_server_setup_server_query_options(request, server_query_option); _dns_server_request_set_callback(request, callback, user_ptr); ret = _dns_server_do_query(request, 0); if (ret != 0) { tlog(TLOG_DEBUG, "do query %s failed.\n", domain); goto errout; } _dns_server_request_release_complete(request, 0); return ret; errout: if (request) { _dns_server_request_set_callback(request, NULL, NULL); _dns_server_request_release(request); } return ret; } static int _dns_server_process_udp_one(struct dns_server_conn_udp *udpconn, struct epoll_event *event, unsigned long now) { int len = 0; unsigned char inpacket[DNS_IN_PACKSIZE]; struct sockaddr_storage from; socklen_t from_len = sizeof(from); struct sockaddr_storage local; socklen_t local_len = sizeof(local); struct msghdr msg; struct iovec iov; char ans_data[4096]; struct cmsghdr *cmsg = NULL; memset(&msg, 0, sizeof(msg)); iov.iov_base = (char *)inpacket; iov.iov_len = sizeof(inpacket); msg.msg_name = &from; msg.msg_namelen = sizeof(from); msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = ans_data; msg.msg_controllen = sizeof(ans_data); len = recvmsg(udpconn->head.fd, &msg, MSG_DONTWAIT); if (len < 0) { if (errno == EAGAIN || errno == EWOULDBLOCK) { return -2; } tlog(TLOG_ERROR, "recvfrom failed, %s\n", strerror(errno)); return -1; } from_len = msg.msg_namelen; for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) { const struct in_pktinfo *pktinfo = (struct in_pktinfo *)CMSG_DATA(cmsg); unsigned char *addr = (unsigned char *)&pktinfo->ipi_addr.s_addr; fill_sockaddr_by_ip(addr, sizeof(in_addr_t), 0, (struct sockaddr *)&local, &local_len); } else if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) { const struct in6_pktinfo *pktinfo = (struct in6_pktinfo *)CMSG_DATA(cmsg); unsigned char *addr = (unsigned char *)pktinfo->ipi6_addr.s6_addr; fill_sockaddr_by_ip(addr, sizeof(struct in6_addr), 0, (struct sockaddr *)&local, &local_len); } } return _dns_server_recv(&udpconn->head, inpacket, len, &local, local_len, &from, from_len); } static int _dns_server_process_udp(struct dns_server_conn_udp *udpconn, struct epoll_event *event, unsigned long now) { int count = 0; while (count < 32) { int ret = _dns_server_process_udp_one(udpconn, event, now); if (ret != 0) { if (ret == -2) { return 0; } return ret; } count++; } return 0; } static void _dns_server_client_touch(struct dns_server_conn_head *conn) { time(&conn->last_request_time); } static int _dns_server_client_close(struct dns_server_conn_head *conn) { if (conn->fd > 0) { _dns_server_epoll_ctl(conn, EPOLL_CTL_DEL, 0); close(conn->fd); conn->fd = -1; } list_del_init(&conn->list); _dns_server_conn_release(conn); return 0; } static int _dns_server_tcp_accept(struct dns_server_conn_tcp_server *tcpserver, struct epoll_event *event, unsigned long now) { struct sockaddr_storage addr; struct dns_server_conn_tcp_client *tcpclient = NULL; socklen_t addr_len = sizeof(addr); int fd = -1; fd = accept4(tcpserver->head.fd, (struct sockaddr *)&addr, &addr_len, SOCK_NONBLOCK | SOCK_CLOEXEC); if (fd < 0) { tlog(TLOG_ERROR, "accept failed, %s", strerror(errno)); return -1; } tcpclient = malloc(sizeof(*tcpclient)); if (tcpclient == NULL) { tlog(TLOG_ERROR, "malloc for tcpclient failed."); goto errout; } memset(tcpclient, 0, sizeof(*tcpclient)); tcpclient->head.fd = fd; tcpclient->head.type = DNS_CONN_TYPE_TCP_CLIENT; tcpclient->head.server_flags = tcpserver->head.server_flags; tcpclient->head.dns_group = tcpserver->head.dns_group; tcpclient->head.ipset_nftset_rule = tcpserver->head.ipset_nftset_rule; atomic_set(&tcpclient->head.refcnt, 0); memcpy(&tcpclient->addr, &addr, addr_len); tcpclient->addr_len = addr_len; tcpclient->localaddr_len = sizeof(struct sockaddr_storage); if (_dns_server_epoll_ctl(&tcpclient->head, EPOLL_CTL_ADD, EPOLLIN) != 0) { tlog(TLOG_ERROR, "epoll ctl failed."); return -1; } if (getsocket_inet(tcpclient->head.fd, (struct sockaddr *)&tcpclient->localaddr, &tcpclient->localaddr_len) != 0) { tlog(TLOG_ERROR, "get local addr failed, %s", strerror(errno)); goto errout; } _dns_server_client_touch(&tcpclient->head); list_add(&tcpclient->head.list, &server.conn_list); _dns_server_conn_get(&tcpclient->head); return 0; errout: if (fd > 0) { close(fd); } if (tcpclient) { free(tcpclient); } return -1; } static ssize_t _ssl_read(struct dns_server_conn_tls_client *conn, void *buff, int num) { ssize_t ret = 0; if (conn == NULL || buff == NULL) { return SSL_ERROR_SYSCALL; } pthread_mutex_lock(&conn->ssl_lock); ret = SSL_read(conn->ssl, buff, num); pthread_mutex_unlock(&conn->ssl_lock); return ret; } static ssize_t _ssl_write(struct dns_server_conn_tls_client *conn, const void *buff, int num) { ssize_t ret = 0; if (conn == NULL || buff == NULL || conn->ssl == NULL) { return SSL_ERROR_SYSCALL; } pthread_mutex_lock(&conn->ssl_lock); ret = SSL_write(conn->ssl, buff, num); pthread_mutex_unlock(&conn->ssl_lock); return ret; } static int _ssl_get_error(struct dns_server_conn_tls_client *conn, int ret) { int err = 0; if (conn == NULL || conn->ssl == NULL) { return SSL_ERROR_SYSCALL; } pthread_mutex_lock(&conn->ssl_lock); err = SSL_get_error(conn->ssl, ret); pthread_mutex_unlock(&conn->ssl_lock); return err; } static int _ssl_do_accept(struct dns_server_conn_tls_client *conn) { int err = 0; if (conn == NULL || conn->ssl == NULL) { return SSL_ERROR_SYSCALL; } pthread_mutex_lock(&conn->ssl_lock); err = SSL_accept(conn->ssl); pthread_mutex_unlock(&conn->ssl_lock); return err; } static int _dns_server_socket_ssl_send(struct dns_server_conn_tls_client *tls_client, const void *buf, int num) { int ret = 0; int ssl_ret = 0; unsigned long ssl_err = 0; if (tls_client->ssl == NULL) { errno = EINVAL; return -1; } if (num < 0) { errno = EINVAL; return -1; } ret = _ssl_write(tls_client, buf, num); if (ret > 0) { return ret; } ssl_ret = _ssl_get_error(tls_client, ret); switch (ssl_ret) { case SSL_ERROR_NONE: return 0; break; case SSL_ERROR_ZERO_RETURN: case SSL_ERROR_WANT_READ: errno = EAGAIN; ret = -1; break; case SSL_ERROR_WANT_WRITE: errno = EAGAIN; ret = -1; break; case SSL_ERROR_SSL: ssl_err = ERR_get_error(); int ssl_reason = ERR_GET_REASON(ssl_err); if (ssl_reason == SSL_R_UNINITIALIZED || ssl_reason == SSL_R_PROTOCOL_IS_SHUTDOWN || ssl_reason == SSL_R_BAD_LENGTH || ssl_reason == SSL_R_SHUTDOWN_WHILE_IN_INIT || ssl_reason == SSL_R_BAD_WRITE_RETRY) { errno = EAGAIN; return -1; } tlog(TLOG_ERROR, "SSL write fail error no: %s(%d)\n", ERR_reason_error_string(ssl_err), ssl_reason); errno = EFAULT; ret = -1; break; case SSL_ERROR_SYSCALL: tlog(TLOG_DEBUG, "SSL syscall failed, %s", strerror(errno)); return ret; default: errno = EFAULT; ret = -1; break; } return ret; } static int _dns_server_socket_ssl_recv(struct dns_server_conn_tls_client *tls_client, void *buf, int num) { ssize_t ret = 0; int ssl_ret = 0; unsigned long ssl_err = 0; if (tls_client->ssl == NULL) { errno = EFAULT; return -1; } ret = _ssl_read(tls_client, buf, num); if (ret >= 0) { return ret; } ssl_ret = _ssl_get_error(tls_client, ret); switch (ssl_ret) { case SSL_ERROR_NONE: case SSL_ERROR_ZERO_RETURN: return 0; break; case SSL_ERROR_WANT_READ: errno = EAGAIN; ret = -1; break; case SSL_ERROR_WANT_WRITE: errno = EAGAIN; ret = -1; break; case SSL_ERROR_SSL: ssl_err = ERR_get_error(); int ssl_reason = ERR_GET_REASON(ssl_err); if (ssl_reason == SSL_R_UNINITIALIZED) { errno = EAGAIN; return -1; } if (ssl_reason == SSL_R_SHUTDOWN_WHILE_IN_INIT || ssl_reason == SSL_R_PROTOCOL_IS_SHUTDOWN) { return 0; } tlog(TLOG_INFO, "SSL read fail error no: %s(%lx), len: %d\n", ERR_reason_error_string(ssl_err), ssl_err, num); errno = EFAULT; ret = -1; break; case SSL_ERROR_SYSCALL: if (errno == 0) { return 0; } if (errno != ECONNRESET) { tlog(TLOG_INFO, "SSL syscall failed, %s ", strerror(errno)); } ret = -1; return ret; default: errno = EFAULT; ret = -1; break; } return ret; } static int _dns_server_tcp_socket_send(struct dns_server_conn_tcp_client *tcp_client, void *data, int data_len) { if (tcp_client->head.type == DNS_CONN_TYPE_TCP_CLIENT) { return send(tcp_client->head.fd, data, data_len, MSG_NOSIGNAL); } else if (tcp_client->head.type == DNS_CONN_TYPE_TLS_CLIENT || tcp_client->head.type == DNS_CONN_TYPE_HTTPS_CLIENT) { int ret = _dns_server_socket_ssl_send((struct dns_server_conn_tls_client *)tcp_client, data, data_len); return ret; } else { return -1; } } static int _dns_server_tcp_socket_recv(struct dns_server_conn_tcp_client *tcp_client, void *data, int data_len) { if (tcp_client->head.type == DNS_CONN_TYPE_TCP_CLIENT) { return recv(tcp_client->head.fd, data, data_len, MSG_NOSIGNAL); } else if (tcp_client->head.type == DNS_CONN_TYPE_TLS_CLIENT || tcp_client->head.type == DNS_CONN_TYPE_HTTPS_CLIENT) { return _dns_server_socket_ssl_recv((struct dns_server_conn_tls_client *)tcp_client, data, data_len); } else { return -1; } } static int _dns_server_tcp_recv(struct dns_server_conn_tcp_client *tcpclient) { ssize_t len = 0; /* Receive data */ while (tcpclient->recvbuff.size < (int)sizeof(tcpclient->recvbuff.buf)) { if (tcpclient->recvbuff.size == (int)sizeof(tcpclient->recvbuff.buf)) { return 0; } len = _dns_server_tcp_socket_recv(tcpclient, tcpclient->recvbuff.buf + tcpclient->recvbuff.size, sizeof(tcpclient->recvbuff.buf) - tcpclient->recvbuff.size); if (len < 0) { if (errno == EAGAIN) { return RECV_ERROR_AGAIN; } if (errno == ECONNRESET) { return RECV_ERROR_CLOSE; } tlog(TLOG_ERROR, "recv failed, %s\n", strerror(errno)); return RECV_ERROR_FAIL; } else if (len == 0) { return RECV_ERROR_CLOSE; } tcpclient->recvbuff.size += len; } return 0; } static int _dns_server_tcp_process_one_request(struct dns_server_conn_tcp_client *tcpclient) { unsigned short request_len = 0; int total_len = tcpclient->recvbuff.size; int proceed_len = 0; unsigned char *request_data = NULL; int ret = 0; /* Handling multiple requests */ for (;;) { if ((total_len - proceed_len) <= (int)sizeof(unsigned short)) { ret = RECV_ERROR_AGAIN; break; } /* Get record length */ request_data = (unsigned char *)(tcpclient->recvbuff.buf + proceed_len); request_len = ntohs(*((unsigned short *)(request_data))); if (request_len >= sizeof(tcpclient->recvbuff.buf)) { tlog(TLOG_DEBUG, "request length is invalid."); return RECV_ERROR_FAIL; } if (request_len > (total_len - proceed_len - sizeof(unsigned short))) { ret = RECV_ERROR_AGAIN; break; } request_data = (unsigned char *)(tcpclient->recvbuff.buf + proceed_len + sizeof(unsigned short)); /* process one record */ ret = _dns_server_recv(&tcpclient->head, request_data, request_len, &tcpclient->localaddr, tcpclient->localaddr_len, &tcpclient->addr, tcpclient->addr_len); if (ret != 0) { return ret; } proceed_len += sizeof(unsigned short) + request_len; } if (total_len > proceed_len && proceed_len > 0) { memmove(tcpclient->recvbuff.buf, tcpclient->recvbuff.buf + proceed_len, total_len - proceed_len); } tcpclient->recvbuff.size -= proceed_len; return ret; } static int _dns_server_tcp_process_requests(struct dns_server_conn_tcp_client *tcpclient) { int recv_ret = 0; int request_ret = 0; int is_eof = 0; for (;;) { recv_ret = _dns_server_tcp_recv(tcpclient); if (recv_ret < 0) { if (recv_ret == RECV_ERROR_CLOSE) { return RECV_ERROR_CLOSE; } if (tcpclient->recvbuff.size > 0) { is_eof = RECV_ERROR_AGAIN; } else { return RECV_ERROR_FAIL; } } request_ret = _dns_server_tcp_process_one_request(tcpclient); if (request_ret < 0) { /* failed */ tlog(TLOG_DEBUG, "process one request failed."); return RECV_ERROR_FAIL; } if (request_ret == RECV_ERROR_AGAIN && is_eof == RECV_ERROR_AGAIN) { /* failed or remote shutdown */ return RECV_ERROR_FAIL; } if (recv_ret == RECV_ERROR_AGAIN && request_ret == RECV_ERROR_AGAIN) { /* process complete */ return 0; } } return 0; } static int _dns_server_tcp_send(struct dns_server_conn_tcp_client *tcpclient) { int len = 0; while (tcpclient->sndbuff.size > 0) { len = _dns_server_tcp_socket_send(tcpclient, tcpclient->sndbuff.buf, tcpclient->sndbuff.size); if (len < 0) { if (errno == EAGAIN) { return RECV_ERROR_AGAIN; } return RECV_ERROR_FAIL; } else if (len == 0) { break; } tcpclient->sndbuff.size -= len; } if (_dns_server_epoll_ctl(&tcpclient->head, EPOLL_CTL_MOD, EPOLLIN) != 0) { tlog(TLOG_ERROR, "epoll ctl failed."); return -1; } return 0; } static int _dns_server_process_tcp(struct dns_server_conn_tcp_client *dnsserver, struct epoll_event *event, unsigned long now) { int ret = 0; if (event->events & EPOLLIN) { ret = _dns_server_tcp_process_requests(dnsserver); if (ret != 0) { _dns_server_client_close(&dnsserver->head); if (ret == RECV_ERROR_CLOSE) { return 0; } tlog(TLOG_DEBUG, "process tcp request failed."); return RECV_ERROR_FAIL; } } if (event->events & EPOLLOUT) { if (_dns_server_tcp_send(dnsserver) != 0) { _dns_server_client_close(&dnsserver->head); tlog(TLOG_DEBUG, "send tcp failed."); return RECV_ERROR_FAIL; } } return 0; } static int _dns_server_tls_accept(struct dns_server_conn_tls_server *tls_server, struct epoll_event *event, unsigned long now) { struct sockaddr_storage addr; struct dns_server_conn_tls_client *tls_client = NULL; socklen_t addr_len = sizeof(addr); int fd = -1; SSL *ssl = NULL; fd = accept4(tls_server->head.fd, (struct sockaddr *)&addr, &addr_len, SOCK_NONBLOCK | SOCK_CLOEXEC); if (fd < 0) { tlog(TLOG_ERROR, "accept failed, %s", strerror(errno)); return -1; } tls_client = malloc(sizeof(*tls_client)); if (tls_client == NULL) { tlog(TLOG_ERROR, "malloc for tls_client failed."); goto errout; } memset(tls_client, 0, sizeof(*tls_client)); tls_client->head.fd = fd; tls_client->head.type = DNS_CONN_TYPE_TLS_CLIENT; tls_client->head.server_flags = tls_server->head.server_flags; tls_client->head.dns_group = tls_server->head.dns_group; tls_client->head.ipset_nftset_rule = tls_server->head.ipset_nftset_rule; atomic_set(&tls_client->head.refcnt, 0); memcpy(&tls_client->addr, &addr, addr_len); tls_client->addr_len = addr_len; tls_client->localaddr_len = sizeof(struct sockaddr_storage); if (_dns_server_epoll_ctl(&tls_client->head, EPOLL_CTL_ADD, EPOLLIN) != 0) { tlog(TLOG_ERROR, "epoll ctl failed."); return -1; } if (getsocket_inet(tls_client->head.fd, (struct sockaddr *)&tls_client->localaddr, &tls_client->localaddr_len) != 0) { tlog(TLOG_ERROR, "get local addr failed, %s", strerror(errno)); goto errout; } ssl = SSL_new(tls_server->ssl_ctx); if (ssl == NULL) { tlog(TLOG_ERROR, "SSL_new failed."); goto errout; } if (SSL_set_fd(ssl, fd) != 1) { tlog(TLOG_ERROR, "SSL_set_fd failed."); goto errout; } tls_client->ssl = ssl; tls_client->status = DNS_SERVER_CLIENT_STATUS_CONNECTING; pthread_mutex_init(&tls_client->ssl_lock, NULL); _dns_server_client_touch(&tls_client->head); list_add(&tls_client->head.list, &server.conn_list); _dns_server_conn_get(&tls_client->head); return 0; errout: if (fd > 0) { close(fd); } if (ssl) { SSL_free(ssl); } if (tls_client) { free(tls_client); } return -1; } static int _dns_server_process_tls(struct dns_server_conn_tls_client *tls_client, struct epoll_event *event, unsigned long now) { int ret = 0; int ssl_ret = 0; struct epoll_event fd_event; if (tls_client->status == DNS_SERVER_CLIENT_STATUS_CONNECTING) { /* do SSL hand shake */ ret = _ssl_do_accept(tls_client); if (ret <= 0) { memset(&fd_event, 0, sizeof(fd_event)); ssl_ret = _ssl_get_error(tls_client, ret); if (ssl_ret == SSL_ERROR_WANT_READ) { fd_event.events = EPOLLIN; } else if (ssl_ret == SSL_ERROR_WANT_WRITE) { fd_event.events = EPOLLOUT | EPOLLIN; } else if (ssl_ret == SSL_ERROR_SYSCALL) { goto errout; } else { unsigned long ssl_err = ERR_get_error(); int ssl_reason = ERR_GET_REASON(ssl_err); char name[DNS_MAX_CNAME_LEN]; tlog(TLOG_DEBUG, "Handshake with %s failed, error no: %s(%d, %d, %d)\n", get_host_by_addr(name, sizeof(name), (struct sockaddr *)&tls_client->addr), ERR_reason_error_string(ssl_err), ret, ssl_ret, ssl_reason); ret = 0; goto errout; } fd_event.data.ptr = tls_client; if (epoll_ctl(server.epoll_fd, EPOLL_CTL_MOD, tls_client->head.fd, &fd_event) != 0) { tlog(TLOG_ERROR, "epoll ctl failed, %s", strerror(errno)); goto errout; } return 0; } tls_client->status = DNS_SERVER_CLIENT_STATUS_CONNECTED; memset(&fd_event, 0, sizeof(fd_event)); fd_event.events = EPOLLIN | EPOLLOUT; fd_event.data.ptr = tls_client; if (epoll_ctl(server.epoll_fd, EPOLL_CTL_MOD, tls_client->head.fd, &fd_event) != 0) { tlog(TLOG_ERROR, "epoll ctl failed, %s", strerror(errno)); goto errout; } } return _dns_server_process_tcp((struct dns_server_conn_tcp_client *)tls_client, event, now); errout: _dns_server_client_close(&tls_client->head); return ret; } static int _dns_server_process(struct dns_server_conn_head *conn, struct epoll_event *event, unsigned long now) { int ret = 0; _dns_server_client_touch(conn); _dns_server_conn_get(conn); if (conn->type == DNS_CONN_TYPE_UDP_SERVER) { struct dns_server_conn_udp *udpconn = (struct dns_server_conn_udp *)conn; ret = _dns_server_process_udp(udpconn, event, now); } else if (conn->type == DNS_CONN_TYPE_TCP_SERVER) { struct dns_server_conn_tcp_server *tcpserver = (struct dns_server_conn_tcp_server *)conn; ret = _dns_server_tcp_accept(tcpserver, event, now); } else if (conn->type == DNS_CONN_TYPE_TCP_CLIENT) { struct dns_server_conn_tcp_client *tcpclient = (struct dns_server_conn_tcp_client *)conn; ret = _dns_server_process_tcp(tcpclient, event, now); if (ret != 0) { char name[DNS_MAX_CNAME_LEN]; tlog(TLOG_DEBUG, "process TCP packet from %s failed.", get_host_by_addr(name, sizeof(name), (struct sockaddr *)&tcpclient->addr)); } } else if (conn->type == DNS_CONN_TYPE_TLS_SERVER) { struct dns_server_conn_tls_server *tls_server = (struct dns_server_conn_tls_server *)conn; ret = _dns_server_tls_accept(tls_server, event, now); } else if (conn->type == DNS_CONN_TYPE_TLS_CLIENT) { struct dns_server_conn_tls_client *tls_client = (struct dns_server_conn_tls_client *)conn; ret = _dns_server_process_tls(tls_client, event, now); if (ret != 0) { char name[DNS_MAX_CNAME_LEN]; tlog(TLOG_DEBUG, "process TLS packet from %s failed.", get_host_by_addr(name, sizeof(name), (struct sockaddr *)&tls_client->addr)); } } else { tlog(TLOG_ERROR, "unsupported dns server type %d", conn->type); _dns_server_client_close(conn); ret = -1; } _dns_server_conn_release(conn); if (ret == RECV_ERROR_INVALID_PACKET) { ret = 0; } return ret; } static int _dns_server_second_ping_check(struct dns_request *request) { struct dns_ip_address *addr_map = NULL; unsigned long bucket = 0; char ip[DNS_MAX_CNAME_LEN] = {0}; int ret = -1; if (request->has_ping_result) { return ret; } /* start tcping */ pthread_mutex_lock(&request->ip_map_lock); hash_for_each(request->ip_map, bucket, addr_map, node) { switch (addr_map->addr_type) { case DNS_T_A: { _dns_server_request_get(request); snprintf(ip, sizeof(ip), "%d.%d.%d.%d", addr_map->ip_addr[0], addr_map->ip_addr[1], addr_map->ip_addr[2], addr_map->ip_addr[3]); ret = _dns_server_check_speed(request, ip); if (ret != 0) { _dns_server_request_release(request); } } break; case DNS_T_AAAA: { _dns_server_request_get(request); snprintf(ip, sizeof(ip), "[%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x]", addr_map->ip_addr[0], addr_map->ip_addr[1], addr_map->ip_addr[2], addr_map->ip_addr[3], addr_map->ip_addr[4], addr_map->ip_addr[5], addr_map->ip_addr[6], addr_map->ip_addr[7], addr_map->ip_addr[8], addr_map->ip_addr[9], addr_map->ip_addr[10], addr_map->ip_addr[11], addr_map->ip_addr[12], addr_map->ip_addr[13], addr_map->ip_addr[14], addr_map->ip_addr[15]); ret = _dns_server_check_speed(request, ip); if (ret != 0) { _dns_server_request_release(request); } } break; default: break; } } pthread_mutex_unlock(&request->ip_map_lock); return ret; } static int _dns_server_prefetch_domain(struct dns_cache *dns_cache) { /* If there are still hits, continue pre-fetching */ struct dns_server_query_option server_query_option; int hitnum = dns_cache_hitnum_dec_get(dns_cache); if (hitnum <= 0) { return -1; } /* start prefetch domain */ tlog(TLOG_DEBUG, "prefetch by cache %s, qtype %d, ttl %d, hitnum %d", dns_cache->info.domain, dns_cache->info.qtype, dns_cache->info.ttl, hitnum); server_query_option.dns_group_name = dns_cache_get_dns_group_name(dns_cache); server_query_option.server_flags = dns_cache_get_query_flag(dns_cache); server_query_option.ecs_enable_flag = 0; if (_dns_server_prefetch_request(dns_cache->info.domain, dns_cache->info.qtype, &server_query_option, PREFETCH_FLAGS_NO_DUALSTACK) != 0) { tlog(TLOG_ERROR, "prefetch domain %s, qtype %d, failed.", dns_cache->info.domain, dns_cache->info.qtype); return -1; } return 0; } static int _dns_server_prefetch_expired_domain(struct dns_cache *dns_cache) { time_t ttl = _dns_server_expired_cache_ttl(dns_cache); if (ttl <= 1) { return -1; } /* start prefetch domain */ tlog(TLOG_DEBUG, "expired domain, prefetch by cache %s, qtype %d, ttl %llu", dns_cache->info.domain, dns_cache->info.qtype, (unsigned long long)ttl); struct dns_server_query_option server_query_option; server_query_option.dns_group_name = dns_cache_get_dns_group_name(dns_cache); server_query_option.server_flags = dns_cache_get_query_flag(dns_cache); server_query_option.ecs_enable_flag = 0; if (_dns_server_prefetch_request(dns_cache->info.domain, dns_cache->info.qtype, &server_query_option, PREFETCH_FLAGS_EXPIRED) != 0) { tlog(TLOG_DEBUG, "prefetch domain %s, qtype %d, failed.", dns_cache->info.domain, dns_cache->info.qtype); return -1; } return 0; } static int _dns_server_cache_expired(struct dns_cache *dns_cache) { if (dns_cache->info.rcode != DNS_RC_NOERROR) { return -1; } if (dns_conf_prefetch == 1 && _dns_cache_is_specify_packet(dns_cache->info.qtype) != 0) { if (dns_conf_serve_expired == 1) { return _dns_server_prefetch_expired_domain(dns_cache); } else { return _dns_server_prefetch_domain(dns_cache); } } return -1; } static void _dns_server_tcp_idle_check(void) { struct dns_server_conn_head *conn = NULL; struct dns_server_conn_head *tmp = NULL; time_t now = 0; if (dns_conf_tcp_idle_time <= 0) { return; } time(&now); list_for_each_entry_safe(conn, tmp, &server.conn_list, list) { if (conn->type != DNS_CONN_TYPE_TCP_CLIENT && conn->type != DNS_CONN_TYPE_TLS_CLIENT) { continue; } if (conn->last_request_time > now - dns_conf_tcp_idle_time) { continue; } _dns_server_client_close(conn); } } #ifdef TEST static void _dns_server_check_need_exit(void) { static int parent_pid = 0; if (parent_pid == 0) { parent_pid = getppid(); } if (parent_pid != getppid()) { tlog(TLOG_WARN, "parent process exit, exit too."); dns_server_stop(); } } #else #define _dns_server_check_need_exit() #endif static void _dns_server_save_cache_to_file(void) { time_t now; int check_time = dns_conf_cache_checkpoint_time; if (dns_conf_cache_persist == 0 || dns_conf_cachesize <= 0 || dns_conf_cache_checkpoint_time <= 0) { return; } time(&now); if (server.cache_save_pid > 0) { int ret = waitpid(server.cache_save_pid, NULL, WNOHANG); if (ret == server.cache_save_pid) { server.cache_save_pid = 0; } else if (ret < 0) { tlog(TLOG_ERROR, "waitpid failed, errno %d, error info '%s'", errno, strerror(errno)); server.cache_save_pid = 0; } else { if (now - 30 > server.cache_save_time) { kill(server.cache_save_pid, SIGKILL); } return; } } if (check_time < 120) { check_time = 120; } if (now - check_time < server.cache_save_time) { return; } /* server is busy, skip*/ pthread_mutex_lock(&server.request_list_lock); if (list_empty(&server.request_list) != 0) { pthread_mutex_unlock(&server.request_list_lock); return; } pthread_mutex_unlock(&server.request_list_lock); server.cache_save_time = now; int pid = fork(); if (pid == 0) { /* child process */ for (int i = 3; i < 1024; i++) { close(i); } tlog_setlevel(TLOG_OFF); _dns_server_cache_save(1); _exit(0); } else if (pid < 0) { tlog(TLOG_DEBUG, "fork failed, errno %d, error info '%s'", errno, strerror(errno)); return; } server.cache_save_pid = pid; } static void _dns_server_period_run_second(void) { static unsigned int sec = 0; sec++; _dns_server_tcp_idle_check(); _dns_server_check_need_exit(); if (sec % IPV6_READY_CHECK_TIME == 0 && is_ipv6_ready == 0) { dns_server_check_ipv6_ready(); } if (sec % 60 == 0) { if (dns_server_check_update_hosts() == 0) { tlog(TLOG_INFO, "Update host file data"); } } _dns_server_save_cache_to_file(); } static void _dns_server_period_run(unsigned int msec) { struct dns_request *request = NULL; struct dns_request *tmp = NULL; LIST_HEAD(check_list); if ((msec % 10) == 0) { _dns_server_period_run_second(); } unsigned long now = get_tick_count(); pthread_mutex_lock(&server.request_list_lock); list_for_each_entry_safe(request, tmp, &server.request_list, list) { /* Need to use tcping detection speed */ int check_order = request->check_order + 1; if (atomic_read(&request->ip_map_num) == 0 || request->has_soa) { continue; } if (request->send_tick < now - (check_order * DNS_PING_CHECK_INTERVAL) && request->has_ping_result == 0) { _dns_server_request_get(request); list_add_tail(&request->check_list, &check_list); request->check_order++; } } pthread_mutex_unlock(&server.request_list_lock); list_for_each_entry_safe(request, tmp, &check_list, check_list) { _dns_server_second_ping_check(request); list_del_init(&request->check_list); _dns_server_request_release(request); } } static void _dns_server_close_socket(void) { struct dns_server_conn_head *conn = NULL; struct dns_server_conn_head *tmp = NULL; list_for_each_entry_safe(conn, tmp, &server.conn_list, list) { _dns_server_client_close(conn); } } static void _dns_server_close_socket_server(void) { struct dns_server_conn_head *conn = NULL; struct dns_server_conn_head *tmp = NULL; list_for_each_entry_safe(conn, tmp, &server.conn_list, list) { switch (conn->type) { case DNS_CONN_TYPE_HTTPS_SERVER: case DNS_CONN_TYPE_TLS_SERVER: { struct dns_server_conn_tls_server *tls_server = (struct dns_server_conn_tls_server *)conn; if (tls_server->ssl_ctx) { SSL_CTX_free(tls_server->ssl_ctx); tls_server->ssl_ctx = NULL; } _dns_server_client_close(conn); break; } case DNS_CONN_TYPE_UDP_SERVER: case DNS_CONN_TYPE_TCP_SERVER: _dns_server_client_close(conn); break; default: break; } } } int dns_server_run(void) { struct epoll_event events[DNS_MAX_EVENTS + 1]; int num = 0; int i = 0; unsigned long now = {0}; unsigned long last = {0}; unsigned int msec = 0; int sleep = 100; int sleep_time = 0; unsigned long expect_time = 0; sleep_time = sleep; now = get_tick_count() - sleep; last = now; expect_time = now + sleep; while (atomic_read(&server.run)) { now = get_tick_count(); if (sleep_time > 0) { sleep_time -= now - last; if (sleep_time <= 0) { sleep_time = 0; } int cnt = sleep_time / sleep; msec -= cnt; expect_time -= cnt * sleep; sleep_time -= cnt * sleep; } if (now >= expect_time) { msec++; if (last != now) { _dns_server_period_run(msec); } sleep_time = sleep - (now - expect_time); if (sleep_time < 0) { sleep_time = 0; expect_time = now; } /* When server is idle, the sleep time is 1000ms, to reduce CPU usage */ pthread_mutex_lock(&server.request_list_lock); if (list_empty(&server.request_list)) { int cnt = 10 - (msec % 10) - 1; sleep_time += sleep * cnt; msec += cnt; /* sleep to next second */ expect_time += sleep * cnt; } pthread_mutex_unlock(&server.request_list_lock); expect_time += sleep; } last = now; num = epoll_wait(server.epoll_fd, events, DNS_MAX_EVENTS, sleep_time); if (num < 0) { usleep(100000); continue; } if (num == 0) { continue; } for (i = 0; i < num; i++) { struct epoll_event *event = &events[i]; /* read event */ if (event->data.fd == server.event_fd) { uint64_t value; int unused __attribute__((unused)); unused = read(server.event_fd, &value, sizeof(uint64_t)); continue; } struct dns_server_conn_head *conn_head = event->data.ptr; if (conn_head == NULL) { tlog(TLOG_ERROR, "invalid fd\n"); continue; } if (_dns_server_process(conn_head, event, now) != 0) { tlog(TLOG_DEBUG, "dns server process failed."); } } } _dns_server_close_socket_server(); close(server.epoll_fd); server.epoll_fd = -1; return 0; } static struct addrinfo *_dns_server_getaddr(const char *host, const char *port, int type, int protocol) { struct addrinfo hints; struct addrinfo *result = NULL; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = type; hints.ai_protocol = protocol; hints.ai_flags = AI_PASSIVE; if (getaddrinfo(host, port, &hints, &result) != 0) { tlog(TLOG_ERROR, "get addr info failed. %s\n", strerror(errno)); goto errout; } return result; errout: if (result) { freeaddrinfo(result); } return NULL; } int dns_server_start(void) { struct dns_server_conn_head *conn = NULL; list_for_each_entry(conn, &server.conn_list, list) { if (conn->fd <= 0) { continue; } if (_dns_server_epoll_ctl(conn, EPOLL_CTL_ADD, EPOLLIN) != 0) { tlog(TLOG_ERROR, "epoll ctl failed."); return -1; } } return 0; } static int _dns_create_socket(const char *host_ip, int type) { int fd = -1; struct addrinfo *gai = NULL; char port_str[16]; char ip[MAX_IP_LEN]; char host_ip_device[MAX_IP_LEN * 2]; int port = 0; char *host = NULL; int optval = 1; int yes = 1; const int priority = SOCKET_PRIORITY; const int ip_tos = SOCKET_IP_TOS; const char *ifname = NULL; safe_strncpy(host_ip_device, host_ip, sizeof(host_ip_device)); ifname = strstr(host_ip_device, "@"); if (ifname) { *(char *)ifname = '\0'; ifname++; } if (parse_ip(host_ip_device, ip, &port) == 0) { host = ip; } if (port <= 0) { port = DEFAULT_DNS_PORT; } snprintf(port_str, sizeof(port_str), "%d", port); gai = _dns_server_getaddr(host, port_str, type, 0); if (gai == NULL) { tlog(TLOG_ERROR, "get address failed.\n"); goto errout; } fd = socket(gai->ai_family, gai->ai_socktype, gai->ai_protocol); if (fd < 0) { tlog(TLOG_ERROR, "create socket failed, family = %d, type = %d, proto = %d, %s\n", gai->ai_family, gai->ai_socktype, gai->ai_protocol, strerror(errno)); goto errout; } if (type == SOCK_STREAM) { if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) != 0) { tlog(TLOG_ERROR, "set socket opt failed."); goto errout; } setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)); } else { setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &optval, sizeof(optval)); setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &optval, sizeof(optval)); } setsockopt(fd, SOL_SOCKET, SO_PRIORITY, &priority, sizeof(priority)); setsockopt(fd, IPPROTO_IP, IP_TOS, &ip_tos, sizeof(ip_tos)); if (ifname != NULL) { struct ifreq ifr; memset(&ifr, 0, sizeof(struct ifreq)); safe_strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); ioctl(fd, SIOCGIFINDEX, &ifr); if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(struct ifreq)) < 0) { tlog(TLOG_ERROR, "bind socket to device %s failed, %s\n", ifr.ifr_name, strerror(errno)); goto errout; } } if (bind(fd, gai->ai_addr, gai->ai_addrlen) != 0) { tlog(TLOG_ERROR, "bind service %s failed, %s\n", host_ip, strerror(errno)); goto errout; } if (type == SOCK_STREAM) { if (listen(fd, 16) != 0) { tlog(TLOG_ERROR, "listen failed.\n"); goto errout; } } fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); freeaddrinfo(gai); return fd; errout: if (fd > 0) { close(fd); } if (gai) { freeaddrinfo(gai); } return -1; } static int _dns_server_set_flags(struct dns_server_conn_head *head, struct dns_bind_ip *bind_ip) { time(&head->last_request_time); head->server_flags = bind_ip->flags; head->dns_group = bind_ip->group; head->ipset_nftset_rule = &bind_ip->nftset_ipset_rule; atomic_set(&head->refcnt, 0); list_add(&head->list, &server.conn_list); return 0; } static int _dns_server_socket_udp(struct dns_bind_ip *bind_ip) { const char *host_ip = NULL; struct dns_server_conn_udp *conn = NULL; int fd = -1; host_ip = bind_ip->ip; conn = malloc(sizeof(struct dns_server_conn_udp)); if (conn == NULL) { goto errout; } INIT_LIST_HEAD(&conn->head.list); fd = _dns_create_socket(host_ip, SOCK_DGRAM); if (fd <= 0) { goto errout; } conn->head.type = DNS_CONN_TYPE_UDP_SERVER; conn->head.fd = fd; _dns_server_set_flags(&conn->head, bind_ip); _dns_server_conn_get(&conn->head); return 0; errout: if (conn) { free(conn); conn = NULL; } if (fd > 0) { close(fd); } return -1; } static int _dns_server_socket_tcp(struct dns_bind_ip *bind_ip) { const char *host_ip = NULL; struct dns_server_conn_tcp_server *conn = NULL; int fd = -1; const int on = 1; host_ip = bind_ip->ip; conn = malloc(sizeof(struct dns_server_conn_tcp_server)); if (conn == NULL) { goto errout; } INIT_LIST_HEAD(&conn->head.list); fd = _dns_create_socket(host_ip, SOCK_STREAM); if (fd <= 0) { goto errout; } setsockopt(fd, SOL_TCP, TCP_FASTOPEN, &on, sizeof(on)); conn->head.type = DNS_CONN_TYPE_TCP_SERVER; conn->head.fd = fd; _dns_server_set_flags(&conn->head, bind_ip); _dns_server_conn_get(&conn->head); return 0; errout: if (conn) { free(conn); conn = NULL; } if (fd > 0) { close(fd); } return -1; } static int _dns_server_socket_tls_ssl_pass_callback(char *buf, int size, int rwflag, void *userdata) { struct dns_bind_ip *bind_ip = userdata; if (bind_ip->ssl_cert_key_pass == NULL || bind_ip->ssl_cert_key_pass[0] == '\0') { return 0; } safe_strncpy(buf, bind_ip->ssl_cert_key_pass, size); return strlen(buf); } static int _dns_server_socket_tls(struct dns_bind_ip *bind_ip, DNS_CONN_TYPE conn_type) { const char *host_ip = NULL; const char *ssl_cert_file = NULL; const char *ssl_cert_key_file = NULL; struct dns_server_conn_tls_server *conn = NULL; int fd = -1; const SSL_METHOD *method = NULL; SSL_CTX *ssl_ctx = NULL; const int on = 1; host_ip = bind_ip->ip; ssl_cert_file = bind_ip->ssl_cert_file; ssl_cert_key_file = bind_ip->ssl_cert_key_file; if (ssl_cert_file == NULL || ssl_cert_key_file == NULL) { tlog(TLOG_WARN, "no cert or cert key file"); goto errout; } if (ssl_cert_file[0] == '\0' || ssl_cert_key_file[0] == '\0') { tlog(TLOG_WARN, "no cert or cert key file"); goto errout; } conn = malloc(sizeof(struct dns_server_conn_tls_server)); if (conn == NULL) { goto errout; } INIT_LIST_HEAD(&conn->head.list); fd = _dns_create_socket(host_ip, SOCK_STREAM); if (fd <= 0) { goto errout; } setsockopt(fd, SOL_TCP, TCP_FASTOPEN, &on, sizeof(on)); #if (OPENSSL_VERSION_NUMBER >= 0x10100000L) method = TLS_server_method(); if (method == NULL) { goto errout; } #else method = SSLv23_server_method(); #endif ssl_ctx = SSL_CTX_new(method); if (ssl_ctx == NULL) { goto errout; } SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_SERVER | SSL_SESS_CACHE_NO_INTERNAL | SSL_SESS_CACHE_NO_AUTO_CLEAR); SSL_CTX_set_default_passwd_cb(ssl_ctx, _dns_server_socket_tls_ssl_pass_callback); SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, bind_ip); /* Set the key and cert */ if (ssl_cert_file[0] != '\0' && SSL_CTX_use_certificate_chain_file(ssl_ctx, ssl_cert_file) <= 0) { tlog(TLOG_ERROR, "load cert %s failed, %s", ssl_cert_file, ERR_error_string(ERR_get_error(), NULL)); goto errout; } if (ssl_cert_key_file[0] != '\0' && SSL_CTX_use_PrivateKey_file(ssl_ctx, ssl_cert_key_file, SSL_FILETYPE_PEM) <= 0) { tlog(TLOG_ERROR, "load cert key %s failed, %s", ssl_cert_key_file, ERR_error_string(ERR_get_error(), NULL)); goto errout; } conn->head.type = conn_type; conn->head.fd = fd; conn->ssl_ctx = ssl_ctx; _dns_server_set_flags(&conn->head, bind_ip); _dns_server_conn_get(&conn->head); return 0; errout: if (ssl_ctx) { SSL_CTX_free(ssl_ctx); ssl_ctx = NULL; } if (conn) { free(conn); conn = NULL; } if (fd > 0) { close(fd); } return -1; } static int _dns_server_socket(void) { int i = 0; for (i = 0; i < dns_conf_bind_ip_num; i++) { struct dns_bind_ip *bind_ip = &dns_conf_bind_ip[i]; switch (bind_ip->type) { case DNS_BIND_TYPE_UDP: if (_dns_server_socket_udp(bind_ip) != 0) { goto errout; } break; case DNS_BIND_TYPE_TCP: if (_dns_server_socket_tcp(bind_ip) != 0) { goto errout; } break; case DNS_BIND_TYPE_HTTPS: if (_dns_server_socket_tls(bind_ip, DNS_CONN_TYPE_HTTPS_SERVER) != 0) { goto errout; } break; case DNS_BIND_TYPE_TLS: if (_dns_server_socket_tls(bind_ip, DNS_CONN_TYPE_TLS_SERVER) != 0) { goto errout; } break; default: break; } } return 0; errout: return -1; } static int _dns_server_audit_init(void) { char *audit_file = SMARTDNS_AUDIT_FILE; if (dns_conf_audit_enable == 0) { return 0; } if (dns_conf_audit_file[0] != 0) { audit_file = dns_conf_audit_file; } dns_audit = tlog_open(audit_file, dns_conf_audit_size, dns_conf_audit_num, 0, 0); if (dns_audit == NULL) { return -1; } if (dns_conf_audit_file_mode > 0) { tlog_set_permission(dns_audit, dns_conf_audit_file_mode, dns_conf_audit_file_mode); } if (dns_conf_audit_console != 0) { tlog_logscreen(dns_audit, 1); } return 0; } static int _dns_server_cache_init(void) { if (dns_cache_init(dns_conf_cachesize, _dns_server_cache_expired) != 0) { tlog(TLOG_ERROR, "init cache failed."); return -1; } const char *dns_cache_file = dns_conf_get_cache_dir(); if (dns_conf_cache_persist == 2) { uint64_t freespace = get_free_space(dns_cache_file); if (freespace >= CACHE_AUTO_ENABLE_SIZE) { tlog(TLOG_INFO, "auto enable cache persist."); dns_conf_cache_persist = 1; } } if (dns_conf_cachesize <= 0 || dns_conf_cache_persist == 0) { return 0; } if (dns_cache_load(dns_cache_file) != 0) { tlog(TLOG_WARN, "Load cache failed."); return 0; } return 0; } static int _dns_server_cache_save(int check_lock) { const char *dns_cache_file = dns_conf_get_cache_dir(); if (dns_conf_cache_persist == 0 || dns_conf_cachesize <= 0) { if (access(dns_cache_file, F_OK) == 0) { unlink(dns_cache_file); } return 0; } if (dns_cache_save(dns_cache_file, check_lock) != 0) { tlog(TLOG_WARN, "save cache failed."); return -1; } return 0; } static int _dns_server_init_wakeup_event(void) { int fdevent = -1; fdevent = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); if (fdevent < 0) { tlog(TLOG_ERROR, "create eventfd failed, %s\n", strerror(errno)); goto errout; } struct epoll_event event; memset(&event, 0, sizeof(event)); event.events = EPOLLIN | EPOLLERR; event.data.fd = fdevent; if (epoll_ctl(server.epoll_fd, EPOLL_CTL_ADD, fdevent, &event) != 0) { tlog(TLOG_ERROR, "set eventfd failed, %s\n", strerror(errno)); goto errout; } server.event_fd = fdevent; return 0; errout: return -1; } int dns_server_init(void) { pthread_attr_t attr; int epollfd = -1; int ret = -1; _dns_server_check_need_exit(); if (server.epoll_fd > 0) { return -1; } if (_dns_server_cache_init() != 0) { tlog(TLOG_ERROR, "init dns cache filed."); goto errout; } if (_dns_server_audit_init() != 0) { tlog(TLOG_ERROR, "init audit failed."); goto errout; } memset(&server, 0, sizeof(server)); pthread_attr_init(&attr); INIT_LIST_HEAD(&server.conn_list); time(&server.cache_save_time); epollfd = epoll_create1(EPOLL_CLOEXEC); if (epollfd < 0) { tlog(TLOG_ERROR, "create epoll failed, %s\n", strerror(errno)); goto errout; } ret = _dns_server_socket(); if (ret != 0) { tlog(TLOG_ERROR, "create server socket failed.\n"); goto errout; } pthread_mutex_init(&server.request_list_lock, NULL); INIT_LIST_HEAD(&server.request_list); server.epoll_fd = epollfd; atomic_set(&server.run, 1); if (dns_server_start() != 0) { tlog(TLOG_ERROR, "start service failed.\n"); goto errout; } dns_server_check_ipv6_ready(); tlog(TLOG_INFO, "%s", (is_ipv6_ready) ? "IPV6 is ready, enable IPV6 features" : "IPV6 is not ready, disable IPV6 features"); if (_dns_server_init_wakeup_event() != 0) { tlog(TLOG_ERROR, "init wakeup event failed."); goto errout; } return 0; errout: atomic_set(&server.run, 0); if (epollfd) { close(epollfd); } _dns_server_close_socket(); pthread_mutex_destroy(&server.request_list_lock); dns_cache_destroy(); return -1; } void dns_server_stop(void) { atomic_set(&server.run, 0); _dns_server_wakeup_thread(); } void dns_server_exit(void) { if (server.event_fd > 0) { close(server.event_fd); server.event_fd = -1; } if (server.cache_save_pid > 0) { kill(server.cache_save_pid, SIGKILL); server.cache_save_pid = 0; } _dns_server_close_socket(); _dns_server_cache_save(0); _dns_server_request_remove_all(); pthread_mutex_destroy(&server.request_list_lock); dns_cache_destroy(); }