mirror of https://github.com/ginuerzh/gost
8 changed files with 63 additions and 867 deletions
@ -1,348 +0,0 @@ |
|||||
package gost |
|
||||
|
|
||||
import ( |
|
||||
"context" |
|
||||
"crypto/aes" |
|
||||
"crypto/cipher" |
|
||||
"crypto/rand" |
|
||||
"crypto/tls" |
|
||||
"errors" |
|
||||
"io" |
|
||||
"net" |
|
||||
"sync" |
|
||||
"time" |
|
||||
|
|
||||
"github.com/go-log/log" |
|
||||
quic "github.com/lucas-clemente/quic-go" |
|
||||
) |
|
||||
|
|
||||
type quicSession struct { |
|
||||
session quic.EarlyConnection |
|
||||
} |
|
||||
|
|
||||
func (session *quicSession) GetConn() (*quicConn, error) { |
|
||||
stream, err := session.session.OpenStreamSync(context.Background()) |
|
||||
if err != nil { |
|
||||
return nil, err |
|
||||
} |
|
||||
return &quicConn{ |
|
||||
Stream: stream, |
|
||||
laddr: session.session.LocalAddr(), |
|
||||
raddr: session.session.RemoteAddr(), |
|
||||
}, nil |
|
||||
} |
|
||||
|
|
||||
func (session *quicSession) Close() error { |
|
||||
return session.session.CloseWithError(quic.ApplicationErrorCode(0), "closed") |
|
||||
} |
|
||||
|
|
||||
type quicTransporter struct { |
|
||||
config *QUICConfig |
|
||||
sessionMutex sync.Mutex |
|
||||
sessions map[string]*quicSession |
|
||||
} |
|
||||
|
|
||||
// QUICTransporter creates a Transporter that is used by QUIC proxy client.
|
|
||||
func QUICTransporter(config *QUICConfig) Transporter { |
|
||||
if config == nil { |
|
||||
config = &QUICConfig{} |
|
||||
} |
|
||||
return &quicTransporter{ |
|
||||
config: config, |
|
||||
sessions: make(map[string]*quicSession), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (tr *quicTransporter) Dial(addr string, options ...DialOption) (conn net.Conn, err error) { |
|
||||
opts := &DialOptions{} |
|
||||
for _, option := range options { |
|
||||
option(opts) |
|
||||
} |
|
||||
|
|
||||
udpAddr, err := net.ResolveUDPAddr("udp", addr) |
|
||||
if err != nil { |
|
||||
return nil, err |
|
||||
} |
|
||||
|
|
||||
tr.sessionMutex.Lock() |
|
||||
defer tr.sessionMutex.Unlock() |
|
||||
|
|
||||
session, ok := tr.sessions[addr] |
|
||||
if !ok { |
|
||||
var pc net.PacketConn |
|
||||
pc, err = net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: 0}) |
|
||||
if err != nil { |
|
||||
return |
|
||||
} |
|
||||
|
|
||||
if tr.config != nil && tr.config.Key != nil { |
|
||||
pc = &quicCipherConn{PacketConn: pc, key: tr.config.Key} |
|
||||
} |
|
||||
|
|
||||
session, err = tr.initSession(udpAddr, pc) |
|
||||
if err != nil { |
|
||||
pc.Close() |
|
||||
return nil, err |
|
||||
} |
|
||||
tr.sessions[addr] = session |
|
||||
} |
|
||||
|
|
||||
conn, err = session.GetConn() |
|
||||
if err != nil { |
|
||||
session.Close() |
|
||||
delete(tr.sessions, addr) |
|
||||
return nil, err |
|
||||
} |
|
||||
return conn, nil |
|
||||
} |
|
||||
|
|
||||
func (tr *quicTransporter) Handshake(conn net.Conn, options ...HandshakeOption) (net.Conn, error) { |
|
||||
return conn, nil |
|
||||
} |
|
||||
|
|
||||
func (tr *quicTransporter) initSession(addr net.Addr, conn net.PacketConn) (*quicSession, error) { |
|
||||
config := tr.config |
|
||||
if config == nil { |
|
||||
config = &QUICConfig{} |
|
||||
} |
|
||||
if config.TLSConfig == nil { |
|
||||
config.TLSConfig = &tls.Config{InsecureSkipVerify: true} |
|
||||
} |
|
||||
|
|
||||
quicConfig := &quic.Config{ |
|
||||
HandshakeIdleTimeout: config.Timeout, |
|
||||
MaxIdleTimeout: config.IdleTimeout, |
|
||||
KeepAlivePeriod: config.KeepAlivePeriod, |
|
||||
Versions: []quic.VersionNumber{ |
|
||||
quic.Version1, |
|
||||
quic.VersionDraft29, |
|
||||
}, |
|
||||
} |
|
||||
session, err := quic.DialEarly(conn, addr, addr.String(), tlsConfigQUICALPN(config.TLSConfig), quicConfig) |
|
||||
if err != nil { |
|
||||
log.Logf("quic dial %s: %v", addr, err) |
|
||||
return nil, err |
|
||||
} |
|
||||
return &quicSession{session: session}, nil |
|
||||
} |
|
||||
|
|
||||
func (tr *quicTransporter) Multiplex() bool { |
|
||||
return true |
|
||||
} |
|
||||
|
|
||||
// QUICConfig is the config for QUIC client and server
|
|
||||
type QUICConfig struct { |
|
||||
TLSConfig *tls.Config |
|
||||
Timeout time.Duration |
|
||||
KeepAlive bool |
|
||||
KeepAlivePeriod time.Duration |
|
||||
IdleTimeout time.Duration |
|
||||
Key []byte |
|
||||
} |
|
||||
|
|
||||
type quicListener struct { |
|
||||
ln quic.EarlyListener |
|
||||
connChan chan net.Conn |
|
||||
errChan chan error |
|
||||
} |
|
||||
|
|
||||
// QUICListener creates a Listener for QUIC proxy server.
|
|
||||
func QUICListener(addr string, config *QUICConfig) (Listener, error) { |
|
||||
if config == nil { |
|
||||
config = &QUICConfig{} |
|
||||
} |
|
||||
quicConfig := &quic.Config{ |
|
||||
HandshakeIdleTimeout: config.Timeout, |
|
||||
KeepAlivePeriod: config.KeepAlivePeriod, |
|
||||
MaxIdleTimeout: config.IdleTimeout, |
|
||||
Versions: []quic.VersionNumber{ |
|
||||
quic.Version1, |
|
||||
quic.VersionDraft29, |
|
||||
}, |
|
||||
} |
|
||||
|
|
||||
tlsConfig := config.TLSConfig |
|
||||
if tlsConfig == nil { |
|
||||
tlsConfig = DefaultTLSConfig |
|
||||
} |
|
||||
var conn net.PacketConn |
|
||||
|
|
||||
udpAddr, err := net.ResolveUDPAddr("udp", addr) |
|
||||
if err != nil { |
|
||||
return nil, err |
|
||||
} |
|
||||
conn, err = net.ListenUDP("udp", udpAddr) |
|
||||
if err != nil { |
|
||||
return nil, err |
|
||||
} |
|
||||
|
|
||||
if config.Key != nil { |
|
||||
conn = &quicCipherConn{PacketConn: conn, key: config.Key} |
|
||||
} |
|
||||
|
|
||||
ln, err := quic.ListenEarly(conn, tlsConfigQUICALPN(tlsConfig), quicConfig) |
|
||||
if err != nil { |
|
||||
return nil, err |
|
||||
} |
|
||||
|
|
||||
l := &quicListener{ |
|
||||
ln: ln, |
|
||||
connChan: make(chan net.Conn, 1024), |
|
||||
errChan: make(chan error, 1), |
|
||||
} |
|
||||
go l.listenLoop() |
|
||||
|
|
||||
return l, nil |
|
||||
} |
|
||||
|
|
||||
func (l *quicListener) listenLoop() { |
|
||||
for { |
|
||||
session, err := l.ln.Accept(context.Background()) |
|
||||
if err != nil { |
|
||||
log.Log("[quic] accept:", err) |
|
||||
l.errChan <- err |
|
||||
close(l.errChan) |
|
||||
return |
|
||||
} |
|
||||
go l.sessionLoop(session) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (l *quicListener) sessionLoop(session quic.Connection) { |
|
||||
log.Logf("[quic] %s <-> %s", session.RemoteAddr(), session.LocalAddr()) |
|
||||
defer log.Logf("[quic] %s >-< %s", session.RemoteAddr(), session.LocalAddr()) |
|
||||
|
|
||||
for { |
|
||||
stream, err := session.AcceptStream(context.Background()) |
|
||||
if err != nil { |
|
||||
log.Log("[quic] accept stream:", err) |
|
||||
session.CloseWithError(quic.ApplicationErrorCode(0), "closed") |
|
||||
return |
|
||||
} |
|
||||
|
|
||||
cc := &quicConn{Stream: stream, laddr: session.LocalAddr(), raddr: session.RemoteAddr()} |
|
||||
select { |
|
||||
case l.connChan <- cc: |
|
||||
default: |
|
||||
cc.Close() |
|
||||
log.Logf("[quic] %s - %s: connection queue is full", session.RemoteAddr(), session.LocalAddr()) |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (l *quicListener) Accept() (conn net.Conn, err error) { |
|
||||
var ok bool |
|
||||
select { |
|
||||
case conn = <-l.connChan: |
|
||||
case err, ok = <-l.errChan: |
|
||||
if !ok { |
|
||||
err = errors.New("accpet on closed listener") |
|
||||
} |
|
||||
} |
|
||||
return |
|
||||
} |
|
||||
|
|
||||
func (l *quicListener) Addr() net.Addr { |
|
||||
return l.ln.Addr() |
|
||||
} |
|
||||
|
|
||||
func (l *quicListener) Close() error { |
|
||||
return l.ln.Close() |
|
||||
} |
|
||||
|
|
||||
type quicConn struct { |
|
||||
quic.Stream |
|
||||
laddr net.Addr |
|
||||
raddr net.Addr |
|
||||
} |
|
||||
|
|
||||
func (c *quicConn) LocalAddr() net.Addr { |
|
||||
return c.laddr |
|
||||
} |
|
||||
|
|
||||
func (c *quicConn) RemoteAddr() net.Addr { |
|
||||
return c.raddr |
|
||||
} |
|
||||
|
|
||||
type quicCipherConn struct { |
|
||||
net.PacketConn |
|
||||
key []byte |
|
||||
} |
|
||||
|
|
||||
func (conn *quicCipherConn) ReadFrom(data []byte) (n int, addr net.Addr, err error) { |
|
||||
n, addr, err = conn.PacketConn.ReadFrom(data) |
|
||||
if err != nil { |
|
||||
return |
|
||||
} |
|
||||
b, err := conn.decrypt(data[:n]) |
|
||||
if err != nil { |
|
||||
return |
|
||||
} |
|
||||
|
|
||||
copy(data, b) |
|
||||
|
|
||||
return len(b), addr, nil |
|
||||
} |
|
||||
|
|
||||
func (conn *quicCipherConn) WriteTo(data []byte, addr net.Addr) (n int, err error) { |
|
||||
b, err := conn.encrypt(data) |
|
||||
if err != nil { |
|
||||
return |
|
||||
} |
|
||||
|
|
||||
_, err = conn.PacketConn.WriteTo(b, addr) |
|
||||
if err != nil { |
|
||||
return |
|
||||
} |
|
||||
|
|
||||
return len(b), nil |
|
||||
} |
|
||||
|
|
||||
func (conn *quicCipherConn) encrypt(data []byte) ([]byte, error) { |
|
||||
c, err := aes.NewCipher(conn.key) |
|
||||
if err != nil { |
|
||||
return nil, err |
|
||||
} |
|
||||
|
|
||||
gcm, err := cipher.NewGCM(c) |
|
||||
if err != nil { |
|
||||
return nil, err |
|
||||
} |
|
||||
|
|
||||
nonce := make([]byte, gcm.NonceSize()) |
|
||||
if _, err = io.ReadFull(rand.Reader, nonce); err != nil { |
|
||||
return nil, err |
|
||||
} |
|
||||
|
|
||||
return gcm.Seal(nonce, nonce, data, nil), nil |
|
||||
} |
|
||||
|
|
||||
func (conn *quicCipherConn) decrypt(data []byte) ([]byte, error) { |
|
||||
c, err := aes.NewCipher(conn.key) |
|
||||
if err != nil { |
|
||||
return nil, err |
|
||||
} |
|
||||
|
|
||||
gcm, err := cipher.NewGCM(c) |
|
||||
if err != nil { |
|
||||
return nil, err |
|
||||
} |
|
||||
|
|
||||
nonceSize := gcm.NonceSize() |
|
||||
if len(data) < nonceSize { |
|
||||
return nil, errors.New("ciphertext too short") |
|
||||
} |
|
||||
|
|
||||
nonce, ciphertext := data[:nonceSize], data[nonceSize:] |
|
||||
return gcm.Open(nil, nonce, ciphertext, nil) |
|
||||
} |
|
||||
|
|
||||
func tlsConfigQUICALPN(tlsConfig *tls.Config) *tls.Config { |
|
||||
if tlsConfig == nil { |
|
||||
panic("quic: tlsconfig is nil") |
|
||||
} |
|
||||
tlsConfigQUIC := &tls.Config{} |
|
||||
*tlsConfigQUIC = *tlsConfig |
|
||||
tlsConfigQUIC.NextProtos = []string{"http/3", "quic/v1"} |
|
||||
return tlsConfigQUIC |
|
||||
} |
|
||||
@ -1,463 +0,0 @@ |
|||||
package gost |
|
||||
|
|
||||
import ( |
|
||||
"crypto/rand" |
|
||||
"crypto/sha256" |
|
||||
"fmt" |
|
||||
"net/http/httptest" |
|
||||
"net/url" |
|
||||
"testing" |
|
||||
) |
|
||||
|
|
||||
func httpOverQUICRoundtrip(targetURL string, data []byte, |
|
||||
clientInfo *url.Userinfo, serverInfo []*url.Userinfo) error { |
|
||||
|
|
||||
ln, err := QUICListener("localhost:0", nil) |
|
||||
if err != nil { |
|
||||
return err |
|
||||
} |
|
||||
|
|
||||
client := &Client{ |
|
||||
Connector: HTTPConnector(clientInfo), |
|
||||
Transporter: QUICTransporter(nil), |
|
||||
} |
|
||||
|
|
||||
server := &Server{ |
|
||||
Listener: ln, |
|
||||
Handler: HTTPHandler( |
|
||||
UsersHandlerOption(serverInfo...), |
|
||||
), |
|
||||
} |
|
||||
|
|
||||
go server.Run() |
|
||||
defer server.Close() |
|
||||
|
|
||||
return proxyRoundtrip(client, server, targetURL, data) |
|
||||
} |
|
||||
|
|
||||
func TestHTTPOverQUIC(t *testing.T) { |
|
||||
httpSrv := httptest.NewServer(httpTestHandler) |
|
||||
defer httpSrv.Close() |
|
||||
|
|
||||
sendData := make([]byte, 128) |
|
||||
rand.Read(sendData) |
|
||||
|
|
||||
for i, tc := range httpProxyTests { |
|
||||
err := httpOverQUICRoundtrip(httpSrv.URL, sendData, tc.cliUser, tc.srvUsers) |
|
||||
if err == nil { |
|
||||
if tc.errStr != "" { |
|
||||
t.Errorf("#%d should failed with error %s", i, tc.errStr) |
|
||||
} |
|
||||
} else { |
|
||||
if tc.errStr == "" { |
|
||||
t.Errorf("#%d got error %v", i, err) |
|
||||
} |
|
||||
if err.Error() != tc.errStr { |
|
||||
t.Errorf("#%d got error %v, want %v", i, err, tc.errStr) |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func BenchmarkHTTPOverQUIC(b *testing.B) { |
|
||||
httpSrv := httptest.NewServer(httpTestHandler) |
|
||||
defer httpSrv.Close() |
|
||||
|
|
||||
sendData := make([]byte, 128) |
|
||||
rand.Read(sendData) |
|
||||
|
|
||||
ln, err := QUICListener("localhost:0", nil) |
|
||||
if err != nil { |
|
||||
b.Error(err) |
|
||||
} |
|
||||
|
|
||||
client := &Client{ |
|
||||
Connector: HTTPConnector(url.UserPassword("admin", "123456")), |
|
||||
Transporter: QUICTransporter(&QUICConfig{KeepAlive: true}), |
|
||||
} |
|
||||
|
|
||||
server := &Server{ |
|
||||
Listener: ln, |
|
||||
Handler: HTTPHandler( |
|
||||
UsersHandlerOption(url.UserPassword("admin", "123456")), |
|
||||
), |
|
||||
} |
|
||||
go server.Run() |
|
||||
defer server.Close() |
|
||||
|
|
||||
for i := 0; i < b.N; i++ { |
|
||||
if err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil { |
|
||||
b.Error(err) |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func BenchmarkHTTPOverQUICParallel(b *testing.B) { |
|
||||
httpSrv := httptest.NewServer(httpTestHandler) |
|
||||
defer httpSrv.Close() |
|
||||
|
|
||||
sendData := make([]byte, 128) |
|
||||
rand.Read(sendData) |
|
||||
|
|
||||
ln, err := QUICListener("localhost:0", nil) |
|
||||
if err != nil { |
|
||||
b.Error(err) |
|
||||
} |
|
||||
|
|
||||
client := &Client{ |
|
||||
Connector: HTTPConnector(url.UserPassword("admin", "123456")), |
|
||||
Transporter: QUICTransporter(nil), |
|
||||
} |
|
||||
|
|
||||
server := &Server{ |
|
||||
Listener: ln, |
|
||||
Handler: HTTPHandler( |
|
||||
UsersHandlerOption(url.UserPassword("admin", "123456")), |
|
||||
), |
|
||||
} |
|
||||
go server.Run() |
|
||||
defer server.Close() |
|
||||
|
|
||||
b.RunParallel(func(pb *testing.PB) { |
|
||||
for pb.Next() { |
|
||||
if err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil { |
|
||||
b.Error(err) |
|
||||
} |
|
||||
} |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
func socks5OverQUICRoundtrip(targetURL string, data []byte, |
|
||||
clientInfo *url.Userinfo, serverInfo []*url.Userinfo) error { |
|
||||
|
|
||||
ln, err := QUICListener("localhost:0", nil) |
|
||||
if err != nil { |
|
||||
return err |
|
||||
} |
|
||||
|
|
||||
client := &Client{ |
|
||||
Connector: SOCKS5Connector(clientInfo), |
|
||||
Transporter: QUICTransporter(nil), |
|
||||
} |
|
||||
|
|
||||
server := &Server{ |
|
||||
Listener: ln, |
|
||||
Handler: SOCKS5Handler( |
|
||||
UsersHandlerOption(serverInfo...), |
|
||||
), |
|
||||
} |
|
||||
|
|
||||
go server.Run() |
|
||||
defer server.Close() |
|
||||
|
|
||||
return proxyRoundtrip(client, server, targetURL, data) |
|
||||
} |
|
||||
|
|
||||
func TestSOCKS5OverQUIC(t *testing.T) { |
|
||||
httpSrv := httptest.NewServer(httpTestHandler) |
|
||||
defer httpSrv.Close() |
|
||||
|
|
||||
sendData := make([]byte, 128) |
|
||||
rand.Read(sendData) |
|
||||
|
|
||||
for i, tc := range socks5ProxyTests { |
|
||||
err := socks5OverQUICRoundtrip(httpSrv.URL, sendData, |
|
||||
tc.cliUser, |
|
||||
tc.srvUsers, |
|
||||
) |
|
||||
if err == nil { |
|
||||
if !tc.pass { |
|
||||
t.Errorf("#%d should failed", i) |
|
||||
} |
|
||||
} else { |
|
||||
// t.Logf("#%d %v", i, err)
|
|
||||
if tc.pass { |
|
||||
t.Errorf("#%d got error: %v", i, err) |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func socks4OverQUICRoundtrip(targetURL string, data []byte) error { |
|
||||
ln, err := QUICListener("localhost:0", nil) |
|
||||
if err != nil { |
|
||||
return err |
|
||||
} |
|
||||
|
|
||||
client := &Client{ |
|
||||
Connector: SOCKS4Connector(), |
|
||||
Transporter: QUICTransporter(nil), |
|
||||
} |
|
||||
|
|
||||
server := &Server{ |
|
||||
Listener: ln, |
|
||||
Handler: SOCKS4Handler(), |
|
||||
} |
|
||||
|
|
||||
go server.Run() |
|
||||
defer server.Close() |
|
||||
|
|
||||
return proxyRoundtrip(client, server, targetURL, data) |
|
||||
} |
|
||||
|
|
||||
func TestSOCKS4OverQUIC(t *testing.T) { |
|
||||
httpSrv := httptest.NewServer(httpTestHandler) |
|
||||
defer httpSrv.Close() |
|
||||
|
|
||||
sendData := make([]byte, 128) |
|
||||
rand.Read(sendData) |
|
||||
|
|
||||
err := socks4OverQUICRoundtrip(httpSrv.URL, sendData) |
|
||||
// t.Logf("#%d %v", i, err)
|
|
||||
if err != nil { |
|
||||
t.Errorf("got error: %v", err) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func socks4aOverQUICRoundtrip(targetURL string, data []byte) error { |
|
||||
ln, err := QUICListener("localhost:0", nil) |
|
||||
if err != nil { |
|
||||
return err |
|
||||
} |
|
||||
|
|
||||
client := &Client{ |
|
||||
Connector: SOCKS4AConnector(), |
|
||||
Transporter: QUICTransporter(nil), |
|
||||
} |
|
||||
|
|
||||
server := &Server{ |
|
||||
Listener: ln, |
|
||||
Handler: SOCKS4Handler(), |
|
||||
} |
|
||||
|
|
||||
go server.Run() |
|
||||
defer server.Close() |
|
||||
|
|
||||
return proxyRoundtrip(client, server, targetURL, data) |
|
||||
} |
|
||||
|
|
||||
func TestSOCKS4AOverQUIC(t *testing.T) { |
|
||||
|
|
||||
httpSrv := httptest.NewServer(httpTestHandler) |
|
||||
defer httpSrv.Close() |
|
||||
|
|
||||
sendData := make([]byte, 128) |
|
||||
rand.Read(sendData) |
|
||||
|
|
||||
err := socks4aOverQUICRoundtrip(httpSrv.URL, sendData) |
|
||||
// t.Logf("#%d %v", i, err)
|
|
||||
if err != nil { |
|
||||
t.Errorf("got error: %v", err) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func ssOverQUICRoundtrip(targetURL string, data []byte, |
|
||||
clientInfo, serverInfo *url.Userinfo) error { |
|
||||
|
|
||||
ln, err := QUICListener("localhost:0", nil) |
|
||||
if err != nil { |
|
||||
return err |
|
||||
} |
|
||||
|
|
||||
client := &Client{ |
|
||||
Connector: ShadowConnector(clientInfo), |
|
||||
Transporter: QUICTransporter(nil), |
|
||||
} |
|
||||
|
|
||||
server := &Server{ |
|
||||
Listener: ln, |
|
||||
Handler: ShadowHandler( |
|
||||
UsersHandlerOption(serverInfo), |
|
||||
), |
|
||||
} |
|
||||
|
|
||||
go server.Run() |
|
||||
defer server.Close() |
|
||||
|
|
||||
return proxyRoundtrip(client, server, targetURL, data) |
|
||||
} |
|
||||
|
|
||||
func TestSSOverQUIC(t *testing.T) { |
|
||||
httpSrv := httptest.NewServer(httpTestHandler) |
|
||||
defer httpSrv.Close() |
|
||||
|
|
||||
sendData := make([]byte, 128) |
|
||||
rand.Read(sendData) |
|
||||
|
|
||||
for i, tc := range ssProxyTests { |
|
||||
err := ssOverQUICRoundtrip(httpSrv.URL, sendData, |
|
||||
tc.clientCipher, |
|
||||
tc.serverCipher, |
|
||||
) |
|
||||
if err == nil { |
|
||||
if !tc.pass { |
|
||||
t.Errorf("#%d should failed", i) |
|
||||
} |
|
||||
} else { |
|
||||
// t.Logf("#%d %v", i, err)
|
|
||||
if tc.pass { |
|
||||
t.Errorf("#%d got error: %v", i, err) |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func sniOverQUICRoundtrip(targetURL string, data []byte, host string) error { |
|
||||
ln, err := QUICListener("localhost:0", nil) |
|
||||
if err != nil { |
|
||||
return err |
|
||||
} |
|
||||
|
|
||||
u, err := url.Parse(targetURL) |
|
||||
if err != nil { |
|
||||
return err |
|
||||
} |
|
||||
|
|
||||
client := &Client{ |
|
||||
Connector: SNIConnector(host), |
|
||||
Transporter: QUICTransporter(nil), |
|
||||
} |
|
||||
|
|
||||
server := &Server{ |
|
||||
Listener: ln, |
|
||||
Handler: SNIHandler(HostHandlerOption(u.Host)), |
|
||||
} |
|
||||
|
|
||||
go server.Run() |
|
||||
defer server.Close() |
|
||||
|
|
||||
return sniRoundtrip(client, server, targetURL, data) |
|
||||
} |
|
||||
|
|
||||
func TestSNIOverQUIC(t *testing.T) { |
|
||||
httpSrv := httptest.NewServer(httpTestHandler) |
|
||||
defer httpSrv.Close() |
|
||||
httpsSrv := httptest.NewTLSServer(httpTestHandler) |
|
||||
defer httpsSrv.Close() |
|
||||
|
|
||||
sendData := make([]byte, 128) |
|
||||
rand.Read(sendData) |
|
||||
|
|
||||
var sniProxyTests = []struct { |
|
||||
targetURL string |
|
||||
host string |
|
||||
pass bool |
|
||||
}{ |
|
||||
{httpSrv.URL, "", true}, |
|
||||
{httpSrv.URL, "example.com", true}, |
|
||||
{httpsSrv.URL, "", true}, |
|
||||
{httpsSrv.URL, "example.com", true}, |
|
||||
} |
|
||||
|
|
||||
for i, tc := range sniProxyTests { |
|
||||
tc := tc |
|
||||
t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) { |
|
||||
err := sniOverQUICRoundtrip(tc.targetURL, sendData, tc.host) |
|
||||
if err == nil { |
|
||||
if !tc.pass { |
|
||||
t.Errorf("#%d should failed", i) |
|
||||
} |
|
||||
} else { |
|
||||
// t.Logf("#%d %v", i, err)
|
|
||||
if tc.pass { |
|
||||
t.Errorf("#%d got error: %v", i, err) |
|
||||
} |
|
||||
} |
|
||||
}) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func quicForwardTunnelRoundtrip(targetURL string, data []byte) error { |
|
||||
ln, err := QUICListener("localhost:0", nil) |
|
||||
if err != nil { |
|
||||
return err |
|
||||
} |
|
||||
|
|
||||
u, err := url.Parse(targetURL) |
|
||||
if err != nil { |
|
||||
return err |
|
||||
} |
|
||||
|
|
||||
client := &Client{ |
|
||||
Connector: ForwardConnector(), |
|
||||
Transporter: QUICTransporter(nil), |
|
||||
} |
|
||||
|
|
||||
server := &Server{ |
|
||||
Listener: ln, |
|
||||
Handler: TCPDirectForwardHandler(u.Host), |
|
||||
} |
|
||||
server.Handler.Init() |
|
||||
|
|
||||
go server.Run() |
|
||||
defer server.Close() |
|
||||
|
|
||||
return proxyRoundtrip(client, server, targetURL, data) |
|
||||
} |
|
||||
|
|
||||
func TestQUICForwardTunnel(t *testing.T) { |
|
||||
httpSrv := httptest.NewServer(httpTestHandler) |
|
||||
defer httpSrv.Close() |
|
||||
|
|
||||
sendData := make([]byte, 128) |
|
||||
rand.Read(sendData) |
|
||||
|
|
||||
err := quicForwardTunnelRoundtrip(httpSrv.URL, sendData) |
|
||||
if err != nil { |
|
||||
t.Error(err) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func httpOverCipherQUICRoundtrip(targetURL string, data []byte, |
|
||||
clientInfo *url.Userinfo, serverInfo []*url.Userinfo) error { |
|
||||
|
|
||||
sum := sha256.Sum256([]byte("12345678")) |
|
||||
cfg := &QUICConfig{ |
|
||||
Key: sum[:], |
|
||||
} |
|
||||
ln, err := QUICListener("localhost:0", cfg) |
|
||||
if err != nil { |
|
||||
return err |
|
||||
} |
|
||||
|
|
||||
client := &Client{ |
|
||||
Connector: HTTPConnector(clientInfo), |
|
||||
Transporter: QUICTransporter(cfg), |
|
||||
} |
|
||||
|
|
||||
server := &Server{ |
|
||||
Listener: ln, |
|
||||
Handler: HTTPHandler( |
|
||||
UsersHandlerOption(serverInfo...), |
|
||||
), |
|
||||
} |
|
||||
|
|
||||
go server.Run() |
|
||||
defer server.Close() |
|
||||
|
|
||||
return proxyRoundtrip(client, server, targetURL, data) |
|
||||
} |
|
||||
|
|
||||
func TestHTTPOverCipherQUIC(t *testing.T) { |
|
||||
httpSrv := httptest.NewServer(httpTestHandler) |
|
||||
defer httpSrv.Close() |
|
||||
|
|
||||
sendData := make([]byte, 128) |
|
||||
rand.Read(sendData) |
|
||||
|
|
||||
for i, tc := range httpProxyTests { |
|
||||
err := httpOverCipherQUICRoundtrip(httpSrv.URL, sendData, tc.cliUser, tc.srvUsers) |
|
||||
if err == nil { |
|
||||
if tc.errStr != "" { |
|
||||
t.Errorf("#%d should failed with error %s", i, tc.errStr) |
|
||||
} |
|
||||
} else { |
|
||||
if tc.errStr == "" { |
|
||||
t.Errorf("#%d got error %v", i, err) |
|
||||
} |
|
||||
if err.Error() != tc.errStr { |
|
||||
t.Errorf("#%d got error %v, want %v", i, err, tc.errStr) |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
Loading…
Reference in new issue