|
|
- package upnp
-
- import (
- "fmt"
- "net"
- "time"
-
- "github.com/pkg/errors"
-
- cmn "github.com/tendermint/tmlibs/common"
- "github.com/tendermint/tmlibs/log"
- )
-
- type UPNPCapabilities struct {
- PortMapping bool
- Hairpin bool
- }
-
- func makeUPNPListener(intPort int, extPort int, logger log.Logger) (NAT, net.Listener, net.IP, error) {
- nat, err := Discover()
- if err != nil {
- return nil, nil, nil, errors.Errorf("NAT upnp could not be discovered: %v", err)
- }
- logger.Info(cmn.Fmt("ourIP: %v", nat.(*upnpNAT).ourIP))
-
- ext, err := nat.GetExternalAddress()
- if err != nil {
- return nat, nil, nil, errors.Errorf("External address error: %v", err)
- }
- logger.Info(cmn.Fmt("External address: %v", ext))
-
- port, err := nat.AddPortMapping("tcp", extPort, intPort, "Tendermint UPnP Probe", 0)
- if err != nil {
- return nat, nil, ext, errors.Errorf("Port mapping error: %v", err)
- }
- logger.Info(cmn.Fmt("Port mapping mapped: %v", port))
-
- // also run the listener, open for all remote addresses.
- listener, err := net.Listen("tcp", fmt.Sprintf(":%v", intPort))
- if err != nil {
- return nat, nil, ext, errors.Errorf("Error establishing listener: %v", err)
- }
- return nat, listener, ext, nil
- }
-
- func testHairpin(listener net.Listener, extAddr string, logger log.Logger) (supportsHairpin bool) {
- // Listener
- go func() {
- inConn, err := listener.Accept()
- if err != nil {
- logger.Info(cmn.Fmt("Listener.Accept() error: %v", err))
- return
- }
- logger.Info(cmn.Fmt("Accepted incoming connection: %v -> %v", inConn.LocalAddr(), inConn.RemoteAddr()))
- buf := make([]byte, 1024)
- n, err := inConn.Read(buf)
- if err != nil {
- logger.Info(cmn.Fmt("Incoming connection read error: %v", err))
- return
- }
- logger.Info(cmn.Fmt("Incoming connection read %v bytes: %X", n, buf))
- if string(buf) == "test data" {
- supportsHairpin = true
- return
- }
- }()
-
- // Establish outgoing
- outConn, err := net.Dial("tcp", extAddr)
- if err != nil {
- logger.Info(cmn.Fmt("Outgoing connection dial error: %v", err))
- return
- }
-
- n, err := outConn.Write([]byte("test data"))
- if err != nil {
- logger.Info(cmn.Fmt("Outgoing connection write error: %v", err))
- return
- }
- logger.Info(cmn.Fmt("Outgoing connection wrote %v bytes", n))
-
- // Wait for data receipt
- time.Sleep(1 * time.Second)
- return
- }
-
- func Probe(logger log.Logger) (caps UPNPCapabilities, err error) {
- logger.Info("Probing for UPnP!")
-
- intPort, extPort := 8001, 8001
-
- nat, listener, ext, err := makeUPNPListener(intPort, extPort, logger)
- if err != nil {
- return
- }
- caps.PortMapping = true
-
- // Deferred cleanup
- defer func() {
- if err := nat.DeletePortMapping("tcp", intPort, extPort); err != nil {
- logger.Error(cmn.Fmt("Port mapping delete error: %v", err))
- }
- if err := listener.Close(); err != nil {
- panic(err)
- }
- }()
-
- supportsHairpin := testHairpin(listener, fmt.Sprintf("%v:%v", ext, extPort), logger)
- if supportsHairpin {
- caps.Hairpin = true
- }
-
- return
- }
|