Skip to content

Commit 09925b3

Browse files
authored
Merge pull request #161 from SenseUnit/port-forward
Port forwarding mode
2 parents f05aa2f + 0bc2edc commit 09925b3

File tree

4 files changed

+93
-4
lines changed

4 files changed

+93
-4
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ Simple, scriptable, secure HTTP/SOCKS5 forward proxy.
4343
* Access filter by JS function
4444
* Upstream proxy selection by JS function
4545
* Seamless proxy client integration with OpenSSH: `ssh -o ProxyCommand="dumbproxy -config proxy.cfg -mode stdio %h %p" root@server1`
46+
* Port-forwarding mode
4647

4748
## Installation
4849

handler/streamhandler.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package handler
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"net"
7+
8+
ddto "github.com/SenseUnit/dumbproxy/dialer/dto"
9+
clog "github.com/SenseUnit/dumbproxy/log"
10+
)
11+
12+
func PortForwardHandler(logger *clog.CondLogger, dialer HandlerDialer, address string, forward ForwardFunc) func(context.Context, net.Conn) {
13+
return func(ctx context.Context, c net.Conn) {
14+
if err := func() error {
15+
defer c.Close()
16+
username := ""
17+
localAddr := c.LocalAddr().String()
18+
ctx = ddto.BoundDialerParamsToContext(ctx, nil, trimAddrPort(localAddr))
19+
ctx = ddto.FilterParamsToContext(ctx, nil, username)
20+
logger.Info("Request: %v => %v %q %v %v %v", c.RemoteAddr().String(), localAddr, username, "STREAM", "CONNECT", address)
21+
target, err := dialer.DialContext(ctx, "tcp", address)
22+
if err != nil {
23+
return fmt.Errorf("connect to %s failed: %w", address, err)
24+
}
25+
defer target.Close()
26+
return forward(ctx, username, c, target)
27+
}(); err != nil {
28+
logger.Error("handler failure: %v", err)
29+
}
30+
}
31+
}

handler/streamserver.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package handler
2+
3+
import (
4+
"net"
5+
)
6+
7+
func StreamServe(l net.Listener, h func(conn net.Conn)) error {
8+
defer l.Close()
9+
for {
10+
conn, err := l.Accept()
11+
if err != nil {
12+
return err
13+
}
14+
go h(conn)
15+
}
16+
}

main.go

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,7 @@ const (
216216
proxyModeHTTP
217217
proxyModeSOCKS5
218218
proxyModeStdIO
219+
proxyModePortForward
219220
)
220221

221222
type 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

Comments
 (0)