update 2026-10-07 18:47:42

This commit is contained in:
action committed 2026-10-07 18:47:42 +08:00
1 parent 617f7ca2a6
commit bd061545ed
4832 files changed
+1693456

No files matched your search

+111
View File
@@ -0,0 +1,111 @@
name: Auto Update oxidns Version
on:
schedule:
# 每天 UTC 02:00 检查(北京时间 10:00)
- cron: '0 2 * * *'
workflow_dispatch: # 允许手动触发
permissions:
contents: write
jobs:
update:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
# 使用 GITHUB_TOKEN 推送;如果分支有保护规则需换成 PAT
token: ${{ secrets.GITHUB_TOKEN }}
fetch-depth: 0
- name: Get latest release tag from upstream
id: upstream
run: |
LATEST_TAG=$(curl -sL \
-H "Accept: application/vnd.github+json" \
https://api.github.com/repos/svenshi/oxidns/releases/latest \
| jq -r '.tag_name')
if [[ -z "$LATEST_TAG" || "$LATEST_TAG" == "null" ]]; then
echo "::error::Failed to fetch latest release tag"
exit 1
fi
NEW_VERSION="${LATEST_TAG#v}"
echo "latest_tag=${LATEST_TAG}" >> "$GITHUB_OUTPUT"
echo "new_version=${NEW_VERSION}" >> "$GITHUB_OUTPUT"
echo "Latest tag: ${LATEST_TAG} (version: ${NEW_VERSION})"
- name: Get current version from Makefile
id: current
run: |
CURRENT_VERSION=$(grep -oP '^PKG_VERSION:=\K.*' Makefile)
echo "current_version=${CURRENT_VERSION}" >> "$GITHUB_OUTPUT"
echo "Current version: ${CURRENT_VERSION}"
- name: Compare versions
id: compare
run: |
if [ "${{ steps.current.outputs.current_version }}" = "${{ steps.upstream.outputs.new_version }}" ]; then
echo "update_needed=false" >> "$GITHUB_OUTPUT"
echo "Already up to date."
else
echo "update_needed=true" >> "$GITHUB_OUTPUT"
echo "Update needed: ${{ steps.current.outputs.current_version }} -> ${{ steps.upstream.outputs.new_version }}"
fi
- name: Get commit hash for the release tag
if: steps.compare.outputs.update_needed == 'true'
id: tag_commit
run: |
TAG="${{ steps.upstream.outputs.latest_tag }}"
REF_JSON=$(curl -sL \
-H "Accept: application/vnd.github+json" \
"https://api.github.com/repos/svenshi/oxidns/git/ref/tags/${TAG}")
OBJECT_TYPE=$(echo "$REF_JSON" | jq -r '.object.type')
COMMIT_SHA=$(echo "$REF_JSON" | jq -r '.object.sha')
# annotated tag 需要再解析一层
if [[ "$OBJECT_TYPE" == "tag" ]]; then
COMMIT_SHA=$(curl -sL \
-H "Accept: application/vnd.github+json" \
"https://api.github.com/repos/svenshi/oxidns/git/tags/${COMMIT_SHA}" \
| jq -r '.object.sha')
fi
if [[ -z "$COMMIT_SHA" || "$COMMIT_SHA" == "null" ]]; then
echo "::error::Failed to fetch commit hash for tag ${TAG}"
exit 1
fi
echo "commit_hash=${COMMIT_SHA}" >> "$GITHUB_OUTPUT"
echo "Commit hash: ${COMMIT_SHA}"
- name: Run update script
if: steps.compare.outputs.update_needed == 'true'
run: |
chmod +x update.sh
./update.sh \
"${{ steps.upstream.outputs.new_version }}" \
"${{ steps.tag_commit.outputs.commit_hash }}"
- name: Commit and push to current branch
if: steps.compare.outputs.update_needed == 'true'
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add Makefile
if git diff --cached --quiet; then
echo "No changes to commit."
exit 0
fi
git commit -m "oxidns: update to ${{ steps.upstream.outputs.new_version }} [skip ci]"
git push origin "HEAD:${{ github.ref_name }}"
+123
View File
@@ -0,0 +1,123 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=oxidns
PKG_VERSION:=1.6.0
PKG_RELEASE:=1
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/svenshi/oxidns.git
PKG_SOURCE_DATE:=2026-09-28
PKG_SOURCE_VERSION:=5e83394976790f60bd4ed0f059aa65ea1c279c21
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
PKG_SOURCE:=$(PKG_SOURCE_SUBDIR).tar.zst
PKG_MIRROR_HASH:=skip
PKG_LICENSE:=GPL-3.0-or-later
PKG_LICENSE_FILES:=LICENSE
PKG_MAINTAINER:=Sven Shi <isvenshi@gmail.com>
PKG_CONFIG_DEPENDS:=CONFIG_PACKAGE_oxidns-webui
# rust 是编译 oxidns 本体必需;node/npm 只在需要构建 webui 时才依赖
PKG_BUILD_DEPENDS:=rust/host PACKAGE_oxidns-webui:node/host
PKG_BUILD_PARALLEL:=1
WEBUI_DIST:=out
include $(INCLUDE_DIR)/package.mk
include $(TOPDIR)/feeds/packages/lang/rust/rust-package.mk
# ---------------------------------------------------------------------------
# Package 定义
# ---------------------------------------------------------------------------
define Package/oxidns
SECTION:=net
CATEGORY:=Network
SUBMENU:=DNS
TITLE:=OxiDNS - High-performance DNS Engine
DEPENDS:=
URL:=https://github.com/svenshi/oxidns
endef
define Package/oxidns/description
A high-performance, programmable DNS engine in Rust with flexible
pipeline-based routing.
endef
define Package/oxidns-webui
$(call Package/oxidns/Default)
SECTION:=net
CATEGORY:=Network
SUBMENU:=DNS
TITLE:=OxiDNS - Web UI
DEPENDS:=+oxidns
endef
define Package/oxidns-webui/description
A web UI for the OxiDNS server.
endef
define Package/oxidns/conffiles
/etc/oxidns/config.yaml
endef
# ---------------------------------------------------------------------------
# Prepare:仅做源码解压与补丁
# ---------------------------------------------------------------------------
define Build/Prepare
$(call Build/Prepare/Default)
endef
# ---------------------------------------------------------------------------
# Configure
# ---------------------------------------------------------------------------
define Build/Configure
$(call Build/Configure/Default)
endef
# ---------------------------------------------------------------------------
# Compile:先编译 Rust,再构建前端(若选中 webui)
# 用 shell 判断而不是 Makefile 的 ifneq,避免配方在解析期被固定
# ---------------------------------------------------------------------------
define Build/Compile
$(call Build/Compile/Cargo,,--features full)
if [ -n "$(CONFIG_PACKAGE_oxidns-webui)" ]; then \
echo "===> Building OxiDNS webui ..."; \
cd $(PKG_BUILD_DIR)/webui || { echo "webui dir missing"; exit 1; }; \
if [ ! -f package.json ]; then \
echo "ERROR: webui/package.json not found"; exit 1; \
fi; \
npx -y pnpm install --ignore-scripts || { echo "pnpm install failed"; exit 1; }; \
npx -y pnpm build || { echo "pnpm build failed"; exit 1; }; \
if [ ! -d "$(WEBUI_DIST)" ]; then \
echo "ERROR: webui build output '$(WEBUI_DIST)' not found"; \
ls -la; exit 1; \
fi; \
echo "===> webui build OK: $(WEBUI_DIST)"; \
fi
endef
# ---------------------------------------------------------------------------
# Install
# ---------------------------------------------------------------------------
define Package/oxidns/install
$(INSTALL_DIR) $(1)/usr/bin
$(INSTALL_BIN) $(PKG_BUILD_DIR)/target/$(RUSTC_TARGET_ARCH)/release/oxidns $(1)/usr/bin/
$(INSTALL_DIR) $(1)/etc/oxidns
$(INSTALL_CONF) ./files/config.yaml $(1)/etc/oxidns/config.yaml
endef
define Package/oxidns-webui/install
$(INSTALL_DIR) $(1)/usr/share/oxidns/webui
$(CP) $(PKG_BUILD_DIR)/webui/$(WEBUI_DIST)/. $(1)/usr/share/oxidns/webui/
endef
$(eval $(call BuildPackage,oxidns))
$(eval $(call BuildPackage,oxidns-webui))
+17
View File
@@ -0,0 +1,17 @@
# OxiDNS OpenWrt Package
高性能 DNS 策略编排引擎的 OpenWrt 包。
## 功能
- UDP/TCP/DoT/DoQ/DoH DNS 服务
- 灵活的策略路由和转发
- DNS 缓存和 TTL 管理
- 回退和负载均衡
- 本地应答和域名改写
- Prometheus 指标导出
- WebUI 管理界面
## 链接
https://github.com/svenshi/oxidns.git
+355
View File
@@ -0,0 +1,355 @@
# ==============================================================================
# 部署位置:家庭网关/软路由(10.0.10.2),统一接管内网 DNS 请求
# 核心职责:
# 1) 国内域名 → 走 DoH/UDP 国内上游(腾讯/阿里/火山)
# 2) 国外域名 → 转发给 Clash (:1053) 走代理出网
# 3) 广告/恶意域名 → 直接黑洞 (NXDOMAIN)
# 4) 微软 NCSI 等连通性检测域名 → 强制走国内序列,避免误判"断网"
# 5) HTTPS RR (qtype=65) → 走国外序列,让 ECH/DoH 走代理
# ==============================================================================
log:
# 日志级别:trace / debug / info / warn / error
level: info
# 日志文件路径(相对路径以 oxiDNS 运行目录为准)
file: ./oxidns.log
rotation:
# 按天切割日志,避免单文件过大
type: daily
# 保留最近 3 个日志文件,超出自动清理
max_files: 3
api:
http:
listen: 0.0.0.0:9199
auth:
type: basic
username: admin
password: oxidns
webui:
root: /usr/share/oxidns/webui
index: index.html
plugins:
# ---------------------------------------------------------------------------
# 定时任务:每天定时下载规则并热加载(无需重启进程)
# ---------------------------------------------------------------------------
- tag: cron_update
type: cron
args:
timezone: Asia/Shanghai
jobs:
- name: update_rules
interval: 1d
executors:
- $download_rules # 先下载最新规则
- $reload_rules # 再热加载 provider,避免下载过程中规则不一致
# ---------------------------------------------------------------------------
# 规则下载:广告/安全列表 + GeoIP/GeoSite 地理位置数据
# ---------------------------------------------------------------------------
- tag: download_rules
type: download
args:
timeout: 180s # 单个文件下载超时时间
startup_if_missing: true # 启动时若本地缺少规则文件则立即补拉
downloads:
# ---------- 广告 / 隐私保护规则 ----------
# anti-ad.net:中文站点优化,国内直连可达
- url: https://anti-ad.net/adguard.txt
dir: /etc/oxidns/rules
filename: antiad.txt
# OISD Basic:综合广告/追踪列表,big.oisd.nl 国内直连可达
- url: https://big.oisd.nl
dir: /etc/oxidns/rules
filename: oisd_big.txt
# Hagezi 系列:统一走 jsDelivr CDN,国内访问更稳定
- url: https://cdn.jsdelivr.net/gh/hagezi/dns-blocklists@main/adblock/ultimate.mini.txt
dir: /etc/oxidns/rules
filename: hagezi_ultimate_mini.txt
- url: https://cdn.jsdelivr.net/gh/hagezi/dns-blocklists@main/adblock/popupads.txt
dir: /etc/oxidns/rules
filename: hagezi_popupads.txt
- url: https://cdn.jsdelivr.net/gh/hagezi/dns-blocklists@main/adblock/hoster.txt
dir: /etc/oxidns/rules
filename: hagezi_hoster.txt
- url: https://cdn.jsdelivr.net/gh/hagezi/dns-blocklists@main/adblock/doh.txt
dir: /etc/oxidns/rules
filename: hagezi_doh.txt
# ---------- 地理位置数据 ----------
# Loyalsoldier/v2ray-rules-dat:GeoSite 域名分类(cn / geolocation-!cn)
- url: https://cdn.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/geosite.dat
dir: /etc/oxidns/rules
filename: geosite.dat
# Loyalsoldier/geoip:仅含中国大陆 + 私有网段
- url: https://cdn.jsdelivr.net/gh/Loyalsoldier/geoip@release/geoip-only-cn-private.dat
dir: /etc/oxidns/rules
filename: geoip.dat
# ---------------------------------------------------------------------------
# 热加载:下载完成后重载以下 provider,无需重启 oxiDNS
# ---------------------------------------------------------------------------
- tag: reload_rules
type: reload_provider
args:
- $ad_rules # 广告规则
- $security_rules # 安全规则
- $geoip_cn # 国内 IP
- $geosite_cn # 国内域名
- $geosite_no_cn # 非国内域名
- $ms_ncsi_white # 微软 NCSI 白名单
# ---------------------------------------------------------------------------
# Prometheus 指标收集(供 Prometheus / Grafana 抓取)
# ---------------------------------------------------------------------------
- tag: metrics_main
type: metrics_collector
args:
name: home # 指标名前缀:home_*
# ---------------------------------------------------------------------------
# 查询记录:落 SQLite,供 WebUI 查询/分析/排障
# ---------------------------------------------------------------------------
- tag: recorder_main
type: query_recorder
args:
path: /etc/oxidns/rules/query_history.sqlite
retention_days: 7 # 只保留最近 7 天,自动清理
# ---------------------------------------------------------------------------
# 规则 Provider:把 AdGuard 格式规则加载成内存 matcher
# ---------------------------------------------------------------------------
# 广告规则:偏"展示型广告 / 弹窗"拦截
- tag: ad_rules
type: adguard_rule
args:
files:
- /etc/oxidns/rules/antiad.txt
- /etc/oxidns/rules/hagezi_popupads.txt
# 安全规则:偏"恶意 / 钓鱼 / 追踪 / 恶意托管商 / DoH 滥用"拦截
- tag: security_rules
type: adguard_rule
args:
files:
- /etc/oxidns/rules/oisd_big.txt
- /etc/oxidns/rules/hagezi_ultimate_mini.txt
- /etc/oxidns/rules/hagezi_hoster.txt
- /etc/oxidns/rules/hagezi_doh.txt
# ---------------------------------------------------------------------------
# 地理位置 Provider:用于 IP / 域名分流判断
# ---------------------------------------------------------------------------
# 中国大陆 + 私有网段 IP
- tag: geoip_cn
type: geoip
args:
file: /etc/oxidns/rules/geoip.dat
selectors:
- cn
country: true # 按国家代码匹配
# 中国大陆域名(geosite-cn 分类)
- tag: geosite_cn
type: geosite
args:
file: /etc/oxidns/rules/geosite.dat
selectors:
- cn
# 非中国大陆域名(geolocation-!cn)
- tag: geosite_no_cn
type: geosite
args:
file: /etc/oxidns/rules/geosite.dat
selectors:
- geolocation-!cn
# ---------------------------------------------------------------------------
# 微软 NCSI 连通性探测域名白名单
# 这些域名必须能解析到任意可达 IP,否则 Windows 会判定"无网络"
# 因此强制走国内上游,不走代理
# ---------------------------------------------------------------------------
- tag: ms_ncsi_white
type: domain_set
args:
files: []
domains:
- msftconnecttest.com
- msftncsi.com
- ipv6.msftconnecttest.com
- www.msftconnecttest.com
- www.msftncsi.com
# ---------------------------------------------------------------------------
# 本地 hosts(可手工写死某些解析,目前为空,预留)
# ---------------------------------------------------------------------------
- tag: hosts
type: hosts
args:
files: []
# ---------------------------------------------------------------------------
# 缓存:分国内/国外两个实例,避免互相污染、TTL 策略也不同
# ---------------------------------------------------------------------------
# 国内缓存:容量较大,TTL 较激进(国内上游稳定)
- tag: cache_direct
type: cache
args:
size: 8192 # LRU 容量
short_circuit: true # 命中缓存直接返回,不再走下游
lazy_cache_ttl: 3600 # 懒加载 TTL(秒)
cache_negative: true # 缓存 NXDOMAIN 等否定应答
max_negative_ttl: 60 # 否定应答最长缓存
negative_ttl_without_soa: 10 # 无 SOA 的否定应答 TTL
max_positive_ttl: 3600 # 强制上限(防上游 TTL 过长)
min_positive_ttl: 5 # 强制下限(防上游 TTL 过短)
ecs_in_key: false # 不把 ECS 写入 key(家庭场景无需按子网区分)
dump_file: /etc/oxidns/rules/dns_cache_direct.dump # 退出时持久化
dump_interval: 1800 # 每 30 分钟周期性 dump
# 国外缓存:容量略小,TTL 更保守(依赖 OpenClash 出口)
- tag: cache_remote
type: cache
args:
size: 4096
short_circuit: true
lazy_cache_ttl: 3600
cache_negative: true
max_negative_ttl: 30
negative_ttl_without_soa: 10
max_positive_ttl: 3600
min_positive_ttl: 5
ecs_in_key: false
dump_file: /etc/oxidns/rules/dns_cache_remote.dump
dump_interval: 1800
# ---------------------------------------------------------------------------
# 广告黑洞:命中广告/安全规则时直接 NXDOMAIN
# ---------------------------------------------------------------------------
- tag: ad_blackhole
type: black_hole
args:
rcode: 3 # 3 = NXDOMAIN
# ---------------------------------------------------------------------------
# 上游转发:分国内 / OpenClash 两条
# ---------------------------------------------------------------------------
# 国内上游:并发查询 + prefer_positive,避免单一上游故障
- tag: forward_cn
type: forward
args:
concurrent: 3 # 同时向 3 个上游发,取最快正响应
response_selection: prefer_positive # 有正响应优先,避免被否定应答"误杀"
upstreams:
# 腾讯 DoH:dial_addr 写死 IP,防止 DoH 域名递归解析死锁
- tag: doh_tencent
addr: https://doh.pub/dns-query
dial_addr: 1.12.12.12
idle_timeout: 30
insecure_skip_verify: false
timeout: 5s
# 阿里 DoH:开启 HTTP/3 加速
- tag: doh_alibaba
addr: https://dns.alidns.com/dns-query
dial_addr: 223.5.5.5
idle_timeout: 30
insecure_skip_verify: false
timeout: 5s
enable_http3: true
# 火山 UDP:传统 UDP 53 作为备用
- tag: udp_volcengine
addr: 180.184.1.1:53
idle_timeout: 30
timeout: 3s
# 国外上游:交给 OpenClash 的 DNS 转发端口
# OpenClash 内部会做 FakeIP + 代理出网
- tag: forward_openclash
type: forward
args:
upstreams:
- tag: openclash_dns
addr: 127.0.0.1:1053
timeout: 8s
# ---------------------------------------------------------------------------
# 三段子序列:每段都是 "cache → upstream → 兜底"
# ---------------------------------------------------------------------------
# NCSI 子序列:仅给微软 NCSI 域名用,强制走国内上游保证"有网"
- tag: ncsi_seq
type: sequence
args:
- exec: $cache_direct
- matches: has_resp # 命中缓存直接返回
exec: accept
- exec: $forward_cn
- exec: accept # 兜底:无论返回什么都接受(让 NCSI 通过)
# 国内子序列:缓存 → 国内上游 → 仅保留国内 IP → 丢境外 IP
- tag: local_seq
type: sequence
args:
- exec: $cache_direct
- matches: has_resp
exec: accept
- exec: $forward_cn
- matches: resp_ip $geoip_cn # 响应 IP 必须属于中国大陆
exec: accept
- exec: drop_resp # 否则丢弃(防境外 IP 污染/劫持)
# 国外子序列:缓存 → OpenClash → 直接接受
- tag: remote_seq
type: sequence
args:
- exec: $cache_remote
- matches: has_resp
exec: accept
- exec: $forward_openclash
- exec: accept
# ---------------------------------------------------------------------------
# 主序列:所有查询的统一入口(按优先级顺序匹配)
# ---------------------------------------------------------------------------
- tag: main_sequence
type: sequence
args:
- exec: $recorder_main # 1) 全量记录查询日志(WebUI 可查)
- exec: $hosts # 2) hosts 优先
- matches: has_resp
exec: accept
- matches: qtype PTR # 3) PTR 反向查询直接拒绝
exec: reject 3 # 不向外泄露内网设备信息
- matches: qname $ms_ncsi_white # 4) 微软 NCSI → 国内序列(保证"已联网")
exec: goto ncsi_seq
- matches: qtype 65 # 5) HTTPS RR (SVCB/HTTPS, type=65)
exec: goto remote_seq # 让 ECH/DoH 引导信息走代理
- matches: qname $ad_rules # 6) 广告域名 → 黑洞
exec: $ad_blackhole
- matches: qname $security_rules # 7) 安全域名 → 黑洞
exec: $ad_blackhole
- matches: qname $geosite_cn # 8) 国内站点 → 国内序列(含 GeoIP 校验)
exec: goto local_seq
- matches: qname $geosite_no_cn # 9) 非国内站点 → 国外序列
exec: goto remote_seq
- exec: goto remote_seq # 10) 兜底:未分类域名一律走国外
# 防止 DNS 泄露到国内上游
- tag: udp_server
type: udp_server
args:
entry: main_sequence
listen: 127.0.0.1:5353
- tag: tcp_server
type: tcp_server
args:
entry: main_sequence
listen: 127.0.0.1:5353
+49
View File
@@ -0,0 +1,49 @@
#!/bin/bash
set -euo pipefail
# 用法: ./update.sh <version> <commit_hash>
# 示例: ./update.sh 1.5.2 abc123def456...
VERSION="$1"
COMMIT_HASH="$2"
MAKEFILE="Makefile"
if [[ -z "$VERSION" || -z "$COMMIT_HASH" ]]; then
echo "Usage: $0 <version> <commit_hash>"
exit 1
fi
# 计算新源文件的时间戳(取当前日期)
SOURCE_DATE=$(date +%Y-%m-%d)
echo "Updating ${MAKEFILE}: version=${VERSION}, commit=${COMMIT_HASH}, date=${SOURCE_DATE}"
# 备份原文件
cp "${MAKEFILE}" "${MAKEFILE}.bak"
# 更新 PKG_VERSION
sed -i "s/^PKG_VERSION:=.*/PKG_VERSION:=${VERSION}/" "${MAKEFILE}"
# 更新 PKG_SOURCE_VERSION
sed -i "s/^PKG_SOURCE_VERSION:=.*/PKG_SOURCE_VERSION:=${COMMIT_HASH}/" "${MAKEFILE}"
# 更新 PKG_SOURCE_DATE
sed -i "s/^PKG_SOURCE_DATE:=.*/PKG_SOURCE_DATE:=${SOURCE_DATE}/" "${MAKEFILE}"
# 更新 PKG_SOURCE(Subdir 依赖 PKG_VERSION)
sed -i "s/^PKG_SOURCE_SUBDIR:=.*/PKG_SOURCE_SUBDIR:=\$(PKG_NAME)-\$(PKG_VERSION)/" "${MAKEFILE}"
# 重置 PKG_RELEASE
sed -i "s/^PKG_RELEASE:=.*/PKG_RELEASE:=1/" "${MAKEFILE}"
# 清除旧的 PKG_MIRROR_HASH(首次构建时会自动生成正确的 hash)
sed -i "s/^PKG_MIRROR_HASH:=.*/PKG_MIRROR_HASH:=skip/" "${MAKEFILE}"
# 显示变更
echo "--- Diff ---"
diff "${MAKEFILE}.bak" "${MAKEFILE}" || true
# 清理备份
rm -f "${MAKEFILE}.bak"
echo "Update complete."