Update On Tue Nov 25 19:40:51 CET 2025

This commit is contained in:
github-action[bot]
2025-11-25 19:40:52 +01:00
parent 152eaef70f
commit ee7416bae7
51 changed files with 1211 additions and 1003 deletions

1
.github/update.log vendored
View File

@@ -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

View File

@@ -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

View File

@@ -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=

View File

@@ -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]]

View File

@@ -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",

View File

@@ -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"
}

View File

@@ -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

View File

@@ -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))

View File

@@ -1,22 +0,0 @@
From ea50d7f6921e2c3017258ce8fdfad632d82fbd87 Mon Sep 17 00:00:00 2001
From: Stephen <stephen@vamrs.com>
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 <stephen@vamrs.com>
---
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)

View File

@@ -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

View File

@@ -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

View File

@@ -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);

View File

@@ -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

48
mieru/apis/log/log.go Normal file
View File

@@ -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 <https://www.gnu.org/licenses/>.
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)
}

24
mieru/pkg/log/callback.go Normal file
View File

@@ -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 <https://www.gnu.org/licenses/>.
package log
type LogMessage struct {
Level string
Message string
}
// Callback is a function that consumes a single log message.
type Callback func(LogMessage)

View File

@@ -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)
}
}
}

View File

@@ -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)

View File

@@ -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
}
}

View File

@@ -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 {

View File

@@ -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")

View File

@@ -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) {

View File

@@ -37,13 +37,6 @@ const maxClientLogFiles = 25
// In Windows, it is typically C:\Users\<user>\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 {

View File

@@ -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 <https://www.gnu.org/licenses/>.
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{})
}

View File

@@ -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)

View File

@@ -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

View File

@@ -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=

View File

@@ -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&notcp=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&notcp=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&notcp=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&notcp=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 |

View File

@@ -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:

View File

@@ -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:

View File

@@ -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:

View File

@@ -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

View File

@@ -7,7 +7,7 @@ NodePass creates tunnels with an unencrypted TCP control channel and configurabl
The general syntax for NodePass commands is:
```bash
nodepass "<core>://<tunnel_addr>/<target_addr>?log=<level>&tls=<mode>&crt=<cert_file>&key=<key_file>&dns=<dns_servers>&min=<min_pool>&max=<max_pool>&mode=<run_mode>&quic=<quic_mode>&dial=<source_ip>&read=<timeout>&rate=<mbps>&slot=<limit>&proxy=<mode>&notcp=<0|1>&noudp=<0|1>"
nodepass "<core>://<tunnel_addr>/<target_addr>?log=<level>&tls=<mode>&crt=<cert_file>&key=<key_file>&dns=<duration>&min=<min_pool>&max=<max_pool>&mode=<run_mode>&quic=<quic_mode>&dial=<source_ip>&read=<timeout>&rate=<mbps>&slot=<limit>&proxy=<mode>&notcp=<0|1>&noudp=<0|1>"
```
Where:
@@ -19,7 +19,7 @@ Where:
Common query parameters:
- `log=<level>`: Log verbosity level (`none`, `debug`, `info`, `warn`, `error`, or `event`)
- `dns=<dns_servers>`: Custom DNS servers (comma-separated IP addresses, default: `1.1.1.1,8.8.8.8`)
- `dns=<duration>`: DNS cache TTL duration (default: `5m`, supports time units like `1h`, `30m`, `15s`, etc.)
- `min=<min_pool>`: Minimum connection pool capacity (default: 64, set by client)
- `max=<max_pool>`: Maximum connection pool capacity (default: 1024, set by server and delivered to client)
- `mode=<run_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://<tunnel_addr>/<target_addr>?log=<level>&tls=<mode>&crt=<cert_file>&key=<key_file>&dns=<dns_servers>&quic=<quic_mode>&max=<max_pool>&mode=<run_mode>&dial=<source_ip>&read=<timeout>&rate=<mbps>&slot=<limit>&proxy=<mode>&notcp=<0|1>&noudp=<0|1>"
nodepass "server://<tunnel_addr>/<target_addr>?log=<level>&tls=<mode>&crt=<cert_file>&key=<key_file>&dns=<duration>&quic=<quic_mode>&max=<max_pool>&mode=<run_mode>&dial=<source_ip>&read=<timeout>&rate=<mbps>&slot=<limit>&proxy=<mode>&notcp=<0|1>&noudp=<0|1>"
```
#### Parameters
@@ -59,7 +59,7 @@ nodepass "server://<tunnel_addr>/<target_addr>?log=<level>&tls=<mode>&crt=<cert_
- `tunnel_addr`: Address for the TCP tunnel endpoint (control channel) that clients will connect to (e.g., 10.1.0.1:10101)
- `target_addr`: The destination address for business data with bidirectional flow support (e.g., 10.1.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.)
- `quic`: QUIC transport mode (0, 1)
- `0`: Use TCP-based connection pool (default)
- `1`: Use QUIC-based UDP connection pool with stream multiplexing
@@ -127,7 +127,7 @@ nodepass "server://10.1.0.1:10101/192.168.1.100:8080?log=debug&quic=1&tls=2&mode
Client mode connects to a NodePass server and supports bidirectional data flow forwarding.
```bash
nodepass "client://<tunnel_addr>/<target_addr>?log=<level>&dns=<dns_servers>&quic=<quic_mode>&min=<min_pool>&mode=<run_mode>&dial=<source_ip>&read=<timeout>&rate=<mbps>&slot=<limit>&proxy=<mode>&notcp=<0|1>&noudp=<0|1>"
nodepass "client://<tunnel_addr>/<target_addr>?log=<level>&dns=<duration>&quic=<quic_mode>&min=<min_pool>&mode=<run_mode>&dial=<source_ip>&read=<timeout>&rate=<mbps>&slot=<limit>&proxy=<mode>&notcp=<0|1>&noudp=<0|1>"
```
#### Parameters
@@ -135,7 +135,7 @@ nodepass "client://<tunnel_addr>/<target_addr>?log=<level>&dns=<dns_servers>&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

View File

@@ -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&notcp=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&notcp=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&notcp=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&notcp=0&noudp=0", // 完整配置URL
"restart": true, // 自启动策略
"meta": { // 用于组织和对端跟踪的元数据
"peer": {
@@ -1581,7 +1581,7 @@ client://<server_host>:<server_port>/<local_host>:<local_port>?<parameters>
| `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` | 两者 |

View File

@@ -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解析以获得最高效率
- **协议感知**根据连接要求自动选择IPv4IPv6地址
- **协议感知**自动处理IPv4IPv6地址
- **线程安全**并发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缓存最佳实践**
- **静态基础设施**对于稳定的生产环境使用较长TTL15m-1h
- **动态DNS**对于频繁变化的主机名使用较短TTL1m-5m
- **开发环境**对于快速迭代和测试使用较短TTL1m-2m
- **高流量服务**在新鲜度和减少DNS服务器负载之间取得平衡
- **地理分布**设置TTL时考虑DNS传播延迟
DNS调优示例
```bash
# 稳定生产环境的长TTL
export NP_DNS_CACHING_TTL=30m
# 动态开发环境的短TTL
export NP_DNS_CACHING_TTL=2m
```
### UDP设置
对于严重依赖UDP流量的应用

View File

@@ -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 DNS1.1.1.1),以隐私保护著称
- 客户端使用Quad9 DNS9.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相关问题
### 示例18IPv6 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"
# 生产API10分钟缓存以平衡性能
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较长缓存TTL30分钟
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较短缓存TTL1分钟
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?
- 应用程序无需修改,透明地实现故障转移
- 仅记录警告和错误,减少日志输出
### 示例22API网关后端池
### 示例21API网关后端池
为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的访问控制
### 示例25Web应用的反向代理支持
### 示例24Web应用的反向代理支持
使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隧道

View File

@@ -47,11 +47,6 @@ NodePass 创建了一个具有独立控制和数据通道的网络架构:
- **TCP**:具有持久连接的全双工流式传输,在客户端单端转发模式下优化了直接连接建立
- **UDP**:具有可配置缓冲区大小和超时的数据报转发
6. **智能DNS解析**
- 智能缓存与后台刷新,提供最优性能
- 自定义DNS服务器与自动故障转移
- 原生支持IPv4/IPv6现代网络
## 数据传输流
NodePass 通过其隧道架构建立双向数据流,支持 TCP 和 UDP 协议。系统支持三种数据流模式:

View File

@@ -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问题

View File

@@ -7,7 +7,7 @@ NodePass创建一个带有未加密TCP控制通道的隧道并为数据交换
NodePass命令的一般语法是
```bash
nodepass "<core>://<tunnel_addr>/<target_addr>?log=<level>&tls=<mode>&crt=<cert_file>&key=<key_file>&dns=<dns_servers>&min=<min_pool>&max=<max_pool>&mode=<run_mode>&quic=<quic_mode>&dial=<source_ip>&read=<timeout>&rate=<mbps>&slot=<limit>&proxy=<mode>&notcp=<0|1>&noudp=<0|1>"
nodepass "<core>://<tunnel_addr>/<target_addr>?log=<level>&tls=<mode>&crt=<cert_file>&key=<key_file>&dns=<duration>&min=<min_pool>&max=<max_pool>&mode=<run_mode>&quic=<quic_mode>&dial=<source_ip>&read=<timeout>&rate=<mbps>&slot=<limit>&proxy=<mode>&notcp=<0|1>&noudp=<0|1>"
```
其中:
@@ -19,7 +19,7 @@ nodepass "<core>://<tunnel_addr>/<target_addr>?log=<level>&tls=<mode>&crt=<cert_
通用查询参数:
- `log=<level>`:日志详细级别(`none``debug``info``warn``error``event`
- `dns=<dns_servers>`自定义DNS服务器逗号分隔的IP地址默认`1.1.1.1,8.8.8.8`
- `dns=<duration>`DNS缓存TTL持续时间默认`5m`,支持时间单位如`1h``30m``15s`
- `min=<min_pool>`最小连接池容量默认64由客户端设置
- `max=<max_pool>`最大连接池容量默认1024由服务端设置并下发给客户端
- `mode=<run_mode>`:运行模式控制(`0``1``2`- 控制操作行为
@@ -51,7 +51,7 @@ NodePass提供三种互补的运行模式以适应各种部署场景。
服务端模式建立隧道控制通道,并支持双向数据流转发。
```bash
nodepass "server://<tunnel_addr>/<target_addr>?log=<level>&tls=<mode>&crt=<cert_file>&key=<key_file>&dns=<dns_servers>&quic=<quic_mode>&max=<max_pool>&mode=<run_mode>&dial=<source_ip>&read=<timeout>&rate=<mbps>&slot=<limit>&proxy=<mode>&notcp=<0|1>&noudp=<0|1>"
nodepass "server://<tunnel_addr>/<target_addr>?log=<level>&tls=<mode>&crt=<cert_file>&key=<key_file>&dns=<duration>&quic=<quic_mode>&max=<max_pool>&mode=<run_mode>&dial=<source_ip>&read=<timeout>&rate=<mbps>&slot=<limit>&proxy=<mode>&notcp=<0|1>&noudp=<0|1>"
```
#### 参数
@@ -59,7 +59,7 @@ nodepass "server://<tunnel_addr>/<target_addr>?log=<level>&tls=<mode>&crt=<cert_
- `tunnel_addr`TCP隧道端点地址控制通道客户端将连接到此处(例如, 10.1.0.1:10101)
- `target_addr`:业务数据的目标地址,支持双向数据流模式(例如, 10.1.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`
- `quic`QUIC传输模式 (0, 1)
- `0`使用基于TCP的连接池默认
- `1`使用基于QUIC的UDP连接池支持流多路复用
@@ -127,7 +127,7 @@ nodepass "server://10.1.0.1:10101/192.168.1.100:8080?log=debug&quic=1&tls=2&mode
客户端模式连接到NodePass服务端并支持双向数据流转发。
```bash
nodepass "client://<tunnel_addr>/<target_addr>?log=<level>&dns=<dns_servers>&quic=<quic_mode>&min=<min_pool>&mode=<run_mode>&dial=<source_ip>&read=<timeout>&rate=<mbps>&slot=<limit>&proxy=<mode>&notcp=<0|1>&noudp=<0|1>"
nodepass "client://<tunnel_addr>/<target_addr>?log=<level>&dns=<duration>&quic=<quic_mode>&min=<min_pool>&mode=<run_mode>&dial=<source_ip>&read=<timeout>&rate=<mbps>&slot=<limit>&proxy=<mode>&notcp=<0|1>&noudp=<0|1>"
```
#### 参数
@@ -135,7 +135,7 @@ nodepass "client://<tunnel_addr>/<target_addr>?log=<level>&dns=<dns_servers>&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`:自动检测(默认)- 首先尝试本地绑定,如果失败则回退到握手模式

View File

@@ -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

View File

@@ -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=

View File

@@ -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&notcp=%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)
}

View File

@@ -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)
}

View File

@@ -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))

View File

@@ -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&notcp=%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)

View File

@@ -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 <admin@cooluc.com>
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

View File

@@ -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 &")

View File

@@ -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 {
</table>
<div class="cbi-section-create cbi-tblsection-create">
<input class="cbi-button cbi-button-add" type="button" value="<%:Add%>" onclick="to_add_node()">
<input class="cbi-button cbi-button-apply" type="button" id="save_order_btn_{{group}}" value="<%:Save Order%>" onclick="save_current_page_order('{{group}}')">
</div>
</fieldset>
</script>
@@ -537,11 +580,12 @@ table td, .table .td {
<td class="td cbi-value-field">{{url_test}}</td>
<td class="td cbi-section-table-cell nowrap cbi-section-actions">
<div class="node-wrapper">
<!--It has been damaged and awaits repair or other solutions.-->
<!--<input class="btn cbi-button" type="button" value="<%:To Top%>" onclick="_cbi_row_top('{{id}}')"/>-->
<input class="cbi-input-checkbox nodes_select" type="checkbox" cbid="{{id}}" />
<input class="btn cbi-button cbi-button-edit" type="button" value="<%:To Top%>" onclick="row_top(this)" title="<%:To Top%>"/>
<input class="btn cbi-button cbi-button-apply" type="button" value="<%:Use%>" id="apply_{{id}}" onclick="open_set_node_div('{{id}}')"/>
<input class="btn cbi-button cbi-button-add" type="button" value="<%:Copy%>" onclick="copy_node('{{id}}')"/>
<input class="btn cbi-button cbi-button-up" type="button" value="<%:Move up%>" onclick="return row_swap(this, true)" title="<%:Move up%>">
<input class="btn cbi-button cbi-button-down" type="button" value="<%:Move down%>" onclick="return row_swap(this, false)" title="<%:Move down%>">
<input class="btn cbi-button cbi-button-edit" type="button" value="<%:Edit%>" onclick="location.href='<%=api.url("node_config")%>/{{id}}'" alt="<%:Edit%>" title="<%:Edit%>">
<input class="btn cbi-button cbi-button-remove" type="button" value="<%:Delete%>" onclick="del_node('{{id}}')" alt="<%:Delete%>" title="<%:Delete%>">
</div>

View File

@@ -421,6 +421,12 @@ msgstr "节点备注"
msgid "Add Mode"
msgstr "添加方式"
msgid "Save Order"
msgstr "保存当前顺序"
msgid "Saved current page order successfully."
msgstr "保存当前页面顺序成功。"
msgid "Type"
msgstr "类型"

View File

@@ -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

View File

@@ -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;