Style: code style
This commit is contained in:
37
listener/tproxy/packet.go
Normal file
37
listener/tproxy/packet.go
Normal file
@ -0,0 +1,37 @@
|
||||
package tproxy
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/Dreamacro/clash/common/pool"
|
||||
)
|
||||
|
||||
type packet struct {
|
||||
lAddr *net.UDPAddr
|
||||
buf []byte
|
||||
}
|
||||
|
||||
func (c *packet) Data() []byte {
|
||||
return c.buf
|
||||
}
|
||||
|
||||
// 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)
|
||||
if err != nil {
|
||||
n = 0
|
||||
return
|
||||
}
|
||||
n, err = tc.Write(b)
|
||||
tc.Close()
|
||||
return
|
||||
}
|
||||
|
||||
// LocalAddr returns the source IP/Port of UDP Packet
|
||||
func (c *packet) LocalAddr() net.Addr {
|
||||
return c.lAddr
|
||||
}
|
||||
|
||||
func (c *packet) Drop() {
|
||||
pool.Put(c.buf)
|
||||
}
|
40
listener/tproxy/setsockopt_linux.go
Normal file
40
listener/tproxy/setsockopt_linux.go
Normal file
@ -0,0 +1,40 @@
|
||||
// +build linux
|
||||
|
||||
package tproxy
|
||||
|
||||
import (
|
||||
"net"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func setsockopt(rc syscall.RawConn, addr string) error {
|
||||
isIPv6 := true
|
||||
host, _, err := net.SplitHostPort(addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ip := net.ParseIP(host)
|
||||
if ip != nil && ip.To4() != nil {
|
||||
isIPv6 = false
|
||||
}
|
||||
|
||||
rc.Control(func(fd uintptr) {
|
||||
err = syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)
|
||||
|
||||
if err == nil {
|
||||
err = syscall.SetsockoptInt(int(fd), syscall.SOL_IP, syscall.IP_TRANSPARENT, 1)
|
||||
}
|
||||
if err == nil && isIPv6 {
|
||||
err = syscall.SetsockoptInt(int(fd), syscall.SOL_IPV6, IPV6_TRANSPARENT, 1)
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
err = syscall.SetsockoptInt(int(fd), syscall.SOL_IP, syscall.IP_RECVORIGDSTADDR, 1)
|
||||
}
|
||||
if err == nil && isIPv6 {
|
||||
err = syscall.SetsockoptInt(int(fd), syscall.SOL_IPV6, IPV6_RECVORIGDSTADDR, 1)
|
||||
}
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
12
listener/tproxy/setsockopt_other.go
Normal file
12
listener/tproxy/setsockopt_other.go
Normal file
@ -0,0 +1,12 @@
|
||||
// +build !linux
|
||||
|
||||
package tproxy
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func setsockopt(rc syscall.RawConn, addr string) error {
|
||||
return errors.New("not supported on current platform")
|
||||
}
|
68
listener/tproxy/tproxy.go
Normal file
68
listener/tproxy/tproxy.go
Normal file
@ -0,0 +1,68 @@
|
||||
package tproxy
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/Dreamacro/clash/adapter/inbound"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
"github.com/Dreamacro/clash/transport/socks5"
|
||||
)
|
||||
|
||||
type Listener struct {
|
||||
net.Listener
|
||||
address string
|
||||
closed bool
|
||||
}
|
||||
|
||||
func New(addr string, in chan<- C.ConnContext) (*Listener, error) {
|
||||
l, err := net.Listen("tcp", addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tl := l.(*net.TCPListener)
|
||||
rc, err := tl.SyscallConn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = setsockopt(rc, addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rl := &Listener{
|
||||
Listener: l,
|
||||
address: addr,
|
||||
}
|
||||
|
||||
go func() {
|
||||
for {
|
||||
c, err := l.Accept()
|
||||
if err != nil {
|
||||
if rl.closed {
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
go rl.handleTProxy(c, in)
|
||||
}
|
||||
}()
|
||||
|
||||
return rl, nil
|
||||
}
|
||||
|
||||
func (l *Listener) Close() {
|
||||
l.closed = true
|
||||
l.Listener.Close()
|
||||
}
|
||||
|
||||
func (l *Listener) Address() string {
|
||||
return l.address
|
||||
}
|
||||
|
||||
func (l *Listener) handleTProxy(conn net.Conn, in chan<- C.ConnContext) {
|
||||
target := socks5.ParseAddrToSocksAddr(conn.LocalAddr())
|
||||
conn.(*net.TCPConn).SetKeepAlive(true)
|
||||
in <- inbound.NewSocket(target, conn, C.TPROXY)
|
||||
}
|
81
listener/tproxy/udp.go
Normal file
81
listener/tproxy/udp.go
Normal file
@ -0,0 +1,81 @@
|
||||
package tproxy
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/Dreamacro/clash/adapter/inbound"
|
||||
"github.com/Dreamacro/clash/common/pool"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
"github.com/Dreamacro/clash/transport/socks5"
|
||||
)
|
||||
|
||||
type UDPListener struct {
|
||||
net.PacketConn
|
||||
address string
|
||||
closed bool
|
||||
}
|
||||
|
||||
func NewUDP(addr string, in chan<- *inbound.PacketAdapter) (*UDPListener, error) {
|
||||
l, err := net.ListenPacket("udp", addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rl := &UDPListener{l, addr, false}
|
||||
|
||||
c := l.(*net.UDPConn)
|
||||
|
||||
rc, err := c.SyscallConn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = setsockopt(rc, addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
go func() {
|
||||
oob := make([]byte, 1024)
|
||||
for {
|
||||
buf := pool.Get(pool.RelayBufferSize)
|
||||
n, oobn, _, lAddr, err := c.ReadMsgUDP(buf, oob)
|
||||
if err != nil {
|
||||
pool.Put(buf)
|
||||
if rl.closed {
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
rAddr, err := getOrigDst(oob, oobn)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
handlePacketConn(l, in, buf[:n], lAddr, rAddr)
|
||||
}
|
||||
}()
|
||||
|
||||
return rl, nil
|
||||
}
|
||||
|
||||
func (l *UDPListener) Close() error {
|
||||
l.closed = true
|
||||
return l.PacketConn.Close()
|
||||
}
|
||||
|
||||
func (l *UDPListener) Address() string {
|
||||
return l.address
|
||||
}
|
||||
|
||||
func handlePacketConn(pc net.PacketConn, in chan<- *inbound.PacketAdapter, buf []byte, lAddr *net.UDPAddr, rAddr *net.UDPAddr) {
|
||||
target := socks5.ParseAddrToSocksAddr(rAddr)
|
||||
pkt := &packet{
|
||||
lAddr: lAddr,
|
||||
buf: buf,
|
||||
}
|
||||
select {
|
||||
case in <- inbound.NewPacket(target, pkt, C.TPROXY):
|
||||
default:
|
||||
}
|
||||
}
|
124
listener/tproxy/udp_linux.go
Normal file
124
listener/tproxy/udp_linux.go
Normal file
@ -0,0 +1,124 @@
|
||||
// +build linux
|
||||
|
||||
package tproxy
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
const (
|
||||
IPV6_TRANSPARENT = 0x4b
|
||||
IPV6_RECVORIGDSTADDR = 0x4a
|
||||
)
|
||||
|
||||
// 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) {
|
||||
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), fmt.Sprintf("net-udp-dial-%s", rAddr.String()))
|
||||
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
|
||||
}
|
||||
|
||||
func getOrigDst(oob []byte, oobn int) (*net.UDPAddr, error) {
|
||||
msgs, err := syscall.ParseSocketControlMessage(oob[:oobn])
|
||||
if err != nil {
|
||||
return nil, 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
|
||||
}
|
||||
}
|
||||
|
||||
return nil, errors.New("cannot find origDst")
|
||||
}
|
16
listener/tproxy/udp_other.go
Normal file
16
listener/tproxy/udp_other.go
Normal file
@ -0,0 +1,16 @@
|
||||
// +build !linux
|
||||
|
||||
package tproxy
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
)
|
||||
|
||||
func getOrigDst(oob []byte, oobn int) (*net.UDPAddr, error) {
|
||||
return nil, errors.New("UDP redir not supported on current platform")
|
||||
}
|
||||
|
||||
func dialUDP(network string, lAddr *net.UDPAddr, rAddr *net.UDPAddr) (*net.UDPConn, error) {
|
||||
return nil, errors.New("UDP redir not supported on current platform")
|
||||
}
|
Reference in New Issue
Block a user