mirror of
https://github.com/SagerNet/sing-box.git
synced 2024-11-25 18:11:28 +00:00
Add custom TLS server support for http based v2ray transports
This commit is contained in:
parent
f15f525c5c
commit
ed50257735
|
@ -8,7 +8,6 @@ import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/sagernet/reality"
|
"github.com/sagernet/reality"
|
||||||
|
@ -135,7 +134,7 @@ func (c *RealityServerConfig) Config() (*tls.Config, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *RealityServerConfig) Client(conn net.Conn) (Conn, error) {
|
func (c *RealityServerConfig) Client(conn net.Conn) (Conn, error) {
|
||||||
return nil, os.ErrInvalid
|
return ClientHandshake(context.Background(), conn, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *RealityServerConfig) Start() error {
|
func (c *RealityServerConfig) Start() error {
|
||||||
|
@ -147,7 +146,7 @@ func (c *RealityServerConfig) Close() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *RealityServerConfig) Server(conn net.Conn) (Conn, error) {
|
func (c *RealityServerConfig) Server(conn net.Conn) (Conn, error) {
|
||||||
return nil, os.ErrInvalid
|
return ServerHandshake(context.Background(), conn, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *RealityServerConfig) ServerHandshake(ctx context.Context, conn net.Conn) (Conn, error) {
|
func (c *RealityServerConfig) ServerHandshake(ctx context.Context, conn net.Conn) (Conn, error) {
|
||||||
|
|
6
go.mod
6
go.mod
|
@ -20,11 +20,13 @@ require (
|
||||||
github.com/miekg/dns v1.1.50
|
github.com/miekg/dns v1.1.50
|
||||||
github.com/oschwald/maxminddb-golang v1.10.0
|
github.com/oschwald/maxminddb-golang v1.10.0
|
||||||
github.com/pires/go-proxyproto v0.6.2
|
github.com/pires/go-proxyproto v0.6.2
|
||||||
|
github.com/sagernet/badhttp v0.0.0-20230228035330-e77eb9a689fd
|
||||||
|
github.com/sagernet/badhttp2 v0.0.0-20230228040529-408b0b8e774d
|
||||||
github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0
|
github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0
|
||||||
github.com/sagernet/gomobile v0.0.0-20221130124640-349ebaa752ca
|
github.com/sagernet/gomobile v0.0.0-20221130124640-349ebaa752ca
|
||||||
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32
|
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32
|
||||||
github.com/sagernet/reality v0.0.0-20230226124550-f98d51fa21b5
|
github.com/sagernet/reality v0.0.0-20230228045158-d3e085a8e5d1
|
||||||
github.com/sagernet/sing v0.1.8-0.20230228031050-b60f6390dfe8
|
github.com/sagernet/sing v0.1.8-0.20230228034829-bb617490652c
|
||||||
github.com/sagernet/sing-dns v0.1.4
|
github.com/sagernet/sing-dns v0.1.4
|
||||||
github.com/sagernet/sing-shadowsocks v0.1.2-0.20230221080503-769c01d6bba9
|
github.com/sagernet/sing-shadowsocks v0.1.2-0.20230221080503-769c01d6bba9
|
||||||
github.com/sagernet/sing-shadowtls v0.0.0-20230221123345-78e50cd7b587
|
github.com/sagernet/sing-shadowtls v0.0.0-20230221123345-78e50cd7b587
|
||||||
|
|
12
go.sum
12
go.sum
|
@ -115,6 +115,10 @@ github.com/quic-go/qtls-go1-19 v0.2.0/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05
|
||||||
github.com/quic-go/qtls-go1-20 v0.1.0 h1:d1PK3ErFy9t7zxKsG3NXBJXZjp/kMLoIb3y/kV54oAI=
|
github.com/quic-go/qtls-go1-20 v0.1.0 h1:d1PK3ErFy9t7zxKsG3NXBJXZjp/kMLoIb3y/kV54oAI=
|
||||||
github.com/quic-go/qtls-go1-20 v0.1.0/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM=
|
github.com/quic-go/qtls-go1-20 v0.1.0/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
|
github.com/sagernet/badhttp v0.0.0-20230228035330-e77eb9a689fd h1:nv3WtVfPGX+i2Ip/TR+Yd3LO1xFSpKUgWmYsXxKJ6vM=
|
||||||
|
github.com/sagernet/badhttp v0.0.0-20230228035330-e77eb9a689fd/go.mod h1:geEm+9ZyRMZ8THRH0XSexeStaMDtkFBf4J1nMK92mAY=
|
||||||
|
github.com/sagernet/badhttp2 v0.0.0-20230228040529-408b0b8e774d h1:RmBTGU4SvqxX57SDvpQtrkiQDaCnr4J/DMYMrUBL7OQ=
|
||||||
|
github.com/sagernet/badhttp2 v0.0.0-20230228040529-408b0b8e774d/go.mod h1:Ag8QdZjLwuy3V2pyOcqlKz4Cdh0wKEOFlYgR3wPUGkI=
|
||||||
github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0 h1:KyhtFFt1Jtp5vW2ohNvstvQffTOQ/s5vENuGXzdA+TM=
|
github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0 h1:KyhtFFt1Jtp5vW2ohNvstvQffTOQ/s5vENuGXzdA+TM=
|
||||||
github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0/go.mod h1:D4SFEOkJK+4W1v86ZhX0jPM0rAL498fyQAChqMtes/I=
|
github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0/go.mod h1:D4SFEOkJK+4W1v86ZhX0jPM0rAL498fyQAChqMtes/I=
|
||||||
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 h1:5+m7c6AkmAylhauulqN/c5dnh8/KssrE9c93TQrXldA=
|
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 h1:5+m7c6AkmAylhauulqN/c5dnh8/KssrE9c93TQrXldA=
|
||||||
|
@ -125,12 +129,12 @@ 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/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
|
||||||
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32 h1:tztuJB+giOWNRKQEBVY2oI3PsheTooMdh+/yxemYQYY=
|
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32 h1:tztuJB+giOWNRKQEBVY2oI3PsheTooMdh+/yxemYQYY=
|
||||||
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32/go.mod h1:QMCkxXAC3CvBgDZVIJp43NWTuwGBScCzMLVLynjERL8=
|
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32/go.mod h1:QMCkxXAC3CvBgDZVIJp43NWTuwGBScCzMLVLynjERL8=
|
||||||
github.com/sagernet/reality v0.0.0-20230226124550-f98d51fa21b5 h1:yDic66vLGsY3zqEyOyRj5tyGfHevLeNv/tXjHUWVzkE=
|
github.com/sagernet/reality v0.0.0-20230228045158-d3e085a8e5d1 h1:8mSzchN6DkM26JKLalPwj2KLMIsEjzlp/pYgznlKE2Q=
|
||||||
github.com/sagernet/reality v0.0.0-20230226124550-f98d51fa21b5/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
|
github.com/sagernet/reality v0.0.0-20230228045158-d3e085a8e5d1/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
|
||||||
github.com/sagernet/sing v0.0.0-20220812082120-05f9836bff8f/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
|
github.com/sagernet/sing v0.0.0-20220812082120-05f9836bff8f/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
|
||||||
github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
|
github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
|
||||||
github.com/sagernet/sing v0.1.8-0.20230228031050-b60f6390dfe8 h1:ZBb6CW6bFovBoW950v0eiitQKYEkB2GGot8tkVfu0gM=
|
github.com/sagernet/sing v0.1.8-0.20230228034829-bb617490652c h1:+YUwfoIkKlMi3Y1QrOy+OlIELC9KWV0+/5F3NX72q8U=
|
||||||
github.com/sagernet/sing v0.1.8-0.20230228031050-b60f6390dfe8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk=
|
github.com/sagernet/sing v0.1.8-0.20230228034829-bb617490652c/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk=
|
||||||
github.com/sagernet/sing-dns v0.1.4 h1:7VxgeoSCiiazDSaXXQVcvrTBxFpOePPq/4XdgnUDN+0=
|
github.com/sagernet/sing-dns v0.1.4 h1:7VxgeoSCiiazDSaXXQVcvrTBxFpOePPq/4XdgnUDN+0=
|
||||||
github.com/sagernet/sing-dns v0.1.4/go.mod h1:1+6pCa48B1AI78lD+/i/dLgpw4MwfnsSpZo0Ds8wzzk=
|
github.com/sagernet/sing-dns v0.1.4/go.mod h1:1+6pCa48B1AI78lD+/i/dLgpw4MwfnsSpZo0Ds8wzzk=
|
||||||
github.com/sagernet/sing-shadowsocks v0.1.2-0.20230221080503-769c01d6bba9 h1:qS39eA4C7x+zhEkySbASrtmb6ebdy5v0y2M6mgkmSO0=
|
github.com/sagernet/sing-shadowsocks v0.1.2-0.20230221080503-769c01d6bba9 h1:qS39eA4C7x+zhEkySbASrtmb6ebdy5v0y2M6mgkmSO0=
|
||||||
|
|
|
@ -10,7 +10,7 @@ require (
|
||||||
github.com/docker/docker v20.10.18+incompatible
|
github.com/docker/docker v20.10.18+incompatible
|
||||||
github.com/docker/go-connections v0.4.0
|
github.com/docker/go-connections v0.4.0
|
||||||
github.com/gofrs/uuid v4.4.0+incompatible
|
github.com/gofrs/uuid v4.4.0+incompatible
|
||||||
github.com/sagernet/sing v0.1.8-0.20230221060643-3401d210384b
|
github.com/sagernet/sing v0.1.8-0.20230228034829-bb617490652c
|
||||||
github.com/sagernet/sing-shadowsocks v0.1.2-0.20230221080503-769c01d6bba9
|
github.com/sagernet/sing-shadowsocks v0.1.2-0.20230221080503-769c01d6bba9
|
||||||
github.com/spyzhov/ajson v0.7.1
|
github.com/spyzhov/ajson v0.7.1
|
||||||
github.com/stretchr/testify v1.8.1
|
github.com/stretchr/testify v1.8.1
|
||||||
|
@ -63,11 +63,13 @@ require (
|
||||||
github.com/quic-go/qtls-go1-18 v0.2.0 // indirect
|
github.com/quic-go/qtls-go1-18 v0.2.0 // indirect
|
||||||
github.com/quic-go/qtls-go1-19 v0.2.0 // indirect
|
github.com/quic-go/qtls-go1-19 v0.2.0 // indirect
|
||||||
github.com/quic-go/qtls-go1-20 v0.1.0 // indirect
|
github.com/quic-go/qtls-go1-20 v0.1.0 // indirect
|
||||||
|
github.com/sagernet/badhttp v0.0.0-20230228035330-e77eb9a689fd // indirect
|
||||||
|
github.com/sagernet/badhttp2 v0.0.0-20230228040529-408b0b8e774d // indirect
|
||||||
github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0 // indirect
|
github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0 // indirect
|
||||||
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect
|
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect
|
||||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect
|
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect
|
||||||
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32 // indirect
|
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32 // indirect
|
||||||
github.com/sagernet/reality v0.0.0-20230226124550-f98d51fa21b5 // indirect
|
github.com/sagernet/reality v0.0.0-20230228045158-d3e085a8e5d1 // indirect
|
||||||
github.com/sagernet/sing-dns v0.1.4 // indirect
|
github.com/sagernet/sing-dns v0.1.4 // indirect
|
||||||
github.com/sagernet/sing-shadowtls v0.0.0-20230221123345-78e50cd7b587 // indirect
|
github.com/sagernet/sing-shadowtls v0.0.0-20230221123345-78e50cd7b587 // indirect
|
||||||
github.com/sagernet/sing-tun v0.1.2-0.20230226091124-0cdb0eed74d9 // indirect
|
github.com/sagernet/sing-tun v0.1.2-0.20230226091124-0cdb0eed74d9 // indirect
|
||||||
|
|
12
test/go.sum
12
test/go.sum
|
@ -130,6 +130,10 @@ github.com/quic-go/qtls-go1-19 v0.2.0 h1:Cvn2WdhyViFUHoOqK52i51k4nDX8EwIh5VJiVM4
|
||||||
github.com/quic-go/qtls-go1-19 v0.2.0/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI=
|
github.com/quic-go/qtls-go1-19 v0.2.0/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI=
|
||||||
github.com/quic-go/qtls-go1-20 v0.1.0 h1:d1PK3ErFy9t7zxKsG3NXBJXZjp/kMLoIb3y/kV54oAI=
|
github.com/quic-go/qtls-go1-20 v0.1.0 h1:d1PK3ErFy9t7zxKsG3NXBJXZjp/kMLoIb3y/kV54oAI=
|
||||||
github.com/quic-go/qtls-go1-20 v0.1.0/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM=
|
github.com/quic-go/qtls-go1-20 v0.1.0/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM=
|
||||||
|
github.com/sagernet/badhttp v0.0.0-20230228035330-e77eb9a689fd h1:nv3WtVfPGX+i2Ip/TR+Yd3LO1xFSpKUgWmYsXxKJ6vM=
|
||||||
|
github.com/sagernet/badhttp v0.0.0-20230228035330-e77eb9a689fd/go.mod h1:geEm+9ZyRMZ8THRH0XSexeStaMDtkFBf4J1nMK92mAY=
|
||||||
|
github.com/sagernet/badhttp2 v0.0.0-20230228040529-408b0b8e774d h1:RmBTGU4SvqxX57SDvpQtrkiQDaCnr4J/DMYMrUBL7OQ=
|
||||||
|
github.com/sagernet/badhttp2 v0.0.0-20230228040529-408b0b8e774d/go.mod h1:Ag8QdZjLwuy3V2pyOcqlKz4Cdh0wKEOFlYgR3wPUGkI=
|
||||||
github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0 h1:KyhtFFt1Jtp5vW2ohNvstvQffTOQ/s5vENuGXzdA+TM=
|
github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0 h1:KyhtFFt1Jtp5vW2ohNvstvQffTOQ/s5vENuGXzdA+TM=
|
||||||
github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0/go.mod h1:D4SFEOkJK+4W1v86ZhX0jPM0rAL498fyQAChqMtes/I=
|
github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0/go.mod h1:D4SFEOkJK+4W1v86ZhX0jPM0rAL498fyQAChqMtes/I=
|
||||||
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 h1:5+m7c6AkmAylhauulqN/c5dnh8/KssrE9c93TQrXldA=
|
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 h1:5+m7c6AkmAylhauulqN/c5dnh8/KssrE9c93TQrXldA=
|
||||||
|
@ -138,12 +142,12 @@ 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/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
|
||||||
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32 h1:tztuJB+giOWNRKQEBVY2oI3PsheTooMdh+/yxemYQYY=
|
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32 h1:tztuJB+giOWNRKQEBVY2oI3PsheTooMdh+/yxemYQYY=
|
||||||
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32/go.mod h1:QMCkxXAC3CvBgDZVIJp43NWTuwGBScCzMLVLynjERL8=
|
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32/go.mod h1:QMCkxXAC3CvBgDZVIJp43NWTuwGBScCzMLVLynjERL8=
|
||||||
github.com/sagernet/reality v0.0.0-20230226124550-f98d51fa21b5 h1:yDic66vLGsY3zqEyOyRj5tyGfHevLeNv/tXjHUWVzkE=
|
github.com/sagernet/reality v0.0.0-20230228045158-d3e085a8e5d1 h1:8mSzchN6DkM26JKLalPwj2KLMIsEjzlp/pYgznlKE2Q=
|
||||||
github.com/sagernet/reality v0.0.0-20230226124550-f98d51fa21b5/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
|
github.com/sagernet/reality v0.0.0-20230228045158-d3e085a8e5d1/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
|
||||||
github.com/sagernet/sing v0.0.0-20220812082120-05f9836bff8f/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
|
github.com/sagernet/sing v0.0.0-20220812082120-05f9836bff8f/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
|
||||||
github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
|
github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
|
||||||
github.com/sagernet/sing v0.1.8-0.20230221060643-3401d210384b h1:Ji2AfGlc4j9AitobOx4k3BCj7eS5nSxL1cgaL81zvlo=
|
github.com/sagernet/sing v0.1.8-0.20230228034829-bb617490652c h1:+YUwfoIkKlMi3Y1QrOy+OlIELC9KWV0+/5F3NX72q8U=
|
||||||
github.com/sagernet/sing v0.1.8-0.20230221060643-3401d210384b/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk=
|
github.com/sagernet/sing v0.1.8-0.20230228034829-bb617490652c/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk=
|
||||||
github.com/sagernet/sing-dns v0.1.4 h1:7VxgeoSCiiazDSaXXQVcvrTBxFpOePPq/4XdgnUDN+0=
|
github.com/sagernet/sing-dns v0.1.4 h1:7VxgeoSCiiazDSaXXQVcvrTBxFpOePPq/4XdgnUDN+0=
|
||||||
github.com/sagernet/sing-dns v0.1.4/go.mod h1:1+6pCa48B1AI78lD+/i/dLgpw4MwfnsSpZo0Ds8wzzk=
|
github.com/sagernet/sing-dns v0.1.4/go.mod h1:1+6pCa48B1AI78lD+/i/dLgpw4MwfnsSpZo0Ds8wzzk=
|
||||||
github.com/sagernet/sing-shadowsocks v0.1.2-0.20230221080503-769c01d6bba9 h1:qS39eA4C7x+zhEkySbASrtmb6ebdy5v0y2M6mgkmSO0=
|
github.com/sagernet/sing-shadowsocks v0.1.2-0.20230221080503-769c01d6bba9 h1:qS39eA4C7x+zhEkySbASrtmb6ebdy5v0y2M6mgkmSO0=
|
||||||
|
|
291
test/reality_test.go
Normal file
291
test/reality_test.go
Normal file
|
@ -0,0 +1,291 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/netip"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
C "github.com/sagernet/sing-box/constant"
|
||||||
|
"github.com/sagernet/sing-box/option"
|
||||||
|
"github.com/sagernet/sing-box/transport/vless"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestVLESSVisionReality(t *testing.T) {
|
||||||
|
_, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
|
||||||
|
|
||||||
|
userUUID := 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.TypeVLESS,
|
||||||
|
VLESSOptions: option.VLESSInboundOptions{
|
||||||
|
ListenOptions: option.ListenOptions{
|
||||||
|
Listen: option.ListenAddress(netip.IPv4Unspecified()),
|
||||||
|
ListenPort: serverPort,
|
||||||
|
},
|
||||||
|
Users: []option.VLESSUser{
|
||||||
|
{
|
||||||
|
Name: "sekai",
|
||||||
|
UUID: userUUID.String(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
TLS: &option.InboundTLSOptions{
|
||||||
|
Enabled: true,
|
||||||
|
ServerName: "google.com",
|
||||||
|
Reality: &option.InboundRealityOptions{
|
||||||
|
Enabled: true,
|
||||||
|
Handshake: option.InboundRealityHandshakeOptions{
|
||||||
|
ServerOptions: option.ServerOptions{
|
||||||
|
Server: "google.com",
|
||||||
|
ServerPort: 443,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ShortID: []string{"0123456789abcdef"},
|
||||||
|
PrivateKey: "UuMBgl7MXTPx9inmQp2UC7Jcnwc6XYbwDNebonM-FCc",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: C.TypeTrojan,
|
||||||
|
Tag: "trojan",
|
||||||
|
TrojanOptions: option.TrojanInboundOptions{
|
||||||
|
ListenOptions: option.ListenOptions{
|
||||||
|
Listen: option.ListenAddress(netip.IPv4Unspecified()),
|
||||||
|
ListenPort: otherPort,
|
||||||
|
},
|
||||||
|
Users: []option.TrojanUser{
|
||||||
|
{
|
||||||
|
Name: "sekai",
|
||||||
|
Password: userUUID.String(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
TLS: &option.InboundTLSOptions{
|
||||||
|
Enabled: true,
|
||||||
|
ServerName: "example.org",
|
||||||
|
CertificatePath: certPem,
|
||||||
|
KeyPath: keyPem,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Outbounds: []option.Outbound{
|
||||||
|
{
|
||||||
|
Type: C.TypeDirect,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: C.TypeTrojan,
|
||||||
|
Tag: "trojan-out",
|
||||||
|
TrojanOptions: option.TrojanOutboundOptions{
|
||||||
|
ServerOptions: option.ServerOptions{
|
||||||
|
Server: "127.0.0.1",
|
||||||
|
ServerPort: otherPort,
|
||||||
|
},
|
||||||
|
Password: userUUID.String(),
|
||||||
|
TLS: &option.OutboundTLSOptions{
|
||||||
|
Enabled: true,
|
||||||
|
ServerName: "example.org",
|
||||||
|
CertificatePath: certPem,
|
||||||
|
},
|
||||||
|
DialerOptions: option.DialerOptions{
|
||||||
|
Detour: "vless-out",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: C.TypeVLESS,
|
||||||
|
Tag: "vless-out",
|
||||||
|
VLESSOptions: option.VLESSOutboundOptions{
|
||||||
|
ServerOptions: option.ServerOptions{
|
||||||
|
Server: "127.0.0.1",
|
||||||
|
ServerPort: serverPort,
|
||||||
|
},
|
||||||
|
UUID: userUUID.String(),
|
||||||
|
Flow: vless.FlowVision,
|
||||||
|
TLS: &option.OutboundTLSOptions{
|
||||||
|
Enabled: true,
|
||||||
|
ServerName: "google.com",
|
||||||
|
Reality: &option.OutboundRealityOptions{
|
||||||
|
Enabled: true,
|
||||||
|
ShortID: "0123456789abcdef",
|
||||||
|
PublicKey: "jNXHt1yRo0vDuchQlIP6Z0ZvjT3KtzVI-T4E7RoLJS0",
|
||||||
|
},
|
||||||
|
UTLS: &option.OutboundUTLSOptions{
|
||||||
|
Enabled: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Route: &option.RouteOptions{
|
||||||
|
Rules: []option.Rule{
|
||||||
|
{
|
||||||
|
DefaultOptions: option.DefaultRule{
|
||||||
|
Inbound: []string{"mixed-in"},
|
||||||
|
Outbound: "trojan-out",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
testSuit(t, clientPort, testPort)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVLESSRealityTransport(t *testing.T) {
|
||||||
|
t.Run("grpc", func(t *testing.T) {
|
||||||
|
testVLESSRealityTransport(t, &option.V2RayTransportOptions{
|
||||||
|
Type: C.V2RayTransportTypeGRPC,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
t.Run("websocket", func(t *testing.T) {
|
||||||
|
testVLESSRealityTransport(t, &option.V2RayTransportOptions{
|
||||||
|
Type: C.V2RayTransportTypeWebsocket,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
t.Run("h2", func(t *testing.T) {
|
||||||
|
testVLESSRealityTransport(t, &option.V2RayTransportOptions{
|
||||||
|
Type: C.V2RayTransportTypeHTTP,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func testVLESSRealityTransport(t *testing.T, transport *option.V2RayTransportOptions) {
|
||||||
|
_, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
|
||||||
|
|
||||||
|
userUUID := 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.TypeVLESS,
|
||||||
|
VLESSOptions: option.VLESSInboundOptions{
|
||||||
|
ListenOptions: option.ListenOptions{
|
||||||
|
Listen: option.ListenAddress(netip.IPv4Unspecified()),
|
||||||
|
ListenPort: serverPort,
|
||||||
|
},
|
||||||
|
Users: []option.VLESSUser{
|
||||||
|
{
|
||||||
|
Name: "sekai",
|
||||||
|
UUID: userUUID.String(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
TLS: &option.InboundTLSOptions{
|
||||||
|
Enabled: true,
|
||||||
|
ServerName: "google.com",
|
||||||
|
Reality: &option.InboundRealityOptions{
|
||||||
|
Enabled: true,
|
||||||
|
Handshake: option.InboundRealityHandshakeOptions{
|
||||||
|
ServerOptions: option.ServerOptions{
|
||||||
|
Server: "google.com",
|
||||||
|
ServerPort: 443,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ShortID: []string{"0123456789abcdef"},
|
||||||
|
PrivateKey: "UuMBgl7MXTPx9inmQp2UC7Jcnwc6XYbwDNebonM-FCc",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Transport: transport,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: C.TypeTrojan,
|
||||||
|
Tag: "trojan",
|
||||||
|
TrojanOptions: option.TrojanInboundOptions{
|
||||||
|
ListenOptions: option.ListenOptions{
|
||||||
|
Listen: option.ListenAddress(netip.IPv4Unspecified()),
|
||||||
|
ListenPort: otherPort,
|
||||||
|
},
|
||||||
|
Users: []option.TrojanUser{
|
||||||
|
{
|
||||||
|
Name: "sekai",
|
||||||
|
Password: userUUID.String(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
TLS: &option.InboundTLSOptions{
|
||||||
|
Enabled: true,
|
||||||
|
ServerName: "example.org",
|
||||||
|
CertificatePath: certPem,
|
||||||
|
KeyPath: keyPem,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Outbounds: []option.Outbound{
|
||||||
|
{
|
||||||
|
Type: C.TypeDirect,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: C.TypeTrojan,
|
||||||
|
Tag: "trojan-out",
|
||||||
|
TrojanOptions: option.TrojanOutboundOptions{
|
||||||
|
ServerOptions: option.ServerOptions{
|
||||||
|
Server: "127.0.0.1",
|
||||||
|
ServerPort: otherPort,
|
||||||
|
},
|
||||||
|
Password: userUUID.String(),
|
||||||
|
TLS: &option.OutboundTLSOptions{
|
||||||
|
Enabled: true,
|
||||||
|
ServerName: "example.org",
|
||||||
|
CertificatePath: certPem,
|
||||||
|
},
|
||||||
|
DialerOptions: option.DialerOptions{
|
||||||
|
Detour: "vless-out",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: C.TypeVLESS,
|
||||||
|
Tag: "vless-out",
|
||||||
|
VLESSOptions: option.VLESSOutboundOptions{
|
||||||
|
ServerOptions: option.ServerOptions{
|
||||||
|
Server: "127.0.0.1",
|
||||||
|
ServerPort: serverPort,
|
||||||
|
},
|
||||||
|
UUID: userUUID.String(),
|
||||||
|
TLS: &option.OutboundTLSOptions{
|
||||||
|
Enabled: true,
|
||||||
|
ServerName: "google.com",
|
||||||
|
Reality: &option.OutboundRealityOptions{
|
||||||
|
Enabled: true,
|
||||||
|
ShortID: "0123456789abcdef",
|
||||||
|
PublicKey: "jNXHt1yRo0vDuchQlIP6Z0ZvjT3KtzVI-T4E7RoLJS0",
|
||||||
|
},
|
||||||
|
UTLS: &option.OutboundUTLSOptions{
|
||||||
|
Enabled: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Transport: transport,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Route: &option.RouteOptions{
|
||||||
|
Rules: []option.Rule{
|
||||||
|
{
|
||||||
|
DefaultOptions: option.DefaultRule{
|
||||||
|
Inbound: []string{"mixed-in"},
|
||||||
|
Outbound: "trojan-out",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
testSuit(t, clientPort, testPort)
|
||||||
|
}
|
|
@ -395,134 +395,3 @@ func testVLESSSelfTLS(t *testing.T, flow string) {
|
||||||
})
|
})
|
||||||
testSuit(t, clientPort, testPort)
|
testSuit(t, clientPort, testPort)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestVLESSVisionReality(t *testing.T) {
|
|
||||||
_, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
|
|
||||||
|
|
||||||
userUUID := 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.TypeVLESS,
|
|
||||||
VLESSOptions: option.VLESSInboundOptions{
|
|
||||||
ListenOptions: option.ListenOptions{
|
|
||||||
Listen: option.ListenAddress(netip.IPv4Unspecified()),
|
|
||||||
ListenPort: serverPort,
|
|
||||||
},
|
|
||||||
Users: []option.VLESSUser{
|
|
||||||
{
|
|
||||||
Name: "sekai",
|
|
||||||
UUID: userUUID.String(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
TLS: &option.InboundTLSOptions{
|
|
||||||
Enabled: true,
|
|
||||||
ServerName: "google.com",
|
|
||||||
Reality: &option.InboundRealityOptions{
|
|
||||||
Enabled: true,
|
|
||||||
Handshake: option.InboundRealityHandshakeOptions{
|
|
||||||
ServerOptions: option.ServerOptions{
|
|
||||||
Server: "google.com",
|
|
||||||
ServerPort: 443,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ShortID: []string{"0123456789abcdef"},
|
|
||||||
PrivateKey: "UuMBgl7MXTPx9inmQp2UC7Jcnwc6XYbwDNebonM-FCc",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Type: C.TypeTrojan,
|
|
||||||
Tag: "trojan",
|
|
||||||
TrojanOptions: option.TrojanInboundOptions{
|
|
||||||
ListenOptions: option.ListenOptions{
|
|
||||||
Listen: option.ListenAddress(netip.IPv4Unspecified()),
|
|
||||||
ListenPort: otherPort,
|
|
||||||
},
|
|
||||||
Users: []option.TrojanUser{
|
|
||||||
{
|
|
||||||
Name: "sekai",
|
|
||||||
Password: userUUID.String(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
TLS: &option.InboundTLSOptions{
|
|
||||||
Enabled: true,
|
|
||||||
ServerName: "example.org",
|
|
||||||
CertificatePath: certPem,
|
|
||||||
KeyPath: keyPem,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Outbounds: []option.Outbound{
|
|
||||||
{
|
|
||||||
Type: C.TypeDirect,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Type: C.TypeTrojan,
|
|
||||||
Tag: "trojan-out",
|
|
||||||
TrojanOptions: option.TrojanOutboundOptions{
|
|
||||||
ServerOptions: option.ServerOptions{
|
|
||||||
Server: "127.0.0.1",
|
|
||||||
ServerPort: otherPort,
|
|
||||||
},
|
|
||||||
Password: userUUID.String(),
|
|
||||||
TLS: &option.OutboundTLSOptions{
|
|
||||||
Enabled: true,
|
|
||||||
ServerName: "example.org",
|
|
||||||
CertificatePath: certPem,
|
|
||||||
},
|
|
||||||
DialerOptions: option.DialerOptions{
|
|
||||||
Detour: "vless-out",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Type: C.TypeVLESS,
|
|
||||||
Tag: "vless-out",
|
|
||||||
VLESSOptions: option.VLESSOutboundOptions{
|
|
||||||
ServerOptions: option.ServerOptions{
|
|
||||||
Server: "127.0.0.1",
|
|
||||||
ServerPort: serverPort,
|
|
||||||
},
|
|
||||||
UUID: userUUID.String(),
|
|
||||||
Flow: vless.FlowVision,
|
|
||||||
TLS: &option.OutboundTLSOptions{
|
|
||||||
Enabled: true,
|
|
||||||
ServerName: "google.com",
|
|
||||||
Reality: &option.OutboundRealityOptions{
|
|
||||||
Enabled: true,
|
|
||||||
ShortID: "0123456789abcdef",
|
|
||||||
PublicKey: "jNXHt1yRo0vDuchQlIP6Z0ZvjT3KtzVI-T4E7RoLJS0",
|
|
||||||
},
|
|
||||||
UTLS: &option.OutboundUTLSOptions{
|
|
||||||
Enabled: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Route: &option.RouteOptions{
|
|
||||||
Rules: []option.Rule{
|
|
||||||
{
|
|
||||||
DefaultOptions: option.DefaultRule{
|
|
||||||
Inbound: []string{"mixed-in"},
|
|
||||||
Outbound: "trojan-out",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
testSuit(t, clientPort, testPort)
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
//go:build !go1.20
|
||||||
|
|
||||||
package v2raygrpclite
|
package v2raygrpclite
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
122
transport/v2raygrpclite/server_badhttp.go
Normal file
122
transport/v2raygrpclite/server_badhttp.go
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
//go:build go1.20 && !go1.21
|
||||||
|
|
||||||
|
package v2raygrpclite
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/sagernet/badhttp"
|
||||||
|
"github.com/sagernet/badhttp2"
|
||||||
|
"github.com/sagernet/badhttp2/h2c"
|
||||||
|
"github.com/sagernet/sing-box/adapter"
|
||||||
|
"github.com/sagernet/sing-box/common/tls"
|
||||||
|
"github.com/sagernet/sing-box/option"
|
||||||
|
"github.com/sagernet/sing-box/transport/v2rayhttp"
|
||||||
|
"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"
|
||||||
|
sHttp "github.com/sagernet/sing/protocol/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ adapter.V2RayServerTransport = (*Server)(nil)
|
||||||
|
|
||||||
|
type Server struct {
|
||||||
|
handler adapter.V2RayServerTransportHandler
|
||||||
|
errorHandler E.Handler
|
||||||
|
httpServer *http.Server
|
||||||
|
h2Server *http2.Server
|
||||||
|
h2cHandler http.Handler
|
||||||
|
path string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) Network() []string {
|
||||||
|
return []string{N.NetworkTCP}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewServer(ctx context.Context, options option.V2RayGRPCOptions, tlsConfig tls.ServerConfig, handler adapter.V2RayServerTransportHandler) (*Server, error) {
|
||||||
|
server := &Server{
|
||||||
|
handler: handler,
|
||||||
|
path: fmt.Sprintf("/%s/Tun", url.QueryEscape(options.ServiceName)),
|
||||||
|
h2Server: new(http2.Server),
|
||||||
|
}
|
||||||
|
server.httpServer = &http.Server{
|
||||||
|
Handler: server,
|
||||||
|
}
|
||||||
|
server.h2cHandler = h2c.NewHandler(server, server.h2Server)
|
||||||
|
if tlsConfig != nil {
|
||||||
|
if len(tlsConfig.NextProtos()) == 0 {
|
||||||
|
tlsConfig.SetNextProtos([]string{http2.NextProtoTLS})
|
||||||
|
}
|
||||||
|
server.httpServer.TLSConfig = tlsConfig
|
||||||
|
}
|
||||||
|
return server, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
|
||||||
|
if request.Method == "PRI" && len(request.Header) == 0 && request.URL.Path == "*" && request.Proto == "HTTP/2.0" {
|
||||||
|
s.h2cHandler.ServeHTTP(writer, request)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if request.URL.Path != s.path {
|
||||||
|
s.fallbackRequest(request.Context(), writer, request, http.StatusNotFound, E.New("bad path: ", request.URL.Path))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if request.Method != http.MethodPost {
|
||||||
|
s.fallbackRequest(request.Context(), writer, request, http.StatusNotFound, E.New("bad method: ", request.Method))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ct := request.Header.Get("Content-Type"); !strings.HasPrefix(ct, "application/grpc") {
|
||||||
|
s.fallbackRequest(request.Context(), writer, request, http.StatusNotFound, E.New("bad content type: ", ct))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
writer.Header().Set("Content-Type", "application/grpc")
|
||||||
|
writer.Header().Set("TE", "trailers")
|
||||||
|
writer.WriteHeader(http.StatusOK)
|
||||||
|
var metadata M.Metadata
|
||||||
|
metadata.Source = sHttp.SourceAddress(v2rayhttp.BadRequest(request))
|
||||||
|
conn := v2rayhttp.NewHTTP2Wrapper(newGunConn(request.Body, writer, writer.(http.Flusher)))
|
||||||
|
s.handler.NewConnection(request.Context(), conn, metadata)
|
||||||
|
conn.CloseWrapper()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) fallbackRequest(ctx context.Context, writer http.ResponseWriter, request *http.Request, statusCode int, err error) {
|
||||||
|
conn := v2rayhttp.NewHTTPConn(request.Body, writer)
|
||||||
|
fErr := s.handler.FallbackConnection(ctx, &conn, M.Metadata{})
|
||||||
|
if fErr == nil {
|
||||||
|
return
|
||||||
|
} else if fErr == os.ErrInvalid {
|
||||||
|
fErr = nil
|
||||||
|
}
|
||||||
|
writer.WriteHeader(statusCode)
|
||||||
|
s.handler.NewError(request.Context(), E.Cause(E.Errors(err, E.Cause(fErr, "fallback connection")), "process connection from ", request.RemoteAddr))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) Serve(listener net.Listener) error {
|
||||||
|
fixTLSConfig := s.httpServer.TLSConfig == nil
|
||||||
|
err := http2.ConfigureServer(s.httpServer, s.h2Server)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if fixTLSConfig {
|
||||||
|
s.httpServer.TLSConfig = nil
|
||||||
|
}
|
||||||
|
if s.httpServer.TLSConfig == nil {
|
||||||
|
return s.httpServer.Serve(listener)
|
||||||
|
} else {
|
||||||
|
return s.httpServer.ServeTLS(listener, "", "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) ServePacket(listener net.PacketConn) error {
|
||||||
|
return os.ErrInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) Close() error {
|
||||||
|
return common.Close(common.PtrOrNil(s.httpServer))
|
||||||
|
}
|
|
@ -1,3 +1,5 @@
|
||||||
|
//go:build !go1.20
|
||||||
|
|
||||||
package v2rayhttp
|
package v2rayhttp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
182
transport/v2rayhttp/server_badhttp.go
Normal file
182
transport/v2rayhttp/server_badhttp.go
Normal file
|
@ -0,0 +1,182 @@
|
||||||
|
//go:build go1.20 && !go1.21
|
||||||
|
|
||||||
|
package v2rayhttp
|
||||||
|
|
||||||
|
import (
|
||||||
|
std_bufio "bufio"
|
||||||
|
"context"
|
||||||
|
"net"
|
||||||
|
stdHTTP "net/http"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/sagernet/badhttp"
|
||||||
|
"github.com/sagernet/badhttp2"
|
||||||
|
"github.com/sagernet/badhttp2/h2c"
|
||||||
|
"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/common"
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
|
N "github.com/sagernet/sing/common/network"
|
||||||
|
sHttp "github.com/sagernet/sing/protocol/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ adapter.V2RayServerTransport = (*Server)(nil)
|
||||||
|
|
||||||
|
type Server struct {
|
||||||
|
ctx context.Context
|
||||||
|
handler adapter.V2RayServerTransportHandler
|
||||||
|
httpServer *http.Server
|
||||||
|
h2Server *http2.Server
|
||||||
|
h2cHandler http.Handler
|
||||||
|
host []string
|
||||||
|
path string
|
||||||
|
method string
|
||||||
|
headers http.Header
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) Network() []string {
|
||||||
|
return []string{N.NetworkTCP}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewServer(ctx context.Context, options option.V2RayHTTPOptions, tlsConfig tls.ServerConfig, handler adapter.V2RayServerTransportHandler) (*Server, error) {
|
||||||
|
server := &Server{
|
||||||
|
ctx: ctx,
|
||||||
|
handler: handler,
|
||||||
|
h2Server: new(http2.Server),
|
||||||
|
host: options.Host,
|
||||||
|
path: options.Path,
|
||||||
|
method: options.Method,
|
||||||
|
headers: make(http.Header),
|
||||||
|
}
|
||||||
|
if server.method == "" {
|
||||||
|
server.method = "PUT"
|
||||||
|
}
|
||||||
|
if !strings.HasPrefix(server.path, "/") {
|
||||||
|
server.path = "/" + server.path
|
||||||
|
}
|
||||||
|
for key, value := range options.Headers {
|
||||||
|
server.headers.Set(key, value)
|
||||||
|
}
|
||||||
|
server.httpServer = &http.Server{
|
||||||
|
Handler: server,
|
||||||
|
ReadHeaderTimeout: C.TCPTimeout,
|
||||||
|
MaxHeaderBytes: http.DefaultMaxHeaderBytes,
|
||||||
|
TLSConfig: tlsConfig,
|
||||||
|
}
|
||||||
|
server.h2cHandler = h2c.NewHandler(server, server.h2Server)
|
||||||
|
return server, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
|
||||||
|
if request.Method == "PRI" && len(request.Header) == 0 && request.URL.Path == "*" && request.Proto == "HTTP/2.0" {
|
||||||
|
s.h2cHandler.ServeHTTP(writer, request)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
host := request.Host
|
||||||
|
if len(s.host) > 0 && !common.Contains(s.host, host) {
|
||||||
|
s.fallbackRequest(request.Context(), writer, request, http.StatusBadRequest, E.New("bad host: ", host))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !strings.HasPrefix(request.URL.Path, s.path) {
|
||||||
|
s.fallbackRequest(request.Context(), writer, request, http.StatusNotFound, E.New("bad path: ", request.URL.Path))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if request.Method != s.method {
|
||||||
|
s.fallbackRequest(request.Context(), writer, request, http.StatusNotFound, E.New("bad method: ", request.Method))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.Header().Set("Cache-Control", "no-store")
|
||||||
|
|
||||||
|
for key, values := range s.headers {
|
||||||
|
for _, value := range values {
|
||||||
|
writer.Header().Set(key, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.WriteHeader(http.StatusOK)
|
||||||
|
writer.(http.Flusher).Flush()
|
||||||
|
|
||||||
|
var metadata M.Metadata
|
||||||
|
metadata.Source = sHttp.SourceAddress(BadRequest(request))
|
||||||
|
if h, ok := writer.(http.Hijacker); ok {
|
||||||
|
conn, _, err := h.Hijack()
|
||||||
|
if err != nil {
|
||||||
|
s.fallbackRequest(request.Context(), writer, request, http.StatusInternalServerError, E.Cause(err, "hijack conn"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.handler.NewConnection(request.Context(), conn, metadata)
|
||||||
|
} else {
|
||||||
|
conn := NewHTTP2Wrapper(&ServerHTTPConn{
|
||||||
|
NewHTTPConn(request.Body, writer),
|
||||||
|
writer.(http.Flusher),
|
||||||
|
})
|
||||||
|
s.handler.NewConnection(request.Context(), conn, metadata)
|
||||||
|
conn.CloseWrapper()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) fallbackRequest(ctx context.Context, writer http.ResponseWriter, request *http.Request, statusCode int, err error) {
|
||||||
|
conn := NewHTTPConn(request.Body, writer)
|
||||||
|
fErr := s.handler.FallbackConnection(ctx, &conn, M.Metadata{})
|
||||||
|
if fErr == nil {
|
||||||
|
return
|
||||||
|
} else if fErr == os.ErrInvalid {
|
||||||
|
fErr = nil
|
||||||
|
}
|
||||||
|
writer.WriteHeader(statusCode)
|
||||||
|
s.handler.NewError(request.Context(), E.Cause(E.Errors(err, E.Cause(fErr, "fallback connection")), "process connection from ", request.RemoteAddr))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) Serve(listener net.Listener) error {
|
||||||
|
fixTLSConfig := s.httpServer.TLSConfig == nil
|
||||||
|
err := http2.ConfigureServer(s.httpServer, s.h2Server)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if fixTLSConfig {
|
||||||
|
s.httpServer.TLSConfig = nil
|
||||||
|
}
|
||||||
|
if s.httpServer.TLSConfig == nil {
|
||||||
|
return s.httpServer.Serve(listener)
|
||||||
|
} else {
|
||||||
|
return s.httpServer.ServeTLS(listener, "", "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) ServePacket(listener net.PacketConn) error {
|
||||||
|
return os.ErrInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) Close() error {
|
||||||
|
return common.Close(common.PtrOrNil(s.httpServer))
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ stdHTTP.ResponseWriter = (*BadResponseWriter)(nil)
|
||||||
|
_ stdHTTP.Hijacker = (*BadResponseWriter)(nil)
|
||||||
|
)
|
||||||
|
|
||||||
|
type BadResponseWriter struct {
|
||||||
|
http.ResponseWriter
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *BadResponseWriter) Header() stdHTTP.Header {
|
||||||
|
return stdHTTP.Header(w.ResponseWriter.Header())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *BadResponseWriter) Hijack() (net.Conn, *std_bufio.ReadWriter, error) {
|
||||||
|
if hijacker, loaded := common.Cast[http.Hijacker](w.ResponseWriter); loaded {
|
||||||
|
return hijacker.Hijack()
|
||||||
|
}
|
||||||
|
return nil, nil, os.ErrInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
func BadRequest(r *http.Request) *stdHTTP.Request {
|
||||||
|
return (*stdHTTP.Request)(unsafe.Pointer(r))
|
||||||
|
}
|
|
@ -1,3 +1,5 @@
|
||||||
|
//go:build !go1.20
|
||||||
|
|
||||||
package v2raywebsocket
|
package v2raywebsocket
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
141
transport/v2raywebsocket/server_badhttp.go
Normal file
141
transport/v2raywebsocket/server_badhttp.go
Normal file
|
@ -0,0 +1,141 @@
|
||||||
|
//go:build go1.20 && !go1.21
|
||||||
|
|
||||||
|
package v2raywebsocket
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/base64"
|
||||||
|
"net"
|
||||||
|
stdHTTP "net/http"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/sagernet/badhttp"
|
||||||
|
"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/v2rayhttp"
|
||||||
|
"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"
|
||||||
|
sHttp "github.com/sagernet/sing/protocol/http"
|
||||||
|
"github.com/sagernet/websocket"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ adapter.V2RayServerTransport = (*Server)(nil)
|
||||||
|
|
||||||
|
type Server struct {
|
||||||
|
ctx context.Context
|
||||||
|
handler adapter.V2RayServerTransportHandler
|
||||||
|
httpServer *http.Server
|
||||||
|
path string
|
||||||
|
maxEarlyData uint32
|
||||||
|
earlyDataHeaderName string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewServer(ctx context.Context, options option.V2RayWebsocketOptions, tlsConfig tls.ServerConfig, handler adapter.V2RayServerTransportHandler) (*Server, error) {
|
||||||
|
server := &Server{
|
||||||
|
ctx: ctx,
|
||||||
|
handler: handler,
|
||||||
|
path: options.Path,
|
||||||
|
maxEarlyData: options.MaxEarlyData,
|
||||||
|
earlyDataHeaderName: options.EarlyDataHeaderName,
|
||||||
|
}
|
||||||
|
if !strings.HasPrefix(server.path, "/") {
|
||||||
|
server.path = "/" + server.path
|
||||||
|
}
|
||||||
|
server.httpServer = &http.Server{
|
||||||
|
Handler: server,
|
||||||
|
ReadHeaderTimeout: C.TCPTimeout,
|
||||||
|
MaxHeaderBytes: http.DefaultMaxHeaderBytes,
|
||||||
|
TLSConfig: tlsConfig,
|
||||||
|
}
|
||||||
|
return server, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var upgrader = websocket.Upgrader{
|
||||||
|
HandshakeTimeout: C.TCPTimeout,
|
||||||
|
CheckOrigin: func(r *stdHTTP.Request) bool {
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
|
||||||
|
if s.maxEarlyData == 0 || s.earlyDataHeaderName != "" {
|
||||||
|
if request.URL.Path != s.path {
|
||||||
|
s.fallbackRequest(request.Context(), writer, request, http.StatusNotFound, E.New("bad path: ", request.URL.Path))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
earlyData []byte
|
||||||
|
err error
|
||||||
|
conn net.Conn
|
||||||
|
)
|
||||||
|
if s.earlyDataHeaderName == "" {
|
||||||
|
if strings.HasPrefix(request.URL.RequestURI(), s.path) {
|
||||||
|
earlyDataStr := request.URL.RequestURI()[len(s.path):]
|
||||||
|
earlyData, err = base64.RawURLEncoding.DecodeString(earlyDataStr)
|
||||||
|
} else {
|
||||||
|
s.fallbackRequest(request.Context(), writer, request, http.StatusNotFound, E.New("bad path: ", request.URL.Path))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
earlyDataStr := request.Header.Get(s.earlyDataHeaderName)
|
||||||
|
if earlyDataStr != "" {
|
||||||
|
earlyData, err = base64.RawURLEncoding.DecodeString(earlyDataStr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
s.fallbackRequest(request.Context(), writer, request, http.StatusBadRequest, E.Cause(err, "decode early data"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
wsConn, err := upgrader.Upgrade(&v2rayhttp.BadResponseWriter{ResponseWriter: writer}, v2rayhttp.BadRequest(request), nil)
|
||||||
|
if err != nil {
|
||||||
|
s.fallbackRequest(request.Context(), writer, request, http.StatusBadRequest, E.Cause(err, "upgrade websocket connection"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var metadata M.Metadata
|
||||||
|
metadata.Source = sHttp.SourceAddress(v2rayhttp.BadRequest(request))
|
||||||
|
conn = NewServerConn(wsConn, metadata.Source.TCPAddr())
|
||||||
|
if len(earlyData) > 0 {
|
||||||
|
conn = bufio.NewCachedConn(conn, buf.As(earlyData))
|
||||||
|
}
|
||||||
|
s.handler.NewConnection(request.Context(), conn, metadata)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) fallbackRequest(ctx context.Context, writer http.ResponseWriter, request *http.Request, statusCode int, err error) {
|
||||||
|
conn := v2rayhttp.NewHTTPConn(request.Body, writer)
|
||||||
|
fErr := s.handler.FallbackConnection(ctx, &conn, M.Metadata{})
|
||||||
|
if fErr == nil {
|
||||||
|
return
|
||||||
|
} else if fErr == os.ErrInvalid {
|
||||||
|
fErr = nil
|
||||||
|
}
|
||||||
|
writer.WriteHeader(statusCode)
|
||||||
|
s.handler.NewError(request.Context(), E.Cause(E.Errors(err, E.Cause(fErr, "fallback connection")), "process connection from ", request.RemoteAddr))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) Network() []string {
|
||||||
|
return []string{N.NetworkTCP}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) Serve(listener net.Listener) error {
|
||||||
|
if s.httpServer.TLSConfig == nil {
|
||||||
|
return s.httpServer.Serve(listener)
|
||||||
|
} else {
|
||||||
|
return s.httpServer.ServeTLS(listener, "", "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) ServePacket(listener net.PacketConn) error {
|
||||||
|
return os.ErrInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) Close() error {
|
||||||
|
return common.Close(common.PtrOrNil(s.httpServer))
|
||||||
|
}
|
Loading…
Reference in a new issue