upnp.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. package upnp
  2. import (
  3. "errors"
  4. "log"
  5. "sync"
  6. "time"
  7. "gitlab.com/NebulousLabs/go-upnp"
  8. )
  9. /*
  10. uPNP Module
  11. This module handles uPNP Connections to the gateway router and create a port forward entry
  12. for the host system at the given port (set with -port paramter)
  13. */
  14. type UPnPClient struct {
  15. Connection *upnp.IGD //UPnP conenction object
  16. ExternalIP string //Storage of external IP address
  17. RequiredPorts []int //All the required ports will be recored
  18. PolicyNames sync.Map //Name for the required port nubmer
  19. }
  20. func NewUPNPClient(basePort int, hostname string) (*UPnPClient, error) {
  21. //Create uPNP forwarding in the NAT router
  22. log.Println("Discovering UPnP router in Local Area Network...")
  23. d, err := upnp.Discover()
  24. if err != nil {
  25. return &UPnPClient{}, err
  26. }
  27. // discover external IP
  28. ip, err := d.ExternalIP()
  29. if err != nil {
  30. return &UPnPClient{}, err
  31. }
  32. //Create the final obejcts
  33. newUPnPObject := &UPnPClient{
  34. Connection: d,
  35. ExternalIP: ip,
  36. RequiredPorts: []int{},
  37. }
  38. //Require the port that is running ArOZ Online Host
  39. err = newUPnPObject.ForwardPort(basePort, hostname)
  40. if err != nil {
  41. return &UPnPClient{}, err
  42. }
  43. return newUPnPObject, nil
  44. }
  45. func (u *UPnPClient) ForwardPort(portNumber int, ruleName string) error {
  46. log.Println("UPnP forwarding new port: ", portNumber, "for "+ruleName+" service")
  47. //Check if port already forwarded
  48. _, ok := u.PolicyNames.Load(portNumber)
  49. if ok {
  50. //Port already forward. Ignore this request
  51. return errors.New("Port already forwarded")
  52. }
  53. // forward a port
  54. err := u.Connection.Forward(uint16(portNumber), ruleName)
  55. if err != nil {
  56. return err
  57. }
  58. u.RequiredPorts = append(u.RequiredPorts, portNumber)
  59. u.PolicyNames.Store(portNumber, ruleName)
  60. return nil
  61. }
  62. func (u *UPnPClient) ClosePort(portNumber int) error {
  63. //Check if port is opened
  64. portOpened := false
  65. newRequiredPort := []int{}
  66. for _, thisPort := range u.RequiredPorts {
  67. if thisPort != portNumber {
  68. newRequiredPort = append(newRequiredPort, thisPort)
  69. } else {
  70. portOpened = true
  71. }
  72. }
  73. if portOpened {
  74. //Update the port list
  75. u.RequiredPorts = newRequiredPort
  76. // Close the port
  77. log.Println("Closing UPnP Port Forward: ", portNumber)
  78. err := u.Connection.Clear(uint16(portNumber))
  79. //Delete the name registry
  80. u.PolicyNames.Delete(portNumber)
  81. if err != nil {
  82. log.Println(err)
  83. return err
  84. }
  85. }
  86. return nil
  87. }
  88. // Renew forward rules, prevent router lease time from flushing the Upnp config
  89. func (u *UPnPClient) RenewForwardRules() {
  90. portsToRenew := u.RequiredPorts
  91. for _, thisPort := range portsToRenew {
  92. ruleName, ok := u.PolicyNames.Load(thisPort)
  93. if !ok {
  94. continue
  95. }
  96. u.ClosePort(thisPort)
  97. time.Sleep(100 * time.Millisecond)
  98. u.ForwardPort(thisPort, ruleName.(string))
  99. }
  100. log.Println("UPnP Port Forward rule renew completed")
  101. }
  102. func (u *UPnPClient) Close() {
  103. //Shutdown the default UPnP Object
  104. if u != nil {
  105. for _, portNumber := range u.RequiredPorts {
  106. err := u.Connection.Clear(uint16(portNumber))
  107. if err != nil {
  108. log.Println(err)
  109. }
  110. }
  111. }
  112. }