mirror of https://gogs.blitter.com/RLabs/xs
Continuing groundwork for cp mode - refactor main client code into shell/copy subroutines; -r option
This commit is contained in:
parent
c3f3bcb13f
commit
5859131678
1
cp.cmd
1
cp.cmd
|
@ -9,3 +9,4 @@ tar -cz -f - testdir/sub1/bar.txt | \
|
||||||
# (in the absence of --xform=.. above, files and dirs will all be extracted
|
# (in the absence of --xform=.. above, files and dirs will all be extracted
|
||||||
# to remote DEST preserving tree structure.)
|
# to remote DEST preserving tree structure.)
|
||||||
|
|
||||||
|
tar cf /dev/stdout ../*.txt | tar xf -
|
||||||
|
|
|
@ -202,7 +202,6 @@ func Dial(protocol string, ipport string, extensions ...string) (hc *Conn, err e
|
||||||
|
|
||||||
hc.r, hc.rm, err = hc.getStream(hc.h.FA())
|
hc.r, hc.rm, err = hc.getStream(hc.h.FA())
|
||||||
hc.w, hc.wm, err = hc.getStream(hc.h.FA())
|
hc.w, hc.wm, err = hc.getStream(hc.h.FA())
|
||||||
|
|
||||||
*hc.closeStat = 99 // open or prematurely-closed status
|
*hc.closeStat = 99 // open or prematurely-closed status
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
199
hkexsh/hkexsh.go
199
hkexsh/hkexsh.go
|
@ -54,11 +54,9 @@ func GetSize() (cols, rows int, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseNonSwitchArgs(a []string, dp string) (user, host, port, path string, isDest bool, otherArgs []string) {
|
func parseNonSwitchArgs(a []string, dp string) (user, host, port, path string, isDest bool, otherArgs []string) {
|
||||||
//TODO: Look for non-option fancyArg of syntax user@host:filespec to set -r,-t and -u
|
// Whether fancyArg is src or dst file depends on flag.Args() index;
|
||||||
// Consider: whether fancyArg is src or dst file depends on flag.Args() index;
|
|
||||||
// fancyArg as last flag.Args() element denotes dstFile
|
// fancyArg as last flag.Args() element denotes dstFile
|
||||||
// fancyArg as not-last flag.Args() element denotes srcFile
|
// fancyArg as not-last flag.Args() element denotes srcFile
|
||||||
// * throw error if >1 fancyArgs are found in flags.Args()
|
|
||||||
var fancyUser, fancyHost, fancyPort, fancyPath string
|
var fancyUser, fancyHost, fancyPort, fancyPath string
|
||||||
for i, arg := range a {
|
for i, arg := range a {
|
||||||
if strings.Contains(arg, ":") || strings.Contains(arg, "@") {
|
if strings.Contains(arg, ":") || strings.Contains(arg, "@") {
|
||||||
|
@ -102,8 +100,82 @@ func parseNonSwitchArgs(a []string, dp string) (user, host, port, path string, i
|
||||||
return fancyUser, fancyHost, fancyPort, fancyPath, isDest, otherArgs
|
return fancyUser, fancyHost, fancyPort, fancyPath, isDest, otherArgs
|
||||||
}
|
}
|
||||||
|
|
||||||
// Demo of a simple client that dials up to a simple test server to
|
// doCopyMode begins a secure hkexsh local<->remote file copy operation.
|
||||||
// send data.
|
func doCopyMode(conn *hkexnet.Conn, remoteDest bool, files string, recurs bool, rec *cmdSpec) {
|
||||||
|
// TODO: Bring in runShellAs(), stripped down, from hkexshd
|
||||||
|
// and build either side of tar pipeline: names?
|
||||||
|
// runTarSrc(), runTarSink() ?
|
||||||
|
if remoteDest {
|
||||||
|
fmt.Println("local files:", files, "remote filepath:", string(rec.cmd))
|
||||||
|
} else {
|
||||||
|
fmt.Println("remote filepath:", string(rec.cmd), "local files:", files)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// doShellMode begins an hkexsh shell session (one-shot command or interactive).
|
||||||
|
func doShellMode(isInteractive bool, conn *hkexnet.Conn, oldState *hkexsh.State, rec *cmdSpec) {
|
||||||
|
//client reader (from server) goroutine
|
||||||
|
//Read remote end's stdout
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
// By deferring a call to wg.Done(),
|
||||||
|
// each goroutine guarantees that it marks
|
||||||
|
// its direction's stream as finished.
|
||||||
|
|
||||||
|
// io.Copy() expects EOF so normally this will
|
||||||
|
// exit with inerr == nil
|
||||||
|
_, inerr := io.Copy(os.Stdout, conn)
|
||||||
|
if inerr != nil {
|
||||||
|
fmt.Println(inerr)
|
||||||
|
_ = hkexsh.Restore(int(os.Stdin.Fd()), oldState) // Best effort.
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
rec.status = int(conn.GetStatus())
|
||||||
|
log.Println("rec.status:", rec.status)
|
||||||
|
|
||||||
|
if isInteractive {
|
||||||
|
log.Println("[* Got EOF *]")
|
||||||
|
_ = hkexsh.Restore(int(os.Stdin.Fd()), oldState) // Best effort.
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Only look for data from stdin to send to remote end
|
||||||
|
// for interactive sessions.
|
||||||
|
if isInteractive {
|
||||||
|
handleTermResizes(conn)
|
||||||
|
|
||||||
|
// client writer (to server) goroutine
|
||||||
|
// Write local stdin to remote end
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
//!defer wg.Done()
|
||||||
|
// Copy() expects EOF so this will
|
||||||
|
// exit with outerr == nil
|
||||||
|
//!_, outerr := io.Copy(conn, os.Stdin)
|
||||||
|
_, outerr := func(conn *hkexnet.Conn, r io.Reader) (w int64, e error) {
|
||||||
|
w, e = io.Copy(conn, r)
|
||||||
|
return w, e
|
||||||
|
}(conn, os.Stdin)
|
||||||
|
|
||||||
|
if outerr != nil {
|
||||||
|
log.Println(outerr)
|
||||||
|
fmt.Println(outerr)
|
||||||
|
_ = hkexsh.Restore(int(os.Stdin.Fd()), oldState) // Best effort.
|
||||||
|
os.Exit(255)
|
||||||
|
}
|
||||||
|
log.Println("[Sent EOF]")
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait until both stdin and stdout goroutines finish before returning
|
||||||
|
// (ensure client gets all data from server before closing)
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
// hkexsh - a client for secure shell and file copy operations.
|
||||||
//
|
//
|
||||||
// While conforming to the basic net.Conn interface HKex.Conn has extra
|
// While conforming to the basic net.Conn interface HKex.Conn has extra
|
||||||
// capabilities designed to allow apps to define connection options,
|
// capabilities designed to allow apps to define connection options,
|
||||||
|
@ -124,6 +196,7 @@ func main() {
|
||||||
var server string
|
var server string
|
||||||
var cmdStr string
|
var cmdStr string
|
||||||
|
|
||||||
|
var recursiveCopy bool
|
||||||
var copySrc []byte
|
var copySrc []byte
|
||||||
var copyDst string
|
var copyDst string
|
||||||
|
|
||||||
|
@ -156,12 +229,10 @@ func main() {
|
||||||
// a srcpath (-r) or dstpath (-t)
|
// a srcpath (-r) or dstpath (-t)
|
||||||
flag.StringVar(&cmdStr, "x", "", "command to run (default empty - interactive shell)")
|
flag.StringVar(&cmdStr, "x", "", "command to run (default empty - interactive shell)")
|
||||||
shellMode = true
|
shellMode = true
|
||||||
} // else {
|
} else {
|
||||||
//// hkexcp accepts srcpath (-r) and dstpath (-t), but not
|
// Note: only makes sense for client->server copies
|
||||||
//// a command (-x)
|
flag.BoolVar(&recursiveCopy, "r", false, "recursive copy/preserve tree copy")
|
||||||
//flag.StringVar(©Src, "r", "", "copy srcpath")
|
}
|
||||||
//flag.StringVar(©Dst, "t", "", "copy dstpath")
|
|
||||||
//}
|
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
tmpUser, tmpHost, tmpPort, tmpPath, pathIsDest, otherArgs :=
|
tmpUser, tmpHost, tmpPort, tmpPath, pathIsDest, otherArgs :=
|
||||||
|
@ -176,21 +247,35 @@ func main() {
|
||||||
server = tmpHost + ":" + tmpPort
|
server = tmpHost + ":" + tmpPort
|
||||||
//fmt.Println("tmpHost sets server to", server)
|
//fmt.Println("tmpHost sets server to", server)
|
||||||
}
|
}
|
||||||
if tmpPath != "" {
|
|
||||||
|
var fileArgs string
|
||||||
|
if !shellMode && tmpPath != "" {
|
||||||
// -if pathIsSrc && len(otherArgs) > 1 ERROR
|
// -if pathIsSrc && len(otherArgs) > 1 ERROR
|
||||||
// -else flatten otherArgs into space-delim list => copySrc
|
// -else flatten otherArgs into space-delim list => copySrc
|
||||||
if pathIsDest {
|
if pathIsDest {
|
||||||
|
if len(otherArgs) == 0 {
|
||||||
|
log.Fatal("ERROR: Must specify at least one src path for copy")
|
||||||
|
} else {
|
||||||
for _, v := range otherArgs {
|
for _, v := range otherArgs {
|
||||||
copySrc = append(copySrc, ' ')
|
copySrc = append(copySrc, ' ')
|
||||||
copySrc = append(copySrc, v...)
|
copySrc = append(copySrc, v...)
|
||||||
}
|
}
|
||||||
fmt.Println(">> copySrc:", string(copySrc))
|
|
||||||
copyDst = tmpPath
|
copyDst = tmpPath
|
||||||
|
fileArgs = string(copySrc)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if len(otherArgs) == 0 {
|
||||||
|
log.Fatal("ERROR: Must specify dest path for copy")
|
||||||
|
} else if len(otherArgs) == 1 {
|
||||||
|
copyDst = otherArgs[0]
|
||||||
|
if strings.Contains(copyDst, "*") || strings.Contains(copyDst, "?") {
|
||||||
|
log.Fatal("ERROR: wildcards not allowed in dest path for copy")
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if len(otherArgs) > 1 {
|
|
||||||
log.Fatal("ERROR: cannot specify more than one dest path for copy")
|
log.Fatal("ERROR: cannot specify more than one dest path for copy")
|
||||||
}
|
}
|
||||||
copySrc = []byte(tmpPath)
|
copySrc = []byte(tmpPath)
|
||||||
|
fileArgs = copyDst
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -222,6 +307,7 @@ func main() {
|
||||||
log.SetOutput(ioutil.Discard)
|
log.SetOutput(ioutil.Discard)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if shellMode {
|
||||||
// We must make the decision about interactivity before Dial()
|
// We must make the decision about interactivity before Dial()
|
||||||
// as it affects chaffing behaviour. 20180805
|
// as it affects chaffing behaviour. 20180805
|
||||||
if len(cmdStr) == 0 {
|
if len(cmdStr) == 0 {
|
||||||
|
@ -232,11 +318,29 @@ func main() {
|
||||||
// non-interactive cmds may complete quickly, so chaff earlier/faster
|
// non-interactive cmds may complete quickly, so chaff earlier/faster
|
||||||
// to help ensure there's some cover to the brief traffic.
|
// to help ensure there's some cover to the brief traffic.
|
||||||
// (ignoring cmdline values)
|
// (ignoring cmdline values)
|
||||||
//!DEBUG
|
|
||||||
//chaffEnabled = false
|
|
||||||
chaffFreqMin = 2
|
chaffFreqMin = 2
|
||||||
chaffFreqMax = 10
|
chaffFreqMax = 10
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// as copy mode is also non-interactive, set up chaffing
|
||||||
|
// just like the 'c' mode above
|
||||||
|
chaffFreqMin = 2
|
||||||
|
chaffFreqMax = 10
|
||||||
|
|
||||||
|
if pathIsDest {
|
||||||
|
// client->server file copy
|
||||||
|
// src file list is in copySrc
|
||||||
|
op = []byte{'D'}
|
||||||
|
fmt.Println("client->server copy:", string(copySrc), "->", copyDst)
|
||||||
|
cmdStr = copyDst
|
||||||
|
} else {
|
||||||
|
// server->client file copy
|
||||||
|
// remote src file(s) in copyDsr
|
||||||
|
op = []byte{'S'}
|
||||||
|
fmt.Println("server->client copy:", string(copySrc), "->", copyDst)
|
||||||
|
cmdStr = string(copySrc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
conn, err := hkexnet.Dial("tcp", server, cAlg, hAlg)
|
conn, err := hkexnet.Dial("tcp", server, cAlg, hAlg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -306,68 +410,15 @@ func main() {
|
||||||
defer conn.ShutdownChaff()
|
defer conn.ShutdownChaff()
|
||||||
}
|
}
|
||||||
|
|
||||||
//client reader (from server) goroutine
|
if shellMode {
|
||||||
//Read remote end's stdout
|
doShellMode(isInteractive, conn, oldState, rec)
|
||||||
wg.Add(1)
|
} else {
|
||||||
go func() {
|
doCopyMode(conn, pathIsDest, fileArgs, recursiveCopy, rec)
|
||||||
defer wg.Done()
|
|
||||||
// By deferring a call to wg.Done(),
|
|
||||||
// each goroutine guarantees that it marks
|
|
||||||
// its direction's stream as finished.
|
|
||||||
|
|
||||||
// io.Copy() expects EOF so normally this will
|
|
||||||
// exit with inerr == nil
|
|
||||||
_, inerr := io.Copy(os.Stdout, conn)
|
|
||||||
if inerr != nil {
|
|
||||||
fmt.Println(inerr)
|
|
||||||
_ = hkexsh.Restore(int(os.Stdin.Fd()), oldState) // Best effort.
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rec.status = int(conn.GetStatus())
|
if oldState != nil {
|
||||||
log.Println("rec.status:", rec.status)
|
|
||||||
|
|
||||||
if isInteractive {
|
|
||||||
log.Println("[* Got EOF *]")
|
|
||||||
_ = hkexsh.Restore(int(os.Stdin.Fd()), oldState) // Best effort.
|
_ = hkexsh.Restore(int(os.Stdin.Fd()), oldState) // Best effort.
|
||||||
}
|
}
|
||||||
}()
|
|
||||||
|
|
||||||
// Only look for data from stdin to send to remote end
|
|
||||||
// for interactive sessions.
|
|
||||||
if isInteractive {
|
|
||||||
handleTermResizes(conn)
|
|
||||||
|
|
||||||
// client writer (to server) goroutine
|
|
||||||
// Write local stdin to remote end
|
|
||||||
wg.Add(1)
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
//!defer wg.Done()
|
|
||||||
// Copy() expects EOF so this will
|
|
||||||
// exit with outerr == nil
|
|
||||||
//!_, outerr := io.Copy(conn, os.Stdin)
|
|
||||||
_, outerr := func(conn *hkexnet.Conn, r io.Reader) (w int64, e error) {
|
|
||||||
w, e = io.Copy(conn, r)
|
|
||||||
return w, e
|
|
||||||
}(conn, os.Stdin)
|
|
||||||
|
|
||||||
if outerr != nil {
|
|
||||||
log.Println(outerr)
|
|
||||||
fmt.Println(outerr)
|
|
||||||
_ = hkexsh.Restore(int(os.Stdin.Fd()), oldState) // Best effort.
|
|
||||||
os.Exit(255)
|
|
||||||
}
|
|
||||||
log.Println("[Sent EOF]")
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait until both stdin and stdout goroutines finish
|
|
||||||
// ** IMPORTANT! This must come before the Restore() tty call below
|
|
||||||
// in order to maintain raw mode for interactive sessions. -rlm 20180805
|
|
||||||
wg.Wait()
|
|
||||||
|
|
||||||
_ = hkexsh.Restore(int(os.Stdin.Fd()), oldState) // Best effort.
|
|
||||||
|
|
||||||
os.Exit(rec.status)
|
os.Exit(rec.status)
|
||||||
}
|
}
|
||||||
|
|
|
@ -369,6 +369,16 @@ func main() {
|
||||||
log.Printf("[Shell completed for %s@%s, status %d]\n", rec.who, hname, cmdStatus)
|
log.Printf("[Shell completed for %s@%s, status %d]\n", rec.who, hname, cmdStatus)
|
||||||
hc.SetStatus(uint8(cmdStatus))
|
hc.SetStatus(uint8(cmdStatus))
|
||||||
}
|
}
|
||||||
|
} else if rec.op[0] == 'D' {
|
||||||
|
// File copy (destination) operation - client copy to server
|
||||||
|
log.Printf("[Client->Server copy]\n")
|
||||||
|
// TODO: call function with hc, rec.cmd, chaffEnabled etc.
|
||||||
|
// func hooks tar cmd right-half of pipe to hc Reader
|
||||||
|
} else if rec.op[0] == 'S' {
|
||||||
|
// File copy (src) operation - server copy to client
|
||||||
|
log.Printf("[Server->Client copy]\n")
|
||||||
|
// TODO: call function to copy rec.cmd (file list) to
|
||||||
|
// tar cmd left-half of pipeline to hc.Writer ?
|
||||||
} else {
|
} else {
|
||||||
log.Println("[Bad cmdSpec]")
|
log.Println("[Bad cmdSpec]")
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue