2022-06-30 13:27:56 +00:00
|
|
|
package route
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"net"
|
|
|
|
|
2022-07-02 06:07:50 +00:00
|
|
|
"github.com/oschwald/geoip2-golang"
|
2022-06-30 13:27:56 +00:00
|
|
|
"github.com/sagernet/sing-box/adapter"
|
2022-07-02 06:07:50 +00:00
|
|
|
C "github.com/sagernet/sing-box/constant"
|
2022-06-30 13:27:56 +00:00
|
|
|
"github.com/sagernet/sing-box/log"
|
2022-07-02 06:07:50 +00:00
|
|
|
"github.com/sagernet/sing-box/option"
|
|
|
|
"github.com/sagernet/sing/common"
|
2022-06-30 13:27:56 +00:00
|
|
|
N "github.com/sagernet/sing/common/network"
|
|
|
|
)
|
|
|
|
|
|
|
|
var _ adapter.Router = (*Router)(nil)
|
|
|
|
|
|
|
|
type Router struct {
|
|
|
|
logger log.Logger
|
|
|
|
defaultOutbound adapter.Outbound
|
|
|
|
outboundByTag map[string]adapter.Outbound
|
2022-07-02 06:07:50 +00:00
|
|
|
|
|
|
|
rules []adapter.Rule
|
|
|
|
geoReader *geoip2.Reader
|
2022-06-30 13:27:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func NewRouter(logger log.Logger) *Router {
|
|
|
|
return &Router{
|
2022-07-02 06:07:50 +00:00
|
|
|
logger: logger.WithPrefix("router: "),
|
2022-06-30 13:27:56 +00:00
|
|
|
outboundByTag: make(map[string]adapter.Outbound),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Router) DefaultOutbound() adapter.Outbound {
|
|
|
|
if r.defaultOutbound == nil {
|
|
|
|
panic("missing default outbound")
|
|
|
|
}
|
|
|
|
return r.defaultOutbound
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Router) Outbound(tag string) (adapter.Outbound, bool) {
|
|
|
|
outbound, loaded := r.outboundByTag[tag]
|
|
|
|
return outbound, loaded
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
2022-07-02 06:07:50 +00:00
|
|
|
for _, rule := range r.rules {
|
|
|
|
if rule.Match(metadata) {
|
|
|
|
r.logger.WithContext(ctx).Info("match ", rule.String())
|
|
|
|
if outbound, loaded := r.Outbound(rule.Outbound()); loaded {
|
|
|
|
return outbound.NewConnection(ctx, conn, metadata.Destination)
|
|
|
|
}
|
|
|
|
r.logger.WithContext(ctx).Error("outbound ", rule.Outbound(), " not found")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
r.logger.WithContext(ctx).Info("no match => ", r.defaultOutbound.Tag())
|
2022-06-30 13:27:56 +00:00
|
|
|
return r.defaultOutbound.NewConnection(ctx, conn, metadata.Destination)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
2022-07-02 06:07:50 +00:00
|
|
|
for _, rule := range r.rules {
|
|
|
|
if rule.Match(metadata) {
|
|
|
|
r.logger.WithContext(ctx).Info("match ", rule.String())
|
|
|
|
if outbound, loaded := r.Outbound(rule.Outbound()); loaded {
|
|
|
|
return outbound.NewPacketConnection(ctx, conn, metadata.Destination)
|
|
|
|
}
|
|
|
|
r.logger.WithContext(ctx).Error("outbound ", rule.Outbound(), " not found")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
r.logger.WithContext(ctx).Info("no match => ", r.defaultOutbound.Tag())
|
2022-06-30 13:27:56 +00:00
|
|
|
return r.defaultOutbound.NewPacketConnection(ctx, conn, metadata.Destination)
|
|
|
|
}
|
2022-07-02 06:07:50 +00:00
|
|
|
|
|
|
|
func (r *Router) Close() error {
|
|
|
|
return common.Close(
|
|
|
|
common.PtrOrNil(r.geoReader),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Router) UpdateOutbounds(outbounds []adapter.Outbound) {
|
|
|
|
var defaultOutbound adapter.Outbound
|
|
|
|
outboundByTag := make(map[string]adapter.Outbound)
|
|
|
|
if len(outbounds) > 0 {
|
|
|
|
defaultOutbound = outbounds[0]
|
|
|
|
}
|
|
|
|
for _, outbound := range outbounds {
|
|
|
|
outboundByTag[outbound.Tag()] = outbound
|
|
|
|
}
|
|
|
|
r.defaultOutbound = defaultOutbound
|
|
|
|
r.outboundByTag = outboundByTag
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Router) UpdateRules(options []option.Rule) error {
|
|
|
|
rules := make([]adapter.Rule, 0, len(options))
|
|
|
|
for i, rule := range options {
|
|
|
|
switch rule.Type {
|
|
|
|
case "", C.RuleTypeDefault:
|
|
|
|
rules = append(rules, NewDefaultRule(i, rule.DefaultOptions))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
r.rules = rules
|
|
|
|
return nil
|
|
|
|
}
|