浏览代码

Feature: support local host name & ptr resolve.

Nick Peng 3 年之前
父节点
当前提交
dae263444f
共有 7 个文件被更改,包括 584 次插入39 次删除
  1. 1 0
      ReadMe.md
  2. 1 0
      ReadMe_en.md
  3. 357 1
      src/dns_conf.c
  4. 37 0
      src/dns_conf.h
  5. 159 36
      src/dns_server.c
  6. 26 1
      src/util.c
  7. 3 1
      src/util.h

+ 1 - 0
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 | [<qtypeid> \| ...] | 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 |

+ 1 - 0
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

+ 357 - 1
src/dns_conf.c

@@ -28,6 +28,7 @@
 #include <string.h>
 #include <syslog.h>
 #include <unistd.h>
+#include <errno.h>
 
 #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();
 }
 

+ 37 - 0
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
 }

+ 159 - 36
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)

+ 26 - 1
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);

+ 3 - 1
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);