Compare commits

...

50 Commits

Author SHA1 Message Date
世界
1635c98783 Bump version 2022-10-19 10:33:46 +08:00
Skyxim
cbcaa0f590 Check destination before udp connect 2022-10-19 10:33:28 +08:00
世界
763b93c021 Fix naive overflow 2022-10-19 10:33:28 +08:00
世界
00da3e0765 Fix sniff fragmented quic client hello 2022-10-19 10:33:28 +08:00
世界
46d7a78158 Fix ssh outbound 2022-10-19 10:31:43 +08:00
世界
de3f70195e Add binary to .gitignore 2022-10-19 10:30:05 +08:00
世界
3105b8c920 Bump version 2022-09-25 22:27:23 +08:00
世界
4c67ab1a54 Fix read source address from grpc-go 2022-09-25 22:27:23 +08:00
世界
84783c5359 Fix fqdn socks5 outbound connection 2022-09-25 14:44:39 +08:00
世界
22b16f82bd Fix missing source address from transport connection 2022-09-25 14:44:33 +08:00
世界
d2add33723 Bump version 2022-09-15 13:12:18 +08:00
世界
ab0daf31c1 Fix clash api proxy type 2022-09-15 13:11:52 +08:00
世界
3d94b948dd Fix port rule match logic 2022-09-15 13:11:20 +08:00
世界
1659ae5d79 Fix close grpc conn 2022-09-15 13:10:18 +08:00
世界
7279855b08 Bump version 2022-09-13 11:25:38 +08:00
世界
925fbca363 Fix concurrent write 2022-09-13 10:36:37 +08:00
世界
a5163e3e3c Fix hysteria inbound 2022-09-13 10:32:14 +08:00
世界
62859e0c6b Fix socks4 client 2022-09-13 10:32:12 +08:00
世界
a37cab48d2 Bump version 2022-09-10 23:13:58 +08:00
世界
c586c8f361 Fix socks4 request 2022-09-10 22:53:06 +08:00
世界
e68fa3e12d Fix processing empty dns result 2022-09-10 22:52:54 +08:00
世界
7f5b9e0e3b Run build on main branch 2022-09-10 22:52:54 +08:00
世界
f7bed32c6f Bump version 2022-09-09 14:43:42 +08:00
世界
ef7f2d82c0 Fix match 4in6 address in ip_cidr 2022-09-09 14:07:02 +08:00
世界
7aa97a332e Fix documentation 2022-09-09 13:54:02 +08:00
世界
7c30dde96b Minor fixes 2022-09-08 18:33:59 +08:00
GyDi
9cef2a0a8f Fix clashapi log level format error 2022-09-08 18:04:06 +08:00
世界
f376683fc3 Update documentation 2022-09-07 23:10:36 +08:00
世界
4b61d6e875 Fix hysteria stream error 2022-09-07 19:16:20 +08:00
世界
7d83e350fd Refine test 2022-09-07 19:16:20 +08:00
世界
500ba69548 Fix processing vmess termination signal 2022-09-07 19:16:20 +08:00
世界
9a422549b1 Fix json format error message 2022-09-07 13:23:26 +08:00
世界
3b48fa455e Fix naive inbound temporary 2022-09-07 12:30:54 +08:00
zakuwaki
ef013e0639 Suppress accept proxyproto failed #65 2022-09-06 23:16:31 +08:00
世界
8f8437a88d Fix wireguard reconnect 2022-09-06 00:11:43 +08:00
世界
1b091c9b07 Update documentation 2022-09-04 13:15:10 +08:00
世界
4801b6f057 Fix DNS routing 2022-09-04 12:49:38 +08:00
世界
9078bc2de5 Fix write trojan udp 2022-09-03 16:58:55 +08:00
世界
b69464dfe9 Update documentation for dial fields 2022-09-03 13:02:41 +08:00
世界
62fa48293a Merge dialer options 2022-09-03 12:55:10 +08:00
世界
b206d0889b Fix dial parallel in direct outbound 2022-09-03 12:01:48 +08:00
世界
ee691d81bf Fix write zero 2022-09-03 09:25:30 +08:00
void aire()
56876a67cc Fix documentation typo (#60) 2022-09-02 19:04:03 +08:00
世界
4a0df713aa Add ws compatibility test 2022-09-01 20:32:47 +08:00
世界
ef801cbfbe Fix server install script 2022-09-01 20:32:47 +08:00
世界
9378fc88d2 Add with_wireguard to default server tag 2022-09-01 20:16:20 +08:00
世界
f46bfcc3d8 Move unstable branch to dev-next 2022-08-31 23:45:42 +08:00
0x7d274284
ccdb238843 Fix documentation typo (#57) 2022-08-31 23:42:36 +08:00
世界
f1f61b4e2b Fix install documentation 2022-08-31 23:37:30 +08:00
世界
a44cb745d9 Fix write log timestamp 2022-08-31 23:35:43 +08:00
91 changed files with 993 additions and 425 deletions

View File

@@ -1,10 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
PROJECTS=$(dirname "$0")/../.. PROJECTS=$(dirname "$0")/../..
go get -x github.com/sagernet/$1@$(git -C $PROJECTS/$1 rev-parse HEAD)
go get -x github.com/sagernet/sing@$(git -C $PROJECTS/sing rev-parse HEAD)
go get -x github.com/sagernet/sing-dns@$(git -C $PROJECTS/sing-dns rev-parse HEAD)
go get -x github.com/sagernet/sing-tun@$(git -C $PROJECTS/sing-tun rev-parse HEAD)
go get -x github.com/sagernet/sing-shadowsocks@$(git -C $PROJECTS/sing-shadowsocks rev-parse HEAD)
go get -x github.com/sagernet/sing-vmess@$(git -C $PROJECTS/sing-vmess rev-parse HEAD)
go mod tidy go mod tidy

View File

@@ -3,14 +3,18 @@ name: Debug build
on: on:
push: push:
branches: branches:
- main
- dev - dev
- dev-next
paths-ignore: paths-ignore:
- '**.md' - '**.md'
- '.github/**' - '.github/**'
- '!.github/workflows/debug.yml' - '!.github/workflows/debug.yml'
pull_request: pull_request:
branches: branches:
- main
- dev - dev
- dev-next
jobs: jobs:
build: build:

View File

@@ -2,7 +2,7 @@ name: Generate Documents
on: on:
push: push:
branches: branches:
- main - dev
paths: paths:
- docs/** - docs/**
- .github/workflows/mkdocs.yml - .github/workflows/mkdocs.yml

3
.gitignore vendored
View File

@@ -4,4 +4,5 @@
/*.db /*.db
/site/ /site/
/bin/ /bin/
/dist/ /dist/
/sing-box

View File

@@ -7,8 +7,8 @@ ENV GOPROXY ${GOPROXY}
ENV CGO_ENABLED=0 ENV CGO_ENABLED=0
RUN set -ex \ RUN set -ex \
&& apk add git build-base \ && apk add git build-base \
&& export COMMIT=$(git rev-parse HEAD) \ && export COMMIT=$(git rev-parse --short HEAD) \
&& go build -v -trimpath -tags 'with_quic,with_acme,with_wireguard,with_clash_api' \ && go build -v -trimpath -tags 'no_gvisor,with_quic,with_wireguard,with_acme' \
-o /go/bin/sing-box \ -o /go/bin/sing-box \
-ldflags "-X github.com/sagernet/sing-box/constant.Commit=${COMMIT} -w -s -buildid=" \ -ldflags "-X github.com/sagernet/sing-box/constant.Commit=${COMMIT} -w -s -buildid=" \
./cmd/sing-box ./cmd/sing-box

View File

@@ -38,13 +38,25 @@ type myUpstreamHandlerWrapper struct {
} }
func (w *myUpstreamHandlerWrapper) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error { func (w *myUpstreamHandlerWrapper) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
w.metadata.Destination = metadata.Destination myMetadata := w.metadata
return w.connectionHandler(ctx, conn, w.metadata) if metadata.Source.IsValid() {
myMetadata.Source = metadata.Source
}
if metadata.Destination.IsValid() {
myMetadata.Destination = metadata.Destination
}
return w.connectionHandler(ctx, conn, myMetadata)
} }
func (w *myUpstreamHandlerWrapper) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error { func (w *myUpstreamHandlerWrapper) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error {
w.metadata.Destination = metadata.Destination myMetadata := w.metadata
return w.packetHandler(ctx, conn, w.metadata) if metadata.Source.IsValid() {
myMetadata.Source = metadata.Source
}
if metadata.Destination.IsValid() {
myMetadata.Destination = metadata.Destination
}
return w.packetHandler(ctx, conn, myMetadata)
} }
func (w *myUpstreamHandlerWrapper) NewError(ctx context.Context, err error) { func (w *myUpstreamHandlerWrapper) NewError(ctx context.Context, err error) {
@@ -78,13 +90,23 @@ func NewUpstreamContextHandler(
func (w *myUpstreamContextHandlerWrapper) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error { func (w *myUpstreamContextHandlerWrapper) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
myMetadata := ContextFrom(ctx) myMetadata := ContextFrom(ctx)
myMetadata.Destination = metadata.Destination if metadata.Source.IsValid() {
myMetadata.Source = metadata.Source
}
if metadata.Destination.IsValid() {
myMetadata.Destination = metadata.Destination
}
return w.connectionHandler(ctx, conn, *myMetadata) return w.connectionHandler(ctx, conn, *myMetadata)
} }
func (w *myUpstreamContextHandlerWrapper) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error { func (w *myUpstreamContextHandlerWrapper) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error {
myMetadata := ContextFrom(ctx) myMetadata := ContextFrom(ctx)
myMetadata.Destination = metadata.Destination if metadata.Source.IsValid() {
myMetadata.Source = metadata.Source
}
if metadata.Destination.IsValid() {
myMetadata.Destination = metadata.Destination
}
return w.packetHandler(ctx, conn, *myMetadata) return w.packetHandler(ctx, conn, *myMetadata)
} }

View File

@@ -38,7 +38,7 @@ func format() error {
return E.Cause(err, "read config") return E.Cause(err, "read config")
} }
var options option.Options var options option.Options
err = json.Unmarshal(configContent, &options) err = options.UnmarshalJSON(configContent)
if err != nil { if err != nil {
return E.Cause(err, "decode config") return E.Cause(err, "decode config")
} }

View File

@@ -9,7 +9,6 @@ import (
"syscall" "syscall"
"github.com/sagernet/sing-box" "github.com/sagernet/sing-box"
"github.com/sagernet/sing-box/common/json"
"github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
@@ -46,7 +45,7 @@ func readConfig() (option.Options, error) {
return option.Options{}, E.Cause(err, "read config") return option.Options{}, E.Cause(err, "read config")
} }
var options option.Options var options option.Options
err = json.Unmarshal(configContent, &options) err = options.UnmarshalJSON(configContent)
if err != nil { if err != nil {
return option.Options{}, E.Cause(err, "decode config") return option.Options{}, E.Cause(err, "decode config")
} }

View File

@@ -1,6 +1,13 @@
package baderror package baderror
import "strings" import (
"context"
"io"
"net"
"strings"
E "github.com/sagernet/sing/common/exceptions"
)
func Contains(err error, msgList ...string) bool { func Contains(err error, msgList ...string) bool {
for _, msg := range msgList { for _, msg := range msgList {
@@ -10,3 +17,46 @@ func Contains(err error, msgList ...string) bool {
} }
return false return false
} }
func WrapH2(err error) error {
if err == nil {
return nil
}
err = E.Unwrap(err)
if err == io.ErrUnexpectedEOF {
return io.EOF
}
if Contains(err, "client disconnected", "body closed by handler") {
return net.ErrClosed
}
return err
}
func WrapGRPC(err error) error {
// grpc uses stupid internal error types
if err == nil {
return nil
}
if Contains(err, "EOF") {
return io.EOF
}
if Contains(err, "Canceled") {
return context.Canceled
}
if Contains(err,
"the client connection is closing",
"server closed the stream without sending trailers") {
return net.ErrClosed
}
return err
}
func WrapQUIC(err error) error {
if err == nil {
return nil
}
if Contains(err, "canceled with error code 0") {
return net.ErrClosed
}
return err
}

View File

@@ -1,26 +0,0 @@
package baderror
import (
"context"
"io"
"net"
)
func WrapGRPC(err error) error {
// grpc uses stupid internal error types
if err == nil {
return nil
}
if Contains(err, "EOF") {
return io.EOF
}
if Contains(err, "Canceled") {
return context.Canceled
}
if Contains(err,
"the client connection is closing",
"server closed the stream without sending trailers") {
return net.ErrClosed
}
return err
}

View File

@@ -1,22 +0,0 @@
package baderror
import (
"io"
"net"
E "github.com/sagernet/sing/common/exceptions"
)
func WrapH2(err error) error {
if err == nil {
return nil
}
err = E.Unwrap(err)
if err == io.ErrUnexpectedEOF {
return io.EOF
}
if Contains(err, "client disconnected", "body closed by handler") {
return net.ErrClosed
}
return err
}

View File

@@ -10,15 +10,12 @@ import (
) )
func New(router adapter.Router, options option.DialerOptions) N.Dialer { func New(router adapter.Router, options option.DialerOptions) N.Dialer {
var dialer N.Dialer
if options.Detour == "" { if options.Detour == "" {
return NewDefault(router, options) dialer = NewDefault(router, options)
} else { } else {
return NewDetour(router, options.Detour) dialer = NewDetour(router, options.Detour)
} }
}
func NewOutbound(router adapter.Router, options option.OutboundDialerOptions) N.Dialer {
dialer := New(router, options.DialerOptions)
domainStrategy := dns.DomainStrategy(options.DomainStrategy) domainStrategy := dns.DomainStrategy(options.DomainStrategy)
if domainStrategy != dns.DomainStrategyAsIS || options.Detour == "" { if domainStrategy != dns.DomainStrategyAsIS || options.Detour == "" {
dialer = NewResolveDialer(router, dialer, domainStrategy, time.Duration(options.FallbackDelay)) dialer = NewResolveDialer(router, dialer, domainStrategy, time.Duration(options.FallbackDelay))

View File

@@ -24,8 +24,7 @@ func QUICClientHello(ctx context.Context, packet []byte) (*adapter.InboundContex
if err != nil { if err != nil {
return nil, err return nil, err
} }
if typeByte&0x40 == 0 {
if typeByte&0x80 == 0 || typeByte&0x40 == 0 {
return nil, E.New("bad type byte") return nil, E.New("bad type byte")
} }
var versionNumber uint32 var versionNumber uint32
@@ -145,9 +144,6 @@ func QUICClientHello(ctx context.Context, packet []byte) (*adapter.InboundContex
default: default:
return nil, E.New("bad packet number length") return nil, E.New("bad packet number length")
} }
if packetNumber != 0 {
return nil, E.New("bad packet number: ", packetNumber)
}
extHdrLen := hdrLen + int(packetNumberLength) extHdrLen := hdrLen + int(packetNumberLength)
copy(newPacket[extHdrLen:hdrLen+4], packet[extHdrLen:]) copy(newPacket[extHdrLen:hdrLen+4], packet[extHdrLen:])
data := newPacket[extHdrLen : int(packetLen)+hdrLen] data := newPacket[extHdrLen : int(packetLen)+hdrLen]
@@ -172,37 +168,76 @@ func QUICClientHello(ctx context.Context, packet []byte) (*adapter.InboundContex
if err != nil { if err != nil {
return nil, err return nil, err
} }
var frameType byte
var frameLen uint64
var fragments []struct {
offset uint64
length uint64
payload []byte
}
decryptedReader := bytes.NewReader(decrypted) decryptedReader := bytes.NewReader(decrypted)
frameType, err := decryptedReader.ReadByte() for {
if err != nil {
return nil, err
}
for frameType == 0x0 {
// skip padding
frameType, err = decryptedReader.ReadByte() frameType, err = decryptedReader.ReadByte()
if err != nil { if err == io.EOF {
return nil, err break
}
switch frameType {
case 0x0:
continue
case 0x1:
continue
case 0x6:
var offset uint64
offset, err = qtls.ReadUvarint(decryptedReader)
if err != nil {
return &adapter.InboundContext{Protocol: C.ProtocolQUIC}, err
}
var length uint64
length, err = qtls.ReadUvarint(decryptedReader)
if err != nil {
return &adapter.InboundContext{Protocol: C.ProtocolQUIC}, err
}
index := len(decrypted) - decryptedReader.Len()
fragments = append(fragments, struct {
offset uint64
length uint64
payload []byte
}{offset, length, decrypted[index : index+int(length)]})
frameLen += length
_, err = decryptedReader.Seek(int64(length), io.SeekCurrent)
if err != nil {
return nil, err
}
default:
// ignore unknown frame type
} }
}
if frameType != 0x6 {
// not crypto frame
return &adapter.InboundContext{Protocol: C.ProtocolQUIC}, nil
}
_, err = qtls.ReadUvarint(decryptedReader)
if err != nil {
return nil, err
}
_, err = qtls.ReadUvarint(decryptedReader)
if err != nil {
return nil, err
} }
tlsHdr := make([]byte, 5) tlsHdr := make([]byte, 5)
tlsHdr[0] = 0x16 tlsHdr[0] = 0x16
binary.BigEndian.PutUint16(tlsHdr[1:], uint16(0x0303)) binary.BigEndian.PutUint16(tlsHdr[1:], uint16(0x0303))
binary.BigEndian.PutUint16(tlsHdr[3:], uint16(decryptedReader.Len())) binary.BigEndian.PutUint16(tlsHdr[3:], uint16(frameLen))
metadata, err := TLSClientHello(ctx, io.MultiReader(bytes.NewReader(tlsHdr), decryptedReader)) var index uint64
var length int
var readers []io.Reader
readers = append(readers, bytes.NewReader(tlsHdr))
find:
for {
for _, fragment := range fragments {
if fragment.offset == index {
readers = append(readers, bytes.NewReader(fragment.payload))
index = fragment.offset + fragment.length
length++
continue find
}
}
if length == len(fragments) {
break
}
return &adapter.InboundContext{Protocol: C.ProtocolQUIC}, E.New("bad fragments")
}
metadata, err := TLSClientHello(ctx, io.MultiReader(readers...))
if err != nil { if err != nil {
return nil, err return &adapter.InboundContext{Protocol: C.ProtocolQUIC}, err
} }
metadata.Protocol = C.ProtocolQUIC metadata.Protocol = C.ProtocolQUIC
return metadata, nil return metadata, nil

View File

@@ -19,6 +19,15 @@ func TestSniffQUICv1(t *testing.T) {
require.Equal(t, metadata.Domain, "cloudflare-quic.com") require.Equal(t, metadata.Domain, "cloudflare-quic.com")
} }
func TestSniffQUICFragment(t *testing.T) {
t.Parallel()
pkt, err := hex.DecodeString("cc00000001082e3d5d1b64040c55000044d0ccea69e773f6631c1d18b04ae9ee75fcfc34ef74fa62533c93534338a86f101a05d70e0697fb483063fa85db1c59ccfbda5c35234931d8524d8aac37eaaad649470a67794cd754b23c98695238b8363452333bc8c4858376b4166e001da2006e35cf98a91e11a56419b2786775284942d0f7163982f7c248867d12dd374957481dbc564013ff785e1916195eef671f725908f761099d992d69231336ba81d9e25fe2fa3a6eff4318a6ccf10176fc841a1b315f7b35c5b292266fc869d76ca533e7d14e86d82db2e22eacd350977e47d2e012d8a5891c5aaf2a0f4c2b2dae897c161e5b68cbb4dee952472bdc1e21504b8f02534ec4366ce3f8bf86efc78e0232778fbd554457567112abdcafcf6d4d8fcf35083c25d9495679614aba21696e338c62b585046cc55ba8c09c844361d889a47c3ea703b4e23545a9ab2c0bb369693a9ddfb5daffa85cf80fdd6ad66738664e5b0a551729b4955cff7255afcb04dee88c2f072c9de7400947a1bd9327ac5d012a33000ada021d4c03d249fb017d6ac9200b2f9436beab8183ddfbe2d8aee31ffb7df9e1cc181c1af80c39a89965d18ed12da8e3ebe2ae1fbe4b348f83ba19e3e3d1c9b22bcf03ab6ad9b30fe180623faa291ebad83bcd71d7b57f2f5e2f3b8e81d24fb70b2f2159239e8f21ffafef2747aba47d97ab4081e603c018b10678cf99cab1fb42156a14486fa435153979d7279fd22cd40af7088bfc7eff41af2f4b3c0c8864d0040d74dff427f7bffdb8c278474ea00311326cf4925471a8cf596cb92119f19e0f789490ba9cb77b98015a987d93e0324cf1a38b55109f00c3e6ddc5180fb107bf468323afec9bb49fd6a86418569789d66cafe3b8253c2aebb3af3782c1c54dd560487d031d28e6a6e23e159581bb1d47efc4da3fe1d169f9ffb0ca9ba61af0a38a92fde5bc5e6ec026e8378a6315a7b95abf1d2da790a391306ce74d0baf8e2ce648ca74c487f2c0a76a28a80cdf5bd34316eb607684fe7e6d9e83824a00e07660d0b90e3cddd61ebf10748263474afa88c300549e64ce2e90560bb1a12dee7e9484f729a8a4ee7c5651adb5194b3b3ae38e501567c7dbf36e7bb37a2c20b74655f47f2d9af18e52e9d4c9c9eee8e63745779b8f0b06f3a09d846ba62eb978ad77c85de1ee2fee3fbb4c2d283c73e1ccba56a4658e48a2665d200f7f9342f8e84c2ba490094a4f94feec89e42d2f654f564c2beb2997bafa1fc2c68ad8e160b63587d49abc31b834878d52acfb05fb73d0e059b206162e3c90b40c4bc08407ffcb3c08431895b691a3fea923f1f3b48db75d3e6b91fd319ffe4d486e0e14bd5c6affc838dee63d9e0b80f169b5e6c02c7321dcb20deb2b8e707b60e345a308d505bbf26a93d8f18b39d62632e9a77cbe48b3b32eb8819d6311a49820d40f5acbf0273c91c36b2269a03e72ee64df3dfb10ddefe73c64ef60870b2b77bd99dea655f5fe791b538a929a14d99f6d69685d72431ea5f0f4b27a044f2f575ab474fcc3857895934de1ca2581798eaef2c17fe5aaf2e6add97fa32997c7026f15c1b1ad0e6043ae506027a7c0242546fdc851cca39a204e56879f2cef838be8ec66e0f2292f8c862e06f810eb9b80c7a467ce6e90155206352c7f82b1173ba3b98d35bb72c259a60db20dd1a43fe6d7aef0265e6eaa5caafd9b64b448ff745a2046acbdb65cf2a5007809808a4828dc99097feedc734c236260c584")
require.NoError(t, err)
metadata, err := sniff.QUICClientHello(context.Background(), pkt)
require.NoError(t, err)
require.Equal(t, metadata.Domain, "cloudflare-quic.com")
}
func FuzzSniffQUIC(f *testing.F) { func FuzzSniffQUIC(f *testing.F) {
f.Fuzz(func(t *testing.T, data []byte) { f.Fuzz(func(t *testing.T, data []byte) {
sniff.QUICClientHello(context.Background(), data) sniff.QUICClientHello(context.Background(), data)

View File

@@ -5,7 +5,6 @@ import (
"context" "context"
"io" "io"
"net" "net"
"os"
"time" "time"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
@@ -30,23 +29,25 @@ func PeekStream(ctx context.Context, conn net.Conn, buffer *buf.Buffer, sniffers
return nil, err return nil, err
} }
var metadata *adapter.InboundContext var metadata *adapter.InboundContext
var errors []error
for _, sniffer := range sniffers { for _, sniffer := range sniffers {
metadata, err = sniffer(ctx, bytes.NewReader(buffer.Bytes())) metadata, err = sniffer(ctx, bytes.NewReader(buffer.Bytes()))
if err != nil { if metadata != nil {
continue return metadata, nil
} }
return metadata, nil errors = append(errors, err)
} }
return nil, os.ErrInvalid return nil, E.Errors(errors...)
} }
func PeekPacket(ctx context.Context, packet []byte, sniffers ...PacketSniffer) (*adapter.InboundContext, error) { func PeekPacket(ctx context.Context, packet []byte, sniffers ...PacketSniffer) (*adapter.InboundContext, error) {
var errors []error
for _, sniffer := range sniffers { for _, sniffer := range sniffers {
sniffMetadata, err := sniffer(ctx, packet) metadata, err := sniffer(ctx, packet)
if err != nil { if metadata != nil {
continue return metadata, nil
} }
return sniffMetadata, nil errors = append(errors, err)
} }
return nil, os.ErrInvalid return nil, E.Errors(errors...)
} }

View File

@@ -1,6 +1,6 @@
package constant package constant
var ( var (
Version = "1.0-beta3" Version = "1.0.6"
Commit = "" Commit = ""
) )

View File

@@ -1,9 +1,35 @@
#### 1.0.1
* Fix match 4in6 address in ip_cidr
* Fix clash api log level format error
* Fix clash api unknown proxy type
#### 1.0
* Fix wireguard reconnect
* Fix naive inbound
* Fix json format error message
* Fix processing vmess termination signal
* Fix hysteria stream error
* Fix listener close when proxyproto failed
#### 1.0-rc1
* Fix write log timestamp
* Fix write zero
* Fix dial parallel in direct outbound
* Fix write trojan udp
* Fix DNS routing
* Add attribute support for geosite
* Update documentation for [Dial Fields](/configuration/shared/dial)
#### 1.0-beta3 #### 1.0-beta3
* Add [chained inbound](/configuration/shared/listen#detour) support * Add [chained inbound](/configuration/shared/listen#detour) support
* Add process_path rule item * Add process_path rule item
* Add macOS redirect support * Add macOS redirect support
* Add ShadowTLS [Inbound](/configuration/inbound/shadowtls), [Outbound](/configuration/outbound/shadowtls) and [Examples](/examples/shadowtls) * Add ShadowTLS [Inbound](/configuration/inbound/shadowtls), [Outbound](/configuration/outbound/shadowtls)
and [Examples](/examples/shadowtls)
* Fix search android package in non-owner users * Fix search android package in non-owner users
* Fix socksaddr type condition * Fix socksaddr type condition
* Fix smux session status * Fix smux session status

View File

@@ -16,12 +16,14 @@
### Fields ### Fields
| Field | Available Context |
|-----------------------------------------------------------------------------------|-------------------|
| `bind_interface` /`bind_address` /`routing_mark` /`reuse_addr` /`connect_timeout` | `detour` not set |
#### detour #### detour
The tag of the upstream outbound. The tag of the upstream outbound.
Other dial fields will be ignored when enabled.
#### bind_interface #### bind_interface
The network interface to bind to. The network interface to bind to.
@@ -57,13 +59,16 @@ One of `prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`.
If set, the requested domain name will be resolved to IP before connect. If set, the requested domain name will be resolved to IP before connect.
`dns.strategy` will be used if empty. | Outbound | Effected domains | Fallback Value |
|----------|--------------------------|-------------------------------------------|
| `direct` | Domain in request | Take `inbound.domain_strategy` if not set |
| others | Domain in server address | / |
#### fallback_delay #### fallback_delay
The length of time to wait before spawning a RFC 6555 Fast Fallback connection. The length of time to wait before spawning a RFC 6555 Fast Fallback connection.
That is, is the amount of time to wait for IPv6 to succeed before assuming That is, is the amount of time to wait for connection to succeed before assuming
that IPv6 is misconfigured and falling back to IPv4 if `prefer_ipv4` is set. that IPv4/IPv6 is misconfigured and falling back to other type of addresses.
If zero, a default delay of 300ms is used. If zero, a default delay of 300ms is used.
Only take effect when `domain_strategy` is `prefer_ipv4` or `prefer_ipv6`. Only take effect when `domain_strategy` is set.

View File

@@ -35,7 +35,7 @@
"password": "8JCsPssfgS8tiRwiMlhARg==", "password": "8JCsPssfgS8tiRwiMlhARg==",
"detour": "shadowtls-out", "detour": "shadowtls-out",
"multiplex": { "multiplex": {
"enabled": 1, "enabled": true,
"max_connections": 4, "max_connections": 4,
"min_streams": 4 "min_streams": 4
} }
@@ -52,4 +52,4 @@
} }
] ]
} }
``` ```

View File

@@ -11,7 +11,7 @@ the public internet.
##### on Linux ##### on Linux
`auto-route` cannot automatically hijack DNS requests with `systemd-resoled` enabled, you can switch to NetworkManager. `auto-route` cannot automatically hijack DNS requests with `systemd-resolved` enabled, you can switch to NetworkManager.
#### System proxy #### System proxy

View File

@@ -10,7 +10,7 @@
##### Linux ##### Linux
`auto-route` 无法自动劫持 DNS 请求如果 `systemd-resoled` 开启, 您可以切换到 NetworkManager. `auto-route` 无法自动劫持 DNS 请求如果 `systemd-resolved` 开启, 您可以切换到 NetworkManager.
#### 系统代理 #### 系统代理

View File

@@ -36,7 +36,7 @@ func getConfigs(logFactory log.Factory) func(w http.ResponseWriter, r *http.Requ
logLevel := logFactory.Level() logLevel := logFactory.Level()
if logLevel == log.LevelTrace { if logLevel == log.LevelTrace {
logLevel = log.LevelDebug logLevel = log.LevelDebug
} else if logLevel > log.LevelError { } else if logLevel < log.LevelError {
logLevel = log.LevelError logLevel = log.LevelError
} }
render.JSON(w, r, &configSchema{ render.JSON(w, r, &configSchema{

View File

@@ -8,10 +8,10 @@ import (
"github.com/sagernet/sing-box/common/json" "github.com/sagernet/sing-box/common/json"
"github.com/sagernet/sing-box/experimental/clashapi/trafficontrol" "github.com/sagernet/sing-box/experimental/clashapi/trafficontrol"
"github.com/sagernet/websocket"
"github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5"
"github.com/go-chi/render" "github.com/go-chi/render"
"github.com/gorilla/websocket"
) )
func connectionRouter(trafficManager *trafficontrol.Manager) http.Handler { func connectionRouter(trafficManager *trafficontrol.Manager) http.Handler {

View File

@@ -70,16 +70,26 @@ func proxyInfo(server *Server, detour adapter.Outbound) *badjson.JSONObject {
case C.TypeSocks: case C.TypeSocks:
clashType = "Socks" clashType = "Socks"
case C.TypeHTTP: case C.TypeHTTP:
clashType = "Http" clashType = "HTTP"
case C.TypeShadowsocks: case C.TypeShadowsocks:
clashType = "Shadowsocks" clashType = "Shadowsocks"
case C.TypeVMess: case C.TypeVMess:
clashType = "Vmess" clashType = "VMess"
case C.TypeTrojan:
clashType = "Trojan"
case C.TypeHysteria:
clashType = "Hysteria"
case C.TypeWireGuard:
clashType = "WireGuard"
case C.TypeTor:
clashType = "Tor"
case C.TypeSSH:
clashType = "SSH"
case C.TypeSelector: case C.TypeSelector:
clashType = "Selector" clashType = "Selector"
isGroup = true isGroup = true
default: default:
clashType = "Unknown" clashType = "Direct"
} }
info.Put("type", clashType) info.Put("type", clashType)
info.Put("name", detour.Tag()) info.Put("name", detour.Tag())

View File

@@ -21,11 +21,11 @@ import (
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
F "github.com/sagernet/sing/common/format" F "github.com/sagernet/sing/common/format"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
"github.com/sagernet/websocket"
"github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5"
"github.com/go-chi/cors" "github.com/go-chi/cors"
"github.com/go-chi/render" "github.com/go-chi/render"
"github.com/gorilla/websocket"
) )
var _ adapter.ClashServer = (*Server)(nil) var _ adapter.ClashServer = (*Server)(nil)

8
go.mod
View File

@@ -12,7 +12,6 @@ require (
github.com/go-chi/cors v1.2.1 github.com/go-chi/cors v1.2.1
github.com/go-chi/render v1.0.2 github.com/go-chi/render v1.0.2
github.com/gofrs/uuid v4.2.0+incompatible github.com/gofrs/uuid v4.2.0+incompatible
github.com/gorilla/websocket v1.5.0
github.com/hashicorp/yamux v0.1.1 github.com/hashicorp/yamux v0.1.1
github.com/logrusorgru/aurora v2.0.3+incompatible github.com/logrusorgru/aurora v2.0.3+incompatible
github.com/mholt/acmez v1.0.4 github.com/mholt/acmez v1.0.4
@@ -20,12 +19,13 @@ require (
github.com/pires/go-proxyproto v0.6.2 github.com/pires/go-proxyproto v0.6.2
github.com/sagernet/certmagic v0.0.0-20220819042630-4a57f8b6853a github.com/sagernet/certmagic v0.0.0-20220819042630-4a57f8b6853a
github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb
github.com/sagernet/sing v0.0.0-20220829115648-e09c9f3fc812 github.com/sagernet/sing v0.0.0-20220913004915-27ddefbb8921
github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666 github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6 github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6
github.com/sagernet/sing-tun v0.0.0-20220828031750-185b6c880a83 github.com/sagernet/sing-tun v0.0.0-20220828031750-185b6c880a83
github.com/sagernet/sing-vmess v0.0.0-20220829020559-33915075430c github.com/sagernet/sing-vmess v0.0.0-20220913015714-c4ab86d40e12
github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195 github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e
github.com/spf13/cobra v1.5.0 github.com/spf13/cobra v1.5.0
github.com/stretchr/testify v1.8.0 github.com/stretchr/testify v1.8.0
go.uber.org/atomic v1.10.0 go.uber.org/atomic v1.10.0
@@ -39,6 +39,8 @@ require (
gvisor.dev/gvisor v0.0.0-20220819163037-ba6e795b139a gvisor.dev/gvisor v0.0.0-20220819163037-ba6e795b139a
) )
//replace github.com/sagernet/sing => ../sing
require ( require (
github.com/ajg/form v1.5.1 // indirect github.com/ajg/form v1.5.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect

12
go.sum
View File

@@ -73,8 +73,6 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
@@ -135,18 +133,20 @@ github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb h1:wc0yQ+SBn4TaTY
github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb/go.mod h1:MIccjRKnPTjWwAOpl+AUGWOkzyTd9tERytudxu+1ra4= github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb/go.mod h1:MIccjRKnPTjWwAOpl+AUGWOkzyTd9tERytudxu+1ra4=
github.com/sagernet/sing v0.0.0-20220812082120-05f9836bff8f/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY= github.com/sagernet/sing v0.0.0-20220812082120-05f9836bff8f/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY= github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
github.com/sagernet/sing v0.0.0-20220829115648-e09c9f3fc812 h1:z5AqoJcMy+MaD9pstLt08c95t2Txlzvp5pk4lqXBQPc= github.com/sagernet/sing v0.0.0-20220913004915-27ddefbb8921 h1:xUHzlIbdlPV/fkToIO9futp9lmKIY+72ezk/whQ8XsI=
github.com/sagernet/sing v0.0.0-20220829115648-e09c9f3fc812/go.mod h1:kZvzh1VDa/Dg/Bt5WaYKU0jl5ept8KKDpl3Ay4gRtRQ= github.com/sagernet/sing v0.0.0-20220913004915-27ddefbb8921/go.mod h1:kZvzh1VDa/Dg/Bt5WaYKU0jl5ept8KKDpl3Ay4gRtRQ=
github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666 h1:XUTocA/Ek0dFxUX+xJCWMPPFZCn2GC/uLrBjTSr1vHY= github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666 h1:XUTocA/Ek0dFxUX+xJCWMPPFZCn2GC/uLrBjTSr1vHY=
github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666/go.mod h1:eDyH7AJmqBGjZQdQmpZIzlbTREudZuWDExMuGKgjRVM= github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666/go.mod h1:eDyH7AJmqBGjZQdQmpZIzlbTREudZuWDExMuGKgjRVM=
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6 h1:JJfDeYYhWunvtxsU/mOVNTmFQmnzGx9dY034qG6G3g4= github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6 h1:JJfDeYYhWunvtxsU/mOVNTmFQmnzGx9dY034qG6G3g4=
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6/go.mod h1:EX3RbZvrwAkPI2nuGa78T2iQXmrkT+/VQtskjou42xM= github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6/go.mod h1:EX3RbZvrwAkPI2nuGa78T2iQXmrkT+/VQtskjou42xM=
github.com/sagernet/sing-tun v0.0.0-20220828031750-185b6c880a83 h1:SoWiHYuOCVedqA7T/CJSZUUrcPGKQb2wFKEq8DphiAI= github.com/sagernet/sing-tun v0.0.0-20220828031750-185b6c880a83 h1:SoWiHYuOCVedqA7T/CJSZUUrcPGKQb2wFKEq8DphiAI=
github.com/sagernet/sing-tun v0.0.0-20220828031750-185b6c880a83/go.mod h1:76r07HS1WRcEI4mE9pFsohfTBUt1j/G9Avz6DaOP3VU= github.com/sagernet/sing-tun v0.0.0-20220828031750-185b6c880a83/go.mod h1:76r07HS1WRcEI4mE9pFsohfTBUt1j/G9Avz6DaOP3VU=
github.com/sagernet/sing-vmess v0.0.0-20220829020559-33915075430c h1:92Gn78/z/t6CkzZ4XWG/uPiCxhUmjPULFEHFMDY6K8k= github.com/sagernet/sing-vmess v0.0.0-20220913015714-c4ab86d40e12 h1:4HYGbTDDemgBVTmaspXbkgjJlXc3hYVjNxSddJndq8Y=
github.com/sagernet/sing-vmess v0.0.0-20220829020559-33915075430c/go.mod h1:82O6gzbxLha/W/jxSVQbsqf2lVdRTjMIgyLug0lpJps= github.com/sagernet/sing-vmess v0.0.0-20220913015714-c4ab86d40e12/go.mod h1:u66Vv7NHXJWfeAmhh7JuJp/cwxmuQlM56QoZ7B7Mmd0=
github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195 h1:5VBIbVw9q7aKbrFdT83mjkyvQ+VaRsQ6yflTepfln38= github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195 h1:5VBIbVw9q7aKbrFdT83mjkyvQ+VaRsQ6yflTepfln38=
github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195/go.mod h1:yedWtra8nyGJ+SyI+ziwuaGMzBatbB10P1IOOZbbSK8= github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195/go.mod h1:yedWtra8nyGJ+SyI+ziwuaGMzBatbB10P1IOOZbbSK8=
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e h1:7uw2njHFGE+VpWamge6o56j2RWk4omF6uLKKxMmcWvs=
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e/go.mod h1:45TUl8+gH4SIKr4ykREbxKWTxkDlSzFENzctB1dVRRY=
github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU= github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU=
github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=

View File

@@ -3,7 +3,6 @@ package inbound
import ( import (
"context" "context"
"net" "net"
"sync"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/settings" "github.com/sagernet/sing-box/common/settings"
@@ -42,7 +41,6 @@ type myInboundAdapter struct {
tcpListener net.Listener tcpListener net.Listener
udpConn *net.UDPConn udpConn *net.UDPConn
udpAddr M.Socksaddr udpAddr M.Socksaddr
packetAccess sync.RWMutex
packetOutboundClosed chan struct{} packetOutboundClosed chan struct{}
packetOutbound chan *myInboundPacket packetOutbound chan *myInboundPacket
} }

View File

@@ -40,7 +40,11 @@ func (a *myInboundAdapter) loopTCPIn() {
for { for {
conn, err := tcpListener.Accept() conn, err := tcpListener.Accept()
if err != nil { if err != nil {
return if E.IsClosed(err) {
return
}
a.logger.Error("accept: ", err)
continue
} }
go a.injectTCP(conn, adapter.InboundContext{}) go a.injectTCP(conn, adapter.InboundContext{})
} }

View File

@@ -203,17 +203,12 @@ func (s *myInboundPacketAdapter) Upstream() any {
} }
func (s *myInboundPacketAdapter) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error { func (s *myInboundPacketAdapter) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
s.packetAccess.RLock()
defer s.packetAccess.RUnlock()
select { select {
case s.packetOutbound <- &myInboundPacket{buffer, destination}:
return nil
case <-s.packetOutboundClosed: case <-s.packetOutboundClosed:
return os.ErrClosed return os.ErrClosed
default:
} }
s.packetOutbound <- &myInboundPacket{buffer, destination}
return nil
} }
func (s *myInboundPacketAdapter) Close() error { func (s *myInboundPacketAdapter) Close() error {

View File

@@ -250,12 +250,6 @@ func (h *Hysteria) acceptStream(ctx context.Context, conn quic.Connection, strea
if err != nil { if err != nil {
return err return err
} }
err = hysteria.WriteServerResponse(stream, hysteria.ServerResponse{
OK: true,
})
if err != nil {
return err
}
var metadata adapter.InboundContext var metadata adapter.InboundContext
metadata.Inbound = h.tag metadata.Inbound = h.tag
metadata.InboundType = C.TypeHysteria metadata.InboundType = C.TypeHysteria
@@ -265,7 +259,14 @@ func (h *Hysteria) acceptStream(ctx context.Context, conn quic.Connection, strea
metadata.Source = M.SocksaddrFromNet(conn.RemoteAddr()) metadata.Source = M.SocksaddrFromNet(conn.RemoteAddr())
metadata.OriginDestination = M.SocksaddrFromNet(conn.LocalAddr()) metadata.OriginDestination = M.SocksaddrFromNet(conn.LocalAddr())
metadata.Destination = M.ParseSocksaddrHostPort(request.Host, request.Port) metadata.Destination = M.ParseSocksaddrHostPort(request.Host, request.Port)
if !request.UDP { if !request.UDP {
err = hysteria.WriteServerResponse(stream, hysteria.ServerResponse{
OK: true,
})
if err != nil {
return err
}
h.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination) h.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
return h.router.RouteConnection(ctx, hysteria.NewConn(stream, metadata.Destination), metadata) return h.router.RouteConnection(ctx, hysteria.NewConn(stream, metadata.Destination), metadata)
} else { } else {
@@ -277,6 +278,13 @@ func (h *Hysteria) acceptStream(ctx context.Context, conn quic.Connection, strea
h.udpSessions[id] = nCh h.udpSessions[id] = nCh
h.udpSessionId += 1 h.udpSessionId += 1
h.udpAccess.Unlock() h.udpAccess.Unlock()
err = hysteria.WriteServerResponse(stream, hysteria.ServerResponse{
OK: true,
UDPSessionID: id,
})
if err != nil {
return err
}
packetConn := hysteria.NewPacketConn(conn, stream, id, metadata.Destination, nCh, common.Closer(func() error { packetConn := hysteria.NewPacketConn(conn, stream, id, metadata.Destination, nCh, common.Closer(func() error {
h.udpAccess.Lock() h.udpAccess.Lock()
if ch, ok := h.udpSessions[id]; ok { if ch, ok := h.udpSessions[id]; ok {

View File

@@ -20,7 +20,6 @@ import (
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/auth" "github.com/sagernet/sing/common/auth"
"github.com/sagernet/sing/common/buf" "github.com/sagernet/sing/common/buf"
"github.com/sagernet/sing/common/bufio"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
@@ -249,7 +248,14 @@ func (c *naiveH1Conn) read(p []byte) (n int, err error) {
c.paddingRemaining = 0 c.paddingRemaining = 0
} }
if c.readPadding < kFirstPaddings { if c.readPadding < kFirstPaddings {
paddingHdr := p[:3] var paddingHdr []byte
if len(p) >= 3 {
paddingHdr = p[:3]
} else {
_paddingHdr := make([]byte, 3)
defer common.KeepAlive(_paddingHdr)
paddingHdr = common.Dup(_paddingHdr)
}
_, err = io.ReadFull(c.Conn, paddingHdr) _, err = io.ReadFull(c.Conn, paddingHdr)
if err != nil { if err != nil {
return return
@@ -361,7 +367,7 @@ func (c *naiveH1Conn) WriteBuffer(buffer *buf.Buffer) error {
n, err = bufio.Copy(w, c.Conn) n, err = bufio.Copy(w, c.Conn)
} }
return n, wrapHttpError(err) return n, wrapHttpError(err)
}*/ }
func (c *naiveH1Conn) ReadFrom(r io.Reader) (n int64, err error) { func (c *naiveH1Conn) ReadFrom(r io.Reader) (n int64, err error) {
if c.writePadding < kFirstPaddings { if c.writePadding < kFirstPaddings {
@@ -371,13 +377,14 @@ func (c *naiveH1Conn) ReadFrom(r io.Reader) (n int64, err error) {
} }
return n, wrapHttpError(err) return n, wrapHttpError(err)
} }
*/
func (c *naiveH1Conn) Upstream() any { func (c *naiveH1Conn) Upstream() any {
return c.Conn return c.Conn
} }
func (c *naiveH1Conn) ReaderReplaceable() bool { func (c *naiveH1Conn) ReaderReplaceable() bool {
return c.readRemaining == kFirstPaddings return c.readPadding == kFirstPaddings
} }
func (c *naiveH1Conn) WriterReplaceable() bool { func (c *naiveH1Conn) WriterReplaceable() bool {
@@ -420,7 +427,14 @@ func (c *naiveH2Conn) read(p []byte) (n int, err error) {
c.paddingRemaining = 0 c.paddingRemaining = 0
} }
if c.readPadding < kFirstPaddings { if c.readPadding < kFirstPaddings {
paddingHdr := p[:3] var paddingHdr []byte
if len(p) >= 3 {
paddingHdr = p[:3]
} else {
_paddingHdr := make([]byte, 3)
defer common.KeepAlive(_paddingHdr)
paddingHdr = common.Dup(_paddingHdr)
}
_, err = io.ReadFull(c.reader, paddingHdr) _, err = io.ReadFull(c.reader, paddingHdr)
if err != nil { if err != nil {
return return
@@ -539,7 +553,7 @@ func (c *naiveH2Conn) WriteBuffer(buffer *buf.Buffer) error {
n, err = bufio.Copy(w, c.reader) n, err = bufio.Copy(w, c.reader)
} }
return n, wrapHttpError(err) return n, wrapHttpError(err)
}*/ }
func (c *naiveH2Conn) ReadFrom(r io.Reader) (n int64, err error) { func (c *naiveH2Conn) ReadFrom(r io.Reader) (n int64, err error) {
if c.writePadding < kFirstPaddings { if c.writePadding < kFirstPaddings {
@@ -548,7 +562,7 @@ func (c *naiveH2Conn) ReadFrom(r io.Reader) (n int64, err error) {
n, err = bufio.Copy(c.writer, r) n, err = bufio.Copy(c.writer, r)
} }
return n, wrapHttpError(err) return n, wrapHttpError(err)
} }*/
func (c *naiveH2Conn) Close() error { func (c *naiveH2Conn) Close() error {
return common.Close( return common.Close(
@@ -586,7 +600,7 @@ func (c *naiveH2Conn) UpstreamWriter() any {
} }
func (c *naiveH2Conn) ReaderReplaceable() bool { func (c *naiveH2Conn) ReaderReplaceable() bool {
return c.readRemaining == kFirstPaddings return c.readPadding == kFirstPaddings
} }
func (c *naiveH2Conn) WriterReplaceable() bool { func (c *naiveH2Conn) WriterReplaceable() bool {

View File

@@ -71,7 +71,7 @@ func (f Formatter) Format(ctx context.Context, level Level, tag string, message
case f.DisableTimestamp: case f.DisableTimestamp:
message = levelString + " " + message message = levelString + " " + message
case f.FullTimestamp: case f.FullTimestamp:
message = F.ToString(int(timestamp.Sub(f.BaseTime)/time.Second)) + " " + levelString + " " + message message = timestamp.Format(f.TimestampFormat) + " " + levelString + " " + message
default: default:
message = levelString + "[" + xd(int(timestamp.Sub(f.BaseTime)/time.Second), 4) + "] " + message message = levelString + "[" + xd(int(timestamp.Sub(f.BaseTime)/time.Second), 4) + "] " + message
} }
@@ -136,7 +136,7 @@ func (f Formatter) FormatWithSimple(ctx context.Context, level Level, tag string
case f.DisableTimestamp: case f.DisableTimestamp:
message = levelString + " " + message message = levelString + " " + message
case f.FullTimestamp: case f.FullTimestamp:
message = F.ToString(int(timestamp.Sub(f.BaseTime)/time.Second)) + " " + levelString + " " + message message = timestamp.Format(f.TimestampFormat) + " " + levelString + " " + message
default: default:
message = levelString + "[" + xd(int(timestamp.Sub(f.BaseTime)/time.Second), 4) + "] " + message message = levelString + "[" + xd(int(timestamp.Sub(f.BaseTime)/time.Second), 4) + "] " + message
} }

View File

@@ -8,7 +8,7 @@ type DirectInboundOptions struct {
} }
type DirectOutboundOptions struct { type DirectOutboundOptions struct {
OutboundDialerOptions DialerOptions
OverrideAddress string `json:"override_address,omitempty"` OverrideAddress string `json:"override_address,omitempty"`
OverridePort uint16 `json:"override_port,omitempty"` OverridePort uint16 `json:"override_port,omitempty"`
ProxyProtocol uint8 `json:"proxy_protocol,omitempty"` ProxyProtocol uint8 `json:"proxy_protocol,omitempty"`

View File

@@ -17,7 +17,7 @@ type HysteriaInboundOptions struct {
} }
type HysteriaOutboundOptions struct { type HysteriaOutboundOptions struct {
OutboundDialerOptions DialerOptions
ServerOptions ServerOptions
Up string `json:"up,omitempty"` Up string `json:"up,omitempty"`
UpMbps int `json:"up_mbps,omitempty"` UpMbps int `json:"up_mbps,omitempty"`

View File

@@ -113,10 +113,6 @@ type DialerOptions struct {
ReuseAddr bool `json:"reuse_addr,omitempty"` ReuseAddr bool `json:"reuse_addr,omitempty"`
ConnectTimeout Duration `json:"connect_timeout,omitempty"` ConnectTimeout Duration `json:"connect_timeout,omitempty"`
TCPFastOpen bool `json:"tcp_fast_open,omitempty"` TCPFastOpen bool `json:"tcp_fast_open,omitempty"`
}
type OutboundDialerOptions struct {
DialerOptions
DomainStrategy DomainStrategy `json:"domain_strategy,omitempty"` DomainStrategy DomainStrategy `json:"domain_strategy,omitempty"`
FallbackDelay Duration `json:"fallback_delay,omitempty"` FallbackDelay Duration `json:"fallback_delay,omitempty"`
} }

View File

@@ -22,7 +22,7 @@ type ShadowsocksDestination struct {
} }
type ShadowsocksOutboundOptions struct { type ShadowsocksOutboundOptions struct {
OutboundDialerOptions DialerOptions
ServerOptions ServerOptions
Method string `json:"method"` Method string `json:"method"`
Password string `json:"password"` Password string `json:"password"`

View File

@@ -11,7 +11,7 @@ type ShadowTLSHandshakeOptions struct {
} }
type ShadowTLSOutboundOptions struct { type ShadowTLSOutboundOptions struct {
OutboundDialerOptions DialerOptions
ServerOptions ServerOptions
TLS *OutboundTLSOptions `json:"tls,omitempty"` TLS *OutboundTLSOptions `json:"tls,omitempty"`
} }

View File

@@ -15,7 +15,7 @@ type HTTPMixedInboundOptions struct {
} }
type SocksOutboundOptions struct { type SocksOutboundOptions struct {
OutboundDialerOptions DialerOptions
ServerOptions ServerOptions
Version string `json:"version,omitempty"` Version string `json:"version,omitempty"`
Username string `json:"username,omitempty"` Username string `json:"username,omitempty"`
@@ -25,7 +25,7 @@ type SocksOutboundOptions struct {
} }
type HTTPOutboundOptions struct { type HTTPOutboundOptions struct {
OutboundDialerOptions DialerOptions
ServerOptions ServerOptions
Username string `json:"username,omitempty"` Username string `json:"username,omitempty"`
Password string `json:"password,omitempty"` Password string `json:"password,omitempty"`

View File

@@ -1,7 +1,7 @@
package option package option
type SSHOutboundOptions struct { type SSHOutboundOptions struct {
OutboundDialerOptions DialerOptions
ServerOptions ServerOptions
User string `json:"user,omitempty"` User string `json:"user,omitempty"`
Password string `json:"password,omitempty"` Password string `json:"password,omitempty"`

View File

@@ -1,7 +1,7 @@
package option package option
type TorOutboundOptions struct { type TorOutboundOptions struct {
OutboundDialerOptions DialerOptions
ExecutablePath string `json:"executable_path,omitempty"` ExecutablePath string `json:"executable_path,omitempty"`
ExtraArgs []string `json:"extra_args,omitempty"` ExtraArgs []string `json:"extra_args,omitempty"`
DataDirectory string `json:"data_directory,omitempty"` DataDirectory string `json:"data_directory,omitempty"`

View File

@@ -15,7 +15,7 @@ type TrojanUser struct {
} }
type TrojanOutboundOptions struct { type TrojanOutboundOptions struct {
OutboundDialerOptions DialerOptions
ServerOptions ServerOptions
Password string `json:"password"` Password string `json:"password"`
Network NetworkList `json:"network,omitempty"` Network NetworkList `json:"network,omitempty"`

View File

@@ -14,7 +14,7 @@ type VMessUser struct {
} }
type VMessOutboundOptions struct { type VMessOutboundOptions struct {
OutboundDialerOptions DialerOptions
ServerOptions ServerOptions
UUID string `json:"uuid"` UUID string `json:"uuid"`
Security string `json:"security"` Security string `json:"security"`

View File

@@ -1,7 +1,7 @@
package option package option
type WireGuardOutboundOptions struct { type WireGuardOutboundOptions struct {
OutboundDialerOptions DialerOptions
ServerOptions ServerOptions
LocalAddress Listable[string] `json:"local_address"` LocalAddress Listable[string] `json:"local_address"`
PrivateKey string `json:"private_key"` PrivateKey string `json:"private_key"`

View File

@@ -79,7 +79,9 @@ func NewEarlyConnection(ctx context.Context, this N.Dialer, conn net.Conn, metad
func NewPacketConnection(ctx context.Context, this N.Dialer, conn N.PacketConn, metadata adapter.InboundContext) error { func NewPacketConnection(ctx context.Context, this N.Dialer, conn N.PacketConn, metadata adapter.InboundContext) error {
switch metadata.Protocol { switch metadata.Protocol {
case C.ProtocolQUIC, C.ProtocolDNS: case C.ProtocolQUIC, C.ProtocolDNS:
return connectPacketConnection(ctx, this, conn, metadata) if !metadata.Destination.Addr.IsUnspecified() {
return connectPacketConnection(ctx, this, conn, metadata)
}
} }
ctx = adapter.WithContext(ctx, &metadata) ctx = adapter.WithContext(ctx, &metadata)
var outConn net.PacketConn var outConn net.PacketConn

View File

@@ -4,12 +4,14 @@ import (
"context" "context"
"net" "net"
"net/netip" "net/netip"
"time"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/dialer" "github.com/sagernet/sing-box/common/dialer"
C "github.com/sagernet/sing-box/constant" C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-dns"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
@@ -17,11 +19,16 @@ import (
"github.com/pires/go-proxyproto" "github.com/pires/go-proxyproto"
) )
var _ adapter.Outbound = (*Direct)(nil) var (
_ adapter.Outbound = (*Direct)(nil)
_ N.ParallelDialer = (*Direct)(nil)
)
type Direct struct { type Direct struct {
myOutboundAdapter myOutboundAdapter
dialer N.Dialer dialer N.Dialer
domainStrategy dns.DomainStrategy
fallbackDelay time.Duration
overrideOption int overrideOption int
overrideDestination M.Socksaddr overrideDestination M.Socksaddr
proxyProto uint8 proxyProto uint8
@@ -36,8 +43,10 @@ func NewDirect(router adapter.Router, logger log.ContextLogger, tag string, opti
logger: logger, logger: logger,
tag: tag, tag: tag,
}, },
dialer: dialer.NewOutbound(router, options.OutboundDialerOptions), domainStrategy: dns.DomainStrategy(options.DomainStrategy),
proxyProto: options.ProxyProtocol, fallbackDelay: time.Duration(options.FallbackDelay),
dialer: dialer.New(router, options.DialerOptions),
proxyProto: options.ProxyProtocol,
} }
if options.ProxyProtocol > 2 { if options.ProxyProtocol > 2 {
return nil, E.New("invalid proxy protocol option: ", options.ProxyProtocol) return nil, E.New("invalid proxy protocol option: ", options.ProxyProtocol)
@@ -99,6 +108,53 @@ func (h *Direct) DialContext(ctx context.Context, network string, destination M.
return conn, nil return conn, nil
} }
func (h *Direct) DialParallel(ctx context.Context, network string, destination M.Socksaddr, destinationAddresses []netip.Addr) (net.Conn, error) {
ctx, metadata := adapter.AppendContext(ctx)
originDestination := metadata.Destination
metadata.Outbound = h.tag
metadata.Destination = destination
switch h.overrideOption {
case 1, 2:
// override address
return h.DialContext(ctx, network, destination)
case 3:
destination.Port = h.overrideDestination.Port
}
network = N.NetworkName(network)
switch network {
case N.NetworkTCP:
h.logger.InfoContext(ctx, "outbound connection to ", destination)
case N.NetworkUDP:
h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
}
var domainStrategy dns.DomainStrategy
if h.domainStrategy != dns.DomainStrategyAsIS {
domainStrategy = h.domainStrategy
} else {
domainStrategy = metadata.DomainStrategy
}
conn, err := N.DialParallel(ctx, h.dialer, network, destination, destinationAddresses, domainStrategy == dns.DomainStrategyPreferIPv6, h.fallbackDelay)
if err != nil {
return nil, err
}
if h.proxyProto > 0 {
source := metadata.Source
if !source.IsValid() {
source = M.SocksaddrFromNet(conn.LocalAddr())
}
if originDestination.Addr.Is6() {
source = M.SocksaddrFrom(netip.AddrFrom16(source.Addr.As16()), source.Port)
}
header := proxyproto.HeaderProxyFromAddrs(h.proxyProto, source.TCPAddr(), originDestination.TCPAddr())
_, err = header.WriteTo(conn)
if err != nil {
conn.Close()
return nil, E.Cause(err, "write proxy protocol header")
}
}
return conn, nil
}
func (h *Direct) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { func (h *Direct) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
ctx, metadata := adapter.AppendContext(ctx) ctx, metadata := adapter.AppendContext(ctx)
metadata.Outbound = h.tag metadata.Outbound = h.tag

View File

@@ -77,12 +77,9 @@ func (d *DNS) handleConnection(ctx context.Context, conn net.Conn, metadata adap
if err != nil { if err != nil {
return err return err
} }
if len(message.Questions) > 0 { metadataInQuery := metadata
question := message.Questions[0]
metadata.Domain = string(question.Name.Data[:question.Name.Length-1])
}
go func() error { go func() error {
response, err := d.router.Exchange(ctx, &message) response, err := d.router.Exchange(adapter.WithContext(ctx, &metadataInQuery), &message)
if err != nil { if err != nil {
return err return err
} }
@@ -125,13 +122,10 @@ func (d *DNS) NewPacketConnection(ctx context.Context, conn N.PacketConn, metada
if err != nil { if err != nil {
return err return err
} }
if len(message.Questions) > 0 {
question := message.Questions[0]
metadata.Domain = string(question.Name.Data[:question.Name.Length-1])
}
timeout.Update() timeout.Update()
metadataInQuery := metadata
go func() error { go func() error {
response, err := d.router.Exchange(ctx, &message) response, err := d.router.Exchange(adapter.WithContext(ctx, &metadataInQuery), &message)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -24,7 +24,7 @@ type HTTP struct {
} }
func NewHTTP(router adapter.Router, logger log.ContextLogger, tag string, options option.HTTPOutboundOptions) (*HTTP, error) { func NewHTTP(router adapter.Router, logger log.ContextLogger, tag string, options option.HTTPOutboundOptions) (*HTTP, error) {
detour, err := dialer.NewTLS(dialer.NewOutbound(router, options.OutboundDialerOptions), options.Server, common.PtrValueOrDefault(options.TLS)) detour, err := dialer.NewTLS(dialer.New(router, options.DialerOptions), options.Server, common.PtrValueOrDefault(options.TLS))
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -117,7 +117,7 @@ func NewHysteria(ctx context.Context, router adapter.Router, logger log.ContextL
tag: tag, tag: tag,
}, },
ctx: ctx, ctx: ctx,
dialer: dialer.NewOutbound(router, options.OutboundDialerOptions), dialer: dialer.New(router, options.DialerOptions),
serverAddr: options.ServerOptions.Build(), serverAddr: options.ServerOptions.Build(),
tlsConfig: tlsConfig, tlsConfig: tlsConfig,
quicConfig: quicConfig, quicConfig: quicConfig,

View File

@@ -44,7 +44,7 @@ func NewShadowsocks(ctx context.Context, router adapter.Router, logger log.Conte
logger: logger, logger: logger,
tag: tag, tag: tag,
}, },
dialer: dialer.NewOutbound(router, options.OutboundDialerOptions), dialer: dialer.New(router, options.DialerOptions),
method: method, method: method,
serverAddr: options.ServerOptions.Build(), serverAddr: options.ServerOptions.Build(),
uot: options.UoT, uot: options.UoT,

View File

@@ -34,7 +34,7 @@ func NewShadowTLS(ctx context.Context, router adapter.Router, logger log.Context
logger: logger, logger: logger,
tag: tag, tag: tag,
}, },
dialer: dialer.NewOutbound(router, options.OutboundDialerOptions), dialer: dialer.New(router, options.DialerOptions),
serverAddr: options.ServerOptions.Build(), serverAddr: options.ServerOptions.Build(),
} }
if options.TLS == nil || !options.TLS.Enabled { if options.TLS == nil || !options.TLS.Enabled {

View File

@@ -20,12 +20,13 @@ var _ adapter.Outbound = (*Socks)(nil)
type Socks struct { type Socks struct {
myOutboundAdapter myOutboundAdapter
client *socks.Client client *socks.Client
uot bool resolve bool
uot bool
} }
func NewSocks(router adapter.Router, logger log.ContextLogger, tag string, options option.SocksOutboundOptions) (*Socks, error) { func NewSocks(router adapter.Router, logger log.ContextLogger, tag string, options option.SocksOutboundOptions) (*Socks, error) {
detour := dialer.NewOutbound(router, options.OutboundDialerOptions) detour := dialer.New(router, options.DialerOptions)
var version socks.Version var version socks.Version
var err error var err error
if options.Version != "" { if options.Version != "" {
@@ -45,6 +46,7 @@ func NewSocks(router adapter.Router, logger log.ContextLogger, tag string, optio
tag: tag, tag: tag,
}, },
socks.NewClient(detour, options.ServerOptions.Build(), version, options.Username, options.Password), socks.NewClient(detour, options.ServerOptions.Build(), version, options.Username, options.Password),
version == socks.Version4,
options.UoT, options.UoT,
}, nil }, nil
} }
@@ -72,6 +74,13 @@ func (h *Socks) DialContext(ctx context.Context, network string, destination M.S
default: default:
return nil, E.Extend(N.ErrUnknownNetwork, network) return nil, E.Extend(N.ErrUnknownNetwork, network)
} }
if h.resolve && destination.IsFqdn() {
addrs, err := h.router.LookupDefault(ctx, destination.Fqdn)
if err != nil {
return nil, err
}
return N.DialSerial(ctx, h.client, network, destination, addrs)
}
return h.client.DialContext(ctx, network, destination) return h.client.DialContext(ctx, network, destination)
} }

View File

@@ -47,7 +47,7 @@ func NewSSH(ctx context.Context, router adapter.Router, logger log.ContextLogger
tag: tag, tag: tag,
}, },
ctx: ctx, ctx: ctx,
dialer: dialer.NewOutbound(router, options.OutboundDialerOptions), dialer: dialer.New(router, options.DialerOptions),
serverAddr: options.ServerOptions.Build(), serverAddr: options.ServerOptions.Build(),
user: options.User, user: options.User,
hostKeyAlgorithms: options.HostKeyAlgorithms, hostKeyAlgorithms: options.HostKeyAlgorithms,
@@ -122,6 +122,9 @@ func (s *SSH) connect() (*ssh.Client, error) {
Auth: s.authMethod, Auth: s.authMethod,
ClientVersion: s.clientVersion, ClientVersion: s.clientVersion,
HostKeyAlgorithms: s.hostKeyAlgorithms, HostKeyAlgorithms: s.hostKeyAlgorithms,
HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
return nil
},
} }
clientConn, chans, reqs, err := ssh.NewClientConn(conn, s.serverAddr.Addr.String(), config) clientConn, chans, reqs, err := ssh.NewClientConn(conn, s.serverAddr.Addr.String(), config)
if err != nil { if err != nil {

View File

@@ -66,7 +66,7 @@ func NewTor(ctx context.Context, router adapter.Router, logger log.ContextLogger
tag: tag, tag: tag,
}, },
ctx: ctx, ctx: ctx,
proxy: NewProxyListener(ctx, logger, dialer.NewOutbound(router, options.OutboundDialerOptions)), proxy: NewProxyListener(ctx, logger, dialer.New(router, options.DialerOptions)),
startConf: &startConf, startConf: &startConf,
options: options.Options, options: options.Options,
}, nil }, nil

View File

@@ -13,6 +13,7 @@ import (
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-box/transport/v2ray" "github.com/sagernet/sing-box/transport/v2ray"
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/bufio"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
@@ -40,7 +41,7 @@ func NewTrojan(ctx context.Context, router adapter.Router, logger log.ContextLog
logger: logger, logger: logger,
tag: tag, tag: tag,
}, },
dialer: dialer.NewOutbound(router, options.OutboundDialerOptions), dialer: dialer.New(router, options.DialerOptions),
serverAddr: options.ServerOptions.Build(), serverAddr: options.ServerOptions.Build(),
key: trojan.Key(options.Password), key: trojan.Key(options.Password),
} }
@@ -125,7 +126,7 @@ func (h *trojanDialer) DialContext(ctx context.Context, network string, destinat
case N.NetworkTCP: case N.NetworkTCP:
return trojan.NewClientConn(conn, h.key, destination), nil return trojan.NewClientConn(conn, h.key, destination), nil
case N.NetworkUDP: case N.NetworkUDP:
return trojan.NewClientPacketConn(conn, h.key), nil return &bufio.BindPacketConn{PacketConn: trojan.NewClientPacketConn(conn, h.key), Addr: destination}, nil
default: default:
return nil, E.Extend(N.ErrUnknownNetwork, network) return nil, E.Extend(N.ErrUnknownNetwork, network)
} }
@@ -136,5 +137,5 @@ func (h *trojanDialer) ListenPacket(ctx context.Context, destination M.Socksaddr
if err != nil { if err != nil {
return nil, err return nil, err
} }
return conn.(*trojan.ClientPacketConn), nil return conn.(net.PacketConn), nil
} }

View File

@@ -42,7 +42,7 @@ func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogg
logger: logger, logger: logger,
tag: tag, tag: tag,
}, },
dialer: dialer.NewOutbound(router, options.OutboundDialerOptions), dialer: dialer.New(router, options.DialerOptions),
serverAddr: options.ServerOptions.Build(), serverAddr: options.ServerOptions.Build(),
} }
var err error var err error

View File

@@ -64,7 +64,7 @@ func NewWireGuard(ctx context.Context, router adapter.Router, logger log.Context
}, },
ctx: ctx, ctx: ctx,
serverAddr: options.ServerOptions.Build(), serverAddr: options.ServerOptions.Build(),
dialer: dialer.NewOutbound(router, options.OutboundDialerOptions), dialer: dialer.New(router, options.DialerOptions),
} }
var endpointIp netip.Addr var endpointIp netip.Addr
if !outbound.serverAddr.IsFqdn() { if !outbound.serverAddr.IsFqdn() {
@@ -275,6 +275,7 @@ func (c *wireClientBind) Open(port uint16) (fns []conn.ReceiveFunc, actualPort u
func (c *wireClientBind) receive(b []byte) (n int, ep conn.Endpoint, err error) { func (c *wireClientBind) receive(b []byte) (n int, ep conn.Endpoint, err error) {
udpConn, err := c.connect() udpConn, err := c.connect()
if err != nil { if err != nil {
err = &wireError{err}
return return
} }
n, err = udpConn.Read(b) n, err = udpConn.Read(b)
@@ -332,10 +333,6 @@ func (w *wireError) Temporary() bool {
return true return true
} }
func (w *wireError) Unwrap() error {
return w.cause
}
type wireConn struct { type wireConn struct {
net.Conn net.Conn
access sync.Mutex access sync.Mutex

View File

@@ -10,7 +10,7 @@ DIR=$(dirname "$0")
PROJECT=$DIR/../.. PROJECT=$DIR/../..
pushd $PROJECT pushd $PROJECT
go install -v -trimpath -ldflags "-s -w -buildid=" -tags "no_gvisor" ./cmd/sing-box go install -v -trimpath -ldflags "-s -w -buildid=" -tags no_gvisor,with_quic,with_wireguard,with_acme ./cmd/sing-box
popd popd
sudo cp $(go env GOPATH)/bin/sing-box /usr/local/bin/ sudo cp $(go env GOPATH)/bin/sing-box /usr/local/bin/

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -e -o pipefail set -e -o pipefail
curl -o go.tar.gz https://go.dev/dl/go1.19.linux-amd64.tar.gz curl -Lo go.tar.gz https://go.dev/dl/go1.19.linux-amd64.tar.gz
sudo rm -rf /usr/local/go sudo rm -rf /usr/local/go
sudo tar -C /usr/local -xzf go.tar.gz sudo tar -C /usr/local -xzf go.tar.gz
rm go.tar.gz rm go.tar.gz

View File

@@ -10,7 +10,7 @@ DIR=$(dirname "$0")
PROJECT=$DIR/../.. PROJECT=$DIR/../..
pushd $PROJECT pushd $PROJECT
go install -v -trimpath -ldflags "-s -w -buildid=" -tags no_gvisor,with_quic,with_acme ./cmd/sing-box go install -v -trimpath -ldflags "-s -w -buildid=" -tags no_gvisor,with_quic,with_wireguard,with_acme ./cmd/sing-box
popd popd
sudo systemctl stop sing-box sudo systemctl stop sing-box

View File

@@ -541,8 +541,8 @@ func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata ad
if metadata.SniffEnabled { if metadata.SniffEnabled {
buffer := buf.NewPacket() buffer := buf.NewPacket()
buffer.FullReset() buffer.FullReset()
sniffMetadata, err := sniff.PeekStream(ctx, conn, buffer, sniff.StreamDomainNameQuery, sniff.TLSClientHello, sniff.HTTPHost) sniffMetadata, _ := sniff.PeekStream(ctx, conn, buffer, sniff.StreamDomainNameQuery, sniff.TLSClientHello, sniff.HTTPHost)
if err == nil { if sniffMetadata != nil {
metadata.Protocol = sniffMetadata.Protocol metadata.Protocol = sniffMetadata.Protocol
metadata.Domain = sniffMetadata.Domain metadata.Domain = sniffMetadata.Domain
if metadata.SniffOverrideDestination && sniff.IsDomainName(metadata.Domain) { if metadata.SniffOverrideDestination && sniff.IsDomainName(metadata.Domain) {
@@ -618,8 +618,8 @@ func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, m
buffer.Release() buffer.Release()
return err return err
} }
sniffMetadata, err := sniff.PeekPacket(ctx, buffer.Bytes(), sniff.DomainNameQuery, sniff.QUICClientHello, sniff.STUNMessage) sniffMetadata, _ := sniff.PeekPacket(ctx, buffer.Bytes(), sniff.DomainNameQuery, sniff.QUICClientHello, sniff.STUNMessage)
if err == nil { if sniffMetadata != nil {
metadata.Protocol = sniffMetadata.Protocol metadata.Protocol = sniffMetadata.Protocol
metadata.Domain = sniffMetadata.Domain metadata.Domain = sniffMetadata.Domain
if metadata.SniffOverrideDestination && sniff.IsDomainName(metadata.Domain) { if metadata.SniffOverrideDestination && sniff.IsDomainName(metadata.Domain) {

View File

@@ -52,6 +52,7 @@ func (r *Router) Exchange(ctx context.Context, message *dnsmessage.Message) (*dn
case dnsmessage.TypeAAAA: case dnsmessage.TypeAAAA:
metadata.IPVersion = 6 metadata.IPVersion = 6
} }
metadata.Domain = string(message.Questions[0].Name.Data[:message.Questions[0].Name.Length-1])
} }
ctx, transport, strategy := r.matchDNS(ctx) ctx, transport, strategy := r.matchDNS(ctx)
ctx, cancel := context.WithTimeout(ctx, C.DNSTimeout) ctx, cancel := context.WithTimeout(ctx, C.DNSTimeout)
@@ -68,6 +69,8 @@ func (r *Router) Exchange(ctx context.Context, message *dnsmessage.Message) (*dn
func (r *Router) Lookup(ctx context.Context, domain string, strategy dns.DomainStrategy) ([]netip.Addr, error) { func (r *Router) Lookup(ctx context.Context, domain string, strategy dns.DomainStrategy) ([]netip.Addr, error) {
r.dnsLogger.DebugContext(ctx, "lookup domain ", domain) r.dnsLogger.DebugContext(ctx, "lookup domain ", domain)
ctx, metadata := adapter.AppendContext(ctx)
metadata.Domain = domain
ctx, transport, transportStrategy := r.matchDNS(ctx) ctx, transport, transportStrategy := r.matchDNS(ctx)
if strategy == dns.DomainStrategyAsIS { if strategy == dns.DomainStrategyAsIS {
strategy = transportStrategy strategy = transportStrategy
@@ -79,6 +82,9 @@ func (r *Router) Lookup(ctx context.Context, domain string, strategy dns.DomainS
r.dnsLogger.InfoContext(ctx, "lookup succeed for ", domain, ": ", strings.Join(F.MapToString(addrs), " ")) r.dnsLogger.InfoContext(ctx, "lookup succeed for ", domain, ": ", strings.Join(F.MapToString(addrs), " "))
} else { } else {
r.dnsLogger.ErrorContext(ctx, E.Cause(err, "lookup failed for ", domain)) r.dnsLogger.ErrorContext(ctx, E.Cause(err, "lookup failed for ", domain))
if err == nil {
err = dns.RCodeNameError
}
} }
return addrs, err return addrs, err
} }

View File

@@ -41,7 +41,9 @@ var _ adapter.Rule = (*DefaultRule)(nil)
type DefaultRule struct { type DefaultRule struct {
items []RuleItem items []RuleItem
sourceAddressItems []RuleItem sourceAddressItems []RuleItem
sourcePortItems []RuleItem
destinationAddressItems []RuleItem destinationAddressItems []RuleItem
destinationPortItems []RuleItem
allItems []RuleItem allItems []RuleItem
invert bool invert bool
outbound string outbound string
@@ -143,7 +145,7 @@ func NewDefaultRule(router adapter.Router, logger log.ContextLogger, options opt
} }
if len(options.SourcePort) > 0 { if len(options.SourcePort) > 0 {
item := NewPortItem(true, options.SourcePort) item := NewPortItem(true, options.SourcePort)
rule.items = append(rule.items, item) rule.sourcePortItems = append(rule.sourcePortItems, item)
rule.allItems = append(rule.allItems, item) rule.allItems = append(rule.allItems, item)
} }
if len(options.SourcePortRange) > 0 { if len(options.SourcePortRange) > 0 {
@@ -151,12 +153,12 @@ func NewDefaultRule(router adapter.Router, logger log.ContextLogger, options opt
if err != nil { if err != nil {
return nil, E.Cause(err, "source_port_range") return nil, E.Cause(err, "source_port_range")
} }
rule.items = append(rule.items, item) rule.sourcePortItems = append(rule.sourcePortItems, item)
rule.allItems = append(rule.allItems, item) rule.allItems = append(rule.allItems, item)
} }
if len(options.Port) > 0 { if len(options.Port) > 0 {
item := NewPortItem(false, options.Port) item := NewPortItem(false, options.Port)
rule.items = append(rule.items, item) rule.destinationPortItems = append(rule.destinationPortItems, item)
rule.allItems = append(rule.allItems, item) rule.allItems = append(rule.allItems, item)
} }
if len(options.PortRange) > 0 { if len(options.PortRange) > 0 {
@@ -164,7 +166,7 @@ func NewDefaultRule(router adapter.Router, logger log.ContextLogger, options opt
if err != nil { if err != nil {
return nil, E.Cause(err, "port_range") return nil, E.Cause(err, "port_range")
} }
rule.items = append(rule.items, item) rule.destinationPortItems = append(rule.destinationPortItems, item)
rule.allItems = append(rule.allItems, item) rule.allItems = append(rule.allItems, item)
} }
if len(options.ProcessName) > 0 { if len(options.ProcessName) > 0 {
@@ -251,6 +253,19 @@ func (r *DefaultRule) Match(metadata *adapter.InboundContext) bool {
} }
} }
if len(r.sourcePortItems) > 0 {
var sourcePortMatch bool
for _, item := range r.sourcePortItems {
if item.Match(metadata) {
sourcePortMatch = true
break
}
}
if !sourcePortMatch {
return r.invert
}
}
if len(r.destinationAddressItems) > 0 { if len(r.destinationAddressItems) > 0 {
var destinationAddressMatch bool var destinationAddressMatch bool
for _, item := range r.destinationAddressItems { for _, item := range r.destinationAddressItems {
@@ -264,6 +279,19 @@ func (r *DefaultRule) Match(metadata *adapter.InboundContext) bool {
} }
} }
if len(r.destinationPortItems) > 0 {
var destinationPortMatch bool
for _, item := range r.destinationPortItems {
if item.Match(metadata) {
destinationPortMatch = true
break
}
}
if !destinationPortMatch {
return r.invert
}
}
return !r.invert return !r.invert
} }

View File

@@ -59,13 +59,13 @@ func NewIPCIDRItem(isSource bool, prefixStrings []string) (*IPCIDRItem, error) {
func (r *IPCIDRItem) Match(metadata *adapter.InboundContext) bool { func (r *IPCIDRItem) Match(metadata *adapter.InboundContext) bool {
if r.isSource { if r.isSource {
return r.ipSet.Contains(metadata.Source.Addr) return r.match(metadata.Source.Addr)
} else { } else {
if metadata.Destination.IsIP() { if metadata.Destination.IsIP() {
return r.ipSet.Contains(metadata.Destination.Addr) return r.match(metadata.Destination.Addr)
} else { } else {
for _, address := range metadata.DestinationAddresses { for _, address := range metadata.DestinationAddresses {
if r.ipSet.Contains(address) { if r.match(address) {
return true return true
} }
} }
@@ -74,6 +74,14 @@ func (r *IPCIDRItem) Match(metadata *adapter.InboundContext) bool {
return false return false
} }
func (r *IPCIDRItem) match(address netip.Addr) bool {
if address.Is4In6() {
return r.ipSet.Contains(netip.AddrFrom4(address.As4()))
} else {
return r.ipSet.Contains(address)
}
}
func (r *IPCIDRItem) String() string { func (r *IPCIDRItem) String() string {
return r.description return r.description
} }

View File

@@ -39,12 +39,15 @@ func NewDNSRule(router adapter.Router, logger log.ContextLogger, options option.
var _ adapter.DNSRule = (*DefaultDNSRule)(nil) var _ adapter.DNSRule = (*DefaultDNSRule)(nil)
type DefaultDNSRule struct { type DefaultDNSRule struct {
items []RuleItem items []RuleItem
addressItems []RuleItem sourceAddressItems []RuleItem
allItems []RuleItem sourcePortItems []RuleItem
invert bool destinationAddressItems []RuleItem
outbound string destinationPortItems []RuleItem
disableCache bool allItems []RuleItem
invert bool
outbound string
disableCache bool
} }
func NewDefaultDNSRule(router adapter.Router, logger log.ContextLogger, options option.DefaultDNSRule) (*DefaultDNSRule, error) { func NewDefaultDNSRule(router adapter.Router, logger log.ContextLogger, options option.DefaultDNSRule) (*DefaultDNSRule, error) {
@@ -90,12 +93,12 @@ func NewDefaultDNSRule(router adapter.Router, logger log.ContextLogger, options
} }
if len(options.Domain) > 0 || len(options.DomainSuffix) > 0 { if len(options.Domain) > 0 || len(options.DomainSuffix) > 0 {
item := NewDomainItem(options.Domain, options.DomainSuffix) item := NewDomainItem(options.Domain, options.DomainSuffix)
rule.addressItems = append(rule.addressItems, item) rule.destinationAddressItems = append(rule.destinationAddressItems, item)
rule.allItems = append(rule.allItems, item) rule.allItems = append(rule.allItems, item)
} }
if len(options.DomainKeyword) > 0 { if len(options.DomainKeyword) > 0 {
item := NewDomainKeywordItem(options.DomainKeyword) item := NewDomainKeywordItem(options.DomainKeyword)
rule.addressItems = append(rule.addressItems, item) rule.destinationAddressItems = append(rule.destinationAddressItems, item)
rule.allItems = append(rule.allItems, item) rule.allItems = append(rule.allItems, item)
} }
if len(options.DomainRegex) > 0 { if len(options.DomainRegex) > 0 {
@@ -103,17 +106,17 @@ func NewDefaultDNSRule(router adapter.Router, logger log.ContextLogger, options
if err != nil { if err != nil {
return nil, E.Cause(err, "domain_regex") return nil, E.Cause(err, "domain_regex")
} }
rule.addressItems = append(rule.addressItems, item) rule.destinationAddressItems = append(rule.destinationAddressItems, item)
rule.allItems = append(rule.allItems, item) rule.allItems = append(rule.allItems, item)
} }
if len(options.Geosite) > 0 { if len(options.Geosite) > 0 {
item := NewGeositeItem(router, logger, options.Geosite) item := NewGeositeItem(router, logger, options.Geosite)
rule.addressItems = append(rule.addressItems, item) rule.destinationAddressItems = append(rule.destinationAddressItems, item)
rule.allItems = append(rule.allItems, item) rule.allItems = append(rule.allItems, item)
} }
if len(options.SourceGeoIP) > 0 { if len(options.SourceGeoIP) > 0 {
item := NewGeoIPItem(router, logger, true, options.SourceGeoIP) item := NewGeoIPItem(router, logger, true, options.SourceGeoIP)
rule.items = append(rule.items, item) rule.sourceAddressItems = append(rule.sourceAddressItems, item)
rule.allItems = append(rule.allItems, item) rule.allItems = append(rule.allItems, item)
} }
if len(options.SourceIPCIDR) > 0 { if len(options.SourceIPCIDR) > 0 {
@@ -121,12 +124,12 @@ func NewDefaultDNSRule(router adapter.Router, logger log.ContextLogger, options
if err != nil { if err != nil {
return nil, E.Cause(err, "source_ipcidr") return nil, E.Cause(err, "source_ipcidr")
} }
rule.items = append(rule.items, item) rule.sourceAddressItems = append(rule.sourceAddressItems, item)
rule.allItems = append(rule.allItems, item) rule.allItems = append(rule.allItems, item)
} }
if len(options.SourcePort) > 0 { if len(options.SourcePort) > 0 {
item := NewPortItem(true, options.SourcePort) item := NewPortItem(true, options.SourcePort)
rule.items = append(rule.items, item) rule.sourcePortItems = append(rule.sourcePortItems, item)
rule.allItems = append(rule.allItems, item) rule.allItems = append(rule.allItems, item)
} }
if len(options.SourcePortRange) > 0 { if len(options.SourcePortRange) > 0 {
@@ -134,12 +137,12 @@ func NewDefaultDNSRule(router adapter.Router, logger log.ContextLogger, options
if err != nil { if err != nil {
return nil, E.Cause(err, "source_port_range") return nil, E.Cause(err, "source_port_range")
} }
rule.items = append(rule.items, item) rule.sourcePortItems = append(rule.sourcePortItems, item)
rule.allItems = append(rule.allItems, item) rule.allItems = append(rule.allItems, item)
} }
if len(options.Port) > 0 { if len(options.Port) > 0 {
item := NewPortItem(false, options.Port) item := NewPortItem(false, options.Port)
rule.items = append(rule.items, item) rule.destinationPortItems = append(rule.destinationPortItems, item)
rule.allItems = append(rule.allItems, item) rule.allItems = append(rule.allItems, item)
} }
if len(options.PortRange) > 0 { if len(options.PortRange) > 0 {
@@ -147,7 +150,7 @@ func NewDefaultDNSRule(router adapter.Router, logger log.ContextLogger, options
if err != nil { if err != nil {
return nil, E.Cause(err, "port_range") return nil, E.Cause(err, "port_range")
} }
rule.items = append(rule.items, item) rule.destinationPortItems = append(rule.destinationPortItems, item)
rule.allItems = append(rule.allItems, item) rule.allItems = append(rule.allItems, item)
} }
if len(options.ProcessName) > 0 { if len(options.ProcessName) > 0 {
@@ -225,18 +228,59 @@ func (r *DefaultDNSRule) Match(metadata *adapter.InboundContext) bool {
return r.invert return r.invert
} }
} }
if len(r.addressItems) > 0 {
var addressMatch bool if len(r.sourceAddressItems) > 0 {
for _, item := range r.addressItems { var sourceAddressMatch bool
for _, item := range r.sourceAddressItems {
if item.Match(metadata) { if item.Match(metadata) {
addressMatch = true sourceAddressMatch = true
break break
} }
} }
if !addressMatch { if !sourceAddressMatch {
return r.invert return r.invert
} }
} }
if len(r.sourcePortItems) > 0 {
var sourcePortMatch bool
for _, item := range r.sourcePortItems {
if item.Match(metadata) {
sourcePortMatch = true
break
}
}
if !sourcePortMatch {
return r.invert
}
}
if len(r.destinationAddressItems) > 0 {
var destinationAddressMatch bool
for _, item := range r.destinationAddressItems {
if item.Match(metadata) {
destinationAddressMatch = true
break
}
}
if !destinationAddressMatch {
return r.invert
}
}
if len(r.destinationPortItems) > 0 {
var destinationPortMatch bool
for _, item := range r.destinationPortItems {
if item.Match(metadata) {
destinationPortMatch = true
break
}
}
if !destinationPortMatch {
return r.invert
}
}
return !r.invert return !r.invert
} }

View File

@@ -22,6 +22,10 @@ func startInstance(t *testing.T, options option.Options) {
options.Log = &option.LogOptions{ options.Log = &option.LogOptions{
Level: "trace", Level: "trace",
} }
} else {
options.Log = &option.LogOptions{
Level: "warning",
}
} }
var instance *box.Box var instance *box.Box
var err error var err error
@@ -78,6 +82,20 @@ func testSuitSimple(t *testing.T, clientPort uint16, testPort uint16) {
require.NoError(t, testPingPongWithPacketConn(t, testPort, dialUDP)) require.NoError(t, testPingPongWithPacketConn(t, testPort, dialUDP))
} }
func testSuitSimple1(t *testing.T, clientPort uint16, testPort uint16) {
dialer := socks.NewClient(N.SystemDialer, M.ParseSocksaddrHostPort("127.0.0.1", clientPort), socks.Version5, "", "")
dialTCP := func() (net.Conn, error) {
return dialer.DialContext(context.Background(), "tcp", M.ParseSocksaddrHostPort("127.0.0.1", testPort))
}
dialUDP := func() (net.PacketConn, error) {
return dialer.ListenPacket(context.Background(), M.ParseSocksaddrHostPort("127.0.0.1", testPort))
}
require.NoError(t, testPingPongWithConn(t, testPort, dialTCP))
require.NoError(t, testPingPongWithPacketConn(t, testPort, dialUDP))
require.NoError(t, testPingPongWithConn(t, testPort, dialTCP))
require.NoError(t, testLargeDataWithPacketConn(t, testPort, dialUDP))
}
func testSuitWg(t *testing.T, clientPort uint16, testPort uint16) { func testSuitWg(t *testing.T, clientPort uint16, testPort uint16) {
dialer := socks.NewClient(N.SystemDialer, M.ParseSocksaddrHostPort("127.0.0.1", clientPort), socks.Version5, "", "") dialer := socks.NewClient(N.SystemDialer, M.ParseSocksaddrHostPort("127.0.0.1", clientPort), socks.Version5, "", "")
dialTCP := func() (net.Conn, error) { dialTCP := func() (net.Conn, error) {
@@ -90,8 +108,8 @@ func testSuitWg(t *testing.T, clientPort uint16, testPort uint16) {
} }
return bufio.NewUnbindPacketConn(conn), nil return bufio.NewUnbindPacketConn(conn), nil
} }
require.NoError(t, testPingPongWithConn(t, testPort, dialTCP))
require.NoError(t, testPingPongWithPacketConn(t, testPort, dialUDP))
require.NoError(t, testLargeDataWithConn(t, testPort, dialTCP)) require.NoError(t, testLargeDataWithConn(t, testPort, dialTCP))
require.NoError(t, testLargeDataWithPacketConn(t, testPort, dialUDP)) require.NoError(t, testLargeDataWithPacketConn(t, testPort, dialUDP))
// require.NoError(t, testPingPongWithConn(t, testPort, dialTCP))
// require.NoError(t, testPingPongWithPacketConn(t, testPort, dialUDP))
} }

View File

@@ -183,6 +183,7 @@ func testPingPongWithConn(t *testing.T, port uint16, cc func() (net.Conn, error)
if err != nil { if err != nil {
return err return err
} }
defer c.Close()
pingCh, pongCh, test := newPingPongPair() pingCh, pongCh, test := newPingPongPair()
go func() { go func() {
@@ -245,6 +246,7 @@ func testPingPongWithPacketConn(t *testing.T, port uint16, pcc func() (net.Packe
if err != nil { if err != nil {
return err return err
} }
defer pc.Close()
go func() { go func() {
if _, err := pc.WriteTo([]byte("ping"), rAddr); err != nil { if _, err := pc.WriteTo([]byte("ping"), rAddr); err != nil {
@@ -301,6 +303,7 @@ func testLargeDataWithConn(t *testing.T, port uint16, cc func() (net.Conn, error
if err != nil { if err != nil {
return err return err
} }
defer c.Close()
go func() { go func() {
c, err := l.Accept() c, err := l.Accept()
@@ -382,21 +385,24 @@ func testLargeDataWithPacketConn(t *testing.T, port uint16, pcc func() (net.Pack
hashMap := map[int][]byte{} hashMap := map[int][]byte{}
mux := sync.Mutex{} mux := sync.Mutex{}
for i := 0; i < times; i++ { for i := 0; i < times; i++ {
buf := make([]byte, chunkSize) go func(idx int) {
if _, err = rand.Read(buf[1:]); err != nil { buf := make([]byte, chunkSize)
t.Log(err.Error()) if _, err := rand.Read(buf[1:]); err != nil {
continue t.Log(err.Error())
} return
buf[0] = byte(i) }
buf[0] = byte(idx)
hash := md5.Sum(buf) hash := md5.Sum(buf)
mux.Lock() mux.Lock()
hashMap[i] = hash[:] hashMap[idx] = hash[:]
mux.Unlock() mux.Unlock()
if _, err = pc.WriteTo(buf, addr); err != nil {
t.Log(err) if _, err := pc.WriteTo(buf, addr); err != nil {
continue t.Log(err.Error())
} return
}
}(i)
} }
return hashMap, nil return hashMap, nil
@@ -432,6 +438,7 @@ func testLargeDataWithPacketConn(t *testing.T, port uint16, pcc func() (net.Pack
if err != nil { if err != nil {
return err return err
} }
defer pc.Close()
go func() { go func() {
sendHash, err := writeRandData(pc, rAddr) sendHash, err := writeRandData(pc, rAddr)

View File

@@ -0,0 +1,52 @@
{
"log": {
"loglevel": "debug"
},
"inbounds": [
{
"listen": "127.0.0.1",
"port": "1080",
"protocol": "socks",
"settings": {
"auth": "noauth",
"udp": true,
"ip": "127.0.0.1"
}
}
],
"outbounds": [
{
"protocol": "vmess",
"settings": {
"vnext": [
{
"address": "127.0.0.1",
"port": 1234,
"users": [
{
"id": ""
}
]
}
]
},
"streamSettings": {
"network": "ws",
"security": "tls",
"tlsSettings": {
"serverName": "example.org",
"certificates": [
{
"certificateFile": "/path/to/certificate.crt",
"keyFile": "/path/to/private.key"
}
]
},
"wsSettings": {
"maxEarlyData": 2048,
"earlyDataHeaderName": ""
}
}
}
]
}

View File

@@ -0,0 +1,41 @@
{
"log": {
"loglevel": "debug"
},
"inbounds": [
{
"listen": "0.0.0.0",
"port": 1234,
"protocol": "vmess",
"settings": {
"clients": [
{
"id": "b831381d-6324-4d53-ad4f-8cda48b30811"
}
]
},
"streamSettings": {
"network": "ws",
"security": "tls",
"tlsSettings": {
"serverName": "example.org",
"certificates": [
{
"certificateFile": "/path/to/certificate.crt",
"keyFile": "/path/to/private.key"
}
]
},
"wsSettings": {
"maxEarlyData": 2048,
"earlyDataHeaderName": ""
}
}
}
],
"outbounds": [
{
"protocol": "freedom"
}
]
}

View File

@@ -10,9 +10,6 @@ import (
func TestProxyProtocol(t *testing.T) { func TestProxyProtocol(t *testing.T) {
startInstance(t, option.Options{ startInstance(t, option.Options{
Log: &option.LogOptions{
Level: "error",
},
Inbounds: []option.Inbound{ Inbounds: []option.Inbound{
{ {
Type: C.TypeMixed, Type: C.TypeMixed,

View File

@@ -10,13 +10,15 @@ require (
github.com/docker/docker v20.10.17+incompatible github.com/docker/docker v20.10.17+incompatible
github.com/docker/go-connections v0.4.0 github.com/docker/go-connections v0.4.0
github.com/gofrs/uuid v4.2.0+incompatible github.com/gofrs/uuid v4.2.0+incompatible
github.com/sagernet/sing v0.0.0-20220829115648-e09c9f3fc812 github.com/sagernet/sing v0.0.0-20220913004915-27ddefbb8921
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6 github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6
github.com/spyzhov/ajson v0.7.1 github.com/spyzhov/ajson v0.7.1
github.com/stretchr/testify v1.8.0 github.com/stretchr/testify v1.8.0
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b
) )
//replace github.com/sagernet/sing => ../../sing
require ( require (
berty.tech/go-libtor v1.0.385 // indirect berty.tech/go-libtor v1.0.385 // indirect
github.com/Microsoft/go-winio v0.5.1 // indirect github.com/Microsoft/go-winio v0.5.1 // indirect
@@ -35,7 +37,6 @@ require (
github.com/golang/mock v1.6.0 // indirect github.com/golang/mock v1.6.0 // indirect
github.com/golang/protobuf v1.5.2 // indirect github.com/golang/protobuf v1.5.2 // indirect
github.com/google/btree v1.0.1 // indirect github.com/google/btree v1.0.1 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/hashicorp/yamux v0.1.1 // indirect github.com/hashicorp/yamux v0.1.1 // indirect
github.com/klauspost/cpuid/v2 v2.1.0 // indirect github.com/klauspost/cpuid/v2 v2.1.0 // indirect
github.com/libdns/libdns v0.2.1 // indirect github.com/libdns/libdns v0.2.1 // indirect
@@ -59,25 +60,26 @@ require (
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect
github.com/sagernet/netlink v0.0.0-20220826133217-3fb4ff92ea17 // indirect github.com/sagernet/netlink v0.0.0-20220826133217-3fb4ff92ea17 // indirect
github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb // indirect github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb // indirect
github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666 // indirect github.com/sagernet/sing-dns v0.0.0-20220903082137-b1102b8fc961 // indirect
github.com/sagernet/sing-tun v0.0.0-20220828031750-185b6c880a83 // indirect github.com/sagernet/sing-tun v0.0.0-20220828031750-185b6c880a83 // indirect
github.com/sagernet/sing-vmess v0.0.0-20220829020559-33915075430c // indirect github.com/sagernet/sing-vmess v0.0.0-20220913015714-c4ab86d40e12 // indirect
github.com/sagernet/smux v0.0.0-20220812084127-e2d085ee3939 // indirect github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195 // indirect
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e // indirect
github.com/sirupsen/logrus v1.8.1 // indirect github.com/sirupsen/logrus v1.8.1 // indirect
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
go.uber.org/atomic v1.10.0 // indirect go.uber.org/atomic v1.10.0 // indirect
go.uber.org/multierr v1.6.0 // indirect go.uber.org/multierr v1.6.0 // indirect
go.uber.org/zap v1.22.0 // indirect go.uber.org/zap v1.22.0 // indirect
go4.org/netipx v0.0.0-20220812043211-3cc044ffd68d // indirect go4.org/netipx v0.0.0-20220812043211-3cc044ffd68d // indirect
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d // indirect golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 // indirect
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 // indirect golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 // indirect
golang.org/x/text v0.3.7 // indirect golang.org/x/text v0.3.7 // indirect
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
golang.org/x/tools v0.1.11-0.20220513221640-090b14e8501f // indirect golang.org/x/tools v0.1.11-0.20220513221640-090b14e8501f // indirect
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // indirect golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // indirect
golang.zx2c4.com/wireguard v0.0.0-20220703234212-c31a7b1ab478 // indirect golang.zx2c4.com/wireguard v0.0.0-20220829161405-d1d08426b27b // indirect
google.golang.org/genproto v0.0.0-20210722135532-667f2b7c528f // indirect google.golang.org/genproto v0.0.0-20210722135532-667f2b7c528f // indirect
google.golang.org/grpc v1.49.0 // indirect google.golang.org/grpc v1.49.0 // indirect
google.golang.org/protobuf v1.28.1 // indirect google.golang.org/protobuf v1.28.1 // indirect

View File

@@ -85,8 +85,6 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
@@ -155,18 +153,20 @@ github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb h1:wc0yQ+SBn4TaTY
github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb/go.mod h1:MIccjRKnPTjWwAOpl+AUGWOkzyTd9tERytudxu+1ra4= github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb/go.mod h1:MIccjRKnPTjWwAOpl+AUGWOkzyTd9tERytudxu+1ra4=
github.com/sagernet/sing v0.0.0-20220812082120-05f9836bff8f/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY= github.com/sagernet/sing v0.0.0-20220812082120-05f9836bff8f/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY= github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
github.com/sagernet/sing v0.0.0-20220829115648-e09c9f3fc812 h1:z5AqoJcMy+MaD9pstLt08c95t2Txlzvp5pk4lqXBQPc= github.com/sagernet/sing v0.0.0-20220913004915-27ddefbb8921 h1:xUHzlIbdlPV/fkToIO9futp9lmKIY+72ezk/whQ8XsI=
github.com/sagernet/sing v0.0.0-20220829115648-e09c9f3fc812/go.mod h1:kZvzh1VDa/Dg/Bt5WaYKU0jl5ept8KKDpl3Ay4gRtRQ= github.com/sagernet/sing v0.0.0-20220913004915-27ddefbb8921/go.mod h1:kZvzh1VDa/Dg/Bt5WaYKU0jl5ept8KKDpl3Ay4gRtRQ=
github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666 h1:XUTocA/Ek0dFxUX+xJCWMPPFZCn2GC/uLrBjTSr1vHY= github.com/sagernet/sing-dns v0.0.0-20220903082137-b1102b8fc961 h1:5JeqhvCGV6AQQiAO0V67Loh2eyO3JNjIQnvRF8NnTE0=
github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666/go.mod h1:eDyH7AJmqBGjZQdQmpZIzlbTREudZuWDExMuGKgjRVM= github.com/sagernet/sing-dns v0.0.0-20220903082137-b1102b8fc961/go.mod h1:vKBBy4mNJRaFuJ8H6kYIOPofsZ1JT5mgdwIlebtvnZ4=
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6 h1:JJfDeYYhWunvtxsU/mOVNTmFQmnzGx9dY034qG6G3g4= github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6 h1:JJfDeYYhWunvtxsU/mOVNTmFQmnzGx9dY034qG6G3g4=
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6/go.mod h1:EX3RbZvrwAkPI2nuGa78T2iQXmrkT+/VQtskjou42xM= github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6/go.mod h1:EX3RbZvrwAkPI2nuGa78T2iQXmrkT+/VQtskjou42xM=
github.com/sagernet/sing-tun v0.0.0-20220828031750-185b6c880a83 h1:SoWiHYuOCVedqA7T/CJSZUUrcPGKQb2wFKEq8DphiAI= github.com/sagernet/sing-tun v0.0.0-20220828031750-185b6c880a83 h1:SoWiHYuOCVedqA7T/CJSZUUrcPGKQb2wFKEq8DphiAI=
github.com/sagernet/sing-tun v0.0.0-20220828031750-185b6c880a83/go.mod h1:76r07HS1WRcEI4mE9pFsohfTBUt1j/G9Avz6DaOP3VU= github.com/sagernet/sing-tun v0.0.0-20220828031750-185b6c880a83/go.mod h1:76r07HS1WRcEI4mE9pFsohfTBUt1j/G9Avz6DaOP3VU=
github.com/sagernet/sing-vmess v0.0.0-20220829020559-33915075430c h1:92Gn78/z/t6CkzZ4XWG/uPiCxhUmjPULFEHFMDY6K8k= github.com/sagernet/sing-vmess v0.0.0-20220913015714-c4ab86d40e12 h1:4HYGbTDDemgBVTmaspXbkgjJlXc3hYVjNxSddJndq8Y=
github.com/sagernet/sing-vmess v0.0.0-20220829020559-33915075430c/go.mod h1:82O6gzbxLha/W/jxSVQbsqf2lVdRTjMIgyLug0lpJps= github.com/sagernet/sing-vmess v0.0.0-20220913015714-c4ab86d40e12/go.mod h1:u66Vv7NHXJWfeAmhh7JuJp/cwxmuQlM56QoZ7B7Mmd0=
github.com/sagernet/smux v0.0.0-20220812084127-e2d085ee3939 h1:pB1Dh1NbwVrLhQhotr4O4Hs3yhiBzmg3AvnUyYjL4x4= github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195 h1:5VBIbVw9q7aKbrFdT83mjkyvQ+VaRsQ6yflTepfln38=
github.com/sagernet/smux v0.0.0-20220812084127-e2d085ee3939/go.mod h1:yedWtra8nyGJ+SyI+ziwuaGMzBatbB10P1IOOZbbSK8= github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195/go.mod h1:yedWtra8nyGJ+SyI+ziwuaGMzBatbB10P1IOOZbbSK8=
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e h1:7uw2njHFGE+VpWamge6o56j2RWk4omF6uLKKxMmcWvs=
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e/go.mod h1:45TUl8+gH4SIKr4ykREbxKWTxkDlSzFENzctB1dVRRY=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
@@ -205,8 +205,8 @@ golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaE
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d h1:3qF+Z8Hkrw9sOhrFHti9TlB1Hkac1x+DNRkv0XQiFjo= golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 h1:Y/gsMcFOcR+6S6f3YeMKl5g+dZMEWqcz5Czj/GWYbkM=
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA= golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA=
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA= golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA=
@@ -275,8 +275,8 @@ golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 h1:UiNENfZ8gDvpiWw7IpOMQ27spWmThO1RwwdQVbJahJM= golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 h1:v6hYoSR9T5oet+pMXwUWkbiVqx/63mlHjefrHmxwfeY=
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@@ -311,8 +311,8 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 h1:Ug9qvr1myri/zFN6xL17LSCBGFDnphBBhzmILHsM5TY= golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 h1:Ug9qvr1myri/zFN6xL17LSCBGFDnphBBhzmILHsM5TY=
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI= golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
golang.zx2c4.com/wireguard v0.0.0-20220703234212-c31a7b1ab478 h1:vDy//hdR+GnROE3OdYbQKt9rdtNdHkDtONvpRwmls/0= golang.zx2c4.com/wireguard v0.0.0-20220829161405-d1d08426b27b h1:qgrKnOfe1zyURRNdmDlGbN32i38Zjmw0B1+TMdHcOvg=
golang.zx2c4.com/wireguard v0.0.0-20220703234212-c31a7b1ab478/go.mod h1:bVQfyl2sCM/QIIGHpWbFGfHPuDvqnCNkT6MQLTCjO/U= golang.zx2c4.com/wireguard v0.0.0-20220829161405-d1d08426b27b/go.mod h1:6y4CqPAy54NwiN4nC8K+R1eMpQDB1P2d25qmunh2RSA=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=

View File

@@ -14,9 +14,6 @@ func TestHysteriaSelf(t *testing.T) {
} }
_, certPem, keyPem := createSelfSignedCertificate(t, "example.org") _, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
startInstance(t, option.Options{ startInstance(t, option.Options{
Log: &option.LogOptions{
Level: "error",
},
Inbounds: []option.Inbound{ Inbounds: []option.Inbound{
{ {
Type: C.TypeMixed, Type: C.TypeMixed,
@@ -83,7 +80,7 @@ func TestHysteriaSelf(t *testing.T) {
}, },
}, },
}) })
testSuitSimple(t, clientPort, testPort) testSuitSimple1(t, clientPort, testPort)
} }
func TestHysteriaInbound(t *testing.T) { func TestHysteriaInbound(t *testing.T) {
@@ -92,9 +89,6 @@ func TestHysteriaInbound(t *testing.T) {
} }
caPem, certPem, keyPem := createSelfSignedCertificate(t, "example.org") caPem, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
startInstance(t, option.Options{ startInstance(t, option.Options{
Log: &option.LogOptions{
Level: "error",
},
Inbounds: []option.Inbound{ Inbounds: []option.Inbound{
{ {
Type: C.TypeHysteria, Type: C.TypeHysteria,
@@ -145,9 +139,6 @@ func TestHysteriaOutbound(t *testing.T) {
}, },
}) })
startInstance(t, option.Options{ startInstance(t, option.Options{
Log: &option.LogOptions{
Level: "error",
},
Inbounds: []option.Inbound{ Inbounds: []option.Inbound{
{ {
Type: C.TypeMixed, Type: C.TypeMixed,
@@ -180,5 +171,5 @@ func TestHysteriaOutbound(t *testing.T) {
}, },
}, },
}) })
testSuitSimple(t, clientPort, testPort) testSuit(t, clientPort, testPort)
} }

View File

@@ -13,9 +13,6 @@ func TestChainedInbound(t *testing.T) {
method := shadowaead_2022.List[0] method := shadowaead_2022.List[0]
password := mkBase64(t, 16) password := mkBase64(t, 16)
startInstance(t, option.Options{ startInstance(t, option.Options{
Log: &option.LogOptions{
Level: "error",
},
Inbounds: []option.Inbound{ Inbounds: []option.Inbound{
{ {
Type: C.TypeMixed, Type: C.TypeMixed,
@@ -62,10 +59,8 @@ func TestChainedInbound(t *testing.T) {
ShadowsocksOptions: option.ShadowsocksOutboundOptions{ ShadowsocksOptions: option.ShadowsocksOutboundOptions{
Method: method, Method: method,
Password: password, Password: password,
OutboundDialerOptions: option.OutboundDialerOptions{ DialerOptions: option.DialerOptions{
DialerOptions: option.DialerOptions{ Detour: "detour-out",
Detour: "detour-out",
},
}, },
}, },
}, },

View File

@@ -37,9 +37,6 @@ func testShadowsocksMux(t *testing.T, protocol string) {
method := shadowaead_2022.List[0] method := shadowaead_2022.List[0]
password := mkBase64(t, 16) password := mkBase64(t, 16)
startInstance(t, option.Options{ startInstance(t, option.Options{
Log: &option.LogOptions{
Level: "error",
},
Inbounds: []option.Inbound{ Inbounds: []option.Inbound{
{ {
Type: C.TypeMixed, Type: C.TypeMixed,
@@ -101,9 +98,6 @@ func testShadowsocksMux(t *testing.T, protocol string) {
func testVMessMux(t *testing.T, protocol string) { func testVMessMux(t *testing.T, protocol string) {
user, _ := uuid.NewV4() user, _ := uuid.NewV4()
startInstance(t, option.Options{ startInstance(t, option.Options{
Log: &option.LogOptions{
Level: "error",
},
Inbounds: []option.Inbound{ Inbounds: []option.Inbound{
{ {
Type: C.TypeMixed, Type: C.TypeMixed,

View File

@@ -13,9 +13,6 @@ import (
func TestNaiveInboundWithNginx(t *testing.T) { func TestNaiveInboundWithNginx(t *testing.T) {
caPem, certPem, keyPem := createSelfSignedCertificate(t, "example.org") caPem, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
startInstance(t, option.Options{ startInstance(t, option.Options{
Log: &option.LogOptions{
Level: "error",
},
Inbounds: []option.Inbound{ Inbounds: []option.Inbound{
{ {
Type: C.TypeNaive, Type: C.TypeNaive,
@@ -62,9 +59,6 @@ func TestNaiveInboundWithNginx(t *testing.T) {
func TestNaiveInbound(t *testing.T) { func TestNaiveInbound(t *testing.T) {
caPem, certPem, keyPem := createSelfSignedCertificate(t, "example.org") caPem, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
startInstance(t, option.Options{ startInstance(t, option.Options{
Log: &option.LogOptions{
Level: "error",
},
Inbounds: []option.Inbound{ Inbounds: []option.Inbound{
{ {
Type: C.TypeNaive, Type: C.TypeNaive,
@@ -110,9 +104,6 @@ func TestNaiveHTTP3Inbound(t *testing.T) {
} }
caPem, certPem, keyPem := createSelfSignedCertificate(t, "example.org") caPem, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
startInstance(t, option.Options{ startInstance(t, option.Options{
Log: &option.LogOptions{
Level: "error",
},
Inbounds: []option.Inbound{ Inbounds: []option.Inbound{
{ {
Type: C.TypeNaive, Type: C.TypeNaive,

View File

@@ -77,9 +77,6 @@ func testShadowsocksInboundWithShadowsocksRust(t *testing.T, method string, pass
Cmd: []string{"-s", F.ToString("127.0.0.1:", serverPort), "-b", F.ToString("0.0.0.0:", clientPort), "-m", method, "-k", password, "-U"}, Cmd: []string{"-s", F.ToString("127.0.0.1:", serverPort), "-b", F.ToString("0.0.0.0:", clientPort), "-m", method, "-k", password, "-U"},
}) })
startInstance(t, option.Options{ startInstance(t, option.Options{
Log: &option.LogOptions{
Level: "error",
},
Inbounds: []option.Inbound{ Inbounds: []option.Inbound{
{ {
Type: C.TypeShadowsocks, Type: C.TypeShadowsocks,
@@ -105,9 +102,6 @@ func testShadowsocksOutboundWithShadowsocksRust(t *testing.T, method string, pas
Cmd: []string{"-s", F.ToString("0.0.0.0:", serverPort), "-m", method, "-k", password, "-U"}, Cmd: []string{"-s", F.ToString("0.0.0.0:", serverPort), "-m", method, "-k", password, "-U"},
}) })
startInstance(t, option.Options{ startInstance(t, option.Options{
Log: &option.LogOptions{
Level: "error",
},
Inbounds: []option.Inbound{ Inbounds: []option.Inbound{
{ {
Type: C.TypeMixed, Type: C.TypeMixed,
@@ -138,9 +132,6 @@ func testShadowsocksOutboundWithShadowsocksRust(t *testing.T, method string, pas
func testShadowsocksSelf(t *testing.T, method string, password string) { func testShadowsocksSelf(t *testing.T, method string, password string) {
startInstance(t, option.Options{ startInstance(t, option.Options{
Log: &option.LogOptions{
Level: "error",
},
Inbounds: []option.Inbound{ Inbounds: []option.Inbound{
{ {
Type: C.TypeMixed, Type: C.TypeMixed,
@@ -199,9 +190,6 @@ func TestShadowsocksUoT(t *testing.T) {
method := shadowaead_2022.List[0] method := shadowaead_2022.List[0]
password := mkBase64(t, 16) password := mkBase64(t, 16)
startInstance(t, option.Options{ startInstance(t, option.Options{
Log: &option.LogOptions{
Level: "error",
},
Inbounds: []option.Inbound{ Inbounds: []option.Inbound{
{ {
Type: C.TypeMixed, Type: C.TypeMixed,

View File

@@ -60,10 +60,11 @@ func TestShadowTLS(t *testing.T) {
ShadowsocksOptions: option.ShadowsocksOutboundOptions{ ShadowsocksOptions: option.ShadowsocksOutboundOptions{
Method: method, Method: method,
Password: password, Password: password,
OutboundDialerOptions: option.OutboundDialerOptions{ DialerOptions: option.DialerOptions{
DialerOptions: option.DialerOptions{ Detour: "detour",
Detour: "detour", },
}, MultiplexOptions: &option.MultiplexOptions{
Enabled: true,
}, },
}, },
}, },
@@ -95,7 +96,7 @@ func TestShadowTLS(t *testing.T) {
}}, }},
}, },
}) })
testTCP(t, clientPort, testPort) testSuit(t, clientPort, testPort)
} }
func TestShadowTLSOutbound(t *testing.T) { func TestShadowTLSOutbound(t *testing.T) {
@@ -131,10 +132,8 @@ func TestShadowTLSOutbound(t *testing.T) {
{ {
Type: C.TypeSocks, Type: C.TypeSocks,
SocksOptions: option.SocksOutboundOptions{ SocksOptions: option.SocksOutboundOptions{
OutboundDialerOptions: option.OutboundDialerOptions{ DialerOptions: option.DialerOptions{
DialerOptions: option.DialerOptions{ Detour: "detour",
Detour: "detour",
},
}, },
}, },
}, },

View File

@@ -20,9 +20,6 @@ func TestTrojanOutbound(t *testing.T) {
}, },
}) })
startInstance(t, option.Options{ startInstance(t, option.Options{
Log: &option.LogOptions{
Level: "error",
},
Inbounds: []option.Inbound{ Inbounds: []option.Inbound{
{ {
Type: C.TypeMixed, Type: C.TypeMixed,
@@ -58,9 +55,6 @@ func TestTrojanOutbound(t *testing.T) {
func TestTrojanSelf(t *testing.T) { func TestTrojanSelf(t *testing.T) {
_, certPem, keyPem := createSelfSignedCertificate(t, "example.org") _, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
startInstance(t, option.Options{ startInstance(t, option.Options{
Log: &option.LogOptions{
Level: "error",
},
Inbounds: []option.Inbound{ Inbounds: []option.Inbound{
{ {
Type: C.TypeMixed, Type: C.TypeMixed,
@@ -128,3 +122,62 @@ func TestTrojanSelf(t *testing.T) {
}) })
testSuit(t, clientPort, testPort) testSuit(t, clientPort, testPort)
} }
func TestTrojanPlainSelf(t *testing.T) {
startInstance(t, option.Options{
Inbounds: []option.Inbound{
{
Type: C.TypeMixed,
Tag: "mixed-in",
MixedOptions: option.HTTPMixedInboundOptions{
ListenOptions: option.ListenOptions{
Listen: option.ListenAddress(netip.IPv4Unspecified()),
ListenPort: clientPort,
},
},
},
{
Type: C.TypeTrojan,
TrojanOptions: option.TrojanInboundOptions{
ListenOptions: option.ListenOptions{
Listen: option.ListenAddress(netip.IPv4Unspecified()),
ListenPort: serverPort,
},
Users: []option.TrojanUser{
{
Name: "sekai",
Password: "password",
},
},
},
},
},
Outbounds: []option.Outbound{
{
Type: C.TypeDirect,
},
{
Type: C.TypeTrojan,
Tag: "trojan-out",
TrojanOptions: option.TrojanOutboundOptions{
ServerOptions: option.ServerOptions{
Server: "127.0.0.1",
ServerPort: serverPort,
},
Password: "password",
},
},
},
Route: &option.RouteOptions{
Rules: []option.Rule{
{
DefaultOptions: option.DefaultRule{
Inbound: []string{"mixed-in"},
Outbound: "trojan-out",
},
},
},
},
})
testSuit(t, clientPort, testPort)
}

View File

@@ -27,9 +27,6 @@ func testV2RayGRPCInbound(t *testing.T, forceLite bool) {
require.NoError(t, err) require.NoError(t, err)
_, certPem, keyPem := createSelfSignedCertificate(t, "example.org") _, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
startInstance(t, option.Options{ startInstance(t, option.Options{
Log: &option.LogOptions{
Level: "error",
},
Inbounds: []option.Inbound{ Inbounds: []option.Inbound{
{ {
Type: C.TypeVMess, Type: C.TypeVMess,
@@ -125,9 +122,6 @@ func testV2RayGRPCOutbound(t *testing.T, forceLite bool) {
}, },
}) })
startInstance(t, option.Options{ startInstance(t, option.Options{
Log: &option.LogOptions{
Level: "error",
},
Inbounds: []option.Inbound{ Inbounds: []option.Inbound{
{ {
Type: C.TypeMixed, Type: C.TypeMixed,
@@ -167,7 +161,7 @@ func testV2RayGRPCOutbound(t *testing.T, forceLite bool) {
}, },
}, },
}) })
testSuitSimple(t, clientPort, testPort) testSuit(t, clientPort, testPort)
} }
func TestV2RayGRPCLite(t *testing.T) { func TestV2RayGRPCLite(t *testing.T) {

View File

@@ -11,31 +11,6 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func TestV2RayWebscoketSelf(t *testing.T) {
t.Run("basic", func(t *testing.T) {
testV2RayTransportSelf(t, &option.V2RayTransportOptions{
Type: C.V2RayTransportTypeWebsocket,
})
})
t.Run("v2ray early data", func(t *testing.T) {
testV2RayTransportSelf(t, &option.V2RayTransportOptions{
Type: C.V2RayTransportTypeWebsocket,
WebsocketOptions: option.V2RayWebsocketOptions{
MaxEarlyData: 2048,
},
})
})
t.Run("xray early data", func(t *testing.T) {
testV2RayTransportSelf(t, &option.V2RayTransportOptions{
Type: C.V2RayTransportTypeWebsocket,
WebsocketOptions: option.V2RayWebsocketOptions{
MaxEarlyData: 2048,
EarlyDataHeaderName: "Sec-WebSocket-Protocol",
},
})
})
}
func TestV2RayHTTPSelf(t *testing.T) { func TestV2RayHTTPSelf(t *testing.T) {
testV2RayTransportSelf(t, &option.V2RayTransportOptions{ testV2RayTransportSelf(t, &option.V2RayTransportOptions{
Type: C.V2RayTransportTypeHTTP, Type: C.V2RayTransportTypeHTTP,
@@ -69,9 +44,6 @@ func testVMessTransportSelf(t *testing.T, server *option.V2RayTransportOptions,
require.NoError(t, err) require.NoError(t, err)
_, certPem, keyPem := createSelfSignedCertificate(t, "example.org") _, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
startInstance(t, option.Options{ startInstance(t, option.Options{
Log: &option.LogOptions{
Level: "error",
},
Inbounds: []option.Inbound{ Inbounds: []option.Inbound{
{ {
Type: C.TypeMixed, Type: C.TypeMixed,
@@ -148,9 +120,6 @@ func testTrojanTransportSelf(t *testing.T, server *option.V2RayTransportOptions,
require.NoError(t, err) require.NoError(t, err)
_, certPem, keyPem := createSelfSignedCertificate(t, "example.org") _, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
startInstance(t, option.Options{ startInstance(t, option.Options{
Log: &option.LogOptions{
Level: "error",
},
Inbounds: []option.Inbound{ Inbounds: []option.Inbound{
{ {
Type: C.TypeMixed, Type: C.TypeMixed,
@@ -229,9 +198,6 @@ func TestVMessQUICSelf(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
_, certPem, keyPem := createSelfSignedCertificate(t, "example.org") _, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
startInstance(t, option.Options{ startInstance(t, option.Options{
Log: &option.LogOptions{
Level: "error",
},
Inbounds: []option.Inbound{ Inbounds: []option.Inbound{
{ {
Type: C.TypeMixed, Type: C.TypeMixed,
@@ -300,16 +266,13 @@ func TestVMessQUICSelf(t *testing.T) {
}, },
}, },
}) })
testSuitSimple(t, clientPort, testPort) testSuit(t, clientPort, testPort)
} }
func testV2RayTransportNOTLSSelf(t *testing.T, transport *option.V2RayTransportOptions) { func testV2RayTransportNOTLSSelf(t *testing.T, transport *option.V2RayTransportOptions) {
user, err := uuid.DefaultGenerator.NewV4() user, err := uuid.DefaultGenerator.NewV4()
require.NoError(t, err) require.NoError(t, err)
startInstance(t, option.Options{ startInstance(t, option.Options{
Log: &option.LogOptions{
Level: "error",
},
Inbounds: []option.Inbound{ Inbounds: []option.Inbound{
{ {
Type: C.TypeMixed, Type: C.TypeMixed,
@@ -367,5 +330,5 @@ func testV2RayTransportNOTLSSelf(t *testing.T, transport *option.V2RayTransportO
}, },
}, },
}) })
testSuitSimple(t, clientPort, testPort) testSuit(t, clientPort, testPort)
} }

197
test/v2ray_ws_test.go Normal file
View File

@@ -0,0 +1,197 @@
package main
import (
"net/netip"
"os"
"testing"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option"
"github.com/gofrs/uuid"
"github.com/spyzhov/ajson"
"github.com/stretchr/testify/require"
)
func TestV2RayWebsocket(t *testing.T) {
t.Run("self", func(t *testing.T) {
testV2RayTransportSelf(t, &option.V2RayTransportOptions{
Type: C.V2RayTransportTypeWebsocket,
})
})
t.Run("self-early-data", func(t *testing.T) {
testV2RayTransportSelf(t, &option.V2RayTransportOptions{
Type: C.V2RayTransportTypeWebsocket,
WebsocketOptions: option.V2RayWebsocketOptions{
MaxEarlyData: 2048,
},
})
})
t.Run("self-xray-early-data", func(t *testing.T) {
testV2RayTransportSelf(t, &option.V2RayTransportOptions{
Type: C.V2RayTransportTypeWebsocket,
WebsocketOptions: option.V2RayWebsocketOptions{
MaxEarlyData: 2048,
EarlyDataHeaderName: "Sec-WebSocket-Protocol",
},
})
})
t.Run("inbound", func(t *testing.T) {
testV2RayWebsocketInbound(t, 0, "")
})
t.Run("inbound-early-data", func(t *testing.T) {
testV2RayWebsocketInbound(t, 2048, "")
})
t.Run("inbound-xray-early-data", func(t *testing.T) {
testV2RayWebsocketInbound(t, 2048, "Sec-WebSocket-Protocol")
})
t.Run("outbound", func(t *testing.T) {
testV2RayWebsocketOutbound(t, 0, "")
})
t.Run("outbound-early-data", func(t *testing.T) {
testV2RayWebsocketOutbound(t, 2048, "")
})
t.Run("outbound-xray-early-data", func(t *testing.T) {
testV2RayWebsocketOutbound(t, 2048, "Sec-WebSocket-Protocol")
})
}
func testV2RayWebsocketInbound(t *testing.T, maxEarlyData uint32, earlyDataHeaderName string) {
userId, err := uuid.DefaultGenerator.NewV4()
require.NoError(t, err)
_, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
startInstance(t, option.Options{
Inbounds: []option.Inbound{
{
Type: C.TypeVMess,
VMessOptions: option.VMessInboundOptions{
ListenOptions: option.ListenOptions{
Listen: option.ListenAddress(netip.IPv4Unspecified()),
ListenPort: serverPort,
},
Users: []option.VMessUser{
{
Name: "sekai",
UUID: userId.String(),
},
},
TLS: &option.InboundTLSOptions{
Enabled: true,
ServerName: "example.org",
CertificatePath: certPem,
KeyPath: keyPem,
},
Transport: &option.V2RayTransportOptions{
Type: C.V2RayTransportTypeWebsocket,
WebsocketOptions: option.V2RayWebsocketOptions{
MaxEarlyData: maxEarlyData,
EarlyDataHeaderName: earlyDataHeaderName,
},
},
},
},
},
})
content, err := os.ReadFile("config/vmess-ws-client.json")
require.NoError(t, err)
config, err := ajson.Unmarshal(content)
require.NoError(t, err)
config.MustKey("inbounds").MustIndex(0).MustKey("port").SetNumeric(float64(clientPort))
outbound := config.MustKey("outbounds").MustIndex(0)
settings := outbound.MustKey("settings").MustKey("vnext").MustIndex(0)
settings.MustKey("port").SetNumeric(float64(serverPort))
user := settings.MustKey("users").MustIndex(0)
user.MustKey("id").SetString(userId.String())
wsSettings := outbound.MustKey("streamSettings").MustKey("wsSettings")
wsSettings.MustKey("maxEarlyData").SetNumeric(float64(maxEarlyData))
wsSettings.MustKey("earlyDataHeaderName").SetString(earlyDataHeaderName)
content, err = ajson.Marshal(config)
require.NoError(t, err)
startDockerContainer(t, DockerOptions{
Image: ImageV2RayCore,
Ports: []uint16{serverPort, testPort},
EntryPoint: "v2ray",
Stdin: content,
Bind: map[string]string{
certPem: "/path/to/certificate.crt",
keyPem: "/path/to/private.key",
},
})
testSuitSimple(t, clientPort, testPort)
}
func testV2RayWebsocketOutbound(t *testing.T, maxEarlyData uint32, earlyDataHeaderName string) {
userId, err := uuid.DefaultGenerator.NewV4()
require.NoError(t, err)
_, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
content, err := os.ReadFile("config/vmess-ws-server.json")
require.NoError(t, err)
config, err := ajson.Unmarshal(content)
require.NoError(t, err)
inbound := config.MustKey("inbounds").MustIndex(0)
inbound.MustKey("port").SetNumeric(float64(serverPort))
inbound.MustKey("settings").MustKey("clients").MustIndex(0).MustKey("id").SetString(userId.String())
wsSettings := inbound.MustKey("streamSettings").MustKey("wsSettings")
wsSettings.MustKey("maxEarlyData").SetNumeric(float64(maxEarlyData))
wsSettings.MustKey("earlyDataHeaderName").SetString(earlyDataHeaderName)
content, err = ajson.Marshal(config)
require.NoError(t, err)
startDockerContainer(t, DockerOptions{
Image: ImageV2RayCore,
Ports: []uint16{serverPort, testPort},
EntryPoint: "v2ray",
Stdin: content,
Env: []string{"V2RAY_VMESS_AEAD_FORCED=false"},
Bind: map[string]string{
certPem: "/path/to/certificate.crt",
keyPem: "/path/to/private.key",
},
})
startInstance(t, option.Options{
Inbounds: []option.Inbound{
{
Type: C.TypeMixed,
Tag: "mixed-in",
MixedOptions: option.HTTPMixedInboundOptions{
ListenOptions: option.ListenOptions{
Listen: option.ListenAddress(netip.IPv4Unspecified()),
ListenPort: clientPort,
},
},
},
},
Outbounds: []option.Outbound{
{
Type: C.TypeVMess,
Tag: "vmess-out",
VMessOptions: option.VMessOutboundOptions{
ServerOptions: option.ServerOptions{
Server: "127.0.0.1",
ServerPort: serverPort,
},
UUID: userId.String(),
Security: "zero",
TLS: &option.OutboundTLSOptions{
Enabled: true,
ServerName: "example.org",
CertificatePath: certPem,
},
Transport: &option.V2RayTransportOptions{
Type: C.V2RayTransportTypeWebsocket,
WebsocketOptions: option.V2RayWebsocketOptions{
MaxEarlyData: maxEarlyData,
EarlyDataHeaderName: earlyDataHeaderName,
},
},
},
},
},
})
testSuit(t, clientPort, testPort)
}

View File

@@ -182,9 +182,6 @@ func testVMessInboundWithV2Ray(t *testing.T, security string, uuid uuid.UUID, al
}) })
startInstance(t, option.Options{ startInstance(t, option.Options{
Log: &option.LogOptions{
Level: "error",
},
Inbounds: []option.Inbound{ Inbounds: []option.Inbound{
{ {
Type: C.TypeVMess, Type: C.TypeVMess,
@@ -231,9 +228,6 @@ func testVMessOutboundWithV2Ray(t *testing.T, security string, uuid uuid.UUID, g
}) })
startInstance(t, option.Options{ startInstance(t, option.Options{
Log: &option.LogOptions{
Level: "error",
},
Inbounds: []option.Inbound{ Inbounds: []option.Inbound{
{ {
Type: C.TypeMixed, Type: C.TypeMixed,
@@ -262,14 +256,11 @@ func testVMessOutboundWithV2Ray(t *testing.T, security string, uuid uuid.UUID, g
}, },
}, },
}) })
testSuitSimple(t, clientPort, testPort) testSuit(t, clientPort, testPort)
} }
func testVMessSelf(t *testing.T, security string, uuid uuid.UUID, alterId int, globalPadding bool, authenticatedLength bool, packetAddr bool) { func testVMessSelf(t *testing.T, security string, uuid uuid.UUID, alterId int, globalPadding bool, authenticatedLength bool, packetAddr bool) {
startInstance(t, option.Options{ startInstance(t, option.Options{
Log: &option.LogOptions{
Level: "error",
},
Inbounds: []option.Inbound{ Inbounds: []option.Inbound{
{ {
Type: C.TypeMixed, Type: C.TypeMixed,

View File

@@ -21,9 +21,6 @@ func TestWireGuard(t *testing.T) {
}) })
time.Sleep(5 * time.Second) time.Sleep(5 * time.Second)
startInstance(t, option.Options{ startInstance(t, option.Options{
Log: &option.LogOptions{
Level: "error",
},
Inbounds: []option.Inbound{ Inbounds: []option.Inbound{
{ {
Type: C.TypeMixed, Type: C.TypeMixed,

View File

@@ -6,6 +6,7 @@ import (
"syscall" "syscall"
"github.com/sagernet/quic-go" "github.com/sagernet/quic-go"
"github.com/sagernet/sing-box/common/baderror"
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
) )
@@ -38,6 +39,16 @@ type StreamWrapper struct {
quic.Stream quic.Stream
} }
func (s *StreamWrapper) Read(p []byte) (n int, err error) {
n, err = s.Stream.Read(p)
return n, baderror.WrapQUIC(err)
}
func (s *StreamWrapper) Write(p []byte) (n int, err error) {
n, err = s.Stream.Write(p)
return n, baderror.WrapQUIC(err)
}
func (s *StreamWrapper) LocalAddr() net.Addr { func (s *StreamWrapper) LocalAddr() net.Addr {
return s.Conn.LocalAddr() return s.Conn.LocalAddr()
} }
@@ -50,14 +61,6 @@ func (s *StreamWrapper) Upstream() any {
return s.Stream return s.Stream
} }
func (s *StreamWrapper) ReaderReplaceable() bool {
return true
}
func (s *StreamWrapper) WriterReplaceable() bool {
return true
}
func (s *StreamWrapper) Close() error { func (s *StreamWrapper) Close() error {
s.CancelRead(0) s.CancelRead(0)
s.Stream.Close() s.Stream.Close()

View File

@@ -62,7 +62,9 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt
} }
func (c *Client) Close() error { func (c *Client) Close() error {
return common.Close(c.conn) return common.Close(
common.PtrOrNil(c.conn),
)
} }
func (c *Client) connect() (*grpc.ClientConn, error) { func (c *Client) connect() (*grpc.ClientConn, error) {

View File

@@ -5,6 +5,7 @@ import (
"crypto/tls" "crypto/tls"
"net" "net"
"os" "os"
"strings"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
@@ -13,6 +14,8 @@ import (
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials"
gM "google.golang.org/grpc/metadata"
"google.golang.org/grpc/peer"
) )
var _ adapter.V2RayServerTransport = (*Server)(nil) var _ adapter.V2RayServerTransport = (*Server)(nil)
@@ -37,7 +40,22 @@ func NewServer(ctx context.Context, options option.V2RayGRPCOptions, tlsConfig *
func (s *Server) Tun(server GunService_TunServer) error { func (s *Server) Tun(server GunService_TunServer) error {
ctx, cancel := context.WithCancel(s.ctx) ctx, cancel := context.WithCancel(s.ctx)
conn := NewGRPCConn(server, cancel) conn := NewGRPCConn(server, cancel)
go s.handler.NewConnection(ctx, conn, M.Metadata{}) var metadata M.Metadata
if remotePeer, loaded := peer.FromContext(server.Context()); loaded {
metadata.Source = M.SocksaddrFromNet(remotePeer.Addr)
}
if grpcMetadata, loaded := gM.FromIncomingContext(server.Context()); loaded {
forwardFrom := strings.Join(grpcMetadata.Get("X-Forwarded-For"), ",")
if forwardFrom != "" {
for _, from := range strings.Split(forwardFrom, ",") {
originAddr := M.ParseSocksaddr(from)
if originAddr.IsValid() {
metadata.Source = originAddr.Unwrap()
}
}
}
}
go s.handler.NewConnection(ctx, conn, metadata)
<-ctx.Done() <-ctx.Done()
return nil return nil
} }

View File

@@ -8,6 +8,7 @@ import (
"net" "net"
"net/http" "net/http"
"os" "os"
"sync"
"time" "time"
"github.com/sagernet/sing-box/common/baderror" "github.com/sagernet/sing-box/common/baderror"
@@ -28,6 +29,7 @@ type GunConn struct {
create chan struct{} create chan struct{}
err error err error
readRemaining int readRemaining int
writeAccess sync.Mutex
} }
func newGunConn(reader io.Reader, writer io.Writer, flusher http.Flusher) *GunConn { func newGunConn(reader io.Reader, writer io.Writer, flusher http.Flusher) *GunConn {
@@ -100,7 +102,9 @@ func (c *GunConn) Write(b []byte) (n int, err error) {
grpcHeader := buf.Get(5) grpcHeader := buf.Get(5)
grpcPayloadLen := uint32(1 + varuintLen + len(b)) grpcPayloadLen := uint32(1 + varuintLen + len(b))
binary.BigEndian.PutUint32(grpcHeader[1:5], grpcPayloadLen) binary.BigEndian.PutUint32(grpcHeader[1:5], grpcPayloadLen)
c.writeAccess.Lock()
_, err = bufio.Copy(c.writer, io.MultiReader(bytes.NewReader(grpcHeader), bytes.NewReader(protobufHeader[:varuintLen+1]), bytes.NewReader(b))) _, err = bufio.Copy(c.writer, io.MultiReader(bytes.NewReader(grpcHeader), bytes.NewReader(protobufHeader[:varuintLen+1]), bytes.NewReader(b)))
c.writeAccess.Unlock()
buf.Put(grpcHeader) buf.Put(grpcHeader)
if c.flusher != nil { if c.flusher != nil {
c.flusher.Flush() c.flusher.Flush()

View File

@@ -14,8 +14,7 @@ import (
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
"github.com/sagernet/websocket"
"github.com/gorilla/websocket"
) )
var _ adapter.V2RayClientTransport = (*Client)(nil) var _ adapter.V2RayClientTransport = (*Client)(nil)

View File

@@ -11,8 +11,7 @@ import (
C "github.com/sagernet/sing-box/constant" C "github.com/sagernet/sing-box/constant"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/websocket"
"github.com/gorilla/websocket"
) )
type WebsocketConn struct { type WebsocketConn struct {
@@ -167,6 +166,9 @@ func (c *EarlyWebsocketConn) SetWriteDeadline(t time.Time) error {
func wrapError(err error) error { func wrapError(err error) error {
if websocket.IsCloseError(err, websocket.CloseNormalClosure) { if websocket.IsCloseError(err, websocket.CloseNormalClosure) {
return io.EOF
}
if websocket.IsCloseError(err, websocket.CloseAbnormalClosure) {
return net.ErrClosed return net.ErrClosed
} }
return err return err

View File

@@ -19,8 +19,7 @@ import (
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
sHttp "github.com/sagernet/sing/protocol/http" sHttp "github.com/sagernet/sing/protocol/http"
"github.com/sagernet/websocket"
"github.com/gorilla/websocket"
) )
var _ adapter.V2RayServerTransport = (*Server)(nil) var _ adapter.V2RayServerTransport = (*Server)(nil)