Accept HTTP1 in naive inbound

This commit is contained in:
世界 2022-08-23 13:22:03 +08:00
parent c9b7acd22c
commit 9edfe7d9d3
No known key found for this signature in database
GPG key ID: CD109927C34A63C4
12 changed files with 356 additions and 118 deletions

View file

@ -57,7 +57,7 @@ test:
@go test -v . && \ @go test -v . && \
pushd test && \ pushd test && \
go mod tidy && \ go mod tidy && \
go test -v -tags '$(TAGS)' . && \ go test -v -tags with_quic,with_wireguard,with_grpc . && \
popd popd
clean: clean:

View file

@ -212,6 +212,15 @@ func (a *myInboundAdapter) injectTCP(conn net.Conn) {
} }
} }
func (a *myInboundAdapter) routeTCP(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) {
a.logger.InfoContext(ctx, "inbound connection from ", metadata.Source)
hErr := a.newConnection(ctx, conn, metadata)
if hErr != nil {
conn.Close()
a.NewError(ctx, E.Cause(hErr, "process connection from ", metadata.Source))
}
}
func (a *myInboundAdapter) loopUDPIn() { func (a *myInboundAdapter) loopUDPIn() {
defer close(a.packetOutboundClosed) defer close(a.packetOutboundClosed)
_buffer := buf.StackNewPacket() _buffer := buf.StackNewPacket()

View file

@ -2,13 +2,13 @@ package inbound
import ( import (
"context" "context"
"crypto/tls"
"encoding/base64" "encoding/base64"
"encoding/binary" "encoding/binary"
"io" "io"
"math/rand" "math/rand"
"net" "net"
"net/http" "net/http"
"net/netip"
"os" "os"
"strings" "strings"
"time" "time"
@ -17,13 +17,11 @@ import (
C "github.com/sagernet/sing-box/constant" C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-dns"
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/auth" "github.com/sagernet/sing/common/auth"
"github.com/sagernet/sing/common/buf" "github.com/sagernet/sing/common/buf"
"github.com/sagernet/sing/common/bufio" "github.com/sagernet/sing/common/bufio"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
F "github.com/sagernet/sing/common/format"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/common/rw" "github.com/sagernet/sing/common/rw"
@ -32,12 +30,7 @@ import (
var _ adapter.Inbound = (*Naive)(nil) var _ adapter.Inbound = (*Naive)(nil)
type Naive struct { type Naive struct {
ctx context.Context myInboundAdapter
router adapter.Router
logger log.ContextLogger
tag string
listenOptions option.ListenOptions
network []string
authenticator auth.Authenticator authenticator auth.Authenticator
tlsConfig *TLSConfig tlsConfig *TLSConfig
httpServer *http.Server httpServer *http.Server
@ -46,76 +39,69 @@ type Naive struct {
func NewNaive(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.NaiveInboundOptions) (*Naive, error) { func NewNaive(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.NaiveInboundOptions) (*Naive, error) {
inbound := &Naive{ inbound := &Naive{
ctx: ctx, myInboundAdapter: myInboundAdapter{
router: router, protocol: C.TypeNaive,
logger: logger, network: options.Network.Build(),
tag: tag, ctx: ctx,
listenOptions: options.ListenOptions, router: router,
network: options.Network.Build(), logger: logger,
tag: tag,
listenOptions: options.ListenOptions,
},
authenticator: auth.NewAuthenticator(options.Users), authenticator: auth.NewAuthenticator(options.Users),
} }
if options.TLS == nil || !options.TLS.Enabled { if common.Contains(inbound.network, N.NetworkUDP) {
return nil, C.ErrTLSRequired if options.TLS == nil || !options.TLS.Enabled {
return nil, E.New("TLS is required for QUIC server")
}
} }
if len(options.Users) == 0 { if len(options.Users) == 0 {
return nil, E.New("missing users") return nil, E.New("missing users")
} }
tlsConfig, err := NewTLSConfig(ctx, logger, common.PtrValueOrDefault(options.TLS)) if options.TLS != nil {
if err != nil { tlsConfig, err := NewTLSConfig(ctx, logger, common.PtrValueOrDefault(options.TLS))
return nil, err if err != nil {
return nil, err
}
inbound.tlsConfig = tlsConfig
} }
inbound.tlsConfig = tlsConfig
return inbound, nil return inbound, nil
} }
func (n *Naive) Type() string {
return C.TypeNaive
}
func (n *Naive) Tag() string {
return n.tag
}
func (n *Naive) Start() error { func (n *Naive) Start() error {
err := n.tlsConfig.Start() var tlsConfig *tls.Config
if err != nil { if n.tlsConfig != nil {
return E.Cause(err, "create TLS config") err := n.tlsConfig.Start()
} if err != nil {
return E.Cause(err, "create TLS config")
var listenAddr string
if nAddr := netip.Addr(n.listenOptions.Listen); nAddr.IsValid() {
if n.listenOptions.ListenPort != 0 {
listenAddr = M.SocksaddrFrom(netip.Addr(n.listenOptions.Listen), n.listenOptions.ListenPort).String()
} else {
listenAddr = net.JoinHostPort(nAddr.String(), ":https")
} }
} else if n.listenOptions.ListenPort != 0 { tlsConfig = n.tlsConfig.Config()
listenAddr = ":" + F.ToString(n.listenOptions.ListenPort)
} else {
listenAddr = ":https"
} }
if common.Contains(n.network, N.NetworkTCP) { if common.Contains(n.network, N.NetworkTCP) {
n.httpServer = &http.Server{ tcpListener, err := n.ListenTCP()
Handler: n,
TLSConfig: n.tlsConfig.Config(),
}
tcpListener, err := net.Listen(M.NetworkFromNetAddr("tcp", netip.Addr(n.listenOptions.Listen)), listenAddr)
if err != nil { if err != nil {
return err return err
} }
n.logger.Info("tcp server started at ", tcpListener.Addr()) n.httpServer = &http.Server{
Handler: n,
TLSConfig: tlsConfig,
}
go func() { go func() {
sErr := n.httpServer.ServeTLS(tcpListener, "", "") var sErr error
if sErr == http.ErrServerClosed { if tlsConfig != nil {
} else if sErr != nil { sErr = n.httpServer.ServeTLS(tcpListener, "", "")
} else {
sErr = n.httpServer.Serve(tcpListener)
}
if sErr != nil && !E.IsClosedOrCanceled(sErr) {
n.logger.Error("http server serve error: ", sErr) n.logger.Error("http server serve error: ", sErr)
} }
}() }()
} }
if common.Contains(n.network, N.NetworkUDP) { if common.Contains(n.network, N.NetworkUDP) {
err = n.configureHTTP3Listener(listenAddr) err := n.configureHTTP3Listener()
if !C.QUIC_AVAILABLE && len(n.network) > 1 { if !C.QUIC_AVAILABLE && len(n.network) > 1 {
log.Warn(E.Cause(err, "naive http3 disabled")) log.Warn(E.Cause(err, "naive http3 disabled"))
} else if err != nil { } else if err != nil {
@ -128,6 +114,7 @@ func (n *Naive) Start() error {
func (n *Naive) Close() error { func (n *Naive) Close() error {
return common.Close( return common.Close(
&n.myInboundAdapter,
common.PtrOrNil(n.httpServer), common.PtrOrNil(n.httpServer),
n.h3Server, n.h3Server,
common.PtrOrNil(n.tlsConfig), common.PtrOrNil(n.tlsConfig),
@ -137,12 +124,12 @@ func (n *Naive) Close() error {
func (n *Naive) ServeHTTP(writer http.ResponseWriter, request *http.Request) { func (n *Naive) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
ctx := log.ContextWithNewID(request.Context()) ctx := log.ContextWithNewID(request.Context())
if request.Method != "CONNECT" { if request.Method != "CONNECT" {
n.logger.ErrorContext(ctx, "bad request: not connect")
rejectHTTP(writer, http.StatusBadRequest) rejectHTTP(writer, http.StatusBadRequest)
n.badRequest(ctx, request, E.New("not CONNECT request"))
return return
} else if request.Header.Get("Padding") == "" { } else if request.Header.Get("Padding") == "" {
n.logger.ErrorContext(ctx, "bad request: missing padding")
rejectHTTP(writer, http.StatusBadRequest) rejectHTTP(writer, http.StatusBadRequest)
n.badRequest(ctx, request, E.New("missing naive padding"))
return return
} }
var authOk bool var authOk bool
@ -156,46 +143,41 @@ func (n *Naive) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
} }
} }
if !authOk { if !authOk {
n.logger.ErrorContext(ctx, "bad request: authorization failed")
rejectHTTP(writer, http.StatusProxyAuthRequired) rejectHTTP(writer, http.StatusProxyAuthRequired)
n.badRequest(ctx, request, E.New("authorization failed"))
return return
} }
writer.Header().Set("Padding", generateNaivePaddingHeader()) writer.Header().Set("Padding", generateNaivePaddingHeader())
writer.WriteHeader(http.StatusOK) writer.WriteHeader(http.StatusOK)
writer.(http.Flusher).Flush() writer.(http.Flusher).Flush()
if request.ProtoMajor == 1 {
n.logger.ErrorContext(ctx, "bad request: http1")
rejectHTTP(writer, http.StatusBadRequest)
return
}
hostPort := request.URL.Host hostPort := request.URL.Host
if hostPort == "" { if hostPort == "" {
hostPort = request.Host hostPort = request.Host
} }
source := M.ParseSocksaddr(request.RemoteAddr) source := M.ParseSocksaddr(request.RemoteAddr)
destination := M.ParseSocksaddr(hostPort) destination := M.ParseSocksaddr(hostPort)
n.newConnection(ctx, &naivePaddingConn{reader: request.Body, writer: writer, flusher: writer.(http.Flusher)}, source, destination)
if hijacker, isHijacker := writer.(http.Hijacker); isHijacker {
conn, _, err := hijacker.Hijack()
if err != nil {
return
}
n.newConnection(ctx, &naiveH1Conn{Conn: conn}, source, destination)
} else {
n.newConnection(ctx, &naiveH2Conn{reader: request.Body, writer: writer, flusher: writer.(http.Flusher)}, source, destination)
}
} }
func (n *Naive) newConnection(ctx context.Context, conn net.Conn, source, destination M.Socksaddr) { func (n *Naive) newConnection(ctx context.Context, conn net.Conn, source, destination M.Socksaddr) {
var metadata adapter.InboundContext metadata := n.createMetadata(conn)
metadata.Inbound = n.tag
metadata.InboundType = C.TypeNaive
metadata.SniffEnabled = n.listenOptions.SniffEnabled
metadata.SniffOverrideDestination = n.listenOptions.SniffOverrideDestination
metadata.DomainStrategy = dns.DomainStrategy(n.listenOptions.DomainStrategy)
metadata.Network = N.NetworkTCP
metadata.Source = source metadata.Source = source
metadata.Destination = destination metadata.Destination = destination
n.logger.InfoContext(ctx, "inbound connection from ", metadata.Source) n.routeTCP(ctx, conn, metadata)
n.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination) }
hErr := n.router.RouteConnection(ctx, conn, metadata)
if hErr != nil { func (n *Naive) badRequest(ctx context.Context, request *http.Request, err error) {
conn.Close() n.NewError(ctx, E.Cause(err, "process connection from ", request.RemoteAddr))
NewError(n.logger, ctx, E.Cause(hErr, "process connection from ", metadata.Source))
}
} }
func rejectHTTP(writer http.ResponseWriter, statusCode int) { func rejectHTTP(writer http.ResponseWriter, statusCode int) {
@ -232,9 +214,174 @@ func generateNaivePaddingHeader() string {
const kFirstPaddings = 8 const kFirstPaddings = 8
var _ net.Conn = (*naivePaddingConn)(nil) type naiveH1Conn struct {
net.Conn
readPadding int
writePadding int
readRemaining int
paddingRemaining int
}
type naivePaddingConn struct { func (c *naiveH1Conn) Read(p []byte) (n int, err error) {
n, err = c.read(p)
return n, wrapHttpError(err)
}
func (c *naiveH1Conn) read(p []byte) (n int, err error) {
if c.readRemaining > 0 {
if len(p) > c.readRemaining {
p = p[:c.readRemaining]
}
n, err = c.Conn.Read(p)
if err != nil {
return
}
c.readRemaining -= n
return
}
if c.paddingRemaining > 0 {
err = rw.SkipN(c.Conn, c.paddingRemaining)
if err != nil {
return
}
c.readRemaining = 0
}
if c.readPadding < kFirstPaddings {
paddingHdr := p[:3]
_, err = io.ReadFull(c.Conn, paddingHdr)
if err != nil {
return
}
originalDataSize := int(binary.BigEndian.Uint16(paddingHdr[:2]))
paddingSize := int(paddingHdr[2])
if len(p) > originalDataSize {
p = p[:originalDataSize]
}
n, err = c.Conn.Read(p)
if err != nil {
return
}
c.readPadding++
c.readRemaining = originalDataSize - n
c.paddingRemaining = paddingSize
return
}
return c.Conn.Read(p)
}
func (c *naiveH1Conn) Write(p []byte) (n int, err error) {
for pLen := len(p); pLen > 0; {
var data []byte
if pLen > 65535 {
data = p[:65535]
p = p[65535:]
pLen -= 65535
} else {
data = p
pLen = 0
}
var writeN int
writeN, err = c.write(data)
n += writeN
if err != nil {
break
}
}
return n, wrapHttpError(err)
}
func (c *naiveH1Conn) write(p []byte) (n int, err error) {
if c.writePadding < kFirstPaddings {
paddingSize := rand.Intn(256)
_buffer := buf.StackNewSize(3 + len(p) + paddingSize)
defer common.KeepAlive(_buffer)
buffer := common.Dup(_buffer)
defer buffer.Release()
header := buffer.Extend(3)
binary.BigEndian.PutUint16(header, uint16(len(p)))
header[2] = byte(paddingSize)
common.Must1(buffer.Write(p))
_, err = c.Conn.Write(buffer.Bytes())
if err == nil {
n = len(p)
}
c.writePadding++
return
}
return c.Conn.Write(p)
}
func (c *naiveH1Conn) FrontHeadroom() int {
if c.writePadding < kFirstPaddings {
return 3
}
return 0
}
func (c *naiveH1Conn) RearHeadroom() int {
if c.writePadding < kFirstPaddings {
return 255
}
return 0
}
func (c *naiveH1Conn) WriterMTU() int {
if c.writePadding < kFirstPaddings {
return 65535
}
return 0
}
func (c *naiveH1Conn) WriteBuffer(buffer *buf.Buffer) error {
defer buffer.Release()
if c.writePadding < kFirstPaddings {
bufferLen := buffer.Len()
if bufferLen > 65535 {
return common.Error(c.Write(buffer.Bytes()))
}
paddingSize := rand.Intn(256)
header := buffer.ExtendHeader(3)
binary.BigEndian.PutUint16(header, uint16(bufferLen))
header[2] = byte(paddingSize)
buffer.Extend(paddingSize)
c.writePadding++
}
return wrapHttpError(common.Error(c.Conn.Write(buffer.Bytes())))
}
func (c *naiveH1Conn) WriteTo(w io.Writer) (n int64, err error) {
if c.readPadding < kFirstPaddings {
n, err = bufio.WriteToN(c, w, kFirstPaddings-c.readPadding)
} else {
n, err = bufio.Copy(w, c.Conn)
}
return n, wrapHttpError(err)
}
func (c *naiveH1Conn) ReadFrom(r io.Reader) (n int64, err error) {
if c.writePadding < kFirstPaddings {
n, err = bufio.ReadFromN(c, r, kFirstPaddings-c.writePadding)
} else {
n, err = bufio.Copy(c.Conn, r)
}
return n, wrapHttpError(err)
}
func (c *naiveH1Conn) Upstream() any {
return c.Conn
}
func (c *naiveH1Conn) ReaderReplaceable() bool {
return c.readRemaining == kFirstPaddings
}
func (c *naiveH1Conn) WriterReplaceable() bool {
return c.writePadding == kFirstPaddings
}
type naiveH2Conn struct {
reader io.Reader reader io.Reader
writer io.Writer writer io.Writer
flusher http.Flusher flusher http.Flusher
@ -245,12 +392,12 @@ type naivePaddingConn struct {
paddingRemaining int paddingRemaining int
} }
func (c *naivePaddingConn) Read(p []byte) (n int, err error) { func (c *naiveH2Conn) Read(p []byte) (n int, err error) {
n, err = c.read(p) n, err = c.read(p)
return n, wrapHttpError(err) return n, wrapHttpError(err)
} }
func (c *naivePaddingConn) read(p []byte) (n int, err error) { func (c *naiveH2Conn) read(p []byte) (n int, err error) {
if c.readRemaining > 0 { if c.readRemaining > 0 {
if len(p) > c.readRemaining { if len(p) > c.readRemaining {
p = p[:c.readRemaining] p = p[:c.readRemaining]
@ -292,7 +439,7 @@ func (c *naivePaddingConn) read(p []byte) (n int, err error) {
return c.reader.Read(p) return c.reader.Read(p)
} }
func (c *naivePaddingConn) Write(p []byte) (n int, err error) { func (c *naiveH2Conn) Write(p []byte) (n int, err error) {
for pLen := len(p); pLen > 0; { for pLen := len(p); pLen > 0; {
var data []byte var data []byte
if pLen > 65535 { if pLen > 65535 {
@ -316,7 +463,7 @@ func (c *naivePaddingConn) Write(p []byte) (n int, err error) {
return n, wrapHttpError(err) return n, wrapHttpError(err)
} }
func (c *naivePaddingConn) write(p []byte) (n int, err error) { func (c *naiveH2Conn) write(p []byte) (n int, err error) {
if c.writePadding < kFirstPaddings { if c.writePadding < kFirstPaddings {
paddingSize := rand.Intn(256) paddingSize := rand.Intn(256)
@ -339,28 +486,28 @@ func (c *naivePaddingConn) write(p []byte) (n int, err error) {
return c.writer.Write(p) return c.writer.Write(p)
} }
func (c *naivePaddingConn) FrontHeadroom() int { func (c *naiveH2Conn) FrontHeadroom() int {
if c.writePadding < kFirstPaddings { if c.writePadding < kFirstPaddings {
return 3 return 3
} }
return 0 return 0
} }
func (c *naivePaddingConn) RearHeadroom() int { func (c *naiveH2Conn) RearHeadroom() int {
if c.writePadding < kFirstPaddings { if c.writePadding < kFirstPaddings {
return 255 return 255
} }
return 0 return 0
} }
func (c *naivePaddingConn) WriterMTU() int { func (c *naiveH2Conn) WriterMTU() int {
if c.writePadding < kFirstPaddings { if c.writePadding < kFirstPaddings {
return 65535 return 65535
} }
return 0 return 0
} }
func (c *naivePaddingConn) WriteBuffer(buffer *buf.Buffer) error { func (c *naiveH2Conn) WriteBuffer(buffer *buf.Buffer) error {
defer buffer.Release() defer buffer.Release()
if c.writePadding < kFirstPaddings { if c.writePadding < kFirstPaddings {
bufferLen := buffer.Len() bufferLen := buffer.Len()
@ -381,7 +528,7 @@ func (c *naivePaddingConn) WriteBuffer(buffer *buf.Buffer) error {
return wrapHttpError(err) return wrapHttpError(err)
} }
func (c *naivePaddingConn) WriteTo(w io.Writer) (n int64, err error) { func (c *naiveH2Conn) WriteTo(w io.Writer) (n int64, err error) {
if c.readPadding < kFirstPaddings { if c.readPadding < kFirstPaddings {
n, err = bufio.WriteToN(c, w, kFirstPaddings-c.readPadding) n, err = bufio.WriteToN(c, w, kFirstPaddings-c.readPadding)
} else { } else {
@ -390,7 +537,7 @@ func (c *naivePaddingConn) WriteTo(w io.Writer) (n int64, err error) {
return n, wrapHttpError(err) return n, wrapHttpError(err)
} }
func (c *naivePaddingConn) ReadFrom(r io.Reader) (n int64, err error) { func (c *naiveH2Conn) ReadFrom(r io.Reader) (n int64, err error) {
if c.writePadding < kFirstPaddings { if c.writePadding < kFirstPaddings {
n, err = bufio.ReadFromN(c, r, kFirstPaddings-c.writePadding) n, err = bufio.ReadFromN(c, r, kFirstPaddings-c.writePadding)
} else { } else {
@ -399,33 +546,49 @@ func (c *naivePaddingConn) ReadFrom(r io.Reader) (n int64, err error) {
return n, wrapHttpError(err) return n, wrapHttpError(err)
} }
func (c *naivePaddingConn) Close() error { func (c *naiveH2Conn) Close() error {
return common.Close( return common.Close(
c.reader, c.reader,
c.writer, c.writer,
) )
} }
func (c *naivePaddingConn) LocalAddr() net.Addr { func (c *naiveH2Conn) LocalAddr() net.Addr {
return nil return nil
} }
func (c *naivePaddingConn) RemoteAddr() net.Addr { func (c *naiveH2Conn) RemoteAddr() net.Addr {
return c.rAddr return c.rAddr
} }
func (c *naivePaddingConn) SetDeadline(t time.Time) error { func (c *naiveH2Conn) SetDeadline(t time.Time) error {
return os.ErrInvalid return os.ErrInvalid
} }
func (c *naivePaddingConn) SetReadDeadline(t time.Time) error { func (c *naiveH2Conn) SetReadDeadline(t time.Time) error {
return os.ErrInvalid return os.ErrInvalid
} }
func (c *naivePaddingConn) SetWriteDeadline(t time.Time) error { func (c *naiveH2Conn) SetWriteDeadline(t time.Time) error {
return os.ErrInvalid return os.ErrInvalid
} }
func (c *naiveH2Conn) UpstreamReader() any {
return c.reader
}
func (c *naiveH2Conn) UpstreamWriter() any {
return c.writer
}
func (c *naiveH2Conn) ReaderReplaceable() bool {
return c.readRemaining == kFirstPaddings
}
func (c *naiveH2Conn) WriterReplaceable() bool {
return c.writePadding == kFirstPaddings
}
func wrapHttpError(err error) error { func wrapHttpError(err error) error {
if err == nil { if err == nil {
return err return err

View file

@ -3,34 +3,26 @@
package inbound package inbound
import ( import (
"net"
"net/netip"
"github.com/sagernet/quic-go"
"github.com/sagernet/quic-go/http3" "github.com/sagernet/quic-go/http3"
M "github.com/sagernet/sing/common/metadata" E "github.com/sagernet/sing/common/exceptions"
) )
func (n *Naive) configureHTTP3Listener(listenAddr string) error { func (n *Naive) configureHTTP3Listener() error {
h3Server := &http3.Server{ h3Server := &http3.Server{
Port: int(n.listenOptions.ListenPort), Port: int(n.listenOptions.ListenPort),
TLSConfig: n.tlsConfig.Config(), TLSConfig: n.tlsConfig.Config(),
Handler: n, Handler: n,
} }
udpListener, err := net.ListenPacket(M.NetworkFromNetAddr("udp", netip.Addr(n.listenOptions.Listen)), listenAddr) udpConn, err := n.ListenUDP()
if err != nil { if err != nil {
return err return err
} }
n.logger.Info("udp server started at ", udpListener.LocalAddr())
go func() { go func() {
sErr := h3Server.Serve(udpListener) sErr := h3Server.Serve(udpConn)
if sErr == quic.ErrServerClosed { udpConn.Close()
udpListener.Close() if sErr != nil && !E.IsClosedOrCanceled(sErr) {
return
} else if sErr != nil {
n.logger.Error("http3 server serve error: ", sErr) n.logger.Error("http3 server serve error: ", sErr)
} }
}() }()

View file

@ -6,6 +6,6 @@ import (
C "github.com/sagernet/sing-box/constant" C "github.com/sagernet/sing-box/constant"
) )
func (n *Naive) configureHTTP3Listener(listenAddr string) error { func (n *Naive) configureHTTP3Listener() error {
return C.ErrQUICNotIncluded return C.ErrQUICNotIncluded
} }

View file

@ -35,6 +35,7 @@ const (
ImageNaive = "pocat/naiveproxy:client" ImageNaive = "pocat/naiveproxy:client"
ImageBoringTun = "ghcr.io/ntkme/boringtun:edge" ImageBoringTun = "ghcr.io/ntkme/boringtun:edge"
ImageHysteria = "tobyxdd/hysteria:latest" ImageHysteria = "tobyxdd/hysteria:latest"
ImageNginx = "nginx:stable"
) )
var allImages = []string{ var allImages = []string{
@ -45,6 +46,7 @@ var allImages = []string{
ImageNaive, ImageNaive,
ImageBoringTun, ImageBoringTun,
ImageHysteria, ImageHysteria,
// ImageNginx,
} }
var localIP = netip.MustParseAddr("127.0.0.1") var localIP = netip.MustParseAddr("127.0.0.1")

View file

@ -0,0 +1,22 @@
server {
listen 10000 ssl http2;
listen [::]:10000 ssl http2;
server_name example.org;
ssl_certificate /etc/nginx/cert.pem;
ssl_certificate_key /etc/nginx/key.pem;
ssl_session_timeout 1d;
ssl_session_cache shared:MozSSL:10m; # about 40000 sessions
ssl_session_tickets off;
# modern configuration
ssl_protocols TLSv1.3;
ssl_prefer_server_ciphers off;
# HSTS (ngx_http_headers_module is required) (63072000 seconds)
add_header Strict-Transport-Security "max-age=63072000" always;
location / {
proxy_pass http://127.0.0.1:10003;
}
}

View file

@ -15,7 +15,7 @@ func TestHysteriaSelf(t *testing.T) {
_, certPem, keyPem := createSelfSignedCertificate(t, "example.org") _, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
startInstance(t, option.Options{ startInstance(t, option.Options{
Log: &option.LogOptions{ Log: &option.LogOptions{
Level: "trace", Level: "error",
}, },
Inbounds: []option.Inbound{ Inbounds: []option.Inbound{
{ {
@ -93,7 +93,7 @@ func TestHysteriaInbound(t *testing.T) {
caPem, certPem, keyPem := createSelfSignedCertificate(t, "example.org") caPem, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
startInstance(t, option.Options{ startInstance(t, option.Options{
Log: &option.LogOptions{ Log: &option.LogOptions{
Level: "trace", Level: "error",
}, },
Inbounds: []option.Inbound{ Inbounds: []option.Inbound{
{ {
@ -146,7 +146,7 @@ func TestHysteriaOutbound(t *testing.T) {
}) })
startInstance(t, option.Options{ startInstance(t, option.Options{
Log: &option.LogOptions{ Log: &option.LogOptions{
Level: "trace", Level: "error",
}, },
Inbounds: []option.Inbound{ Inbounds: []option.Inbound{
{ {

View file

@ -10,6 +10,55 @@ import (
"github.com/sagernet/sing/common/network" "github.com/sagernet/sing/common/network"
) )
// FIXME: nginx do not support CONNECT
func _TestNaiveInboundWithNingx(t *testing.T) {
caPem, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
startInstance(t, option.Options{
Log: &option.LogOptions{
Level: "trace",
},
Inbounds: []option.Inbound{
{
Type: C.TypeNaive,
NaiveOptions: option.NaiveInboundOptions{
ListenOptions: option.ListenOptions{
Listen: option.ListenAddress(netip.IPv4Unspecified()),
ListenPort: otherPort,
},
Users: []auth.User{
{
Username: "sekai",
Password: "password",
},
},
Network: network.NetworkTCP,
},
},
},
})
startDockerContainer(t, DockerOptions{
Image: ImageNginx,
Ports: []uint16{serverPort, otherPort},
Bind: map[string]string{
"naive-nginx.conf": "/etc/nginx/conf.d/naive.conf",
certPem: "/etc/nginx/cert.pem",
keyPem: "/etc/nginx/key.pem",
},
})
startDockerContainer(t, DockerOptions{
Image: ImageNaive,
Ports: []uint16{serverPort, clientPort},
Bind: map[string]string{
"naive.json": "/etc/naiveproxy/config.json",
caPem: "/etc/naiveproxy/ca.pem",
},
Env: []string{
"SSL_CERT_FILE=/etc/naiveproxy/ca.pem",
},
})
testTCP(t, clientPort, testPort)
}
func TestNaiveInbound(t *testing.T) { func TestNaiveInbound(t *testing.T) {
caPem, certPem, keyPem := createSelfSignedCertificate(t, "example.org") caPem, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
startInstance(t, option.Options{ startInstance(t, option.Options{

View file

@ -18,6 +18,7 @@ const (
serverPort uint16 = 10000 + iota serverPort uint16 = 10000 + iota
clientPort clientPort
testPort testPort
otherPort
) )
func TestShadowsocks(t *testing.T) { func TestShadowsocks(t *testing.T) {
@ -199,7 +200,7 @@ func TestShadowsocksUoT(t *testing.T) {
password := mkBase64(t, 16) password := mkBase64(t, 16)
startInstance(t, option.Options{ startInstance(t, option.Options{
Log: &option.LogOptions{ Log: &option.LogOptions{
Level: "trace", Level: "error",
}, },
Inbounds: []option.Inbound{ Inbounds: []option.Inbound{
{ {

View file

@ -310,7 +310,7 @@ func testV2RayTransportNOTLSSelf(t *testing.T, transport *option.V2RayTransportO
require.NoError(t, err) require.NoError(t, err)
startInstance(t, option.Options{ startInstance(t, option.Options{
Log: &option.LogOptions{ Log: &option.LogOptions{
Level: "trace", Level: "error",
}, },
Inbounds: []option.Inbound{ Inbounds: []option.Inbound{
{ {

View file

@ -22,7 +22,7 @@ func TestWireGuard(t *testing.T) {
time.Sleep(5 * time.Second) time.Sleep(5 * time.Second)
startInstance(t, option.Options{ startInstance(t, option.Options{
Log: &option.LogOptions{ Log: &option.LogOptions{
Level: "trace", Level: "error",
}, },
Inbounds: []option.Inbound{ Inbounds: []option.Inbound{
{ {