SSH connection status indicators
- Add Connected bool field to vfs.Entry and RemoteMount - Track connection status in sshState.connectedHosts - Show status icon (connected/disconnected) in pane header when browsing remote host - Async SSH connection test with cancel support for Add Host dialog - Colored labels and styled help text in SSH dialogs - Confirmation dialog when deleting manually-added SSH hosts
This commit is contained in:
parent
df4df6b8f6
commit
1ed2d3defb
224 changed files with 33447 additions and 236 deletions
27
vendor/github.com/kr/fs/LICENSE
generated
vendored
Normal file
27
vendor/github.com/kr/fs/LICENSE
generated
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
Copyright (c) 2012 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
3
vendor/github.com/kr/fs/Readme
generated
vendored
Normal file
3
vendor/github.com/kr/fs/Readme
generated
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
Filesystem Package
|
||||
|
||||
http://godoc.org/github.com/kr/fs
|
||||
36
vendor/github.com/kr/fs/filesystem.go
generated
vendored
Normal file
36
vendor/github.com/kr/fs/filesystem.go
generated
vendored
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
package fs
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// FileSystem defines the methods of an abstract filesystem.
|
||||
type FileSystem interface {
|
||||
|
||||
// ReadDir reads the directory named by dirname and returns a
|
||||
// list of directory entries.
|
||||
ReadDir(dirname string) ([]os.FileInfo, error)
|
||||
|
||||
// Lstat returns a FileInfo describing the named file. If the file is a
|
||||
// symbolic link, the returned FileInfo describes the symbolic link. Lstat
|
||||
// makes no attempt to follow the link.
|
||||
Lstat(name string) (os.FileInfo, error)
|
||||
|
||||
// Join joins any number of path elements into a single path, adding a
|
||||
// separator if necessary. The result is Cleaned; in particular, all
|
||||
// empty strings are ignored.
|
||||
//
|
||||
// The separator is FileSystem specific.
|
||||
Join(elem ...string) string
|
||||
}
|
||||
|
||||
// fs represents a FileSystem provided by the os package.
|
||||
type fs struct{}
|
||||
|
||||
func (f *fs) ReadDir(dirname string) ([]os.FileInfo, error) { return ioutil.ReadDir(dirname) }
|
||||
|
||||
func (f *fs) Lstat(name string) (os.FileInfo, error) { return os.Lstat(name) }
|
||||
|
||||
func (f *fs) Join(elem ...string) string { return filepath.Join(elem...) }
|
||||
95
vendor/github.com/kr/fs/walk.go
generated
vendored
Normal file
95
vendor/github.com/kr/fs/walk.go
generated
vendored
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
// Package fs provides filesystem-related functions.
|
||||
package fs
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
// Walker provides a convenient interface for iterating over the
|
||||
// descendants of a filesystem path.
|
||||
// Successive calls to the Step method will step through each
|
||||
// file or directory in the tree, including the root. The files
|
||||
// are walked in lexical order, which makes the output deterministic
|
||||
// but means that for very large directories Walker can be inefficient.
|
||||
// Walker does not follow symbolic links.
|
||||
type Walker struct {
|
||||
fs FileSystem
|
||||
cur item
|
||||
stack []item
|
||||
descend bool
|
||||
}
|
||||
|
||||
type item struct {
|
||||
path string
|
||||
info os.FileInfo
|
||||
err error
|
||||
}
|
||||
|
||||
// Walk returns a new Walker rooted at root.
|
||||
func Walk(root string) *Walker {
|
||||
return WalkFS(root, new(fs))
|
||||
}
|
||||
|
||||
// WalkFS returns a new Walker rooted at root on the FileSystem fs.
|
||||
func WalkFS(root string, fs FileSystem) *Walker {
|
||||
info, err := fs.Lstat(root)
|
||||
return &Walker{
|
||||
fs: fs,
|
||||
stack: []item{{root, info, err}},
|
||||
}
|
||||
}
|
||||
|
||||
// Step advances the Walker to the next file or directory,
|
||||
// which will then be available through the Path, Stat,
|
||||
// and Err methods.
|
||||
// It returns false when the walk stops at the end of the tree.
|
||||
func (w *Walker) Step() bool {
|
||||
if w.descend && w.cur.err == nil && w.cur.info.IsDir() {
|
||||
list, err := w.fs.ReadDir(w.cur.path)
|
||||
if err != nil {
|
||||
w.cur.err = err
|
||||
w.stack = append(w.stack, w.cur)
|
||||
} else {
|
||||
for i := len(list) - 1; i >= 0; i-- {
|
||||
path := w.fs.Join(w.cur.path, list[i].Name())
|
||||
w.stack = append(w.stack, item{path, list[i], nil})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(w.stack) == 0 {
|
||||
return false
|
||||
}
|
||||
i := len(w.stack) - 1
|
||||
w.cur = w.stack[i]
|
||||
w.stack = w.stack[:i]
|
||||
w.descend = true
|
||||
return true
|
||||
}
|
||||
|
||||
// Path returns the path to the most recent file or directory
|
||||
// visited by a call to Step. It contains the argument to Walk
|
||||
// as a prefix; that is, if Walk is called with "dir", which is
|
||||
// a directory containing the file "a", Path will return "dir/a".
|
||||
func (w *Walker) Path() string {
|
||||
return w.cur.path
|
||||
}
|
||||
|
||||
// Stat returns info for the most recent file or directory
|
||||
// visited by a call to Step.
|
||||
func (w *Walker) Stat() os.FileInfo {
|
||||
return w.cur.info
|
||||
}
|
||||
|
||||
// Err returns the error, if any, for the most recent attempt
|
||||
// by Step to visit a file or directory. If a directory has
|
||||
// an error, w will not descend into that directory.
|
||||
func (w *Walker) Err() error {
|
||||
return w.cur.err
|
||||
}
|
||||
|
||||
// SkipDir causes the currently visited directory to be skipped.
|
||||
// If w is not on a directory, SkipDir has no effect.
|
||||
func (w *Walker) SkipDir() {
|
||||
w.descend = false
|
||||
}
|
||||
10
vendor/github.com/pkg/sftp/.gitignore
generated
vendored
Normal file
10
vendor/github.com/pkg/sftp/.gitignore
generated
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
.*.swo
|
||||
.*.swp
|
||||
|
||||
server_standalone/server_standalone
|
||||
|
||||
examples/*/id_rsa
|
||||
examples/*/id_rsa.pub
|
||||
|
||||
memprofile.out
|
||||
memprofile.svg
|
||||
3
vendor/github.com/pkg/sftp/CONTRIBUTORS
generated
vendored
Normal file
3
vendor/github.com/pkg/sftp/CONTRIBUTORS
generated
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
Dave Cheney <dave@cheney.net>
|
||||
Saulius Gurklys <s4uliu5@gmail.com>
|
||||
John Eikenberry <jae@zhar.net>
|
||||
9
vendor/github.com/pkg/sftp/LICENSE
generated
vendored
Normal file
9
vendor/github.com/pkg/sftp/LICENSE
generated
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
Copyright (c) 2013, Dave Cheney
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
27
vendor/github.com/pkg/sftp/Makefile
generated
vendored
Normal file
27
vendor/github.com/pkg/sftp/Makefile
generated
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
.PHONY: integration integration_w_race benchmark
|
||||
|
||||
integration:
|
||||
go test -integration -v ./...
|
||||
go test -testserver -v ./...
|
||||
go test -integration -testserver -v ./...
|
||||
go test -integration -allocator -v ./...
|
||||
go test -testserver -allocator -v ./...
|
||||
go test -integration -testserver -allocator -v ./...
|
||||
|
||||
integration_w_race:
|
||||
go test -race -integration -v ./...
|
||||
go test -race -testserver -v ./...
|
||||
go test -race -integration -testserver -v ./...
|
||||
go test -race -integration -allocator -v ./...
|
||||
go test -race -testserver -allocator -v ./...
|
||||
go test -race -integration -allocator -testserver -v ./...
|
||||
|
||||
COUNT ?= 1
|
||||
BENCHMARK_PATTERN ?= "."
|
||||
|
||||
benchmark:
|
||||
go test -integration -run=NONE -bench=$(BENCHMARK_PATTERN) -benchmem -count=$(COUNT)
|
||||
|
||||
benchmark_w_memprofile:
|
||||
go test -integration -run=NONE -bench=$(BENCHMARK_PATTERN) -benchmem -count=$(COUNT) -memprofile memprofile.out
|
||||
go tool pprof -svg -output=memprofile.svg memprofile.out
|
||||
44
vendor/github.com/pkg/sftp/README.md
generated
vendored
Normal file
44
vendor/github.com/pkg/sftp/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
sftp
|
||||
----
|
||||
|
||||
The `sftp` package provides support for file system operations on remote ssh
|
||||
servers using the SFTP subsystem. It also implements an SFTP server for serving
|
||||
files from the filesystem.
|
||||
|
||||
 [](https://pkg.go.dev/github.com/pkg/sftp)
|
||||
|
||||
usage and examples
|
||||
------------------
|
||||
|
||||
See [https://pkg.go.dev/github.com/pkg/sftp](https://pkg.go.dev/github.com/pkg/sftp) for
|
||||
examples and usage.
|
||||
|
||||
The basic operation of the package mirrors the facilities of the
|
||||
[os](http://golang.org/pkg/os) package.
|
||||
|
||||
The Walker interface for directory traversal is heavily inspired by Keith
|
||||
Rarick's [fs](https://pkg.go.dev/github.com/kr/fs) package.
|
||||
|
||||
roadmap
|
||||
-------
|
||||
|
||||
* There is way too much duplication in the Client methods. If there was an
|
||||
unmarshal(interface{}) method this would reduce a heap of the duplication.
|
||||
|
||||
contributing
|
||||
------------
|
||||
|
||||
We welcome pull requests, bug fixes and issue reports.
|
||||
|
||||
Before proposing a large change, first please discuss your change by raising an
|
||||
issue.
|
||||
|
||||
For API/code bugs, please include a small, self contained code example to
|
||||
reproduce the issue. For pull requests, remember test coverage.
|
||||
|
||||
We try to handle issues and pull requests with a 0 open philosophy. That means
|
||||
we will try to address the submission as soon as possible and will work toward
|
||||
a resolution. If progress can no longer be made (eg. unreproducible bug) or
|
||||
stops (eg. unresponsive submitter), we will close the bug.
|
||||
|
||||
Thanks.
|
||||
13
vendor/github.com/pkg/sftp/SECURITY.md
generated
vendored
Normal file
13
vendor/github.com/pkg/sftp/SECURITY.md
generated
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
|
||||
Security updates are provided for the latest released version of this package.
|
||||
We also welcome vulnerability reports for the development version to help us ensure it is secure before the next release.
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
If you believe you’ve found a security vulnerability in this project, we strongly encourage you to report it privately using GitHub’s [security advisory system](https://github.com/pkg/sftp/security/advisories/new).
|
||||
This will allow us to review and address the issue before public disclosure.
|
||||
|
||||
Thank you for helping us keep the project secure.
|
||||
96
vendor/github.com/pkg/sftp/allocator.go
generated
vendored
Normal file
96
vendor/github.com/pkg/sftp/allocator.go
generated
vendored
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
package sftp
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
type allocator struct {
|
||||
sync.Mutex
|
||||
available [][]byte
|
||||
// map key is the request order
|
||||
used map[uint32][][]byte
|
||||
}
|
||||
|
||||
func newAllocator() *allocator {
|
||||
return &allocator{
|
||||
// micro optimization: initialize available pages with an initial capacity
|
||||
available: make([][]byte, 0, SftpServerWorkerCount*2),
|
||||
used: make(map[uint32][][]byte),
|
||||
}
|
||||
}
|
||||
|
||||
// GetPage returns a previously allocated and unused []byte or create a new one.
|
||||
// The slice have a fixed size = maxMsgLength, this value is suitable for both
|
||||
// receiving new packets and reading the files to serve
|
||||
func (a *allocator) GetPage(requestOrderID uint32) []byte {
|
||||
a.Lock()
|
||||
defer a.Unlock()
|
||||
|
||||
var result []byte
|
||||
|
||||
// get an available page and remove it from the available ones.
|
||||
if len(a.available) > 0 {
|
||||
truncLength := len(a.available) - 1
|
||||
result = a.available[truncLength]
|
||||
|
||||
a.available[truncLength] = nil // clear out the internal pointer
|
||||
a.available = a.available[:truncLength] // truncate the slice
|
||||
}
|
||||
|
||||
// no preallocated slice found, just allocate a new one
|
||||
if result == nil {
|
||||
result = make([]byte, maxMsgLength)
|
||||
}
|
||||
|
||||
// put result in used pages
|
||||
a.used[requestOrderID] = append(a.used[requestOrderID], result)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// ReleasePages marks unused all pages in use for the given requestID
|
||||
func (a *allocator) ReleasePages(requestOrderID uint32) {
|
||||
a.Lock()
|
||||
defer a.Unlock()
|
||||
|
||||
if used := a.used[requestOrderID]; len(used) > 0 {
|
||||
a.available = append(a.available, used...)
|
||||
}
|
||||
delete(a.used, requestOrderID)
|
||||
}
|
||||
|
||||
// Free removes all the used and available pages.
|
||||
// Call this method when the allocator is not needed anymore
|
||||
func (a *allocator) Free() {
|
||||
a.Lock()
|
||||
defer a.Unlock()
|
||||
|
||||
a.available = nil
|
||||
a.used = make(map[uint32][][]byte)
|
||||
}
|
||||
|
||||
func (a *allocator) countUsedPages() int {
|
||||
a.Lock()
|
||||
defer a.Unlock()
|
||||
|
||||
num := 0
|
||||
for _, p := range a.used {
|
||||
num += len(p)
|
||||
}
|
||||
return num
|
||||
}
|
||||
|
||||
func (a *allocator) countAvailablePages() int {
|
||||
a.Lock()
|
||||
defer a.Unlock()
|
||||
|
||||
return len(a.available)
|
||||
}
|
||||
|
||||
func (a *allocator) isRequestOrderIDUsed(requestOrderID uint32) bool {
|
||||
a.Lock()
|
||||
defer a.Unlock()
|
||||
|
||||
_, ok := a.used[requestOrderID]
|
||||
return ok
|
||||
}
|
||||
136
vendor/github.com/pkg/sftp/attrs.go
generated
vendored
Normal file
136
vendor/github.com/pkg/sftp/attrs.go
generated
vendored
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
package sftp
|
||||
|
||||
// ssh_FXP_ATTRS support
|
||||
// see https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-02.txt#section-5
|
||||
|
||||
import (
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
sshFileXferAttrSize = 0x00000001
|
||||
sshFileXferAttrUIDGID = 0x00000002
|
||||
sshFileXferAttrPermissions = 0x00000004
|
||||
sshFileXferAttrACmodTime = 0x00000008
|
||||
sshFileXferAttrExtended = 0x80000000
|
||||
|
||||
sshFileXferAttrAll = sshFileXferAttrSize | sshFileXferAttrUIDGID | sshFileXferAttrPermissions |
|
||||
sshFileXferAttrACmodTime | sshFileXferAttrExtended
|
||||
)
|
||||
|
||||
// fileInfo is an artificial type designed to satisfy os.FileInfo.
|
||||
type fileInfo struct {
|
||||
name string
|
||||
stat *FileStat
|
||||
}
|
||||
|
||||
// Name returns the base name of the file.
|
||||
func (fi *fileInfo) Name() string { return fi.name }
|
||||
|
||||
// Size returns the length in bytes for regular files; system-dependent for others.
|
||||
func (fi *fileInfo) Size() int64 { return int64(fi.stat.Size) }
|
||||
|
||||
// Mode returns file mode bits.
|
||||
func (fi *fileInfo) Mode() os.FileMode { return fi.stat.FileMode() }
|
||||
|
||||
// ModTime returns the last modification time of the file.
|
||||
func (fi *fileInfo) ModTime() time.Time { return fi.stat.ModTime() }
|
||||
|
||||
// IsDir returns true if the file is a directory.
|
||||
func (fi *fileInfo) IsDir() bool { return fi.Mode().IsDir() }
|
||||
|
||||
func (fi *fileInfo) Sys() interface{} { return fi.stat }
|
||||
|
||||
// FileStat holds the original unmarshalled values from a call to READDIR or
|
||||
// *STAT. It is exported for the purposes of accessing the raw values via
|
||||
// os.FileInfo.Sys(). It is also used server side to store the unmarshalled
|
||||
// values for SetStat.
|
||||
type FileStat struct {
|
||||
Size uint64
|
||||
Mode uint32
|
||||
Mtime uint32
|
||||
Atime uint32
|
||||
UID uint32
|
||||
GID uint32
|
||||
Extended []StatExtended
|
||||
}
|
||||
|
||||
// ModTime returns the Mtime SFTP file attribute converted to a time.Time
|
||||
func (fs *FileStat) ModTime() time.Time {
|
||||
return time.Unix(int64(fs.Mtime), 0)
|
||||
}
|
||||
|
||||
// AccessTime returns the Atime SFTP file attribute converted to a time.Time
|
||||
func (fs *FileStat) AccessTime() time.Time {
|
||||
return time.Unix(int64(fs.Atime), 0)
|
||||
}
|
||||
|
||||
// FileMode returns the Mode SFTP file attribute converted to an os.FileMode
|
||||
func (fs *FileStat) FileMode() os.FileMode {
|
||||
return toFileMode(fs.Mode)
|
||||
}
|
||||
|
||||
// StatExtended contains additional, extended information for a FileStat.
|
||||
type StatExtended struct {
|
||||
ExtType string
|
||||
ExtData string
|
||||
}
|
||||
|
||||
func fileInfoFromStat(stat *FileStat, name string) os.FileInfo {
|
||||
return &fileInfo{
|
||||
name: name,
|
||||
stat: stat,
|
||||
}
|
||||
}
|
||||
|
||||
// FileInfoUidGid extends os.FileInfo and adds callbacks for Uid and Gid retrieval,
|
||||
// as an alternative to *syscall.Stat_t objects on unix systems.
|
||||
type FileInfoUidGid interface {
|
||||
os.FileInfo
|
||||
Uid() uint32
|
||||
Gid() uint32
|
||||
}
|
||||
|
||||
// FileInfoUidGid extends os.FileInfo and adds a callbacks for extended data retrieval.
|
||||
type FileInfoExtendedData interface {
|
||||
os.FileInfo
|
||||
Extended() []StatExtended
|
||||
}
|
||||
|
||||
func fileStatFromInfo(fi os.FileInfo) (uint32, *FileStat) {
|
||||
mtime := fi.ModTime().Unix()
|
||||
atime := mtime
|
||||
var flags uint32 = sshFileXferAttrSize |
|
||||
sshFileXferAttrPermissions |
|
||||
sshFileXferAttrACmodTime
|
||||
|
||||
fileStat := &FileStat{
|
||||
Size: uint64(fi.Size()),
|
||||
Mode: fromFileMode(fi.Mode()),
|
||||
Mtime: uint32(mtime),
|
||||
Atime: uint32(atime),
|
||||
}
|
||||
|
||||
// os specific file stat decoding
|
||||
fileStatFromInfoOs(fi, &flags, fileStat)
|
||||
|
||||
// The call above will include the sshFileXferAttrUIDGID in case
|
||||
// the os.FileInfo can be casted to *syscall.Stat_t on unix.
|
||||
// If fi implements FileInfoUidGid, retrieve Uid, Gid from it instead.
|
||||
if fiExt, ok := fi.(FileInfoUidGid); ok {
|
||||
flags |= sshFileXferAttrUIDGID
|
||||
fileStat.UID = fiExt.Uid()
|
||||
fileStat.GID = fiExt.Gid()
|
||||
}
|
||||
|
||||
// if fi implements FileInfoExtendedData, retrieve extended data from it
|
||||
if fiExt, ok := fi.(FileInfoExtendedData); ok {
|
||||
fileStat.Extended = fiExt.Extended()
|
||||
if len(fileStat.Extended) > 0 {
|
||||
flags |= sshFileXferAttrExtended
|
||||
}
|
||||
}
|
||||
|
||||
return flags, fileStat
|
||||
}
|
||||
12
vendor/github.com/pkg/sftp/attrs_stubs.go
generated
vendored
Normal file
12
vendor/github.com/pkg/sftp/attrs_stubs.go
generated
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
//go:build plan9 || windows || android
|
||||
// +build plan9 windows android
|
||||
|
||||
package sftp
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
func fileStatFromInfoOs(fi os.FileInfo, flags *uint32, fileStat *FileStat) {
|
||||
// todo
|
||||
}
|
||||
17
vendor/github.com/pkg/sftp/attrs_unix.go
generated
vendored
Normal file
17
vendor/github.com/pkg/sftp/attrs_unix.go
generated
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
//go:build darwin || dragonfly || freebsd || (!android && linux) || netbsd || openbsd || solaris || aix || js || zos
|
||||
// +build darwin dragonfly freebsd !android,linux netbsd openbsd solaris aix js zos
|
||||
|
||||
package sftp
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func fileStatFromInfoOs(fi os.FileInfo, flags *uint32, fileStat *FileStat) {
|
||||
if statt, ok := fi.Sys().(*syscall.Stat_t); ok {
|
||||
*flags |= sshFileXferAttrUIDGID
|
||||
fileStat.UID = statt.Uid
|
||||
fileStat.GID = statt.Gid
|
||||
}
|
||||
}
|
||||
2298
vendor/github.com/pkg/sftp/client.go
generated
vendored
Normal file
2298
vendor/github.com/pkg/sftp/client.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
212
vendor/github.com/pkg/sftp/conn.go
generated
vendored
Normal file
212
vendor/github.com/pkg/sftp/conn.go
generated
vendored
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
package sftp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding"
|
||||
"fmt"
|
||||
"io"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// conn implements a bidirectional channel on which client and server
|
||||
// connections are multiplexed.
|
||||
type conn struct {
|
||||
io.Reader
|
||||
io.WriteCloser
|
||||
// this is the same allocator used in packet manager
|
||||
alloc *allocator
|
||||
sync.Mutex // used to serialise writes to sendPacket
|
||||
}
|
||||
|
||||
// the orderID is used in server mode if the allocator is enabled.
|
||||
// For the client mode just pass 0.
|
||||
// It returns io.EOF if the connection is closed and
|
||||
// there are no more packets to read.
|
||||
func (c *conn) recvPacket(orderID uint32) (fxp, []byte, error) {
|
||||
return recvPacket(c, c.alloc, orderID)
|
||||
}
|
||||
|
||||
func (c *conn) sendPacket(m encoding.BinaryMarshaler) error {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
return sendPacket(c, m)
|
||||
}
|
||||
|
||||
func (c *conn) Close() error {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
return c.WriteCloser.Close()
|
||||
}
|
||||
|
||||
type clientConn struct {
|
||||
conn
|
||||
wg sync.WaitGroup
|
||||
|
||||
wait func() error // if non-nil, call this during Wait() to get a possible remote status error.
|
||||
|
||||
sync.Mutex // protects inflight
|
||||
inflight map[uint32]chan<- result // outstanding requests
|
||||
|
||||
closed chan struct{}
|
||||
err error
|
||||
}
|
||||
|
||||
// Wait blocks until the conn has shut down, and return the error
|
||||
// causing the shutdown. It can be called concurrently from multiple
|
||||
// goroutines.
|
||||
func (c *clientConn) Wait() error {
|
||||
<-c.closed
|
||||
|
||||
if c.wait == nil {
|
||||
// Only return this error if c.wait won't return something more useful.
|
||||
return c.err
|
||||
}
|
||||
|
||||
if err := c.wait(); err != nil {
|
||||
|
||||
// TODO: when https://github.com/golang/go/issues/35025 is fixed,
|
||||
// we can remove this if block entirely.
|
||||
// Right now, it’s always going to return this, so it is not useful.
|
||||
// But we have this code here so that as soon as the ssh library is updated,
|
||||
// we can return a possibly more useful error.
|
||||
if err.Error() == "ssh: session not started" {
|
||||
return c.err
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// c.wait returned no error; so, let's return something maybe more useful.
|
||||
return c.err
|
||||
}
|
||||
|
||||
// Close closes the SFTP session.
|
||||
func (c *clientConn) Close() error {
|
||||
defer c.wg.Wait()
|
||||
return c.conn.Close()
|
||||
}
|
||||
|
||||
// recv continuously reads from the server and forwards responses to the
|
||||
// appropriate channel.
|
||||
func (c *clientConn) recv() error {
|
||||
defer c.conn.Close()
|
||||
|
||||
for {
|
||||
typ, data, err := c.recvPacket(0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sid, _, err := unmarshalUint32Safe(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ch, ok := c.getChannel(sid)
|
||||
if !ok {
|
||||
// This is an unexpected occurrence. Send the error
|
||||
// back to all listeners so that they terminate
|
||||
// gracefully.
|
||||
return fmt.Errorf("sid not found: %d", sid)
|
||||
}
|
||||
|
||||
ch <- result{typ: typ, data: data}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *clientConn) putChannel(ch chan<- result, sid uint32) bool {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
select {
|
||||
case <-c.closed:
|
||||
// already closed with broadcastErr, return error on chan.
|
||||
ch <- result{err: ErrSSHFxConnectionLost}
|
||||
return false
|
||||
default:
|
||||
}
|
||||
|
||||
c.inflight[sid] = ch
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *clientConn) getChannel(sid uint32) (chan<- result, bool) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
ch, ok := c.inflight[sid]
|
||||
delete(c.inflight, sid)
|
||||
|
||||
return ch, ok
|
||||
}
|
||||
|
||||
// result captures the result of receiving the a packet from the server
|
||||
type result struct {
|
||||
typ fxp
|
||||
data []byte
|
||||
err error
|
||||
}
|
||||
|
||||
type idmarshaler interface {
|
||||
id() uint32
|
||||
encoding.BinaryMarshaler
|
||||
}
|
||||
|
||||
func (c *clientConn) sendPacket(ctx context.Context, ch chan result, p idmarshaler) (fxp, []byte, error) {
|
||||
if cap(ch) < 1 {
|
||||
ch = make(chan result, 1)
|
||||
}
|
||||
|
||||
c.dispatchRequest(ch, p)
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return 0, nil, ctx.Err()
|
||||
case s := <-ch:
|
||||
return s.typ, s.data, s.err
|
||||
}
|
||||
}
|
||||
|
||||
// dispatchRequest should ideally only be called by race-detection tests outside of this file,
|
||||
// where you have to ensure two packets are in flight sequentially after each other.
|
||||
func (c *clientConn) dispatchRequest(ch chan<- result, p idmarshaler) {
|
||||
sid := p.id()
|
||||
|
||||
if !c.putChannel(ch, sid) {
|
||||
// already closed.
|
||||
return
|
||||
}
|
||||
|
||||
if err := c.conn.sendPacket(p); err != nil {
|
||||
if ch, ok := c.getChannel(sid); ok {
|
||||
ch <- result{err: err}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// broadcastErr sends an error to all goroutines waiting for a response.
|
||||
func (c *clientConn) broadcastErr(err error) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
bcastRes := result{err: ErrSSHFxConnectionLost}
|
||||
for sid, ch := range c.inflight {
|
||||
ch <- bcastRes
|
||||
|
||||
// Replace the chan in inflight,
|
||||
// we have hijacked this chan,
|
||||
// and this guarantees always-only-once sending.
|
||||
c.inflight[sid] = make(chan<- result, 1)
|
||||
}
|
||||
|
||||
c.err = err
|
||||
close(c.closed)
|
||||
}
|
||||
|
||||
type serverConn struct {
|
||||
conn
|
||||
}
|
||||
|
||||
func (s *serverConn) sendError(id uint32, err error) error {
|
||||
return s.sendPacket(statusFromError(id, err))
|
||||
}
|
||||
10
vendor/github.com/pkg/sftp/debug.go
generated
vendored
Normal file
10
vendor/github.com/pkg/sftp/debug.go
generated
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
//go:build debug
|
||||
// +build debug
|
||||
|
||||
package sftp
|
||||
|
||||
import "log"
|
||||
|
||||
func debug(fmt string, args ...interface{}) {
|
||||
log.Printf(fmt, args...)
|
||||
}
|
||||
42
vendor/github.com/pkg/sftp/errno_plan9.go
generated
vendored
Normal file
42
vendor/github.com/pkg/sftp/errno_plan9.go
generated
vendored
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
package sftp
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
var EBADF = syscall.NewError("fd out of range or not open")
|
||||
|
||||
func wrapPathError(filepath string, err error) error {
|
||||
if errno, ok := err.(syscall.ErrorString); ok {
|
||||
return &os.PathError{Path: filepath, Err: errno}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// translateErrno translates a syscall error number to a SFTP error code.
|
||||
func translateErrno(errno syscall.ErrorString) uint32 {
|
||||
switch errno {
|
||||
case "":
|
||||
return sshFxOk
|
||||
case syscall.ENOENT:
|
||||
return sshFxNoSuchFile
|
||||
case syscall.EPERM:
|
||||
return sshFxPermissionDenied
|
||||
}
|
||||
|
||||
return sshFxFailure
|
||||
}
|
||||
|
||||
func translateSyscallError(err error) (uint32, bool) {
|
||||
switch e := err.(type) {
|
||||
case syscall.ErrorString:
|
||||
return translateErrno(e), true
|
||||
case *os.PathError:
|
||||
debug("statusFromError,pathError: error is %T %#v", e.Err, e.Err)
|
||||
if errno, ok := e.Err.(syscall.ErrorString); ok {
|
||||
return translateErrno(errno), true
|
||||
}
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
45
vendor/github.com/pkg/sftp/errno_posix.go
generated
vendored
Normal file
45
vendor/github.com/pkg/sftp/errno_posix.go
generated
vendored
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
//go:build !plan9
|
||||
// +build !plan9
|
||||
|
||||
package sftp
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
const EBADF = syscall.EBADF
|
||||
|
||||
func wrapPathError(filepath string, err error) error {
|
||||
if errno, ok := err.(syscall.Errno); ok {
|
||||
return &os.PathError{Path: filepath, Err: errno}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// translateErrno translates a syscall error number to a SFTP error code.
|
||||
func translateErrno(errno syscall.Errno) uint32 {
|
||||
switch errno {
|
||||
case 0:
|
||||
return sshFxOk
|
||||
case syscall.ENOENT:
|
||||
return sshFxNoSuchFile
|
||||
case syscall.EACCES, syscall.EPERM:
|
||||
return sshFxPermissionDenied
|
||||
}
|
||||
|
||||
return sshFxFailure
|
||||
}
|
||||
|
||||
func translateSyscallError(err error) (uint32, bool) {
|
||||
switch e := err.(type) {
|
||||
case syscall.Errno:
|
||||
return translateErrno(e), true
|
||||
case *os.PathError:
|
||||
debug("statusFromError,pathError: error is %T %#v", e.Err, e.Err)
|
||||
if errno, ok := e.Err.(syscall.Errno); ok {
|
||||
return translateErrno(errno), true
|
||||
}
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
23
vendor/github.com/pkg/sftp/fuzz.go
generated
vendored
Normal file
23
vendor/github.com/pkg/sftp/fuzz.go
generated
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
//go:build gofuzz
|
||||
// +build gofuzz
|
||||
|
||||
package sftp
|
||||
|
||||
import "bytes"
|
||||
|
||||
type sinkfuzz struct{}
|
||||
|
||||
func (*sinkfuzz) Close() error { return nil }
|
||||
func (*sinkfuzz) Write(p []byte) (int, error) { return len(p), nil }
|
||||
|
||||
var devnull = &sinkfuzz{}
|
||||
|
||||
// To run: go-fuzz-build && go-fuzz
|
||||
func Fuzz(data []byte) int {
|
||||
c, err := NewClientPipe(bytes.NewReader(data), devnull)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
c.Close()
|
||||
return 1
|
||||
}
|
||||
296
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/attrs.go
generated
vendored
Normal file
296
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/attrs.go
generated
vendored
Normal file
|
|
@ -0,0 +1,296 @@
|
|||
package sshfx
|
||||
|
||||
// Attributes related flags.
|
||||
const (
|
||||
AttrSize = 1 << iota // SSH_FILEXFER_ATTR_SIZE
|
||||
AttrUIDGID // SSH_FILEXFER_ATTR_UIDGID
|
||||
AttrPermissions // SSH_FILEXFER_ATTR_PERMISSIONS
|
||||
AttrACModTime // SSH_FILEXFER_ACMODTIME
|
||||
|
||||
AttrExtended = 1 << 31 // SSH_FILEXFER_ATTR_EXTENDED
|
||||
)
|
||||
|
||||
// Attributes defines the file attributes type defined in draft-ietf-secsh-filexfer-02
|
||||
//
|
||||
// Defined in: https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-02.txt#section-5
|
||||
type Attributes struct {
|
||||
Flags uint32
|
||||
|
||||
// AttrSize
|
||||
Size uint64
|
||||
|
||||
// AttrUIDGID
|
||||
UID uint32
|
||||
GID uint32
|
||||
|
||||
// AttrPermissions
|
||||
Permissions FileMode
|
||||
|
||||
// AttrACmodTime
|
||||
ATime uint32
|
||||
MTime uint32
|
||||
|
||||
// AttrExtended
|
||||
ExtendedAttributes []ExtendedAttribute
|
||||
}
|
||||
|
||||
// GetSize returns the Size field and a bool that is true if and only if the value is valid/defined.
|
||||
func (a *Attributes) GetSize() (size uint64, ok bool) {
|
||||
return a.Size, a.Flags&AttrSize != 0
|
||||
}
|
||||
|
||||
// SetSize is a convenience function that sets the Size field,
|
||||
// and marks the field as valid/defined in Flags.
|
||||
func (a *Attributes) SetSize(size uint64) {
|
||||
a.Flags |= AttrSize
|
||||
a.Size = size
|
||||
}
|
||||
|
||||
// GetUIDGID returns the UID and GID fields and a bool that is true if and only if the values are valid/defined.
|
||||
func (a *Attributes) GetUIDGID() (uid, gid uint32, ok bool) {
|
||||
return a.UID, a.GID, a.Flags&AttrUIDGID != 0
|
||||
}
|
||||
|
||||
// SetUIDGID is a convenience function that sets the UID and GID fields,
|
||||
// and marks the fields as valid/defined in Flags.
|
||||
func (a *Attributes) SetUIDGID(uid, gid uint32) {
|
||||
a.Flags |= AttrUIDGID
|
||||
a.UID = uid
|
||||
a.GID = gid
|
||||
}
|
||||
|
||||
// GetPermissions returns the Permissions field and a bool that is true if and only if the value is valid/defined.
|
||||
func (a *Attributes) GetPermissions() (perms FileMode, ok bool) {
|
||||
return a.Permissions, a.Flags&AttrPermissions != 0
|
||||
}
|
||||
|
||||
// SetPermissions is a convenience function that sets the Permissions field,
|
||||
// and marks the field as valid/defined in Flags.
|
||||
func (a *Attributes) SetPermissions(perms FileMode) {
|
||||
a.Flags |= AttrPermissions
|
||||
a.Permissions = perms
|
||||
}
|
||||
|
||||
// GetACModTime returns the ATime and MTime fields and a bool that is true if and only if the values are valid/defined.
|
||||
func (a *Attributes) GetACModTime() (atime, mtime uint32, ok bool) {
|
||||
return a.ATime, a.MTime, a.Flags&AttrACModTime != 0
|
||||
}
|
||||
|
||||
// SetACModTime is a convenience function that sets the ATime and MTime fields,
|
||||
// and marks the fields as valid/defined in Flags.
|
||||
func (a *Attributes) SetACModTime(atime, mtime uint32) {
|
||||
a.Flags |= AttrACModTime
|
||||
a.ATime = atime
|
||||
a.MTime = mtime
|
||||
}
|
||||
|
||||
// Len returns the number of bytes a would marshal into.
|
||||
func (a *Attributes) Len() int {
|
||||
length := 4
|
||||
|
||||
if a.Flags&AttrSize != 0 {
|
||||
length += 8
|
||||
}
|
||||
|
||||
if a.Flags&AttrUIDGID != 0 {
|
||||
length += 4 + 4
|
||||
}
|
||||
|
||||
if a.Flags&AttrPermissions != 0 {
|
||||
length += 4
|
||||
}
|
||||
|
||||
if a.Flags&AttrACModTime != 0 {
|
||||
length += 4 + 4
|
||||
}
|
||||
|
||||
if a.Flags&AttrExtended != 0 {
|
||||
length += 4
|
||||
|
||||
for _, ext := range a.ExtendedAttributes {
|
||||
length += ext.Len()
|
||||
}
|
||||
}
|
||||
|
||||
return length
|
||||
}
|
||||
|
||||
// MarshalInto marshals e onto the end of the given Buffer.
|
||||
func (a *Attributes) MarshalInto(buf *Buffer) {
|
||||
buf.AppendUint32(a.Flags)
|
||||
|
||||
if a.Flags&AttrSize != 0 {
|
||||
buf.AppendUint64(a.Size)
|
||||
}
|
||||
|
||||
if a.Flags&AttrUIDGID != 0 {
|
||||
buf.AppendUint32(a.UID)
|
||||
buf.AppendUint32(a.GID)
|
||||
}
|
||||
|
||||
if a.Flags&AttrPermissions != 0 {
|
||||
buf.AppendUint32(uint32(a.Permissions))
|
||||
}
|
||||
|
||||
if a.Flags&AttrACModTime != 0 {
|
||||
buf.AppendUint32(a.ATime)
|
||||
buf.AppendUint32(a.MTime)
|
||||
}
|
||||
|
||||
if a.Flags&AttrExtended != 0 {
|
||||
buf.AppendUint32(uint32(len(a.ExtendedAttributes)))
|
||||
|
||||
for _, ext := range a.ExtendedAttributes {
|
||||
ext.MarshalInto(buf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalBinary returns a as the binary encoding of a.
|
||||
func (a *Attributes) MarshalBinary() ([]byte, error) {
|
||||
buf := NewBuffer(make([]byte, 0, a.Len()))
|
||||
a.MarshalInto(buf)
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// UnmarshalFrom unmarshals an Attributes from the given Buffer into e.
|
||||
//
|
||||
// NOTE: The values of fields not covered in the a.Flags are explicitly undefined.
|
||||
func (a *Attributes) UnmarshalFrom(buf *Buffer) (err error) {
|
||||
flags := buf.ConsumeUint32()
|
||||
|
||||
return a.XXX_UnmarshalByFlags(flags, buf)
|
||||
}
|
||||
|
||||
// XXX_UnmarshalByFlags uses the pre-existing a.Flags field to determine which fields to decode.
|
||||
// DO NOT USE THIS: it is an anti-corruption function to implement existing internal usage in pkg/sftp.
|
||||
// This function is not a part of any compatibility promise.
|
||||
func (a *Attributes) XXX_UnmarshalByFlags(flags uint32, buf *Buffer) (err error) {
|
||||
a.Flags = flags
|
||||
|
||||
// Short-circuit dummy attributes.
|
||||
if a.Flags == 0 {
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
if a.Flags&AttrSize != 0 {
|
||||
a.Size = buf.ConsumeUint64()
|
||||
}
|
||||
|
||||
if a.Flags&AttrUIDGID != 0 {
|
||||
a.UID = buf.ConsumeUint32()
|
||||
a.GID = buf.ConsumeUint32()
|
||||
}
|
||||
|
||||
if a.Flags&AttrPermissions != 0 {
|
||||
a.Permissions = FileMode(buf.ConsumeUint32())
|
||||
}
|
||||
|
||||
if a.Flags&AttrACModTime != 0 {
|
||||
a.ATime = buf.ConsumeUint32()
|
||||
a.MTime = buf.ConsumeUint32()
|
||||
}
|
||||
|
||||
if a.Flags&AttrExtended != 0 {
|
||||
count := buf.ConsumeCount()
|
||||
|
||||
a.ExtendedAttributes = make([]ExtendedAttribute, count)
|
||||
for i := range a.ExtendedAttributes {
|
||||
a.ExtendedAttributes[i].UnmarshalFrom(buf)
|
||||
}
|
||||
}
|
||||
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
// UnmarshalBinary decodes the binary encoding of Attributes into e.
|
||||
func (a *Attributes) UnmarshalBinary(data []byte) error {
|
||||
return a.UnmarshalFrom(NewBuffer(data))
|
||||
}
|
||||
|
||||
// ExtendedAttribute defines the extended file attribute type defined in draft-ietf-secsh-filexfer-02
|
||||
//
|
||||
// Defined in: https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-02.txt#section-5
|
||||
type ExtendedAttribute struct {
|
||||
Type string
|
||||
Data string
|
||||
}
|
||||
|
||||
// Len returns the number of bytes e would marshal into.
|
||||
func (e *ExtendedAttribute) Len() int {
|
||||
return 4 + len(e.Type) + 4 + len(e.Data)
|
||||
}
|
||||
|
||||
// MarshalInto marshals e onto the end of the given Buffer.
|
||||
func (e *ExtendedAttribute) MarshalInto(buf *Buffer) {
|
||||
buf.AppendString(e.Type)
|
||||
buf.AppendString(e.Data)
|
||||
}
|
||||
|
||||
// MarshalBinary returns e as the binary encoding of e.
|
||||
func (e *ExtendedAttribute) MarshalBinary() ([]byte, error) {
|
||||
buf := NewBuffer(make([]byte, 0, e.Len()))
|
||||
e.MarshalInto(buf)
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// UnmarshalFrom unmarshals an ExtendedAattribute from the given Buffer into e.
|
||||
func (e *ExtendedAttribute) UnmarshalFrom(buf *Buffer) (err error) {
|
||||
*e = ExtendedAttribute{
|
||||
Type: buf.ConsumeString(),
|
||||
Data: buf.ConsumeString(),
|
||||
}
|
||||
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
// UnmarshalBinary decodes the binary encoding of ExtendedAttribute into e.
|
||||
func (e *ExtendedAttribute) UnmarshalBinary(data []byte) error {
|
||||
return e.UnmarshalFrom(NewBuffer(data))
|
||||
}
|
||||
|
||||
// NameEntry implements the SSH_FXP_NAME repeated data type from draft-ietf-secsh-filexfer-02
|
||||
//
|
||||
// This type is incompatible with versions 4 or higher.
|
||||
type NameEntry struct {
|
||||
Filename string
|
||||
Longname string
|
||||
Attrs Attributes
|
||||
}
|
||||
|
||||
// Len returns the number of bytes e would marshal into.
|
||||
func (e *NameEntry) Len() int {
|
||||
return 4 + len(e.Filename) + 4 + len(e.Longname) + e.Attrs.Len()
|
||||
}
|
||||
|
||||
// MarshalInto marshals e onto the end of the given Buffer.
|
||||
func (e *NameEntry) MarshalInto(buf *Buffer) {
|
||||
buf.AppendString(e.Filename)
|
||||
buf.AppendString(e.Longname)
|
||||
|
||||
e.Attrs.MarshalInto(buf)
|
||||
}
|
||||
|
||||
// MarshalBinary returns e as the binary encoding of e.
|
||||
func (e *NameEntry) MarshalBinary() ([]byte, error) {
|
||||
buf := NewBuffer(make([]byte, 0, e.Len()))
|
||||
e.MarshalInto(buf)
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// UnmarshalFrom unmarshals an NameEntry from the given Buffer into e.
|
||||
//
|
||||
// NOTE: The values of fields not covered in the a.Flags are explicitly undefined.
|
||||
func (e *NameEntry) UnmarshalFrom(buf *Buffer) (err error) {
|
||||
*e = NameEntry{
|
||||
Filename: buf.ConsumeString(),
|
||||
Longname: buf.ConsumeString(),
|
||||
}
|
||||
|
||||
return e.Attrs.UnmarshalFrom(buf)
|
||||
}
|
||||
|
||||
// UnmarshalBinary decodes the binary encoding of NameEntry into e.
|
||||
func (e *NameEntry) UnmarshalBinary(data []byte) error {
|
||||
return e.UnmarshalFrom(NewBuffer(data))
|
||||
}
|
||||
340
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/buffer.go
generated
vendored
Normal file
340
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/buffer.go
generated
vendored
Normal file
|
|
@ -0,0 +1,340 @@
|
|||
package sshfx
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
)
|
||||
|
||||
// Various encoding errors.
|
||||
var (
|
||||
ErrShortPacket = errors.New("packet too short")
|
||||
ErrLongPacket = errors.New("packet too long")
|
||||
)
|
||||
|
||||
// Buffer wraps up the various encoding details of the SSH format.
|
||||
//
|
||||
// Data types are encoded as per section 4 from https://tools.ietf.org/html/draft-ietf-secsh-architecture-09#page-8
|
||||
type Buffer struct {
|
||||
b []byte
|
||||
off int
|
||||
Err error
|
||||
}
|
||||
|
||||
// NewBuffer creates and initializes a new buffer using buf as its initial contents.
|
||||
// The new buffer takes ownership of buf, and the caller should not use buf after this call.
|
||||
//
|
||||
// In most cases, new(Buffer) (or just declaring a Buffer variable) is sufficient to initialize a Buffer.
|
||||
func NewBuffer(buf []byte) *Buffer {
|
||||
return &Buffer{
|
||||
b: buf,
|
||||
}
|
||||
}
|
||||
|
||||
// NewMarshalBuffer creates a new Buffer ready to start marshaling a Packet into.
|
||||
// It preallocates enough space for uint32(length), uint8(type), uint32(request-id) and size more bytes.
|
||||
func NewMarshalBuffer(size int) *Buffer {
|
||||
return NewBuffer(make([]byte, 4+1+4+size))
|
||||
}
|
||||
|
||||
// Bytes returns a slice of length b.Len() holding the unconsumed bytes in the Buffer.
|
||||
// The slice is valid for use only until the next buffer modification
|
||||
// (that is, only until the next call to an Append or Consume method).
|
||||
func (b *Buffer) Bytes() []byte {
|
||||
return b.b[b.off:]
|
||||
}
|
||||
|
||||
// Len returns the number of unconsumed bytes in the buffer.
|
||||
func (b *Buffer) Len() int { return len(b.b) - b.off }
|
||||
|
||||
// Cap returns the capacity of the buffer’s underlying byte slice,
|
||||
// that is, the total space allocated for the buffer’s data.
|
||||
func (b *Buffer) Cap() int { return cap(b.b) }
|
||||
|
||||
// Reset resets the buffer to be empty, but it retains the underlying storage for use by future Appends.
|
||||
func (b *Buffer) Reset() {
|
||||
*b = Buffer{
|
||||
b: b.b[:0],
|
||||
}
|
||||
}
|
||||
|
||||
// StartPacket resets and initializes the buffer to be ready to start marshaling a packet into.
|
||||
// It truncates the buffer, reserves space for uint32(length), then appends the given packetType and requestID.
|
||||
func (b *Buffer) StartPacket(packetType PacketType, requestID uint32) {
|
||||
*b = Buffer{
|
||||
b: append(b.b[:0], make([]byte, 4)...),
|
||||
}
|
||||
|
||||
b.AppendUint8(uint8(packetType))
|
||||
b.AppendUint32(requestID)
|
||||
}
|
||||
|
||||
// Packet finalizes the packet started from StartPacket.
|
||||
// It is expected that this will end the ownership of the underlying byte-slice,
|
||||
// and so the returned byte-slices may be reused the same as any other byte-slice,
|
||||
// the caller should not use this buffer after this call.
|
||||
//
|
||||
// It writes the packet body length into the first four bytes of the buffer in network byte order (big endian).
|
||||
// The packet body length is the length of this buffer less the 4-byte length itself, plus the length of payload.
|
||||
//
|
||||
// It is assumed that no Consume methods have been called on this buffer,
|
||||
// and so it returns the whole underlying slice.
|
||||
func (b *Buffer) Packet(payload []byte) (header, payloadPassThru []byte, err error) {
|
||||
b.PutLength(len(b.b) - 4 + len(payload))
|
||||
|
||||
return b.b, payload, nil
|
||||
}
|
||||
|
||||
// ConsumeUint8 consumes a single byte from the buffer.
|
||||
// If the buffer does not have enough data, it will set Err to ErrShortPacket.
|
||||
func (b *Buffer) ConsumeUint8() uint8 {
|
||||
if b.Err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
if b.Len() < 1 {
|
||||
b.off = len(b.b)
|
||||
b.Err = ErrShortPacket
|
||||
return 0
|
||||
}
|
||||
|
||||
var v uint8
|
||||
v, b.off = b.b[b.off], b.off+1
|
||||
return v
|
||||
}
|
||||
|
||||
// AppendUint8 appends a single byte into the buffer.
|
||||
func (b *Buffer) AppendUint8(v uint8) {
|
||||
b.b = append(b.b, v)
|
||||
}
|
||||
|
||||
// ConsumeBool consumes a single byte from the buffer, and returns true if that byte is non-zero.
|
||||
// If the buffer does not have enough data, it will set Err to ErrShortPacket.
|
||||
func (b *Buffer) ConsumeBool() bool {
|
||||
return b.ConsumeUint8() != 0
|
||||
}
|
||||
|
||||
// AppendBool appends a single bool into the buffer.
|
||||
// It encodes it as a single byte, with false as 0, and true as 1.
|
||||
func (b *Buffer) AppendBool(v bool) {
|
||||
if v {
|
||||
b.AppendUint8(1)
|
||||
} else {
|
||||
b.AppendUint8(0)
|
||||
}
|
||||
}
|
||||
|
||||
// ConsumeUint16 consumes a single uint16 from the buffer, in network byte order (big-endian).
|
||||
// If the buffer does not have enough data, it will set Err to ErrShortPacket.
|
||||
func (b *Buffer) ConsumeUint16() uint16 {
|
||||
if b.Err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
if b.Len() < 2 {
|
||||
b.off = len(b.b)
|
||||
b.Err = ErrShortPacket
|
||||
return 0
|
||||
}
|
||||
|
||||
v := binary.BigEndian.Uint16(b.b[b.off:])
|
||||
b.off += 2
|
||||
return v
|
||||
}
|
||||
|
||||
// AppendUint16 appends single uint16 into the buffer, in network byte order (big-endian).
|
||||
func (b *Buffer) AppendUint16(v uint16) {
|
||||
b.b = append(b.b,
|
||||
byte(v>>8),
|
||||
byte(v>>0),
|
||||
)
|
||||
}
|
||||
|
||||
// unmarshalUint32 is used internally to read the packet length.
|
||||
// It is unsafe, and so not exported.
|
||||
// Even within this package, its use should be avoided.
|
||||
func unmarshalUint32(b []byte) uint32 {
|
||||
return binary.BigEndian.Uint32(b[:4])
|
||||
}
|
||||
|
||||
// ConsumeUint32 consumes a single uint32 from the buffer, in network byte order (big-endian).
|
||||
// If the buffer does not have enough data, it will set Err to ErrShortPacket.
|
||||
func (b *Buffer) ConsumeUint32() uint32 {
|
||||
if b.Err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
if b.Len() < 4 {
|
||||
b.off = len(b.b)
|
||||
b.Err = ErrShortPacket
|
||||
return 0
|
||||
}
|
||||
|
||||
v := binary.BigEndian.Uint32(b.b[b.off:])
|
||||
b.off += 4
|
||||
return v
|
||||
}
|
||||
|
||||
// AppendUint32 appends a single uint32 into the buffer, in network byte order (big-endian).
|
||||
func (b *Buffer) AppendUint32(v uint32) {
|
||||
b.b = append(b.b,
|
||||
byte(v>>24),
|
||||
byte(v>>16),
|
||||
byte(v>>8),
|
||||
byte(v>>0),
|
||||
)
|
||||
}
|
||||
|
||||
// ConsumeCount consumes a single uint32 count from the buffer, in network byte order (big-endian) as an int.
|
||||
// If the buffer does not have enough data, it will set Err to ErrShortPacket.
|
||||
func (b *Buffer) ConsumeCount() int {
|
||||
return int(b.ConsumeUint32())
|
||||
}
|
||||
|
||||
// AppendCount appends a single int length as a uint32 into the buffer, in network byte order (big-endian).
|
||||
func (b *Buffer) AppendCount(v int) {
|
||||
b.AppendUint32(uint32(v))
|
||||
}
|
||||
|
||||
// ConsumeUint64 consumes a single uint64 from the buffer, in network byte order (big-endian).
|
||||
// If the buffer does not have enough data, it will set Err to ErrShortPacket.
|
||||
func (b *Buffer) ConsumeUint64() uint64 {
|
||||
if b.Err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
if b.Len() < 8 {
|
||||
b.off = len(b.b)
|
||||
b.Err = ErrShortPacket
|
||||
return 0
|
||||
}
|
||||
|
||||
v := binary.BigEndian.Uint64(b.b[b.off:])
|
||||
b.off += 8
|
||||
return v
|
||||
}
|
||||
|
||||
// AppendUint64 appends a single uint64 into the buffer, in network byte order (big-endian).
|
||||
func (b *Buffer) AppendUint64(v uint64) {
|
||||
b.b = append(b.b,
|
||||
byte(v>>56),
|
||||
byte(v>>48),
|
||||
byte(v>>40),
|
||||
byte(v>>32),
|
||||
byte(v>>24),
|
||||
byte(v>>16),
|
||||
byte(v>>8),
|
||||
byte(v>>0),
|
||||
)
|
||||
}
|
||||
|
||||
// ConsumeInt64 consumes a single int64 from the buffer, in network byte order (big-endian) with two’s complement.
|
||||
// If the buffer does not have enough data, it will set Err to ErrShortPacket.
|
||||
func (b *Buffer) ConsumeInt64() int64 {
|
||||
return int64(b.ConsumeUint64())
|
||||
}
|
||||
|
||||
// AppendInt64 appends a single int64 into the buffer, in network byte order (big-endian) with two’s complement.
|
||||
func (b *Buffer) AppendInt64(v int64) {
|
||||
b.AppendUint64(uint64(v))
|
||||
}
|
||||
|
||||
// ConsumeByteSlice consumes a single string of raw binary data from the buffer.
|
||||
// A string is a uint32 length, followed by that number of raw bytes.
|
||||
// If the buffer does not have enough data, or defines a length larger than available, it will set Err to ErrShortPacket.
|
||||
//
|
||||
// The returned slice aliases the buffer contents, and is valid only as long as the buffer is not reused
|
||||
// (that is, only until the next call to Reset, PutLength, StartPacket, or UnmarshalBinary).
|
||||
//
|
||||
// In no case will any Consume calls return overlapping slice aliases,
|
||||
// and Append calls are guaranteed to not disturb this slice alias.
|
||||
func (b *Buffer) ConsumeByteSlice() []byte {
|
||||
length := int(b.ConsumeUint32())
|
||||
if b.Err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if b.Len() < length || length < 0 {
|
||||
b.off = len(b.b)
|
||||
b.Err = ErrShortPacket
|
||||
return nil
|
||||
}
|
||||
|
||||
v := b.b[b.off:]
|
||||
if len(v) > length || cap(v) > length {
|
||||
v = v[:length:length]
|
||||
}
|
||||
b.off += int(length)
|
||||
return v
|
||||
}
|
||||
|
||||
// ConsumeByteSliceCopy consumes a single string of raw binary data as a copy from the buffer.
|
||||
// A string is a uint32 length, followed by that number of raw bytes.
|
||||
// If the buffer does not have enough data, or defines a length larger than available, it will set Err to ErrShortPacket.
|
||||
//
|
||||
// The returned slice does not alias any buffer contents,
|
||||
// and will therefore be valid even if the buffer is later reused.
|
||||
//
|
||||
// If hint has sufficient capacity to hold the data, it will be reused and overwritten,
|
||||
// otherwise a new backing slice will be allocated and returned.
|
||||
func (b *Buffer) ConsumeByteSliceCopy(hint []byte) []byte {
|
||||
data := b.ConsumeByteSlice()
|
||||
|
||||
if grow := len(data) - len(hint); grow > 0 {
|
||||
hint = append(hint, make([]byte, grow)...)
|
||||
}
|
||||
|
||||
n := copy(hint, data)
|
||||
hint = hint[:n]
|
||||
return hint
|
||||
}
|
||||
|
||||
// AppendByteSlice appends a single string of raw binary data into the buffer.
|
||||
// A string is a uint32 length, followed by that number of raw bytes.
|
||||
func (b *Buffer) AppendByteSlice(v []byte) {
|
||||
b.AppendUint32(uint32(len(v)))
|
||||
b.b = append(b.b, v...)
|
||||
}
|
||||
|
||||
// ConsumeString consumes a single string of binary data from the buffer.
|
||||
// A string is a uint32 length, followed by that number of raw bytes.
|
||||
// If the buffer does not have enough data, or defines a length larger than available, it will set Err to ErrShortPacket.
|
||||
//
|
||||
// NOTE: Go implicitly assumes that strings contain UTF-8 encoded data.
|
||||
// All caveats on using arbitrary binary data in Go strings applies.
|
||||
func (b *Buffer) ConsumeString() string {
|
||||
return string(b.ConsumeByteSlice())
|
||||
}
|
||||
|
||||
// AppendString appends a single string of binary data into the buffer.
|
||||
// A string is a uint32 length, followed by that number of raw bytes.
|
||||
func (b *Buffer) AppendString(v string) {
|
||||
b.AppendByteSlice([]byte(v))
|
||||
}
|
||||
|
||||
// PutLength writes the given size into the first four bytes of the buffer in network byte order (big endian).
|
||||
func (b *Buffer) PutLength(size int) {
|
||||
if len(b.b) < 4 {
|
||||
b.b = append(b.b, make([]byte, 4-len(b.b))...)
|
||||
}
|
||||
|
||||
binary.BigEndian.PutUint32(b.b, uint32(size))
|
||||
}
|
||||
|
||||
// MarshalBinary returns a clone of the full internal buffer.
|
||||
func (b *Buffer) MarshalBinary() ([]byte, error) {
|
||||
clone := make([]byte, len(b.b))
|
||||
n := copy(clone, b.b)
|
||||
return clone[:n], nil
|
||||
}
|
||||
|
||||
// UnmarshalBinary sets the internal buffer of b to be a clone of data, and zeros the internal offset.
|
||||
func (b *Buffer) UnmarshalBinary(data []byte) error {
|
||||
if grow := len(data) - len(b.b); grow > 0 {
|
||||
b.b = append(b.b, make([]byte, grow)...)
|
||||
}
|
||||
|
||||
n := copy(b.b, data)
|
||||
b.b = b.b[:n]
|
||||
b.off = 0
|
||||
return nil
|
||||
}
|
||||
143
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/extended_packets.go
generated
vendored
Normal file
143
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/extended_packets.go
generated
vendored
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
package sshfx
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// ExtendedData aliases the untyped interface composition of encoding.BinaryMarshaler and encoding.BinaryUnmarshaler.
|
||||
type ExtendedData = interface {
|
||||
encoding.BinaryMarshaler
|
||||
encoding.BinaryUnmarshaler
|
||||
}
|
||||
|
||||
// ExtendedDataConstructor defines a function that returns a new(ArbitraryExtendedPacket).
|
||||
type ExtendedDataConstructor func() ExtendedData
|
||||
|
||||
var extendedPacketTypes = struct {
|
||||
mu sync.RWMutex
|
||||
constructors map[string]ExtendedDataConstructor
|
||||
}{
|
||||
constructors: make(map[string]ExtendedDataConstructor),
|
||||
}
|
||||
|
||||
// RegisterExtendedPacketType defines a specific ExtendedDataConstructor for the given extension string.
|
||||
func RegisterExtendedPacketType(extension string, constructor ExtendedDataConstructor) {
|
||||
extendedPacketTypes.mu.Lock()
|
||||
defer extendedPacketTypes.mu.Unlock()
|
||||
|
||||
if _, exist := extendedPacketTypes.constructors[extension]; exist {
|
||||
panic("encoding/ssh/filexfer: multiple registration of extended packet type " + extension)
|
||||
}
|
||||
|
||||
extendedPacketTypes.constructors[extension] = constructor
|
||||
}
|
||||
|
||||
func newExtendedPacket(extension string) ExtendedData {
|
||||
extendedPacketTypes.mu.RLock()
|
||||
defer extendedPacketTypes.mu.RUnlock()
|
||||
|
||||
if f := extendedPacketTypes.constructors[extension]; f != nil {
|
||||
return f()
|
||||
}
|
||||
|
||||
return new(Buffer)
|
||||
}
|
||||
|
||||
// ExtendedPacket defines the SSH_FXP_CLOSE packet.
|
||||
type ExtendedPacket struct {
|
||||
ExtendedRequest string
|
||||
|
||||
Data ExtendedData
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *ExtendedPacket) Type() PacketType {
|
||||
return PacketTypeExtended
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
//
|
||||
// The Data is marshaled into binary, and returned as the payload.
|
||||
func (p *ExtendedPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
size := 4 + len(p.ExtendedRequest) // string(extended-request)
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeExtended, reqid)
|
||||
buf.AppendString(p.ExtendedRequest)
|
||||
|
||||
if p.Data != nil {
|
||||
payload, err = p.Data.MarshalBinary()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
//
|
||||
// If p.Data is nil, and the extension has been registered, a new type will be made from the registration.
|
||||
// If the extension has not been registered, then a new Buffer will be allocated.
|
||||
// Then the request-specific-data will be unmarshaled from the rest of the buffer.
|
||||
func (p *ExtendedPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
p.ExtendedRequest = buf.ConsumeString()
|
||||
if buf.Err != nil {
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
if p.Data == nil {
|
||||
p.Data = newExtendedPacket(p.ExtendedRequest)
|
||||
}
|
||||
|
||||
return p.Data.UnmarshalBinary(buf.Bytes())
|
||||
}
|
||||
|
||||
// ExtendedReplyPacket defines the SSH_FXP_CLOSE packet.
|
||||
type ExtendedReplyPacket struct {
|
||||
Data ExtendedData
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *ExtendedReplyPacket) Type() PacketType {
|
||||
return PacketTypeExtendedReply
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
//
|
||||
// The Data is marshaled into binary, and returned as the payload.
|
||||
func (p *ExtendedReplyPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
buf = NewMarshalBuffer(0)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeExtendedReply, reqid)
|
||||
|
||||
if p.Data != nil {
|
||||
payload, err = p.Data.MarshalBinary()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
//
|
||||
// If p.Data is nil, and there is request-specific-data,
|
||||
// then the request-specific-data will be wrapped in a Buffer and assigned to p.Data.
|
||||
func (p *ExtendedReplyPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
if p.Data == nil {
|
||||
p.Data = new(Buffer)
|
||||
}
|
||||
|
||||
return p.Data.UnmarshalBinary(buf.Bytes())
|
||||
}
|
||||
43
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/extensions.go
generated
vendored
Normal file
43
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/extensions.go
generated
vendored
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
package sshfx
|
||||
|
||||
// ExtensionPair defines the extension-pair type defined in draft-ietf-secsh-filexfer-13.
|
||||
// This type is backwards-compatible with how draft-ietf-secsh-filexfer-02 defines extensions.
|
||||
//
|
||||
// Defined in: https://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-4.2
|
||||
type ExtensionPair struct {
|
||||
Name string
|
||||
Data string
|
||||
}
|
||||
|
||||
// Len returns the number of bytes e would marshal into.
|
||||
func (e *ExtensionPair) Len() int {
|
||||
return 4 + len(e.Name) + 4 + len(e.Data)
|
||||
}
|
||||
|
||||
// MarshalInto marshals e onto the end of the given Buffer.
|
||||
func (e *ExtensionPair) MarshalInto(buf *Buffer) {
|
||||
buf.AppendString(e.Name)
|
||||
buf.AppendString(e.Data)
|
||||
}
|
||||
|
||||
// MarshalBinary returns e as the binary encoding of e.
|
||||
func (e *ExtensionPair) MarshalBinary() ([]byte, error) {
|
||||
buf := NewBuffer(make([]byte, 0, e.Len()))
|
||||
e.MarshalInto(buf)
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// UnmarshalFrom unmarshals an ExtensionPair from the given Buffer into e.
|
||||
func (e *ExtensionPair) UnmarshalFrom(buf *Buffer) (err error) {
|
||||
*e = ExtensionPair{
|
||||
Name: buf.ConsumeString(),
|
||||
Data: buf.ConsumeString(),
|
||||
}
|
||||
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
// UnmarshalBinary decodes the binary encoding of ExtensionPair into e.
|
||||
func (e *ExtensionPair) UnmarshalBinary(data []byte) error {
|
||||
return e.UnmarshalFrom(NewBuffer(data))
|
||||
}
|
||||
54
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/filexfer.go
generated
vendored
Normal file
54
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/filexfer.go
generated
vendored
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
// Package sshfx implements the wire encoding for secsh-filexfer as described in https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-02.txt
|
||||
package sshfx
|
||||
|
||||
// PacketMarshaller narrowly defines packets that will only be transmitted.
|
||||
//
|
||||
// ExtendedPacket types will often only implement this interface,
|
||||
// since decoding the whole packet body of an ExtendedPacket can only be done dependent on the ExtendedRequest field.
|
||||
type PacketMarshaller interface {
|
||||
// MarshalPacket is the primary intended way to encode a packet.
|
||||
// The request-id for the packet is set from reqid.
|
||||
//
|
||||
// An optional buffer may be given in b.
|
||||
// If the buffer has a minimum capacity, it shall be truncated and used to marshal the header into.
|
||||
// The minimum capacity for the packet must be a constant expression, and should be at least 9.
|
||||
//
|
||||
// It shall return the main body of the encoded packet in header,
|
||||
// and may optionally return an additional payload to be written immediately after the header.
|
||||
//
|
||||
// It shall encode in the first 4-bytes of the header the proper length of the rest of the header+payload.
|
||||
MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error)
|
||||
}
|
||||
|
||||
// Packet defines the behavior of a full generic SFTP packet.
|
||||
//
|
||||
// InitPacket, and VersionPacket are not generic SFTP packets, and instead implement (Un)MarshalBinary.
|
||||
//
|
||||
// ExtendedPacket types should not iplement this interface,
|
||||
// since decoding the whole packet body of an ExtendedPacket can only be done dependent on the ExtendedRequest field.
|
||||
type Packet interface {
|
||||
PacketMarshaller
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with the specific packet.
|
||||
Type() PacketType
|
||||
|
||||
// UnmarshalPacketBody decodes a packet body from the given Buffer.
|
||||
// It is assumed that the common header values of the length, type and request-id have already been consumed.
|
||||
//
|
||||
// Implementations should not alias the given Buffer,
|
||||
// instead they can consider prepopulating an internal buffer as a hint,
|
||||
// and copying into that buffer if it has sufficient length.
|
||||
UnmarshalPacketBody(buf *Buffer) error
|
||||
}
|
||||
|
||||
// ComposePacket converts returns from MarshalPacket into an equivalent call to MarshalBinary.
|
||||
func ComposePacket(header, payload []byte, err error) ([]byte, error) {
|
||||
return append(header, payload...), err
|
||||
}
|
||||
|
||||
// Default length values,
|
||||
// Defined in draft-ietf-secsh-filexfer-02 section 3.
|
||||
const (
|
||||
DefaultMaxPacketLength = 34000
|
||||
DefaultMaxDataLength = 32768
|
||||
)
|
||||
147
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/fx.go
generated
vendored
Normal file
147
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/fx.go
generated
vendored
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
package sshfx
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Status defines the SFTP error codes used in SSH_FXP_STATUS response packets.
|
||||
type Status uint32
|
||||
|
||||
// Defines the various SSH_FX_* values.
|
||||
const (
|
||||
// see draft-ietf-secsh-filexfer-02
|
||||
// https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-02.txt#section-7
|
||||
StatusOK = Status(iota)
|
||||
StatusEOF
|
||||
StatusNoSuchFile
|
||||
StatusPermissionDenied
|
||||
StatusFailure
|
||||
StatusBadMessage
|
||||
StatusNoConnection
|
||||
StatusConnectionLost
|
||||
StatusOPUnsupported
|
||||
|
||||
// https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-03.txt#section-7
|
||||
StatusV4InvalidHandle
|
||||
StatusV4NoSuchPath
|
||||
StatusV4FileAlreadyExists
|
||||
StatusV4WriteProtect
|
||||
|
||||
// https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-04.txt#section-7
|
||||
StatusV4NoMedia
|
||||
|
||||
// https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-05.txt#section-7
|
||||
StatusV5NoSpaceOnFilesystem
|
||||
StatusV5QuotaExceeded
|
||||
StatusV5UnknownPrincipal
|
||||
StatusV5LockConflict
|
||||
|
||||
// https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-06.txt#section-8
|
||||
StatusV6DirNotEmpty
|
||||
StatusV6NotADirectory
|
||||
StatusV6InvalidFilename
|
||||
StatusV6LinkLoop
|
||||
|
||||
// https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-07.txt#section-8
|
||||
StatusV6CannotDelete
|
||||
StatusV6InvalidParameter
|
||||
StatusV6FileIsADirectory
|
||||
StatusV6ByteRangeLockConflict
|
||||
StatusV6ByteRangeLockRefused
|
||||
StatusV6DeletePending
|
||||
|
||||
// https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-08.txt#section-8.1
|
||||
StatusV6FileCorrupt
|
||||
|
||||
// https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-10.txt#section-9.1
|
||||
StatusV6OwnerInvalid
|
||||
StatusV6GroupInvalid
|
||||
|
||||
// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-9.1
|
||||
StatusV6NoMatchingByteRangeLock
|
||||
)
|
||||
|
||||
func (s Status) Error() string {
|
||||
return s.String()
|
||||
}
|
||||
|
||||
// Is returns true if the target is the same Status code,
|
||||
// or target is a StatusPacket with the same Status code.
|
||||
func (s Status) Is(target error) bool {
|
||||
if target, ok := target.(*StatusPacket); ok {
|
||||
return target.StatusCode == s
|
||||
}
|
||||
|
||||
return s == target
|
||||
}
|
||||
|
||||
func (s Status) String() string {
|
||||
switch s {
|
||||
case StatusOK:
|
||||
return "SSH_FX_OK"
|
||||
case StatusEOF:
|
||||
return "SSH_FX_EOF"
|
||||
case StatusNoSuchFile:
|
||||
return "SSH_FX_NO_SUCH_FILE"
|
||||
case StatusPermissionDenied:
|
||||
return "SSH_FX_PERMISSION_DENIED"
|
||||
case StatusFailure:
|
||||
return "SSH_FX_FAILURE"
|
||||
case StatusBadMessage:
|
||||
return "SSH_FX_BAD_MESSAGE"
|
||||
case StatusNoConnection:
|
||||
return "SSH_FX_NO_CONNECTION"
|
||||
case StatusConnectionLost:
|
||||
return "SSH_FX_CONNECTION_LOST"
|
||||
case StatusOPUnsupported:
|
||||
return "SSH_FX_OP_UNSUPPORTED"
|
||||
case StatusV4InvalidHandle:
|
||||
return "SSH_FX_INVALID_HANDLE"
|
||||
case StatusV4NoSuchPath:
|
||||
return "SSH_FX_NO_SUCH_PATH"
|
||||
case StatusV4FileAlreadyExists:
|
||||
return "SSH_FX_FILE_ALREADY_EXISTS"
|
||||
case StatusV4WriteProtect:
|
||||
return "SSH_FX_WRITE_PROTECT"
|
||||
case StatusV4NoMedia:
|
||||
return "SSH_FX_NO_MEDIA"
|
||||
case StatusV5NoSpaceOnFilesystem:
|
||||
return "SSH_FX_NO_SPACE_ON_FILESYSTEM"
|
||||
case StatusV5QuotaExceeded:
|
||||
return "SSH_FX_QUOTA_EXCEEDED"
|
||||
case StatusV5UnknownPrincipal:
|
||||
return "SSH_FX_UNKNOWN_PRINCIPAL"
|
||||
case StatusV5LockConflict:
|
||||
return "SSH_FX_LOCK_CONFLICT"
|
||||
case StatusV6DirNotEmpty:
|
||||
return "SSH_FX_DIR_NOT_EMPTY"
|
||||
case StatusV6NotADirectory:
|
||||
return "SSH_FX_NOT_A_DIRECTORY"
|
||||
case StatusV6InvalidFilename:
|
||||
return "SSH_FX_INVALID_FILENAME"
|
||||
case StatusV6LinkLoop:
|
||||
return "SSH_FX_LINK_LOOP"
|
||||
case StatusV6CannotDelete:
|
||||
return "SSH_FX_CANNOT_DELETE"
|
||||
case StatusV6InvalidParameter:
|
||||
return "SSH_FX_INVALID_PARAMETER"
|
||||
case StatusV6FileIsADirectory:
|
||||
return "SSH_FX_FILE_IS_A_DIRECTORY"
|
||||
case StatusV6ByteRangeLockConflict:
|
||||
return "SSH_FX_BYTE_RANGE_LOCK_CONFLICT"
|
||||
case StatusV6ByteRangeLockRefused:
|
||||
return "SSH_FX_BYTE_RANGE_LOCK_REFUSED"
|
||||
case StatusV6DeletePending:
|
||||
return "SSH_FX_DELETE_PENDING"
|
||||
case StatusV6FileCorrupt:
|
||||
return "SSH_FX_FILE_CORRUPT"
|
||||
case StatusV6OwnerInvalid:
|
||||
return "SSH_FX_OWNER_INVALID"
|
||||
case StatusV6GroupInvalid:
|
||||
return "SSH_FX_GROUP_INVALID"
|
||||
case StatusV6NoMatchingByteRangeLock:
|
||||
return "SSH_FX_NO_MATCHING_BYTE_RANGE_LOCK"
|
||||
default:
|
||||
return fmt.Sprintf("SSH_FX_UNKNOWN(%d)", s)
|
||||
}
|
||||
}
|
||||
169
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/fxp.go
generated
vendored
Normal file
169
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/fxp.go
generated
vendored
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
package sshfx
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// PacketType defines the various SFTP packet types.
|
||||
type PacketType uint8
|
||||
|
||||
// Request packet types.
|
||||
const (
|
||||
// https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-02.txt#section-3
|
||||
PacketTypeInit = PacketType(iota + 1)
|
||||
PacketTypeVersion
|
||||
PacketTypeOpen
|
||||
PacketTypeClose
|
||||
PacketTypeRead
|
||||
PacketTypeWrite
|
||||
PacketTypeLStat
|
||||
PacketTypeFStat
|
||||
PacketTypeSetstat
|
||||
PacketTypeFSetstat
|
||||
PacketTypeOpenDir
|
||||
PacketTypeReadDir
|
||||
PacketTypeRemove
|
||||
PacketTypeMkdir
|
||||
PacketTypeRmdir
|
||||
PacketTypeRealPath
|
||||
PacketTypeStat
|
||||
PacketTypeRename
|
||||
PacketTypeReadLink
|
||||
PacketTypeSymlink
|
||||
|
||||
// https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-07.txt#section-3.3
|
||||
PacketTypeV6Link
|
||||
|
||||
// https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-08.txt#section-3.3
|
||||
PacketTypeV6Block
|
||||
PacketTypeV6Unblock
|
||||
)
|
||||
|
||||
// Response packet types.
|
||||
const (
|
||||
// https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-02.txt#section-3
|
||||
PacketTypeStatus = PacketType(iota + 101)
|
||||
PacketTypeHandle
|
||||
PacketTypeData
|
||||
PacketTypeName
|
||||
PacketTypeAttrs
|
||||
)
|
||||
|
||||
// Extended packet types.
|
||||
const (
|
||||
// https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-02.txt#section-3
|
||||
PacketTypeExtended = PacketType(iota + 200)
|
||||
PacketTypeExtendedReply
|
||||
)
|
||||
|
||||
func (f PacketType) String() string {
|
||||
switch f {
|
||||
case PacketTypeInit:
|
||||
return "SSH_FXP_INIT"
|
||||
case PacketTypeVersion:
|
||||
return "SSH_FXP_VERSION"
|
||||
case PacketTypeOpen:
|
||||
return "SSH_FXP_OPEN"
|
||||
case PacketTypeClose:
|
||||
return "SSH_FXP_CLOSE"
|
||||
case PacketTypeRead:
|
||||
return "SSH_FXP_READ"
|
||||
case PacketTypeWrite:
|
||||
return "SSH_FXP_WRITE"
|
||||
case PacketTypeLStat:
|
||||
return "SSH_FXP_LSTAT"
|
||||
case PacketTypeFStat:
|
||||
return "SSH_FXP_FSTAT"
|
||||
case PacketTypeSetstat:
|
||||
return "SSH_FXP_SETSTAT"
|
||||
case PacketTypeFSetstat:
|
||||
return "SSH_FXP_FSETSTAT"
|
||||
case PacketTypeOpenDir:
|
||||
return "SSH_FXP_OPENDIR"
|
||||
case PacketTypeReadDir:
|
||||
return "SSH_FXP_READDIR"
|
||||
case PacketTypeRemove:
|
||||
return "SSH_FXP_REMOVE"
|
||||
case PacketTypeMkdir:
|
||||
return "SSH_FXP_MKDIR"
|
||||
case PacketTypeRmdir:
|
||||
return "SSH_FXP_RMDIR"
|
||||
case PacketTypeRealPath:
|
||||
return "SSH_FXP_REALPATH"
|
||||
case PacketTypeStat:
|
||||
return "SSH_FXP_STAT"
|
||||
case PacketTypeRename:
|
||||
return "SSH_FXP_RENAME"
|
||||
case PacketTypeReadLink:
|
||||
return "SSH_FXP_READLINK"
|
||||
case PacketTypeSymlink:
|
||||
return "SSH_FXP_SYMLINK"
|
||||
case PacketTypeV6Link:
|
||||
return "SSH_FXP_LINK"
|
||||
case PacketTypeV6Block:
|
||||
return "SSH_FXP_BLOCK"
|
||||
case PacketTypeV6Unblock:
|
||||
return "SSH_FXP_UNBLOCK"
|
||||
case PacketTypeStatus:
|
||||
return "SSH_FXP_STATUS"
|
||||
case PacketTypeHandle:
|
||||
return "SSH_FXP_HANDLE"
|
||||
case PacketTypeData:
|
||||
return "SSH_FXP_DATA"
|
||||
case PacketTypeName:
|
||||
return "SSH_FXP_NAME"
|
||||
case PacketTypeAttrs:
|
||||
return "SSH_FXP_ATTRS"
|
||||
case PacketTypeExtended:
|
||||
return "SSH_FXP_EXTENDED"
|
||||
case PacketTypeExtendedReply:
|
||||
return "SSH_FXP_EXTENDED_REPLY"
|
||||
default:
|
||||
return fmt.Sprintf("SSH_FXP_UNKNOWN(%d)", f)
|
||||
}
|
||||
}
|
||||
|
||||
func newPacketFromType(typ PacketType) (Packet, error) {
|
||||
switch typ {
|
||||
case PacketTypeOpen:
|
||||
return new(OpenPacket), nil
|
||||
case PacketTypeClose:
|
||||
return new(ClosePacket), nil
|
||||
case PacketTypeRead:
|
||||
return new(ReadPacket), nil
|
||||
case PacketTypeWrite:
|
||||
return new(WritePacket), nil
|
||||
case PacketTypeLStat:
|
||||
return new(LStatPacket), nil
|
||||
case PacketTypeFStat:
|
||||
return new(FStatPacket), nil
|
||||
case PacketTypeSetstat:
|
||||
return new(SetstatPacket), nil
|
||||
case PacketTypeFSetstat:
|
||||
return new(FSetstatPacket), nil
|
||||
case PacketTypeOpenDir:
|
||||
return new(OpenDirPacket), nil
|
||||
case PacketTypeReadDir:
|
||||
return new(ReadDirPacket), nil
|
||||
case PacketTypeRemove:
|
||||
return new(RemovePacket), nil
|
||||
case PacketTypeMkdir:
|
||||
return new(MkdirPacket), nil
|
||||
case PacketTypeRmdir:
|
||||
return new(RmdirPacket), nil
|
||||
case PacketTypeRealPath:
|
||||
return new(RealPathPacket), nil
|
||||
case PacketTypeStat:
|
||||
return new(StatPacket), nil
|
||||
case PacketTypeRename:
|
||||
return new(RenamePacket), nil
|
||||
case PacketTypeReadLink:
|
||||
return new(ReadLinkPacket), nil
|
||||
case PacketTypeSymlink:
|
||||
return new(SymlinkPacket), nil
|
||||
case PacketTypeExtended:
|
||||
return new(ExtendedPacket), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unexpected request packet type: %v", typ)
|
||||
}
|
||||
}
|
||||
230
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/handle_packets.go
generated
vendored
Normal file
230
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/handle_packets.go
generated
vendored
Normal file
|
|
@ -0,0 +1,230 @@
|
|||
package sshfx
|
||||
|
||||
// ClosePacket defines the SSH_FXP_CLOSE packet.
|
||||
type ClosePacket struct {
|
||||
Handle string
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *ClosePacket) Type() PacketType {
|
||||
return PacketTypeClose
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *ClosePacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
size := 4 + len(p.Handle) // string(handle)
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeClose, reqid)
|
||||
buf.AppendString(p.Handle)
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *ClosePacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
*p = ClosePacket{
|
||||
Handle: buf.ConsumeString(),
|
||||
}
|
||||
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
// ReadPacket defines the SSH_FXP_READ packet.
|
||||
type ReadPacket struct {
|
||||
Handle string
|
||||
Offset uint64
|
||||
Length uint32
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *ReadPacket) Type() PacketType {
|
||||
return PacketTypeRead
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *ReadPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
// string(handle) + uint64(offset) + uint32(len)
|
||||
size := 4 + len(p.Handle) + 8 + 4
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeRead, reqid)
|
||||
buf.AppendString(p.Handle)
|
||||
buf.AppendUint64(p.Offset)
|
||||
buf.AppendUint32(p.Length)
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *ReadPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
*p = ReadPacket{
|
||||
Handle: buf.ConsumeString(),
|
||||
Offset: buf.ConsumeUint64(),
|
||||
Length: buf.ConsumeUint32(),
|
||||
}
|
||||
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
// WritePacket defines the SSH_FXP_WRITE packet.
|
||||
type WritePacket struct {
|
||||
Handle string
|
||||
Offset uint64
|
||||
Data []byte
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *WritePacket) Type() PacketType {
|
||||
return PacketTypeWrite
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *WritePacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
// string(handle) + uint64(offset) + uint32(len(data)); data content in payload
|
||||
size := 4 + len(p.Handle) + 8 + 4
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeWrite, reqid)
|
||||
buf.AppendString(p.Handle)
|
||||
buf.AppendUint64(p.Offset)
|
||||
buf.AppendUint32(uint32(len(p.Data)))
|
||||
|
||||
return buf.Packet(p.Data)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
//
|
||||
// If p.Data is already populated, and of sufficient length to hold the data,
|
||||
// then this will copy the data into that byte slice.
|
||||
//
|
||||
// If p.Data has a length insufficient to hold the data,
|
||||
// then this will make a new slice of sufficient length, and copy the data into that.
|
||||
//
|
||||
// This means this _does not_ alias any of the data buffer that is passed in.
|
||||
func (p *WritePacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
*p = WritePacket{
|
||||
Handle: buf.ConsumeString(),
|
||||
Offset: buf.ConsumeUint64(),
|
||||
Data: buf.ConsumeByteSliceCopy(p.Data),
|
||||
}
|
||||
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
// FStatPacket defines the SSH_FXP_FSTAT packet.
|
||||
type FStatPacket struct {
|
||||
Handle string
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *FStatPacket) Type() PacketType {
|
||||
return PacketTypeFStat
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *FStatPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
size := 4 + len(p.Handle) // string(handle)
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeFStat, reqid)
|
||||
buf.AppendString(p.Handle)
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *FStatPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
*p = FStatPacket{
|
||||
Handle: buf.ConsumeString(),
|
||||
}
|
||||
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
// FSetstatPacket defines the SSH_FXP_FSETSTAT packet.
|
||||
type FSetstatPacket struct {
|
||||
Handle string
|
||||
Attrs Attributes
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *FSetstatPacket) Type() PacketType {
|
||||
return PacketTypeFSetstat
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *FSetstatPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
size := 4 + len(p.Handle) + p.Attrs.Len() // string(handle) + ATTRS(attrs)
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeFSetstat, reqid)
|
||||
buf.AppendString(p.Handle)
|
||||
|
||||
p.Attrs.MarshalInto(buf)
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *FSetstatPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
*p = FSetstatPacket{
|
||||
Handle: buf.ConsumeString(),
|
||||
}
|
||||
|
||||
return p.Attrs.UnmarshalFrom(buf)
|
||||
}
|
||||
|
||||
// ReadDirPacket defines the SSH_FXP_READDIR packet.
|
||||
type ReadDirPacket struct {
|
||||
Handle string
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *ReadDirPacket) Type() PacketType {
|
||||
return PacketTypeReadDir
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *ReadDirPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
size := 4 + len(p.Handle) // string(handle)
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeReadDir, reqid)
|
||||
buf.AppendString(p.Handle)
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *ReadDirPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
*p = ReadDirPacket{
|
||||
Handle: buf.ConsumeString(),
|
||||
}
|
||||
|
||||
return buf.Err
|
||||
}
|
||||
99
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/init_packets.go
generated
vendored
Normal file
99
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/init_packets.go
generated
vendored
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
package sshfx
|
||||
|
||||
// InitPacket defines the SSH_FXP_INIT packet.
|
||||
type InitPacket struct {
|
||||
Version uint32
|
||||
Extensions []*ExtensionPair
|
||||
}
|
||||
|
||||
// MarshalBinary returns p as the binary encoding of p.
|
||||
func (p *InitPacket) MarshalBinary() ([]byte, error) {
|
||||
size := 1 + 4 // byte(type) + uint32(version)
|
||||
|
||||
for _, ext := range p.Extensions {
|
||||
size += ext.Len()
|
||||
}
|
||||
|
||||
b := NewBuffer(make([]byte, 4, 4+size))
|
||||
b.AppendUint8(uint8(PacketTypeInit))
|
||||
b.AppendUint32(p.Version)
|
||||
|
||||
for _, ext := range p.Extensions {
|
||||
ext.MarshalInto(b)
|
||||
}
|
||||
|
||||
b.PutLength(size)
|
||||
|
||||
return b.Bytes(), nil
|
||||
}
|
||||
|
||||
// UnmarshalBinary unmarshals a full raw packet out of the given data.
|
||||
// It is assumed that the uint32(length) has already been consumed to receive the data.
|
||||
// It is also assumed that the uint8(type) has already been consumed to which packet to unmarshal into.
|
||||
func (p *InitPacket) UnmarshalBinary(data []byte) (err error) {
|
||||
buf := NewBuffer(data)
|
||||
|
||||
*p = InitPacket{
|
||||
Version: buf.ConsumeUint32(),
|
||||
}
|
||||
|
||||
for buf.Len() > 0 {
|
||||
var ext ExtensionPair
|
||||
if err := ext.UnmarshalFrom(buf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.Extensions = append(p.Extensions, &ext)
|
||||
}
|
||||
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
// VersionPacket defines the SSH_FXP_VERSION packet.
|
||||
type VersionPacket struct {
|
||||
Version uint32
|
||||
Extensions []*ExtensionPair
|
||||
}
|
||||
|
||||
// MarshalBinary returns p as the binary encoding of p.
|
||||
func (p *VersionPacket) MarshalBinary() ([]byte, error) {
|
||||
size := 1 + 4 // byte(type) + uint32(version)
|
||||
|
||||
for _, ext := range p.Extensions {
|
||||
size += ext.Len()
|
||||
}
|
||||
|
||||
b := NewBuffer(make([]byte, 4, 4+size))
|
||||
b.AppendUint8(uint8(PacketTypeVersion))
|
||||
b.AppendUint32(p.Version)
|
||||
|
||||
for _, ext := range p.Extensions {
|
||||
ext.MarshalInto(b)
|
||||
}
|
||||
|
||||
b.PutLength(size)
|
||||
|
||||
return b.Bytes(), nil
|
||||
}
|
||||
|
||||
// UnmarshalBinary unmarshals a full raw packet out of the given data.
|
||||
// It is assumed that the uint32(length) has already been consumed to receive the data.
|
||||
// It is also assumed that the uint8(type) has already been consumed to which packet to unmarshal into.
|
||||
func (p *VersionPacket) UnmarshalBinary(data []byte) (err error) {
|
||||
buf := NewBuffer(data)
|
||||
|
||||
*p = VersionPacket{
|
||||
Version: buf.ConsumeUint32(),
|
||||
}
|
||||
|
||||
for buf.Len() > 0 {
|
||||
var ext ExtensionPair
|
||||
if err := ext.UnmarshalFrom(buf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.Extensions = append(p.Extensions, &ext)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
86
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/open_packets.go
generated
vendored
Normal file
86
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/open_packets.go
generated
vendored
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
package sshfx
|
||||
|
||||
// SSH_FXF_* flags.
|
||||
const (
|
||||
FlagRead = 1 << iota // SSH_FXF_READ
|
||||
FlagWrite // SSH_FXF_WRITE
|
||||
FlagAppend // SSH_FXF_APPEND
|
||||
FlagCreate // SSH_FXF_CREAT
|
||||
FlagTruncate // SSH_FXF_TRUNC
|
||||
FlagExclusive // SSH_FXF_EXCL
|
||||
)
|
||||
|
||||
// OpenPacket defines the SSH_FXP_OPEN packet.
|
||||
type OpenPacket struct {
|
||||
Filename string
|
||||
PFlags uint32
|
||||
Attrs Attributes
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *OpenPacket) Type() PacketType {
|
||||
return PacketTypeOpen
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *OpenPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
// string(filename) + uint32(pflags) + ATTRS(attrs)
|
||||
size := 4 + len(p.Filename) + 4 + p.Attrs.Len()
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeOpen, reqid)
|
||||
buf.AppendString(p.Filename)
|
||||
buf.AppendUint32(p.PFlags)
|
||||
|
||||
p.Attrs.MarshalInto(buf)
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *OpenPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
*p = OpenPacket{
|
||||
Filename: buf.ConsumeString(),
|
||||
PFlags: buf.ConsumeUint32(),
|
||||
}
|
||||
|
||||
return p.Attrs.UnmarshalFrom(buf)
|
||||
}
|
||||
|
||||
// OpenDirPacket defines the SSH_FXP_OPENDIR packet.
|
||||
type OpenDirPacket struct {
|
||||
Path string
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *OpenDirPacket) Type() PacketType {
|
||||
return PacketTypeOpenDir
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *OpenDirPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
size := 4 + len(p.Path) // string(path)
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeOpenDir, reqid)
|
||||
buf.AppendString(p.Path)
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *OpenDirPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
*p = OpenDirPacket{
|
||||
Path: buf.ConsumeString(),
|
||||
}
|
||||
|
||||
return buf.Err
|
||||
}
|
||||
73
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/openssh/fsync.go
generated
vendored
Normal file
73
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/openssh/fsync.go
generated
vendored
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
package openssh
|
||||
|
||||
import (
|
||||
sshfx "github.com/pkg/sftp/internal/encoding/ssh/filexfer"
|
||||
)
|
||||
|
||||
const extensionFSync = "fsync@openssh.com"
|
||||
|
||||
// RegisterExtensionFSync registers the "fsync@openssh.com" extended packet with the encoding/ssh/filexfer package.
|
||||
func RegisterExtensionFSync() {
|
||||
sshfx.RegisterExtendedPacketType(extensionFSync, func() sshfx.ExtendedData {
|
||||
return new(FSyncExtendedPacket)
|
||||
})
|
||||
}
|
||||
|
||||
// ExtensionFSync returns an ExtensionPair suitable to append into an sshfx.InitPacket or sshfx.VersionPacket.
|
||||
func ExtensionFSync() *sshfx.ExtensionPair {
|
||||
return &sshfx.ExtensionPair{
|
||||
Name: extensionFSync,
|
||||
Data: "1",
|
||||
}
|
||||
}
|
||||
|
||||
// FSyncExtendedPacket defines the fsync@openssh.com extend packet.
|
||||
type FSyncExtendedPacket struct {
|
||||
Handle string
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_EXTENDED packet type.
|
||||
func (ep *FSyncExtendedPacket) Type() sshfx.PacketType {
|
||||
return sshfx.PacketTypeExtended
|
||||
}
|
||||
|
||||
// MarshalPacket returns ep as a two-part binary encoding of the full extended packet.
|
||||
func (ep *FSyncExtendedPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
p := &sshfx.ExtendedPacket{
|
||||
ExtendedRequest: extensionFSync,
|
||||
|
||||
Data: ep,
|
||||
}
|
||||
return p.MarshalPacket(reqid, b)
|
||||
}
|
||||
|
||||
// MarshalInto encodes ep into the binary encoding of the fsync@openssh.com extended packet-specific data.
|
||||
func (ep *FSyncExtendedPacket) MarshalInto(buf *sshfx.Buffer) {
|
||||
buf.AppendString(ep.Handle)
|
||||
}
|
||||
|
||||
// MarshalBinary encodes ep into the binary encoding of the fsync@openssh.com extended packet-specific data.
|
||||
//
|
||||
// NOTE: This _only_ encodes the packet-specific data, it does not encode the full extended packet.
|
||||
func (ep *FSyncExtendedPacket) MarshalBinary() ([]byte, error) {
|
||||
// string(handle)
|
||||
size := 4 + len(ep.Handle)
|
||||
|
||||
buf := sshfx.NewBuffer(make([]byte, 0, size))
|
||||
ep.MarshalInto(buf)
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// UnmarshalFrom decodes the fsync@openssh.com extended packet-specific data from buf.
|
||||
func (ep *FSyncExtendedPacket) UnmarshalFrom(buf *sshfx.Buffer) (err error) {
|
||||
*ep = FSyncExtendedPacket{
|
||||
Handle: buf.ConsumeString(),
|
||||
}
|
||||
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
// UnmarshalBinary decodes the fsync@openssh.com extended packet-specific data into ep.
|
||||
func (ep *FSyncExtendedPacket) UnmarshalBinary(data []byte) (err error) {
|
||||
return ep.UnmarshalFrom(sshfx.NewBuffer(data))
|
||||
}
|
||||
76
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/openssh/hardlink.go
generated
vendored
Normal file
76
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/openssh/hardlink.go
generated
vendored
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
package openssh
|
||||
|
||||
import (
|
||||
sshfx "github.com/pkg/sftp/internal/encoding/ssh/filexfer"
|
||||
)
|
||||
|
||||
const extensionHardlink = "hardlink@openssh.com"
|
||||
|
||||
// RegisterExtensionHardlink registers the "hardlink@openssh.com" extended packet with the encoding/ssh/filexfer package.
|
||||
func RegisterExtensionHardlink() {
|
||||
sshfx.RegisterExtendedPacketType(extensionHardlink, func() sshfx.ExtendedData {
|
||||
return new(HardlinkExtendedPacket)
|
||||
})
|
||||
}
|
||||
|
||||
// ExtensionHardlink returns an ExtensionPair suitable to append into an sshfx.InitPacket or sshfx.VersionPacket.
|
||||
func ExtensionHardlink() *sshfx.ExtensionPair {
|
||||
return &sshfx.ExtensionPair{
|
||||
Name: extensionHardlink,
|
||||
Data: "1",
|
||||
}
|
||||
}
|
||||
|
||||
// HardlinkExtendedPacket defines the hardlink@openssh.com extend packet.
|
||||
type HardlinkExtendedPacket struct {
|
||||
OldPath string
|
||||
NewPath string
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_EXTENDED packet type.
|
||||
func (ep *HardlinkExtendedPacket) Type() sshfx.PacketType {
|
||||
return sshfx.PacketTypeExtended
|
||||
}
|
||||
|
||||
// MarshalPacket returns ep as a two-part binary encoding of the full extended packet.
|
||||
func (ep *HardlinkExtendedPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
p := &sshfx.ExtendedPacket{
|
||||
ExtendedRequest: extensionHardlink,
|
||||
|
||||
Data: ep,
|
||||
}
|
||||
return p.MarshalPacket(reqid, b)
|
||||
}
|
||||
|
||||
// MarshalInto encodes ep into the binary encoding of the hardlink@openssh.com extended packet-specific data.
|
||||
func (ep *HardlinkExtendedPacket) MarshalInto(buf *sshfx.Buffer) {
|
||||
buf.AppendString(ep.OldPath)
|
||||
buf.AppendString(ep.NewPath)
|
||||
}
|
||||
|
||||
// MarshalBinary encodes ep into the binary encoding of the hardlink@openssh.com extended packet-specific data.
|
||||
//
|
||||
// NOTE: This _only_ encodes the packet-specific data, it does not encode the full extended packet.
|
||||
func (ep *HardlinkExtendedPacket) MarshalBinary() ([]byte, error) {
|
||||
// string(oldpath) + string(newpath)
|
||||
size := 4 + len(ep.OldPath) + 4 + len(ep.NewPath)
|
||||
|
||||
buf := sshfx.NewBuffer(make([]byte, 0, size))
|
||||
ep.MarshalInto(buf)
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// UnmarshalFrom decodes the hardlink@openssh.com extended packet-specific data from buf.
|
||||
func (ep *HardlinkExtendedPacket) UnmarshalFrom(buf *sshfx.Buffer) (err error) {
|
||||
*ep = HardlinkExtendedPacket{
|
||||
OldPath: buf.ConsumeString(),
|
||||
NewPath: buf.ConsumeString(),
|
||||
}
|
||||
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
// UnmarshalBinary decodes the hardlink@openssh.com extended packet-specific data into ep.
|
||||
func (ep *HardlinkExtendedPacket) UnmarshalBinary(data []byte) (err error) {
|
||||
return ep.UnmarshalFrom(sshfx.NewBuffer(data))
|
||||
}
|
||||
2
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/openssh/openssh.go
generated
vendored
Normal file
2
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/openssh/openssh.go
generated
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
// Package openssh implements the openssh secsh-filexfer extensions as described in https://github.com/openssh/openssh-portable/blob/master/PROTOCOL
|
||||
package openssh
|
||||
76
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/openssh/posix-rename.go
generated
vendored
Normal file
76
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/openssh/posix-rename.go
generated
vendored
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
package openssh
|
||||
|
||||
import (
|
||||
sshfx "github.com/pkg/sftp/internal/encoding/ssh/filexfer"
|
||||
)
|
||||
|
||||
const extensionPOSIXRename = "posix-rename@openssh.com"
|
||||
|
||||
// RegisterExtensionPOSIXRename registers the "posix-rename@openssh.com" extended packet with the encoding/ssh/filexfer package.
|
||||
func RegisterExtensionPOSIXRename() {
|
||||
sshfx.RegisterExtendedPacketType(extensionPOSIXRename, func() sshfx.ExtendedData {
|
||||
return new(POSIXRenameExtendedPacket)
|
||||
})
|
||||
}
|
||||
|
||||
// ExtensionPOSIXRename returns an ExtensionPair suitable to append into an sshfx.InitPacket or sshfx.VersionPacket.
|
||||
func ExtensionPOSIXRename() *sshfx.ExtensionPair {
|
||||
return &sshfx.ExtensionPair{
|
||||
Name: extensionPOSIXRename,
|
||||
Data: "1",
|
||||
}
|
||||
}
|
||||
|
||||
// POSIXRenameExtendedPacket defines the posix-rename@openssh.com extend packet.
|
||||
type POSIXRenameExtendedPacket struct {
|
||||
OldPath string
|
||||
NewPath string
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_EXTENDED packet type.
|
||||
func (ep *POSIXRenameExtendedPacket) Type() sshfx.PacketType {
|
||||
return sshfx.PacketTypeExtended
|
||||
}
|
||||
|
||||
// MarshalPacket returns ep as a two-part binary encoding of the full extended packet.
|
||||
func (ep *POSIXRenameExtendedPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
p := &sshfx.ExtendedPacket{
|
||||
ExtendedRequest: extensionPOSIXRename,
|
||||
|
||||
Data: ep,
|
||||
}
|
||||
return p.MarshalPacket(reqid, b)
|
||||
}
|
||||
|
||||
// MarshalInto encodes ep into the binary encoding of the hardlink@openssh.com extended packet-specific data.
|
||||
func (ep *POSIXRenameExtendedPacket) MarshalInto(buf *sshfx.Buffer) {
|
||||
buf.AppendString(ep.OldPath)
|
||||
buf.AppendString(ep.NewPath)
|
||||
}
|
||||
|
||||
// MarshalBinary encodes ep into the binary encoding of the hardlink@openssh.com extended packet-specific data.
|
||||
//
|
||||
// NOTE: This _only_ encodes the packet-specific data, it does not encode the full extended packet.
|
||||
func (ep *POSIXRenameExtendedPacket) MarshalBinary() ([]byte, error) {
|
||||
// string(oldpath) + string(newpath)
|
||||
size := 4 + len(ep.OldPath) + 4 + len(ep.NewPath)
|
||||
|
||||
buf := sshfx.NewBuffer(make([]byte, 0, size))
|
||||
ep.MarshalInto(buf)
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// UnmarshalFrom decodes the hardlink@openssh.com extended packet-specific data from buf.
|
||||
func (ep *POSIXRenameExtendedPacket) UnmarshalFrom(buf *sshfx.Buffer) (err error) {
|
||||
*ep = POSIXRenameExtendedPacket{
|
||||
OldPath: buf.ConsumeString(),
|
||||
NewPath: buf.ConsumeString(),
|
||||
}
|
||||
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
// UnmarshalBinary decodes the hardlink@openssh.com extended packet-specific data into ep.
|
||||
func (ep *POSIXRenameExtendedPacket) UnmarshalBinary(data []byte) (err error) {
|
||||
return ep.UnmarshalFrom(sshfx.NewBuffer(data))
|
||||
}
|
||||
236
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/openssh/statvfs.go
generated
vendored
Normal file
236
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/openssh/statvfs.go
generated
vendored
Normal file
|
|
@ -0,0 +1,236 @@
|
|||
package openssh
|
||||
|
||||
import (
|
||||
sshfx "github.com/pkg/sftp/internal/encoding/ssh/filexfer"
|
||||
)
|
||||
|
||||
const extensionStatVFS = "statvfs@openssh.com"
|
||||
|
||||
// RegisterExtensionStatVFS registers the "statvfs@openssh.com" extended packet with the encoding/ssh/filexfer package.
|
||||
func RegisterExtensionStatVFS() {
|
||||
sshfx.RegisterExtendedPacketType(extensionStatVFS, func() sshfx.ExtendedData {
|
||||
return new(StatVFSExtendedPacket)
|
||||
})
|
||||
}
|
||||
|
||||
// ExtensionStatVFS returns an ExtensionPair suitable to append into an sshfx.InitPacket or sshfx.VersionPacket.
|
||||
func ExtensionStatVFS() *sshfx.ExtensionPair {
|
||||
return &sshfx.ExtensionPair{
|
||||
Name: extensionStatVFS,
|
||||
Data: "2",
|
||||
}
|
||||
}
|
||||
|
||||
// StatVFSExtendedPacket defines the statvfs@openssh.com extend packet.
|
||||
type StatVFSExtendedPacket struct {
|
||||
Path string
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_EXTENDED packet type.
|
||||
func (ep *StatVFSExtendedPacket) Type() sshfx.PacketType {
|
||||
return sshfx.PacketTypeExtended
|
||||
}
|
||||
|
||||
// MarshalPacket returns ep as a two-part binary encoding of the full extended packet.
|
||||
func (ep *StatVFSExtendedPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
p := &sshfx.ExtendedPacket{
|
||||
ExtendedRequest: extensionStatVFS,
|
||||
|
||||
Data: ep,
|
||||
}
|
||||
return p.MarshalPacket(reqid, b)
|
||||
}
|
||||
|
||||
// MarshalInto encodes ep into the binary encoding of the statvfs@openssh.com extended packet-specific data.
|
||||
func (ep *StatVFSExtendedPacket) MarshalInto(buf *sshfx.Buffer) {
|
||||
buf.AppendString(ep.Path)
|
||||
}
|
||||
|
||||
// MarshalBinary encodes ep into the binary encoding of the statvfs@openssh.com extended packet-specific data.
|
||||
//
|
||||
// NOTE: This _only_ encodes the packet-specific data, it does not encode the full extended packet.
|
||||
func (ep *StatVFSExtendedPacket) MarshalBinary() ([]byte, error) {
|
||||
size := 4 + len(ep.Path) // string(path)
|
||||
|
||||
buf := sshfx.NewBuffer(make([]byte, 0, size))
|
||||
|
||||
ep.MarshalInto(buf)
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// UnmarshalFrom decodes the statvfs@openssh.com extended packet-specific data into ep.
|
||||
func (ep *StatVFSExtendedPacket) UnmarshalFrom(buf *sshfx.Buffer) (err error) {
|
||||
*ep = StatVFSExtendedPacket{
|
||||
Path: buf.ConsumeString(),
|
||||
}
|
||||
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
// UnmarshalBinary decodes the statvfs@openssh.com extended packet-specific data into ep.
|
||||
func (ep *StatVFSExtendedPacket) UnmarshalBinary(data []byte) (err error) {
|
||||
return ep.UnmarshalFrom(sshfx.NewBuffer(data))
|
||||
}
|
||||
|
||||
const extensionFStatVFS = "fstatvfs@openssh.com"
|
||||
|
||||
// RegisterExtensionFStatVFS registers the "fstatvfs@openssh.com" extended packet with the encoding/ssh/filexfer package.
|
||||
func RegisterExtensionFStatVFS() {
|
||||
sshfx.RegisterExtendedPacketType(extensionFStatVFS, func() sshfx.ExtendedData {
|
||||
return new(FStatVFSExtendedPacket)
|
||||
})
|
||||
}
|
||||
|
||||
// ExtensionFStatVFS returns an ExtensionPair suitable to append into an sshfx.InitPacket or sshfx.VersionPacket.
|
||||
func ExtensionFStatVFS() *sshfx.ExtensionPair {
|
||||
return &sshfx.ExtensionPair{
|
||||
Name: extensionFStatVFS,
|
||||
Data: "2",
|
||||
}
|
||||
}
|
||||
|
||||
// FStatVFSExtendedPacket defines the fstatvfs@openssh.com extend packet.
|
||||
type FStatVFSExtendedPacket struct {
|
||||
Path string
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_EXTENDED packet type.
|
||||
func (ep *FStatVFSExtendedPacket) Type() sshfx.PacketType {
|
||||
return sshfx.PacketTypeExtended
|
||||
}
|
||||
|
||||
// MarshalPacket returns ep as a two-part binary encoding of the full extended packet.
|
||||
func (ep *FStatVFSExtendedPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
p := &sshfx.ExtendedPacket{
|
||||
ExtendedRequest: extensionFStatVFS,
|
||||
|
||||
Data: ep,
|
||||
}
|
||||
return p.MarshalPacket(reqid, b)
|
||||
}
|
||||
|
||||
// MarshalInto encodes ep into the binary encoding of the statvfs@openssh.com extended packet-specific data.
|
||||
func (ep *FStatVFSExtendedPacket) MarshalInto(buf *sshfx.Buffer) {
|
||||
buf.AppendString(ep.Path)
|
||||
}
|
||||
|
||||
// MarshalBinary encodes ep into the binary encoding of the statvfs@openssh.com extended packet-specific data.
|
||||
//
|
||||
// NOTE: This _only_ encodes the packet-specific data, it does not encode the full extended packet.
|
||||
func (ep *FStatVFSExtendedPacket) MarshalBinary() ([]byte, error) {
|
||||
size := 4 + len(ep.Path) // string(path)
|
||||
|
||||
buf := sshfx.NewBuffer(make([]byte, 0, size))
|
||||
|
||||
ep.MarshalInto(buf)
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// UnmarshalFrom decodes the statvfs@openssh.com extended packet-specific data into ep.
|
||||
func (ep *FStatVFSExtendedPacket) UnmarshalFrom(buf *sshfx.Buffer) (err error) {
|
||||
*ep = FStatVFSExtendedPacket{
|
||||
Path: buf.ConsumeString(),
|
||||
}
|
||||
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
// UnmarshalBinary decodes the statvfs@openssh.com extended packet-specific data into ep.
|
||||
func (ep *FStatVFSExtendedPacket) UnmarshalBinary(data []byte) (err error) {
|
||||
return ep.UnmarshalFrom(sshfx.NewBuffer(data))
|
||||
}
|
||||
|
||||
// The values for the MountFlags field.
|
||||
// https://github.com/openssh/openssh-portable/blob/master/PROTOCOL
|
||||
const (
|
||||
MountFlagsReadOnly = 0x1 // SSH_FXE_STATVFS_ST_RDONLY
|
||||
MountFlagsNoSUID = 0x2 // SSH_FXE_STATVFS_ST_NOSUID
|
||||
)
|
||||
|
||||
// StatVFSExtendedReplyPacket defines the extended reply packet for statvfs@openssh.com and fstatvfs@openssh.com requests.
|
||||
type StatVFSExtendedReplyPacket struct {
|
||||
BlockSize uint64 /* f_bsize: file system block size */
|
||||
FragmentSize uint64 /* f_frsize: fundamental fs block size / fagment size */
|
||||
Blocks uint64 /* f_blocks: number of blocks (unit f_frsize) */
|
||||
BlocksFree uint64 /* f_bfree: free blocks in filesystem */
|
||||
BlocksAvail uint64 /* f_bavail: free blocks for non-root */
|
||||
Files uint64 /* f_files: total file inodes */
|
||||
FilesFree uint64 /* f_ffree: free file inodes */
|
||||
FilesAvail uint64 /* f_favail: free file inodes for to non-root */
|
||||
FilesystemID uint64 /* f_fsid: file system id */
|
||||
MountFlags uint64 /* f_flag: bit mask of mount flag values */
|
||||
MaxNameLength uint64 /* f_namemax: maximum filename length */
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_EXTENDED_REPLY packet type.
|
||||
func (ep *StatVFSExtendedReplyPacket) Type() sshfx.PacketType {
|
||||
return sshfx.PacketTypeExtendedReply
|
||||
}
|
||||
|
||||
// MarshalPacket returns ep as a two-part binary encoding of the full extended reply packet.
|
||||
func (ep *StatVFSExtendedReplyPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
p := &sshfx.ExtendedReplyPacket{
|
||||
Data: ep,
|
||||
}
|
||||
return p.MarshalPacket(reqid, b)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody returns ep as a two-part binary encoding of the full extended reply packet.
|
||||
func (ep *StatVFSExtendedReplyPacket) UnmarshalPacketBody(buf *sshfx.Buffer) (err error) {
|
||||
p := &sshfx.ExtendedReplyPacket{
|
||||
Data: ep,
|
||||
}
|
||||
return p.UnmarshalPacketBody(buf)
|
||||
}
|
||||
|
||||
// MarshalInto encodes ep into the binary encoding of the (f)statvfs@openssh.com extended reply packet-specific data.
|
||||
func (ep *StatVFSExtendedReplyPacket) MarshalInto(buf *sshfx.Buffer) {
|
||||
buf.AppendUint64(ep.BlockSize)
|
||||
buf.AppendUint64(ep.FragmentSize)
|
||||
buf.AppendUint64(ep.Blocks)
|
||||
buf.AppendUint64(ep.BlocksFree)
|
||||
buf.AppendUint64(ep.BlocksAvail)
|
||||
buf.AppendUint64(ep.Files)
|
||||
buf.AppendUint64(ep.FilesFree)
|
||||
buf.AppendUint64(ep.FilesAvail)
|
||||
buf.AppendUint64(ep.FilesystemID)
|
||||
buf.AppendUint64(ep.MountFlags)
|
||||
buf.AppendUint64(ep.MaxNameLength)
|
||||
}
|
||||
|
||||
// MarshalBinary encodes ep into the binary encoding of the (f)statvfs@openssh.com extended reply packet-specific data.
|
||||
//
|
||||
// NOTE: This _only_ encodes the packet-specific data, it does not encode the full extended reply packet.
|
||||
func (ep *StatVFSExtendedReplyPacket) MarshalBinary() ([]byte, error) {
|
||||
size := 11 * 8 // 11 × uint64(various)
|
||||
|
||||
b := sshfx.NewBuffer(make([]byte, 0, size))
|
||||
ep.MarshalInto(b)
|
||||
return b.Bytes(), nil
|
||||
}
|
||||
|
||||
// UnmarshalFrom decodes the fstatvfs@openssh.com extended reply packet-specific data into ep.
|
||||
func (ep *StatVFSExtendedReplyPacket) UnmarshalFrom(buf *sshfx.Buffer) (err error) {
|
||||
*ep = StatVFSExtendedReplyPacket{
|
||||
BlockSize: buf.ConsumeUint64(),
|
||||
FragmentSize: buf.ConsumeUint64(),
|
||||
Blocks: buf.ConsumeUint64(),
|
||||
BlocksFree: buf.ConsumeUint64(),
|
||||
BlocksAvail: buf.ConsumeUint64(),
|
||||
Files: buf.ConsumeUint64(),
|
||||
FilesFree: buf.ConsumeUint64(),
|
||||
FilesAvail: buf.ConsumeUint64(),
|
||||
FilesystemID: buf.ConsumeUint64(),
|
||||
MountFlags: buf.ConsumeUint64(),
|
||||
MaxNameLength: buf.ConsumeUint64(),
|
||||
}
|
||||
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
// UnmarshalBinary decodes the fstatvfs@openssh.com extended reply packet-specific data into ep.
|
||||
func (ep *StatVFSExtendedReplyPacket) UnmarshalBinary(data []byte) (err error) {
|
||||
return ep.UnmarshalFrom(sshfx.NewBuffer(data))
|
||||
}
|
||||
273
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/packets.go
generated
vendored
Normal file
273
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/packets.go
generated
vendored
Normal file
|
|
@ -0,0 +1,273 @@
|
|||
package sshfx
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
// smallBufferSize is an initial allocation minimal capacity.
|
||||
const smallBufferSize = 64
|
||||
|
||||
// RawPacket implements the general packet format from draft-ietf-secsh-filexfer-02
|
||||
//
|
||||
// RawPacket is intended for use in clients receiving responses,
|
||||
// where a response will be expected to be of a limited number of types,
|
||||
// and unmarshaling unknown/unexpected response packets is unnecessary.
|
||||
//
|
||||
// For servers expecting to receive arbitrary request packet types,
|
||||
// use RequestPacket.
|
||||
//
|
||||
// Defined in https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-02.txt#section-3
|
||||
type RawPacket struct {
|
||||
PacketType PacketType
|
||||
RequestID uint32
|
||||
|
||||
Data Buffer
|
||||
}
|
||||
|
||||
// Type returns the Type field defining the SSH_FXP_xy type for this packet.
|
||||
func (p *RawPacket) Type() PacketType {
|
||||
return p.PacketType
|
||||
}
|
||||
|
||||
// Reset clears the pointers and reference-semantic variables of RawPacket,
|
||||
// releasing underlying resources, and making them and the RawPacket suitable to be reused,
|
||||
// so long as no other references have been kept.
|
||||
func (p *RawPacket) Reset() {
|
||||
p.Data = Buffer{}
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
//
|
||||
// The internal p.RequestID is overridden by the reqid argument.
|
||||
func (p *RawPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
buf = NewMarshalBuffer(0)
|
||||
}
|
||||
|
||||
buf.StartPacket(p.PacketType, reqid)
|
||||
|
||||
return buf.Packet(p.Data.Bytes())
|
||||
}
|
||||
|
||||
// MarshalBinary returns p as the binary encoding of p.
|
||||
//
|
||||
// This is a convenience implementation primarily intended for tests,
|
||||
// because it is inefficient with allocations.
|
||||
func (p *RawPacket) MarshalBinary() ([]byte, error) {
|
||||
return ComposePacket(p.MarshalPacket(p.RequestID, nil))
|
||||
}
|
||||
|
||||
// UnmarshalFrom decodes a RawPacket from the given Buffer into p.
|
||||
//
|
||||
// The Data field will alias the passed in Buffer,
|
||||
// so the buffer passed in should not be reused before RawPacket.Reset().
|
||||
func (p *RawPacket) UnmarshalFrom(buf *Buffer) error {
|
||||
*p = RawPacket{
|
||||
PacketType: PacketType(buf.ConsumeUint8()),
|
||||
RequestID: buf.ConsumeUint32(),
|
||||
}
|
||||
|
||||
p.Data = *buf
|
||||
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
// UnmarshalBinary decodes a full raw packet out of the given data.
|
||||
// It is assumed that the uint32(length) has already been consumed to receive the data.
|
||||
//
|
||||
// This is a convenience implementation primarily intended for tests,
|
||||
// because this must clone the given data byte slice,
|
||||
// as Data is not allowed to alias any part of the data byte slice.
|
||||
func (p *RawPacket) UnmarshalBinary(data []byte) error {
|
||||
clone := make([]byte, len(data))
|
||||
n := copy(clone, data)
|
||||
return p.UnmarshalFrom(NewBuffer(clone[:n]))
|
||||
}
|
||||
|
||||
// readPacket reads a uint32 length-prefixed binary data packet from r.
|
||||
// using the given byte slice as a backing array.
|
||||
//
|
||||
// If the packet length read from r is bigger than maxPacketLength,
|
||||
// or greater than math.MaxInt32 on a 32-bit implementation,
|
||||
// then a `ErrLongPacket` error will be returned.
|
||||
//
|
||||
// If the given byte slice is insufficient to hold the packet,
|
||||
// then it will be extended to fill the packet size.
|
||||
func readPacket(r io.Reader, b []byte, maxPacketLength uint32) ([]byte, error) {
|
||||
if cap(b) < 4 {
|
||||
// We will need allocate our own buffer just for reading the packet length.
|
||||
|
||||
// However, we don’t really want to allocate an extremely narrow buffer (4-bytes),
|
||||
// and cause unnecessary allocation churn from both length reads and small packet reads,
|
||||
// so we use smallBufferSize from the bytes package as a reasonable guess.
|
||||
|
||||
// But if callers really do want to force narrow throw-away allocation of every packet body,
|
||||
// they can do so with a buffer of capacity 4.
|
||||
b = make([]byte, smallBufferSize)
|
||||
}
|
||||
|
||||
if _, err := io.ReadFull(r, b[:4]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
length := unmarshalUint32(b)
|
||||
if int(length) < 5 {
|
||||
// Must have at least uint8(type) and uint32(request-id)
|
||||
|
||||
if int(length) < 0 {
|
||||
// Only possible when strconv.IntSize == 32,
|
||||
// the packet length is longer than math.MaxInt32,
|
||||
// and thus longer than any possible slice.
|
||||
return nil, ErrLongPacket
|
||||
}
|
||||
|
||||
return nil, ErrShortPacket
|
||||
}
|
||||
if length > maxPacketLength {
|
||||
return nil, ErrLongPacket
|
||||
}
|
||||
|
||||
if int(length) > cap(b) {
|
||||
// We know int(length) must be positive, because of tests above.
|
||||
b = make([]byte, length)
|
||||
}
|
||||
|
||||
n, err := io.ReadFull(r, b[:length])
|
||||
return b[:n], err
|
||||
}
|
||||
|
||||
// ReadFrom provides a simple functional packet reader,
|
||||
// using the given byte slice as a backing array.
|
||||
//
|
||||
// To protect against potential denial of service attacks,
|
||||
// if the read packet length is longer than maxPacketLength,
|
||||
// then no packet data will be read, and ErrLongPacket will be returned.
|
||||
// (On 32-bit int architectures, all packets >= 2^31 in length
|
||||
// will return ErrLongPacket regardless of maxPacketLength.)
|
||||
//
|
||||
// If the read packet length is longer than cap(b),
|
||||
// then a throw-away slice will allocated to meet the exact packet length.
|
||||
// This can be used to limit the length of reused buffers,
|
||||
// while still allowing reception of occasional large packets.
|
||||
//
|
||||
// The Data field may alias the passed in byte slice,
|
||||
// so the byte slice passed in should not be reused before RawPacket.Reset().
|
||||
func (p *RawPacket) ReadFrom(r io.Reader, b []byte, maxPacketLength uint32) error {
|
||||
b, err := readPacket(r, b, maxPacketLength)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return p.UnmarshalFrom(NewBuffer(b))
|
||||
}
|
||||
|
||||
// RequestPacket implements the general packet format from draft-ietf-secsh-filexfer-02
|
||||
// but also automatically decode/encodes valid request packets (2 < type < 100 || type == 200).
|
||||
//
|
||||
// RequestPacket is intended for use in servers receiving requests,
|
||||
// where any arbitrary request may be received, and so decoding them automatically
|
||||
// is useful.
|
||||
//
|
||||
// For clients expecting to receive specific response packet types,
|
||||
// where automatic unmarshaling of the packet body does not make sense,
|
||||
// use RawPacket.
|
||||
//
|
||||
// Defined in https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-02.txt#section-3
|
||||
type RequestPacket struct {
|
||||
RequestID uint32
|
||||
|
||||
Request Packet
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with the underlying packet.
|
||||
func (p *RequestPacket) Type() PacketType {
|
||||
return p.Request.Type()
|
||||
}
|
||||
|
||||
// Reset clears the pointers and reference-semantic variables in RequestPacket,
|
||||
// releasing underlying resources, and making them and the RequestPacket suitable to be reused,
|
||||
// so long as no other references have been kept.
|
||||
func (p *RequestPacket) Reset() {
|
||||
p.Request = nil
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
//
|
||||
// The internal p.RequestID is overridden by the reqid argument.
|
||||
func (p *RequestPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
if p.Request == nil {
|
||||
return nil, nil, errors.New("empty request packet")
|
||||
}
|
||||
|
||||
return p.Request.MarshalPacket(reqid, b)
|
||||
}
|
||||
|
||||
// MarshalBinary returns p as the binary encoding of p.
|
||||
//
|
||||
// This is a convenience implementation primarily intended for tests,
|
||||
// because it is inefficient with allocations.
|
||||
func (p *RequestPacket) MarshalBinary() ([]byte, error) {
|
||||
return ComposePacket(p.MarshalPacket(p.RequestID, nil))
|
||||
}
|
||||
|
||||
// UnmarshalFrom decodes a RequestPacket from the given Buffer into p.
|
||||
//
|
||||
// The Request field may alias the passed in Buffer, (e.g. SSH_FXP_WRITE),
|
||||
// so the buffer passed in should not be reused before RequestPacket.Reset().
|
||||
func (p *RequestPacket) UnmarshalFrom(buf *Buffer) error {
|
||||
typ := PacketType(buf.ConsumeUint8())
|
||||
if buf.Err != nil {
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
req, err := newPacketFromType(typ)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*p = RequestPacket{
|
||||
RequestID: buf.ConsumeUint32(),
|
||||
Request: req,
|
||||
}
|
||||
|
||||
return p.Request.UnmarshalPacketBody(buf)
|
||||
}
|
||||
|
||||
// UnmarshalBinary decodes a full request packet out of the given data.
|
||||
// It is assumed that the uint32(length) has already been consumed to receive the data.
|
||||
//
|
||||
// This is a convenience implementation primarily intended for tests,
|
||||
// because this must clone the given data byte slice,
|
||||
// as Request is not allowed to alias any part of the data byte slice.
|
||||
func (p *RequestPacket) UnmarshalBinary(data []byte) error {
|
||||
clone := make([]byte, len(data))
|
||||
n := copy(clone, data)
|
||||
return p.UnmarshalFrom(NewBuffer(clone[:n]))
|
||||
}
|
||||
|
||||
// ReadFrom provides a simple functional packet reader,
|
||||
// using the given byte slice as a backing array.
|
||||
//
|
||||
// To protect against potential denial of service attacks,
|
||||
// if the read packet length is longer than maxPacketLength,
|
||||
// then no packet data will be read, and ErrLongPacket will be returned.
|
||||
// (On 32-bit int architectures, all packets >= 2^31 in length
|
||||
// will return ErrLongPacket regardless of maxPacketLength.)
|
||||
//
|
||||
// If the read packet length is longer than cap(b),
|
||||
// then a throw-away slice will allocated to meet the exact packet length.
|
||||
// This can be used to limit the length of reused buffers,
|
||||
// while still allowing reception of occasional large packets.
|
||||
//
|
||||
// The Request field may alias the passed in byte slice,
|
||||
// so the byte slice passed in should not be reused before RawPacket.Reset().
|
||||
func (p *RequestPacket) ReadFrom(r io.Reader, b []byte, maxPacketLength uint32) error {
|
||||
b, err := readPacket(r, b, maxPacketLength)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return p.UnmarshalFrom(NewBuffer(b))
|
||||
}
|
||||
362
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/path_packets.go
generated
vendored
Normal file
362
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/path_packets.go
generated
vendored
Normal file
|
|
@ -0,0 +1,362 @@
|
|||
package sshfx
|
||||
|
||||
// LStatPacket defines the SSH_FXP_LSTAT packet.
|
||||
type LStatPacket struct {
|
||||
Path string
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *LStatPacket) Type() PacketType {
|
||||
return PacketTypeLStat
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *LStatPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
size := 4 + len(p.Path) // string(path)
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeLStat, reqid)
|
||||
buf.AppendString(p.Path)
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *LStatPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
*p = LStatPacket{
|
||||
Path: buf.ConsumeString(),
|
||||
}
|
||||
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
// SetstatPacket defines the SSH_FXP_SETSTAT packet.
|
||||
type SetstatPacket struct {
|
||||
Path string
|
||||
Attrs Attributes
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *SetstatPacket) Type() PacketType {
|
||||
return PacketTypeSetstat
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *SetstatPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
size := 4 + len(p.Path) + p.Attrs.Len() // string(path) + ATTRS(attrs)
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeSetstat, reqid)
|
||||
buf.AppendString(p.Path)
|
||||
|
||||
p.Attrs.MarshalInto(buf)
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *SetstatPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
*p = SetstatPacket{
|
||||
Path: buf.ConsumeString(),
|
||||
}
|
||||
|
||||
return p.Attrs.UnmarshalFrom(buf)
|
||||
}
|
||||
|
||||
// RemovePacket defines the SSH_FXP_REMOVE packet.
|
||||
type RemovePacket struct {
|
||||
Path string
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *RemovePacket) Type() PacketType {
|
||||
return PacketTypeRemove
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *RemovePacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
size := 4 + len(p.Path) // string(path)
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeRemove, reqid)
|
||||
buf.AppendString(p.Path)
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *RemovePacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
*p = RemovePacket{
|
||||
Path: buf.ConsumeString(),
|
||||
}
|
||||
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
// MkdirPacket defines the SSH_FXP_MKDIR packet.
|
||||
type MkdirPacket struct {
|
||||
Path string
|
||||
Attrs Attributes
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *MkdirPacket) Type() PacketType {
|
||||
return PacketTypeMkdir
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *MkdirPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
size := 4 + len(p.Path) + p.Attrs.Len() // string(path) + ATTRS(attrs)
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeMkdir, reqid)
|
||||
buf.AppendString(p.Path)
|
||||
|
||||
p.Attrs.MarshalInto(buf)
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *MkdirPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
*p = MkdirPacket{
|
||||
Path: buf.ConsumeString(),
|
||||
}
|
||||
|
||||
return p.Attrs.UnmarshalFrom(buf)
|
||||
}
|
||||
|
||||
// RmdirPacket defines the SSH_FXP_RMDIR packet.
|
||||
type RmdirPacket struct {
|
||||
Path string
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *RmdirPacket) Type() PacketType {
|
||||
return PacketTypeRmdir
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *RmdirPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
size := 4 + len(p.Path) // string(path)
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeRmdir, reqid)
|
||||
buf.AppendString(p.Path)
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *RmdirPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
*p = RmdirPacket{
|
||||
Path: buf.ConsumeString(),
|
||||
}
|
||||
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
// RealPathPacket defines the SSH_FXP_REALPATH packet.
|
||||
type RealPathPacket struct {
|
||||
Path string
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *RealPathPacket) Type() PacketType {
|
||||
return PacketTypeRealPath
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *RealPathPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
size := 4 + len(p.Path) // string(path)
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeRealPath, reqid)
|
||||
buf.AppendString(p.Path)
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *RealPathPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
*p = RealPathPacket{
|
||||
Path: buf.ConsumeString(),
|
||||
}
|
||||
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
// StatPacket defines the SSH_FXP_STAT packet.
|
||||
type StatPacket struct {
|
||||
Path string
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *StatPacket) Type() PacketType {
|
||||
return PacketTypeStat
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *StatPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
size := 4 + len(p.Path) // string(path)
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeStat, reqid)
|
||||
buf.AppendString(p.Path)
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *StatPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
*p = StatPacket{
|
||||
Path: buf.ConsumeString(),
|
||||
}
|
||||
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
// RenamePacket defines the SSH_FXP_RENAME packet.
|
||||
type RenamePacket struct {
|
||||
OldPath string
|
||||
NewPath string
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *RenamePacket) Type() PacketType {
|
||||
return PacketTypeRename
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *RenamePacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
// string(oldpath) + string(newpath)
|
||||
size := 4 + len(p.OldPath) + 4 + len(p.NewPath)
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeRename, reqid)
|
||||
buf.AppendString(p.OldPath)
|
||||
buf.AppendString(p.NewPath)
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *RenamePacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
*p = RenamePacket{
|
||||
OldPath: buf.ConsumeString(),
|
||||
NewPath: buf.ConsumeString(),
|
||||
}
|
||||
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
// ReadLinkPacket defines the SSH_FXP_READLINK packet.
|
||||
type ReadLinkPacket struct {
|
||||
Path string
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *ReadLinkPacket) Type() PacketType {
|
||||
return PacketTypeReadLink
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *ReadLinkPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
size := 4 + len(p.Path) // string(path)
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeReadLink, reqid)
|
||||
buf.AppendString(p.Path)
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *ReadLinkPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
*p = ReadLinkPacket{
|
||||
Path: buf.ConsumeString(),
|
||||
}
|
||||
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
// SymlinkPacket defines the SSH_FXP_SYMLINK packet.
|
||||
//
|
||||
// The order of the arguments to the SSH_FXP_SYMLINK method was inadvertently reversed.
|
||||
// Unfortunately, the reversal was not noticed until the server was widely deployed.
|
||||
// Covered in Section 4.1 of https://github.com/openssh/openssh-portable/blob/master/PROTOCOL
|
||||
type SymlinkPacket struct {
|
||||
LinkPath string
|
||||
TargetPath string
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *SymlinkPacket) Type() PacketType {
|
||||
return PacketTypeSymlink
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *SymlinkPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
// string(targetpath) + string(linkpath)
|
||||
size := 4 + len(p.TargetPath) + 4 + len(p.LinkPath)
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeSymlink, reqid)
|
||||
|
||||
// Arguments were inadvertently reversed.
|
||||
buf.AppendString(p.TargetPath)
|
||||
buf.AppendString(p.LinkPath)
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *SymlinkPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
*p = SymlinkPacket{
|
||||
// Arguments were inadvertently reversed.
|
||||
TargetPath: buf.ConsumeString(),
|
||||
LinkPath: buf.ConsumeString(),
|
||||
}
|
||||
|
||||
return buf.Err
|
||||
}
|
||||
114
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/permissions.go
generated
vendored
Normal file
114
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/permissions.go
generated
vendored
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
package sshfx
|
||||
|
||||
// FileMode represents a file’s mode and permission bits.
|
||||
// The bits are defined according to POSIX standards,
|
||||
// and may not apply to the OS being built for.
|
||||
type FileMode uint32
|
||||
|
||||
// Permission flags, defined here to avoid potential inconsistencies in individual OS implementations.
|
||||
const (
|
||||
ModePerm FileMode = 0o0777 // S_IRWXU | S_IRWXG | S_IRWXO
|
||||
ModeUserRead FileMode = 0o0400 // S_IRUSR
|
||||
ModeUserWrite FileMode = 0o0200 // S_IWUSR
|
||||
ModeUserExec FileMode = 0o0100 // S_IXUSR
|
||||
ModeGroupRead FileMode = 0o0040 // S_IRGRP
|
||||
ModeGroupWrite FileMode = 0o0020 // S_IWGRP
|
||||
ModeGroupExec FileMode = 0o0010 // S_IXGRP
|
||||
ModeOtherRead FileMode = 0o0004 // S_IROTH
|
||||
ModeOtherWrite FileMode = 0o0002 // S_IWOTH
|
||||
ModeOtherExec FileMode = 0o0001 // S_IXOTH
|
||||
|
||||
ModeSetUID FileMode = 0o4000 // S_ISUID
|
||||
ModeSetGID FileMode = 0o2000 // S_ISGID
|
||||
ModeSticky FileMode = 0o1000 // S_ISVTX
|
||||
|
||||
ModeType FileMode = 0xF000 // S_IFMT
|
||||
ModeNamedPipe FileMode = 0x1000 // S_IFIFO
|
||||
ModeCharDevice FileMode = 0x2000 // S_IFCHR
|
||||
ModeDir FileMode = 0x4000 // S_IFDIR
|
||||
ModeDevice FileMode = 0x6000 // S_IFBLK
|
||||
ModeRegular FileMode = 0x8000 // S_IFREG
|
||||
ModeSymlink FileMode = 0xA000 // S_IFLNK
|
||||
ModeSocket FileMode = 0xC000 // S_IFSOCK
|
||||
)
|
||||
|
||||
// IsDir reports whether m describes a directory.
|
||||
// That is, it tests for m.Type() == ModeDir.
|
||||
func (m FileMode) IsDir() bool {
|
||||
return (m & ModeType) == ModeDir
|
||||
}
|
||||
|
||||
// IsRegular reports whether m describes a regular file.
|
||||
// That is, it tests for m.Type() == ModeRegular
|
||||
func (m FileMode) IsRegular() bool {
|
||||
return (m & ModeType) == ModeRegular
|
||||
}
|
||||
|
||||
// Perm returns the POSIX permission bits in m (m & ModePerm).
|
||||
func (m FileMode) Perm() FileMode {
|
||||
return (m & ModePerm)
|
||||
}
|
||||
|
||||
// Type returns the type bits in m (m & ModeType).
|
||||
func (m FileMode) Type() FileMode {
|
||||
return (m & ModeType)
|
||||
}
|
||||
|
||||
// String returns a `-rwxrwxrwx` style string representing the `ls -l` POSIX permissions string.
|
||||
func (m FileMode) String() string {
|
||||
var buf [10]byte
|
||||
|
||||
switch m.Type() {
|
||||
case ModeRegular:
|
||||
buf[0] = '-'
|
||||
case ModeDir:
|
||||
buf[0] = 'd'
|
||||
case ModeSymlink:
|
||||
buf[0] = 'l'
|
||||
case ModeDevice:
|
||||
buf[0] = 'b'
|
||||
case ModeCharDevice:
|
||||
buf[0] = 'c'
|
||||
case ModeNamedPipe:
|
||||
buf[0] = 'p'
|
||||
case ModeSocket:
|
||||
buf[0] = 's'
|
||||
default:
|
||||
buf[0] = '?'
|
||||
}
|
||||
|
||||
const rwx = "rwxrwxrwx"
|
||||
for i, c := range rwx {
|
||||
if m&(1<<uint(9-1-i)) != 0 {
|
||||
buf[i+1] = byte(c)
|
||||
} else {
|
||||
buf[i+1] = '-'
|
||||
}
|
||||
}
|
||||
|
||||
if m&ModeSetUID != 0 {
|
||||
if buf[3] == 'x' {
|
||||
buf[3] = 's'
|
||||
} else {
|
||||
buf[3] = 'S'
|
||||
}
|
||||
}
|
||||
|
||||
if m&ModeSetGID != 0 {
|
||||
if buf[6] == 'x' {
|
||||
buf[6] = 's'
|
||||
} else {
|
||||
buf[6] = 'S'
|
||||
}
|
||||
}
|
||||
|
||||
if m&ModeSticky != 0 {
|
||||
if buf[9] == 'x' {
|
||||
buf[9] = 't'
|
||||
} else {
|
||||
buf[9] = 'T'
|
||||
}
|
||||
}
|
||||
|
||||
return string(buf[:])
|
||||
}
|
||||
230
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/response_packets.go
generated
vendored
Normal file
230
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/response_packets.go
generated
vendored
Normal file
|
|
@ -0,0 +1,230 @@
|
|||
package sshfx
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// StatusPacket defines the SSH_FXP_STATUS packet.
|
||||
//
|
||||
// Specified in https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-02.txt#section-7
|
||||
type StatusPacket struct {
|
||||
StatusCode Status
|
||||
ErrorMessage string
|
||||
LanguageTag string
|
||||
}
|
||||
|
||||
// Error makes StatusPacket an error type.
|
||||
func (p *StatusPacket) Error() string {
|
||||
if p.ErrorMessage == "" {
|
||||
return "sftp: " + p.StatusCode.String()
|
||||
}
|
||||
|
||||
return fmt.Sprintf("sftp: %s: %q", p.StatusCode, p.ErrorMessage)
|
||||
}
|
||||
|
||||
// Is returns true if target is a StatusPacket with the same StatusCode,
|
||||
// or target is a Status code which is the same as SatusCode.
|
||||
func (p *StatusPacket) Is(target error) bool {
|
||||
if target, ok := target.(*StatusPacket); ok {
|
||||
return p.StatusCode == target.StatusCode
|
||||
}
|
||||
|
||||
return p.StatusCode == target
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *StatusPacket) Type() PacketType {
|
||||
return PacketTypeStatus
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *StatusPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
// uint32(error/status code) + string(error message) + string(language tag)
|
||||
size := 4 + 4 + len(p.ErrorMessage) + 4 + len(p.LanguageTag)
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeStatus, reqid)
|
||||
buf.AppendUint32(uint32(p.StatusCode))
|
||||
buf.AppendString(p.ErrorMessage)
|
||||
buf.AppendString(p.LanguageTag)
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *StatusPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
*p = StatusPacket{
|
||||
StatusCode: Status(buf.ConsumeUint32()),
|
||||
ErrorMessage: buf.ConsumeString(),
|
||||
LanguageTag: buf.ConsumeString(),
|
||||
}
|
||||
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
// HandlePacket defines the SSH_FXP_HANDLE packet.
|
||||
type HandlePacket struct {
|
||||
Handle string
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *HandlePacket) Type() PacketType {
|
||||
return PacketTypeHandle
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *HandlePacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
size := 4 + len(p.Handle) // string(handle)
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeHandle, reqid)
|
||||
buf.AppendString(p.Handle)
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *HandlePacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
*p = HandlePacket{
|
||||
Handle: buf.ConsumeString(),
|
||||
}
|
||||
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
// DataPacket defines the SSH_FXP_DATA packet.
|
||||
type DataPacket struct {
|
||||
Data []byte
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *DataPacket) Type() PacketType {
|
||||
return PacketTypeData
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *DataPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
size := 4 // uint32(len(data)); data content in payload
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeData, reqid)
|
||||
buf.AppendUint32(uint32(len(p.Data)))
|
||||
|
||||
return buf.Packet(p.Data)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
//
|
||||
// If p.Data is already populated, and of sufficient length to hold the data,
|
||||
// then this will copy the data into that byte slice.
|
||||
//
|
||||
// If p.Data has a length insufficient to hold the data,
|
||||
// then this will make a new slice of sufficient length, and copy the data into that.
|
||||
//
|
||||
// This means this _does not_ alias any of the data buffer that is passed in.
|
||||
func (p *DataPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
*p = DataPacket{
|
||||
Data: buf.ConsumeByteSliceCopy(p.Data),
|
||||
}
|
||||
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
// NamePacket defines the SSH_FXP_NAME packet.
|
||||
type NamePacket struct {
|
||||
Entries []*NameEntry
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *NamePacket) Type() PacketType {
|
||||
return PacketTypeName
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *NamePacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
size := 4 // uint32(len(entries))
|
||||
|
||||
for _, e := range p.Entries {
|
||||
size += e.Len()
|
||||
}
|
||||
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeName, reqid)
|
||||
buf.AppendUint32(uint32(len(p.Entries)))
|
||||
|
||||
for _, e := range p.Entries {
|
||||
e.MarshalInto(buf)
|
||||
}
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *NamePacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
count := buf.ConsumeCount()
|
||||
if buf.Err != nil {
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
*p = NamePacket{
|
||||
Entries: make([]*NameEntry, 0, count),
|
||||
}
|
||||
|
||||
for i := 0; i < count; i++ {
|
||||
var e NameEntry
|
||||
if err := e.UnmarshalFrom(buf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.Entries = append(p.Entries, &e)
|
||||
}
|
||||
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
// AttrsPacket defines the SSH_FXP_ATTRS packet.
|
||||
type AttrsPacket struct {
|
||||
Attrs Attributes
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *AttrsPacket) Type() PacketType {
|
||||
return PacketTypeAttrs
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *AttrsPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
size := p.Attrs.Len() // ATTRS(attrs)
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeAttrs, reqid)
|
||||
p.Attrs.MarshalInto(buf)
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *AttrsPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
return p.Attrs.UnmarshalFrom(buf)
|
||||
}
|
||||
88
vendor/github.com/pkg/sftp/ls_formatting.go
generated
vendored
Normal file
88
vendor/github.com/pkg/sftp/ls_formatting.go
generated
vendored
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
package sftp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/user"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
sshfx "github.com/pkg/sftp/internal/encoding/ssh/filexfer"
|
||||
)
|
||||
|
||||
func lsFormatID(id uint32) string {
|
||||
return strconv.FormatUint(uint64(id), 10)
|
||||
}
|
||||
|
||||
type osIDLookup struct{}
|
||||
|
||||
func (osIDLookup) Filelist(*Request) (ListerAt, error) {
|
||||
return nil, errors.New("unimplemented stub")
|
||||
}
|
||||
|
||||
func (osIDLookup) LookupUserName(uid string) string {
|
||||
u, err := user.LookupId(uid)
|
||||
if err != nil {
|
||||
return uid
|
||||
}
|
||||
|
||||
return u.Username
|
||||
}
|
||||
|
||||
func (osIDLookup) LookupGroupName(gid string) string {
|
||||
g, err := user.LookupGroupId(gid)
|
||||
if err != nil {
|
||||
return gid
|
||||
}
|
||||
|
||||
return g.Name
|
||||
}
|
||||
|
||||
// runLs formats the FileInfo as per `ls -l` style, which is in the 'longname' field of a SSH_FXP_NAME entry.
|
||||
// This is a fairly simple implementation, just enough to look close to openssh in simple cases.
|
||||
func runLs(idLookup NameLookupFileLister, dirent os.FileInfo) string {
|
||||
// example from openssh sftp server:
|
||||
// crw-rw-rw- 1 root wheel 0 Jul 31 20:52 ttyvd
|
||||
// format:
|
||||
// {directory / char device / etc}{rwxrwxrwx} {number of links} owner group size month day [time (this year) | year (otherwise)] name
|
||||
|
||||
symPerms := sshfx.FileMode(fromFileMode(dirent.Mode())).String()
|
||||
|
||||
var numLinks uint64 = 1
|
||||
uid, gid := "0", "0"
|
||||
|
||||
switch sys := dirent.Sys().(type) {
|
||||
case *sshfx.Attributes:
|
||||
uid = lsFormatID(sys.UID)
|
||||
gid = lsFormatID(sys.GID)
|
||||
case *FileStat:
|
||||
uid = lsFormatID(sys.UID)
|
||||
gid = lsFormatID(sys.GID)
|
||||
default:
|
||||
if fiExt, ok := dirent.(FileInfoUidGid); ok {
|
||||
uid = lsFormatID(fiExt.Uid())
|
||||
gid = lsFormatID(fiExt.Gid())
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
numLinks, uid, gid = lsLinksUIDGID(dirent)
|
||||
}
|
||||
|
||||
if idLookup != nil {
|
||||
uid, gid = idLookup.LookupUserName(uid), idLookup.LookupGroupName(gid)
|
||||
}
|
||||
|
||||
mtime := dirent.ModTime()
|
||||
date := mtime.Format("Jan 2")
|
||||
|
||||
var yearOrTime string
|
||||
if mtime.Before(time.Now().AddDate(0, -6, 0)) {
|
||||
yearOrTime = mtime.Format("2006")
|
||||
} else {
|
||||
yearOrTime = mtime.Format("15:04")
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s %4d %-8s %-8s %8d %s %5s %s", symPerms, numLinks, uid, gid, dirent.Size(), date, yearOrTime, dirent.Name())
|
||||
}
|
||||
22
vendor/github.com/pkg/sftp/ls_plan9.go
generated
vendored
Normal file
22
vendor/github.com/pkg/sftp/ls_plan9.go
generated
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
//go:build plan9
|
||||
// +build plan9
|
||||
|
||||
package sftp
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func lsLinksUIDGID(fi os.FileInfo) (numLinks uint64, uid, gid string) {
|
||||
numLinks = 1
|
||||
uid, gid = "0", "0"
|
||||
|
||||
switch sys := fi.Sys().(type) {
|
||||
case *syscall.Dir:
|
||||
uid = sys.Uid
|
||||
gid = sys.Gid
|
||||
}
|
||||
|
||||
return numLinks, uid, gid
|
||||
}
|
||||
12
vendor/github.com/pkg/sftp/ls_stub.go
generated
vendored
Normal file
12
vendor/github.com/pkg/sftp/ls_stub.go
generated
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
//go:build windows || android
|
||||
// +build windows android
|
||||
|
||||
package sftp
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
func lsLinksUIDGID(fi os.FileInfo) (numLinks uint64, uid, gid string) {
|
||||
return 1, "0", "0"
|
||||
}
|
||||
24
vendor/github.com/pkg/sftp/ls_unix.go
generated
vendored
Normal file
24
vendor/github.com/pkg/sftp/ls_unix.go
generated
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
//go:build aix || darwin || dragonfly || freebsd || (!android && linux) || netbsd || openbsd || solaris || js || zos
|
||||
// +build aix darwin dragonfly freebsd !android,linux netbsd openbsd solaris js zos
|
||||
|
||||
package sftp
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func lsLinksUIDGID(fi os.FileInfo) (numLinks uint64, uid, gid string) {
|
||||
numLinks = 1
|
||||
uid, gid = "0", "0"
|
||||
|
||||
switch sys := fi.Sys().(type) {
|
||||
case *syscall.Stat_t:
|
||||
numLinks = uint64(sys.Nlink)
|
||||
uid = lsFormatID(sys.Uid)
|
||||
gid = lsFormatID(sys.Gid)
|
||||
default:
|
||||
}
|
||||
|
||||
return numLinks, uid, gid
|
||||
}
|
||||
137
vendor/github.com/pkg/sftp/match.go
generated
vendored
Normal file
137
vendor/github.com/pkg/sftp/match.go
generated
vendored
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
package sftp
|
||||
|
||||
import (
|
||||
"path"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ErrBadPattern indicates a globbing pattern was malformed.
|
||||
var ErrBadPattern = path.ErrBadPattern
|
||||
|
||||
// Match reports whether name matches the shell pattern.
|
||||
//
|
||||
// This is an alias for path.Match from the standard library,
|
||||
// offered so that callers need not import the path package.
|
||||
// For details, see https://golang.org/pkg/path/#Match.
|
||||
func Match(pattern, name string) (matched bool, err error) {
|
||||
return path.Match(pattern, name)
|
||||
}
|
||||
|
||||
// detect if byte(char) is path separator
|
||||
func isPathSeparator(c byte) bool {
|
||||
return c == '/'
|
||||
}
|
||||
|
||||
// Split splits the path p immediately following the final slash,
|
||||
// separating it into a directory and file name component.
|
||||
//
|
||||
// This is an alias for path.Split from the standard library,
|
||||
// offered so that callers need not import the path package.
|
||||
// For details, see https://golang.org/pkg/path/#Split.
|
||||
func Split(p string) (dir, file string) {
|
||||
return path.Split(p)
|
||||
}
|
||||
|
||||
// Glob returns the names of all files matching pattern or nil
|
||||
// if there is no matching file. The syntax of patterns is the same
|
||||
// as in Match. The pattern may describe hierarchical names such as
|
||||
// /usr/*/bin/ed.
|
||||
//
|
||||
// Glob ignores file system errors such as I/O errors reading directories.
|
||||
// The only possible returned error is ErrBadPattern, when pattern
|
||||
// is malformed.
|
||||
func (c *Client) Glob(pattern string) (matches []string, err error) {
|
||||
if !hasMeta(pattern) {
|
||||
file, err := c.Lstat(pattern)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
dir, _ := Split(pattern)
|
||||
dir = cleanGlobPath(dir)
|
||||
return []string{Join(dir, file.Name())}, nil
|
||||
}
|
||||
|
||||
dir, file := Split(pattern)
|
||||
dir = cleanGlobPath(dir)
|
||||
|
||||
if !hasMeta(dir) {
|
||||
return c.glob(dir, file, nil)
|
||||
}
|
||||
|
||||
// Prevent infinite recursion. See issue 15879.
|
||||
if dir == pattern {
|
||||
return nil, ErrBadPattern
|
||||
}
|
||||
|
||||
var m []string
|
||||
m, err = c.Glob(dir)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for _, d := range m {
|
||||
matches, err = c.glob(d, file, matches)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// cleanGlobPath prepares path for glob matching.
|
||||
func cleanGlobPath(path string) string {
|
||||
switch path {
|
||||
case "":
|
||||
return "."
|
||||
case "/":
|
||||
return path
|
||||
default:
|
||||
return path[0 : len(path)-1] // chop off trailing separator
|
||||
}
|
||||
}
|
||||
|
||||
// glob searches for files matching pattern in the directory dir
|
||||
// and appends them to matches. If the directory cannot be
|
||||
// opened, it returns the existing matches. New matches are
|
||||
// added in lexicographical order.
|
||||
func (c *Client) glob(dir, pattern string, matches []string) (m []string, e error) {
|
||||
m = matches
|
||||
fi, err := c.Stat(dir)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if !fi.IsDir() {
|
||||
return
|
||||
}
|
||||
names, err := c.ReadDir(dir)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
//sort.Strings(names)
|
||||
|
||||
for _, n := range names {
|
||||
matched, err := Match(pattern, n.Name())
|
||||
if err != nil {
|
||||
return m, err
|
||||
}
|
||||
if matched {
|
||||
m = append(m, Join(dir, n.Name()))
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Join joins any number of path elements into a single path, separating
|
||||
// them with slashes.
|
||||
//
|
||||
// This is an alias for path.Join from the standard library,
|
||||
// offered so that callers need not import the path package.
|
||||
// For details, see https://golang.org/pkg/path/#Join.
|
||||
func Join(elem ...string) string {
|
||||
return path.Join(elem...)
|
||||
}
|
||||
|
||||
// hasMeta reports whether path contains any of the magic characters
|
||||
// recognized by Match.
|
||||
func hasMeta(path string) bool {
|
||||
return strings.ContainsAny(path, "\\*?[")
|
||||
}
|
||||
216
vendor/github.com/pkg/sftp/packet-manager.go
generated
vendored
Normal file
216
vendor/github.com/pkg/sftp/packet-manager.go
generated
vendored
Normal file
|
|
@ -0,0 +1,216 @@
|
|||
package sftp
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"sort"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// The goal of the packetManager is to keep the outgoing packets in the same
|
||||
// order as the incoming as is requires by section 7 of the RFC.
|
||||
|
||||
type packetManager struct {
|
||||
requests chan orderedPacket
|
||||
responses chan orderedPacket
|
||||
fini chan struct{}
|
||||
incoming orderedPackets
|
||||
outgoing orderedPackets
|
||||
sender packetSender // connection object
|
||||
working *sync.WaitGroup
|
||||
packetCount uint32
|
||||
// it is not nil if the allocator is enabled
|
||||
alloc *allocator
|
||||
}
|
||||
|
||||
type packetSender interface {
|
||||
sendPacket(encoding.BinaryMarshaler) error
|
||||
}
|
||||
|
||||
func newPktMgr(sender packetSender) *packetManager {
|
||||
s := &packetManager{
|
||||
requests: make(chan orderedPacket, SftpServerWorkerCount),
|
||||
responses: make(chan orderedPacket, SftpServerWorkerCount),
|
||||
fini: make(chan struct{}),
|
||||
incoming: make([]orderedPacket, 0, SftpServerWorkerCount),
|
||||
outgoing: make([]orderedPacket, 0, SftpServerWorkerCount),
|
||||
sender: sender,
|
||||
working: &sync.WaitGroup{},
|
||||
}
|
||||
go s.controller()
|
||||
return s
|
||||
}
|
||||
|
||||
// // packet ordering
|
||||
func (s *packetManager) newOrderID() uint32 {
|
||||
s.packetCount++
|
||||
return s.packetCount
|
||||
}
|
||||
|
||||
// returns the next orderID without incrementing it.
|
||||
// This is used before receiving a new packet, with the allocator enabled, to associate
|
||||
// the slice allocated for the received packet with the orderID that will be used to mark
|
||||
// the allocated slices for reuse once the request is served
|
||||
func (s *packetManager) getNextOrderID() uint32 {
|
||||
return s.packetCount + 1
|
||||
}
|
||||
|
||||
type orderedRequest struct {
|
||||
requestPacket
|
||||
orderid uint32
|
||||
}
|
||||
|
||||
func (s *packetManager) newOrderedRequest(p requestPacket) orderedRequest {
|
||||
return orderedRequest{requestPacket: p, orderid: s.newOrderID()}
|
||||
}
|
||||
func (p orderedRequest) orderID() uint32 { return p.orderid }
|
||||
func (p orderedRequest) setOrderID(oid uint32) { p.orderid = oid }
|
||||
|
||||
type orderedResponse struct {
|
||||
responsePacket
|
||||
orderid uint32
|
||||
}
|
||||
|
||||
func (s *packetManager) newOrderedResponse(p responsePacket, id uint32,
|
||||
) orderedResponse {
|
||||
return orderedResponse{responsePacket: p, orderid: id}
|
||||
}
|
||||
func (p orderedResponse) orderID() uint32 { return p.orderid }
|
||||
func (p orderedResponse) setOrderID(oid uint32) { p.orderid = oid }
|
||||
|
||||
type orderedPacket interface {
|
||||
id() uint32
|
||||
orderID() uint32
|
||||
}
|
||||
type orderedPackets []orderedPacket
|
||||
|
||||
func (o orderedPackets) Sort() {
|
||||
sort.Slice(o, func(i, j int) bool {
|
||||
return o[i].orderID() < o[j].orderID()
|
||||
})
|
||||
}
|
||||
|
||||
// // packet registry
|
||||
// register incoming packets to be handled
|
||||
func (s *packetManager) incomingPacket(pkt orderedRequest) {
|
||||
s.working.Add(1)
|
||||
s.requests <- pkt
|
||||
}
|
||||
|
||||
// register outgoing packets as being ready
|
||||
func (s *packetManager) readyPacket(pkt orderedResponse) {
|
||||
s.responses <- pkt
|
||||
s.working.Done()
|
||||
}
|
||||
|
||||
// shut down packetManager controller
|
||||
func (s *packetManager) close() {
|
||||
// pause until current packets are processed
|
||||
s.working.Wait()
|
||||
close(s.fini)
|
||||
}
|
||||
|
||||
// Passed a worker function, returns a channel for incoming packets.
|
||||
// Keep process packet responses in the order they are received while
|
||||
// maximizing throughput of file transfers.
|
||||
func (s *packetManager) workerChan(runWorker func(chan orderedRequest),
|
||||
) chan orderedRequest {
|
||||
// multiple workers for faster read/writes
|
||||
rwChan := make(chan orderedRequest, SftpServerWorkerCount)
|
||||
for i := 0; i < SftpServerWorkerCount; i++ {
|
||||
runWorker(rwChan)
|
||||
}
|
||||
|
||||
// single worker to enforce sequential processing of everything else
|
||||
cmdChan := make(chan orderedRequest)
|
||||
runWorker(cmdChan)
|
||||
|
||||
pktChan := make(chan orderedRequest, SftpServerWorkerCount)
|
||||
go func() {
|
||||
for pkt := range pktChan {
|
||||
switch pkt.requestPacket.(type) {
|
||||
case *sshFxpReadPacket, *sshFxpWritePacket:
|
||||
s.incomingPacket(pkt)
|
||||
rwChan <- pkt
|
||||
continue
|
||||
case *sshFxpClosePacket:
|
||||
// wait for reads/writes to finish when file is closed
|
||||
// incomingPacket() call must occur after this
|
||||
s.working.Wait()
|
||||
}
|
||||
s.incomingPacket(pkt)
|
||||
// all non-RW use sequential cmdChan
|
||||
cmdChan <- pkt
|
||||
}
|
||||
close(rwChan)
|
||||
close(cmdChan)
|
||||
s.close()
|
||||
}()
|
||||
|
||||
return pktChan
|
||||
}
|
||||
|
||||
// process packets
|
||||
func (s *packetManager) controller() {
|
||||
for {
|
||||
select {
|
||||
case pkt := <-s.requests:
|
||||
debug("incoming id (oid): %v (%v)", pkt.id(), pkt.orderID())
|
||||
s.incoming = append(s.incoming, pkt)
|
||||
s.incoming.Sort()
|
||||
case pkt := <-s.responses:
|
||||
debug("outgoing id (oid): %v (%v)", pkt.id(), pkt.orderID())
|
||||
s.outgoing = append(s.outgoing, pkt)
|
||||
s.outgoing.Sort()
|
||||
case <-s.fini:
|
||||
return
|
||||
}
|
||||
s.maybeSendPackets()
|
||||
}
|
||||
}
|
||||
|
||||
// send as many packets as are ready
|
||||
func (s *packetManager) maybeSendPackets() {
|
||||
for {
|
||||
if len(s.outgoing) == 0 || len(s.incoming) == 0 {
|
||||
debug("break! -- outgoing: %v; incoming: %v",
|
||||
len(s.outgoing), len(s.incoming))
|
||||
break
|
||||
}
|
||||
out := s.outgoing[0]
|
||||
in := s.incoming[0]
|
||||
// debug("incoming: %v", ids(s.incoming))
|
||||
// debug("outgoing: %v", ids(s.outgoing))
|
||||
if in.orderID() == out.orderID() {
|
||||
debug("Sending packet: %v", out.id())
|
||||
s.sender.sendPacket(out.(encoding.BinaryMarshaler))
|
||||
if s.alloc != nil {
|
||||
// mark for reuse the slices allocated for this request
|
||||
s.alloc.ReleasePages(in.orderID())
|
||||
}
|
||||
// pop off heads
|
||||
copy(s.incoming, s.incoming[1:]) // shift left
|
||||
s.incoming[len(s.incoming)-1] = nil // clear last
|
||||
s.incoming = s.incoming[:len(s.incoming)-1] // remove last
|
||||
copy(s.outgoing, s.outgoing[1:]) // shift left
|
||||
s.outgoing[len(s.outgoing)-1] = nil // clear last
|
||||
s.outgoing = s.outgoing[:len(s.outgoing)-1] // remove last
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// func oids(o []orderedPacket) []uint32 {
|
||||
// res := make([]uint32, 0, len(o))
|
||||
// for _, v := range o {
|
||||
// res = append(res, v.orderId())
|
||||
// }
|
||||
// return res
|
||||
// }
|
||||
// func ids(o []orderedPacket) []uint32 {
|
||||
// res := make([]uint32, 0, len(o))
|
||||
// for _, v := range o {
|
||||
// res = append(res, v.id())
|
||||
// }
|
||||
// return res
|
||||
// }
|
||||
135
vendor/github.com/pkg/sftp/packet-typing.go
generated
vendored
Normal file
135
vendor/github.com/pkg/sftp/packet-typing.go
generated
vendored
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
package sftp
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// all incoming packets
|
||||
type requestPacket interface {
|
||||
encoding.BinaryUnmarshaler
|
||||
id() uint32
|
||||
}
|
||||
|
||||
type responsePacket interface {
|
||||
encoding.BinaryMarshaler
|
||||
id() uint32
|
||||
}
|
||||
|
||||
// interfaces to group types
|
||||
type hasPath interface {
|
||||
requestPacket
|
||||
getPath() string
|
||||
}
|
||||
|
||||
type hasHandle interface {
|
||||
requestPacket
|
||||
getHandle() string
|
||||
}
|
||||
|
||||
type notReadOnly interface {
|
||||
notReadOnly()
|
||||
}
|
||||
|
||||
// // define types by adding methods
|
||||
// hasPath
|
||||
func (p *sshFxpLstatPacket) getPath() string { return p.Path }
|
||||
func (p *sshFxpStatPacket) getPath() string { return p.Path }
|
||||
func (p *sshFxpRmdirPacket) getPath() string { return p.Path }
|
||||
func (p *sshFxpReadlinkPacket) getPath() string { return p.Path }
|
||||
func (p *sshFxpRealpathPacket) getPath() string { return p.Path }
|
||||
func (p *sshFxpMkdirPacket) getPath() string { return p.Path }
|
||||
func (p *sshFxpSetstatPacket) getPath() string { return p.Path }
|
||||
func (p *sshFxpStatvfsPacket) getPath() string { return p.Path }
|
||||
func (p *sshFxpRemovePacket) getPath() string { return p.Filename }
|
||||
func (p *sshFxpRenamePacket) getPath() string { return p.Oldpath }
|
||||
func (p *sshFxpSymlinkPacket) getPath() string { return p.Targetpath }
|
||||
func (p *sshFxpOpendirPacket) getPath() string { return p.Path }
|
||||
func (p *sshFxpOpenPacket) getPath() string { return p.Path }
|
||||
|
||||
func (p *sshFxpExtendedPacketPosixRename) getPath() string { return p.Oldpath }
|
||||
func (p *sshFxpExtendedPacketHardlink) getPath() string { return p.Oldpath }
|
||||
|
||||
// getHandle
|
||||
func (p *sshFxpFstatPacket) getHandle() string { return p.Handle }
|
||||
func (p *sshFxpFsetstatPacket) getHandle() string { return p.Handle }
|
||||
func (p *sshFxpReadPacket) getHandle() string { return p.Handle }
|
||||
func (p *sshFxpWritePacket) getHandle() string { return p.Handle }
|
||||
func (p *sshFxpReaddirPacket) getHandle() string { return p.Handle }
|
||||
func (p *sshFxpClosePacket) getHandle() string { return p.Handle }
|
||||
|
||||
// notReadOnly
|
||||
func (p *sshFxpWritePacket) notReadOnly() {}
|
||||
func (p *sshFxpSetstatPacket) notReadOnly() {}
|
||||
func (p *sshFxpFsetstatPacket) notReadOnly() {}
|
||||
func (p *sshFxpRemovePacket) notReadOnly() {}
|
||||
func (p *sshFxpMkdirPacket) notReadOnly() {}
|
||||
func (p *sshFxpRmdirPacket) notReadOnly() {}
|
||||
func (p *sshFxpRenamePacket) notReadOnly() {}
|
||||
func (p *sshFxpSymlinkPacket) notReadOnly() {}
|
||||
func (p *sshFxpExtendedPacketPosixRename) notReadOnly() {}
|
||||
func (p *sshFxpExtendedPacketHardlink) notReadOnly() {}
|
||||
|
||||
// some packets with ID are missing id()
|
||||
func (p *sshFxpDataPacket) id() uint32 { return p.ID }
|
||||
func (p *sshFxpStatusPacket) id() uint32 { return p.ID }
|
||||
func (p *sshFxpStatResponse) id() uint32 { return p.ID }
|
||||
func (p *sshFxpNamePacket) id() uint32 { return p.ID }
|
||||
func (p *sshFxpHandlePacket) id() uint32 { return p.ID }
|
||||
func (p *StatVFS) id() uint32 { return p.ID }
|
||||
func (p *sshFxVersionPacket) id() uint32 { return 0 }
|
||||
|
||||
// take raw incoming packet data and build packet objects
|
||||
func makePacket(p rxPacket) (requestPacket, error) {
|
||||
var pkt requestPacket
|
||||
switch p.pktType {
|
||||
case sshFxpInit:
|
||||
pkt = &sshFxInitPacket{}
|
||||
case sshFxpLstat:
|
||||
pkt = &sshFxpLstatPacket{}
|
||||
case sshFxpOpen:
|
||||
pkt = &sshFxpOpenPacket{}
|
||||
case sshFxpClose:
|
||||
pkt = &sshFxpClosePacket{}
|
||||
case sshFxpRead:
|
||||
pkt = &sshFxpReadPacket{}
|
||||
case sshFxpWrite:
|
||||
pkt = &sshFxpWritePacket{}
|
||||
case sshFxpFstat:
|
||||
pkt = &sshFxpFstatPacket{}
|
||||
case sshFxpSetstat:
|
||||
pkt = &sshFxpSetstatPacket{}
|
||||
case sshFxpFsetstat:
|
||||
pkt = &sshFxpFsetstatPacket{}
|
||||
case sshFxpOpendir:
|
||||
pkt = &sshFxpOpendirPacket{}
|
||||
case sshFxpReaddir:
|
||||
pkt = &sshFxpReaddirPacket{}
|
||||
case sshFxpRemove:
|
||||
pkt = &sshFxpRemovePacket{}
|
||||
case sshFxpMkdir:
|
||||
pkt = &sshFxpMkdirPacket{}
|
||||
case sshFxpRmdir:
|
||||
pkt = &sshFxpRmdirPacket{}
|
||||
case sshFxpRealpath:
|
||||
pkt = &sshFxpRealpathPacket{}
|
||||
case sshFxpStat:
|
||||
pkt = &sshFxpStatPacket{}
|
||||
case sshFxpRename:
|
||||
pkt = &sshFxpRenamePacket{}
|
||||
case sshFxpReadlink:
|
||||
pkt = &sshFxpReadlinkPacket{}
|
||||
case sshFxpSymlink:
|
||||
pkt = &sshFxpSymlinkPacket{}
|
||||
case sshFxpExtended:
|
||||
pkt = &sshFxpExtendedPacket{}
|
||||
default:
|
||||
return nil, fmt.Errorf("unhandled packet type: %s", p.pktType)
|
||||
}
|
||||
if err := pkt.UnmarshalBinary(p.pktBytes); err != nil {
|
||||
// Return partially unpacked packet to allow callers to return
|
||||
// error messages appropriately with necessary id() method.
|
||||
return pkt, err
|
||||
}
|
||||
return pkt, nil
|
||||
}
|
||||
1422
vendor/github.com/pkg/sftp/packet.go
generated
vendored
Normal file
1422
vendor/github.com/pkg/sftp/packet.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
79
vendor/github.com/pkg/sftp/pool.go
generated
vendored
Normal file
79
vendor/github.com/pkg/sftp/pool.go
generated
vendored
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
package sftp
|
||||
|
||||
// bufPool provides a pool of byte-slices to be reused in various parts of the package.
|
||||
// It is safe to use concurrently through a pointer.
|
||||
type bufPool struct {
|
||||
ch chan []byte
|
||||
blen int
|
||||
}
|
||||
|
||||
func newBufPool(depth, bufLen int) *bufPool {
|
||||
return &bufPool{
|
||||
ch: make(chan []byte, depth),
|
||||
blen: bufLen,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *bufPool) Get() []byte {
|
||||
if p.blen <= 0 {
|
||||
panic("bufPool: new buffer creation length must be greater than zero")
|
||||
}
|
||||
|
||||
for {
|
||||
select {
|
||||
case b := <-p.ch:
|
||||
if cap(b) < p.blen {
|
||||
// just in case: throw away any buffer with insufficient capacity.
|
||||
continue
|
||||
}
|
||||
|
||||
return b[:p.blen]
|
||||
|
||||
default:
|
||||
return make([]byte, p.blen)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *bufPool) Put(b []byte) {
|
||||
if p == nil {
|
||||
// functional default: no reuse.
|
||||
return
|
||||
}
|
||||
|
||||
if cap(b) < p.blen || cap(b) > p.blen*2 {
|
||||
// DO NOT reuse buffers with insufficient capacity.
|
||||
// This could cause panics when resizing to p.blen.
|
||||
|
||||
// DO NOT reuse buffers with excessive capacity.
|
||||
// This could cause memory leaks.
|
||||
return
|
||||
}
|
||||
|
||||
select {
|
||||
case p.ch <- b:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
type resChanPool chan chan result
|
||||
|
||||
func newResChanPool(depth int) resChanPool {
|
||||
return make(chan chan result, depth)
|
||||
}
|
||||
|
||||
func (p resChanPool) Get() chan result {
|
||||
select {
|
||||
case ch := <-p:
|
||||
return ch
|
||||
default:
|
||||
return make(chan result, 1)
|
||||
}
|
||||
}
|
||||
|
||||
func (p resChanPool) Put(ch chan result) {
|
||||
select {
|
||||
case p <- ch:
|
||||
default:
|
||||
}
|
||||
}
|
||||
6
vendor/github.com/pkg/sftp/release.go
generated
vendored
Normal file
6
vendor/github.com/pkg/sftp/release.go
generated
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
//go:build !debug
|
||||
// +build !debug
|
||||
|
||||
package sftp
|
||||
|
||||
func debug(fmt string, args ...interface{}) {}
|
||||
57
vendor/github.com/pkg/sftp/request-attrs.go
generated
vendored
Normal file
57
vendor/github.com/pkg/sftp/request-attrs.go
generated
vendored
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
package sftp
|
||||
|
||||
// Methods on the Request object to make working with the Flags bitmasks and
|
||||
// Attr(ibutes) byte blob easier. Use Pflags() when working with an Open/Write
|
||||
// request and AttrFlags() and Attributes() when working with SetStat requests.
|
||||
|
||||
// FileOpenFlags defines Open and Write Flags. Correlate directly with with os.OpenFile flags
|
||||
// (https://golang.org/pkg/os/#pkg-constants).
|
||||
type FileOpenFlags struct {
|
||||
Read, Write, Append, Creat, Trunc, Excl bool
|
||||
}
|
||||
|
||||
func newFileOpenFlags(flags uint32) FileOpenFlags {
|
||||
return FileOpenFlags{
|
||||
Read: flags&sshFxfRead != 0,
|
||||
Write: flags&sshFxfWrite != 0,
|
||||
Append: flags&sshFxfAppend != 0,
|
||||
Creat: flags&sshFxfCreat != 0,
|
||||
Trunc: flags&sshFxfTrunc != 0,
|
||||
Excl: flags&sshFxfExcl != 0,
|
||||
}
|
||||
}
|
||||
|
||||
// Pflags converts the bitmap/uint32 from SFTP Open packet pflag values,
|
||||
// into a FileOpenFlags struct with booleans set for flags set in bitmap.
|
||||
func (r *Request) Pflags() FileOpenFlags {
|
||||
return newFileOpenFlags(r.Flags)
|
||||
}
|
||||
|
||||
// FileAttrFlags that indicate whether SFTP file attributes were passed. When a flag is
|
||||
// true the corresponding attribute should be available from the FileStat
|
||||
// object returned by Attributes method. Used with SetStat.
|
||||
type FileAttrFlags struct {
|
||||
Size, UidGid, Permissions, Acmodtime bool
|
||||
}
|
||||
|
||||
func newFileAttrFlags(flags uint32) FileAttrFlags {
|
||||
return FileAttrFlags{
|
||||
Size: (flags & sshFileXferAttrSize) != 0,
|
||||
UidGid: (flags & sshFileXferAttrUIDGID) != 0,
|
||||
Permissions: (flags & sshFileXferAttrPermissions) != 0,
|
||||
Acmodtime: (flags & sshFileXferAttrACmodTime) != 0,
|
||||
}
|
||||
}
|
||||
|
||||
// AttrFlags returns a FileAttrFlags boolean struct based on the
|
||||
// bitmap/uint32 file attribute flags from the SFTP packaet.
|
||||
func (r *Request) AttrFlags() FileAttrFlags {
|
||||
return newFileAttrFlags(r.Flags)
|
||||
}
|
||||
|
||||
// Attributes parses file attributes byte blob and return them in a
|
||||
// FileStat object.
|
||||
func (r *Request) Attributes() *FileStat {
|
||||
fs, _, _ := unmarshalFileStat(r.Flags, r.Attrs)
|
||||
return fs
|
||||
}
|
||||
54
vendor/github.com/pkg/sftp/request-errors.go
generated
vendored
Normal file
54
vendor/github.com/pkg/sftp/request-errors.go
generated
vendored
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
package sftp
|
||||
|
||||
type fxerr uint32
|
||||
|
||||
// Error types that match the SFTP's SSH_FXP_STATUS codes. Gives you more
|
||||
// direct control of the errors being sent vs. letting the library work them
|
||||
// out from the standard os/io errors.
|
||||
const (
|
||||
ErrSSHFxOk = fxerr(sshFxOk)
|
||||
ErrSSHFxEOF = fxerr(sshFxEOF)
|
||||
ErrSSHFxNoSuchFile = fxerr(sshFxNoSuchFile)
|
||||
ErrSSHFxPermissionDenied = fxerr(sshFxPermissionDenied)
|
||||
ErrSSHFxFailure = fxerr(sshFxFailure)
|
||||
ErrSSHFxBadMessage = fxerr(sshFxBadMessage)
|
||||
ErrSSHFxNoConnection = fxerr(sshFxNoConnection)
|
||||
ErrSSHFxConnectionLost = fxerr(sshFxConnectionLost)
|
||||
ErrSSHFxOpUnsupported = fxerr(sshFxOPUnsupported)
|
||||
)
|
||||
|
||||
// Deprecated error types, these are aliases for the new ones, please use the new ones directly
|
||||
const (
|
||||
ErrSshFxOk = ErrSSHFxOk
|
||||
ErrSshFxEof = ErrSSHFxEOF
|
||||
ErrSshFxNoSuchFile = ErrSSHFxNoSuchFile
|
||||
ErrSshFxPermissionDenied = ErrSSHFxPermissionDenied
|
||||
ErrSshFxFailure = ErrSSHFxFailure
|
||||
ErrSshFxBadMessage = ErrSSHFxBadMessage
|
||||
ErrSshFxNoConnection = ErrSSHFxNoConnection
|
||||
ErrSshFxConnectionLost = ErrSSHFxConnectionLost
|
||||
ErrSshFxOpUnsupported = ErrSSHFxOpUnsupported
|
||||
)
|
||||
|
||||
func (e fxerr) Error() string {
|
||||
switch e {
|
||||
case ErrSSHFxOk:
|
||||
return "OK"
|
||||
case ErrSSHFxEOF:
|
||||
return "EOF"
|
||||
case ErrSSHFxNoSuchFile:
|
||||
return "no such file"
|
||||
case ErrSSHFxPermissionDenied:
|
||||
return "permission denied"
|
||||
case ErrSSHFxBadMessage:
|
||||
return "bad message"
|
||||
case ErrSSHFxNoConnection:
|
||||
return "no connection"
|
||||
case ErrSSHFxConnectionLost:
|
||||
return "connection lost"
|
||||
case ErrSSHFxOpUnsupported:
|
||||
return "operation unsupported"
|
||||
default:
|
||||
return "failure"
|
||||
}
|
||||
}
|
||||
647
vendor/github.com/pkg/sftp/request-example.go
generated
vendored
Normal file
647
vendor/github.com/pkg/sftp/request-example.go
generated
vendored
Normal file
|
|
@ -0,0 +1,647 @@
|
|||
package sftp
|
||||
|
||||
// This serves as an example of how to implement the request server handler as
|
||||
// well as a dummy backend for testing. It implements an in-memory backend that
|
||||
// works as a very simple filesystem with simple flat key-value lookup system.
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
const maxSymlinkFollows = 5
|
||||
|
||||
var errTooManySymlinks = errors.New("too many symbolic links")
|
||||
|
||||
// InMemHandler returns a Handlers object with the test handlers.
|
||||
func InMemHandler() Handlers {
|
||||
root := &root{
|
||||
rootFile: &memFile{name: "/", modtime: time.Now(), isdir: true},
|
||||
files: make(map[string]*memFile),
|
||||
}
|
||||
return Handlers{root, root, root, root}
|
||||
}
|
||||
|
||||
// Example Handlers
|
||||
func (fs *root) Fileread(r *Request) (io.ReaderAt, error) {
|
||||
flags := r.Pflags()
|
||||
if !flags.Read {
|
||||
// sanity check
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
|
||||
return fs.OpenFile(r)
|
||||
}
|
||||
|
||||
func (fs *root) Filewrite(r *Request) (io.WriterAt, error) {
|
||||
flags := r.Pflags()
|
||||
if !flags.Write {
|
||||
// sanity check
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
|
||||
return fs.OpenFile(r)
|
||||
}
|
||||
|
||||
func (fs *root) OpenFile(r *Request) (WriterAtReaderAt, error) {
|
||||
if fs.mockErr != nil {
|
||||
return nil, fs.mockErr
|
||||
}
|
||||
_ = r.WithContext(r.Context()) // initialize context for deadlock testing
|
||||
|
||||
fs.mu.Lock()
|
||||
defer fs.mu.Unlock()
|
||||
|
||||
return fs.openfile(r.Filepath, r.Flags)
|
||||
}
|
||||
|
||||
func (fs *root) putfile(pathname string, file *memFile) error {
|
||||
pathname, err := fs.canonName(pathname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(pathname, "/") {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
|
||||
if _, err := fs.lfetch(pathname); err != os.ErrNotExist {
|
||||
return os.ErrExist
|
||||
}
|
||||
|
||||
file.name = pathname
|
||||
fs.files[pathname] = file
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fs *root) openfile(pathname string, flags uint32) (*memFile, error) {
|
||||
pflags := newFileOpenFlags(flags)
|
||||
|
||||
file, err := fs.fetch(pathname)
|
||||
if err == os.ErrNotExist {
|
||||
if !pflags.Creat {
|
||||
return nil, os.ErrNotExist
|
||||
}
|
||||
|
||||
var count int
|
||||
// You can create files through dangling symlinks.
|
||||
link, err := fs.lfetch(pathname)
|
||||
for err == nil && link.symlink != "" {
|
||||
if pflags.Excl {
|
||||
// unless you also passed in O_EXCL
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
|
||||
if count++; count > maxSymlinkFollows {
|
||||
return nil, errTooManySymlinks
|
||||
}
|
||||
|
||||
pathname = link.symlink
|
||||
link, err = fs.lfetch(pathname)
|
||||
}
|
||||
|
||||
file := &memFile{
|
||||
modtime: time.Now(),
|
||||
}
|
||||
|
||||
if err := fs.putfile(pathname, file); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return file, nil
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if pflags.Creat && pflags.Excl {
|
||||
return nil, os.ErrExist
|
||||
}
|
||||
|
||||
if file.IsDir() {
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
|
||||
if pflags.Trunc {
|
||||
if err := file.Truncate(0); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return file, nil
|
||||
}
|
||||
|
||||
func (fs *root) Filecmd(r *Request) error {
|
||||
if fs.mockErr != nil {
|
||||
return fs.mockErr
|
||||
}
|
||||
_ = r.WithContext(r.Context()) // initialize context for deadlock testing
|
||||
|
||||
fs.mu.Lock()
|
||||
defer fs.mu.Unlock()
|
||||
|
||||
switch r.Method {
|
||||
case "Setstat":
|
||||
file, err := fs.openfile(r.Filepath, sshFxfWrite)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if r.AttrFlags().Size {
|
||||
return file.Truncate(int64(r.Attributes().Size))
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
case "Rename":
|
||||
// SFTP-v2: "It is an error if there already exists a file with the name specified by newpath."
|
||||
// This varies from the POSIX specification, which allows limited replacement of target files.
|
||||
if fs.exists(r.Target) {
|
||||
return os.ErrExist
|
||||
}
|
||||
|
||||
return fs.rename(r.Filepath, r.Target)
|
||||
|
||||
case "Rmdir":
|
||||
return fs.rmdir(r.Filepath)
|
||||
|
||||
case "Remove":
|
||||
// IEEE 1003.1 remove explicitly can unlink files and remove empty directories.
|
||||
// We use instead here the semantics of unlink, which is allowed to be restricted against directories.
|
||||
return fs.unlink(r.Filepath)
|
||||
|
||||
case "Mkdir":
|
||||
return fs.mkdir(r.Filepath)
|
||||
|
||||
case "Link":
|
||||
return fs.link(r.Filepath, r.Target)
|
||||
|
||||
case "Symlink":
|
||||
// NOTE: r.Filepath is the target, and r.Target is the linkpath.
|
||||
return fs.symlink(r.Filepath, r.Target)
|
||||
}
|
||||
|
||||
return errors.New("unsupported")
|
||||
}
|
||||
|
||||
func (fs *root) rename(oldpath, newpath string) error {
|
||||
file, err := fs.lfetch(oldpath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
newpath, err = fs.canonName(newpath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(newpath, "/") {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
|
||||
target, err := fs.lfetch(newpath)
|
||||
if err != os.ErrNotExist {
|
||||
if target == file {
|
||||
// IEEE 1003.1: if oldpath and newpath are the same directory entry,
|
||||
// then return no error, and perform no further action.
|
||||
return nil
|
||||
}
|
||||
|
||||
switch {
|
||||
case file.IsDir():
|
||||
// IEEE 1003.1: if oldpath is a directory, and newpath exists,
|
||||
// then newpath must be a directory, and empty.
|
||||
// It is to be removed prior to rename.
|
||||
if err := fs.rmdir(newpath); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case target.IsDir():
|
||||
// IEEE 1003.1: if oldpath is not a directory, and newpath exists,
|
||||
// then newpath may not be a directory.
|
||||
return syscall.EISDIR
|
||||
}
|
||||
}
|
||||
|
||||
fs.files[newpath] = file
|
||||
|
||||
if file.IsDir() {
|
||||
dirprefix := file.name + "/"
|
||||
|
||||
for name, file := range fs.files {
|
||||
if strings.HasPrefix(name, dirprefix) {
|
||||
newname := path.Join(newpath, strings.TrimPrefix(name, dirprefix))
|
||||
|
||||
fs.files[newname] = file
|
||||
file.name = newname
|
||||
delete(fs.files, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
file.name = newpath
|
||||
delete(fs.files, oldpath)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fs *root) PosixRename(r *Request) error {
|
||||
if fs.mockErr != nil {
|
||||
return fs.mockErr
|
||||
}
|
||||
_ = r.WithContext(r.Context()) // initialize context for deadlock testing
|
||||
|
||||
fs.mu.Lock()
|
||||
defer fs.mu.Unlock()
|
||||
|
||||
return fs.rename(r.Filepath, r.Target)
|
||||
}
|
||||
|
||||
func (fs *root) StatVFS(r *Request) (*StatVFS, error) {
|
||||
if fs.mockErr != nil {
|
||||
return nil, fs.mockErr
|
||||
}
|
||||
|
||||
return getStatVFSForPath(r.Filepath)
|
||||
}
|
||||
|
||||
func (fs *root) mkdir(pathname string) error {
|
||||
dir := &memFile{
|
||||
modtime: time.Now(),
|
||||
isdir: true,
|
||||
}
|
||||
|
||||
return fs.putfile(pathname, dir)
|
||||
}
|
||||
|
||||
func (fs *root) rmdir(pathname string) error {
|
||||
// IEEE 1003.1: If pathname is a symlink, then rmdir should fail with ENOTDIR.
|
||||
dir, err := fs.lfetch(pathname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !dir.IsDir() {
|
||||
return syscall.ENOTDIR
|
||||
}
|
||||
|
||||
// use the dir‘s internal name not the pathname we passed in.
|
||||
// the dir.name is always the canonical name of a directory.
|
||||
pathname = dir.name
|
||||
|
||||
for name := range fs.files {
|
||||
if path.Dir(name) == pathname {
|
||||
return errors.New("directory not empty")
|
||||
}
|
||||
}
|
||||
|
||||
delete(fs.files, pathname)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fs *root) link(oldpath, newpath string) error {
|
||||
file, err := fs.lfetch(oldpath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if file.IsDir() {
|
||||
return errors.New("hard link not allowed for directory")
|
||||
}
|
||||
|
||||
return fs.putfile(newpath, file)
|
||||
}
|
||||
|
||||
// symlink() creates a symbolic link named `linkpath` which contains the string `target`.
|
||||
// NOTE! This would be called with `symlink(req.Filepath, req.Target)` due to different semantics.
|
||||
func (fs *root) symlink(target, linkpath string) error {
|
||||
link := &memFile{
|
||||
modtime: time.Now(),
|
||||
symlink: target,
|
||||
}
|
||||
|
||||
return fs.putfile(linkpath, link)
|
||||
}
|
||||
|
||||
func (fs *root) unlink(pathname string) error {
|
||||
// does not follow symlinks!
|
||||
file, err := fs.lfetch(pathname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if file.IsDir() {
|
||||
// IEEE 1003.1: implementations may opt out of allowing the unlinking of directories.
|
||||
// SFTP-v2: SSH_FXP_REMOVE may not remove directories.
|
||||
return os.ErrInvalid
|
||||
}
|
||||
|
||||
// DO NOT use the file’s internal name.
|
||||
// because of hard-links files cannot have a single canonical name.
|
||||
delete(fs.files, pathname)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type listerat []os.FileInfo
|
||||
|
||||
// Modeled after strings.Reader's ReadAt() implementation
|
||||
func (f listerat) ListAt(ls []os.FileInfo, offset int64) (int, error) {
|
||||
var n int
|
||||
if offset >= int64(len(f)) {
|
||||
return 0, io.EOF
|
||||
}
|
||||
n = copy(ls, f[offset:])
|
||||
if n < len(ls) {
|
||||
return n, io.EOF
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (fs *root) Filelist(r *Request) (ListerAt, error) {
|
||||
if fs.mockErr != nil {
|
||||
return nil, fs.mockErr
|
||||
}
|
||||
_ = r.WithContext(r.Context()) // initialize context for deadlock testing
|
||||
|
||||
fs.mu.Lock()
|
||||
defer fs.mu.Unlock()
|
||||
|
||||
switch r.Method {
|
||||
case "List":
|
||||
files, err := fs.readdir(r.Filepath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return listerat(files), nil
|
||||
|
||||
case "Stat":
|
||||
file, err := fs.fetch(r.Filepath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return listerat{file}, nil
|
||||
}
|
||||
|
||||
return nil, errors.New("unsupported")
|
||||
}
|
||||
|
||||
func (fs *root) readdir(pathname string) ([]os.FileInfo, error) {
|
||||
dir, err := fs.fetch(pathname)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !dir.IsDir() {
|
||||
return nil, syscall.ENOTDIR
|
||||
}
|
||||
|
||||
var files []os.FileInfo
|
||||
|
||||
for name, file := range fs.files {
|
||||
if path.Dir(name) == dir.name {
|
||||
files = append(files, file)
|
||||
}
|
||||
}
|
||||
|
||||
sort.Slice(files, func(i, j int) bool { return files[i].Name() < files[j].Name() })
|
||||
|
||||
return files, nil
|
||||
}
|
||||
|
||||
func (fs *root) Readlink(pathname string) (string, error) {
|
||||
file, err := fs.lfetch(pathname)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if file.symlink == "" {
|
||||
return "", os.ErrInvalid
|
||||
}
|
||||
|
||||
return file.symlink, nil
|
||||
}
|
||||
|
||||
// implements LstatFileLister interface
|
||||
func (fs *root) Lstat(r *Request) (ListerAt, error) {
|
||||
if fs.mockErr != nil {
|
||||
return nil, fs.mockErr
|
||||
}
|
||||
_ = r.WithContext(r.Context()) // initialize context for deadlock testing
|
||||
|
||||
fs.mu.Lock()
|
||||
defer fs.mu.Unlock()
|
||||
|
||||
file, err := fs.lfetch(r.Filepath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return listerat{file}, nil
|
||||
}
|
||||
|
||||
// In memory file-system-y thing that the Handlers live on
|
||||
type root struct {
|
||||
rootFile *memFile
|
||||
mockErr error
|
||||
|
||||
mu sync.Mutex
|
||||
files map[string]*memFile
|
||||
}
|
||||
|
||||
// Set a mocked error that the next handler call will return.
|
||||
// Set to nil to reset for no error.
|
||||
func (fs *root) returnErr(err error) {
|
||||
fs.mockErr = err
|
||||
}
|
||||
|
||||
func (fs *root) lfetch(path string) (*memFile, error) {
|
||||
if path == "/" {
|
||||
return fs.rootFile, nil
|
||||
}
|
||||
|
||||
file, ok := fs.files[path]
|
||||
if file == nil {
|
||||
if ok {
|
||||
delete(fs.files, path)
|
||||
}
|
||||
|
||||
return nil, os.ErrNotExist
|
||||
}
|
||||
|
||||
return file, nil
|
||||
}
|
||||
|
||||
// canonName returns the “canonical” name of a file, that is:
|
||||
// if the directory of the pathname is a symlink, it follows that symlink to the valid directory name.
|
||||
// this is relatively easy, since `dir.name` will be the only valid canonical path for a directory.
|
||||
func (fs *root) canonName(pathname string) (string, error) {
|
||||
dirname, filename := path.Dir(pathname), path.Base(pathname)
|
||||
|
||||
dir, err := fs.fetch(dirname)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if !dir.IsDir() {
|
||||
return "", syscall.ENOTDIR
|
||||
}
|
||||
|
||||
return path.Join(dir.name, filename), nil
|
||||
}
|
||||
|
||||
func (fs *root) exists(path string) bool {
|
||||
path, err := fs.canonName(path)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
_, err = fs.lfetch(path)
|
||||
|
||||
return err != os.ErrNotExist
|
||||
}
|
||||
|
||||
func (fs *root) fetch(pathname string) (*memFile, error) {
|
||||
file, err := fs.lfetch(pathname)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var count int
|
||||
for file.symlink != "" {
|
||||
if count++; count > maxSymlinkFollows {
|
||||
return nil, errTooManySymlinks
|
||||
}
|
||||
|
||||
linkTarget := file.symlink
|
||||
if !path.IsAbs(linkTarget) {
|
||||
linkTarget = path.Join(path.Dir(file.name), linkTarget)
|
||||
}
|
||||
|
||||
file, err = fs.lfetch(linkTarget)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return file, nil
|
||||
}
|
||||
|
||||
// Implements os.FileInfo, io.ReaderAt and io.WriterAt interfaces.
|
||||
// These are the 3 interfaces necessary for the Handlers.
|
||||
// Implements the optional interface TransferError.
|
||||
type memFile struct {
|
||||
name string
|
||||
modtime time.Time
|
||||
symlink string
|
||||
isdir bool
|
||||
|
||||
mu sync.RWMutex
|
||||
content []byte
|
||||
err error
|
||||
}
|
||||
|
||||
// These are helper functions, they must be called while holding the memFile.mu mutex
|
||||
func (f *memFile) size() int64 { return int64(len(f.content)) }
|
||||
func (f *memFile) grow(n int64) { f.content = append(f.content, make([]byte, n)...) }
|
||||
|
||||
// Have memFile fulfill os.FileInfo interface
|
||||
func (f *memFile) Name() string { return path.Base(f.name) }
|
||||
func (f *memFile) Size() int64 {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
|
||||
return f.size()
|
||||
}
|
||||
func (f *memFile) Mode() os.FileMode {
|
||||
if f.isdir {
|
||||
return os.FileMode(0755) | os.ModeDir
|
||||
}
|
||||
if f.symlink != "" {
|
||||
return os.FileMode(0777) | os.ModeSymlink
|
||||
}
|
||||
return os.FileMode(0644)
|
||||
}
|
||||
func (f *memFile) ModTime() time.Time { return f.modtime }
|
||||
func (f *memFile) IsDir() bool { return f.isdir }
|
||||
func (f *memFile) Sys() interface{} {
|
||||
return fakeFileInfoSys()
|
||||
}
|
||||
|
||||
func (f *memFile) ReadAt(b []byte, off int64) (int, error) {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
|
||||
if f.err != nil {
|
||||
return 0, f.err
|
||||
}
|
||||
|
||||
if off < 0 {
|
||||
return 0, errors.New("memFile.ReadAt: negative offset")
|
||||
}
|
||||
|
||||
if off >= f.size() {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
n := copy(b, f.content[off:])
|
||||
if n < len(b) {
|
||||
return n, io.EOF
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (f *memFile) WriteAt(b []byte, off int64) (int, error) {
|
||||
// fmt.Println(string(p), off)
|
||||
// mimic write delays, should be optional
|
||||
time.Sleep(time.Microsecond * time.Duration(len(b)))
|
||||
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
|
||||
if f.err != nil {
|
||||
return 0, f.err
|
||||
}
|
||||
|
||||
grow := int64(len(b)) + off - f.size()
|
||||
if grow > 0 {
|
||||
f.grow(grow)
|
||||
}
|
||||
|
||||
return copy(f.content[off:], b), nil
|
||||
}
|
||||
|
||||
func (f *memFile) Truncate(size int64) error {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
|
||||
if f.err != nil {
|
||||
return f.err
|
||||
}
|
||||
|
||||
grow := size - f.size()
|
||||
if grow <= 0 {
|
||||
f.content = f.content[:size]
|
||||
} else {
|
||||
f.grow(grow)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *memFile) TransferError(err error) {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
|
||||
f.err = err
|
||||
}
|
||||
159
vendor/github.com/pkg/sftp/request-interfaces.go
generated
vendored
Normal file
159
vendor/github.com/pkg/sftp/request-interfaces.go
generated
vendored
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
package sftp
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
// WriterAtReaderAt defines the interface to return when a file is to
|
||||
// be opened for reading and writing
|
||||
type WriterAtReaderAt interface {
|
||||
io.WriterAt
|
||||
io.ReaderAt
|
||||
}
|
||||
|
||||
// Interfaces are differentiated based on required returned values.
|
||||
// All input arguments are to be pulled from Request (the only arg).
|
||||
|
||||
// The Handler interfaces all take the Request object as its only argument.
|
||||
// All the data you should need to handle the call are in the Request object.
|
||||
// The request.Method attribute is initially the most important one as it
|
||||
// determines which Handler gets called.
|
||||
|
||||
// FileReader should return an io.ReaderAt for the filepath
|
||||
// Note in cases of an error, the error text will be sent to the client.
|
||||
// Called for Methods: Get
|
||||
type FileReader interface {
|
||||
Fileread(*Request) (io.ReaderAt, error)
|
||||
}
|
||||
|
||||
// FileWriter should return an io.WriterAt for the filepath.
|
||||
//
|
||||
// The request server code will call Close() on the returned io.WriterAt
|
||||
// object if an io.Closer type assertion succeeds.
|
||||
// Note in cases of an error, the error text will be sent to the client.
|
||||
// Note when receiving an Append flag it is important to not open files using
|
||||
// O_APPEND if you plan to use WriteAt, as they conflict.
|
||||
// Called for Methods: Put, Open
|
||||
type FileWriter interface {
|
||||
Filewrite(*Request) (io.WriterAt, error)
|
||||
}
|
||||
|
||||
// OpenFileWriter is a FileWriter that implements the generic OpenFile method.
|
||||
// You need to implement this optional interface if you want to be able
|
||||
// to read and write from/to the same handle.
|
||||
// Called for Methods: Open
|
||||
type OpenFileWriter interface {
|
||||
FileWriter
|
||||
OpenFile(*Request) (WriterAtReaderAt, error)
|
||||
}
|
||||
|
||||
// FileCmder should return an error
|
||||
// Note in cases of an error, the error text will be sent to the client.
|
||||
// Called for Methods: Setstat, Rename, Rmdir, Mkdir, Link, Symlink, Remove
|
||||
type FileCmder interface {
|
||||
Filecmd(*Request) error
|
||||
}
|
||||
|
||||
// PosixRenameFileCmder is a FileCmder that implements the PosixRename method.
|
||||
// If this interface is implemented PosixRename requests will call it
|
||||
// otherwise they will be handled in the same way as Rename
|
||||
type PosixRenameFileCmder interface {
|
||||
FileCmder
|
||||
PosixRename(*Request) error
|
||||
}
|
||||
|
||||
// StatVFSFileCmder is a FileCmder that implements the StatVFS method.
|
||||
// You need to implement this interface if you want to handle statvfs requests.
|
||||
// Please also be sure that the statvfs@openssh.com extension is enabled
|
||||
type StatVFSFileCmder interface {
|
||||
FileCmder
|
||||
StatVFS(*Request) (*StatVFS, error)
|
||||
}
|
||||
|
||||
// FileLister should return an object that fulfils the ListerAt interface
|
||||
// Note in cases of an error, the error text will be sent to the client.
|
||||
// Called for Methods: List, Stat, Readlink
|
||||
//
|
||||
// Since Filelist returns an os.FileInfo, this can make it non-ideal for implementing Readlink.
|
||||
// This is because the Name receiver method defined by that interface defines that it should only return the base name.
|
||||
// However, Readlink is required to be capable of returning essentially any arbitrary valid path relative or absolute.
|
||||
// In order to implement this more expressive requirement, implement [ReadlinkFileLister] which will then be used instead.
|
||||
type FileLister interface {
|
||||
Filelist(*Request) (ListerAt, error)
|
||||
}
|
||||
|
||||
// LstatFileLister is a FileLister that implements the Lstat method.
|
||||
// If this interface is implemented Lstat requests will call it
|
||||
// otherwise they will be handled in the same way as Stat
|
||||
type LstatFileLister interface {
|
||||
FileLister
|
||||
Lstat(*Request) (ListerAt, error)
|
||||
}
|
||||
|
||||
// RealPathFileLister is a FileLister that implements the Realpath method.
|
||||
// The built-in RealPath implementation does not resolve symbolic links.
|
||||
// By implementing this interface you can customize the returned path
|
||||
// and, for example, resolve symbolinc links if needed for your use case.
|
||||
// You have to return an absolute POSIX path.
|
||||
//
|
||||
// Up to v1.13.5 the signature for the RealPath method was:
|
||||
//
|
||||
// # RealPath(string) string
|
||||
//
|
||||
// we have added a legacyRealPathFileLister that implements the old method
|
||||
// to ensure that your code does not break.
|
||||
// You should use the new method signature to avoid future issues
|
||||
type RealPathFileLister interface {
|
||||
FileLister
|
||||
RealPath(string) (string, error)
|
||||
}
|
||||
|
||||
// ReadlinkFileLister is a FileLister that implements the Readlink method.
|
||||
// By implementing the Readlink method, it is possible to return any arbitrary valid path relative or absolute.
|
||||
// This allows giving a better response than via the default FileLister (which is limited to os.FileInfo, whose Name method should only return the base name of a file)
|
||||
type ReadlinkFileLister interface {
|
||||
FileLister
|
||||
Readlink(string) (string, error)
|
||||
}
|
||||
|
||||
// This interface is here for backward compatibility only
|
||||
type legacyRealPathFileLister interface {
|
||||
FileLister
|
||||
RealPath(string) string
|
||||
}
|
||||
|
||||
// NameLookupFileLister is a FileLister that implmeents the LookupUsername and LookupGroupName methods.
|
||||
// If this interface is implemented, then longname ls formatting will use these to convert usernames and groupnames.
|
||||
type NameLookupFileLister interface {
|
||||
FileLister
|
||||
LookupUserName(string) string
|
||||
LookupGroupName(string) string
|
||||
}
|
||||
|
||||
// ListerAt does for file lists what io.ReaderAt does for files, i.e. a []os.FileInfo buffer is passed to the ListAt function
|
||||
// and the entries that are populated in the buffer will be passed to the client.
|
||||
//
|
||||
// ListAt should return the number of entries copied and an io.EOF error if at end of list.
|
||||
// This is testable by comparing how many you copied to how many could be copied (eg. n < len(ls) below).
|
||||
// The copy() builtin is best for the copying.
|
||||
//
|
||||
// Uid and gid information will on unix systems be retrieved from [os.FileInfo.Sys]
|
||||
// if this function returns a [syscall.Stat_t] when called on a populated entry.
|
||||
// Alternatively, if the entry implements [FileInfoUidGid], it will be used for uid and gid information.
|
||||
//
|
||||
// If a populated entry implements [FileInfoExtendedData], extended attributes will also be returned to the client.
|
||||
//
|
||||
// The request server code will call Close() on ListerAt if an io.Closer type assertion succeeds.
|
||||
//
|
||||
// Note in cases of an error, the error text will be sent to the client.
|
||||
type ListerAt interface {
|
||||
ListAt([]os.FileInfo, int64) (int, error)
|
||||
}
|
||||
|
||||
// TransferError is an optional interface that readerAt and writerAt
|
||||
// can implement to be notified about the error causing Serve() to exit
|
||||
// with the request still open
|
||||
type TransferError interface {
|
||||
TransferError(err error)
|
||||
}
|
||||
16
vendor/github.com/pkg/sftp/request-plan9.go
generated
vendored
Normal file
16
vendor/github.com/pkg/sftp/request-plan9.go
generated
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
//go:build plan9
|
||||
// +build plan9
|
||||
|
||||
package sftp
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func fakeFileInfoSys() interface{} {
|
||||
return &syscall.Dir{}
|
||||
}
|
||||
|
||||
func testOsSys(sys interface{}) error {
|
||||
return nil
|
||||
}
|
||||
53
vendor/github.com/pkg/sftp/request-readme.md
generated
vendored
Normal file
53
vendor/github.com/pkg/sftp/request-readme.md
generated
vendored
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
# Request Based SFTP API
|
||||
|
||||
The request based API allows for custom backends in a way similar to the http
|
||||
package. In order to create a backend you need to implement 4 handler
|
||||
interfaces; one for reading, one for writing, one for misc commands and one for
|
||||
listing files. Each has 1 required method and in each case those methods take
|
||||
the Request as the only parameter and they each return something different.
|
||||
These 4 interfaces are enough to handle all the SFTP traffic in a simplified
|
||||
manner.
|
||||
|
||||
The Request structure has 5 public fields which you will deal with.
|
||||
|
||||
- Method (string) - string name of incoming call
|
||||
- Filepath (string) - POSIX path of file to act on
|
||||
- Flags (uint32) - 32bit bitmask value of file open/create flags
|
||||
- Attrs ([]byte) - byte string of file attribute data
|
||||
- Target (string) - target path for renames and sym-links
|
||||
|
||||
Below are the methods and a brief description of what they need to do.
|
||||
|
||||
### Fileread(*Request) (io.Reader, error)
|
||||
|
||||
Handler for "Get" method and returns an io.Reader for the file which the server
|
||||
then sends to the client.
|
||||
|
||||
### Filewrite(*Request) (io.Writer, error)
|
||||
|
||||
Handler for "Put" method and returns an io.Writer for the file which the server
|
||||
then writes the uploaded file to. The file opening "pflags" are currently
|
||||
preserved in the Request.Flags field as a 32bit bitmask value. See the [SFTP
|
||||
spec](https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-02.txt#section-6.3) for
|
||||
details.
|
||||
|
||||
### Filecmd(*Request) error
|
||||
|
||||
Handles "SetStat", "Rename", "Rmdir", "Mkdir" and "Symlink" methods. Makes the
|
||||
appropriate changes and returns nil for success or an filesystem like error
|
||||
(eg. os.ErrNotExist). The attributes are currently propagated in their raw form
|
||||
([]byte) and will need to be unmarshalled to be useful. See the respond method
|
||||
on sshFxpSetstatPacket for example of you might want to do this.
|
||||
|
||||
### Fileinfo(*Request) ([]os.FileInfo, error)
|
||||
|
||||
Handles "List", "Stat", "Readlink" methods. Gathers/creates FileInfo structs
|
||||
with the data on the files and returns in a list (list of 1 for Stat and
|
||||
Readlink).
|
||||
|
||||
|
||||
## TODO
|
||||
|
||||
- Add support for API users to see trace/debugging info of what is going on
|
||||
inside SFTP server.
|
||||
- Unmarshal the file attributes into a structure on the Request object.
|
||||
355
vendor/github.com/pkg/sftp/request-server.go
generated
vendored
Normal file
355
vendor/github.com/pkg/sftp/request-server.go
generated
vendored
Normal file
|
|
@ -0,0 +1,355 @@
|
|||
package sftp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const defaultMaxTxPacket uint32 = 1 << 15
|
||||
|
||||
// Handlers contains the 4 SFTP server request handlers.
|
||||
type Handlers struct {
|
||||
FileGet FileReader
|
||||
FilePut FileWriter
|
||||
FileCmd FileCmder
|
||||
FileList FileLister
|
||||
}
|
||||
|
||||
// RequestServer abstracts the sftp protocol with an http request-like protocol
|
||||
type RequestServer struct {
|
||||
Handlers Handlers
|
||||
|
||||
*serverConn
|
||||
pktMgr *packetManager
|
||||
|
||||
startDirectory string
|
||||
maxTxPacket uint32
|
||||
|
||||
mu sync.RWMutex
|
||||
handleCount int
|
||||
openRequests map[string]*Request
|
||||
}
|
||||
|
||||
// A RequestServerOption is a function which applies configuration to a RequestServer.
|
||||
type RequestServerOption func(*RequestServer)
|
||||
|
||||
// WithRSAllocator enable the allocator.
|
||||
// After processing a packet we keep in memory the allocated slices
|
||||
// and we reuse them for new packets.
|
||||
// The allocator is experimental
|
||||
func WithRSAllocator() RequestServerOption {
|
||||
return func(rs *RequestServer) {
|
||||
alloc := newAllocator()
|
||||
rs.pktMgr.alloc = alloc
|
||||
rs.conn.alloc = alloc
|
||||
}
|
||||
}
|
||||
|
||||
// WithStartDirectory sets a start directory to use as base for relative paths.
|
||||
// If unset the default is "/"
|
||||
func WithStartDirectory(startDirectory string) RequestServerOption {
|
||||
return func(rs *RequestServer) {
|
||||
rs.startDirectory = cleanPath(startDirectory)
|
||||
}
|
||||
}
|
||||
|
||||
// WithRSMaxTxPacket sets the maximum size of the payload returned to the client,
|
||||
// measured in bytes. The default value is 32768 bytes, and this option
|
||||
// can only be used to increase it. Setting this option to a larger value
|
||||
// should be safe, because the client decides the size of the requested payload.
|
||||
//
|
||||
// The default maximum packet size is 32768 bytes.
|
||||
func WithRSMaxTxPacket(size uint32) RequestServerOption {
|
||||
return func(rs *RequestServer) {
|
||||
if size < defaultMaxTxPacket {
|
||||
return
|
||||
}
|
||||
|
||||
rs.maxTxPacket = size
|
||||
}
|
||||
}
|
||||
|
||||
// NewRequestServer creates/allocates/returns new RequestServer.
|
||||
// Normally there will be one server per user-session.
|
||||
func NewRequestServer(rwc io.ReadWriteCloser, h Handlers, options ...RequestServerOption) *RequestServer {
|
||||
svrConn := &serverConn{
|
||||
conn: conn{
|
||||
Reader: rwc,
|
||||
WriteCloser: rwc,
|
||||
},
|
||||
}
|
||||
rs := &RequestServer{
|
||||
Handlers: h,
|
||||
|
||||
serverConn: svrConn,
|
||||
pktMgr: newPktMgr(svrConn),
|
||||
|
||||
startDirectory: "/",
|
||||
maxTxPacket: defaultMaxTxPacket,
|
||||
|
||||
openRequests: make(map[string]*Request),
|
||||
}
|
||||
|
||||
for _, o := range options {
|
||||
o(rs)
|
||||
}
|
||||
return rs
|
||||
}
|
||||
|
||||
// New Open packet/Request
|
||||
func (rs *RequestServer) nextRequest(r *Request) string {
|
||||
rs.mu.Lock()
|
||||
defer rs.mu.Unlock()
|
||||
|
||||
rs.handleCount++
|
||||
|
||||
r.handle = strconv.Itoa(rs.handleCount)
|
||||
rs.openRequests[r.handle] = r
|
||||
|
||||
return r.handle
|
||||
}
|
||||
|
||||
// Returns Request from openRequests, bool is false if it is missing.
|
||||
//
|
||||
// The Requests in openRequests work essentially as open file descriptors that
|
||||
// you can do different things with. What you are doing with it are denoted by
|
||||
// the first packet of that type (read/write/etc).
|
||||
func (rs *RequestServer) getRequest(handle string) (*Request, bool) {
|
||||
rs.mu.RLock()
|
||||
defer rs.mu.RUnlock()
|
||||
|
||||
r, ok := rs.openRequests[handle]
|
||||
return r, ok
|
||||
}
|
||||
|
||||
// Close the Request and clear from openRequests map
|
||||
func (rs *RequestServer) closeRequest(handle string) error {
|
||||
rs.mu.Lock()
|
||||
defer rs.mu.Unlock()
|
||||
|
||||
if r, ok := rs.openRequests[handle]; ok {
|
||||
delete(rs.openRequests, handle)
|
||||
return r.close()
|
||||
}
|
||||
|
||||
return EBADF
|
||||
}
|
||||
|
||||
// Close the read/write/closer to trigger exiting the main server loop
|
||||
func (rs *RequestServer) Close() error { return rs.conn.Close() }
|
||||
|
||||
func (rs *RequestServer) serveLoop(pktChan chan<- orderedRequest) error {
|
||||
defer close(pktChan) // shuts down sftpServerWorkers
|
||||
|
||||
var err error
|
||||
var pkt requestPacket
|
||||
var pktType fxp
|
||||
var pktBytes []byte
|
||||
|
||||
for {
|
||||
pktType, pktBytes, err = rs.serverConn.recvPacket(rs.pktMgr.getNextOrderID())
|
||||
if err != nil {
|
||||
// we don't care about releasing allocated pages here, the server will quit and the allocator freed
|
||||
return err
|
||||
}
|
||||
|
||||
pkt, err = makePacket(rxPacket{pktType, pktBytes})
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, errUnknownExtendedPacket):
|
||||
// do nothing
|
||||
default:
|
||||
debug("makePacket err: %v", err)
|
||||
rs.conn.Close() // shuts down recvPacket
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
pktChan <- rs.pktMgr.newOrderedRequest(pkt)
|
||||
}
|
||||
}
|
||||
|
||||
// Serve requests for user session
|
||||
func (rs *RequestServer) Serve() error {
|
||||
defer func() {
|
||||
if rs.pktMgr.alloc != nil {
|
||||
rs.pktMgr.alloc.Free()
|
||||
}
|
||||
}()
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
var wg sync.WaitGroup
|
||||
runWorker := func(ch chan orderedRequest) {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
if err := rs.packetWorker(ctx, ch); err != nil {
|
||||
rs.conn.Close() // shuts down recvPacket
|
||||
}
|
||||
}()
|
||||
}
|
||||
pktChan := rs.pktMgr.workerChan(runWorker)
|
||||
|
||||
err := rs.serveLoop(pktChan)
|
||||
|
||||
wg.Wait() // wait for all workers to exit
|
||||
|
||||
rs.mu.Lock()
|
||||
defer rs.mu.Unlock()
|
||||
|
||||
// make sure all open requests are properly closed
|
||||
// (eg. possible on dropped connections, client crashes, etc.)
|
||||
for handle, req := range rs.openRequests {
|
||||
if err == io.EOF {
|
||||
err = io.ErrUnexpectedEOF
|
||||
}
|
||||
req.transferError(err)
|
||||
|
||||
delete(rs.openRequests, handle)
|
||||
req.close()
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (rs *RequestServer) packetWorker(ctx context.Context, pktChan chan orderedRequest) error {
|
||||
for pkt := range pktChan {
|
||||
orderID := pkt.orderID()
|
||||
if epkt, ok := pkt.requestPacket.(*sshFxpExtendedPacket); ok {
|
||||
if epkt.SpecificPacket != nil {
|
||||
pkt.requestPacket = epkt.SpecificPacket
|
||||
}
|
||||
}
|
||||
|
||||
var rpkt responsePacket
|
||||
switch pkt := pkt.requestPacket.(type) {
|
||||
case *sshFxInitPacket:
|
||||
rpkt = &sshFxVersionPacket{Version: sftpProtocolVersion, Extensions: sftpExtensions}
|
||||
case *sshFxpClosePacket:
|
||||
handle := pkt.getHandle()
|
||||
rpkt = statusFromError(pkt.ID, rs.closeRequest(handle))
|
||||
case *sshFxpRealpathPacket:
|
||||
var realPath string
|
||||
var err error
|
||||
|
||||
switch pather := rs.Handlers.FileList.(type) {
|
||||
case RealPathFileLister:
|
||||
realPath, err = pather.RealPath(pkt.getPath())
|
||||
case legacyRealPathFileLister:
|
||||
realPath = pather.RealPath(pkt.getPath())
|
||||
default:
|
||||
realPath = cleanPathWithBase(rs.startDirectory, pkt.getPath())
|
||||
}
|
||||
if err != nil {
|
||||
rpkt = statusFromError(pkt.ID, err)
|
||||
} else {
|
||||
rpkt = cleanPacketPath(pkt, realPath)
|
||||
}
|
||||
case *sshFxpOpendirPacket:
|
||||
request := requestFromPacket(ctx, pkt, rs.startDirectory)
|
||||
handle := rs.nextRequest(request)
|
||||
rpkt = request.opendir(rs.Handlers, pkt)
|
||||
if _, ok := rpkt.(*sshFxpHandlePacket); !ok {
|
||||
// if we return an error we have to remove the handle from the active ones
|
||||
rs.closeRequest(handle)
|
||||
}
|
||||
case *sshFxpOpenPacket:
|
||||
request := requestFromPacket(ctx, pkt, rs.startDirectory)
|
||||
handle := rs.nextRequest(request)
|
||||
rpkt = request.open(rs.Handlers, pkt)
|
||||
if _, ok := rpkt.(*sshFxpHandlePacket); !ok {
|
||||
// if we return an error we have to remove the handle from the active ones
|
||||
rs.closeRequest(handle)
|
||||
}
|
||||
case *sshFxpFstatPacket:
|
||||
handle := pkt.getHandle()
|
||||
request, ok := rs.getRequest(handle)
|
||||
if !ok {
|
||||
rpkt = statusFromError(pkt.ID, EBADF)
|
||||
} else {
|
||||
request = &Request{
|
||||
Method: "Stat",
|
||||
Filepath: cleanPathWithBase(rs.startDirectory, request.Filepath),
|
||||
}
|
||||
rpkt = request.call(rs.Handlers, pkt, rs.pktMgr.alloc, orderID, rs.maxTxPacket)
|
||||
}
|
||||
case *sshFxpFsetstatPacket:
|
||||
handle := pkt.getHandle()
|
||||
request, ok := rs.getRequest(handle)
|
||||
if !ok {
|
||||
rpkt = statusFromError(pkt.ID, EBADF)
|
||||
} else {
|
||||
request = &Request{
|
||||
Method: "Setstat",
|
||||
Filepath: cleanPathWithBase(rs.startDirectory, request.Filepath),
|
||||
}
|
||||
rpkt = request.call(rs.Handlers, pkt, rs.pktMgr.alloc, orderID, rs.maxTxPacket)
|
||||
}
|
||||
case *sshFxpExtendedPacketPosixRename:
|
||||
request := &Request{
|
||||
Method: "PosixRename",
|
||||
Filepath: cleanPathWithBase(rs.startDirectory, pkt.Oldpath),
|
||||
Target: cleanPathWithBase(rs.startDirectory, pkt.Newpath),
|
||||
}
|
||||
rpkt = request.call(rs.Handlers, pkt, rs.pktMgr.alloc, orderID, rs.maxTxPacket)
|
||||
case *sshFxpExtendedPacketStatVFS:
|
||||
request := &Request{
|
||||
Method: "StatVFS",
|
||||
Filepath: cleanPathWithBase(rs.startDirectory, pkt.Path),
|
||||
}
|
||||
rpkt = request.call(rs.Handlers, pkt, rs.pktMgr.alloc, orderID, rs.maxTxPacket)
|
||||
case hasHandle:
|
||||
handle := pkt.getHandle()
|
||||
request, ok := rs.getRequest(handle)
|
||||
if !ok {
|
||||
rpkt = statusFromError(pkt.id(), EBADF)
|
||||
} else {
|
||||
rpkt = request.call(rs.Handlers, pkt, rs.pktMgr.alloc, orderID, rs.maxTxPacket)
|
||||
}
|
||||
case hasPath:
|
||||
request := requestFromPacket(ctx, pkt, rs.startDirectory)
|
||||
rpkt = request.call(rs.Handlers, pkt, rs.pktMgr.alloc, orderID, rs.maxTxPacket)
|
||||
request.close()
|
||||
default:
|
||||
rpkt = statusFromError(pkt.id(), ErrSSHFxOpUnsupported)
|
||||
}
|
||||
|
||||
rs.pktMgr.readyPacket(
|
||||
rs.pktMgr.newOrderedResponse(rpkt, orderID))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// clean and return name packet for file
|
||||
func cleanPacketPath(pkt *sshFxpRealpathPacket, realPath string) responsePacket {
|
||||
return &sshFxpNamePacket{
|
||||
ID: pkt.id(),
|
||||
NameAttrs: []*sshFxpNameAttr{
|
||||
{
|
||||
Name: realPath,
|
||||
LongName: realPath,
|
||||
Attrs: emptyFileStat,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Makes sure we have a clean POSIX (/) absolute path to work with
|
||||
func cleanPath(p string) string {
|
||||
return cleanPathWithBase("/", p)
|
||||
}
|
||||
|
||||
func cleanPathWithBase(base, p string) string {
|
||||
p = filepath.ToSlash(filepath.Clean(p))
|
||||
if !path.IsAbs(p) {
|
||||
return path.Join(base, p)
|
||||
}
|
||||
return p
|
||||
}
|
||||
24
vendor/github.com/pkg/sftp/request-unix.go
generated
vendored
Normal file
24
vendor/github.com/pkg/sftp/request-unix.go
generated
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
//go:build !windows && !plan9
|
||||
// +build !windows,!plan9
|
||||
|
||||
package sftp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func fakeFileInfoSys() interface{} {
|
||||
return &syscall.Stat_t{Uid: 65534, Gid: 65534}
|
||||
}
|
||||
|
||||
func testOsSys(sys interface{}) error {
|
||||
fstat := sys.(*FileStat)
|
||||
if fstat.UID != uint32(65534) {
|
||||
return errors.New("Uid failed to match")
|
||||
}
|
||||
if fstat.GID != uint32(65534) {
|
||||
return errors.New("Gid failed to match")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
670
vendor/github.com/pkg/sftp/request.go
generated
vendored
Normal file
670
vendor/github.com/pkg/sftp/request.go
generated
vendored
Normal file
|
|
@ -0,0 +1,670 @@
|
|||
package sftp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// MaxFilelist is the max number of files to return in a readdir batch.
|
||||
var MaxFilelist int64 = 100
|
||||
|
||||
// state encapsulates the reader/writer/readdir from handlers.
|
||||
type state struct {
|
||||
mu sync.RWMutex
|
||||
|
||||
writerAt io.WriterAt
|
||||
readerAt io.ReaderAt
|
||||
writerAtReaderAt WriterAtReaderAt
|
||||
listerAt ListerAt
|
||||
lsoffset int64
|
||||
}
|
||||
|
||||
// copy returns a shallow copy the state.
|
||||
// This is broken out to specific fields,
|
||||
// because we have to copy around the mutex in state.
|
||||
func (s *state) copy() state {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
return state{
|
||||
writerAt: s.writerAt,
|
||||
readerAt: s.readerAt,
|
||||
writerAtReaderAt: s.writerAtReaderAt,
|
||||
listerAt: s.listerAt,
|
||||
lsoffset: s.lsoffset,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *state) setReaderAt(rd io.ReaderAt) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
s.readerAt = rd
|
||||
}
|
||||
|
||||
func (s *state) getReaderAt() io.ReaderAt {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
return s.readerAt
|
||||
}
|
||||
|
||||
func (s *state) setWriterAt(rd io.WriterAt) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
s.writerAt = rd
|
||||
}
|
||||
|
||||
func (s *state) getWriterAt() io.WriterAt {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
return s.writerAt
|
||||
}
|
||||
|
||||
func (s *state) setWriterAtReaderAt(rw WriterAtReaderAt) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
s.writerAtReaderAt = rw
|
||||
}
|
||||
|
||||
func (s *state) getWriterAtReaderAt() WriterAtReaderAt {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
return s.writerAtReaderAt
|
||||
}
|
||||
|
||||
func (s *state) getAllReaderWriters() (io.ReaderAt, io.WriterAt, WriterAtReaderAt) {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
return s.readerAt, s.writerAt, s.writerAtReaderAt
|
||||
}
|
||||
|
||||
// Returns current offset for file list
|
||||
func (s *state) lsNext() int64 {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
return s.lsoffset
|
||||
}
|
||||
|
||||
// Increases next offset
|
||||
func (s *state) lsInc(offset int64) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
s.lsoffset += offset
|
||||
}
|
||||
|
||||
// manage file read/write state
|
||||
func (s *state) setListerAt(la ListerAt) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
s.listerAt = la
|
||||
}
|
||||
|
||||
func (s *state) getListerAt() ListerAt {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
return s.listerAt
|
||||
}
|
||||
|
||||
func (s *state) closeListerAt() error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
var err error
|
||||
|
||||
if s.listerAt != nil {
|
||||
if c, ok := s.listerAt.(io.Closer); ok {
|
||||
err = c.Close()
|
||||
}
|
||||
s.listerAt = nil
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Request contains the data and state for the incoming service request.
|
||||
type Request struct {
|
||||
// Get, Put, Setstat, Stat, Rename, Remove
|
||||
// Rmdir, Mkdir, List, Readlink, Link, Symlink
|
||||
Method string
|
||||
Filepath string
|
||||
Flags uint32
|
||||
Attrs []byte // convert to sub-struct
|
||||
Target string // for renames and sym-links
|
||||
handle string
|
||||
|
||||
// reader/writer/readdir from handlers
|
||||
state
|
||||
|
||||
// context lasts duration of request
|
||||
ctx context.Context
|
||||
cancelCtx context.CancelFunc
|
||||
}
|
||||
|
||||
// NewRequest creates a new Request object.
|
||||
func NewRequest(method, path string) *Request {
|
||||
return &Request{
|
||||
Method: method,
|
||||
Filepath: cleanPath(path),
|
||||
}
|
||||
}
|
||||
|
||||
// copy returns a shallow copy of existing request.
|
||||
// This is broken out to specific fields,
|
||||
// because we have to copy around the mutex in state.
|
||||
func (r *Request) copy() *Request {
|
||||
return &Request{
|
||||
Method: r.Method,
|
||||
Filepath: r.Filepath,
|
||||
Flags: r.Flags,
|
||||
Attrs: r.Attrs,
|
||||
Target: r.Target,
|
||||
handle: r.handle,
|
||||
|
||||
state: r.state.copy(),
|
||||
|
||||
ctx: r.ctx,
|
||||
cancelCtx: r.cancelCtx,
|
||||
}
|
||||
}
|
||||
|
||||
// New Request initialized based on packet data
|
||||
func requestFromPacket(ctx context.Context, pkt hasPath, baseDir string) *Request {
|
||||
request := &Request{
|
||||
Method: requestMethod(pkt),
|
||||
Filepath: cleanPathWithBase(baseDir, pkt.getPath()),
|
||||
}
|
||||
request.ctx, request.cancelCtx = context.WithCancel(ctx)
|
||||
|
||||
switch p := pkt.(type) {
|
||||
case *sshFxpOpenPacket:
|
||||
request.Flags = p.Pflags
|
||||
request.Attrs = p.Attrs.([]byte)
|
||||
case *sshFxpSetstatPacket:
|
||||
request.Flags = p.Flags
|
||||
request.Attrs = p.Attrs.([]byte)
|
||||
case *sshFxpRenamePacket:
|
||||
request.Target = cleanPathWithBase(baseDir, p.Newpath)
|
||||
case *sshFxpSymlinkPacket:
|
||||
// NOTE: given a POSIX compliant signature: symlink(target, linkpath string)
|
||||
// this makes Request.Target the linkpath, and Request.Filepath the target.
|
||||
request.Target = cleanPathWithBase(baseDir, p.Linkpath)
|
||||
request.Filepath = p.Targetpath
|
||||
case *sshFxpExtendedPacketHardlink:
|
||||
request.Target = cleanPathWithBase(baseDir, p.Newpath)
|
||||
}
|
||||
return request
|
||||
}
|
||||
|
||||
// Context returns the request's context. To change the context,
|
||||
// use WithContext.
|
||||
//
|
||||
// The returned context is always non-nil; it defaults to the
|
||||
// background context.
|
||||
//
|
||||
// For incoming server requests, the context is canceled when the
|
||||
// request is complete or the client's connection closes.
|
||||
func (r *Request) Context() context.Context {
|
||||
if r.ctx != nil {
|
||||
return r.ctx
|
||||
}
|
||||
return context.Background()
|
||||
}
|
||||
|
||||
// WithContext returns a copy of r with its context changed to ctx.
|
||||
// The provided ctx must be non-nil.
|
||||
func (r *Request) WithContext(ctx context.Context) *Request {
|
||||
if ctx == nil {
|
||||
panic("nil context")
|
||||
}
|
||||
r2 := r.copy()
|
||||
r2.ctx = ctx
|
||||
r2.cancelCtx = nil
|
||||
return r2
|
||||
}
|
||||
|
||||
// Close reader/writer if possible
|
||||
func (r *Request) close() error {
|
||||
defer func() {
|
||||
if r.cancelCtx != nil {
|
||||
r.cancelCtx()
|
||||
}
|
||||
}()
|
||||
|
||||
err := r.state.closeListerAt()
|
||||
|
||||
rd, wr, rw := r.getAllReaderWriters()
|
||||
|
||||
// Close errors on a Writer are far more likely to be the important one.
|
||||
// As they can be information that there was a loss of data.
|
||||
if c, ok := wr.(io.Closer); ok {
|
||||
if err2 := c.Close(); err == nil {
|
||||
// update error if it is still nil
|
||||
err = err2
|
||||
}
|
||||
}
|
||||
|
||||
if c, ok := rw.(io.Closer); ok {
|
||||
if err2 := c.Close(); err == nil {
|
||||
// update error if it is still nil
|
||||
err = err2
|
||||
|
||||
r.setWriterAtReaderAt(nil)
|
||||
}
|
||||
}
|
||||
|
||||
if c, ok := rd.(io.Closer); ok {
|
||||
if err2 := c.Close(); err == nil {
|
||||
// update error if it is still nil
|
||||
err = err2
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Notify transfer error if any
|
||||
func (r *Request) transferError(err error) {
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
rd, wr, rw := r.getAllReaderWriters()
|
||||
|
||||
if t, ok := wr.(TransferError); ok {
|
||||
t.TransferError(err)
|
||||
}
|
||||
|
||||
if t, ok := rw.(TransferError); ok {
|
||||
t.TransferError(err)
|
||||
}
|
||||
|
||||
if t, ok := rd.(TransferError); ok {
|
||||
t.TransferError(err)
|
||||
}
|
||||
}
|
||||
|
||||
// called from worker to handle packet/request
|
||||
func (r *Request) call(handlers Handlers, pkt requestPacket, alloc *allocator, orderID uint32, maxTxPacket uint32) responsePacket {
|
||||
switch r.Method {
|
||||
case "Get":
|
||||
return fileget(handlers.FileGet, r, pkt, alloc, orderID, maxTxPacket)
|
||||
case "Put":
|
||||
return fileput(handlers.FilePut, r, pkt, alloc, orderID, maxTxPacket)
|
||||
case "Open":
|
||||
return fileputget(handlers.FilePut, r, pkt, alloc, orderID, maxTxPacket)
|
||||
case "Setstat", "Rename", "Rmdir", "Mkdir", "Link", "Symlink", "Remove", "PosixRename", "StatVFS":
|
||||
return filecmd(handlers.FileCmd, r, pkt)
|
||||
case "List":
|
||||
return filelist(handlers.FileList, r, pkt)
|
||||
case "Stat", "Lstat":
|
||||
return filestat(handlers.FileList, r, pkt)
|
||||
case "Readlink":
|
||||
if readlinkFileLister, ok := handlers.FileList.(ReadlinkFileLister); ok {
|
||||
return readlink(readlinkFileLister, r, pkt)
|
||||
}
|
||||
return filestat(handlers.FileList, r, pkt)
|
||||
default:
|
||||
return statusFromError(pkt.id(), fmt.Errorf("unexpected method: %s", r.Method))
|
||||
}
|
||||
}
|
||||
|
||||
// Additional initialization for Open packets
|
||||
func (r *Request) open(h Handlers, pkt requestPacket) responsePacket {
|
||||
flags := r.Pflags()
|
||||
|
||||
id := pkt.id()
|
||||
|
||||
switch {
|
||||
case flags.Write, flags.Append, flags.Creat, flags.Trunc:
|
||||
if flags.Read {
|
||||
if openFileWriter, ok := h.FilePut.(OpenFileWriter); ok {
|
||||
r.Method = "Open"
|
||||
rw, err := openFileWriter.OpenFile(r)
|
||||
if err != nil {
|
||||
return statusFromError(id, err)
|
||||
}
|
||||
|
||||
r.setWriterAtReaderAt(rw)
|
||||
|
||||
return &sshFxpHandlePacket{
|
||||
ID: id,
|
||||
Handle: r.handle,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
r.Method = "Put"
|
||||
wr, err := h.FilePut.Filewrite(r)
|
||||
if err != nil {
|
||||
return statusFromError(id, err)
|
||||
}
|
||||
|
||||
r.setWriterAt(wr)
|
||||
|
||||
case flags.Read:
|
||||
r.Method = "Get"
|
||||
rd, err := h.FileGet.Fileread(r)
|
||||
if err != nil {
|
||||
return statusFromError(id, err)
|
||||
}
|
||||
|
||||
r.setReaderAt(rd)
|
||||
|
||||
default:
|
||||
return statusFromError(id, errors.New("bad file flags"))
|
||||
}
|
||||
|
||||
return &sshFxpHandlePacket{
|
||||
ID: id,
|
||||
Handle: r.handle,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Request) opendir(h Handlers, pkt requestPacket) responsePacket {
|
||||
r.Method = "List"
|
||||
la, err := h.FileList.Filelist(r)
|
||||
if err != nil {
|
||||
return statusFromError(pkt.id(), wrapPathError(r.Filepath, err))
|
||||
}
|
||||
|
||||
r.setListerAt(la)
|
||||
|
||||
return &sshFxpHandlePacket{
|
||||
ID: pkt.id(),
|
||||
Handle: r.handle,
|
||||
}
|
||||
}
|
||||
|
||||
// wrap FileReader handler
|
||||
func fileget(h FileReader, r *Request, pkt requestPacket, alloc *allocator, orderID uint32, maxTxPacket uint32) responsePacket {
|
||||
rd := r.getReaderAt()
|
||||
if rd == nil {
|
||||
return statusFromError(pkt.id(), errors.New("unexpected read packet"))
|
||||
}
|
||||
|
||||
data, offset, _ := packetData(pkt, alloc, orderID, maxTxPacket)
|
||||
|
||||
n, err := rd.ReadAt(data, offset)
|
||||
// only return EOF error if no data left to read
|
||||
if err != nil && (err != io.EOF || n == 0) {
|
||||
return statusFromError(pkt.id(), err)
|
||||
}
|
||||
|
||||
return &sshFxpDataPacket{
|
||||
ID: pkt.id(),
|
||||
Length: uint32(n),
|
||||
Data: data[:n],
|
||||
}
|
||||
}
|
||||
|
||||
// wrap FileWriter handler
|
||||
func fileput(h FileWriter, r *Request, pkt requestPacket, alloc *allocator, orderID uint32, maxTxPacket uint32) responsePacket {
|
||||
wr := r.getWriterAt()
|
||||
if wr == nil {
|
||||
return statusFromError(pkt.id(), errors.New("unexpected write packet"))
|
||||
}
|
||||
|
||||
data, offset, _ := packetData(pkt, alloc, orderID, maxTxPacket)
|
||||
|
||||
_, err := wr.WriteAt(data, offset)
|
||||
return statusFromError(pkt.id(), err)
|
||||
}
|
||||
|
||||
// wrap OpenFileWriter handler
|
||||
func fileputget(h FileWriter, r *Request, pkt requestPacket, alloc *allocator, orderID uint32, maxTxPacket uint32) responsePacket {
|
||||
rw := r.getWriterAtReaderAt()
|
||||
if rw == nil {
|
||||
return statusFromError(pkt.id(), errors.New("unexpected write and read packet"))
|
||||
}
|
||||
|
||||
switch p := pkt.(type) {
|
||||
case *sshFxpReadPacket:
|
||||
data, offset := p.getDataSlice(alloc, orderID, maxTxPacket), int64(p.Offset)
|
||||
|
||||
n, err := rw.ReadAt(data, offset)
|
||||
// only return EOF error if no data left to read
|
||||
if err != nil && (err != io.EOF || n == 0) {
|
||||
return statusFromError(pkt.id(), err)
|
||||
}
|
||||
|
||||
return &sshFxpDataPacket{
|
||||
ID: pkt.id(),
|
||||
Length: uint32(n),
|
||||
Data: data[:n],
|
||||
}
|
||||
|
||||
case *sshFxpWritePacket:
|
||||
data, offset := p.Data, int64(p.Offset)
|
||||
|
||||
_, err := rw.WriteAt(data, offset)
|
||||
return statusFromError(pkt.id(), err)
|
||||
|
||||
default:
|
||||
return statusFromError(pkt.id(), errors.New("unexpected packet type for read or write"))
|
||||
}
|
||||
}
|
||||
|
||||
// file data for additional read/write packets
|
||||
func packetData(p requestPacket, alloc *allocator, orderID uint32, maxTxPacket uint32) (data []byte, offset int64, length uint32) {
|
||||
switch p := p.(type) {
|
||||
case *sshFxpReadPacket:
|
||||
return p.getDataSlice(alloc, orderID, maxTxPacket), int64(p.Offset), p.Len
|
||||
case *sshFxpWritePacket:
|
||||
return p.Data, int64(p.Offset), p.Length
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// wrap FileCmder handler
|
||||
func filecmd(h FileCmder, r *Request, pkt requestPacket) responsePacket {
|
||||
switch p := pkt.(type) {
|
||||
case *sshFxpFsetstatPacket:
|
||||
r.Flags = p.Flags
|
||||
r.Attrs = p.Attrs.([]byte)
|
||||
}
|
||||
|
||||
switch r.Method {
|
||||
case "PosixRename":
|
||||
if posixRenamer, ok := h.(PosixRenameFileCmder); ok {
|
||||
err := posixRenamer.PosixRename(r)
|
||||
return statusFromError(pkt.id(), err)
|
||||
}
|
||||
|
||||
// PosixRenameFileCmder not implemented handle this request as a Rename
|
||||
r.Method = "Rename"
|
||||
err := h.Filecmd(r)
|
||||
return statusFromError(pkt.id(), err)
|
||||
|
||||
case "StatVFS":
|
||||
if statVFSCmdr, ok := h.(StatVFSFileCmder); ok {
|
||||
stat, err := statVFSCmdr.StatVFS(r)
|
||||
if err != nil {
|
||||
return statusFromError(pkt.id(), err)
|
||||
}
|
||||
stat.ID = pkt.id()
|
||||
return stat
|
||||
}
|
||||
|
||||
return statusFromError(pkt.id(), ErrSSHFxOpUnsupported)
|
||||
}
|
||||
|
||||
err := h.Filecmd(r)
|
||||
return statusFromError(pkt.id(), err)
|
||||
}
|
||||
|
||||
// wrap FileLister handler
|
||||
func filelist(h FileLister, r *Request, pkt requestPacket) responsePacket {
|
||||
lister := r.getListerAt()
|
||||
if lister == nil {
|
||||
return statusFromError(pkt.id(), errors.New("unexpected dir packet"))
|
||||
}
|
||||
|
||||
offset := r.lsNext()
|
||||
finfo := make([]os.FileInfo, MaxFilelist)
|
||||
n, err := lister.ListAt(finfo, offset)
|
||||
r.lsInc(int64(n))
|
||||
// ignore EOF as we only return it when there are no results
|
||||
finfo = finfo[:n] // avoid need for nil tests below
|
||||
|
||||
switch r.Method {
|
||||
case "List":
|
||||
if err != nil && (err != io.EOF || n == 0) {
|
||||
return statusFromError(pkt.id(), err)
|
||||
}
|
||||
|
||||
nameAttrs := make([]*sshFxpNameAttr, 0, len(finfo))
|
||||
|
||||
// If the type conversion fails, we get untyped `nil`,
|
||||
// which is handled by not looking up any names.
|
||||
idLookup, _ := h.(NameLookupFileLister)
|
||||
|
||||
for _, fi := range finfo {
|
||||
nameAttrs = append(nameAttrs, &sshFxpNameAttr{
|
||||
Name: fi.Name(),
|
||||
LongName: runLs(idLookup, fi),
|
||||
Attrs: []interface{}{fi},
|
||||
})
|
||||
}
|
||||
|
||||
return &sshFxpNamePacket{
|
||||
ID: pkt.id(),
|
||||
NameAttrs: nameAttrs,
|
||||
}
|
||||
|
||||
default:
|
||||
err = fmt.Errorf("unexpected method: %s", r.Method)
|
||||
return statusFromError(pkt.id(), err)
|
||||
}
|
||||
}
|
||||
|
||||
func filestat(h FileLister, r *Request, pkt requestPacket) responsePacket {
|
||||
var lister ListerAt
|
||||
var err error
|
||||
|
||||
if r.Method == "Lstat" {
|
||||
if lstatFileLister, ok := h.(LstatFileLister); ok {
|
||||
lister, err = lstatFileLister.Lstat(r)
|
||||
} else {
|
||||
// LstatFileLister not implemented handle this request as a Stat
|
||||
r.Method = "Stat"
|
||||
lister, err = h.Filelist(r)
|
||||
}
|
||||
} else {
|
||||
lister, err = h.Filelist(r)
|
||||
}
|
||||
if err != nil {
|
||||
return statusFromError(pkt.id(), err)
|
||||
}
|
||||
finfo := make([]os.FileInfo, 1)
|
||||
n, err := lister.ListAt(finfo, 0)
|
||||
finfo = finfo[:n] // avoid need for nil tests below
|
||||
|
||||
switch r.Method {
|
||||
case "Stat", "Lstat":
|
||||
if err != nil && err != io.EOF {
|
||||
return statusFromError(pkt.id(), err)
|
||||
}
|
||||
if n == 0 {
|
||||
err = &os.PathError{
|
||||
Op: strings.ToLower(r.Method),
|
||||
Path: r.Filepath,
|
||||
Err: syscall.ENOENT,
|
||||
}
|
||||
return statusFromError(pkt.id(), err)
|
||||
}
|
||||
return &sshFxpStatResponse{
|
||||
ID: pkt.id(),
|
||||
info: finfo[0],
|
||||
}
|
||||
case "Readlink":
|
||||
if err != nil && err != io.EOF {
|
||||
return statusFromError(pkt.id(), err)
|
||||
}
|
||||
if n == 0 {
|
||||
err = &os.PathError{
|
||||
Op: "readlink",
|
||||
Path: r.Filepath,
|
||||
Err: syscall.ENOENT,
|
||||
}
|
||||
return statusFromError(pkt.id(), err)
|
||||
}
|
||||
filename := finfo[0].Name()
|
||||
return &sshFxpNamePacket{
|
||||
ID: pkt.id(),
|
||||
NameAttrs: []*sshFxpNameAttr{
|
||||
{
|
||||
Name: filename,
|
||||
LongName: filename,
|
||||
Attrs: emptyFileStat,
|
||||
},
|
||||
},
|
||||
}
|
||||
default:
|
||||
err = fmt.Errorf("unexpected method: %s", r.Method)
|
||||
return statusFromError(pkt.id(), err)
|
||||
}
|
||||
}
|
||||
|
||||
func readlink(readlinkFileLister ReadlinkFileLister, r *Request, pkt requestPacket) responsePacket {
|
||||
resolved, err := readlinkFileLister.Readlink(r.Filepath)
|
||||
if err != nil {
|
||||
return statusFromError(pkt.id(), err)
|
||||
}
|
||||
return &sshFxpNamePacket{
|
||||
ID: pkt.id(),
|
||||
NameAttrs: []*sshFxpNameAttr{
|
||||
{
|
||||
Name: resolved,
|
||||
LongName: resolved,
|
||||
Attrs: emptyFileStat,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// init attributes of request object from packet data
|
||||
func requestMethod(p requestPacket) (method string) {
|
||||
switch p.(type) {
|
||||
case *sshFxpReadPacket, *sshFxpWritePacket, *sshFxpOpenPacket:
|
||||
// set in open() above
|
||||
case *sshFxpOpendirPacket, *sshFxpReaddirPacket:
|
||||
// set in opendir() above
|
||||
case *sshFxpSetstatPacket, *sshFxpFsetstatPacket:
|
||||
method = "Setstat"
|
||||
case *sshFxpRenamePacket:
|
||||
method = "Rename"
|
||||
case *sshFxpSymlinkPacket:
|
||||
method = "Symlink"
|
||||
case *sshFxpRemovePacket:
|
||||
method = "Remove"
|
||||
case *sshFxpStatPacket, *sshFxpFstatPacket:
|
||||
method = "Stat"
|
||||
case *sshFxpLstatPacket:
|
||||
method = "Lstat"
|
||||
case *sshFxpRmdirPacket:
|
||||
method = "Rmdir"
|
||||
case *sshFxpReadlinkPacket:
|
||||
method = "Readlink"
|
||||
case *sshFxpMkdirPacket:
|
||||
method = "Mkdir"
|
||||
case *sshFxpExtendedPacketHardlink:
|
||||
method = "Link"
|
||||
}
|
||||
return method
|
||||
}
|
||||
13
vendor/github.com/pkg/sftp/request_windows.go
generated
vendored
Normal file
13
vendor/github.com/pkg/sftp/request_windows.go
generated
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
package sftp
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func fakeFileInfoSys() interface{} {
|
||||
return syscall.Win32FileAttributeData{}
|
||||
}
|
||||
|
||||
func testOsSys(sys interface{}) error {
|
||||
return nil
|
||||
}
|
||||
657
vendor/github.com/pkg/sftp/server.go
generated
vendored
Normal file
657
vendor/github.com/pkg/sftp/server.go
generated
vendored
Normal file
|
|
@ -0,0 +1,657 @@
|
|||
package sftp
|
||||
|
||||
// sftp server counterpart
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// SftpServerWorkerCount defines the number of workers for the SFTP server
|
||||
SftpServerWorkerCount = 8
|
||||
)
|
||||
|
||||
type file interface {
|
||||
Stat() (os.FileInfo, error)
|
||||
ReadAt(b []byte, off int64) (int, error)
|
||||
WriteAt(b []byte, off int64) (int, error)
|
||||
Readdir(int) ([]os.FileInfo, error)
|
||||
Name() string
|
||||
Truncate(int64) error
|
||||
Chmod(mode fs.FileMode) error
|
||||
Chown(uid, gid int) error
|
||||
Close() error
|
||||
}
|
||||
|
||||
// Server is an SSH File Transfer Protocol (sftp) server.
|
||||
// This is intended to provide the sftp subsystem to an ssh server daemon.
|
||||
// This implementation currently supports most of sftp server protocol version 3,
|
||||
// as specified at https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-02.txt
|
||||
type Server struct {
|
||||
*serverConn
|
||||
debugStream io.Writer
|
||||
readOnly bool
|
||||
pktMgr *packetManager
|
||||
openFiles map[string]file
|
||||
openFilesLock sync.RWMutex
|
||||
handleCount int
|
||||
workDir string
|
||||
winRoot bool
|
||||
maxTxPacket uint32
|
||||
}
|
||||
|
||||
func (svr *Server) nextHandle(f file) string {
|
||||
svr.openFilesLock.Lock()
|
||||
defer svr.openFilesLock.Unlock()
|
||||
svr.handleCount++
|
||||
handle := strconv.Itoa(svr.handleCount)
|
||||
svr.openFiles[handle] = f
|
||||
return handle
|
||||
}
|
||||
|
||||
func (svr *Server) closeHandle(handle string) error {
|
||||
svr.openFilesLock.Lock()
|
||||
defer svr.openFilesLock.Unlock()
|
||||
if f, ok := svr.openFiles[handle]; ok {
|
||||
delete(svr.openFiles, handle)
|
||||
return f.Close()
|
||||
}
|
||||
|
||||
return EBADF
|
||||
}
|
||||
|
||||
func (svr *Server) getHandle(handle string) (file, bool) {
|
||||
svr.openFilesLock.RLock()
|
||||
defer svr.openFilesLock.RUnlock()
|
||||
f, ok := svr.openFiles[handle]
|
||||
return f, ok
|
||||
}
|
||||
|
||||
type serverRespondablePacket interface {
|
||||
encoding.BinaryUnmarshaler
|
||||
id() uint32
|
||||
respond(svr *Server) responsePacket
|
||||
}
|
||||
|
||||
// NewServer creates a new Server instance around the provided streams, serving
|
||||
// content from the root of the filesystem. Optionally, ServerOption
|
||||
// functions may be specified to further configure the Server.
|
||||
//
|
||||
// A subsequent call to Serve() is required to begin serving files over SFTP.
|
||||
func NewServer(rwc io.ReadWriteCloser, options ...ServerOption) (*Server, error) {
|
||||
svrConn := &serverConn{
|
||||
conn: conn{
|
||||
Reader: rwc,
|
||||
WriteCloser: rwc,
|
||||
},
|
||||
}
|
||||
s := &Server{
|
||||
serverConn: svrConn,
|
||||
debugStream: ioutil.Discard,
|
||||
pktMgr: newPktMgr(svrConn),
|
||||
openFiles: make(map[string]file),
|
||||
maxTxPacket: defaultMaxTxPacket,
|
||||
}
|
||||
|
||||
for _, o := range options {
|
||||
if err := o(s); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// A ServerOption is a function which applies configuration to a Server.
|
||||
type ServerOption func(*Server) error
|
||||
|
||||
// WithDebug enables Server debugging output to the supplied io.Writer.
|
||||
func WithDebug(w io.Writer) ServerOption {
|
||||
return func(s *Server) error {
|
||||
s.debugStream = w
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// ReadOnly configures a Server to serve files in read-only mode.
|
||||
func ReadOnly() ServerOption {
|
||||
return func(s *Server) error {
|
||||
s.readOnly = true
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WindowsRootEnumeratesDrives configures a Server to serve a virtual '/' for windows that lists all drives
|
||||
func WindowsRootEnumeratesDrives() ServerOption {
|
||||
return func(s *Server) error {
|
||||
s.winRoot = true
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithAllocator enable the allocator.
|
||||
// After processing a packet we keep in memory the allocated slices
|
||||
// and we reuse them for new packets.
|
||||
// The allocator is experimental
|
||||
func WithAllocator() ServerOption {
|
||||
return func(s *Server) error {
|
||||
alloc := newAllocator()
|
||||
s.pktMgr.alloc = alloc
|
||||
s.conn.alloc = alloc
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithServerWorkingDirectory sets a working directory to use as base
|
||||
// for relative paths.
|
||||
// If unset the default is current working directory (os.Getwd).
|
||||
func WithServerWorkingDirectory(workDir string) ServerOption {
|
||||
return func(s *Server) error {
|
||||
s.workDir = cleanPath(workDir)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithMaxTxPacket sets the maximum size of the payload returned to the client,
|
||||
// measured in bytes. The default value is 32768 bytes, and this option
|
||||
// can only be used to increase it. Setting this option to a larger value
|
||||
// should be safe, because the client decides the size of the requested payload.
|
||||
//
|
||||
// The default maximum packet size is 32768 bytes.
|
||||
func WithMaxTxPacket(size uint32) ServerOption {
|
||||
return func(s *Server) error {
|
||||
if size < defaultMaxTxPacket {
|
||||
return errors.New("size must be greater than or equal to 32768")
|
||||
}
|
||||
|
||||
s.maxTxPacket = size
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
type rxPacket struct {
|
||||
pktType fxp
|
||||
pktBytes []byte
|
||||
}
|
||||
|
||||
// Up to N parallel servers
|
||||
func (svr *Server) sftpServerWorker(pktChan chan orderedRequest) error {
|
||||
for pkt := range pktChan {
|
||||
// readonly checks
|
||||
readonly := true
|
||||
switch pkt := pkt.requestPacket.(type) {
|
||||
case notReadOnly:
|
||||
readonly = false
|
||||
case *sshFxpOpenPacket:
|
||||
readonly = pkt.readonly()
|
||||
case *sshFxpExtendedPacket:
|
||||
readonly = pkt.readonly()
|
||||
}
|
||||
|
||||
// If server is operating read-only and a write operation is requested,
|
||||
// return permission denied
|
||||
if !readonly && svr.readOnly {
|
||||
svr.pktMgr.readyPacket(
|
||||
svr.pktMgr.newOrderedResponse(statusFromError(pkt.id(), syscall.EPERM), pkt.orderID()),
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
||||
if err := handlePacket(svr, pkt); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func handlePacket(s *Server, p orderedRequest) error {
|
||||
var rpkt responsePacket
|
||||
orderID := p.orderID()
|
||||
switch p := p.requestPacket.(type) {
|
||||
case *sshFxInitPacket:
|
||||
rpkt = &sshFxVersionPacket{
|
||||
Version: sftpProtocolVersion,
|
||||
Extensions: sftpExtensions,
|
||||
}
|
||||
case *sshFxpStatPacket:
|
||||
// stat the requested file
|
||||
info, err := os.Stat(s.toLocalPath(p.Path))
|
||||
rpkt = &sshFxpStatResponse{
|
||||
ID: p.ID,
|
||||
info: info,
|
||||
}
|
||||
if err != nil {
|
||||
rpkt = statusFromError(p.ID, err)
|
||||
}
|
||||
case *sshFxpLstatPacket:
|
||||
// stat the requested file
|
||||
info, err := s.lstat(s.toLocalPath(p.Path))
|
||||
rpkt = &sshFxpStatResponse{
|
||||
ID: p.ID,
|
||||
info: info,
|
||||
}
|
||||
if err != nil {
|
||||
rpkt = statusFromError(p.ID, err)
|
||||
}
|
||||
case *sshFxpFstatPacket:
|
||||
f, ok := s.getHandle(p.Handle)
|
||||
var err error = EBADF
|
||||
var info os.FileInfo
|
||||
if ok {
|
||||
info, err = f.Stat()
|
||||
rpkt = &sshFxpStatResponse{
|
||||
ID: p.ID,
|
||||
info: info,
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
rpkt = statusFromError(p.ID, err)
|
||||
}
|
||||
case *sshFxpMkdirPacket:
|
||||
// TODO FIXME: ignore flags field
|
||||
err := os.Mkdir(s.toLocalPath(p.Path), 0o755)
|
||||
rpkt = statusFromError(p.ID, err)
|
||||
case *sshFxpRmdirPacket:
|
||||
err := os.Remove(s.toLocalPath(p.Path))
|
||||
rpkt = statusFromError(p.ID, err)
|
||||
case *sshFxpRemovePacket:
|
||||
err := os.Remove(s.toLocalPath(p.Filename))
|
||||
rpkt = statusFromError(p.ID, err)
|
||||
case *sshFxpRenamePacket:
|
||||
err := os.Rename(s.toLocalPath(p.Oldpath), s.toLocalPath(p.Newpath))
|
||||
rpkt = statusFromError(p.ID, err)
|
||||
case *sshFxpSymlinkPacket:
|
||||
err := os.Symlink(s.toLocalPath(p.Targetpath), s.toLocalPath(p.Linkpath))
|
||||
rpkt = statusFromError(p.ID, err)
|
||||
case *sshFxpClosePacket:
|
||||
rpkt = statusFromError(p.ID, s.closeHandle(p.Handle))
|
||||
case *sshFxpReadlinkPacket:
|
||||
f, err := os.Readlink(s.toLocalPath(p.Path))
|
||||
rpkt = &sshFxpNamePacket{
|
||||
ID: p.ID,
|
||||
NameAttrs: []*sshFxpNameAttr{
|
||||
{
|
||||
Name: f,
|
||||
LongName: f,
|
||||
Attrs: emptyFileStat,
|
||||
},
|
||||
},
|
||||
}
|
||||
if err != nil {
|
||||
rpkt = statusFromError(p.ID, err)
|
||||
}
|
||||
case *sshFxpRealpathPacket:
|
||||
f, err := filepath.Abs(s.toLocalPath(p.Path))
|
||||
f = cleanPath(f)
|
||||
rpkt = &sshFxpNamePacket{
|
||||
ID: p.ID,
|
||||
NameAttrs: []*sshFxpNameAttr{
|
||||
{
|
||||
Name: f,
|
||||
LongName: f,
|
||||
Attrs: emptyFileStat,
|
||||
},
|
||||
},
|
||||
}
|
||||
if err != nil {
|
||||
rpkt = statusFromError(p.ID, err)
|
||||
}
|
||||
case *sshFxpOpendirPacket:
|
||||
lp := s.toLocalPath(p.Path)
|
||||
|
||||
if stat, err := s.stat(lp); err != nil {
|
||||
rpkt = statusFromError(p.ID, err)
|
||||
} else if !stat.IsDir() {
|
||||
rpkt = statusFromError(p.ID, &os.PathError{
|
||||
Path: lp, Err: syscall.ENOTDIR,
|
||||
})
|
||||
} else {
|
||||
rpkt = (&sshFxpOpenPacket{
|
||||
ID: p.ID,
|
||||
Path: p.Path,
|
||||
Pflags: sshFxfRead,
|
||||
}).respond(s)
|
||||
}
|
||||
case *sshFxpReadPacket:
|
||||
var err error = EBADF
|
||||
f, ok := s.getHandle(p.Handle)
|
||||
if ok {
|
||||
err = nil
|
||||
data := p.getDataSlice(s.pktMgr.alloc, orderID, s.maxTxPacket)
|
||||
n, _err := f.ReadAt(data, int64(p.Offset))
|
||||
if _err != nil && (_err != io.EOF || n == 0) {
|
||||
err = _err
|
||||
}
|
||||
rpkt = &sshFxpDataPacket{
|
||||
ID: p.ID,
|
||||
Length: uint32(n),
|
||||
Data: data[:n],
|
||||
// do not use data[:n:n] here to clamp the capacity, we allocated extra capacity above to avoid reallocations
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
rpkt = statusFromError(p.ID, err)
|
||||
}
|
||||
|
||||
case *sshFxpWritePacket:
|
||||
f, ok := s.getHandle(p.Handle)
|
||||
var err error = EBADF
|
||||
if ok {
|
||||
_, err = f.WriteAt(p.Data, int64(p.Offset))
|
||||
}
|
||||
rpkt = statusFromError(p.ID, err)
|
||||
case *sshFxpExtendedPacket:
|
||||
if p.SpecificPacket == nil {
|
||||
rpkt = statusFromError(p.ID, ErrSSHFxOpUnsupported)
|
||||
} else {
|
||||
rpkt = p.respond(s)
|
||||
}
|
||||
case serverRespondablePacket:
|
||||
rpkt = p.respond(s)
|
||||
default:
|
||||
return fmt.Errorf("unexpected packet type %T", p)
|
||||
}
|
||||
|
||||
s.pktMgr.readyPacket(s.pktMgr.newOrderedResponse(rpkt, orderID))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Serve serves SFTP connections until the streams stop or the SFTP subsystem
|
||||
// is stopped. It returns nil if the server exits cleanly.
|
||||
func (svr *Server) Serve() error {
|
||||
defer func() {
|
||||
if svr.pktMgr.alloc != nil {
|
||||
svr.pktMgr.alloc.Free()
|
||||
}
|
||||
}()
|
||||
var wg sync.WaitGroup
|
||||
runWorker := func(ch chan orderedRequest) {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
if err := svr.sftpServerWorker(ch); err != nil {
|
||||
svr.conn.Close() // shuts down recvPacket
|
||||
}
|
||||
}()
|
||||
}
|
||||
pktChan := svr.pktMgr.workerChan(runWorker)
|
||||
|
||||
var err error
|
||||
var pkt requestPacket
|
||||
var pktType fxp
|
||||
var pktBytes []byte
|
||||
for {
|
||||
pktType, pktBytes, err = svr.serverConn.recvPacket(svr.pktMgr.getNextOrderID())
|
||||
if err != nil {
|
||||
// Check whether the connection terminated cleanly in-between packets.
|
||||
if err == io.EOF {
|
||||
err = nil
|
||||
}
|
||||
// we don't care about releasing allocated pages here, the server will quit and the allocator freed
|
||||
break
|
||||
}
|
||||
|
||||
pkt, err = makePacket(rxPacket{pktType, pktBytes})
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, errUnknownExtendedPacket):
|
||||
//if err := svr.serverConn.sendError(pkt, ErrSshFxOpUnsupported); err != nil {
|
||||
// debug("failed to send err packet: %v", err)
|
||||
// svr.conn.Close() // shuts down recvPacket
|
||||
// break
|
||||
//}
|
||||
default:
|
||||
debug("makePacket err: %v", err)
|
||||
svr.conn.Close() // shuts down recvPacket
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
pktChan <- svr.pktMgr.newOrderedRequest(pkt)
|
||||
}
|
||||
|
||||
close(pktChan) // shuts down sftpServerWorkers
|
||||
wg.Wait() // wait for all workers to exit
|
||||
|
||||
// close any still-open files
|
||||
for handle, file := range svr.openFiles {
|
||||
fmt.Fprintf(svr.debugStream, "sftp server file with handle %q left open: %v\n", handle, file.Name())
|
||||
file.Close()
|
||||
}
|
||||
return err // error from recvPacket
|
||||
}
|
||||
|
||||
type ider interface {
|
||||
id() uint32
|
||||
}
|
||||
|
||||
// The init packet has no ID, so we just return a zero-value ID
|
||||
func (p *sshFxInitPacket) id() uint32 { return 0 }
|
||||
|
||||
type sshFxpStatResponse struct {
|
||||
ID uint32
|
||||
info os.FileInfo
|
||||
}
|
||||
|
||||
func (p *sshFxpStatResponse) marshalPacket() ([]byte, []byte, error) {
|
||||
l := 4 + 1 + 4 // uint32(length) + byte(type) + uint32(id)
|
||||
|
||||
b := make([]byte, 4, l)
|
||||
b = append(b, sshFxpAttrs)
|
||||
b = marshalUint32(b, p.ID)
|
||||
|
||||
var payload []byte
|
||||
payload = marshalFileInfo(payload, p.info)
|
||||
|
||||
return b, payload, nil
|
||||
}
|
||||
|
||||
func (p *sshFxpStatResponse) MarshalBinary() ([]byte, error) {
|
||||
header, payload, err := p.marshalPacket()
|
||||
return append(header, payload...), err
|
||||
}
|
||||
|
||||
var emptyFileStat = []interface{}{uint32(0)}
|
||||
|
||||
func (p *sshFxpOpenPacket) readonly() bool {
|
||||
return !p.hasPflags(sshFxfWrite)
|
||||
}
|
||||
|
||||
func (p *sshFxpOpenPacket) hasPflags(flags ...uint32) bool {
|
||||
for _, f := range flags {
|
||||
if p.Pflags&f == 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (p *sshFxpOpenPacket) respond(svr *Server) responsePacket {
|
||||
var osFlags int
|
||||
if p.hasPflags(sshFxfRead, sshFxfWrite) {
|
||||
osFlags |= os.O_RDWR
|
||||
} else if p.hasPflags(sshFxfWrite) {
|
||||
osFlags |= os.O_WRONLY
|
||||
} else if p.hasPflags(sshFxfRead) {
|
||||
osFlags |= os.O_RDONLY
|
||||
} else {
|
||||
// how are they opening?
|
||||
return statusFromError(p.ID, syscall.EINVAL)
|
||||
}
|
||||
|
||||
// Don't use O_APPEND flag as it conflicts with WriteAt.
|
||||
// The sshFxfAppend flag is a no-op here as the client sends the offsets.
|
||||
|
||||
if p.hasPflags(sshFxfCreat) {
|
||||
osFlags |= os.O_CREATE
|
||||
}
|
||||
if p.hasPflags(sshFxfTrunc) {
|
||||
osFlags |= os.O_TRUNC
|
||||
}
|
||||
if p.hasPflags(sshFxfExcl) {
|
||||
osFlags |= os.O_EXCL
|
||||
}
|
||||
|
||||
mode := os.FileMode(0o644)
|
||||
// Like OpenSSH, we only handle permissions here, and only when the file is being created.
|
||||
// Otherwise, the permissions are ignored.
|
||||
if p.Flags&sshFileXferAttrPermissions != 0 {
|
||||
fs, err := p.unmarshalFileStat(p.Flags)
|
||||
if err != nil {
|
||||
return statusFromError(p.ID, err)
|
||||
}
|
||||
mode = fs.FileMode() & os.ModePerm
|
||||
}
|
||||
|
||||
f, err := svr.openfile(svr.toLocalPath(p.Path), osFlags, mode)
|
||||
if err != nil {
|
||||
return statusFromError(p.ID, err)
|
||||
}
|
||||
|
||||
handle := svr.nextHandle(f)
|
||||
return &sshFxpHandlePacket{ID: p.ID, Handle: handle}
|
||||
}
|
||||
|
||||
func (p *sshFxpReaddirPacket) respond(svr *Server) responsePacket {
|
||||
f, ok := svr.getHandle(p.Handle)
|
||||
if !ok {
|
||||
return statusFromError(p.ID, EBADF)
|
||||
}
|
||||
|
||||
dirents, err := f.Readdir(128)
|
||||
if err != nil {
|
||||
return statusFromError(p.ID, err)
|
||||
}
|
||||
|
||||
idLookup := osIDLookup{}
|
||||
|
||||
ret := &sshFxpNamePacket{ID: p.ID}
|
||||
for _, dirent := range dirents {
|
||||
ret.NameAttrs = append(ret.NameAttrs, &sshFxpNameAttr{
|
||||
Name: dirent.Name(),
|
||||
LongName: runLs(idLookup, dirent),
|
||||
Attrs: []interface{}{dirent},
|
||||
})
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (p *sshFxpSetstatPacket) respond(svr *Server) responsePacket {
|
||||
path := svr.toLocalPath(p.Path)
|
||||
|
||||
debug("setstat name %q", path)
|
||||
|
||||
fs, err := p.unmarshalFileStat(p.Flags)
|
||||
|
||||
if err == nil && (p.Flags&sshFileXferAttrSize) != 0 {
|
||||
err = os.Truncate(path, int64(fs.Size))
|
||||
}
|
||||
if err == nil && (p.Flags&sshFileXferAttrPermissions) != 0 {
|
||||
err = os.Chmod(path, fs.FileMode())
|
||||
}
|
||||
if err == nil && (p.Flags&sshFileXferAttrUIDGID) != 0 {
|
||||
err = os.Chown(path, int(fs.UID), int(fs.GID))
|
||||
}
|
||||
if err == nil && (p.Flags&sshFileXferAttrACmodTime) != 0 {
|
||||
err = os.Chtimes(path, fs.AccessTime(), fs.ModTime())
|
||||
}
|
||||
|
||||
return statusFromError(p.ID, err)
|
||||
}
|
||||
|
||||
func (p *sshFxpFsetstatPacket) respond(svr *Server) responsePacket {
|
||||
f, ok := svr.getHandle(p.Handle)
|
||||
if !ok {
|
||||
return statusFromError(p.ID, EBADF)
|
||||
}
|
||||
|
||||
path := f.Name()
|
||||
|
||||
debug("fsetstat name %q", path)
|
||||
|
||||
fs, err := p.unmarshalFileStat(p.Flags)
|
||||
|
||||
if err == nil && (p.Flags&sshFileXferAttrSize) != 0 {
|
||||
err = f.Truncate(int64(fs.Size))
|
||||
}
|
||||
if err == nil && (p.Flags&sshFileXferAttrPermissions) != 0 {
|
||||
err = f.Chmod(fs.FileMode())
|
||||
}
|
||||
if err == nil && (p.Flags&sshFileXferAttrUIDGID) != 0 {
|
||||
err = f.Chown(int(fs.UID), int(fs.GID))
|
||||
}
|
||||
if err == nil && (p.Flags&sshFileXferAttrACmodTime) != 0 {
|
||||
type chtimer interface {
|
||||
Chtimes(atime, mtime time.Time) error
|
||||
}
|
||||
|
||||
switch f := interface{}(f).(type) {
|
||||
case chtimer:
|
||||
// future-compatible, for when/if *os.File supports Chtimes.
|
||||
err = f.Chtimes(fs.AccessTime(), fs.ModTime())
|
||||
default:
|
||||
err = os.Chtimes(path, fs.AccessTime(), fs.ModTime())
|
||||
}
|
||||
}
|
||||
|
||||
return statusFromError(p.ID, err)
|
||||
}
|
||||
|
||||
func statusFromError(id uint32, err error) *sshFxpStatusPacket {
|
||||
ret := &sshFxpStatusPacket{
|
||||
ID: id,
|
||||
StatusError: StatusError{
|
||||
// sshFXOk = 0
|
||||
// sshFXEOF = 1
|
||||
// sshFXNoSuchFile = 2 ENOENT
|
||||
// sshFXPermissionDenied = 3
|
||||
// sshFXFailure = 4
|
||||
// sshFXBadMessage = 5
|
||||
// sshFXNoConnection = 6
|
||||
// sshFXConnectionLost = 7
|
||||
// sshFXOPUnsupported = 8
|
||||
Code: sshFxOk,
|
||||
},
|
||||
}
|
||||
if err == nil {
|
||||
return ret
|
||||
}
|
||||
|
||||
debug("statusFromError: error is %T %#v", err, err)
|
||||
ret.StatusError.Code = sshFxFailure
|
||||
ret.StatusError.msg = err.Error()
|
||||
|
||||
if os.IsNotExist(err) {
|
||||
ret.StatusError.Code = sshFxNoSuchFile
|
||||
return ret
|
||||
}
|
||||
if code, ok := translateSyscallError(err); ok {
|
||||
ret.StatusError.Code = code
|
||||
return ret
|
||||
}
|
||||
|
||||
if errors.Is(err, io.EOF) {
|
||||
ret.StatusError.Code = sshFxEOF
|
||||
return ret
|
||||
}
|
||||
|
||||
var e fxerr
|
||||
if errors.As(err, &e) {
|
||||
ret.StatusError.Code = uint32(e)
|
||||
return ret
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
27
vendor/github.com/pkg/sftp/server_plan9.go
generated
vendored
Normal file
27
vendor/github.com/pkg/sftp/server_plan9.go
generated
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
package sftp
|
||||
|
||||
import (
|
||||
"path"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
func (s *Server) toLocalPath(p string) string {
|
||||
if s.workDir != "" && !path.IsAbs(p) {
|
||||
p = path.Join(s.workDir, p)
|
||||
}
|
||||
|
||||
lp := filepath.FromSlash(p)
|
||||
|
||||
if path.IsAbs(p) {
|
||||
tmp := lp[1:]
|
||||
|
||||
if filepath.IsAbs(tmp) {
|
||||
// If the FromSlash without any starting slashes is absolute,
|
||||
// then we have a filepath encoded with a prefix '/'.
|
||||
// e.g. "/#s/boot" to "#s/boot"
|
||||
return tmp
|
||||
}
|
||||
}
|
||||
|
||||
return lp
|
||||
}
|
||||
21
vendor/github.com/pkg/sftp/server_posix.go
generated
vendored
Normal file
21
vendor/github.com/pkg/sftp/server_posix.go
generated
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package sftp
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"os"
|
||||
)
|
||||
|
||||
func (s *Server) openfile(path string, flag int, mode fs.FileMode) (file, error) {
|
||||
return os.OpenFile(path, flag, mode)
|
||||
}
|
||||
|
||||
func (s *Server) lstat(name string) (os.FileInfo, error) {
|
||||
return os.Lstat(name)
|
||||
}
|
||||
|
||||
func (s *Server) stat(name string) (os.FileInfo, error) {
|
||||
return os.Stat(name)
|
||||
}
|
||||
21
vendor/github.com/pkg/sftp/server_statvfs_darwin.go
generated
vendored
Normal file
21
vendor/github.com/pkg/sftp/server_statvfs_darwin.go
generated
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
package sftp
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func statvfsFromStatfst(stat *syscall.Statfs_t) (*StatVFS, error) {
|
||||
return &StatVFS{
|
||||
Bsize: uint64(stat.Bsize),
|
||||
Frsize: uint64(stat.Bsize), // fragment size is a linux thing; use block size here
|
||||
Blocks: stat.Blocks,
|
||||
Bfree: stat.Bfree,
|
||||
Bavail: stat.Bavail,
|
||||
Files: stat.Files,
|
||||
Ffree: stat.Ffree,
|
||||
Favail: stat.Ffree, // not sure how to calculate Favail
|
||||
Fsid: uint64(uint64(stat.Fsid.Val[1])<<32 | uint64(stat.Fsid.Val[0])), // endianness?
|
||||
Flag: uint64(stat.Flags), // assuming POSIX?
|
||||
Namemax: 1024, // man 2 statfs shows: #define MAXPATHLEN 1024
|
||||
}, nil
|
||||
}
|
||||
30
vendor/github.com/pkg/sftp/server_statvfs_impl.go
generated
vendored
Normal file
30
vendor/github.com/pkg/sftp/server_statvfs_impl.go
generated
vendored
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
//go:build darwin || linux
|
||||
// +build darwin linux
|
||||
|
||||
// fill in statvfs structure with OS specific values
|
||||
// Statfs_t is different per-kernel, and only exists on some unixes (not Solaris for instance)
|
||||
|
||||
package sftp
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func (p *sshFxpExtendedPacketStatVFS) respond(svr *Server) responsePacket {
|
||||
retPkt, err := getStatVFSForPath(p.Path)
|
||||
if err != nil {
|
||||
return statusFromError(p.ID, err)
|
||||
}
|
||||
retPkt.ID = p.ID
|
||||
|
||||
return retPkt
|
||||
}
|
||||
|
||||
func getStatVFSForPath(name string) (*StatVFS, error) {
|
||||
var stat syscall.Statfs_t
|
||||
if err := syscall.Statfs(name, &stat); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return statvfsFromStatfst(&stat)
|
||||
}
|
||||
23
vendor/github.com/pkg/sftp/server_statvfs_linux.go
generated
vendored
Normal file
23
vendor/github.com/pkg/sftp/server_statvfs_linux.go
generated
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
//go:build linux
|
||||
// +build linux
|
||||
|
||||
package sftp
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func statvfsFromStatfst(stat *syscall.Statfs_t) (*StatVFS, error) {
|
||||
return &StatVFS{
|
||||
Bsize: uint64(stat.Bsize),
|
||||
Frsize: uint64(stat.Frsize),
|
||||
Blocks: stat.Blocks,
|
||||
Bfree: stat.Bfree,
|
||||
Bavail: stat.Bavail,
|
||||
Files: stat.Files,
|
||||
Ffree: stat.Ffree,
|
||||
Favail: stat.Ffree, // not sure how to calculate Favail
|
||||
Flag: uint64(stat.Flags), // assuming POSIX?
|
||||
Namemax: uint64(stat.Namelen),
|
||||
}, nil
|
||||
}
|
||||
13
vendor/github.com/pkg/sftp/server_statvfs_plan9.go
generated
vendored
Normal file
13
vendor/github.com/pkg/sftp/server_statvfs_plan9.go
generated
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
package sftp
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func (p *sshFxpExtendedPacketStatVFS) respond(svr *Server) responsePacket {
|
||||
return statusFromError(p.ID, syscall.EPLAN9)
|
||||
}
|
||||
|
||||
func getStatVFSForPath(name string) (*StatVFS, error) {
|
||||
return nil, syscall.EPLAN9
|
||||
}
|
||||
16
vendor/github.com/pkg/sftp/server_statvfs_stubs.go
generated
vendored
Normal file
16
vendor/github.com/pkg/sftp/server_statvfs_stubs.go
generated
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
//go:build !darwin && !linux && !plan9
|
||||
// +build !darwin,!linux,!plan9
|
||||
|
||||
package sftp
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func (p *sshFxpExtendedPacketStatVFS) respond(svr *Server) responsePacket {
|
||||
return statusFromError(p.ID, syscall.ENOTSUP)
|
||||
}
|
||||
|
||||
func getStatVFSForPath(name string) (*StatVFS, error) {
|
||||
return nil, syscall.ENOTSUP
|
||||
}
|
||||
16
vendor/github.com/pkg/sftp/server_unix.go
generated
vendored
Normal file
16
vendor/github.com/pkg/sftp/server_unix.go
generated
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
//go:build !windows && !plan9
|
||||
// +build !windows,!plan9
|
||||
|
||||
package sftp
|
||||
|
||||
import (
|
||||
"path"
|
||||
)
|
||||
|
||||
func (s *Server) toLocalPath(p string) string {
|
||||
if s.workDir != "" && !path.IsAbs(p) {
|
||||
p = path.Join(s.workDir, p)
|
||||
}
|
||||
|
||||
return p
|
||||
}
|
||||
193
vendor/github.com/pkg/sftp/server_windows.go
generated
vendored
Normal file
193
vendor/github.com/pkg/sftp/server_windows.go
generated
vendored
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
package sftp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
func (s *Server) toLocalPath(p string) string {
|
||||
if s.workDir != "" && !path.IsAbs(p) {
|
||||
p = path.Join(s.workDir, p)
|
||||
}
|
||||
|
||||
lp := filepath.FromSlash(p)
|
||||
|
||||
if path.IsAbs(p) { // starts with '/'
|
||||
if len(p) == 1 && s.winRoot {
|
||||
return `\\.\` // for openfile
|
||||
}
|
||||
|
||||
tmp := lp
|
||||
for len(tmp) > 0 && tmp[0] == '\\' {
|
||||
tmp = tmp[1:]
|
||||
}
|
||||
|
||||
if filepath.IsAbs(tmp) {
|
||||
// If the FromSlash without any starting slashes is absolute,
|
||||
// then we have a filepath encoded with a prefix '/'.
|
||||
// e.g. "/C:/Windows" to "C:\\Windows"
|
||||
return tmp
|
||||
}
|
||||
|
||||
tmp += "\\"
|
||||
|
||||
if filepath.IsAbs(tmp) {
|
||||
// If the FromSlash without any starting slashes but with extra end slash is absolute,
|
||||
// then we have a filepath encoded with a prefix '/' and a dropped '/' at the end.
|
||||
// e.g. "/C:" to "C:\\"
|
||||
return tmp
|
||||
}
|
||||
|
||||
if s.winRoot {
|
||||
// Make it so that "/Windows" is not found, and "/c:/Windows" has to be used
|
||||
return `\\.\` + tmp
|
||||
}
|
||||
}
|
||||
|
||||
return lp
|
||||
}
|
||||
|
||||
func bitsToDrives(bitmap uint32) []string {
|
||||
var drive rune = 'a'
|
||||
var drives []string
|
||||
|
||||
for bitmap != 0 && drive <= 'z' {
|
||||
if bitmap&1 == 1 {
|
||||
drives = append(drives, string(drive)+":")
|
||||
}
|
||||
drive++
|
||||
bitmap >>= 1
|
||||
}
|
||||
|
||||
return drives
|
||||
}
|
||||
|
||||
func getDrives() ([]string, error) {
|
||||
mask, err := windows.GetLogicalDrives()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("GetLogicalDrives: %w", err)
|
||||
}
|
||||
return bitsToDrives(mask), nil
|
||||
}
|
||||
|
||||
type driveInfo struct {
|
||||
fs.FileInfo
|
||||
name string
|
||||
}
|
||||
|
||||
func (i *driveInfo) Name() string {
|
||||
return i.name // since the Name() returned from a os.Stat("C:\\") is "\\"
|
||||
}
|
||||
|
||||
type winRoot struct {
|
||||
drives []string
|
||||
}
|
||||
|
||||
func newWinRoot() (*winRoot, error) {
|
||||
drives, err := getDrives()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &winRoot{
|
||||
drives: drives,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (f *winRoot) Readdir(n int) ([]os.FileInfo, error) {
|
||||
drives := f.drives
|
||||
if n > 0 && len(drives) > n {
|
||||
drives = drives[:n]
|
||||
}
|
||||
f.drives = f.drives[len(drives):]
|
||||
if len(drives) == 0 {
|
||||
return nil, io.EOF
|
||||
}
|
||||
|
||||
var infos []os.FileInfo
|
||||
for _, drive := range drives {
|
||||
fi, err := os.Stat(drive + `\`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
di := &driveInfo{
|
||||
FileInfo: fi,
|
||||
name: drive,
|
||||
}
|
||||
infos = append(infos, di)
|
||||
}
|
||||
|
||||
return infos, nil
|
||||
}
|
||||
|
||||
func (f *winRoot) Stat() (os.FileInfo, error) {
|
||||
return rootFileInfo, nil
|
||||
}
|
||||
func (f *winRoot) ReadAt(b []byte, off int64) (int, error) {
|
||||
return 0, os.ErrPermission
|
||||
}
|
||||
func (f *winRoot) WriteAt(b []byte, off int64) (int, error) {
|
||||
return 0, os.ErrPermission
|
||||
}
|
||||
func (f *winRoot) Name() string {
|
||||
return "/"
|
||||
}
|
||||
func (f *winRoot) Truncate(int64) error {
|
||||
return os.ErrPermission
|
||||
}
|
||||
func (f *winRoot) Chmod(mode fs.FileMode) error {
|
||||
return os.ErrPermission
|
||||
}
|
||||
func (f *winRoot) Chown(uid, gid int) error {
|
||||
return os.ErrPermission
|
||||
}
|
||||
func (f *winRoot) Close() error {
|
||||
f.drives = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) openfile(path string, flag int, mode fs.FileMode) (file, error) {
|
||||
if path == `\\.\` && s.winRoot {
|
||||
return newWinRoot()
|
||||
}
|
||||
return os.OpenFile(path, flag, mode)
|
||||
}
|
||||
|
||||
type winRootFileInfo struct {
|
||||
name string
|
||||
modTime time.Time
|
||||
}
|
||||
|
||||
func (w *winRootFileInfo) Name() string { return w.name }
|
||||
func (w *winRootFileInfo) Size() int64 { return 0 }
|
||||
func (w *winRootFileInfo) Mode() fs.FileMode { return fs.ModeDir | 0555 } // read+execute for all
|
||||
func (w *winRootFileInfo) ModTime() time.Time { return w.modTime }
|
||||
func (w *winRootFileInfo) IsDir() bool { return true }
|
||||
func (w *winRootFileInfo) Sys() interface{} { return nil }
|
||||
|
||||
// Create a new root FileInfo
|
||||
var rootFileInfo = &winRootFileInfo{
|
||||
name: "/",
|
||||
modTime: time.Now(),
|
||||
}
|
||||
|
||||
func (s *Server) lstat(name string) (os.FileInfo, error) {
|
||||
if name == `\\.\` && s.winRoot {
|
||||
return rootFileInfo, nil
|
||||
}
|
||||
return os.Lstat(name)
|
||||
}
|
||||
|
||||
func (s *Server) stat(name string) (os.FileInfo, error) {
|
||||
if name == `\\.\` && s.winRoot {
|
||||
return rootFileInfo, nil
|
||||
}
|
||||
return os.Stat(name)
|
||||
}
|
||||
258
vendor/github.com/pkg/sftp/sftp.go
generated
vendored
Normal file
258
vendor/github.com/pkg/sftp/sftp.go
generated
vendored
Normal file
|
|
@ -0,0 +1,258 @@
|
|||
// Package sftp implements the SSH File Transfer Protocol as described in
|
||||
// https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-02.txt
|
||||
package sftp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
const (
|
||||
sshFxpInit = 1
|
||||
sshFxpVersion = 2
|
||||
sshFxpOpen = 3
|
||||
sshFxpClose = 4
|
||||
sshFxpRead = 5
|
||||
sshFxpWrite = 6
|
||||
sshFxpLstat = 7
|
||||
sshFxpFstat = 8
|
||||
sshFxpSetstat = 9
|
||||
sshFxpFsetstat = 10
|
||||
sshFxpOpendir = 11
|
||||
sshFxpReaddir = 12
|
||||
sshFxpRemove = 13
|
||||
sshFxpMkdir = 14
|
||||
sshFxpRmdir = 15
|
||||
sshFxpRealpath = 16
|
||||
sshFxpStat = 17
|
||||
sshFxpRename = 18
|
||||
sshFxpReadlink = 19
|
||||
sshFxpSymlink = 20
|
||||
sshFxpStatus = 101
|
||||
sshFxpHandle = 102
|
||||
sshFxpData = 103
|
||||
sshFxpName = 104
|
||||
sshFxpAttrs = 105
|
||||
sshFxpExtended = 200
|
||||
sshFxpExtendedReply = 201
|
||||
)
|
||||
|
||||
const (
|
||||
sshFxOk = 0
|
||||
sshFxEOF = 1
|
||||
sshFxNoSuchFile = 2
|
||||
sshFxPermissionDenied = 3
|
||||
sshFxFailure = 4
|
||||
sshFxBadMessage = 5
|
||||
sshFxNoConnection = 6
|
||||
sshFxConnectionLost = 7
|
||||
sshFxOPUnsupported = 8
|
||||
|
||||
// see draft-ietf-secsh-filexfer-13
|
||||
// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-9.1
|
||||
sshFxInvalidHandle = 9
|
||||
sshFxNoSuchPath = 10
|
||||
sshFxFileAlreadyExists = 11
|
||||
sshFxWriteProtect = 12
|
||||
sshFxNoMedia = 13
|
||||
sshFxNoSpaceOnFilesystem = 14
|
||||
sshFxQuotaExceeded = 15
|
||||
sshFxUnknownPrincipal = 16
|
||||
sshFxLockConflict = 17
|
||||
sshFxDirNotEmpty = 18
|
||||
sshFxNotADirectory = 19
|
||||
sshFxInvalidFilename = 20
|
||||
sshFxLinkLoop = 21
|
||||
sshFxCannotDelete = 22
|
||||
sshFxInvalidParameter = 23
|
||||
sshFxFileIsADirectory = 24
|
||||
sshFxByteRangeLockConflict = 25
|
||||
sshFxByteRangeLockRefused = 26
|
||||
sshFxDeletePending = 27
|
||||
sshFxFileCorrupt = 28
|
||||
sshFxOwnerInvalid = 29
|
||||
sshFxGroupInvalid = 30
|
||||
sshFxNoMatchingByteRangeLock = 31
|
||||
)
|
||||
|
||||
const (
|
||||
sshFxfRead = 0x00000001
|
||||
sshFxfWrite = 0x00000002
|
||||
sshFxfAppend = 0x00000004
|
||||
sshFxfCreat = 0x00000008
|
||||
sshFxfTrunc = 0x00000010
|
||||
sshFxfExcl = 0x00000020
|
||||
)
|
||||
|
||||
var (
|
||||
// supportedSFTPExtensions defines the supported extensions
|
||||
supportedSFTPExtensions = []sshExtensionPair{
|
||||
{"hardlink@openssh.com", "1"},
|
||||
{"posix-rename@openssh.com", "1"},
|
||||
{"statvfs@openssh.com", "2"},
|
||||
}
|
||||
sftpExtensions = supportedSFTPExtensions
|
||||
)
|
||||
|
||||
type fxp uint8
|
||||
|
||||
func (f fxp) String() string {
|
||||
switch f {
|
||||
case sshFxpInit:
|
||||
return "SSH_FXP_INIT"
|
||||
case sshFxpVersion:
|
||||
return "SSH_FXP_VERSION"
|
||||
case sshFxpOpen:
|
||||
return "SSH_FXP_OPEN"
|
||||
case sshFxpClose:
|
||||
return "SSH_FXP_CLOSE"
|
||||
case sshFxpRead:
|
||||
return "SSH_FXP_READ"
|
||||
case sshFxpWrite:
|
||||
return "SSH_FXP_WRITE"
|
||||
case sshFxpLstat:
|
||||
return "SSH_FXP_LSTAT"
|
||||
case sshFxpFstat:
|
||||
return "SSH_FXP_FSTAT"
|
||||
case sshFxpSetstat:
|
||||
return "SSH_FXP_SETSTAT"
|
||||
case sshFxpFsetstat:
|
||||
return "SSH_FXP_FSETSTAT"
|
||||
case sshFxpOpendir:
|
||||
return "SSH_FXP_OPENDIR"
|
||||
case sshFxpReaddir:
|
||||
return "SSH_FXP_READDIR"
|
||||
case sshFxpRemove:
|
||||
return "SSH_FXP_REMOVE"
|
||||
case sshFxpMkdir:
|
||||
return "SSH_FXP_MKDIR"
|
||||
case sshFxpRmdir:
|
||||
return "SSH_FXP_RMDIR"
|
||||
case sshFxpRealpath:
|
||||
return "SSH_FXP_REALPATH"
|
||||
case sshFxpStat:
|
||||
return "SSH_FXP_STAT"
|
||||
case sshFxpRename:
|
||||
return "SSH_FXP_RENAME"
|
||||
case sshFxpReadlink:
|
||||
return "SSH_FXP_READLINK"
|
||||
case sshFxpSymlink:
|
||||
return "SSH_FXP_SYMLINK"
|
||||
case sshFxpStatus:
|
||||
return "SSH_FXP_STATUS"
|
||||
case sshFxpHandle:
|
||||
return "SSH_FXP_HANDLE"
|
||||
case sshFxpData:
|
||||
return "SSH_FXP_DATA"
|
||||
case sshFxpName:
|
||||
return "SSH_FXP_NAME"
|
||||
case sshFxpAttrs:
|
||||
return "SSH_FXP_ATTRS"
|
||||
case sshFxpExtended:
|
||||
return "SSH_FXP_EXTENDED"
|
||||
case sshFxpExtendedReply:
|
||||
return "SSH_FXP_EXTENDED_REPLY"
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
type fx uint8
|
||||
|
||||
func (f fx) String() string {
|
||||
switch f {
|
||||
case sshFxOk:
|
||||
return "SSH_FX_OK"
|
||||
case sshFxEOF:
|
||||
return "SSH_FX_EOF"
|
||||
case sshFxNoSuchFile:
|
||||
return "SSH_FX_NO_SUCH_FILE"
|
||||
case sshFxPermissionDenied:
|
||||
return "SSH_FX_PERMISSION_DENIED"
|
||||
case sshFxFailure:
|
||||
return "SSH_FX_FAILURE"
|
||||
case sshFxBadMessage:
|
||||
return "SSH_FX_BAD_MESSAGE"
|
||||
case sshFxNoConnection:
|
||||
return "SSH_FX_NO_CONNECTION"
|
||||
case sshFxConnectionLost:
|
||||
return "SSH_FX_CONNECTION_LOST"
|
||||
case sshFxOPUnsupported:
|
||||
return "SSH_FX_OP_UNSUPPORTED"
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
type unexpectedPacketErr struct {
|
||||
want, got fxp
|
||||
}
|
||||
|
||||
func (u *unexpectedPacketErr) Error() string {
|
||||
return fmt.Sprintf("sftp: unexpected packet: want %v, got %v", u.want, u.got)
|
||||
}
|
||||
|
||||
func unimplementedPacketErr(u fxp) error {
|
||||
return fmt.Errorf("sftp: unimplemented packet type: got %v", u)
|
||||
}
|
||||
|
||||
type unexpectedIDErr struct{ want, got uint32 }
|
||||
|
||||
func (u *unexpectedIDErr) Error() string {
|
||||
return fmt.Sprintf("sftp: unexpected id: want %d, got %d", u.want, u.got)
|
||||
}
|
||||
|
||||
func unimplementedSeekWhence(whence int) error {
|
||||
return fmt.Errorf("sftp: unimplemented seek whence %d", whence)
|
||||
}
|
||||
|
||||
func unexpectedCount(want, got uint32) error {
|
||||
return fmt.Errorf("sftp: unexpected count: want %d, got %d", want, got)
|
||||
}
|
||||
|
||||
type unexpectedVersionErr struct{ want, got uint32 }
|
||||
|
||||
func (u *unexpectedVersionErr) Error() string {
|
||||
return fmt.Sprintf("sftp: unexpected server version: want %v, got %v", u.want, u.got)
|
||||
}
|
||||
|
||||
// A StatusError is returned when an SFTP operation fails, and provides
|
||||
// additional information about the failure.
|
||||
type StatusError struct {
|
||||
Code uint32
|
||||
msg, lang string
|
||||
}
|
||||
|
||||
func (s *StatusError) Error() string {
|
||||
return fmt.Sprintf("sftp: %q (%v)", s.msg, fx(s.Code))
|
||||
}
|
||||
|
||||
// FxCode returns the error code typed to match against the exported codes
|
||||
func (s *StatusError) FxCode() fxerr {
|
||||
return fxerr(s.Code)
|
||||
}
|
||||
|
||||
func getSupportedExtensionByName(extensionName string) (sshExtensionPair, error) {
|
||||
for _, supportedExtension := range supportedSFTPExtensions {
|
||||
if supportedExtension.Name == extensionName {
|
||||
return supportedExtension, nil
|
||||
}
|
||||
}
|
||||
return sshExtensionPair{}, fmt.Errorf("unsupported extension: %s", extensionName)
|
||||
}
|
||||
|
||||
// SetSFTPExtensions allows to customize the supported server extensions.
|
||||
// See the variable supportedSFTPExtensions for supported extensions.
|
||||
// This method accepts a slice of sshExtensionPair names for example 'hardlink@openssh.com'.
|
||||
// If an invalid extension is given an error will be returned and nothing will be changed
|
||||
func SetSFTPExtensions(extensions ...string) error {
|
||||
tempExtensions := []sshExtensionPair{}
|
||||
for _, extension := range extensions {
|
||||
sftpExtension, err := getSupportedExtensionByName(extension)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tempExtensions = append(tempExtensions, sftpExtension)
|
||||
}
|
||||
sftpExtensions = tempExtensions
|
||||
return nil
|
||||
}
|
||||
94
vendor/github.com/pkg/sftp/stat.go
generated
vendored
Normal file
94
vendor/github.com/pkg/sftp/stat.go
generated
vendored
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
package sftp
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
sshfx "github.com/pkg/sftp/internal/encoding/ssh/filexfer"
|
||||
)
|
||||
|
||||
// isRegular returns true if the mode describes a regular file.
|
||||
func isRegular(mode uint32) bool {
|
||||
return sshfx.FileMode(mode)&sshfx.ModeType == sshfx.ModeRegular
|
||||
}
|
||||
|
||||
// toFileMode converts sftp filemode bits to the os.FileMode specification
|
||||
func toFileMode(mode uint32) os.FileMode {
|
||||
var fm = os.FileMode(mode & 0777)
|
||||
|
||||
switch sshfx.FileMode(mode) & sshfx.ModeType {
|
||||
case sshfx.ModeDevice:
|
||||
fm |= os.ModeDevice
|
||||
case sshfx.ModeCharDevice:
|
||||
fm |= os.ModeDevice | os.ModeCharDevice
|
||||
case sshfx.ModeDir:
|
||||
fm |= os.ModeDir
|
||||
case sshfx.ModeNamedPipe:
|
||||
fm |= os.ModeNamedPipe
|
||||
case sshfx.ModeSymlink:
|
||||
fm |= os.ModeSymlink
|
||||
case sshfx.ModeRegular:
|
||||
// nothing to do
|
||||
case sshfx.ModeSocket:
|
||||
fm |= os.ModeSocket
|
||||
}
|
||||
|
||||
if sshfx.FileMode(mode)&sshfx.ModeSetUID != 0 {
|
||||
fm |= os.ModeSetuid
|
||||
}
|
||||
if sshfx.FileMode(mode)&sshfx.ModeSetGID != 0 {
|
||||
fm |= os.ModeSetgid
|
||||
}
|
||||
if sshfx.FileMode(mode)&sshfx.ModeSticky != 0 {
|
||||
fm |= os.ModeSticky
|
||||
}
|
||||
|
||||
return fm
|
||||
}
|
||||
|
||||
// fromFileMode converts from the os.FileMode specification to sftp filemode bits
|
||||
func fromFileMode(mode os.FileMode) uint32 {
|
||||
ret := sshfx.FileMode(mode & os.ModePerm)
|
||||
|
||||
switch mode & os.ModeType {
|
||||
case os.ModeDevice | os.ModeCharDevice:
|
||||
ret |= sshfx.ModeCharDevice
|
||||
case os.ModeDevice:
|
||||
ret |= sshfx.ModeDevice
|
||||
case os.ModeDir:
|
||||
ret |= sshfx.ModeDir
|
||||
case os.ModeNamedPipe:
|
||||
ret |= sshfx.ModeNamedPipe
|
||||
case os.ModeSymlink:
|
||||
ret |= sshfx.ModeSymlink
|
||||
case 0:
|
||||
ret |= sshfx.ModeRegular
|
||||
case os.ModeSocket:
|
||||
ret |= sshfx.ModeSocket
|
||||
}
|
||||
|
||||
if mode&os.ModeSetuid != 0 {
|
||||
ret |= sshfx.ModeSetUID
|
||||
}
|
||||
if mode&os.ModeSetgid != 0 {
|
||||
ret |= sshfx.ModeSetGID
|
||||
}
|
||||
if mode&os.ModeSticky != 0 {
|
||||
ret |= sshfx.ModeSticky
|
||||
}
|
||||
|
||||
return uint32(ret)
|
||||
}
|
||||
|
||||
const (
|
||||
s_ISUID = uint32(sshfx.ModeSetUID)
|
||||
s_ISGID = uint32(sshfx.ModeSetGID)
|
||||
s_ISVTX = uint32(sshfx.ModeSticky)
|
||||
)
|
||||
|
||||
// S_IFMT is a legacy export, and was brought in to support GOOS environments whose sysconfig.S_IFMT may be different from the value used internally by SFTP standards.
|
||||
// There should be no reason why you need to import it, or use it, but unexporting it could cause code to break in a way that cannot be readily fixed.
|
||||
// As such, we continue to export this value as the value used in the SFTP standard.
|
||||
//
|
||||
// Deprecated: Remove use of this value, and avoid any future use as well.
|
||||
// There is no alternative provided, you should never need to access this value.
|
||||
const S_IFMT = uint32(sshfx.ModeType)
|
||||
27
vendor/golang.org/x/crypto/LICENSE
generated
vendored
Normal file
27
vendor/golang.org/x/crypto/LICENSE
generated
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
Copyright 2009 The Go Authors.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google LLC nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
22
vendor/golang.org/x/crypto/PATENTS
generated
vendored
Normal file
22
vendor/golang.org/x/crypto/PATENTS
generated
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
Additional IP Rights Grant (Patents)
|
||||
|
||||
"This implementation" means the copyrightable works distributed by
|
||||
Google as part of the Go project.
|
||||
|
||||
Google hereby grants to You a perpetual, worldwide, non-exclusive,
|
||||
no-charge, royalty-free, irrevocable (except as stated in this section)
|
||||
patent license to make, have made, use, offer to sell, sell, import,
|
||||
transfer and otherwise run, modify and propagate the contents of this
|
||||
implementation of Go, where such license applies only to those patent
|
||||
claims, both currently owned or controlled by Google and acquired in
|
||||
the future, licensable by Google that are necessarily infringed by this
|
||||
implementation of Go. This grant does not include claims that would be
|
||||
infringed only as a consequence of further modification of this
|
||||
implementation. If you or your agent or exclusive licensee institute or
|
||||
order or agree to the institution of patent litigation against any
|
||||
entity (including a cross-claim or counterclaim in a lawsuit) alleging
|
||||
that this implementation of Go or any code incorporated within this
|
||||
implementation of Go constitutes direct or contributory patent
|
||||
infringement, or inducement of patent infringement, then any patent
|
||||
rights granted to you under this License for this implementation of Go
|
||||
shall terminate as of the date such litigation is filed.
|
||||
159
vendor/golang.org/x/crypto/blowfish/block.go
generated
vendored
Normal file
159
vendor/golang.org/x/crypto/blowfish/block.go
generated
vendored
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package blowfish
|
||||
|
||||
// getNextWord returns the next big-endian uint32 value from the byte slice
|
||||
// at the given position in a circular manner, updating the position.
|
||||
func getNextWord(b []byte, pos *int) uint32 {
|
||||
var w uint32
|
||||
j := *pos
|
||||
for i := 0; i < 4; i++ {
|
||||
w = w<<8 | uint32(b[j])
|
||||
j++
|
||||
if j >= len(b) {
|
||||
j = 0
|
||||
}
|
||||
}
|
||||
*pos = j
|
||||
return w
|
||||
}
|
||||
|
||||
// ExpandKey performs a key expansion on the given *Cipher. Specifically, it
|
||||
// performs the Blowfish algorithm's key schedule which sets up the *Cipher's
|
||||
// pi and substitution tables for calls to Encrypt. This is used, primarily,
|
||||
// by the bcrypt package to reuse the Blowfish key schedule during its
|
||||
// set up. It's unlikely that you need to use this directly.
|
||||
func ExpandKey(key []byte, c *Cipher) {
|
||||
j := 0
|
||||
for i := 0; i < 18; i++ {
|
||||
// Using inlined getNextWord for performance.
|
||||
var d uint32
|
||||
for k := 0; k < 4; k++ {
|
||||
d = d<<8 | uint32(key[j])
|
||||
j++
|
||||
if j >= len(key) {
|
||||
j = 0
|
||||
}
|
||||
}
|
||||
c.p[i] ^= d
|
||||
}
|
||||
|
||||
var l, r uint32
|
||||
for i := 0; i < 18; i += 2 {
|
||||
l, r = encryptBlock(l, r, c)
|
||||
c.p[i], c.p[i+1] = l, r
|
||||
}
|
||||
|
||||
for i := 0; i < 256; i += 2 {
|
||||
l, r = encryptBlock(l, r, c)
|
||||
c.s0[i], c.s0[i+1] = l, r
|
||||
}
|
||||
for i := 0; i < 256; i += 2 {
|
||||
l, r = encryptBlock(l, r, c)
|
||||
c.s1[i], c.s1[i+1] = l, r
|
||||
}
|
||||
for i := 0; i < 256; i += 2 {
|
||||
l, r = encryptBlock(l, r, c)
|
||||
c.s2[i], c.s2[i+1] = l, r
|
||||
}
|
||||
for i := 0; i < 256; i += 2 {
|
||||
l, r = encryptBlock(l, r, c)
|
||||
c.s3[i], c.s3[i+1] = l, r
|
||||
}
|
||||
}
|
||||
|
||||
// This is similar to ExpandKey, but folds the salt during the key
|
||||
// schedule. While ExpandKey is essentially expandKeyWithSalt with an all-zero
|
||||
// salt passed in, reusing ExpandKey turns out to be a place of inefficiency
|
||||
// and specializing it here is useful.
|
||||
func expandKeyWithSalt(key []byte, salt []byte, c *Cipher) {
|
||||
j := 0
|
||||
for i := 0; i < 18; i++ {
|
||||
c.p[i] ^= getNextWord(key, &j)
|
||||
}
|
||||
|
||||
j = 0
|
||||
var l, r uint32
|
||||
for i := 0; i < 18; i += 2 {
|
||||
l ^= getNextWord(salt, &j)
|
||||
r ^= getNextWord(salt, &j)
|
||||
l, r = encryptBlock(l, r, c)
|
||||
c.p[i], c.p[i+1] = l, r
|
||||
}
|
||||
|
||||
for i := 0; i < 256; i += 2 {
|
||||
l ^= getNextWord(salt, &j)
|
||||
r ^= getNextWord(salt, &j)
|
||||
l, r = encryptBlock(l, r, c)
|
||||
c.s0[i], c.s0[i+1] = l, r
|
||||
}
|
||||
|
||||
for i := 0; i < 256; i += 2 {
|
||||
l ^= getNextWord(salt, &j)
|
||||
r ^= getNextWord(salt, &j)
|
||||
l, r = encryptBlock(l, r, c)
|
||||
c.s1[i], c.s1[i+1] = l, r
|
||||
}
|
||||
|
||||
for i := 0; i < 256; i += 2 {
|
||||
l ^= getNextWord(salt, &j)
|
||||
r ^= getNextWord(salt, &j)
|
||||
l, r = encryptBlock(l, r, c)
|
||||
c.s2[i], c.s2[i+1] = l, r
|
||||
}
|
||||
|
||||
for i := 0; i < 256; i += 2 {
|
||||
l ^= getNextWord(salt, &j)
|
||||
r ^= getNextWord(salt, &j)
|
||||
l, r = encryptBlock(l, r, c)
|
||||
c.s3[i], c.s3[i+1] = l, r
|
||||
}
|
||||
}
|
||||
|
||||
func encryptBlock(l, r uint32, c *Cipher) (uint32, uint32) {
|
||||
xl, xr := l, r
|
||||
xl ^= c.p[0]
|
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[1]
|
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[2]
|
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[3]
|
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[4]
|
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[5]
|
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[6]
|
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[7]
|
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[8]
|
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[9]
|
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[10]
|
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[11]
|
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[12]
|
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[13]
|
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[14]
|
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[15]
|
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[16]
|
||||
xr ^= c.p[17]
|
||||
return xr, xl
|
||||
}
|
||||
|
||||
func decryptBlock(l, r uint32, c *Cipher) (uint32, uint32) {
|
||||
xl, xr := l, r
|
||||
xl ^= c.p[17]
|
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[16]
|
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[15]
|
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[14]
|
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[13]
|
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[12]
|
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[11]
|
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[10]
|
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[9]
|
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[8]
|
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[7]
|
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[6]
|
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[5]
|
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[4]
|
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[3]
|
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[2]
|
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[1]
|
||||
xr ^= c.p[0]
|
||||
return xr, xl
|
||||
}
|
||||
99
vendor/golang.org/x/crypto/blowfish/cipher.go
generated
vendored
Normal file
99
vendor/golang.org/x/crypto/blowfish/cipher.go
generated
vendored
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package blowfish implements Bruce Schneier's Blowfish encryption algorithm.
|
||||
//
|
||||
// Blowfish is a legacy cipher and its short block size makes it vulnerable to
|
||||
// birthday bound attacks (see https://sweet32.info). It should only be used
|
||||
// where compatibility with legacy systems, not security, is the goal.
|
||||
//
|
||||
// Deprecated: any new system should use AES (from crypto/aes, if necessary in
|
||||
// an AEAD mode like crypto/cipher.NewGCM) or XChaCha20-Poly1305 (from
|
||||
// golang.org/x/crypto/chacha20poly1305).
|
||||
package blowfish
|
||||
|
||||
// The code is a port of Bruce Schneier's C implementation.
|
||||
// See https://www.schneier.com/blowfish.html.
|
||||
|
||||
import "strconv"
|
||||
|
||||
// The Blowfish block size in bytes.
|
||||
const BlockSize = 8
|
||||
|
||||
// A Cipher is an instance of Blowfish encryption using a particular key.
|
||||
type Cipher struct {
|
||||
p [18]uint32
|
||||
s0, s1, s2, s3 [256]uint32
|
||||
}
|
||||
|
||||
type KeySizeError int
|
||||
|
||||
func (k KeySizeError) Error() string {
|
||||
return "crypto/blowfish: invalid key size " + strconv.Itoa(int(k))
|
||||
}
|
||||
|
||||
// NewCipher creates and returns a Cipher.
|
||||
// The key argument should be the Blowfish key, from 1 to 56 bytes.
|
||||
func NewCipher(key []byte) (*Cipher, error) {
|
||||
var result Cipher
|
||||
if k := len(key); k < 1 || k > 56 {
|
||||
return nil, KeySizeError(k)
|
||||
}
|
||||
initCipher(&result)
|
||||
ExpandKey(key, &result)
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// NewSaltedCipher creates a returns a Cipher that folds a salt into its key
|
||||
// schedule. For most purposes, NewCipher, instead of NewSaltedCipher, is
|
||||
// sufficient and desirable. For bcrypt compatibility, the key can be over 56
|
||||
// bytes.
|
||||
func NewSaltedCipher(key, salt []byte) (*Cipher, error) {
|
||||
if len(salt) == 0 {
|
||||
return NewCipher(key)
|
||||
}
|
||||
var result Cipher
|
||||
if k := len(key); k < 1 {
|
||||
return nil, KeySizeError(k)
|
||||
}
|
||||
initCipher(&result)
|
||||
expandKeyWithSalt(key, salt, &result)
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// BlockSize returns the Blowfish block size, 8 bytes.
|
||||
// It is necessary to satisfy the Block interface in the
|
||||
// package "crypto/cipher".
|
||||
func (c *Cipher) BlockSize() int { return BlockSize }
|
||||
|
||||
// Encrypt encrypts the 8-byte buffer src using the key k
|
||||
// and stores the result in dst.
|
||||
// Note that for amounts of data larger than a block,
|
||||
// it is not safe to just call Encrypt on successive blocks;
|
||||
// instead, use an encryption mode like CBC (see crypto/cipher/cbc.go).
|
||||
func (c *Cipher) Encrypt(dst, src []byte) {
|
||||
l := uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3])
|
||||
r := uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7])
|
||||
l, r = encryptBlock(l, r, c)
|
||||
dst[0], dst[1], dst[2], dst[3] = byte(l>>24), byte(l>>16), byte(l>>8), byte(l)
|
||||
dst[4], dst[5], dst[6], dst[7] = byte(r>>24), byte(r>>16), byte(r>>8), byte(r)
|
||||
}
|
||||
|
||||
// Decrypt decrypts the 8-byte buffer src using the key k
|
||||
// and stores the result in dst.
|
||||
func (c *Cipher) Decrypt(dst, src []byte) {
|
||||
l := uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3])
|
||||
r := uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7])
|
||||
l, r = decryptBlock(l, r, c)
|
||||
dst[0], dst[1], dst[2], dst[3] = byte(l>>24), byte(l>>16), byte(l>>8), byte(l)
|
||||
dst[4], dst[5], dst[6], dst[7] = byte(r>>24), byte(r>>16), byte(r>>8), byte(r)
|
||||
}
|
||||
|
||||
func initCipher(c *Cipher) {
|
||||
copy(c.p[0:], p[0:])
|
||||
copy(c.s0[0:], s0[0:])
|
||||
copy(c.s1[0:], s1[0:])
|
||||
copy(c.s2[0:], s2[0:])
|
||||
copy(c.s3[0:], s3[0:])
|
||||
}
|
||||
199
vendor/golang.org/x/crypto/blowfish/const.go
generated
vendored
Normal file
199
vendor/golang.org/x/crypto/blowfish/const.go
generated
vendored
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// The startup permutation array and substitution boxes.
|
||||
// They are the hexadecimal digits of PI; see:
|
||||
// https://www.schneier.com/code/constants.txt.
|
||||
|
||||
package blowfish
|
||||
|
||||
var s0 = [256]uint32{
|
||||
0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed, 0x6a267e96,
|
||||
0xba7c9045, 0xf12c7f99, 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16,
|
||||
0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, 0x0d95748f, 0x728eb658,
|
||||
0x718bcd58, 0x82154aee, 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013,
|
||||
0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, 0x8e79dcb0, 0x603a180e,
|
||||
0x6c9e0e8b, 0xb01e8a3e, 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60,
|
||||
0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, 0x55ca396a, 0x2aab10b6,
|
||||
0xb4cc5c34, 0x1141e8ce, 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a,
|
||||
0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, 0xafd6ba33, 0x6c24cf5c,
|
||||
0x7a325381, 0x28958677, 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193,
|
||||
0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, 0xef845d5d, 0xe98575b1,
|
||||
0xdc262302, 0xeb651b88, 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239,
|
||||
0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, 0x21c66842, 0xf6e96c9a,
|
||||
0x670c9c61, 0xabd388f0, 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3,
|
||||
0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, 0xa1f1651d, 0x39af0176,
|
||||
0x66ca593e, 0x82430e88, 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe,
|
||||
0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, 0x4ed3aa62, 0x363f7706,
|
||||
0x1bfedf72, 0x429b023d, 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b,
|
||||
0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, 0xe3fe501a, 0xb6794c3b,
|
||||
0x976ce0bd, 0x04c006ba, 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463,
|
||||
0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, 0x6dfc511f, 0x9b30952c,
|
||||
0xcc814544, 0xaf5ebd09, 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3,
|
||||
0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, 0x5579c0bd, 0x1a60320a,
|
||||
0xd6a100c6, 0x402c7279, 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8,
|
||||
0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, 0x323db5fa, 0xfd238760,
|
||||
0x53317b48, 0x3e00df82, 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db,
|
||||
0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, 0x695b27b0, 0xbbca58c8,
|
||||
0xe1ffa35d, 0xb8f011a0, 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b,
|
||||
0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, 0xe1ddf2da, 0xa4cb7e33,
|
||||
0x62fb1341, 0xcee4c6e8, 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4,
|
||||
0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, 0xd08ed1d0, 0xafc725e0,
|
||||
0x8e3c5b2f, 0x8e7594b7, 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c,
|
||||
0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, 0x2f2f2218, 0xbe0e1777,
|
||||
0xea752dfe, 0x8b021fa1, 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299,
|
||||
0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, 0x165fa266, 0x80957705,
|
||||
0x93cc7314, 0x211a1477, 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf,
|
||||
0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, 0x00250e2d, 0x2071b35e,
|
||||
0x226800bb, 0x57b8e0af, 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa,
|
||||
0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, 0x83260376, 0x6295cfa9,
|
||||
0x11c81968, 0x4e734a41, 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915,
|
||||
0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, 0x08ba6fb5, 0x571be91f,
|
||||
0xf296ec6b, 0x2a0dd915, 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664,
|
||||
0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a,
|
||||
}
|
||||
|
||||
var s1 = [256]uint32{
|
||||
0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, 0xad6ea6b0, 0x49a7df7d,
|
||||
0x9cee60b8, 0x8fedb266, 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1,
|
||||
0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, 0x3f54989a, 0x5b429d65,
|
||||
0x6b8fe4d6, 0x99f73fd6, 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1,
|
||||
0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, 0x09686b3f, 0x3ebaefc9,
|
||||
0x3c971814, 0x6b6a70a1, 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737,
|
||||
0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, 0xb03ada37, 0xf0500c0d,
|
||||
0xf01c1f04, 0x0200b3ff, 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd,
|
||||
0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, 0x3ae5e581, 0x37c2dadc,
|
||||
0xc8b57634, 0x9af3dda7, 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41,
|
||||
0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, 0x4e548b38, 0x4f6db908,
|
||||
0x6f420d03, 0xf60a04bf, 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af,
|
||||
0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, 0x5512721f, 0x2e6b7124,
|
||||
0x501adde6, 0x9f84cd87, 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c,
|
||||
0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, 0xef1c1847, 0x3215d908,
|
||||
0xdd433b37, 0x24c2ba16, 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd,
|
||||
0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, 0x043556f1, 0xd7a3c76b,
|
||||
0x3c11183b, 0x5924a509, 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e,
|
||||
0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, 0x771fe71c, 0x4e3d06fa,
|
||||
0x2965dcb9, 0x99e71d0f, 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a,
|
||||
0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, 0xf2f74ea7, 0x361d2b3d,
|
||||
0x1939260f, 0x19c27960, 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66,
|
||||
0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, 0xc332ddef, 0xbe6c5aa5,
|
||||
0x65582185, 0x68ab9802, 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84,
|
||||
0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, 0x13cca830, 0xeb61bd96,
|
||||
0x0334fe1e, 0xaa0363cf, 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14,
|
||||
0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, 0x648b1eaf, 0x19bdf0ca,
|
||||
0xa02369b9, 0x655abb50, 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7,
|
||||
0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, 0xf837889a, 0x97e32d77,
|
||||
0x11ed935f, 0x16681281, 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99,
|
||||
0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, 0xcdb30aeb, 0x532e3054,
|
||||
0x8fd948e4, 0x6dbc3128, 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73,
|
||||
0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, 0x45eee2b6, 0xa3aaabea,
|
||||
0xdb6c4f15, 0xfacb4fd0, 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105,
|
||||
0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, 0xcf62a1f2, 0x5b8d2646,
|
||||
0xfc8883a0, 0xc1c7b6a3, 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285,
|
||||
0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, 0x58428d2a, 0x0c55f5ea,
|
||||
0x1dadf43e, 0x233f7061, 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb,
|
||||
0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, 0xa6078084, 0x19f8509e,
|
||||
0xe8efd855, 0x61d99735, 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc,
|
||||
0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, 0xdb73dbd3, 0x105588cd,
|
||||
0x675fda79, 0xe3674340, 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20,
|
||||
0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7,
|
||||
}
|
||||
|
||||
var s2 = [256]uint32{
|
||||
0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, 0x411520f7, 0x7602d4f7,
|
||||
0xbcf46b2e, 0xd4a20068, 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af,
|
||||
0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, 0x4d95fc1d, 0x96b591af,
|
||||
0x70f4ddd3, 0x66a02f45, 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504,
|
||||
0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, 0x28507825, 0x530429f4,
|
||||
0x0a2c86da, 0xe9b66dfb, 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee,
|
||||
0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, 0xaace1e7c, 0xd3375fec,
|
||||
0xce78a399, 0x406b2a42, 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b,
|
||||
0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, 0x3a6efa74, 0xdd5b4332,
|
||||
0x6841e7f7, 0xca7820fb, 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527,
|
||||
0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, 0x55a867bc, 0xa1159a58,
|
||||
0xcca92963, 0x99e1db33, 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c,
|
||||
0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, 0x95c11548, 0xe4c66d22,
|
||||
0x48c1133f, 0xc70f86dc, 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17,
|
||||
0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, 0x257b7834, 0x602a9c60,
|
||||
0xdff8e8a3, 0x1f636c1b, 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115,
|
||||
0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, 0x85b2a20e, 0xe6ba0d99,
|
||||
0xde720c8c, 0x2da2f728, 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0,
|
||||
0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, 0x0a476341, 0x992eff74,
|
||||
0x3a6f6eab, 0xf4f8fd37, 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d,
|
||||
0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, 0xf1290dc7, 0xcc00ffa3,
|
||||
0xb5390f92, 0x690fed0b, 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3,
|
||||
0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, 0x37392eb3, 0xcc115979,
|
||||
0x8026e297, 0xf42e312d, 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c,
|
||||
0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, 0x1a6b1018, 0x11caedfa,
|
||||
0x3d25bdd8, 0xe2e1c3c9, 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a,
|
||||
0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, 0x9dbc8057, 0xf0f7c086,
|
||||
0x60787bf8, 0x6003604d, 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc,
|
||||
0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, 0x77a057be, 0xbde8ae24,
|
||||
0x55464299, 0xbf582e61, 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2,
|
||||
0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, 0x7aeb2661, 0x8b1ddf84,
|
||||
0x846a0e79, 0x915f95e2, 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c,
|
||||
0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, 0xb77f19b6, 0xe0a9dc09,
|
||||
0x662d09a1, 0xc4324633, 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10,
|
||||
0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, 0xdcb7da83, 0x573906fe,
|
||||
0xa1e2ce9b, 0x4fcd7f52, 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027,
|
||||
0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, 0xf0177a28, 0xc0f586e0,
|
||||
0x006058aa, 0x30dc7d62, 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634,
|
||||
0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, 0x6f05e409, 0x4b7c0188,
|
||||
0x39720a3d, 0x7c927c24, 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc,
|
||||
0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, 0x1e50ef5e, 0xb161e6f8,
|
||||
0xa28514d9, 0x6c51133c, 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837,
|
||||
0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0,
|
||||
}
|
||||
|
||||
var s3 = [256]uint32{
|
||||
0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, 0x5cb0679e, 0x4fa33742,
|
||||
0xd3822740, 0x99bc9bbe, 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b,
|
||||
0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, 0x5748ab2f, 0xbc946e79,
|
||||
0xc6a376d2, 0x6549c2c8, 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6,
|
||||
0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, 0xa1fad5f0, 0x6a2d519a,
|
||||
0x63ef8ce2, 0x9a86ee22, 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4,
|
||||
0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, 0x2826a2f9, 0xa73a3ae1,
|
||||
0x4ba99586, 0xef5562e9, 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59,
|
||||
0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, 0xe990fd5a, 0x9e34d797,
|
||||
0x2cf0b7d9, 0x022b8b51, 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28,
|
||||
0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, 0xe029ac71, 0xe019a5e6,
|
||||
0x47b0acfd, 0xed93fa9b, 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28,
|
||||
0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, 0x15056dd4, 0x88f46dba,
|
||||
0x03a16125, 0x0564f0bd, 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a,
|
||||
0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, 0x7533d928, 0xb155fdf5,
|
||||
0x03563482, 0x8aba3cbb, 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f,
|
||||
0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, 0xea7a90c2, 0xfb3e7bce,
|
||||
0x5121ce64, 0x774fbe32, 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680,
|
||||
0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, 0xb39a460a, 0x6445c0dd,
|
||||
0x586cdecf, 0x1c20c8ae, 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb,
|
||||
0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, 0x72eacea8, 0xfa6484bb,
|
||||
0x8d6612ae, 0xbf3c6f47, 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370,
|
||||
0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, 0x4040cb08, 0x4eb4e2cc,
|
||||
0x34d2466a, 0x0115af84, 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048,
|
||||
0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, 0x611560b1, 0xe7933fdc,
|
||||
0xbb3a792b, 0x344525bd, 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9,
|
||||
0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, 0x1a908749, 0xd44fbd9a,
|
||||
0xd0dadecb, 0xd50ada38, 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f,
|
||||
0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, 0xbf97222c, 0x15e6fc2a,
|
||||
0x0f91fc71, 0x9b941525, 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1,
|
||||
0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, 0xe0ec6e0e, 0x1698db3b,
|
||||
0x4c98a0be, 0x3278e964, 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e,
|
||||
0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, 0xdf359f8d, 0x9b992f2e,
|
||||
0xe60b6f47, 0x0fe3f11d, 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f,
|
||||
0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, 0xf523f357, 0xa6327623,
|
||||
0x93a83531, 0x56cccd02, 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc,
|
||||
0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, 0xe6c6c7bd, 0x327a140a,
|
||||
0x45e1d006, 0xc3f27b9a, 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6,
|
||||
0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, 0x53113ec0, 0x1640e3d3,
|
||||
0x38abbd60, 0x2547adf0, 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060,
|
||||
0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, 0x1948c25c, 0x02fb8a8c,
|
||||
0x01c36ae4, 0xd6ebe1f9, 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f,
|
||||
0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6,
|
||||
}
|
||||
|
||||
var p = [18]uint32{
|
||||
0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822, 0x299f31d0,
|
||||
0x082efa98, 0xec4e6c89, 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c,
|
||||
0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, 0x9216d5d9, 0x8979fb1b,
|
||||
}
|
||||
16
vendor/golang.org/x/crypto/chacha20/chacha_arm64.go
generated
vendored
Normal file
16
vendor/golang.org/x/crypto/chacha20/chacha_arm64.go
generated
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build gc && !purego
|
||||
|
||||
package chacha20
|
||||
|
||||
const bufSize = 256
|
||||
|
||||
//go:noescape
|
||||
func xorKeyStreamVX(dst, src []byte, key *[8]uint32, nonce *[3]uint32, counter *uint32)
|
||||
|
||||
func (c *Cipher) xorKeyStreamBlocks(dst, src []byte) {
|
||||
xorKeyStreamVX(dst, src, &c.key, &c.nonce, &c.counter)
|
||||
}
|
||||
307
vendor/golang.org/x/crypto/chacha20/chacha_arm64.s
generated
vendored
Normal file
307
vendor/golang.org/x/crypto/chacha20/chacha_arm64.s
generated
vendored
Normal file
|
|
@ -0,0 +1,307 @@
|
|||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build gc && !purego
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
#define NUM_ROUNDS 10
|
||||
|
||||
// func xorKeyStreamVX(dst, src []byte, key *[8]uint32, nonce *[3]uint32, counter *uint32)
|
||||
TEXT ·xorKeyStreamVX(SB), NOSPLIT, $0
|
||||
MOVD dst+0(FP), R1
|
||||
MOVD src+24(FP), R2
|
||||
MOVD src_len+32(FP), R3
|
||||
MOVD key+48(FP), R4
|
||||
MOVD nonce+56(FP), R6
|
||||
MOVD counter+64(FP), R7
|
||||
|
||||
MOVD $·constants(SB), R10
|
||||
MOVD $·incRotMatrix(SB), R11
|
||||
|
||||
MOVW (R7), R20
|
||||
|
||||
AND $~255, R3, R13
|
||||
ADD R2, R13, R12 // R12 for block end
|
||||
AND $255, R3, R13
|
||||
loop:
|
||||
MOVD $NUM_ROUNDS, R21
|
||||
VLD1 (R11), [V30.S4, V31.S4]
|
||||
|
||||
// load constants
|
||||
// VLD4R (R10), [V0.S4, V1.S4, V2.S4, V3.S4]
|
||||
WORD $0x4D60E940
|
||||
|
||||
// load keys
|
||||
// VLD4R 16(R4), [V4.S4, V5.S4, V6.S4, V7.S4]
|
||||
WORD $0x4DFFE884
|
||||
// VLD4R 16(R4), [V8.S4, V9.S4, V10.S4, V11.S4]
|
||||
WORD $0x4DFFE888
|
||||
SUB $32, R4
|
||||
|
||||
// load counter + nonce
|
||||
// VLD1R (R7), [V12.S4]
|
||||
WORD $0x4D40C8EC
|
||||
|
||||
// VLD3R (R6), [V13.S4, V14.S4, V15.S4]
|
||||
WORD $0x4D40E8CD
|
||||
|
||||
// update counter
|
||||
VADD V30.S4, V12.S4, V12.S4
|
||||
|
||||
chacha:
|
||||
// V0..V3 += V4..V7
|
||||
// V12..V15 <<<= ((V12..V15 XOR V0..V3), 16)
|
||||
VADD V0.S4, V4.S4, V0.S4
|
||||
VADD V1.S4, V5.S4, V1.S4
|
||||
VADD V2.S4, V6.S4, V2.S4
|
||||
VADD V3.S4, V7.S4, V3.S4
|
||||
VEOR V12.B16, V0.B16, V12.B16
|
||||
VEOR V13.B16, V1.B16, V13.B16
|
||||
VEOR V14.B16, V2.B16, V14.B16
|
||||
VEOR V15.B16, V3.B16, V15.B16
|
||||
VREV32 V12.H8, V12.H8
|
||||
VREV32 V13.H8, V13.H8
|
||||
VREV32 V14.H8, V14.H8
|
||||
VREV32 V15.H8, V15.H8
|
||||
// V8..V11 += V12..V15
|
||||
// V4..V7 <<<= ((V4..V7 XOR V8..V11), 12)
|
||||
VADD V8.S4, V12.S4, V8.S4
|
||||
VADD V9.S4, V13.S4, V9.S4
|
||||
VADD V10.S4, V14.S4, V10.S4
|
||||
VADD V11.S4, V15.S4, V11.S4
|
||||
VEOR V8.B16, V4.B16, V16.B16
|
||||
VEOR V9.B16, V5.B16, V17.B16
|
||||
VEOR V10.B16, V6.B16, V18.B16
|
||||
VEOR V11.B16, V7.B16, V19.B16
|
||||
VSHL $12, V16.S4, V4.S4
|
||||
VSHL $12, V17.S4, V5.S4
|
||||
VSHL $12, V18.S4, V6.S4
|
||||
VSHL $12, V19.S4, V7.S4
|
||||
VSRI $20, V16.S4, V4.S4
|
||||
VSRI $20, V17.S4, V5.S4
|
||||
VSRI $20, V18.S4, V6.S4
|
||||
VSRI $20, V19.S4, V7.S4
|
||||
|
||||
// V0..V3 += V4..V7
|
||||
// V12..V15 <<<= ((V12..V15 XOR V0..V3), 8)
|
||||
VADD V0.S4, V4.S4, V0.S4
|
||||
VADD V1.S4, V5.S4, V1.S4
|
||||
VADD V2.S4, V6.S4, V2.S4
|
||||
VADD V3.S4, V7.S4, V3.S4
|
||||
VEOR V12.B16, V0.B16, V12.B16
|
||||
VEOR V13.B16, V1.B16, V13.B16
|
||||
VEOR V14.B16, V2.B16, V14.B16
|
||||
VEOR V15.B16, V3.B16, V15.B16
|
||||
VTBL V31.B16, [V12.B16], V12.B16
|
||||
VTBL V31.B16, [V13.B16], V13.B16
|
||||
VTBL V31.B16, [V14.B16], V14.B16
|
||||
VTBL V31.B16, [V15.B16], V15.B16
|
||||
|
||||
// V8..V11 += V12..V15
|
||||
// V4..V7 <<<= ((V4..V7 XOR V8..V11), 7)
|
||||
VADD V12.S4, V8.S4, V8.S4
|
||||
VADD V13.S4, V9.S4, V9.S4
|
||||
VADD V14.S4, V10.S4, V10.S4
|
||||
VADD V15.S4, V11.S4, V11.S4
|
||||
VEOR V8.B16, V4.B16, V16.B16
|
||||
VEOR V9.B16, V5.B16, V17.B16
|
||||
VEOR V10.B16, V6.B16, V18.B16
|
||||
VEOR V11.B16, V7.B16, V19.B16
|
||||
VSHL $7, V16.S4, V4.S4
|
||||
VSHL $7, V17.S4, V5.S4
|
||||
VSHL $7, V18.S4, V6.S4
|
||||
VSHL $7, V19.S4, V7.S4
|
||||
VSRI $25, V16.S4, V4.S4
|
||||
VSRI $25, V17.S4, V5.S4
|
||||
VSRI $25, V18.S4, V6.S4
|
||||
VSRI $25, V19.S4, V7.S4
|
||||
|
||||
// V0..V3 += V5..V7, V4
|
||||
// V15,V12-V14 <<<= ((V15,V12-V14 XOR V0..V3), 16)
|
||||
VADD V0.S4, V5.S4, V0.S4
|
||||
VADD V1.S4, V6.S4, V1.S4
|
||||
VADD V2.S4, V7.S4, V2.S4
|
||||
VADD V3.S4, V4.S4, V3.S4
|
||||
VEOR V15.B16, V0.B16, V15.B16
|
||||
VEOR V12.B16, V1.B16, V12.B16
|
||||
VEOR V13.B16, V2.B16, V13.B16
|
||||
VEOR V14.B16, V3.B16, V14.B16
|
||||
VREV32 V12.H8, V12.H8
|
||||
VREV32 V13.H8, V13.H8
|
||||
VREV32 V14.H8, V14.H8
|
||||
VREV32 V15.H8, V15.H8
|
||||
|
||||
// V10 += V15; V5 <<<= ((V10 XOR V5), 12)
|
||||
// ...
|
||||
VADD V15.S4, V10.S4, V10.S4
|
||||
VADD V12.S4, V11.S4, V11.S4
|
||||
VADD V13.S4, V8.S4, V8.S4
|
||||
VADD V14.S4, V9.S4, V9.S4
|
||||
VEOR V10.B16, V5.B16, V16.B16
|
||||
VEOR V11.B16, V6.B16, V17.B16
|
||||
VEOR V8.B16, V7.B16, V18.B16
|
||||
VEOR V9.B16, V4.B16, V19.B16
|
||||
VSHL $12, V16.S4, V5.S4
|
||||
VSHL $12, V17.S4, V6.S4
|
||||
VSHL $12, V18.S4, V7.S4
|
||||
VSHL $12, V19.S4, V4.S4
|
||||
VSRI $20, V16.S4, V5.S4
|
||||
VSRI $20, V17.S4, V6.S4
|
||||
VSRI $20, V18.S4, V7.S4
|
||||
VSRI $20, V19.S4, V4.S4
|
||||
|
||||
// V0 += V5; V15 <<<= ((V0 XOR V15), 8)
|
||||
// ...
|
||||
VADD V5.S4, V0.S4, V0.S4
|
||||
VADD V6.S4, V1.S4, V1.S4
|
||||
VADD V7.S4, V2.S4, V2.S4
|
||||
VADD V4.S4, V3.S4, V3.S4
|
||||
VEOR V0.B16, V15.B16, V15.B16
|
||||
VEOR V1.B16, V12.B16, V12.B16
|
||||
VEOR V2.B16, V13.B16, V13.B16
|
||||
VEOR V3.B16, V14.B16, V14.B16
|
||||
VTBL V31.B16, [V12.B16], V12.B16
|
||||
VTBL V31.B16, [V13.B16], V13.B16
|
||||
VTBL V31.B16, [V14.B16], V14.B16
|
||||
VTBL V31.B16, [V15.B16], V15.B16
|
||||
|
||||
// V10 += V15; V5 <<<= ((V10 XOR V5), 7)
|
||||
// ...
|
||||
VADD V15.S4, V10.S4, V10.S4
|
||||
VADD V12.S4, V11.S4, V11.S4
|
||||
VADD V13.S4, V8.S4, V8.S4
|
||||
VADD V14.S4, V9.S4, V9.S4
|
||||
VEOR V10.B16, V5.B16, V16.B16
|
||||
VEOR V11.B16, V6.B16, V17.B16
|
||||
VEOR V8.B16, V7.B16, V18.B16
|
||||
VEOR V9.B16, V4.B16, V19.B16
|
||||
VSHL $7, V16.S4, V5.S4
|
||||
VSHL $7, V17.S4, V6.S4
|
||||
VSHL $7, V18.S4, V7.S4
|
||||
VSHL $7, V19.S4, V4.S4
|
||||
VSRI $25, V16.S4, V5.S4
|
||||
VSRI $25, V17.S4, V6.S4
|
||||
VSRI $25, V18.S4, V7.S4
|
||||
VSRI $25, V19.S4, V4.S4
|
||||
|
||||
SUB $1, R21
|
||||
CBNZ R21, chacha
|
||||
|
||||
// VLD4R (R10), [V16.S4, V17.S4, V18.S4, V19.S4]
|
||||
WORD $0x4D60E950
|
||||
|
||||
// VLD4R 16(R4), [V20.S4, V21.S4, V22.S4, V23.S4]
|
||||
WORD $0x4DFFE894
|
||||
VADD V30.S4, V12.S4, V12.S4
|
||||
VADD V16.S4, V0.S4, V0.S4
|
||||
VADD V17.S4, V1.S4, V1.S4
|
||||
VADD V18.S4, V2.S4, V2.S4
|
||||
VADD V19.S4, V3.S4, V3.S4
|
||||
// VLD4R 16(R4), [V24.S4, V25.S4, V26.S4, V27.S4]
|
||||
WORD $0x4DFFE898
|
||||
// restore R4
|
||||
SUB $32, R4
|
||||
|
||||
// load counter + nonce
|
||||
// VLD1R (R7), [V28.S4]
|
||||
WORD $0x4D40C8FC
|
||||
// VLD3R (R6), [V29.S4, V30.S4, V31.S4]
|
||||
WORD $0x4D40E8DD
|
||||
|
||||
VADD V20.S4, V4.S4, V4.S4
|
||||
VADD V21.S4, V5.S4, V5.S4
|
||||
VADD V22.S4, V6.S4, V6.S4
|
||||
VADD V23.S4, V7.S4, V7.S4
|
||||
VADD V24.S4, V8.S4, V8.S4
|
||||
VADD V25.S4, V9.S4, V9.S4
|
||||
VADD V26.S4, V10.S4, V10.S4
|
||||
VADD V27.S4, V11.S4, V11.S4
|
||||
VADD V28.S4, V12.S4, V12.S4
|
||||
VADD V29.S4, V13.S4, V13.S4
|
||||
VADD V30.S4, V14.S4, V14.S4
|
||||
VADD V31.S4, V15.S4, V15.S4
|
||||
|
||||
VZIP1 V1.S4, V0.S4, V16.S4
|
||||
VZIP2 V1.S4, V0.S4, V17.S4
|
||||
VZIP1 V3.S4, V2.S4, V18.S4
|
||||
VZIP2 V3.S4, V2.S4, V19.S4
|
||||
VZIP1 V5.S4, V4.S4, V20.S4
|
||||
VZIP2 V5.S4, V4.S4, V21.S4
|
||||
VZIP1 V7.S4, V6.S4, V22.S4
|
||||
VZIP2 V7.S4, V6.S4, V23.S4
|
||||
VZIP1 V9.S4, V8.S4, V24.S4
|
||||
VZIP2 V9.S4, V8.S4, V25.S4
|
||||
VZIP1 V11.S4, V10.S4, V26.S4
|
||||
VZIP2 V11.S4, V10.S4, V27.S4
|
||||
VZIP1 V13.S4, V12.S4, V28.S4
|
||||
VZIP2 V13.S4, V12.S4, V29.S4
|
||||
VZIP1 V15.S4, V14.S4, V30.S4
|
||||
VZIP2 V15.S4, V14.S4, V31.S4
|
||||
VZIP1 V18.D2, V16.D2, V0.D2
|
||||
VZIP2 V18.D2, V16.D2, V4.D2
|
||||
VZIP1 V19.D2, V17.D2, V8.D2
|
||||
VZIP2 V19.D2, V17.D2, V12.D2
|
||||
VLD1.P 64(R2), [V16.B16, V17.B16, V18.B16, V19.B16]
|
||||
|
||||
VZIP1 V22.D2, V20.D2, V1.D2
|
||||
VZIP2 V22.D2, V20.D2, V5.D2
|
||||
VZIP1 V23.D2, V21.D2, V9.D2
|
||||
VZIP2 V23.D2, V21.D2, V13.D2
|
||||
VLD1.P 64(R2), [V20.B16, V21.B16, V22.B16, V23.B16]
|
||||
VZIP1 V26.D2, V24.D2, V2.D2
|
||||
VZIP2 V26.D2, V24.D2, V6.D2
|
||||
VZIP1 V27.D2, V25.D2, V10.D2
|
||||
VZIP2 V27.D2, V25.D2, V14.D2
|
||||
VLD1.P 64(R2), [V24.B16, V25.B16, V26.B16, V27.B16]
|
||||
VZIP1 V30.D2, V28.D2, V3.D2
|
||||
VZIP2 V30.D2, V28.D2, V7.D2
|
||||
VZIP1 V31.D2, V29.D2, V11.D2
|
||||
VZIP2 V31.D2, V29.D2, V15.D2
|
||||
VLD1.P 64(R2), [V28.B16, V29.B16, V30.B16, V31.B16]
|
||||
VEOR V0.B16, V16.B16, V16.B16
|
||||
VEOR V1.B16, V17.B16, V17.B16
|
||||
VEOR V2.B16, V18.B16, V18.B16
|
||||
VEOR V3.B16, V19.B16, V19.B16
|
||||
VST1.P [V16.B16, V17.B16, V18.B16, V19.B16], 64(R1)
|
||||
VEOR V4.B16, V20.B16, V20.B16
|
||||
VEOR V5.B16, V21.B16, V21.B16
|
||||
VEOR V6.B16, V22.B16, V22.B16
|
||||
VEOR V7.B16, V23.B16, V23.B16
|
||||
VST1.P [V20.B16, V21.B16, V22.B16, V23.B16], 64(R1)
|
||||
VEOR V8.B16, V24.B16, V24.B16
|
||||
VEOR V9.B16, V25.B16, V25.B16
|
||||
VEOR V10.B16, V26.B16, V26.B16
|
||||
VEOR V11.B16, V27.B16, V27.B16
|
||||
VST1.P [V24.B16, V25.B16, V26.B16, V27.B16], 64(R1)
|
||||
VEOR V12.B16, V28.B16, V28.B16
|
||||
VEOR V13.B16, V29.B16, V29.B16
|
||||
VEOR V14.B16, V30.B16, V30.B16
|
||||
VEOR V15.B16, V31.B16, V31.B16
|
||||
VST1.P [V28.B16, V29.B16, V30.B16, V31.B16], 64(R1)
|
||||
|
||||
ADD $4, R20
|
||||
MOVW R20, (R7) // update counter
|
||||
|
||||
CMP R2, R12
|
||||
BGT loop
|
||||
|
||||
RET
|
||||
|
||||
|
||||
DATA ·constants+0x00(SB)/4, $0x61707865
|
||||
DATA ·constants+0x04(SB)/4, $0x3320646e
|
||||
DATA ·constants+0x08(SB)/4, $0x79622d32
|
||||
DATA ·constants+0x0c(SB)/4, $0x6b206574
|
||||
GLOBL ·constants(SB), NOPTR|RODATA, $32
|
||||
|
||||
DATA ·incRotMatrix+0x00(SB)/4, $0x00000000
|
||||
DATA ·incRotMatrix+0x04(SB)/4, $0x00000001
|
||||
DATA ·incRotMatrix+0x08(SB)/4, $0x00000002
|
||||
DATA ·incRotMatrix+0x0c(SB)/4, $0x00000003
|
||||
DATA ·incRotMatrix+0x10(SB)/4, $0x02010003
|
||||
DATA ·incRotMatrix+0x14(SB)/4, $0x06050407
|
||||
DATA ·incRotMatrix+0x18(SB)/4, $0x0A09080B
|
||||
DATA ·incRotMatrix+0x1c(SB)/4, $0x0E0D0C0F
|
||||
GLOBL ·incRotMatrix(SB), NOPTR|RODATA, $32
|
||||
398
vendor/golang.org/x/crypto/chacha20/chacha_generic.go
generated
vendored
Normal file
398
vendor/golang.org/x/crypto/chacha20/chacha_generic.go
generated
vendored
Normal file
|
|
@ -0,0 +1,398 @@
|
|||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package chacha20 implements the ChaCha20 and XChaCha20 encryption algorithms
|
||||
// as specified in RFC 8439 and draft-irtf-cfrg-xchacha-01.
|
||||
package chacha20
|
||||
|
||||
import (
|
||||
"crypto/cipher"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"math/bits"
|
||||
|
||||
"golang.org/x/crypto/internal/alias"
|
||||
)
|
||||
|
||||
const (
|
||||
// KeySize is the size of the key used by this cipher, in bytes.
|
||||
KeySize = 32
|
||||
|
||||
// NonceSize is the size of the nonce used with the standard variant of this
|
||||
// cipher, in bytes.
|
||||
//
|
||||
// Note that this is too short to be safely generated at random if the same
|
||||
// key is reused more than 2³² times.
|
||||
NonceSize = 12
|
||||
|
||||
// NonceSizeX is the size of the nonce used with the XChaCha20 variant of
|
||||
// this cipher, in bytes.
|
||||
NonceSizeX = 24
|
||||
)
|
||||
|
||||
// Cipher is a stateful instance of ChaCha20 or XChaCha20 using a particular key
|
||||
// and nonce. A *Cipher implements the cipher.Stream interface.
|
||||
type Cipher struct {
|
||||
// The ChaCha20 state is 16 words: 4 constant, 8 of key, 1 of counter
|
||||
// (incremented after each block), and 3 of nonce.
|
||||
key [8]uint32
|
||||
counter uint32
|
||||
nonce [3]uint32
|
||||
|
||||
// The last len bytes of buf are leftover key stream bytes from the previous
|
||||
// XORKeyStream invocation. The size of buf depends on how many blocks are
|
||||
// computed at a time by xorKeyStreamBlocks.
|
||||
buf [bufSize]byte
|
||||
len int
|
||||
|
||||
// overflow is set when the counter overflowed, no more blocks can be
|
||||
// generated, and the next XORKeyStream call should panic.
|
||||
overflow bool
|
||||
|
||||
// The counter-independent results of the first round are cached after they
|
||||
// are computed the first time.
|
||||
precompDone bool
|
||||
p1, p5, p9, p13 uint32
|
||||
p2, p6, p10, p14 uint32
|
||||
p3, p7, p11, p15 uint32
|
||||
}
|
||||
|
||||
var _ cipher.Stream = (*Cipher)(nil)
|
||||
|
||||
// NewUnauthenticatedCipher creates a new ChaCha20 stream cipher with the given
|
||||
// 32 bytes key and a 12 or 24 bytes nonce. If a nonce of 24 bytes is provided,
|
||||
// the XChaCha20 construction will be used. It returns an error if key or nonce
|
||||
// have any other length.
|
||||
//
|
||||
// Note that ChaCha20, like all stream ciphers, is not authenticated and allows
|
||||
// attackers to silently tamper with the plaintext. For this reason, it is more
|
||||
// appropriate as a building block than as a standalone encryption mechanism.
|
||||
// Instead, consider using package golang.org/x/crypto/chacha20poly1305.
|
||||
func NewUnauthenticatedCipher(key, nonce []byte) (*Cipher, error) {
|
||||
// This function is split into a wrapper so that the Cipher allocation will
|
||||
// be inlined, and depending on how the caller uses the return value, won't
|
||||
// escape to the heap.
|
||||
c := &Cipher{}
|
||||
return newUnauthenticatedCipher(c, key, nonce)
|
||||
}
|
||||
|
||||
func newUnauthenticatedCipher(c *Cipher, key, nonce []byte) (*Cipher, error) {
|
||||
if len(key) != KeySize {
|
||||
return nil, errors.New("chacha20: wrong key size")
|
||||
}
|
||||
if len(nonce) == NonceSizeX {
|
||||
// XChaCha20 uses the ChaCha20 core to mix 16 bytes of the nonce into a
|
||||
// derived key, allowing it to operate on a nonce of 24 bytes. See
|
||||
// draft-irtf-cfrg-xchacha-01, Section 2.3.
|
||||
key, _ = HChaCha20(key, nonce[0:16])
|
||||
cNonce := make([]byte, NonceSize)
|
||||
copy(cNonce[4:12], nonce[16:24])
|
||||
nonce = cNonce
|
||||
} else if len(nonce) != NonceSize {
|
||||
return nil, errors.New("chacha20: wrong nonce size")
|
||||
}
|
||||
|
||||
key, nonce = key[:KeySize], nonce[:NonceSize] // bounds check elimination hint
|
||||
c.key = [8]uint32{
|
||||
binary.LittleEndian.Uint32(key[0:4]),
|
||||
binary.LittleEndian.Uint32(key[4:8]),
|
||||
binary.LittleEndian.Uint32(key[8:12]),
|
||||
binary.LittleEndian.Uint32(key[12:16]),
|
||||
binary.LittleEndian.Uint32(key[16:20]),
|
||||
binary.LittleEndian.Uint32(key[20:24]),
|
||||
binary.LittleEndian.Uint32(key[24:28]),
|
||||
binary.LittleEndian.Uint32(key[28:32]),
|
||||
}
|
||||
c.nonce = [3]uint32{
|
||||
binary.LittleEndian.Uint32(nonce[0:4]),
|
||||
binary.LittleEndian.Uint32(nonce[4:8]),
|
||||
binary.LittleEndian.Uint32(nonce[8:12]),
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// The constant first 4 words of the ChaCha20 state.
|
||||
const (
|
||||
j0 uint32 = 0x61707865 // expa
|
||||
j1 uint32 = 0x3320646e // nd 3
|
||||
j2 uint32 = 0x79622d32 // 2-by
|
||||
j3 uint32 = 0x6b206574 // te k
|
||||
)
|
||||
|
||||
const blockSize = 64
|
||||
|
||||
// quarterRound is the core of ChaCha20. It shuffles the bits of 4 state words.
|
||||
// It's executed 4 times for each of the 20 ChaCha20 rounds, operating on all 16
|
||||
// words each round, in columnar or diagonal groups of 4 at a time.
|
||||
func quarterRound(a, b, c, d uint32) (uint32, uint32, uint32, uint32) {
|
||||
a += b
|
||||
d ^= a
|
||||
d = bits.RotateLeft32(d, 16)
|
||||
c += d
|
||||
b ^= c
|
||||
b = bits.RotateLeft32(b, 12)
|
||||
a += b
|
||||
d ^= a
|
||||
d = bits.RotateLeft32(d, 8)
|
||||
c += d
|
||||
b ^= c
|
||||
b = bits.RotateLeft32(b, 7)
|
||||
return a, b, c, d
|
||||
}
|
||||
|
||||
// SetCounter sets the Cipher counter. The next invocation of XORKeyStream will
|
||||
// behave as if (64 * counter) bytes had been encrypted so far.
|
||||
//
|
||||
// To prevent accidental counter reuse, SetCounter panics if counter is less
|
||||
// than the current value.
|
||||
//
|
||||
// Note that the execution time of XORKeyStream is not independent of the
|
||||
// counter value.
|
||||
func (s *Cipher) SetCounter(counter uint32) {
|
||||
// Internally, s may buffer multiple blocks, which complicates this
|
||||
// implementation slightly. When checking whether the counter has rolled
|
||||
// back, we must use both s.counter and s.len to determine how many blocks
|
||||
// we have already output.
|
||||
outputCounter := s.counter - uint32(s.len)/blockSize
|
||||
if s.overflow || counter < outputCounter {
|
||||
panic("chacha20: SetCounter attempted to rollback counter")
|
||||
}
|
||||
|
||||
// In the general case, we set the new counter value and reset s.len to 0,
|
||||
// causing the next call to XORKeyStream to refill the buffer. However, if
|
||||
// we're advancing within the existing buffer, we can save work by simply
|
||||
// setting s.len.
|
||||
if counter < s.counter {
|
||||
s.len = int(s.counter-counter) * blockSize
|
||||
} else {
|
||||
s.counter = counter
|
||||
s.len = 0
|
||||
}
|
||||
}
|
||||
|
||||
// XORKeyStream XORs each byte in the given slice with a byte from the
|
||||
// cipher's key stream. Dst and src must overlap entirely or not at all.
|
||||
//
|
||||
// If len(dst) < len(src), XORKeyStream will panic. It is acceptable
|
||||
// to pass a dst bigger than src, and in that case, XORKeyStream will
|
||||
// only update dst[:len(src)] and will not touch the rest of dst.
|
||||
//
|
||||
// Multiple calls to XORKeyStream behave as if the concatenation of
|
||||
// the src buffers was passed in a single run. That is, Cipher
|
||||
// maintains state and does not reset at each XORKeyStream call.
|
||||
func (s *Cipher) XORKeyStream(dst, src []byte) {
|
||||
if len(src) == 0 {
|
||||
return
|
||||
}
|
||||
if len(dst) < len(src) {
|
||||
panic("chacha20: output smaller than input")
|
||||
}
|
||||
dst = dst[:len(src)]
|
||||
if alias.InexactOverlap(dst, src) {
|
||||
panic("chacha20: invalid buffer overlap")
|
||||
}
|
||||
|
||||
// First, drain any remaining key stream from a previous XORKeyStream.
|
||||
if s.len != 0 {
|
||||
keyStream := s.buf[bufSize-s.len:]
|
||||
if len(src) < len(keyStream) {
|
||||
keyStream = keyStream[:len(src)]
|
||||
}
|
||||
_ = src[len(keyStream)-1] // bounds check elimination hint
|
||||
for i, b := range keyStream {
|
||||
dst[i] = src[i] ^ b
|
||||
}
|
||||
s.len -= len(keyStream)
|
||||
dst, src = dst[len(keyStream):], src[len(keyStream):]
|
||||
}
|
||||
if len(src) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// If we'd need to let the counter overflow and keep generating output,
|
||||
// panic immediately. If instead we'd only reach the last block, remember
|
||||
// not to generate any more output after the buffer is drained.
|
||||
numBlocks := (uint64(len(src)) + blockSize - 1) / blockSize
|
||||
if s.overflow || uint64(s.counter)+numBlocks > 1<<32 {
|
||||
panic("chacha20: counter overflow")
|
||||
} else if uint64(s.counter)+numBlocks == 1<<32 {
|
||||
s.overflow = true
|
||||
}
|
||||
|
||||
// xorKeyStreamBlocks implementations expect input lengths that are a
|
||||
// multiple of bufSize. Platform-specific ones process multiple blocks at a
|
||||
// time, so have bufSizes that are a multiple of blockSize.
|
||||
|
||||
full := len(src) - len(src)%bufSize
|
||||
if full > 0 {
|
||||
s.xorKeyStreamBlocks(dst[:full], src[:full])
|
||||
}
|
||||
dst, src = dst[full:], src[full:]
|
||||
|
||||
// If using a multi-block xorKeyStreamBlocks would overflow, use the generic
|
||||
// one that does one block at a time.
|
||||
const blocksPerBuf = bufSize / blockSize
|
||||
if uint64(s.counter)+blocksPerBuf > 1<<32 {
|
||||
s.buf = [bufSize]byte{}
|
||||
numBlocks := (len(src) + blockSize - 1) / blockSize
|
||||
buf := s.buf[bufSize-numBlocks*blockSize:]
|
||||
copy(buf, src)
|
||||
s.xorKeyStreamBlocksGeneric(buf, buf)
|
||||
s.len = len(buf) - copy(dst, buf)
|
||||
return
|
||||
}
|
||||
|
||||
// If we have a partial (multi-)block, pad it for xorKeyStreamBlocks, and
|
||||
// keep the leftover keystream for the next XORKeyStream invocation.
|
||||
if len(src) > 0 {
|
||||
s.buf = [bufSize]byte{}
|
||||
copy(s.buf[:], src)
|
||||
s.xorKeyStreamBlocks(s.buf[:], s.buf[:])
|
||||
s.len = bufSize - copy(dst, s.buf[:])
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Cipher) xorKeyStreamBlocksGeneric(dst, src []byte) {
|
||||
if len(dst) != len(src) || len(dst)%blockSize != 0 {
|
||||
panic("chacha20: internal error: wrong dst and/or src length")
|
||||
}
|
||||
|
||||
// To generate each block of key stream, the initial cipher state
|
||||
// (represented below) is passed through 20 rounds of shuffling,
|
||||
// alternatively applying quarterRounds by columns (like 1, 5, 9, 13)
|
||||
// or by diagonals (like 1, 6, 11, 12).
|
||||
//
|
||||
// 0:cccccccc 1:cccccccc 2:cccccccc 3:cccccccc
|
||||
// 4:kkkkkkkk 5:kkkkkkkk 6:kkkkkkkk 7:kkkkkkkk
|
||||
// 8:kkkkkkkk 9:kkkkkkkk 10:kkkkkkkk 11:kkkkkkkk
|
||||
// 12:bbbbbbbb 13:nnnnnnnn 14:nnnnnnnn 15:nnnnnnnn
|
||||
//
|
||||
// c=constant k=key b=blockcount n=nonce
|
||||
var (
|
||||
c0, c1, c2, c3 = j0, j1, j2, j3
|
||||
c4, c5, c6, c7 = s.key[0], s.key[1], s.key[2], s.key[3]
|
||||
c8, c9, c10, c11 = s.key[4], s.key[5], s.key[6], s.key[7]
|
||||
_, c13, c14, c15 = s.counter, s.nonce[0], s.nonce[1], s.nonce[2]
|
||||
)
|
||||
|
||||
// Three quarters of the first round don't depend on the counter, so we can
|
||||
// calculate them here, and reuse them for multiple blocks in the loop, and
|
||||
// for future XORKeyStream invocations.
|
||||
if !s.precompDone {
|
||||
s.p1, s.p5, s.p9, s.p13 = quarterRound(c1, c5, c9, c13)
|
||||
s.p2, s.p6, s.p10, s.p14 = quarterRound(c2, c6, c10, c14)
|
||||
s.p3, s.p7, s.p11, s.p15 = quarterRound(c3, c7, c11, c15)
|
||||
s.precompDone = true
|
||||
}
|
||||
|
||||
// A condition of len(src) > 0 would be sufficient, but this also
|
||||
// acts as a bounds check elimination hint.
|
||||
for len(src) >= 64 && len(dst) >= 64 {
|
||||
// The remainder of the first column round.
|
||||
fcr0, fcr4, fcr8, fcr12 := quarterRound(c0, c4, c8, s.counter)
|
||||
|
||||
// The second diagonal round.
|
||||
x0, x5, x10, x15 := quarterRound(fcr0, s.p5, s.p10, s.p15)
|
||||
x1, x6, x11, x12 := quarterRound(s.p1, s.p6, s.p11, fcr12)
|
||||
x2, x7, x8, x13 := quarterRound(s.p2, s.p7, fcr8, s.p13)
|
||||
x3, x4, x9, x14 := quarterRound(s.p3, fcr4, s.p9, s.p14)
|
||||
|
||||
// The remaining 18 rounds.
|
||||
for i := 0; i < 9; i++ {
|
||||
// Column round.
|
||||
x0, x4, x8, x12 = quarterRound(x0, x4, x8, x12)
|
||||
x1, x5, x9, x13 = quarterRound(x1, x5, x9, x13)
|
||||
x2, x6, x10, x14 = quarterRound(x2, x6, x10, x14)
|
||||
x3, x7, x11, x15 = quarterRound(x3, x7, x11, x15)
|
||||
|
||||
// Diagonal round.
|
||||
x0, x5, x10, x15 = quarterRound(x0, x5, x10, x15)
|
||||
x1, x6, x11, x12 = quarterRound(x1, x6, x11, x12)
|
||||
x2, x7, x8, x13 = quarterRound(x2, x7, x8, x13)
|
||||
x3, x4, x9, x14 = quarterRound(x3, x4, x9, x14)
|
||||
}
|
||||
|
||||
// Add back the initial state to generate the key stream, then
|
||||
// XOR the key stream with the source and write out the result.
|
||||
addXor(dst[0:4], src[0:4], x0, c0)
|
||||
addXor(dst[4:8], src[4:8], x1, c1)
|
||||
addXor(dst[8:12], src[8:12], x2, c2)
|
||||
addXor(dst[12:16], src[12:16], x3, c3)
|
||||
addXor(dst[16:20], src[16:20], x4, c4)
|
||||
addXor(dst[20:24], src[20:24], x5, c5)
|
||||
addXor(dst[24:28], src[24:28], x6, c6)
|
||||
addXor(dst[28:32], src[28:32], x7, c7)
|
||||
addXor(dst[32:36], src[32:36], x8, c8)
|
||||
addXor(dst[36:40], src[36:40], x9, c9)
|
||||
addXor(dst[40:44], src[40:44], x10, c10)
|
||||
addXor(dst[44:48], src[44:48], x11, c11)
|
||||
addXor(dst[48:52], src[48:52], x12, s.counter)
|
||||
addXor(dst[52:56], src[52:56], x13, c13)
|
||||
addXor(dst[56:60], src[56:60], x14, c14)
|
||||
addXor(dst[60:64], src[60:64], x15, c15)
|
||||
|
||||
s.counter += 1
|
||||
|
||||
src, dst = src[blockSize:], dst[blockSize:]
|
||||
}
|
||||
}
|
||||
|
||||
// HChaCha20 uses the ChaCha20 core to generate a derived key from a 32 bytes
|
||||
// key and a 16 bytes nonce. It returns an error if key or nonce have any other
|
||||
// length. It is used as part of the XChaCha20 construction.
|
||||
func HChaCha20(key, nonce []byte) ([]byte, error) {
|
||||
// This function is split into a wrapper so that the slice allocation will
|
||||
// be inlined, and depending on how the caller uses the return value, won't
|
||||
// escape to the heap.
|
||||
out := make([]byte, 32)
|
||||
return hChaCha20(out, key, nonce)
|
||||
}
|
||||
|
||||
func hChaCha20(out, key, nonce []byte) ([]byte, error) {
|
||||
if len(key) != KeySize {
|
||||
return nil, errors.New("chacha20: wrong HChaCha20 key size")
|
||||
}
|
||||
if len(nonce) != 16 {
|
||||
return nil, errors.New("chacha20: wrong HChaCha20 nonce size")
|
||||
}
|
||||
|
||||
x0, x1, x2, x3 := j0, j1, j2, j3
|
||||
x4 := binary.LittleEndian.Uint32(key[0:4])
|
||||
x5 := binary.LittleEndian.Uint32(key[4:8])
|
||||
x6 := binary.LittleEndian.Uint32(key[8:12])
|
||||
x7 := binary.LittleEndian.Uint32(key[12:16])
|
||||
x8 := binary.LittleEndian.Uint32(key[16:20])
|
||||
x9 := binary.LittleEndian.Uint32(key[20:24])
|
||||
x10 := binary.LittleEndian.Uint32(key[24:28])
|
||||
x11 := binary.LittleEndian.Uint32(key[28:32])
|
||||
x12 := binary.LittleEndian.Uint32(nonce[0:4])
|
||||
x13 := binary.LittleEndian.Uint32(nonce[4:8])
|
||||
x14 := binary.LittleEndian.Uint32(nonce[8:12])
|
||||
x15 := binary.LittleEndian.Uint32(nonce[12:16])
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
// Diagonal round.
|
||||
x0, x4, x8, x12 = quarterRound(x0, x4, x8, x12)
|
||||
x1, x5, x9, x13 = quarterRound(x1, x5, x9, x13)
|
||||
x2, x6, x10, x14 = quarterRound(x2, x6, x10, x14)
|
||||
x3, x7, x11, x15 = quarterRound(x3, x7, x11, x15)
|
||||
|
||||
// Column round.
|
||||
x0, x5, x10, x15 = quarterRound(x0, x5, x10, x15)
|
||||
x1, x6, x11, x12 = quarterRound(x1, x6, x11, x12)
|
||||
x2, x7, x8, x13 = quarterRound(x2, x7, x8, x13)
|
||||
x3, x4, x9, x14 = quarterRound(x3, x4, x9, x14)
|
||||
}
|
||||
|
||||
_ = out[31] // bounds check elimination hint
|
||||
binary.LittleEndian.PutUint32(out[0:4], x0)
|
||||
binary.LittleEndian.PutUint32(out[4:8], x1)
|
||||
binary.LittleEndian.PutUint32(out[8:12], x2)
|
||||
binary.LittleEndian.PutUint32(out[12:16], x3)
|
||||
binary.LittleEndian.PutUint32(out[16:20], x12)
|
||||
binary.LittleEndian.PutUint32(out[20:24], x13)
|
||||
binary.LittleEndian.PutUint32(out[24:28], x14)
|
||||
binary.LittleEndian.PutUint32(out[28:32], x15)
|
||||
return out, nil
|
||||
}
|
||||
13
vendor/golang.org/x/crypto/chacha20/chacha_noasm.go
generated
vendored
Normal file
13
vendor/golang.org/x/crypto/chacha20/chacha_noasm.go
generated
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build (!arm64 && !s390x && !ppc64 && !ppc64le) || !gc || purego
|
||||
|
||||
package chacha20
|
||||
|
||||
const bufSize = blockSize
|
||||
|
||||
func (s *Cipher) xorKeyStreamBlocks(dst, src []byte) {
|
||||
s.xorKeyStreamBlocksGeneric(dst, src)
|
||||
}
|
||||
16
vendor/golang.org/x/crypto/chacha20/chacha_ppc64x.go
generated
vendored
Normal file
16
vendor/golang.org/x/crypto/chacha20/chacha_ppc64x.go
generated
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build gc && !purego && (ppc64 || ppc64le)
|
||||
|
||||
package chacha20
|
||||
|
||||
const bufSize = 256
|
||||
|
||||
//go:noescape
|
||||
func chaCha20_ctr32_vsx(out, inp *byte, len int, key *[8]uint32, counter *uint32)
|
||||
|
||||
func (c *Cipher) xorKeyStreamBlocks(dst, src []byte) {
|
||||
chaCha20_ctr32_vsx(&dst[0], &src[0], len(src), &c.key, &c.counter)
|
||||
}
|
||||
501
vendor/golang.org/x/crypto/chacha20/chacha_ppc64x.s
generated
vendored
Normal file
501
vendor/golang.org/x/crypto/chacha20/chacha_ppc64x.s
generated
vendored
Normal file
|
|
@ -0,0 +1,501 @@
|
|||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Based on CRYPTOGAMS code with the following comment:
|
||||
// # ====================================================================
|
||||
// # Written by Andy Polyakov <appro@openssl.org> for the OpenSSL
|
||||
// # project. The module is, however, dual licensed under OpenSSL and
|
||||
// # CRYPTOGAMS licenses depending on where you obtain it. For further
|
||||
// # details see http://www.openssl.org/~appro/cryptogams/.
|
||||
// # ====================================================================
|
||||
|
||||
// Code for the perl script that generates the ppc64 assembler
|
||||
// can be found in the cryptogams repository at the link below. It is based on
|
||||
// the original from openssl.
|
||||
|
||||
// https://github.com/dot-asm/cryptogams/commit/a60f5b50ed908e91
|
||||
|
||||
// The differences in this and the original implementation are
|
||||
// due to the calling conventions and initialization of constants.
|
||||
|
||||
//go:build gc && !purego && (ppc64 || ppc64le)
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
#define OUT R3
|
||||
#define INP R4
|
||||
#define LEN R5
|
||||
#define KEY R6
|
||||
#define CNT R7
|
||||
#define TMP R15
|
||||
|
||||
#define CONSTBASE R16
|
||||
#define BLOCKS R17
|
||||
|
||||
// for VPERMXOR
|
||||
#define MASK R18
|
||||
|
||||
DATA consts<>+0x00(SB)/4, $0x61707865
|
||||
DATA consts<>+0x04(SB)/4, $0x3320646e
|
||||
DATA consts<>+0x08(SB)/4, $0x79622d32
|
||||
DATA consts<>+0x0c(SB)/4, $0x6b206574
|
||||
DATA consts<>+0x10(SB)/4, $0x00000001
|
||||
DATA consts<>+0x14(SB)/4, $0x00000000
|
||||
DATA consts<>+0x18(SB)/4, $0x00000000
|
||||
DATA consts<>+0x1c(SB)/4, $0x00000000
|
||||
DATA consts<>+0x20(SB)/4, $0x00000004
|
||||
DATA consts<>+0x24(SB)/4, $0x00000000
|
||||
DATA consts<>+0x28(SB)/4, $0x00000000
|
||||
DATA consts<>+0x2c(SB)/4, $0x00000000
|
||||
DATA consts<>+0x30(SB)/4, $0x0e0f0c0d
|
||||
DATA consts<>+0x34(SB)/4, $0x0a0b0809
|
||||
DATA consts<>+0x38(SB)/4, $0x06070405
|
||||
DATA consts<>+0x3c(SB)/4, $0x02030001
|
||||
DATA consts<>+0x40(SB)/4, $0x0d0e0f0c
|
||||
DATA consts<>+0x44(SB)/4, $0x090a0b08
|
||||
DATA consts<>+0x48(SB)/4, $0x05060704
|
||||
DATA consts<>+0x4c(SB)/4, $0x01020300
|
||||
DATA consts<>+0x50(SB)/4, $0x61707865
|
||||
DATA consts<>+0x54(SB)/4, $0x61707865
|
||||
DATA consts<>+0x58(SB)/4, $0x61707865
|
||||
DATA consts<>+0x5c(SB)/4, $0x61707865
|
||||
DATA consts<>+0x60(SB)/4, $0x3320646e
|
||||
DATA consts<>+0x64(SB)/4, $0x3320646e
|
||||
DATA consts<>+0x68(SB)/4, $0x3320646e
|
||||
DATA consts<>+0x6c(SB)/4, $0x3320646e
|
||||
DATA consts<>+0x70(SB)/4, $0x79622d32
|
||||
DATA consts<>+0x74(SB)/4, $0x79622d32
|
||||
DATA consts<>+0x78(SB)/4, $0x79622d32
|
||||
DATA consts<>+0x7c(SB)/4, $0x79622d32
|
||||
DATA consts<>+0x80(SB)/4, $0x6b206574
|
||||
DATA consts<>+0x84(SB)/4, $0x6b206574
|
||||
DATA consts<>+0x88(SB)/4, $0x6b206574
|
||||
DATA consts<>+0x8c(SB)/4, $0x6b206574
|
||||
DATA consts<>+0x90(SB)/4, $0x00000000
|
||||
DATA consts<>+0x94(SB)/4, $0x00000001
|
||||
DATA consts<>+0x98(SB)/4, $0x00000002
|
||||
DATA consts<>+0x9c(SB)/4, $0x00000003
|
||||
DATA consts<>+0xa0(SB)/4, $0x11223300
|
||||
DATA consts<>+0xa4(SB)/4, $0x55667744
|
||||
DATA consts<>+0xa8(SB)/4, $0x99aabb88
|
||||
DATA consts<>+0xac(SB)/4, $0xddeeffcc
|
||||
DATA consts<>+0xb0(SB)/4, $0x22330011
|
||||
DATA consts<>+0xb4(SB)/4, $0x66774455
|
||||
DATA consts<>+0xb8(SB)/4, $0xaabb8899
|
||||
DATA consts<>+0xbc(SB)/4, $0xeeffccdd
|
||||
GLOBL consts<>(SB), RODATA, $0xc0
|
||||
|
||||
#ifdef GOARCH_ppc64
|
||||
#define BE_XXBRW_INIT() \
|
||||
LVSL (R0)(R0), V24 \
|
||||
VSPLTISB $3, V25 \
|
||||
VXOR V24, V25, V24 \
|
||||
|
||||
#define BE_XXBRW(vr) VPERM vr, vr, V24, vr
|
||||
#else
|
||||
#define BE_XXBRW_INIT()
|
||||
#define BE_XXBRW(vr)
|
||||
#endif
|
||||
|
||||
//func chaCha20_ctr32_vsx(out, inp *byte, len int, key *[8]uint32, counter *uint32)
|
||||
TEXT ·chaCha20_ctr32_vsx(SB),NOSPLIT,$64-40
|
||||
MOVD out+0(FP), OUT
|
||||
MOVD inp+8(FP), INP
|
||||
MOVD len+16(FP), LEN
|
||||
MOVD key+24(FP), KEY
|
||||
MOVD counter+32(FP), CNT
|
||||
|
||||
// Addressing for constants
|
||||
MOVD $consts<>+0x00(SB), CONSTBASE
|
||||
MOVD $16, R8
|
||||
MOVD $32, R9
|
||||
MOVD $48, R10
|
||||
MOVD $64, R11
|
||||
SRD $6, LEN, BLOCKS
|
||||
// for VPERMXOR
|
||||
MOVD $consts<>+0xa0(SB), MASK
|
||||
MOVD $16, R20
|
||||
// V16
|
||||
LXVW4X (CONSTBASE)(R0), VS48
|
||||
ADD $80,CONSTBASE
|
||||
|
||||
// Load key into V17,V18
|
||||
LXVW4X (KEY)(R0), VS49
|
||||
LXVW4X (KEY)(R8), VS50
|
||||
|
||||
// Load CNT, NONCE into V19
|
||||
LXVW4X (CNT)(R0), VS51
|
||||
|
||||
// Clear V27
|
||||
VXOR V27, V27, V27
|
||||
|
||||
BE_XXBRW_INIT()
|
||||
|
||||
// V28
|
||||
LXVW4X (CONSTBASE)(R11), VS60
|
||||
|
||||
// Load mask constants for VPERMXOR
|
||||
LXVW4X (MASK)(R0), V20
|
||||
LXVW4X (MASK)(R20), V21
|
||||
|
||||
// splat slot from V19 -> V26
|
||||
VSPLTW $0, V19, V26
|
||||
|
||||
VSLDOI $4, V19, V27, V19
|
||||
VSLDOI $12, V27, V19, V19
|
||||
|
||||
VADDUWM V26, V28, V26
|
||||
|
||||
MOVD $10, R14
|
||||
MOVD R14, CTR
|
||||
PCALIGN $16
|
||||
loop_outer_vsx:
|
||||
// V0, V1, V2, V3
|
||||
LXVW4X (R0)(CONSTBASE), VS32
|
||||
LXVW4X (R8)(CONSTBASE), VS33
|
||||
LXVW4X (R9)(CONSTBASE), VS34
|
||||
LXVW4X (R10)(CONSTBASE), VS35
|
||||
|
||||
// splat values from V17, V18 into V4-V11
|
||||
VSPLTW $0, V17, V4
|
||||
VSPLTW $1, V17, V5
|
||||
VSPLTW $2, V17, V6
|
||||
VSPLTW $3, V17, V7
|
||||
VSPLTW $0, V18, V8
|
||||
VSPLTW $1, V18, V9
|
||||
VSPLTW $2, V18, V10
|
||||
VSPLTW $3, V18, V11
|
||||
|
||||
// VOR
|
||||
VOR V26, V26, V12
|
||||
|
||||
// splat values from V19 -> V13, V14, V15
|
||||
VSPLTW $1, V19, V13
|
||||
VSPLTW $2, V19, V14
|
||||
VSPLTW $3, V19, V15
|
||||
|
||||
// splat const values
|
||||
VSPLTISW $-16, V27
|
||||
VSPLTISW $12, V28
|
||||
VSPLTISW $8, V29
|
||||
VSPLTISW $7, V30
|
||||
PCALIGN $16
|
||||
loop_vsx:
|
||||
VADDUWM V0, V4, V0
|
||||
VADDUWM V1, V5, V1
|
||||
VADDUWM V2, V6, V2
|
||||
VADDUWM V3, V7, V3
|
||||
|
||||
VPERMXOR V12, V0, V21, V12
|
||||
VPERMXOR V13, V1, V21, V13
|
||||
VPERMXOR V14, V2, V21, V14
|
||||
VPERMXOR V15, V3, V21, V15
|
||||
|
||||
VADDUWM V8, V12, V8
|
||||
VADDUWM V9, V13, V9
|
||||
VADDUWM V10, V14, V10
|
||||
VADDUWM V11, V15, V11
|
||||
|
||||
VXOR V4, V8, V4
|
||||
VXOR V5, V9, V5
|
||||
VXOR V6, V10, V6
|
||||
VXOR V7, V11, V7
|
||||
|
||||
VRLW V4, V28, V4
|
||||
VRLW V5, V28, V5
|
||||
VRLW V6, V28, V6
|
||||
VRLW V7, V28, V7
|
||||
|
||||
VADDUWM V0, V4, V0
|
||||
VADDUWM V1, V5, V1
|
||||
VADDUWM V2, V6, V2
|
||||
VADDUWM V3, V7, V3
|
||||
|
||||
VPERMXOR V12, V0, V20, V12
|
||||
VPERMXOR V13, V1, V20, V13
|
||||
VPERMXOR V14, V2, V20, V14
|
||||
VPERMXOR V15, V3, V20, V15
|
||||
|
||||
VADDUWM V8, V12, V8
|
||||
VADDUWM V9, V13, V9
|
||||
VADDUWM V10, V14, V10
|
||||
VADDUWM V11, V15, V11
|
||||
|
||||
VXOR V4, V8, V4
|
||||
VXOR V5, V9, V5
|
||||
VXOR V6, V10, V6
|
||||
VXOR V7, V11, V7
|
||||
|
||||
VRLW V4, V30, V4
|
||||
VRLW V5, V30, V5
|
||||
VRLW V6, V30, V6
|
||||
VRLW V7, V30, V7
|
||||
|
||||
VADDUWM V0, V5, V0
|
||||
VADDUWM V1, V6, V1
|
||||
VADDUWM V2, V7, V2
|
||||
VADDUWM V3, V4, V3
|
||||
|
||||
VPERMXOR V15, V0, V21, V15
|
||||
VPERMXOR V12, V1, V21, V12
|
||||
VPERMXOR V13, V2, V21, V13
|
||||
VPERMXOR V14, V3, V21, V14
|
||||
|
||||
VADDUWM V10, V15, V10
|
||||
VADDUWM V11, V12, V11
|
||||
VADDUWM V8, V13, V8
|
||||
VADDUWM V9, V14, V9
|
||||
|
||||
VXOR V5, V10, V5
|
||||
VXOR V6, V11, V6
|
||||
VXOR V7, V8, V7
|
||||
VXOR V4, V9, V4
|
||||
|
||||
VRLW V5, V28, V5
|
||||
VRLW V6, V28, V6
|
||||
VRLW V7, V28, V7
|
||||
VRLW V4, V28, V4
|
||||
|
||||
VADDUWM V0, V5, V0
|
||||
VADDUWM V1, V6, V1
|
||||
VADDUWM V2, V7, V2
|
||||
VADDUWM V3, V4, V3
|
||||
|
||||
VPERMXOR V15, V0, V20, V15
|
||||
VPERMXOR V12, V1, V20, V12
|
||||
VPERMXOR V13, V2, V20, V13
|
||||
VPERMXOR V14, V3, V20, V14
|
||||
|
||||
VADDUWM V10, V15, V10
|
||||
VADDUWM V11, V12, V11
|
||||
VADDUWM V8, V13, V8
|
||||
VADDUWM V9, V14, V9
|
||||
|
||||
VXOR V5, V10, V5
|
||||
VXOR V6, V11, V6
|
||||
VXOR V7, V8, V7
|
||||
VXOR V4, V9, V4
|
||||
|
||||
VRLW V5, V30, V5
|
||||
VRLW V6, V30, V6
|
||||
VRLW V7, V30, V7
|
||||
VRLW V4, V30, V4
|
||||
BDNZ loop_vsx
|
||||
|
||||
VADDUWM V12, V26, V12
|
||||
|
||||
VMRGEW V0, V1, V27
|
||||
VMRGEW V2, V3, V28
|
||||
|
||||
VMRGOW V0, V1, V0
|
||||
VMRGOW V2, V3, V2
|
||||
|
||||
VMRGEW V4, V5, V29
|
||||
VMRGEW V6, V7, V30
|
||||
|
||||
XXPERMDI VS32, VS34, $0, VS33
|
||||
XXPERMDI VS32, VS34, $3, VS35
|
||||
XXPERMDI VS59, VS60, $0, VS32
|
||||
XXPERMDI VS59, VS60, $3, VS34
|
||||
|
||||
VMRGOW V4, V5, V4
|
||||
VMRGOW V6, V7, V6
|
||||
|
||||
VMRGEW V8, V9, V27
|
||||
VMRGEW V10, V11, V28
|
||||
|
||||
XXPERMDI VS36, VS38, $0, VS37
|
||||
XXPERMDI VS36, VS38, $3, VS39
|
||||
XXPERMDI VS61, VS62, $0, VS36
|
||||
XXPERMDI VS61, VS62, $3, VS38
|
||||
|
||||
VMRGOW V8, V9, V8
|
||||
VMRGOW V10, V11, V10
|
||||
|
||||
VMRGEW V12, V13, V29
|
||||
VMRGEW V14, V15, V30
|
||||
|
||||
XXPERMDI VS40, VS42, $0, VS41
|
||||
XXPERMDI VS40, VS42, $3, VS43
|
||||
XXPERMDI VS59, VS60, $0, VS40
|
||||
XXPERMDI VS59, VS60, $3, VS42
|
||||
|
||||
VMRGOW V12, V13, V12
|
||||
VMRGOW V14, V15, V14
|
||||
|
||||
VSPLTISW $4, V27
|
||||
VADDUWM V26, V27, V26
|
||||
|
||||
XXPERMDI VS44, VS46, $0, VS45
|
||||
XXPERMDI VS44, VS46, $3, VS47
|
||||
XXPERMDI VS61, VS62, $0, VS44
|
||||
XXPERMDI VS61, VS62, $3, VS46
|
||||
|
||||
VADDUWM V0, V16, V0
|
||||
VADDUWM V4, V17, V4
|
||||
VADDUWM V8, V18, V8
|
||||
VADDUWM V12, V19, V12
|
||||
|
||||
BE_XXBRW(V0)
|
||||
BE_XXBRW(V4)
|
||||
BE_XXBRW(V8)
|
||||
BE_XXBRW(V12)
|
||||
|
||||
CMPU LEN, $64
|
||||
BLT tail_vsx
|
||||
|
||||
// Bottom of loop
|
||||
LXVW4X (INP)(R0), VS59
|
||||
LXVW4X (INP)(R8), VS60
|
||||
LXVW4X (INP)(R9), VS61
|
||||
LXVW4X (INP)(R10), VS62
|
||||
|
||||
VXOR V27, V0, V27
|
||||
VXOR V28, V4, V28
|
||||
VXOR V29, V8, V29
|
||||
VXOR V30, V12, V30
|
||||
|
||||
STXVW4X VS59, (OUT)(R0)
|
||||
STXVW4X VS60, (OUT)(R8)
|
||||
ADD $64, INP
|
||||
STXVW4X VS61, (OUT)(R9)
|
||||
ADD $-64, LEN
|
||||
STXVW4X VS62, (OUT)(R10)
|
||||
ADD $64, OUT
|
||||
BEQ done_vsx
|
||||
|
||||
VADDUWM V1, V16, V0
|
||||
VADDUWM V5, V17, V4
|
||||
VADDUWM V9, V18, V8
|
||||
VADDUWM V13, V19, V12
|
||||
|
||||
BE_XXBRW(V0)
|
||||
BE_XXBRW(V4)
|
||||
BE_XXBRW(V8)
|
||||
BE_XXBRW(V12)
|
||||
|
||||
CMPU LEN, $64
|
||||
BLT tail_vsx
|
||||
|
||||
LXVW4X (INP)(R0), VS59
|
||||
LXVW4X (INP)(R8), VS60
|
||||
LXVW4X (INP)(R9), VS61
|
||||
LXVW4X (INP)(R10), VS62
|
||||
|
||||
VXOR V27, V0, V27
|
||||
VXOR V28, V4, V28
|
||||
VXOR V29, V8, V29
|
||||
VXOR V30, V12, V30
|
||||
|
||||
STXVW4X VS59, (OUT)(R0)
|
||||
STXVW4X VS60, (OUT)(R8)
|
||||
ADD $64, INP
|
||||
STXVW4X VS61, (OUT)(R9)
|
||||
ADD $-64, LEN
|
||||
STXVW4X VS62, (OUT)(V10)
|
||||
ADD $64, OUT
|
||||
BEQ done_vsx
|
||||
|
||||
VADDUWM V2, V16, V0
|
||||
VADDUWM V6, V17, V4
|
||||
VADDUWM V10, V18, V8
|
||||
VADDUWM V14, V19, V12
|
||||
|
||||
BE_XXBRW(V0)
|
||||
BE_XXBRW(V4)
|
||||
BE_XXBRW(V8)
|
||||
BE_XXBRW(V12)
|
||||
|
||||
CMPU LEN, $64
|
||||
BLT tail_vsx
|
||||
|
||||
LXVW4X (INP)(R0), VS59
|
||||
LXVW4X (INP)(R8), VS60
|
||||
LXVW4X (INP)(R9), VS61
|
||||
LXVW4X (INP)(R10), VS62
|
||||
|
||||
VXOR V27, V0, V27
|
||||
VXOR V28, V4, V28
|
||||
VXOR V29, V8, V29
|
||||
VXOR V30, V12, V30
|
||||
|
||||
STXVW4X VS59, (OUT)(R0)
|
||||
STXVW4X VS60, (OUT)(R8)
|
||||
ADD $64, INP
|
||||
STXVW4X VS61, (OUT)(R9)
|
||||
ADD $-64, LEN
|
||||
STXVW4X VS62, (OUT)(R10)
|
||||
ADD $64, OUT
|
||||
BEQ done_vsx
|
||||
|
||||
VADDUWM V3, V16, V0
|
||||
VADDUWM V7, V17, V4
|
||||
VADDUWM V11, V18, V8
|
||||
VADDUWM V15, V19, V12
|
||||
|
||||
BE_XXBRW(V0)
|
||||
BE_XXBRW(V4)
|
||||
BE_XXBRW(V8)
|
||||
BE_XXBRW(V12)
|
||||
|
||||
CMPU LEN, $64
|
||||
BLT tail_vsx
|
||||
|
||||
LXVW4X (INP)(R0), VS59
|
||||
LXVW4X (INP)(R8), VS60
|
||||
LXVW4X (INP)(R9), VS61
|
||||
LXVW4X (INP)(R10), VS62
|
||||
|
||||
VXOR V27, V0, V27
|
||||
VXOR V28, V4, V28
|
||||
VXOR V29, V8, V29
|
||||
VXOR V30, V12, V30
|
||||
|
||||
STXVW4X VS59, (OUT)(R0)
|
||||
STXVW4X VS60, (OUT)(R8)
|
||||
ADD $64, INP
|
||||
STXVW4X VS61, (OUT)(R9)
|
||||
ADD $-64, LEN
|
||||
STXVW4X VS62, (OUT)(R10)
|
||||
ADD $64, OUT
|
||||
|
||||
MOVD $10, R14
|
||||
MOVD R14, CTR
|
||||
BNE loop_outer_vsx
|
||||
|
||||
done_vsx:
|
||||
// Increment counter by number of 64 byte blocks
|
||||
MOVWZ (CNT), R14
|
||||
ADD BLOCKS, R14
|
||||
MOVWZ R14, (CNT)
|
||||
RET
|
||||
|
||||
tail_vsx:
|
||||
ADD $32, R1, R11
|
||||
MOVD LEN, CTR
|
||||
|
||||
// Save values on stack to copy from
|
||||
STXVW4X VS32, (R11)(R0)
|
||||
STXVW4X VS36, (R11)(R8)
|
||||
STXVW4X VS40, (R11)(R9)
|
||||
STXVW4X VS44, (R11)(R10)
|
||||
ADD $-1, R11, R12
|
||||
ADD $-1, INP
|
||||
ADD $-1, OUT
|
||||
PCALIGN $16
|
||||
looptail_vsx:
|
||||
// Copying the result to OUT
|
||||
// in bytes.
|
||||
MOVBZU 1(R12), KEY
|
||||
MOVBZU 1(INP), TMP
|
||||
XOR KEY, TMP, KEY
|
||||
MOVBU KEY, 1(OUT)
|
||||
BDNZ looptail_vsx
|
||||
|
||||
// Clear the stack values
|
||||
STXVW4X VS48, (R11)(R0)
|
||||
STXVW4X VS48, (R11)(R8)
|
||||
STXVW4X VS48, (R11)(R9)
|
||||
STXVW4X VS48, (R11)(R10)
|
||||
BR done_vsx
|
||||
27
vendor/golang.org/x/crypto/chacha20/chacha_s390x.go
generated
vendored
Normal file
27
vendor/golang.org/x/crypto/chacha20/chacha_s390x.go
generated
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build gc && !purego
|
||||
|
||||
package chacha20
|
||||
|
||||
import "golang.org/x/sys/cpu"
|
||||
|
||||
var haveAsm = cpu.S390X.HasVX
|
||||
|
||||
const bufSize = 256
|
||||
|
||||
// xorKeyStreamVX is an assembly implementation of XORKeyStream. It must only
|
||||
// be called when the vector facility is available. Implementation in asm_s390x.s.
|
||||
//
|
||||
//go:noescape
|
||||
func xorKeyStreamVX(dst, src []byte, key *[8]uint32, nonce *[3]uint32, counter *uint32)
|
||||
|
||||
func (c *Cipher) xorKeyStreamBlocks(dst, src []byte) {
|
||||
if cpu.S390X.HasVX {
|
||||
xorKeyStreamVX(dst, src, &c.key, &c.nonce, &c.counter)
|
||||
} else {
|
||||
c.xorKeyStreamBlocksGeneric(dst, src)
|
||||
}
|
||||
}
|
||||
224
vendor/golang.org/x/crypto/chacha20/chacha_s390x.s
generated
vendored
Normal file
224
vendor/golang.org/x/crypto/chacha20/chacha_s390x.s
generated
vendored
Normal file
|
|
@ -0,0 +1,224 @@
|
|||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build gc && !purego
|
||||
|
||||
#include "go_asm.h"
|
||||
#include "textflag.h"
|
||||
|
||||
// This is an implementation of the ChaCha20 encryption algorithm as
|
||||
// specified in RFC 7539. It uses vector instructions to compute
|
||||
// 4 keystream blocks in parallel (256 bytes) which are then XORed
|
||||
// with the bytes in the input slice.
|
||||
|
||||
GLOBL ·constants<>(SB), RODATA|NOPTR, $32
|
||||
// BSWAP: swap bytes in each 4-byte element
|
||||
DATA ·constants<>+0x00(SB)/4, $0x03020100
|
||||
DATA ·constants<>+0x04(SB)/4, $0x07060504
|
||||
DATA ·constants<>+0x08(SB)/4, $0x0b0a0908
|
||||
DATA ·constants<>+0x0c(SB)/4, $0x0f0e0d0c
|
||||
// J0: [j0, j1, j2, j3]
|
||||
DATA ·constants<>+0x10(SB)/4, $0x61707865
|
||||
DATA ·constants<>+0x14(SB)/4, $0x3320646e
|
||||
DATA ·constants<>+0x18(SB)/4, $0x79622d32
|
||||
DATA ·constants<>+0x1c(SB)/4, $0x6b206574
|
||||
|
||||
#define BSWAP V5
|
||||
#define J0 V6
|
||||
#define KEY0 V7
|
||||
#define KEY1 V8
|
||||
#define NONCE V9
|
||||
#define CTR V10
|
||||
#define M0 V11
|
||||
#define M1 V12
|
||||
#define M2 V13
|
||||
#define M3 V14
|
||||
#define INC V15
|
||||
#define X0 V16
|
||||
#define X1 V17
|
||||
#define X2 V18
|
||||
#define X3 V19
|
||||
#define X4 V20
|
||||
#define X5 V21
|
||||
#define X6 V22
|
||||
#define X7 V23
|
||||
#define X8 V24
|
||||
#define X9 V25
|
||||
#define X10 V26
|
||||
#define X11 V27
|
||||
#define X12 V28
|
||||
#define X13 V29
|
||||
#define X14 V30
|
||||
#define X15 V31
|
||||
|
||||
#define NUM_ROUNDS 20
|
||||
|
||||
#define ROUND4(a0, a1, a2, a3, b0, b1, b2, b3, c0, c1, c2, c3, d0, d1, d2, d3) \
|
||||
VAF a1, a0, a0 \
|
||||
VAF b1, b0, b0 \
|
||||
VAF c1, c0, c0 \
|
||||
VAF d1, d0, d0 \
|
||||
VX a0, a2, a2 \
|
||||
VX b0, b2, b2 \
|
||||
VX c0, c2, c2 \
|
||||
VX d0, d2, d2 \
|
||||
VERLLF $16, a2, a2 \
|
||||
VERLLF $16, b2, b2 \
|
||||
VERLLF $16, c2, c2 \
|
||||
VERLLF $16, d2, d2 \
|
||||
VAF a2, a3, a3 \
|
||||
VAF b2, b3, b3 \
|
||||
VAF c2, c3, c3 \
|
||||
VAF d2, d3, d3 \
|
||||
VX a3, a1, a1 \
|
||||
VX b3, b1, b1 \
|
||||
VX c3, c1, c1 \
|
||||
VX d3, d1, d1 \
|
||||
VERLLF $12, a1, a1 \
|
||||
VERLLF $12, b1, b1 \
|
||||
VERLLF $12, c1, c1 \
|
||||
VERLLF $12, d1, d1 \
|
||||
VAF a1, a0, a0 \
|
||||
VAF b1, b0, b0 \
|
||||
VAF c1, c0, c0 \
|
||||
VAF d1, d0, d0 \
|
||||
VX a0, a2, a2 \
|
||||
VX b0, b2, b2 \
|
||||
VX c0, c2, c2 \
|
||||
VX d0, d2, d2 \
|
||||
VERLLF $8, a2, a2 \
|
||||
VERLLF $8, b2, b2 \
|
||||
VERLLF $8, c2, c2 \
|
||||
VERLLF $8, d2, d2 \
|
||||
VAF a2, a3, a3 \
|
||||
VAF b2, b3, b3 \
|
||||
VAF c2, c3, c3 \
|
||||
VAF d2, d3, d3 \
|
||||
VX a3, a1, a1 \
|
||||
VX b3, b1, b1 \
|
||||
VX c3, c1, c1 \
|
||||
VX d3, d1, d1 \
|
||||
VERLLF $7, a1, a1 \
|
||||
VERLLF $7, b1, b1 \
|
||||
VERLLF $7, c1, c1 \
|
||||
VERLLF $7, d1, d1
|
||||
|
||||
#define PERMUTE(mask, v0, v1, v2, v3) \
|
||||
VPERM v0, v0, mask, v0 \
|
||||
VPERM v1, v1, mask, v1 \
|
||||
VPERM v2, v2, mask, v2 \
|
||||
VPERM v3, v3, mask, v3
|
||||
|
||||
#define ADDV(x, v0, v1, v2, v3) \
|
||||
VAF x, v0, v0 \
|
||||
VAF x, v1, v1 \
|
||||
VAF x, v2, v2 \
|
||||
VAF x, v3, v3
|
||||
|
||||
#define XORV(off, dst, src, v0, v1, v2, v3) \
|
||||
VLM off(src), M0, M3 \
|
||||
PERMUTE(BSWAP, v0, v1, v2, v3) \
|
||||
VX v0, M0, M0 \
|
||||
VX v1, M1, M1 \
|
||||
VX v2, M2, M2 \
|
||||
VX v3, M3, M3 \
|
||||
VSTM M0, M3, off(dst)
|
||||
|
||||
#define SHUFFLE(a, b, c, d, t, u, v, w) \
|
||||
VMRHF a, c, t \ // t = {a[0], c[0], a[1], c[1]}
|
||||
VMRHF b, d, u \ // u = {b[0], d[0], b[1], d[1]}
|
||||
VMRLF a, c, v \ // v = {a[2], c[2], a[3], c[3]}
|
||||
VMRLF b, d, w \ // w = {b[2], d[2], b[3], d[3]}
|
||||
VMRHF t, u, a \ // a = {a[0], b[0], c[0], d[0]}
|
||||
VMRLF t, u, b \ // b = {a[1], b[1], c[1], d[1]}
|
||||
VMRHF v, w, c \ // c = {a[2], b[2], c[2], d[2]}
|
||||
VMRLF v, w, d // d = {a[3], b[3], c[3], d[3]}
|
||||
|
||||
// func xorKeyStreamVX(dst, src []byte, key *[8]uint32, nonce *[3]uint32, counter *uint32)
|
||||
TEXT ·xorKeyStreamVX(SB), NOSPLIT, $0
|
||||
MOVD $·constants<>(SB), R1
|
||||
MOVD dst+0(FP), R2 // R2=&dst[0]
|
||||
LMG src+24(FP), R3, R4 // R3=&src[0] R4=len(src)
|
||||
MOVD key+48(FP), R5 // R5=key
|
||||
MOVD nonce+56(FP), R6 // R6=nonce
|
||||
MOVD counter+64(FP), R7 // R7=counter
|
||||
|
||||
// load BSWAP and J0
|
||||
VLM (R1), BSWAP, J0
|
||||
|
||||
// setup
|
||||
MOVD $95, R0
|
||||
VLM (R5), KEY0, KEY1
|
||||
VLL R0, (R6), NONCE
|
||||
VZERO M0
|
||||
VLEIB $7, $32, M0
|
||||
VSRLB M0, NONCE, NONCE
|
||||
|
||||
// initialize counter values
|
||||
VLREPF (R7), CTR
|
||||
VZERO INC
|
||||
VLEIF $1, $1, INC
|
||||
VLEIF $2, $2, INC
|
||||
VLEIF $3, $3, INC
|
||||
VAF INC, CTR, CTR
|
||||
VREPIF $4, INC
|
||||
|
||||
chacha:
|
||||
VREPF $0, J0, X0
|
||||
VREPF $1, J0, X1
|
||||
VREPF $2, J0, X2
|
||||
VREPF $3, J0, X3
|
||||
VREPF $0, KEY0, X4
|
||||
VREPF $1, KEY0, X5
|
||||
VREPF $2, KEY0, X6
|
||||
VREPF $3, KEY0, X7
|
||||
VREPF $0, KEY1, X8
|
||||
VREPF $1, KEY1, X9
|
||||
VREPF $2, KEY1, X10
|
||||
VREPF $3, KEY1, X11
|
||||
VLR CTR, X12
|
||||
VREPF $1, NONCE, X13
|
||||
VREPF $2, NONCE, X14
|
||||
VREPF $3, NONCE, X15
|
||||
|
||||
MOVD $(NUM_ROUNDS/2), R1
|
||||
|
||||
loop:
|
||||
ROUND4(X0, X4, X12, X8, X1, X5, X13, X9, X2, X6, X14, X10, X3, X7, X15, X11)
|
||||
ROUND4(X0, X5, X15, X10, X1, X6, X12, X11, X2, X7, X13, X8, X3, X4, X14, X9)
|
||||
|
||||
ADD $-1, R1
|
||||
BNE loop
|
||||
|
||||
// decrement length
|
||||
ADD $-256, R4
|
||||
|
||||
// rearrange vectors
|
||||
SHUFFLE(X0, X1, X2, X3, M0, M1, M2, M3)
|
||||
ADDV(J0, X0, X1, X2, X3)
|
||||
SHUFFLE(X4, X5, X6, X7, M0, M1, M2, M3)
|
||||
ADDV(KEY0, X4, X5, X6, X7)
|
||||
SHUFFLE(X8, X9, X10, X11, M0, M1, M2, M3)
|
||||
ADDV(KEY1, X8, X9, X10, X11)
|
||||
VAF CTR, X12, X12
|
||||
SHUFFLE(X12, X13, X14, X15, M0, M1, M2, M3)
|
||||
ADDV(NONCE, X12, X13, X14, X15)
|
||||
|
||||
// increment counters
|
||||
VAF INC, CTR, CTR
|
||||
|
||||
// xor keystream with plaintext
|
||||
XORV(0*64, R2, R3, X0, X4, X8, X12)
|
||||
XORV(1*64, R2, R3, X1, X5, X9, X13)
|
||||
XORV(2*64, R2, R3, X2, X6, X10, X14)
|
||||
XORV(3*64, R2, R3, X3, X7, X11, X15)
|
||||
|
||||
// increment pointers
|
||||
MOVD $256(R2), R2
|
||||
MOVD $256(R3), R3
|
||||
|
||||
CMPBNE R4, $0, chacha
|
||||
|
||||
VSTEF $0, CTR, (R7)
|
||||
RET
|
||||
42
vendor/golang.org/x/crypto/chacha20/xor.go
generated
vendored
Normal file
42
vendor/golang.org/x/crypto/chacha20/xor.go
generated
vendored
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found src the LICENSE file.
|
||||
|
||||
package chacha20
|
||||
|
||||
import "runtime"
|
||||
|
||||
// Platforms that have fast unaligned 32-bit little endian accesses.
|
||||
const unaligned = runtime.GOARCH == "386" ||
|
||||
runtime.GOARCH == "amd64" ||
|
||||
runtime.GOARCH == "arm64" ||
|
||||
runtime.GOARCH == "ppc64le" ||
|
||||
runtime.GOARCH == "s390x"
|
||||
|
||||
// addXor reads a little endian uint32 from src, XORs it with (a + b) and
|
||||
// places the result in little endian byte order in dst.
|
||||
func addXor(dst, src []byte, a, b uint32) {
|
||||
_, _ = src[3], dst[3] // bounds check elimination hint
|
||||
if unaligned {
|
||||
// The compiler should optimize this code into
|
||||
// 32-bit unaligned little endian loads and stores.
|
||||
// TODO: delete once the compiler does a reliably
|
||||
// good job with the generic code below.
|
||||
// See issue #25111 for more details.
|
||||
v := uint32(src[0])
|
||||
v |= uint32(src[1]) << 8
|
||||
v |= uint32(src[2]) << 16
|
||||
v |= uint32(src[3]) << 24
|
||||
v ^= a + b
|
||||
dst[0] = byte(v)
|
||||
dst[1] = byte(v >> 8)
|
||||
dst[2] = byte(v >> 16)
|
||||
dst[3] = byte(v >> 24)
|
||||
} else {
|
||||
a += b
|
||||
dst[0] = src[0] ^ byte(a)
|
||||
dst[1] = src[1] ^ byte(a>>8)
|
||||
dst[2] = src[2] ^ byte(a>>16)
|
||||
dst[3] = src[3] ^ byte(a>>24)
|
||||
}
|
||||
}
|
||||
93
vendor/golang.org/x/crypto/curve25519/curve25519.go
generated
vendored
Normal file
93
vendor/golang.org/x/crypto/curve25519/curve25519.go
generated
vendored
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package curve25519 provides an implementation of the X25519 function, which
|
||||
// performs scalar multiplication on the elliptic curve known as Curve25519
|
||||
// according to [RFC 7748].
|
||||
//
|
||||
// The curve25519 package is a wrapper for the X25519 implementation in the
|
||||
// crypto/ecdh package. It is [frozen] and is not accepting new features.
|
||||
//
|
||||
// [RFC 7748]: https://datatracker.ietf.org/doc/html/rfc7748
|
||||
// [frozen]: https://go.dev/wiki/Frozen
|
||||
package curve25519
|
||||
|
||||
import "crypto/ecdh"
|
||||
|
||||
// ScalarMult sets dst to the product scalar * point.
|
||||
//
|
||||
// Deprecated: when provided a low-order point, ScalarMult will set dst to all
|
||||
// zeroes, irrespective of the scalar. Instead, use the X25519 function, which
|
||||
// will return an error.
|
||||
func ScalarMult(dst, scalar, point *[32]byte) {
|
||||
if _, err := x25519(dst, scalar[:], point[:]); err != nil {
|
||||
// The only error condition for x25519 when the inputs are 32 bytes long
|
||||
// is if the output would have been the all-zero value.
|
||||
for i := range dst {
|
||||
dst[i] = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ScalarBaseMult sets dst to the product scalar * base where base is the
|
||||
// standard generator.
|
||||
//
|
||||
// It is recommended to use the X25519 function with Basepoint instead, as
|
||||
// copying into fixed size arrays can lead to unexpected bugs.
|
||||
func ScalarBaseMult(dst, scalar *[32]byte) {
|
||||
curve := ecdh.X25519()
|
||||
priv, err := curve.NewPrivateKey(scalar[:])
|
||||
if err != nil {
|
||||
panic("curve25519: " + err.Error())
|
||||
}
|
||||
copy(dst[:], priv.PublicKey().Bytes())
|
||||
}
|
||||
|
||||
const (
|
||||
// ScalarSize is the size of the scalar input to X25519.
|
||||
ScalarSize = 32
|
||||
// PointSize is the size of the point input to X25519.
|
||||
PointSize = 32
|
||||
)
|
||||
|
||||
// Basepoint is the canonical Curve25519 generator.
|
||||
var Basepoint []byte
|
||||
|
||||
var basePoint = [32]byte{9}
|
||||
|
||||
func init() { Basepoint = basePoint[:] }
|
||||
|
||||
// X25519 returns the result of the scalar multiplication (scalar * point),
|
||||
// according to RFC 7748, Section 5. scalar, point and the return value are
|
||||
// slices of 32 bytes.
|
||||
//
|
||||
// scalar can be generated at random, for example with crypto/rand. point should
|
||||
// be either Basepoint or the output of another X25519 call.
|
||||
//
|
||||
// If point is Basepoint (but not if it's a different slice with the same
|
||||
// contents) a precomputed implementation might be used for performance.
|
||||
func X25519(scalar, point []byte) ([]byte, error) {
|
||||
// Outline the body of function, to let the allocation be inlined in the
|
||||
// caller, and possibly avoid escaping to the heap.
|
||||
var dst [32]byte
|
||||
return x25519(&dst, scalar, point)
|
||||
}
|
||||
|
||||
func x25519(dst *[32]byte, scalar, point []byte) ([]byte, error) {
|
||||
curve := ecdh.X25519()
|
||||
pub, err := curve.NewPublicKey(point)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
priv, err := curve.NewPrivateKey(scalar)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out, err := priv.ECDH(pub)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
copy(dst[:], out)
|
||||
return dst[:], nil
|
||||
}
|
||||
31
vendor/golang.org/x/crypto/internal/alias/alias.go
generated
vendored
Normal file
31
vendor/golang.org/x/crypto/internal/alias/alias.go
generated
vendored
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !purego
|
||||
|
||||
// Package alias implements memory aliasing tests.
|
||||
package alias
|
||||
|
||||
import "unsafe"
|
||||
|
||||
// AnyOverlap reports whether x and y share memory at any (not necessarily
|
||||
// corresponding) index. The memory beyond the slice length is ignored.
|
||||
func AnyOverlap(x, y []byte) bool {
|
||||
return len(x) > 0 && len(y) > 0 &&
|
||||
uintptr(unsafe.Pointer(&x[0])) <= uintptr(unsafe.Pointer(&y[len(y)-1])) &&
|
||||
uintptr(unsafe.Pointer(&y[0])) <= uintptr(unsafe.Pointer(&x[len(x)-1]))
|
||||
}
|
||||
|
||||
// InexactOverlap reports whether x and y share memory at any non-corresponding
|
||||
// index. The memory beyond the slice length is ignored. Note that x and y can
|
||||
// have different lengths and still not have any inexact overlap.
|
||||
//
|
||||
// InexactOverlap can be used to implement the requirements of the crypto/cipher
|
||||
// AEAD, Block, BlockMode and Stream interfaces.
|
||||
func InexactOverlap(x, y []byte) bool {
|
||||
if len(x) == 0 || len(y) == 0 || &x[0] == &y[0] {
|
||||
return false
|
||||
}
|
||||
return AnyOverlap(x, y)
|
||||
}
|
||||
34
vendor/golang.org/x/crypto/internal/alias/alias_purego.go
generated
vendored
Normal file
34
vendor/golang.org/x/crypto/internal/alias/alias_purego.go
generated
vendored
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build purego
|
||||
|
||||
// Package alias implements memory aliasing tests.
|
||||
package alias
|
||||
|
||||
// This is the Google App Engine standard variant based on reflect
|
||||
// because the unsafe package and cgo are disallowed.
|
||||
|
||||
import "reflect"
|
||||
|
||||
// AnyOverlap reports whether x and y share memory at any (not necessarily
|
||||
// corresponding) index. The memory beyond the slice length is ignored.
|
||||
func AnyOverlap(x, y []byte) bool {
|
||||
return len(x) > 0 && len(y) > 0 &&
|
||||
reflect.ValueOf(&x[0]).Pointer() <= reflect.ValueOf(&y[len(y)-1]).Pointer() &&
|
||||
reflect.ValueOf(&y[0]).Pointer() <= reflect.ValueOf(&x[len(x)-1]).Pointer()
|
||||
}
|
||||
|
||||
// InexactOverlap reports whether x and y share memory at any non-corresponding
|
||||
// index. The memory beyond the slice length is ignored. Note that x and y can
|
||||
// have different lengths and still not have any inexact overlap.
|
||||
//
|
||||
// InexactOverlap can be used to implement the requirements of the crypto/cipher
|
||||
// AEAD, Block, BlockMode and Stream interfaces.
|
||||
func InexactOverlap(x, y []byte) bool {
|
||||
if len(x) == 0 || len(y) == 0 || &x[0] == &y[0] {
|
||||
return false
|
||||
}
|
||||
return AnyOverlap(x, y)
|
||||
}
|
||||
9
vendor/golang.org/x/crypto/internal/poly1305/mac_noasm.go
generated
vendored
Normal file
9
vendor/golang.org/x/crypto/internal/poly1305/mac_noasm.go
generated
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build (!amd64 && !loong64 && !ppc64le && !ppc64 && !s390x) || !gc || purego
|
||||
|
||||
package poly1305
|
||||
|
||||
type mac struct{ macGeneric }
|
||||
99
vendor/golang.org/x/crypto/internal/poly1305/poly1305.go
generated
vendored
Normal file
99
vendor/golang.org/x/crypto/internal/poly1305/poly1305.go
generated
vendored
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package poly1305 implements Poly1305 one-time message authentication code as
|
||||
// specified in https://cr.yp.to/mac/poly1305-20050329.pdf.
|
||||
//
|
||||
// Poly1305 is a fast, one-time authentication function. It is infeasible for an
|
||||
// attacker to generate an authenticator for a message without the key. However, a
|
||||
// key must only be used for a single message. Authenticating two different
|
||||
// messages with the same key allows an attacker to forge authenticators for other
|
||||
// messages with the same key.
|
||||
//
|
||||
// Poly1305 was originally coupled with AES in order to make Poly1305-AES. AES was
|
||||
// used with a fixed key in order to generate one-time keys from an nonce.
|
||||
// However, in this package AES isn't used and the one-time key is specified
|
||||
// directly.
|
||||
package poly1305
|
||||
|
||||
import "crypto/subtle"
|
||||
|
||||
// TagSize is the size, in bytes, of a poly1305 authenticator.
|
||||
const TagSize = 16
|
||||
|
||||
// Sum generates an authenticator for msg using a one-time key and puts the
|
||||
// 16-byte result into out. Authenticating two different messages with the same
|
||||
// key allows an attacker to forge messages at will.
|
||||
func Sum(out *[16]byte, m []byte, key *[32]byte) {
|
||||
h := New(key)
|
||||
h.Write(m)
|
||||
h.Sum(out[:0])
|
||||
}
|
||||
|
||||
// Verify returns true if mac is a valid authenticator for m with the given key.
|
||||
func Verify(mac *[16]byte, m []byte, key *[32]byte) bool {
|
||||
var tmp [16]byte
|
||||
Sum(&tmp, m, key)
|
||||
return subtle.ConstantTimeCompare(tmp[:], mac[:]) == 1
|
||||
}
|
||||
|
||||
// New returns a new MAC computing an authentication
|
||||
// tag of all data written to it with the given key.
|
||||
// This allows writing the message progressively instead
|
||||
// of passing it as a single slice. Common users should use
|
||||
// the Sum function instead.
|
||||
//
|
||||
// The key must be unique for each message, as authenticating
|
||||
// two different messages with the same key allows an attacker
|
||||
// to forge messages at will.
|
||||
func New(key *[32]byte) *MAC {
|
||||
m := &MAC{}
|
||||
initialize(key, &m.macState)
|
||||
return m
|
||||
}
|
||||
|
||||
// MAC is an io.Writer computing an authentication tag
|
||||
// of the data written to it.
|
||||
//
|
||||
// MAC cannot be used like common hash.Hash implementations,
|
||||
// because using a poly1305 key twice breaks its security.
|
||||
// Therefore writing data to a running MAC after calling
|
||||
// Sum or Verify causes it to panic.
|
||||
type MAC struct {
|
||||
mac // platform-dependent implementation
|
||||
|
||||
finalized bool
|
||||
}
|
||||
|
||||
// Size returns the number of bytes Sum will return.
|
||||
func (h *MAC) Size() int { return TagSize }
|
||||
|
||||
// Write adds more data to the running message authentication code.
|
||||
// It never returns an error.
|
||||
//
|
||||
// It must not be called after the first call of Sum or Verify.
|
||||
func (h *MAC) Write(p []byte) (n int, err error) {
|
||||
if h.finalized {
|
||||
panic("poly1305: write to MAC after Sum or Verify")
|
||||
}
|
||||
return h.mac.Write(p)
|
||||
}
|
||||
|
||||
// Sum computes the authenticator of all data written to the
|
||||
// message authentication code.
|
||||
func (h *MAC) Sum(b []byte) []byte {
|
||||
var mac [TagSize]byte
|
||||
h.mac.Sum(&mac)
|
||||
h.finalized = true
|
||||
return append(b, mac[:]...)
|
||||
}
|
||||
|
||||
// Verify returns whether the authenticator of all data written to
|
||||
// the message authentication code matches the expected value.
|
||||
func (h *MAC) Verify(expected []byte) bool {
|
||||
var mac [TagSize]byte
|
||||
h.mac.Sum(&mac)
|
||||
h.finalized = true
|
||||
return subtle.ConstantTimeCompare(expected, mac[:]) == 1
|
||||
}
|
||||
93
vendor/golang.org/x/crypto/internal/poly1305/sum_amd64.s
generated
vendored
Normal file
93
vendor/golang.org/x/crypto/internal/poly1305/sum_amd64.s
generated
vendored
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
// Code generated by command: go run sum_amd64_asm.go -out ../sum_amd64.s -pkg poly1305. DO NOT EDIT.
|
||||
|
||||
//go:build gc && !purego
|
||||
|
||||
// func update(state *macState, msg []byte)
|
||||
TEXT ·update(SB), $0-32
|
||||
MOVQ state+0(FP), DI
|
||||
MOVQ msg_base+8(FP), SI
|
||||
MOVQ msg_len+16(FP), R15
|
||||
MOVQ (DI), R8
|
||||
MOVQ 8(DI), R9
|
||||
MOVQ 16(DI), R10
|
||||
MOVQ 24(DI), R11
|
||||
MOVQ 32(DI), R12
|
||||
CMPQ R15, $0x10
|
||||
JB bytes_between_0_and_15
|
||||
|
||||
loop:
|
||||
ADDQ (SI), R8
|
||||
ADCQ 8(SI), R9
|
||||
ADCQ $0x01, R10
|
||||
LEAQ 16(SI), SI
|
||||
|
||||
multiply:
|
||||
MOVQ R11, AX
|
||||
MULQ R8
|
||||
MOVQ AX, BX
|
||||
MOVQ DX, CX
|
||||
MOVQ R11, AX
|
||||
MULQ R9
|
||||
ADDQ AX, CX
|
||||
ADCQ $0x00, DX
|
||||
MOVQ R11, R13
|
||||
IMULQ R10, R13
|
||||
ADDQ DX, R13
|
||||
MOVQ R12, AX
|
||||
MULQ R8
|
||||
ADDQ AX, CX
|
||||
ADCQ $0x00, DX
|
||||
MOVQ DX, R8
|
||||
MOVQ R12, R14
|
||||
IMULQ R10, R14
|
||||
MOVQ R12, AX
|
||||
MULQ R9
|
||||
ADDQ AX, R13
|
||||
ADCQ DX, R14
|
||||
ADDQ R8, R13
|
||||
ADCQ $0x00, R14
|
||||
MOVQ BX, R8
|
||||
MOVQ CX, R9
|
||||
MOVQ R13, R10
|
||||
ANDQ $0x03, R10
|
||||
MOVQ R13, BX
|
||||
ANDQ $-4, BX
|
||||
ADDQ BX, R8
|
||||
ADCQ R14, R9
|
||||
ADCQ $0x00, R10
|
||||
SHRQ $0x02, R14, R13
|
||||
SHRQ $0x02, R14
|
||||
ADDQ R13, R8
|
||||
ADCQ R14, R9
|
||||
ADCQ $0x00, R10
|
||||
SUBQ $0x10, R15
|
||||
CMPQ R15, $0x10
|
||||
JAE loop
|
||||
|
||||
bytes_between_0_and_15:
|
||||
TESTQ R15, R15
|
||||
JZ done
|
||||
MOVQ $0x00000001, BX
|
||||
XORQ CX, CX
|
||||
XORQ R13, R13
|
||||
ADDQ R15, SI
|
||||
|
||||
flush_buffer:
|
||||
SHLQ $0x08, BX, CX
|
||||
SHLQ $0x08, BX
|
||||
MOVB -1(SI), R13
|
||||
XORQ R13, BX
|
||||
DECQ SI
|
||||
DECQ R15
|
||||
JNZ flush_buffer
|
||||
ADDQ BX, R8
|
||||
ADCQ CX, R9
|
||||
ADCQ $0x00, R10
|
||||
MOVQ $0x00000010, R15
|
||||
JMP multiply
|
||||
|
||||
done:
|
||||
MOVQ R8, (DI)
|
||||
MOVQ R9, 8(DI)
|
||||
MOVQ R10, 16(DI)
|
||||
RET
|
||||
47
vendor/golang.org/x/crypto/internal/poly1305/sum_asm.go
generated
vendored
Normal file
47
vendor/golang.org/x/crypto/internal/poly1305/sum_asm.go
generated
vendored
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build gc && !purego && (amd64 || loong64 || ppc64 || ppc64le)
|
||||
|
||||
package poly1305
|
||||
|
||||
//go:noescape
|
||||
func update(state *macState, msg []byte)
|
||||
|
||||
// mac is a wrapper for macGeneric that redirects calls that would have gone to
|
||||
// updateGeneric to update.
|
||||
//
|
||||
// Its Write and Sum methods are otherwise identical to the macGeneric ones, but
|
||||
// using function pointers would carry a major performance cost.
|
||||
type mac struct{ macGeneric }
|
||||
|
||||
func (h *mac) Write(p []byte) (int, error) {
|
||||
nn := len(p)
|
||||
if h.offset > 0 {
|
||||
n := copy(h.buffer[h.offset:], p)
|
||||
if h.offset+n < TagSize {
|
||||
h.offset += n
|
||||
return nn, nil
|
||||
}
|
||||
p = p[n:]
|
||||
h.offset = 0
|
||||
update(&h.macState, h.buffer[:])
|
||||
}
|
||||
if n := len(p) - (len(p) % TagSize); n > 0 {
|
||||
update(&h.macState, p[:n])
|
||||
p = p[n:]
|
||||
}
|
||||
if len(p) > 0 {
|
||||
h.offset += copy(h.buffer[h.offset:], p)
|
||||
}
|
||||
return nn, nil
|
||||
}
|
||||
|
||||
func (h *mac) Sum(out *[16]byte) {
|
||||
state := h.macState
|
||||
if h.offset > 0 {
|
||||
update(&state, h.buffer[:h.offset])
|
||||
}
|
||||
finalize(out, &state.h, &state.s)
|
||||
}
|
||||
312
vendor/golang.org/x/crypto/internal/poly1305/sum_generic.go
generated
vendored
Normal file
312
vendor/golang.org/x/crypto/internal/poly1305/sum_generic.go
generated
vendored
Normal file
|
|
@ -0,0 +1,312 @@
|
|||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// This file provides the generic implementation of Sum and MAC. Other files
|
||||
// might provide optimized assembly implementations of some of this code.
|
||||
|
||||
package poly1305
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"math/bits"
|
||||
)
|
||||
|
||||
// Poly1305 [RFC 7539] is a relatively simple algorithm: the authentication tag
|
||||
// for a 64 bytes message is approximately
|
||||
//
|
||||
// s + m[0:16] * r⁴ + m[16:32] * r³ + m[32:48] * r² + m[48:64] * r mod 2¹³⁰ - 5
|
||||
//
|
||||
// for some secret r and s. It can be computed sequentially like
|
||||
//
|
||||
// for len(msg) > 0:
|
||||
// h += read(msg, 16)
|
||||
// h *= r
|
||||
// h %= 2¹³⁰ - 5
|
||||
// return h + s
|
||||
//
|
||||
// All the complexity is about doing performant constant-time math on numbers
|
||||
// larger than any available numeric type.
|
||||
|
||||
func sumGeneric(out *[TagSize]byte, msg []byte, key *[32]byte) {
|
||||
h := newMACGeneric(key)
|
||||
h.Write(msg)
|
||||
h.Sum(out)
|
||||
}
|
||||
|
||||
func newMACGeneric(key *[32]byte) macGeneric {
|
||||
m := macGeneric{}
|
||||
initialize(key, &m.macState)
|
||||
return m
|
||||
}
|
||||
|
||||
// macState holds numbers in saturated 64-bit little-endian limbs. That is,
|
||||
// the value of [x0, x1, x2] is x[0] + x[1] * 2⁶⁴ + x[2] * 2¹²⁸.
|
||||
type macState struct {
|
||||
// h is the main accumulator. It is to be interpreted modulo 2¹³⁰ - 5, but
|
||||
// can grow larger during and after rounds. It must, however, remain below
|
||||
// 2 * (2¹³⁰ - 5).
|
||||
h [3]uint64
|
||||
// r and s are the private key components.
|
||||
r [2]uint64
|
||||
s [2]uint64
|
||||
}
|
||||
|
||||
type macGeneric struct {
|
||||
macState
|
||||
|
||||
buffer [TagSize]byte
|
||||
offset int
|
||||
}
|
||||
|
||||
// Write splits the incoming message into TagSize chunks, and passes them to
|
||||
// update. It buffers incomplete chunks.
|
||||
func (h *macGeneric) Write(p []byte) (int, error) {
|
||||
nn := len(p)
|
||||
if h.offset > 0 {
|
||||
n := copy(h.buffer[h.offset:], p)
|
||||
if h.offset+n < TagSize {
|
||||
h.offset += n
|
||||
return nn, nil
|
||||
}
|
||||
p = p[n:]
|
||||
h.offset = 0
|
||||
updateGeneric(&h.macState, h.buffer[:])
|
||||
}
|
||||
if n := len(p) - (len(p) % TagSize); n > 0 {
|
||||
updateGeneric(&h.macState, p[:n])
|
||||
p = p[n:]
|
||||
}
|
||||
if len(p) > 0 {
|
||||
h.offset += copy(h.buffer[h.offset:], p)
|
||||
}
|
||||
return nn, nil
|
||||
}
|
||||
|
||||
// Sum flushes the last incomplete chunk from the buffer, if any, and generates
|
||||
// the MAC output. It does not modify its state, in order to allow for multiple
|
||||
// calls to Sum, even if no Write is allowed after Sum.
|
||||
func (h *macGeneric) Sum(out *[TagSize]byte) {
|
||||
state := h.macState
|
||||
if h.offset > 0 {
|
||||
updateGeneric(&state, h.buffer[:h.offset])
|
||||
}
|
||||
finalize(out, &state.h, &state.s)
|
||||
}
|
||||
|
||||
// [rMask0, rMask1] is the specified Poly1305 clamping mask in little-endian. It
|
||||
// clears some bits of the secret coefficient to make it possible to implement
|
||||
// multiplication more efficiently.
|
||||
const (
|
||||
rMask0 = 0x0FFFFFFC0FFFFFFF
|
||||
rMask1 = 0x0FFFFFFC0FFFFFFC
|
||||
)
|
||||
|
||||
// initialize loads the 256-bit key into the two 128-bit secret values r and s.
|
||||
func initialize(key *[32]byte, m *macState) {
|
||||
m.r[0] = binary.LittleEndian.Uint64(key[0:8]) & rMask0
|
||||
m.r[1] = binary.LittleEndian.Uint64(key[8:16]) & rMask1
|
||||
m.s[0] = binary.LittleEndian.Uint64(key[16:24])
|
||||
m.s[1] = binary.LittleEndian.Uint64(key[24:32])
|
||||
}
|
||||
|
||||
// uint128 holds a 128-bit number as two 64-bit limbs, for use with the
|
||||
// bits.Mul64 and bits.Add64 intrinsics.
|
||||
type uint128 struct {
|
||||
lo, hi uint64
|
||||
}
|
||||
|
||||
func mul64(a, b uint64) uint128 {
|
||||
hi, lo := bits.Mul64(a, b)
|
||||
return uint128{lo, hi}
|
||||
}
|
||||
|
||||
func add128(a, b uint128) uint128 {
|
||||
lo, c := bits.Add64(a.lo, b.lo, 0)
|
||||
hi, c := bits.Add64(a.hi, b.hi, c)
|
||||
if c != 0 {
|
||||
panic("poly1305: unexpected overflow")
|
||||
}
|
||||
return uint128{lo, hi}
|
||||
}
|
||||
|
||||
func shiftRightBy2(a uint128) uint128 {
|
||||
a.lo = a.lo>>2 | (a.hi&3)<<62
|
||||
a.hi = a.hi >> 2
|
||||
return a
|
||||
}
|
||||
|
||||
// updateGeneric absorbs msg into the state.h accumulator. For each chunk m of
|
||||
// 128 bits of message, it computes
|
||||
//
|
||||
// h₊ = (h + m) * r mod 2¹³⁰ - 5
|
||||
//
|
||||
// If the msg length is not a multiple of TagSize, it assumes the last
|
||||
// incomplete chunk is the final one.
|
||||
func updateGeneric(state *macState, msg []byte) {
|
||||
h0, h1, h2 := state.h[0], state.h[1], state.h[2]
|
||||
r0, r1 := state.r[0], state.r[1]
|
||||
|
||||
for len(msg) > 0 {
|
||||
var c uint64
|
||||
|
||||
// For the first step, h + m, we use a chain of bits.Add64 intrinsics.
|
||||
// The resulting value of h might exceed 2¹³⁰ - 5, but will be partially
|
||||
// reduced at the end of the multiplication below.
|
||||
//
|
||||
// The spec requires us to set a bit just above the message size, not to
|
||||
// hide leading zeroes. For full chunks, that's 1 << 128, so we can just
|
||||
// add 1 to the most significant (2¹²⁸) limb, h2.
|
||||
if len(msg) >= TagSize {
|
||||
h0, c = bits.Add64(h0, binary.LittleEndian.Uint64(msg[0:8]), 0)
|
||||
h1, c = bits.Add64(h1, binary.LittleEndian.Uint64(msg[8:16]), c)
|
||||
h2 += c + 1
|
||||
|
||||
msg = msg[TagSize:]
|
||||
} else {
|
||||
var buf [TagSize]byte
|
||||
copy(buf[:], msg)
|
||||
buf[len(msg)] = 1
|
||||
|
||||
h0, c = bits.Add64(h0, binary.LittleEndian.Uint64(buf[0:8]), 0)
|
||||
h1, c = bits.Add64(h1, binary.LittleEndian.Uint64(buf[8:16]), c)
|
||||
h2 += c
|
||||
|
||||
msg = nil
|
||||
}
|
||||
|
||||
// Multiplication of big number limbs is similar to elementary school
|
||||
// columnar multiplication. Instead of digits, there are 64-bit limbs.
|
||||
//
|
||||
// We are multiplying a 3 limbs number, h, by a 2 limbs number, r.
|
||||
//
|
||||
// h2 h1 h0 x
|
||||
// r1 r0 =
|
||||
// ----------------
|
||||
// h2r0 h1r0 h0r0 <-- individual 128-bit products
|
||||
// + h2r1 h1r1 h0r1
|
||||
// ------------------------
|
||||
// m3 m2 m1 m0 <-- result in 128-bit overlapping limbs
|
||||
// ------------------------
|
||||
// m3.hi m2.hi m1.hi m0.hi <-- carry propagation
|
||||
// + m3.lo m2.lo m1.lo m0.lo
|
||||
// -------------------------------
|
||||
// t4 t3 t2 t1 t0 <-- final result in 64-bit limbs
|
||||
//
|
||||
// The main difference from pen-and-paper multiplication is that we do
|
||||
// carry propagation in a separate step, as if we wrote two digit sums
|
||||
// at first (the 128-bit limbs), and then carried the tens all at once.
|
||||
|
||||
h0r0 := mul64(h0, r0)
|
||||
h1r0 := mul64(h1, r0)
|
||||
h2r0 := mul64(h2, r0)
|
||||
h0r1 := mul64(h0, r1)
|
||||
h1r1 := mul64(h1, r1)
|
||||
h2r1 := mul64(h2, r1)
|
||||
|
||||
// Since h2 is known to be at most 7 (5 + 1 + 1), and r0 and r1 have their
|
||||
// top 4 bits cleared by rMask{0,1}, we know that their product is not going
|
||||
// to overflow 64 bits, so we can ignore the high part of the products.
|
||||
//
|
||||
// This also means that the product doesn't have a fifth limb (t4).
|
||||
if h2r0.hi != 0 {
|
||||
panic("poly1305: unexpected overflow")
|
||||
}
|
||||
if h2r1.hi != 0 {
|
||||
panic("poly1305: unexpected overflow")
|
||||
}
|
||||
|
||||
m0 := h0r0
|
||||
m1 := add128(h1r0, h0r1) // These two additions don't overflow thanks again
|
||||
m2 := add128(h2r0, h1r1) // to the 4 masked bits at the top of r0 and r1.
|
||||
m3 := h2r1
|
||||
|
||||
t0 := m0.lo
|
||||
t1, c := bits.Add64(m1.lo, m0.hi, 0)
|
||||
t2, c := bits.Add64(m2.lo, m1.hi, c)
|
||||
t3, _ := bits.Add64(m3.lo, m2.hi, c)
|
||||
|
||||
// Now we have the result as 4 64-bit limbs, and we need to reduce it
|
||||
// modulo 2¹³⁰ - 5. The special shape of this Crandall prime lets us do
|
||||
// a cheap partial reduction according to the reduction identity
|
||||
//
|
||||
// c * 2¹³⁰ + n = c * 5 + n mod 2¹³⁰ - 5
|
||||
//
|
||||
// because 2¹³⁰ = 5 mod 2¹³⁰ - 5. Partial reduction since the result is
|
||||
// likely to be larger than 2¹³⁰ - 5, but still small enough to fit the
|
||||
// assumptions we make about h in the rest of the code.
|
||||
//
|
||||
// See also https://speakerdeck.com/gtank/engineering-prime-numbers?slide=23
|
||||
|
||||
// We split the final result at the 2¹³⁰ mark into h and cc, the carry.
|
||||
// Note that the carry bits are effectively shifted left by 2, in other
|
||||
// words, cc = c * 4 for the c in the reduction identity.
|
||||
h0, h1, h2 = t0, t1, t2&maskLow2Bits
|
||||
cc := uint128{t2 & maskNotLow2Bits, t3}
|
||||
|
||||
// To add c * 5 to h, we first add cc = c * 4, and then add (cc >> 2) = c.
|
||||
|
||||
h0, c = bits.Add64(h0, cc.lo, 0)
|
||||
h1, c = bits.Add64(h1, cc.hi, c)
|
||||
h2 += c
|
||||
|
||||
cc = shiftRightBy2(cc)
|
||||
|
||||
h0, c = bits.Add64(h0, cc.lo, 0)
|
||||
h1, c = bits.Add64(h1, cc.hi, c)
|
||||
h2 += c
|
||||
|
||||
// h2 is at most 3 + 1 + 1 = 5, making the whole of h at most
|
||||
//
|
||||
// 5 * 2¹²⁸ + (2¹²⁸ - 1) = 6 * 2¹²⁸ - 1
|
||||
}
|
||||
|
||||
state.h[0], state.h[1], state.h[2] = h0, h1, h2
|
||||
}
|
||||
|
||||
const (
|
||||
maskLow2Bits uint64 = 0x0000000000000003
|
||||
maskNotLow2Bits uint64 = ^maskLow2Bits
|
||||
)
|
||||
|
||||
// select64 returns x if v == 1 and y if v == 0, in constant time.
|
||||
func select64(v, x, y uint64) uint64 { return ^(v-1)&x | (v-1)&y }
|
||||
|
||||
// [p0, p1, p2] is 2¹³⁰ - 5 in little endian order.
|
||||
const (
|
||||
p0 = 0xFFFFFFFFFFFFFFFB
|
||||
p1 = 0xFFFFFFFFFFFFFFFF
|
||||
p2 = 0x0000000000000003
|
||||
)
|
||||
|
||||
// finalize completes the modular reduction of h and computes
|
||||
//
|
||||
// out = h + s mod 2¹²⁸
|
||||
func finalize(out *[TagSize]byte, h *[3]uint64, s *[2]uint64) {
|
||||
h0, h1, h2 := h[0], h[1], h[2]
|
||||
|
||||
// After the partial reduction in updateGeneric, h might be more than
|
||||
// 2¹³⁰ - 5, but will be less than 2 * (2¹³⁰ - 5). To complete the reduction
|
||||
// in constant time, we compute t = h - (2¹³⁰ - 5), and select h as the
|
||||
// result if the subtraction underflows, and t otherwise.
|
||||
|
||||
hMinusP0, b := bits.Sub64(h0, p0, 0)
|
||||
hMinusP1, b := bits.Sub64(h1, p1, b)
|
||||
_, b = bits.Sub64(h2, p2, b)
|
||||
|
||||
// h = h if h < p else h - p
|
||||
h0 = select64(b, h0, hMinusP0)
|
||||
h1 = select64(b, h1, hMinusP1)
|
||||
|
||||
// Finally, we compute the last Poly1305 step
|
||||
//
|
||||
// tag = h + s mod 2¹²⁸
|
||||
//
|
||||
// by just doing a wide addition with the 128 low bits of h and discarding
|
||||
// the overflow.
|
||||
h0, c := bits.Add64(h0, s[0], 0)
|
||||
h1, _ = bits.Add64(h1, s[1], c)
|
||||
|
||||
binary.LittleEndian.PutUint64(out[0:8], h0)
|
||||
binary.LittleEndian.PutUint64(out[8:16], h1)
|
||||
}
|
||||
123
vendor/golang.org/x/crypto/internal/poly1305/sum_loong64.s
generated
vendored
Normal file
123
vendor/golang.org/x/crypto/internal/poly1305/sum_loong64.s
generated
vendored
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
// Copyright 2025 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build gc && !purego
|
||||
|
||||
// func update(state *macState, msg []byte)
|
||||
TEXT ·update(SB), $0-32
|
||||
MOVV state+0(FP), R4
|
||||
MOVV msg_base+8(FP), R5
|
||||
MOVV msg_len+16(FP), R6
|
||||
|
||||
MOVV $0x10, R7
|
||||
|
||||
MOVV (R4), R8 // h0
|
||||
MOVV 8(R4), R9 // h1
|
||||
MOVV 16(R4), R10 // h2
|
||||
MOVV 24(R4), R11 // r0
|
||||
MOVV 32(R4), R12 // r1
|
||||
|
||||
BLT R6, R7, bytes_between_0_and_15
|
||||
|
||||
loop:
|
||||
MOVV (R5), R14 // msg[0:8]
|
||||
MOVV 8(R5), R16 // msg[8:16]
|
||||
ADDV R14, R8, R8 // h0 (x1 + y1 = z1', if z1' < x1 then z1' overflow)
|
||||
ADDV R16, R9, R27
|
||||
SGTU R14, R8, R24 // h0.carry
|
||||
SGTU R9, R27, R28
|
||||
ADDV R27, R24, R9 // h1
|
||||
SGTU R27, R9, R24
|
||||
OR R24, R28, R24 // h1.carry
|
||||
ADDV $0x01, R24, R24
|
||||
ADDV R10, R24, R10 // h2
|
||||
|
||||
ADDV $16, R5, R5 // msg = msg[16:]
|
||||
|
||||
multiply:
|
||||
MULV R8, R11, R14 // h0r0.lo
|
||||
MULHVU R8, R11, R15 // h0r0.hi
|
||||
MULV R9, R11, R13 // h1r0.lo
|
||||
MULHVU R9, R11, R16 // h1r0.hi
|
||||
ADDV R13, R15, R15
|
||||
SGTU R13, R15, R24
|
||||
ADDV R24, R16, R16
|
||||
MULV R10, R11, R25
|
||||
ADDV R16, R25, R25
|
||||
MULV R8, R12, R13 // h0r1.lo
|
||||
MULHVU R8, R12, R16 // h0r1.hi
|
||||
ADDV R13, R15, R15
|
||||
SGTU R13, R15, R24
|
||||
ADDV R24, R16, R16
|
||||
MOVV R16, R8
|
||||
MULV R10, R12, R26 // h2r1
|
||||
MULV R9, R12, R13 // h1r1.lo
|
||||
MULHVU R9, R12, R16 // h1r1.hi
|
||||
ADDV R13, R25, R25
|
||||
ADDV R16, R26, R27
|
||||
SGTU R13, R25, R24
|
||||
ADDV R27, R24, R26
|
||||
ADDV R8, R25, R25
|
||||
SGTU R8, R25, R24
|
||||
ADDV R24, R26, R26
|
||||
AND $3, R25, R10
|
||||
AND $-4, R25, R17
|
||||
ADDV R17, R14, R8
|
||||
ADDV R26, R15, R27
|
||||
SGTU R17, R8, R24
|
||||
SGTU R26, R27, R28
|
||||
ADDV R27, R24, R9
|
||||
SGTU R27, R9, R24
|
||||
OR R24, R28, R24
|
||||
ADDV R24, R10, R10
|
||||
SLLV $62, R26, R27
|
||||
SRLV $2, R25, R28
|
||||
SRLV $2, R26, R26
|
||||
OR R27, R28, R25
|
||||
ADDV R25, R8, R8
|
||||
ADDV R26, R9, R27
|
||||
SGTU R25, R8, R24
|
||||
SGTU R26, R27, R28
|
||||
ADDV R27, R24, R9
|
||||
SGTU R27, R9, R24
|
||||
OR R24, R28, R24
|
||||
ADDV R24, R10, R10
|
||||
|
||||
SUBV $16, R6, R6
|
||||
BGE R6, R7, loop
|
||||
|
||||
bytes_between_0_and_15:
|
||||
BEQ R6, R0, done
|
||||
MOVV $1, R14
|
||||
XOR R15, R15
|
||||
ADDV R6, R5, R5
|
||||
|
||||
flush_buffer:
|
||||
MOVBU -1(R5), R25
|
||||
SRLV $56, R14, R24
|
||||
SLLV $8, R15, R28
|
||||
SLLV $8, R14, R14
|
||||
OR R24, R28, R15
|
||||
XOR R25, R14, R14
|
||||
SUBV $1, R6, R6
|
||||
SUBV $1, R5, R5
|
||||
BNE R6, R0, flush_buffer
|
||||
|
||||
ADDV R14, R8, R8
|
||||
SGTU R14, R8, R24
|
||||
ADDV R15, R9, R27
|
||||
SGTU R15, R27, R28
|
||||
ADDV R27, R24, R9
|
||||
SGTU R27, R9, R24
|
||||
OR R24, R28, R24
|
||||
ADDV R10, R24, R10
|
||||
|
||||
MOVV $16, R6
|
||||
JMP multiply
|
||||
|
||||
done:
|
||||
MOVV R8, (R4)
|
||||
MOVV R9, 8(R4)
|
||||
MOVV R10, 16(R4)
|
||||
RET
|
||||
187
vendor/golang.org/x/crypto/internal/poly1305/sum_ppc64x.s
generated
vendored
Normal file
187
vendor/golang.org/x/crypto/internal/poly1305/sum_ppc64x.s
generated
vendored
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build gc && !purego && (ppc64 || ppc64le)
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// This was ported from the amd64 implementation.
|
||||
|
||||
#ifdef GOARCH_ppc64le
|
||||
#define LE_MOVD MOVD
|
||||
#define LE_MOVWZ MOVWZ
|
||||
#define LE_MOVHZ MOVHZ
|
||||
#else
|
||||
#define LE_MOVD MOVDBR
|
||||
#define LE_MOVWZ MOVWBR
|
||||
#define LE_MOVHZ MOVHBR
|
||||
#endif
|
||||
|
||||
#define POLY1305_ADD(msg, h0, h1, h2, t0, t1, t2) \
|
||||
LE_MOVD (msg)( R0), t0; \
|
||||
LE_MOVD (msg)(R24), t1; \
|
||||
MOVD $1, t2; \
|
||||
ADDC t0, h0, h0; \
|
||||
ADDE t1, h1, h1; \
|
||||
ADDE t2, h2; \
|
||||
ADD $16, msg
|
||||
|
||||
#define POLY1305_MUL(h0, h1, h2, r0, r1, t0, t1, t2, t3, t4, t5) \
|
||||
MULLD r0, h0, t0; \
|
||||
MULHDU r0, h0, t1; \
|
||||
MULLD r0, h1, t4; \
|
||||
MULHDU r0, h1, t5; \
|
||||
ADDC t4, t1, t1; \
|
||||
MULLD r0, h2, t2; \
|
||||
MULHDU r1, h0, t4; \
|
||||
MULLD r1, h0, h0; \
|
||||
ADDE t5, t2, t2; \
|
||||
ADDC h0, t1, t1; \
|
||||
MULLD h2, r1, t3; \
|
||||
ADDZE t4, h0; \
|
||||
MULHDU r1, h1, t5; \
|
||||
MULLD r1, h1, t4; \
|
||||
ADDC t4, t2, t2; \
|
||||
ADDE t5, t3, t3; \
|
||||
ADDC h0, t2, t2; \
|
||||
MOVD $-4, t4; \
|
||||
ADDZE t3; \
|
||||
RLDICL $0, t2, $62, h2; \
|
||||
AND t2, t4, h0; \
|
||||
ADDC t0, h0, h0; \
|
||||
ADDE t3, t1, h1; \
|
||||
SLD $62, t3, t4; \
|
||||
SRD $2, t2; \
|
||||
ADDZE h2; \
|
||||
OR t4, t2, t2; \
|
||||
SRD $2, t3; \
|
||||
ADDC t2, h0, h0; \
|
||||
ADDE t3, h1, h1; \
|
||||
ADDZE h2
|
||||
|
||||
// func update(state *[7]uint64, msg []byte)
|
||||
TEXT ·update(SB), $0-32
|
||||
MOVD state+0(FP), R3
|
||||
MOVD msg_base+8(FP), R4
|
||||
MOVD msg_len+16(FP), R5
|
||||
|
||||
MOVD 0(R3), R8 // h0
|
||||
MOVD 8(R3), R9 // h1
|
||||
MOVD 16(R3), R10 // h2
|
||||
MOVD 24(R3), R11 // r0
|
||||
MOVD 32(R3), R12 // r1
|
||||
|
||||
MOVD $8, R24
|
||||
|
||||
CMP R5, $16
|
||||
BLT bytes_between_0_and_15
|
||||
|
||||
loop:
|
||||
POLY1305_ADD(R4, R8, R9, R10, R20, R21, R22)
|
||||
|
||||
PCALIGN $16
|
||||
multiply:
|
||||
POLY1305_MUL(R8, R9, R10, R11, R12, R16, R17, R18, R14, R20, R21)
|
||||
ADD $-16, R5
|
||||
CMP R5, $16
|
||||
BGE loop
|
||||
|
||||
bytes_between_0_and_15:
|
||||
CMP R5, $0
|
||||
BEQ done
|
||||
MOVD $0, R16 // h0
|
||||
MOVD $0, R17 // h1
|
||||
|
||||
flush_buffer:
|
||||
CMP R5, $8
|
||||
BLE just1
|
||||
|
||||
MOVD $8, R21
|
||||
SUB R21, R5, R21
|
||||
|
||||
// Greater than 8 -- load the rightmost remaining bytes in msg
|
||||
// and put into R17 (h1)
|
||||
LE_MOVD (R4)(R21), R17
|
||||
MOVD $16, R22
|
||||
|
||||
// Find the offset to those bytes
|
||||
SUB R5, R22, R22
|
||||
SLD $3, R22
|
||||
|
||||
// Shift to get only the bytes in msg
|
||||
SRD R22, R17, R17
|
||||
|
||||
// Put 1 at high end
|
||||
MOVD $1, R23
|
||||
SLD $3, R21
|
||||
SLD R21, R23, R23
|
||||
OR R23, R17, R17
|
||||
|
||||
// Remainder is 8
|
||||
MOVD $8, R5
|
||||
|
||||
just1:
|
||||
CMP R5, $8
|
||||
BLT less8
|
||||
|
||||
// Exactly 8
|
||||
LE_MOVD (R4), R16
|
||||
|
||||
CMP R17, $0
|
||||
|
||||
// Check if we've already set R17; if not
|
||||
// set 1 to indicate end of msg.
|
||||
BNE carry
|
||||
MOVD $1, R17
|
||||
BR carry
|
||||
|
||||
less8:
|
||||
MOVD $0, R16 // h0
|
||||
MOVD $0, R22 // shift count
|
||||
CMP R5, $4
|
||||
BLT less4
|
||||
LE_MOVWZ (R4), R16
|
||||
ADD $4, R4
|
||||
ADD $-4, R5
|
||||
MOVD $32, R22
|
||||
|
||||
less4:
|
||||
CMP R5, $2
|
||||
BLT less2
|
||||
LE_MOVHZ (R4), R21
|
||||
SLD R22, R21, R21
|
||||
OR R16, R21, R16
|
||||
ADD $16, R22
|
||||
ADD $-2, R5
|
||||
ADD $2, R4
|
||||
|
||||
less2:
|
||||
CMP R5, $0
|
||||
BEQ insert1
|
||||
MOVBZ (R4), R21
|
||||
SLD R22, R21, R21
|
||||
OR R16, R21, R16
|
||||
ADD $8, R22
|
||||
|
||||
insert1:
|
||||
// Insert 1 at end of msg
|
||||
MOVD $1, R21
|
||||
SLD R22, R21, R21
|
||||
OR R16, R21, R16
|
||||
|
||||
carry:
|
||||
// Add new values to h0, h1, h2
|
||||
ADDC R16, R8
|
||||
ADDE R17, R9
|
||||
ADDZE R10, R10
|
||||
MOVD $16, R5
|
||||
ADD R5, R4
|
||||
BR multiply
|
||||
|
||||
done:
|
||||
// Save h0, h1, h2 in state
|
||||
MOVD R8, 0(R3)
|
||||
MOVD R9, 8(R3)
|
||||
MOVD R10, 16(R3)
|
||||
RET
|
||||
76
vendor/golang.org/x/crypto/internal/poly1305/sum_s390x.go
generated
vendored
Normal file
76
vendor/golang.org/x/crypto/internal/poly1305/sum_s390x.go
generated
vendored
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build gc && !purego
|
||||
|
||||
package poly1305
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/cpu"
|
||||
)
|
||||
|
||||
// updateVX is an assembly implementation of Poly1305 that uses vector
|
||||
// instructions. It must only be called if the vector facility (vx) is
|
||||
// available.
|
||||
//
|
||||
//go:noescape
|
||||
func updateVX(state *macState, msg []byte)
|
||||
|
||||
// mac is a replacement for macGeneric that uses a larger buffer and redirects
|
||||
// calls that would have gone to updateGeneric to updateVX if the vector
|
||||
// facility is installed.
|
||||
//
|
||||
// A larger buffer is required for good performance because the vector
|
||||
// implementation has a higher fixed cost per call than the generic
|
||||
// implementation.
|
||||
type mac struct {
|
||||
macState
|
||||
|
||||
buffer [16 * TagSize]byte // size must be a multiple of block size (16)
|
||||
offset int
|
||||
}
|
||||
|
||||
func (h *mac) Write(p []byte) (int, error) {
|
||||
nn := len(p)
|
||||
if h.offset > 0 {
|
||||
n := copy(h.buffer[h.offset:], p)
|
||||
if h.offset+n < len(h.buffer) {
|
||||
h.offset += n
|
||||
return nn, nil
|
||||
}
|
||||
p = p[n:]
|
||||
h.offset = 0
|
||||
if cpu.S390X.HasVX {
|
||||
updateVX(&h.macState, h.buffer[:])
|
||||
} else {
|
||||
updateGeneric(&h.macState, h.buffer[:])
|
||||
}
|
||||
}
|
||||
|
||||
tail := len(p) % len(h.buffer) // number of bytes to copy into buffer
|
||||
body := len(p) - tail // number of bytes to process now
|
||||
if body > 0 {
|
||||
if cpu.S390X.HasVX {
|
||||
updateVX(&h.macState, p[:body])
|
||||
} else {
|
||||
updateGeneric(&h.macState, p[:body])
|
||||
}
|
||||
}
|
||||
h.offset = copy(h.buffer[:], p[body:]) // copy tail bytes - can be 0
|
||||
return nn, nil
|
||||
}
|
||||
|
||||
func (h *mac) Sum(out *[TagSize]byte) {
|
||||
state := h.macState
|
||||
remainder := h.buffer[:h.offset]
|
||||
|
||||
// Use the generic implementation if we have 2 or fewer blocks left
|
||||
// to sum. The vector implementation has a higher startup time.
|
||||
if cpu.S390X.HasVX && len(remainder) > 2*TagSize {
|
||||
updateVX(&state, remainder)
|
||||
} else if len(remainder) > 0 {
|
||||
updateGeneric(&state, remainder)
|
||||
}
|
||||
finalize(out, &state.h, &state.s)
|
||||
}
|
||||
503
vendor/golang.org/x/crypto/internal/poly1305/sum_s390x.s
generated
vendored
Normal file
503
vendor/golang.org/x/crypto/internal/poly1305/sum_s390x.s
generated
vendored
Normal file
|
|
@ -0,0 +1,503 @@
|
|||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build gc && !purego
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// This implementation of Poly1305 uses the vector facility (vx)
|
||||
// to process up to 2 blocks (32 bytes) per iteration using an
|
||||
// algorithm based on the one described in:
|
||||
//
|
||||
// NEON crypto, Daniel J. Bernstein & Peter Schwabe
|
||||
// https://cryptojedi.org/papers/neoncrypto-20120320.pdf
|
||||
//
|
||||
// This algorithm uses 5 26-bit limbs to represent a 130-bit
|
||||
// value. These limbs are, for the most part, zero extended and
|
||||
// placed into 64-bit vector register elements. Each vector
|
||||
// register is 128-bits wide and so holds 2 of these elements.
|
||||
// Using 26-bit limbs allows us plenty of headroom to accommodate
|
||||
// accumulations before and after multiplication without
|
||||
// overflowing either 32-bits (before multiplication) or 64-bits
|
||||
// (after multiplication).
|
||||
//
|
||||
// In order to parallelise the operations required to calculate
|
||||
// the sum we use two separate accumulators and then sum those
|
||||
// in an extra final step. For compatibility with the generic
|
||||
// implementation we perform this summation at the end of every
|
||||
// updateVX call.
|
||||
//
|
||||
// To use two accumulators we must multiply the message blocks
|
||||
// by r² rather than r. Only the final message block should be
|
||||
// multiplied by r.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// We want to calculate the sum (h) for a 64 byte message (m):
|
||||
//
|
||||
// h = m[0:16]r⁴ + m[16:32]r³ + m[32:48]r² + m[48:64]r
|
||||
//
|
||||
// To do this we split the calculation into the even indices
|
||||
// and odd indices of the message. These form our SIMD 'lanes':
|
||||
//
|
||||
// h = m[ 0:16]r⁴ + m[32:48]r² + <- lane 0
|
||||
// m[16:32]r³ + m[48:64]r <- lane 1
|
||||
//
|
||||
// To calculate this iteratively we refactor so that both lanes
|
||||
// are written in terms of r² and r:
|
||||
//
|
||||
// h = (m[ 0:16]r² + m[32:48])r² + <- lane 0
|
||||
// (m[16:32]r² + m[48:64])r <- lane 1
|
||||
// ^ ^
|
||||
// | coefficients for second iteration
|
||||
// coefficients for first iteration
|
||||
//
|
||||
// So in this case we would have two iterations. In the first
|
||||
// both lanes are multiplied by r². In the second only the
|
||||
// first lane is multiplied by r² and the second lane is
|
||||
// instead multiplied by r. This gives use the odd and even
|
||||
// powers of r that we need from the original equation.
|
||||
//
|
||||
// Notation:
|
||||
//
|
||||
// h - accumulator
|
||||
// r - key
|
||||
// m - message
|
||||
//
|
||||
// [a, b] - SIMD register holding two 64-bit values
|
||||
// [a, b, c, d] - SIMD register holding four 32-bit values
|
||||
// xᵢ[n] - limb n of variable x with bit width i
|
||||
//
|
||||
// Limbs are expressed in little endian order, so for 26-bit
|
||||
// limbs x₂₆[4] will be the most significant limb and x₂₆[0]
|
||||
// will be the least significant limb.
|
||||
|
||||
// masking constants
|
||||
#define MOD24 V0 // [0x0000000000ffffff, 0x0000000000ffffff] - mask low 24-bits
|
||||
#define MOD26 V1 // [0x0000000003ffffff, 0x0000000003ffffff] - mask low 26-bits
|
||||
|
||||
// expansion constants (see EXPAND macro)
|
||||
#define EX0 V2
|
||||
#define EX1 V3
|
||||
#define EX2 V4
|
||||
|
||||
// key (r², r or 1 depending on context)
|
||||
#define R_0 V5
|
||||
#define R_1 V6
|
||||
#define R_2 V7
|
||||
#define R_3 V8
|
||||
#define R_4 V9
|
||||
|
||||
// precalculated coefficients (5r², 5r or 0 depending on context)
|
||||
#define R5_1 V10
|
||||
#define R5_2 V11
|
||||
#define R5_3 V12
|
||||
#define R5_4 V13
|
||||
|
||||
// message block (m)
|
||||
#define M_0 V14
|
||||
#define M_1 V15
|
||||
#define M_2 V16
|
||||
#define M_3 V17
|
||||
#define M_4 V18
|
||||
|
||||
// accumulator (h)
|
||||
#define H_0 V19
|
||||
#define H_1 V20
|
||||
#define H_2 V21
|
||||
#define H_3 V22
|
||||
#define H_4 V23
|
||||
|
||||
// temporary registers (for short-lived values)
|
||||
#define T_0 V24
|
||||
#define T_1 V25
|
||||
#define T_2 V26
|
||||
#define T_3 V27
|
||||
#define T_4 V28
|
||||
|
||||
GLOBL ·constants<>(SB), RODATA, $0x30
|
||||
// EX0
|
||||
DATA ·constants<>+0x00(SB)/8, $0x0006050403020100
|
||||
DATA ·constants<>+0x08(SB)/8, $0x1016151413121110
|
||||
// EX1
|
||||
DATA ·constants<>+0x10(SB)/8, $0x060c0b0a09080706
|
||||
DATA ·constants<>+0x18(SB)/8, $0x161c1b1a19181716
|
||||
// EX2
|
||||
DATA ·constants<>+0x20(SB)/8, $0x0d0d0d0d0d0f0e0d
|
||||
DATA ·constants<>+0x28(SB)/8, $0x1d1d1d1d1d1f1e1d
|
||||
|
||||
// MULTIPLY multiplies each lane of f and g, partially reduced
|
||||
// modulo 2¹³⁰ - 5. The result, h, consists of partial products
|
||||
// in each lane that need to be reduced further to produce the
|
||||
// final result.
|
||||
//
|
||||
// h₁₃₀ = (f₁₃₀g₁₃₀) % 2¹³⁰ + (5f₁₃₀g₁₃₀) / 2¹³⁰
|
||||
//
|
||||
// Note that the multiplication by 5 of the high bits is
|
||||
// achieved by precalculating the multiplication of four of the
|
||||
// g coefficients by 5. These are g51-g54.
|
||||
#define MULTIPLY(f0, f1, f2, f3, f4, g0, g1, g2, g3, g4, g51, g52, g53, g54, h0, h1, h2, h3, h4) \
|
||||
VMLOF f0, g0, h0 \
|
||||
VMLOF f0, g3, h3 \
|
||||
VMLOF f0, g1, h1 \
|
||||
VMLOF f0, g4, h4 \
|
||||
VMLOF f0, g2, h2 \
|
||||
VMLOF f1, g54, T_0 \
|
||||
VMLOF f1, g2, T_3 \
|
||||
VMLOF f1, g0, T_1 \
|
||||
VMLOF f1, g3, T_4 \
|
||||
VMLOF f1, g1, T_2 \
|
||||
VMALOF f2, g53, h0, h0 \
|
||||
VMALOF f2, g1, h3, h3 \
|
||||
VMALOF f2, g54, h1, h1 \
|
||||
VMALOF f2, g2, h4, h4 \
|
||||
VMALOF f2, g0, h2, h2 \
|
||||
VMALOF f3, g52, T_0, T_0 \
|
||||
VMALOF f3, g0, T_3, T_3 \
|
||||
VMALOF f3, g53, T_1, T_1 \
|
||||
VMALOF f3, g1, T_4, T_4 \
|
||||
VMALOF f3, g54, T_2, T_2 \
|
||||
VMALOF f4, g51, h0, h0 \
|
||||
VMALOF f4, g54, h3, h3 \
|
||||
VMALOF f4, g52, h1, h1 \
|
||||
VMALOF f4, g0, h4, h4 \
|
||||
VMALOF f4, g53, h2, h2 \
|
||||
VAG T_0, h0, h0 \
|
||||
VAG T_3, h3, h3 \
|
||||
VAG T_1, h1, h1 \
|
||||
VAG T_4, h4, h4 \
|
||||
VAG T_2, h2, h2
|
||||
|
||||
// REDUCE performs the following carry operations in four
|
||||
// stages, as specified in Bernstein & Schwabe:
|
||||
//
|
||||
// 1: h₂₆[0]->h₂₆[1] h₂₆[3]->h₂₆[4]
|
||||
// 2: h₂₆[1]->h₂₆[2] h₂₆[4]->h₂₆[0]
|
||||
// 3: h₂₆[0]->h₂₆[1] h₂₆[2]->h₂₆[3]
|
||||
// 4: h₂₆[3]->h₂₆[4]
|
||||
//
|
||||
// The result is that all of the limbs are limited to 26-bits
|
||||
// except for h₂₆[1] and h₂₆[4] which are limited to 27-bits.
|
||||
//
|
||||
// Note that although each limb is aligned at 26-bit intervals
|
||||
// they may contain values that exceed 2²⁶ - 1, hence the need
|
||||
// to carry the excess bits in each limb.
|
||||
#define REDUCE(h0, h1, h2, h3, h4) \
|
||||
VESRLG $26, h0, T_0 \
|
||||
VESRLG $26, h3, T_1 \
|
||||
VN MOD26, h0, h0 \
|
||||
VN MOD26, h3, h3 \
|
||||
VAG T_0, h1, h1 \
|
||||
VAG T_1, h4, h4 \
|
||||
VESRLG $26, h1, T_2 \
|
||||
VESRLG $26, h4, T_3 \
|
||||
VN MOD26, h1, h1 \
|
||||
VN MOD26, h4, h4 \
|
||||
VESLG $2, T_3, T_4 \
|
||||
VAG T_3, T_4, T_4 \
|
||||
VAG T_2, h2, h2 \
|
||||
VAG T_4, h0, h0 \
|
||||
VESRLG $26, h2, T_0 \
|
||||
VESRLG $26, h0, T_1 \
|
||||
VN MOD26, h2, h2 \
|
||||
VN MOD26, h0, h0 \
|
||||
VAG T_0, h3, h3 \
|
||||
VAG T_1, h1, h1 \
|
||||
VESRLG $26, h3, T_2 \
|
||||
VN MOD26, h3, h3 \
|
||||
VAG T_2, h4, h4
|
||||
|
||||
// EXPAND splits the 128-bit little-endian values in0 and in1
|
||||
// into 26-bit big-endian limbs and places the results into
|
||||
// the first and second lane of d₂₆[0:4] respectively.
|
||||
//
|
||||
// The EX0, EX1 and EX2 constants are arrays of byte indices
|
||||
// for permutation. The permutation both reverses the bytes
|
||||
// in the input and ensures the bytes are copied into the
|
||||
// destination limb ready to be shifted into their final
|
||||
// position.
|
||||
#define EXPAND(in0, in1, d0, d1, d2, d3, d4) \
|
||||
VPERM in0, in1, EX0, d0 \
|
||||
VPERM in0, in1, EX1, d2 \
|
||||
VPERM in0, in1, EX2, d4 \
|
||||
VESRLG $26, d0, d1 \
|
||||
VESRLG $30, d2, d3 \
|
||||
VESRLG $4, d2, d2 \
|
||||
VN MOD26, d0, d0 \ // [in0₂₆[0], in1₂₆[0]]
|
||||
VN MOD26, d3, d3 \ // [in0₂₆[3], in1₂₆[3]]
|
||||
VN MOD26, d1, d1 \ // [in0₂₆[1], in1₂₆[1]]
|
||||
VN MOD24, d4, d4 \ // [in0₂₆[4], in1₂₆[4]]
|
||||
VN MOD26, d2, d2 // [in0₂₆[2], in1₂₆[2]]
|
||||
|
||||
// func updateVX(state *macState, msg []byte)
|
||||
TEXT ·updateVX(SB), NOSPLIT, $0
|
||||
MOVD state+0(FP), R1
|
||||
LMG msg+8(FP), R2, R3 // R2=msg_base, R3=msg_len
|
||||
|
||||
// load EX0, EX1 and EX2
|
||||
MOVD $·constants<>(SB), R5
|
||||
VLM (R5), EX0, EX2
|
||||
|
||||
// generate masks
|
||||
VGMG $(64-24), $63, MOD24 // [0x00ffffff, 0x00ffffff]
|
||||
VGMG $(64-26), $63, MOD26 // [0x03ffffff, 0x03ffffff]
|
||||
|
||||
// load h (accumulator) and r (key) from state
|
||||
VZERO T_1 // [0, 0]
|
||||
VL 0(R1), T_0 // [h₆₄[0], h₆₄[1]]
|
||||
VLEG $0, 16(R1), T_1 // [h₆₄[2], 0]
|
||||
VL 24(R1), T_2 // [r₆₄[0], r₆₄[1]]
|
||||
VPDI $0, T_0, T_2, T_3 // [h₆₄[0], r₆₄[0]]
|
||||
VPDI $5, T_0, T_2, T_4 // [h₆₄[1], r₆₄[1]]
|
||||
|
||||
// unpack h and r into 26-bit limbs
|
||||
// note: h₆₄[2] may have the low 3 bits set, so h₂₆[4] is a 27-bit value
|
||||
VN MOD26, T_3, H_0 // [h₂₆[0], r₂₆[0]]
|
||||
VZERO H_1 // [0, 0]
|
||||
VZERO H_3 // [0, 0]
|
||||
VGMG $(64-12-14), $(63-12), T_0 // [0x03fff000, 0x03fff000] - 26-bit mask with low 12 bits masked out
|
||||
VESLG $24, T_1, T_1 // [h₆₄[2]<<24, 0]
|
||||
VERIMG $-26&63, T_3, MOD26, H_1 // [h₂₆[1], r₂₆[1]]
|
||||
VESRLG $+52&63, T_3, H_2 // [h₂₆[2], r₂₆[2]] - low 12 bits only
|
||||
VERIMG $-14&63, T_4, MOD26, H_3 // [h₂₆[1], r₂₆[1]]
|
||||
VESRLG $40, T_4, H_4 // [h₂₆[4], r₂₆[4]] - low 24 bits only
|
||||
VERIMG $+12&63, T_4, T_0, H_2 // [h₂₆[2], r₂₆[2]] - complete
|
||||
VO T_1, H_4, H_4 // [h₂₆[4], r₂₆[4]] - complete
|
||||
|
||||
// replicate r across all 4 vector elements
|
||||
VREPF $3, H_0, R_0 // [r₂₆[0], r₂₆[0], r₂₆[0], r₂₆[0]]
|
||||
VREPF $3, H_1, R_1 // [r₂₆[1], r₂₆[1], r₂₆[1], r₂₆[1]]
|
||||
VREPF $3, H_2, R_2 // [r₂₆[2], r₂₆[2], r₂₆[2], r₂₆[2]]
|
||||
VREPF $3, H_3, R_3 // [r₂₆[3], r₂₆[3], r₂₆[3], r₂₆[3]]
|
||||
VREPF $3, H_4, R_4 // [r₂₆[4], r₂₆[4], r₂₆[4], r₂₆[4]]
|
||||
|
||||
// zero out lane 1 of h
|
||||
VLEIG $1, $0, H_0 // [h₂₆[0], 0]
|
||||
VLEIG $1, $0, H_1 // [h₂₆[1], 0]
|
||||
VLEIG $1, $0, H_2 // [h₂₆[2], 0]
|
||||
VLEIG $1, $0, H_3 // [h₂₆[3], 0]
|
||||
VLEIG $1, $0, H_4 // [h₂₆[4], 0]
|
||||
|
||||
// calculate 5r (ignore least significant limb)
|
||||
VREPIF $5, T_0
|
||||
VMLF T_0, R_1, R5_1 // [5r₂₆[1], 5r₂₆[1], 5r₂₆[1], 5r₂₆[1]]
|
||||
VMLF T_0, R_2, R5_2 // [5r₂₆[2], 5r₂₆[2], 5r₂₆[2], 5r₂₆[2]]
|
||||
VMLF T_0, R_3, R5_3 // [5r₂₆[3], 5r₂₆[3], 5r₂₆[3], 5r₂₆[3]]
|
||||
VMLF T_0, R_4, R5_4 // [5r₂₆[4], 5r₂₆[4], 5r₂₆[4], 5r₂₆[4]]
|
||||
|
||||
// skip r² calculation if we are only calculating one block
|
||||
CMPBLE R3, $16, skip
|
||||
|
||||
// calculate r²
|
||||
MULTIPLY(R_0, R_1, R_2, R_3, R_4, R_0, R_1, R_2, R_3, R_4, R5_1, R5_2, R5_3, R5_4, M_0, M_1, M_2, M_3, M_4)
|
||||
REDUCE(M_0, M_1, M_2, M_3, M_4)
|
||||
VGBM $0x0f0f, T_0
|
||||
VERIMG $0, M_0, T_0, R_0 // [r₂₆[0], r²₂₆[0], r₂₆[0], r²₂₆[0]]
|
||||
VERIMG $0, M_1, T_0, R_1 // [r₂₆[1], r²₂₆[1], r₂₆[1], r²₂₆[1]]
|
||||
VERIMG $0, M_2, T_0, R_2 // [r₂₆[2], r²₂₆[2], r₂₆[2], r²₂₆[2]]
|
||||
VERIMG $0, M_3, T_0, R_3 // [r₂₆[3], r²₂₆[3], r₂₆[3], r²₂₆[3]]
|
||||
VERIMG $0, M_4, T_0, R_4 // [r₂₆[4], r²₂₆[4], r₂₆[4], r²₂₆[4]]
|
||||
|
||||
// calculate 5r² (ignore least significant limb)
|
||||
VREPIF $5, T_0
|
||||
VMLF T_0, R_1, R5_1 // [5r₂₆[1], 5r²₂₆[1], 5r₂₆[1], 5r²₂₆[1]]
|
||||
VMLF T_0, R_2, R5_2 // [5r₂₆[2], 5r²₂₆[2], 5r₂₆[2], 5r²₂₆[2]]
|
||||
VMLF T_0, R_3, R5_3 // [5r₂₆[3], 5r²₂₆[3], 5r₂₆[3], 5r²₂₆[3]]
|
||||
VMLF T_0, R_4, R5_4 // [5r₂₆[4], 5r²₂₆[4], 5r₂₆[4], 5r²₂₆[4]]
|
||||
|
||||
loop:
|
||||
CMPBLE R3, $32, b2 // 2 or fewer blocks remaining, need to change key coefficients
|
||||
|
||||
// load next 2 blocks from message
|
||||
VLM (R2), T_0, T_1
|
||||
|
||||
// update message slice
|
||||
SUB $32, R3
|
||||
MOVD $32(R2), R2
|
||||
|
||||
// unpack message blocks into 26-bit big-endian limbs
|
||||
EXPAND(T_0, T_1, M_0, M_1, M_2, M_3, M_4)
|
||||
|
||||
// add 2¹²⁸ to each message block value
|
||||
VLEIB $4, $1, M_4
|
||||
VLEIB $12, $1, M_4
|
||||
|
||||
multiply:
|
||||
// accumulate the incoming message
|
||||
VAG H_0, M_0, M_0
|
||||
VAG H_3, M_3, M_3
|
||||
VAG H_1, M_1, M_1
|
||||
VAG H_4, M_4, M_4
|
||||
VAG H_2, M_2, M_2
|
||||
|
||||
// multiply the accumulator by the key coefficient
|
||||
MULTIPLY(M_0, M_1, M_2, M_3, M_4, R_0, R_1, R_2, R_3, R_4, R5_1, R5_2, R5_3, R5_4, H_0, H_1, H_2, H_3, H_4)
|
||||
|
||||
// carry and partially reduce the partial products
|
||||
REDUCE(H_0, H_1, H_2, H_3, H_4)
|
||||
|
||||
CMPBNE R3, $0, loop
|
||||
|
||||
finish:
|
||||
// sum lane 0 and lane 1 and put the result in lane 1
|
||||
VZERO T_0
|
||||
VSUMQG H_0, T_0, H_0
|
||||
VSUMQG H_3, T_0, H_3
|
||||
VSUMQG H_1, T_0, H_1
|
||||
VSUMQG H_4, T_0, H_4
|
||||
VSUMQG H_2, T_0, H_2
|
||||
|
||||
// reduce again after summation
|
||||
// TODO(mundaym): there might be a more efficient way to do this
|
||||
// now that we only have 1 active lane. For example, we could
|
||||
// simultaneously pack the values as we reduce them.
|
||||
REDUCE(H_0, H_1, H_2, H_3, H_4)
|
||||
|
||||
// carry h[1] through to h[4] so that only h[4] can exceed 2²⁶ - 1
|
||||
// TODO(mundaym): in testing this final carry was unnecessary.
|
||||
// Needs a proof before it can be removed though.
|
||||
VESRLG $26, H_1, T_1
|
||||
VN MOD26, H_1, H_1
|
||||
VAQ T_1, H_2, H_2
|
||||
VESRLG $26, H_2, T_2
|
||||
VN MOD26, H_2, H_2
|
||||
VAQ T_2, H_3, H_3
|
||||
VESRLG $26, H_3, T_3
|
||||
VN MOD26, H_3, H_3
|
||||
VAQ T_3, H_4, H_4
|
||||
|
||||
// h is now < 2(2¹³⁰ - 5)
|
||||
// Pack each lane in h₂₆[0:4] into h₁₂₈[0:1].
|
||||
VESLG $26, H_1, H_1
|
||||
VESLG $26, H_3, H_3
|
||||
VO H_0, H_1, H_0
|
||||
VO H_2, H_3, H_2
|
||||
VESLG $4, H_2, H_2
|
||||
VLEIB $7, $48, H_1
|
||||
VSLB H_1, H_2, H_2
|
||||
VO H_0, H_2, H_0
|
||||
VLEIB $7, $104, H_1
|
||||
VSLB H_1, H_4, H_3
|
||||
VO H_3, H_0, H_0
|
||||
VLEIB $7, $24, H_1
|
||||
VSRLB H_1, H_4, H_1
|
||||
|
||||
// update state
|
||||
VSTEG $1, H_0, 0(R1)
|
||||
VSTEG $0, H_0, 8(R1)
|
||||
VSTEG $1, H_1, 16(R1)
|
||||
RET
|
||||
|
||||
b2: // 2 or fewer blocks remaining
|
||||
CMPBLE R3, $16, b1
|
||||
|
||||
// Load the 2 remaining blocks (17-32 bytes remaining).
|
||||
MOVD $-17(R3), R0 // index of final byte to load modulo 16
|
||||
VL (R2), T_0 // load full 16 byte block
|
||||
VLL R0, 16(R2), T_1 // load final (possibly partial) block and pad with zeros to 16 bytes
|
||||
|
||||
// The Poly1305 algorithm requires that a 1 bit be appended to
|
||||
// each message block. If the final block is less than 16 bytes
|
||||
// long then it is easiest to insert the 1 before the message
|
||||
// block is split into 26-bit limbs. If, on the other hand, the
|
||||
// final message block is 16 bytes long then we append the 1 bit
|
||||
// after expansion as normal.
|
||||
MOVBZ $1, R0
|
||||
MOVD $-16(R3), R3 // index of byte in last block to insert 1 at (could be 16)
|
||||
CMPBEQ R3, $16, 2(PC) // skip the insertion if the final block is 16 bytes long
|
||||
VLVGB R3, R0, T_1 // insert 1 into the byte at index R3
|
||||
|
||||
// Split both blocks into 26-bit limbs in the appropriate lanes.
|
||||
EXPAND(T_0, T_1, M_0, M_1, M_2, M_3, M_4)
|
||||
|
||||
// Append a 1 byte to the end of the second to last block.
|
||||
VLEIB $4, $1, M_4
|
||||
|
||||
// Append a 1 byte to the end of the last block only if it is a
|
||||
// full 16 byte block.
|
||||
CMPBNE R3, $16, 2(PC)
|
||||
VLEIB $12, $1, M_4
|
||||
|
||||
// Finally, set up the coefficients for the final multiplication.
|
||||
// We have previously saved r and 5r in the 32-bit even indexes
|
||||
// of the R_[0-4] and R5_[1-4] coefficient registers.
|
||||
//
|
||||
// We want lane 0 to be multiplied by r² so that can be kept the
|
||||
// same. We want lane 1 to be multiplied by r so we need to move
|
||||
// the saved r value into the 32-bit odd index in lane 1 by
|
||||
// rotating the 64-bit lane by 32.
|
||||
VGBM $0x00ff, T_0 // [0, 0xffffffffffffffff] - mask lane 1 only
|
||||
VERIMG $32, R_0, T_0, R_0 // [_, r²₂₆[0], _, r₂₆[0]]
|
||||
VERIMG $32, R_1, T_0, R_1 // [_, r²₂₆[1], _, r₂₆[1]]
|
||||
VERIMG $32, R_2, T_0, R_2 // [_, r²₂₆[2], _, r₂₆[2]]
|
||||
VERIMG $32, R_3, T_0, R_3 // [_, r²₂₆[3], _, r₂₆[3]]
|
||||
VERIMG $32, R_4, T_0, R_4 // [_, r²₂₆[4], _, r₂₆[4]]
|
||||
VERIMG $32, R5_1, T_0, R5_1 // [_, 5r²₂₆[1], _, 5r₂₆[1]]
|
||||
VERIMG $32, R5_2, T_0, R5_2 // [_, 5r²₂₆[2], _, 5r₂₆[2]]
|
||||
VERIMG $32, R5_3, T_0, R5_3 // [_, 5r²₂₆[3], _, 5r₂₆[3]]
|
||||
VERIMG $32, R5_4, T_0, R5_4 // [_, 5r²₂₆[4], _, 5r₂₆[4]]
|
||||
|
||||
MOVD $0, R3
|
||||
BR multiply
|
||||
|
||||
skip:
|
||||
CMPBEQ R3, $0, finish
|
||||
|
||||
b1: // 1 block remaining
|
||||
|
||||
// Load the final block (1-16 bytes). This will be placed into
|
||||
// lane 0.
|
||||
MOVD $-1(R3), R0
|
||||
VLL R0, (R2), T_0 // pad to 16 bytes with zeros
|
||||
|
||||
// The Poly1305 algorithm requires that a 1 bit be appended to
|
||||
// each message block. If the final block is less than 16 bytes
|
||||
// long then it is easiest to insert the 1 before the message
|
||||
// block is split into 26-bit limbs. If, on the other hand, the
|
||||
// final message block is 16 bytes long then we append the 1 bit
|
||||
// after expansion as normal.
|
||||
MOVBZ $1, R0
|
||||
CMPBEQ R3, $16, 2(PC)
|
||||
VLVGB R3, R0, T_0
|
||||
|
||||
// Set the message block in lane 1 to the value 0 so that it
|
||||
// can be accumulated without affecting the final result.
|
||||
VZERO T_1
|
||||
|
||||
// Split the final message block into 26-bit limbs in lane 0.
|
||||
// Lane 1 will be contain 0.
|
||||
EXPAND(T_0, T_1, M_0, M_1, M_2, M_3, M_4)
|
||||
|
||||
// Append a 1 byte to the end of the last block only if it is a
|
||||
// full 16 byte block.
|
||||
CMPBNE R3, $16, 2(PC)
|
||||
VLEIB $4, $1, M_4
|
||||
|
||||
// We have previously saved r and 5r in the 32-bit even indexes
|
||||
// of the R_[0-4] and R5_[1-4] coefficient registers.
|
||||
//
|
||||
// We want lane 0 to be multiplied by r so we need to move the
|
||||
// saved r value into the 32-bit odd index in lane 0. We want
|
||||
// lane 1 to be set to the value 1. This makes multiplication
|
||||
// a no-op. We do this by setting lane 1 in every register to 0
|
||||
// and then just setting the 32-bit index 3 in R_0 to 1.
|
||||
VZERO T_0
|
||||
MOVD $0, R0
|
||||
MOVD $0x10111213, R12
|
||||
VLVGP R12, R0, T_1 // [_, 0x10111213, _, 0x00000000]
|
||||
VPERM T_0, R_0, T_1, R_0 // [_, r₂₆[0], _, 0]
|
||||
VPERM T_0, R_1, T_1, R_1 // [_, r₂₆[1], _, 0]
|
||||
VPERM T_0, R_2, T_1, R_2 // [_, r₂₆[2], _, 0]
|
||||
VPERM T_0, R_3, T_1, R_3 // [_, r₂₆[3], _, 0]
|
||||
VPERM T_0, R_4, T_1, R_4 // [_, r₂₆[4], _, 0]
|
||||
VPERM T_0, R5_1, T_1, R5_1 // [_, 5r₂₆[1], _, 0]
|
||||
VPERM T_0, R5_2, T_1, R5_2 // [_, 5r₂₆[2], _, 0]
|
||||
VPERM T_0, R5_3, T_1, R5_3 // [_, 5r₂₆[3], _, 0]
|
||||
VPERM T_0, R5_4, T_1, R5_4 // [_, 5r₂₆[4], _, 0]
|
||||
|
||||
// Set the value of lane 1 to be 1.
|
||||
VLEIF $3, $1, R_0 // [_, r₂₆[0], _, 1]
|
||||
|
||||
MOVD $0, R3
|
||||
BR multiply
|
||||
97
vendor/golang.org/x/crypto/ssh/buffer.go
generated
vendored
Normal file
97
vendor/golang.org/x/crypto/ssh/buffer.go
generated
vendored
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package ssh
|
||||
|
||||
import (
|
||||
"io"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// buffer provides a linked list buffer for data exchange
|
||||
// between producer and consumer. Theoretically the buffer is
|
||||
// of unlimited capacity as it does no allocation of its own.
|
||||
type buffer struct {
|
||||
// protects concurrent access to head, tail and closed
|
||||
*sync.Cond
|
||||
|
||||
head *element // the buffer that will be read first
|
||||
tail *element // the buffer that will be read last
|
||||
|
||||
closed bool
|
||||
}
|
||||
|
||||
// An element represents a single link in a linked list.
|
||||
type element struct {
|
||||
buf []byte
|
||||
next *element
|
||||
}
|
||||
|
||||
// newBuffer returns an empty buffer that is not closed.
|
||||
func newBuffer() *buffer {
|
||||
e := new(element)
|
||||
b := &buffer{
|
||||
Cond: newCond(),
|
||||
head: e,
|
||||
tail: e,
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// write makes buf available for Read to receive.
|
||||
// buf must not be modified after the call to write.
|
||||
func (b *buffer) write(buf []byte) {
|
||||
b.Cond.L.Lock()
|
||||
e := &element{buf: buf}
|
||||
b.tail.next = e
|
||||
b.tail = e
|
||||
b.Cond.Signal()
|
||||
b.Cond.L.Unlock()
|
||||
}
|
||||
|
||||
// eof closes the buffer. Reads from the buffer once all
|
||||
// the data has been consumed will receive io.EOF.
|
||||
func (b *buffer) eof() {
|
||||
b.Cond.L.Lock()
|
||||
b.closed = true
|
||||
b.Cond.Signal()
|
||||
b.Cond.L.Unlock()
|
||||
}
|
||||
|
||||
// Read reads data from the internal buffer in buf. Reads will block
|
||||
// if no data is available, or until the buffer is closed.
|
||||
func (b *buffer) Read(buf []byte) (n int, err error) {
|
||||
b.Cond.L.Lock()
|
||||
defer b.Cond.L.Unlock()
|
||||
|
||||
for len(buf) > 0 {
|
||||
// if there is data in b.head, copy it
|
||||
if len(b.head.buf) > 0 {
|
||||
r := copy(buf, b.head.buf)
|
||||
buf, b.head.buf = buf[r:], b.head.buf[r:]
|
||||
n += r
|
||||
continue
|
||||
}
|
||||
// if there is a next buffer, make it the head
|
||||
if len(b.head.buf) == 0 && b.head != b.tail {
|
||||
b.head = b.head.next
|
||||
continue
|
||||
}
|
||||
|
||||
// if at least one byte has been copied, return
|
||||
if n > 0 {
|
||||
break
|
||||
}
|
||||
|
||||
// if nothing was read, and there is nothing outstanding
|
||||
// check to see if the buffer is closed.
|
||||
if b.closed {
|
||||
err = io.EOF
|
||||
break
|
||||
}
|
||||
// out of buffers, wait for producer
|
||||
b.Cond.Wait()
|
||||
}
|
||||
return
|
||||
}
|
||||
624
vendor/golang.org/x/crypto/ssh/certs.go
generated
vendored
Normal file
624
vendor/golang.org/x/crypto/ssh/certs.go
generated
vendored
Normal file
|
|
@ -0,0 +1,624 @@
|
|||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package ssh
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"sort"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Certificate algorithm names from [PROTOCOL.certkeys]. These values can appear
|
||||
// in Certificate.Type, PublicKey.Type, and ClientConfig.HostKeyAlgorithms.
|
||||
// Unlike key algorithm names, these are not passed to AlgorithmSigner nor
|
||||
// returned by MultiAlgorithmSigner and don't appear in the Signature.Format
|
||||
// field.
|
||||
const (
|
||||
CertAlgoRSAv01 = "ssh-rsa-cert-v01@openssh.com"
|
||||
// Deprecated: DSA is only supported at insecure key sizes, and was removed
|
||||
// from major implementations.
|
||||
CertAlgoDSAv01 = InsecureCertAlgoDSAv01
|
||||
// Deprecated: DSA is only supported at insecure key sizes, and was removed
|
||||
// from major implementations.
|
||||
InsecureCertAlgoDSAv01 = "ssh-dss-cert-v01@openssh.com"
|
||||
CertAlgoECDSA256v01 = "ecdsa-sha2-nistp256-cert-v01@openssh.com"
|
||||
CertAlgoECDSA384v01 = "ecdsa-sha2-nistp384-cert-v01@openssh.com"
|
||||
CertAlgoECDSA521v01 = "ecdsa-sha2-nistp521-cert-v01@openssh.com"
|
||||
CertAlgoSKECDSA256v01 = "sk-ecdsa-sha2-nistp256-cert-v01@openssh.com"
|
||||
CertAlgoED25519v01 = "ssh-ed25519-cert-v01@openssh.com"
|
||||
CertAlgoSKED25519v01 = "sk-ssh-ed25519-cert-v01@openssh.com"
|
||||
|
||||
// CertAlgoRSASHA256v01 and CertAlgoRSASHA512v01 can't appear as a
|
||||
// Certificate.Type (or PublicKey.Type), but only in
|
||||
// ClientConfig.HostKeyAlgorithms.
|
||||
CertAlgoRSASHA256v01 = "rsa-sha2-256-cert-v01@openssh.com"
|
||||
CertAlgoRSASHA512v01 = "rsa-sha2-512-cert-v01@openssh.com"
|
||||
)
|
||||
|
||||
const (
|
||||
// Deprecated: use CertAlgoRSAv01.
|
||||
CertSigAlgoRSAv01 = CertAlgoRSAv01
|
||||
// Deprecated: use CertAlgoRSASHA256v01.
|
||||
CertSigAlgoRSASHA2256v01 = CertAlgoRSASHA256v01
|
||||
// Deprecated: use CertAlgoRSASHA512v01.
|
||||
CertSigAlgoRSASHA2512v01 = CertAlgoRSASHA512v01
|
||||
)
|
||||
|
||||
// Certificate types distinguish between host and user
|
||||
// certificates. The values can be set in the CertType field of
|
||||
// Certificate.
|
||||
const (
|
||||
UserCert = 1
|
||||
HostCert = 2
|
||||
)
|
||||
|
||||
// Signature represents a cryptographic signature.
|
||||
type Signature struct {
|
||||
Format string
|
||||
Blob []byte
|
||||
Rest []byte `ssh:"rest"`
|
||||
}
|
||||
|
||||
// CertTimeInfinity can be used for OpenSSHCertV01.ValidBefore to indicate that
|
||||
// a certificate does not expire.
|
||||
const CertTimeInfinity = 1<<64 - 1
|
||||
|
||||
// An Certificate represents an OpenSSH certificate as defined in
|
||||
// [PROTOCOL.certkeys]?rev=1.8. The Certificate type implements the
|
||||
// PublicKey interface, so it can be unmarshaled using
|
||||
// ParsePublicKey.
|
||||
type Certificate struct {
|
||||
Nonce []byte
|
||||
Key PublicKey
|
||||
Serial uint64
|
||||
CertType uint32
|
||||
KeyId string
|
||||
ValidPrincipals []string
|
||||
ValidAfter uint64
|
||||
ValidBefore uint64
|
||||
Permissions
|
||||
Reserved []byte
|
||||
SignatureKey PublicKey
|
||||
Signature *Signature
|
||||
}
|
||||
|
||||
// genericCertData holds the key-independent part of the certificate data.
|
||||
// Overall, certificates contain an nonce, public key fields and
|
||||
// key-independent fields.
|
||||
type genericCertData struct {
|
||||
Serial uint64
|
||||
CertType uint32
|
||||
KeyId string
|
||||
ValidPrincipals []byte
|
||||
ValidAfter uint64
|
||||
ValidBefore uint64
|
||||
CriticalOptions []byte
|
||||
Extensions []byte
|
||||
Reserved []byte
|
||||
SignatureKey []byte
|
||||
Signature []byte
|
||||
}
|
||||
|
||||
func marshalStringList(namelist []string) []byte {
|
||||
var to []byte
|
||||
for _, name := range namelist {
|
||||
s := struct{ N string }{name}
|
||||
to = append(to, Marshal(&s)...)
|
||||
}
|
||||
return to
|
||||
}
|
||||
|
||||
type optionsTuple struct {
|
||||
Key string
|
||||
Value []byte
|
||||
}
|
||||
|
||||
type optionsTupleValue struct {
|
||||
Value string
|
||||
}
|
||||
|
||||
// serialize a map of critical options or extensions
|
||||
// issue #10569 - per [PROTOCOL.certkeys] and SSH implementation,
|
||||
// we need two length prefixes for a non-empty string value
|
||||
func marshalTuples(tups map[string]string) []byte {
|
||||
keys := make([]string, 0, len(tups))
|
||||
for key := range tups {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
var ret []byte
|
||||
for _, key := range keys {
|
||||
s := optionsTuple{Key: key}
|
||||
if value := tups[key]; len(value) > 0 {
|
||||
s.Value = Marshal(&optionsTupleValue{value})
|
||||
}
|
||||
ret = append(ret, Marshal(&s)...)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// issue #10569 - per [PROTOCOL.certkeys] and SSH implementation,
|
||||
// we need two length prefixes for a non-empty option value
|
||||
func parseTuples(in []byte) (map[string]string, error) {
|
||||
tups := map[string]string{}
|
||||
var lastKey string
|
||||
var haveLastKey bool
|
||||
|
||||
for len(in) > 0 {
|
||||
var key, val, extra []byte
|
||||
var ok bool
|
||||
|
||||
if key, in, ok = parseString(in); !ok {
|
||||
return nil, errShortRead
|
||||
}
|
||||
keyStr := string(key)
|
||||
// according to [PROTOCOL.certkeys], the names must be in
|
||||
// lexical order.
|
||||
if haveLastKey && keyStr <= lastKey {
|
||||
return nil, fmt.Errorf("ssh: certificate options are not in lexical order")
|
||||
}
|
||||
lastKey, haveLastKey = keyStr, true
|
||||
// the next field is a data field, which if non-empty has a string embedded
|
||||
if val, in, ok = parseString(in); !ok {
|
||||
return nil, errShortRead
|
||||
}
|
||||
if len(val) > 0 {
|
||||
val, extra, ok = parseString(val)
|
||||
if !ok {
|
||||
return nil, errShortRead
|
||||
}
|
||||
if len(extra) > 0 {
|
||||
return nil, fmt.Errorf("ssh: unexpected trailing data after certificate option value")
|
||||
}
|
||||
tups[keyStr] = string(val)
|
||||
} else {
|
||||
tups[keyStr] = ""
|
||||
}
|
||||
}
|
||||
return tups, nil
|
||||
}
|
||||
|
||||
func parseCert(in []byte, privAlgo string) (*Certificate, error) {
|
||||
nonce, rest, ok := parseString(in)
|
||||
if !ok {
|
||||
return nil, errShortRead
|
||||
}
|
||||
|
||||
key, rest, err := parsePubKey(rest, privAlgo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var g genericCertData
|
||||
if err := Unmarshal(rest, &g); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c := &Certificate{
|
||||
Nonce: nonce,
|
||||
Key: key,
|
||||
Serial: g.Serial,
|
||||
CertType: g.CertType,
|
||||
KeyId: g.KeyId,
|
||||
ValidAfter: g.ValidAfter,
|
||||
ValidBefore: g.ValidBefore,
|
||||
}
|
||||
|
||||
for principals := g.ValidPrincipals; len(principals) > 0; {
|
||||
principal, rest, ok := parseString(principals)
|
||||
if !ok {
|
||||
return nil, errShortRead
|
||||
}
|
||||
c.ValidPrincipals = append(c.ValidPrincipals, string(principal))
|
||||
principals = rest
|
||||
}
|
||||
|
||||
c.CriticalOptions, err = parseTuples(g.CriticalOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.Extensions, err = parseTuples(g.Extensions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.Reserved = g.Reserved
|
||||
k, err := ParsePublicKey(g.SignatureKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// The Type() function is intended to return only certificate key types, but
|
||||
// we use certKeyAlgoNames anyway for safety, to match [Certificate.Type].
|
||||
if _, ok := certKeyAlgoNames[k.Type()]; ok {
|
||||
return nil, fmt.Errorf("ssh: the signature key type %q is invalid for certificates", k.Type())
|
||||
}
|
||||
c.SignatureKey = k
|
||||
c.Signature, rest, ok = parseSignatureBody(g.Signature)
|
||||
if !ok || len(rest) > 0 {
|
||||
return nil, errors.New("ssh: signature parse error")
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
type openSSHCertSigner struct {
|
||||
pub *Certificate
|
||||
signer Signer
|
||||
}
|
||||
|
||||
type algorithmOpenSSHCertSigner struct {
|
||||
*openSSHCertSigner
|
||||
algorithmSigner AlgorithmSigner
|
||||
}
|
||||
|
||||
// NewCertSigner returns a Signer that signs with the given Certificate, whose
|
||||
// private key is held by signer. It returns an error if the public key in cert
|
||||
// doesn't match the key used by signer.
|
||||
func NewCertSigner(cert *Certificate, signer Signer) (Signer, error) {
|
||||
if !bytes.Equal(cert.Key.Marshal(), signer.PublicKey().Marshal()) {
|
||||
return nil, errors.New("ssh: signer and cert have different public key")
|
||||
}
|
||||
|
||||
switch s := signer.(type) {
|
||||
case MultiAlgorithmSigner:
|
||||
return &multiAlgorithmSigner{
|
||||
AlgorithmSigner: &algorithmOpenSSHCertSigner{
|
||||
&openSSHCertSigner{cert, signer}, s},
|
||||
supportedAlgorithms: s.Algorithms(),
|
||||
}, nil
|
||||
case AlgorithmSigner:
|
||||
return &algorithmOpenSSHCertSigner{
|
||||
&openSSHCertSigner{cert, signer}, s}, nil
|
||||
default:
|
||||
return &openSSHCertSigner{cert, signer}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (s *openSSHCertSigner) Sign(rand io.Reader, data []byte) (*Signature, error) {
|
||||
return s.signer.Sign(rand, data)
|
||||
}
|
||||
|
||||
func (s *openSSHCertSigner) PublicKey() PublicKey {
|
||||
return s.pub
|
||||
}
|
||||
|
||||
func (s *algorithmOpenSSHCertSigner) SignWithAlgorithm(rand io.Reader, data []byte, algorithm string) (*Signature, error) {
|
||||
return s.algorithmSigner.SignWithAlgorithm(rand, data, algorithm)
|
||||
}
|
||||
|
||||
const sourceAddressCriticalOption = "source-address"
|
||||
|
||||
// CertChecker does the work of verifying a certificate. Its methods
|
||||
// can be plugged into ClientConfig.HostKeyCallback and
|
||||
// ServerConfig.PublicKeyCallback. For the CertChecker to work,
|
||||
// minimally, the IsAuthority callback should be set.
|
||||
type CertChecker struct {
|
||||
// SupportedCriticalOptions lists the CriticalOptions that the
|
||||
// server application layer understands. These are only used
|
||||
// for user certificates.
|
||||
SupportedCriticalOptions []string
|
||||
|
||||
// IsUserAuthority should return true if the key is recognized as an
|
||||
// authority for user certificate. This must be set if this CertChecker
|
||||
// will be checking user certificates.
|
||||
IsUserAuthority func(auth PublicKey) bool
|
||||
|
||||
// IsHostAuthority should report whether the key is recognized as
|
||||
// an authority for this host. This must be set if this CertChecker
|
||||
// will be checking host certificates.
|
||||
IsHostAuthority func(auth PublicKey, address string) bool
|
||||
|
||||
// Clock is used for verifying time stamps. If nil, time.Now
|
||||
// is used.
|
||||
Clock func() time.Time
|
||||
|
||||
// UserKeyFallback is called when CertChecker.Authenticate encounters a
|
||||
// public key that is not a certificate. It must implement validation
|
||||
// of user keys or else, if nil, all such keys are rejected.
|
||||
UserKeyFallback func(conn ConnMetadata, key PublicKey) (*Permissions, error)
|
||||
|
||||
// HostKeyFallback is called when CertChecker.CheckHostKey encounters a
|
||||
// public key that is not a certificate. It must implement host key
|
||||
// validation or else, if nil, all such keys are rejected.
|
||||
HostKeyFallback HostKeyCallback
|
||||
|
||||
// IsRevoked is called for each certificate so that revocation checking
|
||||
// can be implemented. It should return true if the given certificate
|
||||
// is revoked and false otherwise. If nil, no certificates are
|
||||
// considered to have been revoked.
|
||||
IsRevoked func(cert *Certificate) bool
|
||||
}
|
||||
|
||||
// CheckHostKey checks a host key certificate. This method can be
|
||||
// plugged into ClientConfig.HostKeyCallback.
|
||||
func (c *CertChecker) CheckHostKey(addr string, remote net.Addr, key PublicKey) error {
|
||||
cert, ok := key.(*Certificate)
|
||||
if !ok {
|
||||
if c.HostKeyFallback != nil {
|
||||
return c.HostKeyFallback(addr, remote, key)
|
||||
}
|
||||
return errors.New("ssh: non-certificate host key")
|
||||
}
|
||||
if cert.CertType != HostCert {
|
||||
return fmt.Errorf("ssh: certificate presented as a host key has type %d", cert.CertType)
|
||||
}
|
||||
if !c.IsHostAuthority(cert.SignatureKey, addr) {
|
||||
return fmt.Errorf("ssh: no authorities for hostname: %v", addr)
|
||||
}
|
||||
|
||||
hostname, _, err := net.SplitHostPort(addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Pass hostname only as principal for host certificates (consistent with OpenSSH)
|
||||
return c.CheckCert(hostname, cert)
|
||||
}
|
||||
|
||||
// Authenticate checks a user certificate. Authenticate can be used as
|
||||
// a value for ServerConfig.PublicKeyCallback.
|
||||
func (c *CertChecker) Authenticate(conn ConnMetadata, pubKey PublicKey) (*Permissions, error) {
|
||||
cert, ok := pubKey.(*Certificate)
|
||||
if !ok {
|
||||
if c.UserKeyFallback != nil {
|
||||
return c.UserKeyFallback(conn, pubKey)
|
||||
}
|
||||
return nil, errors.New("ssh: normal key pairs not accepted")
|
||||
}
|
||||
|
||||
if cert.CertType != UserCert {
|
||||
return nil, fmt.Errorf("ssh: cert has type %d", cert.CertType)
|
||||
}
|
||||
if !c.IsUserAuthority(cert.SignatureKey) {
|
||||
return nil, fmt.Errorf("ssh: certificate signed by unrecognized authority")
|
||||
}
|
||||
|
||||
if err := c.CheckCert(conn.User(), cert); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &cert.Permissions, nil
|
||||
}
|
||||
|
||||
// CheckCert checks CriticalOptions, ValidPrincipals, revocation, timestamp and
|
||||
// the signature of the certificate.
|
||||
func (c *CertChecker) CheckCert(principal string, cert *Certificate) error {
|
||||
if c.IsRevoked != nil && c.IsRevoked(cert) {
|
||||
return fmt.Errorf("ssh: certificate serial %d revoked", cert.Serial)
|
||||
}
|
||||
|
||||
for opt := range cert.CriticalOptions {
|
||||
// sourceAddressCriticalOption will be enforced by
|
||||
// serverAuthenticate
|
||||
if opt == sourceAddressCriticalOption {
|
||||
continue
|
||||
}
|
||||
|
||||
found := false
|
||||
for _, supp := range c.SupportedCriticalOptions {
|
||||
if supp == opt {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return fmt.Errorf("ssh: unsupported critical option %q in certificate", opt)
|
||||
}
|
||||
}
|
||||
|
||||
if len(cert.ValidPrincipals) > 0 {
|
||||
// By default, certs are valid for all users/hosts.
|
||||
found := false
|
||||
for _, p := range cert.ValidPrincipals {
|
||||
if p == principal {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return fmt.Errorf("ssh: principal %q not in the set of valid principals for given certificate: %q", principal, cert.ValidPrincipals)
|
||||
}
|
||||
}
|
||||
|
||||
clock := c.Clock
|
||||
if clock == nil {
|
||||
clock = time.Now
|
||||
}
|
||||
|
||||
unixNow := clock().Unix()
|
||||
if after := int64(cert.ValidAfter); after < 0 || unixNow < int64(cert.ValidAfter) {
|
||||
return fmt.Errorf("ssh: cert is not yet valid")
|
||||
}
|
||||
if before := int64(cert.ValidBefore); cert.ValidBefore != uint64(CertTimeInfinity) && (unixNow >= before || before < 0) {
|
||||
return fmt.Errorf("ssh: cert has expired")
|
||||
}
|
||||
if err := cert.SignatureKey.Verify(cert.bytesForSigning(), cert.Signature); err != nil {
|
||||
return fmt.Errorf("ssh: certificate signature does not verify")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SignCert signs the certificate with an authority, setting the Nonce,
|
||||
// SignatureKey, and Signature fields. If the authority implements the
|
||||
// MultiAlgorithmSigner interface the first algorithm in the list is used. This
|
||||
// is useful if you want to sign with a specific algorithm. As specified in
|
||||
// [SSH-CERTS], Section 2.1.1, authority can't be a [Certificate].
|
||||
func (c *Certificate) SignCert(rand io.Reader, authority Signer) error {
|
||||
c.Nonce = make([]byte, 32)
|
||||
if _, err := io.ReadFull(rand, c.Nonce); err != nil {
|
||||
return err
|
||||
}
|
||||
// The Type() function is intended to return only certificate key types, but
|
||||
// we use certKeyAlgoNames anyway for safety, to match [Certificate.Type].
|
||||
if _, ok := certKeyAlgoNames[authority.PublicKey().Type()]; ok {
|
||||
return fmt.Errorf("ssh: certificates cannot be used as authority (public key type %q)",
|
||||
authority.PublicKey().Type())
|
||||
}
|
||||
c.SignatureKey = authority.PublicKey()
|
||||
|
||||
if v, ok := authority.(MultiAlgorithmSigner); ok {
|
||||
if len(v.Algorithms()) == 0 {
|
||||
return errors.New("the provided authority has no signature algorithm")
|
||||
}
|
||||
// Use the first algorithm in the list.
|
||||
sig, err := v.SignWithAlgorithm(rand, c.bytesForSigning(), v.Algorithms()[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Signature = sig
|
||||
return nil
|
||||
} else if v, ok := authority.(AlgorithmSigner); ok && v.PublicKey().Type() == KeyAlgoRSA {
|
||||
// Default to KeyAlgoRSASHA512 for ssh-rsa signers.
|
||||
// TODO: consider using KeyAlgoRSASHA256 as default.
|
||||
sig, err := v.SignWithAlgorithm(rand, c.bytesForSigning(), KeyAlgoRSASHA512)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Signature = sig
|
||||
return nil
|
||||
}
|
||||
|
||||
sig, err := authority.Sign(rand, c.bytesForSigning())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Signature = sig
|
||||
return nil
|
||||
}
|
||||
|
||||
// certKeyAlgoNames is a mapping from known certificate algorithm names to the
|
||||
// corresponding public key signature algorithm.
|
||||
//
|
||||
// This map must be kept in sync with the one in agent/client.go.
|
||||
var certKeyAlgoNames = map[string]string{
|
||||
CertAlgoRSAv01: KeyAlgoRSA,
|
||||
CertAlgoRSASHA256v01: KeyAlgoRSASHA256,
|
||||
CertAlgoRSASHA512v01: KeyAlgoRSASHA512,
|
||||
InsecureCertAlgoDSAv01: InsecureKeyAlgoDSA,
|
||||
CertAlgoECDSA256v01: KeyAlgoECDSA256,
|
||||
CertAlgoECDSA384v01: KeyAlgoECDSA384,
|
||||
CertAlgoECDSA521v01: KeyAlgoECDSA521,
|
||||
CertAlgoSKECDSA256v01: KeyAlgoSKECDSA256,
|
||||
CertAlgoED25519v01: KeyAlgoED25519,
|
||||
CertAlgoSKED25519v01: KeyAlgoSKED25519,
|
||||
}
|
||||
|
||||
// underlyingAlgo returns the signature algorithm associated with algo (which is
|
||||
// an advertised or negotiated public key or host key algorithm). These are
|
||||
// usually the same, except for certificate algorithms.
|
||||
func underlyingAlgo(algo string) string {
|
||||
if a, ok := certKeyAlgoNames[algo]; ok {
|
||||
return a
|
||||
}
|
||||
return algo
|
||||
}
|
||||
|
||||
// certificateAlgo returns the certificate algorithms that uses the provided
|
||||
// underlying signature algorithm.
|
||||
func certificateAlgo(algo string) (certAlgo string, ok bool) {
|
||||
for certName, algoName := range certKeyAlgoNames {
|
||||
if algoName == algo {
|
||||
return certName, true
|
||||
}
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
func (cert *Certificate) bytesForSigning() []byte {
|
||||
c2 := *cert
|
||||
c2.Signature = nil
|
||||
out := c2.Marshal()
|
||||
// Drop trailing signature length.
|
||||
return out[:len(out)-4]
|
||||
}
|
||||
|
||||
// Marshal serializes c into OpenSSH's wire format. It is part of the
|
||||
// PublicKey interface.
|
||||
func (c *Certificate) Marshal() []byte {
|
||||
generic := genericCertData{
|
||||
Serial: c.Serial,
|
||||
CertType: c.CertType,
|
||||
KeyId: c.KeyId,
|
||||
ValidPrincipals: marshalStringList(c.ValidPrincipals),
|
||||
ValidAfter: uint64(c.ValidAfter),
|
||||
ValidBefore: uint64(c.ValidBefore),
|
||||
CriticalOptions: marshalTuples(c.CriticalOptions),
|
||||
Extensions: marshalTuples(c.Extensions),
|
||||
Reserved: c.Reserved,
|
||||
SignatureKey: c.SignatureKey.Marshal(),
|
||||
}
|
||||
if c.Signature != nil {
|
||||
generic.Signature = Marshal(c.Signature)
|
||||
}
|
||||
genericBytes := Marshal(&generic)
|
||||
keyBytes := c.Key.Marshal()
|
||||
_, keyBytes, _ = parseString(keyBytes)
|
||||
prefix := Marshal(&struct {
|
||||
Name string
|
||||
Nonce []byte
|
||||
Key []byte `ssh:"rest"`
|
||||
}{c.Type(), c.Nonce, keyBytes})
|
||||
|
||||
result := make([]byte, 0, len(prefix)+len(genericBytes))
|
||||
result = append(result, prefix...)
|
||||
result = append(result, genericBytes...)
|
||||
return result
|
||||
}
|
||||
|
||||
// Type returns the certificate algorithm name. It is part of the PublicKey interface.
|
||||
func (c *Certificate) Type() string {
|
||||
certName, ok := certificateAlgo(c.Key.Type())
|
||||
if !ok {
|
||||
panic("unknown certificate type for key type " + c.Key.Type())
|
||||
}
|
||||
return certName
|
||||
}
|
||||
|
||||
// Verify verifies a signature against the certificate's public
|
||||
// key. It is part of the PublicKey interface.
|
||||
func (c *Certificate) Verify(data []byte, sig *Signature) error {
|
||||
return c.Key.Verify(data, sig)
|
||||
}
|
||||
|
||||
func parseSignatureBody(in []byte) (out *Signature, rest []byte, ok bool) {
|
||||
format, in, ok := parseString(in)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
out = &Signature{
|
||||
Format: string(format),
|
||||
}
|
||||
|
||||
if out.Blob, in, ok = parseString(in); !ok {
|
||||
return
|
||||
}
|
||||
|
||||
switch out.Format {
|
||||
case KeyAlgoSKECDSA256, CertAlgoSKECDSA256v01, KeyAlgoSKED25519, CertAlgoSKED25519v01:
|
||||
out.Rest = in
|
||||
return out, nil, ok
|
||||
}
|
||||
|
||||
return out, in, ok
|
||||
}
|
||||
|
||||
func parseSignature(in []byte) (out *Signature, rest []byte, ok bool) {
|
||||
sigBytes, rest, ok := parseString(in)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
out, trailing, ok := parseSignatureBody(sigBytes)
|
||||
if !ok || len(trailing) > 0 {
|
||||
return nil, nil, false
|
||||
}
|
||||
return
|
||||
}
|
||||
645
vendor/golang.org/x/crypto/ssh/channel.go
generated
vendored
Normal file
645
vendor/golang.org/x/crypto/ssh/channel.go
generated
vendored
Normal file
|
|
@ -0,0 +1,645 @@
|
|||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package ssh
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const (
|
||||
minPacketLength = 9
|
||||
// channelMaxPacket contains the maximum number of bytes that will be
|
||||
// sent in a single packet. As per RFC 4253, section 6.1, 32k is also
|
||||
// the minimum.
|
||||
channelMaxPacket = 1 << 15
|
||||
// We follow OpenSSH here.
|
||||
channelWindowSize = 64 * channelMaxPacket
|
||||
)
|
||||
|
||||
// NewChannel represents an incoming request to a channel. It must either be
|
||||
// accepted for use by calling Accept, or rejected by calling Reject.
|
||||
type NewChannel interface {
|
||||
// Accept accepts the channel creation request. It returns the Channel
|
||||
// and a Go channel containing SSH requests. The Go channel must be
|
||||
// serviced otherwise the Channel will hang.
|
||||
Accept() (Channel, <-chan *Request, error)
|
||||
|
||||
// Reject rejects the channel creation request. After calling
|
||||
// this, no other methods on the Channel may be called.
|
||||
Reject(reason RejectionReason, message string) error
|
||||
|
||||
// ChannelType returns the type of the channel, as supplied by the
|
||||
// client.
|
||||
ChannelType() string
|
||||
|
||||
// ExtraData returns the arbitrary payload for this channel, as supplied
|
||||
// by the client. This data is specific to the channel type.
|
||||
ExtraData() []byte
|
||||
}
|
||||
|
||||
// A Channel is an ordered, reliable, flow-controlled, duplex stream
|
||||
// that is multiplexed over an SSH connection.
|
||||
type Channel interface {
|
||||
// Read reads up to len(data) bytes from the channel.
|
||||
Read(data []byte) (int, error)
|
||||
|
||||
// Write writes len(data) bytes to the channel.
|
||||
Write(data []byte) (int, error)
|
||||
|
||||
// Close signals end of channel use. No data may be sent after this
|
||||
// call.
|
||||
Close() error
|
||||
|
||||
// CloseWrite signals the end of sending in-band
|
||||
// data. Requests may still be sent, and the other side may
|
||||
// still send data
|
||||
CloseWrite() error
|
||||
|
||||
// SendRequest sends a channel request. If wantReply is true,
|
||||
// it will wait for a reply and return the result as a
|
||||
// boolean, otherwise the return value will be false. Channel
|
||||
// requests are out-of-band messages so they may be sent even
|
||||
// if the data stream is closed or blocked by flow control.
|
||||
// If the channel is closed before a reply is returned, io.EOF
|
||||
// is returned.
|
||||
SendRequest(name string, wantReply bool, payload []byte) (bool, error)
|
||||
|
||||
// Stderr returns an io.ReadWriter that writes to this channel
|
||||
// with the extended data type set to stderr. Stderr may
|
||||
// safely be read and written from a different goroutine than
|
||||
// Read and Write respectively.
|
||||
Stderr() io.ReadWriter
|
||||
}
|
||||
|
||||
// Request is a request sent outside of the normal stream of
|
||||
// data. Requests can either be specific to an SSH channel, or they
|
||||
// can be global.
|
||||
type Request struct {
|
||||
Type string
|
||||
WantReply bool
|
||||
Payload []byte
|
||||
|
||||
ch *channel
|
||||
mux *mux
|
||||
}
|
||||
|
||||
// Reply sends a response to a request. It must be called for all requests
|
||||
// where WantReply is true and is a no-op otherwise. The payload argument is
|
||||
// ignored for replies to channel-specific requests.
|
||||
func (r *Request) Reply(ok bool, payload []byte) error {
|
||||
if !r.WantReply {
|
||||
return nil
|
||||
}
|
||||
|
||||
if r.ch == nil {
|
||||
return r.mux.ackRequest(ok, payload)
|
||||
}
|
||||
|
||||
return r.ch.ackRequest(ok)
|
||||
}
|
||||
|
||||
// RejectionReason is an enumeration used when rejecting channel creation
|
||||
// requests. See RFC 4254, section 5.1.
|
||||
type RejectionReason uint32
|
||||
|
||||
const (
|
||||
Prohibited RejectionReason = iota + 1
|
||||
ConnectionFailed
|
||||
UnknownChannelType
|
||||
ResourceShortage
|
||||
)
|
||||
|
||||
// String converts the rejection reason to human readable form.
|
||||
func (r RejectionReason) String() string {
|
||||
switch r {
|
||||
case Prohibited:
|
||||
return "administratively prohibited"
|
||||
case ConnectionFailed:
|
||||
return "connect failed"
|
||||
case UnknownChannelType:
|
||||
return "unknown channel type"
|
||||
case ResourceShortage:
|
||||
return "resource shortage"
|
||||
}
|
||||
return fmt.Sprintf("unknown reason %d", int(r))
|
||||
}
|
||||
|
||||
func min(a uint32, b int) uint32 {
|
||||
if a < uint32(b) {
|
||||
return a
|
||||
}
|
||||
return uint32(b)
|
||||
}
|
||||
|
||||
type channelDirection uint8
|
||||
|
||||
const (
|
||||
channelInbound channelDirection = iota
|
||||
channelOutbound
|
||||
)
|
||||
|
||||
// channel is an implementation of the Channel interface that works
|
||||
// with the mux class.
|
||||
type channel struct {
|
||||
// R/O after creation
|
||||
chanType string
|
||||
extraData []byte
|
||||
localId, remoteId uint32
|
||||
|
||||
// maxIncomingPayload and maxRemotePayload are the maximum
|
||||
// payload sizes of normal and extended data packets for
|
||||
// receiving and sending, respectively. The wire packet will
|
||||
// be 9 or 13 bytes larger (excluding encryption overhead).
|
||||
maxIncomingPayload uint32
|
||||
maxRemotePayload uint32
|
||||
|
||||
mux *mux
|
||||
|
||||
// decided is set to true if an accept or reject message has been sent
|
||||
// (for outbound channels) or received (for inbound channels).
|
||||
decided bool
|
||||
|
||||
// direction contains either channelOutbound, for channels created
|
||||
// locally, or channelInbound, for channels created by the peer.
|
||||
direction channelDirection
|
||||
|
||||
// Pending internal channel messages.
|
||||
msg chan interface{}
|
||||
|
||||
// Since requests have no ID, there can be only one request
|
||||
// with WantReply=true outstanding. This lock is held by a
|
||||
// goroutine that has such an outgoing request pending.
|
||||
sentRequestMu sync.Mutex
|
||||
|
||||
incomingRequests chan *Request
|
||||
|
||||
sentEOF bool
|
||||
|
||||
// thread-safe data
|
||||
remoteWin window
|
||||
pending *buffer
|
||||
extPending *buffer
|
||||
|
||||
// windowMu protects myWindow, the flow-control window, and myConsumed,
|
||||
// the number of bytes consumed since we last increased myWindow
|
||||
windowMu sync.Mutex
|
||||
myWindow uint32
|
||||
myConsumed uint32
|
||||
|
||||
// writeMu serializes calls to mux.conn.writePacket() and
|
||||
// protects sentClose and packetPool. This mutex must be
|
||||
// different from windowMu, as writePacket can block if there
|
||||
// is a key exchange pending.
|
||||
writeMu sync.Mutex
|
||||
sentClose bool
|
||||
|
||||
// packetPool has a buffer for each extended channel ID to
|
||||
// save allocations during writes.
|
||||
packetPool map[uint32][]byte
|
||||
}
|
||||
|
||||
// writePacket sends a packet. If the packet is a channel close, it updates
|
||||
// sentClose. This method takes the lock c.writeMu.
|
||||
func (ch *channel) writePacket(packet []byte) error {
|
||||
ch.writeMu.Lock()
|
||||
if ch.sentClose {
|
||||
ch.writeMu.Unlock()
|
||||
return io.EOF
|
||||
}
|
||||
ch.sentClose = (packet[0] == msgChannelClose)
|
||||
err := ch.mux.conn.writePacket(packet)
|
||||
ch.writeMu.Unlock()
|
||||
return err
|
||||
}
|
||||
|
||||
func (ch *channel) sendMessage(msg interface{}) error {
|
||||
if debugMux {
|
||||
log.Printf("send(%d): %#v", ch.mux.chanList.offset, msg)
|
||||
}
|
||||
|
||||
p := Marshal(msg)
|
||||
binary.BigEndian.PutUint32(p[1:], ch.remoteId)
|
||||
return ch.writePacket(p)
|
||||
}
|
||||
|
||||
// WriteExtended writes data to a specific extended stream. These streams are
|
||||
// used, for example, for stderr.
|
||||
func (ch *channel) WriteExtended(data []byte, extendedCode uint32) (n int, err error) {
|
||||
if ch.sentEOF {
|
||||
return 0, io.EOF
|
||||
}
|
||||
// 1 byte message type, 4 bytes remoteId, 4 bytes data length
|
||||
opCode := byte(msgChannelData)
|
||||
headerLength := uint32(9)
|
||||
if extendedCode > 0 {
|
||||
headerLength += 4
|
||||
opCode = msgChannelExtendedData
|
||||
}
|
||||
|
||||
ch.writeMu.Lock()
|
||||
packet := ch.packetPool[extendedCode]
|
||||
// We don't remove the buffer from packetPool, so
|
||||
// WriteExtended calls from different goroutines will be
|
||||
// flagged as errors by the race detector.
|
||||
ch.writeMu.Unlock()
|
||||
|
||||
for len(data) > 0 {
|
||||
space := min(ch.maxRemotePayload, len(data))
|
||||
if space, err = ch.remoteWin.reserve(space); err != nil {
|
||||
return n, err
|
||||
}
|
||||
if want := headerLength + space; uint32(cap(packet)) < want {
|
||||
packet = make([]byte, want)
|
||||
} else {
|
||||
packet = packet[:want]
|
||||
}
|
||||
|
||||
todo := data[:space]
|
||||
|
||||
packet[0] = opCode
|
||||
binary.BigEndian.PutUint32(packet[1:], ch.remoteId)
|
||||
if extendedCode > 0 {
|
||||
binary.BigEndian.PutUint32(packet[5:], uint32(extendedCode))
|
||||
}
|
||||
binary.BigEndian.PutUint32(packet[headerLength-4:], uint32(len(todo)))
|
||||
copy(packet[headerLength:], todo)
|
||||
if err = ch.writePacket(packet); err != nil {
|
||||
return n, err
|
||||
}
|
||||
|
||||
n += len(todo)
|
||||
data = data[len(todo):]
|
||||
}
|
||||
|
||||
ch.writeMu.Lock()
|
||||
ch.packetPool[extendedCode] = packet
|
||||
ch.writeMu.Unlock()
|
||||
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (ch *channel) handleData(packet []byte) error {
|
||||
headerLen := 9
|
||||
isExtendedData := packet[0] == msgChannelExtendedData
|
||||
if isExtendedData {
|
||||
headerLen = 13
|
||||
}
|
||||
if len(packet) < headerLen {
|
||||
// malformed data packet
|
||||
return parseError(packet[0])
|
||||
}
|
||||
|
||||
var extended uint32
|
||||
if isExtendedData {
|
||||
extended = binary.BigEndian.Uint32(packet[5:])
|
||||
}
|
||||
|
||||
length := binary.BigEndian.Uint32(packet[headerLen-4 : headerLen])
|
||||
if length == 0 {
|
||||
return nil
|
||||
}
|
||||
if length > ch.maxIncomingPayload {
|
||||
// TODO(hanwen): should send Disconnect?
|
||||
return errors.New("ssh: incoming packet exceeds maximum payload size")
|
||||
}
|
||||
|
||||
data := packet[headerLen:]
|
||||
if length != uint32(len(data)) {
|
||||
return errors.New("ssh: wrong packet length")
|
||||
}
|
||||
|
||||
ch.windowMu.Lock()
|
||||
if ch.myWindow < length {
|
||||
ch.windowMu.Unlock()
|
||||
// TODO(hanwen): should send Disconnect with reason?
|
||||
return errors.New("ssh: remote side wrote too much")
|
||||
}
|
||||
ch.myWindow -= length
|
||||
ch.windowMu.Unlock()
|
||||
|
||||
if extended == 1 {
|
||||
ch.extPending.write(data)
|
||||
} else if extended > 0 {
|
||||
// discard other extended data.
|
||||
} else {
|
||||
ch.pending.write(data)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *channel) adjustWindow(adj uint32) error {
|
||||
c.windowMu.Lock()
|
||||
// Since myConsumed and myWindow are managed on our side, and can never
|
||||
// exceed the initial window setting, we don't worry about overflow.
|
||||
c.myConsumed += adj
|
||||
var sendAdj uint32
|
||||
if (channelWindowSize-c.myWindow > 3*c.maxIncomingPayload) ||
|
||||
(c.myWindow < channelWindowSize/2) {
|
||||
sendAdj = c.myConsumed
|
||||
c.myConsumed = 0
|
||||
c.myWindow += sendAdj
|
||||
}
|
||||
c.windowMu.Unlock()
|
||||
if sendAdj == 0 {
|
||||
return nil
|
||||
}
|
||||
return c.sendMessage(windowAdjustMsg{
|
||||
AdditionalBytes: sendAdj,
|
||||
})
|
||||
}
|
||||
|
||||
func (c *channel) ReadExtended(data []byte, extended uint32) (n int, err error) {
|
||||
switch extended {
|
||||
case 1:
|
||||
n, err = c.extPending.Read(data)
|
||||
case 0:
|
||||
n, err = c.pending.Read(data)
|
||||
default:
|
||||
return 0, fmt.Errorf("ssh: extended code %d unimplemented", extended)
|
||||
}
|
||||
|
||||
if n > 0 {
|
||||
err = c.adjustWindow(uint32(n))
|
||||
// sendWindowAdjust can return io.EOF if the remote
|
||||
// peer has closed the connection, however we want to
|
||||
// defer forwarding io.EOF to the caller of Read until
|
||||
// the buffer has been drained.
|
||||
if n > 0 && err == io.EOF {
|
||||
err = nil
|
||||
}
|
||||
}
|
||||
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (c *channel) close() {
|
||||
c.pending.eof()
|
||||
c.extPending.eof()
|
||||
close(c.msg)
|
||||
close(c.incomingRequests)
|
||||
c.writeMu.Lock()
|
||||
// This is not necessary for a normal channel teardown, but if
|
||||
// there was another error, it is.
|
||||
c.sentClose = true
|
||||
c.writeMu.Unlock()
|
||||
// Unblock writers.
|
||||
c.remoteWin.close()
|
||||
}
|
||||
|
||||
// responseMessageReceived is called when a success or failure message is
|
||||
// received on a channel to check that such a message is reasonable for the
|
||||
// given channel.
|
||||
func (ch *channel) responseMessageReceived() error {
|
||||
if ch.direction == channelInbound {
|
||||
return errors.New("ssh: channel response message received on inbound channel")
|
||||
}
|
||||
if ch.decided {
|
||||
return errors.New("ssh: duplicate response received for channel")
|
||||
}
|
||||
ch.decided = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ch *channel) handlePacket(packet []byte) error {
|
||||
switch packet[0] {
|
||||
case msgChannelData, msgChannelExtendedData:
|
||||
return ch.handleData(packet)
|
||||
case msgChannelClose:
|
||||
ch.sendMessage(channelCloseMsg{PeersID: ch.remoteId})
|
||||
ch.mux.chanList.remove(ch.localId)
|
||||
ch.close()
|
||||
return nil
|
||||
case msgChannelEOF:
|
||||
// RFC 4254 is mute on how EOF affects dataExt messages but
|
||||
// it is logical to signal EOF at the same time.
|
||||
ch.extPending.eof()
|
||||
ch.pending.eof()
|
||||
return nil
|
||||
}
|
||||
|
||||
decoded, err := decode(packet)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch msg := decoded.(type) {
|
||||
case *channelOpenFailureMsg:
|
||||
if err := ch.responseMessageReceived(); err != nil {
|
||||
return err
|
||||
}
|
||||
ch.mux.chanList.remove(msg.PeersID)
|
||||
ch.msg <- msg
|
||||
case *channelOpenConfirmMsg:
|
||||
if err := ch.responseMessageReceived(); err != nil {
|
||||
return err
|
||||
}
|
||||
if msg.MaxPacketSize < minPacketLength || msg.MaxPacketSize > 1<<31 {
|
||||
return fmt.Errorf("ssh: invalid MaxPacketSize %d from peer", msg.MaxPacketSize)
|
||||
}
|
||||
ch.remoteId = msg.MyID
|
||||
ch.maxRemotePayload = msg.MaxPacketSize
|
||||
ch.remoteWin.add(msg.MyWindow)
|
||||
ch.msg <- msg
|
||||
case *windowAdjustMsg:
|
||||
if !ch.remoteWin.add(msg.AdditionalBytes) {
|
||||
return fmt.Errorf("ssh: invalid window update for %d bytes", msg.AdditionalBytes)
|
||||
}
|
||||
case *channelRequestMsg:
|
||||
req := Request{
|
||||
Type: msg.Request,
|
||||
WantReply: msg.WantReply,
|
||||
Payload: msg.RequestSpecificData,
|
||||
ch: ch,
|
||||
}
|
||||
|
||||
ch.incomingRequests <- &req
|
||||
default:
|
||||
ch.msg <- msg
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mux) newChannel(chanType string, direction channelDirection, extraData []byte) *channel {
|
||||
ch := &channel{
|
||||
remoteWin: window{Cond: newCond()},
|
||||
myWindow: channelWindowSize,
|
||||
pending: newBuffer(),
|
||||
extPending: newBuffer(),
|
||||
direction: direction,
|
||||
incomingRequests: make(chan *Request, chanSize),
|
||||
msg: make(chan interface{}, chanSize),
|
||||
chanType: chanType,
|
||||
extraData: extraData,
|
||||
mux: m,
|
||||
packetPool: make(map[uint32][]byte),
|
||||
}
|
||||
ch.localId = m.chanList.add(ch)
|
||||
return ch
|
||||
}
|
||||
|
||||
var errUndecided = errors.New("ssh: must Accept or Reject channel")
|
||||
var errDecidedAlready = errors.New("ssh: can call Accept or Reject only once")
|
||||
|
||||
type extChannel struct {
|
||||
code uint32
|
||||
ch *channel
|
||||
}
|
||||
|
||||
func (e *extChannel) Write(data []byte) (n int, err error) {
|
||||
return e.ch.WriteExtended(data, e.code)
|
||||
}
|
||||
|
||||
func (e *extChannel) Read(data []byte) (n int, err error) {
|
||||
return e.ch.ReadExtended(data, e.code)
|
||||
}
|
||||
|
||||
func (ch *channel) Accept() (Channel, <-chan *Request, error) {
|
||||
if ch.decided {
|
||||
return nil, nil, errDecidedAlready
|
||||
}
|
||||
ch.maxIncomingPayload = channelMaxPacket
|
||||
confirm := channelOpenConfirmMsg{
|
||||
PeersID: ch.remoteId,
|
||||
MyID: ch.localId,
|
||||
MyWindow: ch.myWindow,
|
||||
MaxPacketSize: ch.maxIncomingPayload,
|
||||
}
|
||||
ch.decided = true
|
||||
if err := ch.sendMessage(confirm); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return ch, ch.incomingRequests, nil
|
||||
}
|
||||
|
||||
func (ch *channel) Reject(reason RejectionReason, message string) error {
|
||||
if ch.decided {
|
||||
return errDecidedAlready
|
||||
}
|
||||
reject := channelOpenFailureMsg{
|
||||
PeersID: ch.remoteId,
|
||||
Reason: reason,
|
||||
Message: message,
|
||||
Language: "en",
|
||||
}
|
||||
ch.decided = true
|
||||
return ch.sendMessage(reject)
|
||||
}
|
||||
|
||||
func (ch *channel) Read(data []byte) (int, error) {
|
||||
if !ch.decided {
|
||||
return 0, errUndecided
|
||||
}
|
||||
return ch.ReadExtended(data, 0)
|
||||
}
|
||||
|
||||
func (ch *channel) Write(data []byte) (int, error) {
|
||||
if !ch.decided {
|
||||
return 0, errUndecided
|
||||
}
|
||||
return ch.WriteExtended(data, 0)
|
||||
}
|
||||
|
||||
func (ch *channel) CloseWrite() error {
|
||||
if !ch.decided {
|
||||
return errUndecided
|
||||
}
|
||||
ch.sentEOF = true
|
||||
return ch.sendMessage(channelEOFMsg{
|
||||
PeersID: ch.remoteId})
|
||||
}
|
||||
|
||||
func (ch *channel) Close() error {
|
||||
if !ch.decided {
|
||||
return errUndecided
|
||||
}
|
||||
|
||||
return ch.sendMessage(channelCloseMsg{
|
||||
PeersID: ch.remoteId})
|
||||
}
|
||||
|
||||
// Extended returns an io.ReadWriter that sends and receives data on the given,
|
||||
// SSH extended stream. Such streams are used, for example, for stderr.
|
||||
func (ch *channel) Extended(code uint32) io.ReadWriter {
|
||||
if !ch.decided {
|
||||
return nil
|
||||
}
|
||||
return &extChannel{code, ch}
|
||||
}
|
||||
|
||||
func (ch *channel) Stderr() io.ReadWriter {
|
||||
return ch.Extended(1)
|
||||
}
|
||||
|
||||
func (ch *channel) SendRequest(name string, wantReply bool, payload []byte) (bool, error) {
|
||||
if !ch.decided {
|
||||
return false, errUndecided
|
||||
}
|
||||
|
||||
if wantReply {
|
||||
ch.sentRequestMu.Lock()
|
||||
defer ch.sentRequestMu.Unlock()
|
||||
}
|
||||
|
||||
msg := channelRequestMsg{
|
||||
PeersID: ch.remoteId,
|
||||
Request: name,
|
||||
WantReply: wantReply,
|
||||
RequestSpecificData: payload,
|
||||
}
|
||||
|
||||
if err := ch.sendMessage(msg); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if wantReply {
|
||||
m, ok := (<-ch.msg)
|
||||
if !ok {
|
||||
return false, io.EOF
|
||||
}
|
||||
switch m.(type) {
|
||||
case *channelRequestFailureMsg:
|
||||
return false, nil
|
||||
case *channelRequestSuccessMsg:
|
||||
return true, nil
|
||||
default:
|
||||
return false, fmt.Errorf("ssh: unexpected response to channel request: %#v", m)
|
||||
}
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// ackRequest either sends an ack or nack to the channel request.
|
||||
func (ch *channel) ackRequest(ok bool) error {
|
||||
if !ch.decided {
|
||||
return errUndecided
|
||||
}
|
||||
|
||||
var msg interface{}
|
||||
if !ok {
|
||||
msg = channelRequestFailureMsg{
|
||||
PeersID: ch.remoteId,
|
||||
}
|
||||
} else {
|
||||
msg = channelRequestSuccessMsg{
|
||||
PeersID: ch.remoteId,
|
||||
}
|
||||
}
|
||||
return ch.sendMessage(msg)
|
||||
}
|
||||
|
||||
func (ch *channel) ChannelType() string {
|
||||
return ch.chanType
|
||||
}
|
||||
|
||||
func (ch *channel) ExtraData() []byte {
|
||||
return ch.extraData
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue