mirror of
https://github.com/bolucat/Archive.git
synced 2025-10-08 01:20:58 +08:00
Update On Sat Jan 4 19:31:20 CET 2025
This commit is contained in:
1
.github/update.log
vendored
1
.github/update.log
vendored
@@ -872,3 +872,4 @@ Update On Tue Dec 31 19:31:50 CET 2024
|
|||||||
Update On Wed Jan 1 19:33:17 CET 2025
|
Update On Wed Jan 1 19:33:17 CET 2025
|
||||||
Update On Thu Jan 2 19:33:11 CET 2025
|
Update On Thu Jan 2 19:33:11 CET 2025
|
||||||
Update On Fri Jan 3 19:34:50 CET 2025
|
Update On Fri Jan 3 19:34:50 CET 2025
|
||||||
|
Update On Sat Jan 4 19:31:12 CET 2025
|
||||||
|
35
clash-nyanpasu/backend/Cargo.lock
generated
35
clash-nyanpasu/backend/Cargo.lock
generated
@@ -1586,7 +1586,7 @@ dependencies = [
|
|||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
"url",
|
"url",
|
||||||
"uuid",
|
"uuid",
|
||||||
"webview2-com 0.33.0",
|
"webview2-com",
|
||||||
"which 7.0.1",
|
"which 7.0.1",
|
||||||
"whoami",
|
"whoami",
|
||||||
"window-vibrancy",
|
"window-vibrancy",
|
||||||
@@ -8436,7 +8436,7 @@ dependencies = [
|
|||||||
"url",
|
"url",
|
||||||
"urlpattern",
|
"urlpattern",
|
||||||
"webkit2gtk",
|
"webkit2gtk",
|
||||||
"webview2-com 0.34.0",
|
"webview2-com",
|
||||||
"window-vibrancy",
|
"window-vibrancy",
|
||||||
"windows 0.58.0",
|
"windows 0.58.0",
|
||||||
]
|
]
|
||||||
@@ -8745,7 +8745,7 @@ dependencies = [
|
|||||||
"tauri-utils",
|
"tauri-utils",
|
||||||
"url",
|
"url",
|
||||||
"webkit2gtk",
|
"webkit2gtk",
|
||||||
"webview2-com 0.34.0",
|
"webview2-com",
|
||||||
"windows 0.58.0",
|
"windows 0.58.0",
|
||||||
"wry",
|
"wry",
|
||||||
]
|
]
|
||||||
@@ -10225,20 +10225,6 @@ dependencies = [
|
|||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "webview2-com"
|
|
||||||
version = "0.33.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6f61ff3d9d0ee4efcb461b14eb3acfda2702d10dc329f339303fc3e57215ae2c"
|
|
||||||
dependencies = [
|
|
||||||
"webview2-com-macros",
|
|
||||||
"webview2-com-sys 0.33.0",
|
|
||||||
"windows 0.58.0",
|
|
||||||
"windows-core 0.58.0",
|
|
||||||
"windows-implement 0.58.0",
|
|
||||||
"windows-interface 0.58.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "webview2-com"
|
name = "webview2-com"
|
||||||
version = "0.34.0"
|
version = "0.34.0"
|
||||||
@@ -10246,7 +10232,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "823e7ebcfaea51e78f72c87fc3b65a1e602c321f407a0b36dbb327d7bb7cd921"
|
checksum = "823e7ebcfaea51e78f72c87fc3b65a1e602c321f407a0b36dbb327d7bb7cd921"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"webview2-com-macros",
|
"webview2-com-macros",
|
||||||
"webview2-com-sys 0.34.0",
|
"webview2-com-sys",
|
||||||
"windows 0.58.0",
|
"windows 0.58.0",
|
||||||
"windows-core 0.58.0",
|
"windows-core 0.58.0",
|
||||||
"windows-implement 0.58.0",
|
"windows-implement 0.58.0",
|
||||||
@@ -10264,17 +10250,6 @@ dependencies = [
|
|||||||
"syn 2.0.94",
|
"syn 2.0.94",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "webview2-com-sys"
|
|
||||||
version = "0.33.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a3a3e2eeb58f82361c93f9777014668eb3d07e7d174ee4c819575a9208011886"
|
|
||||||
dependencies = [
|
|
||||||
"thiserror 1.0.69",
|
|
||||||
"windows 0.58.0",
|
|
||||||
"windows-core 0.58.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "webview2-com-sys"
|
name = "webview2-com-sys"
|
||||||
version = "0.34.0"
|
version = "0.34.0"
|
||||||
@@ -11021,7 +10996,7 @@ dependencies = [
|
|||||||
"url",
|
"url",
|
||||||
"webkit2gtk",
|
"webkit2gtk",
|
||||||
"webkit2gtk-sys",
|
"webkit2gtk-sys",
|
||||||
"webview2-com 0.34.0",
|
"webview2-com",
|
||||||
"windows 0.58.0",
|
"windows 0.58.0",
|
||||||
"windows-core 0.58.0",
|
"windows-core 0.58.0",
|
||||||
"windows-version",
|
"windows-version",
|
||||||
|
@@ -214,7 +214,7 @@ windows-sys = { version = "0.59", features = [
|
|||||||
"Win32_System_SystemInformation",
|
"Win32_System_SystemInformation",
|
||||||
] }
|
] }
|
||||||
windows-core = "0.58.0"
|
windows-core = "0.58.0"
|
||||||
webview2-com = "0.33"
|
webview2-com = "0.34"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["custom-protocol", "default-meta"]
|
default = ["custom-protocol", "default-meta"]
|
||||||
|
@@ -16,9 +16,9 @@
|
|||||||
"@emotion/styled": "11.14.0",
|
"@emotion/styled": "11.14.0",
|
||||||
"@juggle/resize-observer": "3.4.0",
|
"@juggle/resize-observer": "3.4.0",
|
||||||
"@material/material-color-utilities": "0.3.0",
|
"@material/material-color-utilities": "0.3.0",
|
||||||
"@mui/icons-material": "6.3.0",
|
"@mui/icons-material": "6.3.1",
|
||||||
"@mui/lab": "6.0.0-beta.21",
|
"@mui/lab": "6.0.0-beta.22",
|
||||||
"@mui/material": "6.3.0",
|
"@mui/material": "6.3.1",
|
||||||
"@nyanpasu/interface": "workspace:^",
|
"@nyanpasu/interface": "workspace:^",
|
||||||
"@nyanpasu/ui": "workspace:^",
|
"@nyanpasu/ui": "workspace:^",
|
||||||
"@tanstack/router-zod-adapter": "1.81.5",
|
"@tanstack/router-zod-adapter": "1.81.5",
|
||||||
@@ -54,7 +54,7 @@
|
|||||||
"@emotion/react": "11.14.0",
|
"@emotion/react": "11.14.0",
|
||||||
"@iconify/json": "2.2.291",
|
"@iconify/json": "2.2.291",
|
||||||
"@monaco-editor/react": "4.6.0",
|
"@monaco-editor/react": "4.6.0",
|
||||||
"@tanstack/react-query": "5.62.11",
|
"@tanstack/react-query": "5.62.15",
|
||||||
"@tanstack/react-router": "1.89.2",
|
"@tanstack/react-router": "1.89.2",
|
||||||
"@tanstack/router-devtools": "1.89.2",
|
"@tanstack/router-devtools": "1.89.2",
|
||||||
"@tanstack/router-plugin": "1.87.13",
|
"@tanstack/router-plugin": "1.87.13",
|
||||||
@@ -79,7 +79,7 @@
|
|||||||
"meta-json-schema": "1.19.1",
|
"meta-json-schema": "1.19.1",
|
||||||
"monaco-yaml": "5.2.3",
|
"monaco-yaml": "5.2.3",
|
||||||
"nanoid": "5.0.9",
|
"nanoid": "5.0.9",
|
||||||
"sass-embedded": "1.83.0",
|
"sass-embedded": "1.83.1",
|
||||||
"shiki": "1.26.1",
|
"shiki": "1.26.1",
|
||||||
"tailwindcss-textshadow": "2.1.3",
|
"tailwindcss-textshadow": "2.1.3",
|
||||||
"unplugin-auto-import": "0.19.0",
|
"unplugin-auto-import": "0.19.0",
|
||||||
|
@@ -17,9 +17,9 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@material/material-color-utilities": "0.3.0",
|
"@material/material-color-utilities": "0.3.0",
|
||||||
"@mui/icons-material": "6.3.0",
|
"@mui/icons-material": "6.3.1",
|
||||||
"@mui/lab": "6.0.0-beta.21",
|
"@mui/lab": "6.0.0-beta.22",
|
||||||
"@mui/material": "6.3.0",
|
"@mui/material": "6.3.1",
|
||||||
"@radix-ui/react-portal": "1.1.3",
|
"@radix-ui/react-portal": "1.1.3",
|
||||||
"@radix-ui/react-scroll-area": "1.2.2",
|
"@radix-ui/react-scroll-area": "1.2.2",
|
||||||
"@tauri-apps/api": "2.2.0",
|
"@tauri-apps/api": "2.2.0",
|
||||||
@@ -42,7 +42,7 @@
|
|||||||
"@types/d3-interpolate-path": "2.0.3",
|
"@types/d3-interpolate-path": "2.0.3",
|
||||||
"clsx": "2.1.1",
|
"clsx": "2.1.1",
|
||||||
"d3-interpolate-path": "2.3.0",
|
"d3-interpolate-path": "2.3.0",
|
||||||
"sass-embedded": "1.83.0",
|
"sass-embedded": "1.83.1",
|
||||||
"tailwind-merge": "2.6.0",
|
"tailwind-merge": "2.6.0",
|
||||||
"typescript-plugin-css-modules": "5.1.0",
|
"typescript-plugin-css-modules": "5.1.0",
|
||||||
"vite-plugin-dts": "4.4.0"
|
"vite-plugin-dts": "4.4.0"
|
||||||
|
@@ -61,7 +61,7 @@
|
|||||||
"@commitlint/config-conventional": "19.6.0",
|
"@commitlint/config-conventional": "19.6.0",
|
||||||
"@eslint/compat": "1.2.4",
|
"@eslint/compat": "1.2.4",
|
||||||
"@ianvs/prettier-plugin-sort-imports": "4.4.0",
|
"@ianvs/prettier-plugin-sort-imports": "4.4.0",
|
||||||
"@tauri-apps/cli": "2.2.0",
|
"@tauri-apps/cli": "2.2.2",
|
||||||
"@types/fs-extra": "11.0.4",
|
"@types/fs-extra": "11.0.4",
|
||||||
"@types/lodash-es": "4.17.12",
|
"@types/lodash-es": "4.17.12",
|
||||||
"@types/node": "22.10.5",
|
"@types/node": "22.10.5",
|
||||||
|
504
clash-nyanpasu/pnpm-lock.yaml
generated
504
clash-nyanpasu/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
14
hysteria/app/internal/tun/check_ipv6_others.go
Normal file
14
hysteria/app/internal/tun/check_ipv6_others.go
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
//go:build !unix && !windows
|
||||||
|
|
||||||
|
package tun
|
||||||
|
|
||||||
|
import "net"
|
||||||
|
|
||||||
|
func isIPv6Supported() bool {
|
||||||
|
lis, err := net.ListenPacket("udp6", "[::1]:0")
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
_ = lis.Close()
|
||||||
|
return true
|
||||||
|
}
|
16
hysteria/app/internal/tun/check_ipv6_unix.go
Normal file
16
hysteria/app/internal/tun/check_ipv6_unix.go
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
//go:build unix
|
||||||
|
|
||||||
|
package tun
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
func isIPv6Supported() bool {
|
||||||
|
sock, err := unix.Socket(unix.AF_INET6, unix.SOCK_DGRAM, unix.IPPROTO_UDP)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
_ = unix.Close(sock)
|
||||||
|
return true
|
||||||
|
}
|
24
hysteria/app/internal/tun/check_ipv6_windows.go
Normal file
24
hysteria/app/internal/tun/check_ipv6_windows.go
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
//go:build windows
|
||||||
|
|
||||||
|
package tun
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
func isIPv6Supported() bool {
|
||||||
|
var wsaData windows.WSAData
|
||||||
|
err := windows.WSAStartup(uint32(0x202), &wsaData)
|
||||||
|
if err != nil {
|
||||||
|
// Failing silently: it is not our duty to report such errors
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
defer windows.WSACleanup()
|
||||||
|
|
||||||
|
sock, err := windows.Socket(windows.AF_INET6, windows.SOCK_DGRAM, windows.IPPROTO_UDP)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
_ = windows.Closesocket(sock)
|
||||||
|
return true
|
||||||
|
}
|
@@ -49,6 +49,10 @@ type EventLogger interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) Serve() error {
|
func (s *Server) Serve() error {
|
||||||
|
if !isIPv6Supported() {
|
||||||
|
s.Logger.Warn("tun-pre-check", zap.String("msg", "IPv6 is not supported or enabled on this system, TUN device is created without IPv6 support."))
|
||||||
|
s.Inet6Address = nil
|
||||||
|
}
|
||||||
tunOpts := tun.Options{
|
tunOpts := tun.Options{
|
||||||
Name: s.IfName,
|
Name: s.IfName,
|
||||||
Inet4Address: s.Inet4Address,
|
Inet4Address: s.Inet4Address,
|
||||||
|
@@ -1,4 +1,21 @@
|
|||||||
--- /dev/null
|
--- /dev/null
|
||||||
|
+++ b/arch/arm/dts/rk3399-fine-3399-u-boot.dtsi
|
||||||
|
@@ -0,0 +1,14 @@
|
||||||
|
+// SPDX-License-Identifier: GPL-2.0+
|
||||||
|
+
|
||||||
|
+#include "rk3399-u-boot.dtsi"
|
||||||
|
+#include "rk3399-sdram-lpddr4-100.dtsi"
|
||||||
|
+
|
||||||
|
+/ {
|
||||||
|
+ chosen {
|
||||||
|
+ u-boot,spl-boot-order = "same-as-spl", &sdhci, &sdmmc;
|
||||||
|
+ };
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+&vdd_log {
|
||||||
|
+ regulator-init-microvolt = <950000>;
|
||||||
|
+};
|
||||||
|
--- /dev/null
|
||||||
+++ b/arch/arm/dts/rk3399-fine-3399.dts
|
+++ b/arch/arm/dts/rk3399-fine-3399.dts
|
||||||
@@ -0,0 +1,789 @@
|
@@ -0,0 +1,789 @@
|
||||||
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
||||||
|
@@ -1,22 +1,12 @@
|
|||||||
FROM golang:1.20-alpine AS builder
|
FROM golang:1.20-alpine AS builder
|
||||||
|
|
||||||
RUN apk update && apk add --no-cache git
|
RUN apk update && apk add --no-cache git
|
||||||
|
|
||||||
RUN git clone https://github.com/enfein/mieru.git /build
|
RUN git clone https://github.com/enfein/mieru.git /build
|
||||||
|
|
||||||
WORKDIR /build
|
WORKDIR /build
|
||||||
|
RUN GOOS=linux CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o mita cmd/mita/mita.go
|
||||||
RUN GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -ldflags="-s -w" -o mita cmd/mita/mita.go
|
|
||||||
|
|
||||||
|
|
||||||
FROM alpine AS base
|
FROM alpine AS base
|
||||||
|
|
||||||
COPY --from=builder /build/mita /usr/local/bin/
|
COPY --from=builder /build/mita /usr/local/bin/
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
RUN chmod +x ./start.sh && adduser -H -D -g "" mita && mkdir -p /etc/mita
|
RUN chmod +x ./start.sh && adduser -H -D -g "" mita && mkdir -p /etc/mita
|
||||||
|
|
||||||
CMD ["./start.sh"]
|
CMD ["./start.sh"]
|
@@ -6,9 +6,7 @@ mita run &
|
|||||||
sleep 2
|
sleep 2
|
||||||
|
|
||||||
mita apply config ./conf/config.json
|
mita apply config ./conf/config.json
|
||||||
|
|
||||||
mita start
|
mita start
|
||||||
|
|
||||||
mita describe config
|
mita describe config
|
||||||
|
|
||||||
wait -n
|
wait -n
|
@@ -13,8 +13,6 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
//go:build !android
|
|
||||||
|
|
||||||
package protocol
|
package protocol
|
||||||
|
|
||||||
import "time"
|
import "time"
|
||||||
|
@@ -1,25 +0,0 @@
|
|||||||
// Copyright (C) 2024 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/>.
|
|
||||||
|
|
||||||
//go:build android
|
|
||||||
|
|
||||||
package protocol
|
|
||||||
|
|
||||||
import "time"
|
|
||||||
|
|
||||||
const (
|
|
||||||
// tickInterval is the event trigger interval.
|
|
||||||
tickInterval = 5 * time.Millisecond
|
|
||||||
)
|
|
@@ -21,7 +21,6 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"math"
|
"math"
|
||||||
"net"
|
"net"
|
||||||
"runtime"
|
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
@@ -579,7 +578,8 @@ func (s *Session) runOutputLoop(ctx context.Context) error {
|
|||||||
|
|
||||||
func (s *Session) runOutputOnceStream() {
|
func (s *Session) runOutputOnceStream() {
|
||||||
if s.outputHasErr.Load() {
|
if s.outputHasErr.Load() {
|
||||||
runtime.Gosched()
|
// Can't run output.
|
||||||
|
time.Sleep(tickInterval)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -605,7 +605,8 @@ func (s *Session) runOutputOnceStream() {
|
|||||||
|
|
||||||
func (s *Session) runOutputOncePacket() {
|
func (s *Session) runOutputOncePacket() {
|
||||||
if s.outputHasErr.Load() {
|
if s.outputHasErr.Load() {
|
||||||
runtime.Gosched()
|
// Can't run output.
|
||||||
|
time.Sleep(tickInterval)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -46,9 +46,13 @@ endef
|
|||||||
|
|
||||||
define Package/luci-theme-$(THEME_NAME)/postinst
|
define Package/luci-theme-$(THEME_NAME)/postinst
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
[ -n "$${IPKG_INSTROOT}" ] || {
|
if [ -z "$${IPKG_INSTROOT}" ]; then
|
||||||
( . /etc/uci-defaults/30-luci-theme-$(THEME_NAME) ) && rm -f /etc/uci-defaults/30-luci-theme-$(THEME_NAME)
|
if [ -f /etc/uci-defaults/30-luci-theme-$(THEME_NAME) ]; then
|
||||||
}
|
. /etc/uci-defaults/30-luci-theme-$(THEME_NAME)
|
||||||
|
rm -f /etc/uci-defaults/30-luci-theme-$(THEME_NAME)
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
exit 0
|
||||||
endef
|
endef
|
||||||
|
|
||||||
$(eval $(call BuildPackage,luci-theme-$(THEME_NAME)))
|
$(eval $(call BuildPackage,luci-theme-$(THEME_NAME)))
|
||||||
|
@@ -71,6 +71,9 @@ DBAI (Device Berbasis Arm Indonesia)
|
|||||||
this theme using bootstrap framework + vanilla css
|
this theme using bootstrap framework + vanilla css
|
||||||
icons made by me + flaticons
|
icons made by me + flaticons
|
||||||
|
|
||||||
|
### Attention
|
||||||
|
This theme required luci-theme-alpha-config installed
|
||||||
|
|
||||||
donate
|
donate
|
||||||
buy me a padang rice or coffee
|
buy me a padang rice or coffee
|
||||||
https://saweria.co/derisamedia
|
https://saweria.co/derisamedia
|
||||||
|
@@ -273,7 +273,7 @@ jobs:
|
|||||||
echo "CONFIG_PACKAGE_luci-app-passwall=m" >> .config
|
echo "CONFIG_PACKAGE_luci-app-passwall=m" >> .config
|
||||||
echo "CONFIG_PACKAGE_luci-app-passwall_Iptables_Transparent_Proxy=y" >> .config
|
echo "CONFIG_PACKAGE_luci-app-passwall_Iptables_Transparent_Proxy=y" >> .config
|
||||||
echo "CONFIG_PACKAGE_luci-app-passwall_Nftables_Transparent_Proxy=y" >> .config
|
echo "CONFIG_PACKAGE_luci-app-passwall_Nftables_Transparent_Proxy=y" >> .config
|
||||||
echo "CONFIG_PACKAGE_luci-app-passwall_INCLUDE_ChinaDNS_NG=y" >> .config
|
echo "CONFIG_PACKAGE_luci-app-passwall_INCLUDE_Geoview=y" >> .config
|
||||||
echo "CONFIG_PACKAGE_luci-app-passwall_INCLUDE_Haproxy=y" >> .config
|
echo "CONFIG_PACKAGE_luci-app-passwall_INCLUDE_Haproxy=y" >> .config
|
||||||
echo "CONFIG_PACKAGE_luci-app-passwall_INCLUDE_Hysteria=y" >> .config
|
echo "CONFIG_PACKAGE_luci-app-passwall_INCLUDE_Hysteria=y" >> .config
|
||||||
echo "CONFIG_PACKAGE_luci-app-passwall_INCLUDE_NaiveProxy=y" >> .config
|
echo "CONFIG_PACKAGE_luci-app-passwall_INCLUDE_NaiveProxy=y" >> .config
|
||||||
@@ -288,7 +288,6 @@ jobs:
|
|||||||
echo "CONFIG_PACKAGE_luci-app-passwall_INCLUDE_Trojan_Plus=y" >> .config
|
echo "CONFIG_PACKAGE_luci-app-passwall_INCLUDE_Trojan_Plus=y" >> .config
|
||||||
echo "CONFIG_PACKAGE_luci-app-passwall_INCLUDE_tuic_client=y" >> .config
|
echo "CONFIG_PACKAGE_luci-app-passwall_INCLUDE_tuic_client=y" >> .config
|
||||||
echo "CONFIG_PACKAGE_luci-app-passwall_INCLUDE_V2ray_Geodata=y" >> .config
|
echo "CONFIG_PACKAGE_luci-app-passwall_INCLUDE_V2ray_Geodata=y" >> .config
|
||||||
echo "CONFIG_PACKAGE_luci-app-passwall_INCLUDE_V2ray_Geoview=y" >> .config
|
|
||||||
echo "CONFIG_PACKAGE_luci-app-passwall_INCLUDE_V2ray_Plugin=y" >> .config
|
echo "CONFIG_PACKAGE_luci-app-passwall_INCLUDE_V2ray_Plugin=y" >> .config
|
||||||
echo "CONFIG_PACKAGE_luci-app-passwall_INCLUDE_Xray=y" >> .config
|
echo "CONFIG_PACKAGE_luci-app-passwall_INCLUDE_Xray=y" >> .config
|
||||||
echo "CONFIG_PACKAGE_luci-app-passwall_INCLUDE_Xray_Plugin=y" >> .config
|
echo "CONFIG_PACKAGE_luci-app-passwall_INCLUDE_Xray_Plugin=y" >> .config
|
||||||
|
@@ -12,6 +12,7 @@ PKG_RELEASE:=1
|
|||||||
PKG_CONFIG_DEPENDS:= \
|
PKG_CONFIG_DEPENDS:= \
|
||||||
CONFIG_PACKAGE_$(PKG_NAME)_Iptables_Transparent_Proxy \
|
CONFIG_PACKAGE_$(PKG_NAME)_Iptables_Transparent_Proxy \
|
||||||
CONFIG_PACKAGE_$(PKG_NAME)_Nftables_Transparent_Proxy \
|
CONFIG_PACKAGE_$(PKG_NAME)_Nftables_Transparent_Proxy \
|
||||||
|
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Geoview \
|
||||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Haproxy \
|
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Haproxy \
|
||||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Hysteria \
|
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Hysteria \
|
||||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_NaiveProxy \
|
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_NaiveProxy \
|
||||||
@@ -26,7 +27,6 @@ PKG_CONFIG_DEPENDS:= \
|
|||||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Trojan_Plus \
|
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Trojan_Plus \
|
||||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_tuic_client \
|
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_tuic_client \
|
||||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_V2ray_Geodata \
|
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_V2ray_Geodata \
|
||||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_V2ray_Geoview \
|
|
||||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_V2ray_Plugin \
|
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_V2ray_Plugin \
|
||||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Xray \
|
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Xray \
|
||||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Xray_Plugin
|
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Xray_Plugin
|
||||||
@@ -64,6 +64,11 @@ config PACKAGE_$(PKG_NAME)_Nftables_Transparent_Proxy
|
|||||||
select PACKAGE_kmod-nft-nat
|
select PACKAGE_kmod-nft-nat
|
||||||
default y if PACKAGE_firewall4
|
default y if PACKAGE_firewall4
|
||||||
|
|
||||||
|
config PACKAGE_$(PKG_NAME)_INCLUDE_Geoview
|
||||||
|
bool "Include Geoview"
|
||||||
|
select PACKAGE_geoview
|
||||||
|
default y if aarch64||arm||i386||x86_64
|
||||||
|
|
||||||
config PACKAGE_$(PKG_NAME)_INCLUDE_Haproxy
|
config PACKAGE_$(PKG_NAME)_INCLUDE_Haproxy
|
||||||
bool "Include Haproxy"
|
bool "Include Haproxy"
|
||||||
select PACKAGE_haproxy
|
select PACKAGE_haproxy
|
||||||
@@ -141,11 +146,6 @@ config PACKAGE_$(PKG_NAME)_INCLUDE_V2ray_Geodata
|
|||||||
select PACKAGE_v2ray-geosite
|
select PACKAGE_v2ray-geosite
|
||||||
default n
|
default n
|
||||||
|
|
||||||
config PACKAGE_$(PKG_NAME)_INCLUDE_V2ray_Geoview
|
|
||||||
bool "Include V2ray_Geoview"
|
|
||||||
select PACKAGE_geoview
|
|
||||||
default y if aarch64||arm||i386||x86_64
|
|
||||||
|
|
||||||
config PACKAGE_$(PKG_NAME)_INCLUDE_V2ray_Plugin
|
config PACKAGE_$(PKG_NAME)_INCLUDE_V2ray_Plugin
|
||||||
bool "Include V2ray-Plugin (Shadowsocks Plugin)"
|
bool "Include V2ray-Plugin (Shadowsocks Plugin)"
|
||||||
select PACKAGE_v2ray-plugin
|
select PACKAGE_v2ray-plugin
|
||||||
|
@@ -290,6 +290,9 @@ o:depends({ _tcp_node_bool = "1" })
|
|||||||
o:value("dnsmasq", "Dnsmasq")
|
o:value("dnsmasq", "Dnsmasq")
|
||||||
o:value("chinadns-ng", translate("ChinaDNS-NG (recommended)"))
|
o:value("chinadns-ng", translate("ChinaDNS-NG (recommended)"))
|
||||||
|
|
||||||
|
o = s:option(DummyValue, "view_chinadns_log", " ")
|
||||||
|
o.template = appname .. "/acl/view_chinadns_log"
|
||||||
|
|
||||||
o = s:option(Flag, "filter_proxy_ipv6", translate("Filter Proxy Host IPv6"), translate("Experimental feature."))
|
o = s:option(Flag, "filter_proxy_ipv6", translate("Filter Proxy Host IPv6"), translate("Experimental feature."))
|
||||||
o.default = "0"
|
o.default = "0"
|
||||||
o:depends({ _tcp_node_bool = "1" })
|
o:depends({ _tcp_node_bool = "1" })
|
||||||
@@ -418,6 +421,4 @@ o:value("direct", translate("Direct DNS"))
|
|||||||
o.description = desc .. "</ul>"
|
o.description = desc .. "</ul>"
|
||||||
o:depends({dns_shunt = "dnsmasq", tcp_proxy_mode = "proxy", chn_list = "direct"})
|
o:depends({dns_shunt = "dnsmasq", tcp_proxy_mode = "proxy", chn_list = "direct"})
|
||||||
|
|
||||||
m:append(Template(appname .. "/acl/footer"))
|
|
||||||
|
|
||||||
return m
|
return m
|
||||||
|
@@ -5,12 +5,6 @@ local api = require "luci.passwall.api"
|
|||||||
//<![CDATA[
|
//<![CDATA[
|
||||||
document.addEventListener("DOMContentLoaded", function () {
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
var url = window.location.href;
|
|
||||||
var sid_match = url.match(/\/acl_config\/(cfg[0-9a-f]+)/);
|
|
||||||
var sid = sid_match ? sid_match[1] : null;
|
|
||||||
if (!sid) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var selects = document.querySelectorAll("select[id*='dns_shunt']");
|
var selects = document.querySelectorAll("select[id*='dns_shunt']");
|
||||||
selects.forEach(function (select) {
|
selects.forEach(function (select) {
|
||||||
if (select.value === "chinadns-ng") {
|
if (select.value === "chinadns-ng") {
|
||||||
@@ -32,7 +26,7 @@ local api = require "luci.passwall.api"
|
|||||||
logLink.href = "#";
|
logLink.href = "#";
|
||||||
logLink.className = "log-link";
|
logLink.className = "log-link";
|
||||||
logLink.style.marginLeft = "10px";
|
logLink.style.marginLeft = "10px";
|
||||||
logLink.setAttribute("onclick", "window.open('" + '<%=api.url("get_chinadns_log")%>' + "?flag=" + sid + "', '_blank')");
|
logLink.setAttribute("onclick", "window.open('" + '<%=api.url("get_chinadns_log") .. "?flag=" .. section%>' + "', '_blank')");
|
||||||
select.insertAdjacentElement("afterend", logLink);
|
select.insertAdjacentElement("afterend", logLink);
|
||||||
}
|
}
|
||||||
}, 1000);
|
}, 1000);
|
@@ -34,13 +34,15 @@ $Version = $Version -replace '"'
|
|||||||
$PackageReleasePath = "${PSScriptRoot}\release"
|
$PackageReleasePath = "${PSScriptRoot}\release"
|
||||||
$PackageName = "shadowsocks-v${Version}.${TargetTriple}.zip"
|
$PackageName = "shadowsocks-v${Version}.${TargetTriple}.zip"
|
||||||
$PackagePath = "${PackageReleasePath}\${PackageName}"
|
$PackagePath = "${PackageReleasePath}\${PackageName}"
|
||||||
|
$ReleaseBuildPath = "${PSScriptRoot}\..\target\release"
|
||||||
|
|
||||||
Write-Host $Version
|
Write-Host $Version
|
||||||
Write-Host $PackageReleasePath
|
Write-Host $PackageReleasePath
|
||||||
Write-Host $PackageName
|
Write-Host $PackageName
|
||||||
Write-Host $PackagePath
|
Write-Host $PackagePath
|
||||||
|
Write-Host $ReleaseBuildPath
|
||||||
|
|
||||||
Push-Location "${PSScriptRoot}\..\target\release"
|
Push-Location $ReleaseBuildPath
|
||||||
|
|
||||||
$ProgressPreference = "SilentlyContinue"
|
$ProgressPreference = "SilentlyContinue"
|
||||||
New-Item "${PackageReleasePath}" -ItemType Directory -ErrorAction SilentlyContinue
|
New-Item "${PackageReleasePath}" -ItemType Directory -ErrorAction SilentlyContinue
|
||||||
@@ -48,7 +50,7 @@ $CompressParam = @{
|
|||||||
LiteralPath = "sslocal.exe", "ssserver.exe", "ssurl.exe", "ssmanager.exe", "ssservice.exe"
|
LiteralPath = "sslocal.exe", "ssserver.exe", "ssurl.exe", "ssmanager.exe", "ssservice.exe"
|
||||||
DestinationPath = "${PackagePath}"
|
DestinationPath = "${PackagePath}"
|
||||||
}
|
}
|
||||||
if ([System.IO.File]::Exists("sswinservice.exe")) {
|
if ([System.IO.File]::Exists("$ReleaseBuildPath\sswinservice.exe")) {
|
||||||
$CompressParam.LiteralPath += "sswinservice.exe"
|
$CompressParam.LiteralPath += "sswinservice.exe"
|
||||||
}
|
}
|
||||||
Compress-Archive @CompressParam
|
Compress-Archive @CompressParam
|
||||||
@@ -60,3 +62,5 @@ $PackageHash = (Get-FileHash -Path "${PackagePath}" -Algorithm SHA256).Hash
|
|||||||
"${PackageHash} ${PackageName}" | Out-File -FilePath "${PackageChecksumPath}"
|
"${PackageHash} ${PackageName}" | Out-File -FilePath "${PackageChecksumPath}"
|
||||||
|
|
||||||
Write-Host "Created release packet checksum ${PackageChecksumPath}"
|
Write-Host "Created release packet checksum ${PackageChecksumPath}"
|
||||||
|
|
||||||
|
Pop-Location
|
||||||
|
@@ -174,9 +174,9 @@ return baseclass.extend({
|
|||||||
],
|
],
|
||||||
|
|
||||||
rules_logical_payload_count: {
|
rules_logical_payload_count: {
|
||||||
'AND': 2,
|
'AND': { req: 2, opt: undefined },
|
||||||
'OR': 2,
|
'OR': { req: 2, opt: undefined },
|
||||||
'NOT': 1,
|
'NOT': { req: 1, opt: 0 },
|
||||||
//'SUB-RULE': 0,
|
//'SUB-RULE': 0,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@@ -125,13 +125,20 @@ class RulesEntry {
|
|||||||
return this.payload[n] || {};
|
return this.payload[n] || {};
|
||||||
}
|
}
|
||||||
|
|
||||||
setPayload(n, obj) {
|
getPayloads() {
|
||||||
|
return this.payload || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
setPayload(n, obj, limit) {
|
||||||
this.payload[n] ||= {};
|
this.payload[n] ||= {};
|
||||||
|
|
||||||
Object.keys(obj).forEach((key) => {
|
Object.keys(obj).forEach((key) => {
|
||||||
this.payload[n][key] = obj[key] || null;
|
this.payload[n][key] = obj[key] || null;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (limit)
|
||||||
|
this.payload.splice(limit);
|
||||||
|
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,7 +159,7 @@ class RulesEntry {
|
|||||||
var logical = hm.rules_logical_type.map(e => e[0] || e).includes(this.type),
|
var logical = hm.rules_logical_type.map(e => e[0] || e).includes(this.type),
|
||||||
factor = '';
|
factor = '';
|
||||||
if (logical) {
|
if (logical) {
|
||||||
let n = hm.rules_logical_payload_count[this.type] || 0;
|
let n = hm.rules_logical_payload_count[this.type] ? hm.rules_logical_payload_count[this.type].opt : 0;
|
||||||
factor = '(%s)'.format(this.payload.slice(0, n).map((payload) => {
|
factor = '(%s)'.format(this.payload.slice(0, n).map((payload) => {
|
||||||
return '(%s‚%s)'.format(payload.type || '', payload.factor || '');
|
return '(%s‚%s)'.format(payload.type || '', payload.factor || '');
|
||||||
}).join('ꓹ'));
|
}).join('ꓹ'));
|
||||||
@@ -215,8 +222,35 @@ function renderPayload(s, total, uciconfig) {
|
|||||||
o.rmempty = false;
|
o.rmempty = false;
|
||||||
o.modalonly = true;
|
o.modalonly = true;
|
||||||
}
|
}
|
||||||
|
var initDynamicPayload = function(o, n, key, uciconfig) {
|
||||||
|
o.load = L.bind(function(n, key, uciconfig, section_id) {
|
||||||
|
return new RulesEntry(uci.get(uciconfig, section_id, 'entry')).getPayloads().slice(n).map(e => e[key]);
|
||||||
|
}, o, n, key, uciconfig);
|
||||||
|
o.validate = function(section_id, value) {
|
||||||
|
value = this.formvalue(section_id);
|
||||||
|
var UIEl = this.section.getUIElement(section_id, 'entry');
|
||||||
|
var rule = new RulesEntry(UIEl.getValue());
|
||||||
|
|
||||||
|
let n = this.option.match(/^payload(\d+)_/)[1];
|
||||||
|
let limit = rule.getPayloads().length;
|
||||||
|
value.forEach((val) => {
|
||||||
|
rule.setPayload(n, {factor: val}); n++;
|
||||||
|
});
|
||||||
|
rule.setPayload(limit, {factor: null}, limit);
|
||||||
|
var newvalue = rule.toString();
|
||||||
|
|
||||||
|
UIEl.node.previousSibling.innerText = newvalue;
|
||||||
|
UIEl.setValue(newvalue);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
o.write = function() {};
|
||||||
|
o.rmempty = true;
|
||||||
|
o.modalonly = true;
|
||||||
|
}
|
||||||
|
|
||||||
var o, prefix;
|
var o, prefix;
|
||||||
|
// StaticList payload
|
||||||
for (var n=0; n<total; n++) {
|
for (var n=0; n<total; n++) {
|
||||||
prefix = `payload${n}_`;
|
prefix = `payload${n}_`;
|
||||||
|
|
||||||
@@ -226,7 +260,7 @@ function renderPayload(s, total, uciconfig) {
|
|||||||
o.value.apply(o, res);
|
o.value.apply(o, res);
|
||||||
})
|
})
|
||||||
Object.keys(hm.rules_logical_payload_count).forEach((key) => {
|
Object.keys(hm.rules_logical_payload_count).forEach((key) => {
|
||||||
if (n < hm.rules_logical_payload_count[key])
|
if (n < hm.rules_logical_payload_count[key].req)
|
||||||
o.depends('type', key);
|
o.depends('type', key);
|
||||||
})
|
})
|
||||||
initPayload(o, n, 'type', uciconfig);
|
initPayload(o, n, 'type', uciconfig);
|
||||||
@@ -297,6 +331,68 @@ function renderPayload(s, total, uciconfig) {
|
|||||||
return new RulesEntry(uci.get(uciconfig, section_id, 'entry')).getPayload(n)[key];
|
return new RulesEntry(uci.get(uciconfig, section_id, 'entry')).getPayload(n)[key];
|
||||||
}, o, n, 'factor', uciconfig)
|
}, o, n, 'factor', uciconfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DynamicList payload
|
||||||
|
var extenbox = {};
|
||||||
|
Object.entries(hm.rules_logical_payload_count).filter(e => e[1].opt === undefined).forEach((e) => {
|
||||||
|
let n = e[1].req;
|
||||||
|
if (!Array.isArray(extenbox[n]))
|
||||||
|
extenbox[n] = [];
|
||||||
|
extenbox[n].push(e[0]);
|
||||||
|
})
|
||||||
|
Object.keys(extenbox).forEach((n) => {
|
||||||
|
prefix = `payload${n}_`;
|
||||||
|
|
||||||
|
o = s.option(form.DynamicList, prefix + 'type', _('Type') + ' ++');
|
||||||
|
o.default = hm.rules_type[0][0];
|
||||||
|
hm.rules_type.forEach((res) => {
|
||||||
|
o.value.apply(o, res);
|
||||||
|
})
|
||||||
|
extenbox[n].forEach((type) => {
|
||||||
|
o.depends('type', type);
|
||||||
|
})
|
||||||
|
initDynamicPayload(o, n, 'type', uciconfig);
|
||||||
|
o.validate = function(section_id, value) {
|
||||||
|
value = this.formvalue(section_id);
|
||||||
|
var UIEl = this.section.getUIElement(section_id, 'entry');
|
||||||
|
var rule = new RulesEntry(UIEl.getValue());
|
||||||
|
|
||||||
|
let n = this.option.match(/^payload(\d+)_/)[1];
|
||||||
|
value.forEach((val) => {
|
||||||
|
rule.setPayload(n, {type: val}); n++;
|
||||||
|
});
|
||||||
|
rule.setPayload(n, {factor: null}, n);
|
||||||
|
var newvalue = rule.toString();
|
||||||
|
|
||||||
|
UIEl.node.previousSibling.innerText = newvalue;
|
||||||
|
UIEl.setValue(newvalue);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
o = s.option(form.DynamicList, prefix + 'fused', _('Factor') + ' ++',
|
||||||
|
_('Content will not be verified, Please make sure you enter it correctly.'));
|
||||||
|
o.value('', _('-- Please choose --'));
|
||||||
|
extenbox[n].forEach((type) => {
|
||||||
|
o.depends(Object.fromEntries([['type', type], [prefix + 'type', /.+/]]));
|
||||||
|
})
|
||||||
|
initDynamicPayload(o, n, 'factor', uciconfig);
|
||||||
|
o.load = L.bind(function(n, key, uciconfig, section_id) {
|
||||||
|
let fusedval = [
|
||||||
|
['', _('-- Please choose --')],
|
||||||
|
['NETWORK', '-- NETWORK --'],
|
||||||
|
['udp', _('UDP')],
|
||||||
|
['tcp', _('TCP')],
|
||||||
|
['RULESET', '-- RULE-SET --']
|
||||||
|
];
|
||||||
|
hm.loadRulesetLabel.call(this, null, section_id);
|
||||||
|
this.keylist = [...fusedval.map(e => e[0]), ...this.keylist];
|
||||||
|
this.vallist = [...fusedval.map(e => e[1]), ...this.vallist];
|
||||||
|
this.super('load', section_id);
|
||||||
|
|
||||||
|
return new RulesEntry(uci.get(uciconfig, section_id, 'entry')).getPayloads().slice(n).map(e => e[key]);
|
||||||
|
}, o, n, 'factor', uciconfig)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderRules(s, uciconfig) {
|
function renderRules(s, uciconfig) {
|
||||||
@@ -356,7 +452,7 @@ function renderRules(s, uciconfig) {
|
|||||||
o.rmempty = false;
|
o.rmempty = false;
|
||||||
o.modalonly = true;
|
o.modalonly = true;
|
||||||
|
|
||||||
renderPayload(s, Math.max(...Object.values(hm.rules_logical_payload_count)), uciconfig);
|
renderPayload(s, Math.max(...Object.values(hm.rules_logical_payload_count).map(e => e.req)), uciconfig);
|
||||||
|
|
||||||
o = s.option(form.ListValue, 'detour', _('Proxy group'));
|
o = s.option(form.ListValue, 'detour', _('Proxy group'));
|
||||||
o.renderWidget = function(/* ... */) {
|
o.renderWidget = function(/* ... */) {
|
||||||
@@ -1018,7 +1114,7 @@ return view.extend({
|
|||||||
so.rmempty = false;
|
so.rmempty = false;
|
||||||
so.editable = true;
|
so.editable = true;
|
||||||
|
|
||||||
so = ss.option(form.ListValue, 'proxy', _('Proxy group'),
|
so = ss.option(form.ListValue, 'proxy', _('Proxy group override'),
|
||||||
_('Override the Proxy group of DNS server.'));
|
_('Override the Proxy group of DNS server.'));
|
||||||
so.renderWidget = function(/* ... */) {
|
so.renderWidget = function(/* ... */) {
|
||||||
var frameEl = form.ListValue.prototype.renderWidget.apply(this, arguments);
|
var frameEl = form.ListValue.prototype.renderWidget.apply(this, arguments);
|
||||||
|
@@ -1667,6 +1667,10 @@ msgstr ""
|
|||||||
msgid "Proxy group"
|
msgid "Proxy group"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: htdocs/luci-static/resources/view/fchomo/client.js:1117
|
||||||
|
msgid "Proxy group override"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: htdocs/luci-static/resources/view/fchomo/global.js:450
|
#: htdocs/luci-static/resources/view/fchomo/global.js:450
|
||||||
msgid "Proxy mode"
|
msgid "Proxy mode"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
@@ -1690,6 +1690,10 @@ msgstr "代理链"
|
|||||||
msgid "Proxy group"
|
msgid "Proxy group"
|
||||||
msgstr "代理组"
|
msgstr "代理组"
|
||||||
|
|
||||||
|
#: htdocs/luci-static/resources/view/fchomo/client.js:1117
|
||||||
|
msgid "Proxy group override"
|
||||||
|
msgstr "代理组覆盖"
|
||||||
|
|
||||||
#: htdocs/luci-static/resources/view/fchomo/global.js:450
|
#: htdocs/luci-static/resources/view/fchomo/global.js:450
|
||||||
msgid "Proxy mode"
|
msgid "Proxy mode"
|
||||||
msgstr "代理模式"
|
msgstr "代理模式"
|
||||||
|
@@ -1690,6 +1690,10 @@ msgstr "代理鏈"
|
|||||||
msgid "Proxy group"
|
msgid "Proxy group"
|
||||||
msgstr "代理組"
|
msgstr "代理組"
|
||||||
|
|
||||||
|
#: htdocs/luci-static/resources/view/fchomo/client.js:1117
|
||||||
|
msgid "Proxy group override"
|
||||||
|
msgstr "代理組覆蓋"
|
||||||
|
|
||||||
#: htdocs/luci-static/resources/view/fchomo/global.js:450
|
#: htdocs/luci-static/resources/view/fchomo/global.js:450
|
||||||
msgid "Proxy mode"
|
msgid "Proxy mode"
|
||||||
msgstr "代理模式"
|
msgstr "代理模式"
|
||||||
|
@@ -12,6 +12,7 @@ PKG_RELEASE:=1
|
|||||||
PKG_CONFIG_DEPENDS:= \
|
PKG_CONFIG_DEPENDS:= \
|
||||||
CONFIG_PACKAGE_$(PKG_NAME)_Iptables_Transparent_Proxy \
|
CONFIG_PACKAGE_$(PKG_NAME)_Iptables_Transparent_Proxy \
|
||||||
CONFIG_PACKAGE_$(PKG_NAME)_Nftables_Transparent_Proxy \
|
CONFIG_PACKAGE_$(PKG_NAME)_Nftables_Transparent_Proxy \
|
||||||
|
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Geoview \
|
||||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Haproxy \
|
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Haproxy \
|
||||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Hysteria \
|
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Hysteria \
|
||||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_NaiveProxy \
|
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_NaiveProxy \
|
||||||
@@ -26,7 +27,6 @@ PKG_CONFIG_DEPENDS:= \
|
|||||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Trojan_Plus \
|
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Trojan_Plus \
|
||||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_tuic_client \
|
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_tuic_client \
|
||||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_V2ray_Geodata \
|
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_V2ray_Geodata \
|
||||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_V2ray_Geoview \
|
|
||||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_V2ray_Plugin \
|
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_V2ray_Plugin \
|
||||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Xray \
|
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Xray \
|
||||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Xray_Plugin
|
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Xray_Plugin
|
||||||
@@ -64,6 +64,11 @@ config PACKAGE_$(PKG_NAME)_Nftables_Transparent_Proxy
|
|||||||
select PACKAGE_kmod-nft-nat
|
select PACKAGE_kmod-nft-nat
|
||||||
default y if PACKAGE_firewall4
|
default y if PACKAGE_firewall4
|
||||||
|
|
||||||
|
config PACKAGE_$(PKG_NAME)_INCLUDE_Geoview
|
||||||
|
bool "Include Geoview"
|
||||||
|
select PACKAGE_geoview
|
||||||
|
default y if aarch64||arm||i386||x86_64
|
||||||
|
|
||||||
config PACKAGE_$(PKG_NAME)_INCLUDE_Haproxy
|
config PACKAGE_$(PKG_NAME)_INCLUDE_Haproxy
|
||||||
bool "Include Haproxy"
|
bool "Include Haproxy"
|
||||||
select PACKAGE_haproxy
|
select PACKAGE_haproxy
|
||||||
@@ -141,11 +146,6 @@ config PACKAGE_$(PKG_NAME)_INCLUDE_V2ray_Geodata
|
|||||||
select PACKAGE_v2ray-geosite
|
select PACKAGE_v2ray-geosite
|
||||||
default n
|
default n
|
||||||
|
|
||||||
config PACKAGE_$(PKG_NAME)_INCLUDE_V2ray_Geoview
|
|
||||||
bool "Include V2ray_Geoview"
|
|
||||||
select PACKAGE_geoview
|
|
||||||
default y if aarch64||arm||i386||x86_64
|
|
||||||
|
|
||||||
config PACKAGE_$(PKG_NAME)_INCLUDE_V2ray_Plugin
|
config PACKAGE_$(PKG_NAME)_INCLUDE_V2ray_Plugin
|
||||||
bool "Include V2ray-Plugin (Shadowsocks Plugin)"
|
bool "Include V2ray-Plugin (Shadowsocks Plugin)"
|
||||||
select PACKAGE_v2ray-plugin
|
select PACKAGE_v2ray-plugin
|
||||||
|
@@ -290,6 +290,9 @@ o:depends({ _tcp_node_bool = "1" })
|
|||||||
o:value("dnsmasq", "Dnsmasq")
|
o:value("dnsmasq", "Dnsmasq")
|
||||||
o:value("chinadns-ng", translate("ChinaDNS-NG (recommended)"))
|
o:value("chinadns-ng", translate("ChinaDNS-NG (recommended)"))
|
||||||
|
|
||||||
|
o = s:option(DummyValue, "view_chinadns_log", " ")
|
||||||
|
o.template = appname .. "/acl/view_chinadns_log"
|
||||||
|
|
||||||
o = s:option(Flag, "filter_proxy_ipv6", translate("Filter Proxy Host IPv6"), translate("Experimental feature."))
|
o = s:option(Flag, "filter_proxy_ipv6", translate("Filter Proxy Host IPv6"), translate("Experimental feature."))
|
||||||
o.default = "0"
|
o.default = "0"
|
||||||
o:depends({ _tcp_node_bool = "1" })
|
o:depends({ _tcp_node_bool = "1" })
|
||||||
@@ -418,6 +421,4 @@ o:value("direct", translate("Direct DNS"))
|
|||||||
o.description = desc .. "</ul>"
|
o.description = desc .. "</ul>"
|
||||||
o:depends({dns_shunt = "dnsmasq", tcp_proxy_mode = "proxy", chn_list = "direct"})
|
o:depends({dns_shunt = "dnsmasq", tcp_proxy_mode = "proxy", chn_list = "direct"})
|
||||||
|
|
||||||
m:append(Template(appname .. "/acl/footer"))
|
|
||||||
|
|
||||||
return m
|
return m
|
||||||
|
@@ -5,12 +5,6 @@ local api = require "luci.passwall.api"
|
|||||||
//<![CDATA[
|
//<![CDATA[
|
||||||
document.addEventListener("DOMContentLoaded", function () {
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
var url = window.location.href;
|
|
||||||
var sid_match = url.match(/\/acl_config\/(cfg[0-9a-f]+)/);
|
|
||||||
var sid = sid_match ? sid_match[1] : null;
|
|
||||||
if (!sid) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var selects = document.querySelectorAll("select[id*='dns_shunt']");
|
var selects = document.querySelectorAll("select[id*='dns_shunt']");
|
||||||
selects.forEach(function (select) {
|
selects.forEach(function (select) {
|
||||||
if (select.value === "chinadns-ng") {
|
if (select.value === "chinadns-ng") {
|
||||||
@@ -32,7 +26,7 @@ local api = require "luci.passwall.api"
|
|||||||
logLink.href = "#";
|
logLink.href = "#";
|
||||||
logLink.className = "log-link";
|
logLink.className = "log-link";
|
||||||
logLink.style.marginLeft = "10px";
|
logLink.style.marginLeft = "10px";
|
||||||
logLink.setAttribute("onclick", "window.open('" + '<%=api.url("get_chinadns_log")%>' + "?flag=" + sid + "', '_blank')");
|
logLink.setAttribute("onclick", "window.open('" + '<%=api.url("get_chinadns_log") .. "?flag=" .. section%>' + "', '_blank')");
|
||||||
select.insertAdjacentElement("afterend", logLink);
|
select.insertAdjacentElement("afterend", logLink);
|
||||||
}
|
}
|
||||||
}, 1000);
|
}, 1000);
|
@@ -6,12 +6,12 @@
|
|||||||
include $(TOPDIR)/rules.mk
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
PKG_NAME:=sing-box
|
PKG_NAME:=sing-box
|
||||||
PKG_VERSION:=1.10.5
|
PKG_VERSION:=1.10.6
|
||||||
PKG_RELEASE:=1
|
PKG_RELEASE:=1
|
||||||
|
|
||||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
|
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
|
||||||
PKG_SOURCE_URL:=https://codeload.github.com/SagerNet/sing-box/tar.gz/v$(PKG_VERSION)?
|
PKG_SOURCE_URL:=https://codeload.github.com/SagerNet/sing-box/tar.gz/v$(PKG_VERSION)?
|
||||||
PKG_HASH:=ca0385b45d160c2c2a1d0e09665f4f04caac27cb3dd9d6132173316dfd873b75
|
PKG_HASH:=bb3ca59c848f1509d0e183c54c91716993ce77ab0c9c48a0de0bcd4cc1f0e021
|
||||||
|
|
||||||
PKG_LICENSE:=GPL-3.0-or-later
|
PKG_LICENSE:=GPL-3.0-or-later
|
||||||
PKG_LICENSE_FILES:=LICENSE
|
PKG_LICENSE_FILES:=LICENSE
|
||||||
|
1
v2rayn/.github/workflows/build-linux.yml
vendored
1
v2rayn/.github/workflows/build-linux.yml
vendored
@@ -74,3 +74,4 @@ jobs:
|
|||||||
file: ${{ github.workspace }}/v2rayN*.zip
|
file: ${{ github.workspace }}/v2rayN*.zip
|
||||||
tag: ${{ github.event.inputs.release_tag }}
|
tag: ${{ github.event.inputs.release_tag }}
|
||||||
file_glob: true
|
file_glob: true
|
||||||
|
prerelease: true
|
1
v2rayn/.github/workflows/build-osx.yml
vendored
1
v2rayn/.github/workflows/build-osx.yml
vendored
@@ -75,3 +75,4 @@ jobs:
|
|||||||
file: ${{ github.workspace }}/v2rayN*.zip
|
file: ${{ github.workspace }}/v2rayN*.zip
|
||||||
tag: ${{ github.event.inputs.release_tag }}
|
tag: ${{ github.event.inputs.release_tag }}
|
||||||
file_glob: true
|
file_glob: true
|
||||||
|
prerelease: true
|
1
v2rayn/.github/workflows/build-windows.yml
vendored
1
v2rayn/.github/workflows/build-windows.yml
vendored
@@ -65,3 +65,4 @@ jobs:
|
|||||||
file: ${{ github.workspace }}/v2rayN*.zip
|
file: ${{ github.workspace }}/v2rayN*.zip
|
||||||
tag: ${{ github.event.inputs.release_tag }}
|
tag: ${{ github.event.inputs.release_tag }}
|
||||||
file_glob: true
|
file_glob: true
|
||||||
|
prerelease: true
|
@@ -11,16 +11,16 @@ echo "When this file exists, app will not store configs under this folder" > "$P
|
|||||||
chmod +x "$PackagePath/v2rayN.app/Contents/MacOS/v2rayN"
|
chmod +x "$PackagePath/v2rayN.app/Contents/MacOS/v2rayN"
|
||||||
|
|
||||||
mkdir -p "$PackagePath/icons.iconset"
|
mkdir -p "$PackagePath/icons.iconset"
|
||||||
sips -z 16 16 "$PackagePath/v2rayN.app/Contents/MacOS/v2rayN.png" --out "$PackagePath/icons.iconset/icon_16x16.png"
|
sips -z 16 16 "$PackagePath/v2rayN.app/Contents/MacOS/v2rayN2.png" --out "$PackagePath/icons.iconset/icon_16x16.png"
|
||||||
sips -z 32 32 "$PackagePath/v2rayN.app/Contents/MacOS/v2rayN.png" --out "$PackagePath/icons.iconset/icon_16x16@2x.png"
|
sips -z 32 32 "$PackagePath/v2rayN.app/Contents/MacOS/v2rayN2.png" --out "$PackagePath/icons.iconset/icon_16x16@2x.png"
|
||||||
sips -z 32 32 "$PackagePath/v2rayN.app/Contents/MacOS/v2rayN.png" --out "$PackagePath/icons.iconset/icon_32x32.png"
|
sips -z 32 32 "$PackagePath/v2rayN.app/Contents/MacOS/v2rayN2.png" --out "$PackagePath/icons.iconset/icon_32x32.png"
|
||||||
sips -z 64 64 "$PackagePath/v2rayN.app/Contents/MacOS/v2rayN.png" --out "$PackagePath/icons.iconset/icon_32x32@2x.png"
|
sips -z 64 64 "$PackagePath/v2rayN.app/Contents/MacOS/v2rayN2.png" --out "$PackagePath/icons.iconset/icon_32x32@2x.png"
|
||||||
sips -z 128 128 "$PackagePath/v2rayN.app/Contents/MacOS/v2rayN.png" --out "$PackagePath/icons.iconset/icon_128x128.png"
|
sips -z 128 128 "$PackagePath/v2rayN.app/Contents/MacOS/v2rayN2.png" --out "$PackagePath/icons.iconset/icon_128x128.png"
|
||||||
sips -z 256 256 "$PackagePath/v2rayN.app/Contents/MacOS/v2rayN.png" --out "$PackagePath/icons.iconset/icon_128x128@2x.png"
|
sips -z 256 256 "$PackagePath/v2rayN.app/Contents/MacOS/v2rayN2.png" --out "$PackagePath/icons.iconset/icon_128x128@2x.png"
|
||||||
sips -z 256 256 "$PackagePath/v2rayN.app/Contents/MacOS/v2rayN.png" --out "$PackagePath/icons.iconset/icon_256x256.png"
|
sips -z 256 256 "$PackagePath/v2rayN.app/Contents/MacOS/v2rayN2.png" --out "$PackagePath/icons.iconset/icon_256x256.png"
|
||||||
sips -z 512 512 "$PackagePath/v2rayN.app/Contents/MacOS/v2rayN.png" --out "$PackagePath/icons.iconset/icon_256x256@2x.png"
|
sips -z 512 512 "$PackagePath/v2rayN.app/Contents/MacOS/v2rayN2.png" --out "$PackagePath/icons.iconset/icon_256x256@2x.png"
|
||||||
sips -z 512 512 "$PackagePath/v2rayN.app/Contents/MacOS/v2rayN.png" --out "$PackagePath/icons.iconset/icon_512x512.png"
|
sips -z 512 512 "$PackagePath/v2rayN.app/Contents/MacOS/v2rayN2.png" --out "$PackagePath/icons.iconset/icon_512x512.png"
|
||||||
sips -z 1024 1024 "$PackagePath/v2rayN.app/Contents/MacOS/v2rayN.png" --out "$PackagePath/icons.iconset/icon_512x512@2x.png"
|
sips -z 1024 1024 "$PackagePath/v2rayN.app/Contents/MacOS/v2rayN2.png" --out "$PackagePath/icons.iconset/icon_512x512@2x.png"
|
||||||
iconutil -c icns "$PackagePath/icons.iconset" -o "$PackagePath/v2rayN.app/Contents/Resources/AppIcon.icns"
|
iconutil -c icns "$PackagePath/icons.iconset" -o "$PackagePath/v2rayN.app/Contents/Resources/AppIcon.icns"
|
||||||
|
|
||||||
cat >"$PackagePath/v2rayN.app/Contents/Info.plist" <<-EOF
|
cat >"$PackagePath/v2rayN.app/Contents/Info.plist" <<-EOF
|
||||||
|
@@ -15,8 +15,15 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var fileName = Uri.UnescapeDataString(string.Join(" ", args));
|
var argData = Uri.UnescapeDataString(string.Join(" ", args));
|
||||||
UpgradeApp.Upgrade(fileName);
|
if (argData.Equals("rebootas"))
|
||||||
|
{
|
||||||
|
Thread.Sleep(1000);
|
||||||
|
Utils.StartV2RayN();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
UpgradeApp.Upgrade(argData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -21,11 +21,11 @@ namespace AmazTool
|
|||||||
Console.WriteLine(Resx.Resource.TryTerminateProcess);
|
Console.WriteLine(Resx.Resource.TryTerminateProcess);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var existing = Process.GetProcessesByName(V2rayN);
|
var existing = Process.GetProcessesByName(Utils.V2rayN);
|
||||||
foreach (var pp in existing)
|
foreach (var pp in existing)
|
||||||
{
|
{
|
||||||
var path = pp.MainModule?.FileName ?? "";
|
var path = pp.MainModule?.FileName ?? "";
|
||||||
if (path.StartsWith(GetPath(V2rayN)))
|
if (path.StartsWith(Utils.GetPath(Utils.V2rayN)))
|
||||||
{
|
{
|
||||||
pp?.Kill();
|
pp?.Kill();
|
||||||
pp?.WaitForExit(1000);
|
pp?.WaitForExit(1000);
|
||||||
@@ -42,7 +42,7 @@ namespace AmazTool
|
|||||||
StringBuilder sb = new();
|
StringBuilder sb = new();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
string thisAppOldFile = $"{GetExePath()}.tmp";
|
string thisAppOldFile = $"{Utils.GetExePath()}.tmp";
|
||||||
File.Delete(thisAppOldFile);
|
File.Delete(thisAppOldFile);
|
||||||
string splitKey = "/";
|
string splitKey = "/";
|
||||||
|
|
||||||
@@ -62,12 +62,12 @@ namespace AmazTool
|
|||||||
if (lst.Length == 1) continue;
|
if (lst.Length == 1) continue;
|
||||||
string fullName = string.Join(splitKey, lst[1..lst.Length]);
|
string fullName = string.Join(splitKey, lst[1..lst.Length]);
|
||||||
|
|
||||||
if (string.Equals(GetExePath(), GetPath(fullName), StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(Utils.GetExePath(), Utils.GetPath(fullName), StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
File.Move(GetExePath(), thisAppOldFile);
|
File.Move(Utils.GetExePath(), thisAppOldFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
string entryOutputPath = GetPath(fullName);
|
string entryOutputPath = Utils.GetPath(fullName);
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(entryOutputPath)!);
|
Directory.CreateDirectory(Path.GetDirectoryName(entryOutputPath)!);
|
||||||
entry.ExtractToFile(entryOutputPath, true);
|
entry.ExtractToFile(entryOutputPath, true);
|
||||||
|
|
||||||
@@ -92,39 +92,11 @@ namespace AmazTool
|
|||||||
|
|
||||||
Console.WriteLine(Resx.Resource.Restartv2rayN);
|
Console.WriteLine(Resx.Resource.Restartv2rayN);
|
||||||
Waiting(2);
|
Waiting(2);
|
||||||
Process process = new()
|
|
||||||
{
|
Utils.StartV2RayN();
|
||||||
StartInfo = new()
|
|
||||||
{
|
|
||||||
UseShellExecute = true,
|
|
||||||
FileName = V2rayN,
|
|
||||||
WorkingDirectory = StartupPath()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
process.Start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetExePath()
|
public static void Waiting(int second)
|
||||||
{
|
|
||||||
return Environment.ProcessPath ?? Process.GetCurrentProcess().MainModule?.FileName ?? string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string StartupPath()
|
|
||||||
{
|
|
||||||
return AppDomain.CurrentDomain.BaseDirectory;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetPath(string fileName)
|
|
||||||
{
|
|
||||||
string startupPath = StartupPath();
|
|
||||||
if (string.IsNullOrEmpty(fileName))
|
|
||||||
{
|
|
||||||
return startupPath;
|
|
||||||
}
|
|
||||||
return Path.Combine(startupPath, fileName);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void Waiting(int second)
|
|
||||||
{
|
{
|
||||||
for (var i = second; i > 0; i--)
|
for (var i = second; i > 0; i--)
|
||||||
{
|
{
|
||||||
@@ -132,7 +104,5 @@ namespace AmazTool
|
|||||||
Thread.Sleep(1000);
|
Thread.Sleep(1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string V2rayN => "v2rayN";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
43
v2rayn/v2rayN/AmazTool/Utils.cs
Normal file
43
v2rayn/v2rayN/AmazTool/Utils.cs
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace AmazTool
|
||||||
|
{
|
||||||
|
internal class Utils
|
||||||
|
{
|
||||||
|
public static string GetExePath()
|
||||||
|
{
|
||||||
|
return Environment.ProcessPath ?? Process.GetCurrentProcess().MainModule?.FileName ?? string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string StartupPath()
|
||||||
|
{
|
||||||
|
return AppDomain.CurrentDomain.BaseDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetPath(string fileName)
|
||||||
|
{
|
||||||
|
string startupPath = StartupPath();
|
||||||
|
if (string.IsNullOrEmpty(fileName))
|
||||||
|
{
|
||||||
|
return startupPath;
|
||||||
|
}
|
||||||
|
return Path.Combine(startupPath, fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string V2rayN => "v2rayN";
|
||||||
|
|
||||||
|
public static void StartV2RayN()
|
||||||
|
{
|
||||||
|
Process process = new()
|
||||||
|
{
|
||||||
|
StartInfo = new()
|
||||||
|
{
|
||||||
|
UseShellExecute = true,
|
||||||
|
FileName = V2rayN,
|
||||||
|
WorkingDirectory = StartupPath()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
process.Start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
105
v2rayn/v2rayN/ServiceLib/Common/ProcUtils.cs
Normal file
105
v2rayn/v2rayN/ServiceLib/Common/ProcUtils.cs
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace ServiceLib.Common;
|
||||||
|
|
||||||
|
public static class ProcUtils
|
||||||
|
{
|
||||||
|
private static readonly string _tag = "ProcUtils";
|
||||||
|
|
||||||
|
public static void ProcessStart(string? fileName, string arguments = "")
|
||||||
|
{
|
||||||
|
ProcessStart(fileName, arguments, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int? ProcessStart(string? fileName, string arguments, string? dir)
|
||||||
|
{
|
||||||
|
if (fileName.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (fileName.Contains(' ')) fileName = fileName.AppendQuotes();
|
||||||
|
if (arguments.Contains(' ')) arguments = arguments.AppendQuotes();
|
||||||
|
|
||||||
|
Process process = new()
|
||||||
|
{
|
||||||
|
StartInfo = new ProcessStartInfo
|
||||||
|
{
|
||||||
|
UseShellExecute = true,
|
||||||
|
FileName = fileName,
|
||||||
|
Arguments = arguments,
|
||||||
|
WorkingDirectory = dir
|
||||||
|
}
|
||||||
|
};
|
||||||
|
process.Start();
|
||||||
|
return process.Id;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logging.SaveLog(_tag, ex);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void RebootAsAdmin(bool blAdmin = true)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ProcessStartInfo startInfo = new()
|
||||||
|
{
|
||||||
|
UseShellExecute = true,
|
||||||
|
Arguments = Global.RebootAs,
|
||||||
|
WorkingDirectory = Utils.StartupPath(),
|
||||||
|
FileName = Utils.GetExePath().AppendQuotes(),
|
||||||
|
Verb = blAdmin ? "runas" : null,
|
||||||
|
};
|
||||||
|
Process.Start(startInfo);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logging.SaveLog(_tag, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task ProcessKill(int pid)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await ProcessKill(Process.GetProcessById(pid), false);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logging.SaveLog(_tag, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task ProcessKill(Process? proc, bool review)
|
||||||
|
{
|
||||||
|
if (proc is null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var fileName = review ? proc?.MainModule?.FileName : null;
|
||||||
|
var processName = review ? proc?.ProcessName : null;
|
||||||
|
|
||||||
|
try { proc?.Kill(true); } catch (Exception ex) { Logging.SaveLog(_tag, ex); }
|
||||||
|
try { proc?.Kill(); } catch (Exception ex) { Logging.SaveLog(_tag, ex); }
|
||||||
|
try { proc?.Close(); } catch (Exception ex) { Logging.SaveLog(_tag, ex); }
|
||||||
|
try { proc?.Dispose(); } catch (Exception ex) { Logging.SaveLog(_tag, ex); }
|
||||||
|
|
||||||
|
await Task.Delay(300);
|
||||||
|
if (review && fileName != null)
|
||||||
|
{
|
||||||
|
var proc2 = Process.GetProcessesByName(processName)
|
||||||
|
.FirstOrDefault(t => t.MainModule?.FileName == fileName);
|
||||||
|
if (proc2 != null)
|
||||||
|
{
|
||||||
|
Logging.SaveLog($"{_tag}, KillProcess not completing the job");
|
||||||
|
await ProcessKill(proc2, false);
|
||||||
|
proc2 = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -517,10 +517,10 @@ namespace ServiceLib.Common
|
|||||||
|
|
||||||
#region 杂项
|
#region 杂项
|
||||||
|
|
||||||
public static bool UpgradeAppExists(out string fileName)
|
public static bool UpgradeAppExists(out string upgradeFileName)
|
||||||
{
|
{
|
||||||
fileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, GetExeName("AmazTool"));
|
upgradeFileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, GetExeName("AmazTool"));
|
||||||
return File.Exists(fileName);
|
return File.Exists(upgradeFileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -591,26 +591,6 @@ namespace ServiceLib.Common
|
|||||||
return Guid.TryParse(strSrc, out _);
|
return Guid.TryParse(strSrc, out _);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ProcessStart(string? fileName, string arguments = "")
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (fileName.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fileName.Contains(' ')) fileName = fileName.AppendQuotes();
|
|
||||||
if (arguments.Contains(' ')) arguments = arguments.AppendQuotes();
|
|
||||||
|
|
||||||
Process.Start(new ProcessStartInfo(fileName, arguments) { UseShellExecute = true });
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logging.SaveLog(_tag, ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Dictionary<string, string> GetSystemHosts()
|
public static Dictionary<string, string> GetSystemHosts()
|
||||||
{
|
{
|
||||||
var systemHosts = new Dictionary<string, string>();
|
var systemHosts = new Dictionary<string, string>();
|
||||||
|
@@ -32,9 +32,9 @@ namespace ServiceLib.Handler
|
|||||||
{
|
{
|
||||||
if (it.CoreType == ECoreType.v2rayN)
|
if (it.CoreType == ECoreType.v2rayN)
|
||||||
{
|
{
|
||||||
if (Utils.UpgradeAppExists(out var fileName))
|
if (Utils.UpgradeAppExists(out var upgradeFileName))
|
||||||
{
|
{
|
||||||
await Utils.SetLinuxChmod(fileName);
|
await Utils.SetLinuxChmod(upgradeFileName);
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -55,7 +55,7 @@ namespace ServiceLib.Handler
|
|||||||
{
|
{
|
||||||
if (node == null)
|
if (node == null)
|
||||||
{
|
{
|
||||||
ShowMsg(false, ResUI.CheckServerSettings);
|
UpdateFunc(false, ResUI.CheckServerSettings);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,13 +63,13 @@ namespace ServiceLib.Handler
|
|||||||
var result = await CoreConfigHandler.GenerateClientConfig(node, fileName);
|
var result = await CoreConfigHandler.GenerateClientConfig(node, fileName);
|
||||||
if (result.Success != true)
|
if (result.Success != true)
|
||||||
{
|
{
|
||||||
ShowMsg(true, result.Msg);
|
UpdateFunc(true, result.Msg);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ShowMsg(true, $"{node.GetSummary()}");
|
UpdateFunc(true, $"{node.GetSummary()}");
|
||||||
ShowMsg(false, $"{Utils.GetRuntimeInfo()}");
|
UpdateFunc(false, $"{Utils.GetRuntimeInfo()}");
|
||||||
ShowMsg(false, string.Format(ResUI.StartService, DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")));
|
UpdateFunc(false, string.Format(ResUI.StartService, DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")));
|
||||||
await CoreStop();
|
await CoreStop();
|
||||||
await Task.Delay(100);
|
await Task.Delay(100);
|
||||||
await CoreStart(node);
|
await CoreStart(node);
|
||||||
@@ -81,15 +81,23 @@ namespace ServiceLib.Handler
|
|||||||
var coreType = selecteds.Exists(t => t.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.WireGuard) ? ECoreType.sing_box : ECoreType.Xray;
|
var coreType = selecteds.Exists(t => t.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.WireGuard) ? ECoreType.sing_box : ECoreType.Xray;
|
||||||
var configPath = Utils.GetConfigPath(Global.CoreSpeedtestConfigFileName);
|
var configPath = Utils.GetConfigPath(Global.CoreSpeedtestConfigFileName);
|
||||||
var result = await CoreConfigHandler.GenerateClientSpeedtestConfig(_config, configPath, selecteds, coreType);
|
var result = await CoreConfigHandler.GenerateClientSpeedtestConfig(_config, configPath, selecteds, coreType);
|
||||||
ShowMsg(false, result.Msg);
|
UpdateFunc(false, result.Msg);
|
||||||
if (result.Success != true)
|
if (result.Success != true)
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ShowMsg(false, string.Format(ResUI.StartService, DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")));
|
UpdateFunc(false, string.Format(ResUI.StartService, DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")));
|
||||||
ShowMsg(false, configPath);
|
UpdateFunc(false, configPath);
|
||||||
return await CoreStartSpeedtest(configPath, coreType);
|
|
||||||
|
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(coreType);
|
||||||
|
var proc = await RunProcess(coreInfo, Global.CoreSpeedtestConfigFileName, true, false);
|
||||||
|
if (proc is null)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return proc.Id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task CoreStop()
|
public async Task CoreStop()
|
||||||
@@ -98,13 +106,13 @@ namespace ServiceLib.Handler
|
|||||||
{
|
{
|
||||||
if (_process != null)
|
if (_process != null)
|
||||||
{
|
{
|
||||||
await KillProcess(_process, true);
|
await ProcUtils.ProcessKill(_process, true);
|
||||||
_process = null;
|
_process = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_processPre != null)
|
if (_processPre != null)
|
||||||
{
|
{
|
||||||
await KillProcess(_processPre, true);
|
await ProcUtils.ProcessKill(_processPre, true);
|
||||||
_processPre = null;
|
_processPre = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,41 +128,8 @@ namespace ServiceLib.Handler
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task CoreStopPid(int pid)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await KillProcess(Process.GetProcessById(pid), false);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logging.SaveLog(_tag, ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#region Private
|
#region Private
|
||||||
|
|
||||||
private string CoreFindExe(CoreInfo coreInfo)
|
|
||||||
{
|
|
||||||
var fileName = string.Empty;
|
|
||||||
foreach (var name in coreInfo.CoreExes)
|
|
||||||
{
|
|
||||||
var vName = Utils.GetBinPath(Utils.GetExeName(name), coreInfo.CoreType.ToString());
|
|
||||||
if (File.Exists(vName))
|
|
||||||
{
|
|
||||||
fileName = vName;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (Utils.IsNullOrEmpty(fileName))
|
|
||||||
{
|
|
||||||
var msg = string.Format(ResUI.NotFoundCore, Utils.GetBinPath("", coreInfo.CoreType.ToString()), string.Join(", ", coreInfo.CoreExes.ToArray()), coreInfo.Url);
|
|
||||||
Logging.SaveLog(msg);
|
|
||||||
ShowMsg(false, msg);
|
|
||||||
}
|
|
||||||
return fileName;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task CoreStart(ProfileItem node)
|
private async Task CoreStart(ProfileItem node)
|
||||||
{
|
{
|
||||||
var coreType = _config.RunningCoreType = AppHandler.Instance.GetCoreType(node, node.ConfigType);
|
var coreType = _config.RunningCoreType = AppHandler.Instance.GetCoreType(node, node.ConfigType);
|
||||||
@@ -194,28 +169,7 @@ namespace ServiceLib.Handler
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<int> CoreStartSpeedtest(string configPath, ECoreType coreType)
|
private void UpdateFunc(bool notify, string msg)
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(coreType);
|
|
||||||
var proc = await RunProcess(coreInfo, Global.CoreSpeedtestConfigFileName, true, false);
|
|
||||||
if (proc is null)
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return proc.Id;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logging.SaveLog(_tag, ex);
|
|
||||||
ShowMsg(false, ex.Message);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ShowMsg(bool notify, string msg)
|
|
||||||
{
|
{
|
||||||
_updateFunc?.Invoke(notify, msg);
|
_updateFunc?.Invoke(notify, msg);
|
||||||
}
|
}
|
||||||
@@ -233,11 +187,12 @@ namespace ServiceLib.Handler
|
|||||||
|
|
||||||
#region Process
|
#region Process
|
||||||
|
|
||||||
private async Task<Process?> RunProcess(CoreInfo coreInfo, string configPath, bool displayLog, bool mayNeedSudo)
|
private async Task<Process?> RunProcess(CoreInfo? coreInfo, string configPath, bool displayLog, bool mayNeedSudo)
|
||||||
{
|
{
|
||||||
var fileName = CoreFindExe(coreInfo);
|
var fileName = CoreInfoHandler.Instance.GetCoreExecFile(coreInfo, out var msg);
|
||||||
if (Utils.IsNullOrEmpty(fileName))
|
if (Utils.IsNullOrEmpty(fileName))
|
||||||
{
|
{
|
||||||
|
UpdateFunc(false, msg);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -272,12 +227,12 @@ namespace ServiceLib.Handler
|
|||||||
proc.OutputDataReceived += (sender, e) =>
|
proc.OutputDataReceived += (sender, e) =>
|
||||||
{
|
{
|
||||||
if (Utils.IsNullOrEmpty(e.Data)) return;
|
if (Utils.IsNullOrEmpty(e.Data)) return;
|
||||||
ShowMsg(false, e.Data + Environment.NewLine);
|
UpdateFunc(false, e.Data + Environment.NewLine);
|
||||||
};
|
};
|
||||||
proc.ErrorDataReceived += (sender, e) =>
|
proc.ErrorDataReceived += (sender, e) =>
|
||||||
{
|
{
|
||||||
if (Utils.IsNullOrEmpty(e.Data)) return;
|
if (Utils.IsNullOrEmpty(e.Data)) return;
|
||||||
ShowMsg(false, e.Data + Environment.NewLine);
|
UpdateFunc(false, e.Data + Environment.NewLine);
|
||||||
|
|
||||||
if (!startUpSuccessful)
|
if (!startUpSuccessful)
|
||||||
{
|
{
|
||||||
@@ -319,40 +274,11 @@ namespace ServiceLib.Handler
|
|||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Logging.SaveLog(_tag, ex);
|
Logging.SaveLog(_tag, ex);
|
||||||
ShowMsg(true, ex.Message);
|
UpdateFunc(true, ex.Message);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task KillProcess(Process? proc, bool review)
|
|
||||||
{
|
|
||||||
if (proc is null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var fileName = proc?.MainModule?.FileName;
|
|
||||||
var processName = proc?.ProcessName;
|
|
||||||
|
|
||||||
try { proc?.Kill(true); } catch (Exception ex) { Logging.SaveLog(_tag, ex); }
|
|
||||||
try { proc?.Kill(); } catch (Exception ex) { Logging.SaveLog(_tag, ex); }
|
|
||||||
try { proc?.Close(); } catch (Exception ex) { Logging.SaveLog(_tag, ex); }
|
|
||||||
try { proc?.Dispose(); } catch (Exception ex) { Logging.SaveLog(_tag, ex); }
|
|
||||||
|
|
||||||
await Task.Delay(500);
|
|
||||||
if (review)
|
|
||||||
{
|
|
||||||
var proc2 = Process.GetProcessesByName(processName)
|
|
||||||
.FirstOrDefault(t => t.MainModule?.FileName == fileName);
|
|
||||||
if (proc2 != null)
|
|
||||||
{
|
|
||||||
Logging.SaveLog($"{_tag}, KillProcess not completing the job");
|
|
||||||
await KillProcess(proc2, false);
|
|
||||||
proc2 = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion Process
|
#endregion Process
|
||||||
|
|
||||||
#region Linux
|
#region Linux
|
||||||
|
@@ -29,6 +29,27 @@
|
|||||||
return _coreInfo ?? [];
|
return _coreInfo ?? [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string GetCoreExecFile(CoreInfo? coreInfo, out string msg)
|
||||||
|
{
|
||||||
|
var fileName = string.Empty;
|
||||||
|
msg = string.Empty;
|
||||||
|
foreach (var name in coreInfo?.CoreExes)
|
||||||
|
{
|
||||||
|
var vName = Utils.GetBinPath(Utils.GetExeName(name), coreInfo.CoreType.ToString());
|
||||||
|
if (File.Exists(vName))
|
||||||
|
{
|
||||||
|
fileName = vName;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (fileName.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
msg = string.Format(ResUI.NotFoundCore, Utils.GetBinPath("", coreInfo.CoreType.ToString()), string.Join(", ", coreInfo.CoreExes.ToArray()), coreInfo.Url);
|
||||||
|
Logging.SaveLog(msg);
|
||||||
|
}
|
||||||
|
return fileName;
|
||||||
|
}
|
||||||
|
|
||||||
private void InitCoreInfo()
|
private void InitCoreInfo()
|
||||||
{
|
{
|
||||||
_coreInfo = [];
|
_coreInfo = [];
|
||||||
|
@@ -4,6 +4,15 @@
|
|||||||
"proxy.example.com": "127.0.0.1"
|
"proxy.example.com": "127.0.0.1"
|
||||||
},
|
},
|
||||||
"servers": [
|
"servers": [
|
||||||
|
{
|
||||||
|
"address": "1.1.1.1",
|
||||||
|
"domains": [
|
||||||
|
"geosite:geolocation-!cn"
|
||||||
|
],
|
||||||
|
"expectIPs": [
|
||||||
|
"geoip:!cn"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"address": "223.5.5.5",
|
"address": "223.5.5.5",
|
||||||
"domains": [
|
"domains": [
|
||||||
@@ -13,7 +22,6 @@
|
|||||||
"geoip:cn"
|
"geoip:cn"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"1.1.1.1",
|
|
||||||
"8.8.8.8",
|
"8.8.8.8",
|
||||||
"https://dns.google/dns-query"
|
"https://dns.google/dns-query"
|
||||||
]
|
]
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<Version>7.5.1</Version>
|
<Version>7.5.2</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@@ -216,7 +216,7 @@ namespace ServiceLib.Services
|
|||||||
{
|
{
|
||||||
if (pid > 0)
|
if (pid > 0)
|
||||||
{
|
{
|
||||||
await CoreHandler.Instance.CoreStopPid(pid);
|
await ProcUtils.ProcessKill(pid);
|
||||||
}
|
}
|
||||||
await ProfileExHandler.Instance.SaveTo();
|
await ProfileExHandler.Instance.SaveTo();
|
||||||
}
|
}
|
||||||
@@ -278,7 +278,7 @@ namespace ServiceLib.Services
|
|||||||
|
|
||||||
if (pid > 0)
|
if (pid > 0)
|
||||||
{
|
{
|
||||||
await CoreHandler.Instance.CoreStopPid(pid);
|
await ProcUtils.ProcessKill(pid);
|
||||||
}
|
}
|
||||||
await ProfileExHandler.Instance.SaveTo();
|
await ProfileExHandler.Instance.SaveTo();
|
||||||
}
|
}
|
||||||
@@ -342,7 +342,7 @@ namespace ServiceLib.Services
|
|||||||
|
|
||||||
if (pid > 0)
|
if (pid > 0)
|
||||||
{
|
{
|
||||||
await CoreHandler.Instance.CoreStopPid(pid);
|
await ProcUtils.ProcessKill(pid);
|
||||||
}
|
}
|
||||||
await ProfileExHandler.Instance.SaveTo();
|
await ProfileExHandler.Instance.SaveTo();
|
||||||
}
|
}
|
||||||
|
@@ -103,7 +103,7 @@ namespace ServiceLib.ViewModels
|
|||||||
address = Utils.GetConfigPath(address);
|
address = Utils.GetConfigPath(address);
|
||||||
if (File.Exists(address))
|
if (File.Exists(address))
|
||||||
{
|
{
|
||||||
Utils.ProcessStart(address);
|
ProcUtils.ProcessStart(address);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@@ -130,11 +130,6 @@ namespace ServiceLib.ViewModels
|
|||||||
DisplayOperationMsg(ResUI.LocalRestoreInvalidZipTips);
|
DisplayOperationMsg(ResUI.LocalRestoreInvalidZipTips);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!Utils.UpgradeAppExists(out _))
|
|
||||||
{
|
|
||||||
DisplayOperationMsg(ResUI.UpgradeAppNotExistTip);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//backup first
|
//backup first
|
||||||
var fileBackup = Utils.GetBackupPath(BackupFileName);
|
var fileBackup = Utils.GetBackupPath(BackupFileName);
|
||||||
@@ -150,13 +145,17 @@ namespace ServiceLib.ViewModels
|
|||||||
|
|
||||||
if (Utils.IsWindows())
|
if (Utils.IsWindows())
|
||||||
{
|
{
|
||||||
service?.RebootAsAdmin(false);
|
ProcUtils.RebootAsAdmin(false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
service?.Shutdown();
|
if (Utils.UpgradeAppExists(out var upgradeFileName))
|
||||||
|
{
|
||||||
|
ProcUtils.ProcessStart(upgradeFileName, Global.RebootAs, Utils.StartupPath());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
service?.Shutdown();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
DisplayOperationMsg(WebDavHandler.Instance.GetLastError());
|
DisplayOperationMsg(WebDavHandler.Instance.GetLastError());
|
||||||
|
@@ -1,9 +1,7 @@
|
|||||||
using ReactiveUI;
|
using ReactiveUI;
|
||||||
using ReactiveUI.Fody.Helpers;
|
using ReactiveUI.Fody.Helpers;
|
||||||
using Splat;
|
using Splat;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Reactive;
|
using System.Reactive;
|
||||||
using System.Reactive.Linq;
|
|
||||||
|
|
||||||
namespace ServiceLib.ViewModels
|
namespace ServiceLib.ViewModels
|
||||||
{
|
{
|
||||||
@@ -312,27 +310,16 @@ namespace ServiceLib.ViewModels
|
|||||||
|
|
||||||
public async Task UpgradeApp(string arg)
|
public async Task UpgradeApp(string arg)
|
||||||
{
|
{
|
||||||
if (!Utils.UpgradeAppExists(out var fileName))
|
if (!Utils.UpgradeAppExists(out var upgradeFileName))
|
||||||
{
|
{
|
||||||
NoticeHandler.Instance.SendMessageAndEnqueue(ResUI.UpgradeAppNotExistTip);
|
NoticeHandler.Instance.SendMessageAndEnqueue(ResUI.UpgradeAppNotExistTip);
|
||||||
Logging.SaveLog("UpgradeApp does not exist");
|
Logging.SaveLog("UpgradeApp does not exist");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Process process = new()
|
var id = ProcUtils.ProcessStart(upgradeFileName, arg, Utils.StartupPath());
|
||||||
|
if (id > 0)
|
||||||
{
|
{
|
||||||
StartInfo = new ProcessStartInfo
|
|
||||||
{
|
|
||||||
UseShellExecute = true,
|
|
||||||
FileName = fileName,
|
|
||||||
Arguments = arg.AppendQuotes(),
|
|
||||||
WorkingDirectory = Utils.StartupPath()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
process.Start();
|
|
||||||
if (process.Id > 0)
|
|
||||||
{
|
|
||||||
await MyAppExitAsync(false);
|
|
||||||
await MyAppExitAsync(false);
|
await MyAppExitAsync(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -513,23 +500,11 @@ namespace ServiceLib.ViewModels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task RebootAsAdmin(bool blAdmin = true)
|
public async Task RebootAsAdmin()
|
||||||
{
|
{
|
||||||
try
|
ProcUtils.RebootAsAdmin();
|
||||||
{
|
|
||||||
ProcessStartInfo startInfo = new()
|
|
||||||
{
|
|
||||||
UseShellExecute = true,
|
|
||||||
Arguments = Global.RebootAs,
|
|
||||||
WorkingDirectory = Utils.StartupPath(),
|
|
||||||
FileName = Utils.GetExePath().AppendQuotes(),
|
|
||||||
Verb = blAdmin ? "runas" : null,
|
|
||||||
};
|
|
||||||
Process.Start(startInfo);
|
|
||||||
await MyAppExitAsync(false);
|
await MyAppExitAsync(false);
|
||||||
}
|
}
|
||||||
catch { }
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task ClearServerStatistics()
|
private async Task ClearServerStatistics()
|
||||||
{
|
{
|
||||||
@@ -542,15 +517,15 @@ namespace ServiceLib.ViewModels
|
|||||||
var path = Utils.StartupPath();
|
var path = Utils.StartupPath();
|
||||||
if (Utils.IsWindows())
|
if (Utils.IsWindows())
|
||||||
{
|
{
|
||||||
Utils.ProcessStart(path);
|
ProcUtils.ProcessStart(path);
|
||||||
}
|
}
|
||||||
else if (Utils.IsLinux())
|
else if (Utils.IsLinux())
|
||||||
{
|
{
|
||||||
Utils.ProcessStart("nautilus", path);
|
ProcUtils.ProcessStart("nautilus", path);
|
||||||
}
|
}
|
||||||
else if (Utils.IsOSX())
|
else if (Utils.IsOSX())
|
||||||
{
|
{
|
||||||
Utils.ProcessStart("open", path);
|
ProcUtils.ProcessStart("open", path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -65,12 +65,12 @@ namespace v2rayN.Desktop.Views
|
|||||||
|
|
||||||
private void linkDnsObjectDoc_Click(object? sender, RoutedEventArgs e)
|
private void linkDnsObjectDoc_Click(object? sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
Utils.ProcessStart("https://xtls.github.io/config/dns.html#dnsobject");
|
ProcUtils.ProcessStart("https://xtls.github.io/config/dns.html#dnsobject");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void linkDnsSingboxObjectDoc_Click(object? sender, RoutedEventArgs e)
|
private void linkDnsSingboxObjectDoc_Click(object? sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
Utils.ProcessStart("https://sing-box.sagernet.org/zh/configuration/dns/");
|
ProcUtils.ProcessStart("https://sing-box.sagernet.org/zh/configuration/dns/");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -330,12 +330,12 @@ namespace v2rayN.Desktop.Views
|
|||||||
|
|
||||||
private void menuPromotion_Click(object? sender, RoutedEventArgs e)
|
private void menuPromotion_Click(object? sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
Utils.ProcessStart($"{Utils.Base64Decode(Global.PromotionUrl)}?t={DateTime.Now.Ticks}");
|
ProcUtils.ProcessStart($"{Utils.Base64Decode(Global.PromotionUrl)}?t={DateTime.Now.Ticks}");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void menuSettingsSetUWP_Click(object? sender, RoutedEventArgs e)
|
private void menuSettingsSetUWP_Click(object? sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
Utils.ProcessStart(Utils.GetBinPath("EnableLoopback.exe"));
|
ProcUtils.ProcessStart(Utils.GetBinPath("EnableLoopback.exe"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task ScanScreenTaskAsync()
|
public async Task ScanScreenTaskAsync()
|
||||||
@@ -481,7 +481,7 @@ namespace v2rayN.Desktop.Views
|
|||||||
{
|
{
|
||||||
if (sender is MenuItem item)
|
if (sender is MenuItem item)
|
||||||
{
|
{
|
||||||
Utils.ProcessStart(item.Tag?.ToString());
|
ProcUtils.ProcessStart(item.Tag?.ToString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -95,7 +95,7 @@ namespace v2rayN.Desktop.Views
|
|||||||
|
|
||||||
private void linkRuleobjectDoc_Click(object? sender, RoutedEventArgs e)
|
private void linkRuleobjectDoc_Click(object? sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
Utils.ProcessStart("https://xtls.github.io/config/routing.html#ruleobject");
|
ProcUtils.ProcessStart("https://xtls.github.io/config/routing.html#ruleobject");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -203,7 +203,7 @@ namespace v2rayN.Desktop.Views
|
|||||||
|
|
||||||
private void linkCustomRulesetPath4Singbox(object? sender, RoutedEventArgs e)
|
private void linkCustomRulesetPath4Singbox(object? sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
Utils.ProcessStart("https://github.com/2dust/v2rayCustomRoutingList/blob/master/singbox_custom_ruleset_example.json");
|
ProcUtils.ProcessStart("https://github.com/2dust/v2rayCustomRoutingList/blob/master/singbox_custom_ruleset_example.json");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -117,12 +117,12 @@ namespace v2rayN.Desktop.Views
|
|||||||
|
|
||||||
private void linkdomainStrategy_Click(object? sender, RoutedEventArgs e)
|
private void linkdomainStrategy_Click(object? sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
Utils.ProcessStart("https://xtls.github.io/config/routing.html");
|
ProcUtils.ProcessStart("https://xtls.github.io/config/routing.html");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void linkdomainStrategy4Singbox_Click(object? sender, RoutedEventArgs e)
|
private void linkdomainStrategy4Singbox_Click(object? sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
Utils.ProcessStart("https://sing-box.sagernet.org/zh/configuration/shared/listen/#domain_strategy");
|
ProcUtils.ProcessStart("https://sing-box.sagernet.org/zh/configuration/shared/listen/#domain_strategy");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void btnCancel_Click(object? sender, RoutedEventArgs e)
|
private void btnCancel_Click(object? sender, RoutedEventArgs e)
|
||||||
|
@@ -48,6 +48,9 @@
|
|||||||
<None Update="v2rayN.png">
|
<None Update="v2rayN.png">
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
|
<None Update="v2rayN2.png">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
BIN
v2rayn/v2rayN/v2rayN.Desktop/v2rayN2.png
Normal file
BIN
v2rayn/v2rayN/v2rayN.Desktop/v2rayN2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 10 KiB |
@@ -66,12 +66,12 @@ namespace v2rayN.Views
|
|||||||
|
|
||||||
private void linkDnsObjectDoc_Click(object sender, RoutedEventArgs e)
|
private void linkDnsObjectDoc_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
Utils.ProcessStart("https://xtls.github.io/config/dns.html#dnsobject");
|
ProcUtils.ProcessStart("https://xtls.github.io/config/dns.html#dnsobject");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void linkDnsSingboxObjectDoc_Click(object sender, RoutedEventArgs e)
|
private void linkDnsSingboxObjectDoc_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
Utils.ProcessStart("https://sing-box.sagernet.org/zh/configuration/dns/");
|
ProcUtils.ProcessStart("https://sing-box.sagernet.org/zh/configuration/dns/");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -308,12 +308,12 @@ namespace v2rayN.Views
|
|||||||
|
|
||||||
private void menuPromotion_Click(object sender, RoutedEventArgs e)
|
private void menuPromotion_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
Utils.ProcessStart($"{Utils.Base64Decode(Global.PromotionUrl)}?t={DateTime.Now.Ticks}");
|
ProcUtils.ProcessStart($"{Utils.Base64Decode(Global.PromotionUrl)}?t={DateTime.Now.Ticks}");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void menuSettingsSetUWP_Click(object sender, RoutedEventArgs e)
|
private void menuSettingsSetUWP_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
Utils.ProcessStart(Utils.GetBinPath("EnableLoopback.exe"));
|
ProcUtils.ProcessStart(Utils.GetBinPath("EnableLoopback.exe"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ScanScreenTaskAsync()
|
private async Task ScanScreenTaskAsync()
|
||||||
@@ -443,7 +443,7 @@ namespace v2rayN.Views
|
|||||||
{
|
{
|
||||||
if (sender is MenuItem item)
|
if (sender is MenuItem item)
|
||||||
{
|
{
|
||||||
Utils.ProcessStart(item.Tag.ToString());
|
ProcUtils.ProcessStart(item.Tag.ToString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -89,7 +89,7 @@ namespace v2rayN.Views
|
|||||||
|
|
||||||
private void linkRuleobjectDoc_Click(object sender, RoutedEventArgs e)
|
private void linkRuleobjectDoc_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
Utils.ProcessStart("https://xtls.github.io/config/routing.html#ruleobject");
|
ProcUtils.ProcessStart("https://xtls.github.io/config/routing.html#ruleobject");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -197,7 +197,7 @@ namespace v2rayN.Views
|
|||||||
|
|
||||||
private void linkCustomRulesetPath4Singbox(object sender, RoutedEventArgs e)
|
private void linkCustomRulesetPath4Singbox(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
Utils.ProcessStart("https://github.com/2dust/v2rayCustomRoutingList/blob/master/singbox_custom_ruleset_example.json");
|
ProcUtils.ProcessStart("https://github.com/2dust/v2rayCustomRoutingList/blob/master/singbox_custom_ruleset_example.json");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -122,12 +122,12 @@ namespace v2rayN.Views
|
|||||||
|
|
||||||
private void linkdomainStrategy_Click(object sender, System.Windows.RoutedEventArgs e)
|
private void linkdomainStrategy_Click(object sender, System.Windows.RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
Utils.ProcessStart("https://xtls.github.io/config/routing.html");
|
ProcUtils.ProcessStart("https://xtls.github.io/config/routing.html");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void linkdomainStrategy4Singbox_Click(object sender, RoutedEventArgs e)
|
private void linkdomainStrategy4Singbox_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
Utils.ProcessStart("https://sing-box.sagernet.org/zh/configuration/shared/listen/#domain_strategy");
|
ProcUtils.ProcessStart("https://sing-box.sagernet.org/zh/configuration/shared/listen/#domain_strategy");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void btnCancel_Click(object sender, System.Windows.RoutedEventArgs e)
|
private void btnCancel_Click(object sender, System.Windows.RoutedEventArgs e)
|
||||||
|
43
v2rayng/.github/workflows/build.yml
vendored
43
v2rayng/.github/workflows/build.yml
vendored
@@ -17,6 +17,8 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: 'true'
|
||||||
|
|
||||||
- name: Prepare build dir
|
- name: Prepare build dir
|
||||||
run: |
|
run: |
|
||||||
@@ -62,7 +64,6 @@ jobs:
|
|||||||
cd ${{ github.workspace }}/build/AndroidLibV2rayLite
|
cd ${{ github.workspace }}/build/AndroidLibV2rayLite
|
||||||
bash compile-tun2socks.sh
|
bash compile-tun2socks.sh
|
||||||
tar -xvzf libtun2socks.so.tgz
|
tar -xvzf libtun2socks.so.tgz
|
||||||
cp -r libs/* ${{ github.workspace }}/V2rayNG/app/libs/
|
|
||||||
env:
|
env:
|
||||||
NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
|
NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
|
||||||
|
|
||||||
@@ -75,7 +76,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Copy libtun2socks
|
- name: Copy libtun2socks
|
||||||
run: |
|
run: |
|
||||||
cp -r ${{ github.workspace }}/build/AndroidLibV2rayLite/libs/* ${{ github.workspace }}/V2rayNG/app/libs/
|
cp -r ${{ github.workspace }}/build/AndroidLibV2rayLite/libs ${{ github.workspace }}/V2rayNG/app
|
||||||
|
|
||||||
- name: Download libv2ray
|
- name: Download libv2ray
|
||||||
uses: robinraju/release-downloader@v1
|
uses: robinraju/release-downloader@v1
|
||||||
@@ -85,6 +86,44 @@ jobs:
|
|||||||
fileName: 'libv2ray.aar'
|
fileName: 'libv2ray.aar'
|
||||||
out-file-path: V2rayNG/app/libs/
|
out-file-path: V2rayNG/app/libs/
|
||||||
|
|
||||||
|
- name: Restore cached libhysteria2
|
||||||
|
id: cache-libhysteria2-restore
|
||||||
|
uses: actions/cache/restore@v4
|
||||||
|
with:
|
||||||
|
path: ${{ github.workspace }}/hysteria/libs
|
||||||
|
key: libhysteria2-${{ runner.os }}-${{ hashFiles('.git/modules/hysteria/HEAD') }}-${{ hashFiles('libhysteria.sh') }}
|
||||||
|
|
||||||
|
- name: Fetch Go version from AndroidLibXrayLite
|
||||||
|
if: steps.cache-libhysteria2-restore.outputs.cache-hit != 'true'
|
||||||
|
run: |
|
||||||
|
GO_VERSION=$(curl -sL https://github.com/2dust/AndroidLibXrayLite/raw/refs/heads/main/go.mod | sed -n -E 's/.*go ([0-9]+\.[0-9]+\.[0-9]+).*/\1/p')
|
||||||
|
echo "Go version: $GO_VERSION"
|
||||||
|
echo "GO_VERSION=$GO_VERSION" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Setup Golang
|
||||||
|
if: steps.cache-libhysteria2-restore.outputs.cache-hit != 'true'
|
||||||
|
uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: '${{ env.GO_VERSION }}'
|
||||||
|
|
||||||
|
- name: Build libhysteria2
|
||||||
|
if: steps.cache-libhysteria2-restore.outputs.cache-hit != 'true'
|
||||||
|
run: |
|
||||||
|
bash libhysteria2.sh
|
||||||
|
env:
|
||||||
|
ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
|
||||||
|
|
||||||
|
- name: Save libhysteria2
|
||||||
|
if: steps.cache-libhysteria2-restore.outputs.cache-hit != 'true'
|
||||||
|
uses: actions/cache/save@v4
|
||||||
|
with:
|
||||||
|
path: ${{ github.workspace }}/hysteria/libs
|
||||||
|
key: libhysteria2-${{ runner.os }}-${{ hashFiles('.git/modules/hysteria/HEAD') }}-${{ hashFiles('libhysteria.sh') }}
|
||||||
|
|
||||||
|
- name: Copy libhysteria2
|
||||||
|
run: |
|
||||||
|
cp -r ${{ github.workspace }}/hysteria/libs ${{ github.workspace }}/V2rayNG/app
|
||||||
|
|
||||||
- name: Setup Java
|
- name: Setup Java
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
|
3
v2rayng/.gitignore
vendored
3
v2rayng/.gitignore
vendored
@@ -3,5 +3,4 @@
|
|||||||
V2rayNG/app/release/output.json
|
V2rayNG/app/release/output.json
|
||||||
.idea/
|
.idea/
|
||||||
.gradle/
|
.gradle/
|
||||||
libtun2socks.so
|
*.so
|
||||||
libhysteria2.so
|
|
||||||
|
3
v2rayng/.gitmodules
vendored
Normal file
3
v2rayng/.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
[submodule "hysteria"]
|
||||||
|
path = hysteria
|
||||||
|
url = https://github.com/apernet/hysteria
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
1
v2rayng/hysteria/.github/FUNDING.yml
vendored
Normal file
1
v2rayng/hysteria/.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
custom: [ 'https://v2.hysteria.network/docs/Donation/' ]
|
26
v2rayng/hysteria/.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
26
v2rayng/hysteria/.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
---
|
||||||
|
name: Bug report
|
||||||
|
about: Report anything you think is a bug and needs to be fixed.
|
||||||
|
title: ''
|
||||||
|
labels: bug
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Describe the bug**
|
||||||
|
A clear and concise description of what the bug is.
|
||||||
|
|
||||||
|
**To Reproduce**
|
||||||
|
Steps to reproduce the behavior.
|
||||||
|
|
||||||
|
**Expected behavior**
|
||||||
|
A clear and concise description of what you expected to happen.
|
||||||
|
|
||||||
|
**Logs**
|
||||||
|
Attach logs from the client/server when the error occurs.
|
||||||
|
|
||||||
|
**Device and Operating System**
|
||||||
|
What are you using it on.
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context about the problem here.
|
26
v2rayng/hysteria/.github/ISSUE_TEMPLATE/bug_report.zh.md
vendored
Normal file
26
v2rayng/hysteria/.github/ISSUE_TEMPLATE/bug_report.zh.md
vendored
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
---
|
||||||
|
name: Bug 反馈
|
||||||
|
about: 反馈任何你认为是 bug 需要修复的问题。
|
||||||
|
title: ''
|
||||||
|
labels: bug
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**描述问题**
|
||||||
|
请尽量清晰精准地描述你遇到的问题。
|
||||||
|
|
||||||
|
**如何复现**
|
||||||
|
复现问题的步骤。
|
||||||
|
|
||||||
|
**预期行为**
|
||||||
|
你认为修复后的行为应该是怎样的。
|
||||||
|
|
||||||
|
**日志**
|
||||||
|
附上客户端/服务器端在错误发生前后的日志。
|
||||||
|
|
||||||
|
**设备和操作系统**
|
||||||
|
你在用什么设备和操作系统。
|
||||||
|
|
||||||
|
**额外信息**
|
||||||
|
其他你认为有助于解决问题的信息。
|
20
v2rayng/hysteria/.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
20
v2rayng/hysteria/.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
name: Feature request
|
||||||
|
about: Suggest an idea for this project.
|
||||||
|
title: ''
|
||||||
|
labels: enhancement
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Is your feature request related to a problem? Please describe.**
|
||||||
|
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||||
|
|
||||||
|
**Describe the solution you'd like**
|
||||||
|
A clear and concise description of what you want to happen.
|
||||||
|
|
||||||
|
**Describe alternatives you've considered**
|
||||||
|
A clear and concise description of any alternative solutions or features you've considered.
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context or screenshots about the feature request here.
|
20
v2rayng/hysteria/.github/ISSUE_TEMPLATE/feature_request.zh.md
vendored
Normal file
20
v2rayng/hysteria/.github/ISSUE_TEMPLATE/feature_request.zh.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
name: 功能请求
|
||||||
|
about: 为这个项目提出改进意见。
|
||||||
|
title: ''
|
||||||
|
labels: enhancement
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**你的功能请求是否与某个问题有关?**
|
||||||
|
请尽量清晰精准地描述你遇到的问题。例如:我家运营商限制 UDP 协议速度,导致 Hysteria 很慢,希望增加 FakeTCP 支持。
|
||||||
|
|
||||||
|
**描述你希望的解决方案**
|
||||||
|
请尽量清晰精准地描述你希望的解决方案。
|
||||||
|
|
||||||
|
**有没有其他替代方案**
|
||||||
|
请尽量清晰精准地描述你认为可能的替代方案。
|
||||||
|
|
||||||
|
**额外信息**
|
||||||
|
其他你认为有助于开发者了解你需求的信息。
|
10
v2rayng/hysteria/.github/dependabot.yml
vendored
Normal file
10
v2rayng/hysteria/.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: "gomod"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "daily"
|
||||||
|
- package-ecosystem: "github-actions"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "daily"
|
104
v2rayng/hysteria/.github/workflows/autotag.yaml
vendored
Normal file
104
v2rayng/hysteria/.github/workflows/autotag.yaml
vendored
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
name: "Create release tags for nested modules"
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- app/v*.*.*
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
tag:
|
||||||
|
name: "Create tags"
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: "Extract tagbase"
|
||||||
|
id: extract_tagbase
|
||||||
|
uses: actions/github-script@v7
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const ref = context.ref;
|
||||||
|
core.info(`context.ref: ${ref}`);
|
||||||
|
const refPrefix = 'refs/tags/app/';
|
||||||
|
if (!ref.startsWith(refPrefix)) {
|
||||||
|
core.setFailed(`context.ref does not start with ${refPrefix}: ${ref}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const tagbase = ref.slice(refPrefix.length);
|
||||||
|
core.info(`tagbase: ${tagbase}`);
|
||||||
|
core.setOutput('tagbase', tagbase);
|
||||||
|
|
||||||
|
- name: "Tagging core/*"
|
||||||
|
uses: actions/github-script@v7
|
||||||
|
env:
|
||||||
|
INPUT_TAGPREFIX: "core/"
|
||||||
|
INPUT_TAGBASE: ${{ steps.extract_tagbase.outputs.tagbase }}
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const tagbase = core.getInput('tagbase', { required: true });
|
||||||
|
const tagprefix = core.getInput('tagprefix', { required: true });
|
||||||
|
const refname = `tags/${tagprefix}${tagbase}`;
|
||||||
|
core.info(`creating ref ${refname}`);
|
||||||
|
try {
|
||||||
|
await github.rest.git.createRef({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
ref: `refs/${refname}`,
|
||||||
|
sha: context.sha
|
||||||
|
});
|
||||||
|
core.info(`created ref ${refname}`);
|
||||||
|
return;
|
||||||
|
} catch (error) {
|
||||||
|
core.info(`failed to create ref ${refname}: ${error}`);
|
||||||
|
}
|
||||||
|
core.info(`updating ref ${refname}`)
|
||||||
|
try {
|
||||||
|
await github.rest.git.updateRef({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
ref: refname,
|
||||||
|
sha: context.sha
|
||||||
|
});
|
||||||
|
core.info(`updated ref ${refname}`);
|
||||||
|
return;
|
||||||
|
} catch (error) {
|
||||||
|
core.setFailed(`failed to update ref ${refname}: ${error}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
- name: "Tagging extras/*"
|
||||||
|
uses: actions/github-script@v7
|
||||||
|
env:
|
||||||
|
INPUT_TAGPREFIX: "extras/"
|
||||||
|
INPUT_TAGBASE: ${{ steps.extract_tagbase.outputs.tagbase }}
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const tagbase = core.getInput('tagbase', { required: true });
|
||||||
|
const tagprefix = core.getInput('tagprefix', { required: true });
|
||||||
|
const refname = `tags/${tagprefix}${tagbase}`;
|
||||||
|
core.info(`creating ref ${refname}`);
|
||||||
|
try {
|
||||||
|
await github.rest.git.createRef({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
ref: `refs/${refname}`,
|
||||||
|
sha: context.sha
|
||||||
|
});
|
||||||
|
core.info(`created ref ${refname}`);
|
||||||
|
return;
|
||||||
|
} catch (error) {
|
||||||
|
core.info(`failed to create ref ${refname}: ${error}`);
|
||||||
|
}
|
||||||
|
core.info(`updating ref ${refname}`)
|
||||||
|
try {
|
||||||
|
await github.rest.git.updateRef({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
ref: refname,
|
||||||
|
sha: context.sha
|
||||||
|
});
|
||||||
|
core.info(`updated ref ${refname}`);
|
||||||
|
return;
|
||||||
|
} catch (error) {
|
||||||
|
core.setFailed(`failed to update ref ${refname}: ${error}`);
|
||||||
|
}
|
44
v2rayng/hysteria/.github/workflows/docker.yml
vendored
Normal file
44
v2rayng/hysteria/.github/workflows/docker.yml
vendored
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
name: "Build Docker Image"
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- app/v*.*.*
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
docker:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
ACTIONS_ALLOW_UNSECURE_COMMANDS: true
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Check out
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Get version
|
||||||
|
id: get_version
|
||||||
|
run: echo "version=$(git describe --tags --always --match 'app/v*' | sed -n 's|app/\([^/-]*\)\(-.*\)\{0,1\}|\1|p')" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v3
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Login to DockerHub
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Build and push
|
||||||
|
id: docker_build
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
push: true
|
||||||
|
platforms: linux/amd64,linux/arm64
|
||||||
|
tags: tobyxdd/hysteria:latest,tobyxdd/hysteria:v2,tobyxdd/hysteria:${{ steps.get_version.outputs.version }}
|
||||||
|
|
||||||
|
- name: Image digest
|
||||||
|
run: echo ${{ steps.docker_build.outputs.digest }}
|
52
v2rayng/hysteria/.github/workflows/master.yml
vendored
Normal file
52
v2rayng/hysteria/.github/workflows/master.yml
vendored
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
name: "Build master branch"
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
ACTIONS_ALLOW_UNSECURE_COMMANDS: true
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Check out
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Go
|
||||||
|
uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: "1.23"
|
||||||
|
|
||||||
|
- name: Setup Python # This is for the build script
|
||||||
|
uses: actions/setup-python@v5
|
||||||
|
with:
|
||||||
|
python-version: "3.11"
|
||||||
|
|
||||||
|
- uses: nttld/setup-ndk@v1
|
||||||
|
id: setup-ndk
|
||||||
|
with:
|
||||||
|
ndk-version: r26b
|
||||||
|
add-to-path: false
|
||||||
|
|
||||||
|
- name: Run build script
|
||||||
|
env:
|
||||||
|
ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
|
||||||
|
run: |
|
||||||
|
export HY_APP_PLATFORMS=$(sed 's/\r$//' platforms.txt | awk '!/^#/ && !/^$/' | paste -sd ",")
|
||||||
|
python hyperbole.py build -r
|
||||||
|
|
||||||
|
- name: Generate hashes
|
||||||
|
run: |
|
||||||
|
for file in build/*; do
|
||||||
|
sha256sum $file >> build/hashes.txt
|
||||||
|
done
|
||||||
|
|
||||||
|
- name: Archive
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: hysteria-master-${{ github.sha }}
|
||||||
|
path: build
|
71
v2rayng/hysteria/.github/workflows/release.yml
vendored
Normal file
71
v2rayng/hysteria/.github/workflows/release.yml
vendored
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
name: "Build release"
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- app/v*.*.*
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
ACTIONS_ALLOW_UNSECURE_COMMANDS: true
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Check out
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Get version
|
||||||
|
id: get_version
|
||||||
|
run: echo "version=$(git describe --tags --always --match 'app/v*' | sed -n 's|app/\([^/-]*\)\(-.*\)\{0,1\}|\1|p')" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Setup Go
|
||||||
|
uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: "1.23"
|
||||||
|
|
||||||
|
- name: Setup Python # This is for the build script
|
||||||
|
uses: actions/setup-python@v5
|
||||||
|
with:
|
||||||
|
python-version: "3.11"
|
||||||
|
|
||||||
|
- uses: nttld/setup-ndk@v1
|
||||||
|
id: setup-ndk
|
||||||
|
with:
|
||||||
|
ndk-version: r26b
|
||||||
|
add-to-path: false
|
||||||
|
|
||||||
|
- name: Run build script
|
||||||
|
env:
|
||||||
|
ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
|
||||||
|
run: |
|
||||||
|
export HY_APP_PLATFORMS=$(sed 's/\r$//' platforms.txt | awk '!/^#/ && !/^$/' | paste -sd ",")
|
||||||
|
python hyperbole.py build -r
|
||||||
|
|
||||||
|
- name: Generate hashes
|
||||||
|
run: |
|
||||||
|
for file in build/*; do
|
||||||
|
sha256sum $file >> build/hashes.txt
|
||||||
|
done
|
||||||
|
|
||||||
|
- name: Upload GitHub
|
||||||
|
uses: softprops/action-gh-release@v2
|
||||||
|
with:
|
||||||
|
files: build/*
|
||||||
|
|
||||||
|
- name: Upload CF bucket
|
||||||
|
uses: shallwefootball/upload-s3-action@v1.3.3
|
||||||
|
with:
|
||||||
|
aws_key_id: ${{ secrets.CF_KEY_ID }}
|
||||||
|
aws_secret_access_key: ${{ secrets.CF_KEY }}
|
||||||
|
aws_bucket: "hydownload"
|
||||||
|
endpoint: "https://bea223c61d5a41250d127bd67f51dfec.r2.cloudflarestorage.com/"
|
||||||
|
source_dir: "build"
|
||||||
|
destination_dir: "app/${{ steps.get_version.outputs.version }}"
|
||||||
|
|
||||||
|
- name: Publish to API
|
||||||
|
run: |
|
||||||
|
export HY_API_POST_KEY=${{ secrets.HY2_API_POST_KEY }}
|
||||||
|
pip install requests
|
||||||
|
python hyperbole.py publish
|
29
v2rayng/hysteria/.github/workflows/scripts.yml
vendored
Normal file
29
v2rayng/hysteria/.github/workflows/scripts.yml
vendored
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
name: "Publish scripts"
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
paths:
|
||||||
|
- scripts/**
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
publish:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
deployments: write
|
||||||
|
name: Publish scripts to Cloudflare Pages
|
||||||
|
steps:
|
||||||
|
- name: Check out
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Publish to Cloudflare Pages
|
||||||
|
uses: cloudflare/pages-action@v1
|
||||||
|
with:
|
||||||
|
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||||
|
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||||
|
projectName: hy2scripts
|
||||||
|
directory: scripts
|
||||||
|
gitHubToken: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
branch: main
|
470
v2rayng/hysteria/.gitignore
vendored
Normal file
470
v2rayng/hysteria/.gitignore
vendored
Normal file
@@ -0,0 +1,470 @@
|
|||||||
|
# Created by https://www.toptal.com/developers/gitignore/api/goland+all,intellij+all,go,windows,linux,macos,python,pycharm+all
|
||||||
|
# Edit at https://www.toptal.com/developers/gitignore?templates=goland+all,intellij+all,go,windows,linux,macos,python,pycharm+all
|
||||||
|
|
||||||
|
### Go ###
|
||||||
|
# If you prefer the allow list template instead of the deny list, see community template:
|
||||||
|
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
|
||||||
|
#
|
||||||
|
# Binaries for programs and plugins
|
||||||
|
*.exe
|
||||||
|
*.exe~
|
||||||
|
*.dll
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
|
||||||
|
# Test binary, built with `go test -c`
|
||||||
|
*.test
|
||||||
|
|
||||||
|
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||||
|
*.out
|
||||||
|
|
||||||
|
# Dependency directories (remove the comment below to include it)
|
||||||
|
# vendor/
|
||||||
|
|
||||||
|
# Go workspace file
|
||||||
|
go.work
|
||||||
|
|
||||||
|
### GoLand+all ###
|
||||||
|
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||||
|
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||||
|
|
||||||
|
# User-specific stuff
|
||||||
|
.idea/**/workspace.xml
|
||||||
|
.idea/**/tasks.xml
|
||||||
|
.idea/**/usage.statistics.xml
|
||||||
|
.idea/**/dictionaries
|
||||||
|
.idea/**/shelf
|
||||||
|
|
||||||
|
# AWS User-specific
|
||||||
|
.idea/**/aws.xml
|
||||||
|
|
||||||
|
# Generated files
|
||||||
|
.idea/**/contentModel.xml
|
||||||
|
|
||||||
|
# Sensitive or high-churn files
|
||||||
|
.idea/**/dataSources/
|
||||||
|
.idea/**/dataSources.ids
|
||||||
|
.idea/**/dataSources.local.xml
|
||||||
|
.idea/**/sqlDataSources.xml
|
||||||
|
.idea/**/dynamic.xml
|
||||||
|
.idea/**/uiDesigner.xml
|
||||||
|
.idea/**/dbnavigator.xml
|
||||||
|
|
||||||
|
# Gradle
|
||||||
|
.idea/**/gradle.xml
|
||||||
|
.idea/**/libraries
|
||||||
|
|
||||||
|
# Gradle and Maven with auto-import
|
||||||
|
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||||
|
# since they will be recreated, and may cause churn. Uncomment if using
|
||||||
|
# auto-import.
|
||||||
|
# .idea/artifacts
|
||||||
|
# .idea/compiler.xml
|
||||||
|
# .idea/jarRepositories.xml
|
||||||
|
# .idea/modules.xml
|
||||||
|
# .idea/*.iml
|
||||||
|
# .idea/modules
|
||||||
|
# *.iml
|
||||||
|
# *.ipr
|
||||||
|
|
||||||
|
# CMake
|
||||||
|
cmake-build-*/
|
||||||
|
|
||||||
|
# Mongo Explorer plugin
|
||||||
|
.idea/**/mongoSettings.xml
|
||||||
|
|
||||||
|
# File-based project format
|
||||||
|
*.iws
|
||||||
|
|
||||||
|
# IntelliJ
|
||||||
|
out/
|
||||||
|
|
||||||
|
# mpeltonen/sbt-idea plugin
|
||||||
|
.idea_modules/
|
||||||
|
|
||||||
|
# JIRA plugin
|
||||||
|
atlassian-ide-plugin.xml
|
||||||
|
|
||||||
|
# Cursive Clojure plugin
|
||||||
|
.idea/replstate.xml
|
||||||
|
|
||||||
|
# SonarLint plugin
|
||||||
|
.idea/sonarlint/
|
||||||
|
|
||||||
|
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||||
|
com_crashlytics_export_strings.xml
|
||||||
|
crashlytics.properties
|
||||||
|
crashlytics-build.properties
|
||||||
|
fabric.properties
|
||||||
|
|
||||||
|
# Editor-based Rest Client
|
||||||
|
.idea/httpRequests
|
||||||
|
|
||||||
|
# Android studio 3.1+ serialized cache file
|
||||||
|
.idea/caches/build_file_checksums.ser
|
||||||
|
|
||||||
|
### GoLand+all Patch ###
|
||||||
|
# Ignore everything but code style settings and run configurations
|
||||||
|
# that are supposed to be shared within teams.
|
||||||
|
|
||||||
|
.idea/*
|
||||||
|
|
||||||
|
!.idea/codeStyles
|
||||||
|
!.idea/runConfigurations
|
||||||
|
|
||||||
|
### Intellij+all ###
|
||||||
|
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||||
|
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||||
|
|
||||||
|
# User-specific stuff
|
||||||
|
|
||||||
|
# AWS User-specific
|
||||||
|
|
||||||
|
# Generated files
|
||||||
|
|
||||||
|
# Sensitive or high-churn files
|
||||||
|
|
||||||
|
# Gradle
|
||||||
|
|
||||||
|
# Gradle and Maven with auto-import
|
||||||
|
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||||
|
# since they will be recreated, and may cause churn. Uncomment if using
|
||||||
|
# auto-import.
|
||||||
|
# .idea/artifacts
|
||||||
|
# .idea/compiler.xml
|
||||||
|
# .idea/jarRepositories.xml
|
||||||
|
# .idea/modules.xml
|
||||||
|
# .idea/*.iml
|
||||||
|
# .idea/modules
|
||||||
|
# *.iml
|
||||||
|
# *.ipr
|
||||||
|
|
||||||
|
# CMake
|
||||||
|
|
||||||
|
# Mongo Explorer plugin
|
||||||
|
|
||||||
|
# File-based project format
|
||||||
|
|
||||||
|
# IntelliJ
|
||||||
|
|
||||||
|
# mpeltonen/sbt-idea plugin
|
||||||
|
|
||||||
|
# JIRA plugin
|
||||||
|
|
||||||
|
# Cursive Clojure plugin
|
||||||
|
|
||||||
|
# SonarLint plugin
|
||||||
|
|
||||||
|
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||||
|
|
||||||
|
# Editor-based Rest Client
|
||||||
|
|
||||||
|
# Android studio 3.1+ serialized cache file
|
||||||
|
|
||||||
|
### Intellij+all Patch ###
|
||||||
|
# Ignore everything but code style settings and run configurations
|
||||||
|
# that are supposed to be shared within teams.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### Linux ###
|
||||||
|
*~
|
||||||
|
|
||||||
|
# temporary files which can be created if a process still has a handle open of a deleted file
|
||||||
|
.fuse_hidden*
|
||||||
|
|
||||||
|
# KDE directory preferences
|
||||||
|
.directory
|
||||||
|
|
||||||
|
# Linux trash folder which might appear on any partition or disk
|
||||||
|
.Trash-*
|
||||||
|
|
||||||
|
# .nfs files are created when an open file is removed but is still being accessed
|
||||||
|
.nfs*
|
||||||
|
|
||||||
|
### macOS ###
|
||||||
|
# General
|
||||||
|
.DS_Store
|
||||||
|
.AppleDouble
|
||||||
|
.LSOverride
|
||||||
|
|
||||||
|
# Icon must end with two \r
|
||||||
|
Icon
|
||||||
|
|
||||||
|
|
||||||
|
# Thumbnails
|
||||||
|
._*
|
||||||
|
|
||||||
|
# Files that might appear in the root of a volume
|
||||||
|
.DocumentRevisions-V100
|
||||||
|
.fseventsd
|
||||||
|
.Spotlight-V100
|
||||||
|
.TemporaryItems
|
||||||
|
.Trashes
|
||||||
|
.VolumeIcon.icns
|
||||||
|
.com.apple.timemachine.donotpresent
|
||||||
|
|
||||||
|
# Directories potentially created on remote AFP share
|
||||||
|
.AppleDB
|
||||||
|
.AppleDesktop
|
||||||
|
Network Trash Folder
|
||||||
|
Temporary Items
|
||||||
|
.apdisk
|
||||||
|
|
||||||
|
### macOS Patch ###
|
||||||
|
# iCloud generated files
|
||||||
|
*.icloud
|
||||||
|
|
||||||
|
### PyCharm+all ###
|
||||||
|
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||||
|
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||||
|
|
||||||
|
# User-specific stuff
|
||||||
|
|
||||||
|
# AWS User-specific
|
||||||
|
|
||||||
|
# Generated files
|
||||||
|
|
||||||
|
# Sensitive or high-churn files
|
||||||
|
|
||||||
|
# Gradle
|
||||||
|
|
||||||
|
# Gradle and Maven with auto-import
|
||||||
|
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||||
|
# since they will be recreated, and may cause churn. Uncomment if using
|
||||||
|
# auto-import.
|
||||||
|
# .idea/artifacts
|
||||||
|
# .idea/compiler.xml
|
||||||
|
# .idea/jarRepositories.xml
|
||||||
|
# .idea/modules.xml
|
||||||
|
# .idea/*.iml
|
||||||
|
# .idea/modules
|
||||||
|
# *.iml
|
||||||
|
# *.ipr
|
||||||
|
|
||||||
|
# CMake
|
||||||
|
|
||||||
|
# Mongo Explorer plugin
|
||||||
|
|
||||||
|
# File-based project format
|
||||||
|
|
||||||
|
# IntelliJ
|
||||||
|
|
||||||
|
# mpeltonen/sbt-idea plugin
|
||||||
|
|
||||||
|
# JIRA plugin
|
||||||
|
|
||||||
|
# Cursive Clojure plugin
|
||||||
|
|
||||||
|
# SonarLint plugin
|
||||||
|
|
||||||
|
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||||
|
|
||||||
|
# Editor-based Rest Client
|
||||||
|
|
||||||
|
# Android studio 3.1+ serialized cache file
|
||||||
|
|
||||||
|
### PyCharm+all Patch ###
|
||||||
|
# Ignore everything but code style settings and run configurations
|
||||||
|
# that are supposed to be shared within teams.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### Python ###
|
||||||
|
# Byte-compiled / optimized / DLL files
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
|
||||||
|
# C extensions
|
||||||
|
|
||||||
|
# Distribution / packaging
|
||||||
|
.Python
|
||||||
|
build/
|
||||||
|
develop-eggs/
|
||||||
|
dist/
|
||||||
|
downloads/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
wheels/
|
||||||
|
share/python-wheels/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
MANIFEST
|
||||||
|
|
||||||
|
# PyInstaller
|
||||||
|
# Usually these files are written by a python script from a template
|
||||||
|
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||||
|
*.manifest
|
||||||
|
*.spec
|
||||||
|
|
||||||
|
# Installer logs
|
||||||
|
pip-log.txt
|
||||||
|
pip-delete-this-directory.txt
|
||||||
|
|
||||||
|
# Unit test / coverage reports
|
||||||
|
htmlcov/
|
||||||
|
.tox/
|
||||||
|
.nox/
|
||||||
|
.coverage
|
||||||
|
.coverage.*
|
||||||
|
.cache
|
||||||
|
nosetests.xml
|
||||||
|
coverage.xml
|
||||||
|
*.cover
|
||||||
|
*.py,cover
|
||||||
|
.hypothesis/
|
||||||
|
.pytest_cache/
|
||||||
|
cover/
|
||||||
|
|
||||||
|
# Translations
|
||||||
|
*.mo
|
||||||
|
*.pot
|
||||||
|
|
||||||
|
# Django stuff:
|
||||||
|
*.log
|
||||||
|
local_settings.py
|
||||||
|
db.sqlite3
|
||||||
|
db.sqlite3-journal
|
||||||
|
|
||||||
|
# Flask stuff:
|
||||||
|
instance/
|
||||||
|
.webassets-cache
|
||||||
|
|
||||||
|
# Scrapy stuff:
|
||||||
|
.scrapy
|
||||||
|
|
||||||
|
# Sphinx documentation
|
||||||
|
docs/_build/
|
||||||
|
|
||||||
|
# PyBuilder
|
||||||
|
.pybuilder/
|
||||||
|
target/
|
||||||
|
|
||||||
|
# Jupyter Notebook
|
||||||
|
.ipynb_checkpoints
|
||||||
|
|
||||||
|
# IPython
|
||||||
|
profile_default/
|
||||||
|
ipython_config.py
|
||||||
|
|
||||||
|
# pyenv
|
||||||
|
# For a library or package, you might want to ignore these files since the code is
|
||||||
|
# intended to run in multiple environments; otherwise, check them in:
|
||||||
|
# .python-version
|
||||||
|
|
||||||
|
# pipenv
|
||||||
|
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||||
|
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||||
|
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||||
|
# install all needed dependencies.
|
||||||
|
#Pipfile.lock
|
||||||
|
|
||||||
|
# poetry
|
||||||
|
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||||
|
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||||
|
# commonly ignored for libraries.
|
||||||
|
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||||
|
#poetry.lock
|
||||||
|
|
||||||
|
# pdm
|
||||||
|
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||||
|
#pdm.lock
|
||||||
|
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
||||||
|
# in version control.
|
||||||
|
# https://pdm.fming.dev/#use-with-ide
|
||||||
|
.pdm.toml
|
||||||
|
|
||||||
|
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||||
|
__pypackages__/
|
||||||
|
|
||||||
|
# Celery stuff
|
||||||
|
celerybeat-schedule
|
||||||
|
celerybeat.pid
|
||||||
|
|
||||||
|
# SageMath parsed files
|
||||||
|
*.sage.py
|
||||||
|
|
||||||
|
# Environments
|
||||||
|
.env
|
||||||
|
.venv
|
||||||
|
env/
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
env.bak/
|
||||||
|
venv.bak/
|
||||||
|
|
||||||
|
# Spyder project settings
|
||||||
|
.spyderproject
|
||||||
|
.spyproject
|
||||||
|
|
||||||
|
# Rope project settings
|
||||||
|
.ropeproject
|
||||||
|
|
||||||
|
# mkdocs documentation
|
||||||
|
/site
|
||||||
|
|
||||||
|
# mypy
|
||||||
|
.mypy_cache/
|
||||||
|
.dmypy.json
|
||||||
|
dmypy.json
|
||||||
|
|
||||||
|
# Pyre type checker
|
||||||
|
.pyre/
|
||||||
|
|
||||||
|
# pytype static type analyzer
|
||||||
|
.pytype/
|
||||||
|
|
||||||
|
# Cython debug symbols
|
||||||
|
cython_debug/
|
||||||
|
|
||||||
|
# PyCharm
|
||||||
|
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||||
|
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||||
|
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||||
|
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||||
|
#.idea/
|
||||||
|
|
||||||
|
### Python Patch ###
|
||||||
|
# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration
|
||||||
|
poetry.toml
|
||||||
|
|
||||||
|
# ruff
|
||||||
|
.ruff_cache/
|
||||||
|
|
||||||
|
# LSP config files
|
||||||
|
pyrightconfig.json
|
||||||
|
|
||||||
|
### Windows ###
|
||||||
|
# Windows thumbnail cache files
|
||||||
|
Thumbs.db
|
||||||
|
Thumbs.db:encryptable
|
||||||
|
ehthumbs.db
|
||||||
|
ehthumbs_vista.db
|
||||||
|
|
||||||
|
# Dump file
|
||||||
|
*.stackdump
|
||||||
|
|
||||||
|
# Folder config file
|
||||||
|
[Dd]esktop.ini
|
||||||
|
|
||||||
|
# Recycle Bin used on file shares
|
||||||
|
$RECYCLE.BIN/
|
||||||
|
|
||||||
|
# Windows Installer files
|
||||||
|
*.cab
|
||||||
|
*.msi
|
||||||
|
*.msix
|
||||||
|
*.msm
|
||||||
|
*.msp
|
||||||
|
|
||||||
|
# Windows shortcuts
|
||||||
|
*.lnk
|
||||||
|
|
||||||
|
# End of https://www.toptal.com/developers/gitignore/api/goland+all,intellij+all,go,windows,linux,macos,python,pycharm+all
|
3
v2rayng/hysteria/CHANGELOG.md
Normal file
3
v2rayng/hysteria/CHANGELOG.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
https://v2.hysteria.network/docs/Changelog/
|
39
v2rayng/hysteria/Dockerfile
Normal file
39
v2rayng/hysteria/Dockerfile
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
FROM golang:1-alpine AS builder
|
||||||
|
|
||||||
|
# GOPROXY is disabled by default, use:
|
||||||
|
# docker build --build-arg GOPROXY="https://goproxy.io" ...
|
||||||
|
# to enable GOPROXY.
|
||||||
|
ARG GOPROXY=""
|
||||||
|
|
||||||
|
ENV GOPROXY ${GOPROXY}
|
||||||
|
|
||||||
|
COPY . /go/src/github.com/apernet/hysteria
|
||||||
|
|
||||||
|
WORKDIR /go/src/github.com/apernet/hysteria
|
||||||
|
|
||||||
|
RUN set -ex \
|
||||||
|
&& apk add git build-base bash python3 \
|
||||||
|
&& python hyperbole.py build -r \
|
||||||
|
&& mv ./build/hysteria-* /go/bin/hysteria
|
||||||
|
|
||||||
|
# multi-stage builds to create the final image
|
||||||
|
FROM alpine AS dist
|
||||||
|
|
||||||
|
# set up nsswitch.conf for Go's "netgo" implementation
|
||||||
|
# - https://github.com/golang/go/blob/go1.9.1/src/net/conf.go#L194-L275
|
||||||
|
# - docker run --rm debian:stretch grep '^hosts:' /etc/nsswitch.conf
|
||||||
|
RUN if [ ! -e /etc/nsswitch.conf ]; then echo 'hosts: files dns' > /etc/nsswitch.conf; fi
|
||||||
|
|
||||||
|
# bash is used for debugging, tzdata is used to add timezone information.
|
||||||
|
# Install ca-certificates to ensure no CA certificate errors.
|
||||||
|
#
|
||||||
|
# Do not try to add the "--no-cache" option when there are multiple "apk"
|
||||||
|
# commands, this will cause the build process to become very slow.
|
||||||
|
RUN set -ex \
|
||||||
|
&& apk upgrade \
|
||||||
|
&& apk add bash tzdata ca-certificates \
|
||||||
|
&& rm -rf /var/cache/apk/*
|
||||||
|
|
||||||
|
COPY --from=builder /go/bin/hysteria /usr/local/bin/hysteria
|
||||||
|
|
||||||
|
ENTRYPOINT ["hysteria"]
|
7
v2rayng/hysteria/LICENSE.md
Normal file
7
v2rayng/hysteria/LICENSE.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
Copyright 2023 Toby
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
153
v2rayng/hysteria/PROTOCOL.md
Normal file
153
v2rayng/hysteria/PROTOCOL.md
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
# Hysteria 2 Protocol Specification
|
||||||
|
|
||||||
|
Hysteria is a TCP & UDP proxy based on QUIC, designed for speed, security and censorship resistance. This document describes the protocol used by Hysteria starting with version 2.0.0, sometimes internally referred to as the "v4" protocol. From here on, we will call it "the protocol" or "the Hysteria protocol".
|
||||||
|
|
||||||
|
## Requirements Language
|
||||||
|
|
||||||
|
The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://tools.ietf.org/html/rfc2119).
|
||||||
|
|
||||||
|
## Underlying Protocol & Wire Format
|
||||||
|
|
||||||
|
The Hysteria protocol MUST be implemented on top of the standard QUIC transport protocol [RFC 9000](https://datatracker.ietf.org/doc/html/rfc9000) with [Unreliable Datagram Extension](https://datatracker.ietf.org/doc/rfc9221/).
|
||||||
|
|
||||||
|
All multibyte numbers use Big Endian format.
|
||||||
|
|
||||||
|
All variable-length integers ("varints") are encoded/decoded as defined in QUIC (RFC 9000).
|
||||||
|
|
||||||
|
## Authentication & HTTP/3 masquerading
|
||||||
|
|
||||||
|
One of the key features of the Hysteria protocol is that to a third party without proper authentication credentials (whether it's a middleman or an active prober), a Hysteria proxy server behaves just like a standard HTTP/3 web server. Additionally, the encrypted traffic between the client and the server appears indistinguishable from normal HTTP/3 traffic.
|
||||||
|
|
||||||
|
Therefore, a Hysteria server MUST implement an HTTP/3 server (as defined by [RFC 9114](https://datatracker.ietf.org/doc/rfc9114/)) and handle HTTP requests as any standard web server would. To prevent active probers from detecting common response patterns in Hysteria servers, implementations SHOULD advise users to either host actual content or set it up as a reverse proxy for other sites.
|
||||||
|
|
||||||
|
An actual Hysteria client, upon connection, MUST send the following HTTP/3 request to the server:
|
||||||
|
|
||||||
|
```
|
||||||
|
:method: POST
|
||||||
|
:path: /auth
|
||||||
|
:host: hysteria
|
||||||
|
Hysteria-Auth: [string]
|
||||||
|
Hysteria-CC-RX: [uint]
|
||||||
|
Hysteria-Padding: [string]
|
||||||
|
```
|
||||||
|
|
||||||
|
`Hysteria-Auth`: Authentication credentials.
|
||||||
|
|
||||||
|
`Hysteria-CC-RX`: Client's maximum receive rate in bytes per second. A value of 0 indicates unknown.
|
||||||
|
|
||||||
|
`Hysteria-Padding`: A random padding string of variable length.
|
||||||
|
|
||||||
|
The Hysteria server MUST identify this special request, and, instead of attempting to serve content or forwarding it to an upstream site, it MUST authenticate the client using the provided information. If authentication is successful, the server MUST send the following response (HTTP status code 233):
|
||||||
|
|
||||||
|
```
|
||||||
|
:status: 233 HyOK
|
||||||
|
Hysteria-UDP: [true/false]
|
||||||
|
Hysteria-CC-RX: [uint/"auto"]
|
||||||
|
Hysteria-Padding: [string]
|
||||||
|
```
|
||||||
|
|
||||||
|
`Hysteria-UDP`: Whether the server supports UDP relay.
|
||||||
|
|
||||||
|
`Hysteria-CC-RX`: Server's maximum receive rate in bytes per second. A value of 0 indicates unlimited; "auto" indicates the server refuses to provide a value and ask the client to use congestion control to determine the rate on its own.
|
||||||
|
|
||||||
|
`Hysteria-Padding`: A random padding string of variable length.
|
||||||
|
|
||||||
|
See the Congestion Control section for more information on how to use the `Hysteria-CC-RX` values.
|
||||||
|
|
||||||
|
`Hysteria-Padding` is optional and is only intended to obfuscate the request/response pattern. It SHOULD be ignored by both sides.
|
||||||
|
|
||||||
|
If authentication fails, the server MUST either act like a standard web server that does not understand the request, or in the case of being a reverse proxy, forward the request to the upstream site and return the response to the client.
|
||||||
|
|
||||||
|
The client MUST check the status code to determine if the authentication was successful. If the status code is anything other than 233, the client MUST consider authentication to have failed and disconnect from the server.
|
||||||
|
|
||||||
|
After (and only after) a client passes authentication, the server MUST consider this QUIC connection to be a Hysteria proxy connection. It MUST then start processing proxy requests from the client as described in the next section.
|
||||||
|
|
||||||
|
## Proxy Requests
|
||||||
|
|
||||||
|
### TCP
|
||||||
|
|
||||||
|
For each TCP connection, the client MUST create a new QUIC bidirectional stream and send the following TCPRequest message:
|
||||||
|
|
||||||
|
```
|
||||||
|
[varint] 0x401 (TCPRequest ID)
|
||||||
|
[varint] Address length
|
||||||
|
[bytes] Address string (host:port)
|
||||||
|
[varint] Padding length
|
||||||
|
[bytes] Random padding
|
||||||
|
```
|
||||||
|
|
||||||
|
The server MUST respond with a TCPResponse message:
|
||||||
|
|
||||||
|
```
|
||||||
|
[uint8] Status (0x00 = OK, 0x01 = Error)
|
||||||
|
[varint] Message length
|
||||||
|
[bytes] Message string
|
||||||
|
[varint] Padding length
|
||||||
|
[bytes] Random padding
|
||||||
|
```
|
||||||
|
|
||||||
|
If the status is OK, the server MUST then begin forwarding data between the client and the specified TCP address until either side closes the connection. If the status is Error, the server MUST close the QUIC stream.
|
||||||
|
|
||||||
|
### UDP
|
||||||
|
|
||||||
|
UDP packets MUST be encapsulated in the following UDPMessage format and sent over QUIC's unreliable datagram (for both client-to-server and server-to-client):
|
||||||
|
|
||||||
|
```
|
||||||
|
[uint32] Session ID
|
||||||
|
[uint16] Packet ID
|
||||||
|
[uint8] Fragment ID
|
||||||
|
[uint8] Fragment count
|
||||||
|
[varint] Address length
|
||||||
|
[bytes] Address string (host:port)
|
||||||
|
[bytes] Payload
|
||||||
|
```
|
||||||
|
|
||||||
|
The client MUST use a unique Session ID for each UDP session. The server SHOULD assign a unique UDP port to each Session ID, unless it has another mechanism to differentiate packets from different sessions (e.g., symmetric NAT, varying outbound IP addresses, etc.).
|
||||||
|
|
||||||
|
The protocol does not provide an explicit way to close a UDP session. While a client can retain and reuse a Session ID indefinitely, the server SHOULD release and reassign the port associated with the Session ID after a period of inactivity or some other criteria. If the client sends a UDP packet to a Session ID that is no longer recognized by the server, the server MUST treat it as a new session and assign a new port.
|
||||||
|
|
||||||
|
If a server does not support UDP relay, it SHOULD silently discard all UDP messages received from the client.
|
||||||
|
|
||||||
|
#### Fragmentation
|
||||||
|
|
||||||
|
Due to the limit imposed by QUIC's unreliable datagram channel, any UDP packet that exceeds QUIC's maximum datagram size MUST either be fragmented or discarded.
|
||||||
|
|
||||||
|
For fragmented packets, each fragment MUST carry the same unique Packet ID. The Fragment ID, starting from 0, indicates the index out of the total Fragment Count. Both the server and client MUST wait for all fragments of a fragmented packet to arrive before processing them. If one or more fragments of a packet are lost, the entire packet MUST be discarded.
|
||||||
|
|
||||||
|
For packets that are not fragmented, the Fragment Count MUST be set to 1. In this case, the values of Packet ID and Fragment ID are irrelevant.
|
||||||
|
|
||||||
|
## Congestion Control
|
||||||
|
|
||||||
|
A unique feature of Hysteria is the ability to set the tx/rx (upload/download) rate on the client side. During authentication, the client sends its rx rate to the server via the `Hysteria-CC-RX` header. The server can use this to determine its transmission rate to the client, and vice versa by returning its rx rate to the client through the same header.
|
||||||
|
|
||||||
|
Three special cases are:
|
||||||
|
|
||||||
|
- If the client sends 0, it doesn't know its own rx rate. The server MUST use a congestion control algorithm (e.g., BBR, Cubic) to adjust its transmission rate.
|
||||||
|
- If the server responds with 0, it has no bandwidth limit. The client MAY transmit at any rate it wants.
|
||||||
|
- If the server responds with "auto", it chooses not to specify a rate. The client MUST use a congestion control algorithm to adjust its transmission rate.
|
||||||
|
|
||||||
|
## "Salamander" Obfuscation
|
||||||
|
|
||||||
|
The Hysteria protocol supports an optional obfuscation layer codenamed "Salamander".
|
||||||
|
|
||||||
|
"Salamander" encapsulates all QUIC packets in the following format:
|
||||||
|
|
||||||
|
```
|
||||||
|
[8 bytes] Salt
|
||||||
|
[bytes] Payload
|
||||||
|
```
|
||||||
|
|
||||||
|
For each QUIC packet, the obfuscator MUST calculate the BLAKE2b-256 hash of a randomly generated 8-byte salt appended to a user-provided pre-shared key.
|
||||||
|
|
||||||
|
```
|
||||||
|
hash = BLAKE2b-256(key + salt)
|
||||||
|
```
|
||||||
|
|
||||||
|
The hash is then used to obfuscate the payload using the following algorithm:
|
||||||
|
|
||||||
|
```
|
||||||
|
for i in range(0, len(payload)):
|
||||||
|
payload[i] ^= hash[i % 32]
|
||||||
|
```
|
||||||
|
|
||||||
|
The deobfuscator MUST use the same algorithms to calculate the salted hash and deobfuscate the payload. Any invalid packet MUST be discarded.
|
60
v2rayng/hysteria/README.md
Normal file
60
v2rayng/hysteria/README.md
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
# 
|
||||||
|
|
||||||
|
[![License][1]][2] [![Release][3]][4] [![Telegram][5]][6] [![Discussions][7]][8]
|
||||||
|
|
||||||
|
[1]: https://img.shields.io/badge/license-MIT-blue
|
||||||
|
[2]: LICENSE.md
|
||||||
|
[3]: https://img.shields.io/github/v/release/apernet/hysteria?style=flat-square
|
||||||
|
[4]: https://github.com/apernet/hysteria/releases
|
||||||
|
[5]: https://img.shields.io/badge/chat-Telegram-blue?style=flat-square
|
||||||
|
[6]: https://t.me/hysteria_github
|
||||||
|
[7]: https://img.shields.io/github/discussions/apernet/hysteria?style=flat-square
|
||||||
|
[8]: https://github.com/apernet/hysteria/discussions
|
||||||
|
|
||||||
|
<h2 style="text-align: center;">Hysteria is a powerful, lightning fast and censorship resistant proxy.</h2>
|
||||||
|
|
||||||
|
### [Get Started](https://v2.hysteria.network/)
|
||||||
|
|
||||||
|
### [中文文档](https://v2.hysteria.network/zh/)
|
||||||
|
|
||||||
|
### [Hysteria 1.x (legacy)](https://v1.hysteria.network/)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<div class="feature-grid">
|
||||||
|
<div>
|
||||||
|
<h3>🛠️ Jack of all trades</h3>
|
||||||
|
<p>Wide range of modes including SOCKS5, HTTP Proxy, TCP/UDP Forwarding, Linux TProxy, TUN - with more features being added constantly.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h3>⚡ Blazing fast</h3>
|
||||||
|
<p>Powered by a customized QUIC protocol, Hysteria is designed to deliver unparalleled performance over unreliable and lossy networks.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h3>✊ Censorship resistant</h3>
|
||||||
|
<p>The protocol masquerades as standard HTTP/3 traffic, making it very difficult for censors to detect and block without widespread collateral damage.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h3>💻 Cross-platform</h3>
|
||||||
|
<p>We have builds for every major platform and architecture. Deploy anywhere & use everywhere. Not to mention the long list of 3rd party apps.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h3>🔗 Easy integration</h3>
|
||||||
|
<p>With built-in support for custom authentication, traffic statistics & access control, Hysteria is easy to integrate into your infrastructure.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h3>🤗 Chill and supportive</h3>
|
||||||
|
<p>We have well-documented specifications and code for developers to contribute and/or build their own apps. And a helpful community, too.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**If you find Hysteria useful, consider giving it a ⭐️!**
|
||||||
|
|
||||||
|
[](https://star-history.com/#apernet/hysteria&Date)
|
1031
v2rayng/hysteria/app/cmd/client.go
Normal file
1031
v2rayng/hysteria/app/cmd/client.go
Normal file
File diff suppressed because it is too large
Load Diff
204
v2rayng/hysteria/app/cmd/client_test.go
Normal file
204
v2rayng/hysteria/app/cmd/client_test.go
Normal file
@@ -0,0 +1,204 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestClientConfig tests the parsing of the client config
|
||||||
|
func TestClientConfig(t *testing.T) {
|
||||||
|
viper.SetConfigFile("client_test.yaml")
|
||||||
|
err := viper.ReadInConfig()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
var config clientConfig
|
||||||
|
err = viper.Unmarshal(&config)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, config, clientConfig{
|
||||||
|
Server: "example.com",
|
||||||
|
Auth: "weak_ahh_password",
|
||||||
|
Transport: clientConfigTransport{
|
||||||
|
Type: "udp",
|
||||||
|
UDP: clientConfigTransportUDP{
|
||||||
|
HopInterval: 30 * time.Second,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Obfs: clientConfigObfs{
|
||||||
|
Type: "salamander",
|
||||||
|
Salamander: clientConfigObfsSalamander{
|
||||||
|
Password: "cry_me_a_r1ver",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
TLS: clientConfigTLS{
|
||||||
|
SNI: "another.example.com",
|
||||||
|
Insecure: true,
|
||||||
|
PinSHA256: "114515DEADBEEF",
|
||||||
|
CA: "custom_ca.crt",
|
||||||
|
},
|
||||||
|
QUIC: clientConfigQUIC{
|
||||||
|
InitStreamReceiveWindow: 1145141,
|
||||||
|
MaxStreamReceiveWindow: 1145142,
|
||||||
|
InitConnectionReceiveWindow: 1145143,
|
||||||
|
MaxConnectionReceiveWindow: 1145144,
|
||||||
|
MaxIdleTimeout: 10 * time.Second,
|
||||||
|
KeepAlivePeriod: 4 * time.Second,
|
||||||
|
DisablePathMTUDiscovery: true,
|
||||||
|
Sockopts: clientConfigQUICSockopts{
|
||||||
|
BindInterface: stringRef("eth0"),
|
||||||
|
FirewallMark: uint32Ref(1234),
|
||||||
|
FdControlUnixSocket: stringRef("test.sock"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Bandwidth: clientConfigBandwidth{
|
||||||
|
Up: "200 mbps",
|
||||||
|
Down: "1 gbps",
|
||||||
|
},
|
||||||
|
FastOpen: true,
|
||||||
|
Lazy: true,
|
||||||
|
SOCKS5: &socks5Config{
|
||||||
|
Listen: "127.0.0.1:1080",
|
||||||
|
Username: "anon",
|
||||||
|
Password: "bro",
|
||||||
|
DisableUDP: true,
|
||||||
|
},
|
||||||
|
HTTP: &httpConfig{
|
||||||
|
Listen: "127.0.0.1:8080",
|
||||||
|
Username: "qqq",
|
||||||
|
Password: "bruh",
|
||||||
|
Realm: "martian",
|
||||||
|
},
|
||||||
|
TCPForwarding: []tcpForwardingEntry{
|
||||||
|
{
|
||||||
|
Listen: "127.0.0.1:8088",
|
||||||
|
Remote: "internal.example.com:80",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
UDPForwarding: []udpForwardingEntry{
|
||||||
|
{
|
||||||
|
Listen: "127.0.0.1:5353",
|
||||||
|
Remote: "internal.example.com:53",
|
||||||
|
Timeout: 50 * time.Second,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
TCPTProxy: &tcpTProxyConfig{
|
||||||
|
Listen: "127.0.0.1:2500",
|
||||||
|
},
|
||||||
|
UDPTProxy: &udpTProxyConfig{
|
||||||
|
Listen: "127.0.0.1:2501",
|
||||||
|
Timeout: 20 * time.Second,
|
||||||
|
},
|
||||||
|
TCPRedirect: &tcpRedirectConfig{
|
||||||
|
Listen: "127.0.0.1:3500",
|
||||||
|
},
|
||||||
|
TUN: &tunConfig{
|
||||||
|
Name: "hytun",
|
||||||
|
MTU: 1500,
|
||||||
|
Timeout: 60 * time.Second,
|
||||||
|
Address: struct {
|
||||||
|
IPv4 string `mapstructure:"ipv4"`
|
||||||
|
IPv6 string `mapstructure:"ipv6"`
|
||||||
|
}{IPv4: "100.100.100.101/30", IPv6: "2001::ffff:ffff:ffff:fff1/126"},
|
||||||
|
Route: &struct {
|
||||||
|
Strict bool `mapstructure:"strict"`
|
||||||
|
IPv4 []string `mapstructure:"ipv4"`
|
||||||
|
IPv6 []string `mapstructure:"ipv6"`
|
||||||
|
IPv4Exclude []string `mapstructure:"ipv4Exclude"`
|
||||||
|
IPv6Exclude []string `mapstructure:"ipv6Exclude"`
|
||||||
|
}{
|
||||||
|
Strict: true,
|
||||||
|
IPv4: []string{"0.0.0.0/0"},
|
||||||
|
IPv6: []string{"2000::/3"},
|
||||||
|
IPv4Exclude: []string{"192.0.2.1/32"},
|
||||||
|
IPv6Exclude: []string{"2001:db8::1/128"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestClientConfigURI tests URI-related functions of clientConfig
|
||||||
|
func TestClientConfigURI(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
uri string
|
||||||
|
uriOK bool
|
||||||
|
config *clientConfig
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
uri: "hysteria2://god@zilla.jp/",
|
||||||
|
uriOK: true,
|
||||||
|
config: &clientConfig{
|
||||||
|
Server: "zilla.jp",
|
||||||
|
Auth: "god",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
uri: "hysteria2://john:wick@continental.org:4443/",
|
||||||
|
uriOK: true,
|
||||||
|
config: &clientConfig{
|
||||||
|
Server: "continental.org:4443",
|
||||||
|
Auth: "john:wick",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
uri: "hysteria2://saul@better.call:7000-10000,20000/",
|
||||||
|
uriOK: true,
|
||||||
|
config: &clientConfig{
|
||||||
|
Server: "better.call:7000-10000,20000",
|
||||||
|
Auth: "saul",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
uri: "hysteria2://noauth.com/?insecure=1&obfs=salamander&obfs-password=66ccff&pinSHA256=deadbeef&sni=crap.cc",
|
||||||
|
uriOK: true,
|
||||||
|
config: &clientConfig{
|
||||||
|
Server: "noauth.com",
|
||||||
|
Auth: "",
|
||||||
|
Obfs: clientConfigObfs{
|
||||||
|
Type: "salamander",
|
||||||
|
Salamander: clientConfigObfsSalamander{
|
||||||
|
Password: "66ccff",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
TLS: clientConfigTLS{
|
||||||
|
SNI: "crap.cc",
|
||||||
|
Insecure: true,
|
||||||
|
PinSHA256: "deadbeef",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
uri: "invalid.bs",
|
||||||
|
uriOK: false,
|
||||||
|
config: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
uri: "https://www.google.com/search?q=test",
|
||||||
|
uriOK: false,
|
||||||
|
config: nil,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.uri, func(t *testing.T) {
|
||||||
|
// Test parseURI
|
||||||
|
nc := &clientConfig{Server: test.uri}
|
||||||
|
assert.Equal(t, nc.parseURI(), test.uriOK)
|
||||||
|
if test.uriOK {
|
||||||
|
assert.Equal(t, nc, test.config)
|
||||||
|
}
|
||||||
|
// Test URI generation
|
||||||
|
if test.config != nil {
|
||||||
|
assert.Equal(t, test.config.URI(), test.uri)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func stringRef(s string) *string {
|
||||||
|
return &s
|
||||||
|
}
|
||||||
|
|
||||||
|
func uint32Ref(i uint32) *uint32 {
|
||||||
|
return &i
|
||||||
|
}
|
85
v2rayng/hysteria/app/cmd/client_test.yaml
Normal file
85
v2rayng/hysteria/app/cmd/client_test.yaml
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
server: example.com
|
||||||
|
|
||||||
|
auth: weak_ahh_password
|
||||||
|
|
||||||
|
transport:
|
||||||
|
type: udp
|
||||||
|
udp:
|
||||||
|
hopInterval: 30s
|
||||||
|
|
||||||
|
obfs:
|
||||||
|
type: salamander
|
||||||
|
salamander:
|
||||||
|
password: cry_me_a_r1ver
|
||||||
|
|
||||||
|
tls:
|
||||||
|
sni: another.example.com
|
||||||
|
insecure: true
|
||||||
|
pinSHA256: 114515DEADBEEF
|
||||||
|
ca: custom_ca.crt
|
||||||
|
|
||||||
|
quic:
|
||||||
|
initStreamReceiveWindow: 1145141
|
||||||
|
maxStreamReceiveWindow: 1145142
|
||||||
|
initConnReceiveWindow: 1145143
|
||||||
|
maxConnReceiveWindow: 1145144
|
||||||
|
maxIdleTimeout: 10s
|
||||||
|
keepAlivePeriod: 4s
|
||||||
|
disablePathMTUDiscovery: true
|
||||||
|
sockopts:
|
||||||
|
bindInterface: eth0
|
||||||
|
fwmark: 1234
|
||||||
|
fdControlUnixSocket: test.sock
|
||||||
|
|
||||||
|
bandwidth:
|
||||||
|
up: 200 mbps
|
||||||
|
down: 1 gbps
|
||||||
|
|
||||||
|
fastOpen: true
|
||||||
|
|
||||||
|
lazy: true
|
||||||
|
|
||||||
|
socks5:
|
||||||
|
listen: 127.0.0.1:1080
|
||||||
|
username: anon
|
||||||
|
password: bro
|
||||||
|
disableUDP: true
|
||||||
|
|
||||||
|
http:
|
||||||
|
listen: 127.0.0.1:8080
|
||||||
|
username: qqq
|
||||||
|
password: bruh
|
||||||
|
realm: martian
|
||||||
|
|
||||||
|
tcpForwarding:
|
||||||
|
- listen: 127.0.0.1:8088
|
||||||
|
remote: internal.example.com:80
|
||||||
|
|
||||||
|
udpForwarding:
|
||||||
|
- listen: 127.0.0.1:5353
|
||||||
|
remote: internal.example.com:53
|
||||||
|
timeout: 50s
|
||||||
|
|
||||||
|
tcpTProxy:
|
||||||
|
listen: 127.0.0.1:2500
|
||||||
|
|
||||||
|
udpTProxy:
|
||||||
|
listen: 127.0.0.1:2501
|
||||||
|
timeout: 20s
|
||||||
|
|
||||||
|
tcpRedirect:
|
||||||
|
listen: 127.0.0.1:3500
|
||||||
|
|
||||||
|
tun:
|
||||||
|
name: "hytun"
|
||||||
|
mtu: 1500
|
||||||
|
timeout: 1m
|
||||||
|
address:
|
||||||
|
ipv4: 100.100.100.101/30
|
||||||
|
ipv6: 2001::ffff:ffff:ffff:fff1/126
|
||||||
|
route:
|
||||||
|
strict: true
|
||||||
|
ipv4: [ 0.0.0.0/0 ]
|
||||||
|
ipv6: [ "2000::/3" ]
|
||||||
|
ipv4Exclude: [ 192.0.2.1/32 ]
|
||||||
|
ipv6Exclude: [ "2001:db8::1/128" ]
|
18
v2rayng/hysteria/app/cmd/errors.go
Normal file
18
v2rayng/hysteria/app/cmd/errors.go
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type configError struct {
|
||||||
|
Field string
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e configError) Error() string {
|
||||||
|
return fmt.Sprintf("invalid config: %s: %s", e.Field, e.Err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e configError) Unwrap() error {
|
||||||
|
return e.Err
|
||||||
|
}
|
63
v2rayng/hysteria/app/cmd/ping.go
Normal file
63
v2rayng/hysteria/app/cmd/ping.go
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
|
||||||
|
"github.com/apernet/hysteria/core/v2/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
// pingCmd represents the ping command
|
||||||
|
var pingCmd = &cobra.Command{
|
||||||
|
Use: "ping address",
|
||||||
|
Short: "Ping mode",
|
||||||
|
Long: "Perform a TCP ping to a specified remote address through the proxy server. Can be used as a simple connectivity test.",
|
||||||
|
Run: runPing,
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.AddCommand(pingCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func runPing(cmd *cobra.Command, args []string) {
|
||||||
|
logger.Info("ping mode")
|
||||||
|
|
||||||
|
if len(args) != 1 {
|
||||||
|
logger.Fatal("must specify one and only one address")
|
||||||
|
}
|
||||||
|
addr := args[0]
|
||||||
|
|
||||||
|
if err := viper.ReadInConfig(); err != nil {
|
||||||
|
logger.Fatal("failed to read client config", zap.Error(err))
|
||||||
|
}
|
||||||
|
var config clientConfig
|
||||||
|
if err := viper.Unmarshal(&config); err != nil {
|
||||||
|
logger.Fatal("failed to parse client config", zap.Error(err))
|
||||||
|
}
|
||||||
|
hyConfig, err := config.Config()
|
||||||
|
if err != nil {
|
||||||
|
logger.Fatal("failed to load client config", zap.Error(err))
|
||||||
|
}
|
||||||
|
|
||||||
|
c, info, err := client.NewClient(hyConfig)
|
||||||
|
if err != nil {
|
||||||
|
logger.Fatal("failed to initialize client", zap.Error(err))
|
||||||
|
}
|
||||||
|
defer c.Close()
|
||||||
|
logger.Info("connected to server",
|
||||||
|
zap.Bool("udpEnabled", info.UDPEnabled),
|
||||||
|
zap.Uint64("tx", info.Tx))
|
||||||
|
|
||||||
|
logger.Info("connecting", zap.String("addr", addr))
|
||||||
|
start := time.Now()
|
||||||
|
conn, err := c.TCP(addr)
|
||||||
|
if err != nil {
|
||||||
|
logger.Fatal("failed to connect", zap.Error(err), zap.String("time", time.Since(start).String()))
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
logger.Info("connected", zap.String("time", time.Since(start).String()))
|
||||||
|
}
|
176
v2rayng/hysteria/app/cmd/root.go
Normal file
176
v2rayng/hysteria/app/cmd/root.go
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"go.uber.org/zap/zapcore"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
appLogo = `
|
||||||
|
░█░█░█░█░█▀▀░▀█▀░█▀▀░█▀▄░▀█▀░█▀█░░░▀▀▄
|
||||||
|
░█▀█░░█░░▀▀█░░█░░█▀▀░█▀▄░░█░░█▀█░░░▄▀░
|
||||||
|
░▀░▀░░▀░░▀▀▀░░▀░░▀▀▀░▀░▀░▀▀▀░▀░▀░░░▀▀▀
|
||||||
|
`
|
||||||
|
appDesc = "a powerful, lightning fast and censorship resistant proxy"
|
||||||
|
appAuthors = "Aperture Internet Laboratory <https://github.com/apernet>"
|
||||||
|
|
||||||
|
appLogLevelEnv = "HYSTERIA_LOG_LEVEL"
|
||||||
|
appLogFormatEnv = "HYSTERIA_LOG_FORMAT"
|
||||||
|
appDisableUpdateCheckEnv = "HYSTERIA_DISABLE_UPDATE_CHECK"
|
||||||
|
appACMEDirEnv = "HYSTERIA_ACME_DIR"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// These values will be injected by the build system
|
||||||
|
appVersion = "Unknown"
|
||||||
|
appDate = "Unknown"
|
||||||
|
appType = "Unknown" // aka channel
|
||||||
|
appToolchain = "Unknown"
|
||||||
|
appCommit = "Unknown"
|
||||||
|
appPlatform = "Unknown"
|
||||||
|
appArch = "Unknown"
|
||||||
|
libVersion = "Unknown"
|
||||||
|
|
||||||
|
appVersionLong = fmt.Sprintf("Version:\t%s\n"+
|
||||||
|
"BuildDate:\t%s\n"+
|
||||||
|
"BuildType:\t%s\n"+
|
||||||
|
"Toolchain:\t%s\n"+
|
||||||
|
"CommitHash:\t%s\n"+
|
||||||
|
"Platform:\t%s\n"+
|
||||||
|
"Architecture:\t%s\n"+
|
||||||
|
"LibVersion:\t%s",
|
||||||
|
appVersion, appDate, appType, appToolchain, appCommit, appPlatform, appArch, libVersion)
|
||||||
|
|
||||||
|
appAboutLong = fmt.Sprintf("%s\n%s\n%s\n\n%s", appLogo, appDesc, appAuthors, appVersionLong)
|
||||||
|
)
|
||||||
|
|
||||||
|
var logger *zap.Logger
|
||||||
|
|
||||||
|
// Flags
|
||||||
|
var (
|
||||||
|
cfgFile string
|
||||||
|
logLevel string
|
||||||
|
logFormat string
|
||||||
|
disableUpdateCheck bool
|
||||||
|
)
|
||||||
|
|
||||||
|
var rootCmd = &cobra.Command{
|
||||||
|
Use: "hysteria",
|
||||||
|
Short: appDesc,
|
||||||
|
Long: appAboutLong,
|
||||||
|
Run: runClient, // Default to client mode
|
||||||
|
}
|
||||||
|
|
||||||
|
var logLevelMap = map[string]zapcore.Level{
|
||||||
|
"debug": zapcore.DebugLevel,
|
||||||
|
"info": zapcore.InfoLevel,
|
||||||
|
"warn": zapcore.WarnLevel,
|
||||||
|
"error": zapcore.ErrorLevel,
|
||||||
|
}
|
||||||
|
|
||||||
|
var logFormatMap = map[string]zapcore.EncoderConfig{
|
||||||
|
"console": {
|
||||||
|
TimeKey: "time",
|
||||||
|
LevelKey: "level",
|
||||||
|
NameKey: "logger",
|
||||||
|
MessageKey: "msg",
|
||||||
|
LineEnding: zapcore.DefaultLineEnding,
|
||||||
|
EncodeLevel: zapcore.CapitalColorLevelEncoder,
|
||||||
|
EncodeTime: zapcore.RFC3339TimeEncoder,
|
||||||
|
EncodeDuration: zapcore.SecondsDurationEncoder,
|
||||||
|
},
|
||||||
|
"json": {
|
||||||
|
TimeKey: "time",
|
||||||
|
LevelKey: "level",
|
||||||
|
NameKey: "logger",
|
||||||
|
MessageKey: "msg",
|
||||||
|
LineEnding: zapcore.DefaultLineEnding,
|
||||||
|
EncodeLevel: zapcore.LowercaseLevelEncoder,
|
||||||
|
EncodeTime: zapcore.EpochMillisTimeEncoder,
|
||||||
|
EncodeDuration: zapcore.SecondsDurationEncoder,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func Execute() {
|
||||||
|
err := rootCmd.Execute()
|
||||||
|
if err != nil {
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
initFlags()
|
||||||
|
cobra.MousetrapHelpText = "" // Disable the mousetrap so Windows users can run the exe directly by double-clicking
|
||||||
|
cobra.OnInitialize(initConfig)
|
||||||
|
cobra.OnInitialize(initLogger) // initLogger must come after initConfig as it depends on config
|
||||||
|
}
|
||||||
|
|
||||||
|
func initFlags() {
|
||||||
|
rootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", "", "config file")
|
||||||
|
rootCmd.PersistentFlags().StringVarP(&logLevel, "log-level", "l", envOrDefaultString(appLogLevelEnv, "info"), "log level")
|
||||||
|
rootCmd.PersistentFlags().StringVarP(&logFormat, "log-format", "f", envOrDefaultString(appLogFormatEnv, "console"), "log format")
|
||||||
|
rootCmd.PersistentFlags().BoolVar(&disableUpdateCheck, "disable-update-check", envOrDefaultBool(appDisableUpdateCheckEnv, false), "disable update check")
|
||||||
|
}
|
||||||
|
|
||||||
|
func initConfig() {
|
||||||
|
if cfgFile != "" {
|
||||||
|
viper.SetConfigFile(cfgFile)
|
||||||
|
} else {
|
||||||
|
viper.SetConfigName("config")
|
||||||
|
viper.SetConfigType("yaml")
|
||||||
|
viper.SupportedExts = append([]string{"yaml", "yml"}, viper.SupportedExts...)
|
||||||
|
viper.AddConfigPath(".")
|
||||||
|
viper.AddConfigPath("$HOME/.hysteria")
|
||||||
|
viper.AddConfigPath("/etc/hysteria/")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func initLogger() {
|
||||||
|
level, ok := logLevelMap[strings.ToLower(logLevel)]
|
||||||
|
if !ok {
|
||||||
|
fmt.Printf("unsupported log level: %s\n", logLevel)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
enc, ok := logFormatMap[strings.ToLower(logFormat)]
|
||||||
|
if !ok {
|
||||||
|
fmt.Printf("unsupported log format: %s\n", logFormat)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
c := zap.Config{
|
||||||
|
Level: zap.NewAtomicLevelAt(level),
|
||||||
|
DisableCaller: true,
|
||||||
|
DisableStacktrace: true,
|
||||||
|
Encoding: strings.ToLower(logFormat),
|
||||||
|
EncoderConfig: enc,
|
||||||
|
OutputPaths: []string{"stderr"},
|
||||||
|
ErrorOutputPaths: []string{"stderr"},
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
logger, err = c.Build()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("failed to initialize logger: %s\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func envOrDefaultString(key, def string) string {
|
||||||
|
if v := os.Getenv(key); v != "" {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
|
||||||
|
func envOrDefaultBool(key string, def bool) bool {
|
||||||
|
if v := os.Getenv(key); v != "" {
|
||||||
|
b, _ := strconv.ParseBool(v)
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
return def
|
||||||
|
}
|
1027
v2rayng/hysteria/app/cmd/server.go
Normal file
1027
v2rayng/hysteria/app/cmd/server.go
Normal file
File diff suppressed because it is too large
Load Diff
187
v2rayng/hysteria/app/cmd/server_test.go
Normal file
187
v2rayng/hysteria/app/cmd/server_test.go
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestServerConfig tests the parsing of the server config
|
||||||
|
func TestServerConfig(t *testing.T) {
|
||||||
|
viper.SetConfigFile("server_test.yaml")
|
||||||
|
err := viper.ReadInConfig()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
var config serverConfig
|
||||||
|
err = viper.Unmarshal(&config)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, config, serverConfig{
|
||||||
|
Listen: ":8443",
|
||||||
|
Obfs: serverConfigObfs{
|
||||||
|
Type: "salamander",
|
||||||
|
Salamander: serverConfigObfsSalamander{
|
||||||
|
Password: "cry_me_a_r1ver",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
TLS: &serverConfigTLS{
|
||||||
|
Cert: "some.crt",
|
||||||
|
Key: "some.key",
|
||||||
|
SNIGuard: "strict",
|
||||||
|
},
|
||||||
|
ACME: &serverConfigACME{
|
||||||
|
Domains: []string{
|
||||||
|
"sub1.example.com",
|
||||||
|
"sub2.example.com",
|
||||||
|
},
|
||||||
|
Email: "haha@cringe.net",
|
||||||
|
CA: "zero",
|
||||||
|
ListenHost: "127.0.0.9",
|
||||||
|
Dir: "random_dir",
|
||||||
|
Type: "dns",
|
||||||
|
HTTP: serverConfigACMEHTTP{
|
||||||
|
AltPort: 8888,
|
||||||
|
},
|
||||||
|
TLS: serverConfigACMETLS{
|
||||||
|
AltPort: 44333,
|
||||||
|
},
|
||||||
|
DNS: serverConfigACMEDNS{
|
||||||
|
Name: "gomommy",
|
||||||
|
Config: map[string]string{
|
||||||
|
"key1": "value1",
|
||||||
|
"key2": "value2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DisableHTTP: true,
|
||||||
|
DisableTLSALPN: true,
|
||||||
|
AltHTTPPort: 8080,
|
||||||
|
AltTLSALPNPort: 4433,
|
||||||
|
},
|
||||||
|
QUIC: serverConfigQUIC{
|
||||||
|
InitStreamReceiveWindow: 77881,
|
||||||
|
MaxStreamReceiveWindow: 77882,
|
||||||
|
InitConnectionReceiveWindow: 77883,
|
||||||
|
MaxConnectionReceiveWindow: 77884,
|
||||||
|
MaxIdleTimeout: 999 * time.Second,
|
||||||
|
MaxIncomingStreams: 256,
|
||||||
|
DisablePathMTUDiscovery: true,
|
||||||
|
},
|
||||||
|
Bandwidth: serverConfigBandwidth{
|
||||||
|
Up: "500 mbps",
|
||||||
|
Down: "100 mbps",
|
||||||
|
},
|
||||||
|
IgnoreClientBandwidth: true,
|
||||||
|
SpeedTest: true,
|
||||||
|
DisableUDP: true,
|
||||||
|
UDPIdleTimeout: 120 * time.Second,
|
||||||
|
Auth: serverConfigAuth{
|
||||||
|
Type: "password",
|
||||||
|
Password: "goofy_ahh_password",
|
||||||
|
UserPass: map[string]string{
|
||||||
|
"yolo": "swag",
|
||||||
|
"lol": "kek",
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
HTTP: serverConfigAuthHTTP{
|
||||||
|
URL: "http://127.0.0.1:5000/auth",
|
||||||
|
Insecure: true,
|
||||||
|
},
|
||||||
|
Command: "/etc/some_command",
|
||||||
|
},
|
||||||
|
Resolver: serverConfigResolver{
|
||||||
|
Type: "udp",
|
||||||
|
TCP: serverConfigResolverTCP{
|
||||||
|
Addr: "123.123.123.123:5353",
|
||||||
|
Timeout: 4 * time.Second,
|
||||||
|
},
|
||||||
|
UDP: serverConfigResolverUDP{
|
||||||
|
Addr: "4.6.8.0:53",
|
||||||
|
Timeout: 2 * time.Second,
|
||||||
|
},
|
||||||
|
TLS: serverConfigResolverTLS{
|
||||||
|
Addr: "dot.yolo.com:8853",
|
||||||
|
Timeout: 10 * time.Second,
|
||||||
|
SNI: "server1.yolo.net",
|
||||||
|
Insecure: true,
|
||||||
|
},
|
||||||
|
HTTPS: serverConfigResolverHTTPS{
|
||||||
|
Addr: "cringe.ahh.cc",
|
||||||
|
Timeout: 5 * time.Second,
|
||||||
|
SNI: "real.stuff.net",
|
||||||
|
Insecure: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Sniff: serverConfigSniff{
|
||||||
|
Enable: true,
|
||||||
|
Timeout: 1 * time.Second,
|
||||||
|
RewriteDomain: true,
|
||||||
|
TCPPorts: "80,443,1000-2000",
|
||||||
|
UDPPorts: "443",
|
||||||
|
},
|
||||||
|
ACL: serverConfigACL{
|
||||||
|
File: "chnroute.txt",
|
||||||
|
Inline: []string{
|
||||||
|
"lmao(ok)",
|
||||||
|
"kek(cringe,boba,tea)",
|
||||||
|
},
|
||||||
|
GeoIP: "some.dat",
|
||||||
|
GeoSite: "some_site.dat",
|
||||||
|
GeoUpdateInterval: 168 * time.Hour,
|
||||||
|
},
|
||||||
|
Outbounds: []serverConfigOutboundEntry{
|
||||||
|
{
|
||||||
|
Name: "goodstuff",
|
||||||
|
Type: "direct",
|
||||||
|
Direct: serverConfigOutboundDirect{
|
||||||
|
Mode: "64",
|
||||||
|
BindIPv4: "2.4.6.8",
|
||||||
|
BindIPv6: "0:0:0:0:0:ffff:0204:0608",
|
||||||
|
BindDevice: "eth233",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "badstuff",
|
||||||
|
Type: "socks5",
|
||||||
|
SOCKS5: serverConfigOutboundSOCKS5{
|
||||||
|
Addr: "shady.proxy.ru:1080",
|
||||||
|
Username: "hackerman",
|
||||||
|
Password: "Elliot Alderson",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "weirdstuff",
|
||||||
|
Type: "http",
|
||||||
|
HTTP: serverConfigOutboundHTTP{
|
||||||
|
URL: "https://eyy.lmao:4443/goofy",
|
||||||
|
Insecure: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
TrafficStats: serverConfigTrafficStats{
|
||||||
|
Listen: ":9999",
|
||||||
|
Secret: "its_me_mario",
|
||||||
|
},
|
||||||
|
Masquerade: serverConfigMasquerade{
|
||||||
|
Type: "proxy",
|
||||||
|
File: serverConfigMasqueradeFile{
|
||||||
|
Dir: "/www/masq",
|
||||||
|
},
|
||||||
|
Proxy: serverConfigMasqueradeProxy{
|
||||||
|
URL: "https://some.site.net",
|
||||||
|
RewriteHost: true,
|
||||||
|
},
|
||||||
|
String: serverConfigMasqueradeString{
|
||||||
|
Content: "aint nothin here",
|
||||||
|
Headers: map[string]string{
|
||||||
|
"content-type": "text/plain",
|
||||||
|
"custom-haha": "lol",
|
||||||
|
},
|
||||||
|
StatusCode: 418,
|
||||||
|
},
|
||||||
|
ListenHTTP: ":80",
|
||||||
|
ListenHTTPS: ":443",
|
||||||
|
ForceHTTPS: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
142
v2rayng/hysteria/app/cmd/server_test.yaml
Normal file
142
v2rayng/hysteria/app/cmd/server_test.yaml
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
listen: :8443
|
||||||
|
|
||||||
|
obfs:
|
||||||
|
type: salamander
|
||||||
|
salamander:
|
||||||
|
password: cry_me_a_r1ver
|
||||||
|
|
||||||
|
tls:
|
||||||
|
cert: some.crt
|
||||||
|
key: some.key
|
||||||
|
sniGuard: strict
|
||||||
|
|
||||||
|
acme:
|
||||||
|
domains:
|
||||||
|
- sub1.example.com
|
||||||
|
- sub2.example.com
|
||||||
|
email: haha@cringe.net
|
||||||
|
ca: zero
|
||||||
|
listenHost: 127.0.0.9
|
||||||
|
dir: random_dir
|
||||||
|
type: dns
|
||||||
|
http:
|
||||||
|
altPort: 8888
|
||||||
|
tls:
|
||||||
|
altPort: 44333
|
||||||
|
dns:
|
||||||
|
name: gomommy
|
||||||
|
config:
|
||||||
|
key1: value1
|
||||||
|
key2: value2
|
||||||
|
disableHTTP: true
|
||||||
|
disableTLSALPN: true
|
||||||
|
altHTTPPort: 8080
|
||||||
|
altTLSALPNPort: 4433
|
||||||
|
|
||||||
|
quic:
|
||||||
|
initStreamReceiveWindow: 77881
|
||||||
|
maxStreamReceiveWindow: 77882
|
||||||
|
initConnReceiveWindow: 77883
|
||||||
|
maxConnReceiveWindow: 77884
|
||||||
|
maxIdleTimeout: 999s
|
||||||
|
maxIncomingStreams: 256
|
||||||
|
disablePathMTUDiscovery: true
|
||||||
|
|
||||||
|
bandwidth:
|
||||||
|
up: 500 mbps
|
||||||
|
down: 100 mbps
|
||||||
|
|
||||||
|
ignoreClientBandwidth: true
|
||||||
|
|
||||||
|
speedTest: true
|
||||||
|
|
||||||
|
disableUDP: true
|
||||||
|
udpIdleTimeout: 120s
|
||||||
|
|
||||||
|
auth:
|
||||||
|
type: password
|
||||||
|
password: goofy_ahh_password
|
||||||
|
userpass:
|
||||||
|
yolo: swag
|
||||||
|
lol: kek
|
||||||
|
foo: bar
|
||||||
|
http:
|
||||||
|
url: http://127.0.0.1:5000/auth
|
||||||
|
insecure: true
|
||||||
|
command: /etc/some_command
|
||||||
|
|
||||||
|
resolver:
|
||||||
|
type: udp
|
||||||
|
tcp:
|
||||||
|
addr: 123.123.123.123:5353
|
||||||
|
timeout: 4s
|
||||||
|
udp:
|
||||||
|
addr: 4.6.8.0:53
|
||||||
|
timeout: 2s
|
||||||
|
tls:
|
||||||
|
addr: dot.yolo.com:8853
|
||||||
|
timeout: 10s
|
||||||
|
sni: server1.yolo.net
|
||||||
|
insecure: true
|
||||||
|
https:
|
||||||
|
addr: cringe.ahh.cc
|
||||||
|
timeout: 5s
|
||||||
|
sni: real.stuff.net
|
||||||
|
insecure: true
|
||||||
|
|
||||||
|
sniff:
|
||||||
|
enable: true
|
||||||
|
timeout: 1s
|
||||||
|
rewriteDomain: true
|
||||||
|
tcpPorts: 80,443,1000-2000
|
||||||
|
udpPorts: 443
|
||||||
|
|
||||||
|
acl:
|
||||||
|
file: chnroute.txt
|
||||||
|
inline:
|
||||||
|
- lmao(ok)
|
||||||
|
- kek(cringe,boba,tea)
|
||||||
|
geoip: some.dat
|
||||||
|
geosite: some_site.dat
|
||||||
|
geoUpdateInterval: 168h
|
||||||
|
|
||||||
|
outbounds:
|
||||||
|
- name: goodstuff
|
||||||
|
type: direct
|
||||||
|
direct:
|
||||||
|
mode: 64
|
||||||
|
bindIPv4: 2.4.6.8
|
||||||
|
bindIPv6: 0:0:0:0:0:ffff:0204:0608
|
||||||
|
bindDevice: eth233
|
||||||
|
- name: badstuff
|
||||||
|
type: socks5
|
||||||
|
socks5:
|
||||||
|
addr: shady.proxy.ru:1080
|
||||||
|
username: hackerman
|
||||||
|
password: Elliot Alderson
|
||||||
|
- name: weirdstuff
|
||||||
|
type: http
|
||||||
|
http:
|
||||||
|
url: https://eyy.lmao:4443/goofy
|
||||||
|
insecure: true
|
||||||
|
|
||||||
|
trafficStats:
|
||||||
|
listen: :9999
|
||||||
|
secret: its_me_mario
|
||||||
|
|
||||||
|
masquerade:
|
||||||
|
type: proxy
|
||||||
|
file:
|
||||||
|
dir: /www/masq
|
||||||
|
proxy:
|
||||||
|
url: https://some.site.net
|
||||||
|
rewriteHost: true
|
||||||
|
string:
|
||||||
|
content: aint nothin here
|
||||||
|
headers:
|
||||||
|
content-type: text/plain
|
||||||
|
custom-haha: lol
|
||||||
|
statusCode: 418
|
||||||
|
listenHTTP: :80
|
||||||
|
listenHTTPS: :443
|
||||||
|
forceHTTPS: true
|
55
v2rayng/hysteria/app/cmd/share.go
Normal file
55
v2rayng/hysteria/app/cmd/share.go
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/apernet/hysteria/app/v2/internal/utils"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
noText bool
|
||||||
|
withQR bool
|
||||||
|
)
|
||||||
|
|
||||||
|
// shareCmd represents the share command
|
||||||
|
var shareCmd = &cobra.Command{
|
||||||
|
Use: "share",
|
||||||
|
Short: "Generate share URI",
|
||||||
|
Long: "Generate a hysteria2:// URI from a client config for sharing",
|
||||||
|
Run: runShare,
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
initShareFlags()
|
||||||
|
rootCmd.AddCommand(shareCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func initShareFlags() {
|
||||||
|
shareCmd.Flags().BoolVar(&noText, "notext", false, "do not show URI as text")
|
||||||
|
shareCmd.Flags().BoolVar(&withQR, "qr", false, "show URI as QR code")
|
||||||
|
}
|
||||||
|
|
||||||
|
func runShare(cmd *cobra.Command, args []string) {
|
||||||
|
if err := viper.ReadInConfig(); err != nil {
|
||||||
|
logger.Fatal("failed to read client config", zap.Error(err))
|
||||||
|
}
|
||||||
|
var config clientConfig
|
||||||
|
if err := viper.Unmarshal(&config); err != nil {
|
||||||
|
logger.Fatal("failed to parse client config", zap.Error(err))
|
||||||
|
}
|
||||||
|
if _, err := config.Config(); err != nil {
|
||||||
|
logger.Fatal("failed to load client config", zap.Error(err))
|
||||||
|
}
|
||||||
|
|
||||||
|
u := config.URI()
|
||||||
|
|
||||||
|
if !noText {
|
||||||
|
fmt.Println(u)
|
||||||
|
}
|
||||||
|
if withQR {
|
||||||
|
utils.PrintQR(u)
|
||||||
|
}
|
||||||
|
}
|
178
v2rayng/hysteria/app/cmd/speedtest.go
Normal file
178
v2rayng/hysteria/app/cmd/speedtest.go
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
|
||||||
|
"github.com/apernet/hysteria/core/v2/client"
|
||||||
|
hyErrors "github.com/apernet/hysteria/core/v2/errors"
|
||||||
|
"github.com/apernet/hysteria/extras/v2/outbounds"
|
||||||
|
"github.com/apernet/hysteria/extras/v2/outbounds/speedtest"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
skipDownload bool
|
||||||
|
skipUpload bool
|
||||||
|
dataSize uint32
|
||||||
|
useBytes bool
|
||||||
|
|
||||||
|
speedtestAddr = fmt.Sprintf("%s:%d", outbounds.SpeedtestDest, 0)
|
||||||
|
)
|
||||||
|
|
||||||
|
// speedtestCmd represents the speedtest command
|
||||||
|
var speedtestCmd = &cobra.Command{
|
||||||
|
Use: "speedtest",
|
||||||
|
Short: "Speed test mode",
|
||||||
|
Long: "Perform a speed test through the proxy server. The server must have speed test support enabled.",
|
||||||
|
Run: runSpeedtest,
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
initSpeedtestFlags()
|
||||||
|
rootCmd.AddCommand(speedtestCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func initSpeedtestFlags() {
|
||||||
|
speedtestCmd.Flags().BoolVar(&skipDownload, "skip-download", false, "Skip download test")
|
||||||
|
speedtestCmd.Flags().BoolVar(&skipUpload, "skip-upload", false, "Skip upload test")
|
||||||
|
speedtestCmd.Flags().Uint32Var(&dataSize, "data-size", 1024*1024*100, "Data size for download and upload tests")
|
||||||
|
speedtestCmd.Flags().BoolVar(&useBytes, "use-bytes", false, "Use bytes per second instead of bits per second")
|
||||||
|
}
|
||||||
|
|
||||||
|
func runSpeedtest(cmd *cobra.Command, args []string) {
|
||||||
|
logger.Info("speed test mode")
|
||||||
|
|
||||||
|
if err := viper.ReadInConfig(); err != nil {
|
||||||
|
logger.Fatal("failed to read client config", zap.Error(err))
|
||||||
|
}
|
||||||
|
var config clientConfig
|
||||||
|
if err := viper.Unmarshal(&config); err != nil {
|
||||||
|
logger.Fatal("failed to parse client config", zap.Error(err))
|
||||||
|
}
|
||||||
|
hyConfig, err := config.Config()
|
||||||
|
if err != nil {
|
||||||
|
logger.Fatal("failed to load client config", zap.Error(err))
|
||||||
|
}
|
||||||
|
|
||||||
|
c, info, err := client.NewClient(hyConfig)
|
||||||
|
if err != nil {
|
||||||
|
logger.Fatal("failed to initialize client", zap.Error(err))
|
||||||
|
}
|
||||||
|
defer c.Close()
|
||||||
|
logger.Info("connected to server",
|
||||||
|
zap.Bool("udpEnabled", info.UDPEnabled),
|
||||||
|
zap.Uint64("tx", info.Tx))
|
||||||
|
|
||||||
|
signalChan := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(signalChan, os.Interrupt, syscall.SIGTERM)
|
||||||
|
defer signal.Stop(signalChan)
|
||||||
|
|
||||||
|
runChan := make(chan struct{}, 1)
|
||||||
|
go func() {
|
||||||
|
if !skipDownload {
|
||||||
|
runDownloadTest(c)
|
||||||
|
}
|
||||||
|
if !skipUpload {
|
||||||
|
runUploadTest(c)
|
||||||
|
}
|
||||||
|
runChan <- struct{}{}
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-signalChan:
|
||||||
|
logger.Info("received signal, shutting down gracefully")
|
||||||
|
case <-runChan:
|
||||||
|
logger.Info("speed test complete")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func runDownloadTest(c client.Client) {
|
||||||
|
logger.Info("performing download test")
|
||||||
|
downConn, err := c.TCP(speedtestAddr)
|
||||||
|
if err != nil {
|
||||||
|
if errors.As(err, &hyErrors.DialError{}) {
|
||||||
|
logger.Fatal("failed to connect (server may not support speed test)", zap.Error(err))
|
||||||
|
} else {
|
||||||
|
logger.Fatal("failed to connect", zap.Error(err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
defer downConn.Close()
|
||||||
|
|
||||||
|
downClient := &speedtest.Client{Conn: downConn}
|
||||||
|
currentTotal := uint32(0)
|
||||||
|
err = downClient.Download(dataSize, func(d time.Duration, b uint32, done bool) {
|
||||||
|
if !done {
|
||||||
|
currentTotal += b
|
||||||
|
logger.Info("downloading",
|
||||||
|
zap.Uint32("bytes", b),
|
||||||
|
zap.String("progress", fmt.Sprintf("%.2f%%", float64(currentTotal)/float64(dataSize)*100)),
|
||||||
|
zap.String("speed", formatSpeed(b, d, useBytes)))
|
||||||
|
} else {
|
||||||
|
logger.Info("download complete",
|
||||||
|
zap.Uint32("bytes", b),
|
||||||
|
zap.String("speed", formatSpeed(b, d, useBytes)))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
logger.Fatal("download test failed", zap.Error(err))
|
||||||
|
}
|
||||||
|
logger.Info("download test complete")
|
||||||
|
}
|
||||||
|
|
||||||
|
func runUploadTest(c client.Client) {
|
||||||
|
logger.Info("performing upload test")
|
||||||
|
upConn, err := c.TCP(speedtestAddr)
|
||||||
|
if err != nil {
|
||||||
|
if errors.As(err, &hyErrors.DialError{}) {
|
||||||
|
logger.Fatal("failed to connect (server may not support speed test)", zap.Error(err))
|
||||||
|
} else {
|
||||||
|
logger.Fatal("failed to connect", zap.Error(err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
defer upConn.Close()
|
||||||
|
|
||||||
|
upClient := &speedtest.Client{Conn: upConn}
|
||||||
|
currentTotal := uint32(0)
|
||||||
|
err = upClient.Upload(dataSize, func(d time.Duration, b uint32, done bool) {
|
||||||
|
if !done {
|
||||||
|
currentTotal += b
|
||||||
|
logger.Info("uploading",
|
||||||
|
zap.Uint32("bytes", b),
|
||||||
|
zap.String("progress", fmt.Sprintf("%.2f%%", float64(currentTotal)/float64(dataSize)*100)),
|
||||||
|
zap.String("speed", formatSpeed(b, d, useBytes)))
|
||||||
|
} else {
|
||||||
|
logger.Info("upload complete",
|
||||||
|
zap.Uint32("bytes", b),
|
||||||
|
zap.String("speed", formatSpeed(b, d, useBytes)))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
logger.Fatal("upload test failed", zap.Error(err))
|
||||||
|
}
|
||||||
|
logger.Info("upload test complete")
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatSpeed(bytes uint32, duration time.Duration, useBytes bool) string {
|
||||||
|
speed := float64(bytes) / duration.Seconds()
|
||||||
|
var units []string
|
||||||
|
if useBytes {
|
||||||
|
units = []string{"B/s", "KB/s", "MB/s", "GB/s"}
|
||||||
|
} else {
|
||||||
|
units = []string{"bps", "Kbps", "Mbps", "Gbps"}
|
||||||
|
speed *= 8
|
||||||
|
}
|
||||||
|
unitIndex := 0
|
||||||
|
for speed > 1000 && unitIndex < len(units)-1 {
|
||||||
|
speed /= 1000
|
||||||
|
unitIndex++
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%.2f %s", speed, units[unitIndex])
|
||||||
|
}
|
88
v2rayng/hysteria/app/cmd/update.go
Normal file
88
v2rayng/hysteria/app/cmd/update.go
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
|
||||||
|
"github.com/apernet/hysteria/app/v2/internal/utils"
|
||||||
|
"github.com/apernet/hysteria/core/v2/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
updateCheckInterval = 24 * time.Hour
|
||||||
|
)
|
||||||
|
|
||||||
|
// checkUpdateCmd represents the checkUpdate command
|
||||||
|
var checkUpdateCmd = &cobra.Command{
|
||||||
|
Use: "check-update",
|
||||||
|
Short: "Check for updates",
|
||||||
|
Long: "Check for updates.",
|
||||||
|
Run: runCheckUpdate,
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.AddCommand(checkUpdateCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func runCheckUpdate(cmd *cobra.Command, args []string) {
|
||||||
|
logger.Info("checking for updates",
|
||||||
|
zap.String("version", appVersion),
|
||||||
|
zap.String("platform", appPlatform),
|
||||||
|
zap.String("arch", appArch),
|
||||||
|
zap.String("channel", appType),
|
||||||
|
)
|
||||||
|
|
||||||
|
checker := utils.NewServerUpdateChecker(appVersion, appPlatform, appArch, appType)
|
||||||
|
resp, err := checker.Check()
|
||||||
|
if err != nil {
|
||||||
|
logger.Fatal("failed to check for updates", zap.Error(err))
|
||||||
|
}
|
||||||
|
if resp.HasUpdate {
|
||||||
|
logger.Info("update available",
|
||||||
|
zap.String("version", resp.LatestVersion),
|
||||||
|
zap.String("url", resp.URL),
|
||||||
|
zap.Bool("urgent", resp.Urgent),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
logger.Info("no update available")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// runCheckUpdateServer is the background update checking routine for server mode
|
||||||
|
func runCheckUpdateServer() {
|
||||||
|
checker := utils.NewServerUpdateChecker(appVersion, appPlatform, appArch, appType)
|
||||||
|
checkUpdateRoutine(checker)
|
||||||
|
}
|
||||||
|
|
||||||
|
// runCheckUpdateClient is the background update checking routine for client mode
|
||||||
|
func runCheckUpdateClient(hyClient client.Client) {
|
||||||
|
checker := utils.NewClientUpdateChecker(appVersion, appPlatform, appArch, appType, hyClient)
|
||||||
|
checkUpdateRoutine(checker)
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkUpdateRoutine(checker *utils.UpdateChecker) {
|
||||||
|
ticker := time.NewTicker(updateCheckInterval)
|
||||||
|
for {
|
||||||
|
logger.Debug("checking for updates",
|
||||||
|
zap.String("version", appVersion),
|
||||||
|
zap.String("platform", appPlatform),
|
||||||
|
zap.String("arch", appArch),
|
||||||
|
zap.String("channel", appType),
|
||||||
|
)
|
||||||
|
resp, err := checker.Check()
|
||||||
|
if err != nil {
|
||||||
|
logger.Debug("failed to check for updates", zap.Error(err))
|
||||||
|
} else if resp.HasUpdate {
|
||||||
|
logger.Info("update available",
|
||||||
|
zap.String("version", resp.LatestVersion),
|
||||||
|
zap.String("url", resp.URL),
|
||||||
|
zap.Bool("urgent", resp.Urgent),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
logger.Debug("no update available")
|
||||||
|
}
|
||||||
|
<-ticker.C
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user