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
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 Sat Nov 22 19:36:29 CET 2025
Update On Sun Nov 23 19:36:55 CET 2025 Update On Sun Nov 23 19:36:55 CET 2025
Update On Mon Nov 24 19:40:17 CET 2025 Update On Mon Nov 24 19:40:17 CET 2025
Update On Tue Nov 25 19:40:43 CET 2025
+1 -1
View File
@@ -32,7 +32,7 @@ require (
github.com/metacubex/sing-shadowsocks v0.2.12 github.com/metacubex/sing-shadowsocks v0.2.12
github.com/metacubex/sing-shadowsocks2 v0.2.7 github.com/metacubex/sing-shadowsocks2 v0.2.7
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2 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-vmess v0.2.4
github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f
github.com/metacubex/smux v0.0.0-20251111013112-03f8d12dafc1 github.com/metacubex/smux v0.0.0-20251111013112-03f8d12dafc1
+2 -2
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-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 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-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.9 h1:jY0Yyt8nnN3yQRN/jTxgqNCmGi1dsFdxdIi7pQUlVVU=
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/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 h1:Tx6AGgCiEf400E/xyDuYyafsel6sGbR8oF7RkAaus6I=
github.com/metacubex/sing-vmess v0.2.4/go.mod h1:21R5R1u90uUvBQF0owoooEu96/SAYYD56nDrwm6nFaM= 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= github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f h1:Sr/DYKYofKHKc4GF3qkRGNuj6XA6c0eqPgEDN+VAsYU=
+155 -77
View File
@@ -168,6 +168,15 @@ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "aligned"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "377e4c0ba83e4431b10df45c1d4666f178ea9c552cac93e60c3a88bf32785923"
dependencies = [
"as-slice",
]
[[package]] [[package]]
name = "aligned-vec" name = "aligned-vec"
version = "0.6.4" version = "0.6.4"
@@ -346,7 +355,7 @@ dependencies = [
"objc2-foundation 0.3.2", "objc2-foundation 0.3.2",
"parking_lot", "parking_lot",
"percent-encoding", "percent-encoding",
"windows-sys 0.60.2", "windows-sys 0.52.0",
"wl-clipboard-rs", "wl-clipboard-rs",
"x11rb", "x11rb",
] ]
@@ -386,6 +395,15 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b" 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]] [[package]]
name = "ash" name = "ash"
version = "0.38.0+1.3.281" 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" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" 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]] [[package]]
name = "av1-grain" name = "av1-grain"
version = "0.2.4" version = "0.2.4"
@@ -850,7 +888,7 @@ dependencies = [
"bitflags 2.9.4", "bitflags 2.9.4",
"cexpr", "cexpr",
"clang-sys", "clang-sys",
"itertools 0.12.1", "itertools 0.10.5",
"lazy_static", "lazy_static",
"lazycell", "lazycell",
"log", "log",
@@ -902,9 +940,12 @@ dependencies = [
[[package]] [[package]]
name = "bitstream-io" name = "bitstream-io"
version = "2.6.0" version = "4.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6099cdc01846bc367c4e7dd630dc5966dccf36b652fae7a74e17b640411a91b2" checksum = "60d4bd9d1db2c6bdf285e223a7fa369d5ce98ec767dec949c6ca62863ce61757"
dependencies = [
"core2",
]
[[package]] [[package]]
name = "block" name = "block"
@@ -971,7 +1012,7 @@ dependencies = [
"boa_interner", "boa_interner",
"boa_macros", "boa_macros",
"boa_string", "boa_string",
"indexmap 2.12.0", "indexmap 2.12.1",
"num-bigint", "num-bigint",
"rustc-hash 2.1.1", "rustc-hash 2.1.1",
] ]
@@ -997,7 +1038,7 @@ dependencies = [
"fast-float2", "fast-float2",
"hashbrown 0.15.5", "hashbrown 0.15.5",
"icu_normalizer 1.5.0", "icu_normalizer 1.5.0",
"indexmap 2.12.0", "indexmap 2.12.1",
"intrusive-collections", "intrusive-collections",
"itertools 0.13.0", "itertools 0.13.0",
"num-bigint", "num-bigint",
@@ -1043,7 +1084,7 @@ dependencies = [
"boa_gc", "boa_gc",
"boa_macros", "boa_macros",
"hashbrown 0.15.5", "hashbrown 0.15.5",
"indexmap 2.12.0", "indexmap 2.12.1",
"once_cell", "once_cell",
"phf 0.11.3", "phf 0.11.3",
"rustc-hash 2.1.1", "rustc-hash 2.1.1",
@@ -1166,9 +1207,9 @@ dependencies = [
[[package]] [[package]]
name = "built" name = "built"
version = "0.7.7" version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56ed6191a7e78c36abdb16ab65341eefd73d64d303fffccdbb00d51e4205967b" checksum = "f4ad8f11f288f48ca24471bbd51ac257aaeaaa07adae295591266b792902ae64"
[[package]] [[package]]
name = "bumpalo" name = "bumpalo"
@@ -1459,9 +1500,9 @@ dependencies = [
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.5.51" version = "4.5.53"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c26d721170e0295f191a69bd9a1f93efcdb0aff38684b61ab5750468972e5f5" checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8"
dependencies = [ dependencies = [
"clap_builder", "clap_builder",
"clap_derive", "clap_derive",
@@ -1469,9 +1510,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_builder" name = "clap_builder"
version = "4.5.51" version = "4.5.53"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75835f0c7bf681bfd05abe44e965760fea999a5286c6eb2d59883634fd02011a" checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00"
dependencies = [ dependencies = [
"anstream", "anstream",
"anstyle", "anstyle",
@@ -1542,7 +1583,7 @@ dependencies = [
"hex", "hex",
"humansize", "humansize",
"image", "image",
"indexmap 2.12.0", "indexmap 2.12.1",
"itertools 0.14.0", "itertools 0.14.0",
"log", "log",
"md-5", "md-5",
@@ -1855,6 +1896,15 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "core2"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "core_maths" name = "core_maths"
version = "0.1.1" version = "0.1.1"
@@ -2294,7 +2344,7 @@ dependencies = [
"libc", "libc",
"option-ext", "option-ext",
"redox_users 0.5.2", "redox_users 0.5.2",
"windows-sys 0.60.2", "windows-sys 0.59.0",
] ]
[[package]] [[package]]
@@ -2803,7 +2853,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad"
dependencies = [ dependencies = [
"libc", "libc",
"windows-sys 0.60.2", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@@ -2844,9 +2894,9 @@ dependencies = [
[[package]] [[package]]
name = "exr" name = "exr"
version = "1.73.0" version = "1.74.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f83197f59927b46c04a183a619b7c29df34e63e63c7869320862268c0ef687e0" checksum = "4300e043a56aa2cb633c01af81ca8f699a321879a7854d3896a0ba89056363be"
dependencies = [ dependencies = [
"bit_field", "bit_field",
"half", "half",
@@ -3428,6 +3478,16 @@ dependencies = [
"weezl", "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]] [[package]]
name = "gimli" name = "gimli"
version = "0.31.1" version = "0.31.1"
@@ -3788,7 +3848,7 @@ dependencies = [
"futures-core", "futures-core",
"futures-sink", "futures-sink",
"http", "http",
"indexmap 2.12.0", "indexmap 2.12.1",
"slab", "slab",
"tokio", "tokio",
"tokio-util", "tokio-util",
@@ -3864,9 +3924,9 @@ dependencies = [
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.16.0" version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
dependencies = [ dependencies = [
"allocator-api2", "allocator-api2",
"foldhash 0.2.0", "foldhash 0.2.0",
@@ -4350,15 +4410,15 @@ dependencies = [
[[package]] [[package]]
name = "image" name = "image"
version = "0.25.8" version = "0.25.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "529feb3e6769d234375c4cf1ee2ce713682b8e76538cb13f9fc23e1400a591e7" checksum = "e6506c6c10786659413faa717ceebcb8f70731c0a60cbae39795fdf114519c1a"
dependencies = [ dependencies = [
"bytemuck", "bytemuck",
"byteorder-lite", "byteorder-lite",
"color_quant", "color_quant",
"exr", "exr",
"gif", "gif 0.14.0",
"image-webp", "image-webp",
"moxcms", "moxcms",
"num-traits", "num-traits",
@@ -4368,8 +4428,8 @@ dependencies = [
"rayon", "rayon",
"rgb", "rgb",
"tiff", "tiff",
"zune-core", "zune-core 0.5.0",
"zune-jpeg", "zune-jpeg 0.5.5",
] ]
[[package]] [[package]]
@@ -4438,12 +4498,12 @@ dependencies = [
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "2.12.0" version = "2.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2"
dependencies = [ dependencies = [
"equivalent", "equivalent",
"hashbrown 0.16.0", "hashbrown 0.16.1",
"serde", "serde",
"serde_core", "serde_core",
] ]
@@ -4643,15 +4703,6 @@ dependencies = [
"either", "either",
] ]
[[package]]
name = "itertools"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
dependencies = [
"either",
]
[[package]] [[package]]
name = "itertools" name = "itertools"
version = "0.13.0" version = "0.13.0"
@@ -4832,7 +4883,7 @@ checksum = "02cb977175687f33fa4afa0c95c112b987ea1443e5a51c8f8ff27dc618270cc2"
dependencies = [ dependencies = [
"cssparser", "cssparser",
"html5ever", "html5ever",
"indexmap 2.12.0", "indexmap 2.12.1",
"selectors", "selectors",
] ]
@@ -4922,7 +4973,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"windows-targets 0.53.3", "windows-targets 0.48.5",
] ]
[[package]] [[package]]
@@ -5357,9 +5408,9 @@ dependencies = [
"cfg_aliases", "cfg_aliases",
"codespan-reporting", "codespan-reporting",
"half", "half",
"hashbrown 0.16.0", "hashbrown 0.16.1",
"hexf-parse", "hexf-parse",
"indexmap 2.12.0", "indexmap 2.12.1",
"libm", "libm",
"log", "log",
"num-traits", "num-traits",
@@ -5741,7 +5792,7 @@ dependencies = [
"http-body-util", "http-body-util",
"hyper", "hyper",
"hyper-util", "hyper-util",
"indexmap 2.12.0", "indexmap 2.12.1",
"interprocess", "interprocess",
"nyanpasu-utils", "nyanpasu-utils",
"pin-project-lite", "pin-project-lite",
@@ -6430,7 +6481,7 @@ checksum = "71ef2dba21be1ce515378b2b7143eaa2a912f9e6ffe162ae20639d56f53d60e3"
dependencies = [ dependencies = [
"allocator-api2", "allocator-api2",
"bumpalo", "bumpalo",
"hashbrown 0.16.0", "hashbrown 0.16.1",
"oxc_data_structures", "oxc_data_structures",
"rustc-hash 2.1.1", "rustc-hash 2.1.1",
] ]
@@ -6658,6 +6709,12 @@ version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
[[package]]
name = "pastey"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec"
[[package]] [[package]]
name = "pathdiff" name = "pathdiff"
version = "0.2.3" version = "0.2.3"
@@ -6741,7 +6798,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db"
dependencies = [ dependencies = [
"fixedbitset 0.4.2", "fixedbitset 0.4.2",
"indexmap 2.12.0", "indexmap 2.12.1",
] ]
[[package]] [[package]]
@@ -6985,7 +7042,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3af6b589e163c5a788fab00ce0c0366f6efbb9959c2f9874b224936af7fce7e1" checksum = "3af6b589e163c5a788fab00ce0c0366f6efbb9959c2f9874b224936af7fce7e1"
dependencies = [ dependencies = [
"base64 0.22.1", "base64 0.22.1",
"indexmap 2.12.0", "indexmap 2.12.1",
"quick-xml 0.38.3", "quick-xml 0.38.3",
"serde", "serde",
"time", "time",
@@ -7326,7 +7383,7 @@ dependencies = [
"once_cell", "once_cell",
"socket2", "socket2",
"tracing", "tracing",
"windows-sys 0.60.2", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@@ -7462,19 +7519,21 @@ checksum = "c3d6831663a5098ea164f89cff59c6284e95f4e3c76ce9848d4529f5ccca9bde"
[[package]] [[package]]
name = "rav1e" name = "rav1e"
version = "0.7.1" version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd87ce80a7665b1cce111f8a16c1f3929f6547ce91ade6addf4ec86a8dda5ce9" checksum = "43b6dd56e85d9483277cde964fd1bdb0428de4fec5ebba7540995639a21cb32b"
dependencies = [ dependencies = [
"aligned-vec",
"arbitrary", "arbitrary",
"arg_enum_proc_macro", "arg_enum_proc_macro",
"arrayvec", "arrayvec",
"av-scenechange",
"av1-grain", "av1-grain",
"bitstream-io", "bitstream-io",
"built", "built",
"cfg-if", "cfg-if",
"interpolate_name", "interpolate_name",
"itertools 0.12.1", "itertools 0.14.0",
"libc", "libc",
"libfuzzer-sys", "libfuzzer-sys",
"log", "log",
@@ -7483,23 +7542,21 @@ dependencies = [
"noop_proc_macro", "noop_proc_macro",
"num-derive 0.4.2", "num-derive 0.4.2",
"num-traits", "num-traits",
"once_cell",
"paste", "paste",
"profiling", "profiling",
"rand 0.8.5", "rand 0.9.2",
"rand_chacha 0.3.1", "rand_chacha 0.9.0",
"simd_helpers", "simd_helpers",
"system-deps", "thiserror 2.0.17",
"thiserror 1.0.69",
"v_frame", "v_frame",
"wasm-bindgen", "wasm-bindgen",
] ]
[[package]] [[package]]
name = "ravif" name = "ravif"
version = "0.11.20" version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5825c26fddd16ab9f515930d49028a630efec172e903483c94796cfe31893e6b" checksum = "ef69c1990ceef18a116855938e74793a5f7496ee907562bd0857b6ac734ab285"
dependencies = [ dependencies = [
"avif-serialize", "avif-serialize",
"imgref", "imgref",
@@ -7707,7 +7764,7 @@ version = "0.45.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8928798c0a55e03c9ca6c4c6846f76377427d2c1e1f7e6de3c06ae57942df43" checksum = "a8928798c0a55e03c9ca6c4c6846f76377427d2c1e1f7e6de3c06ae57942df43"
dependencies = [ dependencies = [
"gif", "gif 0.13.3",
"image-webp", "image-webp",
"log", "log",
"pico-args", "pico-args",
@@ -7715,7 +7772,7 @@ dependencies = [
"svgtypes", "svgtypes",
"tiny-skia", "tiny-skia",
"usvg", "usvg",
"zune-jpeg", "zune-jpeg 0.4.21",
] ]
[[package]] [[package]]
@@ -7893,7 +7950,7 @@ dependencies = [
"errno", "errno",
"libc", "libc",
"linux-raw-sys 0.9.4", "linux-raw-sys 0.9.4",
"windows-sys 0.60.2", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@@ -8189,7 +8246,7 @@ version = "1.0.145"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c"
dependencies = [ dependencies = [
"indexmap 2.12.0", "indexmap 2.12.1",
"itoa", "itoa",
"memchr", "memchr",
"ryu", "ryu",
@@ -8258,7 +8315,7 @@ dependencies = [
"chrono", "chrono",
"hex", "hex",
"indexmap 1.9.3", "indexmap 1.9.3",
"indexmap 2.12.0", "indexmap 2.12.1",
"schemars 0.9.0", "schemars 0.9.0",
"schemars 1.0.4", "schemars 1.0.4",
"serde", "serde",
@@ -8286,7 +8343,7 @@ version = "0.9.34+deprecated"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47"
dependencies = [ dependencies = [
"indexmap 2.12.0", "indexmap 2.12.1",
"itoa", "itoa",
"ryu", "ryu",
"serde", "serde",
@@ -8298,7 +8355,7 @@ name = "serde_yaml_ng"
version = "0.10.0" version = "0.10.0"
source = "git+https://github.com/libnyanpasu/serde-yaml-ng.git?branch=feat/specta#3078760ab9e9a3a9e18ebb4c5d3e577eb74c4a5c" source = "git+https://github.com/libnyanpasu/serde-yaml-ng.git?branch=feat/specta#3078760ab9e9a3a9e18ebb4c5d3e577eb74c4a5c"
dependencies = [ dependencies = [
"indexmap 2.12.0", "indexmap 2.12.1",
"itoa", "itoa",
"ryu", "ryu",
"serde", "serde",
@@ -8697,7 +8754,7 @@ version = "2.0.0-rc.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab7f01e9310a820edd31c80fde3cae445295adde21a3f9416517d7d65015b971" checksum = "ab7f01e9310a820edd31c80fde3cae445295adde21a3f9416517d7d65015b971"
dependencies = [ dependencies = [
"indexmap 2.12.0", "indexmap 2.12.1",
"paste", "paste",
"serde", "serde",
"serde_json", "serde_json",
@@ -9725,7 +9782,7 @@ dependencies = [
"half", "half",
"quick-error", "quick-error",
"weezl", "weezl",
"zune-jpeg", "zune-jpeg 0.4.21",
] ]
[[package]] [[package]]
@@ -9919,7 +9976,7 @@ version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75129e1dc5000bfbaa9fee9d1b21f974f9fbad9daec557a521ee6e080825f6e8" checksum = "75129e1dc5000bfbaa9fee9d1b21f974f9fbad9daec557a521ee6e080825f6e8"
dependencies = [ dependencies = [
"indexmap 2.12.0", "indexmap 2.12.1",
"serde", "serde",
"serde_spanned 1.0.0", "serde_spanned 1.0.0",
"toml_datetime 0.7.0", "toml_datetime 0.7.0",
@@ -9952,7 +10009,7 @@ version = "0.19.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421"
dependencies = [ dependencies = [
"indexmap 2.12.0", "indexmap 2.12.1",
"toml_datetime 0.6.11", "toml_datetime 0.6.11",
"winnow 0.5.40", "winnow 0.5.40",
] ]
@@ -9963,7 +10020,7 @@ version = "0.20.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81"
dependencies = [ dependencies = [
"indexmap 2.12.0", "indexmap 2.12.1",
"toml_datetime 0.6.11", "toml_datetime 0.6.11",
"winnow 0.5.40", "winnow 0.5.40",
] ]
@@ -9974,7 +10031,7 @@ version = "0.22.27"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a"
dependencies = [ dependencies = [
"indexmap 2.12.0", "indexmap 2.12.1",
"serde", "serde",
"serde_spanned 0.6.9", "serde_spanned 0.6.9",
"toml_datetime 0.6.11", "toml_datetime 0.6.11",
@@ -11028,7 +11085,7 @@ dependencies = [
"cfg-if", "cfg-if",
"cfg_aliases", "cfg_aliases",
"document-features", "document-features",
"hashbrown 0.16.0", "hashbrown 0.16.1",
"js-sys", "js-sys",
"log", "log",
"naga", "naga",
@@ -11059,8 +11116,8 @@ dependencies = [
"bytemuck", "bytemuck",
"cfg_aliases", "cfg_aliases",
"document-features", "document-features",
"hashbrown 0.16.0", "hashbrown 0.16.1",
"indexmap 2.12.0", "indexmap 2.12.1",
"log", "log",
"naga", "naga",
"once_cell", "once_cell",
@@ -11126,7 +11183,7 @@ dependencies = [
"gpu-alloc", "gpu-alloc",
"gpu-allocator", "gpu-allocator",
"gpu-descriptor", "gpu-descriptor",
"hashbrown 0.16.0", "hashbrown 0.16.1",
"js-sys", "js-sys",
"khronos-egl", "khronos-egl",
"libc", "libc",
@@ -12191,6 +12248,12 @@ dependencies = [
"lzma-sys", "lzma-sys",
] ]
[[package]]
name = "y4m"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a5a4b21e1a62b67a2970e6831bc091d7b87e119e7f9791aef9702e3bef04448"
[[package]] [[package]]
name = "yansi" name = "yansi"
version = "1.0.1" version = "1.0.1"
@@ -12474,7 +12537,7 @@ dependencies = [
"flate2", "flate2",
"getrandom 0.3.3", "getrandom 0.3.3",
"hmac", "hmac",
"indexmap 2.12.0", "indexmap 2.12.1",
"lzma-rs", "lzma-rs",
"memchr", "memchr",
"pbkdf2", "pbkdf2",
@@ -12494,7 +12557,7 @@ checksum = "caa8cd6af31c3b31c6631b8f483848b91589021b28fffe50adada48d4f4d2ed1"
dependencies = [ dependencies = [
"arbitrary", "arbitrary",
"crc32fast", "crc32fast",
"indexmap 2.12.0", "indexmap 2.12.1",
"memchr", "memchr",
] ]
@@ -12559,6 +12622,12 @@ version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a"
[[package]]
name = "zune-core"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "111f7d9820f05fd715df3144e254d6fc02ee4088b0644c0ffd0efc9e6d9d2773"
[[package]] [[package]]
name = "zune-inflate" name = "zune-inflate"
version = "0.2.54" version = "0.2.54"
@@ -12574,7 +12643,16 @@ version = "0.4.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29ce2c8a9384ad323cf564b67da86e21d3cfdff87908bc1223ed5c99bc792713" checksum = "29ce2c8a9384ad323cf564b67da86e21d3cfdff87908bc1223ed5c99bc792713"
dependencies = [ 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]] [[package]]
@@ -56,7 +56,7 @@
"@csstools/normalize.css": "12.1.1", "@csstools/normalize.css": "12.1.1",
"@emotion/babel-plugin": "11.13.5", "@emotion/babel-plugin": "11.13.5",
"@emotion/react": "11.14.0", "@emotion/react": "11.14.0",
"@iconify/json": "2.2.409", "@iconify/json": "2.2.410",
"@monaco-editor/react": "4.7.0", "@monaco-editor/react": "4.7.0",
"@tanstack/react-query": "5.90.7", "@tanstack/react-query": "5.90.7",
"@tanstack/react-router": "1.134.15", "@tanstack/react-router": "1.134.15",
+2 -2
View File
@@ -2,7 +2,7 @@
"manifest_version": 1, "manifest_version": 1,
"latest": { "latest": {
"mihomo": "v1.19.16", "mihomo": "v1.19.16",
"mihomo_alpha": "alpha-7571c87", "mihomo_alpha": "alpha-66781a5",
"clash_rs": "v0.9.2", "clash_rs": "v0.9.2",
"clash_premium": "2023-09-05-gdcc8d87", "clash_premium": "2023-09-05-gdcc8d87",
"clash_rs_alpha": "0.9.2-alpha+sha.87c7b2c" "clash_rs_alpha": "0.9.2-alpha+sha.87c7b2c"
@@ -69,5 +69,5 @@
"linux-armv7hf": "clash-armv7-unknown-linux-gnueabihf" "linux-armv7hf": "clash-armv7-unknown-linux-gnueabihf"
} }
}, },
"updated_at": "2025-11-23T22:21:13.537Z" "updated_at": "2025-11-24T22:21:21.998Z"
} }
+5 -5
View File
@@ -346,8 +346,8 @@ importers:
specifier: 11.14.0 specifier: 11.14.0
version: 11.14.0(@types/react@19.2.2)(react@19.2.0) version: 11.14.0(@types/react@19.2.2)(react@19.2.0)
'@iconify/json': '@iconify/json':
specifier: 2.2.409 specifier: 2.2.410
version: 2.2.409 version: 2.2.410
'@monaco-editor/react': '@monaco-editor/react':
specifier: 4.7.0 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) 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: prettier-plugin-ember-template-tag:
optional: true optional: true
'@iconify/json@2.2.409': '@iconify/json@2.2.410':
resolution: {integrity: sha512-PnTFu5JSM+GTL2mPwLBi6UDFiQvqG+OVITz9JNfZgSjFuWcF67LhedfjKd4jO/0EgLQLl0StjGbDEu1B19V5vw==} resolution: {integrity: sha512-0IhW9Sfudf3cPQHoCwr2gJMMUUkLW01WIkGoP9PbwVKXl1I/KTRHtM9IchLufT8M86QHBWRcinApzkL60TH9vA==}
'@iconify/types@2.0.0': '@iconify/types@2.0.0':
resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==}
@@ -10341,7 +10341,7 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@iconify/json@2.2.409': '@iconify/json@2.2.410':
dependencies: dependencies:
'@iconify/types': 2.0.0 '@iconify/types': 2.0.0
pathe: 2.0.3 pathe: 2.0.3
-83
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))
@@ -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)
@@ -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
@@ -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
@@ -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);
@@ -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
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
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)
+22 -3
View File
@@ -270,13 +270,32 @@ func (entry *Entry) getBufferPool() (pool BufferPool) {
func (entry *Entry) write() { func (entry *Entry) write() {
entry.Logger.mu.Lock() entry.Logger.mu.Lock()
defer entry.Logger.mu.Unlock() defer entry.Logger.mu.Unlock()
serialized, err := entry.Logger.Formatter.Format(entry) serialized, err := entry.Logger.Formatter.Format(entry)
if err != nil { 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 return
} }
if _, err := entry.Logger.Out.Write(serialized); err != nil { if len(serialized) > 0 {
fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err) 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)
}
} }
} }
+36 -19
View File
@@ -222,25 +222,6 @@ func TestEntryPanic(t *testing.T) {
entry.WithField("err", errBoom).Panic("kaboom") 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) { func TestEntryWithIncorrectField(t *testing.T) {
fn := func() {} 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) { func TestEntryReportCallerRace(t *testing.T) {
logger := New() logger := New()
entry := NewEntry(logger) entry := NewEntry(logger)
+14 -2
View File
@@ -26,6 +26,11 @@ func SetFormatter(formatter Formatter) {
std.SetFormatter(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 // SetReportCaller sets whether the standard logger will include the calling
// method as a field. // method as a field.
func SetReportCaller(include bool) { func SetReportCaller(include bool) {
@@ -33,22 +38,29 @@ func SetReportCaller(include bool) {
} }
// SetLevel sets the standard logger level. // SetLevel sets the standard logger level.
func SetLevel(level string) { func SetLevel(level string) (ok bool) {
level = strings.ToUpper(level) level = strings.ToUpper(level)
switch level { switch level {
case "FATAL": case "FATAL":
std.SetLevel(FatalLevel) std.SetLevel(FatalLevel)
return true
case "ERROR": case "ERROR":
std.SetLevel(ErrorLevel) std.SetLevel(ErrorLevel)
case "WARN": return true
case "WARN", "WARNING":
std.SetLevel(WarnLevel) std.SetLevel(WarnLevel)
return true
case "INFO": case "INFO":
std.SetLevel(InfoLevel) std.SetLevel(InfoLevel)
return true
case "DEBUG": case "DEBUG":
std.SetLevel(DebugLevel) std.SetLevel(DebugLevel)
return true
case "TRACE": case "TRACE":
std.SetLevel(TraceLevel) std.SetLevel(TraceLevel)
return true
default: default:
return false
} }
} }
+8 -2
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. // DaemonFormatter is the a log formatter that is suitable for daemon.
// FATAL log is also printed to stderr. // FATAL log is also printed to stderr.
type DaemonFormatter struct { type DaemonFormatter struct {
NoPrefix bool
NoTimestamp bool NoTimestamp bool
NoLevel bool
} }
func (f *DaemonFormatter) Format(entry *Entry) ([]byte, error) { func (f *DaemonFormatter) Format(entry *Entry) ([]byte, error) {
@@ -77,7 +79,9 @@ func (f *DaemonFormatter) Format(entry *Entry) ([]byte, error) {
if !f.NoTimestamp { if !f.NoTimestamp {
orderedKeys = append(orderedKeys, FieldKeyTime) orderedKeys = append(orderedKeys, FieldKeyTime)
} }
orderedKeys = append(orderedKeys, FieldKeyLevel) if !f.NoLevel {
orderedKeys = append(orderedKeys, FieldKeyLevel)
}
orderedKeys = append(orderedKeys, FieldKeyMsg) orderedKeys = append(orderedKeys, FieldKeyMsg)
if entry.HasCaller() { if entry.HasCaller() {
fileInfo = fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line) 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 = &bytes.Buffer{}
} }
buf.WriteString(LogPrefix) if !f.NoPrefix {
buf.WriteString(LogPrefix)
}
for _, key := range orderedKeys { for _, key := range orderedKeys {
var value string var value string
switch { switch {
+57
View File
@@ -103,6 +103,63 @@ func TestDaemonFormatter(t *testing.T) {
buf.Reset() 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) { func TestNilFormatter(t *testing.T) {
SetFormatter(&NilFormatter{}) SetFormatter(&NilFormatter{})
SetLevel("TRACE") SetLevel("TRACE")
+32 -27
View File
@@ -9,26 +9,22 @@ import (
"time" "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 { type Logger struct {
// The logs are `io.Copy`'d to this in a mutex. It's common to set this to a // 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 // file, or leave it default which is `os.Stderr`. You can also set this to
// something more adventurous, such as logging to Kafka. // something more adventurous, such as logging to Kafka.
Out io.Writer Out io.Writer
// All log entries pass through the formatter before logged to Out. The // All log entries pass through the formatter before logged to Out.
// 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.
Formatter Formatter 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 ReportCaller bool
// The logging level the logger should log at. This is typically (and defaults // The logging level the logger should log at. This is typically (and defaults
@@ -36,18 +32,19 @@ type Logger struct {
// logged. // logged.
Level Level Level Level
// Used to sync writing to the log. Locking is enabled by Default // Function to exit the application, defaults to `os.Exit()`.
mu MutexWrap // This can be useful in testing.
// Reusable empty entry
entryPool sync.Pool
// Function to exit the application, defaults to `os.Exit()`
ExitFunc exitFunc ExitFunc exitFunc
// The buffer pool used to format the log. If it is nil, the default global // The buffer pool used to format the log. If it is nil, the default global
// buffer pool will be used. // buffer pool will be used.
BufferPool BufferPool 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) type exitFunc func(int)
@@ -76,11 +73,12 @@ func (mw *MutexWrap) Disable() {
// Creates a new logger with default configuration. // Creates a new logger with default configuration.
func New() *Logger { func New() *Logger {
return &Logger{ return &Logger{
Out: os.Stderr, Out: os.Stderr,
Formatter: new(CliFormatter), Formatter: &CliFormatter{},
Level: InfoLevel, CallbackFormatter: &DaemonFormatter{NoTimestamp: true, NoLevel: true},
ExitFunc: os.Exit, Level: InfoLevel,
ReportCaller: false, ExitFunc: os.Exit,
ReportCaller: false,
} }
} }
@@ -263,6 +261,13 @@ func (logger *Logger) IsLevelEnabled(level Level) bool {
return logger.level() >= level 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. // SetFormatter sets the logger formatter.
func (logger *Logger) SetFormatter(formatter Formatter) { func (logger *Logger) SetFormatter(formatter Formatter) {
logger.mu.Lock() logger.mu.Lock()
@@ -270,11 +275,11 @@ func (logger *Logger) SetFormatter(formatter Formatter) {
logger.Formatter = formatter logger.Formatter = formatter
} }
// SetOutput sets the logger output. // SetCallback sets the logger callback function.
func (logger *Logger) SetOutput(output io.Writer) { func (logger *Logger) SetCallback(callback Callback) {
logger.mu.Lock() logger.mu.Lock()
defer logger.mu.Unlock() defer logger.mu.Unlock()
logger.Out = output logger.Callback = callback
} }
func (logger *Logger) SetReportCaller(reportCaller bool) { func (logger *Logger) SetReportCaller(reportCaller bool) {
@@ -37,13 +37,6 @@ const maxClientLogFiles = 25
// In Windows, it is typically C:\Users\<user>\AppData\Local\mieru // In Windows, it is typically C:\Users\<user>\AppData\Local\mieru
var cachedClientLogDir string 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. // NewClientLogFile returns a file handler for mieru client to write logs.
func NewClientLogFile() (io.WriteCloser, error) { func NewClientLogFile() (io.WriteCloser, error) {
if err := prepareClientLogDir(); err != nil { if err := prepareClientLogDir(); err != nil {
+25
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{})
}
@@ -208,13 +208,15 @@ func CreateNewConnAndDoRequest(seq int, proxyMode string) {
var client *http.Client var client *http.Client
var err error var err error
for { for {
if proxyMode == Socks5ProxyMode { switch proxyMode {
case Socks5ProxyMode:
socksDialer := socks5.DialSocks5Proxy(&socks5.Client{ socksDialer := socks5.DialSocks5Proxy(&socks5.Client{
Host: *localProxyHost + ":" + strconv.Itoa(*localProxyPort), Host: *localProxyHost + ":" + strconv.Itoa(*localProxyPort),
Timeout: 10 * time.Second,
CmdType: constant.Socks5ConnectCmd, CmdType: constant.Socks5ConnectCmd,
}) })
conn, _, _, err = socksDialer("tcp", *dstHost+":"+strconv.Itoa(*dstPort)) conn, _, _, err = socksDialer("tcp", *dstHost+":"+strconv.Itoa(*dstPort))
} else if proxyMode == HTTPProxyMode { case HTTPProxyMode:
tr := &http.Transport{ tr := &http.Transport{
Proxy: socks5.HTTPTransportProxyFunc("http://" + *localHTTPHost + ":" + strconv.Itoa(*localHTTPPort)), Proxy: socks5.HTTPTransportProxyFunc("http://" + *localHTTPHost + ":" + strconv.Itoa(*localHTTPPort)),
} }
@@ -225,16 +227,19 @@ func CreateNewConnAndDoRequest(seq int, proxyMode string) {
}, },
Timeout: 10 * time.Second, Timeout: 10 * time.Second,
} }
} else if proxyMode == NoProxyMode { case NoProxyMode:
conn, err = net.Dial("tcp", *dstHost+":"+strconv.Itoa(*dstPort)) conn, err = net.Dial("tcp", *dstHost+":"+strconv.Itoa(*dstPort))
} }
if err == nil { if err == nil {
break break
} }
if !errors.Is(err, io.EOF) { if !errors.Is(err, io.EOF) {
log.Fatalf("dial failed: %v", err) log.Fatalf("dial failed: %v", err)
} }
time.Sleep(time.Millisecond)
} }
if client != nil { if client != nil {
defer client.CloseIdleConnections() defer client.CloseIdleConnections()
DoRequestWithExistingHTTPClient(client, seq) DoRequestWithExistingHTTPClient(client, seq)
+1 -1
View File
@@ -32,7 +32,7 @@ require (
github.com/metacubex/sing-shadowsocks v0.2.12 github.com/metacubex/sing-shadowsocks v0.2.12
github.com/metacubex/sing-shadowsocks2 v0.2.7 github.com/metacubex/sing-shadowsocks2 v0.2.7
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2 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-vmess v0.2.4
github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f
github.com/metacubex/smux v0.0.0-20251111013112-03f8d12dafc1 github.com/metacubex/smux v0.0.0-20251111013112-03f8d12dafc1
+2 -2
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-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 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-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.9 h1:jY0Yyt8nnN3yQRN/jTxgqNCmGi1dsFdxdIi7pQUlVVU=
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/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 h1:Tx6AGgCiEf400E/xyDuYyafsel6sGbR8oF7RkAaus6I=
github.com/metacubex/sing-vmess v0.2.4/go.mod h1:21R5R1u90uUvBQF0owoooEu96/SAYYD56nDrwm6nFaM= 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= github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f h1:Sr/DYKYofKHKc4GF3qkRGNuj6XA6c0eqPgEDN+VAsYU=
+3 -3
View File
@@ -65,7 +65,7 @@ API Key authentication is enabled by default, automatically generated and saved
"type": "client|server", "type": "client|server",
"status": "running|stopped|error", "status": "running|stopped|error",
"url": "...", "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, "restart": true,
"meta": { "meta": {
"peer": { "peer": {
@@ -1116,7 +1116,7 @@ The instance object in API responses contains the following fields:
"type": "server", // Instance type: server or client "type": "server", // Instance type: server or client
"status": "running", // Instance status: running, stopped, or error "status": "running", // Instance status: running, stopped, or error
"url": "server://...", // Instance configuration URL "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 "restart": true, // Auto-restart policy
"meta": { // Metadata for organization and peer tracking "meta": { // Metadata for organization and peer tracking
"peer": { "peer": {
@@ -1581,7 +1581,7 @@ Examples:
| `tls` | TLS encryption level | `0`(none), `1`(self-signed), `2`(certificate) | `0` | Server only | | `tls` | TLS encryption level | `0`(none), `1`(self-signed), `2`(certificate) | `0` | Server only |
| `crt` | Certificate path | File path | None | Server only | | `crt` | Certificate path | File path | None | Server only |
| `key` | Private key 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 | | `min` | Minimum pool capacity | Integer > 0 | `64` | Client dual-end handshake mode only |
| `max` | Maximum pool capacity | Integer > 0 | `1024` | Dual-end handshake mode | | `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 | | `mode` | Runtime mode control | `0`(auto), `1`(force mode 1), `2`(force mode 2) | `0` | Both |
+32 -64
View File
@@ -109,63 +109,59 @@ nodepass "server://0.0.0.0:10101/remote.example.com:8080?mode=2"
## DNS Resolution Configuration ## 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) - `dns`: DNS cache TTL duration (default: 5m)
- Comma-separated list of IP addresses (IPv4 or IPv6) - Specifies how long resolved hostnames are cached before re-querying
- Multiple DNS servers provide automatic failover and round-robin load balancing - Accepts time duration format: `1h`, `30m`, `15s`, `500ms`, etc.
- Invalid IP addresses in the list are logged and skipped - Longer TTL reduces DNS query overhead but may cache stale records
- If all provided DNS servers fail, the system falls back to OS default DNS resolution - Shorter TTL ensures fresher DNS data but increases query frequency
- DNS resolution uses a dedicated port (UDP port 53) for all queries - Set to `0` to disable caching (always query DNS on every connection)
- Applies to both client and server modes for resolving all hostnames - Applies to both client and server modes for resolving all hostnames
**DNS Resolver Features:** **DNS Cache Features:**
- **Intelligent Caching**: Resolved hostnames are cached with configurable TTL to reduce DNS query overhead - **System Integration**: Uses operating system's native DNS resolver for maximum compatibility
- **Background Refresh**: Cached entries are proactively refreshed before expiration (at 80% of TTL) to prevent lookup delays - **Intelligent Caching**: Resolved hostnames are cached with configurable TTL to reduce query overhead
- **Automatic Failover**: When a DNS server fails, automatically tries the next server in the list - **Automatic Expiration**: Cached entries are automatically removed after TTL expires
- **Round-Robin Distribution**: DNS queries are distributed across configured servers for load balancing
- **IP Address Bypass**: Direct IP addresses skip DNS resolution for maximum efficiency - **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: Example:
```bash ```bash
# Use Cloudflare and Google DNS servers (default behavior) # Use default 5-minute cache TTL
nodepass "server://0.0.0.0:10101/example.com:8080?dns=1.1.1.1,8.8.8.8" nodepass "server://0.0.0.0:10101/example.com:8080"
# Use custom DNS servers (e.g., corporate DNS) # Set 1-hour cache TTL for stable domains
nodepass "server://0.0.0.0:10101/internal.example.com:8080?dns=10.0.0.53,10.0.1.53" nodepass "server://0.0.0.0:10101/internal.example.com:8080?dns=1h"
# Client with specific DNS configuration # Set 30-second cache TTL for dynamic DNS
nodepass "client://server.example.com:10101/database.local:3306?dns=192.168.1.1,192.168.1.2" nodepass "client://server.example.com:10101/database.local:3306?dns=30s"
# IPv6 DNS servers # Disable DNS caching entirely (query on every connection)
nodepass "server://0.0.0.0:10101/service.example.com:8080?dns=2606:4700:4700::1111,2001:4860:4860::8888" nodepass "server://0.0.0.0:10101/service.example.com:8080?dns=0"
# Combined with other parameters # 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:** **DNS Configuration Use Cases:**
- **Corporate Networks**: Use internal DNS servers to resolve private hostnames - **Corporate Networks**: Use longer TTL (e.g., 1h) for stable internal hostnames
- **Geographic Optimization**: Select DNS servers geographically close to your deployment - **Dynamic DNS**: Use shorter TTL (e.g., 30s) for frequently changing DNS records
- **Privacy Enhancement**: Use privacy-focused DNS providers (e.g., 1.1.1.1, 9.9.9.9) - **High Availability**: Longer TTL reduces DNS server load and improves reliability
- **Reliability**: Configure multiple DNS servers for high availability - **Load Balancing**: Shorter TTL enables faster failover for load-balanced services
- **Compliance**: Meet regulatory requirements for DNS provider selection - **Performance**: Longer TTL reduces connection latency by minimizing DNS queries
- **Testing**: Use specific DNS servers for development or staging environments
- **Performance**: Reduce DNS lookup latency with closer or faster DNS servers
**DNS Caching Behavior:** **DNS Caching Behavior:**
- Cache TTL is controlled by `NP_DNS_CACHING_TTL` environment variable (default: 5 minutes) - Cache TTL is configurable via the `dns` query parameter (default: 5 minutes)
- Background refresh occurs at 80% of TTL to maintain fresh data without delays
- Expired entries are removed and fresh lookups performed on next access - Expired entries are removed and fresh lookups performed on next access
- Cache is per-instance and not shared between NodePass processes - Cache is per-instance and not shared between NodePass processes
- IP addresses are never cached (direct use, no DNS lookup needed) - IP addresses are never cached (direct use, no DNS lookup needed)
- System DNS resolver is used for all hostname lookups
**Important Notes:** **Important Notes:**
- DNS servers must be specified as IP addresses, not hostnames - Both IPv4 and IPv6 addresses are supported
- Both IPv4 and IPv6 DNS server addresses are supported - DNS resolution timeout is controlled by the operating system
- DNS resolution timeout is fixed at 5 seconds per query
- Failed DNS servers are retried in subsequent queries (no permanent blacklisting)
- When using target address groups, each address is resolved independently - When using target address groups, each address is resolved independently
- DNS resolution applies to both tunnel addresses and target addresses - DNS resolution applies to both tunnel addresses and target addresses
- Tunnel address DNS resolution occurs once at startup - 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 | | `tls` | TLS encryption mode | `0` | O | X | O |
| `crt` | Custom certificate path | N/A | O | X | O | | `crt` | Custom certificate path | N/A | O | X | O |
| `key` | Custom key 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 | | `min` | Minimum pool capacity | `64` | X | O | X |
| `max` | Maximum pool capacity | `1024` | O | X | X | | `max` | Maximum pool capacity | `1024` | O | X | X |
| `mode` | Run mode control | `0` | O | O | 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_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_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_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_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_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` | | `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 - Too large: Increased memory usage
- Recommended range: 1000-5000 - 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 ### UDP Settings
For applications relying heavily on UDP traffic: For applications relying heavily on UDP traffic:
+66 -86
View File
@@ -263,113 +263,93 @@ This setup:
- **Load Distribution**: Distributing outbound traffic across multiple network links - **Load Distribution**: Distributing outbound traffic across multiple network links
- **Network Testing**: Simulating traffic from specific network locations - **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 ```bash
# Server side: Use corporate DNS servers for internal service resolution # Server side: 1-hour cache TTL for stable internal hostnames
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" 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 # Client side: Same TTL for consistent behavior
nodepass "client://tunnel.corp.local:10101/127.0.0.1:8080?dns=10.0.0.53,10.0.1.53" nodepass "client://tunnel.corp.local:10101/127.0.0.1:8080?dns=1h"
``` ```
This configuration: This configuration:
- Uses corporate DNS servers (10.0.0.53 and 10.0.1.53) instead of public DNS - Uses 1-hour DNS cache TTL for stable internal services
- Resolves internal .corp.local domains that are not accessible via public DNS - Reduces DNS query overhead in corporate networks
- Automatic failover to second DNS server if first fails - Improves connection performance by minimizing DNS lookups
- DNS results are cached for 5 minutes (default) to improve performance - Suitable for production environments with stable DNS
- Both server and client use consistent DNS configuration
### 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 ```bash
# Server side: Use Cloudflare DNS (privacy-focused) # Server side: 30-second cache TTL for dynamic 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" nodepass "server://0.0.0.0:10101/dynamic.example.com:8080?dns=30s&tls=1&log=info"
# Client side: Use Quad9 DNS (malware blocking) # Client side: Short TTL for load balancing scenarios
nodepass "client://server.example.com:10101/127.0.0.1:8080?dns=9.9.9.9,149.112.112.112" nodepass "client://server.example.com:10101/127.0.0.1:8080?dns=30s"
``` ```
This setup: This setup:
- Server uses Cloudflare DNS (1.1.1.1) known for privacy protection - Uses 30-second DNS cache TTL for dynamic environments
- Client uses Quad9 DNS (9.9.9.9) with built-in malware domain blocking - Enables faster failover for load-balanced services
- Multiple DNS servers provide redundancy - Ensures connections use current DNS records
- All DNS queries are encrypted via DNS-over-TLS when supported by resolvers - 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 ```bash
# Asia-Pacific region: Use Google DNS Asia servers # Development server: No DNS caching for immediate updates
nodepass "server://0.0.0.0:10101/service.example.com:8080?dns=8.8.8.8,8.8.4.4&mode=2" nodepass "server://0.0.0.0:10101/dev.backend.local:8080?dns=0&tls=0&log=debug"
# Europe: Use Cloudflare DNS # Testing client: No caching to see DNS changes immediately
nodepass "server://0.0.0.0:10101/service.example.com:8080?dns=1.1.1.1,1.0.0.1&mode=2" nodepass "client://dev-server.local:10101/127.0.0.1:8080?dns=0&log=debug"
# 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"
``` ```
This configuration: This configuration:
- Reduces DNS lookup latency by using geographically proximate servers - Disables DNS caching (dns=0) for immediate updates
- Improves initial connection establishment time - Every connection performs fresh DNS lookup
- Works with any DNS server accessible via UDP port 53 - 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 ```bash
# Server side: Use IPv6 DNS servers (Cloudflare and Google) # Production API: 10-minute cache for balanced performance
nodepass "server://[::]:10101/ipv6.example.com:8080?dns=2606:4700:4700::1111,2001:4860:4860::8888&mode=2&tls=1" 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 # Staging environment: 2-minute cache for faster updates
nodepass "client://server.example.com:10101/127.0.0.1:8080?dns=1.1.1.1,2606:4700:4700::1111" 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: This setup:
- Server listens on all IPv6 interfaces using [::] - Production uses 10-minute TTL for good performance
- Uses IPv6 DNS servers for native IPv6 resolution - Staging uses 2-minute TTL for faster DNS updates
- Mixed IPv4/IPv6 DNS configuration provides maximum compatibility - Client uses default 5-minute TTL
- Automatically selects IPv4 or IPv6 addresses based on connection type - Each environment optimized for its use case
### Example 19: DNS Caching Tuning for Different Environments **DNS Cache TTL Use Cases**:
- **Corporate Networks**: Long TTL (1h) for stable internal hostnames
Adjust DNS cache TTL for optimal performance: - **Dynamic DNS**: Short TTL (30s-1m) for frequently changing records
- **Load Balancing**: Short TTL enables faster failover
```bash - **Performance**: Longer TTL reduces connection latency
# Production: Stable DNS, longer cache TTL (30 minutes) - **High Availability**: Moderate TTL balances freshness and performance
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
## High Availability and Load Balancing ## 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: Use target address groups for even traffic distribution and automatic failover:
@@ -387,7 +367,7 @@ This configuration:
- Automatically resumes sending traffic to recovered servers - Automatically resumes sending traffic to recovered servers
- Uses TLS encryption to secure the tunnel - 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: Configure primary and replica database instances for high availability access:
@@ -402,7 +382,7 @@ This setup:
- Application requires no modification for transparent failover - Application requires no modification for transparent failover
- Logs only warnings and errors to reduce output - 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: 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 - Client limits bandwidth to 100 Mbps with maximum 2000 concurrent connections
- Single instance failure doesn't affect overall service availability - 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: Configure multi-region service nodes to optimize network latency:
@@ -444,7 +424,7 @@ This setup:
## PROXY Protocol Integration ## 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: 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 - Compatible with HAProxy, Nginx, and other PROXY protocol aware services
- Useful for maintaining accurate access logs and IP-based access controls - 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: Enable web applications behind NodePass to receive original client information:
@@ -487,7 +467,7 @@ This setup:
- Supports compliance requirements for connection auditing - Supports compliance requirements for connection auditing
- Works with web servers that support PROXY protocol (Nginx, HAProxy, etc.) - 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: Maintain client IP information for database access logging and security:
@@ -514,7 +494,7 @@ Benefits:
## Container Deployment ## Container Deployment
### Example 27: Containerized NodePass ### Example 26: Containerized NodePass
Deploy NodePass in a Docker environment: Deploy NodePass in a Docker environment:
@@ -549,7 +529,7 @@ This configuration:
## Master API Management ## Master API Management
### Example 28: Centralized Management ### Example 27: Centralized Management
Set up a central controller for multiple NodePass instances: Set up a central controller for multiple NodePass instances:
@@ -586,7 +566,7 @@ This setup:
- Offers a RESTful API for automation and integration - Offers a RESTful API for automation and integration
- Includes a built-in Swagger UI at http://localhost:9090/api/v1/docs - 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: Use a custom API prefix for the master mode:
@@ -605,7 +585,7 @@ This allows:
- Custom URL paths for security or organizational purposes - Custom URL paths for security or organizational purposes
- Swagger UI access at http://localhost:9090/admin/v1/docs - 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: Monitor instance connection counts and traffic statistics through the master API:
@@ -645,7 +625,7 @@ This monitoring setup provides:
## QUIC Transport Protocol ## 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: 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 - Improved connection establishment with 0-RTT support
- Client automatically receives QUIC configuration from server during handshake - 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: Deploy QUIC tunnel with verified certificates for production:
@@ -684,7 +664,7 @@ This setup:
- Full certificate validation on client side - Full certificate validation on client side
- QUIC configuration automatically delivered from server - 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: Optimize for mobile networks or satellite connections:
@@ -704,7 +684,7 @@ This configuration:
- 0-RTT reconnection for faster recovery after network changes - 0-RTT reconnection for faster recovery after network changes
- Client automatically adopts QUIC from server - 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: 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 - Improved NAT traversal capabilities
- Single UDP socket reduces resource usage - 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: Configure QUIC tunnel for gaming, VoIP, or video streaming:
-5
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 - **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 - **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 ## 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: NodePass establishes a bidirectional data flow through its tunnel architecture, supporting both TCP and UDP protocols. The system supports three data flow modes:
+15 -15
View File
@@ -245,15 +245,15 @@ This guide helps you diagnose and resolve common issues you might encounter when
**Solutions**: **Solutions**:
1. **Verify DNS Configuration** 1. **Verify System DNS Configuration**
- Test DNS server reachability: `ping 1.1.1.1` - Verify resolution works: `nslookup example.com`
- Verify resolution works: `nslookup example.com 8.8.8.8` - Check system's DNS settings (NodePass uses system resolver)
- Ensure `dns` parameter contains valid IP addresses - Ensure network connectivity is working
2. **Network Connectivity** 2. **Network Connectivity**
- Check if firewall blocks UDP port 53 - 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` - Verify domain reachability
- Use internal DNS for corporate networks: `dns=10.0.0.1,8.8.8.8` - Test with alternative domains to isolate issue
### DNS Caching Problems ### DNS Caching Problems
@@ -262,12 +262,11 @@ This guide helps you diagnose and resolve common issues you might encounter when
**Solutions**: **Solutions**:
1. **Adjust Cache TTL** (default 5 minutes) 1. **Adjust Cache TTL** (default 5 minutes)
- Dynamic environments: `export NP_DNS_CACHING_TTL=1m` - Dynamic environments: `dns=1m`
- Stable environments: `export NP_DNS_CACHING_TTL=30m` - Stable environments: `dns=30m`
- Restart NodePass after critical changes
2. **Load Balancing Scenarios** 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 - Or use IP addresses directly to bypass DNS caching
### DNS Performance Optimization ### DNS Performance Optimization
@@ -276,14 +275,15 @@ This guide helps you diagnose and resolve common issues you might encounter when
**Solutions**: **Solutions**:
1. **Choose Appropriate DNS Servers** 1. **Optimize Cache TTL**
- China: `dns=223.5.5.5,119.29.29.29` - Increase TTL for stable environments: `dns=1h`
- International: `dns=1.1.1.1,8.8.8.8` - Reduce TTL for dynamic environments: `dns=1m`
- Use at least 2 DNS servers for redundancy - Balance between freshness and performance
2. **Reduce DNS Queries** 2. **Reduce DNS Queries**
- Use IP addresses directly for performance-critical scenarios - 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 ## Master API Issues
+6 -6
View File
@@ -7,7 +7,7 @@ NodePass creates tunnels with an unencrypted TCP control channel and configurabl
The general syntax for NodePass commands is: The general syntax for NodePass commands is:
```bash ```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: Where:
@@ -19,7 +19,7 @@ Where:
Common query parameters: Common query parameters:
- `log=<level>`: Log verbosity level (`none`, `debug`, `info`, `warn`, `error`, or `event`) - `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) - `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) - `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 - `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. Server mode establishes tunnel control channels and supports bidirectional data flow forwarding.
```bash ```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 #### 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) - `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) - `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) - `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) - `quic`: QUIC transport mode (0, 1)
- `0`: Use TCP-based connection pool (default) - `0`: Use TCP-based connection pool (default)
- `1`: Use QUIC-based UDP connection pool with stream multiplexing - `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. Client mode connects to a NodePass server and supports bidirectional data flow forwarding.
```bash ```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 #### 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) - `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) - `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) - `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) - `min`: Minimum connection pool capacity (default: 64)
- `mode`: Run mode control for client behavior - `mode`: Run mode control for client behavior
- `0`: Automatic detection (default) - attempts local binding first, falls back to handshake mode - `0`: Automatic detection (default) - attempts local binding first, falls back to handshake mode
+3 -3
View File
@@ -65,7 +65,7 @@ API Key 认证默认启用,首次启动自动生成并保存在 `nodepass.gob`
"type": "client|server", "type": "client|server",
"status": "running|stopped|error", "status": "running|stopped|error",
"url": "...", "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, "restart": true,
"meta": { "meta": {
"peer": { "peer": {
@@ -1116,7 +1116,7 @@ API响应中的实例对象包含以下字段:
"type": "server", // 实例类型:server 或 client "type": "server", // 实例类型:server 或 client
"status": "running", // 实例状态:running、stopped 或 error "status": "running", // 实例状态:running、stopped 或 error
"url": "server://...", // 实例配置URL "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, // 自启动策略 "restart": true, // 自启动策略
"meta": { // 用于组织和对端跟踪的元数据 "meta": { // 用于组织和对端跟踪的元数据
"peer": { "peer": {
@@ -1581,7 +1581,7 @@ client://<server_host>:<server_port>/<local_host>:<local_port>?<parameters>
| `tls` | TLS加密级别 | `0`(无), `1`(自签名), `2`(证书) | `0` | 仅服务端 | | `tls` | TLS加密级别 | `0`(无), `1`(自签名), `2`(证书) | `0` | 仅服务端 |
| `crt` | 证书路径 | 文件路径 | 无 | 仅服务端 | | `crt` | 证书路径 | 文件路径 | 无 | 仅服务端 |
| `key` | 私钥路径 | 文件路径 | 无 | 仅服务端 | | `key` | 私钥路径 | 文件路径 | 无 | 仅服务端 |
| `dns` | 自定义DNS服务器 | IP地址,逗号分隔 | `1.1.1.1,8.8.8.8` | 两者 | | `dns` | DNS缓存时间 | 时间长度 (如 `10m`, `30s`, `1h`) | `5m` | 两者 |
| `min` | 最小连接池容量 | 整数 > 0 | `64` | 仅客户端双端握手模式 | | `min` | 最小连接池容量 | 整数 > 0 | `64` | 仅客户端双端握手模式 |
| `max` | 最大连接池容量 | 整数 > 0 | `1024` | 双端握手模式 | | `max` | 最大连接池容量 | 整数 > 0 | `1024` | 双端握手模式 |
| `mode` | 运行模式控制 | `0`(自动), `1`(强制模式1), `2`(强制模式2) | `0` | 两者 | | `mode` | 运行模式控制 | `0`(自动), `1`(强制模式1), `2`(强制模式2) | `0` | 两者 |
+32 -64
View File
@@ -109,63 +109,59 @@ nodepass "server://0.0.0.0:10101/remote.example.com:8080?mode=2"
## DNS解析配置 ## DNS解析配置
NodePass支持自定义DNS服务器配置,具有智能缓存功能,可提高性能和可靠性。内置DNS解析器提供后台刷新、自动故障转移和可配置的TTL管理 NodePass使用系统内置的DNS解析器,具有智能缓存功能,可提高性能和可靠性。DNS缓存减少了查询开销并防止解析延迟
- `dns`自定义DNS服务器地址(默认:1.1.1.1,8.8.8.8 - `dns`DNS缓存TTL持续时间(默认:5m
- 以逗号分隔的IP地址列表(IPv4或IPv6) - 指定已解析的主机名在重新查询前缓存多长时间
- 多个DNS服务器提供自动故障转移和轮询负载均衡 - 接受时间持续时间格式:`1h``30m``15s``500ms`
- 列表中的无效IP地址会被记录并跳过 - 较长的TTL减少DNS查询开销,但可能缓存过时记录
- 如果所有提供的DNS服务器都失败,系统会回退到操作系统默认DNS解析 - 较短的TTL确保更新的DNS数据,但增加查询频率
- DNS解析使用专用端口(UDP端口53)进行所有查询 - 设置为`0`以禁用缓存(每次连接都查询DNS
- 适用于客户端和服务端模式,用于解析所有主机名 - 适用于客户端和服务端模式,用于解析所有主机名
**DNS解析器特性:** **DNS缓存特性:**
- **智能缓存**已解析的主机名会使用可配置的TTL进行缓存,以减少DNS查询开销 - **系统集成**使用操作系统的原生DNS解析器以实现最大兼容性
- **后台刷新**缓存条目在过期前(TTL的80%时)会主动刷新,以防止查找延迟 - **智能缓存**已解析的主机名会使用可配置的TTL进行缓存,以减少查询开销
- **自动故障转移**当DNS服务器失败时,自动尝试列表中的下一个服务器 - **自动过期**缓存条目在TTL过期后自动删除
- **轮询分发**:DNS查询在配置的服务器之间分发以实现负载均衡
- **IP地址绕过**:直接IP地址跳过DNS解析以获得最高效率 - **IP地址绕过**:直接IP地址跳过DNS解析以获得最高效率
- **协议感知**根据连接要求自动选择IPv4IPv6地址 - **协议感知**自动处理IPv4IPv6地址
- **线程安全**:并发DNS查找被安全地缓存并在连接之间共享
示例: 示例:
```bash ```bash
# 使用Cloudflare和Google DNS服务器(默认行为) # 使用默认5分钟缓存TTL
nodepass "server://0.0.0.0:10101/example.com:8080?dns=1.1.1.1,8.8.8.8" nodepass "server://0.0.0.0:10101/example.com:8080"
# 使用自定义DNS服务器(例如企业DNS) # 为稳定域名设置1小时缓存TTL
nodepass "server://0.0.0.0:10101/internal.example.com:8080?dns=10.0.0.53,10.0.1.53" nodepass "server://0.0.0.0:10101/internal.example.com:8080?dns=1h"
# 带有特定DNS配置的客户端 # 为动态DNS设置30秒缓存TTL
nodepass "client://server.example.com:10101/database.local:3306?dns=192.168.1.1,192.168.1.2" nodepass "client://server.example.com:10101/database.local:3306?dns=30s"
# IPv6 DNS服务器 # 完全禁用DNS缓存(每次连接都查询)
nodepass "server://0.0.0.0:10101/service.example.com:8080?dns=2606:4700:4700::1111,2001:4860:4860::8888" 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服务器解析私有主机名 - **企业网络**对稳定的内部主机名使用较长的TTL(例如1h)
- **地理优化**选择地理位置接近部署的DNS服务器 - **动态DNS**对频繁变化的DNS记录使用较短的TTL(例如30s)
- **隐私增强**使用注重隐私的DNS提供商(例如1.1.1.1、9.9.9.9 - **高可用性**较长的TTL减少DNS服务器负载并提高可靠性
- **可靠性**配置多个DNS服务器以实现高可用性 - **负载均衡**较短的TTL使负载均衡服务能更快切换
- **合规**满足DNS提供商选择的监管要求 - **性**较长的TTL通过减少DNS查询来降低连接延迟
- **测试**:为开发或暂存环境使用特定DNS服务器
- **性能**:使用更近或更快的DNS服务器减少DNS查找延迟
**DNS缓存行为:** **DNS缓存行为:**
- 缓存TTL`NP_DNS_CACHING_TTL`环境变量控制(默认:5分钟) - 缓存TTL通过`dns`查询参数配置(默认:5分钟)
- 后台刷新在TTL的80%时发生,以在不延迟的情况下保持新鲜数据
- 过期条目被删除,下次访问时执行新的查找 - 过期条目被删除,下次访问时执行新的查找
- 缓存是每个实例的,不在NodePass进程之间共享 - 缓存是每个实例的,不在NodePass进程之间共享
- IP地址永远不会被缓存(直接使用,不需要DNS查找) - IP地址永远不会被缓存(直接使用,不需要DNS查找)
- 系统DNS解析器用于所有主机名查找
**重要说明:** **重要说明:**
- DNS服务器必须指定为IP地址,而不是主机名 - 支持IPv4和IPv6地址
- 支持IPv4和IPv6 DNS服务器地址 - DNS解析超时由操作系统控制
- DNS解析超时固定为每次查询5秒
- 失败的DNS服务器在后续查询中会重试(没有永久黑名单)
- 使用目标地址组时,每个地址独立解析 - 使用目标地址组时,每个地址独立解析
- DNS解析适用于隧道地址和目标地址 - DNS解析适用于隧道地址和目标地址
- 隧道地址DNS解析在启动时发生一次 - 隧道地址DNS解析在启动时发生一次
@@ -571,7 +567,7 @@ NodePass支持通过URL查询参数进行灵活配置,不同参数在 server、c
| `tls` | TLS加密模式 | `0` | O | X | O | | `tls` | TLS加密模式 | `0` | O | X | O |
| `crt` | 自定义证书路径 | N/A | O | X | O | | `crt` | 自定义证书路径 | N/A | O | X | O |
| `key` | 自定义密钥路径 | 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 | | `min` | 最小连接池容量 | `64` | X | O | X |
| `max` | 最大连接池容量 | `1024` | O | X | X | | `max` | 最大连接池容量 | `1024` | O | X | X |
| `mode` | 运行模式控制 | `0` | O | O | X | | `mode` | 运行模式控制 | `0` | O | O | X |
@@ -606,7 +602,6 @@ NodePass支持通过URL查询参数进行灵活配置,不同参数在 server、c
| `NP_SEMAPHORE_LIMIT` | 信号缓冲区大小 | 65536 | `export NP_SEMAPHORE_LIMIT=2048` | | `NP_SEMAPHORE_LIMIT` | 信号缓冲区大小 | 65536 | `export NP_SEMAPHORE_LIMIT=2048` |
| `NP_TCP_DATA_BUF_SIZE` | TCP数据传输缓冲区大小 | 16384 | `export NP_TCP_DATA_BUF_SIZE=65536` | | `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_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_HANDSHAKE_TIMEOUT` | 握手操作超时 | 5s | `export NP_HANDSHAKE_TIMEOUT=30s` |
| `NP_UDP_READ_TIMEOUT` | UDP读取操作超时 | 30s | `export NP_UDP_READ_TIMEOUT=60s` | | `NP_UDP_READ_TIMEOUT` | UDP读取操作超时 | 30s | `export NP_UDP_READ_TIMEOUT=60s` |
| `NP_TCP_DIAL_TIMEOUT` | TCP连接建立超时 | 5s | `export NP_TCP_DIAL_TIMEOUT=60s` | | `NP_TCP_DIAL_TIMEOUT` | TCP连接建立超时 | 5s | `export NP_TCP_DIAL_TIMEOUT=60s` |
@@ -652,33 +647,6 @@ NodePass支持通过URL查询参数进行灵活配置,不同参数在 server、c
- 太大:内存使用增加 - 太大:内存使用增加
- 推荐范围:1000-5000 - 推荐范围:1000-5000
### DNS解析调优
对于频繁进行主机名查找或动态DNS场景的应用:
- `NP_DNS_CACHING_TTL`DNS缓存保质期
- 控制已解析DNS条目在缓存中保留的时长
- 默认值(5m)在大多数场景下平衡了新鲜度和性能
- 对于稳定的DNS环境,增加此值以减少查询开销(例如15m、30m、1h)
- 对于需要新鲜查找的动态DNS环境,减小此值(例如1m、2m)
- 后台刷新在TTL的80%时进行,确保平滑过渡而不会出现查找延迟
**DNS缓存最佳实践:**
- **静态基础设施**:对于稳定的生产环境使用较长TTL(15m-1h)
- **动态DNS**:对于频繁变化的主机名使用较短TTL(1m-5m)
- **开发环境**:对于快速迭代和测试使用较短TTL(1m-2m)
- **高流量服务**:在新鲜度和减少DNS服务器负载之间取得平衡
- **地理分布**:设置TTL时考虑DNS传播延迟
DNS调优示例:
```bash
# 稳定生产环境的长TTL
export NP_DNS_CACHING_TTL=30m
# 动态开发环境的短TTL
export NP_DNS_CACHING_TTL=2m
```
### UDP设置 ### UDP设置
对于严重依赖UDP流量的应用: 对于严重依赖UDP流量的应用:
+66 -86
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 ```bash
# 服务端:使用企业DNS服务器解析内部服务 # 服务端:为稳定的内部主机名使用1小时缓存TTL
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" nodepass "server://0.0.0.0:10101/internal-api.corp.local:8080?dns=1h&mode=2&tls=1"
# 客户端:使用相同的DNS服务器以保持一致的解析 # 客户端:使用相同的TTL以保持一致行为
nodepass "client://tunnel.corp.local:10101/127.0.0.1:8080?dns=10.0.0.53,10.0.1.53" nodepass "client://tunnel.corp.local:10101/127.0.0.1:8080?dns=1h"
``` ```
此配置: 此配置:
- 使用企业DNS服务器(10.0.0.53和10.0.1.53)而不是公共DNS - 为稳定的内部服务使用1小时DNS缓存TTL
- 解析无法通过公共DNS访问的内部.corp.local域 - 减少企业网络中的DNS查询开销
- 如果第一个DNS服务器失败,自动故障转移到第二个 - 通过最小化DNS查找提高连接性能
- DNS结果缓存5分钟(默认)以提高性能 - 适用于DNS稳定的生产环境
- 服务端和客户端使用一致的DNS配置
### 示例16注重隐私的DNS提供商 ### 示例16动态DNS环境
使用增强隐私的DNS提供商以提高安全性 为频繁变化的DNS记录使用较短的TTL
```bash ```bash
# 服务端:使用Cloudflare DNS(注重隐私) # 服务端:为动态DNS使用30秒缓存TTL
nodepass "server://0.0.0.0:10101/api.example.com:443?dns=1.1.1.1,1.0.0.1&tls=1&log=info" nodepass "server://0.0.0.0:10101/dynamic.example.com:8080?dns=30s&tls=1&log=info"
# 客户端:使用Quad9 DNS(恶意软件拦截) # 客户端:为负载均衡场景使用短TTL
nodepass "client://server.example.com:10101/127.0.0.1:8080?dns=9.9.9.9,149.112.112.112" nodepass "client://server.example.com:10101/127.0.0.1:8080?dns=30s"
``` ```
此设置: 此设置:
- 服务端使用Cloudflare DNS1.1.1.1),以隐私保护著称 - 为动态环境使用30秒DNS缓存TTL
- 客户端使用Quad9 DNS(9.9.9.9),内置恶意域名拦截 - 为负载均衡服务实现更快的故障转移
- 多个DNS服务器提供冗余 - 确保连接使用当前的DNS记录
- 当解析器支持时,所有DNS查询通过DNS-over-TLS加密 - 适合IP频繁变化的云环境
### 示例17地理DNS优化 ### 示例17开发和测试
选择地理位置接近部署的DNS服务器以降低延迟 为开发环境禁用缓存
```bash ```bash
# 亚太地区:使用Google DNS亚洲服务器 # 开发服务器:不使用DNS缓存以立即更新
nodepass "server://0.0.0.0:10101/service.example.com:8080?dns=8.8.8.8,8.8.4.4&mode=2" nodepass "server://0.0.0.0:10101/dev.backend.local:8080?dns=0&tls=0&log=debug"
# 欧洲:使用Cloudflare DNS # 测试客户端:不使用缓存以立即查看DNS更改
nodepass "server://0.0.0.0:10101/service.example.com:8080?dns=1.1.1.1,1.0.0.1&mode=2" nodepass "client://dev-server.local:10101/127.0.0.1:8080?dns=0&log=debug"
# 自定义区域:使用本地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查找延迟 - 禁用DNS缓存(dns=0)以立即更新
- 改善初始连接建立时间 - 每次连接都执行新的DNS查找
- 适用于通过UDP端口53可访问的任何DNS服务器 - 在开发期间DNS记录频繁变化时很有用
- 帮助在测试期间识别DNS相关问题
### 示例18IPv6 DNS配置 ### 示例18混合环境的自定义TTL
为启用IPv6的网络配置IPv6 DNS服务器 使用适中的TTL平衡性能和新鲜度
```bash ```bash
# 服务端:使用IPv6 DNS服务器(Cloudflare和Google # 生产API10分钟缓存以平衡性能
nodepass "server://[::]:10101/ipv6.example.com:8080?dns=2606:4700:4700::1111,2001:4860:4860::8888&mode=2&tls=1" nodepass "server://0.0.0.0:10101/api.example.com:8080?dns=10m&tls=1&mode=2"
# 客户端:混合IPv4和IPv6 DNS服务器 # 暂存环境:2分钟缓存以更快更新
nodepass "client://server.example.com:10101/127.0.0.1:8080?dns=1.1.1.1,2606:4700:4700::1111" 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接口 - 生产环境使用10分钟TTL以获得良好性能
- 使用IPv6 DNS服务器进行原生IPv6解析 - 暂存环境使用2分钟TTL以更快地更新DNS
- 混合IPv4/IPv6 DNS配置提供最大兼容性 - 客户端使用默认5分钟TTL
- 根据连接类型自动选择IPv4或IPv6地址 - 每个环境针对其使用场景进行优化
### 示例19:不同环境的DNS缓存调优 **DNS缓存TTL使用场景**
- **企业网络**:为稳定的内部主机名使用长TTL(1h)
调整DNS缓存TTL以获得最佳性能: - **动态DNS**:为频繁变化的记录使用短TTL(30s-1m)
- **负载均衡**:短TTL实现更快的故障转移
```bash - **性能优化**:较长的TTL降低连接延迟
# 生产环境:稳定DNS,较长缓存TTL(30分钟) - **高可用性**:适中的TTL平衡新鲜度和性能
export NP_DNS_CACHING_TTL=30m
nodepass "server://0.0.0.0:10101/stable.backend.com:8080?dns=1.1.1.1,8.8.8.8&tls=1"
# 开发环境:动态DNS,较短缓存TTL(1分钟)
export NP_DNS_CACHING_TTL=1m
nodepass "server://0.0.0.0:10101/dev.backend.local:8080?dns=10.0.0.53&tls=0&log=debug"
```
此配置:
- **生产环境(30m TTL**:为稳定环境最大化缓存效率
- **开发环境(1m TTL**:为快速变化的配置提供快速DNS更新
- 在TTL的80%时进行后台刷新确保平滑过渡
**DNS配置使用场景**
- **企业网络**:通过企业DNS基础设施解析内部主机名
- **隐私增强**:使用注重隐私的DNS提供商(1.1.1.1、9.9.9.9
- **地理优化**:使用区域接近的DNS服务器减少延迟
- **开发/测试**:在动态环境中使用短TTL快速更新DNS
- **高可用性**:多个DNS服务器提供冗余和故障转移
- **合规性**:满足DNS提供商选择的监管要求
## 高可用性与负载均衡 ## 高可用性与负载均衡
### 示例20:多后端服务器负载均衡 ### 示例19:多后端服务器负载均衡
使用目标地址组实现流量均衡分配和自动故障转移: 使用目标地址组实现流量均衡分配和自动故障转移:
@@ -387,7 +367,7 @@ nodepass "client://server.example.com:10101/127.0.0.1:8080?log=info"
- 故障服务器恢复后自动重新接入流量 - 故障服务器恢复后自动重新接入流量
- 使用TLS加密确保隧道安全 - 使用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网关配置多个后端服务实例: 为API网关配置多个后端服务实例:
@@ -420,7 +400,7 @@ nodepass "client://apigateway.example.com:10101/127.0.0.1:8080?rate=100&slot=200
- 客户端限制带宽100 Mbps,最大2000并发连接 - 客户端限制带宽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协议集成 ## PROXY协议集成
### 示例24:负载均衡器与PROXY协议集成 ### 示例23:负载均衡器与PROXY协议集成
启用PROXY协议支持,与负载均衡器和反向代理集成: 启用PROXY协议支持,与负载均衡器和反向代理集成:
@@ -463,7 +443,7 @@ nodepass "client://tunnel.example.com:10101/127.0.0.1:3000?log=info&proxy=1"
- 兼容HAProxy、Nginx和其他支持PROXY协议的服务 - 兼容HAProxy、Nginx和其他支持PROXY协议的服务
- 有助于维护准确的访问日志和基于IP的访问控制 - 有助于维护准确的访问日志和基于IP的访问控制
### 示例25Web应用的反向代理支持 ### 示例24Web应用的反向代理支持
使NodePass后的Web应用能够接收原始客户端信息: 使NodePass后的Web应用能够接收原始客户端信息:
@@ -487,7 +467,7 @@ nodepass "server://0.0.0.0:10101/127.0.0.1:8080?log=warn&tls=2&crt=/path/to/cert
- 支持连接审计的合规性要求 - 支持连接审计的合规性要求
- 适用于支持PROXY协议的Web服务器(Nginx、HAProxy等) - 适用于支持PROXY协议的Web服务器(Nginx、HAProxy等)
### 示例26:数据库访问与客户端IP保留 ### 示例25:数据库访问与客户端IP保留
为数据库访问日志记录和安全维护客户端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 在Docker环境中部署NodePass
@@ -549,7 +529,7 @@ docker run -d --name nodepass-client \
## 主控API管理 ## 主控API管理
### 示例28:集中化管理 ### 示例27:集中化管理
为多个NodePass实例设置中央控制器: 为多个NodePass实例设置中央控制器:
@@ -586,7 +566,7 @@ curl -X PUT http://localhost:9090/api/v1/instances/{id} \
- 提供用于自动化和集成的RESTful API - 提供用于自动化和集成的RESTful API
- 包含内置的Swagger UI,位于http://localhost:9090/api/v1/docs - 包含内置的Swagger UI,位于http://localhost:9090/api/v1/docs
### 示例29:自定义API前缀 ### 示例28:自定义API前缀
为主控模式使用自定义API前缀: 为主控模式使用自定义API前缀:
@@ -605,7 +585,7 @@ curl -X POST http://localhost:9090/admin/v1/instances \
- 用于安全或组织目的的自定义URL路径 - 用于安全或组织目的的自定义URL路径
- 在http://localhost:9090/admin/v1/docs访问Swagger UI - 在http://localhost:9090/admin/v1/docs访问Swagger UI
### 示例30:实时连接和流量监控 ### 示例29:实时连接和流量监控
通过主控API监控实例的连接数和流量统计: 通过主控API监控实例的连接数和流量统计:
@@ -645,7 +625,7 @@ curl -H "X-API-Key: your-api-key" \
## QUIC传输协议 ## QUIC传输协议
### 示例31: 基于QUIC的流多路复用隧道 ### 示例30: 基于QUIC的流多路复用隧道
使用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支持改善连接建立 - 通过0-RTT支持改善连接建立
- 客户端在握手时自动接收服务器的QUIC配置 - 客户端在握手时自动接收服务器的QUIC配置
### 示例32: 使用自定义TLS证书的QUIC ### 示例31: 使用自定义TLS证书的QUIC
在生产环境部署带有验证证书的QUIC隧道: 在生产环境部署带有验证证书的QUIC隧道:
@@ -684,7 +664,7 @@ nodepass "client://tunnel.example.com:10101/127.0.0.1:8080?mode=2&min=64&log=inf
- 客户端进行完整证书验证 - 客户端进行完整证书验证
- QUIC配置自动从服务器下发 - 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重连在网络变化后实现更快恢复 - 0-RTT重连在网络变化后实现更快恢复
- 客户端自动采用服务器的QUIC配置 - 客户端自动采用服务器的QUIC配置
### 示例34: QUIC与TCP连接池性能对比 ### 示例33: QUIC与TCP连接池性能对比
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穿透能力 - 改善NAT穿透能力
- 单个UDP套接字减少资源使用 - 单个UDP套接字减少资源使用
### 示例35: 实时应用的QUIC ### 示例34: 实时应用的QUIC
为游戏、VoIP或视频流配置QUIC隧道: 为游戏、VoIP或视频流配置QUIC隧道:
-5
View File
@@ -47,11 +47,6 @@ NodePass 创建了一个具有独立控制和数据通道的网络架构:
- **TCP**:具有持久连接的全双工流式传输,在客户端单端转发模式下优化了直接连接建立 - **TCP**:具有持久连接的全双工流式传输,在客户端单端转发模式下优化了直接连接建立
- **UDP**:具有可配置缓冲区大小和超时的数据报转发 - **UDP**:具有可配置缓冲区大小和超时的数据报转发
6. **智能DNS解析**
- 智能缓存与后台刷新,提供最优性能
- 自定义DNS服务器与自动故障转移
- 原生支持IPv4/IPv6现代网络
## 数据传输流 ## 数据传输流
NodePass 通过其隧道架构建立双向数据流,支持 TCP 和 UDP 协议。系统支持三种数据流模式: NodePass 通过其隧道架构建立双向数据流,支持 TCP 和 UDP 协议。系统支持三种数据流模式:
+15 -15
View File
@@ -245,15 +245,15 @@
**解决方案** **解决方案**
1. **验证DNS配置** 1. **验证系统DNS配置**
- 测试DNS服务器可达性:`ping 1.1.1.1` - 验证解析功能:`nslookup example.com`
- 验证解析功能:`nslookup example.com 8.8.8.8` - 检查系统的DNS设置(NodePass使用系统解析器)
- 确保`dns`参数包含有效IP地址 - 确保网络连接正常工作
2. **网络连接问题** 2. **网络连接问题**
- 检查防火墙是否阻止UDP端口53 - 检查防火墙是否阻止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缓存问题 ### DNS缓存问题
@@ -262,12 +262,11 @@
**解决方案** **解决方案**
1. **调整缓存TTL**(默认5分钟) 1. **调整缓存TTL**(默认5分钟)
- 动态环境:`export NP_DNS_CACHING_TTL=1m` - 动态环境:`dns=1m`
- 稳定环境:`export NP_DNS_CACHING_TTL=30m` - 稳定环境:`dns=30m`
- 重要变更后重启NodePass
2. **负载均衡场景** 2. **负载均衡场景**
- 使用较短TTL`export NP_DNS_CACHING_TTL=30s` - 使用较短TTL`dns=30s`
- 或直接使用IP地址避免DNS缓存 - 或直接使用IP地址避免DNS缓存
### DNS性能优化 ### DNS性能优化
@@ -276,14 +275,15 @@
**解决方案** **解决方案**
1. **选择合适的DNS服务器** 1. **优化缓存TTL**
- 中国:`dns=223.5.5.5,119.29.29.29` - 稳定环境增加TTL`dns=1h`
- 国际:`dns=1.1.1.1,8.8.8.8` - 动态环境减少TTL`dns=1m`
- 使用至少2个DNS服务器实现冗余 - 在新鲜度和性能之间取得平衡
2. **减少DNS查询** 2. **减少DNS查询**
- 性能关键场景直接使用IP地址 - 性能关键场景直接使用IP地址
- 稳定环境增加TTL`export NP_DNS_CACHING_TTL=1h` - 稳定主机名增加TTL
- 尽可能预解析地址
## 主控API问题 ## 主控API问题
+6 -6
View File
@@ -7,7 +7,7 @@ NodePass创建一个带有未加密TCP控制通道的隧道,并为数据交换
NodePass命令的一般语法是: NodePass命令的一般语法是:
```bash ```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` - `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,由客户端设置) - `min=<min_pool>`:最小连接池容量(默认:64,由客户端设置)
- `max=<max_pool>`:最大连接池容量(默认:1024,由服务端设置并下发给客户端) - `max=<max_pool>`:最大连接池容量(默认:1024,由服务端设置并下发给客户端)
- `mode=<run_mode>`:运行模式控制(`0``1``2`- 控制操作行为 - `mode=<run_mode>`:运行模式控制(`0``1``2`- 控制操作行为
@@ -51,7 +51,7 @@ NodePass提供三种互补的运行模式,以适应各种部署场景。
服务端模式建立隧道控制通道,并支持双向数据流转发。 服务端模式建立隧道控制通道,并支持双向数据流转发。
```bash ```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) - `tunnel_addr`:TCP隧道端点地址(控制通道),客户端将连接到此处(例如, 10.1.0.1:10101)
- `target_addr`:业务数据的目标地址,支持双向数据流模式(例如, 10.1.0.1:8080) - `target_addr`:业务数据的目标地址,支持双向数据流模式(例如, 10.1.0.1:8080)
- `log`:日志级别(debug, info, warn, error, event) - `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) - `quic`QUIC传输模式 (0, 1)
- `0`:使用基于TCP的连接池(默认) - `0`:使用基于TCP的连接池(默认)
- `1`:使用基于QUIC的UDP连接池,支持流多路复用 - `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服务端并支持双向数据流转发。 客户端模式连接到NodePass服务端并支持双向数据流转发。
```bash ```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) - `tunnel_addr`:要连接的NodePass服务端隧道端点地址(例如, 10.1.0.1:10101)
- `target_addr`:业务数据的目标地址,支持双向数据流模式(例如, 127.0.0.1:8080) - `target_addr`:业务数据的目标地址,支持双向数据流模式(例如, 127.0.0.1:8080)
- `log`:日志级别(debug, info, warn, error, event) - `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 - `min`:最小连接池容量(默认:64
- `mode`:客户端行为的运行模式控制 - `mode`:客户端行为的运行模式控制
- `0`:自动检测(默认)- 首先尝试本地绑定,如果失败则回退到握手模式 - `0`:自动检测(默认)- 首先尝试本地绑定,如果失败则回退到握手模式
+2 -3
View File
@@ -6,13 +6,12 @@ require (
github.com/NodePassProject/cert v1.0.1 github.com/NodePassProject/cert v1.0.1
github.com/NodePassProject/conn v1.0.16 github.com/NodePassProject/conn v1.0.16
github.com/NodePassProject/logs v1.0.3 github.com/NodePassProject/logs v1.0.3
github.com/NodePassProject/name v1.0.0
github.com/NodePassProject/pool v1.0.49 github.com/NodePassProject/pool v1.0.49
github.com/NodePassProject/quic v1.0.6 github.com/NodePassProject/quic v1.0.8
) )
require ( 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/crypto v0.45.0 // indirect
golang.org/x/net v0.47.0 // indirect golang.org/x/net v0.47.0 // indirect
golang.org/x/sys v0.38.0 // indirect golang.org/x/sys v0.38.0 // indirect
+6 -8
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/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 h1:CDUZVQ477vmmFQHazrQCWM0gJPNINm0C2N3FzC4jVyw=
github.com/NodePassProject/logs v1.0.3/go.mod h1:TwtPXOzLtb8iH+fdduQjEEywICXivsM39cy9AinMSks= 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 h1:gktVmE+GsQ0/C0MF8qgRraR7eS3na4k0QrQfR6o4fkM=
github.com/NodePassProject/pool v1.0.49/go.mod h1:joQFk1oocg56QpJ1QK/2g5Jv/AyqYUQgPXMG1gWe8iA= 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.8 h1:S3kmxInIk6UiN0DcbRqSSoOMOY+JTUdTeeRuAr1SNxs=
github.com/NodePassProject/quic v1.0.6/go.mod h1:EJgX8fdugc7w3ISbJ0abIuy1mFMr3uO4XSxci6QB8jg= 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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 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.57.0 h1:AsSSrrMs4qI/hLrKlTH/TGQeTMY0ib1pAOX7vA3AdqE=
github.com/quic-go/quic-go v0.56.0/go.mod h1:9gx5KsFQtw2oZ6GZTyh+7YEvOxWCL9WZAepnHxgAo6c= github.com/quic-go/quic-go v0.57.0/go.mod h1:ly4QBAjHA2VhdnxhojRsCUOeJwKYg+taDlos92xb1+s=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 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 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko=
go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o= go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o=
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
+20 -5
View File
@@ -32,6 +32,7 @@ type Client struct {
func NewClient(parsedURL *url.URL, logger *logs.Logger) (*Client, error) { func NewClient(parsedURL *url.URL, logger *logs.Logger) (*Client, error) {
client := &Client{ client := &Client{
Common: Common{ Common: Common{
parsedURL: parsedURL,
logger: logger, logger: logger,
signalChan: make(chan string, semaphoreLimit), signalChan: make(chan string, semaphoreLimit),
tcpBufferPool: &sync.Pool{ tcpBufferPool: &sync.Pool{
@@ -52,7 +53,7 @@ func NewClient(parsedURL *url.URL, logger *logs.Logger) (*Client, error) {
}, },
tunnelName: parsedURL.Hostname(), 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) return nil, fmt.Errorf("newClient: initConfig failed: %w", err)
} }
client.initRateLimiter() client.initRateLimiter()
@@ -63,7 +64,7 @@ func NewClient(parsedURL *url.URL, logger *logs.Logger) (*Client, error) {
func (c *Client) Run() { func (c *Client) Run() {
logInfo := func(prefix string) { 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", 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.runMode, c.quicMode, c.dialerIP, c.readTimeout, c.rateLimit/125000, c.slotLimit,
c.proxyProtocol, c.disableTCP, c.disableUDP) c.proxyProtocol, c.disableTCP, c.disableUDP)
} }
@@ -156,7 +157,11 @@ func (c *Client) commonStart() error {
c.tlsCode, c.tlsCode,
c.tunnelName, c.tunnelName,
func() (net.Conn, error) { 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() go tcpPool.ClientManager()
c.tunnelPool = tcpPool c.tunnelPool = tcpPool
@@ -169,7 +174,13 @@ func (c *Client) commonStart() error {
reportInterval, reportInterval,
c.tlsCode, c.tlsCode,
c.tunnelName, c.tunnelName,
c.tunnelUDPAddr.String()) func() (string, error) {
udpAddr, err := c.getTunnelUDPAddr()
if err != nil {
return "", err
}
return udpAddr.String(), nil
})
go udpPool.ClientManager() go udpPool.ClientManager()
c.tunnelPool = udpPool c.tunnelPool = udpPool
default: default:
@@ -194,7 +205,11 @@ func (c *Client) commonStart() error {
// tunnelHandshake 与隧道服务端进行握手 // tunnelHandshake 与隧道服务端进行握手
func (c *Client) tunnelHandshake() error { func (c *Client) tunnelHandshake() error {
// 建立隧道TCP连接 // 建立隧道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 { if err != nil {
return fmt.Errorf("tunnelHandshake: dialTimeout failed: %w", err) return fmt.Errorf("tunnelHandshake: dialTimeout failed: %w", err)
} }
+267 -173
View File
@@ -23,15 +23,15 @@ import (
"github.com/NodePassProject/conn" "github.com/NodePassProject/conn"
"github.com/NodePassProject/logs" "github.com/NodePassProject/logs"
"github.com/NodePassProject/name"
) )
// Common 包含所有模式共享的核心功能 // Common 包含所有模式共享的核心功能
type Common struct { type Common struct {
mu sync.Mutex // 互斥锁 mu sync.Mutex // 互斥锁
parsedURL *url.URL // 解析后的URL
logger *logs.Logger // 日志记录器 logger *logs.Logger // 日志记录器
resolver *name.Resolver // 域名解析器 dnsCacheTTL time.Duration // DNS缓存TTL
dnsIPs []string // DNS服务器组 dnsCacheEntries sync.Map // DNS缓存条目
tlsCode string // TLS模式代码 tlsCode string // TLS模式代码
tlsConfig *tls.Config // TLS配置 tlsConfig *tls.Config // TLS配置
coreType string // 核心类型 coreType string // 核心类型
@@ -41,8 +41,10 @@ type Common struct {
dialerIP string // 拨号本地IP dialerIP string // 拨号本地IP
dialerFallback uint32 // 拨号回落标志 dialerFallback uint32 // 拨号回落标志
tunnelKey string // 隧道密钥 tunnelKey string // 隧道密钥
tunnelAddr string // 原始隧道地址
tunnelTCPAddr *net.TCPAddr // 隧道TCP地址 tunnelTCPAddr *net.TCPAddr // 隧道TCP地址
tunnelUDPAddr *net.UDPAddr // 隧道UDP地址 tunnelUDPAddr *net.UDPAddr // 隧道UDP地址
targetAddrs []string // 原始目标地址组
targetTCPAddrs []*net.TCPAddr // 目标TCP地址组 targetTCPAddrs []*net.TCPAddr // 目标TCP地址组
targetUDPAddrs []*net.UDPAddr // 目标UDP地址组 targetUDPAddrs []*net.UDPAddr // 目标UDP地址组
targetIdx uint64 // 目标地址索引 targetIdx uint64 // 目标地址索引
@@ -80,6 +82,13 @@ type Common struct {
cancel context.CancelFunc // 取消函数 cancel context.CancelFunc // 取消函数
} }
// dnsCacheEntry DNS缓存条目
type dnsCacheEntry struct {
tcpAddr *net.TCPAddr
udpAddr *net.UDPAddr
expiredAt time.Time
}
// TransportPool 统一连接池接口 // TransportPool 统一连接池接口
type TransportPool interface { type TransportPool interface {
IncomingGet(timeout time.Duration) (string, net.Conn, error) IncomingGet(timeout time.Duration) (string, net.Conn, error)
@@ -100,7 +109,6 @@ var (
semaphoreLimit = getEnvAsInt("NP_SEMAPHORE_LIMIT", 65536) // 信号量限制 semaphoreLimit = getEnvAsInt("NP_SEMAPHORE_LIMIT", 65536) // 信号量限制
tcpDataBufSize = getEnvAsInt("NP_TCP_DATA_BUF_SIZE", 16384) // TCP缓冲区大小 tcpDataBufSize = getEnvAsInt("NP_TCP_DATA_BUF_SIZE", 16384) // TCP缓冲区大小
udpDataBufSize = getEnvAsInt("NP_UDP_DATA_BUF_SIZE", 16384) // UDP缓冲区大小 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) // 握手超时 handshakeTimeout = getEnvAsDuration("NP_HANDSHAKE_TIMEOUT", 5*time.Second) // 握手超时
tcpDialTimeout = getEnvAsDuration("NP_TCP_DIAL_TIMEOUT", 5*time.Second) // TCP拨号超时 tcpDialTimeout = getEnvAsDuration("NP_TCP_DIAL_TIMEOUT", 5*time.Second) // TCP拨号超时
udpDialTimeout = getEnvAsDuration("NP_UDP_DIAL_TIMEOUT", 5*time.Second) // UDP拨号超时 udpDialTimeout = getEnvAsDuration("NP_UDP_DIAL_TIMEOUT", 5*time.Second) // UDP拨号超时
@@ -116,18 +124,18 @@ var (
// 默认配置 // 默认配置
const ( const (
defaultDNSIPs = "1.1.1.1,8.8.8.8" // 默认DNS服务器 defaultDNSTTL = 5 * time.Minute // 默认DNS缓存TTL
defaultMinPool = 64 // 默认最小池容量 defaultMinPool = 64 // 默认最小池容量
defaultMaxPool = 1024 // 默认最大池容量 defaultMaxPool = 1024 // 默认最大池容量
defaultRunMode = "0" // 默认运行模式 defaultRunMode = "0" // 默认运行模式
defaultQuicMode = "0" // 默认QUIC模式 defaultQuicMode = "0" // 默认QUIC模式
defaultDialerIP = "auto" // 默认拨号本地IP defaultDialerIP = "auto" // 默认拨号本地IP
defaultReadTimeout = 0 * time.Second // 默认读取超时 defaultReadTimeout = 0 * time.Second // 默认读取超时
defaultRateLimit = 0 // 默认速率限制 defaultRateLimit = 0 // 默认速率限制
defaultSlotLimit = 65536 // 默认槽位限制 defaultSlotLimit = 65536 // 默认槽位限制
defaultProxyProtocol = "0" // 默认代理协议 defaultProxyProtocol = "0" // 默认代理协议
defaultTCPStrategy = "0" // 默认TCP策略 defaultTCPStrategy = "0" // 默认TCP策略
defaultUDPStrategy = "0" // 默认UDP策略 defaultUDPStrategy = "0" // 默认UDP策略
) )
// getTCPBuffer 获取TCP缓冲区 // getTCPBuffer 获取TCP缓冲区
@@ -250,6 +258,56 @@ func (c *Common) decode(data []byte) ([]byte, error) {
return c.xor(decoded), nil 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 解析单个地址 // resolveAddr 解析单个地址
func (c *Common) resolveAddr(network, address string) (any, error) { func (c *Common) resolveAddr(network, address string) (any, error) {
host, _, err := net.SplitHostPort(address) 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) return nil, fmt.Errorf("invalid address %s: %w", address, err)
} }
if host == "" { if host == "" || net.ParseIP(host) != nil {
if network == "tcp" { if network == "tcp" {
return net.ResolveTCPAddr("tcp", address) return net.ResolveTCPAddr("tcp", address)
} }
return net.ResolveUDPAddr("udp", address) return net.ResolveUDPAddr("udp", address)
} }
if network == "tcp" { return c.resolve(network, address)
return c.resolver.ResolveTCPAddr("tcp", address)
}
return c.resolver.ResolveUDPAddr("udp", address)
} }
// getAddress 解析和设置地址信息 // resolveTarget 动态解析目标地址
func (c *Common) getAddress(parsedURL *url.URL) error { func (c *Common) resolveTarget(network string, idx int) (any, error) {
// 解析隧道地址 if idx < 0 || idx >= len(c.targetAddrs) {
tunnelAddr := parsedURL.Host return nil, fmt.Errorf("resolveTarget: index %d out of range", idx)
if tunnelAddr == "" {
return fmt.Errorf("getAddress: no valid tunnel address found")
} }
// 解析隧道TCP地址 addr, err := c.resolveAddr(network, c.targetAddrs[idx])
tcpAddr, err := c.resolveAddr("tcp", tunnelAddr)
if err != nil { if err != nil {
return fmt.Errorf("getAddress: resolveTCPAddr failed: %w", err) if network == "tcp" {
} return c.targetTCPAddrs[idx], 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
} }
return c.targetUDPAddrs[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 addr, nil
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
} }
// getCoreType 获取核心类型 // getTunnelTCPAddr 动态解析隧道TCP地址
func (c *Common) getCoreType(parsedURL *url.URL) { func (c *Common) getTunnelTCPAddr() (*net.TCPAddr, error) {
c.coreType = parsedURL.Scheme 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 获取目标地址组的字符串表示 // getTargetAddrsString 获取目标地址组的字符串表示
@@ -368,15 +378,17 @@ func (c *Common) nextTargetIdx() int {
// dialWithRotation 轮询拨号到目标地址组 // dialWithRotation 轮询拨号到目标地址组
func (c *Common) dialWithRotation(network string, timeout time.Duration) (net.Conn, error) { func (c *Common) dialWithRotation(network string, timeout time.Duration) (net.Conn, error) {
var addrCount int addrCount := len(c.targetAddrs)
var getAddr func(int) string
if network == "tcp" { getAddr := func(i int) string {
addrCount = len(c.targetTCPAddrs) addr, _ := c.resolveTarget(network, i)
getAddr = func(i int) string { return c.targetTCPAddrs[i].String() } if tcpAddr, ok := addr.(*net.TCPAddr); ok {
} else { return tcpAddr.String()
addrCount = len(c.targetUDPAddrs) }
getAddr = func(i int) string { return c.targetUDPAddrs[i].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 { 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() startIdx := c.nextTargetIdx()
var lastErr error var lastErr error
for i := range addrCount { 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 { if err == nil {
return conn, 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) 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中获取隧道密钥 // getTunnelKey 从URL中获取隧道密钥
func (c *Common) getTunnelKey(parsedURL *url.URL) { func (c *Common) getTunnelKey() {
if key := parsedURL.User.Username(); key != "" { if key := c.parsedURL.User.Username(); key != "" {
c.tunnelKey = key c.tunnelKey = key
} else { } else {
hash := fnv.New32a() hash := fnv.New32a()
hash.Write([]byte(parsedURL.Port())) hash.Write([]byte(c.parsedURL.Port()))
c.tunnelKey = hex.EncodeToString(hash.Sum(nil)) c.tunnelKey = hex.EncodeToString(hash.Sum(nil))
} }
} }
// getDNSIPs 获取DNS服务器组 // getDNSTTL 获取DNS缓存TTL
func (c *Common) getDNSIPs(parsedURL *url.URL) { func (c *Common) getDNSTTL() {
if dns := parsedURL.Query().Get("dns"); dns != "" { if dns := c.parsedURL.Query().Get("dns"); dns != "" {
ips := strings.SplitSeq(dns, ",") if ttl, err := time.ParseDuration(dns); err == nil && ttl > 0 {
for ipStr := range ips { c.dnsCacheTTL = ttl
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)
}
} }
} else { } else {
for ipStr := range strings.SplitSeq(defaultDNSIPs, ",") { c.dnsCacheTTL = defaultDNSTTL
c.dnsIPs = append(c.dnsIPs, strings.TrimSpace(ipStr))
}
} }
} }
// getPoolCapacity 获取连接池容量设置 // getPoolCapacity 获取连接池容量设置
func (c *Common) getPoolCapacity(parsedURL *url.URL) { func (c *Common) getPoolCapacity() {
if min := parsedURL.Query().Get("min"); min != "" { if min := c.parsedURL.Query().Get("min"); min != "" {
if value, err := strconv.Atoi(min); err == nil && value > 0 { if value, err := strconv.Atoi(min); err == nil && value > 0 {
c.minPoolCapacity = value c.minPoolCapacity = value
} }
@@ -462,7 +555,7 @@ func (c *Common) getPoolCapacity(parsedURL *url.URL) {
c.minPoolCapacity = defaultMinPool 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 { if value, err := strconv.Atoi(max); err == nil && value > 0 {
c.maxPoolCapacity = value c.maxPoolCapacity = value
} }
@@ -472,8 +565,8 @@ func (c *Common) getPoolCapacity(parsedURL *url.URL) {
} }
// getRunMode 获取运行模式 // getRunMode 获取运行模式
func (c *Common) getRunMode(parsedURL *url.URL) { func (c *Common) getRunMode() {
if mode := parsedURL.Query().Get("mode"); mode != "" { if mode := c.parsedURL.Query().Get("mode"); mode != "" {
c.runMode = mode c.runMode = mode
} else { } else {
c.runMode = defaultRunMode c.runMode = defaultRunMode
@@ -481,8 +574,8 @@ func (c *Common) getRunMode(parsedURL *url.URL) {
} }
// getQuicMode 获取QUIC模式 // getQuicMode 获取QUIC模式
func (c *Common) getQuicMode(parsedURL *url.URL) { func (c *Common) getQuicMode() {
if quicMode := parsedURL.Query().Get("quic"); quicMode != "" { if quicMode := c.parsedURL.Query().Get("quic"); quicMode != "" {
c.quicMode = quicMode c.quicMode = quicMode
} else { } else {
c.quicMode = defaultQuicMode c.quicMode = defaultQuicMode
@@ -493,8 +586,8 @@ func (c *Common) getQuicMode(parsedURL *url.URL) {
} }
// getDialerIP 获取拨号本地IP设置 // getDialerIP 获取拨号本地IP设置
func (c *Common) getDialerIP(parsedURL *url.URL) { func (c *Common) getDialerIP() {
if dialerIP := parsedURL.Query().Get("dial"); dialerIP != "" && dialerIP != "auto" { if dialerIP := c.parsedURL.Query().Get("dial"); dialerIP != "" && dialerIP != "auto" {
if ip := net.ParseIP(dialerIP); ip != nil { if ip := net.ParseIP(dialerIP); ip != nil {
c.dialerIP = dialerIP c.dialerIP = dialerIP
return return
@@ -506,8 +599,8 @@ func (c *Common) getDialerIP(parsedURL *url.URL) {
} }
// getReadTimeout 获取读取超时设置 // getReadTimeout 获取读取超时设置
func (c *Common) getReadTimeout(parsedURL *url.URL) { func (c *Common) getReadTimeout() {
if timeout := parsedURL.Query().Get("read"); timeout != "" { if timeout := c.parsedURL.Query().Get("read"); timeout != "" {
if value, err := time.ParseDuration(timeout); err == nil && value > 0 { if value, err := time.ParseDuration(timeout); err == nil && value > 0 {
c.readTimeout = value c.readTimeout = value
} }
@@ -517,8 +610,8 @@ func (c *Common) getReadTimeout(parsedURL *url.URL) {
} }
// getRateLimit 获取速率限制 // getRateLimit 获取速率限制
func (c *Common) getRateLimit(parsedURL *url.URL) { func (c *Common) getRateLimit() {
if limit := parsedURL.Query().Get("rate"); limit != "" { if limit := c.parsedURL.Query().Get("rate"); limit != "" {
if value, err := strconv.Atoi(limit); err == nil && value > 0 { if value, err := strconv.Atoi(limit); err == nil && value > 0 {
c.rateLimit = value * 125000 c.rateLimit = value * 125000
} }
@@ -528,8 +621,8 @@ func (c *Common) getRateLimit(parsedURL *url.URL) {
} }
// getSlotLimit 获取连接槽位限制 // getSlotLimit 获取连接槽位限制
func (c *Common) getSlotLimit(parsedURL *url.URL) { func (c *Common) getSlotLimit() {
if slot := parsedURL.Query().Get("slot"); slot != "" { if slot := c.parsedURL.Query().Get("slot"); slot != "" {
if value, err := strconv.Atoi(slot); err == nil && value > 0 { if value, err := strconv.Atoi(slot); err == nil && value > 0 {
c.slotLimit = int32(value) c.slotLimit = int32(value)
} }
@@ -539,8 +632,8 @@ func (c *Common) getSlotLimit(parsedURL *url.URL) {
} }
// getProxyProtocol 获取代理协议设置 // getProxyProtocol 获取代理协议设置
func (c *Common) getProxyProtocol(parsedURL *url.URL) { func (c *Common) getProxyProtocol() {
if protocol := parsedURL.Query().Get("proxy"); protocol != "" { if protocol := c.parsedURL.Query().Get("proxy"); protocol != "" {
c.proxyProtocol = protocol c.proxyProtocol = protocol
} else { } else {
c.proxyProtocol = defaultProxyProtocol c.proxyProtocol = defaultProxyProtocol
@@ -548,8 +641,8 @@ func (c *Common) getProxyProtocol(parsedURL *url.URL) {
} }
// getTCPStrategy 获取TCP策略 // getTCPStrategy 获取TCP策略
func (c *Common) getTCPStrategy(parsedURL *url.URL) { func (c *Common) getTCPStrategy() {
if tcpStrategy := parsedURL.Query().Get("notcp"); tcpStrategy != "" { if tcpStrategy := c.parsedURL.Query().Get("notcp"); tcpStrategy != "" {
c.disableTCP = tcpStrategy c.disableTCP = tcpStrategy
} else { } else {
c.disableTCP = defaultTCPStrategy c.disableTCP = defaultTCPStrategy
@@ -557,8 +650,8 @@ func (c *Common) getTCPStrategy(parsedURL *url.URL) {
} }
// getUDPStrategy 获取UDP策略 // getUDPStrategy 获取UDP策略
func (c *Common) getUDPStrategy(parsedURL *url.URL) { func (c *Common) getUDPStrategy() {
if udpStrategy := parsedURL.Query().Get("noudp"); udpStrategy != "" { if udpStrategy := c.parsedURL.Query().Get("noudp"); udpStrategy != "" {
c.disableUDP = udpStrategy c.disableUDP = udpStrategy
} else { } else {
c.disableUDP = defaultUDPStrategy c.disableUDP = defaultUDPStrategy
@@ -566,26 +659,24 @@ func (c *Common) getUDPStrategy(parsedURL *url.URL) {
} }
// initConfig 初始化配置 // initConfig 初始化配置
func (c *Common) initConfig(parsedURL *url.URL) error { func (c *Common) initConfig() error {
c.getDNSIPs(parsedURL) if err := c.getAddress(); err != nil {
c.resolver = name.NewResolver(dnsCachingTTL, c.dnsIPs)
if err := c.getAddress(parsedURL); err != nil {
return err return err
} }
c.getCoreType(parsedURL) c.getCoreType()
c.getTunnelKey(parsedURL) c.getDNSTTL()
c.getPoolCapacity(parsedURL) c.getTunnelKey()
c.getRunMode(parsedURL) c.getPoolCapacity()
c.getQuicMode(parsedURL) c.getRunMode()
c.getDialerIP(parsedURL) c.getQuicMode()
c.getReadTimeout(parsedURL) c.getDialerIP()
c.getRateLimit(parsedURL) c.getReadTimeout()
c.getSlotLimit(parsedURL) c.getRateLimit()
c.getProxyProtocol(parsedURL) c.getSlotLimit()
c.getTCPStrategy(parsedURL) c.getProxyProtocol()
c.getUDPStrategy(parsedURL) c.getTCPStrategy()
c.getUDPStrategy()
return nil return nil
} }
@@ -671,7 +762,7 @@ func (c *Common) initTunnelListener() error {
// initTargetListener 初始化目标监听器 // initTargetListener 初始化目标监听器
func (c *Common) initTargetListener() error { 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") return fmt.Errorf("initTargetListener: no target address")
} }
@@ -769,9 +860,7 @@ func (c *Common) stop() {
} }
// 清空DNS缓存 // 清空DNS缓存
if c.resolver != nil { c.clearCache()
c.resolver.ClearCache()
}
} }
// shutdown 共用优雅关闭 // shutdown 共用优雅关闭
@@ -1067,8 +1156,8 @@ func (c *Common) commonTCPLoop() {
}() }()
// 交换数据 // 交换数据
c.logger.Debug("Starting exchange: %v <-> %v", remoteConn.LocalAddr(), targetConn.LocalAddr()) c.logger.Info("Starting exchange: %v <-> %v", targetConn.RemoteAddr(), remoteConn.RemoteAddr())
c.logger.Debug("Exchange complete: %v", conn.DataExchange(remoteConn, targetConn, c.readTimeout, buffer1, buffer2)) c.logger.Info("Exchange complete: %v", conn.DataExchange(targetConn, remoteConn, c.readTimeout, buffer1, buffer2))
}(targetConn) }(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.Info("Starting exchange: %v <-> %v", remoteConn.RemoteAddr(), targetConn.RemoteAddr())
c.logger.Debug("Exchange complete: %v", conn.DataExchange(remoteConn, targetConn, c.readTimeout, buffer1, buffer2)) c.logger.Info("Exchange complete: %v", conn.DataExchange(remoteConn, targetConn, c.readTimeout, buffer1, buffer2))
} }
// commonUDPOnce 共用处理单个UDP请求 // commonUDPOnce 共用处理单个UDP请求
@@ -1587,10 +1676,15 @@ func (c *Common) singleEventLoop() error {
ping := 0 ping := 0
now := time.Now() now := time.Now()
// 尝试连接到目标地址 // 尝试连接到目标地址检测延迟
if conn, err := net.DialTimeout("tcp", c.targetTCPAddrs[c.nextTargetIdx()].String(), reportInterval); err == nil { idx := c.nextTargetIdx()
ping = int(time.Since(now).Milliseconds()) if addr, _ := c.resolveTarget("tcp", idx); addr != nil {
conn.Close() 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.Info("Starting exchange: %v <-> %v", tunnelConn.RemoteAddr(), targetConn.RemoteAddr())
c.logger.Debug("Exchange complete: %v", conn.DataExchange(tunnelConn, targetConn, c.readTimeout, buffer1, buffer2)) c.logger.Info("Exchange complete: %v", conn.DataExchange(tunnelConn, targetConn, c.readTimeout, buffer1, buffer2))
}(tunnelConn) }(tunnelConn)
} }
+3 -6
View File
@@ -1785,9 +1785,9 @@ func (m *Master) generateConfigURL(instance *Instance) string {
// 根据实例类型设置默认参数 // 根据实例类型设置默认参数
switch instance.Type { switch instance.Type {
case "client": 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") == "" { if query.Get("dns") == "" {
query.Set("dns", defaultDNSIPs) query.Set("dns", defaultDNSTTL.String())
} }
if query.Get("min") == "" { if query.Get("min") == "" {
query.Set("min", strconv.Itoa(defaultMinPool)) query.Set("min", strconv.Itoa(defaultMinPool))
@@ -1795,9 +1795,6 @@ func (m *Master) generateConfigURL(instance *Instance) string {
if query.Get("mode") == "" { if query.Get("mode") == "" {
query.Set("mode", defaultRunMode) query.Set("mode", defaultRunMode)
} }
if query.Get("quic") == "" {
query.Set("quic", defaultQuicMode)
}
if query.Get("dial") == "" { if query.Get("dial") == "" {
query.Set("dial", defaultDialerIP) query.Set("dial", defaultDialerIP)
} }
@@ -1822,7 +1819,7 @@ func (m *Master) generateConfigURL(instance *Instance) string {
case "server": case "server":
// server参数: dns, max, mode, quic, dial, read, rate, slot, proxy, notcp, noudp // server参数: dns, max, mode, quic, dial, read, rate, slot, proxy, notcp, noudp
if query.Get("dns") == "" { if query.Get("dns") == "" {
query.Set("dns", defaultDNSIPs) query.Set("dns", defaultDNSTTL.String())
} }
if query.Get("max") == "" { if query.Get("max") == "" {
query.Set("max", strconv.Itoa(defaultMaxPool)) query.Set("max", strconv.Itoa(defaultMaxPool))
+114 -68
View File
@@ -12,7 +12,6 @@ import (
"os" "os"
"os/signal" "os/signal"
"strconv" "strconv"
"strings"
"sync" "sync"
"syscall" "syscall"
"time" "time"
@@ -33,6 +32,7 @@ type Server struct {
func NewServer(parsedURL *url.URL, tlsCode string, tlsConfig *tls.Config, logger *logs.Logger) (*Server, error) { func NewServer(parsedURL *url.URL, tlsCode string, tlsConfig *tls.Config, logger *logs.Logger) (*Server, error) {
server := &Server{ server := &Server{
Common: Common{ Common: Common{
parsedURL: parsedURL,
tlsCode: tlsCode, tlsCode: tlsCode,
tlsConfig: tlsConfig, tlsConfig: tlsConfig,
logger: logger, logger: logger,
@@ -54,7 +54,7 @@ func NewServer(parsedURL *url.URL, tlsCode string, tlsConfig *tls.Config, logger
pongURL: &url.URL{Scheme: "np", Fragment: "o"}, 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) return nil, fmt.Errorf("newServer: initConfig failed: %w", err)
} }
server.initRateLimiter() server.initRateLimiter()
@@ -65,7 +65,7 @@ func NewServer(parsedURL *url.URL, tlsCode string, tlsConfig *tls.Config, logger
func (s *Server) Run() { func (s *Server) Run() {
logInfo := func(prefix string) { 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", 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.runMode, s.quicMode, s.dialerIP, s.readTimeout, s.rateLimit/125000, s.slotLimit,
s.proxyProtocol, s.disableTCP, s.disableUDP) s.proxyProtocol, s.disableTCP, s.disableUDP)
} }
@@ -182,76 +182,121 @@ func (s *Server) start() error {
// tunnelHandshake 与客户端进行握手 // tunnelHandshake 与客户端进行握手
func (s *Server) tunnelHandshake() error { func (s *Server) tunnelHandshake() error {
// 接受隧道连接 type handshakeResult struct {
for s.ctx.Err() == nil { conn *net.TCPConn
tunnelTCPConn, err := s.tunnelListener.Accept() bufReader *bufio.Reader
if err != nil { clientIP string
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
} }
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()) 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{ tunnelURL := &url.URL{
Scheme: "np", Scheme: "np",
User: url.User(s.quicMode), User: url.User(s.quicMode),
@@ -260,6 +305,7 @@ func (s *Server) tunnelHandshake() error {
Fragment: s.tlsCode, Fragment: s.tlsCode,
} }
// 发送隧道配置信息
_, err := s.tunnelTCPConn.Write(s.encode([]byte(tunnelURL.String()))) _, err := s.tunnelTCPConn.Write(s.encode([]byte(tunnelURL.String())))
if err != nil { if err != nil {
return fmt.Errorf("tunnelHandshake: write tunnel config failed: %w", err) return fmt.Errorf("tunnelHandshake: write tunnel config failed: %w", err)
+4 -4
View File
@@ -7,13 +7,13 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_NAME:=openlist2 PKG_NAME:=openlist2
PKG_VERSION:=4.1.7 PKG_VERSION:=4.1.8
PKG_WEB_VERSION:=4.1.7 PKG_WEB_VERSION:=4.1.8
PKG_RELEASE:=1 PKG_RELEASE:=1
PKG_SOURCE:=openlist-$(PKG_VERSION).tar.gz PKG_SOURCE:=openlist-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://codeload.github.com/OpenListTeam/OpenList/tar.gz/v$(PKG_VERSION)? 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) PKG_BUILD_DIR:=$(BUILD_DIR)/OpenList-$(PKG_VERSION)
@@ -24,7 +24,7 @@ PKG_MAINTAINER:=sbwml <admin@cooluc.com>
define Download/openlist-frontend define Download/openlist-frontend
FILE:=openlist-frontend-dist-lite-v$(PKG_WEB_VERSION).tar.gz FILE:=openlist-frontend-dist-lite-v$(PKG_WEB_VERSION).tar.gz
URL:=https://github.com/OpenListTeam/OpenList-Frontend/releases/download/v$(PKG_WEB_VERSION)/ URL:=https://github.com/OpenListTeam/OpenList-Frontend/releases/download/v$(PKG_WEB_VERSION)/
HASH:=2a365c1ce17904926cf275540680f0f24c0c8c3620fcbb98149d7faf781235aa HASH:=00c8cf73fa98ba3ff56de9a8fd02d545ef87236170e03ca6f9a76c04d4cf957e
endef endef
PKG_BUILD_DEPENDS:=golang/host PKG_BUILD_DEPENDS:=golang/host
@@ -83,6 +83,7 @@ function index()
entry({"admin", "services", appname, "clear_all_nodes"}, call("clear_all_nodes")).leaf = true 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, "delete_select_nodes"}, call("delete_select_nodes")).leaf = true
entry({"admin", "services", appname, "get_node"}, call("get_node")).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, "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_node"}, call("subscribe_del_node")).leaf = true
entry({"admin", "services", appname, "subscribe_del_all"}, call("subscribe_del_all")).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
end end
function get_node() function get_node()
local id = http.formvalue("id") local id = http.formvalue("id")
local result = {} local result = {}
local show_node_info = api.uci_get_type("global_other", "show_node_info", "0") 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 if o and o.address and show_node_info == "1" then
local f = api.get_ipv6_full(o.address) local f = api.get_ipv6_full(o.address)
if f ~= "" then if f ~= "" then
@@ -611,14 +611,35 @@ function get_node()
result = uci:get_all(appname, id) result = uci:get_all(appname, id)
add_is_ipv6_key(result) add_is_ipv6_key(result)
else else
local default_nodes = {}
local other_nodes = {}
uci:foreach(appname, "nodes", function(t) uci:foreach(appname, "nodes", function(t)
add_is_ipv6_key(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) 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 end
http_write_json(result) http_write_json(result)
end 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() function update_rules()
local update = http.formvalue("update") local update = http.formvalue("update")
luci.sys.call("lua /usr/share/passwall/rule_update.lua log '" .. update .. "' > /dev/null 2>&1 &") luci.sys.call("lua /usr/share/passwall/rule_update.lua log '" .. update .. "' > /dev/null 2>&1 &")
@@ -210,19 +210,28 @@ table td, .table .td {
document.getElementById("set_node_name").innerHTML = ""; document.getElementById("set_node_name").innerHTML = "";
} }
function _cbi_row_top(id) { function row_swap(btn, up) {
//此函数已经损坏,等待修复或其他解决方案。 const row = btn.closest("tr");
var dom = document.getElementById("cbi-passwall-" + id); if (!row) return;
if (dom) { const parent = row.parentNode;
var trs = document.getElementById("cbi-passwall-nodes").getElementsByClassName("cbi-section-table-row"); if (up) {
if (trs && trs.length > 0) { const prev = row.previousElementSibling;
for (var i = 0; i < trs.length; i++) { if (prev && !prev.classList.contains("cbi-section-table-titles")) {
var up = dom.getElementsByClassName("cbi-button-up"); parent.insertBefore(row, prev);
if (up) {
cbi_row_swap(up[0], true, 'cbi.sts.passwall.nodes');
}
}
} }
} 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 }; 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() { function get_now_use_node() {
XHR.get('<%=api.url("get_now_use_node")%>', null, XHR.get('<%=api.url("get_now_use_node")%>', null,
@@ -522,6 +564,7 @@ table td, .table .td {
</table> </table>
<div class="cbi-section-create cbi-tblsection-create"> <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-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> </div>
</fieldset> </fieldset>
</script> </script>
@@ -537,11 +580,12 @@ table td, .table .td {
<td class="td cbi-value-field">{{url_test}}</td> <td class="td cbi-value-field">{{url_test}}</td>
<td class="td cbi-section-table-cell nowrap cbi-section-actions"> <td class="td cbi-section-table-cell nowrap cbi-section-actions">
<div class="node-wrapper"> <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="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-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-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-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%>"> <input class="btn cbi-button cbi-button-remove" type="button" value="<%:Delete%>" onclick="del_node('{{id}}')" alt="<%:Delete%>" title="<%:Delete%>">
</div> </div>
@@ -421,6 +421,12 @@ msgstr "节点备注"
msgid "Add Mode" msgid "Add Mode"
msgstr "添加方式" msgstr "添加方式"
msgid "Save Order"
msgstr "保存当前顺序"
msgid "Saved current page order successfully."
msgstr "保存当前页面顺序成功。"
msgid "Type" msgid "Type"
msgstr "类型" msgstr "类型"
+2 -2
View File
@@ -21,13 +21,13 @@ define Download/geoip
HASH:=2445b44d9ae3ab9a867c9d1e0e244646c4c378622e14b9afaf3658ecf46a40b9 HASH:=2445b44d9ae3ab9a867c9d1e0e244646c4c378622e14b9afaf3658ecf46a40b9
endef endef
GEOSITE_VER:=20251124114549 GEOSITE_VER:=20251125142217
GEOSITE_FILE:=dlc.dat.$(GEOSITE_VER) GEOSITE_FILE:=dlc.dat.$(GEOSITE_VER)
define Download/geosite define Download/geosite
URL:=https://github.com/v2fly/domain-list-community/releases/download/$(GEOSITE_VER)/ URL:=https://github.com/v2fly/domain-list-community/releases/download/$(GEOSITE_VER)/
URL_FILE:=dlc.dat URL_FILE:=dlc.dat
FILE:=$(GEOSITE_FILE) FILE:=$(GEOSITE_FILE)
HASH:=1685841ffa39bf684e17704fa79ace0acd6f70c84739def9bfcd214dba6a27ba HASH:=ae6033c884713da8e98f2c1d8167141fbb47aadd297b3ba81450d87582827d5c
endef endef
GEOSITE_IRAN_VER:=202511240043 GEOSITE_IRAN_VER:=202511240043
@@ -118,7 +118,23 @@ public class BaseFmt
} }
if (item.Extra.IsNotEmpty()) 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; break;