//DialClient returns two channels where one returns a ssh.Client and the other and error func DialClient(dial, expire time.Duration, ip string, conf *ssh.ClientConfig, retry <-chan struct{}) (*ssh.Client, error) { flux.Report(nil, fmt.Sprintf("MakeDial for %s for dailing at %+s and expiring in %+s", conf.User, dial, expire)) cons := make(chan *ssh.Client) errs := make(chan error) var con net.Conn var sc ssh.Conn var chans <-chan ssh.NewChannel var req <-chan *ssh.Request var err error flux.GoDefer("MakeDial", func() { con, err = net.DialTimeout("tcp", ip, dial) if err != nil { flux.Report(err, fmt.Sprintf("MakeDial:Before for %s net.DailTimeout", ip)) errs <- err return } sc, chans, req, err = ssh.NewClientConn(con, ip, conf) if err != nil { flux.Report(err, fmt.Sprintf("MakeDial:After for %s ssh.NewClientConn", ip)) errs <- err return } flux.Report(nil, fmt.Sprintf("MakeDial initiating NewClient for %s", ip)) cons <- ssh.NewClient(sc, chans, req) return }) expiration := threshold(expire) go func() { for _ = range retry { expiration = threshold(expire) } }() select { case err := <-errs: flux.Report(err, fmt.Sprintf("NewClient Ending!")) return nil, err case som := <-cons: flux.Report(nil, fmt.Sprintf("NewClient Created!")) expiration = nil return som, nil case <-expiration: flux.Report(nil, fmt.Sprintf("MakeDial Expired for %s!", ip)) defer con.Close() if sc != nil { sc.Close() } return nil, ErrTimeout } }
func ProxyChannels(logger lager.Logger, conn ssh.Conn, channels <-chan ssh.NewChannel) { logger = logger.Session("proxy-channels") logger.Info("started") defer logger.Info("completed") defer conn.Close() for newChannel := range channels { logger.Info("new-channel", lager.Data{ "channelType": newChannel.ChannelType(), "extraData": newChannel.ExtraData(), }) targetChan, targetReqs, err := conn.OpenChannel(newChannel.ChannelType(), newChannel.ExtraData()) if err != nil { logger.Error("failed-to-open-channel", err) if openErr, ok := err.(*ssh.OpenChannelError); ok { newChannel.Reject(openErr.Reason, openErr.Message) } else { newChannel.Reject(ssh.ConnectionFailed, err.Error()) } continue } sourceChan, sourceReqs, err := newChannel.Accept() if err != nil { targetChan.Close() continue } toTargetLogger := logger.Session("to-target") toSourceLogger := logger.Session("to-source") go func() { helpers.Copy(toTargetLogger, nil, targetChan, sourceChan) targetChan.CloseWrite() }() go func() { helpers.Copy(toSourceLogger, nil, sourceChan, targetChan) sourceChan.CloseWrite() }() go ProxyRequests(toTargetLogger, newChannel.ChannelType(), sourceReqs, targetChan) go ProxyRequests(toSourceLogger, newChannel.ChannelType(), targetReqs, sourceChan) } }
/* handleConnRequests handles proxying requests read from reqs to the SSH connection sc. info is used for logging */ func handleConnRequests( reqs <-chan *ssh.Request, c ssh.Conn, info string, ) { for r := range reqs { go handleRequest( r, func( name string, wantReply bool, payload []byte, ) (bool, []byte, error) { return c.SendRequest(name, wantReply, payload) }, func() error { return c.Close() }, info, ) } }
func (c *comm) reconnect() (err error) { if c.conn != nil { c.conn.Close() } // Set the conn and client to nil since we'll recreate it c.conn = nil c.client = nil log.Printf("reconnecting to TCP connection for SSH") c.conn, err = c.config.Connection() if err != nil { // Explicitly set this to the REAL nil. Connection() can return // a nil implementation of net.Conn which will make the // "if c.conn == nil" check fail above. Read here for more information // on this psychotic language feature: // // http://golang.org/doc/faq#nil_error c.conn = nil log.Printf("reconnection error: %s", err) return } log.Printf("handshaking with SSH") // Default timeout to 1 minute if it wasn't specified (zero value). For // when you need to handshake from low orbit. var duration time.Duration if c.config.HandshakeTimeout == 0 { duration = 1 * time.Minute } else { duration = c.config.HandshakeTimeout } connectionEstablished := make(chan struct{}, 1) var sshConn ssh.Conn var sshChan <-chan ssh.NewChannel var req <-chan *ssh.Request go func() { sshConn, sshChan, req, err = ssh.NewClientConn(c.conn, c.address, c.config.SSHConfig) close(connectionEstablished) }() select { case <-connectionEstablished: // We don't need to do anything here. We just want select to block until // we connect or timeout. case <-time.After(duration): if c.conn != nil { c.conn.Close() } if sshConn != nil { sshConn.Close() } return ErrHandshakeTimeout } if err != nil { log.Printf("handshake error: %s", err) return } log.Printf("handshake complete!") if sshConn != nil { c.client = ssh.NewClient(sshConn, sshChan, req) } c.connectToAgent() return }