mirror of
https://github.com/SagerNet/sing.git
synced 2025-04-03 20:07: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
|
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
2
go.mod
|
@ -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
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
|
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 {
|
|
@ -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