mirror of
https://github.com/SagerNet/sing-box.git
synced 2026-04-13 20:28:32 +10:00
Compare commits
33 Commits
v1.9.0-rc.
...
v1.9.0-rc.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8ce44b765f | ||
|
|
c2a9edaaa2 | ||
|
|
0a6c5d52a8 | ||
|
|
65db1f207e | ||
|
|
a82cbe4e59 | ||
|
|
77ecc5fd7c | ||
|
|
1f4d493bab | ||
|
|
eba3a6c51e | ||
|
|
513b41de2c | ||
|
|
27eeede81b | ||
|
|
1b3daf2afe | ||
|
|
29f962a4b0 | ||
|
|
4374e59f6e | ||
|
|
3090fb3b4a | ||
|
|
b1060ea1e7 | ||
|
|
fce989a5d9 | ||
|
|
5a8e548f02 | ||
|
|
7f2ff07a33 | ||
|
|
761d4699e4 | ||
|
|
5ecaf9f77f | ||
|
|
4331b97f01 | ||
|
|
bb39cef63f | ||
|
|
14375fe150 | ||
|
|
dec07cbf89 | ||
|
|
7c4c13c350 | ||
|
|
2c681c7425 | ||
|
|
89f3a22068 | ||
|
|
b5cf8377d1 | ||
|
|
9a84661861 | ||
|
|
7131de1e34 | ||
|
|
7a4a44c6d2 | ||
|
|
44277e5dd2 | ||
|
|
1f470c69c4 |
29
.github/workflows/docker.yml
vendored
29
.github/workflows/docker.yml
vendored
@@ -4,13 +4,35 @@ on:
|
||||
release:
|
||||
types:
|
||||
- released
|
||||
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: "The tag version you want to build"
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Get commit to build
|
||||
id: ref
|
||||
run: |-
|
||||
if [[ -z "${{ github.event.inputs.tag }}" ]]; then
|
||||
ref="${{ github.ref_name }}"
|
||||
else
|
||||
ref="${{ github.event.inputs.tag }}"
|
||||
fi
|
||||
echo "ref=$ref"
|
||||
echo "ref=$ref" >> $GITHUB_OUTPUT
|
||||
if [[ $ref == *"-"* ]]; then
|
||||
latest=latest-beta
|
||||
else
|
||||
latest=latest
|
||||
fi
|
||||
echo "latest=$latest"
|
||||
echo "latest=$latest" >> $GITHUB_OUTPUT
|
||||
- name: Checkout
|
||||
uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4
|
||||
with:
|
||||
ref: ${{ steps.ref.outputs.ref }}
|
||||
- name: Setup Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Setup QEMU for Docker Buildx
|
||||
@@ -30,10 +52,11 @@ jobs:
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
platforms: linux/386,linux/amd64,linux/arm64,linux/s390x
|
||||
context: .
|
||||
target: dist
|
||||
build-args: |
|
||||
BUILDKIT_CONTEXT_KEEP_GIT_DIR=1
|
||||
tags: |
|
||||
ghcr.io/sagernet/sing-box:latest
|
||||
ghcr.io/sagernet/sing-box:${{ github.ref_name }}
|
||||
ghcr.io/sagernet/sing-box:${{ steps.ref.outputs.latest }}
|
||||
ghcr.io/sagernet/sing-box:${{ steps.ref.outputs.ref }}
|
||||
push: true
|
||||
|
||||
2
.github/workflows/lint.yml
vendored
2
.github/workflows/lint.yml
vendored
@@ -30,7 +30,7 @@ jobs:
|
||||
with:
|
||||
go-version: ^1.22
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v5
|
||||
uses: golangci/golangci-lint-action@v6
|
||||
with:
|
||||
version: latest
|
||||
args: --timeout=30m
|
||||
|
||||
@@ -86,7 +86,7 @@ type DNSRule interface {
|
||||
Rule
|
||||
DisableCache() bool
|
||||
RewriteTTL() *uint32
|
||||
ClientSubnet() *netip.Addr
|
||||
ClientSubnet() *netip.Prefix
|
||||
WithAddressLimit() bool
|
||||
MatchAddressLimit(metadata *InboundContext) bool
|
||||
}
|
||||
|
||||
@@ -2,6 +2,22 @@
|
||||
icon: material/alert-decagram
|
||||
---
|
||||
|
||||
#### 1.9.0-rc.17
|
||||
|
||||
* Add custom prefix support in EDNS0 client subnet options
|
||||
* Fix hysteria2 crash
|
||||
* Fix `store_rdrc` corrupted
|
||||
* Fixes and improvements
|
||||
|
||||
#### 1.9.0-rc.16
|
||||
|
||||
* Mitigating TunnelVision attacks **1**
|
||||
* Fixes and improvements
|
||||
|
||||
**1**:
|
||||
|
||||
See [TunnelVision](/manual/misc/tunnelvision).
|
||||
|
||||
#### 1.9.0-rc.15
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
@@ -73,6 +73,8 @@ problematic in environments such as macOS, where DNS is proxied and cached by th
|
||||
|
||||
!!! question "Since sing-box 1.9.0"
|
||||
|
||||
Append a `edns0-subnet` OPT extra record with the specified IP address to every query by default.
|
||||
Append a `edns0-subnet` OPT extra record with the specified IP prefix to every query by default.
|
||||
|
||||
If value is an IP address instead of prefix, `/32` or `/128` will be appended automatically.
|
||||
|
||||
Can be overrides by `servers.[].client_subnet` or `rules.[].client_subnet`.
|
||||
|
||||
@@ -71,8 +71,10 @@ icon: material/new-box
|
||||
|
||||
!!! question "自 sing-box 1.9.0 起"
|
||||
|
||||
默认情况下,将带有指定 IP 地址的 `edns0-subnet` OPT 附加记录附加到每个查询。
|
||||
|
||||
默认情况下,将带有指定 IP 前缀的 `edns0-subnet` OPT 附加记录附加到每个查询。
|
||||
|
||||
如果值是 IP 地址而不是前缀,则会自动附加 `/32` 或 `/128`。
|
||||
|
||||
可以被 `servers.[].client_subnet` 或 `rules.[].client_subnet` 覆盖。
|
||||
|
||||
#### fakeip
|
||||
|
||||
@@ -125,7 +125,7 @@ icon: material/new-box
|
||||
"server": "local",
|
||||
"disable_cache": false,
|
||||
"rewrite_ttl": 100,
|
||||
"client_subnet": "127.0.0.1"
|
||||
"client_subnet": "127.0.0.1/24"
|
||||
},
|
||||
{
|
||||
"type": "logical",
|
||||
@@ -134,7 +134,7 @@ icon: material/new-box
|
||||
"server": "local",
|
||||
"disable_cache": false,
|
||||
"rewrite_ttl": 100,
|
||||
"client_subnet": "127.0.0.1"
|
||||
"client_subnet": "127.0.0.1/24"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -339,7 +339,9 @@ Rewrite TTL in DNS responses.
|
||||
|
||||
!!! question "Since sing-box 1.9.0"
|
||||
|
||||
Append a `edns0-subnet` OPT extra record with the specified IP address to every query by default.
|
||||
Append a `edns0-subnet` OPT extra record with the specified IP prefix to every query by default.
|
||||
|
||||
If value is an IP address instead of prefix, `/32` or `/128` will be appended automatically.
|
||||
|
||||
Will overrides `dns.client_subnet` and `servers.[].client_subnet`.
|
||||
|
||||
|
||||
@@ -124,7 +124,7 @@ icon: material/new-box
|
||||
],
|
||||
"server": "local",
|
||||
"disable_cache": false,
|
||||
"client_subnet": "127.0.0.1"
|
||||
"client_subnet": "127.0.0.1/24"
|
||||
},
|
||||
{
|
||||
"type": "logical",
|
||||
@@ -132,7 +132,7 @@ icon: material/new-box
|
||||
"rules": [],
|
||||
"server": "local",
|
||||
"disable_cache": false,
|
||||
"client_subnet": "127.0.0.1"
|
||||
"client_subnet": "127.0.0.1/24"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -337,7 +337,9 @@ DNS 查询类型。值可以为整数或者类型名称字符串。
|
||||
|
||||
!!! question "自 sing-box 1.9.0 起"
|
||||
|
||||
默认情况下,将带有指定 IP 地址的 `edns0-subnet` OPT 附加记录附加到每个查询。
|
||||
默认情况下,将带有指定 IP 前缀的 `edns0-subnet` OPT 附加记录附加到每个查询。
|
||||
|
||||
如果值是 IP 地址而不是前缀,则会自动附加 `/32` 或 `/128`。
|
||||
|
||||
将覆盖 `dns.client_subnet` 与 `servers.[].client_subnet`。
|
||||
|
||||
|
||||
@@ -100,7 +100,9 @@ Default outbound will be used if empty.
|
||||
|
||||
!!! question "Since sing-box 1.9.0"
|
||||
|
||||
Append a `edns0-subnet` OPT extra record with the specified IP address to every query by default.
|
||||
Append a `edns0-subnet` OPT extra record with the specified IP prefix to every query by default.
|
||||
|
||||
If value is an IP address instead of prefix, `/32` or `/128` will be appended automatically.
|
||||
|
||||
Can be overrides by `rules.[].client_subnet`.
|
||||
|
||||
|
||||
@@ -100,7 +100,9 @@ DNS 服务器的地址。
|
||||
|
||||
!!! question "自 sing-box 1.9.0 起"
|
||||
|
||||
默认情况下,将带有指定 IP 地址的 `edns0-subnet` OPT 附加记录附加到每个查询。
|
||||
默认情况下,将带有指定 IP 前缀的 `edns0-subnet` OPT 附加记录附加到每个查询。
|
||||
|
||||
如果值是 IP 地址而不是前缀,则会自动附加 `/32` 或 `/128`。
|
||||
|
||||
可以被 `rules.[].client_subnet` 覆盖。
|
||||
|
||||
|
||||
38
docs/manual/misc/tunnelvision.md
Normal file
38
docs/manual/misc/tunnelvision.md
Normal file
@@ -0,0 +1,38 @@
|
||||
---
|
||||
icon: material/book-lock-open
|
||||
---
|
||||
|
||||
# TunnelVision
|
||||
|
||||
TunnelVision is an attack that uses DHCP option 121 to set higher priority routes
|
||||
so that traffic does not go through the VPN.
|
||||
|
||||
Reference: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2024-3661
|
||||
|
||||
## Status
|
||||
|
||||
### Android
|
||||
|
||||
Android does not handle DHCP option 121 and is not affected.
|
||||
|
||||
### Apple platforms
|
||||
|
||||
Update [sing-box graphical client](/clients/apple/#download) to `1.9.0-rc.16` or newer,
|
||||
then enable `includeAllNetworks` in `Settings` — `Packet Tunnel` and you will be unaffected.
|
||||
|
||||
Note: when `includeAllNetworks` is enabled, the default TUN stack is changed to `gvisor`,
|
||||
and the `system` and `mixed` stacks are not available.
|
||||
|
||||
### Linux
|
||||
|
||||
Update sing-box to `1.9.0-rc.16` or newer, rules generated by `auto-route` are unaffected.
|
||||
|
||||
### Windows
|
||||
|
||||
No solution yet.
|
||||
|
||||
## Workarounds
|
||||
|
||||
* Don't connect to untrusted networks
|
||||
* Relay untrusted network through another device
|
||||
* Just ignore it
|
||||
@@ -1,208 +0,0 @@
|
||||
---
|
||||
icon: material/alpha-t-box
|
||||
---
|
||||
|
||||
# TUIC
|
||||
|
||||
A recently popular Chinese-made simple protocol based on QUIC, the selling point is the BBR congestion control algorithm.
|
||||
|
||||
!!! warning
|
||||
|
||||
Even though GFW rarely blocks UDP-based proxies, such protocols actually have far more characteristics than TCP based proxies.
|
||||
|
||||
| Specification | Binary Characteristics | Active Detect Hiddenness |
|
||||
|-----------------------------------------------------------|------------------------|--------------------------|
|
||||
| [GitHub](https://github.com/EAimTY/tuic/blob/dev/SPEC.md) | :material-alert: | :material-check: |
|
||||
|
||||
## Password Generator
|
||||
|
||||
| Generated UUID | Generated Password | Action |
|
||||
|------------------------|----------------------------|-----------------------------------------------------------------|
|
||||
| <code id="uuid"><code> | <code id="password"><code> | <button class="md-button" onclick="generate()">Refresh</button> |
|
||||
|
||||
<script>
|
||||
function generateUUID() {
|
||||
const uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
||||
let r = Math.random() * 16 | 0,
|
||||
v = c === 'x' ? r : (r & 0x3 | 0x8);
|
||||
return v.toString(16);
|
||||
});
|
||||
document.getElementById("uuid").textContent = uuid;
|
||||
}
|
||||
function generatePassword() {
|
||||
const array = new Uint8Array(16);
|
||||
window.crypto.getRandomValues(array);
|
||||
document.getElementById("password").textContent = btoa(String.fromCharCode.apply(null, array));
|
||||
}
|
||||
function generate() {
|
||||
generateUUID();
|
||||
generatePassword();
|
||||
}
|
||||
generate();
|
||||
</script>
|
||||
|
||||
## :material-server: Server Example
|
||||
|
||||
=== ":material-harddisk: With local certificate"
|
||||
|
||||
```json
|
||||
{
|
||||
"inbounds": [
|
||||
{
|
||||
"type": "tuic",
|
||||
"listen": "::",
|
||||
"listen_port": 8080,
|
||||
"users": [
|
||||
{
|
||||
"name": "sekai",
|
||||
"uuid": "<uuid>",
|
||||
"password": "<password>"
|
||||
}
|
||||
],
|
||||
"congestion_control": "bbr",
|
||||
"tls": {
|
||||
"enabled": true,
|
||||
"server_name": "example.org",
|
||||
"key_path": "/path/to/key.pem",
|
||||
"certificate_path": "/path/to/certificate.pem"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
=== ":material-auto-fix: With ACME"
|
||||
|
||||
```json
|
||||
{
|
||||
"inbounds": [
|
||||
{
|
||||
"type": "tuic",
|
||||
"listen": "::",
|
||||
"listen_port": 8080,
|
||||
"users": [
|
||||
{
|
||||
"name": "sekai",
|
||||
"uuid": "<uuid>",
|
||||
"password": "<password>"
|
||||
}
|
||||
],
|
||||
"congestion_control": "bbr",
|
||||
"tls": {
|
||||
"enabled": true,
|
||||
"server_name": "example.org",
|
||||
"acme": {
|
||||
"domain": "example.org",
|
||||
"email": "admin@example.org"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
=== ":material-cloud: With ACME and Cloudflare API"
|
||||
|
||||
```json
|
||||
{
|
||||
"inbounds": [
|
||||
{
|
||||
"type": "tuic",
|
||||
"listen": "::",
|
||||
"listen_port": 8080,
|
||||
"users": [
|
||||
{
|
||||
"name": "sekai",
|
||||
"uuid": "<uuid>",
|
||||
"password": "<password>"
|
||||
}
|
||||
],
|
||||
"congestion_control": "bbr",
|
||||
"tls": {
|
||||
"enabled": true,
|
||||
"server_name": "example.org",
|
||||
"acme": {
|
||||
"domain": "example.org",
|
||||
"email": "admin@example.org",
|
||||
"dns01_challenge": {
|
||||
"provider": "cloudflare",
|
||||
"api_token": "my_token"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## :material-cellphone-link: Client Example
|
||||
|
||||
=== ":material-web-check: With valid certificate"
|
||||
|
||||
```json
|
||||
{
|
||||
"outbounds": [
|
||||
{
|
||||
"type": "tuic",
|
||||
"server": "127.0.0.1",
|
||||
"server_port": 8080,
|
||||
"uuid": "<uuid>",
|
||||
"password": "<password>",
|
||||
"congestion_control": "bbr",
|
||||
"tls": {
|
||||
"enabled": true,
|
||||
"server_name": "example.org"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
=== ":material-check: With self-sign certificate"
|
||||
|
||||
!!! info "Tip"
|
||||
|
||||
Use `sing-box merge` command to merge configuration and certificate into one file.
|
||||
|
||||
```json
|
||||
{
|
||||
"outbounds": [
|
||||
{
|
||||
"type": "tuic",
|
||||
"server": "127.0.0.1",
|
||||
"server_port": 8080,
|
||||
"uuid": "<uuid>",
|
||||
"password": "<password>",
|
||||
"congestion_control": "bbr",
|
||||
"tls": {
|
||||
"enabled": true,
|
||||
"server_name": "example.org",
|
||||
"certificate_path": "/path/to/certificate.pem"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
=== ":material-alert: Ignore certificate verification"
|
||||
|
||||
```json
|
||||
{
|
||||
"outbounds": [
|
||||
{
|
||||
"type": "tuic",
|
||||
"server": "127.0.0.1",
|
||||
"server_port": 8080,
|
||||
"uuid": "<uuid>",
|
||||
"password": "<password>",
|
||||
"congestion_control": "bbr",
|
||||
"tls": {
|
||||
"enabled": true,
|
||||
"server_name": "example.org",
|
||||
"insecure": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
@@ -471,7 +471,7 @@ flowchart TB
|
||||
}
|
||||
],
|
||||
"server": "google",
|
||||
"client_subnet": "114.114.114.114" // Any China client IP address
|
||||
"client_subnet": "114.114.114.114/24" // Any China client IP address
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -57,6 +57,7 @@ type CacheFile struct {
|
||||
type saveRDRCCacheKey struct {
|
||||
TransportName string
|
||||
QuestionName string
|
||||
QType uint16
|
||||
}
|
||||
|
||||
func New(ctx context.Context, options option.CacheFileOptions) *CacheFile {
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
)
|
||||
|
||||
var bucketRDRC = []byte("rdrc")
|
||||
var bucketRDRC = []byte("rdrc2")
|
||||
|
||||
func (c *CacheFile) StoreRDRC() bool {
|
||||
return c.storeRDRC
|
||||
@@ -19,13 +19,17 @@ func (c *CacheFile) RDRCTimeout() time.Duration {
|
||||
return c.rdrcTimeout
|
||||
}
|
||||
|
||||
func (c *CacheFile) LoadRDRC(transportName string, qName string) (rejected bool) {
|
||||
func (c *CacheFile) LoadRDRC(transportName string, qName string, qType uint16) (rejected bool) {
|
||||
c.saveRDRCAccess.RLock()
|
||||
rejected, cached := c.saveRDRC[saveRDRCCacheKey{transportName, qName}]
|
||||
rejected, cached := c.saveRDRC[saveRDRCCacheKey{transportName, qName, qType}]
|
||||
c.saveRDRCAccess.RUnlock()
|
||||
if cached {
|
||||
return
|
||||
}
|
||||
key := buf.Get(2 + len(qName))
|
||||
binary.BigEndian.PutUint16(key, qType)
|
||||
copy(key[2:], qName)
|
||||
defer buf.Put(key)
|
||||
var deleteCache bool
|
||||
err := c.DB.View(func(tx *bbolt.Tx) error {
|
||||
bucket := c.bucket(tx, bucketRDRC)
|
||||
@@ -36,7 +40,7 @@ func (c *CacheFile) LoadRDRC(transportName string, qName string) (rejected bool)
|
||||
if bucket == nil {
|
||||
return nil
|
||||
}
|
||||
content := bucket.Get([]byte(qName))
|
||||
content := bucket.Get(key)
|
||||
if content == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -61,13 +65,13 @@ func (c *CacheFile) LoadRDRC(transportName string, qName string) (rejected bool)
|
||||
if bucket == nil {
|
||||
return nil
|
||||
}
|
||||
return bucket.Delete([]byte(qName))
|
||||
return bucket.Delete(key)
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *CacheFile) SaveRDRC(transportName string, qName string) error {
|
||||
func (c *CacheFile) SaveRDRC(transportName string, qName string, qType uint16) error {
|
||||
return c.DB.Batch(func(tx *bbolt.Tx) error {
|
||||
bucket, err := c.createBucket(tx, bucketRDRC)
|
||||
if err != nil {
|
||||
@@ -77,20 +81,24 @@ func (c *CacheFile) SaveRDRC(transportName string, qName string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
key := buf.Get(2 + len(qName))
|
||||
binary.BigEndian.PutUint16(key, qType)
|
||||
copy(key[2:], qName)
|
||||
defer buf.Put(key)
|
||||
expiresAt := buf.Get(8)
|
||||
defer buf.Put(expiresAt)
|
||||
binary.BigEndian.PutUint64(expiresAt, uint64(time.Now().Add(c.rdrcTimeout).Unix()))
|
||||
return bucket.Put([]byte(qName), expiresAt)
|
||||
return bucket.Put(key, expiresAt)
|
||||
})
|
||||
}
|
||||
|
||||
func (c *CacheFile) SaveRDRCAsync(transportName string, qName string, logger logger.Logger) {
|
||||
saveKey := saveRDRCCacheKey{transportName, qName}
|
||||
func (c *CacheFile) SaveRDRCAsync(transportName string, qName string, qType uint16, logger logger.Logger) {
|
||||
saveKey := saveRDRCCacheKey{transportName, qName, qType}
|
||||
c.saveRDRCAccess.Lock()
|
||||
c.saveRDRC[saveKey] = true
|
||||
c.saveRDRCAccess.Unlock()
|
||||
go func() {
|
||||
err := c.SaveRDRC(transportName, qName)
|
||||
err := c.SaveRDRC(transportName, qName, qType)
|
||||
if err != nil {
|
||||
logger.Warn("save RDRC: ", err)
|
||||
}
|
||||
|
||||
@@ -82,6 +82,10 @@ func (s *platformInterfaceStub) UnderNetworkExtension() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *platformInterfaceStub) IncludeAllNetworks() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *platformInterfaceStub) ClearDNSCache() {
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ type PlatformInterface interface {
|
||||
UsePlatformInterfaceGetter() bool
|
||||
GetInterfaces() (NetworkInterfaceIterator, error)
|
||||
UnderNetworkExtension() bool
|
||||
IncludeAllNetworks() bool
|
||||
ReadWIFIState() *WIFIState
|
||||
ClearDNSCache()
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ type Interface interface {
|
||||
UsePlatformInterfaceGetter() bool
|
||||
Interfaces() ([]control.Interface, error)
|
||||
UnderNetworkExtension() bool
|
||||
IncludeAllNetworks() bool
|
||||
ClearDNSCache()
|
||||
ReadWIFIState() adapter.WIFIState
|
||||
process.Searcher
|
||||
|
||||
@@ -213,6 +213,10 @@ func (w *platformInterfaceWrapper) UnderNetworkExtension() bool {
|
||||
return w.iif.UnderNetworkExtension()
|
||||
}
|
||||
|
||||
func (w *platformInterfaceWrapper) IncludeAllNetworks() bool {
|
||||
return w.iif.IncludeAllNetworks()
|
||||
}
|
||||
|
||||
func (w *platformInterfaceWrapper) ClearDNSCache() {
|
||||
w.iif.ClearDNSCache()
|
||||
}
|
||||
|
||||
6
go.mod
6
go.mod
@@ -27,13 +27,13 @@ require (
|
||||
github.com/sagernet/quic-go v0.43.0-beta.3
|
||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691
|
||||
github.com/sagernet/sing v0.4.0-beta.18
|
||||
github.com/sagernet/sing-dns v0.2.0-beta.16
|
||||
github.com/sagernet/sing-dns v0.2.0-beta.17
|
||||
github.com/sagernet/sing-mux v0.2.0
|
||||
github.com/sagernet/sing-quic v0.2.0-beta.1
|
||||
github.com/sagernet/sing-quic v0.2.0-beta.2
|
||||
github.com/sagernet/sing-shadowsocks v0.2.6
|
||||
github.com/sagernet/sing-shadowsocks2 v0.2.0
|
||||
github.com/sagernet/sing-shadowtls v0.1.4
|
||||
github.com/sagernet/sing-tun v0.2.8-beta.1
|
||||
github.com/sagernet/sing-tun v0.3.0-beta.2
|
||||
github.com/sagernet/sing-vmess v0.1.8
|
||||
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7
|
||||
github.com/sagernet/tfo-go v0.0.0-20231209031829-7b5343ac1dc6
|
||||
|
||||
12
go.sum
12
go.sum
@@ -108,20 +108,20 @@ github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4Wk
|
||||
github.com/sagernet/sing v0.2.18/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo=
|
||||
github.com/sagernet/sing v0.4.0-beta.18 h1:oK+pvyXnFwxwvQkeUqgxIeATiMHcrH5doLKKDGNmQkU=
|
||||
github.com/sagernet/sing v0.4.0-beta.18/go.mod h1:PFQKbElc2Pke7faBLv8oEba5ehtKO21Ho+TkYemTI3Y=
|
||||
github.com/sagernet/sing-dns v0.2.0-beta.16 h1:bzd4B8eHD7/WO3HrYknvgE8A56/R3n5oXBjNF97iPzQ=
|
||||
github.com/sagernet/sing-dns v0.2.0-beta.16/go.mod h1:XU6Vqr6aHcMz/34Fcv8jmXpRCEuShzW+B7Qg1Xe1nxY=
|
||||
github.com/sagernet/sing-dns v0.2.0-beta.17 h1:LYDdj+UzYAKF5AIjBe/8STU6Uq3cfbRQgNkgh2AxYOU=
|
||||
github.com/sagernet/sing-dns v0.2.0-beta.17/go.mod h1:k/dmFcQpg6+m08gC1yQBy+13+QkuLqpKr4bIreq4U24=
|
||||
github.com/sagernet/sing-mux v0.2.0 h1:4C+vd8HztJCWNYfufvgL49xaOoOHXty2+EAjnzN3IYo=
|
||||
github.com/sagernet/sing-mux v0.2.0/go.mod h1:khzr9AOPocLa+g53dBplwNDz4gdsyx/YM3swtAhlkHQ=
|
||||
github.com/sagernet/sing-quic v0.2.0-beta.1 h1:XR8KPYs50MNcFMR2/lh4eOonYeV15eJolAAWCQZpStI=
|
||||
github.com/sagernet/sing-quic v0.2.0-beta.1/go.mod h1:tVUFk5lcW22Bl0ChWlt4Lo93jw0qir3X1fk2ZSypaA4=
|
||||
github.com/sagernet/sing-quic v0.2.0-beta.2 h1:LPbjdiYd7jL1mqOYhGMBOu5GyKeAyaywTmbYMaPCOlc=
|
||||
github.com/sagernet/sing-quic v0.2.0-beta.2/go.mod h1:tVUFk5lcW22Bl0ChWlt4Lo93jw0qir3X1fk2ZSypaA4=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.6 h1:xr7ylAS/q1cQYS8oxKKajhuQcchd5VJJ4K4UZrrpp0s=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.6/go.mod h1:j2YZBIpWIuElPFL/5sJAj470bcn/3QQ5lxZUNKLDNAM=
|
||||
github.com/sagernet/sing-shadowsocks2 v0.2.0 h1:wpZNs6wKnR7mh1wV9OHwOyUr21VkS3wKFHi+8XwgADg=
|
||||
github.com/sagernet/sing-shadowsocks2 v0.2.0/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ=
|
||||
github.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnVpEx6Tw3k=
|
||||
github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4=
|
||||
github.com/sagernet/sing-tun v0.2.8-beta.1 h1:9loO5/Jqa+54NDi5mKh/CS6LlKsROCSnNQ+jgMcnI+I=
|
||||
github.com/sagernet/sing-tun v0.2.8-beta.1/go.mod h1:xPaOkQhngPMILx+/9DMLCFl4vSxUU2tMnCPSlf05HLo=
|
||||
github.com/sagernet/sing-tun v0.3.0-beta.2 h1:sfeHWnBTKGpFUjXpT+O/JEwFP8oVAo3M0Xx94ghesjU=
|
||||
github.com/sagernet/sing-tun v0.3.0-beta.2/go.mod h1:xPaOkQhngPMILx+/9DMLCFl4vSxUU2tMnCPSlf05HLo=
|
||||
github.com/sagernet/sing-vmess v0.1.8 h1:XVWad1RpTy9b5tPxdm5MCU8cGfrTGdR8qCq6HV2aCNc=
|
||||
github.com/sagernet/sing-vmess v0.1.8/go.mod h1:vhx32UNzTDUkNwOyIjcZQohre1CaytquC5mPplId8uA=
|
||||
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ=
|
||||
|
||||
@@ -166,6 +166,14 @@ func (t *Tun) Start() error {
|
||||
}
|
||||
t.logger.Trace("creating stack")
|
||||
t.tunIf = tunInterface
|
||||
var (
|
||||
forwarderBindInterface bool
|
||||
includeAllNetworks bool
|
||||
)
|
||||
if t.platformInterface != nil {
|
||||
forwarderBindInterface = true
|
||||
includeAllNetworks = t.platformInterface.IncludeAllNetworks()
|
||||
}
|
||||
t.tunStack, err = tun.NewStack(t.stack, tun.StackOptions{
|
||||
Context: t.ctx,
|
||||
Tun: tunInterface,
|
||||
@@ -174,8 +182,9 @@ func (t *Tun) Start() error {
|
||||
UDPTimeout: t.udpTimeout,
|
||||
Handler: t,
|
||||
Logger: t.logger,
|
||||
ForwarderBindInterface: t.platformInterface != nil,
|
||||
ForwarderBindInterface: forwarderBindInterface,
|
||||
InterfaceFinder: t.router.InterfaceFinder(),
|
||||
IncludeAllNetworks: includeAllNetworks,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -66,8 +66,9 @@ nav:
|
||||
- Proxy Protocol:
|
||||
- Shadowsocks: manual/proxy-protocol/shadowsocks.md
|
||||
- Trojan: manual/proxy-protocol/trojan.md
|
||||
- TUIC: manual/proxy-protocol/tuic.md
|
||||
- Hysteria 2: manual/proxy-protocol/hysteria2.md
|
||||
- Misc:
|
||||
- TunnelVision: manual/misc/tunnelvision.md
|
||||
- Configuration:
|
||||
- configuration/index.md
|
||||
- Log:
|
||||
|
||||
@@ -19,7 +19,7 @@ type DNSServerOptions struct {
|
||||
AddressFallbackDelay Duration `json:"address_fallback_delay,omitempty"`
|
||||
Strategy DomainStrategy `json:"strategy,omitempty"`
|
||||
Detour string `json:"detour,omitempty"`
|
||||
ClientSubnet *ListenAddress `json:"client_subnet,omitempty"`
|
||||
ClientSubnet *AddrPrefix `json:"client_subnet,omitempty"`
|
||||
}
|
||||
|
||||
type DNSClientOptions struct {
|
||||
@@ -27,7 +27,7 @@ type DNSClientOptions struct {
|
||||
DisableCache bool `json:"disable_cache,omitempty"`
|
||||
DisableExpire bool `json:"disable_expire,omitempty"`
|
||||
IndependentCache bool `json:"independent_cache,omitempty"`
|
||||
ClientSubnet *ListenAddress `json:"client_subnet,omitempty"`
|
||||
ClientSubnet *AddrPrefix `json:"client_subnet,omitempty"`
|
||||
}
|
||||
|
||||
type DNSFakeIPOptions struct {
|
||||
|
||||
@@ -101,7 +101,7 @@ type DefaultDNSRule struct {
|
||||
Server string `json:"server,omitempty"`
|
||||
DisableCache bool `json:"disable_cache,omitempty"`
|
||||
RewriteTTL *uint32 `json:"rewrite_ttl,omitempty"`
|
||||
ClientSubnet *ListenAddress `json:"client_subnet,omitempty"`
|
||||
ClientSubnet *AddrPrefix `json:"client_subnet,omitempty"`
|
||||
}
|
||||
|
||||
func (r DefaultDNSRule) IsValid() bool {
|
||||
@@ -115,13 +115,13 @@ func (r DefaultDNSRule) IsValid() bool {
|
||||
}
|
||||
|
||||
type LogicalDNSRule struct {
|
||||
Mode string `json:"mode"`
|
||||
Rules []DNSRule `json:"rules,omitempty"`
|
||||
Invert bool `json:"invert,omitempty"`
|
||||
Server string `json:"server,omitempty"`
|
||||
DisableCache bool `json:"disable_cache,omitempty"`
|
||||
RewriteTTL *uint32 `json:"rewrite_ttl,omitempty"`
|
||||
ClientSubnet *ListenAddress `json:"client_subnet,omitempty"`
|
||||
Mode string `json:"mode"`
|
||||
Rules []DNSRule `json:"rules,omitempty"`
|
||||
Invert bool `json:"invert,omitempty"`
|
||||
Server string `json:"server,omitempty"`
|
||||
DisableCache bool `json:"disable_cache,omitempty"`
|
||||
RewriteTTL *uint32 `json:"rewrite_ttl,omitempty"`
|
||||
ClientSubnet *AddrPrefix `json:"client_subnet,omitempty"`
|
||||
}
|
||||
|
||||
func (r LogicalDNSRule) IsValid() bool {
|
||||
|
||||
@@ -51,6 +51,40 @@ func (a *ListenAddress) Build() netip.Addr {
|
||||
return (netip.Addr)(*a)
|
||||
}
|
||||
|
||||
type AddrPrefix netip.Prefix
|
||||
|
||||
func (a AddrPrefix) MarshalJSON() ([]byte, error) {
|
||||
prefix := netip.Prefix(a)
|
||||
if prefix.Bits() == prefix.Addr().BitLen() {
|
||||
return json.Marshal(prefix.Addr().String())
|
||||
} else {
|
||||
return json.Marshal(prefix.String())
|
||||
}
|
||||
}
|
||||
|
||||
func (a *AddrPrefix) UnmarshalJSON(content []byte) error {
|
||||
var value string
|
||||
err := json.Unmarshal(content, &value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
prefix, prefixErr := netip.ParsePrefix(value)
|
||||
if prefixErr == nil {
|
||||
*a = AddrPrefix(prefix)
|
||||
return nil
|
||||
}
|
||||
addr, addrErr := netip.ParseAddr(value)
|
||||
if addrErr == nil {
|
||||
*a = AddrPrefix(netip.PrefixFrom(addr, addr.BitLen()))
|
||||
return nil
|
||||
}
|
||||
return prefixErr
|
||||
}
|
||||
|
||||
func (a AddrPrefix) Build() netip.Prefix {
|
||||
return netip.Prefix(a)
|
||||
}
|
||||
|
||||
type NetworkList string
|
||||
|
||||
func (v *NetworkList) UnmarshalJSON(content []byte) error {
|
||||
|
||||
@@ -287,8 +287,23 @@ func (g *URLTestGroup) Close() error {
|
||||
|
||||
func (g *URLTestGroup) Select(network string) (adapter.Outbound, bool) {
|
||||
var minDelay uint16
|
||||
var minTime time.Time
|
||||
var minOutbound adapter.Outbound
|
||||
switch network {
|
||||
case N.NetworkTCP:
|
||||
if g.selectedOutboundTCP != nil {
|
||||
if history := g.history.LoadURLTestHistory(RealTag(g.selectedOutboundTCP)); history != nil {
|
||||
minOutbound = g.selectedOutboundTCP
|
||||
minDelay = history.Delay
|
||||
}
|
||||
}
|
||||
case N.NetworkUDP:
|
||||
if g.selectedOutboundUDP != nil {
|
||||
if history := g.history.LoadURLTestHistory(RealTag(g.selectedOutboundUDP)); history != nil {
|
||||
minOutbound = g.selectedOutboundUDP
|
||||
minDelay = history.Delay
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, detour := range g.outbounds {
|
||||
if !common.Contains(detour.Network(), network) {
|
||||
continue
|
||||
@@ -297,9 +312,8 @@ func (g *URLTestGroup) Select(network string) (adapter.Outbound, bool) {
|
||||
if history == nil {
|
||||
continue
|
||||
}
|
||||
if minDelay == 0 || minDelay > history.Delay+g.tolerance || minDelay > history.Delay-g.tolerance && minTime.Before(history.Time) {
|
||||
if minDelay == 0 || minDelay > history.Delay+g.tolerance {
|
||||
minDelay = history.Delay
|
||||
minTime = history.Time
|
||||
minOutbound = detour
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ import (
|
||||
"github.com/sagernet/sing-box/outbound"
|
||||
"github.com/sagernet/sing-box/transport/fakeip"
|
||||
"github.com/sagernet/sing-dns"
|
||||
mux "github.com/sagernet/sing-mux"
|
||||
"github.com/sagernet/sing-mux"
|
||||
"github.com/sagernet/sing-tun"
|
||||
"github.com/sagernet/sing-vmess"
|
||||
"github.com/sagernet/sing/common"
|
||||
@@ -235,7 +235,7 @@ func NewRouter(
|
||||
return nil, E.New("parse dns server[", tag, "]: missing address_resolver")
|
||||
}
|
||||
}
|
||||
var clientSubnet netip.Addr
|
||||
var clientSubnet netip.Prefix
|
||||
if server.ClientSubnet != nil {
|
||||
clientSubnet = server.ClientSubnet.Build()
|
||||
} else if dnsOptions.ClientSubnet != nil {
|
||||
|
||||
@@ -40,7 +40,7 @@ type DefaultDNSRule struct {
|
||||
abstractDefaultRule
|
||||
disableCache bool
|
||||
rewriteTTL *uint32
|
||||
clientSubnet *netip.Addr
|
||||
clientSubnet *netip.Prefix
|
||||
}
|
||||
|
||||
func NewDefaultDNSRule(router adapter.Router, logger log.ContextLogger, options option.DefaultDNSRule) (*DefaultDNSRule, error) {
|
||||
@@ -51,7 +51,7 @@ func NewDefaultDNSRule(router adapter.Router, logger log.ContextLogger, options
|
||||
},
|
||||
disableCache: options.DisableCache,
|
||||
rewriteTTL: options.RewriteTTL,
|
||||
clientSubnet: (*netip.Addr)(options.ClientSubnet),
|
||||
clientSubnet: (*netip.Prefix)(options.ClientSubnet),
|
||||
}
|
||||
if len(options.Inbound) > 0 {
|
||||
item := NewInboundRule(options.Inbound)
|
||||
@@ -234,7 +234,7 @@ func (r *DefaultDNSRule) RewriteTTL() *uint32 {
|
||||
return r.rewriteTTL
|
||||
}
|
||||
|
||||
func (r *DefaultDNSRule) ClientSubnet() *netip.Addr {
|
||||
func (r *DefaultDNSRule) ClientSubnet() *netip.Prefix {
|
||||
return r.clientSubnet
|
||||
}
|
||||
|
||||
@@ -272,7 +272,7 @@ type LogicalDNSRule struct {
|
||||
abstractLogicalRule
|
||||
disableCache bool
|
||||
rewriteTTL *uint32
|
||||
clientSubnet *netip.Addr
|
||||
clientSubnet *netip.Prefix
|
||||
}
|
||||
|
||||
func NewLogicalDNSRule(router adapter.Router, logger log.ContextLogger, options option.LogicalDNSRule) (*LogicalDNSRule, error) {
|
||||
@@ -284,6 +284,7 @@ func NewLogicalDNSRule(router adapter.Router, logger log.ContextLogger, options
|
||||
},
|
||||
disableCache: options.DisableCache,
|
||||
rewriteTTL: options.RewriteTTL,
|
||||
clientSubnet: (*netip.Prefix)(options.ClientSubnet),
|
||||
}
|
||||
switch options.Mode {
|
||||
case C.LogicalTypeAnd:
|
||||
@@ -311,7 +312,7 @@ func (r *LogicalDNSRule) RewriteTTL() *uint32 {
|
||||
return r.rewriteTTL
|
||||
}
|
||||
|
||||
func (r *LogicalDNSRule) ClientSubnet() *netip.Addr {
|
||||
func (r *LogicalDNSRule) ClientSubnet() *netip.Prefix {
|
||||
return r.clientSubnet
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user