9 log "github.com/sirupsen/logrus"
12 type UPNPCapabilities struct {
17 func makeUPNPListener(intPort int, extPort int) (NAT, net.Listener, net.IP, error) {
18 nat, err := Discover()
20 return nil, nil, nil, errors.New(fmt.Sprintf("NAT upnp could not be discovered: %v", err))
22 log.WithField("ourIP", nat.(*upnpNAT).ourIP).Info("outIP:")
24 ext, err := nat.GetExternalAddress()
26 return nat, nil, nil, errors.New(fmt.Sprintf("External address error: %v", err))
28 log.WithField("address", ext).Info("External address")
30 port, err := nat.AddPortMapping("tcp", extPort, intPort, "Tendermint UPnP Probe", 0)
32 return nat, nil, ext, errors.New(fmt.Sprintf("Port mapping error: %v", err))
34 log.WithField("port", port).Info("Port mapping mapped")
36 // also run the listener, open for all remote addresses.
37 listener, err := net.Listen("tcp", fmt.Sprintf(":%v", intPort))
39 return nat, nil, ext, errors.New(fmt.Sprintf("Error establishing listener: %v", err))
41 return nat, listener, ext, nil
44 func testHairpin(listener net.Listener, extAddr string) (supportsHairpin bool) {
47 inConn, err := listener.Accept()
49 log.WithField("error", err).Error("Listener.Accept() error")
52 log.WithFields(log.Fields{
53 "LocalAddr": inConn.LocalAddr(),
54 "RemoteAddr": inConn.RemoteAddr(),
55 }).Info("Accepted incoming connection")
56 buf := make([]byte, 1024)
57 n, err := inConn.Read(buf)
59 log.WithField("error", err).Error("Incoming connection read error")
62 log.Infof("Incoming connection read %v bytes: %X", n, buf)
63 if string(buf) == "test data" {
64 supportsHairpin = true
70 outConn, err := net.Dial("tcp", extAddr)
72 log.WithField("error", err).Error("Outgoing connection dial error")
76 n, err := outConn.Write([]byte("test data"))
78 log.WithField("error", err).Error("Outgoing connection write error")
81 log.Infof("Outgoing connection wrote %v bytes", n)
83 // Wait for data receipt
84 time.Sleep(1 * time.Second)
88 func Probe() (caps UPNPCapabilities, err error) {
89 log.Info("Probing for UPnP!")
91 intPort, extPort := 8001, 8001
93 nat, listener, ext, err := makeUPNPListener(intPort, extPort)
97 caps.PortMapping = true
101 err = nat.DeletePortMapping("tcp", intPort, extPort)
103 log.WithField("error", err).Error("Port mapping delete error")
108 supportsHairpin := testHairpin(listener, fmt.Sprintf("%v:%v", ext, extPort))