Liu Song’s Projects


~/Projects/sing

git clone https://code.lsong.org/sing

Commit

Commit
323ace94fda2d86a4580e8854226886ff467c757
Author
世界 <[email protected]>
Date
2022-06-30 17:38:12 +0800 +0800
Diffstat
 common/debug/free.go | 11 -
 common/redir/mode.go | 9 -
 common/redir/redir_linux.go | 37 ----
 common/redir/redir_other.go | 13 -
 common/redir/tproxy_linux.go | 136 -----------------
 common/redir/tproxy_other.go | 30 ---
 common/rw/writev_windows.go | 10 
 go.mod | 2 
 go.sum | 2 
  | 32 +++
 transport/mixed/listener.go | 158 -------------------
 transport/mixed/pac.go | 12 -
 transport/system/sockopt_linux.go | 14 -
 transport/system/sockopt_other.go | 9 -
 transport/tcp/listener.go | 121 ---------------
 transport/tcp/options.go | 11 -
 transport/udp/listener.go | 264 ---------------------------------
 transport/udp/options.go | 11 -

Trim repo


diff --git a/common/debug/free.go b/common/debug/free.go
deleted file mode 100644
index 619819fc0ab7caa6d3a469e00e6483d87f8700de..0000000000000000000000000000000000000000
--- a/common/debug/free.go
+++ /dev/null
@@ -1,11 +0,0 @@
-package debug
-
-import (
-	"runtime/debug"
-)
-
-func Free() {
-	if Enabled {
-		debug.FreeOSMemory()
-	}
-}




diff --git a/common/redir/mode.go b/common/redir/mode.go
deleted file mode 100644
index a265ab74d1acc99207a4a74a748ea3c540470214..0000000000000000000000000000000000000000
--- a/common/redir/mode.go
+++ /dev/null
@@ -1,9 +0,0 @@
-package redir
-
-type TransproxyMode uint8
-
-const (
-	ModeDisabled TransproxyMode = iota
-	ModeRedirect
-	ModeTProxy
-)




diff --git a/common/redir/redir_linux.go b/common/redir/redir_linux.go
deleted file mode 100644
index abb1b1a75f03f67591232bbc2f6cf0f37e235035..0000000000000000000000000000000000000000
--- a/common/redir/redir_linux.go
+++ /dev/null
@@ -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
-	}
-}




diff --git a/common/redir/redir_other.go b/common/redir/redir_other.go
deleted file mode 100644
index a24a2702b22d76ad8de1e308ebdc24f2e1a53b8b..0000000000000000000000000000000000000000
--- a/common/redir/redir_other.go
+++ /dev/null
@@ -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")
-}




diff --git a/common/redir/tproxy_linux.go b/common/redir/tproxy_linux.go
deleted file mode 100644
index 0984332415953c19a906ed071cb907d551d15589..0000000000000000000000000000000000000000
--- a/common/redir/tproxy_linux.go
+++ /dev/null
@@ -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
-}




diff --git a/common/redir/tproxy_other.go b/common/redir/tproxy_other.go
deleted file mode 100644
index 26778e6b85e2fdf93662b027cf5cb74691947275..0000000000000000000000000000000000000000
--- a/common/redir/tproxy_other.go
+++ /dev/null
@@ -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")
-}




diff --git a/common/rw/writev_windows.go b/common/rw/writev_windows.go
index d053450d9fee24bc4e88c5aad8c1f723167edc6d..724d4f4ab2de71e20e12739eb3c99b9f18dd1c62 100644
--- a/common/rw/writev_windows.go
+++ b/common/rw/writev_windows.go
@@ -1,17 +1,19 @@
 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],
 		}
 	}
+
 package rw
-import "golang.org/x/sys/windows"
 	return int(n), err
 }




diff --git a/go.mod b/go.mod
index d6bd075dcbb9c69c7026cfd23fd0c632ce9d39af..219df7701232b214eba5070b56761d760bc5c2bb 100644
--- a/go.mod
+++ b/go.mod
@@ -1,5 +1,3 @@
 module github.com/sagernet/sing
 
 go 1.18
-
-require golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b




diff --git a/go.sum b/go.sum
index a2987a26fdd170eb1355dd264094829ddfd78e6b..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644
--- a/go.sum
+++ b/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=




diff --git a/protocol/http/handshake.go b/protocol/http/handshake.go
new file mode 100644
index 0000000000000000000000000000000000000000..b0603d14b50c2c0a0ae923eee054510e47044ff6
--- /dev/null
+++ b/protocol/http/handshake.go
@@ -0,0 +1,200 @@
+package http
+
+import (
+	std_bufio "bufio"
+	"context"
+	"encoding/base64"
+	"net"
+	"net/http"
+	"strings"
+	"time"
+
+	"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"
+	N "github.com/sagernet/sing/common/network"
+)
+
+type Handler interface {
+	N.TCPConnectionHandler
+	N.UDPConnectionHandler
+}
+
+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 {
+	var httpClient *http.Client
+	for {
+		if authenticator != nil {
+			var authOk bool
+			authorization := request.Header.Get("Proxy-Authorization")
+			if strings.HasPrefix(authorization, "BASIC ") {
+				userPassword, _ := base64.URLEncoding.DecodeString(authorization[6:])
+				userPswdArr := strings.SplitN(string(userPassword), ":", 2)
+				authOk = authenticator.Verify(userPswdArr[0], userPswdArr[1])
+			}
+			if !authOk {
+				err := responseWith(request, http.StatusProxyAuthRequired).Write(conn)
+				if err != nil {
+					return err
+				}
+			}
+		}
+
+		if request.Method == "CONNECT" {
+			portStr := request.URL.Port()
+			if portStr == "" {
+				portStr = "80"
+			}
+			destination := M.ParseSocksaddrHostPortStr(request.URL.Hostname(), portStr)
+			_, err := conn.Write([]byte(F.ToString("HTTP/", request.ProtoMajor, ".", request.ProtoMinor, " 200 Connection established\r\n\r\n")))
+			if err != nil {
+				return E.Cause(err, "write http response")
+			}
+			metadata.Protocol = "http"
+			metadata.Destination = destination
+			return handler.NewConnection(ctx, conn, metadata)
+		}
+
+		keepAlive := strings.TrimSpace(strings.ToLower(request.Header.Get("Proxy-Connection"))) == "keep-alive"
+
+		host := request.Header.Get("Host")
+		if host != "" {
+			request.Host = host
+		}
+
+		request.RequestURI = ""
+
+		removeHopByHopHeaders(request.Header)
+		removeExtraHTTPHostPort(request)
+
+		if request.URL.Scheme == "" || request.URL.Host == "" {
+			return responseWith(request, http.StatusBadRequest).Write(conn)
+		}
+
+		var innerErr error
+		if httpClient == nil {
+			httpClient = &http.Client{
+				Transport: &http.Transport{
+					MaxIdleConns:          100,
+					IdleConnTimeout:       90 * time.Second,
+					TLSHandshakeTimeout:   10 * time.Second,
+					ExpectContinueTimeout: 1 * time.Second,
+					DialContext: func(context context.Context, network, address string) (net.Conn, error) {
+						if network != "tcp" && network != "tcp4" && network != "tcp6" {
+							return nil, E.New("unsupported network ", network)
+						}
+						metadata.Destination = M.ParseSocksaddr(address)
+						metadata.Protocol = "http"
+						left, right := net.Pipe()
+						go func() {
+							err := handler.NewConnection(ctx, right, metadata)
+							if err != nil {
+								innerErr = err
+								common.Close(left, right)
+							}
+						}()
+						return left, nil
+					},
+				},
+				CheckRedirect: func(req *http.Request, via []*http.Request) error {
+					return http.ErrUseLastResponse
+				},
+			}
+		}
+
+		response, err := httpClient.Do(request)
+		if err != nil {
+			return common.AnyError(innerErr, err, responseWith(request, http.StatusBadGateway).Write(conn))
+		}
+
+		removeHopByHopHeaders(response.Header)
+
+		if keepAlive {
+			response.Header.Set("Proxy-Connection", "keep-alive")
+			response.Header.Set("Connection", "keep-alive")
+			response.Header.Set("Keep-Alive", "timeout=4")
+		}
+
+		response.Close = !keepAlive
+
+		err = response.Write(conn)
+		if err != nil {
+			return common.AnyError(innerErr, err)
+		}
+
+		if !keepAlive {
+			return conn.Close()
+		}
+	}
+}
+
+func removeHopByHopHeaders(header http.Header) {
+	// Strip hop-by-hop header based on RFC:
+	// http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.5.1
+	// https://www.mnot.net/blog/2011/07/11/what_proxies_must_do
+
+	header.Del("Proxy-Connection")
+	header.Del("Proxy-Authenticate")
+	header.Del("Proxy-Authorization")
+	header.Del("TE")
+	header.Del("Trailers")
+	header.Del("Transfer-Encoding")
+	header.Del("Upgrade")
+
+	connections := header.Get("Connection")
+	header.Del("Connection")
+	if len(connections) == 0 {
+		return
+	}
+	for _, h := range strings.Split(connections, ",") {
+		header.Del(strings.TrimSpace(h))
+	}
+}
+
+func removeExtraHTTPHostPort(req *http.Request) {
+	host := req.Host
+	if host == "" {
+		host = req.URL.Host
+	}
+
+	if pHost, port, err := net.SplitHostPort(host); err == nil && port == "80" {
+		host = pHost
+	}
+
+	req.Host = host
+	req.URL.Host = host
+}
+
+func responseWith(request *http.Request, statusCode int) *http.Response {
+	return &http.Response{
+		StatusCode: statusCode,
+		Status:     http.StatusText(statusCode),
+		Proto:      request.Proto,
+		ProtoMajor: request.ProtoMajor,
+		ProtoMinor: request.ProtoMinor,
+		Header:     http.Header{},
+	}
+}




diff --git a/protocol/http/listener.go b/protocol/http/listener.go
deleted file mode 100644
index 96b6dd9efb4c885830b3703cf3b7539abdecc46d..0000000000000000000000000000000000000000
--- a/protocol/http/listener.go
+++ /dev/null
@@ -1,178 +0,0 @@
-package http
-
-import (
-	"context"
-	"encoding/base64"
-	"net"
-	"net/http"
-	"strings"
-	"time"
-
-	"github.com/sagernet/sing/common"
-	"github.com/sagernet/sing/common/auth"
-	E "github.com/sagernet/sing/common/exceptions"
-	F "github.com/sagernet/sing/common/format"
-	M "github.com/sagernet/sing/common/metadata"
-	N "github.com/sagernet/sing/common/network"
-)
-
-type Handler interface {
-	N.TCPConnectionHandler
-	N.UDPConnectionHandler
-	E.Handler
-}
-
-func HandleRequest(ctx context.Context, request *http.Request, conn net.Conn, authenticator auth.Authenticator, handler Handler, metadata M.Metadata) error {
-	var httpClient *http.Client
-	for {
-		if authenticator != nil {
-			var authOk bool
-			authorization := request.Header.Get("Proxy-Authorization")
-			if strings.HasPrefix(authorization, "BASIC ") {
-				userPassword, _ := base64.URLEncoding.DecodeString(authorization[6:])
-				userPswdArr := strings.SplitN(string(userPassword), ":", 2)
-				authOk = authenticator.Verify(userPswdArr[0], userPswdArr[1])
-			}
-			if !authOk {
-				err := responseWith(request, http.StatusProxyAuthRequired).Write(conn)
-				if err != nil {
-					return err
-				}
-			}
-		}
-
-		if request.Method == "CONNECT" {
-			portStr := request.URL.Port()
-			if portStr == "" {
-				portStr = "80"
-			}
-			destination := M.ParseSocksaddrHostPortStr(request.URL.Hostname(), portStr)
-			_, err := conn.Write([]byte(F.ToString("HTTP/", request.ProtoMajor, ".", request.ProtoMinor, " 200 Connection established\r\n\r\n")))
-			if err != nil {
-				return E.Cause(err, "write http response")
-			}
-			metadata.Protocol = "http"
-			metadata.Destination = destination
-			return handler.NewConnection(ctx, conn, metadata)
-		}
-
-		keepAlive := strings.TrimSpace(strings.ToLower(request.Header.Get("Proxy-Connection"))) == "keep-alive"
-
-		host := request.Header.Get("Host")
-		if host != "" {
-			request.Host = host
-		}
-
-		request.RequestURI = ""
-
-		removeHopByHopHeaders(request.Header)
-		removeExtraHTTPHostPort(request)
-
-		if request.URL.Scheme == "" || request.URL.Host == "" {
-			return responseWith(request, http.StatusBadRequest).Write(conn)
-		}
-
-		if httpClient == nil {
-			httpClient = &http.Client{
-				Transport: &http.Transport{
-					MaxIdleConns:          100,
-					IdleConnTimeout:       90 * time.Second,
-					TLSHandshakeTimeout:   10 * time.Second,
-					ExpectContinueTimeout: 1 * time.Second,
-					DialContext: func(context context.Context, network, address string) (net.Conn, error) {
-						if network != "tcp" && network != "tcp4" && network != "tcp6" {
-							return nil, E.New("unsupported network ", network)
-						}
-						metadata.Destination = M.ParseSocksaddr(address)
-						metadata.Protocol = "http"
-						left, right := net.Pipe()
-						go func() {
-							err := handler.NewConnection(ctx, right, metadata)
-							if err != nil {
-								common.Close(left, right)
-								handler.HandleError(err)
-							}
-						}()
-						return left, nil
-					},
-				},
-				CheckRedirect: func(req *http.Request, via []*http.Request) error {
-					return http.ErrUseLastResponse
-				},
-			}
-		}
-
-		response, err := httpClient.Do(request)
-		if err != nil {
-			handler.HandleError(err)
-			return responseWith(request, http.StatusBadGateway).Write(conn)
-		}
-
-		removeHopByHopHeaders(response.Header)
-
-		if keepAlive {
-			response.Header.Set("Proxy-Connection", "keep-alive")
-			response.Header.Set("Connection", "keep-alive")
-			response.Header.Set("Keep-Alive", "timeout=4")
-		}
-
-		response.Close = !keepAlive
-
-		err = response.Write(conn)
-		if err != nil {
-			return err
-		}
-
-		if !keepAlive {
-			return conn.Close()
-		}
-	}
-}
-
-func removeHopByHopHeaders(header http.Header) {
-	// Strip hop-by-hop header based on RFC:
-	// http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.5.1
-	// https://www.mnot.net/blog/2011/07/11/what_proxies_must_do
-
-	header.Del("Proxy-Connection")
-	header.Del("Proxy-Authenticate")
-	header.Del("Proxy-Authorization")
-	header.Del("TE")
-	header.Del("Trailers")
-	header.Del("Transfer-Encoding")
-	header.Del("Upgrade")
-
-	connections := header.Get("Connection")
-	header.Del("Connection")
-	if len(connections) == 0 {
-		return
-	}
-	for _, h := range strings.Split(connections, ",") {
-		header.Del(strings.TrimSpace(h))
-	}
-}
-
-func removeExtraHTTPHostPort(req *http.Request) {
-	host := req.Host
-	if host == "" {
-		host = req.URL.Host
-	}
-
-	if pHost, port, err := net.SplitHostPort(host); err == nil && port == "80" {
-		host = pHost
-	}
-
-	req.Host = host
-	req.URL.Host = host
-}
-
-func responseWith(request *http.Request, statusCode int) *http.Response {
-	return &http.Response{
-		StatusCode: statusCode,
-		Status:     http.StatusText(statusCode),
-		Proto:      request.Proto,
-		ProtoMajor: request.ProtoMajor,
-		ProtoMinor: request.ProtoMinor,
-		Header:     http.Header{},
-	}
-}




diff --git a/transport/mixed/listener.go b/transport/mixed/listener.go
deleted file mode 100644
index e804901bfeed78002fdb76d37710f17806162391..0000000000000000000000000000000000000000
--- a/transport/mixed/listener.go
+++ /dev/null
@@ -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,
-	)
-}




diff --git a/transport/mixed/pac.go b/transport/mixed/pac.go
deleted file mode 100644
index 7835d254ef18f6d1b45866f410d5791d8bebc43a..0000000000000000000000000000000000000000
--- a/transport/mixed/pac.go
+++ /dev/null
@@ -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() + `";
-}`
-}




diff --git a/transport/system/sockopt_linux.go b/transport/system/sockopt_linux.go
deleted file mode 100644
index 2f7335a174a64e1de94f74584555fbf274cc2f4a..0000000000000000000000000000000000000000
--- a/transport/system/sockopt_linux.go
+++ /dev/null
@@ -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)
-}




diff --git a/transport/system/sockopt_other.go b/transport/system/sockopt_other.go
deleted file mode 100644
index 804f39dd52c19b8029d9eeaae23401dcfb63a7c3..0000000000000000000000000000000000000000
--- a/transport/system/sockopt_other.go
+++ /dev/null
@@ -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")
-}




diff --git a/transport/tcp/listener.go b/transport/tcp/listener.go
deleted file mode 100644
index 3ae4a2d93d986ee496f7cf0821efb08b74bed29e..0000000000000000000000000000000000000000
--- a/transport/tcp/listener.go
+++ /dev/null
@@ -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)
-}




diff --git a/transport/tcp/options.go b/transport/tcp/options.go
deleted file mode 100644
index 7d12a18cbad21271e5255603f7978d9e4a8ca990..0000000000000000000000000000000000000000
--- a/transport/tcp/options.go
+++ /dev/null
@@ -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
-	}
-}




diff --git a/transport/udp/listener.go b/transport/udp/listener.go
deleted file mode 100644
index b8a5feca536453f2ef1e289c97c354c5e34ed011..0000000000000000000000000000000000000000
--- a/transport/udp/listener.go
+++ /dev/null
@@ -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)
-			}
-		}
-	}
-}




diff --git a/transport/udp/options.go b/transport/udp/options.go
deleted file mode 100644
index 2dab2208b38657eee7c938665bad285c00a2094a..0000000000000000000000000000000000000000
--- a/transport/udp/options.go
+++ /dev/null
@@ -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
-	}
-}