2018-06-10 14:50:03 +00:00
package tunnel
import (
2019-02-02 12:47:38 +00:00
"fmt"
2018-12-05 13:13:29 +00:00
"net"
2019-12-28 10:44:01 +00:00
"runtime"
2018-06-10 14:50:03 +00:00
"sync"
2018-06-16 13:34:13 +00:00
"time"
2018-06-10 14:50:03 +00:00
2021-06-10 06:05:56 +00:00
"github.com/Dreamacro/clash/adapter/inbound"
"github.com/Dreamacro/clash/adapter/provider"
2019-10-11 12:11:18 +00:00
"github.com/Dreamacro/clash/component/nat"
2020-02-15 13:42:46 +00:00
"github.com/Dreamacro/clash/component/resolver"
2018-06-10 14:50:03 +00:00
C "github.com/Dreamacro/clash/constant"
2021-01-23 06:49:46 +00:00
"github.com/Dreamacro/clash/context"
2018-11-21 05:47:46 +00:00
"github.com/Dreamacro/clash/log"
2021-07-01 14:49:29 +00:00
R "github.com/Dreamacro/clash/rule"
2021-01-23 06:49:46 +00:00
"github.com/Dreamacro/clash/tunnel/statistic"
2018-06-10 14:50:03 +00:00
)
var (
2021-01-23 06:49:46 +00:00
tcpQueue = make ( chan C . ConnContext , 200 )
2020-10-20 09:44:39 +00:00
udpQueue = make ( chan * inbound . PacketAdapter , 200 )
2020-09-17 02:48:42 +00:00
natTable = nat . New ( )
rules [ ] C . Rule
proxies = make ( map [ string ] C . Proxy )
providers map [ string ] provider . ProxyProvider
configMux sync . RWMutex
2019-04-24 04:02:52 +00:00
2018-07-25 16:04:59 +00:00
// Outbound Rule
2020-02-15 13:42:46 +00:00
mode = Rule
// default timeout for UDP session
udpTimeout = 60 * time . Second
2021-07-01 14:49:29 +00:00
preProcessCacheFinder , _ = R . NewProcess ( "" , "" , C . ALLNet )
fakeIpMask = net . IPv4Mask ( 0 , 0 , 0xff , 0xff )
fakeIpMaxIp = net . IPv4 ( 0 , 0 , 255 , 255 )
2020-02-15 13:42:46 +00:00
)
func init ( ) {
go process ( )
2018-06-10 14:50:03 +00:00
}
2021-06-13 09:23:10 +00:00
// TCPIn return fan-in queue
func TCPIn ( ) chan <- C . ConnContext {
return tcpQueue
2019-12-28 10:44:01 +00:00
}
2021-06-13 09:23:10 +00:00
// UDPIn return fan-in udp queue
func UDPIn ( ) chan <- * inbound . PacketAdapter {
return udpQueue
2018-06-10 14:50:03 +00:00
}
2018-11-21 05:47:46 +00:00
// Rules return all rules
2020-02-15 13:42:46 +00:00
func Rules ( ) [ ] C . Rule {
return rules
2018-06-18 03:31:49 +00:00
}
2018-11-21 05:47:46 +00:00
// UpdateRules handle update rules
2020-02-15 13:42:46 +00:00
func UpdateRules ( newRules [ ] C . Rule ) {
configMux . Lock ( )
rules = newRules
configMux . Unlock ( )
2018-11-21 05:47:46 +00:00
}
// Proxies return all proxies
2020-02-15 13:42:46 +00:00
func Proxies ( ) map [ string ] C . Proxy {
return proxies
2018-11-21 05:47:46 +00:00
}
2019-12-08 04:17:24 +00:00
// Providers return all compatible providers
2020-02-15 13:42:46 +00:00
func Providers ( ) map [ string ] provider . ProxyProvider {
return providers
2019-12-08 04:17:24 +00:00
}
2018-11-21 05:47:46 +00:00
// UpdateProxies handle update proxies
2020-02-15 13:42:46 +00:00
func UpdateProxies ( newProxies map [ string ] C . Proxy , newProviders map [ string ] provider . ProxyProvider ) {
configMux . Lock ( )
proxies = newProxies
providers = newProviders
configMux . Unlock ( )
2019-04-24 04:02:52 +00:00
}
2018-11-21 05:47:46 +00:00
// Mode return current mode
2020-02-15 13:42:46 +00:00
func Mode ( ) TunnelMode {
return mode
2018-11-21 05:47:46 +00:00
}
// SetMode change the mode of tunnel
2020-02-15 13:42:46 +00:00
func SetMode ( m TunnelMode ) {
mode = m
}
2019-12-28 10:44:01 +00:00
// processUDP starts a loop to handle udp packet
2020-02-15 13:42:46 +00:00
func processUDP ( ) {
2020-10-20 09:44:39 +00:00
queue := udpQueue
for conn := range queue {
2020-02-15 13:42:46 +00:00
handleUDPConn ( conn )
2019-12-28 10:44:01 +00:00
}
}
2020-02-15 13:42:46 +00:00
func process ( ) {
2019-12-28 10:44:01 +00:00
numUDPWorkers := 4
if runtime . NumCPU ( ) > numUDPWorkers {
numUDPWorkers = runtime . NumCPU ( )
}
for i := 0 ; i < numUDPWorkers ; i ++ {
2020-02-15 13:42:46 +00:00
go processUDP ( )
2019-12-28 10:44:01 +00:00
}
2019-10-11 12:11:18 +00:00
2020-10-20 09:44:39 +00:00
queue := tcpQueue
for conn := range queue {
2020-02-15 13:42:46 +00:00
go handleTCPConn ( conn )
2018-06-10 14:50:03 +00:00
}
}
2020-02-15 13:42:46 +00:00
func needLookupIP ( metadata * C . Metadata ) bool {
2020-09-17 02:48:42 +00:00
return resolver . MappingEnabled ( ) && metadata . Host == "" && metadata . DstIP != nil
2019-02-11 07:44:42 +00:00
}
2020-02-15 13:42:46 +00:00
func preHandleMetadata ( metadata * C . Metadata ) error {
2020-02-07 12:53:43 +00:00
// handle IP string on host
2019-10-27 16:02:23 +00:00
if ip := net . ParseIP ( metadata . Host ) ; ip != nil {
metadata . DstIP = ip
2021-03-10 04:11:45 +00:00
metadata . Host = ""
2021-03-12 09:41:37 +00:00
if ip . To4 ( ) != nil {
metadata . AddrType = C . AtypIPv4
} else {
metadata . AddrType = C . AtypIPv6
}
2019-10-27 16:02:23 +00:00
}
2019-05-02 16:05:14 +00:00
// preprocess enhanced-mode metadata
2020-02-15 13:42:46 +00:00
if needLookupIP ( metadata ) {
2020-09-17 02:48:42 +00:00
host , exist := resolver . FindHostByIP ( metadata . DstIP )
2018-12-05 13:13:29 +00:00
if exist {
metadata . Host = host
metadata . AddrType = C . AtypDomainName
2020-09-17 02:48:42 +00:00
if resolver . FakeIPEnabled ( ) {
2019-05-09 13:00:29 +00:00
metadata . DstIP = nil
2020-04-27 13:28:24 +00:00
} else if node := resolver . DefaultHosts . Search ( host ) ; node != nil {
// redir-host should lookup the hosts
metadata . DstIP = node . Data . ( net . IP )
2019-05-02 16:05:14 +00:00
}
2021-07-01 14:49:29 +00:00
} else if resolver . IsFakeIP ( metadata . DstIP ) && ! fakeIpMaxIp . Equal ( metadata . DstIP . Mask ( fakeIpMask ) ) {
2020-02-07 12:53:43 +00:00
return fmt . Errorf ( "fake DNS record %s missing" , metadata . DstIP )
2018-12-05 13:13:29 +00:00
}
}
2020-02-07 12:53:43 +00:00
return nil
}
2021-01-23 06:49:46 +00:00
func resolveMetadata ( ctx C . PlainContext , metadata * C . Metadata ) ( proxy C . Proxy , rule C . Rule , err error ) {
2020-02-15 13:42:46 +00:00
switch mode {
2018-11-21 05:47:46 +00:00
case Direct :
2020-02-15 13:42:46 +00:00
proxy = proxies [ "DIRECT" ]
2018-11-21 05:47:46 +00:00
case Global :
2020-02-15 13:42:46 +00:00
proxy = proxies [ "GLOBAL" ]
2018-07-12 15:28:38 +00:00
// Rule
default :
2020-02-15 13:42:46 +00:00
proxy , rule , err = match ( metadata )
2018-07-12 15:28:38 +00:00
}
2021-01-23 06:49:46 +00:00
return
2019-10-11 12:11:18 +00:00
}
2019-02-02 12:47:38 +00:00
2020-02-15 13:42:46 +00:00
func handleUDPConn ( packet * inbound . PacketAdapter ) {
2019-12-28 10:44:01 +00:00
metadata := packet . Metadata ( )
2019-10-11 12:11:18 +00:00
if ! metadata . Valid ( ) {
log . Warnln ( "[Metadata] not valid: %#v" , metadata )
return
2019-07-25 09:47:39 +00:00
}
2020-10-28 13:26:50 +00:00
// make a fAddr if request ip is fakeip
2020-03-10 12:36:24 +00:00
var fAddr net . Addr
2020-10-17 04:52:43 +00:00
if resolver . IsExistFakeIP ( metadata . DstIP ) {
2020-03-10 12:36:24 +00:00
fAddr = metadata . UDPAddr ( )
}
2020-02-15 13:42:46 +00:00
if err := preHandleMetadata ( metadata ) ; err != nil {
2020-02-07 12:53:43 +00:00
log . Debugln ( "[Metadata PreHandle] error: %s" , err )
return
2020-01-31 11:26:33 +00:00
}
2020-01-31 06:43:54 +00:00
key := packet . LocalAddr ( ) . String ( )
2020-10-28 13:26:50 +00:00
handle := func ( ) bool {
pc := natTable . Get ( key )
if pc != nil {
handleUDPToRemote ( packet , pc , metadata )
return true
}
return false
}
if handle ( ) {
2019-10-11 12:11:18 +00:00
return
}
lockKey := key + "-lock"
2020-10-28 13:26:50 +00:00
cond , loaded := natTable . GetOrCreateLock ( lockKey )
2019-12-28 10:44:01 +00:00
2019-10-11 12:11:18 +00:00
go func ( ) {
2020-10-28 13:26:50 +00:00
if loaded {
cond . L . Lock ( )
cond . Wait ( )
handle ( )
cond . L . Unlock ( )
return
}
2019-10-11 12:11:18 +00:00
2020-10-28 13:26:50 +00:00
defer func ( ) {
2020-02-15 13:42:46 +00:00
natTable . Delete ( lockKey )
2020-10-28 13:26:50 +00:00
cond . Broadcast ( )
} ( )
2021-01-23 06:49:46 +00:00
ctx := context . NewPacketConnContext ( metadata )
proxy , rule , err := resolveMetadata ( ctx , metadata )
2020-10-28 13:26:50 +00:00
if err != nil {
log . Warnln ( "[UDP] Parse metadata failed: %s" , err . Error ( ) )
return
2019-04-24 02:29:29 +00:00
}
2020-10-28 13:26:50 +00:00
rawPc , err := proxy . DialUDP ( metadata )
if err != nil {
2020-11-22 15:38:12 +00:00
if rule == nil {
log . Warnln ( "[UDP] dial %s to %s error: %s" , proxy . Name ( ) , metadata . String ( ) , err . Error ( ) )
} else {
log . Warnln ( "[UDP] dial %s (match %s/%s) to %s error: %s" , proxy . Name ( ) , rule . RuleType ( ) . String ( ) , rule . Payload ( ) , metadata . String ( ) , err . Error ( ) )
}
2020-10-28 13:26:50 +00:00
return
}
2021-01-23 06:49:46 +00:00
ctx . InjectPacketConn ( rawPc )
pc := statistic . NewUDPTracker ( rawPc , statistic . DefaultManager , metadata , rule )
2020-10-28 13:26:50 +00:00
switch true {
case rule != nil :
2021-07-01 14:49:29 +00:00
log . Infoln ( "[UDP] %s(%s) --> %s:%s match %s(%s) %s using %s" , metadata . SourceAddress ( ) , metadata . Process , metadata . String ( ) , metadata . DstPort , rule . RuleType ( ) . String ( ) , rule . Payload ( ) , rule . NetWork ( ) . String ( ) , rawPc . Chains ( ) . String ( ) )
2020-10-28 13:26:50 +00:00
case mode == Global :
2021-07-01 14:49:29 +00:00
log . Infoln ( "[UDP] %s(%s) --> %s using GLOBAL" , metadata . SourceAddress ( ) , metadata . Process , metadata . String ( ) )
2020-10-28 13:26:50 +00:00
case mode == Direct :
2021-07-01 14:49:29 +00:00
log . Infoln ( "[UDP] %s(%s) --> %s using DIRECT" , metadata . SourceAddress ( ) , metadata . Process , metadata . String ( ) )
2020-10-28 13:26:50 +00:00
default :
2021-07-01 14:49:29 +00:00
log . Infoln ( "[UDP] %s(%s) --> %s doesn't match any rule using DIRECT" , metadata . SourceAddress ( ) , metadata . Process , metadata . String ( ) )
2019-08-08 17:28:37 +00:00
}
2020-10-28 13:26:50 +00:00
go handleUDPToLocal ( packet . UDPPacket , pc , key , fAddr )
natTable . Set ( key , pc )
handle ( )
2019-10-11 12:11:18 +00:00
} ( )
}
2019-08-08 17:28:37 +00:00
2021-01-23 06:49:46 +00:00
func handleTCPConn ( ctx C . ConnContext ) {
defer ctx . Conn ( ) . Close ( )
2019-10-11 12:11:18 +00:00
2021-01-23 06:49:46 +00:00
metadata := ctx . Metadata ( )
2019-10-11 12:11:18 +00:00
if ! metadata . Valid ( ) {
log . Warnln ( "[Metadata] not valid: %#v" , metadata )
return
2019-04-23 15:29:36 +00:00
}
2020-02-15 13:42:46 +00:00
if err := preHandleMetadata ( metadata ) ; err != nil {
2020-02-07 12:53:43 +00:00
log . Debugln ( "[Metadata PreHandle] error: %s" , err )
return
}
2021-01-23 06:49:46 +00:00
proxy , rule , err := resolveMetadata ( ctx , metadata )
2019-10-11 12:11:18 +00:00
if err != nil {
2020-11-19 16:27:37 +00:00
log . Warnln ( "[Metadata] parse failed: %s" , err . Error ( ) )
2019-10-11 12:11:18 +00:00
return
}
2019-07-25 09:47:39 +00:00
2019-08-12 06:01:32 +00:00
remoteConn , err := proxy . Dial ( metadata )
2018-06-10 14:50:03 +00:00
if err != nil {
2020-11-22 15:38:12 +00:00
if rule == nil {
log . Warnln ( "[TCP] dial %s to %s error: %s" , proxy . Name ( ) , metadata . String ( ) , err . Error ( ) )
} else {
log . Warnln ( "[TCP] dial %s (match %s/%s) to %s error: %s" , proxy . Name ( ) , rule . RuleType ( ) . String ( ) , rule . Payload ( ) , metadata . String ( ) , err . Error ( ) )
}
2018-06-10 14:50:03 +00:00
return
}
2021-01-23 06:49:46 +00:00
remoteConn = statistic . NewTCPTracker ( remoteConn , statistic . DefaultManager , metadata , rule )
2019-08-12 06:01:32 +00:00
defer remoteConn . Close ( )
2018-06-10 14:50:03 +00:00
2020-01-31 06:58:54 +00:00
switch true {
case rule != nil :
2021-07-01 14:49:29 +00:00
log . Infoln ( "[TCP] %s(%s) --> %s:%s match %s(%s) %s using %s" , metadata . SourceAddress ( ) , metadata . Process , metadata . String ( ) , metadata . DstPort , rule . RuleType ( ) . String ( ) , rule . Payload ( ) , rule . NetWork ( ) . String ( ) , remoteConn . Chains ( ) . String ( ) )
2020-02-15 13:42:46 +00:00
case mode == Global :
2021-07-01 14:49:29 +00:00
log . Infoln ( "[TCP] %s(%s) --> %s using GLOBAL" , metadata . SourceAddress ( ) , metadata . Process , metadata . String ( ) )
2020-02-15 13:42:46 +00:00
case mode == Direct :
2021-07-01 14:49:29 +00:00
log . Infoln ( "[TCP] %s(%s) --> %s using DIRECT" , metadata . SourceAddress ( ) , metadata . Process , metadata . String ( ) )
2020-01-31 06:58:54 +00:00
default :
2021-07-01 14:49:29 +00:00
log . Infoln ( "[TCP] %s(%s) --> %s doesn't match any rule using DIRECT" , metadata . SourceAddress ( ) , metadata . Process , metadata . String ( ) )
2019-08-08 17:28:37 +00:00
}
2021-06-15 09:13:40 +00:00
handleSocket ( ctx , remoteConn )
2018-06-10 14:50:03 +00:00
}
2020-02-15 13:42:46 +00:00
func shouldResolveIP ( rule C . Rule , metadata * C . Metadata ) bool {
2020-07-27 05:47:00 +00:00
return rule . ShouldResolveIP ( ) && metadata . Host != "" && metadata . DstIP == nil
2019-02-02 12:47:38 +00:00
}
2020-02-15 13:42:46 +00:00
func match ( metadata * C . Metadata ) ( C . Proxy , C . Rule , error ) {
configMux . RLock ( )
defer configMux . RUnlock ( )
2018-06-14 16:49:52 +00:00
2019-04-24 04:02:52 +00:00
var resolved bool
2019-09-11 09:00:55 +00:00
2020-02-15 13:42:46 +00:00
if node := resolver . DefaultHosts . Search ( metadata . Host ) ; node != nil {
2019-09-11 09:00:55 +00:00
ip := node . Data . ( net . IP )
2019-10-27 13:44:07 +00:00
metadata . DstIP = ip
2019-09-11 09:00:55 +00:00
resolved = true
}
2021-07-01 14:49:29 +00:00
// preset process name and cache it
preProcessCacheFinder . Match ( metadata )
2020-02-15 13:42:46 +00:00
for _ , rule := range rules {
if ! resolved && shouldResolveIP ( rule , metadata ) {
ip , err := resolver . ResolveIP ( metadata . Host )
2019-02-02 12:47:38 +00:00
if err != nil {
2019-04-24 04:02:52 +00:00
log . Debugln ( "[DNS] resolve %s error: %s" , metadata . Host , err . Error ( ) )
} else {
log . Debugln ( "[DNS] %s --> %s" , metadata . Host , ip . String ( ) )
2019-10-27 13:44:07 +00:00
metadata . DstIP = ip
2019-02-02 12:47:38 +00:00
}
2019-04-24 04:02:52 +00:00
resolved = true
2019-02-02 12:47:38 +00:00
}
2019-10-27 16:02:23 +00:00
if rule . Match ( metadata ) {
2020-02-15 13:42:46 +00:00
adapter , ok := proxies [ rule . Adapter ( ) ]
2019-04-23 15:29:36 +00:00
if ! ok {
continue
2018-06-10 14:50:03 +00:00
}
2019-04-23 15:29:36 +00:00
if metadata . NetWork == C . UDP && ! adapter . SupportUDP ( ) {
2021-04-05 05:31:10 +00:00
log . Debugln ( "%s UDP is not supported" , adapter . Name ( ) )
2019-04-23 15:29:36 +00:00
continue
}
2021-07-01 14:49:29 +00:00
if rule . NetWork ( ) != C . ALLNet && rule . NetWork ( ) != metadata . NetWork {
continue
}
2019-08-08 17:28:37 +00:00
return adapter , rule , nil
2018-06-10 14:50:03 +00:00
}
}
2020-02-15 13:42:46 +00:00
return proxies [ "DIRECT" ] , nil , nil
2018-06-10 14:50:03 +00:00
}