From dae263444f76847da6fbc9ca6e863fff8a371b51 Mon Sep 17 00:00:00 2001 From: Nick Peng Date: Sat, 21 May 2022 00:09:18 +0800 Subject: [PATCH] Feature: support local host name & ptr resolve. --- ReadMe.md | 1 + ReadMe_en.md | 1 + src/dns_conf.c | 358 ++++++++++++++++++++++++++++++++++++++++++++++- src/dns_conf.h | 37 +++++ src/dns_server.c | 195 +++++++++++++++++++++----- src/util.c | 27 +++- src/util.h | 4 +- 7 files changed, 584 insertions(+), 39 deletions(-) diff --git a/ReadMe.md b/ReadMe.md index 4cced7d..feb6633 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -587,6 +587,7 @@ rtt min/avg/max/mdev = 5.954/6.133/6.313/0.195 ms | force-AAAA-SOA | 强制 AAAA 地址返回 SOA | no | [yes\|no] | force-AAAA-SOA yes | | force-qtype-SOA | 强制指定 qtype 返回 SOA | qtype id | [ \| ...] | force-qtype-SOA 65 28 | prefetch-domain | 域名预先获取功能 | no | [yes\|no] | prefetch-domain yes | +| dnsmasq-lease-file | 支持读取dnsmasq dhcp文件解析本地主机名功能 | 无 | dnsmasq dhcp lease文件路径 | dnsmasq-lease-file /var/lib/misc/dnsmasq.leases | | serve-expired | 过期缓存服务功能 | yes | [yes\|no],开启此功能后,如果有请求时尝试回应 TTL 为 0 的过期记录,并发查询记录,以避免查询等待 | | serve-expired-ttl | 过期缓存服务最长超时时间 | 0 | 秒,0 表示停用超时,大于 0 表示指定的超时的秒数 | serve-expired-ttl 0 | | serve-expired-reply-ttl | 回应的过期缓存 TTL | 5 | 秒,0 表示停用超时,大于 0 表示指定的超时的秒数 | serve-expired-reply-ttl 30 | diff --git a/ReadMe_en.md b/ReadMe_en.md index 74cfef3..4149e71 100755 --- a/ReadMe_en.md +++ b/ReadMe_en.md @@ -531,6 +531,7 @@ Note: Merlin firmware is derived from ASUS firmware and can theoretically be use |force-AAAA-SOA|force AAAA query return SOA|no|[yes\|no]|force-AAAA-SOA yes |force-qtype-SOA|force specific qtype return SOA|qtype id|[qtypeid | ...]|force-qtype-SOA 65 28 |prefetch-domain|domain prefetch feature|no|[yes\|no]|prefetch-domain yes +|dnsmasq-lease-file|Support reading dnsmasq dhcp file to resolve local hostname|None|dnsmasq dhcp lease file| dnsmasq-lease-file /var/lib/misc/dnsmasq.leases |serve-expired|Cache serve expired feature|yes|[yes\|no], Attempts to serve old responses from cache with a TTL of 0 in the response without waiting for the actual resolution to finish.|serve-expired yes |serve-expired-ttl|Cache serve expired limite TTL|0|second,0:disable,> 0 seconds after expiration|serve-expired-ttl 0 |serve-expired-reply-ttl|TTL value to use when replying with expired data|5|second,0:disable,> 0 seconds after expiration|serve-expired-reply-ttl 30 diff --git a/src/dns_conf.c b/src/dns_conf.c index 9dd8d00..2ea870b 100644 --- a/src/dns_conf.c +++ b/src/dns_conf.c @@ -28,6 +28,7 @@ #include #include #include +#include #define DEFAULT_DNS_CACHE_SIZE 512 @@ -42,6 +43,14 @@ struct dns_qtype_soa_table dns_qtype_soa_table; /* dns groups */ struct dns_group_table dns_group_table; +struct dns_ptr_table dns_ptr_table; + +char dns_conf_dnsmasq_lease_file[DNS_MAX_PATH]; +time_t dns_conf_dnsmasq_lease_file_time; + +struct dns_hosts_table dns_hosts_table; +int dns_hosts_record_num; + /* server ip/port */ struct dns_bind_ip dns_conf_bind_ip[DNS_MAX_BIND_IP]; int dns_conf_bind_ip_num = 0; @@ -1429,6 +1438,342 @@ errout: return -1; } +static struct dns_ptr *_dns_conf_get_ptr(const char *ptr_domain) +{ + uint32_t key = 0; + struct dns_ptr *ptr = NULL; + + key = hash_string(ptr_domain); + hash_for_each_possible(dns_ptr_table.ptr, ptr, node, key) + { + if (strncmp(ptr->ptr_domain, ptr_domain, DNS_MAX_CNAME_LEN) != 0) { + continue; + } + + return ptr; + } + + ptr = malloc(sizeof(*ptr)); + if (ptr == NULL) { + goto errout; + } + + safe_strncpy(ptr->ptr_domain, ptr_domain, DNS_MAX_PTR_LEN); + hash_add(dns_ptr_table.ptr, &ptr->node, key); + + return ptr; +errout: + if (ptr) { + free(ptr); + } + + return NULL; +} + +static int _conf_ptr_add(const char *hostname, const char *ip) +{ + struct dns_ptr *ptr = NULL; + struct sockaddr_storage addr; + unsigned char *paddr; + socklen_t addr_len = sizeof(addr); + char ptr_domain[DNS_MAX_PTR_LEN]; + + if (getaddr_by_host(ip, (struct sockaddr *)&addr, &addr_len) != 0) { + goto errout; + } + + switch (addr.ss_family) { + case AF_INET: { + struct sockaddr_in *addr_in; + addr_in = (struct sockaddr_in *)&addr; + paddr = (unsigned char *)&(addr_in->sin_addr.s_addr); + snprintf(ptr_domain, sizeof(ptr_domain), "%d.%d.%d.%d.in-addr.arpa", + paddr[3], paddr[2], paddr[1], paddr[0]); + } break; + case AF_INET6: { + struct sockaddr_in6 *addr_in6; + addr_in6 = (struct sockaddr_in6 *)&addr; + if (IN6_IS_ADDR_V4MAPPED(&addr_in6->sin6_addr)) { + paddr = addr_in6->sin6_addr.s6_addr + 12; + snprintf(ptr_domain, sizeof(ptr_domain), "%d.%d.%d.%d.in-addr.arpa", + paddr[3], paddr[2], paddr[1], paddr[0]); + } else { + paddr = addr_in6->sin6_addr.s6_addr; + snprintf( + ptr_domain, sizeof(ptr_domain), + "%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", + paddr[15] & 0xF, (paddr[15] >> 4) & 0xF, paddr[14] & 0xF, + (paddr[14] >> 4) & 0xF, paddr[13] & 0xF, (paddr[13] >> 4) & 0xF, + paddr[12] & 0xF, (paddr[12] >> 4) & 0xF, paddr[11] & 0xF, + (paddr[11] >> 4) & 0xF, paddr[10] & 0xF, (paddr[10] >> 4) & 0xF, + paddr[9] & 0xF, (paddr[9] >> 4) & 0xF, paddr[8] & 0xF, + (paddr[8] >> 4) & 0xF, paddr[7] & 0xF, (paddr[7] >> 4) & 0xF, + paddr[6] & 0xF, (paddr[6] >> 4) & 0xF, paddr[5] & 0xF, + (paddr[5] >> 4) & 0xF, paddr[4] & 0xF, (paddr[4] >> 4) & 0xF, + paddr[3] & 0xF, (paddr[3] >> 4) & 0xF, paddr[2] & 0xF, + (paddr[2] >> 4) & 0xF, paddr[1] & 0xF, (paddr[1] >> 4) & 0xF, + paddr[0] & 0xF, (paddr[0] >> 4) & 0xF); + } + } break; + default: + goto errout; + break; + } + + ptr = _dns_conf_get_ptr(ptr_domain); + if (ptr == NULL) { + goto errout; + } + + safe_strncpy(ptr->hostname, hostname, DNS_MAX_CNAME_LEN); + + return 0; + +errout: + return -1; +} + +static void _config_ptr_table_destroy(void) +{ + struct dns_ptr *ptr = NULL; + struct hlist_node *tmp = NULL; + int i; + + hash_for_each_safe(dns_ptr_table.ptr, i, tmp, ptr, node) + { + hlist_del_init(&ptr->node); + free(ptr); + } +} + +static struct dns_hosts *_dns_conf_get_hosts(const char *hostname, int dns_type) +{ + uint32_t key = 0; + struct dns_hosts *host = NULL; + char hostname_lower[DNS_MAX_CNAME_LEN]; + + key = hash_string(to_lower_case(hostname_lower, hostname, DNS_MAX_CNAME_LEN)); + key = jhash(&dns_type, sizeof(dns_type), key); + hash_for_each_possible(dns_hosts_table.hosts, host, node, key) + { + if (host->dns_type != dns_type) { + continue; + } + if (strncmp(host->domain, hostname_lower, DNS_MAX_CNAME_LEN) != 0) { + continue; + } + + return host; + } + + host = malloc(sizeof(*host)); + if (host == NULL) { + goto errout; + } + + safe_strncpy(host->domain, hostname_lower, DNS_MAX_CNAME_LEN); + host->dns_type = dns_type; + host->is_soa = 1; + hash_add(dns_hosts_table.hosts, &host->node, key); + + return host; +errout: + if (host) { + free(host); + } + + return NULL; +} + +static int _conf_host_add(const char *hostname, const char *ip, dns_hosts_type host_type) +{ + struct dns_hosts *host = NULL; + struct dns_hosts *host_other __attribute__((unused));; + struct sockaddr_storage addr; + socklen_t addr_len = sizeof(addr); + int dns_type = 0; + int dns_type_other = 0; + + if (getaddr_by_host(ip, (struct sockaddr *)&addr, &addr_len) != 0) { + goto errout; + } + + switch (addr.ss_family) { + case AF_INET: + dns_type = DNS_T_A; + dns_type_other = DNS_T_AAAA; + break; + case AF_INET6: { + struct sockaddr_in6 *addr_in6; + addr_in6 = (struct sockaddr_in6 *)&addr; + if (IN6_IS_ADDR_V4MAPPED(&addr_in6->sin6_addr)) { + dns_type = DNS_T_A; + dns_type_other = DNS_T_AAAA; + } else { + dns_type = DNS_T_AAAA; + dns_type_other = DNS_T_A; + } + } break; + default: + goto errout; + break; + } + + host = _dns_conf_get_hosts(hostname, dns_type); + if (host == NULL) { + goto errout; + } + + /* add this to return SOA when addr is not exist */ + host_other = _dns_conf_get_hosts(hostname, dns_type_other); + + host->host_type = host_type; + + switch (addr.ss_family) { + case AF_INET: { + struct sockaddr_in *addr_in; + addr_in = (struct sockaddr_in *)&addr; + memcpy(host->ipv4_addr, &addr_in->sin_addr.s_addr, 4); + host->is_soa = 0; + } break; + case AF_INET6: { + struct sockaddr_in6 *addr_in6; + addr_in6 = (struct sockaddr_in6 *)&addr; + if (IN6_IS_ADDR_V4MAPPED(&addr_in6->sin6_addr)) { + memcpy(host->ipv4_addr, addr_in6->sin6_addr.s6_addr + 12, 4); + } else { + memcpy(host->ipv6_addr, addr_in6->sin6_addr.s6_addr, 16); + } + host->is_soa = 0; + } break; + default: + goto errout; + } + + dns_hosts_record_num++; + return 0; + +errout: + return -1; +} + +static int _conf_dhcp_lease_dnsmasq_add(const char *file) +{ + FILE *fp = NULL; + char line[MAX_LINE_LEN]; + char ip[DNS_MAX_IPLEN]; + char hostname[DNS_MAX_CNAME_LEN]; + int ret = 0; + int line_no = 0; + int filed_num; + + fp = fopen(file, "r"); + if (fp == NULL) { + tlog(TLOG_WARN, "open file %s error, %s", file, strerror(errno)); + return 0; + } + + line_no = 0; + while (fgets(line, MAX_LINE_LEN, fp)) { + line_no++; + filed_num = sscanf(line, "%*s %*s %64s %256s %*s", ip, hostname); + if (filed_num <= 0) { + continue; + } + + if (strncmp(hostname, "*", DNS_MAX_CNAME_LEN) == 0) { + continue; + } + + ret = _conf_host_add(hostname, ip, DNS_HOST_TYPE_DNSMASQ); + if (ret != 0) { + tlog(TLOG_WARN, "add host %s/%s at %d failed", hostname, ip, line_no); + } + + ret = _conf_ptr_add(hostname, ip); + if (ret != 0) { + tlog(TLOG_WARN, "add ptr %s/%s at %d failed.", hostname, ip, line_no); + } + } + + fclose(fp); + + return 0; +} + +static int _conf_dhcp_lease_dnsmasq_file(void *data, int argc, char *argv[]) +{ + struct stat statbuf; + + if (argc < 1) { + return -1; + } + + safe_strncpy(dns_conf_dnsmasq_lease_file, argv[1], DNS_MAX_PATH); + if (_conf_dhcp_lease_dnsmasq_add(argv[1]) != 0) { + return -1; + } + + if (stat(dns_conf_dnsmasq_lease_file, &statbuf) != 0) { + return 0; + } + + dns_conf_dnsmasq_lease_file_time = statbuf.st_mtime; + return 0; +} + +static int _conf_hosts_file(void *data, int argc, char *argv[]) +{ + return 0; +} + +static void _config_host_table_destroy(void) +{ + struct dns_hosts *host = NULL; + struct hlist_node *tmp = NULL; + int i; + + hash_for_each_safe(dns_hosts_table.hosts, i, tmp, host, node) + { + hlist_del_init(&host->node); + free(host); + } + + dns_hosts_record_num = 0; +} + +int dns_server_check_update_hosts(void) +{ + struct stat statbuf; + time_t now; + + if (stat(dns_conf_dnsmasq_lease_file, &statbuf) != 0) { + return -1; + } + + if (dns_conf_dnsmasq_lease_file_time == statbuf.st_mtime) { + return -1; + } + + time(&now); + + if (now - statbuf.st_mtime < 30) { + return -1; + } + + _config_ptr_table_destroy(); + _config_host_table_destroy(); + + if (_conf_dhcp_lease_dnsmasq_add(dns_conf_dnsmasq_lease_file) != 0) { + return -1; + } + + dns_conf_dnsmasq_lease_file_time = statbuf.st_mtime; + return 0; +} + static int _config_log_level(void *data, int argc, char *argv[]) { /* read log level and set */ @@ -1497,6 +1842,8 @@ static struct config_item _config_item[] = { CONF_CUSTOM("ignore-ip", _conf_ip_ignore, NULL), CONF_CUSTOM("edns-client-subnet", _conf_edns_client_subnet, NULL), CONF_CUSTOM("domain-rules", _conf_domain_rules, NULL), + CONF_CUSTOM("dnsmasq-lease-file", _conf_dhcp_lease_dnsmasq_file, NULL), + CONF_CUSTOM("hosts-file", _conf_hosts_file, NULL), CONF_STRING("ca-file", (char *)&dns_conf_ca_file, DNS_MAX_PATH), CONF_STRING("ca-path", (char *)&dns_conf_ca_path, DNS_MAX_PATH), CONF_CUSTOM("conf-file", config_addtional_file, NULL), @@ -1520,10 +1867,15 @@ static int _conf_printf(const char *file, int lineno, int ret) int config_addtional_file(void *data, int argc, char *argv[]) { - char *conf_file = argv[1]; + char *conf_file; char file_path[DNS_MAX_PATH]; char file_path_dir[DNS_MAX_PATH]; + if (argc < 1) { + return -1; + } + + conf_file = argv[1]; if (conf_file[0] != '/') { safe_strncpy(file_path_dir, conf_get_conf_file(), DNS_MAX_PATH); dirname(file_path_dir); @@ -1563,6 +1915,8 @@ static int _dns_server_load_conf_init(void) hash_init(dns_ipset_table.ipset); hash_init(dns_qtype_soa_table.qtype); hash_init(dns_group_table.group); + hash_init(dns_hosts_table.hosts); + hash_init(dns_ptr_table.ptr); return 0; } @@ -1574,6 +1928,8 @@ void dns_server_load_exit(void) Destroy_Radix(dns_conf_address_rule.ipv6, _config_address_destroy, NULL); _config_ipset_table_destroy(); _config_group_table_destroy(); + _config_ptr_table_destroy(); + _config_host_table_destroy(); _config_qtype_soa_table_destroy(); } diff --git a/src/dns_conf.h b/src/dns_conf.h index fec56a4..6874ab3 100644 --- a/src/dns_conf.h +++ b/src/dns_conf.h @@ -35,6 +35,7 @@ extern "C" { #define DNS_MAX_BIND_IP 16 #define DNS_MAX_SERVERS 64 #define DNS_MAX_SERVER_NAME_LEN 128 +#define DNS_MAX_PTR_LEN 128 #define DNS_MAX_IPSET_NAMELEN 32 #define DNS_GROUP_NAME_LEN 32 #define DNS_NAX_GROUP_NUMBER 16 @@ -145,6 +146,40 @@ struct dns_group_table { }; extern struct dns_group_table dns_group_table; +struct dns_ptr { + struct hlist_node node; + char ptr_domain[DNS_MAX_PTR_LEN]; + char hostname[DNS_MAX_CNAME_LEN]; +}; + +struct dns_ptr_table { + DECLARE_HASHTABLE(ptr, 16); +}; +extern struct dns_ptr_table dns_ptr_table; + +typedef enum dns_hosts_type { + DNS_HOST_TYPE_HOST = 0, + DNS_HOST_TYPE_DNSMASQ = 1, +} dns_hosts_type; + +struct dns_hosts { + struct hlist_node node; + char domain[DNS_MAX_CNAME_LEN]; + dns_hosts_type host_type; + int dns_type; + int is_soa; + union { + unsigned char ipv4_addr[DNS_RR_A_LEN]; + unsigned char ipv6_addr[DNS_RR_AAAA_LEN]; + }; +}; + +struct dns_hosts_table { + DECLARE_HASHTABLE(hosts, 16); +}; +extern struct dns_hosts_table dns_hosts_table; +extern int dns_hosts_record_num; + struct dns_servers { char server[DNS_MAX_IPLEN]; unsigned short port; @@ -270,6 +305,8 @@ void dns_server_load_exit(void); int dns_server_load_conf(const char *file); +int dns_server_check_update_hosts(void); + extern int config_addtional_file(void *data, int argc, char *argv[]); #ifdef __cpluscplus } diff --git a/src/dns_server.c b/src/dns_server.c index fea33ec..767bd1c 100644 --- a/src/dns_server.c +++ b/src/dns_server.c @@ -192,6 +192,7 @@ struct dns_request { 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]; @@ -639,32 +640,7 @@ static int _dns_add_rrs(struct dns_server_post_context *context) char *domain = request->domain; if (request->has_ptr) { /* add PTR record */ - char hostname[DNS_MAX_CNAME_LEN]; - if (dns_conf_server_name[0] == 0) { - /* get local host name */ - if (getdomainname(hostname, DNS_MAX_CNAME_LEN) != 0) { - if (gethostname(hostname, DNS_MAX_CNAME_LEN) != 0) { - return -1; - } - } - - /* get host name again */ - if (strncmp(hostname, "(none)", DNS_MAX_CNAME_LEN) == 0) { - if (gethostname(hostname, DNS_MAX_CNAME_LEN) != 0) { - return -1; - } - } - - /* if hostname is (none), return smartdns */ - if (strncmp(hostname, "(none)", DNS_MAX_CNAME_LEN) == 0) { - safe_strncpy(hostname, "smartdns", DNS_MAX_CNAME_LEN); - } - } else { - /* return configured server name */ - safe_strncpy(hostname, dns_conf_server_name, DNS_MAX_CNAME_LEN); - } - - ret = dns_add_PTR(context->packet, DNS_RRS_AN, request->domain, 30, hostname); + ret = dns_add_PTR(context->packet, DNS_RRS_AN, request->domain, 30, request->ptr_hostname); } /* add CNAME record */ @@ -2478,7 +2454,35 @@ static int dns_server_resolve_callback(char *domain, dns_result_type rtype, unsi return 0; } -static int _dns_server_process_ptr(struct dns_request *request) + +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_CNAME_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; @@ -2530,19 +2534,21 @@ static int _dns_server_process_ptr(struct dns_request *request) break; } - if (strstr(request->domain, reverse_addr) != NULL) { + if (strncmp(request->domain, reverse_addr, DNS_MAX_CNAME_LEN) == 0) { found = 1; break; } } /* Determine if the smartdns service is in effect. */ - if (strstr(request->domain, "0.0.0.0.in-addr.arpa") != NULL) { + if (strncmp(request->domain, "0.0.0.0.in-addr.arpa", DNS_MAX_CNAME_LEN) == + 0) { found = 1; } /* Determine if the smartdns service is in effect. */ - if (found == 0 && strncmp(request->domain, "smartdns", sizeof("smartdns")) == 0) { + if (found == 0 && + strncmp(request->domain, "smartdns", sizeof("smartdns")) == 0) { found = 1; } @@ -2550,13 +2556,33 @@ static int _dns_server_process_ptr(struct dns_request *request) goto errout; } - request->rcode = DNS_RC_NOERROR; + char hostname[DNS_MAX_CNAME_LEN]; + if (dns_conf_server_name[0] == 0) { + /* get local host name */ + if (getdomainname(hostname, DNS_MAX_CNAME_LEN) != 0) { + if (gethostname(hostname, DNS_MAX_CNAME_LEN) != 0) { + return -1; + } + } + + /* get host name again */ + if (strncmp(hostname, "(none)", DNS_MAX_CNAME_LEN) == 0) { + if (gethostname(hostname, DNS_MAX_CNAME_LEN) != 0) { + return -1; + } + } + + /* if hostname is (none), return smartdns */ + if (strncmp(hostname, "(none)", DNS_MAX_CNAME_LEN) == 0) { + safe_strncpy(hostname, "smartdns", DNS_MAX_CNAME_LEN); + } + } else { + /* return configured server name */ + safe_strncpy(hostname, dns_conf_server_name, DNS_MAX_CNAME_LEN); + } + request->has_ptr = 1; - struct dns_server_post_context context; - _dns_server_post_context_init(&context, request); - context.do_reply = 1; - context.do_audit = 0; - _dns_request_post(&context); + safe_strncpy(request->ptr_hostname, hostname, DNS_MAX_CNAME_LEN); freeifaddrs(ifaddr); return 0; @@ -2567,6 +2593,28 @@ errout: 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; + } + + return -1; + +reply_exit: + request->rcode = DNS_RC_NOERROR; + struct dns_server_post_context context; + _dns_server_post_context_init(&context, request); + context.do_reply = 1; + context.do_audit = 0; + _dns_request_post(&context); + return 0; +} + static void _dns_server_log_rule(const char *domain, enum domain_rule rule_type, unsigned char *rule_key, int rule_key_len) { @@ -3199,6 +3247,71 @@ static void _dns_server_check_set_passthrough(struct dns_request *request) } } +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->ipv4_addr, host->ipv4_addr, DNS_RR_A_LEN); + request->ttl_v4 = 600; + request->has_ipv4 = 1; + break; + case DNS_T_AAAA: + memcpy(request->ipv6_addr, host->ipv6_addr, DNS_RR_AAAA_LEN); + request->ttl_v6 = 600; + request->has_ipv6 = 1; + break; + default: + goto errout; + break; + } + + request->rcode = DNS_RC_NOERROR; + 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_do_query(struct dns_request *request, const char *domain, int qtype) { int ret = -1; @@ -3219,6 +3332,10 @@ static int _dns_server_do_query(struct dns_request *request, const char *domain, group_name = dns_group; } + if (_dns_server_process_host(request) == 0) { + goto clean_exit; + } + _dns_server_set_dualstack_selection(request); if (_dns_server_process_special_query(request) == 0) { @@ -3873,6 +3990,12 @@ static void _dns_server_period_run_second(void) 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"); + } + } } static void _dns_server_period_run(void) diff --git a/src/util.c b/src/util.c index 1a03837..79cec32 100644 --- a/src/util.c +++ b/src/util.c @@ -133,7 +133,7 @@ errout: return NULL; } -int getaddr_by_host(char *host, struct sockaddr *addr, socklen_t *addr_len) +int getaddr_by_host(const char *host, struct sockaddr *addr, socklen_t *addr_len) { struct addrinfo hints; struct addrinfo *result = NULL; @@ -473,6 +473,31 @@ char *reverse_string(char *output, const char *input, int len, int to_lower_case return begin; } +char *to_lower_case(char *output, const char *input, int len) +{ + char *begin = output; + int i = 0; + if (len <= 0) { + *output = 0; + return output; + } + + len--; + while (i < len && *(input + i) != '\0') { + *output = *(input + i); + if (*output >= 'A' && *output <= 'Z') { + /* To lower case */ + *output = *output + 32; + } + output++; + i++; + } + + *output = 0; + + return begin; +} + static inline void _ipset_add_attr(struct nlmsghdr *netlink_head, uint16_t type, size_t len, const void *data) { struct ipset_netlink_attr *attr = (void *)netlink_head + NETLINK_ALIGN(netlink_head->nlmsg_len); diff --git a/src/util.h b/src/util.h index e7e5622..7aa945b 100644 --- a/src/util.h +++ b/src/util.h @@ -49,7 +49,7 @@ unsigned long get_tick_count(void); char *gethost_by_addr(char *host, int maxsize, struct sockaddr *addr); -int getaddr_by_host(char *host, struct sockaddr *addr, socklen_t *addr_len); +int getaddr_by_host(const char *host, struct sockaddr *addr, socklen_t *addr_len); int getsocknet_inet(int fd, struct sockaddr *addr, socklen_t *addr_len); @@ -65,6 +65,8 @@ int set_fd_nonblock(int fd, int nonblock); char *reverse_string(char *output, const char *input, int len, int to_lower_case); +char *to_lower_case(char *output, const char *input, int len); + void print_stack(void); int ipset_add(const char *ipsetname, const unsigned char addr[], int addr_len, unsigned long timeout);