inbound_detour_dynamic.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. package point
  2. import (
  3. "sync"
  4. "time"
  5. "github.com/v2ray/v2ray-core/app"
  6. "github.com/v2ray/v2ray-core/common/dice"
  7. "github.com/v2ray/v2ray-core/common/log"
  8. v2net "github.com/v2ray/v2ray-core/common/net"
  9. "github.com/v2ray/v2ray-core/proxy"
  10. proxyrepo "github.com/v2ray/v2ray-core/proxy/repo"
  11. )
  12. type InboundDetourHandlerDynamic struct {
  13. sync.RWMutex
  14. space app.Space
  15. config *InboundDetourConfig
  16. portsInUse map[v2net.Port]bool
  17. ichs []proxy.InboundHandler
  18. lastRefresh time.Time
  19. }
  20. func NewInboundDetourHandlerDynamic(space app.Space, config *InboundDetourConfig) (*InboundDetourHandlerDynamic, error) {
  21. handler := &InboundDetourHandlerDynamic{
  22. space: space,
  23. config: config,
  24. portsInUse: make(map[v2net.Port]bool),
  25. }
  26. handler.ichs = make([]proxy.InboundHandler, config.Allocation.Concurrency)
  27. // To test configuration
  28. ich, err := proxyrepo.CreateInboundHandler(config.Protocol, space, config.Settings, &proxy.InboundHandlerMeta{
  29. Address: config.ListenOn,
  30. Port: 0,
  31. Tag: config.Tag})
  32. if err != nil {
  33. log.Error("Point: Failed to create inbound connection handler: ", err)
  34. return nil, err
  35. }
  36. ich.Close()
  37. return handler, nil
  38. }
  39. func (this *InboundDetourHandlerDynamic) pickUnusedPort() v2net.Port {
  40. delta := int(this.config.PortRange.To) - int(this.config.PortRange.From) + 1
  41. for {
  42. r := dice.Roll(delta)
  43. port := this.config.PortRange.From + v2net.Port(r)
  44. _, used := this.portsInUse[port]
  45. if !used {
  46. return port
  47. }
  48. }
  49. }
  50. func (this *InboundDetourHandlerDynamic) GetConnectionHandler() (proxy.InboundHandler, int) {
  51. this.RLock()
  52. defer this.RUnlock()
  53. ich := this.ichs[dice.Roll(len(this.ichs))]
  54. until := this.config.Allocation.Refresh - int((time.Now().Unix()-this.lastRefresh.Unix())/60/1000)
  55. if until < 0 {
  56. until = 0
  57. }
  58. return ich, int(until)
  59. }
  60. func (this *InboundDetourHandlerDynamic) Close() {
  61. this.Lock()
  62. defer this.Unlock()
  63. for _, ich := range this.ichs {
  64. ich.Close()
  65. }
  66. }
  67. func (this *InboundDetourHandlerDynamic) refresh() error {
  68. this.lastRefresh = time.Now()
  69. config := this.config
  70. ich2Recycle := this.ichs
  71. newIchs := make([]proxy.InboundHandler, config.Allocation.Concurrency)
  72. for idx, _ := range newIchs {
  73. port := this.pickUnusedPort()
  74. ich, err := proxyrepo.CreateInboundHandler(config.Protocol, this.space, config.Settings, &proxy.InboundHandlerMeta{
  75. Address: config.ListenOn, Port: port, Tag: config.Tag})
  76. if err != nil {
  77. log.Error("Point: Failed to create inbound connection handler: ", err)
  78. return err
  79. }
  80. err = ich.Start()
  81. if err != nil {
  82. log.Error("Point: Failed to start inbound connection handler: ", err)
  83. return err
  84. }
  85. this.portsInUse[port] = true
  86. newIchs[idx] = ich
  87. }
  88. this.Lock()
  89. this.ichs = newIchs
  90. this.Unlock()
  91. go func() {
  92. time.Sleep(time.Minute)
  93. for _, ich := range ich2Recycle {
  94. if ich == nil {
  95. continue
  96. }
  97. port := ich.Port()
  98. ich.Close()
  99. delete(this.portsInUse, port)
  100. }
  101. ich2Recycle = nil
  102. }()
  103. return nil
  104. }
  105. func (this *InboundDetourHandlerDynamic) Start() error {
  106. err := this.refresh()
  107. if err != nil {
  108. log.Error("Point: Failed to refresh dynamic allocations: ", err)
  109. return err
  110. }
  111. go func() {
  112. for {
  113. time.Sleep(time.Duration(this.config.Allocation.Refresh) * time.Minute)
  114. err := this.refresh()
  115. if err != nil {
  116. log.Error("Point: Failed to refresh dynamic allocations: ", err)
  117. }
  118. }
  119. }()
  120. return nil
  121. }