553 lines
11 KiB
C
553 lines
11 KiB
C
#include "conf.h"
|
|
#include "list.h"
|
|
#include "rbtree.h"
|
|
#include "tlog.h"
|
|
#include "util.h"
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#define MAX_LINE_LEN 1024
|
|
#define MAX_KEY_LEN 64
|
|
|
|
#define DEFAULT_DNS_CACHE_SIZE 512
|
|
|
|
char dns_conf_server_ip[DNS_MAX_IPLEN];
|
|
int dns_conf_cachesize = DEFAULT_DNS_CACHE_SIZE;
|
|
int dns_conf_prefetch = 0;
|
|
struct dns_servers dns_conf_servers[DNS_MAX_SERVERS];
|
|
struct dns_bogus_nxdomain dns_conf_bogus_nxdomain;
|
|
char dns_conf_server_name[DNS_MAX_CONF_CNAME_LEN];
|
|
int dns_conf_server_num;
|
|
int dns_conf_log_level = TLOG_ERROR;
|
|
char dns_conf_log_file[DNS_MAX_PATH];
|
|
int dns_conf_log_size = 1024 * 1024;
|
|
int dns_conf_log_num = 8;
|
|
|
|
art_tree dns_conf_address;
|
|
int dns_conf_rr_ttl;
|
|
int dns_conf_rr_ttl_min;
|
|
int dns_conf_rr_ttl_max;
|
|
|
|
int load_conf_file(const char *file);
|
|
|
|
int config_bind(char *value)
|
|
{
|
|
/* server bind address */
|
|
strncpy(dns_conf_server_ip, value, DNS_MAX_IPLEN);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int config_server_name(char *value)
|
|
{
|
|
strncpy(dns_conf_server_name, value, DNS_MAX_CONF_CNAME_LEN);
|
|
return 0;
|
|
}
|
|
|
|
int config_server(char *value, dns_server_type_t type)
|
|
{
|
|
int index = dns_conf_server_num;
|
|
struct dns_servers *server;
|
|
int port = -1;
|
|
|
|
if (index >= DNS_MAX_SERVERS) {
|
|
tlog(TLOG_ERROR, "exceeds max server number");
|
|
return -1;
|
|
}
|
|
|
|
server = &dns_conf_servers[index];
|
|
/* parse ip, port from value */
|
|
if (parse_ip(value, server->server, &port) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
/* if port is not defined, set port to default 53 */
|
|
if (port == PORT_NOT_DEFINED) {
|
|
port = DEFAULT_DNS_PORT;
|
|
}
|
|
|
|
server->type = type;
|
|
server->port = port;
|
|
dns_conf_server_num++;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int config_address_iter_cb(void *data, const unsigned char *key, uint32_t key_len, void *value)
|
|
{
|
|
free(value);
|
|
return 0;
|
|
}
|
|
|
|
void config_address_destroy(void)
|
|
{
|
|
art_iter(&dns_conf_address, config_address_iter_cb, 0);
|
|
art_tree_destroy(&dns_conf_address);
|
|
}
|
|
|
|
int config_address(char *value)
|
|
{
|
|
struct dns_address *address = NULL;
|
|
struct dns_address *oldaddress;
|
|
char ip[MAX_IP_LEN];
|
|
char domain_key[DNS_MAX_CONF_CNAME_LEN];
|
|
char domain[DNS_MAX_CONF_CNAME_LEN];
|
|
char *begin = NULL;
|
|
char *end = NULL;
|
|
int len = 0;
|
|
int port;
|
|
struct sockaddr_storage addr;
|
|
socklen_t addr_len = sizeof(addr);
|
|
char type = '4';
|
|
|
|
begin = strstr(value, "/");
|
|
if (begin == NULL) {
|
|
goto errout;
|
|
}
|
|
|
|
begin++;
|
|
end = strstr(begin, "/");
|
|
if (end == NULL) {
|
|
goto errout;
|
|
}
|
|
|
|
address = malloc(sizeof(*address));
|
|
if (address == NULL) {
|
|
goto errout;
|
|
}
|
|
|
|
/* remove prefix . */
|
|
while (*begin == '.') {
|
|
begin++;
|
|
}
|
|
|
|
memset(address, 0, sizeof(*address));
|
|
len = end - begin;
|
|
memcpy(domain + 1, begin, len);
|
|
|
|
/* add dot for subdomain */
|
|
domain[0] = '.';
|
|
len++;
|
|
|
|
domain[len] = 0;
|
|
reverse_string(domain_key + 1, domain, len);
|
|
|
|
if (parse_ip(end + 1, ip, &port) != 0) {
|
|
goto errout;
|
|
}
|
|
|
|
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;
|
|
memcpy(address->ipv4_addr, &addr_in->sin_addr.s_addr, 4);
|
|
address->addr_type = DNS_T_A;
|
|
type = '4';
|
|
} 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(address->ipv4_addr, addr_in6->sin6_addr.s6_addr + 12, 4);
|
|
address->addr_type = DNS_T_A;
|
|
type = '4';
|
|
} else {
|
|
memcpy(address->ipv6_addr, addr_in6->sin6_addr.s6_addr, 16);
|
|
address->addr_type = DNS_T_AAAA;
|
|
type = '6';
|
|
}
|
|
} break;
|
|
default:
|
|
goto errout;
|
|
}
|
|
|
|
domain_key[0] = type;
|
|
len++;
|
|
oldaddress = art_insert(&dns_conf_address, (unsigned char *)domain_key, len, address);
|
|
if (oldaddress) {
|
|
free(oldaddress);
|
|
}
|
|
|
|
return 0;
|
|
errout:
|
|
if (address) {
|
|
free(address);
|
|
}
|
|
|
|
tlog(TLOG_ERROR, "add address %s failed", value);
|
|
return 0;
|
|
}
|
|
|
|
int config_server_udp(char *value)
|
|
{
|
|
return config_server(value, DNS_SERVER_UDP);
|
|
}
|
|
|
|
int config_server_tcp(char *value)
|
|
{
|
|
return config_server(value, DNS_SERVER_TCP);
|
|
}
|
|
|
|
int config_server_http(char *value)
|
|
{
|
|
return config_server(value, DNS_SERVER_HTTP);
|
|
}
|
|
|
|
int config_cache_size(char *value)
|
|
{
|
|
/* read dns cache size */
|
|
int cache_size = atoi(value);
|
|
if (cache_size < 0) {
|
|
return -1;
|
|
}
|
|
|
|
dns_conf_cachesize = cache_size;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int config_cache_prefetch_domain(char *value)
|
|
{
|
|
/* read dns cache size */
|
|
if (strncmp("yes", value, sizeof("yes")) == 0 || strncmp("YES", value, sizeof("YES")) == 0) {
|
|
dns_conf_prefetch = 1;
|
|
} else if (strncmp("no", value, sizeof("no")) == 0 || strncmp("NO", value, sizeof("NO")) == 0) {
|
|
dns_conf_prefetch = 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int config_log_level(char *value)
|
|
{
|
|
/* read log level and set */
|
|
if (strncmp("debug", value, MAX_LINE_LEN) == 0) {
|
|
dns_conf_log_level = TLOG_DEBUG;
|
|
} else if (strncmp("info", value, MAX_LINE_LEN) == 0) {
|
|
dns_conf_log_level = TLOG_INFO;
|
|
} else if (strncmp("warn", value, MAX_LINE_LEN) == 0) {
|
|
dns_conf_log_level = TLOG_WARN;
|
|
} else if (strncmp("error", value, MAX_LINE_LEN) == 0) {
|
|
dns_conf_log_level = TLOG_ERROR;
|
|
} else {
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int config_log_file(char *value)
|
|
{
|
|
/* read dns cache size */
|
|
strncpy(dns_conf_log_file, value, DNS_MAX_PATH);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int config_log_size(char *value)
|
|
{
|
|
/* read dns cache size */
|
|
int base = 1;
|
|
|
|
if (strstr(value, "k") || strstr(value, "K")) {
|
|
base = 1024;
|
|
} else if (strstr(value, "m") || strstr(value, "M")) {
|
|
base = 1024 * 1024;
|
|
} else if (strstr(value, "g") || strstr(value, "G")) {
|
|
base = 1024 * 1024 * 1024;
|
|
}
|
|
|
|
int size = atoi(value);
|
|
if (size < 0) {
|
|
return -1;
|
|
}
|
|
|
|
dns_conf_log_size = size * base;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int config_log_num(char *value)
|
|
{
|
|
/* read dns cache size */
|
|
int num = atoi(value);
|
|
if (num < 0) {
|
|
return -1;
|
|
}
|
|
|
|
dns_conf_log_num = num;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int config_rr_ttl(char *value)
|
|
{
|
|
/* read dns cache size */
|
|
int ttl = atoi(value);
|
|
if (ttl < 0) {
|
|
return -1;
|
|
}
|
|
|
|
dns_conf_rr_ttl = ttl;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int config_rr_ttl_min(char *value)
|
|
{
|
|
/* read dns cache size */
|
|
int ttl = atoi(value);
|
|
if (ttl < 0) {
|
|
return -1;
|
|
}
|
|
|
|
dns_conf_rr_ttl_min = ttl;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int config_rr_ttl_max(char *value)
|
|
{
|
|
/* read dns cache size */
|
|
int ttl = atoi(value);
|
|
if (ttl < 0) {
|
|
return -1;
|
|
}
|
|
|
|
dns_conf_rr_ttl_max = ttl;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int dns_bogus_nxdomain_exists(unsigned char *ip, dns_type_t addr_type)
|
|
{
|
|
uint32_t key = 0;
|
|
struct dns_bogus_ip_address *ip_addr = NULL;
|
|
int addr_len = 0;
|
|
|
|
if (addr_type == DNS_T_A) {
|
|
addr_len = 4;
|
|
} else if (addr_type == DNS_T_AAAA) {
|
|
addr_len = 16;
|
|
} else {
|
|
return -1;
|
|
}
|
|
|
|
key = jhash(ip, addr_len, 0);
|
|
hash_for_each_possible(dns_conf_bogus_nxdomain.ip_hash, ip_addr, node, key)
|
|
{
|
|
if (addr_type == DNS_T_A) {
|
|
if (memcmp(ip_addr->ipv4_addr, ip, addr_len) == 0) {
|
|
return 0;
|
|
}
|
|
} else if (addr_type == DNS_T_AAAA) {
|
|
if (memcmp(ip_addr->ipv6_addr, ip, addr_len) == 0) {
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
void conf_bogus_nxdomain_destroy(void)
|
|
{
|
|
struct dns_bogus_ip_address *ip_addr = NULL;
|
|
struct hlist_node *tmp = NULL;
|
|
int i;
|
|
|
|
hash_for_each_safe(dns_conf_bogus_nxdomain.ip_hash, i, tmp, ip_addr, node)
|
|
{
|
|
hlist_del_init(&ip_addr->node);
|
|
free(ip_addr);
|
|
}
|
|
}
|
|
|
|
int conf_bogus_nxdomain(char *value)
|
|
{
|
|
struct dns_bogus_ip_address *ip_addr = NULL;
|
|
char ip[MAX_IP_LEN];
|
|
int port;
|
|
int ret = -1;
|
|
struct sockaddr_storage addr;
|
|
socklen_t addr_len = sizeof(addr);
|
|
uint32_t key;
|
|
|
|
ip_addr = malloc(sizeof(*ip_addr));
|
|
if (ip_addr == NULL) {
|
|
goto errout;
|
|
}
|
|
memset(ip_addr, 0, sizeof(*ip_addr));
|
|
|
|
if (parse_ip(value, ip, &port) != 0) {
|
|
goto errout;
|
|
}
|
|
|
|
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;
|
|
memcpy(ip_addr->ipv4_addr, &addr_in->sin_addr.s_addr, 4);
|
|
ip_addr->addr_type = DNS_T_A;
|
|
addr_len = 4;
|
|
} 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(ip_addr->ipv4_addr, addr_in6->sin6_addr.s6_addr + 12, 4);
|
|
ip_addr->addr_type = DNS_T_A;
|
|
addr_len = 4;
|
|
} else {
|
|
memcpy(ip_addr->ipv6_addr, addr_in6->sin6_addr.s6_addr, 16);
|
|
addr_len = 16;
|
|
}
|
|
} break;
|
|
default:
|
|
goto errout;
|
|
}
|
|
|
|
if (dns_bogus_nxdomain_exists(ip_addr->addr, ip_addr->addr_type) == 0) {
|
|
ret = 0;
|
|
goto errout;
|
|
}
|
|
|
|
key = jhash(ip_addr->addr, addr_len, 0);
|
|
hash_add(dns_conf_bogus_nxdomain.ip_hash, &ip_addr->node, key);
|
|
|
|
return 0;
|
|
errout:
|
|
if (ip_addr) {
|
|
free(ip_addr);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int config_addtional_file(char *value)
|
|
{
|
|
char *file_path = value;
|
|
|
|
if (access(file_path, R_OK) != 0) {
|
|
tlog(TLOG_WARN, "conf file %s is not readable.", file_path);
|
|
return 0;
|
|
}
|
|
|
|
return load_conf_file(file_path);
|
|
}
|
|
|
|
struct config_item {
|
|
const char *item;
|
|
int (*item_func)(char *value);
|
|
};
|
|
|
|
struct config_item config_item[] = {
|
|
{"server-name", config_server_name},
|
|
{"bind", config_bind},
|
|
{"server", config_server_udp},
|
|
{"address", config_address},
|
|
{"server-tcp", config_server_tcp},
|
|
{"server-http", config_server_http},
|
|
{"cache-size", config_cache_size},
|
|
{"prefetch-domain", config_cache_prefetch_domain},
|
|
{"log-level", config_log_level},
|
|
{"log-file", config_log_file},
|
|
{"log-size", config_log_size},
|
|
{"log-num", config_log_num},
|
|
{"rr-ttl", config_rr_ttl},
|
|
{"rr-ttl-min", config_rr_ttl_min},
|
|
{"rr-ttl-max", config_rr_ttl_max},
|
|
{"bogus-nxdomain", conf_bogus_nxdomain},
|
|
{"conf-file", config_addtional_file},
|
|
};
|
|
int config_item_num = sizeof(config_item) / sizeof(struct config_item);
|
|
|
|
int load_conf_init(void)
|
|
{
|
|
art_tree_init(&dns_conf_address);
|
|
hash_init(dns_conf_bogus_nxdomain.ip_hash);
|
|
return 0;
|
|
}
|
|
|
|
void load_exit(void)
|
|
{
|
|
conf_bogus_nxdomain_destroy();
|
|
config_address_destroy();
|
|
}
|
|
|
|
int load_conf_file(const char *file)
|
|
{
|
|
FILE *fp = NULL;
|
|
char line[MAX_LINE_LEN];
|
|
char key[MAX_KEY_LEN];
|
|
char value[MAX_LINE_LEN];
|
|
int filed_num = 0;
|
|
int line_num = 0;
|
|
int i;
|
|
|
|
fp = fopen(file, "r");
|
|
if (fp == NULL) {
|
|
tlog(TLOG_ERROR, "config file %s not exist.", file);
|
|
return -1;
|
|
}
|
|
|
|
while (fgets(line, MAX_LINE_LEN, fp)) {
|
|
line_num++;
|
|
filed_num = sscanf(line, "%63s %1023[^\r\n]s", key, value);
|
|
if (filed_num <= 0) {
|
|
continue;
|
|
}
|
|
|
|
/* comment, skip */
|
|
if (key[0] == '#') {
|
|
continue;
|
|
}
|
|
|
|
/* if field format is not key = value, error */
|
|
if (filed_num != 2) {
|
|
goto errout;
|
|
}
|
|
|
|
for (i = 0; i < config_item_num; i++) {
|
|
if (strncmp(config_item[i].item, key, MAX_KEY_LEN) != 0) {
|
|
continue;
|
|
}
|
|
|
|
/* call item function */
|
|
if (config_item[i].item_func(value) != 0) {
|
|
goto errout;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
fclose(fp);
|
|
|
|
return 0;
|
|
errout:
|
|
printf("invalid config at file %s:%d, %s", file, line_num, line);
|
|
tlog(TLOG_ERROR, "invalid config at file %s:%d, %s", file, line_num, line);
|
|
if (fp) {
|
|
fclose(fp);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int load_conf(const char *file)
|
|
{
|
|
load_conf_init();
|
|
|
|
return load_conf_file(file);
|
|
}
|