mirror of
https://github.com/SagerNet/sing-box.git
synced 2025-06-13 21:54:13 +08:00
Compare commits
74 Commits
f6cea5cc1e
...
1c36072120
Author | SHA1 | Date | |
---|---|---|---|
![]() |
1c36072120 | ||
![]() |
6e9258bede | ||
![]() |
b0f4008071 | ||
![]() |
3f26b7b4e9 | ||
![]() |
dbab3a2178 | ||
![]() |
dbf17cca71 | ||
![]() |
4a0fe88217 | ||
![]() |
4a62401f9e | ||
![]() |
09edc643cb | ||
![]() |
4be4220f20 | ||
![]() |
d1b0c967aa | ||
![]() |
7608266ea7 | ||
![]() |
081dad8f57 | ||
![]() |
3ff990a6a3 | ||
![]() |
9ff3da2bdb | ||
![]() |
92c20a545d | ||
![]() |
2e75cb3c22 | ||
![]() |
4414dc6bee | ||
![]() |
7d11245a90 | ||
![]() |
744e9b4577 | ||
![]() |
153a262f35 | ||
![]() |
39d2fb5467 | ||
![]() |
85a2a5c169 | ||
![]() |
575b5dfb10 | ||
![]() |
f19c500b4f | ||
![]() |
8e5958a7e4 | ||
![]() |
dc5e6ed488 | ||
![]() |
049c18377c | ||
![]() |
870f76a91e | ||
![]() |
d1a945f180 | ||
![]() |
6d56bc1528 | ||
![]() |
25e2d670e7 | ||
![]() |
be8419fa84 | ||
![]() |
27acc99c35 | ||
![]() |
6053a3bad1 | ||
![]() |
c63a58c0fb | ||
![]() |
cc4354b18a | ||
![]() |
8344653443 | ||
![]() |
26f185aa7c | ||
![]() |
a69007f1a2 | ||
![]() |
20729defd6 | ||
![]() |
ff751f8504 | ||
![]() |
689c229fe6 | ||
![]() |
c4cdb68d10 | ||
![]() |
dd99a45df6 | ||
![]() |
cbe077eaa9 | ||
![]() |
47fd88f450 | ||
![]() |
5fdfba6816 | ||
![]() |
e38de3bd8f | ||
![]() |
6f9d507ab3 | ||
![]() |
567de6aa88 | ||
![]() |
5fd7fba75f | ||
![]() |
d80f81157c | ||
![]() |
133e57e85f | ||
![]() |
f854586ac8 | ||
![]() |
a3a54b6e91 | ||
![]() |
fa5dbfcca8 | ||
![]() |
35d5a1f6a6 | ||
![]() |
9f26fea1fc | ||
![]() |
5c71baa23c | ||
![]() |
a75772d01e | ||
![]() |
ae263e2da3 | ||
![]() |
031b6de8b5 | ||
![]() |
6aef4ec1c6 | ||
![]() |
61b7666fd5 | ||
![]() |
ea5339285e | ||
![]() |
d38d58ff6e | ||
![]() |
60570b966b | ||
![]() |
195e859bf7 | ||
![]() |
0ce1f21794 | ||
![]() |
ec5eba1d25 | ||
![]() |
682a95c55e | ||
![]() |
34283f914a | ||
![]() |
fcce280a50 |
@ -74,6 +74,7 @@ type InboundContext struct {
|
|||||||
UDPTimeout time.Duration
|
UDPTimeout time.Duration
|
||||||
TLSFragment bool
|
TLSFragment bool
|
||||||
TLSFragmentFallbackDelay time.Duration
|
TLSFragmentFallbackDelay time.Duration
|
||||||
|
TLSRecordFragment bool
|
||||||
|
|
||||||
NetworkStrategy *C.NetworkStrategy
|
NetworkStrategy *C.NetworkStrategy
|
||||||
NetworkType []C.InterfaceType
|
NetworkType []C.InterfaceType
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
package tf
|
package tf
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/binary"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
@ -17,17 +19,19 @@ type Conn struct {
|
|||||||
tcpConn *net.TCPConn
|
tcpConn *net.TCPConn
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
firstPacketWritten bool
|
firstPacketWritten bool
|
||||||
|
splitRecord bool
|
||||||
fallbackDelay time.Duration
|
fallbackDelay time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewConn(conn net.Conn, ctx context.Context, fallbackDelay time.Duration) (*Conn, error) {
|
func NewConn(conn net.Conn, ctx context.Context, splitRecord bool, fallbackDelay time.Duration) *Conn {
|
||||||
tcpConn, _ := N.UnwrapReader(conn).(*net.TCPConn)
|
tcpConn, _ := N.UnwrapReader(conn).(*net.TCPConn)
|
||||||
return &Conn{
|
return &Conn{
|
||||||
Conn: conn,
|
Conn: conn,
|
||||||
tcpConn: tcpConn,
|
tcpConn: tcpConn,
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
|
splitRecord: splitRecord,
|
||||||
fallbackDelay: fallbackDelay,
|
fallbackDelay: fallbackDelay,
|
||||||
}, nil
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Conn) Write(b []byte) (n int, err error) {
|
func (c *Conn) Write(b []byte) (n int, err error) {
|
||||||
@ -37,10 +41,12 @@ func (c *Conn) Write(b []byte) (n int, err error) {
|
|||||||
}()
|
}()
|
||||||
serverName := indexTLSServerName(b)
|
serverName := indexTLSServerName(b)
|
||||||
if serverName != nil {
|
if serverName != nil {
|
||||||
if c.tcpConn != nil {
|
if !c.splitRecord {
|
||||||
err = c.tcpConn.SetNoDelay(true)
|
if c.tcpConn != nil {
|
||||||
if err != nil {
|
err = c.tcpConn.SetNoDelay(true)
|
||||||
return
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
splits := strings.Split(serverName.ServerName, ".")
|
splits := strings.Split(serverName.ServerName, ".")
|
||||||
@ -61,16 +67,25 @@ func (c *Conn) Write(b []byte) (n int, err error) {
|
|||||||
currentIndex++
|
currentIndex++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
var buffer bytes.Buffer
|
||||||
for i := 0; i <= len(splitIndexes); i++ {
|
for i := 0; i <= len(splitIndexes); i++ {
|
||||||
var payload []byte
|
var payload []byte
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
payload = b[:splitIndexes[i]]
|
payload = b[:splitIndexes[i]]
|
||||||
|
if c.splitRecord {
|
||||||
|
payload = payload[recordLayerHeaderLen:]
|
||||||
|
}
|
||||||
} else if i == len(splitIndexes) {
|
} else if i == len(splitIndexes) {
|
||||||
payload = b[splitIndexes[i-1]:]
|
payload = b[splitIndexes[i-1]:]
|
||||||
} else {
|
} else {
|
||||||
payload = b[splitIndexes[i-1]:splitIndexes[i]]
|
payload = b[splitIndexes[i-1]:splitIndexes[i]]
|
||||||
}
|
}
|
||||||
if c.tcpConn != nil && i != len(splitIndexes) {
|
if c.splitRecord {
|
||||||
|
payloadLen := uint16(len(payload))
|
||||||
|
buffer.Write(b[:3])
|
||||||
|
binary.Write(&buffer, binary.BigEndian, payloadLen)
|
||||||
|
buffer.Write(payload)
|
||||||
|
} else if c.tcpConn != nil && i != len(splitIndexes) {
|
||||||
err = writeAndWaitAck(c.ctx, c.tcpConn, payload, c.fallbackDelay)
|
err = writeAndWaitAck(c.ctx, c.tcpConn, payload, c.fallbackDelay)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
@ -82,11 +97,18 @@ func (c *Conn) Write(b []byte) (n int, err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if c.tcpConn != nil {
|
if c.splitRecord {
|
||||||
err = c.tcpConn.SetNoDelay(false)
|
_, err = c.tcpConn.Write(buffer.Bytes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if c.tcpConn != nil {
|
||||||
|
err = c.tcpConn.SetNoDelay(false)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return len(b), nil
|
return len(b), nil
|
||||||
}
|
}
|
||||||
|
32
common/tlsfragment/conn_test.go
Normal file
32
common/tlsfragment/conn_test.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package tf_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"net"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
tf "github.com/sagernet/sing-box/common/tlsfragment"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTLSFragment(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
tcpConn, err := net.Dial("tcp", "1.1.1.1:443")
|
||||||
|
require.NoError(t, err)
|
||||||
|
tlsConn := tls.Client(tf.NewConn(tcpConn, context.Background(), false, 0), &tls.Config{
|
||||||
|
ServerName: "www.cloudflare.com",
|
||||||
|
})
|
||||||
|
require.NoError(t, tlsConn.Handshake())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTLSRecordFragment(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
tcpConn, err := net.Dial("tcp", "1.1.1.1:443")
|
||||||
|
require.NoError(t, err)
|
||||||
|
tlsConn := tls.Client(tf.NewConn(tcpConn, context.Background(), true, 0), &tls.Config{
|
||||||
|
ServerName: "www.cloudflare.com",
|
||||||
|
})
|
||||||
|
require.NoError(t, tlsConn.Handshake())
|
||||||
|
}
|
@ -2,10 +2,20 @@
|
|||||||
icon: material/alert-decagram
|
icon: material/alert-decagram
|
||||||
---
|
---
|
||||||
|
|
||||||
#### 1.12.0-beta.12
|
#### 1.12.0-beta.13
|
||||||
|
|
||||||
|
* Add TLS record fragment route options **1**
|
||||||
|
* Add missing `accept_routes` option for Tailscale **2**
|
||||||
* Fixes and improvements
|
* Fixes and improvements
|
||||||
|
|
||||||
|
**1**:
|
||||||
|
|
||||||
|
See [Route Action](/configuration/route/rule_action/#tls_record_fragment).
|
||||||
|
|
||||||
|
**2**:
|
||||||
|
|
||||||
|
See [Tailscale](/configuration/endpoint/tailscale/#accept_routes).
|
||||||
|
|
||||||
#### 1.12.0-beta.10
|
#### 1.12.0-beta.10
|
||||||
|
|
||||||
* Add control options for listeners **1**
|
* Add control options for listeners **1**
|
||||||
|
@ -15,6 +15,7 @@ icon: material/new-box
|
|||||||
"control_url": "",
|
"control_url": "",
|
||||||
"ephemeral": false,
|
"ephemeral": false,
|
||||||
"hostname": "",
|
"hostname": "",
|
||||||
|
"accept_routes": false,
|
||||||
"exit_node": "",
|
"exit_node": "",
|
||||||
"exit_node_allow_lan_access": false,
|
"exit_node_allow_lan_access": false,
|
||||||
"advertise_routes": [],
|
"advertise_routes": [],
|
||||||
@ -62,6 +63,10 @@ System hostname is used by default.
|
|||||||
|
|
||||||
Example: `localhost`
|
Example: `localhost`
|
||||||
|
|
||||||
|
#### accept_routes
|
||||||
|
|
||||||
|
Indicates whether the node should accept routes advertised by other nodes.
|
||||||
|
|
||||||
#### exit_node
|
#### exit_node
|
||||||
|
|
||||||
The exit node name or IP address to use.
|
The exit node name or IP address to use.
|
||||||
|
@ -6,6 +6,7 @@ icon: material/new-box
|
|||||||
|
|
||||||
:material-plus: [tls_fragment](#tls_fragment)
|
:material-plus: [tls_fragment](#tls_fragment)
|
||||||
:material-plus: [tls_fragment_fallback_delay](#tls_fragment_fallback_delay)
|
:material-plus: [tls_fragment_fallback_delay](#tls_fragment_fallback_delay)
|
||||||
|
:material-plus: [tls_record_fragment](#tls_record_fragment)
|
||||||
:material-plus: [resolve.disable_cache](#disable_cache)
|
:material-plus: [resolve.disable_cache](#disable_cache)
|
||||||
:material-plus: [resolve.rewrite_ttl](#rewrite_ttl)
|
:material-plus: [resolve.rewrite_ttl](#rewrite_ttl)
|
||||||
:material-plus: [resolve.client_subnet](#client_subnet)
|
:material-plus: [resolve.client_subnet](#client_subnet)
|
||||||
@ -91,7 +92,8 @@ Not available when `method` is set to drop.
|
|||||||
"udp_connect": false,
|
"udp_connect": false,
|
||||||
"udp_timeout": "",
|
"udp_timeout": "",
|
||||||
"tls_fragment": false,
|
"tls_fragment": false,
|
||||||
"tls_fragment_fallback_delay": ""
|
"tls_fragment_fallback_delay": "",
|
||||||
|
"tls_record_fragment": ""
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -164,13 +166,19 @@ If no protocol is sniffed, the following ports will be recognized as protocols b
|
|||||||
|
|
||||||
Fragment TLS handshakes to bypass firewalls.
|
Fragment TLS handshakes to bypass firewalls.
|
||||||
|
|
||||||
This feature is intended to circumvent simple firewalls based on **plaintext packet matching**, and should not be used to circumvent real censorship.
|
This feature is intended to circumvent simple firewalls based on **plaintext packet matching**,
|
||||||
|
and should not be used to circumvent real censorship.
|
||||||
|
|
||||||
Since it is not designed for performance, it should not be applied to all connections, but only to server names that are known to be blocked.
|
Due to poor performance, try `tls_record_fragment` first, and only apply to server names known to be blocked.
|
||||||
|
|
||||||
On Linux, Apple platforms, (administrator privileges required) Windows, the wait time can be automatically detected, otherwise it will fall back to waiting for a fixed time specified by `tls_fragment_fallback_delay`.
|
On Linux, Apple platforms, (administrator privileges required) Windows,
|
||||||
|
the wait time can be automatically detected, otherwise it will fall back to
|
||||||
|
waiting for a fixed time specified by `tls_fragment_fallback_delay`.
|
||||||
|
|
||||||
In addition, if the actual wait time is less than 20ms, it will also fall back to waiting for a fixed time, because the target is considered to be local or behind a transparent proxy.
|
In addition, if the actual wait time is less than 20ms, it will also fall back to waiting for a fixed time,
|
||||||
|
because the target is considered to be local or behind a transparent proxy.
|
||||||
|
|
||||||
|
Conflict with `tls_record_fragment`.
|
||||||
|
|
||||||
#### tls_fragment_fallback_delay
|
#### tls_fragment_fallback_delay
|
||||||
|
|
||||||
@ -180,6 +188,17 @@ The fallback value used when TLS segmentation cannot automatically determine the
|
|||||||
|
|
||||||
`500ms` is used by default.
|
`500ms` is used by default.
|
||||||
|
|
||||||
|
#### tls_record_fragment
|
||||||
|
|
||||||
|
!!! question "Since sing-box 1.12.0"
|
||||||
|
|
||||||
|
Fragment TLS handshake into multiple TLS records to bypass firewalls.
|
||||||
|
|
||||||
|
This feature is intended to circumvent simple firewalls based on **plaintext packet matching**,
|
||||||
|
and should not be used to circumvent real censorship.
|
||||||
|
|
||||||
|
Conflict with `tls_fragment`.
|
||||||
|
|
||||||
### sniff
|
### sniff
|
||||||
|
|
||||||
```json
|
```json
|
||||||
|
@ -5,7 +5,11 @@ icon: material/new-box
|
|||||||
!!! quote "sing-box 1.12.0 中的更改"
|
!!! quote "sing-box 1.12.0 中的更改"
|
||||||
|
|
||||||
:material-plus: [tls_fragment](#tls_fragment)
|
:material-plus: [tls_fragment](#tls_fragment)
|
||||||
:material-plus: [tls_fragment_fallback_delay](#tls_fragment_fallback_delay)
|
:material-plus: [tls_fragment_fallback_delay](#tls_fragment_fallback_delay)
|
||||||
|
:material-plus: [tls_record_fragment](#tls_record_fragment)
|
||||||
|
:material-plus: [resolve.disable_cache](#disable_cache)
|
||||||
|
:material-plus: [resolve.rewrite_ttl](#rewrite_ttl)
|
||||||
|
:material-plus: [resolve.client_subnet](#client_subnet)
|
||||||
|
|
||||||
## 最终动作
|
## 最终动作
|
||||||
|
|
||||||
@ -159,12 +163,15 @@ UDP 连接超时时间。
|
|||||||
|
|
||||||
此功能旨在规避基于**明文数据包匹配**的简单防火墙,不应该用于规避真的审查。
|
此功能旨在规避基于**明文数据包匹配**的简单防火墙,不应该用于规避真的审查。
|
||||||
|
|
||||||
由于它不是为性能设计的,不应被应用于所有连接,而仅应用于已知被阻止的服务器名称。
|
由于性能不佳,请首先尝试 `tls_record_fragment`,且仅应用于已知被阻止的服务器名称。
|
||||||
|
|
||||||
在 Linux、Apple 平台和需要管理员权限的 Windows 系统上,可自动检测等待时间。若无法自动检测,将回退使用 `tls_fragment_fallback_delay` 指定的固定等待时间。
|
在 Linux、Apple 平台和需要管理员权限的 Windows 系统上,可自动检测等待时间。
|
||||||
|
若无法自动检测,将回退使用 `tls_fragment_fallback_delay` 指定的固定等待时间。
|
||||||
|
|
||||||
此外,若实际等待时间小于 20 毫秒,同样会回退至固定等待时间模式,因为此时判定目标处于本地或透明代理之后。
|
此外,若实际等待时间小于 20 毫秒,同样会回退至固定等待时间模式,因为此时判定目标处于本地或透明代理之后。
|
||||||
|
|
||||||
|
与 `tls_record_fragment` 冲突。
|
||||||
|
|
||||||
#### tls_fragment_fallback_delay
|
#### tls_fragment_fallback_delay
|
||||||
|
|
||||||
!!! question "自 sing-box 1.12.0 起"
|
!!! question "自 sing-box 1.12.0 起"
|
||||||
@ -173,6 +180,16 @@ UDP 连接超时时间。
|
|||||||
|
|
||||||
默认使用 `500ms`。
|
默认使用 `500ms`。
|
||||||
|
|
||||||
|
#### tls_record_fragment
|
||||||
|
|
||||||
|
!!! question "自 sing-box 1.12.0 起"
|
||||||
|
|
||||||
|
通过分段 TLS 握手数据包到多个 TLS 记录来绕过防火墙检测。
|
||||||
|
|
||||||
|
此功能旨在规避基于**明文数据包匹配**的简单防火墙,不应该用于规避真的审查。
|
||||||
|
|
||||||
|
与 `tls_fragment` 冲突。
|
||||||
|
|
||||||
### sniff
|
### sniff
|
||||||
|
|
||||||
```json
|
```json
|
||||||
|
5
go.mod
5
go.mod
@ -28,14 +28,15 @@ require (
|
|||||||
github.com/sagernet/quic-go v0.51.0-beta.5
|
github.com/sagernet/quic-go v0.51.0-beta.5
|
||||||
github.com/sagernet/sing v0.6.10-0.20250505040842-ba62fee9470f
|
github.com/sagernet/sing v0.6.10-0.20250505040842-ba62fee9470f
|
||||||
github.com/sagernet/sing-mux v0.3.2
|
github.com/sagernet/sing-mux v0.3.2
|
||||||
github.com/sagernet/sing-quic v0.4.1-0.20250507070325-d2fb1cb09565
|
github.com/sagernet/sing-quic v0.4.1-0.20250511050139-d459f561c9c3
|
||||||
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.20250503051639-fcd445d33c11
|
github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11
|
||||||
github.com/sagernet/sing-tun v0.6.6-0.20250428031943-0686f8c4f210
|
github.com/sagernet/sing-tun v0.6.6-0.20250428031943-0686f8c4f210
|
||||||
github.com/sagernet/sing-vmess v0.2.2-0.20250503051933-9b4cf17393f8
|
github.com/sagernet/sing-vmess v0.2.2-0.20250503051933-9b4cf17393f8
|
||||||
github.com/sagernet/smux v1.5.34-mod.2
|
github.com/sagernet/smux v1.5.34-mod.2
|
||||||
github.com/sagernet/tailscale v1.80.3-mod.4
|
github.com/sagernet/tailscale v1.80.3-mod.5
|
||||||
|
github.com/sagernet/utls v1.6.7
|
||||||
github.com/sagernet/wireguard-go v0.0.1-beta.7
|
github.com/sagernet/wireguard-go v0.0.1-beta.7
|
||||||
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854
|
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854
|
||||||
github.com/spf13/cobra v1.9.1
|
github.com/spf13/cobra v1.9.1
|
||||||
|
10
go.sum
10
go.sum
@ -172,8 +172,8 @@ github.com/sagernet/sing v0.6.10-0.20250505040842-ba62fee9470f h1:lttLhNtFuMItQc
|
|||||||
github.com/sagernet/sing v0.6.10-0.20250505040842-ba62fee9470f/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
github.com/sagernet/sing v0.6.10-0.20250505040842-ba62fee9470f/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||||
github.com/sagernet/sing-mux v0.3.2 h1:meZVFiiStvHThb/trcpAkCrmtJOuItG5Dzl1RRP5/NE=
|
github.com/sagernet/sing-mux v0.3.2 h1:meZVFiiStvHThb/trcpAkCrmtJOuItG5Dzl1RRP5/NE=
|
||||||
github.com/sagernet/sing-mux v0.3.2/go.mod h1:pht8iFY4c9Xltj7rhVd208npkNaeCxzyXCgulDPLUDA=
|
github.com/sagernet/sing-mux v0.3.2/go.mod h1:pht8iFY4c9Xltj7rhVd208npkNaeCxzyXCgulDPLUDA=
|
||||||
github.com/sagernet/sing-quic v0.4.1-0.20250507070325-d2fb1cb09565 h1:svvkxc3NtONBrvB2rrNVmFmFYoBv+jORDOFX6tvFDjY=
|
github.com/sagernet/sing-quic v0.4.1-0.20250511050139-d459f561c9c3 h1:1J+s1yyZ8+YAYaClI+az8YuFgV9NGXUUCZnriKmos6w=
|
||||||
github.com/sagernet/sing-quic v0.4.1-0.20250507070325-d2fb1cb09565/go.mod h1:6K3ESuaXFTjz2Dv6/PNQqg5UK0J1ZO49rqrU2ScZBKg=
|
github.com/sagernet/sing-quic v0.4.1-0.20250511050139-d459f561c9c3/go.mod h1:Mv7CdSyLepmqoLT8rd88Qn3QMv5AbsgjEm3DvEhDVNE=
|
||||||
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=
|
||||||
@ -186,8 +186,10 @@ github.com/sagernet/sing-vmess v0.2.2-0.20250503051933-9b4cf17393f8 h1:zW+zAOCxU
|
|||||||
github.com/sagernet/sing-vmess v0.2.2-0.20250503051933-9b4cf17393f8/go.mod h1:IL8Rr+EGwuqijszZkNrEFTQDKhilEpkqFqOlvdpS6/w=
|
github.com/sagernet/sing-vmess v0.2.2-0.20250503051933-9b4cf17393f8/go.mod h1:IL8Rr+EGwuqijszZkNrEFTQDKhilEpkqFqOlvdpS6/w=
|
||||||
github.com/sagernet/smux v1.5.34-mod.2 h1:gkmBjIjlJ2zQKpLigOkFur5kBKdV6bNRoFu2WkltRQ4=
|
github.com/sagernet/smux v1.5.34-mod.2 h1:gkmBjIjlJ2zQKpLigOkFur5kBKdV6bNRoFu2WkltRQ4=
|
||||||
github.com/sagernet/smux v1.5.34-mod.2/go.mod h1:0KW0+R+ycvA2INW4gbsd7BNyg+HEfLIAxa5N02/28Zc=
|
github.com/sagernet/smux v1.5.34-mod.2/go.mod h1:0KW0+R+ycvA2INW4gbsd7BNyg+HEfLIAxa5N02/28Zc=
|
||||||
github.com/sagernet/tailscale v1.80.3-mod.4 h1:9UgYq8m9mwX5dbTbueVxbRh+bq7AayxemJGM2PkJQnE=
|
github.com/sagernet/tailscale v1.80.3-mod.4.0.20250512093633-e1bc1888c814 h1:B6ejgOuM1BrX4TzWvm1h/LQAOZW1T1jP4PSZe8b/49o=
|
||||||
github.com/sagernet/tailscale v1.80.3-mod.4/go.mod h1:EBxXsWu4OH2ELbQLq32WoBeIubG8KgDrg4/Oaxjs6lI=
|
github.com/sagernet/tailscale v1.80.3-mod.4.0.20250512093633-e1bc1888c814/go.mod h1:EBxXsWu4OH2ELbQLq32WoBeIubG8KgDrg4/Oaxjs6lI=
|
||||||
|
github.com/sagernet/tailscale v1.80.3-mod.5 h1:7V7z+p2C//TGtff20pPnDCt3qP6uFyY62peJoKF9z/A=
|
||||||
|
github.com/sagernet/tailscale v1.80.3-mod.5/go.mod h1:EBxXsWu4OH2ELbQLq32WoBeIubG8KgDrg4/Oaxjs6lI=
|
||||||
github.com/sagernet/wireguard-go v0.0.1-beta.7 h1:ltgBwYHfr+9Wz1eG59NiWnHrYEkDKHG7otNZvu85DXI=
|
github.com/sagernet/wireguard-go v0.0.1-beta.7 h1:ltgBwYHfr+9Wz1eG59NiWnHrYEkDKHG7otNZvu85DXI=
|
||||||
github.com/sagernet/wireguard-go v0.0.1-beta.7/go.mod h1:jGXij2Gn2wbrWuYNUmmNhf1dwcZtvyAvQoe8Xd8MbUo=
|
github.com/sagernet/wireguard-go v0.0.1-beta.7/go.mod h1:jGXij2Gn2wbrWuYNUmmNhf1dwcZtvyAvQoe8Xd8MbUo=
|
||||||
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 h1:6uUiZcDRnZSAegryaUGwPC/Fj13JSHwiTftrXhMmYOc=
|
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 h1:6uUiZcDRnZSAegryaUGwPC/Fj13JSHwiTftrXhMmYOc=
|
||||||
|
@ -158,6 +158,7 @@ type RawRouteOptionsActionOptions struct {
|
|||||||
|
|
||||||
TLSFragment bool `json:"tls_fragment,omitempty"`
|
TLSFragment bool `json:"tls_fragment,omitempty"`
|
||||||
TLSFragmentFallbackDelay badoption.Duration `json:"tls_fragment_fallback_delay,omitempty"`
|
TLSFragmentFallbackDelay badoption.Duration `json:"tls_fragment_fallback_delay,omitempty"`
|
||||||
|
TLSRecordFragment bool `json:"tls_record_fragment,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type RouteOptionsActionOptions RawRouteOptionsActionOptions
|
type RouteOptionsActionOptions RawRouteOptionsActionOptions
|
||||||
@ -170,6 +171,9 @@ func (r *RouteOptionsActionOptions) UnmarshalJSON(data []byte) error {
|
|||||||
if *r == (RouteOptionsActionOptions{}) {
|
if *r == (RouteOptionsActionOptions{}) {
|
||||||
return E.New("empty route option action")
|
return E.New("empty route option action")
|
||||||
}
|
}
|
||||||
|
if r.TLSFragment && r.TLSRecordFragment {
|
||||||
|
return E.New("`tls_fragment` and `tls_record_fragment` are mutually exclusive")
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ type TailscaleEndpointOptions struct {
|
|||||||
ControlURL string `json:"control_url,omitempty"`
|
ControlURL string `json:"control_url,omitempty"`
|
||||||
Ephemeral bool `json:"ephemeral,omitempty"`
|
Ephemeral bool `json:"ephemeral,omitempty"`
|
||||||
Hostname string `json:"hostname,omitempty"`
|
Hostname string `json:"hostname,omitempty"`
|
||||||
|
AcceptRoutes bool `json:"accept_routes,omitempty"`
|
||||||
ExitNode string `json:"exit_node,omitempty"`
|
ExitNode string `json:"exit_node,omitempty"`
|
||||||
ExitNodeAllowLANAccess bool `json:"exit_node_allow_lan_access,omitempty"`
|
ExitNodeAllowLANAccess bool `json:"exit_node_allow_lan_access,omitempty"`
|
||||||
AdvertiseRoutes []netip.Prefix `json:"advertise_routes,omitempty"`
|
AdvertiseRoutes []netip.Prefix `json:"advertise_routes,omitempty"`
|
||||||
|
@ -72,6 +72,7 @@ type Endpoint struct {
|
|||||||
filter *atomic.Pointer[filter.Filter]
|
filter *atomic.Pointer[filter.Filter]
|
||||||
onReconfig wgengine.ReconfigListener
|
onReconfig wgengine.ReconfigListener
|
||||||
|
|
||||||
|
acceptRoutes bool
|
||||||
exitNode string
|
exitNode string
|
||||||
exitNodeAllowLANAccess bool
|
exitNodeAllowLANAccess bool
|
||||||
advertiseRoutes []netip.Prefix
|
advertiseRoutes []netip.Prefix
|
||||||
@ -170,6 +171,7 @@ func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextL
|
|||||||
network: service.FromContext[adapter.NetworkManager](ctx),
|
network: service.FromContext[adapter.NetworkManager](ctx),
|
||||||
platformInterface: service.FromContext[platform.Interface](ctx),
|
platformInterface: service.FromContext[platform.Interface](ctx),
|
||||||
server: server,
|
server: server,
|
||||||
|
acceptRoutes: options.AcceptRoutes,
|
||||||
exitNode: options.ExitNode,
|
exitNode: options.ExitNode,
|
||||||
exitNodeAllowLANAccess: options.ExitNodeAllowLANAccess,
|
exitNodeAllowLANAccess: options.ExitNodeAllowLANAccess,
|
||||||
advertiseRoutes: options.AdvertiseRoutes,
|
advertiseRoutes: options.AdvertiseRoutes,
|
||||||
@ -226,6 +228,10 @@ func (t *Endpoint) Start(stage adapter.StartStage) error {
|
|||||||
|
|
||||||
localBackend := t.server.ExportLocalBackend()
|
localBackend := t.server.ExportLocalBackend()
|
||||||
perfs := &ipn.MaskedPrefs{
|
perfs := &ipn.MaskedPrefs{
|
||||||
|
Prefs: ipn.Prefs{
|
||||||
|
RouteAll: t.acceptRoutes,
|
||||||
|
},
|
||||||
|
RouteAllSet: true,
|
||||||
ExitNodeIPSet: true,
|
ExitNodeIPSet: true,
|
||||||
AdvertiseRoutesSet: true,
|
AdvertiseRoutesSet: true,
|
||||||
}
|
}
|
||||||
|
@ -95,15 +95,9 @@ func (m *ConnectionManager) NewConnection(ctx context.Context, this N.Dialer, co
|
|||||||
if fallbackDelay == 0 {
|
if fallbackDelay == 0 {
|
||||||
fallbackDelay = C.TLSFragmentFallbackDelay
|
fallbackDelay = C.TLSFragmentFallbackDelay
|
||||||
}
|
}
|
||||||
var newConn *tf.Conn
|
remoteConn = tf.NewConn(remoteConn, ctx, false, fallbackDelay)
|
||||||
newConn, err = tf.NewConn(remoteConn, ctx, fallbackDelay)
|
} else if metadata.TLSRecordFragment {
|
||||||
if err != nil {
|
remoteConn = tf.NewConn(remoteConn, ctx, true, 0)
|
||||||
conn.Close()
|
|
||||||
remoteConn.Close()
|
|
||||||
m.logger.ErrorContext(ctx, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
remoteConn = newConn
|
|
||||||
}
|
}
|
||||||
m.access.Lock()
|
m.access.Lock()
|
||||||
element := m.connections.PushBack(conn)
|
element := m.connections.PushBack(conn)
|
||||||
|
@ -40,6 +40,7 @@ func NewRuleAction(ctx context.Context, logger logger.ContextLogger, action opti
|
|||||||
UDPConnect: action.RouteOptions.UDPConnect,
|
UDPConnect: action.RouteOptions.UDPConnect,
|
||||||
TLSFragment: action.RouteOptions.TLSFragment,
|
TLSFragment: action.RouteOptions.TLSFragment,
|
||||||
TLSFragmentFallbackDelay: time.Duration(action.RouteOptions.TLSFragmentFallbackDelay),
|
TLSFragmentFallbackDelay: time.Duration(action.RouteOptions.TLSFragmentFallbackDelay),
|
||||||
|
TLSRecordFragment: action.RouteOptions.TLSRecordFragment,
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
case C.RuleActionTypeRouteOptions:
|
case C.RuleActionTypeRouteOptions:
|
||||||
@ -53,6 +54,7 @@ func NewRuleAction(ctx context.Context, logger logger.ContextLogger, action opti
|
|||||||
UDPTimeout: time.Duration(action.RouteOptionsOptions.UDPTimeout),
|
UDPTimeout: time.Duration(action.RouteOptionsOptions.UDPTimeout),
|
||||||
TLSFragment: action.RouteOptionsOptions.TLSFragment,
|
TLSFragment: action.RouteOptionsOptions.TLSFragment,
|
||||||
TLSFragmentFallbackDelay: time.Duration(action.RouteOptionsOptions.TLSFragmentFallbackDelay),
|
TLSFragmentFallbackDelay: time.Duration(action.RouteOptionsOptions.TLSFragmentFallbackDelay),
|
||||||
|
TLSRecordFragment: action.RouteOptionsOptions.TLSRecordFragment,
|
||||||
}, nil
|
}, nil
|
||||||
case C.RuleActionTypeDirect:
|
case C.RuleActionTypeDirect:
|
||||||
directDialer, err := dialer.New(ctx, option.DialerOptions(action.DirectOptions), false)
|
directDialer, err := dialer.New(ctx, option.DialerOptions(action.DirectOptions), false)
|
||||||
@ -152,15 +154,7 @@ func (r *RuleActionRoute) Type() string {
|
|||||||
func (r *RuleActionRoute) String() string {
|
func (r *RuleActionRoute) String() string {
|
||||||
var descriptions []string
|
var descriptions []string
|
||||||
descriptions = append(descriptions, r.Outbound)
|
descriptions = append(descriptions, r.Outbound)
|
||||||
if r.UDPDisableDomainUnmapping {
|
descriptions = append(descriptions, r.Descriptions()...)
|
||||||
descriptions = append(descriptions, "udp-disable-domain-unmapping")
|
|
||||||
}
|
|
||||||
if r.UDPConnect {
|
|
||||||
descriptions = append(descriptions, "udp-connect")
|
|
||||||
}
|
|
||||||
if r.TLSFragment {
|
|
||||||
descriptions = append(descriptions, "tls-fragment")
|
|
||||||
}
|
|
||||||
return F.ToString("route(", strings.Join(descriptions, ","), ")")
|
return F.ToString("route(", strings.Join(descriptions, ","), ")")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,6 +170,7 @@ type RuleActionRouteOptions struct {
|
|||||||
UDPTimeout time.Duration
|
UDPTimeout time.Duration
|
||||||
TLSFragment bool
|
TLSFragment bool
|
||||||
TLSFragmentFallbackDelay time.Duration
|
TLSFragmentFallbackDelay time.Duration
|
||||||
|
TLSRecordFragment bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RuleActionRouteOptions) Type() string {
|
func (r *RuleActionRouteOptions) Type() string {
|
||||||
@ -183,6 +178,10 @@ func (r *RuleActionRouteOptions) Type() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *RuleActionRouteOptions) String() string {
|
func (r *RuleActionRouteOptions) String() string {
|
||||||
|
return F.ToString("route-options(", strings.Join(r.Descriptions(), ","), ")")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RuleActionRouteOptions) Descriptions() []string {
|
||||||
var descriptions []string
|
var descriptions []string
|
||||||
if r.OverrideAddress.IsValid() {
|
if r.OverrideAddress.IsValid() {
|
||||||
descriptions = append(descriptions, F.ToString("override-address=", r.OverrideAddress.AddrString()))
|
descriptions = append(descriptions, F.ToString("override-address=", r.OverrideAddress.AddrString()))
|
||||||
@ -211,7 +210,16 @@ func (r *RuleActionRouteOptions) String() string {
|
|||||||
if r.UDPTimeout > 0 {
|
if r.UDPTimeout > 0 {
|
||||||
descriptions = append(descriptions, "udp-timeout")
|
descriptions = append(descriptions, "udp-timeout")
|
||||||
}
|
}
|
||||||
return F.ToString("route-options(", strings.Join(descriptions, ","), ")")
|
if r.TLSFragment {
|
||||||
|
descriptions = append(descriptions, "tls-fragment")
|
||||||
|
}
|
||||||
|
if r.TLSFragmentFallbackDelay > 0 {
|
||||||
|
descriptions = append(descriptions, F.ToString("tls-fragment-fallback-delay=", r.TLSFragmentFallbackDelay.String()))
|
||||||
|
}
|
||||||
|
if r.TLSRecordFragment {
|
||||||
|
descriptions = append(descriptions, "tls-record-fragment")
|
||||||
|
}
|
||||||
|
return descriptions
|
||||||
}
|
}
|
||||||
|
|
||||||
type RuleActionDNSRoute struct {
|
type RuleActionDNSRoute struct {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user