github.com/number571/tendermint@v0.34.11-gost/internal/p2p/upnp/probe.go (about) 1 package upnp 2 3 import ( 4 "fmt" 5 "net" 6 "time" 7 8 "github.com/number571/tendermint/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(fmt.Sprintf("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(fmt.Sprintf("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(fmt.Sprintf("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(fmt.Sprintf("Listener.Accept() error: %v", err)) 49 return 50 } 51 logger.Info(fmt.Sprintf("Accepted incoming connection: %v -> %v", inConn.LocalAddr(), inConn.RemoteAddr())) 52 buf := make([]byte, 1024) 53 n, err := inConn.Read(buf) 54 if err != nil { 55 logger.Info(fmt.Sprintf("Incoming connection read error: %v", err)) 56 return 57 } 58 logger.Info(fmt.Sprintf("Incoming connection read %v bytes: %X", n, buf)) 59 if string(buf) == "test data" { 60 supportsHairpin = true 61 return 62 } 63 }() 64 65 // Establish outgoing 66 outConn, err := net.Dial("tcp", extAddr) 67 if err != nil { 68 logger.Info(fmt.Sprintf("Outgoing connection dial error: %v", err)) 69 return 70 } 71 72 n, err := outConn.Write([]byte("test data")) 73 if err != nil { 74 logger.Info(fmt.Sprintf("Outgoing connection write error: %v", err)) 75 return 76 } 77 logger.Info(fmt.Sprintf("Outgoing connection wrote %v bytes", n)) 78 79 // Wait for data receipt 80 time.Sleep(1 * time.Second) 81 return supportsHairpin 82 } 83 84 func Probe(logger log.Logger) (caps Capabilities, err error) { 85 logger.Info("Probing for UPnP!") 86 87 intPort, extPort := 8001, 8001 88 89 nat, listener, ext, err := makeUPNPListener(intPort, extPort, logger) 90 if err != nil { 91 return 92 } 93 caps.PortMapping = true 94 95 // Deferred cleanup 96 defer func() { 97 if err := nat.DeletePortMapping("tcp", intPort, extPort); err != nil { 98 logger.Error(fmt.Sprintf("Port mapping delete error: %v", err)) 99 } 100 if err := listener.Close(); err != nil { 101 logger.Error(fmt.Sprintf("Listener closing error: %v", err)) 102 } 103 }() 104 105 supportsHairpin := testHairpin(listener, fmt.Sprintf("%v:%v", ext, extPort), logger) 106 if supportsHairpin { 107 caps.Hairpin = true 108 } 109 110 return 111 }