github.com/TeaOSLab/EdgeNode@v1.3.8/internal/nodes/listener_manager.go (about)

     1  package nodes
     2  
     3  import (
     4  	"fmt"
     5  	"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
     6  	"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
     7  	teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
     8  	"github.com/TeaOSLab/EdgeNode/internal/firewalls"
     9  	"github.com/TeaOSLab/EdgeNode/internal/goman"
    10  	"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
    11  	"github.com/TeaOSLab/EdgeNode/internal/utils"
    12  	executils "github.com/TeaOSLab/EdgeNode/internal/utils/exec"
    13  	"github.com/iwind/TeaGo/Tea"
    14  	"github.com/iwind/TeaGo/lists"
    15  	"github.com/iwind/TeaGo/maps"
    16  	"github.com/iwind/TeaGo/types"
    17  	"net/url"
    18  	"regexp"
    19  	"runtime"
    20  	"sort"
    21  	"strings"
    22  	"sync"
    23  	"time"
    24  )
    25  
    26  var sharedListenerManager *ListenerManager
    27  
    28  func init() {
    29  	if !teaconst.IsMain {
    30  		return
    31  	}
    32  
    33  	sharedListenerManager = NewListenerManager()
    34  }
    35  
    36  // ListenerManager 端口监听管理器
    37  type ListenerManager struct {
    38  	listenersMap  map[string]*Listener // addr => *Listener
    39  	http3Listener *HTTPListener
    40  
    41  	locker     sync.Mutex
    42  	lastConfig *nodeconfigs.NodeConfig
    43  
    44  	retryListenerMap map[string]*Listener // 需要重试的监听器 addr => Listener
    45  	ticker           *time.Ticker
    46  
    47  	firewalld         *firewalls.Firewalld
    48  	lastPortStrings   string
    49  	lastTCPPortRanges [][2]int
    50  	lastUDPPortRanges [][2]int
    51  }
    52  
    53  // NewListenerManager 获取新对象
    54  func NewListenerManager() *ListenerManager {
    55  	var manager = &ListenerManager{
    56  		listenersMap:     map[string]*Listener{},
    57  		retryListenerMap: map[string]*Listener{},
    58  		ticker:           time.NewTicker(1 * time.Minute),
    59  		firewalld:        firewalls.NewFirewalld(),
    60  	}
    61  
    62  	// 提升测试效率
    63  	if Tea.IsTesting() {
    64  		manager.ticker = time.NewTicker(5 * time.Second)
    65  	}
    66  
    67  	goman.New(func() {
    68  		for range manager.ticker.C {
    69  			manager.retryListeners()
    70  		}
    71  	})
    72  
    73  	return manager
    74  }
    75  
    76  // Start 启动监听
    77  func (this *ListenerManager) Start(nodeConfig *nodeconfigs.NodeConfig) error {
    78  	this.locker.Lock()
    79  	defer this.locker.Unlock()
    80  
    81  	// 重置数据
    82  	this.retryListenerMap = map[string]*Listener{}
    83  
    84  	// 检查是否有变化
    85  	/**if this.lastConfig != nil && this.lastConfig.Version == node.Version {
    86  		return nil
    87  	}**/
    88  	this.lastConfig = nodeConfig
    89  
    90  	// 所有的新地址
    91  	var groupAddrs = []string{}
    92  	var availableServerGroups = nodeConfig.AvailableGroups()
    93  	if !nodeConfig.IsOn {
    94  		availableServerGroups = []*serverconfigs.ServerAddressGroup{}
    95  	}
    96  
    97  	if len(availableServerGroups) == 0 {
    98  		remotelogs.Println("LISTENER_MANAGER", "no available servers to startup")
    99  	}
   100  
   101  	for _, group := range availableServerGroups {
   102  		var addr = group.FullAddr()
   103  		groupAddrs = append(groupAddrs, addr)
   104  	}
   105  
   106  	// 停掉老的
   107  	for listenerKey, listener := range this.listenersMap {
   108  		var addr = listener.FullAddr()
   109  		if !lists.ContainsString(groupAddrs, addr) {
   110  			remotelogs.Println("LISTENER_MANAGER", "close '"+addr+"'")
   111  			_ = listener.Close()
   112  
   113  			delete(this.listenersMap, listenerKey)
   114  		}
   115  	}
   116  
   117  	// 启动新的或修改老的
   118  	for _, group := range availableServerGroups {
   119  		var addr = group.FullAddr()
   120  		listener, ok := this.listenersMap[addr]
   121  		if ok {
   122  			// 不需要打印reload信息,防止日志数量过多
   123  			listener.Reload(group)
   124  		} else {
   125  			remotelogs.Println("LISTENER_MANAGER", "listen '"+this.prettyAddress(addr)+"'")
   126  			listener = NewListener()
   127  			listener.Reload(group)
   128  			err := listener.Listen()
   129  			if err != nil {
   130  				// 放入到重试队列中
   131  				this.retryListenerMap[addr] = listener
   132  
   133  				var firstServer = group.FirstServer()
   134  				if firstServer == nil {
   135  					remotelogs.Error("LISTENER_MANAGER", err.Error())
   136  				} else {
   137  					// 当前占用的进程名
   138  					if strings.Contains(err.Error(), "in use") {
   139  						portIndex := strings.LastIndex(addr, ":")
   140  						if portIndex > 0 {
   141  							var port = addr[portIndex+1:]
   142  							var processName = this.findProcessNameWithPort(group.IsUDP(), port)
   143  							if len(processName) > 0 {
   144  								err = fmt.Errorf("%w (the process using port: '%s')", err, processName)
   145  							}
   146  						}
   147  					}
   148  
   149  					remotelogs.ServerError(firstServer.Id, "LISTENER_MANAGER", "listen '"+addr+"' failed: "+err.Error(), nodeconfigs.NodeLogTypeListenAddressFailed, maps.Map{"address": addr})
   150  				}
   151  
   152  				continue
   153  			} else {
   154  				// TODO 是否是从错误中恢复
   155  			}
   156  			this.listenersMap[addr] = listener
   157  		}
   158  	}
   159  
   160  	// 加入到firewalld
   161  	go this.addToFirewalld(groupAddrs)
   162  
   163  	return nil
   164  }
   165  
   166  // TotalActiveConnections 获取总的活跃连接数
   167  func (this *ListenerManager) TotalActiveConnections() int {
   168  	this.locker.Lock()
   169  	defer this.locker.Unlock()
   170  
   171  	var total = 0
   172  	for _, listener := range this.listenersMap {
   173  		total += listener.listener.CountActiveConnections()
   174  	}
   175  
   176  	if this.http3Listener != nil {
   177  		total += this.http3Listener.CountActiveConnections()
   178  	}
   179  
   180  	return total
   181  }
   182  
   183  // 返回更加友好格式的地址
   184  func (this *ListenerManager) prettyAddress(addr string) string {
   185  	u, err := url.Parse(addr)
   186  	if err != nil {
   187  		return addr
   188  	}
   189  	if regexp.MustCompile(`^:\d+$`).MatchString(u.Host) {
   190  		u.Host = "*" + u.Host
   191  	}
   192  	return u.String()
   193  }
   194  
   195  // 重试失败的Listener
   196  func (this *ListenerManager) retryListeners() {
   197  	this.locker.Lock()
   198  	defer this.locker.Unlock()
   199  
   200  	for addr, listener := range this.retryListenerMap {
   201  		err := listener.Listen()
   202  		if err == nil {
   203  			delete(this.retryListenerMap, addr)
   204  			this.listenersMap[addr] = listener
   205  			remotelogs.ServerSuccess(listener.group.FirstServer().Id, "LISTENER_MANAGER", "retry to listen '"+addr+"' successfully", nodeconfigs.NodeLogTypeListenAddressFailed, maps.Map{"address": addr})
   206  		}
   207  	}
   208  }
   209  
   210  func (this *ListenerManager) findProcessNameWithPort(isUdp bool, port string) string {
   211  	if runtime.GOOS != "linux" {
   212  		return ""
   213  	}
   214  
   215  	path, err := executils.LookPath("ss")
   216  	if err != nil {
   217  		return ""
   218  	}
   219  
   220  	var option = "t"
   221  	if isUdp {
   222  		option = "u"
   223  	}
   224  
   225  	var cmd = executils.NewTimeoutCmd(10*time.Second, path, "-"+option+"lpn", "sport = :"+port)
   226  	cmd.WithStdout()
   227  	err = cmd.Run()
   228  	if err != nil {
   229  		return ""
   230  	}
   231  
   232  	var matches = regexp.MustCompile(`(?U)\(\("(.+)",pid=\d+,fd=\d+\)\)`).FindStringSubmatch(cmd.Stdout())
   233  	if len(matches) > 1 {
   234  		return matches[1]
   235  	}
   236  	return ""
   237  }
   238  
   239  func (this *ListenerManager) addToFirewalld(groupAddrs []string) {
   240  	if !sharedNodeConfig.AutoOpenPorts {
   241  		return
   242  	}
   243  
   244  	if this.firewalld == nil || !this.firewalld.IsReady() {
   245  		return
   246  	}
   247  
   248  	// HTTP/3相关端口
   249  	var http3Ports = sharedNodeConfig.FindHTTP3Ports()
   250  	if len(http3Ports) > 0 {
   251  		for _, port := range http3Ports {
   252  			var groupAddr = "udp://:" + types.String(port)
   253  			if !lists.ContainsString(groupAddrs, groupAddr) {
   254  				groupAddrs = append(groupAddrs, groupAddr)
   255  			}
   256  		}
   257  	}
   258  
   259  	// 组合端口号
   260  	var portStrings = []string{}
   261  	var udpPorts = []int{}
   262  	var tcpPorts = []int{}
   263  	for _, addr := range groupAddrs {
   264  		var protocol = "tcp"
   265  		if strings.HasPrefix(addr, "udp") {
   266  			protocol = "udp"
   267  		}
   268  
   269  		var lastIndex = strings.LastIndex(addr, ":")
   270  		if lastIndex > 0 {
   271  			var portString = addr[lastIndex+1:]
   272  			portStrings = append(portStrings, portString+"/"+protocol)
   273  
   274  			switch protocol {
   275  			case "tcp":
   276  				tcpPorts = append(tcpPorts, types.Int(portString))
   277  			case "udp":
   278  				udpPorts = append(udpPorts, types.Int(portString))
   279  			}
   280  		}
   281  	}
   282  	if len(portStrings) == 0 {
   283  		return
   284  	}
   285  
   286  	// 检查是否有变化
   287  	sort.Strings(portStrings)
   288  	var newPortStrings = strings.Join(portStrings, ",")
   289  	if newPortStrings == this.lastPortStrings {
   290  		return
   291  	}
   292  	this.locker.Lock()
   293  	this.lastPortStrings = newPortStrings
   294  	this.locker.Unlock()
   295  
   296  	remotelogs.Println("FIREWALLD", "opening ports automatically ...")
   297  	defer func() {
   298  		remotelogs.Println("FIREWALLD", "open ports successfully")
   299  	}()
   300  
   301  	// 合并端口
   302  	var tcpPortRanges = utils.MergePorts(tcpPorts)
   303  	var udpPortRanges = utils.MergePorts(udpPorts)
   304  
   305  	defer func() {
   306  		this.locker.Lock()
   307  		this.lastTCPPortRanges = tcpPortRanges
   308  		this.lastUDPPortRanges = udpPortRanges
   309  		this.locker.Unlock()
   310  	}()
   311  
   312  	// 删除老的不存在的端口
   313  	var tcpPortRangesMap = map[string]bool{}
   314  	var udpPortRangesMap = map[string]bool{}
   315  	for _, portRange := range tcpPortRanges {
   316  		tcpPortRangesMap[this.firewalld.PortRangeString(portRange, "tcp")] = true
   317  	}
   318  	for _, portRange := range udpPortRanges {
   319  		udpPortRangesMap[this.firewalld.PortRangeString(portRange, "udp")] = true
   320  	}
   321  
   322  	for _, portRange := range this.lastTCPPortRanges {
   323  		var s = this.firewalld.PortRangeString(portRange, "tcp")
   324  		_, ok := tcpPortRangesMap[s]
   325  		if ok {
   326  			continue
   327  		}
   328  		remotelogs.Println("FIREWALLD", "remove port '"+s+"'")
   329  		_ = this.firewalld.RemovePortRangePermanently(portRange, "tcp")
   330  	}
   331  	for _, portRange := range this.lastUDPPortRanges {
   332  		var s = this.firewalld.PortRangeString(portRange, "udp")
   333  		_, ok := udpPortRangesMap[s]
   334  		if ok {
   335  			continue
   336  		}
   337  		remotelogs.Println("FIREWALLD", "remove port '"+s+"'")
   338  		_ = this.firewalld.RemovePortRangePermanently(portRange, "udp")
   339  	}
   340  
   341  	// 添加新的
   342  	_ = this.firewalld.AllowPortRangesPermanently(tcpPortRanges, "tcp")
   343  	_ = this.firewalld.AllowPortRangesPermanently(udpPortRanges, "udp")
   344  }
   345  
   346  func (this *ListenerManager) reloadFirewalld() {
   347  	this.locker.Lock()
   348  	defer this.locker.Unlock()
   349  
   350  	var nodeConfig = sharedNodeConfig
   351  
   352  	// 所有的新地址
   353  	var groupAddrs = []string{}
   354  	var availableServerGroups = nodeConfig.AvailableGroups()
   355  	if !nodeConfig.IsOn {
   356  		availableServerGroups = []*serverconfigs.ServerAddressGroup{}
   357  	}
   358  
   359  	if len(availableServerGroups) == 0 {
   360  		remotelogs.Println("LISTENER_MANAGER", "no available servers to startup")
   361  	}
   362  
   363  	for _, group := range availableServerGroups {
   364  		var addr = group.FullAddr()
   365  		groupAddrs = append(groupAddrs, addr)
   366  	}
   367  
   368  	go this.addToFirewalld(groupAddrs)
   369  }