mirror of https://gogs.blitter.com/RLabs/xs
Further work on term resizing platform support for Linux and Windows/mintty
This commit is contained in:
parent
89dd225910
commit
f92085bb86
|
@ -32,19 +32,21 @@ type cmdSpec struct {
|
||||||
status int
|
status int
|
||||||
}
|
}
|
||||||
|
|
||||||
// get terminal size using 'stty' command
|
var (
|
||||||
// (Most portable btwn Linux and MSYS/win32, but
|
wg sync.WaitGroup
|
||||||
// TODO: remove external dep on 'stty' utility)
|
)
|
||||||
func getTermSize() (rows int, cols int, err error) {
|
|
||||||
|
// Get terminal size using 'stty' command
|
||||||
|
func GetSize() (cols, rows int, err error) {
|
||||||
cmd := exec.Command("stty", "size")
|
cmd := exec.Command("stty", "size")
|
||||||
cmd.Stdin = os.Stdin
|
cmd.Stdin = os.Stdin
|
||||||
out, err := cmd.Output()
|
out, err := cmd.Output()
|
||||||
//fmt.Printf("out: %#v\n", string(out))
|
|
||||||
//fmt.Printf("err: %#v\n", err)
|
|
||||||
|
|
||||||
fmt.Sscanf(string(out), "%d %d\n", &rows, &cols)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Println(err)
|
||||||
|
cols, rows = 80, 24 //failsafe
|
||||||
|
} else {
|
||||||
|
fmt.Sscanf(string(out), "%d %d\n", &rows, &cols)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -62,8 +64,6 @@ func getTermSize() (rows int, cols int, err error) {
|
||||||
// connection (app-specific, passed through to the server to use or
|
// connection (app-specific, passed through to the server to use or
|
||||||
// ignore at its discretion).
|
// ignore at its discretion).
|
||||||
func main() {
|
func main() {
|
||||||
var wg sync.WaitGroup
|
|
||||||
|
|
||||||
version := "0.1pre (NO WARRANTY)"
|
version := "0.1pre (NO WARRANTY)"
|
||||||
var vopt bool
|
var vopt bool
|
||||||
var dbg bool
|
var dbg bool
|
||||||
|
@ -73,6 +73,7 @@ func main() {
|
||||||
var cmdStr string
|
var cmdStr string
|
||||||
var altUser string
|
var altUser string
|
||||||
var authCookie string
|
var authCookie string
|
||||||
|
var chaffEnabled bool
|
||||||
var chaffFreqMin uint
|
var chaffFreqMin uint
|
||||||
var chaffFreqMax uint
|
var chaffFreqMax uint
|
||||||
var chaffBytesMax uint
|
var chaffBytesMax uint
|
||||||
|
@ -86,6 +87,7 @@ func main() {
|
||||||
flag.StringVar(&cmdStr, "x", "", "command to run (default empty - interactive shell)")
|
flag.StringVar(&cmdStr, "x", "", "command to run (default empty - interactive shell)")
|
||||||
flag.StringVar(&altUser, "u", "", "specify alternate user")
|
flag.StringVar(&altUser, "u", "", "specify alternate user")
|
||||||
flag.StringVar(&authCookie, "a", "", "auth cookie")
|
flag.StringVar(&authCookie, "a", "", "auth cookie")
|
||||||
|
flag.BoolVar(&chaffEnabled, "cE", true, "enabled chaff pkts (default true)")
|
||||||
flag.UintVar(&chaffFreqMin, "cfm", 100, "chaff pkt freq min (msecs)")
|
flag.UintVar(&chaffFreqMin, "cfm", 100, "chaff pkt freq min (msecs)")
|
||||||
flag.UintVar(&chaffFreqMax, "cfM", 5000, "chaff pkt freq max (msecs)")
|
flag.UintVar(&chaffFreqMax, "cfM", 5000, "chaff pkt freq max (msecs)")
|
||||||
flag.UintVar(&chaffBytesMax, "cbM", 64, "chaff pkt size max (bytes)")
|
flag.UintVar(&chaffBytesMax, "cbM", 64, "chaff pkt size max (bytes)")
|
||||||
|
@ -183,7 +185,9 @@ func main() {
|
||||||
|
|
||||||
// Set up chaffing to server
|
// Set up chaffing to server
|
||||||
conn.Chaff(chaffFreqMin, chaffFreqMax, chaffBytesMax) // enable client->server chaffing
|
conn.Chaff(chaffFreqMin, chaffFreqMax, chaffBytesMax) // enable client->server chaffing
|
||||||
conn.EnableChaff()
|
if chaffEnabled {
|
||||||
|
conn.EnableChaff()
|
||||||
|
}
|
||||||
|
|
||||||
//client reader (from server) goroutine
|
//client reader (from server) goroutine
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
|
@ -218,7 +222,7 @@ func main() {
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if isInteractive {
|
if isInteractive {
|
||||||
handleTermResizes()
|
handleTermResizes(conn)
|
||||||
|
|
||||||
// client writer (to server) goroutine
|
// client writer (to server) goroutine
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
|
|
|
@ -12,7 +12,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Handle pty resizes (notify server side)
|
// Handle pty resizes (notify server side)
|
||||||
func handleTermResizes() {
|
func handleTermResizes(conn *hkexsh.Conn) {
|
||||||
rows := 0
|
rows := 0
|
||||||
cols := 0
|
cols := 0
|
||||||
|
|
||||||
|
@ -25,10 +25,10 @@ func handleTermResizes() {
|
||||||
for range ch {
|
for range ch {
|
||||||
// Query client's term size so we can communicate it to server
|
// Query client's term size so we can communicate it to server
|
||||||
// pty after interactive session starts
|
// pty after interactive session starts
|
||||||
rows, cols, err = getTermSize()
|
cols, rows, err = GetSize()
|
||||||
log.Printf("[rows %v cols %v]\n", rows, cols)
|
log.Printf("[rows %v cols %v]\n", rows, cols)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
log.Println(err)
|
||||||
}
|
}
|
||||||
termSzPacket := fmt.Sprintf("%d %d", rows, cols)
|
termSzPacket := fmt.Sprintf("%d %d", rows, cols)
|
||||||
conn.WritePacket([]byte(termSzPacket), hkexsh.CSOTermSize)
|
conn.WritePacket([]byte(termSzPacket), hkexsh.CSOTermSize)
|
||||||
|
|
|
@ -1,8 +1,65 @@
|
||||||
// +build windows
|
// +build windows
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
hkexsh "blitter.com/go/hkexsh"
|
||||||
|
)
|
||||||
|
|
||||||
// Handle pty resizes (notify server side)
|
// Handle pty resizes (notify server side)
|
||||||
func handleTermResizes() {
|
func handleTermResizes(conn *hkexsh.Conn) {
|
||||||
|
var hasStty bool
|
||||||
|
curCols, curRows := 0, 0
|
||||||
|
_, _, err := GetSize()
|
||||||
|
// The above may fail if user doesn't have msys 'stty' util
|
||||||
|
// in PATH. GetSize() will log.Error() once here
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("[1st GetSize:", err, "]")
|
||||||
|
hasStty = false
|
||||||
|
} else {
|
||||||
|
hasStty = true
|
||||||
|
}
|
||||||
|
|
||||||
|
ch := make(chan bool, 1)
|
||||||
|
|
||||||
|
if hasStty {
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
for {
|
||||||
|
ch <- true
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
rows := 0
|
||||||
|
cols := 0
|
||||||
|
for range ch {
|
||||||
|
// Query client's term size so we can communicate it to server
|
||||||
|
// pty after interactive session starts
|
||||||
|
cols, rows, err = GetSize()
|
||||||
|
if err == nil {
|
||||||
|
} else {
|
||||||
|
fmt.Println("[GetSize:", err, "]")
|
||||||
|
}
|
||||||
|
if (curRows != rows) || (curCols != curCols) {
|
||||||
|
curRows = rows
|
||||||
|
curCols = cols
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
termSzPacket := fmt.Sprintf("%d %d", curRows, curCols)
|
||||||
|
conn.WritePacket([]byte(termSzPacket), hkexsh.CSOTermSize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
ch <- true // Initial resize
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -79,7 +79,7 @@ func runCmdAs(who string, cmd string, conn hkex.Conn) (err error) {
|
||||||
// Run a command (via default shell) as a specific user
|
// Run a command (via default shell) as a specific user
|
||||||
//
|
//
|
||||||
// Uses ptys to support commands which expect a terminal.
|
// Uses ptys to support commands which expect a terminal.
|
||||||
func runShellAs(who string, cmd string, interactive bool, conn hkexsh.Conn) (err error) {
|
func runShellAs(who string, cmd string, interactive bool, conn hkexsh.Conn, chaffing bool) (err error) {
|
||||||
u, _ := user.Lookup(who)
|
u, _ := user.Lookup(who)
|
||||||
var uid, gid uint32
|
var uid, gid uint32
|
||||||
fmt.Sscanf(u.Uid, "%d", &uid)
|
fmt.Sscanf(u.Uid, "%d", &uid)
|
||||||
|
@ -134,8 +134,10 @@ func runShellAs(who string, cmd string, interactive bool, conn hkexsh.Conn) (err
|
||||||
_, _ = io.Copy(ptmx, conn)
|
_, _ = io.Copy(ptmx, conn)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
conn.EnableChaff()
|
if chaffing {
|
||||||
|
conn.EnableChaff()
|
||||||
|
}
|
||||||
|
|
||||||
// ..and the pty to stdout.
|
// ..and the pty to stdout.
|
||||||
_, _ = io.Copy(conn, ptmx)
|
_, _ = io.Copy(conn, ptmx)
|
||||||
|
|
||||||
|
@ -160,6 +162,7 @@ func rejectUserMsg() string {
|
||||||
func main() {
|
func main() {
|
||||||
version := "0.1pre (NO WARRANTY)"
|
version := "0.1pre (NO WARRANTY)"
|
||||||
var vopt bool
|
var vopt bool
|
||||||
|
var chaffEnabled bool
|
||||||
var chaffFreqMin uint
|
var chaffFreqMin uint
|
||||||
var chaffFreqMax uint
|
var chaffFreqMax uint
|
||||||
var chaffBytesMax uint
|
var chaffBytesMax uint
|
||||||
|
@ -168,6 +171,7 @@ func main() {
|
||||||
|
|
||||||
flag.BoolVar(&vopt, "v", false, "show version")
|
flag.BoolVar(&vopt, "v", false, "show version")
|
||||||
flag.StringVar(&laddr, "l", ":2000", "interface[:port] to listen")
|
flag.StringVar(&laddr, "l", ":2000", "interface[:port] to listen")
|
||||||
|
flag.BoolVar(&chaffEnabled, "cE", true, "enabled chaff pkts (default true)")
|
||||||
flag.UintVar(&chaffFreqMin, "cfm", 100, "chaff pkt freq min (msecs)")
|
flag.UintVar(&chaffFreqMin, "cfm", 100, "chaff pkt freq min (msecs)")
|
||||||
flag.UintVar(&chaffFreqMax, "cfM", 5000, "chaff pkt freq max (msecs)")
|
flag.UintVar(&chaffFreqMax, "cfM", 5000, "chaff pkt freq max (msecs)")
|
||||||
flag.UintVar(&chaffBytesMax, "cbM", 64, "chaff pkt size max (bytes)")
|
flag.UintVar(&chaffBytesMax, "cbM", 64, "chaff pkt size max (bytes)")
|
||||||
|
@ -175,7 +179,7 @@ func main() {
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
if vopt {
|
if vopt {
|
||||||
fmt.Printf("version v%s\n", version)
|
fmt.Printf("version v%s\n", version)
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -205,7 +209,9 @@ func main() {
|
||||||
log.Println("Accepted client")
|
log.Println("Accepted client")
|
||||||
|
|
||||||
// Set up chaffing to client
|
// Set up chaffing to client
|
||||||
conn.Chaff(chaffFreqMin, chaffFreqMax, chaffBytesMax) // enable client->server chaffing
|
// Will only start when runShellAs() is called
|
||||||
|
// after stdin/stdout are hooked up
|
||||||
|
conn.Chaff(chaffFreqMin, chaffFreqMax, chaffBytesMax) // configure server->client chaffing
|
||||||
|
|
||||||
// Handle the connection in a new goroutine.
|
// Handle the connection in a new goroutine.
|
||||||
// The loop then returns to accepting, so that
|
// The loop then returns to accepting, so that
|
||||||
|
@ -276,14 +282,14 @@ func main() {
|
||||||
if rec.op[0] == 'c' {
|
if rec.op[0] == 'c' {
|
||||||
// Non-interactive command
|
// Non-interactive command
|
||||||
log.Println("[Running command]")
|
log.Println("[Running command]")
|
||||||
runShellAs(string(rec.who), string(rec.cmd), false, conn)
|
runShellAs(string(rec.who), string(rec.cmd), false, conn, chaffEnabled)
|
||||||
// Returned hopefully via an EOF or exit/logout;
|
// Returned hopefully via an EOF or exit/logout;
|
||||||
// Clear current op so user can enter next, or EOF
|
// Clear current op so user can enter next, or EOF
|
||||||
rec.op[0] = 0
|
rec.op[0] = 0
|
||||||
log.Println("[Command complete]")
|
log.Println("[Command complete]")
|
||||||
} else if rec.op[0] == 's' {
|
} else if rec.op[0] == 's' {
|
||||||
log.Println("[Running shell]")
|
log.Println("[Running shell]")
|
||||||
runShellAs(string(rec.who), string(rec.cmd), true, conn)
|
runShellAs(string(rec.who), string(rec.cmd), true, conn, chaffEnabled)
|
||||||
// Returned hopefully via an EOF or exit/logout;
|
// Returned hopefully via an EOF or exit/logout;
|
||||||
// Clear current op so user can enter next, or EOF
|
// Clear current op so user can enter next, or EOF
|
||||||
rec.op[0] = 0
|
rec.op[0] = 0
|
||||||
|
|
Loading…
Reference in New Issue