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 }