Feature: support local host name & ptr resolve.

This commit is contained in:
Nick Peng
2022-05-21 00:09:18 +08:00
parent dbfe9063e4
commit dae263444f
7 changed files with 584 additions and 39 deletions

View File

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