Merge branch 'dev' of https://github.com/Dreamacro/clash into Alpha

This commit is contained in:
wwqgtxx
2022-11-12 20:43:48 +08:00
51 changed files with 641 additions and 742 deletions

View File

@ -5,7 +5,6 @@ import (
"net"
"net/http"
"strings"
"time"
"github.com/Dreamacro/clash/adapter/inbound"
"github.com/Dreamacro/clash/common/cache"
@ -15,7 +14,7 @@ import (
"github.com/Dreamacro/clash/log"
)
func HandleConn(c net.Conn, in chan<- C.ConnContext, cache *cache.Cache[string, bool]) {
func HandleConn(c net.Conn, in chan<- C.ConnContext, cache *cache.LruCache[string, bool]) {
client := newClient(c.RemoteAddr(), in)
defer client.CloseIdleConnections()
@ -62,9 +61,9 @@ func HandleConn(c net.Conn, in chan<- C.ConnContext, cache *cache.Cache[string,
request.RequestURI = ""
if isUpgradeRequest(request) {
if resp = handleUpgrade(conn, conn.RemoteAddr(), request, in); resp == nil {
return // hijack connection
}
handleUpgrade(conn, request, in)
return // hijack connection
}
removeHopByHopHeaders(request.Header)
@ -99,7 +98,7 @@ func HandleConn(c net.Conn, in chan<- C.ConnContext, cache *cache.Cache[string,
_ = conn.Close()
}
func authenticate(request *http.Request, cache *cache.Cache[string, bool]) *http.Response {
func authenticate(request *http.Request, cache *cache.LruCache[string, bool]) *http.Response {
authenticator := authStore.Authenticator()
if authenticator != nil {
credential := parseBasicProxyAuthorization(request)
@ -109,11 +108,11 @@ func authenticate(request *http.Request, cache *cache.Cache[string, bool]) *http
return resp
}
var authed bool
if authed = cache.Get(credential); !authed {
authed, exist := cache.Get(credential)
if !exist {
user, pass, err := decodeBasicProxyAuthorization(credential)
authed = err == nil && authenticator.Verify(user, pass)
cache.Put(credential, authed, time.Minute)
cache.Set(credential, authed)
}
if !authed {
log.Infoln("Auth failed from %s", request.RemoteAddr)

View File

@ -4,7 +4,6 @@ import (
"context"
"github.com/database64128/tfo-go"
"net"
"time"
"github.com/Dreamacro/clash/common/cache"
C "github.com/Dreamacro/clash/constant"
@ -46,9 +45,9 @@ func NewWithAuthenticate(addr string, in chan<- C.ConnContext, authenticate bool
return nil, err
}
var c *cache.Cache[string, bool]
var c *cache.LruCache[string, bool]
if authenticate {
c = cache.New[string, bool](time.Second * 30)
c = cache.New[string, bool](cache.WithAge[string, bool](30))
}
hl := &Listener{

View File

@ -6,7 +6,6 @@ import (
"net"
"net/http"
"strings"
"time"
"github.com/Dreamacro/clash/adapter/inbound"
N "github.com/Dreamacro/clash/common/net"
@ -26,17 +25,15 @@ func isUpgradeRequest(req *http.Request) bool {
return false
}
func handleUpgrade(localConn net.Conn, source net.Addr, request *http.Request, in chan<- C.ConnContext) (resp *http.Response) {
func handleUpgrade(conn net.Conn, request *http.Request, in chan<- C.ConnContext) {
defer conn.Close()
removeProxyHeaders(request.Header)
removeExtraHTTPHostPort(request)
address := request.Host
if _, _, err := net.SplitHostPort(address); err != nil {
port := "80"
if request.TLS != nil {
port = "443"
}
address = net.JoinHostPort(address, port)
address = net.JoinHostPort(address, "80")
}
dstAddr := socks5.ParseAddr(address)
@ -46,9 +43,9 @@ func handleUpgrade(localConn net.Conn, source net.Addr, request *http.Request, i
left, right := net.Pipe()
in <- inbound.NewHTTP(dstAddr, source, right)
in <- inbound.NewHTTP(dstAddr, conn.RemoteAddr(), right)
var remoteServer *N.BufferedConn
var bufferedLeft *N.BufferedConn
if request.TLS != nil {
tlsConn := tls.Client(left, &tls.Config{
ServerName: request.URL.Hostname(),
@ -57,47 +54,36 @@ func handleUpgrade(localConn net.Conn, source net.Addr, request *http.Request, i
ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTLSTimeout)
defer cancel()
if tlsConn.HandshakeContext(ctx) != nil {
_ = localConn.Close()
_ = left.Close()
return
}
remoteServer = N.NewBufferedConn(tlsConn)
bufferedLeft = N.NewBufferedConn(tlsConn)
} else {
remoteServer = N.NewBufferedConn(left)
bufferedLeft = N.NewBufferedConn(left)
}
defer func() {
_ = remoteServer.Close()
_ = bufferedLeft.Close()
}()
err := request.Write(remoteServer)
err := request.Write(bufferedLeft)
if err != nil {
_ = localConn.Close()
return
}
resp, err = http.ReadResponse(remoteServer.Reader(), request)
resp, err := http.ReadResponse(bufferedLeft.Reader(), request)
if err != nil {
return
}
removeProxyHeaders(resp.Header)
err = resp.Write(conn)
if err != nil {
_ = localConn.Close()
return
}
if resp.StatusCode == http.StatusSwitchingProtocols {
removeProxyHeaders(resp.Header)
err = localConn.SetReadDeadline(time.Time{}) // set to not time out
if err != nil {
return
}
err = resp.Write(localConn)
if err != nil {
return
}
N.Relay(remoteServer, localConn) // blocking here
_ = localConn.Close()
resp = nil
N.Relay(bufferedLeft, conn)
}
return
}

View File

@ -4,7 +4,6 @@ import (
"context"
"github.com/database64128/tfo-go"
"net"
"time"
"github.com/Dreamacro/clash/common/cache"
N "github.com/Dreamacro/clash/common/net"
@ -18,8 +17,9 @@ import (
type Listener struct {
listener net.Listener
addr string
cache *cache.Cache[string, bool]
closed bool
cache *cache.LruCache[string, bool]
closed bool
}
// RawAddress implements C.Listener
@ -50,7 +50,7 @@ func New(addr string, inboundTfo bool, in chan<- C.ConnContext) (*Listener, erro
ml := &Listener{
listener: l,
addr: addr,
cache: cache.New[string, bool](30 * time.Second),
cache: cache.New[string, bool](cache.WithAge[string, bool](30)),
}
go func() {
for {
@ -68,7 +68,7 @@ func New(addr string, inboundTfo bool, in chan<- C.ConnContext) (*Listener, erro
return ml, nil
}
func handleConn(conn net.Conn, in chan<- C.ConnContext, cache *cache.Cache[string, bool]) {
func handleConn(conn net.Conn, in chan<- C.ConnContext, cache *cache.LruCache[string, bool]) {
conn.(*net.TCPConn).SetKeepAlive(true)
bufConn := N.NewBufferedConn(conn)

View File

@ -2,13 +2,14 @@ package tproxy
import (
"net"
"net/netip"
"github.com/Dreamacro/clash/common/pool"
)
type packet struct {
pc net.PacketConn
lAddr *net.UDPAddr
lAddr netip.AddrPort
buf []byte
}
@ -18,7 +19,7 @@ func (c *packet) Data() []byte {
// WriteBack opens a new socket binding `addr` to write UDP packet back
func (c *packet) WriteBack(b []byte, addr net.Addr) (n int, err error) {
tc, err := dialUDP("udp", addr.(*net.UDPAddr), c.lAddr)
tc, err := dialUDP("udp", addr.(*net.UDPAddr).AddrPort(), c.lAddr)
if err != nil {
n = 0
return
@ -30,7 +31,7 @@ func (c *packet) WriteBack(b []byte, addr net.Addr) (n int, err error) {
// LocalAddr returns the source IP/Port of UDP Packet
func (c *packet) LocalAddr() net.Addr {
return c.lAddr
return &net.UDPAddr{IP: c.lAddr.Addr().AsSlice(), Port: int(c.lAddr.Port()), Zone: c.lAddr.Addr().Zone()}
}
func (c *packet) Drop() {

View File

@ -2,6 +2,7 @@ package tproxy
import (
"net"
"net/netip"
"github.com/Dreamacro/clash/adapter/inbound"
"github.com/Dreamacro/clash/common/pool"
@ -58,7 +59,7 @@ func NewUDP(addr string, in chan<- *inbound.PacketAdapter) (*UDPListener, error)
oob := make([]byte, 1024)
for {
buf := pool.Get(pool.UDPBufferSize)
n, oobn, _, lAddr, err := c.ReadMsgUDP(buf, oob)
n, oobn, _, lAddr, err := c.ReadMsgUDPAddrPort(buf, oob)
if err != nil {
pool.Put(buf)
if rl.closed {
@ -67,10 +68,15 @@ func NewUDP(addr string, in chan<- *inbound.PacketAdapter) (*UDPListener, error)
continue
}
rAddr, err := getOrigDst(oob, oobn)
rAddr, err := getOrigDst(oob[:oobn])
if err != nil {
continue
}
if rAddr.Addr().Is4() {
// try to unmap 4in6 address
lAddr = netip.AddrPortFrom(lAddr.Addr().Unmap(), lAddr.Port())
}
handlePacketConn(l, in, buf[:n], lAddr, rAddr)
}
}()
@ -78,8 +84,8 @@ func NewUDP(addr string, in chan<- *inbound.PacketAdapter) (*UDPListener, error)
return rl, nil
}
func handlePacketConn(pc net.PacketConn, in chan<- *inbound.PacketAdapter, buf []byte, lAddr *net.UDPAddr, rAddr *net.UDPAddr) {
target := socks5.ParseAddrToSocksAddr(rAddr)
func handlePacketConn(pc net.PacketConn, in chan<- *inbound.PacketAdapter, buf []byte, lAddr, rAddr netip.AddrPort) {
target := socks5.AddrFromStdAddrPort(rAddr)
pkt := &packet{
pc: pc,
lAddr: lAddr,

View File

@ -3,13 +3,14 @@
package tproxy
import (
"encoding/binary"
"errors"
"fmt"
"net"
"net/netip"
"os"
"strconv"
"syscall"
"golang.org/x/sys/unix"
)
const (
@ -19,7 +20,7 @@ const (
// dialUDP acts like net.DialUDP for transparent proxy.
// It binds to a non-local address(`lAddr`).
func dialUDP(network string, lAddr *net.UDPAddr, rAddr *net.UDPAddr) (*net.UDPConn, error) {
func dialUDP(network string, lAddr, rAddr netip.AddrPort) (uc *net.UDPConn, err error) {
rSockAddr, err := udpAddrToSockAddr(rAddr)
if err != nil {
return nil, err
@ -35,23 +36,25 @@ func dialUDP(network string, lAddr *net.UDPAddr, rAddr *net.UDPAddr) (*net.UDPCo
return nil, err
}
defer func() {
if err != nil {
syscall.Close(fd)
}
}()
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
}
@ -60,35 +63,26 @@ func dialUDP(network string, lAddr *net.UDPAddr, rAddr *net.UDPAddr) (*net.UDPCo
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 udpAddrToSockAddr(addr netip.AddrPort) (syscall.Sockaddr, error) {
if addr.Addr().Is4() {
return &syscall.SockaddrInet4{Addr: addr.Addr().As4(), Port: int(addr.Port())}, nil
}
zoneID, err := strconv.ParseUint(addr.Addr().Zone(), 10, 32)
if err != nil {
zoneID = 0
}
return &syscall.SockaddrInet6{Addr: addr.Addr().As16(), Port: int(addr.Port()), ZoneId: uint32(zoneID)}, nil
}
func udpAddrFamily(net string, lAddr, rAddr *net.UDPAddr) int {
func udpAddrFamily(net string, lAddr, rAddr netip.AddrPort) int {
switch net[len(net)-1] {
case '4':
return syscall.AF_INET
@ -96,29 +90,35 @@ func udpAddrFamily(net string, lAddr, rAddr *net.UDPAddr) int {
return syscall.AF_INET6
}
if (lAddr == nil || lAddr.IP.To4() != nil) && (rAddr == nil || lAddr.IP.To4() != nil) {
if lAddr.Addr().Is4() && rAddr.Addr().Is4() {
return syscall.AF_INET
}
return syscall.AF_INET6
}
func getOrigDst(oob []byte, oobn int) (*net.UDPAddr, error) {
msgs, err := syscall.ParseSocketControlMessage(oob[:oobn])
func getOrigDst(oob []byte) (netip.AddrPort, error) {
// oob contains socket control messages which we need to parse.
scms, err := unix.ParseSocketControlMessage(oob)
if err != nil {
return nil, err
return netip.AddrPort{}, fmt.Errorf("parse control message: %w", err)
}
for _, msg := range msgs {
if msg.Header.Level == syscall.SOL_IP && msg.Header.Type == syscall.IP_RECVORIGDSTADDR {
ip := net.IP(msg.Data[4:8])
port := binary.BigEndian.Uint16(msg.Data[2:4])
return &net.UDPAddr{IP: ip, Port: int(port)}, nil
} else if msg.Header.Level == syscall.SOL_IPV6 && msg.Header.Type == IPV6_RECVORIGDSTADDR {
ip := net.IP(msg.Data[8:24])
port := binary.BigEndian.Uint16(msg.Data[2:4])
return &net.UDPAddr{IP: ip, Port: int(port)}, nil
}
// retrieve the destination address from the SCM.
sa, err := unix.ParseOrigDstAddr(&scms[0])
if err != nil {
return netip.AddrPort{}, fmt.Errorf("retrieve destination: %w", err)
}
return nil, errors.New("cannot find origDst")
// encode the destination address into a cmsg.
var rAddr netip.AddrPort
switch v := sa.(type) {
case *unix.SockaddrInet4:
rAddr = netip.AddrPortFrom(netip.AddrFrom4(v.Addr), uint16(v.Port))
case *unix.SockaddrInet6:
rAddr = netip.AddrPortFrom(netip.AddrFrom16(v.Addr), uint16(v.Port))
default:
return netip.AddrPort{}, fmt.Errorf("unsupported address type: %T", v)
}
return rAddr, nil
}

View File

@ -5,12 +5,13 @@ package tproxy
import (
"errors"
"net"
"net/netip"
)
func getOrigDst(oob []byte, oobn int) (*net.UDPAddr, error) {
return nil, errors.New("UDP redir not supported on current platform")
func getOrigDst(oob []byte) (netip.AddrPort, error) {
return netip.AddrPort{}, errors.New("UDP redir not supported on current platform")
}
func dialUDP(network string, lAddr *net.UDPAddr, rAddr *net.UDPAddr) (*net.UDPConn, error) {
func dialUDP(network string, lAddr, rAddr netip.AddrPort) (*net.UDPConn, error) {
return nil, errors.New("UDP redir not supported on current platform")
}