rclone/backend/sftp/ssh_internal.go

100 lines
2.4 KiB
Go

//go:build !plan9
package sftp
import (
"context"
"io"
"net"
"github.com/rclone/rclone/fs"
"github.com/rclone/rclone/fs/fshttp"
"github.com/rclone/rclone/lib/proxy"
"golang.org/x/crypto/ssh"
)
// Internal ssh connections with "golang.org/x/crypto/ssh"
type sshClientInternal struct {
srv *ssh.Client
}
// newSSHClientInternal starts a client connection to the given SSH server. It is a
// convenience function that connects to the given network address,
// initiates the SSH handshake, and then sets up a Client.
func (f *Fs) newSSHClientInternal(ctx context.Context, network, addr string, sshConfig *ssh.ClientConfig) (sshClient, error) {
baseDialer := fshttp.NewDialer(ctx)
var (
conn net.Conn
err error
)
if f.opt.SocksProxy != "" {
conn, err = proxy.SOCKS5Dial(network, addr, f.opt.SocksProxy, baseDialer)
} else {
conn, err = baseDialer.Dial(network, addr)
}
if err != nil {
return nil, err
}
c, chans, reqs, err := ssh.NewClientConn(conn, addr, sshConfig)
if err != nil {
return nil, err
}
fs.Debugf(f, "New connection %s->%s to %q", c.LocalAddr(), c.RemoteAddr(), c.ServerVersion())
srv := ssh.NewClient(c, chans, reqs)
return sshClientInternal{srv}, nil
}
// Wait for connection to close
func (s sshClientInternal) Wait() error {
return s.srv.Conn.Wait()
}
// Send a keepalive over the ssh connection
func (s sshClientInternal) SendKeepAlive() {
_, _, err := s.srv.SendRequest("keepalive@openssh.com", true, nil)
if err != nil {
fs.Debugf(nil, "Failed to send keep alive: %v", err)
}
}
// Close the connection
func (s sshClientInternal) Close() error {
return s.srv.Close()
}
// CanReuse indicates if this client can be reused
func (s sshClientInternal) CanReuse() bool {
return true
}
// Check interfaces
var _ sshClient = sshClientInternal{}
// Thin wrapper for *ssh.Session to implement sshSession interface
type sshSessionInternal struct {
*ssh.Session
}
// Set the stdout
func (s sshSessionInternal) SetStdout(wr io.Writer) {
s.Session.Stdout = wr
}
// Set the stderr
func (s sshSessionInternal) SetStderr(wr io.Writer) {
s.Session.Stderr = wr
}
// NewSession makes an sshSession from an sshClient
func (s sshClientInternal) NewSession() (sshSession, error) {
session, err := s.srv.NewSession()
if err != nil {
return nil, err
}
return sshSessionInternal{Session: session}, nil
}
// Check interfaces
var _ sshSession = sshSessionInternal{}