diff --git a/main.go b/main.go index 2726079..e8aa486 100644 --- a/main.go +++ b/main.go @@ -57,8 +57,10 @@ func main() { if err != nil { panic(err) } + laddrPort := netip.AddrPortFrom(netip.MustParseAddr(localAddr), uint16(portu)) + for { - err := openPort(ctx, target, localAddr, uint16(portu), stun, func(s netip.AddrPort) { + err := openPort(ctx, target, laddrPort, stun, func(s netip.AddrPort) { fmt.Println(s) if comm != "" { c := exec.CommandContext(ctx, comm, localAddr, port, s.Addr().String(), strconv.Itoa(int(s.Port()))) @@ -78,19 +80,19 @@ func main() { } } -func openPort(ctx context.Context, target, localAddr string, portu uint16, +func openPort(ctx context.Context, target string, laddr netip.AddrPort, stun string, finish func(netip.AddrPort), udp bool, testserver bool) error { ctx, cancel := context.WithCancel(ctx) defer cancel() if target != "" { - var forward func(ctx context.Context, port uint16, target string, log func(string)) (io.Closer, error) + var forward func(ctx context.Context, laddr netip.AddrPort, target string, log func(string)) (io.Closer, error) if udp { forward = natmap.ForwardUdp } else { forward = natmap.Forward } - l, err := forward(ctx, portu, target, func(s string) { + l, err := forward(ctx, laddr, target, func(s string) { log.Println(s) }) if err != nil { @@ -99,21 +101,21 @@ func openPort(ctx context.Context, target, localAddr string, portu uint16, defer l.Close() } if testserver { - l, err := testServer(ctx, portu) + l, err := testServer(ctx, laddr) if err != nil { return fmt.Errorf("openPort: %w", err) } defer l.Close() } errCh := make(chan error, 1) - var nmap func(ctx context.Context, stunAddr string, host string, port uint16, log func(error)) (*natmap.Map, netip.AddrPort, error) + var nmap func(ctx context.Context, stunAddr string, laddr netip.AddrPort, log func(error)) (*natmap.Map, netip.AddrPort, error) if udp { nmap = natmap.NatMapUdp } else { nmap = natmap.NatMap } - m, s, err := nmap(ctx, stun, localAddr, uint16(portu), func(s error) { + m, s, err := nmap(ctx, stun, laddr, func(s error) { cancel() select { case errCh <- s: @@ -134,16 +136,16 @@ func openPort(ctx context.Context, target, localAddr string, portu uint16, return nil } -func testServer(ctx context.Context, port uint16) (net.Listener, error) { +func testServer(ctx context.Context, laddr netip.AddrPort) (net.Listener, error) { s := http.Server{ ReadTimeout: 5 * time.Second, WriteTimeout: 10 * time.Second, - Addr: "0.0.0.0:" + strconv.FormatUint(uint64(port), 10), + Addr: laddr.String(), Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("ok")) }), } - l, err := reuse.Listen(ctx, "tcp", "0.0.0.0:"+strconv.FormatUint(uint64(port), 10)) + l, err := reuse.Listen(ctx, "tcp", laddr.String()) if err != nil { return nil, fmt.Errorf("testServer: %w", err) } diff --git a/natmap/natmap.go b/natmap/natmap.go index 7727ec1..5146574 100644 --- a/natmap/natmap.go +++ b/natmap/natmap.go @@ -8,7 +8,6 @@ import ( "net" "net/http" "net/netip" - "strconv" "time" "github.com/xmdhs/natupnp/reuse" @@ -20,21 +19,20 @@ type Map struct { cancel func() } -func getPubulicPort(ctx context.Context, stunAddr string, host string, port uint16, isTcp bool) (netip.AddrPort, error) { +func getPubulicPort(ctx context.Context, stunAddr string, laddr netip.AddrPort, isTcp bool) (netip.AddrPort, error) { var ( upnpP = "TCP" - dialP = "tcp4" + dialP = "tcp" ) if !isTcp { upnpP = "UDP" - dialP = "udp4" + dialP = "udp" } - - err := upnp.AddPortMapping(ctx, "", port, upnpP, port, host, true, "github.com/xmdhs/natupnp", 0) + err := upnp.AddPortMapping(ctx, "", laddr.Port(), upnpP, laddr.Port(), laddr.Addr().String(), true, "github.com/xmdhs/natupnp", 0) if err != nil { return netip.AddrPort{}, fmt.Errorf("getPubulicPort: %w", err) } - stunConn, err := reuse.DialContext(ctx, dialP, "0.0.0.0:"+strconv.Itoa(int(port)), stunAddr) + stunConn, err := reuse.DialContext(ctx, dialP, laddr.String(), stunAddr) if err != nil { return netip.AddrPort{}, fmt.Errorf("getPubulicPort: %w", err) } @@ -47,16 +45,16 @@ func getPubulicPort(ctx context.Context, stunAddr string, host string, port uint return netip.AddrPortFrom(addr, uint16(mapAddr.Port)), nil } -func NatMap(ctx context.Context, stunAddr string, host string, port uint16, log func(error)) (*Map, netip.AddrPort, error) { +func NatMap(ctx context.Context, stunAddr string, laddr netip.AddrPort, log func(error)) (*Map, netip.AddrPort, error) { m := Map{} ctx, cancel := context.WithCancel(ctx) m.cancel = cancel - mapAddr, err := getPubulicPort(ctx, stunAddr, host, port, true) + mapAddr, err := getPubulicPort(ctx, stunAddr, laddr, true) if err != nil { return nil, netip.AddrPort{}, fmt.Errorf("NatMap: %w", err) } - go keepalive(ctx, port, log) + go keepalive(ctx, laddr, log) return &m, mapAddr, nil } @@ -65,17 +63,19 @@ func (m Map) Close() error { return nil } -func keepalive(ctx context.Context, port uint16, log func(error)) { +func keepalive(ctx context.Context, laddr netip.AddrPort, log func(error)) { tr := http.DefaultTransport.(*http.Transport).Clone() tr.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) { - return reuse.DialContext(ctx, "tcp", "0.0.0.0:"+strconv.Itoa(int(port)), addr) + return reuse.DialContext(ctx, "tcp", laddr.String(), addr) } + tr.Proxy = nil c := http.Client{Transport: tr, Timeout: 5 * time.Second} + for { func() { ctx, cancel := context.WithTimeout(ctx, 10*time.Second) defer cancel() - reqs, err := http.NewRequestWithContext(ctx, "GET", "http://connect.rom.miui.com/generate_204", nil) + reqs, err := http.NewRequestWithContext(ctx, "HEAD", "http://www.gstatic.com/generate_204", nil) if err != nil { panic(err) } @@ -100,14 +100,14 @@ func keepalive(ctx context.Context, port uint16, log func(error)) { func GetLocalAddr() (net.Addr, error) { l, err := net.Dial("udp4", "223.5.5.5:53") if err != nil { - return nil, fmt.Errorf("getLocal: %w", err) + return nil, fmt.Errorf("GetLocalAddr: %w", err) } defer l.Close() return l.LocalAddr(), nil } -func Forward(ctx context.Context, port uint16, target string, log func(string)) (io.Closer, error) { - l, err := reuse.Listen(ctx, "tcp", "0.0.0.0:"+strconv.FormatUint(uint64(port), 10)) +func Forward(ctx context.Context, laddr netip.AddrPort, target string, log func(string)) (io.Closer, error) { + l, err := reuse.Listen(ctx, "tcp", laddr.String()) if err != nil { return nil, fmt.Errorf("Forward: %w", err) } diff --git a/natmap/udp.go b/natmap/udp.go index c46bdc0..ce729fe 100644 --- a/natmap/udp.go +++ b/natmap/udp.go @@ -6,32 +6,35 @@ import ( "io" "net" "net/netip" - "strconv" "strings" "time" "github.com/xmdhs/natupnp/reuse" ) -func NatMapUdp(ctx context.Context, stunAddr string, host string, port uint16, log func(error)) (*Map, netip.AddrPort, error) { +func NatMapUdp(ctx context.Context, stunAddr string, laddr netip.AddrPort, log func(error)) (*Map, netip.AddrPort, error) { m := Map{} ctx, cancel := context.WithCancel(ctx) m.cancel = cancel - mapAddr, err := getPubulicPort(ctx, stunAddr, host, port, false) + mapAddr, err := getPubulicPort(ctx, stunAddr, laddr, false) if err != nil { return nil, netip.AddrPort{}, fmt.Errorf("NatMap: %w", err) } - go keepaliveUDP(ctx, port, log) + go keepaliveUDP(ctx, laddr, log) return &m, mapAddr, nil } -func keepaliveUDP(ctx context.Context, port uint16, log func(error)) { +func keepaliveUDP(ctx context.Context, laddr netip.AddrPort, log func(error)) { + raddr := "223.5.5.5:53" + if laddr.Addr().Is6() { + raddr = "[2400:3200::1]:53" + } r := net.Resolver{ PreferGo: true, Dial: func(context context.Context, network, address string) (net.Conn, error) { - conn, err := reuse.DialContext(context, "udp", "0.0.0.0:"+strconv.Itoa(int(port)), "223.5.5.5:53") + conn, err := reuse.DialContext(context, "udp", laddr.String(), raddr) if err != nil { return nil, err } @@ -67,8 +70,8 @@ func (l logger) Println(v ...any) { l.log(build.String()) } -func ForwardUdp(ctx context.Context, port uint16, target string, log func(string)) (io.Closer, error) { - lc, err := reuse.ListenPacket(ctx, "udp", "0.0.0.0:"+strconv.FormatUint(uint64(port), 10)) +func ForwardUdp(ctx context.Context, laddr netip.AddrPort, target string, log func(string)) (io.Closer, error) { + lc, err := reuse.ListenPacket(ctx, "udp", laddr.String()) if err != nil { return nil, err }