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.

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