Add UDP port exposure

Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
This commit is contained in:
Vasyl Gello 2024-07-15 13:30:25 +03:00
parent 582fe511fa
commit b160b3f66d
No known key found for this signature in database
GPG key ID: 8A52BC6C291FB280
4 changed files with 181 additions and 12 deletions

View file

@ -66,3 +66,63 @@ func (m *TCPMappings) Set(value string) error {
*m = append(*m, mapping)
return nil
}
type UDPMapping struct {
Listen *net.UDPAddr
Mapped *net.UDPAddr
}
type UDPMappings []UDPMapping
func (m *UDPMappings) String() string {
return ""
}
func (m *UDPMappings) Set(value string) error {
tokens := strings.Split(value, ":")
if len(tokens) > 2 {
tokens = strings.SplitN(value, ":", 2)
host, port, err := net.SplitHostPort(tokens[1])
if err != nil {
return fmt.Errorf("failed to split host and port: %w", err)
}
tokens = append(tokens[:1], host, port)
}
listenport, err := strconv.Atoi(tokens[0])
if err != nil {
return fmt.Errorf("listen port is invalid: %w", err)
}
if listenport == 0 {
return fmt.Errorf("listen port must not be zero")
}
mapping := UDPMapping{
Listen: &net.UDPAddr{
Port: listenport,
},
Mapped: &net.UDPAddr{
IP: net.IPv6loopback,
Port: listenport,
},
}
tokens = tokens[1:]
if len(tokens) > 0 {
mappedaddr := net.ParseIP(tokens[0])
if mappedaddr == nil {
return fmt.Errorf("invalid mapped address %q", tokens[0])
}
mapping.Mapped.IP = mappedaddr
tokens = tokens[1:]
}
if len(tokens) > 0 {
mappedport, err := strconv.Atoi(tokens[0])
if err != nil {
return fmt.Errorf("mapped port is invalid: %w", err)
}
if mappedport == 0 {
return fmt.Errorf("mapped port must not be zero")
}
mapping.Mapped.Port = mappedport
}
*m = append(*m, mapping)
return nil
}

View file

@ -3,26 +3,48 @@ package types
import "testing"
func TestEndpointMappings(t *testing.T) {
var mappings TCPMappings
if err := mappings.Set("1234"); err != nil {
var tcpMappings TCPMappings
if err := tcpMappings.Set("1234"); err != nil {
t.Fatal(err)
}
if err := mappings.Set("1234:192.168.1.1"); err != nil {
if err := tcpMappings.Set("1234:192.168.1.1"); err != nil {
t.Fatal(err)
}
if err := mappings.Set("1234:192.168.1.1:4321"); err != nil {
if err := tcpMappings.Set("1234:192.168.1.1:4321"); err != nil {
t.Fatal(err)
}
if err := mappings.Set("1234:[2000::1]:4321"); err != nil {
if err := tcpMappings.Set("1234:[2000::1]:4321"); err != nil {
t.Fatal(err)
}
if err := mappings.Set("a"); err == nil {
if err := tcpMappings.Set("a"); err == nil {
t.Fatal("'a' should be an invalid exposed port")
}
if err := mappings.Set("1234:localhost"); err == nil {
if err := tcpMappings.Set("1234:localhost"); err == nil {
t.Fatal("mapped address must be an IP literal")
}
if err := mappings.Set("1234:localhost:a"); err == nil {
if err := tcpMappings.Set("1234:localhost:a"); err == nil {
t.Fatal("'a' should be an invalid mapped port")
}
var udpMappings UDPMappings
if err := udpMappings.Set("1234"); err != nil {
t.Fatal(err)
}
if err := udpMappings.Set("1234:192.168.1.1"); err != nil {
t.Fatal(err)
}
if err := udpMappings.Set("1234:192.168.1.1:4321"); err != nil {
t.Fatal(err)
}
if err := udpMappings.Set("1234:[2000::1]:4321"); err != nil {
t.Fatal(err)
}
if err := udpMappings.Set("a"); err == nil {
t.Fatal("'a' should be an invalid exposed port")
}
if err := udpMappings.Set("1234:localhost"); err == nil {
t.Fatal("mapped address must be an IP literal")
}
if err := udpMappings.Set("1234:localhost:a"); err == nil {
t.Fatal("'a' should be an invalid mapped port")
}
}

22
src/types/udpproxy.go Normal file
View file

@ -0,0 +1,22 @@
package types
import (
"net"
)
func ReverseProxyUDP(mtu uint64, dst net.PacketConn, dstAddr net.Addr, src net.UDPConn) error {
buf := make([]byte, mtu)
for {
n, err := src.Read(buf[:])
if err != nil {
return err
}
if n > 0 {
n, err = dst.WriteTo(buf[:n], dstAddr)
if err != nil {
return err
}
}
}
return nil
}