Style: format code

This commit is contained in:
gVisor bot 2021-10-28 00:06:55 +08:00
parent febc9fb22c
commit 0387c93c56
23 changed files with 280 additions and 545 deletions

View file

@ -158,9 +158,9 @@ func NewTrojan(option TrojanOption) (*Trojan, error) {
} }
tlsConfig := &tls.Config{ tlsConfig := &tls.Config{
NextProtos: option.ALPN, NextProtos: option.ALPN,
MinVersion: tls.VersionTLS12, MinVersion: tls.VersionTLS12,
//InsecureSkipVerify: tOption.SkipCertVerify, InsecureSkipVerify: tOption.SkipCertVerify,
ServerName: tOption.ServerName, ServerName: tOption.ServerName,
} }

45
component/mmdb/mmdb.go Normal file
View file

@ -0,0 +1,45 @@
package mmdb
import (
"sync"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/log"
"github.com/oschwald/geoip2-golang"
)
var (
mmdb *geoip2.Reader
once sync.Once
)
func LoadFromBytes(buffer []byte) {
once.Do(func() {
var err error
mmdb, err = geoip2.FromBytes(buffer)
if err != nil {
log.Fatalln("Can't load mmdb: %s", err.Error())
}
})
}
func Verify() bool {
instance, err := geoip2.Open(C.Path.MMDB())
if err == nil {
instance.Close()
}
return err == nil
}
func Instance() *geoip2.Reader {
once.Do(func() {
var err error
mmdb, err = geoip2.Open(C.Path.MMDB())
if err != nil {
log.Fatalln("Can't load mmdb: %s", err.Error())
}
})
return mmdb
}

View file

@ -6,18 +6,19 @@ import (
"net/http" "net/http"
"os" "os"
"github.com/Dreamacro/clash/component/mmdb"
C "github.com/Dreamacro/clash/constant" C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/log" "github.com/Dreamacro/clash/log"
) )
func downloadGeoIP(path string) (err error) { func downloadMMDB(path string) (err error) {
resp, err := http.Get("https://cdn.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/geoip.dat") resp, err := http.Get("https://cdn.jsdelivr.net/gh/Loyalsoldier/geoip@release/Country.mmdb")
if err != nil { if err != nil {
return return
} }
defer resp.Body.Close() defer resp.Body.Close()
f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0644) f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0o644)
if err != nil { if err != nil {
return err return err
} }
@ -27,6 +28,45 @@ func downloadGeoIP(path string) (err error) {
return err return err
} }
func initMMDB() error {
if _, err := os.Stat(C.Path.MMDB()); os.IsNotExist(err) {
log.Infoln("Can't find MMDB, start download")
if err := downloadMMDB(C.Path.MMDB()); err != nil {
return fmt.Errorf("can't download MMDB: %s", err.Error())
}
}
if !mmdb.Verify() {
log.Warnln("MMDB invalid, remove and download")
if err := os.Remove(C.Path.MMDB()); err != nil {
return fmt.Errorf("can't remove invalid MMDB: %s", err.Error())
}
if err := downloadMMDB(C.Path.MMDB()); err != nil {
return fmt.Errorf("can't download MMDB: %s", err.Error())
}
}
return nil
}
//func downloadGeoIP(path string) (err error) {
// resp, err := http.Get("https://cdn.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/geoip.dat")
// if err != nil {
// return
// }
// defer resp.Body.Close()
//
// f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0644)
// if err != nil {
// return err
// }
// defer f.Close()
// _, err = io.Copy(f, resp.Body)
//
// return err
//}
func downloadGeoSite(path string) (err error) { func downloadGeoSite(path string) (err error) {
resp, err := http.Get("https://cdn.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/geosite.dat") resp, err := http.Get("https://cdn.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/geosite.dat")
if err != nil { if err != nil {
@ -34,7 +74,7 @@ func downloadGeoSite(path string) (err error) {
} }
defer resp.Body.Close() defer resp.Body.Close()
f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0644) f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0o644)
if err != nil { if err != nil {
return err return err
} }
@ -44,17 +84,18 @@ func downloadGeoSite(path string) (err error) {
return err return err
} }
func initGeoIP() error { //
if _, err := os.Stat(C.Path.GeoIP()); os.IsNotExist(err) { //func initGeoIP() error {
log.Infoln("Can't find GeoIP.dat, start download") // if _, err := os.Stat(C.Path.GeoIP()); os.IsNotExist(err) {
if err := downloadGeoIP(C.Path.GeoIP()); err != nil { // log.Infoln("Can't find GeoIP.dat, start download")
return fmt.Errorf("can't download GeoIP.dat: %s", err.Error()) // if err := downloadGeoIP(C.Path.GeoIP()); err != nil {
} // return fmt.Errorf("can't download GeoIP.dat: %s", err.Error())
log.Infoln("Download GeoIP.dat finish") // }
} // log.Infoln("Download GeoIP.dat finish")
// }
return nil //
} // return nil
//}
func initGeoSite() error { func initGeoSite() error {
if _, err := os.Stat(C.Path.GeoSite()); os.IsNotExist(err) { if _, err := os.Stat(C.Path.GeoSite()); os.IsNotExist(err) {
@ -72,7 +113,7 @@ func initGeoSite() error {
func Init(dir string) error { func Init(dir string) error {
// initial homedir // initial homedir
if _, err := os.Stat(dir); os.IsNotExist(err) { if _, err := os.Stat(dir); os.IsNotExist(err) {
if err := os.MkdirAll(dir, 0777); err != nil { if err := os.MkdirAll(dir, 0o777); err != nil {
return fmt.Errorf("can't create config directory %s: %s", dir, err.Error()) return fmt.Errorf("can't create config directory %s: %s", dir, err.Error())
} }
} }
@ -80,7 +121,7 @@ func Init(dir string) error {
// initial config.yaml // initial config.yaml
if _, err := os.Stat(C.Path.Config()); os.IsNotExist(err) { if _, err := os.Stat(C.Path.Config()); os.IsNotExist(err) {
log.Infoln("Can't find config, create a initial config file") log.Infoln("Can't find config, create a initial config file")
f, err := os.OpenFile(C.Path.Config(), os.O_CREATE|os.O_WRONLY, 0644) f, err := os.OpenFile(C.Path.Config(), os.O_CREATE|os.O_WRONLY, 0o644)
if err != nil { if err != nil {
return fmt.Errorf("can't create file %s: %s", C.Path.Config(), err.Error()) return fmt.Errorf("can't create file %s: %s", C.Path.Config(), err.Error())
} }
@ -88,9 +129,14 @@ func Init(dir string) error {
f.Close() f.Close()
} }
// initial GeoIP //// initial GeoIP
if err := initGeoIP(); err != nil { //if err := initGeoIP(); err != nil {
return fmt.Errorf("can't initial GeoIP: %w", err) // return fmt.Errorf("can't initial GeoIP: %w", err)
//}
// initial mmdb
if err := initMMDB(); err != nil {
return fmt.Errorf("can't initial MMDB: %w", err)
} }
// initial GeoSite // initial GeoSite

View file

@ -1,5 +1,7 @@
package constant package constant
import "net"
// Rule Type // Rule Type
const ( const (
Domain RuleType = iota Domain RuleType = iota
@ -54,3 +56,5 @@ type Rule interface {
ShouldResolveIP() bool ShouldResolveIP() bool
RuleExtra() *RuleExtra RuleExtra() *RuleExtra
} }
var TunBroadcastAddr = net.IPv4(198, 18, 255, 255)

View file

@ -2,16 +2,13 @@ package dns
import ( import (
"net" "net"
"strings"
"github.com/Dreamacro/clash/component/mmdb"
"github.com/Dreamacro/clash/component/trie" "github.com/Dreamacro/clash/component/trie"
"github.com/Dreamacro/clash/log" C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/rule/geodata"
"github.com/Dreamacro/clash/rule/geodata/router"
_ "github.com/Dreamacro/clash/rule/geodata/standard"
) )
var multiGeoIPMatcher *router.MultiGeoIPMatcher
type fallbackIPFilter interface { type fallbackIPFilter interface {
Match(net.IP) bool Match(net.IP) bool
} }
@ -21,49 +18,8 @@ type geoipFilter struct {
} }
func (gf *geoipFilter) Match(ip net.IP) bool { func (gf *geoipFilter) Match(ip net.IP) bool {
if multiGeoIPMatcher == nil { record, _ := mmdb.Instance().Country(ip)
countryCode := gf.code return !strings.EqualFold(record.Country.IsoCode, gf.code) && !ip.IsPrivate() && !ip.Equal(C.TunBroadcastAddr)
countryCodePrivate := "private"
geoLoader, err := geodata.GetGeoDataLoader("standard")
if err != nil {
log.Errorln("[GeoIPFilter] GetGeoDataLoader error: %s", err.Error())
return false
}
recordsCN, err := geoLoader.LoadGeoIP(countryCode)
if err != nil {
log.Errorln("[GeoIPFilter] LoadGeoIP error: %s", err.Error())
return false
}
recordsPrivate, err := geoLoader.LoadGeoIP(countryCodePrivate)
if err != nil {
log.Errorln("[GeoIPFilter] LoadGeoIP error: %s", err.Error())
return false
}
geoips := []*router.GeoIP{
{
CountryCode: countryCode,
Cidr: recordsCN,
ReverseMatch: false,
},
{
CountryCode: countryCodePrivate,
Cidr: recordsPrivate,
ReverseMatch: false,
},
}
multiGeoIPMatcher, err = router.NewMultiGeoIPMatcher(geoips)
if err != nil {
log.Errorln("[GeoIPFilter] NewMultiGeoIPMatcher error: %s", err.Error())
return false
}
}
return !multiGeoIPMatcher.ApplyIp(ip)
} }
type ipnetFilter struct { type ipnetFilter struct {

2
go.mod
View file

@ -12,6 +12,7 @@ require (
github.com/insomniacslk/dhcp v0.0.0-20210827173440-b95caade3eac github.com/insomniacslk/dhcp v0.0.0-20210827173440-b95caade3eac
github.com/kr328/tun2socket v0.0.0-20210412191540-3d56c47e2d99 github.com/kr328/tun2socket v0.0.0-20210412191540-3d56c47e2d99
github.com/miekg/dns v1.1.43 github.com/miekg/dns v1.1.43
github.com/oschwald/geoip2-golang v1.5.0
github.com/sirupsen/logrus v1.8.1 github.com/sirupsen/logrus v1.8.1
github.com/stretchr/testify v1.7.0 github.com/stretchr/testify v1.7.0
github.com/xtls/go v0.0.0-20201118062508-3632bf3b7499 github.com/xtls/go v0.0.0-20201118062508-3632bf3b7499
@ -28,6 +29,7 @@ require (
require ( require (
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/google/btree v1.0.1 // indirect github.com/google/btree v1.0.1 // indirect
github.com/oschwald/maxminddb-golang v1.8.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/u-root/uio v0.0.0-20210528114334-82958018845c // indirect github.com/u-root/uio v0.0.0-20210528114334-82958018845c // indirect
golang.org/x/text v0.3.6 // indirect golang.org/x/text v0.3.6 // indirect

5
go.sum
View file

@ -463,6 +463,10 @@ github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.m
github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs=
github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo= github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo=
github.com/oschwald/geoip2-golang v1.5.0 h1:igg2yQIrrcRccB1ytFXqBfOHCjXWIoMv85lVJ1ONZzw=
github.com/oschwald/geoip2-golang v1.5.0/go.mod h1:xdvYt5xQzB8ORWFqPnqMwZpCpgNagttWdoZLlJQzg7s=
github.com/oschwald/maxminddb-golang v1.8.0 h1:Uh/DSnGoxsyp/KYbY1AuP0tYEwfs0sCph9p/UMXK/Hk=
github.com/oschwald/maxminddb-golang v1.8.0/go.mod h1:RXZtst0N6+FY/3qCNmZMBApR19cdQj43/NM9VkrNAis=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
@ -750,6 +754,7 @@ golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191224085550-c709ea063b76/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

View file

@ -20,41 +20,36 @@ import (
"github.com/Dreamacro/clash/common/pool" "github.com/Dreamacro/clash/common/pool"
) )
const utunControlName = "com.apple.net.utun_control" const (
const _IOC_OUT = 0x40000000 utunControlName = "com.apple.net.utun_control"
const _IOC_IN = 0x80000000 iocOut = 0x40000000
const _IOC_INOUT = _IOC_IN | _IOC_OUT iocIn = 0x80000000
iocInout = iocIn | iocOut
)
// _CTLIOCGINFO value derived from /usr/include/sys/{kern_control,ioccom}.h // _CTLIOCGINFO value derived from /usr/include/sys/{kern_control,ioccom}.h
// https://github.com/apple/darwin-xnu/blob/master/bsd/sys/ioccom.h // https://github.com/apple/darwin-xnu/blob/master/bsd/sys/ioccom.h
// #define CTLIOCGINFO _IOWR('N', 3, struct ctl_info) /* get id from name */ = 0xc0644e03 // #define CTLIOCGINFO _IOWR('N', 3, struct ctl_info) /* get id from name */ = 0xc0644e03
const _CTLIOCGINFO = _IOC_INOUT | ((100 & 0x1fff) << 16) | uint32(byte('N'))<<8 | 3 const _CTLIOCGINFO = iocInout | ((100 & 0x1fff) << 16) | uint32(byte('N'))<<8 | 3
// #define SIOCAIFADDR_IN6 _IOW('i', 26, struct in6_aliasreq) = 0x8080691a
//const _SIOCAIFADDR_IN6 = _IOC_IN | ((128 & 0x1fff) << 16) | uint32(byte('i'))<<8 | 26
// #define SIOCPROTOATTACH_IN6 _IOWR('i', 110, struct in6_aliasreq_64) // #define SIOCPROTOATTACH_IN6 _IOWR('i', 110, struct in6_aliasreq_64)
const _SIOCPROTOATTACH_IN6 = _IOC_INOUT | ((128 & 0x1fff) << 16) | uint32(byte('i'))<<8 | 110 const siocprotoattachIn6 = iocInout | ((128 & 0x1fff) << 16) | uint32(byte('i'))<<8 | 110
// #define SIOCLL_START _IOWR('i', 130, struct in6_aliasreq) // #define SIOCLL_START _IOWR('i', 130, struct in6Aliasreq)
const _SIOCLL_START = _IOC_INOUT | ((128 & 0x1fff) << 16) | uint32(byte('i'))<<8 | 130 const siocllStart = iocInout | ((128 & 0x1fff) << 16) | uint32(byte('i'))<<8 | 130
// https://github.com/apple/darwin-xnu/blob/a449c6a3b8014d9406c2ddbdc81795da24aa7443/bsd/netinet6/nd6.h#L469
const ND6_INFINITE_LIFETIME = 0xffffffff
// Following the wireguard-go solution: // Following the wireguard-go solution:
// These unix.SYS_* constants were removed from golang.org/x/sys/unix // These unix.SYS_* constants were removed from golang.org/x/sys/unix
// so copy them here for now. // so copy them here for now.
// See https://github.com/golang/go/issues/41868 // See https://github.com/golang/go/issues/41868
const ( const (
sys_IOCTL = 54 sysIoctl = 54
sys_CONNECT = 98 sysConnect = 98
sys_GETSOCKOPT = 118 sysGetsockopt = 118
) )
type tunDarwin struct { type tunDarwin struct {
//url string
name string name string
tunAddress string tunAddress string
autoRoute bool autoRoute bool
@ -75,6 +70,37 @@ type sockaddrCtl struct {
scReserved [5]uint32 scReserved [5]uint32
} }
type ctlInfo struct {
ctlID uint32
ctlName [96]byte
}
// https://github.com/apple/darwin-xnu/blob/a449c6a3b8014d9406c2ddbdc81795da24aa7443/bsd/sys/sockio.h#L107
// https://github.com/apple/darwin-xnu/blob/a449c6a3b8014d9406c2ddbdc81795da24aa7443/bsd/net/if.h#L570-L575
// https://man.openbsd.org/netintro.4#SIOCAIFADDR
type aliasreq struct {
ifraName [unix.IFNAMSIZ]byte
ifraAddr unix.RawSockaddrInet4
ifraDstaddr unix.RawSockaddrInet4
ifraMask unix.RawSockaddrInet4
}
// SIOCAIFADDR_IN6
// https://github.com/apple/darwin-xnu/blob/a449c6a3b8014d9406c2ddbdc81795da24aa7443/bsd/netinet6/in6_var.h#L114-L119
// https://opensource.apple.com/source/network_cmds/network_cmds-543.260.3/
type in6Addrlifetime struct{}
// https://github.com/apple/darwin-xnu/blob/a449c6a3b8014d9406c2ddbdc81795da24aa7443/bsd/netinet6/in6_var.h#L336-L343
// https://github.com/apple/darwin-xnu/blob/a449c6a3b8014d9406c2ddbdc81795da24aa7443/bsd/netinet6/in6.h#L174-L181
type in6Aliasreq struct {
ifraName [unix.IFNAMSIZ]byte
ifraAddr unix.RawSockaddrInet6
ifraDstaddr unix.RawSockaddrInet6
ifraPrefixmask unix.RawSockaddrInet6
ifraFlags int32
ifraLifetime in6Addrlifetime
}
// https://github.com/apple/darwin-xnu/blob/a449c6a3b8014d9406c2ddbdc81795da24aa7443/bsd/net/if.h#L402-L563 // https://github.com/apple/darwin-xnu/blob/a449c6a3b8014d9406c2ddbdc81795da24aa7443/bsd/net/if.h#L402-L563
//type ifreqAddr struct { //type ifreqAddr struct {
@ -87,7 +113,6 @@ var sockaddrCtlSize uintptr = 32
// OpenTunDevice return a TunDevice according a URL // OpenTunDevice return a TunDevice according a URL
func OpenTunDevice(tunAddress string, autoRoute bool) (TunDevice, error) { func OpenTunDevice(tunAddress string, autoRoute bool) (TunDevice, error) {
name := "utun" name := "utun"
// TODO: configure the MTU // TODO: configure the MTU
mtu := 9000 mtu := 9000
@ -101,23 +126,19 @@ func OpenTunDevice(tunAddress string, autoRoute bool) (TunDevice, error) {
} }
fd, err := unix.Socket(unix.AF_SYSTEM, unix.SOCK_DGRAM, 2) fd, err := unix.Socket(unix.AF_SYSTEM, unix.SOCK_DGRAM, 2)
if err != nil { if err != nil {
return nil, err return nil, err
} }
var ctlInfo = &struct { ctlInfo1 := &ctlInfo{}
ctlID uint32
ctlName [96]byte
}{}
copy(ctlInfo.ctlName[:], []byte(utunControlName)) copy(ctlInfo1.ctlName[:], []byte(utunControlName))
_, _, errno := unix.Syscall( _, _, errno := unix.Syscall(
sys_IOCTL, sysIoctl,
uintptr(fd), uintptr(fd),
uintptr(_CTLIOCGINFO), uintptr(_CTLIOCGINFO),
uintptr(unsafe.Pointer(ctlInfo)), uintptr(unsafe.Pointer(ctlInfo1)),
) )
if errno != 0 { if errno != 0 {
@ -128,14 +149,14 @@ func OpenTunDevice(tunAddress string, autoRoute bool) (TunDevice, error) {
scLen: uint8(sockaddrCtlSize), scLen: uint8(sockaddrCtlSize),
scFamily: unix.AF_SYSTEM, scFamily: unix.AF_SYSTEM,
ssSysaddr: 2, ssSysaddr: 2,
scID: ctlInfo.ctlID, scID: ctlInfo1.ctlID,
scUnit: uint32(ifIndex) + 1, scUnit: uint32(ifIndex) + 1,
} }
scPointer := unsafe.Pointer(&sc) scPointer := unsafe.Pointer(&sc)
_, _, errno = unix.RawSyscall( _, _, errno = unix.RawSyscall(
sys_CONNECT, sysConnect,
uintptr(fd), uintptr(fd),
uintptr(scPointer), uintptr(scPointer),
uintptr(sockaddrCtlSize), uintptr(sockaddrCtlSize),
@ -151,7 +172,6 @@ func OpenTunDevice(tunAddress string, autoRoute bool) (TunDevice, error) {
} }
tun, err := CreateTUNFromFile(os.NewFile(uintptr(fd), ""), mtu, tunAddress, autoRoute) tun, err := CreateTUNFromFile(os.NewFile(uintptr(fd), ""), mtu, tunAddress, autoRoute)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -271,7 +291,6 @@ func (t *tunDarwin) Close() error {
} }
func (t *tunDarwin) getInterfaceMtu() (int, error) { func (t *tunDarwin) getInterfaceMtu() (int, error) {
// open datagram socket // open datagram socket
fd, err := unix.Socket( fd, err := unix.Socket(
@ -279,7 +298,6 @@ func (t *tunDarwin) getInterfaceMtu() (int, error) {
unix.SOCK_DGRAM, unix.SOCK_DGRAM,
0, 0,
) )
if err != nil { if err != nil {
return 0, err return 0, err
} }
@ -291,7 +309,7 @@ func (t *tunDarwin) getInterfaceMtu() (int, error) {
var ifr [64]byte var ifr [64]byte
copy(ifr[:], t.name) copy(ifr[:], t.name)
_, _, errno := unix.Syscall( _, _, errno := unix.Syscall(
sys_IOCTL, sysIoctl,
uintptr(fd), uintptr(fd),
uintptr(unix.SIOCGIFMTU), uintptr(unix.SIOCGIFMTU),
uintptr(unsafe.Pointer(&ifr[0])), uintptr(unsafe.Pointer(&ifr[0])),
@ -312,7 +330,7 @@ func (t *tunDarwin) getName() (string, error) {
var errno syscall.Errno var errno syscall.Errno
t.operateOnFd(func(fd uintptr) { t.operateOnFd(func(fd uintptr) {
_, _, errno = unix.Syscall6( _, _, errno = unix.Syscall6(
sys_GETSOCKOPT, sysGetsockopt,
fd, fd,
2, /* #define SYSPROTO_CONTROL 2 */ 2, /* #define SYSPROTO_CONTROL 2 */
2, /* #define UTUN_OPT_IFNAME 2 */ 2, /* #define UTUN_OPT_IFNAME 2 */
@ -335,7 +353,6 @@ func (t *tunDarwin) setMTU(n int) error {
unix.SOCK_DGRAM, unix.SOCK_DGRAM,
0, 0,
) )
if err != nil { if err != nil {
return err return err
} }
@ -348,7 +365,7 @@ func (t *tunDarwin) setMTU(n int) error {
copy(ifr[:], t.name) copy(ifr[:], t.name)
*(*uint32)(unsafe.Pointer(&ifr[unix.IFNAMSIZ])) = uint32(n) *(*uint32)(unsafe.Pointer(&ifr[unix.IFNAMSIZ])) = uint32(n)
_, _, errno := unix.Syscall( _, _, errno := unix.Syscall(
sys_IOCTL, sysIoctl,
uintptr(fd), uintptr(fd),
uintptr(unix.SIOCSIFMTU), uintptr(unix.SIOCSIFMTU),
uintptr(unsafe.Pointer(&ifr[0])), uintptr(unsafe.Pointer(&ifr[0])),
@ -389,32 +406,22 @@ func (t *tunDarwin) setTunAddress(addr net.IP) error {
} }
defer syscall.Close(fd4) defer syscall.Close(fd4)
// https://github.com/apple/darwin-xnu/blob/a449c6a3b8014d9406c2ddbdc81795da24aa7443/bsd/sys/sockio.h#L107
// https://github.com/apple/darwin-xnu/blob/a449c6a3b8014d9406c2ddbdc81795da24aa7443/bsd/net/if.h#L570-L575
// https://man.openbsd.org/netintro.4#SIOCAIFADDR
type aliasreq struct {
ifra_name [unix.IFNAMSIZ]byte
ifra_addr unix.RawSockaddrInet4
ifra_dstaddr unix.RawSockaddrInet4
ifra_mask unix.RawSockaddrInet4
}
var ip4 [4]byte var ip4 [4]byte
copy(ip4[:], addr.To4()) copy(ip4[:], addr.To4())
ip4mask := [4]byte{255, 255, 0, 0} ip4mask := [4]byte{255, 255, 0, 0}
ifra4 := aliasreq{ ifra4 := aliasreq{
ifra_name: ifr, ifraName: ifr,
ifra_addr: unix.RawSockaddrInet4{ ifraAddr: unix.RawSockaddrInet4{
Len: unix.SizeofSockaddrInet4, Len: unix.SizeofSockaddrInet4,
Family: unix.AF_INET, Family: unix.AF_INET,
Addr: ip4, Addr: ip4,
}, },
ifra_dstaddr: unix.RawSockaddrInet4{ ifraDstaddr: unix.RawSockaddrInet4{
Len: unix.SizeofSockaddrInet4, Len: unix.SizeofSockaddrInet4,
Family: unix.AF_INET, Family: unix.AF_INET,
Addr: ip4, Addr: ip4,
}, },
ifra_mask: unix.RawSockaddrInet4{ ifraMask: unix.RawSockaddrInet4{
Len: unix.SizeofSockaddrInet4, Len: unix.SizeofSockaddrInet4,
Family: unix.AF_INET, Family: unix.AF_INET,
Addr: ip4mask, Addr: ip4mask,
@ -422,7 +429,7 @@ func (t *tunDarwin) setTunAddress(addr net.IP) error {
} }
if _, _, errno := unix.Syscall( if _, _, errno := unix.Syscall(
sys_IOCTL, sysIoctl,
uintptr(fd4), uintptr(fd4),
uintptr(unix.SIOCAIFADDR), uintptr(unix.SIOCAIFADDR),
uintptr(unsafe.Pointer(&ifra4)), uintptr(unsafe.Pointer(&ifra4)),
@ -447,42 +454,24 @@ func (t *tunDarwin) attachLinkLocal() error {
return err return err
} }
defer syscall.Close(fd6) defer syscall.Close(fd6)
// SIOCAIFADDR_IN6
// https://github.com/apple/darwin-xnu/blob/a449c6a3b8014d9406c2ddbdc81795da24aa7443/bsd/netinet6/in6_var.h#L114-L119
// https://opensource.apple.com/source/network_cmds/network_cmds-543.260.3/
type in6_addrlifetime struct {
//ia6t_expire uint64
//ia6t_preferred uint64
//ia6t_vltime uint32
//ia6t_pltime uint32
}
// https://github.com/apple/darwin-xnu/blob/a449c6a3b8014d9406c2ddbdc81795da24aa7443/bsd/netinet6/in6_var.h#L336-L343
// https://github.com/apple/darwin-xnu/blob/a449c6a3b8014d9406c2ddbdc81795da24aa7443/bsd/netinet6/in6.h#L174-L181
type in6_aliasreq struct {
ifra_name [unix.IFNAMSIZ]byte
ifra_addr unix.RawSockaddrInet6
ifra_dstaddr unix.RawSockaddrInet6
ifra_prefixmask unix.RawSockaddrInet6
ifra_flags int32
ifra_lifetime in6_addrlifetime
}
// Attach link-local address // Attach link-local address
ifra6 := in6_aliasreq{ ifra6 := in6Aliasreq{
ifra_name: ifr, ifraName: ifr,
} }
if _, _, errno := unix.Syscall( if _, _, errno := unix.Syscall(
sys_IOCTL, sysIoctl,
uintptr(fd6), uintptr(fd6),
uintptr(_SIOCPROTOATTACH_IN6), uintptr(siocprotoattachIn6),
uintptr(unsafe.Pointer(&ifra6)), uintptr(unsafe.Pointer(&ifra6)),
); errno != 0 { ); errno != 0 {
return fmt.Errorf("failed to attach link-local address on %s: SIOCPROTOATTACH_IN6 %v", t.name, errno) return fmt.Errorf("failed to attach link-local address on %s: SIOCPROTOATTACH_IN6 %v", t.name, errno)
} }
if _, _, errno := unix.Syscall( if _, _, errno := unix.Syscall(
sys_IOCTL, sysIoctl,
uintptr(fd6), uintptr(fd6),
uintptr(_SIOCLL_START), uintptr(siocllStart),
uintptr(unsafe.Pointer(&ifra6)), uintptr(unsafe.Pointer(&ifra6)),
); errno != 0 { ); errno != 0 {
return fmt.Errorf("failed to set ipv6 address on %s: SIOCLL_START %v", t.name, errno) return fmt.Errorf("failed to set ipv6 address on %s: SIOCLL_START %v", t.name, errno)

View file

@ -0,0 +1,30 @@
package commons
import (
"github.com/Dreamacro/clash/component/resolver"
D "github.com/miekg/dns"
)
func RelayDnsPacket(payload []byte) ([]byte, error) {
msg := &D.Msg{}
if err := msg.Unpack(payload); err != nil {
return nil, err
}
r, err := resolver.ServeMsg(msg)
if err != nil {
return nil, err
}
for _, ans := range r.Answer {
header := ans.Header()
if header.Class == D.ClassINET && (header.Rrtype == D.TypeA || header.Rrtype == D.TypeAAAA) {
header.Ttl = 1
}
}
r.SetRcode(msg, r.Rcode)
r.Compress = true
return r.Pack()
}

View file

@ -48,7 +48,6 @@ type gvisorAdapter struct {
// GvisorAdapter create GvisorAdapter // GvisorAdapter create GvisorAdapter
func NewAdapter(device dev.TunDevice, conf config.Tun, tunAddress string, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (ipstack.TunAdapter, error) { func NewAdapter(device dev.TunDevice, conf config.Tun, tunAddress string, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (ipstack.TunAdapter, error) {
ipstack := stack.New(stack.Options{ ipstack := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocolFactory{ipv4.NewProtocol, ipv6.NewProtocol}, NetworkProtocols: []stack.NetworkProtocolFactory{ipv4.NewProtocol, ipv6.NewProtocol},
TransportProtocols: []stack.TransportProtocolFactory{tcp.NewProtocol, udp.NewProtocol}, TransportProtocols: []stack.TransportProtocolFactory{tcp.NewProtocol, udp.NewProtocol},
@ -180,7 +179,6 @@ func (t *gvisorAdapter) AsLinkEndpoint() (result stack.LinkEndpoint, err error)
} }
mtu, err := t.device.MTU() mtu, err := t.device.MTU()
if err != nil { if err != nil {
return nil, errors.New("unable to get device mtu") return nil, errors.New("unable to get device mtu")
} }
@ -195,6 +193,7 @@ func (t *gvisorAdapter) AsLinkEndpoint() (result stack.LinkEndpoint, err error)
n, err := t.device.Read(packet) n, err := t.device.Read(packet)
if err != nil && !t.device.IsClose() { if err != nil && !t.device.IsClose() {
log.Errorln("can not read from tun: %v", err) log.Errorln("can not read from tun: %v", err)
continue
} }
var p tcpip.NetworkProtocolNumber var p tcpip.NetworkProtocolNumber
switch header.IPVersion(packet) { switch header.IPVersion(packet) {

View file

@ -73,7 +73,6 @@ func (e *dnsEndpoint) Close() {
} }
func (e *dnsEndpoint) Wait() { func (e *dnsEndpoint) Wait() {
} }
func (e *dnsEndpoint) HandleError(transErr stack.TransportError, pkt *stack.PacketBuffer) { func (e *dnsEndpoint) HandleError(transErr stack.TransportError, pkt *stack.PacketBuffer) {
@ -102,13 +101,16 @@ func (w *dnsResponseWriter) WriteMsg(msg *D.Msg) error {
_, err = w.Write(b) _, err = w.Write(b)
return err return err
} }
func (w *dnsResponseWriter) TsigStatus() error { func (w *dnsResponseWriter) TsigStatus() error {
// Unsupported // Unsupported
return nil return nil
} }
func (w *dnsResponseWriter) TsigTimersOnly(bool) { func (w *dnsResponseWriter) TsigTimersOnly(bool) {
// Unsupported // Unsupported
} }
func (w *dnsResponseWriter) Hijack() { func (w *dnsResponseWriter) Hijack() {
// Unsupported // Unsupported
} }
@ -128,7 +130,6 @@ func (w *dnsResponseWriter) Close() error {
// CreateDNSServer create a dns server on given netstack // CreateDNSServer create a dns server on given netstack
func CreateDNSServer(s *stack.Stack, resolver *dns.Resolver, mapper *dns.ResolverEnhancer, ip net.IP, port int, nicID tcpip.NICID) (*DNSServer, error) { func CreateDNSServer(s *stack.Stack, resolver *dns.Resolver, mapper *dns.ResolverEnhancer, ip net.IP, port int, nicID tcpip.NICID) (*DNSServer, error) {
var v4 bool var v4 bool
var err error var err error

View file

@ -53,7 +53,6 @@ func (c *fakeConn) Close() error {
} }
func (c *fakeConn) Drop() { func (c *fakeConn) Drop() {
} }
func (c *fakeConn) FakeIP() bool { func (c *fakeConn) FakeIP() bool {

View file

@ -6,10 +6,7 @@ import (
"net" "net"
"time" "time"
"github.com/Dreamacro/clash/component/resolver" D "github.com/Dreamacro/clash/listener/tun/ipstack/commons"
D "github.com/miekg/dns"
"github.com/kr328/tun2socket/binding" "github.com/kr328/tun2socket/binding"
"github.com/kr328/tun2socket/redirect" "github.com/kr328/tun2socket/redirect"
) )
@ -26,8 +23,7 @@ func shouldHijackDns(dnsAddr binding.Address, targetAddr binding.Address) bool {
func hijackUDPDns(pkt []byte, ep *binding.Endpoint, sender redirect.UDPSender) { func hijackUDPDns(pkt []byte, ep *binding.Endpoint, sender redirect.UDPSender) {
go func() { go func() {
answer, err := relayDnsPacket(pkt) answer, err := D.RelayDnsPacket(pkt)
if err != nil { if err != nil {
return return
} }
@ -41,7 +37,9 @@ func hijackUDPDns(pkt []byte, ep *binding.Endpoint, sender redirect.UDPSender) {
func hijackTCPDns(conn net.Conn) { func hijackTCPDns(conn net.Conn) {
go func() { go func() {
defer conn.Close() defer func(conn net.Conn) {
_ = conn.Close()
}(conn)
for { for {
if err := conn.SetReadDeadline(time.Now().Add(defaultDnsReadTimeout)); err != nil { if err := conn.SetReadDeadline(time.Now().Add(defaultDnsReadTimeout)); err != nil {
@ -60,7 +58,7 @@ func hijackTCPDns(conn net.Conn) {
return return
} }
rb, err := relayDnsPacket(data) rb, err := D.RelayDnsPacket(data)
if err != nil { if err != nil {
continue continue
} }
@ -75,27 +73,3 @@ func hijackTCPDns(conn net.Conn) {
} }
}() }()
} }
func relayDnsPacket(payload []byte) ([]byte, error) {
msg := &D.Msg{}
if err := msg.Unpack(payload); err != nil {
return nil, err
}
r, err := resolver.ServeMsg(msg)
if err != nil {
return nil, err
}
for _, ans := range r.Answer {
header := ans.Header()
if header.Class == D.ClassINET && (header.Rrtype == D.TypeA || header.Rrtype == D.TypeAAAA) {
header.Ttl = 1
}
}
r.SetRcode(msg, r.Rcode)
r.Compress = true
return r.Pack()
}

View file

@ -16,12 +16,18 @@ func handleTCP(conn net.Conn, endpoint *binding.Endpoint, tcpIn chan<- C.ConnCon
Port: int(endpoint.Source.Port), Port: int(endpoint.Source.Port),
Zone: "", Zone: "",
} }
dst := &net.TCPAddr{ dst := &net.TCPAddr{
IP: endpoint.Target.IP, IP: endpoint.Target.IP,
Port: int(endpoint.Target.Port), Port: int(endpoint.Target.Port),
Zone: "", Zone: "",
} }
addrType := C.AtypIPv4
if dst.IP.To4() == nil {
addrType = C.AtypIPv6
}
metadata := &C.Metadata{ metadata := &C.Metadata{
NetWork: C.TCP, NetWork: C.TCP,
Type: C.TUN, Type: C.TUN,
@ -29,9 +35,12 @@ func handleTCP(conn net.Conn, endpoint *binding.Endpoint, tcpIn chan<- C.ConnCon
DstIP: dst.IP, DstIP: dst.IP,
SrcPort: strconv.Itoa(src.Port), SrcPort: strconv.Itoa(src.Port),
DstPort: strconv.Itoa(dst.Port), DstPort: strconv.Itoa(dst.Port),
AddrType: C.AtypIPv4, AddrType: addrType,
Host: "", Host: "",
} }
//if c, ok := conn.(*net.TCPConn); ok {
// c.SetKeepAlive(true)
//}
tcpIn <- context.NewConnContext(conn, metadata) tcpIn <- context.NewConnContext(conn, metadata)
} }

View file

@ -26,7 +26,6 @@ type systemAdapter struct {
} }
func NewAdapter(device dev.TunDevice, conf config.Tun, mtu int, gateway, mirror string, onStop func(), tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (ipstack.TunAdapter, error) { func NewAdapter(device dev.TunDevice, conf config.Tun, mtu int, gateway, mirror string, onStop func(), tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (ipstack.TunAdapter, error) {
adapter := &systemAdapter{ adapter := &systemAdapter{
device: device, device: device,
stackName: conf.Stack, stackName: conf.Stack,
@ -37,8 +36,6 @@ func NewAdapter(device dev.TunDevice, conf config.Tun, mtu int, gateway, mirror
adapter.lock.Lock() adapter.lock.Lock()
defer adapter.lock.Unlock() defer adapter.lock.Unlock()
//adapter.stopLocked()
dnsHost, dnsPort, err := net.SplitHostPort(conf.DNSListen) dnsHost, dnsPort, err := net.SplitHostPort(conf.DNSListen)
if err != nil { if err != nil {
return nil, err return nil, err
@ -63,10 +60,7 @@ func NewAdapter(device dev.TunDevice, conf config.Tun, mtu int, gateway, mirror
t.SetTCPHandler(func(conn net.Conn, endpoint *binding.Endpoint) { t.SetTCPHandler(func(conn net.Conn, endpoint *binding.Endpoint) {
if shouldHijackDns(dnsAddr, endpoint.Target) { if shouldHijackDns(dnsAddr, endpoint.Target) {
hijackTCPDns(conn) hijackTCPDns(conn)
log.Debugln("[TUN] hijack dns tcp: %s:%d", endpoint.Target.IP.String(), endpoint.Target.Port)
if log.Level() == log.DEBUG {
log.Debugln("[TUN] hijack dns tcp: %s:%d", endpoint.Target.IP.String(), endpoint.Target.Port)
}
return return
} }
@ -75,10 +69,7 @@ func NewAdapter(device dev.TunDevice, conf config.Tun, mtu int, gateway, mirror
t.SetUDPHandler(func(payload []byte, endpoint *binding.Endpoint, sender redirect.UDPSender) { t.SetUDPHandler(func(payload []byte, endpoint *binding.Endpoint, sender redirect.UDPSender) {
if shouldHijackDns(dnsAddr, endpoint.Target) { if shouldHijackDns(dnsAddr, endpoint.Target) {
hijackUDPDns(payload, endpoint, sender) hijackUDPDns(payload, endpoint, sender)
log.Debugln("[TUN] hijack dns udp: %s:%d", endpoint.Target.IP.String(), endpoint.Target.Port)
if log.Level() == log.DEBUG {
log.Debugln("[TUN] hijack dns udp: %s:%d", endpoint.Target.IP.String(), endpoint.Target.Port)
}
return return
} }

View file

@ -2,7 +2,7 @@ package memconservative
import ( import (
"fmt" "fmt"
"io/ioutil" "os"
"strings" "strings"
C "github.com/Dreamacro/clash/constant" C "github.com/Dreamacro/clash/constant"
@ -54,7 +54,7 @@ func (g GeoIPCache) Unmarshal(filename, code string) (*router.GeoIP, error) {
case errFailedToReadBytes, errFailedToReadExpectedLenBytes, case errFailedToReadBytes, errFailedToReadExpectedLenBytes,
errInvalidGeodataFile, errInvalidGeodataVarintLength: errInvalidGeodataFile, errInvalidGeodataVarintLength:
log.Warnln("failed to decode geoip file: %s%s", filename, ", fallback to the original ReadFile method") log.Warnln("failed to decode geoip file: %s%s", filename, ", fallback to the original ReadFile method")
geoipBytes, err = ioutil.ReadFile(asset) geoipBytes, err = os.ReadFile(asset)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -119,7 +119,7 @@ func (g GeoSiteCache) Unmarshal(filename, code string) (*router.GeoSite, error)
case errFailedToReadBytes, errFailedToReadExpectedLenBytes, case errFailedToReadBytes, errFailedToReadExpectedLenBytes,
errInvalidGeodataFile, errInvalidGeodataVarintLength: errInvalidGeodataFile, errInvalidGeodataVarintLength:
log.Warnln("failed to decode geoip file: %s%s", filename, ", fallback to the original ReadFile method") log.Warnln("failed to decode geoip file: %s%s", filename, ", fallback to the original ReadFile method")
geositeBytes, err = ioutil.ReadFile(asset) geositeBytes, err = os.ReadFile(asset)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -2,7 +2,6 @@ package router
import ( import (
"fmt" "fmt"
"net"
"strings" "strings"
"github.com/Dreamacro/clash/rule/geodata/strmatcher" "github.com/Dreamacro/clash/rule/geodata/strmatcher"
@ -69,44 +68,3 @@ func NewDomainMatcher(domains []*Domain) (*DomainMatcher, error) {
func (m *DomainMatcher) ApplyDomain(domain string) bool { func (m *DomainMatcher) ApplyDomain(domain string) bool {
return len(m.matchers.Match(strings.ToLower(domain))) > 0 return len(m.matchers.Match(strings.ToLower(domain))) > 0
} }
type MultiGeoIPMatcher struct {
matchers []*GeoIPMatcher
}
func NewMultiGeoIPMatcher(geoips []*GeoIP) (*MultiGeoIPMatcher, error) {
var matchers []*GeoIPMatcher
for _, geoip := range geoips {
matcher, err := globalGeoIPContainer.Add(geoip)
if err != nil {
return nil, err
}
matchers = append(matchers, matcher)
}
matcher := &MultiGeoIPMatcher{
matchers: matchers,
}
return matcher, nil
}
func (m *MultiGeoIPMatcher) ApplyIp(ip net.IP) bool {
for _, matcher := range m.matchers {
if matcher.Match(ip) {
return true
}
}
return false
}
func NewGeoIPMatcher(geoip *GeoIP) (*GeoIPMatcher, error) {
matcher, err := globalGeoIPContainer.Add(geoip)
if err != nil {
return nil, err
}
return matcher, nil
}

View file

@ -1,243 +0,0 @@
package router
import (
"encoding/binary"
"fmt"
"net"
"sort"
)
// CIDRList is an alias of []*CIDR to provide sort.Interface.
type CIDRList []*CIDR
// Len implements sort.Interface.
func (l *CIDRList) Len() int {
return len(*l)
}
// Less implements sort.Interface.
func (l *CIDRList) Less(i int, j int) bool {
ci := (*l)[i]
cj := (*l)[j]
if len(ci.Ip) < len(cj.Ip) {
return true
}
if len(ci.Ip) > len(cj.Ip) {
return false
}
for k := 0; k < len(ci.Ip); k++ {
if ci.Ip[k] < cj.Ip[k] {
return true
}
if ci.Ip[k] > cj.Ip[k] {
return false
}
}
return ci.Prefix < cj.Prefix
}
// Swap implements sort.Interface.
func (l *CIDRList) Swap(i int, j int) {
(*l)[i], (*l)[j] = (*l)[j], (*l)[i]
}
type ipv6 struct {
a uint64
b uint64
}
type GeoIPMatcher struct {
countryCode string
reverseMatch bool
ip4 []uint32
prefix4 []uint8
ip6 []ipv6
prefix6 []uint8
}
func normalize4(ip uint32, prefix uint8) uint32 {
return (ip >> (32 - prefix)) << (32 - prefix)
}
func normalize6(ip ipv6, prefix uint8) ipv6 {
if prefix <= 64 {
ip.a = (ip.a >> (64 - prefix)) << (64 - prefix)
ip.b = 0
} else {
ip.b = (ip.b >> (128 - prefix)) << (128 - prefix)
}
return ip
}
func (m *GeoIPMatcher) Init(cidrs []*CIDR) error {
ip4Count := 0
ip6Count := 0
for _, cidr := range cidrs {
ip := cidr.Ip
switch len(ip) {
case 4:
ip4Count++
case 16:
ip6Count++
default:
return fmt.Errorf("unexpect ip length: %d", len(ip))
}
}
cidrList := CIDRList(cidrs)
sort.Sort(&cidrList)
m.ip4 = make([]uint32, 0, ip4Count)
m.prefix4 = make([]uint8, 0, ip4Count)
m.ip6 = make([]ipv6, 0, ip6Count)
m.prefix6 = make([]uint8, 0, ip6Count)
for _, cidr := range cidrs {
ip := cidr.Ip
prefix := uint8(cidr.Prefix)
switch len(ip) {
case 4:
m.ip4 = append(m.ip4, normalize4(binary.BigEndian.Uint32(ip), prefix))
m.prefix4 = append(m.prefix4, prefix)
case 16:
ip6 := ipv6{
a: binary.BigEndian.Uint64(ip[0:8]),
b: binary.BigEndian.Uint64(ip[8:16]),
}
ip6 = normalize6(ip6, prefix)
m.ip6 = append(m.ip6, ip6)
m.prefix6 = append(m.prefix6, prefix)
}
}
return nil
}
func (m *GeoIPMatcher) SetReverseMatch(isReverseMatch bool) {
m.reverseMatch = isReverseMatch
}
func (m *GeoIPMatcher) match4(ip uint32) bool {
if len(m.ip4) == 0 {
return false
}
if ip < m.ip4[0] {
return false
}
size := uint32(len(m.ip4))
l := uint32(0)
r := size
for l < r {
x := ((l + r) >> 1)
if ip < m.ip4[x] {
r = x
continue
}
nip := normalize4(ip, m.prefix4[x])
if nip == m.ip4[x] {
return true
}
l = x + 1
}
return l > 0 && normalize4(ip, m.prefix4[l-1]) == m.ip4[l-1]
}
func less6(a ipv6, b ipv6) bool {
return a.a < b.a || (a.a == b.a && a.b < b.b)
}
func (m *GeoIPMatcher) match6(ip ipv6) bool {
if len(m.ip6) == 0 {
return false
}
if less6(ip, m.ip6[0]) {
return false
}
size := uint32(len(m.ip6))
l := uint32(0)
r := size
for l < r {
x := (l + r) / 2
if less6(ip, m.ip6[x]) {
r = x
continue
}
if normalize6(ip, m.prefix6[x]) == m.ip6[x] {
return true
}
l = x + 1
}
return l > 0 && normalize6(ip, m.prefix6[l-1]) == m.ip6[l-1]
}
// Match returns true if the given ip is included by the GeoIP.
func (m *GeoIPMatcher) Match(ip net.IP) bool {
switch len(ip) {
case 4:
if m.reverseMatch {
return !m.match4(binary.BigEndian.Uint32(ip))
}
return m.match4(binary.BigEndian.Uint32(ip))
case 16:
if m.reverseMatch {
return !m.match6(ipv6{
a: binary.BigEndian.Uint64(ip[0:8]),
b: binary.BigEndian.Uint64(ip[8:16]),
})
}
return m.match6(ipv6{
a: binary.BigEndian.Uint64(ip[0:8]),
b: binary.BigEndian.Uint64(ip[8:16]),
})
default:
return false
}
}
// GeoIPMatcherContainer is a container for GeoIPMatchers. It keeps unique copies of GeoIPMatcher by country code.
type GeoIPMatcherContainer struct {
matchers []*GeoIPMatcher
}
// Add adds a new GeoIP set into the container.
// If the country code of GeoIP is not empty, GeoIPMatcherContainer will try to find an existing one, instead of adding a new one.
func (c *GeoIPMatcherContainer) Add(geoip *GeoIP) (*GeoIPMatcher, error) {
if len(geoip.CountryCode) > 0 {
for _, m := range c.matchers {
if m.countryCode == geoip.CountryCode && m.reverseMatch == geoip.ReverseMatch {
return m, nil
}
}
}
m := &GeoIPMatcher{
countryCode: geoip.CountryCode,
reverseMatch: geoip.ReverseMatch,
}
if err := m.Init(geoip.Cidr); err != nil {
return nil, err
}
if len(geoip.CountryCode) > 0 {
c.matchers = append(c.matchers, m)
}
return m, nil
}
var globalGeoIPContainer GeoIPMatcherContainer

View file

@ -2,7 +2,7 @@ package standard
import ( import (
"fmt" "fmt"
"io/ioutil" "io"
"os" "os"
"strings" "strings"
@ -19,7 +19,7 @@ func ReadFile(path string) ([]byte, error) {
} }
defer reader.Close() defer reader.Close()
return ioutil.ReadAll(reader) return io.ReadAll(reader)
} }
func ReadAsset(file string) ([]byte, error) { func ReadAsset(file string) ([]byte, error) {

View file

@ -1,22 +1,17 @@
package rules package rules
import ( import (
"fmt"
"strings" "strings"
"github.com/Dreamacro/clash/component/mmdb"
C "github.com/Dreamacro/clash/constant" C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/log"
"github.com/Dreamacro/clash/rule/geodata"
"github.com/Dreamacro/clash/rule/geodata/router"
_ "github.com/Dreamacro/clash/rule/geodata/standard"
) )
type GEOIP struct { type GEOIP struct {
country string country string
adapter string adapter string
noResolveIP bool noResolveIP bool
ruleExtra *C.RuleExtra ruleExtra *C.RuleExtra
geoIPMatcher *router.GeoIPMatcher
} }
func (g *GEOIP) RuleType() C.RuleType { func (g *GEOIP) RuleType() C.RuleType {
@ -29,10 +24,11 @@ func (g *GEOIP) Match(metadata *C.Metadata) bool {
return false return false
} }
if strings.EqualFold(g.country, "LAN") { if strings.EqualFold(g.country, "LAN") || C.TunBroadcastAddr.Equal(ip) {
return ip.IsPrivate() return ip.IsPrivate()
} }
return g.geoIPMatcher.Match(ip) record, _ := mmdb.Instance().Country(ip)
return strings.EqualFold(record.Country.IsoCode, g.country)
} }
func (g *GEOIP) Adapter() string { func (g *GEOIP) Adapter() string {
@ -51,39 +47,16 @@ func (g *GEOIP) RuleExtra() *C.RuleExtra {
return g.ruleExtra return g.ruleExtra
} }
func (g *GEOIP) GetCountry() string {
return g.country
}
func NewGEOIP(country string, adapter string, noResolveIP bool, ruleExtra *C.RuleExtra) (*GEOIP, error) { func NewGEOIP(country string, adapter string, noResolveIP bool, ruleExtra *C.RuleExtra) (*GEOIP, error) {
geoLoaderName := "standard"
//geoLoaderName := "memconservative"
geoLoader, err := geodata.GetGeoDataLoader(geoLoaderName)
if err != nil {
return nil, fmt.Errorf("[GeoIP] %s", err.Error())
}
records, err := geoLoader.LoadGeoIP(strings.ReplaceAll(country, "!", ""))
if err != nil {
return nil, fmt.Errorf("[GeoIP] %s", err.Error())
}
geoIP := &router.GeoIP{
CountryCode: country,
Cidr: records,
ReverseMatch: strings.Contains(country, "!"),
}
geoIPMatcher, err := router.NewGeoIPMatcher(geoIP)
if err != nil {
return nil, fmt.Errorf("[GeoIP] %s", err.Error())
}
log.Infoln("Start initial GeoIP rule %s => %s, records: %d", country, adapter, len(records))
geoip := &GEOIP{ geoip := &GEOIP{
country: country, country: country,
adapter: adapter, adapter: adapter,
noResolveIP: noResolveIP, noResolveIP: noResolveIP,
ruleExtra: ruleExtra, ruleExtra: ruleExtra,
geoIPMatcher: geoIPMatcher,
} }
return geoip, nil return geoip, nil

View file

@ -49,24 +49,24 @@ func (gs *GEOSITE) RuleExtra() *C.RuleExtra {
func NewGEOSITE(country string, adapter string, ruleExtra *C.RuleExtra) (*GEOSITE, error) { func NewGEOSITE(country string, adapter string, ruleExtra *C.RuleExtra) (*GEOSITE, error) {
geoLoaderName := "standard" geoLoaderName := "standard"
//geoLoaderName := "memconservative"
geoLoader, err := geodata.GetGeoDataLoader(geoLoaderName) geoLoader, err := geodata.GetGeoDataLoader(geoLoaderName)
if err != nil { if err != nil {
return nil, fmt.Errorf("[GeoSite] %s", err.Error()) return nil, fmt.Errorf("load GeoSite data error, %s", err.Error())
} }
domains, err := geoLoader.LoadGeoSite(country) domains, err := geoLoader.LoadGeoSite(country)
if err != nil { if err != nil {
return nil, fmt.Errorf("[GeoSite] %s", err.Error()) return nil, fmt.Errorf("load GeoSite data error, %s", err.Error())
} }
//linear: linear algorithm /**
//matcher, err := router.NewDomainMatcher(domains) linear: linear algorithm
matcher, err := router.NewDomainMatcher(domains)
//mphminimal perfect hash algorithm mphminimal perfect hash algorithm
*/
matcher, err := router.NewMphMatcherGroup(domains) matcher, err := router.NewMphMatcherGroup(domains)
if err != nil { if err != nil {
return nil, fmt.Errorf("[GeoSite] %s", err.Error()) return nil, fmt.Errorf("load GeoSite data error, %s", err.Error())
} }
log.Infoln("Start initial GeoSite rule %s => %s, records: %d", country, adapter, len(domains)) log.Infoln("Start initial GeoSite rule %s => %s, records: %d", country, adapter, len(domains))

View file

@ -6,7 +6,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"net" "net"
"github.com/gofrs/uuid" "github.com/gofrs/uuid"
@ -87,7 +86,7 @@ func (vc *Conn) recvResponse() error {
length := int64(buf[0]) length := int64(buf[0])
if length != 0 { // addon data length > 0 if length != 0 { // addon data length > 0
io.CopyN(ioutil.Discard, vc.Conn, length) // just discard io.CopyN(io.Discard, vc.Conn, length) // just discard
} }
return nil return nil

View file

@ -34,8 +34,6 @@ var (
udpTimeout = 60 * time.Second udpTimeout = 60 * time.Second
preProcessCacheFinder, _ = R.NewProcess("", "", nil) preProcessCacheFinder, _ = R.NewProcess("", "", nil)
tunBroadcastAddr = net.IPv4(198, 18, 255, 255)
) )
func init() { func init() {
@ -143,7 +141,7 @@ func preHandleMetadata(metadata *C.Metadata) error {
// redir-host should lookup the hosts // redir-host should lookup the hosts
metadata.DstIP = node.Data.(net.IP) metadata.DstIP = node.Data.(net.IP)
} }
} else if resolver.IsFakeIP(metadata.DstIP) && !tunBroadcastAddr.Equal(metadata.DstIP) { } else if resolver.IsFakeIP(metadata.DstIP) && !C.TunBroadcastAddr.Equal(metadata.DstIP) {
return fmt.Errorf("fake DNS record %s missing", metadata.DstIP) return fmt.Errorf("fake DNS record %s missing", metadata.DstIP)
} }
} }
@ -235,7 +233,7 @@ func handleUDPConn(packet *inbound.PacketAdapter) {
switch true { switch true {
case rule != nil: case rule != nil:
log.Infoln("[UDP] %s(%s) --> %s:%s match %s(%s) using %s", metadata.SourceAddress(), metadata.Process, metadata.RemoteAddress(), metadata.DstPort, rule.RuleType().String(), rule.Payload(), rawPc.Chains().String()) log.Infoln("[UDP] %s(%s) --> %s match %s(%s) using %s", metadata.SourceAddress(), metadata.Process, metadata.RemoteAddress(), rule.RuleType().String(), rule.Payload(), rawPc.Chains().String())
case mode == Global: case mode == Global:
log.Infoln("[UDP] %s(%s) --> %s using GLOBAL", metadata.SourceAddress(), metadata.Process, metadata.RemoteAddress()) log.Infoln("[UDP] %s(%s) --> %s using GLOBAL", metadata.SourceAddress(), metadata.Process, metadata.RemoteAddress())
case mode == Direct: case mode == Direct:
@ -285,7 +283,7 @@ func handleTCPConn(ctx C.ConnContext) {
switch true { switch true {
case rule != nil: case rule != nil:
log.Infoln("[TCP] %s(%s) --> %s:%s match %s(%s) using %s", metadata.SourceAddress(), metadata.Process, metadata.RemoteAddress(), metadata.DstPort, rule.RuleType().String(), rule.Payload(), remoteConn.Chains().String()) log.Infoln("[TCP] %s(%s) --> %s match %s(%s) using %s", metadata.SourceAddress(), metadata.Process, metadata.RemoteAddress(), rule.RuleType().String(), rule.Payload(), remoteConn.Chains().String())
case mode == Global: case mode == Global:
log.Infoln("[TCP] %s(%s) --> %s using GLOBAL", metadata.SourceAddress(), metadata.Process, metadata.RemoteAddress()) log.Infoln("[TCP] %s(%s) --> %s using GLOBAL", metadata.SourceAddress(), metadata.Process, metadata.RemoteAddress())
case mode == Direct: case mode == Direct: