Compare commits

...

101 Commits

Author SHA1 Message Date
Nick Peng
63415ef868 Feature: support DOQ. 2023-12-10 22:13:41 +08:00
Nick Peng
c4bffbb1dd tls: refactor tls send recv code. 2023-12-08 23:12:29 +08:00
Nick Peng
7b1ea2c43d test: add test case for bind to group 2023-12-08 22:32:07 +08:00
Nick Peng
5df4364809 feature: add option srv-record support SRV. 2023-12-07 23:15:11 +08:00
Nick Peng
61a6e676bc dns: support parser SRV record. 2023-12-07 22:27:29 +08:00
Nick Peng
9f32e01971 Update smartdns.c, remove unused code. 2023-12-07 09:18:44 +08:00
Nick Peng
c17f5df6cd smartdns: optimize smartdns.c readability 2023-12-06 23:03:32 +08:00
Nick Peng
9554b3debe domain-set: support punycode. 2023-12-06 22:56:48 +08:00
Nick Peng
11d92a67a1 dns_conf: conf-file option support wildcarding 2023-12-04 22:11:02 +08:00
Nick Peng
cde9fe5d5c dns_conf: set default check point time to 24h. 2023-12-02 19:23:33 +08:00
Nick Peng
52a35a5f88 smartdns: update tlog. 2023-12-02 18:38:50 +08:00
Nick Peng
ce18317a08 init-script: only set -R for init-script. 2023-12-01 21:54:24 +08:00
Nick Peng
fd2eb6980f smartdns: support restart smartdns in docker. 2023-12-01 20:41:31 +08:00
Nick Peng
407aba08c5 smartdns: add argument -R support auto restart smartdns for docker. 2023-11-30 22:26:17 +08:00
Nick Peng
7997300781 dns_cache: Fix several issues related to cache memory. 2023-11-30 21:59:08 +08:00
Nick Peng
d094a70078 lib: optimize time wheel code 2023-11-29 22:33:13 +08:00
Nick Peng
066c472467 openwrt: fix init script typo 2023-11-28 21:57:56 +08:00
Nick Peng
fb5b089740 luci-compat: update. 2023-11-21 21:36:55 +08:00
Nick Peng
62e0416d91 openwrt: auto create missing files 2023-11-21 21:30:43 +08:00
Nick Peng
d2e3ae6289 luci: support DOH, DOT server 2023-11-19 20:54:56 +08:00
Nick Peng
fa349b1864 log: output error to syslog when load config file. 2023-11-19 20:52:18 +08:00
Nick Peng
fc82c56947 dns_server: remove tcp keepalive EDNS cause DNSPOD not work. 2023-11-19 19:54:19 +08:00
Nick Peng
26798f8f1a dns_server: suppress some error log 2023-11-19 09:40:46 +08:00
Nick Peng
48ef6f2547 luci: add ip-rules tab page. 2023-11-18 23:25:20 +08:00
Nick Peng
f65c8a1280 luci: Download feature supports configuration weekly 2023-11-18 19:41:07 +08:00
Nick Peng
a10b21c822 dns_server: wait more time for fastest ip. 2023-11-16 23:54:07 +08:00
Nick Peng
9a83df46e0 dns_server: support TCP keepalive timeout EDNS 2023-11-16 23:54:07 +08:00
Nick Peng
a482ceeb5a dns_server: suppress tcp error logs 2023-11-15 22:54:18 +08:00
PikuZheng
4853a964c1 fix conf file missing while complie with openwrt sdk 2023-11-15 12:32:55 +08:00
Nick Peng
b3e16c3c60 dns_cache: fix issue of incorrect cache timeout when the process is restarted. 2023-11-14 23:21:04 +08:00
Nick Peng
eb0d0336de dns_server: fix issue of incorrect caching results when enable ip white list. 2023-11-14 23:09:34 +08:00
Nick Peng
d54d7cf80a dns_conf: add error logs when config is invalid. 2023-11-14 22:38:33 +08:00
Nick Peng
eb9fc6c13d smartdns: when running as non-root, no create pid file. 2023-11-14 22:23:31 +08:00
Nick Peng
c9524065a5 time_wheel: fix time wheel max tiemout value issue 2023-11-14 22:21:22 +08:00
Nick Peng
568f95dbdf smartdns: enhance reliability during initialization. 2023-11-14 22:18:06 +08:00
Nick Peng
79a49c6bd6 dns_conf: fix https server httphost issue. 2023-11-12 09:38:44 +08:00
Nick Peng
8c06923438 dns_server: fix some DOH server issue. 2023-11-11 23:57:18 +08:00
Nick Peng
ef806ecc9c feature: simple support DOH server 2023-11-11 09:58:39 +08:00
Nick Peng
d02bceabf1 dns_conf: fix server-https options issue. 2023-11-11 09:58:39 +08:00
zxlhhyccc
f1debd0878 Add update weekly or daily
At present, it is only updated daily, and under normal circumstances, the data updated daily is not large, so the addition can be updated weekly or daily.
2023-11-07 22:11:53 +08:00
Nick Peng
73c96cf932 dns-server: fix prefetch cache timeout issue 2023-11-07 22:09:34 +08:00
Nick Peng
7bfb4e070a dns_conf: Output log when configuration item is not supported 2023-11-07 00:15:00 +08:00
Nick Peng
14330dd5da dns_server: cache ptr record 2023-11-04 09:38:36 +08:00
totoro
2a8d3aff61 test-speed-check: increase timeout lim for lowend 2023-11-03 19:03:33 +08:00
Nick Peng
3d7db2dacf dns_conf: ip-alias support ip-set input 2023-11-02 22:51:44 +08:00
Nick Peng
aca8cf92cb dns_cache: refactor dns-cache.c, remove unused code. 2023-10-31 22:22:31 +08:00
Nick Peng
f39bdb64b2 dns_conf: Output log when option is not supported 2023-10-23 01:49:49 +00:00
Nick Peng
95c79ce1f6 dns_server: fix memory leak 2023-10-23 01:49:49 +00:00
zxlhhyccc
62171f2a4d add choose update time 2023-10-22 21:58:52 +08:00
Nick Peng
37a87e864e dns_conf: fix wildcard match issue 2023-10-06 22:51:36 +08:00
Nick Peng
a1d067f9eb dns_server: Fix address sub-rule issue. 2023-10-05 17:10:14 +08:00
Nick Peng
96d37332e4 dns_cache: Fix possible crash issue 2023-10-05 16:32:20 +08:00
Nick Peng
3916ea570a dns_cache: fix cache timeout issue 2023-09-27 23:19:45 +08:00
Nick Peng
51c81513ab dns_conf: add short option for server options 2023-09-24 22:55:10 +08:00
Nick Peng
1dd01ff4bd dns_cache: optimize timer wheel for DNS cache 2023-09-24 13:16:33 +08:00
Nick Peng
bfacad33ae dns_cache: Replace cache timeout mechanism with time wheel algorithm to reduce CPU usage 2023-09-23 23:31:19 +08:00
Nick Peng
b7fb501be9 dns_cache: fix insert issue. 2023-09-19 22:15:21 +08:00
Nick Peng
28139d2020 smartdns: Fixe coredump issue caused by running smartdns --help 2023-09-14 21:08:10 +08:00
Nick Peng
f7ede1b7d0 lint: clear lint warnings 2023-09-13 23:39:54 +08:00
Nick Peng
875100f5c1 dns_cache: optimize insertion performance 2023-09-12 21:49:14 +08:00
Frand Ren
1a492f7dc0 add domain rule "root or sub" 2023-09-12 09:22:59 +08:00
Nick Peng
1ff7829b49 dns: simple make DDR request SOA. 2023-09-09 17:20:42 +08:00
Nick Peng
8befd9d5d2 optware: fix init script restart dnsmasq failure. 2023-09-07 23:12:16 +08:00
Nick Peng
5658d72b3b dns_conf: update smartdns.conf and add -no-ip-alias for bind 2023-09-07 23:04:15 +08:00
Nick Peng
1b12709451 feature: add ip-rules and ip-set options 2023-09-06 23:25:13 +08:00
Nick Peng
c39a7b9b41 dns_cache: optimize dns cache. 2023-08-31 22:32:35 +08:00
Nick Peng
901baf80c0 ip-alias: add option -no-ipalias for domain-rules 2023-08-30 00:34:27 +08:00
Nick Peng
45e3455932 dns_cache: reduce cpu usage. 2023-08-29 23:55:04 +08:00
Nick Peng
887ef7b20e dns_cache: some cpu usage optimize for inactive cache 2023-08-24 23:42:39 +08:00
Nick Peng
9307855f7c dns_conf: fix ip-alias issue 2023-08-23 00:13:00 +08:00
Nick Peng
fb7b747f9f cache: Optimize cache memory usage 2023-08-22 23:18:31 +08:00
Nick Peng
7eb9d5d42f action: add docker build CI and update openssl for docker image. 2023-08-22 23:08:58 +08:00
Nick Peng
1054229efb feature: add ip-alias option. 2023-08-16 22:47:47 +08:00
Brainos
c19a39a447 Add nss-lookup.target as dependency for service 2023-08-14 23:27:51 +08:00
MoetaYuko
1ba6ee7cb9 openwrt: add missing EOF to custom.conf 2023-08-09 23:31:15 +08:00
Nick Peng
601ebd590e ssl: output error message when handshake failed. 2023-08-06 21:15:04 +08:00
Nick Peng
b133ce408a dns_conf: fix memory corruption issue when ip number greater than 8. 2023-07-28 22:42:36 +08:00
Nick Peng
8d3a62c568 dns_server: fix bogus-nxdomain issue. 2023-07-26 22:40:26 +08:00
Nick Peng
93a8b87c17 dns_server: fix memory corrupt bug. 2023-07-17 21:47:14 +08:00
Nick Peng
ffc331af21 dns-client: fix bootstrap retry failure issue when os startup. 2023-07-15 21:04:27 +08:00
Nick Peng
89e958abfa dns_client: avoid false re-creation of udp sockets causing retries. 2023-07-14 20:44:10 +08:00
Nick Peng
2576fdb02f dns_client: fix bootstrap dns retry issue. 2023-07-12 22:37:22 +08:00
Nick Peng
7ff6ae3ea0 dns_server: fix edns subnet not working issue. 2023-07-12 19:28:37 +08:00
Nick Peng
c2b072b523 conf: add ddns-domain options 2023-07-12 19:28:32 +08:00
Nick Peng
1df9d624b4 conf: add host-ip option for server. 2023-07-12 19:13:11 +08:00
Nick Peng
6b021946aa conf: support prefix wildcard match. 2023-07-05 00:08:29 +08:00
Nick Peng
087c9f5df2 conf: fix address issue when configuring multiple IPs 2023-07-01 09:20:42 +08:00
Nick Peng
e66928f27f ecs: Optimize ecs-subnet configuration method 2023-06-28 14:23:27 +08:00
Nick Peng
8a9a11d6d9 log: enable output log to console when run as daemon. 2023-06-16 21:57:39 +08:00
Nick Peng
a6e5ceb675 conf: trim prefix space for multiline option 2023-06-15 21:18:08 +08:00
Nick Peng
08567c458b address: support multiple ip addresses 2023-06-14 22:41:53 +08:00
Nick Peng
234c721011 test: fix test case failure issue 2023-06-14 22:40:12 +08:00
Chongyun Lee
45346705d8 tlog: fix declaration of tlog_set_permission 2023-06-11 07:02:37 +08:00
Nick Peng
9b7b2ad12d openwrt: fix adblock not working issue 2023-06-07 23:54:26 +08:00
Nick Peng
f072ff3412 dns_server: optimize result callback and update tlog. 2023-06-07 21:15:59 +08:00
Nick Peng
ad43c796cf force-qtype-SOA: support qtype range. 2023-06-05 22:55:47 +08:00
Nick Peng
f5c8d3ce57 dns_server: improve code readability 2023-06-02 22:52:46 +08:00
Nick Peng
f621b424e2 lint: add clang-tidy linter 2023-05-30 23:26:05 +08:00
Nick Peng
d59c148a28 smartdns: follow sysv daemon initialize steps 2023-05-30 23:25:25 +08:00
Nick Peng
8ea34ab176 dns_conf: A little bit of performance optimization 2023-05-27 22:51:00 +08:00
Nick Peng
0340d272c3 dns_server: fix max ttl reply issue. 2023-05-09 23:22:08 +08:00
64 changed files with 7680 additions and 1649 deletions

45
.clang-tidy Normal file
View File

@@ -0,0 +1,45 @@
Checks: >
-*,
modernize-*,
bugprone-*,
concurrency-*,
misc-*,
readability-*,
performance-*,
portability-*,
google-*,
linuxkernel-*,
-bugprone-narrowing-conversions,
-bugprone-branch-clone,
-bugprone-reserved-identifier,
-bugprone-easily-swappable-parameters,
-bugprone-sizeof-expression,
-bugprone-implicit-widening-of-multiplication-result,
-bugprone-suspicious-memory-comparison,
-bugprone-not-null-terminated-result,
-bugprone-signal-handler,
-bugprone-assignment-in-if-condition,
-concurrency-mt-unsafe,
-modernize-macro-to-enum,
-misc-unused-parameters,
-misc-misplaced-widening-cast,
-misc-no-recursion,
-readability-magic-numbers,
-readability-use-anyofallof,
-readability-identifier-length,
-readability-function-cognitive-complexity,
-readability-named-parameter,
-readability-isolate-declaration,
-readability-else-after-return,
-readability-redundant-control-flow,
-readability-suspicious-call-argument,
-google-readability-casting,
-google-readability-todo,
-performance-no-int-to-ptr,
# clang-analyzer-*,
# clang-analyzer-deadcode.DeadStores,
# clang-analyzer-optin.performance.Padding,
# -clang-analyzer-security.insecureAPI.*
# Turn all the warnings from the checks above into errors.
FormatStyle: file

35
.github/workflows/docker.yml vendored Normal file
View File

@@ -0,0 +1,35 @@
name: Publish Docker Image
on:
workflow_dispatch:
inputs:
version:
description: 'new image tag(e.g. v1.1.0)'
required: true
default: 'latest'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
docker:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to DockerHub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v3
with:
platforms: linux/amd64,linux/arm64
push: true
tags: ${{vars.DOCKERHUB_REPO}}:${{ github.event.inputs.version }}

View File

@@ -2,7 +2,7 @@ FROM ubuntu:latest as smartdns-builder
LABEL previous-stage=smartdns-builder
# prepare builder
ARG OPENSSL_VER=1.1.1f
ARG OPENSSL_VER=3.0.10
RUN apt update && \
apt install -y perl curl make musl-tools musl-dev && \
ln -s /usr/include/linux /usr/include/$(uname -m)-linux-musl && \
@@ -27,7 +27,7 @@ COPY . /build/smartdns/
RUN cd /build/smartdns && \
export CC=musl-gcc && \
export CFLAGS="-I /opt/build/include" && \
export LDFLAGS="-L /opt/build/lib" && \
export LDFLAGS="-L /opt/build/lib -L /opt/build/lib64" && \
sh ./package/build-pkg.sh --platform linux --arch `dpkg --print-architecture` --static && \
\
( cd package && tar -xvf *.tar.gz && chmod a+x smartdns/etc/init.d/smartdns ) && \
@@ -37,9 +37,9 @@ RUN cd /build/smartdns && \
cp package/smartdns/usr /release/ -a && \
cd / && rm -rf /build
FROM busybox:latest
FROM busybox:stable-musl
COPY --from=smartdns-builder /release/ /
EXPOSE 53/udp
VOLUME "/etc/smartdns/"
VOLUME ["/etc/smartdns/"]
CMD ["/usr/sbin/smartdns", "-f", "-x"]

View File

@@ -37,7 +37,7 @@ test -x $SMARTDNS || exit 5
case $1 in
start)
$SMARTDNS "$SMART_DNS_OPTS"
$SMARTDNS "$SMART_DNS_OPTS" -R
while true; do
if [ -e "$PIDFILE" ]; then
break;

View File

@@ -16,6 +16,7 @@
# Include another configuration options
# conf-file [file]
# conf-file blacklist-ip.conf
# conf-file *.conf
# dns server bind ip and port, default dns server port is 53, support binding multi ip and port
# bind udp server
@@ -30,6 +31,8 @@
# tls cert file
# bind-cert-key-pass [password]
# tls private key password
# bind-https server
# bind-https [IP]:[port][@device] [-group [group]] [-no-rule-addr] [-no-rule-nameserver] [-no-rule-ipset] [-no-speed-check] [-no-cache] [-no-rule-soa] [-no-dualstack-selection]
# option:
# -group: set domain request to use the appropriate server group.
# -no-rule-addr: skip address rule.
@@ -39,6 +42,7 @@
# -no-cache: skip cache.
# -no-rule-soa: Skip address SOA(#) rules.
# -no-dualstack-selection: Disable dualstack ip selection.
# -no-ip-alias: ignore ip alias.
# -force-aaaa-soa: force AAAA query return SOA.
# -ipset ipsetname: use ipset rule.
# -nftset nftsetname: use nftset rule.
@@ -100,6 +104,10 @@ bind [::]:53
# List of IPs that will be ignored
# ignore-ip [ip/subnet]
# alias of IPs
# ip-alias [ip/subnet] [ip1[,ip2]...]
# ip-alias 192.168.0.1/24 10.9.0.1,10.9.0.2
# speed check mode
# speed-check-mode [ping|tcp:port|none|,]
# example:
@@ -112,7 +120,7 @@ bind [::]:53
# force specific qtype return soa
# force-qtype-SOA [qtypeid |...]
# force-qtype-SOA [qtypeid,...]
# force-qtype-SOA [qtypeid|start_id-end_id|,...]
# force-qtype-SOA 65 28
# force-qtype-SOA 65,28
force-qtype-SOA 65
@@ -188,14 +196,16 @@ log-level info
# -blacklist-ip: filter result with blacklist ip
# -whitelist-ip: filter result with whitelist ip, result in whitelist-ip will be accepted.
# -check-edns: result must exist edns RR, or discard result.
# -group [group]: set server to group, use with nameserver /domain/group.
# -exclude-default-group: exclude this server from default group.
# -proxy [proxy-name]: use proxy to connect to server.
# g|-group [group]: set server to group, use with nameserver /domain/group.
# e|-exclude-default-group: exclude this server from default group.
# p|-proxy [proxy-name]: use proxy to connect to server.
# -bootstrap-dns: set as bootstrap dns server.
# -set-mark: set mark on packets.
# -subnet [ip/subnet]: set edns client subnet.
# -host-ip [ip]: set dns server host ip.
# server 8.8.8.8 -blacklist-ip -check-edns -group g1 -group g2
# server tls://dns.google:853
# server quic://dns.gooel.com:443
# server https://dns.google/dns-query
# remote tcp dns server list
@@ -208,8 +218,8 @@ log-level info
# -spki-pin: TLS spki pin to verify.
# -tls-host-verify: cert hostname to verify.
# -host-name: TLS sni hostname.
# -no-check-certificate: no check certificate.
# -proxy [proxy-name]: use proxy to connect to server.
# k|-no-check-certificate: no check certificate.
# p|-proxy [proxy-name]: use proxy to connect to server.
# -bootstrap-dns: set as bootstrap dns server.
# Get SPKI with this command:
# echo | openssl s_client -connect '[ip]:853' | openssl x509 -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64
@@ -217,14 +227,28 @@ log-level info
# server-tls 8.8.8.8
# server-tls 1.0.0.1
# remote quic dns server list
# server-quic [IP]:[PORT] [-blacklist-ip] [-whitelist-ip] [-spki-pin [sha256-pin]] [-group [group] ...] [-exclude-default-group]
# -spki-pin: TLS spki pin to verify.
# -tls-host-verify: cert hostname to verify.
# -host-name: TLS sni hostname.
# k|-no-check-certificate: no check certificate.
# p|-proxy [proxy-name]: use proxy to connect to server.
# -bootstrap-dns: set as bootstrap dns server.
# Get SPKI with this command:
# echo | openssl s_client -connect '[ip]:443' | openssl x509 -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64
# default port is 443
# server-quic 8.8.8.8
# server-quic 1.0.0.1
# remote https dns server list
# server-https https://[host]:[port]/path [-blacklist-ip] [-whitelist-ip] [-spki-pin [sha256-pin]] [-group [group] ...] [-exclude-default-group]
# -spki-pin: TLS spki pin to verify.
# -tls-host-verify: cert hostname to verify.
# -host-name: TLS sni hostname.
# -http-host: http host.
# -no-check-certificate: no check certificate.
# -proxy [proxy-name]: use proxy to connect to server.
# k|-no-check-certificate: no check certificate.
# p|-proxy [proxy-name]: use proxy to connect to server.
# -bootstrap-dns: set as bootstrap dns server.
# default port is 443
# server-https https://cloudflare-dns.com/dns-query
@@ -247,14 +271,20 @@ log-level info
# expand-ptr-from-address yes
# specific address to domain
# address /domain/[ip|-|-4|-6|#|#4|#6]
# address /domain/[ip1,ip2|-|-4|-6|#|#4|#6]
# address /www.example.com/1.2.3.4, return ip 1.2.3.4 to client
# address /www.example.com/1.2.3.4,5.6.7.8, return multiple ip addresses
# address /www.example.com/-, ignore address, query from upstream, suffix 4, for ipv4, 6 for ipv6, none for all
# address /www.example.com/#, return SOA to client, suffix 4, for ipv4, 6 for ipv6, none for all
# specific cname to domain
# cname /domain/target
# add srv record, support multiple srv record.
# srv-record /domain/[target][,port][,priority][,weight]
# srv-record /_ldap._tcp.example.com/ldapserver.example.com,389
# srv-record /_ldap._tcp.example.com/
# enalbe DNS64 feature
# dns64 [ip/subnet]
# dns64 64:ff9b::/96
@@ -289,6 +319,9 @@ log-level info
# nftset /www.example.com/-, ignore this domain
# nftset /www.example.com/#6:-, ignore ipv6
# set ddns domain
# ddns-domain domain
# set domain rules
# domain-rules /domain/ [-speed-check-mode [...]]
# rules:
@@ -301,6 +334,8 @@ log-level info
# [-d] -dualstack-ip-selection [yes|no]: same as dualstack-ip-selection option
# -no-serve-expired: ignore expired domain
# -delete: delete domain rule
# -no-ip-alias: ignore ip alias
# -no-cache: ignore cache
# collection of domains
# the domain-set can be used with /domain/ for address, nameserver, ipset, etc.
@@ -315,3 +350,25 @@ log-level info
# nameserver /domain-set:domain-list/server-group
# ipset /domain-set:domain-list/ipset
# domain-rules /domain-set:domain-list/ -speed-check-mode ping
# set ip rules
# ip-rules ip-cidrs [-ip-alias [...]]
# rules:
# [-c] -ip-alias [ip1,ip2]: same as ip-alias option
# [-a] -whitelist-ip: same as whitelist-ip option
# [-n] -blacklist-ip: same as blacklist-ip option
# [-p] -bogus-nxdomain: same as bogus-nxdomain option
# [-t] -ignore-ip: same as ignore-ip option
# collection of IPs
# the ip-set can be used with /ip-cidr/ for ip-alias, ignore-ip, etc.
# ip-set -name [set-name] -type list -file [/path/to/file]
# [-n] -name [set name]: ip set name
# [-t] -type [list]: ip set type, list only now
# [-f] -file [path/to/set]: file path of ip set
#
# example:
# ip-set -name ip-list -file /etc/smartdns/ip-list.conf
# bogus-nxdomain ip-set:ip-list
# ip-alias ip-set:ip-list 1.2.3.4
# ip-alias ip-set:ip-list ip-set:ip-map-list

View File

@@ -67,6 +67,18 @@ msgstr "配置分流域名列表"
msgid "Custom Settings"
msgstr "自定义设置"
msgid "DOH Server"
msgstr "DOH服务器"
msgid "DOH Server Port"
msgstr "DOH服务器端口"
msgid "DOT Server"
msgstr "DOT服务器"
msgid "DOT Server Port"
msgstr "DOT服务器端口"
msgid "DNS Block Setting"
msgstr "域名屏蔽设置"
@@ -184,8 +196,44 @@ msgstr "启用IPV6服务器。"
msgid "Enable TCP DNS Server"
msgstr "启用TCP服务器。"
msgid "Enable daily auto update."
msgstr "启用每自动更新"
msgid "Enable daily(week) auto update."
msgstr "启用每天(每周)自动更新"
msgid "Enable DOH DNS Server"
msgstr "启用DOH服务器。"
msgid "Enable DOT DNS Server"
msgstr "启用DOT服务器。"
msgid "Update Time (Every Week)"
msgstr "更新时间(每周)"
msgid "Every Day"
msgstr "每天"
msgid "Every Monday"
msgstr "每周一"
msgid "Every Tuesday"
msgstr "每周二"
msgid "Every Wednesday"
msgstr "每周三"
msgid "Every Thursday"
msgstr "每周四"
msgid "Every Friday"
msgstr "每周五"
msgid "Every Saturday"
msgstr "每周六"
msgid "Every Sunday"
msgstr "每周日"
msgid "Update Time (Every Day)"
msgstr "更新时间(每天)"
msgid "Enable domain prefetch, accelerate domain response speed."
msgstr "启用域名预加载,加速域名响应速度。"
@@ -398,6 +446,15 @@ msgstr "重启服务"
msgid "Second Server Settings"
msgstr "第二DNS服务器"
msgid "Server certificate file path."
msgstr "服务器证书文件路径。"
msgid "Server certificate key file path."
msgstr "服务器证书私钥文件路径。"
msgid "Server certificate key file password."
msgstr "服务器证书私钥文件密码。"
msgid "Serve expired"
msgstr "缓存过期服务"
@@ -410,6 +467,15 @@ msgstr "服务器组%s不存在"
msgid "Server Name"
msgstr "服务器名称"
msgid "Server Cert"
msgstr "服务器证书"
msgid "Server Cert Key"
msgstr "服务器证书私钥"
msgid "Server Cert Key Pass"
msgstr "服务器证书私钥密码"
msgid "Set Specific domain ip address."
msgstr "设置指定域名的IP地址。"
@@ -478,6 +544,12 @@ msgstr "跳过Nameserver规则。"
msgid "SmartDNS"
msgstr "SmartDNS"
msgid "Smartdns DOH server port."
msgstr "Smartdns DOH服务器端口号。
msgid "Smartdns DOT server port."
msgstr "Smartdns DOT服务器端口号。"
msgid "SmartDNS Server"
msgstr "SmartDNS 服务器"
@@ -648,4 +720,4 @@ msgid "type"
msgstr "类型"
msgid "udp"
msgstr "udp"
msgstr "udp"

View File

@@ -130,6 +130,56 @@ o.cfgvalue = function(...)
return Flag.cfgvalue(...) or "1"
end
---- Enable DOT server;
o = s:taboption("advanced", Flag, "tls_server", translate("DOT Server"), translate("Enable DOT DNS Server"))
o.rmempty = false
o.default = o.disabled
o.cfgvalue = function(...)
return Flag.cfgvalue(...) or "0"
end
o = s:taboption("advanced", Value, "tls_server_port", translate("DOT Server Port"), translate("Smartdns DOT server port."))
o.placeholder = 853
o.default = 853
o.datatype = "port"
o.rempty = false
o:depends('tls_server', '1')
---- Enable DOH server;
o = s:taboption("advanced", Flag, "doh_server", translate("DOH Server"), translate("Enable DOH DNS Server"))
o.rmempty = false
o.default = o.disabled
o.cfgvalue = function(...)
return Flag.cfgvalue(...) or "0"
end
o = s:taboption("advanced", Value, "doh_server_port", translate("DOH Server Port"), translate("Smartdns DOH server port."))
o.placeholder = 843
o.default = 843
o.datatype = "port"
o.rempty = false
o:depends('doh_server', '1')
o = s:taboption("advanced", Value, "bind_cert", translate("Server Cert"), translate("Server certificate file path."))
o.datatype = "string"
o.placeholder = "/var/etc/smartdns/smartdns/smartdns-cert.pem"
o.rempty = true
o:depends('tls_server', '1')
o:depends('doh_server', '1')
o = s:taboption("advanced", Value, "bind_cert_key", translate("Server Cert Key"), translate("Server certificate key file path."))
o.datatype = "string"
o.placeholder = "/var/etc/smartdns/smartdns/smartdns-key.pem"
o.rempty = false
o:depends('tls_server', '1')
o:depends('doh_server', '1')
o = s:taboption("advanced", Value, "bind_cert_key_pass", translate("Server Cert Key Pass"), translate("Server certificate key file password."))
o.datatype = "string"
o.rempty = false
o:depends('tls_server', '1')
o:depends('doh_server', '1')
---- Support IPV6
o = s:taboption("advanced", Flag, "ipv6_server", translate("IPV6 Server"), translate("Enable IPV6 DNS Server"))
o.rmempty = false
@@ -671,11 +721,26 @@ s = m:section(TypedSection, "smartdns", translate("Download Files Setting"), tra
s.anonymous = true
---- download Files Settings
o = s:option(Flag, "enable_auto_update", translate("Enable Auto Update"), translate("Enable daily auto update."))
o = s:option(Flag, "enable_auto_update", translate("Enable Auto Update"), translate("Enable daily(week) auto update."))
o.rmempty = true
o.default = o.disabled
o.rempty = true
o = s:option(ListValue, "auto_update_week_time", translate("Update Time (Every Week)"))
o:value("*", translate("Every Day"))
o:value("1", translate("Every Monday"))
o:value("2", translate("Every Tuesday"))
o:value("3", translate("Every Wednesday"))
o:value("4", translate("Every Thursday"))
o:value("5", translate("Every Friday"))
o:value("6", translate("Every Saturday"))
o:value("0", translate("Every Sunday"))
o.default = "*"
o = s:option(ListValue, "auto_update_day_time", translate("Update Time (Every Day)"))
for i = 0, 23 do o:value(i, i .. ":00") end
o.default = 5
o = s:option(FileUpload, "upload_conf_file", translate("Upload Config File"),
translate("Upload smartdns config file to /etc/smartdns/conf.d"))
o.rmempty = true
@@ -780,4 +845,3 @@ o.write = function()
end
return m

View File

@@ -6,6 +6,10 @@ msgid ""
"Additional Flags for rules, read help on domain-rule for more information."
msgstr "额外的规则标识具体参考domain-rule的帮助说明。"
msgid ""
"Additional Flags for rules, read help on ip-rule for more information."
msgstr "额外的规则标识具体参考ip-rule的帮助说明。"
msgid "Additional Rule Flag"
msgstr "额外规则标识"
@@ -29,12 +33,21 @@ msgstr "自动设置Dnsmasq"
msgid "Automatically set as upstream of dnsmasq when port changes."
msgstr "端口更改时自动设为 dnsmasq 的上游。"
msgid "Blacklist IP"
msgstr "黑名单"
msgid "Blacklist IP Rule, Decline IP addresses within the range."
msgstr "黑名单规则拒绝指定范围的IP地址。"
msgid "Bind Device"
msgstr "绑定到设备"
msgid "Bind Device Name"
msgstr "绑定的设备名称"
msgid "Bogus nxdomain"
msgstr "假冒IP"
msgid "Block domain"
msgstr "屏蔽域名"
@@ -64,9 +77,27 @@ msgstr "配置域名规则列表"
msgid "Configure forwarding domain name list."
msgstr "配置分流域名列表"
msgid "Configure ip rule list."
msgstr "配置IP规则列表"
msgid "Custom Settings"
msgstr "自定义设置"
msgid "Do not use these IP addresses."
msgstr "忽略这些IP地址"
msgid "DOH Server"
msgstr "DOH服务器"
msgid "DOH Server Port"
msgstr "DOH服务器端口"
msgid "DOT Server"
msgstr "DOT服务器"
msgid "DOT Server Port"
msgstr "DOT服务器端口"
msgid "DNS Block Setting"
msgstr "域名屏蔽设置"
@@ -184,8 +215,44 @@ msgstr "启用IPV6服务器。"
msgid "Enable TCP DNS Server"
msgstr "启用TCP服务器。"
msgid "Enable daily auto update."
msgstr "启用每日自动更新"
msgid "Enable daily (weekly) auto update."
msgstr "启用每日(每周)自动更新"
msgid "Enable DOH DNS Server"
msgstr "启用DOH服务器"
msgid "Enable DOT DNS Server"
msgstr "启用DOT服务器"
msgid "Update time (every day)"
msgstr "更新时间(每天)"
msgid "Update Time (Every Week)"
msgstr "更新时间(每周)"
msgid "Every Day"
msgstr "每天"
msgid "Every Monday"
msgstr "每周一"
msgid "Every Tuesday"
msgstr "每周二"
msgid "Every Wednesday"
msgstr "每周三"
msgid "Every Thursday"
msgstr "每周四"
msgid "Every Friday"
msgstr "每周五"
msgid "Every Saturday"
msgstr "每周六"
msgid "Every Sunday"
msgstr "每周日"
msgid "Enable domain prefetch, accelerate domain response speed."
msgstr "启用域名预加载,加速域名响应速度。"
@@ -259,6 +326,30 @@ msgstr "IP黑名单过滤"
msgid "IPV6 Server"
msgstr "IPV6服务器"
msgid "IP alias"
msgstr "IP别名"
msgid "IP Addresses"
msgstr "IP地址"
msgid "IP Address Mapping, Can be used for CDN acceleration with Anycast IP, such as Cloudflare's CDN."
msgstr "IP地址映射可用于支持AnyCast IP的CDN加速比如Cloudflare的CDN。"
msgid "Ignore IP"
msgstr "忽略IP"
msgid "IP Rule List"
msgstr "IP规则列表"
msgid "IP Rule Name"
msgstr "IP规则名称"
msgid "IP Set File"
msgstr "IP集合列表文件"
msgid "IP addresses, CIDR format."
msgstr "IP地址CIDR格式。"
msgid "IPset Name"
msgstr "IPset名称"
@@ -349,6 +440,9 @@ msgstr "无"
msgid "Only socks5 proxy support udp server."
msgstr "仅SOCKS5代理支持UDP服务器。"
msgid "Please check the system logs and check if the configuration is valid."
msgstr "请检查系统日志,并检查配置是否合法。"
msgid "Please set proxy server first."
msgstr "请先设置代理服务器。"
@@ -380,6 +474,9 @@ msgstr "设置返回给客户端的域名TTL最大值。"
msgid "Report bugs"
msgstr "报告BUG"
msgid "Return SOA when the requested result contains a specified IP address."
msgstr "当结果包含对应范围的IP时返回SOA。"
msgid "Resolve Local Hostnames"
msgstr "解析本地主机名"
@@ -398,6 +495,15 @@ msgstr "重启服务"
msgid "Second Server Settings"
msgstr "第二DNS服务器"
msgid "Server certificate file path."
msgstr "服务器证书文件路径。"
msgid "Server certificate key file path."
msgstr "服务器证书私钥文件路径。"
msgid "Server certificate key file password."
msgstr "服务器证书私钥文件密码。"
msgid "Serve expired"
msgstr "缓存过期服务"
@@ -410,6 +516,15 @@ msgstr "服务器组%s不存在"
msgid "Server Name"
msgstr "服务器名称"
msgid "Server Cert"
msgstr "服务器证书"
msgid "Server Cert Key"
msgstr "服务器证书私钥"
msgid "Server Cert Key Pass"
msgstr "服务器证书私钥密码"
msgid "Set Specific domain ip address."
msgstr "设置指定域名的IP地址。"
@@ -419,6 +534,9 @@ msgstr "设置指定域名的规则列表。"
msgid "Set Specific ip blacklist."
msgstr "设置指定的 IP 黑名单列表。"
msgid "Set Specific ip rule list."
msgstr "设置对应IP的规则。"
msgid "Set TLS hostname to verify."
msgstr "设置校验TLS主机名。"
@@ -478,6 +596,12 @@ msgstr "跳过Nameserver规则。"
msgid "SmartDNS"
msgstr "SmartDNS"
msgid "Smartdns DOH server port."
msgstr "Smartdns DOH服务器端口号。
msgid "Smartdns DOT server port."
msgstr "Smartdns DOT服务器端口号。"
msgid "SmartDNS Server"
msgstr "SmartDNS 服务器"
@@ -575,6 +699,9 @@ msgstr "上传域名列表文件,或在下载文件设置页面设置自动下
msgid "Upload domain list file."
msgstr "上传域名列表文件"
msgid "Upload IP set file."
msgstr "上传IP集合列表文件。"
msgid "Upload smartdns config file to /etc/smartdns/conf.d"
msgstr "上传配置文件到/etc/smartdns/conf.d"
@@ -602,6 +729,12 @@ msgstr ""
"用于校验 TLS 服务器的有效性,数值为 Base64 编码的 SPKI 指纹,留空表示不验证 "
"TLS 的合法性。"
msgid "Whitelist IP"
msgstr "白名单"
msgid "Whitelist IP Rule, Accept IP addresses within the range."
msgstr "白名单规则接受指定范围的IP地址。"
msgid "Write cache to disk on exit and load on startup."
msgstr "退出时保存cache到磁盘启动时加载。"

View File

@@ -58,12 +58,17 @@ function smartdnsRenderStatus(res) {
var autoSetDnsmasq = uci.get_first('smartdns', 'smartdns', 'auto_set_dnsmasq');
var smartdnsPort = uci.get_first('smartdns', 'smartdns', 'port');
var smartdnsEnable = uci.get_first('smartdns', 'smartdns', 'enabled');
var dnsmasqServer = uci.get_first('dhcp', 'dnsmasq', 'server');
if (isRunning) {
renderHTML += "<span style=\"color:green;font-weight:bold\">SmartDNS - " + _("RUNNING") + "</span>";
} else {
renderHTML += "<span style=\"color:red;font-weight:bold\">SmartDNS - " + _("NOT RUNNING") + "</span>";
if (smartdnsEnable === '1') {
renderHTML += "<br /><span style=\"color:red;font-weight:bold\">" + _("Please check the system logs and check if the configuration is valid.");
renderHTML += "</span>";
}
return renderHTML;
}
@@ -79,7 +84,6 @@ function smartdnsRenderStatus(res) {
return renderHTML;
}
return view.extend({
load: function () {
return Promise.all([
@@ -156,7 +160,7 @@ return view.extend({
o.default = 53;
o.datatype = "port";
o.rempty = false;
// auto-conf-dnsmasq;
o = s.taboption("settings", form.Flag, "auto_set_dnsmasq", _("Automatically Set Dnsmasq"), _("Automatically set as upstream of dnsmasq when port changes."));
o.rmempty = false;
@@ -220,6 +224,50 @@ return view.extend({
o.rmempty = false;
o.default = o.enabled;
// Enable DOT server;
o = s.taboption("advanced", form.Flag, "tls_server", _("DOT Server"), _("Enable DOT DNS Server"));
o.rmempty = false;
o.default = o.disabled;
o = s.taboption("advanced", form.Value, "tls_server_port", _("DOT Server Port"), _("Smartdns DOT server port."));
o.placeholder = 853;
o.default = 853;
o.datatype = "port";
o.rempty = false;
o.depends('tls_server', '1');
// Enable DOH server;
o = s.taboption("advanced", form.Flag, "doh_server", _("DOH Server"), _("Enable DOH DNS Server"));
o.rmempty = false;
o.default = o.disabled;
o = s.taboption("advanced", form.Value, "doh_server_port", _("DOH Server Port"), _("Smartdns DOH server port."));
o.placeholder = 843;
o.default = 843;
o.datatype = "port";
o.rempty = false;
o.depends('doh_server', '1');
o = s.taboption("advanced", form.Value, "bind_cert", _("Server Cert"), _("Server certificate file path."));
o.datatype = "string";
o.placeholder = "/var/etc/smartdns/smartdns/smartdns-cert.pem"
o.rempty = true;
o.depends('tls_server', '1');
o.depends('doh_server', '1');
o = s.taboption("advanced", form.Value, "bind_cert_key", _("Server Cert Key"), _("Server certificate key file path."));
o.datatype = "string";
o.placeholder = "/var/etc/smartdns/smartdns/smartdns-key.pem"
o.rempty = false;
o.depends('tls_server', '1');
o.depends('doh_server', '1');
o = s.taboption("advanced", form.Value, "bind_cert_key_pass", _("Server Cert Key Pass"), _("Server certificate key file password."));
o.datatype = "string";
o.rempty = false;
o.depends('tls_server', '1');
o.depends('doh_server', '1');
// Support IPV6;
o = s.taboption("advanced", form.Flag, "ipv6_server", _("IPV6 Server"), _("Enable IPV6 DNS Server"));
o.rmempty = false;
@@ -279,7 +327,7 @@ return view.extend({
o.default = o.enabled;
// Ipset no speed.
o = s.taboption("advanced", form.Value, "ipset_no_speed", _("No Speed IPset Name"),
o = s.taboption("advanced", form.Value, "ipset_no_speed", _("No Speed IPset Name"),
_("Ipset name, Add domain result to ipset when speed check fails."));
o.rmempty = true;
o.datatype = "string";
@@ -300,7 +348,7 @@ return view.extend({
}
// NFTset no speed.
o = s.taboption("advanced", form.Value, "nftset_no_speed", _("No Speed NFTset Name"),
o = s.taboption("advanced", form.Value, "nftset_no_speed", _("No Speed NFTset Name"),
_("Nftset name, Add domain result to nftset when speed check fails, format: [#[4|6]:[family#table#set]]"));
o.rmempty = true;
o.datatype = "string";
@@ -343,7 +391,7 @@ return view.extend({
o.rempty = true;
// other args
o = s.taboption("advanced", form.Value, "server_flags", _("Additional Server Args"),
o = s.taboption("advanced", form.Value, "server_flags", _("Additional Server Args"),
_("Additional server args, refer to the help description of the bind option."))
o.default = ""
o.rempty = true
@@ -476,7 +524,7 @@ return view.extend({
}
// other args
o = s.taboption("seconddns", form.Value, "seconddns_server_flags", _("Additional Server Args"),
o = s.taboption("seconddns", form.Value, "seconddns_server_flags", _("Additional Server Args"),
_("Additional server args, refer to the help description of the bind option."))
o.default = ""
o.rempty = true
@@ -492,11 +540,29 @@ return view.extend({
///////////////////////////////////////
// download Files Settings
///////////////////////////////////////
o = s.taboption("files", form.Flag, "enable_auto_update", _("Enable Auto Update"), _("Enable daily auto update."));
o = s.taboption("files", form.Flag, "enable_auto_update", _("Enable Auto Update"), _("Enable daily (weekly) auto update."));
o.rmempty = true;
o.default = o.disabled;
o.rempty = true;
o = s.taboption("files", form.ListValue, "auto_update_week_time", _("Update Time (Every Week)"));
o.value('*', _('Every Day'));
o.value('1', _('Every Monday'));
o.value('2', _('Every Tuesday'));
o.value('3', _('Every Wednesday'));
o.value('4', _('Every Thursday'));
o.value('5', _('Every Friday'));
o.value('6', _('Every Saturday'));
o.value('0', _('Every Sunday'));
o.default = "*";
o.depends('enable_auto_update', '1');
o = s.taboption('files', form.ListValue, 'auto_update_day_time', _("Update time (every day)"));
for (var i = 0; i < 24; i++)
o.value(i, i + ':00');
o.default = '5';
o.depends('enable_auto_update', '1');
o = s.taboption("files", form.FileUpload, "upload_conf_file", _("Upload Config File"),
_("Upload smartdns config file to /etc/smartdns/conf.d"));
o.rmempty = true
@@ -804,6 +870,7 @@ return view.extend({
s.tab("forwarding", _('DNS Forwarding Setting'));
s.tab("block", _("DNS Block Setting"));
s.tab("domain-rule-list", _("Domain Rule List"), _("Set Specific domain rule list."));
s.tab("ip-rule-list", _("IP Rule List"), _("Set Specific ip rule list."));
s.tab("domain-address", _("Domain Address"), _("Set Specific domain ip address."));
s.tab("blackip-list", _("IP Blacklist"), _("Set Specific ip blacklist."));
@@ -1131,6 +1198,72 @@ return view.extend({
});
};
///////////////////////////////////////
// ip rule list;
///////////////////////////////////////
o = s.taboption('ip-rule-list', form.SectionValue, '__ip-rule-list__', form.GridSection, 'ip-rule-list', _('IP Rule List'),
_('Configure ip rule list.'));
ss = o.subsection;
ss.addremove = true;
ss.anonymous = true;
ss.sortable = true;
// enable flag;
so = ss.option(form.Flag, "enabled", _("Enable"), _("Enable"));
so.rmempty = false;
so.default = so.enabled;
so.editable = true;
// name;
so = ss.option(form.Value, "name", _("IP Rule Name"), _("IP Rule Name"));
so.rmempty = true;
so.datatype = "string";
so = ss.option(form.FileUpload, "ip_set_file", _("IP Set File"), _("Upload IP set file."));
so.rmempty = true
so.datatype = "file"
so.modalonly = true;
so.root_directory = "/etc/smartdns/ip-set"
so = ss.option(form.DynamicList, "ip_addr", _("IP Addresses"), _("IP addresses, CIDR format."));
so.rmempty = true;
so.datatype = "ipaddr"
so.modalonly = true;
so = ss.option(form.Flag, "whitelist_ip", _("Whitelist IP"), _("Whitelist IP Rule, Accept IP addresses within the range."));
so.rmempty = true;
so.default = so.disabled;
so.modalonly = true;
so = ss.option(form.Flag, "blacklist_ip", _("Blacklist IP"), _("Blacklist IP Rule, Decline IP addresses within the range."));
so.rmempty = true;
so.default = so.disabled;
so.modalonly = true;
so = ss.option(form.Flag, "ignore_ip", _("Ignore IP"), _("Do not use these IP addresses."));
so.rmempty = true;
so.default = so.disabled;
so.modalonly = true;
so = ss.option(form.Flag, "bogus_nxdomain", _("Bogus nxdomain"), _("Return SOA when the requested result contains a specified IP address."));
so.rmempty = true;
so.default = so.disabled;
so.modalonly = true;
so = ss.option(form.DynamicList, "ip_alias", _("IP alias"), _("IP Address Mapping, Can be used for CDN acceleration with Anycast IP, such as Cloudflare's CDN."));
so.rmempty = true;
so.datatype = 'ipaddr("nomask")';
so.modalonly = true;
// other args
so = ss.option(form.Value, "addition_flag", _("Additional Rule Flag"),
_("Additional Flags for rules, read help on ip-rule for more information."))
so.default = ""
so.rempty = true
so.modalonly = true;
////////////////
// Support
////////////////

View File

@@ -50,12 +50,14 @@ endef
define Package/smartdns/install
$(INSTALL_DIR) $(1)/usr/sbin $(1)/etc/config $(1)/etc/init.d
$(INSTALL_DIR) $(1)/etc/smartdns $(1)/etc/smartdns/domain-set $(1)/etc/smartdns/conf.d/
$(INSTALL_DIR) $(1)/etc/smartdns $(1)/etc/smartdns/domain-set $(1)/etc/smartdns/conf.d/ $(1)/etc/smartdns/ip-set
$(INSTALL_BIN) $(PKG_BUILD_DIR)/src/smartdns $(1)/usr/sbin/smartdns
$(INSTALL_BIN) $(PKG_BUILD_DIR)/package/openwrt/files/etc/init.d/smartdns $(1)/etc/init.d/smartdns
$(INSTALL_CONF) $(PKG_BUILD_DIR)/package/openwrt/address.conf $(1)/etc/smartdns/address.conf
$(INSTALL_CONF) $(PKG_BUILD_DIR)/package/openwrt/blacklist-ip.conf $(1)/etc/smartdns/blacklist-ip.conf
$(INSTALL_CONF) $(PKG_BUILD_DIR)/package/openwrt/custom.conf $(1)/etc/smartdns/custom.conf
$(INSTALL_CONF) $(PKG_BUILD_DIR)/package/openwrt/domain-block.list $(1)/etc/smartdns/domain-block.list
$(INSTALL_CONF) $(PKG_BUILD_DIR)/package/openwrt/domain-forwarding.list $(1)/etc/smartdns/domain-forwarding.list
$(INSTALL_CONF) $(PKG_BUILD_DIR)/package/openwrt/files/etc/config/smartdns $(1)/etc/config/smartdns
endef

View File

@@ -1,2 +1,2 @@
# Add custom settings here.
# please read https://pymumu.github.io/smartdns/config/basic-config/
# please read https://pymumu.github.io/smartdns/config/basic-config/

View File

@@ -27,6 +27,8 @@ if [ ! -d "/run" ]; then
SERVICE_PID_FILE="/var/run/smartdns.pid"
fi
SMARTDNS_DEFAULT_FORWARDING_FILE="/etc/smartdns/domain-forwarding.list"
SMARTDNS_DEFAULT_DOMAIN_BLOCK_FILE="/etc/smartdns/domain-block.list"
SMARTDNS_CONF_DIR="/etc/smartdns"
SMARTDNS_CONF_DOWNLOAD_DIR="$SMARTDNS_CONF_DIR/conf.d"
SMARTDNS_DOMAIN_LIST_DOWNLOAD_DIR="$SMARTDNS_CONF_DIR/domain-set"
@@ -124,6 +126,10 @@ clear_iptable()
{
local OLD_PORT="$1"
local ipv6_server=$2
which iptables >/dev/null 2>&1
[ $? -ne 0 ] && return
IPS="$(ifconfig | grep "inet addr" | grep -v ":127" | grep "Bcast" | awk '{print $2}' | awk -F : '{print $2}')"
for IP in $IPS
do
@@ -260,13 +266,13 @@ disable_auto_update()
enable_auto_update()
{
grep "0 5 * * * /etc/init.d/smartdns updatefiles" /etc/crontabs/root 2>/dev/null
grep "0 $auto_update_day_time * * $auto_update_week_time /etc/init.d/smartdns updatefiles" /etc/crontabs/root 2>/dev/null
if [ $? -eq 0 ]; then
return
fi
disable_auto_update 1
echo "0 5 * * * /etc/init.d/smartdns updatefiles" >> /etc/crontabs/root
echo "0 $auto_update_day_time * * $auto_update_week_time /etc/init.d/smartdns updatefiles" >> /etc/crontabs/root
restart_crond
}
@@ -296,23 +302,27 @@ load_domain_rules()
config_get forwarding_domain_set_file "$section" "forwarding_domain_set_file" ""
[ ! -z "$forwarding_domain_set_file" ] && {
[ ! -e "$forwarding_domain_set_file" ] && touch $forwarding_domain_set_file
conf_append "domain-set" "-name ${domain_set_name}-forwarding-file -file '$forwarding_domain_set_file'"
conf_append "domain-rules" "/domain-set:${domain_set_name}-forwarding-file/ $domain_set_args"
}
[ ! -z "$domain_set_args" ] && {
conf_append "domain-set" "-name ${domain_set_name}-forwarding-list -file /etc/smartdns/domain-forwarding.list"
[ ! -e "$SMARTDNS_DEFAULT_FORWARDING_FILE" ] && touch $SMARTDNS_DEFAULT_FORWARDING_FILE
conf_append "domain-set" "-name ${domain_set_name}-forwarding-list -file $SMARTDNS_DEFAULT_FORWARDING_FILE"
conf_append "domain-rules" "/domain-set:${domain_set_name}-forwarding-list/ $domain_set_args"
}
config_get block_domain_set_file "$section" "block_domain_set_file"
[ ! -z "$block_domain_set_file" ] && {
[ ! -e "$block_domain_set_file" ] && touch $block_domain_set_file
conf_append "domain-set" "-name ${domain_set_name}-block-file -file '$block_domain_set_file'"
conf_append "domain-rules" "/domain-set:${domain_set_name}-block-file/ -group block"
conf_append "domain-rules" "/domain-set:${domain_set_name}-block-file/ -address #"
}
conf_append "domain-set" "-name ${domain_set_name}-block-list -file /etc/smartdns/domain-block.list"
conf_append "domain-rules" "/domain-set:${domain_set_name}-block-list/ --address #"
[ ! -e "$SMARTDNS_DEFAULT_DOMAIN_BLOCK_FILE" ] && touch $SMARTDNS_DEFAULT_DOMAIN_BLOCK_FILE
conf_append "domain-set" "-name ${domain_set_name}-block-list -file $SMARTDNS_DEFAULT_DOMAIN_BLOCK_FILE"
conf_append "domain-rules" "/domain-set:${domain_set_name}-block-list/ -address #"
}
load_domain_rule_list()
@@ -355,16 +365,64 @@ load_domain_rule_list()
[ ! -z "$addition_flag" ] && domain_set_args="$domain_set_args $addition_flag"
[ -z "$domain_set_args" ] && return
[ ! -e "$domain_list_file" ] && touch $domain_list_file
conf_append "domain-set" "-name domain-rule-list-${domain_set_name} -file '$domain_list_file'"
conf_append "domain-rules" "/domain-set:domain-rule-list-${domain_set_name}/ $domain_set_args"
}
ip_rule_addr_append()
{
conf_append "ip-rules" "$1 $IP_set_args"
}
load_IP_rule_list()
{
local section="$1"
local IP_set_args=""
local IP_set_name="$section"
config_get_bool enabled "$section" "enabled" "0"
[ "$enabled" != "1" ] && return
config_get ip_set_file "$section" "ip_set_file" ""
config_get_bool whitelist_ip "$section" "whitelist_ip" "0"
[ "$whitelist_ip" = "1" ] && IP_set_args="$IP_set_args -whitelist-ip"
config_get_bool blacklist_ip "$section" "blacklist_ip" "0"
[ "$blacklist_ip" = "1" ] && IP_set_args="$IP_set_args -blacklist-ip"
config_get_bool ignore_ip "$section" "ignore_ip" "0"
[ "$ignore_ip" = "1" ] && IP_set_args="$IP_set_args -ignore-ip"
config_get_bool bogus_nxdomain "$section" "bogus_nxdomain" "0"
[ "$bogus_nxdomain" = "1" ] && IP_set_args="$IP_set_args -bogus-nxdomain"
config_get ip_alias "$section" "ip_alias" ""
[ ! -z "$ip_alias" ] && {
ip_alias="$(echo "$ip_alias" | sed 's/ /,/g')"
IP_set_args="$IP_set_args -ip-alias $ip_alias"
}
config_get addition_flag "$section" "addition_flag" ""
[ ! -z "$addition_flag" ] && IP_set_args="$IP_set_args $addition_flag"
[ -z "$IP_set_args" ] && return
[ ! -z "$ip_set_file" ] && [ -e "$ip_set_file" ] && {
conf_append "ip-set" "-name ip-rule-list-file-${section} -file '$ip_set_file'"
conf_append "ip-rules" "ip-set:ip-rule-list-file-${section} $IP_set_args"
}
config_list_foreach "$section" "ip_addr" ip_rule_addr_append
}
conf_append_bind()
{
local ADDR=""
local port="$1"
local devices="$2"
local tcp_server="$3"
local bind_type="$1"
local port="$2"
local devices="$3"
local device=""
local ipv6_server="$4"
local ARGS="$5"
@@ -381,16 +439,8 @@ conf_append_bind()
for device in $devices; do
device="@$device"
[ "$device" = "@-" ] && device=""
conf_append "bind" "$ADDR:$port$device $ARGS"
conf_append "$bind_type" "$ADDR:$port$device $ARGS"
done
[ "$tcp_server" = "1" ] && {
for device in $devices; do
device="@$device"
[ "$device" = "@-" ] && device=""
conf_append "bind-tcp" "$ADDR:$port$device $ARGS"
done
}
}
load_second_server()
@@ -448,7 +498,8 @@ load_second_server()
config_get seconddns_server_flags "$section" "seconddns_server_flags" ""
[ -z "$seconddns_server_flags" ] || ARGS="$ARGS $seconddns_server_flags"
conf_append_bind "$seconddns_port" "$device" "$seconddns_tcp_server" "$ipv6_server" "$ARGS"
conf_append_bind "bind" "$seconddns_port" "$device" "$ipv6_server" "$ARGS"
[ "$seconddns_tcp_server" = "1" ] && conf_append_bind "bind-tcp" "$seconddns_port" "$device" "$ipv6_server" "$ARGS"
}
conf_append_conf_files()
@@ -496,8 +547,18 @@ load_service()
config_get port "$section" "port" "53"
config_get ipv6_server "$section" "ipv6_server" "1"
config_get tcp_server "$section" "tcp_server" "1"
config_get tls_server "$section" "tls_server" "0"
config_get tls_server_port "$section" "tls_server_port" "853"
config_get doh_server "$section" "doh_server" "0"
config_get doh_server_port "$section" "doh_server_port" "843"
config_get bind_cert "$section" "bind_cert" ""
config_get bind_cert_key "$section" "bind_cert_key" ""
config_get bind_cert_key_pass "$section" "bind_cert_key_pass" ""
config_get server_flags "$section" "server_flags" ""
config_get auto_update_week_time "$section" "auto_update_week_time" "*"
config_get auto_update_day_time "$section" "auto_update_day_time" "5"
config_get speed_check_mode "$section" "speed_check_mode" ""
[ ! -z "$speed_check_mode" ] && conf_append "speed-check-mode" "$speed_check_mode"
@@ -649,7 +710,14 @@ load_service()
[ "$auto_set_dnsmasq" = "0" ] && [ "$old_auto_set_dnsmasq" = "1" ] && stop_forward_dnsmasq "$old_port" "0"
}
conf_append_bind "$port" "$device" "$tcp_server" "$ipv6_server" "$server_flags"
conf_append_bind "bind" "$port" "$device" "$ipv6_server" "$server_flags"
[ "$tcp_server" = "1" ] && conf_append_bind "bind-tcp" "$port" "$device" "$ipv6_server" "$server_flags"
[ "$tls_server" = "1" ] && conf_append_bind "bind-tls" "$tls_server_port" "$device" "$ipv6_server" "$server_flags"
[ "$doh_server" = "1" ] && conf_append_bind "bind-https" "$doh_server_port" "$device" "$ipv6_server" "$server_flags"
[ ! -z "$bind_cert" ] && conf_append "bind-cert-file" "$bind_cert"
[ ! -z "$bind_cert_key" ] && conf_append "bind-cert-key-file" "$bind_cert_key"
[ ! -z "$bind_cert_key_pass" ] && conf_append "bind-cert-key-pass" "$bind_cert_key_pass"
load_second_server "$section"
@@ -661,6 +729,8 @@ load_service()
config_foreach load_domain_rule_list "domain-rule-list"
config_foreach load_IP_rule_list "ip-rule-list"
{
echo "conf-file $ADDRESS_CONF"
echo "conf-file $BLACKLIST_IP_CONF"

View File

@@ -48,6 +48,7 @@ build()
mkdir $ROOT/root/etc/init.d -p
mkdir $ROOT/root/etc/smartdns/ -p
mkdir $ROOT/root/etc/smartdns/domain-set/ -p
mkdir $ROOT/root/etc/smartdns/ip-set/ -p
mkdir $ROOT/root/etc/smartdns/conf.d/ -p
cp $SMARTDNS_CONF $ROOT/root/etc/smartdns/

View File

@@ -121,8 +121,7 @@ restart_dnsmasq()
PID2="$(echo "$CMD" | awk 'NR==2{print $1}')"
PID2_PPID="$(grep 'PPid:' /proc/$PID2/status | awk '{print $2}' 2>/dev/null)"
if [ "$PID2_PPID" != "$PID1" ]; then
echo "find multiple dnsmasq, but not started by the same process"
return 1
kill -9 "$PID2"
fi
PID=$PID1
else
@@ -341,9 +340,12 @@ case "$1" in
exit 1
fi
SMARTDNS_OPTION=""
[ "$SMARTDNS_CRASH_RESTART" = "1" ] && SMARTDNS_OPTION="$SMARTDNS_OPTION -R"
set_smartdns_port
get_tz
$SMARTDNS_BIN -c "$SMARTDNS_CONF" -p $SMARTDNS_PID
$SMARTDNS_BIN -c "$SMARTDNS_CONF" -p $SMARTDNS_PID $SMARTDNS_OPTION
if [ $? -ne 0 ]; then
clear_rule
exit 1

View File

@@ -5,4 +5,7 @@
SMARTDNS_WORKMODE="1"
# smartdns port
SMARTDNS_PORT="535"
SMARTDNS_PORT="535"
# restart when crash
SMARTDNS_CRASH_RESTART="1"

View File

@@ -15,8 +15,9 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
BIN=smartdns
OBJS_LIB=lib/rbtree.o lib/art.o lib/bitops.o lib/radix.o lib/conf.o lib/nftset.o
OBJS=smartdns.o fast_ping.o dns_client.o dns_server.o dns.o util.o tlog.o dns_conf.o dns_cache.o http_parse.o proxy.o $(OBJS_LIB)
OBJS_LIB=lib/rbtree.o lib/art.o lib/bitops.o lib/radix.o lib/timer_wheel.o lib/idna.o lib/conf.o lib/nftset.o
OBJS_MAIN=smartdns.o fast_ping.o dns_client.o dns_server.o dns.o util.o tlog.o dns_conf.o dns_cache.o http_parse.o proxy.o timer.o
OBJS=$(OBJS_MAIN) $(OBJS_LIB)
# cflags
ifndef CFLAGS
@@ -27,7 +28,7 @@ ifndef CFLAGS
endif
CFLAGS +=-Wall -Wstrict-prototypes -fno-omit-frame-pointer -Wstrict-aliasing -funwind-tables -Wmissing-prototypes -Wshadow -Wextra -Wno-unused-parameter -Wno-implicit-fallthrough
endif
override CFLAGS +=-Iinclude
override CFLAGS +=-Iinclude -I/home/rock/code/build/openssl/openssl-3.2.0/include
override CFLAGS += -DBASE_FILE_NAME='"$(notdir $<)"'
override CFLAGS += $(EXTRA_CFLAGS)
ifdef VER
@@ -39,7 +40,7 @@ override CXXFLAGS +=-Iinclude
# ldflags
ifeq ($(STATIC), yes)
override LDFLAGS += -lssl -lcrypto -Wl,--whole-archive -lpthread -Wl,--no-whole-archive -ldl -static
override LDFLAGS += -lssl -lcrypto -Wl,--whole-archive -lpthread -Wl,--no-whole-archive -ldl -static -L/home/rock/code/build/openssl/openssl-3.2.0
else
override LDFLAGS += -lssl -lcrypto -lpthread -ldl
endif
@@ -51,5 +52,8 @@ all: $(BIN)
$(BIN) : $(OBJS)
$(CC) $(OBJS) -o $@ $(LDFLAGS)
clang-tidy:
clang-tidy -p=. $(OBJS_MAIN:.o=.c) -- $(CFLAGS)
clean:
$(RM) $(OBJS) $(BIN)

219
src/dns.c
View File

@@ -738,14 +738,14 @@ static int _dns_add_opt_RAW(struct dns_packet *packet, dns_opt_code_t opt_rrtype
struct dns_opt *opt = (struct dns_opt *)opt_data;
int len = 0;
opt->code = DNS_OPT_T_TCP_KEEPALIVE;
opt->code = opt_rrtype;
opt->length = sizeof(unsigned short);
memcpy(opt->data, raw, raw_len);
len += raw_len;
len += sizeof(*opt);
return _dns_add_RAW(packet, DNS_RRS_OPT, (dns_type_t)DNS_OPT_T_TCP_KEEPALIVE, "", 0, opt_data, len);
return _dns_add_RAW(packet, DNS_RRS_OPT, (dns_type_t)opt_rrtype, "", 0, opt_data, len);
}
static int _dns_get_opt_RAW(struct dns_rrs *rrs, char *domain, int maxsize, int *ttl, struct dns_opt *dns_opt,
@@ -759,8 +759,6 @@ static int _dns_get_opt_RAW(struct dns_rrs *rrs, char *domain, int maxsize, int
static int __attribute__((unused)) _dns_add_OPT(struct dns_packet *packet, dns_rr_type type, unsigned short opt_code,
unsigned short opt_len, struct dns_opt *opt)
{
// TODO
int ret = 0;
int len = 0;
struct dns_context context;
@@ -806,8 +804,6 @@ static int __attribute__((unused)) _dns_add_OPT(struct dns_packet *packet, dns_r
static int __attribute__((unused)) _dns_get_OPT(struct dns_rrs *rrs, unsigned short *opt_code, unsigned short *opt_len,
struct dns_opt *opt, int *opt_maxlen)
{
// TODO
int qtype = 0;
int qclass = 0;
int rr_len = 0;
@@ -875,6 +871,27 @@ int dns_get_PTR(struct dns_rrs *rrs, char *domain, int maxsize, int *ttl, char *
return _dns_get_RAW(rrs, domain, maxsize, ttl, cname, &len);
}
int dns_add_TXT(struct dns_packet *packet, dns_rr_type type, const char *domain, int ttl, const char *text)
{
int rr_len = strnlen(text, DNS_MAX_CNAME_LEN);
char data[DNS_MAX_CNAME_LEN];
if (rr_len > DNS_MAX_CNAME_LEN - 2) {
return -1;
}
data[0] = rr_len;
rr_len++;
memcpy(data + 1, text, rr_len);
data[rr_len] = 0;
return _dns_add_RAW(packet, type, DNS_T_TXT, domain, ttl, data, rr_len);
}
int dns_get_TXT(struct dns_rrs *rrs, char *domain, int maxsize, int *ttl, char *text, int txt_size)
{
return -1;
}
int dns_add_NS(struct dns_packet *packet, dns_rr_type type, const char *domain, int ttl, const char *cname)
{
int rr_len = strnlen(cname, DNS_MAX_CNAME_LEN) + 1;
@@ -1013,7 +1030,7 @@ int dns_add_OPT_ECS(struct dns_packet *packet, struct dns_opt_ecs *ecs)
return _dns_add_RAW(packet, DNS_RRS_OPT, (dns_type_t)DNS_OPT_T_ECS, "", 0, opt_data, len);
}
int dns_get_OPT_ECS(struct dns_rrs *rrs, unsigned short *opt_code, unsigned short *opt_len, struct dns_opt_ecs *ecs)
int dns_get_OPT_ECS(struct dns_rrs *rrs, struct dns_opt_ecs *ecs)
{
unsigned char opt_data[DNS_MAX_OPT_LEN];
char domain[DNS_MAX_CNAME_LEN] = {0};
@@ -1050,16 +1067,16 @@ int dns_add_OPT_TCP_KEEPALIVE(struct dns_packet *packet, unsigned short timeout)
return _dns_add_opt_RAW(packet, DNS_OPT_T_TCP_KEEPALIVE, &timeout_net, data_len);
}
int dns_get_OPT_TCP_KEEPALIVE(struct dns_rrs *rrs, unsigned short *opt_code, unsigned short *opt_len,
unsigned short *timeout)
int dns_get_OPT_TCP_KEEPALIVE(struct dns_rrs *rrs, unsigned short *timeout)
{
unsigned char opt_data[DNS_MAX_OPT_LEN];
char domain[DNS_MAX_CNAME_LEN] = {0};
struct dns_opt *opt = (struct dns_opt *)opt_data;
int len = DNS_MAX_OPT_LEN;
int ttl = 0;
unsigned char *data = NULL;
if (_dns_get_opt_RAW(rrs, NULL, 0, &ttl, opt, &len) != 0) {
if (_dns_get_opt_RAW(rrs, domain, DNS_MAX_CNAME_LEN, &ttl, opt, &len) != 0) {
return -1;
}
@@ -1087,6 +1104,60 @@ int dns_get_OPT_TCP_KEEPALIVE(struct dns_rrs *rrs, unsigned short *opt_code, uns
return 0;
}
int dns_add_SRV(struct dns_packet *packet, dns_rr_type type, const char *domain, int ttl, int priority, int weight,
int port, const char *target)
{
unsigned char data[DNS_MAX_CNAME_LEN];
unsigned char *data_ptr = data;
int target_len = 0;
if (target == NULL) {
target = "";
}
target_len = strnlen(target, DNS_MAX_CNAME_LEN) + 1;
memcpy(data_ptr, &priority, sizeof(unsigned short));
data_ptr += sizeof(unsigned short);
memcpy(data_ptr, &weight, sizeof(unsigned short));
data_ptr += sizeof(unsigned short);
memcpy(data_ptr, &port, sizeof(unsigned short));
data_ptr += sizeof(unsigned short);
if (data_ptr - data + target_len >= DNS_MAX_CNAME_LEN) {
return -1;
}
safe_strncpy((char *)data_ptr, target, target_len);
data_ptr += target_len;
return _dns_add_RAW(packet, type, DNS_T_SRV, domain, ttl, data, data_ptr - data);
}
int dns_get_SRV(struct dns_rrs *rrs, char *domain, int maxsize, int *ttl, unsigned short *priority,
unsigned short *weight, unsigned short *port, char *target, int target_size)
{
unsigned char data[DNS_MAX_CNAME_LEN];
unsigned char *ptr = data;
int len = sizeof(data);
if (_dns_get_RAW(rrs, domain, maxsize, ttl, data, &len) != 0) {
return -1;
}
if (len < 6) {
return -1;
}
memcpy(priority, ptr, sizeof(unsigned short));
ptr += sizeof(unsigned short);
memcpy(weight, ptr, sizeof(unsigned short));
ptr += sizeof(unsigned short);
memcpy(port, ptr, sizeof(unsigned short));
ptr += sizeof(unsigned short);
safe_strncpy(target, (char *)ptr, target_size);
return 0;
}
int dns_add_HTTPS_start(struct dns_rr_nested *svcparam_buffer, struct dns_packet *packet, dns_rr_type type,
const char *domain, int ttl, int priority, const char *target)
{
@@ -1596,6 +1667,26 @@ static int _dns_encode_CNAME(struct dns_context *context, struct dns_rrs *rrs)
return 0;
}
static int _dns_decode_SRV(struct dns_context *context, unsigned short *priority, unsigned short *weight,
unsigned short *port, char *target, int target_size)
{
int ret = 0;
if (_dns_left_len(context) < 6) {
return -1;
}
*priority = _dns_read_short(&context->ptr);
*weight = _dns_read_short(&context->ptr);
*port = _dns_read_short(&context->ptr);
ret = _dns_decode_domain(context, target, target_size);
if (ret < 0) {
return -1;
}
return 0;
}
static int _dns_decode_SOA(struct dns_context *context, struct dns_soa *soa)
{
int ret = 0;
@@ -1685,10 +1776,56 @@ static int _dns_encode_SOA(struct dns_context *context, struct dns_rrs *rrs)
return 0;
}
static int _dns_encode_SRV(struct dns_context *context, struct dns_rrs *rrs)
{
int ret = 0;
int qtype = 0;
int qclass = 0;
int ttl = 0;
char domain[DNS_MAX_CNAME_LEN];
int rr_len = 0;
unsigned char *rr_len_ptr = NULL;
struct dns_context data_context;
_dns_init_context_by_rrs(rrs, &data_context);
ret = _dns_get_rr_head(&data_context, domain, DNS_MAX_CNAME_LEN, &qtype, &qclass, &ttl, &rr_len);
if (ret < 0) {
return -1;
}
ret = _dns_encode_rr_head(context, domain, qtype, qclass, ttl, rr_len, &rr_len_ptr);
if (ret < 0) {
return -1;
}
rr_len = 0;
if (_dns_left_len(context) < 6) {
return -1;
}
_dns_write_short(&context->ptr, *(unsigned short *)data_context.ptr);
data_context.ptr += 2;
_dns_write_short(&context->ptr, *(unsigned short *)data_context.ptr);
data_context.ptr += 2;
_dns_write_short(&context->ptr, *(unsigned short *)data_context.ptr);
data_context.ptr += 2;
rr_len += 6;
ret = _dns_encode_domain(context, (char *)data_context.ptr);
if (ret < 0) {
return -1;
}
rr_len += ret;
data_context.ptr += strnlen((char *)(data_context.ptr), DNS_MAX_CNAME_LEN) + 1;
_dns_write_short(&rr_len_ptr, rr_len);
return 0;
}
static int _dns_decode_opt_ecs(struct dns_context *context, struct dns_opt_ecs *ecs, int opt_len)
{
// TODO
int len = 0;
if (opt_len < 4) {
return -1;
@@ -1716,7 +1853,6 @@ static int _dns_decode_opt_ecs(struct dns_context *context, struct dns_opt_ecs *
static int _dns_decode_opt_cookie(struct dns_context *context, struct dns_opt_cookie *cookie, int opt_len)
{
// TODO
if (opt_len < (int)member_size(struct dns_opt_cookie, client_cookie)) {
return -1;
}
@@ -1743,6 +1879,23 @@ static int _dns_decode_opt_cookie(struct dns_context *context, struct dns_opt_co
return 0;
}
static int _dns_decode_opt_tcp_keepalive(struct dns_context *context, unsigned short *timeout, int opt_len)
{
if (opt_len == 0) {
*timeout = 0;
return 0;
}
if (opt_len < (int)sizeof(unsigned short)) {
return -1;
}
*timeout = _dns_read_short(&context->ptr);
tlog(TLOG_DEBUG, "OPT TCP KEEPALIVE %u", *timeout);
return 0;
}
static int _dns_encode_OPT(struct dns_context *context, struct dns_rrs *rrs)
{
int ret = 0;
@@ -1861,7 +2014,7 @@ static int _dns_decode_opt(struct dns_context *context, dns_rr_type type, unsign
unsigned short opt_code = 0;
unsigned short opt_len = 0;
unsigned short errcode = (ttl >> 16) & 0xFFFF;
unsigned short ever = (ttl)&0xFFFF;
unsigned short ever = (ttl) & 0xFFFF;
unsigned char *start = context->ptr;
struct dns_packet *packet = context->packet;
int ret = 0;
@@ -1946,6 +2099,20 @@ static int _dns_decode_opt(struct dns_context *context, dns_rr_type type, unsign
return -1;
}
} break;
case DNS_OPT_T_TCP_KEEPALIVE: {
unsigned short timeout = 0;
ret = _dns_decode_opt_tcp_keepalive(context, &timeout, opt_len);
if (ret != 0) {
tlog(TLOG_ERROR, "decode tcp keepalive failed.");
return -1;
}
ret = dns_add_OPT_TCP_KEEPALIVE(packet, timeout);
if (ret != 0) {
tlog(TLOG_ERROR, "add tcp keepalive failed.");
return -1;
}
} break;
case DNS_OPT_T_PADDING:
context->ptr += opt_len;
break;
@@ -2232,6 +2399,24 @@ static int _dns_decode_an(struct dns_context *context, dns_rr_type type)
return -1;
}
} break;
case DNS_T_SRV: {
unsigned short priority = 0;
unsigned short weight = 0;
unsigned short port = 0;
char target[DNS_MAX_CNAME_LEN];
ret = _dns_decode_SRV(context, &priority, &weight, &port, target, DNS_MAX_CNAME_LEN);
if (ret < 0) {
tlog(TLOG_DEBUG, "decode SRV failed, %s", domain);
return -1;
}
ret = dns_add_SRV(packet, type, domain, ttl, priority, weight, port, target);
if (ret < 0) {
tlog(TLOG_DEBUG, "add SRV failed, %s", domain);
return -1;
}
} break;
case DNS_T_OPT: {
unsigned char *opt_start = context->ptr;
ret = _dns_decode_opt(context, type, ttl, rr_len);
@@ -2348,6 +2533,12 @@ static int _dns_encode_an(struct dns_context *context, struct dns_rrs *rrs)
return -1;
}
break;
case DNS_T_SRV:
ret = _dns_encode_SRV(context, rrs);
if (ret < 0) {
return -1;
}
break;
case DNS_T_HTTPS:
ret = _dns_encode_HTTPS(context, rrs);
if (ret < 0) {

View File

@@ -72,6 +72,7 @@ typedef enum dns_type {
DNS_T_SRV = 33,
DNS_T_OPT = 41,
DNS_T_SSHFP = 44,
DNS_T_SVCB = 64,
DNS_T_HTTPS = 65,
DNS_T_SPF = 99,
DNS_T_AXFR = 252,
@@ -262,6 +263,9 @@ int dns_get_A(struct dns_rrs *rrs, char *domain, int maxsize, int *ttl, unsigned
int dns_add_PTR(struct dns_packet *packet, dns_rr_type type, const char *domain, int ttl, const char *cname);
int dns_get_PTR(struct dns_rrs *rrs, char *domain, int maxsize, int *ttl, char *cname, int cname_size);
int dns_add_TXT(struct dns_packet *packet, dns_rr_type type, const char *domain, int ttl, const char *text);
int dns_get_TXT(struct dns_rrs *rrs, char *domain, int maxsize, int *ttl, char *text, int txt_size);
int dns_add_AAAA(struct dns_packet *packet, dns_rr_type type, const char *domain, int ttl,
unsigned char addr[DNS_RR_AAAA_LEN]);
int dns_get_AAAA(struct dns_rrs *rrs, char *domain, int maxsize, int *ttl, unsigned char addr[DNS_RR_AAAA_LEN]);
@@ -276,11 +280,15 @@ int dns_set_OPT_payload_size(struct dns_packet *packet, int payload_size);
int dns_get_OPT_payload_size(struct dns_packet *packet);
int dns_add_OPT_ECS(struct dns_packet *packet, struct dns_opt_ecs *ecs);
int dns_get_OPT_ECS(struct dns_rrs *rrs, unsigned short *opt_code, unsigned short *opt_len, struct dns_opt_ecs *ecs);
int dns_get_OPT_ECS(struct dns_rrs *rrs, struct dns_opt_ecs *ecs);
int dns_add_OPT_TCP_KEEPALIVE(struct dns_packet *packet, unsigned short timeout);
int dns_get_OPT_TCP_KEEPALIVE(struct dns_rrs *rrs, unsigned short *opt_code, unsigned short *opt_len,
unsigned short *timeout);
int dns_get_OPT_TCP_KEEPALIVE(struct dns_rrs *rrs, unsigned short *timeout);
int dns_add_SRV(struct dns_packet *packet, dns_rr_type type, const char *domain, int ttl, int priority, int weight,
int port, const char *target);
int dns_get_SRV(struct dns_rrs *rrs, char *domain, int maxsize, int *ttl, unsigned short *priority,
unsigned short *weight, unsigned short *port, char *target, int target_size);
/* the key must be added in orders, or dig will report FORMERR */
int dns_add_HTTPS_start(struct dns_rr_nested *svcparam_buffer, struct dns_packet *packet, dns_rr_type type,
@@ -302,7 +310,7 @@ int dns_add_HTTPS_end(struct dns_rr_nested *svcparam);
int dns_get_HTTPS_svcparm_start(struct dns_rrs *rrs, struct dns_https_param **https_param, char *domain, int maxsize,
int *ttl, int *priority, char *target, int target_size);
struct dns_https_param *dns_get_HTTPS_svcparm_next(struct dns_rrs *rrs, struct dns_https_param *parm);
struct dns_https_param *dns_get_HTTPS_svcparm_next(struct dns_rrs *rrs, struct dns_https_param *param);
/*
* Packet operation

View File

@@ -18,66 +18,65 @@
#include "dns_cache.h"
#include "stringutil.h"
#include "timer.h"
#include "tlog.h"
#include "util.h"
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#define DNS_CACHE_MAX_HITNUM 5000
#define DNS_CACHE_HITNUM_STEP 2
#define DNS_CACHE_MAX_HITNUM 6000
#define DNS_CACHE_HITNUM_STEP 3
#define DNS_CACHE_HITNUM_STEP_MAX 6
#define DNS_CACHE_READ_TIMEOUT 60
#define DNS_CACHE_FAIL_TIMEOUT (60 * 5)
#define EXPIRED_DOMAIN_PREFETCH_TIME (3600 * 8)
struct dns_cache_head {
DECLARE_HASHTABLE(cache_hash, 16);
struct hash_table cache_hash;
struct list_head cache_list;
struct list_head inactive_list;
atomic_t num;
int size;
int enable_inactive;
int inactive_list_expired;
pthread_mutex_t lock;
dns_cache_callback timeout_callback;
};
typedef int (*dns_cache_read_callback)(struct dns_cache_record *cache_record, struct dns_cache_data *cache_data);
static int is_cache_init;
static struct dns_cache_head dns_cache_head;
int dns_cache_init(int size, int enable_inactive, int inactive_list_expired)
int dns_cache_init(int size, dns_cache_callback timeout_callback)
{
int bits = 0;
if (is_cache_init == 1) {
return -1;
}
INIT_LIST_HEAD(&dns_cache_head.cache_list);
INIT_LIST_HEAD(&dns_cache_head.inactive_list);
hash_init(dns_cache_head.cache_hash);
bits = ilog2(size) - 1;
if (bits >= 20) {
bits = 20;
} else if (bits < 12) {
bits = 12;
}
hash_table_init(dns_cache_head.cache_hash, bits, malloc);
atomic_set(&dns_cache_head.num, 0);
dns_cache_head.size = size;
dns_cache_head.enable_inactive = enable_inactive;
dns_cache_head.inactive_list_expired = inactive_list_expired;
dns_cache_head.timeout_callback = timeout_callback;
pthread_mutex_init(&dns_cache_head.lock, NULL);
is_cache_init = 1;
return 0;
}
static __attribute__((unused)) struct dns_cache *_dns_cache_last(void)
static struct dns_cache *_dns_cache_first(void)
{
struct dns_cache *dns_cache = NULL;
dns_cache = list_last_entry(&dns_cache_head.inactive_list, struct dns_cache, list);
if (dns_cache) {
return dns_cache;
}
return list_last_entry(&dns_cache_head.cache_list, struct dns_cache, list);
}
static struct dns_cache *_dns_inactive_cache_first(void)
{
struct dns_cache *dns_cache = NULL;
dns_cache = list_first_entry_or_null(&dns_cache_head.inactive_list, struct dns_cache, list);
if (dns_cache) {
return dns_cache;
}
return list_first_entry_or_null(&dns_cache_head.cache_list, struct dns_cache, list);
}
@@ -86,7 +85,8 @@ 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);
dns_cache_data_put(dns_cache->cache_data);
dns_cache->cache_data = NULL;
free(dns_cache);
}
@@ -103,6 +103,7 @@ void dns_cache_release(struct dns_cache *dns_cache)
if (dns_cache == NULL) {
return;
}
if (!atomic_dec_and_test(&dns_cache->ref)) {
return;
}
@@ -114,20 +115,10 @@ static void _dns_cache_remove(struct dns_cache *dns_cache)
{
hash_del(&dns_cache->node);
list_del_init(&dns_cache->list);
dns_timer_del(&dns_cache->timer);
dns_cache_release(dns_cache);
}
static void _dns_cache_move_inactive(struct dns_cache *dns_cache)
{
list_del_init(&dns_cache->list);
list_add_tail(&dns_cache->list, &dns_cache_head.inactive_list);
}
enum CACHE_TYPE dns_cache_data_type(struct dns_cache_data *cache_data)
{
return cache_data->head.cache_type;
}
uint32_t dns_cache_get_query_flag(struct dns_cache *dns_cache)
{
return dns_cache->info.query_flag;
@@ -138,91 +129,6 @@ const char *dns_cache_get_dns_group_name(struct dns_cache *dns_cache)
return dns_cache->info.dns_group_name;
}
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(void)
{
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;
}
cache_addr->head.cache_type = CACHE_TYPE_NONE;
cache_addr->head.size = sizeof(struct dns_cache_addr) - sizeof(struct dns_cache_data_head);
cache_addr->head.magic = MAGIC_CACHE_DATA;
return (struct dns_cache_data *)cache_addr;
}
void dns_cache_set_data_soa(struct dns_cache_data *dns_cache, char *cname, int cname_ttl)
{
if (dns_cache == NULL) {
goto errout;
}
dns_cache->head.is_soa = 1;
if (dns_cache->head.cache_type == CACHE_TYPE_PACKET) {
return;
}
struct dns_cache_addr *cache_addr = (struct dns_cache_addr *)dns_cache;
if (cache_addr == NULL) {
goto errout;
}
memset(cache_addr->addr_data.addr, 0, sizeof(cache_addr->addr_data.addr));
if (cname) {
safe_strncpy(cache_addr->addr_data.cname, cname, DNS_MAX_CNAME_LEN);
cache_addr->addr_data.cname_ttl = cname_ttl;
}
cache_addr->addr_data.soa = 1;
cache_addr->head.cache_type = CACHE_TYPE_ADDR;
cache_addr->head.size = sizeof(struct dns_cache_addr) - sizeof(struct dns_cache_data_head);
errout:
return;
}
void dns_cache_set_data_addr(struct dns_cache_data *dns_cache, char *cname, int cname_ttl, unsigned char *addr,
int addr_len)
{
if (dns_cache == NULL) {
goto errout;
}
struct dns_cache_addr *cache_addr = (struct dns_cache_addr *)dns_cache;
if (cache_addr == NULL) {
goto errout;
}
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_type = CACHE_TYPE_ADDR;
cache_addr->head.size = sizeof(struct dns_cache_addr) - sizeof(struct dns_cache_data_head);
errout:
return;
}
struct dns_cache_data *dns_cache_new_data_packet(void *packet, size_t packet_len)
{
struct dns_cache_packet *cache_packet = NULL;
@@ -240,15 +146,50 @@ struct dns_cache_data *dns_cache_new_data_packet(void *packet, size_t packet_len
memcpy(cache_packet->data, packet, packet_len);
memset(&cache_packet->head, 0, sizeof(cache_packet->head));
cache_packet->head.cache_type = CACHE_TYPE_PACKET;
cache_packet->head.size = packet_len;
cache_packet->head.magic = MAGIC_CACHE_DATA;
atomic_set(&cache_packet->head.ref, 1);
return (struct dns_cache_data *)cache_packet;
}
static int _dns_cache_replace(struct dns_cache_key *cache_key, int ttl, int speed, int no_inactive, int inactive,
struct dns_cache_data *cache_data)
static void dns_cache_timer_release(struct tw_base *base, struct tw_timer_list *timer, void *data)
{
struct dns_cache *dns_cache = data;
dns_cache_release(dns_cache);
}
static void dns_cache_expired(struct tw_base *base, struct tw_timer_list *timer, void *data, unsigned long timestamp)
{
struct dns_cache *dns_cache = data;
if (dns_cache->del_pending == 1) {
dns_cache_release(dns_cache);
return;
}
if (dns_cache_head.timeout_callback) {
dns_cache_tmout_action_t tmout_act = dns_cache_head.timeout_callback(dns_cache);
switch (tmout_act) {
case DNS_CACHE_TMOUT_ACTION_OK:
break;
case DNS_CACHE_TMOUT_ACTION_DEL:
dns_cache_release(dns_cache);
return;
case DNS_CACHE_TMOUT_ACTION_RETRY:
dns_timer_mod(&dns_cache->timer, DNS_CACHE_FAIL_TIMEOUT);
return;
default:
break;
}
}
dns_cache->del_pending = 1;
dns_timer_mod(&dns_cache->timer, 5);
}
static int _dns_cache_replace(struct dns_cache_key *cache_key, int rcode, int ttl, int speed, int timeout,
int update_time, struct dns_cache_data *cache_data)
{
struct dns_cache *dns_cache = NULL;
struct dns_cache_data *old_cache_data = NULL;
@@ -260,7 +201,7 @@ static int _dns_cache_replace(struct dns_cache_key *cache_key, int ttl, int spee
/* lookup existing cache */
dns_cache = dns_cache_lookup(cache_key);
if (dns_cache == NULL) {
return dns_cache_insert(cache_key, ttl, speed, no_inactive, cache_data);
return -1;
}
if (ttl < DNS_CACHE_TTL_MIN) {
@@ -270,43 +211,37 @@ static int _dns_cache_replace(struct dns_cache_key *cache_key, int ttl, int spee
/* update cache data */
pthread_mutex_lock(&dns_cache_head.lock);
dns_cache->del_pending = 0;
dns_cache->info.ttl = ttl;
dns_cache->info.rcode = rcode;
dns_cache->info.qtype = cache_key->qtype;
dns_cache->info.query_flag = cache_key->query_flag;
dns_cache->info.ttl = ttl;
dns_cache->info.speed = speed;
dns_cache->info.no_inactive = no_inactive;
dns_cache->info.timeout = timeout;
dns_cache->info.is_visited = 1;
old_cache_data = dns_cache->cache_data;
dns_cache->cache_data = cache_data;
list_del_init(&dns_cache->list);
if (inactive == 0) {
time(&dns_cache->info.insert_time);
time(&dns_cache->info.replace_time);
list_add_tail(&dns_cache->list, &dns_cache_head.cache_list);
} else {
time(&dns_cache->info.replace_time);
list_add_tail(&dns_cache->list, &dns_cache_head.inactive_list);
if (cache_data) {
old_cache_data = dns_cache->cache_data;
dns_cache->cache_data = cache_data;
}
if (update_time) {
time(&dns_cache->info.insert_time);
}
time(&dns_cache->info.replace_time);
list_del(&dns_cache->list);
list_add_tail(&dns_cache->list, &dns_cache_head.cache_list);
dns_timer_mod(&dns_cache->timer, timeout);
pthread_mutex_unlock(&dns_cache_head.lock);
dns_cache_data_free(old_cache_data);
if (old_cache_data) {
dns_cache_data_put(old_cache_data);
}
dns_cache_release(dns_cache);
return 0;
}
int dns_cache_replace(struct dns_cache_key *cache_key, int ttl, int speed, int no_inactive,
int dns_cache_replace(struct dns_cache_key *cache_key, int rcode, int ttl, int speed, int timeout, int update_time,
struct dns_cache_data *cache_data)
{
return _dns_cache_replace(cache_key, ttl, speed, no_inactive, 0, cache_data);
}
int dns_cache_replace_inactive(struct dns_cache_key *cache_key, int ttl, int speed, int no_inactive,
struct dns_cache_data *cache_data)
{
return _dns_cache_replace(cache_key, ttl, speed, no_inactive, 1, cache_data);
return _dns_cache_replace(cache_key, rcode, ttl, speed, timeout, update_time, cache_data);
}
static void _dns_cache_remove_by_domain(struct dns_cache_key *cache_key)
@@ -320,7 +255,7 @@ static void _dns_cache_remove_by_domain(struct dns_cache_key *cache_key)
key = jhash(&cache_key->query_flag, sizeof(cache_key->query_flag), key);
pthread_mutex_lock(&dns_cache_head.lock);
hash_for_each_possible(dns_cache_head.cache_hash, dns_cache, node, key)
hash_table_for_each_possible(dns_cache_head.cache_hash, dns_cache, node, key)
{
if (dns_cache->info.qtype != cache_key->qtype) {
continue;
@@ -345,7 +280,8 @@ static void _dns_cache_remove_by_domain(struct dns_cache_key *cache_key)
pthread_mutex_unlock(&dns_cache_head.lock);
}
static int _dns_cache_insert(struct dns_cache_info *info, struct dns_cache_data *cache_data, struct list_head *head)
static int _dns_cache_insert(struct dns_cache_info *info, struct dns_cache_data *cache_data, struct list_head *head,
int timeout)
{
uint32_t key = 0;
struct dns_cache *dns_cache = NULL;
@@ -372,31 +308,38 @@ static int _dns_cache_insert(struct dns_cache_info *info, struct dns_cache_data
memcpy(&dns_cache->info, info, sizeof(*info));
dns_cache->del_pending = 0;
dns_cache->cache_data = cache_data;
dns_cache->timer.function = dns_cache_expired;
dns_cache->timer.del_function = dns_cache_timer_release;
dns_cache->timer.expires = timeout;
dns_cache->timer.data = dns_cache;
pthread_mutex_lock(&dns_cache_head.lock);
hash_add(dns_cache_head.cache_hash, &dns_cache->node, key);
hash_table_add(dns_cache_head.cache_hash, &dns_cache->node, key);
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 = NULL;
del_cache = _dns_inactive_cache_first();
del_cache = _dns_cache_first();
if (del_cache) {
_dns_cache_remove(del_cache);
}
}
dns_cache_get(dns_cache);
dns_timer_add(&dns_cache->timer);
pthread_mutex_unlock(&dns_cache_head.lock);
return 0;
errout:
if (dns_cache) {
free(dns_cache);
dns_cache_release(dns_cache);
}
return -1;
}
int dns_cache_insert(struct dns_cache_key *cache_key, int ttl, int speed, int no_inactive,
int dns_cache_insert(struct dns_cache_key *cache_key, int rcode, int ttl, int speed, int timeout,
struct dns_cache_data *cache_data)
{
struct dns_cache_info info;
@@ -406,7 +349,7 @@ int dns_cache_insert(struct dns_cache_key *cache_key, int ttl, int speed, int no
}
if (dns_cache_head.size <= 0) {
dns_cache_data_free(cache_data);
dns_cache_data_put(cache_data);
return 0;
}
@@ -423,12 +366,30 @@ int dns_cache_insert(struct dns_cache_key *cache_key, int ttl, int speed, int no
info.ttl = ttl;
info.hitnum_update_add = DNS_CACHE_HITNUM_STEP;
info.speed = speed;
info.no_inactive = no_inactive;
info.timeout = timeout;
info.is_visited = 1;
info.rcode = rcode;
time(&info.insert_time);
time(&info.replace_time);
return _dns_cache_insert(&info, cache_data, &dns_cache_head.cache_list);
return _dns_cache_insert(&info, cache_data, &dns_cache_head.cache_list, timeout);
}
int dns_cache_update_timer(struct dns_cache_key *key, int timeout)
{
struct dns_cache *dns_cache = dns_cache_lookup(key);
if (dns_cache == NULL) {
return -1;
}
pthread_mutex_lock(&dns_cache_head.lock);
dns_timer_mod(&dns_cache->timer, timeout);
dns_cache->del_pending = 0;
pthread_mutex_unlock(&dns_cache_head.lock);
dns_cache_release(dns_cache);
return 0;
}
struct dns_cache *dns_cache_lookup(struct dns_cache_key *cache_key)
@@ -450,7 +411,7 @@ struct dns_cache *dns_cache_lookup(struct dns_cache_key *cache_key)
time(&now);
/* find cache */
pthread_mutex_lock(&dns_cache_head.lock);
hash_for_each_possible(dns_cache_head.cache_hash, dns_cache, node, key)
hash_table_for_each_possible(dns_cache_head.cache_hash, dns_cache, node, key)
{
if (dns_cache->info.qtype != cache_key->qtype) {
continue;
@@ -473,13 +434,7 @@ struct dns_cache *dns_cache_lookup(struct dns_cache_key *cache_key)
}
if (dns_cache_ret) {
/* Return NULL if the cache times out */
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 {
dns_cache_get(dns_cache_ret);
}
dns_cache_get(dns_cache_ret);
}
pthread_mutex_unlock(&dns_cache_head.lock);
@@ -501,51 +456,37 @@ int dns_cache_get_ttl(struct dns_cache *dns_cache)
return ttl;
}
int dns_cache_get_cname_ttl(struct dns_cache *dns_cache)
{
time_t now = 0;
int ttl = 0;
time(&now);
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) {
return 0;
}
ttl = dns_cache->info.insert_time + cache_addr->addr_data.cname_ttl - now;
if (ttl < 0) {
return 0;
}
int addr_ttl = dns_cache_get_ttl(dns_cache);
if (ttl < addr_ttl && ttl < 0) {
return addr_ttl;
}
if (ttl < 0) {
return 0;
}
return ttl;
}
int dns_cache_is_soa(struct dns_cache *dns_cache)
{
if (dns_cache == NULL) {
return 0;
}
if (dns_cache->cache_data->head.is_soa) {
return 1;
}
return 0;
}
struct dns_cache_data *dns_cache_get_data(struct dns_cache *dns_cache)
{
return dns_cache->cache_data;
struct dns_cache_data *cache_data;
pthread_mutex_lock(&dns_cache_head.lock);
dns_cache_data_get(dns_cache->cache_data);
cache_data = dns_cache->cache_data;
pthread_mutex_unlock(&dns_cache_head.lock);
return cache_data;
}
void dns_cache_data_get(struct dns_cache_data *cache_data)
{
if (atomic_inc_return(&cache_data->head.ref) == 1) {
tlog(TLOG_ERROR, "BUG: dns_cache data is invalid.");
return;
}
return;
}
void dns_cache_data_put(struct dns_cache_data *cache_data)
{
if (cache_data == NULL) {
return;
}
if (!atomic_dec_and_test(&cache_data->head.ref)) {
return;
}
free(cache_data);
}
int dns_cache_is_visited(struct dns_cache *dns_cache)
@@ -553,6 +494,11 @@ int dns_cache_is_visited(struct dns_cache *dns_cache)
return dns_cache->info.is_visited;
}
int dns_cache_total_num(void)
{
return atomic_read(&dns_cache_head.num);
}
void dns_cache_delete(struct dns_cache *dns_cache)
{
pthread_mutex_lock(&dns_cache_head.lock);
@@ -591,127 +537,58 @@ void dns_cache_update(struct dns_cache *dns_cache)
pthread_mutex_unlock(&dns_cache_head.lock);
}
static void _dns_cache_remove_expired_ttl(dns_cache_callback inactive_precallback, int ttl_inactive_pre,
unsigned int max_callback_num, const time_t *now)
static int _dns_cache_read_to_cache(struct dns_cache_record *cache_record, struct dns_cache_data *cache_data)
{
struct dns_cache *dns_cache = NULL;
struct dns_cache *tmp = NULL;
unsigned int callback_num = 0;
int ttl = 0;
LIST_HEAD(checklist);
struct list_head *head = NULL;
head = &dns_cache_head.cache_list;
struct dns_cache_info *info = &cache_record->info;
int expired_time = 0;
pthread_mutex_lock(&dns_cache_head.lock);
list_for_each_entry_safe(dns_cache, tmp, &dns_cache_head.inactive_list, list)
{
ttl = dns_cache->info.insert_time + dns_cache->info.ttl - *now;
if (ttl > 0) {
continue;
}
if (dns_cache_head.inactive_list_expired + ttl < 0) {
_dns_cache_remove(dns_cache);
continue;
}
ttl = *now - dns_cache->info.replace_time;
if (ttl < ttl_inactive_pre || inactive_precallback == NULL) {
continue;
}
if (callback_num >= max_callback_num) {
continue;
}
if (dns_cache->del_pending == 1) {
continue;
}
/* If the TTL time is in the pre-timeout range, call callback function */
dns_cache_get(dns_cache);
list_add_tail(&dns_cache->check_list, &checklist);
dns_cache->del_pending = 1;
callback_num++;
time_t now = time(NULL);
if (now < info->replace_time) {
info->replace_time = now;
}
pthread_mutex_unlock(&dns_cache_head.lock);
list_for_each_entry_safe(dns_cache, tmp, &checklist, check_list)
{
/* run inactive_precallback */
if (inactive_precallback) {
inactive_precallback(dns_cache);
expired_time = dns_conf_serve_expired_prefetch_time;
if (expired_time == 0) {
expired_time = dns_conf_serve_expired_ttl / 2;
if (expired_time == 0 || expired_time > EXPIRED_DOMAIN_PREFETCH_TIME) {
expired_time = EXPIRED_DOMAIN_PREFETCH_TIME;
}
dns_cache_release(dns_cache);
}
int passed_time = now - info->replace_time;
int timeout = info->timeout - passed_time;
if ((timeout > expired_time + info->ttl) && expired_time >= 0) {
timeout = expired_time + info->ttl;
}
if (timeout < DNS_CACHE_READ_TIMEOUT * 2) {
timeout = DNS_CACHE_READ_TIMEOUT + (rand() % DNS_CACHE_READ_TIMEOUT);
}
if (_dns_cache_insert(&cache_record->info, cache_data, head, timeout) != 0) {
tlog(TLOG_ERROR, "insert cache data failed.");
cache_data = NULL;
goto errout;
}
dns_cache_data_get(cache_data);
daemon_keepalive();
return 0;
errout:
return -1;
}
void dns_cache_invalidate(dns_cache_callback precallback, int ttl_pre, unsigned int max_callback_num,
dns_cache_callback inactive_precallback, int ttl_inactive_pre)
static int _dns_cache_read_record(int fd, uint32_t cache_number, dns_cache_read_callback callback)
{
struct dns_cache *dns_cache = NULL;
struct dns_cache *tmp = NULL;
time_t now = 0;
int ttl = 0;
LIST_HEAD(checklist);
unsigned int callback_num = 0;
if (max_callback_num <= 0) {
max_callback_num = -1;
}
if (dns_cache_head.size <= 0) {
return;
}
time(&now);
pthread_mutex_lock(&dns_cache_head.lock);
list_for_each_entry_safe(dns_cache, tmp, &dns_cache_head.cache_list, list)
{
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 (precallback && dns_cache->del_pending == 0 && callback_num < max_callback_num) {
list_add_tail(&dns_cache->check_list, &checklist);
dns_cache_get(dns_cache);
dns_cache->del_pending = 1;
callback_num++;
continue;
}
}
if (ttl < 0) {
if (dns_cache_head.enable_inactive && dns_cache->info.no_inactive == 0) {
_dns_cache_move_inactive(dns_cache);
} else {
_dns_cache_remove(dns_cache);
}
}
}
pthread_mutex_unlock(&dns_cache_head.lock);
if (dns_cache_head.enable_inactive && dns_cache_head.inactive_list_expired != 0) {
_dns_cache_remove_expired_ttl(inactive_precallback, ttl_inactive_pre, max_callback_num, &now);
}
list_for_each_entry_safe(dns_cache, tmp, &checklist, check_list)
{
/* run callback */
if (precallback) {
precallback(dns_cache);
}
list_del(&dns_cache->check_list);
dns_cache_release(dns_cache);
}
}
static int _dns_cache_read_record(int fd, uint32_t cache_number)
{
unsigned int i = 0;
ssize_t 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));
@@ -725,15 +602,6 @@ static int _dns_cache_read_record(int fd, uint32_t cache_number)
goto errout;
}
if (cache_record.type == CACHE_RECORD_TYPE_ACTIVE) {
head = &dns_cache_head.cache_list;
} else if (cache_record.type == CACHE_RECORD_TYPE_INACTIVE) {
head = &dns_cache_head.inactive_list;
} else {
tlog(TLOG_ERROR, "read cache record type is invalid.");
goto errout;
}
ret = read(fd, &data_head, sizeof(data_head));
if (ret != sizeof(data_head)) {
tlog(TLOG_ERROR, "read data head failed, %s", strerror(errno));
@@ -757,6 +625,7 @@ static int _dns_cache_read_record(int fd, uint32_t cache_number)
}
memcpy(&cache_data->head, &data_head, sizeof(data_head));
atomic_set(&cache_data->head.ref, 1);
ret = read(fd, cache_data->data, data_head.size);
if (ret != data_head.size) {
tlog(TLOG_ERROR, "read cache data failed, %s", strerror(errno));
@@ -767,29 +636,23 @@ static int _dns_cache_read_record(int fd, uint32_t cache_number)
cache_record.info.is_visited = 0;
cache_record.info.domain[DNS_MAX_CNAME_LEN - 1] = '\0';
cache_record.info.dns_group_name[DNS_GROUP_NAME_LEN - 1] = '\0';
if (cache_record.type >= CACHE_RECORD_TYPE_END) {
tlog(TLOG_ERROR, "read cache record type is invalid.");
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;
}
ret = callback(&cache_record, cache_data);
dns_cache_data_put(cache_data);
cache_data = NULL;
if (ret != 0) {
goto errout;
}
}
return 0;
errout:
if (cache_data) {
free(cache_data);
dns_cache_data_put(cache_data);
}
return -1;
}
int dns_cache_load(const char *file)
static int _dns_cache_file_read(const char *file, dns_cache_read_callback callback)
{
int fd = -1;
ssize_t ret = 0;
@@ -822,7 +685,7 @@ int dns_cache_load(const char *file)
}
tlog(TLOG_INFO, "load cache file %s, total %d records", file, cache_file.cache_number);
if (_dns_cache_read_record(fd, cache_file.cache_number) != 0) {
if (_dns_cache_read_record(fd, cache_file.cache_number, callback) != 0) {
goto errout;
}
@@ -836,17 +699,23 @@ errout:
return -1;
}
static int _dns_cache_write_record(int fd, uint32_t *cache_number, enum CACHE_RECORD_TYPE type, struct list_head *head)
int dns_cache_load(const char *file)
{
return _dns_cache_file_read(file, _dns_cache_read_to_cache);
}
static int _dns_cache_write_record(int fd, uint32_t *cache_number, struct list_head *head)
{
struct dns_cache *dns_cache = NULL;
struct dns_cache *tmp = NULL;
struct dns_cache_record cache_record;
memset(&cache_record, 0, sizeof(cache_record));
pthread_mutex_lock(&dns_cache_head.lock);
list_for_each_entry_safe_reverse(dns_cache, tmp, head, list)
list_for_each_entry_safe(dns_cache, tmp, head, list)
{
cache_record.magic = MAGIC_RECORD;
cache_record.type = type;
memcpy(&cache_record.info, &dns_cache->info, sizeof(struct dns_cache_info));
ssize_t ret = write(fd, &cache_record, sizeof(cache_record));
if (ret != sizeof(cache_record)) {
@@ -874,12 +743,7 @@ errout:
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) {
if (_dns_cache_write_record(fd, cache_number, &dns_cache_head.cache_list) != 0) {
return -1;
}
@@ -945,28 +809,47 @@ errout:
return -1;
}
static int _dns_cache_print(struct dns_cache_record *cache_record, struct dns_cache_data *cache_data)
{
printf("domain: %s, qtype: %d, ttl: %d, speed: %.1fms\n", cache_record->info.domain, cache_record->info.qtype,
cache_record->info.ttl, (float)cache_record->info.speed / 10);
return 0;
}
int dns_cache_print(const char *file)
{
if (access(file, F_OK) != 0) {
tlog(TLOG_ERROR, "cache file %s not exist.", file);
return -1;
}
return _dns_cache_file_read(file, _dns_cache_print);
}
void dns_cache_destroy(void)
{
struct dns_cache *dns_cache = NULL;
struct dns_cache *tmp = NULL;
pthread_mutex_lock(&dns_cache_head.lock);
list_for_each_entry_safe(dns_cache, tmp, &dns_cache_head.inactive_list, list)
{
_dns_cache_delete(dns_cache);
if (is_cache_init == 0) {
return;
}
pthread_mutex_lock(&dns_cache_head.lock);
list_for_each_entry_safe(dns_cache, tmp, &dns_cache_head.cache_list, list)
{
_dns_cache_delete(dns_cache);
_dns_cache_remove(dns_cache);
}
pthread_mutex_unlock(&dns_cache_head.lock);
pthread_mutex_destroy(&dns_cache_head.lock);
hash_table_free(dns_cache_head.cache_hash, free);
is_cache_init = 0;
}
const char *dns_cache_file_version(void)
{
const char *version = "cache ver 1.0";
const char *version = "cache ver 1.3";
return version;
}

View File

@@ -25,6 +25,7 @@
#include "hash.h"
#include "hashtable.h"
#include "list.h"
#include "timer.h"
#include <stdlib.h>
#include <time.h>
@@ -39,21 +40,8 @@ extern "C" {
#define MAGIC_CACHE_DATA 0x61546144
#define MAGIC_RECORD 0x64526352
enum CACHE_TYPE {
CACHE_TYPE_NONE,
CACHE_TYPE_ADDR,
CACHE_TYPE_PACKET,
};
enum CACHE_RECORD_TYPE {
CACHE_RECORD_TYPE_ACTIVE,
CACHE_RECORD_TYPE_INACTIVE,
CACHE_RECORD_TYPE_END,
};
struct dns_cache_data_head {
enum CACHE_TYPE cache_type;
int is_soa;
atomic_t ref;
ssize_t size;
uint32_t magic;
};
@@ -88,9 +76,10 @@ struct dns_cache_info {
char dns_group_name[DNS_GROUP_NAME_LEN];
uint32_t query_flag;
int ttl;
int rcode;
int hitnum;
int speed;
int no_inactive;
int timeout;
int hitnum_update_add;
int is_visited;
time_t insert_time;
@@ -99,7 +88,6 @@ struct dns_cache_info {
struct dns_cache_record {
uint32_t magic;
enum CACHE_RECORD_TYPE type;
struct dns_cache_info info;
};
@@ -113,6 +101,8 @@ struct dns_cache {
struct dns_cache_info info;
struct dns_cache_data *cache_data;
struct tw_timer_list timer;
};
struct dns_cache_file {
@@ -128,28 +118,34 @@ struct dns_cache_key {
uint32_t query_flag;
};
enum CACHE_TYPE dns_cache_data_type(struct dns_cache_data *cache_data);
uint32_t dns_cache_get_query_flag(struct dns_cache *dns_cache);
const char *dns_cache_get_dns_group_name(struct dns_cache *dns_cache);
void dns_cache_data_free(struct dns_cache_data *data);
struct dns_cache_data *dns_cache_new_data_packet(void *packet, size_t packet_len);
int dns_cache_init(int size, int enable_inactive, int inactive_list_expired);
typedef enum DNS_CACHE_TMOUT_ACTION {
DNS_CACHE_TMOUT_ACTION_OK = 0,
DNS_CACHE_TMOUT_ACTION_DEL = 1,
DNS_CACHE_TMOUT_ACTION_RETRY = 2,
} dns_cache_tmout_action_t;
int dns_cache_replace(struct dns_cache_key *key, int ttl, int speed, int no_inactive,
typedef dns_cache_tmout_action_t (*dns_cache_callback)(struct dns_cache *dns_cache);
int dns_cache_init(int size, dns_cache_callback timeout_callback);
int dns_cache_replace(struct dns_cache_key *key, int rcode, int ttl, int speed, int timeout, int update_time,
struct dns_cache_data *cache_data);
int dns_cache_replace_inactive(struct dns_cache_key *key, int ttl, int speed, int no_inactive,
struct dns_cache_data *cache_data);
int dns_cache_insert(struct dns_cache_key *key, int ttl, int speed, int no_inactive, struct dns_cache_data *cache_data);
int dns_cache_insert(struct dns_cache_key *key, int rcode, int ttl, int speed, int timeout,
struct dns_cache_data *cache_data);
struct dns_cache *dns_cache_lookup(struct dns_cache_key *key);
int dns_cache_total_num(void);
int dns_cache_update_timer(struct dns_cache_key *key, int timeout);
void dns_cache_delete(struct dns_cache *dns_cache);
void dns_cache_get(struct dns_cache *dns_cache);
@@ -162,25 +158,13 @@ int dns_cache_is_visited(struct dns_cache *dns_cache);
void dns_cache_update(struct dns_cache *dns_cache);
typedef void dns_cache_callback(struct dns_cache *dns_cache);
void dns_cache_invalidate(dns_cache_callback precallback, int ttl_pre, unsigned int max_callback_num,
dns_cache_callback inactive_precallback, int ttl_inactive_pre);
int dns_cache_get_ttl(struct dns_cache *dns_cache);
int dns_cache_get_cname_ttl(struct dns_cache *dns_cache);
int dns_cache_is_soa(struct dns_cache *dns_cache);
struct dns_cache_data *dns_cache_new_data_addr(void);
struct dns_cache_data *dns_cache_get_data(struct dns_cache *dns_cache);
void dns_cache_set_data_addr(struct dns_cache_data *dns_cache, char *cname, int cname_ttl, unsigned char *addr,
int addr_len);
void dns_cache_data_get(struct dns_cache_data *cache_data);
void dns_cache_set_data_soa(struct dns_cache_data *dns_cache, char *cname, int cname_ttl);
void dns_cache_data_put(struct dns_cache_data *cache_data);
void dns_cache_destroy(void);
@@ -188,6 +172,8 @@ int dns_cache_load(const char *file);
int dns_cache_save(const char *file, int check_lock);
int dns_cache_print(const char *file);
const char *dns_cache_file_version(void);
#ifdef __cplusplus

View File

@@ -61,9 +61,9 @@
#define DNS_TCP_BUFFER (32 * 1024)
#define DNS_TCP_IDLE_TIMEOUT (60 * 10)
#define DNS_TCP_CONNECT_TIMEOUT (5)
#define DNS_QUERY_TIMEOUT (500)
#define DNS_QUERY_TIMEOUT (3000)
#define DNS_QUERY_RETRY (4)
#define DNS_PENDING_SERVER_RETRY 40
#define DNS_PENDING_SERVER_RETRY 60
#define SOCKET_PRIORITY (6)
#define SOCKET_IP_TOS (IPTOS_LOWDELAY | IPTOS_RELIABILITY)
@@ -93,7 +93,7 @@ struct dns_server_info {
/* server ping handle */
struct ping_host_struct *ping_host;
char ip[DNS_HOSTNAME_LEN];
char ip[DNS_MAX_HOSTNAME];
int port;
char proxy_name[DNS_HOSTNAME_LEN];
/* server type */
@@ -107,6 +107,7 @@ struct dns_server_info {
int ttl_range;
SSL *ssl;
int ssl_write_len;
int ssl_want_write;
SSL_CTX *ssl_ctx;
SSL_SESSION *ssl_session;
@@ -161,7 +162,8 @@ struct dns_server_pending {
unsigned int has_v6;
unsigned int query_v4;
unsigned int query_v6;
unsigned int has_soa;
unsigned int has_soa_v4;
unsigned int has_soa_v6;
/* server type */
dns_server_type_t type;
@@ -266,7 +268,7 @@ struct dns_query_struct {
/* replied hash table */
DECLARE_HASHTABLE(replied_map, 4);
};
static int is_client_init;
static struct dns_client client;
static LIST_HEAD(pending_servers);
static pthread_mutex_t pending_server_mutex = PTHREAD_MUTEX_INITIALIZER;
@@ -421,6 +423,9 @@ static const char *_dns_server_get_type_string(dns_server_type_t type)
case DNS_SERVER_HTTPS:
type_str = "https";
break;
case DNS_SERVER_QUIC:
type_str = "quic";
break;
default:
break;
}
@@ -442,8 +447,8 @@ static struct addrinfo *_dns_client_getaddr(const char *host, char *port, int ty
ret = getaddrinfo(host, port, &hints, &result);
if (ret != 0) {
tlog(TLOG_ERROR, "get addr info failed. %s\n", gai_strerror(ret));
tlog(TLOG_ERROR, "host = %s, port = %s, type = %d, protocol = %d", host, port, type, protocol);
tlog(TLOG_WARN, "get addr info failed. %s\n", gai_strerror(ret));
tlog(TLOG_WARN, "host = %s, port = %s, type = %d, protocol = %d", host, port, type, protocol);
goto errout;
}
@@ -861,6 +866,7 @@ static char *_dns_client_server_get_tls_host_verify(struct dns_server_info *serv
struct client_dns_server_flag_https *flag_https = &server_info->flags.https;
tls_host_verify = flag_https->tls_host_verify;
} break;
case DNS_SERVER_QUIC:
case DNS_SERVER_TLS: {
struct client_dns_server_flag_tls *flag_tls = &server_info->flags.tls;
tls_host_verify = flag_tls->tls_host_verify;
@@ -893,6 +899,7 @@ static char *_dns_client_server_get_spki(struct dns_server_info *server_info, in
spki = flag_https->spki;
*spki_len = flag_https->spi_len;
} break;
case DNS_SERVER_QUIC:
case DNS_SERVER_TLS: {
struct client_dns_server_flag_tls *flag_tls = &server_info->flags.tls;
spki = flag_tls->spki;
@@ -953,7 +960,7 @@ static int _dns_client_set_trusted_cert(SSL_CTX *ssl_ctx)
return 0;
}
static SSL_CTX *_ssl_ctx_get(void)
static SSL_CTX *_ssl_ctx_get(int is_quic)
{
pthread_mutex_lock(&client.server_list_lock);
SSL_CTX *ssl_ctx = client.ssl_ctx;
@@ -963,7 +970,18 @@ static SSL_CTX *_ssl_ctx_get(void)
}
#if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
#if (OPENSSL_VERSION_NUMBER >= 0x30200000L)
if (is_quic) {
ssl_ctx = SSL_CTX_new(OSSL_QUIC_client_method());
} else {
ssl_ctx = SSL_CTX_new(TLS_client_method());
}
#else
if (is_quic) {
return NULL;
}
ssl_ctx = SSL_CTX_new(TLS_client_method());
#endif
#else
ssl_ctx = SSL_CTX_new(SSLv23_client_method());
#endif
@@ -1050,6 +1068,12 @@ static int _dns_client_server_add(char *server_ip, char *server_host, int port,
sock_type = SOCK_STREAM;
skip_check_cert = flag_https->skip_check_cert;
} break;
case DNS_SERVER_QUIC: {
struct client_dns_server_flag_tls *flag_tls = &flags->tls;
spki_data_len = flag_tls->spi_len;
sock_type = SOCK_DGRAM;
skip_check_cert = flag_tls->skip_check_cert;
} break;
case DNS_SERVER_TLS: {
struct client_dns_server_flag_tls *flag_tls = &flags->tls;
spki_data_len = flag_tls->spi_len;
@@ -1074,7 +1098,7 @@ static int _dns_client_server_add(char *server_ip, char *server_host, int port,
return 0;
}
snprintf(port_s, 8, "%d", port);
snprintf(port_s, sizeof(port_s), "%d", port);
gai = _dns_client_getaddr(server_ip, port_s, sock_type, 0);
if (gai == NULL) {
tlog(TLOG_DEBUG, "get address failed, %s:%d", server_ip, port);
@@ -1122,8 +1146,12 @@ static int _dns_client_server_add(char *server_ip, char *server_host, int port,
}
/* if server type is TLS, create ssl context */
if (server_type == DNS_SERVER_TLS || server_type == DNS_SERVER_HTTPS) {
server_info->ssl_ctx = _ssl_ctx_get();
if (server_type == DNS_SERVER_TLS || server_type == DNS_SERVER_HTTPS || server_type == DNS_SERVER_QUIC) {
if (server_type == DNS_SERVER_QUIC) {
server_info->ssl_ctx = _ssl_ctx_get(1);
} else {
server_info->ssl_ctx = _ssl_ctx_get(0);
}
if (server_info->ssl_ctx == NULL) {
tlog(TLOG_ERROR, "init ssl failed.");
goto errout;
@@ -1235,6 +1263,7 @@ static void _dns_client_shutdown_socket(struct dns_server_info *server_info)
shutdown(server_info->fd, SHUT_RDWR);
}
break;
case DNS_SERVER_QUIC:
case DNS_SERVER_TLS:
case DNS_SERVER_HTTPS:
if (server_info->ssl) {
@@ -1402,6 +1431,8 @@ static int _dns_client_add_server_pending(char *server_ip, char *server_host, in
struct client_dns_server_flags *flags, int is_pending)
{
int ret = 0;
struct addrinfo *gai = NULL;
char server_ip_tmp[DNS_HOSTNAME_LEN] = {0};
if (server_type >= DNS_SERVER_TYPE_END) {
tlog(TLOG_ERROR, "server type is invalid.");
@@ -1414,6 +1445,22 @@ static int _dns_client_add_server_pending(char *server_ip, char *server_host, in
tlog(TLOG_INFO, "add pending server %s", server_ip);
return 0;
}
} else if (check_is_ipaddr(server_ip) && is_pending == 0) {
gai = _dns_client_getaddr(server_ip, NULL, SOCK_STREAM, 0);
if (gai == NULL) {
return -1;
}
if (get_host_by_addr(server_ip_tmp, sizeof(server_ip_tmp), gai->ai_addr) != NULL) {
tlog(TLOG_INFO, "resolve %s to %s.", server_ip, server_ip_tmp);
server_ip = server_ip_tmp;
} else {
tlog(TLOG_INFO, "resolve %s failed.", server_ip);
freeaddrinfo(gai);
return -1;
}
freeaddrinfo(gai);
}
/* add server */
@@ -1779,17 +1826,12 @@ static int _dns_client_create_socket_udp_proxy(struct dns_server_info *server_in
}
set_fd_nonblock(fd, 1);
set_sock_keepalive(fd, 15, 3, 4);
set_sock_keepalive(fd, 30, 3, 5);
ret = proxy_conn_connect(proxy);
if (ret != 0) {
if (errno == ENETUNREACH || errno == EHOSTUNREACH || errno == EPERM || errno == EACCES) {
tlog(TLOG_DEBUG, "connect %s failed, %s", server_info->ip, strerror(errno));
goto errout;
}
if (errno != EINPROGRESS) {
tlog(TLOG_ERROR, "connect %s failed, %s", server_info->ip, strerror(errno));
tlog(TLOG_DEBUG, "connect %s failed, %s", server_info->ip, strerror(errno));
goto errout;
}
}
@@ -1843,14 +1885,8 @@ static int _dns_client_create_socket_udp(struct dns_server_info *server_info)
server_info->status = DNS_SERVER_STATUS_CONNECTIONLESS;
if (connect(fd, &server_info->addr, server_info->ai_addrlen) != 0) {
if (errno == ENETUNREACH || errno == EHOSTUNREACH || errno == ECONNREFUSED || errno == EPERM ||
errno == EACCES) {
tlog(TLOG_INFO, "connect %s failed, %s", server_info->ip, strerror(errno));
goto errout;
}
if (errno != EINPROGRESS) {
tlog(TLOG_ERROR, "connect %s failed, %s", server_info->ip, strerror(errno));
tlog(TLOG_DEBUG, "connect %s failed, %s", server_info->ip, strerror(errno));
goto errout;
}
}
@@ -1941,7 +1977,7 @@ static int _DNS_client_create_socket_tcp(struct dns_server_info *server_info)
setsockopt(fd, IPPROTO_IP, IP_TOS, &ip_tos, sizeof(ip_tos));
setsockopt(fd, IPPROTO_TCP, TCP_THIN_DUPACK, &yes, sizeof(yes));
setsockopt(fd, IPPROTO_TCP, TCP_THIN_LINEAR_TIMEOUTS, &yes, sizeof(yes));
set_sock_keepalive(fd, 15, 3, 4);
set_sock_keepalive(fd, 30, 3, 5);
if (proxy) {
ret = proxy_conn_connect(proxy);
@@ -1950,13 +1986,8 @@ static int _DNS_client_create_socket_tcp(struct dns_server_info *server_info)
}
if (ret != 0) {
if (errno == ENETUNREACH || errno == EHOSTUNREACH || errno == ECONNREFUSED) {
tlog(TLOG_DEBUG, "connect %s failed, %s", server_info->ip, strerror(errno));
goto errout;
}
if (errno != EINPROGRESS) {
tlog(TLOG_ERROR, "connect %s failed, %s", server_info->ip, strerror(errno));
tlog(TLOG_DEBUG, "connect %s failed, %s", server_info->ip, strerror(errno));
goto errout;
}
}
@@ -1994,6 +2025,138 @@ errout:
return -1;
}
static int _DNS_client_create_socket_quic(struct dns_server_info *server_info, char *hostname)
{
#ifdef OSSL_QUIC1_VERSION
int fd = 0;
struct epoll_event event;
SSL *ssl = NULL;
struct proxy_conn *proxy = NULL;
int ret = -1;
if (server_info->ssl_ctx == NULL) {
tlog(TLOG_ERROR, "create ssl ctx failed, %s", server_info->ip);
goto errout;
}
if (server_info->proxy_name[0] != '\0') {
proxy = proxy_conn_new(server_info->proxy_name, server_info->ip, server_info->port, 1);
if (proxy == NULL) {
tlog(TLOG_ERROR, "create proxy failed, %s, proxy: %s", server_info->ip, server_info->proxy_name);
goto errout;
}
fd = proxy_conn_get_fd(proxy);
} else {
fd = socket(server_info->ai_family, SOCK_DGRAM, IPPROTO_UDP);
}
ssl = SSL_new(server_info->ssl_ctx);
if (ssl == NULL) {
tlog(TLOG_ERROR, "new ssl failed, %s", server_info->ip);
goto errout;
}
if (fd < 0) {
tlog(TLOG_ERROR, "create socket failed, %s", strerror(errno));
goto errout;
}
if (set_fd_nonblock(fd, 1) != 0) {
tlog(TLOG_ERROR, "set socket non block failed, %s", strerror(errno));
goto errout;
}
if (server_info->so_mark >= 0) {
unsigned int so_mark = server_info->so_mark;
if (setsockopt(fd, SOL_SOCKET, SO_MARK, &so_mark, sizeof(so_mark)) != 0) {
tlog(TLOG_DEBUG, "set socket mark failed, %s", strerror(errno));
}
}
if (proxy) {
ret = proxy_conn_connect(proxy);
} else {
ret = connect(fd, &server_info->addr, server_info->ai_addrlen);
}
if (ret != 0) {
if (errno != EINPROGRESS) {
tlog(TLOG_DEBUG, "connect %s failed, %s", server_info->ip, strerror(errno));
goto errout;
}
}
SSL_set_connect_state(ssl);
SSL_set_blocking_mode(ssl, 0);
if (SSL_set_fd(ssl, fd) == 0) {
tlog(TLOG_ERROR, "ssl set fd failed.");
goto errout;
}
/* reuse ssl session */
if (server_info->ssl_session) {
SSL_set_session(ssl, server_info->ssl_session);
}
SSL_set_mode(ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER | SSL_MODE_ENABLE_PARTIAL_WRITE);
if (hostname[0] != 0) {
SSL_set_tlsext_host_name(ssl, hostname);
}
SSL_set1_host(ssl, hostname);
static const unsigned char alpn[] = {3, 'd', 'o', 'q'};
if (SSL_set_alpn_protos(ssl, alpn, sizeof(alpn))) {
tlog(TLOG_INFO, "SSL_set_alpn_protos failed.");
goto errout;
}
server_info->fd = fd;
server_info->ssl = ssl;
server_info->ssl_write_len = -1;
server_info->status = DNS_SERVER_STATUS_CONNECTING;
server_info->proxy = proxy;
memset(&event, 0, sizeof(event));
event.events = EPOLLIN | EPOLLOUT;
event.data.ptr = server_info;
if (epoll_ctl(client.epoll_fd, EPOLL_CTL_ADD, fd, &event) != 0) {
tlog(TLOG_ERROR, "epoll ctl failed.");
goto errout;
}
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 && proxy == NULL) {
close(fd);
}
if (ssl) {
SSL_free(ssl);
}
if (proxy) {
proxy_conn_free(proxy);
}
return -1;
#else
return -1;
#endif
}
static int _DNS_client_create_socket_tls(struct dns_server_info *server_info, char *hostname)
{
int fd = 0;
@@ -2053,7 +2216,7 @@ static int _DNS_client_create_socket_tls(struct dns_server_info *server_info, ch
setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes));
setsockopt(fd, IPPROTO_TCP, TCP_THIN_DUPACK, &yes, sizeof(yes));
setsockopt(fd, IPPROTO_TCP, TCP_THIN_LINEAR_TIMEOUTS, &yes, sizeof(yes));
set_sock_keepalive(fd, 15, 3, 4);
set_sock_keepalive(fd, 30, 3, 5);
setsockopt(fd, SOL_SOCKET, SO_PRIORITY, &priority, sizeof(priority));
setsockopt(fd, IPPROTO_IP, IP_TOS, &ip_tos, sizeof(ip_tos));
@@ -2064,13 +2227,8 @@ static int _DNS_client_create_socket_tls(struct dns_server_info *server_info, ch
}
if (ret != 0) {
if (errno == ENETUNREACH || errno == EHOSTUNREACH || errno == ECONNREFUSED) {
tlog(TLOG_DEBUG, "connect %s failed, %s", server_info->ip, strerror(errno));
goto errout;
}
if (errno != EINPROGRESS) {
tlog(TLOG_ERROR, "connect %s failed, %s", server_info->ip, strerror(errno));
tlog(TLOG_DEBUG, "connect %s failed, %s", server_info->ip, strerror(errno));
goto errout;
}
}
@@ -2151,6 +2309,10 @@ static int _dns_client_create_socket(struct dns_server_info *server_info)
struct client_dns_server_flag_tls *flag_tls = NULL;
flag_tls = &server_info->flags.tls;
return _DNS_client_create_socket_tls(server_info, flag_tls->hostname);
} else if (server_info->type == DNS_SERVER_QUIC) {
struct client_dns_server_flag_tls *flag_tls = NULL;
flag_tls = &server_info->flags.tls;
return _DNS_client_create_socket_quic(server_info, flag_tls->hostname);
} else if (server_info->type == DNS_SERVER_HTTPS) {
struct client_dns_server_flag_https *flag_https = NULL;
flag_https = &server_info->flags.https;
@@ -2239,13 +2401,14 @@ static int _dns_client_process_udp_proxy(struct dns_server_info *server_info, st
}
int latency = get_tick_count() - server_info->send_tick;
tlog(TLOG_DEBUG, "recv udp packet from %s, len: %d, latency: %d",
get_host_by_addr(from_host, sizeof(from_host), (struct sockaddr *)&from), len, latency);
if (latency < server_info->drop_packet_latency_ms) {
tlog(TLOG_DEBUG, "drop packet from %s, latency: %d", from_host, latency);
return 0;
}
tlog(TLOG_DEBUG, "recv udp packet from %s, len: %d",
get_host_by_addr(from_host, sizeof(from_host), (struct sockaddr *)&from), len);
/* update recv time */
time(&server_info->last_recv);
@@ -2322,14 +2485,15 @@ static int _dns_client_process_udp(struct dns_server_info *server_info, struct e
}
}
tlog(TLOG_DEBUG, "recv udp packet from %s, len: %d, ttl: %d",
get_host_by_addr(from_host, sizeof(from_host), (struct sockaddr *)&from), len, ttl);
int latency = get_tick_count() - server_info->send_tick;
tlog(TLOG_DEBUG, "recv udp packet from %s, len: %d, ttl: %d, latency: %d",
get_host_by_addr(from_host, sizeof(from_host), (struct sockaddr *)&from), len, ttl, latency);
/* update recv time */
time(&server_info->last_recv);
int latency = get_tick_count() - server_info->send_tick;
if (latency < server_info->drop_packet_latency_ms) {
tlog(TLOG_DEBUG, "drop packet from %s, latency: %d", from_host, latency);
return 0;
}
@@ -2374,16 +2538,16 @@ static int _dns_client_socket_ssl_send(struct dns_server_info *server, const voi
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;
ret = -SSL_ERROR_WANT_READ;
break;
case SSL_ERROR_WANT_WRITE:
errno = EAGAIN;
ret = -1;
ret = -SSL_ERROR_WANT_WRITE;
break;
case SSL_ERROR_SSL:
ssl_err = ERR_get_error();
@@ -2423,7 +2587,7 @@ static int _dns_client_socket_ssl_recv(struct dns_server_info *server, void *buf
}
ret = _ssl_read(server, buf, num);
if (ret >= 0) {
if (ret > 0) {
return ret;
}
@@ -2435,11 +2599,11 @@ static int _dns_client_socket_ssl_recv(struct dns_server_info *server, void *buf
break;
case SSL_ERROR_WANT_READ:
errno = EAGAIN;
ret = -1;
ret = -SSL_ERROR_WANT_READ;
break;
case SSL_ERROR_WANT_WRITE:
errno = EAGAIN;
ret = -1;
ret = -SSL_ERROR_WANT_WRITE;
break;
case SSL_ERROR_SSL:
ssl_err = ERR_get_error();
@@ -2453,7 +2617,13 @@ static int _dns_client_socket_ssl_recv(struct dns_server_info *server, void *buf
return 0;
}
tlog(TLOG_INFO, "SSL read fail error no: %s(%lx), len: %d\n", ERR_reason_error_string(ssl_err), ssl_err, num);
#ifdef SSL_R_UNEXPECTED_EOF_WHILE_READING
if (ssl_reason == SSL_R_UNEXPECTED_EOF_WHILE_READING) {
return 0;
}
#endif
tlog(TLOG_WARN, "SSL read fail error no: %s(%lx), reason: %d\n", ERR_reason_error_string(ssl_err), ssl_err, ssl_reason);
errno = EFAULT;
ret = -1;
break;
@@ -2462,9 +2632,6 @@ static int _dns_client_socket_ssl_recv(struct dns_server_info *server, void *buf
return 0;
}
if (errno != ECONNRESET) {
tlog(TLOG_INFO, "SSL syscall failed, %s ", strerror(errno));
}
ret = -1;
return ret;
default:
@@ -2476,22 +2643,52 @@ static int _dns_client_socket_ssl_recv(struct dns_server_info *server, void *buf
return ret;
}
static int _dns_client_ssl_poll_event(struct dns_server_info *server_info, int ssl_ret)
{
struct epoll_event fd_event;
memset(&fd_event, 0, sizeof(fd_event));
if (ssl_ret == SSL_ERROR_WANT_READ) {
fd_event.events = EPOLLIN;
} else if (ssl_ret == SSL_ERROR_WANT_WRITE) {
fd_event.events = EPOLLOUT | EPOLLIN;
} else {
goto errout;
}
fd_event.data.ptr = server_info;
if (epoll_ctl(client.epoll_fd, EPOLL_CTL_MOD, server_info->fd, &fd_event) != 0) {
tlog(TLOG_ERROR, "epoll ctl failed, %s", strerror(errno));
goto errout;
}
return 0;
errout:
return -1;
}
static int _dns_client_socket_send(struct dns_server_info *server_info)
{
if (server_info->type == DNS_SERVER_UDP) {
return -1;
} 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) {
} else if (server_info->type == DNS_SERVER_TLS || server_info->type == DNS_SERVER_HTTPS ||
server_info->type == DNS_SERVER_QUIC) {
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;
}
server_info->ssl_want_write = 0;
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;
if (ret < 0 && errno == EAGAIN) {
server_info->ssl_write_len = write_len;
if (_dns_client_ssl_poll_event(server_info, SSL_ERROR_WANT_WRITE) == 0) {
errno = EAGAIN;
}
}
return ret;
@@ -2507,9 +2704,18 @@ static int _dns_client_socket_recv(struct dns_server_info *server_info)
} else if (server_info->type == DNS_SERVER_TCP) {
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, server_info->recv_buff.data + server_info->recv_buff.len,
DNS_TCP_BUFFER - server_info->recv_buff.len);
} else if (server_info->type == DNS_SERVER_TLS || server_info->type == DNS_SERVER_HTTPS ||
server_info->type == DNS_SERVER_QUIC) {
int ret = _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);
if (ret == -SSL_ERROR_WANT_WRITE && errno == EAGAIN) {
if (_dns_client_ssl_poll_event(server_info, SSL_ERROR_WANT_WRITE) == 0) {
errno = EAGAIN;
server_info->ssl_want_write = 1;
}
}
return ret;
} else {
return -1;
}
@@ -2632,7 +2838,7 @@ static int _dns_client_process_tcp(struct dns_server_info *server_info, struct e
goto errout;
}
tlog(TLOG_ERROR, "recv failed, server %s:%d, %s\n", server_info->ip, server_info->port, strerror(errno));
tlog(TLOG_WARN, "recv failed, server %s:%d, %s\n", server_info->ip, server_info->port, strerror(errno));
goto errout;
}
@@ -2674,7 +2880,7 @@ static int _dns_client_process_tcp(struct dns_server_info *server_info, struct e
server_info->status = DNS_SERVER_STATUS_DISCONNECTED;
}
if (server_info->send_buff.len > 0) {
if (server_info->send_buff.len > 0 || server_info->ssl_want_write == 1) {
/* send existing send_buffer data */
len = _dns_client_socket_send(server_info);
if (len < 0) {
@@ -2818,7 +3024,7 @@ static int _dns_client_verify_common_name(struct dns_server_info *server_info, X
tlog(TLOG_DEBUG, "peer SAN: %s", dns->data);
if (_dns_client_tls_matchName(tls_host_verify, (char *)dns->data, dns->length) == 0) {
tlog(TLOG_INFO, "peer SAN match: %s", dns->data);
tlog(TLOG_DEBUG, "peer SAN match: %s", dns->data);
return 0;
}
} break;
@@ -2969,21 +3175,14 @@ 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);
if (ret == 0) {
goto errout;
} else if (ret < 0) {
if (ret <= 0) {
memset(&fd_event, 0, sizeof(fd_event));
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) {
fd_event.events = EPOLLOUT | EPOLLIN;
} else if (ssl_ret == SSL_ERROR_SYSCALL) {
if (errno != ENETUNREACH) {
tlog(TLOG_WARN, "Handshake with %s failed, %s", server_info->ip, strerror(errno));
}
goto errout;
} else {
if (_dns_client_ssl_poll_event(server_info, ssl_ret) == 0) {
return 0;
}
if (ssl_ret != SSL_ERROR_SYSCALL) {
unsigned long ssl_err = ERR_get_error();
int ssl_reason = ERR_GET_REASON(ssl_err);
tlog(TLOG_WARN, "Handshake with %s failed, error no: %s(%d, %d, %d)\n", server_info->ip,
@@ -2991,13 +3190,10 @@ static int _dns_client_process_tls(struct dns_server_info *server_info, struct e
goto errout;
}
fd_event.data.ptr = server_info;
if (epoll_ctl(client.epoll_fd, EPOLL_CTL_MOD, server_info->fd, &fd_event) != 0) {
tlog(TLOG_ERROR, "epoll ctl failed, %s", strerror(errno));
goto errout;
if (errno != ENETUNREACH) {
tlog(TLOG_WARN, "Handshake with %s failed, %s", server_info->ip, strerror(errno));
}
return 0;
goto errout;
}
tlog(TLOG_DEBUG, "tls server %s connected.\n", server_info->ip);
@@ -3127,7 +3323,8 @@ static int _dns_client_process(struct dns_server_info *server_info, struct epoll
} else if (server_info->type == DNS_SERVER_TCP) {
/* receive from tcp */
return _dns_client_process_tcp(server_info, event, now);
} else if (server_info->type == DNS_SERVER_TLS || server_info->type == DNS_SERVER_HTTPS) {
} else if (server_info->type == DNS_SERVER_TLS || server_info->type == DNS_SERVER_HTTPS ||
server_info->type == DNS_SERVER_QUIC) {
/* receive from tls */
return _dns_client_process_tls(server_info, event, now);
} else {
@@ -3329,7 +3526,8 @@ static int _dns_client_send_https(struct dns_server_info *server_info, void *pac
http_len = snprintf((char *)inpacket, DNS_IN_PACKSIZE,
"POST %s HTTP/1.1\r\n"
"Host: %s\r\n"
"content-type: application/dns-message\r\n"
"User-Agent: smartdns\r\n"
"Content-Type: application/dns-message\r\n"
"Content-Length: %d\r\n"
"\r\n",
https_flag->path, https_flag->httphost, len);
@@ -3362,6 +3560,11 @@ static int _dns_client_send_https(struct dns_server_info *server_info, void *pac
return 0;
}
static int _dns_client_send_quic(struct dns_server_info *server_info, void *packet, unsigned short len)
{
return _dns_client_send_tls(server_info, packet, len);
}
static int _dns_client_setup_server_packet(struct dns_server_info *server_info, struct dns_query_struct *query,
void *default_packet, int default_packet_len,
unsigned char *packet_data_buffer, void **packet_data, int *packet_data_len)
@@ -3370,21 +3573,22 @@ static int _dns_client_setup_server_packet(struct dns_server_info *server_info,
struct dns_packet *packet = (struct dns_packet *)packet_buff;
struct dns_head head;
int encode_len = 0;
int repack = 0;
int hitchhiking = 0;
*packet_data = default_packet;
*packet_data_len = default_packet_len;
if (query->qtype != DNS_T_AAAA && query->qtype != DNS_T_A) {
/* no need to encode packet */
return 0;
if (server_info->ecs_ipv4.enable == true || server_info->ecs_ipv6.enable == true) {
repack = 1;
}
if (server_info->ecs_ipv4.enable == false && query->qtype == DNS_T_A) {
/* no need to encode packet */
return 0;
if ((server_info->flags.server_flag & SERVER_FLAG_HITCHHIKING) != 0) {
hitchhiking = 1;
repack = 1;
}
if (server_info->ecs_ipv6.enable == false && query->qtype == DNS_T_AAAA) {
if (repack == 0) {
/* no need to encode packet */
return 0;
}
@@ -3404,6 +3608,11 @@ static int _dns_client_setup_server_packet(struct dns_server_info *server_info,
return -1;
}
if (hitchhiking != 0 && dns_add_domain(packet, "-", query->qtype, DNS_C_IN) != 0) {
tlog(TLOG_ERROR, "add domain to packet failed.");
return -1;
}
/* add question */
if (dns_add_domain(packet, query->domain, query->qtype, DNS_C_IN) != 0) {
tlog(TLOG_ERROR, "add domain to packet failed.");
@@ -3411,11 +3620,21 @@ static int _dns_client_setup_server_packet(struct dns_server_info *server_info,
}
dns_set_OPT_payload_size(packet, DNS_IN_PACKSIZE);
/* dns_add_OPT_TCP_KEEPALIVE(packet, 600); */
if (query->qtype == DNS_T_A && server_info->ecs_ipv4.enable) {
if (server_info->type != DNS_SERVER_UDP) {
dns_add_OPT_TCP_KEEPALIVE(packet, 6000);
}
if ((query->qtype == DNS_T_A && server_info->ecs_ipv4.enable)) {
dns_add_OPT_ECS(packet, &server_info->ecs_ipv4.ecs);
} else if (query->qtype == DNS_T_AAAA && server_info->ecs_ipv6.enable) {
} else if ((query->qtype == DNS_T_AAAA && server_info->ecs_ipv6.enable)) {
dns_add_OPT_ECS(packet, &server_info->ecs_ipv6.ecs);
} else {
if (server_info->ecs_ipv6.enable) {
dns_add_OPT_ECS(packet, &server_info->ecs_ipv6.ecs);
} else if (server_info->ecs_ipv4.enable) {
dns_add_OPT_ECS(packet, &server_info->ecs_ipv4.ecs);
}
}
/* encode packet */
@@ -3479,7 +3698,7 @@ static int _dns_client_send_packet(struct dns_query_struct *query, void *packet,
}
}
total_server++;
tlog(TLOG_DEBUG, "send query to server %s", server_info->ip);
tlog(TLOG_DEBUG, "send query to server %s:%d", server_info->ip, server_info->port);
if (server_info->fd <= 0) {
ret = _dns_client_create_socket(server_info);
if (ret != 0) {
@@ -3496,6 +3715,8 @@ static int _dns_client_send_packet(struct dns_query_struct *query, void *packet,
atomic_inc(&query->dns_request_sent);
send_count++;
errno = 0;
server_info->send_tick = get_tick_count();
switch (server_info->type) {
case DNS_SERVER_UDP:
/* udp query */
@@ -3517,6 +3738,11 @@ static int _dns_client_send_packet(struct dns_query_struct *query, void *packet,
ret = _dns_client_send_https(server_info, packet_data, packet_data_len);
send_err = errno;
break;
case DNS_SERVER_QUIC:
/* quic query */
ret = _dns_client_send_quic(server_info, packet_data, packet_data_len);
send_err = errno;
break;
default:
/* unsupported query type */
ret = -1;
@@ -3548,7 +3774,6 @@ static int _dns_client_send_packet(struct dns_query_struct *query, void *packet,
continue;
}
time(&server_info->last_send);
server_info->send_tick = get_tick_count();
}
pthread_mutex_unlock(&client.server_list_lock);
@@ -3616,7 +3841,7 @@ static int _dns_client_send_query(struct dns_query_struct *query)
}
dns_set_OPT_payload_size(packet, DNS_IN_PACKSIZE);
/* dns_add_OPT_TCP_KEEPALIVE(packet, 600); */
/* dns_add_OPT_TCP_KEEPALIVE(packet, 1200); */
if (_dns_client_dns_add_ecs(query, packet) != 0) {
tlog(TLOG_ERROR, "add ecs failed.");
return -1;
@@ -3651,20 +3876,20 @@ static int _dns_client_query_setup_default_ecs(struct dns_query_struct *query)
if (client.ecs_ipv4.enable) {
add_ipv4_ecs = 1;
} else if (client.ecs_ipv6.enable) {
add_ipv4_ecs = 1;
add_ipv6_ecs = 1;
}
}
if (add_ipv4_ecs) {
memcpy(&query->ecs, &client.ecs_ipv4, sizeof(query->ecs));
return 0;
}
if (add_ipv6_ecs) {
memcpy(&query->ecs, &client.ecs_ipv6, sizeof(query->ecs));
return 0;
}
if (add_ipv4_ecs) {
memcpy(&query->ecs, &client.ecs_ipv4, sizeof(query->ecs));
return 0;
}
return 0;
}
@@ -3889,31 +4114,40 @@ static void _dns_client_check_servers(void)
pthread_mutex_unlock(&client.server_list_lock);
}
static int _dns_client_pending_server_resolve(const char *domain, dns_rtcode_t rtcode, dns_type_t addr_type, char *ip,
unsigned int ping_time, void *user_ptr)
static int _dns_client_pending_server_resolve(const struct dns_result *result, void *user_ptr)
{
struct dns_server_pending *pending = user_ptr;
int ret = 0;
int has_soa = 0;
if (rtcode == DNS_RC_NXDOMAIN) {
pending->has_soa = 1;
if (result->rtcode == DNS_RC_NXDOMAIN || result->has_soa == 1 || result->rtcode == DNS_RC_REFUSED ||
(result->rtcode == DNS_RC_NOERROR && result->ip_num == 0)) {
has_soa = 1;
}
if (addr_type == DNS_T_A) {
if (result->addr_type == DNS_T_A) {
pending->ping_time_v4 = -1;
if (rtcode == DNS_RC_NOERROR) {
if (result->rtcode == DNS_RC_NOERROR && result->ip_num > 0) {
pending->has_v4 = 1;
pending->ping_time_v4 = ping_time;
pending->has_soa = 0;
safe_strncpy(pending->ipv4, ip, DNS_HOSTNAME_LEN);
pending->ping_time_v4 = result->ping_time;
pending->has_soa_v4 = 0;
safe_strncpy(pending->ipv4, result->ip, DNS_HOSTNAME_LEN);
} else if (has_soa) {
pending->has_v4 = 0;
pending->ping_time_v4 = -1;
pending->has_soa_v4 = 1;
}
} else if (addr_type == DNS_T_AAAA) {
} else if (result->addr_type == DNS_T_AAAA) {
pending->ping_time_v6 = -1;
if (rtcode == DNS_RC_NOERROR) {
if (result->rtcode == DNS_RC_NOERROR && result->ip_num > 0) {
pending->has_v6 = 1;
pending->ping_time_v6 = ping_time;
pending->has_soa = 0;
safe_strncpy(pending->ipv6, ip, DNS_HOSTNAME_LEN);
pending->ping_time_v6 = result->ping_time;
pending->has_soa_v6 = 0;
safe_strncpy(pending->ipv6, result->ip, DNS_HOSTNAME_LEN);
} else if (has_soa) {
pending->has_v6 = 0;
pending->ping_time_v6 = -1;
pending->has_soa_v6 = 1;
}
} else {
ret = -1;
@@ -4007,10 +4241,29 @@ static void _dns_client_add_pending_servers(void)
int add_success = 0;
char *dnsserver_ip = NULL;
/* if has no bootstrap DNS, just call getaddrinfo to get address */
if (dns_client_has_bootstrap_dns == 0) {
list_del_init(&pending->retry_list);
_dns_client_server_pending_release(pending);
pending->retry_cnt++;
if (_dns_client_add_pendings(pending, pending->host) != 0) {
pthread_mutex_unlock(&pending_server_mutex);
tlog(TLOG_INFO, "add pending DNS server %s from resolv.conf failed, retry %d...", pending->host,
pending->retry_cnt - 1);
if (pending->retry_cnt - 1 > DNS_PENDING_SERVER_RETRY) {
tlog(TLOG_WARN, "add pending DNS server %s from resolv.conf failed, exit...", pending->host);
exit(1);
}
continue;
}
_dns_client_server_pending_release(pending);
continue;
}
if (pending->query_v4 == 0) {
pending->query_v4 = 1;
_dns_client_server_pending_get(pending);
if (dns_server_query(pending->host, DNS_T_A, 0, _dns_client_pending_server_resolve, pending) != 0) {
if (dns_server_query(pending->host, DNS_T_A, NULL, _dns_client_pending_server_resolve, pending) != 0) {
_dns_client_server_pending_release(pending);
pending->query_v4 = 0;
}
@@ -4019,9 +4272,9 @@ 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, 0, _dns_client_pending_server_resolve, pending) != 0) {
if (dns_server_query(pending->host, DNS_T_AAAA, NULL, _dns_client_pending_server_resolve, pending) != 0) {
_dns_client_server_pending_release(pending);
pending->query_v4 = 0;
pending->query_v6 = 0;
}
}
@@ -4052,7 +4305,7 @@ static void _dns_client_add_pending_servers(void)
continue;
}
if (pending->has_soa && dnsserver_ip == NULL) {
if (dnsserver_ip == NULL && pending->has_soa_v4 && pending->has_soa_v6) {
tlog(TLOG_WARN, "add pending DNS server %s failed, no such host.", pending->host);
_dns_client_server_pending_remove(pending);
continue;
@@ -4068,22 +4321,6 @@ static void _dns_client_add_pending_servers(void)
pending->query_v4 = 0;
pending->query_v6 = 0;
}
/* if has no bootstrap DNS, just call getaddrinfo to get address */
if (dns_client_has_bootstrap_dns == 0) {
if (_dns_client_add_pendings(pending, pending->host) != 0) {
pthread_mutex_unlock(&pending_server_mutex);
tlog(TLOG_INFO, "add pending DNS server %s from resolv.conf failed, retry %d...", pending->host,
pending->retry_cnt - 1);
if (pending->retry_cnt - 1 > DNS_PENDING_SERVER_RETRY) {
tlog(TLOG_WARN, "add pending DNS server %s from resolv.conf failed, exit...", pending->host);
exit(1);
}
continue;
}
_dns_client_server_pending_release(pending);
}
}
}
@@ -4117,14 +4354,19 @@ static void _dns_client_period_run(unsigned int msec)
{
/* free timed out query, and notify caller */
list_del_init(&query->period_list);
_dns_client_check_udp_nat(query);
/* check udp nat after retrying. */
if (atomic_read(&query->retry_count) == 1) {
_dns_client_check_udp_nat(query);
}
if (atomic_dec_and_test(&query->retry_count) || (query->has_result != 0)) {
_dns_client_query_remove(query);
if (query->has_result == 0) {
tlog(TLOG_INFO, "retry query %s, type: %d, id: %d failed", query->domain, query->qtype, query->sid);
tlog(TLOG_DEBUG, "retry query %s, type: %d, id: %d failed", query->domain, query->qtype, query->sid);
}
} else {
tlog(TLOG_INFO, "retry query %s, type: %d, id: %d", query->domain, query->qtype, query->sid);
tlog(TLOG_DEBUG, "retry query %s, type: %d, id: %d", query->domain, query->qtype, query->sid);
_dns_client_send_query(query);
}
_dns_client_query_release(query);
@@ -4333,11 +4575,13 @@ int dns_client_init(void)
int fd_wakeup = -1;
int ret = 0;
if (client.epoll_fd > 0) {
if (is_client_init == 1) {
return -1;
}
srandom(time(NULL));
if (client.epoll_fd > 0) {
return -1;
}
memset(&client, 0, sizeof(client));
pthread_attr_init(&attr);
@@ -4382,6 +4626,7 @@ int dns_client_init(void)
}
client.fd_wakeup = fd_wakeup;
is_client_init = 1;
return 0;
errout:
@@ -4408,6 +4653,10 @@ errout:
void dns_client_exit(void)
{
if (is_client_init == 0) {
return;
}
if (client.tid) {
void *ret = NULL;
atomic_set(&client.run, 0);
@@ -4429,4 +4678,6 @@ void dns_client_exit(void)
SSL_CTX_free(client.ssl_ctx);
client.ssl_ctx = NULL;
}
is_client_init = 0;
}

View File

@@ -33,6 +33,7 @@ typedef enum {
DNS_SERVER_TCP,
DNS_SERVER_TLS,
DNS_SERVER_HTTPS,
DNS_SERVER_QUIC,
DNS_SERVER_TYPE_END,
} dns_server_type_t;

File diff suppressed because it is too large Load Diff

View File

@@ -56,7 +56,12 @@ extern "C" {
#define DEFAULT_DNS_PORT 53
#define DEFAULT_DNS_TLS_PORT 853
#define DEFAULT_DNS_HTTPS_PORT 443
#define DEFAULT_DNS_QUIC_PORT 853
#define DNS_MAX_CONF_CNAME_LEN 256
#define MAX_QTYPE_NUM 65535
#define DNS_MAX_REPLY_IP_NUM 8
#define DNS_DEFAULT_CHECKPOINT_TIME (3600 * 24)
#define SMARTDNS_CONF_FILE "/etc/smartdns/smartdns.conf"
#define SMARTDNS_LOG_FILE "/var/log/smartdns/smartdns.log"
#define SMARTDNS_AUDIT_FILE "/var/log/smartdns/smartdns-audit.log"
@@ -82,6 +87,12 @@ enum domain_rule {
DOMAIN_RULE_MAX,
};
enum ip_rule {
IP_RULE_FLAGS = 0,
IP_RULE_ALIAS = 1,
IP_RULE_MAX,
};
typedef enum {
DNS_BIND_TYPE_UDP,
DNS_BIND_TYPE_TCP,
@@ -114,8 +125,15 @@ typedef enum {
#define DOMAIN_FLAG_NO_SERVE_EXPIRED (1 << 15)
#define DOMAIN_FLAG_CNAME_IGN (1 << 16)
#define DOMAIN_FLAG_NO_CACHE (1 << 17)
#define DOMAIN_FLAG_NO_IPALIAS (1 << 18)
#define IP_RULE_FLAG_BLACKLIST (1 << 0)
#define IP_RULE_FLAG_WHITELIST (1 << 1)
#define IP_RULE_FLAG_BOGUS (1 << 2)
#define IP_RULE_FLAG_IP_IGNORE (1 << 3)
#define SERVER_FLAG_EXCLUDE_DEFAULT (1 << 0)
#define SERVER_FLAG_HITCHHIKING (1 << 1)
#define BIND_FLAG_NO_RULE_ADDR (1 << 0)
#define BIND_FLAG_NO_RULE_NAMESERVER (1 << 1)
@@ -128,6 +146,7 @@ typedef enum {
#define BIND_FLAG_FORCE_AAAA_SOA (1 << 8)
#define BIND_FLAG_NO_RULE_CNAME (1 << 9)
#define BIND_FLAG_NO_RULE_NFTSET (1 << 10)
#define BIND_FLAG_NO_IP_ALIAS (1 << 11)
enum response_mode_type {
DNS_RESPONSE_MODE_FIRST_PING_IP = 0,
@@ -148,12 +167,14 @@ struct dns_rule_flags {
struct dns_rule_address_IPV4 {
struct dns_rule head;
unsigned char ipv4_addr[DNS_RR_A_LEN];
char addr_num;
unsigned char ipv4_addr[][DNS_RR_A_LEN];
};
struct dns_rule_address_IPV6 {
struct dns_rule head;
unsigned char ipv6_addr[DNS_RR_AAAA_LEN];
char addr_num;
unsigned char ipv6_addr[][DNS_RR_AAAA_LEN];
};
struct dns_ipset_name {
@@ -212,8 +233,9 @@ extern struct dns_nftset_names dns_conf_nftset_no_speed;
struct dns_domain_rule {
struct dns_rule head;
unsigned char sub_rule_only : 1;
unsigned char root_rule_only : 1;
struct dns_rule *rules[DOMAIN_RULE_MAX];
int is_sub_rule[DOMAIN_RULE_MAX];
};
struct dns_nameserver_rule {
@@ -303,7 +325,7 @@ struct dns_edns_client_subnet {
};
struct dns_servers {
char server[DNS_MAX_IPLEN];
char server[DNS_MAX_CNAME_LEN];
unsigned short port;
unsigned int result_flag;
unsigned int server_flag;
@@ -343,18 +365,18 @@ struct dns_bogus_ip_address {
};
};
enum address_rule {
ADDRESS_RULE_BLACKLIST = 1,
ADDRESS_RULE_WHITELIST = 2,
ADDRESS_RULE_BOGUS = 3,
ADDRESS_RULE_IP_IGNORE = 4,
struct dns_iplist_ip_address {
int addr_len;
union {
unsigned char ipv4_addr[DNS_RR_A_LEN];
unsigned char ipv6_addr[DNS_RR_AAAA_LEN];
unsigned char addr[0];
};
};
struct dns_ip_address_rule {
unsigned int blacklist : 1;
unsigned int whitelist : 1;
unsigned int bogus : 1;
unsigned int ip_ignore : 1;
struct dns_iplist_ip_addresses {
int ipaddr_num;
struct dns_iplist_ip_address *ipaddr;
};
struct dns_conf_address_rule {
@@ -381,15 +403,7 @@ struct dns_bind_ip {
struct nftset_ipset_rules nftset_ipset_rule;
};
struct dns_qtype_soa_list {
struct hlist_node node;
uint32_t qtypeid;
};
struct dns_qtype_soa_table {
DECLARE_HASHTABLE(qtype, 8);
};
extern struct dns_qtype_soa_table dns_qtype_soa_table;
extern uint8_t *dns_qtype_soa_table;
struct dns_domain_set_rule {
struct list_head list;
@@ -420,8 +434,48 @@ struct dns_domain_set_name_table {
};
extern struct dns_domain_set_name_table dns_domain_set_name_table;
struct dns_ip_rule {
atomic_t refcnt;
enum ip_rule rule;
};
enum dns_ip_set_type {
DNS_IP_SET_LIST = 0,
};
struct dns_ip_rules {
struct dns_ip_rule *rules[IP_RULE_MAX];
};
struct ip_rule_flags {
struct dns_ip_rule head;
unsigned int flags;
unsigned int is_flag_set;
};
struct ip_rule_alias {
struct dns_ip_rule head;
struct dns_iplist_ip_addresses ip_alias;
};
struct dns_ip_set_name {
struct list_head list;
enum dns_ip_set_type type;
char file[DNS_MAX_PATH];
};
struct dns_ip_set_name_list {
struct hlist_node node;
char name[DNS_MAX_CNAME_LEN];
struct list_head set_name_list;
};
struct dns_ip_set_name_table {
DECLARE_HASHTABLE(names, 4);
};
extern struct dns_ip_set_name_table dns_ip_set_name_table;
struct dns_set_rule_add_callback_args {
enum domain_rule type;
int type;
void *rule;
};
@@ -435,6 +489,25 @@ struct dns_dns64 {
uint32_t prefix_len;
};
struct dns_srv_record {
struct list_head list;
char host[DNS_MAX_CNAME_LEN];
unsigned short priority;
unsigned short weight;
unsigned short port;
};
struct dns_srv_records {
char domain[DNS_MAX_CNAME_LEN];
struct hlist_node node;
struct list_head list;
};
struct dns_srv_record_table {
DECLARE_HASHTABLE(srv, 4);
};
extern struct dns_srv_record_table dns_conf_srv_record_table;
extern struct dns_dns64 dns_conf_dns_dns64;
extern struct dns_bind_ip dns_conf_bind_ip[DNS_MAX_BIND_IP];
@@ -520,6 +593,9 @@ extern int dns_save_fail_packet;
extern char dns_save_fail_packet_dir[DNS_MAX_PATH];
extern char dns_resolv_file[DNS_MAX_PATH];
extern int dns_no_pidfile;
extern int dns_no_daemon;
void dns_server_load_exit(void);
int dns_server_load_conf(const char *file);
@@ -528,6 +604,8 @@ int dns_server_check_update_hosts(void);
struct dns_proxy_names *dns_server_get_proxy_nams(const char *proxyname);
struct dns_srv_records *dns_server_get_srv_record(const char *domain);
extern int config_additional_file(void *data, int argc, char *argv[]);
const char *dns_conf_get_cache_dir(void);

File diff suppressed because it is too large Load Diff

View File

@@ -49,9 +49,21 @@ void dns_server_stop(void);
void dns_server_exit(void);
#define MAX_IP_NUM 16
struct dns_result {
const char *domain;
dns_rtcode_t rtcode;
dns_type_t addr_type;
const char *ip;
const unsigned char *ip_addr[MAX_IP_NUM];
int ip_num;
int has_soa;
unsigned int ping_time;
};
/* query result notify function */
typedef int (*dns_result_callback)(const char *domain, dns_rtcode_t rtcode, dns_type_t addr_type, char *ip,
unsigned int ping_time, void *user_ptr);
typedef int (*dns_result_callback)(const struct dns_result *result, void *user_ptr);
/* query domain */
int dns_server_query(const char *domain, int qtype, struct dns_server_query_option *server_query_option,

View File

@@ -183,6 +183,7 @@ struct fast_ping_struct {
int fake_ip_num;
};
static int is_fast_ping_init;
static struct fast_ping_struct ping;
static atomic_t ping_sid = ATOMIC_INIT(0);
static int bool_print_log = 1;
@@ -726,7 +727,7 @@ static int _fast_ping_sendping_v6(struct ping_host_struct *ping_host)
len = sendto(ping.fd_icmp6, &ping_host->packet, sizeof(struct fast_ping_packet), 0, &ping_host->addr,
ping_host->addr_len);
if (len < 0 || len != sizeof(struct fast_ping_packet)) {
if (len != sizeof(struct fast_ping_packet)) {
int err = errno;
if (errno == ENETUNREACH || errno == EINVAL || errno == EADDRNOTAVAIL || errno == EHOSTUNREACH) {
goto errout;
@@ -806,7 +807,7 @@ static int _fast_ping_sendping_v4(struct ping_host_struct *ping_host)
icmp->icmp_cksum = _fast_ping_checksum((void *)packet, sizeof(struct fast_ping_packet));
len = sendto(ping.fd_icmp, packet, sizeof(struct fast_ping_packet), 0, &ping_host->addr, ping_host->addr_len);
if (len < 0 || len != sizeof(struct fast_ping_packet)) {
if (len != sizeof(struct fast_ping_packet)) {
int err = errno;
if (errno == ENETUNREACH || errno == EINVAL || errno == EADDRNOTAVAIL || errno == EPERM || errno == EACCES) {
goto errout;
@@ -858,7 +859,7 @@ static int _fast_ping_sendping_udp(struct ping_host_struct *ping_host)
gettimeofday(&ping_host->last, NULL);
len = sendto(fd, &dns_head, sizeof(dns_head), 0, &ping_host->addr, ping_host->addr_len);
if (len < 0 || len != sizeof(dns_head)) {
if (len != sizeof(dns_head)) {
int err = errno;
if (errno == ENETUNREACH || errno == EINVAL || errno == EADDRNOTAVAIL || errno == EPERM || errno == EACCES) {
goto errout;
@@ -2187,6 +2188,10 @@ int fast_ping_init(void)
int ret = 0;
bool_print_log = 1;
if (is_fast_ping_init == 1) {
return -1;
}
if (ping.epoll_fd > 0) {
return -1;
}
@@ -2232,6 +2237,7 @@ int fast_ping_init(void)
goto errout;
}
is_fast_ping_init = 1;
return 0;
errout:
if (ping.notify_tid) {
@@ -2294,6 +2300,10 @@ static void _fast_ping_close_fds(void)
void fast_ping_exit(void)
{
if (is_fast_ping_init == 0) {
return;
}
if (ping.notify_tid) {
void *retval = NULL;
atomic_set(&ping.run, 0);
@@ -2324,4 +2334,6 @@ void fast_ping_exit(void)
pthread_mutex_destroy(&ping.notify_lock);
pthread_mutex_destroy(&ping.lock);
pthread_mutex_destroy(&ping.map_lock);
is_fast_ping_init = 0;
}

View File

@@ -111,10 +111,10 @@ const char *http_head_get_fields_value(struct http_head *http_head, const char *
uint32_t key;
struct http_head_fields *filed;
key = hash_string(name);
key = hash_string_case(name);
hash_for_each_possible(http_head->field_map, filed, node, key)
{
if (strncmp(filed->name, name, 128) == 0) {
if (strncasecmp(filed->name, name, 128) == 0) {
return filed->value;
}
}
@@ -205,7 +205,7 @@ static int _http_head_add_fields(struct http_head *http_head, char *name, char *
fields->value = value;
list_add_tail(&fields->list, &http_head->field_head.list);
key = hash_string(name);
key = hash_string_case(name);
hash_add(http_head->field_map, &fields->node, key);
return 0;
@@ -384,6 +384,10 @@ int http_head_parse(struct http_head *http_head, const char *data, int data_len)
if (http_head->head_ok == 0) {
for (i = 0; i < data_len; i++, data++) {
*(buff_end + i) = *data;
if (isprint(*data) == 0 && isspace(*data) == 0) {
return -2;
}
if (*data == '\n') {
if (http_head->buff_len + i < 2) {
continue;

View File

@@ -122,9 +122,9 @@ struct config_enum {
.data = value, .min = min_value, .max = max_value \
} \
}
#define CONF_SSIZE(key, value, min_value, max_value) \
#define CONF_SSIZE(key, value, min_value, max_value) \
{ \
key, conf_ssize, &(struct config_item_ssize) \
key, conf_ssize, &(struct config_item_ssize) \
{ \
.data = value, .min = min_value, .max = max_value \
} \
@@ -189,6 +189,8 @@ int load_conf(const char *file, struct config_item items[], conf_error_handler h
void load_exit(void);
int conf_get_current_lineno(void);
const char *conf_get_conf_file(void);
const char *conf_get_conf_fullpath(const char *path, char *fullpath, size_t path_len);

View File

@@ -21,6 +21,7 @@
#include "bitmap.h"
#include "jhash.h"
#include <ctype.h>
/* Fast hashing routine for ints, longs and pointers.
(C) 2002 Nadia Yvette Chambers, IBM */
@@ -223,11 +224,28 @@ static inline uint32_t hash_string_initval(const char *s, uint32_t initval)
return h;
}
static inline uint32_t hash_string_case_initval(const char *s, uint32_t initval)
{
uint32_t h = initval;
while (*s) {
h = h * 31 + tolower(*s);
s++;
}
return h;
}
static inline uint32_t hash_string(const char *s)
{
return hash_string_initval(s, 0);
}
static inline uint32_t hash_string_case(const char *s)
{
return hash_string_case_initval(s, 0);
}
static inline uint32_t hash_string_array(const char **a)
{
uint32_t h = 0;

View File

@@ -29,6 +29,11 @@
#define __must_be_array(a) BUILD_BUG_ON_ZERO(__same_type((a), &(a)[0]))
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + __must_be_array(arr))
struct hash_table {
struct hlist_head *table;
unsigned int size;
};
#define DEFINE_HASHTABLE(name, bits) \
struct hlist_head name[1 << (bits)] = \
{ [0 ... ((1 << (bits)) - 1)] = HLIST_HEAD_INIT }
@@ -38,6 +43,8 @@
#define HASH_SIZE(name) (ARRAY_SIZE(name))
#define HASH_BITS(name) ilog2(HASH_SIZE(name))
#define HASH_TABLE_SIZE(name) (1 << ((name).size))
#define HASH_TABLE_BITS(name) ((name).size)
/* Use hash_32 when possible to allow for fast 32bit hashing in 64bit kernels. */
#define hash_min(val, bits) \
@@ -63,6 +70,16 @@ static inline void __hash_init(struct hlist_head *ht, unsigned int sz)
*/
#define hash_init(hashtable) __hash_init(hashtable, HASH_SIZE(hashtable))
#define hash_table_init(hashtable, bits, malloc_func) \
(hashtable).size = bits; \
(hashtable).table = malloc_func(sizeof(struct hlist_head) * HASH_TABLE_SIZE((hashtable))); \
__hash_init((hashtable).table, HASH_TABLE_SIZE((hashtable)))
#define hash_table_free(hashtable, free_func) \
free_func((hashtable).table); \
(hashtable).table = NULL; \
(hashtable).size = 0;
/**
* hash_add - add an object to a hashtable
* @hashtable: hashtable to add to
@@ -72,6 +89,9 @@ static inline void __hash_init(struct hlist_head *ht, unsigned int sz)
#define hash_add(hashtable, node, key) \
hlist_add_head(node, &hashtable[hash_min(key, HASH_BITS(hashtable))])
#define hash_table_add(hashtable, node, key) \
hlist_add_head(node, &(hashtable).table[hash_min(key, HASH_TABLE_BITS(hashtable))])
/**
* hash_hashed - check whether an object is in any hashtable
* @node: the &struct hlist_node of the object to be checked
@@ -101,6 +121,8 @@ static inline bool __hash_empty(struct hlist_head *ht, unsigned int sz)
*/
#define hash_empty(hashtable) __hash_empty(hashtable, HASH_SIZE(hashtable))
#define hash_table_empty(hashtable) __hash_empty((hashtable).table, HASH_TABLE_SIZE(hashtable))
/**
* hash_del - remove an object from a hashtable
* @node: &struct hlist_node of the object to remove
@@ -122,6 +144,11 @@ static inline void hash_del(struct hlist_node *node)
(bkt)++)\
hlist_for_each_entry(obj, &name[bkt], member)
#define hash_table_for_each(name, bkt, obj, member) \
for ((bkt) = 0, obj = NULL; obj == NULL && (bkt) < (HASH_TABLE_SIZE(name));\
(bkt)++)\
hlist_for_each_entry(obj, &((name).table)[bkt], member)
/**
* hash_for_each_safe - iterate over a hashtable safe against removal of
* hash entry
@@ -136,6 +163,11 @@ static inline void hash_del(struct hlist_node *node)
(bkt)++)\
hlist_for_each_entry_safe(obj, tmp, &name[bkt], member)
#define hash_table_for_each_safe(name, bkt, tmp, obj, member) \
for ((bkt) = 0, obj = NULL; obj == NULL && (bkt) < (HASH_TABLE_SIZE(name));\
(bkt)++)\
hlist_for_each_entry_safe(obj, tmp, &((name).table)[bkt], member)
/**
* hash_for_each_possible - iterate over all possible objects hashing to the
* same bucket
@@ -147,6 +179,9 @@ static inline void hash_del(struct hlist_node *node)
#define hash_for_each_possible(name, obj, member, key) \
hlist_for_each_entry(obj, &name[hash_min(key, HASH_BITS(name))], member)
#define hash_table_for_each_possible(name, obj, member, key) \
hlist_for_each_entry(obj, &((name).table)[hash_min(key, HASH_TABLE_BITS(name))], member)
/**
* hash_for_each_possible_safe - iterate over all possible objects hashing to the
* same bucket safe against removals
@@ -160,4 +195,8 @@ static inline void hash_del(struct hlist_node *node)
hlist_for_each_entry_safe(obj, tmp,\
&name[hash_min(key, HASH_BITS(name))], member)
#define hash_table_for_each_possible_safe(name, obj, tmp, member, key) \
hlist_for_each_entry_safe(obj, tmp,\
&((name).table)[hash_min(key, HASH_TABLE_BITS(name))], member)
#endif

32
src/include/idna.h Normal file
View File

@@ -0,0 +1,32 @@
/*************************************************************************
*
* Copyright (C) 2018-2023 Ruilin Peng (Nick) <pymumu@gmail.com>.
*
* smartdns is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* smartdns is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _SMARTDNS_IDNA_H
#define _SMARTDNS_IDNA_H
#ifdef __cplusplus
extern "C" {
#endif
int utf8_to_punycode(const char *src, int src_len, char *dst, int dst_len);
#ifdef __cplusplus
}
#endif
#endif // !_SMARTDNS_IDNA_H

57
src/include/timer_wheel.h Normal file
View File

@@ -0,0 +1,57 @@
/*************************************************************************
*
* Copyright (C) 2018-2023 Ruilin Peng (Nick) <pymumu@gmail.com>.
*
* smartdns is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* smartdns is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __TIMER_WHEEL_H
#define __TIMER_WHEEL_H
#include "list.h"
#ifdef __cplusplus
extern "C" {
#endif
struct tw_base;
struct tw_timer_list;
typedef void (*tw_func)(struct tw_base *, struct tw_timer_list *, void *, unsigned long);
typedef void (*tw_del_func)(struct tw_base *, struct tw_timer_list *, void *);
struct tw_timer_list {
void *data;
unsigned long expires;
tw_func function;
tw_del_func del_function;
struct list_head entry;
};
struct tw_base *tw_init_timers(void);
int tw_cleanup_timers(struct tw_base *);
void tw_add_timer(struct tw_base *, struct tw_timer_list *);
int tw_del_timer(struct tw_base *, struct tw_timer_list *);
int tw_mod_timer_pending(struct tw_base *, struct tw_timer_list *, unsigned long);
int tw_mod_timer(struct tw_base *, struct tw_timer_list *, unsigned long);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -24,14 +24,21 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
static const char *current_conf_file = NULL;
static int current_conf_lineno = 0;
const char *conf_get_conf_file(void)
{
return current_conf_file;
}
int conf_get_current_lineno(void)
{
return current_conf_lineno;
}
static char *get_dir_name(char *path)
{
if (strstr(path, "/") == NULL) {
@@ -347,6 +354,7 @@ static int load_conf_file(const char *file, struct config_item *items, conf_erro
char value[MAX_LINE_LEN];
int filed_num = 0;
int i = 0;
int last_item_index = -1;
int argc = 0;
char *argv[1024];
int ret = 0;
@@ -354,6 +362,9 @@ static int load_conf_file(const char *file, struct config_item *items, conf_erro
int line_no = 0;
int line_len = 0;
int read_len = 0;
int is_last_line_wrap = 0;
int current_line_wrap = 0;
int is_func_found = 0;
const char *last_file = NULL;
if (handler == NULL) {
@@ -362,21 +373,52 @@ static int load_conf_file(const char *file, struct config_item *items, conf_erro
fp = fopen(file, "r");
if (fp == NULL) {
fprintf(stderr, "open config file '%s' failed, %s\n", file, strerror(errno));
return -1;
}
line_no = 0;
while (fgets(line + line_len, MAX_LINE_LEN - line_len, fp)) {
current_line_wrap = 0;
line_no++;
read_len = strnlen(line + line_len, sizeof(line));
if (read_len >= 2 && *(line + line_len + read_len - 2) == '\\') {
line_len += read_len - 2;
line[line_len] = '\0';
continue;
read_len -= 1;
current_line_wrap = 1;
}
line_len = 0;
filed_num = sscanf(line, "%63s %8192[^\r\n]s", key, value);
/* comment in wrap line, skip */
if (is_last_line_wrap && read_len > 0) {
if (*(line + line_len) == '#') {
continue;
}
}
/* trim prefix spaces in wrap line */
if ((current_line_wrap == 1 || is_last_line_wrap == 1) && read_len > 0) {
is_last_line_wrap = current_line_wrap;
read_len -= 1;
for (i = 0; i < read_len; i++) {
char *ptr = line + line_len + i;
if (*ptr == ' ' || *ptr == '\t') {
continue;
}
memmove(line + line_len, ptr, read_len - i + 1);
line_len += read_len - i;
break;
}
line[line_len] = '\0';
if (current_line_wrap) {
continue;
}
}
line_len = 0;
is_last_line_wrap = 0;
filed_num = sscanf(line, "%63s %8191[^\r\n]s", key, value);
if (filed_num <= 0) {
continue;
}
@@ -392,13 +434,22 @@ static int load_conf_file(const char *file, struct config_item *items, conf_erro
goto errout;
}
for (i = 0;; i++) {
is_func_found = 0;
for (i = last_item_index;; i++) {
if (i < 0) {
continue;
}
if (items[i].item == NULL) {
handler(file, line_no, CONF_RET_NOENT);
break;
}
if (strncmp(items[i].item, key, MAX_KEY_LEN) != 0) {
if (last_item_index >= 0) {
i = -1;
last_item_index = -1;
}
continue;
}
@@ -410,6 +461,7 @@ static int load_conf_file(const char *file, struct config_item *items, conf_erro
/* call item function */
last_file = current_conf_file;
current_conf_file = file;
current_conf_lineno = line_no;
call_ret = items[i].item_func(items[i].item, items[i].data, argc, argv);
ret = handler(file, line_no, call_ret);
if (ret != 0) {
@@ -422,8 +474,14 @@ static int load_conf_file(const char *file, struct config_item *items, conf_erro
current_conf_file = last_file;
}
last_item_index = i;
is_func_found = 1;
break;
}
if (is_func_found == 0) {
handler(file, line_no, CONF_RET_NOENT);
}
}
fclose(fp);

339
src/lib/idna.c Normal file
View File

@@ -0,0 +1,339 @@
/*************************************************************************
*
* Copyright (C) 2018-2023 Ruilin Peng (Nick) <pymumu@gmail.com>.
*
* smartdns is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* smartdns is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define _GNU_SOURCE
#include "idna.h"
#include <limits.h>
static unsigned _utf8_decode_slow(const char **p, const char *pe, unsigned a)
{
unsigned b;
unsigned c;
unsigned d;
unsigned min;
if (a > 0xF7) {
return -1;
}
switch (pe - *p) {
default:
if (a > 0xEF) {
min = 0x10000;
a = a & 7;
b = (unsigned char)*(*p)++;
c = (unsigned char)*(*p)++;
d = (unsigned char)*(*p)++;
break;
}
case 2:
if (a > 0xDF) {
min = 0x800;
b = 0x80 | (a & 15);
c = (unsigned char)*(*p)++;
d = (unsigned char)*(*p)++;
a = 0;
break;
}
case 1:
if (a > 0xBF) {
min = 0x80;
b = 0x80;
c = 0x80 | (a & 31);
d = (unsigned char)*(*p)++;
a = 0;
break;
}
case 0:
return -1;
}
if (0x80 != (0xC0 & (b ^ c ^ d))) {
return -1;
}
b &= 63;
c &= 63;
d &= 63;
a = (a << 18) | (b << 12) | (c << 6) | d;
if (a < min) {
return -1;
}
if (a > 0x10FFFF) {
return -1;
}
if (a >= 0xD800 && a <= 0xDFFF) {
return -1;
}
return a;
}
static unsigned _utf8_decode(const char **p, const char *pe)
{
unsigned a;
a = (unsigned char)*(*p)++;
if (a < 128) {
return a;
}
return _utf8_decode_slow(p, pe, a);
}
static int _utf8_to_punycode_label(const char *s, const char *se, char **d, char *de)
{
static const char alphabet[] = "abcdefghijklmnopqrstuvwxyz0123456789";
const char *ss;
unsigned c;
unsigned h;
unsigned k;
unsigned n;
unsigned m;
unsigned q;
unsigned t;
unsigned x;
unsigned y;
unsigned bias;
unsigned delta;
unsigned todo;
int first;
h = 0;
ss = s;
todo = 0;
while (s < se) {
c = _utf8_decode(&s, se);
if (c == UINT_MAX) {
return -1;
}
if (c < 128) {
h++;
} else {
todo++;
}
}
if (todo > 0) {
if (*d < de) {
*(*d)++ = 'x';
}
if (*d < de) {
*(*d)++ = 'n';
}
if (*d < de) {
*(*d)++ = '-';
}
if (*d < de) {
*(*d)++ = '-';
}
}
x = 0;
s = ss;
while (s < se) {
c = _utf8_decode(&s, se);
if (c > 127) {
continue;
}
if (*d < de) {
*(*d)++ = c;
}
if (++x == h) {
break;
}
}
if (todo == 0) {
return h;
}
if (h > 0) {
if (*d < de) {
*(*d)++ = '-';
}
}
n = 128;
bias = 72;
delta = 0;
first = 1;
while (todo > 0) {
m = -1;
s = ss;
while (s < se) {
c = _utf8_decode(&s, se);
if (c >= n) {
if (c < m) {
m = c;
}
}
}
x = m - n;
y = h + 1;
if (x > ~delta / y) {
return -1;
}
delta += x * y;
n = m;
s = ss;
while (s < se) {
c = _utf8_decode(&s, se);
if (c < n) {
if (++delta == 0) {
return -1;
}
}
if (c != n) {
continue;
}
for (k = 36, q = delta;; k += 36) {
t = 1;
if (k > bias) {
t = k - bias;
}
if (t > 26) {
t = 26;
}
if (q < t) {
break;
}
x = q - t;
y = 36 - t;
q = x / y;
t = t + x % y;
if (*d < de) {
*(*d)++ = alphabet[t];
}
}
if (*d < de) {
*(*d)++ = alphabet[q];
}
delta /= 2;
if (first) {
delta /= 350;
first = 0;
}
h++;
delta += delta / h;
for (bias = 0; delta > 35 * 26 / 2; bias += 36) {
delta /= 35;
}
bias += 36 * delta / (delta + 38);
delta = 0;
todo--;
}
delta++;
n++;
}
return 0;
}
int utf8_to_punycode(const char *src, int src_len, char *dst, int dst_len)
{
const char *si;
const char *se;
const char *st;
unsigned c;
char *ds;
char *de;
int rc;
ds = dst;
si = src;
se = src + src_len;
de = dst + dst_len;
while (si < se) {
st = si;
c = _utf8_decode(&si, se);
if (c == UINT_MAX) {
return -1;
}
if (c != '.') {
if (c != 0x3002) {
if (c != 0xFF0E) {
if (c != 0xFF61) {
continue;
}
}
}
}
rc = _utf8_to_punycode_label(src, st, &dst, de);
if (rc < 0) {
return rc;
}
if (dst < de) {
*dst++ = '.';
}
src = si;
}
if (src < se) {
rc = _utf8_to_punycode_label(src, se, &dst, de);
if (rc < 0) {
return rc;
}
}
if (dst < de) {
*dst++ = '\0';
}
return dst - ds;
}

394
src/lib/timer_wheel.c Normal file
View File

@@ -0,0 +1,394 @@
/*************************************************************************
*
* Copyright (C) 2018-2023 Ruilin Peng (Nick) <pymumu@gmail.com>.
*
* smartdns is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* smartdns is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "bitops.h"
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include "timer_wheel.h"
#define TVR_BITS 10
#define TVN_BITS 6
#define TVR_SIZE (1 << TVR_BITS)
#define TVN_SIZE (1 << TVN_BITS)
#define TVR_MASK (TVR_SIZE - 1)
#define TVN_MASK (TVN_SIZE - 1)
#define INDEX(N) ((base->jiffies >> (TVR_BITS + N * TVN_BITS)) & TVN_MASK)
#define MAX_TVAL ((unsigned long)((1ULL << (TVR_BITS + 4 * TVN_BITS)) - 1))
struct tvec {
struct list_head vec[TVN_SIZE];
};
struct tvec_root {
struct list_head vec[TVR_SIZE];
};
struct tw_base {
pthread_spinlock_t lock;
pthread_t runner;
unsigned long jiffies;
struct tvec_root tv1;
struct tvec tv2;
struct tvec tv3;
struct tvec tv4;
struct tvec tv5;
};
static inline void _tw_add_timer(struct tw_base *base, struct tw_timer_list *timer)
{
int i;
unsigned long idx;
unsigned long expires;
struct list_head *vec;
expires = timer->expires;
idx = expires - base->jiffies;
if (idx < TVR_SIZE) {
i = expires & TVR_MASK;
vec = base->tv1.vec + i;
} else if (idx < 1 << (TVR_BITS + TVN_BITS)) {
i = (expires >> TVR_BITS) & TVN_MASK;
vec = base->tv2.vec + i;
} else if (idx < 1 << (TVR_BITS + 2 * TVN_BITS)) {
i = (expires >> (TVR_BITS + TVN_BITS)) & TVN_MASK;
vec = base->tv3.vec + i;
} else if (idx < 1 << (TVR_BITS + 3 * TVN_BITS)) {
i = (expires >> (TVR_BITS + 2 * TVN_BITS)) & TVN_MASK;
vec = base->tv4.vec + i;
} else if ((signed long)idx < 0) {
vec = base->tv1.vec + (base->jiffies & TVR_MASK);
} else {
if (idx > MAX_TVAL) {
idx = MAX_TVAL;
expires = idx + base->jiffies;
}
i = (expires >> (TVR_BITS + 3 * TVN_BITS)) & TVN_MASK;
vec = base->tv5.vec + i;
}
list_add_tail(&timer->entry, vec);
}
static inline void _tw_detach_timer(struct tw_timer_list *timer)
{
struct list_head *entry = &timer->entry;
list_del(entry);
entry->next = NULL;
}
static inline int _tw_cascade(struct tw_base *base, struct tvec *tv, int index)
{
struct tw_timer_list *timer, *tmp;
struct list_head tv_list;
list_replace_init(tv->vec + index, &tv_list);
list_for_each_entry_safe(timer, tmp, &tv_list, entry)
{
_tw_add_timer(base, timer);
}
return index;
}
static inline int timer_pending(struct tw_timer_list *timer)
{
struct list_head *entry = &timer->entry;
return (entry->next != NULL);
}
static inline int __detach_if_pending(struct tw_timer_list *timer)
{
if (!timer_pending(timer)) {
return 0;
}
_tw_detach_timer(timer);
return 1;
}
static inline int __mod_timer(struct tw_base *base, struct tw_timer_list *timer, int pending_only)
{
int ret = 0;
ret = __detach_if_pending(timer);
if (!ret && pending_only) {
goto done;
}
ret = 1;
_tw_add_timer(base, timer);
done:
return ret;
}
void tw_add_timer(struct tw_base *base, struct tw_timer_list *timer)
{
if (timer->function == NULL) {
return;
}
pthread_spin_lock(&base->lock);
{
timer->expires += base->jiffies - 1;
_tw_add_timer(base, timer);
}
pthread_spin_unlock(&base->lock);
}
int tw_del_timer(struct tw_base *base, struct tw_timer_list *timer)
{
int ret = 0;
pthread_spin_lock(&base->lock);
{
if (timer_pending(timer)) {
ret = 1;
_tw_detach_timer(timer);
}
}
pthread_spin_unlock(&base->lock);
if (ret == 1 && timer->del_function) {
timer->del_function(base, timer, timer->data);
}
return ret;
}
int tw_mod_timer_pending(struct tw_base *base, struct tw_timer_list *timer, unsigned long expires)
{
int ret = 1;
pthread_spin_lock(&base->lock);
{
timer->expires = expires + base->jiffies - 1;
ret = __mod_timer(base, timer, 1);
}
pthread_spin_unlock(&base->lock);
return ret;
}
int tw_mod_timer(struct tw_base *base, struct tw_timer_list *timer, unsigned long expires)
{
int ret = 1;
pthread_spin_lock(&base->lock);
{
if (timer_pending(timer) && timer->expires == expires) {
goto unblock;
}
timer->expires = expires + base->jiffies - 1;
ret = __mod_timer(base, timer, 0);
}
unblock:
pthread_spin_unlock(&base->lock);
return ret;
}
int tw_cleanup_timers(struct tw_base *base)
{
int ret = 0;
void *res = NULL;
ret = pthread_cancel(base->runner);
if (ret != 0) {
goto errout;
}
ret = pthread_join(base->runner, &res);
if (ret != 0) {
goto errout;
}
if (res != PTHREAD_CANCELED) {
goto errout;
}
ret = pthread_spin_destroy(&base->lock);
if (ret != 0) {
goto errout;
}
free(base);
return 0;
errout:
return -1;
}
static inline void run_timers(struct tw_base *base)
{
unsigned long index, call_time;
struct tw_timer_list *timer;
struct list_head work_list;
struct list_head *head = &work_list;
pthread_spin_lock(&base->lock);
{
index = base->jiffies & TVR_MASK;
if (!index && (!_tw_cascade(base, &base->tv2, INDEX(0))) && (!_tw_cascade(base, &base->tv3, INDEX(1))) &&
(!_tw_cascade(base, &base->tv4, INDEX(2))))
_tw_cascade(base, &base->tv5, INDEX(3));
call_time = base->jiffies++;
list_replace_init(base->tv1.vec + index, head);
while (!list_empty(head)) {
tw_func fn;
void *data;
timer = list_first_entry(head, struct tw_timer_list, entry);
fn = timer->function;
data = timer->data;
_tw_detach_timer(timer);
pthread_spin_unlock(&base->lock);
{
fn(base, timer, data, call_time);
}
pthread_spin_lock(&base->lock);
if ((timer_pending(timer) == 0 && timer->del_function)) {
pthread_spin_unlock(&base->lock);
timer->del_function(base, timer, timer->data);
pthread_spin_lock(&base->lock);
}
}
}
pthread_spin_unlock(&base->lock);
}
static unsigned long _tw_tick_count(void)
{
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return (ts.tv_sec * 1000 + ts.tv_nsec / 1000000);
}
static void *timer_work(void *arg)
{
struct tw_base *base = arg;
int sleep = 1000;
int sleep_time = 0;
unsigned long now = {0};
unsigned long last = {0};
unsigned long expect_time = 0;
sleep_time = sleep;
now = _tw_tick_count() - sleep;
last = now;
expect_time = now + sleep;
while (1) {
run_timers(base);
now = _tw_tick_count();
if (sleep_time > 0) {
sleep_time -= now - last;
if (sleep_time <= 0) {
sleep_time = 0;
}
int cnt = sleep_time / sleep;
expect_time -= cnt * sleep;
sleep_time -= cnt * sleep;
}
if (now >= expect_time) {
sleep_time = sleep - (now - expect_time);
if (sleep_time < 0) {
sleep_time = 0;
expect_time = now;
}
expect_time += sleep;
}
last = now;
usleep(sleep_time * 1000);
}
return NULL;
}
struct tw_base *tw_init_timers(void)
{
int j = 0;
int ret = 0;
struct timeval tv = {
0,
};
struct tw_base *base = NULL;
base = malloc(sizeof(*base));
if (!base) {
goto errout;
}
ret = pthread_spin_init(&base->lock, 0);
if (ret != 0) {
goto errout2;
}
for (j = 0; j < TVN_SIZE; j++) {
INIT_LIST_HEAD(base->tv5.vec + j);
INIT_LIST_HEAD(base->tv4.vec + j);
INIT_LIST_HEAD(base->tv3.vec + j);
INIT_LIST_HEAD(base->tv2.vec + j);
}
for (j = 0; j < TVR_SIZE; j++) {
INIT_LIST_HEAD(base->tv1.vec + j);
}
ret = gettimeofday(&tv, 0);
if (ret < 0) {
goto errout1;
}
base->jiffies = tv.tv_sec;
ret = pthread_create(&base->runner, NULL, timer_work, base);
if (ret != 0) {
goto errout1;
}
return base;
errout1:
(void)pthread_spin_destroy(&base->lock);
errout2:
free(base);
errout:
return NULL;
}

View File

@@ -95,8 +95,9 @@ struct proxy_struct {
};
static struct proxy_struct proxy;
static int is_proxy_init;
const char *proxy_socks5_status_code[] = {
static const char *proxy_socks5_status_code[] = {
"success",
"general SOCKS server failure",
"connection not allowed by ruleset",
@@ -234,7 +235,7 @@ int proxy_remove(const char *proxy_name)
static void _proxy_remove_all(void)
{
struct proxy_server_info *server_info;
struct proxy_server_info *server_info = NULL;
struct hlist_node *tmp = NULL;
unsigned int i = 0;
@@ -957,7 +958,7 @@ int proxy_conn_recvfrom(struct proxy_conn *proxy_conn, void *buf, size_t len, in
return -1;
}
ret = recvfrom(proxy_conn->udp_fd, buffer, sizeof(buffer), MSG_NOSIGNAL, NULL, 0);
ret = recvfrom(proxy_conn->udp_fd, buffer, sizeof(buffer), MSG_NOSIGNAL, NULL, NULL);
if (ret <= 0) {
return -1;
}
@@ -1043,15 +1044,26 @@ int proxy_conn_is_udp(struct proxy_conn *proxy_conn)
return proxy_conn->is_udp;
}
int proxy_init()
int proxy_init(void)
{
if (is_proxy_init == 1) {
return -1;
}
memset(&proxy, 0, sizeof(proxy));
hash_init(proxy.proxy_server);
is_proxy_init = 1;
return 0;
}
int proxy_exit()
void proxy_exit(void)
{
if (is_proxy_init == 0) {
return;
}
_proxy_remove_all();
return 0;
is_proxy_init = 0;
return ;
}

View File

@@ -56,7 +56,7 @@ struct proxy_conn;
int proxy_init(void);
int proxy_exit(void);
void proxy_exit(void);
int proxy_add(const char *proxy_name, struct proxy_info *info);

View File

@@ -20,6 +20,7 @@
#include "smartdns.h"
#include "art.h"
#include "atomic.h"
#include "dns_cache.h"
#include "dns_client.h"
#include "dns_conf.h"
#include "dns_server.h"
@@ -27,10 +28,12 @@
#include "hashtable.h"
#include "list.h"
#include "rbtree.h"
#include "timer.h"
#include "tlog.h"
#include "util.h"
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <libgen.h>
#include <linux/capability.h>
#include <openssl/err.h>
@@ -44,12 +47,21 @@
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <syslog.h>
#include <ucontext.h>
#define MAX_KEY_LEN 64
#define SMARTDNS_PID_FILE "/run/smartdns.pid"
#define SMARTDNS_LEGACY_PID_FILE "/var/run/smartdns.pid"
#define TMP_BUFF_LEN_32 32
#define SMARTDNS_CRASH_CODE 254
typedef enum {
SMARTDNS_RUN_MONITOR_OK = 0,
SMARTDNS_RUN_MONITOR_ERROR = 1,
SMARTDNS_RUN_MONITOR_EXIT = 2,
} smartdns_run_monitor_ret;
static int verbose_screen;
@@ -149,12 +161,13 @@ static void _help(void)
" -f run foreground.\n"
" -c [conf] config file.\n"
" -p [pid] pid file path, '-' means don't create pid file.\n"
" -R restart smartdns when crash.\n"
" -S ignore segment fault signal.\n"
" -x verbose screen.\n"
" -v display version.\n"
" -h show this help message.\n"
"Online help: http://pymumu.github.io/smartdns\n"
"Online help: https://pymumu.github.io/smartdns\n"
"Copyright (C) Nick Peng <pymumu@gmail.com>\n"
;
/* clang-format on */
@@ -255,13 +268,13 @@ static int _smartdns_prepare_server_flags(struct client_dns_server_flags *flags,
safe_strncpy(flag_http->tls_host_verify, server->tls_host_verify, sizeof(flag_http->tls_host_verify));
flag_http->skip_check_cert = server->skip_check_cert;
} break;
case DNS_SERVER_QUIC:
case DNS_SERVER_TLS: {
struct client_dns_server_flag_tls *flag_tls = &flags->tls;
flag_tls->spi_len = dns_client_spki_decode(server->spki, (unsigned char *)flag_tls->spki);
safe_strncpy(flag_tls->hostname, server->hostname, sizeof(flag_tls->hostname));
safe_strncpy(flag_tls->tls_host_verify, server->tls_host_verify, sizeof(flag_tls->tls_host_verify));
flag_tls->skip_check_cert = server->skip_check_cert;
} break;
case DNS_SERVER_TCP:
break;
@@ -459,14 +472,31 @@ static int _smartdns_init(void)
int ret = 0;
const char *logfile = _smartdns_log_path();
int i = 0;
char logdir[PATH_MAX] = {0};
int logbuffersize = 0;
int enable_log_screen = 0;
ret = tlog_init(logfile, dns_conf_log_size, dns_conf_log_num, 0, 0);
if (get_system_mem_size() > 1024 * 1024 * 1024) {
logbuffersize = 1024 * 1024;
}
safe_strncpy(logdir, _smartdns_log_path(), PATH_MAX);
if (verbose_screen != 0 || dns_conf_log_console != 0 || access(dir_name(logdir), W_OK) != 0) {
enable_log_screen = 1;
}
unsigned int tlog_flag = TLOG_NONBLOCK;
if (isatty(1) && enable_log_screen == 1) {
tlog_flag |= TLOG_SCREEN_COLOR;
}
ret = tlog_init(logfile, dns_conf_log_size, dns_conf_log_num, logbuffersize, tlog_flag);
if (ret != 0) {
tlog(TLOG_ERROR, "start tlog failed.\n");
goto errout;
}
if (verbose_screen != 0 || dns_conf_log_console != 0) {
if (enable_log_screen) {
tlog_setlogscreen(1);
}
@@ -478,6 +508,11 @@ static int _smartdns_init(void)
tlog(TLOG_NOTICE, "smartdns starting...(Copyright (C) Nick Peng <pymumu@gmail.com>, build: %s %s)", __DATE__,
__TIME__);
if (dns_timer_init() != 0) {
tlog(TLOG_ERROR, "init timer failed.");
goto errout;
}
if (_smartdns_init_ssl() != 0) {
tlog(TLOG_ERROR, "init ssl failed.");
goto errout;
@@ -559,12 +594,12 @@ static int _smartdns_run(void)
static void _smartdns_exit(void)
{
tlog(TLOG_INFO, "smartdns exit...");
dns_client_exit();
proxy_exit();
fast_ping_exit();
dns_server_exit();
_smartdns_destroy_ssl();
dns_timer_destroy();
tlog_exit();
dns_server_load_exit();
}
@@ -605,7 +640,7 @@ static void _sig_error_exit(int signo, siginfo_t *siginfo, void *ct)
__DATE__, __TIME__, arch);
print_stack();
sleep(1);
_exit(0);
_exit(SMARTDNS_CRASH_CODE);
}
static int sig_list[] = {SIGSEGV, SIGABRT, SIGBUS, SIGILL, SIGFPE};
@@ -711,6 +746,169 @@ static int _smartdns_init_pre(void)
return 0;
}
static void _smartdns_early_log(struct tlog_loginfo *loginfo, const char *format, va_list ap)
{
char log_buf[TLOG_MAX_LINE_LEN];
int sys_log_level = LOG_INFO;
int log_buf_maxlen = 0;
if (loginfo->level < TLOG_WARN) {
return;
}
log_buf_maxlen = sizeof(log_buf) - 2;
log_buf[log_buf_maxlen] = '\0';
int len = vsnprintf(log_buf, log_buf_maxlen, format, ap);
if (len <= 0) {
return;
} else if (len >= log_buf_maxlen) {
log_buf[log_buf_maxlen - 2] = '.';
log_buf[log_buf_maxlen - 3] = '.';
log_buf[log_buf_maxlen - 4] = '.';
len = log_buf_maxlen - 1;
}
if (log_buf[len - 1] != '\n') {
log_buf[len] = '\n';
len++;
}
log_buf[len] = '\0';
fprintf(stderr, "%s", log_buf);
switch (loginfo->level) {
case TLOG_ERROR:
sys_log_level = LOG_ERR;
break;
case TLOG_WARN:
sys_log_level = LOG_WARNING;
break;
case TLOG_NOTICE:
sys_log_level = LOG_NOTICE;
break;
case TLOG_INFO:
sys_log_level = LOG_INFO;
break;
case TLOG_DEBUG:
sys_log_level = LOG_DEBUG;
break;
default:
sys_log_level = LOG_INFO;
break;
}
syslog(sys_log_level, "%s", log_buf);
}
static int _smartdns_child_pid = 0;
static int _smartdns_child_restart = 0;
static void _smartdns_run_monitor_sig(int sig)
{
if (_smartdns_child_pid > 0) {
if (sig == SIGHUP) {
_smartdns_child_restart = 1;
kill(_smartdns_child_pid, SIGTERM);
return;
}
kill(_smartdns_child_pid, SIGTERM);
}
waitpid(_smartdns_child_pid, NULL, 0);
_exit(0);
}
static smartdns_run_monitor_ret _smartdns_run_monitor(int restart_when_crash, int is_run_as_daemon)
{
pid_t pid;
int status;
if (restart_when_crash == 0) {
return SMARTDNS_RUN_MONITOR_OK;
}
if (is_run_as_daemon) {
switch (daemon_run(NULL)) {
case DAEMON_RET_CHILD_OK:
break;
case DAEMON_RET_PARENT_OK:
return SMARTDNS_RUN_MONITOR_EXIT;
default:
return SMARTDNS_RUN_MONITOR_ERROR;
}
}
daemon_kickoff(0, 1);
restart:
pid = fork();
if (pid < 0) {
fprintf(stderr, "fork failed, %s\n", strerror(errno));
return SMARTDNS_RUN_MONITOR_ERROR;
} else if (pid == 0) {
return SMARTDNS_RUN_MONITOR_OK;
}
_smartdns_child_pid = pid;
signal(SIGTERM, _smartdns_run_monitor_sig);
signal(SIGHUP, _smartdns_run_monitor_sig);
while (true) {
pid = waitpid(-1, &status, 0);
if (pid == _smartdns_child_pid) {
int need_restart = 0;
char signalmsg[64] = {0};
if (_smartdns_child_restart == 1) {
_smartdns_child_restart = 0;
goto restart;
}
if (WEXITSTATUS(status) == SMARTDNS_CRASH_CODE) {
need_restart = 1;
} else if (WEXITSTATUS(status) == 255) {
fprintf(stderr, "run daemon failed, please check log.\n");
} else if (WIFSIGNALED(status)) {
switch (WTERMSIG(status)) {
case SIGSEGV:
case SIGABRT:
case SIGBUS:
case SIGILL:
case SIGFPE:
snprintf(signalmsg, sizeof(signalmsg), " with signal %d", WTERMSIG(status));
need_restart = 1;
break;
default:
break;
}
}
if (need_restart == 1) {
printf("smartdns crashed%s, restart...\n", signalmsg);
goto restart;
}
break;
}
if (pid < 0) {
sleep(1);
}
}
return SMARTDNS_RUN_MONITOR_ERROR;
}
static void _smartdns_print_error_tip(void)
{
char buff[4096];
char *log_path = realpath(_smartdns_log_path(), buff);
if (log_path != NULL && access(log_path, F_OK) == 0) {
fprintf(stderr, "run daemon failed, please check log at %s\n", log_path);
}
}
#ifdef TEST
static smartdns_post_func _smartdns_post = NULL;
@@ -736,21 +934,32 @@ static void smartdns_test_notify_func(int fd_notify, uint64_t retval)
}
}
int smartdns_main(int argc, char *argv[], int fd_notify)
#define smartdns_close_allfds() \
if (no_close_allfds == 0) { \
close_all_fd(fd_notify); \
}
int smartdns_main(int argc, char *argv[], int fd_notify, int no_close_allfds)
#else
#define smartdns_test_notify(retval)
#define smartdns_close_allfds() close_all_fd(-1)
int main(int argc, char *argv[])
#endif
{
int ret = 0;
int is_foreground = 0;
int is_run_as_daemon = 1;
int opt = 0;
char config_file[MAX_LINE_LEN];
char pid_file[MAX_LINE_LEN];
int is_pid_file_set = 0;
int signal_ignore = 0;
int restart_when_crash = getpid() == 1 ? 1 : 0;
sigset_t empty_sigblock;
struct stat sb;
static struct option long_options[] = {
{"cache-print", required_argument, NULL, 256}, {"help", no_argument, NULL, 'h'}, {NULL, 0, NULL, 0}};
safe_strncpy(config_file, SMARTDNS_CONF_FILE, MAX_LINE_LEN);
if (stat("/run", &sb) == 0 && S_ISDIR(sb.st_mode)) {
@@ -762,17 +971,26 @@ int main(int argc, char *argv[])
/* patch for Asus router: unblock all signal*/
sigemptyset(&empty_sigblock);
sigprocmask(SIG_SETMASK, &empty_sigblock, NULL);
smartdns_close_allfds();
while ((opt = getopt(argc, argv, "fhc:p:SvxN:")) != -1) {
while ((opt = getopt_long(argc, argv, "fhc:p:SvxN:R", long_options, NULL)) != -1) {
switch (opt) {
case 'f':
is_foreground = 1;
is_run_as_daemon = 0;
break;
case 'c':
snprintf(config_file, sizeof(config_file), "%s", optarg);
if (full_path(config_file, sizeof(config_file), optarg) != 0) {
snprintf(config_file, sizeof(config_file), "%s", optarg);
}
break;
case 'p':
snprintf(pid_file, sizeof(pid_file), "%s", optarg);
if (strncmp(optarg, "-", 2) == 0 || full_path(pid_file, sizeof(pid_file), optarg) != 0) {
snprintf(pid_file, sizeof(pid_file), "%s", optarg);
is_pid_file_set = 1;
}
break;
case 'R':
restart_when_crash = 1;
break;
case 'S':
signal_ignore = 1;
@@ -790,19 +1008,54 @@ int main(int argc, char *argv[])
#endif
case 'h':
_help();
return 0;
case 256:
return dns_cache_print(optarg);
break;
default:
fprintf(stderr, "unknown option, please run %s -h for help.\n", argv[0]);
return 1;
}
}
if (dns_server_load_conf(config_file) != 0) {
smartdns_run_monitor_ret init_ret = _smartdns_run_monitor(restart_when_crash, is_run_as_daemon);
if (init_ret != SMARTDNS_RUN_MONITOR_OK) {
if (init_ret == SMARTDNS_RUN_MONITOR_EXIT) {
return 0;
}
return 1;
}
srand(time(NULL));
tlog_reg_early_printf_callback(_smartdns_early_log);
ret = dns_server_load_conf(config_file);
if (ret != 0) {
fprintf(stderr, "load config failed.\n");
goto errout;
}
if (is_foreground == 0) {
if (daemon(0, 0) < 0) {
fprintf(stderr, "run daemon process failed, %s\n", strerror(errno));
return 1;
if (dns_no_daemon || restart_when_crash) {
is_run_as_daemon = 0;
}
if (is_run_as_daemon) {
int child_status = -1;
switch (daemon_run(&child_status)) {
case DAEMON_RET_CHILD_OK:
break;
case DAEMON_RET_PARENT_OK: {
if (child_status != 0 && child_status != -3) {
_smartdns_print_error_tip();
}
return child_status;
} break;
case DAEMON_RET_ERR:
default:
fprintf(stderr, "run daemon failed.\n");
goto errout;
}
}
@@ -810,7 +1063,18 @@ int main(int argc, char *argv[])
_reg_signal();
}
if (strncmp(pid_file, "-", 2) != 0 && create_pid_file(pid_file) != 0) {
if (is_pid_file_set == 0) {
char pid_file_path[MAX_LINE_LEN];
safe_strncpy(pid_file_path, pid_file, MAX_LINE_LEN);
dir_name(pid_file_path);
if (access(pid_file_path, W_OK) != 0) {
dns_no_pidfile = 1;
}
}
if (strncmp(pid_file, "-", 2) != 0 && dns_no_pidfile == 0 && create_pid_file(pid_file) != 0) {
ret = -3;
goto errout;
}
@@ -818,9 +1082,10 @@ int main(int argc, char *argv[])
signal(SIGINT, _sig_exit);
signal(SIGTERM, _sig_exit);
if (_smartdns_init_pre() != 0) {
ret = _smartdns_init_pre();
if (ret != 0) {
fprintf(stderr, "init failed.\n");
return 1;
goto errout;
}
drop_root_privilege();
@@ -831,11 +1096,27 @@ int main(int argc, char *argv[])
goto errout;
}
if (is_run_as_daemon) {
ret = daemon_kickoff(0, dns_conf_log_console | verbose_screen);
if (ret != 0) {
goto errout;
}
} else if (dns_conf_log_console == 0 && verbose_screen == 0) {
daemon_close_stdfds();
}
smartdns_test_notify(1);
ret = _smartdns_run();
tlog(TLOG_INFO, "smartdns exit...");
_smartdns_exit();
return ret;
errout:
if (is_run_as_daemon) {
daemon_kickoff(ret, dns_conf_log_console | verbose_screen);
} else if (dns_conf_log_console == 0 && verbose_screen == 0) {
_smartdns_print_error_tip();
}
smartdns_test_notify(2);
return 1;
_smartdns_exit();
return ret;
}

View File

@@ -29,7 +29,7 @@ typedef void (*smartdns_post_func)(void *arg);
int smartdns_reg_post_func(smartdns_post_func func, void *arg);
int smartdns_main(int argc, char *argv[], int fd_notify);
int smartdns_main(int argc, char *argv[], int fd_notify, int no_close_allfds);
#endif

70
src/timer.c Normal file
View File

@@ -0,0 +1,70 @@
/*************************************************************************
*
* Copyright (C) 2018-2023 Ruilin Peng (Nick) <pymumu@gmail.com>.
*
* smartdns is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* smartdns is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "timer.h"
#include "timer_wheel.h"
static struct tw_base *dns_timer_base = NULL;
int dns_timer_init(void)
{
struct tw_base *tw = tw_init_timers();
if (tw == NULL) {
return -1;
}
dns_timer_base = tw;
return 0;
}
void dns_timer_destroy(void)
{
if (dns_timer_base != NULL) {
tw_cleanup_timers(dns_timer_base);
dns_timer_base = NULL;
}
}
void dns_timer_add(struct tw_timer_list *timer)
{
if (dns_timer_base == NULL) {
return;
}
tw_add_timer(dns_timer_base, timer);
}
int dns_timer_del(struct tw_timer_list *timer)
{
if (dns_timer_base == NULL) {
return 0;
}
return tw_del_timer(dns_timer_base, timer);
}
int dns_timer_mod(struct tw_timer_list *timer, unsigned long expires)
{
if (dns_timer_base == NULL) {
return 0;
}
return tw_mod_timer(dns_timer_base, timer, expires);
}

41
src/timer.h Normal file
View File

@@ -0,0 +1,41 @@
/*************************************************************************
*
* Copyright (C) 2018-2023 Ruilin Peng (Nick) <pymumu@gmail.com>.
*
* smartdns is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* smartdns is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SMART_DNS_TIMER_H
#define SMART_DNS_TIMER_H
#include "timer_wheel.h"
#ifdef __cplusplus
extern "C" {
#endif /*__cplusplus */
int dns_timer_init(void);
void dns_timer_add(struct tw_timer_list *timer);
int dns_timer_del(struct tw_timer_list *timer);
int dns_timer_mod(struct tw_timer_list *timer, unsigned long expires);
void dns_timer_destroy(void);
#ifdef __cplusplus
}
#endif /*__cplusplus */
#endif

View File

@@ -79,6 +79,7 @@ struct tlog_log {
int zip_pid;
int multi_log;
int logscreen;
int logscreen_color;
int segment_log;
int max_line_size;
int print_errmsg;
@@ -148,6 +149,7 @@ static struct tlog tlog;
static int tlog_disable_early_print = 0;
static tlog_level tlog_set_level = TLOG_INFO;
static tlog_format_func tlog_format;
static tlog_early_print_func tlog_early_print;
static unsigned int tlog_localtime_lock = 0;
static const char *tlog_level_str[] = {
@@ -187,7 +189,7 @@ static int _tlog_mkdir(const char *path)
return 0;
}
while (*path == ' ' && *path != '\0') {
while (*path == ' ') {
path++;
}
@@ -330,7 +332,7 @@ void tlog_logcount(struct tlog_log *log, int count)
log->logcount = count;
}
void tlog_set_permission(struct tlog_log *log, unsigned int file, unsigned int archive)
void tlog_set_permission(struct tlog_log *log, mode_t file, mode_t archive)
{
log->file_perm = file;
log->archive_perm = archive;
@@ -628,7 +630,7 @@ int tlog_printf(struct tlog_log *log, const char *format, ...)
return len;
}
static int _tlog_early_print(tlog_level level, const char *file, int line, const char *func, const char *format, va_list ap)
static int _tlog_early_print(struct tlog_info_inter *info_inter, const char *format, va_list ap)
{
char log_buf[TLOG_MAX_LINE_LEN];
size_t len = 0;
@@ -644,9 +646,14 @@ static int _tlog_early_print(tlog_level level, const char *file, int line, const
return -1;
}
if (tlog_early_print != NULL) {
tlog_early_print(&info_inter->info, format, ap);
return out_len;
}
len = snprintf(log_buf, sizeof(log_buf), "[%.4d-%.2d-%.2d %.2d:%.2d:%.2d,%.3d][%5s][%17s:%-4d] ",
cur_time.year, cur_time.mon, cur_time.mday, cur_time.hour, cur_time.min, cur_time.sec, cur_time.usec / 1000,
tlog_get_level_string(level), file, line);
cur_time.year, cur_time.mon, cur_time.mday, cur_time.hour, cur_time.min, cur_time.sec, cur_time.usec / 1000,
tlog_get_level_string(info_inter->info.level), info_inter->info.file, info_inter->info.line);
out_len = len;
len = vsnprintf(log_buf + out_len, sizeof(log_buf) - out_len - 1, format, ap);
out_len += len;
@@ -662,7 +669,7 @@ static int _tlog_early_print(tlog_level level, const char *file, int line, const
}
unused = write(STDOUT_FILENO, log_buf, out_len);
return len;
return out_len;
}
int tlog_vext(tlog_level level, const char *file, int line, const char *func, void *userptr, const char *format, va_list ap)
@@ -673,14 +680,6 @@ int tlog_vext(tlog_level level, const char *file, int line, const char *func, vo
return 0;
}
if (tlog.root == NULL) {
return _tlog_early_print(level, file, line, func, format, ap);
}
if (unlikely(tlog.root->logsize <= 0)) {
return 0;
}
if (level >= TLOG_END) {
return -1;
}
@@ -694,6 +693,14 @@ int tlog_vext(tlog_level level, const char *file, int line, const char *func, vo
return -1;
}
if (tlog.root == NULL) {
return _tlog_early_print(&info_inter, format, ap);
}
if (unlikely(tlog.root->logsize <= 0)) {
return 0;
}
return _tlog_vprintf(tlog.root, _tlog_root_log_buffer, &info_inter, format, ap);
}
@@ -748,7 +755,7 @@ static int _tlog_list_dir(const char *path, list_callback callback, void *userpt
dir = opendir(path);
if (dir == NULL) {
fprintf(stderr, "open directory failed, %s\n", strerror(errno));
fprintf(stderr, "tlog: open directory failed, %s\n", strerror(errno));
goto errout;
}
@@ -859,7 +866,7 @@ static int _tlog_remove_oldlog(struct tlog_log *log)
/* get total log file number */
if (_tlog_list_dir(log->logdir, _tlog_count_log_callback, &count_log) != 0) {
fprintf(stderr, "get log file count failed.\n");
fprintf(stderr, "tlog: get log file count failed.\n");
return -1;
}
@@ -896,7 +903,7 @@ static int _tlog_log_lock(struct tlog_log *log)
snprintf(lock_file, sizeof(lock_file), "%s/%s.lock", log->logdir, log->logname);
fd = open(lock_file, O_RDWR | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR);
if (fd == -1) {
fprintf(stderr, "create pid file failed, %s", strerror(errno));
fprintf(stderr, "tlog: create lock file failed, %s", strerror(errno));
return -1;
}
@@ -1061,8 +1068,14 @@ static int _tlog_archive_log_compressed(struct tlog_log *log)
if (pid == 0) {
_tlog_close_all_fd();
execl(tlog.gzip_cmd, tlog.gzip_cmd, "-1", pending_file, NULL);
fprintf(stderr, "tlog: execl gzip failed, no compress\n");
log->nocompress = 1;
_exit(1);
} else if (pid < 0) {
if (errno == EPERM || errno == EACCES) {
fprintf(stderr, "tlog: vfork failed, errno: %d, no compress\n", errno);
log->nocompress = 1;
}
goto errout;
}
log->zip_pid = pid;
@@ -1139,7 +1152,54 @@ static void _tlog_get_log_name_dir(struct tlog_log *log)
pthread_mutex_unlock(&tlog.lock);
}
static int _tlog_write(struct tlog_log *log, const char *buff, int bufflen)
static int _tlog_write_screen(struct tlog_log *log, struct tlog_loginfo *info, const char *buff, int bufflen)
{
int unused __attribute__((unused));
if (bufflen <= 0) {
return 0;
}
if (log->logscreen == 0) {
return 0;
}
if (log->logscreen_color && info != NULL) {
const char *color = NULL;
switch (info->level) {
case TLOG_DEBUG:
color = "\033[0;90m";
break;
case TLOG_NOTICE:
color = "\033[0;97m";
break;
case TLOG_WARN:
color = "\033[0;33m";
break;
case TLOG_ERROR:
color = "\033[0;31m";
break;
case TLOG_FATAL:
color = "\033[31;1m";
break;
default:
break;
}
if (color != NULL) {
fprintf(stdout, "%s%.*s\033[0m\n", color, bufflen - 2, buff);
} else {
fprintf(stdout, "%s", buff);
}
} else {
/* output log to screen */
unused = write(STDOUT_FILENO, buff, bufflen);
}
return bufflen;
}
static int _tlog_write_ext(struct tlog_log *log, struct tlog_loginfo *info, const char *buff, int bufflen)
{
int len;
int unused __attribute__((unused));
@@ -1155,7 +1215,7 @@ static int _tlog_write(struct tlog_log *log, const char *buff, int bufflen)
/* output log to screen */
if (log->logscreen) {
unused = write(STDOUT_FILENO, buff, bufflen);
_tlog_write_screen(log, info, buff, bufflen);
}
if (log->logcount <= 0) {
@@ -1195,9 +1255,9 @@ static int _tlog_write(struct tlog_log *log, const char *buff, int bufflen)
return -1;
}
log->print_errmsg = 0;
fprintf(stderr, "create log dir %s failed, %s\n", log->logdir, strerror(errno));
fprintf(stderr, "tlog: create log dir %s failed, %s\n", log->logdir, strerror(errno));
if (errno == EACCES && log->logscreen == 0) {
fprintf(stderr, "no permission to write log file, output log to console\n");
fprintf(stderr, "tlog: no permission to write log file, output log to console\n");
tlog_logscreen(log, 1);
tlog_logcount(log, 0);
}
@@ -1211,7 +1271,7 @@ static int _tlog_write(struct tlog_log *log, const char *buff, int bufflen)
return -1;
}
fprintf(stderr, "open log file %s failed, %s\n", logfile, strerror(errno));
fprintf(stderr, "tlog: open log file %s failed, %s\n", logfile, strerror(errno));
log->print_errmsg = 0;
return -1;
}
@@ -1239,6 +1299,11 @@ static int _tlog_write(struct tlog_log *log, const char *buff, int bufflen)
return len;
}
static inline int _tlog_write(struct tlog_log *log, const char *buff, int bufflen)
{
return _tlog_write_ext(log, NULL, buff, bufflen);
}
int tlog_write(struct tlog_log *log, const char *buff, int bufflen)
{
return _tlog_write(log, buff, bufflen);
@@ -1472,7 +1537,7 @@ static int _tlog_root_write_log(struct tlog_log *log, const char *buff, int buff
if (tlog.output_func == NULL) {
if (log->segment_log) {
head = (struct tlog_segment_log_head *)buff;
return _tlog_write(log, head->data, head->len);
return _tlog_write_ext(log, &head->info, head->data, head->len);
}
return _tlog_write(log, buff, bufflen);
}
@@ -1614,6 +1679,11 @@ void tlog_set_early_printf(int enable)
tlog_disable_early_print = (enable == 0) ? 1 : 0;
}
void tlog_reg_early_printf_callback(tlog_early_print_func callback)
{
tlog_early_print = callback;
}
const char *tlog_get_level_string(tlog_level level)
{
if (level >= TLOG_END) {
@@ -1739,7 +1809,7 @@ static void _tlog_get_gzip_cmd_path(void)
if (access(gzip_cmd_path, X_OK) != 0) {
continue;
}
snprintf(tlog.gzip_cmd, sizeof(tlog.gzip_cmd), "%s", gzip_cmd_path);
break;
}
@@ -1752,13 +1822,13 @@ tlog_log *tlog_open(const char *logfile, int maxlogsize, int maxlogcount, int bu
struct tlog_log *log = NULL;
if (tlog.run == 0) {
fprintf(stderr, "tlog is not initialized.");
fprintf(stderr, "tlog: tlog is not initialized.\n");
return NULL;
}
log = (struct tlog_log *)malloc(sizeof(*log));
if (log == NULL) {
fprintf(stderr, "malloc log failed.");
fprintf(stderr, "tlog: malloc log failed.\n");
return NULL;
}
@@ -1780,6 +1850,7 @@ tlog_log *tlog_open(const char *logfile, int maxlogsize, int maxlogcount, int bu
log->block = ((flag & TLOG_NONBLOCK) == 0) ? 1 : 0;
log->nocompress = ((flag & TLOG_NOCOMPRESS) == 0) ? 0 : 1;
log->logscreen = ((flag & TLOG_SCREEN) == 0) ? 0 : 1;
log->logscreen_color = ((flag & TLOG_SCREEN_COLOR) == 0) ? 0 : 1;
log->multi_log = ((flag & TLOG_MULTI_WRITE) == 0) ? 0 : 1;
log->segment_log = ((flag & TLOG_SEGMENT) == 0) ? 0 : 1;
log->max_line_size = TLOG_MAX_LINE_LEN;
@@ -1791,6 +1862,11 @@ tlog_log *tlog_open(const char *logfile, int maxlogsize, int maxlogcount, int bu
log->nocompress = 1;
}
if (log->logscreen_color == 1) {
log->logscreen = 1;
log->segment_log = 1;
}
tlog_rename_logfile(log, logfile);
if (log->nocompress) {
strncpy(log->suffix, TLOG_SUFFIX_LOG, sizeof(log->suffix));
@@ -1800,7 +1876,7 @@ tlog_log *tlog_open(const char *logfile, int maxlogsize, int maxlogcount, int bu
log->buff = (char *)malloc(log->buffsize);
if (log->buff == NULL) {
fprintf(stderr, "malloc log buffer failed, %s\n", strerror(errno));
fprintf(stderr, "tlog: malloc log buffer failed, %s\n", strerror(errno));
goto errout;
}
@@ -1888,7 +1964,7 @@ static void tlog_fork_child(void)
pthread_attr_init(&attr);
int ret = pthread_create(&tlog.tid, &attr, _tlog_work, NULL);
if (ret != 0) {
fprintf(stderr, "create tlog work thread failed, %s\n", strerror(errno));
fprintf(stderr, "tlog: create tlog work thread failed, %s\n", strerror(errno));
goto errout;
}
@@ -1910,12 +1986,12 @@ int tlog_init(const char *logfile, int maxlogsize, int maxlogcount, int buffsize
struct tlog_log *log = NULL;
if (tlog_format != NULL) {
fprintf(stderr, "tlog already initialized.\n");
fprintf(stderr, "tlog: already initialized.\n");
return -1;
}
if (buffsize > 0 && buffsize < TLOG_MAX_LINE_SIZE_SET * 2) {
fprintf(stderr, "buffer size is invalid.\n");
fprintf(stderr, "tlog: buffer size is invalid.\n");
return -1;
}
@@ -1932,19 +2008,19 @@ int tlog_init(const char *logfile, int maxlogsize, int maxlogcount, int buffsize
log = tlog_open(logfile, maxlogsize, maxlogcount, buffsize, flag);
if (log == NULL) {
fprintf(stderr, "init tlog root failed.\n");
fprintf(stderr, "tlog: init tlog root failed.\n");
goto errout;
}
tlog_reg_output_func(log, _tlog_root_write_log);
if ((flag & TLOG_NOCOMPRESS) == 0 && tlog.gzip_cmd[0] == '\0') {
fprintf(stderr, "can not find gzip command, disable compress.\n");
fprintf(stderr, "tlog: can not find gzip command, disable compress.\n");
}
tlog.root = log;
ret = pthread_create(&tlog.tid, &attr, _tlog_work, NULL);
if (ret != 0) {
fprintf(stderr, "create tlog work thread failed, %s\n", strerror(errno));
fprintf(stderr, "tlog: create tlog work thread failed, %s\n", strerror(errno));
goto errout;
}
@@ -1964,6 +2040,7 @@ errout:
pthread_mutex_destroy(&tlog.lock);
tlog.run = 0;
tlog.root = NULL;
tlog_format = NULL;
_tlog_close(log, 1);
@@ -1972,6 +2049,10 @@ errout:
void tlog_exit(void)
{
if (tlog_format == NULL) {
return;
}
if (tlog.tid) {
void *ret = NULL;
tlog.run = 0;

View File

@@ -1,6 +1,6 @@
/*
* tinylog
* Copyright (C) 2018-2021 Ruilin Peng (Nick) <pymumu@gmail.com>
* Copyright (C) 2018-2023 Ruilin Peng (Nick) <pymumu@gmail.com>
* https://github.com/pymumu/tinylog
*/
@@ -66,6 +66,9 @@ struct tlog_time {
/* enable support fork process */
#define TLOG_SUPPORT_FORK (1 << 5)
/* enable output to screen with color */
#define TLOG_SCREEN_COLOR (1 << 6)
struct tlog_loginfo {
tlog_level level;
const char *file;
@@ -111,6 +114,10 @@ extern void tlog_setlogscreen(int enable);
/* enable early log to screen */
extern void tlog_set_early_printf(int enable);
/* set early log callback */
typedef void (*tlog_early_print_func)(struct tlog_loginfo *loginfo, const char *format, va_list ap);
extern void tlog_reg_early_printf_callback(tlog_early_print_func callback);
/* Get log level in string */
extern const char *tlog_get_level_string(tlog_level level);
@@ -139,7 +146,7 @@ steps:
read _tlog_format for example.
*/
typedef int (*tlog_format_func)(char *buff, int maxlen, struct tlog_loginfo *info, void *userptr, const char *format, va_list ap);
extern int tlog_reg_format_func(tlog_format_func func);
extern int tlog_reg_format_func(tlog_format_func callback);
/* register log output callback
Note: info is invalid when flag TLOG_SEGMENT is not set.
@@ -297,4 +304,4 @@ private:
#define tlog_error(...) tlog(TLOG_ERROR, ##__VA_ARGS__)
#define tlog_fatal(...) tlog(TLOG_FATAL, ##__VA_ARGS__)
#endif
#endif // !TLOG_H
#endif // !TLOG_H

View File

@@ -25,6 +25,7 @@
#include "util.h"
#include <arpa/inet.h>
#include <ctype.h>
#include <dirent.h>
#include <dlfcn.h>
#include <errno.h>
#include <fcntl.h>
@@ -38,11 +39,13 @@
#include <openssl/crypto.h>
#include <openssl/ssl.h>
#include <openssl/x509v3.h>
#include <poll.h>
#include <pthread.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <sys/prctl.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <sys/sysinfo.h>
@@ -100,8 +103,20 @@ struct ipset_netlink_msg {
__be16 res_id;
};
enum daemon_msg_type {
DAEMON_MSG_KICKOFF,
DAEMON_MSG_KEEPALIVE,
DAEMON_MSG_DAEMON_PID,
};
struct daemon_msg {
enum daemon_msg_type type;
int value;
};
static int ipset_fd;
static int pidfile_fd;
static int daemon_fd;
unsigned long get_tick_count(void)
{
@@ -153,6 +168,56 @@ errout:
return NULL;
}
int generate_random_addr(unsigned char *addr, int addr_len, int mask)
{
if (mask / 8 > addr_len) {
return -1;
}
int offset = mask / 8;
int bit = 0;
for (int i = offset; i < addr_len; i++) {
bit = 0xFF;
if (i == offset) {
bit = ~(0xFF << (8 - mask % 8)) & 0xFF;
}
addr[i] = jhash(&addr[i], 1, 0) & bit;
}
return 0;
}
int generate_addr_map(const unsigned char *addr_from, const unsigned char *addr_to, unsigned char *addr_out,
int addr_len, int mask)
{
if ((mask / 8) >= addr_len) {
if (mask % 8 != 0) {
return -1;
}
}
int offset = mask / 8;
int bit = mask % 8;
for (int i = 0; i < offset; i++) {
addr_out[i] = addr_to[i];
}
if (bit != 0) {
int mask1 = 0xFF >> bit;
int mask2 = (0xFF << (8 - bit)) & 0xFF;
addr_out[offset] = addr_from[offset] & mask1;
addr_out[offset] |= addr_to[offset] & mask2;
offset = offset + 1;
}
for (int i = offset; i < addr_len; i++) {
addr_out[i] = addr_from[i];
}
return 0;
}
int getaddr_by_host(const char *host, struct sockaddr *addr, socklen_t *addr_len)
{
struct addrinfo hints;
@@ -806,7 +871,11 @@ int create_pid_file(const char *pid_file)
}
if (lockf(fd, F_TLOCK, 0) < 0) {
fprintf(stderr, "Server is already running.\n");
memset(buff, 0, TMP_BUFF_LEN_32);
if (read(fd, buff, TMP_BUFF_LEN_32) <= 0) {
buff[0] = '\0';
}
fprintf(stderr, "Server is already running, pid is %s", buff);
goto errout;
}
@@ -831,6 +900,27 @@ errout:
return -1;
}
int full_path(char *normalized_path, int normalized_path_len, const char *path)
{
const char *p = path;
if (path == NULL || normalized_path == NULL) {
return -1;
}
while (*p == ' ') {
p++;
}
if (*p == '\0' || *p == '/') {
return -1;
}
char buf[PATH_MAX];
snprintf(normalized_path, normalized_path_len, "%s/%s", getcwd(buf, sizeof(buf)), path);
return 0;
}
int generate_cert_key(const char *key_path, const char *cert_path, const char *san, int days)
{
int ret = -1;
@@ -975,6 +1065,10 @@ void SSL_CRYPTO_thread_setup(void)
{
int i = 0;
if (lock_cs != NULL) {
return;
}
lock_cs = OPENSSL_malloc(CRYPTO_num_locks() * sizeof(pthread_mutex_t));
lock_count = OPENSSL_malloc(CRYPTO_num_locks() * sizeof(long));
if (!lock_cs || !lock_count) {
@@ -1004,12 +1098,18 @@ void SSL_CRYPTO_thread_cleanup(void)
{
int i = 0;
if (lock_cs == NULL) {
return;
}
CRYPTO_set_locking_callback(NULL);
for (i = 0; i < CRYPTO_num_locks(); i++) {
pthread_mutex_destroy(&(lock_cs[i]));
}
OPENSSL_free(lock_cs);
OPENSSL_free(lock_count);
lock_cs = NULL;
lock_count = NULL;
}
#endif
@@ -1286,8 +1386,8 @@ int set_sock_keepalive(int fd, int keepidle, int keepinterval, int keepcnt)
}
setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &keepidle, sizeof(keepidle));
setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &keepinterval, sizeof(keepinterval));
setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &keepcnt, sizeof(keepcnt));
setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &keepinterval, sizeof(keepinterval));
setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &keepcnt, sizeof(keepcnt));
return 0;
}
@@ -1387,7 +1487,7 @@ void bug_ext(const char *file, int line, const char *func, const char *errfmt, .
int write_file(const char *filename, void *data, int data_len)
{
int fd = open(filename, O_WRONLY | O_CREAT, 0644);
int fd = open(filename, O_WRONLY | O_CREAT | O_EXCL, 0644);
if (fd < 0) {
return -1;
}
@@ -1435,9 +1535,9 @@ int dns_packet_save(const char *dir, const char *type, const char *from, const v
snprintf(time_s, sizeof(time_s) - 1, "%.4d-%.2d-%.2d %.2d:%.2d:%.2d.%.3d", ptm->tm_year + 1900, ptm->tm_mon + 1,
ptm->tm_mday, ptm->tm_hour, ptm->tm_min, ptm->tm_sec, (int)(tm_val.tv_usec / 1000));
snprintf(filename, sizeof(filename) - 1, "%s/%s-%.4d%.2d%.2d-%.2d%.2d%.2d%.1d.packet", dir, type,
snprintf(filename, sizeof(filename) - 1, "%s/%s-%.4d%.2d%.2d-%.2d%.2d%.2d%.3d.packet", dir, type,
ptm->tm_year + 1900, ptm->tm_mon + 1, ptm->tm_mday, ptm->tm_hour, ptm->tm_min, ptm->tm_sec,
(int)(tm_val.tv_usec / 100000));
(int)(tm_val.tv_usec / 1000));
data = malloc(PACKET_BUF_SIZE);
if (data == NULL) {
@@ -1479,6 +1579,223 @@ out:
return ret;
}
static void _close_all_fd_by_res(void)
{
struct rlimit lim;
int maxfd = 0;
int i = 0;
getrlimit(RLIMIT_NOFILE, &lim);
maxfd = lim.rlim_cur;
if (maxfd > 4096) {
maxfd = 4096;
}
for (i = 3; i < maxfd; i++) {
close(i);
}
}
void close_all_fd(int keepfd)
{
DIR *dirp;
int dir_fd = -1;
struct dirent *dentp;
dirp = opendir("/proc/self/fd");
if (dirp == NULL) {
goto errout;
}
dir_fd = dirfd(dirp);
while ((dentp = readdir(dirp)) != NULL) {
int fd = atol(dentp->d_name);
if (fd < 0) {
continue;
}
if (fd == dir_fd || fd == STDIN_FILENO || fd == STDOUT_FILENO || fd == STDERR_FILENO || fd == keepfd) {
continue;
}
close(fd);
}
closedir(dirp);
return;
errout:
if (dirp) {
closedir(dirp);
}
_close_all_fd_by_res();
return;
}
void daemon_close_stdfds(void)
{
int fd_null = open("/dev/null", O_RDWR);
if (fd_null < 0) {
fprintf(stderr, "open /dev/null failed, %s\n", strerror(errno));
return;
}
dup2(fd_null, STDIN_FILENO);
dup2(fd_null, STDOUT_FILENO);
dup2(fd_null, STDERR_FILENO);
if (fd_null > 2) {
close(fd_null);
}
}
int daemon_kickoff(int status, int no_close)
{
struct daemon_msg msg;
if (daemon_fd <= 0) {
return -1;
}
msg.type = DAEMON_MSG_KICKOFF;
msg.value = status;
int ret = write(daemon_fd, &msg, sizeof(msg));
if (ret != sizeof(msg)) {
fprintf(stderr, "notify parent process failed, %s\n", strerror(errno));
return -1;
}
if (no_close == 0) {
daemon_close_stdfds();
}
close(daemon_fd);
daemon_fd = -1;
return 0;
}
int daemon_keepalive(void)
{
struct daemon_msg msg;
static time_t last = 0;
time_t now = time(NULL);
if (daemon_fd <= 0) {
return -1;
}
if (now == last) {
return 0;
}
last = now;
msg.type = DAEMON_MSG_KEEPALIVE;
msg.value = 0;
int ret = write(daemon_fd, &msg, sizeof(msg));
if (ret != sizeof(msg)) {
return -1;
}
return 0;
}
daemon_ret daemon_run(int *wstatus)
{
pid_t pid = 0;
int fds[2] = {0};
if (pipe(fds) != 0) {
fprintf(stderr, "run daemon process failed, pipe failed, %s\n", strerror(errno));
return -1;
}
pid = fork();
if (pid < 0) {
fprintf(stderr, "run daemon process failed, fork failed, %s\n", strerror(errno));
close(fds[0]);
close(fds[1]);
return -1;
} else if (pid > 0) {
struct pollfd pfd;
int ret = 0;
close(fds[1]);
pfd.fd = fds[0];
pfd.events = POLLIN;
pfd.revents = 0;
do {
ret = poll(&pfd, 1, 3000);
if (ret <= 0) {
fprintf(stderr, "run daemon process failed, wait child timeout, kill child.\n");
goto errout;
}
if (!(pfd.revents & POLLIN)) {
goto errout;
}
struct daemon_msg msg;
ret = read(fds[0], &msg, sizeof(msg));
if (ret != sizeof(msg)) {
goto errout;
}
if (msg.type == DAEMON_MSG_KEEPALIVE) {
continue;
} else if (msg.type == DAEMON_MSG_DAEMON_PID) {
pid = msg.value;
continue;
} else if (msg.type == DAEMON_MSG_KICKOFF) {
if (wstatus != NULL) {
*wstatus = msg.value;
}
return DAEMON_RET_PARENT_OK;
} else {
goto errout;
}
} while (true);
return DAEMON_RET_ERR;
}
setsid();
pid = fork();
if (pid < 0) {
fprintf(stderr, "double fork failed, %s\n", strerror(errno));
_exit(1);
} else if (pid > 0) {
struct daemon_msg msg;
int unused __attribute__((unused));
msg.type = DAEMON_MSG_DAEMON_PID;
msg.value = pid;
unused = write(fds[1], &msg, sizeof(msg));
_exit(0);
}
umask(0);
if (chdir("/") != 0) {
goto errout;
}
close(fds[0]);
daemon_fd = fds[1];
return DAEMON_RET_CHILD_OK;
errout:
kill(pid, SIGKILL);
if (wstatus != NULL) {
*wstatus = -1;
}
return DAEMON_RET_ERR;
}
#ifdef DEBUG
struct _dns_read_packet_info {
int data_len;
@@ -1596,6 +1913,24 @@ static int _dns_debug_display(struct dns_packet *packet)
inet_ntop(AF_INET6, addr, req_host, sizeof(req_host));
printf("domain: %s AAAA: %s TTL:%d\n", name, req_host, ttl);
} break;
case DNS_T_SRV: {
unsigned short priority = 0;
unsigned short weight = 0;
unsigned short port = 0;
int ret = 0;
char name[DNS_MAX_CNAME_LEN] = {0};
char target[DNS_MAX_CNAME_LEN];
ret = dns_get_SRV(rrs, name, DNS_MAX_CNAME_LEN, &ttl, &priority, &weight, &port, target, DNS_MAX_CNAME_LEN);
if (ret < 0) {
tlog(TLOG_DEBUG, "decode SRV failed, %s", name);
return -1;
}
printf("domain: %s SRV: %s TTL: %d priority: %d weight: %d port: %d\n", name, target, ttl, priority,
weight, port);
} break;
case DNS_T_HTTPS: {
char name[DNS_MAX_CNAME_LEN] = {0};
char target[DNS_MAX_CNAME_LEN] = {0};
@@ -1604,7 +1939,7 @@ static int _dns_debug_display(struct dns_packet *packet)
int ret = 0;
ret = dns_get_HTTPS_svcparm_start(rrs, &p, name, DNS_MAX_CNAME_LEN, &ttl, &priority, target,
DNS_MAX_CNAME_LEN);
DNS_MAX_CNAME_LEN);
if (ret != 0) {
printf("get HTTPS svcparm failed\n");
break;

View File

@@ -59,6 +59,11 @@ char *dir_name(char *path);
char *get_host_by_addr(char *host, int maxsize, struct sockaddr *addr);
int generate_random_addr(unsigned char *addr, int addr_len, int mask);
int generate_addr_map(const unsigned char *addr_from, const unsigned char *addr_to, unsigned char *addr_out,
int addr_len, int mask);
int getaddr_by_host(const char *host, struct sockaddr *addr, socklen_t *addr_len);
int getsocket_inet(int fd, struct sockaddr *addr, socklen_t *addr_len);
@@ -105,6 +110,8 @@ int generate_cert_key(const char *key_path, const char *cert_path, const char *s
int create_pid_file(const char *pid_file);
int full_path(char *normalized_path, int normalized_path_len, const char *path);
/* Parse a TLS packet for the Server Name Indication extension in the client
* hello handshake, returning the first server name found (pointer to static
* array)
@@ -138,6 +145,23 @@ uint64_t get_free_space(const char *path);
void print_stack(void);
void close_all_fd(int keepfd);
typedef enum daemon_ret {
DAEMON_RET_OK = 0,
DAEMON_RET_ERR = -1,
DAEMON_RET_CHILD_OK = -2,
DAEMON_RET_PARENT_OK = -3,
} daemon_ret;
daemon_ret daemon_run(int *wstatus);
int daemon_kickoff(int status, int no_close);
int daemon_keepalive(void);
void daemon_close_stdfds(void);
int write_file(const char *filename, void *data, int data_len);
int dns_packet_save(const char *dir, const char *type, const char *from, const void *packet, int packet_len);

View File

@@ -1,6 +1,9 @@
[Unit]
Description=SmartDNS Server
After=network.target
After=network.target
Before=network-online.target
Before=nss-lookup.target
Wants=nss-lookup.target
StartLimitBurst=0
StartLimitIntervalSec=60
@@ -8,7 +11,7 @@ StartLimitIntervalSec=60
Type=forking
PIDFile=@RUNSTATEDIR@/smartdns.pid
EnvironmentFile=@SYSCONFDIR@/default/smartdns
ExecStart=@SBINDIR@/smartdns -p @RUNSTATEDIR@/smartdns.pid $SMART_DNS_OPTS
ExecStart=@SBINDIR@/smartdns -p @RUNSTATEDIR@/smartdns.pid $SMART_DNS_OPTS
Restart=always
RestartSec=2
TimeoutStopSec=15

View File

@@ -23,8 +23,8 @@ CXXFLAGS += -g
CXXFLAGS += -DTEST
CXXFLAGS += -I./ -I../src -I../src/include
SMARTDNS_OBJS = lib/rbtree.o lib/art.o lib/bitops.o lib/radix.o lib/conf.o lib/nftset.o
SMARTDNS_OBJS += smartdns.o fast_ping.o dns_client.o dns_server.o dns.o util.o tlog.o dns_conf.o dns_cache.o http_parse.o proxy.o
SMARTDNS_OBJS = lib/rbtree.o lib/art.o lib/bitops.o lib/radix.o lib/conf.o lib/nftset.o lib/timer_wheel.o lib/idna.o
SMARTDNS_OBJS += smartdns.o fast_ping.o dns_client.o dns_server.o dns.o util.o tlog.o dns_conf.o dns_cache.o http_parse.o proxy.o timer.o
OBJS = $(addprefix ../src/, $(SMARTDNS_OBJS))
TEST_SOURCES := $(wildcard *.cc) $(wildcard */*.cc) $(wildcard */*/*.cc)

View File

@@ -161,3 +161,140 @@ cache-persist no)""");
EXPECT_EQ(client.GetAnswer()[0].GetType(), "AAAA");
EXPECT_EQ(client.GetAnswer()[0].GetData(), "64:ff9b::1010:1010");
}
TEST_F(Address, multiaddress)
{
smartdns::MockServer server_upstream;
smartdns::Server server;
server_upstream.Start("udp://0.0.0.0:61053", [&](struct smartdns::ServerRequestContext *request) {
if (request->qtype == DNS_T_A) {
smartdns::MockServer::AddIP(request, request->domain.c_str(), "1.2.3.4", 700);
return smartdns::SERVER_REQUEST_OK;
} else if (request->qtype == DNS_T_AAAA) {
smartdns::MockServer::AddIP(request, request->domain.c_str(), "64:ff9b::102:304", 700);
return smartdns::SERVER_REQUEST_OK;
}
return smartdns::SERVER_REQUEST_SOA;
});
server.Start(R"""(bind [::]:60053
server 127.0.0.1:61053
log-num 0
log-console yes
log-level debug
speed-check-mode none
address /a.com/10.10.10.10,11.11.11.11,22.22.22.22
address /a.com/64:ff9b::1010:1010,64:ff9b::1111:1111,64:ff9b::2222:2222
cache-persist no)""");
smartdns::Client client;
ASSERT_TRUE(client.Query("a.com A", 60053));
std::cout << client.GetResult() << std::endl;
std::map<std::string, smartdns::DNSRecord *> result;
ASSERT_EQ(client.GetAnswerNum(), 3);
EXPECT_EQ(client.GetStatus(), "NOERROR");
auto answers = client.GetAnswer();
for (int i = 0; i < client.GetAnswerNum(); i++) {
result[client.GetAnswer()[i].GetData()] = &answers[i];
}
ASSERT_NE(result.find("10.10.10.10"), result.end());
auto check_result = result["10.10.10.10"];
EXPECT_EQ(check_result->GetName(), "a.com");
EXPECT_EQ(check_result->GetTTL(), 600);
EXPECT_EQ(check_result->GetType(), "A");
EXPECT_EQ(check_result->GetData(), "10.10.10.10");
ASSERT_NE(result.find("11.11.11.11"), result.end());
check_result = result["11.11.11.11"];
EXPECT_EQ(check_result->GetName(), "a.com");
EXPECT_EQ(check_result->GetTTL(), 600);
EXPECT_EQ(check_result->GetType(), "A");
EXPECT_EQ(check_result->GetData(), "11.11.11.11");
ASSERT_NE(result.find("22.22.22.22"), result.end());
check_result = result["22.22.22.22"];
EXPECT_EQ(check_result->GetName(), "a.com");
EXPECT_EQ(check_result->GetTTL(), 600);
EXPECT_EQ(check_result->GetType(), "A");
EXPECT_EQ(check_result->GetData(), "22.22.22.22");
result.clear();
ASSERT_TRUE(client.Query("a.com AAAA", 60053));
std::cout << client.GetResult() << std::endl;
ASSERT_EQ(client.GetAnswerNum(), 3);
EXPECT_EQ(client.GetStatus(), "NOERROR");
answers = client.GetAnswer();
for (int i = 0; i < client.GetAnswerNum(); i++) {
result[client.GetAnswer()[i].GetData()] = &answers[i];
}
ASSERT_NE(result.find("64:ff9b::1010:1010"), result.end());
check_result = result["64:ff9b::1010:1010"];
EXPECT_EQ(check_result->GetName(), "a.com");
EXPECT_EQ(check_result->GetTTL(), 600);
EXPECT_EQ(check_result->GetType(), "AAAA");
EXPECT_EQ(check_result->GetData(), "64:ff9b::1010:1010");
ASSERT_NE(result.find("64:ff9b::1111:1111"), result.end());
check_result = result["64:ff9b::1111:1111"];
EXPECT_EQ(check_result->GetName(), "a.com");
EXPECT_EQ(check_result->GetTTL(), 600);
EXPECT_EQ(check_result->GetType(), "AAAA");
EXPECT_EQ(check_result->GetData(), "64:ff9b::1111:1111");
ASSERT_NE(result.find("64:ff9b::2222:2222"), result.end());
check_result = result["64:ff9b::2222:2222"];
EXPECT_EQ(check_result->GetName(), "a.com");
EXPECT_EQ(check_result->GetTTL(), 600);
EXPECT_EQ(check_result->GetType(), "AAAA");
EXPECT_EQ(check_result->GetData(), "64:ff9b::2222:2222");
}
TEST_F(Address, soa_sub_ip)
{
smartdns::MockServer server_upstream;
smartdns::Server server;
server_upstream.Start("udp://0.0.0.0:61053", [&](struct smartdns::ServerRequestContext *request) {
if (request->qtype == DNS_T_A) {
smartdns::MockServer::AddIP(request, request->domain.c_str(), "1.2.3.4", 700);
return smartdns::SERVER_REQUEST_OK;
} else if (request->qtype == DNS_T_AAAA) {
smartdns::MockServer::AddIP(request, request->domain.c_str(), "64:ff9b::102:304", 700);
return smartdns::SERVER_REQUEST_OK;
}
return smartdns::SERVER_REQUEST_SOA;
});
server.Start(R"""(bind [::]:60053
server 127.0.0.1:61053
log-num 0
log-console yes
log-level debug
speed-check-mode none
address /a.com/192.168.1.1
address /com/#
cache-persist no)""");
smartdns::Client client;
ASSERT_TRUE(client.Query("a.com A", 60053));
std::cout << client.GetResult() << std::endl;
ASSERT_EQ(client.GetAnswerNum(), 1);
EXPECT_EQ(client.GetStatus(), "NOERROR");
EXPECT_EQ(client.GetAnswer()[0].GetName(), "a.com");
EXPECT_EQ(client.GetAnswer()[0].GetTTL(), 600);
EXPECT_EQ(client.GetAnswer()[0].GetType(), "A");
EXPECT_EQ(client.GetAnswer()[0].GetData(), "192.168.1.1");
ASSERT_TRUE(client.Query("a.com AAAA", 60053));
std::cout << client.GetResult() << std::endl;
ASSERT_EQ(client.GetAnswerNum(), 0);
EXPECT_EQ(client.GetStatus(), "NXDOMAIN");
ASSERT_TRUE(client.Query("b.com A", 60053));
std::cout << client.GetResult() << std::endl;
ASSERT_EQ(client.GetAnswerNum(), 0);
EXPECT_EQ(client.GetStatus(), "NXDOMAIN");
}

View File

@@ -56,6 +56,36 @@ cache-persist no)""");
EXPECT_EQ(client.GetAnswer()[0].GetData(), "1.2.3.4");
}
TEST(Bind, https)
{
Defer
{
unlink("/tmp/smartdns-cert.pem");
unlink("/tmp/smartdns-key.pem");
};
smartdns::Server server_wrap;
smartdns::Server server;
server.Start(R"""(bind [::]:61053
server https://127.0.0.1:60053 -no-check-certificate
log-num 0
log-console yes
log-level debug
cache-persist no)""");
server_wrap.Start(R"""(bind-https [::]:60053
address /example.com/1.2.3.4
log-num 0
log-console yes
log-level debug
cache-persist no)""");
smartdns::Client client;
ASSERT_TRUE(client.Query("example.com", 61053));
ASSERT_EQ(client.GetAnswerNum(), 1);
EXPECT_EQ(client.GetStatus(), "NOERROR");
EXPECT_EQ(client.GetAnswer()[0].GetData(), "1.2.3.4");
}
TEST(Bind, udp_tcp)
{
smartdns::MockServer server_upstream;
@@ -252,3 +282,71 @@ cache-persist no)""");
EXPECT_EQ(client.GetAnswer()[0].GetTTL(), 3);
EXPECT_EQ(client.GetAnswer()[0].GetData(), "1.2.3.4");
}
TEST(Bind, group)
{
smartdns::MockServer server_upstream;
smartdns::MockServer server_upstream1;
smartdns::MockServer server_upstream2;
smartdns::Server server;
server_upstream.Start("udp://0.0.0.0:61053", [](struct smartdns::ServerRequestContext *request) {
if (request->qtype != DNS_T_A) {
return smartdns::SERVER_REQUEST_SOA;
}
smartdns::MockServer::AddIP(request, request->domain.c_str(), "9.10.11.12", 611);
return smartdns::SERVER_REQUEST_OK;
});
server_upstream1.Start("udp://0.0.0.0:62053", [](struct smartdns::ServerRequestContext *request) {
if (request->qtype != DNS_T_A) {
return smartdns::SERVER_REQUEST_SOA;
}
smartdns::MockServer::AddIP(request, request->domain.c_str(), "1.2.3.4", 611);
return smartdns::SERVER_REQUEST_OK;
});
server_upstream2.Start("udp://0.0.0.0:63053", [](struct smartdns::ServerRequestContext *request) {
if (request->qtype != DNS_T_A) {
return smartdns::SERVER_REQUEST_SOA;
}
smartdns::MockServer::AddIP(request, request->domain.c_str(), "5.6.7.8", 611);
return smartdns::SERVER_REQUEST_OK;
});
server.Start(R"""(bind [::]:60053
bind [::]:60153 -group g1
bind [::]:60253 -group g2
server 127.0.0.1:61053
server 127.0.0.1:62053 -group g1 -exclude-default-group
server 127.0.0.1:63053 -group g2 -exclude-default-group
log-num 0
log-console yes
log-level debug
cache-persist no)""");
smartdns::Client client;
ASSERT_TRUE(client.Query("a.com", 60053));
std::cout << client.GetResult() << std::endl;
ASSERT_EQ(client.GetAnswerNum(), 1);
EXPECT_EQ(client.GetStatus(), "NOERROR");
EXPECT_EQ(client.GetAnswer()[0].GetName(), "a.com");
EXPECT_EQ(client.GetAnswer()[0].GetData(), "9.10.11.12");
ASSERT_TRUE(client.Query("a.com", 60153));
std::cout << client.GetResult() << std::endl;
ASSERT_EQ(client.GetAnswerNum(), 1);
EXPECT_EQ(client.GetStatus(), "NOERROR");
EXPECT_EQ(client.GetAnswer()[0].GetName(), "a.com");
EXPECT_EQ(client.GetAnswer()[0].GetData(), "1.2.3.4");
ASSERT_TRUE(client.Query("a.com", 60253));
std::cout << client.GetResult() << std::endl;
ASSERT_EQ(client.GetAnswerNum(), 1);
EXPECT_EQ(client.GetStatus(), "NOERROR");
EXPECT_EQ(client.GetAnswer()[0].GetName(), "a.com");
EXPECT_EQ(client.GetAnswer()[0].GetData(), "5.6.7.8");
}

View File

@@ -127,6 +127,16 @@ cache-persist no)""");
EXPECT_EQ(client.GetAnswer()[0].GetName(), "a.com");
EXPECT_EQ(client.GetAnswer()[0].GetTTL(), 5);
EXPECT_EQ(client.GetAnswer()[0].GetData(), "1.2.3.4");
sleep(1);
ASSERT_TRUE(client.Query("a.com", 60053));
std::cout << client.GetResult() << std::endl;
ASSERT_EQ(client.GetAnswerNum(), 1);
EXPECT_EQ(client.GetStatus(), "NOERROR");
EXPECT_EQ(client.GetAnswer()[0].GetName(), "a.com");
EXPECT_EQ(client.GetAnswer()[0].GetTTL(), 5);
EXPECT_EQ(client.GetAnswer()[0].GetData(), "1.2.3.4");
}
TEST_F(Cache, max_reply_ttl_expired)
@@ -181,6 +191,89 @@ cache-persist no)""");
EXPECT_EQ(client.GetAnswer()[0].GetData(), "1.2.3.4");
}
TEST_F(Cache, prefetch)
{
smartdns::MockServer server_upstream;
smartdns::MockServer server_upstream1;
smartdns::MockServer server_upstream2;
smartdns::Server server;
server_upstream.Start("udp://0.0.0.0:61053", [](struct smartdns::ServerRequestContext *request) {
if (request->qtype != DNS_T_A) {
return smartdns::SERVER_REQUEST_SOA;
}
smartdns::MockServer::AddIP(request, request->domain.c_str(), "9.10.11.12", 611);
return smartdns::SERVER_REQUEST_OK;
});
server_upstream1.Start("udp://0.0.0.0:62053", [](struct smartdns::ServerRequestContext *request) {
if (request->qtype != DNS_T_A) {
return smartdns::SERVER_REQUEST_SOA;
}
smartdns::MockServer::AddIP(request, request->domain.c_str(), "1.2.3.4", 611);
return smartdns::SERVER_REQUEST_OK;
});
server_upstream2.Start("udp://0.0.0.0:63053", [](struct smartdns::ServerRequestContext *request) {
if (request->qtype != DNS_T_A) {
return smartdns::SERVER_REQUEST_SOA;
}
smartdns::MockServer::AddIP(request, request->domain.c_str(), "5.6.7.8", 611);
return smartdns::SERVER_REQUEST_OK;
});
server.MockPing(PING_TYPE_ICMP, "1.2.3.4", 60, 100);
server.MockPing(PING_TYPE_ICMP, "5.6.7.8", 60, 110);
server.MockPing(PING_TYPE_ICMP, "9.10.11.12", 60, 110);
server.Start(R"""(bind [::]:60053
bind [::]:60153 -group g1
server 127.0.0.1:61053
server 127.0.0.1:62053 -group g1 -exclude-default-group
server 127.0.0.1:63053 -group g2
log-num 0
prefetch-domain yes
rr-ttl-max 2
serve-expired no
log-console yes
log-level debug
srv-record-selection no
cache-persist no)""");
smartdns::Client client;
ASSERT_TRUE(client.Query("a.com", 60053));
std::cout << client.GetResult() << std::endl;
ASSERT_EQ(client.GetAnswerNum(), 1);
EXPECT_EQ(client.GetStatus(), "NOERROR");
EXPECT_EQ(client.GetAnswer()[0].GetName(), "a.com");
ASSERT_TRUE(client.Query("a.com", 60153));
std::cout << client.GetResult() << std::endl;
ASSERT_EQ(client.GetAnswerNum(), 1);
EXPECT_EQ(client.GetStatus(), "NOERROR");
EXPECT_EQ(client.GetAnswer()[0].GetName(), "a.com");
EXPECT_EQ(client.GetAnswer()[0].GetData(), "1.2.3.4");
sleep(1);
ASSERT_TRUE(client.Query("a.com", 60053));
std::cout << client.GetResult() << std::endl;
ASSERT_EQ(client.GetAnswerNum(), 2);
EXPECT_EQ(client.GetStatus(), "NOERROR");
EXPECT_EQ(client.GetAnswer()[0].GetName(), "a.com");
sleep(1);
ASSERT_TRUE(client.Query("a.com", 60153));
std::cout << client.GetResult() << std::endl;
ASSERT_EQ(client.GetAnswerNum(), 1);
EXPECT_EQ(client.GetStatus(), "NOERROR");
EXPECT_EQ(client.GetAnswer()[0].GetName(), "a.com");
EXPECT_EQ(client.GetAnswer()[0].GetData(), "1.2.3.4");
}
TEST_F(Cache, nocache)
{
smartdns::MockServer server_upstream;

View File

@@ -77,7 +77,6 @@ TEST_F(Cname, subdomain1)
if (request->domain == "s.a.com") {
smartdns::MockServer::AddIP(request, request->domain.c_str(), "4.5.6.7", 700);
return smartdns::SERVER_REQUEST_OK;
}
smartdns::MockServer::AddIP(request, request->domain.c_str(), "1.2.3.4", 611);
@@ -114,7 +113,6 @@ TEST_F(Cname, subdomain2)
if (request->domain == "a.s.a.com") {
smartdns::MockServer::AddIP(request, request->domain.c_str(), "4.5.6.7", 700);
return smartdns::SERVER_REQUEST_OK;
}
smartdns::MockServer::AddIP(request, request->domain.c_str(), "1.2.3.4", 611);
@@ -139,7 +137,6 @@ cache-persist no)""");
EXPECT_EQ(client.GetAnswer()[1].GetData(), "4.5.6.7");
}
TEST_F(Cname, loop)
{
smartdns::MockServer server_upstream;
@@ -153,7 +150,6 @@ TEST_F(Cname, loop)
if (request->domain == "s.a.com") {
smartdns::MockServer::AddIP(request, request->domain.c_str(), "4.5.6.7", 700);
return smartdns::SERVER_REQUEST_OK;
}
smartdns::MockServer::AddIP(request, request->domain.c_str(), "1.2.3.4", 611);

124
test/cases/test-ddns.cc Normal file
View File

@@ -0,0 +1,124 @@
/*************************************************************************
*
* Copyright (C) 2018-2023 Ruilin Peng (Nick) <pymumu@gmail.com>.
*
* smartdns is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* smartdns is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "client.h"
#include "dns.h"
#include "include/utils.h"
#include "server.h"
#include "util.h"
#include "gtest/gtest.h"
#include <fstream>
class DDNS : public ::testing::Test
{
protected:
virtual void SetUp() {}
virtual void TearDown() {}
};
TEST_F(DDNS, smartdns)
{
smartdns::MockServer server_upstream;
smartdns::Server server;
server_upstream.Start("udp://0.0.0.0:61053", [&](struct smartdns::ServerRequestContext *request) {
return smartdns::SERVER_REQUEST_SOA;
});
server.Start(R"""(bind [::]:60053
server 127.0.0.1:61053
log-num 0
log-console yes
dualstack-ip-selection no
log-level debug
cache-persist no)""");
smartdns::Client client;
ASSERT_TRUE(client.Query("smartdns A", 60053));
std::cout << client.GetResult() << std::endl;
ASSERT_EQ(client.GetAnswerNum(), 1);
EXPECT_EQ(client.GetStatus(), "NOERROR");
EXPECT_EQ(client.GetAnswer()[0].GetName(), "smartdns");
EXPECT_EQ(client.GetAnswer()[0].GetTTL(), 600);
EXPECT_EQ(client.GetAnswer()[0].GetType(), "A");
EXPECT_EQ(client.GetAnswer()[0].GetData(), "127.0.0.1");
ASSERT_TRUE(client.Query("smartdns AAAA", 60053));
std::cout << client.GetResult() << std::endl;
ASSERT_EQ(client.GetAnswerNum(), 1);
EXPECT_EQ(client.GetStatus(), "NOERROR");
EXPECT_EQ(client.GetAnswer()[0].GetName(), "smartdns");
EXPECT_EQ(client.GetAnswer()[0].GetTTL(), 600);
EXPECT_EQ(client.GetAnswer()[0].GetType(), "AAAA");
EXPECT_EQ(client.GetAnswer()[0].GetData(), "::1");
}
TEST_F(DDNS, ddns)
{
smartdns::MockServer server_upstream;
smartdns::Server server;
server_upstream.Start("udp://0.0.0.0:61053", [&](struct smartdns::ServerRequestContext *request) {
return smartdns::SERVER_REQUEST_SOA;
});
server.Start(R"""(bind [::]:60053
server 127.0.0.1:61053
log-num 0
ddns-domain test.ddns.com
ddns-domain test.ddns.org
log-console yes
dualstack-ip-selection no
log-level debug
cache-persist no)""");
smartdns::Client client;
ASSERT_TRUE(client.Query("test.ddns.com A", 60053));
std::cout << client.GetResult() << std::endl;
ASSERT_EQ(client.GetAnswerNum(), 1);
EXPECT_EQ(client.GetStatus(), "NOERROR");
EXPECT_EQ(client.GetAnswer()[0].GetName(), "test.ddns.com");
EXPECT_EQ(client.GetAnswer()[0].GetTTL(), 600);
EXPECT_EQ(client.GetAnswer()[0].GetType(), "A");
EXPECT_EQ(client.GetAnswer()[0].GetData(), "127.0.0.1");
ASSERT_TRUE(client.Query("test.ddns.com AAAA", 60053));
std::cout << client.GetResult() << std::endl;
ASSERT_EQ(client.GetAnswerNum(), 1);
EXPECT_EQ(client.GetStatus(), "NOERROR");
EXPECT_EQ(client.GetAnswer()[0].GetName(), "test.ddns.com");
EXPECT_EQ(client.GetAnswer()[0].GetTTL(), 600);
EXPECT_EQ(client.GetAnswer()[0].GetType(), "AAAA");
EXPECT_EQ(client.GetAnswer()[0].GetData(), "::1");
ASSERT_TRUE(client.Query("test.ddns.org A", 60053));
std::cout << client.GetResult() << std::endl;
ASSERT_EQ(client.GetAnswerNum(), 1);
EXPECT_EQ(client.GetStatus(), "NOERROR");
EXPECT_EQ(client.GetAnswer()[0].GetName(), "test.ddns.org");
EXPECT_EQ(client.GetAnswer()[0].GetTTL(), 600);
EXPECT_EQ(client.GetAnswer()[0].GetType(), "A");
EXPECT_EQ(client.GetAnswer()[0].GetData(), "127.0.0.1");
ASSERT_TRUE(client.Query("test.ddns.org AAAA", 60053));
std::cout << client.GetResult() << std::endl;
ASSERT_EQ(client.GetAnswerNum(), 1);
EXPECT_EQ(client.GetStatus(), "NOERROR");
EXPECT_EQ(client.GetAnswer()[0].GetName(), "test.ddns.org");
EXPECT_EQ(client.GetAnswer()[0].GetTTL(), 600);
EXPECT_EQ(client.GetAnswer()[0].GetType(), "AAAA");
EXPECT_EQ(client.GetAnswer()[0].GetData(), "::1");
}

View File

@@ -39,17 +39,17 @@ TEST_F(DomainRule, bogus_nxdomain)
if (request->qtype != DNS_T_A) {
return smartdns::SERVER_REQUEST_SOA;
}
if (request->domain == "a.com") {
smartdns::MockServer::AddIP(request, request->domain.c_str(), "10.11.12.13", 611);
return smartdns::SERVER_REQUEST_OK;
}
if (request->domain == "a.com") {
smartdns::MockServer::AddIP(request, request->domain.c_str(), "10.11.12.13", 611);
return smartdns::SERVER_REQUEST_OK;
}
smartdns::MockServer::AddIP(request, request->domain.c_str(), "1.2.3.4", 611);
return smartdns::SERVER_REQUEST_OK;
});
/* this ip will be discard, but is reachable */
server.MockPing(PING_TYPE_ICMP, "1.2.3.4", 60, 10);
/* this ip will be discard, but is reachable */
server.MockPing(PING_TYPE_ICMP, "1.2.3.4", 60, 10);
server.Start(R"""(bind [::]:60053
server udp://127.0.0.1:61053 -blacklist-ip
@@ -64,7 +64,7 @@ cache-persist no)""");
ASSERT_EQ(client.GetAuthorityNum(), 1);
EXPECT_EQ(client.GetStatus(), "NXDOMAIN");
ASSERT_TRUE(client.Query("b.com", 60053));
ASSERT_TRUE(client.Query("b.com", 60053));
std::cout << client.GetResult() << std::endl;
ASSERT_EQ(client.GetAnswerNum(), 1);
EXPECT_EQ(client.GetStatus(), "NOERROR");

77
test/cases/test-idna.cc Normal file
View File

@@ -0,0 +1,77 @@
/*************************************************************************
*
* Copyright (C) 2018-2023 Ruilin Peng (Nick) <pymumu@gmail.com>.
*
* smartdns is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* smartdns is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "client.h"
#include "dns.h"
#include "include/utils.h"
#include "server.h"
#include "util.h"
#include "gtest/gtest.h"
#include <fstream>
class IDNA : public ::testing::Test
{
protected:
virtual void SetUp() {}
virtual void TearDown() {}
};
TEST_F(IDNA, match)
{
smartdns::MockServer server_upstream;
smartdns::Server server;
server_upstream.Start("udp://0.0.0.0:61053", [&](struct smartdns::ServerRequestContext *request) {
if (request->qtype == DNS_T_A) {
smartdns::MockServer::AddIP(request, request->domain.c_str(), "1.2.3.4", 700);
return smartdns::SERVER_REQUEST_OK;
} else if (request->qtype == DNS_T_AAAA) {
smartdns::MockServer::AddIP(request, request->domain.c_str(), "64:ff9b::102:304", 700);
return smartdns::SERVER_REQUEST_OK;
}
return smartdns::SERVER_REQUEST_SOA;
});
server.Start(R"""(bind [::]:60053
server 127.0.0.1:61053
log-num 0
log-console yes
log-level debug
speed-check-mode none
address /.com/10.10.10.10
address /.com/64:ff9b::1010:1010
cache-persist no)""");
smartdns::Client client;
ASSERT_TRUE(client.Query("xn--fiqs8s.com A", 60053));
std::cout << client.GetResult() << std::endl;
ASSERT_EQ(client.GetAnswerNum(), 1);
EXPECT_EQ(client.GetStatus(), "NOERROR");
EXPECT_EQ(client.GetAnswer()[0].GetName(), "xn--fiqs8s.com");
EXPECT_EQ(client.GetAnswer()[0].GetTTL(), 600);
EXPECT_EQ(client.GetAnswer()[0].GetType(), "A");
EXPECT_EQ(client.GetAnswer()[0].GetData(), "10.10.10.10");
ASSERT_TRUE(client.Query("xn--fiqs8s.com AAAA", 60053));
std::cout << client.GetResult() << std::endl;
ASSERT_EQ(client.GetAnswerNum(), 1);
EXPECT_EQ(client.GetStatus(), "NOERROR");
EXPECT_EQ(client.GetAnswer()[0].GetName(), "xn--fiqs8s.com");
EXPECT_EQ(client.GetAnswer()[0].GetTTL(), 600);
EXPECT_EQ(client.GetAnswer()[0].GetType(), "AAAA");
EXPECT_EQ(client.GetAnswer()[0].GetData(), "64:ff9b::1010:1010");
}

318
test/cases/test-ip-alias.cc Normal file
View File

@@ -0,0 +1,318 @@
/*************************************************************************
*
* Copyright (C) 2018-2023 Ruilin Peng (Nick) <pymumu@gmail.com>.
*
* smartdns is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* smartdns is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "client.h"
#include "dns.h"
#include "include/utils.h"
#include "server.h"
#include "gtest/gtest.h"
#include <fstream>
class IPAlias : public ::testing::Test
{
protected:
virtual void SetUp() {}
virtual void TearDown() {}
};
TEST(IPAlias, map_multiip_nospeed_check)
{
smartdns::MockServer server_upstream;
smartdns::Server server;
server_upstream.Start("udp://0.0.0.0:61053", [](struct smartdns::ServerRequestContext *request) {
std::string domain = request->domain;
if (request->domain.length() == 0) {
return smartdns::SERVER_REQUEST_ERROR;
}
if (request->qtype == DNS_T_A) {
unsigned char addr[][4] = {{1, 2, 3, 1}, {1, 2, 3, 2}, {1, 2, 3, 3}};
dns_add_A(request->response_packet, DNS_RRS_AN, domain.c_str(), 61, addr[0]);
dns_add_A(request->response_packet, DNS_RRS_AN, domain.c_str(), 61, addr[1]);
dns_add_A(request->response_packet, DNS_RRS_AN, domain.c_str(), 61, addr[2]);
} else if (request->qtype == DNS_T_AAAA) {
unsigned char addr[][16] = {{1, 2, 3, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 2, 3, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2},
{1, 2, 3, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3}};
dns_add_AAAA(request->response_packet, DNS_RRS_AN, domain.c_str(), 61, addr[0]);
dns_add_AAAA(request->response_packet, DNS_RRS_AN, domain.c_str(), 61, addr[1]);
dns_add_AAAA(request->response_packet, DNS_RRS_AN, domain.c_str(), 61, addr[2]);
} else {
return smartdns::SERVER_REQUEST_ERROR;
}
request->response_packet->head.rcode = DNS_RC_NOERROR;
return smartdns::SERVER_REQUEST_OK;
});
server.MockPing(PING_TYPE_ICMP, "1.2.3.4", 60, 100);
server.MockPing(PING_TYPE_ICMP, "5.6.7.8", 60, 110);
server.MockPing(PING_TYPE_ICMP, "9.10.11.12", 60, 140);
server.MockPing(PING_TYPE_ICMP, "10.10.10.10", 60, 120);
server.MockPing(PING_TYPE_ICMP, "11.11.11.11", 60, 150);
server.MockPing(PING_TYPE_ICMP, "0102:0304:0500::", 60, 100);
server.MockPing(PING_TYPE_ICMP, "0506:0708:0900::", 60, 110);
server.MockPing(PING_TYPE_ICMP, "0a0b:0c0d:0e00::", 60, 140);
server.MockPing(PING_TYPE_ICMP, "ffff::1", 60, 120);
server.MockPing(PING_TYPE_ICMP, "ffff::2", 60, 150);
server.Start(R"""(bind [::]:60053
server 127.0.0.1:61053
log-num 0
log-console yes
log-level debug
dualstack-ip-selection no
speed-check-mode none
ip-alias 1.2.3.0/24 10.10.10.10,12.12.12.12,13.13.13.13,15.15.15.15
ip-alias 0102::/16 FFFF::0001,FFFF::0002,FFFF::0003,FFFF::0004
cache-persist no)""");
smartdns::Client client;
ASSERT_TRUE(client.Query("a.com", 60053));
std::cout << client.GetResult() << std::endl;
ASSERT_EQ(client.GetAnswerNum(), 4);
EXPECT_EQ(client.GetStatus(), "NOERROR");
EXPECT_EQ(client.GetAnswer()[0].GetName(), "a.com");
EXPECT_EQ(client.GetAnswer()[0].GetData(), "10.10.10.10");
EXPECT_EQ(client.GetAnswer()[1].GetData(), "12.12.12.12");
EXPECT_EQ(client.GetAnswer()[2].GetData(), "15.15.15.15");
EXPECT_EQ(client.GetAnswer()[3].GetData(), "13.13.13.13");
ASSERT_TRUE(client.Query("a.com AAAA", 60053));
std::cout << client.GetResult() << std::endl;
ASSERT_EQ(client.GetAnswerNum(), 4);
EXPECT_EQ(client.GetStatus(), "NOERROR");
EXPECT_EQ(client.GetAnswer()[0].GetName(), "a.com");
EXPECT_EQ(client.GetAnswer()[0].GetData(), "ffff::1");
EXPECT_EQ(client.GetAnswer()[1].GetData(), "ffff::3");
EXPECT_EQ(client.GetAnswer()[2].GetData(), "ffff::2");
EXPECT_EQ(client.GetAnswer()[3].GetData(), "ffff::4");
}
TEST(IPAlias, map_single_ip_nospeed_check)
{
smartdns::MockServer server_upstream;
smartdns::Server server;
server_upstream.Start("udp://0.0.0.0:61053", [](struct smartdns::ServerRequestContext *request) {
std::string domain = request->domain;
if (request->domain.length() == 0) {
return smartdns::SERVER_REQUEST_ERROR;
}
if (request->qtype == DNS_T_A) {
unsigned char addr[][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}};
dns_add_A(request->response_packet, DNS_RRS_AN, domain.c_str(), 61, addr[0]);
dns_add_A(request->response_packet, DNS_RRS_AN, domain.c_str(), 61, addr[1]);
dns_add_A(request->response_packet, DNS_RRS_AN, domain.c_str(), 61, addr[2]);
} else if (request->qtype == DNS_T_AAAA) {
unsigned char addr[][16] = {{1, 2, 3, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{10, 11, 12, 13, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
dns_add_AAAA(request->response_packet, DNS_RRS_AN, domain.c_str(), 61, addr[0]);
dns_add_AAAA(request->response_packet, DNS_RRS_AN, domain.c_str(), 61, addr[1]);
dns_add_AAAA(request->response_packet, DNS_RRS_AN, domain.c_str(), 61, addr[2]);
} else {
return smartdns::SERVER_REQUEST_ERROR;
}
request->response_packet->head.rcode = DNS_RC_NOERROR;
return smartdns::SERVER_REQUEST_OK;
});
server.MockPing(PING_TYPE_ICMP, "1.2.3.4", 60, 100);
server.MockPing(PING_TYPE_ICMP, "5.6.7.8", 60, 110);
server.MockPing(PING_TYPE_ICMP, "9.10.11.12", 60, 140);
server.MockPing(PING_TYPE_ICMP, "10.10.10.10", 60, 120);
server.MockPing(PING_TYPE_ICMP, "11.11.11.11", 60, 150);
server.MockPing(PING_TYPE_ICMP, "0102:0304:0500::", 60, 100);
server.MockPing(PING_TYPE_ICMP, "0506:0708:0900::", 60, 110);
server.MockPing(PING_TYPE_ICMP, "0a0b:0c0d:0e00::", 60, 140);
server.MockPing(PING_TYPE_ICMP, "ffff::1", 60, 120);
server.MockPing(PING_TYPE_ICMP, "ffff::2", 60, 150);
server.Start(R"""(bind [::]:60053
server 127.0.0.1:61053
log-num 0
log-console yes
log-level debug
dualstack-ip-selection no
speed-check-mode none
ip-alias 1.2.3.4 10.10.10.10
ip-alias 5.6.7.8/32 11.11.11.11
ip-alias 0102:0304:0500:: ffff::1
ip-alias 0506:0708:0900:: ffff::2
cache-persist no)""");
smartdns::Client client;
ASSERT_TRUE(client.Query("a.com", 60053));
std::cout << client.GetResult() << std::endl;
ASSERT_EQ(client.GetAnswerNum(), 3);
EXPECT_EQ(client.GetStatus(), "NOERROR");
EXPECT_EQ(client.GetAnswer()[0].GetName(), "a.com");
EXPECT_EQ(client.GetAnswer()[0].GetData(), "10.10.10.10");
EXPECT_EQ(client.GetAnswer()[1].GetData(), "11.11.11.11");
EXPECT_EQ(client.GetAnswer()[2].GetData(), "9.10.11.12");
ASSERT_TRUE(client.Query("a.com AAAA", 60053));
std::cout << client.GetResult() << std::endl;
ASSERT_EQ(client.GetAnswerNum(), 3);
EXPECT_EQ(client.GetStatus(), "NOERROR");
EXPECT_EQ(client.GetAnswer()[0].GetName(), "a.com");
EXPECT_EQ(client.GetAnswer()[0].GetData(), "ffff::1");
EXPECT_EQ(client.GetAnswer()[1].GetData(), "a0b:c0d:e00::");
EXPECT_EQ(client.GetAnswer()[2].GetData(), "ffff::2");
}
TEST(IPAlias, mapip_withspeed_check)
{
smartdns::MockServer server_upstream;
smartdns::Server server;
server_upstream.Start("udp://0.0.0.0:61053", [](struct smartdns::ServerRequestContext *request) {
std::string domain = request->domain;
if (request->domain.length() == 0) {
return smartdns::SERVER_REQUEST_ERROR;
}
if (request->qtype == DNS_T_A) {
unsigned char addr[][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}};
dns_add_A(request->response_packet, DNS_RRS_AN, domain.c_str(), 61, addr[0]);
dns_add_A(request->response_packet, DNS_RRS_AN, domain.c_str(), 61, addr[1]);
dns_add_A(request->response_packet, DNS_RRS_AN, domain.c_str(), 61, addr[2]);
} else if (request->qtype == DNS_T_AAAA) {
unsigned char addr[][16] = {{1, 2, 3, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{10, 11, 12, 13, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
dns_add_AAAA(request->response_packet, DNS_RRS_AN, domain.c_str(), 61, addr[0]);
dns_add_AAAA(request->response_packet, DNS_RRS_AN, domain.c_str(), 61, addr[1]);
dns_add_AAAA(request->response_packet, DNS_RRS_AN, domain.c_str(), 61, addr[2]);
} else {
return smartdns::SERVER_REQUEST_ERROR;
}
request->response_packet->head.rcode = DNS_RC_NOERROR;
return smartdns::SERVER_REQUEST_OK;
});
server.MockPing(PING_TYPE_ICMP, "1.2.3.4", 60, 100);
server.MockPing(PING_TYPE_ICMP, "5.6.7.8", 60, 110);
server.MockPing(PING_TYPE_ICMP, "9.10.11.12", 60, 140);
server.MockPing(PING_TYPE_ICMP, "10.10.10.10", 60, 120);
server.MockPing(PING_TYPE_ICMP, "11.11.11.11", 60, 150);
server.MockPing(PING_TYPE_ICMP, "0102:0304:0500::", 60, 100);
server.MockPing(PING_TYPE_ICMP, "0506:0708:0900::", 60, 110);
server.MockPing(PING_TYPE_ICMP, "0a0b:0c0d:0e00::", 60, 140);
server.MockPing(PING_TYPE_ICMP, "ffff::1", 60, 120);
server.MockPing(PING_TYPE_ICMP, "ffff::2", 60, 150);
server.Start(R"""(bind [::]:60053
server 127.0.0.1:61053
log-num 0
log-console yes
log-level debug
dualstack-ip-selection no
ip-alias 1.2.3.4 10.10.10.10
ip-alias 5.6.7.8/32 11.11.11.11
ip-alias 0102::/16 ffff::1
ip-alias 0506::/16 ffff::2
cache-persist no)""");
smartdns::Client client;
ASSERT_TRUE(client.Query("a.com", 60053));
std::cout << client.GetResult() << std::endl;
ASSERT_EQ(client.GetAnswerNum(), 1);
EXPECT_EQ(client.GetStatus(), "NOERROR");
EXPECT_EQ(client.GetAnswer()[0].GetName(), "a.com");
EXPECT_EQ(client.GetAnswer()[0].GetData(), "10.10.10.10");
ASSERT_TRUE(client.Query("a.com AAAA", 60053));
std::cout << client.GetResult() << std::endl;
ASSERT_EQ(client.GetAnswerNum(), 1);
EXPECT_EQ(client.GetStatus(), "NOERROR");
EXPECT_EQ(client.GetAnswer()[0].GetName(), "a.com");
EXPECT_EQ(client.GetAnswer()[0].GetData(), "ffff::1");
}
TEST(IPAlias, no_ip_alias)
{
smartdns::MockServer server_upstream;
smartdns::Server server;
server_upstream.Start("udp://0.0.0.0:61053", [](struct smartdns::ServerRequestContext *request) {
std::string domain = request->domain;
if (request->domain.length() == 0) {
return smartdns::SERVER_REQUEST_ERROR;
}
if (request->qtype == DNS_T_A) {
unsigned char addr[][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}};
dns_add_A(request->response_packet, DNS_RRS_AN, domain.c_str(), 61, addr[0]);
dns_add_A(request->response_packet, DNS_RRS_AN, domain.c_str(), 61, addr[1]);
dns_add_A(request->response_packet, DNS_RRS_AN, domain.c_str(), 61, addr[2]);
} else if (request->qtype == DNS_T_AAAA) {
unsigned char addr[][16] = {{1, 2, 3, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{10, 11, 12, 13, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
dns_add_AAAA(request->response_packet, DNS_RRS_AN, domain.c_str(), 61, addr[0]);
dns_add_AAAA(request->response_packet, DNS_RRS_AN, domain.c_str(), 61, addr[1]);
dns_add_AAAA(request->response_packet, DNS_RRS_AN, domain.c_str(), 61, addr[2]);
} else {
return smartdns::SERVER_REQUEST_ERROR;
}
request->response_packet->head.rcode = DNS_RC_NOERROR;
return smartdns::SERVER_REQUEST_OK;
});
server.MockPing(PING_TYPE_ICMP, "1.2.3.4", 60, 100);
server.MockPing(PING_TYPE_ICMP, "5.6.7.8", 60, 110);
server.MockPing(PING_TYPE_ICMP, "9.10.11.12", 60, 140);
server.MockPing(PING_TYPE_ICMP, "10.10.10.10", 60, 120);
server.MockPing(PING_TYPE_ICMP, "11.11.11.11", 60, 150);
server.MockPing(PING_TYPE_ICMP, "0102:0304:0500::", 60, 100);
server.MockPing(PING_TYPE_ICMP, "0506:0708:0900::", 60, 110);
server.MockPing(PING_TYPE_ICMP, "0a0b:0c0d:0e00::", 60, 140);
server.MockPing(PING_TYPE_ICMP, "ffff::1", 60, 120);
server.MockPing(PING_TYPE_ICMP, "ffff::2", 60, 150);
server.Start(R"""(bind [::]:60053
server 127.0.0.1:61053
log-num 0
log-console yes
log-level debug
dualstack-ip-selection no
ip-alias 1.2.3.4 10.10.10.10
ip-alias 5.6.7.8/32 11.11.11.11
ip-alias 0102::/16 ffff::1
ip-alias 0506::/16 ffff::2
domain-rules /a.com/ -no-ip-alias
cache-persist no)""");
smartdns::Client client;
ASSERT_TRUE(client.Query("a.com", 60053));
std::cout << client.GetResult() << std::endl;
ASSERT_EQ(client.GetAnswerNum(), 1);
EXPECT_EQ(client.GetStatus(), "NOERROR");
EXPECT_EQ(client.GetAnswer()[0].GetName(), "a.com");
EXPECT_EQ(client.GetAnswer()[0].GetData(), "1.2.3.4");
ASSERT_TRUE(client.Query("a.com AAAA", 60053));
std::cout << client.GetResult() << std::endl;
ASSERT_EQ(client.GetAnswerNum(), 1);
EXPECT_EQ(client.GetStatus(), "NOERROR");
EXPECT_EQ(client.GetAnswer()[0].GetName(), "a.com");
EXPECT_EQ(client.GetAnswer()[0].GetData(), "102:304:500::");
}

View File

@@ -53,8 +53,8 @@ TEST_F(IPRule, white_list)
return smartdns::SERVER_REQUEST_OK;
});
/* this ip will be discard, but is reachable */
server.MockPing(PING_TYPE_ICMP, "1.2.3.4", 60, 10);
/* this ip will be discard, but is reachable */
server.MockPing(PING_TYPE_ICMP, "1.2.3.4", 60, 10);
server.Start(R"""(bind [::]:60053
server udp://127.0.0.1:61053 -whitelist-ip
@@ -73,6 +73,48 @@ cache-persist no)""");
EXPECT_EQ(client.GetAnswer()[0].GetData(), "4.5.6.7");
}
TEST_F(IPRule, white_list_not_in)
{
smartdns::MockServer server_upstream;
smartdns::MockServer server_upstream2;
smartdns::Server server;
server_upstream.Start("udp://0.0.0.0:61053", [](struct smartdns::ServerRequestContext *request) {
if (request->qtype != DNS_T_A) {
return smartdns::SERVER_REQUEST_SOA;
}
smartdns::MockServer::AddIP(request, request->domain.c_str(), "1.2.3.4", 611);
return smartdns::SERVER_REQUEST_OK;
});
server_upstream2.Start("udp://0.0.0.0:62053", [](struct smartdns::ServerRequestContext *request) {
if (request->qtype != DNS_T_A) {
return smartdns::SERVER_REQUEST_SOA;
}
smartdns::MockServer::AddIP(request, request->domain.c_str(), "9.10.11.12", 611);
return smartdns::SERVER_REQUEST_OK;
});
/* this ip will be discard, but is reachable */
server.MockPing(PING_TYPE_ICMP, "1.2.3.4", 60, 10);
server.Start(R"""(bind [::]:60053
server udp://127.0.0.1:61053 -whitelist-ip
server udp://127.0.0.1:62053 -whitelist-ip
whitelist-ip 4.5.6.7/24
log-num 0
log-console yes
log-level debug
cache-persist no)""");
smartdns::Client client;
ASSERT_TRUE(client.Query("a.com", 60053));
std::cout << client.GetResult() << std::endl;
ASSERT_EQ(client.GetAnswerNum(), 0);
EXPECT_EQ(client.GetStatus(), "SERVFAIL");
}
TEST_F(IPRule, black_list)
{
smartdns::MockServer server_upstream;
@@ -97,8 +139,8 @@ TEST_F(IPRule, black_list)
return smartdns::SERVER_REQUEST_OK;
});
/* this ip will be discard, but is reachable */
server.MockPing(PING_TYPE_ICMP, "4.5.6.7", 60, 10);
/* this ip will be discard, but is reachable */
server.MockPing(PING_TYPE_ICMP, "4.5.6.7", 60, 10);
server.Start(R"""(bind [::]:60053
server udp://127.0.0.1:61053 -blacklist-ip
@@ -134,10 +176,10 @@ TEST_F(IPRule, ignore_ip)
return smartdns::SERVER_REQUEST_OK;
});
/* this ip will be discard, but is reachable */
server.MockPing(PING_TYPE_ICMP, "1.2.3.4", 60, 10);
server.MockPing(PING_TYPE_ICMP, "4.5.6.7", 60, 90);
server.MockPing(PING_TYPE_ICMP, "7.8.9.10", 60, 40);
/* this ip will be discard, but is reachable */
server.MockPing(PING_TYPE_ICMP, "1.2.3.4", 60, 10);
server.MockPing(PING_TYPE_ICMP, "4.5.6.7", 60, 90);
server.MockPing(PING_TYPE_ICMP, "7.8.9.10", 60, 40);
server.Start(R"""(bind [::]:60053
server udp://127.0.0.1:61053 -blacklist-ip
@@ -154,3 +196,128 @@ cache-persist no)""");
EXPECT_EQ(client.GetAnswer()[0].GetName(), "a.com");
EXPECT_EQ(client.GetAnswer()[0].GetData(), "7.8.9.10");
}
TEST_F(IPRule, ignore_ip_set)
{
smartdns::MockServer server_upstream;
smartdns::MockServer server_upstream2;
smartdns::Server server;
std::string file = "/tmp/smartdns_test_ip_set.list" + smartdns::GenerateRandomString(5);
std::ofstream ofs(file);
ASSERT_TRUE(ofs.is_open());
Defer
{
ofs.close();
unlink(file.c_str());
};
server_upstream.Start("udp://0.0.0.0:61053", [](struct smartdns::ServerRequestContext *request) {
if (request->qtype != DNS_T_A) {
return smartdns::SERVER_REQUEST_SOA;
}
smartdns::MockServer::AddIP(request, request->domain.c_str(), "1.2.3.4", 611);
smartdns::MockServer::AddIP(request, request->domain.c_str(), "4.5.6.7", 611);
smartdns::MockServer::AddIP(request, request->domain.c_str(), "7.8.9.10", 611);
return smartdns::SERVER_REQUEST_OK;
});
/* this ip will be discard, but is reachable */
server.MockPing(PING_TYPE_ICMP, "1.2.3.4", 60, 10);
server.MockPing(PING_TYPE_ICMP, "4.5.6.7", 60, 90);
server.MockPing(PING_TYPE_ICMP, "7.8.9.10", 60, 40);
std::string ipset_list = R"""(
1.2.3.0/24
4.5.6.0/24
)""";
ofs.write(ipset_list.c_str(), ipset_list.length());
ofs.flush();
server.Start(R"""(bind [::]:60053
server udp://127.0.0.1:61053 -blacklist-ip
ip-set -name ip-list -file )""" +
file + R"""(
ignore-ip ip-set:ip-list
log-num 0
speed-check-mode none
log-console yes
log-level debug
cache-persist no)""");
smartdns::Client client;
ASSERT_TRUE(client.Query("a.com", 60053));
std::cout << client.GetResult() << std::endl;
ASSERT_EQ(client.GetAnswerNum(), 1);
EXPECT_EQ(client.GetStatus(), "NOERROR");
EXPECT_EQ(client.GetAnswer()[0].GetName(), "a.com");
EXPECT_EQ(client.GetAnswer()[0].GetData(), "7.8.9.10");
}
TEST_F(IPRule, ip_alias_ip_set)
{
smartdns::MockServer server_upstream;
smartdns::MockServer server_upstream2;
smartdns::Server server;
std::string file = "/tmp/smartdns_test_ip_set.list" + smartdns::GenerateRandomString(5);
std::string file_ip = "/tmp/smartdns_test_ip_set_ip.list" + smartdns::GenerateRandomString(5);
std::ofstream ofs(file);
std::ofstream ofs_ip(file_ip);
ASSERT_TRUE(ofs.is_open());
ASSERT_TRUE(ofs_ip.is_open());
Defer
{
ofs.close();
unlink(file.c_str());
ofs_ip.close();
unlink(file_ip.c_str());
};
server_upstream.Start("udp://0.0.0.0:61053", [](struct smartdns::ServerRequestContext *request) {
if (request->qtype != DNS_T_A) {
return smartdns::SERVER_REQUEST_SOA;
}
smartdns::MockServer::AddIP(request, request->domain.c_str(), "1.2.3.4", 611);
smartdns::MockServer::AddIP(request, request->domain.c_str(), "4.5.6.7", 611);
smartdns::MockServer::AddIP(request, request->domain.c_str(), "7.8.9.10", 611);
return smartdns::SERVER_REQUEST_OK;
});
server.MockPing(PING_TYPE_ICMP, "1.2.3.4", 60, 10);
server.MockPing(PING_TYPE_ICMP, "4.5.6.7", 60, 90);
server.MockPing(PING_TYPE_ICMP, "7.8.9.10", 60, 40);
std::string ipset_list = R"""(
1.2.3.0/24
4.5.6.0/24
7.8.9.0/24
)""";
ofs.write(ipset_list.c_str(), ipset_list.length());
ofs.flush();
std::string ipset_list_ip = R"""(
1.1.1.1
)""";
ofs_ip.write(ipset_list_ip.c_str(), ipset_list_ip.length());
ofs_ip.flush();
server.Start(R"""(bind [::]:60053
server udp://127.0.0.1:61053 -blacklist-ip
ip-set -name ip-list -file )""" +
file + R"""(
ip-set -name ip-list-ip -file )""" +
file_ip + R"""(
ip-alias ip-set:ip-list ip-set:ip-list-ip
log-num 0
speed-check-mode none
log-console yes
log-level debug
cache-persist no)""");
smartdns::Client client;
ASSERT_TRUE(client.Query("a.com", 60053));
std::cout << client.GetResult() << std::endl;
ASSERT_EQ(client.GetAnswerNum(), 1);
EXPECT_EQ(client.GetStatus(), "NOERROR");
EXPECT_EQ(client.GetAnswer()[0].GetName(), "a.com");
EXPECT_EQ(client.GetAnswer()[0].GetData(), "1.1.1.1");
}

View File

@@ -42,11 +42,11 @@ TEST_F(Ptr, query)
return smartdns::SERVER_REQUEST_OK;
}
if (request->qtype == DNS_T_PTR) {
dns_add_PTR(request->response_packet, DNS_RRS_AN, request->domain.c_str(), 30, "my-hostname");
request->response_packet->head.rcode = DNS_RC_NOERROR;
return smartdns::SERVER_REQUEST_OK;
}
if (request->qtype == DNS_T_PTR) {
dns_add_PTR(request->response_packet, DNS_RRS_AN, request->domain.c_str(), 30, "my-hostname");
request->response_packet->head.rcode = DNS_RC_NOERROR;
return smartdns::SERVER_REQUEST_OK;
}
return smartdns::SERVER_REQUEST_SOA;
});
@@ -127,11 +127,11 @@ TEST_F(Ptr, smartdns)
return smartdns::SERVER_REQUEST_OK;
}
if (request->qtype == DNS_T_PTR) {
dns_add_PTR(request->response_packet, DNS_RRS_AN, request->domain.c_str(), 30, "my-hostname");
request->response_packet->head.rcode = DNS_RC_NOERROR;
return smartdns::SERVER_REQUEST_OK;
}
if (request->qtype == DNS_T_PTR) {
dns_add_PTR(request->response_packet, DNS_RRS_AN, request->domain.c_str(), 30, "my-hostname");
request->response_packet->head.rcode = DNS_RC_NOERROR;
return smartdns::SERVER_REQUEST_OK;
}
return smartdns::SERVER_REQUEST_SOA;
});

317
test/cases/test-rule.cc Normal file
View File

@@ -0,0 +1,317 @@
/*************************************************************************
*
* Copyright (C) 2018-2023 Ruilin Peng (Nick) <pymumu@gmail.com>.
*
* smartdns is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* smartdns is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "client.h"
#include "dns.h"
#include "include/utils.h"
#include "server.h"
#include "util.h"
#include "gtest/gtest.h"
#include <fstream>
class Rule : public ::testing::Test
{
protected:
virtual void SetUp() {}
virtual void TearDown() {}
};
TEST_F(Rule, Match)
{
smartdns::MockServer server_upstream;
smartdns::Server server;
server_upstream.Start("udp://0.0.0.0:61053", [&](struct smartdns::ServerRequestContext *request) {
if (request->qtype == DNS_T_A) {
smartdns::MockServer::AddIP(request, request->domain.c_str(), "1.2.3.4", 700);
return smartdns::SERVER_REQUEST_OK;
} else if (request->qtype == DNS_T_AAAA) {
smartdns::MockServer::AddIP(request, request->domain.c_str(), "64:ff9b::102:304", 700);
return smartdns::SERVER_REQUEST_OK;
}
return smartdns::SERVER_REQUEST_SOA;
});
server.Start(R"""(bind [::]:60053
server 127.0.0.1:61053
log-num 0
log-console yes
log-level debug
speed-check-mode none
address /a.com/5.6.7.8
cache-persist no)""");
smartdns::Client client;
ASSERT_TRUE(client.Query("a.com A", 60053));
std::cout << client.GetResult() << std::endl;
ASSERT_EQ(client.GetAnswerNum(), 1);
EXPECT_EQ(client.GetStatus(), "NOERROR");
EXPECT_EQ(client.GetAnswer()[0].GetName(), "a.com");
EXPECT_EQ(client.GetAnswer()[0].GetTTL(), 600);
EXPECT_EQ(client.GetAnswer()[0].GetType(), "A");
EXPECT_EQ(client.GetAnswer()[0].GetData(), "5.6.7.8");
ASSERT_TRUE(client.Query("a.a.com A", 60053));
std::cout << client.GetResult() << std::endl;
ASSERT_EQ(client.GetAnswerNum(), 1);
EXPECT_EQ(client.GetStatus(), "NOERROR");
EXPECT_EQ(client.GetAnswer()[0].GetName(), "a.a.com");
EXPECT_EQ(client.GetAnswer()[0].GetTTL(), 600);
EXPECT_EQ(client.GetAnswer()[0].GetType(), "A");
EXPECT_EQ(client.GetAnswer()[0].GetData(), "5.6.7.8");
ASSERT_TRUE(client.Query("aa.com A", 60053));
std::cout << client.GetResult() << std::endl;
ASSERT_EQ(client.GetAnswerNum(), 1);
EXPECT_EQ(client.GetStatus(), "NOERROR");
EXPECT_EQ(client.GetAnswer()[0].GetName(), "aa.com");
EXPECT_EQ(client.GetAnswer()[0].GetTTL(), 700);
EXPECT_EQ(client.GetAnswer()[0].GetType(), "A");
EXPECT_EQ(client.GetAnswer()[0].GetData(), "1.2.3.4");
}
TEST_F(Rule, PrefixWildcardMatch)
{
smartdns::MockServer server_upstream;
smartdns::Server server;
server_upstream.Start("udp://0.0.0.0:61053", [&](struct smartdns::ServerRequestContext *request) {
if (request->qtype == DNS_T_A) {
smartdns::MockServer::AddIP(request, request->domain.c_str(), "1.2.3.4", 700);
return smartdns::SERVER_REQUEST_OK;
} else if (request->qtype == DNS_T_AAAA) {
smartdns::MockServer::AddIP(request, request->domain.c_str(), "64:ff9b::102:304", 700);
return smartdns::SERVER_REQUEST_OK;
}
return smartdns::SERVER_REQUEST_SOA;
});
server.Start(R"""(bind [::]:60053
server 127.0.0.1:61053
log-num 0
log-console yes
log-level debug
speed-check-mode none
address /*a.com/5.6.7.8
cache-persist no)""");
smartdns::Client client;
ASSERT_TRUE(client.Query("a.com A", 60053));
std::cout << client.GetResult() << std::endl;
ASSERT_EQ(client.GetAnswerNum(), 1);
EXPECT_EQ(client.GetStatus(), "NOERROR");
EXPECT_EQ(client.GetAnswer()[0].GetName(), "a.com");
EXPECT_EQ(client.GetAnswer()[0].GetTTL(), 600);
EXPECT_EQ(client.GetAnswer()[0].GetType(), "A");
EXPECT_EQ(client.GetAnswer()[0].GetData(), "5.6.7.8");
ASSERT_TRUE(client.Query("a.a.com A", 60053));
std::cout << client.GetResult() << std::endl;
ASSERT_EQ(client.GetAnswerNum(), 1);
EXPECT_EQ(client.GetStatus(), "NOERROR");
EXPECT_EQ(client.GetAnswer()[0].GetName(), "a.a.com");
EXPECT_EQ(client.GetAnswer()[0].GetTTL(), 600);
EXPECT_EQ(client.GetAnswer()[0].GetType(), "A");
EXPECT_EQ(client.GetAnswer()[0].GetData(), "5.6.7.8");
ASSERT_TRUE(client.Query("aa.com A", 60053));
std::cout << client.GetResult() << std::endl;
ASSERT_EQ(client.GetAnswerNum(), 1);
EXPECT_EQ(client.GetStatus(), "NOERROR");
EXPECT_EQ(client.GetAnswer()[0].GetName(), "aa.com");
EXPECT_EQ(client.GetAnswer()[0].GetTTL(), 600);
EXPECT_EQ(client.GetAnswer()[0].GetType(), "A");
EXPECT_EQ(client.GetAnswer()[0].GetData(), "5.6.7.8");
ASSERT_TRUE(client.Query("ab.com A", 60053));
std::cout << client.GetResult() << std::endl;
ASSERT_EQ(client.GetAnswerNum(), 1);
EXPECT_EQ(client.GetStatus(), "NOERROR");
EXPECT_EQ(client.GetAnswer()[0].GetName(), "ab.com");
EXPECT_EQ(client.GetAnswer()[0].GetTTL(), 700);
EXPECT_EQ(client.GetAnswer()[0].GetType(), "A");
EXPECT_EQ(client.GetAnswer()[0].GetData(), "1.2.3.4");
}
TEST_F(Rule, SubDomainMatchOnly)
{
smartdns::MockServer server_upstream;
smartdns::Server server;
server_upstream.Start("udp://0.0.0.0:61053", [&](struct smartdns::ServerRequestContext *request) {
if (request->qtype == DNS_T_A) {
smartdns::MockServer::AddIP(request, request->domain.c_str(), "1.2.3.4", 700);
return smartdns::SERVER_REQUEST_OK;
} else if (request->qtype == DNS_T_AAAA) {
smartdns::MockServer::AddIP(request, request->domain.c_str(), "64:ff9b::102:304", 700);
return smartdns::SERVER_REQUEST_OK;
}
return smartdns::SERVER_REQUEST_SOA;
});
server.Start(R"""(bind [::]:60053
server 127.0.0.1:61053
log-num 0
log-console yes
log-level debug
speed-check-mode none
address /*.a.com/5.6.7.8
cache-persist no)""");
smartdns::Client client;
ASSERT_TRUE(client.Query("a.com A", 60053));
std::cout << client.GetResult() << std::endl;
ASSERT_EQ(client.GetAnswerNum(), 1);
EXPECT_EQ(client.GetStatus(), "NOERROR");
EXPECT_EQ(client.GetAnswer()[0].GetName(), "a.com");
EXPECT_EQ(client.GetAnswer()[0].GetTTL(), 700);
EXPECT_EQ(client.GetAnswer()[0].GetType(), "A");
EXPECT_EQ(client.GetAnswer()[0].GetData(), "1.2.3.4");
ASSERT_TRUE(client.Query("a.a.com A", 60053));
std::cout << client.GetResult() << std::endl;
ASSERT_EQ(client.GetAnswerNum(), 1);
EXPECT_EQ(client.GetStatus(), "NOERROR");
EXPECT_EQ(client.GetAnswer()[0].GetName(), "a.a.com");
EXPECT_EQ(client.GetAnswer()[0].GetTTL(), 600);
EXPECT_EQ(client.GetAnswer()[0].GetType(), "A");
EXPECT_EQ(client.GetAnswer()[0].GetData(), "5.6.7.8");
ASSERT_TRUE(client.Query("aa.com A", 60053));
std::cout << client.GetResult() << std::endl;
ASSERT_EQ(client.GetAnswerNum(), 1);
EXPECT_EQ(client.GetStatus(), "NOERROR");
EXPECT_EQ(client.GetAnswer()[0].GetName(), "aa.com");
EXPECT_EQ(client.GetAnswer()[0].GetTTL(), 700);
EXPECT_EQ(client.GetAnswer()[0].GetType(), "A");
EXPECT_EQ(client.GetAnswer()[0].GetData(), "1.2.3.4");
}
TEST_F(Rule, RootDomainMatchOnly)
{
smartdns::MockServer server_upstream;
smartdns::Server server;
server_upstream.Start("udp://0.0.0.0:61053", [&](struct smartdns::ServerRequestContext *request) {
if (request->qtype == DNS_T_A) {
smartdns::MockServer::AddIP(request, request->domain.c_str(), "1.2.3.4", 700);
return smartdns::SERVER_REQUEST_OK;
} else if (request->qtype == DNS_T_AAAA) {
smartdns::MockServer::AddIP(request, request->domain.c_str(), "64:ff9b::102:304", 700);
return smartdns::SERVER_REQUEST_OK;
}
return smartdns::SERVER_REQUEST_SOA;
});
server.Start(R"""(bind [::]:60053
server 127.0.0.1:61053
log-num 0
log-console yes
log-level debug
speed-check-mode none
address /-.a.com/5.6.7.8
cache-persist no)""");
smartdns::Client client;
ASSERT_TRUE(client.Query("a.com A", 60053));
std::cout << client.GetResult() << std::endl;
ASSERT_EQ(client.GetAnswerNum(), 1);
EXPECT_EQ(client.GetStatus(), "NOERROR");
EXPECT_EQ(client.GetAnswer()[0].GetName(), "a.com");
EXPECT_EQ(client.GetAnswer()[0].GetTTL(), 600);
EXPECT_EQ(client.GetAnswer()[0].GetType(), "A");
EXPECT_EQ(client.GetAnswer()[0].GetData(), "5.6.7.8");
ASSERT_TRUE(client.Query("a.a.com A", 60053));
std::cout << client.GetResult() << std::endl;
ASSERT_EQ(client.GetAnswerNum(), 1);
EXPECT_EQ(client.GetStatus(), "NOERROR");
EXPECT_EQ(client.GetAnswer()[0].GetName(), "a.a.com");
EXPECT_EQ(client.GetAnswer()[0].GetTTL(), 700);
EXPECT_EQ(client.GetAnswer()[0].GetType(), "A");
EXPECT_EQ(client.GetAnswer()[0].GetData(), "1.2.3.4");
ASSERT_TRUE(client.Query("b.a.com A", 60053));
std::cout << client.GetResult() << std::endl;
ASSERT_EQ(client.GetAnswerNum(), 1);
EXPECT_EQ(client.GetStatus(), "NOERROR");
EXPECT_EQ(client.GetAnswer()[0].GetName(), "b.a.com");
EXPECT_EQ(client.GetAnswer()[0].GetTTL(), 700);
EXPECT_EQ(client.GetAnswer()[0].GetType(), "A");
EXPECT_EQ(client.GetAnswer()[0].GetData(), "1.2.3.4");
ASSERT_TRUE(client.Query("ba.com A", 60053));
std::cout << client.GetResult() << std::endl;
ASSERT_EQ(client.GetAnswerNum(), 1);
EXPECT_EQ(client.GetStatus(), "NOERROR");
EXPECT_EQ(client.GetAnswer()[0].GetName(), "ba.com");
EXPECT_EQ(client.GetAnswer()[0].GetTTL(), 700);
EXPECT_EQ(client.GetAnswer()[0].GetType(), "A");
EXPECT_EQ(client.GetAnswer()[0].GetData(), "1.2.3.4");
}
TEST_F(Rule, AAAA_SOA)
{
smartdns::MockServer server_upstream;
smartdns::Server server;
server_upstream.Start("udp://0.0.0.0:61053", [&](struct smartdns::ServerRequestContext *request) {
if (request->qtype == DNS_T_A) {
smartdns::MockServer::AddIP(request, request->domain.c_str(), "1.2.3.4", 700);
return smartdns::SERVER_REQUEST_OK;
} else if (request->qtype == DNS_T_AAAA) {
smartdns::MockServer::AddIP(request, request->domain.c_str(), "64:ff9b::102:304", 700);
return smartdns::SERVER_REQUEST_OK;
}
return smartdns::SERVER_REQUEST_SOA;
});
server.Start(R"""(bind [::]:60053
server 127.0.0.1:61053
log-num 0
log-console yes
log-level debug
speed-check-mode none
address /-.a.com/#6
address /*.b.com/#6
cache-persist no)""");
smartdns::Client client;
ASSERT_TRUE(client.Query("a.com AAAA", 60053));
std::cout << client.GetResult() << std::endl;
ASSERT_EQ(client.GetAnswerNum(), 0);
EXPECT_EQ(client.GetStatus(), "NOERROR");
ASSERT_TRUE(client.Query("a.a.com AAAA", 60053));
std::cout << client.GetResult() << std::endl;
ASSERT_EQ(client.GetAnswerNum(), 1);
EXPECT_EQ(client.GetStatus(), "NOERROR");
EXPECT_EQ(client.GetAnswer()[0].GetName(), "a.a.com");
EXPECT_EQ(client.GetAnswer()[0].GetTTL(), 700);
EXPECT_EQ(client.GetAnswer()[0].GetType(), "AAAA");
EXPECT_EQ(client.GetAnswer()[0].GetData(), "64:ff9b::102:304");
ASSERT_TRUE(client.Query("a.b.com AAAA", 60053));
std::cout << client.GetResult() << std::endl;
ASSERT_EQ(client.GetAnswerNum(), 0);
EXPECT_EQ(client.GetStatus(), "NOERROR");
ASSERT_TRUE(client.Query("b.com AAAA", 60053));
std::cout << client.GetResult() << std::endl;
ASSERT_EQ(client.GetAnswerNum(), 1);
EXPECT_EQ(client.GetStatus(), "NOERROR");
EXPECT_EQ(client.GetAnswer()[0].GetName(), "b.com");
EXPECT_EQ(client.GetAnswer()[0].GetTTL(), 700);
EXPECT_EQ(client.GetAnswer()[0].GetType(), "AAAA");
EXPECT_EQ(client.GetAnswer()[0].GetData(), "64:ff9b::102:304");
}

View File

@@ -44,7 +44,7 @@ TEST_F(Server, all_unreach)
return smartdns::SERVER_REQUEST_OK;
});
server.MockPing(PING_TYPE_ICMP, "2001::", 128, 10000);
server.MockPing(PING_TYPE_ICMP, "2001::", 128, 10000);
server.Start(R"""(bind [::]:60053
bind-tcp [::]:60053
server tls://255.255.255.255
@@ -58,11 +58,11 @@ cache-persist no)""");
ASSERT_TRUE(client.Query("a.com", 60053));
std::cout << client.GetResult() << std::endl;
EXPECT_EQ(client.GetStatus(), "SERVFAIL");
EXPECT_EQ(client.GetAnswerNum(), 0);
EXPECT_EQ(client.GetAnswerNum(), 0);
/* server should not crash */
ASSERT_TRUE(client.Query("a.com +tcp", 60053));
/* server should not crash */
ASSERT_TRUE(client.Query("a.com +tcp", 60053));
std::cout << client.GetResult() << std::endl;
EXPECT_EQ(client.GetStatus(), "SERVFAIL");
EXPECT_EQ(client.GetAnswerNum(), 0);
EXPECT_EQ(client.GetAnswerNum(), 0);
}

View File

@@ -99,7 +99,7 @@ cache-persist no)""");
std::cout << client.GetResult() << std::endl;
ASSERT_EQ(client.GetAnswerNum(), 2);
EXPECT_EQ(client.GetStatus(), "NOERROR");
EXPECT_LT(client.GetQueryTime(), 20);
EXPECT_LT(client.GetQueryTime(), 40);
EXPECT_EQ(client.GetAnswer()[0].GetName(), "b.com");
EXPECT_EQ(client.GetAnswer()[0].GetTTL(), 600);
@@ -107,7 +107,7 @@ cache-persist no)""");
std::cout << client.GetResult() << std::endl;
ASSERT_EQ(client.GetAnswerNum(), 2);
EXPECT_EQ(client.GetStatus(), "NOERROR");
EXPECT_LT(client.GetQueryTime(), 20);
EXPECT_LT(client.GetQueryTime(), 40);
EXPECT_EQ(client.GetAnswer()[0].GetName(), "a.com");
EXPECT_EQ(client.GetAnswer()[0].GetTTL(), 600);
EXPECT_EQ(client.GetAnswer()[0].GetData(), "1.2.3.4");
@@ -220,7 +220,6 @@ cache-persist no)""");
EXPECT_EQ(client.GetAnswer()[0].GetData(), "5.6.7.8");
}
TEST_F(SpeedCheck, tcp_faster_than_ping)
{
smartdns::MockServer server_upstream;

105
test/cases/test-srv.cc Normal file
View File

@@ -0,0 +1,105 @@
/*************************************************************************
*
* Copyright (C) 2018-2023 Ruilin Peng (Nick) <pymumu@gmail.com>.
*
* smartdns is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* smartdns is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "client.h"
#include "dns.h"
#include "include/utils.h"
#include "server.h"
#include "util.h"
#include "gtest/gtest.h"
#include <fstream>
class SRV : public ::testing::Test
{
protected:
virtual void SetUp() {}
virtual void TearDown() {}
};
TEST_F(SRV, query)
{
smartdns::MockServer server_upstream;
smartdns::Server server;
server_upstream.Start("udp://0.0.0.0:61053", [&](struct smartdns::ServerRequestContext *request) {
if (request->qtype != DNS_T_SRV) {
return smartdns::SERVER_REQUEST_SOA;
}
struct dns_packet *packet = request->response_packet;
dns_add_SRV(packet, DNS_RRS_AN, request->domain.c_str(), 603, 1, 1, 443, "www.example.com");
dns_add_SRV(packet, DNS_RRS_AN, request->domain.c_str(), 603, 1, 1, 443, "www1.example.com");
return smartdns::SERVER_REQUEST_OK;
});
server.Start(R"""(bind [::]:60053
server 127.0.0.1:61053
log-num 0
log-console yes
log-level debug
speed-check-mode none
cache-persist no)""");
smartdns::Client client;
ASSERT_TRUE(client.Query("_ldap._tcp.local.com SRV", 60053));
std::cout << client.GetResult() << std::endl;
ASSERT_EQ(client.GetAnswerNum(), 2);
EXPECT_EQ(client.GetStatus(), "NOERROR");
EXPECT_EQ(client.GetAnswer()[0].GetName(), "_ldap._tcp.local.com");
EXPECT_EQ(client.GetAnswer()[0].GetTTL(), 603);
EXPECT_EQ(client.GetAnswer()[0].GetType(), "SRV");
EXPECT_EQ(client.GetAnswer()[0].GetData(), "1 1 443 www.example.com.");
}
TEST_F(SRV, match)
{
smartdns::MockServer server_upstream;
smartdns::Server server;
server_upstream.Start("udp://0.0.0.0:61053", [&](struct smartdns::ServerRequestContext *request) {
if (request->qtype != DNS_T_SRV) {
return smartdns::SERVER_REQUEST_SOA;
}
struct dns_packet *packet = request->response_packet;
dns_add_SRV(packet, DNS_RRS_AN, request->domain.c_str(), 603, 1, 1, 443, "www.example.com");
dns_add_SRV(packet, DNS_RRS_AN, request->domain.c_str(), 603, 1, 1, 443, "www1.example.com");
return smartdns::SERVER_REQUEST_OK;
});
server.Start(R"""(bind [::]:60053
server 127.0.0.1:61053
log-num 0
log-console yes
log-level debug
srv-record /_ldap._tcp.local.com/www.a.com,443,1,1
srv-record /_ldap._tcp.local.com/www1.a.com,443,1,1
srv-record /_ldap._tcp.local.com/www2.a.com,443,1,1
speed-check-mode none
cache-persist no)""");
smartdns::Client client;
ASSERT_TRUE(client.Query("_ldap._tcp.local.com SRV", 60053));
std::cout << client.GetResult() << std::endl;
ASSERT_EQ(client.GetAnswerNum(), 3);
EXPECT_EQ(client.GetStatus(), "NOERROR");
EXPECT_EQ(client.GetAnswer()[0].GetName(), "_ldap._tcp.local.com");
EXPECT_EQ(client.GetAnswer()[0].GetTTL(), 600);
EXPECT_EQ(client.GetAnswer()[0].GetType(), "SRV");
EXPECT_EQ(client.GetAnswer()[0].GetData(), "1 1 443 www.a.com.");
}

View File

@@ -55,7 +55,7 @@ TEST_F(SubNet, pass_subnet)
for (i = 0; i < rr_count && rrs; i++, rrs = dns_get_rrs_next(request->packet, rrs)) {
memset(&ecs, 0, sizeof(ecs));
ret = dns_get_OPT_ECS(rrs, NULL, NULL, &ecs);
ret = dns_get_OPT_ECS(rrs, &ecs);
if (ret != 0) {
continue;
}
@@ -125,7 +125,7 @@ TEST_F(SubNet, conf)
for (i = 0; i < rr_count && rrs; i++, rrs = dns_get_rrs_next(request->packet, rrs)) {
memset(&ecs, 0, sizeof(ecs));
ret = dns_get_OPT_ECS(rrs, NULL, NULL, &ecs);
ret = dns_get_OPT_ECS(rrs, &ecs);
if (ret != 0) {
continue;
}
@@ -196,7 +196,7 @@ TEST_F(SubNet, conf_v6)
for (i = 0; i < rr_count && rrs; i++, rrs = dns_get_rrs_next(request->packet, rrs)) {
memset(&ecs, 0, sizeof(ecs));
ret = dns_get_OPT_ECS(rrs, NULL, NULL, &ecs);
ret = dns_get_OPT_ECS(rrs, &ecs);
if (ret != 0) {
continue;
}
@@ -245,6 +245,148 @@ cache-persist no)""");
EXPECT_EQ(client.GetAnswer()[0].GetData(), "2001:db8::1");
}
TEST_F(SubNet, v4_server_subnet_txt)
{
smartdns::MockServer server_upstream;
smartdns::Server server;
server_upstream.Start("udp://0.0.0.0:61053", [&](struct smartdns::ServerRequestContext *request) {
if (request->qtype != DNS_T_TXT) {
return smartdns::SERVER_REQUEST_SOA;
}
struct dns_opt_ecs ecs;
struct dns_rrs *rrs = NULL;
int rr_count = 0;
int i = 0;
int ret = 0;
int has_ecs = 0;
rr_count = 0;
rrs = dns_get_rrs_start(request->packet, DNS_RRS_OPT, &rr_count);
if (rr_count <= 0) {
return smartdns::SERVER_REQUEST_ERROR;
}
for (i = 0; i < rr_count && rrs; i++, rrs = dns_get_rrs_next(request->packet, rrs)) {
memset(&ecs, 0, sizeof(ecs));
ret = dns_get_OPT_ECS(rrs, &ecs);
if (ret != 0) {
continue;
}
has_ecs = 1;
break;
}
if (has_ecs == 0) {
return smartdns::SERVER_REQUEST_ERROR;
}
if (ecs.family != DNS_OPT_ECS_FAMILY_IPV4) {
return smartdns::SERVER_REQUEST_ERROR;
}
if (memcmp(ecs.addr, "\x08\x08\x08\x00", 4) != 0) {
return smartdns::SERVER_REQUEST_ERROR;
}
if (ecs.source_prefix != 24) {
return smartdns::SERVER_REQUEST_ERROR;
}
dns_add_TXT(request->response_packet, DNS_RRS_AN, request->domain.c_str(), 6, "hello world");
return smartdns::SERVER_REQUEST_OK;
});
server.Start(R"""(bind [::]:60053
server 127.0.0.1:61053 -subnet 8.8.8.8/24
log-num 0
log-console yes
dualstack-ip-selection no
log-level debug
rr-ttl-min 0
cache-persist no)""");
smartdns::Client client;
ASSERT_TRUE(client.Query("a.com TXT", 60053));
std::cout << client.GetResult() << std::endl;
ASSERT_EQ(client.GetAnswerNum(), 1);
EXPECT_EQ(client.GetStatus(), "NOERROR");
EXPECT_EQ(client.GetAnswer()[0].GetName(), "a.com");
EXPECT_EQ(client.GetAnswer()[0].GetTTL(), 6);
EXPECT_EQ(client.GetAnswer()[0].GetType(), "TXT");
EXPECT_EQ(client.GetAnswer()[0].GetData(), "\"hello world\"");
}
TEST_F(SubNet, v6_default_subnet_txt)
{
smartdns::MockServer server_upstream;
smartdns::Server server;
server_upstream.Start("udp://0.0.0.0:61053", [&](struct smartdns::ServerRequestContext *request) {
if (request->qtype != DNS_T_TXT) {
return smartdns::SERVER_REQUEST_SOA;
}
struct dns_opt_ecs ecs;
struct dns_rrs *rrs = NULL;
int rr_count = 0;
int i = 0;
int ret = 0;
int has_ecs = 0;
rr_count = 0;
rrs = dns_get_rrs_start(request->packet, DNS_RRS_OPT, &rr_count);
if (rr_count <= 0) {
return smartdns::SERVER_REQUEST_ERROR;
}
for (i = 0; i < rr_count && rrs; i++, rrs = dns_get_rrs_next(request->packet, rrs)) {
memset(&ecs, 0, sizeof(ecs));
ret = dns_get_OPT_ECS(rrs, &ecs);
if (ret != 0) {
continue;
}
has_ecs = 1;
break;
}
if (has_ecs == 0) {
return smartdns::SERVER_REQUEST_ERROR;
}
if (ecs.family != DNS_OPT_ECS_FAMILY_IPV6) {
return smartdns::SERVER_REQUEST_ERROR;
}
if (memcmp(ecs.addr, "\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00", 16) != 0) {
return smartdns::SERVER_REQUEST_ERROR;
}
if (ecs.source_prefix != 64) {
return smartdns::SERVER_REQUEST_ERROR;
}
dns_add_TXT(request->response_packet, DNS_RRS_AN, request->domain.c_str(), 6, "hello world");
return smartdns::SERVER_REQUEST_OK;
});
server.Start(R"""(bind [::]:60053
server 127.0.0.1:61053
log-num 0
log-console yes
dualstack-ip-selection no
rr-ttl-min 0
edns-client-subnet ffff:ffff:ffff:ffff:ffff::/64
log-level debug
cache-persist no)""");
smartdns::Client client;
ASSERT_TRUE(client.Query("a.com TXT", 60053));
std::cout << client.GetResult() << std::endl;
ASSERT_EQ(client.GetAnswerNum(), 1);
EXPECT_EQ(client.GetStatus(), "NOERROR");
EXPECT_EQ(client.GetAnswer()[0].GetName(), "a.com");
EXPECT_EQ(client.GetAnswer()[0].GetTTL(), 6);
EXPECT_EQ(client.GetAnswer()[0].GetType(), "TXT");
EXPECT_EQ(client.GetAnswer()[0].GetData(), "\"hello world\"");
}
TEST_F(SubNet, per_server)
{
smartdns::MockServer server_upstream1;
@@ -268,7 +410,7 @@ TEST_F(SubNet, per_server)
for (i = 0; i < rr_count && rrs; i++, rrs = dns_get_rrs_next(request->packet, rrs)) {
memset(&ecs, 0, sizeof(ecs));
ret = dns_get_OPT_ECS(rrs, NULL, NULL, &ecs);
ret = dns_get_OPT_ECS(rrs, &ecs);
if (ret != 0) {
continue;
}
@@ -300,7 +442,7 @@ TEST_F(SubNet, per_server)
for (i = 0; i < rr_count && rrs; i++, rrs = dns_get_rrs_next(request->packet, rrs)) {
memset(&ecs, 0, sizeof(ecs));
ret = dns_get_OPT_ECS(rrs, NULL, NULL, &ecs);
ret = dns_get_OPT_ECS(rrs, &ecs);
if (ret != 0) {
continue;
}
@@ -337,7 +479,7 @@ TEST_F(SubNet, per_server)
for (i = 0; i < rr_count && rrs; i++, rrs = dns_get_rrs_next(request->packet, rrs)) {
memset(&ecs, 0, sizeof(ecs));
ret = dns_get_OPT_ECS(rrs, NULL, NULL, &ecs);
ret = dns_get_OPT_ECS(rrs, &ecs);
if (ret != 0) {
continue;
}
@@ -382,7 +524,7 @@ TEST_F(SubNet, per_server)
for (i = 0; i < rr_count && rrs; i++, rrs = dns_get_rrs_next(request->packet, rrs)) {
memset(&ecs, 0, sizeof(ecs));
ret = dns_get_OPT_ECS(rrs, NULL, NULL, &ecs);
ret = dns_get_OPT_ECS(rrs, &ecs);
if (ret != 0) {
continue;
}

View File

@@ -376,23 +376,21 @@ bool Server::Start(const std::string &conf, enum CONF_TYPE type)
}
smartdns_reg_post_func(Server::StartPost, this);
smartdns_main(args.size(), argv, fds[1]);
smartdns_main(args.size(), argv, fds[1], 0);
_exit(1);
} else if (pid < 0) {
return false;
}
} else if (mode_ == CREATE_MODE_THREAD) {
thread_ = std::thread([&]() {
std::vector<std::string> args = {
"smartdns", "-f", "-x", "-c", conf_file_, "-p", "-",
};
std::vector<std::string> args = {"smartdns", "-f", "-x", "-c", conf_file_, "-p", "-", "-S"};
char *argv[args.size() + 1];
for (size_t i = 0; i < args.size(); i++) {
argv[i] = (char *)args[i].c_str();
}
smartdns_reg_post_func(Server::StartPost, this);
smartdns_main(args.size(), argv, fds[1]);
smartdns_main(args.size(), argv, fds[1], 1);
smartdns_reg_post_func(nullptr, nullptr);
});
} else {