Compare commits

..

196 commits

Author SHA1 Message Date
Russ Magee
12409319e7 Removed go.mod, go.sum 2024-07-15 02:06:26 -07:00
Russtopia
bfcd097a14 Bump version to v0.9.11 2024-05-02 00:48:50 -07:00
Russtopia
136f37e209 Update 'README.md' 2024-05-01 23:58:44 -07:00
Russtopia
ec9b4fe2f4 Merge branch 'whirlpool-hash' of RLabs/xs into master 2024-05-01 23:49:36 -07:00
Russtopia
aa33a3b8a0 Merge branch 'log-listener-ipaddr' of RLabs/xs into master 2024-05-01 23:48:18 -07:00
Russ Magee
7e4aeba93a Add remote IP to net.Listener Accept logmsg 2024-05-01 23:46:43 -07:00
Russ Magee
91bb0778b2 Addition of WHIRLPOOL hash 2024-05-01 23:43:24 -07:00
Russ Magee
952279a108 Moved esc seq table out of copyBuffer to avoid redecls 2024-04-27 22:01:43 -07:00
Russ Magee
dbaa8b5b62 Ensure auth fails if server is somehow built for unsupported platform 2024-03-30 00:48:46 -07:00
Russ Magee
77c9b8654f Left GOPROXY alone 2024-03-02 14:46:15 -08:00
Russ Magee
e42645a2b3 Refreshed go.{mod,sum} and bumped semver in Makefile 2024-03-01 23:34:14 -08:00
Russ Magee
057a3c01c7 Updated go.{mod,sum} 2024-02-25 21:14:20 -08:00
Russ Magee
540cb8ff3a gofmt 2024-02-25 21:14:00 -08:00
Russ Magee
ae67ee6201 Fixed CI script 2024-01-29 21:47:03 -08:00
Russ Magee
8827d67cc6 unified refs to authtoken file to a const string 2024-01-29 21:37:07 -08:00
Russtopia
17d7bc01ef Update 'README.md'
Updated references to .xs_id location
2024-01-29 19:03:13 -08:00
Russ Magee
89ad0e0998 Fixed missed authtoken file ref in auth.go 2024-01-29 18:56:21 -08:00
Russ Magee
713f44086a Bumped version 2024-01-29 18:43:24 -08:00
Russ Magee
08cccb6929 Moved .xs_id to ~/.config/xs 2024-01-29 18:40:26 -08:00
Russ Magee
6212119621 Added max bounds for chaff, rekey intervals and random jitter for rekey interval 2023-12-03 19:22:05 -08:00
Russtopia
faf8769ac4 Merge branch 'remodulate-on-rekey' of RLabs/xs into master
Add optional cipher/hmac algo remodulate on rekey
2023-12-02 02:00:17 -08:00
Russ Magee
32b669192b Add optional cipher/hmac algo remodulate on rekey 2023-12-02 01:58:30 -08:00
Russtopia
e82d968381 Merge branch 'rekeying' of RLabs/xs into master 2023-11-15 00:43:36 -08:00
Russ Magee
032baf63d6 Added rekeying (-r secs) client/server 2023-11-15 00:32:50 -08:00
Russ Magee
c569a5a3c9 Removed debug stmts to do with keepalive 2023-11-07 00:38:51 -08:00
Russtopia
36799ba9e7 Merge branch 'conn-keepalive' of RLabs/xs into master
Merge of branch conn-keepalive
2023-11-06 23:48:51 -08:00
Russ Magee
f0dc681a4c Merge branch 'master' into conn-keepalive 2023-11-06 23:41:59 -08:00
Russ Magee
871f9a200c Experimental dead conn process kill logic 2023-11-06 23:17:13 -08:00
Russ Magee
908a1bcda2 Fix for ConnDead status 2023-11-06 21:57:19 -08:00
Russ Magee
39a8bd2f03 Bump 2023-11-05 19:05:23 -08:00
Russ Magee
9244cc9785 KeepAlive WIP. TODO: check exitStatus logic for shell 'exit' clean exit 2023-11-05 18:53:49 -08:00
Russ Magee
d0f8751b2b debugging 2023-11-05 16:40:12 -08:00
Russ Magee
a3d8543816 Disabled test code 2023-11-05 15:08:31 -08:00
Russ Magee
bcea6d713f Connection keepalive/disconnect 2023-11-05 15:06:43 -08:00
Russ Magee
580053adbd Connection keepalive/disconnect 2023-11-05 14:58:24 -08:00
Russ Magee
74be6173b6 Comment cleanup 2023-11-03 23:57:55 -07:00
Russ Magee
119c039b91 Fixed up mentions of old name hkexsh 2023-10-18 00:52:08 -07:00
Russ Magee
3cd6f8b7e6 Update to use latest hopscotch enc alg 2023-09-13 00:57:41 -07:00
Russtopia
623d622d58 Update README.md with live build status 2023-08-05 12:57:37 -07:00
Russ Magee
06124d7584 Fix for scc rule 2023-05-24 20:47:04 -07:00
Russ Magee
7b6a9d1350 Added scc target 2023-05-24 20:29:26 -07:00
Russ Magee
5ee09de99a Cleaner fix for issues #22,#33 2022-10-12 21:27:48 -07:00
Russ Magee
9ca5ccae32 (issue #33) use of time.Sleep() prior to ptmx.Close() in xsd.runShellAs()
Hack to enable server to complete sending data to client (eg. xs -x "ls /foo" losing end of output)
2022-10-04 22:30:10 -07:00
Russ Magee
bd0b48d98f golint cleanup 2022-09-26 22:09:54 -07:00
Russtopia
3325bb3a4e Update 'README.md' adding authtoken setup example 2022-09-26 00:49:52 -07:00
Russ Magee
0913311351 Suppress Gimme Cookie prompt when in gopt (-g) authtoken create mode 2022-09-25 23:23:33 -07:00
Russ Magee
b2e43f4bad Converted xsnet Read() ctrlStatOp logic to switch 2022-09-25 11:33:09 -07:00
Russ Magee
667328a91c xs/ lint cleanups 2022-09-24 20:01:16 -07:00
Russ Magee
0f28d2c023 More golangci-lint config updates 2022-09-21 00:34:53 -07:00
Russ Magee
5c826f7a5f Updated golangci-lint config; xsd.sysvrc init script updates 2022-09-20 23:02:52 -07:00
Russ Magee
1d13e6a3bd golangci.yml - disable deprecated checkers 2022-09-20 21:12:13 -07:00
Russ Magee
b5f9333b3a Updated golangci-lint config 2022-09-20 20:57:08 -07:00
Russ Magee
9a0dd8270a Cleaned up & updated vendor/ 2022-09-20 20:31:53 -07:00
Russ Magee
0b70cdbac0 Merge branch 'master' of https://gogs.blitter.com/RLabs/xs 2022-09-12 22:13:00 -07:00
Russ Magee
5b6bec0287 Reintroduce go mod init/tidy and remove old vendor dir if present 2022-09-12 22:12:21 -07:00
Russ Magee
254e9fb7d6 Reintroduce go mod init/tidy 2022-09-12 22:05:03 -07:00
Russ Magee
653e732445 Update go mod deps 2022-09-12 20:10:51 -07:00
Russtopia
610781aadf Merge branch 'update-go-callvis-diagrams' of RLabs/xs into master 2022-08-21 00:01:16 -07:00
Russ Magee
1f9f03dfe3 Update go-callvis graphics for wiki 2022-08-21 00:04:06 -07:00
Russ Magee
677172dc1f Avoid dependence on goproxy (use vendored deps) 2022-08-12 21:56:09 -07:00
Russ Magee
7a4560762d Added vendor/ dir to remove dependencies on gopkg.in (down Aug 12 2022) 2022-08-12 21:30:05 -07:00
Russtopia
f24d4d8fdb Update 'README.md' 2022-07-02 12:51:18 -07:00
Russtopia
42ad33065b Update 'README.md' 2022-07-02 12:44:37 -07:00
Russtopia
41769660cc Bump version → 0.9.5.1 2022-07-02 12:01:15 -07:00
Russtopia
73eadf7534 Update 'README.md' 2022-07-02 12:00:12 -07:00
Russ Magee
819b359306 Merge branch 'master' of https://gogs.blitter.com/RLabs/xs 2022-06-16 22:45:33 -07:00
Russ Magee
ee19787b5e Added support for building garbled binaries (https://github.com/burrowers/garble.git) 2022-06-16 22:45:25 -07:00
Russ Magee
0765c8bea6 Handle missing go.mod, go.sum 2022-05-01 23:19:50 -07:00
Russ Magee
a4af720280 Put lint before build in CI script 2022-04-09 20:05:42 -07:00
Russ Magee
4f9a064940 CI script - unset GOMODULE 2022-04-09 19:48:31 -07:00
Russ Magee
ccebf6f4b3 CI script - go mod creation from scratch w/o GOPROXY to test all dependency fetches 2022-04-09 19:47:31 -07:00
Russ Magee
08b8bd37d5 Merged CI script updates 2022-04-09 19:43:38 -07:00
Russ Magee
8e8fff415c Updated build CI script to clean go mod cache prior to build 2022-04-09 19:41:26 -07:00
Russ Magee
81e3728c98 build CI script 2022-04-09 11:50:24 -07:00
Russ Magee
3f6ee1e005 go mod tidy updates 2022-04-09 11:49:21 -07:00
Russ Magee
4168b5038b Fixed IV feed into initial HMAC during chan setup 2021-11-22 18:38:46 -08:00
Russ Magee
02e379e50d Updated README.md for v0.9.3 wrt. default accepted algs 2021-11-15 20:57:29 -08:00
Russ Magee
9c844938a9 Version bump -> v0.9.3 2021-11-15 20:46:49 -08:00
Russ Magee
20113653f7 xsd.initrc require admin to set defaults 2021-11-15 20:35:20 -08:00
Russ Magee
ca4335df9e Fixed typo in example xsd.initrc 2021-11-15 19:25:30 -08:00
Russ Magee
6965ecf78e Merge branch 'master' of https://gogs.blitter.com/RLabs/xs 2021-11-15 18:33:02 -08:00
Russ Magee
d837bd2583 xsd.initrc fix; help improvements 2021-11-15 18:32:42 -08:00
Russ Magee
12b7e525bf Help improvements 2021-11-15 17:57:01 -08:00
Russ Magee
0184e34284 Merge branch 'master' of https://gogs.blitter.com/RLabs/xs 2021-11-14 21:34:32 -08:00
Russ Magee
cfc9ab8590 Fixed error in processing of allowed HMAC algs.
xsd: allowed algs default to none if unspecified.
2021-11-14 21:33:33 -08:00
Russ Magee
129dce4b08 added hopscotch cipher 2021-11-14 21:33:09 -08:00
Russ Magee
232ee0e2de Merge branch 'add-hopscotch-cipher' 2021-11-12 20:40:24 -08:00
Russ Magee
7002f87477 added hopscotch cipher 2021-11-12 20:39:44 -08:00
Russtopia
db8697aa35 Updated links to Yawning-authored algs (kyber, newhope) 2021-11-03 19:58:40 -07:00
Russtopia
6e06c81941 Updated links to Yawning-authored algs (kyber, newhope) 2021-11-03 19:54:20 -07:00
Russtopia
0e453f15d0 Update blurb on 'cryptographic agility' and server alg whitelisting 2021-08-15 13:12:29 -07:00
Russtopia
aae7b14e7c Update 'README.md' 2021-08-15 12:57:46 -07:00
Russtopia
6904268797 Added blurb on crypto primitive negotiation 2021-08-15 02:49:20 -07:00
Russ Magee
b016e3cd77 Added notes on how to avoid/detect client->server copy issues (cf. issue #31)
Signed-off-by: Russ Magee <rmagee@gmail.com>
2021-03-16 19:32:18 -07:00
Russ Magee
e4b69eab2d Cleaned up README.md 2021-02-19 01:15:28 -08:00
Russ Magee
9ba07719cd Fixed bacillus CI script 2021-01-31 22:53:01 -08:00
Russ Magee
83cd38c651 Fixes for file ownership creating ~/.xs_id
Added username field to authToken to allow token-based login to other users

Signed-off-by: Russ Magee <rmagee@gmail.com>
2021-01-31 22:13:05 -08:00
Russ Magee
71f2bdfe8d Fix conflict on cptest.sha1sum 2021-01-15 01:33:02 -08:00
Russ Magee
c1ab12dc1b Updated cptest.sha1sum 2021-01-15 01:28:59 -08:00
Russ Magee
435e2b6380 Updated cptest.sha1sum 2021-01-15 01:23:01 -08:00
Russ Magee
e8986d3464 Merge branch 'master' of https://gogs.blitter.com/RLabs/xs 2021-01-15 01:15:27 -08:00
Russ Magee
c511af6c13 Removed static cptest/ dir, now managed by CI tests 2021-01-15 01:15:08 -08:00
Russ Magee
bcc07a8fac Removed static cptest/ dir, now managed by CI tests 2021-01-15 00:31:36 -08:00
Russ Magee
242d39192c Bump version to v0.9.2 2021-01-13 00:59:27 -08:00
Russ Magee
3fc74bae70 Merge branch 'master' of https://gogs.blitter.com/RLabs/xs 2021-01-13 00:44:20 -08:00
Russ Magee
287f9d1035 use crypto/rand in key spots (for KEM seeds) rather than math/rand 2021-01-13 00:43:26 -08:00
Russ Magee
f2929120b6 use crypto/rand in key spots (for KEM seeds) rather than math/rand
Signed-off-by: Russ Magee <rmagee@gmail.com>
2021-01-12 23:45:58 -08:00
Russ Magee
4e8e064c74 Bump version to v0.9.0 2021-01-10 22:10:18 -08:00
Russ Magee
c23edc6874 Fixed FrodoKEM neg bug (sending cipheropts,opts order)
Also tweaked pad size random to use full range; removed some junk logging
2021-01-10 22:04:52 -08:00
Russ Magee
3193ede825 **BREAKING** Switch to EtA per Krawczyk2001
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-12-15 22:09:18 -08:00
Russ Magee
640e59be4f Comment re: AtE vs. EtA 2020-12-15 21:15:15 -08:00
Russ Magee
5f42894ac2 Added FrodoKEM 2020-12-10 19:21:04 -08:00
Russ Magee
d0f76fd3b4 Added sysv init script
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-10-27 13:22:50 -07:00
Russ Magee
035df99069 Bumped version
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-09-13 22:31:11 -07:00
Russ Magee
0f22f35e7c Tweaked cleanup of .xs_id again
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-09-13 21:51:18 -07:00
Russ Magee
5aca04e1d5 Fix to the fix (sigh) for .xs_id test 2020-09-13 21:36:48 -07:00
Russ Magee
569230a2af Fixed .xs_id file save/restore during tests 2020-09-13 21:29:09 -07:00
Russtopia
01aa67d1f7 Update 'bacillus/ci_pushbuild.sh' 2020-08-23 23:33:58 -07:00
Russtopia
7901cd8809 Update 'bacillus/ci_pushbuild.sh' 2020-08-21 19:49:17 -07:00
Russtopia
611b6853b2 Update 'bacillus/ci_pushbuild.sh' 2020-08-21 19:47:37 -07:00
Russtopia
61b7a80e00 Update 'README.md' 2020-08-21 19:42:15 -07:00
Russ Magee
b3a359a84e Merge branch 'master' of https://gogs.blitter.com/RLabs/xs 2020-08-08 02:00:25 -07:00
Russ Magee
1943b2314a 2020 Copyright update; minor comment typo fixes
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-08-08 01:59:27 -07:00
Russ Magee
499eaa665b Minor doc typo fixes
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-08-08 01:54:46 -07:00
Russ Magee
eb373ff37b Fixed misuse of iota in xsnet/consts.go that broke channel status opcodes
Cleaned up var declarations and added some greppable comments to show xs setup & flow
2020-07-24 23:10:43 -07:00
Russ Magee
1b01ed14f2 Fixed non-MSYS xsd install
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-07-21 22:10:04 -07:00
Russ Magee
09055dffe6 Resynced w/master
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-07-21 22:08:46 -07:00
Russ Magee
f480c8cf78 Bumped version
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-07-21 21:57:31 -07:00
Russ Magee
69a3fb5d08 Fixed login timeout term handling/restoreState * NOTE breaking change to exit codes
Also some value => ref fixes for Conn receiver methods

Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-07-21 21:52:58 -07:00
Russ Magee
3522976ef7 Correct implicit username for MSYS2 eg. xs @server.com
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-06-04 00:53:20 -07:00
Russ Magee
f0a2bb0295 Updates to fix MSYSTEM=MSYS build and install targets
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-06-03 21:39:42 -07:00
Russ Magee
1addd18806 Fixed GOOS=windows MSYSTEM=MSYS install
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-05-24 21:15:28 -07:00
Russ Magee
57b71e7925 FreeBSD note into README.md
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-05-18 23:59:33 -07:00
Russtopia
58c87f7e0e Update 'README.md' 2020-05-18 23:27:40 -07:00
Russ Magee
769df46fb1 Fixed freeBSD gmake note
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-04-26 05:40:09 +00:00
Russ Magee
51488fab6d Allow override on cmdline of MAKE variable to support gmake in freeBSD or via both 'make and 'gmake' names in Linux
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-04-28 17:49:09 -07:00
Russ Magee
0962c0b811 Bumped version, use latest goutmp v1.0.5
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-04-25 19:45:30 -07:00
Russ Magee
3bea3e6ebb Updated go.{mod,sum}
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-04-25 19:35:24 -07:00
Russ Magee
1d30b8582d Fixes for build constraints, MakeRaw()/ReadPassword() etc. fd arg type
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-04-25 18:42:29 -07:00
Russ Magee
47a3c6f1b8 Fixes for int vs. uintptr args (linux vs. freebsd) 2020-04-26 01:15:44 +00:00
Russ Magee
6bb4b90c87 Fixes for int vs. uintptr args (linux vs. freebsd) 2020-04-26 01:14:19 +00:00
Russ Magee
531c846899 Merge branch 'master' of https://gogs.blitter.com/RLabs/xs 2020-04-26 01:04:11 +00:00
Russ Magee
cba36b66f1 FreeBSD 12 support (NOTE: xc not yet functional)
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-04-26 01:03:29 +00:00
Russtopia
5448dbc7b9 Update 'README.md' wrt. default xsd passwd file mode 2020-04-16 00:19:03 -07:00
Russ Magee
09c2408c06 Tweaked 'pv' progress bar output format options
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-04-03 20:08:45 -07:00
Russtopia
4686361ece Update 'CODE_OF_CONDUCT.md' 2020-03-29 23:22:22 -07:00
Russ Magee
e803d3da09 Bumped version, xc reports -v correctly
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-02-26 20:25:01 -08:00
Russ Magee
0c1d5e036e Merge tag 'v0.8.20' into xc-bigfile-EOF 2020-02-26 20:08:46 -08:00
Russ Magee
3b0ddba7f2 Merge tag 'v0.8.19' into xc-bigfile-EOF 2020-02-26 20:06:52 -08:00
Russ Magee
b3ebd0db17 Bumped version
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-02-25 23:33:09 -08:00
Russtopia
d1076c9bd9 Update 'README.md' with xc progress info
'pv' util progress notes added
2020-02-25 23:25:39 -08:00
Russ Magee
2e16c9a692 Remote->Client cp with pv status TODO: Remote total bytesize estimate
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-02-25 23:08:42 -08:00
Russ Magee
9d674315f9 Merge branch 'master' of ssh://blitter.com/var/git/xs 2020-02-25 21:17:37 -08:00
Russ Magee
64a314df11 Client->Remote xc progress indication using pv
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-02-25 21:16:55 -08:00
Russtopia
f6910162b4 Update 'README.md'
ChaCha20 support
2020-02-24 16:50:55 -08:00
Russ Magee
6ac6f02b3b Bumped version
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-02-23 21:52:45 -08:00
Russ Magee
f3bf6d9041 Added login timeout (30s)
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-02-23 21:52:20 -08:00
Russ Magee
138f855a82 Bumped version
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-02-21 20:48:23 -08:00
Russ Magee
e9aa0072a5 Initial aead/chacha20 support (ChaCha20_12)
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-02-21 17:21:19 -08:00
Russ Magee
8de76520e4 Added session_test.go
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-02-19 13:27:56 -08:00
Russ Magee
e84f432033 auth.go: added new unit tests
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-02-18 13:41:44 -08:00
Russ Magee
654de563dc auth.go: AuthCtx added to structure mockable entities for testing
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-02-18 10:53:04 -08:00
Russ Magee
733fc46d86 Added unit tests for auth.go: AuthUserByToken
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-02-17 23:14:37 -08:00
Russ Magee
d18396f535 Added unit tests for auth.go
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-02-17 21:17:11 -08:00
Russ Magee
e320725e07 Added unit tests for auth.go
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-02-17 21:16:10 -08:00
Russ Magee
d4a3521437 Bumped version
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-02-17 00:06:38 -08:00
Russ Magee
c2c43c1c50 Fix for issue #23: null panic if logger obj is null due no active syslog sink
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-02-17 00:02:28 -08:00
Russ Magee
9ffeab2456 Tagged version v0.8.16
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-02-06 19:15:52 -08:00
Russ Magee
0eb785a9dc Removed WANDERER exp c_alg
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-02-06 19:12:45 -08:00
Russ Magee
58652ce935 Updated go.mod deps
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-02-06 19:02:34 -08:00
Russ Magee
1b964a4066 Updated intf to cryptmt
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-02-06 18:56:36 -08:00
Russ Magee
3eee573231 Resync w/cryptmt, wanderer repos
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-02-05 21:26:03 -08:00
Russ Magee
d3d7ac9aae Merge branch 'master' into xc-bigfile-EOF 2020-01-30 23:33:41 -08:00
Russ Magee
6c049dde08 More throughput-friendly hack (just delay at very end vs. each packet)
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-01-30 23:32:36 -08:00
Russ Magee
862c0c3d7f Hack to throttle data overrun (sender->rcvr) during large xc operations
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-01-30 21:14:35 -08:00
Russ Magee
15a1a39d81 Fixed xsd.initrc $ARGS passing 2020-01-30 17:38:39 -08:00
Russ Magee
977380e61f Bumped version
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-01-30 17:22:36 -08:00
Russ Magee
bebfd1cb1f Removed redundant assigns of stdin/out/err to conn prior to ptmx setup 2020-01-30 17:14:56 -08:00
Russ Magee
399babd62a Removed stdout redir in push build script
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-01-30 13:01:10 -08:00
Russ Magee
0303b394c8 fixed /home/russtopia/bin:/opt/eclipse:/usr/lib/go/bin:/home/russtopia/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/opt/bin in push build script
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-01-30 12:58:08 -08:00
Russ Magee
0933aa4ea5 Added lint stage to push script
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-01-30 12:55:15 -08:00
Russ Magee
d3ebcab1d4 Added golangci-lint config
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-01-30 12:50:38 -08:00
Russ Magee
89b6e8bce7 Version tags now reflect mod/vendor build setup.
Switched 'make lint' to use golangci-lint as gometalinter is deprecated.

Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-01-30 12:40:18 -08:00
Russ Magee
eb9ce0e0e2 Updated imports and vendor/ for local fork of schwanenlied.me crypto (chacha20, newhope, kyber)
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-01-30 12:18:39 -08:00
Russ Magee
d75b419c7a Merge branch 'master' of ssh://blitter.com/var/git/xs 2020-01-29 17:25:47 -08:00
Russ Magee
f5be3578a8 1/3 Updated Makefile to allow VENDOR flag (adds -vendor to version string)
2/3 Added vendor/ dir to lock down dependent pkg versions.
The author of git.schwanenlied.me/yawning/{chacha20,newhope,kyber}.git has copied
their repos to gitlab.com/yawning/ but some imports of chacha20 from newhope still
inconsistently refer to git.schwanenlied.me/, breaking build.
Licenses for chacha20 also changed from CC0 to AGPL, which may or may not be an
issue. Until the two aforementioned issues are resolved, locking to last-good
versions is probably the best way forward for now.

To build with vendored deps, use make VENDOR=1 clean all

3/3 Moved body of CI push script into bacillus/
2020-01-29 17:23:44 -08:00
Russ Magee
e55b560230 Introduced bacillus ci_pushbuild script 2020-01-29 16:33:12 -08:00
Russ Magee
d6cd51c79f Moved body of CI push script into project from bacillus 2020-01-29 15:57:43 -08:00
Russ Magee
caac02a77b 1/2 Updated Makefile to allow VENDOR flag (adds -vendor to version string)
2/2 Added vendor/ dir to lock down dependent pkg versions.
The author of git.schwanenlied.me/yawning/{chacha20,newhope,kyber}.git has copied
their repos to gitlab.com/yawning/ but some imports of chacha20 from newhope still
inconsistently refer to git.schwanenlied.me/, breaking build.
Licenses for chacha20 also changed from CC0 to AGPL, which may or may not be an
issue. Until the two aforementioned issues are resolved, locking to last-good
versions is probably the best way forward for now.

To build with vendored deps, use make VENDOR=1 clean all
2020-01-29 13:55:38 -08:00
Russ Magee
7fe915450b Bumped tag 2020-01-24 18:50:33 -08:00
Russ Magee
3be1243bf9 Updated parms to test WANDERER alg to set sboxUpdate mode 2020-01-24 18:49:34 -08:00
Russ Magee
7ce725c7d2 Bumped version -> v0.8.10 2020-01-08 17:16:24 -08:00
Russ Magee
4e0ddf282a Comments on main() xsd 2019-12-19 20:34:05 -08:00
Russ Magee
9f956cff62 branch temp version 2019-12-19 20:04:22 -08:00
Russ Magee
f8f4bcbe77 go.mod updates for gopkg.in/hlandau/passlib.v1 2019-12-19 20:03:32 -08:00
Russ Magee
faf4d5c50a Add (as default) option to use system shadow passwds 2019-12-19 20:01:39 -08:00
44 changed files with 3576 additions and 1373 deletions

160
.golangci.yml Normal file
View file

@ -0,0 +1,160 @@
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"
dupl:
threshold: 125
funlen:
lines: 125
statements: 50
gci:
local-prefixes: github.com/golangci/golangci-lint
goconst:
min-len: 2
min-occurrences: 2
gocritic:
enabled-tags:
- diagnostic
- experimental
- performance
#- style
#- opinionated
disabled-checks:
- commentFormatting
- commentedOutCode
- dupImport # https://github.com/go-critic/go-critic/issues/845
- ifElseChain
- octalLiteral
- whyNoLint
- wrapperFunc
gocyclo:
min-complexity: 15
goimports:
local-prefixes: github.com/golangci/golangci-lint
gomnd:
settings:
mnd:
# don't include the "operation" and "assign"
checks: argument,case,condition,return
govet:
check-shadowing: true
settings:
printf:
funcs:
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Infof
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Warnf
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Errorf
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Fatalf
lll:
line-length: 140
maligned:
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
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
- govet
- ineffassign
#- interfacer
- lll
- misspell
#- nakedret
- noctx
- nolintlint
- rowserrcheck
#- scopelint
- staticcheck
#- structcheck
- stylecheck
- typecheck
- unconvert
- unparam
- unused
#- varcheck
- whitespace
# don't enable:
# - asciicheck
# - deadcode
# - gochecknoglobals
# - gocognit
# - godot
# - godox
# - goerr113
# - golint
# - interfacer
# - maligned
# - nestif
# - prealloc
## - rowserrcheck
# - scopelint
# - structcheck
# - testpackage
# - varcheck
# - wsl
issues:
# Excluding configuration per-path, per-linter, per-text and per-source
exclude-rules:
- path: _test\.go
linters:
- gomnd
# https://github.com/go-critic/go-critic/issues/926
- linters:
- gocritic
text: "unnecessaryDefer:"
# 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'
run:
skip-dirs:
- test/testdata_etc
- internal/cache
- internal/renameio
- internal/robustio
# golangci.com configuration
# https://github.com/golangci/golangci/wiki/Configuration
service:
golangci-lint-version: 1.23.x # use the fixed version to not introduce new linters unexpectedly
prepare:
- echo "here I can run custom commands, but no preparation needed for this repo"

View file

@ -55,5 +55,7 @@ to the project. Any intent to deviate the project from its original purpose
of existence will constitute grounds for remedial action which may include
expulsion from the project.
This document is based upon the Code of Merit version 1.0 (Dec 4 2018).
This document is based upon the original Code of Merit version 1.0 (Dec 4 2018).
(https://web.archive.org/web/20181204203029/http://code-of-merit.org/)
Updated version (Mar 29 2020): https://codeofmerit.org/code/

View file

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2017 - 2019 Russell Magee (xs/xsd/xsnet/xspasswd)
Copyright (c) 2017 - 2021 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

115
Makefile
View file

@ -1,24 +1,60 @@
.PHONY: lint vis clean common client server passwd subpkgs install uninstall reinstall
VERSION := 0.9.11
.PHONY: lint vis clean common client server passwd\
subpkgs install uninstall reinstall scc
## Tag version of binaries with build info wrt.
## GO111MODULE(=on) and vendor/ setup vs. $GOPATH pkg builds
############################################################
ifeq ($(shell go env GOMOD),)
MTAG=
else
MTAG="-m"
endif
#ifeq ($(MAKEOPTS),)
MAKEOPTS = $(MAKEOPTS)
#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"
else
GOBUILDOPTS=
VTAG =
endif
############################################################
GIT_COMMIT := $(shell git rev-list -1 HEAD)
VERSION := 0.8.9
#ifeq ($(BUILDOPTS),)
BUILDOPTS :=$(BUILDOPTS)" -ldflags \"-X main.version=$(VERSION) -X main.gitCommit=$(GIT_COMMIT)\""
BUILDOPTS :=$(BUILDOPTS)"$(GOBUILDOPTS) -ldflags \"-X main.version=$(VERSION)$(MTAG)$(VTAG) -X main.gitCommit=$(GIT_COMMIT)\""
#endif
SUBPKGS = logger spinsult xsnet
TOOLS = xspasswd xs xsd
TOOLS = xs xsd
SUBDIRS = $(LIBS) $(TOOLS)
ifeq ($(GOOS),)
GOOS=$(shell go env GOOS)
endif
ifeq ($(GOOS),windows)
ifeq ($(MSYSTEM),MSYS)
WIN_MSYS=1
endif
endif
INSTPREFIX = /usr/local
all: common client server passwd
all: common client server
clean:
@echo "Make: $(MAKE)"
go clean .
for d in $(SUBDIRS); do\
$(MAKE) -C $$d clean;\
@ -36,69 +72,56 @@ tools:
common:
go build .
go install .
$(GO) build .
go install -a .
client: common
$(MAKE) BUILDOPTS=$(BUILDOPTS) -C xs
ifeq ($(MSYSTEM),)
ifneq ($(GOOS),windows)
server: common
ifeq ($(MSYSTEM),MSYS)
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
passwd: common
$(MAKE) BUILDOPTS=$(BUILDOPTS) -C xspasswd
scc:
@scc --exclude-dir=bacillus,vendor,garbled --cocomo-project-type=rlabs,3,1.12,1,1
vis:
@which go-callvis >/dev/null 2>&1; \
stat=$$?; if [ $$stat -ne "0" ]; then \
/bin/echo "go-callvis not found. Run go get github.com/Russtopia/go-callvis to install."; \
/bin/echo "go-callvis not found. Run go get https://github.com/TrueFurby/go-callvis to install."; \
else \
make -C xs vis;\
make -C xsd vis;\
make -C xspasswd vis; \
$(MAKE) -C xs vis;\
$(MAKE) -C xsd vis;\
fi
lint:
make -C xspasswd lint
make -C xsd lint
make -C xs lint
$(MAKE) -C xsd lint
$(MAKE) -C xs lint
reinstall: uninstall install
install:
cp xs/xs $(INSTPREFIX)/bin
ifeq ($(MSYSTEM),)
ifneq ($(GOOS),windows)
cp xsd/xsd xspasswd/xspasswd $(INSTPREFIX)/sbin
else
mv $(INSTPREFIX)/bin/xs $(INSTPREFIX)/bin/_xs
echo "WIN_MSYS:" $(WIN_MSYS)
ifdef WIN_MSYS
cp xs/mintty_wrapper.sh $(INSTPREFIX)/bin/xs
echo "Cross-build of xsd server for Windows not yet supported"
endif
cp xs/mintty_wrapper.sh $(INSTPREFIX)/bin/xc
cp xs/xs $(INSTPREFIX)/bin/_xs
cp xs/xs $(INSTPREFIX)/bin/_xc
echo "Install of xsd server for Windows not yet supported"
else
echo "Cross-build of xsd server for Windows not yet supported"
endif
cp xs/xs $(INSTPREFIX)/bin
cd $(INSTPREFIX)/bin && ln -s xs xc && cd -
cp xsd/xsd $(INSTPREFIX)/sbin
endif
uninstall:
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
rm -f $(INSTPREFIX)/bin/xs $(INSTPREFIX)/bin/xc \
$(INSTPREFIX)/bin/_xs $(INSTPREFIX)/bin/_xc
ifndef $(WIN_MSYS)
rm -f $(INSTPREFIX)/sbin/xsd
endif

119
README.md
View file

@ -1,12 +1,20 @@
[![GoDoc](https://godoc.org/blitter.com/go/xs?status.svg)](https://godoc.org/blitter.com/go/xs)
# XS
![last build status](https://bacillus.blitter.com/onPush-xs-build/lastStatusIcon)
--
XS (**X**perimental **S**hell) is a 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').
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.
***
**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.**
@ -24,9 +32,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://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)
* 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)
Currently supported session algorithms:
@ -35,10 +43,27 @@ Currently supported session algorithms:
* Twofish-128
* 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
@ -54,8 +79,7 @@ Packets are subject to padding (random size, randomly applied as prefix or postf
Chaffing and tunnels, if specified, are set up during initial client->server connection. Packets from the client local port(s) are sent through the main secured connection to the server's remote port(s), and vice versa, tagged with a chaff or tunnel specifier so that they can be discarded as chaff or de-multiplexed and delivered to the proper tunnel endpoints, respectively.
### Accounts and Passwords
Within the xspasswd/ directory is a password-setting utility. XS uses its own password file distinct from the system /etc/passwd to authenticate clients, using standard bcrypt+salt storage. This is currently done to allow alternate login credentials via xs vs. console/ssh login, due to the experimental nature of the program. At some point in the future an option to use the system's /etc/passwd and /etc/shadow may be implemented, making the use of the auxilliary xspasswd utility optional or obsolete.
Within the ```xspasswd/``` directory is a password-setting utility, ```xspasswd```, used if one wishes ```xs``` access to use separate credentials from those of the default (likely ssh) login method. In this mode, ```xsd``` uses its own password file distinct from the system /etc/passwd to authenticate clients, using standard bcrypt+salt storage. Activate this mode by invoking ```xsd``` with ```-s false```.
HERRADURA KEX
@ -70,41 +94,40 @@ KYBER IND-CCA-2 KEM
As of this time (Oct 2018) Kyber is one of the candidate algorithms submitted to the [NIST post-quantum cryptography project](https://csrc.nist.gov/Projects/Post-Quantum-Cryptography). The authors recommend using it in "... so-called hybrid mode in combination with established "pre-quantum" security; for example in combination with elliptic-curve Diffie-Hellman." THIS PROJECT DOES NOT DO THIS (in case you didn't notice yet, THIS PROJECT IS EXPERIMENTAL.)
### Dependencies:
* Recent version of go (tested, at various times, with go-1.9 to go-1.12.4)
* [github.com/mattn/go-isatty](http://github.com/mattn/go-isatty) //terminal tty detection
* [github.com/kr/pty](http://github.com/kr/pty) //unix pty control (server pty connections)
* [github.com/jameskeane/bcrypt](http://github.com/jameskeane/bcrypt) //password storage/auth
* [blitter.com/go/goutmp](https://gogs.blitter.com/RLabs/goutmp) // wtmp/lastlog C bindings for user accounting
* [https://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
### Installing
As of Go 1.8, one can directly use `go install` to get the client `xs` and server `xsd` binaries; however it is not recommended, as `xsd` requires root and for general use should be in one of the system directories, akin to other daemons. If one insists, the following will work to place them in $HOME/go/bin:
```
$ go install blitter.com/go/xs/xs@latest
$ go install blitter.com/go/xs/xsd@latest
```
(NOTE the `-v` (version) option for binaries obtained in this manner will be blank; another reason to build them yourself locally using the steps below.)
### Get source code
```
$ go get -u blitter.com/go/xs
$ cd $GOPATH/src/blitter.com/go/xs
$ go build ./... # install all dependent go pkgs
$ git clone https://gogs.blitter.com/RLabs/xs
```
### To build
```
$ cd $GOPATH/src/blitter.com/go/xs
$ make clean all
$ cd xs
$ make clean && make
```
### To install, uninstall, re-install
### To install, uninstall, re-install (xsd server)
```
$ sudo make [install | uninstall | reinstall]
```
### To manage service (assuming openrc init)
### To manage service (openrc init)
An example init script (xsd.initrc) is provided. Consult your Linux distribution documentation for proper service/daemon installation. For openrc,
@ -113,13 +136,23 @@ $ 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:
### To set accounts & passwords (DEPRECATED: `-s` is now true by default)
```
$ sudo touch /etc/xs.passwd
@ -154,10 +187,18 @@ or is interrupted.
### Setting up an 'authtoken' for scripted (password-free) logins
Use the -g option of xs to request a token from the remote server, which will return a
hostname:token string. Place this string into $HOME/.xs_id to allow logins without
entering a password (obviously, $HOME/.xs_id on both server and client for the user
hostname:token string. Place this string into $HOME/.config/xs/.xs_id to allow logins without
entering a password (obviously, $HOME/.config/xs/.xs_id on both server and client for the user
should *not* be world-readable.)
```
$ xs -g user@host.net >>~/.config/xs/.xs_id
```
[enter password blindly, authtoken entry will be stored in ~/.config/xs/.xs_id]
NOTE you may need to remove older entries for the same host if this is not the first time you have added
it to your .xs_id file.
### File Copying using xc
xc is a symlink to xs, and the binary checks its own filename to determine whether
@ -183,9 +224,21 @@ xc uses a 'tarpipe' to send file data over the encrypted channel. Use the -d fla
NOTE: Renaming while copying (eg., 'cp /foo/bar/fileA ./fileB') is NOT supported. Put another way, the destination (whether local or remote) must ALWAYS be a directory.
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;
@ -196,3 +249,9 @@ 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```

View file

@ -18,6 +18,7 @@ Architecture
(parts split out into hkexnet/*, hkexsession.go)
(DONE) - Make KEx fully-pluggable: isolate all code to do with Herradura into a
KEx-neutral pkg so it can be swapped out for other methods (eg., DH etc.)
(DONE - test branch) - Use system password db (/etc/{passwd,shadow})
Features
(DONE) - Support for hkcp (hkex-cp) - secure file copy protocol

137
auth.go Normal file → Executable file
View file

@ -2,7 +2,7 @@ package xs
// Package xs - a secure terminal client/server written from scratch in Go
//
// Copyright (c) 2017-2019 Russell Magee
// Copyright (c) 2017-2020 Russell Magee
// Licensed under the terms of the MIT license (see LICENSE.mit in this
// distribution)
//
@ -13,28 +13,95 @@ package xs
import (
"bytes"
"encoding/csv"
"errors"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"os/user"
"runtime"
"strings"
"blitter.com/go/xs/xsnet"
"github.com/jameskeane/bcrypt"
passlib "gopkg.in/hlandau/passlib.v1"
)
func userExistsOnSystem(who string) bool {
_, userErr := user.Lookup(who)
return userErr == nil
type AuthCtx struct {
reader func(string) ([]byte, error) // eg. ioutil.ReadFile()
userlookup func(string) (*user.User, error) // eg. os/user.Lookup()
}
func NewAuthCtx( /*reader func(string) ([]byte, error), userlookup func(string) (*user.User, error)*/ ) (ret *AuthCtx) {
ret = &AuthCtx{ioutil.ReadFile, user.Lookup}
return
}
// --------- System passwd/shadow auth routine(s) --------------
// VerifyPass verifies 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 {
ctx.reader = ioutil.ReadFile // dependency injection hides that this is required
}
passlib.UseDefaults(passlib.Defaults20180601)
var pwFileName string
if runtime.GOOS == "linux" {
pwFileName = "/etc/shadow"
} else if runtime.GOOS == "freebsd" {
pwFileName = "/etc/master.passwd"
} else {
return false, errors.New("Unsupported platform")
}
pwFileData, e := ctx.reader(pwFileName)
if e != nil {
return false, e
}
pwLines := strings.Split(string(pwFileData), "\n")
if len(pwLines) < 1 {
return false, errors.New("Empty shadow file!")
} else {
var line string
var hash string
var idx int
for idx = range pwLines {
line = pwLines[idx]
lFields := strings.Split(line, ":")
if lFields[0] == user {
hash = lFields[1]
break
}
}
if len(hash) == 0 {
return false, errors.New("nil hash!")
} else {
pe := passlib.VerifyNoUpgrade(password, hash)
if pe != nil {
return false, pe
}
}
}
return true, nil
}
// --------- End System passwd/shadow auth routine(s) ----------
// ------------- xs-local passwd auth routine(s) ---------------
// AuthUserByPasswd checks user login information using a password.
// This checks /etc/xs.passwd for auth info, and system /etc/passwd
// to cross-check the user actually exists.
// nolint: gocyclo
func AuthUserByPasswd(username string, auth string, fname string) (valid bool, allowedCmds string) {
b, e := ioutil.ReadFile(fname) // nolint: gosec
func AuthUserByPasswd(ctx *AuthCtx, username string, auth string, fname string) (valid bool, allowedCmds string) {
if ctx.reader == nil {
ctx.reader = ioutil.ReadFile // dependency injection hides that this is required
}
if ctx.userlookup == nil {
ctx.userlookup = user.Lookup // again for dependency injection as dep is now hidden
}
b, e := ctx.reader(fname) // nolint: gosec
if e != nil {
valid = false
log.Printf("ERROR: Cannot read %s!\n", fname)
@ -78,27 +145,37 @@ func AuthUserByPasswd(username string, auth string, fname string) (valid bool, a
r = nil
runtime.GC()
if !userExistsOnSystem(username) {
_, userErr := ctx.userlookup(username)
if userErr != nil {
valid = false
}
return
}
// ------------- End xs-local passwd auth routine(s) -----------
// AuthUserByToken checks user login information against an auth token.
// Auth tokens are stored in each user's $HOME/.xs_id and are requested
// Auth tokens are stored in each user's $HOME/.config/xs/.xs_id and are requested
// via the -g option.
// The function also check system /etc/passwd to cross-check the user
// actually exists.
func AuthUserByToken(username string, connhostname string, auth string) (valid bool) {
func AuthUserByToken(ctx *AuthCtx, username string, connhostname string, auth string) (valid bool) {
if ctx.reader == nil {
ctx.reader = ioutil.ReadFile // dependency injection hides that this is required
}
if ctx.userlookup == nil {
ctx.userlookup = user.Lookup // again for dependency injection as dep is now hidden
}
auth = strings.TrimSpace(auth)
u, ue := user.Lookup(username)
u, ue := ctx.userlookup(username)
if ue != nil {
return false
}
b, e := ioutil.ReadFile(fmt.Sprintf("%s/.xs_id", u.HomeDir))
b, e := ctx.reader(fmt.Sprintf("%s/%s", u.HomeDir, xsnet.XS_ID_AUTHTOKFILE))
if e != nil {
log.Printf("INFO: Cannot read %s/.xs_id\n", u.HomeDir)
log.Printf("INFO: Cannot read %s/%s\n", u.HomeDir, xsnet.XS_ID_AUTHTOKFILE)
return false
}
@ -106,25 +183,53 @@ func AuthUserByToken(username string, connhostname string, auth string) (valid b
r.Comma = ':'
r.Comment = '#'
r.FieldsPerRecord = 2 // connhost:authtoken
r.FieldsPerRecord = 3 // connhost:username: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]}, ":"))
// strings.Join([]string{record[0], record[1], record[2]}, ":"))
if (connhostname == record[0]) &&
(auth == strings.Join([]string{record[0], record[1]}, ":")) {
username == record[1] &&
(auth == strings.Join([]string{record[0], record[1], record[2]}, ":")) {
valid = true
break
}
}
if !userExistsOnSystem(username) {
_, userErr := ctx.userlookup(username)
if userErr != nil {
valid = false
}
return
}
func GetTool(tool string) (ret string) {
ret = "/bin/" + tool
_, err := os.Stat(ret)
if err == nil {
return ret
}
ret = "/usr/bin/" + tool
_, err = os.Stat(ret)
if err == nil {
return ret
}
ret = "/usr/local/bin/" + tool
_, err = os.Stat(ret)
if err == nil {
return ret
}
return ""
}

212
auth_test.go Normal file
View file

@ -0,0 +1,212 @@
package xs
import (
"errors"
"fmt"
"os/user"
"strings"
"testing"
)
type userVerifs struct {
user string
passwd string
good bool
}
var (
dummyShadowA = `johndoe:$6$EeQlTtn/KXdSh6CW$UHbFuEw3UA0Jg9/GoPHxgWk6Ws31x3IjqsP22a9pVMOte0yQwX1.K34oI4FACu8GRg9DArJ5RyWUE9m98qwzZ1:18310:0:99999:7:::
joebloggs:$6$F.0IXOrb0w0VJHG1$3O4PYyng7F3hlh42mbroEdQZvslybY5etPPiLMQJ1xosjABY.Q4xqAfyIfe03Du61ZjGQIt3nL0j12P9k1fsK/:18310:0:99999:7:::
disableduser:!:18310::::::`
dummyAuthTokenFile = "hostA:johndoe:abcdefg\nhostB:imposter:wxyz\n"
dummyXsPasswdFile = `#username:salt:authCookie
bobdobbs:$2a$12$9vqGkFqikspe/2dTARqu1O:$2a$12$9vqGkFqikspe/2dTARqu1OuDKCQ/RYWsnaFjmi.HtmECRkxcZ.kBK
notbob:$2a$12$cZpiYaq5U998cOkXzRKdyu:$2a$12$cZpiYaq5U998cOkXzRKdyuJ2FoEQyVLa3QkYdPQk74VXMoAzhvuP6
`
testGoodUsers = []userVerifs{
{"johndoe", "testpass", true},
{"joebloggs", "testpass2", true},
{"johndoe", "badpass", false},
}
testXsPasswdUsers = []userVerifs{
{"bobdobbs", "praisebob", true},
{"notbob", "imposter", false},
}
userlookup_arg_u string
readfile_arg_f string
)
func newMockAuthCtx(reader func(string) ([]byte, error), userlookup func(string) (*user.User, error)) (ret *AuthCtx) {
ret = &AuthCtx{reader, userlookup}
return
}
func _mock_user_Lookup(username string) (*user.User, error) {
username = userlookup_arg_u
if username == "baduser" {
return &user.User{}, errors.New("bad user")
}
urec := &user.User{Uid: "1000", Gid: "1000", Username: username, Name: "Full Name", HomeDir: "/home/user"}
fmt.Printf(" [mock user rec:%v]\n", urec)
return urec, nil
}
func _mock_ioutil_ReadFile(f string) ([]byte, error) {
f = readfile_arg_f
if f == "/etc/shadow" {
fmt.Println(" [mocking ReadFile(\"/etc/shadow\")]")
return []byte(dummyShadowA), nil
}
if f == "/etc/xs.passwd" {
fmt.Println(" [mocking ReadFile(\"/etc/xs.passwd\")]")
return []byte(dummyXsPasswdFile), nil
}
if strings.Contains(f, "/.xs_id") {
fmt.Println(" [mocking ReadFile(\".xs_id\")]")
return []byte(dummyAuthTokenFile), nil
}
return []byte{}, errors.New("no readfile_arg_f supplied")
}
func _mock_ioutil_ReadFileEmpty(f string) ([]byte, error) {
return []byte{}, nil
}
func _mock_ioutil_ReadFileHasError(f string) ([]byte, error) {
return []byte{}, errors.New("IO Error")
}
func TestVerifyPass(t *testing.T) {
readfile_arg_f = "/etc/shadow"
ctx := newMockAuthCtx(_mock_ioutil_ReadFile, nil)
for idx, rec := range testGoodUsers {
stat, e := VerifyPass(ctx, rec.user, rec.passwd)
if rec.good && (!stat || e != nil) {
t.Fatalf("failed %d\n", idx)
}
}
}
func TestVerifyPassFailsOnEmptyFile(t *testing.T) {
ctx := newMockAuthCtx(_mock_ioutil_ReadFileEmpty, nil)
stat, e := VerifyPass(ctx, "johndoe", "somepass")
if stat || (e == nil) {
t.Fatal("failed to fail w/empty file")
}
}
func TestVerifyPassFailsOnFileError(t *testing.T) {
ctx := newMockAuthCtx(_mock_ioutil_ReadFileEmpty, nil)
stat, e := VerifyPass(ctx, "johndoe", "somepass")
if stat || (e == nil) {
t.Fatal("failed to fail on ioutil.ReadFile error")
}
}
func TestVerifyPassFailsOnDisabledEntry(t *testing.T) {
ctx := newMockAuthCtx(_mock_ioutil_ReadFileEmpty, nil)
stat, e := VerifyPass(ctx, "disableduser", "!")
if stat || (e == nil) {
t.Fatal("failed to fail on disabled user entry")
}
}
////
func TestAuthUserByTokenFailsOnMissingEntryForHost(t *testing.T) {
ctx := newMockAuthCtx(_mock_ioutil_ReadFile, _mock_user_Lookup)
stat := AuthUserByToken(ctx, "johndoe", "hostZ", "abcdefg")
if stat {
t.Fatal("failed to fail on missing/mismatched host entry")
}
}
func TestAuthUserByTokenFailsOnMissingEntryForUser(t *testing.T) {
ctx := newMockAuthCtx(_mock_ioutil_ReadFile, _mock_user_Lookup)
stat := AuthUserByToken(ctx, "unkuser", "hostA", "abcdefg")
if stat {
t.Fatal("failed to fail on wrong user")
}
}
func TestAuthUserByTokenFailsOnUserLookupFailure(t *testing.T) {
ctx := newMockAuthCtx(_mock_ioutil_ReadFile, _mock_user_Lookup)
userlookup_arg_u = "baduser"
stat := AuthUserByToken(ctx, "johndoe", "hostA", "abcdefg")
if stat {
t.Fatal("failed to fail with bad return from user.Lookup()")
}
}
func TestAuthUserByTokenFailsOnMismatchedTokenForUser(t *testing.T) {
ctx := newMockAuthCtx(_mock_ioutil_ReadFile, _mock_user_Lookup)
stat := AuthUserByToken(ctx, "johndoe", "hostA", "badtoken")
if stat {
t.Fatal("failed to fail with valid user, bad token")
}
}
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")
if !stat {
t.Fatal("failed with valid user and token")
}
}
func TestAuthUserByPasswdFailsOnEmptyFile(t *testing.T) {
ctx := newMockAuthCtx(_mock_ioutil_ReadFileEmpty, _mock_user_Lookup)
userlookup_arg_u = "bobdobbs"
readfile_arg_f = "/etc/xs.passwd"
stat, _ := AuthUserByPasswd(ctx, userlookup_arg_u, "praisebob", readfile_arg_f)
if stat {
t.Fatal("failed to fail with missing xs.passwd file")
}
}
func TestAuthUserByPasswdFailsOnBadAuth(t *testing.T) {
ctx := newMockAuthCtx(_mock_ioutil_ReadFile, _mock_user_Lookup)
userlookup_arg_u = "bobdobbs"
readfile_arg_f = "/etc/xs.passwd"
stat, _ := AuthUserByPasswd(ctx, userlookup_arg_u, "wrongpass", readfile_arg_f)
if stat {
t.Fatal("failed to fail with valid user, incorrect passwd in xs.passwd file")
}
}
func TestAuthUserByPasswdFailsOnBadUser(t *testing.T) {
ctx := newMockAuthCtx(_mock_ioutil_ReadFile, _mock_user_Lookup)
userlookup_arg_u = "bobdobbs"
readfile_arg_f = "/etc/xs.passwd"
stat, _ := AuthUserByPasswd(ctx, userlookup_arg_u, "theotherbob", readfile_arg_f)
if stat {
t.Fatal("failed to fail on invalid user vs. xs.passwd file")
}
}
func TestAuthUserByPasswdPassesOnGoodAuth(t *testing.T) {
ctx := newMockAuthCtx(_mock_ioutil_ReadFile, _mock_user_Lookup)
userlookup_arg_u = "bobdobbs"
readfile_arg_f = "/etc/xs.passwd"
stat, _ := AuthUserByPasswd(ctx, userlookup_arg_u, "praisebob", readfile_arg_f)
if !stat {
t.Fatal("failed on valid user w/correct passwd in xs.passwd file")
}
}
func TestAuthUserByPasswdPassesOnOtherGoodAuth(t *testing.T) {
ctx := newMockAuthCtx(_mock_ioutil_ReadFile, _mock_user_Lookup)
userlookup_arg_u = "notbob"
readfile_arg_f = "/etc/xs.passwd"
stat, _ := AuthUserByPasswd(ctx, userlookup_arg_u, "imposter", readfile_arg_f)
if !stat {
t.Fatal("failed on valid user 2nd entry w/correct passwd in xs.passwd file")
}
}

114
bacillus/ci_pushbuild.sh Executable file
View file

@ -0,0 +1,114 @@
#!/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"
echo "workdir: ${BACILLUS_WORKDIR}"
mkdir -p "${BACILLUS_ARTFDIR}"
echo "---"
go env
echo "---"
echo "passed env:"
env
echo "---"
cd ${REPO}
branch=$(git for-each-ref --sort=-committerdate --format='%(refname)' | head -n 1)
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 "Performing remote command on @localhost via authtoken login ..."
tokentest=$(timeout 10 xs -x "echo -n FOO" @localhost)
if [ "${tokentest}" != "FOO" ]; then
echo "AUTHTOKEN LOGIN FAILED"
exit 1
else
echo "client cmd performed OK."
unset tokentest
fi
############
stage "Test(xc S->C)"
############
echo "Testing secure copy from server -> client ..."
./xc_testfiles.sh
tmpdir=$$
mkdir -p /tmp/$tmpdir
cd /tmp/$tmpdir
xc @localhost:${BACILLUS_WORKDIR}/build/xs/cptest .
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 -
rm -rf /tmp/$tmpdir
if [ $stat -eq "0" ]; then
echo "OK."
else
echo "FAILED!"
exit $stat
fi
############
stage "Test(xc 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 "Artifacts"
############
echo -n "Creating tarfile ..."
tar -cz --exclude=.git --exclude=cptest -f ${BACILLUS_ARTFDIR}/xs.tgz .
############
stage "Cleanup"
############
rm -rf cptest
echo
echo "--Done--"

View file

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

View file

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

27
go.mod
View file

@ -1,27 +0,0 @@
module blitter.com/go/xs
go 1.12
require (
blitter.com/go/cryptmt v1.0.0
blitter.com/go/goutmp v1.0.1
blitter.com/go/herradurakex v1.0.0
blitter.com/go/mtwist v1.0.1 // indirect
blitter.com/go/wanderer v0.8.0
git.schwanenlied.me/yawning/chacha20.git v0.0.0-20170904085104-e3b1f968fc63 // indirect
git.schwanenlied.me/yawning/kyber.git v0.0.0-20180530164001-a270899bd22c
git.schwanenlied.me/yawning/newhope.git v0.0.0-20170622154529-9598792ba8f2
github.com/jameskeane/bcrypt v0.0.0-20120420032655-c3cd44c1e20f
github.com/klauspost/cpuid v1.2.2 // indirect
github.com/klauspost/reedsolomon v1.9.3 // indirect
github.com/kr/pty v1.1.4
github.com/mattn/go-isatty v0.0.7
github.com/pkg/errors v0.8.1 // indirect
github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 // indirect
github.com/templexxx/xor v0.0.0-20181023030647-4e92f724b73b // indirect
github.com/tjfoc/gmsm v1.0.1 // indirect
github.com/xtaci/kcp-go v5.4.19+incompatible
golang.org/x/crypto v0.0.0-20190417174047-f416ebab96af
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 // indirect
golang.org/x/sys v0.0.0-20190416152802-12500544f89f
)

47
go.sum
View file

@ -1,47 +0,0 @@
blitter.com/go/cryptmt v1.0.0 h1:n+cNP/ReZrNe/w5FbD8DSfv0Wpj48nxhmMoLEk4hPXs=
blitter.com/go/cryptmt v1.0.0/go.mod h1:tdME2J3O4agaDAYIYNQzzuB28yVGnPSMmV3a/ucSU84=
blitter.com/go/goutmp v1.0.1 h1:jBqtp6pDwSbF4QEC3DjNfyaS8Nv5dFCOyaTfSbbb7TU=
blitter.com/go/goutmp v1.0.1/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/mtwist v1.0.0 h1:/Vg6k12+DC+fokeRrLApL22hZS/EqEazJTo/FDdbDog=
blitter.com/go/mtwist v1.0.0/go.mod h1:aU82Nx8+b1v8oZRNqImfEDzDTPim81rY0ACKAIclV18=
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/wanderer v0.8.0 h1:xT7lKdKLSyonVf0vZW6R46E3gz3B5pJ1VDBlsa4ULDI=
blitter.com/go/wanderer v0.8.0/go.mod h1:FX1pAnZ5woEavy5CUIZco0/Gc2Msb3U0zsmi+6Hs4Rw=
git.schwanenlied.me/yawning/chacha20.git v0.0.0-20170904085104-e3b1f968fc63 h1:bwZNsbw3qFbg6ox55HrA37nPmh+/wtJxZ7uWeiAdUUc=
git.schwanenlied.me/yawning/chacha20.git v0.0.0-20170904085104-e3b1f968fc63/go.mod h1:NYi4Ifd1g/YbhIDgDfw6t7QdsW4tofQWMX/+FiDtJWs=
git.schwanenlied.me/yawning/kyber.git v0.0.0-20180530164001-a270899bd22c/go.mod h1:QrbgzU5EL/1jaMD5pD4Tiikj3R5elPMa+RMwFUTGwQU=
git.schwanenlied.me/yawning/newhope.git v0.0.0-20170622154529-9598792ba8f2 h1:89TYv/+wotJ+QWrH5B/yN0pEQutr2V/5za0VoYiVGCM=
git.schwanenlied.me/yawning/newhope.git v0.0.0-20170622154529-9598792ba8f2/go.mod h1:weMqACFGzJs4Ni+K9shsRd02N4LkDrtGlkRxISK+II0=
github.com/jameskeane/bcrypt v0.0.0-20120420032655-c3cd44c1e20f/go.mod h1:u+9Snq0w+ZdYKi8BBoaxnEwWu0fY4Kvu9ByFpM51t1s=
github.com/klauspost/cpuid v1.2.2 h1:1xAgYebNnsb9LKCdLOvFWtAxGU/33mjJtyOVbmUa0Us=
github.com/klauspost/cpuid v1.2.2/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/reedsolomon v1.9.3 h1:N/VzgeMfHmLc+KHMD1UL/tNkfXAt8FnUqlgXGIduwAY=
github.com/klauspost/reedsolomon v1.9.3/go.mod h1:CwCi+NUr9pqSVktrkN+Ondf06rkhYZ/pcNv7fu+8Un4=
github.com/kr/pty v1.1.4 h1:5Myjjh3JY/NaAi4IsUbHADytDyl1VE1Y9PXDlL+P/VQ=
github.com/kr/pty v1.1.4/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc=
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
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-20181023030647-4e92f724b73b h1:mnG1fcsIB1d/3vbkBak2MM0u+vhGhlQwpeimUi7QncM=
github.com/templexxx/xor v0.0.0-20181023030647-4e92f724b73b/go.mod h1:5XA7W9S6mni3h5uvOC75dA3m9CCCaS83lltmc0ukdi4=
github.com/tjfoc/gmsm v1.0.1 h1:R11HlqhXkDospckjZEihx9SW/2VW0RgdwrykyWMFOQU=
github.com/tjfoc/gmsm v1.0.1/go.mod h1:XxO4hdhhrzAd+G4CjDqaOkd0hUzmtPR/d3EiBBMn/wc=
github.com/xtaci/kcp-go v5.4.19+incompatible h1:vv7Ar1D9WZGiv6deIOluxrC26Oin/2jFtx8sFU5tlvw=
github.com/xtaci/kcp-go v5.4.19+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-20190417174047-f416ebab96af h1:6qGQw30u837TXZbCmLFR9AVA+RjJU1LIbvk0oIkDZGY=
golang.org/x/crypto v0.0.0-20190417174047-f416ebab96af/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 h1:efeOvDhwQ29Dj3SdAV/MJf8oukgn+8D8WgaCaRMchF8=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190416152802-12500544f89f h1:1ZH9RnjNgLzh6YrsRp/c6ddZ8Lq0fq9xztNOoWJ2sz4=
golang.org/x/sys v0.0.0-20190416152802-12500544f89f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=

View file

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

156
logger/logger_bsd.go Normal file
View file

@ -0,0 +1,156 @@
// +build freebsd
// Package logger is a wrapper around UNIX syslog, so that it also may
// be wrapped with something else for Windows (Sadly, the stdlib log/syslog
// is frozen, and there is no Windows implementation.)
package logger
import (
sl "log/syslog"
)
// Priority is the logger priority
type Priority = sl.Priority
// Writer is a syslog Writer
type Writer = sl.Writer
// nolint: golint
const (
// Severity.
// From /usr/include/sys/syslog.h.
// These are the same on Linux, BSD, and OS X.
LOG_EMERG Priority = iota
LOG_ALERT
LOG_CRIT
LOG_ERR
LOG_WARNING
LOG_NOTICE
LOG_INFO
LOG_DEBUG
)
// nolint: golint
const (
// Facility.
// From /usr/include/sys/syslog.h.
// These are the same up to LOG_FTP on Linux, BSD, and OS X.
LOG_KERN Priority = iota << 3
LOG_USER
LOG_MAIL
LOG_DAEMON
LOG_AUTH
LOG_SYSLOG
LOG_LPR
LOG_NEWS
LOG_UUCP
LOG_CRON
LOG_AUTHPRIV
LOG_FTP
_ // unused
_ // unused
_ // unused
_ // unused
LOG_LOCAL0
LOG_LOCAL1
LOG_LOCAL2
LOG_LOCAL3
LOG_LOCAL4
LOG_LOCAL5
LOG_LOCAL6
LOG_LOCAL7
)
var (
l *sl.Writer
)
// New returns a new log Writer.
func New(flags Priority, tag string) (w *Writer, e error) {
w, e = sl.New(flags, tag)
l = w
return w, e
}
// Alert returns a log Alert error
func Alert(s string) error {
if l != nil {
return l.Alert(s)
}
return nil
}
// LogClose closes the log Writer.
func LogClose() error {
if l != nil {
return l.Close()
}
return nil
}
// LogCrit returns a log Alert error
func LogCrit(s string) error {
if l != nil {
return l.Crit(s)
}
return nil
}
// LogDebug returns a log Debug error
func LogDebug(s string) error {
if l != nil {
return l.Debug(s)
}
return nil
}
// LogEmerg returns a log Emerg error
func LogEmerg(s string) error {
if l != nil {
return l.Emerg(s)
}
return nil
}
// LogErr returns a log Err error
func LogErr(s string) error {
if l != nil {
return l.Err(s)
}
return nil
}
// LogInfo returns a log Info error
func LogInfo(s string) error {
if l != nil {
return l.Info(s)
}
return nil
}
// LogNotice returns a log Notice error
func LogNotice(s string) error {
if l != nil {
return l.Notice(s)
}
return nil
}
// LogWarning returns a log Warning error
func LogWarning(s string) error {
if l != nil {
return l.Warning(s)
}
return nil
}
// LogWrite writes to the logger at default level
func LogWrite(b []byte) (int, error) {
if l != nil {
return l.Write(b)
}
return len(b),nil
}

View file

@ -11,6 +11,7 @@ import (
// Priority is the logger priority
type Priority = sl.Priority
// Writer is a syslog Writer
type Writer = sl.Writer
@ -75,50 +76,81 @@ func New(flags Priority, tag string) (w *Writer, e error) {
// Alert returns a log Alert error
func Alert(s string) error {
if l != nil {
return l.Alert(s)
}
return nil
}
// LogClose closes the log Writer.
func LogClose() error {
if l != nil {
return l.Close()
}
return nil
}
// LogCrit returns a log Alert error
func LogCrit(s string) error {
if l != nil {
return l.Crit(s)
}
return nil
}
// LogDebug returns a log Debug error
func LogDebug(s string) error {
if l != nil {
return l.Debug(s)
}
return nil
}
// LogEmerg returns a log Emerg error
func LogEmerg(s string) error {
if l != nil {
return l.Emerg(s)
}
return nil
}
// LogErr returns a log Err error
func LogErr(s string) error {
if l != nil {
return l.Err(s)
}
return nil
}
// LogInfo returns a log Info error
func LogInfo(s string) error {
if l != nil {
return l.Info(s)
}
return nil
}
// LogNotice returns a log Notice error
func LogNotice(s string) error {
if l != nil {
return l.Notice(s)
}
return nil
}
// LogWarning returns a log Warning error
func LogWarning(s string) error {
if l != nil {
return l.Warning(s)
}
return nil
}
// LogWrite writes to the logger at default level
func LogWrite(b []byte) (int, error) {
if l != nil {
return l.Write(b)
}
return len(b),nil
}

View file

@ -1,5 +1,5 @@
// +build windows
//
// Wrapper around UNIX syslog, so that it also may be wrapped
// with something else for Windows.
package logger

View file

@ -2,7 +2,7 @@ package xs
// Package xs - a secure terminal client/server written from scratch in Go
//
// Copyright (c) 2017-2019 Russell Magee
// Copyright (c) 2017-2020 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 (essential the password) used to
// SetAuthCookie stores the authcookie (essentially the password) used to
// authenticate the Session.
func (h *Session) SetAuthCookie(a []byte) {
h.authCookie = a

30
session_test.go Normal file
View file

@ -0,0 +1,30 @@
package xs
import (
"testing"
)
func _newMockSession() (s *Session) {
s = &Session{op: []byte("A"),
who: []byte("johndoe"),
connhost: []byte("host"),
termtype: []byte("vt100"),
cmd: []byte("/bin/false"),
authCookie: []byte("authcookie"),
status: 0}
return s
}
func TestSessionAuthCookieShowTrue(t *testing.T) {
sess := _newMockSession()
if string(sess.AuthCookie(true)) != string(sess.authCookie) {
t.Fatal("Failed to return unredacted authcookie on request")
}
}
func TestSessionAuthCookieShowFalse(t *testing.T) {
sess := _newMockSession()
if string(sess.AuthCookie(false)) != string("**REDACTED**") {
t.Fatal("Failed to return redacted authcookie on request")
}
}

View file

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

128
termmode_bsd.go Normal file
View file

@ -0,0 +1,128 @@
// +build freebsd
package xs
import (
"errors"
"io"
"unsafe"
unix "golang.org/x/sys/unix"
)
/* -------------
* minimal terminal APIs brought in from ssh/terminal
* (they have no real business being there as they aren't specific to
* ssh, but as of Go v1.10, late 2019, core go stdlib hasn't yet done
* the planned terminal lib reorgs.)
* ------------- */
// From github.com/golang/crypto/blob/master/ssh/terminal/util_linux.go
const getTermios = unix.TIOCGETA
const setTermios = unix.TIOCSETA
// From github.com/golang/crypto/blob/master/ssh/terminal/util.go
// State contains the state of a terminal.
type State struct {
termios unix.Termios
}
// MakeRaw put the terminal connected to the given file descriptor into raw
// mode and returns the previous state of the terminal so that it can be
// restored.
func MakeRaw(fd uintptr) (*State, error) {
var oldState State
if _, _, err := unix.Syscall(unix.SYS_IOCTL, fd, getTermios, uintptr(unsafe.Pointer(&oldState.termios))); err != 0 {
return nil, err
}
newState := oldState.termios
newState.Iflag &^= (unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON)
newState.Oflag &^= unix.OPOST
newState.Lflag &^= (unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN)
newState.Cflag &^= (unix.CSIZE | unix.PARENB)
newState.Cflag |= unix.CS8
newState.Cc[unix.VMIN] = 1
newState.Cc[unix.VTIME] = 0
if _, _, err := unix.Syscall(unix.SYS_IOCTL, fd, setTermios, uintptr(unsafe.Pointer(&newState))); err != 0 {
return nil, err
}
return &oldState, nil
}
// Restore restores the terminal connected to the given file descriptor to a
// previous state.
func Restore(fd uintptr, state *State) error {
if state != nil {
if _, _, err := unix.Syscall(unix.SYS_IOCTL, fd, setTermios, uintptr(unsafe.Pointer(state))); err != 0 {
return err
} else {
return nil
}
} else {
return errors.New("nil State")
}
}
// ReadPassword reads a line of input from a terminal without local echo. This
// is commonly used for inputting passwords and other sensitive data. The slice
// returned does not include the \n.
func ReadPassword(fd uintptr) ([]byte, error) {
var oldState State
if _, _, err := unix.Syscall(unix.SYS_IOCTL, fd, getTermios, uintptr(unsafe.Pointer(&oldState.termios))); err != 0 {
return nil, err
}
newState := oldState.termios
newState.Lflag &^= unix.ECHO
newState.Lflag |= unix.ICANON | unix.ISIG
newState.Iflag |= unix.ICRNL
if _, _, err := unix.Syscall(unix.SYS_IOCTL, fd, setTermios, uintptr(unsafe.Pointer(&newState))); err != 0 {
return nil, err
}
defer func() {
unix.Syscall(unix.SYS_IOCTL, fd, setTermios, uintptr(unsafe.Pointer(&oldState.termios)))
}()
return readPasswordLine(passwordReader(fd))
}
// passwordReader is an io.Reader that reads from a specific file descriptor.
type passwordReader int
func (r passwordReader) Read(buf []byte) (int, error) {
return unix.Read(int(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
}
if err != nil {
if err == io.EOF && len(ret) > 0 {
return ret, nil
}
return ret, err
}
}
}

View file

@ -30,8 +30,8 @@ type State struct {
// MakeRaw put the terminal connected to the given file descriptor into raw
// mode and returns the previous state of the terminal so that it can be
// restored.
func MakeRaw(fd int) (*State, error) {
termios, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
func MakeRaw(fd uintptr) (*State, error) {
termios, err := unix.IoctlGetTermios(int(fd), ioctlReadTermios)
if err != nil {
return nil, err
}
@ -47,7 +47,7 @@ func MakeRaw(fd int) (*State, error) {
termios.Cflag |= unix.CS8
termios.Cc[unix.VMIN] = 1
termios.Cc[unix.VTIME] = 0
if err := unix.IoctlSetTermios(fd, ioctlWriteTermios, termios); err != nil {
if err := unix.IoctlSetTermios(int(fd), ioctlWriteTermios, termios); err != nil {
return nil, err
}
@ -56,8 +56,8 @@ func MakeRaw(fd int) (*State, error) {
// GetState returns the current state of a terminal which may be useful to
// restore the terminal after a signal.
func GetState(fd int) (*State, error) {
termios, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
func GetState(fd uintptr) (*State, error) {
termios, err := unix.IoctlGetTermios(int(fd), ioctlReadTermios)
if err != nil {
return nil, err
}
@ -67,9 +67,9 @@ func GetState(fd int) (*State, error) {
// Restore restores the terminal connected to the given file descriptor to a
// previous state.
func Restore(fd int, state *State) error {
func Restore(fd uintptr, state *State) error {
if state != nil {
return unix.IoctlSetTermios(fd, ioctlWriteTermios, &state.termios)
return unix.IoctlSetTermios(int(fd), ioctlWriteTermios, &state.termios)
} else {
return errors.New("nil State")
}
@ -78,8 +78,8 @@ func Restore(fd int, state *State) error {
// ReadPassword reads a line of input from a terminal without local echo. This
// is commonly used for inputting passwords and other sensitive data. The slice
// returned does not include the \n.
func ReadPassword(fd int) ([]byte, error) {
termios, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
func ReadPassword(fd uintptr) ([]byte, error) {
termios, err := unix.IoctlGetTermios(int(fd), ioctlReadTermios)
if err != nil {
return nil, err
}
@ -88,12 +88,12 @@ func ReadPassword(fd int) ([]byte, error) {
newState.Lflag &^= unix.ECHO
newState.Lflag |= unix.ICANON | unix.ISIG
newState.Iflag |= unix.ICRNL
if err := unix.IoctlSetTermios(fd, ioctlWriteTermios, &newState); err != nil {
if err := unix.IoctlSetTermios(int(fd), ioctlWriteTermios, &newState); err != nil {
return nil, err
}
defer func() {
_ = unix.IoctlSetTermios(fd, ioctlWriteTermios, termios) // nolint: gosec
_ = unix.IoctlSetTermios(int(fd), ioctlWriteTermios, termios) // nolint: gosec
}()
return readPasswordLine(passwordReader(fd))

View file

@ -1,5 +1,5 @@
// +build windows
//
// Note the terminal manipulation functions herein are mostly stubs. They
// don't really do anything and the xs demo client depends on a wrapper
// script using the 'stty' tool to actually set the proper mode for
@ -27,7 +27,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(fd int) (*State, error) {
func MakeRaw(fd uintptr) (*State, error) {
// This doesn't really work. The exec.Command() runs a sub-shell
// so the stty mods don't affect the client process.
cmd := exec.Command("stty", "-echo raw")
@ -37,13 +37,13 @@ func MakeRaw(fd int) (*State, error) {
// GetState returns the current state of a terminal which may be useful to
// restore the terminal after a signal.
func GetState(fd int) (*State, error) {
func GetState(fd uintptr) (*State, error) {
return &State{}, nil
}
// Restore restores the terminal connected to the given file descriptor to a
// previous state.
func Restore(fd int, state *State) error {
func Restore(fd uintptr, state *State) error {
cmd := exec.Command("stty", "echo cooked")
cmd.Run()
return nil
@ -52,7 +52,7 @@ func Restore(fd int, state *State) error {
// ReadPassword reads a line of input from a terminal without local echo. This
// is commonly used for inputting passwords and other sensitive data. The slice
// returned does not include the \n.
func ReadPassword(fd int) ([]byte, error) {
func ReadPassword(fd uintptr) ([]byte, error) {
return readPasswordLine(passwordReader(fd))
}

14
xc_testfiles.sh Executable file
View file

@ -0,0 +1,14 @@
#!/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,11 +1,17 @@
.PHONY: clean all vis lint
ifeq ($(GARBLE),y)
GO = garble -literals -tiny -debugdir=garbled
else
GO = go
endif
EXTPKGS = bytes,errors,flag,fmt,internal,io,log,net,os,path,runtime,time,strings,sync,syscall,binary,encoding
EXE = $(notdir $(shell pwd))
all:
echo "BUILDOPTS:" $(BUILDOPTS)
go build $(BUILDOPTS) .
$(GO) build $(BUILDOPTS) .
clean:
$(RM) $(EXE) $(EXE).exe
@ -15,4 +21,4 @@ vis:
../fixup-gv.sh xs.go && cat xs-vis.gv | dot -Tpng -oxs-vis-fixedup.png
lint:
-gometalinter --deadline=60s | sort
-golangci-lint run

View file

@ -1,7 +1,7 @@
#!/bin/bash
#
## This wrapper may be used within the MSYS/mintty Windows
## shell environment to have a functioning hkexsh client with
## shell environment to have a functioning xs client with
## working 'raw' mode and hidden password entry.
##
## mintty uses named pipes and ptys to get a more POSIX-like
@ -28,10 +28,12 @@ cleanup() {
stty sane
}
me="$(basename "$(test -L "$0" && readlink "$0" || echo "$0")")"
if [ ${1}x == "-hx" ]; then
./hkexsh -h
_${me} -h
else
stty -echo raw icrnl
./hkexsh $@
_${me} $@
fi

View file

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

Before After
Before After

View file

@ -10,7 +10,7 @@ digraph gocallvis {
pad="0.0";
nodesep="0.35";
node [shape="ellipse" style="filled" fillcolor="honeydew" fontname="Verdana" penwidth="1.0" margin="0.05,0.0"];
node [shape="box" style="filled,rounded" fillcolor="honeydew" fontname="Verdana" penwidth="1.0" margin="0.05,0.0"];
edge [minlen="2"]
subgraph "cluster_focus" {
@ -20,129 +20,135 @@ labelloc="t";
labeljust="c";
fontsize="18";
"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" ]
"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" ]
subgraph "cluster_blitter.com/go/xs" {
penwidth="0.8";
style="filled";
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="bold";
label="[xs]";
URL="/?f=blitter.com/go/xs";
fontname="Tahoma bold";
"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" ]
"blitter.com/go/xs.Restore" [ tooltip="blitter.com/go/xs.Restore | defined in termmode_linux.go:70" fillcolor="moccasin" label="Restore" penwidth="1.5" ]
"blitter.com/go/xs.GetTool" [ fillcolor="moccasin" label="GetTool" penwidth="1.5" tooltip="blitter.com/go/xs.GetTool | defined in auth.go:217" ]
"blitter.com/go/xs.MakeRaw" [ fillcolor="moccasin" label="MakeRaw" penwidth="1.5" tooltip="blitter.com/go/xs.MakeRaw | defined in termmode_linux.go:33" ]
"blitter.com/go/xs.ReadPassword" [ tooltip="blitter.com/go/xs.ReadPassword | defined in termmode_linux.go:81" fillcolor="moccasin" label="ReadPassword" penwidth="1.5" ]
"blitter.com/go/xs.NewSession" [ fillcolor="moccasin" label="NewSession" penwidth="1.5" tooltip="blitter.com/go/xs.NewSession | defined in session.go:130" ]
subgraph "cluster_*blitter.com/go/xs.Session" {
fontsize="15";
fontcolor="#222222";
labelloc="b";
style="rounded,filled";
fillcolor="wheat2";
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";
style="rounded,filled";
"(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" ]
"(*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";
fontcolor="#222222";
labelloc="b";
style="rounded,filled";
fillcolor="wheat2";
label="(Session)";
tooltip="type: blitter.com/go/xs.Session";
"(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" ]
}
}
subgraph "cluster_blitter.com/go/xs/logger" {
fontsize="16";
penwidth="0.8";
fontsize="16";
fontname="Tahoma bold";
URL="/?f=blitter.com/go/xs/logger";
penwidth="0.8";
tooltip="package: blitter.com/go/xs/logger";
style="filled";
fillcolor="lightyellow";
fontname="bold";
rank="sink";
label="[logger]";
tooltip="package: blitter.com/go/xs/logger";
label="logger";
"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" ]
"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" ]
}
subgraph "cluster_blitter.com/go/xs/spinsult" {
fillcolor="lightyellow";
fontname="bold";
label="[spinsult]";
penwidth="0.8";
style="filled";
fontname="Tahoma bold";
rank="sink";
fontsize="16";
fillcolor="lightyellow";
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" [ fillcolor="moccasin" label="GetSentence" penwidth="1.5" ]
"blitter.com/go/xs/spinsult.GetSentence" [ label="GetSentence" penwidth="1.5" tooltip="blitter.com/go/xs/spinsult.GetSentence | defined in spinsult.go:43" fillcolor="moccasin" ]
}
subgraph "cluster_blitter.com/go/xs/xsnet" {
penwidth="0.8";
fillcolor="lightyellow";
fontname="bold";
fontname="Tahoma bold";
rank="sink";
URL="/?f=blitter.com/go/xs/xsnet";
label="xsnet";
tooltip="package: blitter.com/go/xs/xsnet";
penwidth="0.8";
fontsize="16";
style="filled";
label="[xsnet]";
tooltip="package: blitter.com/go/xs/xsnet";
fillcolor="lightyellow";
URL="/?f=blitter.com/go/xs/xsnet";
"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" ]
"blitter.com/go/xs/xsnet.Init" [ fillcolor="moccasin" label="Init" penwidth="1.5" tooltip="blitter.com/go/xs/xsnet.Init | defined in net.go:198" ]
"blitter.com/go/xs/xsnet.Dial" [ fillcolor="moccasin" label="Dial" penwidth="1.5" tooltip="blitter.com/go/xs/xsnet.Dial | defined in net.go:899" ]
subgraph "cluster_*blitter.com/go/xs/xsnet.Conn" {
fontcolor="#222222";
@ -154,29 +160,29 @@ tooltip="type: *blitter.com/go/xs/xsnet.Conn";
penwidth="0.5";
fontsize="15";
"(*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" ]
"(*blitter.com/go/xs/xsnet.Conn).WritePacket" [ fillcolor="moccasin" label="WritePacket" penwidth="1.5" tooltip="(*blitter.com/go/xs/xsnet.Conn).WritePacket | defined in net.go:1437" ]
"(*blitter.com/go/xs/xsnet.Conn).SetStatus" [ label="SetStatus" penwidth="1.5" tooltip="(*blitter.com/go/xs/xsnet.Conn).SetStatus | defined in net.go:218" fillcolor="moccasin" ]
"(*blitter.com/go/xs/xsnet.Conn).GetStatus" [ fillcolor="moccasin" label="GetStatus" penwidth="1.5" tooltip="(*blitter.com/go/xs/xsnet.Conn).GetStatus | defined in net.go:214" ]
"(*blitter.com/go/xs/xsnet.Conn).Close" [ fillcolor="moccasin" label="Close" penwidth="1.5" tooltip="(*blitter.com/go/xs/xsnet.Conn).Close | defined in net.go:979" ]
"(*blitter.com/go/xs/xsnet.Conn).SetupChaff" [ penwidth="1.5" tooltip="(*blitter.com/go/xs/xsnet.Conn).SetupChaff | defined in net.go:1563" fillcolor="moccasin" label="SetupChaff" ]
"(*blitter.com/go/xs/xsnet.Conn).EnableChaff" [ fillcolor="moccasin" label="EnableChaff" penwidth="1.5" tooltip="(*blitter.com/go/xs/xsnet.Conn).EnableChaff | defined in net.go:1546" ]
"(*blitter.com/go/xs/xsnet.Conn).DisableChaff" [ fillcolor="moccasin" label="DisableChaff" penwidth="1.5" tooltip="(*blitter.com/go/xs/xsnet.Conn).DisableChaff | defined in net.go:1553" ]
"(*blitter.com/go/xs/xsnet.Conn).ShutdownChaff" [ tooltip="(*blitter.com/go/xs/xsnet.Conn).ShutdownChaff | defined in net.go:1558" fillcolor="moccasin" label="ShutdownChaff" penwidth="1.5" ]
}
subgraph "cluster_blitter.com/go/xs/xsnet.Conn" {
labelloc="b";
style="rounded,filled";
fillcolor="wheat2";
label="(Conn)";
label="(Conn)";
tooltip="type: blitter.com/go/xs/xsnet.Conn";
penwidth="0.5";
fontsize="15";
fontcolor="#222222";
labelloc="b";
style="rounded,filled";
fillcolor="wheat2";
"(blitter.com/go/xs/xsnet.Conn).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" ]
"(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" ]
}
@ -184,41 +190,41 @@ fontcolor="#222222";
subgraph "cluster_compress/flate" {
fontsize="16";
fillcolor="#E0FFE1";
label="[compress/flate]";
style="filled";
fillcolor="lightyellow";
label="flate";
URL="/?f=compress/flate";
penwidth="0.8";
style="filled";
fontname="bold";
fontname="Tahoma bold";
rank="sink";
tooltip="package: compress/flate";
subgraph "cluster_compress/flate.CorruptInputError" {
tooltip="type: 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";
penwidth="0.5";
fontsize="15";
fontcolor="#222222";
labelloc="b";
style="rounded,filled";
fillcolor="#c2e3c2";
label="(CorruptInputError)";
"(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";
fillcolor="wheat2";
label="(InternalError)";
tooltip="type: compress/flate.InternalError";
penwidth="0.5";
"(compress/flate.InternalError).Error" [ fillcolor="#adedad" label="Error" penwidth="1.5" ]
"(compress/flate.InternalError).Error" [ tooltip="(compress/flate.InternalError).Error | defined in inflate.go:42" fillcolor="moccasin" label="Error" penwidth="1.5" ]
}
@ -227,210 +233,224 @@ penwidth="0.5";
subgraph "cluster_context" {
fontsize="16";
style="filled";
label="context";
penwidth="0.8";
fontname="bold";
fillcolor="lightyellow";
fontname="Tahoma bold";
rank="sink";
label="[context]";
URL="/?f=context";
tooltip="package: context";
fillcolor="#E0FFE1";
subgraph "cluster_context.deadlineExceededError" {
fontsize="15";
fontcolor="#222222";
labelloc="b";
style="rounded,filled";
fillcolor="#c2e3c2";
style="rounded,filled";
fillcolor="wheat2";
label="(deadlineExceededError)";
tooltip="type: context.deadlineExceededError";
penwidth="0.5";
fontsize="15";
fontcolor="#222222";
labelloc="b";
"(context.deadlineExceededError).Error" [ fillcolor="#adedad" label="Error" penwidth="1.5" ]
"(context.deadlineExceededError).Error" [ fillcolor="moccasin" label="Error" penwidth="1.5" tooltip="(context.deadlineExceededError).Error | defined in context.go:165" ]
}
}
subgraph "cluster_crypto/aes" {
style="filled";
fillcolor="#E0FFE1";
penwidth="0.8";
style="filled";
fontname="Tahoma bold";
rank="sink";
label="[crypto/aes]";
penwidth="0.8";
fontsize="16";
fontname="bold";
label="aes";
URL="/?f=crypto/aes";
tooltip="package: crypto/aes";
fontsize="16";
fillcolor="lightyellow";
subgraph "cluster_crypto/aes.KeySizeError" {
fontcolor="#222222";
labelloc="b";
style="rounded,filled";
fillcolor="#c2e3c2";
style="rounded,filled";
fillcolor="wheat2";
label="(KeySizeError)";
tooltip="type: crypto/aes.KeySizeError";
penwidth="0.5";
fontsize="15";
fontcolor="#222222";
labelloc="b";
"(crypto/aes.KeySizeError).Error" [ fillcolor="#adedad" label="Error" penwidth="1.5" ]
"(crypto/aes.KeySizeError).Error" [ fillcolor="moccasin" label="Error" penwidth="1.5" tooltip="(crypto/aes.KeySizeError).Error | defined in cipher.go:24" ]
}
}
subgraph "cluster_crypto/tls" {
tooltip="package: crypto/tls";
penwidth="0.8";
fontsize="16";
style="filled";
fontname="bold";
rank="sink";
label="[crypto/tls]";
fontname="Tahoma bold";
URL="/?f=crypto/tls";
penwidth="0.8";
fillcolor="#E0FFE1";
tooltip="package: crypto/tls";
style="filled";
fillcolor="lightyellow";
rank="sink";
label="tls";
subgraph "cluster_crypto/tls.RecordHeaderError" {
fontcolor="#222222";
labelloc="b";
style="rounded,filled";
fillcolor="#c2e3c2";
label="(RecordHeaderError)";
tooltip="type: crypto/tls.RecordHeaderError";
subgraph "cluster_*crypto/tls.permanentError" {
label="(*permanentError)";
tooltip="type: *crypto/tls.permanentError";
penwidth="0.5";
fontsize="15";
"(crypto/tls.RecordHeaderError).Error" [ fillcolor="#adedad" label="Error" penwidth="1.5" ]
}
subgraph "cluster_crypto/tls.alert" {
penwidth="0.5";
fontsize="15";
fontcolor="#222222";
labelloc="b";
style="rounded,filled";
fillcolor="#c2e3c2";
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";
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" ]
}
subgraph "cluster_crypto/tls.alert" {
fontcolor="#222222";
labelloc="b";
style="rounded,filled";
fillcolor="wheat2";
label="(alert)";
tooltip="type: crypto/tls.alert";
penwidth="0.5";
fontsize="15";
"(crypto/tls.alert).Error" [ fillcolor="#adedad" label="Error" penwidth="1.5" ]
"(crypto/tls.alert).Error" [ fillcolor="moccasin" label="Error" penwidth="1.5" tooltip="(crypto/tls.alert).Error | defined in alert.go:97" ]
}
}
subgraph "cluster_crypto/x509" {
penwidth="0.8";
tooltip="package: crypto/x509";
fontsize="16";
URL="/?f=crypto/x509";
style="filled";
fillcolor="#E0FFE1";
fontname="bold";
fillcolor="lightyellow";
fontname="Tahoma bold";
rank="sink";
label="[crypto/x509]";
URL="/?f=crypto/x509";
label="x509";
tooltip="package: crypto/x509";
penwidth="0.8";
fontsize="16";
subgraph "cluster_crypto/x509.CertificateInvalidError" {
penwidth="0.5";
label="(CertificateInvalidError)";
tooltip="type: crypto/x509.CertificateInvalidError";
penwidth="0.5";
fontsize="15";
fontcolor="#222222";
labelloc="b";
style="rounded,filled";
fillcolor="#c2e3c2";
label="(CertificateInvalidError)";
tooltip="type: crypto/x509.CertificateInvalidError";
fillcolor="wheat2";
"(crypto/x509.CertificateInvalidError).Error" [ fillcolor="#adedad" label="Error" penwidth="1.5" ]
"(crypto/x509.CertificateInvalidError).Error" [ fillcolor="moccasin" label="Error" penwidth="1.5" tooltip="(crypto/x509.CertificateInvalidError).Error | defined in verify.go:67" ]
}
subgraph "cluster_crypto/x509.HostnameError" {
labelloc="b";
style="rounded,filled";
fillcolor="#c2e3c2";
label="(HostnameError)";
tooltip="type: crypto/x509.HostnameError";
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="#adedad" label="Error" penwidth="1.5" ]
"(crypto/x509.HostnameError).Error" [ fillcolor="moccasin" label="Error" penwidth="1.5" tooltip="(crypto/x509.HostnameError).Error | defined in verify.go:98" ]
}
subgraph "cluster_crypto/x509.SystemRootsError" {
fontsize="15";
tooltip="type: crypto/x509.SystemRootsError";
penwidth="0.5";
fontsize="15";
fontcolor="#222222";
labelloc="b";
style="rounded,filled";
fillcolor="#c2e3c2";
fillcolor="wheat2";
label="(SystemRootsError)";
tooltip="type: crypto/x509.SystemRootsError";
penwidth="0.5";
"(crypto/x509.SystemRootsError).Error" [ fillcolor="#adedad" label="Error" penwidth="1.5" ]
"(crypto/x509.SystemRootsError).Error" [ label="Error" penwidth="1.5" tooltip="(crypto/x509.SystemRootsError).Error | defined in verify.go:159" fillcolor="moccasin" ]
}
subgraph "cluster_crypto/x509.UnhandledCriticalExtension" {
style="rounded,filled";
fillcolor="#c2e3c2";
label="(UnhandledCriticalExtension)";
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" [ fillcolor="#adedad" label="Error" penwidth="1.5" ]
"(crypto/x509.UnhandledCriticalExtension).Error" [ label="Error" penwidth="1.5" tooltip="(crypto/x509.UnhandledCriticalExtension).Error | defined in x509.go:893" fillcolor="moccasin" ]
}
subgraph "cluster_crypto/x509.UnknownAuthorityError" {
style="rounded,filled";
fillcolor="#c2e3c2";
fontsize="15";
fontcolor="#222222";
labelloc="b";
style="rounded,filled";
fillcolor="wheat2";
label="(UnknownAuthorityError)";
tooltip="type: crypto/x509.UnknownAuthorityError";
penwidth="0.5";
fontsize="15";
fontcolor="#222222";
labelloc="b";
"(crypto/x509.UnknownAuthorityError).Error" [ fillcolor="#adedad" label="Error" penwidth="1.5" ]
"(crypto/x509.UnknownAuthorityError).Error" [ tooltip="(crypto/x509.UnknownAuthorityError).Error | defined in verify.go:138" fillcolor="moccasin" label="Error" penwidth="1.5" ]
}
}
subgraph "cluster_github.com/mattn/go-isatty" {
fontname="bold";
penwidth="0.8";
fillcolor="lightyellow";
penwidth="0.8";
fontsize="16";
fontname="Tahoma bold";
rank="sink";
label="[isatty]";
style="filled";
fillcolor="lightyellow";
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" [ label="IsTerminal" penwidth="1.5" fillcolor="moccasin" ]
"github.com/mattn/go-isatty.IsTerminal" [ penwidth="1.5" tooltip="github.com/mattn/go-isatty.IsTerminal | defined in isatty_tcgets.go:10" fillcolor="moccasin" label="IsTerminal" ]
}
subgraph "cluster_github.com/pkg/errors" {
style="filled";
fillcolor="lightyellow";
fontname="Tahoma bold";
label="errors";
URL="/?f=github.com/pkg/errors";
rank="sink";
label="[errors]";
tooltip="package: github.com/pkg/errors";
rank="sink";
penwidth="0.8";
fontsize="16";
fontname="bold";
style="filled";
fillcolor="lightyellow";
subgraph "cluster_*github.com/pkg/errors.fundamental" {
@ -443,107 +463,160 @@ labelloc="b";
style="rounded,filled";
fillcolor="wheat2";
"(*github.com/pkg/errors.fundamental).Error" [ penwidth="1.5" fillcolor="moccasin" label="Error" ]
"(*github.com/pkg/errors.fundamental).Error" [ fillcolor="moccasin" label="Error" penwidth="1.5" tooltip="(*github.com/pkg/errors.fundamental).Error | defined in errors.go:125" ]
}
}
subgraph "cluster_math/rand" {
penwidth="0.8";
fillcolor="#E0FFE1";
style="filled";
fillcolor="lightyellow";
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";
"math/rand.Intn" [ fillcolor="#adedad" label="Intn" penwidth="1.5" ]
subgraph "cluster_vendor/golang.org/x/net/idna.labelError" {
style="rounded,filled";
fillcolor="wheat2";
label="(labelError)";
tooltip="type: vendor/golang.org/x/net/idna.labelError";
penwidth="0.5";
fontsize="15";
fontcolor="#222222";
labelloc="b";
"(vendor/golang.org/x/net/idna.labelError).Error" [ fillcolor="moccasin" label="Error" penwidth="1.5" tooltip="(vendor/golang.org/x/net/idna.labelError).Error | defined in idna10.0.0.go:324" ]
}
subgraph "cluster_vendor/golang.org/x/net/idna.runeError" {
style="rounded,filled";
fillcolor="wheat2";
label="(runeError)";
tooltip="type: vendor/golang.org/x/net/idna.runeError";
penwidth="0.5";
fontsize="15";
fontcolor="#222222";
labelloc="b";
"(vendor/golang.org/x/net/idna.runeError).Error" [ tooltip="(vendor/golang.org/x/net/idna.runeError).Error | defined in idna10.0.0.go:331" fillcolor="moccasin" label="Error" penwidth="1.5" ]
}
}
"blitter.com/go/xs/xs.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" ]
}
"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" ]
}

564
xs/xs.go
View file

@ -1,6 +1,6 @@
// xs client
//
// Copyright (c) 2017-2019 Russell Magee
// Copyright (c) 2017-2020 Russell Magee
// Licensed under the terms of the MIT license (see LICENSE.mit in this
// distribution)
//
@ -14,10 +14,8 @@ import (
"flag"
"fmt"
"io"
"io/ioutil"
"log"
"math/rand"
"net"
"os"
"os/exec"
"os/user"
@ -31,7 +29,7 @@ import (
"time"
"net/http"
_ "net/http/pprof"
_ "net/http/pprof" //nolint:gosec
xs "blitter.com/go/xs"
"blitter.com/go/xs/logger"
@ -58,27 +56,39 @@ 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 @@@\r\n" +
"@@@@ @@@@@@@@@@@@@@@@@@@@@@ I @@@ Bob\r\n" +
"@@@@ @@@@@@@@@@@@@@@@@@@@*@[ i @@@\r\n" +
"@@@@ @@@@@@@@@@@@@@@@@@@@[][ | ]@@@\r\n" +
"@@@@ @@@@@@@@@@@@@@@@@@@@[][ | ]@@@ bOb\r\n" +
"@@@@ ~_,,_ ~@@@@@@@~ ____~ @ @@@\r\n" +
"@@@@ _~ , , `@@@~ _ _`@ ]L J@@@\r\n" +
"@@@@ _~ , , `@@@~ _ _`@ ]L J@@@ o\r\n" +
"@@@@ , @@w@ww+ @@@ww``,,@w@ ][ @@@@\r\n" +
"@@@@, @@@@www@@@ @@@@@@@ww@@@@@[ @@@@\r\n" +
"@@@@, @@@@www@@@ @@@@@@@ww@@@@@[ @@@@ BOB\r\n" +
"@@@@@_|| @@@@@@P' @@P@@@@@@@@@@@[|c@@@@\r\n" +
"@@@@@@w| '@@P~ P]@@@-~, ~Y@@^'],@@@@@@\r\n" +
"@@@@@@w| '@@P~ P]@@@-~, ~Y@@^'],@@@@@@ . o\r\n" +
"@@@@@@@[ _ _J@@Tk ]]@@@@@@\r\n" +
"@@@@@@@@,@ @@, c,,,,,,,y ,w@@[ ,@@@@@@@\r\n" +
"@@@@@@@@@ i @w ====--_@@@@@ @@@@@@@@\r\n" +
"@@@@@@@@@ i @w ====--_@@@@@ @@@@@@@@ o .\r\n" +
"@@@@@@@@@@`,P~ _ ~^^^^Y@@@@@ @@@@@@@@@\r\n" +
"@@@@^^=^@@^ ^' ,ww,w@@@@@ _@@@@@@@@@@\r\n" +
"@@@@^^=^@@^ ^' ,ww,w@@@@@ _@@@@@@@@@@ B o B\r\n" +
"@@@_xJ~ ~ , @@@@@@@P~_@@@@@@@@@@@@\r\n" +
"@@ @, ,@@@,_____ _,J@@@@@@@@@@@@@\r\n" +
"@@L `' ,@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\r\n" +
@ -92,6 +102,14 @@ type (
escSeqs map[byte]escHandler
)
var (
escs = escSeqs{
'i': func(io.Writer) { os.Stdout.Write([]byte("\x1b[s\x1b[2;1H\x1b[1;31m[HKEXSH]\x1b[39;49m\x1b[u")) },
't': func(io.Writer) { os.Stdout.Write([]byte("\x1b[1;32m[HKEXSH]\x1b[39;49m")) },
'B': func(io.Writer) { os.Stdout.Write([]byte("\x1b[1;32m" + bob + "\x1b[39;49m")) },
}
)
// Copy copies from src to dst until either EOF is reached
// on src or an error occurs. It returns the number of bytes
// copied and the first error encountered while copying, if any.
@ -139,11 +157,6 @@ func copyBuffer(dst io.Writer, src io.Reader, buf []byte) (written int64, err er
// or tunnel traffic indicator - note we cannot just spawn a goroutine
// here, as copyBuffer() returns after each burst of data. Scope must
// outlive individual copyBuffer calls).
escs := escSeqs{
'i': func(io.Writer) { os.Stdout.Write([]byte("\x1b[s\x1b[2;1H\x1b[1;31m[HKEXSH]\x1b[39;49m\x1b[u")) },
't': func(io.Writer) { os.Stdout.Write([]byte("\x1b[1;32m[HKEXSH]\x1b[39;49m")) },
'B': func(io.Writer) { os.Stdout.Write([]byte("\x1b[1;32m" + bob + "\x1b[39;49m")) },
}
/*
// If the reader has a WriteTo method, use it to do the copy.
@ -155,7 +168,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 {
@ -176,8 +189,8 @@ func copyBuffer(dst io.Writer, src io.Reader, buf []byte) (written int64, err er
// A repeat of 4 keys (conveniently 'dead' chars for most
// interactive shells; here CTRL-]) shall introduce
// some special responses or actions on the client side.
if seqPos < 4 {
if buf[0] == 0x1d {
if seqPos < 4 { //nolint:gomnd
if buf[0] == DeadCharPrefix {
seqPos++
}
} else {
@ -224,7 +237,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 ||
@ -240,21 +253,47 @@ func GetSize() (cols, rows int, err error) {
return
}
// doCopyMode begins a secure xs local<->remote file copy operation.
//
// TODO: reduce gocyclo
func doCopyMode(conn *xsnet.Conn, remoteDest bool, files string, rec *xs.Session) (exitStatus uint32, err error) {
if remoteDest {
log.Println("local files:", files, "remote filepath:", string(rec.Cmd()))
func buildCmdRemoteToLocal(copyQuiet bool, copyLimitBPS uint, destPath string) (captureStderr bool, cmd string, args []string) {
// Detect if we have 'pv'
// pipeview http://www.ivarch.com/programs/pv.shtml
// and use it for nice client progress display.
_, pverr := os.Stat("/usr/bin/pv")
if pverr != nil {
_, pverr = os.Stat("/usr/local/bin/pv")
}
var c *exec.Cmd
if copyQuiet || pverr != nil {
// copyQuiet and copyLimitBPS are not applicable in dumb copy mode
captureStderr = true
cmd = xs.GetTool("tar")
//os.Clearenv()
//os.Setenv("HOME", u.HomeDir)
//os.Setenv("TERM", "vt102") // TODO: server or client option?
args = []string{"-xz", "-C", destPath}
} else {
// TODO: Query remote side for total file/dir size
bandwidthInBytesPerSec := " -L " + fmt.Sprintf("%d ", copyLimitBPS)
displayOpts := " -pre " //nolint:goconst
cmd = xs.GetTool("bash")
args = []string{"-c", "pv " + displayOpts + bandwidthInBytesPerSec + "| tar -xz -C " + destPath}
}
log.Printf("[%v %v]\n", cmd, args)
return
}
cmdName := "/bin/tar"
cmdArgs := []string{"-cz", "-f", "/dev/stdout"}
func buildCmdLocalToRemote(copyQuiet bool, copyLimitBPS uint, 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.
_, pverr := os.Stat("/usr/bin/pv")
if pverr != nil {
_, pverr = os.Stat("/usr/local/bin/pv")
}
if pverr != nil {
// copyQuiet and copyLimitBPS are not applicable in dumb copy mode
captureStderr = true
cmd = xs.GetTool("tar")
args = []string{"-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
@ -272,22 +311,79 @@ func doCopyMode(conn *xsnet.Conn, remoteDest bool, files string, rec *xs.Session
v, _ = filepath.Abs(v) // #nosec
dirTmp, fileTmp := path.Split(v)
if dirTmp == "" {
cmdArgs = append(cmdArgs, fileTmp)
args = append(args, fileTmp)
} else {
cmdArgs = append(cmdArgs, "-C", dirTmp, fileTmp)
args = append(args, "-C", dirTmp, fileTmp)
}
}
} else {
captureStderr = copyQuiet
bandwidthInBytesPerSec := " -L " + fmt.Sprintf("%d", copyLimitBPS)
displayOpts := " -pre " //nolint:goconst,nolintlint
cmd = xs.GetTool("bash")
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
// its use below, where clients can send scattered sets of source
// files and dirs to be extracted to a single dest dir server-side,
// whilst preserving the subtrees of dirs on the other side.
// Eg., tar -c -f /dev/stdout -C /dirA fileInA -C /some/where/dirB fileInB /foo/dirC
// packages fileInA, fileInB, and dirC at a single toplevel in the tar.
// The tar authors are/were real smarties :)
//
// This is the 'scatter/gather' logic to allow specification of
// files and dirs in different trees to be deposited in a single
// remote destDir.
for _, v := range strings.Split(files, " ") {
v, _ = filepath.Abs(v) // #nosec
dirTmp, fileTmp := path.Split(v)
if dirTmp == "" {
args[1] = args[1] + fileTmp + " "
} else {
args[1] = args[1] + " -C " + dirTmp + " " + fileTmp + " "
}
}
args[1] = args[1] + "| pv" + displayOpts + bandwidthInBytesPerSec + " -s " + getTreeSizeSubCmd(files) + " -c"
}
log.Printf("[%v %v]\n", cmdName, cmdArgs)
// NOTE the lack of quotes around --xform option's sed expression.
// When args are passed in exec() format, no quoting is required
// (as this isn't input from a shell) (right? -rlm 20180823)
log.Printf("[%v %v]\n", cmd, args)
return
}
func getTreeSizeSubCmd(paths string) (c string) {
if runtime.GOOS == "linux" {
c = " $(du -cb " + paths + " | tail -1 | cut -f 1) "
} else {
c = " $(expr $(du -c " + paths + ` | tail -1 | cut -f 1) \* 1024) `
}
return c
}
// doCopyMode begins a secure xs local<->remote file copy operation.
//
// TODO: reduce gocyclo
func doCopyMode(conn *xsnet.Conn, remoteDest bool, files string, copyQuiet bool, copyLimitBPS uint, rec *xs.Session) (exitStatus uint32, err error) {
if remoteDest {
log.Println("local files:", files, "remote filepath:", string(rec.Cmd()))
var c *exec.Cmd
// 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
c.Dir, _ = os.Getwd() // #nosec
log.Println("[wd:", c.Dir, "]")
c.Stdout = conn
stdErrBuffer := new(bytes.Buffer)
c.Stderr = stdErrBuffer
if captureStderr {
c.Stderr = stdErrBuffer
} else {
c.Stderr = os.Stderr
}
// Start the command (no pty)
err = c.Start() // returns immediately
@ -308,7 +404,7 @@ func doCopyMode(conn *xsnet.Conn, remoteDest bool, files string, rec *xs.Session
if err != nil {
fmt.Println("cmd exited immediately. Cannot get cmd.Wait().ExitStatus()")
err = errors.New("cmd exited prematurely")
exitStatus = uint32(2)
exitStatus = uint32(CmdExitedEarly)
} else {
if err = c.Wait(); err != nil {
if exiterr, ok := err.(*exec.ExitError); ok {
@ -320,13 +416,15 @@ func doCopyMode(conn *xsnet.Conn, remoteDest bool, files string, rec *xs.Session
// an ExitStatus() method with the same signature.
if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
exitStatus = uint32(status.ExitStatus())
fmt.Print(stdErrBuffer)
if captureStderr {
fmt.Print(stdErrBuffer)
}
}
}
}
// send CSOExitStatus to inform remote (server) end cp is done
log.Println("Sending local exitStatus:", exitStatus)
r := make([]byte, 4)
r := make([]byte, 4) //nolint:gomnd
binary.BigEndian.PutUint32(r, exitStatus)
_, we := conn.WritePacket(r, xsnet.CSOExitStatus)
if we != nil {
@ -334,7 +432,7 @@ func doCopyMode(conn *xsnet.Conn, remoteDest bool, files string, rec *xs.Session
}
// Do a final read for remote's exit status
s := make([]byte, 4)
s := make([]byte, 4) //nolint:gomnd
_, remErr := conn.Read(s)
if remErr != io.EOF &&
!strings.Contains(remErr.Error(), "use of closed network") &&
@ -353,18 +451,11 @@ func doCopyMode(conn *xsnet.Conn, remoteDest bool, files string, rec *xs.Session
}
} else {
log.Println("remote filepath:", string(rec.Cmd()), "local files:", files)
var c *exec.Cmd
cmdName := "/bin/tar"
destPath := files
cmdArgs := []string{"-xz", "-C", destPath}
log.Printf("[%v %v]\n", cmdName, cmdArgs)
// NOTE the lack of quotes around --xform option's sed expression.
// When args are passed in exec() format, no quoting is required
// (as this isn't input from a shell) (right? -rlm 20180823)
//cmdArgs := []string{"-xvz", "-C", destPath, `--xform=s#.*/\(.*\)#\1#`}
c = exec.Command(cmdName, cmdArgs...) // #nosec
_, cmdName, cmdArgs := buildCmdRemoteToLocal(copyQuiet, copyLimitBPS, destPath)
c := exec.Command(cmdName, cmdArgs...) // #nosec
c.Stdin = conn
c.Stdout = os.Stdout
c.Stderr = os.Stderr
@ -401,8 +492,8 @@ func doCopyMode(conn *xsnet.Conn, remoteDest bool, files string, rec *xs.Session
// 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\"/
@ -420,7 +511,7 @@ func doShellMode(isInteractive bool, conn *xsnet.Conn, oldState *xs.State, rec *
// exit with inerr == nil
_, inerr := io.Copy(os.Stdout, conn)
if inerr != nil {
_ = xs.Restore(int(os.Stdin.Fd()), oldState) // #nosec
restoreTermState(oldState)
// Copy operations and user logging off will cause
// a "use of closed network connection" so handle that
// gracefully here
@ -435,7 +526,7 @@ func doShellMode(isInteractive bool, conn *xsnet.Conn, oldState *xs.State, rec *
if isInteractive {
log.Println("[* Got EOF *]")
_ = xs.Restore(int(os.Stdin.Fd()), oldState) // #nosec
restoreTermState(oldState)
exitWithStatus(int(rec.Status()))
}
}
@ -456,6 +547,8 @@ func doShellMode(isInteractive bool, conn *xsnet.Conn, oldState *xs.State, rec *
_, outerr := func(conn *xsnet.Conn, r io.Reader) (w int64, e error) {
// Copy() expects EOF so this will
// exit with outerr == nil
// NOTE we use a local implementation of Copy() to allow
// for custom key sequences to trigger local actions
w, e = Copy(conn, r)
return w, e
}(conn, os.Stdin)
@ -463,7 +556,7 @@ func doShellMode(isInteractive bool, conn *xsnet.Conn, oldState *xs.State, rec *
if outerr != nil {
log.Println(outerr)
fmt.Println(outerr)
_ = xs.Restore(int(os.Stdin.Fd()), oldState) // #nosec
restoreTermState(oldState)
log.Println("[Hanging up]")
exitWithStatus(0)
}
@ -477,15 +570,15 @@ func doShellMode(isInteractive bool, conn *xsnet.Conn, oldState *xs.State, rec *
}
func usageShell() {
fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0]) // nolint: errcheck
fmt.Fprintf(os.Stderr, "%s [opts] [user]@server\n", os.Args[0]) // nolint: errcheck
fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
fmt.Fprintf(os.Stderr, "%s [opts] [user]@server\n", os.Args[0])
flag.PrintDefaults()
}
func usageCp() {
fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0]) // nolint: errcheck
fmt.Fprintf(os.Stderr, "%s [opts] srcFileOrDir [...] [user]@server[:dstpath]\n", os.Args[0]) // nolint: errcheck
fmt.Fprintf(os.Stderr, "%s [opts] [user]@server[:srcFileOrDir] dstPath\n", os.Args[0]) // nolint: errcheck
fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
fmt.Fprintf(os.Stderr, "%s [opts] srcFileOrDir [...] [user]@server[:dstpath]\n", os.Args[0])
fmt.Fprintf(os.Stderr, "%s [opts] [user]@server[:srcFileOrDir] dstPath\n", os.Args[0])
flag.PrintDefaults()
}
@ -501,18 +594,18 @@ func rejectUserMsg() string {
//
// Server responds with [CSOTunAck:rport] or [CSOTunRefused:rport]
// (handled in xsnet.Read())
func reqTunnel(hc *xsnet.Conn, lp uint16, p string /*net.Addr*/, rp uint16) {
func reqTunnel(hc *xsnet.Conn, lp uint16 /*, p string*/ /*net.Addr*/, rp uint16) {
// Write request to server so it can attempt to set up its end
var bTmp bytes.Buffer
if e := binary.Write(&bTmp, binary.BigEndian, lp); e != nil {
fmt.Fprintln(os.Stderr, "reqTunnel:", e) // nolint: errcheck
fmt.Fprintln(os.Stderr, "reqTunnel:", e)
}
if e := binary.Write(&bTmp, binary.BigEndian, rp); e != nil {
fmt.Fprintln(os.Stderr, "reqTunnel:", e) // nolint: errcheck
fmt.Fprintln(os.Stderr, "reqTunnel:", e)
}
_ = logger.LogDebug(fmt.Sprintln("[Client sending CSOTunSetup]")) // nolint: gosec
_ = logger.LogDebug(fmt.Sprintln("[Client sending CSOTunSetup]"))
if n, e := hc.WritePacket(bTmp.Bytes(), xsnet.CSOTunSetup); e != nil || n != len(bTmp.Bytes()) {
fmt.Fprintln(os.Stderr, "reqTunnel:", e) // nolint: errcheck
fmt.Fprintln(os.Stderr, "reqTunnel:", e)
}
}
@ -525,7 +618,7 @@ func parseNonSwitchArgs(a []string) (user, host, path string, isDest bool, other
if strings.Contains(arg, ":") || strings.Contains(arg, "@") {
fancyArg := strings.Split(flag.Arg(i), "@")
var fancyHostPath []string
if len(fancyArg) < 2 {
if len(fancyArg) < 2 { //nolint:gomnd
//TODO: no user specified, use current
fancyUser = "[default:getUser]"
fancyHostPath = strings.Split(fancyArg[0], ":")
@ -551,8 +644,8 @@ func parseNonSwitchArgs(a []string) (user, host, path string, isDest bool, other
return fancyUser, fancyHost, fancyPath, isDest, otherArgs
}
func launchTuns(conn *xsnet.Conn, remoteHost string, tuns string) {
remAddrs, _ := net.LookupHost(remoteHost) // nolint: gosec
func launchTuns(conn *xsnet.Conn /*remoteHost string,*/, tuns string) {
/*remAddrs, _ := net.LookupHost(remoteHost)*/ //nolint:gocritic,nolintlint
if tuns == "" {
return
@ -561,8 +654,8 @@ func launchTuns(conn *xsnet.Conn, remoteHost string, tuns string) {
tunSpecs := strings.Split(tuns, ",")
for _, tunItem := range tunSpecs {
var lPort, rPort uint16
_, _ = fmt.Sscanf(tunItem, "%d:%d", &lPort, &rPort) // nolint: gosec
reqTunnel(conn, lPort, remAddrs[0], rPort)
_, _ = fmt.Sscanf(tunItem, "%d:%d", &lPort, &rPort)
reqTunnel(conn, lPort /*remAddrs[0],*/, rPort)
}
}
@ -597,50 +690,86 @@ func sendSessionParams(conn io.Writer /* *xsnet.Conn*/, rec *xs.Session) (e erro
}
// TODO: reduce gocyclo
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,...]
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
var copySrc []byte
var copyDst string
authCookie string
chaffEnabled bool
chaffFreqMin uint
chaffFreqMax uint
chaffBytesMax uint
var authCookie string
var chaffEnabled bool
var chaffFreqMin uint
var chaffFreqMax uint
var chaffBytesMax uint
op []byte
)
var op []byte
isInteractive := false
// === Common (xs and xc) option parsing
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]")
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.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.BoolVar(&chaffEnabled, "e", true, "enable chaff pkts")
flag.UintVar(&chaffFreqMin, "f", 100, "chaff pkt freq min `msecs`")
flag.UintVar(&chaffFreqMax, "F", 5000, "chaff pkt freq max `msecs`")
flag.UintVar(&chaffBytesMax, "B", 64, "chaff pkt size max `bytes`")
flag.UintVar(&chaffFreqMin, "f", 100, "chaff pkt freq min `msecs`") //nolint:gomnd
flag.UintVar(&chaffFreqMax, "F", 5000, "chaff pkt freq max `msecs`") //nolint:gomnd
flag.UintVar(&chaffBytesMax, "B", 64, "chaff pkt size max `bytes`") //nolint:gomnd
flag.StringVar(&cpuprofile, "cpuprofile", "", "write cpu profile to <`file`>")
flag.StringVar(&memprofile, "memprofile", "", "write memory profile to <`file`>")
// === xc vs. xs option parsing
// 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.exe" {
if myPath[len(myPath)-1] != "xc" &&
myPath[len(myPath)-1] != "_xc" &&
myPath[len(myPath)-1] != "xc.exe" &&
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)")
@ -649,10 +778,19 @@ func main() {
shellMode = true
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.Usage = usageCp
}
flag.Parse()
if vopt {
fmt.Printf("version %s (%s)\n", version, gitCommit)
exitWithStatus(0)
}
// === Profiling instrumentation
if cpuprofile != "" {
f, err := os.Create(cpuprofile)
if err != nil {
@ -661,23 +799,25 @@ func main() {
defer f.Close()
fmt.Println("StartCPUProfile()")
if err := pprof.StartCPUProfile(f); err != nil {
log.Fatal("could not start CPU profile: ", err)
log.Fatal("could not start CPU profile: ", err) //nolint:gocritic
} else {
defer pprof.StopCPUProfile()
}
go func() { http.ListenAndServe("localhost:6060", nil) }()
go func() { http.ListenAndServe("localhost:6060", nil) }() //nolint:errcheck,gosec
}
// === User, host, port and path args for file operations, if applicable
remoteUser, remoteHost, tmpPath, pathIsDest, otherArgs :=
parseNonSwitchArgs(flag.Args())
//fmt.Println("otherArgs:", otherArgs)
//nolint:gocritic,nolintlint // fmt.Println("otherArgs:", otherArgs)
// Set defaults if user doesn't specify user, path or port
var uname string
if remoteUser == "" {
u, _ := user.Current() // nolint: gosec
uname = u.Username
u, _ := user.Current()
uname = localUserName(u)
} else {
uname = remoteUser
}
@ -689,6 +829,8 @@ func main() {
tmpPath = "."
}
// === Copy mode arg and copy src/dest setup
var fileArgs string
if !shellMode /*&& tmpPath != ""*/ {
// -if pathIsSrc && len(otherArgs) > 1 ERROR
@ -720,59 +862,60 @@ func main() {
}
}
// Do some more option consistency checks
// === Do some final option consistency checks
//fmt.Println("server finally is:", server)
//nolint:gocritic,nolintlint // fmt.Println("server finally is:", server)
if flag.NFlag() == 0 && server == "" {
flag.Usage()
exitWithStatus(0)
}
if vopt {
fmt.Printf("version %s (%s)\n", version, gitCommit)
exitWithStatus(0)
}
if len(cmdStr) != 0 && (len(copySrc) != 0 || len(copyDst) != 0) {
if cmdStr != "" && (len(copySrc) != 0 || copyDst != "") {
log.Fatal("incompatible options -- either cmd (-x) or copy ops but not both")
}
//-------------------------------------------------------------------
// 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") // nolint: errcheck,gosec
Log, _ = logger.New(logger.LOG_USER|logger.LOG_DEBUG|logger.LOG_NOTICE|logger.LOG_ERR, "xs")
xsnet.Init(dbg, "xs", logger.LOG_USER|logger.LOG_DEBUG|logger.LOG_NOTICE|logger.LOG_ERR)
if dbg {
log.SetOutput(Log)
} else {
log.SetOutput(ioutil.Discard)
log.SetOutput(io.Discard)
}
// === Auth token fetch for login
if !gopt {
// See if we can log in via an auth token
u, _ := user.Current() // nolint: gosec
ab, aerr := ioutil.ReadFile(fmt.Sprintf("%s/.xs_id", u.HomeDir))
u, _ := user.Current()
ab, aerr := os.ReadFile(fmt.Sprintf("%s/%s", u.HomeDir, xsnet.XS_ID_AUTHTOKFILE))
if aerr == nil {
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 {
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 == "" {
_, _ = fmt.Fprintln(os.Stderr, "[no authtoken, use -g to request one from server]")
}
} else {
log.Printf("[cannot read %s/.xs_id]\n", u.HomeDir)
log.Printf("[cannot read %s/%s]\n", u.HomeDir, xsnet.XS_ID_AUTHTOKFILE)
}
}
runtime.GC()
// Enforce some sane min/max vals on chaff flags
if chaffFreqMin < 2 {
// === Enforce some sane min/max vals on chaff flags
if chaffFreqMin < 2 { //nolint:gomnd
chaffFreqMin = 2
}
if chaffFreqMax == 0 {
@ -782,15 +925,17 @@ func main() {
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]") // nolint: errcheck
fmt.Fprintln(os.Stderr, "[requesting authtoken from server]")
op = []byte{'A'}
chaffFreqMin = 2
chaffFreqMax = 10
} else if len(cmdStr) == 0 {
} else if cmdStr == "" {
op = []byte{'s'}
isInteractive = true
} else {
@ -811,138 +956,200 @@ func main() {
// client->server file copy
// src file list is in copySrc
op = []byte{'D'}
//fmt.Println("client->server copy:", string(copySrc), "->", copyDst)
//nolint:gocritic,nolintlint // fmt.Println("client->server copy:", string(copySrc), "->", copyDst)
cmdStr = copyDst
} else {
// server->client file copy
// remote src file(s) in copyDsr
op = []byte{'S'}
//fmt.Println("server->client copy:", string(copySrc), "->", copyDst)
//nolint:gocritic,nolintlint // fmt.Println("server->client copy:", string(copySrc), "->", copyDst)
cmdStr = string(copySrc)
}
}
// === TCP / KCP Dial setup
proto := "tcp"
if kcpMode != "unused" {
proto = "kcp"
}
conn, err := xsnet.Dial(proto, server, cipherAlg, hmacAlg, kexAlg, kcpMode)
remodExtArg := ""
if remodRequested {
remodExtArg = "OPT_REMOD"
}
// Pass opt to Dial() via extensions arg
conn, err := xsnet.Dial(proto, server, cipherAlg, hmacAlg, kexAlg, kcpMode, remodExtArg)
if err != nil {
fmt.Println(err)
exitWithStatus(3)
exitWithStatus(XSNetDialFailed)
}
conn.RekeyHelper(rekeySecs)
defer conn.ShutdownRekey()
// === Shell terminal mode (Shell vs. Copy) setup
// Set stdin in raw mode if it's an interactive session
// TODO: send flag to server side indicating this
// affects shell command used
var oldState *xs.State
defer conn.Close() // nolint: errcheck
defer conn.Close()
// From this point on, conn is a secure encrypted channel
// === From this point on, conn is a secure encrypted channel
if shellMode {
if isatty.IsTerminal(os.Stdin.Fd()) {
oldState, err = xs.MakeRaw(int(os.Stdin.Fd()))
oldState, err = xs.MakeRaw(os.Stdin.Fd())
if err != nil {
panic(err)
}
// #gv:s/label=\"main\$1\"/label=\"deferRestore\"/
// TODO:.gv:main:1:deferRestore
defer func() { _ = xs.Restore(int(os.Stdin.Fd()), oldState) }() // nolint: errcheck,gosec
defer restoreTermState(oldState)
} else {
log.Println("NOT A TTY")
}
}
if len(authCookie) == 0 {
//No auth token, prompt for password
fmt.Printf("Gimme cookie:")
ab, e := xs.ReadPassword(int(os.Stdin.Fd()))
fmt.Printf("\r\n")
// === Login phase
// Start login timeout here and disconnect if user/pass phase stalls
// iloginImpatience := time.AfterFunc(20*time.Second, func() {
// i fmt.Printf(" .. [you still there? Waiting for a password.]")
// i})
loginTimeout := time.AfterFunc(30*time.Second, func() { //nolint:gomnd
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.Fd())
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()
// === Session param and TERM setup
// Set up session params and send over to server
rec := xs.NewSession(op, []byte(uname), []byte(remoteHost), []byte(os.Getenv("TERM")), []byte(cmdStr), []byte(authCookie), 0)
sendErr := sendSessionParams(&conn, rec)
if sendErr != nil {
_ = xs.Restore(int(os.Stdin.Fd()), oldState) // nolint: errcheck,gosec
rec.SetStatus(254)
fmt.Fprintln(os.Stderr, "Error: server rejected secure proposal params") // nolint: errcheck
restoreTermState(oldState)
rec.SetStatus(ServerRejectedSecureProposal)
fmt.Fprintln(os.Stderr, "Error: server rejected secure proposal params or login timed out")
exitWithStatus(int(rec.Status()))
//log.Fatal(sendErr)
//nolint:gocritic,nolintlint // log.Fatal(sendErr)
}
//Security scrub
authCookie = "" // nolint: ineffassign
// Security scrub
authCookie = "" //nolint: ineffassign
runtime.GC()
// Read auth reply from server
// === Login Auth
// === Read auth reply from server
authReply := make([]byte, 1) // bool: 0 = fail, 1 = pass
_, err = conn.Read(authReply)
if err != nil {
fmt.Fprintln(os.Stderr, "Error reading auth reply") // nolint: errcheck
rec.SetStatus(255)
// === Exit if auth reply not received
fmt.Fprintln(os.Stderr, "Error reading auth reply")
rec.SetStatus(ErrReadingAuthReply)
} else if authReply[0] == 0 {
fmt.Fprintln(os.Stderr, rejectUserMsg()) // nolint: errcheck
rec.SetStatus(255)
// === .. or if auth failed
fmt.Fprintln(os.Stderr, rejectUserMsg())
rec.SetStatus(GeneralProtocolErr)
} else {
// Set up chaffing to server
// === Set up connection keepalive to server
conn.StartupKeepAlive() // goroutine, returns immediately
defer conn.ShutdownKeepAlive()
// === Set up chaffing to server
conn.SetupChaff(chaffFreqMin, chaffFreqMax, chaffBytesMax) // enable client->server chaffing
if chaffEnabled {
// #gv:s/label=\"main\$2\"/label=\"deferCloseChaff\"/
// TODO:.gv:main:2:deferCloseChaff
conn.EnableChaff() // goroutine, returns immediately
defer conn.DisableChaff()
conn.StartupChaff() // goroutine, returns immediately
defer conn.ShutdownChaff()
}
// Keepalive for any tunnels that may exist
// === (goroutine) Start keepAliveWorker for tunnels
// #gv:s/label=\"main\$1\"/label=\"tunKeepAlive\"/
// TODO:.gv:main:1:tunKeepAlive
//[1]: better to always send tunnel keepAlives even if client didn't specify
// any, to prevent listeners from knowing this.
//[1] if tunSpecStr != "" {
// [1]: better to always send tunnel keepAlives even if client didn't specify
// any, to prevent listeners from knowing this.
// [1] if tunSpecStr != "" {
keepAliveWorker := func() {
for {
// Add a bit of jitter to keepAlive so it doesn't stand out quite as much
time.Sleep(time.Duration(2000-rand.Intn(200)) * time.Millisecond)
time.Sleep(time.Duration(2000-rand.Intn(200)) * time.Millisecond) //nolint:gosec,gomnd
// FIXME: keepAlives should probably have small random packet len/data as well
// to further obscure them vs. interactive or tunnel data
// keepAlives must be >=2 bytes, due to processing elsewhere
conn.WritePacket([]byte{0, 0}, xsnet.CSOTunKeepAlive) // nolint: errcheck,gosec
conn.WritePacket([]byte{0, 0}, xsnet.CSOTunKeepAlive) //nolint: errcheck
}
}
go keepAliveWorker()
//[1]}
// [1]}
// === Session entry (shellMode or copyMode)
if shellMode {
launchTuns(&conn, remoteHost, tunSpecStr)
// === (shell) launch tunnels
launchTuns(&conn /*remoteHost,*/, tunSpecStr)
doShellMode(isInteractive, &conn, oldState, rec)
} else { // copyMode
s, _ := doCopyMode(&conn, pathIsDest, fileArgs, rec) // nolint: errcheck,gosec
} else {
// === (.. or file copy)
s, _ := doCopyMode(&conn, pathIsDest, fileArgs, copyQuiet, copyLimitBPS, rec)
rec.SetStatus(s)
}
if rec.Status() != 0 {
_ = xs.Restore(int(os.Stdin.Fd()), oldState) // nolint: errcheck,gosec
fmt.Fprintln(os.Stderr, "Session exited with status:", rec.Status()) // nolint: errcheck
restoreTermState(oldState)
fmt.Fprintln(os.Stderr, "Session exited with status:", rec.Status())
}
}
if oldState != nil {
_ = xs.Restore(int(os.Stdin.Fd()), oldState) // nolint: gosec
restoreTermState(oldState)
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.Fd(), oldState)
}
// exitWithStatus wraps os.Exit() plus does any required pprof housekeeping
func exitWithStatus(status int) {
if cpuprofile != "" {
@ -957,9 +1164,8 @@ func exitWithStatus(status int) {
defer f.Close()
runtime.GC() // get up-to-date statistics
if err := pprof.WriteHeapProfile(f); err != nil {
log.Fatal("could not write memory profile: ", err)
log.Fatal("could not write memory profile: ", err) //nolint:gocritic
}
}
os.Exit(status)
}

View file

@ -6,7 +6,9 @@ XSD_USER=root
XSD_HOME=/var/run
INST_PREFIX=/usr/local
COMMAND=$INST_PREFIX/sbin/xsd
ARGS=""
#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
depend() {
need net
@ -28,7 +30,7 @@ start() {
-d ${XSD_HOME} \
--make-pidfile --pidfile ${XSD_PIDFILE} \
--start --quiet --background \
--exec "${COMMAND}" "${ARGS}"
--exec "${COMMAND}" -- ${ARGS}
eend $?
}

166
xsd.sysvrc Executable file
View file

@ -0,0 +1,166 @@
#! /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,10 +1,16 @@
.PHONY: clean all vis lint
ifeq ($(GARBLE),y)
GO = garble -literals -tiny -debugdir=garbled
else
GO = go
endif
EXTPKGS = binary,bytes,crypto,encoding,errors,flag,fmt,internal,io,log,net,os,path,runtime,time,strings,sync,syscall
EXE = $(notdir $(shell pwd))
all:
go build $(BUILDOPTS) .
$(GO) build $(BUILDOPTS) .
clean:
$(RM) $(EXE) $(EXE).exe
@ -14,5 +20,5 @@ vis:
../fixup-gv.sh xsd.go && cat xsd-vis.gv | dot -Tpng -oxsd-vis-fixedup.png
lint:
-gometalinter --deadline=60s | sort
-golangci-lint run

Binary file not shown.

Before

Width:  |  Height:  |  Size: 782 KiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Before After
Before After

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

View file

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

View file

@ -1,6 +1,6 @@
package xsnet
// Copyright (c) 2017-2019 Russell Magee
// Copyright (c) 2017-2020 Russell Magee
// Licensed under the terms of the MIT license (see LICENSE.mit in this
// distribution)
//
@ -21,10 +21,12 @@ import (
"log"
"blitter.com/go/cryptmt"
"blitter.com/go/wanderer"
"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...)
@ -56,10 +58,20 @@ func expandKeyMat(keymat []byte, blocksize int) []byte {
return keymat
}
/* 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) {
// Choose a cipher and hmac alg from supported sets, given two uint8 values
func getNewStreamAlgs(cb uint8, hb uint8) (config uint32) {
// Get new cipher and hash algs (clamped to valid values) based on
// the input rekeying data
c := (cb % CAlgNoneDisallowed)
h := (hb % HmacNoneDisallowed)
config = uint32(h<<8) | uint32(c)
logger.LogDebug(fmt.Sprintf("[Chose new algs [%d:%d]", h, c))
return
}
// (Re-)initialize the keystream and hmac state for an xsnet.Conn, returning
// a cipherStream and hash
func (hc *Conn) getStream(keymat []byte) (rc cipher.Stream, mc hash.Hash, err error) {
var key []byte
var block cipher.Block
var iv []byte
@ -103,11 +115,25 @@ func (hc Conn) getStream(keymat []byte) (rc cipher.Stream, mc hash.Hash, err err
rc = cipher.NewOFB(block, iv)
log.Printf("[cipher BLOWFISH_64 (%d)]\n", copts)
case CAlgCryptMT1:
rc = cryptmt.NewCipher(keymat)
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 CAlgWanderer:
rc = wanderer.NewCodec(nil, nil, keymat, 3, 3)
log.Printf("[cipher WANDERER (%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)
if err != nil {
log.Printf("[ChaCha20 config error]\n")
fmt.Printf("[ChaCha20 config error]\n")
}
// TODO: SetCounter() to something derived from key or nonce or extra keymat?
log.Printf("[cipher CHACHA20_12 (%d)]\n", copts)
default:
log.Printf("[invalid cipher (%d)]\n", copts)
fmt.Printf("DOOFUS SET A VALID CIPHER ALG (%d)\n", copts)
@ -131,6 +157,9 @@ func (hc Conn) getStream(keymat []byte) (rc cipher.Stream, mc hash.Hash, err err
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)
@ -139,7 +168,7 @@ func (hc Conn) getStream(keymat []byte) (rc cipher.Stream, mc hash.Hash, err err
//os.Exit(1)
}
if err != nil {
if err == nil && ivlen > 0 {
// 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.

View file

@ -1,6 +1,6 @@
// consts.go - consts for xsnet
// Copyright (c) 2017-2019 Russell Magee
// Copyright (c) 2017-2020 Russell Magee
// Licensed under the terms of the MIT license (see LICENSE.mit in this
// distribution)
//
@ -29,6 +29,10 @@ 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
)
@ -48,6 +52,8 @@ const (
CSEKEXAlgDenied // server rejected proposed KEX alg
CSECipherAlgDenied // server rejected proposed Cipher alg
CSEHMACAlgDenied // server rejected proposed HMAC alg
CSEConnDead // connection keepalives expired
CSELoginTimeout
)
// Extended (>255 UNIX exit status) codes
@ -71,6 +77,8 @@ const (
CSOTunKeepAlive // client tunnel heartbeat
CSOTunDisconn // server -> client: tunnel rport disconnected
CSOTunHangup // client -> server: tunnel lport hung up
CSOKeepAlive // bidir keepalive packet to monitor main connection
CSORekey // TODO: rekey/re-select session cipher/hash algs
)
// TunEndpoint.tunCtl control values - used to control workers for client
@ -90,8 +98,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 = 4*1024*1024*1024 - 1
// TODO: this should be small (max unfragmented packet size?)
const MAX_PAYLOAD_LEN = 2*1024*1024*1024 - 1
// Session symmetric crypto algs
const (
@ -99,7 +107,8 @@ const (
CAlgTwofish128 // golang.org/x/crypto/twofish
CAlgBlowfish64 // golang.org/x/crypto/blowfish
CAlgCryptMT1 //cryptmt using mtwist64
CAlgWanderer // inhouse experimental crypto alg
CAlgChaCha20_12
CAlgHopscotch
CAlgNoneDisallowed
)
@ -110,8 +119,27 @@ type CSCipherAlg uint32
const (
HmacSHA256 = iota
HmacSHA512
HmacWHIRLPOOL
HmacNoneDisallowed
)
// Conn opts outside of basic kex/cipher/hmac connect config
const (
CONone = iota
CORemodulateShields // if set, rekeying also reselects random cipher/hmac alg
)
type COValue uint32
// Available HMACs for hkex.Conn
type CSHmacAlg uint32
// Some bounds-checking consts
const (
REKEY_SECS_MIN = 1
REKEY_SECS_MAX = 28800 // 8 hours
CHAFF_FREQ_MSECS_MIN = 1
CHAFF_FREQ_MSECS_MAX = 300000 // 5 minutes
)
const XS_ID_AUTHTOKFILE = ".config/xs/.xs_id"

View file

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

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
// hkextun.go - Tunnel setup using an established xsnet.Conn
// Copyright (c) 2017-2019 Russell Magee
// Copyright (c) 2017-2020 Russell Magee
// Licensed under the terms of the MIT license (see LICENSE.mit in this
// distribution)
//
@ -37,6 +37,8 @@ type (
// client starts worker to receive/send data using lport
// ... client disconnects: sends remhost [CSOTunClose:rport]
// ... or server disconnects: sends client [CSOTunClose:lport]
// ... or server disconnects: due to client failing to send TunKeepAlive
// events for too long
// server at any time sends [CSOTunRefused:rport] if daemon died
// --

View file

@ -1,10 +1,16 @@
.PHONY: clean all vis lint
ifeq ($(GARBLE),y)
GO = garble -tiny -literals -debugdir=garbled
else
GO = go
endif
EXTPKGS = bytes,errors,flag,fmt,internal,io,log,net,os,path,runtime,time,strings,sync,syscall,binary,encoding
EXE = $(notdir $(shell pwd))
all:
go build $(BUILDOPTS) .
$(GO) build $(BUILDOPTS) .
clean:
$(RM) $(EXE) $(EXE).exe
@ -13,4 +19,4 @@ vis:
go-callvis -format png -file xspasswd-vis -ignore $(EXTPKGS) -group pkg,type .
lint:
-gometalinter --deadline=60s | sort
-golangci-lint run

View file

@ -1,7 +1,7 @@
// Util to generate/store passwords for users in a file akin to /etc/passwd
// suitable for the demo hkexsh server, using bcrypt.
// suitable for the xs server, using bcrypt.
//
// Copyright (c) 2017-2019 Russell Magee
// Copyright (c) 2017-2020 Russell Magee
// Licensed under the terms of the MIT license (see LICENSE.mit in this
// distribution)
//
@ -59,7 +59,7 @@ func main() {
uname = userName
fmt.Printf("New Password:")
ab, err := xs.ReadPassword(int(os.Stdin.Fd()))
ab, err := xs.ReadPassword(os.Stdin.Fd())
fmt.Printf("\r\n")
if err != nil {
log.Fatal(err)
@ -68,7 +68,7 @@ func main() {
newpw = string(ab)
fmt.Printf("Confirm:")
ab, err = xs.ReadPassword(int(os.Stdin.Fd()))
ab, err = xs.ReadPassword(os.Stdin.Fd())
fmt.Printf("\r\n")
if err != nil {
log.Fatal(err)