Completed net.Conn interface implementation for hkex.Conn; some tests of Op protocol in server

This commit is contained in:
Russ Magee 2018-01-17 16:39:01 -08:00
parent e09f052f45
commit 9fb9d073ab
2 changed files with 150 additions and 28 deletions

View file

@ -4,11 +4,82 @@ import (
"flag" "flag"
"fmt" "fmt"
"log" "log"
"os/exec"
"os/user"
"strings"
"syscall"
"time" "time"
hkex "blitter.com/herradurakex" hkex "blitter.com/herradurakex"
) )
const (
OpR = 'r' // read(file) (binary mode)
OpW = 'w' // (over)write
OpA = 'a' // append
OpRm = 'd' // rm
OpRmD = 'D' // rmdir (rm -rf)
OpM = 'm' // mkdir (-p)
OpN = 'n' // re(n)ame (mv)
OpCm = 'c' // chmod
OpCo = 'C' // chown
OpX = 'x' // exec
)
type Op uint8
type cmdRunner struct {
op Op
who string
arg string
authCookie string
CloseHandler func(*cmdRunner)
status int
}
func testCloseHandler(r *cmdRunner) {
fmt.Println("[testCloseHandler()]")
r.arg = "/usr/bin/touch " + r.arg
cmd(r)
}
func cmd(r *cmdRunner) {
switch r.op {
case OpR:
//Clean up r.cmd beforehand
r.arg = strings.TrimSpace(r.arg)
fmt.Printf("[cmd was:'%s']\n", r.arg)
runCmdAs(r.who, r.arg)
fmt.Println(r.arg)
break
default:
fmt.Printf("[cmd %d ignored:%d]\n", int(r.op))
break
}
}
// Run a command (via os.exec) as a specific user
func runCmdAs(who string, cmd string) (err error) {
u, _ := user.Lookup(who)
var uid, gid uint32
fmt.Sscanf(u.Uid, "%d", &uid)
fmt.Sscanf(u.Gid, "%d", &gid)
//fmt.Println("uid:", uid, "gid:", gid)
args := strings.Split(cmd, " ")
arg0 := args[0]
args = args[1:]
c := exec.Command(arg0, args...)
c.SysProcAttr = &syscall.SysProcAttr{}
c.SysProcAttr.Credential = &syscall.Credential{Uid: uid, Gid: gid}
err = c.Run()
if err != nil {
log.Printf("Command finished with error: %v", err)
log.Printf("[%s]\n", cmd)
}
return
}
// Demo of a simple server that listens and spawns goroutines for each // Demo of a simple server that listens and spawns goroutines for each
// connecting client. Note this code is identical to standard tcp // connecting client. Note this code is identical to standard tcp
// server code, save for declaring 'hkex' rather than 'net' // server code, save for declaring 'hkex' rather than 'net'
@ -41,10 +112,10 @@ func main() {
// The loop then returns to accepting, so that // The loop then returns to accepting, so that
// multiple connections may be served concurrently. // multiple connections may be served concurrently.
go func(c hkex.Conn) (e error) { go func(c hkex.Conn) (e error) {
defer c.Close()
ch := make(chan []byte) ch := make(chan []byte)
chN := 0 chN := 0
eCh := make(chan error) eCh := make(chan error)
var connOp *byte = nil
// Start a goroutine to read from our net connection // Start a goroutine to read from our net connection
go func(ch chan []byte, eCh chan error) { go func(ch chan []byte, eCh chan error) {
@ -57,23 +128,14 @@ func main() {
eCh <- err eCh <- err
return return
} }
if connOp == nil {
// Initial xmit - get op byte
// (TODO: determine valid ops
// for now 'e' (echo), 'i' (interactive), 'x' (exec), ... ?)
connOp = new(byte)
*connOp = data[0]
data = data[1:]
chN -= 1
fmt.Printf("[* connOp '%c']\n", *connOp)
}
// send data if we read some. // send data if we read some.
ch <- data[0:chN] ch <- data[0:chN]
} }
}(ch, eCh) }(ch, eCh)
ticker := time.Tick(time.Second / 100) ticker := time.Tick(time.Second / 100)
var r cmdRunner
var connOp *byte = nil
Term: Term:
// continuously read from the connection // continuously read from the connection
for { for {
@ -82,12 +144,32 @@ func main() {
case data := <-ch: case data := <-ch:
// Do something with the data // Do something with the data
fmt.Printf("Client sent %+v\n", data[0:chN]) fmt.Printf("Client sent %+v\n", data[0:chN])
if connOp == nil {
// Initial xmit - get op byte
// (TODO: determine valid ops
// for now 'e' (echo), 'i' (interactive), 'x' (exec), ... ?)
connOp = new(byte)
*connOp = data[0]
data = data[1:chN]
chN -= 1
fmt.Printf("[* connOp '%c']\n", *connOp)
// The CloseHandler typically handles the
// accumulated command data
r = cmdRunner{op: Op(*connOp),
who: "larissa", arg: string(data),
authCookie: "c00ki3",
CloseHandler: testCloseHandler,
status: 0}
}
//fmt.Printf("Client sent %s\n", string(data)) //fmt.Printf("Client sent %s\n", string(data))
// This case means we got an error and the goroutine has finished // This case means we got an error and the goroutine has finished
case err := <-eCh: case err := <-eCh:
// handle our error then exit for loop // handle our error then exit for loop
if err.Error() == "EOF" { if err.Error() == "EOF" {
fmt.Printf("[Client disconnected]\n") fmt.Printf("[Client disconnected]\n")
r.CloseHandler(&r)
} else { } else {
fmt.Printf("Error reading client data! (%+v)\n", err) fmt.Printf("Error reading client data! (%+v)\n", err)
} }
@ -100,7 +182,7 @@ func main() {
} }
} }
// Shut down the connection. // Shut down the connection.
c.Close() //c.Close()
return return
}(conn) }(conn)
} }

View file

@ -34,7 +34,6 @@ import (
// Conn is a HKex connection - a drop-in replacement for net.Conn // Conn is a HKex connection - a drop-in replacement for net.Conn
type Conn struct { type Conn struct {
//net.Conn
c net.Conn // which also implements io.Reader, io.Writer, ... c net.Conn // which also implements io.Reader, io.Writer, ...
h *HerraduraKEx h *HerraduraKEx
cipheropts uint32 // post-KEx cipher/hmac options cipheropts uint32 // post-KEx cipher/hmac options
@ -44,16 +43,12 @@ type Conn struct {
w cipher.Stream w cipher.Stream
} }
func (c *Conn) SetReadDeadline(t time.Time) error {
return c.SetReadDeadline(t)
}
// ConnOpts returns the cipher/hmac options value, which is sent to the // ConnOpts returns the cipher/hmac options value, which is sent to the
// peer but is not itself part of the KEx. // peer but is not itself part of the KEx.
// //
// (Used for protocol-level negotiations after KEx such as // (Used for protocol-level negotiations after KEx such as
// cipher/HMAC algorithm options etc.) // cipher/HMAC algorithm options etc.)
func (c *Conn) ConnOpts() uint32 { func (c Conn) ConnOpts() uint32 {
return c.cipheropts return c.cipheropts
} }
@ -61,7 +56,7 @@ func (c *Conn) ConnOpts() uint32 {
// peer as part of KEx but not part of the KEx itself. // peer as part of KEx but not part of the KEx itself.
// //
// opts - bitfields for cipher and hmac alg. to use after KEx // opts - bitfields for cipher and hmac alg. to use after KEx
func (c *Conn) SetConnOpts(copts uint32) { func (c Conn) SetConnOpts(copts uint32) {
c.cipheropts = copts c.cipheropts = copts
} }
@ -70,7 +65,7 @@ func (c *Conn) SetConnOpts(copts uint32) {
// //
// Consumers of this lib may use this for protocol-level options not part // Consumers of this lib may use this for protocol-level options not part
// of the KEx or encryption info used by the connection. // of the KEx or encryption info used by the connection.
func (c *Conn) Opts() uint32 { func (c Conn) Opts() uint32 {
return c.opts return c.opts
} }
@ -81,7 +76,7 @@ func (c *Conn) Opts() uint32 {
// of the KEx of encryption info used by the connection. // of the KEx of encryption info used by the connection.
// //
// opts - a uint32, caller-defined // opts - a uint32, caller-defined
func (c *Conn) SetOpts(opts uint32) { func (c Conn) SetOpts(opts uint32) {
c.opts = opts c.opts = opts
} }
@ -90,7 +85,7 @@ func (c *Conn) SetOpts(opts uint32) {
// //
// Consumers of this lib may use this to indicate connection-specific // Consumers of this lib may use this to indicate connection-specific
// operations not part of the KEx or encryption info used by the connection. // operations not part of the KEx or encryption info used by the connection.
func (c *Conn) Op() uint8 { func (c Conn) Op() uint8 {
return c.op return c.op
} }
@ -101,11 +96,11 @@ func (c *Conn) Op() uint8 {
// operations not part of the KEx or encryption info used by the connection. // operations not part of the KEx or encryption info used by the connection.
// //
// op - a uint8, caller-defined // op - a uint8, caller-defined
func (c *Conn) SetOp(op uint8) { func (c Conn) SetOp(op uint8) {
c.op = op c.op = op
} }
func (c *Conn) applyConnExtensions(extensions ...string) { func (c Conn) applyConnExtensions(extensions ...string) {
for _, s := range extensions { for _, s := range extensions {
switch s { switch s {
case "C_AES_256": case "C_AES_256":
@ -177,12 +172,57 @@ func Dial(protocol string, ipport string, extensions ...string) (hc *Conn, err e
} }
// Close a hkex.Conn // Close a hkex.Conn
func (c *Conn) Close() (err error) { func (c Conn) Close() (err error) {
err = c.c.Close() err = c.c.Close()
fmt.Println("[Conn Closing]") fmt.Println("[Conn Closing]")
return return
} }
// LocalAddr returns the local network address.
func (c Conn) LocalAddr() net.Addr {
return c.c.LocalAddr()
}
// RemoteAddr returns the remote network address.
func (c Conn) RemoteAddr() net.Addr {
return c.c.RemoteAddr()
}
// SetDeadline sets the read and write deadlines associated
// with the connection. It is equivalent to calling both
// SetReadDeadline and SetWriteDeadline.
//
// A deadline is an absolute time after which I/O operations
// fail with a timeout (see type Error) instead of
// blocking. The deadline applies to all future and pending
// I/O, not just the immediately following call to Read or
// Write. After a deadline has been exceeded, the connection
// can be refreshed by setting a deadline in the future.
//
// An idle timeout can be implemented by repeatedly extending
// the deadline after successful Read or Write calls.
//
// A zero value for t means I/O operations will not time out.
func (c Conn) SetDeadline(t time.Time) error {
return c.SetDeadline(t)
}
// SetWriteDeadline sets the deadline for future Write calls
// and any currently-blocked Write call.
// Even if write times out, it may return n > 0, indicating that
// some of the data was successfully written.
// A zero value for t means Write will not time out.
func (c Conn) SetWriteDeadline(t time.Time) error {
return c.SetWriteDeadline(t)
}
// SetReadDeadline sets the deadline for future Read calls
// and any currently-blocked Read call.
// A zero value for t means Read will not time out.
func (c Conn) SetReadDeadline(t time.Time) error {
return c.SetReadDeadline(t)
}
/*---------------------------------------------------------------------*/ /*---------------------------------------------------------------------*/
// HKExListener is a Listener conforming to net.Listener // HKExListener is a Listener conforming to net.Listener
@ -208,7 +248,7 @@ func Listen(protocol string, ipport string) (hl HKExListener, e error) {
// Close a hkex Listener // Close a hkex Listener
// //
// See go doc io.Close // See go doc io.Close
func (hl *HKExListener) Close() error { func (hl HKExListener) Close() error {
fmt.Println("[Listener Closed]") fmt.Println("[Listener Closed]")
return hl.l.Close() return hl.l.Close()
} }
@ -216,7 +256,7 @@ func (hl *HKExListener) Close() error {
// Accept a client connection, conforming to net.Listener.Accept() // Accept a client connection, conforming to net.Listener.Accept()
// //
// See go doc net.Listener.Accept // See go doc net.Listener.Accept
func (hl *HKExListener) Accept() (hc Conn, err error) { func (hl HKExListener) Accept() (hc Conn, err error) {
c, err := hl.l.Accept() c, err := hl.l.Accept()
if err != nil { if err != nil {
return Conn{c: nil, h: nil, cipheropts: 0, opts: 0, return Conn{c: nil, h: nil, cipheropts: 0, opts: 0,