Skip to content

Commit 671dd8e

Browse files
committed
[+] #28 Support local port frowarding
1 parent dffd811 commit 671dd8e

File tree

7 files changed

+317
-167
lines changed

7 files changed

+317
-167
lines changed

lib/cli/dispatcher/tunnel.go

+44-8
Original file line numberDiff line numberDiff line change
@@ -16,27 +16,63 @@ func (dispatcher Dispatcher) Tunnel(args []string) {
1616
}
1717

1818
if context.Ctx.CurrentTermite != nil {
19-
if len(args) != 3 {
19+
if len(args) != 6 {
2020
log.Error("Arguments error, use `Help Tunnel` to get more information")
2121
dispatcher.TunnelHelp([]string{})
2222
return
2323
}
2424

25-
mode := args[0]
26-
host := args[1]
27-
port, err := strconv.ParseUint(args[2], 10, 16)
25+
action := args[0]
26+
mode := args[1]
27+
src_host := args[2]
28+
src_port, err := strconv.ParseUint(args[3], 10, 16)
2829

2930
if err != nil {
3031
log.Error("Invalid port: %s, use `Help Tunnel` to get more information", args[1])
3132
dispatcher.TunnelHelp([]string{})
3233
return
3334
}
3435

35-
switch strings.ToLower(mode) {
36+
dst_host := args[4]
37+
dst_port, err := strconv.ParseUint(args[5], 10, 16)
38+
39+
if err != nil {
40+
log.Error("Invalid port: %s, use `Help Tunnel` to get more information", args[1])
41+
dispatcher.TunnelHelp([]string{})
42+
return
43+
}
44+
45+
switch strings.ToLower(action) {
3646
case "create":
37-
context.Ctx.CurrentTermite.CreateTunnel(host, uint16(port))
47+
switch strings.ToLower(mode) {
48+
case "pull":
49+
local_address := fmt.Sprintf("%s:%d", dst_host, dst_port)
50+
remote_address := fmt.Sprintf("%s:%d", src_host, src_port)
51+
log.Info("Mapping remote (%s) to local (%s)", remote_address, local_address)
52+
context.AddTunnelConfig(context.Ctx.CurrentTermite, local_address, remote_address)
53+
case "push":
54+
// context.Ctx.CurrentTermite.CreatePushTunnel(src_host, uint16(src_port), dst_host, uint16(dst_port))
55+
log.Error("TBD")
56+
case "dynamic":
57+
log.Error("TBD")
58+
default:
59+
log.Error("Invalid mode: %s, should be in {'Pull', 'Push', 'Dynamic'}", mode)
60+
}
3861
case "delete":
39-
context.Ctx.CurrentTermite.DeleteTunnel(host, uint16(port))
62+
switch strings.ToLower(mode) {
63+
case "pull":
64+
// context.Ctx.CurrentTermite.DeletePullTunnel(dst_host, uint16(dst_port), src_host, uint16(src_port))
65+
log.Error("TBD")
66+
case "push":
67+
// context.Ctx.CurrentTermite.DeleteTunnel(src_host, uint16(src_port), dst_host, uint16(dst_port))
68+
log.Error("TBD")
69+
case "dynamic":
70+
log.Error("TBD")
71+
default:
72+
log.Error("Invalid mode: %s, should be in {'Pull', 'Push', 'Dynamic'}", mode)
73+
}
74+
default:
75+
log.Error("Invalid action: %s, should be in {'Create', 'Delete'}", action)
4076
}
4177
}
4278

@@ -47,7 +83,7 @@ func (dispatcher Dispatcher) Tunnel(args []string) {
4783

4884
func (dispatcher Dispatcher) TunnelHelp(args []string) {
4985
fmt.Println("Usage of Tunnel")
50-
fmt.Println("\tTunnel [Create|Delete] [Host] [Port]")
86+
fmt.Println("\tTunnel [Create|Delete] [Mode] [Src Host] [Src Port] [Dst Host] [Dst Port]")
5187
}
5288

5389
func (dispatcher Dispatcher) TunnelDesc(args []string) {

lib/context/client.go

-1
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,6 @@ func CreateTCPClient(conn net.Conn, server *TCPServer) *TCPClient {
7777
}
7878

7979
func (c *TCPClient) Close() {
80-
log.Debug("Closing client: %s", c.FullDesc())
8180
c.conn.Close()
8281
}
8382

lib/context/context.go

+106
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,33 @@
11
package context
22

33
import (
4+
"net"
45
"os"
56
"strings"
67
"sync"
78

89
"github.com/WangYihang/Platypus/lib/util/config"
10+
"github.com/WangYihang/Platypus/lib/util/log"
911
"github.com/WangYihang/Platypus/lib/util/message"
12+
"github.com/WangYihang/Platypus/lib/util/str"
1013
"github.com/WangYihang/Platypus/lib/util/ui"
1114
"github.com/WangYihang/readline"
1215
"github.com/fatih/color"
1316
"github.com/gin-gonic/gin"
1417
"gopkg.in/olahol/melody.v1"
1518
)
1619

20+
type TunnelConfig struct {
21+
Termite *TermiteClient
22+
Address string
23+
Server *net.Listener
24+
}
25+
26+
type TunnelInstance struct {
27+
Termite *TermiteClient
28+
Conn *net.Conn
29+
}
30+
1731
type Context struct {
1832
Servers map[string](*TCPServer)
1933
NotifyWebSocket *melody.Melody
@@ -22,6 +36,8 @@ type Context struct {
2236
CommandPrompt string
2337
RLInstance *readline.Instance
2438
Interacting *sync.Mutex
39+
TunnelConfig map[string]TunnelConfig
40+
TunnelInstance map[string]TunnelInstance
2541
// Set later in platypus.go
2642
Distributor *Distributor
2743
RESTful *gin.Engine
@@ -40,6 +56,8 @@ func CreateContext() {
4056
CommandPrompt: color.CyanString("» "),
4157
RLInstance: nil,
4258
Interacting: new(sync.Mutex),
59+
TunnelConfig: make(map[string]TunnelConfig),
60+
TunnelInstance: make(map[string]TunnelInstance),
4361
}
4462
}
4563
// Signal Handler
@@ -163,3 +181,91 @@ func Shutdown() {
163181
}
164182
os.Exit(0)
165183
}
184+
185+
func AddTunnelConfig(termite *TermiteClient, local_address string, remote_address string) {
186+
tunnel, err := net.Listen("tcp", local_address)
187+
if err != nil {
188+
log.Error(err.Error())
189+
return
190+
} else {
191+
Ctx.TunnelConfig[local_address] = TunnelConfig{
192+
Termite: termite,
193+
Address: remote_address,
194+
Server: &tunnel,
195+
}
196+
}
197+
198+
go func() {
199+
for {
200+
conn, _ := tunnel.Accept()
201+
202+
token := str.RandomString(0x10)
203+
204+
termite.EncoderLock.Lock()
205+
err := termite.Encoder.Encode(message.Message{
206+
Type: message.TUNNEL_CONNECT,
207+
Body: message.BodyTunnelConnect{
208+
Token: token,
209+
Address: remote_address,
210+
},
211+
})
212+
termite.EncoderLock.Unlock()
213+
214+
if err == nil {
215+
Ctx.TunnelInstance[token] = TunnelInstance{
216+
Conn: &conn,
217+
Termite: termite,
218+
}
219+
}
220+
}
221+
}()
222+
}
223+
224+
func WriteTunnel(termite *TermiteClient, token string, data []byte) {
225+
termite.AtomLock.Lock()
226+
defer func() { termite.AtomLock.Unlock() }()
227+
228+
termite.EncoderLock.Lock()
229+
err := termite.Encoder.Encode(message.Message{
230+
Type: message.TUNNEL_DATA,
231+
Body: message.BodyTunnelData{
232+
Token: token,
233+
Data: data,
234+
},
235+
})
236+
termite.EncoderLock.Unlock()
237+
238+
if err != nil {
239+
log.Error("Network error: %s", err)
240+
}
241+
}
242+
243+
// func DeleteTunnelConfig(local_host string, local_port uint16, remote_host string, remote_port uint16) {
244+
// local_address := fmt.Sprintf("%s:%d", local_host, local_port)
245+
// remote_address := fmt.Sprintf("%s:%d", remote_host, remote_port)
246+
247+
// log.Info("Unmapping from remote %s to local %s", remote_address, local_address)
248+
249+
// if tc, exists := Ctx.TunnelConfig[local_address]; exists {
250+
// c.AtomLock.Lock()
251+
// defer func() { c.AtomLock.Unlock() }()
252+
253+
// c.EncoderLock.Lock()
254+
// err := c.Encoder.Encode(message.Message{
255+
// Type: message.TUNNEL_DELETE,
256+
// Body: message.BodyTunnelDelete{
257+
// Key: key,
258+
// TermiteHash: c.Hash,
259+
// },
260+
// })
261+
// c.EncoderLock.Unlock()
262+
263+
// if err != nil {
264+
// log.Error("Network error: %s", err)
265+
// } else {
266+
// delete(Ctx.TunnelConfig, local_address)
267+
// }
268+
// } else {
269+
// log.Info("No such tunnel from remote %s to local %s", remote_address, local_address)
270+
// }
271+
// }

lib/context/server.go

+52-33
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import (
2020
"github.com/WangYihang/Platypus/lib/util/str"
2121
humanize "github.com/dustin/go-humanize"
2222
"github.com/jedib0t/go-pretty/table"
23-
"github.com/phayes/freeport"
2423
)
2524

2625
type WebSocketMessage struct {
@@ -544,43 +543,63 @@ func TermiteMessageDispatcher(client *TermiteClient) {
544543
log.Error("No such key")
545544
}
546545
case message.TUNNEL_CONNECTED:
547-
target := msg.Body.(*message.BodyTunnelConnected).Target
548-
port, _ := freeport.GetFreePort()
549-
localAddress := fmt.Sprintf("0.0.0.0:%d", port)
550-
tunnel, err := net.Listen("tcp", localAddress)
551-
if err != nil {
552-
log.Error(err.Error())
553-
break
554-
}
555-
log.Info("%s -> %s", localAddress, target)
556-
go func(target string, port int) {
557-
conn, _ := tunnel.Accept()
558-
Ctx.CurrentTermite.Tunnels[target] = &conn
559-
log.Info("Server client tunnel connected: %v", conn)
560-
for {
561-
buf := make([]byte, 1024)
562-
n, err := conn.Read(buf)
563-
if err != nil {
564-
log.Error(err.Error())
565-
break
566-
}
567-
if n > 0 {
568-
log.Info(">> %v", buf[0:n])
569-
Ctx.CurrentTermite.WriteTunnel(target, buf[0:n])
546+
token := msg.Body.(*message.BodyTunnelConnected).Token
547+
log.Success("Tunnel (%s) connected", token)
548+
if ti, exists := Ctx.TunnelInstance[token]; exists {
549+
go func() {
550+
for {
551+
buf := make([]byte, 1024)
552+
n, err := (*ti.Conn).Read(buf)
553+
if err != nil {
554+
log.Success("Tunnel (%s) disconnected: %s", token, err.Error())
555+
ti.Termite.EncoderLock.Lock()
556+
ti.Termite.Encoder.Encode(message.Message{
557+
Type: message.TUNNEL_DISCONNECT,
558+
Body: message.BodyTunnelDisconnect{
559+
Token: token,
560+
},
561+
})
562+
ti.Termite.EncoderLock.Unlock()
563+
(*ti.Conn).Close()
564+
break
565+
} else {
566+
if n > 0 {
567+
data := buf[0:n]
568+
WriteTunnel(ti.Termite, token, data)
569+
}
570+
}
570571
}
571-
}
572-
}(target, port)
572+
}()
573+
} else {
574+
log.Error("No such connection")
575+
}
576+
case message.TUNNEL_CONNECT_FAILED:
577+
token := msg.Body.(*message.BodyTunnelConnectFailed).Token
578+
reason := msg.Body.(*message.BodyTunnelConnectFailed).Reason
579+
if ti, exists := Ctx.TunnelInstance[token]; exists {
580+
log.Error("Connecting to %s failed: %s", token, reason)
581+
(*ti.Conn).Close()
582+
delete(Ctx.TunnelInstance, token)
583+
} else {
584+
log.Error("No such connection")
585+
}
586+
case message.TUNNEL_DISCONNECTED:
587+
token := msg.Body.(*message.BodyTunnelDisconnected).Token
588+
if ti, exists := Ctx.TunnelInstance[token]; exists {
589+
log.Error("%s disconnected", token)
590+
(*ti.Conn).Close()
591+
delete(Ctx.TunnelInstance, token)
592+
} else {
593+
log.Error("No such connection")
594+
}
573595
case message.TUNNEL_DATA:
574-
target := msg.Body.(*message.BodyTunnelData).Target
596+
token := msg.Body.(*message.BodyTunnelData).Token
575597
data := msg.Body.(*message.BodyTunnelData).Data
576-
log.Info("%s, %v, connected", target, data)
577-
578-
if conn, exists := Ctx.CurrentTermite.Tunnels[target]; exists {
579-
(*conn).Write(data)
598+
if ti, exists := Ctx.TunnelInstance[token]; exists {
599+
(*ti.Conn).Write(data)
580600
} else {
581-
log.Error("No such tunnel")
601+
log.Error("No such connection")
582602
}
583-
584603
}
585604
}
586605
}

0 commit comments

Comments
 (0)