Skip to content

Commit 9831992

Browse files
committed
Feature socks udp associate
jpillora#250
1 parent e2d00f5 commit 9831992

File tree

13 files changed

+303
-47
lines changed

13 files changed

+303
-47
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
VERSION=$(shell git describe --abbrev=0 --tags)
2-
BUILD=$(shell git rev-parse HEAD)
2+
# BUILD=$(shell git rev-parse HEAD)
33
DIRBUILD=./build
44
# DIR=${DIRBUILD}/${VERSION}/${BUILD}/bin
55
DIR=${DIRBUILD}

client/client.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ func NewClient(c *Config) (*Client, error) {
8888
}
8989
//set default log level
9090
client.Logger.Info = true
91+
client.Logger.Debug = c.Verbose
9192
//validate remotes
9293
for _, s := range c.Remotes {
9394
r, err := settings.DecodeRemote(s)

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ require (
1717
require (
1818
github.com/andrew-d/go-termutil v0.0.0-20150726205930-009166a695a2 // indirect
1919
github.com/jpillora/ansi v1.0.3 // indirect
20+
github.com/meteorite/scope v0.0.0-20210314203727-1e230fea59ae
21+
github.com/meteorite/socks5 v0.0.0-20210604215257-bf325eecbc5d
2022
github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce // indirect
2123
golang.org/x/sys v0.21.0 // indirect
2224
)

go.sum

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ github.com/andrew-d/go-termutil v0.0.0-20150726205930-009166a695a2 h1:axBiC50cNZ
22
github.com/andrew-d/go-termutil v0.0.0-20150726205930-009166a695a2/go.mod h1:jnzFpU88PccN/tPPhCpnNU8mZphvKxYM9lLNkd8e+os=
33
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
44
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
5+
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
56
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
67
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
78
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
@@ -14,15 +15,25 @@ github.com/jpillora/requestlog v1.0.0 h1:bg++eJ74T7DYL3DlIpiwknrtfdUA9oP/M4fL+Pp
1415
github.com/jpillora/requestlog v1.0.0/go.mod h1:HTWQb7QfDc2jtHnWe2XEIEeJB7gJPnVdpNn52HXPvy8=
1516
github.com/jpillora/sizestr v1.0.0 h1:4tr0FLxs1Mtq3TnsLDV+GYUWG7Q26a6s+tV5Zfw2ygw=
1617
github.com/jpillora/sizestr v1.0.0/go.mod h1:bUhLv4ctkknatr6gR42qPxirmd5+ds1u7mzD+MZ33f0=
18+
github.com/meteorite/scope v0.0.0-20210314203727-1e230fea59ae h1:+OY7loRJNAPnyYG4YoPn1wuAvX4SHeYo2XyIQmHJSWM=
19+
github.com/meteorite/scope v0.0.0-20210314203727-1e230fea59ae/go.mod h1:oGJ2Lf7WjcbEgwaANS+H3ykcLytXXiuHEWLfalSzXb8=
20+
github.com/meteorite/socks5 v0.0.0-20210604215257-bf325eecbc5d h1:0CEORKRd169DDvpucqMLH57o9UPGMF8Akw3W3YLJ1Kk=
21+
github.com/meteorite/socks5 v0.0.0-20210604215257-bf325eecbc5d/go.mod h1:1q/GEvRbr06wBgq8tHcvGKIC+AFTZJrwj73HPfF2LwU=
22+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
23+
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
24+
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
1725
github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce h1:fb190+cK2Xz/dvi9Hv8eCYJYvIGUTN2/KLq1pT6CjEc=
1826
github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce/go.mod h1:o8v6yHRoik09Xen7gje4m9ERNah1d1PPsVq1VEx9vE4=
1927
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
2028
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
2129
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
2230
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
31+
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
2332
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
2433
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
2534
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
2635
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
2736
golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA=
2837
golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
38+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
39+
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

main.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -380,9 +380,9 @@ func client(args []string) {
380380
flags.DurationVar(&config.MaxRetryInterval, "max-retry-interval", 0, "")
381381
flags.StringVar(&config.Proxy, "proxy", "", "")
382382
flags.Var(&headerFlags{config.Headers}, "header", "")
383+
flags.BoolVar(&config.Verbose, "v", false, "")
383384
hostname := flags.String("hostname", "", "")
384385
pid := flags.Bool("pid", false, "")
385-
verbose := flags.Bool("v", false, "")
386386
flags.Usage = func() {
387387
fmt.Print(clientHelp)
388388
os.Exit(0)
@@ -408,7 +408,6 @@ func client(args []string) {
408408
if err != nil {
409409
log.Fatal(err)
410410
}
411-
c.Debug = *verbose
412411
if *pid {
413412
generatePidFile()
414413
}

server/server_handler.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ func (s *Server) handleWebsocket(w http.ResponseWriter, req *http.Request) {
129129
//confirm reverse tunnels are allowed
130130
if r.Reverse && !s.config.Reverse {
131131
l.Debugf("Denied reverse port forwarding request, please enable --reverse")
132-
failed(s.Errorf("Reverse port forwaring not enabled on server"))
132+
failed(s.Errorf("Reverse port forwarding not enabled on server"))
133133
return
134134
}
135135
//confirm reverse tunnel is available

share/settings/remote.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ func isHost(s string) bool {
151151
return true
152152
}
153153

154-
var l4Proto = regexp.MustCompile(`(?i)\/(tcp|udp)$`)
154+
var l4Proto = regexp.MustCompile(`(?i)\/(tcp|udp|sot|sou)$`)
155155

156156
// L4Proto extacts the layer-4 protocol from the given string
157157
func L4Proto(s string) (head, proto string) {

share/tunnel/socks_handler.go

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
package tunnel
2+
3+
import (
4+
"context"
5+
"encoding/gob"
6+
"fmt"
7+
"log"
8+
"net"
9+
10+
"github.com/jpillora/chisel/share/cio"
11+
"github.com/meteorite/scope"
12+
"github.com/meteorite/socks5"
13+
"golang.org/x/crypto/ssh"
14+
)
15+
16+
type socksHandler struct {
17+
p *Proxy
18+
udpLocalAddr *socks5.AddrSpec
19+
sl *log.Logger
20+
udp *socks5.SingleUDPPortAssociate
21+
}
22+
23+
func newSocksHandler(p *Proxy, localUDPAddr *net.UDPAddr, sl *log.Logger) *socksHandler {
24+
return &socksHandler{
25+
p: p,
26+
udpLocalAddr: &socks5.AddrSpec{
27+
IP: localUDPAddr.IP,
28+
Port: localUDPAddr.Port,
29+
},
30+
sl: sl,
31+
}
32+
}
33+
34+
func (h *socksHandler) OnStartServe(ctxServer socks5.ContextGo, _ net.Listener) error {
35+
h.udp = socks5.MakeSingleUDPPortAssociate(h.udpLocalAddr, h, h.sl)
36+
return h.udp.ListenAndServeUDPPort(ctxServer, "udp")
37+
}
38+
39+
func (h *socksHandler) ErrLog() socks5.ErrorLogger {
40+
return h.sl
41+
}
42+
43+
func (h *socksHandler) OnConnect(ctx context.Context, conn net.Conn, req *socks5.Request) error {
44+
return h.p.pipeRemote(ctx, conn, req.DestAddr.Address()+"/sot", func(dst ssh.Channel) error {
45+
code := []byte{0}
46+
_, err := dst.Read(code)
47+
if err != nil {
48+
return fmt.Errorf("can't receive socks code from server: %w", err)
49+
}
50+
if code[0] != socks5.ReplySucceeded {
51+
if err = req.SendError(conn, code[0]); err != nil {
52+
return fmt.Errorf("failed to send reply to client: %w", err)
53+
}
54+
return fmt.Errorf("can't connect to destination server (code: %d)", code[0])
55+
}
56+
if err := req.SendConnectSuccess(conn); err != nil {
57+
return fmt.Errorf("failed to send reply to client: %w", err)
58+
}
59+
return nil
60+
})
61+
}
62+
63+
func (h *socksHandler) OnAssociate(_ context.Context, conn net.Conn, _ *socks5.Request) error {
64+
return h.udp.OnAssociate(conn)
65+
}
66+
67+
func (h *socksHandler) MaxUDPPacketSize() uint {
68+
return maxMTU
69+
}
70+
71+
type socksUdpConnector struct {
72+
*cio.Logger
73+
outbound *udpChannel
74+
}
75+
76+
func (h *socksHandler) MakeRemoteUDPConn(
77+
ctxClient socks5.ContextGo, _ socks5.ContextGo, sendBack socks5.UDPSendBack, onBroken func(),
78+
) (socks5.RemoteUDPConn, error) {
79+
sshConn := h.p.sshTun.getSSH(ctxClient.Ctx())
80+
if sshConn == nil {
81+
return nil, fmt.Errorf("ssh-conn nil")
82+
}
83+
dstSpec := "/sou" //just "/sou" since the remote destination address is sent with each packet
84+
rwc, reqs, err := sshConn.OpenChannel("chisel", []byte(dstSpec))
85+
if err != nil {
86+
return nil, fmt.Errorf("ssh-chan error: %w", err)
87+
}
88+
ctxClient.GoNoError(func() { ssh.DiscardRequests(reqs) })
89+
90+
c := &socksUdpConnector{
91+
Logger: h.p.Logger,
92+
outbound: &udpChannel{
93+
r: gob.NewDecoder(rwc),
94+
w: gob.NewEncoder(rwc),
95+
c: rwc,
96+
},
97+
}
98+
ctxClient.GoNoError(func() {
99+
defer onBroken()
100+
defer scope.Closer(ctxClient.Ctx(), c.outbound.c).Close()
101+
102+
for {
103+
//receive from channel, including source address
104+
p := udpPacket{}
105+
c.Debugf("reading next udp packet from ssh channel to remote")
106+
if err := c.outbound.decode(&p); err != nil {
107+
c.Debugf("decode error: %s", err)
108+
return
109+
}
110+
111+
//parse source address
112+
fromAddr, err := socks5.ParseHostPort(p.Src)
113+
if err != nil {
114+
c.Debugf("error parsing received packet source spec: %s: %s", p.Src, err)
115+
continue
116+
}
117+
118+
//write back to inbound udp
119+
err = sendBack(fromAddr, p.Payload)
120+
if err != nil {
121+
c.Debugf("send back error: %s", err)
122+
return
123+
}
124+
}
125+
})
126+
c.Debugf("new ssh channel for udp is created")
127+
return c, nil
128+
}
129+
130+
func (c *socksUdpConnector) Send(_ context.Context, data []byte, remoteAddr *socks5.AddrSpec) error {
131+
return c.outbound.encode(remoteAddr.Address(), data)
132+
}
133+
func (c *socksUdpConnector) Close() error {
134+
return c.outbound.c.Close()
135+
}

share/tunnel/tunnel.go

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,9 @@ import (
44
"bytes"
55
"context"
66
"errors"
7-
"io"
8-
"log"
9-
"os"
107
"sync"
118
"time"
129

13-
"github.com/armon/go-socks5"
1410
"github.com/jpillora/chisel/share/cio"
1511
"github.com/jpillora/chisel/share/cnet"
1612
"github.com/jpillora/chisel/share/settings"
@@ -43,8 +39,8 @@ type Tunnel struct {
4339
//proxies
4440
proxyCount int
4541
//internals
46-
connStats cnet.ConnCount
47-
socksServer *socks5.Server
42+
connStats cnet.ConnCount
43+
socksAllowed bool
4844
}
4945

5046
// New Tunnel from the given Config
@@ -54,14 +50,11 @@ func New(c Config) *Tunnel {
5450
Config: c,
5551
}
5652
t.activatingConn.Add(1)
57-
//setup socks server (not listening on any port!)
53+
// decide, whether socks server outbound connector (without client-interaction part, not listening on any port)
54+
// is allowed or not
5855
extra := ""
56+
t.socksAllowed = c.Socks
5957
if c.Socks {
60-
sl := log.New(io.Discard, "", 0)
61-
if t.Logger.Debug {
62-
sl = log.New(os.Stdout, "[socks]", log.Ldate|log.Ltime)
63-
}
64-
t.socksServer, _ = socks5.New(&socks5.Config{Logger: sl})
6558
extra += " (SOCKS enabled)"
6659
}
6760
t.Debugf("Created%s", extra)

0 commit comments

Comments
 (0)