mirror of
https://github.com/SagerNet/sing-box.git
synced 2025-06-13 21:54:13 +08:00
Merge branch 'dev-next' into origin/tls-client-auth
Signed-off-by: jose-C2OaWi <111356383+jose-C2OaWi@users.noreply.github.com>
This commit is contained in:
commit
df3ff301e6
@ -109,7 +109,7 @@ type OutboundGroup interface {
|
||||
|
||||
type URLTestGroup interface {
|
||||
OutboundGroup
|
||||
URLTest(ctx context.Context, url string) (map[string]uint16, error)
|
||||
URLTest(ctx context.Context) (map[string]uint16, error)
|
||||
}
|
||||
|
||||
func OutboundTag(detour Outbound) string {
|
||||
|
15
box.go
15
box.go
@ -287,7 +287,7 @@ func (s *Box) start() error {
|
||||
return E.Cause(err, "initialize inbound/", in.Type(), "[", tag, "]")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return s.postStart()
|
||||
}
|
||||
|
||||
func (s *Box) postStart() error {
|
||||
@ -298,20 +298,21 @@ func (s *Box) postStart() error {
|
||||
return E.Cause(err, "start ", serviceName)
|
||||
}
|
||||
}
|
||||
for serviceName, service := range s.outbounds {
|
||||
if lateService, isLateService := service.(adapter.PostStarter); isLateService {
|
||||
s.logger.Trace("post-starting ", service)
|
||||
err := lateService.PostStart()
|
||||
for _, outbound := range s.outbounds {
|
||||
if lateOutbound, isLateOutbound := outbound.(adapter.PostStarter); isLateOutbound {
|
||||
s.logger.Trace("post-starting outbound/", outbound.Tag())
|
||||
err := lateOutbound.PostStart()
|
||||
if err != nil {
|
||||
return E.Cause(err, "post-start ", serviceName)
|
||||
return E.Cause(err, "post-start outbound/", outbound.Tag())
|
||||
}
|
||||
}
|
||||
}
|
||||
s.logger.Trace("post-starting router")
|
||||
err := s.router.PostStart()
|
||||
if err != nil {
|
||||
return E.Cause(err, "post-start router")
|
||||
}
|
||||
return nil
|
||||
return s.router.PostStart()
|
||||
}
|
||||
|
||||
func (s *Box) Close() error {
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"os"
|
||||
|
||||
"github.com/sagernet/sing-box/common/geosite"
|
||||
"github.com/sagernet/sing-box/common/json"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
|
@ -9,5 +9,6 @@ const (
|
||||
QUICTimeout = 30 * time.Second
|
||||
STUNTimeout = 15 * time.Second
|
||||
UDPTimeout = 5 * time.Minute
|
||||
DefaultURLTestInterval = 1 * time.Minute
|
||||
DefaultURLTestInterval = 3 * time.Minute
|
||||
DefaultURLTestIdleTimeout = 30 * time.Second
|
||||
)
|
||||
|
@ -4,25 +4,8 @@ icon: material/alert-decagram
|
||||
|
||||
# ChangeLog
|
||||
|
||||
#### 1.8.0-alpha.8
|
||||
|
||||
* Add context to JSON decode error message **1**
|
||||
* Reject internal fake-ip queries **2**
|
||||
* Fixes and improvements
|
||||
|
||||
**1**:
|
||||
|
||||
JSON parse errors will now include the current key path.
|
||||
Only takes effect when compiled with Go 1.21+.
|
||||
|
||||
**2**:
|
||||
|
||||
All internal DNS queries now skip DNS rules with `server` type `fakeip`,
|
||||
and the default DNS server can no longer be `fakeip`.
|
||||
|
||||
This change is intended to break incorrect usage and essentially requires no action.
|
||||
|
||||
#### 1.8.0-alpha.7
|
||||
#### 1.7.2
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
|
@ -10,9 +10,10 @@
|
||||
"proxy-b",
|
||||
"proxy-c"
|
||||
],
|
||||
"url": "https://www.gstatic.com/generate_204",
|
||||
"interval": "1m",
|
||||
"tolerance": 50,
|
||||
"url": "",
|
||||
"interval": "",
|
||||
"tolerance": 0,
|
||||
"idle_timeout": "",
|
||||
"interrupt_exist_connections": false
|
||||
}
|
||||
```
|
||||
@ -31,12 +32,16 @@ The URL to test. `https://www.gstatic.com/generate_204` will be used if empty.
|
||||
|
||||
#### interval
|
||||
|
||||
The test interval. `1m` will be used if empty.
|
||||
The test interval. `3m` will be used if empty.
|
||||
|
||||
#### tolerance
|
||||
|
||||
The test tolerance in milliseconds. `50` will be used if empty.
|
||||
|
||||
#### idle_timeout
|
||||
|
||||
The idle timeout. `30m` will be used if empty.
|
||||
|
||||
#### interrupt_exist_connections
|
||||
|
||||
Interrupt existing connections when the selected outbound has changed.
|
||||
|
@ -10,9 +10,10 @@
|
||||
"proxy-b",
|
||||
"proxy-c"
|
||||
],
|
||||
"url": "https://www.gstatic.com/generate_204",
|
||||
"interval": "1m",
|
||||
"url": "",
|
||||
"interval": "",
|
||||
"tolerance": 50,
|
||||
"idle_timeout": "",
|
||||
"interrupt_exist_connections": false
|
||||
}
|
||||
```
|
||||
@ -31,12 +32,16 @@
|
||||
|
||||
#### interval
|
||||
|
||||
测试间隔。 默认使用 `1m`。
|
||||
测试间隔。 默认使用 `3m`。
|
||||
|
||||
#### tolerance
|
||||
|
||||
以毫秒为单位的测试容差。 默认使用 `50`。
|
||||
|
||||
#### idle_timeout
|
||||
|
||||
空闲超时。默认使用 `30m`。
|
||||
|
||||
#### interrupt_exist_connections
|
||||
|
||||
当选定的出站发生更改时,中断现有连接。
|
||||
|
@ -83,7 +83,7 @@ func getGroupDelay(server *Server) func(w http.ResponseWriter, r *http.Request)
|
||||
|
||||
var result map[string]uint16
|
||||
if urlTestGroup, isURLTestGroup := group.(adapter.URLTestGroup); isURLTestGroup {
|
||||
result, err = urlTestGroup.URLTest(ctx, url)
|
||||
result, err = urlTestGroup.URLTest(ctx)
|
||||
} else {
|
||||
outbounds := common.FilterNotNil(common.Map(group.All(), func(it string) adapter.Outbound {
|
||||
itOutbound, _ := server.router.Outbound(it)
|
||||
|
1
go.sum
1
go.sum
@ -111,6 +111,7 @@ github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4Wk
|
||||
github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
|
||||
github.com/sagernet/sing v0.1.8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk=
|
||||
github.com/sagernet/sing v0.2.18-0.20231201060417-575186ed63c2 h1:1ydWkFgLURGlrnwRdjyrpo9lp1g5Qq7XrNBghMntWTs=
|
||||
github.com/sagernet/sing v0.2.18-0.20231201060417-575186ed63c2/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo=
|
||||
github.com/sagernet/sing-dns v0.1.11 h1:PPrMCVVrAeR3f5X23I+cmvacXJ+kzuyAsBiWyUKhGSE=
|
||||
github.com/sagernet/sing-dns v0.1.11/go.mod h1:zJ/YjnYB61SYE+ubMcMqVdpaSvsyQ2iShQGO3vuLvvE=
|
||||
github.com/sagernet/sing-mux v0.1.5-0.20231109075101-6b086ed6bb07 h1:ncKb5tVOsCQgCsv6UpsA0jinbNb5OQ5GMPJlyQP3EHM=
|
||||
|
@ -11,5 +11,6 @@ type URLTestOutboundOptions struct {
|
||||
URL string `json:"url,omitempty"`
|
||||
Interval Duration `json:"interval,omitempty"`
|
||||
Tolerance uint16 `json:"tolerance,omitempty"`
|
||||
IdleTimeout Duration `json:"idle_timeout,omitempty"`
|
||||
InterruptExistConnections bool `json:"interrupt_exist_connections,omitempty"`
|
||||
}
|
||||
|
@ -165,6 +165,7 @@ func (d *DNS) NewPacketConnection(ctx context.Context, conn N.PacketConn, metada
|
||||
}
|
||||
timeout.Update()
|
||||
responseBuffer := buf.NewPacket()
|
||||
responseBuffer.Resize(1024, 0)
|
||||
n, err := response.PackBuffer(responseBuffer.FreeBytes())
|
||||
if err != nil {
|
||||
cancel(err)
|
||||
@ -241,6 +242,7 @@ func (d *DNS) newPacketConnection(ctx context.Context, conn N.PacketConn, readWa
|
||||
}
|
||||
timeout.Update()
|
||||
responseBuffer := buf.NewPacket()
|
||||
responseBuffer.Resize(1024, 0)
|
||||
n, err := response.PackBuffer(responseBuffer.FreeBytes())
|
||||
if err != nil {
|
||||
cancel(err)
|
||||
|
@ -3,7 +3,6 @@ package outbound
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"sort"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@ -36,9 +35,9 @@ type URLTest struct {
|
||||
link string
|
||||
interval time.Duration
|
||||
tolerance uint16
|
||||
idleTimeout time.Duration
|
||||
group *URLTestGroup
|
||||
interruptExternalConnections bool
|
||||
started bool
|
||||
}
|
||||
|
||||
func NewURLTest(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.URLTestOutboundOptions) (*URLTest, error) {
|
||||
@ -55,6 +54,7 @@ func NewURLTest(ctx context.Context, router adapter.Router, logger log.ContextLo
|
||||
link: options.URL,
|
||||
interval: time.Duration(options.Interval),
|
||||
tolerance: options.Tolerance,
|
||||
idleTimeout: time.Duration(options.IdleTimeout),
|
||||
interruptExternalConnections: options.InterruptExistConnections,
|
||||
}
|
||||
if len(outbound.tags) == 0 {
|
||||
@ -79,13 +79,26 @@ func (s *URLTest) Start() error {
|
||||
}
|
||||
outbounds = append(outbounds, detour)
|
||||
}
|
||||
s.group = NewURLTestGroup(s.ctx, s.router, s.logger, outbounds, s.link, s.interval, s.tolerance, s.interruptExternalConnections)
|
||||
group, err := NewURLTestGroup(
|
||||
s.ctx,
|
||||
s.router,
|
||||
s.logger,
|
||||
outbounds,
|
||||
s.link,
|
||||
s.interval,
|
||||
s.tolerance,
|
||||
s.idleTimeout,
|
||||
s.interruptExternalConnections,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.group = group
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *URLTest) PostStart() error {
|
||||
s.started = true
|
||||
go s.CheckOutbounds()
|
||||
s.group.PostStart()
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -103,8 +116,8 @@ func (s *URLTest) All() []string {
|
||||
return s.tags
|
||||
}
|
||||
|
||||
func (s *URLTest) URLTest(ctx context.Context, link string) (map[string]uint16, error) {
|
||||
return s.group.URLTest(ctx, link)
|
||||
func (s *URLTest) URLTest(ctx context.Context) (map[string]uint16, error) {
|
||||
return s.group.URLTest(ctx)
|
||||
}
|
||||
|
||||
func (s *URLTest) CheckOutbounds() {
|
||||
@ -112,9 +125,7 @@ func (s *URLTest) CheckOutbounds() {
|
||||
}
|
||||
|
||||
func (s *URLTest) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||
if s.started {
|
||||
s.group.Start()
|
||||
}
|
||||
s.group.Touch()
|
||||
outbound := s.group.Select(network)
|
||||
conn, err := outbound.DialContext(ctx, network, destination)
|
||||
if err == nil {
|
||||
@ -126,9 +137,7 @@ func (s *URLTest) DialContext(ctx context.Context, network string, destination M
|
||||
}
|
||||
|
||||
func (s *URLTest) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||
if s.started {
|
||||
s.group.Start()
|
||||
}
|
||||
s.group.Touch()
|
||||
outbound := s.group.Select(N.NetworkUDP)
|
||||
conn, err := outbound.ListenPacket(ctx, destination)
|
||||
if err == nil {
|
||||
@ -162,6 +171,7 @@ type URLTestGroup struct {
|
||||
link string
|
||||
interval time.Duration
|
||||
tolerance uint16
|
||||
idleTimeout time.Duration
|
||||
history *urltest.HistoryStorage
|
||||
checking atomic.Bool
|
||||
pauseManager pause.Manager
|
||||
@ -173,6 +183,8 @@ type URLTestGroup struct {
|
||||
access sync.Mutex
|
||||
ticker *time.Ticker
|
||||
close chan struct{}
|
||||
started bool
|
||||
lastActive atomic.TypedValue[time.Time]
|
||||
}
|
||||
|
||||
func NewURLTestGroup(
|
||||
@ -183,14 +195,21 @@ func NewURLTestGroup(
|
||||
link string,
|
||||
interval time.Duration,
|
||||
tolerance uint16,
|
||||
idleTimeout time.Duration,
|
||||
interruptExternalConnections bool,
|
||||
) *URLTestGroup {
|
||||
) (*URLTestGroup, error) {
|
||||
if interval == 0 {
|
||||
interval = C.DefaultURLTestInterval
|
||||
}
|
||||
if tolerance == 0 {
|
||||
tolerance = 50
|
||||
}
|
||||
if idleTimeout == 0 {
|
||||
idleTimeout = C.DefaultURLTestIdleTimeout
|
||||
}
|
||||
if interval > idleTimeout {
|
||||
return nil, E.New("interval must be less or equal than idle_timeout")
|
||||
}
|
||||
var history *urltest.HistoryStorage
|
||||
if history = service.PtrFromContext[urltest.HistoryStorage](ctx); history != nil {
|
||||
} else if clashServer := router.ClashServer(); clashServer != nil {
|
||||
@ -206,16 +225,27 @@ func NewURLTestGroup(
|
||||
link: link,
|
||||
interval: interval,
|
||||
tolerance: tolerance,
|
||||
idleTimeout: idleTimeout,
|
||||
history: history,
|
||||
close: make(chan struct{}),
|
||||
pauseManager: pause.ManagerFromContext(ctx),
|
||||
interruptGroup: interrupt.NewGroup(),
|
||||
interruptExternalConnections: interruptExternalConnections,
|
||||
}
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (g *URLTestGroup) Start() {
|
||||
func (g *URLTestGroup) PostStart() {
|
||||
g.started = true
|
||||
g.lastActive.Store(time.Now())
|
||||
go g.CheckOutbounds(false)
|
||||
}
|
||||
|
||||
func (g *URLTestGroup) Touch() {
|
||||
if !g.started {
|
||||
return
|
||||
}
|
||||
if g.ticker != nil {
|
||||
g.lastActive.Store(time.Now())
|
||||
return
|
||||
}
|
||||
g.access.Lock()
|
||||
@ -266,51 +296,38 @@ func (g *URLTestGroup) Select(network string) adapter.Outbound {
|
||||
return minOutbound
|
||||
}
|
||||
|
||||
func (g *URLTestGroup) Fallback(used adapter.Outbound) []adapter.Outbound {
|
||||
outbounds := make([]adapter.Outbound, 0, len(g.outbounds)-1)
|
||||
for _, detour := range g.outbounds {
|
||||
if detour != used {
|
||||
outbounds = append(outbounds, detour)
|
||||
}
|
||||
}
|
||||
sort.SliceStable(outbounds, func(i, j int) bool {
|
||||
oi := outbounds[i]
|
||||
oj := outbounds[j]
|
||||
hi := g.history.LoadURLTestHistory(RealTag(oi))
|
||||
if hi == nil {
|
||||
return false
|
||||
}
|
||||
hj := g.history.LoadURLTestHistory(RealTag(oj))
|
||||
if hj == nil {
|
||||
return false
|
||||
}
|
||||
return hi.Delay < hj.Delay
|
||||
})
|
||||
return outbounds
|
||||
}
|
||||
|
||||
func (g *URLTestGroup) loopCheck() {
|
||||
go g.CheckOutbounds(true)
|
||||
if time.Now().Sub(g.lastActive.Load()) > g.interval {
|
||||
g.lastActive.Store(time.Now())
|
||||
g.CheckOutbounds(false)
|
||||
}
|
||||
for {
|
||||
g.pauseManager.WaitActive()
|
||||
select {
|
||||
case <-g.close:
|
||||
return
|
||||
case <-g.ticker.C:
|
||||
g.CheckOutbounds(false)
|
||||
}
|
||||
if time.Now().Sub(g.lastActive.Load()) > g.idleTimeout {
|
||||
g.access.Lock()
|
||||
g.ticker.Stop()
|
||||
g.ticker = nil
|
||||
g.access.Unlock()
|
||||
return
|
||||
}
|
||||
g.pauseManager.WaitActive()
|
||||
g.CheckOutbounds(false)
|
||||
}
|
||||
}
|
||||
|
||||
func (g *URLTestGroup) CheckOutbounds(force bool) {
|
||||
_, _ = g.urlTest(g.ctx, g.link, force)
|
||||
_, _ = g.urlTest(g.ctx, force)
|
||||
}
|
||||
|
||||
func (g *URLTestGroup) URLTest(ctx context.Context, link string) (map[string]uint16, error) {
|
||||
return g.urlTest(ctx, link, false)
|
||||
func (g *URLTestGroup) URLTest(ctx context.Context) (map[string]uint16, error) {
|
||||
return g.urlTest(ctx, false)
|
||||
}
|
||||
|
||||
func (g *URLTestGroup) urlTest(ctx context.Context, link string, force bool) (map[string]uint16, error) {
|
||||
func (g *URLTestGroup) urlTest(ctx context.Context, force bool) (map[string]uint16, error) {
|
||||
result := make(map[string]uint16)
|
||||
if g.checking.Swap(true) {
|
||||
return result, nil
|
||||
@ -337,7 +354,7 @@ func (g *URLTestGroup) urlTest(ctx context.Context, link string, force bool) (ma
|
||||
b.Go(realTag, func() (any, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), C.TCPTimeout)
|
||||
defer cancel()
|
||||
t, err := urltest.URLTest(ctx, link, p)
|
||||
t, err := urltest.URLTest(ctx, g.link, p)
|
||||
if err != nil {
|
||||
g.logger.Debug("outbound ", tag, " unavailable: ", err)
|
||||
g.history.DeleteURLTestHistory(realTag)
|
||||
|
@ -91,6 +91,7 @@ type Router struct {
|
||||
platformInterface platform.Interface
|
||||
needWIFIState bool
|
||||
wifiState adapter.WIFIState
|
||||
started bool
|
||||
}
|
||||
|
||||
func NewRouter(
|
||||
@ -145,6 +146,9 @@ func NewRouter(
|
||||
router.dnsRules = append(router.dnsRules, dnsRule)
|
||||
}
|
||||
for i, ruleSetOptions := range options.RuleSet {
|
||||
if _, exists := router.ruleSetMap[ruleSetOptions.Tag]; exists {
|
||||
return nil, E.New("duplicate rule-set tag: ", ruleSetOptions.Tag)
|
||||
}
|
||||
ruleSet, err := NewRuleSet(ctx, router, router.logger, ruleSetOptions)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "parse rule-set[", i, "]")
|
||||
@ -619,6 +623,19 @@ func (r *Router) Close() error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *Router) PostStart() error {
|
||||
if len(r.ruleSets) > 0 {
|
||||
for i, ruleSet := range r.ruleSets {
|
||||
err := ruleSet.PostStart()
|
||||
if err != nil {
|
||||
return E.Cause(err, "post start rule-set[", i, "]")
|
||||
}
|
||||
}
|
||||
}
|
||||
r.started = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Router) Outbound(tag string) (adapter.Outbound, bool) {
|
||||
outbound, loaded := r.outboundByTag[tag]
|
||||
return outbound, loaded
|
||||
@ -1073,8 +1090,11 @@ func (r *Router) notifyNetworkUpdate(event int) {
|
||||
}
|
||||
}
|
||||
|
||||
r.ResetNetwork()
|
||||
if !r.started {
|
||||
return
|
||||
}
|
||||
|
||||
_ = r.ResetNetwork()
|
||||
}
|
||||
|
||||
func (r *Router) ResetNetwork() error {
|
||||
|
@ -93,6 +93,12 @@ func (s *RemoteRuleSet) StartContext(ctx context.Context, startContext adapter.R
|
||||
s.lastEtag = savedSet.LastEtag
|
||||
}
|
||||
}
|
||||
if s.lastUpdated.IsZero() {
|
||||
err := s.fetchOnce(ctx, startContext)
|
||||
if err != nil {
|
||||
return E.Cause(err, "initial rule-set: ", s.options.Tag)
|
||||
}
|
||||
}
|
||||
s.updateTicker = time.NewTicker(s.updateInterval)
|
||||
go s.loopUpdate()
|
||||
return nil
|
||||
|
Loading…
x
Reference in New Issue
Block a user