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  }