diff --git a/internal/server/transport/kcp.go b/internal/server/transport/kcp.go index e8ba7dd..f297c1c 100644 --- a/internal/server/transport/kcp.go +++ b/internal/server/transport/kcp.go @@ -572,12 +572,7 @@ func (s *KcpTransport) parsePortMappings(g *kcpGen) { continue } - port, err := strconv.Atoi(localPortOrRange) - if err == nil && port > 1 && port < 65535 { // format port=remoteAddress - localAddr = fmt.Sprintf(":%d", port) - } else { - localAddr = localPortOrRange // format ip:port=remoteAddress - } + localAddr = mappingListenAddress(localPortOrRange) } else { s.logger.Fatalf("invalid port mapping format: %s", portMapping) } diff --git a/internal/server/transport/portmapping.go b/internal/server/transport/portmapping.go new file mode 100644 index 0000000..e6a9c36 --- /dev/null +++ b/internal/server/transport/portmapping.go @@ -0,0 +1,19 @@ +package transport + +import ( + "fmt" + "strconv" + "strings" +) + +// mappingListenAddress converts the numeric shorthand used by forwarded-port +// mappings into a wildcard listen address. Non-numeric values are already full +// listen addresses and are returned unchanged. +func mappingListenAddress(value string) string { + value = strings.TrimSpace(value) + port, err := strconv.Atoi(value) + if err == nil && port >= 1 && port <= 65535 { + return fmt.Sprintf(":%d", port) + } + return value +} diff --git a/internal/server/transport/portmapping_test.go b/internal/server/transport/portmapping_test.go new file mode 100644 index 0000000..579ec42 --- /dev/null +++ b/internal/server/transport/portmapping_test.go @@ -0,0 +1,28 @@ +package transport + +import "testing" + +func TestMappingListenAddress(t *testing.T) { + tests := []struct { + name string + value string + want string + }{ + {"lowest port", "1", ":1"}, + {"highest port", "65535", ":65535"}, + {"typical port", "443", ":443"}, + {"whitespace", " 443 ", ":443"}, + {"IPv4 address", "127.0.0.1:443", "127.0.0.1:443"}, + {"IPv6 address", "[::1]:443", "[::1]:443"}, + {"below range", "0", "0"}, + {"above range", "65536", "65536"}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := mappingListenAddress(tt.value); got != tt.want { + t.Fatalf("mappingListenAddress(%q) = %q, want %q", tt.value, got, tt.want) + } + }) + } +} diff --git a/internal/server/transport/quic.go b/internal/server/transport/quic.go index 47b3643..46cd16f 100644 --- a/internal/server/transport/quic.go +++ b/internal/server/transport/quic.go @@ -459,12 +459,7 @@ func (s *QuicTransport) parsePortMappings(g *quicGen) { continue } - port, err := strconv.Atoi(localPortOrRange) - if err == nil && port > 1 && port < 65535 { // format port=remoteAddress - localAddr = fmt.Sprintf(":%d", port) - } else { - localAddr = localPortOrRange // format ip:port=remoteAddress - } + localAddr = mappingListenAddress(localPortOrRange) } else { s.logger.Fatalf("invalid port mapping format: %s", portMapping) } diff --git a/internal/server/transport/tcp.go b/internal/server/transport/tcp.go index add3d34..acc9ba4 100644 --- a/internal/server/transport/tcp.go +++ b/internal/server/transport/tcp.go @@ -577,12 +577,7 @@ func (s *TcpTransport) parsePortMappings(g *tcpGen) { continue } else { // Handle single local port case - port, err := strconv.Atoi(localPortOrRange) - if err == nil && port > 1 && port < 65535 { // format port=remoteAddress - localAddr = fmt.Sprintf(":%d", port) - } else { - localAddr = localPortOrRange // format ip:port=remoteAddress - } + localAddr = mappingListenAddress(localPortOrRange) } } else { s.logger.Fatalf("invalid port mapping format: %s", portMapping) diff --git a/internal/server/transport/tcpmux.go b/internal/server/transport/tcpmux.go index 1c025e1..34de84c 100644 --- a/internal/server/transport/tcpmux.go +++ b/internal/server/transport/tcpmux.go @@ -601,12 +601,7 @@ func (s *TcpMuxTransport) parsePortMappings(g *tcpMuxGen) { continue } else { // Handle single local port case - port, err := strconv.Atoi(localPortOrRange) - if err == nil && port > 1 && port < 65535 { // format port=remoteAddress - localAddr = fmt.Sprintf(":%d", port) - } else { - localAddr = localPortOrRange // format ip:port=remoteAddress - } + localAddr = mappingListenAddress(localPortOrRange) } } else { s.logger.Fatalf("invalid port mapping format: %s", portMapping) diff --git a/internal/server/transport/udp.go b/internal/server/transport/udp.go index 56b6d6c..3f791e8 100644 --- a/internal/server/transport/udp.go +++ b/internal/server/transport/udp.go @@ -536,12 +536,7 @@ func (s *UdpTransport) parsePortMappings(g *udpGen) { continue } else { // Handle single local port case - port, err := strconv.Atoi(localPortOrRange) - if err == nil && port > 1 && port < 65535 { // format port=remoteAddress - localAddr = fmt.Sprintf(":%d", port) - } else { - localAddr = localPortOrRange // format ip:port=remoteAddress - } + localAddr = mappingListenAddress(localPortOrRange) } } else { s.logger.Fatalf("invalid port mapping format: %s", portMapping) diff --git a/internal/server/transport/ws.go b/internal/server/transport/ws.go index e70908e..ec403e4 100644 --- a/internal/server/transport/ws.go +++ b/internal/server/transport/ws.go @@ -451,12 +451,7 @@ func (s *WsTransport) parsePortMappings(g *wsGen) { continue } else { // Handle single local port case - port, err := strconv.Atoi(localPortOrRange) - if err == nil && port > 1 && port < 65535 { // format port=remoteAddress - localAddr = fmt.Sprintf(":%d", port) - } else { - localAddr = localPortOrRange // format ip:port=remoteAddress - } + localAddr = mappingListenAddress(localPortOrRange) } } else { s.logger.Fatalf("invalid port mapping format: %s", portMapping) diff --git a/internal/server/transport/wsmux.go b/internal/server/transport/wsmux.go index 876ea93..df96226 100644 --- a/internal/server/transport/wsmux.go +++ b/internal/server/transport/wsmux.go @@ -476,12 +476,7 @@ func (s *WsMuxTransport) parsePortMappings(g *wsMuxGen) { continue } else { // Handle single local port case - port, err := strconv.Atoi(localPortOrRange) - if err == nil && port > 1 && port < 65535 { // format port=remoteAddress - localAddr = fmt.Sprintf(":%d", port) - } else { - localAddr = localPortOrRange // format ip:port=remoteAddress - } + localAddr = mappingListenAddress(localPortOrRange) } } else { s.logger.Fatalf("invalid port mapping format: %s", portMapping)