func Hop(through *ssh.Client, toaddr string, c *ssh.ClientConfig) (*ssh.Client, error) { hopconn, err := through.Dial("tcp", toaddr) if err != nil { return nil, err } conn, chans, reqs, err := ssh.NewClientConn(hopconn, toaddr, c) if err != nil { return nil, err } return ssh.NewClient(conn, chans, reqs), nil }
// Start port forwarding for the given SSH client: func loop(sshClient *ssh.Client, localToRemote, remoteToLocal []PortForward) { done := make(chan bool, 1) // Intercept termination signals: sigc := make(chan os.Signal, 1) signal.Notify(sigc, os.Interrupt, os.Kill, syscall.SIGTERM, syscall.SIGQUIT) go func() { sig := <-sigc log.Printf("Caught signal: %s\n", sig) done <- true }() // Forward all the local-to-remote ports: for _, fwd := range localToRemote { // Set up forwarding addresses: localAddr := fwd.LocalAddr remoteAddr := fwd.RemoteAddr log.Printf("Forwarding connections from local %s to remote %s\n", localAddr, remoteAddr) go func() { localListener, err := net.Listen("tcp", localAddr) if err != nil { log.Printf("unable to listen: %s\n", err) done <- true return } defer localListener.Close() log.Printf("Listening...\n") // Begin accepting new connections from the SSH tunnel: for { // Accept a new local connection: local, err := localListener.Accept() if err != nil { log.Printf("Accept: %s\n", err) break } logContext := local.RemoteAddr().String() log.Printf("%s: Accepted local connection.\n", logContext) // Connect to the remote RDP service: remote, err := sshClient.Dial("tcp", remoteAddr) if err != nil { log.Printf("%s: Unable to connect via SSH: %s\n", logContext, err) local.Close() continue } defer remote.Close() // Start forwarding data back 'n forth: go forward(local, remote, logContext) } }() } // Forward all remote-to-local ports: for _, fwd := range remoteToLocal { // Set up forwarding addresses: localAddr := fwd.LocalAddr remoteAddr := fwd.RemoteAddr // Resolve local address: var err error localTCPAddr, err := net.ResolveTCPAddr("tcp", localAddr) if err != nil { log.Printf("unable to resolve local address: %s\n", err) done <- true return } log.Printf("Forwarding connections from remote %s to local %s\n", remoteAddr, localAddr) go func() { // Request the remote side to open a port for forwarding: l, err := sshClient.Listen("tcp", remoteAddr) if err != nil { log.Printf("unable to register tcp forward: %s\n", err) return } log.Printf("Listening...\n") defer l.Close() // Begin accepting new connections from the SSH tunnel: for { // Accept a new remote connection from SSH: remote, err := l.Accept() if err != nil { log.Printf("Accept: %s\n", err) done <- true break } logContext := remote.RemoteAddr().String() log.Printf("%s: Accepted new SSH tunnel connection.\n", logContext) // Connect to the local RDP service: local, err := net.DialTCP("tcp", nil, localTCPAddr) if err != nil { log.Printf("%s: Could not open local connection to: %s\n", logContext, localTCPAddr) remote.Close() continue } // Start forwarding data back 'n forth: go forward(local, remote, logContext) } }() } <-done }