77 lines
1.7 KiB
Go
77 lines
1.7 KiB
Go
package sshtun
|
|
|
|
import (
|
|
"errors"
|
|
"net"
|
|
"reflect"
|
|
"sync"
|
|
"unsafe"
|
|
|
|
"golang.org/x/crypto/ssh"
|
|
)
|
|
|
|
type sishHostListener struct {
|
|
parent *ssh.Client
|
|
}
|
|
|
|
func newSishHostListener(parent *ssh.Client) *sishHostListener {
|
|
return &sishHostListener{parent: parent}
|
|
}
|
|
|
|
func (c *sishHostListener) ListenFakeRemoteHost(ep Endpoint) (net.Listener, error) {
|
|
c.doHandleForwardsOnce()
|
|
m := channelForwardMsg{
|
|
ep.Host,
|
|
uint32(ep.Port),
|
|
}
|
|
// send message
|
|
ok, resp, err := c.parent.SendRequest("tcpip-forward", true, ssh.Marshal(&m))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !ok {
|
|
return nil, errors.New("ssh: tcpip-forward request denied by peer")
|
|
}
|
|
|
|
laddr := &net.TCPAddr{
|
|
IP: net.ParseIP("127.0.0.1"),
|
|
Port: ep.Port,
|
|
}
|
|
if ep.Port == 0 {
|
|
var p struct {
|
|
Port uint32
|
|
}
|
|
if err := ssh.Unmarshal(resp, &p); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
laddr.Port = int(p.Port)
|
|
}
|
|
c.registerForward(laddr)
|
|
return nil, nil
|
|
}
|
|
|
|
func (c *sishHostListener) registerForward(addr *net.TCPAddr) {
|
|
cl := reflect.ValueOf(c.parent).Elem()
|
|
forwardsUn := cl.FieldByName("forwards")
|
|
forwards := reflect.NewAt(forwardsUn.Type(), unsafe.Pointer(forwardsUn.UnsafeAddr())).Elem()
|
|
forwardVal := forwards.MethodByName("add").Call([]reflect.Value{reflect.ValueOf(addr)})[0]
|
|
_ = forwardVal
|
|
}
|
|
|
|
func (c *sishHostListener) doHandleForwardsOnce() {
|
|
cl := reflect.ValueOf(c.parent)
|
|
clVal := cl.Elem()
|
|
onceField := clVal.FieldByName("handleForwardsOnce")
|
|
once := reflect.NewAt(onceField.Type(), unsafe.Pointer(onceField.UnsafeAddr())).Interface().(*sync.Once)
|
|
handleForwards := clVal.MethodByName("handleForwards")
|
|
once.Do(func() {
|
|
handleForwards.Call(nil)
|
|
})
|
|
}
|
|
|
|
type channelForwardMsg struct {
|
|
addr string
|
|
rport uint32
|
|
}
|