mirror of
https://github.com/SagerNet/sing.git
synced 2025-04-03 03:47:38 +03:00
Trim repo
This commit is contained in:
parent
293cfb66d6
commit
323ace94fd
18 changed files with 33 additions and 849 deletions
|
@ -1,11 +0,0 @@
|
|||
package debug
|
||||
|
||||
import (
|
||||
"runtime/debug"
|
||||
)
|
||||
|
||||
func Free() {
|
||||
if Enabled {
|
||||
debug.FreeOSMemory()
|
||||
}
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
package redir
|
||||
|
||||
type TransproxyMode uint8
|
||||
|
||||
const (
|
||||
ModeDisabled TransproxyMode = iota
|
||||
ModeRedirect
|
||||
ModeTProxy
|
||||
)
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -1,16 +1,18 @@
|
|||
package rw
|
||||
|
||||
import "golang.org/x/sys/windows"
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func WriteV(fd uintptr, data ...[]byte) (int, error) {
|
||||
var n uint32
|
||||
buffers := make([]*windows.WSABuf, len(data))
|
||||
buffers := make([]*syscall.WSABuf, len(data))
|
||||
for i, buf := range data {
|
||||
buffers[i] = &windows.WSABuf{
|
||||
buffers[i] = &syscall.WSABuf{
|
||||
Len: uint32(len(buf)),
|
||||
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
|
||||
}
|
||||
|
|
2
go.mod
2
go.mod
|
@ -1,5 +1,3 @@
|
|||
module github.com/sagernet/sing
|
||||
|
||||
go 1.18
|
||||
|
||||
require golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b
|
||||
|
|
2
go.sum
2
go.sum
|
@ -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=
|
|
@ -1,6 +1,7 @@
|
|||
package http
|
||||
|
||||
import (
|
||||
std_bufio "bufio"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"net"
|
||||
|
@ -10,6 +11,8 @@ import (
|
|||
|
||||
"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"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
|
@ -19,7 +22,26 @@ import (
|
|||
type Handler interface {
|
||||
N.TCPConnectionHandler
|
||||
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 {
|
||||
|
@ -72,6 +94,7 @@ func HandleRequest(ctx context.Context, request *http.Request, conn net.Conn, au
|
|||
return responseWith(request, http.StatusBadRequest).Write(conn)
|
||||
}
|
||||
|
||||
var innerErr error
|
||||
if httpClient == nil {
|
||||
httpClient = &http.Client{
|
||||
Transport: &http.Transport{
|
||||
|
@ -89,8 +112,8 @@ func HandleRequest(ctx context.Context, request *http.Request, conn net.Conn, au
|
|||
go func() {
|
||||
err := handler.NewConnection(ctx, right, metadata)
|
||||
if err != nil {
|
||||
innerErr = err
|
||||
common.Close(left, right)
|
||||
handler.HandleError(err)
|
||||
}
|
||||
}()
|
||||
return left, nil
|
||||
|
@ -104,8 +127,7 @@ func HandleRequest(ctx context.Context, request *http.Request, conn net.Conn, au
|
|||
|
||||
response, err := httpClient.Do(request)
|
||||
if err != nil {
|
||||
handler.HandleError(err)
|
||||
return responseWith(request, http.StatusBadGateway).Write(conn)
|
||||
return common.AnyError(innerErr, err, responseWith(request, http.StatusBadGateway).Write(conn))
|
||||
}
|
||||
|
||||
removeHopByHopHeaders(response.Header)
|
||||
|
@ -120,7 +142,7 @@ func HandleRequest(ctx context.Context, request *http.Request, conn net.Conn, au
|
|||
|
||||
err = response.Write(conn)
|
||||
if err != nil {
|
||||
return err
|
||||
return common.AnyError(innerErr, err)
|
||||
}
|
||||
|
||||
if !keepAlive {
|
|
@ -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,
|
||||
)
|
||||
}
|
|
@ -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() + `";
|
||||
}`
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue