Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature socks udp associate #250

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
2 changes: 2 additions & 0 deletions client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ type Config struct {
Headers http.Header
TLS TLSConfig
DialContext func(ctx context.Context, network, addr string) (net.Conn, error)
Verbose bool
}

//TLSConfig for a Client
Expand Down Expand Up @@ -104,6 +105,7 @@ func NewClient(c *Config) (*Client, error) {
}
//set default log level
client.Logger.Info = true
client.Logger.Debug = c.Verbose
//configure tls
if u.Scheme == "wss" {
tc := &tls.Config{}
Expand Down
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ go 1.13

require (
github.com/andrew-d/go-termutil v0.0.0-20150726205930-009166a695a2 // indirect
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5
github.com/fsnotify/fsnotify v1.4.9
github.com/gorilla/websocket v1.4.2
github.com/jpillora/ansi v1.0.2 // indirect
github.com/jpillora/backoff v1.0.0
github.com/jpillora/requestlog v1.0.0
github.com/jpillora/sizestr v1.0.0
github.com/meteorite/scope v0.0.0-20210314203727-1e230fea59ae
github.com/meteorite/socks5 v0.0.0-20210604215257-bf325eecbc5d
github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce // indirect
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e
golang.org/x/net v0.0.0-20210614182718-04defd469f4e
Expand Down
15 changes: 13 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
github.com/andrew-d/go-termutil v0.0.0-20150726205930-009166a695a2 h1:axBiC50cNZOs7ygH5BgQp4N+aYrZ2DNpWZ1KG3VOSOM=
github.com/andrew-d/go-termutil v0.0.0-20150726205930-009166a695a2/go.mod h1:jnzFpU88PccN/tPPhCpnNU8mZphvKxYM9lLNkd8e+os=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
Expand All @@ -14,6 +14,15 @@ github.com/jpillora/requestlog v1.0.0 h1:bg++eJ74T7DYL3DlIpiwknrtfdUA9oP/M4fL+Pp
github.com/jpillora/requestlog v1.0.0/go.mod h1:HTWQb7QfDc2jtHnWe2XEIEeJB7gJPnVdpNn52HXPvy8=
github.com/jpillora/sizestr v1.0.0 h1:4tr0FLxs1Mtq3TnsLDV+GYUWG7Q26a6s+tV5Zfw2ygw=
github.com/jpillora/sizestr v1.0.0/go.mod h1:bUhLv4ctkknatr6gR42qPxirmd5+ds1u7mzD+MZ33f0=
github.com/meteorite/scope v0.0.0-20210314203727-1e230fea59ae h1:+OY7loRJNAPnyYG4YoPn1wuAvX4SHeYo2XyIQmHJSWM=
github.com/meteorite/scope v0.0.0-20210314203727-1e230fea59ae/go.mod h1:oGJ2Lf7WjcbEgwaANS+H3ykcLytXXiuHEWLfalSzXb8=
github.com/meteorite/socks5 v0.0.0-20210604215257-bf325eecbc5d h1:0CEORKRd169DDvpucqMLH57o9UPGMF8Akw3W3YLJ1Kk=
github.com/meteorite/socks5 v0.0.0-20210604215257-bf325eecbc5d/go.mod h1:1q/GEvRbr06wBgq8tHcvGKIC+AFTZJrwj73HPfF2LwU=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please go mod tidy

Copy link
Contributor Author

@Meteorite Meteorite Jun 10, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already did it. testify is here in go.sum, because new dependencies (github.com/meteorite/scope and github.com/meteorite/socks5) use it for their tests:

> go mod graph | grep testify
github.com/meteorite/scope@v0.0.0-20210314203727-1e230fea59ae github.com/stretchr/testify@v1.7.0
github.com/meteorite/socks5@v0.0.0-20210604215257-bf325eecbc5d github.com/stretchr/testify@v1.7.0
...

so, it is a transitive dependency (needed to do go test all). Note, it is not in go.mod.

github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce h1:fb190+cK2Xz/dvi9Hv8eCYJYvIGUTN2/KLq1pT6CjEc=
github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce/go.mod h1:o8v6yHRoik09Xen7gje4m9ERNah1d1PPsVq1VEx9vE4=
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e h1:gsTQYXdTw2Gq7RBsWvlQ91b+aEQ6bXFUngBGuR8sPpI=
Expand All @@ -36,3 +45,5 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
3 changes: 1 addition & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -400,9 +400,9 @@ func client(args []string) {
flags.StringVar(&config.TLS.Cert, "tls-cert", "", "")
flags.StringVar(&config.TLS.Key, "tls-key", "", "")
flags.Var(&headerFlags{config.Headers}, "header", "")
flags.BoolVar(&config.Verbose, "v", false, "")
hostname := flags.String("hostname", "", "")
pid := flags.Bool("pid", false, "")
verbose := flags.Bool("v", false, "")
flags.Usage = func() {
fmt.Print(clientHelp)
os.Exit(0)
Expand All @@ -428,7 +428,6 @@ func client(args []string) {
if err != nil {
log.Fatal(err)
}
c.Debug = *verbose
if *pid {
generatePidFile()
}
Expand Down
2 changes: 1 addition & 1 deletion server/server_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ func (s *Server) handleWebsocket(w http.ResponseWriter, req *http.Request) {
//confirm reverse tunnels are allowed
if r.Reverse && !s.config.Reverse {
l.Debugf("Denied reverse port forwarding request, please enable --reverse")
failed(s.Errorf("Reverse port forwaring not enabled on server"))
failed(s.Errorf("Reverse port forwarding not enabled on server"))
return
}
//confirm reverse tunnel is available
Expand Down
2 changes: 1 addition & 1 deletion share/settings/remote.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ func isHost(s string) bool {
return true
}

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

//L4Proto extacts the layer-4 protocol from the given string
func L4Proto(s string) (head, proto string) {
Expand Down
135 changes: 135 additions & 0 deletions share/tunnel/socks_handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package tunnel

import (
"context"
"encoding/gob"
"fmt"
"github.com/jpillora/chisel/share/cio"
"github.com/meteorite/scope"
"github.com/meteorite/socks5"
"golang.org/x/crypto/ssh"
"log"
"net"
)

type socksHandler struct {
p *Proxy
udpLocalAddr *socks5.AddrSpec
sl *log.Logger
udp *socks5.SingleUDPPortAssociate
}

func newSocksHandler(p *Proxy, localUDPAddr *net.UDPAddr, sl *log.Logger) *socksHandler {
return &socksHandler{
p: p,
udpLocalAddr: &socks5.AddrSpec{
IP: localUDPAddr.IP,
Port: localUDPAddr.Port,
},
sl: sl,
}
}

func (h *socksHandler) OnStartServe(ctxServer socks5.ContextGo, _ net.Listener) error {
h.udp = socks5.MakeSingleUDPPortAssociate(h.udpLocalAddr, h, h.sl)
return h.udp.ListenAndServeUDPPort(ctxServer, "udp")
}

func (h *socksHandler) ErrLog() socks5.ErrorLogger {
return h.sl
}

func (h *socksHandler) OnConnect(ctx context.Context, conn net.Conn, req *socks5.Request) error {
return h.p.pipeRemote(ctx, conn, req.DestAddr.Address()+"/sot", func(dst ssh.Channel) error {
code := []byte{0}
_, err := dst.Read(code)
if err != nil {
return fmt.Errorf("can't receive socks code from server: %w", err)
}
if code[0] != socks5.ReplySucceeded {
if err = req.SendError(conn, code[0]); err != nil {
return fmt.Errorf("failed to send reply to client: %w", err)
}
return fmt.Errorf("can't connect to destination server (code: %d)", code[0])
}
if err := req.SendConnectSuccess(conn); err != nil {
return fmt.Errorf("failed to send reply to client: %w", err)
}
return nil
})
}

func (h *socksHandler) OnAssociate(_ context.Context, conn net.Conn, _ *socks5.Request) error {
return h.udp.OnAssociate(conn)
}

func (h *socksHandler) MaxUDPPacketSize() uint {
return maxMTU
}


type socksUdpConnector struct {
*cio.Logger
outbound *udpChannel
}

func (h *socksHandler) MakeRemoteUDPConn(
ctxClient socks5.ContextGo, _ socks5.ContextGo, sendBack socks5.UDPSendBack, onBroken func(),
) (socks5.RemoteUDPConn, error) {
sshConn := h.p.sshTun.getSSH(ctxClient.Ctx())
if sshConn == nil {
return nil, fmt.Errorf("ssh-conn nil")
}
dstSpec := "/sou" //just "/sou" since the remote destination address is sent with each packet
rwc, reqs, err := sshConn.OpenChannel("chisel", []byte(dstSpec))
if err != nil {
return nil, fmt.Errorf("ssh-chan error: %w", err)
}
ctxClient.GoNoError(func() { ssh.DiscardRequests(reqs) })

c := &socksUdpConnector{
Logger: h.p.Logger,
outbound: &udpChannel{
r: gob.NewDecoder(rwc),
w: gob.NewEncoder(rwc),
c: rwc,
},
}
ctxClient.GoNoError(func() {
defer onBroken()
defer scope.Closer(ctxClient.Ctx(), c.outbound.c).Close()

for {
//receive from channel, including source address
p := udpPacket{}
c.Debugf("reading next udp packet from ssh channel to remote")
if err := c.outbound.decode(&p); err != nil {
c.Debugf("decode error: %s", err)
return
}

//parse source address
fromAddr, err := socks5.ParseHostPort(p.Src)
if err != nil {
c.Debugf("error parsing received packet source spec: %s: %s", p.Src, err)
continue
}

//write back to inbound udp
err = sendBack(fromAddr, p.Payload)
if err != nil {
c.Debugf("send back error: %s", err)
return
}
}
})
c.Debugf("new ssh channel for udp is created")
return c, nil
}

func (c *socksUdpConnector) Send(_ context.Context, data []byte, remoteAddr *socks5.AddrSpec) error {
return c.outbound.encode(remoteAddr.Address(), data)
}
func (c *socksUdpConnector) Close() error {
return c.outbound.c.Close()
}
18 changes: 6 additions & 12 deletions share/tunnel/tunnel.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,9 @@ import (
"bytes"
"context"
"errors"
"io/ioutil"
"log"
"os"
"sync"
"time"

"github.com/armon/go-socks5"
"github.com/jpillora/chisel/share/cio"
"github.com/jpillora/chisel/share/cnet"
"github.com/jpillora/chisel/share/settings"
Expand Down Expand Up @@ -43,8 +39,8 @@ type Tunnel struct {
//proxies
proxyCount int
//internals
connStats cnet.ConnCount
socksServer *socks5.Server
connStats cnet.ConnCount
socksAllowed bool
}

//New Tunnel from the given Config
Expand All @@ -54,14 +50,12 @@ func New(c Config) *Tunnel {
Config: c,
}
t.activatingConn.Add(1)
//setup socks server (not listening on any port!)

// decide, whether socks server outbound connector (without client-interaction part, not listening on any port)
// is allowed or not
extra := ""
t.socksAllowed = c.Socks
if c.Socks {
sl := log.New(ioutil.Discard, "", 0)
if t.Logger.Debug {
sl = log.New(os.Stdout, "[socks]", log.Ldate|log.Ltime)
}
t.socksServer, _ = socks5.New(&socks5.Config{Logger: sl})
extra += " (SOCKS enabled)"
}
t.Debugf("Created%s", extra)
Expand Down
Loading