@@ -39,12 +39,14 @@ const (
3939 hopTimeout time.Duration = 3 * time .Second
4040 flushTimeout time.Duration = 10 * time .Millisecond
4141 availProxyUpdateInterval time.Duration = 30 * time .Second
42- shutdownTimeout time.Duration = 5 * time .Second
4342 kbSize int64 = 1000
4443 rrIndexMax uint32 = 1_000_000
4544)
4645
47- var supportedChainTypes = []string {"strict" , "dynamic" , "random" , "round_robin" }
46+ var (
47+ supportedChainTypes = []string {"strict" , "dynamic" , "random" , "round_robin" }
48+ SupportedTProxyModes = []string {"redirect" , "tproxy" }
49+ )
4850
4951// Hop-by-hop headers
5052// https://datatracker.ietf.org/doc/html/rfc2616#section-13.5.1
@@ -116,6 +118,7 @@ type proxyapp struct {
116118 keyFile string
117119 httpServerAddr string
118120 tproxyAddr string
121+ tproxyMode string
119122 user string
120123 pass string
121124 proxychain chain
@@ -135,10 +138,11 @@ func (p *proxyapp) printProxyChain(pc []proxyEntry) string {
135138 if p .tproxyAddr != "" {
136139 sb .WriteString (" | " )
137140 sb .WriteString (p .tproxyAddr )
138- sb .WriteString (" (tproxy)" )
141+ sb .WriteString (fmt . Sprintf ( " (%s)" , p . tproxyMode ) )
139142 }
140143 } else if p .tproxyAddr != "" {
141144 sb .WriteString (p .tproxyAddr )
145+ sb .WriteString (fmt .Sprintf (" (%s)" , p .tproxyMode ))
142146 }
143147 sb .WriteString (" -> " )
144148 for _ , pe := range pc {
@@ -233,6 +237,10 @@ func (p *proxyapp) getSocks() (proxy.Dialer, *http.Client, error) {
233237 p .mu .RLock ()
234238 defer p .mu .RUnlock ()
235239 chainType := p .proxychain .Type
240+ if len (p .availProxyList ) == 0 {
241+ p .logger .Error ().Msgf ("[%s] No SOCKS5 Proxy available" , chainType )
242+ return nil , nil , fmt .Errorf ("no socks5 proxy available" )
243+ }
236244 var chainLength int
237245 if p .proxychain .Length > len (p .availProxyList ) || p .proxychain .Length <= 0 {
238246 chainLength = len (p .availProxyList )
@@ -586,9 +594,30 @@ func newTproxyServer(pa *proxyapp) *tproxyServer {
586594 quit : make (chan struct {}),
587595 pa : pa ,
588596 }
589- ln , err := net .Listen ("tcp" , ts .pa .tproxyAddr )
597+ // https://iximiuz.com/en/posts/go-net-http-setsockopt-example/
598+ lc := net.ListenConfig {
599+ Control : func (network , address string , conn syscall.RawConn ) error {
600+ var operr error
601+ if err := conn .Control (func (fd uintptr ) {
602+ operr = syscall .SetsockoptInt (int (fd ), unix .IPPROTO_TCP , unix .TCP_USER_TIMEOUT , int (timeout * 1000 ))
603+ operr = syscall .SetsockoptInt (int (fd ), unix .SOL_SOCKET , unix .SO_REUSEADDR , 1 )
604+ if ts .pa .tproxyMode == "tproxy" {
605+ operr = syscall .SetsockoptInt (int (fd ), unix .SOL_IP , unix .IP_TRANSPARENT , 1 )
606+ }
607+ }); err != nil {
608+ return err
609+ }
610+ return operr
611+ },
612+ }
613+
614+ ln , err := lc .Listen (context .Background (), "tcp4" , ts .pa .tproxyAddr )
590615 if err != nil {
591- ts .pa .logger .Fatal ().Err (err ).Msg ("" )
616+ var msg string
617+ if errors .Is (err , unix .EPERM ) {
618+ msg = "try `sudo setcap 'cap_net_admin+ep` for the binary:"
619+ }
620+ ts .pa .logger .Fatal ().Err (err ).Msg (msg )
592621 }
593622 ts .listener = ln
594623 return ts
@@ -613,6 +642,10 @@ func (ts *tproxyServer) serve() {
613642 }
614643 } else {
615644 ts .wg .Add (1 )
645+ err := conn .SetDeadline (time .Now ().Add (timeout ))
646+ if err != nil {
647+ ts .pa .logger .Error ().Err (err ).Msg ("" )
648+ }
616649 go func () {
617650 ts .handleConnection (conn )
618651 ts .wg .Done ()
@@ -644,24 +677,34 @@ func (ts *tproxyServer) getOriginalDst(rawConn syscall.RawConn) (string, error)
644677 }
645678 dstHost := netip .AddrFrom4 (originalDst .Addr )
646679 dstPort := uint16 (originalDst .Port << 8 ) | originalDst .Port >> 8
647- ts .pa .logger .Debug ().Msgf ("[tproxy] getsockopt SO_ORIGINAL_DST %s:%d" , dstHost , dstPort )
648680 return fmt .Sprintf ("%s:%d" , dstHost , dstPort ), nil
649681}
650682
651683func (ts * tproxyServer ) handleConnection (srcConn net.Conn ) {
652- var dstConn net.Conn
684+ var (
685+ dstConn net.Conn
686+ dst string
687+ err error
688+ )
653689 defer srcConn .Close ()
654-
655- rawConn , err := srcConn .(* net.TCPConn ).SyscallConn ()
656- if err != nil {
657- ts .pa .logger .Error ().Err (err ).Msg ("[tproxy] Failed to get raw connection" )
658- return
659- }
660-
661- dst , err := ts .getOriginalDst (rawConn )
662- if err != nil {
663- ts .pa .logger .Error ().Err (err ).Msg ("[tproxy] Failed to get destination address" )
664- return
690+ switch ts .pa .tproxyMode {
691+ case "redirect" :
692+ rawConn , err := srcConn .(* net.TCPConn ).SyscallConn ()
693+ if err != nil {
694+ ts .pa .logger .Error ().Err (err ).Msg ("[tproxy] Failed to get raw connection" )
695+ return
696+ }
697+ dst , err = ts .getOriginalDst (rawConn )
698+ if err != nil {
699+ ts .pa .logger .Error ().Err (err ).Msg ("[tproxy] Failed to get destination address" )
700+ return
701+ }
702+ ts .pa .logger .Debug ().Msgf ("[tproxy] getsockopt SO_ORIGINAL_DST %s" , dst )
703+ case "tproxy" :
704+ dst = srcConn .LocalAddr ().String ()
705+ ts .pa .logger .Debug ().Msgf ("[tproxy] IP_TRANSPARENT %s" , dst )
706+ default :
707+ ts .pa .logger .Fatal ().Msg ("Unknown tproxyMode" )
665708 }
666709 if isLocalAddress (dst ) {
667710 dstConn , err = net .DialTimeout ("tcp" , dst , timeout )
@@ -708,7 +751,7 @@ func (ts *tproxyServer) Shutdown() {
708751 case <- done :
709752 ts .pa .logger .Info ().Msg ("[tproxy] Server gracefully shutdown" )
710753 return
711- case <- time .After (shutdownTimeout ):
754+ case <- time .After (timeout ):
712755 ts .pa .logger .Error ().Msg ("[tproxy] Server timed out waiting for connections to finish" )
713756 return
714757 }
@@ -737,7 +780,7 @@ func (p *proxyapp) Run() {
737780 tproxyServer .Shutdown ()
738781 }
739782 p .logger .Info ().Msg ("Server is shutting down..." )
740- ctx , cancel := context .WithTimeout (context .Background (), shutdownTimeout )
783+ ctx , cancel := context .WithTimeout (context .Background (), timeout )
741784
742785 defer cancel ()
743786 p .httpServer .SetKeepAlivesEnabled (false )
@@ -767,7 +810,7 @@ func (p *proxyapp) Run() {
767810 } else {
768811 go func () {
769812 <- quit
770- p .logger .Info ().Msg ("[tproxy] server is shutting down..." )
813+ p .logger .Info ().Msg ("[tproxy] Server is shutting down..." )
771814 tproxyServer .Shutdown ()
772815 close (done )
773816 }()
@@ -790,6 +833,7 @@ type Config struct {
790833 ServerConfPath string
791834 TProxy string
792835 TProxyOnly string
836+ TProxyMode string
793837}
794838type logWriter struct {
795839}
@@ -883,11 +927,15 @@ func New(conf *Config) *proxyapp {
883927 p .logger = & logger
884928 if runtime .GOOS == "linux" && conf .TProxy != "" && conf .TProxyOnly != "" {
885929 p .logger .Fatal ().Msg ("Cannot specify TPRoxy and TProxyOnly at the same time" )
930+ } else if runtime .GOOS == "linux" && conf .TProxyMode != "" && ! slices .Contains (SupportedTProxyModes , conf .TProxyMode ) {
931+ p .logger .Fatal ().Msg ("Incorrect TProxyMode provided" )
886932 } else if runtime .GOOS != "linux" {
887933 conf .TProxy = ""
888934 conf .TProxyOnly = ""
935+ conf .TProxyMode = ""
889936 p .logger .Warn ().Msg ("[tproxy] functionality only available on linux system" )
890937 }
938+ p .tproxyMode = conf .TProxyMode
891939 tproxyonly := conf .TProxyOnly != ""
892940 if tproxyonly {
893941 p .tproxyAddr = getFullAddress (conf .TProxyOnly )
0 commit comments