From ee7416bae7c3bb8dfa7d3edcde8b2ed6a666a5a8 Mon Sep 17 00:00:00 2001 From: "github-action[bot]" Date: Tue, 25 Nov 2025 19:40:52 +0100 Subject: [PATCH] Update On Tue Nov 25 19:40:51 CET 2025 --- .github/update.log | 1 + clash-meta/go.mod | 2 +- clash-meta/go.sum | 4 +- clash-nyanpasu/backend/Cargo.lock | 232 ++++++--- clash-nyanpasu/frontend/nyanpasu/package.json | 2 +- clash-nyanpasu/manifest/version.json | 4 +- clash-nyanpasu/pnpm-lock.yaml | 10 +- lede/package/boot/uboot-rk35xx/Makefile | 83 ---- .../patches/001-cmd-fix_source.patch | 22 - .../patches/010-migrate-to-python3.patch | 16 - .../patches/100-disable-optee-build.patch | 17 - .../patches/110-fix-build-with-gcc13.patch | 22 - .../200-fallback-to-legacy-image-boot.patch | 44 -- mieru/apis/log/log.go | 48 ++ mieru/pkg/log/callback.go | 24 + mieru/pkg/log/entry.go | 25 +- mieru/pkg/log/entry_test.go | 55 ++- mieru/pkg/log/exported.go | 16 +- mieru/pkg/log/formatter.go | 10 +- mieru/pkg/log/formatter_test.go | 57 +++ mieru/pkg/log/logger.go | 59 +-- .../log/{mieru.go => mieru_client_logging.go} | 7 - mieru/pkg/log/mieru_init.go | 25 + .../{output.go => mieru_test_log_adaptor.go} | 0 .../cmd/sockshttpclient/sockshttpclient.go | 11 +- mihomo/go.mod | 2 +- mihomo/go.sum | 4 +- nodepass/docs/en/api.md | 6 +- nodepass/docs/en/configuration.md | 96 ++-- nodepass/docs/en/examples.md | 152 +++--- nodepass/docs/en/how-it-works.md | 5 - nodepass/docs/en/troubleshooting.md | 30 +- nodepass/docs/en/usage.md | 12 +- nodepass/docs/zh/api.md | 6 +- nodepass/docs/zh/configuration.md | 96 ++-- nodepass/docs/zh/examples.md | 152 +++--- nodepass/docs/zh/how-it-works.md | 5 - nodepass/docs/zh/troubleshooting.md | 30 +- nodepass/docs/zh/usage.md | 12 +- nodepass/go.mod | 5 +- nodepass/go.sum | 14 +- nodepass/internal/client.go | 25 +- nodepass/internal/common.go | 440 +++++++++++------- nodepass/internal/master.go | 9 +- nodepass/internal/server.go | 182 +++++--- openwrt-packages/openlist2/Makefile | 8 +- .../luasrc/controller/passwall.lua | 27 +- .../view/passwall/node_list/node_list.htm | 72 ++- .../luci-app-passwall/po/zh-cn/passwall.po | 6 + small/v2ray-geodata/Makefile | 4 +- .../v2rayN/ServiceLib/Handler/Fmt/BaseFmt.cs | 18 +- 51 files changed, 1211 insertions(+), 1003 deletions(-) delete mode 100644 lede/package/boot/uboot-rk35xx/Makefile delete mode 100644 lede/package/boot/uboot-rk35xx/patches/001-cmd-fix_source.patch delete mode 100644 lede/package/boot/uboot-rk35xx/patches/010-migrate-to-python3.patch delete mode 100644 lede/package/boot/uboot-rk35xx/patches/100-disable-optee-build.patch delete mode 100644 lede/package/boot/uboot-rk35xx/patches/110-fix-build-with-gcc13.patch delete mode 100644 lede/package/boot/uboot-rk35xx/patches/200-fallback-to-legacy-image-boot.patch create mode 100644 mieru/apis/log/log.go create mode 100644 mieru/pkg/log/callback.go rename mieru/pkg/log/{mieru.go => mieru_client_logging.go} (94%) create mode 100644 mieru/pkg/log/mieru_init.go rename mieru/pkg/log/{output.go => mieru_test_log_adaptor.go} (100%) diff --git a/.github/update.log b/.github/update.log index 5158671196..74818a7810 100644 --- a/.github/update.log +++ b/.github/update.log @@ -1192,3 +1192,4 @@ Update On Fri Nov 21 19:35:09 CET 2025 Update On Sat Nov 22 19:36:29 CET 2025 Update On Sun Nov 23 19:36:55 CET 2025 Update On Mon Nov 24 19:40:17 CET 2025 +Update On Tue Nov 25 19:40:43 CET 2025 diff --git a/clash-meta/go.mod b/clash-meta/go.mod index 3c1ab95762..2047d0eb11 100644 --- a/clash-meta/go.mod +++ b/clash-meta/go.mod @@ -32,7 +32,7 @@ require ( github.com/metacubex/sing-shadowsocks v0.2.12 github.com/metacubex/sing-shadowsocks2 v0.2.7 github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2 - github.com/metacubex/sing-tun v0.4.10-0.20251124160354-85cd06f11a84 + github.com/metacubex/sing-tun v0.4.9 github.com/metacubex/sing-vmess v0.2.4 github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f github.com/metacubex/smux v0.0.0-20251111013112-03f8d12dafc1 diff --git a/clash-meta/go.sum b/clash-meta/go.sum index 74a5c27619..bc29d7fef7 100644 --- a/clash-meta/go.sum +++ b/clash-meta/go.sum @@ -129,8 +129,8 @@ github.com/metacubex/sing-shadowsocks2 v0.2.7 h1:hSuuc0YpsfiqYqt1o+fP4m34BQz4e6w github.com/metacubex/sing-shadowsocks2 v0.2.7/go.mod h1:vOEbfKC60txi0ca+yUlqEwOGc3Obl6cnSgx9Gf45KjE= 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.10-0.20251124160354-85cd06f11a84 h1:PlVO8aCeAnVUsvO9X077iX9wz23nSnbRjtPRdE0GsY8= -github.com/metacubex/sing-tun v0.4.10-0.20251124160354-85cd06f11a84/go.mod h1:L/TjQY5JEGy8nvsuYmy/XgMFMCPiF0+AWSFCYfS6r9w= +github.com/metacubex/sing-tun v0.4.9 h1:jY0Yyt8nnN3yQRN/jTxgqNCmGi1dsFdxdIi7pQUlVVU= +github.com/metacubex/sing-tun v0.4.9/go.mod h1:L/TjQY5JEGy8nvsuYmy/XgMFMCPiF0+AWSFCYfS6r9w= github.com/metacubex/sing-vmess v0.2.4 h1:Tx6AGgCiEf400E/xyDuYyafsel6sGbR8oF7RkAaus6I= github.com/metacubex/sing-vmess v0.2.4/go.mod h1:21R5R1u90uUvBQF0owoooEu96/SAYYD56nDrwm6nFaM= github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f h1:Sr/DYKYofKHKc4GF3qkRGNuj6XA6c0eqPgEDN+VAsYU= diff --git a/clash-nyanpasu/backend/Cargo.lock b/clash-nyanpasu/backend/Cargo.lock index b89e781041..8ea04ac39f 100644 --- a/clash-nyanpasu/backend/Cargo.lock +++ b/clash-nyanpasu/backend/Cargo.lock @@ -168,6 +168,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "aligned" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "377e4c0ba83e4431b10df45c1d4666f178ea9c552cac93e60c3a88bf32785923" +dependencies = [ + "as-slice", +] + [[package]] name = "aligned-vec" version = "0.6.4" @@ -346,7 +355,7 @@ dependencies = [ "objc2-foundation 0.3.2", "parking_lot", "percent-encoding", - "windows-sys 0.60.2", + "windows-sys 0.52.0", "wl-clipboard-rs", "x11rb", ] @@ -386,6 +395,15 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b" +[[package]] +name = "as-slice" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "516b6b4f0e40d50dcda9365d53964ec74560ad4284da2e7fc97122cd83174516" +dependencies = [ + "stable_deref_trait", +] + [[package]] name = "ash" version = "0.38.0+1.3.281" @@ -685,6 +703,26 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +[[package]] +name = "av-scenechange" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f321d77c20e19b92c39e7471cf986812cbb46659d2af674adc4331ef3f18394" +dependencies = [ + "aligned", + "anyhow", + "arg_enum_proc_macro", + "arrayvec", + "log", + "num-rational", + "num-traits", + "pastey", + "rayon", + "thiserror 2.0.17", + "v_frame", + "y4m", +] + [[package]] name = "av1-grain" version = "0.2.4" @@ -850,7 +888,7 @@ dependencies = [ "bitflags 2.9.4", "cexpr", "clang-sys", - "itertools 0.12.1", + "itertools 0.10.5", "lazy_static", "lazycell", "log", @@ -902,9 +940,12 @@ dependencies = [ [[package]] name = "bitstream-io" -version = "2.6.0" +version = "4.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6099cdc01846bc367c4e7dd630dc5966dccf36b652fae7a74e17b640411a91b2" +checksum = "60d4bd9d1db2c6bdf285e223a7fa369d5ce98ec767dec949c6ca62863ce61757" +dependencies = [ + "core2", +] [[package]] name = "block" @@ -971,7 +1012,7 @@ dependencies = [ "boa_interner", "boa_macros", "boa_string", - "indexmap 2.12.0", + "indexmap 2.12.1", "num-bigint", "rustc-hash 2.1.1", ] @@ -997,7 +1038,7 @@ dependencies = [ "fast-float2", "hashbrown 0.15.5", "icu_normalizer 1.5.0", - "indexmap 2.12.0", + "indexmap 2.12.1", "intrusive-collections", "itertools 0.13.0", "num-bigint", @@ -1043,7 +1084,7 @@ dependencies = [ "boa_gc", "boa_macros", "hashbrown 0.15.5", - "indexmap 2.12.0", + "indexmap 2.12.1", "once_cell", "phf 0.11.3", "rustc-hash 2.1.1", @@ -1166,9 +1207,9 @@ dependencies = [ [[package]] name = "built" -version = "0.7.7" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56ed6191a7e78c36abdb16ab65341eefd73d64d303fffccdbb00d51e4205967b" +checksum = "f4ad8f11f288f48ca24471bbd51ac257aaeaaa07adae295591266b792902ae64" [[package]] name = "bumpalo" @@ -1459,9 +1500,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.51" +version = "4.5.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c26d721170e0295f191a69bd9a1f93efcdb0aff38684b61ab5750468972e5f5" +checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8" dependencies = [ "clap_builder", "clap_derive", @@ -1469,9 +1510,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.51" +version = "4.5.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75835f0c7bf681bfd05abe44e965760fea999a5286c6eb2d59883634fd02011a" +checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00" dependencies = [ "anstream", "anstyle", @@ -1542,7 +1583,7 @@ dependencies = [ "hex", "humansize", "image", - "indexmap 2.12.0", + "indexmap 2.12.1", "itertools 0.14.0", "log", "md-5", @@ -1855,6 +1896,15 @@ dependencies = [ "libc", ] +[[package]] +name = "core2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" +dependencies = [ + "memchr", +] + [[package]] name = "core_maths" version = "0.1.1" @@ -2294,7 +2344,7 @@ dependencies = [ "libc", "option-ext", "redox_users 0.5.2", - "windows-sys 0.60.2", + "windows-sys 0.59.0", ] [[package]] @@ -2803,7 +2853,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.52.0", ] [[package]] @@ -2844,9 +2894,9 @@ dependencies = [ [[package]] name = "exr" -version = "1.73.0" +version = "1.74.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f83197f59927b46c04a183a619b7c29df34e63e63c7869320862268c0ef687e0" +checksum = "4300e043a56aa2cb633c01af81ca8f699a321879a7854d3896a0ba89056363be" dependencies = [ "bit_field", "half", @@ -3428,6 +3478,16 @@ dependencies = [ "weezl", ] +[[package]] +name = "gif" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f954a9e9159ec994f73a30a12b96a702dde78f5547bcb561174597924f7d4162" +dependencies = [ + "color_quant", + "weezl", +] + [[package]] name = "gimli" version = "0.31.1" @@ -3788,7 +3848,7 @@ dependencies = [ "futures-core", "futures-sink", "http", - "indexmap 2.12.0", + "indexmap 2.12.1", "slab", "tokio", "tokio-util", @@ -3864,9 +3924,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" dependencies = [ "allocator-api2", "foldhash 0.2.0", @@ -4350,15 +4410,15 @@ dependencies = [ [[package]] name = "image" -version = "0.25.8" +version = "0.25.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "529feb3e6769d234375c4cf1ee2ce713682b8e76538cb13f9fc23e1400a591e7" +checksum = "e6506c6c10786659413faa717ceebcb8f70731c0a60cbae39795fdf114519c1a" dependencies = [ "bytemuck", "byteorder-lite", "color_quant", "exr", - "gif", + "gif 0.14.0", "image-webp", "moxcms", "num-traits", @@ -4368,8 +4428,8 @@ dependencies = [ "rayon", "rgb", "tiff", - "zune-core", - "zune-jpeg", + "zune-core 0.5.0", + "zune-jpeg 0.5.5", ] [[package]] @@ -4438,12 +4498,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.12.0" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" +checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" dependencies = [ "equivalent", - "hashbrown 0.16.0", + "hashbrown 0.16.1", "serde", "serde_core", ] @@ -4643,15 +4703,6 @@ dependencies = [ "either", ] -[[package]] -name = "itertools" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" -dependencies = [ - "either", -] - [[package]] name = "itertools" version = "0.13.0" @@ -4832,7 +4883,7 @@ checksum = "02cb977175687f33fa4afa0c95c112b987ea1443e5a51c8f8ff27dc618270cc2" dependencies = [ "cssparser", "html5ever", - "indexmap 2.12.0", + "indexmap 2.12.1", "selectors", ] @@ -4922,7 +4973,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" dependencies = [ "cfg-if", - "windows-targets 0.53.3", + "windows-targets 0.48.5", ] [[package]] @@ -5357,9 +5408,9 @@ dependencies = [ "cfg_aliases", "codespan-reporting", "half", - "hashbrown 0.16.0", + "hashbrown 0.16.1", "hexf-parse", - "indexmap 2.12.0", + "indexmap 2.12.1", "libm", "log", "num-traits", @@ -5741,7 +5792,7 @@ dependencies = [ "http-body-util", "hyper", "hyper-util", - "indexmap 2.12.0", + "indexmap 2.12.1", "interprocess", "nyanpasu-utils", "pin-project-lite", @@ -6430,7 +6481,7 @@ checksum = "71ef2dba21be1ce515378b2b7143eaa2a912f9e6ffe162ae20639d56f53d60e3" dependencies = [ "allocator-api2", "bumpalo", - "hashbrown 0.16.0", + "hashbrown 0.16.1", "oxc_data_structures", "rustc-hash 2.1.1", ] @@ -6658,6 +6709,12 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "pastey" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec" + [[package]] name = "pathdiff" version = "0.2.3" @@ -6741,7 +6798,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset 0.4.2", - "indexmap 2.12.0", + "indexmap 2.12.1", ] [[package]] @@ -6985,7 +7042,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3af6b589e163c5a788fab00ce0c0366f6efbb9959c2f9874b224936af7fce7e1" dependencies = [ "base64 0.22.1", - "indexmap 2.12.0", + "indexmap 2.12.1", "quick-xml 0.38.3", "serde", "time", @@ -7326,7 +7383,7 @@ dependencies = [ "once_cell", "socket2", "tracing", - "windows-sys 0.60.2", + "windows-sys 0.52.0", ] [[package]] @@ -7462,19 +7519,21 @@ checksum = "c3d6831663a5098ea164f89cff59c6284e95f4e3c76ce9848d4529f5ccca9bde" [[package]] name = "rav1e" -version = "0.7.1" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd87ce80a7665b1cce111f8a16c1f3929f6547ce91ade6addf4ec86a8dda5ce9" +checksum = "43b6dd56e85d9483277cde964fd1bdb0428de4fec5ebba7540995639a21cb32b" dependencies = [ + "aligned-vec", "arbitrary", "arg_enum_proc_macro", "arrayvec", + "av-scenechange", "av1-grain", "bitstream-io", "built", "cfg-if", "interpolate_name", - "itertools 0.12.1", + "itertools 0.14.0", "libc", "libfuzzer-sys", "log", @@ -7483,23 +7542,21 @@ dependencies = [ "noop_proc_macro", "num-derive 0.4.2", "num-traits", - "once_cell", "paste", "profiling", - "rand 0.8.5", - "rand_chacha 0.3.1", + "rand 0.9.2", + "rand_chacha 0.9.0", "simd_helpers", - "system-deps", - "thiserror 1.0.69", + "thiserror 2.0.17", "v_frame", "wasm-bindgen", ] [[package]] name = "ravif" -version = "0.11.20" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5825c26fddd16ab9f515930d49028a630efec172e903483c94796cfe31893e6b" +checksum = "ef69c1990ceef18a116855938e74793a5f7496ee907562bd0857b6ac734ab285" dependencies = [ "avif-serialize", "imgref", @@ -7707,7 +7764,7 @@ version = "0.45.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8928798c0a55e03c9ca6c4c6846f76377427d2c1e1f7e6de3c06ae57942df43" dependencies = [ - "gif", + "gif 0.13.3", "image-webp", "log", "pico-args", @@ -7715,7 +7772,7 @@ dependencies = [ "svgtypes", "tiny-skia", "usvg", - "zune-jpeg", + "zune-jpeg 0.4.21", ] [[package]] @@ -7893,7 +7950,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.9.4", - "windows-sys 0.60.2", + "windows-sys 0.52.0", ] [[package]] @@ -8189,7 +8246,7 @@ version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" dependencies = [ - "indexmap 2.12.0", + "indexmap 2.12.1", "itoa", "memchr", "ryu", @@ -8258,7 +8315,7 @@ dependencies = [ "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.12.0", + "indexmap 2.12.1", "schemars 0.9.0", "schemars 1.0.4", "serde", @@ -8286,7 +8343,7 @@ version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.12.0", + "indexmap 2.12.1", "itoa", "ryu", "serde", @@ -8298,7 +8355,7 @@ name = "serde_yaml_ng" version = "0.10.0" source = "git+https://github.com/libnyanpasu/serde-yaml-ng.git?branch=feat/specta#3078760ab9e9a3a9e18ebb4c5d3e577eb74c4a5c" dependencies = [ - "indexmap 2.12.0", + "indexmap 2.12.1", "itoa", "ryu", "serde", @@ -8697,7 +8754,7 @@ version = "2.0.0-rc.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab7f01e9310a820edd31c80fde3cae445295adde21a3f9416517d7d65015b971" dependencies = [ - "indexmap 2.12.0", + "indexmap 2.12.1", "paste", "serde", "serde_json", @@ -9725,7 +9782,7 @@ dependencies = [ "half", "quick-error", "weezl", - "zune-jpeg", + "zune-jpeg 0.4.21", ] [[package]] @@ -9919,7 +9976,7 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75129e1dc5000bfbaa9fee9d1b21f974f9fbad9daec557a521ee6e080825f6e8" dependencies = [ - "indexmap 2.12.0", + "indexmap 2.12.1", "serde", "serde_spanned 1.0.0", "toml_datetime 0.7.0", @@ -9952,7 +10009,7 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.12.0", + "indexmap 2.12.1", "toml_datetime 0.6.11", "winnow 0.5.40", ] @@ -9963,7 +10020,7 @@ version = "0.20.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" dependencies = [ - "indexmap 2.12.0", + "indexmap 2.12.1", "toml_datetime 0.6.11", "winnow 0.5.40", ] @@ -9974,7 +10031,7 @@ version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ - "indexmap 2.12.0", + "indexmap 2.12.1", "serde", "serde_spanned 0.6.9", "toml_datetime 0.6.11", @@ -11028,7 +11085,7 @@ dependencies = [ "cfg-if", "cfg_aliases", "document-features", - "hashbrown 0.16.0", + "hashbrown 0.16.1", "js-sys", "log", "naga", @@ -11059,8 +11116,8 @@ dependencies = [ "bytemuck", "cfg_aliases", "document-features", - "hashbrown 0.16.0", - "indexmap 2.12.0", + "hashbrown 0.16.1", + "indexmap 2.12.1", "log", "naga", "once_cell", @@ -11126,7 +11183,7 @@ dependencies = [ "gpu-alloc", "gpu-allocator", "gpu-descriptor", - "hashbrown 0.16.0", + "hashbrown 0.16.1", "js-sys", "khronos-egl", "libc", @@ -12191,6 +12248,12 @@ dependencies = [ "lzma-sys", ] +[[package]] +name = "y4m" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5a4b21e1a62b67a2970e6831bc091d7b87e119e7f9791aef9702e3bef04448" + [[package]] name = "yansi" version = "1.0.1" @@ -12474,7 +12537,7 @@ dependencies = [ "flate2", "getrandom 0.3.3", "hmac", - "indexmap 2.12.0", + "indexmap 2.12.1", "lzma-rs", "memchr", "pbkdf2", @@ -12494,7 +12557,7 @@ checksum = "caa8cd6af31c3b31c6631b8f483848b91589021b28fffe50adada48d4f4d2ed1" dependencies = [ "arbitrary", "crc32fast", - "indexmap 2.12.0", + "indexmap 2.12.1", "memchr", ] @@ -12559,6 +12622,12 @@ version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" +[[package]] +name = "zune-core" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "111f7d9820f05fd715df3144e254d6fc02ee4088b0644c0ffd0efc9e6d9d2773" + [[package]] name = "zune-inflate" version = "0.2.54" @@ -12574,7 +12643,16 @@ version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29ce2c8a9384ad323cf564b67da86e21d3cfdff87908bc1223ed5c99bc792713" dependencies = [ - "zune-core", + "zune-core 0.4.12", +] + +[[package]] +name = "zune-jpeg" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc6fb7703e32e9a07fb3f757360338b3a567a5054f21b5f52a666752e333d58e" +dependencies = [ + "zune-core 0.5.0", ] [[package]] diff --git a/clash-nyanpasu/frontend/nyanpasu/package.json b/clash-nyanpasu/frontend/nyanpasu/package.json index fc68469f8d..12bc856c15 100644 --- a/clash-nyanpasu/frontend/nyanpasu/package.json +++ b/clash-nyanpasu/frontend/nyanpasu/package.json @@ -56,7 +56,7 @@ "@csstools/normalize.css": "12.1.1", "@emotion/babel-plugin": "11.13.5", "@emotion/react": "11.14.0", - "@iconify/json": "2.2.409", + "@iconify/json": "2.2.410", "@monaco-editor/react": "4.7.0", "@tanstack/react-query": "5.90.7", "@tanstack/react-router": "1.134.15", diff --git a/clash-nyanpasu/manifest/version.json b/clash-nyanpasu/manifest/version.json index de0b61638c..150cd41a03 100644 --- a/clash-nyanpasu/manifest/version.json +++ b/clash-nyanpasu/manifest/version.json @@ -2,7 +2,7 @@ "manifest_version": 1, "latest": { "mihomo": "v1.19.16", - "mihomo_alpha": "alpha-7571c87", + "mihomo_alpha": "alpha-66781a5", "clash_rs": "v0.9.2", "clash_premium": "2023-09-05-gdcc8d87", "clash_rs_alpha": "0.9.2-alpha+sha.87c7b2c" @@ -69,5 +69,5 @@ "linux-armv7hf": "clash-armv7-unknown-linux-gnueabihf" } }, - "updated_at": "2025-11-23T22:21:13.537Z" + "updated_at": "2025-11-24T22:21:21.998Z" } diff --git a/clash-nyanpasu/pnpm-lock.yaml b/clash-nyanpasu/pnpm-lock.yaml index 552b8f4751..68c9bc4ec4 100644 --- a/clash-nyanpasu/pnpm-lock.yaml +++ b/clash-nyanpasu/pnpm-lock.yaml @@ -346,8 +346,8 @@ importers: specifier: 11.14.0 version: 11.14.0(@types/react@19.2.2)(react@19.2.0) '@iconify/json': - specifier: 2.2.409 - version: 2.2.409 + specifier: 2.2.410 + version: 2.2.410 '@monaco-editor/react': specifier: 4.7.0 version: 4.7.0(monaco-editor@0.54.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) @@ -1821,8 +1821,8 @@ packages: prettier-plugin-ember-template-tag: optional: true - '@iconify/json@2.2.409': - resolution: {integrity: sha512-PnTFu5JSM+GTL2mPwLBi6UDFiQvqG+OVITz9JNfZgSjFuWcF67LhedfjKd4jO/0EgLQLl0StjGbDEu1B19V5vw==} + '@iconify/json@2.2.410': + resolution: {integrity: sha512-0IhW9Sfudf3cPQHoCwr2gJMMUUkLW01WIkGoP9PbwVKXl1I/KTRHtM9IchLufT8M86QHBWRcinApzkL60TH9vA==} '@iconify/types@2.0.0': resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} @@ -10341,7 +10341,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@iconify/json@2.2.409': + '@iconify/json@2.2.410': dependencies: '@iconify/types': 2.0.0 pathe: 2.0.3 diff --git a/lede/package/boot/uboot-rk35xx/Makefile b/lede/package/boot/uboot-rk35xx/Makefile deleted file mode 100644 index 26fdc428d1..0000000000 --- a/lede/package/boot/uboot-rk35xx/Makefile +++ /dev/null @@ -1,83 +0,0 @@ -# -# Copyright (C) 2022 ImmortalWrt.org -# -# This is free software, licensed under the GNU General Public License v2. -# See /LICENSE for more information. -# - -include $(TOPDIR)/rules.mk - -PKG_NAME:=uboot-rk35xx -PKG_RELEASE:=1 - -PKG_SOURCE_PROTO:=git -PKG_SOURCE_URL:=https://github.com/radxa/u-boot - -PKG_SOURCE_DATE:=2024-10-29 -PKG_SOURCE_VERSION:=27398f1e19628407fabb279034653d23c9369f12 -PKG_MIRROR_HASH:=d0469f6c1f0d561d1495844fff2b50bc42af16fa4e33f5f42f092770a2bb4967 - -include $(INCLUDE_DIR)/u-boot.mk -include $(INCLUDE_DIR)/package.mk - -define U-Boot/Default - BUILD_TARGET:=rockchip - UENV:=default - HIDDEN:=1 -endef - -define U-Boot/evb-rk3528 - BUILD_SUBTARGET:=armv8 - NAME:=RK3528 Evaluation - BUILD_DEVICES:= \ - armsom_sige1 - DEPENDS:=+PACKAGE_u-boot-evb-rk3528:rkbin-rk3528 - ATF:=rk3528_bl31_v1.17.elf - DDR:=rk3528_ddr_1056MHz_v1.10.bin - UBOOT_CONFIG:=rk3528 - SOC:=rk3528 -endef - -define U-Boot/evb-rk3576 - BUILD_SUBTARGET:=armv8 - NAME:=RK3576 Evaluation - BUILD_DEVICES:= \ - armsom_sige5 \ - ariaboard_photonicat2 \ - friendlyarm_nanopi-m5 \ - friendlyarm_nanopi-r76s - DEPENDS:=+PACKAGE_u-boot-evb-rk3576:rkbin-rk3576 - ATF:=rk3576_bl31_v1.12.elf - DDR:=rk3576_ddr_lp4_2112MHz_lp5_2736MHz_v1.08.bin - UBOOT_CONFIG:=rk3576 - SOC:=rk3576 -endef - -UBOOT_TARGETS := \ - evb-rk3528 \ - evb-rk3576 - -UBOOT_CONFIGURE_VARS += USE_PRIVATE_LIBGCC=yes - -UBOOT_MAKE_FLAGS += \ - BL31=$(STAGING_DIR_IMAGE)/$(ATF) spl/u-boot-spl.bin u-boot.dtb u-boot.itb - -define Build/Configure - $(call Build/Configure/U-Boot) - - $(SED) 's#CONFIG_MKIMAGE_DTC_PATH=.*#CONFIG_MKIMAGE_DTC_PATH="$(PKG_BUILD_DIR)/scripts/dtc/dtc"#g' $(PKG_BUILD_DIR)/.config - echo 'CONFIG_IDENT_STRING=" OpenWrt"' >> $(PKG_BUILD_DIR)/.config -endef - -define Build/InstallDev - $(INSTALL_DIR) $(STAGING_DIR_IMAGE) - $(PKG_BUILD_DIR)/tools/mkimage -n $(SOC) -T rksd -d \ - $(STAGING_DIR_IMAGE)/$(DDR):$(PKG_BUILD_DIR)/spl/u-boot-spl.bin $(PKG_BUILD_DIR)/idbloader.img - $(CP) $(PKG_BUILD_DIR)/idbloader.img $(STAGING_DIR_IMAGE)/$(BUILD_VARIANT)-idbloader.img - $(CP) $(PKG_BUILD_DIR)/u-boot.itb $(STAGING_DIR_IMAGE)/$(BUILD_VARIANT)-u-boot.itb -endef - -define Package/u-boot/install/default -endef - -$(eval $(call BuildPackage/U-Boot)) diff --git a/lede/package/boot/uboot-rk35xx/patches/001-cmd-fix_source.patch b/lede/package/boot/uboot-rk35xx/patches/001-cmd-fix_source.patch deleted file mode 100644 index d3ccb39cbb..0000000000 --- a/lede/package/boot/uboot-rk35xx/patches/001-cmd-fix_source.patch +++ /dev/null @@ -1,22 +0,0 @@ -From ea50d7f6921e2c3017258ce8fdfad632d82fbd87 Mon Sep 17 00:00:00 2001 -From: Stephen -Date: Mon, 8 Nov 2021 14:30:00 +0800 -Subject: [PATCH] cmd: source: fix the error that the command source - failed to execute - -Signed-off-by: Stephen ---- - cmd/source.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - ---- a/cmd/source.c -+++ b/cmd/source.c -@@ -87,7 +87,7 @@ source (ulong addr, const char *fit_unam - * past the zero-terminated sequence of image lengths to get - * to the actual image data - */ -- while (*data++ != IMAGE_PARAM_INVAL); -+ while (*data++); - break; - #endif - #if defined(CONFIG_FIT) diff --git a/lede/package/boot/uboot-rk35xx/patches/010-migrate-to-python3.patch b/lede/package/boot/uboot-rk35xx/patches/010-migrate-to-python3.patch deleted file mode 100644 index 9f139c7530..0000000000 --- a/lede/package/boot/uboot-rk35xx/patches/010-migrate-to-python3.patch +++ /dev/null @@ -1,16 +0,0 @@ ---- a/arch/arm/mach-rockchip/decode_bl31.py -+++ b/arch/arm/mach-rockchip/decode_bl31.py -@@ -1,4 +1,4 @@ --#!/usr/bin/env python2 -+#!/usr/bin/env python3 - # - # Copyright (C) 2020 Rockchip Electronics Co., Ltd - # ---- a/arch/arm/mach-rockchip/make_fit_atf.py -+++ b/arch/arm/mach-rockchip/make_fit_atf.py -@@ -1,4 +1,4 @@ --#!/usr/bin/env python2 -+#!/usr/bin/env python3 - """ - A script to generate FIT image source for rockchip boards - with ARM Trusted Firmware diff --git a/lede/package/boot/uboot-rk35xx/patches/100-disable-optee-build.patch b/lede/package/boot/uboot-rk35xx/patches/100-disable-optee-build.patch deleted file mode 100644 index 97a4c5e785..0000000000 --- a/lede/package/boot/uboot-rk35xx/patches/100-disable-optee-build.patch +++ /dev/null @@ -1,17 +0,0 @@ ---- a/configs/rk3528_defconfig -+++ b/configs/rk3528_defconfig -@@ -197,5 +197,3 @@ CONFIG_AVB_LIBAVB_AB=y - CONFIG_AVB_LIBAVB_ATX=y - CONFIG_AVB_LIBAVB_USER=y - CONFIG_RK_AVB_LIBAVB_USER=y --CONFIG_OPTEE_CLIENT=y --CONFIG_OPTEE_V2=y ---- a/configs/rk3576_defconfig -+++ b/configs/rk3576_defconfig -@@ -224,6 +224,3 @@ CONFIG_AVB_LIBAVB_AB=y - CONFIG_AVB_LIBAVB_ATX=y - CONFIG_AVB_LIBAVB_USER=y - CONFIG_RK_AVB_LIBAVB_USER=y --CONFIG_OPTEE_CLIENT=y --CONFIG_OPTEE_V2=y --CONFIG_OPTEE_ALWAYS_USE_SECURITY_PARTITION=y diff --git a/lede/package/boot/uboot-rk35xx/patches/110-fix-build-with-gcc13.patch b/lede/package/boot/uboot-rk35xx/patches/110-fix-build-with-gcc13.patch deleted file mode 100644 index dba47a8c1e..0000000000 --- a/lede/package/boot/uboot-rk35xx/patches/110-fix-build-with-gcc13.patch +++ /dev/null @@ -1,22 +0,0 @@ ---- a/common/edid.c -+++ b/common/edid.c -@@ -3579,7 +3579,7 @@ int add_cea_modes(struct hdmi_edid_data - { - const u8 *cea = drm_find_cea_extension(edid); - const u8 *db, *hdmi = NULL, *video = NULL; -- u8 dbl, hdmi_len, video_len = 0; -+ u8 dbl, hdmi_len = 0, video_len = 0; - int modes = 0; - - if (cea && cea_revision(cea) >= 3) { ---- a/include/command.h -+++ b/include/command.h -@@ -139,7 +139,7 @@ enum command_ret_t { - * number of ticks the command took to complete. - * @return 0 if the command succeeded, 1 if it failed - */ --int cmd_process(int flag, int argc, char * const argv[], -+enum command_ret_t cmd_process(int flag, int argc, char * const argv[], - int *repeatable, unsigned long *ticks); - - void fixup_cmdtable(cmd_tbl_t *cmdtp, int size); diff --git a/lede/package/boot/uboot-rk35xx/patches/200-fallback-to-legacy-image-boot.patch b/lede/package/boot/uboot-rk35xx/patches/200-fallback-to-legacy-image-boot.patch deleted file mode 100644 index e328029dc3..0000000000 --- a/lede/package/boot/uboot-rk35xx/patches/200-fallback-to-legacy-image-boot.patch +++ /dev/null @@ -1,44 +0,0 @@ -diff --git a/disk/part_efi.c b/disk/part_efi.c -index d4d03de..b803d17 100644 ---- a/disk/part_efi.c -+++ b/disk/part_efi.c -@@ -351,7 +351,7 @@ int part_get_info_efi(struct blk_desc *dev_desc, int part, - return 0; - } - --#ifdef CONFIG_RKIMG_BOOTLOADER -+#if 0 - #if defined(CONFIG_SPL_KERNEL_BOOT) || !defined(CONFIG_SPL_BUILD) - static void gpt_entry_modify(struct blk_desc *dev_desc, - gpt_entry *gpt_pte, -@@ -452,7 +452,7 @@ static int part_test_efi(struct blk_desc *dev_desc) - || (is_pmbr_valid(legacymbr) != 1)) { - return -1; - } --#ifdef CONFIG_RKIMG_BOOTLOADER -+#if 0 - #if defined(CONFIG_SPL_KERNEL_BOOT) || !defined(CONFIG_SPL_BUILD) - gpt_entry *h_gpt_pte = NULL; - gpt_header *h_gpt_head = NULL; -@@ -1084,7 +1084,7 @@ static int is_pmbr_valid(legacy_mbr * mbr) - { - int i = 0; - --#ifdef CONFIG_ARCH_ROCKCHIP -+#if 0 - /* - * In sd-update card, we use RKPARM partition in bootloader to load - * firmware, and use MS-DOS partition in recovery to update system. -diff --git a/include/configs/rockchip-common.h b/include/configs/rockchip-common.h -index c35b7db..b695a7d 100644 ---- a/include/configs/rockchip-common.h -+++ b/include/configs/rockchip-common.h -@@ -179,6 +179,8 @@ - "run distro_bootcmd;" - #endif - -+#define CONFIG_IMAGE_FORMAT_LEGACY /* enable also legacy image format */ -+ - #endif /* CONFIG_SPL_BUILD */ - - #define CONFIG_DISPLAY_BOARDINFO_LATE \ No newline at end of file diff --git a/mieru/apis/log/log.go b/mieru/apis/log/log.go new file mode 100644 index 0000000000..30ded797aa --- /dev/null +++ b/mieru/apis/log/log.go @@ -0,0 +1,48 @@ +// Copyright (C) 2025 mieru authors +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package log + +import ( + "strings" + + "github.com/enfein/mieru/v3/pkg/log" +) + +// As libraries, mieru client API and server API disable mieru internal logging. +// We recommend use a callback function to collect internal log messages. + +type Message = log.LogMessage +type Callback = log.Callback + +// GetLevel returns the current log level. +func GetLevel() string { + return strings.ToUpper(log.GetLevel().String()) +} + +// SetLevel sets the log level. It returns true if successful. +// +// This function determines which log messages are passed to the callback function. +func SetLevel(level string) (ok bool) { + return log.SetLevel(level) +} + +// SetCallback registers a callback function that is invoked +// when a log message is produced. +// +// Set the callback to nil to clear the existing callback function. +func SetCallback(callback Callback) { + log.SetCallback(callback) +} diff --git a/mieru/pkg/log/callback.go b/mieru/pkg/log/callback.go new file mode 100644 index 0000000000..80b57e041d --- /dev/null +++ b/mieru/pkg/log/callback.go @@ -0,0 +1,24 @@ +// Copyright (C) 2025 mieru authors +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package log + +type LogMessage struct { + Level string + Message string +} + +// Callback is a function that consumes a single log message. +type Callback func(LogMessage) diff --git a/mieru/pkg/log/entry.go b/mieru/pkg/log/entry.go index 2b785b7cf4..4d4eb6bb9a 100644 --- a/mieru/pkg/log/entry.go +++ b/mieru/pkg/log/entry.go @@ -270,13 +270,32 @@ func (entry *Entry) getBufferPool() (pool BufferPool) { func (entry *Entry) write() { entry.Logger.mu.Lock() defer entry.Logger.mu.Unlock() + serialized, err := entry.Logger.Formatter.Format(entry) if err != nil { - fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err) + fmt.Fprintf(os.Stderr, "Failed to format log entry: %v\n", err) return } - if _, err := entry.Logger.Out.Write(serialized); err != nil { - fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err) + if len(serialized) > 0 { + if _, err := entry.Logger.Out.Write(serialized); err != nil { + fmt.Fprintf(os.Stderr, "Failed to write to log: %v\n", err) + return + } + } + + if entry.Logger.Callback != nil { + msg, err := entry.Logger.CallbackFormatter.Format(entry) + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to format log entry: %v\n", err) + return + } + if len(msg) > 0 { + logMessage := LogMessage{ + Level: entry.Level.String(), + Message: string(msg), + } + entry.Logger.Callback(logMessage) + } } } diff --git a/mieru/pkg/log/entry_test.go b/mieru/pkg/log/entry_test.go index d80670ee0d..0d3dff46e1 100644 --- a/mieru/pkg/log/entry_test.go +++ b/mieru/pkg/log/entry_test.go @@ -222,25 +222,6 @@ func TestEntryPanic(t *testing.T) { entry.WithField("err", errBoom).Panic("kaboom") } -const ( - badMessage = "this is going to panic" - panicMessage = "this is broken" -) - -type panickyHook struct{} - -func (p *panickyHook) Levels() []Level { - return []Level{InfoLevel} -} - -func (p *panickyHook) Fire(entry *Entry) error { - if entry.Message == badMessage { - panic(panicMessage) - } - - return nil -} - func TestEntryWithIncorrectField(t *testing.T) { fn := func() {} @@ -286,6 +267,42 @@ func TestEntryLogfLevel(t *testing.T) { } } +func TestEntryCallback(t *testing.T) { + logger := New() + logger.Out = &bytes.Buffer{} + logger.SetLevel(InfoLevel) + + // Track whether callback was invoked. + callbackInvoked := false + var capturedLevel string + var capturedMessage string + logger.SetCallback(func(msg LogMessage) { + callbackInvoked = true + capturedLevel = msg.Level + capturedMessage = msg.Message + }) + + // Test callback should be invoked. + entry := NewEntry(logger) + entry.Infof("test message") + if !callbackInvoked { + t.Fatal("callback was not invoked") + } + if capturedLevel != "info" { + t.Fatalf("expected level 'info', got '%s'", capturedLevel) + } + if !strings.Contains(capturedMessage, "test message") { + t.Fatalf("expected message to contain 'test message', got '%s'", capturedMessage) + } + + // Test callback should not be invoked. + callbackInvoked = false + entry.Debugf("debug message") + if callbackInvoked { + t.Fatal("callback should not be invoked for debug level when logger is set to info") + } +} + func TestEntryReportCallerRace(t *testing.T) { logger := New() entry := NewEntry(logger) diff --git a/mieru/pkg/log/exported.go b/mieru/pkg/log/exported.go index c182e5e985..0822571b35 100644 --- a/mieru/pkg/log/exported.go +++ b/mieru/pkg/log/exported.go @@ -26,6 +26,11 @@ func SetFormatter(formatter Formatter) { std.SetFormatter(formatter) } +// SetCallback sets the standard logger callback function. +func SetCallback(callback Callback) { + std.SetCallback(callback) +} + // SetReportCaller sets whether the standard logger will include the calling // method as a field. func SetReportCaller(include bool) { @@ -33,22 +38,29 @@ func SetReportCaller(include bool) { } // SetLevel sets the standard logger level. -func SetLevel(level string) { +func SetLevel(level string) (ok bool) { level = strings.ToUpper(level) switch level { case "FATAL": std.SetLevel(FatalLevel) + return true case "ERROR": std.SetLevel(ErrorLevel) - case "WARN": + return true + case "WARN", "WARNING": std.SetLevel(WarnLevel) + return true case "INFO": std.SetLevel(InfoLevel) + return true case "DEBUG": std.SetLevel(DebugLevel) + return true case "TRACE": std.SetLevel(TraceLevel) + return true default: + return false } } diff --git a/mieru/pkg/log/formatter.go b/mieru/pkg/log/formatter.go index 14cc48da3c..556705d898 100644 --- a/mieru/pkg/log/formatter.go +++ b/mieru/pkg/log/formatter.go @@ -58,7 +58,9 @@ func (f *CliFormatter) Format(entry *Entry) ([]byte, error) { // DaemonFormatter is the a log formatter that is suitable for daemon. // FATAL log is also printed to stderr. type DaemonFormatter struct { + NoPrefix bool NoTimestamp bool + NoLevel bool } func (f *DaemonFormatter) Format(entry *Entry) ([]byte, error) { @@ -77,7 +79,9 @@ func (f *DaemonFormatter) Format(entry *Entry) ([]byte, error) { if !f.NoTimestamp { orderedKeys = append(orderedKeys, FieldKeyTime) } - orderedKeys = append(orderedKeys, FieldKeyLevel) + if !f.NoLevel { + orderedKeys = append(orderedKeys, FieldKeyLevel) + } orderedKeys = append(orderedKeys, FieldKeyMsg) if entry.HasCaller() { fileInfo = fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line) @@ -95,7 +99,9 @@ func (f *DaemonFormatter) Format(entry *Entry) ([]byte, error) { buf = &bytes.Buffer{} } - buf.WriteString(LogPrefix) + if !f.NoPrefix { + buf.WriteString(LogPrefix) + } for _, key := range orderedKeys { var value string switch { diff --git a/mieru/pkg/log/formatter_test.go b/mieru/pkg/log/formatter_test.go index 96c7f18764..97a58af307 100644 --- a/mieru/pkg/log/formatter_test.go +++ b/mieru/pkg/log/formatter_test.go @@ -103,6 +103,63 @@ func TestDaemonFormatter(t *testing.T) { buf.Reset() } +func TestDaemonFormatterOptions(t *testing.T) { + t.Run("NoPrefix", func(t *testing.T) { + // Set a log prefix to test that it's not printed + oldPrefix := LogPrefix + LogPrefix = "[TEST_PREFIX] " + defer func() { + LogPrefix = oldPrefix + }() + + SetFormatter(&DaemonFormatter{NoPrefix: true}) + SetLevel("INFO") + var buf bytes.Buffer + SetOutput(&buf) + defer func() { + SetFormatter(&CliFormatter{}) + SetLevel("INFO") + SetOutput(os.Stdout) + }() + + Infof("This is a test message") + msg := buf.String() + if strings.Contains(msg, "[TEST_PREFIX]") { + t.Errorf("Log prefix should not be printed with NoPrefix: %q", msg) + } + if !strings.Contains(msg, "This is a test message") { + t.Errorf("Log message should still be printed: %q", msg) + } + }) + + t.Run("NoLevel", func(t *testing.T) { + SetFormatter(&DaemonFormatter{NoLevel: true}) + SetLevel("INFO") + var buf bytes.Buffer + SetOutput(&buf) + defer func() { + SetFormatter(&CliFormatter{}) + SetLevel("INFO") + SetOutput(os.Stdout) + }() + + Infof("This is a test message") + msg := buf.String() + checkLogLevel(t, msg, "INFO", false) + if !strings.Contains(msg, "This is a test message") { + t.Errorf("Log message should still be printed: %q", msg) + } + buf.Reset() + + Errorf("This is a test message") + msg = buf.String() + checkLogLevel(t, msg, "ERROR", false) + if !strings.Contains(msg, "This is a test message") { + t.Errorf("Log message should still be printed: %q", msg) + } + }) +} + func TestNilFormatter(t *testing.T) { SetFormatter(&NilFormatter{}) SetLevel("TRACE") diff --git a/mieru/pkg/log/logger.go b/mieru/pkg/log/logger.go index 31399d0b67..76ca232498 100644 --- a/mieru/pkg/log/logger.go +++ b/mieru/pkg/log/logger.go @@ -9,26 +9,22 @@ import ( "time" ) -// LogFunction For big messages, it can be more efficient to pass a function -// and only call it if the log level is actually enables rather than -// generating the log message and then checking if the level is enabled -type LogFunction func() []interface{} - type Logger struct { // The logs are `io.Copy`'d to this in a mutex. It's common to set this to a // file, or leave it default which is `os.Stderr`. You can also set this to // something more adventurous, such as logging to Kafka. Out io.Writer - // All log entries pass through the formatter before logged to Out. The - // included formatters are `TextFormatter` and `JSONFormatter` for which - // TextFormatter is the default. In development (when a TTY is attached) it - // logs with colors, but to a file it wouldn't. You can easily implement your - // own that implements the `Formatter` interface, see the `README` or included - // formatters for examples. + // All log entries pass through the formatter before logged to Out. Formatter Formatter - // Flag for whether to log caller info (off by default) + // An optional callback function for each log message. + Callback Callback + + // A formatter to generate log message passed to callback function. + CallbackFormatter Formatter + + // Flag for whether to log caller info (off by default). ReportCaller bool // The logging level the logger should log at. This is typically (and defaults @@ -36,18 +32,19 @@ type Logger struct { // logged. Level Level - // Used to sync writing to the log. Locking is enabled by Default - mu MutexWrap - - // Reusable empty entry - entryPool sync.Pool - - // Function to exit the application, defaults to `os.Exit()` + // Function to exit the application, defaults to `os.Exit()`. + // This can be useful in testing. ExitFunc exitFunc // The buffer pool used to format the log. If it is nil, the default global // buffer pool will be used. BufferPool BufferPool + + // Used to sync writing to the log. Locking is enabled by Default. + mu MutexWrap + + // Reusable empty entry. + entryPool sync.Pool } type exitFunc func(int) @@ -76,11 +73,12 @@ func (mw *MutexWrap) Disable() { // Creates a new logger with default configuration. func New() *Logger { return &Logger{ - Out: os.Stderr, - Formatter: new(CliFormatter), - Level: InfoLevel, - ExitFunc: os.Exit, - ReportCaller: false, + Out: os.Stderr, + Formatter: &CliFormatter{}, + CallbackFormatter: &DaemonFormatter{NoTimestamp: true, NoLevel: true}, + Level: InfoLevel, + ExitFunc: os.Exit, + ReportCaller: false, } } @@ -263,6 +261,13 @@ func (logger *Logger) IsLevelEnabled(level Level) bool { return logger.level() >= level } +// SetOutput sets the logger output. +func (logger *Logger) SetOutput(output io.Writer) { + logger.mu.Lock() + defer logger.mu.Unlock() + logger.Out = output +} + // SetFormatter sets the logger formatter. func (logger *Logger) SetFormatter(formatter Formatter) { logger.mu.Lock() @@ -270,11 +275,11 @@ func (logger *Logger) SetFormatter(formatter Formatter) { logger.Formatter = formatter } -// SetOutput sets the logger output. -func (logger *Logger) SetOutput(output io.Writer) { +// SetCallback sets the logger callback function. +func (logger *Logger) SetCallback(callback Callback) { logger.mu.Lock() defer logger.mu.Unlock() - logger.Out = output + logger.Callback = callback } func (logger *Logger) SetReportCaller(reportCaller bool) { diff --git a/mieru/pkg/log/mieru.go b/mieru/pkg/log/mieru_client_logging.go similarity index 94% rename from mieru/pkg/log/mieru.go rename to mieru/pkg/log/mieru_client_logging.go index 2a7986f60a..5ddb3baf9a 100644 --- a/mieru/pkg/log/mieru.go +++ b/mieru/pkg/log/mieru_client_logging.go @@ -37,13 +37,6 @@ const maxClientLogFiles = 25 // In Windows, it is typically C:\Users\\AppData\Local\mieru var cachedClientLogDir string -// init modifies the global logger instance with the desired output file (stdout) -// and customized formatter. -func init() { - SetOutput(os.Stdout) - SetFormatter(&CliFormatter{}) -} - // NewClientLogFile returns a file handler for mieru client to write logs. func NewClientLogFile() (io.WriteCloser, error) { if err := prepareClientLogDir(); err != nil { diff --git a/mieru/pkg/log/mieru_init.go b/mieru/pkg/log/mieru_init.go new file mode 100644 index 0000000000..16e9998dca --- /dev/null +++ b/mieru/pkg/log/mieru_init.go @@ -0,0 +1,25 @@ +// Copyright (C) 2025 mieru authors +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package log + +import "os" + +// init modifies the global logger instance with the desired output file (stdout) +// and customized formatter. +func init() { + SetOutput(os.Stdout) + SetFormatter(&CliFormatter{}) +} diff --git a/mieru/pkg/log/output.go b/mieru/pkg/log/mieru_test_log_adaptor.go similarity index 100% rename from mieru/pkg/log/output.go rename to mieru/pkg/log/mieru_test_log_adaptor.go diff --git a/mieru/test/cmd/sockshttpclient/sockshttpclient.go b/mieru/test/cmd/sockshttpclient/sockshttpclient.go index 79c7d3e5c1..d8fc2f870d 100644 --- a/mieru/test/cmd/sockshttpclient/sockshttpclient.go +++ b/mieru/test/cmd/sockshttpclient/sockshttpclient.go @@ -208,13 +208,15 @@ func CreateNewConnAndDoRequest(seq int, proxyMode string) { var client *http.Client var err error for { - if proxyMode == Socks5ProxyMode { + switch proxyMode { + case Socks5ProxyMode: socksDialer := socks5.DialSocks5Proxy(&socks5.Client{ Host: *localProxyHost + ":" + strconv.Itoa(*localProxyPort), + Timeout: 10 * time.Second, CmdType: constant.Socks5ConnectCmd, }) conn, _, _, err = socksDialer("tcp", *dstHost+":"+strconv.Itoa(*dstPort)) - } else if proxyMode == HTTPProxyMode { + case HTTPProxyMode: tr := &http.Transport{ Proxy: socks5.HTTPTransportProxyFunc("http://" + *localHTTPHost + ":" + strconv.Itoa(*localHTTPPort)), } @@ -225,16 +227,19 @@ func CreateNewConnAndDoRequest(seq int, proxyMode string) { }, Timeout: 10 * time.Second, } - } else if proxyMode == NoProxyMode { + case NoProxyMode: conn, err = net.Dial("tcp", *dstHost+":"+strconv.Itoa(*dstPort)) } + if err == nil { break } if !errors.Is(err, io.EOF) { log.Fatalf("dial failed: %v", err) } + time.Sleep(time.Millisecond) } + if client != nil { defer client.CloseIdleConnections() DoRequestWithExistingHTTPClient(client, seq) diff --git a/mihomo/go.mod b/mihomo/go.mod index 3c1ab95762..2047d0eb11 100644 --- a/mihomo/go.mod +++ b/mihomo/go.mod @@ -32,7 +32,7 @@ require ( github.com/metacubex/sing-shadowsocks v0.2.12 github.com/metacubex/sing-shadowsocks2 v0.2.7 github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2 - github.com/metacubex/sing-tun v0.4.10-0.20251124160354-85cd06f11a84 + github.com/metacubex/sing-tun v0.4.9 github.com/metacubex/sing-vmess v0.2.4 github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f github.com/metacubex/smux v0.0.0-20251111013112-03f8d12dafc1 diff --git a/mihomo/go.sum b/mihomo/go.sum index 74a5c27619..bc29d7fef7 100644 --- a/mihomo/go.sum +++ b/mihomo/go.sum @@ -129,8 +129,8 @@ github.com/metacubex/sing-shadowsocks2 v0.2.7 h1:hSuuc0YpsfiqYqt1o+fP4m34BQz4e6w github.com/metacubex/sing-shadowsocks2 v0.2.7/go.mod h1:vOEbfKC60txi0ca+yUlqEwOGc3Obl6cnSgx9Gf45KjE= 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.10-0.20251124160354-85cd06f11a84 h1:PlVO8aCeAnVUsvO9X077iX9wz23nSnbRjtPRdE0GsY8= -github.com/metacubex/sing-tun v0.4.10-0.20251124160354-85cd06f11a84/go.mod h1:L/TjQY5JEGy8nvsuYmy/XgMFMCPiF0+AWSFCYfS6r9w= +github.com/metacubex/sing-tun v0.4.9 h1:jY0Yyt8nnN3yQRN/jTxgqNCmGi1dsFdxdIi7pQUlVVU= +github.com/metacubex/sing-tun v0.4.9/go.mod h1:L/TjQY5JEGy8nvsuYmy/XgMFMCPiF0+AWSFCYfS6r9w= github.com/metacubex/sing-vmess v0.2.4 h1:Tx6AGgCiEf400E/xyDuYyafsel6sGbR8oF7RkAaus6I= github.com/metacubex/sing-vmess v0.2.4/go.mod h1:21R5R1u90uUvBQF0owoooEu96/SAYYD56nDrwm6nFaM= github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f h1:Sr/DYKYofKHKc4GF3qkRGNuj6XA6c0eqPgEDN+VAsYU= diff --git a/nodepass/docs/en/api.md b/nodepass/docs/en/api.md index 37240dc4fa..b7b7818357 100644 --- a/nodepass/docs/en/api.md +++ b/nodepass/docs/en/api.md @@ -65,7 +65,7 @@ API Key authentication is enabled by default, automatically generated and saved "type": "client|server", "status": "running|stopped|error", "url": "...", - "config": "server://0.0.0.0:8080/localhost:3000?log=info&tls=1&dns=1.1.1.1,8.8.8.8&max=1024&mode=0&quic=0&dial=auto&read=1h&rate=100&slot=65536&proxy=0¬cp=0&noudp=0", + "config": "server://0.0.0.0:8080/localhost:3000?log=info&tls=1&dns=5m&max=1024&mode=0&quic=0&dial=auto&read=1h&rate=100&slot=65536&proxy=0¬cp=0&noudp=0", "restart": true, "meta": { "peer": { @@ -1116,7 +1116,7 @@ The instance object in API responses contains the following fields: "type": "server", // Instance type: server or client "status": "running", // Instance status: running, stopped, or error "url": "server://...", // Instance configuration URL - "config": "server://0.0.0.0:8080/localhost:3000?log=info&tls=1&dns=1.1.1.1,8.8.8.8&max=1024&mode=0&quic=0&dial=auto&read=1h&rate=100&slot=65536&proxy=0¬cp=0&noudp=0", // Complete configuration URL + "config": "server://0.0.0.0:8080/localhost:3000?log=info&tls=1&dns=5m&max=1024&mode=0&quic=0&dial=auto&read=1h&rate=100&slot=65536&proxy=0¬cp=0&noudp=0", // Complete configuration URL "restart": true, // Auto-restart policy "meta": { // Metadata for organization and peer tracking "peer": { @@ -1581,7 +1581,7 @@ Examples: | `tls` | TLS encryption level | `0`(none), `1`(self-signed), `2`(certificate) | `0` | Server only | | `crt` | Certificate path | File path | None | Server only | | `key` | Private key path | File path | None | Server only | -| `dns` | Custom DNS servers | Comma-separated IP addresses | `1.1.1.1,8.8.8.8` | Both | +| `dns` | DNS cache duration | Time duration (e.g., `5m`, `30s`, `1h`) | `5m` | Both | | `min` | Minimum pool capacity | Integer > 0 | `64` | Client dual-end handshake mode only | | `max` | Maximum pool capacity | Integer > 0 | `1024` | Dual-end handshake mode | | `mode` | Runtime mode control | `0`(auto), `1`(force mode 1), `2`(force mode 2) | `0` | Both | diff --git a/nodepass/docs/en/configuration.md b/nodepass/docs/en/configuration.md index a1be2bb81b..628a766b49 100644 --- a/nodepass/docs/en/configuration.md +++ b/nodepass/docs/en/configuration.md @@ -109,63 +109,59 @@ nodepass "server://0.0.0.0:10101/remote.example.com:8080?mode=2" ## DNS Resolution Configuration -NodePass supports custom DNS server configuration with intelligent caching for improved performance and reliability. The built-in DNS resolver provides background refresh, automatic failover, and configurable TTL management. +NodePass uses the system's built-in DNS resolver with intelligent caching for improved performance and reliability. The DNS cache reduces query overhead and prevents resolution delays. -- `dns`: Custom DNS server addresses (default: 1.1.1.1,8.8.8.8) - - Comma-separated list of IP addresses (IPv4 or IPv6) - - Multiple DNS servers provide automatic failover and round-robin load balancing - - Invalid IP addresses in the list are logged and skipped - - If all provided DNS servers fail, the system falls back to OS default DNS resolution - - DNS resolution uses a dedicated port (UDP port 53) for all queries +- `dns`: DNS cache TTL duration (default: 5m) + - Specifies how long resolved hostnames are cached before re-querying + - Accepts time duration format: `1h`, `30m`, `15s`, `500ms`, etc. + - Longer TTL reduces DNS query overhead but may cache stale records + - Shorter TTL ensures fresher DNS data but increases query frequency + - Set to `0` to disable caching (always query DNS on every connection) - Applies to both client and server modes for resolving all hostnames -**DNS Resolver Features:** -- **Intelligent Caching**: Resolved hostnames are cached with configurable TTL to reduce DNS query overhead -- **Background Refresh**: Cached entries are proactively refreshed before expiration (at 80% of TTL) to prevent lookup delays -- **Automatic Failover**: When a DNS server fails, automatically tries the next server in the list -- **Round-Robin Distribution**: DNS queries are distributed across configured servers for load balancing +**DNS Cache Features:** +- **System Integration**: Uses operating system's native DNS resolver for maximum compatibility +- **Intelligent Caching**: Resolved hostnames are cached with configurable TTL to reduce query overhead +- **Automatic Expiration**: Cached entries are automatically removed after TTL expires - **IP Address Bypass**: Direct IP addresses skip DNS resolution for maximum efficiency -- **Protocol-Aware**: Automatically selects IPv4 or IPv6 addresses based on connection requirements +- **Protocol-Aware**: Automatically handles both IPv4 and IPv6 addresses +- **Thread-Safe**: Concurrent DNS lookups are safely cached and shared across connections Example: ```bash -# Use Cloudflare and Google DNS servers (default behavior) -nodepass "server://0.0.0.0:10101/example.com:8080?dns=1.1.1.1,8.8.8.8" +# Use default 5-minute cache TTL +nodepass "server://0.0.0.0:10101/example.com:8080" -# Use custom DNS servers (e.g., corporate DNS) -nodepass "server://0.0.0.0:10101/internal.example.com:8080?dns=10.0.0.53,10.0.1.53" +# Set 1-hour cache TTL for stable domains +nodepass "server://0.0.0.0:10101/internal.example.com:8080?dns=1h" -# Client with specific DNS configuration -nodepass "client://server.example.com:10101/database.local:3306?dns=192.168.1.1,192.168.1.2" +# Set 30-second cache TTL for dynamic DNS +nodepass "client://server.example.com:10101/database.local:3306?dns=30s" -# IPv6 DNS servers -nodepass "server://0.0.0.0:10101/service.example.com:8080?dns=2606:4700:4700::1111,2001:4860:4860::8888" +# Disable DNS caching entirely (query on every connection) +nodepass "server://0.0.0.0:10101/service.example.com:8080?dns=0" # Combined with other parameters -nodepass "server://0.0.0.0:10101/backend.example.com:8080?dns=1.1.1.1,8.8.8.8&log=info&tls=1&mode=2" +nodepass "server://0.0.0.0:10101/backend.example.com:8080?dns=10m&log=info&tls=1&mode=2" ``` **DNS Configuration Use Cases:** -- **Corporate Networks**: Use internal DNS servers to resolve private hostnames -- **Geographic Optimization**: Select DNS servers geographically close to your deployment -- **Privacy Enhancement**: Use privacy-focused DNS providers (e.g., 1.1.1.1, 9.9.9.9) -- **Reliability**: Configure multiple DNS servers for high availability -- **Compliance**: Meet regulatory requirements for DNS provider selection -- **Testing**: Use specific DNS servers for development or staging environments -- **Performance**: Reduce DNS lookup latency with closer or faster DNS servers +- **Corporate Networks**: Use longer TTL (e.g., 1h) for stable internal hostnames +- **Dynamic DNS**: Use shorter TTL (e.g., 30s) for frequently changing DNS records +- **High Availability**: Longer TTL reduces DNS server load and improves reliability +- **Load Balancing**: Shorter TTL enables faster failover for load-balanced services +- **Performance**: Longer TTL reduces connection latency by minimizing DNS queries **DNS Caching Behavior:** -- Cache TTL is controlled by `NP_DNS_CACHING_TTL` environment variable (default: 5 minutes) -- Background refresh occurs at 80% of TTL to maintain fresh data without delays +- Cache TTL is configurable via the `dns` query parameter (default: 5 minutes) - Expired entries are removed and fresh lookups performed on next access - Cache is per-instance and not shared between NodePass processes - IP addresses are never cached (direct use, no DNS lookup needed) +- System DNS resolver is used for all hostname lookups **Important Notes:** -- DNS servers must be specified as IP addresses, not hostnames -- Both IPv4 and IPv6 DNS server addresses are supported -- DNS resolution timeout is fixed at 5 seconds per query -- Failed DNS servers are retried in subsequent queries (no permanent blacklisting) +- Both IPv4 and IPv6 addresses are supported +- DNS resolution timeout is controlled by the operating system - When using target address groups, each address is resolved independently - DNS resolution applies to both tunnel addresses and target addresses - Tunnel address DNS resolution occurs once at startup @@ -571,7 +567,7 @@ NodePass allows flexible configuration via URL query parameters. The following t | `tls` | TLS encryption mode | `0` | O | X | O | | `crt` | Custom certificate path | N/A | O | X | O | | `key` | Custom key path | N/A | O | X | O | -| `dns` | Custom DNS servers | `1.1.1.1,8.8.8.8` | O | O | X | +| `dns` | DNS cache TTL | `5m` | O | O | X | | `min` | Minimum pool capacity | `64` | X | O | X | | `max` | Maximum pool capacity | `1024` | O | X | X | | `mode` | Run mode control | `0` | O | O | X | @@ -606,7 +602,6 @@ NodePass behavior can be fine-tuned using environment variables. Below is the co | `NP_SEMAPHORE_LIMIT` | Signal channel buffer size | 65536 | `export NP_SEMAPHORE_LIMIT=2048` | | `NP_TCP_DATA_BUF_SIZE` | Buffer size for TCP data transfer | 16384 | `export NP_TCP_DATA_BUF_SIZE=65536` | | `NP_UDP_DATA_BUF_SIZE` | Buffer size for UDP packets | 16384 | `export NP_UDP_DATA_BUF_SIZE=16384` | -| `NP_DNS_CACHING_TTL` | DNS cache time-to-live duration | 5m | `export NP_DNS_CACHING_TTL=10m` | | `NP_HANDSHAKE_TIMEOUT` | Timeout for handshake operations | 5s | `export NP_HANDSHAKE_TIMEOUT=30s` | | `NP_UDP_READ_TIMEOUT` | Timeout for UDP read operations | 30s | `export NP_UDP_READ_TIMEOUT=60s` | | `NP_TCP_DIAL_TIMEOUT` | Timeout for establishing TCP connections | 5s | `export NP_TCP_DIAL_TIMEOUT=60s` | @@ -652,33 +647,6 @@ The connection pool parameters are important settings for performance tuning in - Too large: Increased memory usage - Recommended range: 1000-5000 -### DNS Resolution Tuning - -For applications with frequent hostname lookups or dynamic DNS scenarios: - -- `NP_DNS_CACHING_TTL`: DNS cache time-to-live duration - - Controls how long resolved DNS entries remain cached - - Default (5m) balances freshness and performance for most scenarios - - Increase for stable DNS environments to reduce query overhead (e.g., 15m, 30m, 1h) - - Decrease for dynamic DNS environments requiring fresh lookups (e.g., 1m, 2m) - - Background refresh at 80% of TTL ensures smooth transitions without lookup delays - -**DNS Caching Best Practices:** -- **Static Infrastructure**: Use longer TTL (15m-1h) for stable production environments -- **Dynamic DNS**: Use shorter TTL (1m-5m) for frequently changing hostnames -- **Development**: Use shorter TTL (1m-2m) for rapid iteration and testing -- **High-Traffic Services**: Balance between freshness and reduced DNS server load -- **Geographic Distribution**: Consider DNS propagation delays when setting TTL - -Example DNS tuning: -```bash -# Long TTL for stable production environment -export NP_DNS_CACHING_TTL=30m - -# Short TTL for dynamic development environment -export NP_DNS_CACHING_TTL=2m -``` - ### UDP Settings For applications relying heavily on UDP traffic: diff --git a/nodepass/docs/en/examples.md b/nodepass/docs/en/examples.md index dc5a5888a9..e06a65dad0 100644 --- a/nodepass/docs/en/examples.md +++ b/nodepass/docs/en/examples.md @@ -263,113 +263,93 @@ This setup: - **Load Distribution**: Distributing outbound traffic across multiple network links - **Network Testing**: Simulating traffic from specific network locations -## DNS Configuration for Custom Resolution +## DNS Cache TTL Configuration -### Example 15: Corporate DNS for Internal Services +### Example 15: Stable Corporate Network -Use corporate DNS servers to resolve internal hostnames: +Use longer TTL for stable internal services: ```bash -# Server side: Use corporate DNS servers for internal service resolution -nodepass "server://0.0.0.0:10101/internal-api.corp.local:8080?dns=10.0.0.53,10.0.1.53&mode=2&tls=1" +# Server side: 1-hour cache TTL for stable internal hostnames +nodepass "server://0.0.0.0:10101/internal-api.corp.local:8080?dns=1h&mode=2&tls=1" -# Client side: Use same DNS servers for consistent resolution -nodepass "client://tunnel.corp.local:10101/127.0.0.1:8080?dns=10.0.0.53,10.0.1.53" +# Client side: Same TTL for consistent behavior +nodepass "client://tunnel.corp.local:10101/127.0.0.1:8080?dns=1h" ``` This configuration: -- Uses corporate DNS servers (10.0.0.53 and 10.0.1.53) instead of public DNS -- Resolves internal .corp.local domains that are not accessible via public DNS -- Automatic failover to second DNS server if first fails -- DNS results are cached for 5 minutes (default) to improve performance -- Both server and client use consistent DNS configuration +- Uses 1-hour DNS cache TTL for stable internal services +- Reduces DNS query overhead in corporate networks +- Improves connection performance by minimizing DNS lookups +- Suitable for production environments with stable DNS -### Example 16: Privacy-Focused DNS Providers +### Example 16: Dynamic DNS Environments -Use privacy-enhanced DNS providers for improved security: +Use shorter TTL for frequently changing DNS records: ```bash -# Server side: Use Cloudflare DNS (privacy-focused) -nodepass "server://0.0.0.0:10101/api.example.com:443?dns=1.1.1.1,1.0.0.1&tls=1&log=info" +# Server side: 30-second cache TTL for dynamic DNS +nodepass "server://0.0.0.0:10101/dynamic.example.com:8080?dns=30s&tls=1&log=info" -# Client side: Use Quad9 DNS (malware blocking) -nodepass "client://server.example.com:10101/127.0.0.1:8080?dns=9.9.9.9,149.112.112.112" +# Client side: Short TTL for load balancing scenarios +nodepass "client://server.example.com:10101/127.0.0.1:8080?dns=30s" ``` This setup: -- Server uses Cloudflare DNS (1.1.1.1) known for privacy protection -- Client uses Quad9 DNS (9.9.9.9) with built-in malware domain blocking -- Multiple DNS servers provide redundancy -- All DNS queries are encrypted via DNS-over-TLS when supported by resolvers +- Uses 30-second DNS cache TTL for dynamic environments +- Enables faster failover for load-balanced services +- Ensures connections use current DNS records +- Ideal for cloud environments with frequent IP changes -### Example 17: Geographic DNS Optimization +### Example 17: Development and Testing -Select DNS servers geographically close to deployment for lower latency: +Disable caching for development environments: ```bash -# Asia-Pacific region: Use Google DNS Asia servers -nodepass "server://0.0.0.0:10101/service.example.com:8080?dns=8.8.8.8,8.8.4.4&mode=2" +# Development server: No DNS caching for immediate updates +nodepass "server://0.0.0.0:10101/dev.backend.local:8080?dns=0&tls=0&log=debug" -# Europe: Use Cloudflare DNS -nodepass "server://0.0.0.0:10101/service.example.com:8080?dns=1.1.1.1,1.0.0.1&mode=2" - -# Custom region: Use local ISP DNS for best performance -nodepass "server://0.0.0.0:10101/service.example.com:8080?dns=203.0.113.1,203.0.113.2&mode=2" +# Testing client: No caching to see DNS changes immediately +nodepass "client://dev-server.local:10101/127.0.0.1:8080?dns=0&log=debug" ``` This configuration: -- Reduces DNS lookup latency by using geographically proximate servers -- Improves initial connection establishment time -- Works with any DNS server accessible via UDP port 53 +- Disables DNS caching (dns=0) for immediate updates +- Every connection performs fresh DNS lookup +- Useful during development when DNS records change frequently +- Helps identify DNS-related issues during testing -### Example 18: IPv6 DNS Configuration +### Example 18: Mixed Environment with Custom TTL -Configure IPv6 DNS servers for IPv6-enabled networks: +Balance performance and freshness with moderate TTL: ```bash -# Server side: Use IPv6 DNS servers (Cloudflare and Google) -nodepass "server://[::]:10101/ipv6.example.com:8080?dns=2606:4700:4700::1111,2001:4860:4860::8888&mode=2&tls=1" +# Production API: 10-minute cache for balanced performance +nodepass "server://0.0.0.0:10101/api.example.com:8080?dns=10m&tls=1&mode=2" -# Client side: Mixed IPv4 and IPv6 DNS servers -nodepass "client://server.example.com:10101/127.0.0.1:8080?dns=1.1.1.1,2606:4700:4700::1111" +# Staging environment: 2-minute cache for faster updates +nodepass "server://0.0.0.0:10102/staging.example.com:8080?dns=2m&tls=1&mode=2" + +# Client: Default 5-minute cache +nodepass "client://server.example.com:10101/127.0.0.1:8080" ``` This setup: -- Server listens on all IPv6 interfaces using [::] -- Uses IPv6 DNS servers for native IPv6 resolution -- Mixed IPv4/IPv6 DNS configuration provides maximum compatibility -- Automatically selects IPv4 or IPv6 addresses based on connection type +- Production uses 10-minute TTL for good performance +- Staging uses 2-minute TTL for faster DNS updates +- Client uses default 5-minute TTL +- Each environment optimized for its use case -### Example 19: DNS Caching Tuning for Different Environments - -Adjust DNS cache TTL for optimal performance: - -```bash -# Production: Stable DNS, longer cache TTL (30 minutes) -export NP_DNS_CACHING_TTL=30m -nodepass "server://0.0.0.0:10101/stable.backend.com:8080?dns=1.1.1.1,8.8.8.8&tls=1" - -# Development: Dynamic DNS, shorter cache TTL (1 minute) -export NP_DNS_CACHING_TTL=1m -nodepass "server://0.0.0.0:10101/dev.backend.local:8080?dns=10.0.0.53&tls=0&log=debug" -``` - -This configuration: -- **Production (30m TTL)**: Maximizes cache efficiency for stable environments -- **Development (1m TTL)**: Quick DNS updates for rapidly changing configurations -- Background refresh at 80% of TTL ensures smooth transitions - -**DNS Configuration Use Cases**: -- **Corporate Networks**: Resolve internal hostnames via corporate DNS infrastructure -- **Privacy Enhancement**: Use privacy-focused DNS providers (1.1.1.1, 9.9.9.9) -- **Geographic Optimization**: Reduce latency with regionally proximate DNS servers -- **Development/Testing**: Quick DNS updates with short TTL in dynamic environments -- **High Availability**: Multiple DNS servers provide redundancy and failover -- **Compliance**: Meet regulatory requirements for DNS provider selection +**DNS Cache TTL Use Cases**: +- **Corporate Networks**: Long TTL (1h) for stable internal hostnames +- **Dynamic DNS**: Short TTL (30s-1m) for frequently changing records +- **Load Balancing**: Short TTL enables faster failover +- **Performance**: Longer TTL reduces connection latency +- **High Availability**: Moderate TTL balances freshness and performance ## High Availability and Load Balancing -### Example 20: Multi-Backend Server Load Balancing +### Example 19: Multi-Backend Server Load Balancing Use target address groups for even traffic distribution and automatic failover: @@ -387,7 +367,7 @@ This configuration: - Automatically resumes sending traffic to recovered servers - Uses TLS encryption to secure the tunnel -### Example 21: Database Primary-Replica Failover +### Example 20: Database Primary-Replica Failover Configure primary and replica database instances for high availability access: @@ -402,7 +382,7 @@ This setup: - Application requires no modification for transparent failover - Logs only warnings and errors to reduce output -### Example 22: API Gateway Backend Pool +### Example 21: API Gateway Backend Pool Configure multiple backend service instances for an API gateway: @@ -420,7 +400,7 @@ This configuration: - Client limits bandwidth to 100 Mbps with maximum 2000 concurrent connections - Single instance failure doesn't affect overall service availability -### Example 23: Geo-Distributed Services +### Example 22: Geo-Distributed Services Configure multi-region service nodes to optimize network latency: @@ -444,7 +424,7 @@ This setup: ## PROXY Protocol Integration -### Example 24: Load Balancer Integration with PROXY Protocol +### Example 23: Load Balancer Integration with PROXY Protocol Enable PROXY protocol support for integration with load balancers and reverse proxies: @@ -463,7 +443,7 @@ This configuration: - Compatible with HAProxy, Nginx, and other PROXY protocol aware services - Useful for maintaining accurate access logs and IP-based access controls -### Example 25: Reverse Proxy Support for Web Applications +### Example 24: Reverse Proxy Support for Web Applications Enable web applications behind NodePass to receive original client information: @@ -487,7 +467,7 @@ This setup: - Supports compliance requirements for connection auditing - Works with web servers that support PROXY protocol (Nginx, HAProxy, etc.) -### Example 26: Database Access with Client IP Preservation +### Example 25: Database Access with Client IP Preservation Maintain client IP information for database access logging and security: @@ -514,7 +494,7 @@ Benefits: ## Container Deployment -### Example 27: Containerized NodePass +### Example 26: Containerized NodePass Deploy NodePass in a Docker environment: @@ -549,7 +529,7 @@ This configuration: ## Master API Management -### Example 28: Centralized Management +### Example 27: Centralized Management Set up a central controller for multiple NodePass instances: @@ -586,7 +566,7 @@ This setup: - Offers a RESTful API for automation and integration - Includes a built-in Swagger UI at http://localhost:9090/api/v1/docs -### Example 29: Custom API Prefix +### Example 28: Custom API Prefix Use a custom API prefix for the master mode: @@ -605,7 +585,7 @@ This allows: - Custom URL paths for security or organizational purposes - Swagger UI access at http://localhost:9090/admin/v1/docs -### Example 30: Real-time Connection and Traffic Monitoring +### Example 29: Real-time Connection and Traffic Monitoring Monitor instance connection counts and traffic statistics through the master API: @@ -645,7 +625,7 @@ This monitoring setup provides: ## QUIC Transport Protocol -### Example 31: QUIC-based Tunnel with Stream Multiplexing +### Example 30: QUIC-based Tunnel with Stream Multiplexing Use QUIC protocol for connection pooling with improved performance in high-latency networks: @@ -665,7 +645,7 @@ This configuration: - Improved connection establishment with 0-RTT support - Client automatically receives QUIC configuration from server during handshake -### Example 32: QUIC with Custom TLS Certificate +### Example 31: QUIC with Custom TLS Certificate Deploy QUIC tunnel with verified certificates for production: @@ -684,7 +664,7 @@ This setup: - Full certificate validation on client side - QUIC configuration automatically delivered from server -### Example 33: QUIC for Mobile/High-Latency Networks +### Example 32: QUIC for Mobile/High-Latency Networks Optimize for mobile networks or satellite connections: @@ -704,7 +684,7 @@ This configuration: - 0-RTT reconnection for faster recovery after network changes - Client automatically adopts QUIC from server -### Example 34: QUIC vs TCP Pool Performance Comparison +### Example 33: QUIC vs TCP Pool Performance Comparison Side-by-side comparison of QUIC and TCP pools: @@ -730,7 +710,7 @@ nodepass "client://server.example.com:10102/127.0.0.1:8081?mode=2&min=128&log=ev - Improved NAT traversal capabilities - Single UDP socket reduces resource usage -### Example 35: QUIC for Real-Time Applications +### Example 34: QUIC for Real-Time Applications Configure QUIC tunnel for gaming, VoIP, or video streaming: diff --git a/nodepass/docs/en/how-it-works.md b/nodepass/docs/en/how-it-works.md index 286d46a721..3a2ee59ddd 100644 --- a/nodepass/docs/en/how-it-works.md +++ b/nodepass/docs/en/how-it-works.md @@ -47,11 +47,6 @@ NodePass creates a network architecture with separate channels for control and d - **TCP**: Full bidirectional streaming with persistent connections, optimized for direct connection establishment in client single-end forwarding mode - **UDP**: Datagram forwarding with configurable buffer sizes and timeouts -6. **Smart DNS Resolution**: - - Intelligent caching with background refresh for optimal performance - - Custom DNS servers with automatic failover - - Native IPv4/IPv6 support for modern networks - ## Data Transmission Flow NodePass establishes a bidirectional data flow through its tunnel architecture, supporting both TCP and UDP protocols. The system supports three data flow modes: diff --git a/nodepass/docs/en/troubleshooting.md b/nodepass/docs/en/troubleshooting.md index ef1a8eec90..5d7f30ee7f 100644 --- a/nodepass/docs/en/troubleshooting.md +++ b/nodepass/docs/en/troubleshooting.md @@ -245,15 +245,15 @@ This guide helps you diagnose and resolve common issues you might encounter when **Solutions**: -1. **Verify DNS Configuration** - - Test DNS server reachability: `ping 1.1.1.1` - - Verify resolution works: `nslookup example.com 8.8.8.8` - - Ensure `dns` parameter contains valid IP addresses +1. **Verify System DNS Configuration** + - Verify resolution works: `nslookup example.com` + - Check system's DNS settings (NodePass uses system resolver) + - Ensure network connectivity is working 2. **Network Connectivity** - Check if firewall blocks UDP port 53 - - Try alternative DNS: `dns=8.8.8.8,1.1.1.1` or `dns=223.5.5.5,119.29.29.29` - - Use internal DNS for corporate networks: `dns=10.0.0.1,8.8.8.8` + - Verify domain reachability + - Test with alternative domains to isolate issue ### DNS Caching Problems @@ -262,12 +262,11 @@ This guide helps you diagnose and resolve common issues you might encounter when **Solutions**: 1. **Adjust Cache TTL** (default 5 minutes) - - Dynamic environments: `export NP_DNS_CACHING_TTL=1m` - - Stable environments: `export NP_DNS_CACHING_TTL=30m` - - Restart NodePass after critical changes + - Dynamic environments: `dns=1m` + - Stable environments: `dns=30m` 2. **Load Balancing Scenarios** - - Use shorter TTL: `export NP_DNS_CACHING_TTL=30s` + - Use shorter TTL: `dns=30s` - Or use IP addresses directly to bypass DNS caching ### DNS Performance Optimization @@ -276,14 +275,15 @@ This guide helps you diagnose and resolve common issues you might encounter when **Solutions**: -1. **Choose Appropriate DNS Servers** - - China: `dns=223.5.5.5,119.29.29.29` - - International: `dns=1.1.1.1,8.8.8.8` - - Use at least 2 DNS servers for redundancy +1. **Optimize Cache TTL** + - Increase TTL for stable environments: `dns=1h` + - Reduce TTL for dynamic environments: `dns=1m` + - Balance between freshness and performance 2. **Reduce DNS Queries** - Use IP addresses directly for performance-critical scenarios - - Increase TTL for stable environments: `export NP_DNS_CACHING_TTL=1h` + - Increase TTL for stable hostnames + - Pre-resolve addresses when possible ## Master API Issues diff --git a/nodepass/docs/en/usage.md b/nodepass/docs/en/usage.md index 928d7ad93e..0447ee8b70 100644 --- a/nodepass/docs/en/usage.md +++ b/nodepass/docs/en/usage.md @@ -7,7 +7,7 @@ NodePass creates tunnels with an unencrypted TCP control channel and configurabl The general syntax for NodePass commands is: ```bash -nodepass ":///?log=&tls=&crt=&key=&dns=&min=&max=&mode=&quic=&dial=&read=&rate=&slot=&proxy=¬cp=<0|1>&noudp=<0|1>" +nodepass ":///?log=&tls=&crt=&key=&dns=&min=&max=&mode=&quic=&dial=&read=&rate=&slot=&proxy=¬cp=<0|1>&noudp=<0|1>" ``` Where: @@ -19,7 +19,7 @@ Where: Common query parameters: - `log=`: Log verbosity level (`none`, `debug`, `info`, `warn`, `error`, or `event`) -- `dns=`: Custom DNS servers (comma-separated IP addresses, default: `1.1.1.1,8.8.8.8`) +- `dns=`: DNS cache TTL duration (default: `5m`, supports time units like `1h`, `30m`, `15s`, etc.) - `min=`: Minimum connection pool capacity (default: 64, set by client) - `max=`: Maximum connection pool capacity (default: 1024, set by server and delivered to client) - `mode=`: Run mode control (`0`, `1`, or `2`) - controls operational behavior @@ -51,7 +51,7 @@ NodePass offers three complementary operating modes to suit various deployment s Server mode establishes tunnel control channels and supports bidirectional data flow forwarding. ```bash -nodepass "server:///?log=&tls=&crt=&key=&dns=&quic=&max=&mode=&dial=&read=&rate=&slot=&proxy=¬cp=<0|1>&noudp=<0|1>" +nodepass "server:///?log=&tls=&crt=&key=&dns=&quic=&max=&mode=&dial=&read=&rate=&slot=&proxy=¬cp=<0|1>&noudp=<0|1>" ``` #### Parameters @@ -59,7 +59,7 @@ nodepass "server:///?log=&tls=&crt=/?log=&dns=&quic=&min=&mode=&dial=&read=&rate=&slot=&proxy=¬cp=<0|1>&noudp=<0|1>" +nodepass "client:///?log=&dns=&quic=&min=&mode=&dial=&read=&rate=&slot=&proxy=¬cp=<0|1>&noudp=<0|1>" ``` #### Parameters @@ -135,7 +135,7 @@ nodepass "client:///?log=&dns=&qui - `tunnel_addr`: Address of the NodePass server's tunnel endpoint to connect to (e.g., 10.1.0.1:10101) - `target_addr`: The destination address for business data with bidirectional flow support (e.g., 127.0.0.1:8080) - `log`: Log level (debug, info, warn, error, event) -- `dns`: Custom DNS servers (comma-separated IP addresses, default: 1.1.1.1,8.8.8.8) +- `dns`: DNS cache TTL duration (default: 5m, supports time units like `1h`, `30m`, `15s`, etc.) - `min`: Minimum connection pool capacity (default: 64) - `mode`: Run mode control for client behavior - `0`: Automatic detection (default) - attempts local binding first, falls back to handshake mode diff --git a/nodepass/docs/zh/api.md b/nodepass/docs/zh/api.md index 77a296fded..89e0797a7f 100644 --- a/nodepass/docs/zh/api.md +++ b/nodepass/docs/zh/api.md @@ -65,7 +65,7 @@ API Key 认证默认启用,首次启动自动生成并保存在 `nodepass.gob` "type": "client|server", "status": "running|stopped|error", "url": "...", - "config": "server://0.0.0.0:8080/localhost:3000?log=info&tls=1&dns=1.1.1.1,8.8.8.8&max=1024&mode=0&quic=0&dial=auto&read=1h&rate=100&slot=65536&proxy=0¬cp=0&noudp=0", + "config": "server://0.0.0.0:8080/localhost:3000?log=info&tls=1&dns=5m&max=1024&mode=0&quic=0&dial=auto&read=1h&rate=100&slot=65536&proxy=0¬cp=0&noudp=0", "restart": true, "meta": { "peer": { @@ -1116,7 +1116,7 @@ API响应中的实例对象包含以下字段: "type": "server", // 实例类型:server 或 client "status": "running", // 实例状态:running、stopped 或 error "url": "server://...", // 实例配置URL - "config": "server://0.0.0.0:8080/localhost:3000?log=info&tls=1&dns=1.1.1.1,8.8.8.8&max=1024&mode=0&quic=0&dial=auto&read=1h&rate=100&slot=65536&proxy=0¬cp=0&noudp=0", // 完整配置URL + "config": "server://0.0.0.0:8080/localhost:3000?log=info&tls=1&dns=5m&max=1024&mode=0&quic=0&dial=auto&read=1h&rate=100&slot=65536&proxy=0¬cp=0&noudp=0", // 完整配置URL "restart": true, // 自启动策略 "meta": { // 用于组织和对端跟踪的元数据 "peer": { @@ -1581,7 +1581,7 @@ client://:/:? | `tls` | TLS加密级别 | `0`(无), `1`(自签名), `2`(证书) | `0` | 仅服务端 | | `crt` | 证书路径 | 文件路径 | 无 | 仅服务端 | | `key` | 私钥路径 | 文件路径 | 无 | 仅服务端 | -| `dns` | 自定义DNS服务器 | IP地址,逗号分隔 | `1.1.1.1,8.8.8.8` | 两者 | +| `dns` | DNS缓存时间 | 时间长度 (如 `10m`, `30s`, `1h`) | `5m` | 两者 | | `min` | 最小连接池容量 | 整数 > 0 | `64` | 仅客户端双端握手模式 | | `max` | 最大连接池容量 | 整数 > 0 | `1024` | 双端握手模式 | | `mode` | 运行模式控制 | `0`(自动), `1`(强制模式1), `2`(强制模式2) | `0` | 两者 | diff --git a/nodepass/docs/zh/configuration.md b/nodepass/docs/zh/configuration.md index 237e2b7d18..ca8b5407de 100644 --- a/nodepass/docs/zh/configuration.md +++ b/nodepass/docs/zh/configuration.md @@ -109,63 +109,59 @@ nodepass "server://0.0.0.0:10101/remote.example.com:8080?mode=2" ## DNS解析配置 -NodePass支持自定义DNS服务器配置,具有智能缓存功能,可提高性能和可靠性。内置DNS解析器提供后台刷新、自动故障转移和可配置的TTL管理。 +NodePass使用系统内置的DNS解析器,具有智能缓存功能,可提高性能和可靠性。DNS缓存减少了查询开销并防止解析延迟。 -- `dns`:自定义DNS服务器地址(默认:1.1.1.1,8.8.8.8) - - 以逗号分隔的IP地址列表(IPv4或IPv6) - - 多个DNS服务器提供自动故障转移和轮询负载均衡 - - 列表中的无效IP地址会被记录并跳过 - - 如果所有提供的DNS服务器都失败,系统会回退到操作系统默认DNS解析 - - DNS解析使用专用端口(UDP端口53)进行所有查询 +- `dns`:DNS缓存TTL持续时间(默认:5m) + - 指定已解析的主机名在重新查询前缓存多长时间 + - 接受时间持续时间格式:`1h`、`30m`、`15s`、`500ms`等 + - 较长的TTL减少DNS查询开销,但可能缓存过时记录 + - 较短的TTL确保更新的DNS数据,但增加查询频率 + - 设置为`0`以禁用缓存(每次连接都查询DNS) - 适用于客户端和服务端模式,用于解析所有主机名 -**DNS解析器特性:** -- **智能缓存**:已解析的主机名会使用可配置的TTL进行缓存,以减少DNS查询开销 -- **后台刷新**:缓存条目在过期前(TTL的80%时)会主动刷新,以防止查找延迟 -- **自动故障转移**:当DNS服务器失败时,自动尝试列表中的下一个服务器 -- **轮询分发**:DNS查询在配置的服务器之间分发以实现负载均衡 +**DNS缓存特性:** +- **系统集成**:使用操作系统的原生DNS解析器以实现最大兼容性 +- **智能缓存**:已解析的主机名会使用可配置的TTL进行缓存,以减少查询开销 +- **自动过期**:缓存条目在TTL过期后自动删除 - **IP地址绕过**:直接IP地址跳过DNS解析以获得最高效率 -- **协议感知**:根据连接要求自动选择IPv4或IPv6地址 +- **协议感知**:自动处理IPv4和IPv6地址 +- **线程安全**:并发DNS查找被安全地缓存并在连接之间共享 示例: ```bash -# 使用Cloudflare和Google DNS服务器(默认行为) -nodepass "server://0.0.0.0:10101/example.com:8080?dns=1.1.1.1,8.8.8.8" +# 使用默认5分钟缓存TTL +nodepass "server://0.0.0.0:10101/example.com:8080" -# 使用自定义DNS服务器(例如企业DNS) -nodepass "server://0.0.0.0:10101/internal.example.com:8080?dns=10.0.0.53,10.0.1.53" +# 为稳定域名设置1小时缓存TTL +nodepass "server://0.0.0.0:10101/internal.example.com:8080?dns=1h" -# 带有特定DNS配置的客户端 -nodepass "client://server.example.com:10101/database.local:3306?dns=192.168.1.1,192.168.1.2" +# 为动态DNS设置30秒缓存TTL +nodepass "client://server.example.com:10101/database.local:3306?dns=30s" -# IPv6 DNS服务器 -nodepass "server://0.0.0.0:10101/service.example.com:8080?dns=2606:4700:4700::1111,2001:4860:4860::8888" +# 完全禁用DNS缓存(每次连接都查询) +nodepass "server://0.0.0.0:10101/service.example.com:8080?dns=0" # 与其他参数组合 -nodepass "server://0.0.0.0:10101/backend.example.com:8080?dns=1.1.1.1,8.8.8.8&log=info&tls=1&mode=2" +nodepass "server://0.0.0.0:10101/backend.example.com:8080?dns=10m&log=info&tls=1&mode=2" ``` **DNS配置使用场景:** -- **企业网络**:使用内部DNS服务器解析私有主机名 -- **地理优化**:选择地理位置接近部署的DNS服务器 -- **隐私增强**:使用注重隐私的DNS提供商(例如1.1.1.1、9.9.9.9) -- **可靠性**:配置多个DNS服务器以实现高可用性 -- **合规性**:满足DNS提供商选择的监管要求 -- **测试**:为开发或暂存环境使用特定DNS服务器 -- **性能**:使用更近或更快的DNS服务器减少DNS查找延迟 +- **企业网络**:对稳定的内部主机名使用较长的TTL(例如1h) +- **动态DNS**:对频繁变化的DNS记录使用较短的TTL(例如30s) +- **高可用性**:较长的TTL减少DNS服务器负载并提高可靠性 +- **负载均衡**:较短的TTL使负载均衡服务能更快切换 +- **性能**:较长的TTL通过减少DNS查询来降低连接延迟 **DNS缓存行为:** -- 缓存TTL由`NP_DNS_CACHING_TTL`环境变量控制(默认:5分钟) -- 后台刷新在TTL的80%时发生,以在不延迟的情况下保持新鲜数据 +- 缓存TTL通过`dns`查询参数配置(默认:5分钟) - 过期条目被删除,下次访问时执行新的查找 - 缓存是每个实例的,不在NodePass进程之间共享 - IP地址永远不会被缓存(直接使用,不需要DNS查找) +- 系统DNS解析器用于所有主机名查找 **重要说明:** -- DNS服务器必须指定为IP地址,而不是主机名 -- 支持IPv4和IPv6 DNS服务器地址 -- DNS解析超时固定为每次查询5秒 -- 失败的DNS服务器在后续查询中会重试(没有永久黑名单) +- 支持IPv4和IPv6地址 +- DNS解析超时由操作系统控制 - 使用目标地址组时,每个地址独立解析 - DNS解析适用于隧道地址和目标地址 - 隧道地址DNS解析在启动时发生一次 @@ -571,7 +567,7 @@ NodePass支持通过URL查询参数进行灵活配置,不同参数在 server、c | `tls` | TLS加密模式 | `0` | O | X | O | | `crt` | 自定义证书路径 | N/A | O | X | O | | `key` | 自定义密钥路径 | N/A | O | X | O | -| `dns` | 自定义DNS服务器 | `1.1.1.1,8.8.8.8` | O | O | X | +| `dns` | DNS缓存TTL | `5m` | O | O | X | | `min` | 最小连接池容量 | `64` | X | O | X | | `max` | 最大连接池容量 | `1024` | O | X | X | | `mode` | 运行模式控制 | `0` | O | O | X | @@ -606,7 +602,6 @@ NodePass支持通过URL查询参数进行灵活配置,不同参数在 server、c | `NP_SEMAPHORE_LIMIT` | 信号缓冲区大小 | 65536 | `export NP_SEMAPHORE_LIMIT=2048` | | `NP_TCP_DATA_BUF_SIZE` | TCP数据传输缓冲区大小 | 16384 | `export NP_TCP_DATA_BUF_SIZE=65536` | | `NP_UDP_DATA_BUF_SIZE` | UDP数据包缓冲区大小 | 16384 | `export NP_UDP_DATA_BUF_SIZE=16384` | -| `NP_DNS_CACHING_TTL` | DNS缓存保质期 | 5m | `export NP_DNS_CACHING_TTL=10m` | | `NP_HANDSHAKE_TIMEOUT` | 握手操作超时 | 5s | `export NP_HANDSHAKE_TIMEOUT=30s` | | `NP_UDP_READ_TIMEOUT` | UDP读取操作超时 | 30s | `export NP_UDP_READ_TIMEOUT=60s` | | `NP_TCP_DIAL_TIMEOUT` | TCP连接建立超时 | 5s | `export NP_TCP_DIAL_TIMEOUT=60s` | @@ -652,33 +647,6 @@ NodePass支持通过URL查询参数进行灵活配置,不同参数在 server、c - 太大:内存使用增加 - 推荐范围:1000-5000 -### DNS解析调优 - -对于频繁进行主机名查找或动态DNS场景的应用: - -- `NP_DNS_CACHING_TTL`:DNS缓存保质期 - - 控制已解析DNS条目在缓存中保留的时长 - - 默认值(5m)在大多数场景下平衡了新鲜度和性能 - - 对于稳定的DNS环境,增加此值以减少查询开销(例如15m、30m、1h) - - 对于需要新鲜查找的动态DNS环境,减小此值(例如1m、2m) - - 后台刷新在TTL的80%时进行,确保平滑过渡而不会出现查找延迟 - -**DNS缓存最佳实践:** -- **静态基础设施**:对于稳定的生产环境使用较长TTL(15m-1h) -- **动态DNS**:对于频繁变化的主机名使用较短TTL(1m-5m) -- **开发环境**:对于快速迭代和测试使用较短TTL(1m-2m) -- **高流量服务**:在新鲜度和减少DNS服务器负载之间取得平衡 -- **地理分布**:设置TTL时考虑DNS传播延迟 - -DNS调优示例: -```bash -# 稳定生产环境的长TTL -export NP_DNS_CACHING_TTL=30m - -# 动态开发环境的短TTL -export NP_DNS_CACHING_TTL=2m -``` - ### UDP设置 对于严重依赖UDP流量的应用: diff --git a/nodepass/docs/zh/examples.md b/nodepass/docs/zh/examples.md index aca204e026..747459622f 100644 --- a/nodepass/docs/zh/examples.md +++ b/nodepass/docs/zh/examples.md @@ -263,113 +263,93 @@ nodepass "client://server.example.com:10101/127.0.0.1:8080?dial=auto" - **负载分配**:在多个网络链路之间分配出站流量 - **网络测试**:模拟来自特定网络位置的流量 -## DNS配置用于自定义解析 +## DNS缓存TTL配置 -### 示例15:企业DNS用于内部服务 +### 示例15:稳定的企业网络 -使用企业DNS服务器解析内部主机名: +为稳定的内部服务使用较长的TTL: ```bash -# 服务端:使用企业DNS服务器解析内部服务 -nodepass "server://0.0.0.0:10101/internal-api.corp.local:8080?dns=10.0.0.53,10.0.1.53&mode=2&tls=1" +# 服务端:为稳定的内部主机名使用1小时缓存TTL +nodepass "server://0.0.0.0:10101/internal-api.corp.local:8080?dns=1h&mode=2&tls=1" -# 客户端:使用相同的DNS服务器以保持一致的解析 -nodepass "client://tunnel.corp.local:10101/127.0.0.1:8080?dns=10.0.0.53,10.0.1.53" +# 客户端:使用相同的TTL以保持一致行为 +nodepass "client://tunnel.corp.local:10101/127.0.0.1:8080?dns=1h" ``` 此配置: -- 使用企业DNS服务器(10.0.0.53和10.0.1.53)而不是公共DNS -- 解析无法通过公共DNS访问的内部.corp.local域 -- 如果第一个DNS服务器失败,自动故障转移到第二个 -- DNS结果缓存5分钟(默认)以提高性能 -- 服务端和客户端使用一致的DNS配置 +- 为稳定的内部服务使用1小时DNS缓存TTL +- 减少企业网络中的DNS查询开销 +- 通过最小化DNS查找提高连接性能 +- 适用于DNS稳定的生产环境 -### 示例16:注重隐私的DNS提供商 +### 示例16:动态DNS环境 -使用增强隐私的DNS提供商以提高安全性: +为频繁变化的DNS记录使用较短的TTL: ```bash -# 服务端:使用Cloudflare DNS(注重隐私) -nodepass "server://0.0.0.0:10101/api.example.com:443?dns=1.1.1.1,1.0.0.1&tls=1&log=info" +# 服务端:为动态DNS使用30秒缓存TTL +nodepass "server://0.0.0.0:10101/dynamic.example.com:8080?dns=30s&tls=1&log=info" -# 客户端:使用Quad9 DNS(恶意软件拦截) -nodepass "client://server.example.com:10101/127.0.0.1:8080?dns=9.9.9.9,149.112.112.112" +# 客户端:为负载均衡场景使用短TTL +nodepass "client://server.example.com:10101/127.0.0.1:8080?dns=30s" ``` 此设置: -- 服务端使用Cloudflare DNS(1.1.1.1),以隐私保护著称 -- 客户端使用Quad9 DNS(9.9.9.9),内置恶意域名拦截 -- 多个DNS服务器提供冗余 -- 当解析器支持时,所有DNS查询通过DNS-over-TLS加密 +- 为动态环境使用30秒DNS缓存TTL +- 为负载均衡服务实现更快的故障转移 +- 确保连接使用当前的DNS记录 +- 适合IP频繁变化的云环境 -### 示例17:地理DNS优化 +### 示例17:开发和测试 -选择地理位置接近部署的DNS服务器以降低延迟: +为开发环境禁用缓存: ```bash -# 亚太地区:使用Google DNS亚洲服务器 -nodepass "server://0.0.0.0:10101/service.example.com:8080?dns=8.8.8.8,8.8.4.4&mode=2" +# 开发服务器:不使用DNS缓存以立即更新 +nodepass "server://0.0.0.0:10101/dev.backend.local:8080?dns=0&tls=0&log=debug" -# 欧洲:使用Cloudflare DNS -nodepass "server://0.0.0.0:10101/service.example.com:8080?dns=1.1.1.1,1.0.0.1&mode=2" - -# 自定义区域:使用本地ISP DNS以获得最佳性能 -nodepass "server://0.0.0.0:10101/service.example.com:8080?dns=203.0.113.1,203.0.113.2&mode=2" +# 测试客户端:不使用缓存以立即查看DNS更改 +nodepass "client://dev-server.local:10101/127.0.0.1:8080?dns=0&log=debug" ``` 此配置: -- 通过使用地理位置接近的服务器减少DNS查找延迟 -- 改善初始连接建立时间 -- 适用于通过UDP端口53可访问的任何DNS服务器 +- 禁用DNS缓存(dns=0)以立即更新 +- 每次连接都执行新的DNS查找 +- 在开发期间DNS记录频繁变化时很有用 +- 帮助在测试期间识别DNS相关问题 -### 示例18:IPv6 DNS配置 +### 示例18:混合环境的自定义TTL -为启用IPv6的网络配置IPv6 DNS服务器: +使用适中的TTL平衡性能和新鲜度: ```bash -# 服务端:使用IPv6 DNS服务器(Cloudflare和Google) -nodepass "server://[::]:10101/ipv6.example.com:8080?dns=2606:4700:4700::1111,2001:4860:4860::8888&mode=2&tls=1" +# 生产API:10分钟缓存以平衡性能 +nodepass "server://0.0.0.0:10101/api.example.com:8080?dns=10m&tls=1&mode=2" -# 客户端:混合IPv4和IPv6 DNS服务器 -nodepass "client://server.example.com:10101/127.0.0.1:8080?dns=1.1.1.1,2606:4700:4700::1111" +# 暂存环境:2分钟缓存以更快更新 +nodepass "server://0.0.0.0:10102/staging.example.com:8080?dns=2m&tls=1&mode=2" + +# 客户端:默认5分钟缓存 +nodepass "client://server.example.com:10101/127.0.0.1:8080" ``` 此设置: -- 服务端使用[::]监听所有IPv6接口 -- 使用IPv6 DNS服务器进行原生IPv6解析 -- 混合IPv4/IPv6 DNS配置提供最大兼容性 -- 根据连接类型自动选择IPv4或IPv6地址 +- 生产环境使用10分钟TTL以获得良好性能 +- 暂存环境使用2分钟TTL以更快地更新DNS +- 客户端使用默认5分钟TTL +- 每个环境针对其使用场景进行优化 -### 示例19:不同环境的DNS缓存调优 - -调整DNS缓存TTL以获得最佳性能: - -```bash -# 生产环境:稳定DNS,较长缓存TTL(30分钟) -export NP_DNS_CACHING_TTL=30m -nodepass "server://0.0.0.0:10101/stable.backend.com:8080?dns=1.1.1.1,8.8.8.8&tls=1" - -# 开发环境:动态DNS,较短缓存TTL(1分钟) -export NP_DNS_CACHING_TTL=1m -nodepass "server://0.0.0.0:10101/dev.backend.local:8080?dns=10.0.0.53&tls=0&log=debug" -``` - -此配置: -- **生产环境(30m TTL)**:为稳定环境最大化缓存效率 -- **开发环境(1m TTL)**:为快速变化的配置提供快速DNS更新 -- 在TTL的80%时进行后台刷新确保平滑过渡 - -**DNS配置使用场景**: -- **企业网络**:通过企业DNS基础设施解析内部主机名 -- **隐私增强**:使用注重隐私的DNS提供商(1.1.1.1、9.9.9.9) -- **地理优化**:使用区域接近的DNS服务器减少延迟 -- **开发/测试**:在动态环境中使用短TTL快速更新DNS -- **高可用性**:多个DNS服务器提供冗余和故障转移 -- **合规性**:满足DNS提供商选择的监管要求 +**DNS缓存TTL使用场景**: +- **企业网络**:为稳定的内部主机名使用长TTL(1h) +- **动态DNS**:为频繁变化的记录使用短TTL(30s-1m) +- **负载均衡**:短TTL实现更快的故障转移 +- **性能优化**:较长的TTL降低连接延迟 +- **高可用性**:适中的TTL平衡新鲜度和性能 ## 高可用性与负载均衡 -### 示例20:多后端服务器负载均衡 +### 示例19:多后端服务器负载均衡 使用目标地址组实现流量均衡分配和自动故障转移: @@ -387,7 +367,7 @@ nodepass "client://server.example.com:10101/127.0.0.1:8080?log=info" - 故障服务器恢复后自动重新接入流量 - 使用TLS加密确保隧道安全 -### 示例21:数据库主从切换 +### 示例20:数据库主从切换 为数据库配置主从实例,实现高可用访问: @@ -402,7 +382,7 @@ nodepass "client://127.0.0.1:3306/db-primary.local:3306,db-secondary.local:3306? - 应用程序无需修改,透明地实现故障转移 - 仅记录警告和错误,减少日志输出 -### 示例22:API网关后端池 +### 示例21:API网关后端池 为API网关配置多个后端服务实例: @@ -420,7 +400,7 @@ nodepass "client://apigateway.example.com:10101/127.0.0.1:8080?rate=100&slot=200 - 客户端限制带宽100 Mbps,最大2000并发连接 - 单个实例故障不影响整体服务可用性 -### 示例23:地域分布式服务 +### 示例22:地域分布式服务 配置多地域服务节点,优化网络延迟: @@ -444,7 +424,7 @@ nodepass "server://0.0.0.0:10101/us-west.service:8080,us-east.service:8080,eu-ce ## PROXY协议集成 -### 示例24:负载均衡器与PROXY协议集成 +### 示例23:负载均衡器与PROXY协议集成 启用PROXY协议支持,与负载均衡器和反向代理集成: @@ -463,7 +443,7 @@ nodepass "client://tunnel.example.com:10101/127.0.0.1:3000?log=info&proxy=1" - 兼容HAProxy、Nginx和其他支持PROXY协议的服务 - 有助于维护准确的访问日志和基于IP的访问控制 -### 示例25:Web应用的反向代理支持 +### 示例24:Web应用的反向代理支持 使NodePass后的Web应用能够接收原始客户端信息: @@ -487,7 +467,7 @@ nodepass "server://0.0.0.0:10101/127.0.0.1:8080?log=warn&tls=2&crt=/path/to/cert - 支持连接审计的合规性要求 - 适用于支持PROXY协议的Web服务器(Nginx、HAProxy等) -### 示例26:数据库访问与客户端IP保留 +### 示例25:数据库访问与客户端IP保留 为数据库访问日志记录和安全维护客户端IP信息: @@ -514,7 +494,7 @@ nodepass "client://dbproxy.example.com:10101/127.0.0.1:5432?proxy=1" ## 容器部署 -### 示例27:容器化NodePass +### 示例26:容器化NodePass 在Docker环境中部署NodePass: @@ -549,7 +529,7 @@ docker run -d --name nodepass-client \ ## 主控API管理 -### 示例28:集中化管理 +### 示例27:集中化管理 为多个NodePass实例设置中央控制器: @@ -586,7 +566,7 @@ curl -X PUT http://localhost:9090/api/v1/instances/{id} \ - 提供用于自动化和集成的RESTful API - 包含内置的Swagger UI,位于http://localhost:9090/api/v1/docs -### 示例29:自定义API前缀 +### 示例28:自定义API前缀 为主控模式使用自定义API前缀: @@ -605,7 +585,7 @@ curl -X POST http://localhost:9090/admin/v1/instances \ - 用于安全或组织目的的自定义URL路径 - 在http://localhost:9090/admin/v1/docs访问Swagger UI -### 示例30:实时连接和流量监控 +### 示例29:实时连接和流量监控 通过主控API监控实例的连接数和流量统计: @@ -645,7 +625,7 @@ curl -H "X-API-Key: your-api-key" \ ## QUIC传输协议 -### 示例31: 基于QUIC的流多路复用隧道 +### 示例30: 基于QUIC的流多路复用隧道 使用QUIC协议进行连接池管理,在高延迟网络中提供更优性能: @@ -665,7 +645,7 @@ nodepass "client://server.example.com:10101/127.0.0.1:8080?mode=2&min=128&log=de - 通过0-RTT支持改善连接建立 - 客户端在握手时自动接收服务器的QUIC配置 -### 示例32: 使用自定义TLS证书的QUIC +### 示例31: 使用自定义TLS证书的QUIC 在生产环境部署带有验证证书的QUIC隧道: @@ -684,7 +664,7 @@ nodepass "client://tunnel.example.com:10101/127.0.0.1:8080?mode=2&min=64&log=inf - 客户端进行完整证书验证 - QUIC配置自动从服务器下发 -### 示例33: 移动/高延迟网络的QUIC +### 示例32: 移动/高延迟网络的QUIC 针对移动网络或卫星连接进行优化: @@ -704,7 +684,7 @@ nodepass "client://mobile.tunnel.com:10101/127.0.0.1:8080?mode=2&min=256&log=war - 0-RTT重连在网络变化后实现更快恢复 - 客户端自动采用服务器的QUIC配置 -### 示例34: QUIC与TCP连接池性能对比 +### 示例33: QUIC与TCP连接池性能对比 QUIC和TCP连接池的并排比较: @@ -730,7 +710,7 @@ nodepass "client://server.example.com:10102/127.0.0.1:8081?mode=2&min=128&log=ev - 改善NAT穿透能力 - 单个UDP套接字减少资源使用 -### 示例35: 实时应用的QUIC +### 示例34: 实时应用的QUIC 为游戏、VoIP或视频流配置QUIC隧道: diff --git a/nodepass/docs/zh/how-it-works.md b/nodepass/docs/zh/how-it-works.md index 22a4311666..8c5d5d2480 100644 --- a/nodepass/docs/zh/how-it-works.md +++ b/nodepass/docs/zh/how-it-works.md @@ -47,11 +47,6 @@ NodePass 创建了一个具有独立控制和数据通道的网络架构: - **TCP**:具有持久连接的全双工流式传输,在客户端单端转发模式下优化了直接连接建立 - **UDP**:具有可配置缓冲区大小和超时的数据报转发 -6. **智能DNS解析**: - - 智能缓存与后台刷新,提供最优性能 - - 自定义DNS服务器与自动故障转移 - - 原生支持IPv4/IPv6现代网络 - ## 数据传输流 NodePass 通过其隧道架构建立双向数据流,支持 TCP 和 UDP 协议。系统支持三种数据流模式: diff --git a/nodepass/docs/zh/troubleshooting.md b/nodepass/docs/zh/troubleshooting.md index 0933861ed0..bd5c14122a 100644 --- a/nodepass/docs/zh/troubleshooting.md +++ b/nodepass/docs/zh/troubleshooting.md @@ -245,15 +245,15 @@ **解决方案**: -1. **验证DNS配置** - - 测试DNS服务器可达性:`ping 1.1.1.1` - - 验证解析功能:`nslookup example.com 8.8.8.8` - - 确保`dns`参数包含有效IP地址 +1. **验证系统DNS配置** + - 验证解析功能:`nslookup example.com` + - 检查系统的DNS设置(NodePass使用系统解析器) + - 确保网络连接正常工作 2. **网络连接问题** - 检查防火墙是否阻止UDP端口53 - - 尝试备用DNS:`dns=8.8.8.8,1.1.1.1`或`dns=223.5.5.5,119.29.29.29` - - 企业网络使用内部DNS:`dns=10.0.0.1,8.8.8.8` + - 验证域名可达性 + - 使用其他域名测试以隔离问题 ### DNS缓存问题 @@ -262,12 +262,11 @@ **解决方案**: 1. **调整缓存TTL**(默认5分钟) - - 动态环境:`export NP_DNS_CACHING_TTL=1m` - - 稳定环境:`export NP_DNS_CACHING_TTL=30m` - - 重要变更后重启NodePass + - 动态环境:`dns=1m` + - 稳定环境:`dns=30m` 2. **负载均衡场景** - - 使用较短TTL:`export NP_DNS_CACHING_TTL=30s` + - 使用较短TTL:`dns=30s` - 或直接使用IP地址避免DNS缓存 ### DNS性能优化 @@ -276,14 +275,15 @@ **解决方案**: -1. **选择合适的DNS服务器** - - 中国:`dns=223.5.5.5,119.29.29.29` - - 国际:`dns=1.1.1.1,8.8.8.8` - - 使用至少2个DNS服务器实现冗余 +1. **优化缓存TTL** + - 稳定环境增加TTL:`dns=1h` + - 动态环境减少TTL:`dns=1m` + - 在新鲜度和性能之间取得平衡 2. **减少DNS查询** - 性能关键场景直接使用IP地址 - - 稳定环境增加TTL:`export NP_DNS_CACHING_TTL=1h` + - 为稳定主机名增加TTL + - 尽可能预解析地址 ## 主控API问题 diff --git a/nodepass/docs/zh/usage.md b/nodepass/docs/zh/usage.md index 0fdf11604d..3fa47533d0 100644 --- a/nodepass/docs/zh/usage.md +++ b/nodepass/docs/zh/usage.md @@ -7,7 +7,7 @@ NodePass创建一个带有未加密TCP控制通道的隧道,并为数据交换 NodePass命令的一般语法是: ```bash -nodepass ":///?log=&tls=&crt=&key=&dns=&min=&max=&mode=&quic=&dial=&read=&rate=&slot=&proxy=¬cp=<0|1>&noudp=<0|1>" +nodepass ":///?log=&tls=&crt=&key=&dns=&min=&max=&mode=&quic=&dial=&read=&rate=&slot=&proxy=¬cp=<0|1>&noudp=<0|1>" ``` 其中: @@ -19,7 +19,7 @@ nodepass ":///?log=&tls=&crt=`:日志详细级别(`none`、`debug`、`info`、`warn`、`error` 或 `event`) -- `dns=`:自定义DNS服务器(逗号分隔的IP地址,默认:`1.1.1.1,8.8.8.8`) +- `dns=`:DNS缓存TTL持续时间(默认:`5m`,支持时间单位如`1h`、`30m`、`15s`等) - `min=`:最小连接池容量(默认:64,由客户端设置) - `max=`:最大连接池容量(默认:1024,由服务端设置并下发给客户端) - `mode=`:运行模式控制(`0`、`1` 或 `2`)- 控制操作行为 @@ -51,7 +51,7 @@ NodePass提供三种互补的运行模式,以适应各种部署场景。 服务端模式建立隧道控制通道,并支持双向数据流转发。 ```bash -nodepass "server:///?log=&tls=&crt=&key=&dns=&quic=&max=&mode=&dial=&read=&rate=&slot=&proxy=¬cp=<0|1>&noudp=<0|1>" +nodepass "server:///?log=&tls=&crt=&key=&dns=&quic=&max=&mode=&dial=&read=&rate=&slot=&proxy=¬cp=<0|1>&noudp=<0|1>" ``` #### 参数 @@ -59,7 +59,7 @@ nodepass "server:///?log=&tls=&crt=/?log=&dns=&quic=&min=&mode=&dial=&read=&rate=&slot=&proxy=¬cp=<0|1>&noudp=<0|1>" +nodepass "client:///?log=&dns=&quic=&min=&mode=&dial=&read=&rate=&slot=&proxy=¬cp=<0|1>&noudp=<0|1>" ``` #### 参数 @@ -135,7 +135,7 @@ nodepass "client:///?log=&dns=&qui - `tunnel_addr`:要连接的NodePass服务端隧道端点地址(例如, 10.1.0.1:10101) - `target_addr`:业务数据的目标地址,支持双向数据流模式(例如, 127.0.0.1:8080) - `log`:日志级别(debug, info, warn, error, event) -- `dns`:自定义DNS服务器(逗号分隔的IP地址,默认:1.1.1.1,8.8.8.8) +- `dns`:DNS缓存TTL持续时间(默认:5m,支持时间单位如`1h`、`30m`、`15s`等) - `min`:最小连接池容量(默认:64) - `mode`:客户端行为的运行模式控制 - `0`:自动检测(默认)- 首先尝试本地绑定,如果失败则回退到握手模式 diff --git a/nodepass/go.mod b/nodepass/go.mod index f944dc6566..4145e3b6ee 100644 --- a/nodepass/go.mod +++ b/nodepass/go.mod @@ -6,13 +6,12 @@ require ( github.com/NodePassProject/cert v1.0.1 github.com/NodePassProject/conn v1.0.16 github.com/NodePassProject/logs v1.0.3 - github.com/NodePassProject/name v1.0.0 github.com/NodePassProject/pool v1.0.49 - github.com/NodePassProject/quic v1.0.6 + github.com/NodePassProject/quic v1.0.8 ) require ( - github.com/quic-go/quic-go v0.56.0 // indirect + github.com/quic-go/quic-go v0.57.0 // indirect golang.org/x/crypto v0.45.0 // indirect golang.org/x/net v0.47.0 // indirect golang.org/x/sys v0.38.0 // indirect diff --git a/nodepass/go.sum b/nodepass/go.sum index 408f67f2ae..b2aad8acc9 100644 --- a/nodepass/go.sum +++ b/nodepass/go.sum @@ -4,20 +4,18 @@ github.com/NodePassProject/conn v1.0.16 h1:ojHfyBveZMcyOikdUs1SOW4yKp92NOBnNhfNe github.com/NodePassProject/conn v1.0.16/go.mod h1:xfQ7ZLUxrtdLsljGHYYCToW+Hdg6DAbmL1Cs94n5h6E= github.com/NodePassProject/logs v1.0.3 h1:CDUZVQ477vmmFQHazrQCWM0gJPNINm0C2N3FzC4jVyw= github.com/NodePassProject/logs v1.0.3/go.mod h1:TwtPXOzLtb8iH+fdduQjEEywICXivsM39cy9AinMSks= -github.com/NodePassProject/name v1.0.0 h1:PBHKMLVzNEDSh6ldlhvdh2ZKj0L22mEoPDGX09zTuv0= -github.com/NodePassProject/name v1.0.0/go.mod h1:tKCe5F/irxW6NpdHK3kpKSJGhAdu5EB1ed1bIdS3u3A= github.com/NodePassProject/pool v1.0.49 h1:gktVmE+GsQ0/C0MF8qgRraR7eS3na4k0QrQfR6o4fkM= github.com/NodePassProject/pool v1.0.49/go.mod h1:joQFk1oocg56QpJ1QK/2g5Jv/AyqYUQgPXMG1gWe8iA= -github.com/NodePassProject/quic v1.0.6 h1:LzfWsflDbEel8UfVOg5RT9qzp+msqtk/SdujBOS2PQE= -github.com/NodePassProject/quic v1.0.6/go.mod h1:EJgX8fdugc7w3ISbJ0abIuy1mFMr3uO4XSxci6QB8jg= +github.com/NodePassProject/quic v1.0.8 h1:S3kmxInIk6UiN0DcbRqSSoOMOY+JTUdTeeRuAr1SNxs= +github.com/NodePassProject/quic v1.0.8/go.mod h1:4t2Y+iSKCvtuA0bpG2YgU03f9crkTUpM4LzUK4RG6XY= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/quic-go/quic-go v0.56.0 h1:q/TW+OLismmXAehgFLczhCDTYB3bFmua4D9lsNBWxvY= -github.com/quic-go/quic-go v0.56.0/go.mod h1:9gx5KsFQtw2oZ6GZTyh+7YEvOxWCL9WZAepnHxgAo6c= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/quic-go/quic-go v0.57.0 h1:AsSSrrMs4qI/hLrKlTH/TGQeTMY0ib1pAOX7vA3AdqE= +github.com/quic-go/quic-go v0.57.0/go.mod h1:ly4QBAjHA2VhdnxhojRsCUOeJwKYg+taDlos92xb1+s= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko= go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o= golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= diff --git a/nodepass/internal/client.go b/nodepass/internal/client.go index 5789f8545f..43f495a65e 100644 --- a/nodepass/internal/client.go +++ b/nodepass/internal/client.go @@ -32,6 +32,7 @@ type Client struct { func NewClient(parsedURL *url.URL, logger *logs.Logger) (*Client, error) { client := &Client{ Common: Common{ + parsedURL: parsedURL, logger: logger, signalChan: make(chan string, semaphoreLimit), tcpBufferPool: &sync.Pool{ @@ -52,7 +53,7 @@ func NewClient(parsedURL *url.URL, logger *logs.Logger) (*Client, error) { }, tunnelName: parsedURL.Hostname(), } - if err := client.initConfig(parsedURL); err != nil { + if err := client.initConfig(); err != nil { return nil, fmt.Errorf("newClient: initConfig failed: %w", err) } client.initRateLimiter() @@ -63,7 +64,7 @@ func NewClient(parsedURL *url.URL, logger *logs.Logger) (*Client, error) { func (c *Client) Run() { logInfo := func(prefix string) { c.logger.Info("%v: client://%v@%v/%v?dns=%v&min=%v&mode=%v&quic=%v&dial=%v&read=%v&rate=%v&slot=%v&proxy=%v¬cp=%v&noudp=%v", - prefix, c.tunnelKey, c.tunnelTCPAddr, c.getTargetAddrsString(), strings.Join(c.dnsIPs, ","), c.minPoolCapacity, + prefix, c.tunnelKey, c.tunnelTCPAddr, c.getTargetAddrsString(), c.dnsCacheTTL, c.minPoolCapacity, c.runMode, c.quicMode, c.dialerIP, c.readTimeout, c.rateLimit/125000, c.slotLimit, c.proxyProtocol, c.disableTCP, c.disableUDP) } @@ -156,7 +157,11 @@ func (c *Client) commonStart() error { c.tlsCode, c.tunnelName, func() (net.Conn, error) { - return net.DialTimeout("tcp", c.tunnelTCPAddr.String(), tcpDialTimeout) + tcpAddr, err := c.getTunnelTCPAddr() + if err != nil { + return nil, err + } + return net.DialTimeout("tcp", tcpAddr.String(), tcpDialTimeout) }) go tcpPool.ClientManager() c.tunnelPool = tcpPool @@ -169,7 +174,13 @@ func (c *Client) commonStart() error { reportInterval, c.tlsCode, c.tunnelName, - c.tunnelUDPAddr.String()) + func() (string, error) { + udpAddr, err := c.getTunnelUDPAddr() + if err != nil { + return "", err + } + return udpAddr.String(), nil + }) go udpPool.ClientManager() c.tunnelPool = udpPool default: @@ -194,7 +205,11 @@ func (c *Client) commonStart() error { // tunnelHandshake 与隧道服务端进行握手 func (c *Client) tunnelHandshake() error { // 建立隧道TCP连接 - tunnelTCPConn, err := net.DialTimeout("tcp", c.tunnelTCPAddr.String(), tcpDialTimeout) + tunnelTCPAddr, err := c.getTunnelTCPAddr() + if err != nil { + return fmt.Errorf("tunnelHandshake: getTunnelTCPAddr failed: %w", err) + } + tunnelTCPConn, err := net.DialTimeout("tcp", tunnelTCPAddr.String(), tcpDialTimeout) if err != nil { return fmt.Errorf("tunnelHandshake: dialTimeout failed: %w", err) } diff --git a/nodepass/internal/common.go b/nodepass/internal/common.go index 7029e3bb25..f18f5e1561 100644 --- a/nodepass/internal/common.go +++ b/nodepass/internal/common.go @@ -23,15 +23,15 @@ import ( "github.com/NodePassProject/conn" "github.com/NodePassProject/logs" - "github.com/NodePassProject/name" ) // Common 包含所有模式共享的核心功能 type Common struct { mu sync.Mutex // 互斥锁 + parsedURL *url.URL // 解析后的URL logger *logs.Logger // 日志记录器 - resolver *name.Resolver // 域名解析器 - dnsIPs []string // DNS服务器组 + dnsCacheTTL time.Duration // DNS缓存TTL + dnsCacheEntries sync.Map // DNS缓存条目 tlsCode string // TLS模式代码 tlsConfig *tls.Config // TLS配置 coreType string // 核心类型 @@ -41,8 +41,10 @@ type Common struct { dialerIP string // 拨号本地IP dialerFallback uint32 // 拨号回落标志 tunnelKey string // 隧道密钥 + tunnelAddr string // 原始隧道地址 tunnelTCPAddr *net.TCPAddr // 隧道TCP地址 tunnelUDPAddr *net.UDPAddr // 隧道UDP地址 + targetAddrs []string // 原始目标地址组 targetTCPAddrs []*net.TCPAddr // 目标TCP地址组 targetUDPAddrs []*net.UDPAddr // 目标UDP地址组 targetIdx uint64 // 目标地址索引 @@ -80,6 +82,13 @@ type Common struct { cancel context.CancelFunc // 取消函数 } +// dnsCacheEntry DNS缓存条目 +type dnsCacheEntry struct { + tcpAddr *net.TCPAddr + udpAddr *net.UDPAddr + expiredAt time.Time +} + // TransportPool 统一连接池接口 type TransportPool interface { IncomingGet(timeout time.Duration) (string, net.Conn, error) @@ -100,7 +109,6 @@ var ( semaphoreLimit = getEnvAsInt("NP_SEMAPHORE_LIMIT", 65536) // 信号量限制 tcpDataBufSize = getEnvAsInt("NP_TCP_DATA_BUF_SIZE", 16384) // TCP缓冲区大小 udpDataBufSize = getEnvAsInt("NP_UDP_DATA_BUF_SIZE", 16384) // UDP缓冲区大小 - dnsCachingTTL = getEnvAsDuration("NP_DNS_CACHING_TTL", 5*time.Minute) // DNS缓存TTL handshakeTimeout = getEnvAsDuration("NP_HANDSHAKE_TIMEOUT", 5*time.Second) // 握手超时 tcpDialTimeout = getEnvAsDuration("NP_TCP_DIAL_TIMEOUT", 5*time.Second) // TCP拨号超时 udpDialTimeout = getEnvAsDuration("NP_UDP_DIAL_TIMEOUT", 5*time.Second) // UDP拨号超时 @@ -116,18 +124,18 @@ var ( // 默认配置 const ( - defaultDNSIPs = "1.1.1.1,8.8.8.8" // 默认DNS服务器 - defaultMinPool = 64 // 默认最小池容量 - defaultMaxPool = 1024 // 默认最大池容量 - defaultRunMode = "0" // 默认运行模式 - defaultQuicMode = "0" // 默认QUIC模式 - defaultDialerIP = "auto" // 默认拨号本地IP - defaultReadTimeout = 0 * time.Second // 默认读取超时 - defaultRateLimit = 0 // 默认速率限制 - defaultSlotLimit = 65536 // 默认槽位限制 - defaultProxyProtocol = "0" // 默认代理协议 - defaultTCPStrategy = "0" // 默认TCP策略 - defaultUDPStrategy = "0" // 默认UDP策略 + defaultDNSTTL = 5 * time.Minute // 默认DNS缓存TTL + defaultMinPool = 64 // 默认最小池容量 + defaultMaxPool = 1024 // 默认最大池容量 + defaultRunMode = "0" // 默认运行模式 + defaultQuicMode = "0" // 默认QUIC模式 + defaultDialerIP = "auto" // 默认拨号本地IP + defaultReadTimeout = 0 * time.Second // 默认读取超时 + defaultRateLimit = 0 // 默认速率限制 + defaultSlotLimit = 65536 // 默认槽位限制 + defaultProxyProtocol = "0" // 默认代理协议 + defaultTCPStrategy = "0" // 默认TCP策略 + defaultUDPStrategy = "0" // 默认UDP策略 ) // getTCPBuffer 获取TCP缓冲区 @@ -250,6 +258,56 @@ func (c *Common) decode(data []byte) ([]byte, error) { return c.xor(decoded), nil } +// resolve 解析地址并缓存 +func (c *Common) resolve(network, address string) (any, error) { + now := time.Now() + + // 快速路径:检查缓存 + if val, ok := c.dnsCacheEntries.Load(address); ok { + entry := val.(*dnsCacheEntry) + if now.Before(entry.expiredAt) { + if network == "tcp" { + return entry.tcpAddr, nil + } + return entry.udpAddr, nil + } + // 删除过期缓存 + c.dnsCacheEntries.Delete(address) + } + + // 慢速路径:系统解析 + tcpAddr, err := net.ResolveTCPAddr("tcp", address) + if err != nil { + return nil, fmt.Errorf("resolve: resolveTCPAddr failed: %w", err) + } + + udpAddr, err := net.ResolveUDPAddr("udp", address) + if err != nil { + return nil, fmt.Errorf("resolve: resolveUDPAddr failed: %w", err) + } + + // 存储新的缓存 + entry := &dnsCacheEntry{ + tcpAddr: tcpAddr, + udpAddr: udpAddr, + expiredAt: now.Add(c.dnsCacheTTL), + } + c.dnsCacheEntries.LoadOrStore(address, entry) + + if network == "tcp" { + return tcpAddr, nil + } + return udpAddr, nil +} + +// clearCache 清空DNS缓存 +func (c *Common) clearCache() { + c.dnsCacheEntries.Range(func(key, value any) bool { + c.dnsCacheEntries.Delete(key) + return true + }) +} + // resolveAddr 解析单个地址 func (c *Common) resolveAddr(network, address string) (any, error) { host, _, err := net.SplitHostPort(address) @@ -257,96 +315,48 @@ func (c *Common) resolveAddr(network, address string) (any, error) { return nil, fmt.Errorf("invalid address %s: %w", address, err) } - if host == "" { + if host == "" || net.ParseIP(host) != nil { if network == "tcp" { return net.ResolveTCPAddr("tcp", address) } return net.ResolveUDPAddr("udp", address) } - if network == "tcp" { - return c.resolver.ResolveTCPAddr("tcp", address) - } - return c.resolver.ResolveUDPAddr("udp", address) + return c.resolve(network, address) } -// getAddress 解析和设置地址信息 -func (c *Common) getAddress(parsedURL *url.URL) error { - // 解析隧道地址 - tunnelAddr := parsedURL.Host - if tunnelAddr == "" { - return fmt.Errorf("getAddress: no valid tunnel address found") +// resolveTarget 动态解析目标地址 +func (c *Common) resolveTarget(network string, idx int) (any, error) { + if idx < 0 || idx >= len(c.targetAddrs) { + return nil, fmt.Errorf("resolveTarget: index %d out of range", idx) } - // 解析隧道TCP地址 - tcpAddr, err := c.resolveAddr("tcp", tunnelAddr) + addr, err := c.resolveAddr(network, c.targetAddrs[idx]) if err != nil { - return fmt.Errorf("getAddress: resolveTCPAddr failed: %w", err) - } - c.tunnelTCPAddr = tcpAddr.(*net.TCPAddr) - - // 解析隧道UDP地址 - udpAddr, err := c.resolveAddr("udp", tunnelAddr) - if err != nil { - return fmt.Errorf("getAddress: resolveUDPAddr failed: %w", err) - } - c.tunnelUDPAddr = udpAddr.(*net.UDPAddr) - - // 处理目标地址组 - targetAddr := strings.TrimPrefix(parsedURL.Path, "/") - if targetAddr == "" { - return fmt.Errorf("getAddress: no valid target address found") - } - - addrList := strings.Split(targetAddr, ",") - tempTCPAddrs := make([]*net.TCPAddr, 0, len(addrList)) - tempUDPAddrs := make([]*net.UDPAddr, 0, len(addrList)) - - for _, addr := range addrList { - addr = strings.TrimSpace(addr) - if addr == "" { - continue + if network == "tcp" { + return c.targetTCPAddrs[idx], err } - - // 解析目标TCP地址 - tcpAddr, err := c.resolveAddr("tcp", addr) - if err != nil { - return fmt.Errorf("getAddress: resolveTCPAddr failed for %s: %w", addr, err) - } - - // 解析目标UDP地址 - udpAddr, err := c.resolveAddr("udp", addr) - if err != nil { - return fmt.Errorf("getAddress: resolveUDPAddr failed for %s: %w", addr, err) - } - - tempTCPAddrs = append(tempTCPAddrs, tcpAddr.(*net.TCPAddr)) - tempUDPAddrs = append(tempUDPAddrs, udpAddr.(*net.UDPAddr)) + return c.targetUDPAddrs[idx], err } - - if len(tempTCPAddrs) == 0 || len(tempUDPAddrs) == 0 || len(tempTCPAddrs) != len(tempUDPAddrs) { - return fmt.Errorf("getAddress: no valid target address found") - } - - // 设置目标地址组 - c.targetTCPAddrs = tempTCPAddrs - c.targetUDPAddrs = tempUDPAddrs - c.targetIdx = 0 - - // 无限循环检查 - tunnelPort := c.tunnelTCPAddr.Port - for _, targetAddr := range c.targetTCPAddrs { - if targetAddr.Port == tunnelPort && (targetAddr.IP.IsLoopback() || c.tunnelTCPAddr.IP.IsUnspecified()) { - return fmt.Errorf("getAddress: tunnel port %d conflicts with target address %s", tunnelPort, targetAddr.String()) - } - } - - return nil + return addr, nil } -// getCoreType 获取核心类型 -func (c *Common) getCoreType(parsedURL *url.URL) { - c.coreType = parsedURL.Scheme +// getTunnelTCPAddr 动态解析隧道TCP地址 +func (c *Common) getTunnelTCPAddr() (*net.TCPAddr, error) { + addr, err := c.resolveAddr("tcp", c.tunnelAddr) + if err != nil { + return c.tunnelTCPAddr, err + } + return addr.(*net.TCPAddr), nil +} + +// getTunnelUDPAddr 动态解析隧道UDP地址 +func (c *Common) getTunnelUDPAddr() (*net.UDPAddr, error) { + addr, err := c.resolveAddr("udp", c.tunnelAddr) + if err != nil { + return c.tunnelUDPAddr, err + } + return addr.(*net.UDPAddr), nil } // getTargetAddrsString 获取目标地址组的字符串表示 @@ -368,15 +378,17 @@ func (c *Common) nextTargetIdx() int { // dialWithRotation 轮询拨号到目标地址组 func (c *Common) dialWithRotation(network string, timeout time.Duration) (net.Conn, error) { - var addrCount int - var getAddr func(int) string + addrCount := len(c.targetAddrs) - if network == "tcp" { - addrCount = len(c.targetTCPAddrs) - getAddr = func(i int) string { return c.targetTCPAddrs[i].String() } - } else { - addrCount = len(c.targetUDPAddrs) - getAddr = func(i int) string { return c.targetUDPAddrs[i].String() } + getAddr := func(i int) string { + addr, _ := c.resolveTarget(network, i) + if tcpAddr, ok := addr.(*net.TCPAddr); ok { + return tcpAddr.String() + } + if udpAddr, ok := addr.(*net.UDPAddr); ok { + return udpAddr.String() + } + return "" } // 配置拨号器 @@ -402,14 +414,21 @@ func (c *Common) dialWithRotation(network string, timeout time.Duration) (net.Co // 单目标地址:快速路径 if addrCount == 1 { - return tryDial(getAddr(0)) + if addr := getAddr(0); addr != "" { + return tryDial(addr) + } + return nil, fmt.Errorf("dialWithRotation: invalid target address") } // 多目标地址:负载均衡 + 故障转移 startIdx := c.nextTargetIdx() var lastErr error for i := range addrCount { - conn, err := tryDial(getAddr((startIdx + i) % addrCount)) + addr := getAddr((startIdx + i) % addrCount) + if addr == "" { + continue + } + conn, err := tryDial(addr) if err == nil { return conn, nil } @@ -419,42 +438,116 @@ func (c *Common) dialWithRotation(network string, timeout time.Duration) (net.Co return nil, fmt.Errorf("dialWithRotation: all %d targets failed: %w", addrCount, lastErr) } +// getAddress 解析和设置地址信息 +func (c *Common) getAddress() error { + // 解析隧道地址 + tunnelAddr := c.parsedURL.Host + if tunnelAddr == "" { + return fmt.Errorf("getAddress: no valid tunnel address found") + } + + // 保存原始隧道地址 + c.tunnelAddr = tunnelAddr + + // 解析隧道TCP地址 + tcpAddr, err := c.resolveAddr("tcp", tunnelAddr) + if err != nil { + return fmt.Errorf("getAddress: resolveTCPAddr failed: %w", err) + } + c.tunnelTCPAddr = tcpAddr.(*net.TCPAddr) + + // 解析隧道UDP地址 + udpAddr, err := c.resolveAddr("udp", tunnelAddr) + if err != nil { + return fmt.Errorf("getAddress: resolveUDPAddr failed: %w", err) + } + c.tunnelUDPAddr = udpAddr.(*net.UDPAddr) + + // 处理目标地址组 + targetAddr := strings.TrimPrefix(c.parsedURL.Path, "/") + if targetAddr == "" { + return fmt.Errorf("getAddress: no valid target address found") + } + + addrList := strings.Split(targetAddr, ",") + tempTCPAddrs := make([]*net.TCPAddr, 0, len(addrList)) + tempUDPAddrs := make([]*net.UDPAddr, 0, len(addrList)) + tempRawAddrs := make([]string, 0, len(addrList)) + + for _, addr := range addrList { + addr = strings.TrimSpace(addr) + if addr == "" { + continue + } + + // 解析目标TCP地址 + tcpAddr, err := c.resolveAddr("tcp", addr) + if err != nil { + return fmt.Errorf("getAddress: resolveTCPAddr failed for %s: %w", addr, err) + } + + // 解析目标UDP地址 + udpAddr, err := c.resolveAddr("udp", addr) + if err != nil { + return fmt.Errorf("getAddress: resolveUDPAddr failed for %s: %w", addr, err) + } + + tempTCPAddrs = append(tempTCPAddrs, tcpAddr.(*net.TCPAddr)) + tempUDPAddrs = append(tempUDPAddrs, udpAddr.(*net.UDPAddr)) + tempRawAddrs = append(tempRawAddrs, addr) + } + + if len(tempTCPAddrs) == 0 || len(tempUDPAddrs) == 0 || len(tempTCPAddrs) != len(tempUDPAddrs) { + return fmt.Errorf("getAddress: no valid target address found") + } + + // 设置目标地址组 + c.targetAddrs = tempRawAddrs + c.targetTCPAddrs = tempTCPAddrs + c.targetUDPAddrs = tempUDPAddrs + c.targetIdx = 0 + + // 无限循环检查 + tunnelPort := c.tunnelTCPAddr.Port + for _, targetAddr := range c.targetTCPAddrs { + if targetAddr.Port == tunnelPort && (targetAddr.IP.IsLoopback() || c.tunnelTCPAddr.IP.IsUnspecified()) { + return fmt.Errorf("getAddress: tunnel port %d conflicts with target address %s", tunnelPort, targetAddr.String()) + } + } + + return nil +} + +// getCoreType 获取核心类型 +func (c *Common) getCoreType() { + c.coreType = c.parsedURL.Scheme +} + // getTunnelKey 从URL中获取隧道密钥 -func (c *Common) getTunnelKey(parsedURL *url.URL) { - if key := parsedURL.User.Username(); key != "" { +func (c *Common) getTunnelKey() { + if key := c.parsedURL.User.Username(); key != "" { c.tunnelKey = key } else { hash := fnv.New32a() - hash.Write([]byte(parsedURL.Port())) + hash.Write([]byte(c.parsedURL.Port())) c.tunnelKey = hex.EncodeToString(hash.Sum(nil)) } } -// getDNSIPs 获取DNS服务器组 -func (c *Common) getDNSIPs(parsedURL *url.URL) { - if dns := parsedURL.Query().Get("dns"); dns != "" { - ips := strings.SplitSeq(dns, ",") - for ipStr := range ips { - ipStr = strings.TrimSpace(ipStr) - if ipStr == "" { - continue - } - if ip := net.ParseIP(ipStr); ip != nil { - c.dnsIPs = append(c.dnsIPs, ip.String()) - } else { - c.logger.Warn("getDNSIPs: invalid IP address: %v", ipStr) - } +// getDNSTTL 获取DNS缓存TTL +func (c *Common) getDNSTTL() { + if dns := c.parsedURL.Query().Get("dns"); dns != "" { + if ttl, err := time.ParseDuration(dns); err == nil && ttl > 0 { + c.dnsCacheTTL = ttl } } else { - for ipStr := range strings.SplitSeq(defaultDNSIPs, ",") { - c.dnsIPs = append(c.dnsIPs, strings.TrimSpace(ipStr)) - } + c.dnsCacheTTL = defaultDNSTTL } } // getPoolCapacity 获取连接池容量设置 -func (c *Common) getPoolCapacity(parsedURL *url.URL) { - if min := parsedURL.Query().Get("min"); min != "" { +func (c *Common) getPoolCapacity() { + if min := c.parsedURL.Query().Get("min"); min != "" { if value, err := strconv.Atoi(min); err == nil && value > 0 { c.minPoolCapacity = value } @@ -462,7 +555,7 @@ func (c *Common) getPoolCapacity(parsedURL *url.URL) { c.minPoolCapacity = defaultMinPool } - if max := parsedURL.Query().Get("max"); max != "" { + if max := c.parsedURL.Query().Get("max"); max != "" { if value, err := strconv.Atoi(max); err == nil && value > 0 { c.maxPoolCapacity = value } @@ -472,8 +565,8 @@ func (c *Common) getPoolCapacity(parsedURL *url.URL) { } // getRunMode 获取运行模式 -func (c *Common) getRunMode(parsedURL *url.URL) { - if mode := parsedURL.Query().Get("mode"); mode != "" { +func (c *Common) getRunMode() { + if mode := c.parsedURL.Query().Get("mode"); mode != "" { c.runMode = mode } else { c.runMode = defaultRunMode @@ -481,8 +574,8 @@ func (c *Common) getRunMode(parsedURL *url.URL) { } // getQuicMode 获取QUIC模式 -func (c *Common) getQuicMode(parsedURL *url.URL) { - if quicMode := parsedURL.Query().Get("quic"); quicMode != "" { +func (c *Common) getQuicMode() { + if quicMode := c.parsedURL.Query().Get("quic"); quicMode != "" { c.quicMode = quicMode } else { c.quicMode = defaultQuicMode @@ -493,8 +586,8 @@ func (c *Common) getQuicMode(parsedURL *url.URL) { } // getDialerIP 获取拨号本地IP设置 -func (c *Common) getDialerIP(parsedURL *url.URL) { - if dialerIP := parsedURL.Query().Get("dial"); dialerIP != "" && dialerIP != "auto" { +func (c *Common) getDialerIP() { + if dialerIP := c.parsedURL.Query().Get("dial"); dialerIP != "" && dialerIP != "auto" { if ip := net.ParseIP(dialerIP); ip != nil { c.dialerIP = dialerIP return @@ -506,8 +599,8 @@ func (c *Common) getDialerIP(parsedURL *url.URL) { } // getReadTimeout 获取读取超时设置 -func (c *Common) getReadTimeout(parsedURL *url.URL) { - if timeout := parsedURL.Query().Get("read"); timeout != "" { +func (c *Common) getReadTimeout() { + if timeout := c.parsedURL.Query().Get("read"); timeout != "" { if value, err := time.ParseDuration(timeout); err == nil && value > 0 { c.readTimeout = value } @@ -517,8 +610,8 @@ func (c *Common) getReadTimeout(parsedURL *url.URL) { } // getRateLimit 获取速率限制 -func (c *Common) getRateLimit(parsedURL *url.URL) { - if limit := parsedURL.Query().Get("rate"); limit != "" { +func (c *Common) getRateLimit() { + if limit := c.parsedURL.Query().Get("rate"); limit != "" { if value, err := strconv.Atoi(limit); err == nil && value > 0 { c.rateLimit = value * 125000 } @@ -528,8 +621,8 @@ func (c *Common) getRateLimit(parsedURL *url.URL) { } // getSlotLimit 获取连接槽位限制 -func (c *Common) getSlotLimit(parsedURL *url.URL) { - if slot := parsedURL.Query().Get("slot"); slot != "" { +func (c *Common) getSlotLimit() { + if slot := c.parsedURL.Query().Get("slot"); slot != "" { if value, err := strconv.Atoi(slot); err == nil && value > 0 { c.slotLimit = int32(value) } @@ -539,8 +632,8 @@ func (c *Common) getSlotLimit(parsedURL *url.URL) { } // getProxyProtocol 获取代理协议设置 -func (c *Common) getProxyProtocol(parsedURL *url.URL) { - if protocol := parsedURL.Query().Get("proxy"); protocol != "" { +func (c *Common) getProxyProtocol() { + if protocol := c.parsedURL.Query().Get("proxy"); protocol != "" { c.proxyProtocol = protocol } else { c.proxyProtocol = defaultProxyProtocol @@ -548,8 +641,8 @@ func (c *Common) getProxyProtocol(parsedURL *url.URL) { } // getTCPStrategy 获取TCP策略 -func (c *Common) getTCPStrategy(parsedURL *url.URL) { - if tcpStrategy := parsedURL.Query().Get("notcp"); tcpStrategy != "" { +func (c *Common) getTCPStrategy() { + if tcpStrategy := c.parsedURL.Query().Get("notcp"); tcpStrategy != "" { c.disableTCP = tcpStrategy } else { c.disableTCP = defaultTCPStrategy @@ -557,8 +650,8 @@ func (c *Common) getTCPStrategy(parsedURL *url.URL) { } // getUDPStrategy 获取UDP策略 -func (c *Common) getUDPStrategy(parsedURL *url.URL) { - if udpStrategy := parsedURL.Query().Get("noudp"); udpStrategy != "" { +func (c *Common) getUDPStrategy() { + if udpStrategy := c.parsedURL.Query().Get("noudp"); udpStrategy != "" { c.disableUDP = udpStrategy } else { c.disableUDP = defaultUDPStrategy @@ -566,26 +659,24 @@ func (c *Common) getUDPStrategy(parsedURL *url.URL) { } // initConfig 初始化配置 -func (c *Common) initConfig(parsedURL *url.URL) error { - c.getDNSIPs(parsedURL) - c.resolver = name.NewResolver(dnsCachingTTL, c.dnsIPs) - - if err := c.getAddress(parsedURL); err != nil { +func (c *Common) initConfig() error { + if err := c.getAddress(); err != nil { return err } - c.getCoreType(parsedURL) - c.getTunnelKey(parsedURL) - c.getPoolCapacity(parsedURL) - c.getRunMode(parsedURL) - c.getQuicMode(parsedURL) - c.getDialerIP(parsedURL) - c.getReadTimeout(parsedURL) - c.getRateLimit(parsedURL) - c.getSlotLimit(parsedURL) - c.getProxyProtocol(parsedURL) - c.getTCPStrategy(parsedURL) - c.getUDPStrategy(parsedURL) + c.getCoreType() + c.getDNSTTL() + c.getTunnelKey() + c.getPoolCapacity() + c.getRunMode() + c.getQuicMode() + c.getDialerIP() + c.getReadTimeout() + c.getRateLimit() + c.getSlotLimit() + c.getProxyProtocol() + c.getTCPStrategy() + c.getUDPStrategy() return nil } @@ -671,7 +762,7 @@ func (c *Common) initTunnelListener() error { // initTargetListener 初始化目标监听器 func (c *Common) initTargetListener() error { - if len(c.targetTCPAddrs) == 0 && len(c.targetUDPAddrs) == 0 { + if len(c.targetAddrs) == 0 { return fmt.Errorf("initTargetListener: no target address") } @@ -769,9 +860,7 @@ func (c *Common) stop() { } // 清空DNS缓存 - if c.resolver != nil { - c.resolver.ClearCache() - } + c.clearCache() } // shutdown 共用优雅关闭 @@ -1067,8 +1156,8 @@ func (c *Common) commonTCPLoop() { }() // 交换数据 - c.logger.Debug("Starting exchange: %v <-> %v", remoteConn.LocalAddr(), targetConn.LocalAddr()) - c.logger.Debug("Exchange complete: %v", conn.DataExchange(remoteConn, targetConn, c.readTimeout, buffer1, buffer2)) + c.logger.Info("Starting exchange: %v <-> %v", targetConn.RemoteAddr(), remoteConn.RemoteAddr()) + c.logger.Info("Exchange complete: %v", conn.DataExchange(targetConn, remoteConn, c.readTimeout, buffer1, buffer2)) }(targetConn) } } @@ -1406,8 +1495,8 @@ func (c *Common) commonTCPOnce(signalURL *url.URL) { }() // 交换数据 - c.logger.Debug("Starting exchange: %v <-> %v", remoteConn.LocalAddr(), targetConn.LocalAddr()) - c.logger.Debug("Exchange complete: %v", conn.DataExchange(remoteConn, targetConn, c.readTimeout, buffer1, buffer2)) + c.logger.Info("Starting exchange: %v <-> %v", remoteConn.RemoteAddr(), targetConn.RemoteAddr()) + c.logger.Info("Exchange complete: %v", conn.DataExchange(remoteConn, targetConn, c.readTimeout, buffer1, buffer2)) } // commonUDPOnce 共用处理单个UDP请求 @@ -1587,10 +1676,15 @@ func (c *Common) singleEventLoop() error { ping := 0 now := time.Now() - // 尝试连接到目标地址 - if conn, err := net.DialTimeout("tcp", c.targetTCPAddrs[c.nextTargetIdx()].String(), reportInterval); err == nil { - ping = int(time.Since(now).Milliseconds()) - conn.Close() + // 尝试连接到目标地址检测延迟 + idx := c.nextTargetIdx() + if addr, _ := c.resolveTarget("tcp", idx); addr != nil { + if tcpAddr, ok := addr.(*net.TCPAddr); ok { + if conn, err := net.DialTimeout("tcp", tcpAddr.String(), reportInterval); err == nil { + ping = int(time.Since(now).Milliseconds()) + conn.Close() + } + } } // 发送检查点事件 @@ -1675,8 +1769,8 @@ func (c *Common) singleTCPLoop() error { }() // 交换数据 - c.logger.Debug("Starting exchange: %v <-> %v", tunnelConn.LocalAddr(), targetConn.LocalAddr()) - c.logger.Debug("Exchange complete: %v", conn.DataExchange(tunnelConn, targetConn, c.readTimeout, buffer1, buffer2)) + c.logger.Info("Starting exchange: %v <-> %v", tunnelConn.RemoteAddr(), targetConn.RemoteAddr()) + c.logger.Info("Exchange complete: %v", conn.DataExchange(tunnelConn, targetConn, c.readTimeout, buffer1, buffer2)) }(tunnelConn) } diff --git a/nodepass/internal/master.go b/nodepass/internal/master.go index 14836ba318..b8769e5f8e 100644 --- a/nodepass/internal/master.go +++ b/nodepass/internal/master.go @@ -1785,9 +1785,9 @@ func (m *Master) generateConfigURL(instance *Instance) string { // 根据实例类型设置默认参数 switch instance.Type { case "client": - // client参数: dns, min, mode, quic, dial, read, rate, slot, proxy, notcp, noudp + // client参数: dns, min, mode, dial, read, rate, slot, proxy, notcp, noudp if query.Get("dns") == "" { - query.Set("dns", defaultDNSIPs) + query.Set("dns", defaultDNSTTL.String()) } if query.Get("min") == "" { query.Set("min", strconv.Itoa(defaultMinPool)) @@ -1795,9 +1795,6 @@ func (m *Master) generateConfigURL(instance *Instance) string { if query.Get("mode") == "" { query.Set("mode", defaultRunMode) } - if query.Get("quic") == "" { - query.Set("quic", defaultQuicMode) - } if query.Get("dial") == "" { query.Set("dial", defaultDialerIP) } @@ -1822,7 +1819,7 @@ func (m *Master) generateConfigURL(instance *Instance) string { case "server": // server参数: dns, max, mode, quic, dial, read, rate, slot, proxy, notcp, noudp if query.Get("dns") == "" { - query.Set("dns", defaultDNSIPs) + query.Set("dns", defaultDNSTTL.String()) } if query.Get("max") == "" { query.Set("max", strconv.Itoa(defaultMaxPool)) diff --git a/nodepass/internal/server.go b/nodepass/internal/server.go index d14168f808..b38a663198 100644 --- a/nodepass/internal/server.go +++ b/nodepass/internal/server.go @@ -12,7 +12,6 @@ import ( "os" "os/signal" "strconv" - "strings" "sync" "syscall" "time" @@ -33,6 +32,7 @@ type Server struct { func NewServer(parsedURL *url.URL, tlsCode string, tlsConfig *tls.Config, logger *logs.Logger) (*Server, error) { server := &Server{ Common: Common{ + parsedURL: parsedURL, tlsCode: tlsCode, tlsConfig: tlsConfig, logger: logger, @@ -54,7 +54,7 @@ func NewServer(parsedURL *url.URL, tlsCode string, tlsConfig *tls.Config, logger pongURL: &url.URL{Scheme: "np", Fragment: "o"}, }, } - if err := server.initConfig(parsedURL); err != nil { + if err := server.initConfig(); err != nil { return nil, fmt.Errorf("newServer: initConfig failed: %w", err) } server.initRateLimiter() @@ -65,7 +65,7 @@ func NewServer(parsedURL *url.URL, tlsCode string, tlsConfig *tls.Config, logger func (s *Server) Run() { logInfo := func(prefix string) { s.logger.Info("%v: server://%v@%v/%v?dns=%v&max=%v&mode=%v&quic=%v&dial=%v&read=%v&rate=%v&slot=%v&proxy=%v¬cp=%v&noudp=%v", - prefix, s.tunnelKey, s.tunnelTCPAddr, s.getTargetAddrsString(), strings.Join(s.dnsIPs, ","), s.maxPoolCapacity, + prefix, s.tunnelKey, s.tunnelTCPAddr, s.getTargetAddrsString(), s.dnsCacheTTL, s.maxPoolCapacity, s.runMode, s.quicMode, s.dialerIP, s.readTimeout, s.rateLimit/125000, s.slotLimit, s.proxyProtocol, s.disableTCP, s.disableUDP) } @@ -182,76 +182,121 @@ func (s *Server) start() error { // tunnelHandshake 与客户端进行握手 func (s *Server) tunnelHandshake() error { - // 接受隧道连接 - for s.ctx.Err() == nil { - tunnelTCPConn, err := s.tunnelListener.Accept() - if err != nil { - s.logger.Error("tunnelHandshake: accept error: %v", err) - select { - case <-s.ctx.Done(): - return fmt.Errorf("tunnelHandshake: context error: %w", s.ctx.Err()) - case <-time.After(serviceCooldown): - } - continue - } - - tunnelTCPConn.SetReadDeadline(time.Now().Add(handshakeTimeout)) - - bufReader := bufio.NewReader(tunnelTCPConn) - rawTunnelKey, err := bufReader.ReadBytes('\n') - if err != nil { - s.logger.Warn("tunnelHandshake: handshake timeout: %v", tunnelTCPConn.RemoteAddr()) - tunnelTCPConn.Close() - select { - case <-s.ctx.Done(): - return fmt.Errorf("tunnelHandshake: context error: %w", s.ctx.Err()) - case <-time.After(serviceCooldown): - } - continue - } - - tunnelTCPConn.SetReadDeadline(time.Time{}) - - // 解码隧道密钥 - tunnelKeyData, err := s.decode(rawTunnelKey) - if err != nil { - s.logger.Warn("tunnelHandshake: decode tunnel key failed: %v", tunnelTCPConn.RemoteAddr()) - tunnelTCPConn.Close() - select { - case <-s.ctx.Done(): - return fmt.Errorf("tunnelHandshake: context error: %w", s.ctx.Err()) - case <-time.After(serviceCooldown): - } - continue - } - tunnelKey := string(tunnelKeyData) - - if tunnelKey != s.tunnelKey { - s.logger.Warn("tunnelHandshake: access denied: %v", tunnelTCPConn.RemoteAddr()) - tunnelTCPConn.Close() - select { - case <-s.ctx.Done(): - return fmt.Errorf("tunnelHandshake: context error: %w", s.ctx.Err()) - case <-time.After(serviceCooldown): - } - continue - } - - s.tunnelTCPConn = tunnelTCPConn.(*net.TCPConn) - s.bufReader = bufio.NewReader(&conn.TimeoutReader{Conn: s.tunnelTCPConn, Timeout: 3 * reportInterval}) - s.tunnelTCPConn.SetKeepAlive(true) - s.tunnelTCPConn.SetKeepAlivePeriod(reportInterval) - - // 记录客户端IP - s.clientIP = s.tunnelTCPConn.RemoteAddr().(*net.TCPAddr).IP.String() - break + type handshakeResult struct { + conn *net.TCPConn + bufReader *bufio.Reader + clientIP string } - if s.ctx.Err() != nil { + successChan := make(chan handshakeResult, 1) + closeChan := make(chan struct{}) + var wg sync.WaitGroup + + go func() { + for { + select { + case <-closeChan: + return + default: + } + + // 接受隧道连接 + rawConn, err := s.tunnelListener.Accept() + if err != nil { + select { + case <-closeChan: + return + default: + continue + } + } + + // 并发处理握手 + wg.Add(1) + go func(rawConn net.Conn) { + defer wg.Done() + + select { + case <-closeChan: + rawConn.Close() + return + default: + } + + bufReader := bufio.NewReader(rawConn) + peek, err := bufReader.Peek(4) + if err == nil && len(peek) == 4 && peek[3] == ' ' { + clientIP := rawConn.RemoteAddr().(*net.TCPAddr).IP.String() + "\n" + if peek[0] == 'G' && peek[1] == 'E' && peek[2] == 'T' { + fmt.Fprintf(rawConn, "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: %d\r\nConnection: close\r\n\r\n%s", len(clientIP), clientIP) + } else { + fmt.Fprint(rawConn, "HTTP/1.1 405 Method Not Allowed\r\nAllow: GET\r\nContent-Length: 0\r\nConnection: close\r\n\r\n") + } + rawConn.Close() + return + } + + // 读取隧道密钥 + rawConn.SetReadDeadline(time.Now().Add(handshakeTimeout)) + rawTunnelKey, err := bufReader.ReadBytes('\n') + if err != nil { + s.logger.Warn("tunnelHandshake: read timeout: %v", rawConn.RemoteAddr()) + rawConn.Close() + return + } + rawConn.SetReadDeadline(time.Time{}) + + // 解码隧道密钥 + tunnelKeyData, err := s.decode(rawTunnelKey) + if err != nil { + s.logger.Warn("tunnelHandshake: decode failed: %v", rawConn.RemoteAddr()) + rawConn.Close() + return + } + + // 验证隧道密钥 + if string(tunnelKeyData) != s.tunnelKey { + s.logger.Warn("tunnelHandshake: access denied: %v", rawConn.RemoteAddr()) + rawConn.Close() + return + } + + tcpConn := rawConn.(*net.TCPConn) + tcpConn.SetKeepAlive(true) + tcpConn.SetKeepAlivePeriod(reportInterval) + + // 返回握手结果 + select { + case successChan <- handshakeResult{ + conn: tcpConn, + bufReader: bufio.NewReader(&conn.TimeoutReader{Conn: tcpConn, Timeout: 3 * reportInterval}), + clientIP: tcpConn.RemoteAddr().(*net.TCPAddr).IP.String(), + }: + close(closeChan) + case <-closeChan: + rawConn.Close() + } + }(rawConn) + } + }() + + // 阻塞等待握手结果 + var result handshakeResult + select { + case result = <-successChan: + wg.Wait() + case <-s.ctx.Done(): + close(closeChan) + wg.Wait() return fmt.Errorf("tunnelHandshake: context error: %w", s.ctx.Err()) } - // 发送客户端配置 + // 保存握手结果 + s.tunnelTCPConn = result.conn + s.bufReader = result.bufReader + s.clientIP = result.clientIP + + // 构建隧道配置信息 tunnelURL := &url.URL{ Scheme: "np", User: url.User(s.quicMode), @@ -260,6 +305,7 @@ func (s *Server) tunnelHandshake() error { Fragment: s.tlsCode, } + // 发送隧道配置信息 _, err := s.tunnelTCPConn.Write(s.encode([]byte(tunnelURL.String()))) if err != nil { return fmt.Errorf("tunnelHandshake: write tunnel config failed: %w", err) diff --git a/openwrt-packages/openlist2/Makefile b/openwrt-packages/openlist2/Makefile index d74dc3f99a..b7c308700d 100644 --- a/openwrt-packages/openlist2/Makefile +++ b/openwrt-packages/openlist2/Makefile @@ -7,13 +7,13 @@ include $(TOPDIR)/rules.mk PKG_NAME:=openlist2 -PKG_VERSION:=4.1.7 -PKG_WEB_VERSION:=4.1.7 +PKG_VERSION:=4.1.8 +PKG_WEB_VERSION:=4.1.8 PKG_RELEASE:=1 PKG_SOURCE:=openlist-$(PKG_VERSION).tar.gz PKG_SOURCE_URL:=https://codeload.github.com/OpenListTeam/OpenList/tar.gz/v$(PKG_VERSION)? -PKG_HASH:=f1b92628be09ba181decc46423c3e0624b78aedfcd28590990a46ba03d75e5e4 +PKG_HASH:=8ba861db0ad29e60fcfb4544ff3744dfa235ab1744ea0c998e1c6f80a630996d PKG_BUILD_DIR:=$(BUILD_DIR)/OpenList-$(PKG_VERSION) @@ -24,7 +24,7 @@ PKG_MAINTAINER:=sbwml define Download/openlist-frontend FILE:=openlist-frontend-dist-lite-v$(PKG_WEB_VERSION).tar.gz URL:=https://github.com/OpenListTeam/OpenList-Frontend/releases/download/v$(PKG_WEB_VERSION)/ - HASH:=2a365c1ce17904926cf275540680f0f24c0c8c3620fcbb98149d7faf781235aa + HASH:=00c8cf73fa98ba3ff56de9a8fd02d545ef87236170e03ca6f9a76c04d4cf957e endef PKG_BUILD_DEPENDS:=golang/host diff --git a/openwrt-passwall/luci-app-passwall/luasrc/controller/passwall.lua b/openwrt-passwall/luci-app-passwall/luasrc/controller/passwall.lua index a73b11328e..e46862f4b1 100644 --- a/openwrt-passwall/luci-app-passwall/luasrc/controller/passwall.lua +++ b/openwrt-passwall/luci-app-passwall/luasrc/controller/passwall.lua @@ -83,6 +83,7 @@ function index() entry({"admin", "services", appname, "clear_all_nodes"}, call("clear_all_nodes")).leaf = true entry({"admin", "services", appname, "delete_select_nodes"}, call("delete_select_nodes")).leaf = true entry({"admin", "services", appname, "get_node"}, call("get_node")).leaf = true + entry({"admin", "services", appname, "save_node_order"}, call("save_node_order")).leaf = true entry({"admin", "services", appname, "update_rules"}, call("update_rules")).leaf = true entry({"admin", "services", appname, "subscribe_del_node"}, call("subscribe_del_node")).leaf = true entry({"admin", "services", appname, "subscribe_del_all"}, call("subscribe_del_all")).leaf = true @@ -591,13 +592,12 @@ function delete_select_nodes() end end - function get_node() local id = http.formvalue("id") local result = {} local show_node_info = api.uci_get_type("global_other", "show_node_info", "0") - function add_is_ipv6_key(o) + local function add_is_ipv6_key(o) if o and o.address and show_node_info == "1" then local f = api.get_ipv6_full(o.address) if f ~= "" then @@ -611,14 +611,35 @@ function get_node() result = uci:get_all(appname, id) add_is_ipv6_key(result) else + local default_nodes = {} + local other_nodes = {} uci:foreach(appname, "nodes", function(t) add_is_ipv6_key(t) - result[#result + 1] = t + if not t.group or t.group == "" then + default_nodes[#default_nodes + 1] = t + else + other_nodes[#other_nodes + 1] = t + end end) + for i = 1, #default_nodes do result[#result + 1] = default_nodes[i] end + for i = 1, #other_nodes do result[#result + 1] = other_nodes[i] end end http_write_json(result) end +function save_node_order() + local ids = http.formvalue("ids") or "" + local new_order = {} + for id in ids:gmatch("([^,]+)") do + new_order[#new_order + 1] = id + end + for idx, name in ipairs(new_order) do + luci.sys.call(string.format("uci -q reorder %s.%s=%d", appname, name, idx - 1)) + end + api.sh_uci_commit(appname) + http_write_json({ status = "ok" }) +end + function update_rules() local update = http.formvalue("update") luci.sys.call("lua /usr/share/passwall/rule_update.lua log '" .. update .. "' > /dev/null 2>&1 &") diff --git a/openwrt-passwall/luci-app-passwall/luasrc/view/passwall/node_list/node_list.htm b/openwrt-passwall/luci-app-passwall/luasrc/view/passwall/node_list/node_list.htm index 79b5c510a9..f8f16e732e 100644 --- a/openwrt-passwall/luci-app-passwall/luasrc/view/passwall/node_list/node_list.htm +++ b/openwrt-passwall/luci-app-passwall/luasrc/view/passwall/node_list/node_list.htm @@ -210,19 +210,28 @@ table td, .table .td { document.getElementById("set_node_name").innerHTML = ""; } - function _cbi_row_top(id) { - //此函数已经损坏,等待修复或其他解决方案。 - var dom = document.getElementById("cbi-passwall-" + id); - if (dom) { - var trs = document.getElementById("cbi-passwall-nodes").getElementsByClassName("cbi-section-table-row"); - if (trs && trs.length > 0) { - for (var i = 0; i < trs.length; i++) { - var up = dom.getElementsByClassName("cbi-button-up"); - if (up) { - cbi_row_swap(up[0], true, 'cbi.sts.passwall.nodes'); - } - } + function row_swap(btn, up) { + const row = btn.closest("tr"); + if (!row) return; + const parent = row.parentNode; + if (up) { + const prev = row.previousElementSibling; + if (prev && !prev.classList.contains("cbi-section-table-titles")) { + parent.insertBefore(row, prev); } + } else { + const next = row.nextElementSibling; + if (next) parent.insertBefore(next, row); + } + } + + function row_top(btn) { + const row = btn.closest("tr"); + if (!row) return; + const parent = row.parentNode; + let firstDataRow = parent.querySelector("tr:not(.cbi-section-table-titles)"); + if (firstDataRow && firstDataRow !== row) { + parent.insertBefore(row, firstDataRow); } } @@ -305,6 +314,39 @@ table td, .table .td { return { address: address, port: port }; } + function save_current_page_order(group) { + var table = document.getElementById("cbi-passwall-nodes-" + group + "-table"); + if (!table) { + alert("<%:No table!%>"); + return; + } + var rows = table.querySelectorAll("tr.cbi-section-table-row"); + if (!rows || rows.length === 0) { + alert("<%:No nodes!%>"); + return; + } + var btn = document.getElementById("save_order_btn_" + group); + if (btn) btn.disabled = true; + var ids = []; + rows.forEach(function(row) { + var id = row.id.replace("cbi-passwall-", ""); + ids.push(id); + }); + XHR.get('<%=api.url("save_node_order")%>', { + group: group, + ids: ids.join(",") + }, + function(x, result) { + if (btn) btn.disabled = false; + if (x && x.status === 200) { + alert("<%:Saved current page order successfully.%>"); + } else { + alert("<%:Save failed!%>"); + } + } + ); + } + //获取当前使用的节点 function get_now_use_node() { XHR.get('<%=api.url("get_now_use_node")%>', null, @@ -522,6 +564,7 @@ table td, .table .td {
+
@@ -537,11 +580,12 @@ table td, .table .td { {{url_test}}
- - + + + /{{id}}'" alt="<%:Edit%>" title="<%:Edit%>">
diff --git a/openwrt-passwall/luci-app-passwall/po/zh-cn/passwall.po b/openwrt-passwall/luci-app-passwall/po/zh-cn/passwall.po index 8e9fefa8c8..f8d85e434e 100644 --- a/openwrt-passwall/luci-app-passwall/po/zh-cn/passwall.po +++ b/openwrt-passwall/luci-app-passwall/po/zh-cn/passwall.po @@ -421,6 +421,12 @@ msgstr "节点备注" msgid "Add Mode" msgstr "添加方式" +msgid "Save Order" +msgstr "保存当前顺序" + +msgid "Saved current page order successfully." +msgstr "保存当前页面顺序成功。" + msgid "Type" msgstr "类型" diff --git a/small/v2ray-geodata/Makefile b/small/v2ray-geodata/Makefile index 6810d14747..f5834e3a8a 100644 --- a/small/v2ray-geodata/Makefile +++ b/small/v2ray-geodata/Makefile @@ -21,13 +21,13 @@ define Download/geoip HASH:=2445b44d9ae3ab9a867c9d1e0e244646c4c378622e14b9afaf3658ecf46a40b9 endef -GEOSITE_VER:=20251124114549 +GEOSITE_VER:=20251125142217 GEOSITE_FILE:=dlc.dat.$(GEOSITE_VER) define Download/geosite URL:=https://github.com/v2fly/domain-list-community/releases/download/$(GEOSITE_VER)/ URL_FILE:=dlc.dat FILE:=$(GEOSITE_FILE) - HASH:=1685841ffa39bf684e17704fa79ace0acd6f70c84739def9bfcd214dba6a27ba + HASH:=ae6033c884713da8e98f2c1d8167141fbb47aadd297b3ba81450d87582827d5c endef GEOSITE_IRAN_VER:=202511240043 diff --git a/v2rayn/v2rayN/ServiceLib/Handler/Fmt/BaseFmt.cs b/v2rayn/v2rayN/ServiceLib/Handler/Fmt/BaseFmt.cs index 2f31e827bf..ffcd15c5ac 100644 --- a/v2rayn/v2rayN/ServiceLib/Handler/Fmt/BaseFmt.cs +++ b/v2rayn/v2rayN/ServiceLib/Handler/Fmt/BaseFmt.cs @@ -118,7 +118,23 @@ public class BaseFmt } if (item.Extra.IsNotEmpty()) { - dicQuery.Add("extra", Utils.UrlEncode(item.Extra)); + var extra = item.Extra; + try + { + var node = JsonNode.Parse(item.Extra); + if (node != null) + { + extra = node.ToJsonString(new JsonSerializerOptions + { + WriteIndented = false, + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping + }); + } + } + catch + { + } + dicQuery.Add("extra", Utils.UrlEncode(extra)); } break;