xs/hkexnet/hkextun.go

244 lines
6.6 KiB
Go
Raw Normal View History

// hkextun.go - Tunnel setup using an established hkexnet.Conn
// Copyright (c) 2017-2018 Russell Magee
// Licensed under the terms of the MIT license (see LICENSE.mit in this
// distribution)
//
// golang implementation by Russ Magee (rmagee_at_gmail.com)
package hkexnet
import (
"bytes"
"encoding/binary"
"fmt"
"io"
"net"
"blitter.com/go/hkexsh/logger"
)
type (
// Tunnels
// --
// 1. client is given (lport, remhost, rport) by local user
// 2. client sends [CSOTunReq:rport] to server
// client=> [CSOTunReq:rport] =>remhost
//
// remhost starts worker to receive/send data using rport
// remhost replies to client with rport to acknowledge tun is ready
// client<= [CSOTunAck:rport] <=remhost
// ... or if rhost rport refuses connection, sends
// [CSOTunRefused:rport]
//
// client starts worker to receive/send data using lport
// ... client disconnects: sends remhost [CSOTunClose:rport]
// ... or server disconnects: sends client [CSOTunClose:lport]
// server at any time sends [CSOTunRefused:rport] if daemon died
// --
// TunEndpoint [securePort:peer:dataPort]
TunEndpoint struct {
Rport uint16 // Names are from client's perspective
Lport uint16 // ... ie., RPort is on server, LPort is on client
Peer string //net.Addr
//Status byte //Last status of tunnel (eg., CSOTunSetupAck)
Ctl chan<- rune //See TunCtl_* consts
Data chan []byte
}
//TunPacket struct {
// n uint32
// data []byte
//}
)
func (hc *Conn) InitTunEndpoint(lp uint16, p string /* net.Addr */, rp uint16) {
if hc.tuns == nil {
hc.tuns = make(map[uint16]*TunEndpoint)
}
if hc.tuns[rp] == nil {
var addrs []net.Addr
if p == "" {
addrs, _ = net.InterfaceAddrs()
p = addrs[0].String()
}
hc.tuns[rp] = &TunEndpoint{/*Status: CSOTunSetup,*/ Peer: p,
Lport: lp, Rport: rp, Data: make(chan[]byte, 32), Ctl: make(chan<- rune)}
logger.LogDebug(fmt.Sprintf("InitTunEndpoint [%d:%s:%d]\n", lp, p, rp))
}
return
}
//func (hc *Conn) GetTunStatus(rp uint16) byte {
// return hc.tuns[rp].Status
//}
func (hc *Conn) StartClientTunnel(lport, rport uint16) {
hc.InitTunEndpoint(lport, "", rport)
t := hc.tuns[rport] // for convenience
go func() {
logger.LogDebug(fmt.Sprintf("Listening for client tunnel port %d", lport))
l, e := net.Listen("tcp", fmt.Sprintf(":%d", lport))
if e != nil {
logger.LogDebug(fmt.Sprintf("[Could not get lport %d! (%s)", lport, e))
} else {
defer l.Close()
for {
c, e := l.Accept()
defer func() {
//if hc.tuns[rport] != nil {
// close(hc.tuns[rport])
// hc.tuns[rport] = nil
//}
c.Close()
}()
if e != nil {
logger.LogDebug(fmt.Sprintf("Accept() got error(%v), hanging up.", e))
break
//log.Fatal(err)
} else {
logger.LogDebug(fmt.Sprintln("Accepted tunnel client"))
// outside client -> tunnel lport
go func() {
var tunDst bytes.Buffer
binary.Write(&tunDst, binary.BigEndian, lport)
binary.Write(&tunDst, binary.BigEndian, rport)
for {
rBuf := make([]byte, 1024)
//Read data from c, encrypt/write via hc to client(lport)
n, e := c.Read(rBuf)
if e != nil {
if e == io.EOF {
logger.LogDebug(fmt.Sprintf("lport Disconnected: shutting down tunnel [%d:%d]", lport, rport))
} else {
logger.LogDebug(fmt.Sprintf("Read error from lport of tun [%d:%d]\n%s", lport, rport, e))
}
hc.WritePacket(tunDst.Bytes(), CSOTunHangup)
break
}
if n > 0 {
rBuf = append(tunDst.Bytes(), rBuf[:n]...)
//logger.LogDebug(fmt.Sprintf("Got lport data:%v", tunDst.Bytes()))
hc.WritePacket(rBuf[:n+4], CSOTunData)
}
}
}()
// tunnel lport -> outside client (c)
go func() {
defer func() {
//if hc.tuns[rport] != nil {
// close(hc.tuns[rport])
// hc.tuns[rport] = nil
//}
c.Close()
}()
for {
//fmt.Printf("Reading from client hc.tuns[%d]\n", lport)
bytes, ok := <-t.Data
if ok {
//fmt.Printf("[Got this through tunnel:%v]\n", bytes)
c.Write(bytes)
} else {
logger.LogDebug(fmt.Sprintf("[Channel closed?]\n"))
//break
}
}
}()
}
}
}
}()
}
func (hc *Conn) StartServerTunnel(lport, rport uint16) {
hc.InitTunEndpoint(lport, "", rport)
t := hc.tuns[rport] // for convenience
logger.LogDebug("Server dialling...")
c, err := net.Dial("tcp", fmt.Sprintf(":%d", rport))
if err != nil {
logger.LogDebug(fmt.Sprintf("Nothing is serving at rport :%d!", rport))
var resp bytes.Buffer
binary.Write(&resp, binary.BigEndian, lport)
binary.Write(&resp, binary.BigEndian, rport)
hc.WritePacket(resp.Bytes(), CSOTunRefused)
} else {
logger.LogDebug(fmt.Sprintf("[Tunnel Opened - %d:%s:%d]", lport, t.Peer, rport))
var resp bytes.Buffer
binary.Write(&resp, binary.BigEndian, lport)
binary.Write(&resp, binary.BigEndian, rport)
logger.LogDebug(fmt.Sprintf("[Writing CSOTunSetupAck[%d:%d]", lport, rport))
hc.WritePacket(resp.Bytes(), CSOTunSetupAck)
//
// worker to read data from the rport (to encrypt & send to client)
//
go func() {
defer func() {
//if hc.tuns[rport] != nil {
// close(hc.tuns[rport])
// hc.tuns[rport] = nil
//}
c.Close()
}()
var tunDst bytes.Buffer
binary.Write(&tunDst, binary.BigEndian, t.Lport)
binary.Write(&tunDst, binary.BigEndian, t.Rport)
for {
rBuf := make([]byte, 1024)
// Read data from c, encrypt/write via hc to client(lport)
n, e := c.Read(rBuf)
if e != nil {
if e == io.EOF {
logger.LogDebug(fmt.Sprintf("rport Disconnected: shutting down tunnel %v\n", t))
} else {
logger.LogDebug(fmt.Sprintf("Read error from rport of tun %v\n%s", t, e))
}
var resp bytes.Buffer
binary.Write(&resp, binary.BigEndian, lport)
binary.Write(&resp, binary.BigEndian, rport)
hc.WritePacket(resp.Bytes(), CSOTunDisconn)
logger.LogDebug(fmt.Sprintf("Closing server rport %d net.Dial()", t.Rport))
break
}
if n > 0 {
rBuf = append(tunDst.Bytes(), rBuf[:n]...)
hc.WritePacket(rBuf[:n+4], CSOTunData)
}
}
}()
// worker to read data from client (already decrypted) & fwd to rport
go func() {
defer func() {
//if hc.tuns[rport] != nil {
//close(hc.tuns[rport])
//hc.tuns[rport] = nil
//}
c.Close()
}()
for {
rData, ok := <-t.Data
if ok {
//logger.LogDebug(fmt.Sprintf("Got client data:%v", rData))
c.Write(rData)
} else {
logger.LogDebug("!!! ERROR reading from hc.tuns[] channel !!!")
break
}
}
}()
}
}