mirror of
https://github.com/apernet/hysteria.git
synced 2025-04-03 04:27:39 +03:00
107 lines
2.4 KiB
Go
107 lines
2.4 KiB
Go
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 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)
|
|
}
|
|
}
|
|
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
|
|
}
|