mirror of
https://github.com/MetaCubeX/mihomo.git
synced 2024-11-16 11:42:43 +08:00
feat: add ssh outbound (#1087)
* feat: add ssh outbound * fix: Modify the way to get dstAddr --------- Co-authored-by: trevid <trevidmy@gmail.com>
This commit is contained in:
parent
37b02b18f7
commit
0bb5568de9
98
adapter/outbound/ssh.go
Normal file
98
adapter/outbound/ssh.go
Normal file
|
@ -0,0 +1,98 @@
|
|||
package outbound
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"os"
|
||||
"runtime"
|
||||
"strconv"
|
||||
|
||||
CN "github.com/metacubex/mihomo/common/net"
|
||||
"github.com/metacubex/mihomo/component/dialer"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
type Ssh struct {
|
||||
*Base
|
||||
|
||||
option *SshOption
|
||||
client *ssh.Client
|
||||
}
|
||||
|
||||
type SshOption struct {
|
||||
BasicOption
|
||||
Name string `proxy:"name"`
|
||||
Server string `proxy:"server"`
|
||||
Port int `proxy:"port"`
|
||||
UserName string `proxy:"username"`
|
||||
Password string `proxy:"password,omitempty"`
|
||||
PrivateKey string `proxy:"privateKey,omitempty"`
|
||||
}
|
||||
|
||||
func (h *Ssh) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.Conn, err error) {
|
||||
c, err := h.client.Dial("tcp", metadata.RemoteAddress())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewConn(CN.NewRefConn(c, h), h), nil
|
||||
}
|
||||
|
||||
func closeSsh(h *Ssh) {
|
||||
if h.client != nil {
|
||||
_ = h.client.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func NewSsh(option SshOption) (*Ssh, error) {
|
||||
addr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port))
|
||||
|
||||
config := ssh.ClientConfig{
|
||||
User: option.UserName,
|
||||
HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
if option.Password == "" {
|
||||
|
||||
b, err := os.ReadFile(option.PrivateKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pKey, err := ssh.ParsePrivateKey(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
config.Auth = []ssh.AuthMethod{
|
||||
ssh.PublicKeys(pKey),
|
||||
}
|
||||
} else {
|
||||
config.Auth = []ssh.AuthMethod{
|
||||
ssh.Password(option.Password),
|
||||
}
|
||||
}
|
||||
|
||||
client, err := ssh.Dial("tcp", addr, &config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
outbound := &Ssh{
|
||||
Base: &Base{
|
||||
name: option.Name,
|
||||
addr: addr,
|
||||
tp: C.Ssh,
|
||||
udp: true,
|
||||
iface: option.Interface,
|
||||
rmark: option.RoutingMark,
|
||||
prefer: C.NewDNSPrefer(option.IPVersion),
|
||||
},
|
||||
option: &option,
|
||||
client: client,
|
||||
}
|
||||
runtime.SetFinalizer(outbound, closeSsh)
|
||||
|
||||
return outbound, nil
|
||||
}
|
|
@ -134,6 +134,13 @@ func ParseProxy(mapping map[string]any) (C.Proxy, error) {
|
|||
break
|
||||
}
|
||||
proxy = outbound.NewRejectWithOption(*rejectOption)
|
||||
case "ssh":
|
||||
sshOption := &outbound.SshOption{}
|
||||
err = decoder.Decode(mapping, sshOption)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
proxy, err = outbound.NewSsh(*sshOption)
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupport proxy type: %s", proxyType)
|
||||
}
|
||||
|
|
|
@ -41,6 +41,7 @@ const (
|
|||
Hysteria2
|
||||
WireGuard
|
||||
Tuic
|
||||
Ssh
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -222,7 +223,8 @@ func (at AdapterType) String() string {
|
|||
return "URLTest"
|
||||
case LoadBalance:
|
||||
return "LoadBalance"
|
||||
|
||||
case Ssh:
|
||||
return "Ssh"
|
||||
default:
|
||||
return "Unknown"
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user