diff --git a/build-linux.bat b/build-linux.bat new file mode 100644 index 0000000..9d969cd --- /dev/null +++ b/build-linux.bat @@ -0,0 +1,4 @@ +SET CGO_ENABLED=0 +SET GOOS=linux +SET GOARCH=amd64 +go build -trimpath -ldflags "-w -s" \ No newline at end of file diff --git a/go.mod b/go.mod index b0b39df..46881db 100644 --- a/go.mod +++ b/go.mod @@ -4,13 +4,14 @@ go 1.20 require github.com/pion/stun v0.5.2 +require github.com/libp2p/go-reuseport v0.3.0 + require ( github.com/huin/goupnp v1.2.0 github.com/pion/dtls/v2 v2.2.6 // indirect github.com/pion/logging v0.2.2 // indirect github.com/pion/transport/v2 v2.2.0 // indirect github.com/pion/udp/v2 v2.0.1 // indirect - github.com/portmapping/go-reuse v0.0.3 golang.org/x/crypto v0.5.0 // indirect golang.org/x/sync v0.2.0 golang.org/x/sys v0.7.0 // indirect diff --git a/go.sum b/go.sum index 9f7f3e4..5a225c0 100644 --- a/go.sum +++ b/go.sum @@ -3,6 +3,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/huin/goupnp v1.2.0 h1:uOKW26NG1hsSSbXIZ1IR7XP9Gjd1U8pnLaCMgntmkmY= github.com/huin/goupnp v1.2.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= +github.com/libp2p/go-reuseport v0.3.0 h1:iiZslO5byUYZEg9iCwJGf5h+sf1Agmqx2V2FDjPyvUw= +github.com/libp2p/go-reuseport v0.3.0/go.mod h1:laea40AimhtfEqysZ71UpYj4S+R9VpH8PgqLo7L+SwI= github.com/pion/dtls/v2 v2.2.6 h1:yXMxKr0Skd+Ub6A8UqXTRLSywskx93ooMRHsQUtd+Z4= github.com/pion/dtls/v2 v2.2.6/go.mod h1:t8fWJCIquY5rlQZwA2yWxUS1+OCrAdXrhVKXB5oD/wY= github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY= @@ -16,8 +18,6 @@ github.com/pion/udp/v2 v2.0.1 h1:xP0z6WNux1zWEjhC7onRA3EwwSliXqu1ElUZAQhUP54= github.com/pion/udp/v2 v2.0.1/go.mod h1:B7uvTMP00lzWdyMr/1PVZXtV3wpPIxBRd4Wl6AksXn8= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/portmapping/go-reuse v0.0.3 h1:iY0JDxTTUaYopewHL0CLN5BqJ0BvDP48VzC2osPpkBQ= -github.com/portmapping/go-reuse v0.0.3/go.mod h1:xKeiOLrJpAUOineqiMEm1bpy6cq0vTdpoiebdRD45mo= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -48,7 +48,6 @@ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20200501145240-bc7a7d42d5c3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/main.go b/main.go index c8482d3..c873eb3 100644 --- a/main.go +++ b/main.go @@ -12,6 +12,7 @@ import ( "time" "github.com/xmdhs/natupnp/natmap" + "github.com/xmdhs/natupnp/reuse" ) var ( @@ -19,20 +20,20 @@ var ( localAddr string port string test bool + target string ) func init() { flag.StringVar(&stun, "s", "stun.sipnet.com:3478", "stun") flag.StringVar(&localAddr, "l", "", "local addr") flag.StringVar(&port, "p", "8086", "port") + flag.StringVar(&target, "d", "", "forward to target host") flag.BoolVar(&test, "t", false, "test server") flag.Parse() } func main() { - if test { - go testServer(port) - } + ctx := context.Background() if localAddr == "" { s, err := natmap.GetLocalAddr() if err != nil { @@ -50,12 +51,21 @@ func main() { panic(err) } - m, s, err := natmap.NatMap(context.Background(), "stun.sipnet.com:3478", localAddr, uint16(portu), func(s string) { + m, s, err := natmap.NatMap(ctx, "stun.sipnet.com:3478", localAddr, uint16(portu), func(s string) { log.Println(s) }) if err != nil { panic(err) } + if test { + go testServer(port) + } + if target != "" { + natmap.Forward(ctx, uint16(portu), target, func(s string) { + log.Println(s) + }) + } + defer m.Close() fmt.Println(s) os.Stdin.Read(make([]byte, 1)) @@ -70,5 +80,12 @@ func testServer(port string) { w.Write([]byte("ok")) }), } - s.ListenAndServe() + l, err := reuse.Listen(context.Background(), "tcp", "0.0.0.0:"+port) + if err != nil { + panic(err) + } + err = s.Serve(l) + if err != nil { + panic(err) + } } diff --git a/natmap/natmap.go b/natmap/natmap.go index e2b0146..16f2b93 100644 --- a/natmap/natmap.go +++ b/natmap/natmap.go @@ -3,6 +3,7 @@ package natmap import ( "context" "fmt" + "io" "net" "net/http" "strconv" @@ -25,8 +26,6 @@ func NatMap(ctx context.Context, stunAddr string, host string, port uint16, log if err != nil { return nil, "", fmt.Errorf("NatMap: %w", err) } - go keepalive(ctx, port, log) - stunConn, err := reuse.DialContext(ctx, "tcp", "0.0.0.0:"+strconv.Itoa(int(port)), stunAddr) if err != nil { return nil, "", fmt.Errorf("NatMap: %w", err) @@ -35,7 +34,7 @@ func NatMap(ctx context.Context, stunAddr string, host string, port uint16, log if err != nil { return nil, "", fmt.Errorf("NatMap: %w", err) } - + go keepalive(ctx, port, log) return &m, fmt.Sprintf("%v:%v", mapAddr.IP.String(), mapAddr.Port), nil } @@ -59,6 +58,7 @@ func keepalive(ctx context.Context, port uint16, log func(string)) { if err != nil { c.CloseIdleConnections() log(err.Error()) + time.Sleep(10 * time.Second) continue } rep.Body.Close() @@ -74,3 +74,30 @@ func GetLocalAddr() (string, error) { defer l.Close() return l.LocalAddr().String(), nil } + +func Forward(ctx context.Context, port uint16, target string, log func(string)) error { + l, err := reuse.Listen(ctx, "tcp", "0.0.0.0:"+strconv.FormatUint(uint64(port), 10)) + if err != nil { + return fmt.Errorf("Forward: %w", err) + } + f := func() { + c, err := l.Accept() + if err != nil { + log(err.Error()) + return + } + defer c.Close() + var d net.Dialer + tc, err := d.DialContext(ctx, "tcp", target) + if err != nil { + log(err.Error()) + return + } + defer tc.Close() + go io.Copy(c, tc) + go io.Copy(tc, c) + } + for { + f() + } +} diff --git a/reuse/reuse.go b/reuse/reuse.go index 6ca70cc..a7bd31c 100644 --- a/reuse/reuse.go +++ b/reuse/reuse.go @@ -5,23 +5,23 @@ import ( "fmt" "net" - "github.com/portmapping/go-reuse" + "github.com/libp2p/go-reuseport" ) func DialContext(ctx context.Context, network, laddr, raddr string) (net.Conn, error) { - nla, err := reuse.ResolveAddr(network, laddr) + nla, err := reuseport.ResolveAddr(network, laddr) if err != nil { return nil, fmt.Errorf("resolving local addr: %w", err) } d := net.Dialer{ - Control: reuse.Control, + Control: reuseport.Control, LocalAddr: nla, } return d.DialContext(ctx, network, raddr) } var listenConfig = net.ListenConfig{ - Control: reuse.Control, + Control: reuseport.Control, } func Listen(ctx context.Context, network, address string) (net.Listener, error) {