Trim repo

This commit is contained in:
世界 2022-06-30 17:38:12 +08:00
parent 293cfb66d6
commit 323ace94fd
No known key found for this signature in database
GPG key ID: CD109927C34A63C4
18 changed files with 33 additions and 849 deletions

View file

@ -1,11 +0,0 @@
package debug
import (
"runtime/debug"
)
func Free() {
if Enabled {
debug.FreeOSMemory()
}
}

View file

@ -1,9 +0,0 @@
package redir
type TransproxyMode uint8
const (
ModeDisabled TransproxyMode = iota
ModeRedirect
ModeTProxy
)

View file

@ -1,37 +0,0 @@
package redir
import (
"net"
"net/netip"
"syscall"
M "github.com/sagernet/sing/common/metadata"
)
func GetOriginalDestination(conn net.Conn) (destination netip.AddrPort, err error) {
rawConn, err := conn.(syscall.Conn).SyscallConn()
if err != nil {
return
}
var rawFd uintptr
err = rawConn.Control(func(fd uintptr) {
rawFd = fd
})
if err != nil {
return
}
const SO_ORIGINAL_DST = 80
if conn.RemoteAddr().(*net.TCPAddr).IP.To4() != nil {
raw, err := syscall.GetsockoptIPv6Mreq(int(rawFd), syscall.IPPROTO_IP, SO_ORIGINAL_DST)
if err != nil {
return netip.AddrPort{}, err
}
return netip.AddrPortFrom(M.AddrFromIP(raw.Multiaddr[4:8]), uint16(raw.Multiaddr[2])<<8+uint16(raw.Multiaddr[3])), nil
} else {
raw, err := syscall.GetsockoptIPv6MTUInfo(int(rawFd), syscall.IPPROTO_IPV6, SO_ORIGINAL_DST)
if err != nil {
return netip.AddrPort{}, err
}
return netip.AddrPortFrom(M.AddrFromIP(raw.Addr.Addr[:]), raw.Addr.Port), nil
}
}

View file

@ -1,13 +0,0 @@
//go:build !linux
package redir
import (
"errors"
"net"
"net/netip"
)
func GetOriginalDestination(conn net.Conn) (destination netip.AddrPort, err error) {
return netip.AddrPort{}, errors.New("unsupported platform")
}

View file

@ -1,136 +0,0 @@
package redir
import (
"encoding/binary"
"net"
"net/netip"
"os"
"strconv"
"syscall"
E "github.com/sagernet/sing/common/exceptions"
F "github.com/sagernet/sing/common/format"
M "github.com/sagernet/sing/common/metadata"
"golang.org/x/sys/unix"
)
func TProxy(fd uintptr, isIPv6 bool) error {
err := syscall.SetsockoptInt(int(fd), syscall.SOL_IP, syscall.IP_TRANSPARENT, 1)
if err != nil {
return err
}
if isIPv6 {
err = syscall.SetsockoptInt(int(fd), syscall.SOL_IPV6, unix.IPV6_TRANSPARENT, 1)
}
return err
}
func TProxyUDP(fd uintptr, isIPv6 bool) error {
err := syscall.SetsockoptInt(int(fd), syscall.SOL_IPV6, unix.IPV6_RECVORIGDSTADDR, 1)
if err != nil {
return err
}
return syscall.SetsockoptInt(int(fd), syscall.SOL_IP, syscall.IP_RECVORIGDSTADDR, 1)
}
func FWMark(fd uintptr, mark int) error {
return syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_MARK, mark)
}
func GetOriginalDestinationFromOOB(oob []byte) (netip.AddrPort, error) {
controlMessages, err := unix.ParseSocketControlMessage(oob)
if err != nil {
return netip.AddrPort{}, err
}
for _, message := range controlMessages {
if message.Header.Level == unix.SOL_IP && message.Header.Type == unix.IP_RECVORIGDSTADDR {
return netip.AddrPortFrom(M.AddrFromIP(message.Data[4:8]), binary.BigEndian.Uint16(message.Data[2:4])), nil
} else if message.Header.Level == unix.SOL_IPV6 && message.Header.Type == unix.IPV6_RECVORIGDSTADDR {
return netip.AddrPortFrom(M.AddrFromIP(message.Data[8:24]), binary.BigEndian.Uint16(message.Data[2:4])), nil
}
}
return netip.AddrPort{}, E.New("not found")
}
func DialUDP(network string, lAddr *net.UDPAddr, rAddr *net.UDPAddr) (*net.UDPConn, error) {
rSockAddr, err := udpAddrToSockAddr(rAddr)
if err != nil {
return nil, err
}
lSockAddr, err := udpAddrToSockAddr(lAddr)
if err != nil {
return nil, err
}
fd, err := syscall.Socket(udpAddrFamily(network, lAddr, rAddr), syscall.SOCK_DGRAM, 0)
if err != nil {
return nil, err
}
if err = syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1); err != nil {
syscall.Close(fd)
return nil, err
}
if err = syscall.SetsockoptInt(fd, syscall.SOL_IP, syscall.IP_TRANSPARENT, 1); err != nil {
syscall.Close(fd)
return nil, err
}
if err = syscall.Bind(fd, lSockAddr); err != nil {
syscall.Close(fd)
return nil, err
}
if err = syscall.Connect(fd, rSockAddr); err != nil {
syscall.Close(fd)
return nil, err
}
fdFile := os.NewFile(uintptr(fd), F.ToString("net-udp-dial-", rAddr))
defer fdFile.Close()
c, err := net.FileConn(fdFile)
if err != nil {
syscall.Close(fd)
return nil, err
}
return c.(*net.UDPConn), nil
}
func udpAddrToSockAddr(addr *net.UDPAddr) (syscall.Sockaddr, error) {
switch {
case addr.IP.To4() != nil:
ip := [4]byte{}
copy(ip[:], addr.IP.To4())
return &syscall.SockaddrInet4{Addr: ip, Port: addr.Port}, nil
default:
ip := [16]byte{}
copy(ip[:], addr.IP.To16())
zoneID, err := strconv.ParseUint(addr.Zone, 10, 32)
if err != nil {
zoneID = 0
}
return &syscall.SockaddrInet6{Addr: ip, Port: addr.Port, ZoneId: uint32(zoneID)}, nil
}
}
func udpAddrFamily(net string, lAddr, rAddr *net.UDPAddr) int {
switch net[len(net)-1] {
case '4':
return syscall.AF_INET
case '6':
return syscall.AF_INET6
}
if (lAddr == nil || lAddr.IP.To4() != nil) && (rAddr == nil || lAddr.IP.To4() != nil) {
return syscall.AF_INET
}
return syscall.AF_INET6
}

View file

@ -1,30 +0,0 @@
//go:build !linux
package redir
import (
"net"
"net/netip"
E "github.com/sagernet/sing/common/exceptions"
)
func TProxy(fd uintptr, isIPv6 bool) error {
return E.New("only available on linux")
}
func TProxyUDP(fd uintptr, isIPv6 bool) error {
return E.New("only available on linux")
}
func FWMark(fd uintptr, mark int) error {
return E.New("only available on linux")
}
func GetOriginalDestinationFromOOB(oob []byte) (netip.AddrPort, error) {
return netip.AddrPort{}, E.New("only available on linux")
}
func DialUDP(network string, lAddr *net.UDPAddr, rAddr *net.UDPAddr) (*net.UDPConn, error) {
return nil, E.New("only available on linux")
}

View file

@ -1,16 +1,18 @@
package rw package rw
import "golang.org/x/sys/windows" import (
"syscall"
)
func WriteV(fd uintptr, data ...[]byte) (int, error) { func WriteV(fd uintptr, data ...[]byte) (int, error) {
var n uint32 var n uint32
buffers := make([]*windows.WSABuf, len(data)) buffers := make([]*syscall.WSABuf, len(data))
for i, buf := range data { for i, buf := range data {
buffers[i] = &windows.WSABuf{ buffers[i] = &syscall.WSABuf{
Len: uint32(len(buf)), Len: uint32(len(buf)),
Buf: &buf[0], Buf: &buf[0],
} }
} }
err := windows.WSASend(windows.Handle(fd), buffers[0], uint32(len(buffers)), &n, 0, nil, nil) err := syscall.WSASend(syscall.Handle(fd), buffers[0], uint32(len(buffers)), &n, 0, nil, nil)
return int(n), err return int(n), err
} }

2
go.mod
View file

@ -1,5 +1,3 @@
module github.com/sagernet/sing module github.com/sagernet/sing
go 1.18 go 1.18
require golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b

2
go.sum
View file

@ -1,2 +0,0 @@
golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b h1:2n253B2r0pYSmEV+UNCQoPfU/FiaizQEK5Gu4Bq4JE8=
golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=

View file

@ -1,6 +1,7 @@
package http package http
import ( import (
std_bufio "bufio"
"context" "context"
"encoding/base64" "encoding/base64"
"net" "net"
@ -10,6 +11,8 @@ import (
"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/bufio"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
F "github.com/sagernet/sing/common/format" F "github.com/sagernet/sing/common/format"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
@ -19,7 +22,26 @@ import (
type Handler interface { type Handler interface {
N.TCPConnectionHandler N.TCPConnectionHandler
N.UDPConnectionHandler N.UDPConnectionHandler
E.Handler }
func HandleConnection(ctx context.Context, conn net.Conn, authenticator auth.Authenticator, handler Handler, metadata M.Metadata) error {
reader := std_bufio.NewReader(conn)
request, err := http.ReadRequest(reader)
if err != nil {
return E.Cause(err, "read http request")
}
if reader.Buffered() > 0 {
_buffer := buf.StackNewSize(reader.Buffered())
defer common.KeepAlive(_buffer)
buffer := common.Dup(_buffer)
defer buffer.Release()
_, err = buffer.ReadFullFrom(reader, reader.Buffered())
if err != nil {
return err
}
conn = bufio.NewCachedConn(conn, buffer)
}
return HandleRequest(ctx, request, conn, authenticator, handler, metadata)
} }
func HandleRequest(ctx context.Context, request *http.Request, conn net.Conn, authenticator auth.Authenticator, handler Handler, metadata M.Metadata) error { func HandleRequest(ctx context.Context, request *http.Request, conn net.Conn, authenticator auth.Authenticator, handler Handler, metadata M.Metadata) error {
@ -72,6 +94,7 @@ func HandleRequest(ctx context.Context, request *http.Request, conn net.Conn, au
return responseWith(request, http.StatusBadRequest).Write(conn) return responseWith(request, http.StatusBadRequest).Write(conn)
} }
var innerErr error
if httpClient == nil { if httpClient == nil {
httpClient = &http.Client{ httpClient = &http.Client{
Transport: &http.Transport{ Transport: &http.Transport{
@ -89,8 +112,8 @@ func HandleRequest(ctx context.Context, request *http.Request, conn net.Conn, au
go func() { go func() {
err := handler.NewConnection(ctx, right, metadata) err := handler.NewConnection(ctx, right, metadata)
if err != nil { if err != nil {
innerErr = err
common.Close(left, right) common.Close(left, right)
handler.HandleError(err)
} }
}() }()
return left, nil return left, nil
@ -104,8 +127,7 @@ func HandleRequest(ctx context.Context, request *http.Request, conn net.Conn, au
response, err := httpClient.Do(request) response, err := httpClient.Do(request)
if err != nil { if err != nil {
handler.HandleError(err) return common.AnyError(innerErr, err, responseWith(request, http.StatusBadGateway).Write(conn))
return responseWith(request, http.StatusBadGateway).Write(conn)
} }
removeHopByHopHeaders(response.Header) removeHopByHopHeaders(response.Header)
@ -120,7 +142,7 @@ func HandleRequest(ctx context.Context, request *http.Request, conn net.Conn, au
err = response.Write(conn) err = response.Write(conn)
if err != nil { if err != nil {
return err return common.AnyError(innerErr, err)
} }
if !keepAlive { if !keepAlive {

View file

@ -1,158 +0,0 @@
package mixed
import (
std_bufio "bufio"
"context"
"io"
"net"
netHttp "net/http"
"net/netip"
"strings"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/auth"
"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"
"github.com/sagernet/sing/common/redir"
"github.com/sagernet/sing/common/rw"
"github.com/sagernet/sing/common/udpnat"
"github.com/sagernet/sing/protocol/http"
"github.com/sagernet/sing/protocol/socks"
"github.com/sagernet/sing/protocol/socks/socks4"
"github.com/sagernet/sing/protocol/socks/socks5"
"github.com/sagernet/sing/transport/tcp"
"github.com/sagernet/sing/transport/udp"
)
type Handler interface {
socks.Handler
}
type Listener struct {
TCPListener *tcp.Listener
UDPListener *udp.Listener
bindAddr netip.Addr
handler Handler
authenticator auth.Authenticator
udpNat *udpnat.Service[netip.AddrPort]
}
func NewListener(bind netip.AddrPort, authenticator auth.Authenticator, transproxy redir.TransproxyMode, udpTimeout int64, handler Handler) *Listener {
listener := &Listener{
bindAddr: bind.Addr(),
handler: handler,
authenticator: authenticator,
}
listener.TCPListener = tcp.NewTCPListener(bind, listener, tcp.WithTransproxyMode(transproxy))
if transproxy == redir.ModeTProxy {
listener.UDPListener = udp.NewUDPListener(bind, listener, udp.WithTransproxyMode(transproxy))
listener.udpNat = udpnat.New[netip.AddrPort](udpTimeout, handler)
}
return listener
}
func (l *Listener) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
if metadata.Destination.IsValid() {
return l.handler.NewConnection(ctx, conn, metadata)
}
headerType, err := rw.ReadByte(conn)
if err != nil {
return err
}
switch headerType {
case socks4.Version, socks5.Version:
return socks.HandleConnection0(ctx, conn, headerType, l.authenticator, l.handler, metadata)
}
reader := std_bufio.NewReader(bufio.NewCachedReader(conn, buf.As([]byte{headerType})))
request, err := http.ReadRequest(reader)
if err != nil {
return E.Cause(err, "read http request")
}
if request.Method == "GET" && request.URL.Path == "/proxy.pac" {
content := newPAC(M.AddrPortFromNet(conn.LocalAddr()))
response := &netHttp.Response{
StatusCode: 200,
Status: netHttp.StatusText(200),
Proto: request.Proto,
ProtoMajor: request.ProtoMajor,
ProtoMinor: request.ProtoMinor,
Header: netHttp.Header{
"Content-Type": {"application/x-ns-proxy-autoconfig"},
},
ContentLength: int64(len(content)),
Body: io.NopCloser(strings.NewReader(content)),
}
err = response.Write(conn)
if err != nil {
return E.Cause(err, "write pac response")
}
return nil
}
if reader.Buffered() > 0 {
_buffer := buf.StackNewSize(reader.Buffered())
defer common.KeepAlive(_buffer)
buffer := common.Dup(_buffer)
defer buffer.Release()
_, err = buffer.ReadFullFrom(reader, reader.Buffered())
if err != nil {
return err
}
conn = bufio.NewCachedConn(conn, buffer)
}
return http.HandleRequest(ctx, request, conn, l.authenticator, l.handler, metadata)
}
func (l *Listener) WriteIsThreadUnsafe() {
}
func (l *Listener) NewPacket(ctx context.Context, conn N.PacketConn, buffer *buf.Buffer, metadata M.Metadata) error {
l.udpNat.NewPacket(ctx, metadata.Source.AddrPort(), buffer, metadata, func(netConn N.PacketConn) N.PacketWriter {
return &tproxyPacketWriter{conn}
})
return nil
}
type tproxyPacketWriter struct {
source N.PacketConn
}
func (w *tproxyPacketWriter) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
defer buffer.Release()
udpConn, err := redir.DialUDP("udp", destination.UDPAddr(), M.SocksaddrFromNet(w.source.LocalAddr()).UDPAddr())
if err != nil {
return E.Cause(err, "tproxy udp write back")
}
defer udpConn.Close()
return common.Error(udpConn.Write(buffer.Bytes()))
}
func (l *Listener) HandleError(err error) {
l.handler.HandleError(err)
}
func (l *Listener) Start() error {
err := l.TCPListener.Start()
if err != nil {
return err
}
if l.UDPListener != nil {
err = l.UDPListener.Start()
}
return err
}
func (l *Listener) Close() error {
return common.Close(
l.TCPListener,
l.UDPListener,
)
}

View file

@ -1,12 +0,0 @@
package mixed
import (
"net/netip"
)
func newPAC(proxyAddr netip.AddrPort) string {
return `
function FindProxyForURL(url, host) {
return "SOCKS5 ` + proxyAddr.String() + `; PROXY ` + proxyAddr.String() + `";
}`
}

View file

@ -1,14 +0,0 @@
package system
import (
"syscall"
)
const (
TCP_FASTOPEN = 23
TCP_FASTOPEN_CONNECT = 30
)
func TCPFastOpen(fd uintptr) error {
return syscall.SetsockoptInt(int(fd), syscall.SOL_TCP, TCP_FASTOPEN_CONNECT, 1)
}

View file

@ -1,9 +0,0 @@
//go:build !linux
package system
import "github.com/sagernet/sing/common/exceptions"
func TCPFastOpen(fd uintptr) error {
return exceptions.New("only available on linux")
}

View file

@ -1,121 +0,0 @@
package tcp
import (
"context"
"net"
"net/netip"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/debug"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/common/redir"
)
type Handler interface {
N.TCPConnectionHandler
E.Handler
}
type Listener struct {
bind netip.AddrPort
handler Handler
trans redir.TransproxyMode
lAddr *net.TCPAddr
*net.TCPListener
}
func NewTCPListener(listen netip.AddrPort, handler Handler, options ...Option) *Listener {
listener := &Listener{
bind: listen,
handler: handler,
}
for _, option := range options {
option(listener)
}
return listener
}
func (l *Listener) Start() error {
tcpListener, err := net.ListenTCP(M.NetworkFromNetAddr("tcp", l.bind.Addr()), net.TCPAddrFromAddrPort(l.bind))
if err != nil {
return err
}
if l.trans == redir.ModeTProxy {
l.lAddr = tcpListener.Addr().(*net.TCPAddr)
fd, err := common.GetFileDescriptor(tcpListener)
if err != nil {
return err
}
err = redir.TProxy(fd, l.bind.Addr().Is6())
if err != nil {
return E.Cause(err, "configure tproxy")
}
}
l.TCPListener = tcpListener
go l.loop()
return nil
}
func (l *Listener) Close() error {
if l == nil || l.TCPListener == nil {
return nil
}
return l.TCPListener.Close()
}
func (l *Listener) loop() {
for {
tcpConn, err := l.AcceptTCP()
if err != nil {
l.handler.HandleError(E.New("tcp listener closed: ", err))
l.Close()
return
}
metadata := M.Metadata{
Source: M.SocksaddrFromNet(tcpConn.RemoteAddr()),
}
switch l.trans {
case redir.ModeRedirect:
destination, err := redir.GetOriginalDestination(tcpConn)
if err == nil {
metadata.Protocol = "redirect"
metadata.Destination = M.SocksaddrFromNetIP(destination)
}
case redir.ModeTProxy:
lAddr := tcpConn.LocalAddr().(*net.TCPAddr)
rAddr := tcpConn.RemoteAddr().(*net.TCPAddr)
if lAddr.Port != l.lAddr.Port || !lAddr.IP.Equal(rAddr.IP) && !lAddr.IP.IsLoopback() && !lAddr.IP.IsPrivate() {
metadata.Protocol = "tproxy"
metadata.Destination = M.SocksaddrFromNet(lAddr)
}
}
go func() {
metadata.Protocol = "tcp"
hErr := l.handler.NewConnection(context.Background(), tcpConn, metadata)
if hErr != nil {
l.handler.HandleError(hErr)
}
debug.Free()
}()
}
}
type Error struct {
Conn net.Conn
Cause error
}
func (e *Error) Error() string {
return e.Cause.Error()
}
func (e *Error) Unwrap() error {
return e.Cause
}
func (e *Error) Close() error {
return common.Close(e.Conn)
}

View file

@ -1,11 +0,0 @@
package tcp
import "github.com/sagernet/sing/common/redir"
type Option func(*Listener)
func WithTransproxyMode(mode redir.TransproxyMode) Option {
return func(listener *Listener) {
listener.trans = mode
}
}

View file

@ -1,264 +0,0 @@
package udp
import (
"context"
"net"
"net/netip"
"os"
"sync"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/buf"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/common/redir"
)
type Handler interface {
N.UDPHandler
E.Handler
}
type Listener struct {
*net.UDPConn
handler Handler
bind netip.AddrPort
tproxy bool
forceAddr6 bool
access sync.RWMutex
closed chan struct{}
outbound chan *outboundPacket
}
func (l *Listener) ReadPacket(buffer *buf.Buffer) (M.Socksaddr, error) {
n, addr, err := l.ReadFromUDPAddrPort(buffer.FreeBytes())
if err != nil {
return M.Socksaddr{}, err
}
buffer.Truncate(n)
return M.SocksaddrFromNetIP(addr), nil
}
func (l *Listener) WriteIsThreadUnsafe() {
}
func (l *Listener) loopBack() {
for {
select {
case packet := <-l.outbound:
err := l.writePacket(packet.buffer, packet.destination)
if err != nil && !E.IsClosed(err) {
l.handler.HandleError(E.New("udp write failed: ", err))
}
continue
case <-l.closed:
}
for {
select {
case packet := <-l.outbound:
packet.buffer.Release()
default:
return
}
}
}
}
type outboundPacket struct {
buffer *buf.Buffer
destination M.Socksaddr
}
func (l *Listener) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
l.access.RLock()
defer l.access.RUnlock()
select {
case <-l.closed:
return os.ErrClosed
default:
}
l.outbound <- &outboundPacket{buffer, destination}
return nil
}
func (l *Listener) writePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
defer buffer.Release()
if destination.Family().IsFqdn() {
udpAddr, err := net.ResolveUDPAddr("udp", destination.String())
if err != nil {
return err
}
return common.Error(l.UDPConn.WriteTo(buffer.Bytes(), udpAddr))
}
if l.forceAddr6 && destination.Addr.Is4() {
destination.Addr = netip.AddrFrom16(destination.Addr.As16())
}
return common.Error(l.UDPConn.WriteToUDPAddrPort(buffer.Bytes(), destination.AddrPort()))
}
func NewUDPListener(listen netip.AddrPort, handler Handler, options ...Option) *Listener {
listener := &Listener{
handler: handler,
bind: listen,
outbound: make(chan *outboundPacket),
closed: make(chan struct{}),
}
for _, option := range options {
option(listener)
}
return listener
}
func (l *Listener) Start() error {
udpConn, err := net.ListenUDP(M.NetworkFromNetAddr("udp", l.bind.Addr()), net.UDPAddrFromAddrPort(l.bind))
if err != nil {
return err
}
l.forceAddr6 = l.bind.Addr().Is6()
if l.tproxy {
fd, err := common.GetFileDescriptor(udpConn)
if err != nil {
return err
}
err = redir.TProxy(fd, l.bind.Addr().Is6())
if err != nil {
return E.Cause(err, "configure tproxy")
}
err = redir.TProxyUDP(fd, l.bind.Addr().Is6())
if err != nil {
return E.Cause(err, "configure tproxy")
}
}
l.UDPConn = udpConn
if _, threadUnsafeHandler := common.Cast[N.ThreadUnsafeWriter](l.handler); threadUnsafeHandler {
go l.loopThreadSafe()
} else {
go l.loop()
}
go l.loopBack()
return nil
}
func (l *Listener) Close() error {
if l == nil || l.UDPConn == nil {
return nil
}
return l.UDPConn.Close()
}
func (l *Listener) loop() {
defer close(l.closed)
_buffer := buf.StackNewPacket()
defer common.KeepAlive(_buffer)
buffer := common.Dup(_buffer)
defer buffer.Release()
buffer.IncRef()
defer buffer.DecRef()
if !l.tproxy {
for {
buffer.Reset()
n, addr, err := l.ReadFromUDPAddrPort(buffer.FreeBytes())
if err != nil {
l.handler.HandleError(E.New("udp listener closed: ", err))
return
}
buffer.Truncate(n)
err = l.handler.NewPacket(context.Background(), l, buffer, M.Metadata{
Protocol: "udp",
Source: M.SocksaddrFromNetIP(addr),
})
if err != nil {
l.handler.HandleError(err)
}
}
} else {
_oob := make([]byte, 1024)
defer common.KeepAlive(_oob)
oob := common.Dup(_oob)
for {
buffer.Reset()
n, oobN, _, addr, err := l.ReadMsgUDPAddrPort(buffer.FreeBytes(), oob)
if err != nil {
l.handler.HandleError(E.New("udp listener closed: ", err))
return
}
buffer.Truncate(n)
destination, err := redir.GetOriginalDestinationFromOOB(oob[:oobN])
if err != nil {
l.handler.HandleError(E.Cause(err, "get original destination"))
continue
}
buffer.Resize(buf.ReversedHeader, n)
err = l.handler.NewPacket(context.Background(), l, buffer, M.Metadata{
Protocol: "tproxy",
Source: M.SocksaddrFromNetIP(addr),
Destination: M.SocksaddrFromNetIP(destination),
})
if err != nil {
l.handler.HandleError(err)
}
}
}
}
func (l *Listener) loopThreadSafe() {
defer close(l.closed)
if !l.tproxy {
for {
buffer := buf.NewPacket()
n, addr, err := l.ReadFromUDPAddrPort(buffer.FreeBytes())
if err != nil {
buffer.Release()
l.handler.HandleError(E.New("udp listener closed: ", err))
return
}
buffer.Truncate(n)
err = l.handler.NewPacket(context.Background(), l, buffer, M.Metadata{
Protocol: "udp",
Source: M.SocksaddrFromNetIP(addr),
})
if err != nil {
buffer.Release()
l.handler.HandleError(err)
}
}
} else {
_oob := make([]byte, 1024)
defer common.KeepAlive(_oob)
oob := common.Dup(_oob)
for {
buffer := buf.NewPacket()
n, oobN, _, addr, err := l.ReadMsgUDPAddrPort(buffer.FreeBytes(), oob)
if err != nil {
l.handler.HandleError(E.New("udp listener closed: ", err))
return
}
buffer.Truncate(n)
destination, err := redir.GetOriginalDestinationFromOOB(oob[:oobN])
if err != nil {
buffer.Release()
l.handler.HandleError(E.Cause(err, "get original destination"))
continue
}
buffer.Resize(buf.ReversedHeader, n)
err = l.handler.NewPacket(context.Background(), l, buffer, M.Metadata{
Protocol: "tproxy",
Source: M.SocksaddrFromNetIP(addr),
Destination: M.SocksaddrFromNetIP(destination),
})
if err != nil {
buffer.Release()
l.handler.HandleError(err)
}
}
}
}

View file

@ -1,11 +0,0 @@
package udp
import "github.com/sagernet/sing/common/redir"
type Option func(*Listener)
func WithTransproxyMode(mode redir.TransproxyMode) Option {
return func(listener *Listener) {
listener.tproxy = mode == redir.ModeTProxy
}
}