-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 9f01146
Showing
7 changed files
with
859 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
.vscode/ | ||
tunnel |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
# Reverse TCP tunnel | ||
For TCP-based services that do not have inbound end point, `Reverse TCP Tunnel` allows us to build a reverse TCP tunnel to open in bound access from a public end point. It virutally extends the TCP listening port to a remote machine in which a `Reverse TCP Tunnel` listener is running. | ||
|
||
We use a simple signaling protocol for tunnel establishment and connection multiplexing. | ||
|
||
## Tunnel listener | ||
Tunnel listener runs in a public end point, when it receives a `ListenRequest` from service that needs tunnelled inbound access, it opens a dynamic TCP port at public interface and multiplexes traffic between service client and the service provider. | ||
|
||
Example command to launch tunnel listener | ||
```bash | ||
./tunnel -l 5555 | ||
``` | ||
|
||
## Tunnel Connector | ||
Tunnel connector runs wihin the private network boundary, it has access to services that requires tunnelled inbound access. | ||
|
||
Example command to establish a reverse tunnelling setup | ||
|
||
```bash | ||
./tunnel -c localhost:5555 www.myservice.com:80 | ||
``` | ||
|
||
## Build | ||
``` | ||
go build | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
module github.com/kelveny/tunnel | ||
|
||
go 1.16 | ||
|
||
require github.com/stretchr/testify v1.7.0 // indirect |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
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/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= | ||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | ||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= | ||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,317 @@ | ||
package main | ||
|
||
import ( | ||
"bytes" | ||
"encoding/binary" | ||
"fmt" | ||
"net" | ||
) | ||
|
||
const ( | ||
PDU_LISTEN_REQUEST = 1 | ||
PDU_LISTEN_RESPONSE = 2 | ||
PDU_TUNNEL_CONNECT_REQUEST = 3 | ||
PDU_TUNNEL_CONNECT_RESPONSE = 4 | ||
PDU_TUNNEL_DATA_INDICATION = 5 | ||
PDU_TUNNEL_DISCONNECT_REQUEST = 6 | ||
PDU_TUNNEL_DISCONNECT_RESPONSE = 7 | ||
) | ||
|
||
type Serializable interface { | ||
GetSerialType() int | ||
GetSerialLength() uint32 | ||
SerializeTo(w *bytes.Buffer) | ||
SerializeFrom(r *bytes.Buffer) | ||
} | ||
|
||
func serializeUInt32To(v uint32, w *bytes.Buffer) { | ||
b := make([]byte, 4) | ||
binary.BigEndian.PutUint32(b, v) | ||
w.Write(b) | ||
} | ||
|
||
func serializeUInt32From(r *bytes.Buffer) uint32 { | ||
b := make([]byte, 4) | ||
r.Read(b) | ||
return binary.BigEndian.Uint32(b) | ||
} | ||
|
||
func getStringSerialLength(s string) uint32 { | ||
return uint32(4 + len([]byte(s))) | ||
} | ||
|
||
func serializeStringTo(s string, w *bytes.Buffer) { | ||
l := uint32(len([]byte(s))) | ||
serializeUInt32To(l, w) | ||
w.Write([]byte(s)) | ||
} | ||
|
||
func serializeStringFrom(r *bytes.Buffer) string { | ||
l := serializeUInt32From(r) | ||
|
||
b := make([]byte, int(l)) | ||
r.Read(b) | ||
return string(b) | ||
} | ||
|
||
func getPduSerialLength(pdu Serializable) uint32 { | ||
return 1 + pdu.GetSerialLength() | ||
} | ||
|
||
func serializePduTo(pdu Serializable, w *bytes.Buffer) { | ||
w.WriteByte(byte(pdu.GetSerialType())) | ||
pdu.SerializeTo(w) | ||
} | ||
|
||
func serializePduFrom(r *bytes.Buffer) Serializable { | ||
t, _ := r.ReadByte() | ||
switch int(t) { | ||
case PDU_LISTEN_REQUEST: | ||
pdu := &ListenRequest{} | ||
pdu.SerializeFrom(r) | ||
return pdu | ||
|
||
case PDU_LISTEN_RESPONSE: | ||
pdu := &ListenResponse{} | ||
pdu.SerializeFrom(r) | ||
return pdu | ||
|
||
case PDU_TUNNEL_CONNECT_REQUEST: | ||
pdu := &TunnelConnectRequest{} | ||
pdu.SerializeFrom(r) | ||
return pdu | ||
|
||
case PDU_TUNNEL_CONNECT_RESPONSE: | ||
pdu := &TunnelConnectResponse{} | ||
pdu.SerializeFrom(r) | ||
return pdu | ||
|
||
case PDU_TUNNEL_DATA_INDICATION: | ||
pdu := &TunnelDataIndication{} | ||
pdu.SerializeFrom(r) | ||
return pdu | ||
|
||
case PDU_TUNNEL_DISCONNECT_REQUEST: | ||
pdu := &TunnelDisconnectRequest{} | ||
pdu.SerializeFrom(r) | ||
return pdu | ||
|
||
case PDU_TUNNEL_DISCONNECT_RESPONSE: | ||
pdu := &TunnelDisconnectResponse{} | ||
pdu.SerializeFrom(r) | ||
return pdu | ||
} | ||
|
||
fmt.Printf("Invalid protocol data\n") | ||
return nil | ||
} | ||
|
||
func sendPdu(conn net.Conn, pdu Serializable) error { | ||
l := getPduSerialLength(pdu) | ||
|
||
b := make([]byte, 4) | ||
binary.BigEndian.PutUint32(b, l) | ||
_, err := conn.Write(b) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
buf := bytes.NewBuffer(nil) | ||
serializePduTo(pdu, buf) | ||
|
||
_, err = conn.Write(buf.Bytes()) | ||
|
||
return err | ||
} | ||
|
||
///////////////////////////////////////////////////////////////////////////// | ||
|
||
type ListenRequest struct { | ||
proxyAddress string | ||
proxyPort int | ||
} | ||
|
||
func (pdu *ListenRequest) GetSerialType() int { | ||
return PDU_LISTEN_REQUEST | ||
} | ||
|
||
func (pdu *ListenRequest) GetSerialLength() uint32 { | ||
return 4 + getStringSerialLength(pdu.proxyAddress) | ||
} | ||
|
||
func (pdu *ListenRequest) SerializeTo(w *bytes.Buffer) { | ||
serializeStringTo(pdu.proxyAddress, w) | ||
serializeUInt32To(uint32(pdu.proxyPort), w) | ||
} | ||
|
||
func (pdu *ListenRequest) SerializeFrom(r *bytes.Buffer) { | ||
pdu.proxyAddress = serializeStringFrom(r) | ||
pdu.proxyPort = int(serializeUInt32From(r)) | ||
} | ||
|
||
///////////////////////////////////////////////////////////////////////////// | ||
|
||
type ListenResponse struct { | ||
proxyAddress string | ||
proxyPort int | ||
tunnelAddress string | ||
tunnelPort int | ||
} | ||
|
||
func (pdu *ListenResponse) GetSerialType() int { | ||
return PDU_LISTEN_RESPONSE | ||
} | ||
|
||
func (pdu *ListenResponse) GetSerialLength() uint32 { | ||
return 8 + getStringSerialLength(pdu.proxyAddress) + getStringSerialLength(pdu.tunnelAddress) | ||
} | ||
|
||
func (pdu *ListenResponse) SerializeTo(w *bytes.Buffer) { | ||
serializeStringTo(pdu.proxyAddress, w) | ||
serializeUInt32To(uint32(pdu.proxyPort), w) | ||
serializeStringTo(pdu.tunnelAddress, w) | ||
serializeUInt32To(uint32(pdu.tunnelPort), w) | ||
} | ||
|
||
func (pdu *ListenResponse) SerializeFrom(r *bytes.Buffer) { | ||
pdu.proxyAddress = serializeStringFrom(r) | ||
pdu.proxyPort = int(serializeUInt32From(r)) | ||
pdu.tunnelAddress = serializeStringFrom(r) | ||
pdu.tunnelPort = int(serializeUInt32From(r)) | ||
} | ||
|
||
///////////////////////////////////////////////////////////////////////////// | ||
|
||
// listener -> proxy | ||
type TunnelConnectRequest struct { | ||
dataConnectionHandle uint32 | ||
clientAddress string | ||
|
||
proxyAddress string | ||
proxyPort int | ||
} | ||
|
||
func (pdu *TunnelConnectRequest) GetSerialType() int { | ||
return PDU_TUNNEL_CONNECT_REQUEST | ||
} | ||
|
||
func (pdu *TunnelConnectRequest) GetSerialLength() uint32 { | ||
return 4 + | ||
getStringSerialLength(pdu.clientAddress) + | ||
getStringSerialLength(pdu.proxyAddress) + | ||
4 | ||
} | ||
|
||
func (pdu *TunnelConnectRequest) SerializeTo(w *bytes.Buffer) { | ||
serializeUInt32To(uint32(pdu.dataConnectionHandle), w) | ||
serializeStringTo(pdu.clientAddress, w) | ||
serializeStringTo(pdu.proxyAddress, w) | ||
serializeUInt32To(uint32(pdu.proxyPort), w) | ||
} | ||
|
||
func (pdu *TunnelConnectRequest) SerializeFrom(r *bytes.Buffer) { | ||
pdu.dataConnectionHandle = Handle(serializeUInt32From(r)) | ||
pdu.clientAddress = serializeStringFrom(r) | ||
pdu.proxyAddress = serializeStringFrom(r) | ||
pdu.proxyPort = int(serializeUInt32From(r)) | ||
} | ||
|
||
///////////////////////////////////////////////////////////////////////////// | ||
|
||
type TunnelConnectResponse struct { | ||
dataConnectionHandle uint32 | ||
proxyConnectionHandle uint32 | ||
} | ||
|
||
func (pdu *TunnelConnectResponse) GetSerialType() int { | ||
return PDU_TUNNEL_CONNECT_RESPONSE | ||
} | ||
|
||
func (pdu *TunnelConnectResponse) GetSerialLength() uint32 { | ||
return 8 | ||
} | ||
|
||
func (pdu *TunnelConnectResponse) SerializeTo(w *bytes.Buffer) { | ||
serializeUInt32To(uint32(pdu.dataConnectionHandle), w) | ||
serializeUInt32To(uint32(pdu.proxyConnectionHandle), w) | ||
} | ||
|
||
func (pdu *TunnelConnectResponse) SerializeFrom(r *bytes.Buffer) { | ||
pdu.dataConnectionHandle = serializeUInt32From(r) | ||
pdu.proxyConnectionHandle = serializeUInt32From(r) | ||
} | ||
|
||
///////////////////////////////////////////////////////////////////////////// | ||
|
||
type TunnelDataIndication struct { | ||
peerConnectionHandle uint32 | ||
data []byte | ||
} | ||
|
||
func (pdu *TunnelDataIndication) GetSerialType() int { | ||
return PDU_TUNNEL_DATA_INDICATION | ||
} | ||
|
||
func (pdu *TunnelDataIndication) GetSerialLength() uint32 { | ||
return uint32(4 + 4 + len(pdu.data)) | ||
} | ||
|
||
func (pdu *TunnelDataIndication) SerializeTo(w *bytes.Buffer) { | ||
serializeUInt32To(uint32(pdu.peerConnectionHandle), w) | ||
serializeUInt32To(uint32(len(pdu.data)), w) | ||
w.Write(pdu.data) | ||
} | ||
|
||
func (pdu *TunnelDataIndication) SerializeFrom(r *bytes.Buffer) { | ||
pdu.peerConnectionHandle = serializeUInt32From(r) | ||
|
||
l := serializeUInt32From(r) | ||
pdu.data = make([]byte, int(l)) | ||
r.Read(pdu.data) | ||
} | ||
|
||
///////////////////////////////////////////////////////////////////////////// | ||
|
||
type TunnelDisconnectRequest struct { | ||
peerConnectionHandle uint32 | ||
} | ||
|
||
func (pdu *TunnelDisconnectRequest) GetSerialType() int { | ||
return PDU_TUNNEL_DISCONNECT_REQUEST | ||
} | ||
|
||
func (pdu *TunnelDisconnectRequest) GetSerialLength() uint32 { | ||
return 4 | ||
} | ||
|
||
func (pdu *TunnelDisconnectRequest) SerializeTo(w *bytes.Buffer) { | ||
serializeUInt32To(uint32(pdu.peerConnectionHandle), w) | ||
} | ||
|
||
func (pdu *TunnelDisconnectRequest) SerializeFrom(r *bytes.Buffer) { | ||
pdu.peerConnectionHandle = serializeUInt32From(r) | ||
} | ||
|
||
///////////////////////////////////////////////////////////////////////////// | ||
|
||
type TunnelDisconnectResponse struct { | ||
peerConnectionHandle uint32 | ||
} | ||
|
||
func (pdu *TunnelDisconnectResponse) GetSerialType() int { | ||
return PDU_TUNNEL_DISCONNECT_RESPONSE | ||
} | ||
|
||
func (pdu *TunnelDisconnectResponse) GetSerialLength() uint32 { | ||
return 4 | ||
} | ||
|
||
func (pdu *TunnelDisconnectResponse) SerializeTo(w *bytes.Buffer) { | ||
serializeUInt32To(uint32(pdu.peerConnectionHandle), w) | ||
} | ||
|
||
func (pdu *TunnelDisconnectResponse) SerializeFrom(r *bytes.Buffer) { | ||
pdu.peerConnectionHandle = serializeUInt32From(r) | ||
} | ||
|
||
///////////////////////////////////////////////////////////////////////////// |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package main | ||
|
||
import ( | ||
"bytes" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestSerializePdu(t *testing.T) { | ||
assert := require.New(t) | ||
|
||
pdu := &ListenRequest{ | ||
proxyAddress: "www.google.com", | ||
proxyPort: 443, | ||
} | ||
|
||
b := bytes.NewBuffer(nil) | ||
serializePduTo(pdu, b) | ||
|
||
pduClone := serializePduFrom(bytes.NewBuffer(b.Bytes())) | ||
assert.True(pduClone != nil) | ||
assert.True(pduClone.(*ListenRequest).proxyAddress == "www.google.com") | ||
assert.True(pduClone.(*ListenRequest).proxyPort == 443) | ||
} |
Oops, something went wrong.