You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

112 lines
2.8 KiB

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