github.com/yaling888/clash@v1.53.0/constant/listener.go (about)

     1  package constant
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"net"
     7  	"net/http"
     8  	"net/url"
     9  	"strconv"
    10  	"strings"
    11  
    12  	"github.com/samber/lo"
    13  
    14  	"github.com/yaling888/clash/component/auth"
    15  )
    16  
    17  type Listener interface {
    18  	RawAddress() string
    19  	Address() string
    20  	Close() error
    21  }
    22  
    23  type AuthenticatorListener interface {
    24  	SetAuthenticator([]auth.AuthUser)
    25  }
    26  
    27  type InboundType string
    28  
    29  const (
    30  	InboundTypeSocks  InboundType = "socks"
    31  	InboundTypeSocks4 InboundType = "socks4"
    32  	InboundTypeSocks5 InboundType = "socks5"
    33  	InboundTypeRedir  InboundType = "redir"
    34  	InboundTypeTproxy InboundType = "tproxy"
    35  	InboundTypeHTTP   InboundType = "http"
    36  	InboundTypeMixed  InboundType = "mixed"
    37  	InboundTypeMitm   InboundType = "mitm"
    38  )
    39  
    40  var supportInboundTypes = map[InboundType]bool{
    41  	InboundTypeSocks:  true,
    42  	InboundTypeSocks4: true,
    43  	InboundTypeSocks5: true,
    44  	InboundTypeRedir:  true,
    45  	InboundTypeTproxy: true,
    46  	InboundTypeHTTP:   true,
    47  	InboundTypeMixed:  true,
    48  	InboundTypeMitm:   true,
    49  }
    50  
    51  type inbound struct {
    52  	Type           InboundType `json:"type" yaml:"type"`
    53  	BindAddress    string      `json:"bind-address" yaml:"bind-address"`
    54  	Authentication *[]string   `json:"authentication" yaml:"authentication"`
    55  	IsFromPortCfg  bool        `json:"-" yaml:"-"`
    56  }
    57  
    58  type Inbound inbound
    59  
    60  // UnmarshalYAML implements yaml.Unmarshaler
    61  func (i *Inbound) UnmarshalYAML(unmarshal func(any) error) error {
    62  	var tp string
    63  	if err := unmarshal(&tp); err != nil {
    64  		var inner inbound
    65  		if err := unmarshal(&inner); err != nil {
    66  			return err
    67  		}
    68  		*i = Inbound(inner)
    69  	} else {
    70  		inner, err := parseInbound(tp)
    71  		if err != nil {
    72  			return err
    73  		}
    74  		*i = Inbound(*inner)
    75  	}
    76  	return verifyInbound(i)
    77  }
    78  
    79  // UnmarshalJSON implements encoding/json.Unmarshaler
    80  func (i *Inbound) UnmarshalJSON(data []byte) error {
    81  	var tp string
    82  	if err := json.Unmarshal(data, &tp); err != nil {
    83  		var inner inbound
    84  		if err := json.Unmarshal(data, &inner); err != nil {
    85  			return err
    86  		}
    87  		*i = Inbound(inner)
    88  	} else {
    89  		inner, err := parseInbound(tp)
    90  		if err != nil {
    91  			return err
    92  		}
    93  		*i = Inbound(*inner)
    94  	}
    95  	return verifyInbound(i)
    96  }
    97  
    98  // MarshalJSON implements encoding/json.Marshaler
    99  func (i *Inbound) MarshalJSON() ([]byte, error) {
   100  	auLen := len(lo.FromPtr(i.Authentication))
   101  	auths := make([]string, 0, auLen)
   102  	if auLen != 0 {
   103  		auths = lo.Map(*i.Authentication, func(au string, _ int) string {
   104  			ss := strings.Split(au, ":")
   105  			s := ss[0]
   106  			l := len(s)
   107  			if l == 0 {
   108  				return ""
   109  			}
   110  			return fmt.Sprintf("%s****%s", s[0:1], s[l-1:l])
   111  		})
   112  	}
   113  	return json.Marshal(map[string]any{
   114  		"type":           string(i.Type),
   115  		"bind-address":   i.BindAddress,
   116  		"authentication": auths,
   117  	})
   118  }
   119  
   120  func (i *Inbound) Key() string {
   121  	if i == nil {
   122  		return ""
   123  	}
   124  	return fmt.Sprintf("%s:%s:%v", i.Type, i.BindAddress, i.IsFromPortCfg)
   125  }
   126  
   127  func (i *Inbound) ToAlias() string {
   128  	if i == nil {
   129  		return "<nil>"
   130  	}
   131  	return string(i.Type) + "://" + i.BindAddress
   132  }
   133  
   134  func parseInbound(alias string) (*inbound, error) {
   135  	u, err := url.Parse(alias)
   136  	if err != nil {
   137  		return nil, err
   138  	}
   139  	listenerType := InboundType(u.Scheme)
   140  	i := &inbound{
   141  		Type:        listenerType,
   142  		BindAddress: u.Host,
   143  	}
   144  	if u.User != nil {
   145  		au := u.User.String()
   146  		if !strings.Contains(au, ":") {
   147  			au += ":"
   148  		}
   149  		i.Authentication = &[]string{au}
   150  	}
   151  	return i, nil
   152  }
   153  
   154  func verifyInbound(i *Inbound) error {
   155  	typeStr := strings.ToLower(string(i.Type))
   156  	switch typeStr {
   157  	case "https":
   158  		i.Type = InboundTypeHTTP
   159  	case "socks4a":
   160  		i.Type = InboundTypeSocks4
   161  	case "socks5h":
   162  		i.Type = InboundTypeSocks5
   163  	default:
   164  		i.Type = InboundType(typeStr)
   165  	}
   166  
   167  	if !supportInboundTypes[i.Type] {
   168  		return fmt.Errorf("not support inbound type: %s", i.Type)
   169  	}
   170  	_, portStr, err := net.SplitHostPort(i.BindAddress)
   171  	if err != nil {
   172  		return fmt.Errorf("parse inbound bind address error, address: %s, error: %w", i.ToAlias(), err)
   173  	}
   174  	port, err := strconv.ParseUint(portStr, 10, 16)
   175  	if err != nil || port == 0 {
   176  		return fmt.Errorf("invalid inbound bind port, address: %s", i.ToAlias())
   177  	}
   178  	if i.Authentication != nil && lo.SomeBy(*i.Authentication, func(s string) bool {
   179  		return !strings.Contains(s, ":")
   180  	}) {
   181  		return fmt.Errorf("invalid inbound authentication, address: %s", i.ToAlias())
   182  	}
   183  	return nil
   184  }
   185  
   186  var proxyInbound *Inbound
   187  
   188  // SetProxyInbound assigns a http or socks inbound to proxyInbound
   189  func SetProxyInbound(tcpInbounds map[string]Inbound) {
   190  	tcpIns := tcpInbounds
   191  	for _, tcp := range tcpIns {
   192  		switch tcp.Type {
   193  		case InboundTypeHTTP, InboundTypeSocks, InboundTypeSocks5, InboundTypeMixed:
   194  			proxyInbound = &tcp
   195  			return
   196  		}
   197  	}
   198  	proxyInbound = nil
   199  }
   200  
   201  // ProxyURL returns a proxy function (for use in a http.Transport),
   202  // nil if no inbound ports are set
   203  func ProxyURL(auth auth.Authenticator) func(*http.Request) (*url.URL, error) {
   204  	mInbound := proxyInbound
   205  	if mInbound == nil {
   206  		return nil
   207  	}
   208  
   209  	var schema string
   210  	switch mInbound.Type {
   211  	case InboundTypeHTTP:
   212  		schema = "http"
   213  	case InboundTypeSocks, InboundTypeSocks5, InboundTypeMixed:
   214  		schema = "socks5"
   215  	default:
   216  		return nil
   217  	}
   218  
   219  	var userInfo *url.Userinfo
   220  	if auths := mInbound.Authentication; len(lo.FromPtr(auths)) != 0 {
   221  		user := strings.Split((*auths)[0], ":")
   222  		userInfo = url.UserPassword(user[0], user[1])
   223  	} else if auth != nil {
   224  		if user := auth.RandomUser(); user != nil {
   225  			userInfo = url.UserPassword(user.User, user.Pass)
   226  		}
   227  	}
   228  
   229  	fixedURL := &url.URL{
   230  		Scheme: schema,
   231  		User:   userInfo,
   232  		Host:   mInbound.BindAddress,
   233  	}
   234  	return http.ProxyURL(fixedURL)
   235  }