Browse Source

Refactor geo resources

世界 3 years ago
parent
commit
2d9203ee74

+ 1 - 2
.gitignore

@@ -1,5 +1,4 @@
 /.idea/
 /vendor/
 /*.json
-/Country.mmdb
-/geosite.db
+/*.db

+ 10 - 111
adapter/handler.go

@@ -6,124 +6,23 @@ import (
 
 	"github.com/sagernet/sing/common/buf"
 	E "github.com/sagernet/sing/common/exceptions"
-	M "github.com/sagernet/sing/common/metadata"
 	N "github.com/sagernet/sing/common/network"
 )
 
-type (
-	ConnectionHandler interface {
-		NewConnection(ctx context.Context, conn net.Conn, metadata InboundContext) error
-	}
-	PacketHandler interface {
-		NewPacket(ctx context.Context, conn N.PacketConn, buffer *buf.Buffer, metadata InboundContext) error
-	}
-	PacketConnectionHandler interface {
-		NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata InboundContext) error
-	}
-	UpstreamHandlerAdapter interface {
-		N.TCPConnectionHandler
-		N.UDPConnectionHandler
-		E.Handler
-	}
-	ConnectionHandlerFunc       = func(ctx context.Context, conn net.Conn, metadata InboundContext) error
-	PacketConnectionHandlerFunc = func(ctx context.Context, conn N.PacketConn, metadata InboundContext) error
-)
-
-func NewUpstreamHandler(
-	metadata InboundContext,
-	connectionHandler ConnectionHandlerFunc,
-	packetHandler PacketConnectionHandlerFunc,
-	errorHandler E.Handler,
-) UpstreamHandlerAdapter {
-	return &myUpstreamHandlerWrapper{
-		metadata:          metadata,
-		connectionHandler: connectionHandler,
-		packetHandler:     packetHandler,
-		errorHandler:      errorHandler,
-	}
-}
-
-var _ UpstreamHandlerAdapter = (*myUpstreamHandlerWrapper)(nil)
-
-type myUpstreamHandlerWrapper struct {
-	metadata          InboundContext
-	connectionHandler ConnectionHandlerFunc
-	packetHandler     PacketConnectionHandlerFunc
-	errorHandler      E.Handler
-}
-
-func (w *myUpstreamHandlerWrapper) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
-	w.metadata.Destination = metadata.Destination
-	return w.connectionHandler(ctx, conn, w.metadata)
-}
-
-func (w *myUpstreamHandlerWrapper) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error {
-	w.metadata.Destination = metadata.Destination
-	return w.packetHandler(ctx, conn, w.metadata)
-}
-
-func (w *myUpstreamHandlerWrapper) NewError(ctx context.Context, err error) {
-	w.errorHandler.NewError(ctx, err)
-}
-
-var myContextType = (*MetadataContext)(nil)
-
-type MetadataContext struct {
-	context.Context
-	Metadata InboundContext
-}
-
-func (c *MetadataContext) Value(key any) any {
-	if key == myContextType {
-		return c
-	}
-	return c.Context.Value(key)
-}
-
-func ContextWithMetadata(ctx context.Context, metadata InboundContext) context.Context {
-	return &MetadataContext{
-		Context:  ctx,
-		Metadata: metadata,
-	}
-}
-
-func UpstreamMetadata(metadata InboundContext) M.Metadata {
-	return M.Metadata{
-		Source:      metadata.Source,
-		Destination: metadata.Destination,
-	}
-}
-
-type myUpstreamContextHandlerWrapper struct {
-	connectionHandler ConnectionHandlerFunc
-	packetHandler     PacketConnectionHandlerFunc
-	errorHandler      E.Handler
-}
-
-func NewUpstreamContextHandler(
-	connectionHandler ConnectionHandlerFunc,
-	packetHandler PacketConnectionHandlerFunc,
-	errorHandler E.Handler,
-) UpstreamHandlerAdapter {
-	return &myUpstreamContextHandlerWrapper{
-		connectionHandler: connectionHandler,
-		packetHandler:     packetHandler,
-		errorHandler:      errorHandler,
-	}
+type ConnectionHandler interface {
+	NewConnection(ctx context.Context, conn net.Conn, metadata InboundContext) error
 }
 
-func (w *myUpstreamContextHandlerWrapper) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
-	myCtx := ctx.Value(myContextType).(*MetadataContext)
-	myCtx.Metadata.Destination = metadata.Destination
-	return w.connectionHandler(ctx, conn, myCtx.Metadata)
+type PacketHandler interface {
+	NewPacket(ctx context.Context, conn N.PacketConn, buffer *buf.Buffer, metadata InboundContext) error
 }
 
-func (w *myUpstreamContextHandlerWrapper) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error {
-	myCtx := ctx.Value(myContextType).(*MetadataContext)
-	myCtx.Metadata.Destination = metadata.Destination
-	return w.packetHandler(ctx, conn, myCtx.Metadata)
+type PacketConnectionHandler interface {
+	NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata InboundContext) error
 }
 
-func (w *myUpstreamContextHandlerWrapper) NewError(ctx context.Context, err error) {
-	w.errorHandler.NewError(ctx, err)
+type UpstreamHandlerAdapter interface {
+	N.TCPConnectionHandler
+	N.UDPConnectionHandler
+	E.Handler
 }

+ 5 - 7
adapter/router.go

@@ -4,25 +4,23 @@ import (
 	"context"
 	"net"
 
-	"github.com/oschwald/geoip2-golang"
+	"github.com/sagernet/sing-box/common/geoip"
 	"github.com/sagernet/sing-box/common/geosite"
 	N "github.com/sagernet/sing/common/network"
 )
 
 type Router interface {
-	Start() error
-	Close() error
-
+	Service
 	Outbound(tag string) (Outbound, bool)
 	RouteConnection(ctx context.Context, conn net.Conn, metadata InboundContext) error
 	RoutePacketConnection(ctx context.Context, conn N.PacketConn, metadata InboundContext) error
-	GeoIPReader() *geoip2.Reader
+	GeoIPReader() *geoip.Reader
 	GeositeReader() *geosite.Reader
 }
 
 type Rule interface {
-	Start() error
-	Close() error
+	Service
+	UpdateGeosite() error
 	Match(metadata *InboundContext) bool
 	Outbound() string
 	String() string

+ 114 - 0
adapter/upstream.go

@@ -0,0 +1,114 @@
+package adapter
+
+import (
+	"context"
+	"net"
+
+	E "github.com/sagernet/sing/common/exceptions"
+	M "github.com/sagernet/sing/common/metadata"
+	N "github.com/sagernet/sing/common/network"
+)
+
+type (
+	ConnectionHandlerFunc       = func(ctx context.Context, conn net.Conn, metadata InboundContext) error
+	PacketConnectionHandlerFunc = func(ctx context.Context, conn N.PacketConn, metadata InboundContext) error
+)
+
+func NewUpstreamHandler(
+	metadata InboundContext,
+	connectionHandler ConnectionHandlerFunc,
+	packetHandler PacketConnectionHandlerFunc,
+	errorHandler E.Handler,
+) UpstreamHandlerAdapter {
+	return &myUpstreamHandlerWrapper{
+		metadata:          metadata,
+		connectionHandler: connectionHandler,
+		packetHandler:     packetHandler,
+		errorHandler:      errorHandler,
+	}
+}
+
+var _ UpstreamHandlerAdapter = (*myUpstreamHandlerWrapper)(nil)
+
+type myUpstreamHandlerWrapper struct {
+	metadata          InboundContext
+	connectionHandler ConnectionHandlerFunc
+	packetHandler     PacketConnectionHandlerFunc
+	errorHandler      E.Handler
+}
+
+func (w *myUpstreamHandlerWrapper) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
+	w.metadata.Destination = metadata.Destination
+	return w.connectionHandler(ctx, conn, w.metadata)
+}
+
+func (w *myUpstreamHandlerWrapper) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error {
+	w.metadata.Destination = metadata.Destination
+	return w.packetHandler(ctx, conn, w.metadata)
+}
+
+func (w *myUpstreamHandlerWrapper) NewError(ctx context.Context, err error) {
+	w.errorHandler.NewError(ctx, err)
+}
+
+var myContextType = (*MetadataContext)(nil)
+
+type MetadataContext struct {
+	context.Context
+	Metadata InboundContext
+}
+
+func (c *MetadataContext) Value(key any) any {
+	if key == myContextType {
+		return c
+	}
+	return c.Context.Value(key)
+}
+
+func ContextWithMetadata(ctx context.Context, metadata InboundContext) context.Context {
+	return &MetadataContext{
+		Context:  ctx,
+		Metadata: metadata,
+	}
+}
+
+func UpstreamMetadata(metadata InboundContext) M.Metadata {
+	return M.Metadata{
+		Source:      metadata.Source,
+		Destination: metadata.Destination,
+	}
+}
+
+type myUpstreamContextHandlerWrapper struct {
+	connectionHandler ConnectionHandlerFunc
+	packetHandler     PacketConnectionHandlerFunc
+	errorHandler      E.Handler
+}
+
+func NewUpstreamContextHandler(
+	connectionHandler ConnectionHandlerFunc,
+	packetHandler PacketConnectionHandlerFunc,
+	errorHandler E.Handler,
+) UpstreamHandlerAdapter {
+	return &myUpstreamContextHandlerWrapper{
+		connectionHandler: connectionHandler,
+		packetHandler:     packetHandler,
+		errorHandler:      errorHandler,
+	}
+}
+
+func (w *myUpstreamContextHandlerWrapper) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
+	myCtx := ctx.Value(myContextType).(*MetadataContext)
+	myCtx.Metadata.Destination = metadata.Destination
+	return w.connectionHandler(ctx, conn, myCtx.Metadata)
+}
+
+func (w *myUpstreamContextHandlerWrapper) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error {
+	myCtx := ctx.Value(myContextType).(*MetadataContext)
+	myCtx.Metadata.Destination = metadata.Destination
+	return w.packetHandler(ctx, conn, myCtx.Metadata)
+}
+
+func (w *myUpstreamContextHandlerWrapper) NewError(ctx context.Context, err error) {
+	w.errorHandler.NewError(ctx, err)
+}

+ 37 - 0
common/geoip/reader.go

@@ -0,0 +1,37 @@
+package geoip
+
+import (
+	"net/netip"
+
+	"github.com/oschwald/maxminddb-golang"
+	E "github.com/sagernet/sing/common/exceptions"
+	N "github.com/sagernet/sing/common/network"
+)
+
+type Reader struct {
+	reader *maxminddb.Reader
+}
+
+func Open(path string) (*Reader, []string, error) {
+	database, err := maxminddb.Open(path)
+	if err != nil {
+		return nil, nil, err
+	}
+	if database.Metadata.DatabaseType != "sing-geoip" {
+		database.Close()
+		return nil, nil, E.New("incorrect database type, expected sing-geoip, got ", database.Metadata.DatabaseType)
+	}
+	return &Reader{database}, database.Metadata.Languages, nil
+}
+
+func (r *Reader) Lookup(addr netip.Addr) string {
+	var code string
+	_ = r.reader.Lookup(addr.AsSlice(), &code)
+	if code != "" {
+		return code
+	}
+	if !N.IsPublicAddr(addr) {
+		return "private"
+	}
+	return "unknown"
+}

+ 8 - 4
common/geosite/reader.go

@@ -14,10 +14,10 @@ type Reader struct {
 	domainLength map[string]int
 }
 
-func Open(path string) (*Reader, error) {
+func Open(path string) (*Reader, []string, error) {
 	content, err := os.Open(path)
 	if err != nil {
-		return nil, err
+		return nil, nil, err
 	}
 	reader := &Reader{
 		reader: content,
@@ -25,9 +25,13 @@ func Open(path string) (*Reader, error) {
 	err = reader.readMetadata()
 	if err != nil {
 		content.Close()
-		return nil, err
+		return nil, nil, err
 	}
-	return reader, nil
+	codes := make([]string, 0, len(reader.domainIndex))
+	for code := range reader.domainIndex {
+		codes = append(codes, code)
+	}
+	return reader, codes, nil
 }
 
 func (r *Reader) readMetadata() error {

+ 26 - 19
route/router.go

@@ -9,8 +9,8 @@ import (
 	"path/filepath"
 	"time"
 
-	"github.com/oschwald/geoip2-golang"
 	"github.com/sagernet/sing-box/adapter"
+	"github.com/sagernet/sing-box/common/geoip"
 	"github.com/sagernet/sing-box/common/geosite"
 	C "github.com/sagernet/sing-box/constant"
 	"github.com/sagernet/sing-box/log"
@@ -36,12 +36,11 @@ type Router struct {
 	defaultOutboundForConnection       adapter.Outbound
 	defaultOutboundForPacketConnection adapter.Outbound
 
-	needGeoIPDatabase bool
-	geoIPOptions      option.GeoIPOptions
-	geoIPReader       *geoip2.Reader
-
+	needGeoIPDatabase   bool
 	needGeositeDatabase bool
+	geoIPOptions        option.GeoIPOptions
 	geositeOptions      option.GeositeOptions
+	geoIPReader         *geoip.Reader
 	geositeReader       *geosite.Reader
 }
 
@@ -154,17 +153,28 @@ func (r *Router) Start() error {
 			return err
 		}
 	}
+	if r.needGeositeDatabase {
+		for _, rule := range r.rules {
+			err := rule.UpdateGeosite()
+			if err != nil {
+				r.logger.Error("failed to initialize geosite: ", err)
+			}
+		}
+		err := common.Close(r.geositeReader)
+		if err != nil {
+			return err
+		}
+	}
 	return nil
 }
 
 func (r *Router) Close() error {
 	return common.Close(
 		common.PtrOrNil(r.geoIPReader),
-		common.PtrOrNil(r.geositeReader),
 	)
 }
 
-func (r *Router) GeoIPReader() *geoip2.Reader {
+func (r *Router) GeoIPReader() *geoip.Reader {
 	return r.geoIPReader
 }
 
@@ -199,7 +209,7 @@ func (r *Router) match(ctx context.Context, metadata adapter.InboundContext, def
 	for i, rule := range r.rules {
 		if rule.Match(&metadata) {
 			detour := rule.Outbound()
-			r.logger.WithContext(ctx).Info("match [", i, "]", rule.String(), " => ", detour)
+			r.logger.WithContext(ctx).Info("match[", i, "] ", rule.String(), " => ", detour)
 			if outbound, loaded := r.Outbound(detour); loaded {
 				return outbound
 			}
@@ -245,7 +255,7 @@ func (r *Router) prepareGeoIPDatabase() error {
 	if r.geoIPOptions.Path != "" {
 		geoPath = r.geoIPOptions.Path
 	} else {
-		geoPath = "Country.mmdb"
+		geoPath = "geoip.db"
 		if foundPath, loaded := C.Find(geoPath); loaded {
 			geoPath = foundPath
 		}
@@ -266,13 +276,12 @@ func (r *Router) prepareGeoIPDatabase() error {
 			return err
 		}
 	}
-	geoReader, err := geoip2.Open(geoPath)
-	if err == nil {
-		r.logger.Info("loaded geoip database")
-		r.geoIPReader = geoReader
-	} else {
+	geoReader, codes, err := geoip.Open(geoPath)
+	if err != nil {
 		return E.Cause(err, "open geoip database")
 	}
+	r.logger.Info("loaded geoip database: ", len(codes), " codes")
+	r.geoIPReader = geoReader
 	return nil
 }
 
@@ -302,9 +311,9 @@ func (r *Router) prepareGeositeDatabase() error {
 			return err
 		}
 	}
-	geoReader, err := geosite.Open(geoPath)
+	geoReader, codes, err := geosite.Open(geoPath)
 	if err == nil {
-		r.logger.Info("loaded geosite database")
+		r.logger.Info("loaded geosite database: ", len(codes), " codes")
 		r.geositeReader = geoReader
 	} else {
 		return E.Cause(err, "open geosite database")
@@ -317,7 +326,7 @@ func (r *Router) downloadGeoIPDatabase(savePath string) error {
 	if r.geoIPOptions.DownloadURL != "" {
 		downloadURL = r.geoIPOptions.DownloadURL
 	} else {
-		downloadURL = "https://cdn.jsdelivr.net/gh/Dreamacro/maxmind-geoip@release/Country.mmdb"
+		downloadURL = "https://github.com/SagerNet/sing-geoip/releases/latest/download/geoip.db"
 	}
 	r.logger.Info("downloading geoip database")
 	var detour adapter.Outbound
@@ -342,7 +351,6 @@ func (r *Router) downloadGeoIPDatabase(savePath string) error {
 	defer saveFile.Close()
 
 	httpClient := &http.Client{
-		Timeout: 5 * time.Second,
 		Transport: &http.Transport{
 			ForceAttemptHTTP2:   true,
 			TLSHandshakeTimeout: 5 * time.Second,
@@ -390,7 +398,6 @@ func (r *Router) downloadGeositeDatabase(savePath string) error {
 	defer saveFile.Close()
 
 	httpClient := &http.Client{
-		Timeout: 5 * time.Second,
 		Transport: &http.Transport{
 			ForceAttemptHTTP2:   true,
 			TLSHandshakeTimeout: 5 * time.Second,

+ 50 - 36
route/rule.go

@@ -44,6 +44,7 @@ type DefaultRule struct {
 	items                   []RuleItem
 	sourceAddressItems      []RuleItem
 	destinationAddressItems []RuleItem
+	allItems                []RuleItem
 	outbound                string
 }
 
@@ -57,12 +58,16 @@ func NewDefaultRule(router adapter.Router, logger log.Logger, options option.Def
 		outbound: options.Outbound,
 	}
 	if len(options.Inbound) > 0 {
-		rule.items = append(rule.items, NewInboundRule(options.Inbound))
+		item := NewInboundRule(options.Inbound)
+		rule.items = append(rule.items, item)
+		rule.allItems = append(rule.allItems, item)
 	}
 	if options.IPVersion > 0 {
 		switch options.IPVersion {
 		case 4, 6:
-			rule.items = append(rule.items, NewIPVersionItem(options.IPVersion == 6))
+			item := NewIPVersionItem(options.IPVersion == 6)
+			rule.items = append(rule.items, item)
+			rule.allItems = append(rule.allItems, item)
 		default:
 			return nil, E.New("invalid ip version: ", options.IPVersion)
 		}
@@ -70,19 +75,27 @@ func NewDefaultRule(router adapter.Router, logger log.Logger, options option.Def
 	if options.Network != "" {
 		switch options.Network {
 		case C.NetworkTCP, C.NetworkUDP:
-			rule.items = append(rule.items, NewNetworkItem(options.Network))
+			item := NewNetworkItem(options.Network)
+			rule.items = append(rule.items, item)
+			rule.allItems = append(rule.allItems, item)
 		default:
 			return nil, E.New("invalid network: ", options.Network)
 		}
 	}
 	if len(options.Protocol) > 0 {
-		rule.items = append(rule.items, NewProtocolItem(options.Protocol))
+		item := NewProtocolItem(options.Protocol)
+		rule.items = append(rule.items, item)
+		rule.allItems = append(rule.allItems, item)
 	}
 	if len(options.Domain) > 0 || len(options.DomainSuffix) > 0 {
-		rule.destinationAddressItems = append(rule.destinationAddressItems, NewDomainItem(options.Domain, options.DomainSuffix))
+		item := NewDomainItem(options.Domain, options.DomainSuffix)
+		rule.destinationAddressItems = append(rule.destinationAddressItems, item)
+		rule.allItems = append(rule.allItems, item)
 	}
 	if len(options.DomainKeyword) > 0 {
-		rule.destinationAddressItems = append(rule.destinationAddressItems, NewDomainKeywordItem(options.DomainKeyword))
+		item := NewDomainKeywordItem(options.DomainKeyword)
+		rule.destinationAddressItems = append(rule.destinationAddressItems, item)
+		rule.allItems = append(rule.allItems, item)
 	}
 	if len(options.DomainRegex) > 0 {
 		item, err := NewDomainRegexItem(options.DomainRegex)
@@ -90,15 +103,22 @@ func NewDefaultRule(router adapter.Router, logger log.Logger, options option.Def
 			return nil, E.Cause(err, "domain_regex")
 		}
 		rule.destinationAddressItems = append(rule.destinationAddressItems, item)
+		rule.allItems = append(rule.allItems, item)
 	}
 	if len(options.Geosite) > 0 {
-		rule.destinationAddressItems = append(rule.destinationAddressItems, NewGeositeItem(router, logger, options.Geosite))
+		item := NewGeositeItem(router, logger, options.Geosite)
+		rule.destinationAddressItems = append(rule.destinationAddressItems, item)
+		rule.allItems = append(rule.allItems, item)
 	}
 	if len(options.SourceGeoIP) > 0 {
-		rule.sourceAddressItems = append(rule.sourceAddressItems, NewGeoIPItem(router, logger, true, options.SourceGeoIP))
+		item := NewGeoIPItem(router, logger, true, options.SourceGeoIP)
+		rule.sourceAddressItems = append(rule.sourceAddressItems, item)
+		rule.allItems = append(rule.allItems, item)
 	}
 	if len(options.GeoIP) > 0 {
-		rule.destinationAddressItems = append(rule.destinationAddressItems, NewGeoIPItem(router, logger, false, options.GeoIP))
+		item := NewGeoIPItem(router, logger, false, options.GeoIP)
+		rule.destinationAddressItems = append(rule.destinationAddressItems, item)
+		rule.allItems = append(rule.allItems, item)
 	}
 	if len(options.SourceIPCIDR) > 0 {
 		item, err := NewIPCIDRItem(true, options.SourceIPCIDR)
@@ -106,6 +126,7 @@ func NewDefaultRule(router adapter.Router, logger log.Logger, options option.Def
 			return nil, E.Cause(err, "source_ipcidr")
 		}
 		rule.sourceAddressItems = append(rule.sourceAddressItems, item)
+		rule.allItems = append(rule.allItems, item)
 	}
 	if len(options.IPCIDR) > 0 {
 		item, err := NewIPCIDRItem(false, options.IPCIDR)
@@ -113,30 +134,23 @@ func NewDefaultRule(router adapter.Router, logger log.Logger, options option.Def
 			return nil, E.Cause(err, "ipcidr")
 		}
 		rule.destinationAddressItems = append(rule.destinationAddressItems, item)
+		rule.allItems = append(rule.allItems, item)
 	}
 	if len(options.SourcePort) > 0 {
-		rule.items = append(rule.items, NewPortItem(true, options.SourcePort))
+		item := NewPortItem(true, options.SourcePort)
+		rule.items = append(rule.items, item)
+		rule.allItems = append(rule.allItems, item)
 	}
 	if len(options.Port) > 0 {
-		rule.items = append(rule.items, NewPortItem(false, options.Port))
+		item := NewPortItem(false, options.Port)
+		rule.items = append(rule.items, item)
+		rule.allItems = append(rule.allItems, item)
 	}
 	return rule, nil
 }
 
 func (r *DefaultRule) Start() error {
-	for _, item := range r.items {
-		err := common.Start(item)
-		if err != nil {
-			return err
-		}
-	}
-	for _, item := range r.sourceAddressItems {
-		err := common.Start(item)
-		if err != nil {
-			return err
-		}
-	}
-	for _, item := range r.destinationAddressItems {
+	for _, item := range r.allItems {
 		err := common.Start(item)
 		if err != nil {
 			return err
@@ -146,22 +160,22 @@ func (r *DefaultRule) Start() error {
 }
 
 func (r *DefaultRule) Close() error {
-	for _, item := range r.items {
-		err := common.Close(item)
-		if err != nil {
-			return err
-		}
-	}
-	for _, item := range r.sourceAddressItems {
+	for _, item := range r.allItems {
 		err := common.Close(item)
 		if err != nil {
 			return err
 		}
 	}
-	for _, item := range r.destinationAddressItems {
-		err := common.Close(item)
-		if err != nil {
-			return err
+	return nil
+}
+
+func (r *DefaultRule) UpdateGeosite() error {
+	for _, item := range r.allItems {
+		if geositeItem, isSite := item.(*GeositeItem); isSite {
+			err := geositeItem.Update()
+			if err != nil {
+				return err
+			}
 		}
 	}
 	return nil
@@ -208,5 +222,5 @@ func (r *DefaultRule) Outbound() string {
 }
 
 func (r *DefaultRule) String() string {
-	return strings.Join(common.Map(r.items, F.ToString0[RuleItem]), " ")
+	return strings.Join(common.Map(r.allItems, F.ToString0[RuleItem]), " ")
 }

+ 3 - 3
route/rule_domain_regex.go

@@ -28,11 +28,11 @@ func NewDomainRegexItem(expressions []string) (*DomainRegexItem, error) {
 	description := "domain_regex="
 	eLen := len(expressions)
 	if eLen == 1 {
-		description = expressions[0]
+		description += expressions[0]
 	} else if eLen > 3 {
-		description = F.ToString("[", strings.Join(expressions[:3], " "), "]")
+		description += F.ToString("[", strings.Join(expressions[:3], " "), "]")
 	} else {
-		description = F.ToString("[", strings.Join(expressions, " "), "]")
+		description += F.ToString("[", strings.Join(expressions, " "), "]")
 	}
 	return &DomainRegexItem{matchers, description}, nil
 }

+ 2 - 12
route/rule_geoip.go

@@ -39,24 +39,14 @@ func (r *GeoIPItem) Match(metadata *adapter.InboundContext) bool {
 	}
 	if r.isSource {
 		if metadata.SourceGeoIPCode == "" {
-			country, err := geoReader.Country(metadata.Source.Addr.AsSlice())
-			if err != nil {
-				r.logger.Error("query geoip for ", metadata.Source.Addr, ": ", err)
-				return false
-			}
-			metadata.SourceGeoIPCode = strings.ToLower(country.Country.IsoCode)
+			metadata.SourceGeoIPCode = geoReader.Lookup(metadata.Source.Addr)
 		}
 	} else {
 		if metadata.Destination.IsFqdn() {
 			return false
 		}
 		if metadata.GeoIPCode == "" {
-			country, err := geoReader.Country(metadata.Destination.Addr.AsSlice())
-			if err != nil {
-				r.logger.Error("query geoip for ", metadata.Destination.Addr, ": ", err)
-				return false
-			}
-			metadata.GeoIPCode = strings.ToLower(country.Country.IsoCode)
+			metadata.GeoIPCode = geoReader.Lookup(metadata.Destination.Addr)
 		}
 	}
 	return r.match(metadata)

+ 7 - 4
route/rule_geosite.go

@@ -27,7 +27,7 @@ func NewGeositeItem(router adapter.Router, logger log.Logger, codes []string) *G
 	}
 }
 
-func (r *GeositeItem) Start() error {
+func (r *GeositeItem) Update() error {
 	geositeReader := r.router.GeositeReader()
 	if geositeReader == nil {
 		return E.New("geosite reader is not initialized")
@@ -50,6 +50,9 @@ func (r *GeositeItem) Start() error {
 }
 
 func (r *GeositeItem) Match(metadata *adapter.InboundContext) bool {
+	if r.matcher == nil {
+		return false
+	}
 	return r.matcher.Match(metadata)
 }
 
@@ -57,11 +60,11 @@ func (r *GeositeItem) String() string {
 	description := "geosite="
 	cLen := len(r.codes)
 	if cLen == 1 {
-		description = r.codes[0]
+		description += r.codes[0]
 	} else if cLen > 3 {
-		description = "[" + strings.Join(r.codes[:3], " ") + "...]"
+		description += "[" + strings.Join(r.codes[:3], " ") + "...]"
 	} else {
-		description = "[" + strings.Join(r.codes, " ") + "]"
+		description += "[" + strings.Join(r.codes, " ") + "]"
 	}
 	return description
 }

+ 10 - 0
route/rule_logical.go

@@ -20,6 +20,16 @@ type LogicalRule struct {
 	outbound string
 }
 
+func (r *LogicalRule) UpdateGeosite() error {
+	for _, rule := range r.rules {
+		err := rule.UpdateGeosite()
+		if err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
 func (r *LogicalRule) Start() error {
 	for _, rule := range r.rules {
 		err := rule.Start()