@@ -13,10 +13,12 @@ import (
1313 "fmt"
1414 "io"
1515 "log"
16+ "maps"
1617 "math/rand"
1718 "net"
1819 "net/http"
1920 "os"
21+ "os/exec"
2022 "os/signal"
2123 "runtime"
2224 "slices"
@@ -65,6 +67,7 @@ type Config struct {
6567 ServerConfPath string
6668 TProxy string
6769 TProxyOnly string
70+ TProxyUDP string
6871 TProxyMode string
6972 Auto bool
7073 Mark uint
@@ -135,6 +138,7 @@ type proxyapp struct {
135138 httpServerAddr string
136139 iface * net.Interface
137140 tproxyAddr string
141+ tproxyAddrUDP string
138142 tproxyMode string
139143 auto bool
140144 mark uint
@@ -249,10 +253,11 @@ func New(conf *Config) *proxyapp {
249253 p .logger .Fatal ().Msg ("Cannot specify TPRoxy and TProxyOnly at the same time" )
250254 } else if runtime .GOOS == "linux" && conf .TProxyMode != "" && ! slices .Contains (SupportedTProxyModes , conf .TProxyMode ) {
251255 p .logger .Fatal ().Msg ("Incorrect TProxyMode provided" )
252- } else if runtime .GOOS != "linux" && (conf .TProxy != "" || conf .TProxyOnly != "" || conf .TProxyMode != "" ) {
256+ } else if runtime .GOOS != "linux" && (conf .TProxy != "" || conf .TProxyOnly != "" || conf .TProxyMode != "" || conf . TProxyUDP != "" ) {
253257 conf .TProxy = ""
254258 conf .TProxyOnly = ""
255259 conf .TProxyMode = ""
260+ conf .TProxyUDP = ""
256261 p .logger .Warn ().Msgf ("[%s] functionality only available on linux systems" , conf .TProxyMode )
257262 }
258263 p .tproxyMode = conf .TProxyMode
@@ -268,6 +273,15 @@ func New(conf *Config) *proxyapp {
268273 if err != nil {
269274 p .logger .Fatal ().Err (err ).Msg ("" )
270275 }
276+ if conf .TProxyUDP != "" {
277+ if p .tproxyMode != "tproxy" {
278+ p .logger .Warn ().Msgf ("[%s] transparent UDP server only supports tproxy mode" , conf .TProxyMode )
279+ }
280+ p .tproxyAddrUDP , err = getFullAddress (conf .TProxyUDP , "" , true )
281+ if err != nil {
282+ p .logger .Fatal ().Err (err ).Msg ("" )
283+ }
284+ }
271285 } else {
272286 p .tproxyAddr , err = getFullAddress (tAddr , "" , false )
273287 if err != nil {
@@ -484,6 +498,9 @@ func New(conf *Config) *proxyapp {
484498 p .logger .Info ().Msgf ("REDIRECT: %s" , p .tproxyAddr )
485499 }
486500 }
501+ if p .tproxyAddrUDP != "" {
502+ p .logger .Info ().Msgf ("TPROXY (UDP): %s" , p .tproxyAddrUDP )
503+ }
487504 return & p
488505}
489506
@@ -496,11 +513,21 @@ func (p *proxyapp) Run() {
496513 go p .arpspoofer .Start ()
497514 }
498515 var tproxyServer * tproxyServer
499- var output map [string ]string
516+ opts := make (map [string ]string , 5 )
517+ if p .auto {
518+ p .applyCommonRedirectRules (opts )
519+ }
500520 if p .tproxyAddr != "" {
501521 tproxyServer = newTproxyServer (p )
502522 if p .auto {
503- output = tproxyServer .applyRedirectRules ()
523+ tproxyServer .applyRedirectRules (opts )
524+ }
525+ }
526+ var tproxyServerUDP * tproxyServerUDP
527+ if p .tproxyAddrUDP != "" {
528+ tproxyServerUDP = newTproxyServerUDP (p )
529+ if p .auto {
530+ tproxyServerUDP .applyRedirectRules (opts )
504531 }
505532 }
506533 if p .proxylist != nil {
@@ -525,15 +552,31 @@ func (p *proxyapp) Run() {
525552 }
526553 close (p .closeConn )
527554 if tproxyServer != nil {
555+ p .logger .Info ().Msgf ("[tcp %s] Server is shutting down..." , p .tproxyMode )
528556 if p .auto {
529- err := tproxyServer .clearRedirectRules (output )
557+ err := tproxyServer .clearRedirectRules ()
530558 if err != nil {
531559 p .logger .Error ().Err (err ).Msg ("Failed clearing iptables rules" )
532560 }
533561 }
534- p .logger .Info ().Msgf ("[%s] Server is shutting down..." , p .tproxyMode )
535562 tproxyServer .Shutdown ()
536563 }
564+ if tproxyServerUDP != nil {
565+ p .logger .Info ().Msgf ("[udp %s] Server is shutting down..." , p .tproxyMode )
566+ if p .auto {
567+ err := tproxyServerUDP .clearRedirectRules ()
568+ if err != nil {
569+ p .logger .Error ().Err (err ).Msg ("Failed clearing iptables rules" )
570+ }
571+ }
572+ tproxyServerUDP .Shutdown ()
573+ }
574+ if p .auto {
575+ err := p .clearCommonRedirectRules (opts )
576+ if err != nil {
577+ p .logger .Error ().Err (err ).Msg ("Failed clearing iptables rules" )
578+ }
579+ }
537580 p .logger .Info ().Msg ("Server is shutting down..." )
538581 ctx , cancel := context .WithTimeout (context .Background (), shutdownTimeout )
539582
@@ -547,6 +590,9 @@ func (p *proxyapp) Run() {
547590 if tproxyServer != nil {
548591 go tproxyServer .ListenAndServe ()
549592 }
593+ if tproxyServerUDP != nil {
594+ go tproxyServerUDP .ListenAndServe ()
595+ }
550596 if p .user != "" && p .pass != "" {
551597 p .httpServer .Handler = p .proxyAuth (p .handler ())
552598 } else {
@@ -571,18 +617,43 @@ func (p *proxyapp) Run() {
571617 p .logger .Error ().Err (err ).Msg ("Failed stopping arp spoofer" )
572618 }
573619 }
620+ close (p .closeConn )
621+ if tproxyServer != nil {
622+ p .logger .Info ().Msgf ("[tcp %s] Server is shutting down..." , p .tproxyMode )
623+ if p .auto {
624+ err := tproxyServer .clearRedirectRules ()
625+ if err != nil {
626+ p .logger .Error ().Err (err ).Msg ("Failed clearing iptables rules" )
627+ }
628+ }
629+ tproxyServer .Shutdown ()
630+ }
631+ if tproxyServerUDP != nil {
632+ p .logger .Info ().Msgf ("[udp %s] Server is shutting down..." , p .tproxyMode )
633+ if p .auto {
634+ err := tproxyServerUDP .clearRedirectRules ()
635+ if err != nil {
636+ p .logger .Error ().Err (err ).Msg ("Failed clearing iptables rules" )
637+ }
638+ }
639+ tproxyServerUDP .Shutdown ()
640+ }
574641 if p .auto {
575- err := tproxyServer . clearRedirectRules ( output )
642+ err := p . clearCommonRedirectRules ( opts )
576643 if err != nil {
577644 p .logger .Error ().Err (err ).Msg ("Failed clearing iptables rules" )
578645 }
579646 }
580- close (p .closeConn )
581- p .logger .Info ().Msgf ("[%s] Server is shutting down..." , p .tproxyMode )
582- tproxyServer .Shutdown ()
583647 close (done )
584648 }()
585- tproxyServer .ListenAndServe ()
649+ if tproxyServer != nil && tproxyServerUDP != nil {
650+ go tproxyServerUDP .ListenAndServe ()
651+ tproxyServer .ListenAndServe ()
652+ } else if tproxyServer != nil {
653+ tproxyServer .ListenAndServe ()
654+ } else {
655+ tproxyServerUDP .ListenAndServe ()
656+ }
586657 }
587658 <- done
588659}
@@ -749,7 +820,9 @@ func (p *proxyapp) handleTunnel(w http.ResponseWriter, r *http.Request) {
749820 var dstConn net.Conn
750821 var err error
751822 if network .IsLocalAddress (r .Host ) {
752- dstConn , err = getBaseDialer (timeout , p .mark ).Dial ("tcp" , r .Host )
823+ ctx , cancel := context .WithTimeout (context .Background (), timeout )
824+ defer cancel ()
825+ dstConn , err = getBaseDialer (timeout , p .mark ).DialContext (ctx , "tcp" , r .Host )
753826 if err != nil {
754827 p .logger .Error ().Err (err ).Msgf ("Failed connecting to %s" , r .Host )
755828 http .Error (w , err .Error (), http .StatusServiceUnavailable )
@@ -1317,3 +1390,127 @@ func (p *proxyapp) proxyAuth(next http.HandlerFunc) http.HandlerFunc {
13171390 http .Error (w , "Proxy Authentication Required" , http .StatusProxyAuthRequired )
13181391 })
13191392}
1393+
1394+ func (p * proxyapp ) applyCommonRedirectRules (opts map [string ]string ) {
1395+ var setex string
1396+ if p .debug {
1397+ setex = "set -ex"
1398+ }
1399+ if p .tproxyMode == "tproxy" {
1400+ cmdClear := exec .Command ("bash" , "-c" , fmt .Sprintf (`
1401+ %s
1402+ iptables -t mangle -F DIVERT 2>/dev/null || true
1403+ iptables -t mangle -X DIVERT 2>/dev/null || true
1404+
1405+ ip rule del fwmark 1 lookup 100 2>/dev/null || true
1406+ ip route flush table 100 2>/dev/null || true
1407+ ` , setex ))
1408+ cmdClear .Stdout = os .Stdout
1409+ cmdClear .Stderr = os .Stderr
1410+ if err := cmdClear .Run (); err != nil {
1411+ p .logger .Fatal ().Err (err ).Msg ("Failed while configuring iptables. Are you root?" )
1412+ }
1413+ cmdInit0 := exec .Command ("bash" , "-c" , fmt .Sprintf (`
1414+ %s
1415+ ip rule add fwmark 1 lookup 100 2>/dev/null || true
1416+ ip route add local 0.0.0.0/0 dev lo table 100 2>/dev/null || true
1417+
1418+ iptables -t mangle -N DIVERT 2>/dev/null || true
1419+ iptables -t mangle -F DIVERT 2>/dev/null || true
1420+ iptables -t mangle -A DIVERT -j MARK --set-mark 1
1421+ iptables -t mangle -A DIVERT -j ACCEPT
1422+ ` , setex ))
1423+ cmdInit0 .Stdout = os .Stdout
1424+ cmdInit0 .Stderr = os .Stderr
1425+ if err := cmdInit0 .Run (); err != nil {
1426+ p .logger .Fatal ().Err (err ).Msg ("Failed while configuring iptables. Are you root?" )
1427+ }
1428+ }
1429+
1430+ _ = createSysctlOptCmd ("net.ipv4.ip_forward" , "1" , setex , opts , p .debug ).Run ()
1431+ cmdClearForward := exec .Command ("bash" , "-c" , fmt .Sprintf (`
1432+ %s
1433+ iptables -t filter -F GOHPTS 2>/dev/null || true
1434+ iptables -t filter -D FORWARD -j GOHPTS 2>/dev/null || true
1435+ iptables -t filter -X GOHPTS 2>/dev/null || true
1436+ ` , setex ))
1437+ cmdClearForward .Stdout = os .Stdout
1438+ cmdClearForward .Stderr = os .Stderr
1439+ if err := cmdClearForward .Run (); err != nil {
1440+ p .logger .Fatal ().Err (err ).Msg ("Failed while configuring iptables. Are you root?" )
1441+ }
1442+ var iface * net.Interface
1443+ var err error
1444+ if p .iface != nil {
1445+ iface = p .iface
1446+ } else {
1447+ iface , err = network .GetDefaultInterface ()
1448+ if err != nil {
1449+ p .logger .Fatal ().Err (err ).Msg ("failed getting default network interface" )
1450+ }
1451+ }
1452+ cmdForwardFilter := exec .Command ("bash" , "-c" , fmt .Sprintf (`
1453+ %s
1454+ iptables -t filter -N GOHPTS 2>/dev/null
1455+ iptables -t filter -F GOHPTS
1456+ iptables -t filter -A FORWARD -j GOHPTS
1457+ iptables -t filter -A GOHPTS -i %s -j ACCEPT
1458+ iptables -t filter -A GOHPTS -o %s -j ACCEPT
1459+ ` , setex , iface .Name , iface .Name ))
1460+ cmdForwardFilter .Stdout = os .Stdout
1461+ cmdForwardFilter .Stderr = os .Stderr
1462+ if err := cmdForwardFilter .Run (); err != nil {
1463+ p .logger .Fatal ().Err (err ).Msg ("Failed while configuring iptables. Are you root?" )
1464+ }
1465+ }
1466+
1467+ func (p * proxyapp ) clearCommonRedirectRules (opts map [string ]string ) error {
1468+ var setex string
1469+ if p .debug {
1470+ setex = "set -ex"
1471+ }
1472+ cmdClear := exec .Command ("bash" , "-c" , fmt .Sprintf (`
1473+ %s
1474+ iptables -t filter -F GOHPTS 2>/dev/null || true
1475+ iptables -t filter -D FORWARD -j GOHPTS 2>/dev/null || true
1476+ iptables -t filter -X GOHPTS 2>/dev/null || true
1477+ ` , setex ))
1478+ cmdClear .Stdout = os .Stdout
1479+ cmdClear .Stderr = os .Stderr
1480+ if err := cmdClear .Run (); err != nil {
1481+ p .logger .Fatal ().Err (err ).Msg ("Failed while configuring iptables. Are you root?" )
1482+ }
1483+ cmds := make ([]string , 0 , len (opts ))
1484+ for _ , cmd := range slices .Sorted (maps .Keys (opts )) {
1485+ cmds = append (cmds , fmt .Sprintf ("sysctl -w %s=%s" , cmd , opts [cmd ]))
1486+ }
1487+ cmdRestoreOpts := exec .Command ("bash" , "-c" , fmt .Sprintf (`
1488+ %s
1489+ %s
1490+ ` , setex , strings .Join (cmds , "\n " )))
1491+ cmdRestoreOpts .Stdout = os .Stdout
1492+ cmdRestoreOpts .Stderr = os .Stderr
1493+ if ! p .debug {
1494+ cmdRestoreOpts .Stdout = nil
1495+ }
1496+ _ = cmdRestoreOpts .Run ()
1497+ if p .tproxyMode == "tproxy" {
1498+ cmd := exec .Command ("bash" , "-c" , fmt .Sprintf (`
1499+ %s
1500+ iptables -t mangle -F DIVERT 2>/dev/null || true
1501+ iptables -t mangle -X DIVERT 2>/dev/null || true
1502+
1503+ ip rule del fwmark 1 lookup 100 2>/dev/null || true
1504+ ip route flush table 100 2>/dev/null || true
1505+ ` , setex ))
1506+ cmd .Stdout = os .Stdout
1507+ cmd .Stderr = os .Stderr
1508+ if ! p .debug {
1509+ cmd .Stdout = nil
1510+ }
1511+ if err := cmd .Run (); err != nil {
1512+ return err
1513+ }
1514+ }
1515+ return nil
1516+ }
0 commit comments