Compare commits

...

71 Commits

Author SHA1 Message Date
Russ Magee a929fdc211 Fix term check to work for MSYS64/CYGWIN64 2024-11-30 18:46:15 -08:00
Russ Magee fc66a0557a Merge branch 'master' of https://gogs.blitter.com/RLabs/xs 2024-11-22 02:45:44 -08:00
Russ Magee bd3f90d308 Bumped version 2024-11-22 02:44:33 -08:00
Russtopia 8c1f90aaff Merge branch 'keepalive-only-shellmode' of RLabs/xs into master 2024-11-22 02:35:04 -08:00
Russ Magee efa01ee0e1 Fix for Issue #40: file copies are aborted 2024-11-21 22:19:47 -08:00
Russ Magee 4aea95fa3c Re-instated isatty check 2024-10-27 14:29:59 -07:00
Russ Magee 8e96e4fb32 Use GOOS in makefile rather than MSYS for Windows MSYS/CYGWIN detection 2024-10-27 12:37:49 -07:00
Russ Magee f07aa457b3 Fixes for MSYS2 and CYGWIN term mode; removed mintty_wrapper.sh 2024-10-27 12:17:34 -07:00
Russ Magee b12c8fe562 Bumped version 2024-10-27 12:12:07 -07:00
Russ Magee e5b6422d70 Fixes for MSYS2 and CYGWIN term mode; removed mintty_wrapper.sh 2024-10-27 11:59:17 -07:00
Russ Magee 12409319e7 Removed go.mod, go.sum 2024-07-15 02:06:26 -07:00
Russtopia bfcd097a14 Bump version to v0.9.11 2024-05-02 00:48:50 -07:00
Russtopia 136f37e209 Update 'README.md' 2024-05-01 23:58:44 -07:00
Russtopia ec9b4fe2f4 Merge branch 'whirlpool-hash' of RLabs/xs into master 2024-05-01 23:49:36 -07:00
Russtopia aa33a3b8a0 Merge branch 'log-listener-ipaddr' of RLabs/xs into master 2024-05-01 23:48:18 -07:00
Russ Magee 7e4aeba93a Add remote IP to net.Listener Accept logmsg 2024-05-01 23:46:43 -07:00
Russ Magee 91bb0778b2 Addition of WHIRLPOOL hash 2024-05-01 23:43:24 -07:00
Russ Magee 952279a108 Moved esc seq table out of copyBuffer to avoid redecls 2024-04-27 22:01:43 -07:00
Russ Magee dbaa8b5b62 Ensure auth fails if server is somehow built for unsupported platform 2024-03-30 00:48:46 -07:00
Russ Magee 77c9b8654f Left GOPROXY alone 2024-03-02 14:46:15 -08:00
Russ Magee e42645a2b3 Refreshed go.{mod,sum} and bumped semver in Makefile 2024-03-01 23:34:14 -08:00
Russ Magee 057a3c01c7 Updated go.{mod,sum} 2024-02-25 21:14:20 -08:00
Russ Magee 540cb8ff3a gofmt 2024-02-25 21:14:00 -08:00
Russ Magee ae67ee6201 Fixed CI script 2024-01-29 21:47:03 -08:00
Russ Magee 8827d67cc6 unified refs to authtoken file to a const string 2024-01-29 21:37:07 -08:00
Russtopia 17d7bc01ef Update 'README.md'
Updated references to .xs_id location
2024-01-29 19:03:13 -08:00
Russ Magee 89ad0e0998 Fixed missed authtoken file ref in auth.go 2024-01-29 18:56:21 -08:00
Russ Magee 713f44086a Bumped version 2024-01-29 18:43:24 -08:00
Russ Magee 08cccb6929 Moved .xs_id to ~/.config/xs 2024-01-29 18:40:26 -08:00
Russ Magee 6212119621 Added max bounds for chaff, rekey intervals and random jitter for rekey interval 2023-12-03 19:22:05 -08:00
Russtopia faf8769ac4 Merge branch 'remodulate-on-rekey' of RLabs/xs into master
Add optional cipher/hmac algo remodulate on rekey
2023-12-02 02:00:17 -08:00
Russ Magee 32b669192b Add optional cipher/hmac algo remodulate on rekey 2023-12-02 01:58:30 -08:00
Russtopia e82d968381 Merge branch 'rekeying' of RLabs/xs into master 2023-11-15 00:43:36 -08:00
Russ Magee 032baf63d6 Added rekeying (-r secs) client/server 2023-11-15 00:32:50 -08:00
Russ Magee c569a5a3c9 Removed debug stmts to do with keepalive 2023-11-07 00:38:51 -08:00
Russtopia 36799ba9e7 Merge branch 'conn-keepalive' of RLabs/xs into master
Merge of branch conn-keepalive
2023-11-06 23:48:51 -08:00
Russ Magee f0dc681a4c Merge branch 'master' into conn-keepalive 2023-11-06 23:41:59 -08:00
Russ Magee 871f9a200c Experimental dead conn process kill logic 2023-11-06 23:17:13 -08:00
Russ Magee 908a1bcda2 Fix for ConnDead status 2023-11-06 21:57:19 -08:00
Russ Magee 39a8bd2f03 Bump 2023-11-05 19:05:23 -08:00
Russ Magee 9244cc9785 KeepAlive WIP. TODO: check exitStatus logic for shell 'exit' clean exit 2023-11-05 18:53:49 -08:00
Russ Magee d0f8751b2b debugging 2023-11-05 16:40:12 -08:00
Russ Magee a3d8543816 Disabled test code 2023-11-05 15:08:31 -08:00
Russ Magee bcea6d713f Connection keepalive/disconnect 2023-11-05 15:06:43 -08:00
Russ Magee 580053adbd Connection keepalive/disconnect 2023-11-05 14:58:24 -08:00
Russ Magee 74be6173b6 Comment cleanup 2023-11-03 23:57:55 -07:00
Russ Magee 119c039b91 Fixed up mentions of old name hkexsh 2023-10-18 00:52:08 -07:00
Russ Magee 3cd6f8b7e6 Update to use latest hopscotch enc alg 2023-09-13 00:57:41 -07:00
Russtopia 623d622d58 Update README.md with live build status 2023-08-05 12:57:37 -07:00
Russ Magee 06124d7584 Fix for scc rule 2023-05-24 20:47:04 -07:00
Russ Magee 7b6a9d1350 Added scc target 2023-05-24 20:29:26 -07:00
Russ Magee 5ee09de99a Cleaner fix for issues #22,#33 2022-10-12 21:27:48 -07:00
Russ Magee 9ca5ccae32 (issue #33) use of time.Sleep() prior to ptmx.Close() in xsd.runShellAs()
Hack to enable server to complete sending data to client (eg. xs -x "ls /foo" losing end of output)
2022-10-04 22:30:10 -07:00
Russ Magee bd0b48d98f golint cleanup 2022-09-26 22:09:54 -07:00
Russtopia 3325bb3a4e Update 'README.md' adding authtoken setup example 2022-09-26 00:49:52 -07:00
Russ Magee 0913311351 Suppress Gimme Cookie prompt when in gopt (-g) authtoken create mode 2022-09-25 23:23:33 -07:00
Russ Magee b2e43f4bad Converted xsnet Read() ctrlStatOp logic to switch 2022-09-25 11:33:09 -07:00
Russ Magee 667328a91c xs/ lint cleanups 2022-09-24 20:01:16 -07:00
Russ Magee 0f28d2c023 More golangci-lint config updates 2022-09-21 00:34:53 -07:00
Russ Magee 5c826f7a5f Updated golangci-lint config; xsd.sysvrc init script updates 2022-09-20 23:02:52 -07:00
Russ Magee 1d13e6a3bd golangci.yml - disable deprecated checkers 2022-09-20 21:12:13 -07:00
Russ Magee b5f9333b3a Updated golangci-lint config 2022-09-20 20:57:08 -07:00
Russ Magee 9a0dd8270a Cleaned up & updated vendor/ 2022-09-20 20:31:53 -07:00
Russ Magee 0b70cdbac0 Merge branch 'master' of https://gogs.blitter.com/RLabs/xs 2022-09-12 22:13:00 -07:00
Russ Magee 5b6bec0287 Reintroduce go mod init/tidy and remove old vendor dir if present 2022-09-12 22:12:21 -07:00
Russ Magee 254e9fb7d6 Reintroduce go mod init/tidy 2022-09-12 22:05:03 -07:00
Russ Magee 653e732445 Update go mod deps 2022-09-12 20:10:51 -07:00
Russtopia 610781aadf Merge branch 'update-go-callvis-diagrams' of RLabs/xs into master 2022-08-21 00:01:16 -07:00
Russ Magee 1f9f03dfe3 Update go-callvis graphics for wiki 2022-08-21 00:04:06 -07:00
Russ Magee 677172dc1f Avoid dependence on goproxy (use vendored deps) 2022-08-12 21:56:09 -07:00
Russ Magee 7a4560762d Added vendor/ dir to remove dependencies on gopkg.in (down Aug 12 2022) 2022-08-12 21:30:05 -07:00
26 changed files with 1635 additions and 1560 deletions

View File

@ -1,327 +1,160 @@
# This file contains all available configuration options
# with their default values.
# options for analysis running
run:
# default concurrency is a available CPU number
concurrency: 4
# timeout for analysis, e.g. 30s, 5m, default is 1m
timeout: 1m
# exit code when at least one issue was found, default is 1
issues-exit-code: 1
# include test files or not, default is true
tests: true
# list of build tags, all linters use it. Default is empty list.
build-tags:
- mytag
# which dirs to skip: issues from them won't be reported;
# can use regexp here: generated.*, regexp is applied on full path;
# default value is empty list, but default dirs are skipped independently
# from this option's value (see skip-dirs-use-default).
skip-dirs:
- src/external_libs
- autogenerated_by_my_lib
# default is true. Enables skipping of directories:
# vendor$, third_party$, testdata$, examples$, Godeps$, builtin$
skip-dirs-use-default: true
# which files to skip: they will be analyzed, but issues from them
# won't be reported. Default value is empty list, but there is
# no need to include all autogenerated files, we confidently recognize
# autogenerated files. If it's not please let us know.
skip-files:
- ".*\\.my\\.go$"
- lib/bad.go
# by default isn't set. If set we pass it to "go list -mod={option}". From "go help modules":
# If invoked with -mod=readonly, the go command is disallowed from the implicit
# automatic updating of go.mod described above. Instead, it fails when any changes
# to go.mod are needed. This setting is most useful to check that go.mod does
# not need updates, such as in a continuous integration and testing system.
# If invoked with -mod=vendor, the go command assumes that the vendor
# directory holds the correct copies of dependencies and ignores
# the dependency descriptions in go.mod.
#! modules-download-mode: readonly|release|vendor
# output configuration options
output:
# colored-line-number|line-number|json|tab|checkstyle|code-climate, default is "colored-line-number"
format: colored-line-number
# print lines of code with issue, default is true
print-issued-lines: true
# print linter name in the end of issue text, default is true
print-linter-name: true
# make issues output unique by line, default is true
uniq-by-line: true
# all available settings of specific linters
linters-settings:
dogsled:
# checks assignments with too many blank identifiers; default is 2
max-blank-identifiers: 2
depguard:
list-type: blacklist
packages:
# logging is allowed only by logutils.Log, logrus
# is allowed to use only in logutils package
- github.com/sirupsen/logrus
packages-with-error-message:
- github.com/sirupsen/logrus: "logging is allowed only by logutils.Log"
dupl:
# tokens count to trigger issue, 150 by default
threshold: 100
errcheck:
# report about not checking of errors in type assetions: `a := b.(MyStruct)`;
# default is false: such cases aren't reported by default.
check-type-assertions: false
# report about assignment of errors to blank identifier: `num, _ := strconv.Atoi(numStr)`;
# default is false: such cases aren't reported by default.
check-blank: false
# [deprecated] comma-separated list of pairs of the form pkg:regex
# the regex is used to ignore names within pkg. (default "fmt:.*").
# see https://github.com/kisielk/errcheck#the-deprecated-method for details
ignore: fmt:.*,io/ioutil:^Read.*
# path to a file containing a list of functions to exclude from checking
# see https://github.com/kisielk/errcheck#excluding-functions for details
#!exclude: /path/to/file.txt
threshold: 125
funlen:
lines: 60
statements: 40
gocognit:
# minimal code complexity to report, 30 by default (but we recommend 10-20)
min-complexity: 10
lines: 125
statements: 50
gci:
local-prefixes: github.com/golangci/golangci-lint
goconst:
# minimal length of string constant, 3 by default
min-len: 3
# minimal occurrences count to trigger, 3 by default
min-occurrences: 3
min-len: 2
min-occurrences: 2
gocritic:
# Which checks should be enabled; can't be combined with 'disabled-checks';
# See https://go-critic.github.io/overview#checks-overview
# To check which checks are enabled run `GL_DEBUG=gocritic golangci-lint run`
# By default list of stable checks is used.
enabled-checks:
#!- rangeValCopy
# Which checks should be disabled; can't be combined with 'enabled-checks'; default is empty
disabled-checks:
- regexpMust
# Enable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.
# Empty list by default. See https://github.com/go-critic/go-critic#usage -> section "Tags".
enabled-tags:
- diagnostic
- experimental
- performance
settings: # settings passed to gocritic
captLocal: # must be valid enabled check name
paramsOnly: true
rangeValCopy:
sizeThreshold: 32
#- style
#- opinionated
disabled-checks:
- commentFormatting
- commentedOutCode
- dupImport # https://github.com/go-critic/go-critic/issues/845
- ifElseChain
- octalLiteral
- whyNoLint
- wrapperFunc
gocyclo:
# minimal code complexity to report, 30 by default (but we recommend 10-20)
min-complexity: 10
godox:
# report any comments starting with keywords, this is useful for TODO or FIXME comments that
# might be left in the code accidentally and should be resolved before merging
keywords: # default keywords are TODO, BUG, and FIXME, these can be overwritten by this setting
- NOTE
- OPTIMIZE # marks code that should be optimized before merging
- HACK # marks hack-arounds that should be removed before merging
gofmt:
# simplify code: gofmt with `-s` option, true by default
simplify: true
min-complexity: 15
goimports:
# put imports beginning with prefix after 3rd-party packages;
# it's a comma-separated list of prefixes
local-prefixes: github.com/org/project
golint:
# minimal confidence for issues, default is 0.8
min-confidence: 0.8
local-prefixes: github.com/golangci/golangci-lint
gomnd:
settings:
mnd:
# the list of enabled checks, see https://github.com/tommy-muehle/go-mnd/#checks for description.
checks: argument,case,condition,operation,return,assign
# don't include the "operation" and "assign"
checks: argument,case,condition,return
govet:
# report about shadowed variables
check-shadowing: true
# settings per analyzer
settings:
printf: # analyzer name, run `go tool vet help` to see all analyzers
funcs: # run `go tool vet help printf` to see available settings for `printf` analyzer
printf:
funcs:
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Infof
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Warnf
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Errorf
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Fatalf
# enable or disable analyzers by name
enable:
- atomicalign
enable-all: false
disable:
- shadow
disable-all: false
depguard:
list-type: blacklist
include-go-root: false
packages:
- github.com/sirupsen/logrus
packages-with-error-message:
# specify an error message to output when a blacklisted package is used
- github.com/sirupsen/logrus: "logging is allowed only by logutils.Log"
lll:
# max line length, lines longer will be reported. Default is 120.
# '\t' is counted as 1 character by default, and can be changed with the tab-width option
line-length: 120
# tab width in spaces. Default to 1.
tab-width: 1
line-length: 140
maligned:
# print struct with more effective memory layout or not, false by default
suggest-new: true
misspell:
# Correct spellings using locale preferences for US or UK.
# Default is to use a neutral variety of English.
# Setting locale to US will correct the British spelling of 'colour' to 'color'.
locale: US
ignore-words:
- someword
nakedret:
# make an issue if func has more lines of code than this setting and it has naked returns; default is 30
max-func-lines: 30
prealloc:
# XXX: we don't recommend using this linter before doing performance profiling.
# For most programs usage of prealloc will be a premature optimization.
# Report preallocation suggestions only on simple loops that have no returns/breaks/continues/gotos in them.
# True by default.
simple: true
range-loops: true # Report preallocation suggestions on range loops, true by default
for-loops: false # Report preallocation suggestions on for loops, false by default
rowserrcheck:
packages:
- github.com/jmoiron/sqlx
unparam:
# Inspect exported functions, default is false. Set to true if no external program/library imports your code.
# XXX: if you enable this setting, unparam will report a lot of false-positives in text editors:
# if it's called for subdir of a project it can't find external interfaces. All text editor integrations
# with golangci-lint call it on a directory with the changed file.
check-exported: false
unused:
# treat code as a program (not a library) and report unused exported identifiers; default is false.
# XXX: if you enable this setting, unused will report a lot of false-positives in text editors:
# if it's called for subdir of a project it can't find funcs usages. All text editor integrations
# with golangci-lint call it on a directory with the changed file.
check-exported: false
whitespace:
multi-if: false # Enforces newlines (or comments) after every multi-line if statement
multi-func: false # Enforces newlines (or comments) after every multi-line function signature
wsl:
# If true append is only allowed to be cuddled if appending value is
# matching variables, fields or types on line above. Default is true.
strict-append: true
# Allow calls and assignments to be cuddled as long as the lines have any
# matching variables, fields or types. Default is true.
allow-assign-and-call: true
# Allow multiline assignments to be cuddled. Default is true.
allow-multiline-assign: true
# Allow declarations (var) to be cuddled.
allow-cuddle-declarations: false
# Allow trailing comments in ending of blocks
allow-trailing-comment: false
# Force newlines in end of case at this limit (0 = never).
force-case-trailing-whitespace: 0
# The custom section can be used to define linter plugins to be loaded at runtime. See README doc
# for more info.
custom:
# Each custom linter should have a unique name.
#! example:
#! # The path to the plugin *.so. Can be absolute or local. Required for each custom linter
#! path: /path/to/example.so
#! # The description of the linter. Optional, just for documentation purposes.
#! description: This is an example usage of a plugin linter.
#! # Intended to point to the repo location of the linter. Optional, just for documentation purposes.
#! original-url: github.com/golangci/example-linter
locale: en_CA
nolintlint:
allow-leading-space: true # don't require machine-readable nolint directives (i.e. with no leading space)
allow-unused: false # report any unused nolint directives
require-explanation: false # don't require an explanation for nolint directives
require-specific: false # don't require nolint directives to be specific about which linter is being skipped
linters:
# please, do not use `enable-all`: it's deprecated and will be removed soon.
# inverted configuration with `enable-all` and `disable` is not scalable during updates of golangci-lint
disable-all: true
enable:
- megacheck
- bodyclose
- depguard
- dogsled
- dupl
- errcheck
- exhaustive
- exportloopref
- funlen
- gochecknoinits
- goconst
- gocritic
- gocyclo
- gofmt
- goimports
#- golint
- gomnd
- goprintffuncname
- gosec
- gosimple
- govet
disable:
- maligned
- prealloc
disable-all: false
presets:
- bugs
- ineffassign
#- interfacer
- lll
- misspell
#- nakedret
- noctx
- nolintlint
- rowserrcheck
#- scopelint
- staticcheck
#- structcheck
- stylecheck
- typecheck
- unconvert
- unparam
- unused
fast: false
#- varcheck
- whitespace
# don't enable:
# - asciicheck
# - deadcode
# - gochecknoglobals
# - gocognit
# - godot
# - godox
# - goerr113
# - golint
# - interfacer
# - maligned
# - nestif
# - prealloc
## - rowserrcheck
# - scopelint
# - structcheck
# - testpackage
# - varcheck
# - wsl
issues:
# List of regexps of issue texts to exclude, empty list by default.
# But independently from this option we use default exclude patterns,
# it can be disabled by `exclude-use-default: false`. To list all
# excluded by default patterns execute `golangci-lint run --help`
exclude:
- abcdef
# Excluding configuration per-path, per-linter, per-text and per-source
exclude-rules:
# Exclude some linters from running on tests files.
- path: _test\.go
linters:
- gocyclo
- errcheck
- dupl
- gosec
- gomnd
# Exclude known linters from partially hard-vendored code,
# which is impossible to exclude via "nolint" comments.
- path: internal/hmac/
text: "weak cryptographic primitive"
linters:
- gosec
# Exclude some staticcheck messages
# https://github.com/go-critic/go-critic/issues/926
- linters:
- staticcheck
text: "SA9003:"
- gocritic
text: "unnecessaryDefer:"
# Exclude lll issues for long lines with go:generate
- linters:
- lll
source: "^//go:generate "
# TODO temporary rule, must be removed
# seems related to v0.34.1, but I was not able to reproduce locally,
# I was also not able to reproduce in the CI of a fork,
# only the golangci-lint CI seems to be affected by this invalid analysis.
- path: pkg/golinters/scopelint.go
text: 'directive `//nolint:interfacer` is unused for linter interfacer'
# Independently from option `exclude` we use default exclude patterns,
# it can be disabled by this option. To list all
# excluded by default patterns execute `golangci-lint run --help`.
# Default value for this option is true.
exclude-use-default: false
run:
skip-dirs:
- test/testdata_etc
- internal/cache
- internal/renameio
- internal/robustio
# Maximum issues count per one linter. Set to 0 to disable. Default is 50.
max-issues-per-linter: 0
# Maximum count of issues with the same text. Set to 0 to disable. Default is 3.
max-same-issues: 0
# Show only new issues: if there are unstaged changes or untracked files,
# only those changes are analyzed, else only changes in HEAD~ are analyzed.
# It's a super-useful option for integration of golangci-lint into existing
# large codebase. It's not practical to fix all existing issues at the moment
# of integration: much better don't allow issues in new code.
# Default is false.
new: false
# Show only new issues created after git revision `REV`
#!new-from-rev: REV
#new-from-rev: HEAD^
# Show only new issues created in git patch with set file path.
#!new-from-patch: path/to/patch/file
# golangci.com configuration
# https://github.com/golangci/golangci/wiki/Configuration
service:
golangci-lint-version: 1.23.x # use the fixed version to not introduce new linters unexpectedly
prepare:
- echo "here I can run custom commands, but no preparation needed for this repo"

View File

@ -1,6 +1,7 @@
VERSION := 0.9.5.1
.PHONY: lint vis clean common client server passwd subpkgs install uninstall reinstall
VERSION := 0.9.13
.PHONY: lint vis clean common client server passwd\
subpkgs install uninstall reinstall scc
## Tag version of binaries with build info wrt.
## GO111MODULE(=on) and vendor/ setup vs. $GOPATH pkg builds
############################################################
@ -42,13 +43,10 @@ ifeq ($(GOOS),)
endif
ifeq ($(GOOS),windows)
ifeq ($(MSYSTEM),MSYS)
WIN_MSYS=1
endif
endif
INSTPREFIX = /usr
else
INSTPREFIX = /usr/local
endif
all: common client server
@ -72,7 +70,7 @@ tools:
common:
$(GO) build .
go install .
go install -a .
client: common
@ -80,12 +78,15 @@ client: common
server: common
ifeq ($(MSYSTEM),MSYS)
ifeq ($(GOOS),windows)
echo "Build of xsd server for Windows not yet supported"
else
$(MAKE) BUILDOPTS=$(BUILDOPTS) -C xsd
endif
scc:
@scc --exclude-dir=bacillus,vendor,garbled --cocomo-project-type=rlabs,3,1.12,1,1
vis:
@which go-callvis >/dev/null 2>&1; \
stat=$$?; if [ $$stat -ne "0" ]; then \
@ -102,13 +103,10 @@ lint:
reinstall: uninstall install
install:
echo "WIN_MSYS:" $(WIN_MSYS)
ifdef WIN_MSYS
cp xs/mintty_wrapper.sh $(INSTPREFIX)/bin/xs
cp xs/mintty_wrapper.sh $(INSTPREFIX)/bin/xc
cp xs/xs $(INSTPREFIX)/bin/_xs
cp xs/xs $(INSTPREFIX)/bin/_xc
echo "Install of xsd server for Windows not yet supported"
ifeq ($(GOOS),windows)
cp xs/xs $(INSTPREFIX)/bin/xs
cp xs/xs $(INSTPREFIX)/bin/xc
@echo "Install of xsd server for Windows not yet supported"
else
cp xs/xs $(INSTPREFIX)/bin
cd $(INSTPREFIX)/bin && ln -s xs xc && cd -
@ -116,8 +114,7 @@ else
endif
uninstall:
rm -f $(INSTPREFIX)/bin/xs $(INSTPREFIX)/bin/xc \
$(INSTPREFIX)/bin/_xs $(INSTPREFIX)/bin/_xc
rm -f $(INSTPREFIX)/bin/xs $(INSTPREFIX)/bin/xc
ifndef $(WIN_MSYS)
rm -f $(INSTPREFIX)/sbin/xsd
endif

View File

@ -1,6 +1,8 @@
[![GoDoc](https://godoc.org/blitter.com/go/xs?status.svg)](https://godoc.org/blitter.com/go/xs)
# XS
![last build status](https://bacillus.blitter.com/onPush-xs-build/lastStatusIcon)
--
XS (**X**perimental **S**hell) is a simple alternative to ssh (<5% total SLOCC) written from scratch in Go.
@ -42,10 +44,12 @@ Currently supported session algorithms:
* Blowfish-64
* CryptMTv1 (64bit) (https://eprint.iacr.org/2005/165.pdf)
* ChaCha20 (https://github.com/aead/chacha20)
* HOPSCOTCH (https://gogs.blitter.com/Russtopia/hopscotch)
[HMAC]
* HMAC-SHA256
* HMAC-SHA512
* WHIRLPOOL
***
**A Note on 'cryptographic agility'**
@ -90,18 +94,6 @@ KYBER IND-CCA-2 KEM
As of this time (Oct 2018) Kyber is one of the candidate algorithms submitted to the [NIST post-quantum cryptography project](https://csrc.nist.gov/Projects/Post-Quantum-Cryptography). The authors recommend using it in "... so-called hybrid mode in combination with established "pre-quantum" security; for example in combination with elliptic-curve Diffie-Hellman." THIS PROJECT DOES NOT DO THIS (in case you didn't notice yet, THIS PROJECT IS EXPERIMENTAL.)
### Dependencies:
* Recent version of go (tested, at various times, with go-1.9 to go-1.12.4)
* [github.com/mattn/go-isatty](http://github.com/mattn/go-isatty) //terminal tty detection
* [github.com/kr/pty](http://github.com/kr/pty) //unix pty control (server pty connections)
* [github.com/jameskeane/bcrypt](http://github.com/jameskeane/bcrypt) //password storage/auth
* [blitter.com/go/goutmp](https://gogs.blitter.com/RLabs/goutmp) // wtmp/lastlog C bindings for user accounting
* [https://gitlab.com/yawning/kyber](https://gogs.blitter.com/RLabs/kyber) // golang Kyber KEM
* [https://gitlab.com/yawning/kyber](https://gogs.blitter.com/RLabs/newhope) // golang NEWHOPE,NEWHOPE-SIMPLE KEX
* [blitter.com/go/mtwist](https://gogs.blitter.com/RLabs/mtwist) // 64-bit Mersenne Twister PRNG
* [blitter.com/go/cryptmt](https://gogs.blitter.com/RLabs/cryptmt) // CryptMTv1 stream cipher
### Installing
@ -195,10 +187,18 @@ or is interrupted.
### Setting up an 'authtoken' for scripted (password-free) logins
Use the -g option of xs to request a token from the remote server, which will return a
hostname:token string. Place this string into $HOME/.xs_id to allow logins without
entering a password (obviously, $HOME/.xs_id on both server and client for the user
hostname:token string. Place this string into $HOME/.config/xs/.xs_id to allow logins without
entering a password (obviously, $HOME/.config/xs/.xs_id on both server and client for the user
should *not* be world-readable.)
```
$ xs -g user@host.net >>~/.config/xs/.xs_id
```
[enter password blindly, authtoken entry will be stored in ~/.config/xs/.xs_id]
NOTE you may need to remove older entries for the same host if this is not the first time you have added
it to your .xs_id file.
### File Copying using xc
xc is a symlink to xs, and the binary checks its own filename to determine whether

9
auth.go Executable file → Normal file
View File

@ -23,6 +23,7 @@ import (
"runtime"
"strings"
"blitter.com/go/xs/xsnet"
"github.com/jameskeane/bcrypt"
passlib "gopkg.in/hlandau/passlib.v1"
)
@ -52,7 +53,7 @@ func VerifyPass(ctx *AuthCtx, user, password string) (bool, error) {
} else if runtime.GOOS == "freebsd" {
pwFileName = "/etc/master.passwd"
} else {
pwFileName = "unsupported"
return false, errors.New("Unsupported platform")
}
pwFileData, e := ctx.reader(pwFileName)
if e != nil {
@ -154,7 +155,7 @@ func AuthUserByPasswd(ctx *AuthCtx, username string, auth string, fname string)
// ------------- End xs-local passwd auth routine(s) -----------
// AuthUserByToken checks user login information against an auth token.
// Auth tokens are stored in each user's $HOME/.xs_id and are requested
// Auth tokens are stored in each user's $HOME/.config/xs/.xs_id and are requested
// via the -g option.
// The function also check system /etc/passwd to cross-check the user
// actually exists.
@ -172,9 +173,9 @@ func AuthUserByToken(ctx *AuthCtx, username string, connhostname string, auth st
return false
}
b, e := ctx.reader(fmt.Sprintf("%s/.xs_id", u.HomeDir))
b, e := ctx.reader(fmt.Sprintf("%s/%s", u.HomeDir, xsnet.XS_ID_AUTHTOKFILE))
if e != nil {
log.Printf("INFO: Cannot read %s/.xs_id\n", u.HomeDir)
log.Printf("INFO: Cannot read %s/%s\n", u.HomeDir, xsnet.XS_ID_AUTHTOKFILE)
return false
}

View File

@ -5,7 +5,7 @@
export GOPATH="${HOME}/go"
export PATH=/usr/local/bin:/usr/bin:/usr/lib/ccache/bin:/bin:$GOPATH/bin
unset GO111MODULE
export GOPROXY="direct"
#export GOPROXY="direct"
#!# GOCACHE will be phased out in v1.12. [github.com/golang/go/issues/26809]
#!export GOCACHE="${HOME}/.cache/go-build"
@ -25,19 +25,14 @@ echo "Building most recent push on branch $branch"
git checkout "$branch"
ls
############
stage "Build"
############
echo "Recreating go.mod from scratch ..."
mv go.mod go.mod.git || true
mv go.sum go.sum.git || true
go mod init
go mod tidy
echo "Cleaning go mod cache ..."
go clean -modcache
echo "Cleaning go cache ..."
go clean -cache
############
stage "Build"
############
echo "Invoking 'make clean' ..."
make clean
echo "Invoking 'make all' ..."
make all
@ -54,12 +49,12 @@ go test -v .
############
stage "Test(Authtoken)"
############
if [ -f ~/.xs_id ]; then
echo "Clearing test user $USER ~/.xs_id file ..."
mv ~/.xs_id ~/.xs_id.bak
if [ -f ~/.config/xs/.xs_id ]; then
echo "Clearing test user $USER .xs_id file ..."
mv ~/.config/xs/.xs_id ~/.config/xs/.xs_id.bak
fi
echo "Setting dummy authtoken in ~/.xs_id ..."
echo "localhost:${USER}:asdfasdfasdf" >~/.xs_id
echo "Setting dummy authtoken in .xs_id ..."
echo "localhost:${USER}:asdfasdfasdf" >~/.config/xs/.xs_id
echo "Performing remote command on @localhost via authtoken login ..."
tokentest=$(timeout 10 xs -x "echo -n FOO" @localhost)
if [ "${tokentest}" != "FOO" ]; then
@ -99,9 +94,9 @@ stage "Test(xc C->S)"
############
echo "TODO ..."
if [ -f ~/.xs_id.bak ]; then
echo "Restoring test user $USER ~/.xs_id file ..."
mv ~/.xs_id.bak ~/.xs_id
if [ -f ~/.config/xs/.xs_id.bak ]; then
echo "Restoring test user $USER .xs_id file ..."
mv ~/.config/xs/.xs_id.bak ~/.config/xs/.xs_id
fi
############

36
go.mod
View File

@ -1,36 +0,0 @@
module blitter.com/go/xs
go 1.17
require (
blitter.com/go/cryptmt v1.0.2
blitter.com/go/goutmp v1.0.6
blitter.com/go/herradurakex v1.0.0
blitter.com/go/hopscotch v0.0.0-20211113042251-b8a306eea4dc
blitter.com/go/kyber v0.0.0-20200130200857-6f2021cb88d9
blitter.com/go/newhope v0.0.0-20200130200750-192fc08a8aae
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da
github.com/creack/pty v1.1.18
github.com/jameskeane/bcrypt v0.0.0-20120420032655-c3cd44c1e20f
github.com/kuking/go-frodokem v1.0.2
github.com/mattn/go-isatty v0.0.14
github.com/xtaci/kcp-go v5.4.20+incompatible
golang.org/x/crypto v0.0.0-20220408190544-5352b0902921
golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f
gopkg.in/hlandau/passlib.v1 v1.0.11
)
require (
blitter.com/go/chacha20 v0.0.0-20200130200441-214e4085f54c // indirect
blitter.com/go/mtwist v1.0.1 // indirect
github.com/klauspost/cpuid/v2 v2.0.6 // indirect
github.com/klauspost/reedsolomon v1.9.16 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 // indirect
github.com/templexxx/xor v0.0.0-20191217153810-f85b25db303b // indirect
github.com/tjfoc/gmsm v1.4.1 // indirect
github.com/xtaci/lossyconn v0.0.0-20200209145036-adba10fffc37 // indirect
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect
gopkg.in/hlandau/easymetric.v1 v1.0.0 // indirect
gopkg.in/hlandau/measurable.v1 v1.0.1 // indirect
)

145
go.sum
View File

@ -1,145 +0,0 @@
blitter.com/go/chacha20 v0.0.0-20200130200441-214e4085f54c h1:LcnFFg6MCIJHf26P7eOUST45fNLHJI5erq0gWZaDLCo=
blitter.com/go/chacha20 v0.0.0-20200130200441-214e4085f54c/go.mod h1:EMJtRcf22WCtHGiXCw+NB/Sb/PYcXtUgUql6LDEwyXo=
blitter.com/go/cryptmt v1.0.2 h1:ZcLhQk7onUssXyQwG3GdXDXctCVnNL+b7aFuvwOdKXc=
blitter.com/go/cryptmt v1.0.2/go.mod h1:tdME2J3O4agaDAYIYNQzzuB28yVGnPSMmV3a/ucSU84=
blitter.com/go/goutmp v1.0.6 h1:jRKRw2WalVBza4T50etAfbvT2xp9G5uykIHTvyB5r0k=
blitter.com/go/goutmp v1.0.6/go.mod h1:DnK/uLBu1/1yLFiuVlmwvWErzAWVp+pDv7t6ZaQRLNc=
blitter.com/go/herradurakex v1.0.0 h1:6XaxY+JLT1HUWPF0gYJnjX3pVjrw4YhYZEzZ1U0wkyc=
blitter.com/go/herradurakex v1.0.0/go.mod h1:m3+vYZX+2dDjdo+n/HDnXEYJX9pwmNeQLgAfJM8mtxw=
blitter.com/go/hopscotch v0.0.0-20211113042251-b8a306eea4dc h1:IS+jxdKSdlqp6TWG3yMoBde/cctBEMwMDg588JHxgTE=
blitter.com/go/hopscotch v0.0.0-20211113042251-b8a306eea4dc/go.mod h1:9Da1oy0t9aUw3wviba+2mP1inbLGbDuCKAO3mmGQha4=
blitter.com/go/kyber v0.0.0-20200130200857-6f2021cb88d9 h1:D45AnrNphtvczBXRp5JQicZRTgaK/Is5bgPDDvRKhTc=
blitter.com/go/kyber v0.0.0-20200130200857-6f2021cb88d9/go.mod h1:SK6QfGG72lIfKW1Td0wH7f0wwN5nSIhV3K+wvzGNjrw=
blitter.com/go/mtwist v1.0.1 h1:PxmoWexfMpLmc8neHP/PcRc3s17ct7iz4d5W/qJVt04=
blitter.com/go/mtwist v1.0.1/go.mod h1:aU82Nx8+b1v8oZRNqImfEDzDTPim81rY0ACKAIclV18=
blitter.com/go/newhope v0.0.0-20200130200750-192fc08a8aae h1:YBBaCcdYRrI1btsmcMTv1VMPmaSXXz0RwKOTgMJYSRU=
blitter.com/go/newhope v0.0.0-20200130200750-192fc08a8aae/go.mod h1:ywoxfDBqInPsqtnxYsmS4SYMJ5D/kNcrFgpvI+Xcun0=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY=
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/jameskeane/bcrypt v0.0.0-20120420032655-c3cd44c1e20f h1:UWGE8Vi+1Agt0lrvnd7UsmvwqWKRzb9byK9iQmsbY0Y=
github.com/jameskeane/bcrypt v0.0.0-20120420032655-c3cd44c1e20f/go.mod h1:u+9Snq0w+ZdYKi8BBoaxnEwWu0fY4Kvu9ByFpM51t1s=
github.com/klauspost/cpuid/v2 v2.0.6 h1:dQ5ueTiftKxp0gyjKSx5+8BtPWkyQbd95m8Gys/RarI=
github.com/klauspost/cpuid/v2 v2.0.6/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/reedsolomon v1.9.16 h1:mR0AwphBwqFv/I3B9AHtNKvzuowI1vrj8/3UX4XRmHA=
github.com/klauspost/reedsolomon v1.9.16/go.mod h1:eqPAcE7xar5CIzcdfwydOEdcmchAKAP/qs14y4GCBOk=
github.com/kuking/go-frodokem v1.0.2 h1:sxdguENCyr6WnLbJ/cjz0AYCW75H1b+E6zXY2ldZnUU=
github.com/kuking/go-frodokem v1.0.2/go.mod h1:83ZX1kHOd72ouCsvbffCqJIj7Ih83MQTAjH2QbqzLZk=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 h1:89CEmDvlq/F7SJEOqkIdNDGJXrQIhuIx9D2DBXjavSU=
github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161/go.mod h1:wM7WEvslTq+iOEAMDLSzhVuOt5BRZ05WirO+b09GHQU=
github.com/templexxx/xor v0.0.0-20191217153810-f85b25db303b h1:fj5tQ8acgNUr6O8LEplsxDhUIe2573iLkJc+PqnzZTI=
github.com/templexxx/xor v0.0.0-20191217153810-f85b25db303b/go.mod h1:5XA7W9S6mni3h5uvOC75dA3m9CCCaS83lltmc0ukdi4=
github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=
github.com/ulikunitz/xz v0.5.8 h1:ERv8V6GKqVi23rgu5cj9pVfVzJbOqAY2Ntl88O6c2nQ=
github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/xtaci/kcp-go v5.4.20+incompatible h1:TN1uey3Raw0sTz0Fg8GkfM0uH3YwzhnZWQ1bABv5xAg=
github.com/xtaci/kcp-go v5.4.20+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE=
github.com/xtaci/lossyconn v0.0.0-20200209145036-adba10fffc37 h1:EWU6Pktpas0n8lLQwDsRyZfmkPeRbdgPtW609es+/9E=
github.com/xtaci/lossyconn v0.0.0-20200209145036-adba10fffc37/go.mod h1:HpMP7DB2CyokmAh4lp0EQnnWhmycP/TvwBGzvuie+H0=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20220408190544-5352b0902921 h1:iU7T1X1J6yxDr0rda54sWGkHgOp5XJrqm79gcNlC2VM=
golang.org/x/crypto v0.0.0-20220408190544-5352b0902921/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190902133755-9109b7679e13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f h1:8w7RhxzTVgUzw/AH/9mUV5q0vMgy40SQRursCcfmkCw=
golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/hlandau/easymetric.v1 v1.0.0 h1:ZbfbH7W3giuVDjWUoFhDOjjv20hiPr5HZ2yMV5f9IeE=
gopkg.in/hlandau/easymetric.v1 v1.0.0/go.mod h1:yh75hypuFzAxmvECh3ZKGCvFnIfapYJh2wv7ASaX2RE=
gopkg.in/hlandau/measurable.v1 v1.0.1 h1:wH5UZKCRUnRr1iD+xIZfwhtxhmr+bprRJttqA1Rklf4=
gopkg.in/hlandau/measurable.v1 v1.0.1/go.mod h1:6N+SYJGMTmetsx7wskULP+juuO+++tsHJkAgzvzsbuM=
gopkg.in/hlandau/passlib.v1 v1.0.11 h1:vKeHwGRdWBD9mm4bJ56GAAdBXpFUYvg/BYYkmphjnmA=
gopkg.in/hlandau/passlib.v1 v1.0.11/go.mod h1:wxGAv2CtQHlzWY8NJp+p045yl4WHyX7v2T6XbOcmqjM=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View File

@ -5,6 +5,7 @@ package xs
import (
"errors"
"io"
"os"
unix "golang.org/x/sys/unix"
)
@ -30,7 +31,8 @@ type State struct {
// MakeRaw put the terminal connected to the given file descriptor into raw
// mode and returns the previous state of the terminal so that it can be
// restored.
func MakeRaw(fd uintptr) (*State, error) {
func MakeRaw(f *os.File) (*State, error) {
fd := f.Fd()
termios, err := unix.IoctlGetTermios(int(fd), ioctlReadTermios)
if err != nil {
return nil, err
@ -56,8 +58,8 @@ func MakeRaw(fd uintptr) (*State, error) {
// GetState returns the current state of a terminal which may be useful to
// restore the terminal after a signal.
func GetState(fd uintptr) (*State, error) {
termios, err := unix.IoctlGetTermios(int(fd), ioctlReadTermios)
func GetState(f *os.File) (*State, error) {
termios, err := unix.IoctlGetTermios(int(f.Fd()), ioctlReadTermios)
if err != nil {
return nil, err
}
@ -67,9 +69,9 @@ func GetState(fd uintptr) (*State, error) {
// Restore restores the terminal connected to the given file descriptor to a
// previous state.
func Restore(fd uintptr, state *State) error {
func Restore(f *os.File, state *State) error {
if state != nil {
return unix.IoctlSetTermios(int(fd), ioctlWriteTermios, &state.termios)
return unix.IoctlSetTermios(int(f.Fd()), ioctlWriteTermios, &state.termios)
} else {
return errors.New("nil State")
}
@ -78,7 +80,8 @@ func Restore(fd uintptr, state *State) error {
// ReadPassword reads a line of input from a terminal without local echo. This
// is commonly used for inputting passwords and other sensitive data. The slice
// returned does not include the \n.
func ReadPassword(fd uintptr) ([]byte, error) {
func ReadPassword(f *os.File) ([]byte, error) {
fd := f.Fd()
termios, err := unix.IoctlGetTermios(int(fd), ioctlReadTermios)
if err != nil {
return nil, err

View File

@ -1,3 +1,4 @@
//go:build windows
// +build windows
// Note the terminal manipulation functions herein are mostly stubs. They
@ -15,10 +16,12 @@
package xs
import (
"io"
"bufio"
"fmt"
"log"
"os"
"os/exec"
"golang.org/x/sys/windows"
"os/signal"
)
type State struct {
@ -27,67 +30,84 @@ type State struct {
// MakeRaw put the terminal connected to the given file descriptor into raw
// mode and returns the previous state of the terminal so that it can be
// restored.
func MakeRaw(fd uintptr) (*State, error) {
// This doesn't really work. The exec.Command() runs a sub-shell
// so the stty mods don't affect the client process.
cmd := exec.Command("stty", "-echo raw")
cmd.Run()
func MakeRaw(f *os.File) (*State, error) {
cmd := exec.Command("stty", "-echo", "raw")
cmd.Stdin = f
err := cmd.Run()
if err != nil {
log.Fatal(err)
return &State{}, err
}
// MSYS2/CYGWIN: wintty needs CTRL-C caught
// ----------------------------------------
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt, os.Kill)
go func() {
for sig := range c {
_ = sig
//fmt.Println(sig)
}
}()
// ----------------------------------------
return &State{}, nil
}
// GetState returns the current state of a terminal which may be useful to
// restore the terminal after a signal.
func GetState(fd uintptr) (*State, error) {
func GetState(f *os.File) (*State, error) {
return &State{}, nil
}
// Restore restores the terminal connected to the given file descriptor to a
// previous state.
func Restore(fd uintptr, state *State) error {
cmd := exec.Command("stty", "echo cooked")
cmd.Run()
func Restore(f *os.File, state *State) error {
cmd := exec.Command("stty", "sane")
cmd.Stdin = f
err := cmd.Run()
if err != nil {
log.Fatal(err)
return nil
}
return nil
}
// ReadPassword reads a line of input from a terminal without local echo. This
// is commonly used for inputting passwords and other sensitive data. The slice
// returned does not include the \n.
func ReadPassword(fd uintptr) ([]byte, error) {
return readPasswordLine(passwordReader(fd))
}
// passwordReader is an io.Reader that reads from a specific file descriptor.
type passwordReader windows.Handle
func (r passwordReader) Read(buf []byte) (int, error) {
return windows.Read(windows.Handle(r), buf)
}
// readPasswordLine reads from reader until it finds \n or io.EOF.
// The slice returned does not include the \n.
// readPasswordLine also ignores any \r it finds.
func readPasswordLine(reader io.Reader) ([]byte, error) {
var buf [1]byte
var ret []byte
for {
n, err := reader.Read(buf[:])
if n > 0 {
switch buf[0] {
case '\n':
return ret, nil
case '\r':
// remove \r from passwords on Windows
default:
ret = append(ret, buf[0])
}
continue
}
func ReadPassword(f *os.File) (pw []byte, err error) {
sttycmd, err := exec.LookPath("stty")
if err != nil {
return nil, err
} else {
//fmt.Printf("stty found at: %v\n", sttycmd)
cmdOff := exec.Command(sttycmd, "-echo")
cmdOff.Stdin = f //os.Stdin
cmdOff.Stdout = nil //os.Stdout
cmdOff.Stderr = nil //os.Stderr
err = cmdOff.Run()
if err != nil {
if err == io.EOF && len(ret) > 0 {
return ret, nil
}
return ret, err
return nil, err
}
//fmt.Printf("Enter password:")
scanner := bufio.NewScanner(os.Stdin)
scanner.Scan()
err = scanner.Err()
if err != nil {
return nil, err
}
pw = scanner.Bytes()
fmt.Println()
cmdOn := exec.Command(sttycmd, "echo")
cmdOn.Stdin = f //os.Stdin
cmdOn.Stdout = nil //os.Stdout
cmdOn.Stderr = nil //os.Stderr
err = cmdOn.Run()
if err != nil {
return nil, err
}
}
return
}

View File

@ -1,39 +0,0 @@
#!/bin/bash
#
## This wrapper may be used within the MSYS/mintty Windows
## shell environment to have a functioning hkexsh client with
## working 'raw' mode and hidden password entry.
##
## mintty uses named pipes and ptys to get a more POSIX-like
## terminal (incl. VT/ANSI codes) rather than the dumb Windows
## console interface; however Go on Windows does not have functioning
## MSYS/mintty code to set raw, echo etc. modes.
##
## Someday it would be preferable to put native Windows term mode
## code into the client build, but this is 'good enough' for now
## (with the exception of tty rows/cols not being set based on
## info from the server).
##
## INSTALLATION
## --
## Build the client, put it somewhere in your $PATH with this
## wrapper and edit the name of the client binary
## eg.,
## $ cp hkexsh.exe /usr/bin/.hkexsh.exe
## $ cp mintty_wrapper.sh /usr/bin/hkexsh
####
trap cleanup EXIT ERR
cleanup() {
stty sane
}
me="$(basename "$(test -L "$0" && readlink "$0" || echo "$0")")"
if [ ${1}x == "-hx" ]; then
_${me} -h
else
stty -echo raw icrnl
_${me} $@
fi

View File

@ -1,3 +1,4 @@
//go:build linux || freebsd
// +build linux freebsd
package main
@ -30,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,gosec
conn.WritePacket([]byte(termSzPacket), xsnet.CSOTermSize) //nolint:errcheck
}
}()
ch <- syscall.SIGWINCH // Initial resize.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

View File

@ -10,207 +10,207 @@ digraph gocallvis {
pad="0.0";
nodesep="0.35";
node [shape="ellipse" style="filled" fillcolor="honeydew" fontname="Verdana" penwidth="1.0" margin="0.05,0.0"];
node [shape="box" style="filled,rounded" fillcolor="honeydew" fontname="Verdana" penwidth="1.0" margin="0.05,0.0"];
edge [minlen="2"]
subgraph "cluster_focus" {
label="main";
bgcolor="#e6ecfa";
label="main";
labelloc="t";
labeljust="c";
fontsize="18";
bgcolor="#e6ecfa";
"blitter.com/go/xs/xs.restoreTermState" [ fillcolor="lightblue" label="restoreTermState" penwidth="0.5" tooltip="blitter.com/go/xs/xs.restoreTermState | defined in xs.go:1095\nat xs.go:1096: calling [blitter.com/go/xs.Restore]" ]
"blitter.com/go/xs/xs.main$2" [ fillcolor="lightblue" label="deferCloseChaff" style="dotted,filled" tooltip="blitter.com/go/xs/xs.main$2 | defined in xs.go:974\nat xs.go:977: calling [blitter.com/go/xs/xs.exitWithStatus]\nat xs.go:975: calling [blitter.com/go/xs/xs.restoreTermState]" ]
"blitter.com/go/xs/xs.exitWithStatus" [ fillcolor="lightblue" label="exitWithStatus" penwidth="0.5" tooltip="blitter.com/go/xs/xs.exitWithStatus | defined in xs.go:1100" ]
"blitter.com/go/xs/xs.handleTermResizes$1" [ label="handleTermResizes$1" style="dotted,filled" tooltip="blitter.com/go/xs/xs.handleTermResizes$1 | defined in termsize_unix.go:21\nat termsize_unix.go:27: calling [blitter.com/go/xs/xs.GetSize]\nat termsize_unix.go:33: calling [(*blitter.com/go/xs/xsnet.Conn).WritePacket]" fillcolor="lightblue" ]
"blitter.com/go/xs/xs.GetSize" [ tooltip="blitter.com/go/xs/xs.GetSize | defined in xs.go:221" fillcolor="lightblue" label="GetSize" penwidth="1.5" ]
"blitter.com/go/xs/xs.sendSessionParams" [ fillcolor="lightblue" label="sendSessionParams" penwidth="0.5" tooltip="blitter.com/go/xs/xs.sendSessionParams | defined in xs.go:649\nat xs.go:651: calling [(blitter.com/go/xs.Session).Who]\nat xs.go:659: calling [(blitter.com/go/xs.Session).Who]\nat xs.go:651: calling [(blitter.com/go/xs.Session).AuthCookie]\nat xs.go:675: calling [(blitter.com/go/xs.Session).AuthCookie]\nat xs.go:651: calling [(blitter.com/go/xs.Session).ConnHost]\nat xs.go:663: calling [(blitter.com/go/xs.Session).ConnHost]\nat xs.go:651: calling [(blitter.com/go/xs.Session).Op]\nat xs.go:655: calling [(blitter.com/go/xs.Session).Op]\nat xs.go:675: calling [(blitter.com/go/xs/xsnet.Conn).Write]\nat xs.go:671: calling [(blitter.com/go/xs/xsnet.Conn).Write]\nat xs.go:667: calling [(blitter.com/go/xs/xsnet.Conn).Write]\nat xs.go:663: calling [(blitter.com/go/xs/xsnet.Conn).Write]\nat xs.go:659: calling [(blitter.com/go/xs/xsnet.Conn).Write]\nat xs.go:655: calling [(blitter.com/go/xs/xsnet.Conn).Write]\nat xs.go:651: calling [(blitter.com/go/xs.Session).Cmd]\nat xs.go:671: calling [(blitter.com/go/xs.Session).Cmd]\nat xs.go:651: calling [(blitter.com/go/xs.Session).TermType]\nat xs.go:667: calling [(blitter.com/go/xs.Session).TermType]" ]
"blitter.com/go/xs/xs.main$3" [ label="main$3" style="dotted,filled" tooltip="blitter.com/go/xs/xs.main$3 | defined in xs.go:1043\nat xs.go:1046: calling [math/rand.Intn]\nat xs.go:1050: calling [(*blitter.com/go/xs/xsnet.Conn).WritePacket]" fillcolor="lightblue" ]
"blitter.com/go/xs/xs.handleTermResizes" [ fillcolor="lightblue" label="handleTermResizes" penwidth="0.5" tooltip="blitter.com/go/xs/xs.handleTermResizes | defined in termsize_unix.go:16\nat termsize_unix.go:21: calling [blitter.com/go/xs/xs.handleTermResizes$1]" ]
"blitter.com/go/xs/xs.main" [ penwidth="0.5" tooltip="blitter.com/go/xs/xs.main | defined in xs.go:680\nat xs.go:769: calling [blitter.com/go/xs/xs.main$1]\nat xs.go:782: calling [blitter.com/go/xs/xs.localUserName]\nat xs.go:1024: calling [blitter.com/go/xs/xs.rejectUserMsg]\nat xs.go:843: calling [blitter.com/go/xs/logger.New]\nat xs.go:938: calling [blitter.com/go/xs/xsnet.Dial]\nat xs.go:1003: calling [(*blitter.com/go/xs.Session).SetStatus]\nat xs.go:1021: calling [(*blitter.com/go/xs.Session).SetStatus]\nat xs.go:1025: calling [(*blitter.com/go/xs.Session).SetStatus]\nat xs.go:1064: calling [(*blitter.com/go/xs.Session).SetStatus]\nat xs.go:1005: calling [(blitter.com/go/xs.Session).Status]\nat xs.go:1067: calling [(blitter.com/go/xs.Session).Status]\nat xs.go:1069: calling [(blitter.com/go/xs.Session).Status]\nat xs.go:1079: calling [(blitter.com/go/xs.Session).Status]\nat xs.go:1053: calling [blitter.com/go/xs/xs.main$3]\nat xs.go:831: calling [blitter.com/go/xs/xs.usageShell]\nat xs.go:956: calling [blitter.com/go/xs.MakeRaw]\nat xs.go:1017: calling [(blitter.com/go/xs/xsnet.Conn).Read]\nat xs.go:1059: calling [blitter.com/go/xs/xs.launchTuns]\nat xs.go:1063: calling [blitter.com/go/xs/xs.doCopyMode]\nat xs.go:950: calling [(*blitter.com/go/xs/xsnet.Conn).Close]\nat xs.go:775: calling [blitter.com/go/xs/xs.parseNonSwitchArgs]\nat xs.go:962: calling [blitter.com/go/xs/xs.restoreTermState]\nat xs.go:1032: calling [(*blitter.com/go/xs/xsnet.Conn).EnableChaff]\nat xs.go:1002: calling [blitter.com/go/xs/xs.restoreTermState]\nat xs.go:1068: calling [blitter.com/go/xs/xs.restoreTermState]\nat xs.go:1074: calling [blitter.com/go/xs/xs.restoreTermState]\nat xs.go:831: calling [blitter.com/go/xs/xs.usageCp]\nat xs.go:955: calling [github.com/mattn/go-isatty.IsTerminal]\nat xs.go:1000: calling [blitter.com/go/xs/xs.sendSessionParams]\nat xs.go:751: calling [blitter.com/go/xs/xs.exitWithStatus]\nat xs.go:832: calling [blitter.com/go/xs/xs.exitWithStatus]\nat xs.go:941: calling [blitter.com/go/xs/xs.exitWithStatus]\nat xs.go:1005: calling [blitter.com/go/xs/xs.exitWithStatus]\nat xs.go:1079: calling [blitter.com/go/xs/xs.exitWithStatus]\nat xs.go:844: calling [blitter.com/go/xs/xsnet.Init]\nat xs.go:1028: calling [(*blitter.com/go/xs/xsnet.Conn).SetupChaff]\nat xs.go:983: calling [blitter.com/go/xs.ReadPassword]\nat xs.go:999: calling [blitter.com/go/xs.NewSession]\nat xs.go:1033: calling [(*blitter.com/go/xs/xsnet.Conn).DisableChaff]\nat xs.go:1034: calling [(*blitter.com/go/xs/xsnet.Conn).ShutdownChaff]\nat xs.go:1060: calling [blitter.com/go/xs/xs.doShellMode]" fillcolor="lightblue" label="main" ]
"blitter.com/go/xs/xs.parseNonSwitchArgs" [ tooltip="blitter.com/go/xs/xs.parseNonSwitchArgs | defined in xs.go:599" fillcolor="lightblue" label="parseNonSwitchArgs" penwidth="0.5" ]
"blitter.com/go/xs/xs.main$1" [ fillcolor="lightblue" label="deferRestore" style="dotted,filled" tooltip="blitter.com/go/xs/xs.main$1 | defined in xs.go:769" ]
"blitter.com/go/xs/xs.localUserName" [ fillcolor="lightblue" label="localUserName" penwidth="0.5" tooltip="blitter.com/go/xs/xs.localUserName | defined in xs.go:1084" ]
"blitter.com/go/xs/xs.rejectUserMsg" [ fillcolor="lightblue" label="rejectUserMsg" penwidth="0.5" tooltip="blitter.com/go/xs/xs.rejectUserMsg | defined in xs.go:576\nat xs.go:577: calling [blitter.com/go/xs/spinsult.GetSentence]" ]
"blitter.com/go/xs/xs.reqTunnel" [ penwidth="0.5" tooltip="blitter.com/go/xs/xs.reqTunnel | defined in xs.go:584\nat xs.go:594: calling [(*blitter.com/go/xs/xsnet.Conn).WritePacket]\nat xs.go:593: calling [blitter.com/go/xs/logger.LogDebug]" fillcolor="lightblue" label="reqTunnel" ]
"blitter.com/go/xs/xs.launchTuns" [ penwidth="0.5" tooltip="blitter.com/go/xs/xs.launchTuns | defined in xs.go:634\nat xs.go:645: calling [blitter.com/go/xs/xs.reqTunnel]" fillcolor="lightblue" label="launchTuns" ]
"blitter.com/go/xs/xs.doShellMode$1" [ tooltip="blitter.com/go/xs/xs.doShellMode$1 | defined in xs.go:490\nat xs.go:507: calling [(crypto/tls.alert).Error]\nat xs.go:503: calling [blitter.com/go/xs/xs.restoreTermState]\nat xs.go:518: calling [blitter.com/go/xs/xs.restoreTermState]\nat xs.go:509: calling [blitter.com/go/xs/xs.exitWithStatus]\nat xs.go:519: calling [blitter.com/go/xs/xs.exitWithStatus]\nat xs.go:507: calling [(compress/flate.InternalError).Error]\nat xs.go:507: calling [(crypto/aes.KeySizeError).Error]\nat xs.go:507: calling [(context.deadlineExceededError).Error]\nat xs.go:507: calling [(crypto/x509.UnhandledCriticalExtension).Error]\nat xs.go:514: calling [(blitter.com/go/xs.Session).Status]\nat xs.go:519: calling [(blitter.com/go/xs.Session).Status]\nat xs.go:507: calling [(crypto/x509.CertificateInvalidError).Error]\nat xs.go:507: calling [(crypto/tls.RecordHeaderError).Error]\nat xs.go:507: calling [(crypto/x509.HostnameError).Error]\nat xs.go:507: calling [(*github.com/pkg/errors.fundamental).Error]\nat xs.go:491: calling [blitter.com/go/xs/xs.doShellMode$1$1]\nat xs.go:507: calling [(*crypto/tls.permamentError).Error]\nat xs.go:507: calling [(crypto/x509.SystemRootsError).Error]\nat xs.go:507: calling [(compress/flate.CorruptInputError).Error]\nat xs.go:513: calling [(*blitter.com/go/xs/xsnet.Conn).GetStatus]\nat xs.go:513: calling [(*blitter.com/go/xs.Session).SetStatus]\nat xs.go:507: calling [(crypto/x509.UnknownAuthorityError).Error]" fillcolor="lightblue" label="shellRemoteToStdin" style="dotted,filled" ]
"blitter.com/go/xs/xs.doShellMode$1$1" [ fillcolor="lightblue" label="doShellMode$1$1" style="dotted,filled" tooltip="blitter.com/go/xs/xs.doShellMode$1$1 | defined in xs.go:491" ]
"blitter.com/go/xs/xs.doShellMode" [ fillcolor="lightblue" label="doShellMode" penwidth="0.5" tooltip="blitter.com/go/xs/xs.doShellMode | defined in xs.go:483\nat xs.go:527: calling [blitter.com/go/xs/xs.handleTermResizes]\nat xs.go:551: calling [blitter.com/go/xs/xs.doShellMode$2]\nat xs.go:522: calling [blitter.com/go/xs/xs.doShellMode$1]" ]
"blitter.com/go/xs/xs.copyBuffer" [ tooltip="blitter.com/go/xs/xs.copyBuffer | defined in xs.go:128\nat xs.go:186: calling [blitter.com/go/xs/xs.copyBuffer$2]\nat xs.go:186: calling [blitter.com/go/xs/xs.copyBuffer$3]\nat xs.go:193: calling [(blitter.com/go/xs/xsnet.Conn).Write]\nat xs.go:186: calling [blitter.com/go/xs/xs.copyBuffer$1]" fillcolor="lightblue" label="copyBuffer" penwidth="0.5" ]
"blitter.com/go/xs/xs.copyBuffer$1" [ style="dotted,filled" tooltip="blitter.com/go/xs/xs.copyBuffer$1 | defined in xs.go:144" fillcolor="lightblue" label="copyBuffer$1" ]
"blitter.com/go/xs/xs.copyBuffer$2" [ tooltip="blitter.com/go/xs/xs.copyBuffer$2 | defined in xs.go:145" fillcolor="lightblue" label="copyBuffer$2" style="dotted,filled" ]
"blitter.com/go/xs/xs.copyBuffer$3" [ fillcolor="lightblue" label="copyBuffer$3" style="dotted,filled" tooltip="blitter.com/go/xs/xs.copyBuffer$3 | defined in xs.go:146" ]
"blitter.com/go/xs/xs.Copy" [ fillcolor="lightblue" label="Copy" penwidth="1.5" tooltip="blitter.com/go/xs/xs.Copy | defined in xs.go:115\nat xs.go:116: calling [blitter.com/go/xs/xs.copyBuffer]" ]
"blitter.com/go/xs/xs.doShellMode$2$1" [ tooltip="blitter.com/go/xs/xs.doShellMode$2$1 | defined in xs.go:536\nat xs.go:539: calling [blitter.com/go/xs/xs.Copy]" fillcolor="lightblue" label="doShellMode$2$1" style="dotted,filled" ]
"blitter.com/go/xs/xs.doShellMode$2" [ fillcolor="lightblue" label="shellStdinToRemote" style="dotted,filled" tooltip="blitter.com/go/xs/xs.doShellMode$2 | defined in xs.go:534\nat xs.go:546: calling [blitter.com/go/xs/xs.restoreTermState]\nat xs.go:548: calling [blitter.com/go/xs/xs.exitWithStatus]\nat xs.go:541: calling [blitter.com/go/xs/xs.doShellMode$2$1]" ]
"blitter.com/go/xs/xs.doCopyMode" [ penwidth="0.5" tooltip="blitter.com/go/xs/xs.doCopyMode | defined in xs.go:354\nat xs.go:417: calling [(*blitter.com/go/xs/xsnet.Conn).WritePacket]\nat xs.go:356: calling [(blitter.com/go/xs.Session).Cmd]\nat xs.go:441: calling [(blitter.com/go/xs.Session).Cmd]\nat xs.go:430: calling [(*blitter.com/go/xs/xsnet.Conn).SetStatus]\nat xs.go:424: calling [(blitter.com/go/xs/xsnet.Conn).Read]\nat xs.go:364: calling [blitter.com/go/xs/xs.buildCmdLocalToRemote]\nat xs.go:435: calling [(*blitter.com/go/xs/xsnet.Conn).GetStatus]\nat xs.go:438: calling [(*blitter.com/go/xs/xsnet.Conn).GetStatus]\nat xs.go:473: calling [(*blitter.com/go/xs/xsnet.Conn).GetStatus]\nat xs.go:475: calling [(*blitter.com/go/xs/xsnet.Conn).GetStatus]\nat xs.go:444: calling [blitter.com/go/xs/xs.buildCmdRemoteToLocal]" fillcolor="lightblue" label="doCopyMode" ]
"blitter.com/go/xs/xs.buildCmdLocalToRemote" [ fillcolor="lightblue" label="buildCmdLocalToRemote" penwidth="0.5" tooltip="blitter.com/go/xs/xs.buildCmdLocalToRemote | defined in xs.go:270\nat xs.go:335: calling [blitter.com/go/xs/xs.getTreeSizeSubCmd]\nat xs.go:283: calling [blitter.com/go/xs.GetTool]\nat xs.go:311: calling [blitter.com/go/xs.GetTool]\nat xs.go:312: calling [blitter.com/go/xs.GetTool]" ]
"blitter.com/go/xs/xs.restoreTermState" [ fillcolor="lightblue" label="restoreTermState" penwidth="0.5" tooltip="blitter.com/go/xs/xs.restoreTermState | defined in xs.go:1117\nat xs.go:1118: calling [blitter.com/go/xs.Restore]" ]
"blitter.com/go/xs/xs.main$2" [ fillcolor="lightblue" label="deferCloseChaff" style="dotted,filled" tooltip="blitter.com/go/xs/xs.main$2 | defined in xs.go:996\nat xs.go:999: calling [blitter.com/go/xs/xs.exitWithStatus]\nat xs.go:997: calling [blitter.com/go/xs/xs.restoreTermState]" ]
"blitter.com/go/xs/xs.exitWithStatus" [ penwidth="0.5" tooltip="blitter.com/go/xs/xs.exitWithStatus | defined in xs.go:1122" fillcolor="lightblue" label="exitWithStatus" ]
"blitter.com/go/xs/xs.doCopyMode" [ fillcolor="lightblue" label="doCopyMode" penwidth="0.5" tooltip="blitter.com/go/xs/xs.doCopyMode | defined in xs.go:354\nat xs.go:435: calling [(*blitter.com/go/xs/xsnet.Conn).GetStatus]\nat xs.go:438: calling [(*blitter.com/go/xs/xsnet.Conn).GetStatus]\nat xs.go:473: calling [(*blitter.com/go/xs/xsnet.Conn).GetStatus]\nat xs.go:475: calling [(*blitter.com/go/xs/xsnet.Conn).GetStatus]\nat xs.go:430: calling [(*blitter.com/go/xs/xsnet.Conn).SetStatus]\nat xs.go:444: calling [blitter.com/go/xs/xs.buildCmdRemoteToLocal]\nat xs.go:424: calling [(blitter.com/go/xs/xsnet.Conn).Read]\nat xs.go:356: calling [(blitter.com/go/xs.Session).Cmd]\nat xs.go:441: calling [(blitter.com/go/xs.Session).Cmd]\nat xs.go:417: calling [(*blitter.com/go/xs/xsnet.Conn).WritePacket]\nat xs.go:364: calling [blitter.com/go/xs/xs.buildCmdLocalToRemote]" ]
"blitter.com/go/xs/xs.buildCmdLocalToRemote" [ tooltip="blitter.com/go/xs/xs.buildCmdLocalToRemote | defined in xs.go:270\nat xs.go:283: calling [blitter.com/go/xs.GetTool]\nat xs.go:311: calling [blitter.com/go/xs.GetTool]\nat xs.go:312: calling [blitter.com/go/xs.GetTool]\nat xs.go:335: calling [blitter.com/go/xs/xs.getTreeSizeSubCmd]" fillcolor="lightblue" label="buildCmdLocalToRemote" penwidth="0.5" ]
"blitter.com/go/xs/xs.getTreeSizeSubCmd" [ fillcolor="lightblue" label="getTreeSizeSubCmd" penwidth="0.5" tooltip="blitter.com/go/xs/xs.getTreeSizeSubCmd | defined in xs.go:342" ]
"blitter.com/go/xs/xs.buildCmdRemoteToLocal" [ fillcolor="lightblue" label="buildCmdRemoteToLocal" penwidth="0.5" tooltip="blitter.com/go/xs/xs.buildCmdRemoteToLocal | defined in xs.go:244\nat xs.go:256: calling [blitter.com/go/xs.GetTool]\nat xs.go:263: calling [blitter.com/go/xs.GetTool]" ]
"blitter.com/go/xs/xs.usageShell" [ label="usageShell" penwidth="0.5" tooltip="blitter.com/go/xs/xs.usageShell | defined in xs.go:559" fillcolor="lightblue" ]
"blitter.com/go/xs/xs.usageCp" [ fillcolor="lightblue" label="usageCp" penwidth="0.5" tooltip="blitter.com/go/xs/xs.usageCp | defined in xs.go:565" ]
"blitter.com/go/xs/xs.copyBuffer" [ fillcolor="lightblue" label="copyBuffer" penwidth="0.5" tooltip="blitter.com/go/xs/xs.copyBuffer | defined in xs.go:128\nat xs.go:186: calling [blitter.com/go/xs/xs.copyBuffer$2]\nat xs.go:186: calling [blitter.com/go/xs/xs.copyBuffer$3]\nat xs.go:193: calling [(blitter.com/go/xs/xsnet.Conn).Write]\nat xs.go:186: calling [blitter.com/go/xs/xs.copyBuffer$1]" ]
"blitter.com/go/xs/xs.copyBuffer$1" [ fillcolor="lightblue" label="copyBuffer$1" style="dotted,filled" tooltip="blitter.com/go/xs/xs.copyBuffer$1 | defined in xs.go:144" ]
"blitter.com/go/xs/xs.copyBuffer$2" [ fillcolor="lightblue" label="copyBuffer$2" style="dotted,filled" tooltip="blitter.com/go/xs/xs.copyBuffer$2 | defined in xs.go:145" ]
"blitter.com/go/xs/xs.copyBuffer$3" [ fillcolor="lightblue" label="copyBuffer$3" style="dotted,filled" tooltip="blitter.com/go/xs/xs.copyBuffer$3 | defined in xs.go:146" ]
"blitter.com/go/xs/xs.reqTunnel" [ tooltip="blitter.com/go/xs/xs.reqTunnel | defined in xs.go:584\nat xs.go:594: calling [(*blitter.com/go/xs/xsnet.Conn).WritePacket]\nat xs.go:593: calling [blitter.com/go/xs/logger.LogDebug]" fillcolor="lightblue" label="reqTunnel" penwidth="0.5" ]
"blitter.com/go/xs/xs.main" [ fillcolor="lightblue" label="main" penwidth="0.5" tooltip="blitter.com/go/xs/xs.main | defined in xs.go:680\nat xs.go:1056: calling [(*blitter.com/go/xs/xsnet.Conn).ShutdownChaff]\nat xs.go:1027: calling [(blitter.com/go/xs.Session).Status]\nat xs.go:1089: calling [(blitter.com/go/xs.Session).Status]\nat xs.go:1091: calling [(blitter.com/go/xs.Session).Status]\nat xs.go:1101: calling [(blitter.com/go/xs.Session).Status]\nat xs.go:864: calling [blitter.com/go/xs/logger.New]\nat xs.go:978: calling [blitter.com/go/xs.MakeRaw]\nat xs.go:1075: calling [blitter.com/go/xs/xs.main$3]\nat xs.go:1039: calling [(blitter.com/go/xs/xsnet.Conn).Read]\nat xs.go:960: calling [blitter.com/go/xs/xsnet.Dial]\nat xs.go:1081: calling [blitter.com/go/xs/xs.launchTuns]\nat xs.go:977: calling [github.com/mattn/go-isatty.IsTerminal]\nat xs.go:772: calling [blitter.com/go/xs/xs.exitWithStatus]\nat xs.go:853: calling [blitter.com/go/xs/xs.exitWithStatus]\nat xs.go:963: calling [blitter.com/go/xs/xs.exitWithStatus]\nat xs.go:1027: calling [blitter.com/go/xs/xs.exitWithStatus]\nat xs.go:1101: calling [blitter.com/go/xs/xs.exitWithStatus]\nat xs.go:1082: calling [blitter.com/go/xs/xs.doShellMode]\nat xs.go:1050: calling [(*blitter.com/go/xs/xsnet.Conn).SetupChaff]\nat xs.go:1022: calling [blitter.com/go/xs/xs.sendSessionParams]\nat xs.go:796: calling [blitter.com/go/xs/xs.parseNonSwitchArgs]\nat xs.go:1024: calling [blitter.com/go/xs/xs.restoreTermState]\nat xs.go:1090: calling [blitter.com/go/xs/xs.restoreTermState]\nat xs.go:1096: calling [blitter.com/go/xs/xs.restoreTermState]\nat xs.go:1055: calling [(*blitter.com/go/xs/xsnet.Conn).DisableChaff]\nat xs.go:1025: calling [(*blitter.com/go/xs.Session).SetStatus]\nat xs.go:1043: calling [(*blitter.com/go/xs.Session).SetStatus]\nat xs.go:1047: calling [(*blitter.com/go/xs.Session).SetStatus]\nat xs.go:1086: calling [(*blitter.com/go/xs.Session).SetStatus]\nat xs.go:852: calling [blitter.com/go/xs/xs.usageShell]\nat xs.go:984: calling [blitter.com/go/xs/xs.restoreTermState]\nat xs.go:1005: calling [blitter.com/go/xs.ReadPassword]\nat xs.go:1054: calling [(*blitter.com/go/xs/xsnet.Conn).EnableChaff]\nat xs.go:1085: calling [blitter.com/go/xs/xs.doCopyMode]\nat xs.go:972: calling [(*blitter.com/go/xs/xsnet.Conn).Close]\nat xs.go:1046: calling [blitter.com/go/xs/xs.rejectUserMsg]\nat xs.go:852: calling [blitter.com/go/xs/xs.usageCp]\nat xs.go:790: calling [blitter.com/go/xs/xs.main$1]\nat xs.go:803: calling [blitter.com/go/xs/xs.localUserName]\nat xs.go:865: calling [blitter.com/go/xs/xsnet.Init]\nat xs.go:1021: calling [blitter.com/go/xs.NewSession]" ]
"blitter.com/go/xs/xs.parseNonSwitchArgs" [ label="parseNonSwitchArgs" penwidth="0.5" tooltip="blitter.com/go/xs/xs.parseNonSwitchArgs | defined in xs.go:599" fillcolor="lightblue" ]
"blitter.com/go/xs/xs.main$1" [ fillcolor="lightblue" label="deferRestore" style="dotted,filled" tooltip="blitter.com/go/xs/xs.main$1 | defined in xs.go:790" ]
"blitter.com/go/xs/xs.localUserName" [ fillcolor="lightblue" label="localUserName" penwidth="0.5" tooltip="blitter.com/go/xs/xs.localUserName | defined in xs.go:1106" ]
"blitter.com/go/xs/xs.sendSessionParams" [ fillcolor="lightblue" label="sendSessionParams" penwidth="0.5" tooltip="blitter.com/go/xs/xs.sendSessionParams | defined in xs.go:649\nat xs.go:651: calling [(blitter.com/go/xs.Session).Cmd]\nat xs.go:671: calling [(blitter.com/go/xs.Session).Cmd]\nat xs.go:651: calling [(blitter.com/go/xs.Session).AuthCookie]\nat xs.go:675: calling [(blitter.com/go/xs.Session).AuthCookie]\nat xs.go:651: calling [(blitter.com/go/xs.Session).Who]\nat xs.go:659: calling [(blitter.com/go/xs.Session).Who]\nat xs.go:651: calling [(blitter.com/go/xs.Session).Op]\nat xs.go:655: calling [(blitter.com/go/xs.Session).Op]\nat xs.go:651: calling [(blitter.com/go/xs.Session).TermType]\nat xs.go:667: calling [(blitter.com/go/xs.Session).TermType]\nat xs.go:651: calling [(blitter.com/go/xs.Session).ConnHost]\nat xs.go:663: calling [(blitter.com/go/xs.Session).ConnHost]\nat xs.go:675: calling [(blitter.com/go/xs/xsnet.Conn).Write]\nat xs.go:671: calling [(blitter.com/go/xs/xsnet.Conn).Write]\nat xs.go:667: calling [(blitter.com/go/xs/xsnet.Conn).Write]\nat xs.go:663: calling [(blitter.com/go/xs/xsnet.Conn).Write]\nat xs.go:659: calling [(blitter.com/go/xs/xsnet.Conn).Write]\nat xs.go:655: calling [(blitter.com/go/xs/xsnet.Conn).Write]" ]
"blitter.com/go/xs/xs.rejectUserMsg" [ fillcolor="lightblue" label="rejectUserMsg" penwidth="0.5" tooltip="blitter.com/go/xs/xs.rejectUserMsg | defined in xs.go:576\nat xs.go:577: calling [blitter.com/go/xs/spinsult.GetSentence]" ]
"blitter.com/go/xs/xs.main$3" [ fillcolor="lightblue" label="main$3" style="dotted,filled" tooltip="blitter.com/go/xs/xs.main$3 | defined in xs.go:1065\nat xs.go:1068: calling [math/rand.Intn]\nat xs.go:1072: calling [(*blitter.com/go/xs/xsnet.Conn).WritePacket]" ]
"blitter.com/go/xs/xs.launchTuns" [ fillcolor="lightblue" label="launchTuns" penwidth="0.5" tooltip="blitter.com/go/xs/xs.launchTuns | defined in xs.go:634\nat xs.go:645: calling [blitter.com/go/xs/xs.reqTunnel]" ]
"blitter.com/go/xs/xs.doShellMode$1" [ tooltip="blitter.com/go/xs/xs.doShellMode$1 | defined in xs.go:490\nat xs.go:509: calling [blitter.com/go/xs/xs.exitWithStatus]\nat xs.go:519: calling [blitter.com/go/xs/xs.exitWithStatus]\nat xs.go:507: calling [(crypto/x509.SystemRootsError).Error]\nat xs.go:513: calling [(*blitter.com/go/xs.Session).SetStatus]\nat xs.go:507: calling [(crypto/x509.CertificateInvalidError).Error]\nat xs.go:507: calling [(vendor/golang.org/x/net/idna.labelError).Error]\nat xs.go:507: calling [(crypto/tls.RecordHeaderError).Error]\nat xs.go:507: calling [(crypto/x509.UnknownAuthorityError).Error]\nat xs.go:513: calling [(*blitter.com/go/xs/xsnet.Conn).GetStatus]\nat xs.go:507: calling [(crypto/tls.alert).Error]\nat xs.go:507: calling [(context.deadlineExceededError).Error]\nat xs.go:507: calling [(crypto/aes.KeySizeError).Error]\nat xs.go:507: calling [(crypto/x509.UnhandledCriticalExtension).Error]\nat xs.go:507: calling [(compress/flate.CorruptInputError).Error]\nat xs.go:507: calling [(*github.com/pkg/errors.fundamental).Error]\nat xs.go:491: calling [blitter.com/go/xs/xs.doShellMode$1$1]\nat xs.go:507: calling [(crypto/x509.HostnameError).Error]\nat xs.go:507: calling [(*crypto/tls.permanentError).Error]\nat xs.go:507: calling [(vendor/golang.org/x/net/idna.runeError).Error]\nat xs.go:514: calling [(blitter.com/go/xs.Session).Status]\nat xs.go:519: calling [(blitter.com/go/xs.Session).Status]\nat xs.go:503: calling [blitter.com/go/xs/xs.restoreTermState]\nat xs.go:518: calling [blitter.com/go/xs/xs.restoreTermState]\nat xs.go:507: calling [(compress/flate.InternalError).Error]" fillcolor="lightblue" label="shellRemoteToStdin" style="dotted,filled" ]
"blitter.com/go/xs/xs.doShellMode$1$1" [ fillcolor="lightblue" label="doShellMode$1$1" style="dotted,filled" tooltip="blitter.com/go/xs/xs.doShellMode$1$1 | defined in xs.go:491" ]
"blitter.com/go/xs/xs.doShellMode" [ fillcolor="lightblue" label="doShellMode" penwidth="0.5" tooltip="blitter.com/go/xs/xs.doShellMode | defined in xs.go:483\nat xs.go:522: calling [blitter.com/go/xs/xs.doShellMode$1]\nat xs.go:551: calling [blitter.com/go/xs/xs.doShellMode$2]\nat xs.go:527: calling [blitter.com/go/xs/xs.handleTermResizes]" ]
"blitter.com/go/xs/xs.handleTermResizes$1" [ tooltip="blitter.com/go/xs/xs.handleTermResizes$1 | defined in termsize_unix.go:21\nat termsize_unix.go:33: calling [(*blitter.com/go/xs/xsnet.Conn).WritePacket]\nat termsize_unix.go:27: calling [blitter.com/go/xs/xs.GetSize]" fillcolor="lightblue" label="handleTermResizes$1" style="dotted,filled" ]
"blitter.com/go/xs/xs.GetSize" [ penwidth="1.5" tooltip="blitter.com/go/xs/xs.GetSize | defined in xs.go:221" fillcolor="lightblue" label="GetSize" ]
"blitter.com/go/xs/xs.handleTermResizes" [ fillcolor="lightblue" label="handleTermResizes" penwidth="0.5" tooltip="blitter.com/go/xs/xs.handleTermResizes | defined in termsize_unix.go:16\nat termsize_unix.go:21: calling [blitter.com/go/xs/xs.handleTermResizes$1]" ]
"blitter.com/go/xs/xs.Copy" [ tooltip="blitter.com/go/xs/xs.Copy | defined in xs.go:115\nat xs.go:116: calling [blitter.com/go/xs/xs.copyBuffer]" fillcolor="lightblue" label="Copy" penwidth="1.5" ]
"blitter.com/go/xs/xs.doShellMode$2$1" [ fillcolor="lightblue" label="doShellMode$2$1" style="dotted,filled" tooltip="blitter.com/go/xs/xs.doShellMode$2$1 | defined in xs.go:536\nat xs.go:539: calling [blitter.com/go/xs/xs.Copy]" ]
"blitter.com/go/xs/xs.doShellMode$2" [ style="dotted,filled" tooltip="blitter.com/go/xs/xs.doShellMode$2 | defined in xs.go:534\nat xs.go:546: calling [blitter.com/go/xs/xs.restoreTermState]\nat xs.go:548: calling [blitter.com/go/xs/xs.exitWithStatus]\nat xs.go:541: calling [blitter.com/go/xs/xs.doShellMode$2$1]" fillcolor="lightblue" label="shellStdinToRemote" ]
"blitter.com/go/xs/xs.usageShell" [ fillcolor="lightblue" label="usageShell" penwidth="0.5" tooltip="blitter.com/go/xs/xs.usageShell | defined in xs.go:559" ]
"blitter.com/go/xs/xs.usageCp" [ label="usageCp" penwidth="0.5" tooltip="blitter.com/go/xs/xs.usageCp | defined in xs.go:565" fillcolor="lightblue" ]
subgraph "cluster_blitter.com/go/xs" {
tooltip="package: blitter.com/go/xs";
penwidth="0.8";
style="filled";
fillcolor="lightyellow";
style="filled";
rank="sink";
tooltip="package: blitter.com/go/xs";
label="xs";
URL="/?f=blitter.com/go/xs";
penwidth="0.8";
fontsize="16";
fillcolor="lightyellow";
fontname="Tahoma bold";
rank="sink";
"blitter.com/go/xs.Restore" [ fillcolor="moccasin" label="Restore" penwidth="1.5" tooltip="blitter.com/go/xs.Restore | defined in termmode_linux.go:70" ]
"blitter.com/go/xs.MakeRaw" [ tooltip="blitter.com/go/xs.MakeRaw | defined in termmode_linux.go:33" fillcolor="moccasin" label="MakeRaw" penwidth="1.5" ]
"blitter.com/go/xs.ReadPassword" [ fillcolor="moccasin" label="ReadPassword" penwidth="1.5" tooltip="blitter.com/go/xs.ReadPassword | defined in termmode_linux.go:81" ]
"blitter.com/go/xs.NewSession" [ tooltip="blitter.com/go/xs.NewSession | defined in session.go:130" fillcolor="moccasin" label="NewSession" penwidth="1.5" ]
"blitter.com/go/xs.GetTool" [ fillcolor="moccasin" label="GetTool" penwidth="1.5" tooltip="blitter.com/go/xs.GetTool | defined in auth.go:211" ]
"blitter.com/go/xs.Restore" [ tooltip="blitter.com/go/xs.Restore | defined in termmode_linux.go:70" fillcolor="moccasin" label="Restore" penwidth="1.5" ]
"blitter.com/go/xs.GetTool" [ fillcolor="moccasin" label="GetTool" penwidth="1.5" tooltip="blitter.com/go/xs.GetTool | defined in auth.go:217" ]
"blitter.com/go/xs.MakeRaw" [ fillcolor="moccasin" label="MakeRaw" penwidth="1.5" tooltip="blitter.com/go/xs.MakeRaw | defined in termmode_linux.go:33" ]
"blitter.com/go/xs.ReadPassword" [ tooltip="blitter.com/go/xs.ReadPassword | defined in termmode_linux.go:81" fillcolor="moccasin" label="ReadPassword" penwidth="1.5" ]
"blitter.com/go/xs.NewSession" [ fillcolor="moccasin" label="NewSession" penwidth="1.5" tooltip="blitter.com/go/xs.NewSession | defined in session.go:130" ]
subgraph "cluster_*blitter.com/go/xs.Session" {
labelloc="b";
style="rounded,filled";
fillcolor="wheat2";
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).SetStatus" [ fillcolor="moccasin" label="SetStatus" penwidth="1.5" tooltip="(*blitter.com/go/xs.Session).SetStatus | defined in session.go:125" ]
}
subgraph "cluster_blitter.com/go/xs.Session" {
fontcolor="#222222";
penwidth="0.5";
fontsize="15";
fontcolor="#222222";
labelloc="b";
style="rounded,filled";
fillcolor="wheat2";
label="(Session)";
tooltip="type: blitter.com/go/xs.Session";
penwidth="0.5";
fontsize="15";
"(blitter.com/go/xs.Session).Cmd" [ fillcolor="moccasin" label="Cmd" penwidth="1.5" tooltip="(blitter.com/go/xs.Session).Cmd | defined in session.go:79" ]
"(blitter.com/go/xs.Session).Op" [ fillcolor="moccasin" label="Op" penwidth="1.5" tooltip="(blitter.com/go/xs.Session).Op | defined in session.go:36" ]
"(blitter.com/go/xs.Session).Who" [ fillcolor="moccasin" label="Who" penwidth="1.5" tooltip="(blitter.com/go/xs.Session).Who | defined in session.go:46" ]
"(blitter.com/go/xs.Session).ConnHost" [ fillcolor="moccasin" label="ConnHost" penwidth="1.5" tooltip="(blitter.com/go/xs.Session).ConnHost | defined in session.go:56" ]
"(blitter.com/go/xs.Session).TermType" [ label="TermType" penwidth="1.5" tooltip="(blitter.com/go/xs.Session).TermType | defined in session.go:67" fillcolor="moccasin" ]
"(blitter.com/go/xs.Session).Cmd" [ fillcolor="moccasin" label="Cmd" penwidth="1.5" tooltip="(blitter.com/go/xs.Session).Cmd | defined in session.go:79" ]
"(blitter.com/go/xs.Session).AuthCookie" [ penwidth="1.5" tooltip="(blitter.com/go/xs.Session).AuthCookie | defined in session.go:92" fillcolor="moccasin" label="AuthCookie" ]
"(blitter.com/go/xs.Session).Status" [ fillcolor="moccasin" label="Status" penwidth="1.5" tooltip="(blitter.com/go/xs.Session).Status | defined in session.go:120" ]
"(blitter.com/go/xs.Session).ConnHost" [ label="ConnHost" penwidth="1.5" tooltip="(blitter.com/go/xs.Session).ConnHost | defined in session.go:56" fillcolor="moccasin" ]
"(blitter.com/go/xs.Session).TermType" [ fillcolor="moccasin" label="TermType" penwidth="1.5" tooltip="(blitter.com/go/xs.Session).TermType | defined in session.go:67" ]
"(blitter.com/go/xs.Session).AuthCookie" [ fillcolor="moccasin" label="AuthCookie" penwidth="1.5" tooltip="(blitter.com/go/xs.Session).AuthCookie | defined in session.go:92" ]
"(blitter.com/go/xs.Session).Status" [ penwidth="1.5" tooltip="(blitter.com/go/xs.Session).Status | defined in session.go:120" fillcolor="moccasin" label="Status" ]
}
}
subgraph "cluster_blitter.com/go/xs/logger" {
fillcolor="lightyellow";
URL="/?f=blitter.com/go/xs/logger";
penwidth="0.8";
style="filled";
penwidth="0.8";
fontsize="16";
fontname="Tahoma bold";
URL="/?f=blitter.com/go/xs/logger";
tooltip="package: blitter.com/go/xs/logger";
style="filled";
fillcolor="lightyellow";
rank="sink";
label="logger";
tooltip="package: blitter.com/go/xs/logger";
fontsize="16";
"blitter.com/go/xs/logger.LogDebug" [ fillcolor="moccasin" label="LogDebug" penwidth="1.5" tooltip="blitter.com/go/xs/logger.LogDebug | defined in logger_linux.go:103" ]
"blitter.com/go/xs/logger.New" [ fillcolor="moccasin" label="New" penwidth="1.5" tooltip="blitter.com/go/xs/logger.New | defined in logger_linux.go:71" ]
"blitter.com/go/xs/logger.LogDebug" [ penwidth="1.5" tooltip="blitter.com/go/xs/logger.LogDebug | defined in logger_linux.go:103" fillcolor="moccasin" label="LogDebug" ]
}
subgraph "cluster_blitter.com/go/xs/spinsult" {
style="filled";
penwidth="0.8";
style="filled";
fontname="Tahoma bold";
rank="sink";
URL="/?f=blitter.com/go/xs/spinsult";
tooltip="package: blitter.com/go/xs/spinsult";
penwidth="0.8";
fontsize="16";
fillcolor="lightyellow";
fontname="Tahoma bold";
label="spinsult";
URL="/?f=blitter.com/go/xs/spinsult";
tooltip="package: blitter.com/go/xs/spinsult";
"blitter.com/go/xs/spinsult.GetSentence" [ fillcolor="moccasin" label="GetSentence" penwidth="1.5" tooltip="blitter.com/go/xs/spinsult.GetSentence | defined in spinsult.go:43" ]
"blitter.com/go/xs/spinsult.GetSentence" [ label="GetSentence" penwidth="1.5" tooltip="blitter.com/go/xs/spinsult.GetSentence | defined in spinsult.go:43" fillcolor="moccasin" ]
}
subgraph "cluster_blitter.com/go/xs/xsnet" {
fillcolor="lightyellow";
fontname="Tahoma bold";
penwidth="0.8";
style="filled";
label="xsnet";
URL="/?f=blitter.com/go/xs/xsnet";
tooltip="package: blitter.com/go/xs/xsnet";
fontsize="16";
fontname="Tahoma bold";
rank="sink";
label="xsnet";
tooltip="package: blitter.com/go/xs/xsnet";
penwidth="0.8";
fontsize="16";
style="filled";
fillcolor="lightyellow";
URL="/?f=blitter.com/go/xs/xsnet";
"blitter.com/go/xs/xsnet.Init" [ fillcolor="moccasin" label="Init" penwidth="1.5" tooltip="blitter.com/go/xs/xsnet.Init | defined in net.go:192" ]
"blitter.com/go/xs/xsnet.Dial" [ fillcolor="moccasin" label="Dial" penwidth="1.5" tooltip="blitter.com/go/xs/xsnet.Dial | defined in net.go:890" ]
"blitter.com/go/xs/xsnet.Init" [ fillcolor="moccasin" label="Init" penwidth="1.5" tooltip="blitter.com/go/xs/xsnet.Init | defined in net.go:198" ]
"blitter.com/go/xs/xsnet.Dial" [ fillcolor="moccasin" label="Dial" penwidth="1.5" tooltip="blitter.com/go/xs/xsnet.Dial | defined in net.go:899" ]
subgraph "cluster_*blitter.com/go/xs/xsnet.Conn" {
labelloc="b";
fontcolor="#222222";
labelloc="b";
style="rounded,filled";
fillcolor="wheat2";
label="(*Conn)";
tooltip="type: *blitter.com/go/xs/xsnet.Conn";
penwidth="0.5";
fontsize="15";
fontcolor="#222222";
"(*blitter.com/go/xs/xsnet.Conn).WritePacket" [ fillcolor="moccasin" label="WritePacket" penwidth="1.5" tooltip="(*blitter.com/go/xs/xsnet.Conn).WritePacket | defined in net.go:1430" ]
"(*blitter.com/go/xs/xsnet.Conn).Close" [ label="Close" penwidth="1.5" tooltip="(*blitter.com/go/xs/xsnet.Conn).Close | defined in net.go:970" fillcolor="moccasin" ]
"(*blitter.com/go/xs/xsnet.Conn).SetupChaff" [ penwidth="1.5" tooltip="(*blitter.com/go/xs/xsnet.Conn).SetupChaff | defined in net.go:1556" fillcolor="moccasin" label="SetupChaff" ]
"(*blitter.com/go/xs/xsnet.Conn).EnableChaff" [ label="EnableChaff" penwidth="1.5" tooltip="(*blitter.com/go/xs/xsnet.Conn).EnableChaff | defined in net.go:1539" fillcolor="moccasin" ]
"(*blitter.com/go/xs/xsnet.Conn).DisableChaff" [ label="DisableChaff" penwidth="1.5" tooltip="(*blitter.com/go/xs/xsnet.Conn).DisableChaff | defined in net.go:1546" fillcolor="moccasin" ]
"(*blitter.com/go/xs/xsnet.Conn).ShutdownChaff" [ fillcolor="moccasin" label="ShutdownChaff" penwidth="1.5" tooltip="(*blitter.com/go/xs/xsnet.Conn).ShutdownChaff | defined in net.go:1551" ]
"(*blitter.com/go/xs/xsnet.Conn).GetStatus" [ fillcolor="moccasin" label="GetStatus" penwidth="1.5" tooltip="(*blitter.com/go/xs/xsnet.Conn).GetStatus | defined in net.go:208" ]
"(*blitter.com/go/xs/xsnet.Conn).SetStatus" [ fillcolor="moccasin" label="SetStatus" penwidth="1.5" tooltip="(*blitter.com/go/xs/xsnet.Conn).SetStatus | defined in net.go:212" ]
"(*blitter.com/go/xs/xsnet.Conn).WritePacket" [ fillcolor="moccasin" label="WritePacket" penwidth="1.5" tooltip="(*blitter.com/go/xs/xsnet.Conn).WritePacket | defined in net.go:1437" ]
"(*blitter.com/go/xs/xsnet.Conn).SetStatus" [ label="SetStatus" penwidth="1.5" tooltip="(*blitter.com/go/xs/xsnet.Conn).SetStatus | defined in net.go:218" fillcolor="moccasin" ]
"(*blitter.com/go/xs/xsnet.Conn).GetStatus" [ fillcolor="moccasin" label="GetStatus" penwidth="1.5" tooltip="(*blitter.com/go/xs/xsnet.Conn).GetStatus | defined in net.go:214" ]
"(*blitter.com/go/xs/xsnet.Conn).Close" [ fillcolor="moccasin" label="Close" penwidth="1.5" tooltip="(*blitter.com/go/xs/xsnet.Conn).Close | defined in net.go:979" ]
"(*blitter.com/go/xs/xsnet.Conn).SetupChaff" [ penwidth="1.5" tooltip="(*blitter.com/go/xs/xsnet.Conn).SetupChaff | defined in net.go:1563" fillcolor="moccasin" label="SetupChaff" ]
"(*blitter.com/go/xs/xsnet.Conn).EnableChaff" [ fillcolor="moccasin" label="EnableChaff" penwidth="1.5" tooltip="(*blitter.com/go/xs/xsnet.Conn).EnableChaff | defined in net.go:1546" ]
"(*blitter.com/go/xs/xsnet.Conn).DisableChaff" [ fillcolor="moccasin" label="DisableChaff" penwidth="1.5" tooltip="(*blitter.com/go/xs/xsnet.Conn).DisableChaff | defined in net.go:1553" ]
"(*blitter.com/go/xs/xsnet.Conn).ShutdownChaff" [ tooltip="(*blitter.com/go/xs/xsnet.Conn).ShutdownChaff | defined in net.go:1558" fillcolor="moccasin" label="ShutdownChaff" penwidth="1.5" ]
}
subgraph "cluster_blitter.com/go/xs/xsnet.Conn" {
labelloc="b";
style="rounded,filled";
fillcolor="wheat2";
label="(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).Write" [ tooltip="(blitter.com/go/xs/xsnet.Conn).Write | defined in net.go:1424" fillcolor="moccasin" label="Write" penwidth="1.5" ]
"(blitter.com/go/xs/xsnet.Conn).Read" [ fillcolor="moccasin" label="Read" penwidth="1.5" tooltip="(blitter.com/go/xs/xsnet.Conn).Read | defined in net.go:1185" ]
"(blitter.com/go/xs/xsnet.Conn).Read" [ penwidth="1.5" tooltip="(blitter.com/go/xs/xsnet.Conn).Read | defined in net.go:1192" fillcolor="moccasin" label="Read" ]
"(blitter.com/go/xs/xsnet.Conn).Write" [ fillcolor="moccasin" label="Write" penwidth="1.5" tooltip="(blitter.com/go/xs/xsnet.Conn).Write | defined in net.go:1431" ]
}
}
subgraph "cluster_compress/flate" {
URL="/?f=compress/flate";
fontsize="16";
style="filled";
fillcolor="lightyellow";
label="flate";
URL="/?f=compress/flate";
penwidth="0.8";
label="compress/flate";
fillcolor="#E0FFE1";
fontname="Tahoma bold";
rank="sink";
tooltip="package: compress/flate";
fontsize="16";
style="filled";
subgraph "cluster_compress/flate.CorruptInputError" {
tooltip="type: compress/flate.CorruptInputError";
penwidth="0.5";
fontsize="15";
fontcolor="#222222";
fontcolor="#222222";
labelloc="b";
style="rounded,filled";
fillcolor="#c2e3c2";
fillcolor="wheat2";
label="(CorruptInputError)";
tooltip="type: compress/flate.CorruptInputError";
penwidth="0.5";
fontsize="15";
"(compress/flate.CorruptInputError).Error" [ fillcolor="#adedad" label="Error" penwidth="1.5" tooltip="(compress/flate.CorruptInputError).Error | defined in inflate.go:35" ]
"(compress/flate.CorruptInputError).Error" [ penwidth="1.5" tooltip="(compress/flate.CorruptInputError).Error | defined in inflate.go:35" fillcolor="moccasin" label="Error" ]
}
@ -221,10 +221,10 @@ fontsize="15";
fontcolor="#222222";
labelloc="b";
style="rounded,filled";
fillcolor="#c2e3c2";
fillcolor="wheat2";
label="(InternalError)";
"(compress/flate.InternalError).Error" [ fillcolor="#adedad" label="Error" penwidth="1.5" tooltip="(compress/flate.InternalError).Error | defined in inflate.go:42" ]
"(compress/flate.InternalError).Error" [ tooltip="(compress/flate.InternalError).Error | defined in inflate.go:42" fillcolor="moccasin" label="Error" penwidth="1.5" ]
}
@ -233,46 +233,46 @@ label="(InternalError)";
subgraph "cluster_context" {
fontsize="16";
style="filled";
fontname="Tahoma bold";
penwidth="0.8";
rank="sink";
label="context";
penwidth="0.8";
fillcolor="lightyellow";
fontname="Tahoma bold";
rank="sink";
URL="/?f=context";
tooltip="package: context";
fillcolor="#E0FFE1";
subgraph "cluster_context.deadlineExceededError" {
tooltip="type: context.deadlineExceededError";
style="rounded,filled";
fillcolor="wheat2";
label="(deadlineExceededError)";
tooltip="type: context.deadlineExceededError";
penwidth="0.5";
fontsize="15";
fontcolor="#222222";
labelloc="b";
style="rounded,filled";
fillcolor="#c2e3c2";
label="(deadlineExceededError)";
"(context.deadlineExceededError).Error" [ fillcolor="#adedad" label="Error" penwidth="1.5" tooltip="(context.deadlineExceededError).Error | defined in context.go:165" ]
"(context.deadlineExceededError).Error" [ fillcolor="moccasin" label="Error" penwidth="1.5" tooltip="(context.deadlineExceededError).Error | defined in context.go:165" ]
}
}
subgraph "cluster_crypto/aes" {
label="crypto/aes";
tooltip="package: crypto/aes";
penwidth="0.8";
penwidth="0.8";
style="filled";
fillcolor="#E0FFE1";
fontname="Tahoma bold";
rank="sink";
fontsize="16";
label="aes";
URL="/?f=crypto/aes";
tooltip="package: crypto/aes";
fontsize="16";
fillcolor="lightyellow";
subgraph "cluster_crypto/aes.KeySizeError" {
style="rounded,filled";
fillcolor="#c2e3c2";
fillcolor="wheat2";
label="(KeySizeError)";
tooltip="type: crypto/aes.KeySizeError";
penwidth="0.5";
@ -280,7 +280,7 @@ fontsize="15";
fontcolor="#222222";
labelloc="b";
"(crypto/aes.KeySizeError).Error" [ fillcolor="#adedad" label="Error" penwidth="1.5" tooltip="(crypto/aes.KeySizeError).Error | defined in cipher.go:24" ]
"(crypto/aes.KeySizeError).Error" [ fillcolor="moccasin" label="Error" penwidth="1.5" tooltip="(crypto/aes.KeySizeError).Error | defined in cipher.go:24" ]
}
@ -288,111 +288,111 @@ labelloc="b";
subgraph "cluster_crypto/tls" {
penwidth="0.8";
style="filled";
fontsize="16";
fontname="Tahoma bold";
rank="sink";
URL="/?f=crypto/tls";
tooltip="package: crypto/tls";
fontsize="16";
fillcolor="#E0FFE1";
label="crypto/tls";
style="filled";
fillcolor="lightyellow";
rank="sink";
label="tls";
subgraph "cluster_*crypto/tls.permamentError" {
tooltip="type: *crypto/tls.permamentError";
subgraph "cluster_*crypto/tls.permanentError" {
label="(*permanentError)";
tooltip="type: *crypto/tls.permanentError";
penwidth="0.5";
fontsize="15";
fontcolor="#222222";
labelloc="b";
style="rounded,filled";
fillcolor="#c2e3c2";
label="(*permamentError)";
fillcolor="wheat2";
"(*crypto/tls.permamentError).Error" [ fillcolor="#adedad" label="Error" penwidth="1.5" tooltip="(*crypto/tls.permamentError).Error | defined in conn.go:175" ]
"(*crypto/tls.permanentError).Error" [ tooltip="(*crypto/tls.permanentError).Error | defined in conn.go:184" fillcolor="moccasin" label="Error" penwidth="1.5" ]
}
subgraph "cluster_crypto/tls.RecordHeaderError" {
label="(RecordHeaderError)";
fillcolor="wheat2";
label="(RecordHeaderError)";
tooltip="type: crypto/tls.RecordHeaderError";
penwidth="0.5";
fontsize="15";
fontcolor="#222222";
labelloc="b";
style="rounded,filled";
fillcolor="#c2e3c2";
"(crypto/tls.RecordHeaderError).Error" [ tooltip="(crypto/tls.RecordHeaderError).Error | defined in conn.go:566" fillcolor="#adedad" label="Error" penwidth="1.5" ]
"(crypto/tls.RecordHeaderError).Error" [ tooltip="(crypto/tls.RecordHeaderError).Error | defined in conn.go:571" fillcolor="moccasin" label="Error" penwidth="1.5" ]
}
subgraph "cluster_crypto/tls.alert" {
style="rounded,filled";
fillcolor="#c2e3c2";
fontcolor="#222222";
labelloc="b";
style="rounded,filled";
fillcolor="wheat2";
label="(alert)";
tooltip="type: crypto/tls.alert";
penwidth="0.5";
fontsize="15";
fontcolor="#222222";
labelloc="b";
"(crypto/tls.alert).Error" [ fillcolor="#adedad" label="Error" penwidth="1.5" tooltip="(crypto/tls.alert).Error | defined in alert.go:97" ]
"(crypto/tls.alert).Error" [ fillcolor="moccasin" label="Error" penwidth="1.5" tooltip="(crypto/tls.alert).Error | defined in alert.go:97" ]
}
}
subgraph "cluster_crypto/x509" {
style="filled";
URL="/?f=crypto/x509";
URL="/?f=crypto/x509";
style="filled";
fillcolor="lightyellow";
fontname="Tahoma bold";
rank="sink";
label="x509";
tooltip="package: crypto/x509";
penwidth="0.8";
fontsize="16";
fillcolor="#E0FFE1";
fontname="Tahoma bold";
rank="sink";
label="crypto/x509";
subgraph "cluster_crypto/x509.CertificateInvalidError" {
tooltip="type: crypto/x509.CertificateInvalidError";
label="(CertificateInvalidError)";
tooltip="type: crypto/x509.CertificateInvalidError";
penwidth="0.5";
fontsize="15";
fontcolor="#222222";
labelloc="b";
style="rounded,filled";
fillcolor="#c2e3c2";
label="(CertificateInvalidError)";
fillcolor="wheat2";
"(crypto/x509.CertificateInvalidError).Error" [ label="Error" penwidth="1.5" tooltip="(crypto/x509.CertificateInvalidError).Error | defined in verify.go:78" fillcolor="#adedad" ]
"(crypto/x509.CertificateInvalidError).Error" [ fillcolor="moccasin" label="Error" penwidth="1.5" tooltip="(crypto/x509.CertificateInvalidError).Error | defined in verify.go:67" ]
}
subgraph "cluster_crypto/x509.HostnameError" {
labelloc="b";
style="rounded,filled";
fillcolor="#c2e3c2";
label="(HostnameError)";
tooltip="type: crypto/x509.HostnameError";
penwidth="0.5";
fontsize="15";
fontcolor="#222222";
"(crypto/x509.HostnameError).Error" [ fillcolor="#adedad" label="Error" penwidth="1.5" tooltip="(crypto/x509.HostnameError).Error | defined in verify.go:109" ]
}
subgraph "cluster_crypto/x509.SystemRootsError" {
fillcolor="#c2e3c2";
label="(SystemRootsError)";
tooltip="type: crypto/x509.SystemRootsError";
tooltip="type: crypto/x509.HostnameError";
penwidth="0.5";
fontsize="15";
fontcolor="#222222";
labelloc="b";
style="rounded,filled";
fillcolor="wheat2";
label="(HostnameError)";
"(crypto/x509.SystemRootsError).Error" [ fillcolor="#adedad" label="Error" penwidth="1.5" tooltip="(crypto/x509.SystemRootsError).Error | defined in verify.go:182" ]
"(crypto/x509.HostnameError).Error" [ fillcolor="moccasin" label="Error" penwidth="1.5" tooltip="(crypto/x509.HostnameError).Error | defined in verify.go:98" ]
}
subgraph "cluster_crypto/x509.SystemRootsError" {
tooltip="type: crypto/x509.SystemRootsError";
penwidth="0.5";
fontsize="15";
fontcolor="#222222";
labelloc="b";
style="rounded,filled";
fillcolor="wheat2";
label="(SystemRootsError)";
"(crypto/x509.SystemRootsError).Error" [ label="Error" penwidth="1.5" tooltip="(crypto/x509.SystemRootsError).Error | defined in verify.go:159" fillcolor="moccasin" ]
}
@ -404,23 +404,23 @@ fontsize="15";
fontcolor="#222222";
labelloc="b";
style="rounded,filled";
fillcolor="#c2e3c2";
fillcolor="wheat2";
"(crypto/x509.UnhandledCriticalExtension).Error" [ fillcolor="#adedad" label="Error" penwidth="1.5" tooltip="(crypto/x509.UnhandledCriticalExtension).Error | defined in x509.go:931" ]
"(crypto/x509.UnhandledCriticalExtension).Error" [ label="Error" penwidth="1.5" tooltip="(crypto/x509.UnhandledCriticalExtension).Error | defined in x509.go:893" fillcolor="moccasin" ]
}
subgraph "cluster_crypto/x509.UnknownAuthorityError" {
fillcolor="#c2e3c2";
label="(UnknownAuthorityError)";
tooltip="type: crypto/x509.UnknownAuthorityError";
penwidth="0.5";
fontsize="15";
fontsize="15";
fontcolor="#222222";
labelloc="b";
style="rounded,filled";
fillcolor="wheat2";
label="(UnknownAuthorityError)";
tooltip="type: crypto/x509.UnknownAuthorityError";
penwidth="0.5";
"(crypto/x509.UnknownAuthorityError).Error" [ fillcolor="#adedad" label="Error" penwidth="1.5" tooltip="(crypto/x509.UnknownAuthorityError).Error | defined in verify.go:161" ]
"(crypto/x509.UnknownAuthorityError).Error" [ tooltip="(crypto/x509.UnknownAuthorityError).Error | defined in verify.go:138" fillcolor="moccasin" label="Error" penwidth="1.5" ]
}
@ -428,151 +428,195 @@ style="rounded,filled";
subgraph "cluster_github.com/mattn/go-isatty" {
penwidth="0.8";
style="filled";
fillcolor="lightyellow";
fontsize="16";
fontname="Tahoma bold";
rank="sink";
style="filled";
fillcolor="lightyellow";
label="isatty";
URL="/?f=github.com/mattn/go-isatty";
fontsize="16";
tooltip="package: github.com/mattn/go-isatty";
"github.com/mattn/go-isatty.IsTerminal" [ fillcolor="moccasin" label="IsTerminal" penwidth="1.5" tooltip="github.com/mattn/go-isatty.IsTerminal | defined in isatty_tcgets.go:9" ]
"github.com/mattn/go-isatty.IsTerminal" [ penwidth="1.5" tooltip="github.com/mattn/go-isatty.IsTerminal | defined in isatty_tcgets.go:10" fillcolor="moccasin" label="IsTerminal" ]
}
subgraph "cluster_github.com/pkg/errors" {
penwidth="0.8";
fontsize="16";
fillcolor="lightyellow";
rank="sink";
fontname="Tahoma bold";
label="errors";
style="filled";
fontname="Tahoma bold";
URL="/?f=github.com/pkg/errors";
tooltip="package: github.com/pkg/errors";
rank="sink";
penwidth="0.8";
fontsize="16";
style="filled";
fillcolor="lightyellow";
subgraph "cluster_*github.com/pkg/errors.fundamental" {
fontsize="15";
label="(*fundamental)";
tooltip="type: *github.com/pkg/errors.fundamental";
penwidth="0.5";
fontsize="15";
fontcolor="#222222";
labelloc="b";
style="rounded,filled";
fillcolor="wheat2";
label="(*fundamental)";
tooltip="type: *github.com/pkg/errors.fundamental";
penwidth="0.5";
"(*github.com/pkg/errors.fundamental).Error" [ tooltip="(*github.com/pkg/errors.fundamental).Error | defined in errors.go:125" fillcolor="moccasin" label="Error" penwidth="1.5" ]
"(*github.com/pkg/errors.fundamental).Error" [ fillcolor="moccasin" label="Error" penwidth="1.5" tooltip="(*github.com/pkg/errors.fundamental).Error | defined in errors.go:125" ]
}
}
subgraph "cluster_math/rand" {
fontname="Tahoma bold";
URL="/?f=math/rand";
penwidth="0.8";
style="filled";
fillcolor="#E0FFE1";
tooltip="package: math/rand";
fontsize="16";
style="filled";
fillcolor="lightyellow";
rank="sink";
label="math/rand";
URL="/?f=math/rand";
tooltip="package: math/rand";
penwidth="0.8";
fontsize="16";
fontname="Tahoma bold";
label="rand";
"math/rand.Intn" [ fillcolor="#adedad" label="Intn" penwidth="1.5" tooltip="math/rand.Intn | defined in rand.go:337" ]
"math/rand.Intn" [ fillcolor="moccasin" label="Intn" penwidth="1.5" tooltip="math/rand.Intn | defined in rand.go:337" ]
}
subgraph "cluster_vendor/golang.org/x/net/idna" {
URL="/?f=vendor/golang.org/x/net/idna";
penwidth="0.8";
fontname="Tahoma bold";
fillcolor="lightyellow";
rank="sink";
label="idna";
tooltip="package: vendor/golang.org/x/net/idna";
fontsize="16";
style="filled";
subgraph "cluster_vendor/golang.org/x/net/idna.labelError" {
style="rounded,filled";
fillcolor="wheat2";
label="(labelError)";
tooltip="type: vendor/golang.org/x/net/idna.labelError";
penwidth="0.5";
fontsize="15";
fontcolor="#222222";
labelloc="b";
"(vendor/golang.org/x/net/idna.labelError).Error" [ fillcolor="moccasin" label="Error" penwidth="1.5" tooltip="(vendor/golang.org/x/net/idna.labelError).Error | defined in idna10.0.0.go:324" ]
}
subgraph "cluster_vendor/golang.org/x/net/idna.runeError" {
style="rounded,filled";
fillcolor="wheat2";
label="(runeError)";
tooltip="type: vendor/golang.org/x/net/idna.runeError";
penwidth="0.5";
fontsize="15";
fontcolor="#222222";
labelloc="b";
"(vendor/golang.org/x/net/idna.runeError).Error" [ tooltip="(vendor/golang.org/x/net/idna.runeError).Error | defined in idna10.0.0.go:331" fillcolor="moccasin" label="Error" penwidth="1.5" ]
}
}
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.main$1" [ arrowhead="normalnoneodot" tooltip="at xs.go:769: calling [blitter.com/go/xs/xs.main$1]" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.localUserName" [ tooltip="at xs.go:782: calling [blitter.com/go/xs/xs.localUserName]" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.rejectUserMsg" [ tooltip="at xs.go:1024: calling [blitter.com/go/xs/xs.rejectUserMsg]" ]
}
"blitter.com/go/xs/xs.doShellMode$1" -> "blitter.com/go/xs/xs.exitWithStatus" [ tooltip="at xs.go:509: calling [blitter.com/go/xs/xs.exitWithStatus]\nat xs.go:519: calling [blitter.com/go/xs/xs.exitWithStatus]" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "(crypto/x509.SystemRootsError).Error" [ style="dashed" color="saddlebrown" tooltip="at xs.go:507: calling [(crypto/x509.SystemRootsError).Error]" ]
"blitter.com/go/xs/xs.main$2" -> "blitter.com/go/xs/xs.exitWithStatus" [ tooltip="at xs.go:999: calling [blitter.com/go/xs/xs.exitWithStatus]" ]
"blitter.com/go/xs/xs.doCopyMode" -> "(*blitter.com/go/xs/xsnet.Conn).GetStatus" [ color="saddlebrown" tooltip="at xs.go:435: calling [(*blitter.com/go/xs/xsnet.Conn).GetStatus]\nat xs.go:438: calling [(*blitter.com/go/xs/xsnet.Conn).GetStatus]\nat xs.go:473: calling [(*blitter.com/go/xs/xsnet.Conn).GetStatus]\nat xs.go:475: calling [(*blitter.com/go/xs/xsnet.Conn).GetStatus]" ]
"blitter.com/go/xs/xs.main" -> "(*blitter.com/go/xs/xsnet.Conn).ShutdownChaff" [ arrowhead="normalnoneodiamond" color="saddlebrown" tooltip="at xs.go:1056: calling [(*blitter.com/go/xs/xsnet.Conn).ShutdownChaff]" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "(*blitter.com/go/xs.Session).SetStatus" [ color="saddlebrown" tooltip="at xs.go:513: calling [(*blitter.com/go/xs.Session).SetStatus]" ]
"blitter.com/go/xs/xs.doShellMode" -> "blitter.com/go/xs/xs.doShellMode$1" [ arrowhead="normalnoneodot" tooltip="at xs.go:522: calling [blitter.com/go/xs/xs.doShellMode$1]" ]
"blitter.com/go/xs/xs.main$2" -> "blitter.com/go/xs/xs.restoreTermState" [ tooltip="at xs.go:997: calling [blitter.com/go/xs/xs.restoreTermState]" ]
"blitter.com/go/xs/xs.doCopyMode" -> "(*blitter.com/go/xs/xsnet.Conn).SetStatus" [ color="saddlebrown" tooltip="at xs.go:430: calling [(*blitter.com/go/xs/xsnet.Conn).SetStatus]" ]
"blitter.com/go/xs/xs.sendSessionParams" -> "(blitter.com/go/xs.Session).Cmd" [ tooltip="at xs.go:651: calling [(blitter.com/go/xs.Session).Cmd]\nat xs.go:671: calling [(blitter.com/go/xs.Session).Cmd]" color="saddlebrown" ]
"blitter.com/go/xs/xs.main" -> "(blitter.com/go/xs.Session).Status" [ color="saddlebrown" tooltip="at xs.go:1027: calling [(blitter.com/go/xs.Session).Status]\nat xs.go:1089: calling [(blitter.com/go/xs.Session).Status]\nat xs.go:1091: calling [(blitter.com/go/xs.Session).Status]\nat xs.go:1101: calling [(blitter.com/go/xs.Session).Status]" ]
"blitter.com/go/xs/xs.main$3" -> "math/rand.Intn" [ tooltip="at xs.go:1068: calling [math/rand.Intn]" color="saddlebrown" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "(crypto/x509.CertificateInvalidError).Error" [ style="dashed" color="saddlebrown" tooltip="at xs.go:507: calling [(crypto/x509.CertificateInvalidError).Error]" ]
"blitter.com/go/xs/xs.doShellMode$2" -> "blitter.com/go/xs/xs.restoreTermState" [ tooltip="at xs.go:546: calling [blitter.com/go/xs/xs.restoreTermState]" ]
"blitter.com/go/xs/xs.doShellMode$2" -> "blitter.com/go/xs/xs.exitWithStatus" [ tooltip="at xs.go:548: calling [blitter.com/go/xs/xs.exitWithStatus]" ]
"blitter.com/go/xs/xs.restoreTermState" -> "blitter.com/go/xs.Restore" [ color="saddlebrown" tooltip="at xs.go:1118: calling [blitter.com/go/xs.Restore]" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/logger.New" [ color="saddlebrown" tooltip="at xs.go:864: calling [blitter.com/go/xs/logger.New]" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs.MakeRaw" [ color="saddlebrown" tooltip="at xs.go:978: calling [blitter.com/go/xs.MakeRaw]" ]
"blitter.com/go/xs/xs.sendSessionParams" -> "(blitter.com/go/xs.Session).AuthCookie" [ color="saddlebrown" tooltip="at xs.go:651: calling [(blitter.com/go/xs.Session).AuthCookie]\nat xs.go:675: calling [(blitter.com/go/xs.Session).AuthCookie]" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.main$3" [ arrowhead="normalnoneodot" tooltip="at xs.go:1075: calling [blitter.com/go/xs/xs.main$3]" ]
"blitter.com/go/xs/xs.doCopyMode" -> "blitter.com/go/xs/xs.buildCmdRemoteToLocal" [ tooltip="at xs.go:444: calling [blitter.com/go/xs/xs.buildCmdRemoteToLocal]" ]
"blitter.com/go/xs/xs.doCopyMode" -> "(blitter.com/go/xs/xsnet.Conn).Read" [ color="saddlebrown" tooltip="at xs.go:424: calling [(blitter.com/go/xs/xsnet.Conn).Read]" ]
"blitter.com/go/xs/xs.sendSessionParams" -> "(blitter.com/go/xs.Session).Who" [ tooltip="at xs.go:651: calling [(blitter.com/go/xs.Session).Who]\nat xs.go:659: calling [(blitter.com/go/xs.Session).Who]" color="saddlebrown" ]
"blitter.com/go/xs/xs.main" -> "(blitter.com/go/xs/xsnet.Conn).Read" [ color="saddlebrown" tooltip="at xs.go:1039: calling [(blitter.com/go/xs/xsnet.Conn).Read]" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xsnet.Dial" [ color="saddlebrown" tooltip="at xs.go:960: calling [blitter.com/go/xs/xsnet.Dial]" ]
"blitter.com/go/xs/xs.sendSessionParams" -> "(blitter.com/go/xs.Session).Op" [ color="saddlebrown" tooltip="at xs.go:651: calling [(blitter.com/go/xs.Session).Op]\nat xs.go:655: calling [(blitter.com/go/xs.Session).Op]" ]
"blitter.com/go/xs/xs.rejectUserMsg" -> "blitter.com/go/xs/spinsult.GetSentence" [ color="saddlebrown" tooltip="at xs.go:577: calling [blitter.com/go/xs/spinsult.GetSentence]" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.launchTuns" [ tooltip="at xs.go:1081: calling [blitter.com/go/xs/xs.launchTuns]" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "(vendor/golang.org/x/net/idna.labelError).Error" [ style="dashed" color="saddlebrown" tooltip="at xs.go:507: calling [(vendor/golang.org/x/net/idna.labelError).Error]" ]
"blitter.com/go/xs/xs.Copy" -> "blitter.com/go/xs/xs.copyBuffer" [ tooltip="at xs.go:116: calling [blitter.com/go/xs/xs.copyBuffer]" ]
"blitter.com/go/xs/xs.main" -> "github.com/mattn/go-isatty.IsTerminal" [ color="saddlebrown" tooltip="at xs.go:977: calling [github.com/mattn/go-isatty.IsTerminal]" ]
"blitter.com/go/xs/xs.sendSessionParams" -> "(blitter.com/go/xs.Session).TermType" [ color="saddlebrown" tooltip="at xs.go:651: calling [(blitter.com/go/xs.Session).TermType]\nat xs.go:667: calling [(blitter.com/go/xs.Session).TermType]" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "(crypto/tls.RecordHeaderError).Error" [ style="dashed" color="saddlebrown" tooltip="at xs.go:507: calling [(crypto/tls.RecordHeaderError).Error]" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "(crypto/x509.UnknownAuthorityError).Error" [ style="dashed" color="saddlebrown" tooltip="at xs.go:507: calling [(crypto/x509.UnknownAuthorityError).Error]" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.exitWithStatus" [ tooltip="at xs.go:772: calling [blitter.com/go/xs/xs.exitWithStatus]\nat xs.go:853: calling [blitter.com/go/xs/xs.exitWithStatus]\nat xs.go:963: calling [blitter.com/go/xs/xs.exitWithStatus]\nat xs.go:1027: calling [blitter.com/go/xs/xs.exitWithStatus]\nat xs.go:1101: calling [blitter.com/go/xs/xs.exitWithStatus]" ]
"blitter.com/go/xs/xs.doShellMode$2" -> "blitter.com/go/xs/xs.doShellMode$2$1" [ tooltip="at xs.go:541: calling [blitter.com/go/xs/xs.doShellMode$2$1]" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.doShellMode" [ tooltip="at xs.go:1082: calling [blitter.com/go/xs/xs.doShellMode]" ]
"blitter.com/go/xs/xs.doShellMode" -> "blitter.com/go/xs/xs.doShellMode$2" [ arrowhead="normalnoneodot" tooltip="at xs.go:551: calling [blitter.com/go/xs/xs.doShellMode$2]" ]
"blitter.com/go/xs/xs.buildCmdLocalToRemote" -> "blitter.com/go/xs.GetTool" [ color="saddlebrown" tooltip="at xs.go:283: calling [blitter.com/go/xs.GetTool]\nat xs.go:311: calling [blitter.com/go/xs.GetTool]\nat xs.go:312: calling [blitter.com/go/xs.GetTool]" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "(*blitter.com/go/xs/xsnet.Conn).GetStatus" [ color="saddlebrown" tooltip="at xs.go:513: calling [(*blitter.com/go/xs/xsnet.Conn).GetStatus]" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "(crypto/tls.alert).Error" [ style="dashed" color="saddlebrown" tooltip="at xs.go:507: calling [(crypto/tls.alert).Error]" ]
"blitter.com/go/xs/xs.doShellMode" -> "blitter.com/go/xs/xs.handleTermResizes" [ tooltip="at xs.go:527: calling [blitter.com/go/xs/xs.handleTermResizes]" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/logger.New" [ color="saddlebrown" tooltip="at xs.go:843: calling [blitter.com/go/xs/logger.New]" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "blitter.com/go/xs/xs.restoreTermState" [ tooltip="at xs.go:503: calling [blitter.com/go/xs/xs.restoreTermState]\nat xs.go:518: calling [blitter.com/go/xs/xs.restoreTermState]" ]
"blitter.com/go/xs/xs.buildCmdLocalToRemote" -> "blitter.com/go/xs/xs.getTreeSizeSubCmd" [ tooltip="at xs.go:335: calling [blitter.com/go/xs/xs.getTreeSizeSubCmd]" ]
"blitter.com/go/xs/xs.handleTermResizes" -> "blitter.com/go/xs/xs.handleTermResizes$1" [ tooltip="at termsize_unix.go:21: calling [blitter.com/go/xs/xs.handleTermResizes$1]" arrowhead="normalnoneodot" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "blitter.com/go/xs/xs.exitWithStatus" [ tooltip="at xs.go:509: calling [blitter.com/go/xs/xs.exitWithStatus]\nat xs.go:519: calling [blitter.com/go/xs/xs.exitWithStatus]" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "(compress/flate.InternalError).Error" [ tooltip="at xs.go:507: calling [(compress/flate.InternalError).Error]" style="dashed" color="saddlebrown" ]
"blitter.com/go/xs/xs.buildCmdRemoteToLocal" -> "blitter.com/go/xs.GetTool" [ color="saddlebrown" tooltip="at xs.go:256: calling [blitter.com/go/xs.GetTool]\nat xs.go:263: calling [blitter.com/go/xs.GetTool]" ]
"blitter.com/go/xs/xs.doCopyMode" -> "(*blitter.com/go/xs/xsnet.Conn).WritePacket" [ color="saddlebrown" tooltip="at xs.go:417: calling [(*blitter.com/go/xs/xsnet.Conn).WritePacket]" ]
"blitter.com/go/xs/xs.buildCmdLocalToRemote" -> "blitter.com/go/xs.GetTool" [ color="saddlebrown" tooltip="at xs.go:283: calling [blitter.com/go/xs.GetTool]\nat xs.go:311: calling [blitter.com/go/xs.GetTool]\nat xs.go:312: calling [blitter.com/go/xs.GetTool]" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xsnet.Dial" [ color="saddlebrown" tooltip="at xs.go:938: calling [blitter.com/go/xs/xsnet.Dial]" ]
"blitter.com/go/xs/xs.main" -> "(*blitter.com/go/xs.Session).SetStatus" [ color="saddlebrown" tooltip="at xs.go:1003: calling [(*blitter.com/go/xs.Session).SetStatus]\nat xs.go:1021: calling [(*blitter.com/go/xs.Session).SetStatus]\nat xs.go:1025: calling [(*blitter.com/go/xs.Session).SetStatus]\nat xs.go:1064: calling [(*blitter.com/go/xs.Session).SetStatus]" ]
"blitter.com/go/xs/xs.main" -> "(blitter.com/go/xs.Session).Status" [ color="saddlebrown" tooltip="at xs.go:1005: calling [(blitter.com/go/xs.Session).Status]\nat xs.go:1067: calling [(blitter.com/go/xs.Session).Status]\nat xs.go:1069: calling [(blitter.com/go/xs.Session).Status]\nat xs.go:1079: calling [(blitter.com/go/xs.Session).Status]" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.main$3" [ arrowhead="normalnoneodot" tooltip="at xs.go:1053: calling [blitter.com/go/xs/xs.main$3]" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "(crypto/aes.KeySizeError).Error" [ style="dashed" color="saddlebrown" tooltip="at xs.go:507: calling [(crypto/aes.KeySizeError).Error]" ]
"blitter.com/go/xs/xs.doCopyMode" -> "(blitter.com/go/xs.Session).Cmd" [ color="saddlebrown" tooltip="at xs.go:356: calling [(blitter.com/go/xs.Session).Cmd]\nat xs.go:441: calling [(blitter.com/go/xs.Session).Cmd]" ]
"blitter.com/go/xs/xs.sendSessionParams" -> "(blitter.com/go/xs.Session).Who" [ color="saddlebrown" tooltip="at xs.go:651: calling [(blitter.com/go/xs.Session).Who]\nat xs.go:659: calling [(blitter.com/go/xs.Session).Who]" ]
"blitter.com/go/xs/xs.sendSessionParams" -> "(blitter.com/go/xs.Session).AuthCookie" [ color="saddlebrown" tooltip="at xs.go:651: calling [(blitter.com/go/xs.Session).AuthCookie]\nat xs.go:675: calling [(blitter.com/go/xs.Session).AuthCookie]" ]
"blitter.com/go/xs/xs.main" -> "(*blitter.com/go/xs/xsnet.Conn).SetupChaff" [ color="saddlebrown" tooltip="at xs.go:1050: calling [(*blitter.com/go/xs/xsnet.Conn).SetupChaff]" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "(context.deadlineExceededError).Error" [ style="dashed" color="saddlebrown" tooltip="at xs.go:507: calling [(context.deadlineExceededError).Error]" ]
"blitter.com/go/xs/xs.doShellMode" -> "blitter.com/go/xs/xs.doShellMode$2" [ arrowhead="normalnoneodot" tooltip="at xs.go:551: calling [blitter.com/go/xs/xs.doShellMode$2]" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.usageShell" [ style="dashed" tooltip="at xs.go:831: calling [blitter.com/go/xs/xs.usageShell]" ]
"blitter.com/go/xs/xs.main$3" -> "math/rand.Intn" [ tooltip="at xs.go:1046: calling [math/rand.Intn]" color="saddlebrown" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs.MakeRaw" [ color="saddlebrown" tooltip="at xs.go:956: calling [blitter.com/go/xs.MakeRaw]" ]
"blitter.com/go/xs/xs.main" -> "(blitter.com/go/xs/xsnet.Conn).Read" [ color="saddlebrown" tooltip="at xs.go:1017: calling [(blitter.com/go/xs/xsnet.Conn).Read]" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "(crypto/x509.UnhandledCriticalExtension).Error" [ style="dashed" color="saddlebrown" tooltip="at xs.go:507: calling [(crypto/x509.UnhandledCriticalExtension).Error]" ]
"blitter.com/go/xs/xs.doShellMode$2" -> "blitter.com/go/xs/xs.restoreTermState" [ tooltip="at xs.go:546: calling [blitter.com/go/xs/xs.restoreTermState]" ]
"blitter.com/go/xs/xs.doCopyMode" -> "(*blitter.com/go/xs/xsnet.Conn).SetStatus" [ color="saddlebrown" tooltip="at xs.go:430: calling [(*blitter.com/go/xs/xsnet.Conn).SetStatus]" ]
"blitter.com/go/xs/xs.reqTunnel" -> "(*blitter.com/go/xs/xsnet.Conn).WritePacket" [ color="saddlebrown" tooltip="at xs.go:594: calling [(*blitter.com/go/xs/xsnet.Conn).WritePacket]" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.launchTuns" [ tooltip="at xs.go:1059: calling [blitter.com/go/xs/xs.launchTuns]" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "(blitter.com/go/xs.Session).Status" [ tooltip="at xs.go:514: calling [(blitter.com/go/xs.Session).Status]\nat xs.go:519: calling [(blitter.com/go/xs.Session).Status]" color="saddlebrown" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "(crypto/x509.CertificateInvalidError).Error" [ tooltip="at xs.go:507: calling [(crypto/x509.CertificateInvalidError).Error]" style="dashed" color="saddlebrown" ]
"blitter.com/go/xs/xs.doShellMode" -> "blitter.com/go/xs/xs.doShellMode$1" [ arrowhead="normalnoneodot" tooltip="at xs.go:522: calling [blitter.com/go/xs/xs.doShellMode$1]" ]
"blitter.com/go/xs/xs.copyBuffer" -> "blitter.com/go/xs/xs.copyBuffer$2" [ tooltip="at xs.go:186: calling [blitter.com/go/xs/xs.copyBuffer$2]" style="dashed" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.doCopyMode" [ tooltip="at xs.go:1063: calling [blitter.com/go/xs/xs.doCopyMode]" ]
"blitter.com/go/xs/xs.copyBuffer" -> "blitter.com/go/xs/xs.copyBuffer$3" [ style="dashed" tooltip="at xs.go:186: calling [blitter.com/go/xs/xs.copyBuffer$3]" ]
"blitter.com/go/xs/xs.doCopyMode" -> "(blitter.com/go/xs/xsnet.Conn).Read" [ tooltip="at xs.go:424: calling [(blitter.com/go/xs/xsnet.Conn).Read]" color="saddlebrown" ]
"blitter.com/go/xs/xs.handleTermResizes$1" -> "blitter.com/go/xs/xs.GetSize" [ tooltip="at termsize_unix.go:27: calling [blitter.com/go/xs/xs.GetSize]" ]
"blitter.com/go/xs/xs.sendSessionParams" -> "(blitter.com/go/xs.Session).ConnHost" [ color="saddlebrown" tooltip="at xs.go:651: calling [(blitter.com/go/xs.Session).ConnHost]\nat xs.go:663: calling [(blitter.com/go/xs.Session).ConnHost]" ]
"blitter.com/go/xs/xs.main$3" -> "(*blitter.com/go/xs/xsnet.Conn).WritePacket" [ tooltip="at xs.go:1050: calling [(*blitter.com/go/xs/xsnet.Conn).WritePacket]" color="saddlebrown" ]
"blitter.com/go/xs/xs.main" -> "(*blitter.com/go/xs/xsnet.Conn).Close" [ arrowhead="normalnoneodiamond" color="saddlebrown" tooltip="at xs.go:950: calling [(*blitter.com/go/xs/xsnet.Conn).Close]" ]
"blitter.com/go/xs/xs.rejectUserMsg" -> "blitter.com/go/xs/spinsult.GetSentence" [ color="saddlebrown" tooltip="at xs.go:577: calling [blitter.com/go/xs/spinsult.GetSentence]" ]
"blitter.com/go/xs/xs.copyBuffer" -> "(blitter.com/go/xs/xsnet.Conn).Write" [ style="dashed" color="saddlebrown" tooltip="at xs.go:193: calling [(blitter.com/go/xs/xsnet.Conn).Write]" ]
"blitter.com/go/xs/xs.sendSessionParams" -> "(blitter.com/go/xs.Session).Op" [ tooltip="at xs.go:651: calling [(blitter.com/go/xs.Session).Op]\nat xs.go:655: calling [(blitter.com/go/xs.Session).Op]" color="saddlebrown" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.parseNonSwitchArgs" [ tooltip="at xs.go:775: calling [blitter.com/go/xs/xs.parseNonSwitchArgs]" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.restoreTermState" [ tooltip="at xs.go:962: calling [blitter.com/go/xs/xs.restoreTermState]" arrowhead="normalnoneodiamond" ]
"blitter.com/go/xs/xs.doCopyMode" -> "blitter.com/go/xs/xs.buildCmdLocalToRemote" [ tooltip="at xs.go:364: calling [blitter.com/go/xs/xs.buildCmdLocalToRemote]" ]
"blitter.com/go/xs/xs.main$2" -> "blitter.com/go/xs/xs.exitWithStatus" [ tooltip="at xs.go:977: calling [blitter.com/go/xs/xs.exitWithStatus]" ]
"blitter.com/go/xs/xs.handleTermResizes$1" -> "(*blitter.com/go/xs/xsnet.Conn).WritePacket" [ color="saddlebrown" tooltip="at termsize_unix.go:33: calling [(*blitter.com/go/xs/xsnet.Conn).WritePacket]" ]
"blitter.com/go/xs/xs.sendSessionParams" -> "(blitter.com/go/xs/xsnet.Conn).Write" [ style="dashed" color="saddlebrown" tooltip="at xs.go:675: calling [(blitter.com/go/xs/xsnet.Conn).Write]\nat xs.go:671: calling [(blitter.com/go/xs/xsnet.Conn).Write]\nat xs.go:667: calling [(blitter.com/go/xs/xsnet.Conn).Write]\nat xs.go:663: calling [(blitter.com/go/xs/xsnet.Conn).Write]\nat xs.go:659: calling [(blitter.com/go/xs/xsnet.Conn).Write]\nat xs.go:655: calling [(blitter.com/go/xs/xsnet.Conn).Write]" ]
"blitter.com/go/xs/xs.main" -> "(*blitter.com/go/xs/xsnet.Conn).EnableChaff" [ color="saddlebrown" tooltip="at xs.go:1032: calling [(*blitter.com/go/xs/xsnet.Conn).EnableChaff]" ]
"blitter.com/go/xs/xs.doShellMode$2" -> "blitter.com/go/xs/xs.exitWithStatus" [ tooltip="at xs.go:548: calling [blitter.com/go/xs/xs.exitWithStatus]" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.restoreTermState" [ tooltip="at xs.go:1002: calling [blitter.com/go/xs/xs.restoreTermState]\nat xs.go:1068: calling [blitter.com/go/xs/xs.restoreTermState]\nat xs.go:1074: calling [blitter.com/go/xs/xs.restoreTermState]" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "(crypto/tls.RecordHeaderError).Error" [ tooltip="at xs.go:507: calling [(crypto/tls.RecordHeaderError).Error]" style="dashed" color="saddlebrown" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "(crypto/x509.HostnameError).Error" [ style="dashed" color="saddlebrown" tooltip="at xs.go:507: calling [(crypto/x509.HostnameError).Error]" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.usageCp" [ style="dashed" tooltip="at xs.go:831: calling [blitter.com/go/xs/xs.usageCp]" ]
"blitter.com/go/xs/xs.main" -> "github.com/mattn/go-isatty.IsTerminal" [ color="saddlebrown" tooltip="at xs.go:955: calling [github.com/mattn/go-isatty.IsTerminal]" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "(*github.com/pkg/errors.fundamental).Error" [ color="saddlebrown" tooltip="at xs.go:507: calling [(*github.com/pkg/errors.fundamental).Error]" style="dashed" ]
"blitter.com/go/xs/xs.Copy" -> "blitter.com/go/xs/xs.copyBuffer" [ tooltip="at xs.go:116: calling [blitter.com/go/xs/xs.copyBuffer]" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "(crypto/aes.KeySizeError).Error" [ style="dashed" color="saddlebrown" tooltip="at xs.go:507: calling [(crypto/aes.KeySizeError).Error]" ]
"blitter.com/go/xs/xs.doShellMode$2$1" -> "blitter.com/go/xs/xs.Copy" [ tooltip="at xs.go:539: calling [blitter.com/go/xs/xs.Copy]" ]
"blitter.com/go/xs/xs.doShellMode$2" -> "blitter.com/go/xs/xs.doShellMode$2$1" [ tooltip="at xs.go:541: calling [blitter.com/go/xs/xs.doShellMode$2$1]" ]
"blitter.com/go/xs/xs.restoreTermState" -> "blitter.com/go/xs.Restore" [ color="saddlebrown" tooltip="at xs.go:1096: calling [blitter.com/go/xs.Restore]" ]
"blitter.com/go/xs/xs.main$2" -> "blitter.com/go/xs/xs.restoreTermState" [ tooltip="at xs.go:975: calling [blitter.com/go/xs/xs.restoreTermState]" ]
"blitter.com/go/xs/xs.sendSessionParams" -> "(blitter.com/go/xs.Session).Cmd" [ color="saddlebrown" tooltip="at xs.go:651: calling [(blitter.com/go/xs.Session).Cmd]\nat xs.go:671: calling [(blitter.com/go/xs.Session).Cmd]" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.sendSessionParams" [ tooltip="at xs.go:1000: calling [blitter.com/go/xs/xs.sendSessionParams]" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "blitter.com/go/xs/xs.doShellMode$1$1" [ arrowhead="normalnoneodiamond" tooltip="at xs.go:491: calling [blitter.com/go/xs/xs.doShellMode$1$1]" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "(*crypto/tls.permamentError).Error" [ tooltip="at xs.go:507: calling [(*crypto/tls.permamentError).Error]" style="dashed" color="saddlebrown" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "(crypto/x509.SystemRootsError).Error" [ style="dashed" color="saddlebrown" tooltip="at xs.go:507: calling [(crypto/x509.SystemRootsError).Error]" ]
"blitter.com/go/xs/xs.doCopyMode" -> "(*blitter.com/go/xs/xsnet.Conn).GetStatus" [ color="saddlebrown" tooltip="at xs.go:435: calling [(*blitter.com/go/xs/xsnet.Conn).GetStatus]\nat xs.go:438: calling [(*blitter.com/go/xs/xsnet.Conn).GetStatus]\nat xs.go:473: calling [(*blitter.com/go/xs/xsnet.Conn).GetStatus]\nat xs.go:475: calling [(*blitter.com/go/xs/xsnet.Conn).GetStatus]" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.exitWithStatus" [ tooltip="at xs.go:751: calling [blitter.com/go/xs/xs.exitWithStatus]\nat xs.go:832: calling [blitter.com/go/xs/xs.exitWithStatus]\nat xs.go:941: calling [blitter.com/go/xs/xs.exitWithStatus]\nat xs.go:1005: calling [blitter.com/go/xs/xs.exitWithStatus]\nat xs.go:1079: calling [blitter.com/go/xs/xs.exitWithStatus]" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xsnet.Init" [ color="saddlebrown" tooltip="at xs.go:844: calling [blitter.com/go/xs/xsnet.Init]" ]
"blitter.com/go/xs/xs.main" -> "(*blitter.com/go/xs/xsnet.Conn).SetupChaff" [ color="saddlebrown" tooltip="at xs.go:1028: calling [(*blitter.com/go/xs/xsnet.Conn).SetupChaff]" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "(compress/flate.CorruptInputError).Error" [ color="saddlebrown" tooltip="at xs.go:507: calling [(compress/flate.CorruptInputError).Error]" style="dashed" ]
"blitter.com/go/xs/xs.doCopyMode" -> "(blitter.com/go/xs.Session).Cmd" [ color="saddlebrown" tooltip="at xs.go:356: calling [(blitter.com/go/xs.Session).Cmd]\nat xs.go:441: calling [(blitter.com/go/xs.Session).Cmd]" ]
"blitter.com/go/xs/xs.doCopyMode" -> "(*blitter.com/go/xs/xsnet.Conn).WritePacket" [ tooltip="at xs.go:417: calling [(*blitter.com/go/xs/xsnet.Conn).WritePacket]" color="saddlebrown" ]
"blitter.com/go/xs/xs.sendSessionParams" -> "(blitter.com/go/xs.Session).ConnHost" [ color="saddlebrown" tooltip="at xs.go:651: calling [(blitter.com/go/xs.Session).ConnHost]\nat xs.go:663: calling [(blitter.com/go/xs.Session).ConnHost]" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.sendSessionParams" [ tooltip="at xs.go:1022: calling [blitter.com/go/xs/xs.sendSessionParams]" ]
"blitter.com/go/xs/xs.doCopyMode" -> "blitter.com/go/xs/xs.buildCmdLocalToRemote" [ tooltip="at xs.go:364: calling [blitter.com/go/xs/xs.buildCmdLocalToRemote]" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.parseNonSwitchArgs" [ tooltip="at xs.go:796: calling [blitter.com/go/xs/xs.parseNonSwitchArgs]" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.restoreTermState" [ tooltip="at xs.go:1024: calling [blitter.com/go/xs/xs.restoreTermState]\nat xs.go:1090: calling [blitter.com/go/xs/xs.restoreTermState]\nat xs.go:1096: calling [blitter.com/go/xs/xs.restoreTermState]" ]
"blitter.com/go/xs/xs.main" -> "(*blitter.com/go/xs/xsnet.Conn).DisableChaff" [ color="saddlebrown" tooltip="at xs.go:1055: calling [(*blitter.com/go/xs/xsnet.Conn).DisableChaff]" arrowhead="normalnoneodiamond" ]
"blitter.com/go/xs/xs.launchTuns" -> "blitter.com/go/xs/xs.reqTunnel" [ tooltip="at xs.go:645: calling [blitter.com/go/xs/xs.reqTunnel]" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "(*blitter.com/go/xs/xsnet.Conn).GetStatus" [ color="saddlebrown" tooltip="at xs.go:513: calling [(*blitter.com/go/xs/xsnet.Conn).GetStatus]" ]
"blitter.com/go/xs/xs.sendSessionParams" -> "(blitter.com/go/xs.Session).TermType" [ color="saddlebrown" tooltip="at xs.go:651: calling [(blitter.com/go/xs.Session).TermType]\nat xs.go:667: calling [(blitter.com/go/xs.Session).TermType]" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs.ReadPassword" [ color="saddlebrown" tooltip="at xs.go:983: calling [blitter.com/go/xs.ReadPassword]" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs.NewSession" [ color="saddlebrown" tooltip="at xs.go:999: calling [blitter.com/go/xs.NewSession]" ]
"blitter.com/go/xs/xs.main" -> "(*blitter.com/go/xs/xsnet.Conn).DisableChaff" [ arrowhead="normalnoneodiamond" color="saddlebrown" tooltip="at xs.go:1033: calling [(*blitter.com/go/xs/xsnet.Conn).DisableChaff]" ]
"blitter.com/go/xs/xs.main" -> "(*blitter.com/go/xs/xsnet.Conn).ShutdownChaff" [ color="saddlebrown" tooltip="at xs.go:1034: calling [(*blitter.com/go/xs/xsnet.Conn).ShutdownChaff]" arrowhead="normalnoneodiamond" ]
"blitter.com/go/xs/xs.reqTunnel" -> "blitter.com/go/xs/logger.LogDebug" [ color="saddlebrown" tooltip="at xs.go:593: calling [blitter.com/go/xs/logger.LogDebug]" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "(*blitter.com/go/xs.Session).SetStatus" [ color="saddlebrown" tooltip="at xs.go:513: calling [(*blitter.com/go/xs.Session).SetStatus]" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "(crypto/x509.UnknownAuthorityError).Error" [ style="dashed" color="saddlebrown" tooltip="at xs.go:507: calling [(crypto/x509.UnknownAuthorityError).Error]" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "(crypto/x509.UnhandledCriticalExtension).Error" [ style="dashed" color="saddlebrown" tooltip="at xs.go:507: calling [(crypto/x509.UnhandledCriticalExtension).Error]" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "(compress/flate.CorruptInputError).Error" [ style="dashed" color="saddlebrown" tooltip="at xs.go:507: calling [(compress/flate.CorruptInputError).Error]" ]
"blitter.com/go/xs/xs.buildCmdLocalToRemote" -> "blitter.com/go/xs/xs.getTreeSizeSubCmd" [ tooltip="at xs.go:335: calling [blitter.com/go/xs/xs.getTreeSizeSubCmd]" ]
"blitter.com/go/xs/xs.buildCmdRemoteToLocal" -> "blitter.com/go/xs.GetTool" [ color="saddlebrown" tooltip="at xs.go:256: calling [blitter.com/go/xs.GetTool]\nat xs.go:263: calling [blitter.com/go/xs.GetTool]" ]
"blitter.com/go/xs/xs.sendSessionParams" -> "(blitter.com/go/xs/xsnet.Conn).Write" [ style="dashed" color="saddlebrown" tooltip="at xs.go:675: calling [(blitter.com/go/xs/xsnet.Conn).Write]\nat xs.go:671: calling [(blitter.com/go/xs/xsnet.Conn).Write]\nat xs.go:667: calling [(blitter.com/go/xs/xsnet.Conn).Write]\nat xs.go:663: calling [(blitter.com/go/xs/xsnet.Conn).Write]\nat xs.go:659: calling [(blitter.com/go/xs/xsnet.Conn).Write]\nat xs.go:655: calling [(blitter.com/go/xs/xsnet.Conn).Write]" ]
"blitter.com/go/xs/xs.main" -> "(*blitter.com/go/xs.Session).SetStatus" [ color="saddlebrown" tooltip="at xs.go:1025: calling [(*blitter.com/go/xs.Session).SetStatus]\nat xs.go:1043: calling [(*blitter.com/go/xs.Session).SetStatus]\nat xs.go:1047: calling [(*blitter.com/go/xs.Session).SetStatus]\nat xs.go:1086: calling [(*blitter.com/go/xs.Session).SetStatus]" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "(*github.com/pkg/errors.fundamental).Error" [ style="dashed" color="saddlebrown" tooltip="at xs.go:507: calling [(*github.com/pkg/errors.fundamental).Error]" ]
"blitter.com/go/xs/xs.handleTermResizes" -> "blitter.com/go/xs/xs.handleTermResizes$1" [ arrowhead="normalnoneodot" tooltip="at termsize_unix.go:21: calling [blitter.com/go/xs/xs.handleTermResizes$1]" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.usageShell" [ style="dashed" tooltip="at xs.go:852: calling [blitter.com/go/xs/xs.usageShell]" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.restoreTermState" [ arrowhead="normalnoneodiamond" tooltip="at xs.go:984: calling [blitter.com/go/xs/xs.restoreTermState]" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs.ReadPassword" [ color="saddlebrown" tooltip="at xs.go:1005: calling [blitter.com/go/xs.ReadPassword]" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "blitter.com/go/xs/xs.doShellMode$1$1" [ arrowhead="normalnoneodiamond" tooltip="at xs.go:491: calling [blitter.com/go/xs/xs.doShellMode$1$1]" ]
"blitter.com/go/xs/xs.handleTermResizes$1" -> "(*blitter.com/go/xs/xsnet.Conn).WritePacket" [ color="saddlebrown" tooltip="at termsize_unix.go:33: calling [(*blitter.com/go/xs/xsnet.Conn).WritePacket]" ]
"blitter.com/go/xs/xs.main" -> "(*blitter.com/go/xs/xsnet.Conn).EnableChaff" [ color="saddlebrown" tooltip="at xs.go:1054: calling [(*blitter.com/go/xs/xsnet.Conn).EnableChaff]" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "(crypto/x509.HostnameError).Error" [ style="dashed" color="saddlebrown" tooltip="at xs.go:507: calling [(crypto/x509.HostnameError).Error]" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "(*crypto/tls.permanentError).Error" [ color="saddlebrown" tooltip="at xs.go:507: calling [(*crypto/tls.permanentError).Error]" style="dashed" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.doCopyMode" [ tooltip="at xs.go:1085: calling [blitter.com/go/xs/xs.doCopyMode]" ]
"blitter.com/go/xs/xs.copyBuffer" -> "blitter.com/go/xs/xs.copyBuffer$2" [ style="dashed" tooltip="at xs.go:186: calling [blitter.com/go/xs/xs.copyBuffer$2]" ]
"blitter.com/go/xs/xs.copyBuffer" -> "blitter.com/go/xs/xs.copyBuffer$3" [ style="dashed" tooltip="at xs.go:186: calling [blitter.com/go/xs/xs.copyBuffer$3]" ]
"blitter.com/go/xs/xs.main" -> "(*blitter.com/go/xs/xsnet.Conn).Close" [ arrowhead="normalnoneodiamond" color="saddlebrown" tooltip="at xs.go:972: calling [(*blitter.com/go/xs/xsnet.Conn).Close]" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.rejectUserMsg" [ tooltip="at xs.go:1046: calling [blitter.com/go/xs/xs.rejectUserMsg]" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.usageCp" [ tooltip="at xs.go:852: calling [blitter.com/go/xs/xs.usageCp]" style="dashed" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "(vendor/golang.org/x/net/idna.runeError).Error" [ color="saddlebrown" tooltip="at xs.go:507: calling [(vendor/golang.org/x/net/idna.runeError).Error]" style="dashed" ]
"blitter.com/go/xs/xs.copyBuffer" -> "(blitter.com/go/xs/xsnet.Conn).Write" [ tooltip="at xs.go:193: calling [(blitter.com/go/xs/xsnet.Conn).Write]" style="dashed" color="saddlebrown" ]
"blitter.com/go/xs/xs.copyBuffer" -> "blitter.com/go/xs/xs.copyBuffer$1" [ style="dashed" tooltip="at xs.go:186: calling [blitter.com/go/xs/xs.copyBuffer$1]" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.doShellMode" [ tooltip="at xs.go:1060: calling [blitter.com/go/xs/xs.doShellMode]" ]
"blitter.com/go/xs/xs.doCopyMode" -> "blitter.com/go/xs/xs.buildCmdRemoteToLocal" [ tooltip="at xs.go:444: calling [blitter.com/go/xs/xs.buildCmdRemoteToLocal]" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.main$1" [ tooltip="at xs.go:790: calling [blitter.com/go/xs/xs.main$1]" arrowhead="normalnoneodot" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.localUserName" [ tooltip="at xs.go:803: calling [blitter.com/go/xs/xs.localUserName]" ]
"blitter.com/go/xs/xs.reqTunnel" -> "(*blitter.com/go/xs/xsnet.Conn).WritePacket" [ color="saddlebrown" tooltip="at xs.go:594: calling [(*blitter.com/go/xs/xsnet.Conn).WritePacket]" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xsnet.Init" [ color="saddlebrown" tooltip="at xs.go:865: calling [blitter.com/go/xs/xsnet.Init]" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs.NewSession" [ tooltip="at xs.go:1021: calling [blitter.com/go/xs.NewSession]" color="saddlebrown" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "(blitter.com/go/xs.Session).Status" [ tooltip="at xs.go:514: calling [(blitter.com/go/xs.Session).Status]\nat xs.go:519: calling [(blitter.com/go/xs.Session).Status]" color="saddlebrown" ]
"blitter.com/go/xs/xs.handleTermResizes$1" -> "blitter.com/go/xs/xs.GetSize" [ tooltip="at termsize_unix.go:27: calling [blitter.com/go/xs/xs.GetSize]" ]
"blitter.com/go/xs/xs.reqTunnel" -> "blitter.com/go/xs/logger.LogDebug" [ color="saddlebrown" tooltip="at xs.go:593: calling [blitter.com/go/xs/logger.LogDebug]" ]
"blitter.com/go/xs/xs.main$3" -> "(*blitter.com/go/xs/xsnet.Conn).WritePacket" [ tooltip="at xs.go:1072: calling [(*blitter.com/go/xs/xsnet.Conn).WritePacket]" color="saddlebrown" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "blitter.com/go/xs/xs.restoreTermState" [ tooltip="at xs.go:503: calling [blitter.com/go/xs/xs.restoreTermState]\nat xs.go:518: calling [blitter.com/go/xs/xs.restoreTermState]" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "(compress/flate.InternalError).Error" [ tooltip="at xs.go:507: calling [(compress/flate.InternalError).Error]" style="dashed" color="saddlebrown" ]
}

377
xs/xs.go
View File

@ -1,5 +1,4 @@
// xs client
//
// Copyright (c) 2017-2020 Russell Magee
// Licensed under the terms of the MIT license (see LICENSE.mit in this
@ -15,10 +14,8 @@ import (
"flag"
"fmt"
"io"
"io/ioutil"
"log"
"math/rand"
"net"
"os"
"os/exec"
"os/user"
@ -32,13 +29,14 @@ import (
"time"
"net/http"
_ "net/http/pprof"
_ "net/http/pprof" //nolint:gosec
xs "blitter.com/go/xs"
"blitter.com/go/xs/logger"
"blitter.com/go/xs/spinsult"
"blitter.com/go/xs/xsnet"
isatty "github.com/mattn/go-isatty"
"github.com/mattn/go-isatty"
//isatty "github.com/mattn/go-isatty"
)
var (
@ -59,6 +57,18 @@ 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" +
@ -93,6 +103,14 @@ type (
escSeqs map[byte]escHandler
)
var (
escs = escSeqs{
'i': func(io.Writer) { os.Stdout.Write([]byte("\x1b[s\x1b[2;1H\x1b[1;31m[HKEXSH]\x1b[39;49m\x1b[u")) },
't': func(io.Writer) { os.Stdout.Write([]byte("\x1b[1;32m[HKEXSH]\x1b[39;49m")) },
'B': func(io.Writer) { os.Stdout.Write([]byte("\x1b[1;32m" + bob + "\x1b[39;49m")) },
}
)
// Copy copies from src to dst until either EOF is reached
// on src or an error occurs. It returns the number of bytes
// copied and the first error encountered while copying, if any.
@ -140,11 +158,6 @@ func copyBuffer(dst io.Writer, src io.Reader, buf []byte) (written int64, err er
// or tunnel traffic indicator - note we cannot just spawn a goroutine
// here, as copyBuffer() returns after each burst of data. Scope must
// outlive individual copyBuffer calls).
escs := escSeqs{
'i': func(io.Writer) { os.Stdout.Write([]byte("\x1b[s\x1b[2;1H\x1b[1;31m[HKEXSH]\x1b[39;49m\x1b[u")) },
't': func(io.Writer) { os.Stdout.Write([]byte("\x1b[1;32m[HKEXSH]\x1b[39;49m")) },
'B': func(io.Writer) { os.Stdout.Write([]byte("\x1b[1;32m" + bob + "\x1b[39;49m")) },
}
/*
// If the reader has a WriteTo method, use it to do the copy.
@ -156,7 +169,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 {
@ -177,8 +190,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 {
if buf[0] == 0x1d {
if seqPos < 4 { //nolint:gomnd
if buf[0] == DeadCharPrefix {
seqPos++
}
} else {
@ -225,7 +238,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 ||
@ -241,7 +254,7 @@ func GetSize() (cols, rows int, err error) {
return
}
func buildCmdRemoteToLocal(copyQuiet bool, copyLimitBPS uint, destPath, files string) (captureStderr bool, cmd string, args []string) {
func buildCmdRemoteToLocal(copyQuiet bool, copyLimitBPS uint, destPath string) (captureStderr bool, cmd string, args []string) {
// Detect if we have 'pv'
// pipeview http://www.ivarch.com/programs/pv.shtml
// and use it for nice client progress display.
@ -259,7 +272,7 @@ func buildCmdRemoteToLocal(copyQuiet bool, copyLimitBPS uint, destPath, files st
} else {
// TODO: Query remote side for total file/dir size
bandwidthInBytesPerSec := " -L " + fmt.Sprintf("%d ", copyLimitBPS)
displayOpts := " -pre "
displayOpts := " -pre " //nolint:goconst
cmd = xs.GetTool("bash")
args = []string{"-c", "pv " + displayOpts + bandwidthInBytesPerSec + "| tar -xz -C " + destPath}
}
@ -307,7 +320,7 @@ func buildCmdLocalToRemote(copyQuiet bool, copyLimitBPS uint, files string) (cap
} else {
captureStderr = copyQuiet
bandwidthInBytesPerSec := " -L " + fmt.Sprintf("%d", copyLimitBPS)
displayOpts := " -pre "
displayOpts := " -pre " //nolint:goconst,nolintlint
cmd = xs.GetTool("bash")
args = []string{"-c", xs.GetTool("tar") + " -cz -f /dev/stdout "}
files = strings.TrimSpace(files)
@ -357,9 +370,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
@ -392,7 +405,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(2)
exitStatus = uint32(CmdExitedEarly)
} else {
if err = c.Wait(); err != nil {
if exiterr, ok := err.(*exec.ExitError); ok {
@ -412,7 +425,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)
r := make([]byte, 4) //nolint:gomnd
binary.BigEndian.PutUint32(r, exitStatus)
_, we := conn.WritePacket(r, xsnet.CSOExitStatus)
if we != nil {
@ -420,7 +433,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)
s := make([]byte, 4) //nolint:gomnd
_, remErr := conn.Read(s)
if remErr != io.EOF &&
!strings.Contains(remErr.Error(), "use of closed network") &&
@ -441,10 +454,9 @@ func doCopyMode(conn *xsnet.Conn, remoteDest bool, files string, copyQuiet bool,
log.Println("remote filepath:", string(rec.Cmd()), "local files:", files)
destPath := files
_, cmdName, cmdArgs := buildCmdRemoteToLocal(copyQuiet, copyLimitBPS, destPath, strings.TrimSpace(files))
_, cmdName, cmdArgs := buildCmdRemoteToLocal(copyQuiet, copyLimitBPS, destPath)
var c *exec.Cmd
c = exec.Command(cmdName, cmdArgs...) // #nosec
c := exec.Command(cmdName, cmdArgs...) // #nosec
c.Stdin = conn
c.Stdout = os.Stdout
c.Stderr = os.Stderr
@ -481,8 +493,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\"/
@ -536,6 +548,8 @@ func doShellMode(isInteractive bool, conn *xsnet.Conn, oldState *xs.State, rec *
_, outerr := func(conn *xsnet.Conn, r io.Reader) (w int64, e error) {
// Copy() expects EOF so this will
// exit with outerr == nil
// NOTE we use a local implementation of Copy() to allow
// for custom key sequences to trigger local actions
w, e = Copy(conn, r)
return w, e
}(conn, os.Stdin)
@ -557,15 +571,15 @@ func doShellMode(isInteractive bool, conn *xsnet.Conn, oldState *xs.State, rec *
}
func usageShell() {
fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0]) // nolint: errcheck
fmt.Fprintf(os.Stderr, "%s [opts] [user]@server\n", os.Args[0]) // nolint: errcheck
fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
fmt.Fprintf(os.Stderr, "%s [opts] [user]@server\n", os.Args[0])
flag.PrintDefaults()
}
func usageCp() {
fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0]) // nolint: errcheck
fmt.Fprintf(os.Stderr, "%s [opts] srcFileOrDir [...] [user]@server[:dstpath]\n", os.Args[0]) // nolint: errcheck
fmt.Fprintf(os.Stderr, "%s [opts] [user]@server[:srcFileOrDir] dstPath\n", os.Args[0]) // nolint: errcheck
fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
fmt.Fprintf(os.Stderr, "%s [opts] srcFileOrDir [...] [user]@server[:dstpath]\n", os.Args[0])
fmt.Fprintf(os.Stderr, "%s [opts] [user]@server[:srcFileOrDir] dstPath\n", os.Args[0])
flag.PrintDefaults()
}
@ -581,18 +595,18 @@ func rejectUserMsg() string {
//
// Server responds with [CSOTunAck:rport] or [CSOTunRefused:rport]
// (handled in xsnet.Read())
func reqTunnel(hc *xsnet.Conn, lp uint16, p string /*net.Addr*/, rp uint16) {
func reqTunnel(hc *xsnet.Conn, lp uint16 /*, p string*/ /*net.Addr*/, rp uint16) {
// Write request to server so it can attempt to set up its end
var bTmp bytes.Buffer
if e := binary.Write(&bTmp, binary.BigEndian, lp); e != nil {
fmt.Fprintln(os.Stderr, "reqTunnel:", e) // nolint: errcheck
fmt.Fprintln(os.Stderr, "reqTunnel:", e)
}
if e := binary.Write(&bTmp, binary.BigEndian, rp); e != nil {
fmt.Fprintln(os.Stderr, "reqTunnel:", e) // nolint: errcheck
fmt.Fprintln(os.Stderr, "reqTunnel:", e)
}
_ = logger.LogDebug(fmt.Sprintln("[Client sending CSOTunSetup]")) // nolint: gosec
_ = logger.LogDebug(fmt.Sprintln("[Client sending CSOTunSetup]"))
if n, e := hc.WritePacket(bTmp.Bytes(), xsnet.CSOTunSetup); e != nil || n != len(bTmp.Bytes()) {
fmt.Fprintln(os.Stderr, "reqTunnel:", e) // nolint: errcheck
fmt.Fprintln(os.Stderr, "reqTunnel:", e)
}
}
@ -605,7 +619,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 {
if len(fancyArg) < 2 { //nolint:gomnd
//TODO: no user specified, use current
fancyUser = "[default:getUser]"
fancyHostPath = strings.Split(fancyArg[0], ":")
@ -631,8 +645,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: gosec
func launchTuns(conn *xsnet.Conn /*remoteHost string,*/, tuns string) {
/*remAddrs, _ := net.LookupHost(remoteHost)*/ //nolint:gocritic,nolintlint
if tuns == "" {
return
@ -641,8 +655,8 @@ func launchTuns(conn *xsnet.Conn, remoteHost string, tuns string) {
tunSpecs := strings.Split(tuns, ",")
for _, tunItem := range tunSpecs {
var lPort, rPort uint16
_, _ = fmt.Sscanf(tunItem, "%d:%d", &lPort, &rPort) // nolint: gosec
reqTunnel(conn, lPort, remAddrs[0], rPort)
_, _ = fmt.Sscanf(tunItem, "%d:%d", &lPort, &rPort)
reqTunnel(conn, lPort /*remAddrs[0],*/, rPort)
}
}
@ -677,25 +691,26 @@ func sendSessionParams(conn io.Writer /* *xsnet.Conn*/, rec *xs.Session) (e erro
}
// TODO: reduce gocyclo
func main() {
func main() { //nolint: funlen, gocyclo
var (
isInteractive bool
vopt bool
gopt bool //login via password, asking server to generate authToken
dbg bool
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
tunSpecStr string // lport1:rport1[,lport2:rport2,...]
copySrc []byte
copyDst string
copyQuiet bool
copyLimitBPS uint
isInteractive bool
vopt bool
gopt bool // true: 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
server string
port uint
cmdStr string
tunSpecStr string // lport1:rport1[,lport2:rport2,...]
rekeySecs uint
remodRequested bool // true: when rekeying, switch to random cipher/hmac alg
copySrc []byte
copyDst string
copyQuiet bool
copyLimitBPS uint
authCookie string
chaffEnabled bool
@ -706,7 +721,7 @@ func main() {
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")
@ -719,7 +734,8 @@ func main() {
C_CHACHA20_12`)
flag.StringVar(&hmacAlg, "m", "H_SHA256", "session `HMAC`"+`
H_SHA256
H_SHA512`)
H_SHA512
H_WHIRLPOOL`)
flag.StringVar(&kexAlg, "k", "KEX_HERRADURA512", "KEx `alg`"+`
KEX_HERRADURA256
KEX_HERRADURA512
@ -734,18 +750,20 @@ func main() {
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")
flag.UintVar(&port, "p", 2000, "``port")
//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") //nolint:lll
flag.UintVar(&port, "p", 2000, "``port") //nolint:gomnd,lll
flag.UintVar(&rekeySecs, "r", 300, "rekey interval in `secs`")
flag.BoolVar(&remodRequested, "R", false, "Borg Countermeasures (remodulate cipher/hmac alg on each rekey)")
//nolint:gocritic,nolintlint // flag.StringVar(&authCookie, "a", "", "auth cookie")
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.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.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))
@ -762,7 +780,7 @@ func main() {
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")
flag.UintVar(&copyLimitBPS, "L", 8589934592, "copy max rate in bytes per sec") //nolint:gomnd
flag.Usage = usageCp
}
flag.Parse()
@ -772,7 +790,7 @@ func main() {
exitWithStatus(0)
}
//=== Profiling instrumentation
// === Profiling instrumentation
if cpuprofile != "" {
f, err := os.Create(cpuprofile)
@ -782,24 +800,24 @@ func main() {
defer f.Close()
fmt.Println("StartCPUProfile()")
if err := pprof.StartCPUProfile(f); err != nil {
log.Fatal("could not start CPU profile: ", err)
log.Fatal("could not start CPU profile: ", err) //nolint:gocritic
} else {
defer pprof.StopCPUProfile()
}
go func() { http.ListenAndServe("localhost:6060", nil) }()
go func() { http.ListenAndServe("localhost:6060", nil) }() //nolint:errcheck,gosec
}
//=== 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())
//fmt.Println("otherArgs:", otherArgs)
//nolint:gocritic,nolintlint // fmt.Println("otherArgs:", otherArgs)
// Set defaults if user doesn't specify user, path or port
var uname string
if remoteUser == "" {
u, _ := user.Current() // nolint: gosec
u, _ := user.Current()
uname = localUserName(u)
} else {
uname = remoteUser
@ -812,7 +830,7 @@ func main() {
tmpPath = "."
}
//=== Copy mode arg and copy src/dest setup
// === Copy mode arg and copy src/dest setup
var fileArgs string
if !shellMode /*&& tmpPath != ""*/ {
@ -845,15 +863,15 @@ func main() {
}
}
//=== Do some final option consistency checks
// === Do some final option consistency checks
//fmt.Println("server finally is:", server)
//nolint:gocritic,nolintlint // fmt.Println("server finally is:", server)
if flag.NFlag() == 0 && server == "" {
flag.Usage()
exitWithStatus(0)
}
if len(cmdStr) != 0 && (len(copySrc) != 0 || len(copyDst) != 0) {
if cmdStr != "" && (len(copySrc) != 0 || copyDst != "") {
log.Fatal("incompatible options -- either cmd (-x) or copy ops but not both")
}
@ -861,27 +879,27 @@ func main() {
// either the shell session or copy operation.
_ = shellMode
Log, _ = logger.New(logger.LOG_USER|logger.LOG_DEBUG|logger.LOG_NOTICE|logger.LOG_ERR, "xs") // nolint: errcheck,gosec
Log, _ = logger.New(logger.LOG_USER|logger.LOG_DEBUG|logger.LOG_NOTICE|logger.LOG_ERR, "xs")
xsnet.Init(dbg, "xs", logger.LOG_USER|logger.LOG_DEBUG|logger.LOG_NOTICE|logger.LOG_ERR)
if dbg {
log.SetOutput(Log)
} else {
log.SetOutput(ioutil.Discard)
log.SetOutput(io.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() // nolint: gosec
ab, aerr := ioutil.ReadFile(fmt.Sprintf("%s/.xs_id", u.HomeDir))
u, _ := user.Current()
ab, aerr := os.ReadFile(fmt.Sprintf("%s/%s", u.HomeDir, xsnet.XS_ID_AUTHTOKFILE))
if aerr == nil {
for _, line := range strings.Split(string(ab), "\n") {
line = line + "\n"
idx := strings.Index(string(line), remoteHost+":"+uname)
line += "\n"
idx := strings.Index(line, remoteHost+":"+uname)
if idx >= 0 {
line = line[idx:]
entries := strings.SplitN(string(line), "\n", -1)
entries := strings.SplitN(line, "\n", -1)
authCookie = strings.TrimSpace(entries[0])
// Security scrub
line = ""
@ -891,15 +909,14 @@ func main() {
if authCookie == "" {
_, _ = fmt.Fprintln(os.Stderr, "[no authtoken, use -g to request one from server]")
}
} else {
log.Printf("[cannot read %s/.xs_id]\n", u.HomeDir)
log.Printf("[cannot read %s/%s]\n", u.HomeDir, xsnet.XS_ID_AUTHTOKFILE)
}
}
runtime.GC()
//=== Enforce some sane min/max vals on chaff flags
if chaffFreqMin < 2 {
// === Enforce some sane min/max vals on chaff flags
if chaffFreqMin < 2 { //nolint:gomnd
chaffFreqMin = 2
}
if chaffFreqMax == 0 {
@ -909,17 +926,17 @@ func main() {
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()
// as it affects chaffing behaviour. 20180805
if gopt {
fmt.Fprintln(os.Stderr, "[requesting authtoken from server]") // nolint: errcheck
fmt.Fprintln(os.Stderr, "[requesting authtoken from server]")
op = []byte{'A'}
chaffFreqMin = 2
chaffFreqMax = 10
} else if len(cmdStr) == 0 {
} else if cmdStr == "" {
op = []byte{'s'}
isInteractive = true
} else {
@ -940,42 +957,86 @@ func main() {
// client->server file copy
// src file list is in copySrc
op = []byte{'D'}
//fmt.Println("client->server copy:", string(copySrc), "->", copyDst)
//nolint:gocritic,nolintlint // 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)
//nolint:gocritic,nolintlint // fmt.Println("server->client copy:", string(copySrc), "->", copyDst)
cmdStr = string(copySrc)
}
}
//=== TCP / KCP Dial setup
// === TCP / KCP Dial setup
proto := "tcp"
if kcpMode != "unused" {
proto = "kcp"
}
conn, err := xsnet.Dial(proto, server, cipherAlg, hmacAlg, kexAlg, kcpMode)
remodExtArg := ""
if remodRequested {
remodExtArg = "OPT_REMOD"
}
// Pass opt to Dial() via extensions arg
conn, err := xsnet.Dial(proto, server, cipherAlg, hmacAlg, kexAlg, kcpMode, remodExtArg)
if err != nil {
fmt.Println(err)
exitWithStatus(3)
exitWithStatus(XSNetDialFailed)
}
//=== Shell terminal mode (Shell vs. Copy) setup
conn.RekeyHelper(rekeySecs)
defer conn.ShutdownRekey()
// === Shell terminal mode (Shell vs. Copy) setup
defer conn.Close()
// === From this point on, conn is a secure encrypted channel
// === BEGIN Login phase
// Set stdin in raw mode if it's an interactive session
// TODO: send flag to server side indicating this
// affects shell command used
var oldState *xs.State
defer conn.Close() // nolint: errcheck
//=== From this point on, conn is a secure encrypted channel
// 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
restoreTermState(oldState)
fmt.Printf(" .. [login timeout]\n")
exitWithStatus(xsnet.CSELoginTimeout)
})
if authCookie == "" {
if !gopt {
// No auth token, prompt for password
fmt.Printf("Gimme cookie:")
}
ab, e := xs.ReadPassword(os.Stdin)
if !gopt {
fmt.Printf("\r\n")
}
if e != nil {
panic(e)
}
authCookie = string(ab)
}
//nolint:gocritic,nolintlint // i_ = loginImpatience.Stop()
_ = loginTimeout.Stop()
// Security scrub
runtime.GC()
// === END Login phase
// === Terminal mode adjustment for session
if shellMode {
if isatty.IsTerminal(os.Stdin.Fd()) {
oldState, err = xs.MakeRaw(os.Stdin.Fd())
if isatty.IsTerminal(os.Stdin.Fd()) ||
isatty.IsCygwinTerminal(os.Stdin.Fd()) {
oldState, err = xs.MakeRaw(os.Stdin)
if err != nil {
panic(err)
}
@ -987,108 +1048,83 @@ func main() {
}
}
//=== 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() {
restoreTermState(oldState)
fmt.Printf(" .. [login timeout]\n")
exitWithStatus(xsnet.CSOLoginTimeout)
})
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")
if e != nil {
panic(e)
}
authCookie = string(ab)
}
//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(254)
fmt.Fprintln(os.Stderr, "Error: server rejected secure proposal params or login timed out") // nolint: errcheck
rec.SetStatus(ServerRejectedSecureProposal)
fmt.Fprintln(os.Stderr, "Error: server rejected secure proposal params or login timed out")
exitWithStatus(int(rec.Status()))
//log.Fatal(sendErr)
//nolint:gocritic,nolintlint // log.Fatal(sendErr)
}
//Security scrub
authCookie = "" // nolint: ineffassign
// 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
fmt.Fprintln(os.Stderr, "Error reading auth reply") // nolint: errcheck
rec.SetStatus(255)
// === Exit if auth reply not received
fmt.Fprintln(os.Stderr, "Error reading auth reply")
rec.SetStatus(ErrReadingAuthReply)
} else if authReply[0] == 0 {
//=== .. or if auth failed
fmt.Fprintln(os.Stderr, rejectUserMsg()) // nolint: errcheck
rec.SetStatus(255)
// === .. or if auth failed
fmt.Fprintln(os.Stderr, rejectUserMsg())
rec.SetStatus(GeneralProtocolErr)
} 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\"/
// TODO:.gv:main:2:deferCloseChaff
conn.EnableChaff() // goroutine, returns immediately
defer conn.DisableChaff()
conn.StartupChaff() // goroutine, returns immediately
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
// any, to prevent listeners from knowing this.
//[1] if tunSpecStr != "" {
// [1]: better to always send tunnel keepAlives even if client didn't specify
// any, to prevent listeners from knowing this.
// [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)
time.Sleep(time.Duration(2000-rand.Intn(200)) * time.Millisecond) //nolint:gosec,gomnd
// 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
conn.WritePacket([]byte{0, 0}, xsnet.CSOTunKeepAlive) // nolint: errcheck,gosec
conn.WritePacket([]byte{0, 0}, xsnet.CSOTunKeepAlive) //nolint: errcheck
}
}
go keepAliveWorker()
//[1]}
// [1]}
//=== Session entry (shellMode or copyMode)
// === Session entry (shellMode or copyMode)
if shellMode {
//=== (shell) launch tunnels
launchTuns(&conn, remoteHost, tunSpecStr)
// === Set up connection keepalive to server
conn.StartupKeepAlive() // goroutine, returns immediately
defer conn.ShutdownKeepAlive()
// === (shell) launch tunnels
launchTuns(&conn /*remoteHost,*/, tunSpecStr)
doShellMode(isInteractive, &conn, oldState, rec)
} else {
//=== (.. or file copy)
s, _ := doCopyMode(&conn, pathIsDest, fileArgs, copyQuiet, copyLimitBPS, rec) // nolint: errcheck,gosec
// === (.. or file copy)
s, _ := doCopyMode(&conn, pathIsDest, fileArgs, copyQuiet, copyLimitBPS, rec)
rec.SetStatus(s)
}
if rec.Status() != 0 {
restoreTermState(oldState)
fmt.Fprintln(os.Stderr, "Session exited with status:", rec.Status()) // nolint: errcheck
fmt.Fprintln(os.Stderr, "Session exited with status:", rec.Status())
}
}
@ -1097,7 +1133,7 @@ func main() {
oldState = nil
}
//=== Exit
// === Exit
exitWithStatus(int(rec.Status()))
}
@ -1115,7 +1151,7 @@ func localUserName(u *user.User) string {
}
func restoreTermState(oldState *xs.State) {
_ = xs.Restore(os.Stdin.Fd(), oldState) // nolint: errcheck,gosec
_ = xs.Restore(os.Stdin, oldState)
}
// exitWithStatus wraps os.Exit() plus does any required pprof housekeeping
@ -1132,9 +1168,8 @@ 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)
log.Fatal("could not write memory profile: ", err) //nolint:gocritic
}
}
os.Exit(status)
}

View File

@ -11,6 +11,10 @@
set -e
echo "SET XSD_OPTS in this script to define allow KEX, cipher and hmac algs"
#XSD_OPTS="-L -aK KEX_all -aC C_all -aH H_all"
exit 1
# /etc/init.d/xsd: start and stop the eXperimental "secure" Shell Daemon
test -x /usr/local/sbin/xsd || exit 0

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

File diff suppressed because one or more lines are too long

View File

@ -16,13 +16,15 @@ import (
"flag"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"os"
"os/exec"
"os/signal"
"os/user"
"path"
"runtime"
"runtime/pprof"
"strings"
"sync"
"syscall"
@ -45,6 +47,14 @@ var (
// Log - syslog output (with no -d)
Log *logger.Writer
cpuprofile string
memprofile string
)
const (
AuthTokenLen = 64
LoginTimeoutSecs = 30
)
func ioctl(fd, request, argp uintptr) error {
@ -66,23 +76,18 @@ func ptsName(fd uintptr) (string, error) {
/* -------------------------------------------------------------- */
// 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
u, _ := user.Lookup(who)
var uid, gid uint32
fmt.Sscanf(u.Uid, "%d", &uid) // nolint: gosec,errcheck
fmt.Sscanf(u.Gid, "%d", &gid) // nolint: gosec,errcheck
fmt.Sscanf(u.Uid, "%d", &uid)
fmt.Sscanf(u.Gid, "%d", &gid)
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)
// target user.
os.Clearenv()
os.Setenv("HOME", u.HomeDir) // nolint: gosec,errcheck
os.Setenv("TERM", ttype) // nolint: gosec,errcheck
os.Setenv("XS_SESSION", "1") // nolint: gosec,errcheck
os.Setenv("HOME", u.HomeDir)
os.Setenv("TERM", ttype)
os.Setenv("XS_SESSION", "1")
var c *exec.Cmd
cmdName := xs.GetTool("tar")
@ -101,13 +106,14 @@ func runClientToServerCopyAs(who, ttype string, conn *xsnet.Conn, fpath string,
// (as this isn't input from a shell) (right? -rlm 20180823)
//cmdArgs := []string{"-x", "-C", destDir, `--xform=s#.*/\(.*\)#\1#`}
fmt.Println(cmdName, cmdArgs)
c = exec.Command(cmdName, cmdArgs...) // nolint: gosec
c = exec.Command(cmdName, cmdArgs...)
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}
//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}
@ -116,9 +122,8 @@ func runClientToServerCopyAs(who, ttype string, conn *xsnet.Conn, fpath string,
c.Stderr = os.Stderr
if chaffing {
conn.EnableChaff()
conn.StartupChaff()
}
defer conn.DisableChaff()
defer conn.ShutdownChaff()
// Start the command (no pty)
@ -173,21 +178,16 @@ func runServerToClientCopyAs(who, ttype string, conn *xsnet.Conn, srcPath string
return
}
var uid, gid uint32
_, _ = fmt.Sscanf(u.Uid, "%d", &uid) // nolint: gosec
_, _ = fmt.Sscanf(u.Gid, "%d", &gid) // nolint: gosec
_, _ = fmt.Sscanf(u.Uid, "%d", &uid)
_, _ = fmt.Sscanf(u.Gid, "%d", &gid)
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)
// target user.
os.Clearenv()
_ = os.Setenv("HOME", u.HomeDir) // nolint: gosec
_ = os.Setenv("TERM", ttype) // nolint: gosec
_ = os.Setenv("XS_SESSION", "1") // nolint: gosec
_ = os.Setenv("HOME", u.HomeDir)
_ = os.Setenv("TERM", ttype)
_ = os.Setenv("XS_SESSION", "1")
var c *exec.Cmd
cmdName := xs.GetTool("tar")
@ -198,11 +198,12 @@ func runServerToClientCopyAs(who, ttype string, conn *xsnet.Conn, srcPath string
srcDir, srcBase := path.Split(srcPath)
cmdArgs := []string{"-cz", "-C", srcDir, "-f", "-", srcBase}
c = exec.Command(cmdName, cmdArgs...) // nolint: gosec
c = exec.Command(cmdName, cmdArgs...)
//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}
//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}
@ -217,10 +218,9 @@ func runServerToClientCopyAs(who, ttype string, conn *xsnet.Conn, srcPath string
//c.Stderr = nil
if chaffing {
conn.EnableChaff()
conn.StartupChaff()
}
//defer conn.Close()
defer conn.DisableChaff()
defer conn.ShutdownChaff()
// Start the command (no pty)
@ -252,11 +252,10 @@ func runServerToClientCopyAs(who, ttype string, conn *xsnet.Conn, srcPath string
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) {
// Run a command (via default shell) as a specific user. Uses
// ptys to support commands which expect a terminal. //nolint:gofmt
func runShellAs(who, hname, ttype, cmd string, interactive bool, //nolint:funlen
conn *xsnet.Conn, chaffing bool) (exitStatus uint32, err error) {
var wg sync.WaitGroup
u, err := user.Lookup(who)
if err != nil {
@ -264,54 +263,57 @@ func runShellAs(who, hname, ttype, cmd string, interactive bool, conn *xsnet.Con
return
}
var uid, gid uint32
_, _ = fmt.Sscanf(u.Uid, "%d", &uid) // nolint: gosec
_, _ = fmt.Sscanf(u.Gid, "%d", &gid) // nolint: gosec
_, _ = fmt.Sscanf(u.Uid, "%d", &uid)
_, _ = fmt.Sscanf(u.Gid, "%d", &gid)
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)
// target user.
os.Clearenv()
_ = os.Setenv("HOME", u.HomeDir) // nolint: gosec
_ = os.Setenv("TERM", ttype) // nolint: gosec
_ = os.Setenv("XS_SESSION", "1") // nolint: gosec
_ = os.Setenv("HOME", u.HomeDir)
_ = os.Setenv("TERM", ttype)
_ = os.Setenv("XS_SESSION", "1")
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)
//
// Note login will drop privs to the intended user for us
// Use the server's login binary (post-auth)
//
// 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(xs.GetTool("login"), "-f", "-p", who) // nolint: gosec
// One drawback of using 'login' is that the remote side
// cannot give us back the shell's exit code, since it
// exits back to 'login', which usually returns its own
// 0 status back to us.
//
// Note login will drop privs to the intended user for us.
//
c = exec.Command(xs.GetTool("login"), "-f", "-p", who) //nolint:gosec
} else {
// Using our separate login via local passwd file
// Run shell directly (which allows nonzero exit codes back to
// the local system upon shell exit, whereas 'login' does not.)
//
// Note we must drop privs ourselves for the user shell
// Note we must drop privs ourselves for the user shell since
// we aren't using 'login' on the remote end which would do it
// for us.
//
c = exec.Command(xs.GetTool("bash"), "-i", "-l") // nolint: gosec
c = exec.Command(xs.GetTool("bash"), "-i", "-l") //nolint:gosec
c.SysProcAttr = &syscall.SysProcAttr{}
c.SysProcAttr.Credential = &syscall.Credential{Uid: uid, Gid: gid}
}
} else {
c = exec.Command(xs.GetTool("bash"), "-c", cmd) // nolint: gosec
c = exec.Command(xs.GetTool("bash"), "-c", cmd) //nolint:gosec
c.SysProcAttr = &syscall.SysProcAttr{}
c.SysProcAttr.Credential = &syscall.Credential{Uid: uid, Gid: gid}
}
//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}
//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
// Start the command with a pty.
@ -325,7 +327,7 @@ func runShellAs(who, hname, ttype, cmd string, interactive bool, conn *xsnet.Con
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())
@ -336,6 +338,9 @@ func runShellAs(who, hname, ttype, cmd string, interactive bool, conn *xsnet.Con
defer func() { goutmp.Unput_utmp(utmpx) }()
goutmp.Put_lastlog_entry("xs", who, pts, hname)
conn.Pproc = c.Process.Pid
//fmt.Printf("[process %d started]\n", c.Process.Pid)
log.Printf("[%s]\n", cmd)
if err != nil {
log.Printf("Command finished with error: %v", err)
@ -345,7 +350,7 @@ func runShellAs(who, hname, ttype, cmd string, interactive bool, conn *xsnet.Con
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
pty.Setsize(ptmx, &pty.Winsize{Rows: sz.Rows, Cols: sz.Cols}) //nolint:errcheck
}
log.Println("*** WinCh goroutine done ***")
}()
@ -361,14 +366,17 @@ func runShellAs(who, hname, ttype, cmd string, interactive bool, conn *xsnet.Con
}
}()
// === Set up connection keepalive to client
conn.StartupKeepAlive() // goroutine, returns immediately
defer conn.ShutdownKeepAlive()
if chaffing {
conn.EnableChaff()
conn.StartupChaff()
// #gv:s/label=\"runShellAs\$4\"/label=\"deferChaffShutdown\"/
defer func() {
conn.ShutdownChaff()
}()
}
// #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
@ -406,7 +414,7 @@ func runShellAs(who, hname, ttype, cmd string, interactive bool, conn *xsnet.Con
}
conn.SetStatus(xsnet.CSOType(exitStatus))
} else {
logger.LogDebug("*** Main proc has exited. ***")
logger.LogDebug(fmt.Sprintf("*** Main proc has exited (%d) ***", c.ProcessState.ExitCode())) //nolint:errcheck
// Background jobs still may be running; close the
// pty anyway, so the client can return before
// wg.Wait() below completes (Issue #18)
@ -428,8 +436,8 @@ func GenAuthToken(who string, connhost string) string {
//}
hname := connhost
token := make([]byte, 64)
_, _ = rand.Read(token) // nolint: gosec
token := make([]byte, AuthTokenLen)
_, _ = rand.Read(token)
return fmt.Sprintf("%s:%s:%s", hname, who, hex.EncodeToString(token))
}
@ -505,7 +513,7 @@ func (a *allowedHMACAlgs) Set(value string) error {
// daemon dies, all clients will be rudely disconnected.
// Consider this when planning to restart or upgrade in-place an installation.
// TODO: reduce gocyclo
func main() {
func main() { //nolint:funlen,gocyclo
var vopt bool
var chaffEnabled bool
var chaffFreqMin uint
@ -513,21 +521,24 @@ func main() {
var chaffBytesMax uint
var dbg bool
var laddr string
var rekeySecs uint
var remodSupported bool // true: when rekeying, switch to random cipher/hmac alg
var useSystemPasswd bool
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.UintVar(&rekeySecs, "r", 300, "rekey interval in `secs`")
flag.BoolVar(&remodSupported, "R", false, "Borg Countermeasures (remodulate cipher/hmac alg on each rekey)")
flag.StringVar(&laddr, "l", ":2000", "interface[:port] to listen") //nolint:gomnd,lll
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`) //nolint:lll
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.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.BoolVar(&useSystemPasswd, "s", true, "use system shadow passwds")
flag.BoolVar(&dbg, "d", false, "debug logging")
flag.Var(&aKEXAlgs, "aK", "Allowed KEX `alg`s (eg. '-aK KEXAlgA -aK KEXAlgB ...')" + `
flag.Var(&aKEXAlgs, "aK", "Allowed KEX `alg`s (eg. '-aK KEXAlgA -aK KEXAlgB ...')"+`
KEX_all
KEX_HERRADURA256
KEX_HERRADURA512
@ -542,7 +553,7 @@ func main() {
KEX_FRODOKEM_1344SHAKE
KEX_FRODOKEM_976AES
KEX_FRODOKEM_976SHAKE`)
flag.Var(&aCipherAlgs, "aC", "Allowed `cipher`s (eg. '-aC CAlgA -aC CAlgB ...')" + `
flag.Var(&aCipherAlgs, "aC", "Allowed `cipher`s (eg. '-aC CAlgA -aC CAlgB ...')"+`
C_all
C_AES_256
C_TWOFISH_128
@ -550,10 +561,14 @@ func main() {
C_CRYPTMT1
C_HOPSCOTCH
C_CHACHA20_12`)
flag.Var(&aHMACAlgs, "aH", "Allowed `HMAC`s (eg. '-aH HMACAlgA -aH HMACAlgB ...')" + `
flag.Var(&aHMACAlgs, "aH", "Allowed `HMAC`s (eg. '-aH HMACAlgA -aH HMACAlgB ...')"+`
H_all
H_SHA256
H_SHA512`)
H_SHA512
H_WHIRLPOOL`)
flag.StringVar(&cpuprofile, "cpuprofile", "", "write cpu profile to <`file`>")
flag.StringVar(&memprofile, "memprofile", "", "write memory profile to <`file`>")
flag.Parse()
@ -569,8 +584,26 @@ func main() {
}
}
// === Profiling instrumentation
if cpuprofile != "" {
f, err := os.Create(cpuprofile)
if err != nil {
log.Fatal("could not create CPU profile: ", err)
}
defer f.Close()
fmt.Println("StartCPUProfile()")
if err := pprof.StartCPUProfile(f); err != nil {
log.Fatal("could not start CPU profile: ", err) //nolint:gocritic
} else {
defer pprof.StopCPUProfile()
}
go func() { http.ListenAndServe("localhost:6060", nil) }() //nolint:errcheck,gosec
}
// Enforce some sane min/max vals on chaff flags
if chaffFreqMin < 2 {
if chaffFreqMin < 2 { //nolint:gomnd
chaffFreqMin = 2
}
if chaffFreqMax == 0 {
@ -580,49 +613,52 @@ func main() {
chaffBytesMax = 64
}
Log, _ = logger.New(logger.LOG_DAEMON|logger.LOG_DEBUG|logger.LOG_NOTICE|logger.LOG_ERR, "xsd") // nolint: gosec
Log, _ = logger.New(logger.LOG_DAEMON|logger.LOG_DEBUG|logger.LOG_NOTICE|logger.LOG_ERR, "xsd")
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)
log.SetOutput(io.Discard)
}
// Set up allowed algs, if specified (default allow all)
if len(aKEXAlgs) == 0 {
aKEXAlgs = []string{"none"}
}
logger.LogNotice(fmt.Sprintf("Allowed KEXAlgs: %v\n", aKEXAlgs)) // nolint: gosec,errcheck
logger.LogNotice(fmt.Sprintf("Allowed KEXAlgs: %v\n", aKEXAlgs)) //nolint:errcheck
if len(aCipherAlgs) == 0 {
aCipherAlgs = []string{"none"}
}
logger.LogNotice(fmt.Sprintf("Allowed CipherAlgs: %v\n", aCipherAlgs)) // nolint: gosec,errcheck
logger.LogNotice(fmt.Sprintf("Allowed CipherAlgs: %v\n", aCipherAlgs)) //nolint:errcheck
if len(aHMACAlgs) == 0 {
aHMACAlgs = []string{"none"}
}
logger.LogNotice(fmt.Sprintf("Allowed HMACAlgs: %v\n", aHMACAlgs)) // nolint: gosec,errcheck
logger.LogNotice(fmt.Sprintf("Allowed HMACAlgs: %v\n", aHMACAlgs)) //nolint:errcheck
// 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))
signal.Notify(exitCh, os.Signal(syscall.SIGTERM), os.Signal(syscall.SIGINT), os.Signal(syscall.SIGHUP), os.Signal(syscall.SIGUSR1), os.Signal(syscall.SIGUSR2)) //nolint:lll
go func() {
for {
sig := <-exitCh
switch sig.String() {
case "terminated":
logger.LogNotice(fmt.Sprintf("[Got signal: %s]", sig)) // nolint: gosec,errcheck
switch sig {
case syscall.SIGTERM: //"terminated":
logger.LogNotice(fmt.Sprintf("[Got signal: %s]", sig.String())) //nolint:errcheck
signal.Reset()
syscall.Kill(0, syscall.SIGTERM) // nolint: gosec,errcheck
case "interrupt":
logger.LogNotice(fmt.Sprintf("[Got signal: %s]", sig)) // nolint: gosec,errcheck
syscall.Kill(0, syscall.SIGTERM) //nolint:errcheck
case syscall.SIGINT: //"interrupt":
logger.LogNotice(fmt.Sprintf("[Got signal: %s]", sig.String())) //nolint: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
syscall.Kill(0, syscall.SIGINT) //nolint:errcheck
case syscall.SIGHUP: //"hangup":
logger.LogNotice(fmt.Sprintf("[Got signal: %s - nop]", sig.String())) //nolint:errcheck
if cpuprofile != "" || memprofile != "" {
dumpProf()
}
default:
logger.LogNotice(fmt.Sprintf("[Got signal: %s - ignored]", sig)) // nolint: gosec,errcheck
logger.LogNotice(fmt.Sprintf("[Got signal: %s - ignored]", sig.String())) //nolint:errcheck
}
}
}()
@ -635,13 +671,14 @@ func main() {
if err != nil {
log.Fatal(err)
}
defer l.Close() // nolint: errcheck
defer l.Close()
log.Println("Serving on", laddr)
for {
// Wait for a connection.
// Then check if client-proposed algs are allowed
conn, err := l.Accept()
//logger.LogDebug(fmt.Sprintf("l.Accept()\n"))
if err != nil {
log.Printf("Accept() got error(%v), hanging up.\n", err)
} else {
@ -660,6 +697,24 @@ func main() {
} else {
log.Println("Accepted client")
// Only enable cipher alg changes on re-key if we were told
// to support it (launching xsd with -R), *and* the client
// proposes to use it.
if !remodSupported {
if (conn.Opts() & xsnet.CORemodulateShields) != 0 {
logger.LogDebug("[client proposed cipher/hmac remod, but we don't support it.]")
conn.Close()
continue
}
} else {
if conn.Opts()&xsnet.CORemodulateShields != 0 {
logger.LogDebug("[cipher/hmac remodulation active]")
} else {
logger.LogDebug("[cipher/hmac remodulation inactive]")
}
}
conn.RekeyHelper(rekeySecs)
// Set up chaffing to client
// Will only start when runShellAs() is called
// after stdin/stdout are hooked up
@ -669,12 +724,13 @@ func main() {
// 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
defer hc.ShutdownRekey()
defer hc.Close()
// Start login timeout here and disconnect if user/pass phase stalls
loginTimeout := time.AfterFunc(30*time.Second, func() {
logger.LogNotice(fmt.Sprintln("Login timed out")) // nolint: errcheck,gosec
hc.Write([]byte{0}) // nolint: gosec,errcheck
loginTimeout := time.AfterFunc(LoginTimeoutSecs*time.Second, func() {
logger.LogNotice(fmt.Sprintln("Login timed out")) //nolint:errcheck
hc.Write([]byte{0}) //nolint:errcheck
hc.Close()
})
@ -763,10 +819,10 @@ func main() {
// Tell client if auth was valid
if valid {
hc.Write([]byte{1}) // nolint: gosec,errcheck
hc.Write([]byte{1}) //nolint:errcheck
} else {
logger.LogNotice(fmt.Sprintln("Invalid user", string(rec.Who()))) // nolint: errcheck,gosec
hc.Write([]byte{0}) // nolint: gosec,errcheck
logger.LogNotice(fmt.Sprintln("Invalid user", string(rec.Who()))) //nolint:errcheck
hc.Write([]byte{0}) //nolint:errcheck
return
}
@ -776,15 +832,15 @@ func main() {
// 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
logger.LogNotice(fmt.Sprintf("[Generating autologin token for [%s@%s]]\n", rec.Who(), hname)) //nolint:errcheck
token := GenAuthToken(string(rec.Who()), string(rec.ConnHost()))
tokenCmd := fmt.Sprintf("echo \"%s\" | tee -a ~/.xs_id", token)
tokenCmd := fmt.Sprintf("echo %q | tee -a ~/%s", token, xsnet.XS_ID_AUTHTOKFILE)
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
logger.LogErr(fmt.Sprintf("[Error generating autologin token for %s@%s]\n", rec.Who(), hname)) //nolint:errcheck
} else {
log.Printf("[Autologin token generation completed for %s@%s, status %d]\n", rec.Who(), hname, cmdStatus)
hc.SetStatus(xsnet.CSOType(cmdStatus))
@ -793,31 +849,31 @@ func main() {
// 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
logger.LogNotice(fmt.Sprintf("[Running command for [%s@%s]]\n", rec.Who(), hname)) //nolint: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
logger.LogErr(fmt.Sprintf("[Error spawning cmd for %s@%s]\n", rec.Who(), hname)) //nolint:errcheck
} else {
logger.LogNotice(fmt.Sprintf("[Command completed for %s@%s, status %d]\n", rec.Who(), hname, cmdStatus)) // nolint: gosec,errcheck
logger.LogNotice(fmt.Sprintf("[Command completed for %s@%s, status %d]\n", rec.Who(), hname, cmdStatus)) //nolint: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
logger.LogNotice(fmt.Sprintf("[Running shell for [%s@%s]]\n", rec.Who(), hname)) //nolint: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
Log.Err(fmt.Sprintf("[Error spawning shell for %s@%s]\n", rec.Who(), hname)) //nolint:errcheck
} else {
logger.LogNotice(fmt.Sprintf("[Shell completed for %s@%s, status %d]\n", rec.Who(), hname, cmdStatus)) // nolint: gosec,errcheck
logger.LogNotice(fmt.Sprintf("[Shell completed for %s@%s, status %d]\n", rec.Who(), hname, cmdStatus)) //nolint:errcheck
hc.SetStatus(xsnet.CSOType(cmdStatus))
}
} else if rec.Op()[0] == 'D' {
@ -825,41 +881,38 @@ func main() {
log.Printf("[Client->Server copy]\n")
addr := hc.RemoteAddr()
hname := goutmp.GetHost(addr.String())
logger.LogNotice(fmt.Sprintf("[c->s copy for %s@%s]\n", rec.Who(), hname)) // nolint: gosec,errcheck
logger.LogNotice(fmt.Sprintf("[c->s copy for %s@%s]\n", rec.Who(), hname)) //nolint: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("[c->s copy error for %s@%s]\n", rec.Who(), hname)) // nolint: gosec,errcheck
logger.LogErr(fmt.Sprintf("[c->s copy error for %s@%s]\n", rec.Who(), hname)) //nolint:errcheck
} else {
logger.LogNotice(fmt.Sprintf("[c->s copy completed for %s@%s, status %d]\n", rec.Who(), hname, cmdStatus)) // nolint: gosec,errcheck
logger.LogNotice(fmt.Sprintf("[c->s copy completed for %s@%s, status %d]\n", rec.Who(), hname, cmdStatus)) //nolint:errcheck
}
// TODO: Test this with huge files.. see Bug #22 - do we need to
// sync w/sender (client) that we've gotten all data?
hc.SetStatus(xsnet.CSOType(cmdStatus))
// Send CSOExitStatus *before* client closes channel
s := make([]byte, 4)
s := make([]byte, 4) //nolint:gomnd
binary.BigEndian.PutUint32(s, cmdStatus)
log.Printf("** cp writing closeStat %d at Close()\n", cmdStatus)
hc.WritePacket(s, xsnet.CSOExitStatus) // nolint: gosec,errcheck
hc.WritePacket(s, xsnet.CSOExitStatus) //nolint: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("[s->c copy for %s@%s]\n", rec.Who(), hname)) // nolint: gosec,errcheck
logger.LogNotice(fmt.Sprintf("[s->c copy for %s@%s]\n", rec.Who(), hname)) //nolint:errcheck
cmdStatus, runErr := runServerToClientCopyAs(string(rec.Who()), string(rec.TermType()), hc, string(rec.Cmd()), chaffEnabled)
if runErr != nil {
logger.LogErr(fmt.Sprintf("[s->c copy error for %s@%s]\n", rec.Who(), hname)) // nolint: gosec,errcheck
logger.LogErr(fmt.Sprintf("[s->c copy error for %s@%s]\n", rec.Who(), hname)) //nolint:errcheck
} else {
// Returned hopefully via an EOF or exit/logout;
logger.LogNotice(fmt.Sprintf("[s->c copy completed for %s@%s, status %d]\n", rec.Who(), hname, cmdStatus)) // nolint: gosec,errcheck
logger.LogNotice(fmt.Sprintf("[s->c copy completed for %s@%s, status %d]\n", rec.Who(), hname, cmdStatus)) //nolint:errcheck
}
// HACK: Bug #22: (xc) Need to wait for rcvr to get final data
// TODO: Await specific msg from client to inform they have gotten all data from the tarpipe
time.Sleep(time.Duration(900 * time.Millisecond)) // Let rcvr set this on setup?
// Clear current op so user can enter next, or EOF
rec.SetOp([]byte{0})
@ -868,12 +921,32 @@ func main() {
//_, _ = hc.Read(nil /*ackByte*/)
//fmt.Println("Got remote end ack.")
} else {
logger.LogErr(fmt.Sprintln("[Bad xs.Session]")) // nolint: gosec,errcheck
logger.LogErr(fmt.Sprintln("[Bad xs.Session]")) //nolint:errcheck
}
return
}(&conn) // nolint: errcheck
}(&conn) //nolint:errcheck
} // algs valid and not blacklisted
} // Accept() success
} //endfor
//logger.LogNotice(fmt.Sprintln("[Exiting]")) // nolint: gosec,errcheck
//logger.LogNotice(fmt.Sprintln("[Exiting]")) //nolint:errcheck
}
func dumpProf() {
if cpuprofile != "" {
pprof.StopCPUProfile()
}
if memprofile != "" {
f, err := os.Create(memprofile)
if err != nil {
log.Fatal("could not create memory profile: ", err)
}
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
}
}
//os.Exit(status)
}

22
xsnet/chan.go Normal file → Executable file
View File

@ -22,10 +22,11 @@ import (
"blitter.com/go/cryptmt"
"blitter.com/go/hopscotch"
"blitter.com/go/xs/logger"
"github.com/aead/chacha20/chacha"
"golang.org/x/crypto/blowfish"
"golang.org/x/crypto/twofish"
whirlpool "github.com/jzelinskie/whirlpool"
// hash algos must be manually imported thusly:
// (Would be nice if the golang pkg docs were more clear
// on this...)
@ -57,9 +58,19 @@ func expandKeyMat(keymat []byte, blocksize int) []byte {
return keymat
}
/* Support functionality to set up encryption after a channel has
been negotiated via xsnet.go
*/
// Choose a cipher and hmac alg from supported sets, given two uint8 values
func getNewStreamAlgs(cb uint8, hb uint8) (config uint32) {
// Get new cipher and hash algs (clamped to valid values) based on
// the input rekeying data
c := (cb % CAlgNoneDisallowed)
h := (hb % HmacNoneDisallowed)
config = uint32(h<<8) | uint32(c)
logger.LogDebug(fmt.Sprintf("[Chose new algs [%d:%d]", h, c))
return
}
// (Re-)initialize the keystream and hmac state for an xsnet.Conn, returning
// a cipherStream and hash
func (hc *Conn) getStream(keymat []byte) (rc cipher.Stream, mc hash.Hash, err error) {
var key []byte
var block cipher.Block
@ -146,6 +157,9 @@ func (hc *Conn) getStream(keymat []byte) (rc cipher.Stream, mc hash.Hash, err er
if !halg.Available() {
log.Fatal("hash not available!")
}
case HmacWHIRLPOOL:
log.Printf("[hash HmacWHIRLPOOL (%d)]\n", hopts)
mc = whirlpool.New()
default:
log.Printf("[invalid hmac (%d)]\n", hopts)
fmt.Printf("DOOFUS SET A VALID HMAC ALG (%d)\n", hopts)

28
xsnet/consts.go Normal file → Executable file
View File

@ -52,6 +52,8 @@ const (
CSEKEXAlgDenied // server rejected proposed KEX alg
CSECipherAlgDenied // server rejected proposed Cipher alg
CSEHMACAlgDenied // server rejected proposed HMAC alg
CSEConnDead // connection keepalives expired
CSELoginTimeout
)
// Extended (>255 UNIX exit status) codes
@ -67,9 +69,6 @@ const (
CSOExitStatus // Remote cmd exit status
CSOChaff // Dummy packet, do not pass beyond decryption
// Client side errors
CSOLoginTimeout
// Tunnel setup/control/status
CSOTunSetup // client -> server tunnel setup request (dstport)
CSOTunSetupAck // server -> client tunnel setup ack
@ -78,6 +77,8 @@ const (
CSOTunKeepAlive // client tunnel heartbeat
CSOTunDisconn // server -> client: tunnel rport disconnected
CSOTunHangup // client -> server: tunnel lport hung up
CSOKeepAlive // bidir keepalive packet to monitor main connection
CSORekey // TODO: rekey/re-select session cipher/hash algs
)
// TunEndpoint.tunCtl control values - used to control workers for client
@ -97,7 +98,7 @@ const (
// Channel status Op byte type (see CSONone, ... and CSENone, ...)
type CSOType uint32
//TODO: this should be small (max unfragmented packet size?)
// TODO: this should be small (max unfragmented packet size?)
const MAX_PAYLOAD_LEN = 2*1024*1024*1024 - 1
// Session symmetric crypto algs
@ -118,8 +119,27 @@ type CSCipherAlg uint32
const (
HmacSHA256 = iota
HmacSHA512
HmacWHIRLPOOL
HmacNoneDisallowed
)
// Conn opts outside of basic kex/cipher/hmac connect config
const (
CONone = iota
CORemodulateShields // if set, rekeying also reselects random cipher/hmac alg
)
type COValue uint32
// Available HMACs for hkex.Conn
type CSHmacAlg uint32
// Some bounds-checking consts
const (
REKEY_SECS_MIN = 1
REKEY_SECS_MAX = 28800 // 8 hours
CHAFF_FREQ_MSECS_MIN = 1
CHAFF_FREQ_MSECS_MAX = 300000 // 5 minutes
)
const XS_ID_AUTHTOKFILE = ".config/xs/.xs_id"

0
xsnet/kcp.go Normal file → Executable file
View File

307
xsnet/net.go Normal file → Executable file
View File

@ -9,7 +9,7 @@
package xsnet
// Implementation of HKEx-wrapped versions of the golang standard
// Implementation of key-exchange-wrapped versions of the golang standard
// net package interfaces, allowing clients and servers to simply replace
// 'net.Dial' and 'net.Listen' with 'hkex.Dial' and 'hkex.Listen'
// (though some extra methods are implemented and must be used
@ -64,7 +64,6 @@ type (
// see: https://en.wikipedia.org/wiki/chaff_(countermeasure)
ChaffConfig struct {
shutdown bool //set to inform chaffHelper to shut down
enabled bool
msecsMin uint //msecs min interval
msecsMax uint //msecs max interval
szMax uint // max size in bytes
@ -87,8 +86,11 @@ type (
Rows uint16
Cols uint16
chaff ChaffConfig
tuns *map[uint16](*TunEndpoint)
keepalive uint // if this reaches zero, conn is considered dead
rekey uint // if nonzero, rekeying interval in seconds
Pproc int // proc ID of command run on this conn
chaff ChaffConfig
tuns *map[uint16](*TunEndpoint)
closeStat *CSOType // close status (CSOExitStatus)
r cipher.Stream //read cipherStream
@ -174,6 +176,8 @@ func (h *CSHmacAlg) String() string {
return "H_SHA256"
case HmacSHA512:
return "H_SHA512"
case HmacWHIRLPOOL:
return "H_WHIRLPOOL"
default:
return "H_ERR_UNK"
}
@ -238,7 +242,7 @@ func (hc *Conn) SetConnOpts(copts uint32) {
//
// Consumers of this lib may use this for protocol-level options not part
// of the KEx or encryption info used by the connection.
func (hc Conn) Opts() uint32 {
func (hc *Conn) Opts() uint32 {
return hc.opts
}
@ -267,8 +271,6 @@ func _new(kexAlg KEXAlg, conn *net.Conn) (hc *Conn, e error) {
tempMap := make(map[uint16]*TunEndpoint)
hc.tuns = &tempMap
*hc.closeStat = CSEStillOpen // open or prematurely-closed status
// Set up KEx/KEM-specifics
switch kexAlg {
case KEX_HERRADURA256:
@ -310,9 +312,9 @@ func _new(kexAlg KEXAlg, conn *net.Conn) (hc *Conn, e error) {
// applyConnExtensions processes optional Dial() negotiation
// parameters. See also getkexalgnum().
//
// Currently defined extension values
// # Currently defined extension values
//
// KEx algs
// # KEx algs
//
// KEX_HERRADURA256 KEX_HERRADURA512 KEX_HERRADURA1024 KEX_HERRADURA2048
//
@ -320,11 +322,11 @@ func _new(kexAlg KEXAlg, conn *net.Conn) (hc *Conn, e error) {
//
// KEX_NEWHOPE KEX_NEWHOPE_SIMPLE
//
// Session (symmetric) crypto
// # Session (symmetric) crypto
//
// C_AES_256 C_TWOFISH_128 C_BLOWFISH_128 C_CRYPTMT1 C_CHACHA20_12 C_HOPSCOTCH
//
// Session HMACs
// # Session HMACs
//
// H_SHA256 H_SHA512
func (hc *Conn) applyConnExtensions(extensions ...string) {
@ -362,6 +364,13 @@ func (hc *Conn) applyConnExtensions(extensions ...string) {
log.Println("[extension arg = H_SHA512]")
hc.cipheropts &= (0xFFFF00FF)
hc.cipheropts |= (HmacSHA512 << 8)
case "H_WHIRLPOOL":
log.Println("[extension arg = H_WHIRLPOOL]")
hc.cipheropts &= (0xFFFF00FF)
hc.cipheropts |= (HmacWHIRLPOOL << 8)
case "OPT_REMOD":
log.Println("[extension arg = OPT_REMOD]")
hc.opts |= CORemodulateShields
//default:
// log.Printf("[Dial ext \"%s\" ignored]\n", s)
}
@ -884,12 +893,12 @@ func HKExAcceptSetup(c *net.Conn, hc *Conn) (err error) {
// Dial as net.Dial(), but with implicit key exchange to set up secure
// channel on connect
//
// Can be called like net.Dial(), defaulting to C_AES_256/H_SHA256,
// or additional extensions can be passed amongst the following:
// Can be called like net.Dial(), defaulting to C_AES_256/H_SHA256,
// or additional extensions can be passed amongst the following:
//
// "C_AES_256" | "C_TWOFISH_128" | ...
// "C_AES_256" | "C_TWOFISH_128" | ...
//
// "H_SHA256" | "H_SHA512" | ...
// "H_SHA256" | "H_SHA512" | ...
//
// See go doc -u xsnet.applyConnExtensions
func Dial(protocol string, ipport string, extensions ...string) (hc Conn, err error) {
@ -973,12 +982,25 @@ func Dial(protocol string, ipport string, extensions ...string) (hc Conn, err er
// Close a hkex.Conn
func (hc *Conn) Close() (err error) {
hc.DisableChaff()
hc.ShutdownChaff()
s := make([]byte, 4)
binary.BigEndian.PutUint32(s, uint32(*hc.closeStat))
log.Printf("** Writing closeStat %d at Close()\n", *hc.closeStat)
//(*hc.c).SetWriteDeadline(time.Now().Add(500 * time.Millisecond))
hc.WritePacket(s, CSOExitStatus)
// HACK: Bug #22,#33: Need to wait for rcvr to get final data
// TODO: Find a way to explicitly check if any outgoing data is pending
//Ensure socket has sent all data to client prior to closing
//NOTE: This is not ideal, as it would be better to somehow
//determine if there is any pending outgoing (write) data to the
//underlying socket (TCP/KCP) prior to closing; however Go's net pkg
//completely hides lower-level stuff. net.Conn.Close() according to
//docs sends written data in the background, so how best to determine
//all data has been sent? -rlm 2022-10-04
time.Sleep(10 * time.Millisecond) //nolint:gomnd
err = (*hc.c).Close()
logger.LogDebug(fmt.Sprintln("[Conn Closing]"))
return
@ -1073,7 +1095,7 @@ func (hl HKExListener) Close() error {
return hl.l.Close()
}
// Addr returns a the listener's network address.
// Addr returns the listener's network address.
//
// See go doc net.Listener.Addr
func (hl HKExListener) Addr() net.Addr {
@ -1098,7 +1120,7 @@ func (hl *HKExListener) Accept() (hc Conn, err error) {
return Conn{}, err
}
logger.LogDebug(fmt.Sprintln("[net.Listener Accepted]"))
logger.LogDebug(fmt.Sprintf("[net.Listener Accepted %v]\n", c.RemoteAddr()))
}
// Read KEx alg proposed by client
var kexAlg KEXAlg
@ -1185,7 +1207,7 @@ func (hl *HKExListener) Accept() (hc Conn, err error) {
// packet processing.
//
// See go doc io.Reader
func (hc Conn) Read(b []byte) (n int, err error) {
func (hc *Conn) Read(b []byte) (n int, err error) {
for {
if hc.dBuf.Len() > 0 {
break
@ -1195,6 +1217,7 @@ 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 {
@ -1202,7 +1225,8 @@ func (hc Conn) Read(b []byte) (n int, err error) {
return 0, io.EOF
}
if strings.HasSuffix(err.Error(), "use of closed network connection") {
logger.LogDebug(fmt.Sprintln("[Client hung up]"))
logger.LogDebug(fmt.Sprintln("[Client hung up(1)]"))
//!rlm hc.SetStatus(CSENone) //FIXME: re-examine this (exit 9 w/o it - 2023-11-05)
return 0, io.EOF
}
etxt := fmt.Sprintf("** Failed read:%s (%s) **", "ctrlStatOp", err)
@ -1215,7 +1239,9 @@ 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 {
@ -1223,27 +1249,30 @@ func (hc Conn) Read(b []byte) (n int, err error) {
return 0, io.EOF
}
if strings.HasSuffix(err.Error(), "use of closed network connection") {
logger.LogDebug(fmt.Sprintln("[Client hung up]"))
logger.LogDebug(fmt.Sprintln("[Client hung up(2)]"))
return 0, io.EOF
}
etxt := fmt.Sprintf("** Failed read:%s (%s) **", "HMAC", err)
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" {
return 0, io.EOF
}
if strings.HasSuffix(err.Error(), "use of closed network connection") {
logger.LogDebug(fmt.Sprintln("[Client hung up]"))
logger.LogDebug(fmt.Sprintln("[Client hung up(3)]"))
return 0, io.EOF
}
etxt := fmt.Sprintf("** Failed read:%s (%s) **", "payloadLen", err)
logger.LogDebug(etxt)
return 0, errors.New(etxt)
}
//----------------------------------------------------------
if payloadLen > MAX_PAYLOAD_LEN {
logger.LogDebug(fmt.Sprintf("[Insane payloadLen:%v]\n", payloadLen))
@ -1251,6 +1280,7 @@ 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 {
@ -1258,19 +1288,21 @@ func (hc Conn) Read(b []byte) (n int, err error) {
return 0, io.EOF
}
if strings.HasSuffix(err.Error(), "use of closed network connection") {
logger.LogDebug(fmt.Sprintln("[Client hung up]"))
logger.LogDebug(fmt.Sprintln("[Client hung up(4)]"))
return 0, io.EOF
}
etxt := fmt.Sprintf("** Failed read:%s (%s) **", "payloadBytes", err)
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)
@ -1280,7 +1312,9 @@ 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
@ -1289,6 +1323,7 @@ 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]))
@ -1297,6 +1332,7 @@ 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]
@ -1307,15 +1343,33 @@ func (hc Conn) Read(b []byte) (n int, err error) {
} else {
payloadBytes = payloadBytes[0 : len(payloadBytes)-int(padLen)]
}
//--------------------------------------------------
// Throw away pkt if it's chaff (ie., caller to Read() won't see this data)
if ctrlStatOp == CSOChaff {
switch ctrlStatOp {
case CSOChaff:
// Throw away pkt if it's chaff (ie., caller to Read() won't see this data)
log.Printf("[Chaff pkt, discarded (len %d)]\n", decryptN)
} else if ctrlStatOp == CSOTermSize {
case CSOKeepAlive:
//logger.LogDebug(fmt.Sprintf("[got keepAlive pkt, discarded (len %d)]\n", decryptN))
// payload of keepalive (2 bytes) is not currently used (0x55aa fixed)
_ = binary.BigEndian.Uint16(payloadBytes[0:2])
hc.ResetKeepAlive()
case CSORekey:
// rekey
//logger.LogDebug(fmt.Sprintf("[Got rekey [%02x %02x %02x ...]\n",
// payloadBytes[0], payloadBytes[1], payloadBytes[2]))
rekeyData := payloadBytes
if (hc.opts & CORemodulateShields) != 0 {
hc.Lock()
hc.cipheropts = getNewStreamAlgs(rekeyData[0], rekeyData[1])
hc.Unlock()
}
hc.r, hc.rm, err = hc.getStream(rekeyData)
case 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}
} else if ctrlStatOp == CSOExitStatus {
case CSOExitStatus:
if len(payloadBytes) > 0 {
hc.SetStatus(CSOType(binary.BigEndian.Uint32(payloadBytes)))
} else {
@ -1323,7 +1377,7 @@ func (hc Conn) Read(b []byte) (n int, err error) {
hc.SetStatus(CSETruncCSO)
}
hc.Close()
} else if ctrlStatOp == CSOTunSetup {
case CSOTunSetup:
// server side tunnel setup in response to client
lport := binary.BigEndian.Uint16(payloadBytes[0:2])
rport := binary.BigEndian.Uint16(payloadBytes[2:4])
@ -1335,7 +1389,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
} else if ctrlStatOp == CSOTunSetupAck {
case CSOTunSetupAck:
lport := binary.BigEndian.Uint16(payloadBytes[0:2])
rport := binary.BigEndian.Uint16(payloadBytes[2:4])
if _, ok := (*hc.tuns)[rport]; !ok {
@ -1346,7 +1400,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
} else if ctrlStatOp == CSOTunRefused {
case CSOTunRefused:
// client side receiving CSOTunRefused means the remote side
// could not dial() rport. So we cannot yet listen()
// for client-side on lport.
@ -1358,7 +1412,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))
}
} else if ctrlStatOp == CSOTunDisconn {
case CSOTunDisconn:
// server side's rport has disconnected (server lost)
lport := binary.BigEndian.Uint16(payloadBytes[0:2])
rport := binary.BigEndian.Uint16(payloadBytes[2:4])
@ -1368,7 +1422,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))
}
} else if ctrlStatOp == CSOTunHangup {
case CSOTunHangup:
// client side's lport has hung up
lport := binary.BigEndian.Uint16(payloadBytes[0:2])
rport := binary.BigEndian.Uint16(payloadBytes[2:4])
@ -1378,7 +1432,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))
}
} else if ctrlStatOp == CSOTunData {
case 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:])
@ -1391,20 +1445,23 @@ 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))
}
} else if ctrlStatOp == CSOTunKeepAlive {
case 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.
_ = binary.BigEndian.Uint16(payloadBytes[0:2])
//logger.LogDebug(fmt.Sprintf("[Server] Got CSOTunKeepAlive"))
// though CSOTunKeepAlive sends an endp (uint16), we don't use it,
// preferring to refresh *all* tunnels on the message.
// (?rlm 2023-11-04 -- TODO: verify this, it's been a while.)
for _, t := range *hc.tuns {
hc.Lock()
t.KeepAlive = 0
hc.Unlock()
}
} else if ctrlStatOp == CSONone {
case CSONone:
hc.dBuf.Write(payloadBytes)
} else {
default:
logger.LogDebug(fmt.Sprintf("[Unknown CSOType:%d]", ctrlStatOp))
}
}
@ -1424,13 +1481,18 @@ func (hc Conn) Read(b []byte) (n int, err error) {
// Write a byte slice
//
// See go doc io.Writer
func (hc Conn) Write(b []byte) (n int, err error) {
func (hc *Conn) Write(b []byte) (n int, err error) {
//logger.LogDebug("[+Write]")
n, err = hc.WritePacket(b, CSONone)
//logger.LogDebug("[-Write]")
return n, err
}
// Write a byte slice with specified ctrlStatOp byte
func (hc *Conn) WritePacket(b []byte, ctrlStatOp byte) (n int, err error) {
hc.Lock()
defer hc.Unlock()
//log.Printf("[Encrypting...]\r\n")
var hmacOut []uint8
var payloadLen uint32
@ -1458,15 +1520,6 @@ func (hc *Conn) WritePacket(b []byte, ctrlStatOp byte) (n int, err error) {
b = append([]byte{byte(padSide)}, append([]byte{byte(padLen)}, append(b, padBytes...)...)...)
}
// N.B. Originally this Lock() surrounded only the
// calls to binary.Write(hc.c ..) however there appears
// to be some other unshareable state in the Conn
// struct that must be protected to serialize main and
// chaff data written to it.
//
// Would be nice to determine if the mutex scope
// could be tightened.
hc.Lock()
payloadLen = uint32(len(b))
if hc.logPlainText {
log.Printf(" >:ptext:\r\n%s\r\n", hex.Dump(b[0:payloadLen]))
@ -1524,7 +1577,6 @@ func (hc *Conn) WritePacket(b []byte, ctrlStatOp byte) (n int, err error) {
} else {
//fmt.Println("[a]WriteError!")
}
hc.Unlock()
if err != nil {
log.Println(err)
@ -1539,53 +1591,182 @@ func (hc *Conn) WritePacket(b []byte, ctrlStatOp byte) (n int, err error) {
return retN, err
}
func (hc *Conn) EnableChaff() {
func (hc *Conn) StartupChaff() {
hc.chaff.shutdown = false
hc.chaff.enabled = true
log.Println("Chaffing ENABLED")
hc.chaffHelper()
}
func (hc *Conn) DisableChaff() {
hc.chaff.enabled = false
log.Println("Chaffing DISABLED")
}
func (hc *Conn) ShutdownChaff() {
hc.Lock()
hc.chaff.shutdown = true
hc.Unlock()
log.Println("Chaffing SHUTDOWN")
}
func (hc *Conn) SetupChaff(msecsMin uint, msecsMax uint, szMax uint) {
// Enforce bounds on chaff frequency and pkt size
hc.Lock()
if hc.chaff.msecsMin < CHAFF_FREQ_MSECS_MIN {
hc.chaff.msecsMin = CHAFF_FREQ_MSECS_MIN
}
if hc.chaff.msecsMax > CHAFF_FREQ_MSECS_MAX {
hc.chaff.msecsMax = CHAFF_FREQ_MSECS_MAX
}
hc.Unlock()
hc.chaff.msecsMin = msecsMin //move these to params of chaffHelper() ?
hc.chaff.msecsMax = msecsMax
hc.chaff.szMax = szMax
}
func (hc *Conn) ShutdownRekey() {
hc.Lock()
hc.rekey = 0
hc.Unlock()
}
func (hc *Conn) RekeyHelper(intervalSecs uint) {
if intervalSecs < REKEY_SECS_MIN {
intervalSecs = REKEY_SECS_MIN
}
if intervalSecs > REKEY_SECS_MAX {
intervalSecs = REKEY_SECS_MAX
}
go func() {
hc.Lock()
hc.rekey = intervalSecs
hc.Unlock()
for {
hc.Lock()
rekey := hc.rekey
hc.Unlock()
if rekey != 0 {
jitter := rand.Intn(int(rekey)) / 4
rekey = rekey - uint(jitter)
if rekey < 1 {
rekey = 1
}
//logger.LogDebug(fmt.Sprintf("[rekeyHelper Loop]\n"))
time.Sleep(time.Duration(rekey) * time.Second)
// Send rekey to other end
rekeyData := make([]byte, 64)
_, err := crand.Read(rekeyData)
//logger.LogDebug(fmt.Sprintf("[rekey [%02x %02x %02x ...]\n",
// rekeyData[0], rekeyData[1], rekeyData[2]))
//logger.LogDebug("[+rekeyHelper]")
_, err = hc.WritePacket(rekeyData, CSORekey)
hc.Lock()
if (hc.opts & CORemodulateShields) != 0 {
hc.cipheropts = getNewStreamAlgs(rekeyData[0], rekeyData[1])
}
hc.w, hc.wm, err = hc.getStream(rekeyData)
//logger.LogDebug("[-rekeyHelper]")
hc.Unlock()
if err != nil {
log.Printf("[rekey WritePacket err! (%v) rekey dying ...]\n", err)
return
}
} else {
return
}
}
}()
}
// Helper routine to spawn a chaffing goroutine for each Conn
func (hc *Conn) chaffHelper() {
go func() {
var nextDuration int
for {
var nextDuration int
if hc.chaff.enabled {
//logger.LogDebug(fmt.Sprintf("[chaffHelper Loop]\n"))
hc.Lock()
shutdown := hc.chaff.shutdown
hc.Unlock()
if !shutdown {
var bufTmp []byte
bufTmp = make([]byte, rand.Intn(int(hc.chaff.szMax)))
min := int(hc.chaff.msecsMin)
nextDuration = rand.Intn(int(hc.chaff.msecsMax)-min) + min
_, _ = rand.Read(bufTmp)
//logger.LogDebug("[+chaffHelper]")
_, err := hc.WritePacket(bufTmp, CSOChaff)
//logger.LogDebug("[-chaffHelper]")
if err != nil {
log.Println("[ *** error - chaffHelper quitting *** ]")
hc.chaff.enabled = false
log.Println("[ *** error - chaffHelper shutting down *** ]")
hc.Lock()
hc.chaff.shutdown = true
hc.Unlock()
break
}
}
time.Sleep(time.Duration(nextDuration) * time.Millisecond)
if hc.chaff.shutdown {
log.Println("*** chaffHelper shutting down")
} else {
log.Println("[ *** chaffHelper shutting down *** ]")
break
}
time.Sleep(time.Duration(nextDuration) * time.Millisecond)
}
}()
}
func (hc *Conn) StartupKeepAlive() {
hc.ResetKeepAlive()
log.Println("KeepAlive ENABLED")
hc.keepaliveHelper()
}
func (hc *Conn) ShutdownKeepAlive() {
log.Println("Conn SHUTDOWN")
hc.Close()
}
func (hc *Conn) ResetKeepAlive() {
hc.Lock()
hc.keepalive = 3
hc.Unlock()
log.Println("KeepAlive RESET")
}
// Helper routine to spawn a keepalive goroutine for each Conn
func (hc *Conn) keepaliveHelper() {
go func() {
for {
nextDuration := 10000
bufTmp := []byte{0x55, 0xaa}
//logger.LogDebug("[+keepaliveHelper]")
_, err := hc.WritePacket(bufTmp, CSOKeepAlive)
//logger.LogDebug("[-keepaliveHelper]")
//logger.LogDebug(fmt.Sprintf("[keepalive]\n"))
if err != nil {
logger.LogDebug(fmt.Sprintf("[ *** error - keepaliveHelper quitting *** ]\n"))
break
}
time.Sleep(time.Duration(nextDuration) * time.Millisecond)
hc.Lock()
hc.keepalive -= 1
hc.Unlock()
//logger.LogDebug(fmt.Sprintf("[keepAlive is now %d]\n", hc.keepalive))
//if rand.Intn(8) == 0 {
// hc.keepalive = 0
//}
if hc.keepalive == 0 {
logger.LogDebug(fmt.Sprintf("*** keepaliveHelper shutting down\n"))
hc.SetStatus(CSEConnDead)
hc.ShutdownKeepAlive()
if hc.Pproc != 0 {
//fmt.Printf("[pid %d needs to be killed]\n", hc.Pproc)
//syscall.Kill(hc.Pproc, syscall.SIGABRT) //nolint:errcheck
//exec.Command("taskkill", "/f", "/pid", strconv.Itoa(hc.Pproc)).Run()
hc.kill()
}
break
}
}
}()
}

13
xsnet/net_linux.go Executable file
View File

@ -0,0 +1,13 @@
//go:build linux
// +build linux
package xsnet
import (
"syscall"
)
func (hc *Conn) kill() {
syscall.Kill(hc.Pproc, syscall.SIGABRT) //nolint:errcheck
}

13
xsnet/net_windows.go Executable file
View File

@ -0,0 +1,13 @@
//go:build windows
// +build windows
package xsnet
import (
"os/exec"
"strconv"
)
func (hc *Conn) kill() {
exec.Command("taskkill", "/f", "/pid", strconv.Itoa(hc.Pproc)).Run()
}

2
xsnet/tun.go Normal file → Executable file
View File

@ -37,6 +37,8 @@ type (
// client starts worker to receive/send data using lport
// ... client disconnects: sends remhost [CSOTunClose:rport]
// ... or server disconnects: sends client [CSOTunClose:lport]
// ... or server disconnects: due to client failing to send TunKeepAlive
// events for too long
// server at any time sends [CSOTunRefused:rport] if daemon died
// --

View File

@ -1,5 +1,5 @@
// Util to generate/store passwords for users in a file akin to /etc/passwd
// suitable for the demo hkexsh server, using bcrypt.
// suitable for the xs server, using bcrypt.
//
// Copyright (c) 2017-2020 Russell Magee
// Licensed under the terms of the MIT license (see LICENSE.mit in this