|
@@ -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();
|
|
|
}
|
|
|
|