Compare commits

...

79 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
Russtopia f24d4d8fdb Update 'README.md' 2022-07-02 12:51:18 -07:00
Russtopia 42ad33065b Update 'README.md' 2022-07-02 12:44:37 -07:00
Russtopia 41769660cc Bump version → 0.9.5.1 2022-07-02 12:01:15 -07:00
Russtopia 73eadf7534 Update 'README.md' 2022-07-02 12:00:12 -07:00
Russ Magee 819b359306 Merge branch 'master' of https://gogs.blitter.com/RLabs/xs 2022-06-16 22:45:33 -07:00
Russ Magee ee19787b5e Added support for building garbled binaries (https://github.com/burrowers/garble.git) 2022-06-16 22:45:25 -07:00
Russ Magee 0765c8bea6 Handle missing go.mod, go.sum 2022-05-01 23:19:50 -07:00
Russ Magee a4af720280 Put lint before build in CI script 2022-04-09 20:05:42 -07:00
32 changed files with 1710 additions and 1567 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.4
.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
############################################################
@ -10,6 +11,14 @@ else
MTAG="-m"
endif
# If available, one may build 'garbled' binaries
# See https://github.com/burrowers/garble.git
ifeq ($(GARBLE),y)
GO=garble -literals -tiny -debugdir=garbled
else
GO=go
endif
ifneq ($(VENDOR),)
GOBUILDOPTS :=-v -mod vendor
VTAG = "-v"
@ -34,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
@ -63,8 +69,8 @@ tools:
common:
go build .
go install .
$(GO) build .
go install -a .
client: common
@ -72,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 \
@ -94,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 -
@ -108,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,35 +94,34 @@ 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
As of Go 1.8, one can directly use `go install` to get the client `xs` and server `xsd` binaries; however it is not recommended, as `xsd` requires root and for general use should be in one of the system directories, akin to other daemons. If one insists, the following will work to place them in $HOME/go/bin:
```
$ go install blitter.com/go/xs/xs@latest
$ go install blitter.com/go/xs/xsd@latest
```
(NOTE the `-v` (version) option for binaries obtained in this manner will be blank; another reason to build them yourself locally using the steps below.)
### Get source code
```
$ go get -u blitter.com/go/xs
$ cd $GOPATH/src/blitter.com/go/xs
$ go build ./... # install all dependent go pkgs
$ git clone https://gogs.blitter.com/RLabs/xs
```
### To build
```
$ cd $GOPATH/src/blitter.com/go/xs
$ make clean all
$ cd xs
$ make clean && make
```
### To install, uninstall, re-install
### To install, uninstall, re-install (xsd server)
```
$ sudo make [install | uninstall | reinstall]
@ -146,9 +149,10 @@ The make system assumes installation in /usr/local/sbin (xsd, xspasswd) and /usr
```
$ sudo rc-config [start | restart | stop] xsd
# .. or sudo /etc/init.d/xsd [start | restart stop]
```
### To set accounts & passwords:
### To set accounts & passwords (DEPRECATED: `-s` is now true by default)
```
$ sudo touch /etc/xs.passwd
@ -183,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
@ -214,7 +226,7 @@ NOTE: Renaming while copying (eg., 'cp /foo/bar/fileA ./fileB') is NOT supported
If the 'pv' pipeview utility is available (http://www.ivarch.com/programs/pv.shtml) file transfer progress and bandwidth control will be available (suppress the former with the -q option, set the latter with -L &lt;bytes_per_second&gt;).
Special care should be taken when doing client->server copies: since the tarpipe (should) always succeed at least sending data to the remote side, a destination with no write permission will not return a nonzero status and the client closes its end after sending all data, giving the server no opportunity to send an error code to the client.
Special care should be taken when doing clientserver copies: since the tarpipe (should) always succeed at least sending data to the remote side, a destination with no write permission will not return a nonzero status and the client closes its end after sending all data, giving the server no opportunity to send an error code to the client.
It is recommended to test beforehand if the server-side destination is writable (and optionally if the destination already exists, if one does not want to clobber an existing path) by:
```
@ -226,7 +238,7 @@ Perhaps in future a more complex handshake will be devised to allow the client t
### Tunnels
Simple tunnels (client -> server, no reverse tunnels for now) are supported.
Simple tunnels (client server, no reverse tunnels for now) are supported.
Syntax: xs -T=&lt;tunspec&gt;{,&lt;tunspec&gt;...}
.. where &lt;tunspec&gt; is &lt;localport:remoteport&gt;

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,22 +25,22 @@ 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
mv go.sum go.sum.git
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
############
stage "Lint"
############
make lint
############
stage "UnitTests"
############
@ -49,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
@ -94,16 +94,11 @@ 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
############
stage "Lint"
############
make lint
############
stage "Artifacts"
############

35
go.mod
View File

@ -1,35 +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 v4.3.4+incompatible
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c
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
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb // indirect
gopkg.in/hlandau/easymetric.v1 v1.0.0 // indirect
gopkg.in/hlandau/measurable.v1 v1.0.1 // indirect
)

135
go.sum
View File

@ -1,135 +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 v4.3.4+incompatible h1:T56s9GLhx+KZUn5T8aO2Didfa4uTYvjeVIRLt6uYdhE=
github.com/xtaci/kcp-go v4.3.4+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE=
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 h1:4yd7jl+vXjalO5ztz6Vc1VADv+S/80LGJmyl1ROJ2AI=
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
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 h1:mUVeFHoDKis5nxCAzoAi7E8Ghb86EXh/RK6wtvJIqRY=
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
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-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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/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

@ -2,8 +2,14 @@
EXE = $(notdir $(shell pwd))
ifeq ($(GARBLE),y)
GO=garble -literals -tiny -debugdir=garbled
else
GO = go
endif
all:
go build .
$(GO) build .
clean:
$(RM) $(EXE) $(EXE).exe

View File

@ -1,11 +1,18 @@
.PHONY: info clean lib
ifeq ($(GARBLE),y)
GO = garble -literals -tiny -debugdir=garbled
else
GO = go
endif
all: lib
clean:
go clean .
lib: info
$(GO) build .
go install .
ifneq ($(MSYSTEM),)

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,11 +1,17 @@
.PHONY: clean all vis lint
ifeq ($(GARBLE),y)
GO = garble -literals -tiny -debugdir=garbled
else
GO = go
endif
EXTPKGS = bytes,errors,flag,fmt,internal,io,log,net,os,path,runtime,time,strings,sync,syscall,binary,encoding
EXE = $(notdir $(shell pwd))
all:
echo "BUILDOPTS:" $(BUILDOPTS)
go build $(BUILDOPTS) .
$(GO) build $(BUILDOPTS) .
clean:
$(RM) $(EXE) $(EXE).exe

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

View File

@ -1,10 +1,16 @@
.PHONY: clean all vis lint
ifeq ($(GARBLE),y)
GO = garble -literals -tiny -debugdir=garbled
else
GO = go
endif
EXTPKGS = binary,bytes,crypto,encoding,errors,flag,fmt,internal,io,log,net,os,path,runtime,time,strings,sync,syscall
EXE = $(notdir $(shell pwd))
all:
go build $(BUILDOPTS) .
$(GO) build $(BUILDOPTS) .
clean:
$(RM) $(EXE) $(EXE).exe

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)
}

View File

@ -1,12 +1,18 @@
.PHONY: info clean lib
ifeq ($(GARBLE),y)
GO = garble -tiny -literals -debugdir=garbled
else
GO = go
endif
all: lib
clean:
go clean .
lib: info
go install .
$(GO) install .
ifneq ($(MSYSTEM),)
info:

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,10 +1,16 @@
.PHONY: clean all vis lint
ifeq ($(GARBLE),y)
GO = garble -tiny -literals -debugdir=garbled
else
GO = go
endif
EXTPKGS = bytes,errors,flag,fmt,internal,io,log,net,os,path,runtime,time,strings,sync,syscall,binary,encoding
EXE = $(notdir $(shell pwd))
all:
go build $(BUILDOPTS) .
$(GO) build $(BUILDOPTS) .
clean:
$(RM) $(EXE) $(EXE).exe

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