Skip to content

Commit

Permalink
Feature socks udp associate
Browse files Browse the repository at this point in the history
  • Loading branch information
VHSgunzo committed Jun 23, 2024
1 parent e2d00f5 commit 9831992
Show file tree
Hide file tree
Showing 13 changed files with 303 additions and 47 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
VERSION=$(shell git describe --abbrev=0 --tags)
BUILD=$(shell git rev-parse HEAD)
# BUILD=$(shell git rev-parse HEAD)
DIRBUILD=./build
# DIR=${DIRBUILD}/${VERSION}/${BUILD}/bin
DIR=${DIRBUILD}
Expand Down
1 change: 1 addition & 0 deletions client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ func NewClient(c *Config) (*Client, error) {
}
//set default log level
client.Logger.Info = true
client.Logger.Debug = c.Verbose
//validate remotes
for _, s := range c.Remotes {
r, err := settings.DecodeRemote(s)
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ require (
require (
github.com/andrew-d/go-termutil v0.0.0-20150726205930-009166a695a2 // indirect
github.com/jpillora/ansi v1.0.3 // indirect
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/sys v0.21.0 // indirect
)
11 changes: 11 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ github.com/andrew-d/go-termutil v0.0.0-20150726205930-009166a695a2 h1:axBiC50cNZ
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/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
Expand All @@ -14,15 +15,25 @@ 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/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/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.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA=
golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
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 @@ -380,9 +380,9 @@ func client(args []string) {
flags.DurationVar(&config.MaxRetryInterval, "max-retry-interval", 0, "")
flags.StringVar(&config.Proxy, "proxy", "", "")
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 @@ -408,7 +408,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 @@ -129,7 +129,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"
"log"
"net"

"github.com/jpillora/chisel/share/cio"
"github.com/meteorite/scope"
"github.com/meteorite/socks5"
"golang.org/x/crypto/ssh"
)

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()
}
17 changes: 5 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"
"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,11 @@ 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(io.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

0 comments on commit 9831992

Please sign in to comment.