github.com/vipernet-xyz/tm@v0.34.24/p2p/upnp/probe.go (about) 1 package upnp 2 3 import ( 4 "fmt" 5 "net" 6 "time" 7 8 "github.com/vipernet-xyz/tm/libs/log" 9 ) 10 11 type Capabilities struct { 12 PortMapping bool 13 Hairpin bool 14 } 15 16 func makeUPNPListener(intPort int, extPort int, logger log.Logger) (NAT, net.Listener, net.IP, error) { 17 nat, err := Discover() 18 if err != nil { 19 return nil, nil, nil, fmt.Errorf("nat upnp could not be discovered: %v", err) 20 } 21 logger.Info("make upnp listener", "msg", log.NewLazySprintf("ourIP: %v", nat.(*upnpNAT).ourIP)) 22 23 ext, err := nat.GetExternalAddress() 24 if err != nil { 25 return nat, nil, nil, fmt.Errorf("external address error: %v", err) 26 } 27 logger.Info("make upnp listener", "msg", log.NewLazySprintf("External address: %v", ext)) 28 29 port, err := nat.AddPortMapping("tcp", extPort, intPort, "Tendermint UPnP Probe", 0) 30 if err != nil { 31 return nat, nil, ext, fmt.Errorf("port mapping error: %v", err) 32 } 33 logger.Info("make upnp listener", "msg", log.NewLazySprintf("Port mapping mapped: %v", port)) 34 35 // also run the listener, open for all remote addresses. 36 listener, err := net.Listen("tcp", fmt.Sprintf(":%v", intPort)) 37 if err != nil { 38 return nat, nil, ext, fmt.Errorf("error establishing listener: %v", err) 39 } 40 return nat, listener, ext, nil 41 } 42 43 func testHairpin(listener net.Listener, extAddr string, logger log.Logger) (supportsHairpin bool) { 44 // Listener 45 go func() { 46 inConn, err := listener.Accept() 47 if err != nil { 48 logger.Info("test hair pin", "msg", log.NewLazySprintf("Listener.Accept() error: %v", err)) 49 return 50 } 51 logger.Info("test hair pin", 52 "msg", 53 log.NewLazySprintf("Accepted incoming connection: %v -> %v", inConn.LocalAddr(), inConn.RemoteAddr())) 54 buf := make([]byte, 1024) 55 n, err := inConn.Read(buf) 56 if err != nil { 57 logger.Info("test hair pin", 58 "msg", 59 log.NewLazySprintf("Incoming connection read error: %v", err)) 60 return 61 } 62 logger.Info("test hair pin", 63 "msg", 64 log.NewLazySprintf("Incoming connection read %v bytes: %X", n, buf)) 65 if string(buf) == "test data" { 66 supportsHairpin = true 67 return 68 } 69 }() 70 71 // Establish outgoing 72 outConn, err := net.Dial("tcp", extAddr) 73 if err != nil { 74 logger.Info("test hair pin", "msg", log.NewLazySprintf("Outgoing connection dial error: %v", err)) 75 return 76 } 77 78 n, err := outConn.Write([]byte("test data")) 79 if err != nil { 80 logger.Info("test hair pin", "msg", log.NewLazySprintf("Outgoing connection write error: %v", err)) 81 return 82 } 83 logger.Info("test hair pin", "msg", log.NewLazySprintf("Outgoing connection wrote %v bytes", n)) 84 85 // Wait for data receipt 86 time.Sleep(1 * time.Second) 87 return supportsHairpin 88 } 89 90 func Probe(logger log.Logger) (caps Capabilities, err error) { 91 logger.Info("Probing for UPnP!") 92 93 intPort, extPort := 8001, 8001 94 95 nat, listener, ext, err := makeUPNPListener(intPort, extPort, logger) 96 if err != nil { 97 return 98 } 99 caps.PortMapping = true 100 101 // Deferred cleanup 102 defer func() { 103 if err := nat.DeletePortMapping("tcp", intPort, extPort); err != nil { 104 logger.Error(fmt.Sprintf("Port mapping delete error: %v", err)) 105 } 106 if err := listener.Close(); err != nil { 107 logger.Error(fmt.Sprintf("Listener closing error: %v", err)) 108 } 109 }() 110 111 supportsHairpin := testHairpin(listener, fmt.Sprintf("%v:%v", ext, extPort), logger) 112 if supportsHairpin { 113 caps.Hairpin = true 114 } 115 116 return 117 }