Proxy: Add Hysteria outbound & transport (version 2, udphop) and Salamander udpmask (#5508)

https://github.com/XTLS/Xray-core/issues/3547#issuecomment-3549896520
https://github.com/XTLS/Xray-core/issues/2635#issuecomment-3570871754
This commit is contained in:
LjhAUMEM
2026-01-13 21:31:51 +08:00
committed by GitHub
parent 8a9dbd407f
commit 92ada2dd1d
47 changed files with 5818 additions and 243 deletions
@@ -0,0 +1,107 @@
package utils
import (
"sort"
"strconv"
"strings"
)
// PortUnion is a collection of multiple port ranges.
type PortUnion []PortRange
// PortRange represents a range of ports.
// Start and End are inclusive. [Start, End]
type PortRange struct {
Start, End uint16
}
// ParsePortUnion parses a string of comma-separated port ranges (or single ports) into a PortUnion.
// Returns nil if the input is invalid.
// The returned PortUnion is guaranteed to be normalized.
func ParsePortUnion(s string) PortUnion {
if s == "all" || s == "*" {
// Wildcard special case
return PortUnion{PortRange{0, 65535}}
}
var result PortUnion
portStrs := strings.Split(s, ",")
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
}
result = append(result, PortRange{uint16(start), uint16(end)})
} else {
// Single port
port, err := strconv.ParseUint(portStr, 10, 16)
if err != nil {
return nil
}
result = append(result, PortRange{uint16(port), uint16(port)})
}
}
if result == nil {
return nil
}
return result.Normalize()
}
// Normalize normalizes a PortUnion.
// No overlapping ranges, ranges are sorted from low to high.
func (u PortUnion) Normalize() PortUnion {
if len(u) == 0 {
return u
}
sort.Slice(u, func(i, j int) bool {
if u[i].Start == u[j].Start {
return u[i].End < u[j].End
}
return u[i].Start < u[j].Start
})
normalized := PortUnion{u[0]}
for _, current := range u[1:] {
last := &normalized[len(normalized)-1]
if uint32(current.Start) <= uint32(last.End)+1 {
if current.End > last.End {
last.End = current.End
}
} else {
normalized = append(normalized, current)
}
}
return normalized
}
// Ports returns all ports in the PortUnion as a slice.
func (u PortUnion) Ports() []uint16 {
var ports []uint16
for _, r := range u {
for i := uint32(r.Start); i <= uint32(r.End); i++ {
ports = append(ports, uint16(i))
}
}
return ports
}
// Contains returns true if the PortUnion contains the given port.
func (u PortUnion) Contains(port uint16) bool {
for _, r := range u {
if port >= r.Start && port <= r.End {
return true
}
}
return false
}
@@ -0,0 +1,150 @@
package utils
import (
"reflect"
"slices"
"testing"
)
func TestParsePortUnion(t *testing.T) {
tests := []struct {
name string
s string
want PortUnion
}{
{
name: "empty",
s: "",
want: nil,
},
{
name: "all 1",
s: "all",
want: PortUnion{{0, 65535}},
},
{
name: "all 2",
s: "*",
want: PortUnion{{0, 65535}},
},
{
name: "single port",
s: "1234",
want: PortUnion{{1234, 1234}},
},
{
name: "multiple ports (unsorted)",
s: "5678,1234,9012",
want: PortUnion{{1234, 1234}, {5678, 5678}, {9012, 9012}},
},
{
name: "one range",
s: "1234-1240",
want: PortUnion{{1234, 1240}},
},
{
name: "one range (reversed)",
s: "1240-1234",
want: PortUnion{{1234, 1240}},
},
{
name: "multiple ports and ranges (reversed, unsorted, overlapping)",
s: "5678,1200-1236,9100-9012,1234-1240",
want: PortUnion{{1200, 1240}, {5678, 5678}, {9012, 9100}},
},
{
name: "multiple ports and ranges with 65535 (reversed, unsorted, overlapping)",
s: "5678,1200-1236,65531-65535,65532-65534,9100-9012,1234-1240",
want: PortUnion{{1200, 1240}, {5678, 5678}, {9012, 9100}, {65531, 65535}},
},
{
name: "multiple ports and ranges with 65535 (reversed, unsorted, overlapping) 2",
s: "5678,1200-1236,65532-65535,65531-65534,9100-9012,1234-1240",
want: PortUnion{{1200, 1240}, {5678, 5678}, {9012, 9100}, {65531, 65535}},
},
{
name: "invalid 1",
s: "1234-",
want: nil,
},
{
name: "invalid 2",
s: "1234-ggez",
want: nil,
},
{
name: "invalid 3",
s: "233,",
want: nil,
},
{
name: "invalid 4",
s: "1234-1240-1250",
want: nil,
},
{
name: "invalid 5",
s: "-,,",
want: nil,
},
{
name: "invalid 6",
s: "http",
want: nil,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := ParsePortUnion(tt.s); !reflect.DeepEqual(got, tt.want) {
t.Errorf("ParsePortUnion() = %v, want %v", got, tt.want)
}
})
}
}
func TestPortUnion_Ports(t *testing.T) {
tests := []struct {
name string
pu PortUnion
want []uint16
}{
{
name: "single port",
pu: PortUnion{{1234, 1234}},
want: []uint16{1234},
},
{
name: "multiple ports",
pu: PortUnion{{1234, 1236}},
want: []uint16{1234, 1235, 1236},
},
{
name: "multiple ports and ranges",
pu: PortUnion{{1234, 1236}, {5678, 5680}, {9000, 9002}},
want: []uint16{1234, 1235, 1236, 5678, 5679, 5680, 9000, 9001, 9002},
},
{
name: "single port 65535",
pu: PortUnion{{65535, 65535}},
want: []uint16{65535},
},
{
name: "port range with 65535",
pu: PortUnion{{65530, 65535}},
want: []uint16{65530, 65531, 65532, 65533, 65534, 65535},
},
{
name: "multiple ports and ranges with 65535",
pu: PortUnion{{65530, 65535}, {1234, 1236}},
want: []uint16{65530, 65531, 65532, 65533, 65534, 65535, 1234, 1235, 1236},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := tt.pu.Ports(); !slices.Equal(got, tt.want) {
t.Errorf("PortUnion.Ports() = %v, want %v", got, tt.want)
}
})
}
}