Compare commits

...

6 commits

Author SHA1 Message Date
世界
297f0b2a2b
Update quic-go to v0.49.0 2025-02-06 08:47:28 +08:00
世界
da0a8689f2
Update the behavior of ignore_client_bandwidth 2025-01-07 12:37:17 +08:00
世界
06d01d20a7
Add port hopping for hysteria2 2025-01-07 12:37:17 +08:00
世界
20f4b153bc
Update handler usages 2025-01-07 12:37:17 +08:00
世界
2419e1d7f7
Fix source not unwrapped 2025-01-07 12:25:39 +08:00
世界
8356fa4ad8
Update dependencies 2024-11-18 12:49:51 +08:00
10 changed files with 397 additions and 71 deletions

14
go.mod
View file

@ -4,9 +4,9 @@ go 1.20
require (
github.com/gofrs/uuid/v5 v5.3.0
github.com/sagernet/quic-go v0.48.1-beta.1
github.com/sagernet/sing v0.5.0
golang.org/x/crypto v0.28.0
github.com/sagernet/quic-go v0.49.0-beta.1
github.com/sagernet/sing v0.6.0-beta.9
golang.org/x/crypto v0.32.0
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842
)
@ -16,8 +16,10 @@ require (
github.com/onsi/ginkgo/v2 v2.9.7 // indirect
github.com/quic-go/qpack v0.4.0 // indirect
github.com/quic-go/qtls-go1-20 v0.4.1 // indirect
golang.org/x/net v0.30.0 // indirect
golang.org/x/sys v0.26.0 // indirect
golang.org/x/text v0.19.0 // indirect
golang.org/x/mod v0.20.0 // indirect
golang.org/x/net v0.34.0 // indirect
golang.org/x/sync v0.10.0 // indirect
golang.org/x/sys v0.29.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/tools v0.24.0 // indirect
)

28
go.sum
View file

@ -19,23 +19,27 @@ github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
github.com/quic-go/qtls-go1-20 v0.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs=
github.com/quic-go/qtls-go1-20 v0.4.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
github.com/sagernet/quic-go v0.48.1-beta.1 h1:ElPaV5yzlXIKZpqFMAcUGax6vddi3zt4AEpT94Z0vwo=
github.com/sagernet/quic-go v0.48.1-beta.1/go.mod h1:1WgdDIVD1Gybp40JTWketeSfKA/+or9YMLaG5VeTk4k=
github.com/sagernet/sing v0.5.0 h1:soo2wVwLcieKWWKIksFNK6CCAojUgAppqQVwyRYGkEM=
github.com/sagernet/sing v0.5.0/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
github.com/sagernet/quic-go v0.49.0-beta.1 h1:3LdoCzVVfYRibZns1tYWSIoB65fpTmrwy+yfK8DQ8Jk=
github.com/sagernet/quic-go v0.49.0-beta.1/go.mod h1:uesWD1Ihrldq1M3XtjuEvIUqi8WHNsRs71b3Lt1+p/U=
github.com/sagernet/sing v0.6.0-beta.9 h1:P8lKa5hN53fRNAVCIKy5cWd6/kLO5c4slhdsfehSmHs=
github.com/sagernet/sing v0.6.0-beta.9/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM=
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=

View file

@ -46,8 +46,8 @@ type ServiceOptions struct {
}
type ServerHandler interface {
N.TCPConnectionHandler
N.UDPConnectionHandler
N.TCPConnectionHandlerEx
N.UDPConnectionHandlerEx
}
type Service[U comparable] struct {
@ -159,7 +159,7 @@ func (s *Service[U]) loopConnections(listener qtls.Listener) {
Service: s,
ctx: s.ctx,
quicConn: connection,
source: M.SocksaddrFromNet(connection.RemoteAddr()),
source: M.SocksaddrFromNet(connection.RemoteAddr()).Unwrap(),
connDone: make(chan struct{}),
udpConnMap: make(map[uint32]*udpPacketConn),
}
@ -244,10 +244,7 @@ func (s *serverSession[U]) handleStream(stream quic.Stream) error {
}
ctx := auth.ContextWithUser(s.ctx, s.authUser)
if !request.UDP {
_ = s.handler.NewConnection(ctx, &serverConn{Stream: stream}, M.Metadata{
Source: s.source,
Destination: M.ParseSocksaddrHostPort(request.Host, request.Port),
})
s.handler.NewConnectionEx(ctx, &serverConn{Stream: stream}, s.source, M.ParseSocksaddrHostPort(request.Host, request.Port), nil)
} else {
if s.udpDisabled {
return WriteServerResponse(stream, ServerResponse{
@ -278,10 +275,7 @@ func (s *serverSession[U]) handleStream(stream quic.Stream) error {
return err
}
newCtx, newConn := canceler.NewPacketConn(udpConn.ctx, udpConn, s.udpTimeout)
go s.handler.NewPacketConnection(newCtx, newConn, M.Metadata{
Source: s.source,
Destination: M.ParseSocksaddrHostPort(request.Host, request.Port),
})
go s.handler.NewPacketConnectionEx(newCtx, newConn, s.source, M.ParseSocksaddrHostPort(request.Host, request.Port), nil)
holdBuffer := make([]byte, 1024)
for {
_, hErr := stream.Read(holdBuffer)
@ -330,7 +324,7 @@ type serverConn struct {
func (c *serverConn) HandshakeFailure(err error) error {
if c.responseWritten {
return os.ErrClosed
return os.ErrInvalid
}
c.responseWritten = true
return WriteServerResponse(c.Stream, ServerResponse{

View file

@ -3,11 +3,14 @@ package hysteria2
import (
"context"
"io"
"math"
"net"
"net/http"
"net/url"
"os"
"runtime"
"strconv"
"strings"
"sync"
"time"
@ -36,6 +39,8 @@ type ClientOptions struct {
Logger logger.Logger
BrutalDebug bool
ServerAddress M.Socksaddr
ServerPorts []string
HopInterval time.Duration
SendBPS uint64
ReceiveBPS uint64
SalamanderPassword string
@ -50,6 +55,8 @@ type Client struct {
logger logger.Logger
brutalDebug bool
serverAddr M.Socksaddr
serverPorts []uint16
hopInterval time.Duration
sendBPS uint64
receiveBPS uint64
salamanderPassword string
@ -76,12 +83,22 @@ func NewClient(options ClientOptions) (*Client, error) {
if len(options.TLSConfig.NextProtos()) == 0 {
options.TLSConfig.SetNextProtos([]string{http3.NextProtoH3})
}
var serverPorts []uint16
if len(options.ServerPorts) > 0 {
var err error
serverPorts, err = parsePorts(options.ServerPorts)
if err != nil {
return nil, err
}
}
return &Client{
ctx: options.Context,
dialer: options.Dialer,
logger: options.Logger,
brutalDebug: options.BrutalDebug,
serverAddr: options.ServerAddress,
serverPorts: serverPorts,
hopInterval: options.HopInterval,
sendBPS: options.SendBPS,
receiveBPS: options.ReceiveBPS,
salamanderPassword: options.SalamanderPassword,
@ -92,6 +109,38 @@ func NewClient(options ClientOptions) (*Client, error) {
}, nil
}
func parsePorts(serverPorts []string) ([]uint16, error) {
var portList []uint16
for _, portRange := range serverPorts {
if !strings.Contains(portRange, ":") {
return nil, E.New("bad port range: ", portRange)
}
subIndex := strings.Index(portRange, ":")
var (
start, end uint64
err error
)
if subIndex > 0 {
start, err = strconv.ParseUint(portRange[:subIndex], 10, 16)
if err != nil {
return nil, E.Cause(err, E.Cause(err, "bad port range: ", portRange))
}
}
if subIndex == len(portRange)-1 {
end = math.MaxUint16
} else {
end, err = strconv.ParseUint(portRange[subIndex+1:], 10, 16)
if err != nil {
return nil, E.Cause(err, E.Cause(err, "bad port range: ", portRange))
}
}
for i := start; i <= end; i++ {
portList = append(portList, uint16(i))
}
}
return portList, nil
}
func (c *Client) offer(ctx context.Context) (*clientQUICConnection, error) {
conn := c.conn
if conn != nil && conn.active() {
@ -111,19 +160,34 @@ func (c *Client) offer(ctx context.Context) (*clientQUICConnection, error) {
}
func (c *Client) offerNew(ctx context.Context) (*clientQUICConnection, error) {
udpConn, err := c.dialer.DialContext(c.ctx, "udp", c.serverAddr)
dialFunc := func(serverAddr M.Socksaddr) (net.PacketConn, error) {
udpConn, err := c.dialer.DialContext(c.ctx, "udp", serverAddr)
if err != nil {
return nil, err
}
var packetConn net.PacketConn
packetConn = bufio.NewUnbindPacketConn(udpConn)
if c.salamanderPassword != "" {
packetConn = NewSalamanderConn(packetConn, []byte(c.salamanderPassword))
}
return packetConn, nil
}
var (
packetConn net.PacketConn
err error
)
if len(c.serverPorts) == 0 {
packetConn, err = dialFunc(c.serverAddr)
} else {
packetConn, err = NewHopPacketConn(dialFunc, c.serverAddr, c.serverPorts, c.hopInterval)
}
if err != nil {
return nil, err
}
var packetConn net.PacketConn
packetConn = bufio.NewUnbindPacketConn(udpConn)
if c.salamanderPassword != "" {
packetConn = NewSalamanderConn(packetConn, []byte(c.salamanderPassword))
}
var quicConn quic.EarlyConnection
http3Transport, err := qtls.CreateTransport(packetConn, &quicConn, c.serverAddr, c.tlsConfig, c.quicConfig)
if err != nil {
udpConn.Close()
packetConn.Close()
return nil, err
}
request := &http.Request{
@ -141,14 +205,14 @@ func (c *Client) offerNew(ctx context.Context) (*clientQUICConnection, error) {
if quicConn != nil {
quicConn.CloseWithError(0, "")
}
udpConn.Close()
packetConn.Close()
return nil, err
}
if response.StatusCode != protocol.StatusAuthOK {
if quicConn != nil {
quicConn.CloseWithError(0, "")
}
udpConn.Close()
packetConn.Close()
return nil, E.New("authentication failed, status code: ", response.StatusCode)
}
response.Body.Close()
@ -172,7 +236,7 @@ func (c *Client) offerNew(ctx context.Context) (*clientQUICConnection, error) {
}
conn := &clientQUICConnection{
quicConn: quicConn,
rawConn: udpConn,
rawConn: packetConn,
connDone: make(chan struct{}),
udpDisabled: !authResponse.UDPEnabled,
udpConnMap: make(map[uint32]*udpPacketConn),

269
hysteria2/hop.go Normal file
View file

@ -0,0 +1,269 @@
package hysteria2
import (
"errors"
"math/rand"
"net"
"os"
"sync"
"syscall"
"time"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/buf"
M "github.com/sagernet/sing/common/metadata"
)
const (
packetQueueSize = 1024
udpBufferSize = 2048
defaultHopInterval = 30 * time.Second
)
type HopPacketConn struct {
dialFunc func(M.Socksaddr) (net.PacketConn, error)
destination M.Socksaddr
ports []uint16
interval time.Duration
access sync.Mutex
prevConn net.PacketConn
currentConn net.PacketConn
portIndex int
readBufferSize int
writeBufferSize int
packetChan chan *buf.Buffer
errChan chan error
doneChan chan struct{}
done bool
}
func NewHopPacketConn(
dialFunc func(M.Socksaddr) (net.PacketConn, error),
destination M.Socksaddr,
ports []uint16,
interval time.Duration,
) (*HopPacketConn, error) {
if interval == 0 {
interval = defaultHopInterval
}
hopConn := &HopPacketConn{
dialFunc: dialFunc,
destination: destination,
ports: ports,
interval: interval,
packetChan: make(chan *buf.Buffer, packetQueueSize),
errChan: make(chan error),
doneChan: make(chan struct{}),
}
currentConn, err := dialFunc(hopConn.nextAddr())
if err != nil {
return nil, err
}
hopConn.currentConn = currentConn
go hopConn.recvLoop(currentConn)
go hopConn.hopLoop()
return hopConn, nil
}
func (c *HopPacketConn) nextAddr() M.Socksaddr {
c.portIndex = rand.Intn(len(c.ports))
return M.Socksaddr{
Addr: c.destination.Addr,
Fqdn: c.destination.Fqdn,
Port: c.ports[c.portIndex],
}
}
func (c *HopPacketConn) recvLoop(conn net.PacketConn) {
for {
buffer := buf.NewSize(udpBufferSize)
n, _, err := conn.ReadFrom(buffer.FreeBytes())
if err != nil {
buffer.Release()
var netErr net.Error
if errors.As(err, &netErr) && netErr.Timeout() {
// Only pass through timeout errors here, not permanent errors
// like connection closed. Connection close is normal as we close
// the old connection to exit this loop every time we hop.
c.errChan <- netErr
}
return
}
buffer.Truncate(n)
select {
case c.packetChan <- buffer:
default:
buffer.Release()
}
}
}
func (c *HopPacketConn) hopLoop() {
ticker := time.NewTicker(c.interval)
defer ticker.Stop()
for {
select {
case <-ticker.C:
c.hop()
case <-c.doneChan:
return
}
}
}
func (c *HopPacketConn) hop() {
c.access.Lock()
defer c.access.Unlock()
if c.done {
return
}
nextAddr := c.nextAddr()
newConn, err := c.dialFunc(nextAddr)
if err != nil {
return
}
if c.prevConn != nil {
c.prevConn.Close()
}
c.prevConn = c.currentConn
c.currentConn = newConn
if c.readBufferSize > 0 {
_ = trySetReadBuffer(newConn, c.readBufferSize)
}
if c.writeBufferSize > 0 {
_ = trySetWriteBuffer(newConn, c.writeBufferSize)
}
go c.recvLoop(newConn)
}
func (c *HopPacketConn) ReadFrom(b []byte) (n int, addr net.Addr, err error) {
for {
select {
case packet := <-c.packetChan:
n = copy(b, packet.Bytes())
packet.Release()
return n, (*hopFakeAddr)(nil), nil
case err = <-c.errChan:
return 0, nil, err
case <-c.doneChan:
return 0, nil, net.ErrClosed
}
}
}
func (c *HopPacketConn) WriteTo(b []byte, _ net.Addr) (n int, err error) {
c.access.Lock()
defer c.access.Unlock()
if c.done {
return 0, net.ErrClosed
}
return c.currentConn.WriteTo(b, (*hopFakeAddr)(nil))
}
func (c *HopPacketConn) Close() error {
c.access.Lock()
defer c.access.Unlock()
if c.done {
return nil
}
if c.prevConn != nil {
_ = c.prevConn.Close()
}
err := c.currentConn.Close()
close(c.doneChan)
c.done = true
return err
}
func (c *HopPacketConn) LocalAddr() net.Addr {
c.access.Lock()
defer c.access.Unlock()
return c.currentConn.LocalAddr()
}
func (c *HopPacketConn) SetDeadline(t time.Time) error {
c.access.Lock()
defer c.access.Unlock()
if c.prevConn != nil {
_ = c.prevConn.SetDeadline(t)
}
return c.currentConn.SetDeadline(t)
}
func (c *HopPacketConn) SetReadDeadline(t time.Time) error {
c.access.Lock()
defer c.access.Unlock()
if c.prevConn != nil {
_ = c.prevConn.SetReadDeadline(t)
}
return c.currentConn.SetReadDeadline(t)
}
func (c *HopPacketConn) SetWriteDeadline(t time.Time) error {
c.access.Lock()
defer c.access.Unlock()
if c.prevConn != nil {
_ = c.prevConn.SetWriteDeadline(t)
}
return c.currentConn.SetWriteDeadline(t)
}
func (c *HopPacketConn) SetReadBuffer(bytes int) error {
c.access.Lock()
defer c.access.Unlock()
c.readBufferSize = bytes
if c.prevConn != nil {
_ = trySetReadBuffer(c.prevConn, bytes)
}
return trySetReadBuffer(c.currentConn, bytes)
}
func (c *HopPacketConn) SetWriteBuffer(bytes int) error {
c.access.Lock()
defer c.access.Unlock()
c.writeBufferSize = bytes
if c.prevConn != nil {
_ = trySetWriteBuffer(c.prevConn, bytes)
}
return trySetWriteBuffer(c.currentConn, bytes)
}
func (c *HopPacketConn) SyscallConn() (syscall.RawConn, error) {
c.access.Lock()
defer c.access.Unlock()
rawConn, isRawConn := common.Cast[syscall.Conn](c.currentConn)
if !isRawConn {
return nil, os.ErrInvalid
}
return rawConn.SyscallConn()
}
func trySetReadBuffer(pc any, bytes int) error {
udpConn, isUDPConn := common.Cast[interface {
SetReadBuffer(bytes int) error
}](pc)
if !isUDPConn {
return nil
}
return udpConn.SetReadBuffer(bytes)
}
func trySetWriteBuffer(pc any, bytes int) error {
udpConn, isUDPConn := common.Cast[interface {
SetWriteBuffer(bytes int) error
}](pc)
if !isUDPConn {
return nil
}
return udpConn.SetWriteBuffer(bytes)
}
type hopFakeAddr struct{}
func (a *hopFakeAddr) Network() string {
return "udphop"
}
func (a *hopFakeAddr) String() string {
return "<udphop>"
}

View file

@ -69,6 +69,10 @@ func (s *SalamanderPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err erro
return len(p), nil
}
func (s *SalamanderPacketConn) Upstream() any {
return s.PacketConn
}
type VectorisedSalamanderPacketConn struct {
SalamanderPacketConn
writer N.VectorisedPacketWriter

View file

@ -47,8 +47,8 @@ type ServiceOptions struct {
}
type ServerHandler interface {
N.TCPConnectionHandler
N.UDPConnectionHandler
N.TCPConnectionHandlerEx
N.UDPConnectionHandlerEx
}
type Service[U comparable] struct {
@ -156,7 +156,7 @@ func (s *Service[U]) handleConnection(connection quic.Connection) {
Service: s,
ctx: s.ctx,
quicConn: connection,
source: M.SocksaddrFromNet(connection.RemoteAddr()),
source: M.SocksaddrFromNet(connection.RemoteAddr()).Unwrap(),
connDone: make(chan struct{}),
udpConnMap: make(map[uint32]*udpPacketConn),
}
@ -188,7 +188,7 @@ func (s *serverSession[U]) ServeHTTP(w http.ResponseWriter, r *http.Request) {
protocol.AuthResponseToHeader(w.Header(), protocol.AuthResponse{
UDPEnabled: !s.udpDisabled,
Rx: s.receiveBPS,
RxAuto: s.ignoreClientBandwidth,
RxAuto: s.receiveBPS == 0 && s.ignoreClientBandwidth,
})
w.WriteHeader(protocol.StatusAuthOK)
return
@ -201,7 +201,12 @@ func (s *serverSession[U]) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
s.authUser = user
s.authenticated = true
if !s.ignoreClientBandwidth && request.Rx > 0 {
var rxAuto bool
if s.receiveBPS > 0 && s.ignoreClientBandwidth && request.Rx == 0 {
s.logger.Debug("process connection from ", r.RemoteAddr, ": BBR disabled by server")
s.masqueradeHandler.ServeHTTP(w, r)
return
} else if !(s.receiveBPS == 0 && s.ignoreClientBandwidth) && request.Rx > 0 {
rx := request.Rx
if s.sendBPS > 0 && rx > s.sendBPS {
rx = s.sendBPS
@ -217,11 +222,12 @@ func (s *serverSession[U]) ServeHTTP(w http.ResponseWriter, r *http.Request) {
congestion.ByteCount(s.quicConn.Config().InitialPacketSize),
congestion.ByteCount(congestion_meta1.InitialCongestionWindow),
))
rxAuto = true
}
protocol.AuthResponseToHeader(w.Header(), protocol.AuthResponse{
UDPEnabled: !s.udpDisabled,
Rx: s.receiveBPS,
RxAuto: s.ignoreClientBandwidth,
RxAuto: rxAuto,
})
w.WriteHeader(protocol.StatusAuthOK)
if s.ctx.Done() != nil {
@ -250,8 +256,6 @@ func (s *serverSession[U]) handleStream0(frameType http3.FrameType, id quic.Conn
}
go func() {
hErr := s.handleStream(stream)
stream.CancelRead(0)
stream.Close()
if hErr != nil {
stream.CancelRead(0)
stream.Close()
@ -266,11 +270,7 @@ func (s *serverSession[U]) handleStream(stream quic.Stream) error {
if err != nil {
return E.New("read TCP request")
}
ctx := auth.ContextWithUser(s.ctx, s.authUser)
_ = s.handler.NewConnection(ctx, &serverConn{Stream: stream}, M.Metadata{
Source: s.source,
Destination: M.ParseSocksaddr(destinationString),
})
s.handler.NewConnectionEx(auth.ContextWithUser(s.ctx, s.authUser), &serverConn{Stream: stream}, s.source, M.ParseSocksaddr(destinationString), nil)
return nil
}
@ -299,7 +299,7 @@ type serverConn struct {
func (c *serverConn) HandshakeFailure(err error) error {
if c.responseWritten {
return os.ErrClosed
return os.ErrInvalid
}
c.responseWritten = true
buffer := protocol.WriteTCPResponse(false, err.Error(), nil)

View file

@ -49,10 +49,7 @@ func (s *serverSession[U]) handleUDPMessage(message *udpMessage) {
s.udpConnMap[message.sessionID] = udpConn
s.udpAccess.Unlock()
newCtx, newConn := canceler.NewPacketConn(udpConn.ctx, udpConn, s.udpTimeout)
go s.handler.NewPacketConnection(newCtx, newConn, M.Metadata{
Source: s.source,
Destination: M.ParseSocksaddr(message.destination),
})
go s.handler.NewPacketConnectionEx(newCtx, newConn, s.source, M.ParseSocksaddr(message.destination), nil)
}
udpConn.inputPacket(message)
}

View file

@ -40,8 +40,8 @@ type ServiceOptions struct {
}
type ServiceHandler interface {
N.TCPConnectionHandler
N.UDPConnectionHandler
N.TCPConnectionHandlerEx
N.UDPConnectionHandlerEx
}
type Service[U comparable] struct {
@ -163,7 +163,7 @@ func (s *Service[U]) handleConnection(connection quic.Connection) {
Service: s,
ctx: s.ctx,
quicConn: connection,
source: M.SocksaddrFromNet(connection.RemoteAddr()),
source: M.SocksaddrFromNet(connection.RemoteAddr()).Unwrap(),
connDone: make(chan struct{}),
authDone: make(chan struct{}),
udpConnMap: make(map[uint16]*udpPacketConn),
@ -362,11 +362,7 @@ func (s *serverSession[U]) handleStream(stream quic.Stream) error {
} else {
conn = bufio.NewCachedConn(conn, buffer)
}
ctx := auth.ContextWithUser(s.ctx, s.authUser)
_ = s.handler.NewConnection(ctx, conn, M.Metadata{
Source: s.source,
Destination: destination,
})
s.handler.NewConnectionEx(auth.ContextWithUser(s.ctx, s.authUser), conn, s.source, destination, nil)
return nil
}

View file

@ -5,7 +5,6 @@ import (
"github.com/sagernet/sing/common/auth"
"github.com/sagernet/sing/common/canceler"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
)
func (s *serverSession[U]) loopMessages() {
@ -67,10 +66,7 @@ func (s *serverSession[U]) handleUDPMessage(message *udpMessage, udpStream bool)
s.udpConnMap[message.sessionID] = udpConn
s.udpAccess.Unlock()
newCtx, newConn := canceler.NewPacketConn(udpConn.ctx, udpConn, s.udpTimeout)
go s.handler.NewPacketConnection(newCtx, newConn, M.Metadata{
Source: s.source,
Destination: message.destination,
})
go s.handler.NewPacketConnectionEx(newCtx, newConn, s.source, message.destination, nil)
}
udpConn.inputPacket(message)
}