mirror of
https://github.com/shtorm-7/sing-box-extended.git
synced 2026-07-26 00:53:04 +03:00
Compare commits
15 Commits
v1.13.14-e
...
v1.13.14-e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c243a5b6e5 | ||
|
|
b4ec688f1a | ||
|
|
2e083b2d8f | ||
|
|
909f19640c | ||
|
|
a79e16c03c | ||
|
|
d2805ffcc9 | ||
|
|
7c19b660b4 | ||
|
|
33907d2e23 | ||
|
|
ea48501b1d | ||
|
|
ff11f007ec | ||
|
|
dc4170ad6b | ||
|
|
03f2be2ee6 | ||
|
|
6941854ce2 | ||
|
|
09d2dca3c3 | ||
|
|
425bfaf779 |
19
Dockerfile
19
Dockerfile
@@ -1,18 +1,23 @@
|
||||
FROM --platform=$BUILDPLATFORM golang:1.26-alpine AS builder
|
||||
FROM --platform=$BUILDPLATFORM golang:1.26 AS builder
|
||||
LABEL maintainer="shtorm-7"
|
||||
COPY . /go/src/github.com/sagernet/sing-box
|
||||
WORKDIR /go/src/github.com/sagernet/sing-box
|
||||
ARG TARGETOS TARGETARCH
|
||||
ARG GOPROXY=""
|
||||
ENV GOPROXY ${GOPROXY}
|
||||
ENV CGO_ENABLED=0
|
||||
ARG CRONET_GO_PATH=/go/src/github.com/sagernet/sing-box/cronet-go
|
||||
ENV GOPROXY=${GOPROXY}
|
||||
ENV CGO_ENABLED=1
|
||||
ENV GOOS=$TARGETOS
|
||||
ENV GOARCH=$TARGETARCH
|
||||
ENV CRONET_GO_PATH=${CRONET_GO_PATH}
|
||||
RUN set -ex \
|
||||
&& apk add git build-base \
|
||||
&& export COMMIT=$(git rev-parse --short HEAD) \
|
||||
&& export VERSION=$(go run ./cmd/internal/read_tag) \
|
||||
&& export TAGS=$(cat release/DEFAULT_BUILD_TAGS_OTHERS) \
|
||||
&& git config --global --add safe.directory /go/src/github.com/sagernet/sing-box \
|
||||
&& export GOTOOLCHAIN=local \
|
||||
&& export CGO_ENABLED=1 GOOS=$TARGETOS GOARCH=$TARGETARCH \
|
||||
&& eval "$(CGO_ENABLED=0 GOOS= GOARCH= go run -C "$CRONET_GO_PATH" ./cmd/build-naive --target=$GOOS/$GOARCH --libc=musl env --export)" \
|
||||
&& export CGO_LDFLAGS="$CGO_LDFLAGS -Wl,-z,notext -Wl,-z,execstack" \
|
||||
&& export VERSION=$(CGO_ENABLED=0 GOOS= GOARCH= go run ./cmd/internal/read_tag) \
|
||||
&& export TAGS=$(cat release/DEFAULT_BUILD_TAGS_DOCKER) \
|
||||
&& export LDFLAGS_SHARED=$(cat release/LDFLAGS) \
|
||||
&& go build -v -trimpath -tags "$TAGS" \
|
||||
-o /go/bin/sing-box \
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
[](LICENSE)
|
||||
[](go.mod)
|
||||
[](https://codeberg.org/shtorm-7/sing-box-extended)
|
||||
[](https://t.me/sing_box_extended)
|
||||
[](https://t.me/sing_box_extended)
|
||||
|
||||
Sing-box with extended features.
|
||||
|
||||
|
||||
@@ -29,8 +29,8 @@ type Conn struct {
|
||||
|
||||
func (c *Conn) Close() error {
|
||||
c.group.access.Lock()
|
||||
defer c.group.access.Unlock()
|
||||
c.group.connections.Remove(c.element)
|
||||
c.group.access.Unlock()
|
||||
return c.Conn.Close()
|
||||
}
|
||||
|
||||
@@ -58,8 +58,8 @@ type PacketConn struct {
|
||||
|
||||
func (c *PacketConn) Close() error {
|
||||
c.group.access.Lock()
|
||||
defer c.group.access.Unlock()
|
||||
c.group.connections.Remove(c.element)
|
||||
c.group.access.Unlock()
|
||||
return c.PacketConn.Close()
|
||||
}
|
||||
|
||||
@@ -87,8 +87,8 @@ type SingPacketConn struct {
|
||||
|
||||
func (c *SingPacketConn) Close() error {
|
||||
c.group.access.Lock()
|
||||
defer c.group.access.Unlock()
|
||||
c.group.connections.Remove(c.element)
|
||||
c.group.access.Unlock()
|
||||
return c.PacketConn.Close()
|
||||
}
|
||||
|
||||
|
||||
@@ -47,15 +47,19 @@ func (g *Group) NewSingPacketConn(conn N.PacketConn, isExternal bool, isProvider
|
||||
|
||||
func (g *Group) Interrupt(interruptExternalConnections bool) {
|
||||
g.access.Lock()
|
||||
defer g.access.Unlock()
|
||||
var toDelete []*list.Element[*groupConnItem]
|
||||
var toClose []io.Closer
|
||||
for element := g.connections.Front(); element != nil; element = element.Next() {
|
||||
if !element.Value.isExternal || interruptExternalConnections {
|
||||
element.Value.conn.Close()
|
||||
toDelete = append(toDelete, element)
|
||||
toClose = append(toClose, element.Value.conn)
|
||||
}
|
||||
}
|
||||
for _, element := range toDelete {
|
||||
g.connections.Remove(element)
|
||||
}
|
||||
g.access.Unlock()
|
||||
for _, conn := range toClose {
|
||||
_ = conn.Close()
|
||||
}
|
||||
}
|
||||
|
||||
66
common/interrupt/group_test.go
Normal file
66
common/interrupt/group_test.go
Normal file
@@ -0,0 +1,66 @@
|
||||
package interrupt
|
||||
|
||||
import (
|
||||
"net"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type closeBarrierConn struct {
|
||||
net.Conn
|
||||
barrier *sync.WaitGroup
|
||||
}
|
||||
|
||||
func (c *closeBarrierConn) Close() error {
|
||||
c.barrier.Done()
|
||||
c.barrier.Wait()
|
||||
return c.Conn.Close()
|
||||
}
|
||||
|
||||
func TestNestedGroupsInterruptWithoutDeadlock(t *testing.T) {
|
||||
groupA := NewGroup()
|
||||
groupB := NewGroup()
|
||||
barrier := &sync.WaitGroup{}
|
||||
barrier.Add(2)
|
||||
|
||||
barrierA, barrierAPeer := net.Pipe()
|
||||
barrierB, barrierBPeer := net.Pipe()
|
||||
t.Cleanup(func() {
|
||||
barrierAPeer.Close()
|
||||
barrierBPeer.Close()
|
||||
})
|
||||
groupA.NewConn(&closeBarrierConn{Conn: barrierA, barrier: barrier}, true, false)
|
||||
groupB.NewConn(&closeBarrierConn{Conn: barrierB, barrier: barrier}, true, false)
|
||||
|
||||
connA, connAPeer := net.Pipe()
|
||||
connB, connBPeer := net.Pipe()
|
||||
t.Cleanup(func() {
|
||||
connAPeer.Close()
|
||||
connBPeer.Close()
|
||||
})
|
||||
wrapperA := groupA.NewConn(connA, true, false)
|
||||
wrapperB := groupB.NewConn(connB, true, false)
|
||||
groupA.NewConn(wrapperB, true, false)
|
||||
groupB.NewConn(wrapperA, true, false)
|
||||
|
||||
done := make(chan struct{}, 2)
|
||||
go func() {
|
||||
groupA.Interrupt(true)
|
||||
done <- struct{}{}
|
||||
}()
|
||||
go func() {
|
||||
groupB.Interrupt(true)
|
||||
done <- struct{}{}
|
||||
}()
|
||||
|
||||
timeout := time.NewTimer(time.Second)
|
||||
defer timeout.Stop()
|
||||
for range 2 {
|
||||
select {
|
||||
case <-done:
|
||||
case <-timeout.C:
|
||||
t.Fatal("nested group interrupt deadlocked")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -168,9 +168,9 @@ func (e *RealityClientConfig) ClientHandshake(ctx context.Context, conn net.Conn
|
||||
}
|
||||
binary.BigEndian.PutUint64(hello.SessionId, uint64(nowTime.Unix()))
|
||||
|
||||
hello.SessionId[0] = 1
|
||||
hello.SessionId[1] = 8
|
||||
hello.SessionId[2] = 1
|
||||
hello.SessionId[0] = 26
|
||||
hello.SessionId[1] = 7
|
||||
hello.SessionId[2] = 11
|
||||
binary.BigEndian.PutUint32(hello.SessionId[4:], uint32(time.Now().Unix()))
|
||||
copy(hello.SessionId[8:], e.shortID[:])
|
||||
if debug.Enabled {
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
"tag": "vless-out",
|
||||
// Supported protocols: hysteria, hysteria2, shadowsocks, trojan, tuic, vless, vmess
|
||||
"link": "vless://b5e41c8c-c437-4689-b863-76208a3efb4b@0.0.0.0:443?..."
|
||||
// Dial Fields
|
||||
}
|
||||
],
|
||||
"route": {
|
||||
@@ -34,4 +35,4 @@
|
||||
"default_domain_resolver": "default",
|
||||
"auto_detect_interface": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
2
go.mod
2
go.mod
@@ -231,7 +231,7 @@ replace github.com/sagernet/sing-mux => github.com/shtorm-7/sing-mux v0.3.4-exte
|
||||
|
||||
replace github.com/ameshkov/dnscrypt/v2 => github.com/shtorm-7/dnscrypt/v2 v2.4.0-extended-1.0.0
|
||||
|
||||
replace github.com/sagernet/sing-vmess => github.com/shtorm-7/sing-vmess v0.2.7-extended-1.0.0
|
||||
replace github.com/sagernet/sing-vmess => github.com/shtorm-7/sing-vmess v0.2.8-extended-1.0.0
|
||||
|
||||
replace github.com/dolonet/mtg-multi => github.com/shtorm-7/mtg-multi v1.11.0-extended-1.0.0
|
||||
|
||||
|
||||
4
go.sum
4
go.sum
@@ -387,8 +387,8 @@ github.com/shtorm-7/sing v0.8.10-extended-1.2.0 h1:5yw9j0+P2QkRWvxBvb71wvNdpAlHm
|
||||
github.com/shtorm-7/sing v0.8.10-extended-1.2.0/go.mod h1:olXxWQNqRW/l2Q6JI3b2Qmz8iQnIFlOeeH8bx6JhgUA=
|
||||
github.com/shtorm-7/sing-mux v0.3.4-extended-1.0.0 h1:a5OoXr3e2ACbM6vDIaaGL44IdHQ6wPjcSoU13vfC0Sw=
|
||||
github.com/shtorm-7/sing-mux v0.3.4-extended-1.0.0/go.mod h1:QvlKMyNBNrQoyX4x+gq028uPbLM2XeRpWtDsWBJbFSk=
|
||||
github.com/shtorm-7/sing-vmess v0.2.7-extended-1.0.0 h1:WVheKmQH5hSQbJU1ZTKthKSutkTLWSb2hp4JuQhJBow=
|
||||
github.com/shtorm-7/sing-vmess v0.2.7-extended-1.0.0/go.mod h1:5aYoOtYksAyS0NXDm0qKeTYW1yoE1bJVcv+XLcVoyJs=
|
||||
github.com/shtorm-7/sing-vmess v0.2.8-extended-1.0.0 h1:OjvqZOhYSi9eIJGYC0iPDPqvgo/asujvhAGkFzbZ5/Y=
|
||||
github.com/shtorm-7/sing-vmess v0.2.8-extended-1.0.0/go.mod h1:P11scgTxMxVVQ8dlM27yNm3Cro40mD0+gHbnqrNGDuY=
|
||||
github.com/shtorm-7/tailscale v1.92.4-sing-box-1.13-mod.7-extended-1.0.2 h1:hSMjh97OszszOd8HrzpaYUQH9dWRRBluJCbwQyz8ZOk=
|
||||
github.com/shtorm-7/tailscale v1.92.4-sing-box-1.13-mod.7-extended-1.0.2/go.mod h1:TYIIqO5sZpWq873rLIeO2usszSMUpR3h6WdqVVs65ug=
|
||||
github.com/shtorm-7/wireguard-go v0.0.2-beta.1-extended-1.4.3 h1:jtOA73D4F5qRV70//ahOt20KBnWvQimAFjtIiOtt0ps=
|
||||
|
||||
@@ -23,9 +23,9 @@ type FallbackOutboundOptions struct {
|
||||
}
|
||||
|
||||
type GroupCommonOption struct {
|
||||
Outbounds []string `json:"outbounds"`
|
||||
Providers []string `json:"providers"`
|
||||
Exclude *badoption.Regexp `json:"exclude,omitempty"`
|
||||
Include *badoption.Regexp `json:"include,omitempty"`
|
||||
UseAllProviders bool `json:"use_all_providers,omitempty"`
|
||||
Outbounds []string `json:"outbounds"`
|
||||
Providers badoption.Listable[string] `json:"providers,omitempty"`
|
||||
Exclude *badoption.Regexp `json:"exclude,omitempty"`
|
||||
Include *badoption.Regexp `json:"include,omitempty"`
|
||||
UseAllProviders bool `json:"use_all_providers,omitempty"`
|
||||
}
|
||||
|
||||
@@ -118,13 +118,13 @@ type V2RayXHTTPBaseOptions struct {
|
||||
Path string `json:"path,omitempty"`
|
||||
Headers map[string]string `json:"headers,omitempty"`
|
||||
DomainStrategy DomainStrategy `json:"domain_strategy,omitempty"`
|
||||
XPaddingBytes badoption.Range[int] `json:"x_padding_bytes"`
|
||||
XPaddingBytes badoption.Range[int] `json:"x_padding_bytes"`
|
||||
NoGRPCHeader bool `json:"no_grpc_header,omitempty"`
|
||||
NoSSEHeader bool `json:"no_sse_header,omitempty"`
|
||||
ScMaxEachPostBytes *badoption.Range[int] `json:"sc_max_each_post_bytes"`
|
||||
ScMinPostsIntervalMs *badoption.Range[int] `json:"sc_min_posts_interval_ms"`
|
||||
ScMaxEachPostBytes *badoption.Range[int] `json:"sc_max_each_post_bytes"`
|
||||
ScMinPostsIntervalMs *badoption.Range[int] `json:"sc_min_posts_interval_ms"`
|
||||
ScMaxBufferedPosts int64 `json:"sc_max_buffered_posts,omitempty"`
|
||||
ScStreamUpServerSecs *badoption.Range[int] `json:"sc_stream_up_server_secs"`
|
||||
ScStreamUpServerSecs *badoption.Range[int] `json:"sc_stream_up_server_secs"`
|
||||
ServerMaxHeaderBytes int `json:"server_max_header_bytes"`
|
||||
TrustedXForwardedFor badoption.Listable[string] `json:"trusted_x_forwarded_for,omitempty"`
|
||||
Xmux *V2RayXHTTPXmuxOptions `json:"xmux"`
|
||||
@@ -140,9 +140,9 @@ type V2RayXHTTPBaseOptions struct {
|
||||
SeqKey string `json:"seq_key,omitempty"`
|
||||
UplinkDataPlacement string `json:"uplink_data_placement,omitempty"`
|
||||
UplinkDataKey string `json:"uplink_data_key,omitempty"`
|
||||
UplinkChunkSize *badoption.Range[int] `json:"uplink_chunk_size,omitempty"`
|
||||
UplinkChunkSize *badoption.Range[int] `json:"uplink_chunk_size,omitempty"`
|
||||
SessionIDTable string `json:"session_id_table,omitempty"`
|
||||
SessionIDLength badoption.Range[int] `json:"session_id_length,omitempty"`
|
||||
SessionIDLength badoption.Range[int] `json:"session_id_length,omitempty"`
|
||||
CongestionController string `json:"congestion_controller,omitempty"`
|
||||
CWND int `json:"cwnd,omitempty"`
|
||||
}
|
||||
@@ -497,7 +497,7 @@ type V2RayXHTTPXmuxOptions struct {
|
||||
CMaxReuseTimes badoption.Range[int] `json:"c_max_reuse_times"`
|
||||
HMaxRequestTimes badoption.Range[int] `json:"h_max_request_times"`
|
||||
HMaxReusableSecs badoption.Range[int] `json:"h_max_reusable_secs"`
|
||||
HKeepAlivePeriod int64 `json:"h_keep_alive_period"`
|
||||
HKeepAlivePeriod int64 `json:"h_keep_alive_period"`
|
||||
}
|
||||
|
||||
func (m *V2RayXHTTPXmuxOptions) GetNormalizedMaxConcurrency() badoption.Range[int] {
|
||||
|
||||
@@ -102,10 +102,9 @@ func (n *Inbound) Start(stage adapter.StartStage) error {
|
||||
}
|
||||
|
||||
func (n *Inbound) Close() error {
|
||||
err := common.Close(&n.listener)
|
||||
n.proxy.Shutdown()
|
||||
return common.Close(
|
||||
&n.listener,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
func (h *Inbound) UpdateUsers(users []option.MTProxyUser) {
|
||||
|
||||
@@ -77,7 +77,16 @@ func ConnectTunnel(ctx context.Context, dialer N.Dialer, tlsConfig aTLS.Config,
|
||||
if !ok || quicEndpoint == nil {
|
||||
return nil, nil, nil, errors.New("missing HTTP/3 UDP endpoint")
|
||||
}
|
||||
udpConn, err := dialer.ListenPacket(ctx, M.SocksaddrFromNetIP(quicEndpoint.AddrPort()))
|
||||
endpointAddrPort := quicEndpoint.AddrPort()
|
||||
udpConn, err := dialer.ListenPacket(
|
||||
ctx,
|
||||
M.SocksaddrFromNetIP(
|
||||
netip.AddrPortFrom(
|
||||
endpointAddrPort.Addr().Unmap(),
|
||||
endpointAddrPort.Port(),
|
||||
),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
@@ -94,18 +94,7 @@ func (u *clientPacketConn) writePacketToServer(buffer *buf.Buffer, source M.Sock
|
||||
common.Must(binary.Write(header, binary.BigEndian, source.Port))
|
||||
common.Must(binary.Write(header, binary.BigEndian, uint8(len(appName))))
|
||||
common.Must1(header.WriteString(appName))
|
||||
_, err := u.writer.Write(header.Bytes())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = u.writer.Write(buffer.Bytes())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if u.flusher != nil {
|
||||
u.flusher.Flush()
|
||||
}
|
||||
return nil
|
||||
return u.writeChunks(header.Bytes(), buffer.Bytes())
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -195,16 +184,5 @@ func (u *serverPacketConn) writePacketToClient(buffer *buf.Buffer, source M.Sock
|
||||
common.Must1(header.Write(sourceAddress[:]))
|
||||
common.Must(binary.Write(header, binary.BigEndian, source.Port))
|
||||
common.Must(header.WriteZeroN(16 + 2))
|
||||
_, err := u.writer.Write(header.Bytes())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = u.writer.Write(buffer.Bytes())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if u.flusher != nil {
|
||||
u.flusher.Flush()
|
||||
}
|
||||
return nil
|
||||
return u.writeChunks(header.Bytes(), buffer.Bytes())
|
||||
}
|
||||
|
||||
@@ -71,6 +71,9 @@ type httpConn struct {
|
||||
localAddr net.Addr
|
||||
deadline *time.Timer
|
||||
done chan struct{}
|
||||
closed bool
|
||||
|
||||
mtx sync.Mutex
|
||||
}
|
||||
|
||||
func (h *httpConn) setup(body io.ReadCloser, err error) {
|
||||
@@ -93,6 +96,9 @@ func (h *httpConn) waitCreated() error {
|
||||
}
|
||||
|
||||
func (h *httpConn) Close() error {
|
||||
h.mtx.Lock()
|
||||
h.closed = true
|
||||
h.mtx.Unlock()
|
||||
h.setup(nil, net.ErrClosed)
|
||||
if closer, ok := h.writer.(io.Closer); ok {
|
||||
_ = closer.Close()
|
||||
@@ -117,11 +123,28 @@ func (h *httpConn) Close() error {
|
||||
}
|
||||
|
||||
func (h *httpConn) writeFlush(p []byte) (n int, err error) {
|
||||
n, err = h.writer.Write(p)
|
||||
err = h.writeChunks(p)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (h *httpConn) writeChunks(chunks ...[]byte) error {
|
||||
h.mtx.Lock()
|
||||
defer h.mtx.Unlock()
|
||||
if h.closed {
|
||||
return net.ErrClosed
|
||||
}
|
||||
for _, chunk := range chunks {
|
||||
if _, err := h.writer.Write(chunk); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if h.flusher != nil {
|
||||
h.flusher.Flush()
|
||||
}
|
||||
return n, err
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *httpConn) RemoteAddr() net.Addr {
|
||||
|
||||
@@ -348,6 +348,7 @@ func createHTTPClient(ctx context.Context, dest M.Socksaddr, dialer N.Dialer, op
|
||||
}
|
||||
conn, dErr := qtls.DialEarly(ctx, bufio.NewUnbindPacketConn(udpConn), udpConn.RemoteAddr(), tlsConfig, cfg)
|
||||
if dErr != nil {
|
||||
_ = udpConn.Close()
|
||||
return nil, dErr
|
||||
}
|
||||
if congestionControlFactory != nil {
|
||||
|
||||
Reference in New Issue
Block a user