Update On Fri Aug 1 20:44:22 CEST 2025

This commit is contained in:
github-action[bot]
2025-08-01 20:44:22 +02:00
parent 06d7e6a465
commit 97c319bf3a
46 changed files with 537 additions and 308 deletions

1
.github/update.log vendored
View File

@@ -1076,3 +1076,4 @@ Update On Mon Jul 28 20:43:06 CEST 2025
Update On Tue Jul 29 20:43:03 CEST 2025
Update On Wed Jul 30 20:44:03 CEST 2025
Update On Thu Jul 31 20:43:42 CEST 2025
Update On Fri Aug 1 20:44:14 CEST 2025

View File

@@ -23,17 +23,16 @@ brook server -l :9999 -p hello
- [iOS](https://apps.apple.com/us/app/brook-network-tool/id1216002642)
- [Android](https://github.com/txthinking/brook/releases/latest/download/Brook.apk)
- [macOS](https://apps.apple.com/us/app/brook-network-tool/id1216002642)
- [About App Mode on macOS](https://www.txthinking.com/talks/articles/macos-app-mode-en.article)
- [Windows](https://github.com/txthinking/brook/releases/latest/download/Brook.msix)
- [How to install Brook on Windows](https://www.txthinking.com/talks/articles/msix-brook-en.article)
- [Linux](https://github.com/txthinking/brook/releases/latest/download/Brook.bin)
- [How to install Brook on Linux](https://www.txthinking.com/talks/articles/linux-app-brook-en.article)
- [OpenWrt](https://www.txthinking.com/talks/articles/brook-openwrt-one-en.article)
- [How to install Brook on OpenWrt](https://www.txthinking.com/talks/articles/brook-openwrt-en.article)
> You may want to use `brook link` to customize some parameters
- [About App Mode on macOS](https://www.txthinking.com/talks/articles/macos-app-mode-en.article)
- [How to install Brook on Windows](https://www.txthinking.com/talks/articles/msix-brook-en.article)
- [How to install Brook on Linux](https://www.txthinking.com/talks/articles/linux-app-brook-en.article)
- [How to install Brook on OpenWrt](https://www.txthinking.com/talks/articles/brook-openwrt-en.article)
## Docs
https://www.txthinking.com/brook.html

View File

@@ -53,7 +53,7 @@ func main() {
df := func() {}
app := cli.NewApp()
app.Name = "Brook"
app.Version = "20250202"
app.Version = "20250808"
app.Usage = "A cross-platform programmable network tool"
app.Authors = []*cli.Author{
{

View File

@@ -177,7 +177,7 @@
</div>
</div>
<div style="display: flex;justify-content:center;padding:15px;">
<div style="color:grey">Made with <t-icon name="heart" style="color:red;"></t-icon> by <t-link href="https://www.txthinking.com" target="_blank">TxThinking</t-link></div>
<t-link href="https://www.txthinking.com" target="_blank" style="color:grey">TxThinking</t-link>
</div>
<t-back-top shape="circle" container="#top" style="position: fixed" :offset="['24px', '80px']"></t-back-top>
</div>

View File

@@ -1,7 +1,7 @@
{
"version": "20250202",
"text": "Brook Store: Get Brook Server without deploying it yourself",
"link": "https://store.brook.app/",
"text_zh": "Brook Store: 无需自己部署即可获取 Brook Server",
"link_zh": "https://store.brook.app/"
"version": "20250808",
"text": "",
"link": "",
"text_zh": "",
"link_zh": ""
}

View File

@@ -41,6 +41,13 @@
"author": "TxThinking",
"author_url": "https://www.txthinking.com"
},
{
"name": "Block some AD domains",
"url": "https://raw.githubusercontent.com/txthinking/brook/master/programmable/modules/block_ad_domain.tengo",
"kind": "module",
"author": "TxThinking",
"author_url": "https://www.txthinking.com"
},
{
"name": "iOS 豆瓣 v7.66.0 去广告",
"url": "https://raw.githubusercontent.com/txthinking/brook/master/programmable/modules/douban.tengo",
@@ -50,8 +57,8 @@
"author_url": "https://www.txthinking.com"
},
{
"name": "Block some AD domains",
"url": "https://raw.githubusercontent.com/txthinking/brook/master/programmable/modules/block_ad_domain.tengo",
"name": "小红书定制 IP 归属地",
"url": "https://raw.githubusercontent.com/txthinking/brook/master/programmable/modules/xiaohongshu.tengo",
"kind": "module",
"author": "TxThinking",
"author_url": "https://www.txthinking.com"

View File

@@ -0,0 +1,20 @@
// 小红书定制 IP 归属地
// Note: 需要通过 `brooklinks` 预先定一个 key 为 `beijing` 的 brook link
modules = append(modules, {
dnsquery: func(m) {
text := import("text")
l := [
"fengkongcloud.com",
"xhscdn.com",
"xhscdn.net",
"xiaohongshu.com"
]
for v in l {
if m.domain == v || text.has_suffix(m.domain, "."+v) {
return {brooklinkkey: "beijing"}
}
}
}
})

View File

@@ -30,7 +30,7 @@ require (
github.com/metacubex/sing-shadowsocks v0.2.11
github.com/metacubex/sing-shadowsocks2 v0.2.5
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2
github.com/metacubex/sing-tun v0.4.7-0.20250721020617-8e7c37ed3d97
github.com/metacubex/sing-tun v0.4.7-0.20250801130030-308b828865ae
github.com/metacubex/sing-vmess v0.2.4-0.20250731011226-ea28d589924d
github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f
github.com/metacubex/smux v0.0.0-20250503055512-501391591dee

View File

@@ -129,8 +129,8 @@ github.com/metacubex/sing-shadowsocks2 v0.2.5 h1:MnPn0hbcDkSJt6TlpI15XImHKK6IqaO
github.com/metacubex/sing-shadowsocks2 v0.2.5/go.mod h1:Zyh+rAQRyevYfG/COCvDs1c/YMhGqCuknn7QrGmoQIw=
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2 h1:gXU+MYPm7Wme3/OAY2FFzVq9d9GxPHOqu5AQfg/ddhI=
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2/go.mod h1:mbfboaXauKJNIHJYxQRa+NJs4JU9NZfkA+I33dS2+9E=
github.com/metacubex/sing-tun v0.4.7-0.20250721020617-8e7c37ed3d97 h1:YYpc60UZE2G0pUeHbRw9erDrUDZrPQy8QzWFqA3kHsk=
github.com/metacubex/sing-tun v0.4.7-0.20250721020617-8e7c37ed3d97/go.mod h1:2YywXPWW8Z97kTH7RffOeykKzU+l0aiKlglWV1PAS64=
github.com/metacubex/sing-tun v0.4.7-0.20250801130030-308b828865ae h1:hKYGuBaJBLqNmySrYrOGDXXsyxXNaR8omO96QDsRI2s=
github.com/metacubex/sing-tun v0.4.7-0.20250801130030-308b828865ae/go.mod h1:2YywXPWW8Z97kTH7RffOeykKzU+l0aiKlglWV1PAS64=
github.com/metacubex/sing-vmess v0.2.4-0.20250731011226-ea28d589924d h1:koSVAQiYqU/qtJ527e+wbsEPvTaM39HW75LSvh04IT0=
github.com/metacubex/sing-vmess v0.2.4-0.20250731011226-ea28d589924d/go.mod h1:21R5R1u90uUvBQF0owoooEu96/SAYYD56nDrwm6nFaM=
github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f h1:Sr/DYKYofKHKc4GF3qkRGNuj6XA6c0eqPgEDN+VAsYU=

View File

@@ -8170,9 +8170,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.141"
version = "1.0.142"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30b9eff21ebe718216c6ec64e1d9ac57087aad11efc64e32002bce4a0d4c03d3"
checksum = "030fedb782600dcbd6f02d479bf0d817ac3bb40d644745b769d6a96bc3afc5a7"
dependencies = [
"indexmap 2.10.0",
"itoa",

View File

@@ -2,8 +2,8 @@
"manifest_version": 1,
"latest": {
"mihomo": "v1.19.12",
"mihomo_alpha": "alpha-f04af73",
"clash_rs": "v0.8.1",
"mihomo_alpha": "alpha-e8fddd8",
"clash_rs": "v0.8.2",
"clash_premium": "2023-09-05-gdcc8d87",
"clash_rs_alpha": "0.8.1-alpha+sha.3164865"
},
@@ -69,5 +69,5 @@
"linux-armv7hf": "clash-armv7-unknown-linux-gnueabihf"
}
},
"updated_at": "2025-07-30T22:23:15.357Z"
"updated_at": "2025-07-31T22:21:34.711Z"
}

View File

@@ -100,7 +100,7 @@
"stylelint": "16.23.0",
"stylelint-config-html": "1.1.0",
"stylelint-config-recess-order": "7.1.0",
"stylelint-config-standard": "38.0.0",
"stylelint-config-standard": "39.0.0",
"stylelint-declaration-block-no-ignored-properties": "2.8.0",
"stylelint-order": "7.0.0",
"stylelint-scss": "6.12.1",
@@ -111,7 +111,7 @@
},
"packageManager": "pnpm@10.13.1",
"engines": {
"node": "22.17.1"
"node": "22.18.0"
},
"pnpm": {
"overrides": {

View File

@@ -149,8 +149,8 @@ importers:
specifier: 7.1.0
version: 7.1.0(stylelint-order@7.0.0(stylelint@16.23.0(typescript@5.8.3)))(stylelint@16.23.0(typescript@5.8.3))
stylelint-config-standard:
specifier: 38.0.0
version: 38.0.0(stylelint@16.23.0(typescript@5.8.3))
specifier: 39.0.0
version: 39.0.0(stylelint@16.23.0(typescript@5.8.3))
stylelint-declaration-block-no-ignored-properties:
specifier: 2.8.0
version: 2.8.0(stylelint@16.23.0(typescript@5.8.3))
@@ -615,8 +615,8 @@ importers:
specifier: 2.26.22
version: 2.26.22
undici:
specifier: 7.12.0
version: 7.12.0
specifier: 7.13.0
version: 7.13.0
yargs:
specifier: 18.0.0
version: 18.0.0
@@ -7836,17 +7836,17 @@ packages:
stylelint: '>=16.18'
stylelint-order: '>=7'
stylelint-config-recommended@16.0.0:
resolution: {integrity: sha512-4RSmPjQegF34wNcK1e1O3Uz91HN8P1aFdFzio90wNK9mjgAI19u5vsU868cVZboKzCaa5XbpvtTzAAGQAxpcXA==}
stylelint-config-recommended@17.0.0:
resolution: {integrity: sha512-WaMSdEiPfZTSFVoYmJbxorJfA610O0tlYuU2aEwY33UQhSPgFbClrVJYWvy3jGJx+XW37O+LyNLiZOEXhKhJmA==}
engines: {node: '>=18.12.0'}
peerDependencies:
stylelint: ^16.16.0
stylelint: ^16.23.0
stylelint-config-standard@38.0.0:
resolution: {integrity: sha512-uj3JIX+dpFseqd/DJx8Gy3PcRAJhlEZ2IrlFOc4LUxBX/PNMEQ198x7LCOE2Q5oT9Vw8nyc4CIL78xSqPr6iag==}
stylelint-config-standard@39.0.0:
resolution: {integrity: sha512-JabShWORb8Bmc1A47ZyJstran60P3yUdI1zWMpGYPeFiC6xzHXJMkpKAd8EjIhq3HPUplIWWMDJ/xu0AiPd+kA==}
engines: {node: '>=18.12.0'}
peerDependencies:
stylelint: ^16.18.0
stylelint: ^16.23.0
stylelint-declaration-block-no-ignored-properties@2.8.0:
resolution: {integrity: sha512-Ws8Cav7Y+SPN0JsV407LrnNXWOrqGjxShf+37GBtnU/C58Syve9c0+I/xpLcFOosST3ternykn3Lp77f3ITnFw==}
@@ -8153,8 +8153,8 @@ packages:
resolution: {integrity: sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==}
engines: {node: '>=14.0'}
undici@7.12.0:
resolution: {integrity: sha512-GrKEsc3ughskmGA9jevVlIOPMiiAHJ4OFUtaAH+NhfTUSiZ1wMPIQqQvAJUrJspFXJt3EBWgpAeoHEDVT1IBug==}
undici@7.13.0:
resolution: {integrity: sha512-l+zSMssRqrzDcb3fjMkjjLGmuiiK2pMIcV++mJaAc9vhjSGpvM7h43QgP+OAMb1GImHmbPyG2tBXeuyG5iY4gA==}
engines: {node: '>=20.18.1'}
unicode-canonical-property-names-ecmascript@2.0.1:
@@ -16585,14 +16585,14 @@ snapshots:
stylelint: 16.23.0(typescript@5.8.3)
stylelint-order: 7.0.0(stylelint@16.23.0(typescript@5.8.3))
stylelint-config-recommended@16.0.0(stylelint@16.23.0(typescript@5.8.3)):
stylelint-config-recommended@17.0.0(stylelint@16.23.0(typescript@5.8.3)):
dependencies:
stylelint: 16.23.0(typescript@5.8.3)
stylelint-config-standard@38.0.0(stylelint@16.23.0(typescript@5.8.3)):
stylelint-config-standard@39.0.0(stylelint@16.23.0(typescript@5.8.3)):
dependencies:
stylelint: 16.23.0(typescript@5.8.3)
stylelint-config-recommended: 16.0.0(stylelint@16.23.0(typescript@5.8.3))
stylelint-config-recommended: 17.0.0(stylelint@16.23.0(typescript@5.8.3))
stylelint-declaration-block-no-ignored-properties@2.8.0(stylelint@16.23.0(typescript@5.8.3)):
dependencies:
@@ -17001,7 +17001,7 @@ snapshots:
dependencies:
'@fastify/busboy': 2.1.1
undici@7.12.0: {}
undici@7.13.0: {}
unicode-canonical-property-names-ecmascript@2.0.1: {}

View File

@@ -26,7 +26,7 @@
"picocolors": "1.1.1",
"tar": "7.4.3",
"telegram": "2.26.22",
"undici": "7.12.0",
"undici": "7.13.0",
"yargs": "18.0.0"
}
}

View File

@@ -55,12 +55,22 @@ export async function upload(
return true;
},
onError: function (error) {
onError: function (error: Error | tus.DetailedError) {
if (CURRENT_UPLOAD_LIST[filePath].interval) {
clearInterval(CURRENT_UPLOAD_LIST[filePath].interval);
}
delete CURRENT_UPLOAD_LIST[filePath];
reject(new Error(`Upload failed: ${error.message}`));
const message =
error instanceof tus.DetailedError
? error.originalResponse === null
? "000 No connection"
: error.originalResponse.getBody()
: "Upload failed";
console.error(error);
reject(new Error(message));
},
onProgress: function (bytesUploaded) {
const fileData = CURRENT_UPLOAD_LIST[filePath];

View File

@@ -132,7 +132,6 @@ import {
import { files as api } from "@/api";
import ProgressBar from "@/components/ProgressBar.vue";
import prettyBytes from "pretty-bytes";
import { StatusError } from "@/api/utils.js";
const USAGE_DEFAULT = { used: "0 B", total: "0 B", usedPercentage: 0 };
@@ -181,13 +180,9 @@ export default {
total: prettyBytes(usage.total, { binary: true }),
usedPercentage: Math.round((usage.used / usage.total) * 100),
};
} catch (error) {
if (error instanceof StatusError && error.is_canceled) {
return;
}
this.$showError(error);
} finally {
return Object.assign(this.usage, usageStats);
}
return Object.assign(this.usage, usageStats);
},
toRoot() {
this.$router.push({ path: "/files" });

View File

@@ -26,6 +26,7 @@
import {
computed,
defineAsyncComponent,
inject,
onBeforeUnmount,
onMounted,
onUnmounted,
@@ -50,6 +51,8 @@ import { name } from "../utils/constants";
const Editor = defineAsyncComponent(() => import("@/views/files/Editor.vue"));
const Preview = defineAsyncComponent(() => import("@/views/files/Preview.vue"));
const $showError = inject<IToastError>("$showError")!;
const layoutStore = useLayoutStore();
const fileStore = useFileStore();
const uploadStore = useUploadStore();
@@ -109,7 +112,7 @@ watch(reload, (newValue) => {
newValue && fetchData();
});
watch(uploadError, (newValue) => {
newValue && layoutStore.showError();
newValue && $showError(newValue);
});
// Define functions

View File

@@ -609,6 +609,13 @@ choice
bool "madvise"
endchoice
config KERNEL_ARM64_CONTPTE
bool "Compile the kernel with Contiguous PTE mappings for user memory"
depends on KERNEL_ARM64
depends on KERNEL_TRANSPARENT_HUGEPAGE
depends on LINUX_6_12
default y
config KERNEL_HUGETLBFS
bool

View File

@@ -230,7 +230,7 @@ Below is an example to configure a proxy chain.
```
1. In the `egress` -> `proxies` property, list the information of outbound proxy servers. The current version only supports socks5 outbound, so the value of `protocol` must be set to `SOCKS5_PROXY_PROTOCOL`. If the outbound proxy server requires socks5 username and password authentication, please fill in the `socks5Authentication` property. Otherwise, please remove the `socks5Authentication` property.
2. In the `egress` -> `rules` property, list outbound rules. `proxyNames` needs to point to proxies that exist in `egress` -> `proxies` property.
2. In the `egress` -> `rules` property, list outbound rules. Outbound actions include `DIRECT`, `PROXY` and `REJECT`. `proxyNames` must be set if `PROXY` action is used. `proxyNames` needs to point to proxies that exist in `egress` -> `proxies` property.
If you want to turn off the outbound proxy feature, simply set the `egress` property to an empty value `{}`.

View File

@@ -230,7 +230,7 @@ mieru 客户端 -> GFW -> mita 服务器 -> cloudflare 代理客户端 -> cloudf
```
1.`egress` -> `proxies` 属性中列举出站代理服务器的信息。当前版本只支持 socks5 出站,因此 `protocol` 的值必须设定为 `SOCKS5_PROXY_PROTOCOL`。如果出站代理服务器需要 socks5 用户名和密码验证,请填写 `socks5Authentication` 属性。否则,请删除 `socks5Authentication` 属性。
2.`egress` -> `rules` 属性中列举出站规则。`proxyNames` 需要指向 `egress` -> `proxies` 属性中存在的代理。
2.`egress` -> `rules` 属性中列举出站规则。出站行为包括 `DIRECT``PROXY``REJECT`。其中 `PROXY` 行为必须指定 `proxyNames``proxyNames` 需要指向 `egress` -> `proxies` 属性中存在的代理。
如果想要关闭出站代理功能,将 `egress` 属性设置为空 `{}` 即可。

View File

@@ -30,7 +30,7 @@ require (
github.com/metacubex/sing-shadowsocks v0.2.11
github.com/metacubex/sing-shadowsocks2 v0.2.5
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2
github.com/metacubex/sing-tun v0.4.7-0.20250721020617-8e7c37ed3d97
github.com/metacubex/sing-tun v0.4.7-0.20250801130030-308b828865ae
github.com/metacubex/sing-vmess v0.2.4-0.20250731011226-ea28d589924d
github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f
github.com/metacubex/smux v0.0.0-20250503055512-501391591dee

View File

@@ -129,8 +129,8 @@ github.com/metacubex/sing-shadowsocks2 v0.2.5 h1:MnPn0hbcDkSJt6TlpI15XImHKK6IqaO
github.com/metacubex/sing-shadowsocks2 v0.2.5/go.mod h1:Zyh+rAQRyevYfG/COCvDs1c/YMhGqCuknn7QrGmoQIw=
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2 h1:gXU+MYPm7Wme3/OAY2FFzVq9d9GxPHOqu5AQfg/ddhI=
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2/go.mod h1:mbfboaXauKJNIHJYxQRa+NJs4JU9NZfkA+I33dS2+9E=
github.com/metacubex/sing-tun v0.4.7-0.20250721020617-8e7c37ed3d97 h1:YYpc60UZE2G0pUeHbRw9erDrUDZrPQy8QzWFqA3kHsk=
github.com/metacubex/sing-tun v0.4.7-0.20250721020617-8e7c37ed3d97/go.mod h1:2YywXPWW8Z97kTH7RffOeykKzU+l0aiKlglWV1PAS64=
github.com/metacubex/sing-tun v0.4.7-0.20250801130030-308b828865ae h1:hKYGuBaJBLqNmySrYrOGDXXsyxXNaR8omO96QDsRI2s=
github.com/metacubex/sing-tun v0.4.7-0.20250801130030-308b828865ae/go.mod h1:2YywXPWW8Z97kTH7RffOeykKzU+l0aiKlglWV1PAS64=
github.com/metacubex/sing-vmess v0.2.4-0.20250731011226-ea28d589924d h1:koSVAQiYqU/qtJ527e+wbsEPvTaM39HW75LSvh04IT0=
github.com/metacubex/sing-vmess v0.2.4-0.20250731011226-ea28d589924d/go.mod h1:21R5R1u90uUvBQF0owoooEu96/SAYYD56nDrwm6nFaM=
github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f h1:Sr/DYKYofKHKc4GF3qkRGNuj6XA6c0eqPgEDN+VAsYU=

View File

@@ -8,12 +8,12 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=ddns-go
PKG_VERSION:=6.12.0
PKG_VERSION:=6.12.1
PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://codeload.github.com/jeessy2/ddns-go/tar.gz/v$(PKG_VERSION)?
PKG_HASH:=59f5a705f08f539c011e12c01b4e82791130bfb4ccfb332f2b6545945fa70e38
PKG_HASH:=8e6cf10808f5e89f6527f42f37e55c275f4ae17738f6836c8e9cb5e4318fff43
PKG_LICENSE:=MIT
PKG_LICENSE_FILES:=LICENSE

View File

@@ -16,7 +16,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-amlogic
PKG_VERSION:=3.1.260
PKG_VERSION:=3.1.261
PKG_RELEASE:=1
PKG_LICENSE:=GPL-2.0 License

View File

@@ -195,7 +195,11 @@ else
# 6. MAINLINE_UBOOT
# 7. ANDROID_UBOOT
AMLOGIC_SOC="$(echo "${ret}" | awk -F ':' '{print $3}')"
FDTFILE="$(echo "${ret}" | awk -F ':' '{print $4}')"
FDTFILE="$(
grep -oE 'meson[^[:space:]]*\.dtb' /boot/extlinux/extlinux.conf 2>/dev/null ||
grep -oE 'meson[^[:space:]]*\.dtb' /boot/uEnv.txt 2>/dev/null ||
echo "${ret}" | awk -F':' '{print $4}'
)"
UBOOT_OVERLOAD="$(echo "${ret}" | awk -F ':' '{print $5}')"
MAINLINE_UBOOT="$(echo "${ret}" | awk -F ':' '{print $6}')"
ANDROID_UBOOT="$(echo "${ret}" | awk -F ':' '{print $7}')"

View File

@@ -21,7 +21,7 @@ env:
jobs:
job_check:
name: Check Version
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
outputs:
passwall_version: ${{ steps.check_version.outputs.latest_version }}
has_update: ${{ steps.check_version.outputs.has_update }}
@@ -74,7 +74,7 @@ jobs:
name: Build passwall [Luci ${{ matrix.luci_ver }}]
needs: job_check
if: needs.job_check.outputs.has_update == 'true'
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
@@ -89,50 +89,58 @@ jobs:
steps:
- name: Install packages
run: |
sudo -E rm -rf /usr/share/dotnet /etc/mysql /etc/php /etc/apt/sources.list.d /usr/local/lib/android
sudo -E rm -rf /usr/share/dotnet /etc/mysql /etc/php /usr/local/lib/android
echo "Install packages"
sudo -E apt-get -qq update
sudo -E apt-get -qq install build-essential clang flex bison g++ gawk gcc-multilib g++-multilib gettext git libncurses-dev libssl-dev python3-distutils python3-setuptools rsync swig unzip zlib1g-dev file wget
sudo -E apt-get -qq install ack antlr3 asciidoc autoconf automake autopoint binutils bison build-essential \
bzip2 ccache clang cmake cpio curl device-tree-compiler ecj fastjar flex gawk gettext gcc-multilib \
g++-multilib git gnutls-dev gperf haveged help2man intltool lib32gcc-s1 libc6-dev-i386 libelf-dev \
libglib2.0-dev libgmp3-dev libltdl-dev libmpc-dev libmpfr-dev libncurses-dev libpython3-dev \
libreadline-dev libssl-dev libtool libyaml-dev libz-dev lld llvm lrzsz mkisofs msmtp nano \
ninja-build p7zip p7zip-full patch pkgconf python3 python3-pip python3-ply python3-docutils \
python3-pyelftools qemu-utils re2c rsync scons squashfs-tools subversion swig texinfo uglifyjs \
upx-ucl unzip vim wget xmlto xxd zlib1g-dev zstd
sudo -E apt-get -qq autoremove --purge
sudo -E apt-get -qq clean
- name: Cache openwrt SDK
id: cache-sdk
uses: actions/cache@v4
with:
path: sdk
key: openwrt-luci-${{ matrix.luci_ver }}-x86_64
- name: Initialization environment
if: steps.cache-sdk.outputs.cache-hit != 'true'
run: |
wget ${{ matrix.sdk_url }}
file_name=$(echo ${{ matrix.sdk_url }} | awk -F/ '{print $NF}')
mkdir sdk && tar --zstd -x -f $file_name -C ./sdk --strip-components=1
mkdir sdk
if [[ $file_name == *.tar.xz ]]; then
tar -xJf $file_name -C ./sdk --strip-components=1
elif [[ $file_name == *.tar.zst ]]; then
tar --zstd -x -f $file_name -C ./sdk --strip-components=1
else
echo "Unsupported file format: $file_name"
exit 1
fi
cd sdk
echo "src-git base https://github.com/openwrt/openwrt.git;openwrt-${{ matrix.sdk_ver }}" > feeds.conf
echo "src-git packages https://github.com/openwrt/packages.git;openwrt-${{ matrix.sdk_ver }}" >> feeds.conf
echo "src-git luci https://github.com/openwrt/luci.git;openwrt-${{ matrix.luci_ver }}" >> feeds.conf
echo "src-git routing https://github.com/openwrt/routing.git;openwrt-${{ matrix.sdk_ver }}" >> feeds.conf
echo "src-git passwall_packages https://github.com/${{ env.packages }}.git;main" >> feeds.conf
echo "src-git passwall https://github.com/${{ env.passwall }}.git;${{ github.ref_name }}" >> feeds.conf
cat > feeds.conf.default << EOF
src-git passwall_packages https://github.com/xiaorouji/openwrt-passwall-packages.git;main
src-git passwall https://github.com/${{ env.passwall }}.git;${{ github.ref_name }}
src-git base https://github.com/openwrt/openwrt.git;openwrt-${{ matrix.sdk_ver }}
src-git packages https://github.com/openwrt/packages.git;openwrt-${{ matrix.sdk_ver }}
src-git luci https://github.com/openwrt/luci.git;openwrt-${{ matrix.luci_ver }}
src-git routing https://github.com/openwrt/routing.git;openwrt-${{ matrix.sdk_ver }}
src-git telephony https://github.com/openwrt/telephony.git;openwrt-${{ matrix.sdk_ver }}
EOF
./scripts/feeds update -a
./scripts/feeds install -a
#--------------------------------------begin_patches------------------------------------------
echo "Start applying the patch"
echo "update golang version"
rm -rf feeds/packages/lang/golang
git clone https://github.com/sbwml/packages_lang_golang -b 24.x feeds/packages/lang/golang
./scripts/feeds update -a
echo "CONFIG_PACKAGE_luci-app-passwall=m" > .config
./scripts/feeds install -d n luci-app-passwall
make package/luci-app-passwall/download -j8
- name: Update passwall feeds
if: steps.cache-sdk.outputs.cache-hit == 'true'
run: |
cd sdk
sed -i '6s/main/${{ github.ref_name }}/' feeds.conf
./scripts/feeds update passwall_packages
./scripts/feeds update passwall
./scripts/feeds install luci-app-passwall
echo "Patch application completed"
#--------------------------------------end_patches--------------------------------------------
- name: Compile passwall
id: compile
@@ -170,7 +178,7 @@ jobs:
job_auto_compile:
if: ${{ needs.job_check.outputs.has_update == 'true' && needs.job_check.outputs.prerelease == 'false' }}
needs: job_check
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
name: build (${{ matrix.platform }})
strategy:
fail-fast: false
@@ -251,10 +259,17 @@ jobs:
steps:
- name: Initialization ${{ matrix.platform }} compile environment
run: |
sudo -E rm -rf /usr/share/dotnet /etc/mysql /etc/php /etc/apt/sources.list.d /usr/local/lib/android
echo "install packages!!!!!!"
sudo -E rm -rf /usr/share/dotnet /etc/mysql /etc/php /usr/local/lib/android
echo "Install packages"
sudo -E apt-get -qq update
sudo -E apt-get -qq install build-essential clang flex bison g++ gawk gcc-multilib g++-multilib gettext git libncurses-dev libssl-dev python3-distutils python3-setuptools rsync swig unzip zlib1g-dev file wget
sudo -E apt-get -qq install ack antlr3 asciidoc autoconf automake autopoint binutils bison build-essential \
bzip2 ccache clang cmake cpio curl device-tree-compiler ecj fastjar flex gawk gettext gcc-multilib \
g++-multilib git gnutls-dev gperf haveged help2man intltool lib32gcc-s1 libc6-dev-i386 libelf-dev \
libglib2.0-dev libgmp3-dev libltdl-dev libmpc-dev libmpfr-dev libncurses-dev libpython3-dev \
libreadline-dev libssl-dev libtool libyaml-dev libz-dev lld llvm lrzsz mkisofs msmtp nano \
ninja-build p7zip p7zip-full patch pkgconf python3 python3-pip python3-ply python3-docutils \
python3-pyelftools qemu-utils re2c rsync scons squashfs-tools subversion swig texinfo uglifyjs \
upx-ucl unzip vim wget xmlto xxd zlib1g-dev zstd
sudo -E apt-get -qq autoremove --purge
sudo -E apt-get -qq clean
@@ -262,7 +277,15 @@ jobs:
run: |
wget ${{ matrix.url_sdk }}
file_name=$(echo ${{matrix.url_sdk}} | awk -F/ '{print $NF}')
mkdir sdk && tar --zstd -x -f $file_name -C ./sdk --strip-components=1
mkdir sdk
if [[ $file_name == *.tar.xz ]]; then
tar -xJf $file_name -C ./sdk --strip-components=1
elif [[ $file_name == *.tar.zst ]]; then
tar --zstd -x -f $file_name -C ./sdk --strip-components=1
else
echo "Unsupported file format: $file_name"
exit 1
fi
cd sdk
- name: SSH connection to Actions
@@ -272,19 +295,35 @@ jobs:
- name: ${{ matrix.platform }} feeds configuration packages
run: |
cd sdk
echo "src-git base https://github.com/openwrt/openwrt.git;openwrt-${{ matrix.sdk_ver }}" > feeds.conf
echo "src-git packages https://github.com/openwrt/packages.git;openwrt-${{ matrix.sdk_ver }}" >> feeds.conf
echo "src-git luci https://github.com/openwrt/luci.git;openwrt-${{ matrix.sdk_ver }}" >> feeds.conf
echo "src-git routing https://github.com/openwrt/routing.git;openwrt-${{ matrix.sdk_ver }}" >> feeds.conf
echo "src-git passwall_packages https://github.com/${{ env.packages }}.git;main" >> feeds.conf
echo "src-git passwall https://github.com/${{ env.passwall }}.git;main" >> feeds.conf
cat > feeds.conf.default << EOF
src-git passwall_packages https://github.com/xiaorouji/openwrt-passwall-packages.git;main
src-git passwall https://github.com/${{ env.passwall }}.git;${{ github.ref_name }}
src-git base https://github.com/openwrt/openwrt.git;openwrt-${{ matrix.sdk_ver }}
src-git packages https://github.com/openwrt/packages.git;openwrt-${{ matrix.sdk_ver }}
src-git luci https://github.com/openwrt/luci.git;openwrt-${{ matrix.sdk_ver }}
src-git routing https://github.com/openwrt/routing.git;openwrt-${{ matrix.sdk_ver }}
src-git telephony https://github.com/openwrt/telephony.git;openwrt-${{ matrix.sdk_ver }}
EOF
./scripts/feeds update -a
./scripts/feeds install -a -f -p passwall_packages
./scripts/feeds install luci-app-passwall
./scripts/feeds install -a
#--------------------------------------begin_patches------------------------------------------
echo "Start applying the patch"
echo "update golang version"
rm -rf feeds/packages/lang/golang
git clone https://github.com/sbwml/packages_lang_golang -b 25.x feeds/packages/lang/golang
git clone https://github.com/sbwml/packages_lang_golang -b 24.x feeds/packages/lang/golang
echo "fixed rust host build error"
sed -i 's/--set=llvm\.download-ci-llvm=false/--set=llvm.download-ci-llvm=true/' feeds/packages/lang/rust/Makefile
grep -q -- '--ci false \\' feeds/packages/lang/rust/Makefile || sed -i '/x\.py \\/a \ --ci false \\' feeds/packages/lang/rust/Makefile
echo "Patch application completed"
#--------------------------------------end_patches--------------------------------------------
echo "CONFIG_ALL_NONSHARED=n" > .config
echo "CONFIG_ALL_KMODS=n" >> .config

View File

@@ -1,9 +1,6 @@
## :mega:注意
由于 Sing-box 在 1.12.0 版本中移除 Geo 只保留规则集([详情](https://sing-box.sagernet.org/zh/deprecated/#geoip)Passwall 为适应这一变更,同时兼容 Xray 和 Sing-box 的分流方式,从 25.3.9 版起Sing-box 分流将依赖 Geoview 从 Geofile 生成规则集。**未安装 Geoview 将无法使用 Sing-box 分流**。
## 📌另外:
由于 Geoview v0.1.9 及之前版本在转换规则时的一些问题,**请务必将 Geoview 更新到 v0.1.10 版**。
## 📌如何能编译到最新代码?
`./scripts/feeds install -a` 操作完成后,执行以下命令:

View File

@@ -6,7 +6,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-passwall
PKG_VERSION:=25.7.15
PKG_VERSION:=25.8.1
PKG_RELEASE:=1
PKG_CONFIG_DEPENDS:= \

View File

@@ -20,7 +20,7 @@ env:
jobs:
job_check:
name: Check Version
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
outputs:
passwall2_version: ${{ steps.check_version.outputs.latest_version }}
has_update: ${{ steps.check_version.outputs.has_update }}
@@ -73,7 +73,7 @@ jobs:
name: Build passwall2 [Luci ${{ matrix.luci_ver }}]
needs: job_check
if: needs.job_check.outputs.has_update == 'true'
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
@@ -88,50 +88,58 @@ jobs:
steps:
- name: Install packages
run: |
sudo -E rm -rf /usr/share/dotnet /etc/mysql /etc/php /etc/apt/sources.list.d /usr/local/lib/android
sudo -E rm -rf /usr/share/dotnet /etc/mysql /etc/php /usr/local/lib/android
echo "Install packages"
sudo -E apt-get -qq update
sudo -E apt-get -qq install build-essential clang flex bison g++ gawk gcc-multilib g++-multilib gettext git libncurses-dev libssl-dev python3-distutils python3-setuptools rsync swig unzip zlib1g-dev file wget
sudo -E apt-get -qq install ack antlr3 asciidoc autoconf automake autopoint binutils bison build-essential \
bzip2 ccache clang cmake cpio curl device-tree-compiler ecj fastjar flex gawk gettext gcc-multilib \
g++-multilib git gnutls-dev gperf haveged help2man intltool lib32gcc-s1 libc6-dev-i386 libelf-dev \
libglib2.0-dev libgmp3-dev libltdl-dev libmpc-dev libmpfr-dev libncurses-dev libpython3-dev \
libreadline-dev libssl-dev libtool libyaml-dev libz-dev lld llvm lrzsz mkisofs msmtp nano \
ninja-build p7zip p7zip-full patch pkgconf python3 python3-pip python3-ply python3-docutils \
python3-pyelftools qemu-utils re2c rsync scons squashfs-tools subversion swig texinfo uglifyjs \
upx-ucl unzip vim wget xmlto xxd zlib1g-dev zstd
sudo -E apt-get -qq autoremove --purge
sudo -E apt-get -qq clean
- name: Cache openwrt SDK
id: cache-sdk
uses: actions/cache@v4
with:
path: sdk
key: openwrt-luci-${{ matrix.luci_ver }}-x86_64
- name: Initialization environment
if: steps.cache-sdk.outputs.cache-hit != 'true'
run: |
wget ${{ matrix.sdk_url }}
file_name=$(echo ${{ matrix.sdk_url }} | awk -F/ '{print $NF}')
mkdir sdk && tar --zstd -x -f $file_name -C ./sdk --strip-components=1
mkdir sdk
if [[ $file_name == *.tar.xz ]]; then
tar -xJf $file_name -C ./sdk --strip-components=1
elif [[ $file_name == *.tar.zst ]]; then
tar --zstd -x -f $file_name -C ./sdk --strip-components=1
else
echo "Unsupported file format: $file_name"
exit 1
fi
cd sdk
echo "src-git base https://github.com/openwrt/openwrt.git;openwrt-${{ matrix.sdk_ver }}" > feeds.conf
echo "src-git packages https://github.com/openwrt/packages.git;openwrt-${{ matrix.sdk_ver }}" >> feeds.conf
echo "src-git luci https://github.com/openwrt/luci.git;openwrt-${{ matrix.luci_ver }}" >> feeds.conf
echo "src-git routing https://git.openwrt.org/feed/routing.git;openwrt-${{ matrix.sdk_ver }}" >> feeds.conf
echo "src-git passwall_packages https://github.com/${{ env.packages }}.git;main" >> feeds.conf
echo "src-git passwall2 https://github.com/${{ env.passwall2 }}.git;${{ github.ref_name }}" >> feeds.conf
cat > feeds.conf.default << EOF
src-git passwall_packages https://github.com/${{ env.packages }}.git;main
src-git passwall2 https://github.com/${{ env.passwall2 }}.git;${{ github.ref_name }}
src-git base https://github.com/openwrt/openwrt.git;openwrt-${{ matrix.sdk_ver }}
src-git packages https://github.com/openwrt/packages.git;openwrt-${{ matrix.sdk_ver }}
src-git luci https://github.com/openwrt/luci.git;openwrt-${{ matrix.luci_ver }}
src-git routing https://github.com/openwrt/routing.git;openwrt-${{ matrix.sdk_ver }}
src-git telephony https://github.com/openwrt/telephony.git;openwrt-${{ matrix.sdk_ver }}
EOF
./scripts/feeds update -a
./scripts/feeds install -a
#--------------------------------------begin_patches------------------------------------------
echo "Start applying the patch"
echo "update golang version"
rm -rf feeds/packages/lang/golang
git clone https://github.com/sbwml/packages_lang_golang -b 24.x feeds/packages/lang/golang
./scripts/feeds update -a
echo "CONFIG_PACKAGE_luci-app-passwall2=m" > .config
./scripts/feeds install -d n luci-app-passwall2
make package/luci-app-passwall2/download -j$(nproc)
- name: Update passwall2 feeds
if: steps.cache-sdk.outputs.cache-hit == 'true'
run: |
cd sdk
sed -i '6s/main/${{ github.ref_name }}/' feeds.conf
./scripts/feeds update passwall_packages
./scripts/feeds update passwall2
./scripts/feeds install luci-app-passwall2
echo "Patch application completed"
#--------------------------------------end_patches--------------------------------------------
- name: Compile passwall2
id: compile
@@ -169,7 +177,7 @@ jobs:
job_auto_compile:
if: ${{ needs.job_check.outputs.has_update == 'true' && needs.job_check.outputs.prerelease == 'false' }}
needs: job_check
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
name: build (${{ matrix.platform }})
strategy:
fail-fast: false
@@ -250,10 +258,17 @@ jobs:
steps:
- name: Initialization ${{ matrix.platform }} compile environment
run: |
sudo -E rm -rf /usr/share/dotnet /etc/mysql /etc/php /etc/apt/sources.list.d /usr/local/lib/android
echo "install packages!!!!!!"
sudo -E rm -rf /usr/share/dotnet /etc/mysql /etc/php /usr/local/lib/android
echo "Install packages"
sudo -E apt-get -qq update
sudo -E apt-get -qq install build-essential clang flex bison g++ gawk gcc-multilib g++-multilib gettext git libncurses-dev libssl-dev python3-distutils python3-setuptools rsync swig unzip zlib1g-dev file wget
sudo -E apt-get -qq install ack antlr3 asciidoc autoconf automake autopoint binutils bison build-essential \
bzip2 ccache clang cmake cpio curl device-tree-compiler ecj fastjar flex gawk gettext gcc-multilib \
g++-multilib git gnutls-dev gperf haveged help2man intltool lib32gcc-s1 libc6-dev-i386 libelf-dev \
libglib2.0-dev libgmp3-dev libltdl-dev libmpc-dev libmpfr-dev libncurses-dev libpython3-dev \
libreadline-dev libssl-dev libtool libyaml-dev libz-dev lld llvm lrzsz mkisofs msmtp nano \
ninja-build p7zip p7zip-full patch pkgconf python3 python3-pip python3-ply python3-docutils \
python3-pyelftools qemu-utils re2c rsync scons squashfs-tools subversion swig texinfo uglifyjs \
upx-ucl unzip vim wget xmlto xxd zlib1g-dev zstd
sudo -E apt-get -qq autoremove --purge
sudo -E apt-get -qq clean
@@ -261,7 +276,15 @@ jobs:
run: |
wget ${{ matrix.url_sdk }}
file_name=$(echo ${{matrix.url_sdk}} | awk -F/ '{print $NF}')
mkdir sdk && tar --zstd -x -f $file_name -C ./sdk --strip-components=1
mkdir sdk
if [[ $file_name == *.tar.xz ]]; then
tar -xJf $file_name -C ./sdk --strip-components=1
elif [[ $file_name == *.tar.zst ]]; then
tar --zstd -x -f $file_name -C ./sdk --strip-components=1
else
echo "Unsupported file format: $file_name"
exit 1
fi
cd sdk
- name: SSH connection to Actions
@@ -271,20 +294,37 @@ jobs:
- name: ${{ matrix.platform }} feeds configuration packages
run: |
cd sdk
echo "src-git base https://github.com/openwrt/openwrt.git;openwrt-${{ matrix.sdk_ver }}" > feeds.conf
echo "src-git packages https://github.com/openwrt/packages.git;openwrt-${{ matrix.sdk_ver }}" >> feeds.conf
echo "src-git luci https://github.com/openwrt/luci.git;openwrt-${{ matrix.sdk_ver }}" >> feeds.conf
echo "src-git routing https://git.openwrt.org/feed/routing.git;openwrt-${{ matrix.sdk_ver }}" >> feeds.conf
echo "src-git passwall_packages https://github.com/${{ env.packages }}.git;main" >> feeds.conf
echo "src-git passwall2 https://github.com/${{ env.passwall2 }}.git;main" >> feeds.conf
cat > feeds.conf.default << EOF
src-git passwall_packages https://github.com/${{ env.packages }}.git;main
src-git passwall2 https://github.com/${{ env.passwall2 }}.git;${{ github.ref_name }}
src-git base https://github.com/openwrt/openwrt.git;openwrt-${{ matrix.sdk_ver }}
src-git packages https://github.com/openwrt/packages.git;openwrt-${{ matrix.sdk_ver }}
src-git luci https://github.com/openwrt/luci.git;openwrt-${{ matrix.sdk_ver }}
src-git routing https://github.com/openwrt/routing.git;openwrt-${{ matrix.sdk_ver }}
src-git telephony https://github.com/openwrt/telephony.git;openwrt-${{ matrix.sdk_ver }}
EOF
./scripts/feeds update -a
./scripts/feeds install -a -f -p passwall_packages
./scripts/feeds install luci-app-passwall2
./scripts/feeds install -a
#--------------------------------------begin_patches------------------------------------------
echo "Start applying the patch"
echo "update golang version"
rm -rf feeds/packages/lang/golang
git clone https://github.com/sbwml/packages_lang_golang -b 24.x feeds/packages/lang/golang
echo "fixed rust host build error"
sed -i 's/--set=llvm\.download-ci-llvm=false/--set=llvm.download-ci-llvm=true/' feeds/packages/lang/rust/Makefile
grep -q -- '--ci false \\' feeds/packages/lang/rust/Makefile || sed -i '/x\.py \\/a \ --ci false \\' feeds/packages/lang/rust/Makefile
echo "Patch application completed"
#--------------------------------------end_patches--------------------------------------------
echo "CONFIG_ALL_NONSHARED=n" > .config
echo "CONFIG_ALL_KMODS=n" >> .config
echo "CONFIG_ALL=n" >> .config

View File

@@ -27,14 +27,14 @@ require (
github.com/sagernet/gomobile v0.1.7
github.com/sagernet/gvisor v0.0.0-20250325023245-7a9c0f5725fb
github.com/sagernet/quic-go v0.52.0-beta.1
github.com/sagernet/sing v0.7.0-beta.1.0.20250722151551-64142925accb
github.com/sagernet/sing v0.7.0-beta.2
github.com/sagernet/sing-mux v0.3.2
github.com/sagernet/sing-quic v0.5.0-beta.3
github.com/sagernet/sing-shadowsocks v0.2.8
github.com/sagernet/sing-shadowsocks2 v0.2.1
github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11
github.com/sagernet/sing-tun v0.6.10-0.20250721014417-ebbe32588cfb
github.com/sagernet/sing-vmess v0.2.5
github.com/sagernet/sing-tun v0.7.0-beta.1
github.com/sagernet/sing-vmess v0.2.6
github.com/sagernet/smux v1.5.34-mod.2
github.com/sagernet/tailscale v1.80.3-mod.5
github.com/sagernet/wireguard-go v0.0.1-beta.7

View File

@@ -167,8 +167,8 @@ github.com/sagernet/nftables v0.3.0-beta.4/go.mod h1:OQXAjvjNGGFxaTgVCSTRIhYB5/l
github.com/sagernet/quic-go v0.52.0-beta.1 h1:hWkojLg64zjV+MJOvJU/kOeWndm3tiEfBLx5foisszs=
github.com/sagernet/quic-go v0.52.0-beta.1/go.mod h1:OV+V5kEBb8kJS7k29MzDu6oj9GyMc7HA07sE1tedxz4=
github.com/sagernet/sing v0.6.9/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
github.com/sagernet/sing v0.7.0-beta.1.0.20250722151551-64142925accb h1:9DU5JA9Cow/bUfdP1v1pYMbAkFiW17UbI4b/iEPjVnc=
github.com/sagernet/sing v0.7.0-beta.1.0.20250722151551-64142925accb/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
github.com/sagernet/sing v0.7.0-beta.2 h1:UImAKtHGQX205lGYYXKA2qnEeVSml+hKS1oaOwvA14c=
github.com/sagernet/sing v0.7.0-beta.2/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
github.com/sagernet/sing-mux v0.3.2 h1:meZVFiiStvHThb/trcpAkCrmtJOuItG5Dzl1RRP5/NE=
github.com/sagernet/sing-mux v0.3.2/go.mod h1:pht8iFY4c9Xltj7rhVd208npkNaeCxzyXCgulDPLUDA=
github.com/sagernet/sing-quic v0.5.0-beta.3 h1:X/acRNsqQNfDlmwE7SorHfaZiny5e67hqIzM/592ric=
@@ -179,10 +179,10 @@ github.com/sagernet/sing-shadowsocks2 v0.2.1 h1:dWV9OXCeFPuYGHb6IRqlSptVnSzOelnq
github.com/sagernet/sing-shadowsocks2 v0.2.1/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ=
github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11 h1:tK+75l64tm9WvEFrYRE1t0YxoFdWQqw/h7Uhzj0vJ+w=
github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11/go.mod h1:sWqKnGlMipCHaGsw1sTTlimyUpgzP4WP3pjhCsYt9oA=
github.com/sagernet/sing-tun v0.6.10-0.20250721014417-ebbe32588cfb h1:cvHEzjk3sVy80UA9PFKX15MzSP0g1uKwUspOm2ds3no=
github.com/sagernet/sing-tun v0.6.10-0.20250721014417-ebbe32588cfb/go.mod h1:AHJuRrLbNRJuivuFZ2VhXwDj4ViYp14szG5EkkKAqRQ=
github.com/sagernet/sing-vmess v0.2.5 h1:UI9KKqfDnMiyvNRknhd2ZCiHdvBl/Gv+Vg/y1yC6RIU=
github.com/sagernet/sing-vmess v0.2.5/go.mod h1:5aYoOtYksAyS0NXDm0qKeTYW1yoE1bJVcv+XLcVoyJs=
github.com/sagernet/sing-tun v0.7.0-beta.1 h1:mBIFXYAnGO5ey/HcCYanqnBx61E7yF8zTFGRZonGYmY=
github.com/sagernet/sing-tun v0.7.0-beta.1/go.mod h1:AHJuRrLbNRJuivuFZ2VhXwDj4ViYp14szG5EkkKAqRQ=
github.com/sagernet/sing-vmess v0.2.6 h1:1c4dGzeGy0kpBXXrT1sgiMZtHhdJylIT8eWrGhJYZec=
github.com/sagernet/sing-vmess v0.2.6/go.mod h1:5aYoOtYksAyS0NXDm0qKeTYW1yoE1bJVcv+XLcVoyJs=
github.com/sagernet/smux v1.5.34-mod.2 h1:gkmBjIjlJ2zQKpLigOkFur5kBKdV6bNRoFu2WkltRQ4=
github.com/sagernet/smux v1.5.34-mod.2/go.mod h1:0KW0+R+ycvA2INW4gbsd7BNyg+HEfLIAxa5N02/28Zc=
github.com/sagernet/tailscale v1.80.3-mod.5 h1:7V7z+p2C//TGtff20pPnDCt3qP6uFyY62peJoKF9z/A=

View File

@@ -5,12 +5,12 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=brook
PKG_VERSION:=20250202
PKG_VERSION:=20250808
PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://codeload.github.com/txthinking/brook/tar.gz/v$(PKG_VERSION)?
PKG_HASH:=2ee6bf43345b2cbf883eeaa8350da161352610e4fee82c29b0d3411a3e761f1f
PKG_HASH:=d78e8066ba5377c3841c8b6dcc6949cccbc04f3e475a3ac34587721438cde494
PKG_MAINTAINER:=Tianling Shen <cnsztl@immortalwrt.org>
PKG_LICENSE:=GPL-3.0

View File

@@ -6,7 +6,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-passwall
PKG_VERSION:=25.7.15
PKG_VERSION:=25.8.1
PKG_RELEASE:=1
PKG_CONFIG_DEPENDS:= \

View File

@@ -130,7 +130,7 @@ public sealed class CoreInfoHandler
{
CoreType = ECoreType.hysteria,
CoreExes = ["hysteria"],
Arguments = "-c {0}",
Arguments = "",
Url = GetCoreUrl(ECoreType.hysteria),
},
@@ -180,7 +180,7 @@ public sealed class CoreInfoHandler
{
CoreType = ECoreType.hysteria2,
CoreExes = ["hysteria-windows-amd64", "hysteria-linux-amd64", "hysteria"],
Arguments = "-c {0}",
Arguments = "",
Url = GetCoreUrl(ECoreType.hysteria2),
},

View File

@@ -6,21 +6,21 @@ import (
)
type DokodemoConfig struct {
Host *Address `json:"address"`
PortValue uint16 `json:"port"`
NetworkList *NetworkList `json:"network"`
Redirect bool `json:"followRedirect"`
UserLevel uint32 `json:"userLevel"`
Address *Address `json:"address"`
Port uint16 `json:"port"`
Network *NetworkList `json:"network"`
FollowRedirect bool `json:"followRedirect"`
UserLevel uint32 `json:"userLevel"`
}
func (v *DokodemoConfig) Build() (proto.Message, error) {
config := new(dokodemo.Config)
if v.Host != nil {
config.Address = v.Host.Build()
if v.Address != nil {
config.Address = v.Address.Build()
}
config.Port = uint32(v.PortValue)
config.Networks = v.NetworkList.Build()
config.FollowRedirect = v.Redirect
config.Port = uint32(v.Port)
config.Networks = v.Network.Build()
config.FollowRedirect = v.FollowRedirect
config.UserLevel = v.UserLevel
return config, nil
}

View File

@@ -412,8 +412,9 @@ type TLSConfig struct {
MasterKeyLog string `json:"masterKeyLog"`
ServerNameToVerify string `json:"serverNameToVerify"`
VerifyPeerCertInNames []string `json:"verifyPeerCertInNames"`
ECHConfigList string `json:"echConfigList"`
ECHServerKeys string `json:"echServerKeys"`
ECHConfigList string `json:"echConfigList"`
ECHForceQuery bool `json:"echForceQuery"`
}
// Build implements Buildable.
@@ -485,8 +486,6 @@ func (c *TLSConfig) Build() (proto.Message, error) {
}
config.VerifyPeerCertInNames = c.VerifyPeerCertInNames
config.EchConfigList = c.ECHConfigList
if c.ECHServerKeys != "" {
EchPrivateKey, err := base64.StdEncoding.DecodeString(c.ECHServerKeys)
if err != nil {
@@ -494,6 +493,8 @@ func (c *TLSConfig) Build() (proto.Message, error) {
}
config.EchServerKeys = EchPrivateKey
}
config.EchForceQuery = c.ECHForceQuery
config.EchConfigList = c.ECHConfigList
return config, nil
}

View File

@@ -21,6 +21,7 @@ import (
var (
inboundConfigLoader = NewJSONConfigLoader(ConfigCreatorCache{
"tunnel": func() interface{} { return new(DokodemoConfig) },
"dokodemo-door": func() interface{} { return new(DokodemoConfig) },
"http": func() interface{} { return new(HTTPServerConfig) },
"shadowsocks": func() interface{} { return new(ShadowsocksServerConfig) },
@@ -33,8 +34,10 @@ var (
}, "protocol", "settings")
outboundConfigLoader = NewJSONConfigLoader(ConfigCreatorCache{
"block": func() interface{} { return new(BlackholeConfig) },
"blackhole": func() interface{} { return new(BlackholeConfig) },
"loopback": func() interface{} { return new(LoopbackConfig) },
"direct": func() interface{} { return new(FreedomConfig) },
"freedom": func() interface{} { return new(FreedomConfig) },
"http": func() interface{} { return new(HTTPClientConfig) },
"shadowsocks": func() interface{} { return new(ShadowsocksClientConfig) },
@@ -242,7 +245,7 @@ func (c *InboundDetourConfig) Build() (*core.InboundHandlerConfig, error) {
return nil, errors.New("failed to load inbound detour config for protocol ", c.Protocol).Base(err)
}
if dokodemoConfig, ok := rawConfig.(*DokodemoConfig); ok {
receiverSettings.ReceiveOriginalDestination = dokodemoConfig.Redirect
receiverSettings.ReceiveOriginalDestination = dokodemoConfig.FollowRedirect
}
ts, err := rawConfig.(Buildable).Build()
if err != nil {

View File

@@ -3,6 +3,8 @@ package dokodemo
import (
"context"
"runtime"
"strconv"
"strings"
"sync/atomic"
"github.com/xtls/xray-core/common"
@@ -73,6 +75,25 @@ func (d *DokodemoDoor) Process(ctx context.Context, network net.Network, conn st
Port: d.port,
}
if !d.config.FollowRedirect {
host, port, err := net.SplitHostPort(conn.LocalAddr().String())
if dest.Address == nil {
if err != nil {
dest.Address = net.DomainAddress("localhost")
} else {
if strings.Contains(host, ".") {
dest.Address = net.LocalHostIP
} else {
dest.Address = net.LocalHostIPv6
}
}
}
if dest.Port == 0 {
dest.Port = net.Port(common.Must2(strconv.Atoi(port)).(int))
}
}
destinationOverridden := false
if d.config.FollowRedirect {
outbounds := session.OutboundsFromContext(ctx)

View File

@@ -217,8 +217,9 @@ type Config struct {
// @Document After allow_insecure (automatically), if the server's cert can't be verified by any of these names, pinned_peer_certificate_chain_sha256 will be tried.
// @Critical
VerifyPeerCertInNames []string `protobuf:"bytes,17,rep,name=verify_peer_cert_in_names,json=verifyPeerCertInNames,proto3" json:"verify_peer_cert_in_names,omitempty"`
EchConfigList string `protobuf:"bytes,18,opt,name=ech_config_list,json=echConfigList,proto3" json:"ech_config_list,omitempty"`
EchServerKeys []byte `protobuf:"bytes,19,opt,name=ech_server_keys,json=echServerKeys,proto3" json:"ech_server_keys,omitempty"`
EchServerKeys []byte `protobuf:"bytes,18,opt,name=ech_server_keys,json=echServerKeys,proto3" json:"ech_server_keys,omitempty"`
EchConfigList string `protobuf:"bytes,19,opt,name=ech_config_list,json=echConfigList,proto3" json:"ech_config_list,omitempty"`
EchForceQuery bool `protobuf:"varint,20,opt,name=ech_force_query,json=echForceQuery,proto3" json:"ech_force_query,omitempty"`
}
func (x *Config) Reset() {
@@ -363,6 +364,13 @@ func (x *Config) GetVerifyPeerCertInNames() []string {
return nil
}
func (x *Config) GetEchServerKeys() []byte {
if x != nil {
return x.EchServerKeys
}
return nil
}
func (x *Config) GetEchConfigList() string {
if x != nil {
return x.EchConfigList
@@ -370,11 +378,11 @@ func (x *Config) GetEchConfigList() string {
return ""
}
func (x *Config) GetEchServerKeys() []byte {
func (x *Config) GetEchForceQuery() bool {
if x != nil {
return x.EchServerKeys
return x.EchForceQuery
}
return nil
return false
}
var File_transport_internet_tls_config_proto protoreflect.FileDescriptor
@@ -408,7 +416,7 @@ var file_transport_internet_tls_config_proto_rawDesc = []byte{
0x4e, 0x43, 0x49, 0x50, 0x48, 0x45, 0x52, 0x4d, 0x45, 0x4e, 0x54, 0x10, 0x00, 0x12, 0x14, 0x0a,
0x10, 0x41, 0x55, 0x54, 0x48, 0x4f, 0x52, 0x49, 0x54, 0x59, 0x5f, 0x56, 0x45, 0x52, 0x49, 0x46,
0x59, 0x10, 0x01, 0x12, 0x13, 0x0a, 0x0f, 0x41, 0x55, 0x54, 0x48, 0x4f, 0x52, 0x49, 0x54, 0x59,
0x5f, 0x49, 0x53, 0x53, 0x55, 0x45, 0x10, 0x02, 0x22, 0xea, 0x06, 0x0a, 0x06, 0x43, 0x6f, 0x6e,
0x5f, 0x49, 0x53, 0x53, 0x55, 0x45, 0x10, 0x02, 0x22, 0x92, 0x07, 0x0a, 0x06, 0x43, 0x6f, 0x6e,
0x66, 0x69, 0x67, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x69, 0x6e, 0x73,
0x65, 0x63, 0x75, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x61, 0x6c, 0x6c,
0x6f, 0x77, 0x49, 0x6e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x12, 0x4a, 0x0a, 0x0b, 0x63, 0x65,
@@ -458,20 +466,22 @@ var file_transport_internet_tls_config_proto_rawDesc = []byte{
0x65, 0x72, 0x69, 0x66, 0x79, 0x5f, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x5f,
0x69, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x11, 0x20, 0x03, 0x28, 0x09, 0x52, 0x15,
0x76, 0x65, 0x72, 0x69, 0x66, 0x79, 0x50, 0x65, 0x65, 0x72, 0x43, 0x65, 0x72, 0x74, 0x49, 0x6e,
0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x65, 0x63, 0x68, 0x5f, 0x63, 0x6f, 0x6e,
0x66, 0x69, 0x67, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d,
0x65, 0x63, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x26, 0x0a,
0x0f, 0x65, 0x63, 0x68, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x6b, 0x65, 0x79, 0x73,
0x18, 0x13, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x65, 0x63, 0x68, 0x53, 0x65, 0x72, 0x76, 0x65,
0x72, 0x4b, 0x65, 0x79, 0x73, 0x42, 0x73, 0x0a, 0x1f, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61,
0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65,
0x72, 0x6e, 0x65, 0x74, 0x2e, 0x74, 0x6c, 0x73, 0x50, 0x01, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68,
0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79,
0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f,
0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x74, 0x6c, 0x73, 0xaa, 0x02, 0x1b, 0x58,
0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e,
0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x54, 0x6c, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x33,
0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x65, 0x63, 0x68, 0x5f, 0x73, 0x65, 0x72,
0x76, 0x65, 0x72, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d,
0x65, 0x63, 0x68, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x26, 0x0a,
0x0f, 0x65, 0x63, 0x68, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x6c, 0x69, 0x73, 0x74,
0x18, 0x13, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x65, 0x63, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69,
0x67, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x65, 0x63, 0x68, 0x5f, 0x66, 0x6f, 0x72,
0x63, 0x65, 0x5f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x14, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d,
0x65, 0x63, 0x68, 0x46, 0x6f, 0x72, 0x63, 0x65, 0x51, 0x75, 0x65, 0x72, 0x79, 0x42, 0x73, 0x0a,
0x1f, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70,
0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x74, 0x6c, 0x73,
0x50, 0x01, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78,
0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x74, 0x72,
0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74,
0x2f, 0x74, 0x6c, 0x73, 0xaa, 0x02, 0x1b, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e,
0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x54,
0x6c, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (

View File

@@ -92,7 +92,9 @@ message Config {
*/
repeated string verify_peer_cert_in_names = 17;
string ech_config_list = 18;
bytes ech_server_keys = 18;
bytes ech_server_keys = 19;
}
string ech_config_list = 19;
bool ech_force_query = 20;
}

View File

@@ -32,8 +32,26 @@ func ApplyECH(c *Config, config *tls.Config) error {
nameToQuery := c.ServerName
var DNSServer string
// for server
if len(c.EchServerKeys) != 0 {
KeySets, err := ConvertToGoECHKeys(c.EchServerKeys)
if err != nil {
return errors.New("Failed to unmarshal ECHKeySetList: ", err)
}
config.EncryptedClientHelloKeys = KeySets
}
// for client
if len(c.EchConfigList) != 0 {
defer func() {
// if failed to get ECHConfig, use an invalid one to make connection fail
if err != nil {
if c.EchForceQuery {
ECHConfig = []byte{1, 1, 4, 5, 1, 4}
}
}
config.EncryptedClientHelloConfigList = ECHConfig
}()
// direct base64 config
if strings.Contains(c.EchConfigList, "://") {
// query config from dns
@@ -51,7 +69,7 @@ func ApplyECH(c *Config, config *tls.Config) error {
if nameToQuery == "" {
return errors.New("Using DNS for ECH Config needs serverName or use Server format example.com+https://1.1.1.1/dns-query")
}
ECHConfig, err = QueryRecord(nameToQuery, DNSServer)
ECHConfig, err = QueryRecord(nameToQuery, DNSServer, c.EchForceQuery)
if err != nil {
return err
}
@@ -61,17 +79,6 @@ func ApplyECH(c *Config, config *tls.Config) error {
return errors.New("Failed to unmarshal ECHConfigList: ", err)
}
}
config.EncryptedClientHelloConfigList = ECHConfig
}
// for server
if len(c.EchServerKeys) != 0 {
KeySets, err := ConvertToGoECHKeys(c.EchServerKeys)
if err != nil {
return errors.New("Failed to unmarshal ECHKeySetList: ", err)
}
config.EncryptedClientHelloKeys = KeySets
}
return nil
@@ -86,9 +93,11 @@ type ECHConfigCache struct {
type echConfigRecord struct {
config []byte
expire time.Time
err error
}
var (
// key value must be like this: "example.com|udp://1.1.1.1"
GlobalECHConfigCache = utils.NewTypedSyncMap[string, *ECHConfigCache]()
clientForECHDOH = utils.NewTypedSyncMap[string, *http.Client]()
)
@@ -96,7 +105,7 @@ var (
// Update updates the ECH config for given domain and server.
// this method is concurrent safe, only one update request will be sent, others get the cache.
// if isLockedUpdate is true, it will not try to acquire the lock.
func (c *ECHConfigCache) Update(domain string, server string, isLockedUpdate bool) ([]byte, error) {
func (c *ECHConfigCache) Update(domain string, server string, forceQuery bool, isLockedUpdate bool) ([]byte, error) {
if !isLockedUpdate {
c.UpdateLock.Lock()
defer c.UpdateLock.Unlock()
@@ -105,13 +114,23 @@ func (c *ECHConfigCache) Update(domain string, server string, isLockedUpdate boo
configRecord := c.configRecord.Load()
if configRecord.expire.After(time.Now()) {
errors.LogDebug(context.Background(), "Cache hit for domain after double check: ", domain)
return configRecord.config, nil
return configRecord.config, configRecord.err
}
// Query ECH config from DNS server
errors.LogDebug(context.Background(), "Trying to query ECH config for domain: ", domain, " with ECH server: ", server)
echConfig, ttl, err := dnsQuery(server, domain)
if err != nil {
return nil, err
if forceQuery {
return nil, err
} else {
configRecord = &echConfigRecord{
config: nil,
expire: time.Now().Add(10 * time.Minute),
err: err,
}
c.configRecord.Store(configRecord)
return echConfig, err
}
}
configRecord = &echConfigRecord{
config: echConfig,
@@ -123,30 +142,31 @@ func (c *ECHConfigCache) Update(domain string, server string, isLockedUpdate boo
// QueryRecord returns the ECH config for given domain.
// If the record is not in cache or expired, it will query the DNS server and update the cache.
func QueryRecord(domain string, server string) ([]byte, error) {
echConfigCache, ok := GlobalECHConfigCache.Load(domain)
func QueryRecord(domain string, server string, forceQuery bool) ([]byte, error) {
GlobalECHConfigCacheKey := domain + "|" + server
echConfigCache, ok := GlobalECHConfigCache.Load(GlobalECHConfigCacheKey)
if !ok {
echConfigCache = &ECHConfigCache{}
echConfigCache.configRecord.Store(&echConfigRecord{})
echConfigCache, _ = GlobalECHConfigCache.LoadOrStore(domain, echConfigCache)
echConfigCache, _ = GlobalECHConfigCache.LoadOrStore(GlobalECHConfigCacheKey, echConfigCache)
}
configRecord := echConfigCache.configRecord.Load()
if configRecord.expire.After(time.Now()) {
errors.LogDebug(context.Background(), "Cache hit for domain: ", domain)
return configRecord.config, nil
return configRecord.config, configRecord.err
}
// If expire is zero value, it means we are in initial state, wait for the query to finish
// otherwise return old value immediately and update in a goroutine
// but if the cache is too old, wait for update
if configRecord.expire == (time.Time{}) || configRecord.expire.Add(time.Hour*6).Before(time.Now()) {
return echConfigCache.Update(domain, server, false)
return echConfigCache.Update(domain, server, false, forceQuery)
} else {
// If someone already acquired the lock, it means it is updating, do not start another update goroutine
if echConfigCache.UpdateLock.TryLock() {
go func() {
defer echConfigCache.UpdateLock.Unlock()
echConfigCache.Update(domain, server, true)
echConfigCache.Update(domain, server, true, forceQuery)
}()
}
return configRecord.config, nil
@@ -165,7 +185,7 @@ func dnsQuery(server string, domain string) ([]byte, uint32, error) {
m.Id = 0
msg, err := m.Pack()
if err != nil {
return []byte{}, 0, err
return nil, 0, err
}
var client *http.Client
if client, _ = clientForECHDOH.Load(server); client == nil {
@@ -194,20 +214,20 @@ func dnsQuery(server string, domain string) ([]byte, uint32, error) {
}
req, err := http.NewRequest("POST", server, bytes.NewReader(msg))
if err != nil {
return []byte{}, 0, err
return nil, 0, err
}
req.Header.Set("Content-Type", "application/dns-message")
resp, err := client.Do(req)
if err != nil {
return []byte{}, 0, err
return nil, 0, err
}
defer resp.Body.Close()
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return []byte{}, 0, err
return nil, 0, err
}
if resp.StatusCode != http.StatusOK {
return []byte{}, 0, errors.New("query failed with response code:", resp.StatusCode)
return nil, 0, errors.New("query failed with response code:", resp.StatusCode)
}
dnsResolve = respBody
} else if strings.HasPrefix(server, "udp://") { // for classic udp dns server
@@ -231,24 +251,25 @@ func dnsQuery(server string, domain string) ([]byte, uint32, error) {
}
}()
if err != nil {
return []byte{}, 0, err
return nil, 0, err
}
msg, err := m.Pack()
if err != nil {
return []byte{}, 0, err
return nil, 0, err
}
conn.Write(msg)
udpResponse := make([]byte, 512)
conn.SetReadDeadline(time.Now().Add(5 * time.Second))
_, err = conn.Read(udpResponse)
if err != nil {
return []byte{}, 0, err
return nil, 0, err
}
dnsResolve = udpResponse
}
respMsg := new(dns.Msg)
err := respMsg.Unpack(dnsResolve)
if err != nil {
return []byte{}, 0, errors.New("failed to unpack dns response for ECH: ", err)
return nil, 0, errors.New("failed to unpack dns response for ECH: ", err)
}
if len(respMsg.Answer) > 0 {
for _, answer := range respMsg.Answer {
@@ -262,7 +283,7 @@ func dnsQuery(server string, domain string) ([]byte, uint32, error) {
}
}
}
return []byte{}, 0, errors.New("no ech record found")
return nil, 0, errors.New("no ech record found")
}
// reference github.com/OmarTariq612/goech

View File

@@ -1,4 +1,4 @@
package tls_test
package tls
import (
"io"
@@ -8,13 +8,12 @@ import (
"testing"
"github.com/xtls/xray-core/common"
. "github.com/xtls/xray-core/transport/internet/tls"
)
func TestECHDial(t *testing.T) {
config := &Config{
ServerName: "encryptedsni.com",
EchConfigList: "udp://1.1.1.1",
ServerName: "cloudflare.com",
EchConfigList: "encryptedsni.com+udp://1.1.1.1",
}
// test concurrent Dial(to test cache problem)
wg := sync.WaitGroup{}
@@ -28,7 +27,7 @@ func TestECHDial(t *testing.T) {
TLSClientConfig: TLSConfig,
},
}
resp, err := client.Get("https://encryptedsni.com/cdn-cgi/trace")
resp, err := client.Get("https://cloudflare.com/cdn-cgi/trace")
common.Must(err)
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
@@ -40,4 +39,51 @@ func TestECHDial(t *testing.T) {
}()
}
wg.Wait()
// check cache
echConfigCache, ok := GlobalECHConfigCache.Load("encryptedsni.com|udp://1.1.1.1")
if !ok {
t.Error("ECH config cache not found")
}
ok = echConfigCache.UpdateLock.TryLock()
if !ok {
t.Error("ECH config cache dead lock detected")
}
echConfigCache.UpdateLock.Unlock()
configRecord := echConfigCache.configRecord.Load()
if configRecord == nil {
t.Error("ECH config record not found in cache")
}
}
func TestECHDialFail(t *testing.T) {
config := &Config{
ServerName: "cloudflare.com",
EchConfigList: "udp://1.1.1.1",
}
TLSConfig := config.GetTLSConfig()
TLSConfig.NextProtos = []string{"http/1.1"}
client := &http.Client{
Transport: &http.Transport{
TLSClientConfig: TLSConfig,
},
}
resp, err := client.Get("https://cloudflare.com/cdn-cgi/trace")
common.Must(err)
defer resp.Body.Close()
_, err = io.ReadAll(resp.Body)
common.Must(err)
// check cache
echConfigCache, ok := GlobalECHConfigCache.Load("cloudflare.com|udp://1.1.1.1")
if !ok {
t.Error("ECH config cache not found")
}
configRecord := echConfigCache.configRecord.Load()
if configRecord == nil {
t.Error("ECH config record not found in cache")
return
}
if configRecord.err == nil {
t.Error("unexpected nil error in ECH config record")
}
}

View File

@@ -1,104 +1,107 @@
from .common import InfoExtractor
from ..networking.exceptions import HTTPError
from .streaks import StreaksBaseIE
from ..utils import (
ExtractorError,
clean_html,
int_or_none,
str_or_none,
unified_timestamp,
urljoin,
url_or_none,
)
from ..utils.traversal import find_element, traverse_obj
from ..utils.traversal import traverse_obj
class TBSJPEpisodeIE(InfoExtractor):
class TBSJPBaseIE(StreaksBaseIE):
def _search_window_app_json(self, webpage, name, item_id, **kwargs):
return self._search_json(r'window\.app\s*=', webpage, f'{name} info', item_id, **kwargs)
class TBSJPEpisodeIE(TBSJPBaseIE):
_VALID_URL = r'https?://cu\.tbs\.co\.jp/episode/(?P<id>[\d_]+)'
_GEO_BYPASS = False
_TESTS = [{
'url': 'https://cu.tbs.co.jp/episode/23613_2044134_1000049010',
'skip': 'streams geo-restricted, Japan only. Also, will likely expire eventually',
'url': 'https://cu.tbs.co.jp/episode/14694_2094162_1000123656',
'skip': 'geo-blocked to japan + 7-day expiry',
'info_dict': {
'title': 'VIVANT 第三話 誤送金完結へ!絶体絶命の反撃開始',
'id': '23613_2044134_1000049010',
'title': 'クロちゃん、寝て起きたら川のほとりにいてその向こう岸に亡くなった父親がいたら死の淵にいるかと思う説 ほか',
'id': '14694_2094162_1000123656',
'ext': 'mp4',
'upload_date': '20230728',
'duration': 3517,
'release_timestamp': 1691118230,
'episode': '第三話 誤送金完結へ!絶体絶命の反撃開始',
'release_date': '20230804',
'categories': 'count:11',
'episode_number': 3,
'timestamp': 1690522538,
'description': 'md5:2b796341af1ef772034133174ba4a895',
'series': 'VIVANT',
'display_id': 'ref:14694_2094162_1000123656',
'description': 'md5:1a82fcdeb5e2e82190544bb72721c46e',
'uploader': 'TBS',
'uploader_id': 'tbs',
'duration': 2752,
'thumbnail': 'md5:d8855c8c292683c95a84cafdb42300bc',
'categories': ['エンタメ', '水曜日のダウンタウン', 'ダウンタウン', '浜田雅功', '松本人志', '水ダウ', '動画', 'バラエティ'],
'cast': ['浜田 雅功', '藤本 敏史', 'ビビる 大木', '千原 ジュニア', '横澤 夏子', 'せいや', 'あの', '服部 潤'],
'genres': ['variety'],
'series': '水曜日のダウンタウン',
'series_id': '14694',
'episode': 'クロちゃん、寝て起きたら川のほとりにいてその向こう岸に亡くなった父親がいたら死の淵にいるかと思う説 ほか',
'episode_number': 341,
'episode_id': '14694_2094162_1000123656',
'timestamp': 1753778992,
'upload_date': '20250729',
'release_timestamp': 1753880402,
'release_date': '20250730',
'modified_timestamp': 1753880741,
'modified_date': '20250730',
'live_status': 'not_live',
},
}]
def _real_extract(self, url):
video_id = self._match_id(url)
webpage = self._download_webpage(url, video_id)
meta = self._search_json(r'window\.app\s*=', webpage, 'episode info', video_id, fatal=False)
meta = self._search_window_app_json(webpage, 'episode', video_id, fatal=False)
episode = traverse_obj(meta, ('falcorCache', 'catalog', 'episode', video_id, 'value'))
tf_path = self._search_regex(
r'<script[^>]+src=["\'](/assets/tf\.[^"\']+\.js)["\']', webpage, 'stream API config')
tf_js = self._download_webpage(urljoin(url, tf_path), video_id, note='Downloading stream API config')
video_url = self._search_regex(r'videoPlaybackUrl:\s*[\'"]([^\'"]+)[\'"]', tf_js, 'stream API url')
api_key = self._search_regex(r'api_key:\s*[\'"]([^\'"]+)[\'"]', tf_js, 'stream API key')
try:
source_meta = self._download_json(f'{video_url}ref:{video_id}', video_id,
headers={'X-Streaks-Api-Key': api_key},
note='Downloading stream metadata')
except ExtractorError as e:
if isinstance(e.cause, HTTPError) and e.cause.status == 403:
self.raise_geo_restricted(countries=['JP'])
raise
formats, subtitles = [], {}
for src in traverse_obj(source_meta, ('sources', ..., 'src')):
fmts, subs = self._extract_m3u8_formats_and_subtitles(src, video_id, fatal=False)
formats.extend(fmts)
self._merge_subtitles(subs, target=subtitles)
return {
'title': traverse_obj(webpage, ({find_element(tag='h3')}, {clean_html})),
'id': video_id,
**self._extract_from_streaks_api(
'tbs', f'ref:{video_id}', headers={'Origin': 'https://cu.tbs.co.jp'}),
**traverse_obj(episode, {
'categories': ('keywords', {list}),
'id': ('content_id', {str}),
'description': ('description', 0, 'value'),
'timestamp': ('created_at', {unified_timestamp}),
'release_timestamp': ('pub_date', {unified_timestamp}),
'title': ('title', ..., 'value', {str}, any),
'cast': (
'credit', ..., 'name', ..., 'value', {clean_html}, any,
{lambda x: x.split(',')}, ..., {str.strip}, filter, all, filter),
'categories': ('keywords', ..., {str}, filter, all, filter),
'description': ('description', ..., 'value', {clean_html}, any),
'duration': ('tv_episode_info', 'duration', {int_or_none}),
'episode': ('title', lambda _, v: not v.get('is_phonetic'), 'value', {str}, any),
'episode_id': ('content_id', {str}),
'episode_number': ('tv_episode_info', 'episode_number', {int_or_none}),
'episode': ('title', lambda _, v: not v.get('is_phonetic'), 'value'),
'series': ('custom_data', 'program_name'),
}, get_all=False),
'formats': formats,
'subtitles': subtitles,
'genres': ('genre', ..., {str}, filter, all, filter),
'release_timestamp': ('pub_date', {unified_timestamp}),
'series': ('custom_data', 'program_name', {str}),
'tags': ('tags', ..., {str}, filter, all, filter),
'thumbnail': ('artwork', ..., 'url', {url_or_none}, any),
'timestamp': ('created_at', {unified_timestamp}),
'uploader': ('tv_show_info', 'networks', ..., {str}, any),
}),
**traverse_obj(episode, ('tv_episode_info', {
'duration': ('duration', {int_or_none}),
'episode_number': ('episode_number', {int_or_none}),
'series_id': ('show_content_id', {str}),
})),
'id': video_id,
}
class TBSJPProgramIE(InfoExtractor):
class TBSJPProgramIE(TBSJPBaseIE):
_VALID_URL = r'https?://cu\.tbs\.co\.jp/program/(?P<id>\d+)'
_TESTS = [{
'url': 'https://cu.tbs.co.jp/program/23601',
'playlist_mincount': 4,
'url': 'https://cu.tbs.co.jp/program/14694',
'playlist_mincount': 1,
'info_dict': {
'id': '23601',
'categories': ['エンタメ', 'ミライカプセル', '会社', '働く', 'バラエティ', '動画'],
'description': '幼少期の夢は大人になって、どう成長したのだろうか?\nそしてその夢は今後、どのように広がっていくのか?\nいま話題の会社で働く人の「夢の成長」を描く',
'series': 'ミライカプセル -I have a dream-',
'title': 'ミライカプセル -I have a dream-',
'id': '14694',
'title': '水曜日のダウンタウン',
'description': 'md5:cf1d46c76c2755d7f87512498718b837',
'categories': ['エンタメ', '水曜日のダウンタウン', 'ダウンタウン', '浜田雅功', '松本人志', '水ダウ', '動画', 'バラエティ'],
'series': '水曜日のダウンタウン',
},
}]
def _real_extract(self, url):
programme_id = self._match_id(url)
webpage = self._download_webpage(url, programme_id)
meta = self._search_json(r'window\.app\s*=', webpage, 'programme info', programme_id)
meta = self._search_window_app_json(webpage, 'programme', programme_id)
programme = traverse_obj(meta, ('falcorCache', 'catalog', 'program', programme_id, 'false', 'value'))
return {
@@ -116,7 +119,7 @@ class TBSJPProgramIE(InfoExtractor):
}
class TBSJPPlaylistIE(InfoExtractor):
class TBSJPPlaylistIE(TBSJPBaseIE):
_VALID_URL = r'https?://cu\.tbs\.co\.jp/playlist/(?P<id>[\da-f]+)'
_TESTS = [{
'url': 'https://cu.tbs.co.jp/playlist/184f9970e7ba48e4915f1b252c55015e',
@@ -129,8 +132,8 @@ class TBSJPPlaylistIE(InfoExtractor):
def _real_extract(self, url):
playlist_id = self._match_id(url)
page = self._download_webpage(url, playlist_id)
meta = self._search_json(r'window\.app\s*=', page, 'playlist info', playlist_id)
webpage = self._download_webpage(url, playlist_id)
meta = self._search_window_app_json(webpage, 'playlist', playlist_id)
playlist = traverse_obj(meta, ('falcorCache', 'playList', playlist_id))
def entries():