Compare commits

..

21 Commits

Author SHA1 Message Date
世界
8ce244dd04 Fix documentation
Signed-off-by: 世界 <i@sekai.icu>
Signed-off-by: unknowndevQwQ <unknowndevQwQ@pm.me>
2022-09-26 12:25:18 +08:00
世界
0f57b93925 Update documentation 2022-09-25 22:29:18 +08:00
世界
c90a77a185 Refine 4in6 processing 2022-09-25 22:29:18 +08:00
世界
c6586f19fa Fix read source address from grpc-go 2022-09-25 22:29:18 +08:00
世界
cbab86ae38 Refine tproxy write back 2022-09-25 22:29:18 +08:00
世界
17b5f031f1 Fix shadowsocks plugins 2022-09-25 16:43:12 +08:00
世界
b00b6b9e25 Fix fqdn socks5 outbound connection 2022-09-25 14:42:39 +08:00
世界
fb6b3b0401 Fix missing source address from transport connection 2022-09-23 18:55:28 +08:00
世界
22ea878fe9 Improve websocket writer 2022-09-23 18:55:07 +08:00
世界
abe3dc6039 Add self sign cert support 2022-09-23 17:13:18 +08:00
世界
852829b9dc Add VMess benchmark result 2022-09-23 16:13:29 +08:00
世界
407509c985 Fix leaks and add test 2022-09-23 13:14:31 +08:00
世界
9856b73cb5 Update documentation 2022-09-23 10:30:07 +08:00
世界
f42356fbcb Fix system stack ipv4 overflow 2022-09-23 10:29:15 +08:00
世界
d0b467671a Merge VLESS to library 2022-09-23 10:28:51 +08:00
世界
c18c545798 Add stdio test 2022-09-23 10:28:24 +08:00
世界
693ef293ac Update buffer usage 2022-09-23 10:27:48 +08:00
世界
a006627795 Disable DF on direct outbound by default 2022-09-23 10:27:46 +08:00
世界
0738b184e4 Fix url test interval 2022-09-23 10:27:42 +08:00
世界
42524ba04e Fix dns sniffer 2022-09-17 16:59:28 +08:00
世界
63fc95b96d Add mux server and XUDP client for VMess 2022-09-17 11:54:04 +08:00
65 changed files with 914 additions and 768 deletions

View File

@@ -1,7 +1,8 @@
NAME = sing-box
COMMIT = $(shell git rev-parse --short HEAD)
TAGS ?= with_gvisor,with_quic,with_wireguard,with_clash_api
PARAMS = -v -trimpath -tags '$(TAGS)' -ldflags '-s -w -buildid='
TAGS_TEST ?= with_gvisor,with_quic,with_wireguard,with_grpc,with_ech,with_utls,with_shadowsocksr
PARAMS = -v -trimpath -tags "$(TAGS)" -ldflags "-s -w -buildid="
MAIN = ./cmd/sing-box
.PHONY: test release
@@ -59,13 +60,19 @@ release_install:
go install -v github.com/tcnksm/ghr@latest
test:
@go test -v . && \
@go test -v ./... && \
cd test && \
go mod tidy && \
go test -v -tags with_gvisor,with_quic,with_wireguard,with_grpc,with_ech,with_utls,with_shadowsocksr .
go test -v -tags "$(TAGS_TEST)" .
test_stdio:
@go test -v ./... && \
cd test && \
go mod tidy && \
go test -v -tags "$(TAGS_TEST),force_stdio" .
clean:
rm -rf bin dist
rm -rf bin dist sing-box
rm -f $(shell go env GOPATH)/sing-box
update:

View File

@@ -38,13 +38,25 @@ type myUpstreamHandlerWrapper struct {
}
func (w *myUpstreamHandlerWrapper) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
w.metadata.Destination = metadata.Destination
return w.connectionHandler(ctx, conn, w.metadata)
myMetadata := 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 {
w.metadata.Destination = metadata.Destination
return w.packetHandler(ctx, conn, w.metadata)
myMetadata := 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) {
@@ -78,13 +90,23 @@ func NewUpstreamContextHandler(
func (w *myUpstreamContextHandlerWrapper) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
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)
}
func (w *myUpstreamContextHandlerWrapper) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error {
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)
}

View File

@@ -110,7 +110,13 @@ func NewDefault(router adapter.Router, options option.DialerOptions) *DefaultDia
if options.TCPFastOpen {
warnTFOOnUnsupportedPlatform.Check()
}
if !options.UDPFragment {
var udpFragment bool
if options.UDPFragment != nil {
udpFragment = *options.UDPFragment
} else {
udpFragment = options.UDPFragmentDefault
}
if !udpFragment {
dialer.Control = control.Append(dialer.Control, control.DisableUDPFragment())
listener.Control = control.Append(listener.Control, control.DisableUDPFragment())
}
@@ -140,7 +146,7 @@ func (d *DefaultDialer) DialContext(ctx context.Context, network string, address
case N.NetworkUDP:
return d.udpDialer.DialContext(ctx, network, address.String())
}
return d.dialer.DialContext(ctx, network, address.Unwrap().String())
return d.dialer.DialContext(ctx, network, address.String())
}
func (d *DefaultDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {

View File

@@ -466,10 +466,7 @@ func (c *ClientPacketAddrConn) ReadPacket(buffer *buf.Buffer) (destination M.Soc
if err != nil {
return
}
if buffer.FreeLen() < int(length) {
return destination, io.ErrShortBuffer
}
_, err = io.ReadFull(c.ExtendedConn, buffer.Extend(int(length)))
_, err = buffer.ReadFullFrom(c.ExtendedConn, int(length))
return
}

View File

@@ -3,7 +3,6 @@ package mux
import (
"context"
"encoding/binary"
"io"
"net"
"github.com/sagernet/sing-box/adapter"
@@ -15,6 +14,7 @@ import (
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/common/rw"
"github.com/sagernet/sing/common/task"
)
func NewConnection(ctx context.Context, router adapter.Router, errorHandler E.Handler, logger log.ContextLogger, conn net.Conn, metadata adapter.InboundContext) error {
@@ -26,14 +26,21 @@ func NewConnection(ctx context.Context, router adapter.Router, errorHandler E.Ha
if err != nil {
return err
}
var stream net.Conn
for {
stream, err = session.Accept()
if err != nil {
return err
var group task.Group
group.Append0(func(ctx context.Context) error {
var stream net.Conn
for {
stream, err = session.Accept()
if err != nil {
return err
}
go newConnection(ctx, router, errorHandler, logger, stream, metadata)
}
go newConnection(ctx, router, errorHandler, logger, stream, metadata)
}
})
group.Cleanup(func() {
session.Close()
})
return group.Run(ctx)
}
func newConnection(ctx context.Context, router adapter.Router, errorHandler E.Handler, logger log.ContextLogger, stream net.Conn, metadata adapter.InboundContext) {
@@ -158,9 +165,6 @@ func (c *ServerPacketConn) ReadPacket(buffer *buf.Buffer) (destination M.Socksad
if err != nil {
return
}
if buffer.FreeLen() < int(length) {
return destination, io.ErrShortBuffer
}
_, err = buffer.ReadFullFrom(c.ExtendedConn, int(length))
if err != nil {
return
@@ -223,9 +227,6 @@ func (c *ServerPacketAddrConn) ReadPacket(buffer *buf.Buffer) (destination M.Soc
if err != nil {
return
}
if buffer.FreeLen() < int(length) {
return destination, io.ErrShortBuffer
}
_, err = buffer.ReadFullFrom(c.ExtendedConn, int(length))
if err != nil {
return

View File

@@ -28,11 +28,5 @@ type Info struct {
}
func FindProcessInfo(searcher Searcher, ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*Info, error) {
info, err := findProcessInfo(searcher, ctx, network, source, destination)
if err != nil {
if source.Addr().Is4In6() {
info, err = findProcessInfo(searcher, ctx, network, netip.AddrPortFrom(netip.AddrFrom4(source.Addr().As4()), source.Port()), destination)
}
}
return info, err
return findProcessInfo(searcher, ctx, network, source, destination)
}

View File

@@ -36,8 +36,8 @@ func (l *Listener) Accept() (net.Conn, error) {
}
if header != nil {
return &bufio.AddrConn{Conn: conn, Metadata: M.Metadata{
Source: M.SocksaddrFromNet(header.SourceAddr),
Destination: M.SocksaddrFromNet(header.DestinationAddr),
Source: M.SocksaddrFromNet(header.SourceAddr).Unwrap(),
Destination: M.SocksaddrFromNet(header.DestinationAddr).Unwrap(),
}}, nil
}
return conn, nil

View File

@@ -2,14 +2,11 @@ package redir
import (
"encoding/binary"
"net"
"net/netip"
"os"
"strconv"
"syscall"
"github.com/sagernet/sing/common/control"
E "github.com/sagernet/sing/common/exceptions"
F "github.com/sagernet/sing/common/format"
M "github.com/sagernet/sing/common/metadata"
"golang.org/x/sys/unix"
@@ -32,6 +29,18 @@ func TProxy(fd uintptr, isIPv6 bool) error {
return err
}
func TProxyWriteBack() control.Func {
return func(network, address string, conn syscall.RawConn) error {
return control.Raw(conn, func(fd uintptr) error {
if M.ParseSocksaddr(address).Addr.Is6() {
return syscall.SetsockoptInt(int(fd), syscall.SOL_IPV6, unix.IPV6_TRANSPARENT, 1)
} else {
return syscall.SetsockoptInt(int(fd), syscall.SOL_IP, syscall.IP_TRANSPARENT, 1)
}
})
}
}
func GetOriginalDestinationFromOOB(oob []byte) (netip.AddrPort, error) {
controlMessages, err := unix.ParseSocketControlMessage(oob)
if err != nil {
@@ -46,79 +55,3 @@ func GetOriginalDestinationFromOOB(oob []byte) (netip.AddrPort, error) {
}
return netip.AddrPort{}, E.New("not found")
}
func DialUDP(lAddr *net.UDPAddr, rAddr *net.UDPAddr) (*net.UDPConn, error) {
rSockAddr, err := udpAddrToSockAddr(rAddr)
if err != nil {
return nil, err
}
lSockAddr, err := udpAddrToSockAddr(lAddr)
if err != nil {
return nil, err
}
fd, err := syscall.Socket(udpAddrFamily(lAddr, rAddr), syscall.SOCK_DGRAM, 0)
if err != nil {
return nil, err
}
if err = syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1); err != nil {
syscall.Close(fd)
return nil, err
}
if err = syscall.SetsockoptInt(fd, syscall.SOL_IP, syscall.IP_TRANSPARENT, 1); err != nil {
syscall.Close(fd)
return nil, err
}
if err = syscall.Bind(fd, lSockAddr); err != nil {
syscall.Close(fd)
return nil, err
}
if err = syscall.Connect(fd, rSockAddr); err != nil {
syscall.Close(fd)
return nil, err
}
fdFile := os.NewFile(uintptr(fd), F.ToString("net-udp-dial-", rAddr))
defer fdFile.Close()
c, err := net.FileConn(fdFile)
if err != nil {
syscall.Close(fd)
return nil, err
}
return c.(*net.UDPConn), nil
}
func udpAddrToSockAddr(addr *net.UDPAddr) (syscall.Sockaddr, error) {
switch {
case addr.IP.To4() != nil:
ip := [4]byte{}
copy(ip[:], addr.IP.To4())
return &syscall.SockaddrInet4{Addr: ip, Port: addr.Port}, nil
default:
ip := [16]byte{}
copy(ip[:], addr.IP.To16())
zoneID, err := strconv.ParseUint(addr.Zone, 10, 32)
if err != nil {
zoneID = 0
}
return &syscall.SockaddrInet6{Addr: ip, Port: addr.Port, ZoneId: uint32(zoneID)}, nil
}
}
func udpAddrFamily(lAddr, rAddr *net.UDPAddr) int {
if (lAddr == nil || lAddr.IP.To4() != nil) && (rAddr == nil || lAddr.IP.To4() != nil) {
return syscall.AF_INET
}
return syscall.AF_INET6
}

View File

@@ -3,19 +3,20 @@
package redir
import (
"net"
"net/netip"
"os"
"github.com/sagernet/sing/common/control"
)
func TProxy(fd uintptr, isIPv6 bool) error {
return os.ErrInvalid
}
func TProxyWriteBack() control.Func {
return nil
}
func GetOriginalDestinationFromOOB(oob []byte) (netip.AddrPort, error) {
return netip.AddrPort{}, os.ErrInvalid
}
func DialUDP(lAddr *net.UDPAddr, rAddr *net.UDPAddr) (*net.UDPConn, error) {
return nil, os.ErrInvalid
}

View File

@@ -11,6 +11,7 @@ import (
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/buf"
M "github.com/sagernet/sing/common/metadata"
"github.com/sagernet/sing/common/task"
mDNS "github.com/miekg/dns"
@@ -49,5 +50,8 @@ func DomainNameQuery(ctx context.Context, packet []byte) (*adapter.InboundContex
if err != nil {
return nil, err
}
if len(msg.Question) == 0 || msg.Question[0].Qclass != mDNS.ClassINET || !M.IsDomainName(msg.Question[0].Name) {
return nil, os.ErrInvalid
}
return &adapter.InboundContext{Protocol: C.ProtocolDNS}, nil
}

View File

@@ -1,6 +0,0 @@
package sniff
import _ "unsafe" // for linkname
//go:linkname IsDomainName net.isDomainName
func IsDomainName(domain string) bool

View File

@@ -165,8 +165,6 @@ func newECHClient(router adapter.Router, serverAddress string, options option.Ou
return &echClientConfig{&tlsConfig}, nil
}
const typeHTTPS = 65
func fetchECHClientConfig(router adapter.Router) func(ctx context.Context, serverName string) ([]cftls.ECHConfig, error) {
return func(ctx context.Context, serverName string) ([]cftls.ECHConfig, error) {
message := &mDNS.Msg{

50
common/tls/mkcert.go Normal file
View File

@@ -0,0 +1,50 @@
package tls
import (
"crypto/rand"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"math/big"
"time"
)
func GenerateKeyPair(serverName string) (*tls.Certificate, error) {
key, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return nil, err
}
serialNumber, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128))
if err != nil {
return nil, err
}
template := &x509.Certificate{
SerialNumber: serialNumber,
NotBefore: time.Now().Add(time.Hour * -1),
NotAfter: time.Now().Add(time.Hour),
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
Subject: pkix.Name{
CommonName: serverName,
},
DNSNames: []string{serverName},
}
publicDer, err := x509.CreateCertificate(rand.Reader, template, template, key.Public(), key)
if err != nil {
return nil, err
}
privateDer, err := x509.MarshalPKCS8PrivateKey(key)
if err != nil {
return nil, err
}
publicPem := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: publicDer})
privPem := pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: privateDer})
keyPair, err := tls.X509KeyPair(publicPem, privPem)
if err != nil {
return nil, err
}
return &keyPair, err
}

View File

@@ -34,6 +34,8 @@ func (c *STDServerConfig) SetNextProtos(nextProto []string) {
c.config.NextProtos = nextProto
}
var errInsecureUnused = E.New("tls: insecure unused")
func newSTDServer(ctx context.Context, logger log.Logger, options option.InboundTLSOptions) (ServerConfig, error) {
if !options.Enabled {
return nil, nil
@@ -46,6 +48,9 @@ func newSTDServer(ctx context.Context, logger log.Logger, options option.Inbound
if err != nil {
return nil, err
}
if options.Insecure {
return nil, errInsecureUnused
}
} else {
tlsConfig = &tls.Config{}
}
@@ -102,17 +107,23 @@ func newSTDServer(ctx context.Context, logger log.Logger, options option.Inbound
}
key = content
}
if certificate == nil {
return nil, E.New("missing certificate")
if certificate == nil && key == nil && options.Insecure {
tlsConfig.GetCertificate = func(info *tls.ClientHelloInfo) (*tls.Certificate, error) {
return GenerateKeyPair(info.ServerName)
}
} else {
if certificate == nil {
return nil, E.New("missing certificate")
} else if key == nil {
return nil, E.New("missing key")
}
keyPair, err := tls.X509KeyPair(certificate, key)
if err != nil {
return nil, E.Cause(err, "parse x509 key pair")
}
tlsConfig.Certificates = []tls.Certificate{keyPair}
}
if key == nil {
return nil, E.New("missing key")
}
keyPair, err := tls.X509KeyPair(certificate, key)
if err != nil {
return nil, E.Cause(err, "parse x509 key pair")
}
tlsConfig.Certificates = []tls.Certificate{keyPair}
}
return &STDServerConfig{
config: tlsConfig,

View File

@@ -1,3 +1,3 @@
package constant
var Version = "1.1-beta6"
var Version = "1.1-beta8"

View File

@@ -1,3 +1,27 @@
#### 1.1-beta8
* Fix leaks on close
* Improve websocket writer
* Refine tproxy write back
* Refine 4in6 processing
* Fix shadowsocks plugins
* Fix missing source address from transport connection
* Fix fqdn socks5 outbound connection
* Fix read source address from grpc-go
#### 1.0.5
* Fix missing source address from transport connection
* Fix fqdn socks5 outbound connection
* Fix read source address from grpc-go
#### 1.1-beta7
* Add v2ray mux and XUDP support for VMess inbound
* Add XUDP support for VMess outbound
* Disable DF on direct outbound by default
* Fix bugs in 1.1-beta6
#### 1.1-beta6
* Add [URLTest outbound](/configuration/outbound/urltest)

View File

@@ -14,7 +14,7 @@
"authenticated_length": true,
"network": "tcp",
"tls": {},
"packet_addr": false,
"packet_encoding": "",
"multiplex": {},
"transport": {},
@@ -84,9 +84,13 @@ Both is enabled by default.
TLS configuration, see [TLS](/configuration/shared/tls/#outbound).
#### packet_addr
#### packet_encoding
Enable packetaddr support.
| Encoding | Description |
|------------|-----------------------|
| (none) | Disabled |
| packetaddr | Supported by v2ray 5+ |
| xudp | Supported by xray |
#### multiplex

View File

@@ -14,7 +14,7 @@
"authenticated_length": true,
"network": "tcp",
"tls": {},
"packet_addr": false,
"packet_encoding": "",
"multiplex": {},
"transport": {},
@@ -84,9 +84,13 @@ VMess 用户 ID。
TLS 配置, 参阅 [TLS](/zh/configuration/shared/tls/#outbound)。
#### packet_addr
#### packet_encoding
启用 packetaddr 支持。
| 编码 | 描述 |
|------------|---------------|
| (空) | 禁用 |
| packetaddr | 由 v2ray 5+ 支持 |
| xudp | 由 xray 支持 |
#### multiplex

View File

@@ -95,7 +95,9 @@
| cn | 17.8M | 140.3M |
| cn (Loyalsoldier) | 74.3M | 246.7M |
#### Shadowsocks benchmark
#### Benchmark
##### Shadowsocks
| / | none | aes-128-gcm | 2022-blake3-aes-128-gcm |
|------------------------------------|:---------:|:-----------:|:-----------------------:|
@@ -103,6 +105,13 @@
| shadowsocks-rust (v1.15.0-alpha.5) | 10.7 Gbps | / | 9.36 Gbps |
| sing-box | 29.0 Gbps | / | 11.8 Gbps |
##### VMess
| / | TCP | HTTP | H2 TLS | WebSocket TLS | gRPC TLS |
|--------------------|:---------:|:---------:|:---------:|:-------------:|:---------:|
| v2ray-core (5.1.0) | 7.86 GBps | 2.86 Gbps | 1.83 Gbps | 2.36 Gbps | 2.43 Gbps |
| sing-box | 7.96 Gbps | 8.09 Gbps | 6.11 Gbps | 8.02 Gbps | 6.35 Gbps |
#### License
| / | License |

View File

@@ -45,10 +45,6 @@ sing-box version
It is also recommended to use systemd to manage sing-box service,
see [Linux server installation example](./examples/linux-server-installation).
## Contributors
[![](https://opencollective.com/sagernet/contributors.svg?width=740&button=false)](https://github.com/sagernet/sing-box/graphs/contributors)
## License
```

View File

@@ -25,13 +25,13 @@ go install -v -tags with_clash_api github.com/sagernet/sing-box/cmd/sing-box@lat
| 构建标志 | 描述 |
|------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `with_quic` | 启用 QUIC 支持,参阅 [QUIC 和 HTTP3 DNS 传输层](./configuration/dns/server)[Naive 入站](./configuration/inbound/naive)[Hysteria 入站](./configuration/inbound/hysteria)[Hysteria 出站](./configuration/outbound/hysteria) 和 [V2Ray 传输层#QUIC](./configuration/shared/v2ray-transport#quic)。 |
| `with_grpc` | 启用标准 gRPCuTLS](https://github.com/refraction-networking/utls) 支持, 参阅 [TLS](./configuration/shared/tls#utls)。 |
| `with_acme` | 启用 ACME TLS 证书签发支持,参阅 [TLS](./configuration/shared/tls)。 |
| `with_clash_api` | 启用 Clash api 支 支持,参阅 [V2Ray 传输层#gRPC](./configuration/shared/v2ray-transport#grpc)。 |
| `with_grpc` | 启用标准 gRPC 支持,参阅 [V2Ray 传输层#gRPC](./configuration/shared/v2ray-transport#grpc)。 |
| `with_wireguard` | 启用 WireGuard 支持,参阅 [WireGuard 出站](./configuration/outbound/wireguard)。 |
| `with_shadowsocksr` | 启用 ShadowsocksR 支持,参阅 [ShadowsocksR 出站](./configuration/outbound/shadowsocksr)。 |
| `with_ech` | 启用 TLS ECH 扩展支持,参阅 [TLS](./configuration/shared/tls#ech)。 |
| `with_utls` | 启用 [持,参阅 [实验性](./configuration/experimental#clash-api-fields)。 |
| `with_utls` | 启用 uTLS 支持,参阅 [实验性](./configuration/experimental#clash-api-fields)。 |
| `with_acme` | 启用 ACME TLS 证书签发支持,参阅 [TLS](./configuration/shared/tls)。 |
| `with_clash_api` | 启用 Clash API 支持,参阅 [实验性](./configuration/experimental#clash-api-fields)。 |
| `with_gvisor` | 启用 gVisor 支持,参阅 [Tun 入站](./configuration/inbound/tun#stack) 和 [WireGuard 出站](./configuration/outbound/wireguard#system_interface)。 |
| `with_embedded_tor` (需要 CGO) | 启用 嵌入式 Tor 支持,参阅 [Tor 出站](./configuration/outbound/tor)。 |
| `with_lwip` (需要 CGO) | 启用 LWIP Tun 栈支持,参阅 [Tun 入站](./configuration/inbound/tun#stack)。 |
@@ -42,13 +42,9 @@ go install -v -tags with_clash_api github.com/sagernet/sing-box/cmd/sing-box@lat
sing-box version
```
同时推荐使用 Systemd 来管理 sing-box 服务器实例。
同时推荐使用 systemd 来管理 sing-box 服务器实例。
参阅 [Linux 服务器安装示例](./examples/linux-server-installation)。
## 贡献者
[![](https://opencollective.com/sagernet/contributors.svg?width=740&button=false)](https://github.com/sagernet/sing-box/graphs/contributors)
## 授权
```

14
go.mod
View File

@@ -23,21 +23,21 @@ require (
github.com/pires/go-proxyproto v0.6.2
github.com/refraction-networking/utls v1.1.2
github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb
github.com/sagernet/sing v0.0.0-20220916071326-834794b006ea
github.com/sagernet/sing v0.0.0-20220925112014-b12b8b7fd220
github.com/sagernet/sing-dns v0.0.0-20220915084601-812e0864b45b
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6
github.com/sagernet/sing-tun v0.0.0-20220916073459-0032242c9617
github.com/sagernet/sing-vmess v0.0.0-20220913015714-c4ab86d40e12
github.com/sagernet/sing-tun v0.0.0-20220925112147-6bad0c2380ca
github.com/sagernet/sing-vmess v0.0.0-20220925083655-063bc85ea685
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/stretchr/testify v1.8.0
go.etcd.io/bbolt v1.3.6
go.uber.org/atomic v1.10.0
go4.org/netipx v0.0.0-20220812043211-3cc044ffd68d
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90
golang.org/x/net v0.0.0-20220909164309-bea034e7d591
golang.org/x/sys v0.0.0-20220913120320-3275c407cedc
go4.org/netipx v0.0.0-20220925034521-797b0c90d8ab
golang.org/x/crypto v0.0.0-20220924013350-4ba4fb4dd9e7
golang.org/x/net v0.0.0-20220923203811-8be639271d50
golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8
golang.zx2c4.com/wireguard v0.0.0-20220829161405-d1d08426b27b
google.golang.org/grpc v1.49.0
google.golang.org/protobuf v1.28.1

28
go.sum
View File

@@ -145,16 +145,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-20220916071326-834794b006ea h1:ZAWvZdeByPBBz3Vs+w3Erbh+DDo7D4biokoPhXl0nNU=
github.com/sagernet/sing v0.0.0-20220916071326-834794b006ea/go.mod h1:x3NHUeJBQwV75L51zwmLKQdLtRvR+M4PmXkfQtU1vIY=
github.com/sagernet/sing v0.0.0-20220925112014-b12b8b7fd220 h1:fQk/BHOeHw5murjeNTdmkXmDy9cMlbubRINRH7GDuu4=
github.com/sagernet/sing v0.0.0-20220925112014-b12b8b7fd220/go.mod h1:5/u6RMDMoGIkSNtrZb41kJvyIFg3Ysn69P3WiAu8m0c=
github.com/sagernet/sing-dns v0.0.0-20220915084601-812e0864b45b h1:cXCMNJ9heZ+c6l+qUcku60x9KyXo4SOAaJfg/6spOmU=
github.com/sagernet/sing-dns v0.0.0-20220915084601-812e0864b45b/go.mod h1:SrvWLfOSlnFmH32CWXicfilAGgIXR0VjrH6yRbuXYww=
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-20220916073459-0032242c9617 h1:fNTNmylhB/UjoBLusmrFu2B1fat4OCkDkQXTgrE7ZsE=
github.com/sagernet/sing-tun v0.0.0-20220916073459-0032242c9617/go.mod h1:5AhPUv9jWDQ3pv3Mj78SL/1TSjhoaj6WNASxRKLqXqM=
github.com/sagernet/sing-vmess v0.0.0-20220913015714-c4ab86d40e12 h1:4HYGbTDDemgBVTmaspXbkgjJlXc3hYVjNxSddJndq8Y=
github.com/sagernet/sing-vmess v0.0.0-20220913015714-c4ab86d40e12/go.mod h1:u66Vv7NHXJWfeAmhh7JuJp/cwxmuQlM56QoZ7B7Mmd0=
github.com/sagernet/sing-tun v0.0.0-20220925112147-6bad0c2380ca h1:Owgx9izFNYyMyUZ61td+mL3vumBhJz4zNismYlCyQbw=
github.com/sagernet/sing-tun v0.0.0-20220925112147-6bad0c2380ca/go.mod h1:ftP5VXlp3RJO5+WS3mPFk7jB9B/K/QrPEZX3BUORGQY=
github.com/sagernet/sing-vmess v0.0.0-20220925083655-063bc85ea685 h1:AZzFNRR/ZwMTceUQ1b/mxx6oyKqmFymdMn/yleJmoVM=
github.com/sagernet/sing-vmess v0.0.0-20220925083655-063bc85ea685/go.mod h1:bwhAdSNET1X+j9DOXGj9NIQR39xgcWIk1rOQ9lLD+gM=
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/websocket v0.0.0-20220913015213-615516348b4e h1:7uw2njHFGE+VpWamge6o56j2RWk4omF6uLKKxMmcWvs=
@@ -188,16 +188,16 @@ go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9i
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
go.uber.org/zap v1.22.0 h1:Zcye5DUgBloQ9BaT4qc9BnjOFog5TvBSAGkJ3Nf70c0=
go.uber.org/zap v1.22.0/go.mod h1:H4siCOZOrAolnUPJEkfaSjDqyP+BDS0DdDWzwcgt3+U=
go4.org/netipx v0.0.0-20220812043211-3cc044ffd68d h1:ggxwEf5eu0l8v+87VhX1czFh8zJul3hK16Gmruxn7hw=
go4.org/netipx v0.0.0-20220812043211-3cc044ffd68d/go.mod h1:tgPU4N2u9RByaTN3NC2p9xOzyFpte4jYwsIIRF7XlSc=
go4.org/netipx v0.0.0-20220925034521-797b0c90d8ab h1:+yW1yrZ09EYNu1spCUOHBBNRbrLnfmutwyhbhCv3b6Q=
go4.org/netipx v0.0.0-20220925034521-797b0c90d8ab/go.mod h1:tgPU4N2u9RByaTN3NC2p9xOzyFpte4jYwsIIRF7XlSc=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
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-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
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/crypto v0.0.0-20220924013350-4ba4fb4dd9e7 h1:WJywXQVIb56P2kAvXeMGTIgQ1ZHQxR60+F9dLsodECc=
golang.org/x/crypto v0.0.0-20220924013350-4ba4fb4dd9e7/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=
@@ -229,8 +229,8 @@ golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211111160137-58aab5ef257a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220630215102-69896b714898/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220909164309-bea034e7d591 h1:D0B/7al0LLrVC8aWF4+oxpv/m8bc7ViFfVS8/gXGdqI=
golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.0.0-20220923203811-8be639271d50 h1:vKyz8L3zkd+xrMeIaBsQ/MNVPVFSffdaU3ZyYlBGFnI=
golang.org/x/net v0.0.0-20220923203811-8be639271d50/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -264,8 +264,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-20220913120320-3275c407cedc h1:dpclq5m2YrqPGStKmtw7IcNbKLfbIqKXvNxDJKdIKYc=
golang.org/x/sys v0.0.0-20220913120320-3275c407cedc/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 h1:h+EGohizhe9XlX18rfpa8k8RAc5XyaeamM+0VHRd4lc=
golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8/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=

View File

@@ -141,13 +141,13 @@ func (a *myInboundAdapter) createMetadata(conn net.Conn, metadata adapter.Inboun
metadata.SniffOverrideDestination = a.listenOptions.SniffOverrideDestination
metadata.DomainStrategy = dns.DomainStrategy(a.listenOptions.DomainStrategy)
if !metadata.Source.IsValid() {
metadata.Source = M.SocksaddrFromNet(conn.RemoteAddr())
metadata.Source = M.SocksaddrFromNet(conn.RemoteAddr()).Unwrap()
}
if !metadata.Destination.IsValid() {
metadata.Destination = M.SocksaddrFromNet(conn.LocalAddr())
metadata.Destination = M.SocksaddrFromNet(conn.LocalAddr()).Unwrap()
}
if tcpConn, isTCP := common.Cast[*net.TCPConn](conn); isTCP {
metadata.OriginDestination = M.SocksaddrFromNet(tcpConn.LocalAddr())
metadata.OriginDestination = M.SocksaddrFromNet(tcpConn.LocalAddr()).Unwrap()
}
return metadata
}

View File

@@ -54,7 +54,7 @@ func (a *myInboundAdapter) loopUDPIn() {
metadata.SniffEnabled = a.listenOptions.SniffEnabled
metadata.SniffOverrideDestination = a.listenOptions.SniffOverrideDestination
metadata.DomainStrategy = dns.DomainStrategy(a.listenOptions.DomainStrategy)
metadata.Source = M.SocksaddrFromNetIP(addr)
metadata.Source = M.SocksaddrFromNetIP(addr).Unwrap()
metadata.OriginDestination = a.udpAddr
err = a.packetHandler.NewPacket(a.ctx, packetService, buffer, metadata)
if err != nil {
@@ -86,7 +86,7 @@ func (a *myInboundAdapter) loopUDPOOBIn() {
metadata.SniffEnabled = a.listenOptions.SniffEnabled
metadata.SniffOverrideDestination = a.listenOptions.SniffOverrideDestination
metadata.DomainStrategy = dns.DomainStrategy(a.listenOptions.DomainStrategy)
metadata.Source = M.SocksaddrFromNetIP(addr)
metadata.Source = M.SocksaddrFromNetIP(addr).Unwrap()
metadata.OriginDestination = a.udpAddr
err = a.oobPacketHandler.NewPacket(a.ctx, packetService, buffer, oob[:oobN], metadata)
if err != nil {
@@ -112,7 +112,7 @@ func (a *myInboundAdapter) loopUDPInThreadSafe() {
metadata.SniffEnabled = a.listenOptions.SniffEnabled
metadata.SniffOverrideDestination = a.listenOptions.SniffOverrideDestination
metadata.DomainStrategy = dns.DomainStrategy(a.listenOptions.DomainStrategy)
metadata.Source = M.SocksaddrFromNetIP(addr)
metadata.Source = M.SocksaddrFromNetIP(addr).Unwrap()
metadata.OriginDestination = a.udpAddr
err = a.packetHandler.NewPacket(a.ctx, packetService, buffer, metadata)
if err != nil {
@@ -140,7 +140,7 @@ func (a *myInboundAdapter) loopUDPOOBInThreadSafe() {
metadata.SniffEnabled = a.listenOptions.SniffEnabled
metadata.SniffOverrideDestination = a.listenOptions.SniffOverrideDestination
metadata.DomainStrategy = dns.DomainStrategy(a.listenOptions.DomainStrategy)
metadata.Source = M.SocksaddrFromNetIP(addr)
metadata.Source = M.SocksaddrFromNetIP(addr).Unwrap()
metadata.OriginDestination = a.udpAddr
err = a.oobPacketHandler.NewPacket(a.ctx, packetService, buffer, oob[:oobN], metadata)
if err != nil {

View File

@@ -261,9 +261,9 @@ func (h *Hysteria) acceptStream(ctx context.Context, conn quic.Connection, strea
metadata.SniffEnabled = h.listenOptions.SniffEnabled
metadata.SniffOverrideDestination = h.listenOptions.SniffOverrideDestination
metadata.DomainStrategy = dns.DomainStrategy(h.listenOptions.DomainStrategy)
metadata.Source = M.SocksaddrFromNet(conn.RemoteAddr())
metadata.OriginDestination = M.SocksaddrFromNet(conn.LocalAddr())
metadata.Destination = M.ParseSocksaddrHostPort(request.Host, request.Port)
metadata.Source = M.SocksaddrFromNet(conn.RemoteAddr()).Unwrap()
metadata.OriginDestination = M.SocksaddrFromNet(conn.LocalAddr()).Unwrap()
metadata.Destination = M.ParseSocksaddrHostPort(request.Host, request.Port).Unwrap()
if !request.UDP {
err = hysteria.WriteServerResponse(stream, hysteria.ServerResponse{

View File

@@ -75,7 +75,7 @@ func (t *TProxy) Start() error {
}
func (t *TProxy) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
metadata.Destination = M.SocksaddrFromNet(conn.LocalAddr())
metadata.Destination = M.SocksaddrFromNet(conn.LocalAddr()).Unwrap()
return t.newConnection(ctx, conn, metadata)
}
@@ -84,14 +84,15 @@ func (t *TProxy) NewPacket(ctx context.Context, conn N.PacketConn, buffer *buf.B
if err != nil {
return E.Cause(err, "get tproxy destination")
}
metadata.Destination = M.SocksaddrFromNetIP(destination)
metadata.Destination = M.SocksaddrFromNetIP(destination).Unwrap()
t.udpNat.NewContextPacket(ctx, metadata.Source.AddrPort(), buffer, adapter.UpstreamMetadata(metadata), func(natConn N.PacketConn) (context.Context, N.PacketWriter) {
return adapter.WithContext(log.ContextWithNewID(ctx), &metadata), &tproxyPacketWriter{source: natConn}
return adapter.WithContext(log.ContextWithNewID(ctx), &metadata), &tproxyPacketWriter{ctx: ctx, source: natConn, destination: metadata.Destination}
})
return nil
}
type tproxyPacketWriter struct {
ctx context.Context
source N.PacketConn
destination M.Socksaddr
conn *net.UDPConn
@@ -99,25 +100,27 @@ type tproxyPacketWriter struct {
func (w *tproxyPacketWriter) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
defer buffer.Release()
var udpConn *net.UDPConn
if w.destination == destination && w.conn != nil {
_, err := w.conn.WriteToUDPAddrPort(buffer.Bytes(), M.AddrPortFromNet(w.source.LocalAddr()))
if err == nil {
w.conn = nil
}
return err
}
var listener net.ListenConfig
listener.Control = control.Append(listener.Control, control.ReuseAddr())
listener.Control = control.Append(listener.Control, redir.TProxyWriteBack())
packetConn, err := listener.ListenPacket(w.ctx, "udp", destination.String())
if err != nil {
return err
}
udpConn := packetConn.(*net.UDPConn)
if w.destination == destination {
if w.conn != nil {
udpConn = w.conn
}
w.conn = udpConn
} else {
defer udpConn.Close()
}
if udpConn == nil {
var err error
udpConn, err = redir.DialUDP(destination.UDPAddr(), M.SocksaddrFromNet(w.source.LocalAddr()).UDPAddr())
if err != nil {
return E.Cause(err, "tproxy udp write back")
}
if w.destination == destination {
w.conn = udpConn
} else {
defer udpConn.Close()
}
}
return common.Error(udpConn.Write(buffer.Bytes()))
return common.Error(udpConn.WriteToUDPAddrPort(buffer.Bytes(), M.AddrPortFromNet(w.source.LocalAddr())))
}
func (w *tproxyPacketWriter) Close() error {

View File

@@ -120,17 +120,18 @@ func (h *Outbound) UnmarshalJSON(bytes []byte) error {
}
type DialerOptions struct {
Detour string `json:"detour,omitempty"`
BindInterface string `json:"bind_interface,omitempty"`
BindAddress *ListenAddress `json:"bind_address,omitempty"`
ProtectPath string `json:"protect_path,omitempty"`
RoutingMark int `json:"routing_mark,omitempty"`
ReuseAddr bool `json:"reuse_addr,omitempty"`
ConnectTimeout Duration `json:"connect_timeout,omitempty"`
TCPFastOpen bool `json:"tcp_fast_open,omitempty"`
UDPFragment bool `json:"udp_fragment,omitempty"`
DomainStrategy DomainStrategy `json:"domain_strategy,omitempty"`
FallbackDelay Duration `json:"fallback_delay,omitempty"`
Detour string `json:"detour,omitempty"`
BindInterface string `json:"bind_interface,omitempty"`
BindAddress *ListenAddress `json:"bind_address,omitempty"`
ProtectPath string `json:"protect_path,omitempty"`
RoutingMark int `json:"routing_mark,omitempty"`
ReuseAddr bool `json:"reuse_addr,omitempty"`
ConnectTimeout Duration `json:"connect_timeout,omitempty"`
TCPFastOpen bool `json:"tcp_fast_open,omitempty"`
UDPFragment *bool `json:"udp_fragment,omitempty"`
UDPFragmentDefault bool `json:"-"`
DomainStrategy DomainStrategy `json:"domain_strategy,omitempty"`
FallbackDelay Duration `json:"fallback_delay,omitempty"`
}
type ServerOptions struct {

View File

@@ -3,6 +3,7 @@ package option
type InboundTLSOptions struct {
Enabled bool `json:"enabled,omitempty"`
ServerName string `json:"server_name,omitempty"`
Insecure bool `json:"insecure,omitempty"`
ALPN Listable[string] `json:"alpn,omitempty"`
MinVersion string `json:"min_version,omitempty"`
MaxVersion string `json:"max_version,omitempty"`

View File

@@ -23,7 +23,7 @@ type VMessOutboundOptions struct {
AuthenticatedLength bool `json:"authenticated_length,omitempty"`
Network NetworkList `json:"network,omitempty"`
TLS *OutboundTLSOptions `json:"tls,omitempty"`
PacketAddr bool `json:"packet_addr,omitempty"`
PacketEncoding string `json:"packet_encoding,omitempty"`
Multiplex *MultiplexOptions `json:"multiplex,omitempty"`
Transport *V2RayTransportOptions `json:"transport,omitempty"`
}

View File

@@ -35,6 +35,7 @@ type Direct struct {
}
func NewDirect(router adapter.Router, logger log.ContextLogger, tag string, options option.DirectOutboundOptions) (*Direct, error) {
options.UDPFragmentDefault = true
outbound := &Direct{
myOutboundAdapter: myOutboundAdapter{
protocol: C.TypeDirect,

View File

@@ -38,6 +38,7 @@ type Hysteria struct {
recvBPS uint64
connAccess sync.Mutex
conn quic.Connection
rawConn net.Conn
udpAccess sync.RWMutex
udpSessions map[uint32]chan *hysteria.UDPMessage
udpDefragger hysteria.Defragger
@@ -149,7 +150,6 @@ func (h *Hysteria) offer(ctx context.Context) (quic.Connection, error) {
if err != nil {
return nil, err
}
h.conn = conn
if common.Contains(h.network, N.NetworkUDP) {
for _, session := range h.udpSessions {
close(session)
@@ -201,6 +201,8 @@ func (h *Hysteria) offerNew(ctx context.Context) (quic.Connection, error) {
return nil, E.New("remote error: ", serverHello.Message)
}
quicConn.SetCongestionControl(hysteria.NewBrutalSender(congestion.ByteCount(serverHello.RecvBPS)))
h.conn = quicConn
h.rawConn = udpConn
return quicConn, nil
}
@@ -240,6 +242,7 @@ func (h *Hysteria) Close() error {
defer h.udpAccess.Unlock()
if h.conn != nil {
h.conn.CloseWithError(0, "")
h.rawConn.Close()
}
for _, session := range h.udpSessions {
close(session)

View File

@@ -74,7 +74,7 @@ func (h *Socks) DialContext(ctx context.Context, network string, destination M.S
default:
return nil, E.Extend(N.ErrUnknownNetwork, network)
}
if destination.IsFqdn() {
if h.resolve && destination.IsFqdn() {
addrs, err := h.router.LookupDefault(ctx, destination.Fqdn)
if err != nil {
return nil, err

View File

@@ -103,6 +103,10 @@ func (h *Trojan) NewPacketConnection(ctx context.Context, conn N.PacketConn, met
return NewPacketConnection(ctx, h, conn, metadata)
}
func (h *Trojan) Close() error {
return common.Close(h.multiplexDialer, h.transport)
}
type trojanDialer Trojan
func (h *trojanDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {

View File

@@ -148,7 +148,7 @@ func NewURLTestGroup(router adapter.Router, logger log.Logger, outbounds []adapt
link = "http://www.gstatic.com/generate_204"
}
if interval == 0 {
interval = C.TCPTimeout
interval = C.DefaultURLTestInterval
}
if tolerance == 0 {
tolerance = 50

View File

@@ -11,8 +11,9 @@ import (
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-box/transport/v2ray"
"github.com/sagernet/sing-box/transport/vless"
"github.com/sagernet/sing-dns"
"github.com/sagernet/sing-vmess/packetaddr"
"github.com/sagernet/sing-vmess/vless"
"github.com/sagernet/sing/common"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
@@ -73,10 +74,6 @@ func NewVLESS(ctx context.Context, router adapter.Router, logger log.ContextLogg
return outbound, nil
}
func (h *VLESS) Close() error {
return common.Close(h.transport)
}
func (h *VLESS) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
ctx, metadata := adapter.AppendContext(ctx)
metadata.Outbound = h.tag
@@ -104,7 +101,7 @@ func (h *VLESS) DialContext(ctx context.Context, network string, destination M.S
return h.client.DialEarlyConn(conn, destination), nil
case N.NetworkUDP:
h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
return h.client.DialPacketConn(conn, destination), nil
return h.client.DialEarlyPacketConn(conn, destination), nil
default:
return nil, E.Extend(N.ErrUnknownNetwork, network)
}
@@ -129,11 +126,11 @@ func (h *VLESS) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.
return nil, err
}
if h.xudp {
return h.client.DialXUDPPacketConn(conn, destination), nil
return h.client.DialEarlyXUDPPacketConn(conn, destination), nil
} else if h.packetAddr {
return packetaddr.NewConn(h.client.DialPacketConn(conn, M.Socksaddr{Fqdn: packetaddr.SeqPacketMagicAddress}), destination), nil
return dialer.NewResolvePacketConn(ctx, h.router, dns.DomainStrategyAsIS, packetaddr.NewConn(h.client.DialEarlyPacketConn(conn, M.Socksaddr{Fqdn: packetaddr.SeqPacketMagicAddress}), destination)), nil
} else {
return h.client.DialPacketConn(conn, destination), nil
return h.client.DialEarlyPacketConn(conn, destination), nil
}
}
@@ -144,3 +141,7 @@ func (h *VLESS) NewConnection(ctx context.Context, conn net.Conn, metadata adapt
func (h *VLESS) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
return NewPacketConnection(ctx, h, conn, metadata)
}
func (h *VLESS) Close() error {
return common.Close(h.transport)
}

View File

@@ -12,6 +12,7 @@ import (
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-box/transport/v2ray"
"github.com/sagernet/sing-dns"
"github.com/sagernet/sing-vmess"
"github.com/sagernet/sing-vmess/packetaddr"
"github.com/sagernet/sing/common"
@@ -31,6 +32,7 @@ type VMess struct {
tlsConfig tls.Config
transport adapter.V2RayClientTransport
packetAddr bool
xudp bool
}
func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.VMessOutboundOptions) (*VMess, error) {
@@ -62,8 +64,14 @@ func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogg
if err != nil {
return nil, err
}
if outbound.multiplexDialer == nil && options.PacketAddr {
switch options.PacketEncoding {
case "":
case "packetaddr":
outbound.packetAddr = true
case "xudp":
outbound.xudp = true
default:
return nil, E.New("unknown packet encoding: ", options.PacketEncoding)
}
var clientOptions []vmess.ClientOption
if options.GlobalPadding {
@@ -88,7 +96,7 @@ func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogg
}
func (h *VMess) Close() error {
return common.Close(h.transport)
return common.Close(h.multiplexDialer, h.transport)
}
func (h *VMess) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
@@ -176,7 +184,9 @@ func (h *vmessDialer) ListenPacket(ctx context.Context, destination M.Socksaddr)
return nil, err
}
if h.packetAddr {
return packetaddr.NewConn(h.client.DialEarlyPacketConn(conn, M.Socksaddr{Fqdn: packetaddr.SeqPacketMagicAddress}), destination), nil
return dialer.NewResolvePacketConn(ctx, h.router, dns.DomainStrategyAsIS, packetaddr.NewConn(h.client.DialEarlyPacketConn(conn, M.Socksaddr{Fqdn: packetaddr.SeqPacketMagicAddress}), destination)), nil
} else if h.xudp {
return h.client.DialEarlyXUDPPacketConn(conn, destination), nil
} else {
return h.client.DialEarlyPacketConn(conn, destination), nil
}

View File

@@ -162,9 +162,8 @@ func (w *WireGuard) Start() error {
}
func (w *WireGuard) Close() error {
return common.Close(
w.tunDevice,
common.PtrOrNil(w.device),
common.PtrOrNil(w.bind),
)
if w.device != nil {
w.device.Close()
}
return common.Close(w.tunDevice)
}

View File

@@ -554,7 +554,7 @@ func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata ad
if err == nil {
metadata.Protocol = sniffMetadata.Protocol
metadata.Domain = sniffMetadata.Domain
if metadata.SniffOverrideDestination && sniff.IsDomainName(metadata.Domain) {
if metadata.SniffOverrideDestination && M.IsDomainName(metadata.Domain) {
metadata.Destination = M.Socksaddr{
Fqdn: metadata.Domain,
Port: metadata.Destination.Port,
@@ -631,7 +631,7 @@ func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, m
if err == nil {
metadata.Protocol = sniffMetadata.Protocol
metadata.Domain = sniffMetadata.Domain
if metadata.SniffOverrideDestination && sniff.IsDomainName(metadata.Domain) {
if metadata.SniffOverrideDestination && M.IsDomainName(metadata.Domain) {
metadata.Destination = M.Socksaddr{
Fqdn: metadata.Domain,
Port: metadata.Destination.Port,

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.match(metadata.Source.Addr)
return r.ipSet.Contains(metadata.Source.Addr)
} else {
if metadata.Destination.IsIP() {
return r.match(metadata.Destination.Addr)
return r.ipSet.Contains(metadata.Destination.Addr)
} else {
for _, address := range metadata.DestinationAddresses {
if r.match(address) {
if r.ipSet.Contains(address) {
return true
}
}
@@ -74,14 +74,6 @@ 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

@@ -15,9 +15,14 @@ import (
"github.com/sagernet/sing/protocol/socks"
"github.com/stretchr/testify/require"
"go.uber.org/goleak"
)
func startInstance(t *testing.T, options option.Options) {
func TestMain(m *testing.M) {
goleak.VerifyTestMain(m)
}
func startInstance(t *testing.T, options option.Options) *box.Box {
if debug.Enabled {
options.Log = &option.LogOptions{
Level: "trace",
@@ -27,10 +32,12 @@ func startInstance(t *testing.T, options option.Options) {
Level: "warning",
}
}
// ctx := context.Background()
ctx, cancel := context.WithCancel(context.Background())
var instance *box.Box
var err error
for retry := 0; retry < 3; retry++ {
instance, err = box.New(context.Background(), options)
instance, err = box.New(ctx, options)
require.NoError(t, err)
err = instance.Start()
if err != nil {
@@ -42,7 +49,9 @@ func startInstance(t *testing.T, options option.Options) {
require.NoError(t, err)
t.Cleanup(func() {
instance.Close()
cancel()
})
return instance
}
func testSuit(t *testing.T, clientPort uint16, testPort uint16) {

View File

@@ -7,7 +7,6 @@ import (
"errors"
"io"
"net"
"net/http"
_ "net/http/pprof"
"net/netip"
"sync"
@@ -39,6 +38,7 @@ const (
ImageShadowTLS = "ghcr.io/ihciah/shadow-tls:latest"
ImageShadowsocksR = "teddysun/shadowsocks-r:latest"
ImageXRayCore = "teddysun/xray:latest"
ImageShadowsocksLegacy = "mritd/shadowsocks:latest"
)
var allImages = []string{
@@ -53,6 +53,7 @@ var allImages = []string{
ImageShadowTLS,
ImageShadowsocksR,
ImageXRayCore,
ImageShadowsocksLegacy,
}
var localIP = netip.MustParseAddr("127.0.0.1")
@@ -101,12 +102,6 @@ func init() {
io.Copy(io.Discard, imageStream)
}
go func() {
err = http.ListenAndServe("0.0.0.0:8965", nil)
if err != nil {
log.Debug(err)
}
}()
}
func newPingPongPair() (chan []byte, chan []byte, func(t *testing.T) error) {

View File

@@ -0,0 +1,41 @@
{
"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": "",
"alterId": 0,
"security": "none",
"experiments": ""
}
]
}
]
},
"mux": {
"enabled": true
}
}
]
}

View File

@@ -10,10 +10,11 @@ require (
github.com/docker/docker v20.10.18+incompatible
github.com/docker/go-connections v0.4.0
github.com/gofrs/uuid v4.3.0+incompatible
github.com/sagernet/sing v0.0.0-20220914045234-93cc53b60cee
github.com/sagernet/sing v0.0.0-20220921101604-86d7d510231f
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6
github.com/spyzhov/ajson v0.7.1
github.com/stretchr/testify v1.8.0
go.uber.org/goleak v1.2.0
golang.org/x/net v0.0.0-20220909164309-bea034e7d591
)
@@ -21,6 +22,7 @@ require (
require (
berty.tech/go-libtor v1.0.385 // indirect
github.com/Dreamacro/clash v1.11.8 // indirect
github.com/Microsoft/go-winio v0.5.1 // indirect
github.com/ajg/form v1.5.1 // indirect
github.com/andybalholm/brotli v1.0.4 // indirect
@@ -65,20 +67,19 @@ require (
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect
github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb // indirect
github.com/sagernet/shadowsocksr v0.0.0-20220912092645-c9ab93f81bb0 // indirect
github.com/sagernet/sing-dns v0.0.0-20220913115644-aebff1dfbba8 // indirect
github.com/sagernet/sing-tun v0.0.0-20220914100102-057dd738a7f7 // indirect
github.com/sagernet/sing-vmess v0.0.0-20220913015714-c4ab86d40e12 // indirect
github.com/sagernet/sing-dns v0.0.0-20220915084601-812e0864b45b // indirect
github.com/sagernet/sing-tun v0.0.0-20220922083325-80ee99472704 // indirect
github.com/sagernet/sing-vmess v0.0.0-20220923035311-d70afe4ab2c9 // 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.9.0 // indirect
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
go.etcd.io/bbolt v1.3.6 // 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-20220829220503-c86fa9a7ed90 // indirect
golang.org/x/crypto v0.0.0-20220919173607-35f4265a4bc0 // 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-20220913120320-3275c407cedc // indirect
@@ -86,7 +87,7 @@ require (
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-20220904105730-b51010ba13f0 // 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

@@ -5,6 +5,8 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Dreamacro/clash v1.11.8 h1:t/sy3/tiihRlvV3SsliYFjj8rKpbLw5IJ2PymiHcwS8=
github.com/Dreamacro/clash v1.11.8/go.mod h1:LsWCcJFoKuL1C5F2c0m/1690wihTHYSU3J+im09yTwQ=
github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY=
github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
@@ -105,8 +107,8 @@ github.com/klauspost/cpuid/v2 v2.1.0/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8t
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/libdns/libdns v0.2.1 h1:Wu59T7wSHRgtA0cfxC+n1c/e+O3upJGWytknkmFEDis=
github.com/libdns/libdns v0.2.1/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40=
github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
@@ -161,27 +163,25 @@ github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6E
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb h1:wc0yQ+SBn4TaTYRwpwvEm3nc4eRdxk6vtRbouLVZAzk=
github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb/go.mod h1:MIccjRKnPTjWwAOpl+AUGWOkzyTd9tERytudxu+1ra4=
github.com/sagernet/shadowsocksr v0.0.0-20220912092645-c9ab93f81bb0 h1:lQ4RFWY/wBYmXl/zJJCwQbhiEIbgEqC7j+nhZYkgwmU=
github.com/sagernet/shadowsocksr v0.0.0-20220912092645-c9ab93f81bb0/go.mod h1:xSHLCsdgy5QIozXFEv6uDgMWzyrRdFFjr3TgL+juu6g=
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-20220914045234-93cc53b60cee h1:+3w7+QWnhWi3Qz7+Xcais8zViHRUPIkmxq3eYZm/zvk=
github.com/sagernet/sing v0.0.0-20220914045234-93cc53b60cee/go.mod h1:x3NHUeJBQwV75L51zwmLKQdLtRvR+M4PmXkfQtU1vIY=
github.com/sagernet/sing-dns v0.0.0-20220913115644-aebff1dfbba8 h1:Iyfl+Rm5jcDvXuy/jpOBI3eu35ujci50tkqYHHwwg+8=
github.com/sagernet/sing-dns v0.0.0-20220913115644-aebff1dfbba8/go.mod h1:bPVnJ5gJ0WmUfN1bJP9Cis0ab8SSByx6JVzyLJjDMwA=
github.com/sagernet/sing v0.0.0-20220921101604-86d7d510231f h1:GX416thAwyc0vHBOal/qplvdhFgYO2dHD5GqADCJ0Ig=
github.com/sagernet/sing v0.0.0-20220921101604-86d7d510231f/go.mod h1:x3NHUeJBQwV75L51zwmLKQdLtRvR+M4PmXkfQtU1vIY=
github.com/sagernet/sing-dns v0.0.0-20220915084601-812e0864b45b h1:cXCMNJ9heZ+c6l+qUcku60x9KyXo4SOAaJfg/6spOmU=
github.com/sagernet/sing-dns v0.0.0-20220915084601-812e0864b45b/go.mod h1:SrvWLfOSlnFmH32CWXicfilAGgIXR0VjrH6yRbuXYww=
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-20220914100102-057dd738a7f7 h1:zdvFDYMz8s0e9UmOxMk0wNGOKh64KfeWpx8UAbJJI60=
github.com/sagernet/sing-tun v0.0.0-20220914100102-057dd738a7f7/go.mod h1:5AhPUv9jWDQ3pv3Mj78SL/1TSjhoaj6WNASxRKLqXqM=
github.com/sagernet/sing-vmess v0.0.0-20220913015714-c4ab86d40e12 h1:4HYGbTDDemgBVTmaspXbkgjJlXc3hYVjNxSddJndq8Y=
github.com/sagernet/sing-vmess v0.0.0-20220913015714-c4ab86d40e12/go.mod h1:u66Vv7NHXJWfeAmhh7JuJp/cwxmuQlM56QoZ7B7Mmd0=
github.com/sagernet/sing-tun v0.0.0-20220922083325-80ee99472704 h1:DOQQXQbB2gq4n2FuMHrL07HRs2naCCsuiu/9l1JFb9A=
github.com/sagernet/sing-tun v0.0.0-20220922083325-80ee99472704/go.mod h1:5AhPUv9jWDQ3pv3Mj78SL/1TSjhoaj6WNASxRKLqXqM=
github.com/sagernet/sing-vmess v0.0.0-20220923035311-d70afe4ab2c9 h1:w7JlEa4xHXuuPTL3Opb+Z5f/l66SYi54rSfobzuxSF8=
github.com/sagernet/sing-vmess v0.0.0-20220923035311-d70afe4ab2c9/go.mod h1:bwhAdSNET1X+j9DOXGj9NIQR39xgcWIk1rOQ9lLD+gM=
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/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.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spyzhov/ajson v0.7.1 h1:1MDIlPc6x0zjNtpa7tDzRAyFAvRX+X8ZsvtYz5lZg6A=
github.com/spyzhov/ajson v0.7.1/go.mod h1:63V+CGM6f1Bu/p4nLIN8885ojBdt88TbLoSFzyqMuVA=
@@ -205,8 +205,9 @@ go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqe
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk=
go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo=
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
@@ -218,11 +219,10 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
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-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
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/crypto v0.0.0-20220919173607-35f4265a4bc0 h1:a5Yg6ylndHHYJqIPrdq0AhvR6KTvDTAvgBtaidhEevY=
golang.org/x/crypto v0.0.0-20220919173607-35f4265a4bc0/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=
@@ -230,6 +230,7 @@ golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTk
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug=
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
@@ -266,8 +267,8 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde h1:ejfdSekXMDxDLbRrJMwUk6KnSLZ2McaUCVcIKM+N6jc=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -295,10 +296,10 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/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-20220715151400-c0bba94af5f8/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-20220913120320-3275c407cedc h1:dpclq5m2YrqPGStKmtw7IcNbKLfbIqKXvNxDJKdIKYc=
golang.org/x/sys v0.0.0-20220913120320-3275c407cedc/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
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=
@@ -334,8 +335,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-20220904105730-b51010ba13f0 h1:5ZkdpbduT/g+9OtbSDvbF3KvfQG45CtH/ppO8FUmvCQ=
golang.zx2c4.com/wireguard v0.0.0-20220904105730-b51010ba13f0/go.mod h1:enML0deDxY1ux+B6ANGiwtg0yAJi1rctkTpcHNAVPyg=
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=

170
test/mux_cool_test.go Normal file
View File

@@ -0,0 +1,170 @@
package main
import (
"net/netip"
"os"
"testing"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option"
"github.com/spyzhov/ajson"
"github.com/stretchr/testify/require"
)
func TestMuxCoolServer(t *testing.T) {
userId := newUUID()
content, err := os.ReadFile("config/vmess-mux-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).MustKey("settings").MustKey("vnext").MustIndex(0)
outbound.MustKey("port").SetNumeric(float64(serverPort))
user := outbound.MustKey("users").MustIndex(0)
user.MustKey("id").SetString(userId.String())
content, err = ajson.Marshal(config)
require.NoError(t, err)
startDockerContainer(t, DockerOptions{
Image: ImageV2RayCore,
Ports: []uint16{serverPort, testPort},
EntryPoint: "v2ray",
Stdin: content,
})
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(),
},
},
},
},
},
})
testSuit(t, clientPort, testPort)
}
func TestMuxCoolClient(t *testing.T) {
user := newUUID()
content, err := os.ReadFile("config/vmess-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(user.String())
content, err = ajson.Marshal(config)
require.NoError(t, err)
startDockerContainer(t, DockerOptions{
Image: ImageXRayCore,
Ports: []uint16{serverPort, testPort},
EntryPoint: "xray",
Stdin: content,
})
startInstance(t, option.Options{
Inbounds: []option.Inbound{
{
Type: C.TypeMixed,
MixedOptions: option.HTTPMixedInboundOptions{
ListenOptions: option.ListenOptions{
Listen: option.ListenAddress(netip.IPv4Unspecified()),
ListenPort: clientPort,
},
},
},
},
Outbounds: []option.Outbound{
{
Type: C.TypeVMess,
VMessOptions: option.VMessOutboundOptions{
ServerOptions: option.ServerOptions{
Server: "127.0.0.1",
ServerPort: serverPort,
},
UUID: user.String(),
PacketEncoding: "xudp",
},
},
},
})
testSuit(t, clientPort, testPort)
}
func TestMuxCoolSelf(t *testing.T) {
user := newUUID()
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.TypeVMess,
VMessOptions: option.VMessInboundOptions{
ListenOptions: option.ListenOptions{
Listen: option.ListenAddress(netip.IPv4Unspecified()),
ListenPort: serverPort,
},
Users: []option.VMessUser{
{
Name: "sekai",
UUID: user.String(),
},
},
},
},
},
Outbounds: []option.Outbound{
{
Type: C.TypeDirect,
},
{
Type: C.TypeVMess,
Tag: "vmess-out",
VMessOptions: option.VMessOutboundOptions{
ServerOptions: option.ServerOptions{
Server: "127.0.0.1",
ServerPort: serverPort,
},
UUID: user.String(),
PacketEncoding: "xudp",
},
},
},
Route: &option.RouteOptions{
Rules: []option.Rule{
{
DefaultOptions: option.DefaultRule{
Inbound: []string{"mixed-in"},
Outbound: "vmess-out",
},
},
},
},
})
testSuit(t, clientPort, testPort)
}

View File

@@ -17,6 +17,10 @@ var muxProtocols = []mux.Protocol{
mux.ProtocolSMux,
}
func TestVMessSMux(t *testing.T) {
testVMessMux(t, mux.ProtocolSMux.String())
}
func TestShadowsocksMux(t *testing.T) {
for _, protocol := range muxProtocols {
t.Run(protocol.String(), func(t *testing.T) {
@@ -25,14 +29,6 @@ func TestShadowsocksMux(t *testing.T) {
}
}
func TestVMessMux(t *testing.T) {
for _, protocol := range muxProtocols {
t.Run(protocol.String(), func(t *testing.T) {
testVMessMux(t, protocol.String())
})
}
}
func testShadowsocksMux(t *testing.T, protocol string) {
method := shadowaead_2022.List[0]
password := mkBase64(t, 16)

63
test/ss_plugin_test.go Normal file
View File

@@ -0,0 +1,63 @@
package main
import (
"net/netip"
"testing"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option"
)
func TestShadowsocksObfs(t *testing.T) {
for _, mode := range []string{
"http", "tls",
} {
t.Run("obfs-local "+mode, func(t *testing.T) {
testShadowsocksPlugin(t, "obfs-local", "obfs="+mode, "--plugin obfs-server --plugin-opts obfs="+mode)
})
}
}
func TestShadowsocksV2RayPlugin(t *testing.T) {
testShadowsocksPlugin(t, "v2ray-plugin", "", "--plugin v2ray-plugin --plugin-opts=server")
}
func testShadowsocksPlugin(t *testing.T, name string, opts string, args string) {
startDockerContainer(t, DockerOptions{
Image: ImageShadowsocksLegacy,
Ports: []uint16{serverPort, testPort},
Env: []string{
"SS_MODULE=ss-server",
"SS_CONFIG=-s 0.0.0.0 -u -p 10000 -m chacha20-ietf-poly1305 -k FzcLbKs2dY9mhL " + args,
},
})
startInstance(t, option.Options{
Inbounds: []option.Inbound{
{
Type: C.TypeMixed,
MixedOptions: option.HTTPMixedInboundOptions{
ListenOptions: option.ListenOptions{
Listen: option.ListenAddress(netip.IPv4Unspecified()),
ListenPort: clientPort,
},
},
},
},
Outbounds: []option.Outbound{
{
Type: C.TypeShadowsocks,
ShadowsocksOptions: option.ShadowsocksOutboundOptions{
ServerOptions: option.ServerOptions{
Server: "127.0.0.1",
ServerPort: serverPort,
},
Method: "chacha20-ietf-poly1305",
Password: "FzcLbKs2dY9mhL",
Plugin: name,
PluginOptions: opts,
},
},
},
})
testSuitSimple(t, clientPort, testPort)
}

View File

@@ -18,7 +18,7 @@ func newUUID() uuid.UUID {
return user
}
func _TestVMessAuto(t *testing.T) {
func TestVMessAuto(t *testing.T) {
security := "auto"
t.Run("self", func(t *testing.T) {
testVMessSelf(t, security, 0, false, false, false)
@@ -306,7 +306,7 @@ func testVMessSelf(t *testing.T, security string, alterId int, globalPadding boo
AlterId: alterId,
GlobalPadding: globalPadding,
AuthenticatedLength: authenticatedLength,
PacketAddr: packetAddr,
PacketEncoding: "packetaddr",
},
},
},

View File

@@ -462,7 +462,7 @@ func (c *PacketConn) ReadPacket(buffer *buf.Buffer) (destination M.Socksaddr, er
return
}
err = common.Error(buffer.Write(msg.Data))
destination = M.ParseSocksaddrHostPort(msg.Host, msg.Port)
destination = M.ParseSocksaddrHostPort(msg.Host, msg.Port).Unwrap()
return
}
@@ -473,20 +473,39 @@ func (c *PacketConn) ReadPacketThreadSafe() (buffer *buf.Buffer, destination M.S
return
}
buffer = buf.As(msg.Data)
destination = M.ParseSocksaddrHostPort(msg.Host, msg.Port)
destination = M.ParseSocksaddrHostPort(msg.Host, msg.Port).Unwrap()
return
}
func (c *PacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
return WriteUDPMessage(c.session, UDPMessage{
SessionID: c.sessionId,
Host: destination.Unwrap().AddrString(),
Host: destination.AddrString(),
Port: destination.Port,
FragCount: 1,
Data: buffer.Bytes(),
})
}
func (c *PacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
msg := <-c.msgCh
if msg == nil {
err = net.ErrClosed
return
}
n = copy(p, msg.Data)
addr = M.ParseSocksaddrHostPort(msg.Host, msg.Port).UDPAddr()
return
}
func (c *PacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
err = c.WritePacket(buf.As(p), M.SocksaddrFromNet(addr))
if err == nil {
n = len(p)
}
return
}
func (c *PacketConn) LocalAddr() net.Addr {
return nil
}
@@ -507,14 +526,6 @@ func (c *PacketConn) SetWriteDeadline(t time.Time) error {
return os.ErrInvalid
}
func (c *PacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
return 0, nil, os.ErrInvalid
}
func (c *PacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
return 0, os.ErrInvalid
}
func (c *PacketConn) Read(b []byte) (n int, err error) {
return 0, os.ErrInvalid
}

View File

@@ -19,7 +19,10 @@ func init() {
}
func newObfsLocal(pluginOpts Args, router adapter.Router, dialer N.Dialer, serverAddr M.Socksaddr) (Plugin, error) {
var plugin ObfsLocal
plugin := &ObfsLocal{
dialer: dialer,
serverAddr: serverAddr,
}
mode := "http"
if obfsMode, loaded := pluginOpts.Get("obfs"); loaded {
mode = obfsMode
@@ -35,7 +38,7 @@ func newObfsLocal(pluginOpts Args, router adapter.Router, dialer N.Dialer, serve
return nil, E.New("unknown obfs mode ", mode)
}
plugin.port = F.ToString(serverAddr.Port)
return &plugin, nil
return plugin, nil
}
type ObfsLocal struct {

View File

@@ -2,12 +2,15 @@ package sip003
import (
"context"
"net"
"strconv"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/tls"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-box/transport/v2ray"
"github.com/sagernet/sing-vmess"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
@@ -56,6 +59,7 @@ func newV2RayPlugin(pluginOpts Args, router adapter.Router, dialer N.Dialer, ser
}
}
var mux int
var transportOptions option.V2RayTransportOptions
switch mode {
case "websocket":
@@ -68,6 +72,15 @@ func newV2RayPlugin(pluginOpts Args, router adapter.Router, dialer N.Dialer, ser
Path: path,
},
}
if muxOpt, loaded := pluginOpts.Get("mux"); loaded {
muxVal, err := strconv.Atoi(muxOpt)
if err != nil {
return nil, E.Cause(err, "parse mux value")
}
mux = muxVal
} else {
mux = 1
}
case "quic":
transportOptions = option.V2RayTransportOptions{
Type: C.V2RayTransportTypeQUIC,
@@ -76,5 +89,28 @@ func newV2RayPlugin(pluginOpts Args, router adapter.Router, dialer N.Dialer, ser
return nil, E.New("v2ray-plugin: unknown mode: " + mode)
}
return v2ray.NewClientTransport(context.Background(), dialer, serverAddr, transportOptions, tlsClient)
transport, err := v2ray.NewClientTransport(context.Background(), dialer, serverAddr, transportOptions, tlsClient)
if err != nil {
return nil, err
}
if mux > 0 {
return &v2rayMuxWrapper{transport}, nil
}
return transport, nil
}
var _ Plugin = (*v2rayMuxWrapper)(nil)
type v2rayMuxWrapper struct {
adapter.V2RayClientTransport
}
func (w *v2rayMuxWrapper) DialContext(ctx context.Context) (net.Conn, error) {
conn, err := w.V2RayClientTransport.DialContext(ctx)
if err != nil {
return nil, err
}
return vmess.NewMuxConnWrapper(conn, vmess.MuxDestination), nil
}

View File

@@ -4,6 +4,7 @@ import (
"context"
"net"
"os"
"strings"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/tls"
@@ -13,6 +14,8 @@ import (
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
gM "google.golang.org/grpc/metadata"
"google.golang.org/grpc/peer"
)
var _ adapter.V2RayServerTransport = (*Server)(nil)
@@ -41,7 +44,22 @@ func NewServer(ctx context.Context, options option.V2RayGRPCOptions, tlsConfig t
func (s *Server) Tun(server GunService_TunServer) error {
ctx, cancel := context.WithCancel(s.ctx)
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()
return nil
}

View File

@@ -25,8 +25,9 @@ type Client struct {
serverAddr M.Socksaddr
tlsConfig *tls.STDConfig
quicConfig *quic.Config
conn quic.Connection
connAccess sync.Mutex
conn quic.Connection
rawConn net.Conn
}
func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, options option.V2RayQUICOptions, tlsConfig tls.Config) (adapter.V2RayClientTransport, error) {
@@ -64,7 +65,6 @@ func (c *Client) offer() (quic.Connection, error) {
if err != nil {
return nil, err
}
c.conn = conn
return conn, nil
}
@@ -80,6 +80,8 @@ func (c *Client) offerNew() (quic.Connection, error) {
packetConn.Close()
return nil, err
}
c.conn = quicConn
c.rawConn = udpConn
return quicConn, nil
}
@@ -94,3 +96,7 @@ func (c *Client) DialContext(ctx context.Context) (net.Conn, error) {
}
return &hysteria.StreamWrapper{Conn: conn, Stream: stream}, nil
}
func (c *Client) Close() error {
return common.Close(c.conn, c.rawConn)
}

View File

@@ -74,7 +74,7 @@ func (c *Client) DialContext(ctx context.Context) (net.Conn, error) {
if c.maxEarlyData <= 0 {
conn, response, err := c.dialer.DialContext(ctx, c.uri, c.headers)
if err == nil {
return &WebsocketConn{Conn: conn}, nil
return &WebsocketConn{Conn: conn, Writer: &Writer{conn, false}}, nil
}
return nil, wrapDialError(response, err)
} else {

View File

@@ -10,16 +10,26 @@ import (
"time"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing/common/buf"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/websocket"
)
type WebsocketConn struct {
*websocket.Conn
*Writer
remoteAddr net.Addr
reader io.Reader
}
func NewServerConn(wsConn *websocket.Conn, remoteAddr net.Addr) *WebsocketConn {
return &WebsocketConn{
Conn: wsConn,
remoteAddr: remoteAddr,
Writer: &Writer{wsConn, true},
}
}
func (c *WebsocketConn) Close() error {
err := c.WriteControl(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""), time.Now().Add(C.TCPTimeout))
if err != nil {
@@ -47,14 +57,6 @@ func (c *WebsocketConn) Read(b []byte) (n int, err error) {
}
}
func (c *WebsocketConn) Write(b []byte) (n int, err error) {
err = wrapError(c.WriteMessage(websocket.BinaryMessage, b))
if err != nil {
return
}
return len(b), nil
}
func (c *WebsocketConn) RemoteAddr() net.Addr {
if c.remoteAddr != nil {
return c.remoteAddr
@@ -66,6 +68,10 @@ func (c *WebsocketConn) SetDeadline(t time.Time) error {
return os.ErrInvalid
}
func (c *WebsocketConn) FrontHeadroom() int {
return frontHeadroom
}
type EarlyWebsocketConn struct {
*Client
ctx context.Context
@@ -90,9 +96,9 @@ func (c *EarlyWebsocketConn) Write(b []byte) (n int, err error) {
conn *websocket.Conn
response *http.Response
)
if len(earlyData) > int(c.maxEarlyData) {
earlyData = earlyData[:c.maxEarlyData]
lateData = lateData[c.maxEarlyData:]
if len(b) > int(c.maxEarlyData) {
earlyData = b[:c.maxEarlyData]
lateData = b[c.maxEarlyData:]
} else {
earlyData = b
}
@@ -111,7 +117,7 @@ func (c *EarlyWebsocketConn) Write(b []byte) (n int, err error) {
if err != nil {
return 0, wrapDialError(response, err)
}
c.conn = &WebsocketConn{Conn: conn}
c.conn = &WebsocketConn{Conn: conn, Writer: &Writer{conn, false}}
close(c.create)
if len(lateData) > 0 {
_, err = c.conn.Write(lateData)
@@ -122,6 +128,46 @@ func (c *EarlyWebsocketConn) Write(b []byte) (n int, err error) {
return len(b), nil
}
func (c *EarlyWebsocketConn) WriteBuffer(buffer *buf.Buffer) error {
if c.conn != nil {
return c.conn.WriteBuffer(buffer)
}
var (
earlyData []byte
lateData []byte
conn *websocket.Conn
response *http.Response
err error
)
if buffer.Len() > int(c.maxEarlyData) {
earlyData = buffer.Bytes()[:c.maxEarlyData]
lateData = buffer.Bytes()[c.maxEarlyData:]
} else {
earlyData = buffer.Bytes()
}
if len(earlyData) > 0 {
earlyDataString := base64.RawURLEncoding.EncodeToString(earlyData)
if c.earlyDataHeaderName == "" {
conn, response, err = c.dialer.DialContext(c.ctx, c.uri+earlyDataString, c.headers)
} else {
headers := c.headers.Clone()
headers.Set(c.earlyDataHeaderName, earlyDataString)
conn, response, err = c.dialer.DialContext(c.ctx, c.uri, headers)
}
} else {
conn, response, err = c.dialer.DialContext(c.ctx, c.uri, c.headers)
}
if err != nil {
return wrapDialError(response, err)
}
c.conn = &WebsocketConn{Conn: conn, Writer: &Writer{conn, false}}
close(c.create)
if len(lateData) > 0 {
_, err = c.conn.Write(lateData)
}
return err
}
func (c *EarlyWebsocketConn) Close() error {
if c.conn == nil {
return nil
@@ -164,6 +210,10 @@ func (c *EarlyWebsocketConn) SetWriteDeadline(t time.Time) error {
return c.conn.SetWriteDeadline(t)
}
func (c *EarlyWebsocketConn) FrontHeadroom() int {
return frontHeadroom
}
func wrapError(err error) error {
if websocket.IsCloseError(err, websocket.CloseNormalClosure) {
return io.EOF

View File

@@ -0,0 +1,6 @@
package v2raywebsocket
import _ "unsafe"
//go:linkname maskBytes github.com/sagernet/websocket.maskBytes
func maskBytes(key [4]byte, pos int, b []byte) int

View File

@@ -108,10 +108,7 @@ func (s *Server) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
}
var metadata M.Metadata
metadata.Source = sHttp.SourceAddress(request)
conn = &WebsocketConn{
Conn: wsConn,
remoteAddr: metadata.Source.TCPAddr(),
}
conn = NewServerConn(wsConn, metadata.Source.TCPAddr())
if len(earlyData) > 0 {
conn = bufio.NewCachedConn(conn, buf.As(earlyData))
}

View File

@@ -0,0 +1,73 @@
package v2raywebsocket
import (
"encoding/binary"
"math/rand"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/buf"
"github.com/sagernet/websocket"
)
const frontHeadroom = 14
type Writer struct {
*websocket.Conn
isServer bool
}
func (w *Writer) Write(p []byte) (n int, err error) {
err = w.Conn.WriteMessage(websocket.BinaryMessage, p)
if err != nil {
return
}
return len(p), nil
}
func (w *Writer) WriteBuffer(buffer *buf.Buffer) error {
defer buffer.Release()
var payloadBitLength int
dataLen := buffer.Len()
data := buffer.Bytes()
if dataLen < 126 {
payloadBitLength = 1
} else if dataLen < 65536 {
payloadBitLength = 3
} else {
payloadBitLength = 9
}
var headerLen int
headerLen += 1 // FIN / RSV / OPCODE
headerLen += payloadBitLength
if !w.isServer {
headerLen += 4 // MASK KEY
}
header := buffer.ExtendHeader(headerLen)
header[0] = websocket.BinaryMessage | 1<<7
if w.isServer {
header[1] = 0
} else {
header[1] = 1 << 7
}
if dataLen < 126 {
header[1] |= byte(dataLen)
} else if dataLen < 65536 {
header[1] |= 126
binary.BigEndian.PutUint16(header[2:], uint16(dataLen))
} else {
header[1] |= 127
binary.BigEndian.PutUint64(header[2:], uint64(dataLen))
}
if !w.isServer {
maskKey := rand.Uint32()
binary.BigEndian.PutUint32(header[1+payloadBitLength:], maskKey)
maskBytes(*(*[4]byte)(header[1+payloadBitLength:]), 0, data)
}
return common.Error(w.Conn.NetConn().Write(buffer.Bytes()))
}

View File

@@ -1,144 +0,0 @@
package vless
import (
"encoding/binary"
"io"
"net"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/buf"
M "github.com/sagernet/sing/common/metadata"
"github.com/gofrs/uuid"
)
type Client struct {
key []byte
}
func NewClient(userId string) (*Client, error) {
user := uuid.FromStringOrNil(userId)
if user == uuid.Nil {
user = uuid.NewV5(user, userId)
}
return &Client{key: user.Bytes()}, nil
}
func (c *Client) DialEarlyConn(conn net.Conn, destination M.Socksaddr) *Conn {
return &Conn{Conn: conn, key: c.key, destination: destination}
}
func (c *Client) DialPacketConn(conn net.Conn, destination M.Socksaddr) *PacketConn {
return &PacketConn{Conn: conn, key: c.key, destination: destination}
}
func (c *Client) DialXUDPPacketConn(conn net.Conn, destination M.Socksaddr) *XUDPConn {
return &XUDPConn{Conn: conn, key: c.key, destination: destination}
}
type Conn struct {
net.Conn
key []byte
destination M.Socksaddr
requestWritten bool
responseRead bool
}
func (c *Conn) Read(b []byte) (n int, err error) {
if !c.responseRead {
err = ReadResponse(c.Conn)
if err != nil {
return
}
c.responseRead = true
}
return c.Conn.Read(b)
}
func (c *Conn) Write(b []byte) (n int, err error) {
if !c.requestWritten {
err = WriteRequest(c.Conn, Request{c.key, CommandTCP, c.destination}, b)
if err == nil {
n = len(b)
}
c.requestWritten = true
return
}
return c.Conn.Write(b)
}
func (c *Conn) Upstream() any {
return c.Conn
}
type PacketConn struct {
net.Conn
key []byte
destination M.Socksaddr
requestWritten bool
responseRead bool
}
func (c *PacketConn) Read(b []byte) (n int, err error) {
if !c.responseRead {
err = ReadResponse(c.Conn)
if err != nil {
return
}
c.responseRead = true
}
var length uint16
err = binary.Read(c.Conn, binary.BigEndian, &length)
if err != nil {
return
}
if cap(b) < int(length) {
return 0, io.ErrShortBuffer
}
return io.ReadFull(c.Conn, b[:length])
}
func (c *PacketConn) Write(b []byte) (n int, err error) {
if !c.requestWritten {
err = WritePacketRequest(c.Conn, Request{c.key, CommandUDP, c.destination}, b)
if err == nil {
n = len(b)
}
c.requestWritten = true
return
}
err = binary.Write(c.Conn, binary.BigEndian, uint16(len(b)))
if err != nil {
return
}
return c.Conn.Write(b)
}
func (c *PacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
defer buffer.Release()
dataLen := buffer.Len()
binary.BigEndian.PutUint16(buffer.ExtendHeader(2), uint16(dataLen))
if !c.requestWritten {
err := WritePacketRequest(c.Conn, Request{c.key, CommandUDP, c.destination}, buffer.Bytes())
c.requestWritten = true
return err
}
return common.Error(c.Conn.Write(buffer.Bytes()))
}
func (c *PacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
n, err = c.Read(p)
return
}
func (c *PacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
return c.Write(p)
}
func (c *PacketConn) FrontHeadroom() int {
return 2
}
func (c *PacketConn) Upstream() any {
return c.Conn
}

View File

@@ -1,104 +0,0 @@
package vless
import (
"encoding/binary"
"io"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/buf"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
"github.com/sagernet/sing/common/rw"
)
const (
Version = 0
CommandTCP = 1
CommandUDP = 2
CommandMux = 3
NetworkUDP = 2
)
var AddressSerializer = M.NewSerializer(
M.AddressFamilyByte(0x01, M.AddressFamilyIPv4),
M.AddressFamilyByte(0x02, M.AddressFamilyFqdn),
M.AddressFamilyByte(0x03, M.AddressFamilyIPv6),
M.PortThenAddress(),
)
type Request struct {
UUID []byte
Command byte
Destination M.Socksaddr
}
func WriteRequest(writer io.Writer, request Request, payload []byte) error {
var requestLen int
requestLen += 1 // version
requestLen += 16 // uuid
requestLen += 1 // protobuf length
requestLen += 1 // command
requestLen += AddressSerializer.AddrPortLen(request.Destination)
requestLen += len(payload)
_buffer := buf.StackNewSize(requestLen)
defer common.KeepAlive(_buffer)
buffer := common.Dup(_buffer)
defer buffer.Release()
common.Must(
buffer.WriteByte(Version),
common.Error(buffer.Write(request.UUID)),
buffer.WriteByte(0),
buffer.WriteByte(CommandTCP),
AddressSerializer.WriteAddrPort(buffer, request.Destination),
common.Error(buffer.Write(payload)),
)
return common.Error(writer.Write(buffer.Bytes()))
}
func WritePacketRequest(writer io.Writer, request Request, payload []byte) error {
var requestLen int
requestLen += 1 // version
requestLen += 16 // uuid
requestLen += 1 // protobuf length
requestLen += 1 // command
requestLen += AddressSerializer.AddrPortLen(request.Destination)
if len(payload) > 0 {
requestLen += 2
requestLen += len(payload)
}
_buffer := buf.StackNewSize(requestLen)
defer common.KeepAlive(_buffer)
buffer := common.Dup(_buffer)
defer buffer.Release()
common.Must(
buffer.WriteByte(Version),
common.Error(buffer.Write(request.UUID)),
buffer.WriteByte(0),
buffer.WriteByte(CommandUDP),
AddressSerializer.WriteAddrPort(buffer, request.Destination),
binary.Write(buffer, binary.BigEndian, uint16(len(payload))),
common.Error(buffer.Write(payload)),
)
return common.Error(writer.Write(buffer.Bytes()))
}
func ReadResponse(reader io.Reader) error {
version, err := rw.ReadByte(reader)
if err != nil {
return err
}
if version != Version {
return E.New("unknown version: ", version)
}
protobufLength, err := rw.ReadByte(reader)
if err != nil {
return err
}
if protobufLength > 0 {
err = rw.SkipN(reader, int(protobufLength))
if err != nil {
return err
}
}
return nil
}

View File

@@ -1,174 +0,0 @@
package vless
import (
"encoding/binary"
"io"
"net"
"os"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/buf"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
)
type XUDPConn struct {
net.Conn
key []byte
destination M.Socksaddr
requestWritten bool
responseRead bool
}
func (c *XUDPConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
return 0, nil, os.ErrInvalid
}
func (c *XUDPConn) ReadPacket(buffer *buf.Buffer) (destination M.Socksaddr, err error) {
start := buffer.Start()
if !c.responseRead {
err = ReadResponse(c.Conn)
if err != nil {
return
}
c.responseRead = true
buffer.FullReset()
}
_, err = buffer.ReadFullFrom(c.Conn, 6)
if err != nil {
return
}
var length uint16
err = binary.Read(buffer, binary.BigEndian, &length)
if err != nil {
return
}
header, err := buffer.ReadBytes(4)
if err != nil {
return
}
switch header[2] {
case 1:
// frame new
return M.Socksaddr{}, E.New("unexpected frame new")
case 2:
// frame keep
if length != 4 {
_, err = buffer.ReadFullFrom(c.Conn, int(length)-2)
if err != nil {
return
}
buffer.Advance(1)
destination, err = AddressSerializer.ReadAddrPort(buffer)
if err != nil {
return
}
} else {
_, err = buffer.ReadFullFrom(c.Conn, 2)
if err != nil {
return
}
destination = c.destination
}
case 3:
// frame end
return M.Socksaddr{}, io.EOF
case 4:
// frame keep alive
default:
return M.Socksaddr{}, E.New("unexpected frame: ", buffer.Byte(2))
}
// option error
if header[3]&2 == 2 {
return M.Socksaddr{}, E.Cause(net.ErrClosed, "remote closed")
}
// option data
if header[3]&1 != 1 {
buffer.Resize(start, 0)
return c.ReadPacket(buffer)
} else {
err = binary.Read(buffer, binary.BigEndian, &length)
if err != nil {
return
}
buffer.Resize(start, 0)
_, err = buffer.ReadFullFrom(c.Conn, int(length))
return
}
}
func (c *XUDPConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
destination := M.SocksaddrFromNet(addr)
headerLen := c.frontHeadroom(AddressSerializer.AddrPortLen(destination))
buffer := buf.NewSize(headerLen + len(p))
buffer.Advance(headerLen)
common.Must1(buffer.Write(p))
err = c.WritePacket(buffer, destination)
if err == nil {
n = len(p)
}
return
}
func (c *XUDPConn) frontHeadroom(addrLen int) int {
if !c.requestWritten {
var headerLen int
headerLen += 1 // version
headerLen += 16 // uuid
headerLen += 1 // protobuf length
headerLen += 1 // command
headerLen += 2 // frame len
headerLen += 5 // frame header
headerLen += addrLen
headerLen += 2 // payload len
return headerLen
} else {
return 7 + addrLen + 2
}
}
func (c *XUDPConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
defer buffer.Release()
dataLen := buffer.Len()
addrLen := M.SocksaddrSerializer.AddrPortLen(destination)
if !c.requestWritten {
header := buf.With(buffer.ExtendHeader(c.frontHeadroom(addrLen)))
common.Must(
header.WriteByte(Version),
common.Error(header.Write(c.key)),
header.WriteByte(0),
header.WriteByte(CommandMux),
binary.Write(header, binary.BigEndian, uint16(5+addrLen)),
header.WriteByte(0),
header.WriteByte(0),
header.WriteByte(1), // frame type new
header.WriteByte(1), // option data
header.WriteByte(NetworkUDP),
AddressSerializer.WriteAddrPort(header, destination),
binary.Write(header, binary.BigEndian, uint16(dataLen)),
)
c.requestWritten = true
} else {
header := buffer.ExtendHeader(c.frontHeadroom(addrLen))
binary.BigEndian.PutUint16(header, uint16(5+addrLen))
header[2] = 0
header[3] = 0
header[4] = 2 // frame keep
header[5] = 1 // option data
header[6] = NetworkUDP
err := AddressSerializer.WriteAddrPort(buf.With(header[7:]), destination)
if err != nil {
return err
}
binary.BigEndian.PutUint16(header[7+addrLen:], uint16(dataLen))
}
return common.Error(c.Conn.Write(buffer.Bytes()))
}
func (c *XUDPConn) FrontHeadroom() int {
return c.frontHeadroom(M.MaxSocksaddrLength)
}
func (c *XUDPConn) Upstream() any {
return c.Conn
}

View File

@@ -20,6 +20,7 @@ type ClientBind struct {
peerAddr M.Socksaddr
connAccess sync.Mutex
conn *wireConn
done chan struct{}
}
func NewClientBind(ctx context.Context, dialer N.Dialer, peerAddr M.Socksaddr) *ClientBind {
@@ -63,6 +64,12 @@ func (c *ClientBind) connect() (*wireConn, error) {
}
func (c *ClientBind) Open(port uint16) (fns []conn.ReceiveFunc, actualPort uint16, err error) {
select {
case <-c.done:
err = net.ErrClosed
return
default:
}
return []conn.ReceiveFunc{c.receive}, 0, nil
}
@@ -75,7 +82,12 @@ func (c *ClientBind) receive(b []byte) (n int, ep conn.Endpoint, err error) {
n, err = udpConn.Read(b)
if err != nil {
udpConn.Close()
err = &wireError{err}
select {
case <-c.done:
default:
err = &wireError{err}
}
return
}
ep = Endpoint(c.peerAddr)
return
@@ -85,6 +97,16 @@ func (c *ClientBind) Close() error {
c.connAccess.Lock()
defer c.connAccess.Unlock()
common.Close(common.PtrOrNil(c.conn))
if c.done == nil {
c.done = make(chan struct{})
return nil
}
select {
case <-c.done:
return net.ErrClosed
default:
close(c.done)
}
return nil
}

View File

@@ -35,7 +35,6 @@ type StackDevice struct {
events chan tun.Event
outbound chan *stack.PacketBuffer
dispatcher stack.NetworkDispatcher
done chan struct{}
addr4 tcpip.Address
addr6 tcpip.Address
}
@@ -51,7 +50,6 @@ func NewStackDevice(localAddresses []netip.Prefix, mtu uint32) (*StackDevice, er
mtu: mtu,
events: make(chan tun.Event, 1),
outbound: make(chan *stack.PacketBuffer, 256),
done: make(chan struct{}),
}
err := ipStack.CreateNIC(defaultNIC, (*wireEndpoint)(tunDevice))
if err != nil {
@@ -193,11 +191,11 @@ func (w *StackDevice) Events() chan tun.Event {
func (w *StackDevice) Close() error {
select {
case <-w.done:
case <-w.events:
return os.ErrClosed
default:
close(w.events)
}
close(w.done)
w.stack.Close()
for _, endpoint := range w.stack.CleanupEndpoints() {
endpoint.Abort()

View File

@@ -106,5 +106,11 @@ func (w *SystemDevice) Events() chan wgTun.Event {
}
func (w *SystemDevice) Close() error {
select {
case <-w.events:
return os.ErrClosed
default:
close(w.events)
}
return w.device.Close()
}