mirror of
https://gogs.blitter.com/RLabs/xs
synced 2024-08-14 10:26:42 +00:00
The Great Renaming: hkexsh -> xs (Xperimental Shell)
Signed-off-by: Russ Magee <rmagee@gmail.com>
This commit is contained in:
parent
423410bb40
commit
b19687c80b
41 changed files with 1101 additions and 813 deletions
18
xsd/Makefile
Normal file
18
xsd/Makefile
Normal file
|
@ -0,0 +1,18 @@
|
|||
.PHONY: clean all vis lint
|
||||
|
||||
EXTPKGS = binary,bytes,crypto,encoding,errors,flag,fmt,internal,io,log,net,os,path,runtime,time,strings,sync,syscall
|
||||
EXE = $(notdir $(shell pwd))
|
||||
|
||||
all:
|
||||
go build $(BUILDOPTS) .
|
||||
|
||||
clean:
|
||||
$(RM) $(EXE) $(EXE).exe
|
||||
|
||||
vis:
|
||||
go-callvis -file xsd-vis -format png -ignore $(EXTPKGS) -group pkg,type .
|
||||
../fixup-gv.sh xsd.go && cat xsd-vis.gv | dot -Tpng -oxsd-vis-fixedup.png
|
||||
|
||||
lint:
|
||||
-gometalinter --deadline=60s | sort
|
||||
|
BIN
xsd/hkexshd-vis.png
Normal file
BIN
xsd/hkexshd-vis.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 581 KiB |
BIN
xsd/xsd-vis-fixedup.png
Normal file
BIN
xsd/xsd-vis-fixedup.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 782 KiB |
347
xsd/xsd-vis.gv
Executable file
347
xsd/xsd-vis.gv
Executable file
|
@ -0,0 +1,347 @@
|
|||
digraph gocallvis {
|
||||
label="blitter.com/go/xs/xsd";
|
||||
labeljust="l";
|
||||
fontname="Arial";
|
||||
fontsize="14";
|
||||
rankdir="LR";
|
||||
bgcolor="lightgray";
|
||||
style="solid";
|
||||
penwidth="0.5";
|
||||
pad="0.0";
|
||||
nodesep="0.35";
|
||||
|
||||
node [shape="ellipse" style="filled" fillcolor="honeydew" fontname="Verdana" penwidth="1.0" margin="0.05,0.0"];
|
||||
edge [minlen="2"]
|
||||
|
||||
subgraph "cluster_focus" {
|
||||
label="main";
|
||||
labelloc="t";
|
||||
labeljust="c";
|
||||
fontsize="18";
|
||||
bgcolor="#e6ecfa";
|
||||
|
||||
"blitter.com/go/xs/xsd.main$2" [ style="dotted,filled" fillcolor="lightblue" label="main$2" ]
|
||||
"blitter.com/go/xs/xsd.GenAuthToken" [ fillcolor="lightblue" label="GenAuthToken" penwidth="1.5" ]
|
||||
"blitter.com/go/xs/xsd.runShellAs" [ fillcolor="lightblue" label="runShellAs" penwidth="0.5" ]
|
||||
"blitter.com/go/xs/xsd.runShellAs$1" [ fillcolor="lightblue" label="deferPtmxClose" style="dotted,filled" ]
|
||||
"blitter.com/go/xs/xsd.ptsName" [ label="ptsName" penwidth="0.5" fillcolor="lightblue" ]
|
||||
"blitter.com/go/xs/xsd.ioctl" [ fillcolor="lightblue" label="ioctl" penwidth="0.5" ]
|
||||
"blitter.com/go/xs/xsd.runShellAs$2" [ fillcolor="lightblue" label="termResizeWatcher" style="dotted,filled" ]
|
||||
"blitter.com/go/xs/xsd.runShellAs$3" [ style="dotted,filled" fillcolor="lightblue" label="stdinToPtyWorker" ]
|
||||
"blitter.com/go/xs/xsd.runShellAs$4" [ style="dotted,filled" fillcolor="lightblue" label="deferChaffShutdown" ]
|
||||
"blitter.com/go/xs/xsd.runShellAs$5" [ fillcolor="lightblue" label="ptyToStdoutWorker" style="dotted,filled" ]
|
||||
"blitter.com/go/xs/xsd.runShellAs$6" [ fillcolor="lightblue" label="runShellAs$6" style="dotted,filled" ]
|
||||
"blitter.com/go/xs/xsd.runClientToServerCopyAs" [ fillcolor="lightblue" label="runClientToServerCopyAs" penwidth="0.5" ]
|
||||
"blitter.com/go/xs/xsd.runServerToClientCopyAs" [ fillcolor="lightblue" label="runServerToClientCopyAs" penwidth="0.5" ]
|
||||
"blitter.com/go/xs/xsd.main" [ fillcolor="lightblue" label="main" penwidth="0.5" ]
|
||||
"blitter.com/go/xs/xsd.main$1" [ fillcolor="lightblue" label="main$1" style="dotted,filled" ]
|
||||
|
||||
subgraph "cluster_blitter.com/go/goutmp" {
|
||||
URL="/?f=blitter.com/go/goutmp";
|
||||
tooltip="package: blitter.com/go/goutmp";
|
||||
fontsize="16";
|
||||
style="filled";
|
||||
fillcolor="lightyellow";
|
||||
fontname="bold";
|
||||
rank="sink";
|
||||
label="[goutmp]";
|
||||
penwidth="0.8";
|
||||
|
||||
"blitter.com/go/goutmp.GetHost" [ fillcolor="moccasin" label="GetHost" penwidth="1.5" ]
|
||||
"blitter.com/go/goutmp.Put_utmp" [ fillcolor="moccasin" label="Put_utmp" penwidth="1.5" ]
|
||||
"blitter.com/go/goutmp.Unput_utmp" [ label="Unput_utmp" penwidth="1.5" fillcolor="moccasin" ]
|
||||
"blitter.com/go/goutmp.Put_lastlog_entry" [ fillcolor="moccasin" label="Put_lastlog_entry" penwidth="1.5" ]
|
||||
|
||||
}
|
||||
|
||||
subgraph "cluster_blitter.com/go/xs" {
|
||||
fontsize="16";
|
||||
fontname="bold";
|
||||
rank="sink";
|
||||
penwidth="0.8";
|
||||
fillcolor="lightyellow";
|
||||
label="[xs]";
|
||||
URL="/?f=blitter.com/go/xs";
|
||||
tooltip="package: blitter.com/go/xs";
|
||||
style="filled";
|
||||
|
||||
"blitter.com/go/xs.AuthUserByToken" [ fillcolor="moccasin" label="AuthUserByToken" penwidth="1.5" ]
|
||||
"blitter.com/go/xs.AuthUserByPasswd" [ label="AuthUserByPasswd" penwidth="1.5" fillcolor="moccasin" ]
|
||||
|
||||
subgraph "cluster_*blitter.com/go/xs.Session" {
|
||||
tooltip="type: *blitter.com/go/xs.Session";
|
||||
penwidth="0.5";
|
||||
fontsize="15";
|
||||
fontcolor="#222222";
|
||||
labelloc="b";
|
||||
style="rounded,filled";
|
||||
fillcolor="wheat2";
|
||||
label="(*Session)";
|
||||
|
||||
"(*blitter.com/go/xs.Session).SetOp" [ fillcolor="moccasin" label="SetOp" penwidth="1.5" ]
|
||||
"(*blitter.com/go/xs.Session).SetWho" [ penwidth="1.5" fillcolor="moccasin" label="SetWho" ]
|
||||
"(*blitter.com/go/xs.Session).SetConnHost" [ penwidth="1.5" fillcolor="moccasin" label="SetConnHost" ]
|
||||
"(*blitter.com/go/xs.Session).SetTermType" [ fillcolor="moccasin" label="SetTermType" penwidth="1.5" ]
|
||||
"(*blitter.com/go/xs.Session).SetCmd" [ fillcolor="moccasin" label="SetCmd" penwidth="1.5" ]
|
||||
"(*blitter.com/go/xs.Session).SetAuthCookie" [ fillcolor="moccasin" label="SetAuthCookie" penwidth="1.5" ]
|
||||
"(*blitter.com/go/xs.Session).ClearAuthCookie" [ fillcolor="moccasin" label="ClearAuthCookie" penwidth="1.5" ]
|
||||
|
||||
}
|
||||
|
||||
subgraph "cluster_blitter.com/go/xs.Session" {
|
||||
fillcolor="wheat2";
|
||||
label="(Session)";
|
||||
tooltip="type: blitter.com/go/xs.Session";
|
||||
penwidth="0.5";
|
||||
fontsize="15";
|
||||
fontcolor="#222222";
|
||||
labelloc="b";
|
||||
style="rounded,filled";
|
||||
|
||||
"(blitter.com/go/xs.Session).Op" [ fillcolor="moccasin" label="Op" penwidth="1.5" ]
|
||||
"(blitter.com/go/xs.Session).Who" [ fillcolor="moccasin" label="Who" penwidth="1.5" ]
|
||||
"(blitter.com/go/xs.Session).ConnHost" [ penwidth="1.5" fillcolor="moccasin" label="ConnHost" ]
|
||||
"(blitter.com/go/xs.Session).Cmd" [ penwidth="1.5" fillcolor="moccasin" label="Cmd" ]
|
||||
"(blitter.com/go/xs.Session).AuthCookie" [ label="AuthCookie" penwidth="1.5" fillcolor="moccasin" ]
|
||||
"(blitter.com/go/xs.Session).TermType" [ fillcolor="moccasin" label="TermType" penwidth="1.5" ]
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
subgraph "cluster_blitter.com/go/xs/logger" {
|
||||
penwidth="0.8";
|
||||
fontsize="16";
|
||||
style="filled";
|
||||
fontname="bold";
|
||||
URL="/?f=blitter.com/go/xs/logger";
|
||||
fillcolor="lightyellow";
|
||||
rank="sink";
|
||||
label="[logger]";
|
||||
tooltip="package: blitter.com/go/xs/logger";
|
||||
|
||||
"blitter.com/go/xs/logger.LogNotice" [ fillcolor="moccasin" label="LogNotice" penwidth="1.5" ]
|
||||
"blitter.com/go/xs/logger.LogDebug" [ fillcolor="moccasin" label="LogDebug" penwidth="1.5" ]
|
||||
"blitter.com/go/xs/logger.LogErr" [ fillcolor="moccasin" label="LogErr" penwidth="1.5" ]
|
||||
"blitter.com/go/xs/logger.New" [ label="New" penwidth="1.5" fillcolor="moccasin" ]
|
||||
|
||||
}
|
||||
|
||||
subgraph "cluster_blitter.com/go/xs/xsnet" {
|
||||
penwidth="0.8";
|
||||
label="[xsnet]";
|
||||
fillcolor="lightyellow";
|
||||
fontname="bold";
|
||||
rank="sink";
|
||||
URL="/?f=blitter.com/go/xs/xsnet";
|
||||
tooltip="package: blitter.com/go/xs/xsnet";
|
||||
fontsize="16";
|
||||
style="filled";
|
||||
|
||||
"blitter.com/go/xs/xsnet.Init" [ fillcolor="moccasin" label="Init" penwidth="1.5" ]
|
||||
"blitter.com/go/xs/xsnet.Listen" [ fillcolor="moccasin" label="Listen" penwidth="1.5" ]
|
||||
|
||||
subgraph "cluster_*blitter.com/go/xs/xsnet.Conn" {
|
||||
label="(*Conn)";
|
||||
tooltip="type: *blitter.com/go/xs/xsnet.Conn";
|
||||
penwidth="0.5";
|
||||
fontsize="15";
|
||||
fontcolor="#222222";
|
||||
labelloc="b";
|
||||
style="rounded,filled";
|
||||
fillcolor="wheat2";
|
||||
|
||||
"(*blitter.com/go/xs/xsnet.Conn).Close" [ fillcolor="moccasin" label="Close" penwidth="1.5" ]
|
||||
"(*blitter.com/go/xs/xsnet.Conn).RemoteAddr" [ fillcolor="moccasin" label="RemoteAddr" penwidth="1.5" ]
|
||||
"(*blitter.com/go/xs/xsnet.Conn).EnableChaff" [ fillcolor="moccasin" label="EnableChaff" penwidth="1.5" ]
|
||||
"(*blitter.com/go/xs/xsnet.Conn).DisableChaff" [ label="DisableChaff" penwidth="1.5" fillcolor="moccasin" ]
|
||||
"(*blitter.com/go/xs/xsnet.Conn).ShutdownChaff" [ fillcolor="moccasin" label="ShutdownChaff" penwidth="1.5" ]
|
||||
"(*blitter.com/go/xs/xsnet.Conn).SetStatus" [ fillcolor="moccasin" label="SetStatus" penwidth="1.5" ]
|
||||
"(*blitter.com/go/xs/xsnet.Conn).WritePacket" [ fillcolor="moccasin" label="WritePacket" penwidth="1.5" ]
|
||||
"(*blitter.com/go/xs/xsnet.Conn).SetupChaff" [ penwidth="1.5" fillcolor="moccasin" label="SetupChaff" ]
|
||||
|
||||
}
|
||||
|
||||
subgraph "cluster_*blitter.com/go/xs/xsnet.HKExListener" {
|
||||
labelloc="b";
|
||||
style="rounded,filled";
|
||||
fillcolor="wheat2";
|
||||
label="(*HKExListener)";
|
||||
tooltip="type: *blitter.com/go/xs/xsnet.HKExListener";
|
||||
penwidth="0.5";
|
||||
fontsize="15";
|
||||
fontcolor="#222222";
|
||||
|
||||
"(*blitter.com/go/xs/xsnet.HKExListener).Accept" [ penwidth="1.5" fillcolor="moccasin" label="Accept" ]
|
||||
|
||||
}
|
||||
|
||||
subgraph "cluster_blitter.com/go/xs/xsnet.Conn" {
|
||||
tooltip="type: blitter.com/go/xs/xsnet.Conn";
|
||||
penwidth="0.5";
|
||||
fontsize="15";
|
||||
fontcolor="#222222";
|
||||
labelloc="b";
|
||||
style="rounded,filled";
|
||||
fillcolor="wheat2";
|
||||
label="(Conn)";
|
||||
|
||||
"(blitter.com/go/xs/xsnet.Conn).Write" [ fillcolor="moccasin" label="Write" penwidth="1.5" ]
|
||||
|
||||
}
|
||||
|
||||
subgraph "cluster_blitter.com/go/xs/xsnet.HKExListener" {
|
||||
style="rounded,filled";
|
||||
fillcolor="wheat2";
|
||||
label="(HKExListener)";
|
||||
tooltip="type: blitter.com/go/xs/xsnet.HKExListener";
|
||||
penwidth="0.5";
|
||||
fontsize="15";
|
||||
fontcolor="#222222";
|
||||
labelloc="b";
|
||||
|
||||
"(blitter.com/go/xs/xsnet.HKExListener).Close" [ label="Close" penwidth="1.5" fillcolor="moccasin" ]
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
subgraph "cluster_context" {
|
||||
fontsize="16";
|
||||
fillcolor="#E0FFE1";
|
||||
label="[context]";
|
||||
tooltip="package: context";
|
||||
penwidth="0.8";
|
||||
style="filled";
|
||||
fontname="bold";
|
||||
rank="sink";
|
||||
URL="/?f=context";
|
||||
|
||||
|
||||
subgraph "cluster_context.deadlineExceededError" {
|
||||
labelloc="b";
|
||||
style="rounded,filled";
|
||||
fillcolor="#c2e3c2";
|
||||
label="(deadlineExceededError)";
|
||||
tooltip="type: context.deadlineExceededError";
|
||||
penwidth="0.5";
|
||||
fontsize="15";
|
||||
fontcolor="#222222";
|
||||
|
||||
"(context.deadlineExceededError).Error" [ fillcolor="#adedad" label="Error" penwidth="1.5" ]
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
subgraph "cluster_github.com/kr/pty" {
|
||||
label="[pty]";
|
||||
URL="/?f=github.com/kr/pty";
|
||||
penwidth="0.8";
|
||||
style="filled";
|
||||
fillcolor="lightyellow";
|
||||
rank="sink";
|
||||
fontsize="16";
|
||||
fontname="bold";
|
||||
tooltip="package: github.com/kr/pty";
|
||||
|
||||
"github.com/kr/pty.Start" [ fillcolor="moccasin" label="Start" penwidth="1.5" ]
|
||||
"github.com/kr/pty.Setsize" [ label="Setsize" penwidth="1.5" fillcolor="moccasin" ]
|
||||
|
||||
}
|
||||
|
||||
subgraph "cluster_github.com/pkg/errors" {
|
||||
fontname="bold";
|
||||
label="[errors]";
|
||||
style="filled";
|
||||
fontsize="16";
|
||||
fillcolor="lightyellow";
|
||||
rank="sink";
|
||||
URL="/?f=github.com/pkg/errors";
|
||||
tooltip="package: github.com/pkg/errors";
|
||||
penwidth="0.8";
|
||||
|
||||
|
||||
subgraph "cluster_*github.com/pkg/errors.fundamental" {
|
||||
fontcolor="#222222";
|
||||
labelloc="b";
|
||||
style="rounded,filled";
|
||||
fillcolor="wheat2";
|
||||
label="(*fundamental)";
|
||||
tooltip="type: *github.com/pkg/errors.fundamental";
|
||||
penwidth="0.5";
|
||||
fontsize="15";
|
||||
|
||||
"(*github.com/pkg/errors.fundamental).Error" [ label="Error" penwidth="1.5" fillcolor="moccasin" ]
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
"blitter.com/go/xs/xsd.main$2" -> "(*blitter.com/go/xs/xsnet.Conn).Close" [ arrowhead="normalnoneodiamond" color="saddlebrown" ]
|
||||
"blitter.com/go/xs/xsd.main$2" -> "(*blitter.com/go/xs.Session).SetOp" [ color="saddlebrown" ]
|
||||
"blitter.com/go/xs/xsd.main$2" -> "(*blitter.com/go/xs.Session).SetWho" [ color="saddlebrown" ]
|
||||
"blitter.com/go/xs/xsd.main$2" -> "(*blitter.com/go/xs.Session).SetConnHost" [ color="saddlebrown" ]
|
||||
"blitter.com/go/xs/xsd.main$2" -> "(*blitter.com/go/xs.Session).SetTermType" [ color="saddlebrown" ]
|
||||
"blitter.com/go/xs/xsd.main$2" -> "(*blitter.com/go/xs.Session).SetCmd" [ color="saddlebrown" ]
|
||||
"blitter.com/go/xs/xsd.main$2" -> "(*blitter.com/go/xs.Session).SetAuthCookie" [ color="saddlebrown" ]
|
||||
"blitter.com/go/xs/xsd.main$2" -> "(blitter.com/go/xs.Session).Op" [ color="saddlebrown" ]
|
||||
"blitter.com/go/xs/xsd.main$2" -> "(blitter.com/go/xs.Session).Who" [ color="saddlebrown" ]
|
||||
"blitter.com/go/xs/xsd.main$2" -> "(blitter.com/go/xs.Session).ConnHost" [ color="saddlebrown" ]
|
||||
"blitter.com/go/xs/xsd.main$2" -> "(blitter.com/go/xs.Session).Cmd" [ color="saddlebrown" ]
|
||||
"blitter.com/go/xs/xsd.main$2" -> "(blitter.com/go/xs.Session).AuthCookie" [ color="saddlebrown" ]
|
||||
"blitter.com/go/xs/xsd.main$2" -> "blitter.com/go/xs.AuthUserByToken" [ color="saddlebrown" ]
|
||||
"blitter.com/go/xs/xsd.main$2" -> "(*blitter.com/go/xs.Session).ClearAuthCookie" [ color="saddlebrown" ]
|
||||
"blitter.com/go/xs/xsd.main$2" -> "blitter.com/go/xs.AuthUserByPasswd" [ color="saddlebrown" ]
|
||||
"blitter.com/go/xs/xsd.main$2" -> "(blitter.com/go/xs/xsnet.Conn).Write" [ color="saddlebrown" ]
|
||||
"blitter.com/go/xs/xsd.main$2" -> "blitter.com/go/xs/logger.LogNotice" [ color="saddlebrown" ]
|
||||
"blitter.com/go/xs/xsd.main$2" -> "(*blitter.com/go/xs/xsnet.Conn).RemoteAddr" [ color="saddlebrown" ]
|
||||
"blitter.com/go/xs/xsd.main$2" -> "blitter.com/go/goutmp.GetHost" [ color="saddlebrown" ]
|
||||
"blitter.com/go/xs/xsd.main$2" -> "blitter.com/go/xs/xsd.GenAuthToken" [ ]
|
||||
"blitter.com/go/xs/xsd.main$2" -> "(blitter.com/go/xs.Session).TermType" [ color="saddlebrown" ]
|
||||
"blitter.com/go/xs/xsd.runShellAs" -> "github.com/kr/pty.Start" [ color="saddlebrown" ]
|
||||
"blitter.com/go/xs/xsd.runShellAs" -> "blitter.com/go/xs/xsd.runShellAs$1" [ arrowhead="normalnoneodiamond" ]
|
||||
"blitter.com/go/xs/xsd.ptsName" -> "blitter.com/go/xs/xsd.ioctl" [ ]
|
||||
"blitter.com/go/xs/xsd.runShellAs" -> "blitter.com/go/xs/xsd.ptsName" [ ]
|
||||
"blitter.com/go/xs/xsd.runShellAs" -> "blitter.com/go/goutmp.Put_utmp" [ color="saddlebrown" ]
|
||||
"blitter.com/go/xs/xsd.runShellAs$2" -> "blitter.com/go/goutmp.Unput_utmp" [ color="saddlebrown" ]
|
||||
"blitter.com/go/xs/xsd.runShellAs" -> "blitter.com/go/xs/xsd.runShellAs$2" [ arrowhead="normalnoneodiamond" ]
|
||||
"blitter.com/go/xs/xsd.runShellAs" -> "blitter.com/go/goutmp.Put_lastlog_entry" [ color="saddlebrown" ]
|
||||
"blitter.com/go/xs/xsd.runShellAs$3" -> "github.com/kr/pty.Setsize" [ color="saddlebrown" ]
|
||||
"blitter.com/go/xs/xsd.runShellAs" -> "blitter.com/go/xs/xsd.runShellAs$3" [ arrowhead="normalnoneodot" ]
|
||||
"blitter.com/go/xs/xsd.runShellAs$4" -> "(context.deadlineExceededError).Error" [ color="saddlebrown" style="dashed" ]
|
||||
"blitter.com/go/xs/xsd.runShellAs$4" -> "(*github.com/pkg/errors.fundamental).Error" [ style="dashed" color="saddlebrown" ]
|
||||
"blitter.com/go/xs/xsd.runShellAs" -> "blitter.com/go/xs/xsd.runShellAs$4" [ arrowhead="normalnoneodot" ]
|
||||
"blitter.com/go/xs/xsd.runShellAs" -> "(*blitter.com/go/xs/xsnet.Conn).EnableChaff" [ color="saddlebrown" ]
|
||||
"blitter.com/go/xs/xsd.runShellAs$5" -> "(*blitter.com/go/xs/xsnet.Conn).DisableChaff" [ color="saddlebrown" ]
|
||||
"blitter.com/go/xs/xsd.runShellAs$5" -> "(*blitter.com/go/xs/xsnet.Conn).ShutdownChaff" [ color="saddlebrown" ]
|
||||
"blitter.com/go/xs/xsd.runShellAs" -> "blitter.com/go/xs/xsd.runShellAs$5" [ arrowhead="normalnoneodiamond" ]
|
||||
"blitter.com/go/xs/xsd.runShellAs$6" -> "(context.deadlineExceededError).Error" [ style="dashed" color="saddlebrown" ]
|
||||
"blitter.com/go/xs/xsd.runShellAs$6" -> "(*github.com/pkg/errors.fundamental).Error" [ style="dashed" color="saddlebrown" ]
|
||||
"blitter.com/go/xs/xsd.runShellAs" -> "blitter.com/go/xs/xsd.runShellAs$6" [ arrowhead="normalnoneodot" ]
|
||||
"blitter.com/go/xs/xsd.runShellAs" -> "blitter.com/go/xs/logger.LogDebug" [ color="saddlebrown" ]
|
||||
"blitter.com/go/xs/xsd.runShellAs" -> "(*blitter.com/go/xs/xsnet.Conn).SetStatus" [ color="saddlebrown" ]
|
||||
"blitter.com/go/xs/xsd.main$2" -> "blitter.com/go/xs/xsd.runShellAs" [ ]
|
||||
"blitter.com/go/xs/xsd.main$2" -> "blitter.com/go/xs/logger.LogErr" [ color="saddlebrown" ]
|
||||
"blitter.com/go/xs/xsd.main$2" -> "(*blitter.com/go/xs/xsnet.Conn).SetStatus" [ color="saddlebrown" ]
|
||||
"blitter.com/go/xs/xsd.runClientToServerCopyAs" -> "(*blitter.com/go/xs/xsnet.Conn).EnableChaff" [ color="saddlebrown" ]
|
||||
"blitter.com/go/xs/xsd.runClientToServerCopyAs" -> "(*blitter.com/go/xs/xsnet.Conn).DisableChaff" [ arrowhead="normalnoneodiamond" color="saddlebrown" ]
|
||||
"blitter.com/go/xs/xsd.runClientToServerCopyAs" -> "(*blitter.com/go/xs/xsnet.Conn).ShutdownChaff" [ arrowhead="normalnoneodiamond" color="saddlebrown" ]
|
||||
"blitter.com/go/xs/xsd.main$2" -> "blitter.com/go/xs/xsd.runClientToServerCopyAs" [ ]
|
||||
"blitter.com/go/xs/xsd.main$2" -> "(*blitter.com/go/xs/xsnet.Conn).WritePacket" [ color="saddlebrown" ]
|
||||
"blitter.com/go/xs/xsd.runServerToClientCopyAs" -> "(*blitter.com/go/xs/xsnet.Conn).EnableChaff" [ color="saddlebrown" ]
|
||||
"blitter.com/go/xs/xsd.runServerToClientCopyAs" -> "(*blitter.com/go/xs/xsnet.Conn).DisableChaff" [ arrowhead="normalnoneodiamond" color="saddlebrown" ]
|
||||
"blitter.com/go/xs/xsd.runServerToClientCopyAs" -> "(*blitter.com/go/xs/xsnet.Conn).ShutdownChaff" [ color="saddlebrown" arrowhead="normalnoneodiamond" ]
|
||||
"blitter.com/go/xs/xsd.main$2" -> "blitter.com/go/xs/xsd.runServerToClientCopyAs" [ ]
|
||||
"blitter.com/go/xs/xsd.main" -> "blitter.com/go/xs/logger.New" [ color="saddlebrown" ]
|
||||
"blitter.com/go/xs/xsd.main" -> "blitter.com/go/xs/xsnet.Init" [ color="saddlebrown" ]
|
||||
"blitter.com/go/xs/xsd.main$1" -> "blitter.com/go/xs/logger.LogNotice" [ color="saddlebrown" ]
|
||||
"blitter.com/go/xs/xsd.main" -> "blitter.com/go/xs/xsd.main$1" [ arrowhead="normalnoneodot" ]
|
||||
"blitter.com/go/xs/xsd.main" -> "blitter.com/go/xs/xsnet.Listen" [ color="saddlebrown" ]
|
||||
"blitter.com/go/xs/xsd.main" -> "(blitter.com/go/xs/xsnet.HKExListener).Close" [ arrowhead="normalnoneodiamond" color="saddlebrown" ]
|
||||
"blitter.com/go/xs/xsd.main" -> "(*blitter.com/go/xs/xsnet.HKExListener).Accept" [ color="saddlebrown" ]
|
||||
"blitter.com/go/xs/xsd.main" -> "(*blitter.com/go/xs/xsnet.Conn).SetupChaff" [ color="saddlebrown" ]
|
||||
"blitter.com/go/xs/xsd.main" -> "blitter.com/go/xs/xsd.main$2" [ arrowhead="normalnoneodot" ]
|
||||
}
|
727
xsd/xsd.go
Executable file
727
xsd/xsd.go
Executable file
|
@ -0,0 +1,727 @@
|
|||
// xsd server
|
||||
//
|
||||
// Copyright (c) 2017-2019 Russell Magee
|
||||
// Licensed under the terms of the MIT license (see LICENSE.mit in this
|
||||
// distribution)
|
||||
//
|
||||
// golang implementation by Russ Magee (rmagee_at_gmail.com)
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"os/user"
|
||||
"path"
|
||||
"sync"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"blitter.com/go/goutmp"
|
||||
xs "blitter.com/go/xs"
|
||||
"blitter.com/go/xs/xsnet"
|
||||
"blitter.com/go/xs/logger"
|
||||
"github.com/kr/pty"
|
||||
)
|
||||
|
||||
var (
|
||||
version string
|
||||
gitCommit string // set in -ldflags by build
|
||||
|
||||
useSysLogin bool
|
||||
kcpMode string // set to a valid KCP BlockCrypt alg tag to use rather than TCP
|
||||
|
||||
// Log - syslog output (with no -d)
|
||||
Log *logger.Writer
|
||||
)
|
||||
|
||||
func ioctl(fd, request, argp uintptr) error {
|
||||
if _, _, e := syscall.Syscall6(syscall.SYS_IOCTL, fd, request, argp, 0, 0, 0); e != 0 {
|
||||
return e
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ptsName(fd uintptr) (string, error) {
|
||||
var n uintptr
|
||||
err := ioctl(fd, syscall.TIOCGPTN, uintptr(unsafe.Pointer(&n)))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return fmt.Sprintf("/dev/pts/%d", n), nil
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------- */
|
||||
// Perform a client->server copy
|
||||
func runClientToServerCopyAs(who, ttype string, conn *xsnet.Conn, fpath string, chaffing bool) (exitStatus uint32, err error) {
|
||||
u, _ := user.Lookup(who) // nolint: gosec
|
||||
var uid, gid uint32
|
||||
fmt.Sscanf(u.Uid, "%d", &uid) // nolint: gosec,errcheck
|
||||
fmt.Sscanf(u.Gid, "%d", &gid) // nolint: gosec,errcheck
|
||||
log.Println("uid:", uid, "gid:", gid)
|
||||
|
||||
// Need to clear server's env and set key vars of the
|
||||
// target user. This isn't perfect (TERM doesn't seem to
|
||||
// work 100%; ANSI/xterm colour isn't working even
|
||||
// if we set "xterm" or "ansi" here; and line count
|
||||
// reported by 'stty -a' defaults to 24 regardless
|
||||
// of client shell window used to run client.
|
||||
// Investigate -- rlm 2018-01-26)
|
||||
os.Clearenv()
|
||||
os.Setenv("HOME", u.HomeDir) // nolint: gosec,errcheck
|
||||
os.Setenv("TERM", ttype) // nolint: gosec,errcheck
|
||||
os.Setenv("HKEXSH", "1") // nolint: gosec,errcheck
|
||||
|
||||
var c *exec.Cmd
|
||||
cmdName := "/bin/tar"
|
||||
|
||||
var destDir string
|
||||
if path.IsAbs(fpath) {
|
||||
destDir = fpath
|
||||
} else {
|
||||
destDir = path.Join(u.HomeDir, fpath)
|
||||
}
|
||||
|
||||
cmdArgs := []string{"-xz", "-C", destDir}
|
||||
|
||||
// NOTE the lack of quotes around --xform option's sed expression.
|
||||
// When args are passed in exec() format, no quoting is required
|
||||
// (as this isn't input from a shell) (right? -rlm 20180823)
|
||||
//cmdArgs := []string{"-x", "-C", destDir, `--xform=s#.*/\(.*\)#\1#`}
|
||||
c = exec.Command(cmdName, cmdArgs...) // nolint: gosec
|
||||
|
||||
c.Dir = destDir
|
||||
|
||||
//If os.Clearenv() isn't called by server above these will be seen in the
|
||||
//client's session env.
|
||||
//c.Env = []string{"HOME=" + u.HomeDir, "SUDO_GID=", "SUDO_UID=", "SUDO_USER=", "SUDO_COMMAND=", "MAIL=", "LOGNAME="+who}
|
||||
//c.Dir = u.HomeDir
|
||||
c.SysProcAttr = &syscall.SysProcAttr{}
|
||||
c.SysProcAttr.Credential = &syscall.Credential{Uid: uid, Gid: gid}
|
||||
c.Stdin = conn
|
||||
c.Stdout = os.Stdout
|
||||
c.Stderr = os.Stderr
|
||||
|
||||
if chaffing {
|
||||
conn.EnableChaff()
|
||||
}
|
||||
defer conn.DisableChaff()
|
||||
defer conn.ShutdownChaff()
|
||||
|
||||
// Start the command (no pty)
|
||||
log.Printf("[%v %v]\n", cmdName, cmdArgs)
|
||||
err = c.Start() // returns immediately
|
||||
/////////////
|
||||
// NOTE: There is, apparently, a bug in Go stdlib here. Start()
|
||||
// can actually return immediately, on a command which *does*
|
||||
// start but exits quickly, with c.Wait() error
|
||||
// "c.Wait status: exec: not started".
|
||||
// As in this example, attempting a client->server copy to
|
||||
// a nonexistent remote dir (it's tar exiting right away, exitStatus
|
||||
// 2, stderr
|
||||
// /bin/tar -xz -C /home/someuser/nosuchdir
|
||||
// stderr: fork/exec /bin/tar: no such file or directory
|
||||
//
|
||||
// In this case, c.Wait() won't give us the real
|
||||
// exit status (is it lost?).
|
||||
/////////////
|
||||
if err != nil {
|
||||
log.Println("cmd exited immediately. Cannot get cmd.Wait().ExitStatus()")
|
||||
err = errors.New("cmd exited prematurely")
|
||||
//exitStatus = uint32(254)
|
||||
exitStatus = xsnet.CSEExecFail
|
||||
} else {
|
||||
if err := c.Wait(); err != nil {
|
||||
//fmt.Println("*** c.Wait() done ***")
|
||||
if exiterr, ok := err.(*exec.ExitError); ok {
|
||||
// The program has exited with an exit code != 0
|
||||
|
||||
// This works on both Unix and Windows. Although package
|
||||
// syscall is generally platform dependent, WaitStatus is
|
||||
// defined for both Unix and Windows and in both cases has
|
||||
// an ExitStatus() method with the same signature.
|
||||
if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
|
||||
exitStatus = uint32(status.ExitStatus())
|
||||
//err = errors.New("cmd returned nonzero status")
|
||||
log.Printf("Exit Status: %d\n", exitStatus)
|
||||
}
|
||||
}
|
||||
}
|
||||
log.Println("*** client->server cp finished ***")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Perform a server->client copy
|
||||
func runServerToClientCopyAs(who, ttype string, conn *xsnet.Conn, srcPath string, chaffing bool) (exitStatus uint32, err error) {
|
||||
u, err := user.Lookup(who)
|
||||
if err != nil {
|
||||
exitStatus = 1
|
||||
return
|
||||
}
|
||||
var uid, gid uint32
|
||||
_, _ = fmt.Sscanf(u.Uid, "%d", &uid) // nolint: gosec
|
||||
_, _ = fmt.Sscanf(u.Gid, "%d", &gid) // nolint: gosec
|
||||
log.Println("uid:", uid, "gid:", gid)
|
||||
|
||||
// Need to clear server's env and set key vars of the
|
||||
// target user. This isn't perfect (TERM doesn't seem to
|
||||
// work 100%; ANSI/xterm colour isn't working even
|
||||
// if we set "xterm" or "ansi" here; and line count
|
||||
// reported by 'stty -a' defaults to 24 regardless
|
||||
// of client shell window used to run client.
|
||||
// Investigate -- rlm 2018-01-26)
|
||||
os.Clearenv()
|
||||
_ = os.Setenv("HOME", u.HomeDir) // nolint: gosec
|
||||
_ = os.Setenv("TERM", ttype) // nolint: gosec
|
||||
_ = os.Setenv("HKEXSH", "1") // nolint: gosec
|
||||
|
||||
var c *exec.Cmd
|
||||
cmdName := "/bin/tar"
|
||||
if !path.IsAbs(srcPath) {
|
||||
srcPath = fmt.Sprintf("%s%c%s", u.HomeDir, os.PathSeparator, srcPath)
|
||||
}
|
||||
|
||||
srcDir, srcBase := path.Split(srcPath)
|
||||
cmdArgs := []string{"-cz", "-C", srcDir, "-f", "-", srcBase}
|
||||
|
||||
c = exec.Command(cmdName, cmdArgs...) // nolint: gosec
|
||||
|
||||
//If os.Clearenv() isn't called by server above these will be seen in the
|
||||
//client's session env.
|
||||
//c.Env = []string{"HOME=" + u.HomeDir, "SUDO_GID=", "SUDO_UID=", "SUDO_USER=", "SUDO_COMMAND=", "MAIL=", "LOGNAME="+who}
|
||||
c.Dir = u.HomeDir
|
||||
c.SysProcAttr = &syscall.SysProcAttr{}
|
||||
c.SysProcAttr.Credential = &syscall.Credential{Uid: uid, Gid: gid}
|
||||
c.Stdout = conn
|
||||
// Stderr sinkholing (or buffering to something other than stdout)
|
||||
// is important. Any extraneous output to tarpipe messes up remote
|
||||
// side as it's expecting pure tar data.
|
||||
// (For example, if user specifies abs paths, tar outputs
|
||||
// "Removing leading '/' from path names")
|
||||
stdErrBuffer := new(bytes.Buffer)
|
||||
c.Stderr = stdErrBuffer
|
||||
//c.Stderr = nil
|
||||
|
||||
if chaffing {
|
||||
conn.EnableChaff()
|
||||
}
|
||||
//defer conn.Close()
|
||||
defer conn.DisableChaff()
|
||||
defer conn.ShutdownChaff()
|
||||
|
||||
// Start the command (no pty)
|
||||
log.Printf("[%v %v]\n", cmdName, cmdArgs)
|
||||
err = c.Start() // returns immediately
|
||||
if err != nil {
|
||||
log.Printf("Command finished with error: %v", err)
|
||||
return xsnet.CSEExecFail, err // !?
|
||||
}
|
||||
if err := c.Wait(); err != nil {
|
||||
//fmt.Println("*** c.Wait() done ***")
|
||||
if exiterr, ok := err.(*exec.ExitError); ok {
|
||||
// The program has exited with an exit code != 0
|
||||
|
||||
// This works on both Unix and Windows. Although package
|
||||
// syscall is generally platform dependent, WaitStatus is
|
||||
// defined for both Unix and Windows and in both cases has
|
||||
// an ExitStatus() method with the same signature.
|
||||
if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
|
||||
exitStatus = uint32(status.ExitStatus())
|
||||
if len(stdErrBuffer.Bytes()) > 0 {
|
||||
log.Print(stdErrBuffer)
|
||||
}
|
||||
log.Printf("Exit Status: %d", exitStatus)
|
||||
}
|
||||
}
|
||||
}
|
||||
//fmt.Println("*** server->client cp finished ***")
|
||||
return
|
||||
}
|
||||
|
||||
// Run a command (via default shell) as a specific user
|
||||
//
|
||||
// Uses ptys to support commands which expect a terminal.
|
||||
// nolint: gocyclo
|
||||
func runShellAs(who, hname, ttype, cmd string, interactive bool, conn *xsnet.Conn, chaffing bool) (exitStatus uint32, err error) {
|
||||
var wg sync.WaitGroup
|
||||
u, err := user.Lookup(who)
|
||||
if err != nil {
|
||||
exitStatus = 1
|
||||
return
|
||||
}
|
||||
var uid, gid uint32
|
||||
_, _ = fmt.Sscanf(u.Uid, "%d", &uid) // nolint: gosec
|
||||
_, _ = fmt.Sscanf(u.Gid, "%d", &gid) // nolint: gosec
|
||||
log.Println("uid:", uid, "gid:", gid)
|
||||
|
||||
// Need to clear server's env and set key vars of the
|
||||
// target user. This isn't perfect (TERM doesn't seem to
|
||||
// work 100%; ANSI/xterm colour isn't working even
|
||||
// if we set "xterm" or "ansi" here; and line count
|
||||
// reported by 'stty -a' defaults to 24 regardless
|
||||
// of client shell window used to run client.
|
||||
// Investigate -- rlm 2018-01-26)
|
||||
os.Clearenv()
|
||||
_ = os.Setenv("HOME", u.HomeDir) // nolint: gosec
|
||||
_ = os.Setenv("TERM", ttype) // nolint: gosec
|
||||
_ = os.Setenv("HKEXSH", "1") // nolint: gosec
|
||||
|
||||
var c *exec.Cmd
|
||||
if interactive {
|
||||
if useSysLogin {
|
||||
// Use the server's login binary (post-auth
|
||||
// which is still done via our own bcrypt file)
|
||||
// Things UNIX login does, like print the 'motd',
|
||||
// and use the shell specified by /etc/passwd, will be done
|
||||
// automagically, at the cost of another external tool
|
||||
// dependency.
|
||||
//
|
||||
c = exec.Command("/bin/login", "-f", "-p", who) // nolint: gosec
|
||||
} else {
|
||||
c = exec.Command("/bin/bash", "-i", "-l") // nolint: gosec
|
||||
}
|
||||
} else {
|
||||
c = exec.Command("/bin/bash", "-c", cmd) // nolint: gosec
|
||||
}
|
||||
//If os.Clearenv() isn't called by server above these will be seen in the
|
||||
//client's session env.
|
||||
//c.Env = []string{"HOME=" + u.HomeDir, "SUDO_GID=", "SUDO_UID=", "SUDO_USER=", "SUDO_COMMAND=", "MAIL=", "LOGNAME="+who}
|
||||
c.Dir = u.HomeDir
|
||||
c.SysProcAttr = &syscall.SysProcAttr{}
|
||||
if useSysLogin {
|
||||
// If using server's login binary, drop to user creds
|
||||
// is taken care of by it.
|
||||
c.SysProcAttr.Credential = &syscall.Credential{}
|
||||
} else {
|
||||
c.SysProcAttr.Credential = &syscall.Credential{Uid: uid, Gid: gid}
|
||||
}
|
||||
c.Stdin = conn
|
||||
c.Stdout = conn
|
||||
c.Stderr = conn
|
||||
|
||||
// Start the command with a pty.
|
||||
ptmx, err := pty.Start(c) // returns immediately with ptmx file
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return xsnet.CSEPtyExecFail, err
|
||||
}
|
||||
// Make sure to close the pty at the end.
|
||||
// #gv:s/label=\"runShellAs\$1\"/label=\"deferPtmxClose\"/
|
||||
defer func() {
|
||||
//logger.LogDebug(fmt.Sprintf("[Exited process was %d]", c.Process.Pid))
|
||||
_ = ptmx.Close()
|
||||
}() // nolint: gosec
|
||||
|
||||
// get pty info for system accounting (who, lastlog)
|
||||
pts, pe := ptsName(ptmx.Fd())
|
||||
if pe != nil {
|
||||
return xsnet.CSEPtyGetNameFail, err
|
||||
}
|
||||
utmpx := goutmp.Put_utmp(who, pts, hname)
|
||||
defer func() { goutmp.Unput_utmp(utmpx) }()
|
||||
goutmp.Put_lastlog_entry("xs", who, pts, hname)
|
||||
|
||||
log.Printf("[%s]\n", cmd)
|
||||
if err != nil {
|
||||
log.Printf("Command finished with error: %v", err)
|
||||
} else {
|
||||
// Watch for term resizes
|
||||
// #gv:s/label=\"runShellAs\$2\"/label=\"termResizeWatcher\"/
|
||||
go func() {
|
||||
for sz := range conn.WinCh {
|
||||
log.Printf("[Setting term size to: %v %v]\n", sz.Rows, sz.Cols)
|
||||
pty.Setsize(ptmx, &pty.Winsize{Rows: sz.Rows, Cols: sz.Cols}) // nolint: gosec,errcheck
|
||||
}
|
||||
log.Println("*** WinCh goroutine done ***")
|
||||
}()
|
||||
|
||||
// Copy stdin to the pty.. (bgnd goroutine)
|
||||
// #gv:s/label=\"runShellAs\$3\"/label=\"stdinToPtyWorker\"/
|
||||
go func() {
|
||||
_, e := io.Copy(ptmx, conn)
|
||||
if e != nil {
|
||||
log.Println("** stdin->pty ended **:", e.Error())
|
||||
} else {
|
||||
log.Println("*** stdin->pty goroutine done ***")
|
||||
}
|
||||
}()
|
||||
|
||||
if chaffing {
|
||||
conn.EnableChaff()
|
||||
}
|
||||
// #gv:s/label=\"runShellAs\$4\"/label=\"deferChaffShutdown\"/
|
||||
defer func() {
|
||||
conn.DisableChaff()
|
||||
conn.ShutdownChaff()
|
||||
}()
|
||||
|
||||
// ..and the pty to stdout.
|
||||
// This may take some time exceeding that of the
|
||||
// actual command's lifetime, so the c.Wait() below
|
||||
// must synchronize with the completion of this goroutine
|
||||
// to ensure all stdout data gets to the client before
|
||||
// connection is closed.
|
||||
wg.Add(1)
|
||||
// #gv:s/label=\"runShellAs\$5\"/label=\"ptyToStdoutWorker\"/
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
_, e := io.Copy(conn, ptmx)
|
||||
if e != nil {
|
||||
log.Println("** pty->stdout ended **:", e.Error())
|
||||
} else {
|
||||
// The above io.Copy() will exit when the command attached
|
||||
// to the pty exits
|
||||
log.Println("*** pty->stdout goroutine done ***")
|
||||
}
|
||||
}()
|
||||
|
||||
if err := c.Wait(); err != nil {
|
||||
//fmt.Println("*** c.Wait() done ***")
|
||||
if exiterr, ok := err.(*exec.ExitError); ok {
|
||||
// The program has exited with an exit code != 0
|
||||
|
||||
// This works on both Unix and Windows. Although package
|
||||
// syscall is generally platform dependent, WaitStatus is
|
||||
// defined for both Unix and Windows and in both cases has
|
||||
// an ExitStatus() method with the same signature.
|
||||
if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
|
||||
exitStatus = uint32(status.ExitStatus())
|
||||
log.Printf("Exit Status: %d", exitStatus)
|
||||
}
|
||||
}
|
||||
conn.SetStatus(xsnet.CSOType(exitStatus))
|
||||
} else {
|
||||
logger.LogDebug("*** Main proc has exited. ***")
|
||||
// Background jobs still may be running; close the
|
||||
// pty anyway, so the client can return before
|
||||
// wg.Wait() below completes (Issue #18)
|
||||
if interactive {
|
||||
_ = ptmx.Close()
|
||||
}
|
||||
}
|
||||
wg.Wait() // Wait on pty->stdout completion to client
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GenAuthToken generates a pseudorandom auth token for a specific
|
||||
// user from a specific host to allow non-interactive logins.
|
||||
func GenAuthToken(who string, connhost string) string {
|
||||
//tokenA, e := os.Hostname()
|
||||
//if e != nil {
|
||||
// tokenA = "badhost"
|
||||
//}
|
||||
tokenA := connhost
|
||||
|
||||
tokenB := make([]byte, 64)
|
||||
_, _ = rand.Read(tokenB) // nolint: gosec
|
||||
return fmt.Sprintf("%s:%s", tokenA, hex.EncodeToString(tokenB))
|
||||
}
|
||||
|
||||
// Demo of a simple server that listens and spawns goroutines for each
|
||||
// connecting client. Note this code is identical to standard tcp
|
||||
// server code, save for declaring 'xsnet' rather than 'net'
|
||||
// Listener and Conns. The KEx and encrypt/decrypt is done within the type.
|
||||
// Compare to 'serverp.go' in this directory to see the equivalence.
|
||||
// TODO: reduce gocyclo
|
||||
func main() {
|
||||
var vopt bool
|
||||
var chaffEnabled bool
|
||||
var chaffFreqMin uint
|
||||
var chaffFreqMax uint
|
||||
var chaffBytesMax uint
|
||||
var dbg bool
|
||||
var laddr string
|
||||
|
||||
flag.BoolVar(&vopt, "v", false, "show version")
|
||||
flag.StringVar(&laddr, "l", ":2000", "interface[:port] to listen")
|
||||
flag.StringVar(&kcpMode, "K", "unused", `set to 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.BoolVar(&useSysLogin, "L", false, "use system login")
|
||||
flag.BoolVar(&chaffEnabled, "e", true, "enable chaff pkts")
|
||||
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.BoolVar(&dbg, "d", false, "debug logging")
|
||||
flag.Parse()
|
||||
|
||||
if vopt {
|
||||
fmt.Printf("version %s (%s)\n", version, gitCommit)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
{
|
||||
me, e := user.Current()
|
||||
if e != nil || me.Uid != "0" {
|
||||
log.Fatal("Must run as root.")
|
||||
}
|
||||
}
|
||||
|
||||
// Enforce some sane min/max vals on chaff flags
|
||||
if chaffFreqMin < 2 {
|
||||
chaffFreqMin = 2
|
||||
}
|
||||
if chaffFreqMax == 0 {
|
||||
chaffFreqMax = chaffFreqMin + 1
|
||||
}
|
||||
if chaffBytesMax == 0 || chaffBytesMax > 4096 {
|
||||
chaffBytesMax = 64
|
||||
}
|
||||
|
||||
Log, _ = logger.New(logger.LOG_DAEMON|logger.LOG_DEBUG|logger.LOG_NOTICE|logger.LOG_ERR, "xsd") // nolint: gosec
|
||||
xsnet.Init(dbg, "xsd", logger.LOG_DAEMON|logger.LOG_DEBUG|logger.LOG_NOTICE|logger.LOG_ERR)
|
||||
if dbg {
|
||||
log.SetOutput(Log)
|
||||
} else {
|
||||
log.SetOutput(ioutil.Discard)
|
||||
}
|
||||
|
||||
// Set up handler for daemon signalling
|
||||
exitCh := make(chan os.Signal, 1)
|
||||
signal.Notify(exitCh, os.Signal(syscall.SIGTERM), os.Signal(syscall.SIGINT), os.Signal(syscall.SIGHUP), os.Signal(syscall.SIGUSR1), os.Signal(syscall.SIGUSR2))
|
||||
go func() {
|
||||
for {
|
||||
sig := <-exitCh
|
||||
switch sig.String() {
|
||||
case "terminated":
|
||||
logger.LogNotice(fmt.Sprintf("[Got signal: %s]", sig)) // nolint: gosec,errcheck
|
||||
signal.Reset()
|
||||
syscall.Kill(0, syscall.SIGTERM) // nolint: gosec,errcheck
|
||||
case "interrupt":
|
||||
logger.LogNotice(fmt.Sprintf("[Got signal: %s]", sig)) // nolint: gosec,errcheck
|
||||
signal.Reset()
|
||||
syscall.Kill(0, syscall.SIGINT) // nolint: gosec,errcheck
|
||||
case "hangup":
|
||||
logger.LogNotice(fmt.Sprintf("[Got signal: %s - nop]", sig)) // nolint:gosec,errcheck
|
||||
default:
|
||||
logger.LogNotice(fmt.Sprintf("[Got signal: %s - ignored]", sig)) // nolint: gosec,errcheck
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
proto := "tcp"
|
||||
if kcpMode != "unused" {
|
||||
proto = "kcp"
|
||||
}
|
||||
l, err := xsnet.Listen(proto, laddr, kcpMode)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer l.Close() // nolint: errcheck
|
||||
|
||||
log.Println("Serving on", laddr)
|
||||
for {
|
||||
// Wait for a connection.
|
||||
conn, err := l.Accept()
|
||||
if err != nil {
|
||||
log.Printf("Accept() got error(%v), hanging up.\n", err)
|
||||
} else {
|
||||
log.Println("Accepted client")
|
||||
|
||||
// Set up chaffing to client
|
||||
// Will only start when runShellAs() is called
|
||||
// after stdin/stdout are hooked up
|
||||
conn.SetupChaff(chaffFreqMin, chaffFreqMax, chaffBytesMax) // configure server->client chaffing
|
||||
|
||||
// Handle the connection in a new goroutine.
|
||||
// The loop then returns to accepting, so that
|
||||
// multiple connections may be served concurrently.
|
||||
go func(hc *xsnet.Conn) (e error) {
|
||||
defer hc.Close() // nolint: errcheck
|
||||
|
||||
//We use io.ReadFull() here to guarantee we consume
|
||||
//just the data we want for the xs.Session, and no more.
|
||||
//Otherwise data will be sitting in the channel that isn't
|
||||
//passed down to the command handlers.
|
||||
var rec xs.Session
|
||||
var len1, len2, len3, len4, len5, len6 uint32
|
||||
|
||||
n, err := fmt.Fscanf(hc, "%d %d %d %d %d %d\n", &len1, &len2, &len3, &len4, &len5, &len6)
|
||||
log.Printf("xs.Session read:%d %d %d %d %d %d\n", len1, len2, len3, len4, len5, len6)
|
||||
|
||||
if err != nil || n < 6 {
|
||||
log.Println("[Bad xs.Session fmt]")
|
||||
return err
|
||||
}
|
||||
|
||||
tmp := make([]byte, len1)
|
||||
_, err = io.ReadFull(hc, tmp)
|
||||
if err != nil {
|
||||
log.Println("[Bad xs.Session.Op]")
|
||||
return err
|
||||
}
|
||||
rec.SetOp(tmp)
|
||||
|
||||
tmp = make([]byte, len2)
|
||||
_, err = io.ReadFull(hc, tmp)
|
||||
if err != nil {
|
||||
log.Println("[Bad xs.Session.Who]")
|
||||
return err
|
||||
}
|
||||
rec.SetWho(tmp)
|
||||
|
||||
tmp = make([]byte, len3)
|
||||
_, err = io.ReadFull(hc, tmp)
|
||||
if err != nil {
|
||||
log.Println("[Bad xs.Session.ConnHost]")
|
||||
return err
|
||||
}
|
||||
rec.SetConnHost(tmp)
|
||||
|
||||
tmp = make([]byte, len4)
|
||||
_, err = io.ReadFull(hc, tmp)
|
||||
if err != nil {
|
||||
log.Println("[Bad xs.Session.TermType]")
|
||||
return err
|
||||
}
|
||||
rec.SetTermType(tmp)
|
||||
|
||||
tmp = make([]byte, len5)
|
||||
_, err = io.ReadFull(hc, tmp)
|
||||
if err != nil {
|
||||
log.Println("[Bad xs.Session.Cmd]")
|
||||
return err
|
||||
}
|
||||
rec.SetCmd(tmp)
|
||||
|
||||
tmp = make([]byte, len6)
|
||||
_, err = io.ReadFull(hc, tmp)
|
||||
if err != nil {
|
||||
log.Println("[Bad xs.Session.AuthCookie]")
|
||||
return err
|
||||
}
|
||||
rec.SetAuthCookie(tmp)
|
||||
|
||||
log.Printf("[xs.Session: op:%c who:%s connhost:%s cmd:%s auth:****]\n",
|
||||
rec.Op()[0], string(rec.Who()), string(rec.ConnHost()), string(rec.Cmd()))
|
||||
|
||||
var valid bool
|
||||
var allowedCmds string // Currently unused
|
||||
if xs.AuthUserByToken(string(rec.Who()), string(rec.ConnHost()), string(rec.AuthCookie(true))) {
|
||||
valid = true
|
||||
} else {
|
||||
valid, allowedCmds = xs.AuthUserByPasswd(string(rec.Who()), string(rec.AuthCookie(true)), "/etc/xs.passwd")
|
||||
}
|
||||
|
||||
// Security scrub
|
||||
rec.ClearAuthCookie()
|
||||
|
||||
// Tell client if auth was valid
|
||||
if valid {
|
||||
hc.Write([]byte{1}) // nolint: gosec,errcheck
|
||||
} else {
|
||||
logger.LogNotice(fmt.Sprintln("Invalid user", string(rec.Who()))) // nolint: errcheck,gosec
|
||||
hc.Write([]byte{0}) // nolint: gosec,errcheck
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("[allowedCmds:%s]\n", allowedCmds)
|
||||
|
||||
if rec.Op()[0] == 'A' {
|
||||
// Generate automated login token
|
||||
addr := hc.RemoteAddr()
|
||||
hname := goutmp.GetHost(addr.String())
|
||||
logger.LogNotice(fmt.Sprintf("[Generating autologin token for [%s@%s]]\n", rec.Who(), hname)) // nolint: gosec,errcheck
|
||||
token := GenAuthToken(string(rec.Who()), string(rec.ConnHost()))
|
||||
tokenCmd := fmt.Sprintf("echo \"%s\" | tee -a ~/.xs_id", token)
|
||||
cmdStatus, runErr := runShellAs(string(rec.Who()), hname, string(rec.TermType()), tokenCmd, false, hc, chaffEnabled)
|
||||
// Returned hopefully via an EOF or exit/logout;
|
||||
// Clear current op so user can enter next, or EOF
|
||||
rec.SetOp([]byte{0})
|
||||
if runErr != nil {
|
||||
logger.LogErr(fmt.Sprintf("[Error generating autologin token for %s@%s]\n", rec.Who(), hname)) // nolint: gosec,errcheck
|
||||
} else {
|
||||
log.Printf("[Autologin token generation completed for %s@%s, status %d]\n", rec.Who(), hname, cmdStatus)
|
||||
hc.SetStatus(xsnet.CSOType(cmdStatus))
|
||||
}
|
||||
} else if rec.Op()[0] == 'c' {
|
||||
// Non-interactive command
|
||||
addr := hc.RemoteAddr()
|
||||
hname := goutmp.GetHost(addr.String())
|
||||
logger.LogNotice(fmt.Sprintf("[Running command for [%s@%s]]\n", rec.Who(), hname)) // nolint: gosec,errcheck
|
||||
cmdStatus, runErr := runShellAs(string(rec.Who()), hname, string(rec.TermType()), string(rec.Cmd()), false, hc, chaffEnabled)
|
||||
// Returned hopefully via an EOF or exit/logout;
|
||||
// Clear current op so user can enter next, or EOF
|
||||
rec.SetOp([]byte{0})
|
||||
if runErr != nil {
|
||||
logger.LogErr(fmt.Sprintf("[Error spawning cmd for %s@%s]\n", rec.Who(), hname)) // nolint: gosec,errcheck
|
||||
} else {
|
||||
logger.LogNotice(fmt.Sprintf("[Command completed for %s@%s, status %d]\n", rec.Who(), hname, cmdStatus)) // nolint: gosec,errcheck
|
||||
hc.SetStatus(xsnet.CSOType(cmdStatus))
|
||||
}
|
||||
} else if rec.Op()[0] == 's' {
|
||||
// Interactive session
|
||||
addr := hc.RemoteAddr()
|
||||
hname := goutmp.GetHost(addr.String())
|
||||
logger.LogNotice(fmt.Sprintf("[Running shell for [%s@%s]]\n", rec.Who(), hname)) // nolint: gosec,errcheck
|
||||
|
||||
cmdStatus, runErr := runShellAs(string(rec.Who()), hname, string(rec.TermType()), string(rec.Cmd()), true, hc, chaffEnabled)
|
||||
// Returned hopefully via an EOF or exit/logout;
|
||||
// Clear current op so user can enter next, or EOF
|
||||
rec.SetOp([]byte{0})
|
||||
if runErr != nil {
|
||||
Log.Err(fmt.Sprintf("[Error spawning shell for %s@%s]\n", rec.Who(), hname)) // nolint: gosec,errcheck
|
||||
} else {
|
||||
logger.LogNotice(fmt.Sprintf("[Shell completed for %s@%s, status %d]\n", rec.Who(), hname, cmdStatus)) // nolint: gosec,errcheck
|
||||
hc.SetStatus(xsnet.CSOType(cmdStatus))
|
||||
}
|
||||
} else if rec.Op()[0] == 'D' {
|
||||
// File copy (destination) operation - client copy to server
|
||||
log.Printf("[Client->Server copy]\n")
|
||||
addr := hc.RemoteAddr()
|
||||
hname := goutmp.GetHost(addr.String())
|
||||
logger.LogNotice(fmt.Sprintf("[Running copy for [%s@%s]]\n", rec.Who(), hname)) // nolint: gosec,errcheck
|
||||
cmdStatus, runErr := runClientToServerCopyAs(string(rec.Who()), string(rec.TermType()), hc, string(rec.Cmd()), chaffEnabled)
|
||||
// Returned hopefully via an EOF or exit/logout;
|
||||
// Clear current op so user can enter next, or EOF
|
||||
rec.SetOp([]byte{0})
|
||||
if runErr != nil {
|
||||
logger.LogErr(fmt.Sprintf("[Error running cp for %s@%s]\n", rec.Who(), hname)) // nolint: gosec,errcheck
|
||||
} else {
|
||||
logger.LogNotice(fmt.Sprintf("[Command completed for %s@%s, status %d]\n", rec.Who(), hname, cmdStatus)) // nolint: gosec,errcheck
|
||||
}
|
||||
hc.SetStatus(xsnet.CSOType(cmdStatus))
|
||||
|
||||
// Send CSOExitStatus *before* client closes channel
|
||||
s := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(s, cmdStatus)
|
||||
log.Printf("** cp writing closeStat %d at Close()\n", cmdStatus)
|
||||
hc.WritePacket(s, xsnet.CSOExitStatus) // nolint: gosec,errcheck
|
||||
} else if rec.Op()[0] == 'S' {
|
||||
// File copy (src) operation - server copy to client
|
||||
log.Printf("[Server->Client copy]\n")
|
||||
addr := hc.RemoteAddr()
|
||||
hname := goutmp.GetHost(addr.String())
|
||||
logger.LogNotice(fmt.Sprintf("[Running copy for [%s@%s]]\n", rec.Who(), hname)) // nolint: gosec,errcheck
|
||||
cmdStatus, runErr := runServerToClientCopyAs(string(rec.Who()), string(rec.TermType()), hc, string(rec.Cmd()), chaffEnabled)
|
||||
if runErr != nil {
|
||||
logger.LogErr(fmt.Sprintf("[Error spawning cp for %s@%s]\n", rec.Who(), hname)) // nolint: gosec,errcheck
|
||||
} else {
|
||||
// Returned hopefully via an EOF or exit/logout;
|
||||
logger.LogNotice(fmt.Sprintf("[Command completed for %s@%s, status %d]\n", rec.Who(), hname, cmdStatus)) // nolint: gosec,errcheck
|
||||
}
|
||||
// Clear current op so user can enter next, or EOF
|
||||
rec.SetOp([]byte{0})
|
||||
hc.SetStatus(xsnet.CSOType(cmdStatus))
|
||||
//fmt.Println("Waiting for EOF from other end.")
|
||||
//_, _ = hc.Read(nil /*ackByte*/)
|
||||
//fmt.Println("Got remote end ack.")
|
||||
} else {
|
||||
logger.LogErr(fmt.Sprintln("[Bad xs.Session]")) // nolint: gosec,errcheck
|
||||
}
|
||||
return
|
||||
}(&conn) // nolint: errcheck
|
||||
} // Accept() success
|
||||
} //endfor
|
||||
//logger.LogNotice(fmt.Sprintln("[Exiting]")) // nolint: gosec,errcheck
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue