Skip to content

Commit 16fad84

Browse files
committed
subcommand dialer
1 parent a9f4d2e commit 16fad84

File tree

2 files changed

+84
-0
lines changed

2 files changed

+84
-0
lines changed

dialer/command.go

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package dialer
2+
3+
import (
4+
"context"
5+
"errors"
6+
"fmt"
7+
"net"
8+
"net/url"
9+
"os"
10+
"os/exec"
11+
"time"
12+
13+
xproxy "golang.org/x/net/proxy"
14+
)
15+
16+
type CommandDialer struct {
17+
command []string
18+
waitDelay time.Duration
19+
}
20+
21+
func CommandDialerFromURL(u *url.URL, _ xproxy.Dialer) (xproxy.Dialer, error) {
22+
params, err := url.ParseQuery(u.RawQuery)
23+
if err != nil {
24+
return nil, err
25+
}
26+
cmd := params.Get("cmd")
27+
if cmd == "" {
28+
return nil, errors.New("command is not specified")
29+
}
30+
args := params["arg"]
31+
waitDelay := 5 * time.Second
32+
if wd := params.Get("wait_delay"); wd != "" {
33+
waitDelay, err = time.ParseDuration(wd)
34+
if err != nil {
35+
return nil, fmt.Errorf("unable to parse wait_delay parameter: %w", err)
36+
}
37+
}
38+
command := make([]string, 0, len(args)+1)
39+
command = append(command, cmd)
40+
command = append(command, args...)
41+
return &CommandDialer{
42+
command: command,
43+
waitDelay: waitDelay,
44+
}, nil
45+
}
46+
47+
func (d *CommandDialer) Dial(network, address string) (net.Conn, error) {
48+
return d.DialContext(context.Background(), network, address)
49+
}
50+
51+
func (d *CommandDialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
52+
cmd := exec.CommandContext(ctx, d.command[0], d.command[1:]...)
53+
cmd.Env = append(os.Environ(),
54+
"DUMBPROXY_DST_ADDR="+address,
55+
"DUMBPROXY_DST_NET="+network,
56+
)
57+
cmdIn, err := cmd.StdinPipe()
58+
if err != nil {
59+
return nil, fmt.Errorf("can't create stdin pipe for subprocess: %w", err)
60+
}
61+
cmdOut, err := cmd.StdoutPipe()
62+
if err != nil {
63+
return nil, fmt.Errorf("can't create stdout pipe for subprocess: %w", err)
64+
}
65+
cmd.Stderr = os.Stderr
66+
cmd.WaitDelay = d.waitDelay
67+
if err := cmd.Start(); err != nil {
68+
return nil, fmt.Errorf("unable to start subprocess: %w", err)
69+
}
70+
go func() {
71+
cmd.Wait()
72+
cmdIn.Close()
73+
cmdOut.Close()
74+
}()
75+
return NewPipeConn(cmdOut.(ReadPipe), cmdIn.(WritePipe)), nil
76+
}
77+
78+
func (d *CommandDialer) WantsHostname(_ context.Context, _, _ string) bool {
79+
return true
80+
}
81+
82+
var _ Dialer = new(CommandDialer)
83+
var _ HostnameWanter = new(CommandDialer)

dialer/dialer.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ func init() {
2929
return NeverRequireHostname(MaybeWrapWithContextDialer(d)), nil
3030
})
3131
xproxy.RegisterDialerType("chain", ChainFromURL)
32+
xproxy.RegisterDialerType("cmd", CommandDialerFromURL)
3233
}
3334

3435
type LegacyDialer interface {

0 commit comments

Comments
 (0)