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