1package tunnel 2 3import ( 4 "fmt" 5 "net" 6 "runtime" 7 "sync" 8 "time" 9 10 "github.com/Dreamacro/clash/adapters/inbound" 11 "github.com/Dreamacro/clash/adapters/provider" 12 "github.com/Dreamacro/clash/component/nat" 13 "github.com/Dreamacro/clash/component/resolver" 14 C "github.com/Dreamacro/clash/constant" 15 "github.com/Dreamacro/clash/context" 16 "github.com/Dreamacro/clash/log" 17 "github.com/Dreamacro/clash/tunnel/statistic" 18) 19 20var ( 21 tcpQueue = make(chan C.ConnContext, 200) 22 udpQueue = make(chan *inbound.PacketAdapter, 200) 23 natTable = nat.New() 24 rules []C.Rule 25 proxies = make(map[string]C.Proxy) 26 providers map[string]provider.ProxyProvider 27 configMux sync.RWMutex 28 29 // Outbound Rule 30 mode = Rule 31 32 // default timeout for UDP session 33 udpTimeout = 60 * time.Second 34) 35 36func init() { 37 go process() 38} 39 40// Add request to queue 41func Add(ctx C.ConnContext) { 42 tcpQueue <- ctx 43} 44 45// AddPacket add udp Packet to queue 46func AddPacket(packet *inbound.PacketAdapter) { 47 select { 48 case udpQueue <- packet: 49 default: 50 } 51} 52 53// Rules return all rules 54func Rules() []C.Rule { 55 return rules 56} 57 58// UpdateRules handle update rules 59func UpdateRules(newRules []C.Rule) { 60 configMux.Lock() 61 rules = newRules 62 configMux.Unlock() 63} 64 65// Proxies return all proxies 66func Proxies() map[string]C.Proxy { 67 return proxies 68} 69 70// Providers return all compatible providers 71func Providers() map[string]provider.ProxyProvider { 72 return providers 73} 74 75// UpdateProxies handle update proxies 76func UpdateProxies(newProxies map[string]C.Proxy, newProviders map[string]provider.ProxyProvider) { 77 configMux.Lock() 78 proxies = newProxies 79 providers = newProviders 80 configMux.Unlock() 81} 82 83// Mode return current mode 84func Mode() TunnelMode { 85 return mode 86} 87 88// SetMode change the mode of tunnel 89func SetMode(m TunnelMode) { 90 mode = m 91} 92 93// processUDP starts a loop to handle udp packet 94func processUDP() { 95 queue := udpQueue 96 for conn := range queue { 97 handleUDPConn(conn) 98 } 99} 100 101func process() { 102 numUDPWorkers := 4 103 if runtime.NumCPU() > numUDPWorkers { 104 numUDPWorkers = runtime.NumCPU() 105 } 106 for i := 0; i < numUDPWorkers; i++ { 107 go processUDP() 108 } 109 110 queue := tcpQueue 111 for conn := range queue { 112 go handleTCPConn(conn) 113 } 114} 115 116func needLookupIP(metadata *C.Metadata) bool { 117 return resolver.MappingEnabled() && metadata.Host == "" && metadata.DstIP != nil 118} 119 120func preHandleMetadata(metadata *C.Metadata) error { 121 // handle IP string on host 122 if ip := net.ParseIP(metadata.Host); ip != nil { 123 metadata.DstIP = ip 124 metadata.Host = "" 125 if ip.To4() != nil { 126 metadata.AddrType = C.AtypIPv4 127 } else { 128 metadata.AddrType = C.AtypIPv6 129 } 130 } 131 132 // preprocess enhanced-mode metadata 133 if needLookupIP(metadata) { 134 host, exist := resolver.FindHostByIP(metadata.DstIP) 135 if exist { 136 metadata.Host = host 137 metadata.AddrType = C.AtypDomainName 138 if resolver.FakeIPEnabled() { 139 metadata.DstIP = nil 140 } else if node := resolver.DefaultHosts.Search(host); node != nil { 141 // redir-host should lookup the hosts 142 metadata.DstIP = node.Data.(net.IP) 143 } 144 } else if resolver.IsFakeIP(metadata.DstIP) { 145 return fmt.Errorf("fake DNS record %s missing", metadata.DstIP) 146 } 147 } 148 149 return nil 150} 151 152func resolveMetadata(ctx C.PlainContext, metadata *C.Metadata) (proxy C.Proxy, rule C.Rule, err error) { 153 switch mode { 154 case Direct: 155 proxy = proxies["DIRECT"] 156 case Global: 157 proxy = proxies["GLOBAL"] 158 // Rule 159 default: 160 proxy, rule, err = match(metadata) 161 } 162 return 163} 164 165func handleUDPConn(packet *inbound.PacketAdapter) { 166 metadata := packet.Metadata() 167 if !metadata.Valid() { 168 log.Warnln("[Metadata] not valid: %#v", metadata) 169 return 170 } 171 172 // make a fAddr if request ip is fakeip 173 var fAddr net.Addr 174 if resolver.IsExistFakeIP(metadata.DstIP) { 175 fAddr = metadata.UDPAddr() 176 } 177 178 if err := preHandleMetadata(metadata); err != nil { 179 log.Debugln("[Metadata PreHandle] error: %s", err) 180 return 181 } 182 183 key := packet.LocalAddr().String() 184 185 handle := func() bool { 186 pc := natTable.Get(key) 187 if pc != nil { 188 handleUDPToRemote(packet, pc, metadata) 189 return true 190 } 191 return false 192 } 193 194 if handle() { 195 return 196 } 197 198 lockKey := key + "-lock" 199 cond, loaded := natTable.GetOrCreateLock(lockKey) 200 201 go func() { 202 if loaded { 203 cond.L.Lock() 204 cond.Wait() 205 handle() 206 cond.L.Unlock() 207 return 208 } 209 210 defer func() { 211 natTable.Delete(lockKey) 212 cond.Broadcast() 213 }() 214 215 ctx := context.NewPacketConnContext(metadata) 216 proxy, rule, err := resolveMetadata(ctx, metadata) 217 if err != nil { 218 log.Warnln("[UDP] Parse metadata failed: %s", err.Error()) 219 return 220 } 221 222 rawPc, err := proxy.DialUDP(metadata) 223 if err != nil { 224 if rule == nil { 225 log.Warnln("[UDP] dial %s to %s error: %s", proxy.Name(), metadata.String(), err.Error()) 226 } else { 227 log.Warnln("[UDP] dial %s (match %s/%s) to %s error: %s", proxy.Name(), rule.RuleType().String(), rule.Payload(), metadata.String(), err.Error()) 228 } 229 return 230 } 231 ctx.InjectPacketConn(rawPc) 232 pc := statistic.NewUDPTracker(rawPc, statistic.DefaultManager, metadata, rule) 233 234 switch true { 235 case rule != nil: 236 log.Infoln("[UDP] %s --> %v match %s(%s) using %s", metadata.SourceAddress(), metadata.String(), rule.RuleType().String(), rule.Payload(), rawPc.Chains().String()) 237 case mode == Global: 238 log.Infoln("[UDP] %s --> %v using GLOBAL", metadata.SourceAddress(), metadata.String()) 239 case mode == Direct: 240 log.Infoln("[UDP] %s --> %v using DIRECT", metadata.SourceAddress(), metadata.String()) 241 default: 242 log.Infoln("[UDP] %s --> %v doesn't match any rule using DIRECT", metadata.SourceAddress(), metadata.String()) 243 } 244 245 go handleUDPToLocal(packet.UDPPacket, pc, key, fAddr) 246 247 natTable.Set(key, pc) 248 handle() 249 }() 250} 251 252func handleTCPConn(ctx C.ConnContext) { 253 defer ctx.Conn().Close() 254 255 metadata := ctx.Metadata() 256 if !metadata.Valid() { 257 log.Warnln("[Metadata] not valid: %#v", metadata) 258 return 259 } 260 261 if err := preHandleMetadata(metadata); err != nil { 262 log.Debugln("[Metadata PreHandle] error: %s", err) 263 return 264 } 265 266 proxy, rule, err := resolveMetadata(ctx, metadata) 267 if err != nil { 268 log.Warnln("[Metadata] parse failed: %s", err.Error()) 269 return 270 } 271 272 remoteConn, err := proxy.Dial(metadata) 273 if err != nil { 274 if rule == nil { 275 log.Warnln("[TCP] dial %s to %s error: %s", proxy.Name(), metadata.String(), err.Error()) 276 } else { 277 log.Warnln("[TCP] dial %s (match %s/%s) to %s error: %s", proxy.Name(), rule.RuleType().String(), rule.Payload(), metadata.String(), err.Error()) 278 } 279 return 280 } 281 remoteConn = statistic.NewTCPTracker(remoteConn, statistic.DefaultManager, metadata, rule) 282 defer remoteConn.Close() 283 284 switch true { 285 case rule != nil: 286 log.Infoln("[TCP] %s --> %v match %s(%s) using %s", metadata.SourceAddress(), metadata.String(), rule.RuleType().String(), rule.Payload(), remoteConn.Chains().String()) 287 case mode == Global: 288 log.Infoln("[TCP] %s --> %v using GLOBAL", metadata.SourceAddress(), metadata.String()) 289 case mode == Direct: 290 log.Infoln("[TCP] %s --> %v using DIRECT", metadata.SourceAddress(), metadata.String()) 291 default: 292 log.Infoln("[TCP] %s --> %v doesn't match any rule using DIRECT", metadata.SourceAddress(), metadata.String()) 293 } 294 295 switch c := ctx.(type) { 296 case *context.HTTPContext: 297 handleHTTP(c, remoteConn) 298 default: 299 handleSocket(ctx, remoteConn) 300 } 301} 302 303func shouldResolveIP(rule C.Rule, metadata *C.Metadata) bool { 304 return rule.ShouldResolveIP() && metadata.Host != "" && metadata.DstIP == nil 305} 306 307func match(metadata *C.Metadata) (C.Proxy, C.Rule, error) { 308 configMux.RLock() 309 defer configMux.RUnlock() 310 311 var resolved bool 312 313 if node := resolver.DefaultHosts.Search(metadata.Host); node != nil { 314 ip := node.Data.(net.IP) 315 metadata.DstIP = ip 316 resolved = true 317 } 318 319 for _, rule := range rules { 320 if !resolved && shouldResolveIP(rule, metadata) { 321 ip, err := resolver.ResolveIP(metadata.Host) 322 if err != nil { 323 log.Debugln("[DNS] resolve %s error: %s", metadata.Host, err.Error()) 324 } else { 325 log.Debugln("[DNS] %s --> %s", metadata.Host, ip.String()) 326 metadata.DstIP = ip 327 } 328 resolved = true 329 } 330 331 if rule.Match(metadata) { 332 adapter, ok := proxies[rule.Adapter()] 333 if !ok { 334 continue 335 } 336 337 if metadata.NetWork == C.UDP && !adapter.SupportUDP() { 338 log.Debugln("%s UDP is not supported", adapter.Name()) 339 continue 340 } 341 return adapter, rule, nil 342 } 343 } 344 345 return proxies["DIRECT"], nil, nil 346} 347