Compare commits
	
		
			145 commits
		
	
	
		
			71f2bdfe8d
			...
			e7a3d60b36
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | e7a3d60b36 | ||
|  | 88e513bbdf | ||
|  | 3196039413 | ||
|  | 1d8419caef | ||
|  | d60101a599 | ||
|  | 8bf3d894ae | ||
|  | e23c7f8861 | ||
|  | 279f20d10b | ||
|  | 6890f3e5eb | ||
|  | b8fc8c72ed | ||
|  | a7e782a913 | ||
|  | 96bf86d867 | ||
|  | c24f689b68 | ||
|  | e9de3fa114 | ||
|  | b966983c16 | ||
|  | db52ec143d | ||
|  | bfd2e7cf32 | ||
|  | 8b1688b2dc | ||
|  | 23b964a720 | ||
|  | ad1978c6b5 | ||
|  | f231bbadcf | ||
|  | cb92b247d7 | ||
|  | 0204284bb2 | ||
|  | b1aa6fa947 | ||
|  | 0b283722c3 | ||
|  | 02641f21e5 | ||
|  | 7f87225e34 | ||
|  | cf63c5a239 | ||
|  | d95d38096e | ||
|  | ec76919ba9 | ||
|  | e79f08d79f | ||
|  | 06de51630e | ||
|  | 20154ff582 | ||
|  | 6e89175593 | ||
|  | 94f22846cf | ||
|  | 23b108ec3c | ||
|  | eea72c41ce | ||
|  | 453b2580e6 | ||
|  | d0c1505904 | ||
|  | ff4b5ab418 | ||
|  | 986c158180 | ||
|  | b7ace9d7ad | ||
|  | 49814ff8e0 | ||
|  | 7d8f34a925 | ||
|  | 62f630394b | ||
|  | ea97eb8ccd | ||
|  | 314970443e | ||
|  | 689aa7d532 | ||
|  | 0ce72570dc | ||
|  | 0a508125ba | ||
|  | 6ce007abf3 | ||
|  | 74d9201a4c | ||
|  | 757714625f | ||
|  | dbae15bff3 | ||
|  | 9cd48bd828 | ||
|  | f3e8383dce | ||
|  | 50e786e549 | ||
|  | c7228d3267 | ||
|  | 5bd3ee0e4c | ||
|  | f372666187 | ||
|  | 3bb9c795e4 | ||
|  | 9fa8f38614 | ||
|  | 234ff25d73 | ||
|  | fd95e526e5 | ||
|  | 373c1b384e | ||
|  | 7907b23fce | ||
|  | d68f716839 | ||
|  | 089ccba342 | ||
|  | 399f59ed73 | ||
|  | 6f24660f9e | ||
|  | 25c3f6cae0 | ||
|  | aaa5a4b3d5 | ||
|  | f5c155de48 | ||
|  | 0b1cae1ec8 | ||
|  | 4b66e87c74 | ||
|  | 52decd5b75 | ||
|  | a41a22644b | ||
|  | 3f9696a10b | ||
|  | 1df11fd67b | ||
|  | 18cde5a88c | ||
|  | bbc19a2168 | ||
|  | a17f178aab | ||
|  | a8a747fa48 | ||
|  | 91ab31d0e7 | ||
|  | d9f08997e3 | ||
|  | 134676a60b | ||
|  | 86a7af45d4 | ||
|  | 791c095a6d | ||
|  | 33173eb847 | ||
|  | fd70933884 | ||
|  | 26a1ee17cf | ||
|  | 7e9df023c9 | ||
|  | f6a85ca071 | ||
|  | 96cdec0a86 | ||
|  | ff57630342 | ||
|  | 34399dd907 | ||
|  | 76d407e92a | ||
|  | f0254c664f | ||
|  | e58572ebb5 | ||
|  | d542e4949e | ||
|  | 1faa929cdb | ||
|  | b5c88b953b | ||
|  | f34befae8a | ||
|  | 9582eeb068 | ||
|  | 70523971ef | ||
|  | 6e8156eefb | ||
|  | 1a2e97706c | ||
|  | 6b3154eeb6 | ||
|  | a4ceaed4c5 | ||
|  | 167f1b6bfb | ||
|  | 8c543bfe6b | ||
|  | da27bd50a1 | ||
|  | dfc709444d | ||
|  | 9646bde80d | ||
|  | 790fce296e | ||
|  | 023aa41878 | ||
|  | f22b756a22 | ||
|  | 04e4f1b0b7 | ||
|  | 8df5364c19 | ||
|  | 8cb3d0aea9 | ||
|  | 106e4d80c1 | ||
|  | cb0b9a770a | ||
|  | e6a3facf56 | ||
|  | ab2f98eadb | ||
|  | 68b8c48e4d | ||
|  | 1057a78df3 | ||
|  | c1bef57150 | ||
|  | c83b7dd203 | ||
|  | 910d6fc278 | ||
|  | 1a1e15d65b | ||
|  | ce4594941b | ||
|  | b309224a7e | ||
|  | 3217f8f07f | ||
|  | 6a83aa35a1 | ||
|  | 25feb560a7 | ||
|  | 57679260c4 | ||
|  | 979a1fde3e | ||
|  | 9c0f01fb4f | ||
|  | 75b6fcc20c | ||
|  | 4fe6271979 | ||
|  | c12e3bca9e | ||
|  | 7c1464f084 | ||
|  | fc79001725 | ||
|  | 3db5d2ba21 | ||
|  | aa15866305 | 
							
								
								
									
										327
									
								
								.golangci.yml
									
										
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,327 @@ | ||||||
|  | # This file contains all available configuration options | ||||||
|  | # with their default values. | ||||||
|  | 
 | ||||||
|  | # options for analysis running | ||||||
|  | run: | ||||||
|  |   # default concurrency is a available CPU number | ||||||
|  |   concurrency: 4 | ||||||
|  | 
 | ||||||
|  |   # timeout for analysis, e.g. 30s, 5m, default is 1m | ||||||
|  |   timeout: 1m | ||||||
|  | 
 | ||||||
|  |   # exit code when at least one issue was found, default is 1 | ||||||
|  |   issues-exit-code: 1 | ||||||
|  | 
 | ||||||
|  |   # include test files or not, default is true | ||||||
|  |   tests: true | ||||||
|  | 
 | ||||||
|  |   # list of build tags, all linters use it. Default is empty list. | ||||||
|  |   build-tags: | ||||||
|  |     - mytag | ||||||
|  | 
 | ||||||
|  |   # which dirs to skip: issues from them won't be reported; | ||||||
|  |   # can use regexp here: generated.*, regexp is applied on full path; | ||||||
|  |   # default value is empty list, but default dirs are skipped independently | ||||||
|  |   # from this option's value (see skip-dirs-use-default). | ||||||
|  |   skip-dirs: | ||||||
|  |     - src/external_libs | ||||||
|  |     - autogenerated_by_my_lib | ||||||
|  | 
 | ||||||
|  |   # default is true. Enables skipping of directories: | ||||||
|  |   #   vendor$, third_party$, testdata$, examples$, Godeps$, builtin$ | ||||||
|  |   skip-dirs-use-default: true | ||||||
|  | 
 | ||||||
|  |   # which files to skip: they will be analyzed, but issues from them | ||||||
|  |   # won't be reported. Default value is empty list, but there is | ||||||
|  |   # no need to include all autogenerated files, we confidently recognize | ||||||
|  |   # autogenerated files. If it's not please let us know. | ||||||
|  |   skip-files: | ||||||
|  |     - ".*\\.my\\.go$" | ||||||
|  |     - lib/bad.go | ||||||
|  | 
 | ||||||
|  |   # by default isn't set. If set we pass it to "go list -mod={option}". From "go help modules": | ||||||
|  |   # If invoked with -mod=readonly, the go command is disallowed from the implicit | ||||||
|  |   # automatic updating of go.mod described above. Instead, it fails when any changes | ||||||
|  |   # to go.mod are needed. This setting is most useful to check that go.mod does | ||||||
|  |   # not need updates, such as in a continuous integration and testing system. | ||||||
|  |   # If invoked with -mod=vendor, the go command assumes that the vendor | ||||||
|  |   # directory holds the correct copies of dependencies and ignores | ||||||
|  |   # the dependency descriptions in go.mod. | ||||||
|  |   #! modules-download-mode: readonly|release|vendor | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # output configuration options | ||||||
|  | output: | ||||||
|  |   # colored-line-number|line-number|json|tab|checkstyle|code-climate, default is "colored-line-number" | ||||||
|  |   format: colored-line-number | ||||||
|  | 
 | ||||||
|  |   # print lines of code with issue, default is true | ||||||
|  |   print-issued-lines: true | ||||||
|  | 
 | ||||||
|  |   # print linter name in the end of issue text, default is true | ||||||
|  |   print-linter-name: true | ||||||
|  | 
 | ||||||
|  |   # make issues output unique by line, default is true | ||||||
|  |   uniq-by-line: true | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # all available settings of specific linters | ||||||
|  | linters-settings: | ||||||
|  |   dogsled: | ||||||
|  |     # checks assignments with too many blank identifiers; default is 2 | ||||||
|  |     max-blank-identifiers: 2 | ||||||
|  |   dupl: | ||||||
|  |     # tokens count to trigger issue, 150 by default | ||||||
|  |     threshold: 100 | ||||||
|  |   errcheck: | ||||||
|  |     # report about not checking of errors in type assetions: `a := b.(MyStruct)`; | ||||||
|  |     # default is false: such cases aren't reported by default. | ||||||
|  |     check-type-assertions: false | ||||||
|  | 
 | ||||||
|  |     # report about assignment of errors to blank identifier: `num, _ := strconv.Atoi(numStr)`; | ||||||
|  |     # default is false: such cases aren't reported by default. | ||||||
|  |     check-blank: false | ||||||
|  | 
 | ||||||
|  |     # [deprecated] comma-separated list of pairs of the form pkg:regex | ||||||
|  |     # the regex is used to ignore names within pkg. (default "fmt:.*"). | ||||||
|  |     # see https://github.com/kisielk/errcheck#the-deprecated-method for details | ||||||
|  |     ignore: fmt:.*,io/ioutil:^Read.* | ||||||
|  | 
 | ||||||
|  |     # path to a file containing a list of functions to exclude from checking | ||||||
|  |     # see https://github.com/kisielk/errcheck#excluding-functions for details | ||||||
|  |     #!exclude: /path/to/file.txt | ||||||
|  |   funlen: | ||||||
|  |     lines: 60 | ||||||
|  |     statements: 40 | ||||||
|  |   gocognit: | ||||||
|  |     # minimal code complexity to report, 30 by default (but we recommend 10-20) | ||||||
|  |     min-complexity: 10 | ||||||
|  |   goconst: | ||||||
|  |     # minimal length of string constant, 3 by default | ||||||
|  |     min-len: 3 | ||||||
|  |     # minimal occurrences count to trigger, 3 by default | ||||||
|  |     min-occurrences: 3 | ||||||
|  |   gocritic: | ||||||
|  |     # Which checks should be enabled; can't be combined with 'disabled-checks'; | ||||||
|  |     # See https://go-critic.github.io/overview#checks-overview | ||||||
|  |     # To check which checks are enabled run `GL_DEBUG=gocritic golangci-lint run` | ||||||
|  |     # By default list of stable checks is used. | ||||||
|  |     enabled-checks: | ||||||
|  |       #!- rangeValCopy | ||||||
|  | 
 | ||||||
|  |     # Which checks should be disabled; can't be combined with 'enabled-checks'; default is empty | ||||||
|  |     disabled-checks: | ||||||
|  |       - regexpMust | ||||||
|  | 
 | ||||||
|  |     # Enable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks. | ||||||
|  |     # Empty list by default. See https://github.com/go-critic/go-critic#usage -> section "Tags". | ||||||
|  |     enabled-tags: | ||||||
|  |       - performance | ||||||
|  | 
 | ||||||
|  |     settings: # settings passed to gocritic | ||||||
|  |       captLocal: # must be valid enabled check name | ||||||
|  |         paramsOnly: true | ||||||
|  |       rangeValCopy: | ||||||
|  |         sizeThreshold: 32 | ||||||
|  |   gocyclo: | ||||||
|  |     # minimal code complexity to report, 30 by default (but we recommend 10-20) | ||||||
|  |     min-complexity: 10 | ||||||
|  |   godox: | ||||||
|  |     # report any comments starting with keywords, this is useful for TODO or FIXME comments that | ||||||
|  |     # might be left in the code accidentally and should be resolved before merging | ||||||
|  |     keywords: # default keywords are TODO, BUG, and FIXME, these can be overwritten by this setting | ||||||
|  |       - NOTE | ||||||
|  |       - OPTIMIZE # marks code that should be optimized before merging | ||||||
|  |       - HACK # marks hack-arounds that should be removed before merging | ||||||
|  |   gofmt: | ||||||
|  |     # simplify code: gofmt with `-s` option, true by default | ||||||
|  |     simplify: true | ||||||
|  |   goimports: | ||||||
|  |     # put imports beginning with prefix after 3rd-party packages; | ||||||
|  |     # it's a comma-separated list of prefixes | ||||||
|  |     local-prefixes: github.com/org/project | ||||||
|  |   golint: | ||||||
|  |     # minimal confidence for issues, default is 0.8 | ||||||
|  |     min-confidence: 0.8 | ||||||
|  |   gomnd: | ||||||
|  |     settings: | ||||||
|  |       mnd: | ||||||
|  |         # the list of enabled checks, see https://github.com/tommy-muehle/go-mnd/#checks for description. | ||||||
|  |         checks: argument,case,condition,operation,return,assign | ||||||
|  |   govet: | ||||||
|  |     # report about shadowed variables | ||||||
|  |     check-shadowing: true | ||||||
|  | 
 | ||||||
|  |     # settings per analyzer | ||||||
|  |     settings: | ||||||
|  |       printf: # analyzer name, run `go tool vet help` to see all analyzers | ||||||
|  |         funcs: # run `go tool vet help printf` to see available settings for `printf` analyzer | ||||||
|  |           - (github.com/golangci/golangci-lint/pkg/logutils.Log).Infof | ||||||
|  |           - (github.com/golangci/golangci-lint/pkg/logutils.Log).Warnf | ||||||
|  |           - (github.com/golangci/golangci-lint/pkg/logutils.Log).Errorf | ||||||
|  |           - (github.com/golangci/golangci-lint/pkg/logutils.Log).Fatalf | ||||||
|  | 
 | ||||||
|  |     # enable or disable analyzers by name | ||||||
|  |     enable: | ||||||
|  |       - atomicalign | ||||||
|  |     enable-all: false | ||||||
|  |     disable: | ||||||
|  |       - shadow | ||||||
|  |     disable-all: false | ||||||
|  |   depguard: | ||||||
|  |     list-type: blacklist | ||||||
|  |     include-go-root: false | ||||||
|  |     packages: | ||||||
|  |       - github.com/sirupsen/logrus | ||||||
|  |     packages-with-error-message: | ||||||
|  |       # specify an error message to output when a blacklisted package is used | ||||||
|  |       - github.com/sirupsen/logrus: "logging is allowed only by logutils.Log" | ||||||
|  |   lll: | ||||||
|  |     # max line length, lines longer will be reported. Default is 120. | ||||||
|  |     # '\t' is counted as 1 character by default, and can be changed with the tab-width option | ||||||
|  |     line-length: 120 | ||||||
|  |     # tab width in spaces. Default to 1. | ||||||
|  |     tab-width: 1 | ||||||
|  |   maligned: | ||||||
|  |     # print struct with more effective memory layout or not, false by default | ||||||
|  |     suggest-new: true | ||||||
|  |   misspell: | ||||||
|  |     # Correct spellings using locale preferences for US or UK. | ||||||
|  |     # Default is to use a neutral variety of English. | ||||||
|  |     # Setting locale to US will correct the British spelling of 'colour' to 'color'. | ||||||
|  |     locale: US | ||||||
|  |     ignore-words: | ||||||
|  |       - someword | ||||||
|  |   nakedret: | ||||||
|  |     # make an issue if func has more lines of code than this setting and it has naked returns; default is 30 | ||||||
|  |     max-func-lines: 30 | ||||||
|  |   prealloc: | ||||||
|  |     # XXX: we don't recommend using this linter before doing performance profiling. | ||||||
|  |     # For most programs usage of prealloc will be a premature optimization. | ||||||
|  | 
 | ||||||
|  |     # Report preallocation suggestions only on simple loops that have no returns/breaks/continues/gotos in them. | ||||||
|  |     # True by default. | ||||||
|  |     simple: true | ||||||
|  |     range-loops: true # Report preallocation suggestions on range loops, true by default | ||||||
|  |     for-loops: false # Report preallocation suggestions on for loops, false by default | ||||||
|  |   rowserrcheck: | ||||||
|  |     packages: | ||||||
|  |       - github.com/jmoiron/sqlx | ||||||
|  |   unparam: | ||||||
|  |     # Inspect exported functions, default is false. Set to true if no external program/library imports your code. | ||||||
|  |     # XXX: if you enable this setting, unparam will report a lot of false-positives in text editors: | ||||||
|  |     # if it's called for subdir of a project it can't find external interfaces. All text editor integrations | ||||||
|  |     # with golangci-lint call it on a directory with the changed file. | ||||||
|  |     check-exported: false | ||||||
|  |   unused: | ||||||
|  |     # treat code as a program (not a library) and report unused exported identifiers; default is false. | ||||||
|  |     # XXX: if you enable this setting, unused will report a lot of false-positives in text editors: | ||||||
|  |     # if it's called for subdir of a project it can't find funcs usages. All text editor integrations | ||||||
|  |     # with golangci-lint call it on a directory with the changed file. | ||||||
|  |     check-exported: false | ||||||
|  |   whitespace: | ||||||
|  |     multi-if: false   # Enforces newlines (or comments) after every multi-line if statement | ||||||
|  |     multi-func: false # Enforces newlines (or comments) after every multi-line function signature | ||||||
|  |   wsl: | ||||||
|  |     # If true append is only allowed to be cuddled if appending value is | ||||||
|  |     # matching variables, fields or types on line above. Default is true. | ||||||
|  |     strict-append: true | ||||||
|  |     # Allow calls and assignments to be cuddled as long as the lines have any | ||||||
|  |     # matching variables, fields or types. Default is true. | ||||||
|  |     allow-assign-and-call: true | ||||||
|  |     # Allow multiline assignments to be cuddled. Default is true. | ||||||
|  |     allow-multiline-assign: true | ||||||
|  |     # Allow declarations (var) to be cuddled. | ||||||
|  |     allow-cuddle-declarations: false | ||||||
|  |     # Allow trailing comments in ending of blocks | ||||||
|  |     allow-trailing-comment: false | ||||||
|  |     # Force newlines in end of case at this limit (0 = never). | ||||||
|  |     force-case-trailing-whitespace: 0 | ||||||
|  | 
 | ||||||
|  |   # The custom section can be used to define linter plugins to be loaded at runtime. See README doc | ||||||
|  |   #  for more info. | ||||||
|  |   custom: | ||||||
|  |     # Each custom linter should have a unique name. | ||||||
|  |     #! example: | ||||||
|  |     #!  # The path to the plugin *.so. Can be absolute or local. Required for each custom linter | ||||||
|  |     #!  path: /path/to/example.so | ||||||
|  |     #!  # The description of the linter. Optional, just for documentation purposes. | ||||||
|  |     #!  description: This is an example usage of a plugin linter. | ||||||
|  |     #!  # Intended to point to the repo location of the linter. Optional, just for documentation purposes. | ||||||
|  |     #!  original-url: github.com/golangci/example-linter | ||||||
|  | 
 | ||||||
|  | linters: | ||||||
|  |   enable: | ||||||
|  |     - megacheck | ||||||
|  |     - govet | ||||||
|  |   disable: | ||||||
|  |     - maligned | ||||||
|  |     - prealloc | ||||||
|  |   disable-all: false | ||||||
|  |   presets: | ||||||
|  |     - bugs | ||||||
|  |     - unused | ||||||
|  |   fast: false | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | issues: | ||||||
|  |   # List of regexps of issue texts to exclude, empty list by default. | ||||||
|  |   # But independently from this option we use default exclude patterns, | ||||||
|  |   # it can be disabled by `exclude-use-default: false`. To list all | ||||||
|  |   # excluded by default patterns execute `golangci-lint run --help` | ||||||
|  |   exclude: | ||||||
|  |     - abcdef | ||||||
|  | 
 | ||||||
|  |   # Excluding configuration per-path, per-linter, per-text and per-source | ||||||
|  |   exclude-rules: | ||||||
|  |     # Exclude some linters from running on tests files. | ||||||
|  |     - path: _test\.go | ||||||
|  |       linters: | ||||||
|  |         - gocyclo | ||||||
|  |         - errcheck | ||||||
|  |         - dupl | ||||||
|  |         - gosec | ||||||
|  | 
 | ||||||
|  |     # Exclude known linters from partially hard-vendored code, | ||||||
|  |     # which is impossible to exclude via "nolint" comments. | ||||||
|  |     - path: internal/hmac/ | ||||||
|  |       text: "weak cryptographic primitive" | ||||||
|  |       linters: | ||||||
|  |         - gosec | ||||||
|  | 
 | ||||||
|  |     # Exclude some staticcheck messages | ||||||
|  |     - linters: | ||||||
|  |         - staticcheck | ||||||
|  |       text: "SA9003:" | ||||||
|  | 
 | ||||||
|  |     # Exclude lll issues for long lines with go:generate | ||||||
|  |     - linters: | ||||||
|  |         - lll | ||||||
|  |       source: "^//go:generate " | ||||||
|  | 
 | ||||||
|  |   # Independently from option `exclude` we use default exclude patterns, | ||||||
|  |   # it can be disabled by this option. To list all | ||||||
|  |   # excluded by default patterns execute `golangci-lint run --help`. | ||||||
|  |   # Default value for this option is true. | ||||||
|  |   exclude-use-default: false | ||||||
|  | 
 | ||||||
|  |   # Maximum issues count per one linter. Set to 0 to disable. Default is 50. | ||||||
|  |   max-issues-per-linter: 0 | ||||||
|  | 
 | ||||||
|  |   # Maximum count of issues with the same text. Set to 0 to disable. Default is 3. | ||||||
|  |   max-same-issues: 0 | ||||||
|  | 
 | ||||||
|  |   # Show only new issues: if there are unstaged changes or untracked files, | ||||||
|  |   # only those changes are analyzed, else only changes in HEAD~ are analyzed. | ||||||
|  |   # It's a super-useful option for integration of golangci-lint into existing | ||||||
|  |   # large codebase. It's not practical to fix all existing issues at the moment | ||||||
|  |   # of integration: much better don't allow issues in new code. | ||||||
|  |   # Default is false. | ||||||
|  |   new: false | ||||||
|  | 
 | ||||||
|  |   # Show only new issues created after git revision `REV` | ||||||
|  |   #!new-from-rev: REV | ||||||
|  |   #new-from-rev: HEAD^ | ||||||
|  | 
 | ||||||
|  |   # Show only new issues created in git patch with set file path. | ||||||
|  |   #!new-from-patch: path/to/patch/file | ||||||
|  | @ -1 +0,0 @@ | ||||||
| CODE_OF_MERIT.md |  | ||||||
							
								
								
									
										61
									
								
								CODE_OF_CONDUCT.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,61 @@ | ||||||
|  | # Code of Merit (1.0-r1) | ||||||
|  | 
 | ||||||
|  | 1. The project creators, lead developers, core team, ("The Maintainers") constitute | ||||||
|  | the managing members of the project and have final say in every decision | ||||||
|  | of the project, technical or otherwise, including overruling previous decisions. | ||||||
|  | There are no limitations to this decisional power. | ||||||
|  | 
 | ||||||
|  | 2. Contributions are an expected result of your membership on the project. | ||||||
|  | Don't expect others to do your work or help you with your work forever.  | ||||||
|  | 
 | ||||||
|  | 3. All members have the same opportunities to seek any challenge they want | ||||||
|  | within the project.  | ||||||
|  | 
 | ||||||
|  | 4. Authority or position in the project will be proportional | ||||||
|  | to the accrued contribution. Seniority must be earned. | ||||||
|  | 
 | ||||||
|  | 5. Software is evolutive: the better implementations must supersede lesser | ||||||
|  | implementations. Technical advantage is the primary evaluation metric. | ||||||
|  | 
 | ||||||
|  | 6. This is a space for technical prowess; topics outside of the project | ||||||
|  | will not be tolerated. | ||||||
|  | 
 | ||||||
|  | 7. Non technical conflicts will be discussed in a separate space. Disruption | ||||||
|  | of the project will not be allowed. | ||||||
|  | 
 | ||||||
|  | 8. Individual characteristics, including but not limited to, | ||||||
|  | body, sex, sexual preference, race, language, religion, nationality, | ||||||
|  | or political preferences are irrelevant in the scope of the project and | ||||||
|  | will not be taken into account concerning your value or that of your contribution | ||||||
|  | to the project. | ||||||
|  | 
 | ||||||
|  | 9. Discuss or debate the idea, not the person. | ||||||
|  | 
 | ||||||
|  | 10. There is no room for ambiguity: Ambiguity will be met with questioning; | ||||||
|  | further ambiguity will be met with silence. It is the responsibility | ||||||
|  | of the originator to provide requested context. | ||||||
|  | 
 | ||||||
|  | 11. If something is illegal outside the scope of the project, it is illegal | ||||||
|  | in the scope of the project. This Code of Merit does not take precedence over | ||||||
|  | governing law within the jurisdiction(s) of The Maintainers. | ||||||
|  | 
 | ||||||
|  |     (11.a) If the algorithms implemented in this project (eg., cryptography) | ||||||
|  |   are illegal or prohibited in your local jurisdiction, it is your personal | ||||||
|  |   responsibility to consider how, or if, you should attempt to participate in this | ||||||
|  |   project. The Maintainers disclaim all liability for others' actions and decisions | ||||||
|  |   in this regard. | ||||||
|  | 
 | ||||||
|  | 12. This Code of Merit governs the technical procedures of the project not the  | ||||||
|  | activities outside of it.  | ||||||
|  | 
 | ||||||
|  | 13. Participation on the project equates to agreement of this Code of Merit. | ||||||
|  | 
 | ||||||
|  | 14. No objectives beyond the stated objectives of this project are relevant | ||||||
|  | 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 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/ | ||||||
|  | @ -1,58 +0,0 @@ | ||||||
| # Code of Merit (1.0-r1) |  | ||||||
| 
 |  | ||||||
| 1. The project creators, lead developers, core team, ("The Maintainers") constitute |  | ||||||
| the managing members of the project and have final say in every decision |  | ||||||
| of the project, technical or otherwise, including overruling previous decisions. |  | ||||||
| There are no limitations to this decisional power. |  | ||||||
| 
 |  | ||||||
| 2. Contributions are an expected result of your membership on the project. |  | ||||||
| Don't expect others to do your work or help you with your work forever.  |  | ||||||
| 
 |  | ||||||
| 3. All members have the same opportunities to seek any challenge they want |  | ||||||
| within the project.  |  | ||||||
| 
 |  | ||||||
| 4. Authority or position in the project will be proportional |  | ||||||
| to the accrued contribution. Seniority must be earned. |  | ||||||
| 
 |  | ||||||
| 5. Software is evolutive: the better implementations must supersede lesser |  | ||||||
| implementations. Technical advantage is the primary evaluation metric. |  | ||||||
| 
 |  | ||||||
| 6. This is a space for technical prowess; topics outside of the project |  | ||||||
| will not be tolerated. |  | ||||||
| 
 |  | ||||||
| 7. Non technical conflicts will be discussed in a separate space. Disruption |  | ||||||
| of the project will not be allowed. |  | ||||||
| 
 |  | ||||||
| 8. Individual characteristics, including but not limited to, |  | ||||||
| body, sex, sexual preference, race, language, religion, nationality, |  | ||||||
| or political preferences are irrelevant in the scope of the project and |  | ||||||
| will not be taken into account concerning your value or that of your contribution |  | ||||||
| to the project. |  | ||||||
| 
 |  | ||||||
| 9. Discuss or debate the idea, not the person. |  | ||||||
| 
 |  | ||||||
| 10. There is no room for ambiguity: Ambiguity will be met with questioning; |  | ||||||
| further ambiguity will be met with silence. It is the responsibility |  | ||||||
| of the originator to provide requested context. |  | ||||||
| 
 |  | ||||||
| 11. If something is illegal outside the scope of the project, it is illegal |  | ||||||
| in the scope of the project. This Code of Merit does not take precedence over |  | ||||||
| governing law within the jurisdiction(s) of The Maintainers. |  | ||||||
| 
 |  | ||||||
|     (11.a) If the algorithms implemented in this project (eg., cryptography) |  | ||||||
|   are illegal or prohibited in your local jurisdiction, it is your personal |  | ||||||
|   responsibility to consider how, or if, you should attempt to participate in this |  | ||||||
|   project. The Maintainers disclaim all liability for others' actions and decisions |  | ||||||
|   in this regard. |  | ||||||
| 
 |  | ||||||
| 12. This Code of Merit governs the technical procedures of the project not the  |  | ||||||
| activities outside of it.  |  | ||||||
| 
 |  | ||||||
| 13. Participation on the project equates to agreement of this Code of Merit. |  | ||||||
| 
 |  | ||||||
| 14. No objectives beyond the stated objectives of this project are relevant |  | ||||||
| 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 (http://code-of-merit.org), version 1.0. |  | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| MIT License | MIT License | ||||||
| 
 | 
 | ||||||
| Copyright (c) 2017 - 2018 Russell Magee (hkexsh/hkexshd/hkexnet/hkexpasswd) | Copyright (c) 2017 - 2019 Russell Magee (xs/xsd/xsnet/xspasswd) | ||||||
| 
 | 
 | ||||||
| Permission is hereby granted, free of charge, to any person obtaining a copy | Permission is hereby granted, free of charge, to any person obtaining a copy | ||||||
| of this software and associated documentation files (the "Software"), to deal | of this software and associated documentation files (the "Software"), to deal | ||||||
							
								
								
									
										110
									
								
								Makefile
									
										
									
									
									
								
							
							
						
						|  | @ -1,14 +1,51 @@ | ||||||
|  | VERSION := 0.9.2 | ||||||
| .PHONY: lint vis clean common client server passwd subpkgs install uninstall reinstall | .PHONY: lint vis clean common client server passwd subpkgs install uninstall reinstall | ||||||
| 
 | 
 | ||||||
| SUBPKGS = logger spinsult hkexnet | ## Tag version of binaries with build info wrt.
 | ||||||
| TOOLS = hkexpasswd hkexsh hkexshd | ## GO111MODULE(=on) and vendor/ setup vs. $GOPATH pkg builds
 | ||||||
|  | ############################################################
 | ||||||
|  | ifeq ($(shell go env GOMOD),) | ||||||
|  | MTAG= | ||||||
|  | else | ||||||
|  | MTAG="-m" | ||||||
|  | endif | ||||||
|  | 
 | ||||||
|  | ifneq ($(VENDOR),) | ||||||
|  | GOBUILDOPTS :=-v -mod vendor | ||||||
|  | VTAG = "-v" | ||||||
|  | else | ||||||
|  | GOBUILDOPTS= | ||||||
|  | VTAG = | ||||||
|  | endif | ||||||
|  | ############################################################
 | ||||||
|  | 
 | ||||||
|  | GIT_COMMIT := $(shell git rev-list -1 HEAD) | ||||||
|  | 
 | ||||||
|  | #ifeq ($(BUILDOPTS),)
 | ||||||
|  | BUILDOPTS :=$(BUILDOPTS)"$(GOBUILDOPTS) -ldflags \"-X main.version=$(VERSION)$(MTAG)$(VTAG) -X main.gitCommit=$(GIT_COMMIT)\"" | ||||||
|  | #endif
 | ||||||
|  | 
 | ||||||
|  | SUBPKGS = logger spinsult xsnet | ||||||
|  | TOOLS = xs xsd | ||||||
| SUBDIRS = $(LIBS) $(TOOLS) | 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 | INSTPREFIX = /usr/local | ||||||
| 
 | 
 | ||||||
| all: common client server passwd | all: common client server | ||||||
| 
 | 
 | ||||||
| clean: | clean: | ||||||
|  | 	@echo "Make: $(MAKE)" | ||||||
| 	go clean . | 	go clean . | ||||||
| 	for d in $(SUBDIRS); do\
 | 	for d in $(SUBDIRS); do\
 | ||||||
| 	  $(MAKE) -C $$d clean;\
 | 	  $(MAKE) -C $$d clean;\
 | ||||||
|  | @ -16,78 +53,63 @@ clean: | ||||||
| 
 | 
 | ||||||
| subpkgs: | subpkgs: | ||||||
| 	for d in $(SUBPKGS); do\
 | 	for d in $(SUBPKGS); do\
 | ||||||
| 	  $(MAKE) -C $$d all;\
 | 	  $(MAKE) BUILDOPTS=$(BUILDOPTS) -C $$d all;\
 | ||||||
| 	done | 	done | ||||||
| 
 | 
 | ||||||
| tools: | tools: | ||||||
| 	for d in $(TOOLS); do\
 | 	for d in $(TOOLS); do\
 | ||||||
| 	  $(MAKE) -C $$d all;\
 | 	  $(MAKE) BUILDOPTS=$(BUILDOPTS) -C $$d all;\
 | ||||||
| 	done | 	done | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| common: | common: | ||||||
|  | 	go build . | ||||||
| 	go install . | 	go install . | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| client: common | client: common | ||||||
| 	$(MAKE) -C hkexsh | 	$(MAKE) BUILDOPTS=$(BUILDOPTS) -C xs | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| ifeq ($(MSYSTEM),) |  | ||||||
| ifneq ($(GOOS),windows) |  | ||||||
| server: common | server: common | ||||||
| 	$(MAKE) -C hkexshd | ifeq ($(MSYSTEM),MSYS) | ||||||
|  | 	echo "Build of xsd server for Windows not yet supported" | ||||||
| else | else | ||||||
| 	echo "Cross-build of hkexshd server for Windows not yet supported" | 	$(MAKE) BUILDOPTS=$(BUILDOPTS) -C xsd | ||||||
| endif | endif | ||||||
| else |  | ||||||
| server: common |  | ||||||
| 	echo "hkexshd server not (yet) supported on Windows" |  | ||||||
| endif |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| passwd: common |  | ||||||
| 	$(MAKE) -C hkexpasswd |  | ||||||
| 
 | 
 | ||||||
| vis: | vis: | ||||||
| 	@which go-callvis >/dev/null 2>&1; \
 | 	@which go-callvis >/dev/null 2>&1; \
 | ||||||
| 	stat=$$?; if [ $$stat -ne "0" ]; then \
 | 	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 \
 | 	else \
 | ||||||
| 	  make -C hkexsh vis;\
 | 	  $(MAKE) -C xs vis;\
 | ||||||
| 	  make -C hkexshd vis;\
 | 	  $(MAKE) -C xsd vis;\
 | ||||||
| 	  make -C hkexpasswd vis; \
 |  | ||||||
| 	fi | 	fi | ||||||
| 
 | 
 | ||||||
| lint: | lint: | ||||||
| 	make -C hkexpasswd lint | 	$(MAKE) -C xsd lint | ||||||
| 	make -C hkexshd lint | 	$(MAKE) -C xs lint | ||||||
| 	make -C hkexsh lint |  | ||||||
| 
 | 
 | ||||||
| reinstall: uninstall install | reinstall: uninstall install | ||||||
| 
 | 
 | ||||||
| install: | install: | ||||||
| 	cp hkexsh/hkexsh $(INSTPREFIX)/bin | 	echo "WIN_MSYS:" $(WIN_MSYS) | ||||||
| ifeq ($(MSYSTEM),) | ifdef WIN_MSYS | ||||||
| ifneq ($(GOOS),windows) | 	cp xs/mintty_wrapper.sh $(INSTPREFIX)/bin/xs | ||||||
| 	cp hkexshd/hkexshd hkexpasswd/hkexpasswd $(INSTPREFIX)/sbin | 	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 | else | ||||||
| 	mv $(INSTPREFIX)/bin/hkexsh $(INSTPREFIX)/bin/_hkexsh | 	cp xs/xs $(INSTPREFIX)/bin | ||||||
| 	cp hkexsh/mintty_wrapper.sh $(INSTPREFIX)/bin/hkexsh | 	cd $(INSTPREFIX)/bin && ln -s xs xc && cd - | ||||||
| 	echo "Cross-build of hkexshd server for Windows not yet supported" | 	cp xsd/xsd $(INSTPREFIX)/sbin | ||||||
| endif | endif | ||||||
| else |  | ||||||
| 	echo "Cross-build of hkexshd server for Windows not yet supported" |  | ||||||
| endif |  | ||||||
| 	cd $(INSTPREFIX)/bin && ln -s hkexsh hkexcp && cd - |  | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
| uninstall: | uninstall: | ||||||
| 	rm -f $(INSTPREFIX)/bin/hkexsh $(INSTPREFIX)/bin/hkexcp $(INSTPREFIX)/bin/_hkexsh | 	rm -f $(INSTPREFIX)/bin/xs $(INSTPREFIX)/bin/xc \
 | ||||||
| ifeq ($(MSYSTEM),) | 	$(INSTPREFIX)/bin/_xs $(INSTPREFIX)/bin/_xc | ||||||
| ifneq ($(GOOS),windows) | ifndef $(WIN_MSYS) | ||||||
| 	rm -f $(INSTPREFIX)/sbin/hkexshd $(INSTPREFIX)/sbin/hkexpasswd | 	rm -f $(INSTPREFIX)/sbin/xsd | ||||||
| else |  | ||||||
| endif |  | ||||||
| else |  | ||||||
| endif | endif | ||||||
|  |  | ||||||
							
								
								
									
										183
									
								
								README.md
									
										
									
									
									
								
							
							
						
						|  | @ -1,30 +1,33 @@ | ||||||
| [](https://godoc.org/blitter.com/go/hkexsh) | [](https://godoc.org/blitter.com/go/xs) | ||||||
| 
 | 
 | ||||||
| 
 | # XS | ||||||
| HKExSh |  | ||||||
| -- | -- | ||||||
| 
 | 
 | ||||||
| HKExSh (**H**erradura**K**yber**Ex** **Sh**ell) is a golang implementation of a simple | XS (**X**perimental **S**hell) is a golang implementation of a simple remote shell client and | ||||||
| remote shell client and server, similar in role to ssh, offering | server, similar in role to ssh, offering encrypted interactive and non-interactive sessions (remote commands), | ||||||
| encrypted interactive and non-interactive sessions, file copying and tunnels with traffic obfuscation ('chaffing'). | remote file copying and tunnels with optional traffic obfuscation ('chaffing'). | ||||||
|  | 
 | ||||||
|  | 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.** | ||||||
| 
 | 
 | ||||||
| *** | *** | ||||||
| 
 | 
 | ||||||
| **NOTE: Due to the experimental nature of the HerraduraKEx and Kyber IND-CCA-2 algorithms, 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.** | The client and server programs (xs and xsd) use a mostly drop-in | ||||||
| 
 |  | ||||||
| *** |  | ||||||
| 
 |  | ||||||
| The client and server programs (hkexsh and hkexshd) use a mostly drop-in |  | ||||||
| replacement for golang's standard golang/pkg/net facilities (net.Dial(), net.Listen(), net.Accept() | replacement for golang's standard golang/pkg/net facilities (net.Dial(), net.Listen(), net.Accept() | ||||||
| and the net.Conn type), which automatically negotiate keying material for | and the net.Conn type), which automatically negotiate keying material for | ||||||
| secure sockets using one of a selectable set of experimental key exchange (KEX) or | secure sockets using one of a selectable set of experimental key exchange (KEX) or | ||||||
| key encapsulation mechanisms (KEM). | key encapsulation mechanisms (KEM). | ||||||
| 
 | 
 | ||||||
|  | ### Key Exchange | ||||||
| Currently supported exchanges are: | Currently supported exchanges are: | ||||||
| 
 | 
 | ||||||
| * The HerraduraKEx key exchange algorithm first released at | * The HerraduraKEx key exchange algorithm first released at | ||||||
| [Omar Elejandro Herrera Reyna's HerraduraKEx project](http://github.com/Caume/HerraduraKEx); | [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 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 FrodoKEM algorithm [frodokem.org](https://frodokem.org/) :: Go version by [Eduardo E. S. Riccardi](https://github.com/kuking/go-frodokem) | ||||||
| 
 | 
 | ||||||
| Currently supported session algorithms: | Currently supported session algorithms: | ||||||
| 
 | 
 | ||||||
|  | @ -33,23 +36,27 @@ Currently supported session algorithms: | ||||||
| * Twofish-128 | * Twofish-128 | ||||||
| * Blowfish-64 | * Blowfish-64 | ||||||
| * CryptMTv1 (64bit) (https://eprint.iacr.org/2005/165.pdf) | * CryptMTv1 (64bit) (https://eprint.iacr.org/2005/165.pdf) | ||||||
|  | * ChaCha20 (https://github.com/aead/chacha20) | ||||||
| 
 | 
 | ||||||
| [HMAC] | [HMAC] | ||||||
| * HMAC-SHA256 | * HMAC-SHA256 | ||||||
| * HMAC-SHA512 | * HMAC-SHA512 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| Calls to hkexnet.Dial() and hkexnet.Listen()/Accept() are generally the same as calls to the equivalents within the _net_ package; however upon connection a key exchange automatically occurs whereby client and server independently derive the same keying material, and all following traffic is secured by a symmetric encryption algorithm. | ### Conn | ||||||
|  | Calls to xsnet.Dial() and xsnet.Listen()/Accept() are generally the same as calls to the equivalents within the _net_ package; however upon connection a key exchange automatically occurs whereby client and server independently derive the same keying material, and all following traffic is secured by a symmetric encryption algorithm. | ||||||
| 
 | 
 | ||||||
| Above the hkexnet.Conn layer, the server and client apps in this repository (hkexshd/ and hkexsh/ respectively) negotiate session settings (cipher/hmac algorithms, interactive/non-interactive, tunnels, if any, etc.) to be used for communication. | ### Session Negotiation | ||||||
|  | Above the xsnet.Conn layer, the server and client apps in this repository (xsd/ and xs/ respectively) negotiate session settings (cipher/hmac algorithms, interactive/non-interactive mode, tunnel specifiers, etc.) to be used for communication. | ||||||
| 
 | 
 | ||||||
| Packets are subject to random padding (size, prefix/postfix), and (optionally) the client and server | ### Padding and Chaffing | ||||||
| channels can both send _chaff_ packets at random defineable intervals to help thwart analysis of session activity (applicable to interactive and non-interactive command sessions, file copies and tunnels). | Packets are subject to padding (random size, randomly applied as prefix or postfix), and optionally the client and server channels can both send _chaff_ packets at random defineable intervals to help thwart analysis of session activity (applicable to interactive and non-interactive command sessions, file copies and tunnels). | ||||||
| 
 | 
 | ||||||
| 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 tunnel specifier so that they can be de-multiplexed and delivered to the proper tunnel endpoints. | ### Mux/Demux of Chaffing and Tunnel Data | ||||||
| 
 | 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. | ||||||
| Finally, within the hkexpasswd/ directory is a password-setting utility. HKExSh uses its own passwd file distinct from the system /etc/passwd to authenticate clients, using standard bcrypt+salt storage. |  | ||||||
| 
 | 
 | ||||||
|  | ### Accounts and Passwords | ||||||
|  | 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 | HERRADURA KEX | ||||||
| 
 | 
 | ||||||
|  | @ -64,76 +71,106 @@ 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.) | 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: | ### Dependencies: | ||||||
| -- | 
 | ||||||
| * Recent version of go (tested, at various times, with go-1.9 to go-1.11.1) | * 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/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/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 | * [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 | * [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/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/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 | * [blitter.com/go/cryptmt](https://gogs.blitter.com/RLabs/cryptmt) // CryptMTv1 stream cipher | ||||||
| 
 | 
 | ||||||
| Get source code | ### Get source code | ||||||
| -- | 
 | ||||||
| * $ go get -u blitter.com/go/hkexsh | ``` | ||||||
| * $ cd $GOPATH/src/blitter.com/go/hkexsh | $ go get -u blitter.com/go/xs | ||||||
| * $ go build ./... # install all dependent go pkgs | $ cd $GOPATH/src/blitter.com/go/xs | ||||||
|  | $ go build ./... # install all dependent go pkgs | ||||||
|  | ``` | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| To build | ### To build | ||||||
| -- |  | ||||||
| * $ cd $GOPATH/src/blitter.com/go/hkexsh |  | ||||||
| * $ make clean all |  | ||||||
| 
 | 
 | ||||||
| To install, uninstall, re-install | ``` | ||||||
| -- | $ cd $GOPATH/src/blitter.com/go/xs | ||||||
| * $ sudo make [install | uninstall | reinstall] | $ make clean all | ||||||
|  | ``` | ||||||
| 
 | 
 | ||||||
| To manage service (assuming openrc init) | ### To install, uninstall, re-install | ||||||
| -- |  | ||||||
| * $ sudo rc-config [start | restart | stop] hkexshd |  | ||||||
| 
 | 
 | ||||||
|  | ``` | ||||||
|  | $ sudo make [install | uninstall | reinstall] | ||||||
|  | ``` | ||||||
| 
 | 
 | ||||||
| An example init script (hkexshd.initrc) is provided. Consult your Linux distribution documentation for proper service/daemon installation. Default assumes installation in /usr/local/sbin (hkexshd, hkexpasswd) and /usr/local/bin (hkexsh/hkexcp symlink). | ### 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, | ||||||
| 
 | 
 | ||||||
| To set accounts & passwords: | ``` | ||||||
| -- | $ sudo cp xsd.initrc /etc/init.d/xsd | ||||||
| * $ sudo touch /etc/hkexsh.passwd | $ sudo rc-config add xsd default | ||||||
| * $ sudo hkexpasswd/hkexpasswd -u joebloggs | ``` | ||||||
| * $ <enter a password, enter again to confirm> |  | ||||||
| 
 | 
 | ||||||
|  | ### 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 | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ### To set accounts & passwords: | ||||||
|  | 
 | ||||||
|  | ``` | ||||||
|  | $ sudo touch /etc/xs.passwd | ||||||
|  | $ sudo xspasswd/xspasswd -u joebloggs | ||||||
|  | $ <enter a password, enter again to confirm> | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ### Testing Client and Server from $GOPATH dev tree (w/o 'make install') | ||||||
| 
 | 
 | ||||||
| Testing Client and Server from $GOPATH dev tree (w/o 'make install') |  | ||||||
| -- |  | ||||||
| In separate shells A and B: | In separate shells A and B: | ||||||
| * [A]$ cd hkexshd && sudo ./hkexshd &  # add -d for debugging | ``` | ||||||
|  | [A]$ cd xsd && sudo ./xsd &  # add -d for debugging | ||||||
|  | ``` | ||||||
| 
 | 
 | ||||||
| Interactive shell | Interactive shell | ||||||
| * [B]$ cd hkexsh && ./hkexsh joebloggs@host-or-ip # add -d for debugging | ``` | ||||||
|  | [B]$ cd xs && ./xs joebloggs@host-or-ip # add -d for debugging | ||||||
|  | ``` | ||||||
| 
 | 
 | ||||||
| One-shot command | One-shot command | ||||||
| * [B]$ cd hkexsh && ./hkexsh -x "ls /tmp" joebloggs@host-or-ip | ``` | ||||||
|  | [B]$ cd xs && ./xs -x "ls /tmp" joebloggs@host-or-ip | ||||||
|  | ``` | ||||||
| 
 | 
 | ||||||
| WARNING WARNING WARNING: the -d debug flag will echo passwords to the log/console! | WARNING WARNING WARNING: the -d debug flag will echo passwords to the log/console! | ||||||
| Logging on Linux usually goes to /var/log/syslog and/or /var/log/debug, /var/log/daemon.log. | Logging on Linux usually goes to /var/log/syslog and/or /var/log/debug, /var/log/daemon.log. | ||||||
| 
 | 
 | ||||||
| NOTE if running client (hkexsh) with -d, one will likely need to run 'reset' afterwards | NOTE if running client (xs) with -d, one will likely need to run 'reset' afterwards | ||||||
| to fix up the shell tty afterwards, as stty echo may not be restored if client crashes | to fix up the shell tty afterwards, as stty echo may not be restored if client crashes | ||||||
| or is interrupted. | or is interrupted. | ||||||
| 
 | 
 | ||||||
| Setting up an 'authtoken' for scripted (password-free) logins | ### Setting up an 'authtoken' for scripted (password-free) logins | ||||||
| -- | 
 | ||||||
| Use the -g option of hkexsh to request a token from the remote server, which will return a | 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/.hkexsh_id to allow logins without | hostname:token string. Place this string into $HOME/.xs_id to allow logins without | ||||||
| entering a password (obviously, $HOME/.hkexsh_id on both server and client for the user | entering a password (obviously, $HOME/.xs_id on both server and client for the user | ||||||
| should *not* be world-readable.) | should *not* be world-readable.) | ||||||
| 
 | 
 | ||||||
| File Copying using hkexcp | ### File Copying using xc | ||||||
| -- | 
 | ||||||
| hkexcp is a symlink to hkexsh, and the binary checks its own filename to determine whether | xc is a symlink to xs, and the binary checks its own filename to determine whether | ||||||
| it is being invoked in 'shell' or 'copy' mode. Refer to the '-h' output for differences in | it is being invoked in 'shell' or 'copy' mode. Refer to the '-h' output for differences in | ||||||
| accepted options. | accepted options. | ||||||
| 
 | 
 | ||||||
|  | @ -143,25 +180,37 @@ remote user. File operations are all performed as the remote user, so account pe | ||||||
| as expected. | as expected. | ||||||
| 
 | 
 | ||||||
| Local (client) to remote (server) copy: | Local (client) to remote (server) copy: | ||||||
| * hkexcp fileA /some/where/fileB /some/where/else/dirC joebloggs@host-or-ip:remoteDir | ``` | ||||||
|  | $ xc fileA /some/where/fileB /some/where/else/dirC joebloggs@host-or-ip:remoteDir | ||||||
|  | ``` | ||||||
| 
 | 
 | ||||||
| Remote (server) to local (client) copy: | Remote (server) to local (client) copy: | ||||||
| * hkexcp joebloggs@host-or-ip:/remoteDirOrFile /some/where/local/Dir | ``` | ||||||
|  | $ xc joebloggs@host-or-ip:/remoteDirOrFile /some/where/local/Dir | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | xc uses a 'tarpipe' to send file data over the encrypted channel. Use the -d flag on client or server to see the generated tar commands if you're curious. | ||||||
| 
 | 
 | ||||||
| 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. | 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. | ||||||
| 
 | 
 | ||||||
| hkexcp uses tar (a 'tarpipe') with gzip compression, sending tar data over the hkex encrypted channel. Use the -d flag on client or server to see the generated tar commands if you're curious. | 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 <bytes_per_second>). | ||||||
|  | 
 | ||||||
|  | ### Tunnels | ||||||
| 
 | 
 | ||||||
| Tunnels |  | ||||||
| -- |  | ||||||
| Simple tunnels (client -> server, no reverse tunnels for now) are supported. | Simple tunnels (client -> server, no reverse tunnels for now) are supported. | ||||||
| 
 | 
 | ||||||
| Syntax: hkexsh -T=<tunspec>{,<tunspec>...} | Syntax: xs -T=<tunspec>{,<tunspec>...} | ||||||
| .. where <tunspec> is <localport:remoteport> | .. where <tunspec> is <localport:remoteport> | ||||||
| 
 | 
 | ||||||
| Example, tunnelling ssh through hkexsh (NOTE [issue #15](https://blitter.com:3000/RLabs/hkexsh/issues/15)) | Example, tunnelling ssh through xs | ||||||
| 
 | 
 | ||||||
| * [server side] $ sudo /usr/sbin/sshd -p 7002 | * [server side] ```$ sudo /usr/sbin/sshd -p 7002``` | ||||||
| * [client side, term A] $ hkexsh -T=6002:7002 user@server | * [client side, term A] ```$ xs -T=6002:7002 user@server``` | ||||||
| * [client side, term B] $ ssh user@localhost -p 6002 | * [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``` | ||||||
|  |  | ||||||
							
								
								
									
										1
									
								
								TODO.txt
									
										
									
									
									
								
							
							
						
						|  | @ -18,6 +18,7 @@ Architecture | ||||||
|   (parts split out into hkexnet/*, hkexsession.go) |   (parts split out into hkexnet/*, hkexsession.go) | ||||||
| (DONE) - Make KEx fully-pluggable: isolate all code to do with Herradura into a | (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.) |   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 | Features | ||||||
| (DONE) - Support for hkcp (hkex-cp) - secure file copy protocol | (DONE) - Support for hkcp (hkex-cp) - secure file copy protocol | ||||||
|  |  | ||||||
							
								
								
									
										227
									
								
								auth.go
									
										
									
									
									
										Executable file
									
								
							
							
						
						|  | @ -0,0 +1,227 @@ | ||||||
|  | package xs | ||||||
|  | 
 | ||||||
|  | // Package xs - a secure terminal client/server written from scratch in Go | ||||||
|  | // | ||||||
|  | // Copyright (c) 2017-2020 Russell Magee | ||||||
|  | // Licensed under the terms of the MIT license (see LICENSE.mit in this | ||||||
|  | // distribution) | ||||||
|  | // | ||||||
|  | // golang implementation by Russ Magee (rmagee_at_gmail.com) | ||||||
|  | 
 | ||||||
|  | // Authentication routines for the HKExSh | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"bytes" | ||||||
|  | 	"encoding/csv" | ||||||
|  | 	"errors" | ||||||
|  | 	"fmt" | ||||||
|  | 	"io" | ||||||
|  | 	"io/ioutil" | ||||||
|  | 	"log" | ||||||
|  | 	"os" | ||||||
|  | 	"os/user" | ||||||
|  | 	"runtime" | ||||||
|  | 	"strings" | ||||||
|  | 
 | ||||||
|  | 	"github.com/jameskeane/bcrypt" | ||||||
|  | 	passlib "gopkg.in/hlandau/passlib.v1" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | 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 { | ||||||
|  | 		pwFileName = "unsupported" | ||||||
|  | 	} | ||||||
|  | 	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(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) | ||||||
|  | 	} | ||||||
|  | 	r := csv.NewReader(bytes.NewReader(b)) | ||||||
|  | 
 | ||||||
|  | 	r.Comma = ':' | ||||||
|  | 	r.Comment = '#' | ||||||
|  | 	r.FieldsPerRecord = 3 // username:salt:authCookie [TODO:disallowedCmdList (a,b,...)] | ||||||
|  | 	for { | ||||||
|  | 		record, err := r.Read() | ||||||
|  | 		if err == io.EOF { | ||||||
|  | 			// Use dummy entry if user not found | ||||||
|  | 			// (prevent user enumeration attack via obvious timing diff; | ||||||
|  | 			// ie., not attempting any auth at all) | ||||||
|  | 			record = []string{"$nosuchuser$", | ||||||
|  | 				"$2a$12$l0coBlRDNEJeQVl6GdEPbU", | ||||||
|  | 				"$2a$12$l0coBlRDNEJeQVl6GdEPbUC/xmuOANvqgmrMVum6S4i.EXPgnTXy6"} | ||||||
|  | 			username = "$nosuchuser$" | ||||||
|  | 			err = nil | ||||||
|  | 		} | ||||||
|  | 		if err != nil { | ||||||
|  | 			log.Fatal(err) | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if username == record[0] { | ||||||
|  | 			tmp, err := bcrypt.Hash(auth, record[1]) | ||||||
|  | 			if err != nil { | ||||||
|  | 				break | ||||||
|  | 			} | ||||||
|  | 			if tmp == record[2] && username != "$nosuchuser$" { | ||||||
|  | 				valid = true | ||||||
|  | 			} | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	// Security scrub | ||||||
|  | 	for i := range b { | ||||||
|  | 		b[i] = 0 | ||||||
|  | 	} | ||||||
|  | 	r = nil | ||||||
|  | 	runtime.GC() | ||||||
|  | 
 | ||||||
|  | 	_, 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 | ||||||
|  | // via the -g option. | ||||||
|  | // The function also check system /etc/passwd to cross-check the user | ||||||
|  | // actually exists. | ||||||
|  | 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 := ctx.userlookup(username) | ||||||
|  | 	if ue != nil { | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	b, e := ctx.reader(fmt.Sprintf("%s/.xs_id", u.HomeDir)) | ||||||
|  | 	if e != nil { | ||||||
|  | 		log.Printf("INFO: Cannot read %s/.xs_id\n", u.HomeDir) | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	r := csv.NewReader(bytes.NewReader(b)) | ||||||
|  | 
 | ||||||
|  | 	r.Comma = ':' | ||||||
|  | 	r.Comment = '#' | ||||||
|  | 	r.FieldsPerRecord = 2 // connhost:authtoken | ||||||
|  | 	for { | ||||||
|  | 		record, err := r.Read() | ||||||
|  | 		if err == io.EOF { | ||||||
|  | 			return false | ||||||
|  | 		} | ||||||
|  | 		record[0] = strings.TrimSpace(record[0]) | ||||||
|  | 		record[1] = strings.TrimSpace(record[1]) | ||||||
|  | 		//fmt.Println("auth:", auth, "record:", | ||||||
|  | 		//	strings.Join([]string{record[0], record[1]}, ":")) | ||||||
|  | 
 | ||||||
|  | 		if (connhostname == record[0]) && | ||||||
|  | 			(auth == strings.Join([]string{record[0], record[1]}, ":")) { | ||||||
|  | 			valid = true | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	_, 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
									
								
							
							
						
						|  | @ -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:abcdefg\nhostB: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: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") | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										105
									
								
								bacillus/ci_pushbuild.sh
									
										
									
									
									
										Executable file
									
								
							
							
						
						|  | @ -0,0 +1,105 @@ | ||||||
|  | #!/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 | ||||||
|  | export GO111MODULE=on | ||||||
|  | # GOCACHE will be phased out in v1.12. [github.com/golang/go/issues/26809] | ||||||
|  | export GOCACHE="${HOME}/.cache/go-build" | ||||||
|  | 
 | ||||||
|  | echo "workdir: ${BACILLUS_WORKDIR}" | ||||||
|  | mkdir -p "${BACILLUS_ARTFDIR}" | ||||||
|  | 
 | ||||||
|  | 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 | ||||||
|  | 
 | ||||||
|  | ############ | ||||||
|  | stage "Build" | ||||||
|  | ############ | ||||||
|  | make all | ||||||
|  | 
 | ||||||
|  | ############ | ||||||
|  | stage "UnitTests" | ||||||
|  | ############ | ||||||
|  | go test -v . | ||||||
|  | 
 | ||||||
|  | ############ | ||||||
|  | stage "Test(Authtoken)" | ||||||
|  | ############ | ||||||
|  | if [ -f ~/.xs_id ]; then | ||||||
|  |   echo "Clearing test user $USER ~/.xs_id file ..." | ||||||
|  |   mv ~/.xs_id ~/.xs_id.bak | ||||||
|  | fi | ||||||
|  | echo "Setting dummy authtoken in ~/.xs_id ..." | ||||||
|  | echo "localhost:asdfasdfasdf" >~/.xs_id | ||||||
|  | echo "Performing remote command on @localhost via authtoken login ..." | ||||||
|  | tokentest=$(timeout 10 xs -x "echo -n FOO" @localhost) | ||||||
|  | if [ "${tokentest}" != "FOO" ]; then | ||||||
|  |   echo "AUTHTOKEN LOGIN FAILED" | ||||||
|  |   exit 1 | ||||||
|  | else | ||||||
|  |   echo "client cmd performed OK." | ||||||
|  |   unset tokentest | ||||||
|  | fi | ||||||
|  | 
 | ||||||
|  | ############ | ||||||
|  | stage "Test(S->C)" | ||||||
|  | ############ | ||||||
|  | echo "Testing secure copy from server -> client ..." | ||||||
|  | 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(C->S)" | ||||||
|  | ############ | ||||||
|  | echo "TODO ..." | ||||||
|  | 
 | ||||||
|  | if [ -f ~/.xs_id.bak ]; then | ||||||
|  |   echo "Restoring test user $USER ~/.xs_id file ..." | ||||||
|  |   mv ~/.xs_id.bak ~/.xs_id | ||||||
|  | fi | ||||||
|  | 
 | ||||||
|  | ############ | ||||||
|  | stage "Lint" | ||||||
|  | ############ | ||||||
|  | make lint | ||||||
|  | 
 | ||||||
|  | ############ | ||||||
|  | stage "Artifacts" | ||||||
|  | ############ | ||||||
|  | echo -n "Creating tarfile ..." | ||||||
|  | tar -cz --exclude=.git --exclude=cptest -f ${BACILLUS_ARTFDIR}/xs.tgz . | ||||||
|  | 
 | ||||||
|  | ############ | ||||||
|  | stage "Cleanup" | ||||||
|  | ############ | ||||||
|  | # nop | ||||||
|  | 
 | ||||||
|  | echo | ||||||
|  | echo "--Done--" | ||||||
							
								
								
									
										11
									
								
								consts.go
									
										
									
									
									
								
							
							
						
						|  | @ -1,13 +1,10 @@ | ||||||
| // Package hkexsh - a secure terminal client/server written from scratch in Go | // Package xs - a secure terminal client/server written from scratch in Go | ||||||
| // | // | ||||||
| // Copyright (c) 2017-2018 Russell Magee | // Copyright (c) 2017-2020 Russell Magee | ||||||
| // Licensed under the terms of the MIT license (see LICENSE.mit in this | // Licensed under the terms of the MIT license (see LICENSE.mit in this | ||||||
| // distribution) | // distribution) | ||||||
| // | // | ||||||
| // golang implementation by Russ Magee (rmagee_at_gmail.com) | // golang implementation by Russ Magee (rmagee_at_gmail.com) | ||||||
| package hkexsh | package xs | ||||||
| 
 | 
 | ||||||
| // common constants for the HKExSh | // common constants for the XS (Xperimental Shell) | ||||||
| 
 |  | ||||||
| // Version string returned by tools |  | ||||||
| const Version = "0.8.0 (NO WARRANTY)" |  | ||||||
|  |  | ||||||
							
								
								
									
										6
									
								
								cptest.sha1sum
									
										
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,6 @@ | ||||||
|  | 306637b5c621892078ebadd9454a78820a000598  cptest/file16KB | ||||||
|  | 1a118dfff291352eb4aec02c34f4f957669460fc  cptest/file1KB | ||||||
|  | f474d5da45890b7cb5b0ae84c8ade5abcb3b4474  cptest/file32KB | ||||||
|  | 03939175ceac92b9c6464d037a0243e22563c423  cptest/file6B | ||||||
|  | da67c7698b25d94c0cc20284ba9d4008cdee201b  cptest/subdir/file32MB | ||||||
|  | 9da9888265371375b48c224b94a0b3132b7ddc41  cptest/subdir/file64MB | ||||||
							
								
								
									
										
											BIN
										
									
								
								cptest/file16KB
									
										
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								cptest/file1KB
									
										
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								cptest/file32KB
									
										
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										1
									
								
								cptest/file6B
									
										
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1 @@ | ||||||
|  | fileB | ||||||
							
								
								
									
										
											BIN
										
									
								
								cptest/subdir/file32MB
									
										
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								cptest/subdir/file64MB
									
										
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										34
									
								
								go.mod
									
										
									
									
									
								
							
							
						
						|  | @ -1,14 +1,30 @@ | ||||||
| module blitter.com/go/hkexsh | module blitter.com/go/xs | ||||||
|  | 
 | ||||||
|  | go 1.12 | ||||||
| 
 | 
 | ||||||
| require ( | require ( | ||||||
| 	blitter.com/go/cryptmt v1.0.0 | 	blitter.com/go/cryptmt v1.0.2 | ||||||
| 	blitter.com/go/goutmp v1.0.0 | 	blitter.com/go/goutmp v1.0.5 | ||||||
| 	blitter.com/go/herradurakex v1.0.0 | 	blitter.com/go/herradurakex v1.0.0 | ||||||
| 	blitter.com/go/mtwist v1.0.0 // indirect | 	blitter.com/go/kyber v0.0.0-20200130200857-6f2021cb88d9 | ||||||
| 	git.schwanenlied.me/yawning/kyber.git v0.0.0-20180530164001-a270899bd22c | 	blitter.com/go/mtwist v1.0.1 // indirect | ||||||
|  | 	blitter.com/go/newhope v0.0.0-20200130200750-192fc08a8aae | ||||||
|  | 	github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da | ||||||
|  | 	github.com/creack/pty v1.1.11 | ||||||
| 	github.com/jameskeane/bcrypt v0.0.0-20120420032655-c3cd44c1e20f | 	github.com/jameskeane/bcrypt v0.0.0-20120420032655-c3cd44c1e20f | ||||||
| 	github.com/kr/pty v1.1.3 | 	github.com/klauspost/reedsolomon v1.9.9 // indirect | ||||||
| 	github.com/mattn/go-isatty v0.0.4 | 	github.com/kuking/go-frodokem v1.0.1 | ||||||
| 	golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9 | 	github.com/mattn/go-isatty v0.0.12 | ||||||
| 	golang.org/x/sys v0.0.0-20181213200352-4d1cda033e06 | 	github.com/mmcloughlin/avo v0.0.0-20200523190732-4439b6b2c061 // indirect | ||||||
|  | 	github.com/pkg/errors v0.9.1 // indirect | ||||||
|  | 	github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 // indirect | ||||||
|  | 	github.com/templexxx/xor v0.0.0-20191217153810-f85b25db303b // indirect | ||||||
|  | 	github.com/tjfoc/gmsm v1.3.1 // indirect | ||||||
|  | 	github.com/xtaci/kcp-go v5.4.20+incompatible | ||||||
|  | 	github.com/xtaci/lossyconn v0.0.0-20200209145036-adba10fffc37 // indirect | ||||||
|  | 	golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37 | ||||||
|  | 	golang.org/x/sys v0.0.0-20200523222454-059865788121 | ||||||
|  | 	gopkg.in/hlandau/easymetric.v1 v1.0.0 // indirect | ||||||
|  | 	gopkg.in/hlandau/measurable.v1 v1.0.1 // indirect | ||||||
|  | 	gopkg.in/hlandau/passlib.v1 v1.0.10 | ||||||
| ) | ) | ||||||
|  |  | ||||||
							
								
								
									
										118
									
								
								go.sum
									
										
									
									
									
								
							
							
						
						|  | @ -1,36 +1,94 @@ | ||||||
| blitter.com/go/cryptmt v0.0.0-20181209042848-f3e54a9d98fa h1:M9Ewnr7XZYJANeUj9Jm1ucMwL8rEyHgXcHhIw3RQtB8= | blitter.com/go/chacha20 v0.0.0-20200130200441-214e4085f54c h1:LcnFFg6MCIJHf26P7eOUST45fNLHJI5erq0gWZaDLCo= | ||||||
| blitter.com/go/cryptmt v0.0.0-20181209042848-f3e54a9d98fa/go.mod h1:tdME2J3O4agaDAYIYNQzzuB28yVGnPSMmV3a/ucSU84= | blitter.com/go/chacha20 v0.0.0-20200130200441-214e4085f54c/go.mod h1:EMJtRcf22WCtHGiXCw+NB/Sb/PYcXtUgUql6LDEwyXo= | ||||||
| blitter.com/go/cryptmt v1.0.0 h1:n+cNP/ReZrNe/w5FbD8DSfv0Wpj48nxhmMoLEk4hPXs= | blitter.com/go/cryptmt v1.0.2 h1:ZcLhQk7onUssXyQwG3GdXDXctCVnNL+b7aFuvwOdKXc= | ||||||
| blitter.com/go/cryptmt v1.0.0/go.mod h1:tdME2J3O4agaDAYIYNQzzuB28yVGnPSMmV3a/ucSU84= | blitter.com/go/cryptmt v1.0.2/go.mod h1:tdME2J3O4agaDAYIYNQzzuB28yVGnPSMmV3a/ucSU84= | ||||||
| blitter.com/go/goutmp v0.0.0-20181114075424-907ffc4058d9 h1:kcFQxwfPpVMBcLg9GIcHJmSW4ZNrcpeTUFD8wpIwu9Y= | blitter.com/go/goutmp v1.0.5 h1:isP6bxSs1O06Oy7wB8u4y5SgLr22txfjg/gjG4qn0Og= | ||||||
| blitter.com/go/goutmp v0.0.0-20181114075424-907ffc4058d9/go.mod h1:gtlbjC8xGzMk/Cf0BpnVltSa3awOqJ+B5WAxVptTMxk= | blitter.com/go/goutmp v1.0.5/go.mod h1:gtlbjC8xGzMk/Cf0BpnVltSa3awOqJ+B5WAxVptTMxk= | ||||||
| blitter.com/go/goutmp v1.0.0 h1:9BPQTnahoMHyF+IC8gj/Em+i2RDZtVSupoEgRhmaJg4= |  | ||||||
| blitter.com/go/goutmp v1.0.0/go.mod h1:gtlbjC8xGzMk/Cf0BpnVltSa3awOqJ+B5WAxVptTMxk= |  | ||||||
| blitter.com/go/herradurakex v0.0.0-20181207001539-873ba2e58872 h1:W0dt8bwmGO7mUr2F5E1R4xjbgYc0xYF/kaAbkVudeNk= |  | ||||||
| blitter.com/go/herradurakex v0.0.0-20181207001539-873ba2e58872/go.mod h1:m3+vYZX+2dDjdo+n/HDnXEYJX9pwmNeQLgAfJM8mtxw= |  | ||||||
| blitter.com/go/herradurakex v1.0.0 h1:6XaxY+JLT1HUWPF0gYJnjX3pVjrw4YhYZEzZ1U0wkyc= | 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/herradurakex v1.0.0/go.mod h1:m3+vYZX+2dDjdo+n/HDnXEYJX9pwmNeQLgAfJM8mtxw= | ||||||
| blitter.com/go/mtwist v0.0.0-20181024062339-1a11c643f88c h1:zQ6vgMPQ8J3ZJNYsskpfjeY1eoBbLjCJPeZGCZYdVbI= | blitter.com/go/kyber v0.0.0-20200130200857-6f2021cb88d9 h1:D45AnrNphtvczBXRp5JQicZRTgaK/Is5bgPDDvRKhTc= | ||||||
| blitter.com/go/mtwist v0.0.0-20181024062339-1a11c643f88c/go.mod h1:aU82Nx8+b1v8oZRNqImfEDzDTPim81rY0ACKAIclV18= | blitter.com/go/kyber v0.0.0-20200130200857-6f2021cb88d9/go.mod h1:SK6QfGG72lIfKW1Td0wH7f0wwN5nSIhV3K+wvzGNjrw= | ||||||
| blitter.com/go/mtwist v1.0.0 h1:/Vg6k12+DC+fokeRrLApL22hZS/EqEazJTo/FDdbDog= | blitter.com/go/mtwist v1.0.1 h1:PxmoWexfMpLmc8neHP/PcRc3s17ct7iz4d5W/qJVt04= | ||||||
| blitter.com/go/mtwist v1.0.0/go.mod h1:aU82Nx8+b1v8oZRNqImfEDzDTPim81rY0ACKAIclV18= | blitter.com/go/mtwist v1.0.1/go.mod h1:aU82Nx8+b1v8oZRNqImfEDzDTPim81rY0ACKAIclV18= | ||||||
| git.schwanenlied.me/yawning/kyber.git v0.0.0-20180530164001-a270899bd22c h1:SGOx1s56QSOmuCegRcG3yvOG7W8PvRS9ZVnFQl5K2aQ= | blitter.com/go/newhope v0.0.0-20200130200750-192fc08a8aae h1:YBBaCcdYRrI1btsmcMTv1VMPmaSXXz0RwKOTgMJYSRU= | ||||||
| git.schwanenlied.me/yawning/kyber.git v0.0.0-20180530164001-a270899bd22c/go.mod h1:QrbgzU5EL/1jaMD5pD4Tiikj3R5elPMa+RMwFUTGwQU= | blitter.com/go/newhope v0.0.0-20200130200750-192fc08a8aae/go.mod h1:ywoxfDBqInPsqtnxYsmS4SYMJ5D/kNcrFgpvI+Xcun0= | ||||||
| github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= | github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY= | ||||||
| github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA= | ||||||
|  | github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw= | ||||||
|  | github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= | ||||||
|  | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= | ||||||
|  | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||||||
| github.com/jameskeane/bcrypt v0.0.0-20120420032655-c3cd44c1e20f h1:UWGE8Vi+1Agt0lrvnd7UsmvwqWKRzb9byK9iQmsbY0Y= | github.com/jameskeane/bcrypt v0.0.0-20120420032655-c3cd44c1e20f h1:UWGE8Vi+1Agt0lrvnd7UsmvwqWKRzb9byK9iQmsbY0Y= | ||||||
| github.com/jameskeane/bcrypt v0.0.0-20120420032655-c3cd44c1e20f/go.mod h1:u+9Snq0w+ZdYKi8BBoaxnEwWu0fY4Kvu9ByFpM51t1s= | github.com/jameskeane/bcrypt v0.0.0-20120420032655-c3cd44c1e20f/go.mod h1:u+9Snq0w+ZdYKi8BBoaxnEwWu0fY4Kvu9ByFpM51t1s= | ||||||
| github.com/kr/pty v1.1.3 h1:/Um6a/ZmD5tF7peoOJ5oN5KMQ0DrGVQSXLNwyckutPk= | github.com/klauspost/cpuid v1.2.4 h1:EBfaK0SWSwk+fgk6efYFWdzl8MwRWoOO1gkmiaTXPW4= | ||||||
| github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= | github.com/klauspost/cpuid v1.2.4/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= | ||||||
| github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs= | github.com/klauspost/reedsolomon v1.9.9 h1:qCL7LZlv17xMixl55nq2/Oa1Y86nfO8EqDfv2GHND54= | ||||||
| github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= | github.com/klauspost/reedsolomon v1.9.9/go.mod h1:O7yFFHiQwDR6b2t63KPUpccPtNdp5ADgh1gg4fd12wo= | ||||||
|  | github.com/kuking/go-frodokem v1.0.1 h1:13bks3u4CPpvUtOLttT+A37j9myV4kLnS7Z3qDiTm4o= | ||||||
|  | github.com/kuking/go-frodokem v1.0.1/go.mod h1:TzD0W9QnVOcwigeSySEuNZfJaGxWRtFRb7hXe/w/waI= | ||||||
|  | github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= | ||||||
|  | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= | ||||||
|  | github.com/mmcloughlin/avo v0.0.0-20200523190732-4439b6b2c061 h1:UCU8+cLbbvyxi0sQ9fSeoEhZgvrrD9HKMtX6Gmc1vk8= | ||||||
|  | github.com/mmcloughlin/avo v0.0.0-20200523190732-4439b6b2c061/go.mod h1:wqKykBG2QzQDJEzvRkcS8x6MiSJkF52hXZsXcjaB3ls= | ||||||
|  | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= | ||||||
|  | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= | ||||||
| github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | ||||||
| github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||||||
| github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | ||||||
| github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= | github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= | ||||||
| golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9 h1:mKdxBk7AujPs8kU4m80U72y/zjbZ3UcXC7dClwKbUI0= | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= | ||||||
| golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= | github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 h1:89CEmDvlq/F7SJEOqkIdNDGJXrQIhuIx9D2DBXjavSU= | ||||||
| golang.org/x/sys v0.0.0-20181211161752-7da8ea5c8182 h1:3jwI9dC+BuoXWS+QtR/HhfGTGTuB6ZzL6II6S1IuVvo= | github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161/go.mod h1:wM7WEvslTq+iOEAMDLSzhVuOt5BRZ05WirO+b09GHQU= | ||||||
| golang.org/x/sys v0.0.0-20181211161752-7da8ea5c8182/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | github.com/templexxx/xor v0.0.0-20191217153810-f85b25db303b h1:fj5tQ8acgNUr6O8LEplsxDhUIe2573iLkJc+PqnzZTI= | ||||||
| golang.org/x/sys v0.0.0-20181213200352-4d1cda033e06 h1:0oC8rFnE+74kEmuHZ46F6KHsMr5Gx2gUQPuNz28iQZM= | github.com/templexxx/xor v0.0.0-20191217153810-f85b25db303b/go.mod h1:5XA7W9S6mni3h5uvOC75dA3m9CCCaS83lltmc0ukdi4= | ||||||
| golang.org/x/sys v0.0.0-20181213200352-4d1cda033e06/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | github.com/tjfoc/gmsm v1.3.1 h1:+k3IAlF81c31/TllJmIfuCYnjl8ziMdTWGWJcP9J1uo= | ||||||
|  | github.com/tjfoc/gmsm v1.3.1/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w= | ||||||
|  | github.com/ulikunitz/xz v0.5.7 h1:YvTNdFzX6+W5m9msiYg/zpkSURPPtOlzbqYjrFn7Yt4= | ||||||
|  | github.com/ulikunitz/xz v0.5.7/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= | ||||||
|  | github.com/xtaci/kcp-go v5.4.20+incompatible h1:TN1uey3Raw0sTz0Fg8GkfM0uH3YwzhnZWQ1bABv5xAg= | ||||||
|  | github.com/xtaci/kcp-go v5.4.20+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE= | ||||||
|  | github.com/xtaci/lossyconn v0.0.0-20200209145036-adba10fffc37 h1:EWU6Pktpas0n8lLQwDsRyZfmkPeRbdgPtW609es+/9E= | ||||||
|  | github.com/xtaci/lossyconn v0.0.0-20200209145036-adba10fffc37/go.mod h1:HpMP7DB2CyokmAh4lp0EQnnWhmycP/TvwBGzvuie+H0= | ||||||
|  | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= | ||||||
|  | golang.org/x/arch v0.0.0-20190909030613-46d78d1859ac/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4= | ||||||
|  | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= | ||||||
|  | golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | ||||||
|  | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | ||||||
|  | golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= | ||||||
|  | golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= | ||||||
|  | golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37 h1:cg5LA/zNPRzIXIWSCxQW10Rvpy94aQh3LT/ShoCpkHw= | ||||||
|  | golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= | ||||||
|  | golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ= | ||||||
|  | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= | ||||||
|  | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ= | ||||||
|  | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | ||||||
|  | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||||||
|  | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8= | ||||||
|  | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||||||
|  | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||||
|  | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||||
|  | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||||||
|  | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||||
|  | golang.org/x/sys v0.0.0-20190902133755-9109b7679e13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||||
|  | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||||
|  | golang.org/x/sys v0.0.0-20200523222454-059865788121 h1:rITEj+UZHYC927n8GT97eC3zrpzXdb/voyeOuVKS46o= | ||||||
|  | golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||||
|  | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | ||||||
|  | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | ||||||
|  | golang.org/x/tools v0.0.0-20200425043458-8463f397d07c h1:iHhCR0b26amDCiiO+kBguKZom9aMF+NrFxh9zeKR/XU= | ||||||
|  | golang.org/x/tools v0.0.0-20200425043458-8463f397d07c/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= | ||||||
|  | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||||
|  | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||||
|  | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= | ||||||
|  | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||||
|  | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= | ||||||
|  | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||||||
|  | gopkg.in/hlandau/easymetric.v1 v1.0.0 h1:ZbfbH7W3giuVDjWUoFhDOjjv20hiPr5HZ2yMV5f9IeE= | ||||||
|  | gopkg.in/hlandau/easymetric.v1 v1.0.0/go.mod h1:yh75hypuFzAxmvECh3ZKGCvFnIfapYJh2wv7ASaX2RE= | ||||||
|  | gopkg.in/hlandau/measurable.v1 v1.0.1 h1:wH5UZKCRUnRr1iD+xIZfwhtxhmr+bprRJttqA1Rklf4= | ||||||
|  | gopkg.in/hlandau/measurable.v1 v1.0.1/go.mod h1:6N+SYJGMTmetsx7wskULP+juuO+++tsHJkAgzvzsbuM= | ||||||
|  | gopkg.in/hlandau/passlib.v1 v1.0.10 h1:q5xh9ZHp907XTjVw8/EqG03//fnlITnIYQmv4Gn7TpE= | ||||||
|  | gopkg.in/hlandau/passlib.v1 v1.0.10/go.mod h1:wxGAv2CtQHlzWY8NJp+p045yl4WHyX7v2T6XbOcmqjM= | ||||||
|  | gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= | ||||||
|  | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | ||||||
|  | rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= | ||||||
|  |  | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| env: | env: | ||||||
|   project: hkexsh |   project: xs | ||||||
|   version: 0.8.0 |   version: 0.8.0 | ||||||
|   buildDir: build/ |   buildDir: build/ | ||||||
|   docDir: doc/ |   docDir: doc/ | ||||||
|  | @ -38,7 +38,7 @@ commands: | ||||||
| 
 | 
 | ||||||
|   app: |   app: | ||||||
|     aliases: [ build ] |     aliases: [ build ] | ||||||
|     help: build the hkexsh tools |     help: build the xs tools | ||||||
|     exec: | |     exec: | | ||||||
|       make clean |       make clean | ||||||
|       make all |       make all | ||||||
|  |  | ||||||
							
								
								
									
										131
									
								
								hkexauth.go
									
										
									
									
									
								
							
							
						
						|  | @ -1,131 +0,0 @@ | ||||||
| package hkexsh |  | ||||||
| 
 |  | ||||||
| // Package hkexsh - a secure terminal client/server written from scratch in Go |  | ||||||
| // |  | ||||||
| // Copyright (c) 2017-2018 Russell Magee |  | ||||||
| // Licensed under the terms of the MIT license (see LICENSE.mit in this |  | ||||||
| // distribution) |  | ||||||
| // |  | ||||||
| // golang implementation by Russ Magee (rmagee_at_gmail.com) |  | ||||||
| 
 |  | ||||||
| // Authentication routines for the HKExSh |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"bytes" |  | ||||||
| 	"encoding/csv" |  | ||||||
| 	"fmt" |  | ||||||
| 	"io" |  | ||||||
| 	"io/ioutil" |  | ||||||
| 	"log" |  | ||||||
| 	"os/user" |  | ||||||
| 	"runtime" |  | ||||||
| 	"strings" |  | ||||||
| 
 |  | ||||||
| 	"github.com/jameskeane/bcrypt" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| func userExistsOnSystem(who string) bool { |  | ||||||
| 	_, userErr := user.Lookup(who) |  | ||||||
| 	return userErr == nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // AuthUserByPasswd checks user login information using a password. |  | ||||||
| // This checks /etc/hkexsh.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 |  | ||||||
| 	if e != nil { |  | ||||||
| 		valid = false |  | ||||||
| 		log.Println("ERROR: Cannot read hkexsh.passwd file!") |  | ||||||
| 		log.Fatal(e) |  | ||||||
| 	} |  | ||||||
| 	r := csv.NewReader(bytes.NewReader(b)) |  | ||||||
| 
 |  | ||||||
| 	r.Comma = ':' |  | ||||||
| 	r.Comment = '#' |  | ||||||
| 	r.FieldsPerRecord = 3 // username:salt:authCookie [TODO:disallowedCmdList (a,b,...)] |  | ||||||
| 	for { |  | ||||||
| 		record, err := r.Read() |  | ||||||
| 		if err == io.EOF { |  | ||||||
| 			// Use dummy entry if user not found |  | ||||||
| 			// (prevent user enumeration attack via obvious timing diff; |  | ||||||
| 			// ie., not attempting any auth at all) |  | ||||||
| 			record = []string{"$nosuchuser$", |  | ||||||
| 				"$2a$12$l0coBlRDNEJeQVl6GdEPbU", |  | ||||||
| 				"$2a$12$l0coBlRDNEJeQVl6GdEPbUC/xmuOANvqgmrMVum6S4i.EXPgnTXy6"} |  | ||||||
| 			username = "$nosuchuser$" |  | ||||||
| 			err = nil |  | ||||||
| 		} |  | ||||||
| 		if err != nil { |  | ||||||
| 			log.Fatal(err) |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		if username == record[0] { |  | ||||||
| 			tmp, err := bcrypt.Hash(auth, record[1]) |  | ||||||
| 			if err != nil { |  | ||||||
| 				break |  | ||||||
| 			} |  | ||||||
| 			if tmp == record[2] && username != "$nosuchuser$" { |  | ||||||
| 				valid = true |  | ||||||
| 			} |  | ||||||
| 			break |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	// Security scrub |  | ||||||
| 	for i := range b { |  | ||||||
| 		b[i] = 0 |  | ||||||
| 	} |  | ||||||
| 	r = nil |  | ||||||
| 	runtime.GC() |  | ||||||
| 
 |  | ||||||
| 	if !userExistsOnSystem(username) { |  | ||||||
| 		valid = false |  | ||||||
| 	} |  | ||||||
| 	return |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // AuthUserByToken checks user login information against an auth token. |  | ||||||
| // Auth tokens are stored in each user's $HOME/.hkexsh_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) { |  | ||||||
| 	auth = strings.TrimSpace(auth) |  | ||||||
| 	u, ue := user.Lookup(username) |  | ||||||
| 	if ue != nil { |  | ||||||
| 		return false |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	b, e := ioutil.ReadFile(fmt.Sprintf("%s/.hkexsh_id", u.HomeDir)) |  | ||||||
| 	if e != nil { |  | ||||||
| 		log.Printf("INFO: Cannot read %s/.hkexsh_id\n", u.HomeDir) |  | ||||||
| 		return false |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	r := csv.NewReader(bytes.NewReader(b)) |  | ||||||
| 
 |  | ||||||
| 	r.Comma = ':' |  | ||||||
| 	r.Comment = '#' |  | ||||||
| 	r.FieldsPerRecord = 2 // connhost:authtoken |  | ||||||
| 	for { |  | ||||||
| 		record, err := r.Read() |  | ||||||
| 		if err == io.EOF { |  | ||||||
| 			return false |  | ||||||
| 		} |  | ||||||
| 		record[0] = strings.TrimSpace(record[0]) |  | ||||||
| 		record[1] = strings.TrimSpace(record[1]) |  | ||||||
| 		//fmt.Println("auth:", auth, "record:", |  | ||||||
| 		//	strings.Join([]string{record[0], record[1]}, ":")) |  | ||||||
| 
 |  | ||||||
| 		if (connhostname == record[0]) && |  | ||||||
| 			(auth == strings.Join([]string{record[0], record[1]}, ":")) { |  | ||||||
| 			valid = true |  | ||||||
| 			break |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	if !userExistsOnSystem(username) { |  | ||||||
| 		valid = false |  | ||||||
| 	} |  | ||||||
| 	return |  | ||||||
| } |  | ||||||
|  | @ -1,63 +0,0 @@ | ||||||
| digraph gocallvis { |  | ||||||
|     label="blitter.com/go/hkexsh/hkexpasswd"; |  | ||||||
|     labeljust="l"; |  | ||||||
|     fontname="Arial"; |  | ||||||
|     fontsize="14"; |  | ||||||
|     rankdir="LR"; |  | ||||||
|     bgcolor="lightgray"; |  | ||||||
|     style="solid"; |  | ||||||
|     penwidth="0.5"; |  | ||||||
|     pad="0.0"; |  | ||||||
|     nodesep="0.35"; |  | ||||||
| 
 |  | ||||||
|     node [shape="ellipse" style="filled" fillcolor="honeydew" fontname="Verdana" penwidth="1.0" margin="0.05,0.0"]; |  | ||||||
|     edge [minlen="2"] |  | ||||||
| 
 |  | ||||||
|     subgraph "cluster_focus" { |  | ||||||
|         labelloc="t"; |  | ||||||
| labeljust="c"; |  | ||||||
| fontsize="18"; |  | ||||||
| bgcolor="#e6ecfa"; |  | ||||||
| label="main"; |  | ||||||
|          |  | ||||||
|         "blitter.com/go/hkexsh/hkexpasswd.main" [ fillcolor="lightblue" label="main" penwidth="0.5" ] |  | ||||||
|          |  | ||||||
|         subgraph "cluster_blitter.com/go/hkexsh" { |  | ||||||
|         penwidth="0.8"; |  | ||||||
| style="filled"; |  | ||||||
| fillcolor="lightyellow"; |  | ||||||
| fontname="bold"; |  | ||||||
| label="[hkexsh]"; |  | ||||||
| URL="/?f=blitter.com/go/hkexsh"; |  | ||||||
| tooltip="package: blitter.com/go/hkexsh"; |  | ||||||
| fontsize="16"; |  | ||||||
| rank="sink"; |  | ||||||
|          |  | ||||||
|         "blitter.com/go/hkexsh.ReadPassword" [ fillcolor="moccasin" label="ReadPassword" penwidth="1.5" ] |  | ||||||
|          |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|         subgraph "cluster_github.com/jameskeane/bcrypt" { |  | ||||||
|         tooltip="package: github.com/jameskeane/bcrypt"; |  | ||||||
| penwidth="0.8"; |  | ||||||
| fontsize="16"; |  | ||||||
| style="filled"; |  | ||||||
| rank="sink"; |  | ||||||
| label="[bcrypt]"; |  | ||||||
| URL="/?f=github.com/jameskeane/bcrypt"; |  | ||||||
| fillcolor="lightyellow"; |  | ||||||
| fontname="bold"; |  | ||||||
|          |  | ||||||
|         "github.com/jameskeane/bcrypt.Salt" [ fillcolor="moccasin" label="Salt" penwidth="1.5" ] |  | ||||||
|         "github.com/jameskeane/bcrypt.Hash" [ fillcolor="moccasin" label="Hash" penwidth="1.5" ] |  | ||||||
|         "github.com/jameskeane/bcrypt.Match" [ fillcolor="moccasin" label="Match" penwidth="1.5" ] |  | ||||||
|          |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     "blitter.com/go/hkexsh/hkexpasswd.main" -> "blitter.com/go/hkexsh.ReadPassword" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexpasswd.main" -> "github.com/jameskeane/bcrypt.Salt" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexpasswd.main" -> "github.com/jameskeane/bcrypt.Hash" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexpasswd.main" -> "github.com/jameskeane/bcrypt.Match" [ color="saddlebrown" ] |  | ||||||
| } |  | ||||||
| Before Width: | Height: | Size: 32 KiB | 
|  | @ -1,17 +0,0 @@ | ||||||
| .PHONY: clean all vis lint |  | ||||||
| 
 |  | ||||||
| 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 . |  | ||||||
| 
 |  | ||||||
| clean: |  | ||||||
| 	$(RM) $(EXE) $(EXE).exe |  | ||||||
| 
 |  | ||||||
| vis: |  | ||||||
| 	go-callvis -file hkexsh-vis -format png -ignore $(EXTPKGS) -group pkg,type . |  | ||||||
| 	../fixup-gv.sh hkexsh.go && cat hkexsh-vis.gv | dot -Tpng -ohkexsh-vis-fixedup.png |  | ||||||
| 
 |  | ||||||
| lint: |  | ||||||
| 	-gometalinter --deadline=60s | sort |  | ||||||
|  | @ -1 +0,0 @@ | ||||||
| hkexsh |  | ||||||
| Before Width: | Height: | Size: 613 KiB | 
|  | @ -1,263 +0,0 @@ | ||||||
| digraph gocallvis { |  | ||||||
|     label="blitter.com/go/hkexsh/hkexsh"; |  | ||||||
|     labeljust="l"; |  | ||||||
|     fontname="Arial"; |  | ||||||
|     fontsize="14"; |  | ||||||
|     rankdir="LR"; |  | ||||||
|     bgcolor="lightgray"; |  | ||||||
|     style="solid"; |  | ||||||
|     penwidth="0.5"; |  | ||||||
|     pad="0.0"; |  | ||||||
|     nodesep="0.35"; |  | ||||||
| 
 |  | ||||||
|     node [shape="ellipse" style="filled" fillcolor="honeydew" fontname="Verdana" penwidth="1.0" margin="0.05,0.0"]; |  | ||||||
|     edge [minlen="2"] |  | ||||||
| 
 |  | ||||||
|     subgraph "cluster_focus" { |  | ||||||
|         fontsize="18"; |  | ||||||
| bgcolor="#e6ecfa"; |  | ||||||
| label="main"; |  | ||||||
| labelloc="t"; |  | ||||||
| labeljust="c"; |  | ||||||
|          |  | ||||||
|         "blitter.com/go/hkexsh/hkexsh.handleTermResizes$1" [ fillcolor="lightblue" label="handleTermResizes$1" style="dotted,filled" ] |  | ||||||
|         "blitter.com/go/hkexsh/hkexsh.GetSize" [ fillcolor="lightblue" label="GetSize" penwidth="1.5" ] |  | ||||||
|         "blitter.com/go/hkexsh/hkexsh.doCopyMode" [ fillcolor="lightblue" label="doCopyMode" penwidth="0.5" ] |  | ||||||
|         "blitter.com/go/hkexsh/hkexsh.copyBuffer" [ fillcolor="lightblue" label="copyBuffer" penwidth="0.5" ] |  | ||||||
|         "blitter.com/go/hkexsh/hkexsh.copyBuffer$1" [ style="dotted,filled" fillcolor="lightblue" label="copyBuffer$1" ] |  | ||||||
|         "blitter.com/go/hkexsh/hkexsh.copyBuffer$2" [ fillcolor="lightblue" label="copyBuffer$2" style="dotted,filled" ] |  | ||||||
|         "blitter.com/go/hkexsh/hkexsh.copyBuffer$3" [ fillcolor="lightblue" label="copyBuffer$3" style="dotted,filled" ] |  | ||||||
|         "blitter.com/go/hkexsh/hkexsh.reqTunnel" [ fillcolor="lightblue" label="reqTunnel" penwidth="0.5" ] |  | ||||||
|         "blitter.com/go/hkexsh/hkexsh.launchTuns" [ fillcolor="lightblue" label="launchTuns" penwidth="0.5" ] |  | ||||||
|         "blitter.com/go/hkexsh/hkexsh.main$2" [ fillcolor="lightblue" label="deferCloseChaff" style="dotted,filled" ] |  | ||||||
|         "blitter.com/go/hkexsh/hkexsh.sendSessionParams" [ label="sendSessionParams" penwidth="0.5" fillcolor="lightblue" ] |  | ||||||
|         "blitter.com/go/hkexsh/hkexsh.handleTermResizes" [ fillcolor="lightblue" label="handleTermResizes" penwidth="0.5" ] |  | ||||||
|         "blitter.com/go/hkexsh/hkexsh.Copy" [ penwidth="1.5" fillcolor="lightblue" label="Copy" ] |  | ||||||
|         "blitter.com/go/hkexsh/hkexsh.doShellMode$2$1" [ fillcolor="lightblue" label="doShellMode$2$1" style="dotted,filled" ] |  | ||||||
|         "blitter.com/go/hkexsh/hkexsh.doShellMode$2" [ fillcolor="lightblue" label="shellStdinToRemote" style="dotted,filled" ] |  | ||||||
|         "blitter.com/go/hkexsh/hkexsh.main" [ fillcolor="lightblue" label="main" penwidth="0.5" ] |  | ||||||
|         "blitter.com/go/hkexsh/hkexsh.parseNonSwitchArgs" [ fillcolor="lightblue" label="parseNonSwitchArgs" penwidth="0.5" ] |  | ||||||
|         "blitter.com/go/hkexsh/hkexsh.main$1" [ fillcolor="lightblue" label="deferRestore" style="dotted,filled" ] |  | ||||||
|         "blitter.com/go/hkexsh/hkexsh.rejectUserMsg" [ fillcolor="lightblue" label="rejectUserMsg" penwidth="0.5" ] |  | ||||||
|         "blitter.com/go/hkexsh/hkexsh.doShellMode$1" [ fillcolor="lightblue" label="shellRemoteToStdin" style="dotted,filled" ] |  | ||||||
|         "blitter.com/go/hkexsh/hkexsh.doShellMode$1$1" [ label="doShellMode$1$1" style="dotted,filled" fillcolor="lightblue" ] |  | ||||||
|         "blitter.com/go/hkexsh/hkexsh.doShellMode" [ penwidth="0.5" fillcolor="lightblue" label="doShellMode" ] |  | ||||||
|         "blitter.com/go/hkexsh/hkexsh.usageShell" [ fillcolor="lightblue" label="usageShell" penwidth="0.5" ] |  | ||||||
|         "blitter.com/go/hkexsh/hkexsh.usageCp" [ fillcolor="lightblue" label="usageCp" penwidth="0.5" ] |  | ||||||
|          |  | ||||||
|         subgraph "cluster_blitter.com/go/hkexsh" { |  | ||||||
|         penwidth="0.8"; |  | ||||||
| fontsize="16"; |  | ||||||
| style="filled"; |  | ||||||
| fillcolor="lightyellow"; |  | ||||||
| fontname="bold"; |  | ||||||
| rank="sink"; |  | ||||||
| label="[hkexsh]"; |  | ||||||
| URL="/?f=blitter.com/go/hkexsh"; |  | ||||||
| tooltip="package: blitter.com/go/hkexsh"; |  | ||||||
|          |  | ||||||
|         "blitter.com/go/hkexsh.Restore" [ fillcolor="moccasin" label="Restore" penwidth="1.5" ] |  | ||||||
|         "blitter.com/go/hkexsh.MakeRaw" [ penwidth="1.5" fillcolor="moccasin" label="MakeRaw" ] |  | ||||||
|         "blitter.com/go/hkexsh.ReadPassword" [ fillcolor="moccasin" label="ReadPassword" penwidth="1.5" ] |  | ||||||
|         "blitter.com/go/hkexsh.NewSession" [ fillcolor="moccasin" label="NewSession" penwidth="1.5" ] |  | ||||||
|          |  | ||||||
|         subgraph "cluster_*blitter.com/go/hkexsh.Session" { |  | ||||||
|         tooltip="type: *blitter.com/go/hkexsh.Session"; |  | ||||||
| penwidth="0.5"; |  | ||||||
| fontsize="15"; |  | ||||||
| fontcolor="#222222"; |  | ||||||
| labelloc="b"; |  | ||||||
| style="rounded,filled"; |  | ||||||
| fillcolor="wheat2"; |  | ||||||
| label="(*Session)"; |  | ||||||
|          |  | ||||||
|         "(*blitter.com/go/hkexsh.Session).SetStatus" [ label="SetStatus" penwidth="1.5" fillcolor="moccasin" ] |  | ||||||
|          |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|         subgraph "cluster_blitter.com/go/hkexsh.Session" { |  | ||||||
|         labelloc="b"; |  | ||||||
| style="rounded,filled"; |  | ||||||
| fillcolor="wheat2"; |  | ||||||
| label="(Session)"; |  | ||||||
| tooltip="type: blitter.com/go/hkexsh.Session"; |  | ||||||
| penwidth="0.5"; |  | ||||||
| fontsize="15"; |  | ||||||
| fontcolor="#222222"; |  | ||||||
|          |  | ||||||
|         "(blitter.com/go/hkexsh.Session).Cmd" [ label="Cmd" penwidth="1.5" fillcolor="moccasin" ] |  | ||||||
|         "(blitter.com/go/hkexsh.Session).Op" [ fillcolor="moccasin" label="Op" penwidth="1.5" ] |  | ||||||
|         "(blitter.com/go/hkexsh.Session).Who" [ fillcolor="moccasin" label="Who" penwidth="1.5" ] |  | ||||||
|         "(blitter.com/go/hkexsh.Session).ConnHost" [ label="ConnHost" penwidth="1.5" fillcolor="moccasin" ] |  | ||||||
|         "(blitter.com/go/hkexsh.Session).TermType" [ fillcolor="moccasin" label="TermType" penwidth="1.5" ] |  | ||||||
|         "(blitter.com/go/hkexsh.Session).AuthCookie" [ fillcolor="moccasin" label="AuthCookie" penwidth="1.5" ] |  | ||||||
|         "(blitter.com/go/hkexsh.Session).Status" [ fillcolor="moccasin" label="Status" penwidth="1.5" ] |  | ||||||
|          |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|         subgraph "cluster_blitter.com/go/hkexsh/hkexnet" { |  | ||||||
|         penwidth="0.8"; |  | ||||||
| style="filled"; |  | ||||||
| fillcolor="lightyellow"; |  | ||||||
| fontname="bold"; |  | ||||||
| label="[hkexnet]"; |  | ||||||
| tooltip="package: blitter.com/go/hkexsh/hkexnet"; |  | ||||||
| fontsize="16"; |  | ||||||
| rank="sink"; |  | ||||||
| URL="/?f=blitter.com/go/hkexsh/hkexnet"; |  | ||||||
|          |  | ||||||
|         "blitter.com/go/hkexsh/hkexnet.Init" [ fillcolor="moccasin" label="Init" penwidth="1.5" ] |  | ||||||
|         "blitter.com/go/hkexsh/hkexnet.Dial" [ fillcolor="moccasin" label="Dial" penwidth="1.5" ] |  | ||||||
|          |  | ||||||
|         subgraph "cluster_*blitter.com/go/hkexsh/hkexnet.Conn" { |  | ||||||
|         style="rounded,filled"; |  | ||||||
| fillcolor="wheat2"; |  | ||||||
| label="(*Conn)"; |  | ||||||
| tooltip="type: *blitter.com/go/hkexsh/hkexnet.Conn"; |  | ||||||
| penwidth="0.5"; |  | ||||||
| fontsize="15"; |  | ||||||
| fontcolor="#222222"; |  | ||||||
| labelloc="b"; |  | ||||||
|          |  | ||||||
|         "(*blitter.com/go/hkexsh/hkexnet.Conn).WritePacket" [ label="WritePacket" penwidth="1.5" fillcolor="moccasin" ] |  | ||||||
|         "(*blitter.com/go/hkexsh/hkexnet.Conn).SetStatus" [ fillcolor="moccasin" label="SetStatus" penwidth="1.5" ] |  | ||||||
|         "(*blitter.com/go/hkexsh/hkexnet.Conn).Close" [ fillcolor="moccasin" label="Close" penwidth="1.5" ] |  | ||||||
|         "(*blitter.com/go/hkexsh/hkexnet.Conn).SetupChaff" [ label="SetupChaff" penwidth="1.5" fillcolor="moccasin" ] |  | ||||||
|         "(*blitter.com/go/hkexsh/hkexnet.Conn).EnableChaff" [ fillcolor="moccasin" label="EnableChaff" penwidth="1.5" ] |  | ||||||
|         "(*blitter.com/go/hkexsh/hkexnet.Conn).DisableChaff" [ fillcolor="moccasin" label="DisableChaff" penwidth="1.5" ] |  | ||||||
|         "(*blitter.com/go/hkexsh/hkexnet.Conn).ShutdownChaff" [ fillcolor="moccasin" label="ShutdownChaff" penwidth="1.5" ] |  | ||||||
|          |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|         subgraph "cluster_blitter.com/go/hkexsh/hkexnet.Conn" { |  | ||||||
|         label="(Conn)"; |  | ||||||
| tooltip="type: blitter.com/go/hkexsh/hkexnet.Conn"; |  | ||||||
| penwidth="0.5"; |  | ||||||
| fontsize="15"; |  | ||||||
| fontcolor="#222222"; |  | ||||||
| labelloc="b"; |  | ||||||
| style="rounded,filled"; |  | ||||||
| fillcolor="wheat2"; |  | ||||||
|          |  | ||||||
|         "(blitter.com/go/hkexsh/hkexnet.Conn).Read" [ fillcolor="moccasin" label="Read" penwidth="1.5" ] |  | ||||||
|         "(blitter.com/go/hkexsh/hkexnet.Conn).GetStatus" [ label="GetStatus" penwidth="1.5" fillcolor="moccasin" ] |  | ||||||
|         "(blitter.com/go/hkexsh/hkexnet.Conn).Write" [ fillcolor="moccasin" label="Write" penwidth="1.5" ] |  | ||||||
|          |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|         subgraph "cluster_blitter.com/go/hkexsh/logger" { |  | ||||||
|         fontsize="16"; |  | ||||||
| rank="sink"; |  | ||||||
| label="[logger]"; |  | ||||||
| URL="/?f=blitter.com/go/hkexsh/logger"; |  | ||||||
| tooltip="package: blitter.com/go/hkexsh/logger"; |  | ||||||
| penwidth="0.8"; |  | ||||||
| fillcolor="lightyellow"; |  | ||||||
| fontname="bold"; |  | ||||||
| style="filled"; |  | ||||||
|          |  | ||||||
|         "blitter.com/go/hkexsh/logger.LogDebug" [ label="LogDebug" penwidth="1.5" fillcolor="moccasin" ] |  | ||||||
|         "blitter.com/go/hkexsh/logger.New" [ fillcolor="moccasin" label="New" penwidth="1.5" ] |  | ||||||
|          |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|         subgraph "cluster_blitter.com/go/hkexsh/spinsult" { |  | ||||||
|         URL="/?f=blitter.com/go/hkexsh/spinsult"; |  | ||||||
| tooltip="package: blitter.com/go/hkexsh/spinsult"; |  | ||||||
| penwidth="0.8"; |  | ||||||
| fontsize="16"; |  | ||||||
| style="filled"; |  | ||||||
| fillcolor="lightyellow"; |  | ||||||
| fontname="bold"; |  | ||||||
| rank="sink"; |  | ||||||
| label="[spinsult]"; |  | ||||||
|          |  | ||||||
|         "blitter.com/go/hkexsh/spinsult.GetSentence" [ label="GetSentence" penwidth="1.5" fillcolor="moccasin" ] |  | ||||||
|          |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|         subgraph "cluster_github.com/mattn/go-isatty" { |  | ||||||
|         rank="sink"; |  | ||||||
| label="[isatty]"; |  | ||||||
| URL="/?f=github.com/mattn/go-isatty"; |  | ||||||
| style="filled"; |  | ||||||
| fontsize="16"; |  | ||||||
| fillcolor="lightyellow"; |  | ||||||
| fontname="bold"; |  | ||||||
| tooltip="package: github.com/mattn/go-isatty"; |  | ||||||
| penwidth="0.8"; |  | ||||||
|          |  | ||||||
|         "github.com/mattn/go-isatty.IsTerminal" [ fillcolor="moccasin" label="IsTerminal" penwidth="1.5" ] |  | ||||||
|          |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     "blitter.com/go/hkexsh/hkexsh.handleTermResizes$1" -> "blitter.com/go/hkexsh/hkexsh.GetSize" [  ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexsh.handleTermResizes$1" -> "(*blitter.com/go/hkexsh/hkexnet.Conn).WritePacket" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexsh.doCopyMode" -> "(blitter.com/go/hkexsh.Session).Cmd" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexsh.doCopyMode" -> "(*blitter.com/go/hkexsh/hkexnet.Conn).WritePacket" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexsh.doCopyMode" -> "(blitter.com/go/hkexsh/hkexnet.Conn).Read" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexsh.doCopyMode" -> "(*blitter.com/go/hkexsh/hkexnet.Conn).SetStatus" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexsh.doCopyMode" -> "(blitter.com/go/hkexsh/hkexnet.Conn).GetStatus" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexsh.copyBuffer" -> "(blitter.com/go/hkexsh/hkexnet.Conn).Write" [ color="saddlebrown" style="dashed" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexsh.copyBuffer" -> "blitter.com/go/hkexsh/hkexsh.copyBuffer$1" [ style="dashed" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexsh.copyBuffer" -> "blitter.com/go/hkexsh/hkexsh.copyBuffer$2" [ style="dashed" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexsh.copyBuffer" -> "blitter.com/go/hkexsh/hkexsh.copyBuffer$3" [ style="dashed" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexsh.reqTunnel" -> "blitter.com/go/hkexsh/logger.LogDebug" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexsh.reqTunnel" -> "(*blitter.com/go/hkexsh/hkexnet.Conn).WritePacket" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexsh.launchTuns" -> "blitter.com/go/hkexsh/hkexsh.reqTunnel" [  ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexsh.main$2" -> "(*blitter.com/go/hkexsh/hkexnet.Conn).WritePacket" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexsh.sendSessionParams" -> "(blitter.com/go/hkexsh.Session).Op" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexsh.sendSessionParams" -> "(blitter.com/go/hkexsh.Session).Who" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexsh.sendSessionParams" -> "(blitter.com/go/hkexsh.Session).ConnHost" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexsh.sendSessionParams" -> "(blitter.com/go/hkexsh.Session).TermType" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexsh.sendSessionParams" -> "(blitter.com/go/hkexsh.Session).Cmd" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexsh.sendSessionParams" -> "(blitter.com/go/hkexsh.Session).AuthCookie" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexsh.sendSessionParams" -> "(blitter.com/go/hkexsh/hkexnet.Conn).Write" [ color="saddlebrown" style="dashed" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexsh.handleTermResizes" -> "blitter.com/go/hkexsh/hkexsh.handleTermResizes$1" [ arrowhead="normalnoneodot" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexsh.Copy" -> "blitter.com/go/hkexsh/hkexsh.copyBuffer" [  ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexsh.doShellMode$2$1" -> "blitter.com/go/hkexsh/hkexsh.Copy" [  ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexsh.doShellMode$2" -> "blitter.com/go/hkexsh/hkexsh.doShellMode$2$1" [  ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexsh.doShellMode$2" -> "blitter.com/go/hkexsh.Restore" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexsh.main" -> "blitter.com/go/hkexsh/hkexsh.parseNonSwitchArgs" [  ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexsh.main" -> "blitter.com/go/hkexsh/logger.New" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexsh.main" -> "blitter.com/go/hkexsh/hkexnet.Init" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexsh.main" -> "blitter.com/go/hkexsh/hkexnet.Dial" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexsh.main" -> "(*blitter.com/go/hkexsh/hkexnet.Conn).Close" [ arrowhead="normalnoneodiamond" color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexsh.main" -> "github.com/mattn/go-isatty.IsTerminal" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexsh.main" -> "blitter.com/go/hkexsh.MakeRaw" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexsh.main$1" -> "blitter.com/go/hkexsh.Restore" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexsh.main" -> "blitter.com/go/hkexsh/hkexsh.main$1" [ arrowhead="normalnoneodiamond" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexsh.main" -> "blitter.com/go/hkexsh.ReadPassword" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexsh.main" -> "blitter.com/go/hkexsh.NewSession" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexsh.main" -> "blitter.com/go/hkexsh/hkexsh.sendSessionParams" [  ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexsh.main" -> "(blitter.com/go/hkexsh/hkexnet.Conn).Read" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexsh.main" -> "(*blitter.com/go/hkexsh.Session).SetStatus" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexsh.rejectUserMsg" -> "blitter.com/go/hkexsh/spinsult.GetSentence" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexsh.main" -> "blitter.com/go/hkexsh/hkexsh.rejectUserMsg" [  ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexsh.main" -> "(*blitter.com/go/hkexsh/hkexnet.Conn).SetupChaff" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexsh.main" -> "(*blitter.com/go/hkexsh/hkexnet.Conn).EnableChaff" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexsh.main" -> "(*blitter.com/go/hkexsh/hkexnet.Conn).DisableChaff" [ arrowhead="normalnoneodiamond" color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexsh.main" -> "(*blitter.com/go/hkexsh/hkexnet.Conn).ShutdownChaff" [ color="saddlebrown" arrowhead="normalnoneodiamond" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexsh.main" -> "blitter.com/go/hkexsh/hkexsh.main$2" [ arrowhead="normalnoneodot" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexsh.main" -> "blitter.com/go/hkexsh/hkexsh.launchTuns" [  ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexsh.doShellMode$1" -> "blitter.com/go/hkexsh/hkexsh.doShellMode$1$1" [ arrowhead="normalnoneodiamond" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexsh.doShellMode$1" -> "blitter.com/go/hkexsh.Restore" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexsh.doShellMode$1" -> "(blitter.com/go/hkexsh/hkexnet.Conn).GetStatus" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexsh.doShellMode$1" -> "(*blitter.com/go/hkexsh.Session).SetStatus" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexsh.doShellMode$1" -> "(blitter.com/go/hkexsh.Session).Status" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexsh.doShellMode" -> "blitter.com/go/hkexsh/hkexsh.doShellMode$1" [ arrowhead="normalnoneodot" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexsh.doShellMode" -> "blitter.com/go/hkexsh/hkexsh.handleTermResizes" [  ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexsh.doShellMode" -> "blitter.com/go/hkexsh/hkexsh.doShellMode$2" [ arrowhead="normalnoneodot" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexsh.main" -> "blitter.com/go/hkexsh/hkexsh.doShellMode" [  ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexsh.main" -> "(blitter.com/go/hkexsh.Session).Status" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexsh.main" -> "blitter.com/go/hkexsh/hkexsh.doCopyMode" [  ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexsh.main" -> "blitter.com/go/hkexsh.Restore" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexsh.main" -> "blitter.com/go/hkexsh/hkexsh.usageShell" [ style="dashed" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexsh.main" -> "blitter.com/go/hkexsh/hkexsh.usageCp" [ style="dashed" ] |  | ||||||
| } |  | ||||||
| Before Width: | Height: | Size: 68 KiB | 
| Before Width: | Height: | Size: 630 KiB | 
|  | @ -1,281 +0,0 @@ | ||||||
| digraph gocallvis { |  | ||||||
|     label="blitter.com/go/hkexsh/hkexshd"; |  | ||||||
|     labeljust="l"; |  | ||||||
|     fontname="Arial"; |  | ||||||
|     fontsize="14"; |  | ||||||
|     rankdir="LR"; |  | ||||||
|     bgcolor="lightgray"; |  | ||||||
|     style="solid"; |  | ||||||
|     penwidth="0.5"; |  | ||||||
|     pad="0.0"; |  | ||||||
|     nodesep="0.35"; |  | ||||||
| 
 |  | ||||||
|     node [shape="ellipse" style="filled" fillcolor="honeydew" fontname="Verdana" penwidth="1.0" margin="0.05,0.0"]; |  | ||||||
|     edge [minlen="2"] |  | ||||||
| 
 |  | ||||||
|     subgraph "cluster_focus" { |  | ||||||
|         labeljust="c"; |  | ||||||
| fontsize="18"; |  | ||||||
| bgcolor="#e6ecfa"; |  | ||||||
| label="main"; |  | ||||||
| labelloc="t"; |  | ||||||
|          |  | ||||||
|         "blitter.com/go/hkexsh/hkexshd.runClientToServerCopyAs" [ label="runClientToServerCopyAs" penwidth="0.5" fillcolor="lightblue" ] |  | ||||||
|         "blitter.com/go/hkexsh/hkexshd.main" [ fillcolor="lightblue" label="main" penwidth="0.5" ] |  | ||||||
|         "blitter.com/go/hkexsh/hkexshd.main$1" [ style="dotted,filled" fillcolor="lightblue" label="main$1" ] |  | ||||||
|         "blitter.com/go/hkexsh/hkexshd.main$2" [ fillcolor="lightblue" label="main$2" style="dotted,filled" ] |  | ||||||
|         "blitter.com/go/hkexsh/hkexshd.GenAuthToken" [ penwidth="1.5" fillcolor="lightblue" label="GenAuthToken" ] |  | ||||||
|         "blitter.com/go/hkexsh/hkexshd.runShellAs" [ fillcolor="lightblue" label="runShellAs" penwidth="0.5" ] |  | ||||||
|         "blitter.com/go/hkexsh/hkexshd.runShellAs$1" [ style="dotted,filled" fillcolor="lightblue" label="deferPtmxClose" ] |  | ||||||
|         "blitter.com/go/hkexsh/hkexshd.runShellAs$2" [ style="dotted,filled" fillcolor="lightblue" label="termResizeWatcher" ] |  | ||||||
|         "blitter.com/go/hkexsh/hkexshd.runShellAs$3" [ style="dotted,filled" fillcolor="lightblue" label="stdinToPtyWorker" ] |  | ||||||
|         "blitter.com/go/hkexsh/hkexshd.runShellAs$4" [ fillcolor="lightblue" label="deferChaffShutdown" style="dotted,filled" ] |  | ||||||
|         "blitter.com/go/hkexsh/hkexshd.runShellAs$5" [ label="ptyToStdoutWorker" style="dotted,filled" fillcolor="lightblue" ] |  | ||||||
|         "blitter.com/go/hkexsh/hkexshd.main$2$1" [ fillcolor="lightblue" label="main$2$1" style="dotted,filled" ] |  | ||||||
|         "blitter.com/go/hkexsh/hkexshd.runServerToClientCopyAs" [ fillcolor="lightblue" label="runServerToClientCopyAs" penwidth="0.5" ] |  | ||||||
|          |  | ||||||
|         subgraph "cluster_blitter.com/go/goutmp" { |  | ||||||
|         URL="/?f=blitter.com/go/goutmp"; |  | ||||||
| fontsize="16"; |  | ||||||
| fillcolor="lightyellow"; |  | ||||||
| fontname="bold"; |  | ||||||
| rank="sink"; |  | ||||||
| penwidth="0.8"; |  | ||||||
| style="filled"; |  | ||||||
| label="[goutmp]"; |  | ||||||
| tooltip="package: blitter.com/go/goutmp"; |  | ||||||
|          |  | ||||||
|         "blitter.com/go/goutmp.GetHost" [ fillcolor="moccasin" label="GetHost" penwidth="1.5" ] |  | ||||||
|         "blitter.com/go/goutmp.Put_utmp" [ fillcolor="moccasin" label="Put_utmp" penwidth="1.5" ] |  | ||||||
|         "blitter.com/go/goutmp.Unput_utmp" [ fillcolor="moccasin" label="Unput_utmp" penwidth="1.5" ] |  | ||||||
|         "blitter.com/go/goutmp.Put_lastlog_entry" [ fillcolor="moccasin" label="Put_lastlog_entry" penwidth="1.5" ] |  | ||||||
|          |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|         subgraph "cluster_blitter.com/go/hkexsh" { |  | ||||||
|         tooltip="package: blitter.com/go/hkexsh"; |  | ||||||
| style="filled"; |  | ||||||
| fillcolor="lightyellow"; |  | ||||||
| fontname="bold"; |  | ||||||
| rank="sink"; |  | ||||||
| URL="/?f=blitter.com/go/hkexsh"; |  | ||||||
| penwidth="0.8"; |  | ||||||
| fontsize="16"; |  | ||||||
| label="[hkexsh]"; |  | ||||||
|          |  | ||||||
|         "blitter.com/go/hkexsh.AuthUserByToken" [ fillcolor="moccasin" label="AuthUserByToken" penwidth="1.5" ] |  | ||||||
|         "blitter.com/go/hkexsh.AuthUserByPasswd" [ label="AuthUserByPasswd" penwidth="1.5" fillcolor="moccasin" ] |  | ||||||
|          |  | ||||||
|         subgraph "cluster_*blitter.com/go/hkexsh.Session" { |  | ||||||
|         penwidth="0.5"; |  | ||||||
| fontsize="15"; |  | ||||||
| fontcolor="#222222"; |  | ||||||
| labelloc="b"; |  | ||||||
| style="rounded,filled"; |  | ||||||
| fillcolor="wheat2"; |  | ||||||
| label="(*Session)"; |  | ||||||
| tooltip="type: *blitter.com/go/hkexsh.Session"; |  | ||||||
|          |  | ||||||
|         "(*blitter.com/go/hkexsh.Session).SetOp" [ fillcolor="moccasin" label="SetOp" penwidth="1.5" ] |  | ||||||
|         "(*blitter.com/go/hkexsh.Session).SetWho" [ fillcolor="moccasin" label="SetWho" penwidth="1.5" ] |  | ||||||
|         "(*blitter.com/go/hkexsh.Session).SetConnHost" [ label="SetConnHost" penwidth="1.5" fillcolor="moccasin" ] |  | ||||||
|         "(*blitter.com/go/hkexsh.Session).SetTermType" [ fillcolor="moccasin" label="SetTermType" penwidth="1.5" ] |  | ||||||
|         "(*blitter.com/go/hkexsh.Session).SetCmd" [ fillcolor="moccasin" label="SetCmd" penwidth="1.5" ] |  | ||||||
|         "(*blitter.com/go/hkexsh.Session).SetAuthCookie" [ penwidth="1.5" fillcolor="moccasin" label="SetAuthCookie" ] |  | ||||||
|         "(*blitter.com/go/hkexsh.Session).ClearAuthCookie" [ fillcolor="moccasin" label="ClearAuthCookie" penwidth="1.5" ] |  | ||||||
|          |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|         subgraph "cluster_blitter.com/go/hkexsh.Session" { |  | ||||||
|         style="rounded,filled"; |  | ||||||
| fillcolor="wheat2"; |  | ||||||
| label="(Session)"; |  | ||||||
| tooltip="type: blitter.com/go/hkexsh.Session"; |  | ||||||
| penwidth="0.5"; |  | ||||||
| fontsize="15"; |  | ||||||
| fontcolor="#222222"; |  | ||||||
| labelloc="b"; |  | ||||||
|          |  | ||||||
|         "(blitter.com/go/hkexsh.Session).Op" [ fillcolor="moccasin" label="Op" penwidth="1.5" ] |  | ||||||
|         "(blitter.com/go/hkexsh.Session).Who" [ fillcolor="moccasin" label="Who" penwidth="1.5" ] |  | ||||||
|         "(blitter.com/go/hkexsh.Session).ConnHost" [ fillcolor="moccasin" label="ConnHost" penwidth="1.5" ] |  | ||||||
|         "(blitter.com/go/hkexsh.Session).Cmd" [ label="Cmd" penwidth="1.5" fillcolor="moccasin" ] |  | ||||||
|         "(blitter.com/go/hkexsh.Session).AuthCookie" [ fillcolor="moccasin" label="AuthCookie" penwidth="1.5" ] |  | ||||||
|         "(blitter.com/go/hkexsh.Session).TermType" [ fillcolor="moccasin" label="TermType" penwidth="1.5" ] |  | ||||||
|          |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|         subgraph "cluster_blitter.com/go/hkexsh/hkexnet" { |  | ||||||
|         fontsize="16"; |  | ||||||
| fontname="bold"; |  | ||||||
| label="[hkexnet]"; |  | ||||||
| penwidth="0.8"; |  | ||||||
| style="filled"; |  | ||||||
| fillcolor="lightyellow"; |  | ||||||
| rank="sink"; |  | ||||||
| URL="/?f=blitter.com/go/hkexsh/hkexnet"; |  | ||||||
| tooltip="package: blitter.com/go/hkexsh/hkexnet"; |  | ||||||
|          |  | ||||||
|         "blitter.com/go/hkexsh/hkexnet.Init" [ fillcolor="moccasin" label="Init" penwidth="1.5" ] |  | ||||||
|         "blitter.com/go/hkexsh/hkexnet.Listen" [ penwidth="1.5" fillcolor="moccasin" label="Listen" ] |  | ||||||
|          |  | ||||||
|         subgraph "cluster_*blitter.com/go/hkexsh/hkexnet.Conn" { |  | ||||||
|         label="(*Conn)"; |  | ||||||
| tooltip="type: *blitter.com/go/hkexsh/hkexnet.Conn"; |  | ||||||
| penwidth="0.5"; |  | ||||||
| fontsize="15"; |  | ||||||
| fontcolor="#222222"; |  | ||||||
| labelloc="b"; |  | ||||||
| style="rounded,filled"; |  | ||||||
| fillcolor="wheat2"; |  | ||||||
|          |  | ||||||
|         "(*blitter.com/go/hkexsh/hkexnet.Conn).EnableChaff" [ penwidth="1.5" fillcolor="moccasin" label="EnableChaff" ] |  | ||||||
|         "(*blitter.com/go/hkexsh/hkexnet.Conn).DisableChaff" [ fillcolor="moccasin" label="DisableChaff" penwidth="1.5" ] |  | ||||||
|         "(*blitter.com/go/hkexsh/hkexnet.Conn).ShutdownChaff" [ fillcolor="moccasin" label="ShutdownChaff" penwidth="1.5" ] |  | ||||||
|         "(*blitter.com/go/hkexsh/hkexnet.Conn).SetupChaff" [ fillcolor="moccasin" label="SetupChaff" penwidth="1.5" ] |  | ||||||
|         "(*blitter.com/go/hkexsh/hkexnet.Conn).Close" [ fillcolor="moccasin" label="Close" penwidth="1.5" ] |  | ||||||
|         "(*blitter.com/go/hkexsh/hkexnet.Conn).RemoteAddr" [ penwidth="1.5" fillcolor="moccasin" label="RemoteAddr" ] |  | ||||||
|         "(*blitter.com/go/hkexsh/hkexnet.Conn).SetStatus" [ fillcolor="moccasin" label="SetStatus" penwidth="1.5" ] |  | ||||||
|         "(*blitter.com/go/hkexsh/hkexnet.Conn).WritePacket" [ fillcolor="moccasin" label="WritePacket" penwidth="1.5" ] |  | ||||||
|          |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|         subgraph "cluster_*blitter.com/go/hkexsh/hkexnet.HKExListener" { |  | ||||||
|         penwidth="0.5"; |  | ||||||
| fontsize="15"; |  | ||||||
| fontcolor="#222222"; |  | ||||||
| labelloc="b"; |  | ||||||
| style="rounded,filled"; |  | ||||||
| fillcolor="wheat2"; |  | ||||||
| label="(*HKExListener)"; |  | ||||||
| tooltip="type: *blitter.com/go/hkexsh/hkexnet.HKExListener"; |  | ||||||
|          |  | ||||||
|         "(*blitter.com/go/hkexsh/hkexnet.HKExListener).Accept" [ penwidth="1.5" fillcolor="moccasin" label="Accept" ] |  | ||||||
|          |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|         subgraph "cluster_blitter.com/go/hkexsh/hkexnet.Conn" { |  | ||||||
|         labelloc="b"; |  | ||||||
| style="rounded,filled"; |  | ||||||
| fillcolor="wheat2"; |  | ||||||
| label="(Conn)"; |  | ||||||
| tooltip="type: blitter.com/go/hkexsh/hkexnet.Conn"; |  | ||||||
| penwidth="0.5"; |  | ||||||
| fontsize="15"; |  | ||||||
| fontcolor="#222222"; |  | ||||||
|          |  | ||||||
|         "(blitter.com/go/hkexsh/hkexnet.Conn).Write" [ fillcolor="moccasin" label="Write" penwidth="1.5" ] |  | ||||||
|          |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|         subgraph "cluster_blitter.com/go/hkexsh/hkexnet.HKExListener" { |  | ||||||
|         fillcolor="wheat2"; |  | ||||||
| label="(HKExListener)"; |  | ||||||
| tooltip="type: blitter.com/go/hkexsh/hkexnet.HKExListener"; |  | ||||||
| penwidth="0.5"; |  | ||||||
| fontsize="15"; |  | ||||||
| fontcolor="#222222"; |  | ||||||
| labelloc="b"; |  | ||||||
| style="rounded,filled"; |  | ||||||
|          |  | ||||||
|         "(blitter.com/go/hkexsh/hkexnet.HKExListener).Close" [ label="Close" penwidth="1.5" fillcolor="moccasin" ] |  | ||||||
|          |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|         subgraph "cluster_blitter.com/go/hkexsh/logger" { |  | ||||||
|         fillcolor="lightyellow"; |  | ||||||
| rank="sink"; |  | ||||||
| label="[logger]"; |  | ||||||
| URL="/?f=blitter.com/go/hkexsh/logger"; |  | ||||||
| fontsize="16"; |  | ||||||
| style="filled"; |  | ||||||
| fontname="bold"; |  | ||||||
| tooltip="package: blitter.com/go/hkexsh/logger"; |  | ||||||
| penwidth="0.8"; |  | ||||||
|          |  | ||||||
|         "blitter.com/go/hkexsh/logger.New" [ fillcolor="moccasin" label="New" penwidth="1.5" ] |  | ||||||
|         "blitter.com/go/hkexsh/logger.LogNotice" [ fillcolor="moccasin" label="LogNotice" penwidth="1.5" ] |  | ||||||
|         "blitter.com/go/hkexsh/logger.LogErr" [ fillcolor="moccasin" label="LogErr" penwidth="1.5" ] |  | ||||||
|          |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|         subgraph "cluster_github.com/kr/pty" { |  | ||||||
|         URL="/?f=github.com/kr/pty"; |  | ||||||
| tooltip="package: github.com/kr/pty"; |  | ||||||
| style="filled"; |  | ||||||
| fontname="bold"; |  | ||||||
| rank="sink"; |  | ||||||
| label="[pty]"; |  | ||||||
| penwidth="0.8"; |  | ||||||
| fontsize="16"; |  | ||||||
| fillcolor="lightyellow"; |  | ||||||
|          |  | ||||||
|         "github.com/kr/pty.Start" [ fillcolor="moccasin" label="Start" penwidth="1.5" ] |  | ||||||
|         "github.com/kr/pty.Setsize" [ fillcolor="moccasin" label="Setsize" penwidth="1.5" ] |  | ||||||
|          |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     "blitter.com/go/hkexsh/hkexshd.runClientToServerCopyAs" -> "(*blitter.com/go/hkexsh/hkexnet.Conn).EnableChaff" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexshd.runClientToServerCopyAs" -> "(*blitter.com/go/hkexsh/hkexnet.Conn).DisableChaff" [ color="saddlebrown" arrowhead="normalnoneodiamond" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexshd.runClientToServerCopyAs" -> "(*blitter.com/go/hkexsh/hkexnet.Conn).ShutdownChaff" [ arrowhead="normalnoneodiamond" color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexshd.main" -> "blitter.com/go/hkexsh/logger.New" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexshd.main" -> "blitter.com/go/hkexsh/hkexnet.Init" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexshd.main$1" -> "blitter.com/go/hkexsh/logger.LogNotice" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexshd.main" -> "blitter.com/go/hkexsh/hkexshd.main$1" [ arrowhead="normalnoneodot" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexshd.main" -> "blitter.com/go/hkexsh/hkexnet.Listen" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexshd.main" -> "(blitter.com/go/hkexsh/hkexnet.HKExListener).Close" [ color="saddlebrown" arrowhead="normalnoneodiamond" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexshd.main" -> "(*blitter.com/go/hkexsh/hkexnet.HKExListener).Accept" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexshd.main" -> "(*blitter.com/go/hkexsh/hkexnet.Conn).SetupChaff" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexshd.main$2" -> "(*blitter.com/go/hkexsh/hkexnet.Conn).Close" [ arrowhead="normalnoneodiamond" color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexshd.main$2" -> "(*blitter.com/go/hkexsh.Session).SetOp" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexshd.main$2" -> "(*blitter.com/go/hkexsh.Session).SetWho" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexshd.main$2" -> "(*blitter.com/go/hkexsh.Session).SetConnHost" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexshd.main$2" -> "(*blitter.com/go/hkexsh.Session).SetTermType" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexshd.main$2" -> "(*blitter.com/go/hkexsh.Session).SetCmd" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexshd.main$2" -> "(*blitter.com/go/hkexsh.Session).SetAuthCookie" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexshd.main$2" -> "(blitter.com/go/hkexsh.Session).Op" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexshd.main$2" -> "(blitter.com/go/hkexsh.Session).Who" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexshd.main$2" -> "(blitter.com/go/hkexsh.Session).ConnHost" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexshd.main$2" -> "(blitter.com/go/hkexsh.Session).Cmd" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexshd.main$2" -> "(blitter.com/go/hkexsh.Session).AuthCookie" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexshd.main$2" -> "blitter.com/go/hkexsh.AuthUserByToken" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexshd.main$2" -> "(*blitter.com/go/hkexsh.Session).ClearAuthCookie" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexshd.main$2" -> "blitter.com/go/hkexsh.AuthUserByPasswd" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexshd.main$2" -> "(blitter.com/go/hkexsh/hkexnet.Conn).Write" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexshd.main$2" -> "blitter.com/go/hkexsh/logger.LogNotice" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexshd.main$2" -> "(*blitter.com/go/hkexsh/hkexnet.Conn).RemoteAddr" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexshd.main$2" -> "blitter.com/go/goutmp.GetHost" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexshd.main$2" -> "blitter.com/go/hkexsh/hkexshd.GenAuthToken" [  ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexshd.main$2" -> "(blitter.com/go/hkexsh.Session).TermType" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexshd.runShellAs" -> "github.com/kr/pty.Start" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexshd.runShellAs" -> "blitter.com/go/hkexsh/hkexshd.runShellAs$1" [ arrowhead="normalnoneodiamond" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexshd.runShellAs$2" -> "github.com/kr/pty.Setsize" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexshd.runShellAs" -> "blitter.com/go/hkexsh/hkexshd.runShellAs$2" [ arrowhead="normalnoneodot" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexshd.runShellAs" -> "blitter.com/go/hkexsh/hkexshd.runShellAs$3" [ arrowhead="normalnoneodot" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexshd.runShellAs" -> "(*blitter.com/go/hkexsh/hkexnet.Conn).EnableChaff" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexshd.runShellAs$4" -> "(*blitter.com/go/hkexsh/hkexnet.Conn).DisableChaff" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexshd.runShellAs$4" -> "(*blitter.com/go/hkexsh/hkexnet.Conn).ShutdownChaff" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexshd.runShellAs" -> "blitter.com/go/hkexsh/hkexshd.runShellAs$4" [ arrowhead="normalnoneodiamond" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexshd.runShellAs" -> "blitter.com/go/hkexsh/hkexshd.runShellAs$5" [ arrowhead="normalnoneodot" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexshd.runShellAs" -> "(*blitter.com/go/hkexsh/hkexnet.Conn).SetStatus" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexshd.main$2" -> "blitter.com/go/hkexsh/hkexshd.runShellAs" [  ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexshd.main$2" -> "blitter.com/go/hkexsh/logger.LogErr" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexshd.main$2" -> "(*blitter.com/go/hkexsh/hkexnet.Conn).SetStatus" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexshd.main$2" -> "blitter.com/go/goutmp.Put_utmp" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexshd.main$2$1" -> "blitter.com/go/goutmp.Unput_utmp" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexshd.main$2" -> "blitter.com/go/hkexsh/hkexshd.main$2$1" [ arrowhead="normalnoneodiamond" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexshd.main$2" -> "blitter.com/go/goutmp.Put_lastlog_entry" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexshd.main$2" -> "blitter.com/go/hkexsh/hkexshd.runClientToServerCopyAs" [  ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexshd.main$2" -> "(*blitter.com/go/hkexsh/hkexnet.Conn).WritePacket" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexshd.runServerToClientCopyAs" -> "(*blitter.com/go/hkexsh/hkexnet.Conn).EnableChaff" [ color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexshd.runServerToClientCopyAs" -> "(*blitter.com/go/hkexsh/hkexnet.Conn).DisableChaff" [ arrowhead="normalnoneodiamond" color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexshd.runServerToClientCopyAs" -> "(*blitter.com/go/hkexsh/hkexnet.Conn).ShutdownChaff" [ arrowhead="normalnoneodiamond" color="saddlebrown" ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexshd.main$2" -> "blitter.com/go/hkexsh/hkexshd.runServerToClientCopyAs" [  ] |  | ||||||
|     "blitter.com/go/hkexsh/hkexshd.main" -> "blitter.com/go/hkexsh/hkexshd.main$2" [ arrowhead="normalnoneodot" ] |  | ||||||
| } |  | ||||||
| Before Width: | Height: | Size: 626 KiB | 
							
								
								
									
										156
									
								
								logger/logger_bsd.go
									
										
									
									
									
										Normal 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 | ||||||
|  | } | ||||||
|  | @ -11,6 +11,7 @@ import ( | ||||||
| 
 | 
 | ||||||
| // Priority is the logger priority | // Priority is the logger priority | ||||||
| type Priority = sl.Priority | type Priority = sl.Priority | ||||||
|  | 
 | ||||||
| // Writer is a syslog Writer | // Writer is a syslog Writer | ||||||
| type Writer = sl.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 | // Alert returns a log Alert error | ||||||
| func Alert(s string) error { | func Alert(s string) error { | ||||||
|  | 	if l != nil { | ||||||
| 		return l.Alert(s) | 		return l.Alert(s) | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | 
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // LogClose closes the log Writer. | // LogClose closes the log Writer. | ||||||
| func LogClose() error { | func LogClose() error { | ||||||
|  | 	if l != nil { | ||||||
| 		return l.Close() | 		return l.Close() | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // LogCrit returns a log Alert error | // LogCrit returns a log Alert error | ||||||
| func LogCrit(s string) error { | func LogCrit(s string) error { | ||||||
|  | 	if l != nil { | ||||||
| 		return l.Crit(s) | 		return l.Crit(s) | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // LogDebug returns a log Debug error | // LogDebug returns a log Debug error | ||||||
| func LogDebug(s string) error { | func LogDebug(s string) error { | ||||||
|  | 	if l != nil { | ||||||
| 		return l.Debug(s) | 		return l.Debug(s) | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // LogEmerg returns a log Emerg error | // LogEmerg returns a log Emerg error | ||||||
| func LogEmerg(s string) error { | func LogEmerg(s string) error { | ||||||
|  | 	if l != nil { | ||||||
| 		return l.Emerg(s) | 		return l.Emerg(s) | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // LogErr returns a log Err error | // LogErr returns a log Err error | ||||||
| func LogErr(s string) error { | func LogErr(s string) error { | ||||||
|  | 	if l != nil { | ||||||
| 		return l.Err(s) | 		return l.Err(s) | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // LogInfo returns a log Info error | // LogInfo returns a log Info error | ||||||
| func LogInfo(s string) error { | func LogInfo(s string) error { | ||||||
|  | 	if l != nil { | ||||||
| 		return l.Info(s) | 		return l.Info(s) | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // LogNotice returns a log Notice error | // LogNotice returns a log Notice error | ||||||
| func LogNotice(s string) error { | func LogNotice(s string) error { | ||||||
|  | 	if l != nil { | ||||||
| 		return l.Notice(s) | 		return l.Notice(s) | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // LogWarning returns a log Warning error | // LogWarning returns a log Warning error | ||||||
| func LogWarning(s string) error { | func LogWarning(s string) error { | ||||||
|  | 	if l != nil { | ||||||
| 		return l.Warning(s) | 		return l.Warning(s) | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // LogWrite writes to the logger at default level | // LogWrite writes to the logger at default level | ||||||
| func LogWrite(b []byte) (int, error) { | func LogWrite(b []byte) (int, error) { | ||||||
|  | 	if l != nil { | ||||||
| 		return l.Write(b) | 		return l.Write(b) | ||||||
|  | 	} | ||||||
|  | 	return len(b),nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| // +build windows | // +build windows | ||||||
| // | 
 | ||||||
| // Wrapper around UNIX syslog, so that it also may be wrapped | // Wrapper around UNIX syslog, so that it also may be wrapped | ||||||
| // with something else for Windows. | // with something else for Windows. | ||||||
| package logger | package logger | ||||||
|  |  | ||||||
|  | @ -1,8 +1,8 @@ | ||||||
| package hkexsh | package xs | ||||||
| 
 | 
 | ||||||
| // Package hkexsh - a secure terminal client/server written from scratch in Go | // Package xs - a secure terminal client/server written from scratch in Go | ||||||
| // | // | ||||||
| // Copyright (c) 2017-2018 Russell Magee | // Copyright (c) 2017-2020 Russell Magee | ||||||
| // Licensed under the terms of the MIT license (see LICENSE.mit in this | // Licensed under the terms of the MIT license (see LICENSE.mit in this | ||||||
| // distribution) | // distribution) | ||||||
| // | // | ||||||
|  | @ -28,7 +28,7 @@ type Session struct { | ||||||
| 
 | 
 | ||||||
| // Output Session record as a string. Implements Stringer interface. | // Output Session record as a string. Implements Stringer interface. | ||||||
| func (h *Session) String() string { | func (h *Session) String() string { | ||||||
| 	return fmt.Sprintf("hkexsh.Session:\nOp:%v\nWho:%v\nCmd:%v\nAuthCookie:%v\nStatus:%v", | 	return fmt.Sprintf("xs.Session:\nOp:%v\nWho:%v\nCmd:%v\nAuthCookie:%v\nStatus:%v", | ||||||
| 		h.op, h.who, h.cmd, h.AuthCookie(false), h.status) | 		h.op, h.who, h.cmd, h.AuthCookie(false), h.status) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -96,7 +96,7 @@ func (h Session) AuthCookie(reallyShow bool) []byte { | ||||||
| 	return []byte("**REDACTED**") | 	return []byte("**REDACTED**") | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // SetAuthCookie stores the authcookie (essential the password) used to | // SetAuthCookie stores the authcookie (essentially the password) used to | ||||||
| // authenticate the Session. | // authenticate the Session. | ||||||
| func (h *Session) SetAuthCookie(a []byte) { | func (h *Session) SetAuthCookie(a []byte) { | ||||||
| 	h.authCookie = a | 	h.authCookie = a | ||||||
							
								
								
									
										30
									
								
								session_test.go
									
										
									
									
									
										Normal 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") | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										128
									
								
								termmode_bsd.go
									
										
									
									
									
										Normal 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 | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | @ -1,8 +1,9 @@ | ||||||
| // +build linux | // +build linux | ||||||
| 
 | 
 | ||||||
| package hkexsh | package xs | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
|  | 	"errors" | ||||||
| 	"io" | 	"io" | ||||||
| 
 | 
 | ||||||
| 	unix "golang.org/x/sys/unix" | 	unix "golang.org/x/sys/unix" | ||||||
|  | @ -11,7 +12,7 @@ import ( | ||||||
| /* ------------- | /* ------------- | ||||||
|  * minimal terminal APIs brought in from ssh/terminal |  * minimal terminal APIs brought in from ssh/terminal | ||||||
|  * (they have no real business being there as they aren't specific to |  * (they have no real business being there as they aren't specific to | ||||||
|  * ssh, but as of Go v1.10, early 2018, core go stdlib hasn't yet done |  * ssh, but as of Go v1.10, late 2019, core go stdlib hasn't yet done | ||||||
|  * the planned terminal lib reorgs.) |  * the planned terminal lib reorgs.) | ||||||
|  * ------------- */ |  * ------------- */ | ||||||
| 
 | 
 | ||||||
|  | @ -29,8 +30,8 @@ type State struct { | ||||||
| // MakeRaw put the terminal connected to the given file descriptor into raw | // 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 | // mode and returns the previous state of the terminal so that it can be | ||||||
| // restored. | // restored. | ||||||
| func MakeRaw(fd int) (*State, error) { | func MakeRaw(fd uintptr) (*State, error) { | ||||||
| 	termios, err := unix.IoctlGetTermios(fd, ioctlReadTermios) | 	termios, err := unix.IoctlGetTermios(int(fd), ioctlReadTermios) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
|  | @ -46,7 +47,7 @@ func MakeRaw(fd int) (*State, error) { | ||||||
| 	termios.Cflag |= unix.CS8 | 	termios.Cflag |= unix.CS8 | ||||||
| 	termios.Cc[unix.VMIN] = 1 | 	termios.Cc[unix.VMIN] = 1 | ||||||
| 	termios.Cc[unix.VTIME] = 0 | 	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 | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -55,8 +56,8 @@ func MakeRaw(fd int) (*State, error) { | ||||||
| 
 | 
 | ||||||
| // GetState returns the current state of a terminal which may be useful to | // GetState returns the current state of a terminal which may be useful to | ||||||
| // restore the terminal after a signal. | // restore the terminal after a signal. | ||||||
| func GetState(fd int) (*State, error) { | func GetState(fd uintptr) (*State, error) { | ||||||
| 	termios, err := unix.IoctlGetTermios(fd, ioctlReadTermios) | 	termios, err := unix.IoctlGetTermios(int(fd), ioctlReadTermios) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
|  | @ -66,15 +67,19 @@ func GetState(fd int) (*State, error) { | ||||||
| 
 | 
 | ||||||
| // Restore restores the terminal connected to the given file descriptor to a | // Restore restores the terminal connected to the given file descriptor to a | ||||||
| // previous state. | // previous state. | ||||||
| func Restore(fd int, state *State) error { | func Restore(fd uintptr, state *State) error { | ||||||
| 	return unix.IoctlSetTermios(fd, ioctlWriteTermios, &state.termios) | 	if state != nil { | ||||||
|  | 		return unix.IoctlSetTermios(int(fd), ioctlWriteTermios, &state.termios) | ||||||
|  | 	} else { | ||||||
|  | 		return errors.New("nil State") | ||||||
|  | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // ReadPassword reads a line of input from a terminal without local echo.  This | // 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 | // is commonly used for inputting passwords and other sensitive data. The slice | ||||||
| // returned does not include the \n. | // returned does not include the \n. | ||||||
| func ReadPassword(fd int) ([]byte, error) { | func ReadPassword(fd uintptr) ([]byte, error) { | ||||||
| 	termios, err := unix.IoctlGetTermios(fd, ioctlReadTermios) | 	termios, err := unix.IoctlGetTermios(int(fd), ioctlReadTermios) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
|  | @ -83,12 +88,12 @@ func ReadPassword(fd int) ([]byte, error) { | ||||||
| 	newState.Lflag &^= unix.ECHO | 	newState.Lflag &^= unix.ECHO | ||||||
| 	newState.Lflag |= unix.ICANON | unix.ISIG | 	newState.Lflag |= unix.ICANON | unix.ISIG | ||||||
| 	newState.Iflag |= unix.ICRNL | 	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 | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	defer func() { | 	defer func() { | ||||||
| 		_ = unix.IoctlSetTermios(fd, ioctlWriteTermios, termios) // nolint: gosec | 		_ = unix.IoctlSetTermios(int(fd), ioctlWriteTermios, termios) // nolint: gosec | ||||||
| 	}() | 	}() | ||||||
| 
 | 
 | ||||||
| 	return readPasswordLine(passwordReader(fd)) | 	return readPasswordLine(passwordReader(fd)) | ||||||
|  | @ -1,7 +1,7 @@ | ||||||
| // +build windows | // +build windows | ||||||
| // | 
 | ||||||
| // Note the terminal manipulation functions herein are mostly stubs. They | // Note the terminal manipulation functions herein are mostly stubs. They | ||||||
| // don't really do anything and the hkexsh demo client depends on a wrapper | // 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 | // script using the 'stty' tool to actually set the proper mode for | ||||||
| // password login and raw mode required, then restoring it upon logout/exit. | // password login and raw mode required, then restoring it upon logout/exit. | ||||||
| // | // | ||||||
|  | @ -12,7 +12,7 @@ | ||||||
| // here; the wrapper does the bare minimum to make the client workable | // here; the wrapper does the bare minimum to make the client workable | ||||||
| // under MSYS+mintty which is what I use. | // under MSYS+mintty which is what I use. | ||||||
| 
 | 
 | ||||||
| package hkexsh | package xs | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"io" | 	"io" | ||||||
|  | @ -27,7 +27,7 @@ type State struct { | ||||||
| // MakeRaw put the terminal connected to the given file descriptor into raw | // 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 | // mode and returns the previous state of the terminal so that it can be | ||||||
| // restored. | // 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 | 	// This doesn't really work. The exec.Command() runs a sub-shell | ||||||
| 	// so the stty mods don't affect the client process. | 	// so the stty mods don't affect the client process. | ||||||
| 	cmd := exec.Command("stty", "-echo raw") | 	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 | // GetState returns the current state of a terminal which may be useful to | ||||||
| // restore the terminal after a signal. | // restore the terminal after a signal. | ||||||
| func GetState(fd int) (*State, error) { | func GetState(fd uintptr) (*State, error) { | ||||||
| 	return &State{}, nil | 	return &State{}, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Restore restores the terminal connected to the given file descriptor to a | // Restore restores the terminal connected to the given file descriptor to a | ||||||
| // previous state. | // previous state. | ||||||
| func Restore(fd int, state *State) error { | func Restore(fd uintptr, state *State) error { | ||||||
| 	cmd := exec.Command("stty", "echo cooked") | 	cmd := exec.Command("stty", "echo cooked") | ||||||
| 	cmd.Run() | 	cmd.Run() | ||||||
| 	return nil | 	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 | // 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 | // is commonly used for inputting passwords and other sensitive data. The slice | ||||||
| // returned does not include the \n. | // returned does not include the \n. | ||||||
| func ReadPassword(fd int) ([]byte, error) { | func ReadPassword(fd uintptr) ([]byte, error) { | ||||||
| 	return readPasswordLine(passwordReader(fd)) | 	return readPasswordLine(passwordReader(fd)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
| Before Width: | Height: | Size: 675 KiB After Width: | Height: | Size: 675 KiB | 
							
								
								
									
										18
									
								
								xs/Makefile
									
										
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,18 @@ | ||||||
|  | .PHONY: clean all vis lint | ||||||
|  | 
 | ||||||
|  | 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) . | ||||||
|  | 
 | ||||||
|  | clean: | ||||||
|  | 	$(RM) $(EXE) $(EXE).exe | ||||||
|  | 
 | ||||||
|  | vis: | ||||||
|  | 	go-callvis -file xs-vis -format png -ignore $(EXTPKGS) -group pkg,type . | ||||||
|  | 	../fixup-gv.sh xs.go && cat xs-vis.gv | dot -Tpng -oxs-vis-fixedup.png | ||||||
|  | 
 | ||||||
|  | lint: | ||||||
|  | 	-golangci-lint run | ||||||
|  | @ -28,10 +28,12 @@ cleanup() { | ||||||
|   stty sane |   stty sane | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | me="$(basename "$(test -L "$0" && readlink "$0" || echo "$0")")" | ||||||
|  | 
 | ||||||
| if [ ${1}x == "-hx" ]; then | if [ ${1}x == "-hx" ]; then | ||||||
|   ./hkexsh -h |   _${me} -h | ||||||
| else | else | ||||||
|   stty -echo raw icrnl |   stty -echo raw icrnl | ||||||
|   ./hkexsh $@ |   _${me} $@ | ||||||
| fi | fi | ||||||
| 
 | 
 | ||||||
|  | @ -1,4 +1,4 @@ | ||||||
| // +build linux | // +build linux freebsd | ||||||
| 
 | 
 | ||||||
| package main | package main | ||||||
| 
 | 
 | ||||||
|  | @ -9,11 +9,11 @@ import ( | ||||||
| 	"os/signal" | 	"os/signal" | ||||||
| 	"syscall" | 	"syscall" | ||||||
| 
 | 
 | ||||||
| 	"blitter.com/go/hkexsh/hkexnet" | 	"blitter.com/go/xs/xsnet" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // Handle pty resizes (notify server side) | // Handle pty resizes (notify server side) | ||||||
| func handleTermResizes(conn *hkexnet.Conn) { | func handleTermResizes(conn *xsnet.Conn) { | ||||||
| 	ch := make(chan os.Signal, 1) | 	ch := make(chan os.Signal, 1) | ||||||
| 	signal.Notify(ch, syscall.SIGWINCH) | 	signal.Notify(ch, syscall.SIGWINCH) | ||||||
| 	wg.Add(1) | 	wg.Add(1) | ||||||
|  | @ -30,7 +30,7 @@ func handleTermResizes(conn *hkexnet.Conn) { | ||||||
| 				log.Println(err) | 				log.Println(err) | ||||||
| 			} | 			} | ||||||
| 			termSzPacket := fmt.Sprintf("%d %d", rows, cols) | 			termSzPacket := fmt.Sprintf("%d %d", rows, cols) | ||||||
| 			conn.WritePacket([]byte(termSzPacket), hkexnet.CSOTermSize) // nolint: errcheck,gosec | 			conn.WritePacket([]byte(termSzPacket), xsnet.CSOTermSize) // nolint: errcheck,gosec | ||||||
| 		} | 		} | ||||||
| 	}() | 	}() | ||||||
| 	ch <- syscall.SIGWINCH // Initial resize. | 	ch <- syscall.SIGWINCH // Initial resize. | ||||||
|  | @ -6,11 +6,11 @@ import ( | ||||||
| 	"log" | 	"log" | ||||||
| 	"time" | 	"time" | ||||||
| 
 | 
 | ||||||
| 	"blitter.com/go/hkexsh/hkexnet" | 	"blitter.com/go/xs/xsnet" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // Handle pty resizes (notify server side) | // Handle pty resizes (notify server side) | ||||||
| func handleTermResizes(conn *hkexnet.Conn) { | func handleTermResizes(conn *xsnet.Conn) { | ||||||
| 	var hasStty bool | 	var hasStty bool | ||||||
| 	curCols, curRows := 0, 0 | 	curCols, curRows := 0, 0 | ||||||
| 	_, _, err := GetSize() | 	_, _, err := GetSize() | ||||||
|  | @ -57,7 +57,7 @@ func handleTermResizes(conn *hkexnet.Conn) { | ||||||
| 					log.Println(err) | 					log.Println(err) | ||||||
| 				} | 				} | ||||||
| 				termSzPacket := fmt.Sprintf("%d %d", curRows, curCols) | 				termSzPacket := fmt.Sprintf("%d %d", curRows, curCols) | ||||||
| 				conn.WritePacket([]byte(termSzPacket), hkexnet.CSOTermSize) | 				conn.WritePacket([]byte(termSzPacket), xsnet.CSOTermSize) | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	}() | 	}() | ||||||
							
								
								
									
										
											BIN
										
									
								
								xs/xs-vis-fixedup.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.1 MiB | 
							
								
								
									
										549
									
								
								xs/xs-vis.gv
									
										
									
									
									
										Executable file
									
								
							
							
						
						|  | @ -0,0 +1,549 @@ | ||||||
|  | digraph gocallvis { | ||||||
|  |     label="blitter.com/go/xs/xs"; | ||||||
|  |     labeljust="l"; | ||||||
|  |     fontname="Arial"; | ||||||
|  |     fontsize="14"; | ||||||
|  |     rankdir="LR"; | ||||||
|  |     bgcolor="lightgray"; | ||||||
|  |     style="solid"; | ||||||
|  |     penwidth="0.5"; | ||||||
|  |     pad="0.0"; | ||||||
|  |     nodesep="0.35"; | ||||||
|  | 
 | ||||||
|  |     node [shape="ellipse" style="filled" fillcolor="honeydew" fontname="Verdana" penwidth="1.0" margin="0.05,0.0"]; | ||||||
|  |     edge [minlen="2"] | ||||||
|  | 
 | ||||||
|  |     subgraph "cluster_focus" { | ||||||
|  |         bgcolor="#e6ecfa"; | ||||||
|  | label="main"; | ||||||
|  | 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" ] | ||||||
|  |          | ||||||
|  |         subgraph "cluster_blitter.com/go/xs" { | ||||||
|  |         penwidth="0.8"; | ||||||
|  | style="filled"; | ||||||
|  | rank="sink"; | ||||||
|  | tooltip="package: blitter.com/go/xs"; | ||||||
|  | fontsize="16"; | ||||||
|  | fillcolor="lightyellow"; | ||||||
|  | fontname="bold"; | ||||||
|  | label="[xs]"; | ||||||
|  | URL="/?f=blitter.com/go/xs"; | ||||||
|  |          | ||||||
|  |         "blitter.com/go/xs.Restore" [ fillcolor="moccasin" label="Restore" penwidth="1.5" ] | ||||||
|  |         "blitter.com/go/xs.MakeRaw" [ penwidth="1.5" fillcolor="moccasin" label="MakeRaw" ] | ||||||
|  |         "blitter.com/go/xs.ReadPassword" [ fillcolor="moccasin" label="ReadPassword" penwidth="1.5" ] | ||||||
|  |         "blitter.com/go/xs.NewSession" [ fillcolor="moccasin" label="NewSession" penwidth="1.5" ] | ||||||
|  |          | ||||||
|  |         subgraph "cluster_*blitter.com/go/xs.Session" { | ||||||
|  |         fontsize="15"; | ||||||
|  | fontcolor="#222222"; | ||||||
|  | labelloc="b"; | ||||||
|  | style="rounded,filled"; | ||||||
|  | fillcolor="wheat2"; | ||||||
|  | label="(*Session)"; | ||||||
|  | tooltip="type: *blitter.com/go/xs.Session"; | ||||||
|  | penwidth="0.5"; | ||||||
|  |          | ||||||
|  |         "(*blitter.com/go/xs.Session).SetStatus" [ fillcolor="moccasin" label="SetStatus" penwidth="1.5" ] | ||||||
|  |          | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |         subgraph "cluster_blitter.com/go/xs.Session" { | ||||||
|  |         style="rounded,filled"; | ||||||
|  | fillcolor="wheat2"; | ||||||
|  | label="(Session)"; | ||||||
|  | tooltip="type: blitter.com/go/xs.Session"; | ||||||
|  | penwidth="0.5"; | ||||||
|  | fontsize="15"; | ||||||
|  | fontcolor="#222222"; | ||||||
|  | labelloc="b"; | ||||||
|  |          | ||||||
|  |         "(blitter.com/go/xs.Session).Cmd" [ fillcolor="moccasin" label="Cmd" penwidth="1.5" ] | ||||||
|  |         "(blitter.com/go/xs.Session).Status" [ penwidth="1.5" fillcolor="moccasin" label="Status" ] | ||||||
|  |         "(blitter.com/go/xs.Session).Op" [ fillcolor="moccasin" label="Op" penwidth="1.5" ] | ||||||
|  |         "(blitter.com/go/xs.Session).Who" [ fillcolor="moccasin" label="Who" penwidth="1.5" ] | ||||||
|  |         "(blitter.com/go/xs.Session).ConnHost" [ fillcolor="moccasin" label="ConnHost" penwidth="1.5" ] | ||||||
|  |         "(blitter.com/go/xs.Session).TermType" [ fillcolor="moccasin" label="TermType" penwidth="1.5" ] | ||||||
|  |         "(blitter.com/go/xs.Session).AuthCookie" [ label="AuthCookie" penwidth="1.5" fillcolor="moccasin" ] | ||||||
|  |          | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |         subgraph "cluster_blitter.com/go/xs/logger" { | ||||||
|  |         fontsize="16"; | ||||||
|  | URL="/?f=blitter.com/go/xs/logger"; | ||||||
|  | penwidth="0.8"; | ||||||
|  | style="filled"; | ||||||
|  | fillcolor="lightyellow"; | ||||||
|  | fontname="bold"; | ||||||
|  | rank="sink"; | ||||||
|  | label="[logger]"; | ||||||
|  | tooltip="package: blitter.com/go/xs/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" ] | ||||||
|  |          | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |         subgraph "cluster_blitter.com/go/xs/spinsult" { | ||||||
|  |         fillcolor="lightyellow"; | ||||||
|  | fontname="bold"; | ||||||
|  | label="[spinsult]"; | ||||||
|  | URL="/?f=blitter.com/go/xs/spinsult"; | ||||||
|  | tooltip="package: blitter.com/go/xs/spinsult"; | ||||||
|  | penwidth="0.8"; | ||||||
|  | fontsize="16"; | ||||||
|  | style="filled"; | ||||||
|  | rank="sink"; | ||||||
|  |          | ||||||
|  |         "blitter.com/go/xs/spinsult.GetSentence" [ fillcolor="moccasin" label="GetSentence" penwidth="1.5" ] | ||||||
|  |          | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |         subgraph "cluster_blitter.com/go/xs/xsnet" { | ||||||
|  |         penwidth="0.8"; | ||||||
|  | fillcolor="lightyellow"; | ||||||
|  | fontname="bold"; | ||||||
|  | rank="sink"; | ||||||
|  | URL="/?f=blitter.com/go/xs/xsnet"; | ||||||
|  | fontsize="16"; | ||||||
|  | style="filled"; | ||||||
|  | label="[xsnet]"; | ||||||
|  | tooltip="package: 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" ] | ||||||
|  |          | ||||||
|  |         subgraph "cluster_*blitter.com/go/xs/xsnet.Conn" { | ||||||
|  |         fontcolor="#222222"; | ||||||
|  | labelloc="b"; | ||||||
|  | style="rounded,filled"; | ||||||
|  | fillcolor="wheat2"; | ||||||
|  | label="(*Conn)"; | ||||||
|  | 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" ] | ||||||
|  |          | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |         subgraph "cluster_blitter.com/go/xs/xsnet.Conn" { | ||||||
|  |         labelloc="b"; | ||||||
|  | style="rounded,filled"; | ||||||
|  | fillcolor="wheat2"; | ||||||
|  | label="(Conn)"; | ||||||
|  | tooltip="type: blitter.com/go/xs/xsnet.Conn"; | ||||||
|  | penwidth="0.5"; | ||||||
|  | fontsize="15"; | ||||||
|  | fontcolor="#222222"; | ||||||
|  |          | ||||||
|  |         "(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" ] | ||||||
|  |          | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |         subgraph "cluster_compress/flate" { | ||||||
|  |         fontsize="16"; | ||||||
|  | fillcolor="#E0FFE1"; | ||||||
|  | label="[compress/flate]"; | ||||||
|  | URL="/?f=compress/flate"; | ||||||
|  | penwidth="0.8"; | ||||||
|  | style="filled"; | ||||||
|  | fontname="bold"; | ||||||
|  | rank="sink"; | ||||||
|  | tooltip="package: compress/flate"; | ||||||
|  |          | ||||||
|  |          | ||||||
|  |         subgraph "cluster_compress/flate.CorruptInputError" { | ||||||
|  |         tooltip="type: compress/flate.CorruptInputError"; | ||||||
|  | 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"; | ||||||
|  | label="(InternalError)"; | ||||||
|  | tooltip="type: compress/flate.InternalError"; | ||||||
|  | penwidth="0.5"; | ||||||
|  |          | ||||||
|  |         "(compress/flate.InternalError).Error" [ fillcolor="#adedad" label="Error" penwidth="1.5" ] | ||||||
|  |          | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |         subgraph "cluster_context" { | ||||||
|  |         fontsize="16"; | ||||||
|  | style="filled"; | ||||||
|  | penwidth="0.8"; | ||||||
|  | fontname="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"; | ||||||
|  | label="(deadlineExceededError)"; | ||||||
|  | tooltip="type: context.deadlineExceededError"; | ||||||
|  | penwidth="0.5"; | ||||||
|  |          | ||||||
|  |         "(context.deadlineExceededError).Error" [ fillcolor="#adedad" label="Error" penwidth="1.5" ] | ||||||
|  |          | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |         subgraph "cluster_crypto/aes" { | ||||||
|  |         style="filled"; | ||||||
|  | fillcolor="#E0FFE1"; | ||||||
|  | rank="sink"; | ||||||
|  | label="[crypto/aes]"; | ||||||
|  | penwidth="0.8"; | ||||||
|  | fontsize="16"; | ||||||
|  | fontname="bold"; | ||||||
|  | URL="/?f=crypto/aes"; | ||||||
|  | tooltip="package: crypto/aes"; | ||||||
|  |          | ||||||
|  |          | ||||||
|  |         subgraph "cluster_crypto/aes.KeySizeError" { | ||||||
|  |         fontcolor="#222222"; | ||||||
|  | labelloc="b"; | ||||||
|  | style="rounded,filled"; | ||||||
|  | fillcolor="#c2e3c2"; | ||||||
|  | label="(KeySizeError)"; | ||||||
|  | tooltip="type: crypto/aes.KeySizeError"; | ||||||
|  | penwidth="0.5"; | ||||||
|  | fontsize="15"; | ||||||
|  |          | ||||||
|  |         "(crypto/aes.KeySizeError).Error" [ fillcolor="#adedad" label="Error" penwidth="1.5" ] | ||||||
|  |          | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |         subgraph "cluster_crypto/tls" { | ||||||
|  |         tooltip="package: crypto/tls"; | ||||||
|  | fontsize="16"; | ||||||
|  | style="filled"; | ||||||
|  | fontname="bold"; | ||||||
|  | rank="sink"; | ||||||
|  | label="[crypto/tls]"; | ||||||
|  | URL="/?f=crypto/tls"; | ||||||
|  | penwidth="0.8"; | ||||||
|  | fillcolor="#E0FFE1"; | ||||||
|  |          | ||||||
|  |          | ||||||
|  |         subgraph "cluster_crypto/tls.RecordHeaderError" { | ||||||
|  |         fontcolor="#222222"; | ||||||
|  | labelloc="b"; | ||||||
|  | style="rounded,filled"; | ||||||
|  | fillcolor="#c2e3c2"; | ||||||
|  | label="(RecordHeaderError)"; | ||||||
|  | tooltip="type: crypto/tls.RecordHeaderError"; | ||||||
|  | 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"; | ||||||
|  | label="(alert)"; | ||||||
|  | tooltip="type: crypto/tls.alert"; | ||||||
|  |          | ||||||
|  |         "(crypto/tls.alert).Error" [ fillcolor="#adedad" label="Error" penwidth="1.5" ] | ||||||
|  |          | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |         subgraph "cluster_crypto/x509" { | ||||||
|  |         penwidth="0.8"; | ||||||
|  | tooltip="package: crypto/x509"; | ||||||
|  | fontsize="16"; | ||||||
|  | style="filled"; | ||||||
|  | fillcolor="#E0FFE1"; | ||||||
|  | fontname="bold"; | ||||||
|  | rank="sink"; | ||||||
|  | label="[crypto/x509]"; | ||||||
|  | URL="/?f=crypto/x509"; | ||||||
|  |          | ||||||
|  |          | ||||||
|  |         subgraph "cluster_crypto/x509.CertificateInvalidError" { | ||||||
|  |         penwidth="0.5"; | ||||||
|  | fontsize="15"; | ||||||
|  | fontcolor="#222222"; | ||||||
|  | labelloc="b"; | ||||||
|  | style="rounded,filled"; | ||||||
|  | fillcolor="#c2e3c2"; | ||||||
|  | label="(CertificateInvalidError)"; | ||||||
|  | tooltip="type: crypto/x509.CertificateInvalidError"; | ||||||
|  |          | ||||||
|  |         "(crypto/x509.CertificateInvalidError).Error" [ fillcolor="#adedad" label="Error" penwidth="1.5" ] | ||||||
|  |          | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |         subgraph "cluster_crypto/x509.HostnameError" { | ||||||
|  |         labelloc="b"; | ||||||
|  | style="rounded,filled"; | ||||||
|  | fillcolor="#c2e3c2"; | ||||||
|  | label="(HostnameError)"; | ||||||
|  | tooltip="type: crypto/x509.HostnameError"; | ||||||
|  | penwidth="0.5"; | ||||||
|  | fontsize="15"; | ||||||
|  | fontcolor="#222222"; | ||||||
|  |          | ||||||
|  |         "(crypto/x509.HostnameError).Error" [ fillcolor="#adedad" label="Error" penwidth="1.5" ] | ||||||
|  |          | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |         subgraph "cluster_crypto/x509.SystemRootsError" { | ||||||
|  |         fontsize="15"; | ||||||
|  | fontcolor="#222222"; | ||||||
|  | labelloc="b"; | ||||||
|  | style="rounded,filled"; | ||||||
|  | fillcolor="#c2e3c2"; | ||||||
|  | label="(SystemRootsError)"; | ||||||
|  | tooltip="type: crypto/x509.SystemRootsError"; | ||||||
|  | penwidth="0.5"; | ||||||
|  |          | ||||||
|  |         "(crypto/x509.SystemRootsError).Error" [ fillcolor="#adedad" label="Error" penwidth="1.5" ] | ||||||
|  |          | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |         subgraph "cluster_crypto/x509.UnhandledCriticalExtension" { | ||||||
|  |         style="rounded,filled"; | ||||||
|  | fillcolor="#c2e3c2"; | ||||||
|  | label="(UnhandledCriticalExtension)"; | ||||||
|  | tooltip="type: crypto/x509.UnhandledCriticalExtension"; | ||||||
|  | penwidth="0.5"; | ||||||
|  | fontsize="15"; | ||||||
|  | fontcolor="#222222"; | ||||||
|  | labelloc="b"; | ||||||
|  |          | ||||||
|  |         "(crypto/x509.UnhandledCriticalExtension).Error" [ fillcolor="#adedad" label="Error" penwidth="1.5" ] | ||||||
|  |          | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |         subgraph "cluster_crypto/x509.UnknownAuthorityError" { | ||||||
|  |         style="rounded,filled"; | ||||||
|  | fillcolor="#c2e3c2"; | ||||||
|  | label="(UnknownAuthorityError)"; | ||||||
|  | tooltip="type: crypto/x509.UnknownAuthorityError"; | ||||||
|  | penwidth="0.5"; | ||||||
|  | fontsize="15"; | ||||||
|  | fontcolor="#222222"; | ||||||
|  | labelloc="b"; | ||||||
|  |          | ||||||
|  |         "(crypto/x509.UnknownAuthorityError).Error" [ fillcolor="#adedad" label="Error" penwidth="1.5" ] | ||||||
|  |          | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |         subgraph "cluster_github.com/mattn/go-isatty" { | ||||||
|  |         fontname="bold"; | ||||||
|  | penwidth="0.8"; | ||||||
|  | fillcolor="lightyellow"; | ||||||
|  | rank="sink"; | ||||||
|  | label="[isatty]"; | ||||||
|  | URL="/?f=github.com/mattn/go-isatty"; | ||||||
|  | tooltip="package: github.com/mattn/go-isatty"; | ||||||
|  | fontsize="16"; | ||||||
|  | style="filled"; | ||||||
|  |          | ||||||
|  |         "github.com/mattn/go-isatty.IsTerminal" [ label="IsTerminal" penwidth="1.5" fillcolor="moccasin" ] | ||||||
|  |          | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |         subgraph "cluster_github.com/pkg/errors" { | ||||||
|  |         style="filled"; | ||||||
|  | fillcolor="lightyellow"; | ||||||
|  | URL="/?f=github.com/pkg/errors"; | ||||||
|  | rank="sink"; | ||||||
|  | label="[errors]"; | ||||||
|  | tooltip="package: github.com/pkg/errors"; | ||||||
|  | penwidth="0.8"; | ||||||
|  | fontsize="16"; | ||||||
|  | fontname="bold"; | ||||||
|  |          | ||||||
|  |          | ||||||
|  |         subgraph "cluster_*github.com/pkg/errors.fundamental" { | ||||||
|  |         label="(*fundamental)"; | ||||||
|  | tooltip="type: *github.com/pkg/errors.fundamental"; | ||||||
|  | penwidth="0.5"; | ||||||
|  | fontsize="15"; | ||||||
|  | fontcolor="#222222"; | ||||||
|  | labelloc="b"; | ||||||
|  | style="rounded,filled"; | ||||||
|  | fillcolor="wheat2"; | ||||||
|  |          | ||||||
|  |         "(*github.com/pkg/errors.fundamental).Error" [ penwidth="1.5" fillcolor="moccasin" label="Error" ] | ||||||
|  |          | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |         subgraph "cluster_math/rand" { | ||||||
|  |         penwidth="0.8"; | ||||||
|  | fillcolor="#E0FFE1"; | ||||||
|  | rank="sink"; | ||||||
|  | URL="/?f=math/rand"; | ||||||
|  | fontsize="16"; | ||||||
|  | style="filled"; | ||||||
|  | fontname="bold"; | ||||||
|  | label="[math/rand]"; | ||||||
|  | tooltip="package: math/rand"; | ||||||
|  |          | ||||||
|  |         "math/rand.Intn" [ fillcolor="#adedad" label="Intn" 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" ] | ||||||
|  | } | ||||||
|  | @ -1,6 +1,7 @@ | ||||||
| // hkexsh client | // xs client | ||||||
|  | 
 | ||||||
| // | // | ||||||
| // Copyright (c) 2017-2018 Russell Magee | // Copyright (c) 2017-2020 Russell Magee | ||||||
| // Licensed under the terms of the MIT license (see LICENSE.mit in this | // Licensed under the terms of the MIT license (see LICENSE.mit in this | ||||||
| // distribution) | // distribution) | ||||||
| // | // | ||||||
|  | @ -16,6 +17,7 @@ import ( | ||||||
| 	"io" | 	"io" | ||||||
| 	"io/ioutil" | 	"io/ioutil" | ||||||
| 	"log" | 	"log" | ||||||
|  | 	"math/rand" | ||||||
| 	"net" | 	"net" | ||||||
| 	"os" | 	"os" | ||||||
| 	"os/exec" | 	"os/exec" | ||||||
|  | @ -23,46 +25,61 @@ import ( | ||||||
| 	"path" | 	"path" | ||||||
| 	"path/filepath" | 	"path/filepath" | ||||||
| 	"runtime" | 	"runtime" | ||||||
|  | 	"runtime/pprof" | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"sync" | 	"sync" | ||||||
| 	"syscall" | 	"syscall" | ||||||
| 	"time" | 	"time" | ||||||
| 
 | 
 | ||||||
| 	hkexsh "blitter.com/go/hkexsh" | 	"net/http" | ||||||
| 	"blitter.com/go/hkexsh/hkexnet" | 	_ "net/http/pprof" | ||||||
| 	"blitter.com/go/hkexsh/logger" | 
 | ||||||
| 	"blitter.com/go/hkexsh/spinsult" | 	xs "blitter.com/go/xs" | ||||||
|  | 	"blitter.com/go/xs/logger" | ||||||
|  | 	"blitter.com/go/xs/spinsult" | ||||||
|  | 	"blitter.com/go/xs/xsnet" | ||||||
| 	isatty "github.com/mattn/go-isatty" | 	isatty "github.com/mattn/go-isatty" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| var ( | var ( | ||||||
|  | 	version   string | ||||||
|  | 	gitCommit string // set in -ldflags by build | ||||||
|  | 
 | ||||||
|  | 	// wg controls when the goroutines handling client I/O complete | ||||||
| 	wg sync.WaitGroup | 	wg sync.WaitGroup | ||||||
|  | 
 | ||||||
|  | 	kcpMode string // set to a valid KCP BlockCrypt alg tag to use rather than TCP | ||||||
|  | 
 | ||||||
| 	// Log defaults to regular syslog output (no -d) | 	// Log defaults to regular syslog output (no -d) | ||||||
| 	Log *logger.Writer | 	Log *logger.Writer | ||||||
|  | 
 | ||||||
|  | 	cpuprofile string | ||||||
|  | 	memprofile string | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| //////////////////////////////////////////////////// | //////////////////////////////////////////////////// | ||||||
| 
 | 
 | ||||||
|  | // Praise Bob. Do not remove, lest ye lose Slack. | ||||||
| const bob = string("\r\n\r\n" + | const bob = string("\r\n\r\n" + | ||||||
| 	"@@@@@@@^^~~~~~~~~~~~~~~~~~~~~^@@@@@@@@@\r\n" + | 	"@@@@@@@^^~~~~~~~~~~~~~~~~~~~~^@@@@@@@@@\r\n" + | ||||||
| 	"@@@@@@^     ~^  @  @@ @ @ @ I  ~^@@@@@@\r\n" + | 	"@@@@@@^     ~^  @  @@ @ @ @ I  ~^@@@@@@\r\n" + | ||||||
| 	"@@@@@            ~ ~~ ~I          @@@@@\r\n" + | 	"@@@@@            ~ ~~ ~I          @@@@@\r\n" + | ||||||
| 	"@@@@'                  '  _,w@<    @@@@\r\n" + | 	"@@@@'                  '  _,w@<    @@@@    .\r\n" + | ||||||
| 	"@@@@     @@@@@@@@w___,w@@@@@@@@  @  @@@\r\n" + | 	"@@@@     @@@@@@@@w___,w@@@@@@@@  @  @@@\r\n" + | ||||||
| 	"@@@@     @@@@@@@@@@@@@@@@@@@@@@  I  @@@\r\n" + | 	"@@@@     @@@@@@@@@@@@@@@@@@@@@@  I  @@@   Bob\r\n" + | ||||||
| 	"@@@@     @@@@@@@@@@@@@@@@@@@@*@[ i  @@@\r\n" + | 	"@@@@     @@@@@@@@@@@@@@@@@@@@*@[ i  @@@\r\n" + | ||||||
| 	"@@@@     @@@@@@@@@@@@@@@@@@@@[][ | ]@@@\r\n" + | 	"@@@@     @@@@@@@@@@@@@@@@@@@@[][ | ]@@@           bOb\r\n" + | ||||||
| 	"@@@@     ~_,,_ ~@@@@@@@~ ____~ @    @@@\r\n" + | 	"@@@@     ~_,,_ ~@@@@@@@~ ____~ @    @@@\r\n" + | ||||||
| 	"@@@@    _~ ,  ,  `@@@~  _  _`@ ]L  J@@@\r\n" + | 	"@@@@    _~ ,  ,  `@@@~  _  _`@ ]L  J@@@    o\r\n" + | ||||||
| 	"@@@@  , @@w@ww+   @@@ww``,,@w@ ][  @@@@\r\n" + | 	"@@@@  , @@w@ww+   @@@ww``,,@w@ ][  @@@@\r\n" + | ||||||
| 	"@@@@,  @@@@www@@@ @@@@@@@ww@@@@@[  @@@@\r\n" + | 	"@@@@,  @@@@www@@@ @@@@@@@ww@@@@@[  @@@@      BOB\r\n" + | ||||||
| 	"@@@@@_|| @@@@@@P' @@P@@@@@@@@@@@[|c@@@@\r\n" + | 	"@@@@@_|| @@@@@@P' @@P@@@@@@@@@@@[|c@@@@\r\n" + | ||||||
| 	"@@@@@@w| '@@P~  P]@@@-~, ~Y@@^'],@@@@@@\r\n" + | 	"@@@@@@w| '@@P~  P]@@@-~, ~Y@@^'],@@@@@@           . o\r\n" + | ||||||
| 	"@@@@@@@[   _        _J@@Tk     ]]@@@@@@\r\n" + | 	"@@@@@@@[   _        _J@@Tk     ]]@@@@@@\r\n" + | ||||||
| 	"@@@@@@@@,@ @@, c,,,,,,,y ,w@@[ ,@@@@@@@\r\n" + | 	"@@@@@@@@,@ @@, c,,,,,,,y ,w@@[ ,@@@@@@@\r\n" + | ||||||
| 	"@@@@@@@@@ i @w   ====--_@@@@@  @@@@@@@@\r\n" + | 	"@@@@@@@@@ i @w   ====--_@@@@@  @@@@@@@@       o .\r\n" + | ||||||
| 	"@@@@@@@@@@`,P~ _ ~^^^^Y@@@@@  @@@@@@@@@\r\n" + | 	"@@@@@@@@@@`,P~ _ ~^^^^Y@@@@@  @@@@@@@@@\r\n" + | ||||||
| 	"@@@@^^=^@@^   ^' ,ww,w@@@@@ _@@@@@@@@@@\r\n" + | 	"@@@@^^=^@@^   ^' ,ww,w@@@@@ _@@@@@@@@@@    B   o   B\r\n" + | ||||||
| 	"@@@_xJ~ ~   ,    @@@@@@@P~_@@@@@@@@@@@@\r\n" + | 	"@@@_xJ~ ~   ,    @@@@@@@P~_@@@@@@@@@@@@\r\n" + | ||||||
| 	"@@   @,   ,@@@,_____   _,J@@@@@@@@@@@@@\r\n" + | 	"@@   @,   ,@@@,_____   _,J@@@@@@@@@@@@@\r\n" + | ||||||
| 	"@@L  `' ,@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\r\n" + | 	"@@L  `' ,@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\r\n" + | ||||||
|  | @ -70,7 +87,9 @@ const bob = string("\r\n\r\n" + | ||||||
| 	"\r\n") | 	"\r\n") | ||||||
| 
 | 
 | ||||||
| type ( | type ( | ||||||
|  | 	// Handler for special functions invoked by escSeqs | ||||||
| 	escHandler func(io.Writer) | 	escHandler func(io.Writer) | ||||||
|  | 	// escSeqs is a map of special keystroke sequences to trigger escHandlers | ||||||
| 	escSeqs map[byte]escHandler | 	escSeqs map[byte]escHandler | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | @ -91,6 +110,8 @@ type ( | ||||||
| // calls a client-custom version of copyBuffer(), which allows | // calls a client-custom version of copyBuffer(), which allows | ||||||
| // some client escape sequences to trigger special actions during | // some client escape sequences to trigger special actions during | ||||||
| // interactive sessions. | // interactive sessions. | ||||||
|  | // | ||||||
|  | // (See go doc xs/xs.{escSeqs,escHandler}) | ||||||
| func Copy(dst io.Writer, src io.Reader) (written int64, err error) { | func Copy(dst io.Writer, src io.Reader) (written int64, err error) { | ||||||
| 	written, err = copyBuffer(dst, src, nil) | 	written, err = copyBuffer(dst, src, nil) | ||||||
| 	return | 	return | ||||||
|  | @ -102,9 +123,13 @@ func Copy(dst io.Writer, src io.Reader) (written int64, err error) { | ||||||
| // This private version of copyBuffer is derived from the | // This private version of copyBuffer is derived from the | ||||||
| // go stdlib pkg/io, with escape sequence interpretation to trigger | // go stdlib pkg/io, with escape sequence interpretation to trigger | ||||||
| // some special client-side actions. | // some special client-side actions. | ||||||
|  | // | ||||||
|  | // (See go doc xs/xs.{escSeqs,escHandler}) | ||||||
| func copyBuffer(dst io.Writer, src io.Reader, buf []byte) (written int64, err error) { | func copyBuffer(dst io.Writer, src io.Reader, buf []byte) (written int64, err error) { | ||||||
| 	// NOTE: using dst.Write() in these esc funcs will cause the output | 	// NOTE: using dst.Write() in these esc funcs will cause the output | ||||||
| 	// to function as a 'macro', outputting as if user typed the sequence. | 	// to function as a 'macro', outputting as if user typed the sequence | ||||||
|  | 	// (that is, the client 'sees' the user type it, and the server 'sees' | ||||||
|  | 	// it as well). | ||||||
| 	// | 	// | ||||||
| 	// Using os.Stdout outputs to the client's term w/o it or the server | 	// Using os.Stdout outputs to the client's term w/o it or the server | ||||||
| 	// 'seeing' the output. | 	// 'seeing' the output. | ||||||
|  | @ -115,8 +140,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 | 	// or tunnel traffic indicator - note we cannot just spawn a goroutine | ||||||
| 	// here, as copyBuffer() returns after each burst of data. Scope must | 	// here, as copyBuffer() returns after each burst of data. Scope must | ||||||
| 	// outlive individual copyBuffer calls). | 	// outlive individual copyBuffer calls). | ||||||
| 	// (Note that since this custom copyBuffer func is used only by |  | ||||||
| 	// the hkexsh client, it should eventually be moved to client.) |  | ||||||
| 	escs := escSeqs{ | 	escs := escSeqs{ | ||||||
| 		'i': func(io.Writer) { os.Stdout.Write([]byte("\x1b[s\x1b[2;1H\x1b[1;31m[HKEXSH]\x1b[39;49m\x1b[u")) }, | 		'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")) }, | 		't': func(io.Writer) { os.Stdout.Write([]byte("\x1b[1;32m[HKEXSH]\x1b[39;49m")) }, | ||||||
|  | @ -158,7 +181,7 @@ func copyBuffer(dst io.Writer, src io.Reader, buf []byte) (written int64, err er | ||||||
| 				if buf[0] == 0x1d { | 				if buf[0] == 0x1d { | ||||||
| 					seqPos++ | 					seqPos++ | ||||||
| 				} | 				} | ||||||
| 			} else /* seqPos > 0 */ { | 			} else { | ||||||
| 				if v, ok := escs[buf[0]]; ok { | 				if v, ok := escs[buf[0]]; ok { | ||||||
| 					v(dst) | 					v(dst) | ||||||
| 					nr-- | 					nr-- | ||||||
|  | @ -187,13 +210,14 @@ func copyBuffer(dst io.Writer, src io.Reader, buf []byte) (written int64, err er | ||||||
| 			break | 			break | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	//_,_ = dst.Write([]byte{0x2f}) |  | ||||||
| 	return written, err | 	return written, err | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| //////////////////////////////////////////////////// | //////////////////////////////////////////////////// | ||||||
| 
 | 
 | ||||||
| // GetSize gets the terminal size using 'stty' command | // GetSize gets the terminal size using 'stty' command | ||||||
|  | // | ||||||
|  | // TODO: do in code someday instead of using external 'stty' | ||||||
| func GetSize() (cols, rows int, err error) { | func GetSize() (cols, rows int, err error) { | ||||||
| 	cmd := exec.Command("stty", "size") // #nosec | 	cmd := exec.Command("stty", "size") // #nosec | ||||||
| 	cmd.Stdin = os.Stdin | 	cmd.Stdin = os.Stdin | ||||||
|  | @ -217,20 +241,47 @@ func GetSize() (cols, rows int, err error) { | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // doCopyMode begins a secure hkexsh local<->remote file copy operation. | func buildCmdRemoteToLocal(copyQuiet bool, copyLimitBPS uint, destPath, files string) (captureStderr bool, cmd string, args []string) { | ||||||
| // TODO: reduce gocyclo | 	// Detect if we have 'pv' | ||||||
| func doCopyMode(conn *hkexnet.Conn, remoteDest bool, files string, rec *hkexsh.Session) (exitStatus uint32, err error) { | 	// pipeview http://www.ivarch.com/programs/pv.shtml | ||||||
| 	if remoteDest { | 	// and use it for nice client progress display. | ||||||
| 		log.Println("local files:", files, "remote filepath:", string(rec.Cmd())) | 	_, 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() | 		args = []string{"-xz", "-C", destPath} | ||||||
| 		//os.Setenv("HOME", u.HomeDir) | 	} else { | ||||||
| 		//os.Setenv("TERM", "vt102") // TODO: server or client option? | 		// TODO: Query remote side for total file/dir size | ||||||
|  | 		bandwidthInBytesPerSec := " -L " + fmt.Sprintf("%d ", copyLimitBPS) | ||||||
|  | 		displayOpts := " -pre " | ||||||
|  | 		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" | func buildCmdLocalToRemote(copyQuiet bool, copyLimitBPS uint, files string) (captureStderr bool, cmd string, args []string) { | ||||||
| 		cmdArgs := []string{"-cz", "-f", "/dev/stdout"} | 	// 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) | 		files = strings.TrimSpace(files) | ||||||
| 		// Awesome fact: tar actually can take multiple -C args, and | 		// Awesome fact: tar actually can take multiple -C args, and | ||||||
| 		// changes to the dest dir *as it sees each one*. This enables | 		// changes to the dest dir *as it sees each one*. This enables | ||||||
|  | @ -248,23 +299,79 @@ func doCopyMode(conn *hkexnet.Conn, remoteDest bool, files string, rec *hkexsh.S | ||||||
| 			v, _ = filepath.Abs(v) // #nosec | 			v, _ = filepath.Abs(v) // #nosec | ||||||
| 			dirTmp, fileTmp := path.Split(v) | 			dirTmp, fileTmp := path.Split(v) | ||||||
| 			if dirTmp == "" { | 			if dirTmp == "" { | ||||||
| 				cmdArgs = append(cmdArgs, fileTmp) | 				args = append(args, fileTmp) | ||||||
| 			} else { | 			} else { | ||||||
| 				cmdArgs = append(cmdArgs, "-C", dirTmp, fileTmp) | 				args = append(args, "-C", dirTmp, fileTmp) | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  | 	} else { | ||||||
|  | 		captureStderr = copyQuiet | ||||||
|  | 		bandwidthInBytesPerSec := " -L " + fmt.Sprintf("%d", copyLimitBPS) | ||||||
|  | 		displayOpts := " -pre " | ||||||
|  | 		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) | 	log.Printf("[%v %v]\n", cmd, args) | ||||||
| 		// NOTE the lack of quotes around --xform option's sed expression. | 	return | ||||||
| 		// 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", files, `--xform=s#.*/\(.*\)#\1#`} | 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 = exec.Command(cmdName, cmdArgs...) // #nosec | ||||||
| 		c.Dir, _ = os.Getwd()                 // #nosec | 		c.Dir, _ = os.Getwd()                 // #nosec | ||||||
| 		log.Println("[wd:", c.Dir, "]") | 		log.Println("[wd:", c.Dir, "]") | ||||||
| 		c.Stdout = conn | 		c.Stdout = conn | ||||||
| 		stdErrBuffer := new(bytes.Buffer) | 		stdErrBuffer := new(bytes.Buffer) | ||||||
|  | 		if captureStderr { | ||||||
| 			c.Stderr = stdErrBuffer | 			c.Stderr = stdErrBuffer | ||||||
|  | 		} else { | ||||||
|  | 			c.Stderr = os.Stderr | ||||||
|  | 		} | ||||||
| 
 | 
 | ||||||
| 		// Start the command (no pty) | 		// Start the command (no pty) | ||||||
| 		err = c.Start() // returns immediately | 		err = c.Start() // returns immediately | ||||||
|  | @ -297,8 +404,9 @@ func doCopyMode(conn *hkexnet.Conn, remoteDest bool, files string, rec *hkexsh.S | ||||||
| 					// an ExitStatus() method with the same signature. | 					// an ExitStatus() method with the same signature. | ||||||
| 					if status, ok := exiterr.Sys().(syscall.WaitStatus); ok { | 					if status, ok := exiterr.Sys().(syscall.WaitStatus); ok { | ||||||
| 						exitStatus = uint32(status.ExitStatus()) | 						exitStatus = uint32(status.ExitStatus()) | ||||||
|  | 						if captureStderr { | ||||||
| 							fmt.Print(stdErrBuffer) | 							fmt.Print(stdErrBuffer) | ||||||
| 						fmt.Printf("Exit Status: %d\n", exitStatus) //# | 						} | ||||||
| 					} | 					} | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
|  | @ -306,7 +414,7 @@ func doCopyMode(conn *hkexnet.Conn, remoteDest bool, files string, rec *hkexsh.S | ||||||
| 			log.Println("Sending local exitStatus:", exitStatus) | 			log.Println("Sending local exitStatus:", exitStatus) | ||||||
| 			r := make([]byte, 4) | 			r := make([]byte, 4) | ||||||
| 			binary.BigEndian.PutUint32(r, exitStatus) | 			binary.BigEndian.PutUint32(r, exitStatus) | ||||||
| 			_, we := conn.WritePacket(r, hkexnet.CSOExitStatus) | 			_, we := conn.WritePacket(r, xsnet.CSOExitStatus) | ||||||
| 			if we != nil { | 			if we != nil { | ||||||
| 				fmt.Println("Error:", we) | 				fmt.Println("Error:", we) | ||||||
| 			} | 			} | ||||||
|  | @ -331,21 +439,11 @@ func doCopyMode(conn *hkexnet.Conn, remoteDest bool, files string, rec *hkexsh.S | ||||||
| 		} | 		} | ||||||
| 	} else { | 	} else { | ||||||
| 		log.Println("remote filepath:", string(rec.Cmd()), "local files:", files) | 		log.Println("remote filepath:", string(rec.Cmd()), "local files:", files) | ||||||
| 		var c *exec.Cmd |  | ||||||
| 
 |  | ||||||
| 		//os.Clearenv() |  | ||||||
| 		//os.Setenv("HOME", u.HomeDir) |  | ||||||
| 		//os.Setenv("TERM", "vt102") // TODO: server or client option? |  | ||||||
| 
 |  | ||||||
| 		cmdName := "/bin/tar" |  | ||||||
| 		destPath := files | 		destPath := files | ||||||
| 
 | 
 | ||||||
| 		cmdArgs := []string{"-xz", "-C", destPath} | 		_, cmdName, cmdArgs := buildCmdRemoteToLocal(copyQuiet, copyLimitBPS, destPath, strings.TrimSpace(files)) | ||||||
| 		log.Printf("[%v %v]\n", cmdName, cmdArgs) | 
 | ||||||
| 		// NOTE the lack of quotes around --xform option's sed expression. | 		var c *exec.Cmd | ||||||
| 		// 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 | 		c = exec.Command(cmdName, cmdArgs...) // #nosec | ||||||
| 		c.Stdin = conn | 		c.Stdin = conn | ||||||
| 		c.Stdout = os.Stdout | 		c.Stdout = os.Stdout | ||||||
|  | @ -355,7 +453,6 @@ func doCopyMode(conn *hkexnet.Conn, remoteDest bool, files string, rec *hkexsh.S | ||||||
| 		err = c.Start() // returns immediately | 		err = c.Start() // returns immediately | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			fmt.Println(err) | 			fmt.Println(err) | ||||||
| 			//log.Fatal(err) |  | ||||||
| 		} else { | 		} else { | ||||||
| 			if err = c.Wait(); err != nil { | 			if err = c.Wait(); err != nil { | ||||||
| 				if exiterr, ok := err.(*exec.ExitError); ok { | 				if exiterr, ok := err.(*exec.ExitError); ok { | ||||||
|  | @ -367,7 +464,6 @@ func doCopyMode(conn *hkexnet.Conn, remoteDest bool, files string, rec *hkexsh.S | ||||||
| 					// an ExitStatus() method with the same signature. | 					// an ExitStatus() method with the same signature. | ||||||
| 					if status, ok := exiterr.Sys().(syscall.WaitStatus); ok { | 					if status, ok := exiterr.Sys().(syscall.WaitStatus); ok { | ||||||
| 						exitStatus = uint32(status.ExitStatus()) | 						exitStatus = uint32(status.ExitStatus()) | ||||||
| 						log.Printf("Exit Status: %d", exitStatus) |  | ||||||
| 					} | 					} | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
|  | @ -382,8 +478,9 @@ func doCopyMode(conn *hkexnet.Conn, remoteDest bool, files string, rec *hkexsh.S | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // doShellMode begins an hkexsh shell session (one-shot command or interactive). | // doShellMode begins an xs shell session (one-shot command or | ||||||
| func doShellMode(isInteractive bool, conn *hkexnet.Conn, oldState *hkexsh.State, rec *hkexsh.Session) { | // interactive). | ||||||
|  | func doShellMode(isInteractive bool, conn *xsnet.Conn, oldState *xs.State, rec *xs.Session) { | ||||||
| 	//client reader (from server) goroutine | 	//client reader (from server) goroutine | ||||||
| 	//Read remote end's stdout | 	//Read remote end's stdout | ||||||
| 
 | 
 | ||||||
|  | @ -403,13 +500,13 @@ func doShellMode(isInteractive bool, conn *hkexnet.Conn, oldState *hkexsh.State, | ||||||
| 		// exit with inerr == nil | 		// exit with inerr == nil | ||||||
| 		_, inerr := io.Copy(os.Stdout, conn) | 		_, inerr := io.Copy(os.Stdout, conn) | ||||||
| 		if inerr != nil { | 		if inerr != nil { | ||||||
| 			_ = hkexsh.Restore(int(os.Stdin.Fd()), oldState) // #nosec | 			restoreTermState(oldState) | ||||||
| 			// Copy operations and user logging off will cause | 			// Copy operations and user logging off will cause | ||||||
| 			// a "use of closed network connection" so handle that | 			// a "use of closed network connection" so handle that | ||||||
| 			// gracefully here | 			// gracefully here | ||||||
| 			if !strings.HasSuffix(inerr.Error(), "use of closed network connection") { | 			if !strings.HasSuffix(inerr.Error(), "use of closed network connection") { | ||||||
| 				log.Println(inerr) | 				log.Println(inerr) | ||||||
| 				os.Exit(1) | 				exitWithStatus(1) | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | @ -418,8 +515,8 @@ func doShellMode(isInteractive bool, conn *hkexnet.Conn, oldState *hkexsh.State, | ||||||
| 
 | 
 | ||||||
| 		if isInteractive { | 		if isInteractive { | ||||||
| 			log.Println("[* Got EOF *]") | 			log.Println("[* Got EOF *]") | ||||||
| 			_ = hkexsh.Restore(int(os.Stdin.Fd()), oldState) // #nosec | 			restoreTermState(oldState) | ||||||
| 			os.Exit(int(rec.Status())) | 			exitWithStatus(int(rec.Status())) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	go shellRemoteToStdin() | 	go shellRemoteToStdin() | ||||||
|  | @ -436,8 +533,7 @@ func doShellMode(isInteractive bool, conn *hkexnet.Conn, oldState *hkexsh.State, | ||||||
| 		// TODO:.gv:doShellMode:2:shellStdinToRemote | 		// TODO:.gv:doShellMode:2:shellStdinToRemote | ||||||
| 		shellStdinToRemote := func() { | 		shellStdinToRemote := func() { | ||||||
| 			defer wg.Done() | 			defer wg.Done() | ||||||
| 			//!defer wg.Done() | 			_, outerr := func(conn *xsnet.Conn, r io.Reader) (w int64, e error) { | ||||||
| 			_, outerr := func(conn *hkexnet.Conn, r io.Reader) (w int64, e error) { |  | ||||||
| 				// Copy() expects EOF so this will | 				// Copy() expects EOF so this will | ||||||
| 				// exit with outerr == nil | 				// exit with outerr == nil | ||||||
| 				w, e = Copy(conn, r) | 				w, e = Copy(conn, r) | ||||||
|  | @ -447,9 +543,9 @@ func doShellMode(isInteractive bool, conn *hkexnet.Conn, oldState *hkexsh.State, | ||||||
| 			if outerr != nil { | 			if outerr != nil { | ||||||
| 				log.Println(outerr) | 				log.Println(outerr) | ||||||
| 				fmt.Println(outerr) | 				fmt.Println(outerr) | ||||||
| 				_ = hkexsh.Restore(int(os.Stdin.Fd()), oldState) // #nosec | 				restoreTermState(oldState) | ||||||
| 				log.Println("[Hanging up]") | 				log.Println("[Hanging up]") | ||||||
| 				os.Exit(0) | 				exitWithStatus(0) | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		go shellStdinToRemote() | 		go shellStdinToRemote() | ||||||
|  | @ -473,6 +569,10 @@ func usageCp() { | ||||||
| 	flag.PrintDefaults() | 	flag.PrintDefaults() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // rejectUserMsg snarkily rebukes users giving incorrect | ||||||
|  | // credentials. | ||||||
|  | // | ||||||
|  | // TODO: do this from the server side and have client just emit that | ||||||
| func rejectUserMsg() string { | func rejectUserMsg() string { | ||||||
| 	return "Begone, " + spinsult.GetSentence() + "\r\n" | 	return "Begone, " + spinsult.GetSentence() + "\r\n" | ||||||
| } | } | ||||||
|  | @ -480,8 +580,8 @@ func rejectUserMsg() string { | ||||||
| // Transmit request to server for it to set up the remote end of a tunnel | // Transmit request to server for it to set up the remote end of a tunnel | ||||||
| // | // | ||||||
| // Server responds with [CSOTunAck:rport] or [CSOTunRefused:rport] | // Server responds with [CSOTunAck:rport] or [CSOTunRefused:rport] | ||||||
| // (handled in hkexnet.Read()) | // (handled in xsnet.Read()) | ||||||
| func reqTunnel(hc *hkexnet.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 | 	// Write request to server so it can attempt to set up its end | ||||||
| 	var bTmp bytes.Buffer | 	var bTmp bytes.Buffer | ||||||
| 	if e := binary.Write(&bTmp, binary.BigEndian, lp); e != nil { | 	if e := binary.Write(&bTmp, binary.BigEndian, lp); e != nil { | ||||||
|  | @ -491,7 +591,7 @@ func reqTunnel(hc *hkexnet.Conn, lp uint16, p string /*net.Addr*/, rp uint16) { | ||||||
| 		fmt.Fprintln(os.Stderr, "reqTunnel:", e) // nolint: errcheck | 		fmt.Fprintln(os.Stderr, "reqTunnel:", e) // nolint: errcheck | ||||||
| 	} | 	} | ||||||
| 	_ = logger.LogDebug(fmt.Sprintln("[Client sending CSOTunSetup]")) // nolint: gosec | 	_ = logger.LogDebug(fmt.Sprintln("[Client sending CSOTunSetup]")) // nolint: gosec | ||||||
| 	if n, e := hc.WritePacket(bTmp.Bytes(), hkexnet.CSOTunSetup); e != nil || n != len(bTmp.Bytes()) { | 	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) // nolint: errcheck | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | @ -521,15 +621,9 @@ func parseNonSwitchArgs(a []string) (user, host, path string, isDest bool, other | ||||||
| 			} | 			} | ||||||
| 			fancyHost = fancyHostPath[0] | 			fancyHost = fancyHostPath[0] | ||||||
| 
 | 
 | ||||||
| 			//if fancyPath == "" { |  | ||||||
| 			//	fancyPath = "." |  | ||||||
| 			//} |  | ||||||
| 
 |  | ||||||
| 			if i == len(a)-1 { | 			if i == len(a)-1 { | ||||||
| 				isDest = true | 				isDest = true | ||||||
| 				//fmt.Println("remote path isDest") |  | ||||||
| 			} | 			} | ||||||
| 			//fmt.Println("fancyArgs: user:", fancyUser, "host:", fancyHost, "path:", fancyPath) |  | ||||||
| 		} else { | 		} else { | ||||||
| 			otherArgs = append(otherArgs, a[i]) | 			otherArgs = append(otherArgs, a[i]) | ||||||
| 		} | 		} | ||||||
|  | @ -537,7 +631,7 @@ func parseNonSwitchArgs(a []string) (user, host, path string, isDest bool, other | ||||||
| 	return fancyUser, fancyHost, fancyPath, isDest, otherArgs | 	return fancyUser, fancyHost, fancyPath, isDest, otherArgs | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func launchTuns(conn *hkexnet.Conn, remoteHost string, tuns string) { | func launchTuns(conn *xsnet.Conn, remoteHost string, tuns string) { | ||||||
| 	remAddrs, _ := net.LookupHost(remoteHost) // nolint: gosec | 	remAddrs, _ := net.LookupHost(remoteHost) // nolint: gosec | ||||||
| 
 | 
 | ||||||
| 	if tuns == "" { | 	if tuns == "" { | ||||||
|  | @ -552,7 +646,7 @@ func launchTuns(conn *hkexnet.Conn, remoteHost string, tuns string) { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func sendSessionParams(conn io.Writer /* *hkexnet.Conn*/, rec *hkexsh.Session) (e error) { | func sendSessionParams(conn io.Writer /* *xsnet.Conn*/, rec *xs.Session) (e error) { | ||||||
| 	_, e = fmt.Fprintf(conn, "%d %d %d %d %d %d\n", | 	_, e = fmt.Fprintf(conn, "%d %d %d %d %d %d\n", | ||||||
| 		len(rec.Op()), len(rec.Who()), len(rec.ConnHost()), len(rec.TermType()), len(rec.Cmd()), len(rec.AuthCookie(true))) | 		len(rec.Op()), len(rec.Who()), len(rec.ConnHost()), len(rec.TermType()), len(rec.Cmd()), len(rec.AuthCookie(true))) | ||||||
| 	if e != nil { | 	if e != nil { | ||||||
|  | @ -582,71 +676,101 @@ func sendSessionParams(conn io.Writer /* *hkexnet.Conn*/, rec *hkexsh.Session) ( | ||||||
| 	return e | 	return e | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // hkexsh - a client for secure shell and file copy operations. |  | ||||||
| // |  | ||||||
| // While conforming to the basic net.Conn interface HKex.Conn has extra |  | ||||||
| // capabilities designed to allow apps to define connection options, |  | ||||||
| // encryption/hmac settings and operations across the encrypted channel. |  | ||||||
| // |  | ||||||
| // Initial setup is the same as using plain net.Dial(), but one may |  | ||||||
| // specify extra extension tags (strings) to set the cipher and hmac |  | ||||||
| // setting desired; as well as the intended operation mode for the |  | ||||||
| // connection (app-specific, passed through to the server to use or |  | ||||||
| // ignore at its discretion). |  | ||||||
| // TODO: reduce gocyclo | // TODO: reduce gocyclo | ||||||
| func main() { | func main() { | ||||||
| 	version := hkexsh.Version | 	var ( | ||||||
| 	var vopt bool | 		isInteractive bool | ||||||
| 	var gopt bool //login via password, asking server to generate authToken | 		vopt          bool | ||||||
| 	var dbg bool | 		gopt          bool //login via password, asking server to generate authToken | ||||||
| 	var shellMode bool   // if true act as shell, else file copier | 		dbg           bool | ||||||
| 	var cipherAlg string //cipher alg | 		shellMode     bool   // if true act as shell, else file copier | ||||||
| 	var hmacAlg string   //hmac alg | 		cipherAlg     string //cipher alg | ||||||
| 	var kexAlg string    //KEX/KEM alg | 		hmacAlg       string //hmac alg | ||||||
| 	var server string | 		kexAlg        string //KEX/KEM alg | ||||||
| 	var port uint | 		server        string | ||||||
| 	var cmdStr string | 		port          uint | ||||||
| 	var tunSpecStr string // lport1:rport1[,lport2:rport2,...] | 		cmdStr        string | ||||||
|  | 		tunSpecStr    string // lport1:rport1[,lport2:rport2,...] | ||||||
| 
 | 
 | ||||||
| 	var copySrc []byte | 		copySrc      []byte | ||||||
| 	var copyDst string | 		copyDst      string | ||||||
|  | 		copyQuiet    bool | ||||||
|  | 		copyLimitBPS uint | ||||||
| 
 | 
 | ||||||
| 	var authCookie string | 		authCookie    string | ||||||
| 	var chaffEnabled bool | 		chaffEnabled  bool | ||||||
| 	var chaffFreqMin uint | 		chaffFreqMin  uint | ||||||
| 	var chaffFreqMax uint | 		chaffFreqMax  uint | ||||||
| 	var chaffBytesMax uint | 		chaffBytesMax uint | ||||||
| 
 | 
 | ||||||
| 	var op []byte | 		op []byte | ||||||
| 	isInteractive := false | 	) | ||||||
|  | 
 | ||||||
|  | 	//=== Common (xs and xc) option parsing | ||||||
| 
 | 
 | ||||||
| 	flag.BoolVar(&vopt, "v", false, "show version") | 	flag.BoolVar(&vopt, "v", false, "show version") | ||||||
| 	flag.BoolVar(&dbg, "d", false, "debug logging") | 	flag.BoolVar(&dbg, "d", false, "debug logging") | ||||||
| 	flag.StringVar(&cipherAlg, "c", "C_AES_256", "`cipher` [\"C_AES_256\" | \"C_TWOFISH_128\" | \"C_BLOWFISH_64\" | \"C_CRYPTMT1\"]") | 	flag.StringVar(&cipherAlg, "c", "C_AES_256", "session `cipher` [C_AES_256 | C_TWOFISH_128 | C_BLOWFISH_64 | C_CRYPTMT1 | C_CHACHA20_12]") | ||||||
| 	flag.StringVar(&hmacAlg, "m", "H_SHA256", "`hmac` [\"H_SHA256\"]") | 	flag.StringVar(&hmacAlg, "m", "H_SHA256", "session `HMAC` [H_SHA256 | H_SHA512]") | ||||||
| 	flag.StringVar(&kexAlg, "k", "KEX_HERRADURA256", "`kex` [\"KEX_HERRADURA{256/512/1024/2048}\" | \"KEX_KYBER{512/768/1024}\"]") | 	flag.StringVar(&kexAlg, "k", "KEX_HERRADURA512", "KEx `alg` [KEX_HERRADURA{256/512/1024/2048} | KEX_KYBER{512/768/1024} | KEX_NEWHOPE | KEX_NEWHOPE_SIMPLE | KEX_FRODOKEM_{1344|976}{AES|SHAKE}]") | ||||||
| 	flag.UintVar(&port, "p", 2000, "`port`") | 	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(&authCookie, "a", "", "auth cookie") | ||||||
| 	flag.BoolVar(&chaffEnabled, "e", true, "enabled chaff pkts (default true)") | 	flag.BoolVar(&chaffEnabled, "e", true, "enable chaff pkts") | ||||||
| 	flag.UintVar(&chaffFreqMin, "f", 100, "`msecs-min` chaff pkt freq min (msecs)") | 	flag.UintVar(&chaffFreqMin, "f", 100, "chaff pkt freq min `msecs`") | ||||||
| 	flag.UintVar(&chaffFreqMax, "F", 5000, "`msecs-max` chaff pkt freq max (msecs)") | 	flag.UintVar(&chaffFreqMax, "F", 5000, "chaff pkt freq max `msecs`") | ||||||
| 	flag.UintVar(&chaffBytesMax, "B", 64, "chaff pkt size max (bytes)") | 	flag.UintVar(&chaffBytesMax, "B", 64, "chaff pkt size max `bytes`") | ||||||
|  | 
 | ||||||
|  | 	flag.StringVar(&cpuprofile, "cpuprofile", "", "write cpu profile to <`file`>") | ||||||
|  | 	flag.StringVar(&memprofile, "memprofile", "", "write memory profile to <`file`>") | ||||||
|  | 
 | ||||||
|  | 	//=== xc vs. xs option parsing | ||||||
| 
 | 
 | ||||||
| 	// Find out what program we are (shell or copier) | 	// Find out what program we are (shell or copier) | ||||||
| 	myPath := strings.Split(os.Args[0], string(os.PathSeparator)) | 	myPath := strings.Split(os.Args[0], string(os.PathSeparator)) | ||||||
| 	if myPath[len(myPath)-1] != "hkexcp" && myPath[len(myPath)-1] != "hkexcp.exe" { | 	if myPath[len(myPath)-1] != "xc" && | ||||||
| 		// hkexsh accepts a command (-x) but not | 		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) | 		// a srcpath (-r) or dstpath (-t) | ||||||
| 		flag.StringVar(&cmdStr, "x", "", "`command` to run (if not specified run interactive shell)") | 		flag.StringVar(&cmdStr, "x", "", "run <`command`> (if not specified, run interactive shell)") | ||||||
| 		flag.StringVar(&tunSpecStr, "T", "", "`tunnelspec` localPort:remotePort[,localPort:remotePort,...]") | 		flag.StringVar(&tunSpecStr, "T", "", "``tunnelspec - localPort:remotePort[,localPort:remotePort,...]") | ||||||
| 		flag.BoolVar(&gopt, "g", false, "ask server to generate authtoken") | 		flag.BoolVar(&gopt, "g", false, "ask server to generate authtoken") | ||||||
| 		shellMode = true | 		shellMode = true | ||||||
| 		flag.Usage = usageShell | 		flag.Usage = usageShell | ||||||
| 	} else { | 	} else { | ||||||
|  | 		flag.BoolVar(©Quiet, "q", false, "do not output progress bar during copy") | ||||||
|  | 		flag.UintVar(©LimitBPS, "L", 8589934592, "copy max rate in bytes per sec") | ||||||
| 		flag.Usage = usageCp | 		flag.Usage = usageCp | ||||||
| 	} | 	} | ||||||
| 	flag.Parse() | 	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 { | ||||||
|  | 			log.Fatal("could not create CPU profile: ", err) | ||||||
|  | 		} | ||||||
|  | 		defer f.Close() | ||||||
|  | 		fmt.Println("StartCPUProfile()") | ||||||
|  | 		if err := pprof.StartCPUProfile(f); err != nil { | ||||||
|  | 			log.Fatal("could not start CPU profile: ", err) | ||||||
|  | 		} else { | ||||||
|  | 			defer pprof.StopCPUProfile() | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		go func() { http.ListenAndServe("localhost:6060", nil) }() | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	//=== User, host, port and path args for file operations, if applicable | ||||||
|  | 
 | ||||||
| 	remoteUser, remoteHost, tmpPath, pathIsDest, otherArgs := | 	remoteUser, remoteHost, tmpPath, pathIsDest, otherArgs := | ||||||
| 		parseNonSwitchArgs(flag.Args()) | 		parseNonSwitchArgs(flag.Args()) | ||||||
| 	//fmt.Println("otherArgs:", otherArgs) | 	//fmt.Println("otherArgs:", otherArgs) | ||||||
|  | @ -655,7 +779,7 @@ func main() { | ||||||
| 	var uname string | 	var uname string | ||||||
| 	if remoteUser == "" { | 	if remoteUser == "" { | ||||||
| 		u, _ := user.Current() // nolint: gosec | 		u, _ := user.Current() // nolint: gosec | ||||||
| 		uname = u.Username | 		uname = localUserName(u) | ||||||
| 	} else { | 	} else { | ||||||
| 		uname = remoteUser | 		uname = remoteUser | ||||||
| 	} | 	} | ||||||
|  | @ -667,6 +791,8 @@ func main() { | ||||||
| 		tmpPath = "." | 		tmpPath = "." | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	//=== Copy mode arg and copy src/dest setup | ||||||
|  | 
 | ||||||
| 	var fileArgs string | 	var fileArgs string | ||||||
| 	if !shellMode /*&& tmpPath != ""*/ { | 	if !shellMode /*&& tmpPath != ""*/ { | ||||||
| 		// -if pathIsSrc && len(otherArgs) > 1 ERROR | 		// -if pathIsSrc && len(otherArgs) > 1 ERROR | ||||||
|  | @ -698,40 +824,36 @@ func main() { | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Do some more option consistency checks | 	//=== Do some final option consistency checks | ||||||
| 
 | 
 | ||||||
| 	//fmt.Println("server finally is:", server) | 	//fmt.Println("server finally is:", server) | ||||||
| 	if flag.NFlag() == 0 && server == "" { | 	if flag.NFlag() == 0 && server == "" { | ||||||
| 		flag.Usage() | 		flag.Usage() | ||||||
| 		os.Exit(0) | 		exitWithStatus(0) | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if vopt { |  | ||||||
| 		fmt.Printf("version v%s\n", version) |  | ||||||
| 		os.Exit(0) |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if len(cmdStr) != 0 && (len(copySrc) != 0 || len(copyDst) != 0) { | 	if len(cmdStr) != 0 && (len(copySrc) != 0 || len(copyDst) != 0) { | ||||||
| 		log.Fatal("incompatible options -- either cmd (-x) or copy ops but not both") | 		log.Fatal("incompatible options -- either cmd (-x) or copy ops but not both") | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	//------------------------------------------------------------------- |  | ||||||
| 	// Here we have parsed all options and can now carry out | 	// Here we have parsed all options and can now carry out | ||||||
| 	// either the shell session or copy operation. | 	// either the shell session or copy operation. | ||||||
| 	_ = shellMode | 	_ = shellMode | ||||||
| 
 | 
 | ||||||
| 	Log, _ = logger.New(logger.LOG_USER|logger.LOG_DEBUG|logger.LOG_NOTICE|logger.LOG_ERR, "hkexsh") // nolint: errcheck,gosec | 	Log, _ = logger.New(logger.LOG_USER|logger.LOG_DEBUG|logger.LOG_NOTICE|logger.LOG_ERR, "xs") // nolint: errcheck,gosec | ||||||
| 	hkexnet.Init(dbg, "hkexsh", logger.LOG_USER|logger.LOG_DEBUG|logger.LOG_NOTICE|logger.LOG_ERR) | 	xsnet.Init(dbg, "xs", logger.LOG_USER|logger.LOG_DEBUG|logger.LOG_NOTICE|logger.LOG_ERR) | ||||||
| 	if dbg { | 	if dbg { | ||||||
| 		log.SetOutput(Log) | 		log.SetOutput(Log) | ||||||
| 	} else { | 	} else { | ||||||
| 		log.SetOutput(ioutil.Discard) | 		log.SetOutput(ioutil.Discard) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	//=== Auth token fetch for login | ||||||
|  | 
 | ||||||
| 	if !gopt { | 	if !gopt { | ||||||
| 		// See if we can log in via an auth token | 		// See if we can log in via an auth token | ||||||
| 		u, _ := user.Current() // nolint: gosec | 		u, _ := user.Current() // nolint: gosec | ||||||
| 		ab, aerr := ioutil.ReadFile(fmt.Sprintf("%s/.hkexsh_id", u.HomeDir)) | 		ab, aerr := ioutil.ReadFile(fmt.Sprintf("%s/.xs_id", u.HomeDir)) | ||||||
| 		if aerr == nil { | 		if aerr == nil { | ||||||
| 			idx := strings.Index(string(ab), remoteHost) | 			idx := strings.Index(string(ab), remoteHost) | ||||||
| 			if idx >= 0 { | 			if idx >= 0 { | ||||||
|  | @ -745,10 +867,23 @@ func main() { | ||||||
| 				_, _ = fmt.Fprintln(os.Stderr, "[no authtoken, use -g to request one from server]") | 				_, _ = fmt.Fprintln(os.Stderr, "[no authtoken, use -g to request one from server]") | ||||||
| 			} | 			} | ||||||
| 		} else { | 		} else { | ||||||
| 			log.Printf("[cannot read %s/.hkexsh_id]\n", u.HomeDir) | 			log.Printf("[cannot read %s/.xs_id]\n", u.HomeDir) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	//=== Enforce some sane min/max vals on chaff flags | ||||||
|  | 	if chaffFreqMin < 2 { | ||||||
|  | 		chaffFreqMin = 2 | ||||||
|  | 	} | ||||||
|  | 	if chaffFreqMax == 0 { | ||||||
|  | 		chaffFreqMax = chaffFreqMin + 1 | ||||||
|  | 	} | ||||||
|  | 	if chaffBytesMax == 0 || chaffBytesMax > 4096 { | ||||||
|  | 		chaffBytesMax = 64 | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	//=== Shell vs. Copy mode chaff and cmd setup | ||||||
|  | 
 | ||||||
| 	if shellMode { | 	if shellMode { | ||||||
| 		// We must make the decision about interactivity before Dial() | 		// We must make the decision about interactivity before Dial() | ||||||
| 		// as it affects chaffing behaviour. 20180805 | 		// as it affects chaffing behaviour. 20180805 | ||||||
|  | @ -789,67 +924,102 @@ func main() { | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	conn, err := hkexnet.Dial("tcp", server, cipherAlg, hmacAlg, kexAlg) | 	//=== TCP / KCP Dial setup | ||||||
|  | 
 | ||||||
|  | 	proto := "tcp" | ||||||
|  | 	if kcpMode != "unused" { | ||||||
|  | 		proto = "kcp" | ||||||
|  | 	} | ||||||
|  | 	conn, err := xsnet.Dial(proto, server, cipherAlg, hmacAlg, kexAlg, kcpMode) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		fmt.Println(err) | 		fmt.Println(err) | ||||||
| 		panic(err) | 		exitWithStatus(3) | ||||||
| 	} | 	} | ||||||
| 	defer conn.Close() // nolint: errcheck | 
 | ||||||
| 	// From this point on, conn is a secure encrypted channel | 	//=== Shell terminal mode (Shell vs. Copy) setup | ||||||
| 
 | 
 | ||||||
| 	// Set stdin in raw mode if it's an interactive session | 	// Set stdin in raw mode if it's an interactive session | ||||||
| 	// TODO: send flag to server side indicating this | 	// TODO: send flag to server side indicating this | ||||||
| 	//  affects shell command used | 	//  affects shell command used | ||||||
| 	var oldState *hkexsh.State | 	var oldState *xs.State | ||||||
|  | 	defer conn.Close() // nolint: errcheck | ||||||
|  | 
 | ||||||
|  | 	//=== From this point on, conn is a secure encrypted channel | ||||||
|  | 
 | ||||||
| 	if shellMode { | 	if shellMode { | ||||||
| 		if isatty.IsTerminal(os.Stdin.Fd()) { | 		if isatty.IsTerminal(os.Stdin.Fd()) { | ||||||
| 			oldState, err = hkexsh.MakeRaw(int(os.Stdin.Fd())) | 			oldState, err = xs.MakeRaw(os.Stdin.Fd()) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				panic(err) | 				panic(err) | ||||||
| 			} | 			} | ||||||
| 			// #gv:s/label=\"main\$1\"/label=\"deferRestore\"/ | 			// #gv:s/label=\"main\$1\"/label=\"deferRestore\"/ | ||||||
| 			// TODO:.gv:main:1:deferRestore | 			// TODO:.gv:main:1:deferRestore | ||||||
| 			defer func() { _ = hkexsh.Restore(int(os.Stdin.Fd()), oldState) }() // nolint: errcheck,gosec | 			defer restoreTermState(oldState) | ||||||
| 		} else { | 		} else { | ||||||
| 			log.Println("NOT A TTY") | 			log.Println("NOT A TTY") | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	//=== Login phase | ||||||
|  | 
 | ||||||
|  | 	// Start login timeout here and disconnect if user/pass phase stalls | ||||||
|  | 	//iloginImpatience := time.AfterFunc(20*time.Second, func() { | ||||||
|  | 	//i	fmt.Printf(" .. [you still there? Waiting for a password.]") | ||||||
|  | 	//i}) | ||||||
|  | 	loginTimeout := time.AfterFunc(30*time.Second, func() { | ||||||
|  | 		restoreTermState(oldState) | ||||||
|  | 		fmt.Printf(" .. [login timeout]\n") | ||||||
|  | 		exitWithStatus(xsnet.CSOLoginTimeout) | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
| 	if len(authCookie) == 0 { | 	if len(authCookie) == 0 { | ||||||
| 		//No auth token, prompt for password | 		//No auth token, prompt for password | ||||||
| 		fmt.Printf("Gimme cookie:") | 		fmt.Printf("Gimme cookie:") | ||||||
| 		ab, e := hkexsh.ReadPassword(int(os.Stdin.Fd())) | 		ab, e := xs.ReadPassword(os.Stdin.Fd()) | ||||||
| 		fmt.Printf("\r\n") | 		fmt.Printf("\r\n") | ||||||
| 		if e != nil { | 		if e != nil { | ||||||
| 			panic(e) | 			panic(e) | ||||||
| 		} | 		} | ||||||
| 		authCookie = string(ab) | 		authCookie = string(ab) | ||||||
| 	} | 	} | ||||||
|  | 
 | ||||||
|  | 	//i_ = loginImpatience.Stop() | ||||||
|  | 	_ = loginTimeout.Stop() | ||||||
| 	// Security scrub | 	// Security scrub | ||||||
| 	runtime.GC() | 	runtime.GC() | ||||||
| 
 | 
 | ||||||
|  | 	//=== Session param and TERM setup | ||||||
|  | 
 | ||||||
| 	// Set up session params and send over to server | 	// Set up session params and send over to server | ||||||
| 	rec := hkexsh.NewSession(op, []byte(uname), []byte(remoteHost), []byte(os.Getenv("TERM")), []byte(cmdStr), []byte(authCookie), 0) | 	rec := xs.NewSession(op, []byte(uname), []byte(remoteHost), []byte(os.Getenv("TERM")), []byte(cmdStr), []byte(authCookie), 0) | ||||||
| 	sendErr := sendSessionParams(&conn, rec) | 	sendErr := sendSessionParams(&conn, rec) | ||||||
| 	if sendErr != nil { | 	if sendErr != nil { | ||||||
| 		log.Fatal(sendErr) | 		restoreTermState(oldState) | ||||||
|  | 		rec.SetStatus(254) | ||||||
|  | 		fmt.Fprintln(os.Stderr, "Error: server rejected secure proposal params or login timed out") // nolint: errcheck | ||||||
|  | 		exitWithStatus(int(rec.Status())) | ||||||
|  | 		//log.Fatal(sendErr) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	//Security scrub | 	//Security scrub | ||||||
| 	authCookie = "" // nolint: ineffassign | 	authCookie = "" // nolint: ineffassign | ||||||
| 	runtime.GC() | 	runtime.GC() | ||||||
| 
 | 
 | ||||||
| 	// Read auth reply from server | 	//=== Login Auth | ||||||
|  | 
 | ||||||
|  | 	//=== Read auth reply from server | ||||||
| 	authReply := make([]byte, 1) // bool: 0 = fail, 1 = pass | 	authReply := make([]byte, 1) // bool: 0 = fail, 1 = pass | ||||||
| 	_, err = conn.Read(authReply) | 	_, err = conn.Read(authReply) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
|  | 		//=== Exit if auth reply not received | ||||||
| 		fmt.Fprintln(os.Stderr, "Error reading auth reply") // nolint: errcheck | 		fmt.Fprintln(os.Stderr, "Error reading auth reply") // nolint: errcheck | ||||||
| 		rec.SetStatus(255) | 		rec.SetStatus(255) | ||||||
| 	} else if authReply[0] == 0 { | 	} else if authReply[0] == 0 { | ||||||
|  | 		//=== .. or if auth failed | ||||||
| 		fmt.Fprintln(os.Stderr, rejectUserMsg()) // nolint: errcheck | 		fmt.Fprintln(os.Stderr, rejectUserMsg()) // nolint: errcheck | ||||||
| 		rec.SetStatus(255) | 		rec.SetStatus(255) | ||||||
| 	} else { | 	} else { | ||||||
| 		// Set up chaffing to server | 		//=== Set up chaffing to server | ||||||
| 		conn.SetupChaff(chaffFreqMin, chaffFreqMax, chaffBytesMax) // enable client->server chaffing | 		conn.SetupChaff(chaffFreqMin, chaffFreqMax, chaffBytesMax) // enable client->server chaffing | ||||||
| 		if chaffEnabled { | 		if chaffEnabled { | ||||||
| 			// #gv:s/label=\"main\$2\"/label=\"deferCloseChaff\"/ | 			// #gv:s/label=\"main\$2\"/label=\"deferCloseChaff\"/ | ||||||
|  | @ -859,34 +1029,85 @@ func main() { | ||||||
| 			defer conn.ShutdownChaff() | 			defer conn.ShutdownChaff() | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		// Keepalive for any tunnels that may exist | 		//=== (goroutine) Start keepAliveWorker for tunnels | ||||||
| 		// #gv:s/label=\"main\$1\"/label=\"tunKeepAlive\"/ | 		// #gv:s/label=\"main\$1\"/label=\"tunKeepAlive\"/ | ||||||
| 		// TODO:.gv:main:1: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 != "" { | ||||||
| 		keepAliveWorker := func() { | 		keepAliveWorker := func() { | ||||||
| 			for { | 			for { | ||||||
| 				time.Sleep(time.Duration(2) * time.Second) | 				// Add a bit of jitter to keepAlive so it doesn't stand out quite as much | ||||||
| 				conn.WritePacket([]byte{0, 0}, hkexnet.CSOTunKeepAlive) // nolint: errcheck,gosec | 				time.Sleep(time.Duration(2000-rand.Intn(200)) * time.Millisecond) | ||||||
|  | 				// FIXME: keepAlives should probably have small random packet len/data as well | ||||||
|  | 				// to further obscure them vs. interactive or tunnel data | ||||||
|  | 				// keepAlives must be  >=2 bytes, due to processing elsewhere | ||||||
|  | 				conn.WritePacket([]byte{0, 0}, xsnet.CSOTunKeepAlive) // nolint: errcheck,gosec | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		go keepAliveWorker() | 		go keepAliveWorker() | ||||||
|  | 		//[1]} | ||||||
| 
 | 
 | ||||||
|  | 		//=== Session entry (shellMode or copyMode) | ||||||
| 		if shellMode { | 		if shellMode { | ||||||
|  | 			//=== (shell) launch tunnels | ||||||
| 			launchTuns(&conn, remoteHost, tunSpecStr) | 			launchTuns(&conn, remoteHost, tunSpecStr) | ||||||
| 
 |  | ||||||
| 			doShellMode(isInteractive, &conn, oldState, rec) | 			doShellMode(isInteractive, &conn, oldState, rec) | ||||||
| 		} else { // copyMode | 		} else { | ||||||
| 			s, _ := doCopyMode(&conn, pathIsDest, fileArgs, rec) // nolint: errcheck,gosec | 			//=== (.. or file copy) | ||||||
|  | 			s, _ := doCopyMode(&conn, pathIsDest, fileArgs, copyQuiet, copyLimitBPS, rec) // nolint: errcheck,gosec | ||||||
| 			rec.SetStatus(s) | 			rec.SetStatus(s) | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if rec.Status() != 0 { | 		if rec.Status() != 0 { | ||||||
| 			_ = hkexsh.Restore(int(os.Stdin.Fd()), oldState)                     // nolint: errcheck,gosec | 			restoreTermState(oldState) | ||||||
| 			fmt.Fprintln(os.Stderr, "Session exited with status:", rec.Status()) // nolint: errcheck | 			fmt.Fprintln(os.Stderr, "Session exited with status:", rec.Status()) // nolint: errcheck | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if oldState != nil { | 	if oldState != nil { | ||||||
| 		_ = hkexsh.Restore(int(os.Stdin.Fd()), oldState) // nolint: gosec | 		restoreTermState(oldState) | ||||||
|  | 		oldState = nil | ||||||
| 	} | 	} | ||||||
| 	os.Exit(int(rec.Status())) | 
 | ||||||
|  | 	//=== 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) // nolint: errcheck,gosec | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // exitWithStatus wraps os.Exit() plus does any required pprof housekeeping | ||||||
|  | func exitWithStatus(status int) { | ||||||
|  | 	if cpuprofile != "" { | ||||||
|  | 		pprof.StopCPUProfile() | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if memprofile != "" { | ||||||
|  | 		f, err := os.Create(memprofile) | ||||||
|  | 		if err != nil { | ||||||
|  | 			log.Fatal("could not create memory profile: ", err) | ||||||
|  | 		} | ||||||
|  | 		defer f.Close() | ||||||
|  | 		runtime.GC() // get up-to-date statistics | ||||||
|  | 		if err := pprof.WriteHeapProfile(f); err != nil { | ||||||
|  | 			log.Fatal("could not write memory profile: ", err) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	os.Exit(status) | ||||||
| } | } | ||||||
							
								
								
									
										
											BIN
										
									
								
								xs/xs_seq.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 67 KiB | 
|  | @ -1,12 +1,12 @@ | ||||||
| #!/sbin/openrc-run | #!/sbin/openrc-run | ||||||
| 
 | 
 | ||||||
| SVCNAME=hkexshd | SVCNAME=xsd | ||||||
| HKEXSHD_PIDFILE=/var/run/hkexshd.pid | XSD_PIDFILE=/var/run/xsd.pid | ||||||
| HKEXSHD_USER=root | XSD_USER=root | ||||||
| HKEXSHD_HOME=/var/run | XSD_HOME=/var/run | ||||||
| INST_PREFIX=/usr/local | INST_PREFIX=/usr/local | ||||||
| COMMAND=$INST_PREFIX/sbin/hkexshd | COMMAND=$INST_PREFIX/sbin/xsd | ||||||
| ARGS="" | ARGS="-L" | ||||||
| 
 | 
 | ||||||
| depend() { | depend() { | ||||||
|     need net |     need net | ||||||
|  | @ -25,15 +25,15 @@ start() { | ||||||
| 
 | 
 | ||||||
|     ebegin "Starting ${SVCNAME}" |     ebegin "Starting ${SVCNAME}" | ||||||
|     start-stop-daemon \ |     start-stop-daemon \ | ||||||
|         -d ${HKEXSHD_HOME} \ |         -d ${XSD_HOME} \ | ||||||
|         --make-pidfile --pidfile ${HKEXSHD_PIDFILE} \ |         --make-pidfile --pidfile ${XSD_PIDFILE} \ | ||||||
|         --start --quiet --background \ |         --start --quiet --background \ | ||||||
|         --exec "${COMMAND}" "${ARGS}" |         --exec "${COMMAND}" -- "${ARGS}" | ||||||
|     eend $? |     eend $? | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| stop() { | stop() { | ||||||
|     ebegin "Stopping ${SVCNAME}" |     ebegin "Stopping ${SVCNAME}" | ||||||
|     start-stop-daemon --stop --quiet --pidfile $HKEXSHD_PIDFILE |     start-stop-daemon --stop --quiet --pidfile $XSD_PIDFILE | ||||||
|     eend $? |     eend $? | ||||||
| } | } | ||||||
							
								
								
									
										162
									
								
								xsd.sysvrc
									
										
									
									
									
										Executable file
									
								
							
							
						
						|  | @ -0,0 +1,162 @@ | ||||||
|  | #! /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 | ||||||
|  | 
 | ||||||
|  | # /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 | ||||||
|  | @ -4,15 +4,15 @@ EXTPKGS = binary,bytes,crypto,encoding,errors,flag,fmt,internal,io,log,net,os,pa | ||||||
| EXE = $(notdir $(shell pwd)) | EXE = $(notdir $(shell pwd)) | ||||||
| 
 | 
 | ||||||
| all: | all: | ||||||
| 	go build . | 	go build $(BUILDOPTS) . | ||||||
| 
 | 
 | ||||||
| clean: | clean: | ||||||
| 	$(RM) $(EXE) $(EXE).exe | 	$(RM) $(EXE) $(EXE).exe | ||||||
| 
 | 
 | ||||||
| vis: | vis: | ||||||
| 	go-callvis -file hkexshd-vis -format png -ignore $(EXTPKGS) -group pkg,type . | 	go-callvis -file xsd-vis -format png -ignore $(EXTPKGS) -group pkg,type . | ||||||
| 	../fixup-gv.sh hkexshd.go && cat hkexshd-vis.gv | dot -Tpng -ohkexshd-vis-fixedup.png | 	../fixup-gv.sh xsd.go && cat xsd-vis.gv | dot -Tpng -oxsd-vis-fixedup.png | ||||||
| 
 | 
 | ||||||
| lint: | lint: | ||||||
| 	-gometalinter --deadline=60s | sort | 	-golangci-lint run | ||||||
| 
 | 
 | ||||||
							
								
								
									
										
											BIN
										
									
								
								xsd/hkexshd-vis.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 581 KiB | 
							
								
								
									
										
											BIN
										
									
								
								xsd/xsd-vis-fixedup.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 782 KiB | 
							
								
								
									
										347
									
								
								xsd/xsd-vis.gv
									
										
									
									
									
										Executable file
									
								
							
							
						
						|  | @ -0,0 +1,347 @@ | ||||||
|  | digraph gocallvis { | ||||||
|  |     label="blitter.com/go/xs/xsd"; | ||||||
|  |     labeljust="l"; | ||||||
|  |     fontname="Arial"; | ||||||
|  |     fontsize="14"; | ||||||
|  |     rankdir="LR"; | ||||||
|  |     bgcolor="lightgray"; | ||||||
|  |     style="solid"; | ||||||
|  |     penwidth="0.5"; | ||||||
|  |     pad="0.0"; | ||||||
|  |     nodesep="0.35"; | ||||||
|  | 
 | ||||||
|  |     node [shape="ellipse" style="filled" fillcolor="honeydew" fontname="Verdana" penwidth="1.0" margin="0.05,0.0"]; | ||||||
|  |     edge [minlen="2"] | ||||||
|  | 
 | ||||||
|  |     subgraph "cluster_focus" { | ||||||
|  |         label="main"; | ||||||
|  | labelloc="t"; | ||||||
|  | labeljust="c"; | ||||||
|  | fontsize="18"; | ||||||
|  | bgcolor="#e6ecfa"; | ||||||
|  |          | ||||||
|  |         "blitter.com/go/xs/xsd.main$2" [ style="dotted,filled" fillcolor="lightblue" label="main$2" ] | ||||||
|  |         "blitter.com/go/xs/xsd.GenAuthToken" [ fillcolor="lightblue" label="GenAuthToken" penwidth="1.5" ] | ||||||
|  |         "blitter.com/go/xs/xsd.runShellAs" [ fillcolor="lightblue" label="runShellAs" penwidth="0.5" ] | ||||||
|  |         "blitter.com/go/xs/xsd.runShellAs$1" [ fillcolor="lightblue" label="deferPtmxClose" style="dotted,filled" ] | ||||||
|  |         "blitter.com/go/xs/xsd.ptsName" [ label="ptsName" penwidth="0.5" fillcolor="lightblue" ] | ||||||
|  |         "blitter.com/go/xs/xsd.ioctl" [ fillcolor="lightblue" label="ioctl" penwidth="0.5" ] | ||||||
|  |         "blitter.com/go/xs/xsd.runShellAs$2" [ fillcolor="lightblue" label="termResizeWatcher" style="dotted,filled" ] | ||||||
|  |         "blitter.com/go/xs/xsd.runShellAs$3" [ style="dotted,filled" fillcolor="lightblue" label="stdinToPtyWorker" ] | ||||||
|  |         "blitter.com/go/xs/xsd.runShellAs$4" [ style="dotted,filled" fillcolor="lightblue" label="deferChaffShutdown" ] | ||||||
|  |         "blitter.com/go/xs/xsd.runShellAs$5" [ fillcolor="lightblue" label="ptyToStdoutWorker" style="dotted,filled" ] | ||||||
|  |         "blitter.com/go/xs/xsd.runShellAs$6" [ fillcolor="lightblue" label="runShellAs$6" style="dotted,filled" ] | ||||||
|  |         "blitter.com/go/xs/xsd.runClientToServerCopyAs" [ fillcolor="lightblue" label="runClientToServerCopyAs" penwidth="0.5" ] | ||||||
|  |         "blitter.com/go/xs/xsd.runServerToClientCopyAs" [ fillcolor="lightblue" label="runServerToClientCopyAs" penwidth="0.5" ] | ||||||
|  |         "blitter.com/go/xs/xsd.main" [ fillcolor="lightblue" label="main" penwidth="0.5" ] | ||||||
|  |         "blitter.com/go/xs/xsd.main$1" [ fillcolor="lightblue" label="main$1" style="dotted,filled" ] | ||||||
|  |          | ||||||
|  |         subgraph "cluster_blitter.com/go/goutmp" { | ||||||
|  |         URL="/?f=blitter.com/go/goutmp"; | ||||||
|  | tooltip="package: blitter.com/go/goutmp"; | ||||||
|  | fontsize="16"; | ||||||
|  | style="filled"; | ||||||
|  | fillcolor="lightyellow"; | ||||||
|  | fontname="bold"; | ||||||
|  | rank="sink"; | ||||||
|  | label="[goutmp]"; | ||||||
|  | penwidth="0.8"; | ||||||
|  |          | ||||||
|  |         "blitter.com/go/goutmp.GetHost" [ fillcolor="moccasin" label="GetHost" penwidth="1.5" ] | ||||||
|  |         "blitter.com/go/goutmp.Put_utmp" [ fillcolor="moccasin" label="Put_utmp" penwidth="1.5" ] | ||||||
|  |         "blitter.com/go/goutmp.Unput_utmp" [ label="Unput_utmp" penwidth="1.5" fillcolor="moccasin" ] | ||||||
|  |         "blitter.com/go/goutmp.Put_lastlog_entry" [ fillcolor="moccasin" label="Put_lastlog_entry" penwidth="1.5" ] | ||||||
|  |          | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |         subgraph "cluster_blitter.com/go/xs" { | ||||||
|  |         fontsize="16"; | ||||||
|  | fontname="bold"; | ||||||
|  | rank="sink"; | ||||||
|  | penwidth="0.8"; | ||||||
|  | fillcolor="lightyellow"; | ||||||
|  | label="[xs]"; | ||||||
|  | URL="/?f=blitter.com/go/xs"; | ||||||
|  | tooltip="package: blitter.com/go/xs"; | ||||||
|  | style="filled"; | ||||||
|  |          | ||||||
|  |         "blitter.com/go/xs.AuthUserByToken" [ fillcolor="moccasin" label="AuthUserByToken" penwidth="1.5" ] | ||||||
|  |         "blitter.com/go/xs.AuthUserByPasswd" [ label="AuthUserByPasswd" penwidth="1.5" fillcolor="moccasin" ] | ||||||
|  |          | ||||||
|  |         subgraph "cluster_*blitter.com/go/xs.Session" { | ||||||
|  |         tooltip="type: *blitter.com/go/xs.Session"; | ||||||
|  | penwidth="0.5"; | ||||||
|  | fontsize="15"; | ||||||
|  | fontcolor="#222222"; | ||||||
|  | labelloc="b"; | ||||||
|  | style="rounded,filled"; | ||||||
|  | fillcolor="wheat2"; | ||||||
|  | label="(*Session)"; | ||||||
|  |          | ||||||
|  |         "(*blitter.com/go/xs.Session).SetOp" [ fillcolor="moccasin" label="SetOp" penwidth="1.5" ] | ||||||
|  |         "(*blitter.com/go/xs.Session).SetWho" [ penwidth="1.5" fillcolor="moccasin" label="SetWho" ] | ||||||
|  |         "(*blitter.com/go/xs.Session).SetConnHost" [ penwidth="1.5" fillcolor="moccasin" label="SetConnHost" ] | ||||||
|  |         "(*blitter.com/go/xs.Session).SetTermType" [ fillcolor="moccasin" label="SetTermType" penwidth="1.5" ] | ||||||
|  |         "(*blitter.com/go/xs.Session).SetCmd" [ fillcolor="moccasin" label="SetCmd" penwidth="1.5" ] | ||||||
|  |         "(*blitter.com/go/xs.Session).SetAuthCookie" [ fillcolor="moccasin" label="SetAuthCookie" penwidth="1.5" ] | ||||||
|  |         "(*blitter.com/go/xs.Session).ClearAuthCookie" [ fillcolor="moccasin" label="ClearAuthCookie" penwidth="1.5" ] | ||||||
|  |          | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |         subgraph "cluster_blitter.com/go/xs.Session" { | ||||||
|  |         fillcolor="wheat2"; | ||||||
|  | label="(Session)"; | ||||||
|  | tooltip="type: blitter.com/go/xs.Session"; | ||||||
|  | penwidth="0.5"; | ||||||
|  | fontsize="15"; | ||||||
|  | fontcolor="#222222"; | ||||||
|  | labelloc="b"; | ||||||
|  | style="rounded,filled"; | ||||||
|  |          | ||||||
|  |         "(blitter.com/go/xs.Session).Op" [ fillcolor="moccasin" label="Op" penwidth="1.5" ] | ||||||
|  |         "(blitter.com/go/xs.Session).Who" [ fillcolor="moccasin" label="Who" penwidth="1.5" ] | ||||||
|  |         "(blitter.com/go/xs.Session).ConnHost" [ penwidth="1.5" fillcolor="moccasin" label="ConnHost" ] | ||||||
|  |         "(blitter.com/go/xs.Session).Cmd" [ penwidth="1.5" fillcolor="moccasin" label="Cmd" ] | ||||||
|  |         "(blitter.com/go/xs.Session).AuthCookie" [ label="AuthCookie" penwidth="1.5" fillcolor="moccasin" ] | ||||||
|  |         "(blitter.com/go/xs.Session).TermType" [ fillcolor="moccasin" label="TermType" penwidth="1.5" ] | ||||||
|  |          | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |         subgraph "cluster_blitter.com/go/xs/logger" { | ||||||
|  |         penwidth="0.8"; | ||||||
|  | fontsize="16"; | ||||||
|  | style="filled"; | ||||||
|  | fontname="bold"; | ||||||
|  | URL="/?f=blitter.com/go/xs/logger"; | ||||||
|  | fillcolor="lightyellow"; | ||||||
|  | rank="sink"; | ||||||
|  | label="[logger]"; | ||||||
|  | tooltip="package: blitter.com/go/xs/logger"; | ||||||
|  |          | ||||||
|  |         "blitter.com/go/xs/logger.LogNotice" [ fillcolor="moccasin" label="LogNotice" penwidth="1.5" ] | ||||||
|  |         "blitter.com/go/xs/logger.LogDebug" [ fillcolor="moccasin" label="LogDebug" penwidth="1.5" ] | ||||||
|  |         "blitter.com/go/xs/logger.LogErr" [ fillcolor="moccasin" label="LogErr" penwidth="1.5" ] | ||||||
|  |         "blitter.com/go/xs/logger.New" [ label="New" penwidth="1.5" fillcolor="moccasin" ] | ||||||
|  |          | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |         subgraph "cluster_blitter.com/go/xs/xsnet" { | ||||||
|  |         penwidth="0.8"; | ||||||
|  | label="[xsnet]"; | ||||||
|  | fillcolor="lightyellow"; | ||||||
|  | fontname="bold"; | ||||||
|  | rank="sink"; | ||||||
|  | URL="/?f=blitter.com/go/xs/xsnet"; | ||||||
|  | tooltip="package: blitter.com/go/xs/xsnet"; | ||||||
|  | fontsize="16"; | ||||||
|  | style="filled"; | ||||||
|  |          | ||||||
|  |         "blitter.com/go/xs/xsnet.Init" [ fillcolor="moccasin" label="Init" penwidth="1.5" ] | ||||||
|  |         "blitter.com/go/xs/xsnet.Listen" [ fillcolor="moccasin" label="Listen" penwidth="1.5" ] | ||||||
|  |          | ||||||
|  |         subgraph "cluster_*blitter.com/go/xs/xsnet.Conn" { | ||||||
|  |         label="(*Conn)"; | ||||||
|  | tooltip="type: *blitter.com/go/xs/xsnet.Conn"; | ||||||
|  | penwidth="0.5"; | ||||||
|  | fontsize="15"; | ||||||
|  | fontcolor="#222222"; | ||||||
|  | labelloc="b"; | ||||||
|  | style="rounded,filled"; | ||||||
|  | fillcolor="wheat2"; | ||||||
|  |          | ||||||
|  |         "(*blitter.com/go/xs/xsnet.Conn).Close" [ fillcolor="moccasin" label="Close" penwidth="1.5" ] | ||||||
|  |         "(*blitter.com/go/xs/xsnet.Conn).RemoteAddr" [ fillcolor="moccasin" label="RemoteAddr" penwidth="1.5" ] | ||||||
|  |         "(*blitter.com/go/xs/xsnet.Conn).EnableChaff" [ fillcolor="moccasin" label="EnableChaff" penwidth="1.5" ] | ||||||
|  |         "(*blitter.com/go/xs/xsnet.Conn).DisableChaff" [ label="DisableChaff" penwidth="1.5" fillcolor="moccasin" ] | ||||||
|  |         "(*blitter.com/go/xs/xsnet.Conn).ShutdownChaff" [ fillcolor="moccasin" label="ShutdownChaff" penwidth="1.5" ] | ||||||
|  |         "(*blitter.com/go/xs/xsnet.Conn).SetStatus" [ fillcolor="moccasin" label="SetStatus" penwidth="1.5" ] | ||||||
|  |         "(*blitter.com/go/xs/xsnet.Conn).WritePacket" [ fillcolor="moccasin" label="WritePacket" penwidth="1.5" ] | ||||||
|  |         "(*blitter.com/go/xs/xsnet.Conn).SetupChaff" [ penwidth="1.5" fillcolor="moccasin" label="SetupChaff" ] | ||||||
|  |          | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |         subgraph "cluster_*blitter.com/go/xs/xsnet.HKExListener" { | ||||||
|  |         labelloc="b"; | ||||||
|  | style="rounded,filled"; | ||||||
|  | fillcolor="wheat2"; | ||||||
|  | label="(*HKExListener)"; | ||||||
|  | tooltip="type: *blitter.com/go/xs/xsnet.HKExListener"; | ||||||
|  | penwidth="0.5"; | ||||||
|  | fontsize="15"; | ||||||
|  | fontcolor="#222222"; | ||||||
|  |          | ||||||
|  |         "(*blitter.com/go/xs/xsnet.HKExListener).Accept" [ penwidth="1.5" fillcolor="moccasin" label="Accept" ] | ||||||
|  |          | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |         subgraph "cluster_blitter.com/go/xs/xsnet.Conn" { | ||||||
|  |         tooltip="type: blitter.com/go/xs/xsnet.Conn"; | ||||||
|  | penwidth="0.5"; | ||||||
|  | fontsize="15"; | ||||||
|  | fontcolor="#222222"; | ||||||
|  | labelloc="b"; | ||||||
|  | style="rounded,filled"; | ||||||
|  | fillcolor="wheat2"; | ||||||
|  | label="(Conn)"; | ||||||
|  |          | ||||||
|  |         "(blitter.com/go/xs/xsnet.Conn).Write" [ fillcolor="moccasin" label="Write" penwidth="1.5" ] | ||||||
|  |          | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |         subgraph "cluster_blitter.com/go/xs/xsnet.HKExListener" { | ||||||
|  |         style="rounded,filled"; | ||||||
|  | fillcolor="wheat2"; | ||||||
|  | label="(HKExListener)"; | ||||||
|  | tooltip="type: blitter.com/go/xs/xsnet.HKExListener"; | ||||||
|  | penwidth="0.5"; | ||||||
|  | fontsize="15"; | ||||||
|  | fontcolor="#222222"; | ||||||
|  | labelloc="b"; | ||||||
|  |          | ||||||
|  |         "(blitter.com/go/xs/xsnet.HKExListener).Close" [ label="Close" penwidth="1.5" fillcolor="moccasin" ] | ||||||
|  |          | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |         subgraph "cluster_context" { | ||||||
|  |         fontsize="16"; | ||||||
|  | fillcolor="#E0FFE1"; | ||||||
|  | label="[context]"; | ||||||
|  | tooltip="package: context"; | ||||||
|  | penwidth="0.8"; | ||||||
|  | style="filled"; | ||||||
|  | fontname="bold"; | ||||||
|  | rank="sink"; | ||||||
|  | URL="/?f=context"; | ||||||
|  |          | ||||||
|  |          | ||||||
|  |         subgraph "cluster_context.deadlineExceededError" { | ||||||
|  |         labelloc="b"; | ||||||
|  | style="rounded,filled"; | ||||||
|  | fillcolor="#c2e3c2"; | ||||||
|  | label="(deadlineExceededError)"; | ||||||
|  | tooltip="type: context.deadlineExceededError"; | ||||||
|  | penwidth="0.5"; | ||||||
|  | fontsize="15"; | ||||||
|  | fontcolor="#222222"; | ||||||
|  |          | ||||||
|  |         "(context.deadlineExceededError).Error" [ fillcolor="#adedad" label="Error" penwidth="1.5" ] | ||||||
|  |          | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |         subgraph "cluster_github.com/kr/pty" { | ||||||
|  |         label="[pty]"; | ||||||
|  | URL="/?f=github.com/kr/pty"; | ||||||
|  | penwidth="0.8"; | ||||||
|  | style="filled"; | ||||||
|  | fillcolor="lightyellow"; | ||||||
|  | rank="sink"; | ||||||
|  | fontsize="16"; | ||||||
|  | fontname="bold"; | ||||||
|  | tooltip="package: github.com/kr/pty"; | ||||||
|  |          | ||||||
|  |         "github.com/kr/pty.Start" [ fillcolor="moccasin" label="Start" penwidth="1.5" ] | ||||||
|  |         "github.com/kr/pty.Setsize" [ label="Setsize" penwidth="1.5" fillcolor="moccasin" ] | ||||||
|  |          | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |         subgraph "cluster_github.com/pkg/errors" { | ||||||
|  |         fontname="bold"; | ||||||
|  | label="[errors]"; | ||||||
|  | style="filled"; | ||||||
|  | fontsize="16"; | ||||||
|  | fillcolor="lightyellow"; | ||||||
|  | rank="sink"; | ||||||
|  | URL="/?f=github.com/pkg/errors"; | ||||||
|  | tooltip="package: github.com/pkg/errors"; | ||||||
|  | penwidth="0.8"; | ||||||
|  |          | ||||||
|  |          | ||||||
|  |         subgraph "cluster_*github.com/pkg/errors.fundamental" { | ||||||
|  |         fontcolor="#222222"; | ||||||
|  | labelloc="b"; | ||||||
|  | style="rounded,filled"; | ||||||
|  | fillcolor="wheat2"; | ||||||
|  | label="(*fundamental)"; | ||||||
|  | tooltip="type: *github.com/pkg/errors.fundamental"; | ||||||
|  | penwidth="0.5"; | ||||||
|  | fontsize="15"; | ||||||
|  |          | ||||||
|  |         "(*github.com/pkg/errors.fundamental).Error" [ label="Error" penwidth="1.5" fillcolor="moccasin" ] | ||||||
|  |          | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     "blitter.com/go/xs/xsd.main$2" -> "(*blitter.com/go/xs/xsnet.Conn).Close" [ arrowhead="normalnoneodiamond" color="saddlebrown" ] | ||||||
|  |     "blitter.com/go/xs/xsd.main$2" -> "(*blitter.com/go/xs.Session).SetOp" [ color="saddlebrown" ] | ||||||
|  |     "blitter.com/go/xs/xsd.main$2" -> "(*blitter.com/go/xs.Session).SetWho" [ color="saddlebrown" ] | ||||||
|  |     "blitter.com/go/xs/xsd.main$2" -> "(*blitter.com/go/xs.Session).SetConnHost" [ color="saddlebrown" ] | ||||||
|  |     "blitter.com/go/xs/xsd.main$2" -> "(*blitter.com/go/xs.Session).SetTermType" [ color="saddlebrown" ] | ||||||
|  |     "blitter.com/go/xs/xsd.main$2" -> "(*blitter.com/go/xs.Session).SetCmd" [ color="saddlebrown" ] | ||||||
|  |     "blitter.com/go/xs/xsd.main$2" -> "(*blitter.com/go/xs.Session).SetAuthCookie" [ color="saddlebrown" ] | ||||||
|  |     "blitter.com/go/xs/xsd.main$2" -> "(blitter.com/go/xs.Session).Op" [ color="saddlebrown" ] | ||||||
|  |     "blitter.com/go/xs/xsd.main$2" -> "(blitter.com/go/xs.Session).Who" [ color="saddlebrown" ] | ||||||
|  |     "blitter.com/go/xs/xsd.main$2" -> "(blitter.com/go/xs.Session).ConnHost" [ color="saddlebrown" ] | ||||||
|  |     "blitter.com/go/xs/xsd.main$2" -> "(blitter.com/go/xs.Session).Cmd" [ color="saddlebrown" ] | ||||||
|  |     "blitter.com/go/xs/xsd.main$2" -> "(blitter.com/go/xs.Session).AuthCookie" [ color="saddlebrown" ] | ||||||
|  |     "blitter.com/go/xs/xsd.main$2" -> "blitter.com/go/xs.AuthUserByToken" [ color="saddlebrown" ] | ||||||
|  |     "blitter.com/go/xs/xsd.main$2" -> "(*blitter.com/go/xs.Session).ClearAuthCookie" [ color="saddlebrown" ] | ||||||
|  |     "blitter.com/go/xs/xsd.main$2" -> "blitter.com/go/xs.AuthUserByPasswd" [ color="saddlebrown" ] | ||||||
|  |     "blitter.com/go/xs/xsd.main$2" -> "(blitter.com/go/xs/xsnet.Conn).Write" [ color="saddlebrown" ] | ||||||
|  |     "blitter.com/go/xs/xsd.main$2" -> "blitter.com/go/xs/logger.LogNotice" [ color="saddlebrown" ] | ||||||
|  |     "blitter.com/go/xs/xsd.main$2" -> "(*blitter.com/go/xs/xsnet.Conn).RemoteAddr" [ color="saddlebrown" ] | ||||||
|  |     "blitter.com/go/xs/xsd.main$2" -> "blitter.com/go/goutmp.GetHost" [ color="saddlebrown" ] | ||||||
|  |     "blitter.com/go/xs/xsd.main$2" -> "blitter.com/go/xs/xsd.GenAuthToken" [  ] | ||||||
|  |     "blitter.com/go/xs/xsd.main$2" -> "(blitter.com/go/xs.Session).TermType" [ color="saddlebrown" ] | ||||||
|  |     "blitter.com/go/xs/xsd.runShellAs" -> "github.com/kr/pty.Start" [ color="saddlebrown" ] | ||||||
|  |     "blitter.com/go/xs/xsd.runShellAs" -> "blitter.com/go/xs/xsd.runShellAs$1" [ arrowhead="normalnoneodiamond" ] | ||||||
|  |     "blitter.com/go/xs/xsd.ptsName" -> "blitter.com/go/xs/xsd.ioctl" [  ] | ||||||
|  |     "blitter.com/go/xs/xsd.runShellAs" -> "blitter.com/go/xs/xsd.ptsName" [  ] | ||||||
|  |     "blitter.com/go/xs/xsd.runShellAs" -> "blitter.com/go/goutmp.Put_utmp" [ color="saddlebrown" ] | ||||||
|  |     "blitter.com/go/xs/xsd.runShellAs$2" -> "blitter.com/go/goutmp.Unput_utmp" [ color="saddlebrown" ] | ||||||
|  |     "blitter.com/go/xs/xsd.runShellAs" -> "blitter.com/go/xs/xsd.runShellAs$2" [ arrowhead="normalnoneodiamond" ] | ||||||
|  |     "blitter.com/go/xs/xsd.runShellAs" -> "blitter.com/go/goutmp.Put_lastlog_entry" [ color="saddlebrown" ] | ||||||
|  |     "blitter.com/go/xs/xsd.runShellAs$3" -> "github.com/kr/pty.Setsize" [ color="saddlebrown" ] | ||||||
|  |     "blitter.com/go/xs/xsd.runShellAs" -> "blitter.com/go/xs/xsd.runShellAs$3" [ arrowhead="normalnoneodot" ] | ||||||
|  |     "blitter.com/go/xs/xsd.runShellAs$4" -> "(context.deadlineExceededError).Error" [ color="saddlebrown" style="dashed" ] | ||||||
|  |     "blitter.com/go/xs/xsd.runShellAs$4" -> "(*github.com/pkg/errors.fundamental).Error" [ style="dashed" color="saddlebrown" ] | ||||||
|  |     "blitter.com/go/xs/xsd.runShellAs" -> "blitter.com/go/xs/xsd.runShellAs$4" [ arrowhead="normalnoneodot" ] | ||||||
|  |     "blitter.com/go/xs/xsd.runShellAs" -> "(*blitter.com/go/xs/xsnet.Conn).EnableChaff" [ color="saddlebrown" ] | ||||||
|  |     "blitter.com/go/xs/xsd.runShellAs$5" -> "(*blitter.com/go/xs/xsnet.Conn).DisableChaff" [ color="saddlebrown" ] | ||||||
|  |     "blitter.com/go/xs/xsd.runShellAs$5" -> "(*blitter.com/go/xs/xsnet.Conn).ShutdownChaff" [ color="saddlebrown" ] | ||||||
|  |     "blitter.com/go/xs/xsd.runShellAs" -> "blitter.com/go/xs/xsd.runShellAs$5" [ arrowhead="normalnoneodiamond" ] | ||||||
|  |     "blitter.com/go/xs/xsd.runShellAs$6" -> "(context.deadlineExceededError).Error" [ style="dashed" color="saddlebrown" ] | ||||||
|  |     "blitter.com/go/xs/xsd.runShellAs$6" -> "(*github.com/pkg/errors.fundamental).Error" [ style="dashed" color="saddlebrown" ] | ||||||
|  |     "blitter.com/go/xs/xsd.runShellAs" -> "blitter.com/go/xs/xsd.runShellAs$6" [ arrowhead="normalnoneodot" ] | ||||||
|  |     "blitter.com/go/xs/xsd.runShellAs" -> "blitter.com/go/xs/logger.LogDebug" [ color="saddlebrown" ] | ||||||
|  |     "blitter.com/go/xs/xsd.runShellAs" -> "(*blitter.com/go/xs/xsnet.Conn).SetStatus" [ color="saddlebrown" ] | ||||||
|  |     "blitter.com/go/xs/xsd.main$2" -> "blitter.com/go/xs/xsd.runShellAs" [  ] | ||||||
|  |     "blitter.com/go/xs/xsd.main$2" -> "blitter.com/go/xs/logger.LogErr" [ color="saddlebrown" ] | ||||||
|  |     "blitter.com/go/xs/xsd.main$2" -> "(*blitter.com/go/xs/xsnet.Conn).SetStatus" [ color="saddlebrown" ] | ||||||
|  |     "blitter.com/go/xs/xsd.runClientToServerCopyAs" -> "(*blitter.com/go/xs/xsnet.Conn).EnableChaff" [ color="saddlebrown" ] | ||||||
|  |     "blitter.com/go/xs/xsd.runClientToServerCopyAs" -> "(*blitter.com/go/xs/xsnet.Conn).DisableChaff" [ arrowhead="normalnoneodiamond" color="saddlebrown" ] | ||||||
|  |     "blitter.com/go/xs/xsd.runClientToServerCopyAs" -> "(*blitter.com/go/xs/xsnet.Conn).ShutdownChaff" [ arrowhead="normalnoneodiamond" color="saddlebrown" ] | ||||||
|  |     "blitter.com/go/xs/xsd.main$2" -> "blitter.com/go/xs/xsd.runClientToServerCopyAs" [  ] | ||||||
|  |     "blitter.com/go/xs/xsd.main$2" -> "(*blitter.com/go/xs/xsnet.Conn).WritePacket" [ color="saddlebrown" ] | ||||||
|  |     "blitter.com/go/xs/xsd.runServerToClientCopyAs" -> "(*blitter.com/go/xs/xsnet.Conn).EnableChaff" [ color="saddlebrown" ] | ||||||
|  |     "blitter.com/go/xs/xsd.runServerToClientCopyAs" -> "(*blitter.com/go/xs/xsnet.Conn).DisableChaff" [ arrowhead="normalnoneodiamond" color="saddlebrown" ] | ||||||
|  |     "blitter.com/go/xs/xsd.runServerToClientCopyAs" -> "(*blitter.com/go/xs/xsnet.Conn).ShutdownChaff" [ color="saddlebrown" arrowhead="normalnoneodiamond" ] | ||||||
|  |     "blitter.com/go/xs/xsd.main$2" -> "blitter.com/go/xs/xsd.runServerToClientCopyAs" [  ] | ||||||
|  |     "blitter.com/go/xs/xsd.main" -> "blitter.com/go/xs/logger.New" [ color="saddlebrown" ] | ||||||
|  |     "blitter.com/go/xs/xsd.main" -> "blitter.com/go/xs/xsnet.Init" [ color="saddlebrown" ] | ||||||
|  |     "blitter.com/go/xs/xsd.main$1" -> "blitter.com/go/xs/logger.LogNotice" [ color="saddlebrown" ] | ||||||
|  |     "blitter.com/go/xs/xsd.main" -> "blitter.com/go/xs/xsd.main$1" [ arrowhead="normalnoneodot" ] | ||||||
|  |     "blitter.com/go/xs/xsd.main" -> "blitter.com/go/xs/xsnet.Listen" [ color="saddlebrown" ] | ||||||
|  |     "blitter.com/go/xs/xsd.main" -> "(blitter.com/go/xs/xsnet.HKExListener).Close" [ arrowhead="normalnoneodiamond" color="saddlebrown" ] | ||||||
|  |     "blitter.com/go/xs/xsd.main" -> "(*blitter.com/go/xs/xsnet.HKExListener).Accept" [ color="saddlebrown" ] | ||||||
|  |     "blitter.com/go/xs/xsd.main" -> "(*blitter.com/go/xs/xsnet.Conn).SetupChaff" [ color="saddlebrown" ] | ||||||
|  |     "blitter.com/go/xs/xsd.main" -> "blitter.com/go/xs/xsd.main$2" [ arrowhead="normalnoneodot" ] | ||||||
|  | } | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| // hkexshd server | // xsd server | ||||||
| // | // | ||||||
| // Copyright (c) 2017-2018 Russell Magee | // Copyright (c) 2017-2020 Russell Magee | ||||||
| // Licensed under the terms of the MIT license (see LICENSE.mit in this | // Licensed under the terms of the MIT license (see LICENSE.mit in this | ||||||
| // distribution) | // distribution) | ||||||
| // | // | ||||||
|  | @ -23,24 +23,49 @@ import ( | ||||||
| 	"os/signal" | 	"os/signal" | ||||||
| 	"os/user" | 	"os/user" | ||||||
| 	"path" | 	"path" | ||||||
|  | 	"strings" | ||||||
| 	"sync" | 	"sync" | ||||||
| 	"syscall" | 	"syscall" | ||||||
|  | 	"time" | ||||||
|  | 	"unsafe" | ||||||
| 
 | 
 | ||||||
| 	"blitter.com/go/goutmp" | 	"blitter.com/go/goutmp" | ||||||
| 	hkexsh "blitter.com/go/hkexsh" | 	xs "blitter.com/go/xs" | ||||||
| 	"blitter.com/go/hkexsh/hkexnet" | 	"blitter.com/go/xs/logger" | ||||||
| 	"blitter.com/go/hkexsh/logger" | 	"blitter.com/go/xs/xsnet" | ||||||
| 	"github.com/kr/pty" | 	"github.com/creack/pty" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| var ( | var ( | ||||||
|  | 	version   string | ||||||
|  | 	gitCommit string // set in -ldflags by build | ||||||
|  | 
 | ||||||
|  | 	useSysLogin bool | ||||||
|  | 	kcpMode     string // set to a valid KCP BlockCrypt alg tag to use rather than TCP | ||||||
|  | 
 | ||||||
| 	// Log - syslog output (with no -d) | 	// Log - syslog output (with no -d) | ||||||
| 	Log *logger.Writer | 	Log *logger.Writer | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | func ioctl(fd, request, argp uintptr) error { | ||||||
|  | 	if _, _, e := syscall.Syscall6(syscall.SYS_IOCTL, fd, request, argp, 0, 0, 0); e != 0 { | ||||||
|  | 		return e | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func ptsName(fd uintptr) (string, error) { | ||||||
|  | 	var n uintptr | ||||||
|  | 	err := ioctl(fd, syscall.TIOCGPTN, uintptr(unsafe.Pointer(&n))) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", err | ||||||
|  | 	} | ||||||
|  | 	return fmt.Sprintf("/dev/pts/%d", n), nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /* -------------------------------------------------------------- */ | /* -------------------------------------------------------------- */ | ||||||
| // Perform a client->server copy | // Perform a client->server copy | ||||||
| func runClientToServerCopyAs(who, ttype string, conn *hkexnet.Conn, fpath string, chaffing bool) (exitStatus uint32, err error) { | func runClientToServerCopyAs(who, ttype string, conn *xsnet.Conn, fpath string, chaffing bool) (exitStatus uint32, err error) { | ||||||
| 	u, _ := user.Lookup(who) // nolint: gosec | 	u, _ := user.Lookup(who) // nolint: gosec | ||||||
| 	var uid, gid uint32 | 	var uid, gid uint32 | ||||||
| 	fmt.Sscanf(u.Uid, "%d", &uid) // nolint: gosec,errcheck | 	fmt.Sscanf(u.Uid, "%d", &uid) // nolint: gosec,errcheck | ||||||
|  | @ -57,10 +82,10 @@ func runClientToServerCopyAs(who, ttype string, conn *hkexnet.Conn, fpath string | ||||||
| 	os.Clearenv() | 	os.Clearenv() | ||||||
| 	os.Setenv("HOME", u.HomeDir) // nolint: gosec,errcheck | 	os.Setenv("HOME", u.HomeDir) // nolint: gosec,errcheck | ||||||
| 	os.Setenv("TERM", ttype)     // nolint: gosec,errcheck | 	os.Setenv("TERM", ttype)     // nolint: gosec,errcheck | ||||||
| 	os.Setenv("HKEXSH", "1")     // nolint: gosec,errcheck | 	os.Setenv("XS_SESSION", "1")     // nolint: gosec,errcheck | ||||||
| 
 | 
 | ||||||
| 	var c *exec.Cmd | 	var c *exec.Cmd | ||||||
| 	cmdName := "/bin/tar" | 	cmdName := xs.GetTool("tar") | ||||||
| 
 | 
 | ||||||
| 	var destDir string | 	var destDir string | ||||||
| 	if path.IsAbs(fpath) { | 	if path.IsAbs(fpath) { | ||||||
|  | @ -75,6 +100,7 @@ func runClientToServerCopyAs(who, ttype string, conn *hkexnet.Conn, fpath string | ||||||
| 	// When args are passed in exec() format, no quoting is required | 	// When args are passed in exec() format, no quoting is required | ||||||
| 	// (as this isn't input from a shell) (right? -rlm 20180823) | 	// (as this isn't input from a shell) (right? -rlm 20180823) | ||||||
| 	//cmdArgs := []string{"-x", "-C", destDir, `--xform=s#.*/\(.*\)#\1#`} | 	//cmdArgs := []string{"-x", "-C", destDir, `--xform=s#.*/\(.*\)#\1#`} | ||||||
|  | 	fmt.Println(cmdName, cmdArgs) | ||||||
| 	c = exec.Command(cmdName, cmdArgs...) // nolint: gosec | 	c = exec.Command(cmdName, cmdArgs...) // nolint: gosec | ||||||
| 
 | 
 | ||||||
| 	c.Dir = destDir | 	c.Dir = destDir | ||||||
|  | @ -116,7 +142,7 @@ func runClientToServerCopyAs(who, ttype string, conn *hkexnet.Conn, fpath string | ||||||
| 		log.Println("cmd exited immediately. Cannot get cmd.Wait().ExitStatus()") | 		log.Println("cmd exited immediately. Cannot get cmd.Wait().ExitStatus()") | ||||||
| 		err = errors.New("cmd exited prematurely") | 		err = errors.New("cmd exited prematurely") | ||||||
| 		//exitStatus = uint32(254) | 		//exitStatus = uint32(254) | ||||||
| 		exitStatus = hkexnet.CSEExecFail | 		exitStatus = xsnet.CSEExecFail | ||||||
| 	} else { | 	} else { | ||||||
| 		if err := c.Wait(); err != nil { | 		if err := c.Wait(); err != nil { | ||||||
| 			//fmt.Println("*** c.Wait() done ***") | 			//fmt.Println("*** c.Wait() done ***") | ||||||
|  | @ -140,7 +166,7 @@ func runClientToServerCopyAs(who, ttype string, conn *hkexnet.Conn, fpath string | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Perform a server->client copy | // Perform a server->client copy | ||||||
| func runServerToClientCopyAs(who, ttype string, conn *hkexnet.Conn, srcPath string, chaffing bool) (exitStatus uint32, err error) { | func runServerToClientCopyAs(who, ttype string, conn *xsnet.Conn, srcPath string, chaffing bool) (exitStatus uint32, err error) { | ||||||
| 	u, err := user.Lookup(who) | 	u, err := user.Lookup(who) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		exitStatus = 1 | 		exitStatus = 1 | ||||||
|  | @ -161,10 +187,10 @@ func runServerToClientCopyAs(who, ttype string, conn *hkexnet.Conn, srcPath stri | ||||||
| 	os.Clearenv() | 	os.Clearenv() | ||||||
| 	_ = os.Setenv("HOME", u.HomeDir) // nolint: gosec | 	_ = os.Setenv("HOME", u.HomeDir) // nolint: gosec | ||||||
| 	_ = os.Setenv("TERM", ttype)     // nolint: gosec | 	_ = os.Setenv("TERM", ttype)     // nolint: gosec | ||||||
| 	_ = os.Setenv("HKEXSH", "1")     // nolint: gosec | 	_ = os.Setenv("XS_SESSION", "1")     // nolint: gosec | ||||||
| 
 | 
 | ||||||
| 	var c *exec.Cmd | 	var c *exec.Cmd | ||||||
| 	cmdName := "/bin/tar" | 	cmdName := xs.GetTool("tar") | ||||||
| 	if !path.IsAbs(srcPath) { | 	if !path.IsAbs(srcPath) { | ||||||
| 		srcPath = fmt.Sprintf("%s%c%s", u.HomeDir, os.PathSeparator, srcPath) | 		srcPath = fmt.Sprintf("%s%c%s", u.HomeDir, os.PathSeparator, srcPath) | ||||||
| 	} | 	} | ||||||
|  | @ -202,7 +228,7 @@ func runServerToClientCopyAs(who, ttype string, conn *hkexnet.Conn, srcPath stri | ||||||
| 	err = c.Start() // returns immediately | 	err = c.Start() // returns immediately | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Printf("Command finished with error: %v", err) | 		log.Printf("Command finished with error: %v", err) | ||||||
| 		return hkexnet.CSEExecFail, err // !? | 		return xsnet.CSEExecFail, err // !? | ||||||
| 	} | 	} | ||||||
| 	if err := c.Wait(); err != nil { | 	if err := c.Wait(); err != nil { | ||||||
| 		//fmt.Println("*** c.Wait() done ***") | 		//fmt.Println("*** c.Wait() done ***") | ||||||
|  | @ -230,7 +256,7 @@ func runServerToClientCopyAs(who, ttype string, conn *hkexnet.Conn, srcPath stri | ||||||
| // | // | ||||||
| // Uses ptys to support commands which expect a terminal. | // Uses ptys to support commands which expect a terminal. | ||||||
| // nolint: gocyclo | // nolint: gocyclo | ||||||
| func runShellAs(who, ttype string, cmd string, interactive bool, conn *hkexnet.Conn, chaffing bool) (exitStatus uint32, err error) { | func runShellAs(who, hname, ttype, cmd string, interactive bool, conn *xsnet.Conn, chaffing bool) (exitStatus uint32, err error) { | ||||||
| 	var wg sync.WaitGroup | 	var wg sync.WaitGroup | ||||||
| 	u, err := user.Lookup(who) | 	u, err := user.Lookup(who) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
|  | @ -252,32 +278,59 @@ func runShellAs(who, ttype string, cmd string, interactive bool, conn *hkexnet.C | ||||||
| 	os.Clearenv() | 	os.Clearenv() | ||||||
| 	_ = os.Setenv("HOME", u.HomeDir) // nolint: gosec | 	_ = os.Setenv("HOME", u.HomeDir) // nolint: gosec | ||||||
| 	_ = os.Setenv("TERM", ttype)     // nolint: gosec | 	_ = os.Setenv("TERM", ttype)     // nolint: gosec | ||||||
| 	_ = os.Setenv("HKEXSH", "1")     // nolint: gosec | 	_ = os.Setenv("XS_SESSION", "1")     // nolint: gosec | ||||||
| 
 | 
 | ||||||
| 	var c *exec.Cmd | 	var c *exec.Cmd | ||||||
| 	if interactive { | 	if interactive { | ||||||
| 		c = exec.Command("/bin/bash", "-i", "-l") // nolint: gosec | 		if useSysLogin { | ||||||
|  | 			// Use the server's login binary (post-auth | ||||||
|  | 			// which is still done via our own bcrypt file) | ||||||
|  | 			// Things UNIX login does, like print the 'motd', | ||||||
|  | 			// and use the shell specified by /etc/passwd, will be done | ||||||
|  | 			// automagically, at the cost of another external tool | ||||||
|  | 			// dependency. | ||||||
|  | 			// | ||||||
|  | 			c = exec.Command(xs.GetTool("login"), "-f", "-p", who) // nolint: gosec | ||||||
| 		} else { | 		} else { | ||||||
| 		c = exec.Command("/bin/bash", "-c", cmd) // nolint: gosec | 			c = exec.Command(xs.GetTool("bash"), "-i", "-l") // nolint: gosec | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		c = exec.Command(xs.GetTool("bash"), "-c", cmd) // nolint: gosec | ||||||
| 	} | 	} | ||||||
| 	//If os.Clearenv() isn't called by server above these will be seen in the | 	//If os.Clearenv() isn't called by server above these will be seen in the | ||||||
| 	//client's session env. | 	//client's session env. | ||||||
| 	//c.Env = []string{"HOME=" + u.HomeDir, "SUDO_GID=", "SUDO_UID=", "SUDO_USER=", "SUDO_COMMAND=", "MAIL=", "LOGNAME="+who} | 	//c.Env = []string{"HOME=" + u.HomeDir, "SUDO_GID=", "SUDO_UID=", "SUDO_USER=", "SUDO_COMMAND=", "MAIL=", "LOGNAME="+who} | ||||||
| 	c.Dir = u.HomeDir | 	c.Dir = u.HomeDir | ||||||
| 	c.SysProcAttr = &syscall.SysProcAttr{} | 	c.SysProcAttr = &syscall.SysProcAttr{} | ||||||
|  | 	if useSysLogin { | ||||||
|  | 		// If using server's login binary, drop to user creds | ||||||
|  | 		// is taken care of by it. | ||||||
|  | 		c.SysProcAttr.Credential = &syscall.Credential{} | ||||||
|  | 	} else { | ||||||
| 		c.SysProcAttr.Credential = &syscall.Credential{Uid: uid, Gid: gid} | 		c.SysProcAttr.Credential = &syscall.Credential{Uid: uid, Gid: gid} | ||||||
| 	c.Stdin = conn | 	} | ||||||
| 	c.Stdout = conn |  | ||||||
| 	c.Stderr = conn |  | ||||||
| 
 | 
 | ||||||
| 	// Start the command with a pty. | 	// Start the command with a pty. | ||||||
| 	ptmx, err := pty.Start(c) // returns immediately with ptmx file | 	ptmx, err := pty.Start(c) // returns immediately with ptmx file | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return hkexnet.CSEPtyExecFail, err | 		log.Println(err) | ||||||
|  | 		return xsnet.CSEPtyExecFail, err | ||||||
| 	} | 	} | ||||||
| 	// Make sure to close the pty at the end. | 	// Make sure to close the pty at the end. | ||||||
| 	// #gv:s/label=\"runShellAs\$1\"/label=\"deferPtmxClose\"/ | 	// #gv:s/label=\"runShellAs\$1\"/label=\"deferPtmxClose\"/ | ||||||
| 	defer func() { _ = ptmx.Close() }() // nolint: gosec | 	defer func() { | ||||||
|  | 		//logger.LogDebug(fmt.Sprintf("[Exited process was %d]", c.Process.Pid)) | ||||||
|  | 		_ = ptmx.Close() | ||||||
|  | 	}() // nolint: gosec | ||||||
|  | 
 | ||||||
|  | 	// get pty info for system accounting (who, lastlog) | ||||||
|  | 	pts, pe := ptsName(ptmx.Fd()) | ||||||
|  | 	if pe != nil { | ||||||
|  | 		return xsnet.CSEPtyGetNameFail, err | ||||||
|  | 	} | ||||||
|  | 	utmpx := goutmp.Put_utmp(who, pts, hname) | ||||||
|  | 	defer func() { goutmp.Unput_utmp(utmpx) }() | ||||||
|  | 	goutmp.Put_lastlog_entry("xs", who, pts, hname) | ||||||
| 
 | 
 | ||||||
| 	log.Printf("[%s]\n", cmd) | 	log.Printf("[%s]\n", cmd) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
|  | @ -347,7 +400,15 @@ func runShellAs(who, ttype string, cmd string, interactive bool, conn *hkexnet.C | ||||||
| 					log.Printf("Exit Status: %d", exitStatus) | 					log.Printf("Exit Status: %d", exitStatus) | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 			conn.SetStatus(hkexnet.CSOType(exitStatus)) | 			conn.SetStatus(xsnet.CSOType(exitStatus)) | ||||||
|  | 		} else { | ||||||
|  | 			logger.LogDebug("*** Main proc has exited. ***") | ||||||
|  | 			// Background jobs still may be running; close the | ||||||
|  | 			// pty anyway, so the client can return before | ||||||
|  | 			// wg.Wait() below completes (Issue #18) | ||||||
|  | 			if interactive { | ||||||
|  | 				_ = ptmx.Close() | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
| 		wg.Wait() // Wait on pty->stdout completion to client | 		wg.Wait() // Wait on pty->stdout completion to client | ||||||
| 	} | 	} | ||||||
|  | @ -368,15 +429,79 @@ func GenAuthToken(who string, connhost string) string { | ||||||
| 	return fmt.Sprintf("%s:%s", tokenA, hex.EncodeToString(tokenB)) | 	return fmt.Sprintf("%s:%s", tokenA, hex.EncodeToString(tokenB)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Demo of a simple server that listens and spawns goroutines for each | var ( | ||||||
| // connecting client. Note this code is identical to standard tcp | 	aKEXAlgs    allowedKEXAlgs | ||||||
| // server code, save for declaring 'hkex' rather than 'net' | 	aCipherAlgs allowedCipherAlgs | ||||||
| // Listener and Conns. The KEx and encrypt/decrypt is done within the type. | 	aHMACAlgs   allowedHMACAlgs | ||||||
| // Compare to 'serverp.go' in this directory to see the equivalence. | ) | ||||||
|  | 
 | ||||||
|  | type allowedKEXAlgs []string    // TODO | ||||||
|  | type allowedCipherAlgs []string // TODO | ||||||
|  | type allowedHMACAlgs []string   // TODO | ||||||
|  | 
 | ||||||
|  | func (a allowedKEXAlgs) allowed(k xsnet.KEXAlg) bool { | ||||||
|  | 	for i := 0; i < len(a); i++ { | ||||||
|  | 		if a[i] == "KEX_all" || a[i] == k.String() { | ||||||
|  | 			return true | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return false | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (a *allowedKEXAlgs) String() string { | ||||||
|  | 	return fmt.Sprintf("allowedKEXAlgs: %v", *a) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (a *allowedKEXAlgs) Set(value string) error { | ||||||
|  | 	*a = append(*a, strings.TrimSpace(value)) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (a allowedCipherAlgs) allowed(c xsnet.CSCipherAlg) bool { | ||||||
|  | 	for i := 0; i < len(a); i++ { | ||||||
|  | 		if a[i] == "C_all" || a[i] == c.String() { | ||||||
|  | 			return true | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return false | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (a *allowedCipherAlgs) String() string { | ||||||
|  | 	return fmt.Sprintf("allowedCipherAlgs: %v", *a) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (a *allowedCipherAlgs) Set(value string) error { | ||||||
|  | 	*a = append(*a, strings.TrimSpace(value)) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (a allowedHMACAlgs) allowed(h xsnet.CSHmacAlg) bool { | ||||||
|  | 	for i := 0; i < len(a); i++ { | ||||||
|  | 		if a[i] == "H_all" || a[i] == h.String() { | ||||||
|  | 			return true | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return false | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (a *allowedHMACAlgs) String() string { | ||||||
|  | 	return fmt.Sprintf("allowedHMACAlgs: %v", *a) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (a *allowedHMACAlgs) Set(value string) error { | ||||||
|  | 	*a = append(*a, strings.TrimSpace(value)) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Main server that listens and spawns goroutines for each | ||||||
|  | // connecting client to serve interactive or file copy sessions | ||||||
|  | // and any requested tunnels. | ||||||
|  | // Note that this server does not do UNIX forks of itself to give | ||||||
|  | // each client its own separate manager process, so if the main | ||||||
|  | // daemon dies, all clients will be rudely disconnected. | ||||||
|  | // Consider this when planning to restart or upgrade in-place an installation. | ||||||
| // TODO: reduce gocyclo | // TODO: reduce gocyclo | ||||||
| func main() { | func main() { | ||||||
| 	version := hkexsh.Version |  | ||||||
| 
 |  | ||||||
| 	var vopt bool | 	var vopt bool | ||||||
| 	var chaffEnabled bool | 	var chaffEnabled bool | ||||||
| 	var chaffFreqMin uint | 	var chaffFreqMin uint | ||||||
|  | @ -385,17 +510,27 @@ func main() { | ||||||
| 	var dbg bool | 	var dbg bool | ||||||
| 	var laddr string | 	var laddr string | ||||||
| 
 | 
 | ||||||
|  | 	var useSystemPasswd bool | ||||||
|  | 
 | ||||||
| 	flag.BoolVar(&vopt, "v", false, "show version") | 	flag.BoolVar(&vopt, "v", false, "show version") | ||||||
| 	flag.StringVar(&laddr, "l", ":2000", "interface[:port] to listen") | 	flag.StringVar(&laddr, "l", ":2000", "interface[:port] to listen") | ||||||
| 	flag.BoolVar(&chaffEnabled, "e", true, "enabled chaff pkts") | 	flag.StringVar(&kcpMode, "K", "unused", `set to one of ["KCP_NONE","KCP_AES", "KCP_BLOWFISH", "KCP_CAST5", "KCP_SM4", "KCP_SALSA20", "KCP_SIMPLEXOR", "KCP_TEA", "KCP_3DES", "KCP_TWOFISH", "KCP_XTEA"] to use KCP (github.com/xtaci/kcp-go) reliable UDP instead of TCP`) | ||||||
|  | 	flag.BoolVar(&useSysLogin, "L", false, "use system login") | ||||||
|  | 	flag.BoolVar(&chaffEnabled, "e", true, "enable chaff pkts") | ||||||
| 	flag.UintVar(&chaffFreqMin, "f", 100, "chaff pkt freq min (msecs)") | 	flag.UintVar(&chaffFreqMin, "f", 100, "chaff pkt freq min (msecs)") | ||||||
| 	flag.UintVar(&chaffFreqMax, "F", 5000, "chaff pkt freq max (msecs)") | 	flag.UintVar(&chaffFreqMax, "F", 5000, "chaff pkt freq max (msecs)") | ||||||
| 	flag.UintVar(&chaffBytesMax, "B", 64, "chaff pkt size max (bytes)") | 	flag.UintVar(&chaffBytesMax, "B", 64, "chaff pkt size max (bytes)") | ||||||
|  | 	flag.BoolVar(&useSystemPasswd, "s", true, "use system shadow passwds") | ||||||
| 	flag.BoolVar(&dbg, "d", false, "debug logging") | 	flag.BoolVar(&dbg, "d", false, "debug logging") | ||||||
|  | 
 | ||||||
|  | 	flag.Var(&aKEXAlgs, "aK", `List of allowed KEX algs (eg. 'KEXAlgA KEXAlgB ... KEXAlgN') (default allow all)`) | ||||||
|  | 	flag.Var(&aCipherAlgs, "aC", `List of allowed ciphers (eg. 'CipherAlgA CipherAlgB ... CipherAlgN') (default allow all)`) | ||||||
|  | 	flag.Var(&aHMACAlgs, "aH", `List of allowed HMACs (eg. 'HMACAlgA HMACAlgB ... HMACAlgN') (default allow all)`) | ||||||
|  | 
 | ||||||
| 	flag.Parse() | 	flag.Parse() | ||||||
| 
 | 
 | ||||||
| 	if vopt { | 	if vopt { | ||||||
| 		fmt.Printf("version v%s\n", version) | 		fmt.Printf("version %s (%s)\n", version, gitCommit) | ||||||
| 		os.Exit(0) | 		os.Exit(0) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -406,14 +541,41 @@ func main() { | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	Log, _ = logger.New(logger.LOG_DAEMON|logger.LOG_DEBUG|logger.LOG_NOTICE|logger.LOG_ERR, "hkexshd") // nolint: gosec | 	// Enforce some sane min/max vals on chaff flags | ||||||
| 	hkexnet.Init(dbg, "hkexshd", logger.LOG_DAEMON|logger.LOG_DEBUG|logger.LOG_NOTICE|logger.LOG_ERR) | 	if chaffFreqMin < 2 { | ||||||
|  | 		chaffFreqMin = 2 | ||||||
|  | 	} | ||||||
|  | 	if chaffFreqMax == 0 { | ||||||
|  | 		chaffFreqMax = chaffFreqMin + 1 | ||||||
|  | 	} | ||||||
|  | 	if chaffBytesMax == 0 || chaffBytesMax > 4096 { | ||||||
|  | 		chaffBytesMax = 64 | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	Log, _ = logger.New(logger.LOG_DAEMON|logger.LOG_DEBUG|logger.LOG_NOTICE|logger.LOG_ERR, "xsd") // nolint: gosec | ||||||
|  | 	xsnet.Init(dbg, "xsd", logger.LOG_DAEMON|logger.LOG_DEBUG|logger.LOG_NOTICE|logger.LOG_ERR) | ||||||
| 	if dbg { | 	if dbg { | ||||||
| 		log.SetOutput(Log) | 		log.SetOutput(Log) | ||||||
| 	} else { | 	} else { | ||||||
| 		log.SetOutput(ioutil.Discard) | 		log.SetOutput(ioutil.Discard) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	// Set up allowed algs, if specified (default allow all) | ||||||
|  | 	if len(aKEXAlgs) == 0 { | ||||||
|  | 		aKEXAlgs = []string{"KEX_all"} | ||||||
|  | 	} | ||||||
|  | 	logger.LogNotice(fmt.Sprintf("Allowed KEXAlgs: %v\n", aKEXAlgs)) // nolint: gosec,errcheck | ||||||
|  | 
 | ||||||
|  | 	if len(aCipherAlgs) == 0 { | ||||||
|  | 		aCipherAlgs = []string{"C_all"} | ||||||
|  | 	} | ||||||
|  | 	logger.LogNotice(fmt.Sprintf("Allowed CipherAlgs: %v\n", aCipherAlgs)) // nolint: gosec,errcheck | ||||||
|  | 
 | ||||||
|  | 	if len(aHMACAlgs) == 0 { | ||||||
|  | 		aHMACAlgs = []string{"H_all"} | ||||||
|  | 	} | ||||||
|  | 	logger.LogNotice(fmt.Sprintf("Allowed HMACAlgs: %v\n", aHMACAlgs)) // nolint: gosec,errcheck | ||||||
|  | 
 | ||||||
| 	// Set up handler for daemon signalling | 	// Set up handler for daemon signalling | ||||||
| 	exitCh := make(chan os.Signal, 1) | 	exitCh := make(chan os.Signal, 1) | ||||||
| 	signal.Notify(exitCh, os.Signal(syscall.SIGTERM), os.Signal(syscall.SIGINT), os.Signal(syscall.SIGHUP), os.Signal(syscall.SIGUSR1), os.Signal(syscall.SIGUSR2)) | 	signal.Notify(exitCh, os.Signal(syscall.SIGTERM), os.Signal(syscall.SIGINT), os.Signal(syscall.SIGHUP), os.Signal(syscall.SIGUSR1), os.Signal(syscall.SIGUSR2)) | ||||||
|  | @ -437,9 +599,11 @@ func main() { | ||||||
| 		} | 		} | ||||||
| 	}() | 	}() | ||||||
| 
 | 
 | ||||||
| 	// Listen on TCP port 2000 on all available unicast and | 	proto := "tcp" | ||||||
| 	// anycast IP addresses of the local system. | 	if kcpMode != "unused" { | ||||||
| 	l, err := hkexnet.Listen("tcp", laddr) | 		proto = "kcp" | ||||||
|  | 	} | ||||||
|  | 	l, err := xsnet.Listen(proto, laddr, kcpMode) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Fatal(err) | 		log.Fatal(err) | ||||||
| 	} | 	} | ||||||
|  | @ -448,9 +612,22 @@ func main() { | ||||||
| 	log.Println("Serving on", laddr) | 	log.Println("Serving on", laddr) | ||||||
| 	for { | 	for { | ||||||
| 		// Wait for a connection. | 		// Wait for a connection. | ||||||
|  | 		// Then check if client-proposed algs are allowed | ||||||
| 		conn, err := l.Accept() | 		conn, err := l.Accept() | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			log.Printf("Accept() got error(%v), hanging up.\n", err) | 			log.Printf("Accept() got error(%v), hanging up.\n", err) | ||||||
|  | 		} else if !aKEXAlgs.allowed(conn.KEX()) { | ||||||
|  | 			log.Printf("Accept() rejected for banned KEX alg %d, hanging up.\n", conn.KEX()) | ||||||
|  | 			conn.SetStatus(xsnet.CSEKEXAlgDenied) | ||||||
|  | 			conn.Close() | ||||||
|  | 		} else if !aCipherAlgs.allowed(conn.CAlg()) { | ||||||
|  | 			log.Printf("Accept() rejected for banned Cipher alg %d, hanging up.\n", conn.CAlg()) | ||||||
|  | 			conn.SetStatus(xsnet.CSECipherAlgDenied) | ||||||
|  | 			conn.Close() | ||||||
|  | 		} else if !aHMACAlgs.allowed(conn.HAlg()) { | ||||||
|  | 			log.Printf("Accept() rejected for banned HMAC alg %d, hanging up.\n", conn.HAlg()) | ||||||
|  | 			conn.SetStatus(xsnet.CSEHMACAlgDenied) | ||||||
|  | 			conn.Close() | ||||||
| 		} else { | 		} else { | ||||||
| 			log.Println("Accepted client") | 			log.Println("Accepted client") | ||||||
| 
 | 
 | ||||||
|  | @ -462,28 +639,35 @@ func main() { | ||||||
| 			// Handle the connection in a new goroutine. | 			// Handle the connection in a new goroutine. | ||||||
| 			// The loop then returns to accepting, so that | 			// The loop then returns to accepting, so that | ||||||
| 			// multiple connections may be served concurrently. | 			// multiple connections may be served concurrently. | ||||||
| 			go func(hc *hkexnet.Conn) (e error) { | 			go func(hc *xsnet.Conn) (e error) { | ||||||
| 				defer hc.Close() // nolint: errcheck | 				defer hc.Close() // nolint: errcheck | ||||||
| 
 | 
 | ||||||
|  | 				// Start login timeout here and disconnect if user/pass phase stalls | ||||||
|  | 				loginTimeout := time.AfterFunc(30*time.Second, func() { | ||||||
|  | 					logger.LogNotice(fmt.Sprintln("Login timed out")) // nolint: errcheck,gosec | ||||||
|  | 					hc.Write([]byte{0})                               // nolint: gosec,errcheck | ||||||
|  | 					hc.Close() | ||||||
|  | 				}) | ||||||
|  | 
 | ||||||
| 				//We use io.ReadFull() here to guarantee we consume | 				//We use io.ReadFull() here to guarantee we consume | ||||||
| 				//just the data we want for the hkexsh.Session, and no more. | 				//just the data we want for the xs.Session, and no more. | ||||||
| 				//Otherwise data will be sitting in the channel that isn't | 				//Otherwise data will be sitting in the channel that isn't | ||||||
| 				//passed down to the command handlers. | 				//passed down to the command handlers. | ||||||
| 				var rec hkexsh.Session | 				var rec xs.Session | ||||||
| 				var len1, len2, len3, len4, len5, len6 uint32 | 				var len1, len2, len3, len4, len5, len6 uint32 | ||||||
| 
 | 
 | ||||||
| 				n, err := fmt.Fscanf(hc, "%d %d %d %d %d %d\n", &len1, &len2, &len3, &len4, &len5, &len6) | 				n, err := fmt.Fscanf(hc, "%d %d %d %d %d %d\n", &len1, &len2, &len3, &len4, &len5, &len6) | ||||||
| 				log.Printf("hkexsh.Session read:%d %d %d %d %d %d\n", len1, len2, len3, len4, len5, len6) | 				log.Printf("xs.Session read:%d %d %d %d %d %d\n", len1, len2, len3, len4, len5, len6) | ||||||
| 
 | 
 | ||||||
| 				if err != nil || n < 6 { | 				if err != nil || n < 6 { | ||||||
| 					log.Println("[Bad hkexsh.Session fmt]") | 					log.Println("[Bad xs.Session fmt]") | ||||||
| 					return err | 					return err | ||||||
| 				} | 				} | ||||||
| 
 | 
 | ||||||
| 				tmp := make([]byte, len1) | 				tmp := make([]byte, len1) | ||||||
| 				_, err = io.ReadFull(hc, tmp) | 				_, err = io.ReadFull(hc, tmp) | ||||||
| 				if err != nil { | 				if err != nil { | ||||||
| 					log.Println("[Bad hkexsh.Session.Op]") | 					log.Println("[Bad xs.Session.Op]") | ||||||
| 					return err | 					return err | ||||||
| 				} | 				} | ||||||
| 				rec.SetOp(tmp) | 				rec.SetOp(tmp) | ||||||
|  | @ -491,7 +675,7 @@ func main() { | ||||||
| 				tmp = make([]byte, len2) | 				tmp = make([]byte, len2) | ||||||
| 				_, err = io.ReadFull(hc, tmp) | 				_, err = io.ReadFull(hc, tmp) | ||||||
| 				if err != nil { | 				if err != nil { | ||||||
| 					log.Println("[Bad hkexsh.Session.Who]") | 					log.Println("[Bad xs.Session.Who]") | ||||||
| 					return err | 					return err | ||||||
| 				} | 				} | ||||||
| 				rec.SetWho(tmp) | 				rec.SetWho(tmp) | ||||||
|  | @ -499,7 +683,7 @@ func main() { | ||||||
| 				tmp = make([]byte, len3) | 				tmp = make([]byte, len3) | ||||||
| 				_, err = io.ReadFull(hc, tmp) | 				_, err = io.ReadFull(hc, tmp) | ||||||
| 				if err != nil { | 				if err != nil { | ||||||
| 					log.Println("[Bad hkexsh.Session.ConnHost]") | 					log.Println("[Bad xs.Session.ConnHost]") | ||||||
| 					return err | 					return err | ||||||
| 				} | 				} | ||||||
| 				rec.SetConnHost(tmp) | 				rec.SetConnHost(tmp) | ||||||
|  | @ -507,7 +691,7 @@ func main() { | ||||||
| 				tmp = make([]byte, len4) | 				tmp = make([]byte, len4) | ||||||
| 				_, err = io.ReadFull(hc, tmp) | 				_, err = io.ReadFull(hc, tmp) | ||||||
| 				if err != nil { | 				if err != nil { | ||||||
| 					log.Println("[Bad hkexsh.Session.TermType]") | 					log.Println("[Bad xs.Session.TermType]") | ||||||
| 					return err | 					return err | ||||||
| 				} | 				} | ||||||
| 				rec.SetTermType(tmp) | 				rec.SetTermType(tmp) | ||||||
|  | @ -515,7 +699,7 @@ func main() { | ||||||
| 				tmp = make([]byte, len5) | 				tmp = make([]byte, len5) | ||||||
| 				_, err = io.ReadFull(hc, tmp) | 				_, err = io.ReadFull(hc, tmp) | ||||||
| 				if err != nil { | 				if err != nil { | ||||||
| 					log.Println("[Bad hkexsh.Session.Cmd]") | 					log.Println("[Bad xs.Session.Cmd]") | ||||||
| 					return err | 					return err | ||||||
| 				} | 				} | ||||||
| 				rec.SetCmd(tmp) | 				rec.SetCmd(tmp) | ||||||
|  | @ -523,22 +707,28 @@ func main() { | ||||||
| 				tmp = make([]byte, len6) | 				tmp = make([]byte, len6) | ||||||
| 				_, err = io.ReadFull(hc, tmp) | 				_, err = io.ReadFull(hc, tmp) | ||||||
| 				if err != nil { | 				if err != nil { | ||||||
| 					log.Println("[Bad hkexsh.Session.AuthCookie]") | 					log.Println("[Bad xs.Session.AuthCookie]") | ||||||
| 					return err | 					return err | ||||||
| 				} | 				} | ||||||
| 				rec.SetAuthCookie(tmp) | 				rec.SetAuthCookie(tmp) | ||||||
| 
 | 
 | ||||||
| 				log.Printf("[hkexsh.Session: op:%c who:%s connhost:%s cmd:%s auth:****]\n", | 				log.Printf("[xs.Session: op:%c who:%s connhost:%s cmd:%s auth:****]\n", | ||||||
| 					rec.Op()[0], string(rec.Who()), string(rec.ConnHost()), string(rec.Cmd())) | 					rec.Op()[0], string(rec.Who()), string(rec.ConnHost()), string(rec.Cmd())) | ||||||
| 
 | 
 | ||||||
| 				var valid bool | 				var valid bool | ||||||
| 				var allowedCmds string // Currently unused | 				var allowedCmds string // Currently unused | ||||||
| 				if hkexsh.AuthUserByToken(string(rec.Who()), string(rec.ConnHost()), string(rec.AuthCookie(true))) { | 				if xs.AuthUserByToken(xs.NewAuthCtx(), string(rec.Who()), string(rec.ConnHost()), string(rec.AuthCookie(true))) { | ||||||
| 					valid = true | 					valid = true | ||||||
| 				} else { | 				} else { | ||||||
| 					valid, allowedCmds = hkexsh.AuthUserByPasswd(string(rec.Who()), string(rec.AuthCookie(true)), "/etc/hkexsh.passwd") | 					if useSystemPasswd { | ||||||
|  | 						//var passErr error | ||||||
|  | 						valid, _ /*passErr*/ = xs.VerifyPass(xs.NewAuthCtx(), string(rec.Who()), string(rec.AuthCookie(true))) | ||||||
|  | 					} else { | ||||||
|  | 						valid, allowedCmds = xs.AuthUserByPasswd(xs.NewAuthCtx(), string(rec.Who()), string(rec.AuthCookie(true)), "/etc/xs.passwd") | ||||||
|  | 					} | ||||||
| 				} | 				} | ||||||
| 
 | 
 | ||||||
|  | 				_ = loginTimeout.Stop() | ||||||
| 				// Security scrub | 				// Security scrub | ||||||
| 				rec.ClearAuthCookie() | 				rec.ClearAuthCookie() | ||||||
| 
 | 
 | ||||||
|  | @ -559,8 +749,8 @@ func main() { | ||||||
| 					hname := goutmp.GetHost(addr.String()) | 					hname := goutmp.GetHost(addr.String()) | ||||||
| 					logger.LogNotice(fmt.Sprintf("[Generating autologin token for [%s@%s]]\n", rec.Who(), hname)) // nolint: gosec,errcheck | 					logger.LogNotice(fmt.Sprintf("[Generating autologin token for [%s@%s]]\n", rec.Who(), hname)) // nolint: gosec,errcheck | ||||||
| 					token := GenAuthToken(string(rec.Who()), string(rec.ConnHost())) | 					token := GenAuthToken(string(rec.Who()), string(rec.ConnHost())) | ||||||
| 					tokenCmd := fmt.Sprintf("echo \"%s\" | tee -a ~/.hkexsh_id", token) | 					tokenCmd := fmt.Sprintf("echo \"%s\" | tee -a ~/.xs_id", token) | ||||||
| 					cmdStatus, runErr := runShellAs(string(rec.Who()), string(rec.TermType()), tokenCmd, false, hc, chaffEnabled) | 					cmdStatus, runErr := runShellAs(string(rec.Who()), hname, string(rec.TermType()), tokenCmd, false, hc, chaffEnabled) | ||||||
| 					// Returned hopefully via an EOF or exit/logout; | 					// Returned hopefully via an EOF or exit/logout; | ||||||
| 					// Clear current op so user can enter next, or EOF | 					// Clear current op so user can enter next, or EOF | ||||||
| 					rec.SetOp([]byte{0}) | 					rec.SetOp([]byte{0}) | ||||||
|  | @ -568,14 +758,14 @@ func main() { | ||||||
| 						logger.LogErr(fmt.Sprintf("[Error generating autologin token for %s@%s]\n", rec.Who(), hname)) // nolint: gosec,errcheck | 						logger.LogErr(fmt.Sprintf("[Error generating autologin token for %s@%s]\n", rec.Who(), hname)) // nolint: gosec,errcheck | ||||||
| 					} else { | 					} else { | ||||||
| 						log.Printf("[Autologin token generation completed for %s@%s, status %d]\n", rec.Who(), hname, cmdStatus) | 						log.Printf("[Autologin token generation completed for %s@%s, status %d]\n", rec.Who(), hname, cmdStatus) | ||||||
| 						hc.SetStatus(hkexnet.CSOType(cmdStatus)) | 						hc.SetStatus(xsnet.CSOType(cmdStatus)) | ||||||
| 					} | 					} | ||||||
| 				} else if rec.Op()[0] == 'c' { | 				} else if rec.Op()[0] == 'c' { | ||||||
| 					// Non-interactive command | 					// Non-interactive command | ||||||
| 					addr := hc.RemoteAddr() | 					addr := hc.RemoteAddr() | ||||||
| 					hname := goutmp.GetHost(addr.String()) | 					hname := goutmp.GetHost(addr.String()) | ||||||
| 					logger.LogNotice(fmt.Sprintf("[Running command for [%s@%s]]\n", rec.Who(), hname)) // nolint: gosec,errcheck | 					logger.LogNotice(fmt.Sprintf("[Running command for [%s@%s]]\n", rec.Who(), hname)) // nolint: gosec,errcheck | ||||||
| 					cmdStatus, runErr := runShellAs(string(rec.Who()), string(rec.TermType()), string(rec.Cmd()), false, hc, chaffEnabled) | 					cmdStatus, runErr := runShellAs(string(rec.Who()), hname, string(rec.TermType()), string(rec.Cmd()), false, hc, chaffEnabled) | ||||||
| 					// Returned hopefully via an EOF or exit/logout; | 					// Returned hopefully via an EOF or exit/logout; | ||||||
| 					// Clear current op so user can enter next, or EOF | 					// Clear current op so user can enter next, or EOF | ||||||
| 					rec.SetOp([]byte{0}) | 					rec.SetOp([]byte{0}) | ||||||
|  | @ -583,7 +773,7 @@ func main() { | ||||||
| 						logger.LogErr(fmt.Sprintf("[Error spawning cmd for %s@%s]\n", rec.Who(), hname)) // nolint: gosec,errcheck | 						logger.LogErr(fmt.Sprintf("[Error spawning cmd for %s@%s]\n", rec.Who(), hname)) // nolint: gosec,errcheck | ||||||
| 					} else { | 					} else { | ||||||
| 						logger.LogNotice(fmt.Sprintf("[Command completed for %s@%s, status %d]\n", rec.Who(), hname, cmdStatus)) // nolint: gosec,errcheck | 						logger.LogNotice(fmt.Sprintf("[Command completed for %s@%s, status %d]\n", rec.Who(), hname, cmdStatus)) // nolint: gosec,errcheck | ||||||
| 						hc.SetStatus(hkexnet.CSOType(cmdStatus)) | 						hc.SetStatus(xsnet.CSOType(cmdStatus)) | ||||||
| 					} | 					} | ||||||
| 				} else if rec.Op()[0] == 's' { | 				} else if rec.Op()[0] == 's' { | ||||||
| 					// Interactive session | 					// Interactive session | ||||||
|  | @ -591,10 +781,7 @@ func main() { | ||||||
| 					hname := goutmp.GetHost(addr.String()) | 					hname := goutmp.GetHost(addr.String()) | ||||||
| 					logger.LogNotice(fmt.Sprintf("[Running shell for [%s@%s]]\n", rec.Who(), hname)) // nolint: gosec,errcheck | 					logger.LogNotice(fmt.Sprintf("[Running shell for [%s@%s]]\n", rec.Who(), hname)) // nolint: gosec,errcheck | ||||||
| 
 | 
 | ||||||
| 					utmpx := goutmp.Put_utmp(string(rec.Who()), hname) | 					cmdStatus, runErr := runShellAs(string(rec.Who()), hname, string(rec.TermType()), string(rec.Cmd()), true, hc, chaffEnabled) | ||||||
| 					defer func() { goutmp.Unput_utmp(utmpx) }() |  | ||||||
| 					goutmp.Put_lastlog_entry("hkexsh", string(rec.Who()), hname) |  | ||||||
| 					cmdStatus, runErr := runShellAs(string(rec.Who()), string(rec.TermType()), string(rec.Cmd()), true, hc, chaffEnabled) |  | ||||||
| 					// Returned hopefully via an EOF or exit/logout; | 					// Returned hopefully via an EOF or exit/logout; | ||||||
| 					// Clear current op so user can enter next, or EOF | 					// Clear current op so user can enter next, or EOF | ||||||
| 					rec.SetOp([]byte{0}) | 					rec.SetOp([]byte{0}) | ||||||
|  | @ -602,7 +789,7 @@ func main() { | ||||||
| 						Log.Err(fmt.Sprintf("[Error spawning shell for %s@%s]\n", rec.Who(), hname)) // nolint: gosec,errcheck | 						Log.Err(fmt.Sprintf("[Error spawning shell for %s@%s]\n", rec.Who(), hname)) // nolint: gosec,errcheck | ||||||
| 					} else { | 					} else { | ||||||
| 						logger.LogNotice(fmt.Sprintf("[Shell completed for %s@%s, status %d]\n", rec.Who(), hname, cmdStatus)) // nolint: gosec,errcheck | 						logger.LogNotice(fmt.Sprintf("[Shell completed for %s@%s, status %d]\n", rec.Who(), hname, cmdStatus)) // nolint: gosec,errcheck | ||||||
| 						hc.SetStatus(hkexnet.CSOType(cmdStatus)) | 						hc.SetStatus(xsnet.CSOType(cmdStatus)) | ||||||
| 					} | 					} | ||||||
| 				} else if rec.Op()[0] == 'D' { | 				} else if rec.Op()[0] == 'D' { | ||||||
| 					// File copy (destination) operation - client copy to server | 					// File copy (destination) operation - client copy to server | ||||||
|  | @ -619,13 +806,15 @@ func main() { | ||||||
| 					} else { | 					} else { | ||||||
| 						logger.LogNotice(fmt.Sprintf("[Command completed for %s@%s, status %d]\n", rec.Who(), hname, cmdStatus)) // nolint: gosec,errcheck | 						logger.LogNotice(fmt.Sprintf("[Command completed for %s@%s, status %d]\n", rec.Who(), hname, cmdStatus)) // nolint: gosec,errcheck | ||||||
| 					} | 					} | ||||||
| 					hc.SetStatus(hkexnet.CSOType(cmdStatus)) | 					// TODO: Test this with huge files.. see Bug #22 - do we need to | ||||||
|  | 					//   sync w/sender (client) that we've gotten all data? | ||||||
|  | 					hc.SetStatus(xsnet.CSOType(cmdStatus)) | ||||||
| 
 | 
 | ||||||
| 					// Send CSOExitStatus *before* client closes channel | 					// Send CSOExitStatus *before* client closes channel | ||||||
| 					s := make([]byte, 4) | 					s := make([]byte, 4) | ||||||
| 					binary.BigEndian.PutUint32(s, cmdStatus) | 					binary.BigEndian.PutUint32(s, cmdStatus) | ||||||
| 					log.Printf("** cp writing closeStat %d at Close()\n", cmdStatus) | 					log.Printf("** cp writing closeStat %d at Close()\n", cmdStatus) | ||||||
| 					hc.WritePacket(s, hkexnet.CSOExitStatus) // nolint: gosec,errcheck | 					hc.WritePacket(s, xsnet.CSOExitStatus) // nolint: gosec,errcheck | ||||||
| 				} else if rec.Op()[0] == 'S' { | 				} else if rec.Op()[0] == 'S' { | ||||||
| 					// File copy (src) operation - server copy to client | 					// File copy (src) operation - server copy to client | ||||||
| 					log.Printf("[Server->Client copy]\n") | 					log.Printf("[Server->Client copy]\n") | ||||||
|  | @ -639,14 +828,18 @@ func main() { | ||||||
| 						// Returned hopefully via an EOF or exit/logout; | 						// Returned hopefully via an EOF or exit/logout; | ||||||
| 						logger.LogNotice(fmt.Sprintf("[Command completed for %s@%s, status %d]\n", rec.Who(), hname, cmdStatus)) // nolint: gosec,errcheck | 						logger.LogNotice(fmt.Sprintf("[Command completed for %s@%s, status %d]\n", rec.Who(), hname, cmdStatus)) // nolint: gosec,errcheck | ||||||
| 					} | 					} | ||||||
|  | 					// HACK: Bug #22: (xc) Need to wait for rcvr to get final data | ||||||
|  | 					// TODO: Await specific msg from client to inform they have gotten all data from the tarpipe | ||||||
|  | 					time.Sleep(time.Duration(900 * time.Millisecond)) // Let rcvr set this on setup? | ||||||
|  | 
 | ||||||
| 					// Clear current op so user can enter next, or EOF | 					// Clear current op so user can enter next, or EOF | ||||||
| 					rec.SetOp([]byte{0}) | 					rec.SetOp([]byte{0}) | ||||||
| 					hc.SetStatus(hkexnet.CSOType(cmdStatus)) | 					hc.SetStatus(xsnet.CSOType(cmdStatus)) | ||||||
| 					//fmt.Println("Waiting for EOF from other end.") | 					//fmt.Println("Waiting for EOF from other end.") | ||||||
| 					//_, _ = hc.Read(nil /*ackByte*/) | 					//_, _ = hc.Read(nil /*ackByte*/) | ||||||
| 					//fmt.Println("Got remote end ack.") | 					//fmt.Println("Got remote end ack.") | ||||||
| 				} else { | 				} else { | ||||||
| 					logger.LogErr(fmt.Sprintln("[Bad hkexsh.Session]")) // nolint: gosec,errcheck | 					logger.LogErr(fmt.Sprintln("[Bad xs.Session]")) // nolint: gosec,errcheck | ||||||
| 				} | 				} | ||||||
| 				return | 				return | ||||||
| 			}(&conn) // nolint: errcheck | 			}(&conn) // nolint: errcheck | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| package hkexnet | package xsnet | ||||||
| 
 | 
 | ||||||
| // Copyright (c) 2017-2018 Russell Magee | // Copyright (c) 2017-2020 Russell Magee | ||||||
| // Licensed under the terms of the MIT license (see LICENSE.mit in this | // Licensed under the terms of the MIT license (see LICENSE.mit in this | ||||||
| // distribution) | // distribution) | ||||||
| // | // | ||||||
|  | @ -20,9 +20,11 @@ import ( | ||||||
| 	"hash" | 	"hash" | ||||||
| 	"log" | 	"log" | ||||||
| 
 | 
 | ||||||
|  | 	"blitter.com/go/cryptmt" | ||||||
|  | 	"github.com/aead/chacha20/chacha" | ||||||
| 	"golang.org/x/crypto/blowfish" | 	"golang.org/x/crypto/blowfish" | ||||||
| 	"golang.org/x/crypto/twofish" | 	"golang.org/x/crypto/twofish" | ||||||
| 	"blitter.com/go/cryptmt" | 
 | ||||||
| 	// hash algos must be manually imported thusly: | 	// hash algos must be manually imported thusly: | ||||||
| 	// (Would be nice if the golang pkg docs were more clear | 	// (Would be nice if the golang pkg docs were more clear | ||||||
| 	// on this...) | 	// on this...) | ||||||
|  | @ -55,9 +57,9 @@ func expandKeyMat(keymat []byte, blocksize int) []byte { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /* Support functionality to set up encryption after a channel has | /* Support functionality to set up encryption after a channel has | ||||||
| been negotiated via hkexnet.go | been negotiated via xsnet.go | ||||||
| */ | */ | ||||||
| func (hc Conn) getStream(keymat []byte) (rc cipher.Stream, mc hash.Hash, err error) { | func (hc *Conn) getStream(keymat []byte) (rc cipher.Stream, mc hash.Hash, err error) { | ||||||
| 	var key []byte | 	var key []byte | ||||||
| 	var block cipher.Block | 	var block cipher.Block | ||||||
| 	var iv []byte | 	var iv []byte | ||||||
|  | @ -75,7 +77,6 @@ func (hc Conn) getStream(keymat []byte) (rc cipher.Stream, mc hash.Hash, err err | ||||||
| 		iv = keymat[aes.BlockSize : aes.BlockSize+ivlen] | 		iv = keymat[aes.BlockSize : aes.BlockSize+ivlen] | ||||||
| 		rc = cipher.NewOFB(block, iv) | 		rc = cipher.NewOFB(block, iv) | ||||||
| 		log.Printf("[cipher AES_256 (%d)]\n", copts) | 		log.Printf("[cipher AES_256 (%d)]\n", copts) | ||||||
| 		break |  | ||||||
| 	case CAlgTwofish128: | 	case CAlgTwofish128: | ||||||
| 		keymat = expandKeyMat(keymat, twofish.BlockSize) | 		keymat = expandKeyMat(keymat, twofish.BlockSize) | ||||||
| 		key = keymat[0:twofish.BlockSize] | 		key = keymat[0:twofish.BlockSize] | ||||||
|  | @ -84,7 +85,6 @@ func (hc Conn) getStream(keymat []byte) (rc cipher.Stream, mc hash.Hash, err err | ||||||
| 		iv = keymat[twofish.BlockSize : twofish.BlockSize+ivlen] | 		iv = keymat[twofish.BlockSize : twofish.BlockSize+ivlen] | ||||||
| 		rc = cipher.NewOFB(block, iv) | 		rc = cipher.NewOFB(block, iv) | ||||||
| 		log.Printf("[cipher TWOFISH_128 (%d)]\n", copts) | 		log.Printf("[cipher TWOFISH_128 (%d)]\n", copts) | ||||||
| 		break |  | ||||||
| 	case CAlgBlowfish64: | 	case CAlgBlowfish64: | ||||||
| 		keymat = expandKeyMat(keymat, blowfish.BlockSize) | 		keymat = expandKeyMat(keymat, blowfish.BlockSize) | ||||||
| 		key = keymat[0:blowfish.BlockSize] | 		key = keymat[0:blowfish.BlockSize] | ||||||
|  | @ -102,11 +102,21 @@ func (hc Conn) getStream(keymat []byte) (rc cipher.Stream, mc hash.Hash, err err | ||||||
| 		iv = keymat[blowfish.BlockSize : blowfish.BlockSize+ivlen] | 		iv = keymat[blowfish.BlockSize : blowfish.BlockSize+ivlen] | ||||||
| 		rc = cipher.NewOFB(block, iv) | 		rc = cipher.NewOFB(block, iv) | ||||||
| 		log.Printf("[cipher BLOWFISH_64 (%d)]\n", copts) | 		log.Printf("[cipher BLOWFISH_64 (%d)]\n", copts) | ||||||
| 		break |  | ||||||
| 	case CAlgCryptMT1: | 	case CAlgCryptMT1: | ||||||
| 		rc = cryptmt.NewCipher(keymat) | 		rc = cryptmt.New(nil, nil, keymat) | ||||||
| 		log.Printf("[cipher CRYPTMT1 (%d)]\n", copts) | 		log.Printf("[cipher CRYPTMT1 (%d)]\n", copts) | ||||||
| 		break | 	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: | 	default: | ||||||
| 		log.Printf("[invalid cipher (%d)]\n", copts) | 		log.Printf("[invalid cipher (%d)]\n", copts) | ||||||
| 		fmt.Printf("DOOFUS SET A VALID CIPHER ALG (%d)\n", copts) | 		fmt.Printf("DOOFUS SET A VALID CIPHER ALG (%d)\n", copts) | ||||||
|  | @ -123,7 +133,6 @@ func (hc Conn) getStream(keymat []byte) (rc cipher.Stream, mc hash.Hash, err err | ||||||
| 		if !halg.Available() { | 		if !halg.Available() { | ||||||
| 			log.Fatal("hash not available!") | 			log.Fatal("hash not available!") | ||||||
| 		} | 		} | ||||||
| 		break |  | ||||||
| 	case HmacSHA512: | 	case HmacSHA512: | ||||||
| 		log.Printf("[hash HmacSHA512 (%d)]\n", hopts) | 		log.Printf("[hash HmacSHA512 (%d)]\n", hopts) | ||||||
| 		halg := crypto.SHA512 | 		halg := crypto.SHA512 | ||||||
|  | @ -131,7 +140,6 @@ func (hc Conn) getStream(keymat []byte) (rc cipher.Stream, mc hash.Hash, err err | ||||||
| 		if !halg.Available() { | 		if !halg.Available() { | ||||||
| 			log.Fatal("hash not available!") | 			log.Fatal("hash not available!") | ||||||
| 		} | 		} | ||||||
| 		break |  | ||||||
| 	default: | 	default: | ||||||
| 		log.Printf("[invalid hmac (%d)]\n", hopts) | 		log.Printf("[invalid hmac (%d)]\n", hopts) | ||||||
| 		fmt.Printf("DOOFUS SET A VALID HMAC ALG (%d)\n", hopts) | 		fmt.Printf("DOOFUS SET A VALID HMAC ALG (%d)\n", hopts) | ||||||
|  | @ -1,12 +1,17 @@ | ||||||
| // consts.go - consts for hkexnet | // consts.go - consts for xsnet | ||||||
| 
 | 
 | ||||||
| // Copyright (c) 2017-2018 Russell Magee | // Copyright (c) 2017-2020 Russell Magee | ||||||
| // Licensed under the terms of the MIT license (see LICENSE.mit in this | // Licensed under the terms of the MIT license (see LICENSE.mit in this | ||||||
| // distribution) | // distribution) | ||||||
| // | // | ||||||
| // golang implementation by Russ Magee (rmagee_at_gmail.com) | // golang implementation by Russ Magee (rmagee_at_gmail.com) | ||||||
| package hkexnet | package xsnet | ||||||
| 
 | 
 | ||||||
|  | // KEX algorithm values | ||||||
|  | // | ||||||
|  | // Specified (in string form) as the extensions parameter | ||||||
|  | // to xsnet.Dial() | ||||||
|  | // Alg is sent in a uint8 so there are up to 256 possible | ||||||
| const ( | const ( | ||||||
| 	KEX_HERRADURA256 = iota // this MUST be first for default if omitted in ctor | 	KEX_HERRADURA256 = iota // this MUST be first for default if omitted in ctor | ||||||
| 	KEX_HERRADURA512 | 	KEX_HERRADURA512 | ||||||
|  | @ -20,14 +25,19 @@ const ( | ||||||
| 	KEX_KYBER768 | 	KEX_KYBER768 | ||||||
| 	KEX_KYBER1024 | 	KEX_KYBER1024 | ||||||
| 	KEX_resvd11 | 	KEX_resvd11 | ||||||
| 	KEX_resvd12 | 	KEX_NEWHOPE | ||||||
| 	KEX_resvd13 | 	KEX_NEWHOPE_SIMPLE // 'NewHopeLP-Simple' - https://eprint.iacr.org/2016/1157 | ||||||
| 	KEX_resvd14 | 	KEX_resvd14 | ||||||
| 	KEX_resvd15 | 	KEX_resvd15 | ||||||
|  | 	KEX_FRODOKEM_1344AES | ||||||
|  | 	KEX_FRODOKEM_1344SHAKE | ||||||
|  | 	KEX_FRODOKEM_976AES | ||||||
|  | 	KEX_FRODOKEM_976SHAKE | ||||||
|  | 	KEX_invalid = 255 | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // Sent from client to server in order to specify which | // Sent from client to server in order to specify which | ||||||
| // algo shall be used (eg., HerraduraKEx, [TODO: others...]) | // algo shall be used (see xsnet.KEX_HERRADURA256, ...) | ||||||
| type KEXAlg uint8 | type KEXAlg uint8 | ||||||
| 
 | 
 | ||||||
| // Extended exit status codes - indicate comm/pty issues | // Extended exit status codes - indicate comm/pty issues | ||||||
|  | @ -38,6 +48,10 @@ const ( | ||||||
| 	CSEStillOpen       // Channel closed unexpectedly | 	CSEStillOpen       // Channel closed unexpectedly | ||||||
| 	CSEExecFail        // cmd.Start() (exec) failed | 	CSEExecFail        // cmd.Start() (exec) failed | ||||||
| 	CSEPtyExecFail     // pty.Start() (exec w/pty) failed | 	CSEPtyExecFail     // pty.Start() (exec w/pty) failed | ||||||
|  | 	CSEPtyGetNameFail  // failed to obtain pty name | ||||||
|  | 	CSEKEXAlgDenied    // server rejected proposed KEX alg | ||||||
|  | 	CSECipherAlgDenied // server rejected proposed Cipher alg | ||||||
|  | 	CSEHMACAlgDenied   // server rejected proposed HMAC alg | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // Extended (>255 UNIX exit status) codes | // Extended (>255 UNIX exit status) codes | ||||||
|  | @ -53,6 +67,9 @@ const ( | ||||||
| 	CSOExitStatus         // Remote cmd exit status | 	CSOExitStatus         // Remote cmd exit status | ||||||
| 	CSOChaff              // Dummy packet, do not pass beyond decryption | 	CSOChaff              // Dummy packet, do not pass beyond decryption | ||||||
| 
 | 
 | ||||||
|  | 	// Client side errors | ||||||
|  | 	CSOLoginTimeout | ||||||
|  | 
 | ||||||
| 	// Tunnel setup/control/status | 	// Tunnel setup/control/status | ||||||
| 	CSOTunSetup     // client -> server tunnel setup request (dstport) | 	CSOTunSetup     // client -> server tunnel setup request (dstport) | ||||||
| 	CSOTunSetupAck  // server -> client tunnel setup ack | 	CSOTunSetupAck  // server -> client tunnel setup ack | ||||||
|  | @ -63,8 +80,8 @@ const ( | ||||||
| 	CSOTunHangup    // client -> server: tunnel lport hung up | 	CSOTunHangup    // client -> server: tunnel lport hung up | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // TunEndpoint.tunCtl control values - used to control workers for client or server tunnels | // TunEndpoint.tunCtl control values - used to control workers for client | ||||||
| // depending on the code | // or server tunnels depending on the code | ||||||
| const ( | const ( | ||||||
| 	TunCtl_Client_Listen = 'a' | 	TunCtl_Client_Listen = 'a' | ||||||
| 	// [CSOTunAccept] | 	// [CSOTunAccept] | ||||||
|  | @ -77,28 +94,31 @@ const ( | ||||||
| 	// action:server side should dial() rport on client's behalf | 	// action:server side should dial() rport on client's behalf | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // Channel status Op byte type | // Channel status Op byte type (see CSONone, ... and CSENone, ...) | ||||||
| type CSOType uint32 | type CSOType uint32 | ||||||
| 
 | 
 | ||||||
| //TODO: this should be small (max unfragmented packet size?) | //TODO: this should be small (max unfragmented packet size?) | ||||||
| const MAX_PAYLOAD_LEN = 4*1024*1024*1024 - 1 | const MAX_PAYLOAD_LEN = 2*1024*1024*1024 - 1 | ||||||
| 
 | 
 | ||||||
|  | // Session symmetric crypto algs | ||||||
| const ( | const ( | ||||||
| 	CAlgAES256     = iota | 	CAlgAES256     = iota | ||||||
| 	CAlgTwofish128 // golang.org/x/crypto/twofish | 	CAlgTwofish128 // golang.org/x/crypto/twofish | ||||||
| 	CAlgBlowfish64 // golang.org/x/crypto/blowfish | 	CAlgBlowfish64 // golang.org/x/crypto/blowfish | ||||||
| 	CAlgCryptMT1   //cryptmt using mtwist64 | 	CAlgCryptMT1   //cryptmt using mtwist64 | ||||||
|  | 	CAlgChaCha20_12 | ||||||
| 	CAlgNoneDisallowed | 	CAlgNoneDisallowed | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // Available ciphers for hkex.Conn | // Available ciphers for hkex.Conn | ||||||
| type CSCipherAlg uint32 | type CSCipherAlg uint32 | ||||||
| 
 | 
 | ||||||
|  | // Session packet auth HMAC algs | ||||||
| const ( | const ( | ||||||
| 	HmacSHA256 = iota | 	HmacSHA256 = iota | ||||||
| 	HmacSHA512 | 	HmacSHA512 | ||||||
| 	HmacNoneDisallowed | 	HmacNoneDisallowed | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // Available HMACs for hkex.Conn (TODO: not currently used) | // Available HMACs for hkex.Conn | ||||||
| type CSHmacAlg uint32 | type CSHmacAlg uint32 | ||||||
							
								
								
									
										129
									
								
								xsnet/kcp.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,129 @@ | ||||||
|  | package xsnet | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"crypto/sha1" | ||||||
|  | 	"errors" | ||||||
|  | 	"fmt" | ||||||
|  | 	"net" | ||||||
|  | 
 | ||||||
|  | 	"blitter.com/go/xs/logger" | ||||||
|  | 	kcp "github.com/xtaci/kcp-go" | ||||||
|  | 	"golang.org/x/crypto/pbkdf2" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | const ( | ||||||
|  | 	KCP_NONE = iota | ||||||
|  | 	KCP_AES | ||||||
|  | 	KCP_BLOWFISH | ||||||
|  | 	KCP_CAST5 | ||||||
|  | 	KCP_SM4 | ||||||
|  | 	KCP_SALSA20 | ||||||
|  | 	KCP_SIMPLEXOR | ||||||
|  | 	KCP_TEA | ||||||
|  | 	KCP_3DES | ||||||
|  | 	KCP_TWOFISH | ||||||
|  | 	KCP_XTEA | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // for github.com/xtaci/kcp-go BlockCrypt alg selection | ||||||
|  | type KCPAlg uint8 | ||||||
|  | 
 | ||||||
|  | var ( | ||||||
|  | 	kcpKeyBytes  []byte = []byte("SET THIS") // symmetric crypto key for KCP (github.com/xtaci/kcp-go) if used | ||||||
|  | 	kcpSaltBytes []byte = []byte("ALSO SET THIS") | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func getKCPalgnum(extensions []string) (k KCPAlg) { | ||||||
|  | 	k = KCP_AES // default | ||||||
|  | 	var s string | ||||||
|  | 	for _, s = range extensions { | ||||||
|  | 		switch s { | ||||||
|  | 		case "KCP_NONE": | ||||||
|  | 			k = KCP_NONE | ||||||
|  | 			break //golint:ignore SA4011 out of for | ||||||
|  | 		case "KCP_AES": | ||||||
|  | 			k = KCP_AES | ||||||
|  | 			break //out of for | ||||||
|  | 		case "KCP_BLOWFISH": | ||||||
|  | 			k = KCP_BLOWFISH | ||||||
|  | 			break //out of for | ||||||
|  | 		case "KCP_CAST5": | ||||||
|  | 			k = KCP_CAST5 | ||||||
|  | 			break //out of for | ||||||
|  | 		case "KCP_SM4": | ||||||
|  | 			k = KCP_SM4 | ||||||
|  | 			break //out of for | ||||||
|  | 		case "KCP_SALSA20": | ||||||
|  | 			k = KCP_SALSA20 | ||||||
|  | 			break //out of for | ||||||
|  | 		case "KCP_SIMPLEXOR": | ||||||
|  | 			k = KCP_SIMPLEXOR | ||||||
|  | 			break //out of for | ||||||
|  | 		case "KCP_TEA": | ||||||
|  | 			k = KCP_TEA | ||||||
|  | 			break //out of for | ||||||
|  | 		case "KCP_3DES": | ||||||
|  | 			k = KCP_3DES | ||||||
|  | 			break //out of for | ||||||
|  | 		case "KCP_TWOFISH": | ||||||
|  | 			k = KCP_TWOFISH | ||||||
|  | 			break //out of for | ||||||
|  | 		case "KCP_XTEA": | ||||||
|  | 			k = KCP_XTEA | ||||||
|  | 			break //out of for | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	logger.LogDebug(fmt.Sprintf("[KCP BlockCrypt '%s' activated]", s)) | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func SetKCPKeyAndSalt(key []byte, salt []byte) { | ||||||
|  | 	kcpKeyBytes = key | ||||||
|  | 	kcpSaltBytes = salt | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func _newKCPBlockCrypt(key []byte, extensions []string) (b kcp.BlockCrypt, e error) { | ||||||
|  | 	switch getKCPalgnum(extensions) { | ||||||
|  | 	case KCP_NONE: | ||||||
|  | 		return kcp.NewNoneBlockCrypt(key) | ||||||
|  | 	case KCP_AES: | ||||||
|  | 		return kcp.NewAESBlockCrypt(key) | ||||||
|  | 	case KCP_BLOWFISH: | ||||||
|  | 		return kcp.NewBlowfishBlockCrypt(key) | ||||||
|  | 	case KCP_CAST5: | ||||||
|  | 		return kcp.NewCast5BlockCrypt(key) | ||||||
|  | 	case KCP_SM4: | ||||||
|  | 		return kcp.NewSM4BlockCrypt(key) | ||||||
|  | 	case KCP_SALSA20: | ||||||
|  | 		return kcp.NewSalsa20BlockCrypt(key) | ||||||
|  | 	case KCP_SIMPLEXOR: | ||||||
|  | 		return kcp.NewSimpleXORBlockCrypt(key) | ||||||
|  | 	case KCP_TEA: | ||||||
|  | 		return kcp.NewTEABlockCrypt(key) | ||||||
|  | 	case KCP_3DES: | ||||||
|  | 		return kcp.NewTripleDESBlockCrypt(key) | ||||||
|  | 	case KCP_TWOFISH: | ||||||
|  | 		return kcp.NewTwofishBlockCrypt(key) | ||||||
|  | 	case KCP_XTEA: | ||||||
|  | 		return kcp.NewXTEABlockCrypt(key) | ||||||
|  | 	} | ||||||
|  | 	return nil, errors.New("Invalid KCP BlockCrypto specified") | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func kcpDial(ipport string, extensions []string) (c net.Conn, err error) { | ||||||
|  | 	kcpKey := pbkdf2.Key(kcpKeyBytes, kcpSaltBytes, 1024, 32, sha1.New) | ||||||
|  | 	block, be := _newKCPBlockCrypt([]byte(kcpKey), extensions) | ||||||
|  | 	_ = be | ||||||
|  | 	return kcp.DialWithOptions(ipport, block, 10, 3) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func kcpListen(ipport string, extensions []string) (l net.Listener, err error) { | ||||||
|  | 	kcpKey := pbkdf2.Key(kcpKeyBytes, kcpSaltBytes, 1024, 32, sha1.New) | ||||||
|  | 	block, be := _newKCPBlockCrypt([]byte(kcpKey), extensions) | ||||||
|  | 	_ = be | ||||||
|  | 	return kcp.ListenWithOptions(ipport, block, 10, 3) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (hl *HKExListener) AcceptKCP() (c net.Conn, e error) { | ||||||
|  | 	return hl.l.(*kcp.Listener).AcceptKCP() | ||||||
|  | } | ||||||
|  | @ -1,12 +1,12 @@ | ||||||
| // hkextun.go - Tunnel setup using an established hkexnet.Conn | // hkextun.go - Tunnel setup using an established xsnet.Conn | ||||||
| 
 | 
 | ||||||
| // Copyright (c) 2017-2018 Russell Magee | // Copyright (c) 2017-2020 Russell Magee | ||||||
| // Licensed under the terms of the MIT license (see LICENSE.mit in this | // Licensed under the terms of the MIT license (see LICENSE.mit in this | ||||||
| // distribution) | // distribution) | ||||||
| // | // | ||||||
| // golang implementation by Russ Magee (rmagee_at_gmail.com) | // golang implementation by Russ Magee (rmagee_at_gmail.com) | ||||||
| 
 | 
 | ||||||
| package hkexnet | package xsnet | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"bytes" | 	"bytes" | ||||||
|  | @ -18,7 +18,7 @@ import ( | ||||||
| 	"sync" | 	"sync" | ||||||
| 	"time" | 	"time" | ||||||
| 
 | 
 | ||||||
| 	"blitter.com/go/hkexsh/logger" | 	"blitter.com/go/xs/logger" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| type ( | type ( | ||||||
|  | @ -46,7 +46,7 @@ type ( | ||||||
| 		Lport     uint16    // ... ie., RPort is on server, LPort is on client | 		Lport     uint16    // ... ie., RPort is on server, LPort is on client | ||||||
| 		Peer      string    //net.Addr | 		Peer      string    //net.Addr | ||||||
| 		Died      bool      // set by client upon receipt of a CSOTunDisconn | 		Died      bool      // set by client upon receipt of a CSOTunDisconn | ||||||
| 		KeepAlive uint      // must be reset by client to keep server dial() alive | 		KeepAlive uint32    // must be reset by client to keep server dial() alive | ||||||
| 		Ctl       chan rune //See TunCtl_* consts | 		Ctl       chan rune //See TunCtl_* consts | ||||||
| 		Data      chan []byte | 		Data      chan []byte | ||||||
| 	} | 	} | ||||||
|  | @ -67,6 +67,8 @@ func (hc *Conn) CollapseAllTunnels(client bool) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (hc *Conn) InitTunEndpoint(lp uint16, p string /* net.Addr */, rp uint16) { | func (hc *Conn) InitTunEndpoint(lp uint16, p string /* net.Addr */, rp uint16) { | ||||||
|  | 	hc.Lock() | ||||||
|  | 	defer hc.Unlock() | ||||||
| 	if (*hc.tuns) == nil { | 	if (*hc.tuns) == nil { | ||||||
| 		(*hc.tuns) = make(map[uint16]*TunEndpoint) | 		(*hc.tuns) = make(map[uint16]*TunEndpoint) | ||||||
| 	} | 	} | ||||||
|  | @ -87,6 +89,7 @@ func (hc *Conn) InitTunEndpoint(lp uint16, p string /* net.Addr */, rp uint16) { | ||||||
| 			// data channel removed on closure. Re-create it | 			// data channel removed on closure. Re-create it | ||||||
| 			(*hc.tuns)[rp].Data = make(chan []byte, 1) | 			(*hc.tuns)[rp].Data = make(chan []byte, 1) | ||||||
| 		} | 		} | ||||||
|  | 		(*hc.tuns)[rp].KeepAlive = 0 | ||||||
| 		(*hc.tuns)[rp].Died = false | 		(*hc.tuns)[rp].Died = false | ||||||
| 	} | 	} | ||||||
| 	return | 	return | ||||||
|  | @ -149,34 +152,23 @@ func (hc *Conn) StartClientTunnel(lport, rport uint16) { | ||||||
| 										if e == io.EOF { | 										if e == io.EOF { | ||||||
| 											logger.LogDebug(fmt.Sprintf("[ClientTun] worker A: lport Disconnected: shutting down tunnel %v", (*hc.tuns)[rport])) | 											logger.LogDebug(fmt.Sprintf("[ClientTun] worker A: lport Disconnected: shutting down tunnel %v", (*hc.tuns)[rport])) | ||||||
| 											// if Died was already set, server-side already is gone. | 											// if Died was already set, server-side already is gone. | ||||||
| 											if !(*hc.tuns)[rport].Died { | 											if hc.TunIsAlive(rport) { | ||||||
| 												hc.WritePacket(tunDst.Bytes(), CSOTunHangup) | 												hc.WritePacket(tunDst.Bytes(), CSOTunHangup) | ||||||
| 											} | 											} | ||||||
| 											(*hc.tuns)[rport].Died = true | 											hc.ShutdownTun(rport) // FIXME: race-C | ||||||
| 											if (*hc.tuns)[rport].Data != nil { |  | ||||||
| 												close((*hc.tuns)[rport].Data) |  | ||||||
| 												(*hc.tuns)[rport].Data = nil |  | ||||||
| 											} |  | ||||||
| 											break | 											break | ||||||
| 										} else if strings.Contains(e.Error(), "i/o timeout") { | 										} else if strings.Contains(e.Error(), "i/o timeout") { | ||||||
| 											if (*hc.tuns)[rport].Died { | 											if !hc.TunIsAlive(rport) { | ||||||
| 												logger.LogDebug(fmt.Sprintf("[ClientTun] worker A: timeout: Server side died, hanging up %v", (*hc.tuns)[rport])) | 												logger.LogDebug(fmt.Sprintf("[ClientTun] worker A: timeout: Server side died, hanging up %v", (*hc.tuns)[rport])) | ||||||
| 												if (*hc.tuns)[rport].Data != nil { | 												hc.ShutdownTun(rport) | ||||||
| 													close((*hc.tuns)[rport].Data) |  | ||||||
| 													(*hc.tuns)[rport].Data = nil |  | ||||||
| 												} |  | ||||||
| 												break | 												break | ||||||
| 											} | 											} | ||||||
| 										} else { | 										} else { | ||||||
| 											logger.LogDebug(fmt.Sprintf("[ClientTun] worker A: Read error from lport of tun %v\n%s", (*hc.tuns)[rport], e)) | 											logger.LogDebug(fmt.Sprintf("[ClientTun] worker A: Read error from lport of tun %v\n%s", (*hc.tuns)[rport], e)) | ||||||
| 											if !(*hc.tuns)[rport].Died { | 											if hc.TunIsAlive(rport) { | ||||||
| 												hc.WritePacket(tunDst.Bytes(), CSOTunHangup) | 												hc.WritePacket(tunDst.Bytes(), CSOTunHangup) | ||||||
| 											} | 											} | ||||||
| 											(*hc.tuns)[rport].Died = true | 											hc.ShutdownTun(rport) | ||||||
| 											if (*hc.tuns)[rport].Data != nil { |  | ||||||
| 												close((*hc.tuns)[rport].Data) |  | ||||||
| 												(*hc.tuns)[rport].Data = nil |  | ||||||
| 											} |  | ||||||
| 											break | 											break | ||||||
| 										} | 										} | ||||||
| 									} | 									} | ||||||
|  | @ -207,7 +199,7 @@ func (hc *Conn) StartClientTunnel(lport, rport uint16) { | ||||||
| 								logger.LogDebug("[ClientTun] worker B: starting") | 								logger.LogDebug("[ClientTun] worker B: starting") | ||||||
| 
 | 
 | ||||||
| 								for { | 								for { | ||||||
| 									bytes, ok := <-(*hc.tuns)[rport].Data | 									bytes, ok := <-(*hc.tuns)[rport].Data // FIXME: race-C w/ShutdownTun calls | ||||||
| 									if ok { | 									if ok { | ||||||
| 										c.SetWriteDeadline(time.Now().Add(200 * time.Millisecond)) | 										c.SetWriteDeadline(time.Now().Add(200 * time.Millisecond)) | ||||||
| 										_, e := c.Write(bytes) | 										_, e := c.Write(bytes) | ||||||
|  | @ -229,7 +221,7 @@ func (hc *Conn) StartClientTunnel(lport, rport uint16) { | ||||||
| 						// When both workers have exited due to a disconnect or other | 						// When both workers have exited due to a disconnect or other | ||||||
| 						// condition, it's safe to remove the tunnel descriptor. | 						// condition, it's safe to remove the tunnel descriptor. | ||||||
| 						logger.LogDebug("[ClientTun] workers exited") | 						logger.LogDebug("[ClientTun] workers exited") | ||||||
| 						delete((*hc.tuns), rport) | 						hc.ShutdownTun(rport) | ||||||
| 					} // end for-accept | 					} // end for-accept | ||||||
| 				} // end Listen() block | 				} // end Listen() block | ||||||
| 			} | 			} | ||||||
|  | @ -237,6 +229,50 @@ func (hc *Conn) StartClientTunnel(lport, rport uint16) { | ||||||
| 	}() | 	}() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func (hc *Conn) AgeTunnel(endp uint16) uint32 { | ||||||
|  | 	hc.Lock() | ||||||
|  | 	defer hc.Unlock() | ||||||
|  | 	(*hc.tuns)[endp].KeepAlive += 1 | ||||||
|  | 	return (*hc.tuns)[endp].KeepAlive | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (hc *Conn) ResetTunnelAge(endp uint16) { | ||||||
|  | 	hc.Lock() | ||||||
|  | 	defer hc.Unlock() | ||||||
|  | 	(*hc.tuns)[endp].KeepAlive = 0 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (hc *Conn) TunIsNil(endp uint16) bool { | ||||||
|  | 	hc.Lock() | ||||||
|  | 	defer hc.Unlock() | ||||||
|  | 	return (*hc.tuns)[endp] == nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (hc *Conn) TunIsAlive(endp uint16) bool { | ||||||
|  | 	hc.Lock() | ||||||
|  | 	defer hc.Unlock() | ||||||
|  | 	return !(*hc.tuns)[endp].Died | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (hc *Conn) MarkTunDead(endp uint16) { | ||||||
|  | 	hc.Lock() | ||||||
|  | 	defer hc.Unlock() | ||||||
|  | 	(*hc.tuns)[endp].Died = true | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (hc *Conn) ShutdownTun(endp uint16) { | ||||||
|  | 	hc.Lock() | ||||||
|  | 	defer hc.Unlock() | ||||||
|  | 	if (*hc.tuns)[endp] != nil { | ||||||
|  | 		(*hc.tuns)[endp].Died = true | ||||||
|  | 		if (*hc.tuns)[endp].Data != nil { | ||||||
|  | 			close((*hc.tuns)[endp].Data) | ||||||
|  | 			(*hc.tuns)[endp].Data = nil | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	delete((*hc.tuns), endp) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func (hc *Conn) StartServerTunnel(lport, rport uint16) { | func (hc *Conn) StartServerTunnel(lport, rport uint16) { | ||||||
| 	hc.InitTunEndpoint(lport, "", rport) | 	hc.InitTunEndpoint(lport, "", rport) | ||||||
| 	var err error | 	var err error | ||||||
|  | @ -244,12 +280,34 @@ func (hc *Conn) StartServerTunnel(lport, rport uint16) { | ||||||
| 	go func() { | 	go func() { | ||||||
| 		var wg sync.WaitGroup | 		var wg sync.WaitGroup | ||||||
| 
 | 
 | ||||||
|  | 		// | ||||||
|  | 		// worker to age server tunnel and kill it if keepalives | ||||||
|  | 		// stop from client | ||||||
|  | 		// | ||||||
|  | 		wg.Add(1) | ||||||
|  | 		go func() { | ||||||
|  | 			defer wg.Done() | ||||||
|  | 			for { | ||||||
|  | 				time.Sleep(100 * time.Millisecond) | ||||||
|  | 				if hc.TunIsNil(rport) { | ||||||
|  | 					logger.LogDebug("[ServerTun] worker A: Client endpoint removed.") | ||||||
|  | 					break | ||||||
|  | 				} | ||||||
|  | 				age := hc.AgeTunnel(rport) | ||||||
|  | 				if age > 25 { | ||||||
|  | 					hc.MarkTunDead(rport) | ||||||
|  | 					logger.LogDebug("[ServerTun] worker A: Client died, hanging up.") | ||||||
|  | 					break | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		}() | ||||||
|  | 
 | ||||||
| 		for cmd := range (*hc.tuns)[rport].Ctl { | 		for cmd := range (*hc.tuns)[rport].Ctl { | ||||||
| 			var c net.Conn | 			var c net.Conn | ||||||
| 			logger.LogDebug(fmt.Sprintf("[ServerTun] got Ctl '%c'.", cmd)) | 			logger.LogDebug(fmt.Sprintf("[ServerTun] got Ctl '%c'.", cmd)) | ||||||
| 			if cmd == 'd' { | 			if cmd == 'd' { | ||||||
| 				// if re-using tunnel, re-init it | 				// if re-using tunnel, re-init it | ||||||
| 				if (*hc.tuns)[rport] == nil { | 				if hc.TunIsNil(rport) { | ||||||
| 					hc.InitTunEndpoint(lport, "", rport) | 					hc.InitTunEndpoint(lport, "", rport) | ||||||
| 				} | 				} | ||||||
| 				logger.LogDebug("[ServerTun] dialling...") | 				logger.LogDebug("[ServerTun] dialling...") | ||||||
|  | @ -294,34 +352,23 @@ func (hc *Conn) StartServerTunnel(lport, rport uint16) { | ||||||
| 							if e != nil { | 							if e != nil { | ||||||
| 								if e == io.EOF { | 								if e == io.EOF { | ||||||
| 									logger.LogDebug(fmt.Sprintf("[ServerTun] worker A: rport Disconnected: shutting down tunnel %v", (*hc.tuns)[rport])) | 									logger.LogDebug(fmt.Sprintf("[ServerTun] worker A: rport Disconnected: shutting down tunnel %v", (*hc.tuns)[rport])) | ||||||
| 									if !(*hc.tuns)[rport].Died { | 									if hc.TunIsAlive(rport) { | ||||||
| 										hc.WritePacket(tunDst.Bytes(), CSOTunDisconn) | 										hc.WritePacket(tunDst.Bytes(), CSOTunDisconn) | ||||||
| 									} | 									} | ||||||
| 									(*hc.tuns)[rport].Died = true | 									hc.ShutdownTun(rport) // FIXME: race-A | ||||||
| 									if (*hc.tuns)[rport].Data != nil { |  | ||||||
| 										close((*hc.tuns)[rport].Data) |  | ||||||
| 										(*hc.tuns)[rport].Data = nil |  | ||||||
| 									} |  | ||||||
| 									break | 									break | ||||||
| 								} else if strings.Contains(e.Error(), "i/o timeout") { | 								} else if strings.Contains(e.Error(), "i/o timeout") { | ||||||
| 									if (*hc.tuns)[rport].Died { | 									if !hc.TunIsAlive(rport) { | ||||||
| 										logger.LogDebug(fmt.Sprintf("[ServerTun] worker A: timeout: Server side died, hanging up %v", (*hc.tuns)[rport])) | 										logger.LogDebug(fmt.Sprintf("[ServerTun] worker A: timeout: Server side died, hanging up %v", (*hc.tuns)[rport])) | ||||||
| 										if (*hc.tuns)[rport].Data != nil { | 										hc.ShutdownTun(rport) // FIXME: race-B | ||||||
| 											close((*hc.tuns)[rport].Data) |  | ||||||
| 											(*hc.tuns)[rport].Data = nil |  | ||||||
| 										} |  | ||||||
| 										break | 										break | ||||||
| 									} | 									} | ||||||
| 								} else { | 								} else { | ||||||
| 									logger.LogDebug(fmt.Sprintf("[ServerTun] worker A: Read error from rport of tun %v: %s", (*hc.tuns)[rport], e)) | 									logger.LogDebug(fmt.Sprintf("[ServerTun] worker A: Read error from rport of tun %v: %s", (*hc.tuns)[rport], e)) | ||||||
| 									if !(*hc.tuns)[rport].Died { | 									if hc.TunIsAlive(rport) { | ||||||
| 										hc.WritePacket(tunDst.Bytes(), CSOTunDisconn) | 										hc.WritePacket(tunDst.Bytes(), CSOTunDisconn) | ||||||
| 									} | 									} | ||||||
| 									(*hc.tuns)[rport].Died = true | 									hc.ShutdownTun(rport) // FIXME: race-C | ||||||
| 									if (*hc.tuns)[rport].Data != nil { |  | ||||||
| 										close((*hc.tuns)[rport].Data) |  | ||||||
| 										(*hc.tuns)[rport].Data = nil |  | ||||||
| 									} |  | ||||||
| 									break | 									break | ||||||
| 								} | 								} | ||||||
| 							} | 							} | ||||||
|  | @ -329,14 +376,6 @@ func (hc *Conn) StartServerTunnel(lport, rport uint16) { | ||||||
| 								rBuf = append(tunDst.Bytes(), rBuf[:n]...) | 								rBuf = append(tunDst.Bytes(), rBuf[:n]...) | ||||||
| 								hc.WritePacket(rBuf[:n+4], CSOTunData) | 								hc.WritePacket(rBuf[:n+4], CSOTunData) | ||||||
| 							} | 							} | ||||||
| 
 |  | ||||||
| 							if (*hc.tuns)[rport].KeepAlive > 50 { |  | ||||||
| 									(*hc.tuns)[rport].Died = true |  | ||||||
| 									logger.LogDebug("[ServerTun] worker A: Client died, hanging up.") |  | ||||||
| 							} else { |  | ||||||
| 								(*hc.tuns)[rport].KeepAlive += 1 |  | ||||||
| 							} |  | ||||||
| 
 |  | ||||||
| 						} | 						} | ||||||
| 						logger.LogDebug("[ServerTun] worker A: exiting") | 						logger.LogDebug("[ServerTun] worker A: exiting") | ||||||
| 					}() | 					}() | ||||||
|  | @ -354,7 +393,7 @@ func (hc *Conn) StartServerTunnel(lport, rport uint16) { | ||||||
| 
 | 
 | ||||||
| 						logger.LogDebug("[ServerTun] worker B: starting") | 						logger.LogDebug("[ServerTun] worker B: starting") | ||||||
| 						for { | 						for { | ||||||
| 							rData, ok := <-(*hc.tuns)[rport].Data | 							rData, ok := <-(*hc.tuns)[rport].Data // FIXME: race-A, race-B, race-C (w/ShutdownTun() calls) | ||||||
| 							if ok { | 							if ok { | ||||||
| 								c.SetWriteDeadline(time.Now().Add(200 * time.Millisecond)) | 								c.SetWriteDeadline(time.Now().Add(200 * time.Millisecond)) | ||||||
| 								_, e := c.Write(rData) | 								_, e := c.Write(rData) | ||||||
|  | @ -4,13 +4,13 @@ EXTPKGS = bytes,errors,flag,fmt,internal,io,log,net,os,path,runtime,time,strings | ||||||
| EXE = $(notdir $(shell pwd)) | EXE = $(notdir $(shell pwd)) | ||||||
| 
 | 
 | ||||||
| all: | all: | ||||||
| 	go build . | 	go build $(BUILDOPTS) . | ||||||
| 
 | 
 | ||||||
| clean: | clean: | ||||||
| 	$(RM) $(EXE) $(EXE).exe | 	$(RM) $(EXE) $(EXE).exe | ||||||
| 
 | 
 | ||||||
| vis: | vis: | ||||||
| 	go-callvis -format png -file hkexpasswd-vis -ignore $(EXTPKGS) -group pkg,type . | 	go-callvis -format png -file xspasswd-vis -ignore $(EXTPKGS) -group pkg,type . | ||||||
| 
 | 
 | ||||||
| lint: | lint: | ||||||
| 	-gometalinter --deadline=60s | sort | 	-golangci-lint run | ||||||
							
								
								
									
										
											BIN
										
									
								
								xspasswd/xspasswd-vis.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 34 KiB | 
|  | @ -1,7 +1,7 @@ | ||||||
| // Util to generate/store passwords for users in a file akin to /etc/passwd | // 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 demo hkexsh server, using bcrypt. | ||||||
| // | // | ||||||
| // Copyright (c) 2017-2018 Russell Magee | // Copyright (c) 2017-2020 Russell Magee | ||||||
| // Licensed under the terms of the MIT license (see LICENSE.mit in this | // Licensed under the terms of the MIT license (see LICENSE.mit in this | ||||||
| // distribution) | // distribution) | ||||||
| // | // | ||||||
|  | @ -17,21 +17,33 @@ import ( | ||||||
| 	"log" | 	"log" | ||||||
| 	"os" | 	"os" | ||||||
| 
 | 
 | ||||||
| 	hkexsh "blitter.com/go/hkexsh" | 	xs "blitter.com/go/xs" | ||||||
| 	"github.com/jameskeane/bcrypt" | 	"github.com/jameskeane/bcrypt" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | var ( | ||||||
|  | 	version   string | ||||||
|  | 	gitCommit string | ||||||
|  | ) | ||||||
|  | 
 | ||||||
| // nolint: gocyclo | // nolint: gocyclo | ||||||
| func main() { | func main() { | ||||||
|  | 	var vopt bool | ||||||
| 	var pfName string | 	var pfName string | ||||||
| 	var newpw string | 	var newpw string | ||||||
| 	var confirmpw string | 	var confirmpw string | ||||||
| 	var userName string | 	var userName string | ||||||
| 
 | 
 | ||||||
|  | 	flag.BoolVar(&vopt, "v", false, "show version") | ||||||
| 	flag.StringVar(&userName, "u", "", "username") | 	flag.StringVar(&userName, "u", "", "username") | ||||||
| 	flag.StringVar(&pfName, "f", "/etc/hkexsh.passwd", "passwd file") | 	flag.StringVar(&pfName, "f", "/etc/xs.passwd", "passwd file") | ||||||
| 	flag.Parse() | 	flag.Parse() | ||||||
| 
 | 
 | ||||||
|  | 	if vopt { | ||||||
|  | 		fmt.Printf("version %s (%s)\n", version, gitCommit) | ||||||
|  | 		os.Exit(0) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	var uname string | 	var uname string | ||||||
| 	if len(userName) == 0 { | 	if len(userName) == 0 { | ||||||
| 		log.Println("specify username with -u") | 		log.Println("specify username with -u") | ||||||
|  | @ -47,7 +59,7 @@ func main() { | ||||||
| 	uname = userName | 	uname = userName | ||||||
| 
 | 
 | ||||||
| 	fmt.Printf("New Password:") | 	fmt.Printf("New Password:") | ||||||
| 	ab, err := hkexsh.ReadPassword(int(os.Stdin.Fd())) | 	ab, err := xs.ReadPassword(os.Stdin.Fd()) | ||||||
| 	fmt.Printf("\r\n") | 	fmt.Printf("\r\n") | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Fatal(err) | 		log.Fatal(err) | ||||||
|  | @ -56,7 +68,7 @@ func main() { | ||||||
| 	newpw = string(ab) | 	newpw = string(ab) | ||||||
| 
 | 
 | ||||||
| 	fmt.Printf("Confirm:") | 	fmt.Printf("Confirm:") | ||||||
| 	ab, err = hkexsh.ReadPassword(int(os.Stdin.Fd())) | 	ab, err = xs.ReadPassword(os.Stdin.Fd()) | ||||||
| 	fmt.Printf("\r\n") | 	fmt.Printf("\r\n") | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Fatal(err) | 		log.Fatal(err) | ||||||
|  | @ -118,7 +130,7 @@ func main() { | ||||||
| 		records = append(records, newRec) | 		records = append(records, newRec) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	outFile, err := ioutil.TempFile("", "hkexsh-passwd") | 	outFile, err := ioutil.TempFile("", "xs-passwd") | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Fatal(err) | 		log.Fatal(err) | ||||||
| 	} | 	} | ||||||