Compare commits

..

No commits in common. "b2e43f4bad256f24aa2c4ebb02c759c22108ad47" and "0f28d2c023283838d79c5a94be5ff1dba7d332f0" have entirely different histories.

4 changed files with 100 additions and 128 deletions

View file

@ -25,8 +25,6 @@ linters-settings:
#- style
#- opinionated
disabled-checks:
- commentFormatting
- commentedOutCode
- dupImport # https://github.com/go-critic/go-critic/issues/845
- ifElseChain
- octalLiteral

View file

@ -31,7 +31,7 @@ func handleTermResizes(conn *xsnet.Conn) {
log.Println(err)
}
termSzPacket := fmt.Sprintf("%d %d", rows, cols)
conn.WritePacket([]byte(termSzPacket), xsnet.CSOTermSize) //nolint:errcheck
conn.WritePacket([]byte(termSzPacket), xsnet.CSOTermSize)
}
}()
ch <- syscall.SIGWINCH // Initial resize.

181
xs/xs.go
View file

@ -14,6 +14,7 @@ import (
"flag"
"fmt"
"io"
"io/ioutil"
"log"
"math/rand"
"os"
@ -56,18 +57,6 @@ var (
////////////////////////////////////////////////////
const (
CmdExitedEarly = 2
XSNetDialFailed = 3
ErrReadingAuthReply = 253
ServerRejectedSecureProposal = 254
GeneralProtocolErr = 255
)
const (
DeadCharPrefix = 0x1d
)
// Praise Bob. Do not remove, lest ye lose Slack.
const bob = string("\r\n\r\n" +
"@@@@@@@^^~~~~~~~~~~~~~~~~~~~~^@@@@@@@@@\r\n" +
@ -165,7 +154,7 @@ func copyBuffer(dst io.Writer, src io.Reader, buf []byte) (written int64, err er
if rt, ok := dst.(io.ReaderFrom); ok {
return rt.ReadFrom(src)
}
*/ //nolint:gocritic,nolintlint
*/
if buf == nil {
size := 32 * 1024
if l, ok := src.(*io.LimitedReader); ok && int64(size) > l.N {
@ -186,8 +175,8 @@ func copyBuffer(dst io.Writer, src io.Reader, buf []byte) (written int64, err er
// A repeat of 4 keys (conveniently 'dead' chars for most
// interactive shells; here CTRL-]) shall introduce
// some special responses or actions on the client side.
if seqPos < 4 { //nolint:gomnd
if buf[0] == DeadCharPrefix {
if seqPos < 4 {
if buf[0] == 0x1d {
seqPos++
}
} else {
@ -234,7 +223,7 @@ func GetSize() (cols, rows int, err error) {
if err != nil {
log.Println(err)
cols, rows = 80, 24 // failsafe
cols, rows = 80, 24 //failsafe
} else {
n, err := fmt.Sscanf(string(out), "%d %d\n", &rows, &cols)
if n < 2 ||
@ -268,7 +257,7 @@ func buildCmdRemoteToLocal(copyQuiet bool, copyLimitBPS uint, destPath string) (
} else {
// TODO: Query remote side for total file/dir size
bandwidthInBytesPerSec := " -L " + fmt.Sprintf("%d ", copyLimitBPS)
displayOpts := " -pre " //nolint:goconst
displayOpts := " -pre "
cmd = xs.GetTool("bash")
args = []string{"-c", "pv " + displayOpts + bandwidthInBytesPerSec + "| tar -xz -C " + destPath}
}
@ -316,7 +305,7 @@ func buildCmdLocalToRemote(copyQuiet bool, copyLimitBPS uint, files string) (cap
} else {
captureStderr = copyQuiet
bandwidthInBytesPerSec := " -L " + fmt.Sprintf("%d", copyLimitBPS)
displayOpts := " -pre " //nolint:goconst,nolintlint
displayOpts := " -pre "
cmd = xs.GetTool("bash")
args = []string{"-c", xs.GetTool("tar") + " -cz -f /dev/stdout "}
files = strings.TrimSpace(files)
@ -366,9 +355,9 @@ func doCopyMode(conn *xsnet.Conn, remoteDest bool, files string, copyQuiet bool,
var c *exec.Cmd
// os.Clearenv()
// os.Setenv("HOME", u.HomeDir)
// os.Setenv("TERM", "vt102") // TODO: server or client option?
//os.Clearenv()
//os.Setenv("HOME", u.HomeDir)
//os.Setenv("TERM", "vt102") // TODO: server or client option?
captureStderr, cmdName, cmdArgs := buildCmdLocalToRemote(copyQuiet, copyLimitBPS, strings.TrimSpace(files))
c = exec.Command(cmdName, cmdArgs...) // #nosec
@ -401,7 +390,7 @@ func doCopyMode(conn *xsnet.Conn, remoteDest bool, files string, copyQuiet bool,
if err != nil {
fmt.Println("cmd exited immediately. Cannot get cmd.Wait().ExitStatus()")
err = errors.New("cmd exited prematurely")
exitStatus = uint32(CmdExitedEarly)
exitStatus = uint32(2)
} else {
if err = c.Wait(); err != nil {
if exiterr, ok := err.(*exec.ExitError); ok {
@ -421,7 +410,7 @@ func doCopyMode(conn *xsnet.Conn, remoteDest bool, files string, copyQuiet bool,
}
// send CSOExitStatus to inform remote (server) end cp is done
log.Println("Sending local exitStatus:", exitStatus)
r := make([]byte, 4) //nolint:gomnd
r := make([]byte, 4)
binary.BigEndian.PutUint32(r, exitStatus)
_, we := conn.WritePacket(r, xsnet.CSOExitStatus)
if we != nil {
@ -429,7 +418,7 @@ func doCopyMode(conn *xsnet.Conn, remoteDest bool, files string, copyQuiet bool,
}
// Do a final read for remote's exit status
s := make([]byte, 4) //nolint:gomnd
s := make([]byte, 4)
_, remErr := conn.Read(s)
if remErr != io.EOF &&
!strings.Contains(remErr.Error(), "use of closed network") &&
@ -489,8 +478,8 @@ func doCopyMode(conn *xsnet.Conn, remoteDest bool, files string, copyQuiet bool,
// doShellMode begins an xs shell session (one-shot command or
// interactive).
func doShellMode(isInteractive bool, conn *xsnet.Conn, oldState *xs.State, rec *xs.Session) {
// Client reader (from server) goroutine
// Read remote end's stdout
//client reader (from server) goroutine
//Read remote end's stdout
wg.Add(1)
// #gv:s/label=\"doShellMode\$1\"/label=\"shellRemoteToStdin\"/
@ -613,7 +602,7 @@ func parseNonSwitchArgs(a []string) (user, host, path string, isDest bool, other
if strings.Contains(arg, ":") || strings.Contains(arg, "@") {
fancyArg := strings.Split(flag.Arg(i), "@")
var fancyHostPath []string
if len(fancyArg) < 2 { //nolint:gomnd
if len(fancyArg) < 2 {
//TODO: no user specified, use current
fancyUser = "[default:getUser]"
fancyHostPath = strings.Split(fancyArg[0], ":")
@ -639,8 +628,8 @@ func parseNonSwitchArgs(a []string) (user, host, path string, isDest bool, other
return fancyUser, fancyHost, fancyPath, isDest, otherArgs
}
func launchTuns(conn *xsnet.Conn /*remoteHost string,*/, tuns string) {
/*remAddrs, _ := net.LookupHost(remoteHost)*/ //nolint:gocritic,nolintlint
func launchTuns(conn *xsnet.Conn, remoteHost string, tuns string) {
/*remAddrs, _ := net.LookupHost(remoteHost)*/
if tuns == "" {
return
@ -689,12 +678,12 @@ func main() { //nolint: funlen, gocyclo
var (
isInteractive bool
vopt bool
gopt bool // true: login via password, asking server to generate authToken
gopt bool //login via password, asking server to generate authToken
dbg bool
shellMode bool // true: act as shell, false: file copier
cipherAlg string
hmacAlg string
kexAlg string
shellMode bool // if true act as shell, else file copier
cipherAlg string //cipher alg
hmacAlg string //hmac alg
kexAlg string //KEX/KEM alg
server string
port uint
cmdStr string
@ -714,7 +703,7 @@ func main() { //nolint: funlen, gocyclo
op []byte
)
// === Common (xs and xc) option parsing
//=== Common (xs and xc) option parsing
flag.BoolVar(&vopt, "v", false, "show version")
flag.BoolVar(&dbg, "d", false, "debug logging")
@ -742,18 +731,18 @@ func main() { //nolint: funlen, gocyclo
KEX_FRODOKEM_1344SHAKE
KEX_FRODOKEM_976AES
KEX_FRODOKEM_976SHAKE`)
flag.StringVar(&kcpMode, "K", "unused", "KCP `alg`, one of [KCP_NONE | KCP_AES | KCP_BLOWFISH | KCP_CAST5 | KCP_SM4 | KCP_SALSA20 | KCP_SIMPLEXOR | KCP_TEA | KCP_3DES | KCP_TWOFISH | KCP_XTEA] to use KCP (github.com/xtaci/kcp-go) reliable UDP instead of TCP") //nolint:lll
flag.UintVar(&port, "p", 2000, "``port") //nolint:gomnd,lll
//nolint:gocritic,nolintlint // flag.StringVar(&authCookie, "a", "", "auth cookie")
flag.StringVar(&kcpMode, "K", "unused", "KCP `alg`, one of [KCP_NONE | KCP_AES | KCP_BLOWFISH | KCP_CAST5 | KCP_SM4 | KCP_SALSA20 | KCP_SIMPLEXOR | KCP_TEA | KCP_3DES | KCP_TWOFISH | KCP_XTEA] to use KCP (github.com/xtaci/kcp-go) reliable UDP instead of TCP")
flag.UintVar(&port, "p", 2000, "``port")
//flag.StringVar(&authCookie, "a", "", "auth cookie")
flag.BoolVar(&chaffEnabled, "e", true, "enable chaff pkts")
flag.UintVar(&chaffFreqMin, "f", 100, "chaff pkt freq min `msecs`") //nolint:gomnd
flag.UintVar(&chaffFreqMax, "F", 5000, "chaff pkt freq max `msecs`") //nolint:gomnd
flag.UintVar(&chaffBytesMax, "B", 64, "chaff pkt size max `bytes`") //nolint:gomnd
flag.UintVar(&chaffFreqMin, "f", 100, "chaff pkt freq min `msecs`")
flag.UintVar(&chaffFreqMax, "F", 5000, "chaff pkt freq max `msecs`")
flag.UintVar(&chaffBytesMax, "B", 64, "chaff pkt size max `bytes`")
flag.StringVar(&cpuprofile, "cpuprofile", "", "write cpu profile to <`file`>")
flag.StringVar(&memprofile, "memprofile", "", "write memory profile to <`file`>")
// === xc vs. xs option parsing
//=== xc vs. xs option parsing
// Find out what program we are (shell or copier)
myPath := strings.Split(os.Args[0], string(os.PathSeparator))
@ -770,7 +759,7 @@ func main() { //nolint: funlen, gocyclo
flag.Usage = usageShell
} else {
flag.BoolVar(&copyQuiet, "q", false, "do not output progress bar during copy")
flag.UintVar(&copyLimitBPS, "L", 8589934592, "copy max rate in bytes per sec") //nolint:gomnd
flag.UintVar(&copyLimitBPS, "L", 8589934592, "copy max rate in bytes per sec")
flag.Usage = usageCp
}
flag.Parse()
@ -780,7 +769,7 @@ func main() { //nolint: funlen, gocyclo
exitWithStatus(0)
}
// === Profiling instrumentation
//=== Profiling instrumentation
if cpuprofile != "" {
f, err := os.Create(cpuprofile)
@ -790,19 +779,19 @@ func main() { //nolint: funlen, gocyclo
defer f.Close()
fmt.Println("StartCPUProfile()")
if err := pprof.StartCPUProfile(f); err != nil {
log.Fatal("could not start CPU profile: ", err) //nolint:gocritic
log.Fatal("could not start CPU profile: ", err)
} else {
defer pprof.StopCPUProfile()
}
go func() { http.ListenAndServe("localhost:6060", nil) }() //nolint:errcheck,gosec
go func() { http.ListenAndServe("localhost:6060", nil) }()
}
// === User, host, port and path args for file operations, if applicable
//=== User, host, port and path args for file operations, if applicable
remoteUser, remoteHost, tmpPath, pathIsDest, otherArgs :=
parseNonSwitchArgs(flag.Args())
//nolint:gocritic,nolintlint // fmt.Println("otherArgs:", otherArgs)
//fmt.Println("otherArgs:", otherArgs)
// Set defaults if user doesn't specify user, path or port
var uname string
@ -820,7 +809,7 @@ func main() { //nolint: funlen, gocyclo
tmpPath = "."
}
// === Copy mode arg and copy src/dest setup
//=== Copy mode arg and copy src/dest setup
var fileArgs string
if !shellMode /*&& tmpPath != ""*/ {
@ -853,15 +842,15 @@ func main() { //nolint: funlen, gocyclo
}
}
// === Do some final option consistency checks
//=== Do some final option consistency checks
//nolint:gocritic,nolintlint // fmt.Println("server finally is:", server)
//fmt.Println("server finally is:", server)
if flag.NFlag() == 0 && server == "" {
flag.Usage()
exitWithStatus(0)
}
if cmdStr != "" && (len(copySrc) != 0 || copyDst != "") {
if len(cmdStr) != 0 && (len(copySrc) != 0 || len(copyDst) != 0) {
log.Fatal("incompatible options -- either cmd (-x) or copy ops but not both")
}
@ -874,18 +863,18 @@ func main() { //nolint: funlen, gocyclo
if dbg {
log.SetOutput(Log)
} else {
log.SetOutput(io.Discard)
log.SetOutput(ioutil.Discard)
}
// === Auth token fetch for login
//=== Auth token fetch for login
if !gopt {
// See if we can log in via an auth token
u, _ := user.Current()
ab, aerr := os.ReadFile(fmt.Sprintf("%s/.xs_id", u.HomeDir))
ab, aerr := ioutil.ReadFile(fmt.Sprintf("%s/.xs_id", u.HomeDir))
if aerr == nil {
for _, line := range strings.Split(string(ab), "\n") {
line += "\n"
line = line + "\n"
idx := strings.Index(line, remoteHost+":"+uname)
if idx >= 0 {
line = line[idx:]
@ -905,8 +894,8 @@ func main() { //nolint: funlen, gocyclo
}
runtime.GC()
// === Enforce some sane min/max vals on chaff flags
if chaffFreqMin < 2 { //nolint:gomnd
//=== Enforce some sane min/max vals on chaff flags
if chaffFreqMin < 2 {
chaffFreqMin = 2
}
if chaffFreqMax == 0 {
@ -916,7 +905,7 @@ func main() { //nolint: funlen, gocyclo
chaffBytesMax = 64
}
// === Shell vs. Copy mode chaff and cmd setup
//=== Shell vs. Copy mode chaff and cmd setup
if shellMode {
// We must make the decision about interactivity before Dial()
@ -926,7 +915,7 @@ func main() { //nolint: funlen, gocyclo
op = []byte{'A'}
chaffFreqMin = 2
chaffFreqMax = 10
} else if cmdStr == "" {
} else if len(cmdStr) == 0 {
op = []byte{'s'}
isInteractive = true
} else {
@ -947,18 +936,18 @@ func main() { //nolint: funlen, gocyclo
// client->server file copy
// src file list is in copySrc
op = []byte{'D'}
//nolint:gocritic,nolintlint // fmt.Println("client->server copy:", string(copySrc), "->", copyDst)
//fmt.Println("client->server copy:", string(copySrc), "->", copyDst)
cmdStr = copyDst
} else {
// server->client file copy
// remote src file(s) in copyDsr
op = []byte{'S'}
//nolint:gocritic,nolintlint // fmt.Println("server->client copy:", string(copySrc), "->", copyDst)
//fmt.Println("server->client copy:", string(copySrc), "->", copyDst)
cmdStr = string(copySrc)
}
}
// === TCP / KCP Dial setup
//=== TCP / KCP Dial setup
proto := "tcp"
if kcpMode != "unused" {
@ -967,10 +956,10 @@ func main() { //nolint: funlen, gocyclo
conn, err := xsnet.Dial(proto, server, cipherAlg, hmacAlg, kexAlg, kcpMode)
if err != nil {
fmt.Println(err)
exitWithStatus(XSNetDialFailed)
exitWithStatus(3)
}
// === Shell terminal mode (Shell vs. Copy) setup
//=== Shell terminal mode (Shell vs. Copy) setup
// Set stdin in raw mode if it's an interactive session
// TODO: send flag to server side indicating this
@ -978,7 +967,7 @@ func main() { //nolint: funlen, gocyclo
var oldState *xs.State
defer conn.Close()
// === From this point on, conn is a secure encrypted channel
//=== From this point on, conn is a secure encrypted channel
if shellMode {
if isatty.IsTerminal(os.Stdin.Fd()) {
@ -994,20 +983,20 @@ func main() { //nolint: funlen, gocyclo
}
}
// === Login phase
//=== Login phase
// Start login timeout here and disconnect if user/pass phase stalls
// iloginImpatience := time.AfterFunc(20*time.Second, func() {
// i fmt.Printf(" .. [you still there? Waiting for a password.]")
// i})
loginTimeout := time.AfterFunc(30*time.Second, func() { //nolint:gomnd
//iloginImpatience := time.AfterFunc(20*time.Second, func() {
//i fmt.Printf(" .. [you still there? Waiting for a password.]")
//i})
loginTimeout := time.AfterFunc(30*time.Second, func() {
restoreTermState(oldState)
fmt.Printf(" .. [login timeout]\n")
exitWithStatus(xsnet.CSOLoginTimeout)
})
if authCookie == "" {
// No auth token, prompt for password
if len(authCookie) == 0 {
//No auth token, prompt for password
fmt.Printf("Gimme cookie:")
ab, e := xs.ReadPassword(os.Stdin.Fd())
fmt.Printf("\r\n")
@ -1017,43 +1006,43 @@ func main() { //nolint: funlen, gocyclo
authCookie = string(ab)
}
//nolint:gocritic,nolintlint // i_ = loginImpatience.Stop()
//i_ = loginImpatience.Stop()
_ = loginTimeout.Stop()
// Security scrub
runtime.GC()
// === Session param and TERM setup
//=== Session param and TERM setup
// Set up session params and send over to server
rec := xs.NewSession(op, []byte(uname), []byte(remoteHost), []byte(os.Getenv("TERM")), []byte(cmdStr), []byte(authCookie), 0)
sendErr := sendSessionParams(&conn, rec)
if sendErr != nil {
restoreTermState(oldState)
rec.SetStatus(ServerRejectedSecureProposal)
rec.SetStatus(254)
fmt.Fprintln(os.Stderr, "Error: server rejected secure proposal params or login timed out")
exitWithStatus(int(rec.Status()))
//nolint:gocritic,nolintlint // log.Fatal(sendErr)
//log.Fatal(sendErr)
}
// Security scrub
//Security scrub
authCookie = "" //nolint: ineffassign
runtime.GC()
// === Login Auth
//=== Login Auth
// === Read auth reply from server
//=== Read auth reply from server
authReply := make([]byte, 1) // bool: 0 = fail, 1 = pass
_, err = conn.Read(authReply)
if err != nil {
// === Exit if auth reply not received
//=== Exit if auth reply not received
fmt.Fprintln(os.Stderr, "Error reading auth reply")
rec.SetStatus(ErrReadingAuthReply)
rec.SetStatus(255)
} else if authReply[0] == 0 {
// === .. or if auth failed
//=== .. or if auth failed
fmt.Fprintln(os.Stderr, rejectUserMsg())
rec.SetStatus(GeneralProtocolErr)
rec.SetStatus(255)
} else {
// === Set up chaffing to server
//=== Set up chaffing to server
conn.SetupChaff(chaffFreqMin, chaffFreqMax, chaffBytesMax) // enable client->server chaffing
if chaffEnabled {
// #gv:s/label=\"main\$2\"/label=\"deferCloseChaff\"/
@ -1063,16 +1052,16 @@ func main() { //nolint: funlen, gocyclo
defer conn.ShutdownChaff()
}
// === (goroutine) Start keepAliveWorker for tunnels
//=== (goroutine) Start keepAliveWorker for tunnels
// #gv:s/label=\"main\$1\"/label=\"tunKeepAlive\"/
// TODO:.gv:main:1:tunKeepAlive
// [1]: better to always send tunnel keepAlives even if client didn't specify
//[1]: better to always send tunnel keepAlives even if client didn't specify
// any, to prevent listeners from knowing this.
// [1] if tunSpecStr != "" {
//[1] if tunSpecStr != "" {
keepAliveWorker := func() {
for {
// Add a bit of jitter to keepAlive so it doesn't stand out quite as much
time.Sleep(time.Duration(2000-rand.Intn(200)) * time.Millisecond) //nolint:gosec,gomnd
time.Sleep(time.Duration(2000-rand.Intn(200)) * time.Millisecond) //nolint:gosec
// FIXME: keepAlives should probably have small random packet len/data as well
// to further obscure them vs. interactive or tunnel data
// keepAlives must be >=2 bytes, due to processing elsewhere
@ -1080,15 +1069,15 @@ func main() { //nolint: funlen, gocyclo
}
}
go keepAliveWorker()
// [1]}
//[1]}
// === Session entry (shellMode or copyMode)
//=== Session entry (shellMode or copyMode)
if shellMode {
// === (shell) launch tunnels
launchTuns(&conn /*remoteHost,*/, tunSpecStr)
//=== (shell) launch tunnels
launchTuns(&conn, remoteHost, tunSpecStr)
doShellMode(isInteractive, &conn, oldState, rec)
} else {
// === (.. or file copy)
//=== (.. or file copy)
s, _ := doCopyMode(&conn, pathIsDest, fileArgs, copyQuiet, copyLimitBPS, rec)
rec.SetStatus(s)
}
@ -1104,7 +1093,7 @@ func main() { //nolint: funlen, gocyclo
oldState = nil
}
// === Exit
//=== Exit
exitWithStatus(int(rec.Status()))
}
@ -1139,7 +1128,7 @@ func exitWithStatus(status int) {
defer f.Close()
runtime.GC() // get up-to-date statistics
if err := pprof.WriteHeapProfile(f); err != nil {
log.Fatal("could not write memory profile: ", err) //nolint:gocritic
log.Fatal("could not write memory profile: ", err)
}
}

View file

@ -1195,7 +1195,6 @@ func (hc Conn) Read(b []byte) (n int, err error) {
var hmacIn [HMAC_CHK_SZ]uint8
var payloadLen uint32
//------------- Read ctrl/status opcode --------------------
// Read ctrl/status opcode (CSOHmacInvalid on hmac mismatch)
err = binary.Read(*hc.c, binary.BigEndian, &ctrlStatOp)
if err != nil {
@ -1216,9 +1215,7 @@ func (hc Conn) Read(b []byte) (n int, err error) {
hc.Close()
return 0, errors.New("** ALERT - remote end detected HMAC mismatch - possible channel tampering **")
}
//----------------------------------------------------------
//------------------ Read HMAC len ------------------------
// Read the hmac and payload len first
err = binary.Read(*hc.c, binary.BigEndian, &hmacIn)
if err != nil {
@ -1233,9 +1230,7 @@ func (hc Conn) Read(b []byte) (n int, err error) {
logger.LogDebug(etxt)
return 0, errors.New(etxt)
}
//----------------------------------------------------------
//------------------ Read Payload len ---------------------
err = binary.Read(*hc.c, binary.BigEndian, &payloadLen)
if err != nil {
if err.Error() == "EOF" {
@ -1249,7 +1244,6 @@ func (hc Conn) Read(b []byte) (n int, err error) {
logger.LogDebug(etxt)
return 0, errors.New(etxt)
}
//----------------------------------------------------------
if payloadLen > MAX_PAYLOAD_LEN {
logger.LogDebug(fmt.Sprintf("[Insane payloadLen:%v]\n", payloadLen))
@ -1257,7 +1251,6 @@ func (hc Conn) Read(b []byte) (n int, err error) {
return 1, errors.New("Insane payloadLen")
}
//-------------------- Read Payload ------------------------
var payloadBytes = make([]byte, payloadLen)
n, err = io.ReadFull(*hc.c, payloadBytes)
if err != nil {
@ -1272,14 +1265,12 @@ func (hc Conn) Read(b []byte) (n int, err error) {
logger.LogDebug(etxt)
return 0, errors.New(etxt)
}
//----------------------------------------------------------
if hc.logCipherText {
log.Printf(" <:ctext:\r\n%s\r\n", hex.Dump(payloadBytes[:n]))
}
//fmt.Printf(" <:ctext:\r\n%s\r\n", hex.Dump(payloadBytes[:n]))
//---------------- Verify Payload via HMAC -----------------
hc.rm.Write(payloadBytes) // Calc hmac on received data
hTmp := hc.rm.Sum(nil)[0:HMAC_CHK_SZ]
//log.Printf("<%04x) HMAC:(i)%s (c)%02x\r\n", decryptN, hex.EncodeToString([]byte(hmacIn[0:])), hTmp)
@ -1289,9 +1280,7 @@ func (hc Conn) Read(b []byte) (n int, err error) {
logger.LogDebug(fmt.Sprintln("** ALERT - detected HMAC mismatch, possible channel tampering **"))
_, _ = (*hc.c).Write([]byte{CSOHmacInvalid})
}
//----------------------------------------------------------
//------------------- Decrypt Payload ----------------------
db := bytes.NewBuffer(payloadBytes[:n]) //copying payloadBytes to db
// The StreamReader acts like a pipe, decrypting
// whatever is available and forwarding the result
@ -1300,7 +1289,6 @@ func (hc Conn) Read(b []byte) (n int, err error) {
// The caller isn't necessarily reading the full payload so we need
// to decrypt to an intermediate buffer, draining it on demand of caller
decryptN, err := rs.Read(payloadBytes)
//----------------------------------------------------------
if hc.logPlainText {
log.Printf(" <:ptext:\r\n%s\r\n", hex.Dump(payloadBytes[:n]))
@ -1309,7 +1297,6 @@ func (hc Conn) Read(b []byte) (n int, err error) {
log.Println("xsnet.Read():", err)
//panic(err)
} else {
//------------ Discard Padding ---------------------
// Padding: Read padSide, padLen, (padding | d) or (d | padding)
padSide := payloadBytes[0]
padLen := payloadBytes[1]
@ -1320,17 +1307,15 @@ func (hc Conn) Read(b []byte) (n int, err error) {
} else {
payloadBytes = payloadBytes[0 : len(payloadBytes)-int(padLen)]
}
//--------------------------------------------------
switch ctrlStatOp {
case CSOChaff:
// Throw away pkt if it's chaff (ie., caller to Read() won't see this data)
if ctrlStatOp == CSOChaff {
log.Printf("[Chaff pkt, discarded (len %d)]\n", decryptN)
case CSOTermSize:
} else if ctrlStatOp == CSOTermSize {
fmt.Sscanf(string(payloadBytes), "%d %d", &hc.Rows, &hc.Cols)
log.Printf("[TermSize pkt: rows %v cols %v]\n", hc.Rows, hc.Cols)
hc.WinCh <- WinSize{hc.Rows, hc.Cols}
case CSOExitStatus:
} else if ctrlStatOp == CSOExitStatus {
if len(payloadBytes) > 0 {
hc.SetStatus(CSOType(binary.BigEndian.Uint32(payloadBytes)))
} else {
@ -1338,7 +1323,7 @@ func (hc Conn) Read(b []byte) (n int, err error) {
hc.SetStatus(CSETruncCSO)
}
hc.Close()
case CSOTunSetup:
} else if ctrlStatOp == CSOTunSetup {
// server side tunnel setup in response to client
lport := binary.BigEndian.Uint16(payloadBytes[0:2])
rport := binary.BigEndian.Uint16(payloadBytes[2:4])
@ -1350,7 +1335,7 @@ func (hc Conn) Read(b []byte) (n int, err error) {
logger.LogDebug(fmt.Sprintf("[Server] Got CSOTunSetup [%d:%d]", lport, rport))
}
(*hc.tuns)[rport].Ctl <- 'd' // Dial() rport
case CSOTunSetupAck:
} else if ctrlStatOp == CSOTunSetupAck {
lport := binary.BigEndian.Uint16(payloadBytes[0:2])
rport := binary.BigEndian.Uint16(payloadBytes[2:4])
if _, ok := (*hc.tuns)[rport]; !ok {
@ -1361,7 +1346,7 @@ func (hc Conn) Read(b []byte) (n int, err error) {
logger.LogDebug(fmt.Sprintf("[Client] Got CSOTunSetupAck [%d:%d]", lport, rport))
}
(*hc.tuns)[rport].Ctl <- 'a' // Listen() for lport connection
case CSOTunRefused:
} else if ctrlStatOp == CSOTunRefused {
// client side receiving CSOTunRefused means the remote side
// could not dial() rport. So we cannot yet listen()
// for client-side on lport.
@ -1373,7 +1358,7 @@ func (hc Conn) Read(b []byte) (n int, err error) {
} else {
logger.LogDebug(fmt.Sprintf("[Client] CSOTunRefused on already-closed tun [%d:%d]", lport, rport))
}
case CSOTunDisconn:
} else if ctrlStatOp == CSOTunDisconn {
// server side's rport has disconnected (server lost)
lport := binary.BigEndian.Uint16(payloadBytes[0:2])
rport := binary.BigEndian.Uint16(payloadBytes[2:4])
@ -1383,7 +1368,7 @@ func (hc Conn) Read(b []byte) (n int, err error) {
} else {
logger.LogDebug(fmt.Sprintf("[Client] CSOTunDisconn on already-closed tun [%d:%d]", lport, rport))
}
case CSOTunHangup:
} else if ctrlStatOp == CSOTunHangup {
// client side's lport has hung up
lport := binary.BigEndian.Uint16(payloadBytes[0:2])
rport := binary.BigEndian.Uint16(payloadBytes[2:4])
@ -1393,7 +1378,7 @@ func (hc Conn) Read(b []byte) (n int, err error) {
} else {
logger.LogDebug(fmt.Sprintf("[Server] CSOTunHangup to already-closed tun [%d:%d]", lport, rport))
}
case CSOTunData:
} else if ctrlStatOp == CSOTunData {
lport := binary.BigEndian.Uint16(payloadBytes[0:2])
rport := binary.BigEndian.Uint16(payloadBytes[2:4])
//fmt.Printf("[Got CSOTunData: [lport %d:rport %d] data:%v\n", lport, rport, payloadBytes[4:])
@ -1406,7 +1391,7 @@ func (hc Conn) Read(b []byte) (n int, err error) {
} else {
logger.LogDebug(fmt.Sprintf("[Attempt to write data to closed tun [%d:%d]", lport, rport))
}
case CSOTunKeepAlive:
} else if ctrlStatOp == CSOTunKeepAlive {
// client side has sent keepalive for tunnels -- if client
// dies or exits unexpectedly the absence of this will
// let the server know to hang up on Dial()ed server rports.
@ -1417,9 +1402,9 @@ func (hc Conn) Read(b []byte) (n int, err error) {
t.KeepAlive = 0
hc.Unlock()
}
case CSONone:
} else if ctrlStatOp == CSONone {
hc.dBuf.Write(payloadBytes)
default:
} else {
logger.LogDebug(fmt.Sprintf("[Unknown CSOType:%d]", ctrlStatOp))
}
}