mirror of
https://github.com/p4gefau1t/trojan-go.git
synced 2024-04-21 12:21:34 +00:00
182 lines
4.0 KiB
Go
182 lines
4.0 KiB
Go
// +build linux
|
|
|
|
package tproxy
|
|
|
|
import (
|
|
"context"
|
|
"net"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/LiamHaworth/go-tproxy"
|
|
"github.com/p4gefau1t/trojan-go/common"
|
|
"github.com/p4gefau1t/trojan-go/conf"
|
|
"github.com/p4gefau1t/trojan-go/log"
|
|
"github.com/p4gefau1t/trojan-go/protocol"
|
|
)
|
|
|
|
type TProxyInboundConnSession struct {
|
|
protocol.ConnSession
|
|
reqeust *protocol.Request
|
|
conn net.Conn
|
|
}
|
|
|
|
func (i *TProxyInboundConnSession) Read(p []byte) (int, error) {
|
|
return i.conn.Read(p)
|
|
}
|
|
|
|
func (i *TProxyInboundConnSession) Write(p []byte) (int, error) {
|
|
return i.conn.Write(p)
|
|
}
|
|
|
|
func (i *TProxyInboundConnSession) Close() error {
|
|
return i.conn.Close()
|
|
}
|
|
|
|
func (i *TProxyInboundConnSession) GetRequest() *protocol.Request {
|
|
return i.reqeust
|
|
}
|
|
|
|
func (i *TProxyInboundConnSession) parseRequest() error {
|
|
addr, err := getOriginalTCPDest(i.conn.(*net.TCPConn))
|
|
if err != nil {
|
|
return common.NewError("failed to get original dst").Base(err)
|
|
}
|
|
req := &protocol.Request{
|
|
Address: &common.Address{
|
|
IP: addr.IP,
|
|
Port: addr.Port,
|
|
},
|
|
Command: protocol.Connect,
|
|
}
|
|
if addr.IP.To4() != nil {
|
|
req.AddressType = common.IPv4
|
|
} else {
|
|
req.AddressType = common.IPv6
|
|
}
|
|
i.reqeust = req
|
|
return nil
|
|
}
|
|
|
|
func NewInboundConnSession(conn net.Conn) (protocol.ConnSession, *protocol.Request, error) {
|
|
i := &TProxyInboundConnSession{
|
|
conn: conn,
|
|
}
|
|
if err := i.parseRequest(); err != nil {
|
|
return nil, nil, common.NewError("failed to parse request").Base(err)
|
|
}
|
|
return i, i.reqeust, nil
|
|
}
|
|
|
|
type udpSession struct {
|
|
src *net.UDPAddr
|
|
dst *net.UDPAddr
|
|
expire time.Time
|
|
}
|
|
|
|
type NATInboundPacketSession struct {
|
|
protocol.PacketSession
|
|
request *protocol.Request
|
|
conn *net.UDPConn
|
|
tableMutex sync.Mutex
|
|
sessionTable map[string]*udpSession
|
|
ctx context.Context
|
|
cancel context.CancelFunc
|
|
}
|
|
|
|
func (i *NATInboundPacketSession) cleanExpiredSession() {
|
|
for {
|
|
i.tableMutex.Lock()
|
|
now := time.Now()
|
|
for k, v := range i.sessionTable {
|
|
if now.After(v.expire) {
|
|
delete(i.sessionTable, k)
|
|
}
|
|
}
|
|
i.tableMutex.Unlock()
|
|
select {
|
|
case <-time.After(protocol.UDPTimeout):
|
|
case <-i.ctx.Done():
|
|
i.conn.Close()
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
func (i *NATInboundPacketSession) WritePacket(req *protocol.Request, packet []byte) (int, error) {
|
|
i.tableMutex.Lock()
|
|
defer i.tableMutex.Unlock()
|
|
session, found := i.sessionTable[req.String()]
|
|
if !found {
|
|
return 0, common.NewError("session not found " + req.String())
|
|
}
|
|
conn, err := tproxy.DialUDP("udp", session.dst, session.src)
|
|
if err != nil {
|
|
return 0, common.NewError("cannot dial to source").Base(err)
|
|
}
|
|
defer conn.Close()
|
|
return conn.Write(packet)
|
|
}
|
|
|
|
func (i *NATInboundPacketSession) ReadPacket() (*protocol.Request, []byte, error) {
|
|
buf := [protocol.MaxUDPPacketSize]byte{}
|
|
n, src, dst, err := tproxy.ReadFromUDP(i.conn, buf[:])
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
i.tableMutex.Lock()
|
|
i.sessionTable[dst.String()] = &udpSession{
|
|
src: src,
|
|
dst: dst,
|
|
expire: time.Now().Add(protocol.UDPTimeout),
|
|
}
|
|
i.tableMutex.Unlock()
|
|
log.Debug("tproxy udp packet from", src, "to", dst)
|
|
req := &protocol.Request{
|
|
Address: &common.Address{
|
|
IP: dst.IP,
|
|
Port: dst.Port,
|
|
NetworkType: "udp",
|
|
},
|
|
}
|
|
if dst.IP.To4() != nil {
|
|
req.AddressType = common.IPv4
|
|
} else {
|
|
req.AddressType = common.IPv6
|
|
}
|
|
return req, buf[0:n], nil
|
|
}
|
|
|
|
func (i *NATInboundPacketSession) Close() error {
|
|
i.cancel()
|
|
return nil
|
|
}
|
|
|
|
func NewInboundPacketSession(ctx context.Context, config *conf.GlobalConfig) (protocol.PacketSession, error) {
|
|
localIP, err := config.LocalAddress.ResolveIP(false)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
addr := &net.UDPAddr{
|
|
IP: localIP,
|
|
Port: int(config.LocalAddress.Port),
|
|
}
|
|
conn, err := tproxy.ListenUDP("udp", addr)
|
|
if err != nil {
|
|
return nil, common.NewError("failed to listen udp addr").Base(err)
|
|
}
|
|
ctx, cancel := context.WithCancel(ctx)
|
|
i := &NATInboundPacketSession{
|
|
conn: conn,
|
|
sessionTable: make(map[string]*udpSession, 1024),
|
|
ctx: ctx,
|
|
cancel: cancel,
|
|
}
|
|
go i.cleanExpiredSession()
|
|
return i, nil
|
|
}
|