From 012e4485621939af60bbd8708c4d15c8a04039e9 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 12 Mar 2024 15:06:41 +0800 Subject: [PATCH] fix: when hysteria2 set `ports`, `port` can be empty --- adapter/outbound/hysteria2.go | 59 ++++++++++------------------------- common/utils/ranges.go | 16 ++++++++-- 2 files changed, 30 insertions(+), 45 deletions(-) diff --git a/adapter/outbound/hysteria2.go b/adapter/outbound/hysteria2.go index 5c817373..0ad7c214 100644 --- a/adapter/outbound/hysteria2.go +++ b/adapter/outbound/hysteria2.go @@ -8,10 +8,10 @@ import ( "net" "runtime" "strconv" - "strings" "time" CN "github.com/metacubex/mihomo/common/net" + "github.com/metacubex/mihomo/common/utils" "github.com/metacubex/mihomo/component/ca" "github.com/metacubex/mihomo/component/dialer" "github.com/metacubex/mihomo/component/proxydialer" @@ -44,7 +44,7 @@ type Hysteria2Option struct { BasicOption Name string `proxy:"name"` Server string `proxy:"server"` - Port int `proxy:"port"` + Port int `proxy:"port,omitempty"` Ports string `proxy:"ports,omitempty"` HopInterval int `proxy:"hop-interval,omitempty"` Up string `proxy:"up,omitempty"` @@ -91,41 +91,6 @@ func closeHysteria2(h *Hysteria2) { } } -func parsePorts(portStr string) (ports []uint16) { - portStrs := strings.Split(portStr, ",") - for _, portStr := range portStrs { - if strings.Contains(portStr, "-") { - // Port range - portRange := strings.Split(portStr, "-") - if len(portRange) != 2 { - return nil - } - start, err := strconv.ParseUint(portRange[0], 10, 16) - if err != nil { - return nil - } - end, err := strconv.ParseUint(portRange[1], 10, 16) - if err != nil { - return nil - } - if start > end { - start, end = end, start - } - for i := start; i <= end; i++ { - ports = append(ports, uint16(i)) - } - } else { - // Single port - port, err := strconv.ParseUint(portStr, 10, 16) - if err != nil { - return nil - } - ports = append(ports, uint16(port)) - } - } - return ports -} - func NewHysteria2(option Hysteria2Option) (*Hysteria2, error) { addr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port)) var salamanderPassword string @@ -187,13 +152,18 @@ func NewHysteria2(option Hysteria2Option) (*Hysteria2, error) { }, } + var ranges utils.IntRanges[uint16] + var serverAddress []string if option.Ports != "" { - ports := parsePorts(option.Ports) - if len(ports) > 0 { - serverAddress := make([]string, len(ports)) - for i, port := range ports { - serverAddress[i] = net.JoinHostPort(option.Server, strconv.Itoa(int(port))) - } + ranges, err = utils.NewUnsignedRanges[uint16](option.Ports) + if err != nil { + return nil, err + } + ranges.Range(func(port uint16) bool { + serverAddress = append(serverAddress, net.JoinHostPort(option.Server, strconv.Itoa(int(port)))) + return true + }) + if len(serverAddress) > 0 { clientOptions.ServerAddress = func(ctx context.Context) (*net.UDPAddr, error) { return resolveUDPAddrWithPrefer(ctx, "udp", serverAddress[fastrand.Intn(len(serverAddress))], C.NewDNSPrefer(option.IPVersion)) } @@ -206,6 +176,9 @@ func NewHysteria2(option Hysteria2Option) (*Hysteria2, error) { clientOptions.HopInterval = time.Duration(option.HopInterval) * time.Second } } + if option.Port == 0 && len(serverAddress) == 0 { + return nil, errors.New("invalid port") + } client, err := hysteria2.NewClient(clientOptions) if err != nil { diff --git a/common/utils/ranges.go b/common/utils/ranges.go index 810105ff..e656e34b 100644 --- a/common/utils/ranges.go +++ b/common/utils/ranges.go @@ -20,6 +20,8 @@ func newIntRanges[T constraints.Integer](expected string, parseFn func(string) ( return nil, nil } + // support: 200,302 or 200,204,401-429,501-503 + expected = strings.ReplaceAll(expected, ",", "/") list := strings.Split(expected, "/") if len(list) > 28 { return nil, fmt.Errorf("%w, too many ranges to use, maximum support 28 ranges", errIntRanges) @@ -47,9 +49,9 @@ func newIntRangesFromList[T constraints.Integer](list []string, parseFn func(str } switch statusLen { - case 1: + case 1: // Port range ranges = append(ranges, NewRange(T(start), T(start))) - case 2: + case 2: // Single port end, err := parseFn(strings.Trim(status[1], "[ ]")) if err != nil { return nil, errIntRanges @@ -130,3 +132,13 @@ func (ranges IntRanges[T]) ToString() string { return strings.Join(terms, "/") } + +func (ranges IntRanges[T]) Range(f func(t T) bool) { + for _, r := range ranges { + for i := r.Start(); i <= r.End(); i++ { + if !f(i) { + return + } + } + } +}