Compare commits

..

39 Commits

Author SHA1 Message Date
Nick Peng
f2593b62aa dns-server: return NOTIMP when request opcode is not supported. 2022-12-04 13:55:24 +08:00
Nick Peng
f671c34e7c Support specific domain names to query IPV6, when setting force-AAAA-SOA yes 2022-12-03 15:03:34 +08:00
Nick Peng
dad31179d2 DNS: support parser TYPE65 RR. 2022-12-03 14:28:45 +08:00
Nick Peng
410047822d install-script: optimize linux install script 2022-12-01 22:19:34 +08:00
Nick Peng
f38d16a069 smartdns: reply NOTIMP when query is not supported. 2022-12-01 22:18:49 +08:00
Nick Peng
c8734d603b nftset: disable nftset when os not support 2022-11-29 23:04:48 +08:00
Nick Peng
bd31cc0a36 conf: force TYPE65 SOA enable by default 2022-11-23 22:29:56 +08:00
Nick Peng
5a356e577d nftset: Fix isssue of adding IP error when nftset has interval flag 2022-11-23 22:18:28 +08:00
Nick Peng
7d02843fea dns_conf: fix domain-rule speed check option bug 2022-11-21 23:45:49 +08:00
Nick Peng
b9429e04d2 fast_ping: make debug log happy 2022-11-20 11:43:59 +08:00
Nick Peng
bea238e7ae dns_conf: fix speed-check-mode option incorrect issue. 2022-11-20 00:33:05 +08:00
Nick Peng
ad4c2144da nftset: fix nft option ignore flag not working issue 2022-11-19 19:28:27 +08:00
Nick Peng
04985216a8 dns_cache: separate DNS cache 2022-11-17 23:51:15 +08:00
Nick Peng
324aa2d77d nftset: fix family option not working issue 2022-11-16 22:37:25 +08:00
Nick Peng
bc379a3c3e ping: increase ping work thread priority 2022-11-16 22:11:18 +08:00
Nick Peng
f6b9a1b81a dns-conf: support disable sni name by setting host-name name to '-' 2022-11-16 00:22:06 +08:00
Nick Peng
f619ca8f68 ping: Optimize the ping callback thread to reduce inaccurate results caused by blocking 2022-11-16 00:20:39 +08:00
Nick Peng
42a4fdebfd smartdns: fix setcap crash issue. 2022-11-15 22:39:36 +08:00
Nick Peng
85d011eae8 nftset: Remove libnftable dependency 2022-11-15 22:31:34 +08:00
Nick Peng
934701941b dns: skip padding record 2022-11-11 00:22:35 +08:00
Nick Peng
9974c50dbb dns_server: fixed rcode error issue 2022-11-10 19:35:55 +08:00
Nick Peng
93af473e08 dns_cache: optimize cache for no result record. 2022-11-08 23:31:15 +08:00
Nick Peng
76d5322676 conf: Make hostnames match exactly 2022-11-08 23:31:15 +08:00
Nick Peng
facf672081 dns_client: fix crash issue when upstream response delay is lower than 1ms 2022-11-07 22:15:45 +08:00
Nick Peng
5558e38cb4 fast-ping: fix dns ping not working issue 2022-11-05 17:46:28 +08:00
Nick Peng
ffd3cb2db5 dns-client: Fix issue that bootstrap dns not working caused by reducing CPU usage 2022-11-05 16:23:19 +08:00
Nick Peng
91ee65231e server: reply udp packet with original route path 2022-11-03 20:44:04 +08:00
Nick Peng
7b8ff57773 cpu-usage: reduce cpu usage when idle 2022-10-27 21:38:04 +08:00
LoveSy
391ef310b4 Simple support for nftables (#1117)
* Simple support for nftables

Co-authored-by: Chen Zhenge <Mr.ChenWithCapsule@outlook.com>
2022-10-23 19:17:33 +08:00
Nick Peng
51e1ba6897 update readme 2022-10-20 23:18:45 +08:00
Nick Peng
efc18996a8 luci: remove unnecessary permissions 2022-10-15 10:41:48 +08:00
soffchen
9bc857f628 smartdns: fix typo 2022-10-11 18:45:44 +08:00
YiiSh
8523f26c62 update smartdns start priority same with dnsmasq 2022-10-08 23:24:13 +08:00
Nick Peng
6c800ea12f luci: Fix RPC error when config is empty and add restart button. 2022-10-08 21:59:35 +08:00
Nick Peng
eeaadcf313 openwrt: remove deprecated option and restore dnsmasq when stop 2022-09-29 23:53:51 +08:00
Nick Peng
ccb47ef327 conf: fix local domain handling issues 2022-09-28 20:28:01 +08:00
HiGarfield
a13433abac Update smartdns.lua (#1092)
luci-compat: fix dnsmasq redirect status
2022-09-28 09:22:51 +08:00
Nick Peng
799d19f233 conf: add option resolv-hostname to skip resolve local hostname 2022-09-28 00:53:39 +08:00
Nick Peng
0fa17063c0 luci: avoid unnecessary configuration writes 2022-09-27 21:50:08 +08:00
31 changed files with 2527 additions and 371 deletions

0
.clang-format Executable file → Normal file
View File

1
.gitignore vendored
View File

@@ -2,3 +2,4 @@
*.o *.o
.DS_Store .DS_Store
*.swp. *.swp.
systemd/smartdns.service

View File

@@ -17,6 +17,8 @@ SmartDNS 同时支持指定特定域名 IP 地址,并高性匹配,可达到
- [特性](#特性) - [特性](#特性)
- [架构](#架构) - [架构](#架构)
- [下载](#下载) - [下载](#下载)
- [使用官方安装源](#使用官方安装源)
- [手工下载安装](#手工下载安装)
- [安装和使用](#安装和使用) - [安装和使用](#安装和使用)
- [标准 Linux 系统 / 树莓派](#标准-linux-系统--树莓派) - [标准 Linux 系统 / 树莓派](#标准-linux-系统--树莓派)
- [OpenWrt](#openwrt) - [OpenWrt](#openwrt)
@@ -121,6 +123,9 @@ rtt min/avg/max/mdev = 5.954/6.133/6.313/0.195 ms
9. **高性能、占用资源少** 9. **高性能、占用资源少**
多线程异步 IO 模式cache 缓存查询结果。 多线程异步 IO 模式cache 缓存查询结果。
10. **主流系统官方支持**
主流路由系统官方软件源安装smartdns。
## 架构 ## 架构
![Architecture](https://github.com/pymumu/test/releases/download/blob/architecture.png) ![Architecture](https://github.com/pymumu/test/releases/download/blob/architecture.png)
@@ -132,6 +137,19 @@ rtt min/avg/max/mdev = 5.954/6.133/6.313/0.195 ms
## 下载 ## 下载
### 使用官方安装源
smartdns已经合入主流系统的软件仓库可以直接使用系统安装命令直接安装。
系统|安装方式|说明|
--|--|--
openwrt|opkg update</br>opkg install luci-app-smartdns</br>opkg install smartdns|22.03之后的系统。软件源路径https://downloads.openwrt.org/releases/
ddwrt|官方最新固件service页面->SmartDNS Resolver->启用。|选择界面参考https://forum.dd-wrt.com/demo/Services.html
debian|apt-get install smartdns|
entware|ipkg update</br>ipkg install smartdns|软件源路径https://bin.entware.net/
### 手工下载安装
-------------- --------------
下载对应系统或固件版本的 SmartDNS 安装包,对应关系如下。 下载对应系统或固件版本的 SmartDNS 安装包,对应关系如下。
@@ -497,8 +515,8 @@ rtt min/avg/max/mdev = 5.954/6.133/6.313/0.195 ms
| 键名 | 功能说明 | 默认值 | 可用值/要求 | 举例 | | 键名 | 功能说明 | 默认值 | 可用值/要求 | 举例 |
| :--- | :--- | :--- | :--- | :--- | | :--- | :--- | :--- | :--- | :--- |
| server-name | DNS 服务器名称 | 操作系统主机名 / smartdns | 符合主机名规格的字符串 | server-name smartdns | | server-name | DNS 服务器名称 | 操作系统主机名 / smartdns | 符合主机名规格的字符串 | server-name smartdns |
| bind | DNS 监听端口号 | [::]:53 | 可绑定多个端口。<br>IP:PORT: 服务器 IP:端口号<br>[-group]: 请求时使用的 DNS 服务器组<br>[-no-rule-addr]:跳过 address 规则<br>[-no-rule-nameserver]:跳过 Nameserver 规则<br>[-no-rule-ipset]:跳过 ipset 规则<br>[-no-rule-soa]:跳过 SOA(#) 规则<br>[-no-dualstack-selection]:停用双栈测速<br>[-no-speed-check]:停用测速<br>[-no-cache]:停止缓存 | bind :53 | | bind | DNS 监听端口号 | [::]:53 | 可绑定多个端口。<br>IP:PORT: 服务器 IP:端口号<br>[-group]: 请求时使用的 DNS 服务器组<br>[-no-rule-addr]:跳过 address 规则<br>[-no-rule-nameserver]:跳过 Nameserver 规则<br>[-no-rule-ipset]:跳过 ipset 和 nftset 规则<br>[-no-rule-soa]:跳过 SOA(#) 规则<br>[-no-dualstack-selection]:停用双栈测速<br>[-no-speed-check]:停用测速<br>[-no-cache]:停止缓存 | bind :53 |
| bind-tcp | DNS TCP 监听端口号 | [::]:53 | 可绑定多个端口。<br>IP:PORT: 服务器 IP:端口号<br>[-group]: 请求时使用的 DNS 服务器组<br>[-no-rule-addr]:跳过 address 规则<br>[-no-rule-nameserver]:跳过 nameserver 规则<br>[-no-rule-ipset]:跳过 ipset 规则。<br>[-no-rule-soa]:跳过 SOA(#) 规则<br>[-no-dualstack-selection]:停用双栈测速<br>[-no-speed-check]:停用测速<br>[-no-cache]:停止缓存 | bind-tcp :53 | | bind-tcp | DNS TCP 监听端口号 | [::]:53 | 可绑定多个端口。<br>IP:PORT: 服务器 IP:端口号<br>[-group]: 请求时使用的 DNS 服务器组<br>[-no-rule-addr]:跳过 address 规则<br>[-no-rule-nameserver]:跳过 nameserver 规则<br>[-no-rule-ipset]:跳过 ipset 和 nftset 规则。<br>[-no-rule-soa]:跳过 SOA(#) 规则<br>[-no-dualstack-selection]:停用双栈测速<br>[-no-speed-check]:停用测速<br>[-no-cache]:停止缓存 | bind-tcp :53 |
| cache-size | 域名结果缓存个数 | 512 | 大于等于 0 的数字 | cache-size 512 | | cache-size | 域名结果缓存个数 | 512 | 大于等于 0 的数字 | cache-size 512 |
| cache-persist | 是否持久化缓存 | 自动。<br>当 cache-file 所在的位置有超过 128 MB 的可用空间时启用,否则禁用。 | [yes\|no] | cache-persist yes | | cache-persist | 是否持久化缓存 | 自动。<br>当 cache-file 所在的位置有超过 128 MB 的可用空间时启用,否则禁用。 | [yes\|no] | cache-persist yes |
| cache-file | 缓存持久化文件路径 | /tmp/smartdns.cache | 合法路径字符串 | cache-file /tmp/smartdns.cache | | cache-file | 缓存持久化文件路径 | /tmp/smartdns.cache | 合法路径字符串 | cache-file /tmp/smartdns.cache |
@@ -520,16 +538,19 @@ rtt min/avg/max/mdev = 5.954/6.133/6.313/0.195 ms
| conf-file | 附加配置文件 | 无 | 合法路径字符串 | conf-file /etc/smartdns/smartdns.more.conf | | conf-file | 附加配置文件 | 无 | 合法路径字符串 | conf-file /etc/smartdns/smartdns.more.conf |
| server | 上游 UDP DNS | 无 | 可重复。<br>[ip][:port]:服务器 IP:端口(可选)<br>[-blacklist-ip]:配置 IP 过滤结果。<br>[-whitelist-ip]:指定仅接受参数中配置的 IP 范围<br>[-group [group] ...]DNS 服务器所属组,比如 office 和 foreign和 nameserver 配套使用<br>[-exclude-default-group]:将 DNS 服务器从默认组中排除 | server 8.8.8.8:53 -blacklist-ip -group g1 | | server | 上游 UDP DNS | 无 | 可重复。<br>[ip][:port]:服务器 IP:端口(可选)<br>[-blacklist-ip]:配置 IP 过滤结果。<br>[-whitelist-ip]:指定仅接受参数中配置的 IP 范围<br>[-group [group] ...]DNS 服务器所属组,比如 office 和 foreign和 nameserver 配套使用<br>[-exclude-default-group]:将 DNS 服务器从默认组中排除 | server 8.8.8.8:53 -blacklist-ip -group g1 |
| server-tcp | 上游 TCP DNS | 无 | 可重复。<br>[ip][:port]:服务器 IP:端口(可选)<br>[-blacklist-ip]:配置 IP 过滤结果<br>[-whitelist-ip]:指定仅接受参数中配置的 IP 范围。<br>[-group [group] ...]DNS 服务器所属组,比如 office 和 foreign和 nameserver 配套使用<br>[-exclude-default-group]:将 DNS 服务器从默认组中排除 | server-tcp 8.8.8.8:53 | | server-tcp | 上游 TCP DNS | 无 | 可重复。<br>[ip][:port]:服务器 IP:端口(可选)<br>[-blacklist-ip]:配置 IP 过滤结果<br>[-whitelist-ip]:指定仅接受参数中配置的 IP 范围。<br>[-group [group] ...]DNS 服务器所属组,比如 office 和 foreign和 nameserver 配套使用<br>[-exclude-default-group]:将 DNS 服务器从默认组中排除 | server-tcp 8.8.8.8:53 |
| server-tls | 上游 TLS DNS | 无 | 可重复。<br>[ip][:port]:服务器 IP:端口(可选)<br>[-spki-pin [sha256-pin]]TLS 合法性校验 SPKI 值base64 编码的 sha256 SPKI pin 值<br>[-host-name]TLS SNI 名称<br>[-tls-host-verify]TLS 证书主机名校验<br> [-no-check-certificate]:跳过证书校验<br>[-blacklist-ip]:配置 IP 过滤结果<br>[-whitelist-ip]:仅接受参数中配置的 IP 范围<br>[-group [group] ...]DNS 服务器所属组,比如 office 和 foreign和 nameserver 配套使用<br>[-exclude-default-group]:将 DNS 服务器从默认组中排除 | server-tls 8.8.8.8:853 | | server-tls | 上游 TLS DNS | 无 | 可重复。<br>[ip][:port]:服务器 IP:端口(可选)<br>[-spki-pin [sha256-pin]]TLS 合法性校验 SPKI 值base64 编码的 sha256 SPKI pin 值<br>[-host-name]TLS SNI 名称, 名称设置为-表示停用SNI名称<br>[-tls-host-verify]TLS 证书主机名校验<br> [-no-check-certificate]:跳过证书校验<br>[-blacklist-ip]:配置 IP 过滤结果<br>[-whitelist-ip]:仅接受参数中配置的 IP 范围<br>[-group [group] ...]DNS 服务器所属组,比如 office 和 foreign和 nameserver 配套使用<br>[-exclude-default-group]:将 DNS 服务器从默认组中排除 | server-tls 8.8.8.8:853 |
| server-https | 上游 HTTPS DNS | 无 | 可重复。<br>https://[host][:port]/path服务器 IP:端口(可选)<br>[-spki-pin [sha256-pin]]TLS 合法性校验 SPKI 值base64 编码的 sha256 SPKI pin 值<br>[-host-name]TLS SNI 名称<br>[-http-host]http 协议头主机名<br>[-tls-host-verify]TLS 证书主机名校验<br> [-no-check-certificate]:跳过证书校验<br>[-blacklist-ip]:配置 IP 过滤结果<br>[-whitelist-ip]:仅接受参数中配置的 IP 范围。<br>[-group [group] ...]DNS 服务器所属组,比如 office 和 foreign和 nameserver 配套使用<br>[-exclude-default-group]:将 DNS 服务器从默认组中排除 | server-https https://cloudflare-dns.com/dns-query | | server-https | 上游 HTTPS DNS | 无 | 可重复。<br>https://[host][:port]/path服务器 IP:端口(可选)<br>[-spki-pin [sha256-pin]]TLS 合法性校验 SPKI 值base64 编码的 sha256 SPKI pin 值<br>[-host-name]TLS SNI 名称<br>[-http-host]http 协议头主机名<br>[-tls-host-verify]TLS 证书主机名校验<br> [-no-check-certificate]:跳过证书校验<br>[-blacklist-ip]:配置 IP 过滤结果<br>[-whitelist-ip]:仅接受参数中配置的 IP 范围。<br>[-group [group] ...]DNS 服务器所属组,比如 office 和 foreign和 nameserver 配套使用<br>[-exclude-default-group]:将 DNS 服务器从默认组中排除 | server-https https://cloudflare-dns.com/dns-query |
| speed-check-mode | 测速模式选择 | 无 | [ping\|tcp:[80]\|none] | speed-check-mode ping,tcp:80,tcp:443 | | speed-check-mode | 测速模式选择 | 无 | [ping\|tcp:[80]\|none] | speed-check-mode ping,tcp:80,tcp:443 |
| response-mode | 首次查询响应模式 | first-ping |模式:[fisrt-ping\|fastest-ip\|fastest-response]<br> [first-ping]: 最快ping响应地址模式DNS上游最快查询时延+ping时延最短查询等待与链接体验最佳;<br>[fastest-ip]: 最快IP地址模式查询到的所有IP地址中ping最短的IP。需等待IP测速; <br>[fastest-response]: 最快响应的DNS结果DNS查询等待时间最短返回的IP地址可能不是最快。| response-mode first-ping | | response-mode | 首次查询响应模式 | first-ping |模式:[fisrt-ping\|fastest-ip\|fastest-response]<br> [first-ping]: 最快ping响应地址模式DNS上游最快查询时延+ping时延最短查询等待与链接体验最佳;<br>[fastest-ip]: 最快IP地址模式查询到的所有IP地址中ping最短的IP。需等待IP测速; <br>[fastest-response]: 最快响应的DNS结果DNS查询等待时间最短返回的IP地址可能不是最快。| response-mode first-ping |
| address | 指定域名 IP 地址 | 无 | address /domain/[ip\|-\|-4\|-6\|#\|#4\|#6] <br>- 表示忽略 <br># 表示返回 SOA <br>4 表示 IPv4 <br>6 表示 IPv6 | address /www.example.com/1.2.3.4 | | address | 指定域名 IP 地址 | 无 | address /domain/[ip\|-\|-4\|-6\|#\|#4\|#6] <br>- 表示忽略 <br># 表示返回 SOA <br>4 表示 IPv4 <br>6 表示 IPv6 | address /www.example.com/1.2.3.4 |
| nameserver | 指定域名使用 server 组解析 | 无 | nameserver /domain/[group\|-], group 为组名,- 表示忽略此规则,配套 server 中的 -group 参数使用 | nameserver /www.example.com/office | | nameserver | 指定域名使用 server 组解析 | 无 | nameserver /domain/[group\|-], group 为组名,- 表示忽略此规则,配套 server 中的 -group 参数使用 | nameserver /www.example.com/office |
| ipset | 域名 ipset | 无 | ipset /domain/[ipset\|-\|#[4\|6]:[ipset\|-][,#[4\|6]:[ipset\|-]]]-表示忽略 | ipset /www.example.com/#4:dns4,#6:- | | ipset | 域名 ipset | 无 | ipset /domain/[ipset\|-\|#[4\|6]:[ipset\|-][,#[4\|6]:[ipset\|-]]]-表示忽略 | ipset /www.example.com/#4:dns4,#6:- |
| ipset-timeout | 设置 ipset 超时功能启用 | 自动 | [yes] | ipset-timeout yes | | ipset-timeout | 设置 ipset 超时功能启用 | no | [yes\|no] | ipset-timeout yes |
| domain-rules | 设置域名规则 | 无 | domain-rules /domain/ [-rules...]<br>[-c\|-speed-check-mode]:测速模式,参考 speed-check-mode 配置<br>[-a\|-address]:参考 address 配置<br>[-n\|-nameserver]:参考 nameserver 配置<br>[-p\|-ipset]参考ipset配置<br>[-d\|-dualstack-ip-selection]:参考 dualstack-ip-selection | domain-rules /www.example.com/ -speed-check-mode none | | nftset | 域名 nftset | 无 | nftset /domain/[#4\|#6\|-]:[family#nftable#nftset\|-][,#[4\|6]:[family#nftable#nftset\|-]]]-表示忽略ipv4 地址的 family 只支持 inet 和 ipipv6 地址的 family 只支持 inet 和 ip6由于 nft 限制,两种地址只能分开存放于两个 set 中。| nftset /www.example.com/#4:inet#mytab#dns4,#6:- |
| domain-set | 设置域名集合 | 无 | domain-set [options...]<br>[-n\|-name]:域名集合名称 <br>[-t\|-type]域名集合类型当前仅支持list格式为域名列表一行一个域名。<br>[-f\|-file]:域名集合文件路径。<br> 选项需要配合address, nameserver, ipset等需要指定域名的地方使用使用方式为 /domain-set:[name]/| domain-set -name set -type list -file /path/to/list <br> address /domain-set:set/1.2.4.8 | | nftset-timeout | 设置 nftset 超时功能启用 | no | [yes\|no] | nftset-timeout yes |
| nftset-debug | 设置 nftset 调试功能启用 | no | [yes\|no] | nftset-debug yes |
| domain-rules | 设置域名规则 | 无 | domain-rules /domain/ [-rules...]<br>[-c\|-speed-check-mode]:测速模式,参考 speed-check-mode 配置<br>[-a\|-address]:参考 address 配置<br>[-n\|-nameserver]:参考 nameserver 配置<br>[-p\|-ipset]参考ipset配置<br>[-t\|-nftset]参考nftset配置<br>[-d\|-dualstack-ip-selection]:参考 dualstack-ip-selection | domain-rules /www.example.com/ -speed-check-mode none |
| domain-set | 设置域名集合 | 无 | domain-set [options...]<br>[-n\|-name]:域名集合名称 <br>[-t\|-type]域名集合类型当前仅支持list格式为域名列表一行一个域名。<br>[-f\|-file]:域名集合文件路径。<br> 选项需要配合address, nameserver, ipset, nftset等需要指定域名的地方使用使用方式为 /domain-set:[name]/| domain-set -name set -type list -file /path/to/list <br> address /domain-set:set/1.2.4.8 |
| bogus-nxdomain | 假冒 IP 地址过滤 | 无 | [ip/subnet],可重复 | bogus-nxdomain 1.2.3.4/16 | | bogus-nxdomain | 假冒 IP 地址过滤 | 无 | [ip/subnet],可重复 | bogus-nxdomain 1.2.3.4/16 |
| ignore-ip | 忽略 IP 地址 | 无 | [ip/subnet],可重复 | ignore-ip 1.2.3.4/16 | | ignore-ip | 忽略 IP 地址 | 无 | [ip/subnet],可重复 | ignore-ip 1.2.3.4/16 |
| whitelist-ip | 白名单 IP 地址 | 无 | [ip/subnet],可重复 | whitelist-ip 1.2.3.4/16 | | whitelist-ip | 白名单 IP 地址 | 无 | [ip/subnet],可重复 | whitelist-ip 1.2.3.4/16 |
@@ -723,6 +744,9 @@ rtt min/avg/max/mdev = 5.954/6.133/6.313/0.195 ms
... ...
``` ```
14. 更多问题
如有更多问题请查阅或提交issue: [https://github.com/pymumu/smartdns/issues](https://github.com/pymumu/smartdns/issues)
## 编译 ## 编译
SmartDNS 提供了编译软件包的脚本(`package/build-pkg.sh`),支持编译 LuCI、Debian、OpenWrt 和 Optware 安装包。 SmartDNS 提供了编译软件包的脚本(`package/build-pkg.sh`),支持编译 LuCI、Debian、OpenWrt 和 Optware 安装包。

63
ReadMe_en.md Executable file → Normal file
View File

@@ -9,19 +9,27 @@ Support Raspberry Pi, openwrt, ASUS router, Windows and other devices.
## Table Of Content ## Table Of Content
1. [Software Show](#software-show) - [SmartDNS](#smartdns)
1. [Features](#features) - [Table Of Content](#table-of-content)
1. [Architecture](#architecture) - [Software Show](#software-show)
1. [Usage](#usage) - [Features](#features)
1. [Download the package](#download-the-package) - [Architecture](#architecture)
1. [Standard Linux system installation/Raspberry Pi, X86_64 system](#standard-linux-system-installation/raspberry-pi,-x86_64-system) - [Usage](#usage)
1. [openwrt/LEDE](#openwrt/lede) - [Use official installation source](#use-official-installation-source)
1. [ASUS router native firmware / Merlin firmware](#asus-router-native-firmware-/-merlin-firmware) - [Download the package](#download-the-package)
1. [optware/entware](#optware/entware) - [Standard Linux system installation/Raspberry Pi, X86_64 system](#standard-linux-system-installationraspberry-pi-x86_64-system)
1. [Windows 10 WSL Installation/WSL ubuntu](#windows-10-wsl-installation/wsl-ubuntu) - [openwrt](#openwrt)
1. [Configuration parameter](#configuration-parameter) - [ASUS router native firmware / Merlin firmware](#asus-router-native-firmware--merlin-firmware)
1. [Donate](#Donate) - [optware/entware](#optwareentware)
1. [FAQ](#FAQ) - [Windows 10 WSL Installation/WSL ubuntu](#windows-10-wsl-installationwsl-ubuntu)
- [Configuration parameter](#configuration-parameter)
- [FAQ](#faq)
- [Compile](#compile)
- [Donate](#donate)
- [PayPal](#paypal)
- [Alipay](#alipay)
- [Wechat](#wechat)
- [Open Source License](#open-source-license)
## Software Show ## Software Show
@@ -121,6 +129,17 @@ From the comparison, smartdns found the fastest IP address to visit www.baidu.co
## Usage ## Usage
### Use official installation source
smartdns can already be installed using system package management tools.
System|Installation|Instructions|
--|--|--
openwrt|opkg update</br>opkg install luci-app-smartdns</br>opkg install smartdns|systems after 22.03. Software source: https://downloads.openwrt.org/releases/
ddwrt|latest firmware. goto services page abd enable SmartDNS Resolver. |Demo: https://forum.dd-wrt.com/demo/Services.html
debian|apt-get install smartdns|
entware|ipkg update</br>ipkg install smartdns|Software source: https://bin.entware.net/
### Download the package ### Download the package
-------------- --------------
@@ -455,8 +474,8 @@ Note: Merlin firmware is derived from ASUS firmware and can theoretically be use
|parameter|Parameter function|Default value|Value type|Example| |parameter|Parameter function|Default value|Value type|Example|
|--|--|--|--|--| |--|--|--|--|--|
|server-name|DNS name|host name/smartdns|any string like hosname|server-name smartdns |server-name|DNS name|host name/smartdns|any string like hosname|server-name smartdns
|bind|DNS listening port number|[::]:53|Support binding multiple ports<br>`IP:PORT`: server IP, port number. <br>`[-group]`: The DNS server group used when requesting. <br>`[-no-rule-addr]`: Skip the address rule. <br>`[-no-rule-nameserver]`: Skip the Nameserver rule. <br>`[-no-rule-ipset]`: Skip the Ipset rule. <br>`[-no-rule-soa]`: Skip address SOA(#) rules.<br>`[-no-dualstack-selection]`: Disable dualstack ip selection.<br>`[-no-speed-check]`: Disable speed measurement. <br>`[-no-cache]`: stop caching |bind :53 |bind|DNS listening port number|[::]:53|Support binding multiple ports<br>`IP:PORT`: server IP, port number. <br>`[-group]`: The DNS server group used when requesting. <br>`[-no-rule-addr]`: Skip the address rule. <br>`[-no-rule-nameserver]`: Skip the Nameserver rule. <br>`[-no-rule-ipset]`: Skip the Ipset or nftset rules. <br>`[-no-rule-soa]`: Skip address SOA(#) rules.<br>`[-no-dualstack-selection]`: Disable dualstack ip selection.<br>`[-no-speed-check]`: Disable speed measurement. <br>`[-no-cache]`: stop caching |bind :53
|bind-tcp|TCP mode DNS listening port number|[::]:53|Support binding multiple ports<br>`IP:PORT`: server IP, port number. <br>`[-group]`: The DNS server group used when requesting. <br>`[-no-rule-addr]`: Skip the address rule. <br>`[-no-rule-nameserver]`: Skip the Nameserver rule. <br>`[-no-rule-ipset]`: Skip the Ipset rule. <br>`[-no-rule-soa]`: Skip address SOA(#) rules.<br>`[-no-dualstack-selection]`: Disable dualstack ip selection.<br>`[-no-speed-check]`: Disable speed measurement. <br>`[-no-cache]`: stop caching |bind-tcp :53 |bind-tcp|TCP mode DNS listening port number|[::]:53|Support binding multiple ports<br>`IP:PORT`: server IP, port number. <br>`[-group]`: The DNS server group used when requesting. <br>`[-no-rule-addr]`: Skip the address rule. <br>`[-no-rule-nameserver]`: Skip the Nameserver rule. <br>`[-no-rule-ipset]`: Skip the Ipset or nftset rules. <br>`[-no-rule-soa]`: Skip address SOA(#) rules.<br>`[-no-dualstack-selection]`: Disable dualstack ip selection.<br>`[-no-speed-check]`: Disable speed measurement. <br>`[-no-cache]`: stop caching |bind-tcp :53
|cache-size|Domain name result cache number|512|integer|cache-size 512 |cache-size|Domain name result cache number|512|integer|cache-size 512
|cache-persist|enable persist cache|Auto: Enabled if the location of `cache-file` has more than 128MB of free space.|[yes\|no]|cache-persist yes |cache-persist|enable persist cache|Auto: Enabled if the location of `cache-file` has more than 128MB of free space.|[yes\|no]|cache-persist yes
|cache-file|cache persist file|/tmp/smartdns.cache|路径|cache-file /tmp/smartdns.cache |cache-file|cache persist file|/tmp/smartdns.cache|路径|cache-file /tmp/smartdns.cache
@@ -478,16 +497,19 @@ Note: Merlin firmware is derived from ASUS firmware and can theoretically be use
|conf-file|additional conf file|None|File path|conf-file /etc/smartdns/smartdns.more.conf |conf-file|additional conf file|None|File path|conf-file /etc/smartdns/smartdns.more.conf
|server|Upstream UDP DNS server|None|Repeatable <br>`[ip][:port]`: Server IP, port optional. <br>`[-blacklist-ip]`: The "-blacklist-ip" parameter is to filtering IPs which is configured by "blacklist-ip". <br>`[-whitelist-ip]`: whitelist-ip parameter specifies that only the IP range configured in whitelist-ip is accepted. <br>`[-group [group] ...]`: The group to which the DNS server belongs, such as office, foreign, use with nameserver. <br>`[-exclude-default-group]`: Exclude DNS servers from the default group| server 8.8.8.8:53 -blacklist-ip |server|Upstream UDP DNS server|None|Repeatable <br>`[ip][:port]`: Server IP, port optional. <br>`[-blacklist-ip]`: The "-blacklist-ip" parameter is to filtering IPs which is configured by "blacklist-ip". <br>`[-whitelist-ip]`: whitelist-ip parameter specifies that only the IP range configured in whitelist-ip is accepted. <br>`[-group [group] ...]`: The group to which the DNS server belongs, such as office, foreign, use with nameserver. <br>`[-exclude-default-group]`: Exclude DNS servers from the default group| server 8.8.8.8:53 -blacklist-ip
|server-tcp|Upstream TCP DNS server|None|Repeatable <br>`[ip][:port]`: Server IP, port optional. <br>`[-blacklist-ip]`: The "-blacklist-ip" parameter is to filtering IPs which is configured by "blacklist-ip". <br>`[-whitelist-ip]`: whitelist-ip parameter specifies that only the IP range configured in whitelist-ip is accepted. <br>`[-group [group] ...]`: The group to which the DNS server belongs, such as office, foreign, use with nameserver. <br>`[-exclude-default-group]`: Exclude DNS servers from the default group| server-tcp 8.8.8.8:53 |server-tcp|Upstream TCP DNS server|None|Repeatable <br>`[ip][:port]`: Server IP, port optional. <br>`[-blacklist-ip]`: The "-blacklist-ip" parameter is to filtering IPs which is configured by "blacklist-ip". <br>`[-whitelist-ip]`: whitelist-ip parameter specifies that only the IP range configured in whitelist-ip is accepted. <br>`[-group [group] ...]`: The group to which the DNS server belongs, such as office, foreign, use with nameserver. <br>`[-exclude-default-group]`: Exclude DNS servers from the default group| server-tcp 8.8.8.8:53
|server-tls|Upstream TLS DNS server|None|Repeatable <br>`[ip][:port]`: Server IP, port optional. <br>`[-spki-pin [sha256-pin]]`: TLS verify SPKI value, a base64 encoded SHA256 hash<br>`[-host-name]`:TLS Server name. <br>`[-tls-host-verify]`: TLS cert hostname to verify. <br>`-no-check-certificate:`: No check certificate. <br>`[-blacklist-ip]`: The "-blacklist-ip" parameter is to filtering IPs which is configured by "blacklist-ip". <br>`[-whitelist-ip]`: whitelist-ip parameter specifies that only the IP range configured in whitelist-ip is accepted. <br>`[-group [group] ...]`: The group to which the DNS server belongs, such as office, foreign, use with nameserver. <br>`[-exclude-default-group]`: Exclude DNS servers from the default group| server-tls 8.8.8.8:853 |server-tls|Upstream TLS DNS server|None|Repeatable <br>`[ip][:port]`: Server IP, port optional. <br>`[-spki-pin [sha256-pin]]`: TLS verify SPKI value, a base64 encoded SHA256 hash<br>`[-host-name]`:TLS Server name. `-` to disable SNI name.<br>`[-tls-host-verify]`: TLS cert hostname to verify. <br>`-no-check-certificate:`: No check certificate. <br>`[-blacklist-ip]`: The "-blacklist-ip" parameter is to filtering IPs which is configured by "blacklist-ip". <br>`[-whitelist-ip]`: whitelist-ip parameter specifies that only the IP range configured in whitelist-ip is accepted. <br>`[-group [group] ...]`: The group to which the DNS server belongs, such as office, foreign, use with nameserver. <br>`[-exclude-default-group]`: Exclude DNS servers from the default group| server-tls 8.8.8.8:853
|server-https|Upstream HTTPS DNS server|None|Repeatable <br>`https://[host][:port]/path`: Server IP, port optional. <br>`[-spki-pin [sha256-pin]]`: TLS verify SPKI value, a base64 encoded SHA256 hash<br>`[-host-name]`:TLS Server name<br>`[-http-host]`http header host. <br>`[-tls-host-verify]`: TLS cert hostname to verify. <br>`-no-check-certificate:`: No check certificate. <br>`[-blacklist-ip]`: The "-blacklist-ip" parameter is to filtering IPs which is configured by "blacklist-ip". <br>`[-whitelist-ip]`: whitelist-ip parameter specifies that only the IP range configured in whitelist-ip is accepted. <br>`[-group [group] ...]`: The group to which the DNS server belongs, such as office, foreign, use with nameserver. <br>`[-exclude-default-group]`: Exclude DNS servers from the default group| server-https https://cloudflare-dns.com/dns-query |server-https|Upstream HTTPS DNS server|None|Repeatable <br>`https://[host][:port]/path`: Server IP, port optional. <br>`[-spki-pin [sha256-pin]]`: TLS verify SPKI value, a base64 encoded SHA256 hash<br>`[-host-name]`:TLS Server name<br>`[-http-host]`http header host. <br>`[-tls-host-verify]`: TLS cert hostname to verify. <br>`-no-check-certificate:`: No check certificate. <br>`[-blacklist-ip]`: The "-blacklist-ip" parameter is to filtering IPs which is configured by "blacklist-ip". <br>`[-whitelist-ip]`: whitelist-ip parameter specifies that only the IP range configured in whitelist-ip is accepted. <br>`[-group [group] ...]`: The group to which the DNS server belongs, such as office, foreign, use with nameserver. <br>`[-exclude-default-group]`: Exclude DNS servers from the default group| server-https https://cloudflare-dns.com/dns-query
|speed-check-mode|Speed mode|None|[ping\|tcp:[80]\|none]|speed-check-mode ping,tcp:80,tcp:443 |speed-check-mode|Speed mode|None|[ping\|tcp:[80]\|none]|speed-check-mode ping,tcp:80,tcp:443
|response-mode|First query response mode|first-ping|Mode: [fisrt-ping\|fastest-ip\|fastest-response]<br> [first-ping]: The fastest dns + ping response mode, DNS query delay + ping delay is the shortest;<br>[fastest-ip]: The fastest IP address mode, return the fastest ip address, may take some time to test speed. <br>[fastest-response]: The fastest response DNS result mode, the DNS query waiting time is the shortest. | response-mode first-ping | |response-mode|First query response mode|first-ping|Mode: [fisrt-ping\|fastest-ip\|fastest-response]<br> [first-ping]: The fastest dns + ping response mode, DNS query delay + ping delay is the shortest;<br>[fastest-ip]: The fastest IP address mode, return the fastest ip address, may take some time to test speed. <br>[fastest-response]: The fastest response DNS result mode, the DNS query waiting time is the shortest. | response-mode first-ping |
|address|Domain IP address|None|address /domain/[ip\|-\|-4\|-6\|#\|#4\|#6], `-` for ignore, `#` for return SOA, `4` for IPV4, `6` for IPV6| address /www.example.com/1.2.3.4 |address|Domain IP address|None|address /domain/[ip\|-\|-4\|-6\|#\|#4\|#6], `-` for ignore, `#` for return SOA, `4` for IPV4, `6` for IPV6| address /www.example.com/1.2.3.4
|nameserver|To query domain with specific server group|None|nameserver /domain/[group\|-], `group` is the group name, `-` means ignore this rule, use the `-group` parameter in the related server|nameserver /www.example.com/office |nameserver|To query domain with specific server group|None|nameserver /domain/[group\|-], `group` is the group name, `-` means ignore this rule, use the `-group` parameter in the related server|nameserver /www.example.com/office
|ipset|Domain IPSet|None|ipset /domain/[ipset\|-\|#[4\|6]:[ipset\|-][,#[4\|6]:[ipset\|-]]], `-` for ignore|ipset /www.example.com/#4:dns4,#6:- |ipset|Domain IPSet|None|ipset /domain/[ipset\|-\|#[4\|6]:[ipset\|-][,#[4\|6]:[ipset\|-]]], `-` for ignore|ipset /www.example.com/#4:dns4,#6:-
|ipset-timeout|ipset timeout enable|auto|[yes]|ipset-timeout yes |ipset-timeout|ipset timeout enable|no|[yes\|no]|ipset-timeout yes
|domain-rules|set domain rules|None|domain-rules /domain/ [-rules...]<br>`[-c\|-speed-check-mode]`: set speed check modesame as parameter `speed-check-mode`<br>`[-a\|-address]`: same as parameter `address` <br>`[-n\|-nameserver]`: same as parameter `nameserver`<br>`[-p\|-ipset]`: same as parameter `ipset`<br>`[-d\|-dualstack-ip-selection]`: same as parameter `dualstack-ip-selection`|domain-rules /www.example.com/ -speed-check-mode none |nftset|Domain nftset|None|nftset /domain/[#4\|#6\|-]:[family#nftable#nftset\|-][,#[4\|6]:[family#nftable#nftset\|-]]], `-` to ignore; the valid families are inet and ip for ipv4 addresses while the valid ones are inet and ip6 for ipv6 addresses; due to the limitation of nft, two types of addresses have to be stored in two sets|nftset /www.example.com/#4:inet#mytab#dns4,#6:-
| domain-set | collection of domains|None| domain-set [options...]<br>[-n\|-name]name of set <br>[-t\|-type] [list]: set type, only support list, one domain per line <br>[-f\|-file]file path of domain set<br> used with address, nameserver, ipset, example: /domain-set:[name]/ | domain-set -name set -type list -file /path/to/list <br> address /domain-set:set/1.2.4.8 | |nftset-timeout|nftset timeout enable|no|[yes\|no]|nftset-timeout yes
|nftset-debug|nftset debug enable|no|[yes\|no]|nftset-debug yes
|domain-rules|set domain rules|None|domain-rules /domain/ [-rules...]<br>`[-c\|-speed-check-mode]`: set speed check modesame as parameter `speed-check-mode`<br>`[-a\|-address]`: same as parameter `address` <br>`[-n\|-nameserver]`: same as parameter `nameserver`<br>`[-p\|-ipset]`: same as parameter `nftset`<br>`[-t\|-nftset]`: same as parameter `nftset`<br>`[-d\|-dualstack-ip-selection]`: same as parameter `dualstack-ip-selection`|domain-rules /www.example.com/ -speed-check-mode none
| domain-set | collection of domains|None| domain-set [options...]<br>[-n\|-name]name of set <br>[-t\|-type] [list]: set type, only support list, one domain per line <br>[-f\|-file]file path of domain set<br> used with address, nameserver, ipset, nftset, example: /domain-set:[name]/ | domain-set -name set -type list -file /path/to/list <br> address /domain-set:set/1.2.4.8 |
|bogus-nxdomain|bogus IP address|None|[IP/subnet], Repeatable| bogus-nxdomain 1.2.3.4/16 |bogus-nxdomain|bogus IP address|None|[IP/subnet], Repeatable| bogus-nxdomain 1.2.3.4/16
|ignore-ip|ignore ip address|None|[ip/subnet], Repeatable| ignore-ip 1.2.3.4/16 |ignore-ip|ignore ip address|None|[ip/subnet], Repeatable| ignore-ip 1.2.3.4/16
|whitelist-ip|ip whitelist|None|[ip/subnet], RepeatableWhen the filtering server responds IPs in the IP whitelist, only result in whitelist will be accepted| whitelist-ip 1.2.3.4/16 |whitelist-ip|ip whitelist|None|[ip/subnet], RepeatableWhen the filtering server responds IPs in the IP whitelist, only result in whitelist will be accepted| whitelist-ip 1.2.3.4/16
@@ -664,6 +686,9 @@ Note: Merlin firmware is derived from ASUS firmware and can theoretically be use
... ...
```` ````
1. More questions
More questions, please read issue: [https://github.com/pymumu/smartdns/issues](https://github.com/pymumu/smartdns/issues)
## Compile ## Compile
smartdns contains scripts for compiling packages, supports compiling luci, debian, openwrt, opare installation packages, and can execute `package/build-pkg.sh` compilation. smartdns contains scripts for compiling packages, supports compiling luci, debian, openwrt, opare installation packages, and can execute `package/build-pkg.sh` compilation.

View File

@@ -4,6 +4,9 @@
# server-name smartdns # server-name smartdns
# #
# whether resolv local hostname to ip address
# resolv-hostname yes
# dns server run user # dns server run user
# user [username] # user [username]
# example: run as nobody # example: run as nobody
@@ -23,7 +26,7 @@
# -group: set domain request to use the appropriate server group. # -group: set domain request to use the appropriate server group.
# -no-rule-addr: skip address rule. # -no-rule-addr: skip address rule.
# -no-rule-nameserver: skip nameserver rule. # -no-rule-nameserver: skip nameserver rule.
# -no-rule-ipset: skip ipset rule. # -no-rule-ipset: skip ipset rule or nftset rule.
# -no-speed-check: do not check speed. # -no-speed-check: do not check speed.
# -no-cache: skip cache. # -no-cache: skip cache.
# -no-rule-soa: Skip address SOA(#) rules. # -no-rule-soa: Skip address SOA(#) rules.
@@ -93,6 +96,7 @@ cache-size 16384
# force specific qtype return soa # force specific qtype return soa
# force-qtype-SOA [qtypeid |...] # force-qtype-SOA [qtypeid |...]
# force-qtype-SOA 65 28 # force-qtype-SOA 65 28
force-qtype-SOA 65
# Enable IPV4, IPV6 dual stack IP optimization selection strategy # Enable IPV4, IPV6 dual stack IP optimization selection strategy
# dualstack-ip-selection-threshold [num] (0~1000) # dualstack-ip-selection-threshold [num] (0~1000)
@@ -130,6 +134,7 @@ cache-size 16384
# log-size: size of each log file, support k,m,g # log-size: size of each log file, support k,m,g
# log-num: number of logs # log-num: number of logs
log-level info log-level info
# log-file /var/log/smartdns/smartdns.log # log-file /var/log/smartdns/smartdns.log
# log-size 128k # log-size 128k
# log-num 2 # log-num 2
@@ -210,6 +215,18 @@ log-level info
# ipset /www.example.com/block, set ipset with ipset name of block # ipset /www.example.com/block, set ipset with ipset name of block
# ipset /www.example.com/-, ignore this domain # ipset /www.example.com/-, ignore this domain
# enable nftset timeout by ttl feature
# nftset-timeout [yes]
# enable nftset debug, check nftset setting result, output log when error.
# nftset-debug [no]
# specific nftset to domain
# nftset /domain/[#4:ip#table#set,#6:ipv6#table#setv6]
# nftset /www.example.com/ip#table#set, equivalent to 'nft add element ip table set { ... }'
# nftset /www.example.com/-, ignore this domain
# nftset /www.example.com/#6:-, ignore ipv6
# set domain rules # set domain rules
# domain-rules /domain/ [-speed-check-mode [...]] # domain-rules /domain/ [-speed-check-mode [...]]
# rules: # rules:
@@ -218,6 +235,7 @@ log-level info
# [-a] -address [address|-]: same as address option # [-a] -address [address|-]: same as address option
# [-n] -nameserver [group|-]: same as nameserver option # [-n] -nameserver [group|-]: same as nameserver option
# [-p] -ipset [ipset|-]: same as ipset option # [-p] -ipset [ipset|-]: same as ipset option
# [-t] -nftset [nftset|-]: same as nftset option
# [-d] -dualstack-ip-selection [yes|no]: same as dualstack-ip-selection option # [-d] -dualstack-ip-selection [yes|no]: same as dualstack-ip-selection option
# collection of domains # collection of domains

View File

@@ -81,7 +81,7 @@ build()
main() main()
{ {
OPTS=`getopt -o o:h --long arch:,filearch:,ver:,platform:,cross-tool:,static,only-package,outputdir: \ OPTS=`getopt -o o:h --long arch:,filearch:,ver:,platform:,cross-tool:,with-nftables,static,only-package,outputdir: \
-n "" -- "$@"` -n "" -- "$@"`
if [ "$#" -le "1" ]; then if [ "$#" -le "1" ]; then

View File

@@ -65,9 +65,31 @@ clean_service()
get_systemd_path() get_systemd_path()
{ {
service="`systemctl --no-legend| grep '\.service' | head -n 1 | awk '{print $1}'`" service="`systemctl --no-legend| grep '\.service' | head -n 1 | awk '{print $1}' 2>/dev/null`"
SERVICE_PATH="`systemctl show $service | grep FragmentPath | awk -F'=' '{print $2}'`" SERVICE_PATH="`systemctl show $service | grep FragmentPath | awk -F'=' '{print $2}' 2>/dev/null`"
dirname $SERVICE_PATH if [ ! -z "$SERVICE_PATH" ]; then
SERVICE_PATH="`dirname $SERVICE_PATH 2>/dev/null`"
if [ -d "$SERVICE_PATH" ]; then
echo "$SERVICE_PATH"
return 0
fi
fi
SERVICE_PATH="`pkg-config systemd --variable=systemdsystemunitdir 2>/dev/null`"
if [ ! -z "$SERVICE_PATH" ]; then
if [ -d "$SERVICE_PATH" ]; then
echo "$SERVICE_PATH"
return 0
fi
fi
SERVICE_PATH="/lib/systemd/system"
if [ ! -d "$SERVICE_PATH" ]; then
echo "$SERVICE_PATH"
return 0
fi
return 1
} }
install_files() install_files()
@@ -106,6 +128,7 @@ install_files()
if [ $ISSYSTEMD -eq 0 ]; then if [ $ISSYSTEMD -eq 0 ]; then
SYSTEM_UNIT_PATH="`get_systemd_path`" SYSTEM_UNIT_PATH="`get_systemd_path`"
if [ -z "$SYSTEM_UNIT_PATH" ]; then if [ -z "$SYSTEM_UNIT_PATH" ]; then
echo "cannot find systemd path"
return 1 return 1
fi fi
install -v -m 0644 -t $PREFIX$SYSTEM_UNIT_PATH systemd/smartdns.service install -v -m 0644 -t $PREFIX$SYSTEM_UNIT_PATH systemd/smartdns.service

View File

@@ -38,7 +38,7 @@ end
function act_status() function act_status()
local e={} local e={}
local ipv6_server; local ipv6_server;
local dnsmasq_server = luci.sys.exec("uci get dhcp.@dnsmasq[0].server") local dnsmasq_server = smartdns.get_config_option("dhcp", "dnsmasq", "server", {nil})[1]
local auto_set_dnsmasq = smartdns.get_config_option("smartdns", "smartdns", "auto_set_dnsmasq", nil); local auto_set_dnsmasq = smartdns.get_config_option("smartdns", "smartdns", "auto_set_dnsmasq", nil);
e.auto_set_dnsmasq = auto_set_dnsmasq e.auto_set_dnsmasq = auto_set_dnsmasq
@@ -47,7 +47,7 @@ function act_status()
if e.local_port ~= nil and e.local_port ~= "53" and auto_set_dnsmasq ~= nil and auto_set_dnsmasq == "1" then if e.local_port ~= nil and e.local_port ~= "53" and auto_set_dnsmasq ~= nil and auto_set_dnsmasq == "1" then
local str; local str;
str = "127.0.0.1#" .. e.local_port str = "127.0.0.1#" .. e.local_port
if string.sub(dnsmasq_server,1,string.len(str)) ~= str then if dnsmasq_server ~= str then
e.dnsmasq_redirect_failure = 1 e.dnsmasq_redirect_failure = 1
end end
end end

View File

@@ -126,7 +126,7 @@ o = s:taboption("settings", Flag, "force_https_soa", translate("Force HTTPS SOA"
o.rmempty = false o.rmempty = false
o.default = o.enabled o.default = o.enabled
o.cfgvalue = function(...) o.cfgvalue = function(...)
return Flag.cfgvalue(...) or "0" return Flag.cfgvalue(...) or "1"
end end
---- rr-ttl ---- rr-ttl

View File

@@ -184,6 +184,12 @@ msgstr "解析本地主机名"
msgid "Resolve local hostnames by reading Dnsmasq lease file." msgid "Resolve local hostnames by reading Dnsmasq lease file."
msgstr "读取Dnsmasq的租约文件解析本地主机名。" msgstr "读取Dnsmasq的租约文件解析本地主机名。"
msgid "Restart"
msgstr "重启"
msgid "Restart Service"
msgstr "重启服务"
msgid "Second Server Settings" msgid "Second Server Settings"
msgstr "第二DNS服务器" msgstr "第二DNS服务器"

View File

@@ -3,8 +3,7 @@
"description": "Grant access to LuCI app smartdns", "description": "Grant access to LuCI app smartdns",
"read": { "read": {
"file": { "file": {
"/etc/smartdns/*": [ "read" ], "/etc/smartdns/*": [ "read" ]
"/usr/sbin/smartdns": [ "exec" ]
}, },
"ubus": { "ubus": {
"service": [ "list" ] "service": [ "list" ]
@@ -13,7 +12,8 @@
}, },
"write": { "write": {
"file": { "file": {
"/etc/smartdns/*": [ "write" ] "/etc/smartdns/*": [ "write" ],
"/etc/init.d/smartdns restart": [ "exec" ]
}, },
"uci": [ "smartdns" ] "uci": [ "smartdns" ]
} }

View File

@@ -24,6 +24,7 @@
'require view'; 'require view';
'require poll'; 'require poll';
'require rpc'; 'require rpc';
'require ui';
var conf = 'smartdns'; var conf = 'smartdns';
var callServiceList = rpc.declare({ var callServiceList = rpc.declare({
@@ -32,6 +33,7 @@ var callServiceList = rpc.declare({
params: ['name'], params: ['name'],
expect: { '': {} } expect: { '': {} }
}); });
var pollAdded = false;
function getServiceStatus() { function getServiceStatus() {
return L.resolveDefault(callServiceList(conf), {}) return L.resolveDefault(callServiceList(conf), {})
@@ -106,14 +108,16 @@ return view.extend({
view.innerHTML = smartdnsRenderStatus(res); view.innerHTML = smartdnsRenderStatus(res);
}); });
} }
poll.add(renderStatus, 1);
return E('div', { class: 'cbi-map' }, if (pollAdded == false) {
E('div', { class: 'cbi-section' }, [ poll.add(renderStatus, 1);
E('div', { id: 'service_status' }, pollAdded = true;
_('Collecting data ...')) }
])
); return E('div', { class: 'cbi-section' }, [
E('div', { id: 'service_status' },
_('Collecting data ...'))
]);
} }
// Basic; // Basic;
@@ -192,7 +196,7 @@ return view.extend({
// Force HTTPS SOA // Force HTTPS SOA
o = s.taboption("settings", form.Flag, "force_https_soa", _("Force HTTPS SOA"), _("Force HTTPS SOA.")); o = s.taboption("settings", form.Flag, "force_https_soa", _("Force HTTPS SOA"), _("Force HTTPS SOA."));
o.rmempty = false; o.rmempty = false;
o.default = o.disabled; o.default = o.enabled;
// rr-ttl; // rr-ttl;
o = s.taboption("settings", form.Value, "rr_ttl", _("Domain TTL"), _("TTL for all domain result.")); o = s.taboption("settings", form.Value, "rr_ttl", _("Domain TTL"), _("TTL for all domain result."));
@@ -296,7 +300,12 @@ return view.extend({
return fs.trimmed('/etc/smartdns/custom.conf'); return fs.trimmed('/etc/smartdns/custom.conf');
}; };
o.write = function (section_id, formvalue) { o.write = function (section_id, formvalue) {
return fs.write('/etc/smartdns/custom.conf', formvalue.trim().replace(/\r\n/g, '\n') + '\n'); return this.cfgvalue(section_id).then(function (value) {
if (value == formvalue) {
return
}
return fs.write('/etc/smartdns/custom.conf', formvalue.trim().replace(/\r\n/g, '\n') + '\n');
});
}; };
o = s.taboption("custom", form.Flag, "coredump", _("Generate Coredump"), o = s.taboption("custom", form.Flag, "coredump", _("Generate Coredump"),
@@ -435,7 +444,12 @@ return view.extend({
return fs.trimmed('/etc/smartdns/address.conf'); return fs.trimmed('/etc/smartdns/address.conf');
}; };
o.write = function (section_id, formvalue) { o.write = function (section_id, formvalue) {
return fs.write('/etc/smartdns/address.conf', formvalue.trim().replace(/\r\n/g, '\n') + '\n'); return this.cfgvalue(section_id).then(function (value) {
if (value == formvalue) {
return
}
return fs.write('/etc/smartdns/address.conf', formvalue.trim().replace(/\r\n/g, '\n') + '\n');
});
}; };
// IP Blacklist; // IP Blacklist;
@@ -447,7 +461,12 @@ return view.extend({
return fs.trimmed('/etc/smartdns/blacklist-ip.conf'); return fs.trimmed('/etc/smartdns/blacklist-ip.conf');
}; };
o.write = function (section_id, formvalue) { o.write = function (section_id, formvalue) {
return fs.write('/etc/smartdns/blacklist-ip.conf', formvalue.trim().replace(/\r\n/g, '\n') + '\n'); return this.cfgvalue(section_id).then(function (value) {
if (value == formvalue) {
return
}
return fs.write('/etc/smartdns/blacklist-ip.conf', formvalue.trim().replace(/\r\n/g, '\n') + '\n');
});
}; };
// Doman addresss; // Doman addresss;
@@ -471,6 +490,17 @@ return view.extend({
window.open("https://pymumu.github.io/smartdns/#donate", '_blank'); window.open("https://pymumu.github.io/smartdns/#donate", '_blank');
}; };
o = s.option(form.DummyValue, "_restart", _("Restart Service"));
o.renderWidget = function () {
return E('button', {
'class': 'btn cbi-button cbi-button-apply',
'id': 'btn_restart',
'click': ui.createHandlerFn(this, function () {
return fs.exec('/etc/init.d/smartdns', ['restart'])
.catch(function (e) { ui.addNotification(null, E('p', e.message), 'error') });
})
}, [_("Restart")]);
}
return m.render(); return m.render();
} }
}); });

View File

@@ -15,7 +15,7 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
START=99 START=19
NAME=smartdns NAME=smartdns
USE_PROCD=1 USE_PROCD=1
SERVICE_USE_PID=1 SERVICE_USE_PID=1
@@ -31,6 +31,7 @@ CUSTOM_CONF="$SMARTDNS_CONF_DIR/custom.conf"
SMARTDNS_CONF_TMP="${SMARTDNS_CONF}.tmp" SMARTDNS_CONF_TMP="${SMARTDNS_CONF}.tmp"
COREDUMP="0" COREDUMP="0"
RESPAWN="1" RESPAWN="1"
DO_RELOAD="0"
set_forward_dnsmasq() set_forward_dnsmasq()
{ {
@@ -48,7 +49,7 @@ set_forward_dnsmasq()
uci -q set dhcp.@dnsmasq[0].rebind_protection=0 uci -q set dhcp.@dnsmasq[0].rebind_protection=0
uci -q set dhcp.@dnsmasq[0].domainneeded=0 uci -q set dhcp.@dnsmasq[0].domainneeded=0
uci commit dhcp uci commit dhcp
/etc/init.d/dnsmasq restart /etc/init.d/dnsmasq reload
} }
stop_forward_dnsmasq() stop_forward_dnsmasq()
@@ -66,7 +67,7 @@ stop_forward_dnsmasq()
uci -q set dhcp.@dnsmasq[0].rebind_protection=1 uci -q set dhcp.@dnsmasq[0].rebind_protection=1
uci -q set dhcp.@dnsmasq[0].domainneeded=1 uci -q set dhcp.@dnsmasq[0].domainneeded=1
uci commit dhcp uci commit dhcp
[ "$norestart" != "1" ] && /etc/init.d/dnsmasq restart [ "$norestart" != "1" ] && /etc/init.d/dnsmasq reload
} }
set_main_dns() set_main_dns()
@@ -79,7 +80,7 @@ set_main_dns()
[ -z "$hostip" ] && return [ -z "$hostip" ] && return
[ "$dnsmasq_port" = "53" ] && { [ "$dnsmasq_port" = "53" ] && {
uci -q set dhcp.@dnsmasq[0].port=0 uci -q set dhcp.@dnsmasq[0].port=0
uci -q set dhcp.lan.dhcp_option="6,$hostip" uci -q add_list dhcp.lan.dhcp_option="6,$hostip"
} }
# for some third-party firmware # for some third-party firmware
@@ -90,12 +91,13 @@ set_main_dns()
} }
uci commit dhcp uci commit dhcp
/etc/init.d/dnsmasq restart /etc/init.d/dnsmasq reload
} }
stop_main_dns() stop_main_dns()
{ {
local norestart="$1" local norestart="$1"
hostip="$(uci -q get network.lan.ipaddr)"
dnsmasq_port="$(uci -q get dhcp.@dnsmasq[0].port)" dnsmasq_port="$(uci -q get dhcp.@dnsmasq[0].port)"
redir_dns="$(uci -q get dhcp.@dnsmasq[0].old_dns_redirect)" redir_dns="$(uci -q get dhcp.@dnsmasq[0].old_dns_redirect)"
[ "$dnsmasq_port" != "0" ] && return [ "$dnsmasq_port" != "0" ] && return
@@ -104,9 +106,9 @@ stop_main_dns()
uci -q delete dhcp.@dnsmasq[0].old_dns_redirect uci -q delete dhcp.@dnsmasq[0].old_dns_redirect
} }
uci -q delete dhcp.@dnsmasq[0].port uci -q delete dhcp.@dnsmasq[0].port
uci -q delete dhcp.lan.dhcp_option uci -q del_list dhcp.lan.dhcp_option="6,$hostip"
uci commit dhcp uci commit dhcp
[ "$norestart" != "1" ] && /etc/init.d/dnsmasq restart [ "$norestart" != "1" ] && /etc/init.d/dnsmasq reload
} }
clear_iptable() clear_iptable()
@@ -314,7 +316,7 @@ load_service()
config_get force_aaaa_soa "$section" "force_aaaa_soa" "0" config_get force_aaaa_soa "$section" "force_aaaa_soa" "0"
[ "$force_aaaa_soa" = "1" ] && qtype_soa_list="$qtype_soa_list 28" [ "$force_aaaa_soa" = "1" ] && qtype_soa_list="$qtype_soa_list 28"
config_get force_https_soa "$section" "force_https_soa" "0" config_get force_https_soa "$section" "force_https_soa" "1"
[ "$force_https_soa" = "1" ] && qtype_soa_list="$qtype_soa_list 65" [ "$force_https_soa" = "1" ] && qtype_soa_list="$qtype_soa_list 65"
config_get auto_set_dnsmasq "$section" "auto_set_dnsmasq" "1" config_get auto_set_dnsmasq "$section" "auto_set_dnsmasq" "1"
@@ -452,10 +454,16 @@ load_service()
unload_service() unload_service()
{ {
local section="$1" local section="$1"
[ "$DO_RELOAD" = "1" ] && return 0
config_get_bool enabled "$section" "enabled" '0'
dnsmasq_port="$(uci -q get dhcp.@dnsmasq[0].port)" dnsmasq_port="$(uci -q get dhcp.@dnsmasq[0].port)"
config_get port "$section" "port" "53" config_get port "$section" "port" "53"
config_get old_port "$section" "old_port" "0"
config_get auto_set_dnsmasq "$section" "auto_set_dnsmasq" "0" config_get auto_set_dnsmasq "$section" "auto_set_dnsmasq" "0"
config_get old_enabled "$section" "old_enabled" "0"
config_get old_port "$section" "old_port" "0"
config_get old_auto_set_dnsmasq "$section" "old_auto_set_dnsmasq" "0"
[ -z "${dnsmasq_port}" ] && dnsmasq_port="53" [ -z "${dnsmasq_port}" ] && dnsmasq_port="53"
[ "$enabled" = "1" ] && { [ "$enabled" = "1" ] && {
@@ -479,6 +487,8 @@ start_service()
reload_service() reload_service()
{ {
DO_RELOAD="1"
stop stop
start start
DO_RELOAD="0"
} }

View File

@@ -15,7 +15,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
BIN=smartdns BIN=smartdns
OBJS_LIB=lib/rbtree.o lib/art.o lib/bitops.o lib/radix.o lib/conf.o 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 $(OBJS_LIB) 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 $(OBJS_LIB)
# cflags # cflags
@@ -41,7 +41,7 @@ endif
.PHONY: all clean .PHONY: all clean
all: $(BIN) all: $(BIN)
$(BIN) : $(OBJS) $(BIN) : $(OBJS)
$(CC) $(OBJS) -o $@ $(LDFLAGS) $(CC) $(OBJS) -o $@ $(LDFLAGS)

406
src/dns.c
View File

@@ -542,6 +542,106 @@ static int _dns_get_rr_head(struct dns_context *context, char *domain, int maxsi
return len; return len;
} }
struct dns_rr_nested *dns_add_rr_nested_start(struct dns_rr_nested *rr_nested_buffer, struct dns_packet *packet,
dns_rr_type type, dns_type_t rtype, const char *domain, int ttl)
{
int len = 0;
memset(rr_nested_buffer, 0, sizeof(*rr_nested_buffer));
rr_nested_buffer->type = type;
int ret = 0;
/* resource record */
/* |domain |
* |qtype | qclass |
* | ttl |
* | rrlen | rrdata |
*/
ret = _dns_add_rrs_start(packet, &rr_nested_buffer->context);
if (ret < 0) {
return NULL;
}
rr_nested_buffer->rr_start = rr_nested_buffer->context.ptr;
/* add rr head */
len = _dns_add_rr_head(&rr_nested_buffer->context, domain, rtype, DNS_C_IN, ttl, 0);
if (len < 0) {
return NULL;
}
rr_nested_buffer->rr_len_ptr = rr_nested_buffer->context.ptr - 2;
rr_nested_buffer->rr_head_len = len;
return rr_nested_buffer;
}
int dns_add_rr_nested_memcpy(struct dns_rr_nested *rr_nested, void *data, int data_len)
{
if (rr_nested == NULL || data == NULL || data_len <= 0) {
return -1;
}
if (_dns_left_len(&rr_nested->context) < data_len) {
return -1;
}
memcpy(rr_nested->context.ptr, data, data_len);
rr_nested->context.ptr += data_len;
return 0;
}
int dns_add_rr_nested_end(struct dns_rr_nested *rr_nested, dns_type_t rtype)
{
if (rr_nested == NULL || rr_nested->rr_start == NULL) {
return -1;
}
int len = rr_nested->context.ptr - rr_nested->rr_start;
unsigned char *ptr = rr_nested->rr_len_ptr;
if (ptr == NULL || _dns_left_len(&rr_nested->context) < 2) {
return -1;
}
_dns_write_short(&ptr, len - rr_nested->rr_head_len);
return _dns_rr_add_end(rr_nested->context.packet, rr_nested->type, rtype, len);
}
void *dns_get_rr_nested_start(struct dns_rrs *rrs, char *domain, int maxsize, int *qtype, int *ttl, int *rr_len)
{
struct dns_context data_context;
int qclass = 0;
int ret = 0;
_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 NULL;
}
if (qclass != DNS_C_IN) {
return NULL;
}
if (*rr_len < 2) {
return NULL;
}
return data_context.ptr;
}
void *dns_get_rr_nested_next(struct dns_rrs *rrs, void *rr_nested, int rr_nested_len)
{
void *end = rrs->data + rrs->len;
void *p = rr_nested + rr_nested_len;
if (p == end) {
return NULL;
} else if (p > end) {
return NULL;
}
return p;
}
static int _dns_add_RAW(struct dns_packet *packet, dns_rr_type rrtype, dns_type_t rtype, const char *domain, int ttl, static int _dns_add_RAW(struct dns_packet *packet, dns_rr_type rrtype, dns_type_t rtype, const char *domain, int ttl,
const void *raw, int raw_len) const void *raw, int raw_len)
{ {
@@ -966,6 +1066,155 @@ int dns_get_OPT_TCP_KEEYALIVE(struct dns_rrs *rrs, unsigned short *opt_code, uns
return 0; 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)
{
svcparam_buffer = dns_add_rr_nested_start(svcparam_buffer, packet, type, DNS_T_HTTPS, domain, ttl);
if (svcparam_buffer == NULL) {
return -1;
}
int target_len = strnlen(target, DNS_MAX_CNAME_LEN) + 1;
if (_dns_left_len(&svcparam_buffer->context) < 2 + target_len) {
return -1;
}
/* add rr data */
_dns_write_short(&svcparam_buffer->context.ptr, priority);
safe_strncpy((char *)svcparam_buffer->context.ptr, target, target_len);
svcparam_buffer->context.ptr += target_len;
return 0;
}
int dns_HTTPS_add_raw(struct dns_rr_nested *svcparam, unsigned short key, unsigned char *value, unsigned short len)
{
if (_dns_left_len(&svcparam->context) < 2 + len) {
return -1;
}
dns_add_rr_nested_memcpy(svcparam, &key, 2);
dns_add_rr_nested_memcpy(svcparam, &len, 2);
dns_add_rr_nested_memcpy(svcparam, value, len);
return 0;
}
int dns_HTTPS_add_port(struct dns_rr_nested *svcparam, unsigned short port)
{
if (_dns_left_len(&svcparam->context) < 6) {
return -1;
}
unsigned short value = DNS_HTTPS_T_PORT;
dns_add_rr_nested_memcpy(svcparam, &value, 2);
value = 2;
dns_add_rr_nested_memcpy(svcparam, &value, 2);
value = htons(port);
dns_add_rr_nested_memcpy(svcparam, &value, 2);
return 0;
}
int dns_HTTPS_add_ech(struct dns_rr_nested *svcparam, void *ech, int ech_len)
{
if (_dns_left_len(&svcparam->context) < 2 + 2 + ech_len) {
return -1;
}
unsigned short value = DNS_HTTPS_T_ECH;
dns_add_rr_nested_memcpy(svcparam, &value, 2);
value = ech_len;
dns_add_rr_nested_memcpy(svcparam, &value, 2);
dns_add_rr_nested_memcpy(svcparam, ech, ech_len);
return 0;
}
int dns_HTTPS_add_ipv4hint(struct dns_rr_nested *svcparam, unsigned char addr[][DNS_RR_A_LEN], int addr_num)
{
if (_dns_left_len(&svcparam->context) < 4 + addr_num * DNS_RR_A_LEN) {
return -1;
}
unsigned short value = DNS_HTTPS_T_IPV4HINT;
dns_add_rr_nested_memcpy(svcparam, &value, 2);
value = addr_num * DNS_RR_A_LEN;
dns_add_rr_nested_memcpy(svcparam, &value, 2);
for (int i = 0; i < addr_num; i++) {
dns_add_rr_nested_memcpy(svcparam, addr[i], DNS_RR_A_LEN);
}
return 0;
}
int dns_HTTPS_add_ipv6hint(struct dns_rr_nested *svcparam, unsigned char addr[][DNS_RR_AAAA_LEN], int addr_num)
{
if (_dns_left_len(&svcparam->context) < 4 + addr_num * DNS_RR_AAAA_LEN) {
return -1;
}
unsigned short value = DNS_HTTPS_T_IPV6HINT;
dns_add_rr_nested_memcpy(svcparam, &value, 2);
value = addr_num * DNS_RR_AAAA_LEN;
dns_add_rr_nested_memcpy(svcparam, &value, 2);
for (int i = 0; i < addr_num; i++) {
dns_add_rr_nested_memcpy(svcparam, addr[i], DNS_RR_AAAA_LEN);
}
return 0;
}
int dns_add_HTTPS_end(struct dns_rr_nested *svcparam)
{
return dns_add_rr_nested_end(svcparam, DNS_T_HTTPS);
}
struct dns_https_param *dns_get_HTTPS_svcparm_start(struct dns_rrs *rrs, char *domain, int maxsize, int *ttl,
int *priority, char *target, int target_size)
{
int qtype = 0;
unsigned char *data = NULL;
int rr_len = 0;
data = dns_get_rr_nested_start(rrs, domain, maxsize, &qtype, ttl, &rr_len);
if (data == NULL) {
return NULL;
}
if (qtype != DNS_T_HTTPS) {
return NULL;
}
if (rr_len < 2) {
return NULL;
}
*priority = _dns_read_short(&data);
rr_len -= 2;
if (rr_len <= 0) {
return NULL;
}
int len = strnlen((char *)data, rr_len);
safe_strncpy(target, (char *)data, target_size);
data += len + 1;
rr_len -= len + 1;
if (rr_len <= 0) {
return NULL;
}
return (struct dns_https_param *)data;
}
struct dns_https_param *dns_get_HTTPS_svcparm_next(struct dns_rrs *rrs, struct dns_https_param *param)
{
return dns_get_rr_nested_next(rrs, param, sizeof(struct dns_https_param) + param->len);
}
/* /*
* Format: * Format:
* |DNS_NAME\0(string)|qtype(short)|qclass(short)| * |DNS_NAME\0(string)|qtype(short)|qclass(short)|
@@ -1624,7 +1873,7 @@ static int _dns_decode_opt(struct dns_context *context, dns_rr_type type, unsign
opt_len = _dns_read_short(&context->ptr); opt_len = _dns_read_short(&context->ptr);
if (_dns_left_len(context) < opt_len) { if (_dns_left_len(context) < opt_len) {
tlog(TLOG_ERROR, "read opt data failed, opt_code = %d, opt_le = %d", opt_code, opt_len); tlog(TLOG_ERROR, "read opt data failed, opt_code = %d, opt_len = %d", opt_code, opt_len);
return -1; return -1;
} }
@@ -1652,6 +1901,9 @@ static int _dns_decode_opt(struct dns_context *context, dns_rr_type type, unsign
return -1; return -1;
} }
} break; } break;
case DNS_OPT_T_PADDING:
context->ptr += opt_len;
break;
default: default:
context->ptr += opt_len; context->ptr += opt_len;
tlog(TLOG_DEBUG, "DNS opt type = %d not supported", opt_code); tlog(TLOG_DEBUG, "DNS opt type = %d not supported", opt_code);
@@ -1662,6 +1914,139 @@ static int _dns_decode_opt(struct dns_context *context, dns_rr_type type, unsign
return 0; return 0;
} }
static int _dns_encode_HTTPS(struct dns_context *context, struct dns_rrs *rrs)
{
int ret = 0;
int qtype = 0;
int qclass = 0;
char domain[DNS_MAX_CNAME_LEN];
char target[DNS_MAX_CNAME_LEN] = {0};
unsigned char *rr_len_ptr = NULL;
unsigned char *start = NULL;
unsigned char *rr_start = NULL;
int ttl = 0;
int priority = 0;
struct dns_https_param *param = NULL;
param = dns_get_HTTPS_svcparm_start(rrs, domain, DNS_MAX_CNAME_LEN, &ttl, &priority, target, DNS_MAX_CNAME_LEN);
if (param == NULL) {
tlog(TLOG_ERROR, "get https param failed.");
return -1;
}
ret = _dns_encode_rr_head(context, domain, qtype, qclass, ttl, 0, &rr_len_ptr);
if (ret < 0) {
return -1;
}
rr_start = context->ptr;
if (_dns_left_len(context) < 2) {
tlog(TLOG_ERROR, "left len is invalid.");
return -1;
}
_dns_write_short(&context->ptr, priority);
ret = _dns_encode_domain(context, target);
if (ret < 0) {
return -1;
}
start = context->ptr;
for (; param != NULL; param = dns_get_HTTPS_svcparm_next(rrs, param)) {
if (context->ptr - start > rrs->len || _dns_left_len(context) <= 0) {
return -1;
}
_dns_write_short(&context->ptr, param->key);
_dns_write_short(&context->ptr, param->len);
switch (param->key) {
case DNS_HTTPS_T_MANDATORY:
case DNS_HTTPS_T_NO_DEFAULT_ALPN:
case DNS_HTTPS_T_ALPN:
case DNS_HTTPS_T_PORT:
case DNS_HTTPS_T_IPV4HINT:
case DNS_HTTPS_T_ECH:
case DNS_HTTPS_T_IPV6HINT: {
memcpy(context->ptr, param->value, param->len);
context->ptr += param->len;
} break;
default:
/* skip unknown key */
context->ptr -= 4;
break;
}
}
_dns_write_short(&rr_len_ptr, context->ptr - rr_start);
return 0;
}
static int _dns_decode_HTTPS(struct dns_context *context, const char *domain, dns_rr_type type, unsigned int ttl,
int rr_len)
{
unsigned char *start = context->ptr;
struct dns_packet *packet = context->packet;
int ret = 0;
unsigned short priority;
unsigned short key;
unsigned short value_len;
unsigned char *value = NULL;
char target[DNS_MAX_CNAME_LEN] = {0};
struct dns_rr_nested param;
if (rr_len < 2) {
tlog(TLOG_DEBUG, "https len is invalid.");
return -1;
}
priority = _dns_read_short(&context->ptr);
ret = _dns_decode_domain(context, target, sizeof(target));
if (ret < 0) {
return -1;
}
dns_add_HTTPS_start(&param, packet, DNS_RRS_AN, domain, ttl, priority, target);
while (context->ptr - start < rr_len) {
if (_dns_left_len(context) < 4) {
tlog(TLOG_WARN, "data length is invalid, %d:%d", _dns_left_len(context),
(int)(context->ptr - context->data));
return -1;
}
key = _dns_read_short(&context->ptr);
value_len = _dns_read_short(&context->ptr);
value = context->ptr;
if (_dns_left_len(context) < value_len) {
tlog(TLOG_ERROR, "read https data failed, svcParam key = %d, https_len = %d", key, value_len);
return -1;
}
switch (key) {
case DNS_HTTPS_T_MANDATORY:
case DNS_HTTPS_T_ALPN:
case DNS_HTTPS_T_NO_DEFAULT_ALPN:
case DNS_HTTPS_T_PORT:
case DNS_HTTPS_T_IPV4HINT:
case DNS_HTTPS_T_ECH:
case DNS_HTTPS_T_IPV6HINT: {
dns_HTTPS_add_raw(&param, key, value, value_len);
} break;
default:
tlog(TLOG_DEBUG, "DNS HTTPS key = %d not supported", key);
break;
}
context->ptr += value_len;
}
dns_add_HTTPS_end(&param);
return 0;
}
static int _dns_decode_qd(struct dns_context *context) static int _dns_decode_qd(struct dns_context *context)
{ {
struct dns_packet *packet = context->packet; struct dns_packet *packet = context->packet;
@@ -1805,6 +2190,19 @@ static int _dns_decode_an(struct dns_context *context, dns_rr_type type)
dns_set_OPT_payload_size(packet, qclass); dns_set_OPT_payload_size(packet, qclass);
} break; } break;
case DNS_T_HTTPS: {
unsigned char *https_start = context->ptr;
ret = _dns_decode_HTTPS(context, domain, type, ttl, rr_len);
if (ret < 0) {
tlog(TLOG_DEBUG, "decode HTTPS failed, %s", domain);
return -1;
}
if (context->ptr - https_start != rr_len) {
tlog(TLOG_DEBUG, "opt length mismatch, %s\n", domain);
return -1;
}
} break;
default: { default: {
unsigned char raw_data[1024]; unsigned char raw_data[1024];
if (_dns_left_len(context) < rr_len || rr_len >= (int)sizeof(raw_data)) { if (_dns_left_len(context) < rr_len || rr_len >= (int)sizeof(raw_data)) {
@@ -1883,6 +2281,12 @@ static int _dns_encode_an(struct dns_context *context, struct dns_rrs *rrs)
return -1; return -1;
} }
break; break;
case DNS_T_HTTPS:
ret = _dns_encode_HTTPS(context, rrs);
if (ret < 0) {
return -1;
}
break;
default: default:
ret = _dns_encode_raw(context, rrs); ret = _dns_encode_raw(context, rrs);
if (ret < 0) { if (ret < 0) {

View File

@@ -73,12 +73,25 @@ typedef enum dns_type {
} dns_type_t; } dns_type_t;
typedef enum dns_opt_code { typedef enum dns_opt_code {
DNS_OPT_T_ECS = 8, // OPT ECS DNS_OPT_T_ECS = 8, // OPT ECS
DNS_OPT_T_COOKIE = 10, //OPT Cookie DNS_OPT_T_COOKIE = 10, // OPT Cookie
DNS_OPT_T_TCP_KEEPALIVE = 11, DNS_OPT_T_TCP_KEEPALIVE = 11,
DNS_OPT_T_PADDING = 12,
DNS_OPT_T_ALL = 255 DNS_OPT_T_ALL = 255
} dns_opt_code_t; } dns_opt_code_t;
/* https://datatracker.ietf.org/doc/draft-ietf-dnsop-svcb-https/11/ */
typedef enum dns_htts_svcparam {
DNS_HTTPS_T_MANDATORY = 0,
DNS_HTTPS_T_ALPN = 1,
DNS_HTTPS_T_NO_DEFAULT_ALPN = 2,
DNS_HTTPS_T_PORT = 3,
DNS_HTTPS_T_IPV4HINT = 4,
DNS_HTTPS_T_ECH = 5,
DNS_HTTPS_T_IPV6HINT = 6,
DNS_HTTPS_T_ALL = 255
} dns_htts_svcparam_t;
typedef enum dns_opcode { typedef enum dns_opcode {
DNS_OP_QUERY = 0, DNS_OP_QUERY = 0,
DNS_OP_IQUERY = 1, DNS_OP_IQUERY = 1,
@@ -183,7 +196,7 @@ struct dns_opt_ecs {
unsigned char source_prefix; unsigned char source_prefix;
unsigned char scope_prefix; unsigned char scope_prefix;
unsigned char addr[DNS_RR_AAAA_LEN]; unsigned char addr[DNS_RR_AAAA_LEN];
} __attribute__((packed));; } __attribute__((packed));
/* OPT COOLIE */ /* OPT COOLIE */
struct dns_opt_cookie { struct dns_opt_cookie {
@@ -199,9 +212,31 @@ struct dns_opt {
unsigned char data[0]; unsigned char data[0];
} __attribute__((packed)); } __attribute__((packed));
struct dns_rr_nested {
struct dns_context context;
unsigned char *rr_start;
unsigned char *rr_len_ptr;
unsigned short rr_head_len;
dns_rr_type type;
};
struct dns_https_param {
unsigned short key;
unsigned short len;
unsigned char value[0];
};
struct dns_rrs *dns_get_rrs_next(struct dns_packet *packet, struct dns_rrs *rrs); struct dns_rrs *dns_get_rrs_next(struct dns_packet *packet, struct dns_rrs *rrs);
struct dns_rrs *dns_get_rrs_start(struct dns_packet *packet, dns_rr_type type, int *count); struct dns_rrs *dns_get_rrs_start(struct dns_packet *packet, dns_rr_type type, int *count);
struct dns_rr_nested *dns_add_rr_nested_start(struct dns_rr_nested *rr_nested_buffer, struct dns_packet *packet,
dns_rr_type type, dns_type_t rtype, const char *domain, int ttl);
int dns_add_rr_nested_end(struct dns_rr_nested *rr_nested, dns_type_t rtype);
int dns_add_rr_nested_memcpy(struct dns_rr_nested *rr_nested, void *data, int data_len);
void *dns_get_rr_nested_start(struct dns_rrs *rrs, char *domain, int maxsize, int *qtype, int *ttl, int *rr_len);
void *dns_get_rr_nested_next(struct dns_rrs *rrs, void *rr_nested, int rr_nested_len);
/* /*
* Question * Question
*/ */
@@ -214,7 +249,8 @@ int dns_get_domain(struct dns_rrs *rrs, char *domain, int maxsize, int *qtype, i
int dns_add_CNAME(struct dns_packet *packet, dns_rr_type type, const char *domain, int ttl, const char *cname); int dns_add_CNAME(struct dns_packet *packet, dns_rr_type type, const char *domain, int ttl, const char *cname);
int dns_get_CNAME(struct dns_rrs *rrs, char *domain, int maxsize, int *ttl, char *cname, int cname_size); int dns_get_CNAME(struct dns_rrs *rrs, char *domain, int maxsize, int *ttl, char *cname, int cname_size);
int dns_add_A(struct dns_packet *packet, dns_rr_type type, const char *domain, int ttl, unsigned char addr[DNS_RR_A_LEN]); int dns_add_A(struct dns_packet *packet, dns_rr_type type, const char *domain, int ttl,
unsigned char addr[DNS_RR_A_LEN]);
int dns_get_A(struct dns_rrs *rrs, char *domain, int maxsize, int *ttl, unsigned char addr[DNS_RR_A_LEN]); int dns_get_A(struct dns_rrs *rrs, char *domain, int maxsize, int *ttl, unsigned char addr[DNS_RR_A_LEN]);
int dns_add_PTR(struct dns_packet *packet, dns_rr_type type, const char *domain, int ttl, char *cname); int dns_add_PTR(struct dns_packet *packet, dns_rr_type type, const char *domain, int ttl, char *cname);
@@ -239,6 +275,25 @@ int dns_get_OPT_ECS(struct dns_rrs *rrs, unsigned short *opt_code, unsigned shor
int dns_add_OPT_TCP_KEEYALIVE(struct dns_packet *packet, unsigned short timeout); int dns_add_OPT_TCP_KEEYALIVE(struct dns_packet *packet, unsigned short timeout);
int dns_get_OPT_TCP_KEEYALIVE(struct dns_rrs *rrs, unsigned short *opt_code, unsigned short *opt_len, int dns_get_OPT_TCP_KEEYALIVE(struct dns_rrs *rrs, unsigned short *opt_code, unsigned short *opt_len,
unsigned short *timeout); unsigned short *timeout);
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);
int dns_HTTPS_add_raw(struct dns_rr_nested *svcparam, unsigned short key, unsigned char *value, unsigned short len);
int dns_HTTPS_add_port(struct dns_rr_nested *svcparam, unsigned short port);
int dns_HTTPS_add_alpn(struct dns_rr_nested *svcparam, const char *alpn);
int dns_HTTPS_add_no_default_alpn(struct dns_rr_nested *svcparam);
int dns_HTTPS_add_ipv4hint(struct dns_rr_nested *svcparam, unsigned char addr[][DNS_RR_A_LEN],
int addr_num);
int dns_HTTPS_add_ipv6hint(struct dns_rr_nested *svcparam, unsigned char addr[][DNS_RR_AAAA_LEN],
int addr_num);
int dns_HTTPS_add_ech(struct dns_rr_nested *svcparam, void *ech, int ech_len);
int dns_add_HTTPS_end(struct dns_rr_nested *svcparam);
struct dns_https_param *dns_get_HTTPS_svcparm_start(struct dns_rrs *rrs, 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);
/* /*
* Packet operation * Packet operation
*/ */

View File

@@ -30,7 +30,7 @@
#define DNS_CACHE_HITNUM_STEP_MAX 6 #define DNS_CACHE_HITNUM_STEP_MAX 6
struct dns_cache_head { struct dns_cache_head {
DECLARE_HASHTABLE(cache_hash, 10); DECLARE_HASHTABLE(cache_hash, 16);
struct list_head cache_list; struct list_head cache_list;
struct list_head inactive_list; struct list_head inactive_list;
atomic_t num; atomic_t num;
@@ -128,14 +128,14 @@ enum CACHE_TYPE dns_cache_data_type(struct dns_cache_data *cache_data)
return cache_data->head.cache_type; return cache_data->head.cache_type;
} }
uint32_t dns_cache_get_query_flag(struct dns_cache_data *cache_data) uint32_t dns_cache_get_query_flag(struct dns_cache *dns_cache)
{ {
return cache_data->head.query_flag; return dns_cache->info.query_flag;
} }
const char *dns_cache_get_dns_group_name(struct dns_cache_data *cache_data) const char *dns_cache_get_dns_group_name(struct dns_cache *dns_cache)
{ {
return cache_data->head.dns_group_name; return dns_cache->info.dns_group_name;
} }
void dns_cache_data_free(struct dns_cache_data *data) void dns_cache_data_free(struct dns_cache_data *data)
@@ -161,8 +161,7 @@ struct dns_cache_data *dns_cache_new_data(void)
return (struct dns_cache_data *)cache_addr; return (struct dns_cache_data *)cache_addr;
} }
void dns_cache_set_data_soa(struct dns_cache_data *dns_cache, struct dns_cache_query_option *query_option, char *cname, void dns_cache_set_data_soa(struct dns_cache_data *dns_cache, char *cname, int cname_ttl)
int cname_ttl)
{ {
if (dns_cache == NULL) { if (dns_cache == NULL) {
goto errout; goto errout;
@@ -185,13 +184,6 @@ void dns_cache_set_data_soa(struct dns_cache_data *dns_cache, struct dns_cache_q
cache_addr->addr_data.cname_ttl = cname_ttl; cache_addr->addr_data.cname_ttl = cname_ttl;
} }
if (query_option) {
cache_addr->head.query_flag = query_option->query_flag;
if (query_option->dns_group_name) {
safe_strncpy(cache_addr->head.dns_group_name, query_option->dns_group_name, DNS_CACHE_GROUP_NAME_LEN);
}
}
cache_addr->addr_data.soa = 1; cache_addr->addr_data.soa = 1;
cache_addr->head.cache_type = CACHE_TYPE_ADDR; cache_addr->head.cache_type = CACHE_TYPE_ADDR;
cache_addr->head.size = sizeof(struct dns_cache_addr) - sizeof(struct dns_cache_data_head); cache_addr->head.size = sizeof(struct dns_cache_addr) - sizeof(struct dns_cache_data_head);
@@ -199,8 +191,8 @@ errout:
return; return;
} }
void dns_cache_set_data_addr(struct dns_cache_data *dns_cache, struct dns_cache_query_option *query_option, char *cname, void dns_cache_set_data_addr(struct dns_cache_data *dns_cache, char *cname, int cname_ttl, unsigned char *addr,
int cname_ttl, unsigned char *addr, int addr_len) int addr_len)
{ {
if (dns_cache == NULL) { if (dns_cache == NULL) {
goto errout; goto errout;
@@ -224,21 +216,13 @@ void dns_cache_set_data_addr(struct dns_cache_data *dns_cache, struct dns_cache_
cache_addr->addr_data.cname_ttl = cname_ttl; cache_addr->addr_data.cname_ttl = cname_ttl;
} }
if (query_option) {
cache_addr->head.query_flag = query_option->query_flag;
if (query_option->dns_group_name) {
safe_strncpy(cache_addr->head.dns_group_name, query_option->dns_group_name, DNS_CACHE_GROUP_NAME_LEN);
}
}
cache_addr->head.cache_type = CACHE_TYPE_ADDR; cache_addr->head.cache_type = CACHE_TYPE_ADDR;
cache_addr->head.size = sizeof(struct dns_cache_addr) - sizeof(struct dns_cache_data_head); cache_addr->head.size = sizeof(struct dns_cache_addr) - sizeof(struct dns_cache_data_head);
errout: errout:
return; return;
} }
struct dns_cache_data *dns_cache_new_data_packet(struct dns_cache_query_option *query_option, void *packet, struct dns_cache_data *dns_cache_new_data_packet(void *packet, size_t packet_len)
size_t packet_len)
{ {
struct dns_cache_packet *cache_packet = NULL; struct dns_cache_packet *cache_packet = NULL;
size_t data_size = 0; size_t data_size = 0;
@@ -255,19 +239,13 @@ struct dns_cache_data *dns_cache_new_data_packet(struct dns_cache_query_option *
memcpy(cache_packet->data, packet, packet_len); memcpy(cache_packet->data, packet, packet_len);
memset(&cache_packet->head, 0, sizeof(cache_packet->head)); memset(&cache_packet->head, 0, sizeof(cache_packet->head));
if (query_option) {
cache_packet->head.query_flag = query_option->query_flag;
if (query_option->dns_group_name) {
strncpy(cache_packet->head.dns_group_name, query_option->dns_group_name, DNS_CACHE_GROUP_NAME_LEN - 1);
}
}
cache_packet->head.cache_type = CACHE_TYPE_PACKET; cache_packet->head.cache_type = CACHE_TYPE_PACKET;
cache_packet->head.size = packet_len; cache_packet->head.size = packet_len;
return (struct dns_cache_data *)cache_packet; return (struct dns_cache_data *)cache_packet;
} }
static int _dns_cache_replace(char *domain, int ttl, dns_type_t qtype, int speed, int inactive, static int _dns_cache_replace(struct dns_cache_key *cache_key, int ttl, int speed, int inactive,
struct dns_cache_data *cache_data) struct dns_cache_data *cache_data)
{ {
struct dns_cache *dns_cache = NULL; struct dns_cache *dns_cache = NULL;
@@ -278,9 +256,9 @@ static int _dns_cache_replace(char *domain, int ttl, dns_type_t qtype, int speed
} }
/* lookup existing cache */ /* lookup existing cache */
dns_cache = dns_cache_lookup(domain, qtype); dns_cache = dns_cache_lookup(cache_key);
if (dns_cache == NULL) { if (dns_cache == NULL) {
return dns_cache_insert(domain, ttl, qtype, speed, cache_data); return dns_cache_insert(cache_key, ttl, speed, cache_data);
} }
if (ttl < DNS_CACHE_TTL_MIN) { if (ttl < DNS_CACHE_TTL_MIN) {
@@ -291,7 +269,8 @@ static int _dns_cache_replace(char *domain, int ttl, dns_type_t qtype, int speed
pthread_mutex_lock(&dns_cache_head.lock); pthread_mutex_lock(&dns_cache_head.lock);
dns_cache->del_pending = 0; dns_cache->del_pending = 0;
dns_cache->info.ttl = ttl; dns_cache->info.ttl = ttl;
dns_cache->info.qtype = qtype; dns_cache->info.qtype = cache_key->qtype;
dns_cache->info.query_flag = cache_key->query_flag;
dns_cache->info.ttl = ttl; dns_cache->info.ttl = ttl;
dns_cache->info.speed = speed; dns_cache->info.speed = speed;
old_cache_data = dns_cache->cache_data; old_cache_data = dns_cache->cache_data;
@@ -314,31 +293,42 @@ static int _dns_cache_replace(char *domain, int ttl, dns_type_t qtype, int speed
return 0; return 0;
} }
int dns_cache_replace(char *domain, int ttl, dns_type_t qtype, int speed, struct dns_cache_data *cache_data) int dns_cache_replace(struct dns_cache_key *cache_key, int ttl, int speed, struct dns_cache_data *cache_data)
{ {
return _dns_cache_replace(domain, ttl, qtype, speed, 0, cache_data); return _dns_cache_replace(cache_key, ttl, speed, 0, cache_data);
} }
int dns_cache_replace_inactive(char *domain, int ttl, dns_type_t qtype, int speed, struct dns_cache_data *cache_data) int dns_cache_replace_inactive(struct dns_cache_key *cache_key, int ttl, int speed, struct dns_cache_data *cache_data)
{ {
return _dns_cache_replace(domain, ttl, qtype, speed, 1, cache_data); return _dns_cache_replace(cache_key, ttl, speed, 1, cache_data);
} }
static void _dns_cache_remove_by_domain(const char *domain, dns_type_t qtype) static void _dns_cache_remove_by_domain(struct dns_cache_key *cache_key)
{ {
uint32_t key = 0; uint32_t key = 0;
struct dns_cache *dns_cache = NULL; struct dns_cache *dns_cache = NULL;
key = hash_string(domain); key = hash_string(cache_key->domain);
key = jhash(&qtype, sizeof(qtype), key); key = jhash(&cache_key->qtype, sizeof(cache_key->qtype), key);
key = hash_string_initval(cache_key->dns_group_name, key);
key = jhash(&cache_key->query_flag, sizeof(cache_key->query_flag), key);
pthread_mutex_lock(&dns_cache_head.lock); pthread_mutex_lock(&dns_cache_head.lock);
hash_for_each_possible(dns_cache_head.cache_hash, dns_cache, node, key) hash_for_each_possible(dns_cache_head.cache_hash, dns_cache, node, key)
{ {
if (dns_cache->info.qtype != qtype) { if (dns_cache->info.qtype != cache_key->qtype) {
continue; continue;
} }
if (strncmp(domain, dns_cache->info.domain, DNS_MAX_CNAME_LEN) != 0) { if (dns_cache->info.query_flag != cache_key->query_flag) {
continue;
}
if (strncmp(cache_key->domain, dns_cache->info.domain, DNS_MAX_CNAME_LEN) != 0) {
continue;
}
if (strncmp(dns_cache->info.dns_group_name, cache_key->dns_group_name, DNS_MAX_CNAME_LEN) != 0) {
continue; continue;
} }
@@ -355,7 +345,12 @@ static int _dns_cache_insert(struct dns_cache_info *info, struct dns_cache_data
struct dns_cache *dns_cache = NULL; struct dns_cache *dns_cache = NULL;
/* if cache already exists, free */ /* if cache already exists, free */
_dns_cache_remove_by_domain(info->domain, info->qtype); struct dns_cache_key cache_key;
cache_key.qtype = info->qtype;
cache_key.query_flag = info->query_flag;
cache_key.domain = info->domain;
cache_key.dns_group_name = info->dns_group_name;
_dns_cache_remove_by_domain(&cache_key);
dns_cache = malloc(sizeof(*dns_cache)); dns_cache = malloc(sizeof(*dns_cache));
if (dns_cache == NULL) { if (dns_cache == NULL) {
@@ -365,6 +360,8 @@ static int _dns_cache_insert(struct dns_cache_info *info, struct dns_cache_data
memset(dns_cache, 0, sizeof(*dns_cache)); memset(dns_cache, 0, sizeof(*dns_cache));
key = hash_string(info->domain); key = hash_string(info->domain);
key = jhash(&info->qtype, sizeof(info->qtype), key); key = jhash(&info->qtype, sizeof(info->qtype), key);
key = hash_string_initval(info->dns_group_name, key);
key = jhash(&info->query_flag, sizeof(info->query_flag), key);
atomic_set(&dns_cache->ref, 1); atomic_set(&dns_cache->ref, 1);
memcpy(&dns_cache->info, info, sizeof(*info)); memcpy(&dns_cache->info, info, sizeof(*info));
dns_cache->del_pending = 0; dns_cache->del_pending = 0;
@@ -393,11 +390,11 @@ errout:
return -1; return -1;
} }
int dns_cache_insert(char *domain, int ttl, dns_type_t qtype, int speed, struct dns_cache_data *cache_data) int dns_cache_insert(struct dns_cache_key *cache_key, int ttl, int speed, struct dns_cache_data *cache_data)
{ {
struct dns_cache_info info; struct dns_cache_info info;
if (cache_data == NULL || domain == NULL) { if (cache_data == NULL || cache_key == NULL || cache_key->dns_group_name == NULL || cache_key->domain == NULL) {
return -1; return -1;
} }
@@ -411,8 +408,10 @@ int dns_cache_insert(char *domain, int ttl, dns_type_t qtype, int speed, struct
} }
info.hitnum = 3; info.hitnum = 3;
safe_strncpy(info.domain, domain, DNS_MAX_CNAME_LEN); safe_strncpy(info.domain, cache_key->domain, DNS_MAX_CNAME_LEN);
info.qtype = qtype; info.qtype = cache_key->qtype;
safe_strncpy(info.dns_group_name, cache_key->dns_group_name, DNS_GROUP_NAME_LEN);
info.query_flag = cache_key->query_flag;
info.ttl = ttl; info.ttl = ttl;
info.hitnum_update_add = DNS_CACHE_HITNUM_STEP; info.hitnum_update_add = DNS_CACHE_HITNUM_STEP;
info.speed = speed; info.speed = speed;
@@ -422,7 +421,7 @@ int dns_cache_insert(char *domain, int ttl, dns_type_t qtype, int speed, struct
return _dns_cache_insert(&info, cache_data, &dns_cache_head.cache_list); return _dns_cache_insert(&info, cache_data, &dns_cache_head.cache_list);
} }
struct dns_cache *dns_cache_lookup(char *domain, dns_type_t qtype) struct dns_cache *dns_cache_lookup(struct dns_cache_key *cache_key)
{ {
uint32_t key = 0; uint32_t key = 0;
struct dns_cache *dns_cache = NULL; struct dns_cache *dns_cache = NULL;
@@ -433,19 +432,29 @@ struct dns_cache *dns_cache_lookup(char *domain, dns_type_t qtype)
return NULL; return NULL;
} }
key = hash_string(domain); key = hash_string(cache_key->domain);
key = jhash(&qtype, sizeof(qtype), key); key = jhash(&cache_key->qtype, sizeof(cache_key->qtype), key);
key = hash_string_initval(cache_key->dns_group_name, key);
key = jhash(&cache_key->query_flag, sizeof(cache_key->query_flag), key);
time(&now); time(&now);
/* find cache */ /* find cache */
pthread_mutex_lock(&dns_cache_head.lock); pthread_mutex_lock(&dns_cache_head.lock);
hash_for_each_possible(dns_cache_head.cache_hash, dns_cache, node, key) hash_for_each_possible(dns_cache_head.cache_hash, dns_cache, node, key)
{ {
if (dns_cache->info.qtype != qtype) { if (dns_cache->info.qtype != cache_key->qtype) {
continue; continue;
} }
if (strncmp(domain, dns_cache->info.domain, DNS_MAX_CNAME_LEN) != 0) { if (strncmp(cache_key->domain, dns_cache->info.domain, DNS_MAX_CNAME_LEN) != 0) {
continue;
}
if (strncmp(cache_key->dns_group_name, dns_cache->info.dns_group_name, DNS_GROUP_NAME_LEN) != 0) {
continue;
}
if (cache_key->query_flag != dns_cache->info.query_flag) {
continue; continue;
} }

View File

@@ -21,6 +21,7 @@
#include "atomic.h" #include "atomic.h"
#include "dns.h" #include "dns.h"
#include "dns_conf.h"
#include "hash.h" #include "hash.h"
#include "hashtable.h" #include "hashtable.h"
#include "list.h" #include "list.h"
@@ -48,15 +49,8 @@ enum CACHE_RECORD_TYPE {
CACHE_RECORD_TYPE_INACTIVE, CACHE_RECORD_TYPE_INACTIVE,
}; };
struct dns_cache_query_option {
uint32_t query_flag;
const char *dns_group_name;
};
struct dns_cache_data_head { struct dns_cache_data_head {
enum CACHE_TYPE cache_type; enum CACHE_TYPE cache_type;
uint32_t query_flag;
char dns_group_name[DNS_CACHE_GROUP_NAME_LEN];
int is_soa; int is_soa;
ssize_t size; ssize_t size;
}; };
@@ -87,13 +81,15 @@ struct dns_cache_packet {
struct dns_cache_info { struct dns_cache_info {
char domain[DNS_MAX_CNAME_LEN]; char domain[DNS_MAX_CNAME_LEN];
dns_type_t qtype;
char dns_group_name[DNS_GROUP_NAME_LEN];
uint32_t query_flag;
int ttl; int ttl;
int hitnum; int hitnum;
int speed; int speed;
int hitnum_update_add; int hitnum_update_add;
time_t insert_time; time_t insert_time;
time_t replace_time; time_t replace_time;
dns_type_t qtype;
}; };
struct dns_cache_record { struct dns_cache_record {
@@ -120,26 +116,32 @@ struct dns_cache_file {
uint32_t cache_number; uint32_t cache_number;
}; };
struct dns_cache_key {
const char *domain;
dns_type_t qtype;
const char *dns_group_name;
uint32_t query_flag;
};
enum CACHE_TYPE dns_cache_data_type(struct dns_cache_data *cache_data); enum CACHE_TYPE dns_cache_data_type(struct dns_cache_data *cache_data);
uint32_t dns_cache_get_query_flag(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_data *cache_data); const char *dns_cache_get_dns_group_name(struct dns_cache *dns_cache);
void dns_cache_data_free(struct dns_cache_data *data); void dns_cache_data_free(struct dns_cache_data *data);
struct dns_cache_data *dns_cache_new_data_packet(struct dns_cache_query_option *query_option, void *packet, struct dns_cache_data *dns_cache_new_data_packet(void *packet, size_t packet_len);
size_t packet_len);
int dns_cache_init(int size, int enable_inactive, int inactive_list_expired); int dns_cache_init(int size, int enable_inactive, int inactive_list_expired);
int dns_cache_replace(char *domain, int ttl, dns_type_t qtype, int speed, struct dns_cache_data *cache_data); int dns_cache_replace(struct dns_cache_key *key, int ttl, int speed, struct dns_cache_data *cache_data);
int dns_cache_replace_inactive(char *domain, int ttl, dns_type_t qtype, int speed, struct dns_cache_data *cache_data); int dns_cache_replace_inactive(struct dns_cache_key *key, int ttl, int speed, struct dns_cache_data *cache_data);
int dns_cache_insert(char *domain, int ttl, dns_type_t qtype, int speed, struct dns_cache_data *cache_data); int dns_cache_insert(struct dns_cache_key *key, int ttl, int speed, struct dns_cache_data *cache_data);
struct dns_cache *dns_cache_lookup(char *domain, dns_type_t qtype); struct dns_cache *dns_cache_lookup(struct dns_cache_key *key);
void dns_cache_delete(struct dns_cache *dns_cache); void dns_cache_delete(struct dns_cache *dns_cache);
@@ -166,11 +168,10 @@ struct dns_cache_data *dns_cache_new_data(void);
struct dns_cache_data *dns_cache_get_data(struct dns_cache *dns_cache); 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, struct dns_cache_query_option *query_option, char *cname, void dns_cache_set_data_addr(struct dns_cache_data *dns_cache, char *cname, int cname_ttl, unsigned char *addr,
int cname_ttl, unsigned char *addr, int addr_len); int addr_len);
void dns_cache_set_data_soa(struct dns_cache_data *dns_cache, struct dns_cache_query_option *query_option, char *cname, void dns_cache_set_data_soa(struct dns_cache_data *dns_cache, char *cname, int cname_ttl);
int cname_ttl);
void dns_cache_destroy(void); void dns_cache_destroy(void);

View File

@@ -184,8 +184,9 @@ struct dns_client {
int ssl_verify_skip; int ssl_verify_skip;
/* query list */ /* query list */
pthread_mutex_t dns_request_lock;
struct list_head dns_request_list; struct list_head dns_request_list;
pthread_cond_t run_cond;
atomic_t run_period;
atomic_t dns_server_num; atomic_t dns_server_num;
/* ECS */ /* ECS */
@@ -1312,7 +1313,12 @@ static int _dns_client_server_pending(char *server_ip, int port, dns_server_type
pthread_mutex_lock(&pending_server_mutex); pthread_mutex_lock(&pending_server_mutex);
list_add_tail(&pending->list, &pending_servers); list_add_tail(&pending->list, &pending_servers);
atomic_set(&client.run_period, 1);
pthread_mutex_unlock(&pending_server_mutex); pthread_mutex_unlock(&pending_server_mutex);
pthread_mutex_lock(&client.domain_map_lock);
pthread_cond_signal(&client.run_cond);
pthread_mutex_unlock(&client.domain_map_lock);
return 0; return 0;
errout: errout:
if (pending) { if (pending) {
@@ -3129,7 +3135,13 @@ int dns_client_query(const char *domain, int qtype, dns_client_callback callback
} }
pthread_mutex_lock(&client.domain_map_lock); pthread_mutex_lock(&client.domain_map_lock);
list_add_tail(&query->dns_request_list, &client.dns_request_list); if (hash_hashed(&query->domain_node)) {
if (list_empty(&client.dns_request_list)) {
pthread_cond_signal(&client.run_cond);
}
list_add_tail(&query->dns_request_list, &client.dns_request_list);
}
pthread_mutex_unlock(&client.domain_map_lock); pthread_mutex_unlock(&client.domain_map_lock);
tlog(TLOG_INFO, "send request %s, qtype %d, id %d\n", domain, qtype, query->sid); tlog(TLOG_INFO, "send request %s, qtype %d, id %d\n", domain, qtype, query->sid);
@@ -3262,6 +3274,12 @@ static void _dns_client_add_pending_servers(void)
dely = 0; dely = 0;
pthread_mutex_lock(&pending_server_mutex); pthread_mutex_lock(&pending_server_mutex);
if (list_empty(&pending_servers)) {
atomic_set(&client.run_period, 0);
} else {
atomic_set(&client.run_period, 1);
}
list_for_each_entry_safe(pending, tmp, &pending_servers, list) list_for_each_entry_safe(pending, tmp, &pending_servers, list)
{ {
list_add(&pending->retry_list, &retry_list); list_add(&pending->retry_list, &retry_list);
@@ -3402,15 +3420,25 @@ static void *_dns_client_work(void *arg)
int num = 0; int num = 0;
int i = 0; int i = 0;
unsigned long now = {0}; unsigned long now = {0};
unsigned long last = {0};
unsigned int sleep = 100; unsigned int sleep = 100;
int sleep_time = 0; int sleep_time = 0;
unsigned long expect_time = 0; unsigned long expect_time = 0;
sleep_time = sleep; sleep_time = sleep;
now = get_tick_count() - sleep; now = get_tick_count() - sleep;
last = now;
expect_time = now + sleep; expect_time = now + sleep;
while (atomic_read(&client.run)) { while (atomic_read(&client.run)) {
now = get_tick_count(); now = get_tick_count();
if (sleep_time > 0) {
sleep_time -= now - last;
if (sleep_time <= 0) {
sleep_time = 0;
}
}
last = now;
if (now >= expect_time) { if (now >= expect_time) {
_dns_client_period_run(); _dns_client_period_run();
sleep_time = sleep - (now - expect_time); sleep_time = sleep - (now - expect_time);
@@ -3421,6 +3449,15 @@ static void *_dns_client_work(void *arg)
expect_time += sleep; expect_time += sleep;
} }
pthread_mutex_lock(&client.domain_map_lock);
if (list_empty(&client.dns_request_list) && atomic_read(&client.run_period) == 0) {
pthread_cond_wait(&client.run_cond, &client.domain_map_lock);
expect_time = get_tick_count();
pthread_mutex_unlock(&client.domain_map_lock);
continue;
}
pthread_mutex_unlock(&client.domain_map_lock);
num = epoll_wait(client.epoll_fd, events, DNS_MAX_EVENTS, sleep_time); num = epoll_wait(client.epoll_fd, events, DNS_MAX_EVENTS, sleep_time);
if (num < 0) { if (num < 0) {
usleep(100000); usleep(100000);
@@ -3496,6 +3533,7 @@ int dns_client_init(void)
memset(&client, 0, sizeof(client)); memset(&client, 0, sizeof(client));
pthread_attr_init(&attr); pthread_attr_init(&attr);
atomic_set(&client.dns_server_num, 0); atomic_set(&client.dns_server_num, 0);
atomic_set(&client.run_period, 0);
epollfd = epoll_create1(EPOLL_CLOEXEC); epollfd = epoll_create1(EPOLL_CLOEXEC);
if (epollfd < 0) { if (epollfd < 0) {
@@ -3511,6 +3549,8 @@ int dns_client_init(void)
hash_init(client.group); hash_init(client.group);
INIT_LIST_HEAD(&client.dns_request_list); INIT_LIST_HEAD(&client.dns_request_list);
pthread_cond_init(&client.run_cond, NULL);
if (dns_client_add_group(DNS_SERVER_GROUP_DEFAULT) != 0) { if (dns_client_add_group(DNS_SERVER_GROUP_DEFAULT) != 0) {
tlog(TLOG_ERROR, "add default server group failed."); tlog(TLOG_ERROR, "add default server group failed.");
goto errout; goto errout;
@@ -3542,6 +3582,7 @@ errout:
pthread_mutex_destroy(&client.server_list_lock); pthread_mutex_destroy(&client.server_list_lock);
pthread_mutex_destroy(&client.domain_map_lock); pthread_mutex_destroy(&client.domain_map_lock);
pthread_cond_destroy(&client.run_cond);
return -1; return -1;
} }
@@ -3551,6 +3592,9 @@ void dns_client_exit(void)
if (client.tid) { if (client.tid) {
void *ret = NULL; void *ret = NULL;
atomic_set(&client.run, 0); atomic_set(&client.run, 0);
pthread_mutex_lock(&client.domain_map_lock);
pthread_cond_signal(&client.run_cond);
pthread_mutex_unlock(&client.domain_map_lock);
pthread_join(client.tid, &ret); pthread_join(client.tid, &ret);
client.tid = 0; client.tid = 0;
} }
@@ -3563,6 +3607,7 @@ void dns_client_exit(void)
pthread_mutex_destroy(&client.server_list_lock); pthread_mutex_destroy(&client.server_list_lock);
pthread_mutex_destroy(&client.domain_map_lock); pthread_mutex_destroy(&client.domain_map_lock);
pthread_cond_destroy(&client.run_cond);
if (client.ssl_ctx) { if (client.ssl_ctx) {
SSL_CTX_free(client.ssl_ctx); SSL_CTX_free(client.ssl_ctx);
client.ssl_ctx = NULL; client.ssl_ctx = NULL;

View File

@@ -40,6 +40,11 @@ struct dns_ipset_table {
}; };
static struct dns_ipset_table dns_ipset_table; static struct dns_ipset_table dns_ipset_table;
struct dns_nftset_table {
DECLARE_HASHTABLE(nftset, 8);
};
static struct dns_nftset_table dns_nftset_table;
struct dns_qtype_soa_table dns_qtype_soa_table; struct dns_qtype_soa_table dns_qtype_soa_table;
struct dns_domain_set_rule_table dns_domain_set_rule_table; struct dns_domain_set_rule_table dns_domain_set_rule_table;
@@ -83,6 +88,7 @@ int dns_conf_serve_expired_reply_ttl = 3;
struct dns_servers dns_conf_servers[DNS_MAX_SERVERS]; struct dns_servers dns_conf_servers[DNS_MAX_SERVERS];
char dns_conf_server_name[DNS_MAX_SERVER_NAME_LEN]; char dns_conf_server_name[DNS_MAX_SERVER_NAME_LEN];
int dns_conf_server_num; int dns_conf_server_num;
int dns_conf_resolv_hostname = 1;
struct dns_domain_check_orders dns_conf_check_orders = { struct dns_domain_check_orders dns_conf_check_orders = {
.orders = .orders =
@@ -132,6 +138,8 @@ int dns_conf_local_ttl;
int dns_conf_force_AAAA_SOA; int dns_conf_force_AAAA_SOA;
int dns_conf_force_no_cname; int dns_conf_force_no_cname;
int dns_conf_ipset_timeout_enable; int dns_conf_ipset_timeout_enable;
int dns_conf_nftset_timeout_enable;
int dns_conf_nftset_debug_enable;
char dns_conf_user[DNS_CONF_USRNAME_LEN]; char dns_conf_user[DNS_CONF_USRNAME_LEN];
@@ -169,6 +177,10 @@ static void *_new_dns_rule(enum domain_rule domain_rule)
case DOMAIN_RULE_IPSET_IPV6: case DOMAIN_RULE_IPSET_IPV6:
size = sizeof(struct dns_ipset_rule); size = sizeof(struct dns_ipset_rule);
break; break;
case DOMAIN_RULE_NFTSET_IP:
case DOMAIN_RULE_NFTSET_IP6:
size = sizeof(struct dns_nftset_rule);
break;
case DOMAIN_RULE_NAMESERVER: case DOMAIN_RULE_NAMESERVER:
size = sizeof(struct dns_nameserver_rule); size = sizeof(struct dns_nameserver_rule);
break; break;
@@ -423,6 +435,9 @@ static int _config_server(int argc, char *argv[], dns_server_type_t type, int de
} }
case 'h': { case 'h': {
safe_strncpy(server->hostname, optarg, DNS_MAX_CNAME_LEN); safe_strncpy(server->hostname, optarg, DNS_MAX_CNAME_LEN);
if (strncmp(server->hostname, "-", 2) == 0) {
server->hostname[0] = '\0';
}
break; break;
} }
case 'H': { case 'H': {
@@ -518,7 +533,8 @@ static void _config_address_destroy(radix_node_t *node, void *cbctx)
node->data = NULL; node->data = NULL;
} }
static int _config_domain_set_rule_add_ext(char *set_name, enum domain_rule type, void *rule, unsigned int flags, int is_clear_flag) static int _config_domain_set_rule_add_ext(char *set_name, enum domain_rule type, void *rule, unsigned int flags,
int is_clear_flag)
{ {
struct dns_domain_set_rule *set_rule = NULL; struct dns_domain_set_rule *set_rule = NULL;
struct dns_domain_set_rule_list *set_rule_list = NULL; struct dns_domain_set_rule_list *set_rule_list = NULL;
@@ -851,6 +867,174 @@ errout:
return 0; return 0;
} }
static void _config_nftset_table_destroy(void)
{
struct dns_nftset_name *nftset = NULL;
struct hlist_node *tmp = NULL;
unsigned long i = 0;
hash_for_each_safe(dns_nftset_table.nftset, i, tmp, nftset, node)
{
hlist_del_init(&nftset->node);
free(nftset);
}
}
static const struct dns_nftset_name *_dns_conf_get_nftable(const char *familyname, const char *tablename,
const char *setname)
{
uint32_t key = 0;
struct dns_nftset_name *nftset_name = NULL;
if (familyname == NULL || tablename == NULL || setname == NULL) {
return NULL;
}
const char *hasher[4] = {familyname, tablename, setname, NULL};
key = hash_string_array(hasher);
hash_for_each_possible(dns_nftset_table.nftset, nftset_name, node, key)
{
if (strncmp(nftset_name->nftfamilyname, familyname, DNS_MAX_NFTSET_FAMILYLEN) == 0 &&
strncmp(nftset_name->nfttablename, tablename, DNS_MAX_NFTSET_NAMELEN) == 0 &&
strncmp(nftset_name->nftsetname, setname, DNS_MAX_NFTSET_NAMELEN) == 0) {
return nftset_name;
}
}
nftset_name = malloc(sizeof(*nftset_name));
if (nftset_name == NULL) {
goto errout;
}
safe_strncpy(nftset_name->nftfamilyname, familyname, DNS_MAX_NFTSET_FAMILYLEN);
safe_strncpy(nftset_name->nfttablename, tablename, DNS_MAX_NFTSET_NAMELEN);
safe_strncpy(nftset_name->nftsetname, setname, DNS_MAX_NFTSET_NAMELEN);
hash_add(dns_nftset_table.nftset, &nftset_name->node, key);
return nftset_name;
errout:
if (nftset_name) {
free(nftset_name);
}
return NULL;
}
static int _conf_domain_rule_nftset(char *domain, const char *nftsetname)
{
struct dns_nftset_rule *nftset_rule = NULL;
const struct dns_nftset_name *nftset = NULL;
char *copied_name = NULL;
enum domain_rule type = 0;
int ignore_flag = 0;
char *setname = NULL;
char *tablename = NULL;
char *family = NULL;
copied_name = strdup(nftsetname);
if (copied_name == NULL) {
goto errout;
}
for (char *tok = strtok(copied_name, ","); tok; tok = strtok(NULL, ",")) {
char *saveptr = NULL;
char *tok_set = NULL;
nftset_rule = NULL;
if (strncmp(tok, "#4:", 3U) == 0) {
type = DOMAIN_RULE_NFTSET_IP;
ignore_flag = DOMAIN_FLAG_NFTSET_IP_IGN;
} else if (strncmp(tok, "#6:", 3U) == 0) {
type = DOMAIN_RULE_NFTSET_IP6;
ignore_flag = DOMAIN_FLAG_NFTSET_IP6_IGN;
} else if (strncmp(tok, "-", 2U) == 0) {
_config_domain_rule_flag_set(domain, DOMAIN_FLAG_NFTSET_INET_IGN, 0);
continue;
} else {
goto errout;
}
tok_set = tok + 3;
if (strncmp(tok_set, "-", 2U) == 0) {
_config_domain_rule_flag_set(domain, ignore_flag, 0);
continue;
}
family = strtok_r(tok_set, "#", &saveptr);
if (family == NULL) {
goto errout;
}
tablename = strtok_r(NULL, "#", &saveptr);
if (tablename == NULL) {
goto errout;
}
setname = strtok_r(NULL, "#", &saveptr);
if (setname == NULL) {
goto errout;
}
/* new ipset domain */
nftset = _dns_conf_get_nftable(family, tablename, setname);
if (nftset == NULL) {
goto errout;
}
nftset_rule = _new_dns_rule(type);
if (nftset_rule == NULL) {
goto errout;
}
nftset_rule->nfttablename = nftset->nfttablename;
nftset_rule->nftsetname = nftset->nftsetname;
nftset_rule->familyname = nftset->nftfamilyname;
if (_config_domain_rule_add(domain, type, nftset_rule) != 0) {
goto errout;
}
_dns_rule_put(&nftset_rule->head);
}
goto clear;
errout:
tlog(TLOG_ERROR, "add nftset %s %s failed", domain, nftsetname);
if (nftset_rule) {
_dns_rule_put(&nftset_rule->head);
}
clear:
if (copied_name) {
free(copied_name);
}
return 0;
}
static int _config_nftset(void *data, int argc, char *argv[])
{
char domain[DNS_MAX_CONF_CNAME_LEN];
char *value = argv[1];
if (argc <= 1) {
goto errout;
}
if (_get_domain(value, domain, DNS_MAX_CONF_CNAME_LEN, &value) != 0) {
goto errout;
}
return _conf_domain_rule_nftset(domain, value);
errout:
tlog(TLOG_ERROR, "add nftset %s failed", value);
return 0;
}
static int _conf_domain_rule_address(char *domain, const char *domain_address) static int _conf_domain_rule_address(char *domain, const char *domain_address)
{ {
struct dns_rule_address_IPV4 *address_ipv4 = NULL; struct dns_rule_address_IPV4 *address_ipv4 = NULL;
@@ -981,6 +1165,11 @@ errout:
return 0; return 0;
} }
static void _config_speed_check_mode_clear(struct dns_domain_check_orders *check_orders)
{
memset(check_orders->orders, 0, sizeof(check_orders->orders));
}
static int _config_speed_check_mode_parser(struct dns_domain_check_orders *check_orders, const char *mode) static int _config_speed_check_mode_parser(struct dns_domain_check_orders *check_orders, const char *mode)
{ {
char tmpbuff[DNS_MAX_OPT_LEN]; char tmpbuff[DNS_MAX_OPT_LEN];
@@ -991,6 +1180,7 @@ static int _config_speed_check_mode_parser(struct dns_domain_check_orders *check
int i = 0; int i = 0;
safe_strncpy(tmpbuff, mode, DNS_MAX_OPT_LEN); safe_strncpy(tmpbuff, mode, DNS_MAX_OPT_LEN);
_config_speed_check_mode_clear(check_orders);
ptr = tmpbuff; ptr = tmpbuff;
do { do {
@@ -1663,6 +1853,7 @@ static int _conf_domain_rules(void *data, int argc, char *argv[])
{"speed-check-mode", required_argument, NULL, 'c'}, {"speed-check-mode", required_argument, NULL, 'c'},
{"address", required_argument, NULL, 'a'}, {"address", required_argument, NULL, 'a'},
{"ipset", required_argument, NULL, 'p'}, {"ipset", required_argument, NULL, 'p'},
{"nftset", required_argument, NULL, 't'},
{"nameserver", required_argument, NULL, 'n'}, {"nameserver", required_argument, NULL, 'n'},
{"dualstack-ip-selection", required_argument, NULL, 'd'}, {"dualstack-ip-selection", required_argument, NULL, 'd'},
{NULL, no_argument, NULL, 0} {NULL, no_argument, NULL, 0}
@@ -1748,6 +1939,19 @@ static int _conf_domain_rules(void *data, int argc, char *argv[])
break; break;
} }
case 't': {
const char *nftsetname = optarg;
if (nftsetname == NULL) {
goto errout;
}
if (_conf_domain_rule_nftset(domain, nftsetname) != 0) {
tlog(TLOG_ERROR, "add nftset rule failed.");
goto errout;
}
break;
}
default: default:
break; break;
} }
@@ -2121,26 +2325,42 @@ static int _config_log_level(void *data, int argc, char *argv[])
static void _config_setup_smartdns_domain(void) static void _config_setup_smartdns_domain(void)
{ {
char hostname[DNS_MAX_CNAME_LEN]; char hostname[DNS_MAX_CNAME_LEN];
/* get local host name */ char domainname[DNS_MAX_CNAME_LEN];
if (getdomainname(hostname, DNS_MAX_CNAME_LEN) != 0) {
gethostname(hostname, DNS_MAX_CNAME_LEN); hostname[0] = '\0';
domainname[0] = '\0';
/* get local domain name */
if (getdomainname(domainname, DNS_MAX_CNAME_LEN - 1) == 0) {
/* check domain is valid */
if (strncmp(domainname, "(none)", DNS_MAX_CNAME_LEN - 1) == 0) {
domainname[0] = '\0';
}
} }
/* get host name again */ if (gethostname(hostname, DNS_MAX_CNAME_LEN - 1) == 0) {
if (strncmp(hostname, "(none)", DNS_MAX_CNAME_LEN - 1) == 0) { /* check hostname is valid */
gethostname(hostname, DNS_MAX_CNAME_LEN); if (strncmp(hostname, "(none)", DNS_MAX_CNAME_LEN - 1) == 0) {
hostname[0] = '\0';
}
} }
/* if hostname is (none), return smartdns */ if (dns_conf_resolv_hostname == 1) {
if (strncmp(hostname, "(none)", DNS_MAX_CNAME_LEN - 1) == 0) { /* add hostname to rule table */
safe_strncpy(hostname, "smartdns", DNS_MAX_CNAME_LEN); if (hostname[0] != '\0') {
_config_domain_rule_flag_set(hostname, DOMAIN_FLAG_SMARTDNS_DOMAIN, 0);
}
/* add domainname to rule table */
if (domainname[0] != '\0') {
char full_domain[DNS_MAX_CNAME_LEN];
snprintf(full_domain, DNS_MAX_CNAME_LEN, "%.64s.%.128s", hostname, domainname);
_config_domain_rule_flag_set(full_domain, DOMAIN_FLAG_SMARTDNS_DOMAIN, 0);
}
} }
if (hostname[0] != '\0') { /* add server name to rule table */
_config_domain_rule_flag_set(hostname, DOMAIN_FLAG_SMARTDNS_DOMAIN, 0); if (dns_conf_server_name[0] != '\0' && strncmp(dns_conf_server_name, "smartdns", DNS_MAX_CNAME_LEN - 1) != 0) {
}
if (dns_conf_server_name[0] != '\0') {
_config_domain_rule_flag_set(dns_conf_server_name, DOMAIN_FLAG_SMARTDNS_DOMAIN, 0); _config_domain_rule_flag_set(dns_conf_server_name, DOMAIN_FLAG_SMARTDNS_DOMAIN, 0);
} }
@@ -2149,6 +2369,7 @@ static void _config_setup_smartdns_domain(void)
static struct config_item _config_item[] = { static struct config_item _config_item[] = {
CONF_STRING("server-name", (char *)dns_conf_server_name, DNS_MAX_SERVER_NAME_LEN), CONF_STRING("server-name", (char *)dns_conf_server_name, DNS_MAX_SERVER_NAME_LEN),
CONF_YESNO("resolv-hostname", &dns_conf_resolv_hostname),
CONF_CUSTOM("bind", _config_bind_ip_udp, NULL), CONF_CUSTOM("bind", _config_bind_ip_udp, NULL),
CONF_CUSTOM("bind-tcp", _config_bind_ip_tcp, NULL), CONF_CUSTOM("bind-tcp", _config_bind_ip_tcp, NULL),
CONF_CUSTOM("server", _config_server_udp, NULL), CONF_CUSTOM("server", _config_server_udp, NULL),
@@ -2159,6 +2380,9 @@ static struct config_item _config_item[] = {
CONF_CUSTOM("address", _config_address, NULL), CONF_CUSTOM("address", _config_address, NULL),
CONF_YESNO("ipset-timeout", &dns_conf_ipset_timeout_enable), CONF_YESNO("ipset-timeout", &dns_conf_ipset_timeout_enable),
CONF_CUSTOM("ipset", _config_ipset, NULL), CONF_CUSTOM("ipset", _config_ipset, NULL),
CONF_YESNO("nftset-timeout", &dns_conf_nftset_timeout_enable),
CONF_YESNO("nftset-debug", &dns_conf_nftset_debug_enable),
CONF_CUSTOM("nftset", _config_nftset, NULL),
CONF_CUSTOM("speed-check-mode", _config_speed_check_mode, NULL), CONF_CUSTOM("speed-check-mode", _config_speed_check_mode, NULL),
CONF_INT("tcp-idle-time", &dns_conf_tcp_idle_time, 0, 3600), CONF_INT("tcp-idle-time", &dns_conf_tcp_idle_time, 0, 3600),
CONF_INT("cache-size", &dns_conf_cachesize, 0, CONF_INT_MAX), CONF_INT("cache-size", &dns_conf_cachesize, 0, CONF_INT_MAX),
@@ -2365,6 +2589,7 @@ static int _dns_server_load_conf_init(void)
art_tree_init(&dns_conf_domain_rule); art_tree_init(&dns_conf_domain_rule);
hash_init(dns_ipset_table.ipset); hash_init(dns_ipset_table.ipset);
hash_init(dns_nftset_table.nftset);
hash_init(dns_qtype_soa_table.qtype); hash_init(dns_qtype_soa_table.qtype);
hash_init(dns_group_table.group); hash_init(dns_group_table.group);
hash_init(dns_hosts_table.hosts); hash_init(dns_hosts_table.hosts);
@@ -2372,8 +2597,6 @@ static int _dns_server_load_conf_init(void)
hash_init(dns_domain_set_rule_table.rule_list); hash_init(dns_domain_set_rule_table.rule_list);
hash_init(dns_domain_set_name_table.names); hash_init(dns_domain_set_name_table.names);
_config_setup_smartdns_domain();
return 0; return 0;
} }
@@ -2383,6 +2606,7 @@ void dns_server_load_exit(void)
Destroy_Radix(dns_conf_address_rule.ipv4, _config_address_destroy, NULL); Destroy_Radix(dns_conf_address_rule.ipv4, _config_address_destroy, NULL);
Destroy_Radix(dns_conf_address_rule.ipv6, _config_address_destroy, NULL); Destroy_Radix(dns_conf_address_rule.ipv6, _config_address_destroy, NULL);
_config_ipset_table_destroy(); _config_ipset_table_destroy();
_config_nftset_table_destroy();
_config_group_table_destroy(); _config_group_table_destroy();
_config_ptr_table_destroy(); _config_ptr_table_destroy();
_config_host_table_destroy(); _config_host_table_destroy();
@@ -2456,6 +2680,7 @@ errout:
static int _dns_conf_load_post(void) static int _dns_conf_load_post(void)
{ {
_config_setup_smartdns_domain();
_dns_conf_speed_check_mode_verify(); _dns_conf_speed_check_mode_verify();
if (dns_conf_cachesize == 0 && dns_conf_response_mode == DNS_RESPONSE_MODE_FASTEST_RESPONSE) { if (dns_conf_cachesize == 0 && dns_conf_response_mode == DNS_RESPONSE_MODE_FASTEST_RESPONSE) {

View File

@@ -38,6 +38,8 @@ extern "C" {
#define DNS_MAX_SERVER_NAME_LEN 128 #define DNS_MAX_SERVER_NAME_LEN 128
#define DNS_MAX_PTR_LEN 128 #define DNS_MAX_PTR_LEN 128
#define DNS_MAX_IPSET_NAMELEN 32 #define DNS_MAX_IPSET_NAMELEN 32
#define DNS_MAX_NFTSET_FAMILYLEN 8
#define DNS_MAX_NFTSET_NAMELEN 256
#define DNS_GROUP_NAME_LEN 32 #define DNS_GROUP_NAME_LEN 32
#define DNS_NAX_GROUP_NUMBER 16 #define DNS_NAX_GROUP_NUMBER 16
#define DNS_MAX_IPLEN 64 #define DNS_MAX_IPLEN 64
@@ -62,6 +64,8 @@ enum domain_rule {
DOMAIN_RULE_IPSET, DOMAIN_RULE_IPSET,
DOMAIN_RULE_IPSET_IPV4, DOMAIN_RULE_IPSET_IPV4,
DOMAIN_RULE_IPSET_IPV6, DOMAIN_RULE_IPSET_IPV6,
DOMAIN_RULE_NFTSET_IP,
DOMAIN_RULE_NFTSET_IP6,
DOMAIN_RULE_NAMESERVER, DOMAIN_RULE_NAMESERVER,
DOMAIN_RULE_CHECKSPEED, DOMAIN_RULE_CHECKSPEED,
DOMAIN_RULE_MAX, DOMAIN_RULE_MAX,
@@ -90,6 +94,9 @@ typedef enum {
#define DOMAIN_FLAG_NAMESERVER_IGNORE (1 << 9) #define DOMAIN_FLAG_NAMESERVER_IGNORE (1 << 9)
#define DOMAIN_FLAG_DUALSTACK_SELECT (1 << 10) #define DOMAIN_FLAG_DUALSTACK_SELECT (1 << 10)
#define DOMAIN_FLAG_SMARTDNS_DOMAIN (1 << 11) #define DOMAIN_FLAG_SMARTDNS_DOMAIN (1 << 11)
#define DOMAIN_FLAG_NFTSET_INET_IGN (1 << 12)
#define DOMAIN_FLAG_NFTSET_IP_IGN (1 << 13)
#define DOMAIN_FLAG_NFTSET_IP6_IGN (1 << 14)
#define SERVER_FLAG_EXCLUDE_DEFAULT (1 << 0) #define SERVER_FLAG_EXCLUDE_DEFAULT (1 << 0)
@@ -135,9 +142,24 @@ struct dns_ipset_rule {
const char *ipsetname; const char *ipsetname;
}; };
struct dns_nftset_name {
struct hlist_node node;
char nftfamilyname[DNS_MAX_NFTSET_FAMILYLEN];
char nfttablename[DNS_MAX_NFTSET_NAMELEN];
char nftsetname[DNS_MAX_NFTSET_NAMELEN];
};
struct dns_nftset_rule {
struct dns_rule head;
const char *familyname;
const char *nfttablename;
const char *nftsetname;
};
struct dns_domain_rule { struct dns_domain_rule {
struct dns_rule head; struct dns_rule head;
struct dns_rule *rules[DOMAIN_RULE_MAX]; struct dns_rule *rules[DOMAIN_RULE_MAX];
int is_sub_rule[DOMAIN_RULE_MAX];
}; };
struct dns_nameserver_rule { struct dns_nameserver_rule {
@@ -366,6 +388,8 @@ extern int dns_conf_rr_ttl_min;
extern int dns_conf_rr_ttl_max; extern int dns_conf_rr_ttl_max;
extern int dns_conf_force_AAAA_SOA; extern int dns_conf_force_AAAA_SOA;
extern int dns_conf_ipset_timeout_enable; extern int dns_conf_ipset_timeout_enable;
extern int dns_conf_nftset_timeout_enable;
extern int dns_conf_nftset_debug_enable;
extern int dns_conf_local_ttl; extern int dns_conf_local_ttl;
extern int dns_conf_force_no_cname; extern int dns_conf_force_no_cname;

View File

@@ -28,6 +28,7 @@
#include "fast_ping.h" #include "fast_ping.h"
#include "hashtable.h" #include "hashtable.h"
#include "list.h" #include "list.h"
#include "nftset.h"
#include "tlog.h" #include "tlog.h"
#include "util.h" #include "util.h"
#include <errno.h> #include <errno.h>
@@ -40,6 +41,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <sys/epoll.h> #include <sys/epoll.h>
#include <sys/eventfd.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/types.h> #include <sys/types.h>
@@ -154,6 +156,8 @@ struct dns_request_pending_list {
pthread_mutex_t request_list_lock; pthread_mutex_t request_list_lock;
unsigned short qtype; unsigned short qtype;
char domain[DNS_MAX_CNAME_LEN]; char domain[DNS_MAX_CNAME_LEN];
uint32_t server_flags;
char dns_group_name[DNS_GROUP_NAME_LEN];
struct list_head request_list; struct list_head request_list;
struct hlist_node node; struct hlist_node node;
}; };
@@ -176,11 +180,13 @@ struct dns_request {
/* dns query */ /* dns query */
char domain[DNS_MAX_CNAME_LEN]; char domain[DNS_MAX_CNAME_LEN];
dns_type_t qtype; dns_type_t qtype;
int qclass;
unsigned long send_tick; unsigned long send_tick;
unsigned short id; unsigned short id;
unsigned short rcode; unsigned short rcode;
unsigned short ss_family; unsigned short ss_family;
char remote_server_fail; char remote_server_fail;
char skip_qtype_soa;
socklen_t addr_len; socklen_t addr_len;
union { union {
struct sockaddr_in in; struct sockaddr_in in;
@@ -246,6 +252,7 @@ struct dns_request {
struct dns_server { struct dns_server {
atomic_t run; atomic_t run;
int epoll_fd; int epoll_fd;
int event_fd;
struct list_head conn_list; struct list_head conn_list;
/* dns request list */ /* dns request list */
@@ -269,7 +276,14 @@ static void _dns_server_request_get(struct dns_request *request);
static void _dns_server_request_release(struct dns_request *request); static void _dns_server_request_release(struct dns_request *request);
static void _dns_server_request_release_complete(struct dns_request *request, int do_complete); static void _dns_server_request_release_complete(struct dns_request *request, int do_complete);
static int _dns_server_reply_passthrouth(struct dns_server_post_context *context); static int _dns_server_reply_passthrouth(struct dns_server_post_context *context);
static int _dns_server_do_query(struct dns_request *request); static int _dns_server_do_query(struct dns_request *request, int skip_notify_event);
static void _dns_server_wakup_thread(void)
{
uint64_t u = 1;
int unused __attribute__((unused));
unused = write(server.event_fd, &u, sizeof(u));
}
static int _dns_server_forward_request(unsigned char *inpacket, int inpacket_len) static int _dns_server_forward_request(unsigned char *inpacket, int inpacket_len)
{ {
@@ -325,6 +339,15 @@ static void *_dns_server_get_dns_rule(struct dns_request *request, enum domain_r
return request->domain_rule.rules[rule]; return request->domain_rule.rules[rule];
} }
static int _dns_server_is_dns_rule_extact_match(struct dns_request *request, enum domain_rule rule)
{
if (rule >= DOMAIN_RULE_MAX || request == NULL) {
return 0;
}
return request->domain_rule.is_sub_rule[rule] == 0;
}
static void _dns_server_set_dualstack_selection(struct dns_request *request) static void _dns_server_set_dualstack_selection(struct dns_request *request)
{ {
struct dns_rule_flags *rule_flag = NULL; struct dns_rule_flags *rule_flag = NULL;
@@ -364,12 +387,6 @@ static int _dns_server_is_return_soa(struct dns_request *request)
return 0; return 0;
} }
if (request->qtype == DNS_T_AAAA) {
if (_dns_server_has_bind_flag(request, BIND_FLAG_FORCE_AAAA_SOA) == 0 || dns_conf_force_AAAA_SOA == 1) {
return 1;
}
}
rule_flag = _dns_server_get_dns_rule(request, DOMAIN_RULE_FLAGS); rule_flag = _dns_server_get_dns_rule(request, DOMAIN_RULE_FLAGS);
if (rule_flag) { if (rule_flag) {
flags = rule_flag->flags; flags = rule_flag->flags;
@@ -377,11 +394,39 @@ static int _dns_server_is_return_soa(struct dns_request *request)
return 1; return 1;
} }
if ((flags & DOMAIN_FLAG_ADDR_IPV4_SOA) && (request->qtype == DNS_T_A)) { if (flags & DOMAIN_FLAG_ADDR_IGN) {
return 1; request->skip_qtype_soa = 1;
return 0;
} }
if ((flags & DOMAIN_FLAG_ADDR_IPV6_SOA) && (request->qtype == DNS_T_AAAA)) { switch (request->qtype) {
case DNS_T_A:
if (flags & DOMAIN_FLAG_ADDR_IPV4_SOA) {
return 1;
}
if (flags & DOMAIN_FLAG_ADDR_IPV4_IGN) {
request->skip_qtype_soa = 1;
return 0;
}
break;
case DNS_T_AAAA:
if (flags & DOMAIN_FLAG_ADDR_IPV6_SOA) {
return 1;
}
if (flags & DOMAIN_FLAG_ADDR_IPV6_IGN) {
request->skip_qtype_soa = 1;
return 0;
}
break;
default:
break;
}
}
if (request->qtype == DNS_T_AAAA) {
if (_dns_server_has_bind_flag(request, BIND_FLAG_FORCE_AAAA_SOA) == 0 || dns_conf_force_AAAA_SOA == 1) {
return 1; return 1;
} }
} }
@@ -783,7 +828,7 @@ static int _dns_setup_dns_packet(struct dns_server_post_context *context)
} }
/* add request domain */ /* add request domain */
ret = dns_add_domain(context->packet, request->domain, context->qtype, DNS_C_IN); ret = dns_add_domain(context->packet, request->domain, context->qtype, request->qclass);
if (ret != 0) { if (ret != 0) {
return -1; return -1;
} }
@@ -896,10 +941,57 @@ static int _dns_server_reply_udp(struct dns_request *request, struct dns_server_
unsigned char *inpacket, int inpacket_len) unsigned char *inpacket, int inpacket_len)
{ {
int send_len = 0; int send_len = 0;
struct iovec iovec[1];
struct msghdr msg;
struct cmsghdr *cmsg;
char msg_control[64];
if (atomic_read(&server.run) == 0 || inpacket == NULL || inpacket_len <= 0) { if (atomic_read(&server.run) == 0 || inpacket == NULL || inpacket_len <= 0) {
return -1; return -1;
} }
iovec[0].iov_base = inpacket;
iovec[0].iov_len = inpacket_len;
memset(msg_control, 0, sizeof(msg_control));
msg.msg_iov = iovec;
msg.msg_iovlen = 1;
msg.msg_control = msg_control;
msg.msg_controllen = sizeof(msg_control);
msg.msg_flags = 0;
msg.msg_name = &request->addr;
msg.msg_namelen = request->addr_len;
cmsg = CMSG_FIRSTHDR(&msg);
if (request->localaddr.ss_family == AF_INET) {
struct sockaddr_in *s4 = (struct sockaddr_in *)&request->localaddr;
cmsg->cmsg_level = SOL_IP;
cmsg->cmsg_type = IP_PKTINFO;
cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
msg.msg_controllen = CMSG_SPACE(sizeof(struct in_pktinfo));
struct in_pktinfo *pktinfo = (struct in_pktinfo *)CMSG_DATA(cmsg);
memset(pktinfo, 0, sizeof(*pktinfo));
pktinfo->ipi_spec_dst = s4->sin_addr;
} else if (request->localaddr.ss_family == AF_INET6) {
struct sockaddr_in6 *s6 = (struct sockaddr_in6 *)&request->localaddr;
cmsg->cmsg_level = IPPROTO_IPV6;
cmsg->cmsg_type = IPV6_PKTINFO;
cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
msg.msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo));
struct in6_pktinfo *pktinfo = (struct in6_pktinfo *)CMSG_DATA(cmsg);
memset(pktinfo, 0, sizeof(*pktinfo));
pktinfo->ipi6_addr = s6->sin6_addr;
} else {
goto use_send;
}
send_len = sendmsg(udpserver->head.fd, &msg, 0);
if (send_len == inpacket_len) {
return 0;
}
use_send:
send_len = sendto(udpserver->head.fd, inpacket, inpacket_len, 0, &request->addr, request->addr_len); send_len = sendto(udpserver->head.fd, inpacket, inpacket_len, 0, &request->addr, request->addr_len);
if (send_len != inpacket_len) { if (send_len != inpacket_len) {
tlog(TLOG_ERROR, "send failed, %s", strerror(errno)); tlog(TLOG_ERROR, "send failed, %s", strerror(errno));
@@ -950,8 +1042,6 @@ static int _dns_server_request_update_cache(struct dns_request *request, dns_typ
speed = request->ping_time; speed = request->ping_time;
if (has_soa) { if (has_soa) {
struct dns_cache_query_option cache_option;
if (request->dualstack_selection && request->has_ip && request->qtype == DNS_T_AAAA) { if (request->dualstack_selection && request->has_ip && request->qtype == DNS_T_AAAA) {
ttl = _dns_server_get_conf_ttl(request->ip_ttl); ttl = _dns_server_get_conf_ttl(request->ip_ttl);
} else { } else {
@@ -961,27 +1051,31 @@ static int _dns_server_request_update_cache(struct dns_request *request, dns_typ
} }
} }
cache_option.query_flag = request->server_flags; dns_cache_set_data_soa(cache_data, request->cname, request->ttl_cname);
cache_option.dns_group_name = 0;
dns_cache_set_data_soa(cache_data, &cache_option, request->cname, request->ttl_cname);
} }
tlog(TLOG_DEBUG, "cache %s qtype: %d ttl: %d\n", request->domain, qtype, ttl); tlog(TLOG_DEBUG, "cache %s qtype: %d ttl: %d\n", request->domain, qtype, ttl);
/* if doing prefetch, update cache only */ /* if doing prefetch, update cache only */
struct dns_cache_key cache_key;
cache_key.dns_group_name = request->dns_group_name;
cache_key.domain = request->domain;
cache_key.qtype = request->qtype;
cache_key.query_flag = request->server_flags;
if (request->prefetch) { if (request->prefetch) {
if (request->prefetch_expired_domain == 0) { if (request->prefetch_expired_domain == 0) {
if (dns_cache_replace(request->domain, ttl, qtype, speed, cache_data) != 0) { if (dns_cache_replace(&cache_key, ttl, speed, cache_data) != 0) {
goto errout; goto errout;
} }
} else { } else {
if (dns_cache_replace_inactive(request->domain, ttl, qtype, speed, cache_data) != 0) { if (dns_cache_replace_inactive(&cache_key, ttl, speed, cache_data) != 0) {
goto errout; goto errout;
} }
} }
} else { } else {
/* insert result to cache */ /* insert result to cache */
if (dns_cache_insert(request->domain, ttl, qtype, speed, cache_data) != 0) { if (dns_cache_insert(&cache_key, ttl, speed, cache_data) != 0) {
goto errout; goto errout;
} }
} }
@@ -998,7 +1092,6 @@ static int _dns_cache_cname_packet(struct dns_server_post_context *context)
{ {
struct dns_packet *packet = context->packet; struct dns_packet *packet = context->packet;
struct dns_packet *cname_packet = NULL; struct dns_packet *cname_packet = NULL;
struct dns_cache_query_option cache_option;
int ret = 0; int ret = 0;
int i = 0; int i = 0;
int j = 0; int j = 0;
@@ -1097,9 +1190,7 @@ static int _dns_cache_cname_packet(struct dns_server_post_context *context)
return -1; return -1;
} }
cache_option.query_flag = request->server_flags; cache_packet = dns_cache_new_data_packet(inpacket_buff, inpacket_len);
cache_option.dns_group_name = request->dns_group_name;
cache_packet = dns_cache_new_data_packet(&cache_option, inpacket_buff, inpacket_len);
if (cache_packet == NULL) { if (cache_packet == NULL) {
return -1; return -1;
} }
@@ -1114,19 +1205,25 @@ static int _dns_cache_cname_packet(struct dns_server_post_context *context)
tlog(TLOG_DEBUG, "Cache CNAME: %s, qtype: %d, speed: %d", request->cname, request->qtype, speed); tlog(TLOG_DEBUG, "Cache CNAME: %s, qtype: %d, speed: %d", request->cname, request->qtype, speed);
/* if doing prefetch, update cache only */ /* if doing prefetch, update cache only */
struct dns_cache_key cache_key;
cache_key.dns_group_name = request->dns_group_name;
cache_key.domain = request->cname;
cache_key.qtype = context->qtype;
cache_key.query_flag = request->server_flags;
if (request->prefetch) { if (request->prefetch) {
if (request->prefetch_expired_domain == 0) { if (request->prefetch_expired_domain == 0) {
if (dns_cache_replace(request->cname, ttl, context->qtype, speed, cache_packet) != 0) { if (dns_cache_replace(&cache_key, ttl, speed, cache_packet) != 0) {
goto errout; goto errout;
} }
} else { } else {
if (dns_cache_replace_inactive(request->cname, ttl, context->qtype, speed, cache_packet) != 0) { if (dns_cache_replace_inactive(&cache_key, ttl, speed, cache_packet) != 0) {
goto errout; goto errout;
} }
} }
} else { } else {
/* insert result to cache */ /* insert result to cache */
if (dns_cache_insert(request->cname, ttl, context->qtype, speed, cache_packet) != 0) { if (dns_cache_insert(&cache_key, ttl, speed, cache_packet) != 0) {
goto errout; goto errout;
} }
} }
@@ -1142,25 +1239,28 @@ errout:
static int _dns_cache_packet(struct dns_server_post_context *context) static int _dns_cache_packet(struct dns_server_post_context *context)
{ {
struct dns_cache_query_option cache_option;
struct dns_request *request = context->request; struct dns_request *request = context->request;
cache_option.query_flag = request->server_flags; struct dns_cache_data *cache_packet = dns_cache_new_data_packet(context->inpacket, context->inpacket_len);
cache_option.dns_group_name = request->dns_group_name;
struct dns_cache_data *cache_packet =
dns_cache_new_data_packet(&cache_option, context->inpacket, context->inpacket_len);
if (cache_packet == NULL) { if (cache_packet == NULL) {
return -1; return -1;
} }
/* if doing prefetch, update cache only */ /* if doing prefetch, update cache only */
struct dns_cache_key cache_key;
cache_key.dns_group_name = request->dns_group_name;
cache_key.domain = request->domain;
cache_key.qtype = context->qtype;
cache_key.query_flag = request->server_flags;
if (request->prefetch) { if (request->prefetch) {
if (dns_cache_replace(request->domain, context->reply_ttl, context->qtype, -1, cache_packet) != 0) { if (dns_cache_replace(&cache_key, context->reply_ttl, -1, cache_packet) != 0) {
goto errout; goto errout;
} }
} else { } else {
/* insert result to cache */ /* insert result to cache */
if (dns_cache_insert(request->domain, context->reply_ttl, context->qtype, -1, cache_packet) != 0) { if (dns_cache_insert(&cache_key, context->reply_ttl, -1, cache_packet) != 0) {
goto errout; goto errout;
} }
} }
@@ -1259,12 +1359,18 @@ static int _dns_cache_reply_packet(struct dns_server_post_context *context)
return 0; return 0;
} }
if (context->packet->head.rcode == DNS_RC_SERVFAIL || context->packet->head.rcode == DNS_RC_NXDOMAIN) { if (context->packet->head.rcode == DNS_RC_SERVFAIL || context->packet->head.rcode == DNS_RC_NXDOMAIN ||
context->packet->head.rcode == DNS_RC_NOTIMP) {
context->reply_ttl = DNS_SERVER_FAIL_TTL; context->reply_ttl = DNS_SERVER_FAIL_TTL;
/* Do not cache record if cannot connect to remote */ /* Do not cache record if cannot connect to remote */
if (request->remote_server_fail == 0 && context->packet->head.rcode == DNS_RC_SERVFAIL) { if (request->remote_server_fail == 0 && context->packet->head.rcode == DNS_RC_SERVFAIL) {
return 0; return 0;
} }
if (context->packet->head.rcode == DNS_RC_NOTIMP) {
return 0;
}
return _dns_cache_packet(context); return _dns_cache_packet(context);
} }
@@ -1272,11 +1378,7 @@ static int _dns_cache_reply_packet(struct dns_server_post_context *context)
return _dns_cache_specify_packet(context); return _dns_cache_specify_packet(context);
} }
struct dns_cache_query_option cache_option; struct dns_cache_data *cache_packet = dns_cache_new_data_packet(context->inpacket, context->inpacket_len);
cache_option.query_flag = request->server_flags;
cache_option.dns_group_name = request->dns_group_name;
struct dns_cache_data *cache_packet =
dns_cache_new_data_packet(&cache_option, context->inpacket, context->inpacket_len);
if (cache_packet == NULL) { if (cache_packet == NULL) {
return -1; return -1;
} }
@@ -1298,7 +1400,7 @@ static int _dns_cache_reply_packet(struct dns_server_post_context *context)
return 0; return 0;
} }
static int _dns_server_setup_ipset_packet(struct dns_server_post_context *context) static int _dns_server_setup_ipset_nftset_packet(struct dns_server_post_context *context)
{ {
int ttl = 0; int ttl = 0;
struct dns_request *request = context->request; struct dns_request *request = context->request;
@@ -1311,6 +1413,8 @@ static int _dns_server_setup_ipset_packet(struct dns_server_post_context *contex
struct dns_ipset_rule *ipset_rule = NULL; struct dns_ipset_rule *ipset_rule = NULL;
struct dns_ipset_rule *ipset_rule_v4 = NULL; struct dns_ipset_rule *ipset_rule_v4 = NULL;
struct dns_ipset_rule *ipset_rule_v6 = NULL; struct dns_ipset_rule *ipset_rule_v6 = NULL;
struct dns_nftset_rule *nftset_ip = NULL;
struct dns_nftset_rule *nftset_ip6 = NULL;
struct dns_rule_flags *rule_flags = NULL; struct dns_rule_flags *rule_flags = NULL;
if (_dns_server_has_bind_flag(request, BIND_FLAG_NO_RULE_IPSET) == 0) { if (_dns_server_has_bind_flag(request, BIND_FLAG_NO_RULE_IPSET) == 0) {
@@ -1330,14 +1434,24 @@ static int _dns_server_setup_ipset_packet(struct dns_server_post_context *contex
if (!rule_flags || (rule_flags->flags & DOMAIN_FLAG_IPSET_IGN) == 0) { if (!rule_flags || (rule_flags->flags & DOMAIN_FLAG_IPSET_IGN) == 0) {
ipset_rule = _dns_server_get_dns_rule(request, DOMAIN_RULE_IPSET); ipset_rule = _dns_server_get_dns_rule(request, DOMAIN_RULE_IPSET);
} }
if (!rule_flags || (rule_flags->flags & DOMAIN_FLAG_IPSET_IPV4_IGN) == 0) { if (!rule_flags || (rule_flags->flags & DOMAIN_FLAG_IPSET_IPV4_IGN) == 0) {
ipset_rule_v4 = _dns_server_get_dns_rule(request, DOMAIN_RULE_IPSET_IPV4); ipset_rule_v4 = _dns_server_get_dns_rule(request, DOMAIN_RULE_IPSET_IPV4);
} }
if (!rule_flags || (rule_flags->flags & DOMAIN_FLAG_IPSET_IPV6_IGN) == 0) { if (!rule_flags || (rule_flags->flags & DOMAIN_FLAG_IPSET_IPV6_IGN) == 0) {
ipset_rule_v6 = _dns_server_get_dns_rule(request, DOMAIN_RULE_IPSET_IPV6); ipset_rule_v6 = _dns_server_get_dns_rule(request, DOMAIN_RULE_IPSET_IPV6);
} }
if (!(ipset_rule || ipset_rule_v4 || ipset_rule_v6)) { if (!rule_flags || (rule_flags->flags & DOMAIN_FLAG_NFTSET_IP_IGN) == 0) {
nftset_ip = _dns_server_get_dns_rule(request, DOMAIN_RULE_NFTSET_IP);
}
if (!rule_flags || (rule_flags->flags & DOMAIN_FLAG_NFTSET_IP6_IGN) == 0) {
nftset_ip6 = _dns_server_get_dns_rule(request, DOMAIN_RULE_NFTSET_IP6);
}
if (!(ipset_rule || ipset_rule_v4 || ipset_rule_v6 || nftset_ip || nftset_ip6)) {
return 0; return 0;
} }
@@ -1354,14 +1468,21 @@ static int _dns_server_setup_ipset_packet(struct dns_server_post_context *contex
dns_get_A(rrs, name, DNS_MAX_CNAME_LEN, &ttl, addr); dns_get_A(rrs, name, DNS_MAX_CNAME_LEN, &ttl, addr);
rule = ipset_rule_v4 ? ipset_rule_v4 : ipset_rule; rule = ipset_rule_v4 ? ipset_rule_v4 : ipset_rule;
if (rule == NULL) { if (rule != NULL) {
break; /* add IPV4 to ipset */
tlog(TLOG_DEBUG, "IPSET-MATCH: domain: %s, ipset: %s, IP: %d.%d.%d.%d", request->domain,
rule->ipsetname, addr[0], addr[1], addr[2], addr[3]);
ipset_add(rule->ipsetname, addr, DNS_RR_A_LEN, request->ip_ttl * 2);
} }
/* add IPV4 to ipset */ if (nftset_ip != NULL) {
ipset_add(rule->ipsetname, addr, DNS_RR_A_LEN, request->ip_ttl * 2); /* add IPV4 to ipset */
tlog(TLOG_DEBUG, "IPSET-MATCH: domain: %s, ipset: %s, IP: %d.%d.%d.%d", request->domain, tlog(TLOG_DEBUG, "NFTSET-MATCH: domain: %s, nftset: %s %s %s, IP: %d.%d.%d.%d", request->domain,
rule->ipsetname, addr[0], addr[1], addr[2], addr[3]); nftset_ip->familyname, nftset_ip->nfttablename, nftset_ip->nftsetname, addr[0], addr[1],
addr[2], addr[3]);
nftset_add(nftset_ip->familyname, nftset_ip->nfttablename, nftset_ip->nftsetname, addr,
DNS_RR_A_LEN, request->ip_ttl * 2);
}
} break; } break;
case DNS_T_AAAA: { case DNS_T_AAAA: {
unsigned char addr[16]; unsigned char addr[16];
@@ -1372,16 +1493,27 @@ static int _dns_server_setup_ipset_packet(struct dns_server_post_context *contex
dns_get_AAAA(rrs, name, DNS_MAX_CNAME_LEN, &ttl, addr); dns_get_AAAA(rrs, name, DNS_MAX_CNAME_LEN, &ttl, addr);
rule = ipset_rule_v6 ? ipset_rule_v6 : ipset_rule; rule = ipset_rule_v6 ? ipset_rule_v6 : ipset_rule;
if (rule == NULL) { if (rule != NULL) {
break; tlog(TLOG_DEBUG,
"IPSET-MATCH: domain: %s, ipset: %s, IP: "
"%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x",
request->domain, rule->ipsetname, addr[0], addr[1], addr[2], addr[3], addr[4], addr[5],
addr[6], addr[7], addr[8], addr[9], addr[10], addr[11], addr[12], addr[13], addr[14],
addr[15]);
ipset_add(rule->ipsetname, addr, DNS_RR_AAAA_LEN, request->ip_ttl * 2);
} }
ipset_add(rule->ipsetname, addr, DNS_RR_AAAA_LEN, request->ip_ttl * 2); if (nftset_ip6 != NULL) {
tlog(TLOG_DEBUG, /* add IPV6 to ipset */
"IPSET-MATCH: domain: %s, ipset: %s, IP: " tlog(TLOG_DEBUG,
"%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x", "NFTSET-MATCH: domain: %s, nftset: %s %s %s, IP: "
request->domain, rule->ipsetname, addr[0], addr[1], addr[2], addr[3], addr[4], addr[5], addr[6], "%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x",
addr[7], addr[8], addr[9], addr[10], addr[11], addr[12], addr[13], addr[14], addr[15]); request->domain, nftset_ip6->familyname, nftset_ip6->nfttablename, nftset_ip6->nftsetname,
addr[0], addr[1], addr[2], addr[3], addr[4], addr[5], addr[6], addr[7], addr[8], addr[9],
addr[10], addr[11], addr[12], addr[13], addr[14], addr[15]);
nftset_add(nftset_ip6->familyname, nftset_ip6->nfttablename, nftset_ip6->nftsetname, addr,
DNS_RR_AAAA_LEN, request->ip_ttl * 2);
}
} break; } break;
default: default:
break; break;
@@ -1420,7 +1552,7 @@ static int _dns_request_post(struct dns_server_post_context *context)
} }
/* setup ipset */ /* setup ipset */
_dns_server_setup_ipset_packet(context); _dns_server_setup_ipset_nftset_packet(context);
if (context->do_reply == 0) { if (context->do_reply == 0) {
return 0; return 0;
@@ -1913,7 +2045,9 @@ static int _dns_server_set_to_pending_list(struct dns_request *request)
} }
key = hash_string(request->domain); key = hash_string(request->domain);
key = hash_string_initval(request->dns_group_name, key);
key = jhash(&(request->qtype), sizeof(request->qtype), key); key = jhash(&(request->qtype), sizeof(request->qtype), key);
key = jhash(&(request->server_flags), sizeof(request->server_flags), key);
pthread_mutex_lock(&server.request_pending_lock); pthread_mutex_lock(&server.request_pending_lock);
hash_for_each_possible(server.request_pending, pending_list_tmp, node, key) hash_for_each_possible(server.request_pending, pending_list_tmp, node, key)
{ {
@@ -1921,6 +2055,14 @@ static int _dns_server_set_to_pending_list(struct dns_request *request)
continue; continue;
} }
if (request->server_flags != pending_list_tmp->server_flags) {
continue;
}
if (strcmp(request->dns_group_name, pending_list_tmp->dns_group_name) != 0) {
continue;
}
if (strncmp(request->domain, pending_list_tmp->domain, DNS_MAX_CNAME_LEN) != 0) { if (strncmp(request->domain, pending_list_tmp->domain, DNS_MAX_CNAME_LEN) != 0) {
continue; continue;
} }
@@ -1941,7 +2083,9 @@ static int _dns_server_set_to_pending_list(struct dns_request *request)
INIT_LIST_HEAD(&pending_list->request_list); INIT_LIST_HEAD(&pending_list->request_list);
INIT_HLIST_NODE(&pending_list->node); INIT_HLIST_NODE(&pending_list->node);
pending_list->qtype = request->qtype; pending_list->qtype = request->qtype;
pending_list->server_flags = request->server_flags;
safe_strncpy(pending_list->domain, request->domain, DNS_MAX_CNAME_LEN); safe_strncpy(pending_list->domain, request->domain, DNS_MAX_CNAME_LEN);
safe_strncpy(pending_list->dns_group_name, request->dns_group_name, DNS_GROUP_NAME_LEN);
hash_add(server.request_pending, &pending_list->node, key); hash_add(server.request_pending, &pending_list->node, key);
request->request_pending_list = pending_list; request->request_pending_list = pending_list;
} else { } else {
@@ -1981,6 +2125,7 @@ static struct dns_request *_dns_server_new_request(void)
request->dualstack_selection_ping_time = -1; request->dualstack_selection_ping_time = -1;
request->rcode = DNS_RC_SERVFAIL; request->rcode = DNS_RC_SERVFAIL;
request->conn = NULL; request->conn = NULL;
request->qclass = DNS_C_IN;
request->result_callback = NULL; request->result_callback = NULL;
request->check_order_list = &dns_conf_check_orders; request->check_order_list = &dns_conf_check_orders;
INIT_LIST_HEAD(&request->list); INIT_LIST_HEAD(&request->list);
@@ -2459,6 +2604,10 @@ static int _dns_server_process_answer(struct dns_request *request, const char *d
} }
request->remote_server_fail = 0; request->remote_server_fail = 0;
if (request->rcode == DNS_RC_SERVFAIL) {
request->rcode = packet->head.rcode;
}
for (j = 1; j < DNS_RRS_END; j++) { for (j = 1; j < DNS_RRS_END; j++) {
rrs = dns_get_rrs_start(packet, j, &rr_count); rrs = dns_get_rrs_start(packet, j, &rr_count);
for (i = 0; i < rr_count && rrs; i++, rrs = dns_get_rrs_next(packet, rrs)) { for (i = 0; i < rr_count && rrs; i++, rrs = dns_get_rrs_next(packet, rrs)) {
@@ -2495,6 +2644,7 @@ static int _dns_server_process_answer(struct dns_request *request, const char *d
continue; continue;
} }
safe_strncpy(cname, domain_cname, DNS_MAX_CNAME_LEN); safe_strncpy(cname, domain_cname, DNS_MAX_CNAME_LEN);
request->ttl_cname = _dns_server_get_conf_ttl(ttl);
tlog(TLOG_DEBUG, "name: %s ttl: %d cname: %s\n", name, ttl, cname); tlog(TLOG_DEBUG, "name: %s ttl: %d cname: %s\n", name, ttl, cname);
} break; } break;
case DNS_T_SOA: { case DNS_T_SOA: {
@@ -2547,6 +2697,10 @@ static int _dns_server_passthrough_rule_check(struct dns_request *request, const
} }
request->remote_server_fail = 0; request->remote_server_fail = 0;
if (request->rcode == DNS_RC_SERVFAIL) {
request->rcode = packet->head.rcode;
}
for (j = 1; j < DNS_RRS_END; j++) { for (j = 1; j < DNS_RRS_END; j++) {
rrs = dns_get_rrs_start(packet, j, &rr_count); rrs = dns_get_rrs_start(packet, j, &rr_count);
for (i = 0; i < rr_count && rrs; i++, rrs = dns_get_rrs_next(packet, rrs)) { for (i = 0; i < rr_count && rrs; i++, rrs = dns_get_rrs_next(packet, rrs)) {
@@ -2769,7 +2923,7 @@ static int _dns_server_reply_passthrouth(struct dns_server_post_context *context
_dns_cache_reply_packet(context); _dns_cache_reply_packet(context);
if (_dns_server_setup_ipset_packet(context) != 0) { if (_dns_server_setup_ipset_nftset_packet(context) != 0) {
tlog(TLOG_DEBUG, "setup ipset failed."); tlog(TLOG_DEBUG, "setup ipset failed.");
} }
@@ -2815,6 +2969,7 @@ static void _dns_server_query_end(struct dns_request *request)
request->has_ping_result = 1; request->has_ping_result = 1;
_dns_server_request_complete(request); _dns_server_request_complete(request);
} }
out: out:
_dns_server_request_release(request); _dns_server_request_release(request);
} }
@@ -3161,33 +3316,40 @@ static int _dns_server_process_local_ptr(struct dns_request *request)
goto errout; goto errout;
} }
char hostname[DNS_MAX_CNAME_LEN]; char full_hostname[DNS_MAX_CNAME_LEN];
if (dns_conf_server_name[0] == 0) { if (dns_conf_server_name[0] == 0) {
/* get local host name */ char hostname[DNS_MAX_CNAME_LEN];
if (getdomainname(hostname, DNS_MAX_CNAME_LEN) != 0) { char domainname[DNS_MAX_CNAME_LEN];
if (gethostname(hostname, DNS_MAX_CNAME_LEN) != 0) {
return -1; /* get local domain name */
if (getdomainname(domainname, DNS_MAX_CNAME_LEN - 1) == 0) {
/* check domain is valid */
if (strncmp(domainname, "(none)", DNS_MAX_CNAME_LEN - 1) == 0) {
domainname[0] = '\0';
} }
} }
/* get host name again */ if (gethostname(hostname, DNS_MAX_CNAME_LEN - 1) == 0) {
if (strncmp(hostname, "(none)", DNS_MAX_CNAME_LEN - 1) == 0) { /* check hostname is valid */
if (gethostname(hostname, DNS_MAX_CNAME_LEN) != 0) { if (strncmp(hostname, "(none)", DNS_MAX_CNAME_LEN - 1) == 0) {
return -1; hostname[0] = '\0';
} }
} }
/* if hostname is (none), return smartdns */ if (hostname[0] != '\0' && domainname[0] != '\0') {
if (strncmp(hostname, "(none)", DNS_MAX_CNAME_LEN - 1) == 0) { snprintf(full_hostname, sizeof(full_hostname), "%.64s.%.128s", hostname, domainname);
safe_strncpy(hostname, "smartdns", DNS_MAX_CNAME_LEN); } else if (hostname[0] != '\0') {
safe_strncpy(full_hostname, hostname, DNS_MAX_CNAME_LEN);
} else {
safe_strncpy(full_hostname, "smartdns", DNS_MAX_CNAME_LEN);
} }
} else { } else {
/* return configured server name */ /* return configured server name */
safe_strncpy(hostname, dns_conf_server_name, DNS_MAX_CNAME_LEN); safe_strncpy(full_hostname, dns_conf_server_name, DNS_MAX_CNAME_LEN);
} }
request->has_ptr = 1; request->has_ptr = 1;
safe_strncpy(request->ptr_hostname, hostname, DNS_MAX_CNAME_LEN); safe_strncpy(request->ptr_hostname, full_hostname, DNS_MAX_CNAME_LEN);
freeifaddrs(ifaddr); freeifaddrs(ifaddr);
return 0; return 0;
@@ -3268,12 +3430,20 @@ static void _dns_server_update_rule_by_flags(struct dns_request *request)
request->domain_rule.rules[DOMAIN_RULE_IPSET_IPV6] = NULL; request->domain_rule.rules[DOMAIN_RULE_IPSET_IPV6] = NULL;
} }
if (flags & DOMAIN_FLAG_NFTSET_IP_IGN || flags & DOMAIN_FLAG_NFTSET_INET_IGN) {
request->domain_rule.rules[DOMAIN_RULE_NFTSET_IP] = NULL;
}
if (flags & DOMAIN_FLAG_NFTSET_IP6_IGN || flags & DOMAIN_FLAG_NFTSET_INET_IGN) {
request->domain_rule.rules[DOMAIN_RULE_NFTSET_IP6] = NULL;
}
if (flags & DOMAIN_FLAG_NAMESERVER_IGNORE) { if (flags & DOMAIN_FLAG_NAMESERVER_IGNORE) {
request->domain_rule.rules[DOMAIN_RULE_NAMESERVER] = NULL; request->domain_rule.rules[DOMAIN_RULE_NAMESERVER] = NULL;
} }
} }
static int _dns_server_get_rules(unsigned char *key, uint32_t key_len, void *value, void *arg) static int _dns_server_get_rules(unsigned char *key, uint32_t key_len, int is_subkey, void *value, void *arg)
{ {
struct rule_walk_args *walk_args = arg; struct rule_walk_args *walk_args = arg;
struct dns_request *request = walk_args->args; struct dns_request *request = walk_args->args;
@@ -3289,6 +3459,7 @@ static int _dns_server_get_rules(unsigned char *key, uint32_t key_len, void *val
} }
request->domain_rule.rules[i] = domain_rule->rules[i]; request->domain_rule.rules[i] = domain_rule->rules[i];
request->domain_rule.is_sub_rule[i] = is_subkey;
walk_args->key[i] = key; walk_args->key[i] = key;
walk_args->key_len[i] = key_len; walk_args->key_len[i] = key_len;
} }
@@ -3459,6 +3630,10 @@ static int _dns_server_qtype_soa(struct dns_request *request)
{ {
struct dns_qtype_soa_list *soa_list = NULL; struct dns_qtype_soa_list *soa_list = NULL;
if (request->skip_qtype_soa) {
return -1;
}
uint32_t key = hash_32_generic(request->qtype, 32); uint32_t key = hash_32_generic(request->qtype, 32);
hash_for_each_possible(dns_qtype_soa_table.qtype, soa_list, node, key) hash_for_each_possible(dns_qtype_soa_table.qtype, soa_list, node, key)
{ {
@@ -3628,7 +3803,13 @@ static int _dns_server_process_cache(struct dns_request *request)
goto out; goto out;
} }
dns_cache = dns_cache_lookup(request->domain, request->qtype); struct dns_cache_key cache_key;
cache_key.dns_group_name = request->dns_group_name;
cache_key.domain = request->domain;
cache_key.qtype = request->qtype;
cache_key.query_flag = request->server_flags;
dns_cache = dns_cache_lookup(&cache_key);
if (dns_cache == NULL) { if (dns_cache == NULL) {
goto out; goto out;
} }
@@ -3651,7 +3832,8 @@ static int _dns_server_process_cache(struct dns_request *request)
goto out; goto out;
} }
dualstack_dns_cache = dns_cache_lookup(request->domain, dualstack_qtype); cache_key.qtype = dualstack_qtype;
dualstack_dns_cache = dns_cache_lookup(&cache_key);
if (dualstack_dns_cache && dns_cache_is_soa(dualstack_dns_cache) == 0 && if (dualstack_dns_cache && dns_cache_is_soa(dualstack_dns_cache) == 0 &&
(dualstack_dns_cache->info.speed > 0)) { (dualstack_dns_cache->info.speed > 0)) {
@@ -3691,8 +3873,8 @@ out_update_cache:
dns_query_options.server_flags = request->server_flags; dns_query_options.server_flags = request->server_flags;
dns_query_options.dns_group_name = request->dns_group_name; dns_query_options.dns_group_name = request->dns_group_name;
if (request->conn == NULL) { if (request->conn == NULL) {
dns_query_options.server_flags = dns_cache_get_query_flag(dns_cache->cache_data); dns_query_options.server_flags = dns_cache_get_query_flag(dns_cache);
dns_query_options.dns_group_name = dns_cache_get_dns_group_name(dns_cache->cache_data); dns_query_options.dns_group_name = dns_cache_get_dns_group_name(dns_cache);
} }
dns_query_options.ecs_enable_flag = 0; dns_query_options.ecs_enable_flag = 0;
@@ -3828,6 +4010,10 @@ static int _dns_server_process_smartdns_domain(struct dns_request *request)
return -1; return -1;
} }
if (_dns_server_is_dns_rule_extact_match(request, DOMAIN_RULE_FLAGS) == 0) {
return -1;
}
flags = rule_flag->flags; flags = rule_flag->flags;
if (!(flags & DOMAIN_FLAG_SMARTDNS_DOMAIN)) { if (!(flags & DOMAIN_FLAG_SMARTDNS_DOMAIN)) {
return -1; return -1;
@@ -4018,7 +4204,7 @@ static int _dns_server_query_dualstack(struct dns_request *request)
request_dualstack->dualstack_request = request; request_dualstack->dualstack_request = request;
_dns_server_request_set_callback(request_dualstack, dns_server_dualstack_callback, request); _dns_server_request_set_callback(request_dualstack, dns_server_dualstack_callback, request);
request->request_wait++; request->request_wait++;
ret = _dns_server_do_query(request_dualstack); ret = _dns_server_do_query(request_dualstack, 0);
if (ret != 0) { if (ret != 0) {
request->request_wait--; request->request_wait--;
tlog(TLOG_ERROR, "do query %s type %d failed.\n", request->domain, qtype); tlog(TLOG_ERROR, "do query %s type %d failed.\n", request->domain, qtype);
@@ -4038,7 +4224,7 @@ errout:
return ret; return ret;
} }
static int _dns_server_do_query(struct dns_request *request) static int _dns_server_do_query(struct dns_request *request, int skip_notify_event)
{ {
int ret = -1; int ret = -1;
const char *group_name = NULL; const char *group_name = NULL;
@@ -4114,6 +4300,9 @@ static int _dns_server_do_query(struct dns_request *request)
_dns_server_setup_query_option(request, &options); _dns_server_setup_query_option(request, &options);
pthread_mutex_lock(&server.request_list_lock); pthread_mutex_lock(&server.request_list_lock);
if (list_empty(&server.request_list) && skip_notify_event == 1) {
_dns_server_wakup_thread();
}
list_add_tail(&request->list, &server.request_list); list_add_tail(&request->list, &server.request_list);
pthread_mutex_unlock(&server.request_list_lock); pthread_mutex_unlock(&server.request_list_lock);
@@ -4138,6 +4327,20 @@ errout:
return ret; return ret;
} }
static int _dns_server_check_request_supported(struct dns_request *request, struct dns_packet *packet)
{
if (request->qclass != DNS_C_IN) {
return -1;
}
if (packet->head.opcode != DNS_OP_QUERY) {
return -1;
}
return 0;
}
static int _dns_server_parser_request(struct dns_request *request, struct dns_packet *packet) static int _dns_server_parser_request(struct dns_request *request, struct dns_packet *packet)
{ {
struct dns_rrs *rrs = NULL; struct dns_rrs *rrs = NULL;
@@ -4170,6 +4373,11 @@ static int _dns_server_parser_request(struct dns_request *request, struct dns_pa
break; break;
} }
request->qclass = qclass;
if (_dns_server_check_request_supported(request, packet) != 0) {
goto errout;
}
/* get request opts */ /* get request opts */
rr_count = 0; rr_count = 0;
rrs = dns_get_rrs_start(packet, DNS_RRS_OPT, &rr_count); rrs = dns_get_rrs_start(packet, DNS_RRS_OPT, &rr_count);
@@ -4188,6 +4396,7 @@ static int _dns_server_parser_request(struct dns_request *request, struct dns_pa
return 0; return 0;
errout: errout:
request->rcode = DNS_RC_NOTIMP;
return -1; return -1;
} }
@@ -4227,6 +4436,11 @@ static int _dns_server_recv(struct dns_server_conn_head *conn, unsigned char *in
goto errout; goto errout;
} }
memcpy(&request->localaddr, local, local_len);
_dns_server_request_set_client(request, conn);
_dns_server_request_set_client_addr(request, from, from_len);
_dns_server_request_set_id(request, packet->head.id);
if (_dns_server_parser_request(request, packet) != 0) { if (_dns_server_parser_request(request, packet) != 0) {
tlog(TLOG_DEBUG, "parser request failed."); tlog(TLOG_DEBUG, "parser request failed.");
ret = RECV_ERROR_INVALID_PACKET; ret = RECV_ERROR_INVALID_PACKET;
@@ -4235,11 +4449,7 @@ static int _dns_server_recv(struct dns_server_conn_head *conn, unsigned char *in
tlog(TLOG_INFO, "query server %s from %s, qtype = %d\n", request->domain, name, request->qtype); tlog(TLOG_INFO, "query server %s from %s, qtype = %d\n", request->domain, name, request->qtype);
memcpy(&request->localaddr, local, local_len); ret = _dns_server_do_query(request, 1);
_dns_server_request_set_client(request, conn);
_dns_server_request_set_client_addr(request, from, from_len);
_dns_server_request_set_id(request, packet->head.id);
ret = _dns_server_do_query(request);
if (ret != 0) { if (ret != 0) {
tlog(TLOG_ERROR, "do query %s failed.\n", request->domain); tlog(TLOG_ERROR, "do query %s failed.\n", request->domain);
goto errout; goto errout;
@@ -4291,7 +4501,7 @@ static int _dns_server_prefetch_request(char *domain, dns_type_t qtype, int expi
request->qtype = qtype; request->qtype = qtype;
_dns_server_setup_server_query_options(request, server_query_option); _dns_server_setup_server_query_options(request, server_query_option);
_dns_server_request_set_enable_prefetch(request, expired_domain); _dns_server_request_set_enable_prefetch(request, expired_domain);
ret = _dns_server_do_query(request); ret = _dns_server_do_query(request, 0);
if (ret != 0) { if (ret != 0) {
tlog(TLOG_ERROR, "do query %s failed.\n", request->domain); tlog(TLOG_ERROR, "do query %s failed.\n", request->domain);
goto errout; goto errout;
@@ -4323,7 +4533,7 @@ int dns_server_query(const char *domain, int qtype, struct dns_server_query_opti
request->qtype = qtype; request->qtype = qtype;
_dns_server_setup_server_query_options(request, server_query_option); _dns_server_setup_server_query_options(request, server_query_option);
_dns_server_request_set_callback(request, callback, user_ptr); _dns_server_request_set_callback(request, callback, user_ptr);
ret = _dns_server_do_query(request); ret = _dns_server_do_query(request, 0);
if (ret != 0) { if (ret != 0) {
tlog(TLOG_ERROR, "do query %s failed.\n", domain); tlog(TLOG_ERROR, "do query %s failed.\n", domain);
goto errout; goto errout;
@@ -4726,8 +4936,8 @@ static void _dns_server_prefetch_domain(struct dns_cache *dns_cache)
/* start prefetch domain */ /* start prefetch domain */
tlog(TLOG_DEBUG, "prefetch by cache %s, qtype %d, ttl %d, hitnum %d", dns_cache->info.domain, dns_cache->info.qtype, tlog(TLOG_DEBUG, "prefetch by cache %s, qtype %d, ttl %d, hitnum %d", dns_cache->info.domain, dns_cache->info.qtype,
dns_cache->info.ttl, hitnum); dns_cache->info.ttl, hitnum);
server_query_option.dns_group_name = dns_cache_get_dns_group_name(dns_cache->cache_data); server_query_option.dns_group_name = dns_cache_get_dns_group_name(dns_cache);
server_query_option.server_flags = dns_cache_get_query_flag(dns_cache->cache_data); server_query_option.server_flags = dns_cache_get_query_flag(dns_cache);
server_query_option.ecs_enable_flag = 0; server_query_option.ecs_enable_flag = 0;
if (_dns_server_prefetch_request(dns_cache->info.domain, dns_cache->info.qtype, 0, &server_query_option) != 0) { if (_dns_server_prefetch_request(dns_cache->info.domain, dns_cache->info.qtype, 0, &server_query_option) != 0) {
tlog(TLOG_ERROR, "prefetch domain %s, qtype %d, failed.", dns_cache->info.domain, dns_cache->info.qtype); tlog(TLOG_ERROR, "prefetch domain %s, qtype %d, failed.", dns_cache->info.domain, dns_cache->info.qtype);
@@ -4741,8 +4951,8 @@ static void _dns_server_prefetch_expired_domain(struct dns_cache *dns_cache)
dns_cache->info.qtype, dns_cache->info.ttl); dns_cache->info.qtype, dns_cache->info.ttl);
struct dns_server_query_option server_query_option; struct dns_server_query_option server_query_option;
server_query_option.dns_group_name = dns_cache_get_dns_group_name(dns_cache->cache_data); server_query_option.dns_group_name = dns_cache_get_dns_group_name(dns_cache);
server_query_option.server_flags = dns_cache_get_query_flag(dns_cache->cache_data); server_query_option.server_flags = dns_cache_get_query_flag(dns_cache);
server_query_option.ecs_enable_flag = 0; server_query_option.ecs_enable_flag = 0;
if (_dns_server_prefetch_request(dns_cache->info.domain, dns_cache->info.qtype, 1, &server_query_option) != 0) { if (_dns_server_prefetch_request(dns_cache->info.domain, dns_cache->info.qtype, 1, &server_query_option) != 0) {
@@ -4829,14 +5039,12 @@ static void _dns_server_period_run_second(void)
} }
} }
static void _dns_server_period_run(void) static void _dns_server_period_run(unsigned int msec)
{ {
struct dns_request *request = NULL; struct dns_request *request = NULL;
struct dns_request *tmp = NULL; struct dns_request *tmp = NULL;
static unsigned int msec = 0;
LIST_HEAD(check_list); LIST_HEAD(check_list);
msec++;
if (msec % 10 == 0) { if (msec % 10 == 0) {
_dns_server_period_run_second(); _dns_server_period_run_second();
} }
@@ -4904,22 +5112,50 @@ int dns_server_run(void)
int num = 0; int num = 0;
int i = 0; int i = 0;
unsigned long now = {0}; unsigned long now = {0};
unsigned long last = {0};
unsigned int msec = 0;
int sleep = 100; int sleep = 100;
int sleep_time = 0; int sleep_time = 0;
unsigned long expect_time = 0; unsigned long expect_time = 0;
sleep_time = sleep; sleep_time = sleep;
now = get_tick_count() - sleep; now = get_tick_count() - sleep;
last = now;
expect_time = now + sleep; expect_time = now + sleep;
while (atomic_read(&server.run)) { while (atomic_read(&server.run)) {
now = get_tick_count(); now = get_tick_count();
if (sleep_time > 0) {
sleep_time -= now - last;
if (sleep_time <= 0) {
sleep_time = 0;
}
int cnt = sleep_time / sleep;
msec -= cnt;
expect_time -= cnt * sleep;
sleep_time -= cnt * sleep;
}
last = now;
if (now >= expect_time) { if (now >= expect_time) {
_dns_server_period_run(); msec++;
_dns_server_period_run(msec);
sleep_time = sleep - (now - expect_time); sleep_time = sleep - (now - expect_time);
if (sleep_time < 0) { if (sleep_time < 0) {
sleep_time = 0; sleep_time = 0;
expect_time = now; expect_time = now;
} }
/* When server is idle, the sleep time is 1000ms, to reduce CPU usage */
pthread_mutex_lock(&server.request_list_lock);
if (list_empty(&server.request_list)) {
int cnt = 10 - (msec % 10) - 1;
sleep_time += sleep * cnt;
msec += cnt;
/* sleep to next second */
expect_time += sleep * cnt;
}
pthread_mutex_unlock(&server.request_list_lock);
expect_time += sleep; expect_time += sleep;
} }
@@ -4935,6 +5171,14 @@ int dns_server_run(void)
for (i = 0; i < num; i++) { for (i = 0; i < num; i++) {
struct epoll_event *event = &events[i]; struct epoll_event *event = &events[i];
/* read event */
if (event->data.fd == server.event_fd) {
uint64_t value;
int unused __attribute__((unused));
unused = read(server.event_fd, &value, sizeof(uint64_t));
continue;
}
struct dns_server_conn_head *conn_head = event->data.ptr; struct dns_server_conn_head *conn_head = event->data.ptr;
if (conn_head == NULL) { if (conn_head == NULL) {
tlog(TLOG_ERROR, "invalid fd\n"); tlog(TLOG_ERROR, "invalid fd\n");
@@ -5258,6 +5502,31 @@ static int _dns_server_cache_save(void)
return 0; return 0;
} }
static int _dns_server_init_wakeup_event(void)
{
int fdevent = -1;
fdevent = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
if (fdevent < 0) {
tlog(TLOG_ERROR, "create eventfd failed, %s\n", strerror(errno));
goto errout;
}
struct epoll_event event;
memset(&event, 0, sizeof(event));
event.events = EPOLLIN | EPOLLERR;
event.data.fd = fdevent;
if (epoll_ctl(server.epoll_fd, EPOLL_CTL_ADD, fdevent, &event) != 0) {
tlog(TLOG_ERROR, "set eventfd failed, %s\n", strerror(errno));
goto errout;
}
server.event_fd = fdevent;
return 0;
errout:
return -1;
}
int dns_server_init(void) int dns_server_init(void)
{ {
pthread_attr_t attr; pthread_attr_t attr;
@@ -5308,6 +5577,11 @@ int dns_server_init(void)
tlog(TLOG_INFO, "%s", tlog(TLOG_INFO, "%s",
(is_ipv6_ready) ? "IPV6 is ready, enable IPV6 features" : "IPV6 is not ready, disable IPV6 features"); (is_ipv6_ready) ? "IPV6 is ready, enable IPV6 features" : "IPV6 is not ready, disable IPV6 features");
if (_dns_server_init_wakeup_event() != 0) {
tlog(TLOG_ERROR, "init wakeup event failed.");
goto errout;
}
return 0; return 0;
errout: errout:
atomic_set(&server.run, 0); atomic_set(&server.run, 0);
@@ -5327,10 +5601,15 @@ errout:
void dns_server_stop(void) void dns_server_stop(void)
{ {
atomic_set(&server.run, 0); atomic_set(&server.run, 0);
_dns_server_wakup_thread();
} }
void dns_server_exit(void) void dns_server_exit(void)
{ {
if (server.event_fd > 0) {
close(server.event_fd);
server.event_fd = -1;
}
_dns_server_close_socket(); _dns_server_close_socket();
_dns_server_cache_save(); _dns_server_cache_save();
_dns_server_request_remove_all(); _dns_server_request_remove_all();

View File

@@ -19,6 +19,7 @@
#include "fast_ping.h" #include "fast_ping.h"
#include "atomic.h" #include "atomic.h"
#include "hashtable.h" #include "hashtable.h"
#include "list.h"
#include "tlog.h" #include "tlog.h"
#include "util.h" #include "util.h"
#include <arpa/inet.h> #include <arpa/inet.h>
@@ -36,6 +37,8 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <sys/epoll.h> #include <sys/epoll.h>
#include <sys/eventfd.h>
#include <sys/resource.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/types.h> #include <sys/types.h>
#include <unistd.h> #include <unistd.h>
@@ -62,6 +65,9 @@ struct ping_dns_head {
unsigned short ancount; unsigned short ancount;
unsigned short aucount; unsigned short aucount;
unsigned short adcount; unsigned short adcount;
char qd_name;
unsigned short q_qtype;
unsigned short q_qclass;
} __attribute__((packed)); } __attribute__((packed));
typedef enum FAST_PING_TYPE { typedef enum FAST_PING_TYPE {
@@ -123,6 +129,15 @@ struct ping_host_struct {
struct fast_ping_packet packet; struct fast_ping_packet packet;
}; };
struct fast_ping_notify_event {
struct list_head list;
struct ping_host_struct *ping_host;
FAST_PING_RESULT ping_result;
unsigned int seq;
int ttl;
struct timeval tvresult;
};
struct fast_ping_struct { struct fast_ping_struct {
atomic_t run; atomic_t run;
pthread_t tid; pthread_t tid;
@@ -140,6 +155,12 @@ struct fast_ping_struct {
int fd_udp6; int fd_udp6;
struct ping_host_struct udp6_host; struct ping_host_struct udp6_host;
int event_fd;
pthread_t notify_tid;
pthread_cond_t notify_cond;
pthread_mutex_t notify_lock;
struct list_head notify_event_list;
pthread_mutex_t map_lock; pthread_mutex_t map_lock;
DECLARE_HASHTABLE(addrmap, 6); DECLARE_HASHTABLE(addrmap, 6);
}; };
@@ -148,6 +169,15 @@ static struct fast_ping_struct ping;
static atomic_t ping_sid = ATOMIC_INIT(0); static atomic_t ping_sid = ATOMIC_INIT(0);
static int bool_print_log = 1; static int bool_print_log = 1;
static void _fast_ping_host_put(struct ping_host_struct *ping_host);
static void _fast_ping_wakup_thread(void)
{
uint64_t u = 1;
int unused __attribute__((unused));
unused = write(ping.event_fd, &u, sizeof(u));
}
static uint16_t _fast_ping_checksum(uint16_t *header, size_t len) static uint16_t _fast_ping_checksum(uint16_t *header, size_t len)
{ {
uint32_t sum = 0; uint32_t sum = 0;
@@ -365,6 +395,53 @@ static void _fast_ping_close_host_sock(struct ping_host_struct *ping_host)
ping_host->fd = -1; ping_host->fd = -1;
} }
static void _fast_ping_release_notify_event(struct fast_ping_notify_event *ping_notify_event)
{
pthread_mutex_lock(&ping.notify_lock);
list_del_init(&ping_notify_event->list);
pthread_mutex_unlock(&ping.notify_lock);
if (ping_notify_event->ping_host) {
_fast_ping_host_put(ping_notify_event->ping_host);
ping_notify_event->ping_host = NULL;
}
free(ping_notify_event);
}
static int _fast_ping_send_notify_event(struct ping_host_struct *ping_host, FAST_PING_RESULT ping_result,
unsigned int seq, int ttl, struct timeval *tvresult)
{
struct fast_ping_notify_event *notify_event = NULL;
notify_event = malloc(sizeof(struct fast_ping_notify_event));
if (notify_event == NULL) {
goto errout;
}
memset(notify_event, 0, sizeof(struct fast_ping_notify_event));
INIT_LIST_HEAD(&notify_event->list);
notify_event->seq = seq;
notify_event->ttl = ttl;
notify_event->ping_result = ping_result;
notify_event->tvresult = *tvresult;
pthread_mutex_lock(&ping.notify_lock);
if (list_empty(&ping.notify_event_list)) {
pthread_cond_signal(&ping.notify_cond);
}
list_add_tail(&notify_event->list, &ping.notify_event_list);
notify_event->ping_host = ping_host;
_fast_ping_host_get(ping_host);
pthread_mutex_unlock(&ping.notify_lock);
return 0;
errout:
if (notify_event) {
_fast_ping_release_notify_event(notify_event);
}
return -1;
}
static void _fast_ping_host_put(struct ping_host_struct *ping_host) static void _fast_ping_host_put(struct ping_host_struct *ping_host)
{ {
int ref_cnt = atomic_dec_and_test(&ping_host->ref); int ref_cnt = atomic_dec_and_test(&ping_host->ref);
@@ -387,8 +464,7 @@ static void _fast_ping_host_put(struct ping_host_struct *ping_host)
tv.tv_sec = 0; tv.tv_sec = 0;
tv.tv_usec = 0; tv.tv_usec = 0;
ping_host->ping_callback(ping_host, ping_host->host, PING_RESULT_END, &ping_host->addr, ping_host->addr_len, _fast_ping_send_notify_event(ping_host, PING_RESULT_END, ping_host->seq, ping_host->ttl, &tv);
ping_host->seq, ping_host->ttl, &tv, ping_host->error, ping_host->userptr);
} }
tlog(TLOG_DEBUG, "ping %s end, id %d", ping_host->host, ping_host->sid); tlog(TLOG_DEBUG, "ping %s end, id %d", ping_host->host, ping_host->sid);
@@ -414,8 +490,7 @@ static void _fast_ping_host_remove(struct ping_host_struct *ping_host)
tv.tv_sec = 0; tv.tv_sec = 0;
tv.tv_usec = 0; tv.tv_usec = 0;
ping_host->ping_callback(ping_host, ping_host->host, PING_RESULT_END, &ping_host->addr, ping_host->addr_len, _fast_ping_send_notify_event(ping_host, PING_RESULT_END, ping_host->seq, ping_host->ttl, &tv);
ping_host->seq, ping_host->ttl, &tv, ping_host->error, ping_host->userptr);
} }
_fast_ping_host_put(ping_host); _fast_ping_host_put(ping_host);
@@ -537,6 +612,11 @@ static int _fast_ping_sendping_udp(struct ping_host_struct *ping_host)
memset(&dns_head, 0, sizeof(dns_head)); memset(&dns_head, 0, sizeof(dns_head));
dns_head.id = htons(ping_host->sid); dns_head.id = htons(ping_host->sid);
dns_head.flag = flag; dns_head.flag = flag;
dns_head.qdcount = htons(1);
dns_head.qd_name = 0;
dns_head.q_qtype = htons(2); /* DNS_T_NS */
dns_head.q_qclass = htons(1);
gettimeofday(&ping_host->last, NULL); gettimeofday(&ping_host->last, NULL);
len = sendto(fd, &dns_head, sizeof(dns_head), 0, &ping_host->addr, ping_host->addr_len); 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 < 0 || len != sizeof(dns_head)) {
@@ -646,6 +726,8 @@ static int _fast_ping_sendping(struct ping_host_struct *ping_host)
if (ret != 0) { if (ret != 0) {
ping_host->error = errno; ping_host->error = errno;
return ret; return ret;
} else {
ping_host->error = 0;
} }
return 0; return 0;
@@ -1122,6 +1204,9 @@ struct ping_host_struct *fast_ping_start(PING_TYPE type, const char *host, int c
pthread_mutex_lock(&ping.map_lock); pthread_mutex_lock(&ping.map_lock);
_fast_ping_host_get(ping_host); _fast_ping_host_get(ping_host);
if (hash_empty(ping.addrmap)) {
_fast_ping_wakup_thread();
}
hash_add(ping.addrmap, &ping_host->addr_node, addrkey); hash_add(ping.addrmap, &ping_host->addr_node, addrkey);
ping_host->run = 1; ping_host->run = 1;
pthread_mutex_unlock(&ping.map_lock); pthread_mutex_unlock(&ping.map_lock);
@@ -1194,7 +1279,7 @@ static struct fast_ping_packet *_fast_ping_icmp6_packet(struct ping_host_struct
packet->ttl = hops; packet->ttl = hops;
if (icmp6->icmp6_type != ICMP6_ECHO_REPLY) { if (icmp6->icmp6_type != ICMP6_ECHO_REPLY) {
tlog(TLOG_DEBUG, "icmp6 type faild, %d:%d", icmp6->icmp6_type, ICMP6_ECHO_REPLY); errno = ENETUNREACH;
return NULL; return NULL;
} }
@@ -1235,7 +1320,7 @@ static struct fast_ping_packet *_fast_ping_icmp_packet(struct ping_host_struct *
} }
if (icmp->icmp_type != ICMP_ECHOREPLY) { if (icmp->icmp_type != ICMP_ECHOREPLY) {
tlog(TLOG_DEBUG, "icmp type faild, %d:%d", icmp->icmp_type, ICMP_ECHOREPLY); errno = ENETUNREACH;
return NULL; return NULL;
} }
@@ -1317,6 +1402,10 @@ static int _fast_ping_process_icmp(struct ping_host_struct *ping_host, struct ti
packet = _fast_ping_recv_packet(ping_host, &msg, inpacket, len, now); packet = _fast_ping_recv_packet(ping_host, &msg, inpacket, len, now);
if (packet == NULL) { if (packet == NULL) {
char name[PING_MAX_HOSTLEN]; char name[PING_MAX_HOSTLEN];
if (errno == ENETUNREACH) {
goto errout;
}
tlog(TLOG_DEBUG, "recv ping packet from %s failed.", tlog(TLOG_DEBUG, "recv ping packet from %s failed.",
gethost_by_addr(name, sizeof(name), (struct sockaddr *)&from)); gethost_by_addr(name, sizeof(name), (struct sockaddr *)&from));
goto errout; goto errout;
@@ -1353,9 +1442,8 @@ static int _fast_ping_process_icmp(struct ping_host_struct *ping_host, struct ti
recv_ping_host->ttl = packet->ttl; recv_ping_host->ttl = packet->ttl;
tv_sub(&tvresult, tvsend); tv_sub(&tvresult, tvsend);
if (recv_ping_host->ping_callback) { if (recv_ping_host->ping_callback) {
recv_ping_host->ping_callback(recv_ping_host, recv_ping_host->host, PING_RESULT_RESPONSE, &recv_ping_host->addr, _fast_ping_send_notify_event(recv_ping_host, PING_RESULT_RESPONSE, recv_ping_host->seq, recv_ping_host->ttl,
recv_ping_host->addr_len, recv_ping_host->seq, recv_ping_host->ttl, &tvresult, &tvresult);
ping_host->error, recv_ping_host->userptr);
} }
recv_ping_host->send = 0; recv_ping_host->send = 0;
@@ -1388,9 +1476,7 @@ static int _fast_ping_process_tcp(struct ping_host_struct *ping_host, struct epo
} }
tv_sub(&tvresult, tvsend); tv_sub(&tvresult, tvsend);
if (ping_host->ping_callback) { if (ping_host->ping_callback) {
ping_host->ping_callback(ping_host, ping_host->host, PING_RESULT_RESPONSE, &ping_host->addr, _fast_ping_send_notify_event(ping_host, PING_RESULT_RESPONSE, ping_host->seq, ping_host->ttl, &tvresult);
ping_host->addr_len, ping_host->seq, ping_host->ttl, &tvresult, ping_host->error,
ping_host->userptr);
} }
ping_host->send = 0; ping_host->send = 0;
@@ -1484,9 +1570,8 @@ static int _fast_ping_process_udp(struct ping_host_struct *ping_host, struct tim
tvsend = &recv_ping_host->last; tvsend = &recv_ping_host->last;
tv_sub(&tvresult, tvsend); tv_sub(&tvresult, tvsend);
if (recv_ping_host->ping_callback) { if (recv_ping_host->ping_callback) {
recv_ping_host->ping_callback(recv_ping_host, recv_ping_host->host, PING_RESULT_RESPONSE, &recv_ping_host->addr, _fast_ping_send_notify_event(recv_ping_host, PING_RESULT_RESPONSE, recv_ping_host->seq, recv_ping_host->ttl,
recv_ping_host->addr_len, recv_ping_host->seq, recv_ping_host->ttl, &tvresult, &tvresult);
ping_host->error, recv_ping_host->userptr);
} }
recv_ping_host->send = 0; recv_ping_host->send = 0;
@@ -1593,9 +1678,7 @@ static void _fast_ping_period_run(void)
tv_sub(&interval, &ping_host->last); tv_sub(&interval, &ping_host->last);
millisecond = interval.tv_sec * 1000 + interval.tv_usec / 1000; millisecond = interval.tv_sec * 1000 + interval.tv_usec / 1000;
if (millisecond >= ping_host->timeout && ping_host->send == 1) { if (millisecond >= ping_host->timeout && ping_host->send == 1) {
ping_host->ping_callback(ping_host, ping_host->host, PING_RESULT_TIMEOUT, &ping_host->addr, _fast_ping_send_notify_event(ping_host, PING_RESULT_TIMEOUT, ping_host->seq, ping_host->ttl, &interval);
ping_host->addr_len, ping_host->seq, ping_host->ttl, &interval, ping_host->error,
ping_host->userptr);
ping_host->send = 0; ping_host->send = 0;
} }
@@ -1621,22 +1704,84 @@ static void _fast_ping_period_run(void)
} }
} }
static void _fast_ping_process_notify_event(struct fast_ping_notify_event *ping_notify_event)
{
struct ping_host_struct *ping_host = ping_notify_event->ping_host;
if (ping_host == NULL) {
return;
}
ping_host->ping_callback(ping_host, ping_host->host, ping_notify_event->ping_result, &ping_host->addr,
ping_host->addr_len, ping_notify_event->seq, ping_notify_event->ttl,
&ping_notify_event->tvresult, ping_host->error, ping_host->userptr);
}
static void *_fast_ping_notify_worker(void *arg)
{
struct fast_ping_notify_event *ping_notify_event = NULL;
while (atomic_read(&ping.run)) {
pthread_mutex_lock(&ping.notify_lock);
if (list_empty(&ping.notify_event_list)) {
pthread_cond_wait(&ping.notify_cond, &ping.notify_lock);
}
ping_notify_event = list_first_entry_or_null(&ping.notify_event_list, struct fast_ping_notify_event, list);
if (ping_notify_event) {
list_del_init(&ping_notify_event->list);
}
pthread_mutex_unlock(&ping.notify_lock);
if (ping_notify_event == NULL) {
continue;
}
_fast_ping_process_notify_event(ping_notify_event);
_fast_ping_release_notify_event(ping_notify_event);
}
return NULL;
}
static void _fast_ping_remove_all_notify_event(void)
{
struct fast_ping_notify_event *notify_event = NULL;
struct fast_ping_notify_event *tmp = NULL;
list_for_each_entry_safe(notify_event, tmp, &ping.notify_event_list, list)
{
_fast_ping_process_notify_event(notify_event);
_fast_ping_release_notify_event(notify_event);
}
}
static void *_fast_ping_work(void *arg) static void *_fast_ping_work(void *arg)
{ {
struct epoll_event events[PING_MAX_EVENTS + 1]; struct epoll_event events[PING_MAX_EVENTS + 1];
int num = 0; int num = 0;
int i = 0; int i = 0;
unsigned long now = {0}; unsigned long now = {0};
unsigned long last = {0};
struct timeval tvnow = {0}; struct timeval tvnow = {0};
int sleep = 100; int sleep = 100;
int sleep_time = 0; int sleep_time = 0;
unsigned long expect_time = 0; unsigned long expect_time = 0;
setpriority(PRIO_PROCESS, 0, -5);
sleep_time = sleep; sleep_time = sleep;
now = get_tick_count() - sleep; now = get_tick_count() - sleep;
last = now;
expect_time = now + sleep; expect_time = now + sleep;
while (atomic_read(&ping.run)) { while (atomic_read(&ping.run)) {
now = get_tick_count(); now = get_tick_count();
if (sleep_time > 0) {
sleep_time -= now - last;
if (sleep_time <= 0) {
sleep_time = 0;
}
}
last = now;
if (now >= expect_time) { if (now >= expect_time) {
_fast_ping_period_run(); _fast_ping_period_run();
sleep_time = sleep - (now - expect_time); sleep_time = sleep - (now - expect_time);
@@ -1647,12 +1792,22 @@ static void *_fast_ping_work(void *arg)
expect_time += sleep; expect_time += sleep;
} }
pthread_mutex_lock(&ping.map_lock);
if (hash_empty(ping.addrmap)) {
sleep_time = -1;
}
pthread_mutex_unlock(&ping.map_lock);
num = epoll_wait(ping.epoll_fd, events, PING_MAX_EVENTS, sleep_time); num = epoll_wait(ping.epoll_fd, events, PING_MAX_EVENTS, sleep_time);
if (num < 0) { if (num < 0) {
usleep(100000); usleep(100000);
continue; continue;
} }
if (sleep_time == -1) {
expect_time = get_tick_count();
}
if (num == 0) { if (num == 0) {
continue; continue;
} }
@@ -1660,6 +1815,14 @@ static void *_fast_ping_work(void *arg)
gettimeofday(&tvnow, NULL); gettimeofday(&tvnow, NULL);
for (i = 0; i < num; i++) { for (i = 0; i < num; i++) {
struct epoll_event *event = &events[i]; struct epoll_event *event = &events[i];
/* read event */
if (event->data.fd == ping.event_fd) {
uint64_t value;
int unused __attribute__((unused));
unused = read(ping.event_fd, &value, sizeof(uint64_t));
continue;
}
struct ping_host_struct *ping_host = (struct ping_host_struct *)event->data.ptr; struct ping_host_struct *ping_host = (struct ping_host_struct *)event->data.ptr;
_fast_ping_process(ping_host, event, &tvnow); _fast_ping_process(ping_host, event, &tvnow);
} }
@@ -1671,6 +1834,31 @@ static void *_fast_ping_work(void *arg)
return NULL; return NULL;
} }
static int _fast_ping_init_wakeup_event(void)
{
int fdevent = -1;
fdevent = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
if (fdevent < 0) {
tlog(TLOG_ERROR, "create eventfd failed, %s\n", strerror(errno));
goto errout;
}
struct epoll_event event;
memset(&event, 0, sizeof(event));
event.events = EPOLLIN | EPOLLERR;
event.data.fd = fdevent;
if (epoll_ctl(ping.epoll_fd, EPOLL_CTL_ADD, fdevent, &event) != 0) {
tlog(TLOG_ERROR, "set eventfd failed, %s\n", strerror(errno));
goto errout;
}
ping.event_fd = fdevent;
return 0;
errout:
return -1;
}
int fast_ping_init(void) int fast_ping_init(void)
{ {
pthread_attr_t attr; pthread_attr_t attr;
@@ -1693,32 +1881,67 @@ int fast_ping_init(void)
pthread_mutex_init(&ping.map_lock, NULL); pthread_mutex_init(&ping.map_lock, NULL);
pthread_mutex_init(&ping.lock, NULL); pthread_mutex_init(&ping.lock, NULL);
pthread_mutex_init(&ping.notify_lock, NULL);
pthread_cond_init(&ping.notify_cond, NULL);
INIT_LIST_HEAD(&ping.notify_event_list);
hash_init(ping.addrmap); hash_init(ping.addrmap);
ping.epoll_fd = epollfd;
ping.no_unprivileged_ping = !has_unprivileged_ping(); ping.no_unprivileged_ping = !has_unprivileged_ping();
ping.ident = (getpid() & 0XFFFF); ping.ident = (getpid() & 0XFFFF);
atomic_set(&ping.run, 1); atomic_set(&ping.run, 1);
ret = pthread_create(&ping.tid, &attr, _fast_ping_work, NULL); ret = pthread_create(&ping.tid, &attr, _fast_ping_work, NULL);
if (ret != 0) { if (ret != 0) {
tlog(TLOG_ERROR, "create ping work thread failed, %s\n", strerror(errno)); tlog(TLOG_ERROR, "create ping work thread failed, %s\n", strerror(ret));
goto errout;
}
ret = pthread_create(&ping.notify_tid, &attr, _fast_ping_notify_worker, NULL);
if (ret != 0) {
tlog(TLOG_ERROR, "create ping notifyer work thread failed, %s\n", strerror(ret));
goto errout;
}
ping.epoll_fd = epollfd;
ret = _fast_ping_init_wakeup_event();
if (ret != 0) {
tlog(TLOG_ERROR, "init wakeup event failed, %s\n", strerror(errno));
goto errout; goto errout;
} }
return 0; return 0;
errout: errout:
if (ping.notify_tid) {
void *retval = NULL;
atomic_set(&ping.run, 0);
pthread_cond_signal(&ping.notify_cond);
pthread_join(ping.notify_tid, &retval);
ping.notify_tid = 0;
}
if (ping.tid) { if (ping.tid) {
void *retval = NULL; void *retval = NULL;
atomic_set(&ping.run, 0); atomic_set(&ping.run, 0);
_fast_ping_wakup_thread();
pthread_join(ping.tid, &retval); pthread_join(ping.tid, &retval);
ping.tid = 0; ping.tid = 0;
} }
if (epollfd) { if (epollfd > 0) {
close(epollfd); close(epollfd);
} }
if (ping.event_fd) {
close(ping.event_fd);
ping.event_fd = -1;
}
pthread_cond_destroy(&ping.notify_cond);
pthread_mutex_destroy(&ping.notify_lock);
pthread_mutex_destroy(&ping.lock); pthread_mutex_destroy(&ping.lock);
pthread_mutex_destroy(&ping.map_lock); pthread_mutex_destroy(&ping.map_lock);
memset(&ping, 0, sizeof(ping));
return -1; return -1;
} }
@@ -1748,16 +1971,33 @@ static void _fast_ping_close_fds(void)
void fast_ping_exit(void) void fast_ping_exit(void)
{ {
if (ping.notify_tid) {
void *retval = NULL;
atomic_set(&ping.run, 0);
pthread_cond_signal(&ping.notify_cond);
pthread_join(ping.notify_tid, &retval);
ping.notify_tid = 0;
}
if (ping.tid) { if (ping.tid) {
void *ret = NULL; void *ret = NULL;
atomic_set(&ping.run, 0); atomic_set(&ping.run, 0);
_fast_ping_wakup_thread();
pthread_join(ping.tid, &ret); pthread_join(ping.tid, &ret);
ping.tid = 0; ping.tid = 0;
} }
if (ping.event_fd > 0) {
close(ping.event_fd);
ping.event_fd = -1;
}
_fast_ping_close_fds(); _fast_ping_close_fds();
_fast_ping_remove_all(); _fast_ping_remove_all();
_fast_ping_remove_all_notify_event();
pthread_cond_destroy(&ping.notify_cond);
pthread_mutex_destroy(&ping.notify_lock);
pthread_mutex_destroy(&ping.lock); pthread_mutex_destroy(&ping.lock);
pthread_mutex_destroy(&ping.map_lock); pthread_mutex_destroy(&ping.map_lock);
} }

View File

@@ -200,10 +200,11 @@ void *art_substring(const art_tree *t, const unsigned char *str, int str_len, un
* @arg t The tree * @arg t The tree
* @arg str The key * @arg str The key
* @arg str_len The length of the key * @arg str_len The length of the key
* @arg is_subkey is sub key or not
* @return NULL if the item was not found, otherwise * @return NULL if the item was not found, otherwise
* the value pointer is returned. * the value pointer is returned.
*/ */
typedef int (*walk_func)(unsigned char *key, uint32_t key_len, void *value, void *arg); typedef int (*walk_func)(unsigned char *key, uint32_t key_len, int is_subkey, void *value, void *arg);
void art_substring_walk(const art_tree *t, const unsigned char *str, int str_len, walk_func func, void *arg); void art_substring_walk(const art_tree *t, const unsigned char *str, int str_len, walk_func func, void *arg);
/** /**

View File

@@ -16,7 +16,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef _GENERIC_HASH_H #ifndef _GENERIC_HASH_H
#define _GENERIC_HASH_H #define _GENERIC_HASH_H
@@ -30,7 +29,7 @@
#endif #endif
#ifndef BITS_PER_LONG #ifndef BITS_PER_LONG
# define BITS_PER_LONG __WORDSIZE #define BITS_PER_LONG __WORDSIZE
#endif #endif
/* /*
@@ -39,14 +38,12 @@
* more efficiently than using fls() and fls64() * more efficiently than using fls() and fls64()
* - the arch is not required to handle n==0 if implementing the fallback * - the arch is not required to handle n==0 if implementing the fallback
*/ */
static inline __attribute__((const)) static inline __attribute__((const)) int __ilog2_u32(uint32_t n)
int __ilog2_u32(uint32_t n)
{ {
return fls(n) - 1; return fls(n) - 1;
} }
static inline __attribute__((const)) static inline __attribute__((const)) int __ilog2_u64(uint64_t n)
int __ilog2_u64(uint64_t n)
{ {
return fls64(n) - 1; return fls64(n) - 1;
} }
@@ -61,78 +58,73 @@ int __ilog2_u64(uint64_t n)
* *
* selects the appropriately-sized optimised version depending on sizeof(n) * selects the appropriately-sized optimised version depending on sizeof(n)
*/ */
#define ilog2(n) \ #define ilog2(n) \
( \ (__builtin_constant_p(n) ? ((n) < 2 ? 0 \
__builtin_constant_p(n) ? ( \ : (n) & (1ULL << 63) ? 63 \
(n) < 2 ? 0 : \ : (n) & (1ULL << 62) ? 62 \
(n) & (1ULL << 63) ? 63 : \ : (n) & (1ULL << 61) ? 61 \
(n) & (1ULL << 62) ? 62 : \ : (n) & (1ULL << 60) ? 60 \
(n) & (1ULL << 61) ? 61 : \ : (n) & (1ULL << 59) ? 59 \
(n) & (1ULL << 60) ? 60 : \ : (n) & (1ULL << 58) ? 58 \
(n) & (1ULL << 59) ? 59 : \ : (n) & (1ULL << 57) ? 57 \
(n) & (1ULL << 58) ? 58 : \ : (n) & (1ULL << 56) ? 56 \
(n) & (1ULL << 57) ? 57 : \ : (n) & (1ULL << 55) ? 55 \
(n) & (1ULL << 56) ? 56 : \ : (n) & (1ULL << 54) ? 54 \
(n) & (1ULL << 55) ? 55 : \ : (n) & (1ULL << 53) ? 53 \
(n) & (1ULL << 54) ? 54 : \ : (n) & (1ULL << 52) ? 52 \
(n) & (1ULL << 53) ? 53 : \ : (n) & (1ULL << 51) ? 51 \
(n) & (1ULL << 52) ? 52 : \ : (n) & (1ULL << 50) ? 50 \
(n) & (1ULL << 51) ? 51 : \ : (n) & (1ULL << 49) ? 49 \
(n) & (1ULL << 50) ? 50 : \ : (n) & (1ULL << 48) ? 48 \
(n) & (1ULL << 49) ? 49 : \ : (n) & (1ULL << 47) ? 47 \
(n) & (1ULL << 48) ? 48 : \ : (n) & (1ULL << 46) ? 46 \
(n) & (1ULL << 47) ? 47 : \ : (n) & (1ULL << 45) ? 45 \
(n) & (1ULL << 46) ? 46 : \ : (n) & (1ULL << 44) ? 44 \
(n) & (1ULL << 45) ? 45 : \ : (n) & (1ULL << 43) ? 43 \
(n) & (1ULL << 44) ? 44 : \ : (n) & (1ULL << 42) ? 42 \
(n) & (1ULL << 43) ? 43 : \ : (n) & (1ULL << 41) ? 41 \
(n) & (1ULL << 42) ? 42 : \ : (n) & (1ULL << 40) ? 40 \
(n) & (1ULL << 41) ? 41 : \ : (n) & (1ULL << 39) ? 39 \
(n) & (1ULL << 40) ? 40 : \ : (n) & (1ULL << 38) ? 38 \
(n) & (1ULL << 39) ? 39 : \ : (n) & (1ULL << 37) ? 37 \
(n) & (1ULL << 38) ? 38 : \ : (n) & (1ULL << 36) ? 36 \
(n) & (1ULL << 37) ? 37 : \ : (n) & (1ULL << 35) ? 35 \
(n) & (1ULL << 36) ? 36 : \ : (n) & (1ULL << 34) ? 34 \
(n) & (1ULL << 35) ? 35 : \ : (n) & (1ULL << 33) ? 33 \
(n) & (1ULL << 34) ? 34 : \ : (n) & (1ULL << 32) ? 32 \
(n) & (1ULL << 33) ? 33 : \ : (n) & (1ULL << 31) ? 31 \
(n) & (1ULL << 32) ? 32 : \ : (n) & (1ULL << 30) ? 30 \
(n) & (1ULL << 31) ? 31 : \ : (n) & (1ULL << 29) ? 29 \
(n) & (1ULL << 30) ? 30 : \ : (n) & (1ULL << 28) ? 28 \
(n) & (1ULL << 29) ? 29 : \ : (n) & (1ULL << 27) ? 27 \
(n) & (1ULL << 28) ? 28 : \ : (n) & (1ULL << 26) ? 26 \
(n) & (1ULL << 27) ? 27 : \ : (n) & (1ULL << 25) ? 25 \
(n) & (1ULL << 26) ? 26 : \ : (n) & (1ULL << 24) ? 24 \
(n) & (1ULL << 25) ? 25 : \ : (n) & (1ULL << 23) ? 23 \
(n) & (1ULL << 24) ? 24 : \ : (n) & (1ULL << 22) ? 22 \
(n) & (1ULL << 23) ? 23 : \ : (n) & (1ULL << 21) ? 21 \
(n) & (1ULL << 22) ? 22 : \ : (n) & (1ULL << 20) ? 20 \
(n) & (1ULL << 21) ? 21 : \ : (n) & (1ULL << 19) ? 19 \
(n) & (1ULL << 20) ? 20 : \ : (n) & (1ULL << 18) ? 18 \
(n) & (1ULL << 19) ? 19 : \ : (n) & (1ULL << 17) ? 17 \
(n) & (1ULL << 18) ? 18 : \ : (n) & (1ULL << 16) ? 16 \
(n) & (1ULL << 17) ? 17 : \ : (n) & (1ULL << 15) ? 15 \
(n) & (1ULL << 16) ? 16 : \ : (n) & (1ULL << 14) ? 14 \
(n) & (1ULL << 15) ? 15 : \ : (n) & (1ULL << 13) ? 13 \
(n) & (1ULL << 14) ? 14 : \ : (n) & (1ULL << 12) ? 12 \
(n) & (1ULL << 13) ? 13 : \ : (n) & (1ULL << 11) ? 11 \
(n) & (1ULL << 12) ? 12 : \ : (n) & (1ULL << 10) ? 10 \
(n) & (1ULL << 11) ? 11 : \ : (n) & (1ULL << 9) ? 9 \
(n) & (1ULL << 10) ? 10 : \ : (n) & (1ULL << 8) ? 8 \
(n) & (1ULL << 9) ? 9 : \ : (n) & (1ULL << 7) ? 7 \
(n) & (1ULL << 8) ? 8 : \ : (n) & (1ULL << 6) ? 6 \
(n) & (1ULL << 7) ? 7 : \ : (n) & (1ULL << 5) ? 5 \
(n) & (1ULL << 6) ? 6 : \ : (n) & (1ULL << 4) ? 4 \
(n) & (1ULL << 5) ? 5 : \ : (n) & (1ULL << 3) ? 3 \
(n) & (1ULL << 4) ? 4 : \ : (n) & (1ULL << 2) ? 2 \
(n) & (1ULL << 3) ? 3 : \ : 1) \
(n) & (1ULL << 2) ? 2 : \ : (sizeof(n) <= 4) ? __ilog2_u32(n) \
1 ) : \ : __ilog2_u64(n))
(sizeof(n) <= 4) ? \
__ilog2_u32(n) : \
__ilog2_u64(n) \
)
#if BITS_PER_LONG == 32 #if BITS_PER_LONG == 32
#define GOLDEN_RATIO_PRIME GOLDEN_RATIO_32 #define GOLDEN_RATIO_PRIME GOLDEN_RATIO_32
@@ -219,16 +211,36 @@ static inline uint32_t hash32_ptr(const void *ptr)
return (uint32_t)val; return (uint32_t)val;
} }
static inline uint32_t hash_string_initval(const char *s, uint32_t initval)
{
uint32_t h = initval;
while (*s) {
h = h * 31 + *s;
s++;
}
return h;
}
static inline uint32_t hash_string(const char *s) static inline uint32_t hash_string(const char *s)
{ {
uint32_t h = 0; return hash_string_initval(s, 0);
}
while (*s) { static inline uint32_t hash_string_array(const char **a)
h = h * 31 + *s; {
s++; uint32_t h = 0;
}
return h; const char *s;
while ((s = *a++)) {
while (*s) {
h = h * 31 + *s;
s++;
}
}
return h;
} }
#endif /* _GENERIC_HASH_H */ #endif /* _GENERIC_HASH_H */

36
src/include/nftset.h Normal file
View File

@@ -0,0 +1,36 @@
/*************************************************************************
*
* Copyright (C) 2018-2022 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 _NFTSET_H
#define _NFTSET_H
#ifdef __cpluscplus
extern "C" {
#endif
int nftset_add(const char *familyname, const char *tablename, const char *setname, const unsigned char addr[],
int addr_len, unsigned long timeout);
int nftset_del(const char *familyname, const char *tablename, const char *setname, const unsigned char addr[],
int addr_len);
#ifdef __cpluscplus
}
#endif
#endif // !_NFTSET_H

View File

@@ -1090,7 +1090,7 @@ void art_substring_walk(const art_tree *t, const unsigned char *str, int str_len
// Check if the expanded path matches // Check if the expanded path matches
if (!str_prefix_matches((art_leaf*)n, str, str_len)) { if (!str_prefix_matches((art_leaf*)n, str, str_len)) {
found = (art_leaf*)n; found = (art_leaf*)n;
stop_search = func(found->key, found->key_len, found->value, arg); stop_search = func(found->key, found->key_len, found->key_len != (uint32_t)str_len, found->value, arg);
} }
break; break;
} }
@@ -1103,7 +1103,7 @@ void art_substring_walk(const art_tree *t, const unsigned char *str, int str_len
// Check if the expanded path matches // Check if the expanded path matches
if (!str_prefix_matches((art_leaf*)m, str, str_len)) { if (!str_prefix_matches((art_leaf*)m, str, str_len)) {
found = (art_leaf*)m; found = (art_leaf*)m;
stop_search = func(found->key, found->key_len, found->value, arg); stop_search = func(found->key, found->key_len, found->key_len != (uint32_t)str_len, found->value, arg);
} }
} }

616
src/lib/nftset.c Normal file
View File

@@ -0,0 +1,616 @@
/*************************************************************************
*
* Copyright (C) 2018-2022 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 /* See feature_test_macros(7) */
#include "nftset.h"
#include "../dns_conf.h"
#include "../tlog.h"
#include <errno.h>
#include <linux/netfilter.h>
#include <linux/netfilter/nfnetlink.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <memory.h>
#include <poll.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#ifdef NFNL_SUBSYS_NFTABLES
#include <linux/netfilter/nf_tables.h>
struct nlmsgreq {
struct nlmsghdr h;
struct nfgenmsg m;
};
enum { PAYLOAD_MAX = 2048 };
static int nftset_fd;
static int _nftset_get_nffamily_from_str(const char *family)
{
if (strncmp(family, "inet", sizeof("inet")) == 0) {
return NFPROTO_INET;
} else if (strncmp(family, "ip", sizeof("ip")) == 0) {
return NFPROTO_IPV4;
} else if (strncmp(family, "ip6", sizeof("ip6")) == 0) {
return NFPROTO_IPV6;
} else if (strncmp(family, "arp", sizeof("arp")) == 0) {
return NFPROTO_ARP;
} else if (strncmp(family, "netdev", sizeof("netdev")) == 0) {
return NFPROTO_NETDEV;
} else if (strncmp(family, "bridge", sizeof("bridge")) == 0) {
return NFPROTO_BRIDGE;
} else if (strncmp(family, "decnet", sizeof("decnet")) == 0) {
return NFPROTO_DECNET;
} else {
return NFPROTO_UNSPEC;
}
}
static struct rtattr *_nftset_nlmsg_tail(struct nlmsghdr *n)
{
return (struct rtattr *)((uint8_t *)n + NLMSG_ALIGN(n->nlmsg_len));
}
static int _nftset_addattr(struct nlmsghdr *n, int maxlen, __u16 type, const void *data, __u16 alen)
{
const __u16 len = RTA_LENGTH(alen);
const ssize_t newlen = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len);
if (newlen > maxlen) {
errno = ENOSPC;
return -1;
}
struct rtattr *attr = _nftset_nlmsg_tail(n);
attr->rta_len = len;
attr->rta_type = type;
void *rta_data = RTA_DATA(attr);
memcpy(rta_data, data, alen);
memset((uint8_t *)rta_data + alen, 0, RTA_ALIGN(len) - len);
n->nlmsg_len = newlen;
return 0;
}
static int _nftset_addattr_string(struct nlmsghdr *n, int maxlen, __u16 type, const char *s)
{
return _nftset_addattr(n, maxlen, type, s, strlen(s) + 1);
}
static int __attribute__((unused)) _nftset_addattr_uint32(struct nlmsghdr *n, int maxlen, __u16 type, const uint32_t v)
{
return _nftset_addattr(n, maxlen, type, &v, sizeof(uint32_t));
}
static int __attribute__((unused)) _nftset_addattr_uint16(struct nlmsghdr *n, int maxlen, __u16 type, const uint16_t v)
{
return _nftset_addattr(n, maxlen, type, &v, sizeof(uint16_t));
}
static int __attribute__((unused)) _nftset_addattr_uint8(struct nlmsghdr *n, int maxlen, __u16 type, const uint8_t v)
{
return _nftset_addattr(n, maxlen, type, &v, sizeof(uint8_t));
}
static struct rtattr *_nftset_addattr_nest(struct nlmsghdr *n, int maxlen, __u16 type)
{
struct rtattr *attr = _nftset_nlmsg_tail(n);
if (-1 == _nftset_addattr(n, maxlen, type, NULL, 0)) {
return NULL;
}
return attr;
}
static void _nftset_addattr_nest_end(struct nlmsghdr *n, struct rtattr *nest)
{
const void *tail = _nftset_nlmsg_tail(n);
nest->rta_len = (uint8_t *)tail - (uint8_t *)nest;
}
static int _nftset_start_batch(void *buf, void **nextbuf)
{
struct nlmsgreq *req = (struct nlmsgreq *)buf;
memset(buf, 0, sizeof(struct nlmsgreq));
req->h.nlmsg_len = NLMSG_LENGTH(sizeof(struct nfgenmsg));
req->h.nlmsg_flags = NLM_F_REQUEST;
req->h.nlmsg_type = NFNL_MSG_BATCH_BEGIN;
req->h.nlmsg_seq = time(NULL);
req->m.res_id = NFNL_SUBSYS_NFTABLES;
if (nextbuf) {
*nextbuf = (uint8_t *)buf + req->h.nlmsg_len;
}
return 0;
}
static int _nftset_end_batch(void *buf, void **nextbuf)
{
struct nlmsgreq *req = (struct nlmsgreq *)buf;
memset(buf, 0, sizeof(struct nlmsgreq));
req->h.nlmsg_len = NLMSG_LENGTH(sizeof(struct nfgenmsg));
req->h.nlmsg_flags = NLM_F_REQUEST;
req->h.nlmsg_type = NFNL_MSG_BATCH_END;
req->h.nlmsg_seq = time(NULL);
req->m.res_id = NFNL_SUBSYS_NFTABLES;
if (nextbuf) {
*nextbuf = (uint8_t *)buf + req->h.nlmsg_len;
}
return 0;
}
static int _nftset_socket_init(void)
{
struct sockaddr_nl addr = {0};
addr.nl_family = AF_NETLINK;
addr.nl_pid = 0;
int fd = 0;
if (nftset_fd > 0) {
return 0;
}
fd = socket(AF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, NETLINK_NETFILTER);
if (fd < 0) {
return -1;
}
if (bind(fd, (struct sockaddr *)(&addr), sizeof(addr)) < 0) {
close(fd);
return -2;
}
nftset_fd = fd;
return 0;
}
static int _nftset_socket_request(void *msg, int msg_len, void *ret_msg, int ret_msg_len)
{
int ret = -1;
struct pollfd pfds;
int do_recv = 0;
int last_errno = 0;
int len = 0;
if (_nftset_socket_init() != 0) {
return -1;
}
/* clear pending error message*/
for (;;) {
uint8_t buff[1024];
ret = recv(nftset_fd, buff, sizeof(buff), MSG_DONTWAIT);
if (ret < 0) {
break;
}
}
for (;;) {
len = send(nftset_fd, msg, msg_len, 0);
if (len == msg_len) {
break;
}
if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
struct timespec waiter;
waiter.tv_sec = 0;
waiter.tv_nsec = 10000;
nanosleep(&waiter, NULL);
continue;
}
return -1;
}
if (ret_msg == NULL || ret_msg_len <= 0) {
return 0;
}
pfds.fd = nftset_fd;
pfds.events = POLLIN;
pfds.revents = 0;
ret = poll(&pfds, 1, 100);
if (ret <= 0) {
return -1;
}
if ((pfds.revents & POLLIN) == 0) {
return -1;
}
memset(ret_msg, 0, ret_msg_len);
len = 0;
for (;;) {
ret = recv(nftset_fd, ret_msg + len, ret_msg_len - len, 0);
if (ret < 0) {
if (errno == EAGAIN && do_recv == 1) {
break;
}
if (errno == EAGAIN && last_errno != 0) {
errno = last_errno;
}
return -1;
}
do_recv = 1;
len += ret;
struct nlmsghdr *nlh = (struct nlmsghdr *)ret_msg;
if (nlh->nlmsg_type == NLMSG_ERROR) {
struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(nlh);
if (err->error != 0) {
errno = -err->error;
last_errno = errno;
return -1;
}
continue;
}
if (nlh->nlmsg_type & (NFNL_SUBSYS_NFTABLES << 8)) {
if (nlh->nlmsg_type & NLMSG_DONE) {
break;
}
}
errno = ENOTSUP;
return -1;
}
return 0;
}
static int _nftset_socket_send(void *msg, int msg_len)
{
char recvbuff[1024];
if (dns_conf_nftset_debug_enable == 0) {
return _nftset_socket_request(msg, msg_len, NULL, 0);
}
return _nftset_socket_request(msg, msg_len, recvbuff, sizeof(recvbuff));
}
static int _nftset_get_nftset(int nffamily, const char *table_name, const char *setname, void *buf, void **nextbuf)
{
struct nlmsgreq *req = (struct nlmsgreq *)buf;
memset(buf, 0, sizeof(struct nlmsgreq));
req->h.nlmsg_len = NLMSG_LENGTH(sizeof(struct nfgenmsg));
req->h.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
req->h.nlmsg_type = NFNL_SUBSYS_NFTABLES << 8 | NFT_MSG_GETSET;
req->h.nlmsg_seq = time(NULL);
req->m.nfgen_family = nffamily;
req->m.res_id = NFNL_SUBSYS_NFTABLES;
req->m.version = 0;
struct nlmsghdr *n = &req->h;
_nftset_addattr_string(n, PAYLOAD_MAX, NFTA_SET_ELEM_LIST_SET, setname);
_nftset_addattr_string(n, PAYLOAD_MAX, NFTA_SET_ELEM_LIST_TABLE, table_name);
if (nextbuf) {
*nextbuf = (uint8_t *)buf + req->h.nlmsg_len;
}
return 0;
}
static int _nftset_get_flags(int nffamily, const char *tablename, const char *setname, uint32_t *flags)
{
uint8_t buf[PAYLOAD_MAX];
uint8_t result[PAYLOAD_MAX];
void *next = buf;
int buffer_len = 0;
if (flags == NULL) {
return -1;
}
_nftset_get_nftset(nffamily, tablename, setname, next, &next);
buffer_len = (uint8_t *)next - buf;
int ret = _nftset_socket_request(buf, buffer_len, result, sizeof(result));
if (ret < 0) {
return -1;
}
struct nlmsghdr *nlh = (struct nlmsghdr *)result;
struct nfgenmsg *nfmsg = (struct nfgenmsg *)NLMSG_DATA(nlh);
struct nfattr *nfa = (struct nfattr *)NFM_NFA(nfmsg);
*flags = 0;
for (; NFA_OK(nfa, nlh->nlmsg_len); nfa = NFA_NEXT(nfa, nlh->nlmsg_len)) {
if (nfa->nfa_type == NFTA_SET_FLAGS) {
*flags = ntohl(*(uint32_t *)NFA_DATA(nfa));
break;
}
}
return 0;
}
static int _nftset_del_element(int nffamily, const char *table_name, const char *setname, const void *data,
int data_len, const void *data_interval, int data_interval_len, void *buf,
void **nextbuf)
{
struct nlmsgreq *req = (struct nlmsgreq *)buf;
memset(buf, 0, sizeof(struct nlmsgreq));
req->h.nlmsg_len = NLMSG_LENGTH(sizeof(struct nfgenmsg));
req->h.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE;
req->h.nlmsg_type = NFNL_SUBSYS_NFTABLES << 8 | NFT_MSG_DELSETELEM;
req->h.nlmsg_seq = time(NULL);
if (dns_conf_nftset_debug_enable) {
req->h.nlmsg_flags |= NLM_F_ACK;
}
req->m.nfgen_family = nffamily;
struct nlmsghdr *n = &req->h;
_nftset_addattr_string(n, PAYLOAD_MAX, NFTA_SET_ELEM_LIST_TABLE, table_name);
_nftset_addattr_string(n, PAYLOAD_MAX, NFTA_SET_ELEM_LIST_SET, setname);
struct rtattr *nest_list = _nftset_addattr_nest(n, PAYLOAD_MAX, NLA_F_NESTED | NFTA_SET_ELEM_LIST_ELEMENTS);
struct rtattr *nest_elem = _nftset_addattr_nest(n, PAYLOAD_MAX, NLA_F_NESTED);
struct rtattr *nest_elem_key = _nftset_addattr_nest(n, PAYLOAD_MAX, NLA_F_NESTED | NFTA_SET_ELEM_KEY);
_nftset_addattr(n, PAYLOAD_MAX, NFTA_DATA_VALUE, data, data_len);
_nftset_addattr_nest_end(n, nest_elem_key);
_nftset_addattr_nest_end(n, nest_elem);
/* interval attribute */
if (data_interval && data_interval_len > 0) {
struct rtattr *nest_interval_end = _nftset_addattr_nest(n, PAYLOAD_MAX, NLA_F_NESTED | NFTA_LIST_ELEM);
_nftset_addattr_uint32(n, PAYLOAD_MAX, NFTA_SET_ELEM_FLAGS, htonl(NFT_SET_ELEM_INTERVAL_END));
struct rtattr *nest_elem_interval_key = _nftset_addattr_nest(n, PAYLOAD_MAX, NLA_F_NESTED | NFTA_SET_ELEM_KEY);
_nftset_addattr(n, PAYLOAD_MAX, NFTA_DATA_VALUE, data_interval, data_interval_len);
_nftset_addattr_nest_end(n, nest_elem_interval_key);
_nftset_addattr_nest_end(n, nest_interval_end);
}
_nftset_addattr_nest_end(n, nest_list);
if (nextbuf) {
*nextbuf = (uint8_t *)buf + req->h.nlmsg_len;
}
return 0;
}
static int _nftset_add_element(int nffamily, const char *table_name, const char *setname, const void *data,
int data_len, const void *data_interval, int data_interval_len, unsigned long timeout,
void *buf, void **nextbuf)
{
struct nlmsgreq *req = (struct nlmsgreq *)buf;
memset(buf, 0, sizeof(struct nlmsgreq));
req->h.nlmsg_len = NLMSG_LENGTH(sizeof(struct nfgenmsg));
req->h.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE;
req->h.nlmsg_type = NFNL_SUBSYS_NFTABLES << 8 | NFT_MSG_NEWSETELEM;
req->h.nlmsg_seq = time(NULL);
if (dns_conf_nftset_debug_enable) {
req->h.nlmsg_flags |= NLM_F_ACK;
}
req->m.nfgen_family = nffamily;
struct nlmsghdr *n = &req->h;
_nftset_addattr_string(n, PAYLOAD_MAX, NFTA_SET_ELEM_LIST_TABLE, table_name);
_nftset_addattr_string(n, PAYLOAD_MAX, NFTA_SET_ELEM_LIST_SET, setname);
struct rtattr *nest_list = _nftset_addattr_nest(n, PAYLOAD_MAX, NLA_F_NESTED | NFTA_SET_ELEM_LIST_ELEMENTS);
struct rtattr *nest_elem = _nftset_addattr_nest(n, PAYLOAD_MAX, NLA_F_NESTED | NFTA_LIST_ELEM);
struct rtattr *nest_elem_key = _nftset_addattr_nest(n, PAYLOAD_MAX, NLA_F_NESTED | NFTA_SET_ELEM_KEY);
_nftset_addattr(n, PAYLOAD_MAX, NFTA_DATA_VALUE, data, data_len);
_nftset_addattr_nest_end(n, nest_elem_key);
if (timeout > 0) {
uint64_t timeout_value = htobe64(timeout * 1000);
_nftset_addattr(n, PAYLOAD_MAX, NFTA_SET_ELEM_TIMEOUT, &timeout_value, sizeof(timeout_value));
}
_nftset_addattr_nest_end(n, nest_elem);
/* interval attribute */
if (data_interval && data_interval_len > 0) {
struct rtattr *nest_interval_end = _nftset_addattr_nest(n, PAYLOAD_MAX, NLA_F_NESTED | NFTA_LIST_ELEM);
_nftset_addattr_uint32(n, PAYLOAD_MAX, NFTA_SET_ELEM_FLAGS, htonl(NFT_SET_ELEM_INTERVAL_END));
struct rtattr *nest_elem_interval_key = _nftset_addattr_nest(n, PAYLOAD_MAX, NLA_F_NESTED | NFTA_SET_ELEM_KEY);
_nftset_addattr(n, PAYLOAD_MAX, NFTA_DATA_VALUE, data_interval, data_interval_len);
_nftset_addattr_nest_end(n, nest_elem_interval_key);
_nftset_addattr_nest_end(n, nest_interval_end);
}
_nftset_addattr_nest_end(n, nest_list);
if (nextbuf) {
*nextbuf = (uint8_t *)buf + req->h.nlmsg_len;
}
return 0;
}
static int _nftset_process_setflags(uint32_t flags, const unsigned char addr[], int addr_len, unsigned long *timeout,
uint8_t **interval_addr, int *interval_addr_len)
{
uint8_t *addr_end = *interval_addr;
if ((flags & NFT_SET_TIMEOUT) == 0 && timeout != NULL) {
*timeout = 0;
}
if ((flags & NFT_SET_INTERVAL) && addr_end != NULL) {
if (addr_len == 4) {
addr_end[0] = addr[0];
addr_end[1] = addr[1];
addr_end[2] = addr[2];
addr_end[3] = addr[3] + 1;
if (addr_end[3] == 0) {
return -1;
}
*interval_addr_len = 4;
} else if (addr_len == 16) {
memcpy(addr_end, addr, 16);
addr_end[15] = addr[15] + 1;
if (addr_end[15] == 0) {
return -1;
}
*interval_addr_len = 16;
}
} else {
*interval_addr = NULL;
*interval_addr_len = 0;
}
return 0;
}
static int _nftset_del(int nffamily, const char *tablename, const char *setname, const unsigned char addr[],
int addr_len, const unsigned char addr_end[], int addr_end_len)
{
uint8_t buf[PAYLOAD_MAX];
void *next = buf;
int buffer_len = 0;
_nftset_start_batch(next, &next);
_nftset_del_element(nffamily, tablename, setname, addr, addr_len, addr_end, addr_end_len, next, &next);
_nftset_end_batch(next, &next);
buffer_len = (uint8_t *)next - buf;
return _nftset_socket_send(buf, buffer_len);
}
int nftset_del(const char *familyname, const char *tablename, const char *setname, const unsigned char addr[],
int addr_len)
{
int nffamily = _nftset_get_nffamily_from_str(familyname);
uint8_t addr_end_buff[16] = {0};
uint8_t *addr_end = addr_end_buff;
uint32_t flags = 0;
int addr_end_len = 0;
int ret = -1;
ret = _nftset_get_flags(nffamily, tablename, setname, &flags);
if (ret == 0) {
ret = _nftset_process_setflags(flags, addr, addr_len, 0, &addr_end, &addr_end_len);
if (ret != 0) {
return -1;
}
} else {
addr_end = NULL;
addr_end_len = 0;
}
ret = _nftset_del(nffamily, tablename, setname, addr, addr_len, addr_end, addr_end_len);
if (ret != 0 && errno != ENOENT) {
tlog(TLOG_ERROR, "nftset delete failed, family:%s, table:%s, set:%s, error:%s", familyname, tablename, setname,
strerror(errno));
}
return ret;
}
int nftset_add(const char *familyname, const char *tablename, const char *setname, const unsigned char addr[],
int addr_len, unsigned long timeout)
{
uint8_t buf[PAYLOAD_MAX];
uint8_t addr_end_buff[16] = {0};
uint8_t *addr_end = addr_end_buff;
uint32_t flags = 0;
int addr_end_len = 0;
void *next = buf;
int buffer_len = 0;
int ret = -1;
int nffamily = _nftset_get_nffamily_from_str(familyname);
if (dns_conf_nftset_timeout_enable == 0) {
timeout = 0;
}
ret = _nftset_get_flags(nffamily, tablename, setname, &flags);
if (ret == 0) {
ret = _nftset_process_setflags(flags, addr, addr_len, &timeout, &addr_end, &addr_end_len);
if (ret != 0) {
if (dns_conf_nftset_debug_enable) {
tlog(TLOG_ERROR, "nftset add failed, family:%s, table:%s, set:%s, error:%s", familyname, tablename,
setname, "ip is invalid");
}
return -1;
}
} else {
addr_end = NULL;
addr_end_len = 0;
}
if (timeout > 0) {
_nftset_del(nffamily, tablename, setname, addr, addr_len, addr_end, addr_end_len);
}
_nftset_start_batch(next, &next);
_nftset_add_element(nffamily, tablename, setname, addr, addr_len, addr_end, addr_end_len, timeout, next, &next);
_nftset_end_batch(next, &next);
buffer_len = (uint8_t *)next - buf;
ret = _nftset_socket_send(buf, buffer_len);
if (ret != 0) {
tlog(TLOG_ERROR, "nftset add failed, family:%s, table:%s, set:%s, error:%s", familyname, tablename, setname,
strerror(errno));
}
return ret;
}
#else
int nftset_add(const char *familyname, const char *tablename, const char *setname, const unsigned char addr[],
int addr_len, unsigned long timeout)
{
return 0;
}
int nftset_del(const char *familyname, const char *tablename, const char *setname, const unsigned char addr[],
int addr_len)
{
return 0;
}
#endif

View File

@@ -40,6 +40,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <sys/prctl.h> #include <sys/prctl.h>
#include <sys/resource.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
#include <ucontext.h> #include <ucontext.h>
@@ -99,7 +100,7 @@ out:
static int drop_root_privilege(void) static int drop_root_privilege(void)
{ {
struct __user_cap_data_struct cap; struct __user_cap_data_struct cap[2];
struct __user_cap_header_struct header; struct __user_cap_header_struct header;
#ifdef _LINUX_CAPABILITY_VERSION_3 #ifdef _LINUX_CAPABILITY_VERSION_3
header.version = _LINUX_CAPABILITY_VERSION_3; header.version = _LINUX_CAPABILITY_VERSION_3;
@@ -115,16 +116,20 @@ static int drop_root_privilege(void)
return -1; return -1;
} }
if (capget(&header, &cap) < 0) { memset(cap, 0, sizeof(cap));
if (capget(&header, cap) < 0) {
return -1; return -1;
} }
prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0); prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
cap.effective |= (1 << CAP_NET_RAW | 1 << CAP_NET_ADMIN); for (int i = 0; i < 2; i++) {
cap.permitted |= (1 << CAP_NET_RAW | 1 << CAP_NET_ADMIN); cap[i].effective = (1 << CAP_NET_RAW | 1 << CAP_NET_ADMIN | 1 << CAP_NET_BIND_SERVICE);
cap[i].permitted = (1 << CAP_NET_RAW | 1 << CAP_NET_ADMIN | 1 << CAP_NET_BIND_SERVICE);
}
unused = setgid(gid); unused = setgid(gid);
unused = setuid(uid); unused = setuid(uid);
if (capset(&header, &cap) < 0) { if (capset(&header, cap) < 0) {
return -1; return -1;
} }
@@ -138,7 +143,7 @@ static void _help(void)
char *help = "" char *help = ""
"Usage: smartdns [OPTION]...\n" "Usage: smartdns [OPTION]...\n"
"Start smartdns server.\n" "Start smartdns server.\n"
" -f run forground.\n" " -f run foreground.\n"
" -c [conf] config file.\n" " -c [conf] config file.\n"
" -p [pid] pid file path, '-' means don't create pid file.\n" " -p [pid] pid file path, '-' means don't create pid file.\n"
" -S ignore segment fault signal.\n" " -S ignore segment fault signal.\n"
@@ -508,17 +513,28 @@ static int _smartdns_create_logdir(void)
return 0; return 0;
} }
static int _set_rlimit(void)
{
struct rlimit value;
value.rlim_cur = 40;
value.rlim_max = 40;
setrlimit(RLIMIT_NICE, &value);
return 0;
}
static int _smartdns_init_pre(void) static int _smartdns_init_pre(void)
{ {
_smartdns_create_logdir(); _smartdns_create_logdir();
_set_rlimit();
return 0; return 0;
} }
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
int ret = 0; int ret = 0;
int is_forground = 0; int is_foreground = 0;
int opt = 0; int opt = 0;
char config_file[MAX_LINE_LEN]; char config_file[MAX_LINE_LEN];
char pid_file[MAX_LINE_LEN]; char pid_file[MAX_LINE_LEN];
@@ -535,7 +551,7 @@ int main(int argc, char *argv[])
while ((opt = getopt(argc, argv, "fhc:p:SvxN:")) != -1) { while ((opt = getopt(argc, argv, "fhc:p:SvxN:")) != -1) {
switch (opt) { switch (opt) {
case 'f': case 'f':
is_forground = 1; is_foreground = 1;
break; break;
case 'c': case 'c':
snprintf(config_file, sizeof(config_file), "%s", optarg); snprintf(config_file, sizeof(config_file), "%s", optarg);
@@ -568,7 +584,7 @@ int main(int argc, char *argv[])
goto errout; goto errout;
} }
if (is_forground == 0) { if (is_foreground == 0) {
if (daemon(0, 0) < 0) { if (daemon(0, 0) < 0) {
fprintf(stderr, "run daemon process failed, %s\n", strerror(errno)); fprintf(stderr, "run daemon process failed, %s\n", strerror(errno));
return 1; return 1;

View File

@@ -18,10 +18,11 @@
#ifndef _GNU_SOURCE #ifndef _GNU_SOURCE
#define _GNU_SOURCE #define _GNU_SOURCE
#include <stdio.h>
#endif #endif
#include "util.h"
#include "dns_conf.h" #include "dns_conf.h"
#include "tlog.h" #include "tlog.h"
#include "util.h"
#include <arpa/inet.h> #include <arpa/inet.h>
#include <dlfcn.h> #include <dlfcn.h>
#include <errno.h> #include <errno.h>
@@ -30,6 +31,7 @@
#include <linux/capability.h> #include <linux/capability.h>
#include <linux/limits.h> #include <linux/limits.h>
#include <linux/netlink.h> #include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <netinet/tcp.h> #include <netinet/tcp.h>
#include <openssl/crypto.h> #include <openssl/crypto.h>
#include <openssl/ssl.h> #include <openssl/ssl.h>
@@ -1371,6 +1373,62 @@ static int _dns_debug_display(struct dns_packet *packet)
inet_ntop(AF_INET6, addr, req_host, sizeof(req_host)); inet_ntop(AF_INET6, addr, req_host, sizeof(req_host));
printf("domain: %s AAAA: %s TTL:%d\n", name, req_host, ttl); printf("domain: %s AAAA: %s TTL:%d\n", name, req_host, ttl);
} break; } break;
case DNS_T_HTTPS: {
char name[DNS_MAX_CNAME_LEN] = {0};
char target[DNS_MAX_CNAME_LEN] = {0};
struct dns_https_param *p = NULL;
int priority = 0;
p = dns_get_HTTPS_svcparm_start(rrs, name, DNS_MAX_CNAME_LEN, &ttl, &priority, target,
DNS_MAX_CNAME_LEN);
if (p == NULL) {
printf("get HTTPS svcparm failed\n");
break;
}
printf("domain: %s HTTPS: %s TTL: %d priority: %d\n", name, target, ttl, priority);
for (; p; p = dns_get_HTTPS_svcparm_next(rrs, p)) {
switch (p->key) {
case DNS_HTTPS_T_MANDATORY: {
printf(" HTTPS: mandatory: %s\n", p->value);
} break;
case DNS_HTTPS_T_ALPN: {
printf(" HTTPS: alpn: %s\n", p->value);
} break;
case DNS_HTTPS_T_NO_DEFAULT_ALPN: {
printf(" HTTPS: no_default_alpn: %s\n", p->value);
} break;
case DNS_HTTPS_T_PORT: {
int port = *(unsigned short *)(p->value);
printf(" HTTPS: port: %d\n", port);
} break;
case DNS_HTTPS_T_IPV4HINT: {
printf(" HTTPS: ipv4hint: %d\n", p->len / 4);
for (int k = 0; k < p->len / 4; k++) {
char ip[16] = {0};
inet_ntop(AF_INET, p->value + k * 4, ip, sizeof(ip));
printf(" ipv4: %s\n", ip);
}
} break;
case DNS_HTTPS_T_ECH: {
printf(" HTTPS: ech: ");
for (int k = 0; k < p->len; k++) {
printf("%02x ", p->value[k]);
}
printf("\n");
} break;
case DNS_HTTPS_T_IPV6HINT: {
printf(" HTTPS: ipv6hint: %d\n", p->len / 16);
for (int k = 0; k < p->len / 16; k++) {
char ip[64] = {0};
inet_ntop(AF_INET6, p->value + k * 16, ip, sizeof(ip));
printf(" ipv6: %s\n", ip);
}
} break;
}
}
} break;
case DNS_T_NS: { case DNS_T_NS: {
char cname[DNS_MAX_CNAME_LEN]; char cname[DNS_MAX_CNAME_LEN];
char name[DNS_MAX_CNAME_LEN] = {0}; char name[DNS_MAX_CNAME_LEN] = {0};
@@ -1454,4 +1512,4 @@ errout:
return -1; return -1;
} }
#endif #endif

View File

@@ -46,9 +46,7 @@ extern "C" {
#define MAX_IP_LEN 64 #define MAX_IP_LEN 64
#ifndef BASE_FILE_NAME #ifndef BASE_FILE_NAME
#define BASE_FILE_NAME \ #define BASE_FILE_NAME (__builtin_strrchr(__FILE__, '/') ? __builtin_strrchr(__FILE__, '/') + 1 : __FILE__)
(__builtin_strrchr(__FILE__, '/') ? __builtin_strrchr(__FILE__, '/') + 1 \
: __FILE__)
#endif #endif
#define BUG(format, ...) bug_ext(BASE_FILE_NAME, __LINE__, __func__, format, ##__VA_ARGS__) #define BUG(format, ...) bug_ext(BASE_FILE_NAME, __LINE__, __func__, format, ##__VA_ARGS__)