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