mirror of
https://github.com/SagerNet/sing-tun.git
synced 2025-03-31 10:27:39 +03:00
191 lines
4 KiB
Go
191 lines
4 KiB
Go
//go:build with_gvisor
|
|
|
|
package tun
|
|
|
|
import (
|
|
"context"
|
|
"net"
|
|
"os"
|
|
"time"
|
|
|
|
"github.com/sagernet/gvisor/pkg/tcpip"
|
|
"github.com/sagernet/gvisor/pkg/tcpip/adapters/gonet"
|
|
"github.com/sagernet/gvisor/pkg/tcpip/stack"
|
|
"github.com/sagernet/gvisor/pkg/tcpip/transport/tcp"
|
|
"github.com/sagernet/gvisor/pkg/waiter"
|
|
"github.com/sagernet/sing/common"
|
|
)
|
|
|
|
type gLazyConn struct {
|
|
tcpConn *gonet.TCPConn
|
|
parentCtx context.Context
|
|
stack *stack.Stack
|
|
request *tcp.ForwarderRequest
|
|
localAddr net.Addr
|
|
remoteAddr net.Addr
|
|
handshakeDone bool
|
|
handshakeErr error
|
|
}
|
|
|
|
func (c *gLazyConn) HandshakeContext(ctx context.Context) error {
|
|
if c.handshakeDone {
|
|
return nil
|
|
}
|
|
defer func() {
|
|
c.handshakeDone = true
|
|
}()
|
|
var (
|
|
wq waiter.Queue
|
|
endpoint tcpip.Endpoint
|
|
)
|
|
handshakeCtx, cancel := context.WithCancel(ctx)
|
|
go func() {
|
|
select {
|
|
case <-c.parentCtx.Done():
|
|
wq.Notify(wq.Events())
|
|
case <-handshakeCtx.Done():
|
|
}
|
|
}()
|
|
endpoint, err := c.request.CreateEndpoint(&wq)
|
|
cancel()
|
|
if err != nil {
|
|
gErr := gonet.TranslateNetstackError(err)
|
|
c.handshakeErr = gErr
|
|
c.request.Complete(true)
|
|
return gErr
|
|
}
|
|
c.request.Complete(false)
|
|
endpoint.SocketOptions().SetKeepAlive(true)
|
|
endpoint.SetSockOpt(common.Ptr(tcpip.KeepaliveIdleOption(15 * time.Second)))
|
|
endpoint.SetSockOpt(common.Ptr(tcpip.KeepaliveIntervalOption(15 * time.Second)))
|
|
tcpConn := gonet.NewTCPConn(&wq, endpoint)
|
|
c.tcpConn = tcpConn
|
|
return nil
|
|
}
|
|
|
|
func (c *gLazyConn) HandshakeFailure(err error) error {
|
|
if c.handshakeDone {
|
|
return os.ErrInvalid
|
|
}
|
|
c.request.Complete(err != ErrDrop)
|
|
c.handshakeDone = true
|
|
c.handshakeErr = err
|
|
return nil
|
|
}
|
|
|
|
func (c *gLazyConn) HandshakeSuccess() error {
|
|
return c.HandshakeContext(context.Background())
|
|
}
|
|
|
|
func (c *gLazyConn) Read(b []byte) (n int, err error) {
|
|
if !c.handshakeDone {
|
|
err = c.HandshakeContext(context.Background())
|
|
if err != nil {
|
|
return
|
|
}
|
|
} else if c.handshakeErr != nil {
|
|
return 0, c.handshakeErr
|
|
}
|
|
return c.tcpConn.Read(b)
|
|
}
|
|
|
|
func (c *gLazyConn) Write(b []byte) (n int, err error) {
|
|
if !c.handshakeDone {
|
|
err = c.HandshakeContext(context.Background())
|
|
if err != nil {
|
|
return
|
|
}
|
|
} else if c.handshakeErr != nil {
|
|
return 0, c.handshakeErr
|
|
}
|
|
return c.tcpConn.Write(b)
|
|
}
|
|
|
|
func (c *gLazyConn) LocalAddr() net.Addr {
|
|
return c.localAddr
|
|
}
|
|
|
|
func (c *gLazyConn) RemoteAddr() net.Addr {
|
|
return c.remoteAddr
|
|
}
|
|
|
|
func (c *gLazyConn) SetDeadline(t time.Time) error {
|
|
if !c.handshakeDone {
|
|
err := c.HandshakeContext(context.Background())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
} else if c.handshakeErr != nil {
|
|
return c.handshakeErr
|
|
}
|
|
return c.tcpConn.SetDeadline(t)
|
|
}
|
|
|
|
func (c *gLazyConn) SetReadDeadline(t time.Time) error {
|
|
if !c.handshakeDone {
|
|
err := c.HandshakeContext(context.Background())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
} else if c.handshakeErr != nil {
|
|
return c.handshakeErr
|
|
}
|
|
return c.tcpConn.SetReadDeadline(t)
|
|
}
|
|
|
|
func (c *gLazyConn) SetWriteDeadline(t time.Time) error {
|
|
if !c.handshakeDone {
|
|
err := c.HandshakeContext(context.Background())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
} else if c.handshakeErr != nil {
|
|
return c.handshakeErr
|
|
}
|
|
return c.tcpConn.SetWriteDeadline(t)
|
|
}
|
|
|
|
func (c *gLazyConn) Close() error {
|
|
if !c.handshakeDone {
|
|
c.request.Complete(true)
|
|
c.handshakeErr = net.ErrClosed
|
|
return nil
|
|
} else if c.handshakeErr != nil {
|
|
return nil
|
|
}
|
|
return c.tcpConn.Close()
|
|
}
|
|
|
|
func (c *gLazyConn) CloseRead() error {
|
|
if !c.handshakeDone {
|
|
c.request.Complete(true)
|
|
c.handshakeErr = net.ErrClosed
|
|
return nil
|
|
} else if c.handshakeErr != nil {
|
|
return nil
|
|
}
|
|
return c.tcpConn.CloseRead()
|
|
}
|
|
|
|
func (c *gLazyConn) CloseWrite() error {
|
|
if !c.handshakeDone {
|
|
c.request.Complete(true)
|
|
c.handshakeErr = net.ErrClosed
|
|
return nil
|
|
} else if c.handshakeErr != nil {
|
|
return nil
|
|
}
|
|
return c.tcpConn.CloseRead()
|
|
}
|
|
|
|
func (c *gLazyConn) ReaderReplaceable() bool {
|
|
return c.handshakeDone && c.handshakeErr == nil
|
|
}
|
|
|
|
func (c *gLazyConn) WriterReplaceable() bool {
|
|
return c.handshakeDone && c.handshakeErr == nil
|
|
}
|
|
|
|
func (c *gLazyConn) Upstream() any {
|
|
return c.tcpConn
|
|
}
|