2021-07-17 22:36:42 +08:00
|
|
|
// ref: https://www.kernel.org/doc/Documentation/networking/tproxy.txt
|
|
|
|
// @LiamHaworth: https://github.com/LiamHaworth/go-tproxy/blob/master/tproxy_udp.go
|
|
|
|
|
|
|
|
package tproxy
|
|
|
|
|
|
|
|
import (
|
|
|
|
"net"
|
|
|
|
"net/url"
|
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
tp "github.com/LiamHaworth/go-tproxy"
|
|
|
|
|
|
|
|
"github.com/nadoo/glider/log"
|
|
|
|
"github.com/nadoo/glider/proxy"
|
|
|
|
)
|
|
|
|
|
|
|
|
// TProxy struct.
|
|
|
|
type TProxy struct {
|
|
|
|
proxy proxy.Proxy
|
|
|
|
addr string
|
|
|
|
}
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
proxy.RegisterServer("tproxy", NewTProxyServer)
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewTProxy returns a tproxy.
|
|
|
|
func NewTProxy(s string, p proxy.Proxy) (*TProxy, error) {
|
|
|
|
u, err := url.Parse(s)
|
|
|
|
if err != nil {
|
|
|
|
log.F("[tproxy] parse err: %s", err)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
addr := u.Host
|
|
|
|
|
|
|
|
tp := &TProxy{
|
|
|
|
proxy: p,
|
|
|
|
addr: addr,
|
|
|
|
}
|
|
|
|
|
|
|
|
return tp, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewTProxyServer returns a udp tunnel server.
|
|
|
|
func NewTProxyServer(s string, p proxy.Proxy) (proxy.Server, error) {
|
|
|
|
return NewTProxy(s, p)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ListenAndServe listens on server's addr and serves connections.
|
|
|
|
func (s *TProxy) ListenAndServe() {
|
|
|
|
// go s.ListenAndServeTCP()
|
|
|
|
s.ListenAndServeUDP()
|
|
|
|
}
|
|
|
|
|
|
|
|
// ListenAndServeTCP .
|
|
|
|
func (s *TProxy) ListenAndServeTCP() {
|
|
|
|
log.F("[tproxy] tcp mode not supported now, please use 'redir' instead")
|
|
|
|
}
|
|
|
|
|
|
|
|
// ListenAndServeUDP .
|
|
|
|
func (s *TProxy) ListenAndServeUDP() {
|
|
|
|
laddr, err := net.ResolveUDPAddr("udp", s.addr)
|
|
|
|
if err != nil {
|
|
|
|
log.F("[tproxyu] failed to resolve addr %s: %v", s.addr, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
lc, err := tp.ListenUDP("udp", laddr)
|
|
|
|
if err != nil {
|
|
|
|
log.F("[tproxyu] failed to listen on %s: %v", s.addr, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-07-17 23:43:22 +08:00
|
|
|
log.F("[tproxyu] listening UDP on %s", s.addr)
|
|
|
|
|
2021-07-17 22:36:42 +08:00
|
|
|
var nm sync.Map
|
|
|
|
buf := make([]byte, proxy.UDPBufSize)
|
|
|
|
|
|
|
|
for {
|
|
|
|
n, lraddr, dstAddr, err := tp.ReadFromUDP(lc, buf)
|
|
|
|
if err != nil {
|
|
|
|
log.F("[tproxyu] read error: %v", err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
var session *natEntry
|
|
|
|
v, ok := nm.Load(lraddr.String())
|
|
|
|
|
|
|
|
if !ok && v == nil {
|
|
|
|
pc, dialer, writeTo, err := s.proxy.DialUDP("udp", dstAddr.String())
|
|
|
|
if err != nil {
|
|
|
|
log.F("[tproxyu] dial to %s error: %v", dstAddr, err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
lpc, err := tp.DialUDP("udp", dstAddr, lraddr)
|
|
|
|
if err != nil {
|
|
|
|
log.F("[tproxyu] dial to %s as %s error: %v", lraddr, dstAddr, err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
session = newNatEntry(pc, writeTo)
|
|
|
|
nm.Store(lraddr.String(), session)
|
|
|
|
|
|
|
|
go func(lc net.PacketConn, pc net.PacketConn, lraddr *net.UDPAddr) {
|
|
|
|
proxy.RelayUDP(lc, lraddr, pc, 2*time.Minute)
|
|
|
|
pc.Close()
|
|
|
|
nm.Delete(lraddr.String())
|
|
|
|
}(lpc, pc, lraddr)
|
|
|
|
|
|
|
|
log.F("[tproxyu] %s <-> %s via %s", lraddr, dstAddr, dialer.Addr())
|
|
|
|
|
|
|
|
} else {
|
|
|
|
session = v.(*natEntry)
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = session.WriteTo(buf[:n], session.writeTo)
|
|
|
|
if err != nil {
|
|
|
|
log.F("[tproxyu] writeTo %s error: %v", session.writeTo, err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Serve .
|
|
|
|
func (s *TProxy) Serve(c net.Conn) {
|
|
|
|
log.F("[tproxy] func Serve: can not be called directly")
|
|
|
|
}
|
|
|
|
|
|
|
|
type natEntry struct {
|
|
|
|
net.PacketConn
|
|
|
|
writeTo net.Addr
|
|
|
|
}
|
|
|
|
|
|
|
|
func newNatEntry(pc net.PacketConn, writeTo net.Addr) *natEntry {
|
|
|
|
return &natEntry{PacketConn: pc, writeTo: writeTo}
|
|
|
|
}
|