Repository: twoone-3/AdGuardHomeForMagisk
Branch: main
Commit: 6a961919a0cc
Files: 28
Total size: 99.5 KB
Directory structure:
gitextract_2fth1gt4/
├── .gitattributes
├── .github/
│ └── workflows/
│ └── pack.yml
├── .gitignore
├── LICENSE
├── README.md
├── README_en.md
├── changelog.md
├── docs/
│ └── index.md
├── pack.ps1
├── src/
│ ├── META-INF/
│ │ └── com/
│ │ └── google/
│ │ └── android/
│ │ ├── update-binary
│ │ └── updater-script
│ ├── action.sh
│ ├── bin/
│ │ ├── AdGuardHome.yaml
│ │ └── data/
│ │ └── filters/
│ │ └── 1732747955.txt
│ ├── customize.sh
│ ├── module.prop
│ ├── scripts/
│ │ ├── base.sh
│ │ ├── debug.sh
│ │ ├── inotify.sh
│ │ ├── iptables.sh
│ │ └── tool.sh
│ ├── service.sh
│ ├── settings.conf
│ ├── uninstall.sh
│ └── webroot/
│ ├── app.js
│ ├── index.html
│ └── style.css
└── version.json
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitattributes
================================================
* text eol=lf
*.ps1 text eol=crlf
*.bat text eol=crlf
================================================
FILE: .github/workflows/pack.yml
================================================
name: Pack AdGuardHomeForRoot
on:
workflow_dispatch:
push:
tags:
- "[0-9]*"
permissions:
contents: write
jobs:
package:
name: Build ${{ matrix.arch }} package
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
arch: [arm64, armv7]
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Resolve archive URL
id: vars
shell: bash
run: |
set -euo pipefail
case "${{ matrix.arch }}" in
arm64)
echo "archive_url=https://github.com/AdguardTeam/AdGuardHome/releases/latest/download/AdGuardHome_linux_arm64.tar.gz" >> "$GITHUB_OUTPUT"
;;
armv7)
echo "archive_url=https://github.com/AdguardTeam/AdGuardHome/releases/latest/download/AdGuardHome_linux_armv7.tar.gz" >> "$GITHUB_OUTPUT"
;;
*)
echo "Unsupported arch: ${{ matrix.arch }}" >&2
exit 1
;;
esac
- name: Download release archive
shell: bash
run: |
set -euo pipefail
mkdir -p cache
curl -fL "${{ steps.vars.outputs.archive_url }}" -o "cache/AdGuardHome_linux_${{ matrix.arch }}.tar.gz"
- name: Extract archive
shell: bash
run: |
set -euo pipefail
mkdir -p "cache/${{ matrix.arch }}"
tar -xzf "cache/AdGuardHome_linux_${{ matrix.arch }}.tar.gz" -C "cache/${{ matrix.arch }}"
- name: Stage package files
shell: bash
run: |
set -euo pipefail
rm -rf staging
mkdir -p staging
cp -a src/. staging/
cp "cache/${{ matrix.arch }}/AdGuardHome/AdGuardHome" "staging/bin/AdGuardHome"
- name: Create zip package
shell: bash
run: |
set -euo pipefail
rm -f "AdGuardHomeForRoot_${{ matrix.arch }}.zip"
(
cd staging
zip -r "../AdGuardHomeForRoot_${{ matrix.arch }}.zip" .
)
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: AdGuardHomeForRoot_${{ matrix.arch }}
path: AdGuardHomeForRoot_${{ matrix.arch }}.zip
if-no-files-found: error
release:
name: Create GitHub Release
runs-on: ubuntu-latest
needs: package
if: startsWith(github.ref, 'refs/tags/')
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Validate tag format
shell: bash
run: |
set -euo pipefail
if [[ ! "${GITHUB_REF_NAME}" =~ ^[0-9]{8}$ ]]; then
echo "Tag must be an 8-digit date (YYYYMMDD), got: ${GITHUB_REF_NAME}" >&2
exit 1
fi
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
pattern: AdGuardHomeForRoot_*
merge-multiple: true
- name: Create release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ github.ref_name }}
name: ${{ github.ref_name }}
body_path: changelog.md
files: |
AdGuardHomeForRoot_arm64.zip
AdGuardHomeForRoot_armv7.zip
================================================
FILE: .gitignore
================================================
cache/
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2025 twoone3
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# AdGuardHome for Root
[English](README_en.md) | 简体中文




[](docs/index.md)
[](https://t.me/+Q3Ur_HCYdM0xM2I1)
[](https://t.me/+Q3Ur_HCYdM0xM2I1)
关注我们的频道获取最新消息,或加入我们的群组进行讨论!
## 简介
- 本模块是一个在安卓设备上运行 [AdGuardHome](https://github.com/AdguardTeam/AdGuardHome) 的模块,提供了一个本地 DNS 服务器,能够屏蔽广告、恶意软件和跟踪器。
- 它可以作为一个本地广告拦截模块使用,也可以通过调整配置文件,转变为一个独立运行的 AdGuardHome 工具。
- 该模块支持 Magisk、KernelSU 和 APatch 等多种安装方式,适用于大多数 Android 设备。
- 该模块的设计初衷是为了提供一个轻量级的广告拦截解决方案,避免了使用 VPN 的复杂性和性能损失。
- 它可以与其他代理软件(如 [NekoBox](https://github.com/MatsuriDayo/NekoBoxForAndroid)、[FlClash](https://github.com/chen08209/FlClash)、[box for magisk](https://github.com/taamarin/box_for_magisk)、[akashaProxy](https://github.com/akashaProxy/akashaProxy) 等)共存,提供更好的隐私保护和网络安全。
## 特性
- 可选将本机 DNS 请求转发到本地 AdGuardHome 服务器
- 使用 [秋风广告规则](https://github.com/TG-Twilight/AWAvenue-Ads-Rule) 过滤广告,轻量,省电,少误杀
- 可从 <http://127.0.0.1:3000> 访问 AdGuardHome 控制面板,支持查询统计,修改 DNS 上游服务器以及自定义规则等功能
## 教程
1. 前往 [Release](https://github.com/twoone-3/AdGuardHomeForRoot/releases/latest) 页面下载模块
2. 检查 Android 设置 -> 网络和互联网 -> 高级 -> 私人 DNS,确保 `私人 DNS` 关闭
3. 在 root 管理器中安装模块,重启设备
4. 若看到模块运行成功的提示,则可以访问 <http://127.0.0.1:3000> 进入 AdGuardHome 后台,默认用户密码 root/root
5. 若需高级使用教程和常见问题解答,请访问 **[文档与教程](docs/index.md)**。
## 鸣谢
- [AWAwenue Ads Rule](https://github.com/TG-Twilight/AWAvenue-Ads-Rule)
- [AdguardHome_magisk](https://github.com/410154425/AdGuardHome_magisk)
- [akashaProxy](https://github.com/ModuleList/akashaProxy)
- [box_for_magisk](https://github.com/taamarin/box_for_magisk)
> 特别感谢赞助:
>
> - y******a - 200
> - 偶****** - 10
================================================
FILE: README_en.md
================================================
# AdGuardHome for Root
English | [简体中文](README.md)




[](docs/index.md)
[](https://t.me/+Q3Ur_HCYdM0xM2I1)
[](https://t.me/+Q3Ur_HCYdM0xM2I1)
Follow our channel for the latest news, or join our group for discussion!
## Introduction
- This module is a module that runs [AdGuardHome](https://github.com/AdguardTeam/AdGuardHome) on Android devices, providing a local DNS server that can block ads, malware, and trackers.
- It can be used as a local ad-blocking module or transformed into a standalone AdGuardHome tool by adjusting the configuration file.
- The module supports multiple installation methods, including Magisk, KernelSU, and APatch, making it compatible with most Android devices.
- The design of this module aims to provide a lightweight ad-blocking solution, avoiding the complexity and performance loss associated with using VPNs.
- It can coexist with other proxy software (such as [NekoBox](https://github.com/MatsuriDayo/NekoBoxForAndroid), [FlClash](https://github.com/chen08209/FlClash), [box for magisk](https://github.com/taamarin/box_for_magisk), [akashaProxy](https://github.com/akashaProxy/akashaProxy)), providing better privacy protection and network security.
## Features
- Optionally forward local DNS requests to the local AdGuardHome server
- Filter ads using [AWAvenue-Ads-Rule](https://github.com/TG-Twilight/AWAvenue-Ads-Rule) for lightweight, power-saving, and fewer false positives
- Access the AdGuardHome control panel from <http://127.0.0.1:3000>, supporting query statistics, modifying DNS upstream servers, and custom rules, etc.
## Tutorial
1. Go to the [Release](https://github.com/twoone-3/AdGuardHomeForRoot/releases/latest) page to download the module
2. Check Android Settings -> Network & Internet -> Advanced -> Private DNS, ensure `Private DNS` is turned off
3. Install the module in the root manager and reboot the device
4. If you see a successful module running prompt, you can access <http://127.0.0.1:3000> to enter the AdGuardHome backend, default username and password are root/root
5. For advanced usage tutorials and FAQs, please visit **[Docs & Tutorials](docs/index.md)**.
## Acknowledgments
- [AWAwenue Ads Rule](https://github.com/TG-Twilight/AWAvenue-Ads-Rule)
- [AdguardHome_magisk](https://github.com/410154425/AdGuardHome_magisk)
- [akashaProxy](https://github.com/ModuleList/akashaProxy)
- [box_for_magisk](https://github.com/taamarin/box_for_magisk)
> Special thanks to sponsors:
>
> - y******a - 200
> - 偶****** - 10
================================================
FILE: changelog.md
================================================
# Changelog
- 同步 AdGuard Home v0.107.74
- Sync with AdGuard Home v0.107.74
- 新增 WebUI 配置界面 #61 (wTNTw)
- New WebUI interface #61 (wTNTw)
================================================
FILE: docs/index.md
================================================
# 教程 (Tutorials)
> **For English Users:** This module is primarily designed for Chinese users. If you require the documentation in English, we kindly recommend using a reliable translation tool.
## 安装 (Installation)
本模块仅适用于已经 root 的安卓设备,支持 [Magisk](https://github.com/topjohnwu/Magisk) / [KernelSU](https://github.com/tiann/KernelSU) / [APatch](https://github.com/bmax121/APatch) 等 root 工具
在 Release 页面下载 zip 文件,提供了 arm64 和 armv7 两个版本。一般推荐使用 arm64 版,因为它在性能上更优,并且与大多数现代设备兼容。
---
## 配置 (Configuration)
模块默认的 AdGuardHome 后台地址为 `http://127.0.0.1:3000`,可以通过浏览器直接访问,默认账号和密码均为 `root`。
在 AdGuardHome 后台,你可以执行以下操作:
- 查看 DNS 查询统计信息
- 修改各种 DNS 配置
- 查看日志
- 添加自定义规则
如果你更倾向于使用app管理AdGuardHome,可以尝试使用 [AdGuard Home Manager](https://github.com/JGeek00/adguard-home-manager) 应用。
---
## 模块控制 (Module Control)
模块的状态会实时显示在`module.prop`文件中,在root管理器中可以看到模块的状态信息(如果没刷新请手动刷新)
模块实时监测`/data/adb/modules/AdGuardHome`目录下的`disable`文件,如果存在则禁用模块,不存在则启用模块
如果你想用其他方法来启停,你可以在文件管理器中手动创建和删除文件,也可以使用shell命令
```shell
touch /data/adb/modules/AdGuardHome/disable
```
```shell
rm /data/adb/modules/AdGuardHome/disable
```
本模块可以分为两部分,一部分是 AdGuardHome 本身,它在本地搭建了一个可自定义拦截功能的 DNS 服务器,另一部分是 iptables 转发规则,它负责将本机所有53端口出口流量重定向到 AdGuardHome
---
## 与代理软件共存 (Coexistence with Proxy Software)
代理软件主要分为两类:
**代理应用**:如 [NekoBox](https://github.com/MatsuriDayo/NekoBoxForAndroid)、[FlClash](https://github.com/chen08209/FlClash) 等。这些应用通常具有图形化界面,便于用户配置和管理代理规则。
以下是我自用的 FlClash 配置文件示例:
```yaml
proxy-providers:
provider1:
type: http
url: ""
interval: 86400
provider2:
type: http
url: ""
interval: 86400
proxy-groups:
- name: PROXY
type: select
include-all: true
rules:
proxy-groups:
- name: PROXY
type: select
include-all: true
rules:
- GEOSITE,private,DIRECT
- GEOSITE,googlefcm,DIRECT
- GEOSITE,bilibili,DIRECT
- GEOSITE,onedrive,PROXY
- GEOSITE,twitter,PROXY
- GEOSITE,youtube,PROXY
- GEOSITE,telegram,PROXY
- GEOSITE,google,PROXY
- GEOSITE,microsoft@cn,DIRECT
- GEOSITE,category-scholar-!cn,PROXY
- GEOSITE,steam@cn,DIRECT
- GEOSITE,category-games@cn,DIRECT
- GEOSITE,geolocation-!cn,PROXY
- GEOSITE,cn,DIRECT
- GEOIP,private,DIRECT,no-resolve
- GEOIP,google,DIRECT
- GEOIP,telegram,PROXY
- GEOIP,cn,DIRECT
- MATCH,DIRECT
```
没有写 DNS 部分是因为 FlClash 支持 DNS 覆写,在软件内就可配置 DNS 部分,将域名解析服务器改为 127.0.0.1:5591 即可使用本地的 adgh 作为DNS服务器
**代理模块**:如 [box_for_magisk](https://github.com/taamarin/box_for_magisk)、[akashaProxy](https://github.com/akashaProxy/akashaProxy) 等。这些模块通常运行在系统层级,适合需要更高权限或更深度集成的场景。
代理应用的 `分应用代理/访问控制` 功能非常实用。通过将国内应用设置为绕过模式,可以减少不必要的流量经过代理,同时这些绕过的应用仍然能够正常屏蔽广告。
如果使用代理模块,强烈建议禁用模块的 iptables 转发规则。禁用后,模块仅运行 AdGuardHome 本身。随后,将代理模块的上游 DNS 服务器配置为 `127.0.0.1:5591`,即可确保代理软件的所有 DNS 查询通过 AdGuardHome 进行广告屏蔽。
```yaml
dns:
# ...
default-nameserver:
- 223.5.5.5
nameserver:
- 127.0.0.1:5591
# ...
```
---
## 模块目录与配置文件 (Module Directory and Configuration Files)
模块的文件结构主要分为以下两个目录:
- **`/data/adb/agh`**:包含 AdGuardHome 的核心文件,包括二进制文件、工具脚本和配置文件。
- **`/data/adb/modules/AdGuardHome`**:存储模块的启动脚本和运行时数据文件。
模块的配置文件也分为两部分:
- **`/data/adb/agh/bin/AdGuardHome.yaml`**:AdGuardHome 的主配置文件。
- **`/data/adb/agh/settings.conf`**:模块的配置文件,具体说明请参考文件内的注释。
在更新模块时,用户可以选择是否保留原有的配置文件。如果选择不保留,系统会自动将原配置文件备份到 **`/data/adb/agh/backup`** 目录,以确保数据安全。
---
## 模块打包 (Module Packaging)
模块根目录下提供了一个名为 `pack.ps1` 的打包脚本,用户可以通过它快速生成模块的安装包。
在 Windows 系统上,打开 PowerShell 并执行以下命令:
```powershell
.\pack.ps1
```
运行脚本后,以下操作将自动完成:
1. 创建 `cache` 目录(如果尚未存在)。
2. 下载并缓存最新版本的 AdGuardHome(仅在 `cache` 目录中未找到缓存时执行下载)。
3. 将 AdGuardHome 与模块的其他文件打包成一个 ZIP 文件。
该脚本的设计确保了高效性:如果 `cache` 目录中已存在 AdGuardHome 的缓存版本,则无需重复下载,从而节省时间和带宽。
## 常见问题 (Frequently Asked Questions)
### **Q: 模块安装后无法正常运行怎么办?**
**A:**
- 检查 AdGuardHome 是否在运行:
使用以下命令查看进程状态:
```shell
ps | grep AdGuardHome
```
- 确保设备的 **私人 DNS** 功能已关闭:
前往 **设置 -> 网络和互联网 -> 高级 -> 私人 DNS**,并将其设置为关闭。
### **Q: 如何更改 AdGuardHome 的默认端口?**
**A:**
- 打开 **`/data/adb/agh/bin/AdGuardHome.yaml`** 文件。
- 修改 `bind_host` 的端口号为所需值。
- 保存文件后,重启模块以应用更改。
### **Q: 如何禁用模块的 iptables 转发规则?**
**A:**
- 编辑 **`/data/adb/agh/settings.conf`** 文件。
- 将 `ENABLE_IPTABLES` 参数设置为 `false`。
- 保存文件后,重启模块。
### **Q: 使用代理模块时,广告屏蔽无效怎么办?**
**A:**
- 确保代理模块的上游 DNS 服务器配置为 **`127.0.0.1:5591`**。
- 检查代理模块的配置文件,确保所有 DNS 查询通过 AdGuardHome。
### **Q: 模块是否会影响设备性能?**
**A:**
- 模块对性能的影响较小,但在低性能设备上可能会有轻微延迟。
- 推荐使用 **arm64** 版本以获得更好的性能。
### **Q:使用模块后,无法访问 Google 怎么办?**
**A:**
- 如果你用的是 FlClash,可尝试在`settings.conf`填入以下配置:
```ini
ignore_src_list="172.19.0.1"
```
此问题与节点质量有关,有的机场不改也没问题
================================================
FILE: pack.ps1
================================================
# 定义下载 URL 和路径变量
$CacheDir = "$PSScriptRoot\cache"
$UrlWitchCachePath = @{
"https://github.com/AdguardTeam/AdGuardHome/releases/latest/download/AdGuardHome_linux_arm64.tar.gz" = "$CacheDir\AdGuardHome_linux_arm64.tar.gz"
"https://github.com/AdguardTeam/AdGuardHome/releases/latest/download/AdGuardHome_linux_armv7.tar.gz" = "$CacheDir\AdGuardHome_linux_armv7.tar.gz"
}
# 创建缓存目录
if (-Not (Test-Path -Path $CacheDir)) {
Write-Host "Creating cache directory..."
New-Item -Path $CacheDir -ItemType Directory
}
# 下载文件,有缓存时不再下载
Write-Host "Downloading AdGuardHome..."
foreach ($url in $UrlWitchCachePath.Keys) {
$CachePath = $UrlWitchCachePath[$url]
if (-Not (Test-Path -Path $CachePath)) {
Write-Host "Downloading $url..."
Invoke-WebRequest -Uri $url -OutFile $CachePath
if ($?) {
Write-Host "Download completed successfully."
}
else {
Write-Host "Download failed. Exiting..."
exit 1
}
}
else {
Write-Host "File already exists in cache. Skipping download."
}
}
# 使用 tar 解压文件
Write-Host "Extracting AdGuardHome..."
foreach ($url in $UrlWitchCachePath.Keys) {
$CachePath = $UrlWitchCachePath[$url]
if ($CachePath -match 'AdGuardHome_linux_(arm64|armv7)\.tar\.gz$') {
$ExtractDir = "./cache/" + $matches[1]
}
else {
throw "Invalid file path: $CachePath"
}
if (-Not (Test-Path -Path $ExtractDir)) {
New-Item -Path $ExtractDir -ItemType Directory
Write-Host "Extracting $CachePath..."
tar -xzf $CachePath -C $ExtractDir
if ($?) {
Write-Host "Extraction completed successfully."
}
else {
Write-Host "Extraction failed"
exit 1
}
}
}
# 给项目打包,使用 7-Zip 压缩 zip
Write-Host "Packing AdGuardHome..."
$OutputPathArm64 = "$CacheDir\AdGuardHomeForRoot_arm64.zip"
$OutputPathArmv7 = "$CacheDir\AdGuardHomeForRoot_armv7.zip"
if (Test-Path -Path $OutputPathArm64) {
Remove-Item -Path $OutputPathArm64
}
if (Test-Path -Path $OutputPathArmv7) {
Remove-Item -Path $OutputPathArmv7
}
# 设置项目根目录
$ProjectRoot = "$PSScriptRoot\src"
$env:PATH += ";C:\Program Files\7-Zip"
# pack arm64
7z a -tzip $OutputPathArm64 "$ProjectRoot\*.sh"
7z a -tzip $OutputPathArm64 "$ProjectRoot\settings.conf"
7z a -tzip $OutputPathArm64 "$ProjectRoot\module.prop"
7z a -tzip $OutputPathArm64 "$ProjectRoot\META-INF"
7z a -tzip $OutputPathArm64 "$ProjectRoot\scripts"
7z a -tzip $OutputPathArm64 "$ProjectRoot\webroot"
7z a -tzip $OutputPathArm64 "$ProjectRoot\bin\"
7z a -tzip $OutputPathArm64 "$CacheDir\arm64\AdGuardHome\AdGuardHome"
7z rn $OutputPathArm64 "AdGuardHome" "bin/AdGuardHome"
# pack armv7
7z a -tzip $OutputPathArmv7 "$ProjectRoot\*.sh"
7z a -tzip $OutputPathArmv7 "$ProjectRoot\settings.conf"
7z a -tzip $OutputPathArmv7 "$ProjectRoot\module.prop"
7z a -tzip $OutputPathArmv7 "$ProjectRoot\META-INF"
7z a -tzip $OutputPathArmv7 "$ProjectRoot\scripts"
7z a -tzip $OutputPathArmv7 "$ProjectRoot\webroot"
7z a -tzip $OutputPathArmv7 "$ProjectRoot\bin\"
7z a -tzip $OutputPathArmv7 "$CacheDir\armv7\AdGuardHome\AdGuardHome"
7z rn $OutputPathArmv7 "AdGuardHome" "bin/AdGuardHome"
Write-Host "Packing completed successfully."
================================================
FILE: src/META-INF/com/google/android/update-binary
================================================
#!/sbin/sh
#################
# Initialization
#################
umask 022
# echo before loading util_functions
ui_print() { echo "$1"; }
require_new_magisk() {
ui_print "*******************************"
ui_print " 请升级安装 Magisk v20.4或以上! "
ui_print "*******************************"
exit 1
}
#########################
# Load util_functions.sh
#########################
OUTFD=$2
ZIPFILE=$3
mount /data 2>/dev/null
[ -f /data/adb/magisk/util_functions.sh ] || require_new_magisk
. /data/adb/magisk/util_functions.sh
[ $MAGISK_VER_CODE -lt 20400 ] && require_new_magisk
install_module
exit 0
================================================
FILE: src/META-INF/com/google/android/updater-script
================================================
#MAGISK
================================================
FILE: src/action.sh
================================================
. /data/adb/agh/settings.conf
$SCRIPT_DIR/tool.sh toggle
sleep 1
================================================
FILE: src/bin/AdGuardHome.yaml
================================================
http:
pprof:
port: 6060
enabled: false
address: 127.0.0.1:3000
session_ttl: 720h
users:
- name: root
password: $2b$12$D3zeMIBFfzcQTqGqB.k7GOkMvqx1jgsrdiRCn2kwOHl.kNmmPfMom
auth_attempts: 5
block_auth_min: 15
http_proxy: ""
language: ""
theme: auto
dns:
bind_hosts:
- 127.0.0.1
port: 5591
anonymize_client_ip: false
ratelimit: 0
ratelimit_subnet_len_ipv4: 24
ratelimit_subnet_len_ipv6: 56
ratelimit_whitelist: []
refuse_any: true
upstream_dns:
- https://doh.pub/dns-query
- https://dns.alidns.com/dns-query
upstream_dns_file: ""
bootstrap_dns:
- 119.29.29.29
- 223.5.5.5
- 223.6.6.6
- 1.1.1.1
- 8.8.8.8
fallback_dns:
- https://dns.google/dns-query
- https://cloudflare-dns.com/dns-query
upstream_mode: load_balance
fastest_timeout: 1s
allowed_clients: []
disallowed_clients: []
blocked_hosts:
- version.bind
- id.server
- hostname.bind
trusted_proxies:
- 127.0.0.0/8
- ::1/128
cache_enabled: true
cache_size: 67108864
cache_ttl_min: 0
cache_ttl_max: 0
cache_optimistic: true
cache_optimistic_answer_ttl: 30s
cache_optimistic_max_age: 12h
bogus_nxdomain: []
aaaa_disabled: false
enable_dnssec: false
edns_client_subnet:
custom_ip: ""
enabled: true
use_custom: false
max_goroutines: 300
handle_ddr: true
ipset: []
ipset_file: ""
bootstrap_prefer_ipv6: false
upstream_timeout: 10s
private_networks: []
use_private_ptr_resolvers: false
local_ptr_upstreams: []
use_dns64: false
dns64_prefixes: []
serve_http3: false
use_http3_upstreams: false
serve_plain_dns: true
hostsfile_enabled: true
pending_requests:
enabled: true
tls:
enabled: false
server_name: ""
force_https: false
port_https: 443
port_dns_over_tls: 853
port_dns_over_quic: 853
port_dnscrypt: 0
dnscrypt_config_file: ""
allow_unencrypted_doh: false
certificate_chain: ""
private_key: ""
certificate_path: ""
private_key_path: ""
strict_sni_check: false
querylog:
dir_path: ""
ignored: []
interval: 24h
size_memory: 1000
enabled: true
file_enabled: true
statistics:
dir_path: ""
ignored: []
interval: 24h
enabled: true
filters:
- enabled: true
url: https://raw.githubusercontent.com/TG-Twilight/AWAvenue-Ads-Rule/main/AWAvenue-Ads-Rule.txt
name: AWAvenue
id: 1732747955
whitelist_filters: []
user_rules:
- ""
dhcp:
enabled: false
interface_name: ""
local_domain_name: lan
dhcpv4:
gateway_ip: ""
subnet_mask: ""
range_start: ""
range_end: ""
lease_duration: 86400
icmp_timeout_msec: 1000
options: []
dhcpv6:
range_start: ""
lease_duration: 86400
ra_slaac_only: false
ra_allow_slaac: false
filtering:
blocking_ipv4: ""
blocking_ipv6: ""
blocked_services:
schedule:
time_zone: Asia/Shanghai
ids: []
protection_disabled_until: null
safe_search:
enabled: false
bing: true
duckduckgo: true
ecosia: true
google: true
pixabay: true
yandex: true
youtube: true
blocking_mode: default
parental_block_host: family-block.dns.adguard.com
safebrowsing_block_host: standard-block.dns.adguard.com
rewrites: []
safe_fs_patterns: []
safebrowsing_cache_size: 1048576
safesearch_cache_size: 1048576
parental_cache_size: 1048576
cache_time: 30
filters_update_interval: 72
blocked_response_ttl: 10
filtering_enabled: true
rewrites_enabled: true
parental_enabled: false
safebrowsing_enabled: false
protection_enabled: true
clients:
runtime_sources:
whois: true
arp: true
rdns: true
dhcp: true
hosts: true
persistent: []
log:
enabled: true
file: ""
max_backups: 0
max_size: 100
max_age: 3
compress: false
local_time: false
verbose: false
os:
group: ""
user: ""
rlimit_nofile: 0
schema_version: 32
================================================
FILE: src/bin/data/filters/1732747955.txt
================================================
||1500020991.vodplayer.wxamedia.com^
||1500022744.vodplayer.wxamedia.com^
||1500026601.vodplayer.wxamedia.com^
||8le8le.com^
||8rn1y79f02-1.algolianet.com^
||a0.app.xiaomi.com^
||aaid.umeng.com^
||abtest-ch.snssdk.com^
||ad-cdn.qingting.fm^
||ad-scope.com^
||ad-scope.com.cn^
||ad-sdk-config.youdao.com^
||ad-splash-tracking.hktvmall.com^
||ad-splash.hktvmall.com^
||ad.12306.cn^
||ad.51wnl.com^
||ad.api.3g.youku.com^
||ad.bwton.com^
||ad.cctv.com^
||ad.cyapi.cn^
||ad.doubleclick.net^
||ad.gameley.com^
||ad.hpplay.cn^
||ad.iot.360.cn^
||ad.jia.360.cn^
||ad.life.360.cn^
||ad.partner.gifshow.com^
||ad.qingting.fm^
||ad.qq.com^
||ad.richmob.cn^
||ad.shenshiads.com^
||ad.shunchangzhixing.com^
||ad.tencentmusic.com^
||ad.toutiao.com^
||ad.v3mh.com^
||ad.weibo.com^
||ad.winrar.com.cn^
||ad.ximalaya.com^
||ad.xunkids.com^
||ad.zijieapi.com^
||adapi.izuiyou.com^
||adapi.yynetwk.com^
||adashbc.ut.taobao.com^
||adashx.m.taobao.com^
||adashxgc.ut.taobao.com^
||adc.hpplay.cn^
||adcdn.hpplay.cn^
||adcdn.tencentmusic.com^
||adclick.g.doubleclick.net^
||adclick.tencentmusic.com^
||adcolony.com^
||addata.jd.com^
||adeng.hpplay.cn^
||adexp.chinaliftoff.io^
||adexp.liftoff.io^
||adexpo.tencentmusic.com^
||adfilter.imtt.qq.com^
||adguanggao.eee114.com^
||adimg.uve.weibo.com^
||adjust.cn^
||adjust.com^
||adks.hpplay.cn^
||adlink-api.huan.tv^
||adm.funshion.com^
||ads-api-o.api.leiniao.com^
||ads-api.tiktok.com^
||ads-api.twitter.com^
||ads-img-al.xhscdn.com^
||ads-img-qc.xhscdn.com^
||ads-jp.tiktok.com^
||ads-marketing-vivofs.vivo.com.cn^
||ads-sdk-api.ranfenghd.com^
||ads-sg.tiktok.com^
||ads-video-al.xhscdn.com^
||ads-video-qc.xhscdn.com^
||ads.95516.com^
||ads.auctions.yahoo.com^
||ads.cup.com.cn^
||ads.google.cn^
||ads.huan.tv^
||ads.huantest.com^
||ads.icloseli.cn^
||ads.inmobi.com^
||ads.linkedin.com^
||ads.pinterest.com^
||ads.pubmatic.com^
||ads.raidrive.com^
||ads.servebom.com^
||ads.service.kugou.com^
||ads.tiktok.com^
||ads.v3mh.com^
||ads.youtube.com^
||ads.zhinengxiyifang.cn^
||ads3-normal-hl.zijieapi.com^
||ads3-normal-lf.zijieapi.com^
||ads3-normal-lq.zijieapi.com^
||ads3-normal.zijieapi.com^
||ads5-normal-hl.zijieapi.com^
||ads5-normal-lf.zijieapi.com^
||ads5-normal-lq.zijieapi.com^
||ads5-normal.zijieapi.com^
||adsdk.vivo.com.cn^
||adse.test.ximalaya.com^
||adse.wsa.ximalaya.com^
||adse.ximalaya.com^
||adsebs.ximalaya.com^
||adsense.google.cn^
||adserver.unityads.unity3d.com^
||adservice.google.com^
||adservice.sigmob.cn^
||adserviceretry.kugou.com^
||adsfile.bssdlbig.kugou.com^
||adsfile.qq.com^
||adsfilebssdlbig.ali.kugou.com^
||adsfileretry.service.kugou.com^
||adsfs-sdkconfig.heytapimage.com^
||adsfs.oppomobile.com^
||adslvfile.qq.com^
||adsmart.konka.com^
||adsmind.gdtimg.com^
||adsmind.ugdtimg.com^
||adsp.xunlei.com^
||adstat.izuiyou.com^
||adstats.tencentmusic.com^
||adstore-1252524079.file.myqcloud.com^
||adstore-index-1252524079.file.myqcloud.com^
||adstrategy.biz.weibo.com^
||adstudio-assets.scdn.co^
||adstudio.spotify.com^
||adtago.s3.amazonaws.com^
||adtech.yahooinc.com^
||adtrack.quark.cn^
||adtracker.medproad.com^
||adui.tg.meitu.com^
||adv-api.shenshiads.com^
||adv.sec.intl.miui.com^
||adv.sec.miui.com^
||advertising-api-eu.amazon.com^
||advertising-api-fe.amazon.com^
||advertising-api.amazon.com^
||advertising.apple.com^
||advertising.yahoo.com^
||advertising.yandex.ru^
||advice-ads.s3.amazonaws.com^
||adview.cn^
||adx-ad.smart-tv.cn^
||adx-api.jinmo.tech^
||adx-bj-req.anythinktech.com^
||adx-bj.anythinktech.com^
||adx-cn.anythinktech.com^
||adx-drcn.op.dbankcloud.cn^
||adx-open-service.youku.com^
||adx-os.anythinktech.com^
||adx-saas.advlion.com^
||adx-strategy-api-cn.statisticslinks.com^
||adx.ads.heytapmobi.com^
||adx.ads.oppomobile.com^
||adx.appsdk.com.cn^
||adx.sogaha.cn^
||adx.tuia.cn^
||adxlog-adnet.vivo.com.cn^
||adxserver.ad.cmvideo.cn^
||aegis.qq.com^
||aem.dentsuchina.cn^
||afs.googlesyndication.com^
||agn.aty.sohu.com^
||aiseet.aa.atianqi.com^
||ali-ad.a.yximgs.com^
||ali-p2p-v2.pull.yximgs.com^
||alicoccdncnc-xn.inter.71edge.com^
||alicoccdnct-xn.inter.71edge.com^
||alog.umeng.com^
||alogus.umeng.com^
||als.baidu.com^
||amdcopen.m.taobao.com^
||an.facebook.com^
||analysis.chatglm.cn^
||analysis.yozocloud.cn^
||analytics-api.samsunghealthcn.com^
||analytics.adjust.cn^
||analytics.oceanengine.com^
||analytics.pinterest.com^
||analytics.pointdrive.linkedin.com^
||analytics.query.yahoo.com^
||analytics.rayjump.com^
||analytics.s3.amazonaws.com^
||analytics.tiktok.com^
||analytics.woozooo.com^
||analyticsengine.s3.amazonaws.com^
||analyze.lemurbrowser.com^
||andrqd.play.aiseet.atianqi.com^
||ap.dongdianqiu.com^
||ap.dongqiudi.com^
||apd-pcdnwxlogin.teg.tencent-cloud.net^
||apd-pcdnwxnat.teg.tencent-cloud.net^
||apd-pcdnwxstat.teg.tencent-cloud.net^
||api-access.pangolin-sdk-toutiao.com^
||api-access.pangolin-sdk-toutiao1.com^
||api-access.pangolin-sdk-toutiao2.com^
||api-access.pangolin-sdk-toutiao3.com^
||api-access.pangolin-sdk-toutiao4.com^
||api-access.pangolin-sdk-toutiao5.com^
||api-ad-data.kajicam.com^
||api-ad-product.huxiu.com^
||api-ad.kajicam.com^
||api-adservices.apple.com^
||api-gd.hiaiabc.com^
||api-htp.beizi.biz^
||api.ad.xiaomi.com^
||api.anythinktech.com^
||api.aqyad.com^
||api.e.kuaishou.com^
||api.fu.xcultur.com^
||api.htp.hubcloud.com.cn^
||api.hzsanjiaomao.com^
||api.installer.xiaomi.com^
||api.jietuhb.com^
||api.kingdata.ksyun.com^
||api.shanghailingye.cn^
||api.ssp.xcultur.com^
||api.statsig.com^
||api.touch-moblie.com^
||api.yfanads.com^
||api5-normal-quic-lf.ixigua.com^
||apiyd.my91app.com^
||app-measurement.com^
||appcfg.v.qq.com^
||applog-perf.uc.cn^
||applog.lc.quark.cn^
||applog.uc.cn^
||applog.zijieapi.com^
||applovin.com^
||aqyad.com^
||aspect-upush.umeng.com^
||auction.unityads.unity3d.com^
||audid-api.taobao.com^
||audid.umeng.com^
||b1-data.ads.heytapmobi.com^
||badjs.weixinbridge.com^
||baichuan-sdk.alicdn.com^
||baichuan-sdk.taobao.com^
||bdad.123pan.cn^
||bdapi-ads.realmemobile.com^
||bdapi-in-ads.realmemobile.com^
||bdapi.ads.oppomobile.com^
||bdcdncnc.inter.71edge.com^
||beacon-api.aliyuncs.com^
||beacon.qq.com^
||beaconcdn.qq.com^
||beacons.gvt2.com^
||beizi.biz^
||bes-mtj.baidu.com^
||bgg.baidu.com^
||bianxian.com^
||bingads.microsoft.com^
||biz.weibo.com^
||bj-td-menta-01-callback.advlion.com^
||bj.ad.track.66mobi.com^
||books-analytics-events.apple.com^
||bootpreload.uve.weibo.com^
||browser.events.data.msn.cn^
||browser.events.data.msn.com^
||browsercfg-drcn.cloud.dbankcloud.cn^
||bsrv.qq.com^
||business-api.tiktok.com^
||c.bidtoolads.com^
||c.etoolads.cn^
||c.evidon.com^
||c.gj.qq.com^
||c.kuaiduizuoye.com^
||c.sayhi.360.cn^
||c2.gdt.qq.com^
||canvas-cdn.gdt.qq.com^
||catalog.fjwhcbsh.com^
||cbjs.baidu.com^
||ccc-x.jd.com^
||ccs.umeng.com^
||cdn-ad.wtzw.com^
||cdn-ads.oss-cn-shanghai.aliyuncs.com^
||cdn-f.adsmoloco.com^
||cdn-plugin-sync-upgrade-juui.hismarttv.com^
||cdn.aiclk.com^
||cdn.chinaliftoff-creatives.io^
||cdn.hpplay.com.cn^
||cdn.iads.unitychina.cn^
||cdn.liftoff-creatives.io^
||cdn.ynuf.aliapp.org^
||cfg.imtt.qq.com^
||chat1.jd.com^
||chiq-cloud.com^
||ck.ads.oppomobile.com^
||click.chinaliftoff.io^
||click.googleanalytics.com^
||click.liftoff.io^
||click.oneplus.cn^
||click4.chinaliftoff.io^
||clog.miguvideo.com^
||cloooud.com^
||cn-api.anythinktech.com^
||cnlogs.umeng.com^
||cnlogs.umengcloud.com^
||cnzz.com^
||collect.kugou.com^
||commdata.v.qq.com^
||conf.hpplay.cn^
||config.chsmarttv.com^
||config.inmobi.com^
||config.unityads.unity3d.com^
||cpc-service-square.aiclk.com^
||cpro.baidustatic.com^
||crash2.zhihu.com^
||crashlyticsreports-pa.googleapis.com^
||csjplatform.com^
||d.applovin.com^
||d.applvn.com^
||da.anythinktech.com^
||data-sdk-uuid-log.d.meituan.net^
||data.ads.oppomobile.com^
||data.chsmarttv.com^
||data.mistat.india.xiaomi.com^
||data.mistat.rus.xiaomi.com^
||data.mistat.xiaomi.com^
||datacollection.uve.weibo.com^
||dc.sigmob.cn^
||de.tynt.com^
||diagnosis.ad.xiaomi.com^
||dig.bdurl.net^
||dlogs.bwton.com^
||dm.toutiao.com^
||domain.aishengji.com^
||doubleclick-cn.net^
||download.changhong.upgrade2.huan.tv^
||downloadxml.changhong.upgrade2.huan.tv^
||drcn-weather.cloud.huawei.com^
||dsp-x.jd.com^
||dsp.fcbox.com^
||dualstack-logs.amap.com^
||dxp.baidu.com^
||dyp2p-ali.douyucdn.cn^
||dyp2p-hw.douyucdn.cn^
||e.ad.xiaomi.com^
||eclick.baidu.com^
||edge.ads.twitch.tv^
||ef-dongfeng.tanx.com^
||engine.dcad01.com^
||entry.baidu.com^
||et-eus.w.inmobi.com^
||event.tradplusad.com^
||events-drcn.op.dbankcloud.cn^
||events.reddit.com^
||events.redditmedia.com^
||fancyapi.com^
||fclog.baidu.com^
||feed-image.baidu.com^
||file.daihuo.qq.com^
||firebaselogging-pa.googleapis.com^
||flurry.com^
||frontend-perf-service.e.kuaishou.com^
||fxgate.baidu.com^
||g-adnet.hiaiabc.com^
||g-staic.ganjingworld.com^
||g.dtv.cn.miaozhen.com^
||g.fancyapi.com^
||g2.ganjing.world^
||game.loveota.com^
||gdfp.gifshow.com^
||gemini.yahoo.com^
||geo.yahoo.com^
||getui.cn^
||ggx.cmvideo.cn^
||ggx01.miguvideo.com^
||ggx03.miguvideo.com^
||globalapi.ad.xiaomi.com^
||google-analytics.com^
||googleads.g.doubleclick-cn.net^
||googleads.g.doubleclick.net^
||googleadservices-cn.com^
||googleadservices.com^
||googletagservices-cn.com^
||gorgon.youdao.com^
||gromore.pangolin-sdk-toutiao.com^
||grs.dbankcloud.com^
||grs.hicloud.com^
||gslb.hpplay.cn^
||gvideo.qpic.cn^
||h-adashx.ut.taobao.com^
||h.trace.qq.com^
||h5.analytics.126.net^
||h5.hpplay.com.cn^
||hanlanad.com^
||hc-ssp.sm.cn^
||heads-ak.spotify.com.edgesuite.net^
||heads-fa.spotify.com^
||hexagon-analytics.com^
||hlog.bigda.com^
||hm.baidu.com^
||hmma.baidu.com^
||hotupgrade.hpplay.cn^
||houyi.kkmh.com^
||hpplay.cdn.cibn.cc^
||httpdns.bcelive.com^
||httpdns.ocloud.oppomobile.com^
||hugelog.fcbox.com^
||huichuan-mc.sm.cn^
||huichuan.sm.cn^
||hw-ot-ad.a.yximgs.com^
||hw-p2p-pull.video-voip.com^
||hw-p2p.pull.yximgs.com^
||hwpub-s01-drcn.cloud.dbankcloud.cn^
||hybrid.miniapp.taobao.com^
||hye.comp.360os.com^
||hyt.comp.360os.com^
||i.l-new.inmobicdn.net^
||i.l.inmobicdn.net^
||iad.apple.com^
||iad.g.163.com^
||iadctest.qwapi.com^
||iadmusicmat.music.126.net^
||iadsdk.apple.com^
||iadworkbench.apple.com^
||ifacelog.iqiyi.com^
||ifs.tanx.com^
||ii.gdt.qq.com^
||image-ad.sm.cn^
||image.hpplay.cn^
||images.outbrainimg.com^
||images.pinduoduo.com^
||imdns.hpplay.cn^
||img-c.heytapimage.com^
||img-x.jd.com^
||img.adnyg.com.w.kunlungr.com^
||img2.360buyimg.com^
||impression-asia.chinaliftoff.io^
||impression-asia.liftoff.io^
||impression.appsflyer.com^
||in.treasuredata.com^
||ios.bugly.qq.com^
||iot-eu-logser.realme.com^
||iot-logser.realme.com^
||ipv4.kkmh.com^
||irc.qubiankeji.com^
||ixav-cse.avlyun.com^
||iyfbodn.com^
||janapi.jd.com^
||jiguang.cn^
||jpush.cn^
||jpush.html5.qq.com^
||jpush.io^
||jswebcollects.kugou.com^
||kde.qq.com^
||kepler.jd.com^
||kl.67it.com^
||klink.volceapplog.com^
||knicks.jd.com^
||ks-p2p-v2.pull.yximgs.com^
||ks-p2p.pull.yximgs.com^
||ks.ferlytc.com^
||ks.pull.yximgs.com^
||l6.fancyapi.com^
||launcher.smart-tv.cn^
||launcherimg.smart-tv.cn^
||lf3-ad-union-sdk.pglstatp-toutiao.com^
||lf6-ad-union-sdk.pglstatp-toutiao.com^
||lftxali.fancyapi.com^
||lh3.googleadsserving.cn^
||litchiads.com^
||live-api.hktvmall.com^
||liveats-vod.video.ptqy.gitv.tv^
||livemonitor.huan.tv^
||livep.l.aiseet.atianqi.com^
||lives.l.aiseet.atianqi.com^
||lives.l.ott.video.qq.com^
||livewebbs2pcdn.msstatic.com^
||lm10111.jtrincc.cn^
||log-api-mn.huxiu.com^
||log-api.huxiu.com^
||log-api.pangolin-sdk-toutiao-b.com^
||log-api.pangolin-sdk-toutiao.com^
||log-report.com^
||log-sdk.gifshow.com^
||log-sdk.ksapisrv.com^
||log-upload-os.hoyoverse.com^
||log-upload.mihoyo.com^
||log-verify.dutils.com^
||log-verify.hiaiabc.com^
||log.ad.xiaomi.com^
||log.aispeech.com^
||log.amemv.com^
||log.appstore3.huan.tv^
||log.avlyun.com^
||log.byteoversea.com^
||log.fc.yahoo.com^
||log.iflytek.com^
||log.ireader.com^
||log.kuwo.cn^
||log.pinterest.com^
||log.popin.cc^
||log.stat.kugou.com^
||log.tagtic.cn^
||log.tbs.qq.com^
||log.vcgame.cn^
||log.web.kugou.com^
||log.zijieapi.com^
||log1.cmpassport.com^
||logbak.hicloud.com^
||logrcv.aiclk.com^
||logrcv.yunxish.com^
||logs.amap.com^
||logservice.hicloud.com^
||logservice1.hicloud.com^
||logtj.kugou.com^
||logupdate.avlyun.sec.miui.com^
||logwebs.kugou.com^
||luimg.baidu.com^
||m-adnet.hiaiabc.com^
||m.ad.zhangyue.com^
||m.atm.youku.com^
||m.shenshiads.com^
||mapi.m.jd.com^
||masdkv6.3g.qq.com^
||mazu.m.qq.com^
||mbdlog.iqiyi.com^
||mcs.zijieapi.com^
||medproad.com^
||metrics.data.hicloud.com^
||metrics.icloud.com^
||metrics.mzstatic.com^
||metrics2.data.hicloud.com^
||metrika.yandex.ru^
||mi.gdt.qq.com^
||miav-cse.avlyun.com^
||micro-xdb.com^
||mini-prog-drm.vodplayvideo.net^
||mission-pub.smart-tv.cn^
||miui-fxcse.avlyun.com^
||mlog.bigda.com^
||mnqlog.ldmnq.com^
||moatads.com^
||mobads-logs.baidu.com^
||mobads-pre-config.cdn.bcebos.com^
||mobads.baidu.com^
||mobaliyun.res.mgtv.com^
||mobile.da.mgtv.com^
||mobilead.kuwo.cn^
||mobilelog.kugou.com^
||mobilelog.upqzfile.com^
||monitor-ads-test.huan.tv^
||monitor-uu.play.aiseet.atianqi.com^
||monitor.adxsenmeng.com^
||monitor.music.qq.com^
||monitor.uu.qq.com^
||monsetting.toutiao.com^
||mores.toponad.com^
||ms.applovin.com^
||ms.applvn.com^
||ms4.applovin.com^
||ms4.applvn.com^
||msdk.voiceads.cn^
||mssdk.volces.com^
||mssdk.zijieapi.com^
||mtj.baidu.com^
||nadvideo2.baidu.com^
||newvoice.chiq5.smart-tv.cn^
||nex.163.com^
||nmetrics.samsung.com^
||notes-analytics-events.apple.com^
||notify.sec.miui.com^
||nsclick.baidu.com^
||o-sdk.mediation.unity3d.com^
||o2o.api.xiaomi.com^
||offerwall.yandex.net^
||ogads-pa.clients6.google.com^
||omgmta.play.aiseet.atianqi.com^
||open-set-api.shenshiads.com^
||open.e.kuaishou.cn^
||open.e.kuaishou.com^
||open.kuaishouzt.com^
||open.kwaizt.com^
||open.snssdk.com^
||openadapi.fancydsp.com^
||optimus-ads.amap.com^
||orbit.jd.com^
||oss.cdn.aiclk.com^
||oth.eve.mdt.qq.com^
||oth.str.mdt.qq.com^
||otheve.play.aiseet.atianqi.com^
||outlookads.live.com^
||p.l.qq.com^
||p.s.360.cn^
||p1-be-pack-sign.pglstatp-toutiao.com^
||p1-lm.adkwai.com^
||p2-be-pack-sign.pglstatp-toutiao.com^
||p2-lm.adkwai.com^
||p2pchunk-table.douyucdn.cn^
||p2plive-ali.douyucdn.cn^
||p2pupdate.gamedl.qq.com^
||p2pupgrade.gamedl.qq.com^
||p3-be-pack-sign.pglstatp-toutiao.com^
||p3-lm.adkwai.com^
||p3-tt.byteimg.com^
||p4-be-pack-sign.pglstatp-toutiao.com^
||p5-be-pack-sign.pglstatp-toutiao.com^
||p6-be-pack-sign.pglstatp-toutiao.com^
||p66-ad.adkwai.com^
||pagead2.googleadservices.com^
||pagead2.googlesyndication.com^
||pangolin-sdk-toutiao-b.com^
||pay.sboot.cn^
||pcdn.xmcdn.com^
||pcdn.yximgs.com^
||pcm-img.zhls.qq.com^
||pgdt.gtimg.cn^
||pgdt.ugdtimg.com^
||pglstatp-toutiao.com^
||pig.pupuapi.com^
||pin.hpplay.cn^
||pixon.ads-pixiv.net^
||pkoplink.com^
||pl.cp31.ott.cibntv.net^
||plbslog.umeng.com^
||pms.mb.qq.com^
||policy.video.ptqy.gitv.tv^
||pos.baidu.com^
||prod-mediate-events.applovin.com^
||promotion-partner.kuaishou.com^
||proxy.advp.apple.com^
||public.gdtimg.com^
||q.i.gdt.qq.com^
||qcwx.medproad.com^
||qmlog.baertt.com^
||qn-cdnfile1pcdn.msstatic.com^
||qqdata.ab.qq.com^
||qzs.gdtimg.com^
||recommend-drcn.hms.dbankcloud.cn^
||report.tv.kohesport.qq.com^
||res.hubcloud.com.cn^
||res1.applovin.com^
||res1.hubcloud.com.cn^
||res2.hubcloud.com.cn^
||res3.hubcloud.com.cn^
||resolve.umeng.com^
||review.gdtimg.com^
||rlog.popin.cc^
||rms-drcn.platform.dbankcloud.cn^
||roi.soulapp.cn^
||rp.hpplay.cn^
||rps.hpplay.cn^
||rpt.gdt.qq.com^
||rt.applovin.com^
||rt.applvn.com^
||rtb.adxsenmeng.com^
||rtb.voiceads.cn^
||s.amazon-adsystem.com^
||s1.cdn-sg.advlion.com^
||s8t.teads.tv^
||saad.ms.zhangyue.net^
||saas.hpplay.cn^
||safebrowsing.urlsec.gg.com^
||samsung-com.112.2o7.net^
||samsungads.com^
||saxysec.com^
||scs.openspeech.cn^
||sdk-ab-config.qquanquan.com^
||sdk-cache.video.ptqy.gitv.tv^
||sdk.1rtb.net^
||sdk.ad.smaato.net^
||sdk.adx.adwangmai.com^
||sdk.aqyad.com^
||sdk.beizi.biz^
||sdk.cferw.com^
||sdk.e.qq.com^
||sdk.hzsanjiaomao.com^
||sdk.markmedia.com.cn^
||sdk.mobads.adwangmai.com^
||sdkapi.cloooud.com^
||sdkapp.uve.weibo.com^
||sdkauth.hpplay.cn^
||sdkconf.avlyun.com^
||sdkconfig.ad.intl.xiaomi.com^
||sdkconfig.ad.xiaomi.com^
||sdkconfig.play.aiseet.atianqi.com^
||sdkconfig.video.qq.com^
||sdkoptedge.chinanetcenter.com^
||sdkreport.e.qq.com^
||sdktmp.hubcloud.com.cn^
||sdownload.stargame.com^
||search.ixigua.com^
||search3-search.ixigua.com^
||search5-search-hl.ixigua.com^
||search5-search.ixigua.com^
||securemetrics.apple.com^
||securepubads.g.doubleclick.net^
||sensors-log.dongqiudi.com^
||sentry.music.163.com^
||service.changhong.upgrade2.huan.tv^
||service.vmos.cn^
||sf16-static.i18n-pglstatp.com^
||sf3-fe-tos.pglstatp-toutiao.com^
||shouji.sogou.com^
||sigmob.com^
||skdisplay.jd.com^
||sl.hpplay.cn^
||slb-p2p.vcloud.ks-live.com^
||smad.ms.zhangyue.net^
||smartad.10010.com^
||smetrics.samsung.com^
||sms.ads.oppomobile.com^
||sngmta.qq.com^
||snowflake.qq.com^
||ssp.cloooud.com^
||staging-notify.sec.miui.com^
||stat.dongqiudi.com^
||stat.y.qq.com^
||static-s.iqiyi.com^
||static.ads-twitter.com^
||statichf.shihuocdn.cn^
||statics.woozooo.com^
||stats.wp.com^
||statsigapi.net^
||stg-data.ads.heytapmobi.com^
||stun.hitv.com^
||success.ctobsnssdk.com^
||supply.inmobicdn.net^
||sv-video.play.aiseet.atianqi.com^
||syh-imp.cdnjtzy.com^
||syh.zybang.com^
||szbdyd.com^
||t-adx.52qumao.com^
||t-dsp.pinduoduo.com^
||t.applovin.com^
||t.l.qq.com^
||t.teads.tv^
||t.track.ad.xiaomi.com^
||t002.ottcn.com^
||t1.a.market.xiaomi.com^
||t1.teads.tv^
||t2.a.market.xiaomi.com^
||t2.fancyapi.com^
||t3.a.market.xiaomi.com^
||t7z.cupid.ptqy.gitv.tv^
||tagtic.cn^
||tangram.e.qq.com^
||target.ads.jihuoniao.com^
||tdc.qq.com^
||tdsdk.cpatrk.net^
||tdsdk.xdrig.com^
||telecome.cn^
||telemetry.sdk.inmobi.com^
||tencent-dtv.m.cn.miaozhen.com^
||test.ad.xiaomi.com^
||test.e.ad.xiaomi.com^
||tj.b.qq.com^
||tj.video.qq.com^
||tk.anythinktech.com^
||tmead.y.qq.com^
||tmeadcomm.y.qq.com^
||tmfmazu-wangka.m.qq.com^
||tmfmazu.m.qq.com^
||tmfsdk.m.qq.com^
||tmfsdktcpv4.m.qq.com^
||tnc0-aliec2.zijieapi.com^
||tnc0-alisc1.zijieapi.com^
||tnc0-bjlgy.zijieapi.com^
||tnc3-aliec1.toutiaoapi.com^
||tnc3-aliec2.bytedance.com^
||tnc3-aliec2.snssdk.com^
||tnc3-aliec2.toutiaoapi.com^
||tnc3-aliec2.zijieapi.com^
||tnc3-alisc1.bytedance.com^
||tnc3-alisc1.snssdk.com^
||tnc3-alisc1.toutiaoapi.com^
||tnc3-alisc1.zijieapi.com^
||tnc3-alisc2.zijieapi.com^
||tnc3-bjlgy.bytedance.com^
||tnc3-bjlgy.snssdk.com^
||tnc3-bjlgy.toutiaoapi.com^
||tnc3-bjlgy.zijieapi.com^
||toblog.ctobsnssdk.com^
||tpa-hcdn.iqiyi.com^
||tpc.googlesyndication-cn.com^
||tpc.googlesyndication.com^
||tr-asia.adsmoloco.com^
||trace.aqyad.com^
||trace.qq.com^
||tracelog-debug.aiclk.com^
||tracelog-debug.qquanquan.com^
||tracelog-debug.yunxish.com^
||track.lc.quark.cn^
||track.uc.cn^
||tracker.ai.xiaomi.com^
||tracker.gitee.com^
||tracking.miui.com^
||tracking.rus.miui.com^
||tuiguang.meitu.com^
||tvapp.hpplay.cn^
||tvuser-ch.cedock.com^
||tx-ad.a.yximgs.com^
||tx-kmpaudio.pull.yximgs.com^
||tx-p2p-pull.live-voip.com^
||tx-p2p-v2.pull.yximgs.com^
||tytx.m.cn.miaozhen.com^
||tz.sec.xiaomi.com^
||uapi.ads.heytapmobi.com^
||udc.yahoo.com^
||udcm.yahoo.com^
||uedas.qidian.com^
||uhabo.com^
||ulog-sdk.gifshow.com^
||ulogjs.gifshow.com^
||ulogs.umeng.com^
||ulogs.umengcloud.com^
||umengacs.m.taobao.com^
||umengjmacs.m.taobao.com^
||umini.shujupie.com^
||union-mlog.bigda.com^
||union.baidu.cn^
||union.baidu.com^
||update.avlyun.sec.miui.com^
||update.lejiao.tv^
||update0.aiclk.com^
||upgrade-update.hismarttv.com^
||uranus.jd.com^
||us.l.qq.com^
||usr-api.aiclk.com^
||utoken.umeng.com^
||v.110route.cn^
||v.adintl.cn^
||v.adx.hubcloud.com.cn^
||v1-ad.video.yximgs.com^
||v2-ad.video.yximgs.com^
||v2-api-channel-launcher.hismarttv.com^
||v2.gdt.qq.com^
||v2mi.gdt.qq.com^
||v3-ad.video.yximgs.com^
||v3.gdt.qq.com^
||vd6.l.qq.com^
||vi.l.qq.com^
||video-ad.sm.cn^
||video-dsp.pddpic.com^
||video.dispatch.tc.qq.com^
||video.market.xiaomi.com^
||vipauth.hpplay.cn^
||vipgslb.hpplay.cn^
||virusinfo-cloudscan-cn.heytapmobi.com^
||vlive.qqvideo.tc.qq.com^
||volc.bj.ad.track.66mobi.com^
||vungle.com^
||w.l.qq.com^
||w1.askwai.com^
||w1.gskwai.com^
||w1.kskwai.com^
||watson.microsoft.com^
||watson.telemetry.microsoft.com^
||wcp.taobao.com^
||weather-analytics-events.apple.com^
||weather-community-drcn.weather.dbankcloud.cn^
||webstat.qiumibao.com^
||webview.unityads.unity3d.com^
||widgets.outbrain.com^
||widgets.pinterest.com^
||wildcard.moatads.com.edgekey.net^
||win.gdt.qq.com^
||wn.x.jd.com^
||ws-keyboard.shouji.sogou.com^
||ws.sj.qq.com^
||wv.inner-active.mobi^
||wwads.cn^
||wxa.wxs.qq.com^
||wxaintpcos.wxqcloud.qq.com.cn^
||wximg.wxs.qq.com^
||wxsmw.wxs.qq.com^
||wxsnsad.tc.qq.com^
||wxsnsdy.tc.qq.com^
||wxsnsdy.wxs.qq.com^
||wxsnsdythumb.wxs.qq.com^
||xc.gdt.qq.com^
||xcx.dcad01.com^
||xiaomi-dtv.m.cn.miaozhen.com^
||xlog.jd.com^
||xlviiirdr.com^
||xlviirdr.com^
||xycdn.com^
||yk-ssp-ad.cp31.ott.cibntv.net^
||yk-ssp.ad.youku.com^
||ykad-data.cp31.ott.cibntv.net^
||ykad-data.youku.com^
||ykad-gateway.youku.com^
||youku-acs.m.taobao.com^
||ytx-file.110route.cn^
||zeus.ad.xiaomi.com^
||zhihu-web-analytics.zhihu.com^
||zjres-ad.kajicam.com^
||zxid-m.mobileservice.cn^
||1rtb.com^
||a.market.xiaomi.com^
||ad.gameley.com^
||adintl.cn^
||adx.adwangmai.com^
||data.hicloud.com^
||log.aliyuncs.com^
||ubixioe.com^
||vlion.cn^
||yfanads.com^
||zhangyuyidong.cn^
*-ad.sm.cn*
*-ad.video.yximgs.com*
*-ad.wtzw.com*
*-be-pack-sign.pglstatp-toutiao.com*
*-normal.zijieapi.com*
================================================
FILE: src/customize.sh
================================================
SKIPUNZIP=1
# most of the users are Chinese, so set default language to Chinese
language="zh"
# try to get the system language
locale=$(getprop persist.sys.locale || getprop ro.product.locale || getprop persist.sys.language)
# if the system language is English, set language to English
if echo "$locale" | grep -qi "en"; then
language="en"
fi
function info() {
[ "$language" = "en" ] && ui_print "$1" || ui_print "$2"
}
function error() {
[ "$language" = "en" ] && abort "$1" || abort "$2"
}
info "- 🚀 Installing AdGuardHome for $ARCH" "- 🚀 开始安装 AdGuardHome for $ARCH"
AGH_DIR="/data/adb/agh"
BIN_DIR="$AGH_DIR/bin"
SCRIPT_DIR="$AGH_DIR/scripts"
PID_FILE="$AGH_DIR/bin/agh.pid"
info "- 📦 Extracting module basic files..." "- 📦 解压模块基本文件..."
unzip -o "$ZIPFILE" "action.sh" -d "$MODPATH" >/dev/null 2>&1
unzip -o "$ZIPFILE" "module.prop" -d "$MODPATH" >/dev/null 2>&1
unzip -o "$ZIPFILE" "service.sh" -d "$MODPATH" >/dev/null 2>&1
unzip -o "$ZIPFILE" "uninstall.sh" -d "$MODPATH" >/dev/null 2>&1
unzip -o "$ZIPFILE" "webroot/*" -d "$MODPATH" >/dev/null 2>&1
extract_keep_config() {
info "- 🌈 Keeping old configuration files..." "- 🌈 保留原来的配置文件..."
info "- 📜 Extracting script files..." "- 📜 正在解压脚本文件..."
unzip -o "$ZIPFILE" "scripts/*" -d $AGH_DIR >/dev/null 2>&1 || {
error "- ❌ Failed to extract scripts!" "- ❌ 解压脚本文件失败!"
}
info "- 🛠️ Extracting binary files except configuration..." "- 🛠️ 正在解压二进制文件(不包括配置文件)..."
unzip -o "$ZIPFILE" "bin/*" -x "bin/AdGuardHome.yaml" -d $AGH_DIR >/dev/null 2>&1 || {
error "- ❌ Failed to extract binary files!" "- ❌ 解压二进制文件失败!"
}
info "- 🚫 Skipping configuration file extraction..." "- 🚫 跳过解压配置文件..."
}
extract_no_config() {
info "- 💾 Backing up old configuration files with .bak extension..." "- 💾 使用 .bak 扩展名备份旧配置文件..."
[ -f "$AGH_DIR/settings.conf" ] && mv "$AGH_DIR/settings.conf" "$AGH_DIR/settings.conf.bak"
[ -f "$AGH_DIR/bin/AdGuardHome.yaml" ] && mv "$AGH_DIR/bin/AdGuardHome.yaml" "$AGH_DIR/bin/AdGuardHome.yaml.bak"
extract_all
}
extract_all() {
info "- 🌟 Extracting script files..." "- 🌟 正在解压脚本文件..."
unzip -o "$ZIPFILE" "scripts/*" -d $AGH_DIR >/dev/null 2>&1 || {
error "- ❌ Failed to extract scripts" "- ❌ 解压脚本文件失败"
}
info "- 🛠️ Extracting binary files..." "- 🛠️ 正在解压二进制文件..."
unzip -o "$ZIPFILE" "bin/*" -d $AGH_DIR >/dev/null 2>&1 || {
error "- ❌ Failed to extract binary files" "- ❌ 解压二进制文件失败"
}
info "- 📜 Extracting configuration files..." "- 📜 正在解压配置文件..."
unzip -o "$ZIPFILE" "settings.conf" -d $AGH_DIR >/dev/null 2>&1 || {
error "- ❌ Failed to extract configuration files" "- ❌ 解压配置文件失败"
}
}
if [ -d "$AGH_DIR" ]; then
info "- ⏹️ Found old version, stopping all AdGuardHome processes..." "- ⏹️ 发现旧版模块,正在停止所有 AdGuardHome 进程..."
pkill -f "AdGuardHome" || pkill -9 -f "AdGuardHome"
info "- 🔄 Do you want to keep the old configuration? (If not, it will be automatically backed up)" "- 🔄 是否保留原来的配置文件?(若不保留则自动备份)"
info "- 🔊 (Volume Up = Yes, Volume Down = No, 30s no input = Yes)" "- 🔊 (音量上键 = 是, 音量下键 = 否,30秒无操作 = 是)"
START_TIME=$(date +%s)
while true; do
NOW_TIME=$(date +%s)
timeout 1 getevent -lc 1 2>&1 | grep KEY_VOLUME >"$TMPDIR/events"
if [ $((NOW_TIME - START_TIME)) -gt 29 ]; then
info "- ⏰ No input detected after 30 seconds, defaulting to keep old configuration." "- ⏰ 30秒无输入,默认保留原配置。"
extract_keep_config
break
elif $(cat $TMPDIR/events | grep -q KEY_VOLUMEUP); then
extract_keep_config
break
elif $(cat $TMPDIR/events | grep -q KEY_VOLUMEDOWN); then
extract_no_config
break
fi
done
else
info "- 📦 First time installation, extracting files..." "- 📦 第一次安装,正在解压文件..."
mkdir -p "$AGH_DIR" "$BIN_DIR" "$SCRIPT_DIR"
extract_all
fi
info "- 🔐 Setting permissions..." "- 🔐 设置权限..."
chmod +x "$BIN_DIR/AdGuardHome"
chown root:net_raw "$BIN_DIR/AdGuardHome"
chmod +x "$SCRIPT_DIR"/*.sh "$MODPATH"/*.sh
info "- 🎉 Installation completed, please reboot." "- 🎉 安装完成,请重启设备。"
================================================
FILE: src/module.prop
================================================
id=AdGuardHome
name=AdGuardHome for Root
version=20260419
versionCode=49
author=twoone3
description=none
updateJson=https://raw.githubusercontent.com/twoone-3/AdGuardHomeForRoot/main/version.json
================================================
FILE: src/scripts/base.sh
================================================
# add busybox to PATH
[ -d "/data/adb/magisk" ] && export PATH="/data/adb/magisk:$PATH"
[ -d "/data/adb/ksu/bin" ] && export PATH="/data/adb/ksu/bin:$PATH"
[ -d "/data/adb/ap/bin" ] && export PATH="/data/adb/ap/bin:$PATH"
# most of the users are Chinese, so set default language to Chinese
language="zh"
# try to get the system language
locale=$(getprop persist.sys.locale || getprop ro.product.locale || getprop persist.sys.language)
# if the system language is English, set language to English
if echo "$locale" | grep -qi "en"; then
language="en"
fi
function log() {
local timestamp=$(date "+%Y-%m-%d %H:%M:%S")
local str
[ "$language" = "en" ] && str="$timestamp $1" || str="$timestamp $2"
echo "$str" | tee -a "$AGH_DIR/history.log"
}
function update_description() {
local description
[ "$language" = "en" ] && description="$1" || description="$2"
sed -i "/^description=/c\description=$description" "$MOD_PATH/module.prop"
}
================================================
FILE: src/scripts/debug.sh
================================================
#!/system/bin/sh
AGH_DIR="/data/adb/agh"
LOG="$AGH_DIR/debug.log"
{
echo "==== AdGuardHome Debug Log ===="
date
echo
echo "== System Info =="
uname -a
echo "Android Version: $(getprop ro.build.version.release)"
echo "Device: $(getprop ro.product.model)"
echo "Architecture: $(uname -m)"
echo
echo "== AdGuardHome Version =="
if [ -f "$AGH_DIR/bin/AdGuardHome" ]; then
"$AGH_DIR/bin/AdGuardHome" --version
else
echo "AdGuardHome binary not found"
fi
echo
echo "== Root Method =="
if [ -d "/data/adb/magisk" ]; then
echo "Magisk"
elif [ -d "/data/adb/ksu" ]; then
echo "KernelSU"
elif [ -d "/data/adb/ap" ]; then
echo "APatch"
else
echo "Unknown"
fi
echo
echo "== BusyBox Version =="
[ -d "/data/adb/magisk" ] && export PATH="/data/adb/magisk:$PATH"
[ -d "/data/adb/ksu/bin" ] && export PATH="/data/adb/ksu/bin:$PATH"
[ -d "/data/adb/ap/bin" ] && export PATH="/data/adb/ap/bin:$PATH"
if command -v busybox >/dev/null 2>&1; then
busybox --version
else
echo "BusyBox not found"
fi
echo "== AGH Directory Listing =="
ls -lR "$AGH_DIR"
echo
echo "== AGH Bin Log (last 30 lines) =="
tail -n 30 "$AGH_DIR/bin.log" 2>/dev/null
echo
echo "== AGH Settings =="
cat "$AGH_DIR/settings.conf" 2>/dev/null
echo
echo "== AGH PID File =="
cat "$AGH_DIR/bin/agh.pid" 2>/dev/null
echo
echo "== Running Processes (AdGuardHome) =="
ps -A | grep AdGuardHome
echo
echo "== iptables -t nat -L -n -v =="
iptables -t nat -L -n -v
echo
echo "== ip6tables -t filter -L -n -v =="
ip6tables -t filter -L -n -v
echo
echo "== Network Interfaces =="
ip addr
echo
} >"$LOG" 2>&1
echo "Debug info collected in $LOG"
================================================
FILE: src/scripts/inotify.sh
================================================
. /data/adb/agh/settings.conf
readonly EVENTS=$1
readonly MONITOR_DIR=$2
readonly MONITOR_FILE=$3
if [ "${MONITOR_FILE}" = "disable" ]; then
if [ "${EVENTS}" = "d" ]; then
$SCRIPT_DIR/tool.sh start
elif [ "${EVENTS}" = "n" ]; then
$SCRIPT_DIR/tool.sh stop
fi
fi
================================================
FILE: src/scripts/iptables.sh
================================================
. /data/adb/agh/settings.conf
. /data/adb/agh/scripts/base.sh
iptables_w="iptables -w 64"
ip6tables_w="ip6tables -w 64"
check_ipv6_nat_support() {
if $ip6tables_w -t nat -L >/dev/null 2>&1; then
return 0
else
return 1
fi
}
enable_iptables() {
if $iptables_w -t nat -L ADGUARD_REDIRECT_DNS >/dev/null 2>&1; then
log "ADGUARD_REDIRECT_DNS chain already exists" "ADGUARD_REDIRECT_DNS 链已经存在"
return 0
fi
log "Creating ADGUARD_REDIRECT_DNS chain and adding rules" "创建 ADGUARD_REDIRECT_DNS 链并添加规则"
$iptables_w -t nat -N ADGUARD_REDIRECT_DNS || return 1
$iptables_w -t nat -A ADGUARD_REDIRECT_DNS -m owner --uid-owner $adg_user --gid-owner $adg_group -j RETURN || return 1
for subnet in $ignore_dest_list; do
$iptables_w -t nat -A ADGUARD_REDIRECT_DNS -d $subnet -j RETURN || return 1
done
for subnet in $ignore_src_list; do
$iptables_w -t nat -A ADGUARD_REDIRECT_DNS -s $subnet -j RETURN || return 1
done
$iptables_w -t nat -A ADGUARD_REDIRECT_DNS -p udp --dport 53 -j REDIRECT --to-ports $redir_port || return 1
$iptables_w -t nat -A ADGUARD_REDIRECT_DNS -p tcp --dport 53 -j REDIRECT --to-ports $redir_port || return 1
$iptables_w -t nat -I OUTPUT -j ADGUARD_REDIRECT_DNS || return 1
log "Applied iptables rules successfully" "成功应用 iptables 规则"
}
disable_iptables() {
if ! $iptables_w -t nat -L ADGUARD_REDIRECT_DNS >/dev/null 2>&1; then
log "ADGUARD_REDIRECT_DNS chain does not exist" "ADGUARD_REDIRECT_DNS 链不存在"
return 0
fi
log "Deleting ADGUARD_REDIRECT_DNS chain and rules" "删除 ADGUARD_REDIRECT_DNS 链及规则"
$iptables_w -t nat -D OUTPUT -j ADGUARD_REDIRECT_DNS || return 1
$iptables_w -t nat -F ADGUARD_REDIRECT_DNS || return 1
$iptables_w -t nat -X ADGUARD_REDIRECT_DNS || return 1
}
add_block_ipv6_dns() {
if $ip6tables_w -t filter -L ADGUARD_BLOCK_DNS >/dev/null 2>&1; then
log "ADGUARD_BLOCK_DNS chain already exists" "ADGUARD_BLOCK_DNS 链已经存在"
return 0
fi
log "Creating ADGUARD_BLOCK_DNS chain and adding rules" "创建 ADGUARD_BLOCK_DNS 链并添加规则"
$ip6tables_w -t filter -N ADGUARD_BLOCK_DNS || return 1
$ip6tables_w -t filter -A ADGUARD_BLOCK_DNS -p udp --dport 53 -j DROP || return 1
$ip6tables_w -t filter -A ADGUARD_BLOCK_DNS -p tcp --dport 53 -j DROP || return 1
$ip6tables_w -t filter -I OUTPUT -j ADGUARD_BLOCK_DNS || return 1
log "Applied ipv6 iptables rules successfully" "成功应用 ipv6 iptables 规则"
}
del_block_ipv6_dns() {
if ! $ip6tables_w -t filter -L ADGUARD_BLOCK_DNS >/dev/null 2>&1; then
log "ADGUARD_BLOCK_DNS chain does not exist" "ADGUARD_BLOCK_DNS 链不存在"
return 0
fi
log "Deleting ADGUARD_BLOCK_DNS chain and rules" "删除 ADGUARD_BLOCK_DNS 链及规则"
$ip6tables_w -t filter -F ADGUARD_BLOCK_DNS || return 1
$ip6tables_w -t filter -D OUTPUT -j ADGUARD_BLOCK_DNS || return 1
$ip6tables_w -t filter -X ADGUARD_BLOCK_DNS || return 1
}
enable_ipv6_iptables() {
if ! check_ipv6_nat_support; then
log "IPv6 NAT is not supported, skipping IPv6 DNS hijack" "IPv6 NAT 不支持,跳过 IPv6 DNS 劫持"
return 0
fi
if $ip6tables_w -t nat -L ADGUARD_REDIRECT_DNS6 >/dev/null 2>&1; then
log "ADGUARD_REDIRECT_DNS6 chain already exists" "ADGUARD_REDIRECT_DNS6 链已经存在"
return 0
fi
log "Creating ADGUARD_REDIRECT_DNS6 chain and adding rules" "创建 ADGUARD_REDIRECT_DNS6 链并添加规则"
$ip6tables_w -t nat -N ADGUARD_REDIRECT_DNS6 || return 1
$ip6tables_w -t nat -A ADGUARD_REDIRECT_DNS6 -m owner --uid-owner $adg_user --gid-owner $adg_group -j RETURN || return 1
for subnet in $ignore_dest_list; do
$ip6tables_w -t nat -A ADGUARD_REDIRECT_DNS6 -d $subnet -j RETURN || return 1
done
for subnet in $ignore_src_list; do
$ip6tables_w -t nat -A ADGUARD_REDIRECT_DNS6 -s $subnet -j RETURN || return 1
done
$ip6tables_w -t nat -A ADGUARD_REDIRECT_DNS6 -p udp --dport 53 -j REDIRECT --to-ports $redir_port || return 1
$ip6tables_w -t nat -A ADGUARD_REDIRECT_DNS6 -p tcp --dport 53 -j REDIRECT --to-ports $redir_port || return 1
$ip6tables_w -t nat -I OUTPUT -j ADGUARD_REDIRECT_DNS6 || return 1
log "Applied ipv6 iptables rules successfully" "成功应用 ipv6 iptables 规则"
}
disable_ipv6_iptables() {
if ! check_ipv6_nat_support; then
log "IPv6 NAT is not supported, skipping IPv6 DNS hijack cleanup" "IPv6 NAT 不支持,跳过 IPv6 DNS 劫持清理"
return 0
fi
if ! $ip6tables_w -t nat -L ADGUARD_REDIRECT_DNS6 >/dev/null 2>&1; then
log "ADGUARD_REDIRECT_DNS6 chain does not exist" "ADGUARD_REDIRECT_DNS6 链不存在"
return 0
fi
log "Deleting ADGUARD_REDIRECT_DNS6 chain and rules" "删除 ADGUARD_REDIRECT_DNS6 链及规则"
$ip6tables_w -t nat -D OUTPUT -j ADGUARD_REDIRECT_DNS6 || return 1
$ip6tables_w -t nat -F ADGUARD_REDIRECT_DNS6 || return 1
$ip6tables_w -t nat -X ADGUARD_REDIRECT_DNS6 || return 1
}
case "$1" in
enable)
log "Enabling iptables and ipv6 DNS blocking if configured" "启用 iptables"
enable_iptables || exit 1
if [ "$block_ipv6_dns" = true ]; then
log "IPv6 DNS mode: block (DROP IPv6 DNS traffic)" "IPv6 DNS 模式: block (丢弃 IPv6 DNS 流量)"
add_block_ipv6_dns || exit 1
else
log "IPv6 DNS mode: hijack (NAT REDIRECT to AdGuard Home)" "IPv6 DNS 模式: hijack (劫持 IPv6 到 AdGuard Home)"
enable_ipv6_iptables || exit 1
fi
;;
disable)
log "Disabling iptables and ipv6 DNS blocking" "禁用 iptables 和 ipv6 DNS 阻断"
disable_iptables || exit 1
del_block_ipv6_dns || exit 1
disable_ipv6_iptables || exit 1
;;
*)
echo "Usage: $0 {enable|disable}"
exit 1
;;
esac
================================================
FILE: src/scripts/tool.sh
================================================
. /data/adb/agh/settings.conf
. /data/adb/agh/scripts/base.sh
start_adguardhome() {
# check if AdGuardHome is already running
if [ -f "$PID_FILE" ] && ps | grep -w "$adg_pid" | grep -q "AdGuardHome"; then
log "AdGuardHome is already running" "AdGuardHome 已经在运行"
exit 0
fi
# to fix https://github.com/AdguardTeam/AdGuardHome/issues/7002
export SSL_CERT_DIR="/system/etc/security/cacerts/"
# set timezone to Shanghai
export TZ="Asia/Shanghai"
# backup old log and overwrite new log
if [ -f "$AGH_DIR/bin.log" ]; then
mv "$AGH_DIR/bin.log" "$AGH_DIR/bin.log.bak"
fi
# run binary
busybox setuidgid "$adg_user:$adg_group" "$BIN_DIR/AdGuardHome" >"$AGH_DIR/bin.log" 2>&1 &
adg_pid=$!
# check if AdGuardHome started successfully
if ps | grep -w "$adg_pid" | grep -q "AdGuardHome"; then
echo "$adg_pid" >"$PID_FILE"
# check if iptables is enabled
if [ "$enable_iptables" = true ]; then
$SCRIPT_DIR/iptables.sh enable
log "🥰 started PID: $adg_pid iptables: enabled" "🥰 启动成功 PID: $adg_pid iptables 已启用"
update_description "🥰 Started PID: $adg_pid iptables: enabled" "🥰 启动成功 PID: $adg_pid iptables 已启用"
else
log "🥰 started PID: $adg_pid iptables: disabled" "🥰 启动成功 PID: $adg_pid iptables 已禁用"
update_description "🥰 Started PID: $adg_pid iptables: disabled" "🥰 启动成功 PID: $adg_pid iptables 已禁用"
fi
else
log "😭 Error occurred, check logs for details" "😭 出现错误,请检查日志以获取详细信息"
update_description "😭 Error occurred, check logs for details" "😭 出现错误,请检查日志以获取详细信息"
$SCRIPT_DIR/debug.sh
exit 1
fi
}
stop_adguardhome() {
if [ -f "$PID_FILE" ]; then
pid=$(cat "$PID_FILE")
kill $pid || kill -9 $pid
rm "$PID_FILE"
log "AdGuardHome stopped (PID: $pid)" "AdGuardHome 已停止 (PID: $pid)"
else
pkill -f "AdGuardHome" || pkill -9 -f "AdGuardHome"
log "AdGuardHome force stopped" "AdGuardHome 强制停止"
fi
update_description "❌ Stopped" "❌ 已停止"
$SCRIPT_DIR/iptables.sh disable
}
toggle_adguardhome() {
if [ -f "$PID_FILE" ] && ps | grep -w "$(cat $PID_FILE)" | grep -q "AdGuardHome"; then
stop_adguardhome
else
start_adguardhome
fi
}
case "$1" in
start)
start_adguardhome
;;
stop)
stop_adguardhome
;;
toggle)
toggle_adguardhome
;;
*)
echo "Usage: $0 {start|stop|toggle}"
exit 1
;;
esac
================================================
FILE: src/service.sh
================================================
until [ $(getprop init.svc.bootanim) = "stopped" ]; do
sleep 12
done
/data/adb/agh/scripts/tool.sh start
inotifyd /data/adb/agh/scripts/inotify.sh /data/adb/modules/AdGuardHome:d,n &
================================================
FILE: src/settings.conf
================================================
# 是否启用内置的 iptables 规则
# Whether to enable the built-in iptables rules
enable_iptables=true
# 阻断 ipv6 的 DNS 请求,仅在 enable_iptables=true 时生效
# Block DNS requests for ipv6, only effective when enable_iptables=true
block_ipv6_dns=true
# 重定向端口,请与 AdGuardHome 的设置保持一致
# Redirect port, please keep it consistent with AdGuardHome settings
redir_port=5591
# 用户组和用户,用于绕过 AdGuardHome 本身
# User group and user, used to bypass AdGuardHome itself
adg_user=root
adg_group=net_raw
# 绕过目的地址列表,用空格分隔
# list of destination addresses to bypass, separated by spaces
ignore_dest_list=""
# 绕过源地址列表,用空格分隔
# list of source addresses to bypass, separated by spaces
ignore_src_list=""
# 文件路径,请勿修改
# File paths, do not modify
readonly AGH_DIR="/data/adb/agh"
readonly BIN_DIR="$AGH_DIR/bin"
readonly SCRIPT_DIR="$AGH_DIR/scripts"
readonly PID_FILE="$AGH_DIR/bin/agh.pid"
readonly MOD_PATH="/data/adb/modules/AdGuardHome"
================================================
FILE: src/uninstall.sh
================================================
[ -d "/data/adb/agh" ] && rm -rf "/data/adb/agh"
================================================
FILE: src/webroot/app.js
================================================
/**
* AdGuardHome for Root - WebUI Application
*
* KernelSU API: ksu.exec(cmd) returns stdout string synchronously
* KernelSU API: ksu.toast(msg) shows a toast
*/
// ==================== Module API Layer ====================
var ModuleAPI = {
_api: null,
init: function() {
if (typeof ksu !== 'undefined' && typeof ksu.exec === 'function') {
this._api = ksu;
} else if (typeof ap !== 'undefined' && typeof ap.exec === 'function') {
this._api = ap;
}
},
isAvailable: function() {
return this._api !== null;
},
/**
* Execute shell command SYNCHRONOUSLY.
* ksu.exec(cmd) returns stdout string directly.
* Returns { errno, stdout }
*/
exec: function(command) {
if (!this._api) {
return { errno: -1, stdout: '' };
}
try {
var result = this._api.exec(command);
return {
errno: 0,
stdout: (result || '').replace(/\r/g, '')
};
} catch (e) {
return { errno: -1, stdout: '' };
}
},
toast: function(msg) {
if (this._api && typeof this._api.toast === 'function') {
try { this._api.toast(msg); } catch (e) {}
}
}
};
ModuleAPI.init();
// ==================== Utility Functions ====================
function showToast(message, type, duration) {
type = type || 'info';
duration = duration || 3000;
var container = document.getElementById('toastContainer');
var toastEl = document.createElement('div');
toastEl.className = 'toast ' + type;
toastEl.textContent = message;
container.appendChild(toastEl);
setTimeout(function() {
toastEl.style.animation = 'toastOut 0.3s ease forwards';
setTimeout(function() { toastEl.remove(); }, 300);
}, duration);
}
function showLoading(show) {
document.getElementById('loadingOverlay').style.display = show ? 'flex' : 'none';
}
// ==================== Paths ====================
var AGH_DIR = '/data/adb/agh';
var CONF_FILE = AGH_DIR + '/settings.conf';
var PID_FILE = AGH_DIR + '/bin/agh.pid';
var LOG_FILE = AGH_DIR + '/history.log';
var SCRIPT_DIR = AGH_DIR + '/scripts';
var BIN_DIR = AGH_DIR + '/bin';
var MODULE_PROP = '/data/adb/modules/AdGuardHome/module.prop';
// ==================== State ====================
var currentSettings = {};
var settingsChanged = false;
var isRunning = false;
var webPort = '3000';
var webUser = 'root';
var webPassword = 'root';
var statsFailCount = 0;
var statsAuthVisible = false;
// ==================== Tab / Page Switching ====================
var currentPage = 0;
var pageCount = 2;
function switchTab(index) {
if (index < 0 || index >= pageCount) return;
currentPage = index;
var container = document.getElementById('pagesContainer');
container.classList.remove('no-transition');
container.style.transform = 'translateX(' + (-index * 50) + '%)';
// Update floating tab active state
var tabs = document.querySelectorAll('.floating-tab-item');
for (var i = 0; i < tabs.length; i++) {
if (i === index) {
tabs[i].classList.add('active');
} else {
tabs[i].classList.remove('active');
}
}
// Always show floating tab when switching pages
var floatingTab = document.getElementById('floatingTab');
if (floatingTab) {
floatingTab.classList.remove('tab-hidden');
}
}
// ==================== Touch Swipe Support ====================
(function() {
var startX = 0;
var startY = 0;
var deltaX = 0;
var swiping = false;
var container = null;
function getContainer() {
if (!container) {
container = document.getElementById('pagesContainer');
}
return container;
}
function onTouchStart(e) {
var tag = e.target.tagName.toLowerCase();
if (tag === 'input' || tag === 'textarea' || tag === 'select' || tag === 'button') return;
startX = e.touches[0].clientX;
startY = e.touches[0].clientY;
deltaX = 0;
swiping = false;
}
function onTouchMove(e) {
if (!startX && !startY) return;
var dx = e.touches[0].clientX - startX;
var dy = e.touches[0].clientY - startY;
if (!swiping) {
if (Math.abs(dx) > Math.abs(dy) && Math.abs(dx) > 10) {
swiping = true;
getContainer().classList.add('no-transition');
} else {
return;
}
}
if (Math.abs(dx) > Math.abs(dy)) {
e.preventDefault();
}
deltaX = dx;
var pageWidth = window.innerWidth;
var baseOffset = -currentPage * pageWidth;
var offset = baseOffset + deltaX;
var minOffset = -(pageCount - 1) * pageWidth;
if (offset > 0) offset = offset * 0.3;
if (offset < minOffset) offset = minOffset + (offset - minOffset) * 0.3;
getContainer().style.transform = 'translateX(' + offset + 'px)';
}
function onTouchEnd(e) {
if (!swiping) return;
swiping = false;
var threshold = window.innerWidth * 0.2;
if (deltaX < -threshold && currentPage < pageCount - 1) {
switchTab(currentPage + 1);
} else if (deltaX > threshold && currentPage > 0) {
switchTab(currentPage - 1);
} else {
switchTab(currentPage);
}
deltaX = 0;
}
document.addEventListener('DOMContentLoaded', function() {
var c = getContainer();
if (c) {
c.addEventListener('touchstart', onTouchStart, { passive: true });
c.addEventListener('touchmove', onTouchMove, { passive: false });
c.addEventListener('touchend', onTouchEnd, { passive: true });
}
});
})();
// ==================== Status Check ====================
function checkStatus() {
var badge = document.getElementById('statusBadge');
var statusText = document.getElementById('statusText');
var pidBadge = document.getElementById('pidBadge');
var running = false;
var pid = '-';
var pidResult = ModuleAPI.exec('cat ' + PID_FILE + ' 2>/dev/null');
pid = pidResult.stdout.trim();
if (pid && /^\d+$/.test(pid)) {
running = true;
} else {
pid = '-';
}
isRunning = running;
badge.className = 'status-badge ' + (running ? 'running' : 'stopped');
statusText.textContent = running ? 'Running' : 'Stopped';
pidBadge.textContent = 'PID: ' + pid;
document.getElementById('btnStart').disabled = running;
document.getElementById('btnStop').disabled = !running;
document.getElementById('btnRestart').disabled = !running;
}
// ==================== Load Settings ====================
function getConf(key) {
var r = ModuleAPI.exec('grep "^' + key + '=" ' + CONF_FILE + ' 2>/dev/null | cut -d= -f2');
return (r.stdout || '').replace(/\r/g, '').trim();
}
function getConfQuoted(key) {
return getConf(key).replace(/^"|"$/g, '');
}
function loadSettings() {
var settings = {};
settings.enable_iptables = getConf('enable_iptables');
settings.block_ipv6_dns = getConf('block_ipv6_dns');
settings.redir_port = getConf('redir_port') || '5591';
settings.adg_user = getConf('adg_user') || 'root';
settings.adg_group = getConf('adg_group') || 'net_raw';
settings.ignore_dest_list = getConfQuoted('ignore_dest_list');
settings.ignore_src_list = getConfQuoted('ignore_src_list');
currentSettings = settings;
applySettingsToUI(settings);
}
function applySettingsToUI(settings) {
document.getElementById('setEnableIptables').checked = (settings.enable_iptables === 'true');
document.getElementById('setBlockIpv6').checked = (settings.block_ipv6_dns === 'true');
document.getElementById('setRedirPort').value = settings.redir_port || '5591';
document.getElementById('setAdgUser').value = settings.adg_user || 'root';
document.getElementById('setAdgGroup').value = settings.adg_group || 'net_raw';
document.getElementById('setIgnoreDest').value = settings.ignore_dest_list || '';
document.getElementById('setIgnoreSrc').value = settings.ignore_src_list || '';
settingsChanged = false;
document.getElementById('btnSaveSettings').disabled = true;
}
function onSettingChange() {
settingsChanged = true;
document.getElementById('btnSaveSettings').disabled = false;
}
// ==================== Save Settings ====================
function saveSettings() {
showLoading(true);
var enableIptables = document.getElementById('setEnableIptables').checked;
var blockIpv6 = document.getElementById('setBlockIpv6').checked;
var redirPort = document.getElementById('setRedirPort').value || '5591';
var adgUser = document.getElementById('setAdgUser').value || 'root';
var adgGroup = document.getElementById('setAdgGroup').value || 'net_raw';
var ignoreDest = document.getElementById('setIgnoreDest').value || '';
var ignoreSrc = document.getElementById('setIgnoreSrc').value || '';
var cmd =
"sed -i 's#^enable_iptables=.*#enable_iptables=" + enableIptables + "#' " + CONF_FILE +
" && sed -i 's#^block_ipv6_dns=.*#block_ipv6_dns=" + blockIpv6 + "#' " + CONF_FILE +
" && sed -i 's#^redir_port=.*#redir_port=" + redirPort + "#' " + CONF_FILE +
" && sed -i 's#^adg_user=.*#adg_user=" + adgUser + "#' " + CONF_FILE +
" && sed -i 's#^adg_group=.*#adg_group=" + adgGroup + "#' " + CONF_FILE +
" && sed -i 's#^ignore_dest_list=.*#ignore_dest_list=\"" + ignoreDest + "\"#' " + CONF_FILE +
" && sed -i 's#^ignore_src_list=.*#ignore_src_list=\"" + ignoreSrc + "\"#' " + CONF_FILE;
var result = ModuleAPI.exec(cmd);
if (result.errno === 0) {
settingsChanged = false;
document.getElementById('btnSaveSettings').disabled = true;
showToast('Settings saved successfully', 'success');
} else {
showToast('Failed to save settings', 'error');
}
showLoading(false);
}
// ==================== Log Viewer ====================
function loadLog() {
if (!ModuleAPI.isAvailable()) return;
var result = ModuleAPI.exec("awk '{a[NR]=$0}END{for(i=(NR>5?NR-4:1);i<=NR;i++)printf \"%s::NL::\",a[i]}' " + LOG_FILE + " 2>/dev/null");
var logViewer = document.getElementById('logViewer');
if (logViewer) {
var raw = (result.stdout || '').trim();
var lines = raw.split('::NL::').filter(function(l) { return l !== ''; });
var content = lines.join('\n');
logViewer.textContent = content || 'No log entries';
logViewer.scrollTop = logViewer.scrollHeight;
}
}
// ==================== Stats Help Modal ====================
function toggleStatsHelp() {
var overlay = document.getElementById('statsHelpOverlay');
if (overlay) {
overlay.classList.add('visible');
}
}
function closeStatsHelp() {
var overlay = document.getElementById('statsHelpOverlay');
if (overlay) {
overlay.classList.remove('visible');
}
}
// ==================== Stats View Switching ====================
function showStatsDataView() {
statsAuthVisible = false;
var dataView = document.getElementById('statsDataView');
var authView = document.getElementById('statsAuthView');
if (dataView) dataView.style.display = '';
if (authView) authView.style.display = 'none';
}
function showStatsAuthView() {
statsAuthVisible = true;
var dataView = document.getElementById('statsDataView');
var authView = document.getElementById('statsAuthView');
if (dataView) dataView.style.display = 'none';
if (authView) authView.style.display = '';
}
function submitCredentials() {
var userEl = document.getElementById('authUser');
var passEl = document.getElementById('authPassword');
if (userEl) webUser = userEl.value || 'root';
if (passEl) webPassword = passEl.value || 'root';
statsFailCount = 0;
showStatsDataView();
loadStats();
}
// ==================== Query Log Statistics ====================
function loadStats() {
if (!ModuleAPI.isAvailable()) return;
if (!isRunning) return;
if (webPort === '-') return;
if (statsAuthVisible) return;
var url = 'http://127.0.0.1:' + webPort + '/control/stats';
var auth = webUser + ':' + webPassword;
var cmd = 'wget -qO- --user=' + webUser + ' --password=' + webPassword + ' ' + url + ' 2>/dev/null || curl -s -u ' + auth + ' ' + url + ' 2>/dev/null';
var result = ModuleAPI.exec(cmd);
var raw = (result.stdout || '').trim();
var queriesEl = document.getElementById('statQueries');
var blockedEl = document.getElementById('statBlocked');
var timeEl = document.getElementById('statTime');
if (!raw || raw.charAt(0) !== '{') {
statsFailCount++;
if (queriesEl) queriesEl.textContent = '-';
if (blockedEl) blockedEl.textContent = '-';
if (timeEl) timeEl.textContent = '-';
if (statsFailCount >= 2) {
showStatsAuthView();
}
return;
}
try {
var stats = JSON.parse(raw);
if (queriesEl) queriesEl.textContent = (typeof stats.num_dns_queries === 'number') ? stats.num_dns_queries : '-';
if (blockedEl) blockedEl.textContent = (typeof stats.num_blocked_filtering === 'number') ? stats.num_blocked_filtering : '-';
if (timeEl) timeEl.textContent = (typeof stats.avg_processing_time === 'number') ? stats.avg_processing_time.toFixed(2) + 's' : '-';
statsFailCount = 0;
} catch (e) {
statsFailCount++;
if (queriesEl) queriesEl.textContent = '-';
if (blockedEl) blockedEl.textContent = '-';
if (timeEl) timeEl.textContent = '-';
if (statsFailCount >= 2) {
showStatsAuthView();
}
}
}
// ==================== Control ====================
function controlAdGuard(action) {
showLoading(true);
var cmd;
switch (action) {
case 'start':
cmd = SCRIPT_DIR + '/tool.sh start';
break;
case 'stop':
cmd = SCRIPT_DIR + '/tool.sh stop';
break;
case 'restart':
cmd = SCRIPT_DIR + '/tool.sh stop; sleep 1; ' + SCRIPT_DIR + '/tool.sh start';
break;
default:
showLoading(false);
return;
}
var result = ModuleAPI.exec(cmd);
if (result.errno === 0) {
var label = action.charAt(0).toUpperCase() + action.slice(1);
showToast(label + ' successful', 'success');
} else {
showToast(action + ' failed', 'error');
}
setTimeout(function() {
checkStatus();
loadLog();
showLoading(false);
}, 2000);
}
// ==================== Open AdGuardHome ====================
function openAdGuardHome() {
var url = (webPort !== '-') ? ('http://127.0.0.1:' + webPort) : 'http://127.0.0.1:3000';
window.open(url, '_blank');
}
// ==================== Debug Info ====================
function collectDebugInfo() {
showLoading(true);
ModuleAPI.exec(SCRIPT_DIR + '/debug.sh 2>/dev/null');
showToast('Debug info collected to ' + AGH_DIR + '/debug.log', 'success');
showLoading(false);
}
// ==================== Load Web Port ====================
function loadWebPort() {
var result = ModuleAPI.exec("grep 'address:' " + BIN_DIR + "/AdGuardHome.yaml 2>/dev/null | head -1");
var line = result.stdout.trim();
var match = line.match(/address:\s*\S+:(\d+)/);
if (match && match[1]) {
webPort = match[1];
} else {
webPort = '3000';
}
}
// ==================== Load Version ====================
function loadVersion() {
var result = ModuleAPI.exec('grep "^version=" ' + MODULE_PROP + ' 2>/dev/null | cut -d= -f2');
var version = result.stdout.trim();
if (version) {
document.getElementById('versionText').textContent = 'v' + version;
}
}
// ==================== Scroll Hide Floating Tab ====================
function setupScrollHideTab() {
var pages = document.querySelectorAll('.page');
var floatingTab = document.getElementById('floatingTab');
if (!floatingTab) return;
for (var i = 0; i < pages.length; i++) {
(function(page) {
page.addEventListener('scroll', function() {
var atTop = page.scrollTop <= 2;
var atBottom = page.scrollTop + page.clientHeight >= page.scrollHeight - 2;
if (atTop || atBottom) {
floatingTab.classList.remove('tab-hidden');
} else {
floatingTab.classList.add('tab-hidden');
}
});
})(pages[i]);
}
}
// ==================== Initialize ====================
function init() {
if (!ModuleAPI.isAvailable()) {
document.getElementById('noApiWarning').style.display = 'block';
document.getElementById('statusBadge').className = 'status-badge stopped';
document.getElementById('statusText').textContent = 'No API';
return;
}
loadSettings();
checkStatus();
loadWebPort();
loadVersion();
loadLog();
loadStats();
setupScrollHideTab();
switchTab(0);
}
// Auto-refresh every 5 seconds
setInterval(function() {
if (ModuleAPI.isAvailable()) {
checkStatus();
loadLog();
loadStats();
}
}, 5000);
// Start
document.addEventListener('DOMContentLoaded', init);
================================================
FILE: src/webroot/index.html
================================================
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>AdGuardHome for Root</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="app-wrapper">
<!-- Pages Container -->
<div class="pages-container" id="pagesContainer">
<!-- Page 1: Main -->
<div class="page page-main" id="pageMain">
<div class="container">
<!-- Header -->
<div class="header">
<div class="header-icon">
<svg viewBox="0 0 24 24"><path d="M12 1L3 5v6c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V5l-9-4zm0 10.99h7c-.53 4.12-3.28 7.79-7 8.94V12H5V6.3l7-3.11v8.8z"/></svg>
</div>
<h1>AdGuardHome for Root</h1>
<div class="version" id="versionText">v20260315</div>
<div class="status-row">
<span class="status-badge loading" id="statusBadge">
<span class="status-dot"></span>
<span id="statusText">Loading...</span>
</span>
<span class="pid-badge" id="pidBadge">PID: -</span>
</div>
</div>
<!-- No API Warning -->
<div class="no-api-warning" id="noApiWarning" style="display:none;">
<h3>⚠️ Module API Not Available</h3>
<p>This WebUI requires KernelSU or APatch module manager. Please open this page from the module manager app.</p>
</div>
<!-- Statistics -->
<div class="card card-stats">
<div class="card-title">
<svg class="card-title-icon" viewBox="0 0 24 24"><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM9 17H7v-7h2v7zm4 0h-2V7h2v10zm4 0h-2v-4h2v4z"/></svg>
Query Log Statistics
<button class="btn-help" onclick="toggleStatsHelp()">?</button>
</div>
<!-- Stats Data View -->
<div class="stats-grid" id="statsDataView">
<div class="stat-item">
<div class="stat-value" id="statQueries">-</div>
<div class="stat-label">DNS查询</div>
</div>
<div class="stat-item">
<div class="stat-value" id="statBlocked">-</div>
<div class="stat-label">规则拦截</div>
</div>
<div class="stat-item">
<div class="stat-value" id="statTime">-</div>
<div class="stat-label">处理耗时</div>
</div>
</div>
<!-- Credential Input View (hidden by default) -->
<div class="stats-auth-form" id="statsAuthView" style="display:none;">
<div class="stats-auth-hint">认证失败,请输入 AdGuardHome Web UI 账号密码</div>
<div class="input-group">
<label class="input-label">Username</label>
<input type="text" class="input-field" id="authUser" placeholder="root" value="root">
</div>
<div class="input-group">
<label class="input-label">Password</label>
<input type="password" class="input-field" id="authPassword" placeholder="root" value="root">
</div>
<button class="btn btn-primary btn-full" onclick="submitCredentials()" style="margin-top:8px;">确认</button>
</div>
</div>
<!-- Open Web Panel -->
<div class="card card-action">
<button class="btn btn-link btn-full" onclick="openAdGuardHome()">
AdGuardHome Web Panel
</button>
</div>
<!-- Control -->
<div class="card">
<div class="control-buttons">
<button class="btn btn-primary" id="btnStart" onclick="controlAdGuard('start')" disabled>Start</button>
<button class="btn btn-danger" id="btnStop" onclick="controlAdGuard('stop')" disabled>Stop</button>
<button class="btn btn-secondary" id="btnRestart" onclick="controlAdGuard('restart')" disabled>Restart</button>
<button class="btn btn-secondary" onclick="collectDebugInfo()">Debug Info</button>
</div>
<div class="log-viewer" id="logViewer"></div>
</div>
<!-- Footer -->
<div class="footer">
<p>AdGuardHome for Root by <a href="https://github.com/twoone-3">twoone3</a></p>
<p style="margin-top:4px;">MIT License · <a href="https://github.com/twoone-3/AdGuardHomeForRoot">GitHub</a></p>
</div>
</div>
</div>
<!-- Page 2: Settings -->
<div class="page page-settings" id="pageSettings">
<div class="container">
<!-- Settings Header -->
<div class="settings-header">
<div class="card-title">Settings</div>
</div>
<!-- Settings Content -->
<div class="card">
<div class="setting-group">
<div class="setting-row">
<div class="setting-label">
<div class="title">Enable Iptables</div>
<div class="desc">Redirect DNS requests via iptables</div>
</div>
<label class="toggle">
<input type="checkbox" id="setEnableIptables" onchange="onSettingChange()">
<span class="toggle-slider"></span>
</label>
</div>
<div class="setting-row">
<div class="setting-label">
<div class="title">Block IPv6 DNS</div>
<div class="desc">Block DNS requests over IPv6</div>
</div>
<label class="toggle">
<input type="checkbox" id="setBlockIpv6" onchange="onSettingChange()">
<span class="toggle-slider"></span>
</label>
</div>
</div>
<div class="setting-group">
<div class="input-group">
<label class="input-label">Redirect Port (must match AdGuardHome DNS port)</label>
<input type="number" class="input-field" id="setRedirPort" placeholder="5591" onchange="onSettingChange()">
</div>
<div class="input-group">
<label class="input-label">Run User</label>
<input type="text" class="input-field" id="setAdgUser" placeholder="root" onchange="onSettingChange()">
</div>
<div class="input-group">
<label class="input-label">Run User Group</label>
<input type="text" class="input-field" id="setAdgGroup" placeholder="net_raw" onchange="onSettingChange()">
</div>
<div class="input-group">
<label class="input-label">Bypass Destinations (space-separated)</label>
<input type="text" class="input-field" id="setIgnoreDest" placeholder="" onchange="onSettingChange()">
</div>
<div class="input-group">
<label class="input-label">Bypass Sources (space-separated)</label>
<input type="text" class="input-field" id="setIgnoreSrc" placeholder="" onchange="onSettingChange()">
</div>
</div>
<div class="setting-group" style="margin-bottom:0;">
<button class="btn btn-primary btn-full" id="btnSaveSettings" onclick="saveSettings()" disabled>
Save Settings
</button>
</div>
</div>
</div>
</div>
</div>
<!-- Floating Tab Bar -->
<div class="floating-tab" id="floatingTab">
<div class="floating-tab-item active" data-page="0" onclick="switchTab(0)">
<svg class="tab-icon" viewBox="0 0 24 24"><path d="M12 2L2 12h3v8h6v-6h2v6h6v-8h3L12 2zm0 2.84L19.16 12H18v8h-4v-6H10v6H6v-8H4.84L12 4.84z"/></svg>
<span class="tab-label">Main</span>
</div>
<div class="floating-tab-item" data-page="1" onclick="switchTab(1)">
<svg class="tab-icon" viewBox="0 0 24 24"><path d="M19.14,12.94c0.04-0.3,0.06-0.61,0.06-0.94c0-0.32-0.02-0.64-0.07-0.94l2.03-1.58c0.18-0.14,0.23-0.41,0.12-0.61 l-1.92-3.32c-0.12-0.22-0.37-0.29-0.59-0.22l-2.39,0.96c-0.5-0.38-1.03-0.7-1.62-0.94L14.4,2.81c-0.04-0.24-0.24-0.41-0.48-0.41 h-3.84c-0.24,0-0.43,0.17-0.47,0.41L9.25,5.35C8.66,5.59,8.12,5.92,7.63,6.29L5.24,5.33c-0.22-0.08-0.47,0-0.59,0.22L2.74,8.87 C2.62,9.08,2.66,9.34,2.86,9.48l2.03,1.58C4.84,11.36,4.8,11.69,4.8,12s0.02,0.64,0.07,0.94l-2.03,1.58 c-0.18,0.14-0.23,0.41-0.12,0.61l1.92,3.32c0.12,0.22,0.37,0.29,0.59,0.22l2.39-0.96c0.5,0.38,1.03,0.7,1.62,0.94l0.36,2.54 c0.05,0.24,0.24,0.41,0.48,0.41h3.84c0.24,0,0.44-0.17,0.47-0.41l0.36-2.54c0.59-0.24,1.13-0.56,1.62-0.94l2.39,0.96 c0.22,0.08,0.47,0,0.59-0.22l1.92-3.32c0.12-0.22,0.07-0.47-0.12-0.61L19.14,12.94z M12,15.6c-1.98,0-3.6-1.62-3.6-3.6 s1.62-3.6,3.6-3.6s3.6,1.62,3.6,3.6S13.98,15.6,12,15.6z"/></svg>
<span class="tab-label">Settings</span>
</div>
</div>
</div>
<!-- Stats Help Modal -->
<div class="help-modal-overlay" id="statsHelpOverlay" onclick="closeStatsHelp()">
<div class="help-modal" onclick="event.stopPropagation()">
<div class="help-modal-title">指标说明</div>
<div class="help-modal-item">
<strong>DNS查询</strong>
<p>AdGuard Home 核心处理的 DNS 查询总数。</p>
</div>
<div class="help-modal-item">
<strong>规则拦截</strong>
<p>被过滤规则拦截的 DNS 请求数量。</p>
</div>
<div class="help-modal-item">
<strong>处理耗时</strong>
<p>处理每个 DNS 请求的平均耗时(秒)。</p>
</div>
<div class="help-modal-note">数据通过 AdGuard Home OpenAPI 实时获取,统计周期由核心配置决定。</div>
<button class="btn btn-secondary btn-full" onclick="closeStatsHelp()" style="margin-top:12px;">关闭</button>
</div>
</div>
<!-- Toast Container -->
<div class="toast-container" id="toastContainer"></div>
<!-- Loading Overlay -->
<div class="loading-overlay" id="loadingOverlay" style="display:none;">
<div class="spinner"></div>
</div>
<script src="app.js"></script>
</body>
</html>
================================================
FILE: src/webroot/style.css
================================================
/* AdGuardHome for Root - WebUI Styles */
/* ===== Dark Theme (default) ===== */
:root {
--primary: #68BC71;
--primary-dark: #4CAF50;
--primary-light: #A5D6A7;
--bg: #0c0e14;
--surface: #161923;
--surface-2: #1e2231;
--surface-3: #2a2f42;
--text: #E8EAED;
--text-secondary: #9AA0A6;
--text-hint: #6B7280;
--danger: #EF5350;
--danger-dark: #C62828;
--warning: #FFC107;
--info: #42A5F5;
--success: #66BB6A;
--border: #252a3a;
--radius: 14px;
--radius-sm: 10px;
--shadow: 0 2px 12px rgba(0,0,0,0.35);
--transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
--log-bg: #0a0c12;
--toggle-off: #3a3f50;
}
/* ===== Light Theme ===== */
@media (prefers-color-scheme: light) {
:root {
--bg: #f0f2f5;
--surface: #ffffff;
--surface-2: #f5f6f8;
--surface-3: #e8eaed;
--text: #1f1f1f;
--text-secondary: #5f6368;
--text-hint: #80868b;
--border: #dfe1e5;
--shadow: 0 2px 12px rgba(0,0,0,0.08);
--log-bg: #f8f9fa;
--toggle-off: #bdbdbd;
}
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Noto Sans SC', 'PingFang SC', 'Microsoft YaHei', sans-serif;
background: var(--bg);
color: var(--text);
line-height: 1.6;
height: 100vh;
overflow: hidden;
-webkit-font-smoothing: antialiased;
}
/* ===== App Wrapper ===== */
.app-wrapper {
height: 100vh;
display: flex;
flex-direction: column;
overflow: hidden;
position: relative;
}
/* ===== Pages Container (horizontal sliding) ===== */
.pages-container {
flex: 1;
display: flex;
width: 200%;
overflow: hidden;
transition: transform 0.35s cubic-bezier(0.4, 0, 0.2, 1);
will-change: transform;
}
.pages-container.no-transition {
transition: none;
}
.page {
width: 50%;
height: 100%;
overflow-y: auto;
overflow-x: hidden;
-webkit-overflow-scrolling: touch;
touch-action: pan-y;
padding-bottom: 88px; /* space for floating tab */
}
.page::-webkit-scrollbar { width: 4px; }
.page::-webkit-scrollbar-track { background: transparent; }
.page::-webkit-scrollbar-thumb { background: var(--surface-3); border-radius: 4px; }
.container {
max-width: 600px;
margin: 0 auto;
padding: 16px;
}
/* ===== Header ===== */
.header {
text-align: center;
padding: 16px 0 12px;
}
.header-icon {
width: 48px;
height: 48px;
background: linear-gradient(145deg, var(--primary), var(--primary-dark));
border-radius: 14px;
display: inline-flex;
align-items: center;
justify-content: center;
margin-bottom: 8px;
box-shadow: 0 4px 16px rgba(104, 188, 113, 0.25);
}
.header-icon svg {
width: 28px;
height: 28px;
fill: white;
}
.header h1 {
font-size: 20px;
font-weight: 700;
color: var(--text);
margin-bottom: 2px;
letter-spacing: -0.3px;
}
.header .version {
font-size: 12px;
color: var(--text-hint);
}
/* ===== Status Row (badge + PID inline) ===== */
.status-row {
display: inline-flex;
align-items: center;
gap: 10px;
margin-top: 6px;
}
/* ===== Status Badge ===== */
.status-badge {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 4px 14px;
border-radius: 20px;
font-size: 13px;
font-weight: 600;
}
.status-badge.running {
background: rgba(102, 187, 106, 0.15);
color: var(--success);
}
.status-badge.stopped {
background: rgba(239, 83, 80, 0.15);
color: var(--danger);
}
.status-badge.loading {
background: rgba(66, 165, 245, 0.15);
color: var(--info);
}
.status-dot {
width: 8px;
height: 8px;
border-radius: 50%;
display: inline-block;
}
.status-badge.running .status-dot {
background: var(--success);
box-shadow: 0 0 8px var(--success);
animation: pulse 2s infinite;
}
.status-badge.stopped .status-dot {
background: var(--danger);
}
.status-badge.loading .status-dot {
background: var(--info);
animation: pulse 1s infinite;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.4; }
}
/* ===== PID Badge ===== */
.pid-badge {
display: inline-flex;
align-items: center;
padding: 4px 12px;
border-radius: 20px;
font-size: 12px;
font-weight: 600;
background: var(--surface-2);
color: var(--text-secondary);
border: 1px solid var(--border);
}
/* ===== Card ===== */
.card {
background: var(--surface);
border-radius: var(--radius);
padding: 20px;
margin-bottom: 14px;
border: 1px solid var(--border);
box-shadow: var(--shadow);
transition: box-shadow 0.25s ease;
}
.card.card-action {
padding: 10px 20px;
border-color: rgba(104, 188, 113, 0.15);
}
.card-title {
font-size: 15px;
font-weight: 600;
color: var(--text);
margin-bottom: 16px;
display: flex;
align-items: center;
gap: 8px;
}
.card-title .icon {
font-size: 18px;
}
.card-title-icon {
width: 18px;
height: 18px;
fill: var(--primary);
flex-shrink: 0;
}
/* ===== Statistics Card ===== */
.card-stats {
padding: 16px 20px;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 8px;
}
.stat-item {
text-align: center;
padding: 12px 4px;
background: var(--surface-2);
border-radius: var(--radius-sm);
border: 1px solid var(--border);
}
.stat-value {
font-size: 24px;
font-weight: 700;
color: var(--primary);
line-height: 1.2;
margin-bottom: 4px;
}
.stat-label {
font-size: 11px;
color: var(--text-hint);
font-weight: 500;
}
/* ===== Stats Auth Form ===== */
.stats-auth-form {
padding: 4px 0;
}
.stats-auth-hint {
font-size: 12px;
color: var(--warning);
margin-bottom: 10px;
text-align: center;
line-height: 1.5;
}
/* ===== Help Button ===== */
.btn-help {
width: 16px;
height: 16px;
border-radius: 50%;
border: 1px solid var(--text-hint);
background: transparent;
color: var(--text-hint);
font-size: 10px;
font-weight: 700;
cursor: pointer;
display: inline-flex;
align-items: center;
justify-content: center;
margin-left: auto;
flex-shrink: 0;
padding: 0;
line-height: 1;
transition: var(--transition);
font-family: inherit;
}
.btn-help:hover {
border-color: var(--primary);
color: var(--primary);
background: rgba(104, 188, 113, 0.08);
}
/* ===== Help Modal ===== */
.help-modal-overlay {
position: fixed;
top: 0; left: 0; right: 0; bottom: 0;
background: rgba(0, 0, 0, 0.5);
display: none;
align-items: center;
justify-content: center;
z-index: 500;
backdrop-filter: blur(4px);
-webkit-backdrop-filter: blur(4px);
padding: 24px;
}
.help-modal-overlay.visible {
display: flex;
}
.help-modal {
background: var(--surface);
border-radius: var(--radius);
padding: 20px;
max-width: 360px;
width: 100%;
border: 1px solid var(--border);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
animation: modalIn 0.2s ease;
}
@keyframes modalIn {
from { opacity: 0; transform: scale(0.95); }
to { opacity: 1; transform: scale(1); }
}
.help-modal-title {
font-size: 16px;
font-weight: 700;
color: var(--text);
margin-bottom: 14px;
text-align: center;
}
.help-modal-item {
padding: 8px 0;
}
.help-modal-item + .help-modal-item {
border-top: 1px solid var(--border);
}
.help-modal-item strong {
font-size: 13px;
color: var(--primary);
display: block;
margin-bottom: 2px;
}
.help-modal-item p {
font-size: 12px;
color: var(--text-secondary);
line-height: 1.5;
margin: 0;
}
.help-modal-note {
font-size: 11px;
color: var(--text-hint);
text-align: center;
margin-top: 12px;
padding-top: 10px;
border-top: 1px solid var(--border);
line-height: 1.5;
}
/* ===== Buttons ===== */
.control-buttons {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
}
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 6px;
padding: 11px 16px;
border: none;
border-radius: var(--radius-sm);
font-size: 13px;
font-weight: 600;
cursor: pointer;
transition: var(--transition);
outline: none;
position: relative;
overflow: hidden;
font-family: inherit;
letter-spacing: 0.2px;
}
.btn:active { transform: scale(0.97); }
.btn:disabled { opacity: 0.45; cursor: not-allowed; transform: none; }
.btn-primary {
background: linear-gradient(145deg, var(--primary), var(--primary-dark));
color: white;
}
.btn-primary:hover:not(:disabled) { box-shadow: 0 4px 16px rgba(104, 188, 113, 0.35); }
.btn-danger {
background: linear-gradient(145deg, var(--danger), var(--danger-dark));
color: white;
}
.btn-danger:hover:not(:disabled) { box-shadow: 0 4px 16px rgba(239, 83, 80, 0.35); }
.btn-secondary { background: var(--surface-2); color: var(--text); border: 1px solid var(--border); }
.btn-secondary:hover:not(:disabled) { background: var(--surface-3); border-color: var(--text-hint); }
.btn-full { width: 100%; padding: 13px; }
.btn-link {
background: linear-gradient(135deg, rgba(104, 188, 113, 0.12), rgba(104, 188, 113, 0.04));
color: var(--primary);
border: 1px solid rgba(104, 188, 113, 0.2);
font-weight: 600;
}
.btn-link:hover:not(:disabled) {
background: linear-gradient(135deg, rgba(104, 188, 113, 0.22), rgba(104, 188, 113, 0.08));
border-color: rgba(104, 188, 113, 0.4);
}
/* ===== Settings ===== */
.settings-header {
padding: 24px 0 8px;
}
.settings-header .card-title {
margin-bottom: 0;
}
.setting-group { margin-bottom: 12px; }
.setting-group:last-child { margin-bottom: 0; }
.setting-row {
display: flex;
align-items: center;
justify-content: space-between;
padding: 10px 0;
border-bottom: 1px solid var(--border);
}
.setting-row:last-child { border-bottom: none; padding-bottom: 0; }
.setting-row:first-child { padding-top: 0; }
.setting-label { flex: 1; }
.setting-label .title { font-size: 14px; font-weight: 500; color: var(--text); }
.setting-label .desc { font-size: 12px; color: var(--text-hint); margin-top: 2px; }
/* ===== Toggle Switch ===== */
.toggle {
position: relative;
width: 48px;
height: 26px;
flex-shrink: 0;
margin-left: 12px;
}
.toggle input { opacity: 0; width: 0; height: 0; }
.toggle-slider {
position: absolute;
cursor: pointer;
top: 0; left: 0; right: 0; bottom: 0;
background: var(--toggle-off);
border-radius: 26px;
transition: var(--transition);
}
.toggle-slider:before {
content: '';
position: absolute;
height: 20px;
width: 20px;
left: 3px;
bottom: 3px;
background: white;
border-radius: 50%;
transition: var(--transition);
}
.toggle input:checked + .toggle-slider { background: var(--primary); }
.toggle input:checked + .toggle-slider:before { transform: translateX(22px); }
.toggle input:disabled + .toggle-slider { opacity: 0.5; cursor: not-allowed; }
/* ===== Input ===== */
.input-field {
width: 100%;
padding: 10px 14px;
background: var(--surface-2);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
color: var(--text);
font-size: 14px;
font-family: inherit;
transition: var(--transition);
outline: none;
}
.input-field:focus {
border-color: var(--primary);
box-shadow: 0 0 0 3px rgba(104, 188, 113, 0.15);
}
.input-field:disabled { opacity: 0.5; }
.input-group { margin-top: 8px; }
.input-label {
font-size: 12px;
color: var(--text-hint);
margin-bottom: 6px;
display: block;
}
/* ===== Log Viewer ===== */
.log-viewer {
background: var(--log-bg);
border-radius: var(--radius-sm);
padding: 10px 12px;
font-family: 'JetBrains Mono', 'Fira Code', 'Cascadia Code', 'Consolas', monospace;
font-size: 10px;
line-height: 1.7;
max-height: 160px;
min-height: 80px;
overflow-y: auto;
color: var(--text-hint);
white-space: pre-wrap;
word-break: break-all;
border: 1px solid var(--border);
border-top: 2px solid var(--surface-3);
margin-top: 12px;
}
.log-viewer::-webkit-scrollbar { width: 3px; }
.log-viewer::-webkit-scrollbar-track { background: transparent; }
.log-viewer::-webkit-scrollbar-thumb { background: var(--surface-3); border-radius: 3px; }
/* ===== Toast ===== */
.toast-container {
position: fixed;
bottom: 80px;
left: 50%;
transform: translateX(-50%);
z-index: 1000;
display: flex;
flex-direction: column;
gap: 8px;
pointer-events: none;
}
.toast {
padding: 12px 20px;
border-radius: var(--radius-sm);
font-size: 14px;
font-weight: 500;
color: white;
box-shadow: 0 4px 16px rgba(0,0,0,0.4);
animation: toastIn 0.3s ease;
white-space: nowrap;
pointer-events: auto;
}
.toast.success { background: var(--success); }
.toast.error { background: var(--danger); }
.toast.info { background: var(--info); }
.toast.warning { background: #E65100; }
@keyframes toastIn {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes toastOut {
from { opacity: 1; transform: translateY(0); }
to { opacity: 0; transform: translateY(-20px); }
}
/* ===== Loading Overlay ===== */
.loading-overlay {
position: fixed;
top: 0; left: 0; right: 0; bottom: 0;
background: rgba(128, 128, 128, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 999;
backdrop-filter: blur(4px);
}
.spinner {
width: 40px;
height: 40px;
border: 3px solid var(--surface-3);
border-top-color: var(--primary);
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
/* ===== No API Warning ===== */
.no-api-warning {
background: rgba(239, 83, 80, 0.1);
border: 1px solid rgba(239, 83, 80, 0.3);
border-radius: var(--radius);
padding: 20px;
text-align: center;
margin-bottom: 12px;
}
.no-api-warning h3 {
color: var(--danger);
margin-bottom: 8px;
font-size: 16px;
}
.no-api-warning p {
color: var(--text-secondary);
font-size: 13px;
line-height: 1.6;
}
/* ===== Footer ===== */
.footer {
text-align: center;
padding: 16px 0 24px;
color: var(--text-hint);
font-size: 12px;
}
.footer a {
color: var(--primary);
text-decoration: none;
}
.footer a:hover { text-decoration: underline; }
/* ===== Floating Tab Bar ===== */
.floating-tab {
position: fixed;
bottom: 24px;
left: 50%;
transform: translateX(-50%);
display: flex;
align-items: center;
gap: 4px;
padding: 5px 6px;
background: var(--surface);
border: 1px solid var(--border);
border-radius: 28px;
box-shadow: 0 4px 24px rgba(0,0,0,0.45);
z-index: 100;
-webkit-tap-highlight-color: transparent;
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.3s ease;
}
.floating-tab.tab-hidden {
transform: translateX(-50%) translateY(80px);
opacity: 0;
pointer-events: none;
}
.floating-tab-item {
display: flex;
align-items: center;
justify-content: center;
gap: 6px;
padding: 8px 22px;
border-radius: 22px;
cursor: pointer;
transition: all 0.25s ease;
color: var(--text-hint);
user-select: none;
font-size: 12px;
font-weight: 600;
}
.floating-tab-item.active {
background: linear-gradient(145deg, var(--primary), var(--primary-dark));
color: white;
box-shadow: 0 2px 10px rgba(104, 188, 113, 0.35);
}
.tab-icon {
width: 18px;
height: 18px;
fill: currentColor;
}
.tab-label {
font-size: 12px;
font-weight: 600;
}
/* ===== Responsive ===== */
@media (max-width: 400px) {
.container { padding: 12px; }
.card { padding: 16px; }
.control-buttons { grid-template-columns: 1fr 1fr; }
}
================================================
FILE: version.json
================================================
{
"versionCode": 49,
"version": "20260419",
"zipUrl": "https://github.com/twoone-3/AdGuardHomeForRoot/releases/latest/download/AdGuardHomeForRoot_arm64.zip",
"changelog": "https://raw.githubusercontent.com/twoone-3/AdGuardHomeForRoot/main/changelog.md"
}
gitextract_2fth1gt4/ ├── .gitattributes ├── .github/ │ └── workflows/ │ └── pack.yml ├── .gitignore ├── LICENSE ├── README.md ├── README_en.md ├── changelog.md ├── docs/ │ └── index.md ├── pack.ps1 ├── src/ │ ├── META-INF/ │ │ └── com/ │ │ └── google/ │ │ └── android/ │ │ ├── update-binary │ │ └── updater-script │ ├── action.sh │ ├── bin/ │ │ ├── AdGuardHome.yaml │ │ └── data/ │ │ └── filters/ │ │ └── 1732747955.txt │ ├── customize.sh │ ├── module.prop │ ├── scripts/ │ │ ├── base.sh │ │ ├── debug.sh │ │ ├── inotify.sh │ │ ├── iptables.sh │ │ └── tool.sh │ ├── service.sh │ ├── settings.conf │ ├── uninstall.sh │ └── webroot/ │ ├── app.js │ ├── index.html │ └── style.css └── version.json
SYMBOL INDEX (28 symbols across 1 files)
FILE: src/webroot/app.js
function showToast (line 54) | function showToast(message, type, duration) {
function showLoading (line 68) | function showLoading(show) {
function switchTab (line 95) | function switchTab(index) {
function getContainer (line 128) | function getContainer() {
function onTouchStart (line 135) | function onTouchStart(e) {
function onTouchMove (line 145) | function onTouchMove(e) {
function onTouchEnd (line 177) | function onTouchEnd(e) {
function checkStatus (line 205) | function checkStatus() {
function getConf (line 234) | function getConf(key) {
function getConfQuoted (line 239) | function getConfQuoted(key) {
function loadSettings (line 243) | function loadSettings() {
function applySettingsToUI (line 257) | function applySettingsToUI(settings) {
function onSettingChange (line 270) | function onSettingChange() {
function saveSettings (line 276) | function saveSettings() {
function loadLog (line 310) | function loadLog() {
function toggleStatsHelp (line 324) | function toggleStatsHelp() {
function closeStatsHelp (line 331) | function closeStatsHelp() {
function showStatsDataView (line 339) | function showStatsDataView() {
function showStatsAuthView (line 347) | function showStatsAuthView() {
function submitCredentials (line 355) | function submitCredentials() {
function loadStats (line 366) | function loadStats() {
function controlAdGuard (line 413) | function controlAdGuard(action) {
function openAdGuardHome (line 449) | function openAdGuardHome() {
function collectDebugInfo (line 455) | function collectDebugInfo() {
function loadWebPort (line 463) | function loadWebPort() {
function loadVersion (line 475) | function loadVersion() {
function setupScrollHideTab (line 484) | function setupScrollHideTab() {
function init (line 506) | function init() {
Condensed preview — 28 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (113K chars).
[
{
"path": ".gitattributes",
"chars": 54,
"preview": "* text eol=lf\n*.ps1 text eol=crlf\n*.bat text eol=crlf\n"
},
{
"path": ".github/workflows/pack.yml",
"chars": 3258,
"preview": "name: Pack AdGuardHomeForRoot\n\non:\n workflow_dispatch:\n push:\n tags:\n - \"[0-9]*\"\n\npermissions:\n contents: wri"
},
{
"path": ".gitignore",
"chars": 7,
"preview": "cache/\n"
},
{
"path": "LICENSE",
"chars": 1064,
"preview": "MIT License\n\nCopyright (c) 2025 twoone3\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof"
},
{
"path": "README.md",
"chars": 2329,
"preview": "# AdGuardHome for Root\n\n[English](README_en.md) | 简体中文\n\n\n\n\n- New WebUI inte"
},
{
"path": "docs/index.md",
"chars": 4586,
"preview": "# 教程 (Tutorials)\n\n> **For English Users:** This module is primarily designed for Chinese users. If you require the docum"
},
{
"path": "pack.ps1",
"chars": 3221,
"preview": "# 定义下载 URL 和路径变量\r\n$CacheDir = \"$PSScriptRoot\\cache\"\r\n$UrlWitchCachePath = @{\r\n \"https://github.com/AdguardTeam/AdGuardH"
},
{
"path": "src/META-INF/com/google/android/update-binary",
"chars": 604,
"preview": "#!/sbin/sh\n\n#################\n# Initialization\n#################\n\numask 022\n\n# echo before loading util_functions\nui_pri"
},
{
"path": "src/META-INF/com/google/android/updater-script",
"chars": 8,
"preview": "#MAGISK\n"
},
{
"path": "src/action.sh",
"chars": 67,
"preview": ". /data/adb/agh/settings.conf\n\n$SCRIPT_DIR/tool.sh toggle\n\nsleep 1\n"
},
{
"path": "src/bin/AdGuardHome.yaml",
"chars": 3864,
"preview": "http:\n pprof:\n port: 6060\n enabled: false\n address: 127.0.0.1:3000\n session_ttl: 720h\nusers:\n - name: root\n "
},
{
"path": "src/bin/data/filters/1732747955.txt",
"chars": 21466,
"preview": "||1500020991.vodplayer.wxamedia.com^\n||1500022744.vodplayer.wxamedia.com^\n||1500026601.vodplayer.wxamedia.com^\n||8le8le."
},
{
"path": "src/customize.sh",
"chars": 3994,
"preview": "SKIPUNZIP=1\n\n# most of the users are Chinese, so set default language to Chinese\nlanguage=\"zh\"\n\n# try to get the system "
},
{
"path": "src/module.prop",
"chars": 195,
"preview": "id=AdGuardHome\nname=AdGuardHome for Root\nversion=20260419\nversionCode=49\nauthor=twoone3\ndescription=none\nupdateJson=http"
},
{
"path": "src/scripts/base.sh",
"chars": 950,
"preview": "# add busybox to PATH\n[ -d \"/data/adb/magisk\" ] && export PATH=\"/data/adb/magisk:$PATH\"\n[ -d \"/data/adb/ksu/bin\" ] && ex"
},
{
"path": "src/scripts/debug.sh",
"chars": 1732,
"preview": "#!/system/bin/sh\n\nAGH_DIR=\"/data/adb/agh\"\nLOG=\"$AGH_DIR/debug.log\"\n\n{\n echo \"==== AdGuardHome Debug Log ====\"\n date\n "
},
{
"path": "src/scripts/inotify.sh",
"chars": 278,
"preview": ". /data/adb/agh/settings.conf\n\nreadonly EVENTS=$1\nreadonly MONITOR_DIR=$2\nreadonly MONITOR_FILE=$3\n\nif [ \"${MONITOR_FILE"
},
{
"path": "src/scripts/iptables.sh",
"chars": 5458,
"preview": ". /data/adb/agh/settings.conf\n. /data/adb/agh/scripts/base.sh\n\niptables_w=\"iptables -w 64\"\nip6tables_w=\"ip6tables -w 64\""
},
{
"path": "src/scripts/tool.sh",
"chars": 2334,
"preview": ". /data/adb/agh/settings.conf\n. /data/adb/agh/scripts/base.sh\n\nstart_adguardhome() {\n # check if AdGuardHome is already"
},
{
"path": "src/service.sh",
"chars": 187,
"preview": "until [ $(getprop init.svc.bootanim) = \"stopped\" ]; do\n sleep 12\ndone\n\n/data/adb/agh/scripts/tool.sh start\n\ninotifyd /d"
},
{
"path": "src/settings.conf",
"chars": 898,
"preview": "# 是否启用内置的 iptables 规则\n# Whether to enable the built-in iptables rules\nenable_iptables=true\n\n# 阻断 ipv6 的 DNS 请求,仅在 enable"
},
{
"path": "src/uninstall.sh",
"chars": 49,
"preview": "[ -d \"/data/adb/agh\" ] && rm -rf \"/data/adb/agh\"\n"
},
{
"path": "src/webroot/app.js",
"chars": 16419,
"preview": "/**\n * AdGuardHome for Root - WebUI Application\n * \n * KernelSU API: ksu.exec(cmd) returns stdout string synchronously\n "
},
{
"path": "src/webroot/index.html",
"chars": 9806,
"preview": "<!DOCTYPE html>\n<html lang=\"zh-CN\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width, ini"
},
{
"path": "src/webroot/style.css",
"chars": 15359,
"preview": "/* AdGuardHome for Root - WebUI Styles */\n\n/* ===== Dark Theme (default) ===== */\n:root {\n --primary: #68BC71;\n --prim"
},
{
"path": "version.json",
"chars": 263,
"preview": "{\n \"versionCode\": 49,\n \"version\": \"20260419\",\n \"zipUrl\": \"https://github.com/twoone-3/AdGuardHomeForRoot/releases/lat"
}
]
About this extraction
This page contains the full source code of the twoone-3/AdGuardHomeForMagisk GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 28 files (99.5 KB), approximately 34.9k tokens, and a symbol index with 28 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.