Improve: lazy resolve ip

This commit is contained in:
Dreamacro 2019-02-02 20:47:38 +08:00
parent e30a628702
commit bfe51e46b0
2 changed files with 46 additions and 18 deletions

View file

@ -39,9 +39,17 @@ type Metadata struct {
Port string Port string
} }
func (addr *Metadata) String() string { func (m *Metadata) String() string {
if addr.Host == "" { if m.Host == "" {
return addr.IP.String() return m.IP.String()
} }
return addr.Host return m.Host
}
func (m *Metadata) Valid() bool {
return m.Host != "" || m.IP != nil
}
func (m *Metadata) NeedLoopUpHost() bool {
return m.Source == REDIR
} }

View file

@ -1,6 +1,7 @@
package tunnel package tunnel
import ( import (
"fmt"
"net" "net"
"sync" "sync"
"time" "time"
@ -10,7 +11,7 @@ import (
"github.com/Dreamacro/clash/dns" "github.com/Dreamacro/clash/dns"
"github.com/Dreamacro/clash/log" "github.com/Dreamacro/clash/log"
"gopkg.in/eapache/channels.v1" channels "gopkg.in/eapache/channels.v1"
) )
var ( var (
@ -80,6 +81,10 @@ func (t *Tunnel) SetResolver(resolver *dns.Resolver) {
t.resolver = resolver t.resolver = resolver
} }
func (t *Tunnel) hasResolver() bool {
return t.resolver != nil
}
func (t *Tunnel) process() { func (t *Tunnel) process() {
queue := t.queue.Out() queue := t.queue.Out()
for { for {
@ -106,20 +111,12 @@ func (t *Tunnel) handleConn(localConn C.ServerAdapter) {
defer localConn.Close() defer localConn.Close()
metadata := localConn.Metadata() metadata := localConn.Metadata()
if metadata.Source == C.REDIR && t.resolver != nil { if metadata.NeedLoopUpHost() && t.hasResolver() {
host, exist := t.resolver.IPToHost(*metadata.IP) host, exist := t.resolver.IPToHost(*metadata.IP)
if exist { if exist {
metadata.Host = host metadata.Host = host
metadata.AddrType = C.AtypDomainName metadata.AddrType = C.AtypDomainName
} }
} else if metadata.IP == nil && metadata.AddrType == C.AtypDomainName {
ip, err := t.resolveIP(metadata.Host)
if err != nil {
log.Debugln("[DNS] resolve %s error: %s", metadata.Host, err.Error())
} else {
log.Debugln("[DNS] %s --> %s", metadata.Host, ip.String())
metadata.IP = &ip
}
} }
var proxy C.Proxy var proxy C.Proxy
@ -130,8 +127,18 @@ func (t *Tunnel) handleConn(localConn C.ServerAdapter) {
proxy = t.proxies["GLOBAL"] proxy = t.proxies["GLOBAL"]
// Rule // Rule
default: default:
proxy = t.match(metadata) var err error
proxy, err = t.match(metadata)
if err != nil {
return
}
} }
if !metadata.Valid() {
log.Warnln("[Metadata] not valid: %#v", metadata)
return
}
remoConn, err := proxy.Generator(metadata) remoConn, err := proxy.Generator(metadata)
if err != nil { if err != nil {
log.Warnln("Proxy[%s] connect [%s] error: %s", proxy.Name(), metadata.String(), err.Error()) log.Warnln("Proxy[%s] connect [%s] error: %s", proxy.Name(), metadata.String(), err.Error())
@ -147,20 +154,33 @@ func (t *Tunnel) handleConn(localConn C.ServerAdapter) {
} }
} }
func (t *Tunnel) match(metadata *C.Metadata) C.Proxy { func (t *Tunnel) shouldResolveIP(rule C.Rule, metadata *C.Metadata) bool {
return (rule.RuleType() == C.GEOIP || rule.RuleType() == C.IPCIDR) && metadata.Host != "" && metadata.IP == nil
}
func (t *Tunnel) match(metadata *C.Metadata) (C.Proxy, error) {
t.configLock.RLock() t.configLock.RLock()
defer t.configLock.RUnlock() defer t.configLock.RUnlock()
for _, rule := range t.rules { for _, rule := range t.rules {
if t.shouldResolveIP(rule, metadata) {
ip, err := t.resolveIP(metadata.Host)
if err != nil {
return nil, fmt.Errorf("[DNS] resolve %s error: %s", metadata.Host, err.Error())
}
log.Debugln("[DNS] %s --> %s", metadata.Host, ip.String())
metadata.IP = &ip
}
if rule.IsMatch(metadata) { if rule.IsMatch(metadata) {
if a, ok := t.proxies[rule.Adapter()]; ok { if a, ok := t.proxies[rule.Adapter()]; ok {
log.Infoln("%v match %s using %s", metadata.String(), rule.RuleType().String(), rule.Adapter()) log.Infoln("%v match %s using %s", metadata.String(), rule.RuleType().String(), rule.Adapter())
return a return a, nil
} }
} }
} }
log.Infoln("%v doesn't match any rule using DIRECT", metadata.String()) log.Infoln("%v doesn't match any rule using DIRECT", metadata.String())
return t.proxies["DIRECT"] return t.proxies["DIRECT"], nil
} }
func newTunnel() *Tunnel { func newTunnel() *Tunnel {