Skip to content

Commit

Permalink
Add shadowtls v2 support
Browse files Browse the repository at this point in the history
  • Loading branch information
nekohasekai committed Oct 6, 2022
1 parent d135d0f commit b0ad9bb
Show file tree
Hide file tree
Showing 11 changed files with 412 additions and 55 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ 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-20221001030341-348376220066
github.com/sagernet/sing v0.0.0-20221006081821-c4e9bf11fa00
github.com/sagernet/sing-dns v0.0.0-20220929010544-ee843807aae3
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6
github.com/sagernet/sing-tun v0.0.0-20221005115555-9a556307f6a3
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,8 @@ 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-20221001030341-348376220066 h1:kQSd+x9ZLBcjl2+VjCLlkxzlD8VO5Q9X3FHDb7OGoN4=
github.com/sagernet/sing v0.0.0-20221001030341-348376220066/go.mod h1:zvgDYKI+vCAW9RyfyrKTgleI+DOa8lzHMPC7VZo3OL4=
github.com/sagernet/sing v0.0.0-20221006081821-c4e9bf11fa00 h1:UkgEDnH3L9eBxob+3AbE9wM4mjFnRLnaPjfLSNe+C74=
github.com/sagernet/sing v0.0.0-20221006081821-c4e9bf11fa00/go.mod h1:zvgDYKI+vCAW9RyfyrKTgleI+DOa8lzHMPC7VZo3OL4=
github.com/sagernet/sing-dns v0.0.0-20220929010544-ee843807aae3 h1:AEdyJxEDFq38z0pBX/0MpikQapGMIch+1ADe9k1bJqU=
github.com/sagernet/sing-dns v0.0.0-20220929010544-ee843807aae3/go.mod h1:SrvWLfOSlnFmH32CWXicfilAGgIXR0VjrH6yRbuXYww=
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6 h1:JJfDeYYhWunvtxsU/mOVNTmFQmnzGx9dY034qG6G3g4=
Expand Down
97 changes: 85 additions & 12 deletions inbound/shadowtls.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,17 @@ import (
"encoding/binary"
"io"
"net"
"os"

"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/dialer"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-box/transport/shadowtls"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/buf"
"github.com/sagernet/sing/common/bufio"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
Expand All @@ -22,6 +27,8 @@ type ShadowTLS struct {
myInboundAdapter
handshakeDialer N.Dialer
handshakeAddr M.Socksaddr
v2 bool
password string
}

func NewShadowTLS(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowTLSInboundOptions) (*ShadowTLS, error) {
Expand All @@ -37,6 +44,16 @@ func NewShadowTLS(ctx context.Context, router adapter.Router, logger log.Context
},
handshakeDialer: dialer.New(router, options.Handshake.DialerOptions),
handshakeAddr: options.Handshake.ServerOptions.Build(),
password: options.Password,
}
switch options.Version {
case 0:
fallthrough
case 1:
case 2:
inbound.v2 = true
default:
return nil, E.New("unknown shadowtls protocol version: ", options.Version)
}
inbound.connHandler = inbound
return inbound, nil
Expand All @@ -47,19 +64,39 @@ func (s *ShadowTLS) NewConnection(ctx context.Context, conn net.Conn, metadata a
if err != nil {
return err
}
var handshake task.Group
handshake.Append("client handshake", func(ctx context.Context) error {
return s.copyUntilHandshakeFinished(handshakeConn, conn)
})
handshake.Append("server handshake", func(ctx context.Context) error {
return s.copyUntilHandshakeFinished(conn, handshakeConn)
})
handshake.FastFail()
err = handshake.Run(ctx)
if err != nil {
return err
if !s.v2 {
var handshake task.Group
handshake.Append("client handshake", func(ctx context.Context) error {
return s.copyUntilHandshakeFinished(handshakeConn, conn)
})
handshake.Append("server handshake", func(ctx context.Context) error {
return s.copyUntilHandshakeFinished(conn, handshakeConn)
})
handshake.FastFail()
handshake.Cleanup(func() {
handshakeConn.Close()
})
err = handshake.Run(ctx)
if err != nil {
return err
}
return s.newConnection(ctx, conn, metadata)
} else {
hashConn := shadowtls.NewHashWriteConn(conn, s.password)
go bufio.Copy(hashConn, handshakeConn)
var request *buf.Buffer
request, err = s.copyUntilHandshakeFinishedV2(handshakeConn, conn, hashConn)
if err == nil {
handshakeConn.Close()
return s.newConnection(ctx, bufio.NewCachedConn(shadowtls.NewConn(conn), request), metadata)
} else if err == os.ErrPermission {
s.logger.WarnContext(ctx, "fallback connection")
hashConn.Fallback()
return common.Error(bufio.Copy(handshakeConn, conn))
} else {
return err
}
}
return s.newConnection(ctx, conn, metadata)
}

func (s *ShadowTLS) copyUntilHandshakeFinished(dst io.Writer, src io.Reader) error {
Expand Down Expand Up @@ -91,3 +128,39 @@ func (s *ShadowTLS) copyUntilHandshakeFinished(dst io.Writer, src io.Reader) err
}
}
}

func (s *ShadowTLS) copyUntilHandshakeFinishedV2(dst net.Conn, src io.Reader, hash *shadowtls.HashWriteConn) (*buf.Buffer, error) {
const applicationData = 0x17
var tlsHdr [5]byte
var applicationDataCount int
for {
_, err := io.ReadFull(src, tlsHdr[:])
if err != nil {
return nil, err
}
length := binary.BigEndian.Uint16(tlsHdr[3:])
if tlsHdr[0] == applicationData {
data := buf.NewSize(int(length))
_, err = data.ReadFullFrom(src, int(length))
if err != nil {
data.Release()
return nil, err
}
if length >= 8 && bytes.Equal(data.To(8), hash.Sum()) {
data.Advance(8)
return data, nil
}
_, err = io.Copy(dst, io.MultiReader(bytes.NewReader(tlsHdr[:]), data))
data.Release()
applicationDataCount++
} else {
_, err = io.Copy(dst, io.MultiReader(bytes.NewReader(tlsHdr[:]), io.LimitReader(src, int64(length))))
}
if err != nil {
return nil, err
}
if applicationDataCount > 3 {
return nil, os.ErrPermission
}
}
}
6 changes: 5 additions & 1 deletion option/shadowtls.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package option

type ShadowTLSInboundOptions struct {
ListenOptions
Version int `json:"version,omitempty"`
Password string `json:"password,omitempty"`
Handshake ShadowTLSHandshakeOptions `json:"handshake"`
}

Expand All @@ -13,5 +15,7 @@ type ShadowTLSHandshakeOptions struct {
type ShadowTLSOutboundOptions struct {
DialerOptions
ServerOptions
TLS *OutboundTLSOptions `json:"tls,omitempty"`
Version int `json:"version,omitempty"`
Password string `json:"password,omitempty"`
TLS *OutboundTLSOptions `json:"tls,omitempty"`
}
35 changes: 29 additions & 6 deletions outbound/shadowtls.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ import (
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-box/transport/shadowtls"
"github.com/sagernet/sing/common"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
)
Expand All @@ -23,6 +25,8 @@ type ShadowTLS struct {
dialer N.Dialer
serverAddr M.Socksaddr
tlsConfig tls.Config
v2 bool
password string
}

func NewShadowTLS(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowTLSOutboundOptions) (*ShadowTLS, error) {
Expand All @@ -36,12 +40,22 @@ func NewShadowTLS(ctx context.Context, router adapter.Router, logger log.Context
},
dialer: dialer.New(router, options.DialerOptions),
serverAddr: options.ServerOptions.Build(),
password: options.Password,
}
if options.TLS == nil || !options.TLS.Enabled {
return nil, C.ErrTLSRequired
}
options.TLS.MinVersion = "1.2"
options.TLS.MaxVersion = "1.2"
switch options.Version {
case 0:
fallthrough
case 1:
options.TLS.MinVersion = "1.2"
options.TLS.MaxVersion = "1.2"
case 2:
outbound.v2 = true
default:
return nil, E.New("unknown shadowtls protocol version: ", options.Version)
}
var err error
outbound.tlsConfig, err = tls.NewClient(router, options.Server, common.PtrValueOrDefault(options.TLS))
if err != nil {
Expand All @@ -60,11 +74,20 @@ func (s *ShadowTLS) DialContext(ctx context.Context, network string, destination
if err != nil {
return nil, err
}
_, err = tls.ClientHandshake(ctx, conn, s.tlsConfig)
if err != nil {
return nil, err
if !s.v2 {
_, err = tls.ClientHandshake(ctx, conn, s.tlsConfig)
if err != nil {
return nil, err
}
return conn, nil
} else {
hashConn := shadowtls.NewHashReadConn(conn, s.password)
_, err = tls.ClientHandshake(ctx, hashConn, s.tlsConfig)
if err != nil {
return nil, err
}
return shadowtls.NewClientConn(hashConn), nil
}
return conn, nil
}

func (s *ShadowTLS) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
Expand Down
16 changes: 8 additions & 8 deletions test/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ 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-20220930130214-cb9b17d6a4a7
github.com/sagernet/sing v0.0.0-20221006081821-c4e9bf11fa00
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-20220927171203-f486391704dc
golang.org/x/net v0.0.0-20221004154528-8021a29435af
)

//replace github.com/sagernet/sing => ../../sing
Expand All @@ -26,10 +26,10 @@ require (
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
github.com/caddyserver/certmagic v0.17.1 // indirect
github.com/caddyserver/certmagic v0.17.2 // indirect
github.com/cloudflare/circl v1.2.1-0.20220831060716-4cf0150356fc // indirect
github.com/cretz/bine v0.2.0 // indirect
github.com/database64128/tfo-go v1.1.2 // indirect
github.com/database64128/tfo-go/v2 v2.0.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/docker/distribution v2.8.1+incompatible // indirect
github.com/docker/go-units v0.4.0 // indirect
Expand All @@ -44,7 +44,7 @@ require (
github.com/google/btree v1.0.1 // indirect
github.com/hashicorp/yamux v0.1.1 // indirect
github.com/klauspost/compress v1.13.6 // indirect
github.com/klauspost/cpuid/v2 v2.1.0 // indirect
github.com/klauspost/cpuid/v2 v2.1.1 // indirect
github.com/libdns/libdns v0.2.1 // indirect
github.com/logrusorgru/aurora v2.0.3+incompatible // indirect
github.com/marten-seemann/qpack v0.2.1 // indirect
Expand All @@ -68,7 +68,7 @@ require (
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect
github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb // indirect
github.com/sagernet/sing-dns v0.0.0-20220929010544-ee843807aae3 // indirect
github.com/sagernet/sing-tun v0.0.0-20220929163559-a93592a9b581 // indirect
github.com/sagernet/sing-tun v0.0.0-20221005115555-9a556307f6a3 // indirect
github.com/sagernet/sing-vmess v0.0.0-20220925083655-063bc85ea685 // indirect
github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195 // indirect
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e // indirect
Expand All @@ -77,9 +77,9 @@ require (
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
go.uber.org/zap v1.23.0 // indirect
go4.org/netipx v0.0.0-20220925034521-797b0c90d8ab // indirect
golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be // indirect
golang.org/x/crypto v0.0.0-20221005025214-4161e89ecf1b // 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-20220928140112-f11e5e49a4ec // indirect
Expand Down
Loading

0 comments on commit b0ad9bb

Please sign in to comment.