mirror of
https://github.com/SagerNet/sing-box.git
synced 2025-06-13 21:54:13 +08:00
Compare commits
74 Commits
b1f24b0a32
...
d7a88a2ae3
Author | SHA1 | Date | |
---|---|---|---|
![]() |
d7a88a2ae3 | ||
![]() |
eb8170b4b7 | ||
![]() |
536ae39cc3 | ||
![]() |
579b92161b | ||
![]() |
4a25eb422a | ||
![]() |
75686656bf | ||
![]() |
aba73a2f58 | ||
![]() |
a63ba6e2a0 | ||
![]() |
2954a2653b | ||
![]() |
c903ed41ad | ||
![]() |
8c9eff621f | ||
![]() |
ca6d7226bb | ||
![]() |
193363d13e | ||
![]() |
345f3a5eb0 | ||
![]() |
ec0a3644c0 | ||
![]() |
d4f887f363 | ||
![]() |
c237f0aa22 | ||
![]() |
e92713989c | ||
![]() |
e78c98e8c3 | ||
![]() |
9d4e6b8d4d | ||
![]() |
eccf0e9392 | ||
![]() |
4ef4431217 | ||
![]() |
573eaedce2 | ||
![]() |
a206399047 | ||
![]() |
93f12a87f2 | ||
![]() |
ca27cf4e7a | ||
![]() |
fa13bb9d7c | ||
![]() |
2d3e02c2a3 | ||
![]() |
fb5000c6e7 | ||
![]() |
14cd3506db | ||
![]() |
2948be1d3d | ||
![]() |
4f1c62db4d | ||
![]() |
ee5739a395 | ||
![]() |
edd96dea15 | ||
![]() |
042ffecaf4 | ||
![]() |
9866cc065b | ||
![]() |
03de355135 | ||
![]() |
1a8645ca87 | ||
![]() |
8f76b733d6 | ||
![]() |
4dd206c756 | ||
![]() |
64e3071441 | ||
![]() |
121e5ed762 | ||
![]() |
49a83266d8 | ||
![]() |
3c853bd1d6 | ||
![]() |
0f94d53a01 | ||
![]() |
a6c98a3eb4 | ||
![]() |
085153438a | ||
![]() |
0fd71861cd | ||
![]() |
2b5e4ad830 | ||
![]() |
f6bdda4f79 | ||
![]() |
a99fbe9da6 | ||
![]() |
bda3583fe9 | ||
![]() |
59c1560616 | ||
![]() |
03322ba9d0 | ||
![]() |
3346a3ea0e | ||
![]() |
b810d48560 | ||
![]() |
9bbe116f53 | ||
![]() |
eafd7a7709 | ||
![]() |
8c495c4b4f | ||
![]() |
d1cdece862 | ||
![]() |
a08aa0ea4e | ||
![]() |
ae65178b8f | ||
![]() |
cdfef2bfd6 | ||
![]() |
fc7b188ec7 | ||
![]() |
4c4e61d0b5 | ||
![]() |
38009eed8a | ||
![]() |
e1689ce632 | ||
![]() |
ad632ad871 | ||
![]() |
8cd8aecc5a | ||
![]() |
9063e3a86e | ||
![]() |
d378904cf6 | ||
![]() |
1a66fcf634 | ||
![]() |
f891b0babb | ||
![]() |
12554e9433 |
2
.github/workflows/lint.yml
vendored
2
.github/workflows/lint.yml
vendored
@ -30,7 +30,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
go-version: ^1.24.2
|
go-version: ^1.24.2
|
||||||
- name: golangci-lint
|
- name: golangci-lint
|
||||||
uses: golangci/golangci-lint-action@v6
|
uses: golangci/golangci-lint-action@v7
|
||||||
with:
|
with:
|
||||||
version: latest
|
version: latest
|
||||||
args: --timeout=30m
|
args: --timeout=30m
|
||||||
|
@ -24,7 +24,7 @@ type Router interface {
|
|||||||
RuleSet(tag string) (RuleSet, bool)
|
RuleSet(tag string) (RuleSet, bool)
|
||||||
NeedWIFIState() bool
|
NeedWIFIState() bool
|
||||||
Rules() []Rule
|
Rules() []Rule
|
||||||
SetTracker(tracker ConnectionTracker)
|
AppendTracker(tracker ConnectionTracker)
|
||||||
ResetNetwork()
|
ResetNetwork()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
4
box.go
4
box.go
@ -314,7 +314,7 @@ func New(options Options) (*Box, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, E.Cause(err, "create clash-server")
|
return nil, E.Cause(err, "create clash-server")
|
||||||
}
|
}
|
||||||
router.SetTracker(clashServer)
|
router.AppendTracker(clashServer)
|
||||||
service.MustRegister[adapter.ClashServer](ctx, clashServer)
|
service.MustRegister[adapter.ClashServer](ctx, clashServer)
|
||||||
services = append(services, clashServer)
|
services = append(services, clashServer)
|
||||||
}
|
}
|
||||||
@ -324,7 +324,7 @@ func New(options Options) (*Box, error) {
|
|||||||
return nil, E.Cause(err, "create v2ray-server")
|
return nil, E.Cause(err, "create v2ray-server")
|
||||||
}
|
}
|
||||||
if v2rayServer.StatsService() != nil {
|
if v2rayServer.StatsService() != nil {
|
||||||
router.SetTracker(v2rayServer.StatsService())
|
router.AppendTracker(v2rayServer.StatsService())
|
||||||
services = append(services, v2rayServer)
|
services = append(services, v2rayServer)
|
||||||
service.MustRegister[adapter.V2RayServer](ctx, v2rayServer)
|
service.MustRegister[adapter.V2RayServer](ctx, v2rayServer)
|
||||||
}
|
}
|
||||||
|
@ -68,7 +68,9 @@ func UTP(_ context.Context, metadata *adapter.InboundContext, packet []byte) err
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if extension > 0x04 {
|
||||||
|
return os.ErrInvalid
|
||||||
|
}
|
||||||
var length byte
|
var length byte
|
||||||
err = binary.Read(reader, binary.BigEndian, &length)
|
err = binary.Read(reader, binary.BigEndian, &length)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -71,3 +71,19 @@ func TestSniffUDPTracker(t *testing.T) {
|
|||||||
require.Equal(t, C.ProtocolBitTorrent, metadata.Protocol)
|
require.Equal(t, C.ProtocolBitTorrent, metadata.Protocol)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSniffNotUTP(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
packets := []string{
|
||||||
|
"0102736470696e674958d580121500000000000079aaed6717a39c27b07c0c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
}
|
||||||
|
for _, pkt := range packets {
|
||||||
|
pkt, err := hex.DecodeString(pkt)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
var metadata adapter.InboundContext
|
||||||
|
err = sniff.UTP(context.TODO(), &metadata, pkt)
|
||||||
|
require.Error(t, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -123,6 +123,7 @@ func (s *STDECHClientConfig) ClientHandshake(ctx context.Context, conn net.Conn)
|
|||||||
if response.Rcode != mDNS.RcodeSuccess {
|
if response.Rcode != mDNS.RcodeSuccess {
|
||||||
return nil, E.Cause(dns.RcodeError(response.Rcode), "fetch ECH config list")
|
return nil, E.Cause(dns.RcodeError(response.Rcode), "fetch ECH config list")
|
||||||
}
|
}
|
||||||
|
match:
|
||||||
for _, rr := range response.Answer {
|
for _, rr := range response.Answer {
|
||||||
switch resource := rr.(type) {
|
switch resource := rr.(type) {
|
||||||
case *mDNS.HTTPS:
|
case *mDNS.HTTPS:
|
||||||
@ -133,11 +134,14 @@ func (s *STDECHClientConfig) ClientHandshake(ctx context.Context, conn net.Conn)
|
|||||||
return nil, E.Cause(err, "decode ECH config")
|
return nil, E.Cause(err, "decode ECH config")
|
||||||
}
|
}
|
||||||
s.config.EncryptedClientHelloConfigList = echConfigList
|
s.config.EncryptedClientHelloConfigList = echConfigList
|
||||||
|
break match
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, E.New("no ECH config found in DNS records")
|
if len(s.config.EncryptedClientHelloConfigList) == 0 {
|
||||||
|
return nil, E.New("no ECH config found in DNS records")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
tlsConn, err := s.Client(conn)
|
tlsConn, err := s.Client(conn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -11,7 +11,6 @@ const (
|
|||||||
ProtocolSSH = "ssh"
|
ProtocolSSH = "ssh"
|
||||||
ProtocolRDP = "rdp"
|
ProtocolRDP = "rdp"
|
||||||
ProtocolNTP = "ntp"
|
ProtocolNTP = "ntp"
|
||||||
ProtocolMTProto = "mtproto"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -212,8 +212,8 @@ type dnsConnection struct {
|
|||||||
|
|
||||||
func (c *dnsConnection) Close(err error) {
|
func (c *dnsConnection) Close(err error) {
|
||||||
c.closeOnce.Do(func() {
|
c.closeOnce.Do(func() {
|
||||||
close(c.done)
|
|
||||||
c.err = err
|
c.err = err
|
||||||
|
close(c.done)
|
||||||
})
|
})
|
||||||
c.Conn.Close()
|
c.Conn.Close()
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ If enabled in the inbound, the protocol and domain name (if present) of by the c
|
|||||||
|
|
||||||
| QUIC Client | Type |
|
| QUIC Client | Type |
|
||||||
|:------------------------:|:----------:|
|
|:------------------------:|:----------:|
|
||||||
| Chromium/Cronet | `chrimium` |
|
| Chromium/Cronet | `chromium` |
|
||||||
| Safari/Apple Network API | `safari` |
|
| Safari/Apple Network API | `safari` |
|
||||||
| Firefox / uquic firefox | `firefox` |
|
| Firefox / uquic firefox | `firefox` |
|
||||||
| quic-go / uquic chrome | `quic-go` |
|
| quic-go / uquic chrome | `quic-go` |
|
||||||
|
@ -39,7 +39,7 @@ type BoxService struct {
|
|||||||
clashServer adapter.ClashServer
|
clashServer adapter.ClashServer
|
||||||
pauseManager pause.Manager
|
pauseManager pause.Manager
|
||||||
|
|
||||||
servicePauseFields
|
iOSPauseFields
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewService(configContent string, platformInterface PlatformInterface) (*BoxService, error) {
|
func NewService(configContent string, platformInterface PlatformInterface) (*BoxService, error) {
|
||||||
|
@ -1,31 +1,33 @@
|
|||||||
package libbox
|
package libbox
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"sync"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
C "github.com/sagernet/sing-box/constant"
|
||||||
)
|
)
|
||||||
|
|
||||||
type servicePauseFields struct {
|
type iOSPauseFields struct {
|
||||||
pauseAccess sync.Mutex
|
endPauseTimer *time.Timer
|
||||||
pauseTimer *time.Timer
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *BoxService) Pause() {
|
func (s *BoxService) Pause() {
|
||||||
s.pauseAccess.Lock()
|
s.pauseManager.DevicePause()
|
||||||
defer s.pauseAccess.Unlock()
|
if !C.IsIos {
|
||||||
if s.pauseTimer != nil {
|
s.instance.Router().ResetNetwork()
|
||||||
s.pauseTimer.Stop()
|
} else {
|
||||||
|
if s.endPauseTimer == nil {
|
||||||
|
s.endPauseTimer = time.AfterFunc(time.Minute, s.pauseManager.DeviceWake)
|
||||||
|
} else {
|
||||||
|
s.endPauseTimer.Reset(time.Minute)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
s.pauseTimer = time.AfterFunc(3*time.Second, s.ResetNetwork)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *BoxService) Wake() {
|
func (s *BoxService) Wake() {
|
||||||
s.pauseAccess.Lock()
|
if !C.IsIos {
|
||||||
defer s.pauseAccess.Unlock()
|
s.pauseManager.DeviceWake()
|
||||||
if s.pauseTimer != nil {
|
s.instance.Router().ResetNetwork()
|
||||||
s.pauseTimer.Stop()
|
|
||||||
}
|
}
|
||||||
s.pauseTimer = time.AfterFunc(3*time.Minute, s.ResetNetwork)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *BoxService) ResetNetwork() {
|
func (s *BoxService) ResetNetwork() {
|
||||||
|
6
go.mod
6
go.mod
@ -9,7 +9,7 @@ require (
|
|||||||
github.com/cretz/bine v0.2.0
|
github.com/cretz/bine v0.2.0
|
||||||
github.com/go-chi/chi/v5 v5.2.1
|
github.com/go-chi/chi/v5 v5.2.1
|
||||||
github.com/go-chi/render v1.0.3
|
github.com/go-chi/render v1.0.3
|
||||||
github.com/gofrs/uuid/v5 v5.3.1
|
github.com/gofrs/uuid/v5 v5.3.2
|
||||||
github.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905
|
github.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905
|
||||||
github.com/libdns/alidns v1.0.3
|
github.com/libdns/alidns v1.0.3
|
||||||
github.com/libdns/cloudflare v0.1.1
|
github.com/libdns/cloudflare v0.1.1
|
||||||
@ -26,9 +26,9 @@ require (
|
|||||||
github.com/sagernet/gvisor v0.0.0-20250325023245-7a9c0f5725fb
|
github.com/sagernet/gvisor v0.0.0-20250325023245-7a9c0f5725fb
|
||||||
github.com/sagernet/quic-go v0.49.0-beta.1
|
github.com/sagernet/quic-go v0.49.0-beta.1
|
||||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691
|
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691
|
||||||
github.com/sagernet/sing v0.6.6-0.20250403102645-159e489fc399
|
github.com/sagernet/sing v0.6.6-0.20250406122223-d47540857e58
|
||||||
github.com/sagernet/sing-mux v0.3.1
|
github.com/sagernet/sing-mux v0.3.1
|
||||||
github.com/sagernet/sing-quic v0.4.1-beta.1
|
github.com/sagernet/sing-quic v0.4.1
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.7
|
github.com/sagernet/sing-shadowsocks v0.2.7
|
||||||
github.com/sagernet/sing-shadowsocks2 v0.2.0
|
github.com/sagernet/sing-shadowsocks2 v0.2.0
|
||||||
github.com/sagernet/sing-shadowtls v0.2.1-0.20250316154757-6f9e732e5056
|
github.com/sagernet/sing-shadowtls v0.2.1-0.20250316154757-6f9e732e5056
|
||||||
|
12
go.sum
12
go.sum
@ -66,8 +66,8 @@ github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
|
|||||||
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
||||||
github.com/godbus/dbus/v5 v5.1.1-0.20230522191255-76236955d466 h1:sQspH8M4niEijh3PFscJRLDnkL547IeP7kpPe3uUhEg=
|
github.com/godbus/dbus/v5 v5.1.1-0.20230522191255-76236955d466 h1:sQspH8M4niEijh3PFscJRLDnkL547IeP7kpPe3uUhEg=
|
||||||
github.com/godbus/dbus/v5 v5.1.1-0.20230522191255-76236955d466/go.mod h1:ZiQxhyQ+bbbfxUKVvjfO498oPYvtYhZzycal3G/NHmU=
|
github.com/godbus/dbus/v5 v5.1.1-0.20230522191255-76236955d466/go.mod h1:ZiQxhyQ+bbbfxUKVvjfO498oPYvtYhZzycal3G/NHmU=
|
||||||
github.com/gofrs/uuid/v5 v5.3.1 h1:aPx49MwJbekCzOyhZDjJVb0hx3A0KLjlbLx6p2gY0p0=
|
github.com/gofrs/uuid/v5 v5.3.2 h1:2jfO8j3XgSwlz/wHqemAEugfnTlikAYHhnqQ8Xh4fE0=
|
||||||
github.com/gofrs/uuid/v5 v5.3.1/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
|
github.com/gofrs/uuid/v5 v5.3.2/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||||
@ -178,12 +178,12 @@ github.com/sagernet/quic-go v0.49.0-beta.1/go.mod h1:uesWD1Ihrldq1M3XtjuEvIUqi8W
|
|||||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc=
|
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc=
|
||||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
|
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
|
||||||
github.com/sagernet/sing v0.2.18/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo=
|
github.com/sagernet/sing v0.2.18/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo=
|
||||||
github.com/sagernet/sing v0.6.6-0.20250403102645-159e489fc399 h1:jQ5sGyxICxdPqoakOEE6TbSTYOf/grmt31e/ad749O4=
|
github.com/sagernet/sing v0.6.6-0.20250406122223-d47540857e58 h1:hq0W1/K6/UOrv+tCm9YrZ/yDFZJlPVtLmBvayuwUxDM=
|
||||||
github.com/sagernet/sing v0.6.6-0.20250403102645-159e489fc399/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
github.com/sagernet/sing v0.6.6-0.20250406122223-d47540857e58/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||||
github.com/sagernet/sing-mux v0.3.1 h1:kvCc8HyGAskDHDQ0yQvoTi/7J4cZPB/VJMsAM3MmdQI=
|
github.com/sagernet/sing-mux v0.3.1 h1:kvCc8HyGAskDHDQ0yQvoTi/7J4cZPB/VJMsAM3MmdQI=
|
||||||
github.com/sagernet/sing-mux v0.3.1/go.mod h1:Mkdz8LnDstthz0HWuA/5foncnDIdcNN5KZ6AdJX+x78=
|
github.com/sagernet/sing-mux v0.3.1/go.mod h1:Mkdz8LnDstthz0HWuA/5foncnDIdcNN5KZ6AdJX+x78=
|
||||||
github.com/sagernet/sing-quic v0.4.1-beta.1 h1:V2VfMckT3EQR3ZdfSzJgZZDsvfZZH42QAZpnOnHKa0s=
|
github.com/sagernet/sing-quic v0.4.1 h1:pxlMa4efZu/M07RgGagNNDDyl6ZUwpmNUjRTpgHOWK4=
|
||||||
github.com/sagernet/sing-quic v0.4.1-beta.1/go.mod h1:c+CytOEyeN20KCTFIP8YQUkNDVFLSzjrEPqP7Hlnxys=
|
github.com/sagernet/sing-quic v0.4.1/go.mod h1:tqPa0/Wqa19MkkSlKVZZX5sHxtiDR9BROcn4ufcbVdY=
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.7 h1:zaopR1tbHEw5Nk6FAkM05wCslV6ahVegEZaKMv9ipx8=
|
github.com/sagernet/sing-shadowsocks v0.2.7 h1:zaopR1tbHEw5Nk6FAkM05wCslV6ahVegEZaKMv9ipx8=
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.7/go.mod h1:0rIKJZBR65Qi0zwdKezt4s57y/Tl1ofkaq6NlkzVuyE=
|
github.com/sagernet/sing-shadowsocks v0.2.7/go.mod h1:0rIKJZBR65Qi0zwdKezt4s57y/Tl1ofkaq6NlkzVuyE=
|
||||||
github.com/sagernet/sing-shadowsocks2 v0.2.0 h1:wpZNs6wKnR7mh1wV9OHwOyUr21VkS3wKFHi+8XwgADg=
|
github.com/sagernet/sing-shadowsocks2 v0.2.0 h1:wpZNs6wKnR7mh1wV9OHwOyUr21VkS3wKFHi+8XwgADg=
|
||||||
|
@ -19,6 +19,7 @@ import (
|
|||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
|
"github.com/sagernet/sing/common/x/list"
|
||||||
"github.com/sagernet/sing/service"
|
"github.com/sagernet/sing/service"
|
||||||
"github.com/sagernet/sing/service/pause"
|
"github.com/sagernet/sing/service/pause"
|
||||||
)
|
)
|
||||||
@ -27,10 +28,7 @@ func RegisterURLTest(registry *outbound.Registry) {
|
|||||||
outbound.Register[option.URLTestOutboundOptions](registry, C.TypeURLTest, NewURLTest)
|
outbound.Register[option.URLTestOutboundOptions](registry, C.TypeURLTest, NewURLTest)
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var _ adapter.OutboundGroup = (*URLTest)(nil)
|
||||||
_ adapter.OutboundGroup = (*URLTest)(nil)
|
|
||||||
_ adapter.InterfaceUpdateListener = (*URLTest)(nil)
|
|
||||||
)
|
|
||||||
|
|
||||||
type URLTest struct {
|
type URLTest struct {
|
||||||
outbound.Adapter
|
outbound.Adapter
|
||||||
@ -172,15 +170,12 @@ func (s *URLTest) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn,
|
|||||||
s.connection.NewPacketConnection(ctx, s, conn, metadata, onClose)
|
s.connection.NewPacketConnection(ctx, s, conn, metadata, onClose)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *URLTest) InterfaceUpdated() {
|
|
||||||
go s.group.CheckOutbounds(true)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
type URLTestGroup struct {
|
type URLTestGroup struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
router adapter.Router
|
router adapter.Router
|
||||||
outboundManager adapter.OutboundManager
|
outbound adapter.OutboundManager
|
||||||
|
pause pause.Manager
|
||||||
|
pauseCallback *list.Element[pause.Callback]
|
||||||
logger log.Logger
|
logger log.Logger
|
||||||
outbounds []adapter.Outbound
|
outbounds []adapter.Outbound
|
||||||
link string
|
link string
|
||||||
@ -189,17 +184,15 @@ type URLTestGroup struct {
|
|||||||
idleTimeout time.Duration
|
idleTimeout time.Duration
|
||||||
history adapter.URLTestHistoryStorage
|
history adapter.URLTestHistoryStorage
|
||||||
checking atomic.Bool
|
checking atomic.Bool
|
||||||
pauseManager pause.Manager
|
|
||||||
selectedOutboundTCP adapter.Outbound
|
selectedOutboundTCP adapter.Outbound
|
||||||
selectedOutboundUDP adapter.Outbound
|
selectedOutboundUDP adapter.Outbound
|
||||||
interruptGroup *interrupt.Group
|
interruptGroup *interrupt.Group
|
||||||
interruptExternalConnections bool
|
interruptExternalConnections bool
|
||||||
|
access sync.Mutex
|
||||||
access sync.Mutex
|
ticker *time.Ticker
|
||||||
ticker *time.Ticker
|
close chan struct{}
|
||||||
close chan struct{}
|
started bool
|
||||||
started bool
|
lastActive atomic.TypedValue[time.Time]
|
||||||
lastActive atomic.TypedValue[time.Time]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewURLTestGroup(ctx context.Context, outboundManager adapter.OutboundManager, logger log.Logger, outbounds []adapter.Outbound, link string, interval time.Duration, tolerance uint16, idleTimeout time.Duration, interruptExternalConnections bool) (*URLTestGroup, error) {
|
func NewURLTestGroup(ctx context.Context, outboundManager adapter.OutboundManager, logger log.Logger, outbounds []adapter.Outbound, link string, interval time.Duration, tolerance uint16, idleTimeout time.Duration, interruptExternalConnections bool) (*URLTestGroup, error) {
|
||||||
@ -225,7 +218,7 @@ func NewURLTestGroup(ctx context.Context, outboundManager adapter.OutboundManage
|
|||||||
}
|
}
|
||||||
return &URLTestGroup{
|
return &URLTestGroup{
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
outboundManager: outboundManager,
|
outbound: outboundManager,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
outbounds: outbounds,
|
outbounds: outbounds,
|
||||||
link: link,
|
link: link,
|
||||||
@ -234,13 +227,15 @@ func NewURLTestGroup(ctx context.Context, outboundManager adapter.OutboundManage
|
|||||||
idleTimeout: idleTimeout,
|
idleTimeout: idleTimeout,
|
||||||
history: history,
|
history: history,
|
||||||
close: make(chan struct{}),
|
close: make(chan struct{}),
|
||||||
pauseManager: service.FromContext[pause.Manager](ctx),
|
pause: service.FromContext[pause.Manager](ctx),
|
||||||
interruptGroup: interrupt.NewGroup(),
|
interruptGroup: interrupt.NewGroup(),
|
||||||
interruptExternalConnections: interruptExternalConnections,
|
interruptExternalConnections: interruptExternalConnections,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *URLTestGroup) PostStart() {
|
func (g *URLTestGroup) PostStart() {
|
||||||
|
g.access.Lock()
|
||||||
|
defer g.access.Unlock()
|
||||||
g.started = true
|
g.started = true
|
||||||
g.lastActive.Store(time.Now())
|
g.lastActive.Store(time.Now())
|
||||||
go g.CheckOutbounds(false)
|
go g.CheckOutbounds(false)
|
||||||
@ -250,24 +245,25 @@ func (g *URLTestGroup) Touch() {
|
|||||||
if !g.started {
|
if !g.started {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
g.access.Lock()
|
||||||
|
defer g.access.Unlock()
|
||||||
if g.ticker != nil {
|
if g.ticker != nil {
|
||||||
g.lastActive.Store(time.Now())
|
g.lastActive.Store(time.Now())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
g.access.Lock()
|
|
||||||
defer g.access.Unlock()
|
|
||||||
if g.ticker != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
g.ticker = time.NewTicker(g.interval)
|
g.ticker = time.NewTicker(g.interval)
|
||||||
go g.loopCheck()
|
go g.loopCheck()
|
||||||
|
g.pauseCallback = pause.RegisterTicker(g.pause, g.ticker, g.interval, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *URLTestGroup) Close() error {
|
func (g *URLTestGroup) Close() error {
|
||||||
|
g.access.Lock()
|
||||||
|
defer g.access.Unlock()
|
||||||
if g.ticker == nil {
|
if g.ticker == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
g.ticker.Stop()
|
g.ticker.Stop()
|
||||||
|
g.pause.UnregisterCallback(g.pauseCallback)
|
||||||
close(g.close)
|
close(g.close)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -331,10 +327,11 @@ func (g *URLTestGroup) loopCheck() {
|
|||||||
g.access.Lock()
|
g.access.Lock()
|
||||||
g.ticker.Stop()
|
g.ticker.Stop()
|
||||||
g.ticker = nil
|
g.ticker = nil
|
||||||
|
g.pause.UnregisterCallback(g.pauseCallback)
|
||||||
|
g.pauseCallback = nil
|
||||||
g.access.Unlock()
|
g.access.Unlock()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
g.pauseManager.WaitActive()
|
|
||||||
g.CheckOutbounds(false)
|
g.CheckOutbounds(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -367,7 +364,7 @@ func (g *URLTestGroup) urlTest(ctx context.Context, force bool) (map[string]uint
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
checked[realTag] = true
|
checked[realTag] = true
|
||||||
p, loaded := g.outboundManager.Outbound(realTag)
|
p, loaded := g.outbound.Outbound(realTag)
|
||||||
if !loaded {
|
if !loaded {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -129,7 +129,6 @@ func (w *Endpoint) Close() error {
|
|||||||
|
|
||||||
func (w *Endpoint) InterfaceUpdated() {
|
func (w *Endpoint) InterfaceUpdated() {
|
||||||
w.endpoint.BindUpdate()
|
w.endpoint.BindUpdate()
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Endpoint) PrepareConnection(network string, source M.Socksaddr, destination M.Socksaddr) error {
|
func (w *Endpoint) PrepareConnection(network string, source M.Socksaddr, destination M.Socksaddr) error {
|
||||||
|
@ -133,7 +133,6 @@ func (o *Outbound) Close() error {
|
|||||||
|
|
||||||
func (o *Outbound) InterfaceUpdated() {
|
func (o *Outbound) InterfaceUpdated() {
|
||||||
o.endpoint.BindUpdate()
|
o.endpoint.BindUpdate()
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *Outbound) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
func (o *Outbound) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||||
|
@ -59,10 +59,6 @@ func (r *Router) RouteConnectionEx(ctx context.Context, conn net.Conn, metadata
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *Router) routeConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) error {
|
func (r *Router) routeConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) error {
|
||||||
if r.pauseManager.IsDevicePaused() {
|
|
||||||
return E.New("reject connection to ", metadata.Destination, " while device paused")
|
|
||||||
}
|
|
||||||
|
|
||||||
//nolint:staticcheck
|
//nolint:staticcheck
|
||||||
if metadata.InboundDetour != "" {
|
if metadata.InboundDetour != "" {
|
||||||
if metadata.LastInbound == metadata.InboundDetour {
|
if metadata.LastInbound == metadata.InboundDetour {
|
||||||
@ -139,8 +135,8 @@ func (r *Router) routeConnection(ctx context.Context, conn net.Conn, metadata ad
|
|||||||
for _, buffer := range buffers {
|
for _, buffer := range buffers {
|
||||||
conn = bufio.NewCachedConn(conn, buffer)
|
conn = bufio.NewCachedConn(conn, buffer)
|
||||||
}
|
}
|
||||||
if r.tracker != nil {
|
for _, tracker := range r.trackers {
|
||||||
conn = r.tracker.RoutedConnection(ctx, conn, metadata, selectedRule, selectedOutbound)
|
conn = tracker.RoutedConnection(ctx, conn, metadata, selectedRule, selectedOutbound)
|
||||||
}
|
}
|
||||||
if outboundHandler, isHandler := selectedOutbound.(adapter.ConnectionHandlerEx); isHandler {
|
if outboundHandler, isHandler := selectedOutbound.(adapter.ConnectionHandlerEx); isHandler {
|
||||||
outboundHandler.NewConnectionEx(ctx, conn, metadata, onClose)
|
outboundHandler.NewConnectionEx(ctx, conn, metadata, onClose)
|
||||||
@ -185,9 +181,6 @@ func (r *Router) RoutePacketConnectionEx(ctx context.Context, conn N.PacketConn,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *Router) routePacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) error {
|
func (r *Router) routePacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) error {
|
||||||
if r.pauseManager.IsDevicePaused() {
|
|
||||||
return E.New("reject packet connection to ", metadata.Destination, " while device paused")
|
|
||||||
}
|
|
||||||
//nolint:staticcheck
|
//nolint:staticcheck
|
||||||
if metadata.InboundDetour != "" {
|
if metadata.InboundDetour != "" {
|
||||||
if metadata.LastInbound == metadata.InboundDetour {
|
if metadata.LastInbound == metadata.InboundDetour {
|
||||||
@ -257,8 +250,8 @@ func (r *Router) routePacketConnection(ctx context.Context, conn N.PacketConn, m
|
|||||||
conn = bufio.NewCachedPacketConn(conn, buffer.Buffer, buffer.Destination)
|
conn = bufio.NewCachedPacketConn(conn, buffer.Buffer, buffer.Destination)
|
||||||
N.PutPacketBuffer(buffer)
|
N.PutPacketBuffer(buffer)
|
||||||
}
|
}
|
||||||
if r.tracker != nil {
|
for _, tracker := range r.trackers {
|
||||||
conn = r.tracker.RoutedPacketConnection(ctx, conn, metadata, selectedRule, selectedOutbound)
|
conn = tracker.RoutedPacketConnection(ctx, conn, metadata, selectedRule, selectedOutbound)
|
||||||
}
|
}
|
||||||
if metadata.FakeIP {
|
if metadata.FakeIP {
|
||||||
conn = bufio.NewNATPacketConn(bufio.NewNetPacketConn(conn), metadata.OriginDestination, metadata.Destination)
|
conn = bufio.NewNATPacketConn(bufio.NewNetPacketConn(conn), metadata.OriginDestination, metadata.Destination)
|
||||||
|
@ -36,7 +36,7 @@ type Router struct {
|
|||||||
ruleSetMap map[string]adapter.RuleSet
|
ruleSetMap map[string]adapter.RuleSet
|
||||||
processSearcher process.Searcher
|
processSearcher process.Searcher
|
||||||
pauseManager pause.Manager
|
pauseManager pause.Manager
|
||||||
tracker adapter.ConnectionTracker
|
trackers []adapter.ConnectionTracker
|
||||||
platformInterface platform.Interface
|
platformInterface platform.Interface
|
||||||
needWIFIState bool
|
needWIFIState bool
|
||||||
started bool
|
started bool
|
||||||
@ -203,8 +203,8 @@ func (r *Router) Rules() []adapter.Rule {
|
|||||||
return r.rules
|
return r.rules
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Router) SetTracker(tracker adapter.ConnectionTracker) {
|
func (r *Router) AppendTracker(tracker adapter.ConnectionTracker) {
|
||||||
r.tracker = tracker
|
r.trackers = append(r.trackers, tracker)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Router) ResetNetwork() {
|
func (r *Router) ResetNetwork() {
|
||||||
|
@ -305,6 +305,9 @@ func (r *RuleActionReject) Error(ctx context.Context) error {
|
|||||||
default:
|
default:
|
||||||
panic(F.ToString("unknown reject method: ", r.Method))
|
panic(F.ToString("unknown reject method: ", r.Method))
|
||||||
}
|
}
|
||||||
|
if r.NoDrop {
|
||||||
|
return returnErr
|
||||||
|
}
|
||||||
r.dropAccess.Lock()
|
r.dropAccess.Lock()
|
||||||
defer r.dropAccess.Unlock()
|
defer r.dropAccess.Unlock()
|
||||||
timeNow := time.Now()
|
timeNow := time.Now()
|
||||||
|
@ -105,7 +105,7 @@ func (s *RemoteRuleSet) StartContext(ctx context.Context, startContext *adapter.
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if s.lastUpdated.IsZero() {
|
if s.lastUpdated.IsZero() {
|
||||||
err := s.fetchOnce(ctx, startContext)
|
err := s.fetch(ctx, startContext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return E.Cause(err, "initial rule-set: ", s.options.Tag)
|
return E.Cause(err, "initial rule-set: ", s.options.Tag)
|
||||||
}
|
}
|
||||||
@ -200,7 +200,7 @@ func (s *RemoteRuleSet) loadBytes(content []byte) error {
|
|||||||
|
|
||||||
func (s *RemoteRuleSet) loopUpdate() {
|
func (s *RemoteRuleSet) loopUpdate() {
|
||||||
if time.Since(s.lastUpdated) > s.updateInterval {
|
if time.Since(s.lastUpdated) > s.updateInterval {
|
||||||
err := s.fetchOnce(s.ctx, nil)
|
err := s.fetch(s.ctx, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("fetch rule-set ", s.options.Tag, ": ", err)
|
s.logger.Error("fetch rule-set ", s.options.Tag, ": ", err)
|
||||||
} else if s.refs.Load() == 0 {
|
} else if s.refs.Load() == 0 {
|
||||||
@ -213,18 +213,21 @@ func (s *RemoteRuleSet) loopUpdate() {
|
|||||||
case <-s.ctx.Done():
|
case <-s.ctx.Done():
|
||||||
return
|
return
|
||||||
case <-s.updateTicker.C:
|
case <-s.updateTicker.C:
|
||||||
s.pauseManager.WaitActive()
|
s.updateOnce()
|
||||||
err := s.fetchOnce(s.ctx, nil)
|
|
||||||
if err != nil {
|
|
||||||
s.logger.Error("fetch rule-set ", s.options.Tag, ": ", err)
|
|
||||||
} else if s.refs.Load() == 0 {
|
|
||||||
s.rules = nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *RemoteRuleSet) fetchOnce(ctx context.Context, startContext *adapter.HTTPStartContext) error {
|
func (s *RemoteRuleSet) updateOnce() {
|
||||||
|
err := s.fetch(s.ctx, nil)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("fetch rule-set ", s.options.Tag, ": ", err)
|
||||||
|
} else if s.refs.Load() == 0 {
|
||||||
|
s.rules = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RemoteRuleSet) fetch(ctx context.Context, startContext *adapter.HTTPStartContext) error {
|
||||||
s.logger.Debug("updating rule-set ", s.options.Tag, " from URL: ", s.options.RemoteOptions.URL)
|
s.logger.Debug("updating rule-set ", s.options.Tag, " from URL: ", s.options.RemoteOptions.URL)
|
||||||
var httpClient *http.Client
|
var httpClient *http.Client
|
||||||
if startContext != nil {
|
if startContext != nil {
|
||||||
|
@ -30,7 +30,7 @@ type Endpoint struct {
|
|||||||
allowedAddress []netip.Prefix
|
allowedAddress []netip.Prefix
|
||||||
tunDevice Device
|
tunDevice Device
|
||||||
device *device.Device
|
device *device.Device
|
||||||
pauseManager pause.Manager
|
pause pause.Manager
|
||||||
pauseCallback *list.Element[pause.Callback]
|
pauseCallback *list.Element[pause.Callback]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,9 +187,9 @@ func (e *Endpoint) Start(resolve bool) error {
|
|||||||
return E.Cause(err, "setup wireguard: \n", ipcConf)
|
return E.Cause(err, "setup wireguard: \n", ipcConf)
|
||||||
}
|
}
|
||||||
e.device = wgDevice
|
e.device = wgDevice
|
||||||
e.pauseManager = service.FromContext[pause.Manager](e.options.Context)
|
e.pause = service.FromContext[pause.Manager](e.options.Context)
|
||||||
if e.pauseManager != nil {
|
if e.pause != nil {
|
||||||
e.pauseCallback = e.pauseManager.RegisterCallback(e.onPauseUpdated)
|
e.pauseCallback = e.pause.RegisterCallback(e.onPauseUpdated)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -217,16 +217,16 @@ func (e *Endpoint) Close() error {
|
|||||||
e.device.Close()
|
e.device.Close()
|
||||||
}
|
}
|
||||||
if e.pauseCallback != nil {
|
if e.pauseCallback != nil {
|
||||||
e.pauseManager.UnregisterCallback(e.pauseCallback)
|
e.pause.UnregisterCallback(e.pauseCallback)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Endpoint) onPauseUpdated(event int) {
|
func (e *Endpoint) onPauseUpdated(event int) {
|
||||||
switch event {
|
switch event {
|
||||||
case pause.EventDevicePaused:
|
case pause.EventDevicePaused, pause.EventNetworkPause:
|
||||||
e.device.Down()
|
e.device.Down()
|
||||||
case pause.EventDeviceWake:
|
case pause.EventDeviceWake, pause.EventNetworkWake:
|
||||||
e.device.Up()
|
e.device.Up()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user