Merge remote-tracking branch 'pro-plus/plus-pro' into Feature
# Conflicts: # .github/workflows/Alpha.yml # .github/workflows/codeql-analysis.yml # .github/workflows/docker.yml # .github/workflows/linter.yml # .github/workflows/stale.yml # Makefile # README.md # adapter/outbound/vless.go # component/dialer/dialer.go # component/geodata/geodata.go # component/geodata/router/condition.go # config/config.go # config/initial.go # constant/metadata.go # constant/path.go # constant/rule.go # constant/rule_extra.go # dns/filters.go # go.mod # go.sum # hub/executor/executor.go # hub/route/configs.go # listener/listener.go # listener/tun/dev/dev.go # listener/tun/dev/dev_darwin.go # listener/tun/dev/dev_linux.go # listener/tun/dev/dev_windows.go # listener/tun/dev/dev_windows_extra.go # listener/tun/dev/wintun/dll_windows.go # listener/tun/dev/wintun/session_windows.go # listener/tun/ipstack/gvisor/tun.go # listener/tun/ipstack/gvisor/tundns.go # listener/tun/ipstack/stack_adapter.go # listener/tun/ipstack/system/tun.go # listener/tun/tun_adapter.go # main.go # rule/base.go # rule/common/process.go # rule/geoip.go # rule/parser.go # rule/port.go # test/go.mod # test/go.sum # test/vless_test.go # transport/vless/xtls.go # tunnel/tunnel.go
This commit is contained in:
@ -10,7 +10,6 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"sync/atomic"
|
||||
"time"
|
||||
_ "unsafe"
|
||||
|
@ -86,7 +86,5 @@ func (session Session) AllocateSendPacket(packetSize int) (packet []byte, err er
|
||||
}
|
||||
|
||||
func (session Session) SendPacket(packet []byte) {
|
||||
if packet != nil && len(packet) > 0 {
|
||||
syscall.Syscall(procWintunSendPacket.Addr(), 2, session.handle, uintptr(unsafe.Pointer(&packet[0])), 0)
|
||||
}
|
||||
syscall.Syscall(procWintunSendPacket.Addr(), 2, session.handle, uintptr(unsafe.Pointer(&packet[0])), 0)
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
@ -47,14 +48,13 @@ type gvisorAdapter struct {
|
||||
writeHandle *channel.NotificationHandle
|
||||
}
|
||||
|
||||
// NewAdapter GvisorAdapter create GvisorAdapter
|
||||
// GvisorAdapter create GvisorAdapter
|
||||
func NewAdapter(device dev.TunDevice, conf config.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (ipstack.TunAdapter, error) {
|
||||
ipstack := stack.New(stack.Options{
|
||||
NetworkProtocols: []stack.NetworkProtocolFactory{ipv4.NewProtocol, ipv6.NewProtocol},
|
||||
TransportProtocols: []stack.TransportProtocolFactory{tcp.NewProtocol, udp.NewProtocol},
|
||||
})
|
||||
|
||||
adapter := &gvisorAdapter{
|
||||
device: device,
|
||||
ipstack: ipstack,
|
||||
udpIn: udpIn,
|
||||
@ -85,10 +85,13 @@ func NewAdapter(device dev.TunDevice, conf config.Tun, tcpIn chan<- C.ConnContex
|
||||
// maximum number of half-open tcp connection set to 1024
|
||||
// receive buffer size set to 20k
|
||||
tcpFwd := tcp.NewForwarder(ipstack, pool.RelayBufferSize, 1024, func(r *tcp.ForwarderRequest) {
|
||||
src := net.JoinHostPort(r.ID().RemoteAddress.String(), strconv.Itoa((int)(r.ID().RemotePort)))
|
||||
dst := net.JoinHostPort(r.ID().LocalAddress.String(), strconv.Itoa((int)(r.ID().LocalPort)))
|
||||
log.Debugln("Get TCP Syn %v -> %s in ipstack", src, dst)
|
||||
var wq waiter.Queue
|
||||
ep, err := r.CreateEndpoint(&wq)
|
||||
if err != nil {
|
||||
log.Warnln("Can't create TCP Endpoint in ipstack: %v", err)
|
||||
log.Warnln("Can't create TCP Endpoint(%s -> %s) in ipstack: %v", src, dst, err)
|
||||
r.Complete(true)
|
||||
return
|
||||
}
|
||||
@ -113,7 +116,7 @@ func NewAdapter(device dev.TunDevice, conf config.Tun, tcpIn chan<- C.ConnContex
|
||||
ipstack.SetTransportProtocolHandler(udp.ProtocolNumber, adapter.udpHandlePacket)
|
||||
|
||||
if resolver.DefaultResolver != nil {
|
||||
err = adapter.ReCreateDNSServer(resolver.DefaultResolver.(*dns.Resolver), resolver.DefaultHostMapper.(*dns.ResolverEnhancer), conf.DnsHijack)
|
||||
err = adapter.ReCreateDNSServer(resolver.DefaultResolver.(*dns.Resolver), resolver.DefaultHostMapper.(*dns.ResolverEnhancer), conf.DNSListen)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -190,6 +193,9 @@ func (t *gvisorAdapter) AsLinkEndpoint() (result stack.LinkEndpoint, err error)
|
||||
for !t.device.IsClose() {
|
||||
packet := make([]byte, mtu)
|
||||
n, err := t.device.Read(packet)
|
||||
if n == 0 {
|
||||
continue
|
||||
}
|
||||
if err != nil && !t.device.IsClose() {
|
||||
log.Errorln("can not read from tun: %v", err)
|
||||
continue
|
||||
@ -202,9 +208,12 @@ func (t *gvisorAdapter) AsLinkEndpoint() (result stack.LinkEndpoint, err error)
|
||||
p = header.IPv6ProtocolNumber
|
||||
}
|
||||
if linkEP.IsAttached() {
|
||||
linkEP.InjectInbound(p, stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||
packetBuffer := stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||
Data: buffer.View(packet[:n]).ToVectorisedView(),
|
||||
}))
|
||||
})
|
||||
|
||||
linkEP.InjectInbound(p, packetBuffer)
|
||||
packetBuffer.DecRef()
|
||||
} else {
|
||||
log.Debugln("received packet from tun when %s is not attached to any dispatcher.", t.device.Name())
|
||||
}
|
||||
@ -222,14 +231,14 @@ func (t *gvisorAdapter) AsLinkEndpoint() (result stack.LinkEndpoint, err error)
|
||||
|
||||
// WriteNotify implements channel.Notification.WriteNotify.
|
||||
func (t *gvisorAdapter) WriteNotify() {
|
||||
packet, ok := t.linkCache.Read()
|
||||
if ok {
|
||||
packetBuffer := t.linkCache.Read()
|
||||
if packetBuffer != nil {
|
||||
var vv buffer.VectorisedView
|
||||
// Append upper headers.
|
||||
vv.AppendView(packet.Pkt.NetworkHeader().View())
|
||||
vv.AppendView(packet.Pkt.TransportHeader().View())
|
||||
vv.AppendView(packetBuffer.NetworkHeader().View())
|
||||
vv.AppendView(packetBuffer.TransportHeader().View())
|
||||
// Append data payload.
|
||||
vv.Append(packet.Pkt.Data().ExtractVV())
|
||||
vv.Append(packetBuffer.Data().ExtractVV())
|
||||
|
||||
_, err := t.device.Write(vv.ToView())
|
||||
if err != nil && !t.device.IsClose() {
|
||||
|
87
listener/tun/ipstack/lwip/dns.go
Normal file
87
listener/tun/ipstack/lwip/dns.go
Normal file
@ -0,0 +1,87 @@
|
||||
package lwip
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/Dreamacro/clash/component/resolver"
|
||||
D "github.com/Dreamacro/clash/listener/tun/ipstack/commons"
|
||||
"github.com/Dreamacro/clash/log"
|
||||
"github.com/yaling888/go-lwip"
|
||||
)
|
||||
|
||||
const defaultDnsReadTimeout = time.Second * 8
|
||||
|
||||
func shouldHijackDns(dnsIP net.IP, targetIp net.IP, targetPort int) bool {
|
||||
if targetPort != 53 {
|
||||
return false
|
||||
}
|
||||
|
||||
return dnsIP.Equal(net.IPv4zero) || dnsIP.Equal(targetIp)
|
||||
}
|
||||
|
||||
func hijackUDPDns(conn golwip.UDPConn, pkt []byte, addr *net.UDPAddr) {
|
||||
go func() {
|
||||
defer func(conn golwip.UDPConn) {
|
||||
_ = conn.Close()
|
||||
}(conn)
|
||||
|
||||
answer, err := D.RelayDnsPacket(pkt)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, _ = conn.WriteFrom(answer, addr)
|
||||
}()
|
||||
}
|
||||
|
||||
func hijackTCPDns(conn net.Conn) {
|
||||
go func() {
|
||||
defer func(conn net.Conn) {
|
||||
_ = conn.Close()
|
||||
}(conn)
|
||||
|
||||
if err := conn.SetDeadline(time.Now().Add(defaultDnsReadTimeout)); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for {
|
||||
var length uint16
|
||||
if binary.Read(conn, binary.BigEndian, &length) != nil {
|
||||
return
|
||||
}
|
||||
|
||||
data := make([]byte, length)
|
||||
|
||||
_, err := io.ReadFull(conn, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
rb, err := D.RelayDnsPacket(data)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if binary.Write(conn, binary.BigEndian, uint16(len(rb))) != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if _, err = conn.Write(rb); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
type dnsHandler struct{}
|
||||
|
||||
func newDnsHandler() golwip.DnsHandler {
|
||||
return &dnsHandler{}
|
||||
}
|
||||
|
||||
func (d dnsHandler) ResolveIP(host string) (net.IP, error) {
|
||||
log.Debugln("[TUN] lwip resolve ip for host: %s", host)
|
||||
return resolver.ResolveIP(host)
|
||||
}
|
61
listener/tun/ipstack/lwip/tcp.go
Normal file
61
listener/tun/ipstack/lwip/tcp.go
Normal file
@ -0,0 +1,61 @@
|
||||
package lwip
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strconv"
|
||||
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
"github.com/Dreamacro/clash/context"
|
||||
"github.com/Dreamacro/clash/log"
|
||||
"github.com/yaling888/go-lwip"
|
||||
)
|
||||
|
||||
type tcpHandler struct {
|
||||
dnsIP net.IP
|
||||
tcpIn chan<- C.ConnContext
|
||||
}
|
||||
|
||||
func newTCPHandler(dnsIP net.IP, tcpIn chan<- C.ConnContext) golwip.TCPConnHandler {
|
||||
return &tcpHandler{dnsIP, tcpIn}
|
||||
}
|
||||
|
||||
func (h *tcpHandler) Handle(conn net.Conn, target *net.TCPAddr) error {
|
||||
if shouldHijackDns(h.dnsIP, target.IP, target.Port) {
|
||||
hijackTCPDns(conn)
|
||||
log.Debugln("[TUN] hijack dns tcp: %s:%d", target.IP.String(), target.Port)
|
||||
return nil
|
||||
}
|
||||
|
||||
if conn.RemoteAddr() == nil {
|
||||
_ = conn.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
src, _ := conn.LocalAddr().(*net.TCPAddr)
|
||||
dst, _ := conn.RemoteAddr().(*net.TCPAddr)
|
||||
|
||||
addrType := C.AtypIPv4
|
||||
if dst.IP.To4() == nil {
|
||||
addrType = C.AtypIPv6
|
||||
}
|
||||
|
||||
metadata := &C.Metadata{
|
||||
NetWork: C.TCP,
|
||||
Type: C.TUN,
|
||||
SrcIP: src.IP,
|
||||
DstIP: dst.IP,
|
||||
SrcPort: strconv.Itoa(src.Port),
|
||||
DstPort: strconv.Itoa(dst.Port),
|
||||
AddrType: addrType,
|
||||
Host: "",
|
||||
}
|
||||
|
||||
go func(conn net.Conn, metadata *C.Metadata) {
|
||||
//if c, ok := conn.(*net.TCPConn); ok {
|
||||
// c.SetKeepAlive(true)
|
||||
//}
|
||||
h.tcpIn <- context.NewConnContext(conn, metadata)
|
||||
}(conn, metadata)
|
||||
|
||||
return nil
|
||||
}
|
121
listener/tun/ipstack/lwip/tun.go
Normal file
121
listener/tun/ipstack/lwip/tun.go
Normal file
@ -0,0 +1,121 @@
|
||||
package lwip
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
"github.com/Dreamacro/clash/adapter/inbound"
|
||||
"github.com/Dreamacro/clash/common/pool"
|
||||
"github.com/Dreamacro/clash/config"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
"github.com/Dreamacro/clash/listener/tun/dev"
|
||||
"github.com/Dreamacro/clash/listener/tun/ipstack"
|
||||
"github.com/Dreamacro/clash/log"
|
||||
"github.com/yaling888/go-lwip"
|
||||
)
|
||||
|
||||
type lwipAdapter struct {
|
||||
device dev.TunDevice
|
||||
lwipStack golwip.LWIPStack
|
||||
lock sync.Mutex
|
||||
mtu int
|
||||
stackName string
|
||||
dnsListen string
|
||||
autoRoute bool
|
||||
}
|
||||
|
||||
func NewAdapter(device dev.TunDevice, conf config.Tun, mtu int, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (ipstack.TunAdapter, error) {
|
||||
adapter := &lwipAdapter{
|
||||
device: device,
|
||||
mtu: mtu,
|
||||
stackName: conf.Stack,
|
||||
dnsListen: conf.DNSListen,
|
||||
autoRoute: conf.AutoRoute,
|
||||
}
|
||||
|
||||
adapter.lock.Lock()
|
||||
defer adapter.lock.Unlock()
|
||||
|
||||
dnsHost, _, err := net.SplitHostPort(conf.DNSListen)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dnsIP := net.ParseIP(dnsHost)
|
||||
|
||||
// Register output function, write packets from lwip stack to tun device
|
||||
golwip.RegisterOutputFn(func(data []byte) (int, error) {
|
||||
return device.Write(data)
|
||||
})
|
||||
|
||||
// Set custom buffer pool
|
||||
golwip.SetPoolAllocator(newLWIPPool())
|
||||
|
||||
// Setup TCP/IP stack.
|
||||
lwipStack, err := golwip.NewLWIPStack(mtu)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
adapter.lwipStack = lwipStack
|
||||
|
||||
golwip.RegisterDnsHandler(newDnsHandler())
|
||||
golwip.RegisterTCPConnHandler(newTCPHandler(dnsIP, tcpIn))
|
||||
golwip.RegisterUDPConnHandler(newUDPHandler(dnsIP, udpIn))
|
||||
|
||||
// Copy packets from tun device to lwip stack, it's the loop.
|
||||
go func(lwipStack golwip.LWIPStack, device dev.TunDevice, mtu int) {
|
||||
_, err := io.CopyBuffer(lwipStack.(io.Writer), device, make([]byte, mtu))
|
||||
if err != nil {
|
||||
log.Debugln("copying data failed: %v", err)
|
||||
}
|
||||
}(lwipStack, device, mtu)
|
||||
|
||||
return adapter, nil
|
||||
}
|
||||
|
||||
func (l *lwipAdapter) Stack() string {
|
||||
return l.stackName
|
||||
}
|
||||
|
||||
func (l *lwipAdapter) AutoRoute() bool {
|
||||
return l.autoRoute
|
||||
}
|
||||
|
||||
func (l *lwipAdapter) DNSListen() string {
|
||||
return l.dnsListen
|
||||
}
|
||||
|
||||
func (l *lwipAdapter) Close() {
|
||||
l.lock.Lock()
|
||||
defer l.lock.Unlock()
|
||||
|
||||
l.stopLocked()
|
||||
}
|
||||
|
||||
func (l *lwipAdapter) stopLocked() {
|
||||
if l.lwipStack != nil {
|
||||
_ = l.lwipStack.Close()
|
||||
}
|
||||
|
||||
if l.device != nil {
|
||||
_ = l.device.Close()
|
||||
}
|
||||
|
||||
l.lwipStack = nil
|
||||
l.device = nil
|
||||
}
|
||||
|
||||
type lwipPool struct{}
|
||||
|
||||
func (p lwipPool) Get(size int) []byte {
|
||||
return pool.Get(size)
|
||||
}
|
||||
|
||||
func (p lwipPool) Put(buf []byte) error {
|
||||
return pool.Put(buf)
|
||||
}
|
||||
|
||||
func newLWIPPool() golwip.LWIPPool {
|
||||
return &lwipPool{}
|
||||
}
|
74
listener/tun/ipstack/lwip/udp.go
Normal file
74
listener/tun/ipstack/lwip/udp.go
Normal file
@ -0,0 +1,74 @@
|
||||
package lwip
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
|
||||
"github.com/Dreamacro/clash/adapter/inbound"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
"github.com/Dreamacro/clash/log"
|
||||
"github.com/Dreamacro/clash/transport/socks5"
|
||||
"github.com/yaling888/go-lwip"
|
||||
)
|
||||
|
||||
type udpPacket struct {
|
||||
source *net.UDPAddr
|
||||
payload []byte
|
||||
sender golwip.UDPConn
|
||||
}
|
||||
|
||||
func (u *udpPacket) Data() []byte {
|
||||
return u.payload
|
||||
}
|
||||
|
||||
func (u *udpPacket) WriteBack(b []byte, addr net.Addr) (n int, err error) {
|
||||
_, ok := addr.(*net.UDPAddr)
|
||||
if !ok {
|
||||
return 0, io.ErrClosedPipe
|
||||
}
|
||||
|
||||
return u.sender.WriteFrom(b, u.source)
|
||||
}
|
||||
|
||||
func (u *udpPacket) Drop() {
|
||||
}
|
||||
|
||||
func (u *udpPacket) LocalAddr() net.Addr {
|
||||
return u.source
|
||||
}
|
||||
|
||||
type udpHandler struct {
|
||||
dnsIP net.IP
|
||||
udpIn chan<- *inbound.PacketAdapter
|
||||
}
|
||||
|
||||
func newUDPHandler(dnsIP net.IP, udpIn chan<- *inbound.PacketAdapter) golwip.UDPConnHandler {
|
||||
return &udpHandler{dnsIP, udpIn}
|
||||
}
|
||||
|
||||
func (h *udpHandler) Connect(golwip.UDPConn, *net.UDPAddr) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *udpHandler) ReceiveTo(conn golwip.UDPConn, data []byte, addr *net.UDPAddr) error {
|
||||
if shouldHijackDns(h.dnsIP, addr.IP, addr.Port) {
|
||||
hijackUDPDns(conn, data, addr)
|
||||
log.Debugln("[TUN] hijack dns udp: %s:%d", addr.IP.String(), addr.Port)
|
||||
return nil
|
||||
}
|
||||
|
||||
packet := &udpPacket{
|
||||
source: conn.LocalAddr(),
|
||||
payload: data,
|
||||
sender: conn,
|
||||
}
|
||||
|
||||
go func(addr *net.UDPAddr, packet *udpPacket) {
|
||||
select {
|
||||
case h.udpIn <- inbound.NewPacket(socks5.ParseAddrToSocksAddr(addr), packet, C.TUN):
|
||||
default:
|
||||
}
|
||||
}(addr, packet)
|
||||
|
||||
return nil
|
||||
}
|
Reference in New Issue
Block a user