hysteria/extras/utils/portunion.go
Haruue d65997c02b
fix: inf loop in PortUnion.Ports() when end=65535
fix: #1240

Any uint16 value is less than or equal to 65535.
2024-11-05 00:47:02 +09:00

111 lines
2.5 KiB
Go

package utils
import (
"math"
"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 current.Start <= 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 := r.Start; i <= r.End; i++ {
ports = append(ports, i)
if i == math.MaxUint16 {
break
}
}
}
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
}