mirror of
https://github.com/xjasonlyu/tun2socks.git
synced 2026-10-20 08:18:35 +00:00
107 lines
2.3 KiB
Go
107 lines
2.3 KiB
Go
package ssh
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"net"
|
|
"net/url"
|
|
"os"
|
|
|
|
"golang.org/x/crypto/ssh"
|
|
|
|
"github.com/xjasonlyu/tun2socks/v2/dialer"
|
|
M "github.com/xjasonlyu/tun2socks/v2/metadata"
|
|
"github.com/xjasonlyu/tun2socks/v2/proxy"
|
|
"github.com/xjasonlyu/tun2socks/v2/proxy/internal/utils"
|
|
)
|
|
|
|
type SSH struct {
|
|
addr string
|
|
config *ssh.ClientConfig
|
|
}
|
|
|
|
func New(addr, user, pass, keyFile, passphrase string) (*SSH, error) {
|
|
var auth []ssh.AuthMethod
|
|
if pass != "" {
|
|
auth = append(auth, ssh.Password(pass))
|
|
}
|
|
if keyFile != "" {
|
|
key, err := os.ReadFile(keyFile)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("ssh: read file: %w", err)
|
|
}
|
|
var signer ssh.Signer
|
|
if passphrase != "" {
|
|
signer, err = ssh.ParsePrivateKeyWithPassphrase(
|
|
key, []byte(passphrase))
|
|
} else {
|
|
signer, err = ssh.ParsePrivateKey(key)
|
|
}
|
|
if err != nil {
|
|
return nil, fmt.Errorf("ssh: parse private key: %w", err)
|
|
}
|
|
auth = append(auth, ssh.PublicKeys(signer))
|
|
}
|
|
return &SSH{
|
|
addr: addr,
|
|
config: &ssh.ClientConfig{
|
|
User: user,
|
|
Auth: auth,
|
|
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
|
Timeout: utils.TCPConnectTimeout,
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
func (s *SSH) DialContext(ctx context.Context, metadata *M.Metadata) (_ net.Conn, err error) {
|
|
c, err := dialer.DialContext(ctx, "tcp", s.addr)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("connect to %s: %w", s.addr, err)
|
|
}
|
|
utils.SetKeepAlive(c)
|
|
|
|
defer func(c net.Conn) {
|
|
utils.SafeConnClose(c, err)
|
|
}(c)
|
|
|
|
sc, ch, reqs, err := ssh.NewClientConn(c, s.addr, s.config)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
client := ssh.NewClient(sc, ch, reqs)
|
|
conn, err := client.Dial("tcp", metadata.DestinationAddress())
|
|
if err != nil {
|
|
client.Close()
|
|
return nil, err
|
|
}
|
|
return &sshConn{conn, client}, nil
|
|
}
|
|
|
|
func (s *SSH) DialUDP(*M.Metadata) (net.PacketConn, error) {
|
|
return nil, errors.ErrUnsupported
|
|
}
|
|
|
|
type sshConn struct {
|
|
net.Conn
|
|
client *ssh.Client
|
|
}
|
|
|
|
func (c *sshConn) Close() error {
|
|
defer c.client.Close()
|
|
return c.Conn.Close()
|
|
}
|
|
|
|
func Parse(u *url.URL) (proxy.Proxy, error) {
|
|
address, username := u.Host, u.User.Username()
|
|
password, _ := u.User.Password()
|
|
keyFile := u.Query().Get("privateKeyFile")
|
|
passphrase := u.Query().Get("passphrase")
|
|
return New(address, username, password, keyFile, passphrase)
|
|
}
|
|
|
|
func init() {
|
|
proxy.RegisterProtocol("ssh", Parse)
|
|
}
|