Compare commits

...

16 Commits

Author SHA1 Message Date
Nick Peng
86902d2e34 serve-expired: support config reply ttl 2020-09-06 15:21:37 +08:00
Nick Peng
6f30fe6d05 compile: remove O_CLOEXEC flags 2020-09-05 19:37:50 +08:00
Nick Peng
0b45da29c7 cache: support persist cache when restart smartdns 2020-09-05 12:55:30 +08:00
Nick Peng
2b81fffb7e dns_client: fix ssl race condition issue. 2020-09-02 22:03:24 +08:00
Nick Peng
7af6f475da dns-client: reduce ssl session cache size 2020-08-27 23:34:22 +08:00
Purple Grape
c3b6560b46 add rpm support 2020-08-25 22:02:45 +08:00
Nick Peng
a3d3364a32 dns_client: avoid disconnect with server after query complete 2020-08-24 23:54:06 +08:00
Nick Peng
b8a36ccb8c dns_client: avoid connect failure 2020-08-24 23:18:35 +08:00
Nick Peng
aee19be262 dns-cache: support cache when speed check is disabled 2020-08-23 20:12:50 +08:00
Nick Peng
98429e88f1 speed_check: fix query failure when speed-check is none and dualstack-selection is enable. 2020-08-23 15:28:56 +08:00
Nick Peng
80cb27c795 tlog: update tlog 2020-08-22 08:13:55 +08:00
Kevin Zhou
cb73eadf01 Make the ipset working as expected when the domain passthrough 2020-08-22 07:57:00 +08:00
Nick Peng
69ba3f8789 pending: fix pending server resolve issue 2020-08-16 17:05:51 +08:00
Nick Peng
c380bbe0e3 tlog: update tlog 2020-08-13 01:23:40 +08:00
Nick Peng
da74e877c5 dns_client: repeat same arguments with SSL_write 2020-08-13 00:56:51 +08:00
Nick Peng
a300873b3f dns-client: fix ssl send request issue 2020-08-11 00:09:38 +08:00
18 changed files with 1284 additions and 266 deletions

View File

@@ -504,6 +504,8 @@ https://github.com/pymumu/smartdns/releases
|bind|DNS监听端口号|[::]:53|可绑定多个端口<br>`IP:PORT`: 服务器IP端口号。<br>`[-group]`: 请求时使用的DNS服务器组。<br>`[-no-rule-addr]`跳过address规则。<br>`[-no-rule-nameserver]`跳过Nameserver规则。<br>`[-no-rule-ipset]`跳过Ipset规则。<br>`[no-rule-soa]`跳过SOA(#)规则.<br>`[no-dualstack-selection]`:停用双栈测速。<br>`[-no-speed-check]`:停用测速。<br>`[-no-cache]`:停止缓存|bind :53
|bind-tcp|TCP DNS监听端口号|[::]:53|可绑定多个端口<br>`IP:PORT`: 服务器IP端口号。<br>`[-group]`: 请求时使用的DNS服务器组。<br>`[-no-rule-addr]`跳过address规则。<br>`[-no-rule-nameserver]`跳过Nameserver规则。<br>`[-no-rule-ipset]`跳过Ipset规则。<br>`[no-rule-soa]`跳过SOA(#)规则.<br>`[no-dualstack-selection]`:停用双栈测速。<br>`[-no-speed-check]`:停用测速。<br>`[-no-cache]`:停止缓存|bind-tcp :53
|cache-size|域名结果缓存个数|512|数字|cache-size 512
|cache-persist|是否持久化缓存|no|[yes\|no]|cache-persist yes
|cache-file|缓存持久化文件路径|/tmp/smartdns.cache|路径|cache-file /tmp/smartdns.cache
|tcp-idle-time|TCP链接空闲超时时间|120|数字|tcp-idle-time 120
|rr-ttl|域名结果TTL|远程查询结果|大于0的数字|rr-ttl 600
|rr-ttl-min|允许的最小TTL值|远程查询结果|大于0的数字|rr-ttl-min 60
@@ -535,6 +537,7 @@ https://github.com/pymumu/smartdns/releases
|prefetch-domain|域名预先获取功能|no|[yes\|no]|prefetch-domain yes
|serve-expired|过期缓存服务功能|no|[yes\|no]开启此功能后如果有请求时尝试回应TTL为0的过期记录并并发查询记录以避免查询等待|serve-expired yes
|serve-expired-ttl|过期缓存服务最长超时时间|0|秒0表示停用超时> 0表示指定的超时的秒数|serve-expired-ttl 0
|serve-expired-reply-ttl|回应的过期缓存TTL|5|秒0表示停用超时> 0表示指定的超时的秒数|serve-expired-reply-ttl 30
|dualstack-ip-selection|双栈IP优选|no|[yes\|no]|dualstack-ip-selection yes
|dualstack-ip-selection-threshold|双栈IP优选阈值|30ms|毫秒|dualstack-ip-selection-threshold [0-1000]
|ca-file|证书文件|/etc/ssl/certs/ca-certificates.crt|路径|ca-file /etc/ssl/certs/ca-certificates.crt

View File

@@ -498,6 +498,8 @@ Note: Merlin firmware is derived from ASUS firmware and can theoretically be use
|bind|DNS listening port number|[::]:53|Support binding multiple ports<br>`IP:PORT`: server IP, port number. <br>`[-group]`: The DNS server group used when requesting. <br>`[-no-rule-addr]`: Skip the address rule. <br>`[-no-rule-nameserver]`: Skip the Nameserver rule. <br>`[-no-rule-ipset]`: Skip the Ipset rule. <br>`[-no-rule-soa]`: Skip address SOA(#) rules.<br>`[-no-dualstack-selection]`: Disable dualstack ip selection.<br>`[-no-speed-check]`: Disable speed measurement. <br>`[-no-cache]`: stop caching |bind :53
|bind-tcp|TCP mode DNS listening port number|[::]:53|Support binding multiple ports<br>`IP:PORT`: server IP, port number. <br>`[-group]`: The DNS server group used when requesting. <br>`[-no-rule-addr]`: Skip the address rule. <br>`[-no-rule-nameserver]`: Skip the Nameserver rule. <br>`[-no-rule-ipset]`: Skip the Ipset rule. <br>`[-no-rule-soa]`: Skip address SOA(#) rules.<br>`[-no-dualstack-selection]`: Disable dualstack ip selection.<br>`[-no-speed-check]`: Disable speed measurement. <br>`[-no-cache]`: stop caching |bind-tcp :53
|cache-size|Domain name result cache number|512|integer|cache-size 512
|cache-persist|enable persist cache|no|[yes\|no]|cache-persist yes
|cache-file|cache persist file|/tmp/smartdns.cache|路径|cache-file /tmp/smartdns.cache
|tcp-idle-time|TCP connection idle timeout|120|integer|tcp-idle-time 120
|rr-ttl|Domain name TTL|Remote query result|number greater than 0|rr-ttl 600
|rr-ttl-min|Domain name Minimum TTL|Remote query result|number greater than 0|rr-ttl-min 60
@@ -529,6 +531,7 @@ Note: Merlin firmware is derived from ASUS firmware and can theoretically be use
|prefetch-domain|domain prefetch feature|no|[yes\|no]|prefetch-domain yes
|serve-expired|Cache serve expired feature|no|[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|second0disable> 0 seconds after expiration|serve-expired-ttl 0
|serve-expired-reply-ttl|TTL value to use when replying with expired data|5|second0disable> 0 seconds after expiration|serve-expired-reply-ttl 30
|dualstack-ip-selection|Dualstack ip selection|no|[yes\|no]|dualstack-ip-selection yes
|dualstack-ip-selection-threshold|Dualstack ip select threadhold|30ms|millisecond|dualstack-ip-selection-threshold [0-1000]
|ca-file|certificate file|/etc/ssl/certs/ca-certificates.crt|path|ca-file /etc/ssl/certs/ca-certificates.crt

View File

@@ -38,7 +38,13 @@ bind [::]:53
# dns cache size
# cache-size [number]
# 0: for no cache
cache-size 512
cache-size 4096
# enable persist cache when restart
# cache-persist yes
# cache persist file
# cache-file /tmp/smartdns.cache
# prefetch domain
# prefetch-domain [yes|no]
@@ -52,6 +58,10 @@ cache-size 512
# serve-expired-ttl [num]
# serve-expired-ttl 0
# reply TTL value to use when replying with expired data
# serve-expired-reply-ttl [num]
# serve-expired-reply-ttl 30
# List of hosts that supply bogus NX domain results
# bogus-nxdomain [ip/subnet]

View File

@@ -332,9 +332,28 @@ case "$1" in
return 0
fi
if [ ! -d "/proc/$pid" ]; then
return 0;
kill -15 "$pid" 2>/dev/null
SLEEP=`which usleep`
SLEEPTIME=200000
if [ -z "$SLEEP" ]; then
SLEEP="sleep"
SLEEPTIME=0.2
fi
N=30
while [ $N -gt 0 ]
do
pid="$(cat "$SMARTDNS_PID" | head -n 1 2>/dev/null)"
if [ -z "$pid" ]; then
return 0
fi
if [ ! -d "/proc/$pid" ]; then
return 0;
fi
$SLEEP $SLEEPTIME 2>/dev/null
N=$((N-1))
done
kill -9 "$pid" 2>/dev/null
;;

View File

@@ -0,0 +1,63 @@
Name: smartdns
Version: 31
Release: 1%{?dist}
Summary: smartdns
License: GPL 3.0
URL: https://github.com/pymumu/smartdns
Source0: smartdns-Release31.tar.gz
BuildRequires: glibc
Requires: glibc
Requires: systemd
%description
A local DNS server to obtain the fastest website IP for the best Internet experience.
%prep
%setup -q -n smartdns-Release31
%build
cd src
make %{?_smp_mflags}
%install
rm -rf $RPM_BUILD_ROOT
%{__install} -D -m 755 src/smartdns $RPM_BUILD_ROOT%{_sbindir}/smartdns
%{__install} -D -m 644 etc/smartdns/smartdns.conf $RPM_BUILD_ROOT%{_sysconfdir}/smartdns/smartdns.conf
%{__install} -D -m 644 systemd/smartdns.service.in $RPM_BUILD_ROOT%{_unitdir}/smartdns.service
cat > $RPM_BUILD_ROOT%{_unitdir}/smartdns.service <<EOF
[Unit]
Description=smartdns
ConditionFileIsExecutable=/usr/sbin/smartdns
After=syslog.target network-online.target
[Service]
Type=simple
ExecStart=/usr/sbin/smartdns -c /etc/smartdns/smartdns.conf -f
PIDFile=/run/smartdns.pid
Restart=on-failure
KillMode=process
[Install]
WantedBy=multi-user.target
EOF
%files
%defattr(-,root,root,-)
%{_sbindir}/smartdns
%config(noreplace) %{_sysconfdir}/smartdns/smartdns.conf
%{_unitdir}/smartdns.service
%post
%systemd_post %{name}.service
%preun
%systemd_preun %{name}.service
%postun
%systemd_postun_with_restart %{name}.service

View File

@@ -19,7 +19,11 @@
#include "dns_cache.h"
#include "stringutil.h"
#include "tlog.h"
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <string.h>
#include <sys/types.h>
#define DNS_CACHE_MAX_HITNUM 5000
#define DNS_CACHE_HITNUM_STEP 2
@@ -65,7 +69,7 @@ static __attribute__((unused)) struct dns_cache *_dns_cache_last(void)
return list_last_entry(&dns_cache_head.cache_list, struct dns_cache, list);
}
static struct dns_cache *_dns_cache_first(void)
static struct dns_cache *_dns_inactive_cache_first(void)
{
struct dns_cache *dns_cache = NULL;
@@ -82,6 +86,7 @@ static void _dns_cache_delete(struct dns_cache *dns_cache)
hash_del(&dns_cache->node);
list_del_init(&dns_cache->list);
atomic_dec(&dns_cache_head.num);
dns_cache_data_free(dns_cache->cache_data);
free(dns_cache);
}
@@ -118,10 +123,89 @@ static void _dns_cache_move_inactive(struct dns_cache *dns_cache)
list_add_tail(&dns_cache->list, &dns_cache_head.inactive_list);
}
int dns_cache_replace(char *domain, char *cname, int cname_ttl, int ttl, dns_type_t qtype, unsigned char *addr,
int addr_len, int speed)
enum CACHE_TYPE dns_cache_data_type(struct dns_cache_data *cache_data)
{
return cache_data->head.cache_type;
}
uint32_t dns_cache_get_cache_flag(struct dns_cache_data *cache_data)
{
return cache_data->head.cache_flag;
}
void dns_cache_data_free(struct dns_cache_data *data)
{
if (data == NULL) {
return;
}
free(data);
}
struct dns_cache_data *dns_cache_new_data_addr(uint32_t cache_flag, char *cname, int cname_ttl, unsigned char *addr,
int addr_len)
{
struct dns_cache_addr *cache_addr = malloc(sizeof(struct dns_cache_addr));
memset(cache_addr, 0, sizeof(struct dns_cache_addr));
if (cache_addr == NULL) {
return NULL;
}
if (addr_len == DNS_RR_A_LEN) {
memcpy(cache_addr->addr_data.addr, addr, DNS_RR_A_LEN);
} else if (addr_len != DNS_RR_AAAA_LEN) {
memcpy(cache_addr->addr_data.addr, addr, DNS_RR_AAAA_LEN);
} else {
goto errout;
}
if (cname) {
safe_strncpy(cache_addr->addr_data.cname, cname, DNS_MAX_CNAME_LEN);
cache_addr->addr_data.cname_ttl = cname_ttl;
}
cache_addr->head.cache_flag = cache_flag;
cache_addr->head.cache_type = CACHE_TYPE_ADDR;
cache_addr->head.size = sizeof(struct dns_cache_addr) - sizeof(struct dns_cache_data_head);
return (struct dns_cache_data *)cache_addr;
errout:
if (cache_addr) {
free(cache_addr);
cache_addr = NULL;
}
return NULL;
}
struct dns_cache_data *dns_cache_new_data_packet(uint32_t cache_flag, void *packet, size_t packet_len)
{
struct dns_cache_packet *cache_packet = NULL;
size_t data_size = 0;
if (packet == NULL || packet_len <= 0) {
return NULL;
}
data_size = sizeof(*cache_packet) + packet_len;
cache_packet = malloc(data_size);
if (cache_packet == NULL) {
return NULL;
}
memcpy(cache_packet->data, packet, packet_len);
cache_packet->head.cache_flag = cache_flag;
cache_packet->head.cache_type = CACHE_TYPE_PACKET;
cache_packet->head.size = packet_len;
return (struct dns_cache_data *)cache_packet;
}
int dns_cache_replace(char *domain, int ttl, dns_type_t qtype, int speed, struct dns_cache_data *cache_data)
{
struct dns_cache *dns_cache = NULL;
struct dns_cache_data *old_cache_data = NULL;
if (dns_cache_head.size <= 0) {
return 0;
@@ -130,7 +214,7 @@ int dns_cache_replace(char *domain, char *cname, int cname_ttl, int ttl, dns_typ
/* lookup existing cache */
dns_cache = dns_cache_lookup(domain, qtype);
if (dns_cache == NULL) {
return dns_cache_insert(domain, cname, cname_ttl, ttl, qtype, addr, addr_len, speed);
return dns_cache_insert(domain, ttl, qtype, speed, cache_data);
}
if (ttl < DNS_CACHE_TTL_MIN) {
@@ -139,58 +223,30 @@ int dns_cache_replace(char *domain, char *cname, int cname_ttl, int ttl, dns_typ
/* update cache data */
pthread_mutex_lock(&dns_cache_head.lock);
dns_cache->ttl = ttl;
dns_cache->qtype = qtype;
dns_cache->ttl = ttl;
dns_cache->del_pending = 0;
dns_cache->speed = speed;
time(&dns_cache->insert_time);
if (qtype == DNS_T_A) {
if (addr_len != DNS_RR_A_LEN) {
goto errout_unlock;
}
memcpy(dns_cache->addr, addr, DNS_RR_A_LEN);
} else if (qtype == DNS_T_AAAA) {
if (addr_len != DNS_RR_AAAA_LEN) {
goto errout_unlock;
}
memcpy(dns_cache->addr, addr, DNS_RR_AAAA_LEN);
} else {
goto errout_unlock;
}
if (cname) {
safe_strncpy(dns_cache->cname, cname, DNS_MAX_CNAME_LEN);
dns_cache->cname_ttl = cname_ttl;
}
dns_cache->info.ttl = ttl;
dns_cache->info.qtype = qtype;
dns_cache->info.ttl = ttl;
dns_cache->info.speed = speed;
time(&dns_cache->info.insert_time);
old_cache_data = dns_cache->cache_data;
dns_cache->cache_data = cache_data;
list_del_init(&dns_cache->list);
list_add_tail(&dns_cache->list, &dns_cache_head.cache_list);
pthread_mutex_unlock(&dns_cache_head.lock);
dns_cache_data_free(old_cache_data);
dns_cache_release(dns_cache);
return 0;
errout_unlock:
pthread_mutex_unlock(&dns_cache_head.lock);
// errout:
if (dns_cache) {
dns_cache_release(dns_cache);
}
return -1;
}
int dns_cache_insert(char *domain, char *cname, int cname_ttl, int ttl, dns_type_t qtype, unsigned char *addr,
int addr_len, int speed)
int _dns_cache_insert(struct dns_cache_info *info, struct dns_cache_data *cache_data, struct list_head *head)
{
uint32_t key = 0;
struct dns_cache *dns_cache = NULL;
if (dns_cache_head.size <= 0) {
return 0;
}
/* if cache already exists, free */
dns_cache = dns_cache_lookup(domain, qtype);
dns_cache = dns_cache_lookup(info->domain, info->qtype);
if (dns_cache) {
dns_cache_delete(dns_cache);
dns_cache_release(dns_cache);
@@ -202,50 +258,22 @@ int dns_cache_insert(char *domain, char *cname, int cname_ttl, int ttl, dns_type
goto errout;
}
if (ttl < DNS_CACHE_TTL_MIN) {
ttl = DNS_CACHE_TTL_MIN;
}
key = hash_string(domain);
key = jhash(&qtype, sizeof(qtype), key);
safe_strncpy(dns_cache->domain, domain, DNS_MAX_CNAME_LEN);
dns_cache->cname[0] = 0;
dns_cache->qtype = qtype;
dns_cache->ttl = ttl;
atomic_set(&dns_cache->hitnum, 3);
dns_cache->hitnum_update_add = DNS_CACHE_HITNUM_STEP;
dns_cache->del_pending = 0;
dns_cache->speed = speed;
memset(dns_cache, 0, sizeof(*dns_cache));
key = hash_string(info->domain);
key = jhash(&info->qtype, sizeof(info->qtype), key);
atomic_set(&dns_cache->ref, 1);
time(&dns_cache->insert_time);
if (qtype == DNS_T_A) {
if (addr_len != DNS_RR_A_LEN) {
goto errout;
}
memcpy(dns_cache->addr, addr, DNS_RR_A_LEN);
} else if (qtype == DNS_T_AAAA) {
if (addr_len != DNS_RR_AAAA_LEN) {
goto errout;
}
memcpy(dns_cache->addr, addr, DNS_RR_AAAA_LEN);
} else {
goto errout;
}
if (cname) {
safe_strncpy(dns_cache->cname, cname, DNS_MAX_CNAME_LEN);
dns_cache->cname_ttl = cname_ttl;
}
memcpy(&dns_cache->info, info, sizeof(*info));
dns_cache->del_pending = 0;
dns_cache->cache_data = cache_data;
pthread_mutex_lock(&dns_cache_head.lock);
hash_add(dns_cache_head.cache_hash, &dns_cache->node, key);
list_add_tail(&dns_cache->list, &dns_cache_head.cache_list);
list_add_tail(&dns_cache->list, head);
INIT_LIST_HEAD(&dns_cache->check_list);
/* Release extra cache, remove oldest cache record */
if (atomic_inc_return(&dns_cache_head.num) > dns_cache_head.size) {
struct dns_cache *del_cache;
del_cache = _dns_cache_first();
del_cache = _dns_inactive_cache_first();
if (del_cache) {
_dns_cache_remove(del_cache);
}
@@ -261,6 +289,33 @@ errout:
return -1;
}
int dns_cache_insert(char *domain, int ttl, dns_type_t qtype, int speed, struct dns_cache_data *cache_data)
{
struct dns_cache_info info;
if (cache_data == NULL || domain == NULL) {
return -1;
}
if (dns_cache_head.size <= 0) {
return 0;
}
if (ttl < DNS_CACHE_TTL_MIN) {
ttl = DNS_CACHE_TTL_MIN;
}
info.hitnum = 3;
safe_strncpy(info.domain, domain, DNS_MAX_CNAME_LEN);
info.qtype = qtype;
info.ttl = ttl;
info.hitnum_update_add = DNS_CACHE_HITNUM_STEP;
info.speed = speed;
time(&info.insert_time);
return _dns_cache_insert(&info, cache_data, &dns_cache_head.cache_list);
}
struct dns_cache *dns_cache_lookup(char *domain, dns_type_t qtype)
{
uint32_t key = 0;
@@ -280,11 +335,11 @@ struct dns_cache *dns_cache_lookup(char *domain, dns_type_t qtype)
pthread_mutex_lock(&dns_cache_head.lock);
hash_for_each_possible(dns_cache_head.cache_hash, dns_cache, node, key)
{
if (dns_cache->qtype != qtype) {
if (dns_cache->info.qtype != qtype) {
continue;
}
if (strncmp(domain, dns_cache->domain, DNS_MAX_CNAME_LEN) != 0) {
if (strncmp(domain, dns_cache->info.domain, DNS_MAX_CNAME_LEN) != 0) {
continue;
}
@@ -294,7 +349,7 @@ struct dns_cache *dns_cache_lookup(char *domain, dns_type_t qtype)
if (dns_cache_ret) {
/* Return NULL if the cache times out */
if (dns_cache_head.enable_inactive == 0 && (now - dns_cache_ret->insert_time > dns_cache_ret->ttl)) {
if (dns_cache_head.enable_inactive == 0 && (now - dns_cache_ret->info.insert_time > dns_cache_ret->info.ttl)) {
_dns_cache_remove(dns_cache_ret);
dns_cache_ret = NULL;
} else {
@@ -313,7 +368,7 @@ int dns_cache_get_ttl(struct dns_cache *dns_cache)
int ttl = 0;
time(&now);
ttl = dns_cache->insert_time + dns_cache->ttl - now;
ttl = dns_cache->info.insert_time + dns_cache->info.ttl - now;
if (ttl < 0) {
return 0;
}
@@ -321,6 +376,11 @@ int dns_cache_get_ttl(struct dns_cache *dns_cache)
return ttl;
}
struct dns_cache_data *dns_cache_get_data(struct dns_cache *dns_cache)
{
return dns_cache->cache_data;
}
void dns_cache_delete(struct dns_cache *dns_cache)
{
pthread_mutex_lock(&dns_cache_head.lock);
@@ -330,15 +390,14 @@ void dns_cache_delete(struct dns_cache *dns_cache)
int dns_cache_hitnum_dec_get(struct dns_cache *dns_cache)
{
int hitnum = 0;
pthread_mutex_lock(&dns_cache_head.lock);
hitnum = atomic_dec_return(&dns_cache->hitnum);
if (dns_cache->hitnum_update_add > DNS_CACHE_HITNUM_STEP) {
dns_cache->hitnum_update_add--;
dns_cache->info.hitnum--;
if (dns_cache->info.hitnum_update_add > DNS_CACHE_HITNUM_STEP) {
dns_cache->info.hitnum_update_add--;
}
pthread_mutex_unlock(&dns_cache_head.lock);
return hitnum;
return dns_cache->info.hitnum;
}
void dns_cache_update(struct dns_cache *dns_cache)
@@ -347,13 +406,13 @@ void dns_cache_update(struct dns_cache *dns_cache)
if (!list_empty(&dns_cache->list)) {
list_del_init(&dns_cache->list);
list_add_tail(&dns_cache->list, &dns_cache_head.cache_list);
atomic_add(dns_cache->hitnum_update_add, &dns_cache->hitnum);
if (atomic_read(&dns_cache->hitnum) > DNS_CACHE_MAX_HITNUM) {
atomic_set(&dns_cache->hitnum, DNS_CACHE_MAX_HITNUM);
dns_cache->info.hitnum += dns_cache->info.hitnum_update_add;
if (dns_cache->info.hitnum > DNS_CACHE_MAX_HITNUM) {
dns_cache->info.hitnum = DNS_CACHE_MAX_HITNUM;
}
if (dns_cache->hitnum_update_add < DNS_CACHE_HITNUM_STEP_MAX) {
dns_cache->hitnum_update_add++;
if (dns_cache->info.hitnum_update_add < DNS_CACHE_HITNUM_STEP_MAX) {
dns_cache->info.hitnum_update_add++;
}
}
pthread_mutex_unlock(&dns_cache_head.lock);
@@ -367,7 +426,7 @@ void _dns_cache_remove_expired_ttl(time_t *now)
list_for_each_entry_safe(dns_cache, tmp, &dns_cache_head.inactive_list, list)
{
ttl = dns_cache->insert_time + dns_cache->ttl - *now;
ttl = dns_cache->info.insert_time + dns_cache->info.ttl - *now;
if (ttl > 0) {
continue;
}
@@ -396,7 +455,7 @@ void dns_cache_invalidate(dns_cache_preinvalid_callback callback, int ttl_pre)
pthread_mutex_lock(&dns_cache_head.lock);
list_for_each_entry_safe(dns_cache, tmp, &dns_cache_head.cache_list, list)
{
ttl = dns_cache->insert_time + dns_cache->ttl - now;
ttl = dns_cache->info.insert_time + dns_cache->info.ttl - now;
if (ttl > 0 && ttl < ttl_pre) {
/* If the TTL time is in the pre-timeout range, call callback function */
if (callback && dns_cache->del_pending == 0) {
@@ -432,6 +491,216 @@ void dns_cache_invalidate(dns_cache_preinvalid_callback callback, int ttl_pre)
}
}
static int _dns_cache_read_record(int fd, uint32_t cache_number)
{
int i = 0;
int ret = 0;
struct dns_cache_record cache_record;
struct dns_cache_data_head data_head;
struct dns_cache_data *cache_data = NULL;
struct list_head *head = NULL;
for (i = 0; i < cache_number; i++) {
ret = read(fd, &cache_record, sizeof(cache_record));
if (ret != sizeof(cache_record)) {
tlog(TLOG_ERROR, "read cache failed, %s", strerror(errno));
goto errout;
}
if (cache_record.magic != MAGIC_CACHE_DATA) {
tlog(TLOG_ERROR, "magic is invalid.");
goto errout;
}
if (cache_record.type == CACHE_RECORD_TYPE_ACTIVE) {
head = &dns_cache_head.cache_list;
} else {
head = &dns_cache_head.inactive_list;
}
ret = read(fd, &data_head, sizeof(data_head));
if (ret != sizeof(data_head)) {
tlog(TLOG_ERROR, "read data head failed, %s", strerror(errno));
goto errout;
}
if (data_head.size > 1024 * 8) {
tlog(TLOG_ERROR, "data may invalid, skip load cache.");
goto errout;
}
cache_data = malloc(data_head.size + sizeof(data_head));
if (cache_data == NULL) {
tlog(TLOG_ERROR, "malloc cache data failed %s", strerror(errno));
goto errout;
}
memcpy(&cache_data->head, &data_head, sizeof(data_head));
ret = read(fd, cache_data->data, data_head.size);
if (ret != data_head.size) {
tlog(TLOG_ERROR, "read cache data failed, %s", strerror(errno));
goto errout;
}
if (_dns_cache_insert(&cache_record.info, cache_data, head) != 0) {
tlog(TLOG_ERROR, "insert cache data failed.");
cache_data = NULL;
goto errout;
}
cache_data = NULL;
}
return 0;
errout:
if (cache_data) {
free(cache_data);
}
return -1;
}
int dns_cache_load(const char *file)
{
int fd = -1;
int ret = 0;
fd = open(file, O_RDONLY);
if (fd < 0) {
return 0;
}
struct dns_cache_file cache_file;
ret = read(fd, &cache_file, sizeof(cache_file));
if (ret != sizeof(cache_file)) {
tlog(TLOG_ERROR, "read cache head failed.");
goto errout;
}
if (cache_file.magic != MAGIC_NUMBER) {
tlog(TLOG_ERROR, "cache file is invalid.");
goto errout;
}
if (strncmp(cache_file.version, __TIMESTAMP__, DNS_CACHE_VERSION_LEN) != 0) {
tlog(TLOG_WARN, "cache version is different, skip load cache.");
goto errout;
}
if (_dns_cache_read_record(fd, cache_file.cache_number) != 0) {
goto errout;
}
close(fd);
return 0;
errout:
if (fd > 0) {
close(fd);
}
return -1;
}
static int _dns_cache_write_record(int fd, uint32_t *cache_number, enum CACHE_RECORD_TYPE type, struct list_head *head)
{
struct dns_cache *dns_cache = NULL;
struct dns_cache *tmp = NULL;
struct dns_cache_record cache_record;
pthread_mutex_lock(&dns_cache_head.lock);
list_for_each_entry_safe_reverse(dns_cache, tmp, head, list)
{
cache_record.magic = MAGIC_CACHE_DATA;
cache_record.type = type;
memcpy(&cache_record.info, &dns_cache->info, sizeof(struct dns_cache_info));
int ret = write(fd, &cache_record, sizeof(cache_record));
if (ret != sizeof(cache_record)) {
tlog(TLOG_ERROR, "write cache failed, %s", strerror(errno));
goto errout;
}
struct dns_cache_data *cache_data = dns_cache->cache_data;
ret = write(fd, cache_data, sizeof(*cache_data) + cache_data->head.size);
if (ret != sizeof(*cache_data) + cache_data->head.size) {
tlog(TLOG_ERROR, "write cache data failed, %s", strerror(errno));
goto errout;
}
(*cache_number)++;
}
pthread_mutex_unlock(&dns_cache_head.lock);
return 0;
errout:
pthread_mutex_unlock(&dns_cache_head.lock);
return -1;
}
static int _dns_cache_write_records(int fd, uint32_t *cache_number)
{
if (_dns_cache_write_record(fd, cache_number, CACHE_RECORD_TYPE_ACTIVE, &dns_cache_head.cache_list) != 0) {
return -1;
}
if (_dns_cache_write_record(fd, cache_number, CACHE_RECORD_TYPE_INACTIVE, &dns_cache_head.inactive_list) != 0) {
return -1;
}
return 0;
}
int dns_cache_save(const char *file)
{
int fd = -1;
uint32_t cache_number = 0;
tlog(TLOG_DEBUG, "write cache file %s", file);
fd = open(file, O_TRUNC | O_CREAT | O_WRONLY, 0640);
if (fd < 0) {
tlog(TLOG_ERROR, "create file %s failed, %s", file, strerror(errno));
goto errout;
}
struct dns_cache_file cache_file;
memset(&cache_file, 0, sizeof(cache_file));
cache_file.magic = MAGIC_NUMBER;
safe_strncpy(cache_file.version, __TIMESTAMP__, DNS_CACHE_VERSION_LEN);
cache_file.cache_number = 0;
if (lseek(fd, sizeof(cache_file), SEEK_SET) < 0) {
tlog(TLOG_ERROR, "seek file %s failed, %s", file, strerror(errno));
goto errout;
}
if (_dns_cache_write_records(fd, &cache_number) != 0) {
tlog(TLOG_ERROR, "write record to file %s failed.", file);
goto errout;
}
if (lseek(fd, 0, SEEK_SET) < 0) {
tlog(TLOG_ERROR, "seek file %s failed, %s", file, strerror(errno));
goto errout;
}
cache_file.cache_number = cache_number;
if (write(fd, &cache_file, sizeof(cache_file)) != sizeof(cache_file)) {
tlog(TLOG_ERROR, "write file head %s failed, %s, %d", file, strerror(errno), fd);
goto errout;
}
tlog(TLOG_DEBUG, "wrote total %d records.", cache_number);
close(fd);
return 0;
errout:
if (fd > 0) {
close(fd);
}
return -1;
}
void dns_cache_destroy(void)
{
struct dns_cache *dns_cache = NULL;

View File

@@ -32,36 +32,100 @@ extern "C" {
#endif
#define DNS_CACHE_TTL_MIN 30
#define DNS_CACHE_VERSION_LEN 32
#define MAGIC_NUMBER 0x6548634163536e44
#define MAGIC_CACHE_DATA 0x44615461
enum CACHE_TYPE {
CACHE_TYPE_NONE,
CACHE_TYPE_ADDR,
CACHE_TYPE_PACKET,
};
enum CACHE_RECORD_TYPE {
CACHE_RECORD_TYPE_ACTIVE,
CACHE_RECORD_TYPE_INACTIVE,
};
struct dns_cache_data_head {
uint32_t cache_flag;
enum CACHE_TYPE cache_type;
size_t size;
};
struct dns_cache_data {
struct dns_cache_data_head head;
unsigned char data[0];
};
struct dns_cache_addr {
struct dns_cache_data_head head;
struct dns_cache_addr_data {
unsigned int cname_ttl;
char cname[DNS_MAX_CNAME_LEN];
union {
unsigned char ipv4_addr[DNS_RR_A_LEN];
unsigned char ipv6_addr[DNS_RR_AAAA_LEN];
unsigned char addr[0];
};
} addr_data;
};
struct dns_cache_packet {
struct dns_cache_data_head head;
unsigned char data[0];
};
struct dns_cache_info {
char domain[DNS_MAX_CNAME_LEN];
int ttl;
int hitnum;
int speed;
int hitnum_update_add;
time_t insert_time;
dns_type_t qtype;
};
struct dns_cache_record {
uint32_t magic;
enum CACHE_RECORD_TYPE type;
struct dns_cache_info info;
};
struct dns_cache {
struct hlist_node node;
struct list_head list;
struct list_head check_list;
atomic_t ref;
char domain[DNS_MAX_CNAME_LEN];
char cname[DNS_MAX_CNAME_LEN];
unsigned int cname_ttl;
unsigned int ttl;
int speed;
atomic_t hitnum;
int hitnum_update_add;
int del_pending;
time_t insert_time;
dns_type_t qtype;
union {
unsigned char ipv4_addr[DNS_RR_A_LEN];
unsigned char ipv6_addr[DNS_RR_AAAA_LEN];
unsigned char addr[0];
};
struct dns_cache_info info;
struct dns_cache_data *cache_data;
};
struct dns_cache_file {
uint64_t magic;
char version[DNS_CACHE_VERSION_LEN];
uint32_t cache_number;
};
enum CACHE_TYPE dns_cache_data_type(struct dns_cache_data *cache_data);
uint32_t dns_cache_get_cache_flag(struct dns_cache_data *cache_data);
void dns_cache_data_free(struct dns_cache_data *data);
struct dns_cache_data *dns_cache_new_data_addr(uint32_t cache_flag, char *cname, int cname_ttl, unsigned char *addr,
int addr_len);
struct dns_cache_data *dns_cache_new_data_packet(uint32_t cache_flag, void *packet, size_t packet_len);
int dns_cache_init(int size, int enable_inactive, int inactive_list_expired);
int dns_cache_replace(char *domain, char *cname, int cname_ttl, int ttl, dns_type_t qtype, unsigned char *addr,
int addr_len, int speed);
int dns_cache_replace(char *domain, int ttl, dns_type_t qtype, int speed, struct dns_cache_data *cache_data);
int dns_cache_insert(char *domain, char *cname, int cname_ttl, int ttl, dns_type_t qtype, unsigned char *addr,
int addr_len, int speed);
int dns_cache_insert(char *domain, int ttl, dns_type_t qtype, int speed, struct dns_cache_data *cache_data);
struct dns_cache *dns_cache_lookup(char *domain, dns_type_t qtype);
@@ -81,8 +145,14 @@ void dns_cache_invalidate(dns_cache_preinvalid_callback callback, int ttl_pre);
int dns_cache_get_ttl(struct dns_cache *dns_cache);
struct dns_cache_data *dns_cache_get_data(struct dns_cache *dns_cache);
void dns_cache_destroy(void);
int dns_cache_load(const char *file);
int dns_cache_save(const char *file);
#ifdef __cpluscplus
}
#endif

View File

@@ -105,8 +105,10 @@ struct dns_server_info {
int ttl;
int ttl_range;
SSL *ssl;
int ssl_write_len;
SSL_CTX *ssl_ctx;
SSL_SESSION *ssl_session;
pthread_mutex_t lock;
char skip_check_cert;
dns_server_status status;
@@ -248,6 +250,69 @@ static LIST_HEAD(pending_servers);
static pthread_mutex_t pending_server_mutex = PTHREAD_MUTEX_INITIALIZER;
static int dns_client_has_bootstrap_dns = 0;
int _ssl_read(struct dns_server_info *server, void *buff, int num)
{
int ret = 0;
pthread_mutex_lock(&server->lock);
ret = SSL_read(server->ssl, buff, num);
pthread_mutex_unlock(&server->lock);
return ret;
}
int _ssl_write(struct dns_server_info *server, const void *buff, int num)
{
int ret = 0;
pthread_mutex_lock(&server->lock);
ret = SSL_write(server->ssl, buff, num);
pthread_mutex_unlock(&server->lock);
return ret;
}
int _ssl_shutdown(struct dns_server_info *server)
{
int ret = 0;
pthread_mutex_lock(&server->lock);
ret = SSL_shutdown(server->ssl);
pthread_mutex_unlock(&server->lock);
return ret;
}
int _ssl_get_error(struct dns_server_info *server, int ret)
{
int err = 0;
pthread_mutex_lock(&server->lock);
err = SSL_get_error(server->ssl, ret);
pthread_mutex_unlock(&server->lock);
return err;
}
int _ssl_do_handshake(struct dns_server_info *server)
{
int err = 0;
pthread_mutex_lock(&server->lock);
err = SSL_do_handshake(server->ssl);
pthread_mutex_unlock(&server->lock);
return err;
}
int _ssl_session_reused(struct dns_server_info *server)
{
int err = 0;
pthread_mutex_lock(&server->lock);
err = SSL_session_reused(server->ssl);
pthread_mutex_unlock(&server->lock);
return err;
}
SSL_SESSION *_ssl_get1_session(struct dns_server_info *server)
{
SSL_SESSION *ret = 0;
pthread_mutex_lock(&server->lock);
ret = SSL_get1_session(server->ssl);
pthread_mutex_unlock(&server->lock);
return ret;
}
const char *_dns_server_get_type_string(dns_server_type_t type)
{
const char *type_str = "";
@@ -836,6 +901,7 @@ static int _dns_client_server_add(char *server_ip, char *server_host, int port,
server_info->ttl = ttl;
server_info->ttl_range = 0;
server_info->skip_check_cert = skip_check_cert;
pthread_mutex_init(&server_info->lock, NULL);
memcpy(&server_info->flags, flags, sizeof(server_info->flags));
/* exclude this server from default group */
@@ -861,6 +927,7 @@ static int _dns_client_server_add(char *server_ip, char *server_host, int port,
SSL_CTX_set_options(server_info->ssl_ctx, SSL_OP_ALL | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
SSL_CTX_set_session_cache_mode(server_info->ssl_ctx, SSL_SESS_CACHE_CLIENT);
SSL_CTX_sess_set_cache_size(server_info->ssl_ctx, 32);
if (_dns_client_set_trusted_cert(server_info->ssl_ctx) != 0) {
tlog(TLOG_WARN, "disable check certificate for %s.", server_info->ip);
server_info->skip_check_cert = 1;
@@ -914,6 +981,7 @@ errout:
server_info->ssl_ctx = NULL;
}
pthread_mutex_destroy(&server_info->lock);
free(server_info);
}
@@ -932,9 +1000,12 @@ static void _dns_client_close_socket(struct dns_server_info *server_info)
if (server_info->ssl) {
/* Shutdown ssl */
SSL_shutdown(server_info->ssl);
if (server_info->status == DNS_SERVER_STATUS_CONNECTED) {
_ssl_shutdown(server_info);
}
SSL_free(server_info->ssl);
server_info->ssl = NULL;
server_info->ssl_write_len = -1;
}
/* remove fd from epoll */
@@ -949,6 +1020,36 @@ static void _dns_client_close_socket(struct dns_server_info *server_info)
tlog(TLOG_DEBUG, "server %s closed.", server_info->ip);
}
static void _dns_client_shutdown_socket(struct dns_server_info *server_info)
{
if (server_info->fd <= 0) {
return;
}
switch (server_info->type) {
case DNS_SERVER_UDP:
return;
break;
case DNS_SERVER_TCP:
if (server_info->fd > 0) {
shutdown(server_info->fd, SHUT_RDWR);
}
break;
case DNS_SERVER_TLS:
case DNS_SERVER_HTTPS:
if (server_info->ssl) {
/* Shutdown ssl */
if (server_info->status == DNS_SERVER_STATUS_CONNECTED) {
_ssl_shutdown(server_info);
}
shutdown(server_info->fd, SHUT_RDWR);
}
break;
default:
break;
}
}
static void _dns_client_server_close(struct dns_server_info *server_info)
{
/* stop ping task */
@@ -980,6 +1081,7 @@ static void _dns_client_server_remove_all(void)
{
list_del(&server_info->list);
_dns_client_server_close(server_info);
pthread_mutex_destroy(&server_info->lock);
free(server_info);
}
pthread_mutex_unlock(&client.server_list_lock);
@@ -1433,7 +1535,7 @@ static int _dns_client_recv(struct dns_server_info *server_info, unsigned char *
}
_dns_client_query_release(query);
return ret;
return 0;
}
static int _dns_client_create_socket_udp(struct dns_server_info *server_info)
@@ -1524,6 +1626,9 @@ static int _DNS_client_create_socket_tcp(struct dns_server_info *server_info)
}
}
server_info->fd = fd;
server_info->status = DNS_SERVER_STATUS_CONNECTING;
memset(&event, 0, sizeof(event));
event.events = EPOLLIN | EPOLLOUT;
event.data.ptr = server_info;
@@ -1532,17 +1637,19 @@ static int _DNS_client_create_socket_tcp(struct dns_server_info *server_info)
return -1;
}
server_info->fd = fd;
server_info->status = DNS_SERVER_STATUS_CONNECTING;
tlog(TLOG_DEBUG, "tcp server %s connecting.\n", server_info->ip);
return 0;
errout:
if (server_info->fd > 0) {
server_info->fd = -1;
}
server_info->status = DNS_SERVER_STATUS_INIT;
if (fd > 0) {
close(fd);
}
return -1;
}
@@ -1617,6 +1724,11 @@ static int _DNS_client_create_socket_tls(struct dns_server_info *server_info, ch
SSL_set_tlsext_host_name(ssl, hostname);
}
server_info->fd = fd;
server_info->ssl = ssl;
server_info->ssl_write_len = -1;
server_info->status = DNS_SERVER_STATUS_CONNECTING;
memset(&event, 0, sizeof(event));
event.events = EPOLLIN | EPOLLOUT;
event.data.ptr = server_info;
@@ -1625,14 +1737,20 @@ static int _DNS_client_create_socket_tls(struct dns_server_info *server_info, ch
goto errout;
}
server_info->fd = fd;
server_info->ssl = ssl;
server_info->status = DNS_SERVER_STATUS_CONNECTING;
tlog(TLOG_DEBUG, "tls server %s connecting.\n", server_info->ip);
return 0;
errout:
if (server_info->fd > 0) {
server_info->fd = -1;
}
if (server_info->ssl) {
server_info->ssl = NULL;
}
server_info->status = DNS_SERVER_STATUS_INIT;
if (fd > 0) {
close(fd);
}
@@ -1731,28 +1849,28 @@ static int _dns_client_process_udp(struct dns_server_info *server_info, struct e
return 0;
}
static int _dns_client_socket_ssl_send(SSL *ssl, const void *buf, int num)
static int _dns_client_socket_ssl_send(struct dns_server_info *server, const void *buf, int num)
{
int ret = 0;
int ssl_ret = 0;
unsigned long ssl_err = 0;
if (ssl == NULL) {
if (server->ssl == NULL) {
errno = EINVAL;
return -1;
}
ret = SSL_write(ssl, buf, num);
ret = _ssl_write(server, buf, num);
if (ret > 0) {
return ret;
}
ssl_ret = SSL_get_error(ssl, ret);
ssl_ret = _ssl_get_error(server, ret);
switch (ssl_ret) {
case SSL_ERROR_NONE:
case SSL_ERROR_ZERO_RETURN:
return 0;
break;
case SSL_ERROR_ZERO_RETURN:
case SSL_ERROR_WANT_READ:
errno = EAGAIN;
ret = -1;
@@ -1765,7 +1883,8 @@ static int _dns_client_socket_ssl_send(SSL *ssl, const void *buf, int num)
ssl_err = ERR_get_error();
int ssl_reason = ERR_GET_REASON(ssl_err);
if (ssl_reason == SSL_R_UNINITIALIZED || ssl_reason == SSL_R_PROTOCOL_IS_SHUTDOWN ||
ssl_reason == SSL_R_BAD_LENGTH || ssl_reason == SSL_R_SHUTDOWN_WHILE_IN_INIT) {
ssl_reason == SSL_R_BAD_LENGTH || ssl_reason == SSL_R_SHUTDOWN_WHILE_IN_INIT ||
ssl_reason == SSL_R_BAD_WRITE_RETRY) {
errno = EAGAIN;
return -1;
}
@@ -1786,23 +1905,23 @@ static int _dns_client_socket_ssl_send(SSL *ssl, const void *buf, int num)
return ret;
}
static int _dns_client_socket_ssl_recv(SSL *ssl, void *buf, int num)
static int _dns_client_socket_ssl_recv(struct dns_server_info *server, void *buf, int num)
{
int ret = 0;
int ssl_ret = 0;
unsigned long ssl_err = 0;
if (ssl == NULL) {
if (server->ssl == NULL) {
errno = EFAULT;
return -1;
}
ret = SSL_read(ssl, buf, num);
ret = _ssl_read(server, buf, num);
if (ret >= 0) {
return ret;
}
ssl_ret = SSL_get_error(ssl, ret);
ssl_ret = _ssl_get_error(server, ret);
switch (ssl_ret) {
case SSL_ERROR_NONE:
case SSL_ERROR_ZERO_RETURN:
@@ -1828,11 +1947,15 @@ static int _dns_client_socket_ssl_recv(SSL *ssl, void *buf, int num)
return 0;
}
tlog(TLOG_ERROR, "SSL read fail error no: %s(%ld)\n", ERR_reason_error_string(ssl_err), ssl_err);
tlog(TLOG_ERROR, "SSL read fail error no: %s(%lx)\n", ERR_reason_error_string(ssl_err), ssl_err);
errno = EFAULT;
ret = -1;
break;
case SSL_ERROR_SYSCALL:
if (errno == 0) {
return 0;
}
if (errno != ECONNRESET) {
tlog(TLOG_INFO, "SSL syscall failed, %s ", strerror(errno));
}
@@ -1854,7 +1977,18 @@ static int _dns_client_socket_send(struct dns_server_info *server_info)
} else if (server_info->type == DNS_SERVER_TCP) {
return send(server_info->fd, server_info->send_buff.data, server_info->send_buff.len, MSG_NOSIGNAL);
} else if (server_info->type == DNS_SERVER_TLS || server_info->type == DNS_SERVER_HTTPS) {
return _dns_client_socket_ssl_send(server_info->ssl, server_info->send_buff.data, server_info->send_buff.len);
int write_len = server_info->send_buff.len;
if (server_info->ssl_write_len > 0) {
write_len = server_info->ssl_write_len;
server_info->ssl_write_len = -1;
}
int ret = _dns_client_socket_ssl_send(server_info, server_info->send_buff.data, write_len);
if (ret != 0) {
if (errno == EAGAIN) {
server_info->ssl_write_len = write_len;
}
}
return ret;
} else {
return -1;
}
@@ -1868,7 +2002,7 @@ static int _dns_client_socket_recv(struct dns_server_info *server_info)
return recv(server_info->fd, server_info->recv_buff.data + server_info->recv_buff.len,
DNS_TCP_BUFFER - server_info->recv_buff.len, 0);
} else if (server_info->type == DNS_SERVER_TLS || server_info->type == DNS_SERVER_HTTPS) {
return _dns_client_socket_ssl_recv(server_info->ssl, server_info->recv_buff.data + server_info->recv_buff.len,
return _dns_client_socket_ssl_recv(server_info, server_info->recv_buff.data + server_info->recv_buff.len,
DNS_TCP_BUFFER - server_info->recv_buff.len);
} else {
return -1;
@@ -1986,6 +2120,11 @@ static int _dns_client_process_tcp(struct dns_server_info *server_info, struct e
goto errout;
}
if (errno == ETIMEDOUT) {
tlog(TLOG_INFO, "recv failed, server %s:%d, %s\n", server_info->ip, server_info->port, strerror(errno));
goto errout;
}
tlog(TLOG_ERROR, "recv failed, server %s:%d, %s\n", server_info->ip, server_info->port, strerror(errno));
goto errout;
}
@@ -2115,7 +2254,8 @@ static int _dns_client_tls_matchName(const char *host, const char *pattern, int
return match;
}
static int _dns_client_tls_get_cert_CN(X509 *cert, char *cn, int max_cn_len) {
static int _dns_client_tls_get_cert_CN(X509 *cert, char *cn, int max_cn_len)
{
X509_NAME *cert_name = NULL;
cert_name = X509_get_subject_name(cert);
@@ -2153,8 +2293,10 @@ static int _dns_client_tls_verify(struct dns_server_info *server_info)
return -1;
}
pthread_mutex_lock(&server_info->lock);
cert = SSL_get_peer_certificate(server_info->ssl);
if (cert == NULL) {
pthread_mutex_unlock(&server_info->lock);
tlog(TLOG_ERROR, "get peer certificate failed.");
return -1;
}
@@ -2162,6 +2304,7 @@ static int _dns_client_tls_verify(struct dns_server_info *server_info)
if (server_info->skip_check_cert == 0) {
long res = SSL_get_verify_result(server_info->ssl);
if (res != X509_V_OK) {
pthread_mutex_unlock(&server_info->lock);
peer_CN[0] = '\0';
_dns_client_tls_get_cert_CN(cert, peer_CN, sizeof(peer_CN));
tlog(TLOG_WARN, "peer server %s certificate verify failed", server_info->ip);
@@ -2169,6 +2312,7 @@ static int _dns_client_tls_verify(struct dns_server_info *server_info)
goto errout;
}
}
pthread_mutex_unlock(&server_info->lock);
if (_dns_client_tls_get_cert_CN(cert, peer_CN, sizeof(peer_CN)) != 0) {
tlog(TLOG_ERROR, "get cert CN failed.");
@@ -2268,12 +2412,12 @@ static int _dns_client_process_tls(struct dns_server_info *server_info, struct e
if (server_info->status == DNS_SERVER_STATUS_CONNECTING) {
/* do SSL hand shake */
ret = SSL_do_handshake(server_info->ssl);
ret = _ssl_do_handshake(server_info);
if (ret == 0) {
goto errout;
} else if (ret < 0) {
memset(&fd_event, 0, sizeof(fd_event));
ssl_ret = SSL_get_error(server_info->ssl, ret);
ssl_ret = _ssl_get_error(server_info, ret);
if (ssl_ret == SSL_ERROR_WANT_READ) {
fd_event.events = EPOLLIN;
} else if (ssl_ret == SSL_ERROR_WANT_WRITE) {
@@ -2302,7 +2446,7 @@ static int _dns_client_process_tls(struct dns_server_info *server_info, struct e
tlog(TLOG_DEBUG, "tls server %s connected.\n", server_info->ip);
/* Was the stored session reused? */
if (SSL_session_reused(server_info->ssl)) {
if (_ssl_session_reused(server_info)) {
tlog(TLOG_DEBUG, "reused session");
} else {
tlog(TLOG_DEBUG, "new session");
@@ -2320,7 +2464,7 @@ static int _dns_client_process_tls(struct dns_server_info *server_info, struct e
}
/* save ssl session for next request */
server_info->ssl_session = SSL_get1_session(server_info->ssl);
server_info->ssl_session = _ssl_get1_session(server_info);
pthread_mutex_unlock(&client.server_list_lock);
}
@@ -2471,13 +2615,13 @@ static int _dns_client_send_tls(struct dns_server_info *server_info, void *packe
return -1;
}
send_len = _dns_client_socket_ssl_send(server_info->ssl, inpacket, len);
send_len = _dns_client_socket_ssl_send(server_info, inpacket, len);
if (send_len <= 0) {
if (errno == EAGAIN || errno == EPIPE || server_info->ssl == NULL) {
/* save data to buffer, and retry when EPOLLOUT is available */
return _dns_client_send_data_to_buffer(server_info, inpacket, len);
} else if (server_info->ssl && errno != ENOMEM) {
SSL_set_shutdown(server_info->ssl, SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN);
SSL_shutdown(server_info->ssl);
}
return -1;
} else if (send_len < len) {
@@ -2522,13 +2666,13 @@ static int _dns_client_send_https(struct dns_server_info *server_info, void *pac
return -1;
}
send_len = _dns_client_socket_ssl_send(server_info->ssl, inpacket, http_len);
send_len = _dns_client_socket_ssl_send(server_info, inpacket, http_len);
if (send_len <= 0) {
if (errno == EAGAIN || errno == EPIPE || server_info->ssl == NULL) {
/* save data to buffer, and retry when EPOLLOUT is available */
return _dns_client_send_data_to_buffer(server_info, inpacket, http_len);
} else if (server_info->ssl && errno != ENOMEM) {
SSL_set_shutdown(server_info->ssl, SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN);
_ssl_shutdown(server_info);
}
return -1;
} else if (send_len < http_len) {
@@ -2596,21 +2740,18 @@ static int _dns_client_send_packet(struct dns_query_struct *query, void *packet,
tlog(TLOG_DEBUG, "send query to %s failed, %s, type: %d", server_info->ip, strerror(send_err),
server_info->type);
_dns_client_close_socket(server_info);
} else if (send_err != ENOMEM) {
tlog(TLOG_ERROR, "send query to %s failed, %s, type: %d", server_info->ip, strerror(send_err),
server_info->type);
} else {
tlog(TLOG_DEBUG, "send query to %s failed, %s, type: %d", server_info->ip, strerror(send_err),
server_info->type);
time_t now;
time(&now);
if (now - 5 > server_info->last_recv) {
server_info->recv_buff.len = 0;
server_info->send_buff.len = 0;
tlog(TLOG_DEBUG, "server %s not response, retry.", server_info->ip);
_dns_client_close_socket(server_info);
}
atomic_dec(&query->dns_request_sent);
continue;
}
tlog(TLOG_DEBUG, "send query to %s failed, %s, type: %d", server_info->ip, strerror(send_err),
server_info->type);
time_t now;
time(&now);
if (now - 5 > server_info->last_recv || send_err != ENOMEM) {
_dns_client_shutdown_socket(server_info);
}
atomic_dec(&query->dns_request_sent);
continue;
}
@@ -2867,7 +3008,7 @@ static void _dns_client_add_pending_servers(void)
if (pending->query_v4 == 0) {
pending->query_v4 = 1;
_dns_client_server_pending_get(pending);
if (dns_server_query(pending->host, DNS_T_A, _dns_client_pending_server_resolve, pending) != 0) {
if (dns_server_query(pending->host, DNS_T_A, 0, _dns_client_pending_server_resolve, pending) != 0) {
_dns_client_server_pending_release_lck(pending);
}
}
@@ -2875,14 +3016,13 @@ static void _dns_client_add_pending_servers(void)
if (pending->query_v6 == 0) {
pending->query_v6 = 1;
_dns_client_server_pending_get(pending);
if (dns_server_query(pending->host, DNS_T_AAAA, _dns_client_pending_server_resolve, pending) != 0) {
if (dns_server_query(pending->host, DNS_T_AAAA, 0, _dns_client_pending_server_resolve, pending) != 0) {
_dns_client_server_pending_release_lck(pending);
}
}
/* if both A, AAAA has query result, select fastest IP address */
if (pending->has_v4 && pending->has_v6) {
if (pending->ping_time_v4 <= pending->ping_time_v6 && pending->ipv4[0]) {
dnsserver_ip = pending->ipv4;
} else {

View File

@@ -50,6 +50,7 @@ int dns_conf_cachesize = DEFAULT_DNS_CACHE_SIZE;
int dns_conf_prefetch = 0;
int dns_conf_serve_expired = 0;
int dns_conf_serve_expired_ttl = 0;
int dns_conf_serve_expired_reply_ttl = 5;
/* upstream servers */
struct dns_servers dns_conf_servers[DNS_MAX_SERVERS];
@@ -72,6 +73,9 @@ int dns_conf_log_num = 8;
char dns_conf_ca_file[DNS_MAX_PATH];
char dns_conf_ca_path[DNS_MAX_PATH];
char dns_conf_cache_file[DNS_MAX_PATH];
int dns_conf_cache_persist = 2;
/* auditing */
int dns_conf_audit_enable = 0;
int dns_conf_audit_log_SOA;
@@ -107,6 +111,10 @@ static int _get_domain(char *value, char *domain, int max_dmain_size, char **ptr
char *end = NULL;
int len = 0;
if (value == NULL || domain == NULL) {
goto errout;
}
/* first field */
begin = strstr(value, "/");
if (begin == NULL) {
@@ -1354,9 +1362,12 @@ static struct config_item _config_item[] = {
CONF_CUSTOM("speed-check-mode", _config_speed_check_mode, NULL),
CONF_INT("tcp-idle-time", &dns_conf_tcp_idle_time, 0, 3600),
CONF_INT("cache-size", &dns_conf_cachesize, 0, CONF_INT_MAX),
CONF_STRING("cache-file", (char *)&dns_conf_cache_file, DNS_MAX_PATH),
CONF_YESNO("cache-persist", &dns_conf_cache_persist),
CONF_YESNO("prefetch-domain", &dns_conf_prefetch),
CONF_YESNO("serve-expired", &dns_conf_serve_expired),
CONF_INT("serve-expired-ttl", &dns_conf_serve_expired_ttl, 0, CONF_INT_MAX),
CONF_INT("serve-expired-reply-ttl", &dns_conf_serve_expired_reply_ttl, 0, CONF_INT_MAX),
CONF_YESNO("dualstack-ip-selection", &dns_conf_dualstack_ip_selection),
CONF_INT("dualstack-ip-selection-threshold", &dns_conf_dualstack_ip_selection_threshold, 0, 1000),
CONF_CUSTOM("log-level", _config_log_level, NULL),

View File

@@ -49,6 +49,7 @@ extern "C" {
#define SMARTDNS_CONF_FILE "/etc/smartdns/smartdns.conf"
#define SMARTDNS_LOG_FILE "/var/log/smartdns.log"
#define SMARTDNS_AUDIT_FILE "/var/log/smartdns-audit.log"
#define SMARTDNS_CACHE_FILE "/tmp/smartdns.cache"
enum domain_rule {
DOMAIN_RULE_FLAGS = 0,
@@ -90,7 +91,7 @@ typedef enum {
#define BIND_FLAG_NO_SPEED_CHECK (1 << 5)
#define BIND_FLAG_NO_CACHE (1 << 6)
#define BIND_FLAG_NO_DUALSTACK_SELECTION (1 << 7)
#define BIND_FLAG_FORCE_AAAA_SOA (1 << 8)
#define BIND_FLAG_FORCE_AAAA_SOA (1 << 8)
struct dns_rule_flags {
unsigned int flags;
@@ -204,6 +205,7 @@ extern int dns_conf_cachesize;
extern int dns_conf_prefetch;
extern int dns_conf_serve_expired;
extern int dns_conf_serve_expired_ttl;
extern int dns_conf_serve_expired_reply_ttl;
extern struct dns_servers dns_conf_servers[DNS_MAX_SERVERS];
extern int dns_conf_server_num;
@@ -215,6 +217,9 @@ extern int dns_conf_log_num;
extern char dns_conf_ca_file[DNS_MAX_PATH];
extern char dns_conf_ca_path[DNS_MAX_PATH];
extern char dns_conf_cache_file[DNS_MAX_PATH];
extern int dns_conf_cache_persist;
extern struct dns_domain_check_order dns_conf_check_order;
extern struct dns_server_groups dns_conf_server_groups[DNS_NAX_GROUP_NUMBER];

View File

@@ -52,6 +52,7 @@
#define DNS_PING_SECOND_TIMEOUT (DNS_REQUEST_MAX_TIMEOUT - DNS_TCPPING_START)
#define SOCKET_IP_TOS (IPTOS_LOWDELAY | IPTOS_RELIABILITY)
#define SOCKET_PRIORITY (6)
#define CACHE_AUTO_ENABLE_SIZE (1024 * 1024 * 128)
#define RECV_ERROR_AGAIN 1
#define RECV_ERROR_OK 0
@@ -137,6 +138,8 @@ struct dns_request {
atomic_t refcnt;
struct dns_server_conn_head *conn;
uint32_t server_flags;
/* dns request list */
struct list_head list;
@@ -206,7 +209,7 @@ static struct dns_server server;
static tlog_log *dns_audit;
static int _dns_server_prefetch_request(char *domain, dns_type_t qtype);
static int _dns_server_prefetch_request(char *domain, dns_type_t qtype, uint32_t server_flags);
static int _dns_server_forward_request(unsigned char *inpacket, int inpacket_len)
{
@@ -216,17 +219,27 @@ static int _dns_server_forward_request(unsigned char *inpacket, int inpacket_len
static int _dns_server_has_bind_flag(struct dns_request *request, uint32_t flag)
{
if (request->conn == NULL) {
return -1;
}
if (request->conn->server_flags & flag) {
if (request->server_flags & flag) {
return 0;
}
return -1;
}
static int _dns_server_get_conf_ttl(int ttl)
{
if (dns_conf_rr_ttl > 0) {
return dns_conf_rr_ttl;
}
if (dns_conf_rr_ttl_max > 0 && ttl > dns_conf_rr_ttl_max) {
ttl = dns_conf_rr_ttl_max;
} else if (dns_conf_rr_ttl_min > 0 && ttl < dns_conf_rr_ttl_min) {
ttl = dns_conf_rr_ttl_min;
}
return ttl;
}
static int _dns_server_epoll_ctl(struct dns_server_conn_head *head, int op, uint32_t events)
{
struct epoll_event event;
@@ -478,7 +491,7 @@ static int _dns_server_reply_udp(struct dns_request *request, struct dns_server_
send_len =
sendto(udpserver->head.fd, inpacket, inpacket_len, 0, (struct sockaddr *)&request->addr, request->addr_len);
if (send_len != inpacket_len) {
tlog(TLOG_ERROR, "send failed.");
tlog(TLOG_ERROR, "send failed, %s", strerror(errno));
return -1;
}
@@ -692,10 +705,47 @@ static int _dns_setup_ipset(struct dns_request *request)
return ret;
}
static int _dns_server_request_update_cache(struct dns_request *request, dns_type_t qtype,
struct dns_cache_data *cache_data)
{
int ttl;
int speed = 0;
if (qtype == DNS_T_A) {
ttl = _dns_server_get_conf_ttl(request->ttl_v4);
speed = request->ping_ttl_v4;
} else if (qtype == DNS_T_AAAA) {
ttl = _dns_server_get_conf_ttl(request->ttl_v6);
speed = request->ping_ttl_v6;
} else {
goto errout;
}
/* if doing prefetch, update cache only */
if (request->prefetch) {
if (dns_cache_replace(request->domain, ttl, qtype, speed, cache_data) != 0) {
goto errout;
}
} else {
/* insert result to cache */
if (dns_cache_insert(request->domain, ttl, qtype, speed, cache_data) != 0) {
goto errout;
}
}
return 0;
errout:
if (cache_data) {
dns_cache_data_free(cache_data);
}
return -1;
}
static int _dns_server_request_complete_A(struct dns_request *request)
{
char *cname = NULL;
int cname_ttl = 0;
struct dns_cache_data *cache_data = NULL;
if (request->has_cname) {
cname = request->cname;
@@ -718,23 +768,32 @@ static int _dns_server_request_complete_A(struct dns_request *request)
return 0;
}
/* if doing prefetch, update cache only */
if (request->prefetch) {
dns_cache_replace(request->domain, cname, cname_ttl, request->ttl_v4, DNS_T_A, request->ipv4_addr, DNS_RR_A_LEN,
request->ping_ttl_v4);
} else {
/* insert result to cache */
dns_cache_insert(request->domain, cname, cname_ttl, request->ttl_v4, DNS_T_A, request->ipv4_addr, DNS_RR_A_LEN,
request->ping_ttl_v4);
cache_data = dns_cache_new_data_addr(request->server_flags, cname, cname_ttl, request->ipv4_addr, DNS_RR_A_LEN);
if (cache_data == NULL) {
goto errout;
}
if (_dns_server_request_update_cache(request, DNS_T_A, cache_data) != 0) {
goto errout;
}
return 0;
errout:
if (cache_data) {
dns_cache_data_free(cache_data);
cache_data = NULL;
}
return -1;
}
static int _dns_server_request_complete_AAAA(struct dns_request *request)
{
int ret = -1;
char *cname = NULL;
int cname_ttl = 0;
struct dns_cache_data *cache_data = NULL;
if (request->has_cname) {
cname = request->cname;
@@ -756,13 +815,14 @@ static int _dns_server_request_complete_AAAA(struct dns_request *request)
/* if doing prefetch, update cache only */
if (_dns_server_has_bind_flag(request, BIND_FLAG_NO_CACHE) != 0) {
if (request->prefetch) {
dns_cache_replace(request->domain, cname, cname_ttl, request->ttl_v6, DNS_T_AAAA, request->ipv6_addr,
DNS_RR_AAAA_LEN, request->ping_ttl_v6);
} else {
/* insert result to cache */
dns_cache_insert(request->domain, cname, cname_ttl, request->ttl_v6, DNS_T_AAAA, request->ipv6_addr,
DNS_RR_AAAA_LEN, request->ping_ttl_v6);
cache_data =
dns_cache_new_data_addr(request->server_flags, cname, cname_ttl, request->ipv6_addr, DNS_T_AAAA);
if (cache_data == NULL) {
goto errout;
}
if (_dns_server_request_update_cache(request, DNS_T_AAAA, cache_data) != 0) {
goto errout;
}
}
@@ -778,21 +838,25 @@ static int _dns_server_request_complete_AAAA(struct dns_request *request)
request->ping_ttl_v6 < 0) {
tlog(TLOG_DEBUG, "Force IPV4 perfered.");
if (_dns_server_has_bind_flag(request, BIND_FLAG_NO_CACHE) != 0) {
if (request->prefetch) {
dns_cache_replace(request->domain, cname, cname_ttl, request->ttl_v4, DNS_T_A, request->ipv4_addr,
DNS_RR_A_LEN, request->ping_ttl_v4);
} else {
dns_cache_insert(request->domain, cname, cname_ttl, request->ttl_v4, DNS_T_A, request->ipv4_addr,
DNS_RR_A_LEN, request->ping_ttl_v4);
cache_data =
dns_cache_new_data_addr(request->server_flags, cname, cname_ttl, request->ipv4_addr, DNS_T_A);
if (cache_data == NULL) {
goto errout;
}
if (_dns_server_request_update_cache(request, DNS_T_A, cache_data) != 0) {
goto errout;
}
}
if (request->dualstack_selection) {
if (_dns_server_reply_SOA(DNS_RC_NOERROR, request) != 0) {
return -1;
ret = -1;
goto errout;
}
return 1;
ret = 1;
goto errout;
}
}
}
@@ -800,6 +864,14 @@ static int _dns_server_request_complete_AAAA(struct dns_request *request)
request->has_ipv4 = 0;
return 0;
errout:
if (cache_data == NULL) {
dns_cache_data_free(cache_data);
cache_data = NULL;
}
return ret;
}
static int _dns_server_request_complete(struct dns_request *request)
@@ -1196,20 +1268,6 @@ static int _dns_ip_address_check_add(struct dns_request *request, unsigned char
return 0;
}
static int _dns_server_get_conf_ttl(int ttl)
{
if (dns_conf_rr_ttl > 0) {
return dns_conf_rr_ttl;
}
if (dns_conf_rr_ttl_max > 0 && ttl > dns_conf_rr_ttl_max) {
ttl = dns_conf_rr_ttl_max;
} else if (dns_conf_rr_ttl_min > 0 && ttl < dns_conf_rr_ttl_min) {
ttl = dns_conf_rr_ttl_min;
}
return ttl;
}
static int _dns_server_ip_rule_check(struct dns_request *request, unsigned char *addr, int addr_len,
dns_type_t addr_type, int result_flag)
{
@@ -1631,6 +1689,163 @@ static int _dns_server_passthrough_rule_check(struct dns_request *request, char
return -1;
}
static int _dns_server_get_answer(struct dns_request *request, struct dns_packet *packet)
{
int i = 0;
int j = 0;
int ttl = 0;
struct dns_rrs *rrs = NULL;
int rr_count = 0;
char name[DNS_MAX_CNAME_LEN] = {0};
for (j = 1; j < DNS_RRS_END; j++) {
rrs = dns_get_rrs_start(packet, j, &rr_count);
for (i = 0; i < rr_count && rrs; i++, rrs = dns_get_rrs_next(packet, rrs)) {
switch (rrs->type) {
case DNS_T_A: {
unsigned char addr[4];
char name[DNS_MAX_CNAME_LEN] = {0};
if (request->qtype != DNS_T_A) {
continue;
}
/* get A result */
dns_get_A(rrs, name, DNS_MAX_CNAME_LEN, &ttl, addr);
memcpy(request->ipv4_addr, addr, DNS_RR_A_LEN);
request->ttl_v4 = _dns_server_get_conf_ttl(ttl);
request->has_ipv4 = 1;
request->rcode = packet->head.rcode;
} break;
case DNS_T_AAAA: {
unsigned char addr[16];
char name[DNS_MAX_CNAME_LEN] = {0};
if (request->qtype != DNS_T_AAAA) {
/* ignore non-matched query type */
continue;
}
dns_get_AAAA(rrs, name, DNS_MAX_CNAME_LEN, &ttl, addr);
memcpy(request->ipv6_addr, addr, DNS_RR_AAAA_LEN);
request->ttl_v6 = _dns_server_get_conf_ttl(ttl);
request->has_ipv6 = 1;
request->rcode = packet->head.rcode;
} break;
case DNS_T_NS: {
char cname[DNS_MAX_CNAME_LEN];
dns_get_CNAME(rrs, name, DNS_MAX_CNAME_LEN, &ttl, cname, DNS_MAX_CNAME_LEN);
tlog(TLOG_DEBUG, "NS: %s ttl:%d cname: %s\n", name, ttl, cname);
} break;
case DNS_T_CNAME: {
char cname[DNS_MAX_CNAME_LEN];
dns_get_CNAME(rrs, name, DNS_MAX_CNAME_LEN, &ttl, cname, DNS_MAX_CNAME_LEN);
tlog(TLOG_DEBUG, "name:%s ttl: %d cname: %s\n", name, ttl, cname);
safe_strncpy(request->cname, cname, DNS_MAX_CNAME_LEN);
request->ttl_cname = ttl;
request->has_cname = 1;
} break;
case DNS_T_SOA: {
request->has_soa = 1;
request->rcode = packet->head.rcode;
dns_get_SOA(rrs, name, 128, &ttl, &request->soa);
tlog(TLOG_DEBUG,
"domain: %s, qtype: %d, SOA: mname: %s, rname: %s, serial: %d, refresh: %d, retry: %d, expire: "
"%d, minimum: %d",
request->domain, request->qtype, request->soa.mname, request->soa.rname, request->soa.serial,
request->soa.refresh, request->soa.retry, request->soa.expire, request->soa.minimum);
if (atomic_inc_return(&request->soa_num) >= (dns_server_num() / 2)) {
_dns_server_request_complete(request);
}
} break;
default:
tlog(TLOG_DEBUG, "%s, qtype: %d", name, rrs->type);
break;
}
}
}
return 0;
}
static int _dns_server_setup_ipset_packet(struct dns_request *request, struct dns_packet *packet)
{
int ttl;
char name[DNS_MAX_CNAME_LEN] = {0};
int rr_count;
int i = 0;
int j = 0;
struct dns_rrs *rrs = NULL;
struct dns_ipset_rule *ipset_rule = NULL;
struct dns_rule_flags *rule_flags = NULL;
if (_dns_server_has_bind_flag(request, BIND_FLAG_NO_RULE_IPSET) == 0) {
return 0;
}
/* check ipset rule */
rule_flags = request->domain_rule.rules[DOMAIN_RULE_FLAGS];
if (rule_flags) {
if (rule_flags->flags & DOMAIN_FLAG_IPSET_IGNORE) {
return 0;
}
}
ipset_rule = request->domain_rule.rules[DOMAIN_RULE_IPSET];
if (ipset_rule == NULL) {
return 0;
}
for (j = 1; j < DNS_RRS_END; j++) {
rrs = dns_get_rrs_start(packet, j, &rr_count);
for (i = 0; i < rr_count && rrs; i++, rrs = dns_get_rrs_next(packet, rrs)) {
switch (rrs->type) {
case DNS_T_A: {
unsigned char addr[4];
if (request->qtype != DNS_T_A) {
/* ignore non-matched query type */
if (request->dualstack_selection == 0) {
break;
}
}
/* get A result */
dns_get_A(rrs, name, DNS_MAX_CNAME_LEN, &ttl, addr);
/* add IPV4 to ipset */
ipset_add(ipset_rule->ipsetname, addr, DNS_RR_A_LEN, request->ttl_v4 * 2);
tlog(TLOG_DEBUG, "IPSET-MATCH-PASSTHROUTH: domain: %s, ipset: %s, IP: %d.%d.%d.%d", request->domain,
ipset_rule->ipsetname, addr[0], addr[1], addr[2], addr[3]);
} break;
case DNS_T_AAAA: {
unsigned char addr[16];
if (request->qtype != DNS_T_AAAA) {
/* ignore non-matched query type */
break;
}
dns_get_AAAA(rrs, name, DNS_MAX_CNAME_LEN, &ttl, addr);
/* add IPV6 to ipset */
if (request->has_ipv6) {
if (request->has_ipv4) {
ipset_add(ipset_rule->ipsetname, addr, DNS_RR_A_LEN, request->ttl_v4 * 2);
}
ipset_add(ipset_rule->ipsetname, addr, DNS_RR_AAAA_LEN, request->ttl_v6 * 2);
}
tlog(TLOG_DEBUG,
"IPSET-MATCH-PASSTHROUTH: domain: %s, ipset: %s, IP: "
"%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x",
request->domain, ipset_rule->ipsetname, addr[0], addr[1], addr[2], addr[3], addr[4], addr[5],
addr[6], addr[7], addr[8], addr[9], addr[10], addr[11], addr[12], addr[13], addr[14], addr[15]);
} break;
default:
break;
}
}
}
return 0;
}
static int _dns_server_reply_passthrouth(struct dns_request *request, struct dns_packet *packet,
unsigned char *inpacket, int inpacket_len)
{
@@ -1640,6 +1855,11 @@ static int _dns_server_reply_passthrouth(struct dns_request *request, struct dns
return 0;
}
_dns_server_get_answer(request, packet);
if (request->result_callback) {
_dns_result_callback(request);
}
if (request->conn == NULL) {
return 0;
}
@@ -1648,7 +1868,27 @@ static int _dns_server_reply_passthrouth(struct dns_request *request, struct dns
dns_server_update_reply_packet_id(request, inpacket, inpacket_len);
ret = _dns_reply_inpacket(request, inpacket, inpacket_len);
return ret;
if (packet->head.rcode != DNS_RC_NOERROR && packet->head.rcode != DNS_RC_NXDOMAIN) {
return ret;
}
if (_dns_server_has_bind_flag(request, BIND_FLAG_NO_CACHE) != 0 &&
(request->qtype == DNS_T_AAAA || request->qtype == DNS_T_A)) {
struct dns_cache_data *cache_packet = dns_cache_new_data_packet(request->server_flags, inpacket, inpacket_len);
if (cache_packet == NULL) {
return ret;
}
if (_dns_server_request_update_cache(request, request->qtype, cache_packet) != 0) {
tlog(TLOG_WARN, "update packet cache failed.");
}
}
if (_dns_server_setup_ipset_packet(request, packet) != 0) {
tlog(TLOG_DEBUG, "setup ipset failed.");
}
return 0;
}
static int dns_server_resolve_callback(char *domain, dns_result_type rtype, unsigned int result_flag,
@@ -1673,7 +1913,6 @@ static int dns_server_resolve_callback(char *domain, dns_result_type rtype, unsi
return _dns_server_reply_passthrouth(request, packet, inpacket, inpacket_len);
}
_dns_server_process_answer(request, domain, packet, result_flag);
return 0;
} else if (rtype == DNS_QUERY_ERR) {
@@ -1791,7 +2030,6 @@ static void _dns_server_log_rule(const char *domain, enum domain_rule rule_type,
int rule_key_len)
{
char rule_name[DNS_MAX_CNAME_LEN];
if (rule_key_len <= 0) {
return;
}
@@ -2026,6 +2264,128 @@ static void _dns_server_process_speed_check_rule(struct dns_request *request)
request->check_order_list = check_order;
}
static int _dns_server_get_expired_ttl_reply(struct dns_cache *dns_cache)
{
int ttl = dns_cache_get_ttl(dns_cache);
if (ttl > 0) {
return ttl;
}
return dns_conf_serve_expired_reply_ttl;
}
static int _dns_server_process_cache_addr(struct dns_request *request, struct dns_cache *dns_cache)
{
struct dns_cache_addr *cache_addr = (struct dns_cache_addr *)dns_cache_get_data(dns_cache);
if (cache_addr->head.cache_type != CACHE_TYPE_ADDR) {
goto errout;
}
/* Cache hits, returning results in the cache */
switch (request->qtype) {
case DNS_T_A:
memcpy(request->ipv4_addr, cache_addr->addr_data.ipv4_addr, DNS_RR_A_LEN);
request->ttl_v4 = _dns_server_get_expired_ttl_reply(dns_cache);
request->has_ipv4 = 1;
break;
case DNS_T_AAAA:
memcpy(request->ipv6_addr, cache_addr->addr_data.ipv6_addr, DNS_RR_AAAA_LEN);
request->ttl_v6 = _dns_server_get_expired_ttl_reply(dns_cache);
request->has_ipv6 = 1;
break;
default:
goto errout;
break;
}
if (cache_addr->addr_data.cname[0] != 0) {
safe_strncpy(request->cname, cache_addr->addr_data.cname, DNS_MAX_CNAME_LEN);
request->has_cname = 1;
request->ttl_cname = cache_addr->addr_data.cname_ttl;
}
request->rcode = DNS_RC_NOERROR;
_dns_result_callback(request);
if (request->prefetch == 0) {
_dns_reply(request);
}
return 0;
errout:
return -1;
}
static int _dns_server_process_cache_packet(struct dns_request *request, struct dns_cache *dns_cache)
{
struct dns_cache_packet *cache_packet = (struct dns_cache_packet *)dns_cache_get_data(dns_cache);
if (cache_packet->head.cache_type != CACHE_TYPE_PACKET) {
goto errout;
}
if (dns_cache->info.qtype != request->qtype) {
goto errout;
}
if (atomic_inc_return(&request->notified) != 1) {
return 0;
}
if (request->result_callback) {
unsigned char packet_buff[DNS_PACKSIZE];
struct dns_packet *packet = (struct dns_packet *)packet_buff;
if (dns_decode(packet, DNS_PACKSIZE, cache_packet->data, cache_packet->head.size) != 0) {
goto errout;
}
_dns_server_get_answer(request, packet);
_dns_result_callback(request);
}
if (request->conn == NULL) {
return 0;
}
/* When passthrough, modify the id to be the id of the client request. */
dns_server_update_reply_packet_id(request, cache_packet->data, cache_packet->head.size);
return _dns_reply_inpacket(request, cache_packet->data, cache_packet->head.size);
errout:
return -1;
}
static int _dns_server_process_cache_data(struct dns_request *request, struct dns_cache *dns_cache)
{
enum CACHE_TYPE cache_type = CACHE_TYPE_NONE;
int ret = -1;
cache_type = dns_cache_data_type(dns_cache->cache_data);
switch (cache_type) {
case CACHE_TYPE_ADDR:
ret = _dns_server_process_cache_addr(request, dns_cache);
if (ret != 0) {
goto out;
}
break;
case CACHE_TYPE_PACKET:
ret = _dns_server_process_cache_packet(request, dns_cache);
if (ret != 0) {
goto out;
}
break;
default:
goto out;
break;
}
return 0;
out:
return -1;
}
static int _dns_server_process_cache(struct dns_request *request)
{
struct dns_cache *dns_cache = NULL;
@@ -2043,24 +2403,28 @@ static int _dns_server_process_cache(struct dns_request *request)
if (dns_cache_A) {
tlog(TLOG_DEBUG, "No IPV6 Found, Force IPV4 perfered.");
if (dns_cache_get_ttl(dns_cache_A) == 0) {
_dns_server_prefetch_request(request->domain, request->qtype);
uint32_t server_flags = request->server_flags;
if (request->conn == NULL) {
server_flags = dns_cache_get_cache_flag(dns_cache_A->cache_data);
}
_dns_server_prefetch_request(request->domain, request->qtype, server_flags);
}
ret = _dns_server_reply_SOA(DNS_RC_NOERROR, request);
ret = _dns_server_reply_SOA(DNS_RC_NOERROR, request);
goto out;
}
}
goto out;
}
if (request->qtype != dns_cache->qtype) {
if (request->qtype != dns_cache->info.qtype) {
goto out;
}
if (request->dualstack_selection && request->qtype == DNS_T_AAAA) {
dns_cache_A = dns_cache_lookup(request->domain, DNS_T_A);
if (dns_cache_A && (dns_cache_A->speed > 0)) {
if ((dns_cache_A->speed + (dns_conf_dualstack_ip_selection_threshold * 10)) < dns_cache->speed ||
dns_cache->speed < 0) {
if (dns_cache_A && (dns_cache_A->info.speed > 0)) {
if ((dns_cache_A->info.speed + (dns_conf_dualstack_ip_selection_threshold * 10)) < dns_cache->info.speed ||
dns_cache->info.speed < 0) {
tlog(TLOG_DEBUG, "Force IPV4 perfered.");
ret = _dns_server_reply_SOA(DNS_RC_NOERROR, request);
goto out_update_cache;
@@ -2068,42 +2432,18 @@ static int _dns_server_process_cache(struct dns_request *request)
}
}
/* Cache hits, returning results in the cache */
switch (request->qtype) {
case DNS_T_A:
memcpy(request->ipv4_addr, dns_cache->ipv4_addr, DNS_RR_A_LEN);
request->ttl_v4 = dns_cache_get_ttl(dns_cache);
request->has_ipv4 = 1;
break;
case DNS_T_AAAA:
memcpy(request->ipv6_addr, dns_cache->ipv6_addr, DNS_RR_AAAA_LEN);
request->ttl_v6 = dns_cache_get_ttl(dns_cache);
request->has_ipv6 = 1;
break;
default:
ret = _dns_server_process_cache_data(request, dns_cache);
if (ret != 0) {
goto out;
break;
}
if (dns_cache->cname[0] != 0) {
safe_strncpy(request->cname, dns_cache->cname, DNS_MAX_CNAME_LEN);
request->has_cname = 1;
request->ttl_cname = dns_cache->cname_ttl;
}
request->rcode = DNS_RC_NOERROR;
_dns_result_callback(request);
if (request->prefetch == 0) {
_dns_reply(request);
}
ret = 0;
out_update_cache:
if (dns_cache_get_ttl(dns_cache) == 0) {
_dns_server_prefetch_request(request->domain, request->qtype);
uint32_t server_flags = request->server_flags;
if (request->conn == NULL) {
server_flags = dns_cache_get_cache_flag(dns_cache->cache_data);
}
_dns_server_prefetch_request(request->domain, request->qtype, server_flags);
} else {
dns_cache_update(dns_cache);
}
@@ -2124,6 +2464,7 @@ out:
static void _dns_server_request_set_client(struct dns_request *request, struct dns_server_conn_head *conn)
{
request->conn = conn;
request->server_flags = conn->server_flags;
_dns_server_conn_get(conn);
}
@@ -2224,6 +2565,10 @@ static void _dns_server_check_set_passthrough(struct dns_request *request)
if (_dns_server_has_bind_flag(request, BIND_FLAG_NO_SPEED_CHECK) == 0) {
request->passthrough = 1;
}
if (request->passthrough == 1) {
request->dualstack_selection = 0;
}
}
static int _dns_server_do_query(struct dns_request *request, const char *domain, int qtype)
@@ -2389,7 +2734,7 @@ errout:
return ret;
}
static int _dns_server_prefetch_request(char *domain, dns_type_t qtype)
static int _dns_server_prefetch_request(char *domain, dns_type_t qtype, uint32_t server_flags)
{
int ret = -1;
struct dns_request *request = NULL;
@@ -2400,6 +2745,7 @@ static int _dns_server_prefetch_request(char *domain, dns_type_t qtype)
goto errout;
}
request->server_flags = server_flags;
_dns_server_request_set_enable_prefetch(request);
ret = _dns_server_do_query(request, domain, qtype);
if (ret != 0) {
@@ -2417,7 +2763,7 @@ errout:
return ret;
}
int dns_server_query(char *domain, int qtype, dns_result_callback callback, void *user_ptr)
int dns_server_query(char *domain, int qtype, uint32_t server_flags, dns_result_callback callback, void *user_ptr)
{
int ret = -1;
struct dns_request *request = NULL;
@@ -2428,6 +2774,7 @@ int dns_server_query(char *domain, int qtype, dns_result_callback callback, void
goto errout;
}
request->server_flags = server_flags;
_dns_server_request_set_callback(request, callback, user_ptr);
ret = _dns_server_do_query(request, domain, qtype);
if (ret != 0) {
@@ -2439,6 +2786,7 @@ int dns_server_query(char *domain, int qtype, dns_result_callback callback, void
return ret;
errout:
if (request) {
_dns_server_request_set_callback(request, NULL, NULL);
_dns_server_request_release(request);
}
@@ -2821,10 +3169,11 @@ static void _dns_server_prefetch_domain(struct dns_cache *dns_cache)
}
/* start prefetch domain */
tlog(TLOG_DEBUG, "prefetch by cache %s, qtype %d, ttl %d, hitnum %d", dns_cache->domain, dns_cache->qtype,
dns_cache->ttl, hitnum);
if (_dns_server_prefetch_request(dns_cache->domain, dns_cache->qtype) != 0) {
tlog(TLOG_ERROR, "prefetch domain %s, qtype %d, failed.", dns_cache->domain, dns_cache->qtype);
tlog(TLOG_DEBUG, "prefetch by cache %s, qtype %d, ttl %d, hitnum %d", dns_cache->info.domain, dns_cache->info.qtype,
dns_cache->info.ttl, hitnum);
if (_dns_server_prefetch_request(dns_cache->info.domain, dns_cache->info.qtype,
dns_cache_get_cache_flag(dns_cache->cache_data)) != 0) {
tlog(TLOG_ERROR, "prefetch domain %s, qtype %d, failed.", dns_cache->info.domain, dns_cache->info.qtype);
}
}
@@ -3073,7 +3422,7 @@ static int _dns_create_socket(const char *host_ip, int type)
setsockopt(fd, IPPROTO_IP, IP_TOS, &ip_tos, sizeof(ip_tos));
if (bind(fd, gai->ai_addr, gai->ai_addrlen) != 0) {
tlog(TLOG_ERROR, "bind service failed, %s\n", strerror(errno));
tlog(TLOG_ERROR, "bind service %s failed, %s\n", host_ip, strerror(errno));
goto errout;
}
@@ -3230,6 +3579,60 @@ static int _dns_server_audit_init(void)
return 0;
}
static int _dns_server_cache_init(void)
{
if (dns_cache_init(dns_conf_cachesize, dns_conf_serve_expired, dns_conf_serve_expired_ttl) != 0) {
tlog(TLOG_ERROR, "init cache failed.");
return -1;
}
char *dns_cache_file = SMARTDNS_CACHE_FILE;
if (dns_conf_cache_file[0] != 0) {
dns_cache_file = dns_conf_cache_file;
}
if (dns_conf_cache_persist == 2) {
uint64_t freespace = get_free_space(dns_cache_file);
if (freespace >= CACHE_AUTO_ENABLE_SIZE) {
tlog(TLOG_INFO, "auto enable cache persist.");
dns_conf_cache_persist = 1;
}
}
if (dns_conf_cachesize <= 0 || dns_conf_cache_persist == 0) {
return 0;
}
if (dns_cache_load(dns_cache_file) != 0) {
tlog(TLOG_WARN, "Load cache failed.");
return 0;
}
return 0;
}
static int _dns_server_cache_save(void)
{
char *dns_cache_file = SMARTDNS_CACHE_FILE;
if (dns_conf_cache_file[0] != 0) {
dns_cache_file = dns_conf_cache_file;
}
if (dns_conf_cache_persist == 0 || dns_conf_cachesize <= 0) {
if (access(dns_cache_file, F_OK) == 0) {
unlink(dns_cache_file);
}
return 0;
}
if (dns_cache_save(dns_cache_file) != 0) {
tlog(TLOG_WARN, "save cache failed.");
return -1;
}
return 0;
}
int dns_server_init(void)
{
pthread_attr_t attr;
@@ -3240,9 +3643,9 @@ int dns_server_init(void)
return -1;
}
if (dns_cache_init(dns_conf_cachesize, dns_conf_serve_expired, dns_conf_serve_expired_ttl) != 0) {
tlog(TLOG_ERROR, "init cache failed.");
return -1;
if (_dns_server_cache_init() != 0) {
tlog(TLOG_ERROR, "init dns cache filed.");
goto errout;
}
if (_dns_server_audit_init() != 0) {
@@ -3318,5 +3721,6 @@ void dns_server_exit(void)
pthread_mutex_destroy(&server.request_list_lock);
_dns_server_cache_save();
dns_cache_destroy();
}

View File

@@ -20,6 +20,7 @@
#define _SMART_DNS_SERVER_H
#include "dns.h"
#include <stdint.h>
#ifdef __cpluscplus
extern "C" {
@@ -40,7 +41,7 @@ typedef int (*dns_result_callback)(char *domain, dns_rtcode_t rtcode, dns_type_t
unsigned int ping_time, void *user_ptr);
/* query domain */
int dns_server_query(char *domain, int qtype, dns_result_callback callback, void *user_ptr);
int dns_server_query(char *domain, int qtype, uint32_t server_flags, dns_result_callback callback, void *user_ptr);
#ifdef __cpluscplus
}

View File

@@ -439,6 +439,7 @@ int main(int argc, char *argv[])
goto errout;
}
signal(SIGPIPE, SIG_IGN);
if (dns_server_load_conf(config_file) != 0) {
fprintf(stderr, "load config failed.\n");
goto errout;
@@ -451,7 +452,7 @@ int main(int argc, char *argv[])
}
signal(SIGINT, _sig_exit);
signal(SIGPIPE, SIG_IGN);
signal(SIGTERM, _sig_exit);
atexit(_smartdns_exit);
return _smartdns_run();

View File

@@ -1,6 +1,6 @@
/*
* tinylog
* Copyright (C) 2018-2019 Nick Peng <pymumu@gmail.com>
* Copyright (C) 2018-2020 Nick Peng <pymumu@gmail.com>
* https://github.com/pymumu/tinylog
*/
#ifndef _GNU_SOURCE
@@ -1377,7 +1377,7 @@ static void *_tlog_work(void *arg)
log = _tlog_wait_log_locked(log);
if (log == NULL) {
pthread_mutex_unlock(&tlog.lock);
if (errno != ETIMEDOUT) {
if (errno != ETIMEDOUT && tlog.run) {
sleep(1);
}
continue;

View File

@@ -1,6 +1,6 @@
/*
* tinylog
* Copyright (C) 2018-2019 Ruilin Peng (Nick) <pymumu@gmail.com>
* Copyright (C) 2018-2020 Ruilin Peng (Nick) <pymumu@gmail.com>
* https://github.com/pymumu/tinylog
*/
@@ -196,6 +196,7 @@ public:
Buffer LogStream(tlog_level level, const char *file, int line, const char *func, void *userptr) {
return Buffer(new Stream, [=](Stream *st) {
tlog_ext(level, file, line, func, userptr, "%s", st->str().c_str());
delete st;
});
}
};
@@ -215,6 +216,7 @@ public:
Buffer Out(tlog_log *log) {
return Buffer(new Stream, [=](Stream *st) {
tlog_printf(log, "%s", st->str().c_str());
delete st;
});
}
};

View File

@@ -35,6 +35,7 @@
#include <string.h>
#include <sys/prctl.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
@@ -994,4 +995,17 @@ int set_sock_lingertime(int fd, int time)
}
return 0;
}
}
uint64_t get_free_space(const char *path)
{
uint64_t size = 0;
struct statvfs buf;
if (statvfs(path, &buf) != 0) {
return 0;
}
size = (uint64_t)buf.f_frsize * buf.f_bavail;
return size;
}

View File

@@ -106,6 +106,8 @@ int set_sock_keepalive(int fd, int keepidle, int keepinterval, int keepcnt);
int set_sock_lingertime(int fd, int time);
uint64_t get_free_space(const char *path);
#ifdef __cplusplus
}
#endif /*__cplusplus */

View File

@@ -1,8 +1,9 @@
[Unit]
Description=smart dns server
Description=smartdns server
After=network.target
StartLimitBurst=0
StartLimitIntervalSec=60
TimeoutStopSec=5
[Service]
Type=forking