mirror of
https://github.com/SagerNet/sing-box.git
synced 2024-11-22 00:21:30 +00:00
Refactor geo resources
This commit is contained in:
parent
8392567962
commit
2d9203ee74
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1,5 +1,4 @@
|
||||||
/.idea/
|
/.idea/
|
||||||
/vendor/
|
/vendor/
|
||||||
/*.json
|
/*.json
|
||||||
/Country.mmdb
|
/*.db
|
||||||
/geosite.db
|
|
|
@ -6,124 +6,23 @@ import (
|
||||||
|
|
||||||
"github.com/sagernet/sing/common/buf"
|
"github.com/sagernet/sing/common/buf"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type ConnectionHandler interface {
|
||||||
ConnectionHandler interface {
|
NewConnection(ctx context.Context, conn net.Conn, metadata InboundContext) error
|
||||||
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 PacketHandler interface {
|
||||||
|
NewPacket(ctx context.Context, conn N.PacketConn, buffer *buf.Buffer, metadata InboundContext) error
|
||||||
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 {
|
type PacketConnectionHandler interface {
|
||||||
w.metadata.Destination = metadata.Destination
|
NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata InboundContext) error
|
||||||
return w.connectionHandler(ctx, conn, w.metadata)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *myUpstreamHandlerWrapper) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error {
|
type UpstreamHandlerAdapter interface {
|
||||||
w.metadata.Destination = metadata.Destination
|
N.TCPConnectionHandler
|
||||||
return w.packetHandler(ctx, conn, w.metadata)
|
N.UDPConnectionHandler
|
||||||
}
|
E.Handler
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,25 +4,23 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/oschwald/geoip2-golang"
|
"github.com/sagernet/sing-box/common/geoip"
|
||||||
"github.com/sagernet/sing-box/common/geosite"
|
"github.com/sagernet/sing-box/common/geosite"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Router interface {
|
type Router interface {
|
||||||
Start() error
|
Service
|
||||||
Close() error
|
|
||||||
|
|
||||||
Outbound(tag string) (Outbound, bool)
|
Outbound(tag string) (Outbound, bool)
|
||||||
RouteConnection(ctx context.Context, conn net.Conn, metadata InboundContext) error
|
RouteConnection(ctx context.Context, conn net.Conn, metadata InboundContext) error
|
||||||
RoutePacketConnection(ctx context.Context, conn N.PacketConn, metadata InboundContext) error
|
RoutePacketConnection(ctx context.Context, conn N.PacketConn, metadata InboundContext) error
|
||||||
GeoIPReader() *geoip2.Reader
|
GeoIPReader() *geoip.Reader
|
||||||
GeositeReader() *geosite.Reader
|
GeositeReader() *geosite.Reader
|
||||||
}
|
}
|
||||||
|
|
||||||
type Rule interface {
|
type Rule interface {
|
||||||
Start() error
|
Service
|
||||||
Close() error
|
UpdateGeosite() error
|
||||||
Match(metadata *InboundContext) bool
|
Match(metadata *InboundContext) bool
|
||||||
Outbound() string
|
Outbound() string
|
||||||
String() string
|
String() string
|
||||||
|
|
114
adapter/upstream.go
Normal file
114
adapter/upstream.go
Normal file
|
@ -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
common/geoip/reader.go
Normal file
37
common/geoip/reader.go
Normal file
|
@ -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"
|
||||||
|
}
|
|
@ -14,10 +14,10 @@ type Reader struct {
|
||||||
domainLength map[string]int
|
domainLength map[string]int
|
||||||
}
|
}
|
||||||
|
|
||||||
func Open(path string) (*Reader, error) {
|
func Open(path string) (*Reader, []string, error) {
|
||||||
content, err := os.Open(path)
|
content, err := os.Open(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
reader := &Reader{
|
reader := &Reader{
|
||||||
reader: content,
|
reader: content,
|
||||||
|
@ -25,9 +25,13 @@ func Open(path string) (*Reader, error) {
|
||||||
err = reader.readMetadata()
|
err = reader.readMetadata()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
content.Close()
|
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 {
|
func (r *Reader) readMetadata() error {
|
||||||
|
|
|
@ -9,8 +9,8 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/oschwald/geoip2-golang"
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
|
"github.com/sagernet/sing-box/common/geoip"
|
||||||
"github.com/sagernet/sing-box/common/geosite"
|
"github.com/sagernet/sing-box/common/geosite"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
|
@ -36,12 +36,11 @@ type Router struct {
|
||||||
defaultOutboundForConnection adapter.Outbound
|
defaultOutboundForConnection adapter.Outbound
|
||||||
defaultOutboundForPacketConnection adapter.Outbound
|
defaultOutboundForPacketConnection adapter.Outbound
|
||||||
|
|
||||||
needGeoIPDatabase bool
|
needGeoIPDatabase bool
|
||||||
geoIPOptions option.GeoIPOptions
|
|
||||||
geoIPReader *geoip2.Reader
|
|
||||||
|
|
||||||
needGeositeDatabase bool
|
needGeositeDatabase bool
|
||||||
|
geoIPOptions option.GeoIPOptions
|
||||||
geositeOptions option.GeositeOptions
|
geositeOptions option.GeositeOptions
|
||||||
|
geoIPReader *geoip.Reader
|
||||||
geositeReader *geosite.Reader
|
geositeReader *geosite.Reader
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,17 +153,28 @@ func (r *Router) Start() error {
|
||||||
return err
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Router) Close() error {
|
func (r *Router) Close() error {
|
||||||
return common.Close(
|
return common.Close(
|
||||||
common.PtrOrNil(r.geoIPReader),
|
common.PtrOrNil(r.geoIPReader),
|
||||||
common.PtrOrNil(r.geositeReader),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Router) GeoIPReader() *geoip2.Reader {
|
func (r *Router) GeoIPReader() *geoip.Reader {
|
||||||
return r.geoIPReader
|
return r.geoIPReader
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -199,7 +209,7 @@ func (r *Router) match(ctx context.Context, metadata adapter.InboundContext, def
|
||||||
for i, rule := range r.rules {
|
for i, rule := range r.rules {
|
||||||
if rule.Match(&metadata) {
|
if rule.Match(&metadata) {
|
||||||
detour := rule.Outbound()
|
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 {
|
if outbound, loaded := r.Outbound(detour); loaded {
|
||||||
return outbound
|
return outbound
|
||||||
}
|
}
|
||||||
|
@ -245,7 +255,7 @@ func (r *Router) prepareGeoIPDatabase() error {
|
||||||
if r.geoIPOptions.Path != "" {
|
if r.geoIPOptions.Path != "" {
|
||||||
geoPath = r.geoIPOptions.Path
|
geoPath = r.geoIPOptions.Path
|
||||||
} else {
|
} else {
|
||||||
geoPath = "Country.mmdb"
|
geoPath = "geoip.db"
|
||||||
if foundPath, loaded := C.Find(geoPath); loaded {
|
if foundPath, loaded := C.Find(geoPath); loaded {
|
||||||
geoPath = foundPath
|
geoPath = foundPath
|
||||||
}
|
}
|
||||||
|
@ -266,13 +276,12 @@ func (r *Router) prepareGeoIPDatabase() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
geoReader, err := geoip2.Open(geoPath)
|
geoReader, codes, err := geoip.Open(geoPath)
|
||||||
if err == nil {
|
if err != nil {
|
||||||
r.logger.Info("loaded geoip database")
|
|
||||||
r.geoIPReader = geoReader
|
|
||||||
} else {
|
|
||||||
return E.Cause(err, "open geoip database")
|
return E.Cause(err, "open geoip database")
|
||||||
}
|
}
|
||||||
|
r.logger.Info("loaded geoip database: ", len(codes), " codes")
|
||||||
|
r.geoIPReader = geoReader
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -302,9 +311,9 @@ func (r *Router) prepareGeositeDatabase() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
geoReader, err := geosite.Open(geoPath)
|
geoReader, codes, err := geosite.Open(geoPath)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
r.logger.Info("loaded geosite database")
|
r.logger.Info("loaded geosite database: ", len(codes), " codes")
|
||||||
r.geositeReader = geoReader
|
r.geositeReader = geoReader
|
||||||
} else {
|
} else {
|
||||||
return E.Cause(err, "open geosite database")
|
return E.Cause(err, "open geosite database")
|
||||||
|
@ -317,7 +326,7 @@ func (r *Router) downloadGeoIPDatabase(savePath string) error {
|
||||||
if r.geoIPOptions.DownloadURL != "" {
|
if r.geoIPOptions.DownloadURL != "" {
|
||||||
downloadURL = r.geoIPOptions.DownloadURL
|
downloadURL = r.geoIPOptions.DownloadURL
|
||||||
} else {
|
} 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")
|
r.logger.Info("downloading geoip database")
|
||||||
var detour adapter.Outbound
|
var detour adapter.Outbound
|
||||||
|
@ -342,7 +351,6 @@ func (r *Router) downloadGeoIPDatabase(savePath string) error {
|
||||||
defer saveFile.Close()
|
defer saveFile.Close()
|
||||||
|
|
||||||
httpClient := &http.Client{
|
httpClient := &http.Client{
|
||||||
Timeout: 5 * time.Second,
|
|
||||||
Transport: &http.Transport{
|
Transport: &http.Transport{
|
||||||
ForceAttemptHTTP2: true,
|
ForceAttemptHTTP2: true,
|
||||||
TLSHandshakeTimeout: 5 * time.Second,
|
TLSHandshakeTimeout: 5 * time.Second,
|
||||||
|
@ -390,7 +398,6 @@ func (r *Router) downloadGeositeDatabase(savePath string) error {
|
||||||
defer saveFile.Close()
|
defer saveFile.Close()
|
||||||
|
|
||||||
httpClient := &http.Client{
|
httpClient := &http.Client{
|
||||||
Timeout: 5 * time.Second,
|
|
||||||
Transport: &http.Transport{
|
Transport: &http.Transport{
|
||||||
ForceAttemptHTTP2: true,
|
ForceAttemptHTTP2: true,
|
||||||
TLSHandshakeTimeout: 5 * time.Second,
|
TLSHandshakeTimeout: 5 * time.Second,
|
||||||
|
|
|
@ -44,6 +44,7 @@ type DefaultRule struct {
|
||||||
items []RuleItem
|
items []RuleItem
|
||||||
sourceAddressItems []RuleItem
|
sourceAddressItems []RuleItem
|
||||||
destinationAddressItems []RuleItem
|
destinationAddressItems []RuleItem
|
||||||
|
allItems []RuleItem
|
||||||
outbound string
|
outbound string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,12 +58,16 @@ func NewDefaultRule(router adapter.Router, logger log.Logger, options option.Def
|
||||||
outbound: options.Outbound,
|
outbound: options.Outbound,
|
||||||
}
|
}
|
||||||
if len(options.Inbound) > 0 {
|
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 {
|
if options.IPVersion > 0 {
|
||||||
switch options.IPVersion {
|
switch options.IPVersion {
|
||||||
case 4, 6:
|
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:
|
default:
|
||||||
return nil, E.New("invalid ip version: ", options.IPVersion)
|
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 != "" {
|
if options.Network != "" {
|
||||||
switch options.Network {
|
switch options.Network {
|
||||||
case C.NetworkTCP, C.NetworkUDP:
|
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:
|
default:
|
||||||
return nil, E.New("invalid network: ", options.Network)
|
return nil, E.New("invalid network: ", options.Network)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(options.Protocol) > 0 {
|
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 {
|
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 {
|
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 {
|
if len(options.DomainRegex) > 0 {
|
||||||
item, err := NewDomainRegexItem(options.DomainRegex)
|
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")
|
return nil, E.Cause(err, "domain_regex")
|
||||||
}
|
}
|
||||||
rule.destinationAddressItems = append(rule.destinationAddressItems, item)
|
rule.destinationAddressItems = append(rule.destinationAddressItems, item)
|
||||||
|
rule.allItems = append(rule.allItems, item)
|
||||||
}
|
}
|
||||||
if len(options.Geosite) > 0 {
|
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 {
|
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 {
|
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 {
|
if len(options.SourceIPCIDR) > 0 {
|
||||||
item, err := NewIPCIDRItem(true, options.SourceIPCIDR)
|
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")
|
return nil, E.Cause(err, "source_ipcidr")
|
||||||
}
|
}
|
||||||
rule.sourceAddressItems = append(rule.sourceAddressItems, item)
|
rule.sourceAddressItems = append(rule.sourceAddressItems, item)
|
||||||
|
rule.allItems = append(rule.allItems, item)
|
||||||
}
|
}
|
||||||
if len(options.IPCIDR) > 0 {
|
if len(options.IPCIDR) > 0 {
|
||||||
item, err := NewIPCIDRItem(false, options.IPCIDR)
|
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")
|
return nil, E.Cause(err, "ipcidr")
|
||||||
}
|
}
|
||||||
rule.destinationAddressItems = append(rule.destinationAddressItems, item)
|
rule.destinationAddressItems = append(rule.destinationAddressItems, item)
|
||||||
|
rule.allItems = append(rule.allItems, item)
|
||||||
}
|
}
|
||||||
if len(options.SourcePort) > 0 {
|
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 {
|
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
|
return rule, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *DefaultRule) Start() error {
|
func (r *DefaultRule) Start() error {
|
||||||
for _, item := range r.items {
|
for _, item := range r.allItems {
|
||||||
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 {
|
|
||||||
err := common.Start(item)
|
err := common.Start(item)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -146,22 +160,22 @@ func (r *DefaultRule) Start() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *DefaultRule) Close() error {
|
func (r *DefaultRule) Close() error {
|
||||||
for _, item := range r.items {
|
for _, item := range r.allItems {
|
||||||
err := common.Close(item)
|
err := common.Close(item)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, item := range r.sourceAddressItems {
|
return nil
|
||||||
err := common.Close(item)
|
}
|
||||||
if err != nil {
|
|
||||||
return err
|
func (r *DefaultRule) UpdateGeosite() error {
|
||||||
}
|
for _, item := range r.allItems {
|
||||||
}
|
if geositeItem, isSite := item.(*GeositeItem); isSite {
|
||||||
for _, item := range r.destinationAddressItems {
|
err := geositeItem.Update()
|
||||||
err := common.Close(item)
|
if err != nil {
|
||||||
if err != nil {
|
return err
|
||||||
return err
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -208,5 +222,5 @@ func (r *DefaultRule) Outbound() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *DefaultRule) String() 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]), " ")
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,11 +28,11 @@ func NewDomainRegexItem(expressions []string) (*DomainRegexItem, error) {
|
||||||
description := "domain_regex="
|
description := "domain_regex="
|
||||||
eLen := len(expressions)
|
eLen := len(expressions)
|
||||||
if eLen == 1 {
|
if eLen == 1 {
|
||||||
description = expressions[0]
|
description += expressions[0]
|
||||||
} else if eLen > 3 {
|
} else if eLen > 3 {
|
||||||
description = F.ToString("[", strings.Join(expressions[:3], " "), "]")
|
description += F.ToString("[", strings.Join(expressions[:3], " "), "]")
|
||||||
} else {
|
} else {
|
||||||
description = F.ToString("[", strings.Join(expressions, " "), "]")
|
description += F.ToString("[", strings.Join(expressions, " "), "]")
|
||||||
}
|
}
|
||||||
return &DomainRegexItem{matchers, description}, nil
|
return &DomainRegexItem{matchers, description}, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,24 +39,14 @@ func (r *GeoIPItem) Match(metadata *adapter.InboundContext) bool {
|
||||||
}
|
}
|
||||||
if r.isSource {
|
if r.isSource {
|
||||||
if metadata.SourceGeoIPCode == "" {
|
if metadata.SourceGeoIPCode == "" {
|
||||||
country, err := geoReader.Country(metadata.Source.Addr.AsSlice())
|
metadata.SourceGeoIPCode = geoReader.Lookup(metadata.Source.Addr)
|
||||||
if err != nil {
|
|
||||||
r.logger.Error("query geoip for ", metadata.Source.Addr, ": ", err)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
metadata.SourceGeoIPCode = strings.ToLower(country.Country.IsoCode)
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if metadata.Destination.IsFqdn() {
|
if metadata.Destination.IsFqdn() {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if metadata.GeoIPCode == "" {
|
if metadata.GeoIPCode == "" {
|
||||||
country, err := geoReader.Country(metadata.Destination.Addr.AsSlice())
|
metadata.GeoIPCode = geoReader.Lookup(metadata.Destination.Addr)
|
||||||
if err != nil {
|
|
||||||
r.logger.Error("query geoip for ", metadata.Destination.Addr, ": ", err)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
metadata.GeoIPCode = strings.ToLower(country.Country.IsoCode)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return r.match(metadata)
|
return r.match(metadata)
|
||||||
|
|
|
@ -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()
|
geositeReader := r.router.GeositeReader()
|
||||||
if geositeReader == nil {
|
if geositeReader == nil {
|
||||||
return E.New("geosite reader is not initialized")
|
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 {
|
func (r *GeositeItem) Match(metadata *adapter.InboundContext) bool {
|
||||||
|
if r.matcher == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return r.matcher.Match(metadata)
|
return r.matcher.Match(metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,11 +60,11 @@ func (r *GeositeItem) String() string {
|
||||||
description := "geosite="
|
description := "geosite="
|
||||||
cLen := len(r.codes)
|
cLen := len(r.codes)
|
||||||
if cLen == 1 {
|
if cLen == 1 {
|
||||||
description = r.codes[0]
|
description += r.codes[0]
|
||||||
} else if cLen > 3 {
|
} else if cLen > 3 {
|
||||||
description = "[" + strings.Join(r.codes[:3], " ") + "...]"
|
description += "[" + strings.Join(r.codes[:3], " ") + "...]"
|
||||||
} else {
|
} else {
|
||||||
description = "[" + strings.Join(r.codes, " ") + "]"
|
description += "[" + strings.Join(r.codes, " ") + "]"
|
||||||
}
|
}
|
||||||
return description
|
return description
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,16 @@ type LogicalRule struct {
|
||||||
outbound string
|
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 {
|
func (r *LogicalRule) Start() error {
|
||||||
for _, rule := range r.rules {
|
for _, rule := range r.rules {
|
||||||
err := rule.Start()
|
err := rule.Start()
|
||||||
|
|
Loading…
Reference in a new issue