mirror of
https://github.com/e1732a364fed/v2ray_simple.git
synced 2025-10-23 16:53:14 +08:00
修订代码,示例;添加geosite分流,域名现已支持full,sub,regex,geosite,match
This commit is contained in:
44
configs.go
44
configs.go
@@ -21,6 +21,33 @@ func init() {
|
|||||||
flag.IntVar(&jsonMode, "jm", 0, "json mode, 0:verysimple mode; 1: v2ray mode(not implemented yet)")
|
flag.IntVar(&jsonMode, "jm", 0, "json mode, 0:verysimple mode; 1: v2ray mode(not implemented yet)")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//mainfallback, dnsConf, routePolicy
|
||||||
|
func loadCommonComponentsFromStandardConf() {
|
||||||
|
|
||||||
|
if len(standardConf.Fallbacks) != 0 {
|
||||||
|
mainFallback = httpLayer.NewClassicFallbackFromConfList(standardConf.Fallbacks)
|
||||||
|
}
|
||||||
|
|
||||||
|
if dnsConf := standardConf.DnsConf; dnsConf != nil {
|
||||||
|
dnsMachine = proxy.LoadDnsMachine(dnsConf)
|
||||||
|
}
|
||||||
|
|
||||||
|
hasAppLevelMyCountry := (standardConf.App != nil && standardConf.App.MyCountryISO_3166 != "")
|
||||||
|
|
||||||
|
if standardConf.Route != nil || hasAppLevelMyCountry {
|
||||||
|
|
||||||
|
netLayer.LoadMaxmindGeoipFile("")
|
||||||
|
|
||||||
|
routePolicy = netLayer.NewRoutePolicy()
|
||||||
|
if hasAppLevelMyCountry {
|
||||||
|
routePolicy.AddRouteSet(netLayer.NewRouteSetForMyCountry(standardConf.App.MyCountryISO_3166))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
proxy.LoadRulesForRoutePolicy(standardConf.Route, routePolicy)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// set conf variable, or exit the program; 还会设置mainFallback
|
// set conf variable, or exit the program; 还会设置mainFallback
|
||||||
// 先检查configFileName是否存在,存在就尝试加载文件,否则尝试 -L参数
|
// 先检查configFileName是否存在,存在就尝试加载文件,否则尝试 -L参数
|
||||||
func loadConfig() (err error) {
|
func loadConfig() (err error) {
|
||||||
@@ -37,26 +64,21 @@ func loadConfig() (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(standardConf.Fallbacks) != 0 {
|
|
||||||
mainFallback = httpLayer.NewClassicFallbackFromConfList(standardConf.Fallbacks)
|
|
||||||
|
|
||||||
}
|
|
||||||
confMode = 1
|
confMode = 1
|
||||||
|
|
||||||
|
//loglevel 和 noreadv这种会被 命令行覆盖的配置,需要直接在 loadConfig函数中先处理一遍
|
||||||
if appConf := standardConf.App; appConf != nil {
|
if appConf := standardConf.App; appConf != nil {
|
||||||
if appConf.LogLevel != nil {
|
default_uuid = appConf.DefaultUUID
|
||||||
|
|
||||||
|
if appConf.LogLevel != nil && !utils.IsFlagPassed("ll") {
|
||||||
utils.LogLevel = *appConf.LogLevel
|
utils.LogLevel = *appConf.LogLevel
|
||||||
|
|
||||||
}
|
}
|
||||||
default_uuid = appConf.DefaultUUID
|
if appConf.NoReadV && !utils.IsFlagPassed("readv") {
|
||||||
if appConf.NoReadV {
|
|
||||||
netLayer.UseReadv = false
|
netLayer.UseReadv = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if dnsConf := standardConf.DnsConf; dnsConf != nil {
|
|
||||||
|
|
||||||
dnsMachine = proxy.LoadDnsMachine(dnsConf)
|
|
||||||
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
//默认认为所有其他后缀的都是json格式,因为有时我会用 server.json.vless 这种写法
|
//默认认为所有其他后缀的都是json格式,因为有时我会用 server.json.vless 这种写法
|
||||||
|
@@ -85,7 +85,7 @@ path = "ohmygod_verysimple_is_very_simple"
|
|||||||
[[route]]
|
[[route]]
|
||||||
dialTag = "my_vless1"
|
dialTag = "my_vless1"
|
||||||
|
|
||||||
# 这个route中,我们只给了tag, 没给其它限定条件,这个是无效的,永远匹配不到。
|
# 上面这个route中,我们只给了tag, 没给其它限定条件,这个是无效的,永远匹配不到。
|
||||||
|
|
||||||
|
|
||||||
[[route]]
|
[[route]]
|
||||||
@@ -98,9 +98,9 @@ country = ["US"]
|
|||||||
|
|
||||||
|
|
||||||
# 比如这个就是 将CN国家的ip 导向自己的grpc节点
|
# 比如这个就是 将CN国家的ip 导向自己的grpc节点
|
||||||
[[route]]
|
#[[route]]
|
||||||
dialTag = "my_grpc"
|
#dialTag = "my_grpc"
|
||||||
country = ["CN"]
|
#country = ["CN"]
|
||||||
|
|
||||||
|
|
||||||
# 比如这个就是 将CN国家的ip进行直连
|
# 比如这个就是 将CN国家的ip进行直连
|
||||||
@@ -108,20 +108,31 @@ country = ["CN"]
|
|||||||
#dialTag = "direct"
|
#dialTag = "direct"
|
||||||
#country = ["CN"]
|
#country = ["CN"]
|
||||||
|
|
||||||
|
# 本示例为了测试节点可用性, 默认将直连的路由注释掉了, 如果你在CN国家并想直连CN的ip, 请取消注释上面三行. 并移除其它路由CN的route项
|
||||||
|
|
||||||
|
|
||||||
# 下面这种dialTag传入列表的用法非常简洁, 可以达到负载均衡的效果,
|
# 下面这种dialTag传入列表的用法非常简洁, 可以达到负载均衡的效果,
|
||||||
# 每次路由US国家的流量都会随机从列表中选一项
|
# 每次路由US国家的流量都会随机从列表中选一项
|
||||||
#[[route]]
|
#[[route]]
|
||||||
#dialTag = ["my_vps1","myvps2"]
|
#dialTag = ["my_vps1","myvps2"]
|
||||||
#country = ["US"]
|
#country = ["US"]
|
||||||
|
|
||||||
# 本示例为了测试节点可用性, 默认将直连的路由注释掉了, 如果你在CN国家并想直连CN的ip, 请取消注释上面三行. 并移除其它路由CN的route项
|
|
||||||
|
|
||||||
# 如果所有route均不匹配,则数据会流向 "proxy" 这个tag 的 dial,如果 没有任何dial具有 "proxy" 这个标签名,则流向第一个dial
|
# 如果所有route均不匹配,则数据会流向 "proxy" 这个tag 的 dial,如果 没有任何dial具有 "proxy" 这个标签名,则流向第一个dial
|
||||||
|
|
||||||
# 如果匹配了app.mycountry, 则数据会直接被直连.
|
# 如果匹配了app.mycountry, 则数据会直接被直连.
|
||||||
# 其它分流匹配示例:
|
# 其它分流匹配示例:
|
||||||
# ip = ["0.0.0.0/8","10.0.0.0/8","fe80::/10","10.0.0.1"]
|
# ip = ["0.0.0.0/8","10.0.0.0/8","fe80::/10","10.0.0.1"]
|
||||||
# domain = ["www.google.com","www.twitter.com"]
|
|
||||||
|
# 域名匹配完全兼容 v2ray,请参考 https://www.v2fly.org/config/routing.html#ruleobject
|
||||||
|
# domain = ["domain:www.google.com","full:www.twitter.com", "geosite:cn"]
|
||||||
|
|
||||||
|
# 比如这个就是 将CN国家的域名 导向自己的grpc节点
|
||||||
|
[[route]]
|
||||||
|
dialTag = "my_grpc"
|
||||||
|
domain = ["geosite:cn"]
|
||||||
|
|
||||||
# network = ["tcp","udp"]
|
# network = ["tcp","udp"]
|
||||||
# inTag = ["tag1","tag2"]
|
# inTag = ["tag1","tag2"]
|
||||||
# country = ["CN"]
|
# country = ["CN"]
|
||||||
|
69
main.go
69
main.go
@@ -86,16 +86,6 @@ func init() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func isFlagPassed(name string) bool {
|
|
||||||
found := false
|
|
||||||
flag.Visit(func(f *flag.Flag) {
|
|
||||||
if f.Name == name {
|
|
||||||
found = true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return found
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
@@ -123,12 +113,6 @@ func main() {
|
|||||||
defer p.Stop()
|
defer p.Stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
ll_beforeLoadConfigFile := utils.LogLevel
|
|
||||||
usereadv_beforeLoadConfigFile := netLayer.UseReadv
|
|
||||||
|
|
||||||
cmdLL_given := isFlagPassed("ll")
|
|
||||||
cmdUseReadv_given := isFlagPassed("readv")
|
|
||||||
|
|
||||||
if err := loadConfig(); err != nil && !isFlexible() {
|
if err := loadConfig(); err != nil && !isFlexible() {
|
||||||
log.Printf("no config exist, and no api server or interactive cli enabled, exiting...")
|
log.Printf("no config exist, and no api server or interactive cli enabled, exiting...")
|
||||||
os.Exit(-1)
|
os.Exit(-1)
|
||||||
@@ -136,20 +120,6 @@ func main() {
|
|||||||
|
|
||||||
netLayer.Prepare()
|
netLayer.Prepare()
|
||||||
|
|
||||||
//有点尴尬, 读取配置文件必须要用命令行参数,而配置文件里的部分配置又会覆盖部分命令行参数
|
|
||||||
|
|
||||||
if cmdLL_given && utils.LogLevel != ll_beforeLoadConfigFile {
|
|
||||||
//配置文件配置了日志等级, 但是因为 命令行给出的值优先, 所以要设回
|
|
||||||
|
|
||||||
utils.LogLevel = ll_beforeLoadConfigFile
|
|
||||||
}
|
|
||||||
|
|
||||||
if cmdUseReadv_given && netLayer.UseReadv != usereadv_beforeLoadConfigFile {
|
|
||||||
//配置文件配置了readv, 但是因为 命令行给出的值优先, 所以要设回
|
|
||||||
|
|
||||||
netLayer.UseReadv = usereadv_beforeLoadConfigFile
|
|
||||||
}
|
|
||||||
|
|
||||||
//Printf不会发生 escapes to heap 现象,所以我们统一用 Printf
|
//Printf不会发生 escapes to heap 现象,所以我们统一用 Printf
|
||||||
fmt.Printf("Log Level:%d\n", utils.LogLevel)
|
fmt.Printf("Log Level:%d\n", utils.LogLevel)
|
||||||
fmt.Printf("UseReadv:%t\n", netLayer.UseReadv)
|
fmt.Printf("UseReadv:%t\n", netLayer.UseReadv)
|
||||||
@@ -183,13 +153,14 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
case standardMode:
|
case standardMode:
|
||||||
|
|
||||||
|
loadCommonComponentsFromStandardConf()
|
||||||
|
|
||||||
//虽然标准模式支持多个Server,目前先只考虑一个
|
//虽然标准模式支持多个Server,目前先只考虑一个
|
||||||
//多个Server存在的话,则必须要用 tag指定路由; 然后,我们需在预先阶段就判断好tag指定的路由
|
//多个Server存在的话,则必须要用 tag指定路由; 然后,我们需在预先阶段就判断好tag指定的路由
|
||||||
|
|
||||||
if len(standardConf.Listen) < 1 {
|
if len(standardConf.Listen) < 1 {
|
||||||
if ce := utils.CanLogWarn("no listen in config settings"); ce != nil {
|
utils.Warn("no listen in config settings")
|
||||||
ce.Write()
|
|
||||||
}
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,21 +182,6 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hasMyCountry := (standardConf.App != nil && standardConf.App.MyCountryISO_3166 != "")
|
|
||||||
|
|
||||||
if standardConf.Route != nil || hasMyCountry {
|
|
||||||
|
|
||||||
netLayer.LoadMaxmindGeoipFile("")
|
|
||||||
|
|
||||||
routePolicy = netLayer.NewRoutePolicy()
|
|
||||||
if hasMyCountry {
|
|
||||||
routePolicy.AddRouteSet(netLayer.NewRouteSetForMyCountry(standardConf.App.MyCountryISO_3166))
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
proxy.LoadRulesForRoutePolicy(standardConf.Route, routePolicy)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var defaultOutClient proxy.Client
|
var defaultOutClient proxy.Client
|
||||||
@@ -241,9 +197,7 @@ func main() {
|
|||||||
case standardMode:
|
case standardMode:
|
||||||
|
|
||||||
if len(standardConf.Dial) < 1 {
|
if len(standardConf.Dial) < 1 {
|
||||||
if ce := utils.CanLogWarn("no dial in config settings"); ce != nil {
|
utils.Warn("no dial in config settings")
|
||||||
ce.Write()
|
|
||||||
}
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -331,15 +285,13 @@ func listenSer(inServer proxy.Server, defaultOutClientForThis proxy.Client) {
|
|||||||
|
|
||||||
handleFunc := inServer.HandleInitialLayersFunc()
|
handleFunc := inServer.HandleInitialLayersFunc()
|
||||||
if handleFunc == nil {
|
if handleFunc == nil {
|
||||||
utils.ZapLogger.Fatal("inServer.IsHandleInitialLayers but inServer.HandleInitialLayersFunc() returns nil")
|
utils.Fatal("inServer.IsHandleInitialLayers but inServer.HandleInitialLayersFunc() returns nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
//baseConn可以为nil,quic就是如此
|
//baseConn可以为nil,quic就是如此
|
||||||
newConnChan, baseConn := handleFunc()
|
newConnChan, baseConn := handleFunc()
|
||||||
if newConnChan == nil {
|
if newConnChan == nil {
|
||||||
if ce := utils.CanLogErr("StarthandleInitialLayers can't extablish baseConn"); ce != nil {
|
utils.Error("StarthandleInitialLayers can't extablish baseConn")
|
||||||
ce.Write()
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -347,10 +299,7 @@ func listenSer(inServer proxy.Server, defaultOutClientForThis proxy.Client) {
|
|||||||
for {
|
for {
|
||||||
newConn, ok := <-newConnChan
|
newConn, ok := <-newConnChan
|
||||||
if !ok {
|
if !ok {
|
||||||
if ce := utils.CanLogErr("read from SuperProxy not ok"); ce != nil {
|
utils.Error("read from SuperProxy not ok")
|
||||||
ce.Write()
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
quic.CloseSession(baseConn)
|
quic.CloseSession(baseConn)
|
||||||
|
|
||||||
@@ -1150,7 +1099,7 @@ func dialClient(iics incomingInserverConnState, targetAddr netLayer.Addr, client
|
|||||||
|
|
||||||
} else {
|
} else {
|
||||||
//虽然拨号失败,但是不能认为我们一定有错误, 因为很可能申请的ip本身就是不可达的, 所以不是error等级而是warn等级
|
//虽然拨号失败,但是不能认为我们一定有错误, 因为很可能申请的ip本身就是不可达的, 所以不是error等级而是warn等级
|
||||||
if ce := utils.CanLogWarn("failed in dial"); ce != nil {
|
if ce := utils.CanLogWarn("failed dialing"); ce != nil {
|
||||||
ce.Write(
|
ce.Write(
|
||||||
zap.String("target", realTargetAddr.String()),
|
zap.String("target", realTargetAddr.String()),
|
||||||
zap.Error(err),
|
zap.Error(err),
|
||||||
|
@@ -383,6 +383,9 @@ func UDPAddr_v4_to_Bytes(addr *net.UDPAddr) [6]byte {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func UDPAddr2AddrPort(ua *net.UDPAddr) netip.AddrPort {
|
func UDPAddr2AddrPort(ua *net.UDPAddr) netip.AddrPort {
|
||||||
|
if ua == nil {
|
||||||
|
return netip.AddrPort{}
|
||||||
|
}
|
||||||
a, _ := netip.AddrFromSlice(ua.IP)
|
a, _ := netip.AddrFromSlice(ua.IP)
|
||||||
return netip.AddrPortFrom(a, uint16(ua.Port))
|
return netip.AddrPortFrom(a, uint16(ua.Port))
|
||||||
}
|
}
|
||||||
|
@@ -12,6 +12,8 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/hahahrfool/v2ray_simple/utils"
|
"github.com/hahahrfool/v2ray_simple/utils"
|
||||||
)
|
)
|
||||||
@@ -83,7 +85,9 @@ var GeositeListMap = make(map[string]*GeositeList)
|
|||||||
//geosite:cn 这种是geosite列表匹配
|
//geosite:cn 这种是geosite列表匹配
|
||||||
|
|
||||||
func IsDomainInsideGeosite(geositeName string, domain string) bool {
|
func IsDomainInsideGeosite(geositeName string, domain string) bool {
|
||||||
|
geositeName = strings.ToUpper(geositeName)
|
||||||
glist := GeositeListMap[geositeName]
|
glist := GeositeListMap[geositeName]
|
||||||
|
//log.Println("IsDomainInsideGeosite called", geositeName, len(glist))
|
||||||
if glist == nil {
|
if glist == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -91,11 +95,15 @@ func IsDomainInsideGeosite(geositeName string, domain string) bool {
|
|||||||
if _, found := glist.FullDomains[domain]; found {
|
if _, found := glist.FullDomains[domain]; found {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if found := HasFullOrSubDomain(domain, MapGeositeDomainHaser(glist.Domains)); found {
|
if HasFullOrSubDomain(domain, MapGeositeDomainHaser(glist.Domains)) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
//todo: regex part
|
for _, reg := range glist.RegexDomains {
|
||||||
|
if reg.MatchString(domain) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -122,7 +130,7 @@ type GeositeList struct {
|
|||||||
|
|
||||||
FullDomains map[string]GeositeDomain
|
FullDomains map[string]GeositeDomain
|
||||||
Domains map[string]GeositeDomain
|
Domains map[string]GeositeDomain
|
||||||
RegexDomains []GeositeDomain
|
RegexDomains []*regexp.Regexp
|
||||||
}
|
}
|
||||||
|
|
||||||
type MapGeositeDomainHaser map[string]GeositeDomain
|
type MapGeositeDomainHaser map[string]GeositeDomain
|
||||||
@@ -132,10 +140,15 @@ func (mdh MapGeositeDomainHaser) HasDomain(d string) bool {
|
|||||||
return found
|
return found
|
||||||
}
|
}
|
||||||
|
|
||||||
//从 geosite/data 文件夹中读取所有文件并加载到 GeositeListMap 中
|
//从 geosite/data 文件夹中读取所有文件并加载到 GeositeListMap 中.
|
||||||
|
//
|
||||||
|
//该 geosite/data 就是 github.com/v2fly/domain-list-community 项目的 data文件夹.
|
||||||
func LoadGeositeFiles() (err error) {
|
func LoadGeositeFiles() (err error) {
|
||||||
dir := "geosite/data"
|
dir := "geosite/data"
|
||||||
dir = utils.GetFilePath(dir)
|
dir = utils.GetFilePath(dir)
|
||||||
|
if !utils.DirExist(dir) {
|
||||||
|
return os.ErrNotExist
|
||||||
|
}
|
||||||
ref := make(map[string]*GeositeRawList)
|
ref := make(map[string]*GeositeRawList)
|
||||||
err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -169,7 +182,7 @@ func LoadGeositeFiles() (err error) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 该函数适用于系统中没有git的情况, 如果有git我们直接 git clone就行了
|
// 该函数适用于系统中没有git的情况, 如果有git我们直接 git clone就行了,而且还能不断pull进行滚动更新
|
||||||
func DownloadCommunity_DomainListFiles() {
|
func DownloadCommunity_DomainListFiles() {
|
||||||
resp, err := http.Get("https://api.github.com/repos/v2fly/domain-list-community/releases/latest")
|
resp, err := http.Get("https://api.github.com/repos/v2fly/domain-list-community/releases/latest")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@@ -5,11 +5,12 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
//来自 v2fly, 改动改了一下命名。
|
//来自 v2fly, 有一定改动.
|
||||||
|
|
||||||
// GeositeRawList 用于序列化
|
// GeositeRawList 用于序列化
|
||||||
type GeositeRawList struct {
|
type GeositeRawList struct {
|
||||||
@@ -34,7 +35,7 @@ func LoadGeositeFile(path string) (*GeositeRawList, error) {
|
|||||||
if len(line) == 0 {
|
if len(line) == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
entry, err := parseGeoSiteEntry(line)
|
entry, err := parseGeositeEntry(line)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -44,7 +45,7 @@ func LoadGeositeFile(path string) (*GeositeRawList, error) {
|
|||||||
return list, nil
|
return list, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseGeoSiteEntry(line string) (GeositeDomain, error) {
|
func parseGeositeEntry(line string) (GeositeDomain, error) {
|
||||||
line = strings.TrimSpace(line)
|
line = strings.TrimSpace(line)
|
||||||
parts := strings.Split(line, " ")
|
parts := strings.Split(line, " ")
|
||||||
|
|
||||||
@@ -219,13 +220,17 @@ func (grl *GeositeRawList) ToGeositeList() (gl *GeositeList) {
|
|||||||
gl.Name = grl.Name
|
gl.Name = grl.Name
|
||||||
gl.Domains = make(map[string]GeositeDomain)
|
gl.Domains = make(map[string]GeositeDomain)
|
||||||
gl.FullDomains = make(map[string]GeositeDomain)
|
gl.FullDomains = make(map[string]GeositeDomain)
|
||||||
gl.RegexDomains = make([]GeositeDomain, 0)
|
gl.RegexDomains = make([]*regexp.Regexp, 0)
|
||||||
for _, v := range grl.Domains {
|
for _, v := range grl.Domains {
|
||||||
switch v.Type {
|
switch v.Type {
|
||||||
case "domain":
|
case "domain":
|
||||||
gl.Domains[v.Value] = v
|
gl.Domains[v.Value] = v
|
||||||
case "regexp":
|
case "regexp":
|
||||||
gl.RegexDomains = append(gl.RegexDomains, v)
|
reg, err := regexp.Compile(v.Value)
|
||||||
|
if err == nil {
|
||||||
|
gl.RegexDomains = append(gl.RegexDomains, reg)
|
||||||
|
|
||||||
|
}
|
||||||
case "full":
|
case "full":
|
||||||
gl.FullDomains[v.Value] = v
|
gl.FullDomains[v.Value] = v
|
||||||
}
|
}
|
||||||
|
@@ -3,6 +3,7 @@ package netLayer
|
|||||||
import (
|
import (
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/yl2chen/cidranger"
|
"github.com/yl2chen/cidranger"
|
||||||
@@ -53,9 +54,17 @@ type TargetDescription struct {
|
|||||||
// RouteSet 只负责把一些属性相同的 “网络层/传输层 特征” 放到一起
|
// RouteSet 只负责把一些属性相同的 “网络层/传输层 特征” 放到一起
|
||||||
type RouteSet struct {
|
type RouteSet struct {
|
||||||
//网络层
|
//网络层
|
||||||
NetRanger cidranger.Ranger //一个范围
|
NetRanger cidranger.Ranger //一个范围
|
||||||
IPs map[netip.Addr]bool //一个确定值
|
IPs map[netip.Addr]bool //一个确定值
|
||||||
Domains, InTags, Countries map[string]bool // Countries 使用 ISO 3166 字符串 作为key
|
|
||||||
|
//Match 匹配任意字符串
|
||||||
|
//Domains匹配子域名,当此域名是目标域名或其子域名时,该规则生效
|
||||||
|
//Full只匹配完整域名
|
||||||
|
Domains, Full, InTags, Countries map[string]bool // Countries 使用 ISO 3166 字符串 作为key
|
||||||
|
|
||||||
|
//Regex是正则匹配域名
|
||||||
|
Regex []*regexp.Regexp
|
||||||
|
Match, Geosites []string
|
||||||
|
|
||||||
//传输层
|
//传输层
|
||||||
AllowedTransportLayerProtocols uint16
|
AllowedTransportLayerProtocols uint16
|
||||||
@@ -76,7 +85,7 @@ func NewRouteSetForMyCountry(iso string) *RouteSet {
|
|||||||
AllowedTransportLayerProtocols: TCP | UDP, //默认即支持tcp和udp
|
AllowedTransportLayerProtocols: TCP | UDP, //默认即支持tcp和udp
|
||||||
|
|
||||||
}
|
}
|
||||||
rs.Countries[iso] = true
|
rs.Countries[strings.ToUpper(iso)] = true
|
||||||
rs.Domains[strings.ToLower(iso)] = true //iso字符串的小写正好可以作为顶级域名
|
rs.Domains[strings.ToLower(iso)] = true //iso字符串的小写正好可以作为顶级域名
|
||||||
return rs
|
return rs
|
||||||
}
|
}
|
||||||
@@ -85,7 +94,10 @@ func NewFullRouteSet() *RouteSet {
|
|||||||
return &RouteSet{
|
return &RouteSet{
|
||||||
NetRanger: cidranger.NewPCTrieRanger(),
|
NetRanger: cidranger.NewPCTrieRanger(),
|
||||||
IPs: make(map[netip.Addr]bool),
|
IPs: make(map[netip.Addr]bool),
|
||||||
|
Match: make([]string, 0),
|
||||||
Domains: make(map[string]bool),
|
Domains: make(map[string]bool),
|
||||||
|
Full: make(map[string]bool),
|
||||||
|
Geosites: make([]string, 0),
|
||||||
InTags: make(map[string]bool),
|
InTags: make(map[string]bool),
|
||||||
Countries: make(map[string]bool),
|
Countries: make(map[string]bool),
|
||||||
AllowedTransportLayerProtocols: TCP | UDP, //默认即支持tcp和udp
|
AllowedTransportLayerProtocols: TCP | UDP, //默认即支持tcp和udp
|
||||||
@@ -160,12 +172,45 @@ func (sg *RouteSet) IsAddrIn(a Addr) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if a.Name != "" {
|
if a.Name != "" {
|
||||||
if sg.Domains != nil {
|
|
||||||
|
if len(sg.Full) > 0 {
|
||||||
|
if _, found := sg.Full[a.Name]; found {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(sg.Domains) > 0 {
|
||||||
|
|
||||||
return HasFullOrSubDomain(a.Name, MapDomainHaser(sg.Domains))
|
return HasFullOrSubDomain(a.Name, MapDomainHaser(sg.Domains))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(sg.Match) > 0 {
|
||||||
|
for _, m := range sg.Match {
|
||||||
|
if strings.Contains(a.Name, m) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(sg.Regex) > 0 {
|
||||||
|
for _, reg := range sg.Regex {
|
||||||
|
if reg.MatchString(a.Name) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(sg.Geosites) > 0 && len(GeositeListMap) > 0 {
|
||||||
|
|
||||||
|
for _, g := range sg.Geosites {
|
||||||
|
if IsDomainInsideGeosite(g, a.Name) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@@ -6,6 +6,7 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"os"
|
"os"
|
||||||
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -172,6 +173,14 @@ func LoadRulesForRoutePolicy(rules []*RuleConf, policy *netLayer.RoutePolicy) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func LoadRuleForRouteSet(rule *RuleConf) (rs *netLayer.RouteSet) {
|
func LoadRuleForRouteSet(rule *RuleConf) (rs *netLayer.RouteSet) {
|
||||||
|
if len(netLayer.GeositeListMap) == 0 {
|
||||||
|
err := netLayer.LoadGeositeFiles()
|
||||||
|
if err != nil {
|
||||||
|
if ce := utils.CanLogWarn("geosite folder not exist"); ce != nil {
|
||||||
|
ce.Write(zap.Error(err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
rs = netLayer.NewFullRouteSet()
|
rs = netLayer.NewFullRouteSet()
|
||||||
|
|
||||||
switch value := rule.DialTag.(type) {
|
switch value := rule.DialTag.(type) {
|
||||||
@@ -185,12 +194,37 @@ func LoadRuleForRouteSet(rule *RuleConf) (rs *netLayer.RouteSet) {
|
|||||||
rs.Countries[c] = true
|
rs.Countries[c] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, c := range rule.Domains {
|
for _, d := range rule.Domains {
|
||||||
rs.Domains[c] = true
|
colonIdx := strings.Index(d, ":")
|
||||||
|
if colonIdx < 0 {
|
||||||
|
rs.Match = append(rs.Match, d)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
switch d[:colonIdx] {
|
||||||
|
case "geosite":
|
||||||
|
if netLayer.GeositeListMap != nil {
|
||||||
|
rs.Geosites = append(rs.Geosites, d[colonIdx+1:])
|
||||||
|
|
||||||
|
}
|
||||||
|
case "full":
|
||||||
|
rs.Full[d[colonIdx+1:]] = true
|
||||||
|
case "domain":
|
||||||
|
rs.Domains[d[colonIdx+1:]] = true
|
||||||
|
case "regexp":
|
||||||
|
reg, err := regexp.Compile(d[colonIdx+1:])
|
||||||
|
if err == nil {
|
||||||
|
rs.Regex = append(rs.Regex, reg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
continue
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, c := range rule.InTags {
|
for _, t := range rule.InTags {
|
||||||
rs.InTags[c] = true
|
rs.InTags[t] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
//ip 过滤 需要 分辨 cidr 和普通ip
|
//ip 过滤 需要 分辨 cidr 和普通ip
|
||||||
|
17
utils/log.go
17
utils/log.go
@@ -1,4 +1,3 @@
|
|||||||
// Package utils provides utilities that is used in all sub-packages in verysimple
|
|
||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -102,3 +101,19 @@ func CanLogFatal(msg string) *zapcore.CheckedEntry {
|
|||||||
return ZapLogger.Check(zap.FatalLevel, msg)
|
return ZapLogger.Check(zap.FatalLevel, msg)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Debug(msg string) {
|
||||||
|
ZapLogger.Debug(msg)
|
||||||
|
}
|
||||||
|
func Info(msg string) {
|
||||||
|
ZapLogger.Info(msg)
|
||||||
|
}
|
||||||
|
func Warn(msg string) {
|
||||||
|
ZapLogger.Warn(msg)
|
||||||
|
}
|
||||||
|
func Error(msg string) {
|
||||||
|
ZapLogger.Error(msg)
|
||||||
|
}
|
||||||
|
func Fatal(msg string) {
|
||||||
|
ZapLogger.Fatal(msg)
|
||||||
|
}
|
||||||
|
14
utils/utils.go
Normal file
14
utils/utils.go
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
// Package utils provides utilities that is used in all codes in verysimple
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import "flag"
|
||||||
|
|
||||||
|
func IsFlagPassed(name string) bool {
|
||||||
|
found := false
|
||||||
|
flag.Visit(func(f *flag.Flag) {
|
||||||
|
if f.Name == name {
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return found
|
||||||
|
}
|
Reference in New Issue
Block a user