Compare commits

...

32 Commits

Author SHA1 Message Date
世界
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
72 changed files with 585 additions and 291 deletions

View File

@@ -1,10 +1,5 @@
#!/usr/bin/env bash
PROJECTS=$(dirname "$0")/../..
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 get -x github.com/sagernet/$1@$(git -C $PROJECTS/$1 rev-parse HEAD)
go mod tidy

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,13 @@
package baderror
import "strings"
import (
"context"
"io"
"net"
"strings"
E "github.com/sagernet/sing/common/exceptions"
)
func Contains(err error, msgList ...string) bool {
for _, msg := range msgList {
@@ -10,3 +17,46 @@ func Contains(err error, msgList ...string) bool {
}
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 {
var dialer N.Dialer
if options.Detour == "" {
return NewDefault(router, options)
dialer = NewDefault(router, options)
} 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)
if domainStrategy != dns.DomainStrategyAsIS || options.Detour == "" {
dialer = NewResolveDialer(router, dialer, domainStrategy, time.Duration(options.FallbackDelay))

View File

@@ -1,6 +1,6 @@
package constant
var (
Version = "1.0-beta3"
Version = "1.0.2"
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
* Add [chained inbound](/configuration/shared/listen#detour) support
* Add process_path rule item
* 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 socksaddr type condition
* Fix smux session status

View File

@@ -16,12 +16,14 @@
### Fields
| Field | Available Context |
|-----------------------------------------------------------------------------------|-------------------|
| `bind_interface` /`bind_address` /`routing_mark` /`reuse_addr` /`connect_timeout` | `detour` not set |
#### detour
The tag of the upstream outbound.
Other dial fields will be ignored when enabled.
#### bind_interface
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.
`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
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 IPv6 is misconfigured and falling back to IPv4 if `prefer_ipv4` is set.
That is, is the amount of time to wait for connection to succeed before assuming
that IPv4/IPv6 is misconfigured and falling back to other type of addresses.
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==",
"detour": "shadowtls-out",
"multiplex": {
"enabled": 1,
"enabled": true,
"max_connections": 4,
"min_streams": 4
}
@@ -52,4 +52,4 @@
}
]
}
```
```

View File

@@ -11,7 +11,7 @@ the public internet.
##### 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

View File

@@ -10,7 +10,7 @@
##### 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()
if logLevel == log.LevelTrace {
logLevel = log.LevelDebug
} else if logLevel > log.LevelError {
} else if logLevel < log.LevelError {
logLevel = log.LevelError
}
render.JSON(w, r, &configSchema{

View File

@@ -75,11 +75,13 @@ func proxyInfo(server *Server, detour adapter.Outbound) *badjson.JSONObject {
clashType = "Shadowsocks"
case C.TypeVMess:
clashType = "Vmess"
case C.TypeTrojan:
clashType = "Trojan"
case C.TypeSelector:
clashType = "Selector"
isGroup = true
default:
clashType = "Unknown"
clashType = "Socks"
}
info.Put("type", clashType)
info.Put("name", detour.Tag())

6
go.mod
View File

@@ -20,11 +20,11 @@ require (
github.com/pires/go-proxyproto v0.6.2
github.com/sagernet/certmagic v0.0.0-20220819042630-4a57f8b6853a
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-20220910144724-62c4ebdbcb3f
github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6
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-20220907073918-72d7fdf6825f
github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195
github.com/spf13/cobra v1.5.0
github.com/stretchr/testify v1.8.0
@@ -39,6 +39,8 @@ require (
gvisor.dev/gvisor v0.0.0-20220819163037-ba6e795b139a
)
//replace github.com/sagernet/sing => ../sing
require (
github.com/ajg/form v1.5.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect

8
go.sum
View File

@@ -135,16 +135,16 @@ 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/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-20220829115648-e09c9f3fc812 h1:z5AqoJcMy+MaD9pstLt08c95t2Txlzvp5pk4lqXBQPc=
github.com/sagernet/sing v0.0.0-20220829115648-e09c9f3fc812/go.mod h1:kZvzh1VDa/Dg/Bt5WaYKU0jl5ept8KKDpl3Ay4gRtRQ=
github.com/sagernet/sing v0.0.0-20220910144724-62c4ebdbcb3f h1:w1TJq7Lw3It35tDyMsZLtYz4T2msf1UK9JxC85L5+sk=
github.com/sagernet/sing v0.0.0-20220910144724-62c4ebdbcb3f/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/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/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/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-20220829020559-33915075430c/go.mod h1:82O6gzbxLha/W/jxSVQbsqf2lVdRTjMIgyLug0lpJps=
github.com/sagernet/sing-vmess v0.0.0-20220907073918-72d7fdf6825f h1:6l9aXZqAl1JqXJWi89KHpWnM/moQUPGG+XiwMc+yD0A=
github.com/sagernet/sing-vmess v0.0.0-20220907073918-72d7fdf6825f/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/go.mod h1:yedWtra8nyGJ+SyI+ziwuaGMzBatbB10P1IOOZbbSK8=
github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU=

View File

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

View File

@@ -20,7 +20,6 @@ import (
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/auth"
"github.com/sagernet/sing/common/buf"
"github.com/sagernet/sing/common/bufio"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
@@ -361,7 +360,7 @@ func (c *naiveH1Conn) WriteBuffer(buffer *buf.Buffer) error {
n, err = bufio.Copy(w, c.Conn)
}
return n, wrapHttpError(err)
}*/
}
func (c *naiveH1Conn) ReadFrom(r io.Reader) (n int64, err error) {
if c.writePadding < kFirstPaddings {
@@ -371,13 +370,14 @@ func (c *naiveH1Conn) ReadFrom(r io.Reader) (n int64, err error) {
}
return n, wrapHttpError(err)
}
*/
func (c *naiveH1Conn) Upstream() any {
return c.Conn
}
func (c *naiveH1Conn) ReaderReplaceable() bool {
return c.readRemaining == kFirstPaddings
return c.readPadding == kFirstPaddings
}
func (c *naiveH1Conn) WriterReplaceable() bool {
@@ -539,7 +539,7 @@ func (c *naiveH2Conn) WriteBuffer(buffer *buf.Buffer) error {
n, err = bufio.Copy(w, c.reader)
}
return n, wrapHttpError(err)
}*/
}
func (c *naiveH2Conn) ReadFrom(r io.Reader) (n int64, err error) {
if c.writePadding < kFirstPaddings {
@@ -548,7 +548,7 @@ func (c *naiveH2Conn) ReadFrom(r io.Reader) (n int64, err error) {
n, err = bufio.Copy(c.writer, r)
}
return n, wrapHttpError(err)
}
}*/
func (c *naiveH2Conn) Close() error {
return common.Close(
@@ -586,7 +586,7 @@ func (c *naiveH2Conn) UpstreamWriter() any {
}
func (c *naiveH2Conn) ReaderReplaceable() bool {
return c.readRemaining == kFirstPaddings
return c.readPadding == kFirstPaddings
}
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:
message = levelString + " " + message
case f.FullTimestamp:
message = F.ToString(int(timestamp.Sub(f.BaseTime)/time.Second)) + " " + levelString + " " + message
message = timestamp.Format(f.TimestampFormat) + " " + levelString + " " + message
default:
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:
message = levelString + " " + message
case f.FullTimestamp:
message = F.ToString(int(timestamp.Sub(f.BaseTime)/time.Second)) + " " + levelString + " " + message
message = timestamp.Format(f.TimestampFormat) + " " + levelString + " " + message
default:
message = levelString + "[" + xd(int(timestamp.Sub(f.BaseTime)/time.Second), 4) + "] " + message
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -4,12 +4,14 @@ import (
"context"
"net"
"net/netip"
"time"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/dialer"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-dns"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
@@ -17,11 +19,16 @@ import (
"github.com/pires/go-proxyproto"
)
var _ adapter.Outbound = (*Direct)(nil)
var (
_ adapter.Outbound = (*Direct)(nil)
_ N.ParallelDialer = (*Direct)(nil)
)
type Direct struct {
myOutboundAdapter
dialer N.Dialer
domainStrategy dns.DomainStrategy
fallbackDelay time.Duration
overrideOption int
overrideDestination M.Socksaddr
proxyProto uint8
@@ -36,8 +43,10 @@ func NewDirect(router adapter.Router, logger log.ContextLogger, tag string, opti
logger: logger,
tag: tag,
},
dialer: dialer.NewOutbound(router, options.OutboundDialerOptions),
proxyProto: options.ProxyProtocol,
domainStrategy: dns.DomainStrategy(options.DomainStrategy),
fallbackDelay: time.Duration(options.FallbackDelay),
dialer: dialer.New(router, options.DialerOptions),
proxyProto: options.ProxyProtocol,
}
if options.ProxyProtocol > 2 {
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
}
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) {
ctx, metadata := adapter.AppendContext(ctx)
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 {
return err
}
if len(message.Questions) > 0 {
question := message.Questions[0]
metadata.Domain = string(question.Name.Data[:question.Name.Length-1])
}
metadataInQuery := metadata
go func() error {
response, err := d.router.Exchange(ctx, &message)
response, err := d.router.Exchange(adapter.WithContext(ctx, &metadataInQuery), &message)
if err != nil {
return err
}
@@ -125,13 +122,10 @@ func (d *DNS) NewPacketConnection(ctx context.Context, conn N.PacketConn, metada
if err != nil {
return err
}
if len(message.Questions) > 0 {
question := message.Questions[0]
metadata.Domain = string(question.Name.Data[:question.Name.Length-1])
}
timeout.Update()
metadataInQuery := metadata
go func() error {
response, err := d.router.Exchange(ctx, &message)
response, err := d.router.Exchange(adapter.WithContext(ctx, &metadataInQuery), &message)
if err != nil {
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) {
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 {
return nil, err
}

View File

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

View File

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

View File

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

View File

@@ -25,7 +25,7 @@ type Socks struct {
}
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 err error
if options.Version != "" {

View File

@@ -47,7 +47,7 @@ func NewSSH(ctx context.Context, router adapter.Router, logger log.ContextLogger
tag: tag,
},
ctx: ctx,
dialer: dialer.NewOutbound(router, options.OutboundDialerOptions),
dialer: dialer.New(router, options.DialerOptions),
serverAddr: options.ServerOptions.Build(),
user: options.User,
hostKeyAlgorithms: options.HostKeyAlgorithms,

View File

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

View File

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

View File

@@ -64,7 +64,7 @@ func NewWireGuard(ctx context.Context, router adapter.Router, logger log.Context
},
ctx: ctx,
serverAddr: options.ServerOptions.Build(),
dialer: dialer.NewOutbound(router, options.OutboundDialerOptions),
dialer: dialer.New(router, options.DialerOptions),
}
var endpointIp netip.Addr
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) {
udpConn, err := c.connect()
if err != nil {
err = &wireError{err}
return
}
n, err = udpConn.Read(b)
@@ -332,10 +333,6 @@ func (w *wireError) Temporary() bool {
return true
}
func (w *wireError) Unwrap() error {
return w.cause
}
type wireConn struct {
net.Conn
access sync.Mutex

View File

@@ -10,7 +10,7 @@ DIR=$(dirname "$0")
PROJECT=$DIR/../..
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
sudo cp $(go env GOPATH)/bin/sing-box /usr/local/bin/

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env bash
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 tar -C /usr/local -xzf go.tar.gz
rm go.tar.gz

View File

@@ -10,7 +10,7 @@ DIR=$(dirname "$0")
PROJECT=$DIR/../..
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
sudo systemctl stop sing-box

View File

@@ -52,6 +52,7 @@ func (r *Router) Exchange(ctx context.Context, message *dnsmessage.Message) (*dn
case dnsmessage.TypeAAAA:
metadata.IPVersion = 6
}
metadata.Domain = string(message.Questions[0].Name.Data[:message.Questions[0].Name.Length-1])
}
ctx, transport, strategy := r.matchDNS(ctx)
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) {
r.dnsLogger.DebugContext(ctx, "lookup domain ", domain)
ctx, metadata := adapter.AppendContext(ctx)
metadata.Domain = domain
ctx, transport, transportStrategy := r.matchDNS(ctx)
if strategy == dns.DomainStrategyAsIS {
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), " "))
} else {
r.dnsLogger.ErrorContext(ctx, E.Cause(err, "lookup failed for ", domain))
if err == nil {
err = dns.RCodeNameError
}
}
return addrs, err
}

View File

@@ -59,13 +59,13 @@ func NewIPCIDRItem(isSource bool, prefixStrings []string) (*IPCIDRItem, error) {
func (r *IPCIDRItem) Match(metadata *adapter.InboundContext) bool {
if r.isSource {
return r.ipSet.Contains(metadata.Source.Addr)
return r.match(metadata.Source.Addr)
} else {
if metadata.Destination.IsIP() {
return r.ipSet.Contains(metadata.Destination.Addr)
return r.match(metadata.Destination.Addr)
} else {
for _, address := range metadata.DestinationAddresses {
if r.ipSet.Contains(address) {
if r.match(address) {
return true
}
}
@@ -74,6 +74,14 @@ func (r *IPCIDRItem) Match(metadata *adapter.InboundContext) bool {
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 {
return r.description
}

View File

@@ -22,6 +22,10 @@ func startInstance(t *testing.T, options option.Options) {
options.Log = &option.LogOptions{
Level: "trace",
}
} else {
options.Log = &option.LogOptions{
Level: "warning",
}
}
var instance *box.Box
var err error

View File

@@ -183,6 +183,7 @@ func testPingPongWithConn(t *testing.T, port uint16, cc func() (net.Conn, error)
if err != nil {
return err
}
defer c.Close()
pingCh, pongCh, test := newPingPongPair()
go func() {
@@ -245,6 +246,7 @@ func testPingPongWithPacketConn(t *testing.T, port uint16, pcc func() (net.Packe
if err != nil {
return err
}
defer pc.Close()
go func() {
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 {
return err
}
defer c.Close()
go func() {
c, err := l.Accept()
@@ -432,6 +435,7 @@ func testLargeDataWithPacketConn(t *testing.T, port uint16, pcc func() (net.Pack
if err != nil {
return err
}
defer pc.Close()
go func() {
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) {
startInstance(t, option.Options{
Log: &option.LogOptions{
Level: "error",
},
Inbounds: []option.Inbound{
{
Type: C.TypeMixed,

View File

@@ -10,13 +10,15 @@ require (
github.com/docker/docker v20.10.17+incompatible
github.com/docker/go-connections v0.4.0
github.com/gofrs/uuid v4.2.0+incompatible
github.com/sagernet/sing v0.0.0-20220829115648-e09c9f3fc812
github.com/sagernet/sing v0.0.0-20220903085538-02b9ca1cc133
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6
github.com/spyzhov/ajson v0.7.1
github.com/stretchr/testify v1.8.0
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b
)
//replace github.com/sagernet/sing => ../../sing
require (
berty.tech/go-libtor v1.0.385 // indirect
github.com/Microsoft/go-winio v0.5.1 // indirect
@@ -59,25 +61,25 @@ require (
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/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-vmess v0.0.0-20220829020559-33915075430c // indirect
github.com/sagernet/smux v0.0.0-20220812084127-e2d085ee3939 // indirect
github.com/sagernet/sing-vmess v0.0.0-20220907073918-72d7fdf6825f // indirect
github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195 // indirect
github.com/sirupsen/logrus v1.8.1 // indirect
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
go.uber.org/atomic v1.10.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
go.uber.org/zap v1.22.0 // 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/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/time v0.0.0-20191024005414-555d28b269f0 // 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/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/grpc v1.49.0 // indirect
google.golang.org/protobuf v1.28.1 // indirect

View File

@@ -155,18 +155,18 @@ 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/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-20220829115648-e09c9f3fc812 h1:z5AqoJcMy+MaD9pstLt08c95t2Txlzvp5pk4lqXBQPc=
github.com/sagernet/sing v0.0.0-20220829115648-e09c9f3fc812/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/go.mod h1:eDyH7AJmqBGjZQdQmpZIzlbTREudZuWDExMuGKgjRVM=
github.com/sagernet/sing v0.0.0-20220903085538-02b9ca1cc133 h1:krnb8wKEFIdXhmJYlhJMbEcPsJFISy2fz90uHVz7hMU=
github.com/sagernet/sing v0.0.0-20220903085538-02b9ca1cc133/go.mod h1:kZvzh1VDa/Dg/Bt5WaYKU0jl5ept8KKDpl3Ay4gRtRQ=
github.com/sagernet/sing-dns v0.0.0-20220903082137-b1102b8fc961 h1:5JeqhvCGV6AQQiAO0V67Loh2eyO3JNjIQnvRF8NnTE0=
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/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/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-20220829020559-33915075430c/go.mod h1:82O6gzbxLha/W/jxSVQbsqf2lVdRTjMIgyLug0lpJps=
github.com/sagernet/smux v0.0.0-20220812084127-e2d085ee3939 h1:pB1Dh1NbwVrLhQhotr4O4Hs3yhiBzmg3AvnUyYjL4x4=
github.com/sagernet/smux v0.0.0-20220812084127-e2d085ee3939/go.mod h1:yedWtra8nyGJ+SyI+ziwuaGMzBatbB10P1IOOZbbSK8=
github.com/sagernet/sing-vmess v0.0.0-20220907073918-72d7fdf6825f h1:6l9aXZqAl1JqXJWi89KHpWnM/moQUPGG+XiwMc+yD0A=
github.com/sagernet/sing-vmess v0.0.0-20220907073918-72d7fdf6825f/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/go.mod h1:yedWtra8nyGJ+SyI+ziwuaGMzBatbB10P1IOOZbbSK8=
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/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-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-20220826181053-bd7e27e6170d h1:3qF+Z8Hkrw9sOhrFHti9TlB1Hkac1x+DNRkv0XQiFjo=
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 h1:Y/gsMcFOcR+6S6f3YeMKl5g+dZMEWqcz5Czj/GWYbkM=
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-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA=
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-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-20220825204002-c680a09ffe64 h1:UiNENfZ8gDvpiWw7IpOMQ27spWmThO1RwwdQVbJahJM=
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 h1:v6hYoSR9T5oet+pMXwUWkbiVqx/63mlHjefrHmxwfeY=
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-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
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.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/wireguard v0.0.0-20220703234212-c31a7b1ab478 h1:vDy//hdR+GnROE3OdYbQKt9rdtNdHkDtONvpRwmls/0=
golang.zx2c4.com/wireguard v0.0.0-20220703234212-c31a7b1ab478/go.mod h1:bVQfyl2sCM/QIIGHpWbFGfHPuDvqnCNkT6MQLTCjO/U=
golang.zx2c4.com/wireguard v0.0.0-20220829161405-d1d08426b27b h1:qgrKnOfe1zyURRNdmDlGbN32i38Zjmw0B1+TMdHcOvg=
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.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
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")
startInstance(t, option.Options{
Log: &option.LogOptions{
Level: "error",
},
Inbounds: []option.Inbound{
{
Type: C.TypeMixed,
@@ -92,9 +89,6 @@ func TestHysteriaInbound(t *testing.T) {
}
caPem, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
startInstance(t, option.Options{
Log: &option.LogOptions{
Level: "error",
},
Inbounds: []option.Inbound{
{
Type: C.TypeHysteria,
@@ -145,9 +139,6 @@ func TestHysteriaOutbound(t *testing.T) {
},
})
startInstance(t, option.Options{
Log: &option.LogOptions{
Level: "error",
},
Inbounds: []option.Inbound{
{
Type: C.TypeMixed,

View File

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

View File

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

View File

@@ -13,9 +13,6 @@ import (
func TestNaiveInboundWithNginx(t *testing.T) {
caPem, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
startInstance(t, option.Options{
Log: &option.LogOptions{
Level: "error",
},
Inbounds: []option.Inbound{
{
Type: C.TypeNaive,
@@ -62,9 +59,6 @@ func TestNaiveInboundWithNginx(t *testing.T) {
func TestNaiveInbound(t *testing.T) {
caPem, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
startInstance(t, option.Options{
Log: &option.LogOptions{
Level: "error",
},
Inbounds: []option.Inbound{
{
Type: C.TypeNaive,
@@ -110,9 +104,6 @@ func TestNaiveHTTP3Inbound(t *testing.T) {
}
caPem, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
startInstance(t, option.Options{
Log: &option.LogOptions{
Level: "error",
},
Inbounds: []option.Inbound{
{
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"},
})
startInstance(t, option.Options{
Log: &option.LogOptions{
Level: "error",
},
Inbounds: []option.Inbound{
{
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"},
})
startInstance(t, option.Options{
Log: &option.LogOptions{
Level: "error",
},
Inbounds: []option.Inbound{
{
Type: C.TypeMixed,
@@ -138,9 +132,6 @@ func testShadowsocksOutboundWithShadowsocksRust(t *testing.T, method string, pas
func testShadowsocksSelf(t *testing.T, method string, password string) {
startInstance(t, option.Options{
Log: &option.LogOptions{
Level: "error",
},
Inbounds: []option.Inbound{
{
Type: C.TypeMixed,
@@ -199,9 +190,6 @@ func TestShadowsocksUoT(t *testing.T) {
method := shadowaead_2022.List[0]
password := mkBase64(t, 16)
startInstance(t, option.Options{
Log: &option.LogOptions{
Level: "error",
},
Inbounds: []option.Inbound{
{
Type: C.TypeMixed,

View File

@@ -60,10 +60,11 @@ func TestShadowTLS(t *testing.T) {
ShadowsocksOptions: option.ShadowsocksOutboundOptions{
Method: method,
Password: password,
OutboundDialerOptions: option.OutboundDialerOptions{
DialerOptions: option.DialerOptions{
Detour: "detour",
},
DialerOptions: option.DialerOptions{
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) {
@@ -131,10 +132,8 @@ func TestShadowTLSOutbound(t *testing.T) {
{
Type: C.TypeSocks,
SocksOptions: option.SocksOutboundOptions{
OutboundDialerOptions: option.OutboundDialerOptions{
DialerOptions: option.DialerOptions{
Detour: "detour",
},
DialerOptions: option.DialerOptions{
Detour: "detour",
},
},
},

View File

@@ -20,9 +20,6 @@ func TestTrojanOutbound(t *testing.T) {
},
})
startInstance(t, option.Options{
Log: &option.LogOptions{
Level: "error",
},
Inbounds: []option.Inbound{
{
Type: C.TypeMixed,
@@ -58,9 +55,6 @@ func TestTrojanOutbound(t *testing.T) {
func TestTrojanSelf(t *testing.T) {
_, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
startInstance(t, option.Options{
Log: &option.LogOptions{
Level: "error",
},
Inbounds: []option.Inbound{
{
Type: C.TypeMixed,

View File

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

View File

@@ -11,31 +11,6 @@ import (
"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) {
testV2RayTransportSelf(t, &option.V2RayTransportOptions{
Type: C.V2RayTransportTypeHTTP,
@@ -69,9 +44,6 @@ func testVMessTransportSelf(t *testing.T, server *option.V2RayTransportOptions,
require.NoError(t, err)
_, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
startInstance(t, option.Options{
Log: &option.LogOptions{
Level: "error",
},
Inbounds: []option.Inbound{
{
Type: C.TypeMixed,
@@ -148,9 +120,6 @@ func testTrojanTransportSelf(t *testing.T, server *option.V2RayTransportOptions,
require.NoError(t, err)
_, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
startInstance(t, option.Options{
Log: &option.LogOptions{
Level: "error",
},
Inbounds: []option.Inbound{
{
Type: C.TypeMixed,
@@ -229,9 +198,6 @@ func TestVMessQUICSelf(t *testing.T) {
require.NoError(t, err)
_, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
startInstance(t, option.Options{
Log: &option.LogOptions{
Level: "error",
},
Inbounds: []option.Inbound{
{
Type: C.TypeMixed,
@@ -307,9 +273,6 @@ func testV2RayTransportNOTLSSelf(t *testing.T, transport *option.V2RayTransportO
user, err := uuid.DefaultGenerator.NewV4()
require.NoError(t, err)
startInstance(t, option.Options{
Log: &option.LogOptions{
Level: "error",
},
Inbounds: []option.Inbound{
{
Type: C.TypeMixed,

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,
},
},
},
},
},
})
testSuitSimple(t, clientPort, testPort)
}

View File

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

View File

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

View File

@@ -6,6 +6,7 @@ import (
"syscall"
"github.com/sagernet/quic-go"
"github.com/sagernet/sing-box/common/baderror"
"github.com/sagernet/sing/common"
)
@@ -38,6 +39,16 @@ type StreamWrapper struct {
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 {
return s.Conn.LocalAddr()
}
@@ -50,14 +61,6 @@ func (s *StreamWrapper) Upstream() any {
return s.Stream
}
func (s *StreamWrapper) ReaderReplaceable() bool {
return true
}
func (s *StreamWrapper) WriterReplaceable() bool {
return true
}
func (s *StreamWrapper) Close() error {
s.CancelRead(0)
s.Stream.Close()

View File

@@ -167,6 +167,9 @@ func (c *EarlyWebsocketConn) SetWriteDeadline(t time.Time) error {
func wrapError(err error) error {
if websocket.IsCloseError(err, websocket.CloseNormalClosure) {
return io.EOF
}
if websocket.IsCloseError(err, websocket.CloseAbnormalClosure) {
return net.ErrClosed
}
return err