mirror of
https://github.com/shtorm-7/sing-box-extended.git
synced 2026-06-21 18:29:13 +03:00
Compare commits
4 Commits
v1.12.12-e
...
v1.12.12-e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
287fe834db | ||
|
|
d7f0cea4ff | ||
|
|
d8b470d1ba | ||
|
|
984fc295b3 |
@@ -10,6 +10,7 @@ Sing-box with extended features.
|
||||
* Mieru
|
||||
* XHTTP
|
||||
* SDNS (DNSCrypt)
|
||||
* Extended Wireguard options
|
||||
* Unified delay
|
||||
|
||||
## Examples
|
||||
|
||||
@@ -13,7 +13,7 @@ const (
|
||||
Size = 8192
|
||||
)
|
||||
|
||||
var zero = [Size * 10]byte{0}
|
||||
var ErrBufferFull = E.New("buffer is full")
|
||||
|
||||
var pool = bytespool.GetPool(Size)
|
||||
|
||||
@@ -144,7 +144,7 @@ func (b *Buffer) Bytes() []byte {
|
||||
}
|
||||
|
||||
// Extend increases the buffer size by n bytes, and returns the extended part.
|
||||
// It panics if result size is larger than buf.Size.
|
||||
// It panics if result size is larger than size of this buffer.
|
||||
func (b *Buffer) Extend(n int32) []byte {
|
||||
end := b.end + n
|
||||
if end > int32(len(b.v)) {
|
||||
@@ -152,7 +152,7 @@ func (b *Buffer) Extend(n int32) []byte {
|
||||
}
|
||||
ext := b.v[b.end:end]
|
||||
b.end = end
|
||||
copy(ext, zero[:])
|
||||
clear(ext)
|
||||
return ext
|
||||
}
|
||||
|
||||
@@ -215,7 +215,7 @@ func (b *Buffer) Resize(from, to int32) {
|
||||
b.start += from
|
||||
b.Check()
|
||||
if b.end > oldEnd {
|
||||
copy(b.v[oldEnd:b.end], zero[:])
|
||||
clear(b.v[oldEnd:b.end])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,6 +244,14 @@ func (b *Buffer) Cap() int32 {
|
||||
return int32(len(b.v))
|
||||
}
|
||||
|
||||
// Available returns the available capacity of the buffer content.
|
||||
func (b *Buffer) Available() int32 {
|
||||
if b == nil {
|
||||
return 0
|
||||
}
|
||||
return int32(len(b.v)) - b.end
|
||||
}
|
||||
|
||||
// IsEmpty returns true if the buffer is empty.
|
||||
func (b *Buffer) IsEmpty() bool {
|
||||
return b.Len() == 0
|
||||
@@ -258,13 +266,16 @@ func (b *Buffer) IsFull() bool {
|
||||
func (b *Buffer) Write(data []byte) (int, error) {
|
||||
nBytes := copy(b.v[b.end:], data)
|
||||
b.end += int32(nBytes)
|
||||
if nBytes < len(data) {
|
||||
return nBytes, ErrBufferFull
|
||||
}
|
||||
return nBytes, nil
|
||||
}
|
||||
|
||||
// WriteByte writes a single byte into the buffer.
|
||||
func (b *Buffer) WriteByte(v byte) error {
|
||||
if b.IsFull() {
|
||||
return E.New("buffer full")
|
||||
return ErrBufferFull
|
||||
}
|
||||
b.v[b.end] = v
|
||||
b.end++
|
||||
|
||||
@@ -22,6 +22,7 @@ var ErrReadTimeout = E.New("IO timeout")
|
||||
|
||||
// TimeoutReader is a reader that returns error if Read() operation takes longer than the given timeout.
|
||||
type TimeoutReader interface {
|
||||
Reader
|
||||
ReadMultiBufferTimeout(time.Duration) (MultiBuffer, error)
|
||||
}
|
||||
|
||||
|
||||
@@ -144,7 +144,7 @@ func Compact(mb MultiBuffer) MultiBuffer {
|
||||
|
||||
for i := 1; i < len(mb); i++ {
|
||||
curr := mb[i]
|
||||
if last.Len()+curr.Len() > Size {
|
||||
if curr.Len() > last.Available() {
|
||||
mb2 = append(mb2, last)
|
||||
last = curr
|
||||
} else {
|
||||
|
||||
@@ -75,9 +75,10 @@ func (w *BufferToBytesWriter) ReadFrom(reader io.Reader) (int64, error) {
|
||||
// BufferedWriter is a Writer with internal buffer.
|
||||
type BufferedWriter struct {
|
||||
sync.Mutex
|
||||
writer Writer
|
||||
buffer *Buffer
|
||||
buffered bool
|
||||
writer Writer
|
||||
buffer *Buffer
|
||||
buffered bool
|
||||
flushNext bool
|
||||
}
|
||||
|
||||
// NewBufferedWriter creates a new BufferedWriter.
|
||||
@@ -161,6 +162,12 @@ func (w *BufferedWriter) WriteMultiBuffer(b MultiBuffer) error {
|
||||
}
|
||||
}
|
||||
|
||||
if w.flushNext {
|
||||
w.buffered = false
|
||||
w.flushNext = false
|
||||
return w.flushInternal()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -201,6 +208,13 @@ func (w *BufferedWriter) SetBuffered(f bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetFlushNext will wait the next WriteMultiBuffer to flush and set buffered = false
|
||||
func (w *BufferedWriter) SetFlushNext() {
|
||||
w.Lock()
|
||||
defer w.Unlock()
|
||||
w.flushNext = true
|
||||
}
|
||||
|
||||
// ReadFrom implements io.ReaderFrom.
|
||||
func (w *BufferedWriter) ReadFrom(reader io.Reader) (int64, error) {
|
||||
if err := w.SetBuffered(false); err != nil {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package common
|
||||
|
||||
import "reflect"
|
||||
|
||||
// Must panics if err is not nil.
|
||||
func Must(err error) {
|
||||
if err != nil {
|
||||
@@ -17,3 +19,14 @@ func Must2(v interface{}, err error) interface{} {
|
||||
func Error2(v interface{}, err error) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// CloseIfExists call obj.Close() if obj is not nil.
|
||||
func CloseIfExists(obj any) error {
|
||||
if obj != nil {
|
||||
v := reflect.ValueOf(obj)
|
||||
if !v.IsNil() {
|
||||
return Close(obj)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -9,6 +9,9 @@ func RandBetween(from int64, to int64) int64 {
|
||||
if from == to {
|
||||
return from
|
||||
}
|
||||
if from > to {
|
||||
from, to = to, from
|
||||
}
|
||||
bigInt, _ := rand.Int(rand.Reader, big.NewInt(to-from))
|
||||
return from + bigInt.Int64()
|
||||
}
|
||||
|
||||
@@ -200,16 +200,19 @@ func (p *pipe) Interrupt() {
|
||||
p.Lock()
|
||||
defer p.Unlock()
|
||||
|
||||
if !p.data.IsEmpty() {
|
||||
buf.ReleaseMulti(p.data)
|
||||
p.data = nil
|
||||
if p.state == closed {
|
||||
p.state = errord
|
||||
}
|
||||
}
|
||||
|
||||
if p.state == closed || p.state == errord {
|
||||
return
|
||||
}
|
||||
|
||||
p.state = errord
|
||||
|
||||
if !p.data.IsEmpty() {
|
||||
buf.ReleaseMulti(p.data)
|
||||
p.data = nil
|
||||
}
|
||||
|
||||
common.Must(p.done.Close())
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package signal
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/common/xray"
|
||||
@@ -14,10 +15,12 @@ type ActivityUpdater interface {
|
||||
}
|
||||
|
||||
type ActivityTimer struct {
|
||||
sync.RWMutex
|
||||
mu sync.RWMutex
|
||||
updated chan struct{}
|
||||
checkTask *task.Periodic
|
||||
onTimeout func()
|
||||
consumed atomic.Bool
|
||||
once sync.Once
|
||||
}
|
||||
|
||||
func (t *ActivityTimer) Update() {
|
||||
@@ -37,39 +40,39 @@ func (t *ActivityTimer) check() error {
|
||||
}
|
||||
|
||||
func (t *ActivityTimer) finish() {
|
||||
t.Lock()
|
||||
defer t.Unlock()
|
||||
t.once.Do(func() {
|
||||
t.consumed.Store(true)
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
|
||||
if t.onTimeout != nil {
|
||||
common.CloseIfExists(t.checkTask)
|
||||
t.onTimeout()
|
||||
t.onTimeout = nil
|
||||
}
|
||||
if t.checkTask != nil {
|
||||
t.checkTask.Close()
|
||||
t.checkTask = nil
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (t *ActivityTimer) SetTimeout(timeout time.Duration) {
|
||||
if t.consumed.Load() {
|
||||
return
|
||||
}
|
||||
if timeout == 0 {
|
||||
t.finish()
|
||||
return
|
||||
}
|
||||
|
||||
checkTask := &task.Periodic{
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
// double check, just in case
|
||||
if t.consumed.Load() {
|
||||
return
|
||||
}
|
||||
newCheckTask := &task.Periodic{
|
||||
Interval: timeout,
|
||||
Execute: t.check,
|
||||
}
|
||||
|
||||
t.Lock()
|
||||
|
||||
if t.checkTask != nil {
|
||||
t.checkTask.Close()
|
||||
}
|
||||
t.checkTask = checkTask
|
||||
t.Unlock()
|
||||
common.CloseIfExists(t.checkTask)
|
||||
t.checkTask = newCheckTask
|
||||
t.Update()
|
||||
common.Must(checkTask.Start())
|
||||
common.Must(newCheckTask.Start())
|
||||
}
|
||||
|
||||
func CancelAfterInactivity(ctx context.Context, cancel context.CancelFunc, timeout time.Duration) *ActivityTimer {
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
},
|
||||
"profile": {
|
||||
"detour": "direct",
|
||||
// for getting existing WARP device profile
|
||||
// For getting existing WARP device profile, else sing-box will create new profile
|
||||
"id": "",
|
||||
"private_key": "",
|
||||
"auth_token": ""
|
||||
@@ -56,7 +56,7 @@
|
||||
"experimental": {
|
||||
"cache_file": {
|
||||
"enabled": true,
|
||||
"store_warp_config": true
|
||||
"store_warp_config": true // For saving WARP device profiles
|
||||
}
|
||||
}
|
||||
}
|
||||
52
examples/wireguard/client.json
Normal file
52
examples/wireguard/client.json
Normal file
@@ -0,0 +1,52 @@
|
||||
{
|
||||
"log": {
|
||||
"level": "error"
|
||||
},
|
||||
"dns": {
|
||||
"servers": [
|
||||
{
|
||||
"type": "local",
|
||||
"tag": "default"
|
||||
}
|
||||
]
|
||||
},
|
||||
"endpoints": [
|
||||
{
|
||||
"type": "wireguard",
|
||||
"tag": "wireguard-out",
|
||||
"mtu": 1408,
|
||||
"address": null,
|
||||
"private_key": "",
|
||||
"listen_port": 10000,
|
||||
"peers": [
|
||||
{
|
||||
"address": "example.com",
|
||||
"port": 10001,
|
||||
"reserved": "AAAA"
|
||||
}
|
||||
],
|
||||
"udp_timeout": "5m0s",
|
||||
// Extended options
|
||||
"preallocated_buffers_per_pool": 256, // Set limit for preallocated buffers (can be useful for devices with low RAM)
|
||||
"disable_pauses": true, // Disable pauses when android device in sleep mode
|
||||
}
|
||||
],
|
||||
"inbounds": [
|
||||
{
|
||||
"type": "mixed",
|
||||
"tag": "mixed-in",
|
||||
"listen_port": 7897
|
||||
}
|
||||
],
|
||||
"outbounds": [
|
||||
{
|
||||
"type": "direct",
|
||||
"tag": "direct"
|
||||
},
|
||||
],
|
||||
"route": {
|
||||
"final": "wireguard-out",
|
||||
"default_domain_resolver": "default",
|
||||
"auto_detect_interface": true
|
||||
}
|
||||
}
|
||||
@@ -48,7 +48,6 @@
|
||||
"h_keep_alive_period": 60
|
||||
},
|
||||
"download": {
|
||||
"mode": "",
|
||||
"host": "example.com",
|
||||
"path": "/xhttp",
|
||||
"domain_strategy": "prefer_ipv4",
|
||||
|
||||
8
go.mod
8
go.mod
@@ -23,7 +23,6 @@ require (
|
||||
github.com/mholt/acmez/v3 v3.1.2
|
||||
github.com/miekg/dns v1.1.67
|
||||
github.com/oschwald/maxminddb-golang v1.13.1
|
||||
github.com/quic-go/quic-go v0.54.0
|
||||
github.com/sagernet/asc-go v0.0.0-20241217030726-d563060fe4e1
|
||||
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a
|
||||
github.com/sagernet/cors v1.2.1
|
||||
@@ -54,6 +53,7 @@ require (
|
||||
golang.org/x/mod v0.27.0
|
||||
golang.org/x/net v0.43.0
|
||||
golang.org/x/sys v0.35.0
|
||||
golang.org/x/time v0.11.0
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10
|
||||
google.golang.org/grpc v1.73.0
|
||||
google.golang.org/protobuf v1.36.6
|
||||
@@ -66,7 +66,6 @@ require (
|
||||
github.com/ameshkov/dnsstamps v1.0.3 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.0 // indirect
|
||||
go.uber.org/mock v0.5.0 // indirect
|
||||
golang.org/x/sync v0.16.0 // indirect
|
||||
golang.org/x/text v0.28.0 // indirect
|
||||
golang.org/x/tools v0.36.0 // indirect
|
||||
@@ -142,7 +141,6 @@ require (
|
||||
go.uber.org/zap/exp v0.3.0 // indirect
|
||||
go4.org/mem v0.0.0-20240501181205-ae6ca9944745 // indirect
|
||||
golang.org/x/term v0.34.0 // indirect
|
||||
golang.org/x/time v0.11.0 // indirect
|
||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
||||
golang.zx2c4.com/wireguard/windows v0.5.3 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 // indirect
|
||||
@@ -150,7 +148,9 @@ require (
|
||||
lukechampine.com/blake3 v1.4.1 // indirect
|
||||
)
|
||||
|
||||
replace github.com/sagernet/wireguard-go => github.com/shtorm-7/wireguard-go v0.0.1-beta.7-extended-1.0.1
|
||||
replace github.com/sagernet/wireguard-go => github.com/shtorm-7/wireguard-go v0.0.1-beta.7-extended-1.1.0
|
||||
|
||||
replace github.com/sagernet/tailscale => github.com/shtorm-7/tailscale v1.80.3-sing-box-1.12-mod.2-extended-1.0.0
|
||||
|
||||
replace github.com/sagernet/sing-dns => github.com/shtorm-7/sing-dns v0.4.6-extended-1.0.0
|
||||
|
||||
|
||||
12
go.sum
12
go.sum
@@ -151,8 +151,6 @@ github.com/prometheus-community/pro-bing v0.4.0 h1:YMbv+i08gQz97OZZBwLyvmmQEEzyf
|
||||
github.com/prometheus-community/pro-bing v0.4.0/go.mod h1:b7wRYZtCcPmt4Sz319BykUU241rWLe1VFXyiyWK/dH4=
|
||||
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
||||
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
|
||||
github.com/quic-go/quic-go v0.54.0 h1:6s1YB9QotYI6Ospeiguknbp2Znb/jZYjZLRXn9kMQBg=
|
||||
github.com/quic-go/quic-go v0.54.0/go.mod h1:e68ZEaCdyviluZmy44P6Iey98v/Wfz6HCjQEm+l8zTY=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/safchain/ethtool v0.3.0 h1:gimQJpsI6sc1yIqP/y8GYgiXn/NjgvpM0RNoWLVVmP0=
|
||||
github.com/safchain/ethtool v0.3.0/go.mod h1:SA9BwrgyAqNo7M+uaL6IYbxpm5wk3L7Mm6ocLW+CJUs=
|
||||
@@ -193,14 +191,14 @@ github.com/sagernet/sing-vmess v0.2.7 h1:2ee+9kO0xW5P4mfe6TYVWf9VtY8k1JhNysBqsiY
|
||||
github.com/sagernet/sing-vmess v0.2.7/go.mod h1:5aYoOtYksAyS0NXDm0qKeTYW1yoE1bJVcv+XLcVoyJs=
|
||||
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/tailscale v1.80.3-sing-box-1.12-mod.2 h1:MO7s4ni2bSfAOhcan2rdQSWCztkMXmqyg6jYPZp8bEE=
|
||||
github.com/sagernet/tailscale v1.80.3-sing-box-1.12-mod.2/go.mod h1:EBxXsWu4OH2ELbQLq32WoBeIubG8KgDrg4/Oaxjs6lI=
|
||||
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 h1:6uUiZcDRnZSAegryaUGwPC/Fj13JSHwiTftrXhMmYOc=
|
||||
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854/go.mod h1:LtfoSK3+NG57tvnVEHgcuBW9ujgE8enPSgzgwStwCAA=
|
||||
github.com/shtorm-7/dnscrypt/v2 v2.4.0-extended-1.0.0 h1:e5s7RKBd2rIPR0StbvZ2vTVtJ5jDTsTk5wtIIapZTRg=
|
||||
github.com/shtorm-7/dnscrypt/v2 v2.4.0-extended-1.0.0/go.mod h1:WpEFV2uhebXb8Jhes/5/fSdpmhGV8TL22RDaeWwV6hI=
|
||||
github.com/shtorm-7/wireguard-go v0.0.1-beta.7-extended-1.0.1 h1:IjsKFhL4HlvRo6IpU5kjLnL7TkP5vvCTJyOD6QzKADo=
|
||||
github.com/shtorm-7/wireguard-go v0.0.1-beta.7-extended-1.0.1/go.mod h1:DHxMTUaBGHP3tf8nJ/N8AkcoJDD0PHECLhTfLsw+ylQ=
|
||||
github.com/shtorm-7/tailscale v1.80.3-sing-box-1.12-mod.2-extended-1.0.0 h1:Yp4dIRwiwLda9JXyGMHkfYRr2r01NarkzsNd/oi10dk=
|
||||
github.com/shtorm-7/tailscale v1.80.3-sing-box-1.12-mod.2-extended-1.0.0/go.mod h1:+znUAXWwgcgza5mb5do8j9RC95rpY9lbSc/TyEyCGa4=
|
||||
github.com/shtorm-7/wireguard-go v0.0.1-beta.7-extended-1.1.0 h1:bTmx3NiEeH7mdgsifyNUxIEAA0wokRMSm8iS/hln6n0=
|
||||
github.com/shtorm-7/wireguard-go v0.0.1-beta.7-extended-1.1.0/go.mod h1:DHxMTUaBGHP3tf8nJ/N8AkcoJDD0PHECLhTfLsw+ylQ=
|
||||
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
|
||||
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
|
||||
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
||||
@@ -271,8 +269,6 @@ go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
||||
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
|
||||
go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||
|
||||
@@ -110,7 +110,6 @@ type V2RayHTTPUpgradeOptions struct {
|
||||
}
|
||||
|
||||
type V2RayXHTTPBaseOptions struct {
|
||||
Mode string `json:"mode"`
|
||||
Host string `json:"host,omitempty"`
|
||||
Path string `json:"path,omitempty"`
|
||||
Headers map[string]string `json:"headers,omitempty"`
|
||||
@@ -126,6 +125,7 @@ type V2RayXHTTPBaseOptions struct {
|
||||
}
|
||||
|
||||
type V2RayXHTTPOptions struct {
|
||||
Mode string `json:"mode"`
|
||||
V2RayXHTTPBaseOptions
|
||||
Download *V2RayXHTTPDownloadOptions `json:"download"`
|
||||
}
|
||||
|
||||
@@ -7,16 +7,18 @@ import (
|
||||
)
|
||||
|
||||
type WireGuardEndpointOptions struct {
|
||||
System bool `json:"system,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
MTU uint32 `json:"mtu,omitempty"`
|
||||
Address badoption.Listable[netip.Prefix] `json:"address"`
|
||||
PrivateKey string `json:"private_key"`
|
||||
ListenPort uint16 `json:"listen_port,omitempty"`
|
||||
Peers []WireGuardPeer `json:"peers,omitempty"`
|
||||
UDPTimeout badoption.Duration `json:"udp_timeout,omitempty"`
|
||||
Workers int `json:"workers,omitempty"`
|
||||
Amnezia *WireGuardAmnezia `json:"amnezia,omitempty"`
|
||||
System bool `json:"system,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
MTU uint32 `json:"mtu,omitempty"`
|
||||
Address badoption.Listable[netip.Prefix] `json:"address"`
|
||||
PrivateKey string `json:"private_key"`
|
||||
ListenPort uint16 `json:"listen_port,omitempty"`
|
||||
Peers []WireGuardPeer `json:"peers,omitempty"`
|
||||
UDPTimeout badoption.Duration `json:"udp_timeout,omitempty"`
|
||||
Workers int `json:"workers,omitempty"`
|
||||
PreallocatedBuffersPerPool uint32 `json:"preallocated_buffers_per_pool,omitempty"`
|
||||
DisablePauses bool `json:"disable_pauses,omitempty"`
|
||||
Amnezia *WireGuardAmnezia `json:"amnezia,omitempty"`
|
||||
DialerOptions
|
||||
}
|
||||
|
||||
@@ -31,13 +33,15 @@ type WireGuardPeer struct {
|
||||
}
|
||||
|
||||
type WireGuardWARPEndpointOptions struct {
|
||||
System bool `json:"system,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
ListenPort uint16 `json:"listen_port,omitempty"`
|
||||
UDPTimeout badoption.Duration `json:"udp_timeout,omitempty"`
|
||||
Workers int `json:"workers,omitempty"`
|
||||
Amnezia *WireGuardAmnezia `json:"amnezia,omitempty"`
|
||||
Profile WARPProfile `json:"profile,omitempty"`
|
||||
System bool `json:"system,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
ListenPort uint16 `json:"listen_port,omitempty"`
|
||||
UDPTimeout badoption.Duration `json:"udp_timeout,omitempty"`
|
||||
Workers int `json:"workers,omitempty"`
|
||||
PreallocatedBuffersPerPool uint32 `json:"preallocated_buffers_per_pool,omitempty"`
|
||||
DisablePauses bool `json:"disable_pauses,omitempty"`
|
||||
Amnezia *WireGuardAmnezia `json:"amnezia,omitempty"`
|
||||
Profile WARPProfile `json:"profile,omitempty"`
|
||||
DialerOptions
|
||||
}
|
||||
|
||||
@@ -58,13 +62,15 @@ type LegacyWireGuardOutboundOptions struct {
|
||||
PrivateKey string `json:"private_key"`
|
||||
Peers []LegacyWireGuardPeer `json:"peers,omitempty"`
|
||||
ServerOptions
|
||||
PeerPublicKey string `json:"peer_public_key"`
|
||||
PreSharedKey string `json:"pre_shared_key,omitempty"`
|
||||
Reserved []uint8 `json:"reserved,omitempty"`
|
||||
Workers int `json:"workers,omitempty"`
|
||||
MTU uint32 `json:"mtu,omitempty"`
|
||||
Network NetworkList `json:"network,omitempty"`
|
||||
Amnezia *WireGuardAmnezia `json:"amnezia,omitempty"`
|
||||
PeerPublicKey string `json:"peer_public_key"`
|
||||
PreSharedKey string `json:"pre_shared_key,omitempty"`
|
||||
Reserved []uint8 `json:"reserved,omitempty"`
|
||||
Workers int `json:"workers,omitempty"`
|
||||
PreallocatedBuffersPerPool uint32 `json:"preallocated_buffers_per_pool,omitempty"`
|
||||
DisablePauses bool `json:"disable_pauses,omitempty"`
|
||||
MTU uint32 `json:"mtu,omitempty"`
|
||||
Network NetworkList `json:"network,omitempty"`
|
||||
Amnezia *WireGuardAmnezia `json:"amnezia,omitempty"`
|
||||
}
|
||||
|
||||
type LegacyWireGuardPeer struct {
|
||||
|
||||
@@ -124,8 +124,10 @@ func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextL
|
||||
Reserved: it.Reserved,
|
||||
}
|
||||
}),
|
||||
Workers: options.Workers,
|
||||
Amnezia: amnezia,
|
||||
Workers: options.Workers,
|
||||
PreallocatedBuffersPerPool: options.PreallocatedBuffersPerPool,
|
||||
DisablePauses: options.DisablePauses,
|
||||
Amnezia: amnezia,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -130,13 +130,15 @@ func NewWARPEndpoint(ctx context.Context, router adapter.Router, logger log.Cont
|
||||
logger,
|
||||
tag,
|
||||
option.WireGuardEndpointOptions{
|
||||
System: options.System,
|
||||
Name: options.Name,
|
||||
ListenPort: options.ListenPort,
|
||||
UDPTimeout: options.UDPTimeout,
|
||||
Workers: options.Workers,
|
||||
Amnezia: options.Amnezia,
|
||||
DialerOptions: options.DialerOptions,
|
||||
System: options.System,
|
||||
Name: options.Name,
|
||||
ListenPort: options.ListenPort,
|
||||
UDPTimeout: options.UDPTimeout,
|
||||
Workers: options.Workers,
|
||||
PreallocatedBuffersPerPool: options.PreallocatedBuffersPerPool,
|
||||
DisablePauses: options.DisablePauses,
|
||||
Amnezia: options.Amnezia,
|
||||
DialerOptions: options.DialerOptions,
|
||||
|
||||
Address: badoption.Listable[netip.Prefix]{
|
||||
netip.MustParsePrefix(config.Interface.Addresses.V4 + "/32"),
|
||||
|
||||
@@ -125,9 +125,11 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
|
||||
}
|
||||
return endpointAddresses[0], nil
|
||||
},
|
||||
Peers: peers,
|
||||
Workers: options.Workers,
|
||||
Amnezia: amnezia,
|
||||
Peers: peers,
|
||||
Workers: options.Workers,
|
||||
PreallocatedBuffersPerPool: options.PreallocatedBuffersPerPool,
|
||||
DisablePauses: options.DisablePauses,
|
||||
Amnezia: amnezia,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -13,8 +13,8 @@ import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/quic-go/quic-go"
|
||||
"github.com/quic-go/quic-go/http3"
|
||||
"github.com/sagernet/quic-go"
|
||||
"github.com/sagernet/quic-go/http3"
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/tls"
|
||||
"github.com/sagernet/sing-box/common/xray/buf"
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"github.com/sagernet/sing-box/common/xray/signal/done"
|
||||
"github.com/sagernet/sing-box/common/xray/uuid"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
qtls "github.com/sagernet/sing-quic"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
@@ -47,14 +48,6 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt
|
||||
return nil, E.New("mode is not set")
|
||||
}
|
||||
dest := serverAddr
|
||||
var gotlsConfig *gotls.Config
|
||||
if tlsConfig != nil {
|
||||
var err error
|
||||
gotlsConfig, err = tlsConfig.Config()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
baseRequestURL, err := getBaseRequestURL(
|
||||
&options.V2RayXHTTPBaseOptions, dest, tlsConfig,
|
||||
)
|
||||
@@ -71,7 +64,7 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt
|
||||
xmuxOptions = *options.Xmux
|
||||
}
|
||||
xmuxManager := NewXmuxManager(xmuxOptions, func() XmuxConn {
|
||||
return createHTTPClient(dest, dialer, &options.V2RayXHTTPBaseOptions, tlsConfig, gotlsConfig)
|
||||
return createHTTPClient(dest, dialer, &options.V2RayXHTTPBaseOptions, tlsConfig)
|
||||
})
|
||||
getHTTPClient := func() (DialerClient, *XmuxClient) {
|
||||
xmuxClient := xmuxManager.GetXmuxClient(ctx)
|
||||
@@ -91,16 +84,11 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt
|
||||
}
|
||||
dest2 := options2.ServerOptions.Build()
|
||||
var tlsConfig2 tls.Config
|
||||
var gotlsConfig2 *gotls.Config
|
||||
if options2.TLS != nil {
|
||||
tlsConfig2, err = tls.NewClient(ctx, options2.Server, common.PtrValueOrDefault(options2.TLS))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gotlsConfig2, err = tlsConfig2.Config()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
baseRequestURL2, err := getBaseRequestURL(&options2.V2RayXHTTPBaseOptions, dest2, tlsConfig2)
|
||||
if err != nil {
|
||||
@@ -116,7 +104,7 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt
|
||||
xmuxOptions2 = *options2.Xmux
|
||||
}
|
||||
xmuxManager2 := NewXmuxManager(xmuxOptions2, func() XmuxConn {
|
||||
return createHTTPClient(dest2, dialer2, &options2.V2RayXHTTPBaseOptions, tlsConfig2, gotlsConfig2)
|
||||
return createHTTPClient(dest2, dialer2, &options2.V2RayXHTTPBaseOptions, tlsConfig2)
|
||||
})
|
||||
getHTTPClient2 = func() (DialerClient, *XmuxClient) {
|
||||
xmuxClient2 := xmuxManager2.GetXmuxClient(ctx)
|
||||
@@ -262,11 +250,11 @@ func (c *Client) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func decideHTTPVersion(gotlsConfig *gotls.Config) string {
|
||||
if gotlsConfig == nil || len(gotlsConfig.NextProtos) == 0 || gotlsConfig.NextProtos[0] == "http/1.1" {
|
||||
func decideHTTPVersion(tlsConfig tls.Config) string {
|
||||
if tlsConfig == nil || len(tlsConfig.NextProtos()) == 0 || tlsConfig.NextProtos()[0] == "http/1.1" {
|
||||
return "1.1"
|
||||
}
|
||||
if gotlsConfig.NextProtos[0] == "h3" {
|
||||
if tlsConfig.NextProtos()[0] == "h3" {
|
||||
return "3"
|
||||
}
|
||||
return "2"
|
||||
@@ -298,8 +286,8 @@ func getBaseRequestURL(options *option.V2RayXHTTPBaseOptions, dest M.Socksaddr,
|
||||
return requestURL, nil
|
||||
}
|
||||
|
||||
func createHTTPClient(dest M.Socksaddr, dialer N.Dialer, options *option.V2RayXHTTPBaseOptions, tlsConfig tls.Config, gotlsConfig *gotls.Config) DialerClient {
|
||||
httpVersion := decideHTTPVersion(gotlsConfig)
|
||||
func createHTTPClient(dest M.Socksaddr, dialer N.Dialer, options *option.V2RayXHTTPBaseOptions, tlsConfig tls.Config) DialerClient {
|
||||
httpVersion := decideHTTPVersion(tlsConfig)
|
||||
dialContext := func(ctxInner context.Context) (net.Conn, error) {
|
||||
conn, err := dialer.DialContext(ctxInner, "tcp", dest)
|
||||
if err != nil {
|
||||
@@ -332,14 +320,13 @@ func createHTTPClient(dest M.Socksaddr, dialer N.Dialer, options *option.V2RayXH
|
||||
KeepAlivePeriod: keepAlivePeriod,
|
||||
}
|
||||
transport = &http3.Transport{
|
||||
QUICConfig: quicConfig,
|
||||
TLSClientConfig: gotlsConfig.Clone(),
|
||||
Dial: func(ctx context.Context, addr string, tlsCfg *gotls.Config, cfg *quic.Config) (*quic.Conn, error) {
|
||||
QUICConfig: quicConfig,
|
||||
Dial: func(ctx context.Context, addr string, tlsCfg *gotls.Config, cfg *quic.Config) (quic.EarlyConnection, error) {
|
||||
udpConn, dErr := dialer.DialContext(ctx, N.NetworkUDP, dest)
|
||||
if dErr != nil {
|
||||
return nil, dErr
|
||||
}
|
||||
return quic.DialEarly(ctx, bufio.NewUnbindPacketConn(udpConn), udpConn.RemoteAddr(), tlsCfg, cfg)
|
||||
return qtls.DialEarly(ctx, bufio.NewUnbindPacketConn(udpConn), udpConn.RemoteAddr(), tlsConfig, cfg)
|
||||
},
|
||||
}
|
||||
case "2":
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package xhttp
|
||||
|
||||
import (
|
||||
common "github.com/sagernet/sing-box/common/xray"
|
||||
"github.com/sagernet/sing-box/common/xray/buf"
|
||||
"github.com/sagernet/sing-box/common/xray/pipe"
|
||||
)
|
||||
@@ -24,15 +25,17 @@ func (w uploadWriter) Write(b []byte) (int, error) {
|
||||
b = b[:capacity]
|
||||
}
|
||||
*/
|
||||
buffer := buf.New()
|
||||
n, err := buffer.Write(b)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
err = w.WriteMultiBuffer([]*buf.Buffer{buffer})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
buffer := buf.MultiBufferContainer{}
|
||||
common.Must2(buffer.Write(b))
|
||||
|
||||
var writed int
|
||||
for _, buff := range buffer.MultiBuffer {
|
||||
err := w.WriteMultiBuffer(buf.MultiBuffer{buff})
|
||||
if err != nil {
|
||||
return writed, err
|
||||
}
|
||||
writed += int(buff.Len())
|
||||
}
|
||||
return n, nil
|
||||
return writed, nil
|
||||
}
|
||||
|
||||
@@ -177,7 +177,7 @@ func (e *Endpoint) Start(resolve bool) error {
|
||||
e.options.Logger.Error(fmt.Sprintf(strings.ToLower(format), args...))
|
||||
},
|
||||
}
|
||||
wgDevice := device.NewDevice(e.options.Context, e.tunDevice, bind, logger, e.options.Workers)
|
||||
wgDevice := device.NewDevice(e.options.Context, e.tunDevice, bind, logger, e.options.Workers, e.options.PreallocatedBuffersPerPool, e.options.DisablePauses)
|
||||
e.tunDevice.SetDevice(wgDevice)
|
||||
ipcConf := e.ipcConf
|
||||
if e.options.Amnezia != nil {
|
||||
|
||||
@@ -12,22 +12,24 @@ import (
|
||||
)
|
||||
|
||||
type EndpointOptions struct {
|
||||
Context context.Context
|
||||
Logger logger.ContextLogger
|
||||
System bool
|
||||
Handler tun.Handler
|
||||
UDPTimeout time.Duration
|
||||
Dialer N.Dialer
|
||||
CreateDialer func(interfaceName string) N.Dialer
|
||||
Name string
|
||||
MTU uint32
|
||||
Address []netip.Prefix
|
||||
PrivateKey string
|
||||
ListenPort uint16
|
||||
ResolvePeer func(domain string) (netip.Addr, error)
|
||||
Peers []PeerOptions
|
||||
Workers int
|
||||
Amnezia *AmneziaOptions
|
||||
Context context.Context
|
||||
Logger logger.ContextLogger
|
||||
System bool
|
||||
Handler tun.Handler
|
||||
UDPTimeout time.Duration
|
||||
Dialer N.Dialer
|
||||
CreateDialer func(interfaceName string) N.Dialer
|
||||
Name string
|
||||
MTU uint32
|
||||
Address []netip.Prefix
|
||||
PrivateKey string
|
||||
ListenPort uint16
|
||||
ResolvePeer func(domain string) (netip.Addr, error)
|
||||
Peers []PeerOptions
|
||||
Workers int
|
||||
PreallocatedBuffersPerPool uint32
|
||||
DisablePauses bool
|
||||
Amnezia *AmneziaOptions
|
||||
}
|
||||
|
||||
type PeerOptions struct {
|
||||
|
||||
Reference in New Issue
Block a user