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