WIP tunnel states, re-dial when not required needs debugging.

This commit is contained in:
Russ Magee 2018-11-07 19:35:32 -08:00
parent dd37922fb2
commit 3b35751e2e
4 changed files with 161 additions and 116 deletions

View File

@ -56,7 +56,6 @@ const (
// Tunnel setup/control/status // Tunnel setup/control/status
CSOTunSetup // client -> server tunnel setup request (dstport) CSOTunSetup // client -> server tunnel setup request (dstport)
CSOTunInUse // server -> client: tunnel rport is in use
CSOTunSetupAck // server -> client tunnel setup ack CSOTunSetupAck // server -> client tunnel setup ack
CSOTunAccept // client -> server: tunnel client got an Accept() CSOTunAccept // client -> server: tunnel client got an Accept()
// (Do we need a CSOTunAcceptAck server->client?) // (Do we need a CSOTunAcceptAck server->client?)
@ -66,23 +65,26 @@ const (
CSOTunHangup // client -> server: tunnel lport hung up CSOTunHangup // client -> server: tunnel lport hung up
) )
// TunEndpoint.tunCtl control values // TunEndpoint.tunCtl control values - used to control workers for client or server tunnels
// depending on the code
const ( const (
TunCtl_AcceptedClient = 'a' // client side has accept()ed a conn TunCtl_Client_Listen = 'a'
TunCtl_Server_Dial = 'd' // server has dialled OK, client side can accept() conns
// [CSOTunAccept] // [CSOTunAccept]
// status: client listen() worker accepted conn on lport // status: client listen() worker accepted conn on lport
// action:server side should dial() rport on client's behalf // action:server side should dial() rport on client's behalf
TunCtl_LostClient = 'h' // client side has hung up TunCtl_Info_Hangup = 'h' // client side has hung up
// [CSOTunHangup] // [CSOTunHangup]
// status: client side conn hung up from lport // status: client side conn hung up from lport
// action:server side should hang up on rport, on client's behalf // action:server side should hang up on rport, on client's behalf
TunCtl_ConnRefused = 'r' // server side couldn't complete tunnel TunCtl_Info_ConnRefused = 'r' // server side couldn't complete tunnel
// [CSOTunRefused] // [CSOTunRefused]
// status:server side could not dial() remote side // status:server side could not dial() remote side
TunCtl_LostConn = 'l' // server side disconnected TunCtl_Info_LostConn = 'x' // server side disconnected
// [CSOTunDisconn] // [CSOTunDisconn]
// status:server side lost connection to rport // status:server side lost connection to rport
// action:client should disconnect accepted lport connection // action:client should disconnect accepted lport connection

View File

@ -85,7 +85,7 @@ type (
Cols uint16 Cols uint16
chaff ChaffConfig chaff ChaffConfig
tuns map[uint16]*TunEndpoint tuns *map[uint16](*TunEndpoint)
closeStat *CSOType // close status (CSOExitStatus) closeStat *CSOType // close status (CSOExitStatus)
r cipher.Stream //read cipherStream r cipher.Stream //read cipherStream
@ -208,6 +208,8 @@ func _new(kexAlg KEXAlg, conn *net.Conn) (hc *Conn, e error) {
closeStat: new(CSOType), closeStat: new(CSOType),
WinCh: make(chan WinSize, 1), WinCh: make(chan WinSize, 1),
dBuf: new(bytes.Buffer)} dBuf: new(bytes.Buffer)}
tempMap := make(map[uint16]*TunEndpoint)
hc.tuns = &tempMap
*hc.closeStat = CSEStillOpen // open or prematurely-closed status *hc.closeStat = CSEStillOpen // open or prematurely-closed status
@ -814,39 +816,58 @@ func (hc Conn) Read(b []byte) (n int, err error) {
// server side tunnel setup in response to client // server side tunnel setup in response to client
lport := binary.BigEndian.Uint16(payloadBytes[0:2]) lport := binary.BigEndian.Uint16(payloadBytes[0:2])
rport := binary.BigEndian.Uint16(payloadBytes[2:4]) rport := binary.BigEndian.Uint16(payloadBytes[2:4])
logger.LogDebug(fmt.Sprintf("Read(): Tunnel setup [%d:%d]", lport, rport)) logger.LogDebug(fmt.Sprintf("mapkey is %d", rport))
hc.StartServerTunnel(lport, rport) if _, ok := (*hc.tuns)[rport]; !ok {
hc.tuns[rport].Ctl <- 'a' // Dial() rport // tunnel first-time open
logger.LogDebug(fmt.Sprintf("[Server] Got Initial CSOTunSetup [%d:%d]", lport, rport))
hc.StartServerTunnel(lport, rport)
} else {
logger.LogDebug(fmt.Sprintf("[Server] Got CSOTunSetup [%d:%d]", lport, rport))
}
(*hc.tuns)[rport].Ctl <- 'd' // Dial() rport
} else if ctrlStatOp == CSOTunSetupAck { } else if ctrlStatOp == CSOTunSetupAck {
lport := binary.BigEndian.Uint16(payloadBytes[0:2]) lport := binary.BigEndian.Uint16(payloadBytes[0:2])
rport := binary.BigEndian.Uint16(payloadBytes[2:4]) rport := binary.BigEndian.Uint16(payloadBytes[2:4])
logger.LogDebug(fmt.Sprintf("Read(): Tunnel setup ack [%d:%d]", lport, rport)) logger.LogDebug(fmt.Sprintf("mapkey is %d\n", rport))
hc.dBuf.Write(payloadBytes) if _, ok := (*hc.tuns)[rport]; !ok {
// tunnel first-time open
logger.LogDebug(fmt.Sprintf("[Client] Got Initial CSOTunSetupAck [%d:%d]", lport, rport))
hc.StartClientTunnel(lport, rport)
} else {
logger.LogDebug(fmt.Sprintf("[Client] Got CSOTunSetupAck [%d:%d]", lport, rport))
}
(*hc.tuns)[rport].Ctl <- 'a' // Listen() for lport connection
} else if ctrlStatOp == CSOTunRefused { } else if ctrlStatOp == CSOTunRefused {
// client side has been told nothing is listening on rport // client side receiving CSOTunRefused means the remote side
// could not dial() rport. So we cannot yet listen()
// for client-side on lport.
lport := binary.BigEndian.Uint16(payloadBytes[0:2]) lport := binary.BigEndian.Uint16(payloadBytes[0:2])
rport := binary.BigEndian.Uint16(payloadBytes[2:4]) rport := binary.BigEndian.Uint16(payloadBytes[2:4])
logger.LogDebug(fmt.Sprintf("Read(): Tunnel refused [%d:%d]", lport, rport)) logger.LogDebug(fmt.Sprintf("mapkey is %d\n", rport))
hc.dBuf.Write(payloadBytes) logger.LogDebug(fmt.Sprintf("[Client] Got CSOTunRefused [%d:%d]", lport, rport))
(*hc.tuns)[rport].Ctl <- 'r' // client should NOT Listen()
} else if ctrlStatOp == CSOTunDisconn { } else if ctrlStatOp == CSOTunDisconn {
// server side's rport has disconnected (server lost) // server side's rport has disconnected (server lost)
lport := binary.BigEndian.Uint16(payloadBytes[0:2]) lport := binary.BigEndian.Uint16(payloadBytes[0:2])
rport := binary.BigEndian.Uint16(payloadBytes[2:4]) rport := binary.BigEndian.Uint16(payloadBytes[2:4])
logger.LogDebug(fmt.Sprintf("Read(): Tunnel server disconnected [%d:%d]", lport, rport)) logger.LogDebug(fmt.Sprintf("mapkey is %d\n", rport))
hc.dBuf.Write(payloadBytes) logger.LogDebug(fmt.Sprintf("[Client] Got CSOTunDisconn [%d:%d]", lport, rport))
(*hc.tuns)[rport].Ctl <- 'x' // client should hangup on current lport conn
} else if ctrlStatOp == CSOTunHangup { } else if ctrlStatOp == CSOTunHangup {
// client side's lport has hung up // client side's lport has hung up
lport := binary.BigEndian.Uint16(payloadBytes[0:2]) lport := binary.BigEndian.Uint16(payloadBytes[0:2])
rport := binary.BigEndian.Uint16(payloadBytes[2:4]) rport := binary.BigEndian.Uint16(payloadBytes[2:4])
logger.LogDebug(fmt.Sprintf("Read(): Tunnel client hung up [%d:%d]", lport, rport)) logger.LogDebug(fmt.Sprintf("mapkey is %d\n", rport))
hc.dBuf.Write(payloadBytes) logger.LogDebug(fmt.Sprintf("[Server] Got CSOTunHangup [%d:%d]", lport, rport))
(*hc.tuns)[rport].Ctl <- 'h' // server should hang up on currently-dialled rport
} else if ctrlStatOp == CSOTunData { } else if ctrlStatOp == CSOTunData {
lport := binary.BigEndian.Uint16(payloadBytes[0:2]) lport := binary.BigEndian.Uint16(payloadBytes[0:2])
rport := binary.BigEndian.Uint16(payloadBytes[2:4]) rport := binary.BigEndian.Uint16(payloadBytes[2:4])
logger.LogDebug(fmt.Sprintf("mapkey is %d\n", rport))
//fmt.Printf("[Got CSOTunData: [lport %d:rport %d] data:%v\n", lport, rport, payloadBytes[4:]) //fmt.Printf("[Got CSOTunData: [lport %d:rport %d] data:%v\n", lport, rport, payloadBytes[4:])
if hc.tuns[rport] != nil { if _, ok := (*hc.tuns)[rport]; ok {
logger.LogDebug(fmt.Sprintf("[Writing data to rport [%d:%d]", lport, rport)) logger.LogDebug(fmt.Sprintf("[Writing data to rport [%d:%d]", lport, rport))
hc.tuns[rport].Data <- payloadBytes[4:] (*hc.tuns)[rport].Data <- payloadBytes[4:]
} else { } else {
logger.LogDebug(fmt.Sprintf("[Attempt to write data to closed tun [%d:%d]", lport, rport)) logger.LogDebug(fmt.Sprintf("[Attempt to write data to closed tun [%d:%d]", lport, rport))
} }

View File

@ -48,118 +48,147 @@ type (
) )
func (hc *Conn) InitTunEndpoint(lp uint16, p string /* net.Addr */, rp uint16) { func (hc *Conn) InitTunEndpoint(lp uint16, p string /* net.Addr */, rp uint16) {
if hc.tuns == nil { if (*hc.tuns) == nil {
hc.tuns = make(map[uint16]*TunEndpoint) (*hc.tuns) = make(map[uint16]*TunEndpoint)
} }
if hc.tuns[rp] == nil { if (*hc.tuns)[rp] == nil {
var addrs []net.Addr var addrs []net.Addr
if p == "" { if p == "" {
addrs, _ = net.InterfaceAddrs() addrs, _ = net.InterfaceAddrs()
p = addrs[0].String() p = addrs[0].String()
} }
hc.tuns[rp] = &TunEndpoint{ /*Status: CSOTunSetup,*/ Peer: p, (*hc.tuns)[rp] = &TunEndpoint{ /*Status: CSOTunSetup,*/ Peer: p,
Lport: lp, Rport: rp, Data: make(chan []byte, 1), Lport: lp, Rport: rp, Data: make(chan []byte, 1),
Ctl: make(chan rune, 1)} Ctl: make(chan rune, 1)}
logger.LogDebug(fmt.Sprintf("InitTunEndpoint [%d:%s:%d]\n", lp, p, rp)) logger.LogDebug(fmt.Sprintf("InitTunEndpoint [%d:%s:%d]", lp, p, rp))
} else {
logger.LogDebug(fmt.Sprintf("InitTunEndpoint [reusing] [%d:%s:%d]", (*hc.tuns)[rp].Lport, (*hc.tuns)[rp].Peer, (*hc.tuns)[rp].Rport))
} }
return return
} }
func (hc *Conn) StartClientTunnel(lport, rport uint16) { func (hc *Conn) StartClientTunnel(lport, rport uint16) {
hc.InitTunEndpoint(lport, "", rport) hc.InitTunEndpoint(lport, "", rport)
t := hc.tuns[rport] // for convenience t := (*hc.tuns)[rport] // for convenience
var l HKExListener
go func() { go func() {
logger.LogDebug(fmt.Sprintf("Listening for client tunnel port %d", lport)) weAreListening := false
l, e := net.Listen("tcp", fmt.Sprintf(":%d", lport)) for cmd := range t.Ctl {
if e != nil { logger.LogDebug(fmt.Sprintf("[ClientTun] Listening for client tunnel port %d", lport))
logger.LogDebug(fmt.Sprintf("[Could not get lport %d! (%s)", lport, e))
} else {
defer l.Close()
for {
c, e := l.Accept()
defer func() {
c.Close()
}()
if cmd == 'a' && !weAreListening {
l, e := net.Listen("tcp", fmt.Sprintf(":%d", lport))
if e != nil { if e != nil {
logger.LogDebug(fmt.Sprintf("Accept() got error(%v), hanging up.", e)) logger.LogDebug(fmt.Sprintf("[ClientTun] Could not get lport %d! (%s)", lport, e))
break
} else { } else {
logger.LogDebug(fmt.Sprintln("Accepted tunnel client")) weAreListening = true
for {
// outside client -> tunnel lport c, e := l.Accept()
go func() {
var tunDst bytes.Buffer var tunDst bytes.Buffer
// ask server to dial() its side, rport
binary.Write(&tunDst, binary.BigEndian, lport) binary.Write(&tunDst, binary.BigEndian, lport)
binary.Write(&tunDst, binary.BigEndian, rport) binary.Write(&tunDst, binary.BigEndian, rport)
for { hc.WritePacket(tunDst.Bytes(), CSOTunSetup)
rBuf := make([]byte, 1024)
//Read data from c, encrypt/write via hc to client(lport) if e != nil {
n, e := c.Read(rBuf) logger.LogDebug(fmt.Sprintf("[ClientTun] Accept() got error(%v), hanging up.", e))
if e != nil { break
if e == io.EOF { } else {
logger.LogDebug(fmt.Sprintf("lport Disconnected: shutting down tunnel [%d:%d]", lport, rport)) logger.LogDebug(fmt.Sprintf("[ClientTun] Accepted tunnel client %v", t))
} else {
logger.LogDebug(fmt.Sprintf("Read error from lport of tun [%d:%d]\n%s", lport, rport, e)) // outside client -> tunnel lport
go func() {
defer func() {
c.Close()
}()
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("[ClientTun] lport Disconnected: shutting down tunnel %v", t))
} else {
logger.LogDebug(fmt.Sprintf("[ClientTun] Read error from lport of tun %v\n%s", t, e))
}
hc.WritePacket(tunDst.Bytes(), CSOTunHangup)
break
}
if n > 0 {
rBuf = append(tunDst.Bytes(), rBuf[:n]...)
_, de := hc.WritePacket(rBuf[:n+4], CSOTunData)
if de != nil {
logger.LogDebug(fmt.Sprintf("[ClientTun] Error writing to tunnel %v, %s]\n", t, de))
break
}
}
} }
hc.WritePacket(tunDst.Bytes(), CSOTunHangup) }()
break
}
if n > 0 {
rBuf = append(tunDst.Bytes(), rBuf[:n]...)
hc.WritePacket(rBuf[:n+4], CSOTunData)
}
}
}()
// tunnel lport -> outside client (c) // tunnel lport -> outside client (c)
go func() { go func() {
defer func() { defer func() {
c.Close() c.Close()
}() }()
for { for {
bytes, ok := <-t.Data bytes, ok := <-t.Data
if ok { if ok {
c.Write(bytes) _, e := c.Write(bytes)
} else { if e != nil {
logger.LogDebug(fmt.Sprintf("[Channel closed?]\n")) logger.LogDebug(fmt.Sprintf("[ClientTun] lport conn closed"))
break break
} }
} } else {
}() logger.LogDebug(fmt.Sprintf("[ClientTun] Channel closed?"))
break
}
}
}()
} } // end Accept() worker block
} // end for-accept
} // end Listen() block
} else if cmd == 'r' {
logger.LogDebug(fmt.Sprintf("[ClientTun] Server replied TunRefused %v\n", t))
} else if cmd == 'x' {
logger.LogDebug(fmt.Sprintf("[ClientTun] Server replied TunDisconn, closing lport %v\n", t))
l.Close()
weAreListening = false
} }
} } // end t.Ctl for
}() }()
} }
func (hc *Conn) StartServerTunnel(lport, rport uint16) { func (hc *Conn) StartServerTunnel(lport, rport uint16) {
hc.InitTunEndpoint(lport, "", rport) hc.InitTunEndpoint(lport, "", rport)
t := hc.tuns[rport] // for convenience t := (*hc.tuns)[rport] // for convenience
var err error var err error
go func() { go func() {
weAreDialled := false
for cmd := range t.Ctl { for cmd := range t.Ctl {
var c net.Conn var c net.Conn
if cmd == 'a' { if cmd == 'd' && !weAreDialled {
logger.LogDebug("Server dialling...") logger.LogDebug("[ServerTun] dialling...")
c, err = net.Dial("tcp", fmt.Sprintf(":%d", rport)) c, err = net.Dial("tcp", fmt.Sprintf(":%d", rport))
if err != nil { if err != nil {
logger.LogDebug(fmt.Sprintf("Nothing is serving at rport :%d!", rport)) logger.LogDebug(fmt.Sprintf("[ServerTun] Dial() error for tun %v: %s", t, err))
var resp bytes.Buffer var resp bytes.Buffer
binary.Write(&resp, binary.BigEndian /*lport*/, uint16(0)) binary.Write(&resp, binary.BigEndian /*lport*/, uint16(0))
binary.Write(&resp, binary.BigEndian, rport) binary.Write(&resp, binary.BigEndian, rport)
hc.WritePacket(resp.Bytes(), CSOTunRefused) hc.WritePacket(resp.Bytes(), CSOTunRefused)
} else { } else {
logger.LogDebug(fmt.Sprintf("[Tunnel Opened - %d:%s:%d]", lport, t.Peer, rport)) logger.LogDebug(fmt.Sprintf("[ServerTun] Tunnel Opened - %v", t))
weAreDialled = true
var resp bytes.Buffer var resp bytes.Buffer
binary.Write(&resp, binary.BigEndian, lport) binary.Write(&resp, binary.BigEndian, lport)
binary.Write(&resp, binary.BigEndian, rport) binary.Write(&resp, binary.BigEndian, rport)
logger.LogDebug(fmt.Sprintf("[Writing CSOTunSetupAck[%d:%d]", lport, rport)) logger.LogDebug(fmt.Sprintf("[ServerTun] Writing CSOTunSetupAck %v", t))
hc.WritePacket(resp.Bytes(), CSOTunSetupAck) hc.WritePacket(resp.Bytes(), CSOTunSetupAck)
// //
@ -167,7 +196,9 @@ func (hc *Conn) StartServerTunnel(lport, rport uint16) {
// //
go func() { go func() {
defer func() { defer func() {
logger.LogDebug("[ServerTun] (deferred hangup workerA)")
c.Close() c.Close()
weAreDialled = false
}() }()
var tunDst bytes.Buffer var tunDst bytes.Buffer
@ -179,15 +210,15 @@ func (hc *Conn) StartServerTunnel(lport, rport uint16) {
n, e := c.Read(rBuf) n, e := c.Read(rBuf)
if e != nil { if e != nil {
if e == io.EOF { if e == io.EOF {
logger.LogDebug(fmt.Sprintf("rport Disconnected: shutting down tunnel %v\n", t)) logger.LogDebug(fmt.Sprintf("[ServerTun] rport Disconnected: shutting down tunnel %v", t))
} else { } else {
logger.LogDebug(fmt.Sprintf("Read error from rport of tun %v\n%s", t, e)) logger.LogDebug(fmt.Sprintf("[ServerTun] Read error from rport of tun %v: %s", t, e))
} }
var resp bytes.Buffer var resp bytes.Buffer
binary.Write(&resp, binary.BigEndian, lport) binary.Write(&resp, binary.BigEndian, lport)
binary.Write(&resp, binary.BigEndian, rport) binary.Write(&resp, binary.BigEndian, rport)
hc.WritePacket(resp.Bytes(), CSOTunDisconn) hc.WritePacket(resp.Bytes(), CSOTunDisconn)
logger.LogDebug(fmt.Sprintf("Closing server rport %d net.Dial()", t.Rport)) logger.LogDebug(fmt.Sprintf("[ServerTun] Closing server rport %d net.Dial()", t.Rport))
break break
} }
if n > 0 { if n > 0 {
@ -200,22 +231,32 @@ func (hc *Conn) StartServerTunnel(lport, rport uint16) {
// worker to read data from client (already decrypted) & fwd to rport // worker to read data from client (already decrypted) & fwd to rport
go func() { go func() {
defer func() { defer func() {
logger.LogDebug("[ServerTun] (deferred hangup workerB)")
c.Close() c.Close()
weAreDialled = false
}() }()
for { for {
rData, ok := <-t.Data rData, ok := <-t.Data
if ok { if ok {
c.Write(rData) _, e := c.Write(rData)
if e != nil {
logger.LogDebug(fmt.Sprintf("[ServerTun] ERROR writing to rport conn"))
break
}
} else { } else {
logger.LogDebug("[ERROR reading from hc.tuns[] channel - closed?]") logger.LogDebug("[ServerTun] ERROR reading from hc.tuns[] channel - closed?")
break break
} }
} }
}() }()
} }
} // TODO: elseifs for other state transtions driven by client } else if cmd == 'h' {
} // client side has hung up
}() // t.Ctl read loop logger.LogDebug(fmt.Sprintf("[ServerTun] Client hung up: hanging up on rport %v", t))
logger.LogDebug("[ServerTunnel() exiting t.Ctl read loop - channel closed??]") weAreDialled = false
}
} // t.Ctl read loop
logger.LogDebug("[ServerTun] Tunnel exiting t.Ctl read loop - channel closed??")
}()
} }

View File

@ -349,26 +349,7 @@ func reqTunnel(hc *hkexnet.Conn, lp uint16, p string /*net.Addr*/, rp uint16) {
fmt.Printf("bTmp:%x\n", bTmp.Bytes()) fmt.Printf("bTmp:%x\n", bTmp.Bytes())
logger.LogDebug(fmt.Sprintln("[Client sending CSOTunSetup]")) logger.LogDebug(fmt.Sprintln("[Client sending CSOTunSetup]"))
hc.WritePacket(bTmp.Bytes(), hkexnet.CSOTunSetup) hc.WritePacket(bTmp.Bytes(), hkexnet.CSOTunSetup)
// Server should reply immediately with CSOTunSetupAck[lport:rport]
// hkexnet.Read() on server side handles server side tun setup.
resp := make([]byte, 4)
var lpResp, rpResp uint16
n, e := io.ReadFull(hc, resp)
if n < 4 || e != nil {
logger.LogErr(fmt.Sprintf("[Client tun response len %d, %s\n", n, e))
} else {
lpResp = binary.BigEndian.Uint16(resp[0:2])
rpResp = binary.BigEndian.Uint16(resp[2:4])
}
if lpResp == lp && rpResp == rp {
logger.LogDebug("[Client got tun setup ack OK]")
hc.StartClientTunnel(lp, rp)
} else {
logger.LogDebug(fmt.Sprintf("[Client tun response ports [%d:%d]\n", lpResp, rpResp))
logger.LogDebug(fmt.Sprintln("[Client tun setup FAILED]"))
}
return return
} }