@@ -216,6 +216,7 @@ const (
216216 proxyModeHTTP
217217 proxyModeSOCKS5
218218 proxyModeStdIO
219+ proxyModePortForward
219220)
220221
221222type proxyModeArg struct {
@@ -231,6 +232,8 @@ func (a *proxyModeArg) Set(arg string) error {
231232 val = proxyModeSOCKS5
232233 case "stdio" :
233234 val = proxyModeStdIO
235+ case "port-forward" :
236+ val = proxyModePortForward
234237 default :
235238 return fmt .Errorf ("unrecognized proxy mode %q" , arg )
236239 }
@@ -246,6 +249,8 @@ func (a *proxyModeArg) String() string {
246249 return "socks5"
247250 case proxyModeStdIO :
248251 return "stdio"
252+ case proxyModePortForward :
253+ return "port-forward"
249254 default :
250255 return fmt .Sprintf ("proxyMode(%d)" , int (a .value ))
251256 }
@@ -382,7 +387,7 @@ func parse_args() *CLIArgs {
382387 })
383388 flag .BoolVar (& args .unixSockUnlink , "unix-sock-unlink" , true , "delete file object located at Unix domain socket bind path before binding" )
384389 flag .Var (& args .unixSockMode , "unix-sock-mode" , "set file mode for bound unix socket" )
385- flag .Var (& args .mode , "mode" , "proxy operation mode (http/socks5/stdio)" )
390+ flag .Var (& args .mode , "mode" , "proxy operation mode (http/socks5/stdio/port-forward )" )
386391 flag .StringVar (& args .auth , "auth" , "none://" , "auth parameters" )
387392 flag .IntVar (& args .verbosity , "verbosity" , 20 , "logging verbosity " +
388393 "(10 - debug, 20 - info, 30 - warning, 40 - error, 50 - critical)" )
@@ -513,12 +518,13 @@ func run() int {
513518 return 0
514519 }
515520
516- if args .mode .value == proxyModeStdIO {
521+ switch args .mode .value {
522+ case proxyModeStdIO , proxyModePortForward :
517523 // expect exactly two positional arguments
518524 if len (args .positionalArgs ) != 2 {
519525 arg_fail ("Exactly two positional arguments are expected in this mode: host and port" )
520526 }
521- } else {
527+ default :
522528 // we don't expect positional arguments in the main operation mode
523529 if len (args .positionalArgs ) > 0 {
524530 arg_fail ("Unexpected positional arguments! Check your command line." )
@@ -882,8 +888,15 @@ func run() int {
882888 }()
883889 server := socks5 .NewServer (opts ... )
884890 if err := server .Serve (listener ); err != nil {
885- mainLogger .Info ("Reached normal server termination." )
891+ if errors .Is (err , net .ErrClosed ) {
892+ mainLogger .Info ("Reached normal server termination." )
893+ return 0
894+ } else {
895+ mainLogger .Critical ("Server failure: %v" , err )
896+ return 1
897+ }
886898 }
899+ mainLogger .Info ("Reached normal server termination." )
887900 return 0
888901 case proxyModeStdIO :
889902 handler := handler .StdIOHandler (dialerRoot , proxyLogger , forwarder )
@@ -892,6 +905,34 @@ func run() int {
892905 mainLogger .Error ("Connection interrupted: %v" , err )
893906 }
894907 return 0
908+ case proxyModePortForward :
909+ logger := clog .NewCondLogger (log .New (logWriter , "PORT-FWD: " ,
910+ log .LstdFlags | log .Lshortfile ),
911+ args .verbosity )
912+ address := net .JoinHostPort (args .positionalArgs [0 ], args .positionalArgs [1 ])
913+ streamHandler := handler .PortForwardHandler (logger , dialerRoot , address , forwarder )
914+ connHandler := func (c net.Conn ) {
915+ ctx , cl := context .WithCancel (stopContext )
916+ defer cl ()
917+ streamHandler (ctx , c )
918+ }
919+
920+ go func () {
921+ <- stopContext .Done ()
922+ mainLogger .Info ("Shutting down..." )
923+ listener .Close ()
924+ }()
925+ if err := handler .StreamServe (listener , connHandler ); err != nil {
926+ if errors .Is (err , net .ErrClosed ) {
927+ mainLogger .Info ("Reached normal server termination." )
928+ return 0
929+ } else {
930+ mainLogger .Critical ("Server failure: %v" , err )
931+ return 1
932+ }
933+ }
934+ mainLogger .Info ("Reached normal server termination." )
935+ return 0
895936 }
896937
897938 mainLogger .Critical ("unknown proxy mode" )
0 commit comments