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 }