├── README.md ├── build.sh ├── cmd ├── reset │ └── main.go ├── unielevate │ ├── elevate_darwin.go │ ├── elevate_windows.go │ └── main.go └── uniproxy │ └── main.go ├── common ├── balance │ └── balance.go ├── encrypt │ └── sha.go ├── file │ └── file.go └── sysproxy │ ├── darwin_test.go │ ├── sysproxy_darwin.go │ ├── sysproxy_other.go │ └── sysproxy_windows.go ├── conf └── conf.go ├── geo ├── geo.go ├── geoip.dat └── geosite.dat ├── go.mod ├── go.sum ├── handle ├── handle.go ├── proxy.go ├── rsp.go ├── server.go └── status.go ├── middleware └── logger.go ├── proxy ├── config.go ├── proxy.go └── proxy_test.go ├── router └── router.go └── v2b ├── v2b.go └── v2b_test.go /README.md: -------------------------------------------------------------------------------- 1 | # UniProxy 2 | 基于 SingBox 重新实现的 V2board 潮汐客户端核心库。 3 | 4 | ## 构建 5 | ``` bash 6 | # Windows 7 | GOOS=windows bash build.sh 8 | # MacOS 9 | GOOS=darwin bash build.sh 10 | ``` 11 | ## 使用 12 | 构建后替换潮汐客户端 `resources/libs/` 路径下同名二进制文件。 13 | 14 | ## Thanks 15 | [sing-box](https://github.com/SagerNet/sing-box) 16 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | export CGO_ENABLED=0 2 | if [ "$GOOS" == "windows" ] 3 | then 4 | suf=".exe" 5 | fi 6 | wget https://github.com/SagerNet/sing-geosite/releases/latest/download/geosite.db -O geo/geosite.dat 7 | wget https://github.com/SagerNet/sing-geoip/releases/latest/download/geoip.db -O geo/geoip.dat 8 | 9 | uni="uniproxy" 10 | cd cmd/$uni || exit 11 | go build -o ../../uniproxy$suf -ldflags '-s -w' -gcflags="all=-trimpath=${PWD}" -asmflags="all=-trimpath=${PWD}" -tags "with_quic with_gvisor with_grpc with_utls" 12 | cd ../reset || exit 13 | go build -o ../../reset$suf -ldflags '-s -w' -gcflags="all=-trimpath=${PWD}" -asmflags="all=-trimpath=${PWD}" -------------------------------------------------------------------------------- /cmd/reset/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "UniProxy/common/sysproxy" 5 | ) 6 | 7 | func main() { 8 | sysproxy.ClearSystemProxy() 9 | } 10 | -------------------------------------------------------------------------------- /cmd/unielevate/elevate_darwin.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "os/exec" 6 | ) 7 | 8 | func ExecElevateCommand(cmd string) (*exec.Cmd, error) { 9 | return nil, errors.New("not impl") 10 | } 11 | -------------------------------------------------------------------------------- /cmd/unielevate/elevate_windows.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | "os/exec" 6 | ) 7 | 8 | func ExecElevateCommand(cmd string) (*exec.Cmd, error) { 9 | c := exec.Command("elevate.exe", "-k", cmd) 10 | c.Stdout = os.Stdout 11 | c.Stderr = os.Stderr 12 | err := c.Start() 13 | if err != nil { 14 | return nil, err 15 | } 16 | return c, nil 17 | } 18 | -------------------------------------------------------------------------------- /cmd/unielevate/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | "os/signal" 6 | "strings" 7 | "syscall" 8 | ) 9 | 10 | const defaultName = "uniproxy_base.exe" 11 | 12 | func main() { 13 | var cmdArgs []string 14 | if len(os.Args) > 1 { 15 | if strings.HasPrefix(os.Args[1], "-") { 16 | cmdArgs = make([]string, 0, len(os.Args)+2) 17 | cmdArgs = append(cmdArgs, defaultName) 18 | cmdArgs = append(cmdArgs, os.Args[1:]...) 19 | cmdArgs = append(cmdArgs, "-tun") 20 | } else { 21 | cmdArgs = os.Args[1:] 22 | cmdArgs = append(cmdArgs, "-tun") 23 | } 24 | } else { 25 | cmdArgs = os.Args[1:] 26 | cmdArgs = append(cmdArgs, "-tun") 27 | } 28 | c, err := ExecElevateCommand(strings.Join(os.Args, " ")) 29 | if err != nil { 30 | return 31 | } 32 | defer c.Wait() 33 | 34 | // waiting signal 35 | s := make(chan os.Signal, 1) 36 | signal.Notify(s, syscall.SIGINT, syscall.SIGKILL, syscall.SIGTERM) 37 | <-s 38 | } 39 | -------------------------------------------------------------------------------- /cmd/uniproxy/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "UniProxy/conf" 5 | "UniProxy/proxy" 6 | "UniProxy/router" 7 | "flag" 8 | log "github.com/sirupsen/logrus" 9 | "strconv" 10 | ) 11 | 12 | var host = flag.String("host", "127.0.0.1", "host") 13 | var port = flag.Int("port", 11451, "port") 14 | var config = flag.String("conf", "", "config file") 15 | var tun = flag.Bool("tun", false, "tun mode") 16 | 17 | func main() { 18 | flag.Parse() 19 | err := conf.Init(*config) 20 | if err != nil { 21 | log.WithField("err", err).Fatalln("init conf failed") 22 | } 23 | switch conf.C.Log.Level { 24 | case "info": 25 | log.SetLevel(log.InfoLevel) 26 | case "warn": 27 | log.SetLevel(log.WarnLevel) 28 | case "error": 29 | log.SetLevel(log.ErrorLevel) 30 | } 31 | proxy.TunMode = *tun 32 | proxy.ResUrl = "http://127.0.0.1:" + strconv.Itoa(*port) 33 | router.Init() 34 | if err := router.Start(*host, *port); err != nil { 35 | log.Fatalln("start error:", err) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /common/balance/balance.go: -------------------------------------------------------------------------------- 1 | package balance 2 | 3 | import "math/rand" 4 | 5 | type List[T any] struct { 6 | balance string // round or random 7 | index int 8 | elements []T 9 | } 10 | 11 | func New[T any](balance string, elements []T) *List[T] { 12 | return &List[T]{ 13 | balance: balance, 14 | index: 0, 15 | elements: elements, 16 | } 17 | } 18 | 19 | func (l *List[T]) Next() T { 20 | var temp T 21 | if len(l.elements) == 0 { 22 | return temp 23 | } 24 | if len(l.elements) == 1 { 25 | return l.elements[0] 26 | } 27 | switch l.balance { 28 | case "round": 29 | temp = l.elements[l.index] 30 | l.index = (l.index) % len(l.elements) 31 | case "random": 32 | temp = l.elements[rand.Intn(len(l.elements))] 33 | } 34 | return temp 35 | } 36 | -------------------------------------------------------------------------------- /common/encrypt/sha.go: -------------------------------------------------------------------------------- 1 | package encrypt 2 | 3 | import ( 4 | "crypto/sha1" 5 | "encoding/hex" 6 | ) 7 | 8 | func Sha(b []byte) string { 9 | e := sha1.Sum(b) 10 | return hex.EncodeToString(e[:]) 11 | } 12 | -------------------------------------------------------------------------------- /common/file/file.go: -------------------------------------------------------------------------------- 1 | package file 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "os" 7 | ) 8 | 9 | func IsExist(path string) bool { 10 | _, err := os.Stat(path) 11 | return err == nil || !os.IsNotExist(err) 12 | } 13 | 14 | func Copy(path1 string, path2 string) error { 15 | f, err := os.Open(path1) 16 | if err != nil { 17 | return fmt.Errorf("open file1 error: %s", err) 18 | } 19 | f2, err := os.OpenFile(path2, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0744) 20 | if err != nil { 21 | return fmt.Errorf("open file2 error: %s", err) 22 | } 23 | _, err = io.Copy(f2, f) 24 | if err != nil { 25 | return fmt.Errorf("stream copy error: %s", err) 26 | } 27 | return nil 28 | } 29 | -------------------------------------------------------------------------------- /common/sysproxy/darwin_test.go: -------------------------------------------------------------------------------- 1 | package sysproxy 2 | 3 | import "testing" 4 | 5 | func TestClearSystemProxy(t *testing.T) { 6 | t.Log(ClearSystemProxy()) 7 | } 8 | -------------------------------------------------------------------------------- /common/sysproxy/sysproxy_darwin.go: -------------------------------------------------------------------------------- 1 | package sysproxy 2 | 3 | import ( 4 | "github.com/sagernet/sing/common/shell" 5 | "strings" 6 | ) 7 | 8 | func ClearSystemProxy() error { 9 | o, err := shell.Exec("networksetup", "-listallnetworkservices").ReadOutput() 10 | if err != nil { 11 | return err 12 | } 13 | nets := strings.Split(o, "\n") 14 | for i := range nets { 15 | shell.Exec("networksetup", "-setsocksfirewallproxystate", nets[i], "off").Attach().Run() 16 | shell.Exec("networksetup", "-setwebproxystate", nets[i], "off").Attach().Run() 17 | shell.Exec("networksetup", "-setsecurewebproxystate", nets[i], "off").Attach().Run() 18 | } 19 | return nil 20 | } 21 | -------------------------------------------------------------------------------- /common/sysproxy/sysproxy_other.go: -------------------------------------------------------------------------------- 1 | //go:build !(windows || darwin) 2 | 3 | package sysproxy 4 | 5 | import ( 6 | "os" 7 | ) 8 | 9 | func ClearSystemProxy() error { 10 | return os.ErrInvalid 11 | } 12 | -------------------------------------------------------------------------------- /common/sysproxy/sysproxy_windows.go: -------------------------------------------------------------------------------- 1 | package sysproxy 2 | 3 | import "github.com/sagernet/sing/common/wininet" 4 | 5 | func ClearSystemProxy() error { 6 | return wininet.ClearSystemProxy() 7 | } 8 | -------------------------------------------------------------------------------- /conf/conf.go: -------------------------------------------------------------------------------- 1 | package conf 2 | 3 | import ( 4 | "encoding/json" 5 | "os" 6 | ) 7 | 8 | var C *Conf 9 | 10 | type Conf struct { 11 | path string 12 | Log Log `json:"Log"` 13 | Api Api `json:"Api"` 14 | } 15 | 16 | type Log struct { 17 | Level string `json:"Level"` 18 | } 19 | 20 | type Api struct { 21 | Balance string `json:"Balance"` 22 | Baseurl []string `json:"Baseurl"` 23 | } 24 | 25 | func Init(path string) error { 26 | C = New(path) 27 | if path == "" { 28 | return nil 29 | } 30 | err := C.Load() 31 | if err != nil { 32 | return err 33 | } 34 | return nil 35 | } 36 | 37 | func New(path string) *Conf { 38 | return &Conf{ 39 | path: path, 40 | Log: Log{ 41 | Level: "info", 42 | }, 43 | } 44 | } 45 | 46 | func (c *Conf) Load() error { 47 | f, err := os.Open(c.path) 48 | if err != nil { 49 | return err 50 | } 51 | defer f.Close() 52 | err = json.NewDecoder(f).Decode(c) 53 | if err != nil { 54 | return err 55 | } 56 | return nil 57 | } 58 | -------------------------------------------------------------------------------- /geo/geo.go: -------------------------------------------------------------------------------- 1 | package geo 2 | 3 | import _ "embed" 4 | 5 | //go:embed geoip.dat 6 | var Ip []byte 7 | 8 | //go:embed geosite.dat 9 | var Site []byte 10 | -------------------------------------------------------------------------------- /geo/geoip.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yuzuki616/UniProxy/86ef09799d0839a85e51e61ffcb70bc113cf3a38/geo/geoip.dat -------------------------------------------------------------------------------- /geo/geosite.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yuzuki616/UniProxy/86ef09799d0839a85e51e61ffcb70bc113cf3a38/geo/geosite.dat -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module UniProxy 2 | 3 | go 1.20 4 | 5 | require ( 6 | github.com/avast/retry-go/v4 v4.6.0 7 | github.com/gin-gonic/gin v1.9.1 8 | github.com/go-resty/resty/v2 v2.7.0 9 | github.com/sagernet/sing v0.3.8 10 | github.com/sagernet/sing-box v1.8.13 11 | github.com/sirupsen/logrus v1.9.3 12 | ) 13 | 14 | require ( 15 | berty.tech/go-libtor v1.0.385 // indirect 16 | github.com/ajg/form v1.5.1 // indirect 17 | github.com/andybalholm/brotli v1.0.6 // indirect 18 | github.com/bytedance/sonic v1.9.1 // indirect 19 | github.com/caddyserver/certmagic v0.20.0 // indirect 20 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect 21 | github.com/cloudflare/circl v1.3.7 // indirect 22 | github.com/cretz/bine v0.2.0 // indirect 23 | github.com/fsnotify/fsnotify v1.7.0 // indirect 24 | github.com/gabriel-vasile/mimetype v1.4.2 // indirect 25 | github.com/gaukas/godicttls v0.0.4 // indirect 26 | github.com/gin-contrib/sse v0.1.0 // indirect 27 | github.com/go-chi/chi/v5 v5.0.12 // indirect 28 | github.com/go-chi/cors v1.2.1 // indirect 29 | github.com/go-chi/render v1.0.3 // indirect 30 | github.com/go-ole/go-ole v1.3.0 // indirect 31 | github.com/go-playground/locales v0.14.1 // indirect 32 | github.com/go-playground/universal-translator v0.18.1 // indirect 33 | github.com/go-playground/validator/v10 v10.14.0 // indirect 34 | github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect 35 | github.com/gobwas/httphead v0.1.0 // indirect 36 | github.com/gobwas/pool v0.2.1 // indirect 37 | github.com/goccy/go-json v0.10.2 // indirect 38 | github.com/gofrs/uuid/v5 v5.1.0 // indirect 39 | github.com/google/btree v1.1.2 // indirect 40 | github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a // indirect 41 | github.com/hashicorp/yamux v0.1.1 // indirect 42 | github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2 // indirect 43 | github.com/josharian/native v1.1.0 // indirect 44 | github.com/json-iterator/go v1.1.12 // indirect 45 | github.com/klauspost/compress v1.17.4 // indirect 46 | github.com/klauspost/cpuid/v2 v2.2.5 // indirect 47 | github.com/leodido/go-urn v1.2.4 // indirect 48 | github.com/libdns/alidns v1.0.3 // indirect 49 | github.com/libdns/cloudflare v0.1.1 // indirect 50 | github.com/libdns/libdns v0.2.2 // indirect 51 | github.com/logrusorgru/aurora v2.0.3+incompatible // indirect 52 | github.com/mattn/go-isatty v0.0.19 // indirect 53 | github.com/mholt/acmez v1.2.0 // indirect 54 | github.com/miekg/dns v1.1.59 // indirect 55 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 56 | github.com/modern-go/reflect2 v1.0.2 // indirect 57 | github.com/onsi/ginkgo/v2 v2.9.7 // indirect 58 | github.com/ooni/go-libtor v1.1.8 // indirect 59 | github.com/oschwald/maxminddb-golang v1.12.0 // indirect 60 | github.com/pelletier/go-toml/v2 v2.0.8 // indirect 61 | github.com/pierrec/lz4/v4 v4.1.14 // indirect 62 | github.com/quic-go/qpack v0.4.0 // indirect 63 | github.com/quic-go/qtls-go1-20 v0.4.1 // indirect 64 | github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a // indirect 65 | github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1 // indirect 66 | github.com/sagernet/gvisor v0.0.0-20231209105102-8d27a30e436e // indirect 67 | github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect 68 | github.com/sagernet/quic-go v0.40.1 // indirect 69 | github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 // indirect 70 | github.com/sagernet/sing-dns v0.1.14 // indirect 71 | github.com/sagernet/sing-mux v0.2.0 // indirect 72 | github.com/sagernet/sing-quic v0.1.12 // indirect 73 | github.com/sagernet/sing-shadowsocks v0.2.6 // indirect 74 | github.com/sagernet/sing-shadowsocks2 v0.2.0 // indirect 75 | github.com/sagernet/sing-shadowtls v0.1.4 // indirect 76 | github.com/sagernet/sing-tun v0.2.7 // indirect 77 | github.com/sagernet/sing-vmess v0.1.8 // indirect 78 | github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 // indirect 79 | github.com/sagernet/tfo-go v0.0.0-20231209031829-7b5343ac1dc6 // indirect 80 | github.com/sagernet/utls v1.5.4 // indirect 81 | github.com/sagernet/wireguard-go v0.0.0-20231215174105-89dec3b2f3e8 // indirect 82 | github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 // indirect 83 | github.com/twitchyliquid64/golang-asm v0.15.1 // indirect 84 | github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect 85 | github.com/ugorji/go/codec v1.2.11 // indirect 86 | github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect 87 | github.com/zeebo/blake3 v0.2.3 // indirect 88 | go.uber.org/multierr v1.11.0 // indirect 89 | go.uber.org/zap v1.27.0 // indirect 90 | go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect 91 | golang.org/x/arch v0.3.0 // indirect 92 | golang.org/x/crypto v0.22.0 // indirect 93 | golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f // indirect 94 | golang.org/x/mod v0.17.0 // indirect 95 | golang.org/x/net v0.24.0 // indirect 96 | golang.org/x/sync v0.7.0 // indirect 97 | golang.org/x/sys v0.19.0 // indirect 98 | golang.org/x/text v0.14.0 // indirect 99 | golang.org/x/time v0.5.0 // indirect 100 | golang.org/x/tools v0.20.0 // indirect 101 | google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect 102 | google.golang.org/grpc v1.63.2 // indirect 103 | google.golang.org/protobuf v1.33.0 // indirect 104 | gopkg.in/yaml.v3 v3.0.1 // indirect 105 | lukechampine.com/blake3 v1.2.1 // indirect 106 | ) 107 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | berty.tech/go-libtor v1.0.385 h1:RWK94C3hZj6Z2GdvePpHJLnWYobFr3bY/OdUJ5aoEXw= 2 | berty.tech/go-libtor v1.0.385/go.mod h1:9swOOQVb+kmvuAlsgWUK/4c52pm69AdbJsxLzk+fJEw= 3 | github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= 4 | github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= 5 | github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI= 6 | github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= 7 | github.com/avast/retry-go/v4 v4.6.0 h1:K9xNA+KeB8HHc2aWFuLb25Offp+0iVRXEvFx8IinRJA= 8 | github.com/avast/retry-go/v4 v4.6.0/go.mod h1:gvWlPhBVsvBbLkVGDg/KwvBv0bEkCOLRRSHKIr2PyOE= 9 | github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= 10 | github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= 11 | github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= 12 | github.com/caddyserver/certmagic v0.20.0 h1:bTw7LcEZAh9ucYCRXyCpIrSAGplplI0vGYJ4BpCQ/Fc= 13 | github.com/caddyserver/certmagic v0.20.0/go.mod h1:N4sXgpICQUskEWpj7zVzvWD41p3NYacrNoZYiRM2jTg= 14 | github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= 15 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= 16 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= 17 | github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= 18 | github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= 19 | github.com/cretz/bine v0.1.0/go.mod h1:6PF6fWAvYtwjRGkAuDEJeWNOv3a2hUouSP/yRYXmvHw= 20 | github.com/cretz/bine v0.2.0 h1:8GiDRGlTgz+o8H9DSnsl+5MeBK4HsExxgl6WgzOCuZo= 21 | github.com/cretz/bine v0.2.0/go.mod h1:WU4o9QR9wWp8AVKtTM1XD5vUHkEqnf2vVSo6dBqbetI= 22 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 23 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 24 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 25 | github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= 26 | github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= 27 | github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= 28 | github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= 29 | github.com/gaukas/godicttls v0.0.4 h1:NlRaXb3J6hAnTmWdsEKb9bcSBD6BvcIjdGdeb0zfXbk= 30 | github.com/gaukas/godicttls v0.0.4/go.mod h1:l6EenT4TLWgTdwslVb4sEMOCf7Bv0JAK67deKr9/NCI= 31 | github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= 32 | github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= 33 | github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= 34 | github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= 35 | github.com/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s= 36 | github.com/go-chi/chi/v5 v5.0.12/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= 37 | github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4= 38 | github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= 39 | github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4= 40 | github.com/go-chi/render v1.0.3/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0= 41 | github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= 42 | github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= 43 | github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= 44 | github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= 45 | github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= 46 | github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= 47 | github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= 48 | github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= 49 | github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= 50 | github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= 51 | github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY= 52 | github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I= 53 | github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= 54 | github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= 55 | github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= 56 | github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= 57 | github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= 58 | github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= 59 | github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= 60 | github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= 61 | github.com/gofrs/uuid/v5 v5.1.0 h1:S5rqVKIigghZTCBKPCw0Y+bXkn26K3TB5mvQq2Ix8dk= 62 | github.com/gofrs/uuid/v5 v5.1.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8= 63 | github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= 64 | github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= 65 | github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= 66 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 67 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 68 | github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a h1:fEBsGL/sjAuJrgah5XqmmYsTLzJp/TO9Lhy39gkverk= 69 | github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= 70 | github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= 71 | github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= 72 | github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2 h1:9K06NfxkBh25x56yVhWWlKFE8YpicaSfHwoV8SFbueA= 73 | github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2/go.mod h1:3A9PQ1cunSDF/1rbTq99Ts4pVnycWg+vlPkfeD2NLFI= 74 | github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= 75 | github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA= 76 | github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= 77 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 78 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 79 | github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= 80 | github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= 81 | github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= 82 | github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= 83 | github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= 84 | github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= 85 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 86 | github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= 87 | github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= 88 | github.com/libdns/alidns v1.0.3 h1:LFHuGnbseq5+HCeGa1aW8awyX/4M2psB9962fdD2+yQ= 89 | github.com/libdns/alidns v1.0.3/go.mod h1:e18uAG6GanfRhcJj6/tps2rCMzQJaYVcGKT+ELjdjGE= 90 | github.com/libdns/cloudflare v0.1.1 h1:FVPfWwP8zZCqj268LZjmkDleXlHPlFU9KC4OJ3yn054= 91 | github.com/libdns/cloudflare v0.1.1/go.mod h1:9VK91idpOjg6v7/WbjkEW49bSCxj00ALesIFDhJ8PBU= 92 | github.com/libdns/libdns v0.2.0/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40= 93 | github.com/libdns/libdns v0.2.2 h1:O6ws7bAfRPaBsgAYt8MDe2HcNBGC29hkZ9MX2eUSX3s= 94 | github.com/libdns/libdns v0.2.2/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ= 95 | github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8= 96 | github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= 97 | github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= 98 | github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 99 | github.com/mholt/acmez v1.2.0 h1:1hhLxSgY5FvH5HCnGUuwbKY2VQVo8IU7rxXKSnZ7F30= 100 | github.com/mholt/acmez v1.2.0/go.mod h1:VT9YwH1xgNX1kmYY89gY8xPJC84BFAisjo8Egigt4kE= 101 | github.com/miekg/dns v1.1.59 h1:C9EXc/UToRwKLhK5wKU/I4QVsBUc8kE6MkHBkeypWZs= 102 | github.com/miekg/dns v1.1.59/go.mod h1:nZpewl5p6IvctfgrckopVx2OlSEHPRO/U4SYkRklrEk= 103 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 104 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 105 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 106 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= 107 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 108 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= 109 | github.com/onsi/ginkgo/v2 v2.9.7 h1:06xGQy5www2oN160RtEZoTvnP2sPhEfePYmCDc2szss= 110 | github.com/onsi/ginkgo/v2 v2.9.7/go.mod h1:cxrmXWykAwTwhQsJOPfdIDiJ+l2RYq7U8hFU+M/1uw0= 111 | github.com/onsi/gomega v1.27.7 h1:fVih9JD6ogIiHUN6ePK7HJidyEDpWGVB5mzM7cWNXoU= 112 | github.com/ooni/go-libtor v1.1.8 h1:Wo3V3DVTxl5vZdxtQakqYP+DAHx7pPtAFSl1bnAa08w= 113 | github.com/ooni/go-libtor v1.1.8/go.mod h1:q1YyLwRD9GeMyeerVvwc0vJ2YgwDLTp2bdVcrh/JXyI= 114 | github.com/oschwald/maxminddb-golang v1.12.0 h1:9FnTOD0YOhP7DGxGsq4glzpGy5+w7pq50AS6wALUMYs= 115 | github.com/oschwald/maxminddb-golang v1.12.0/go.mod h1:q0Nob5lTCqyQ8WT6FYgS1L7PXKVVbgiymefNwIjPzgY= 116 | github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= 117 | github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= 118 | github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE= 119 | github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= 120 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 121 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 122 | github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= 123 | github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= 124 | github.com/quic-go/qtls-go1-20 v0.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs= 125 | github.com/quic-go/qtls-go1-20 v0.4.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k= 126 | github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a h1:+NkI2670SQpQWvkkD2QgdTuzQG263YZ+2emfpeyGqW0= 127 | github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a/go.mod h1:63s7jpZqcDAIpj8oI/1v4Izok+npJOHACFCU6+huCkM= 128 | github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1 h1:YbmpqPQEMdlk9oFSKYWRqVuu9qzNiOayIonKmv1gCXY= 129 | github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1/go.mod h1:J2yAxTFPDjrDPhuAi9aWFz2L3ox9it4qAluBBbN0H5k= 130 | github.com/sagernet/gvisor v0.0.0-20231209105102-8d27a30e436e h1:DOkjByVeAR56dkszjnMZke4wr7yM/1xHaJF3G9olkEE= 131 | github.com/sagernet/gvisor v0.0.0-20231209105102-8d27a30e436e/go.mod h1:fLxq/gtp0qzkaEwywlRRiGmjOK5ES/xUzyIKIFP2Asw= 132 | github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE= 133 | github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM= 134 | github.com/sagernet/quic-go v0.40.1 h1:qLeTIJR0d0JWRmDWo346nLsVN6EWihd1kalJYPEd0TM= 135 | github.com/sagernet/quic-go v0.40.1/go.mod h1:CcKTpzTAISxrM4PA5M20/wYuz9Tj6Tx4DwGbNl9UQrU= 136 | github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc= 137 | github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU= 138 | github.com/sagernet/sing v0.2.18/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo= 139 | github.com/sagernet/sing v0.3.8 h1:gm4JKalPhydMYX2zFOTnnd4TXtM/16WFRqSjMepYQQk= 140 | github.com/sagernet/sing v0.3.8/go.mod h1:+60H3Cm91RnL9dpVGWDPHt0zTQImO9Vfqt9a4rSambI= 141 | github.com/sagernet/sing-box v1.8.13 h1:M8mLo7AWDKV9d+Bq41fOz7B5CvS4+xhBR1tmwzrr7PY= 142 | github.com/sagernet/sing-box v1.8.13/go.mod h1:Cb0FBO7r5UnQJQ7Ql0jgdJ8SmzCyFvCQ7eeXq2clTOI= 143 | github.com/sagernet/sing-dns v0.1.14 h1:kxE/Ik3jMXmD3sXsdt9MgrNzLFWt64mghV+MQqzyf40= 144 | github.com/sagernet/sing-dns v0.1.14/go.mod h1:AA+vZMNovuPN5i/sPnfF6756Nq94nzb5nXodMWbta5w= 145 | github.com/sagernet/sing-mux v0.2.0 h1:4C+vd8HztJCWNYfufvgL49xaOoOHXty2+EAjnzN3IYo= 146 | github.com/sagernet/sing-mux v0.2.0/go.mod h1:khzr9AOPocLa+g53dBplwNDz4gdsyx/YM3swtAhlkHQ= 147 | github.com/sagernet/sing-quic v0.1.12 h1:4KjG7LASZck0svGDfzf3aVNidRRQRC/w2HUMk/PHiNE= 148 | github.com/sagernet/sing-quic v0.1.12/go.mod h1:L+VtzvuPbf8VW8F4R7KiygqpXY4lO7t2wwcQuHjh8Ew= 149 | github.com/sagernet/sing-shadowsocks v0.2.6 h1:xr7ylAS/q1cQYS8oxKKajhuQcchd5VJJ4K4UZrrpp0s= 150 | github.com/sagernet/sing-shadowsocks v0.2.6/go.mod h1:j2YZBIpWIuElPFL/5sJAj470bcn/3QQ5lxZUNKLDNAM= 151 | github.com/sagernet/sing-shadowsocks2 v0.2.0 h1:wpZNs6wKnR7mh1wV9OHwOyUr21VkS3wKFHi+8XwgADg= 152 | github.com/sagernet/sing-shadowsocks2 v0.2.0/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ= 153 | github.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnVpEx6Tw3k= 154 | github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4= 155 | github.com/sagernet/sing-tun v0.2.7 h1:6QtJkeSj6BTTQPGxbbiuV8eh7GdV46w2G0N8CmISwdc= 156 | github.com/sagernet/sing-tun v0.2.7/go.mod h1:MKAAHUzVfj7d9zos4lsz6wjXu86/mJyd/gejiAnWj/w= 157 | github.com/sagernet/sing-vmess v0.1.8 h1:XVWad1RpTy9b5tPxdm5MCU8cGfrTGdR8qCq6HV2aCNc= 158 | github.com/sagernet/sing-vmess v0.1.8/go.mod h1:vhx32UNzTDUkNwOyIjcZQohre1CaytquC5mPplId8uA= 159 | github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ= 160 | github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7/go.mod h1:FP9X2xjT/Az1EsG/orYYoC+5MojWnuI7hrffz8fGwwo= 161 | github.com/sagernet/tfo-go v0.0.0-20231209031829-7b5343ac1dc6 h1:z3SJQhVyU63FT26Wn/UByW6b7q8QKB0ZkPqsyqcz2PI= 162 | github.com/sagernet/tfo-go v0.0.0-20231209031829-7b5343ac1dc6/go.mod h1:73xRZuxwkFk4aiLw28hG8W6o9cr2UPrGL9pdY2UTbvY= 163 | github.com/sagernet/utls v1.5.4 h1:KmsEGbB2dKUtCNC+44NwAdNAqnqQ6GA4pTO0Yik56co= 164 | github.com/sagernet/utls v1.5.4/go.mod h1:CTGxPWExIloRipK3XFpYv0OVyhO8kk3XCGW/ieyTh1s= 165 | github.com/sagernet/wireguard-go v0.0.0-20231215174105-89dec3b2f3e8 h1:R0OMYAScomNAVpTfbHFpxqJpvwuhxSRi+g6z7gZhABs= 166 | github.com/sagernet/wireguard-go v0.0.0-20231215174105-89dec3b2f3e8/go.mod h1:K4J7/npM+VAMUeUmTa2JaA02JmyheP0GpRBOUvn3ecc= 167 | github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 h1:6uUiZcDRnZSAegryaUGwPC/Fj13JSHwiTftrXhMmYOc= 168 | github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854/go.mod h1:LtfoSK3+NG57tvnVEHgcuBW9ujgE8enPSgzgwStwCAA= 169 | github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= 170 | github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= 171 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 172 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 173 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 174 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 175 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 176 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 177 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 178 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 179 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 180 | github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 181 | github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 182 | github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= 183 | github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= 184 | github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= 185 | github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 h1:tHNk7XK9GkmKUR6Gh8gVBKXc2MVSZ4G/NnWLtzw4gNA= 186 | github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264= 187 | github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= 188 | github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= 189 | github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg= 190 | github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= 191 | github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY= 192 | github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= 193 | github.com/zeebo/blake3 v0.2.3 h1:TFoLXsjeXqRNFxSbk35Dk4YtszE/MQQGK10BH4ptoTg= 194 | github.com/zeebo/blake3 v0.2.3/go.mod h1:mjJjZpnsyIVtVgTOSpJ9vmRE4wgDeyt2HU3qXvvKCaQ= 195 | github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo= 196 | github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4= 197 | go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= 198 | go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= 199 | go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= 200 | go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= 201 | go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= 202 | go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M= 203 | go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= 204 | golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= 205 | golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= 206 | golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= 207 | golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= 208 | golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= 209 | golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= 210 | golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= 211 | golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f h1:99ci1mjWVBWwJiEKYY6jWa4d2nTQVIEhZIptnrVb1XY= 212 | golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI= 213 | golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= 214 | golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 215 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 216 | golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 217 | golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 218 | golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= 219 | golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= 220 | golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= 221 | golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 222 | golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 223 | golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 224 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 225 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 226 | golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 227 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 228 | golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 229 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 230 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 231 | golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 232 | golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= 233 | golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 234 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 235 | golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q= 236 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 237 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 238 | golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= 239 | golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 240 | golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= 241 | golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= 242 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 243 | golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY= 244 | golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg= 245 | google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de h1:cZGRis4/ot9uVm639a+rHCUaG0JJHEsdyzSQTMX+suY= 246 | google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY= 247 | google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM= 248 | google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= 249 | google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= 250 | google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= 251 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 252 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= 253 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 254 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 255 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 256 | lukechampine.com/blake3 v1.2.1 h1:YuqqRuaqsGV71BV/nm9xlI0MKUv4QC54jQnBChWbGnI= 257 | lukechampine.com/blake3 v1.2.1/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= 258 | rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= 259 | -------------------------------------------------------------------------------- /handle/handle.go: -------------------------------------------------------------------------------- 1 | package handle 2 | 3 | import ( 4 | "UniProxy/common/balance" 5 | "UniProxy/conf" 6 | "github.com/gin-gonic/gin" 7 | log "github.com/sirupsen/logrus" 8 | "io" 9 | "net/http" 10 | "net/http/httputil" 11 | "net/url" 12 | "strings" 13 | ) 14 | 15 | var urlBalance *balance.List[string] 16 | 17 | func ReverseProxy(c *gin.Context) { 18 | if urlBalance == nil { 19 | urlBalance = balance.New[string](conf.C.Api.Balance, conf.C.Api.Baseurl) 20 | } 21 | if len(conf.C.Api.Balance) == 0 { 22 | return 23 | } 24 | u := urlBalance.Next() 25 | target, err := url.Parse(u) 26 | if err != nil { 27 | log.WithField("err", err).Error("parse url failed") 28 | c.JSON(400, Rsp{ 29 | Success: false, 30 | }) 31 | } 32 | proxy := httputil.NewSingleHostReverseProxy(target) 33 | proxy.Director = func(req *http.Request) { 34 | req.Host = target.Host 35 | req.URL.Scheme = target.Scheme 36 | req.URL.Host = target.Host 37 | if len(req.Form) != 0 { 38 | r := strings.NewReader(req.Form.Encode()) 39 | req.ContentLength = int64(r.Len()) 40 | req.Body = io.NopCloser(r) 41 | } 42 | } 43 | proxy.ServeHTTP(c.Writer, c.Request) 44 | } 45 | -------------------------------------------------------------------------------- /handle/proxy.go: -------------------------------------------------------------------------------- 1 | package handle 2 | 3 | import ( 4 | "UniProxy/proxy" 5 | "github.com/gin-gonic/gin" 6 | log "github.com/sirupsen/logrus" 7 | ) 8 | 9 | type StartUniProxyRequest struct { 10 | Tag string `json:"tag"` 11 | Uuid string `json:"uuid"` 12 | GlobalMode bool `json:"global_mode"` 13 | } 14 | 15 | func StartUniProxy(c *gin.Context) { 16 | p := StartUniProxyRequest{} 17 | err := c.ShouldBindJSON(&p) 18 | if err != nil { 19 | c.JSON(400, Rsp{ 20 | Success: false, 21 | }) 22 | return 23 | } 24 | proxy.GlobalMode = p.GlobalMode 25 | err = proxy.StartProxy(p.Tag, p.Uuid, servers[p.Tag]) 26 | if err != nil { 27 | log.WithField("err", err).Error("start proxy failed") 28 | c.JSON(400, Rsp{ 29 | Success: false, 30 | Message: err.Error(), 31 | }) 32 | return 33 | } 34 | c.JSON(200, Rsp{ 35 | Success: true, 36 | Message: "ok", 37 | Data: StatusData{ 38 | Inited: inited, 39 | Running: proxy.Running, 40 | GlobalMode: proxy.GlobalMode, 41 | SystemProxy: proxy.SystemProxy, 42 | }, 43 | }) 44 | return 45 | } 46 | 47 | func StopUniProxy(c *gin.Context) { 48 | if proxy.Running { 49 | proxy.StopProxy() 50 | } 51 | c.JSON(200, Rsp{ 52 | Success: true, 53 | Message: "ok", 54 | }) 55 | } 56 | 57 | func SetSystemProxy(c *gin.Context) { 58 | c.JSON(200, Rsp{ 59 | Success: true, 60 | Message: "ok", 61 | }) 62 | } 63 | 64 | func ClearSystemProxy(c *gin.Context) { 65 | err := proxy.ClearSystemProxy() 66 | if err != nil { 67 | c.JSON(200, Rsp{ 68 | Success: false, 69 | Message: err.Error(), 70 | }) 71 | } 72 | c.JSON(200, Rsp{ 73 | Success: true, 74 | Message: "ok", 75 | }) 76 | } 77 | -------------------------------------------------------------------------------- /handle/rsp.go: -------------------------------------------------------------------------------- 1 | package handle 2 | 3 | type Rsp struct { 4 | Success bool `json:"success"` 5 | Message string `json:"message"` 6 | Data interface{} `json:"data"` 7 | } 8 | 9 | type StatusData struct { 10 | GlobalMode bool `json:"global_mode"` 11 | Inited bool `json:"inited"` 12 | Running bool `json:"running"` 13 | SystemProxy bool `json:"system_proxy"` 14 | } 15 | -------------------------------------------------------------------------------- /handle/server.go: -------------------------------------------------------------------------------- 1 | package handle 2 | 3 | import ( 4 | "UniProxy/v2b" 5 | "fmt" 6 | "github.com/gin-gonic/gin" 7 | log "github.com/sirupsen/logrus" 8 | "time" 9 | ) 10 | 11 | var servers map[string]*v2b.ServerInfo 12 | var updateTime time.Time 13 | 14 | func GetServers(c *gin.Context) { 15 | if len(servers) != 0 { 16 | if time.Now().Before(updateTime) { 17 | c.JSON(200, Rsp{ 18 | Success: true, 19 | Data: servers, 20 | }) 21 | return 22 | } 23 | } 24 | r, err := v2b.GetServers() 25 | if err != nil { 26 | log.Error("get server list error: ", err) 27 | c.JSON(400, Rsp{Success: false, Message: err.Error()}) 28 | return 29 | } 30 | updateTime = time.Now().Add(180 * time.Hour) 31 | if len(r) != 0 { 32 | servers = make(map[string]*v2b.ServerInfo, len(r)) 33 | for i := range r { 34 | servers[fmt.Sprintf( 35 | "%s_%d", 36 | r[i].Type, 37 | r[i].Id, 38 | )] = &r[i] 39 | } 40 | } 41 | c.JSON(200, Rsp{ 42 | Success: true, 43 | Data: servers, 44 | }) 45 | return 46 | } 47 | -------------------------------------------------------------------------------- /handle/status.go: -------------------------------------------------------------------------------- 1 | package handle 2 | 3 | import ( 4 | "UniProxy/common/balance" 5 | "UniProxy/conf" 6 | "UniProxy/proxy" 7 | "UniProxy/v2b" 8 | "github.com/gin-gonic/gin" 9 | log "github.com/sirupsen/logrus" 10 | "os" 11 | "path" 12 | ) 13 | 14 | type initParamsRequest struct { 15 | MixedPort int `json:"mixed_port"` 16 | AppName string `json:"app_name"` 17 | Url string `json:"url"` 18 | Token string `json:"token"` 19 | License string `json:"license"` 20 | UserPath string `json:"user_path"` 21 | } 22 | 23 | var inited bool 24 | 25 | func InitParams(c *gin.Context) { 26 | p := initParamsRequest{} 27 | err := c.ShouldBindJSON(&p) 28 | if err != nil { 29 | c.JSON(400, &Rsp{Success: false, Message: err.Error()}) 30 | return 31 | } 32 | f, err := os.OpenFile(path.Join(p.UserPath, "uniproxy.log"), os.O_TRUNC|os.O_RDWR|os.O_CREATE, 0755) 33 | if err != nil { 34 | c.JSON(400, &Rsp{Success: false, Message: err.Error()}) 35 | return 36 | } 37 | log.SetOutput(f) 38 | if len(conf.C.Api.Baseurl) == 0 { 39 | conf.C.Api.Baseurl = []string{p.Url} 40 | } 41 | urlBalance = balance.New[string](conf.C.Api.Balance, conf.C.Api.Baseurl) 42 | v2b.Init(conf.C.Api.Balance, conf.C.Api.Baseurl, p.Token) 43 | proxy.InPort = p.MixedPort 44 | proxy.DataPath = p.UserPath 45 | servers = make(map[string]*v2b.ServerInfo) 46 | inited = true 47 | c.JSON(200, &Rsp{Success: true}) 48 | } 49 | 50 | func GetStatus(c *gin.Context) { 51 | c.JSON(200, &Rsp{ 52 | Success: true, 53 | Data: StatusData{ 54 | Inited: inited, 55 | Running: proxy.Running, 56 | GlobalMode: proxy.GlobalMode, 57 | SystemProxy: proxy.SystemProxy, 58 | }, 59 | }) 60 | } 61 | -------------------------------------------------------------------------------- /middleware/logger.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | log "github.com/sirupsen/logrus" 6 | "time" 7 | ) 8 | 9 | func Logger(c *gin.Context) { 10 | path := c.Request.URL.Path 11 | raw := c.Request.URL.RawQuery 12 | // Start timer 13 | start := time.Now() 14 | log.Infof("[%s:%d] | %s %s", 15 | c.Request.Method, c.Writer.Status(), c.ClientIP()+" |", path) 16 | // Process request 17 | c.Next() 18 | latency := time.Now().Sub(start) 19 | if latency > time.Minute { 20 | latency = latency - latency%time.Second 21 | } 22 | if raw != "" { 23 | path = path + "?" + raw 24 | } 25 | log.Infof("[%s:%d] %s | %s %s", 26 | c.Request.Method, c.Writer.Status(), latency, c.ClientIP()+" |", path) 27 | } 28 | -------------------------------------------------------------------------------- /proxy/config.go: -------------------------------------------------------------------------------- 1 | package proxy 2 | 3 | import ( 4 | "UniProxy/common/file" 5 | "UniProxy/geo" 6 | "UniProxy/v2b" 7 | "encoding/base64" 8 | "errors" 9 | "fmt" 10 | C "github.com/sagernet/sing-box/constant" 11 | "github.com/sagernet/sing-box/option" 12 | "net/netip" 13 | "net/url" 14 | "os" 15 | "path" 16 | "strconv" 17 | ) 18 | 19 | func GetSingBoxConfig(uuid string, server *v2b.ServerInfo) (option.Options, error) { 20 | in := option.Inbound{} 21 | if TunMode { 22 | in.Type = "tun" 23 | in.TunOptions = option.TunInboundOptions{ 24 | Inet4Address: option.Listable[netip.Prefix]{ 25 | netip.MustParsePrefix("172.19.0.1/30"), 26 | }, 27 | Inet6Address: option.Listable[netip.Prefix]{ 28 | netip.MustParsePrefix("fdfe:dcba:9876::1/126"), 29 | }, 30 | MTU: 9000, 31 | AutoRoute: true, 32 | StrictRoute: true, 33 | Inet4RouteAddress: option.Listable[netip.Prefix]{ 34 | netip.MustParsePrefix("0.0.0.0/1"), 35 | netip.MustParsePrefix("128.0.0.0/1"), 36 | }, 37 | Inet6RouteAddress: option.Listable[netip.Prefix]{ 38 | netip.MustParsePrefix("::/1"), 39 | netip.MustParsePrefix("8000::/1"), 40 | }, 41 | Stack: "gvisor", 42 | } 43 | } else { 44 | in.Type = "mixed" 45 | addr, _ := netip.ParseAddr("127.0.0.1") 46 | in.MixedOptions = option.HTTPMixedInboundOptions{ 47 | ListenOptions: option.ListenOptions{ 48 | Listen: (*option.ListenAddress)(&addr), 49 | ListenPort: uint16(InPort), 50 | }, 51 | SetSystemProxy: SystemProxy, 52 | } 53 | } 54 | so := option.ServerOptions{ 55 | Server: server.Host, 56 | ServerPort: uint16(server.Port), 57 | } 58 | var out option.Outbound 59 | switch server.Type { 60 | case "vmess", "vless": 61 | transport := &option.V2RayTransportOptions{ 62 | Type: server.Network, 63 | } 64 | switch transport.Type { 65 | case "tcp": 66 | transport.Type = "" 67 | case "http": 68 | case "ws": 69 | var u *url.URL 70 | u, err := url.Parse(server.NetworkSettings.Path) 71 | if err != nil { 72 | return option.Options{}, err 73 | } 74 | ed, _ := strconv.Atoi(u.Query().Get("ed")) 75 | transport.WebsocketOptions.EarlyDataHeaderName = "Sec-WebSocket-Protocol" 76 | transport.WebsocketOptions.MaxEarlyData = uint32(ed) 77 | transport.WebsocketOptions.Path = u.Path 78 | case "grpc": 79 | transport.GRPCOptions.ServiceName = server.ServerName 80 | } 81 | out = option.Outbound{ 82 | Tag: "p", 83 | Type: server.Type, 84 | } 85 | if server.Type == "vmess" { 86 | out.VMessOptions = option.VMessOutboundOptions{ 87 | UUID: uuid, 88 | Security: "auto", 89 | AuthenticatedLength: true, 90 | Network: "tcp", 91 | ServerOptions: so, 92 | Transport: transport, 93 | } 94 | if server.Tls == 1 { 95 | out.VMessOptions.TLS = &option.OutboundTLSOptions{ 96 | Enabled: true, 97 | ServerName: server.ServerName, 98 | Insecure: server.TlsSettings.AllowInsecure != "0", 99 | } 100 | } 101 | } else { 102 | out.VLESSOptions = option.VLESSOutboundOptions{ 103 | UUID: uuid, 104 | ServerOptions: so, 105 | Flow: server.Flow, 106 | Transport: transport, 107 | } 108 | switch server.Tls { 109 | case 1: 110 | out.VLESSOptions.TLS = &option.OutboundTLSOptions{ 111 | Enabled: true, 112 | ServerName: server.ServerName, 113 | Insecure: server.TlsSettings.AllowInsecure != "0", 114 | } 115 | case 2: 116 | out.VLESSOptions.TLS = &option.OutboundTLSOptions{ 117 | Enabled: true, 118 | ServerName: server.TlsSettings.RealityDest, 119 | Insecure: true, 120 | UTLS: &option.OutboundUTLSOptions{ 121 | Enabled: true, 122 | Fingerprint: "chrome", 123 | }, 124 | Reality: &option.OutboundRealityOptions{ 125 | Enabled: true, 126 | ShortID: server.TlsSettings.ShortId, 127 | PublicKey: server.TlsSettings.PublicKey, 128 | }, 129 | } 130 | } 131 | } 132 | case "shadowsocks": 133 | var keyLength int 134 | switch server.Cipher { 135 | case "2022-blake3-aes-128-gcm": 136 | keyLength = 16 137 | case "2022-blake3-aes-256-gcm": 138 | keyLength = 32 139 | } 140 | var pw string 141 | if keyLength != 0 { 142 | pw = base64.StdEncoding.EncodeToString([]byte(uuid[:keyLength])) 143 | } else { 144 | pw = uuid 145 | } 146 | out = option.Outbound{ 147 | Type: "shadowsocks", 148 | Tag: "p", 149 | ShadowsocksOptions: option.ShadowsocksOutboundOptions{ 150 | ServerOptions: so, 151 | Password: pw, 152 | Method: server.Cipher, 153 | }, 154 | } 155 | case "trojan": 156 | out = option.Outbound{ 157 | Type: "trojan", 158 | Tag: "p", 159 | TrojanOptions: option.TrojanOutboundOptions{ 160 | ServerOptions: so, 161 | Password: uuid, 162 | }, 163 | } 164 | if server.Tls != 0 { 165 | out.TrojanOptions.TLS = &option.OutboundTLSOptions{ 166 | Enabled: true, 167 | ServerName: server.ServerName, 168 | Insecure: server.TlsSettings.AllowInsecure != "0", 169 | } 170 | } 171 | case "hysteria": 172 | out = option.Outbound{ 173 | Tag: "p", 174 | Type: "hysteria", 175 | HysteriaOptions: option.HysteriaOutboundOptions{ 176 | ServerOptions: option.ServerOptions{ 177 | Server: server.Host, 178 | ServerPort: uint16(server.Port), 179 | }, 180 | UpMbps: server.UpMbps, 181 | DownMbps: server.DownMbps, 182 | Obfs: server.ServerKey, 183 | AuthString: uuid, 184 | }, 185 | } 186 | out.HysteriaOptions.TLS = &option.OutboundTLSOptions{ 187 | Enabled: true, 188 | Insecure: server.AllowInsecure != 0, 189 | ServerName: server.ServerName, 190 | } 191 | default: 192 | return option.Options{}, errors.New("server type is unknown") 193 | } 194 | r, err := getRules(GlobalMode) 195 | if err != nil { 196 | return option.Options{}, fmt.Errorf("get rules error: %s", err) 197 | } 198 | return option.Options{ 199 | Log: &option.LogOptions{ 200 | //Output: path.Join(DataPath, "proxy.log"), 201 | }, 202 | Inbounds: []option.Inbound{ 203 | in, 204 | }, 205 | Outbounds: []option.Outbound{ 206 | out, 207 | { 208 | Tag: "d", 209 | Type: "direct", 210 | }, 211 | }, 212 | Route: r, 213 | }, nil 214 | } 215 | 216 | func getRules(global bool) (*option.RouteOptions, error) { 217 | var r option.RouteOptions 218 | if !global { 219 | err := checkRes(DataPath) 220 | if err != nil { 221 | return nil, fmt.Errorf("check res err: %s", err) 222 | } 223 | r = option.RouteOptions{ 224 | GeoIP: &option.GeoIPOptions{ 225 | DownloadURL: ResUrl + "/geoip.db", 226 | Path: path.Join(DataPath, "geoip.dat"), 227 | }, 228 | Geosite: &option.GeositeOptions{ 229 | DownloadURL: ResUrl + "/geosite.db", 230 | Path: path.Join(DataPath, "geosite.dat"), 231 | }, 232 | } 233 | r.Rules = []option.Rule{ 234 | { 235 | Type: C.RuleTypeDefault, 236 | DefaultOptions: option.DefaultRule{ 237 | GeoIP: option.Listable[string]{ 238 | "cn", "private", 239 | }, 240 | Geosite: option.Listable[string]{ 241 | "cn", 242 | }, 243 | Outbound: "d", 244 | }, 245 | }, 246 | } 247 | return &r, nil 248 | } else { 249 | r = option.RouteOptions{ 250 | AutoDetectInterface: true, 251 | } 252 | } 253 | return &r, nil 254 | } 255 | 256 | func checkRes(p string) error { 257 | if !file.IsExist(path.Join(p, "geoip.dat")) { 258 | f, err := os.OpenFile(path.Join(p, "geoip.dat"), os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0755) 259 | if err != nil { 260 | return err 261 | } 262 | defer f.Close() 263 | _, err = f.Write(geo.Ip) 264 | if err != nil { 265 | return err 266 | } 267 | } 268 | if !file.IsExist(path.Join(p, "geosite.dat")) { 269 | f, err := os.OpenFile(path.Join(p, "geosite.dat"), os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0755) 270 | if err != nil { 271 | return err 272 | } 273 | defer f.Close() 274 | _, err = f.Write(geo.Site) 275 | if err != nil { 276 | return err 277 | } 278 | } 279 | return nil 280 | } 281 | -------------------------------------------------------------------------------- /proxy/proxy.go: -------------------------------------------------------------------------------- 1 | package proxy 2 | 3 | import ( 4 | "UniProxy/common/sysproxy" 5 | "UniProxy/v2b" 6 | "context" 7 | box "github.com/sagernet/sing-box" 8 | ) 9 | 10 | var ( 11 | Running bool 12 | SystemProxy bool 13 | GlobalMode bool 14 | TunMode bool 15 | InPort int 16 | DataPath string 17 | ResUrl string 18 | ) 19 | 20 | var client *box.Box 21 | 22 | func StartProxy(tag string, uuid string, server *v2b.ServerInfo) error { 23 | if Running { 24 | StopProxy() 25 | } 26 | SystemProxy = true 27 | c, err := GetSingBoxConfig(uuid, server) 28 | if err != nil { 29 | return err 30 | } 31 | client, err = box.New(box.Options{ 32 | Context: context.Background(), 33 | Options: c, 34 | }) 35 | if err != nil { 36 | return err 37 | } 38 | err = client.Start() 39 | if err != nil { 40 | return err 41 | } 42 | Running = true 43 | return nil 44 | } 45 | 46 | func StopProxy() { 47 | if Running { 48 | client.Close() 49 | Running = false 50 | } 51 | } 52 | 53 | func ClearSystemProxy() error { 54 | if Running { 55 | client.Close() 56 | Running = false 57 | return nil 58 | } 59 | sysproxy.ClearSystemProxy() 60 | return nil 61 | } 62 | -------------------------------------------------------------------------------- /proxy/proxy_test.go: -------------------------------------------------------------------------------- 1 | package proxy 2 | 3 | import ( 4 | "UniProxy/v2b" 5 | "testing" 6 | ) 7 | 8 | func TestStartProxy(t *testing.T) { 9 | v2b.Init("http://127.0.0.1:1022", "xxxxxxxx") 10 | s, _ := v2b.GetServers() 11 | t.Log(s[0]) 12 | InPort = 1151 13 | GlobalMode = true 14 | t.Log(StartProxy("test", "xxxxxx", &s[0])) 15 | select {} 16 | } 17 | -------------------------------------------------------------------------------- /router/router.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "UniProxy/conf" 5 | "UniProxy/geo" 6 | "UniProxy/handle" 7 | "UniProxy/middleware" 8 | "fmt" 9 | "github.com/gin-gonic/gin" 10 | ) 11 | 12 | var engine *gin.Engine 13 | 14 | func Init() { 15 | gin.SetMode(gin.ReleaseMode) 16 | engine = gin.New() 17 | engine.Use(middleware.Logger, gin.Recovery()) 18 | } 19 | 20 | func loadRoute() { 21 | // status 22 | engine.POST("initParams", handle.InitParams) 23 | engine.GET("getStatus", handle.GetStatus) 24 | // servers 25 | engine.GET("getServers", handle.GetServers) 26 | // proxy 27 | engine.POST("startUniProxy", handle.StartUniProxy) 28 | engine.GET("stopUniProxy", handle.StopUniProxy) 29 | engine.GET("setSystemProxy", handle.SetSystemProxy) 30 | engine.GET("clearSystemProxy", handle.ClearSystemProxy) 31 | engine.GET("geosite.db", func(c *gin.Context) { 32 | c.Header("content-disposition", "attachment; filename=\"geosite.db\"") 33 | c.Data(200, "application/octet-stream", geo.Site) 34 | }) 35 | engine.GET("geoip.db", func(c *gin.Context) { 36 | c.Header("content-disposition", "attachment; filename=\"geoip.db\"") 37 | c.Data(200, "application/octet-stream", geo.Ip) 38 | }) 39 | if len(conf.C.Api.Baseurl) != 0 { 40 | engine.NoRoute(handle.ReverseProxy) 41 | } 42 | } 43 | 44 | func Start(host string, port int) error { 45 | loadRoute() 46 | err := engine.Run(fmt.Sprintf("%s:%d", host, port)) 47 | if err != nil { 48 | return err 49 | } 50 | return nil 51 | } 52 | -------------------------------------------------------------------------------- /v2b/v2b.go: -------------------------------------------------------------------------------- 1 | package v2b 2 | 3 | import ( 4 | "UniProxy/common/balance" 5 | "encoding/json" 6 | "errors" 7 | "github.com/avast/retry-go/v4" 8 | "github.com/go-resty/resty/v2" 9 | "time" 10 | ) 11 | 12 | var ( 13 | clients *balance.List[*resty.Client] 14 | etag string 15 | ) 16 | 17 | func Init(b string, url []string, auth string) { 18 | cs := make([]*resty.Client, len(url)) 19 | for i, u := range url { 20 | cs[i] = resty.New(). 21 | SetTimeout(time.Second*40). 22 | SetQueryParam("auth_data", auth). 23 | SetBaseURL(u). 24 | SetRetryCount(3). 25 | SetRetryWaitTime(3 * time.Second) 26 | } 27 | clients = balance.New[*resty.Client](b, cs) 28 | } 29 | 30 | type ServerFetchRsp struct { 31 | Data []ServerInfo `json:"data"` 32 | } 33 | 34 | type ServerInfo struct { 35 | Id int `json:"id"` 36 | Name string `json:"name"` 37 | Host string `json:"host"` 38 | Port int `json:"port"` 39 | Network string `json:"network"` 40 | Type string `json:"type"` 41 | Cipher string `json:"cipher"` 42 | Tls int `json:"tls"` 43 | Flow string `json:"flow"` 44 | TlsSettings struct { 45 | ServerName string `json:"serverName"` 46 | ServerPort string `json:"server_port"` 47 | AllowInsecure string `json:"allowInsecure"` 48 | RealityDest string `json:"server_name"` 49 | ShortId string `json:"short_id"` 50 | PublicKey string `json:"public_key"` 51 | } `json:"tls_settings"` 52 | NetworkSettings struct { 53 | Path string `json:"path"` 54 | Headers interface{} `json:"headers"` 55 | ServerName string `json:"serverName"` 56 | } `json:"networkSettings"` 57 | CreatedAt interface{} `json:"created_at"` 58 | AllowInsecure int `json:"insecure"` 59 | LastCheckAt interface{} `json:"last_check_at"` 60 | Tags interface{} `json:"tags"` 61 | UpMbps int `json:"up_mbps"` 62 | ServerName string `json:"server_name"` 63 | ServerKey string `json:"server_key"` 64 | DownMbps int `json:"down_mbps"` 65 | } 66 | 67 | func GetServers() ([]ServerInfo, error) { 68 | var r *resty.Response 69 | err := retry.Do(func() error { 70 | c := clients.Next() 71 | rsp, err := c.R(). 72 | SetHeader("If-None-Match", etag). 73 | Get("api/v1/user/server/fetch") 74 | if err != nil { 75 | return err 76 | } 77 | if rsp.StatusCode() == 304 { 78 | return nil 79 | } 80 | etag = rsp.Header().Get("ETag") 81 | if rsp.StatusCode() != 200 { 82 | return nil 83 | } 84 | r = rsp 85 | return nil 86 | }, retry.Attempts(3)) 87 | if err != nil { 88 | return nil, err 89 | } 90 | if r.StatusCode() == 304 { 91 | return nil, nil 92 | } 93 | rsp := &ServerFetchRsp{} 94 | err = json.Unmarshal(r.Body(), rsp) 95 | if err != nil { 96 | return nil, err 97 | } 98 | if len(rsp.Data) == 0 { 99 | return nil, errors.New("no servers") 100 | } 101 | return rsp.Data, nil 102 | } 103 | -------------------------------------------------------------------------------- /v2b/v2b_test.go: -------------------------------------------------------------------------------- 1 | package v2b 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestGetServers(t *testing.T) { 8 | Init("http://127.0.0.1", "xxxxxxxxx") 9 | t.Log(GetServers()) 10 | 11 | } 12 | --------------------------------------------------------------------------------