Compare commits

..

No commits in common. "master" and "v0.8.22" have entirely different histories.

40 changed files with 1743 additions and 2705 deletions

View File

@ -1,160 +1,327 @@
# 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:
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"
dogsled:
# checks assignments with too many blank identifiers; default is 2
max-blank-identifiers: 2
dupl:
threshold: 125
# 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
funlen:
lines: 125
statements: 50
gci:
local-prefixes: github.com/golangci/golangci-lint
lines: 60
statements: 40
gocognit:
# minimal code complexity to report, 30 by default (but we recommend 10-20)
min-complexity: 10
goconst:
min-len: 2
min-occurrences: 2
# minimal length of string constant, 3 by default
min-len: 3
# minimal occurrences count to trigger, 3 by default
min-occurrences: 3
gocritic:
enabled-tags:
- diagnostic
- experimental
- performance
#- style
#- opinionated
# 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:
- commentFormatting
- commentedOutCode
- dupImport # https://github.com/go-critic/go-critic/issues/845
- ifElseChain
- octalLiteral
- whyNoLint
- wrapperFunc
- 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:
- performance
settings: # settings passed to gocritic
captLocal: # must be valid enabled check name
paramsOnly: true
rangeValCopy:
sizeThreshold: 32
gocyclo:
min-complexity: 15
# 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
goimports:
local-prefixes: github.com/golangci/golangci-lint
# 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
gomnd:
settings:
mnd:
# don't include the "operation" and "assign"
checks: argument,case,condition,return
# the list of enabled checks, see https://github.com/tommy-muehle/go-mnd/#checks for description.
checks: argument,case,condition,operation,return,assign
govet:
# report about shadowed variables
check-shadowing: true
# settings per analyzer
settings:
printf:
funcs:
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
- (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:
line-length: 140
# 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
maligned:
# print struct with more effective memory layout or not, false by default
suggest-new: true
misspell:
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
# 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
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:
- bodyclose
- depguard
- dogsled
- dupl
- errcheck
- exhaustive
- exportloopref
- funlen
- gochecknoinits
- goconst
- gocritic
- gocyclo
- gofmt
- goimports
#- golint
- gomnd
- goprintffuncname
- gosec
- gosimple
- megacheck
- govet
- ineffassign
#- interfacer
- lll
- misspell
#- nakedret
- noctx
- nolintlint
- rowserrcheck
#- scopelint
- staticcheck
#- structcheck
- stylecheck
- typecheck
- unconvert
- unparam
disable:
- maligned
- prealloc
disable-all: false
presets:
- bugs
- unused
#- varcheck
- whitespace
fast: false
# 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:
- gomnd
- gocyclo
- errcheck
- dupl
- gosec
# https://github.com/go-critic/go-critic/issues/926
# 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
- linters:
- gocritic
text: "unnecessaryDefer:"
- staticcheck
text: "SA9003:"
# 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'
# Exclude lll issues for long lines with go:generate
- linters:
- lll
source: "^//go:generate "
run:
skip-dirs:
- test/testdata_etc
- internal/cache
- internal/renameio
- internal/robustio
# 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
# 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"
# 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

View File

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2017 - 2021 Russell Magee (xs/xsd/xsnet/xspasswd)
Copyright (c) 2017 - 2019 Russell Magee (xs/xsd/xsnet/xspasswd)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -1,7 +1,6 @@
VERSION := 0.9.13
.PHONY: lint vis clean common client server passwd\
subpkgs install uninstall reinstall scc
VERSION := 0.8.22
.PHONY: lint vis clean common client server passwd subpkgs install uninstall reinstall
## Tag version of binaries with build info wrt.
## GO111MODULE(=on) and vendor/ setup vs. $GOPATH pkg builds
############################################################
@ -11,14 +10,6 @@ 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"
@ -35,20 +26,12 @@ BUILDOPTS :=$(BUILDOPTS)"$(GOBUILDOPTS) -ldflags \"-X main.version=$(VERSION)$(M
#endif
SUBPKGS = logger spinsult xsnet
TOOLS = xs xsd
TOOLS = xspasswd xs xsd
SUBDIRS = $(LIBS) $(TOOLS)
ifeq ($(GOOS),)
GOOS=$(shell go env GOOS)
endif
ifeq ($(GOOS),windows)
INSTPREFIX = /usr
else
INSTPREFIX = /usr/local
endif
all: common client server
all: common client server passwd
clean:
@echo "Make: $(MAKE)"
@ -69,23 +52,29 @@ tools:
common:
$(GO) build .
go install -a .
go build .
go install .
client: common
$(MAKE) BUILDOPTS=$(BUILDOPTS) -C xs
ifeq ($(MSYSTEM),)
ifneq ($(GOOS),windows)
server: common
ifeq ($(GOOS),windows)
echo "Build of xsd server for Windows not yet supported"
else
$(MAKE) BUILDOPTS=$(BUILDOPTS) -C xsd
else
echo "Cross-build of xsd server for Windows not yet supported"
endif
else
server: common
echo "xsd server not (yet) supported on Windows"
endif
scc:
@scc --exclude-dir=bacillus,vendor,garbled --cocomo-project-type=rlabs,3,1.12,1,1
passwd: common
$(MAKE) BUILDOPTS=$(BUILDOPTS) -C xspasswd
vis:
@which go-callvis >/dev/null 2>&1; \
@ -94,27 +83,38 @@ vis:
else \
$(MAKE) -C xs vis;\
$(MAKE) -C xsd vis;\
$(MAKE) -C xspasswd vis; \
fi
lint:
$(MAKE) -C xspasswd lint
$(MAKE) -C xsd lint
$(MAKE) -C xs lint
reinstall: uninstall install
install:
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 -
cp xsd/xsd $(INSTPREFIX)/sbin
ifeq ($(MSYSTEM),)
ifneq ($(GOOS),windows)
cp xsd/xsd xspasswd/xspasswd $(INSTPREFIX)/sbin
else
mv $(INSTPREFIX)/bin/xs $(INSTPREFIX)/bin/_xs
cp xs/mintty_wrapper.sh $(INSTPREFIX)/bin/xs
echo "Cross-build of xsd server for Windows not yet supported"
endif
else
echo "Cross-build of xsd server for Windows not yet supported"
endif
cd $(INSTPREFIX)/bin && ln -s xs xc && cd -
uninstall:
rm -f $(INSTPREFIX)/bin/xs $(INSTPREFIX)/bin/xc
ifndef $(WIN_MSYS)
rm -f $(INSTPREFIX)/sbin/xsd
rm -f $(INSTPREFIX)/bin/xs $(INSTPREFIX)/bin/xc $(INSTPREFIX)/bin/_xs
ifeq ($(MSYSTEM),)
ifneq ($(GOOS),windows)
rm -f $(INSTPREFIX)/sbin/xsd $(INSTPREFIX)/sbin/xspasswd
else
endif
else
endif

6
README.freebsd Normal file
View File

@ -0,0 +1,6 @@
## The Makefile(s) to build require GNU make (gmake).
## Please install and invoke build via:
## $ MAKE=gmake make <targets>
## eg.,
## $ MAKE=gmake make clean all

113
README.md
View File

@ -1,20 +1,12 @@
[![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
--
XS (**X**perimental **S**hell) is a simple alternative to ssh (<5% total SLOCC) written from scratch in Go.
A testbed for candidate PQC (Post-Quantum Cryptography) KEMs (Key-Encapsulation Mechanisms) and symmetric
session encryption algorithms.
xs also features integrated traffic chaffing to obscure interactive session and file copy activity.
Supports encrypted interactive and non-interactive sessions (remote commands), remote file copying and tunnels.
Runs on Linux, FreeBSD, Windows (client only, MSYS) and Android (within Termux). https://gogs.blitter.com/RLabs/xs
It is stable to the point that I use it for day-to-day remote access in place of, and in preference to, ssh.
XS (**X**perimental **S**hell) is a golang implementation of a simple remote shell client and
server, similar in role to ssh, offering encrypted interactive and non-interactive sessions,
file copying and tunnels with traffic obfuscation ('chaffing').
***
**NOTE: Due to the experimental nature of the KEX/KEM algorithms used, and the novelty of the overall codebase, this package SHOULD BE CONSIDERED EXTREMELY EXPERIMENTAL and USED WITH CAUTION. It DEFINITELY SHOULD NOT be used for any sensitive applications. USE AT YOUR OWN RISK. NEITHER WARRANTY NOR CLAIM OF FITNESS FOR PURPOSE IS EXPRESSED OR IMPLIED.**
@ -32,9 +24,9 @@ Currently supported exchanges are:
* The HerraduraKEx key exchange algorithm first released at
[Omar Elejandro Herrera Reyna's HerraduraKEx project](http://github.com/Caume/HerraduraKEx);
* The KYBER IND-CCA-2 secure key encapsulation mechanism, [pq-crystals Kyber](https://pq-crystals.org/kyber/) :: [Yawning/kyber golang implementation](https://gitlab.com/yawning/kyber)
* The NEWHOPE algorithm [newhopecrypto.org](https://www.newhopecrypto.org/) :: [Yawning/newhope golang implementation](https://gitlab.com/yawning/newhope)
* The FrodoKEM algorithm [frodokem.org](https://frodokem.org/) :: Go version by [Eduardo E. S. Riccardi](https://github.com/kuking/go-frodokem)
* The KYBER IND-CCA-2 secure key encapsulation mechanism, [pq-crystals Kyber](https://pq-crystals.org/kyber/) :: [Yawning/kyber golang implementation](https://git.schwanenlied.me/yawning/kyber)
* The NEWHOPE algorithm [newhopecrypto.org](https://www.newhopecrypto.org/) :: [Yawning/go-newhope golang implementation](https://git.schwanenlied.me/yawning/newhope)
Currently supported session algorithms:
@ -44,26 +36,10 @@ 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'**
It has been suggested recently to me that offering multiple cryptographic primitives is considered bad in 2021.
An interesting question. See [this write-up for a discussion](https://paragonie.com/blog/2019/10/against-agility-in-cryptography-protocols).
xs operates via the philosophy that **it is the server admin's prerogitive to configure local policy wrt. allowed cryptographic primitives**. The connection protocol makes no allowance for any sort of 'downgrades' or algo substitution during negotiation; there is no 'fallback' mode or two-way negotiation of what primitives to use, which would open the possibility of downgrade attacks. Unlike `ssh`, the server does not offer to clients a list of supported algorithms; the client can only offer a single configuration to the server, which it simply accepts or rejects without comment to the client.
In all releases prior to v0.9.3, absent a specific whitelist of algs to allow, the server allows 'all' combinations of the above cryptographic primitives to be proposed by clients (but again, **only one** combination is proposed by the client in a single connect attempt). If the admin wishes to restrict the accepted algorithms now or at any future time, they may use the `-aK`, `-aC` and `-aH` options when launching the server to define a whitelist which excludes certain primitives.
As of release v0.9.3, the default when supplying no explicit KEX, cipher or HMAC algorithms to `xsd` results in *no* algs being accepted; so the admin must decide on a specific whitelist of algorithms.
***
### Conn
@ -94,40 +70,41 @@ 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:
### 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.)
* 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://git.schwanenlied.me/yawning/kyber](https://git.schwanenlied.me/yawning/kyber) // golang Kyber KEM
* [https://git.schwanenlied.me/yawning/newhope](https://git.schwanenlied.me/yawning/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
### Get source code
```
$ git clone https://gogs.blitter.com/RLabs/xs
$ go get -u blitter.com/go/xs
$ cd $GOPATH/src/blitter.com/go/xs
$ go build ./... # install all dependent go pkgs
```
### To build
```
$ cd xs
$ make clean && make
$ cd $GOPATH/src/blitter.com/go/xs
$ make clean all
```
### To install, uninstall, re-install (xsd server)
### To install, uninstall, re-install
```
$ sudo make [install | uninstall | reinstall]
```
### To manage service (openrc init)
### To manage service (assuming openrc init)
An example init script (xsd.initrc) is provided. Consult your Linux distribution documentation for proper service/daemon installation. For openrc,
@ -136,23 +113,13 @@ $ sudo cp xsd.initrc /etc/init.d/xsd
$ sudo rc-config add xsd default
```
### To manage service (sysV init)
An example init script (xsd.sysvrc) is provided. Consult your Linux distribution documentation for proper service/daemon installation. For sysV init,
```
$ sudo cp xsd.sysvrc /etc/init.d/xsd
$ sudo sysv-rc-conf --level 2345 xsd on
```
The make system assumes installation in /usr/local/sbin (xsd, xspasswd) and /usr/local/bin (xs/xc symlink).
```
$ sudo rc-config [start | restart | stop] xsd
# .. or sudo /etc/init.d/xsd [start | restart stop]
```
### To set accounts & passwords (DEPRECATED: `-s` is now true by default)
### To set accounts & passwords:
```
$ sudo touch /etc/xs.passwd
@ -187,18 +154,10 @@ 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/.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
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
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
@ -226,19 +185,9 @@ 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.
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:
```
$ xs -x "test -w /dest/path" me@myserver ## If clobbering /dest/path is OK, or
$ xs -x "test -w /dest/path -o ! -e /dest/path" me@myserver ## To prevent clobbering
```
Perhaps in future a more complex handshake will be devised to allow the client to half-close the tarpipe, allowing the server to complete its side of the operation and send back its success or failure code, but the current connection protocol does not allow this. If this is a deal-breaking feature, please contact the maintainer.
### 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;
@ -249,9 +198,3 @@ Example, tunnelling ssh through xs
* [client side, term A] ```$ xs -T=6002:7002 user@server```
* [client side, term B] ```$ ssh user@localhost -p 6002```
### Building for FreeBSD
The Makefile(s) to build require GNU make (gmake).
Please install and invoke build via:
```$ gmake clean all```

35
auth.go
View File

@ -2,7 +2,7 @@ package xs
// Package xs - a secure terminal client/server written from scratch in Go
//
// Copyright (c) 2017-2020 Russell Magee
// Copyright (c) 2017-2019 Russell Magee
// Licensed under the terms of the MIT license (see LICENSE.mit in this
// distribution)
//
@ -23,7 +23,6 @@ import (
"runtime"
"strings"
"blitter.com/go/xs/xsnet"
"github.com/jameskeane/bcrypt"
passlib "gopkg.in/hlandau/passlib.v1"
)
@ -39,8 +38,7 @@ func NewAuthCtx( /*reader func(string) ([]byte, error), userlookup func(string)
}
// --------- System passwd/shadow auth routine(s) --------------
// VerifyPass verifies a password against system standard shadow file
// Verify a password against system standard shadow file
// Note auxilliary fields for expiry policy are *not* inspected.
func VerifyPass(ctx *AuthCtx, user, password string) (bool, error) {
if ctx.reader == nil {
@ -53,7 +51,7 @@ func VerifyPass(ctx *AuthCtx, user, password string) (bool, error) {
} else if runtime.GOOS == "freebsd" {
pwFileName = "/etc/master.passwd"
} else {
return false, errors.New("Unsupported platform")
pwFileName = "unsupported"
}
pwFileData, e := ctx.reader(pwFileName)
if e != nil {
@ -155,7 +153,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/.config/xs/.xs_id and are requested
// Auth tokens are stored in each user's $HOME/.xs_id and are requested
// via the -g option.
// The function also check system /etc/passwd to cross-check the user
// actually exists.
@ -173,9 +171,9 @@ func AuthUserByToken(ctx *AuthCtx, username string, connhostname string, auth st
return false
}
b, e := ctx.reader(fmt.Sprintf("%s/%s", u.HomeDir, xsnet.XS_ID_AUTHTOKFILE))
b, e := ctx.reader(fmt.Sprintf("%s/.xs_id", u.HomeDir))
if e != nil {
log.Printf("INFO: Cannot read %s/%s\n", u.HomeDir, xsnet.XS_ID_AUTHTOKFILE)
log.Printf("INFO: Cannot read %s/.xs_id\n", u.HomeDir)
return false
}
@ -183,27 +181,19 @@ func AuthUserByToken(ctx *AuthCtx, username string, connhostname string, auth st
r.Comma = ':'
r.Comment = '#'
r.FieldsPerRecord = 3 // connhost:username:authtoken
r.FieldsPerRecord = 2 // connhost:authtoken
for {
record, err := r.Read()
if err == io.EOF {
return false
}
if len(record) < 3 ||
len(record[0]) < 1 ||
len(record[1]) < 1 ||
len(record[2]) < 1 {
return false
}
record[0] = strings.TrimSpace(record[0])
record[1] = strings.TrimSpace(record[1])
record[2] = strings.TrimSpace(record[2])
//fmt.Println("auth:", auth, "record:",
// strings.Join([]string{record[0], record[1], record[2]}, ":"))
// strings.Join([]string{record[0], record[1]}, ":"))
if (connhostname == record[0]) &&
username == record[1] &&
(auth == strings.Join([]string{record[0], record[1], record[2]}, ":")) {
(auth == strings.Join([]string{record[0], record[1]}, ":")) {
valid = true
break
}
@ -216,20 +206,21 @@ func AuthUserByToken(ctx *AuthCtx, username string, connhostname string, auth st
}
func GetTool(tool string) (ret string) {
ret = "/bin/" + tool
ret = "/bin/"+tool
_, err := os.Stat(ret)
if err == nil {
return ret
}
ret = "/usr/bin/" + tool
ret = "/usr/bin/"+tool
_, err = os.Stat(ret)
if err == nil {
return ret
}
ret = "/usr/local/bin/" + tool
ret = "/usr/local/bin/"+tool
_, err = os.Stat(ret)
if err == nil {
return ret
}
return ""
}

View File

@ -19,7 +19,7 @@ var (
joebloggs:$6$F.0IXOrb0w0VJHG1$3O4PYyng7F3hlh42mbroEdQZvslybY5etPPiLMQJ1xosjABY.Q4xqAfyIfe03Du61ZjGQIt3nL0j12P9k1fsK/:18310:0:99999:7:::
disableduser:!:18310::::::`
dummyAuthTokenFile = "hostA:johndoe:abcdefg\nhostB:imposter:wxyz\n"
dummyAuthTokenFile = "hostA:abcdefg\nhostB:wxyz\n"
dummyXsPasswdFile = `#username:salt:authCookie
bobdobbs:$2a$12$9vqGkFqikspe/2dTARqu1O:$2a$12$9vqGkFqikspe/2dTARqu1OuDKCQ/RYWsnaFjmi.HtmECRkxcZ.kBK
@ -155,7 +155,7 @@ func TestAuthUserByTokenSucceedsWithMatchedUserAndToken(t *testing.T) {
ctx := newMockAuthCtx(_mock_ioutil_ReadFile, _mock_user_Lookup)
userlookup_arg_u = "johndoe"
readfile_arg_f = "/.xs_id"
stat := AuthUserByToken(ctx, userlookup_arg_u, "hostA", "hostA:johndoe:abcdefg")
stat := AuthUserByToken(ctx, userlookup_arg_u, "hostA", "hostA:abcdefg")
if !stat {
t.Fatal("failed with valid user and token")
}

View File

@ -1,13 +1,10 @@
#!/bin/bash
#
## bacillus (https://gogs.blitter.com/Russtopia/bacillus) build/test CI script
export GOPATH="${HOME}/go"
export PATH=/usr/local/bin:/usr/bin:/usr/lib/ccache/bin:/bin:$GOPATH/bin
unset GO111MODULE
#export GOPROXY="direct"
#!# GOCACHE will be phased out in v1.12. [github.com/golang/go/issues/26809]
#!export GOCACHE="${HOME}/.cache/go-build"
export GO111MODULE=on
# GOCACHE will be phased out in v1.12. [github.com/golang/go/issues/26809]
export GOCACHE="${HOME}/.cache/go-build"
echo "workdir: ${BACILLUS_WORKDIR}"
mkdir -p "${BACILLUS_ARTFDIR}"
@ -25,36 +22,17 @@ echo "Building most recent push on branch $branch"
git checkout "$branch"
ls
go mod init
go mod tidy
############
stage "Build"
############
echo "Invoking 'make clean' ..."
make clean
echo "Invoking 'make all' ..."
make all
############
stage "Lint"
############
make lint
############
stage "UnitTests"
############
go test -v .
############
stage "Test(Authtoken)"
############
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" >~/.config/xs/.xs_id
echo "Clearing test user $USER ~/.xs_id file ..."
rm -f ~/.xs_id
echo "Setting dummy authtoken in ~/.xs_id ..."
echo "localhost:asdfasdfasdf" >~/.xs_id
echo "Performing remote command on @localhost via authtoken login ..."
tokentest=$(timeout 10 xs -x "echo -n FOO" @localhost)
if [ "${tokentest}" != "FOO" ]; then
@ -65,12 +43,8 @@ else
unset tokentest
fi
############
stage "Test(xc S->C)"
############
stage "Test(S->C)"
echo "Testing secure copy from server -> client ..."
./xc_testfiles.sh
tmpdir=$$
mkdir -p /tmp/$tmpdir
cd /tmp/$tmpdir
@ -79,8 +53,8 @@ echo -n "Integrity check on copied files (sha1sum) ..."
sha1sum $(find cptest -type f | sort) >sc.sha1sum
diff sc.sha1sum ${BACILLUS_WORKDIR}/build/xs/cptest.sha1sum
stat=$?
cd -
cd -
rm -rf /tmp/$tmpdir
if [ $stat -eq "0" ]; then
echo "OK."
@ -89,26 +63,18 @@ else
exit $stat
fi
############
stage "Test(xc C->S)"
############
stage "Test(C->S)"
echo "TODO ..."
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"
############
echo -n "Creating tarfile ..."
tar -cz --exclude=.git --exclude=cptest -f ${BACILLUS_ARTFDIR}/xs.tgz .
############
stage "Cleanup"
############
rm -rf cptest
rm -f ~/.xs_id
echo
echo "--Done--"

View File

@ -1,6 +1,6 @@
// Package xs - a secure terminal client/server written from scratch in Go
//
// Copyright (c) 2017-2020 Russell Magee
// Copyright (c) 2017-2019 Russell Magee
// Licensed under the terms of the MIT license (see LICENSE.mit in this
// distribution)
//

View File

@ -1,6 +1,6 @@
6010a446cdcf8c1203c2d08998cc69a8c88f77d5 cptest/file16KB
f086ea96f3718efd78e6791178f967585acb3701 cptest/file1KB
6061e16c3d3840712d0b8e5268c49b5c2f8137ac cptest/file32KB
490af32035bbe737480f06439a02d91a171ac407 cptest/file6B
e4a8a4ad9678b7265a28c5f0cb5b078e1049ea23 cptest/subdir/file32MB
82b50550c7d0e0d6ac18d9d0796b6814c3e038bf cptest/subdir/file64MB
306637b5c621892078ebadd9454a78820a000598 cptest/file16KB
1a118dfff291352eb4aec02c34f4f957669460fc cptest/file1KB
f474d5da45890b7cb5b0ae84c8ade5abcb3b4474 cptest/file32KB
03939175ceac92b9c6464d037a0243e22563c423 cptest/file6B
da67c7698b25d94c0cc20284ba9d4008cdee201b cptest/subdir/file32MB
9da9888265371375b48c224b94a0b3132b7ddc41 cptest/subdir/file64MB

28
go.mod Normal file
View File

@ -0,0 +1,28 @@
module blitter.com/go/xs
go 1.12
require (
blitter.com/go/cryptmt v1.0.2
blitter.com/go/goutmp v1.0.5
blitter.com/go/herradurakex v1.0.0
blitter.com/go/kyber v0.0.0-20200130200857-6f2021cb88d9
blitter.com/go/mtwist v1.0.1 // indirect
blitter.com/go/newhope v0.0.0-20200130200750-192fc08a8aae
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da
github.com/jameskeane/bcrypt v0.0.0-20120420032655-c3cd44c1e20f
github.com/klauspost/cpuid v1.2.3 // indirect
github.com/klauspost/reedsolomon v1.9.4 // indirect
github.com/kr/pty v1.1.8
github.com/mattn/go-isatty v0.0.12
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.3.0 // indirect
github.com/xtaci/kcp-go v5.4.20+incompatible
golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5
golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f
gopkg.in/hlandau/easymetric.v1 v1.0.0 // indirect
gopkg.in/hlandau/measurable.v1 v1.0.1 // indirect
gopkg.in/hlandau/passlib.v1 v1.0.10
)

70
go.sum Normal file
View File

@ -0,0 +1,70 @@
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.3 h1:4VPU9COXG36W5454pcV65FieVaaw64mdhvjuTVOuWTM=
blitter.com/go/goutmp v1.0.3/go.mod h1:gtlbjC8xGzMk/Cf0BpnVltSa3awOqJ+B5WAxVptTMxk=
blitter.com/go/goutmp v1.0.4 h1:19Y4xgvdEbGESS0vuyWlPyZgjYNYxHkDfmbObKU5J6A=
blitter.com/go/goutmp v1.0.4/go.mod h1:gtlbjC8xGzMk/Cf0BpnVltSa3awOqJ+B5WAxVptTMxk=
blitter.com/go/goutmp v1.0.5 h1:isP6bxSs1O06Oy7wB8u4y5SgLr22txfjg/gjG4qn0Og=
blitter.com/go/goutmp v1.0.5/go.mod h1:gtlbjC8xGzMk/Cf0BpnVltSa3awOqJ+B5WAxVptTMxk=
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/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=
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/creack/pty v1.1.7 h1:6pwm8kMQKCmgUg0ZHTm5+/YvRK0s3THD/28+T6/kk4A=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
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 v1.2.3 h1:CCtW0xUnWGVINKvE/WWOYKdsPV6mawAtvQuSl8guwQs=
github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/reedsolomon v1.9.4 h1:FB9jDBGqUNyhUg4Gszz384ulFqVSc61Pdap+HRPgnSo=
github.com/klauspost/reedsolomon v1.9.4/go.mod h1:CwCi+NUr9pqSVktrkN+Ondf06rkhYZ/pcNv7fu+8Un4=
github.com/kr/pty v1.1.8 h1:AkaSdXYQOWeaO3neb8EM634ahkXXe3jYbVh/F9lq+GI=
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
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/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
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.3.0 h1:i7c6Za/IlgBvnGxYpfD7L3TGuaS+v6oGcgq+J9/ecEA=
github.com/tjfoc/gmsm v1.3.0/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w=
github.com/xtaci/kcp-go v1.0.1 h1:SEZn2Iick6ualQpV8yY9cXdDTgbfBHPIwJl7UvxfxLQ=
github.com/xtaci/kcp-go v5.4.20+incompatible h1:TN1uey3Raw0sTz0Fg8GkfM0uH3YwzhnZWQ1bABv5xAg=
github.com/xtaci/kcp-go v5.4.20+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE=
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-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5 h1:Q7tZBpemrlsc2I7IyODzhtallWRSm4Q0d09pL6XbQtU=
golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
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-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f h1:gWF768j/LaZugp8dyS4UwsslYCYz9XgFxvlgsn0n9H8=
golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
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.10 h1:q5xh9ZHp907XTjVw8/EqG03//fnlITnIYQmv4Gn7TpE=
gopkg.in/hlandau/passlib.v1 v1.0.10/go.mod h1:wxGAv2CtQHlzWY8NJp+p045yl4WHyX7v2T6XbOcmqjM=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@ -2,14 +2,8 @@
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

@ -2,7 +2,7 @@ package xs
// Package xs - a secure terminal client/server written from scratch in Go
//
// Copyright (c) 2017-2020 Russell Magee
// Copyright (c) 2017-2019 Russell Magee
// Licensed under the terms of the MIT license (see LICENSE.mit in this
// distribution)
//
@ -96,7 +96,7 @@ func (h Session) AuthCookie(reallyShow bool) []byte {
return []byte("**REDACTED**")
}
// SetAuthCookie stores the authcookie (essentially the password) used to
// SetAuthCookie stores the authcookie (essential the password) used to
// authenticate the Session.
func (h *Session) SetAuthCookie(a []byte) {
h.authCookie = a

View File

@ -1,18 +1,11 @@
.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,7 +5,6 @@ package xs
import (
"errors"
"io"
"os"
unix "golang.org/x/sys/unix"
)
@ -31,8 +30,7 @@ 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(f *os.File) (*State, error) {
fd := f.Fd()
func MakeRaw(fd uintptr) (*State, error) {
termios, err := unix.IoctlGetTermios(int(fd), ioctlReadTermios)
if err != nil {
return nil, err
@ -58,8 +56,8 @@ func MakeRaw(f *os.File) (*State, error) {
// GetState returns the current state of a terminal which may be useful to
// restore the terminal after a signal.
func GetState(f *os.File) (*State, error) {
termios, err := unix.IoctlGetTermios(int(f.Fd()), ioctlReadTermios)
func GetState(fd uintptr) (*State, error) {
termios, err := unix.IoctlGetTermios(int(fd), ioctlReadTermios)
if err != nil {
return nil, err
}
@ -69,9 +67,9 @@ func GetState(f *os.File) (*State, error) {
// Restore restores the terminal connected to the given file descriptor to a
// previous state.
func Restore(f *os.File, state *State) error {
func Restore(fd uintptr, state *State) error {
if state != nil {
return unix.IoctlSetTermios(int(f.Fd()), ioctlWriteTermios, &state.termios)
return unix.IoctlSetTermios(int(fd), ioctlWriteTermios, &state.termios)
} else {
return errors.New("nil State")
}
@ -80,8 +78,7 @@ func Restore(f *os.File, 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(f *os.File) ([]byte, error) {
fd := f.Fd()
func ReadPassword(fd uintptr) ([]byte, error) {
termios, err := unix.IoctlGetTermios(int(fd), ioctlReadTermios)
if err != nil {
return nil, err

View File

@ -1,4 +1,3 @@
//go:build windows
// +build windows
// Note the terminal manipulation functions herein are mostly stubs. They
@ -16,12 +15,10 @@
package xs
import (
"bufio"
"fmt"
"log"
"os"
"io"
"os/exec"
"os/signal"
"golang.org/x/sys/windows"
)
type State struct {
@ -30,84 +27,67 @@ 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(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)
}
}()
// ----------------------------------------
func MakeRaw(fd int) (*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()
return &State{}, nil
}
// GetState returns the current state of a terminal which may be useful to
// restore the terminal after a signal.
func GetState(f *os.File) (*State, error) {
func GetState(fd int) (*State, error) {
return &State{}, nil
}
// Restore restores the terminal connected to the given file descriptor to a
// previous state.
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
}
func Restore(fd int, state *State) error {
cmd := exec.Command("stty", "echo cooked")
cmd.Run()
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(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 {
return nil, err
}
func ReadPassword(fd int) ([]byte, error) {
return readPasswordLine(passwordReader(fd))
}
//fmt.Printf("Enter password:")
scanner := bufio.NewScanner(os.Stdin)
scanner.Scan()
err = scanner.Err()
if err != nil {
return nil, err
// 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
}
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
if err == io.EOF && len(ret) > 0 {
return ret, nil
}
return ret, err
}
}
return
}

View File

@ -1,14 +0,0 @@
#!/bin/bash
## setup.sh - create some files for xc copy testing
dir=cptest
mkdir -p ${dir}/subdir
dd bs=1024 count=16 if=/dev/urandom of=${dir}/file16KB
dd bs=1024 count=16 if=/dev/urandom of=${dir}/file1KB
dd bs=1024 count=16 if=/dev/urandom of=${dir}/file32KB
dd bs=1024 count=16 if=/dev/urandom of=${dir}/file6B
dd bs=1048576 count=32 if=/dev/urandom of=${dir}/subdir/file32MB
dd bs=1048576 count=64 if=/dev/urandom of=${dir}/subdir/file64MB
sha1sum $(find ${dir} -type f | sort) >${dir}.sha1sum

View File

@ -1,17 +1,11 @@
.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

37
xs/mintty_wrapper.sh Executable file
View File

@ -0,0 +1,37 @@
#!/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
}
if [ ${1}x == "-hx" ]; then
./hkexsh -h
else
stty -echo raw icrnl
./hkexsh $@
fi

View File

@ -1,4 +1,3 @@
//go:build linux || freebsd
// +build linux freebsd
package main
@ -31,7 +30,7 @@ func handleTermResizes(conn *xsnet.Conn) {
log.Println(err)
}
termSzPacket := fmt.Sprintf("%d %d", rows, cols)
conn.WritePacket([]byte(termSzPacket), xsnet.CSOTermSize) //nolint:errcheck
conn.WritePacket([]byte(termSzPacket), xsnet.CSOTermSize) // nolint: errcheck,gosec
}
}()
ch <- syscall.SIGWINCH // Initial resize.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

View File

@ -10,7 +10,7 @@ digraph gocallvis {
pad="0.0";
nodesep="0.35";
node [shape="box" style="filled,rounded" fillcolor="honeydew" fontname="Verdana" penwidth="1.0" margin="0.05,0.0"];
node [shape="ellipse" style="filled" fillcolor="honeydew" fontname="Verdana" penwidth="1.0" margin="0.05,0.0"];
edge [minlen="2"]
subgraph "cluster_focus" {
@ -20,135 +20,129 @@ labelloc="t";
labeljust="c";
fontsize="18";
"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.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" ]
"blitter.com/go/xs/xs.reqTunnel" [ fillcolor="lightblue" label="reqTunnel" penwidth="0.5" ]
"blitter.com/go/xs/xs.launchTuns" [ label="launchTuns" penwidth="0.5" fillcolor="lightblue" ]
"blitter.com/go/xs/xs.main$3" [ fillcolor="lightblue" label="main$3" style="dotted,filled" ]
"blitter.com/go/xs/xs.doCopyMode" [ fillcolor="lightblue" label="doCopyMode" penwidth="0.5" ]
"blitter.com/go/xs/xs.copyBuffer" [ fillcolor="lightblue" label="copyBuffer" penwidth="0.5" ]
"blitter.com/go/xs/xs.copyBuffer$1" [ label="copyBuffer$1" style="dotted,filled" fillcolor="lightblue" ]
"blitter.com/go/xs/xs.copyBuffer$2" [ label="copyBuffer$2" style="dotted,filled" fillcolor="lightblue" ]
"blitter.com/go/xs/xs.copyBuffer$3" [ fillcolor="lightblue" label="copyBuffer$3" style="dotted,filled" ]
"blitter.com/go/xs/xs.Copy" [ label="Copy" penwidth="1.5" fillcolor="lightblue" ]
"blitter.com/go/xs/xs.doShellMode$1" [ fillcolor="lightblue" label="shellRemoteToStdin" style="dotted,filled" ]
"blitter.com/go/xs/xs.doShellMode$1$1" [ fillcolor="lightblue" label="doShellMode$1$1" style="dotted,filled" ]
"blitter.com/go/xs/xs.exitWithStatus" [ fillcolor="lightblue" label="exitWithStatus" penwidth="0.5" ]
"blitter.com/go/xs/xs.doShellMode" [ label="doShellMode" penwidth="0.5" fillcolor="lightblue" ]
"blitter.com/go/xs/xs.handleTermResizes$1" [ fillcolor="lightblue" label="handleTermResizes$1" style="dotted,filled" ]
"blitter.com/go/xs/xs.GetSize" [ fillcolor="lightblue" label="GetSize" penwidth="1.5" ]
"blitter.com/go/xs/xs.handleTermResizes" [ label="handleTermResizes" penwidth="0.5" fillcolor="lightblue" ]
"blitter.com/go/xs/xs.doShellMode$2$1" [ fillcolor="lightblue" label="doShellMode$2$1" style="dotted,filled" ]
"blitter.com/go/xs/xs.doShellMode$2" [ fillcolor="lightblue" label="shellStdinToRemote" style="dotted,filled" ]
"blitter.com/go/xs/xs.sendSessionParams" [ fillcolor="lightblue" label="sendSessionParams" penwidth="0.5" ]
"blitter.com/go/xs/xs.main" [ label="main" penwidth="0.5" fillcolor="lightblue" ]
"blitter.com/go/xs/xs.parseNonSwitchArgs" [ fillcolor="lightblue" label="parseNonSwitchArgs" penwidth="0.5" ]
"blitter.com/go/xs/xs.main$1" [ fillcolor="lightblue" label="deferRestore" style="dotted,filled" ]
"blitter.com/go/xs/xs.main$2" [ fillcolor="lightblue" label="deferCloseChaff" style="dotted,filled" ]
"blitter.com/go/xs/xs.rejectUserMsg" [ fillcolor="lightblue" label="rejectUserMsg" penwidth="0.5" ]
"blitter.com/go/xs/xs.usageShell" [ fillcolor="lightblue" label="usageShell" penwidth="0.5" ]
"blitter.com/go/xs/xs.usageCp" [ label="usageCp" penwidth="0.5" fillcolor="lightblue" ]
subgraph "cluster_blitter.com/go/xs" {
style="filled";
penwidth="0.8";
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";
fontname="bold";
label="[xs]";
URL="/?f=blitter.com/go/xs";
"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" ]
"blitter.com/go/xs.Restore" [ fillcolor="moccasin" label="Restore" penwidth="1.5" ]
"blitter.com/go/xs.MakeRaw" [ penwidth="1.5" fillcolor="moccasin" label="MakeRaw" ]
"blitter.com/go/xs.ReadPassword" [ fillcolor="moccasin" label="ReadPassword" penwidth="1.5" ]
"blitter.com/go/xs.NewSession" [ fillcolor="moccasin" label="NewSession" penwidth="1.5" ]
subgraph "cluster_*blitter.com/go/xs.Session" {
fillcolor="wheat2";
label="(*Session)";
tooltip="type: *blitter.com/go/xs.Session";
penwidth="0.5";
fontsize="15";
fontcolor="#222222";
labelloc="b";
style="rounded,filled";
"(*blitter.com/go/xs.Session).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" {
penwidth="0.5";
fontsize="15";
fontsize="15";
fontcolor="#222222";
labelloc="b";
style="rounded,filled";
fillcolor="wheat2";
label="(*Session)";
tooltip="type: *blitter.com/go/xs.Session";
penwidth="0.5";
"(*blitter.com/go/xs.Session).SetStatus" [ fillcolor="moccasin" label="SetStatus" penwidth="1.5" ]
}
subgraph "cluster_blitter.com/go/xs.Session" {
style="rounded,filled";
fillcolor="wheat2";
label="(Session)";
tooltip="type: blitter.com/go/xs.Session";
penwidth="0.5";
fontsize="15";
fontcolor="#222222";
labelloc="b";
"(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" [ 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" ]
"(blitter.com/go/xs.Session).Cmd" [ fillcolor="moccasin" label="Cmd" penwidth="1.5" ]
"(blitter.com/go/xs.Session).Status" [ penwidth="1.5" fillcolor="moccasin" label="Status" ]
"(blitter.com/go/xs.Session).Op" [ fillcolor="moccasin" label="Op" penwidth="1.5" ]
"(blitter.com/go/xs.Session).Who" [ fillcolor="moccasin" label="Who" penwidth="1.5" ]
"(blitter.com/go/xs.Session).ConnHost" [ fillcolor="moccasin" label="ConnHost" penwidth="1.5" ]
"(blitter.com/go/xs.Session).TermType" [ fillcolor="moccasin" label="TermType" penwidth="1.5" ]
"(blitter.com/go/xs.Session).AuthCookie" [ label="AuthCookie" penwidth="1.5" fillcolor="moccasin" ]
}
}
subgraph "cluster_blitter.com/go/xs/logger" {
penwidth="0.8";
fontsize="16";
fontname="Tahoma bold";
fontsize="16";
URL="/?f=blitter.com/go/xs/logger";
tooltip="package: blitter.com/go/xs/logger";
penwidth="0.8";
style="filled";
fillcolor="lightyellow";
fontname="bold";
rank="sink";
label="logger";
label="[logger]";
tooltip="package: blitter.com/go/xs/logger";
"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" [ fillcolor="moccasin" label="LogDebug" penwidth="1.5" ]
"blitter.com/go/xs/logger.New" [ fillcolor="moccasin" label="New" penwidth="1.5" ]
}
subgraph "cluster_blitter.com/go/xs/spinsult" {
penwidth="0.8";
style="filled";
fontname="Tahoma bold";
rank="sink";
fontsize="16";
fillcolor="lightyellow";
label="spinsult";
fillcolor="lightyellow";
fontname="bold";
label="[spinsult]";
URL="/?f=blitter.com/go/xs/spinsult";
tooltip="package: blitter.com/go/xs/spinsult";
penwidth="0.8";
fontsize="16";
style="filled";
rank="sink";
"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" ]
"blitter.com/go/xs/spinsult.GetSentence" [ fillcolor="moccasin" label="GetSentence" penwidth="1.5" ]
}
subgraph "cluster_blitter.com/go/xs/xsnet" {
fontname="Tahoma bold";
penwidth="0.8";
fillcolor="lightyellow";
fontname="bold";
rank="sink";
label="xsnet";
tooltip="package: blitter.com/go/xs/xsnet";
penwidth="0.8";
URL="/?f=blitter.com/go/xs/xsnet";
fontsize="16";
style="filled";
fillcolor="lightyellow";
URL="/?f=blitter.com/go/xs/xsnet";
label="[xsnet]";
tooltip="package: 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: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" ]
"blitter.com/go/xs/xsnet.Init" [ fillcolor="moccasin" label="Init" penwidth="1.5" ]
"blitter.com/go/xs/xsnet.Dial" [ label="Dial" penwidth="1.5" fillcolor="moccasin" ]
subgraph "cluster_*blitter.com/go/xs/xsnet.Conn" {
fontcolor="#222222";
@ -160,29 +154,29 @@ tooltip="type: *blitter.com/go/xs/xsnet.Conn";
penwidth="0.5";
fontsize="15";
"(*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" ]
"(*blitter.com/go/xs/xsnet.Conn).WritePacket" [ penwidth="1.5" fillcolor="moccasin" label="WritePacket" ]
"(*blitter.com/go/xs/xsnet.Conn).SetStatus" [ fillcolor="moccasin" label="SetStatus" penwidth="1.5" ]
"(*blitter.com/go/xs/xsnet.Conn).Close" [ penwidth="1.5" fillcolor="moccasin" label="Close" ]
"(*blitter.com/go/xs/xsnet.Conn).SetupChaff" [ penwidth="1.5" fillcolor="moccasin" label="SetupChaff" ]
"(*blitter.com/go/xs/xsnet.Conn).EnableChaff" [ fillcolor="moccasin" label="EnableChaff" penwidth="1.5" ]
"(*blitter.com/go/xs/xsnet.Conn).DisableChaff" [ fillcolor="moccasin" label="DisableChaff" penwidth="1.5" ]
"(*blitter.com/go/xs/xsnet.Conn).ShutdownChaff" [ fillcolor="moccasin" label="ShutdownChaff" penwidth="1.5" ]
}
subgraph "cluster_blitter.com/go/xs/xsnet.Conn" {
label="(Conn)";
labelloc="b";
style="rounded,filled";
fillcolor="wheat2";
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).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" ]
"(blitter.com/go/xs/xsnet.Conn).Read" [ label="Read" penwidth="1.5" fillcolor="moccasin" ]
"(blitter.com/go/xs/xsnet.Conn).GetStatus" [ fillcolor="moccasin" label="GetStatus" penwidth="1.5" ]
"(blitter.com/go/xs/xsnet.Conn).Write" [ label="Write" penwidth="1.5" fillcolor="moccasin" ]
}
@ -190,41 +184,41 @@ fillcolor="wheat2";
subgraph "cluster_compress/flate" {
fontsize="16";
style="filled";
fillcolor="lightyellow";
label="flate";
fillcolor="#E0FFE1";
label="[compress/flate]";
URL="/?f=compress/flate";
penwidth="0.8";
fontname="Tahoma bold";
style="filled";
fontname="bold";
rank="sink";
tooltip="package: compress/flate";
subgraph "cluster_compress/flate.CorruptInputError" {
fontcolor="#222222";
labelloc="b";
style="rounded,filled";
fillcolor="wheat2";
label="(CorruptInputError)";
tooltip="type: compress/flate.CorruptInputError";
penwidth="0.5";
fontsize="15";
"(compress/flate.CorruptInputError).Error" [ penwidth="1.5" tooltip="(compress/flate.CorruptInputError).Error | defined in inflate.go:35" fillcolor="moccasin" label="Error" ]
}
subgraph "cluster_compress/flate.InternalError" {
tooltip="type: compress/flate.InternalError";
tooltip="type: compress/flate.CorruptInputError";
penwidth="0.5";
fontsize="15";
fontcolor="#222222";
labelloc="b";
style="rounded,filled";
fillcolor="wheat2";
label="(InternalError)";
fillcolor="#c2e3c2";
label="(CorruptInputError)";
"(compress/flate.InternalError).Error" [ tooltip="(compress/flate.InternalError).Error | defined in inflate.go:42" fillcolor="moccasin" label="Error" penwidth="1.5" ]
"(compress/flate.CorruptInputError).Error" [ penwidth="1.5" fillcolor="#adedad" label="Error" ]
}
subgraph "cluster_compress/flate.InternalError" {
fontsize="15";
fontcolor="#222222";
labelloc="b";
style="rounded,filled";
fillcolor="#c2e3c2";
label="(InternalError)";
tooltip="type: compress/flate.InternalError";
penwidth="0.5";
"(compress/flate.InternalError).Error" [ fillcolor="#adedad" label="Error" penwidth="1.5" ]
}
@ -233,224 +227,210 @@ label="(InternalError)";
subgraph "cluster_context" {
fontsize="16";
style="filled";
label="context";
penwidth="0.8";
fillcolor="lightyellow";
fontname="Tahoma bold";
fontname="bold";
rank="sink";
label="[context]";
URL="/?f=context";
tooltip="package: context";
fillcolor="#E0FFE1";
subgraph "cluster_context.deadlineExceededError" {
style="rounded,filled";
fillcolor="wheat2";
fontsize="15";
fontcolor="#222222";
labelloc="b";
style="rounded,filled";
fillcolor="#c2e3c2";
label="(deadlineExceededError)";
tooltip="type: context.deadlineExceededError";
penwidth="0.5";
fontsize="15";
fontcolor="#222222";
labelloc="b";
"(context.deadlineExceededError).Error" [ fillcolor="moccasin" label="Error" penwidth="1.5" tooltip="(context.deadlineExceededError).Error | defined in context.go:165" ]
"(context.deadlineExceededError).Error" [ fillcolor="#adedad" label="Error" penwidth="1.5" ]
}
}
subgraph "cluster_crypto/aes" {
penwidth="0.8";
style="filled";
fontname="Tahoma bold";
style="filled";
fillcolor="#E0FFE1";
rank="sink";
label="aes";
label="[crypto/aes]";
penwidth="0.8";
fontsize="16";
fontname="bold";
URL="/?f=crypto/aes";
tooltip="package: crypto/aes";
fontsize="16";
fillcolor="lightyellow";
subgraph "cluster_crypto/aes.KeySizeError" {
style="rounded,filled";
fillcolor="wheat2";
fontcolor="#222222";
labelloc="b";
style="rounded,filled";
fillcolor="#c2e3c2";
label="(KeySizeError)";
tooltip="type: crypto/aes.KeySizeError";
penwidth="0.5";
fontsize="15";
fontcolor="#222222";
labelloc="b";
"(crypto/aes.KeySizeError).Error" [ fillcolor="moccasin" label="Error" penwidth="1.5" tooltip="(crypto/aes.KeySizeError).Error | defined in cipher.go:24" ]
"(crypto/aes.KeySizeError).Error" [ fillcolor="#adedad" label="Error" penwidth="1.5" ]
}
}
subgraph "cluster_crypto/tls" {
penwidth="0.8";
tooltip="package: crypto/tls";
fontsize="16";
fontname="Tahoma bold";
URL="/?f=crypto/tls";
tooltip="package: crypto/tls";
style="filled";
fillcolor="lightyellow";
fontname="bold";
rank="sink";
label="tls";
label="[crypto/tls]";
URL="/?f=crypto/tls";
penwidth="0.8";
fillcolor="#E0FFE1";
subgraph "cluster_*crypto/tls.permanentError" {
label="(*permanentError)";
tooltip="type: *crypto/tls.permanentError";
penwidth="0.5";
fontsize="15";
fontcolor="#222222";
subgraph "cluster_crypto/tls.RecordHeaderError" {
fontcolor="#222222";
labelloc="b";
style="rounded,filled";
fillcolor="wheat2";
"(*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" {
fillcolor="wheat2";
fillcolor="#c2e3c2";
label="(RecordHeaderError)";
tooltip="type: crypto/tls.RecordHeaderError";
penwidth="0.5";
fontsize="15";
fontcolor="#222222";
labelloc="b";
style="rounded,filled";
"(crypto/tls.RecordHeaderError).Error" [ tooltip="(crypto/tls.RecordHeaderError).Error | defined in conn.go:571" fillcolor="moccasin" label="Error" penwidth="1.5" ]
"(crypto/tls.RecordHeaderError).Error" [ fillcolor="#adedad" label="Error" penwidth="1.5" ]
}
subgraph "cluster_crypto/tls.alert" {
fontcolor="#222222";
penwidth="0.5";
fontsize="15";
fontcolor="#222222";
labelloc="b";
style="rounded,filled";
fillcolor="wheat2";
fillcolor="#c2e3c2";
label="(alert)";
tooltip="type: crypto/tls.alert";
penwidth="0.5";
fontsize="15";
"(crypto/tls.alert).Error" [ fillcolor="moccasin" label="Error" penwidth="1.5" tooltip="(crypto/tls.alert).Error | defined in alert.go:97" ]
"(crypto/tls.alert).Error" [ fillcolor="#adedad" label="Error" penwidth="1.5" ]
}
}
subgraph "cluster_crypto/x509" {
URL="/?f=crypto/x509";
style="filled";
fillcolor="lightyellow";
fontname="Tahoma bold";
rank="sink";
label="x509";
penwidth="0.8";
tooltip="package: crypto/x509";
penwidth="0.8";
fontsize="16";
style="filled";
fillcolor="#E0FFE1";
fontname="bold";
rank="sink";
label="[crypto/x509]";
URL="/?f=crypto/x509";
subgraph "cluster_crypto/x509.CertificateInvalidError" {
label="(CertificateInvalidError)";
tooltip="type: crypto/x509.CertificateInvalidError";
penwidth="0.5";
penwidth="0.5";
fontsize="15";
fontcolor="#222222";
labelloc="b";
style="rounded,filled";
fillcolor="wheat2";
fillcolor="#c2e3c2";
label="(CertificateInvalidError)";
tooltip="type: crypto/x509.CertificateInvalidError";
"(crypto/x509.CertificateInvalidError).Error" [ fillcolor="moccasin" label="Error" penwidth="1.5" tooltip="(crypto/x509.CertificateInvalidError).Error | defined in verify.go:67" ]
"(crypto/x509.CertificateInvalidError).Error" [ fillcolor="#adedad" label="Error" penwidth="1.5" ]
}
subgraph "cluster_crypto/x509.HostnameError" {
tooltip="type: crypto/x509.HostnameError";
labelloc="b";
style="rounded,filled";
fillcolor="#c2e3c2";
label="(HostnameError)";
tooltip="type: crypto/x509.HostnameError";
penwidth="0.5";
fontsize="15";
fontcolor="#222222";
labelloc="b";
style="rounded,filled";
fillcolor="wheat2";
label="(HostnameError)";
"(crypto/x509.HostnameError).Error" [ fillcolor="moccasin" label="Error" penwidth="1.5" tooltip="(crypto/x509.HostnameError).Error | defined in verify.go:98" ]
"(crypto/x509.HostnameError).Error" [ fillcolor="#adedad" label="Error" penwidth="1.5" ]
}
subgraph "cluster_crypto/x509.SystemRootsError" {
tooltip="type: crypto/x509.SystemRootsError";
penwidth="0.5";
fontsize="15";
fontsize="15";
fontcolor="#222222";
labelloc="b";
style="rounded,filled";
fillcolor="wheat2";
fillcolor="#c2e3c2";
label="(SystemRootsError)";
tooltip="type: crypto/x509.SystemRootsError";
penwidth="0.5";
"(crypto/x509.SystemRootsError).Error" [ label="Error" penwidth="1.5" tooltip="(crypto/x509.SystemRootsError).Error | defined in verify.go:159" fillcolor="moccasin" ]
"(crypto/x509.SystemRootsError).Error" [ fillcolor="#adedad" label="Error" penwidth="1.5" ]
}
subgraph "cluster_crypto/x509.UnhandledCriticalExtension" {
label="(UnhandledCriticalExtension)";
style="rounded,filled";
fillcolor="#c2e3c2";
label="(UnhandledCriticalExtension)";
tooltip="type: crypto/x509.UnhandledCriticalExtension";
penwidth="0.5";
fontsize="15";
fontcolor="#222222";
labelloc="b";
style="rounded,filled";
fillcolor="wheat2";
"(crypto/x509.UnhandledCriticalExtension).Error" [ label="Error" penwidth="1.5" tooltip="(crypto/x509.UnhandledCriticalExtension).Error | defined in x509.go:893" fillcolor="moccasin" ]
"(crypto/x509.UnhandledCriticalExtension).Error" [ fillcolor="#adedad" label="Error" penwidth="1.5" ]
}
subgraph "cluster_crypto/x509.UnknownAuthorityError" {
fontsize="15";
fontcolor="#222222";
labelloc="b";
style="rounded,filled";
fillcolor="wheat2";
style="rounded,filled";
fillcolor="#c2e3c2";
label="(UnknownAuthorityError)";
tooltip="type: crypto/x509.UnknownAuthorityError";
penwidth="0.5";
fontsize="15";
fontcolor="#222222";
labelloc="b";
"(crypto/x509.UnknownAuthorityError).Error" [ tooltip="(crypto/x509.UnknownAuthorityError).Error | defined in verify.go:138" fillcolor="moccasin" label="Error" penwidth="1.5" ]
"(crypto/x509.UnknownAuthorityError).Error" [ fillcolor="#adedad" label="Error" penwidth="1.5" ]
}
}
subgraph "cluster_github.com/mattn/go-isatty" {
penwidth="0.8";
fontsize="16";
fontname="Tahoma bold";
rank="sink";
style="filled";
fontname="bold";
penwidth="0.8";
fillcolor="lightyellow";
label="isatty";
rank="sink";
label="[isatty]";
URL="/?f=github.com/mattn/go-isatty";
tooltip="package: github.com/mattn/go-isatty";
fontsize="16";
style="filled";
"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" ]
"github.com/mattn/go-isatty.IsTerminal" [ label="IsTerminal" penwidth="1.5" fillcolor="moccasin" ]
}
subgraph "cluster_github.com/pkg/errors" {
fontname="Tahoma bold";
label="errors";
style="filled";
fillcolor="lightyellow";
URL="/?f=github.com/pkg/errors";
tooltip="package: github.com/pkg/errors";
rank="sink";
label="[errors]";
tooltip="package: github.com/pkg/errors";
penwidth="0.8";
fontsize="16";
style="filled";
fillcolor="lightyellow";
fontname="bold";
subgraph "cluster_*github.com/pkg/errors.fundamental" {
@ -463,160 +443,107 @@ labelloc="b";
style="rounded,filled";
fillcolor="wheat2";
"(*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" ]
"(*github.com/pkg/errors.fundamental).Error" [ penwidth="1.5" fillcolor="moccasin" label="Error" ]
}
}
subgraph "cluster_math/rand" {
style="filled";
fillcolor="lightyellow";
penwidth="0.8";
fillcolor="#E0FFE1";
rank="sink";
URL="/?f=math/rand";
tooltip="package: math/rand";
penwidth="0.8";
fontsize="16";
fontname="Tahoma bold";
label="rand";
"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";
fontname="bold";
label="[math/rand]";
tooltip="package: math/rand";
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" ]
"math/rand.Intn" [ fillcolor="#adedad" label="Intn" penwidth="1.5" ]
}
}
}
"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/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$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.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" -> "(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.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" ]
"blitter.com/go/xs/xs.reqTunnel" -> "blitter.com/go/xs/logger.LogDebug" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.reqTunnel" -> "(*blitter.com/go/xs/xsnet.Conn).WritePacket" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.launchTuns" -> "blitter.com/go/xs/xs.reqTunnel" [ ]
"blitter.com/go/xs/xs.main$3" -> "math/rand.Intn" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.main$3" -> "(*blitter.com/go/xs/xsnet.Conn).WritePacket" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.doCopyMode" -> "(blitter.com/go/xs.Session).Cmd" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.doCopyMode" -> "(*blitter.com/go/xs/xsnet.Conn).WritePacket" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.doCopyMode" -> "(blitter.com/go/xs/xsnet.Conn).Read" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.doCopyMode" -> "(*blitter.com/go/xs/xsnet.Conn).SetStatus" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.doCopyMode" -> "(blitter.com/go/xs/xsnet.Conn).GetStatus" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.copyBuffer" -> "(blitter.com/go/xs/xsnet.Conn).Write" [ color="saddlebrown" style="dashed" ]
"blitter.com/go/xs/xs.copyBuffer" -> "blitter.com/go/xs/xs.copyBuffer$1" [ style="dashed" ]
"blitter.com/go/xs/xs.copyBuffer" -> "blitter.com/go/xs/xs.copyBuffer$2" [ style="dashed" ]
"blitter.com/go/xs/xs.copyBuffer" -> "blitter.com/go/xs/xs.copyBuffer$3" [ style="dashed" ]
"blitter.com/go/xs/xs.Copy" -> "blitter.com/go/xs/xs.copyBuffer" [ ]
"blitter.com/go/xs/xs.doShellMode$1" -> "blitter.com/go/xs/xs.doShellMode$1$1" [ arrowhead="normalnoneodiamond" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "blitter.com/go/xs.Restore" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "(blitter.com/go/xs/xsnet.Conn).GetStatus" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "(*blitter.com/go/xs.Session).SetStatus" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "(blitter.com/go/xs.Session).Status" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "blitter.com/go/xs/xs.exitWithStatus" [ ]
"blitter.com/go/xs/xs.doShellMode$1" -> "(crypto/x509.CertificateInvalidError).Error" [ style="dashed" color="saddlebrown" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "(crypto/aes.KeySizeError).Error" [ style="dashed" color="saddlebrown" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "(crypto/x509.HostnameError).Error" [ color="saddlebrown" style="dashed" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "(crypto/x509.UnhandledCriticalExtension).Error" [ style="dashed" color="saddlebrown" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "(context.deadlineExceededError).Error" [ style="dashed" color="saddlebrown" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "(compress/flate.CorruptInputError).Error" [ style="dashed" color="saddlebrown" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "(crypto/tls.RecordHeaderError).Error" [ style="dashed" color="saddlebrown" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "(crypto/x509.UnknownAuthorityError).Error" [ style="dashed" color="saddlebrown" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "(crypto/x509.SystemRootsError).Error" [ style="dashed" color="saddlebrown" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "(compress/flate.InternalError).Error" [ style="dashed" color="saddlebrown" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "(crypto/tls.alert).Error" [ style="dashed" color="saddlebrown" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "(*github.com/pkg/errors.fundamental).Error" [ style="dashed" color="saddlebrown" ]
"blitter.com/go/xs/xs.doShellMode" -> "blitter.com/go/xs/xs.doShellMode$1" [ arrowhead="normalnoneodot" ]
"blitter.com/go/xs/xs.handleTermResizes$1" -> "blitter.com/go/xs/xs.GetSize" [ ]
"blitter.com/go/xs/xs.handleTermResizes$1" -> "(*blitter.com/go/xs/xsnet.Conn).WritePacket" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.handleTermResizes" -> "blitter.com/go/xs/xs.handleTermResizes$1" [ arrowhead="normalnoneodot" ]
"blitter.com/go/xs/xs.doShellMode" -> "blitter.com/go/xs/xs.handleTermResizes" [ ]
"blitter.com/go/xs/xs.doShellMode$2$1" -> "blitter.com/go/xs/xs.Copy" [ ]
"blitter.com/go/xs/xs.doShellMode$2" -> "blitter.com/go/xs/xs.doShellMode$2$1" [ ]
"blitter.com/go/xs/xs.doShellMode$2" -> "blitter.com/go/xs.Restore" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.doShellMode$2" -> "blitter.com/go/xs/xs.exitWithStatus" [ ]
"blitter.com/go/xs/xs.doShellMode" -> "blitter.com/go/xs/xs.doShellMode$2" [ arrowhead="normalnoneodot" ]
"blitter.com/go/xs/xs.sendSessionParams" -> "(blitter.com/go/xs.Session).Op" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.sendSessionParams" -> "(blitter.com/go/xs.Session).Who" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.sendSessionParams" -> "(blitter.com/go/xs.Session).ConnHost" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.sendSessionParams" -> "(blitter.com/go/xs.Session).TermType" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.sendSessionParams" -> "(blitter.com/go/xs.Session).Cmd" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.sendSessionParams" -> "(blitter.com/go/xs.Session).AuthCookie" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.sendSessionParams" -> "(blitter.com/go/xs/xsnet.Conn).Write" [ style="dashed" color="saddlebrown" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.parseNonSwitchArgs" [ ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.main$1" [ arrowhead="normalnoneodot" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.exitWithStatus" [ ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/logger.New" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xsnet.Init" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xsnet.Dial" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.main" -> "(*blitter.com/go/xs/xsnet.Conn).Close" [ arrowhead="normalnoneodiamond" color="saddlebrown" ]
"blitter.com/go/xs/xs.main" -> "github.com/mattn/go-isatty.IsTerminal" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs.MakeRaw" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.main$2" -> "blitter.com/go/xs.Restore" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.main$2" [ arrowhead="normalnoneodiamond" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs.ReadPassword" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs.NewSession" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.sendSessionParams" [ ]
"blitter.com/go/xs/xs.main" -> "(blitter.com/go/xs/xsnet.Conn).Read" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.main" -> "(*blitter.com/go/xs.Session).SetStatus" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.rejectUserMsg" -> "blitter.com/go/xs/spinsult.GetSentence" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.rejectUserMsg" [ ]
"blitter.com/go/xs/xs.main" -> "(*blitter.com/go/xs/xsnet.Conn).SetupChaff" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.main" -> "(*blitter.com/go/xs/xsnet.Conn).EnableChaff" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.main" -> "(*blitter.com/go/xs/xsnet.Conn).DisableChaff" [ arrowhead="normalnoneodiamond" color="saddlebrown" ]
"blitter.com/go/xs/xs.main" -> "(*blitter.com/go/xs/xsnet.Conn).ShutdownChaff" [ arrowhead="normalnoneodiamond" color="saddlebrown" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.main$3" [ arrowhead="normalnoneodot" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.launchTuns" [ ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.doShellMode" [ ]
"blitter.com/go/xs/xs.main" -> "(blitter.com/go/xs.Session).Status" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.doCopyMode" [ ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs.Restore" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.usageShell" [ style="dashed" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.usageCp" [ style="dashed" ]
}

459
xs/xs.go
View File

@ -1,6 +1,7 @@
// xs client
//
// Copyright (c) 2017-2020 Russell Magee
// Copyright (c) 2017-2019 Russell Magee
// Licensed under the terms of the MIT license (see LICENSE.mit in this
// distribution)
//
@ -14,8 +15,10 @@ import (
"flag"
"fmt"
"io"
"io/ioutil"
"log"
"math/rand"
"net"
"os"
"os/exec"
"os/user"
@ -29,14 +32,13 @@ import (
"time"
"net/http"
_ "net/http/pprof" //nolint:gosec
_ "net/http/pprof"
xs "blitter.com/go/xs"
"blitter.com/go/xs/logger"
"blitter.com/go/xs/spinsult"
"blitter.com/go/xs/xsnet"
"github.com/mattn/go-isatty"
//isatty "github.com/mattn/go-isatty"
isatty "github.com/mattn/go-isatty"
)
var (
@ -57,39 +59,27 @@ 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" +
"@@@@@@^ ~^ @ @@ @ @ @ I ~^@@@@@@\r\n" +
"@@@@@ ~ ~~ ~I @@@@@\r\n" +
"@@@@' ' _,w@< @@@@ .\r\n" +
"@@@@' ' _,w@< @@@@\r\n" +
"@@@@ @@@@@@@@w___,w@@@@@@@@ @ @@@\r\n" +
"@@@@ @@@@@@@@@@@@@@@@@@@@@@ I @@@ Bob\r\n" +
"@@@@ @@@@@@@@@@@@@@@@@@@@@@ I @@@\r\n" +
"@@@@ @@@@@@@@@@@@@@@@@@@@*@[ i @@@\r\n" +
"@@@@ @@@@@@@@@@@@@@@@@@@@[][ | ]@@@ bOb\r\n" +
"@@@@ @@@@@@@@@@@@@@@@@@@@[][ | ]@@@\r\n" +
"@@@@ ~_,,_ ~@@@@@@@~ ____~ @ @@@\r\n" +
"@@@@ _~ , , `@@@~ _ _`@ ]L J@@@ o\r\n" +
"@@@@ _~ , , `@@@~ _ _`@ ]L J@@@\r\n" +
"@@@@ , @@w@ww+ @@@ww``,,@w@ ][ @@@@\r\n" +
"@@@@, @@@@www@@@ @@@@@@@ww@@@@@[ @@@@ BOB\r\n" +
"@@@@, @@@@www@@@ @@@@@@@ww@@@@@[ @@@@\r\n" +
"@@@@@_|| @@@@@@P' @@P@@@@@@@@@@@[|c@@@@\r\n" +
"@@@@@@w| '@@P~ P]@@@-~, ~Y@@^'],@@@@@@ . o\r\n" +
"@@@@@@w| '@@P~ P]@@@-~, ~Y@@^'],@@@@@@\r\n" +
"@@@@@@@[ _ _J@@Tk ]]@@@@@@\r\n" +
"@@@@@@@@,@ @@, c,,,,,,,y ,w@@[ ,@@@@@@@\r\n" +
"@@@@@@@@@ i @w ====--_@@@@@ @@@@@@@@ o .\r\n" +
"@@@@@@@@@ i @w ====--_@@@@@ @@@@@@@@\r\n" +
"@@@@@@@@@@`,P~ _ ~^^^^Y@@@@@ @@@@@@@@@\r\n" +
"@@@@^^=^@@^ ^' ,ww,w@@@@@ _@@@@@@@@@@ B o B\r\n" +
"@@@@^^=^@@^ ^' ,ww,w@@@@@ _@@@@@@@@@@\r\n" +
"@@@_xJ~ ~ , @@@@@@@P~_@@@@@@@@@@@@\r\n" +
"@@ @, ,@@@,_____ _,J@@@@@@@@@@@@@\r\n" +
"@@L `' ,@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\r\n" +
@ -103,14 +93,6 @@ 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.
@ -158,6 +140,11 @@ 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.
@ -169,7 +156,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 {
@ -190,8 +177,8 @@ func copyBuffer(dst io.Writer, src io.Reader, buf []byte) (written int64, err er
// A repeat of 4 keys (conveniently 'dead' chars for most
// interactive shells; here CTRL-]) shall introduce
// some special responses or actions on the client side.
if seqPos < 4 { //nolint:gomnd
if buf[0] == DeadCharPrefix {
if seqPos < 4 {
if buf[0] == 0x1d {
seqPos++
}
} else {
@ -238,7 +225,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 ||
@ -254,7 +241,7 @@ func GetSize() (cols, rows int, err error) {
return
}
func buildCmdRemoteToLocal(copyQuiet bool, copyLimitBPS uint, destPath string) (captureStderr bool, cmd string, args []string) {
func buildCmdRemoteToLocal(copyQuiet bool, copyLimitBPS uint, destPath, files 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.
@ -269,10 +256,10 @@ func buildCmdRemoteToLocal(copyQuiet bool, copyLimitBPS uint, destPath string) (
cmd = xs.GetTool("tar")
args = []string{"-xz", "-C", destPath}
} else {
} else {
// TODO: Query remote side for total file/dir size
bandwidthInBytesPerSec := " -L " + fmt.Sprintf("%d ", copyLimitBPS)
displayOpts := " -pre " //nolint:goconst
displayOpts := " -pre "
cmd = xs.GetTool("bash")
args = []string{"-c", "pv " + displayOpts + bandwidthInBytesPerSec + "| tar -xz -C " + destPath}
}
@ -320,9 +307,9 @@ func buildCmdLocalToRemote(copyQuiet bool, copyLimitBPS uint, files string) (cap
} else {
captureStderr = copyQuiet
bandwidthInBytesPerSec := " -L " + fmt.Sprintf("%d", copyLimitBPS)
displayOpts := " -pre " //nolint:goconst,nolintlint
displayOpts := " -pre "
cmd = xs.GetTool("bash")
args = []string{"-c", xs.GetTool("tar") + " -cz -f /dev/stdout "}
args = []string{"-c", xs.GetTool("tar")+" -cz -f /dev/stdout "}
files = strings.TrimSpace(files)
// Awesome fact: tar actually can take multiple -C args, and
// changes to the dest dir *as it sees each one*. This enables
@ -370,9 +357,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
@ -405,7 +392,7 @@ func doCopyMode(conn *xsnet.Conn, remoteDest bool, files string, copyQuiet bool,
if err != nil {
fmt.Println("cmd exited immediately. Cannot get cmd.Wait().ExitStatus()")
err = errors.New("cmd exited prematurely")
exitStatus = uint32(CmdExitedEarly)
exitStatus = uint32(2)
} else {
if err = c.Wait(); err != nil {
if exiterr, ok := err.(*exec.ExitError); ok {
@ -425,7 +412,7 @@ func doCopyMode(conn *xsnet.Conn, remoteDest bool, files string, copyQuiet bool,
}
// send CSOExitStatus to inform remote (server) end cp is done
log.Println("Sending local exitStatus:", exitStatus)
r := make([]byte, 4) //nolint:gomnd
r := make([]byte, 4)
binary.BigEndian.PutUint32(r, exitStatus)
_, we := conn.WritePacket(r, xsnet.CSOExitStatus)
if we != nil {
@ -433,7 +420,7 @@ func doCopyMode(conn *xsnet.Conn, remoteDest bool, files string, copyQuiet bool,
}
// Do a final read for remote's exit status
s := make([]byte, 4) //nolint:gomnd
s := make([]byte, 4)
_, remErr := conn.Read(s)
if remErr != io.EOF &&
!strings.Contains(remErr.Error(), "use of closed network") &&
@ -454,9 +441,10 @@ 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)
_, cmdName, cmdArgs := buildCmdRemoteToLocal(copyQuiet, copyLimitBPS, destPath, strings.TrimSpace(files))
c := exec.Command(cmdName, cmdArgs...) // #nosec
var c *exec.Cmd
c = exec.Command(cmdName, cmdArgs...) // #nosec
c.Stdin = conn
c.Stdout = os.Stdout
c.Stderr = os.Stderr
@ -493,8 +481,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\"/
@ -548,8 +536,6 @@ 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)
@ -571,15 +557,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])
fmt.Fprintf(os.Stderr, "%s [opts] [user]@server\n", os.Args[0])
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
flag.PrintDefaults()
}
func usageCp() {
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])
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
flag.PrintDefaults()
}
@ -595,18 +581,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)
fmt.Fprintln(os.Stderr, "reqTunnel:", e) // nolint: errcheck
}
if e := binary.Write(&bTmp, binary.BigEndian, rp); e != nil {
fmt.Fprintln(os.Stderr, "reqTunnel:", e)
fmt.Fprintln(os.Stderr, "reqTunnel:", e) // nolint: errcheck
}
_ = logger.LogDebug(fmt.Sprintln("[Client sending CSOTunSetup]"))
_ = logger.LogDebug(fmt.Sprintln("[Client sending CSOTunSetup]")) // nolint: gosec
if n, e := hc.WritePacket(bTmp.Bytes(), xsnet.CSOTunSetup); e != nil || n != len(bTmp.Bytes()) {
fmt.Fprintln(os.Stderr, "reqTunnel:", e)
fmt.Fprintln(os.Stderr, "reqTunnel:", e) // nolint: errcheck
}
}
@ -619,7 +605,7 @@ func parseNonSwitchArgs(a []string) (user, host, path string, isDest bool, other
if strings.Contains(arg, ":") || strings.Contains(arg, "@") {
fancyArg := strings.Split(flag.Arg(i), "@")
var fancyHostPath []string
if len(fancyArg) < 2 { //nolint:gomnd
if len(fancyArg) < 2 {
//TODO: no user specified, use current
fancyUser = "[default:getUser]"
fancyHostPath = strings.Split(fancyArg[0], ":")
@ -645,8 +631,8 @@ func parseNonSwitchArgs(a []string) (user, host, path string, isDest bool, other
return fancyUser, fancyHost, fancyPath, isDest, otherArgs
}
func launchTuns(conn *xsnet.Conn /*remoteHost string,*/, tuns string) {
/*remAddrs, _ := net.LookupHost(remoteHost)*/ //nolint:gocritic,nolintlint
func launchTuns(conn *xsnet.Conn, remoteHost string, tuns string) {
remAddrs, _ := net.LookupHost(remoteHost) // nolint: gosec
if tuns == "" {
return
@ -655,8 +641,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)
reqTunnel(conn, lPort /*remAddrs[0],*/, rPort)
_, _ = fmt.Sscanf(tunItem, "%d:%d", &lPort, &rPort) // nolint: gosec
reqTunnel(conn, lPort, remAddrs[0], rPort)
}
}
@ -691,86 +677,52 @@ func sendSessionParams(conn io.Writer /* *xsnet.Conn*/, rec *xs.Session) (e erro
}
// TODO: reduce gocyclo
func main() { //nolint: funlen, gocyclo
var (
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
func main() {
var vopt bool
var gopt bool //login via password, asking server to generate authToken
var dbg bool
var shellMode bool // if true act as shell, else file copier
var cipherAlg string //cipher alg
var hmacAlg string //hmac alg
var kexAlg string //KEX/KEM alg
var server string
var port uint
var cmdStr string
var tunSpecStr string // lport1:rport1[,lport2:rport2,...]
authCookie string
chaffEnabled bool
chaffFreqMin uint
chaffFreqMax uint
chaffBytesMax uint
var copySrc []byte
var copyDst string
var copyQuiet bool
var copyLimitBPS uint
op []byte
)
var authCookie string
var chaffEnabled bool
var chaffFreqMin uint
var chaffFreqMax uint
var chaffBytesMax uint
// === Common (xs and xc) option parsing
var op []byte
isInteractive := false
flag.BoolVar(&vopt, "v", false, "show version")
flag.BoolVar(&dbg, "d", false, "debug logging")
flag.StringVar(&cipherAlg, "c", "C_AES_256", "session `cipher`"+`
C_AES_256
C_TWOFISH_128
C_BLOWFISH_64
C_CRYPTMT1
C_HOPSCOTCH
C_CHACHA20_12`)
flag.StringVar(&hmacAlg, "m", "H_SHA256", "session `HMAC`"+`
H_SHA256
H_SHA512
H_WHIRLPOOL`)
flag.StringVar(&kexAlg, "k", "KEX_HERRADURA512", "KEx `alg`"+`
KEX_HERRADURA256
KEX_HERRADURA512
KEX_HERRADURA1024
KEX_HERRADURA2048
KEX_KYBER512
KEX_KYBER768
KEX_KYBER1024
KEX_NEWHOPE
KEX_NEWHOPE_SIMPLE
KEX_FRODOKEM_1344AES
KEX_FRODOKEM_1344SHAKE
KEX_FRODOKEM_976AES
KEX_FRODOKEM_976SHAKE`)
flag.StringVar(&kcpMode, "K", "unused", "KCP `alg`, one of [KCP_NONE | KCP_AES | KCP_BLOWFISH | KCP_CAST5 | KCP_SM4 | KCP_SALSA20 | KCP_SIMPLEXOR | KCP_TEA | KCP_3DES | KCP_TWOFISH | KCP_XTEA] to use KCP (github.com/xtaci/kcp-go) reliable UDP instead of TCP") //nolint:lll
flag.UintVar(&port, "p", 2000, "``port") //nolint:gomnd,lll
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.StringVar(&cipherAlg, "c", "C_AES_256", "session `cipher` [C_AES_256 | C_TWOFISH_128 | C_BLOWFISH_64 | C_CRYPTMT1 | C_CHACHA20_12]")
flag.StringVar(&hmacAlg, "m", "H_SHA256", "session `HMAC` [H_SHA256 | H_SHA512]")
flag.StringVar(&kexAlg, "k", "KEX_HERRADURA512", "KEx `alg` [KEX_HERRADURA{256/512/1024/2048} | KEX_KYBER{512/768/1024} | KEX_NEWHOPE | KEX_NEWHOPE_SIMPLE]")
flag.StringVar(&kcpMode, "K", "unused", "KCP `alg`, one of [KCP_NONE | KCP_AES | KCP_BLOWFISH | KCP_CAST5 | KCP_SM4 | KCP_SALSA20 | KCP_SIMPLEXOR | KCP_TEA | KCP_3DES | KCP_TWOFISH | KCP_XTEA] to use KCP (github.com/xtaci/kcp-go) reliable UDP instead of TCP")
flag.UintVar(&port, "p", 2000, "``port")
//flag.StringVar(&authCookie, "a", "", "auth cookie")
flag.BoolVar(&chaffEnabled, "e", true, "enable chaff pkts")
flag.UintVar(&chaffFreqMin, "f", 100, "chaff pkt freq min `msecs`") //nolint:gomnd
flag.UintVar(&chaffFreqMax, "F", 5000, "chaff pkt freq max `msecs`") //nolint:gomnd
flag.UintVar(&chaffBytesMax, "B", 64, "chaff pkt size max `bytes`") //nolint:gomnd
flag.UintVar(&chaffFreqMin, "f", 100, "chaff pkt freq min `msecs`")
flag.UintVar(&chaffFreqMax, "F", 5000, "chaff pkt freq max `msecs`")
flag.UintVar(&chaffBytesMax, "B", 64, "chaff pkt size max `bytes`")
flag.StringVar(&cpuprofile, "cpuprofile", "", "write cpu profile to <`file`>")
flag.StringVar(&memprofile, "memprofile", "", "write memory profile to <`file`>")
// === xc vs. xs option parsing
// Find out what program we are (shell or copier)
myPath := strings.Split(os.Args[0], string(os.PathSeparator))
if myPath[len(myPath)-1] != "xc" &&
myPath[len(myPath)-1] != "_xc" &&
myPath[len(myPath)-1] != "xc.exe" &&
myPath[len(myPath)-1] != "_xc.exe" {
if myPath[len(myPath)-1] != "xc" && myPath[len(myPath)-1] != "xc.exe" {
// xs accepts a command (-x) but not
// a srcpath (-r) or dstpath (-t)
flag.StringVar(&cmdStr, "x", "", "run <`command`> (if not specified, run interactive shell)")
@ -780,7 +732,7 @@ func main() { //nolint: funlen, gocyclo
flag.Usage = usageShell
} else {
flag.BoolVar(&copyQuiet, "q", false, "do not output progress bar during copy")
flag.UintVar(&copyLimitBPS, "L", 8589934592, "copy max rate in bytes per sec") //nolint:gomnd
flag.UintVar(&copyLimitBPS, "L", 8589934592, "copy max rate in bytes per sec")
flag.Usage = usageCp
}
flag.Parse()
@ -790,8 +742,6 @@ func main() { //nolint: funlen, gocyclo
exitWithStatus(0)
}
// === Profiling instrumentation
if cpuprofile != "" {
f, err := os.Create(cpuprofile)
if err != nil {
@ -800,25 +750,23 @@ func main() { //nolint: funlen, gocyclo
defer f.Close()
fmt.Println("StartCPUProfile()")
if err := pprof.StartCPUProfile(f); err != nil {
log.Fatal("could not start CPU profile: ", err) //nolint:gocritic
log.Fatal("could not start CPU profile: ", err)
} else {
defer pprof.StopCPUProfile()
}
go func() { http.ListenAndServe("localhost:6060", nil) }() //nolint:errcheck,gosec
go func() { http.ListenAndServe("localhost:6060", nil) }()
}
// === User, host, port and path args for file operations, if applicable
remoteUser, remoteHost, tmpPath, pathIsDest, otherArgs :=
parseNonSwitchArgs(flag.Args())
//nolint:gocritic,nolintlint // fmt.Println("otherArgs:", otherArgs)
//fmt.Println("otherArgs:", otherArgs)
// Set defaults if user doesn't specify user, path or port
var uname string
if remoteUser == "" {
u, _ := user.Current()
uname = localUserName(u)
u, _ := user.Current() // nolint: gosec
uname = u.Username
} else {
uname = remoteUser
}
@ -830,8 +778,6 @@ func main() { //nolint: funlen, gocyclo
tmpPath = "."
}
// === Copy mode arg and copy src/dest setup
var fileArgs string
if !shellMode /*&& tmpPath != ""*/ {
// -if pathIsSrc && len(otherArgs) > 1 ERROR
@ -863,60 +809,54 @@ func main() { //nolint: funlen, gocyclo
}
}
// === Do some final option consistency checks
// Do some more option consistency checks
//nolint:gocritic,nolintlint // fmt.Println("server finally is:", server)
//fmt.Println("server finally is:", server)
if flag.NFlag() == 0 && server == "" {
flag.Usage()
exitWithStatus(0)
}
if cmdStr != "" && (len(copySrc) != 0 || copyDst != "") {
if len(cmdStr) != 0 && (len(copySrc) != 0 || len(copyDst) != 0) {
log.Fatal("incompatible options -- either cmd (-x) or copy ops but not both")
}
//-------------------------------------------------------------------
// Here we have parsed all options and can now carry out
// either the shell session or copy operation.
_ = shellMode
Log, _ = logger.New(logger.LOG_USER|logger.LOG_DEBUG|logger.LOG_NOTICE|logger.LOG_ERR, "xs")
Log, _ = logger.New(logger.LOG_USER|logger.LOG_DEBUG|logger.LOG_NOTICE|logger.LOG_ERR, "xs") // nolint: errcheck,gosec
xsnet.Init(dbg, "xs", logger.LOG_USER|logger.LOG_DEBUG|logger.LOG_NOTICE|logger.LOG_ERR)
if dbg {
log.SetOutput(Log)
} else {
log.SetOutput(io.Discard)
log.SetOutput(ioutil.Discard)
}
// === Auth token fetch for login
if !gopt {
// See if we can log in via an auth token
u, _ := user.Current()
ab, aerr := os.ReadFile(fmt.Sprintf("%s/%s", u.HomeDir, xsnet.XS_ID_AUTHTOKFILE))
u, _ := user.Current() // nolint: gosec
ab, aerr := ioutil.ReadFile(fmt.Sprintf("%s/.xs_id", u.HomeDir))
if aerr == nil {
for _, line := range strings.Split(string(ab), "\n") {
line += "\n"
idx := strings.Index(line, remoteHost+":"+uname)
if idx >= 0 {
line = line[idx:]
entries := strings.SplitN(line, "\n", -1)
authCookie = strings.TrimSpace(entries[0])
// Security scrub
line = ""
break
}
}
if authCookie == "" {
idx := strings.Index(string(ab), remoteHost)
if idx >= 0 {
ab = ab[idx:]
entries := strings.SplitN(string(ab), "\n", -1)
authCookie = strings.TrimSpace(entries[0])
// Security scrub
ab = nil
runtime.GC()
} else {
_, _ = fmt.Fprintln(os.Stderr, "[no authtoken, use -g to request one from server]")
}
} else {
log.Printf("[cannot read %s/%s]\n", u.HomeDir, xsnet.XS_ID_AUTHTOKFILE)
log.Printf("[cannot read %s/.xs_id]\n", u.HomeDir)
}
}
runtime.GC()
// === Enforce some sane min/max vals on chaff flags
if chaffFreqMin < 2 { //nolint:gomnd
// Enforce some sane min/max vals on chaff flags
if chaffFreqMin < 2 {
chaffFreqMin = 2
}
if chaffFreqMax == 0 {
@ -926,17 +866,15 @@ func main() { //nolint: funlen, gocyclo
chaffBytesMax = 64
}
// === 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]")
fmt.Fprintln(os.Stderr, "[requesting authtoken from server]") // nolint: errcheck
op = []byte{'A'}
chaffFreqMin = 2
chaffFreqMax = 10
} else if cmdStr == "" {
} else if len(cmdStr) == 0 {
op = []byte{'s'}
isInteractive = true
} else {
@ -957,86 +895,38 @@ func main() { //nolint: funlen, gocyclo
// client->server file copy
// src file list is in copySrc
op = []byte{'D'}
//nolint:gocritic,nolintlint // fmt.Println("client->server copy:", string(copySrc), "->", copyDst)
//fmt.Println("client->server copy:", string(copySrc), "->", copyDst)
cmdStr = copyDst
} else {
// server->client file copy
// remote src file(s) in copyDsr
op = []byte{'S'}
//nolint:gocritic,nolintlint // fmt.Println("server->client copy:", string(copySrc), "->", copyDst)
//fmt.Println("server->client copy:", string(copySrc), "->", copyDst)
cmdStr = string(copySrc)
}
}
// === TCP / KCP Dial setup
proto := "tcp"
if kcpMode != "unused" {
proto = "kcp"
}
remodExtArg := ""
if remodRequested {
remodExtArg = "OPT_REMOD"
}
// Pass opt to Dial() via extensions arg
conn, err := xsnet.Dial(proto, server, cipherAlg, hmacAlg, kexAlg, kcpMode, remodExtArg)
conn, err := xsnet.Dial(proto, server, cipherAlg, hmacAlg, kexAlg, kcpMode)
if err != nil {
fmt.Println(err)
exitWithStatus(XSNetDialFailed)
exitWithStatus(3)
}
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
// 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
// From this point on, conn is a secure encrypted channel
if shellMode {
if isatty.IsTerminal(os.Stdin.Fd()) ||
isatty.IsCygwinTerminal(os.Stdin.Fd()) {
oldState, err = xs.MakeRaw(os.Stdin)
if isatty.IsTerminal(os.Stdin.Fd()) {
oldState, err = xs.MakeRaw(os.Stdin.Fd())
if err != nil {
panic(err)
}
@ -1048,83 +938,91 @@ func main() { //nolint: funlen, gocyclo
}
}
// === Session param and TERM setup
// Start login timeout here and disconnect if user/pass phase stalls
loginTimeout := time.AfterFunc(30*time.Second, func() {
fmt.Printf(" .. [login timeout]")
})
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)
}
_ = loginTimeout.Stop()
// Security scrub
runtime.GC()
// Set up session params and send over to server
rec := xs.NewSession(op, []byte(uname), []byte(remoteHost), []byte(os.Getenv("TERM")), []byte(cmdStr), []byte(authCookie), 0)
sendErr := sendSessionParams(&conn, rec)
if sendErr != nil {
restoreTermState(oldState)
rec.SetStatus(ServerRejectedSecureProposal)
fmt.Fprintln(os.Stderr, "Error: server rejected secure proposal params or login timed out")
rec.SetStatus(254)
fmt.Fprintln(os.Stderr, "Error: server rejected secure proposal params or login timed out") // nolint: errcheck
exitWithStatus(int(rec.Status()))
//nolint:gocritic,nolintlint // log.Fatal(sendErr)
//log.Fatal(sendErr)
}
// Security scrub
authCookie = "" //nolint: ineffassign
//Security scrub
authCookie = "" // nolint: ineffassign
runtime.GC()
// === 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")
rec.SetStatus(ErrReadingAuthReply)
fmt.Fprintln(os.Stderr, "Error reading auth reply") // nolint: errcheck
rec.SetStatus(255)
} else if authReply[0] == 0 {
// === .. or if auth failed
fmt.Fprintln(os.Stderr, rejectUserMsg())
rec.SetStatus(GeneralProtocolErr)
fmt.Fprintln(os.Stderr, rejectUserMsg()) // nolint: errcheck
rec.SetStatus(255)
} else {
// === Set up chaffing to server
// Set up chaffing to server
conn.SetupChaff(chaffFreqMin, chaffFreqMax, chaffBytesMax) // enable client->server chaffing
if chaffEnabled {
// #gv:s/label=\"main\$2\"/label=\"deferCloseChaff\"/
// TODO:.gv:main:2:deferCloseChaff
conn.StartupChaff() // goroutine, returns immediately
conn.EnableChaff() // goroutine, returns immediately
defer conn.DisableChaff()
defer conn.ShutdownChaff()
}
// === (goroutine) Start keepAliveWorker for tunnels
// Keepalive for any tunnels that may exist
// #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) //nolint:gosec,gomnd
time.Sleep(time.Duration(2000-rand.Intn(200)) * time.Millisecond)
// 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
conn.WritePacket([]byte{0, 0}, xsnet.CSOTunKeepAlive) // nolint: errcheck,gosec
}
}
go keepAliveWorker()
// [1]}
//[1]}
// === Session entry (shellMode or copyMode)
if shellMode {
// === Set up connection keepalive to server
conn.StartupKeepAlive() // goroutine, returns immediately
defer conn.ShutdownKeepAlive()
// === (shell) launch tunnels
launchTuns(&conn /*remoteHost,*/, tunSpecStr)
launchTuns(&conn, remoteHost, tunSpecStr)
doShellMode(isInteractive, &conn, oldState, rec)
} else {
// === (.. or file copy)
s, _ := doCopyMode(&conn, pathIsDest, fileArgs, copyQuiet, copyLimitBPS, rec)
} else { // copyMode
s, _ := doCopyMode(&conn, pathIsDest, fileArgs, copyQuiet, copyLimitBPS, rec) // nolint: errcheck,gosec
rec.SetStatus(s)
}
if rec.Status() != 0 {
restoreTermState(oldState)
fmt.Fprintln(os.Stderr, "Session exited with status:", rec.Status())
fmt.Fprintln(os.Stderr, "Session exited with status:", rec.Status()) // nolint: errcheck
}
}
@ -1133,25 +1031,11 @@ func main() { //nolint: funlen, gocyclo
oldState = nil
}
// === Exit
exitWithStatus(int(rec.Status()))
}
// currentUser returns the current username minus any OS-specific prefixes
// such as MS Windows workgroup prefixes (eg. workgroup\user).
func localUserName(u *user.User) string {
if u == nil {
log.Fatal("null User?!")
}
// WinAPI: username may have CIFS prefix %USERDOMAIN%\
userspec := strings.Split(u.Username, `\`)
username := userspec[len(userspec)-1]
return username
}
func restoreTermState(oldState *xs.State) {
_ = xs.Restore(os.Stdin, oldState)
_ = xs.Restore(os.Stdin.Fd(), oldState) // nolint: errcheck,gosec
}
// exitWithStatus wraps os.Exit() plus does any required pprof housekeeping
@ -1168,8 +1052,9 @@ func exitWithStatus(status int) {
defer f.Close()
runtime.GC() // get up-to-date statistics
if err := pprof.WriteHeapProfile(f); err != nil {
log.Fatal("could not write memory profile: ", err) //nolint:gocritic
log.Fatal("could not write memory profile: ", err)
}
}
os.Exit(status)
}

View File

@ -6,9 +6,7 @@ XSD_USER=root
XSD_HOME=/var/run
INST_PREFIX=/usr/local
COMMAND=$INST_PREFIX/sbin/xsd
#ARGS="-L -aK KEX_all -aC C_all -aH H_all"
echo "SET ARGS in this script to define allow KEX, cipher and hmac algs"
exit 1
ARGS="-L"
depend() {
need net
@ -30,7 +28,7 @@ start() {
-d ${XSD_HOME} \
--make-pidfile --pidfile ${XSD_PIDFILE} \
--start --quiet --background \
--exec "${COMMAND}" -- ${ARGS}
--exec "${COMMAND}" -- "${ARGS}"
eend $?
}

View File

@ -1,166 +0,0 @@
#! /bin/sh
### BEGIN INIT INFO
# Provides: xsd
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop:
# Short-Description: eXperimental Shell Daemon
### END INIT INFO
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
( /usr/local/sbin/xsd -h 2>&1 | grep -q chaff ) 2>/dev/null || exit 0
umask 022
#if test -f /etc/default/ssh; then
# . /etc/default/ssh
#fi
. /lib/lsb/init-functions
if [ -n "$2" ]; then
XSD_OPTS="$XSD_OPTS $2"
fi
# Are we running from init?
run_by_init() {
([ "$previous" ] && [ "$runlevel" ]) || [ "$runlevel" = S ]
}
check_for_no_start() {
# forget it if we're trying to start, and /etc/xsd_not_to_be_run exists
if [ -e /etc/xsd_not_to_be_run ]; then
if [ "$1" = log_end_msg ]; then
log_end_msg 0 || true
fi
if ! run_by_init; then
log_action_msg "eXperimental Shell Daemon not in use (/etc/xsd_not_to_be_run)" || true
fi
exit 0
fi
}
check_dev_null() {
if [ ! -c /dev/null ]; then
if [ "$1" = log_end_msg ]; then
log_end_msg 1 || true
fi
if ! run_by_init; then
log_action_msg "/dev/null is not a character device!" || true
fi
exit 1
fi
}
#check_privsep_dir() {
# # Create the PrivSep empty dir if necessary
# if [ ! -d /run/sshd ]; then
# mkdir /run/sshd
# chmod 0755 /run/sshd
# fi
#}
#check_config() {
# if [ ! -e /etc/xsd_not_to_be_run ]; then
# /usr/local/sbin/xsd $XSD_OPTS -t || exit 1
# fi
#}
export PATH="${PATH:+$PATH:}/usr/local/sbin:/usr/sbin:/sbin"
case "$1" in
start)
#check_privsep_dir
check_for_no_start
check_dev_null
log_daemon_msg "Starting eXperimental Shell Daemon" "xsd" || true
if start-stop-daemon --start -b --quiet --oknodo --chuid 0:0 --exec /usr/local/sbin/xsd -- $XSD_OPTS; then
log_end_msg 0 || true
else
log_end_msg 1 || true
fi
;;
stop)
log_daemon_msg "Stopping eXperimental Shell Daemon" "xsd" || true
if start-stop-daemon --stop --quiet --oknodo --exec /usr/local/sbin/xsd; then
log_end_msg 0 || true
else
log_end_msg 1 || true
fi
;;
reload|force-reload)
check_for_no_start
#check_config
log_daemon_msg "Reloading eXperimental Shell Daemon's configuration" "xsd" || true
if start-stop-daemon --stop --signal 1 --quiet --oknodo --exec /usr/local/sbin/xsd; then
log_end_msg 0 || true
else
log_end_msg 1 || true
fi
;;
restart)
#check_privsep_dir
#check_config
log_daemon_msg "Restarting eXperimental Shell Daemon" "xsd" || true
start-stop-daemon --stop --quiet --oknodo --retry 30 --exec /usr/local/sbin/xsd
check_for_no_start log_end_msg
check_dev_null log_end_msg
if start-stop-daemon --start -b --quiet --oknodo --chuid 0:0 --exec /usr/local/sbin/xsd -- $XSD_OPTS; then
log_end_msg 0 || true
else
log_end_msg 1 || true
fi
;;
try-restart)
#check_privsep_dir
#check_config
log_daemon_msg "Restarting eXperimental Shell Daemon" "xsd" || true
RET=0
start-stop-daemon --stop --quiet --retry 30 --exec /usr/local/sbin/xsd || RET="$?"
case $RET in
0)
# old daemon stopped
check_for_no_start log_end_msg
check_dev_null log_end_msg
if start-stop-daemon --start -b --quiet --oknodo --chuid 0:0 --exec /usr/local/sbin/xsd -- $XSD_OPTS; then
log_end_msg 0 || true
else
log_end_msg 1 || true
fi
;;
1)
# daemon not running
log_progress_msg "(not running)" || true
log_end_msg 0 || true
;;
*)
# failed to stop
log_progress_msg "(failed to stop)" || true
log_end_msg 1 || true
;;
esac
;;
status)
status_of_proc -p /run/xsd.pid /usr/local/sbin/xsd xsd && exit 0 || exit $?
;;
*)
log_action_msg "Usage: /etc/init.d/xsd {start|stop|reload|force-reload|restart|try-restart|status}" || true
exit 1
esac
exit 0

View File

@ -1,16 +1,10 @@
.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: 782 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -1,18 +1,12 @@
.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:

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

@ -1,6 +1,6 @@
package xsnet
// Copyright (c) 2017-2020 Russell Magee
// Copyright (c) 2017-2019 Russell Magee
// Licensed under the terms of the MIT license (see LICENSE.mit in this
// distribution)
//
@ -21,12 +21,10 @@ import (
"log"
"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...)
@ -58,20 +56,10 @@ func expandKeyMat(keymat []byte, blocksize int) []byte {
return keymat
}
// 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) {
/* Support functionality to set up encryption after a channel has
been negotiated via xsnet.go
*/
func (hc Conn) getStream(keymat []byte) (rc cipher.Stream, mc hash.Hash, err error) {
var key []byte
var block cipher.Block
var iv []byte
@ -116,18 +104,13 @@ func (hc *Conn) getStream(keymat []byte) (rc cipher.Stream, mc hash.Hash, err er
log.Printf("[cipher BLOWFISH_64 (%d)]\n", copts)
case CAlgCryptMT1:
rc = cryptmt.New(nil, nil, keymat)
//NOTE: this alg is not based on block cipher, no IV
log.Printf("[cipher CRYPTMT1 (%d)]\n", copts)
case CAlgHopscotch:
rc = hopscotch.New(nil, nil, 4, keymat)
//NOTE: this alg is not based on block cipher, no IV
log.Printf("[cipher HOPSCOTCH (%d)]\n", copts)
case CAlgChaCha20_12:
keymat = expandKeyMat(keymat, chacha.KeySize)
key = keymat[0:chacha.KeySize]
ivlen = chacha.INonceSize
iv = keymat[chacha.KeySize : chacha.KeySize+ivlen]
rc, err = chacha.NewCipher(iv, key, chacha.INonceSize)
rc, err = chacha.NewCipher(iv, key, 20)
if err != nil {
log.Printf("[ChaCha20 config error]\n")
fmt.Printf("[ChaCha20 config error]\n")
@ -157,9 +140,6 @@ 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)
@ -168,7 +148,7 @@ func (hc *Conn) getStream(keymat []byte) (rc cipher.Stream, mc hash.Hash, err er
//os.Exit(1)
}
if err == nil && ivlen > 0 {
if err != nil {
// Feed the IV into the hmac: all traffic in the connection must
// feed its data into the hmac afterwards, so both ends can xor
// that with the stream to detect corruption.

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

@ -1,6 +1,6 @@
// consts.go - consts for xsnet
// Copyright (c) 2017-2020 Russell Magee
// Copyright (c) 2017-2019 Russell Magee
// Licensed under the terms of the MIT license (see LICENSE.mit in this
// distribution)
//
@ -29,10 +29,6 @@ const (
KEX_NEWHOPE_SIMPLE // 'NewHopeLP-Simple' - https://eprint.iacr.org/2016/1157
KEX_resvd14
KEX_resvd15
KEX_FRODOKEM_1344AES
KEX_FRODOKEM_1344SHAKE
KEX_FRODOKEM_976AES
KEX_FRODOKEM_976SHAKE
KEX_invalid = 255
)
@ -52,8 +48,6 @@ 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
@ -77,8 +71,6 @@ 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
@ -98,8 +90,8 @@ const (
// Channel status Op byte type (see CSONone, ... and CSENone, ...)
type CSOType uint32
// TODO: this should be small (max unfragmented packet size?)
const MAX_PAYLOAD_LEN = 2*1024*1024*1024 - 1
//TODO: this should be small (max unfragmented packet size?)
const MAX_PAYLOAD_LEN = 4*1024*1024*1024 - 1
// Session symmetric crypto algs
const (
@ -108,7 +100,6 @@ const (
CAlgBlowfish64 // golang.org/x/crypto/blowfish
CAlgCryptMT1 //cryptmt using mtwist64
CAlgChaCha20_12
CAlgHopscotch
CAlgNoneDisallowed
)
@ -119,27 +110,8 @@ 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"

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

@ -40,7 +40,7 @@ func getKCPalgnum(extensions []string) (k KCPAlg) {
switch s {
case "KCP_NONE":
k = KCP_NONE
break //golint:ignore SA4011 out of for
break //out of for
case "KCP_AES":
k = KCP_AES
break //out of for

659
xsnet/net.go Executable file → Normal file

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -1,13 +0,0 @@
//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()
}

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

@ -1,6 +1,6 @@
// hkextun.go - Tunnel setup using an established xsnet.Conn
// Copyright (c) 2017-2020 Russell Magee
// Copyright (c) 2017-2019 Russell Magee
// Licensed under the terms of the MIT license (see LICENSE.mit in this
// distribution)
//
@ -37,8 +37,6 @@ 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,16 +1,10 @@
.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,7 +1,7 @@
// Util to generate/store passwords for users in a file akin to /etc/passwd
// suitable for the xs server, using bcrypt.
// suitable for the demo hkexsh server, using bcrypt.
//
// Copyright (c) 2017-2020 Russell Magee
// Copyright (c) 2017-2019 Russell Magee
// Licensed under the terms of the MIT license (see LICENSE.mit in this
// distribution)
//