ports.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. package ports
  2. import (
  3. "errors"
  4. "net"
  5. "strconv"
  6. "sync"
  7. "time"
  8. "github.com/fatedier/frp/pkg/config/types"
  9. )
  10. const (
  11. MinPort = 1
  12. MaxPort = 65535
  13. MaxPortReservedDuration = time.Duration(24) * time.Hour
  14. CleanReservedPortsInterval = time.Hour
  15. )
  16. var (
  17. ErrPortAlreadyUsed = errors.New("port already used")
  18. ErrPortNotAllowed = errors.New("port not allowed")
  19. ErrPortUnAvailable = errors.New("port unavailable")
  20. ErrNoAvailablePort = errors.New("no available port")
  21. )
  22. type PortCtx struct {
  23. ProxyName string
  24. Port int
  25. Closed bool
  26. UpdateTime time.Time
  27. }
  28. type Manager struct {
  29. reservedPorts map[string]*PortCtx
  30. usedPorts map[int]*PortCtx
  31. freePorts map[int]struct{}
  32. bindAddr string
  33. netType string
  34. mu sync.Mutex
  35. }
  36. func NewManager(netType string, bindAddr string, allowPorts []types.PortsRange) *Manager {
  37. pm := &Manager{
  38. reservedPorts: make(map[string]*PortCtx),
  39. usedPorts: make(map[int]*PortCtx),
  40. freePorts: make(map[int]struct{}),
  41. bindAddr: bindAddr,
  42. netType: netType,
  43. }
  44. if len(allowPorts) > 0 {
  45. for _, pair := range allowPorts {
  46. if pair.Single > 0 {
  47. pm.freePorts[pair.Single] = struct{}{}
  48. } else {
  49. for i := pair.Start; i <= pair.End; i++ {
  50. pm.freePorts[i] = struct{}{}
  51. }
  52. }
  53. }
  54. } else {
  55. for i := MinPort; i <= MaxPort; i++ {
  56. pm.freePorts[i] = struct{}{}
  57. }
  58. }
  59. go pm.cleanReservedPortsWorker()
  60. return pm
  61. }
  62. func (pm *Manager) Acquire(name string, port int) (realPort int, err error) {
  63. portCtx := &PortCtx{
  64. ProxyName: name,
  65. Closed: false,
  66. UpdateTime: time.Now(),
  67. }
  68. var ok bool
  69. pm.mu.Lock()
  70. defer func() {
  71. if err == nil {
  72. portCtx.Port = realPort
  73. }
  74. pm.mu.Unlock()
  75. }()
  76. // check reserved ports first
  77. if port == 0 {
  78. if ctx, ok := pm.reservedPorts[name]; ok {
  79. if pm.isPortAvailable(ctx.Port) {
  80. realPort = ctx.Port
  81. pm.usedPorts[realPort] = portCtx
  82. pm.reservedPorts[name] = portCtx
  83. delete(pm.freePorts, realPort)
  84. return
  85. }
  86. }
  87. }
  88. if port == 0 {
  89. // get random port
  90. count := 0
  91. maxTryTimes := 5
  92. for k := range pm.freePorts {
  93. count++
  94. if count > maxTryTimes {
  95. break
  96. }
  97. if pm.isPortAvailable(k) {
  98. realPort = k
  99. pm.usedPorts[realPort] = portCtx
  100. pm.reservedPorts[name] = portCtx
  101. delete(pm.freePorts, realPort)
  102. break
  103. }
  104. }
  105. if realPort == 0 {
  106. err = ErrNoAvailablePort
  107. }
  108. } else {
  109. // specified port
  110. if _, ok = pm.freePorts[port]; ok {
  111. if pm.isPortAvailable(port) {
  112. realPort = port
  113. pm.usedPorts[realPort] = portCtx
  114. pm.reservedPorts[name] = portCtx
  115. delete(pm.freePorts, realPort)
  116. } else {
  117. err = ErrPortUnAvailable
  118. }
  119. } else {
  120. if _, ok = pm.usedPorts[port]; ok {
  121. err = ErrPortAlreadyUsed
  122. } else {
  123. err = ErrPortNotAllowed
  124. }
  125. }
  126. }
  127. return
  128. }
  129. func (pm *Manager) isPortAvailable(port int) bool {
  130. if pm.netType == "udp" {
  131. addr, err := net.ResolveUDPAddr("udp", net.JoinHostPort(pm.bindAddr, strconv.Itoa(port)))
  132. if err != nil {
  133. return false
  134. }
  135. l, err := net.ListenUDP("udp", addr)
  136. if err != nil {
  137. return false
  138. }
  139. l.Close()
  140. return true
  141. }
  142. l, err := net.Listen(pm.netType, net.JoinHostPort(pm.bindAddr, strconv.Itoa(port)))
  143. if err != nil {
  144. return false
  145. }
  146. l.Close()
  147. return true
  148. }
  149. func (pm *Manager) Release(port int) {
  150. pm.mu.Lock()
  151. defer pm.mu.Unlock()
  152. if ctx, ok := pm.usedPorts[port]; ok {
  153. pm.freePorts[port] = struct{}{}
  154. delete(pm.usedPorts, port)
  155. ctx.Closed = true
  156. ctx.UpdateTime = time.Now()
  157. }
  158. }
  159. // Release reserved port if it isn't used in last 24 hours.
  160. func (pm *Manager) cleanReservedPortsWorker() {
  161. for {
  162. time.Sleep(CleanReservedPortsInterval)
  163. pm.mu.Lock()
  164. for name, ctx := range pm.reservedPorts {
  165. if ctx.Closed && time.Since(ctx.UpdateTime) > MaxPortReservedDuration {
  166. delete(pm.reservedPorts, name)
  167. }
  168. }
  169. pm.mu.Unlock()
  170. }
  171. }