github.com/igoogolx/clash@v1.19.8/listener/listener.go (about)

     1  package listener
     2  
     3  import (
     4  	"fmt"
     5  	"net"
     6  	"strconv"
     7  	"strings"
     8  	"sync"
     9  
    10  	"github.com/igoogolx/clash/adapter/inbound"
    11  	"github.com/igoogolx/clash/config"
    12  	C "github.com/igoogolx/clash/constant"
    13  	"github.com/igoogolx/clash/listener/http"
    14  	"github.com/igoogolx/clash/listener/mixed"
    15  	"github.com/igoogolx/clash/listener/redir"
    16  	"github.com/igoogolx/clash/listener/socks"
    17  	"github.com/igoogolx/clash/listener/tproxy"
    18  	"github.com/igoogolx/clash/listener/tunnel"
    19  	"github.com/igoogolx/clash/log"
    20  
    21  	"github.com/samber/lo"
    22  )
    23  
    24  var (
    25  	allowLan    = false
    26  	bindAddress = "*"
    27  
    28  	tcpListeners = map[C.Inbound]C.Listener{}
    29  	udpListeners = map[C.Inbound]C.Listener{}
    30  
    31  	tunnelTCPListeners = map[string]*tunnel.Listener{}
    32  	tunnelUDPListeners = map[string]*tunnel.PacketConn{}
    33  
    34  	// lock for recreate function
    35  	recreateMux sync.Mutex
    36  	tunnelMux   sync.Mutex
    37  )
    38  
    39  type Ports struct {
    40  	Port       int `json:"port"`
    41  	SocksPort  int `json:"socks-port"`
    42  	RedirPort  int `json:"redir-port"`
    43  	TProxyPort int `json:"tproxy-port"`
    44  	MixedPort  int `json:"mixed-port"`
    45  }
    46  
    47  var tcpListenerCreators = map[C.InboundType]tcpListenerCreator{
    48  	C.InboundTypeHTTP:   http.New,
    49  	C.InboundTypeSocks:  socks.New,
    50  	C.InboundTypeRedir:  redir.New,
    51  	C.InboundTypeTproxy: tproxy.New,
    52  	C.InboundTypeMixed:  mixed.New,
    53  }
    54  
    55  var udpListenerCreators = map[C.InboundType]udpListenerCreator{
    56  	C.InboundTypeSocks:  socks.NewUDP,
    57  	C.InboundTypeRedir:  tproxy.NewUDP,
    58  	C.InboundTypeTproxy: tproxy.NewUDP,
    59  	C.InboundTypeMixed:  socks.NewUDP,
    60  }
    61  
    62  type (
    63  	tcpListenerCreator func(addr string, tcpIn chan<- C.ConnContext) (C.Listener, error)
    64  	udpListenerCreator func(addr string, udpIn chan<- *inbound.PacketAdapter) (C.Listener, error)
    65  )
    66  
    67  func AllowLan() bool {
    68  	return allowLan
    69  }
    70  
    71  func BindAddress() string {
    72  	return bindAddress
    73  }
    74  
    75  func SetAllowLan(al bool) {
    76  	allowLan = al
    77  }
    78  
    79  func SetBindAddress(host string) {
    80  	bindAddress = host
    81  }
    82  
    83  func createListener(inbound C.Inbound, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) {
    84  	addr := inbound.BindAddress
    85  	if portIsZero(addr) {
    86  		return
    87  	}
    88  	tcpCreator := tcpListenerCreators[inbound.Type]
    89  	udpCreator := udpListenerCreators[inbound.Type]
    90  	if tcpCreator == nil && udpCreator == nil {
    91  		log.Errorln("inbound type %s is not supported", inbound.Type)
    92  		return
    93  	}
    94  	if tcpCreator != nil {
    95  		tcpListener, err := tcpCreator(addr, tcpIn)
    96  		if err != nil {
    97  			log.Errorln("create addr %s tcp listener error: %v", addr, err)
    98  			return
    99  		}
   100  		tcpListeners[inbound] = tcpListener
   101  	}
   102  	if udpCreator != nil {
   103  		udpListener, err := udpCreator(addr, udpIn)
   104  		if err != nil {
   105  			log.Errorln("create addr %s udp listener error: %v", addr, err)
   106  			return
   107  		}
   108  		udpListeners[inbound] = udpListener
   109  	}
   110  	log.Infoln("inbound %s created successfully", inbound.ToAlias())
   111  }
   112  
   113  func closeListener(inbound C.Inbound) {
   114  	listener := tcpListeners[inbound]
   115  	if listener != nil {
   116  		if err := listener.Close(); err != nil {
   117  			log.Errorln("close tcp address `%s` error: %s", inbound.ToAlias(), err.Error())
   118  		}
   119  		delete(tcpListeners, inbound)
   120  	}
   121  	listener = udpListeners[inbound]
   122  	if listener != nil {
   123  		if err := listener.Close(); err != nil {
   124  			log.Errorln("close udp address `%s` error: %s", inbound.ToAlias(), err.Error())
   125  		}
   126  		delete(udpListeners, inbound)
   127  	}
   128  }
   129  
   130  func getNeedCloseAndCreateInbound(originInbounds []C.Inbound, newInbounds []C.Inbound) ([]C.Inbound, []C.Inbound) {
   131  	needCloseMap := map[C.Inbound]bool{}
   132  	needClose := []C.Inbound{}
   133  	needCreate := []C.Inbound{}
   134  
   135  	for _, inbound := range originInbounds {
   136  		needCloseMap[inbound] = true
   137  	}
   138  	for _, inbound := range newInbounds {
   139  		if needCloseMap[inbound] {
   140  			delete(needCloseMap, inbound)
   141  		} else {
   142  			needCreate = append(needCreate, inbound)
   143  		}
   144  	}
   145  	for inbound := range needCloseMap {
   146  		needClose = append(needClose, inbound)
   147  	}
   148  	return needClose, needCreate
   149  }
   150  
   151  // only recreate inbound config listener
   152  func ReCreateListeners(inbounds []C.Inbound, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) {
   153  	newInbounds := []C.Inbound{}
   154  	newInbounds = append(newInbounds, inbounds...)
   155  	for _, inbound := range getInbounds() {
   156  		if inbound.IsFromPortCfg {
   157  			newInbounds = append(newInbounds, inbound)
   158  		}
   159  	}
   160  	reCreateListeners(newInbounds, tcpIn, udpIn)
   161  }
   162  
   163  // only recreate ports config listener
   164  func ReCreatePortsListeners(ports Ports, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) {
   165  	newInbounds := []C.Inbound{}
   166  	newInbounds = append(newInbounds, GetInbounds()...)
   167  	newInbounds = addPortInbound(newInbounds, C.InboundTypeHTTP, ports.Port)
   168  	newInbounds = addPortInbound(newInbounds, C.InboundTypeSocks, ports.SocksPort)
   169  	newInbounds = addPortInbound(newInbounds, C.InboundTypeRedir, ports.RedirPort)
   170  	newInbounds = addPortInbound(newInbounds, C.InboundTypeTproxy, ports.TProxyPort)
   171  	newInbounds = addPortInbound(newInbounds, C.InboundTypeMixed, ports.MixedPort)
   172  	reCreateListeners(newInbounds, tcpIn, udpIn)
   173  }
   174  
   175  func addPortInbound(inbounds []C.Inbound, inboundType C.InboundType, port int) []C.Inbound {
   176  	if port != 0 {
   177  		inbounds = append(inbounds, C.Inbound{
   178  			Type:          inboundType,
   179  			BindAddress:   genAddr(bindAddress, port, allowLan),
   180  			IsFromPortCfg: true,
   181  		})
   182  	}
   183  	return inbounds
   184  }
   185  
   186  func reCreateListeners(inbounds []C.Inbound, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) {
   187  	recreateMux.Lock()
   188  	defer recreateMux.Unlock()
   189  	needClose, needCreate := getNeedCloseAndCreateInbound(getInbounds(), inbounds)
   190  	for _, inbound := range needClose {
   191  		closeListener(inbound)
   192  	}
   193  	for _, inbound := range needCreate {
   194  		createListener(inbound, tcpIn, udpIn)
   195  	}
   196  }
   197  
   198  func PatchTunnel(tunnels []config.Tunnel, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) {
   199  	tunnelMux.Lock()
   200  	defer tunnelMux.Unlock()
   201  
   202  	type addrProxy struct {
   203  		network string
   204  		addr    string
   205  		target  string
   206  		proxy   string
   207  	}
   208  
   209  	tcpOld := lo.Map(
   210  		lo.Keys(tunnelTCPListeners),
   211  		func(key string, _ int) addrProxy {
   212  			parts := strings.Split(key, "/")
   213  			return addrProxy{
   214  				network: "tcp",
   215  				addr:    parts[0],
   216  				target:  parts[1],
   217  				proxy:   parts[2],
   218  			}
   219  		},
   220  	)
   221  	udpOld := lo.Map(
   222  		lo.Keys(tunnelUDPListeners),
   223  		func(key string, _ int) addrProxy {
   224  			parts := strings.Split(key, "/")
   225  			return addrProxy{
   226  				network: "udp",
   227  				addr:    parts[0],
   228  				target:  parts[1],
   229  				proxy:   parts[2],
   230  			}
   231  		},
   232  	)
   233  	oldElm := lo.Union(tcpOld, udpOld)
   234  
   235  	newElm := lo.FlatMap(
   236  		tunnels,
   237  		func(tunnel config.Tunnel, _ int) []addrProxy {
   238  			return lo.Map(
   239  				tunnel.Network,
   240  				func(network string, _ int) addrProxy {
   241  					return addrProxy{
   242  						network: network,
   243  						addr:    tunnel.Address,
   244  						target:  tunnel.Target,
   245  						proxy:   tunnel.Proxy,
   246  					}
   247  				},
   248  			)
   249  		},
   250  	)
   251  
   252  	needClose, needCreate := lo.Difference(oldElm, newElm)
   253  
   254  	for _, elm := range needClose {
   255  		key := fmt.Sprintf("%s/%s/%s", elm.addr, elm.target, elm.proxy)
   256  		if elm.network == "tcp" {
   257  			tunnelTCPListeners[key].Close()
   258  			delete(tunnelTCPListeners, key)
   259  		} else {
   260  			tunnelUDPListeners[key].Close()
   261  			delete(tunnelUDPListeners, key)
   262  		}
   263  	}
   264  
   265  	for _, elm := range needCreate {
   266  		key := fmt.Sprintf("%s/%s/%s", elm.addr, elm.target, elm.proxy)
   267  		if elm.network == "tcp" {
   268  			l, err := tunnel.New(elm.addr, elm.target, elm.proxy, tcpIn)
   269  			if err != nil {
   270  				log.Errorln("Start tunnel %s error: %s", elm.target, err.Error())
   271  				continue
   272  			}
   273  			tunnelTCPListeners[key] = l
   274  			log.Infoln("Tunnel(tcp/%s) proxy %s listening at: %s", elm.target, elm.proxy, tunnelTCPListeners[key].Address())
   275  		} else {
   276  			l, err := tunnel.NewUDP(elm.addr, elm.target, elm.proxy, udpIn)
   277  			if err != nil {
   278  				log.Errorln("Start tunnel %s error: %s", elm.target, err.Error())
   279  				continue
   280  			}
   281  			tunnelUDPListeners[key] = l
   282  			log.Infoln("Tunnel(udp/%s) proxy %s listening at: %s", elm.target, elm.proxy, tunnelUDPListeners[key].Address())
   283  		}
   284  	}
   285  }
   286  
   287  func GetInbounds() []C.Inbound {
   288  	return lo.Filter(getInbounds(), func(inbound C.Inbound, idx int) bool {
   289  		return !inbound.IsFromPortCfg
   290  	})
   291  }
   292  
   293  // GetInbounds return the inbounds of proxy servers
   294  func getInbounds() []C.Inbound {
   295  	var inbounds []C.Inbound
   296  	for inbound := range tcpListeners {
   297  		inbounds = append(inbounds, inbound)
   298  	}
   299  	for inbound := range udpListeners {
   300  		if _, ok := tcpListeners[inbound]; !ok {
   301  			inbounds = append(inbounds, inbound)
   302  		}
   303  	}
   304  	return inbounds
   305  }
   306  
   307  // GetPorts return the ports of proxy servers
   308  func GetPorts() *Ports {
   309  	ports := &Ports{}
   310  	for _, inbound := range getInbounds() {
   311  		fillPort(inbound, ports)
   312  	}
   313  	return ports
   314  }
   315  
   316  func fillPort(inbound C.Inbound, ports *Ports) {
   317  	if inbound.IsFromPortCfg {
   318  		port := getPort(inbound.BindAddress)
   319  		switch inbound.Type {
   320  		case C.InboundTypeHTTP:
   321  			ports.Port = port
   322  		case C.InboundTypeSocks:
   323  			ports.SocksPort = port
   324  		case C.InboundTypeTproxy:
   325  			ports.TProxyPort = port
   326  		case C.InboundTypeRedir:
   327  			ports.RedirPort = port
   328  		case C.InboundTypeMixed:
   329  			ports.MixedPort = port
   330  		default:
   331  			// do nothing
   332  		}
   333  	}
   334  }
   335  
   336  func portIsZero(addr string) bool {
   337  	_, port, err := net.SplitHostPort(addr)
   338  	if port == "0" || port == "" || err != nil {
   339  		return true
   340  	}
   341  	return false
   342  }
   343  
   344  func genAddr(host string, port int, allowLan bool) string {
   345  	if allowLan {
   346  		if host == "*" {
   347  			return fmt.Sprintf(":%d", port)
   348  		}
   349  		return fmt.Sprintf("%s:%d", host, port)
   350  	}
   351  
   352  	return fmt.Sprintf("127.0.0.1:%d", port)
   353  }
   354  
   355  func getPort(addr string) int {
   356  	_, portStr, err := net.SplitHostPort(addr)
   357  	if err != nil {
   358  		return 0
   359  	}
   360  	port, err := strconv.Atoi(portStr)
   361  	if err != nil {
   362  		return 0
   363  	}
   364  	return port
   365  }