func (config BeaconConfig) Dial() (*ssh.Client, error) { workerPrivateKeyBytes, err := ioutil.ReadFile(string(config.WorkerPrivateKey)) if err != nil { return nil, fmt.Errorf("failed to read worker private key: %s", err) } workerPrivateKey, err := ssh.ParsePrivateKey(workerPrivateKeyBytes) if err != nil { return nil, fmt.Errorf("failed to parse worker private key: %s", err) } tsaAddr := fmt.Sprintf("%s:%d", config.Host, config.Port) conn, err := net.DialTimeout("tcp", tsaAddr, 10*time.Second) if err != nil { return nil, fmt.Errorf("failed to connect to TSA:", err) } clientConfig := &ssh.ClientConfig{ User: "******", // doesn't matter HostKeyCallback: config.checkHostKey, Auth: []ssh.AuthMethod{ssh.PublicKeys(workerPrivateKey)}, } clientConn, chans, reqs, err := ssh.NewClientConn(conn, tsaAddr, clientConfig) if err != nil { return nil, fmt.Errorf("failed to construct client connection:", err) } return ssh.NewClient(clientConn, chans, reqs), nil }
func executeCmd(cmd, hostname string, config *ssh.ClientConfig, timeout time.Duration) (string, error) { // Dial up TCP connection to remote machine. conn, err := net.Dial("tcp", hostname+":22") if err != nil { return "", fmt.Errorf("Failed to ssh connect to %s. Make sure \"PubkeyAuthentication yes\" is in your sshd_config: %s", hostname, err) } defer util.Close(conn) util.LogErr(conn.SetDeadline(time.Now().Add(timeout))) // Create new SSH client connection. sshConn, sshChan, req, err := ssh.NewClientConn(conn, hostname+":22", config) if err != nil { return "", fmt.Errorf("Failed to ssh connect to %s: %s", hostname, err) } // Use client connection to create new client. client := ssh.NewClient(sshConn, sshChan, req) // Client connections can support multiple interactive sessions. session, err := client.NewSession() if err != nil { return "", fmt.Errorf("Failed to ssh connect to %s: %s", hostname, err) } var stdoutBuf bytes.Buffer session.Stdout = &stdoutBuf if err := session.Run(cmd); err != nil { return "", fmt.Errorf("Errored or Timeout out while running \"%s\" on %s: %s", cmd, hostname, err) } return stdoutBuf.String(), nil }
func sshOnConn(conn net.Conn, h conf.Host) (*ssh.Client, error) { var auths []ssh.AuthMethod if h.Pass != "" { auths = append(auths, ssh.Password(h.Pass)) auths = append(auths, ssh.KeyboardInteractive(kbdInteractive(h.Pass))) } if h.Key != "" { k := &keyring{} err := k.loadPEM([]byte(h.Key)) if err != nil { return nil, err } for _, k := range k.keys { s, _ := ssh.NewSignerFromKey(k) auths = append(auths, ssh.PublicKeys(s)) } } config := &ssh.ClientConfig{ User: h.User, Auth: auths, } debugln("handshake & authenticate") cc, nc, reqs, err := ssh.NewClientConn(conn, conn.RemoteAddr().String(), config) if err != nil { return nil, err } client := ssh.NewClient(cc, nc, reqs) return client, nil }
func SSHDialTimeout(network, addr string, config *ssh.ClientConfig, timeout time.Duration) (*ssh.Client, error) { conn, err := net.DialTimeout(network, addr, timeout) if err != nil { return nil, err } timeoutConn := &Conn{conn, timeout, timeout} c, chans, reqs, err := ssh.NewClientConn(timeoutConn, addr, config) if err != nil { return nil, err } client := ssh.NewClient(c, chans, reqs) // this sends keepalive packets every 2 seconds // there's no useful response from these, so we can just abort if there's an error go func() { t := time.NewTicker(2 * time.Second) defer t.Stop() for { <-t.C if _, _, err := client.Conn.SendRequest("*****@*****.**", true, nil); err != nil { return } } }() return client, nil }
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 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 } sshConn, sshChan, req, err := ssh.NewClientConn(c.conn, c.address, c.config.SSHConfig) if err != nil { log.Printf("handshake error: %s", err) } if sshConn != nil { c.client = ssh.NewClient(sshConn, sshChan, req) } c.connectToAgent() return }
//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 connect(username, host string, authMethod ssh.AuthMethod, timeout time.Duration) (*Client, error) { if username == "" { user, err := user.Current() if err != nil { return nil, fmt.Errorf("Username wasn't specified and couldn't get current user: %v", err) } username = user.Username } config := &ssh.ClientConfig{ User: username, Auth: []ssh.AuthMethod{authMethod}, } host = addPortToHost(host) conn, err := net.DialTimeout("tcp", host, timeout) if err != nil { return nil, err } sshConn, chans, reqs, err := ssh.NewClientConn(conn, host, config) if err != nil { return nil, err } client := ssh.NewClient(sshConn, chans, reqs) c := &Client{SSHClient: client} return c, nil }
// NewRemotePassAuthRunnerWithTimeouts is one of functions for creating remote // runner. Use this one instead of NewRemotePassAuthRunner if you need to setup // nondefault timeouts for ssh connection func NewRemotePassAuthRunnerWithTimeouts( user, host, password string, timeouts Timeouts, ) (*Remote, error) { config := &ssh.ClientConfig{ User: user, Auth: []ssh.AuthMethod{ssh.Password(password)}, } dialer := net.Dialer{ Timeout: timeouts.ConnectionTimeout, Deadline: time.Now().Add(timeouts.ConnectionTimeout), KeepAlive: timeouts.KeepAlive, } conn, err := dialer.Dial("tcp", host) if err != nil { return nil, err } connection := &timeBoundedConnection{ Conn: conn, readTimeout: timeouts.SendTimeout, writeTimeout: timeouts.ReceiveTimeout, } sshConnection, channels, requests, err := ssh.NewClientConn( connection, host, config, ) if err != nil { return nil, err } return &Remote{ssh.NewClient(sshConnection, channels, requests)}, nil }
func (s *server) TryDial(config *ssh.ClientConfig) (*ssh.Client, error) { sshd, err := exec.LookPath("sshd") if err != nil { s.t.Skipf("skipping test: %v", err) } c1, c2, err := unixConnection() if err != nil { s.t.Fatalf("unixConnection: %v", err) } s.cmd = exec.Command(sshd, "-f", s.configfile, "-i", "-e") f, err := c2.File() if err != nil { s.t.Fatalf("UnixConn.File: %v", err) } defer f.Close() s.cmd.Stdin = f s.cmd.Stdout = f s.cmd.Stderr = &s.output if err := s.cmd.Start(); err != nil { s.t.Fail() s.Shutdown() s.t.Fatalf("s.cmd.Start: %v", err) } s.clientConn = c1 conn, chans, reqs, err := ssh.NewClientConn(c1, "", config) if err != nil { return nil, err } return ssh.NewClient(conn, chans, reqs), nil }
func DialSSHTimeout(network, addr string, config *ssh.ClientConfig, timeout time.Duration) (*ssh.Client, error) { conn, err := net.DialTimeout(network, addr, timeout) if err != nil { return nil, err } timeoutConn := &SSHConn{conn, timeout, timeout} c, chans, reqs, err := ssh.NewClientConn(timeoutConn, addr, config) if err != nil { return nil, err } client := ssh.NewClient(c, chans, reqs) // this sends keepalive packets every 3 seconds // there's no useful response from these, so we can just abort if there's an error go func() { t := time.NewTicker(KEEPALIVE_INTERVAL) defer t.Stop() for { <-t.C _, _, err := client.Conn.SendRequest("*****@*****.**", true, nil) if err != nil { log.Fatalf("Remote server did not respond to keepalive.") return } } }() return client, nil }
func (c *comm) reconnect() error { // Close previous connection. if c.conn != nil { c.Close() } var err error 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 c.config.Logger.Error("reconnection error", "error", err) return err } sshConn, sshChan, req, err := ssh.NewClientConn(c.conn, c.address, c.config.SSHConfig) if err != nil { c.config.Logger.Error("handshake error", "error", err) c.Close() return err } if sshConn != nil { c.client = ssh.NewClient(sshConn, sshChan, req) } c.connectToAgent() return nil }
func (ctx *ExecContext) reconnect() (err error) { if ctx.hostname != "" { ctx.isReconnecting = true username := ctx.username addr := fmt.Sprintf("%s:%d", ctx.hostname, ctx.port) ctx.unlock() agentConn, err := net.Dial("unix", os.Getenv("SSH_AUTH_SOCK")) if err != nil { ctx.lock() ctx.isReconnecting = false return err } defer agentConn.Close() ag := agent.NewClient(agentConn) auths := []ssh.AuthMethod{ssh.PublicKeysCallback(ag.Signers)} config := &ssh.ClientConfig{ User: username, Auth: auths, } conn, err := net.DialTimeout("tcp", addr, networkTimeout) if err != nil { ctx.lock() ctx.isReconnecting = false return err } timeoutConn := &Conn{conn, networkTimeout, networkTimeout} c, chans, reqs, err := ssh.NewClientConn(timeoutConn, addr, config) if err != nil { ctx.lock() ctx.isReconnecting = false return err } client := ssh.NewClient(c, chans, reqs) // Send periodic keepalive messages go func() { t := time.NewTicker(networkTimeout / 2) defer t.Stop() for { <-t.C _, _, err := client.Conn.SendRequest("*****@*****.**", true, nil) if err != nil { ctx.lock() if ctx.sshClient == client { ctx.isConnected = false } ctx.unlock() return } } }() ctx.lock() ctx.isReconnecting = false ctx.sshClient = client } ctx.isConnected = true return nil }
// The function ssh.Dial doesn't have timeout mechanism for dial so this function is used func dialWithTimeout(network, addr string, config *ssh.ClientConfig, timeout time.Duration) (*ssh.Client, error) { conn, err := net.DialTimeout(network, addr, timeout) if err != nil { return nil, err } c, chans, reqs, err := ssh.NewClientConn(conn, addr, config) if err != nil { return nil, err } return ssh.NewClient(c, chans, reqs), nil }
// DialThrough will create a new connection from the ssh server sc is connected to. DialThrough is an SSHDialer. func (sc *SSHClient) DialThrough(net, addr string, config *ssh.ClientConfig) (*ssh.Client, error) { conn, err := sc.conn.Dial(net, addr) if err != nil { return nil, err } c, chans, reqs, err := ssh.NewClientConn(conn, addr, config) if err != nil { return nil, err } return ssh.NewClient(c, chans, reqs), nil }
func (opts *sshOpts) dial(config *ssh.ClientConfig) (*ssh.Client, error) { addr := opts.Hostname + ":" + strconv.Itoa(opts.Port) timeout := opts.Timeout * float64(time.Second) conn, err := net.DialTimeout("tcp", addr, time.Duration(timeout)) if err != nil { return nil, err } c, chans, reqs, err := ssh.NewClientConn(conn, addr, config) if err != nil { return nil, err } return ssh.NewClient(c, chans, reqs), nil }
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 }
func (d *realSSHDialer) Dial(network, addr string, config *ssh.ClientConfig) (*ssh.Client, error) { conn, err := net.DialTimeout(network, addr, config.Timeout) if err != nil { return nil, err } conn.SetReadDeadline(time.Now().Add(30 * time.Second)) c, chans, reqs, err := ssh.NewClientConn(conn, addr, config) if err != nil { return nil, err } conn.SetReadDeadline(time.Time{}) return ssh.NewClient(c, chans, reqs), nil }
func NewClient(clientNetConn net.Conn, clientConfig *ssh.ClientConfig) *ssh.Client { if clientConfig == nil { clientConfig = &ssh.ClientConfig{ User: "******", Auth: []ssh.AuthMethod{ ssh.Password("secret"), }, } } clientConn, clientChannels, clientRequests, clientConnErr := ssh.NewClientConn(clientNetConn, "0.0.0.0", clientConfig) Expect(clientConnErr).NotTo(HaveOccurred()) return ssh.NewClient(clientConn, clientChannels, clientRequests) }
// NewRemoteKeyAuthRunnerWithTimeouts is one of functions for creating remote // runner. Use this one instead of NewRemoteKeyAuthRunner if you need to setup // nondefault timeouts for ssh connection func NewRemoteKeyAuthRunnerWithTimeouts( user, host, key string, timeouts Timeouts, ) (*Remote, error) { if _, err := os.Stat(key); os.IsNotExist(err) { return nil, err } pemBytes, err := ioutil.ReadFile(key) if err != nil { return nil, err } signer, err := ssh.ParsePrivateKey(pemBytes) if err != nil { return nil, errors.New("can't parse pem data: " + err.Error()) } config := &ssh.ClientConfig{ User: user, Auth: []ssh.AuthMethod{ssh.PublicKeys(signer)}, } dialer := net.Dialer{ Timeout: timeouts.ConnectionTimeout, Deadline: time.Now().Add(timeouts.ConnectionTimeout), KeepAlive: timeouts.KeepAlive, } conn, err := dialer.Dial("tcp", host) if err != nil { return nil, err } connection := &timeBoundedConnection{ Conn: conn, readTimeout: timeouts.SendTimeout, writeTimeout: timeouts.ReceiveTimeout, } sshConnection, channels, requests, err := ssh.NewClientConn( connection, host, config, ) if err != nil { return nil, err } return &Remote{ssh.NewClient(sshConnection, channels, requests)}, nil }
func (c *Communicator) Client() (*ssh.Client, error) { if c.client != nil { return c.client, nil } // create ssh client. client, err := ssh.Dial("tcp", c.Config.HostOrDefault()+":"+c.Config.PortOrDefault(), c.ClientConfig) if err != nil { return nil, err } c.clientConns = append(c.clientConns, client) // If it has a upstream server? for c.UpstreamConfig != nil { // It is next server config to connect. var config *Config = nil // Does the upstream server need proxy to connet? proxy := c.UpstreamConfig.PopProxyConfig() if proxy != nil { config = proxy } else { config = c.UpstreamConfig c.UpstreamConfig = nil } // dial to ssh proxy connection, err := client.Dial("tcp", config.HostOrDefault()+":"+config.PortOrDefault()) if err != nil { return nil, err } c.clientConns = append(c.clientConns, connection) conn, chans, reqs, err := ssh.NewClientConn(connection, config.HostOrDefault()+":"+config.PortOrDefault(), c.ClientConfig) if err != nil { return nil, err } client = ssh.NewClient(conn, chans, reqs) c.clientConns = append(c.clientConns, client) if err != nil { return nil, err } } c.client = client return c.client, nil }
func connToTransport(conn net.Conn, config *ssh.ClientConfig) (*TransportSSH, error) { c, chans, reqs, err := ssh.NewClientConn(conn, conn.RemoteAddr().String(), config) if err != nil { return nil, err } t := &TransportSSH{} t.sshClient = ssh.NewClient(c, chans, reqs) err = t.setupSession() if err != nil { return nil, err } return t, nil }
func TestSetupForwardAgent(t *testing.T) { a, b, err := netPipe() if err != nil { t.Fatalf("netPipe: %v", err) } defer a.Close() defer b.Close() _, socket, cleanup := startAgent(t) defer cleanup() serverConf := ssh.ServerConfig{ NoClientAuth: true, } serverConf.AddHostKey(testSigners["rsa"]) incoming := make(chan *ssh.ServerConn, 1) go func() { conn, _, _, err := ssh.NewServerConn(a, &serverConf) if err != nil { t.Fatalf("Server: %v", err) } incoming <- conn }() conf := ssh.ClientConfig{} conn, chans, reqs, err := ssh.NewClientConn(b, "", &conf) if err != nil { t.Fatalf("NewClientConn: %v", err) } client := ssh.NewClient(conn, chans, reqs) if err := ForwardToRemote(client, socket); err != nil { t.Fatalf("SetupForwardAgent: %v", err) } server := <-incoming ch, reqs, err := server.OpenChannel(channelType, nil) if err != nil { t.Fatalf("OpenChannel(%q): %v", channelType, err) } go ssh.DiscardRequests(reqs) agentClient := NewClient(ch) testAgentInterface(t, agentClient, testPrivateKeys["rsa"], nil, 0) conn.Close() }
func getSSHClient( sshSigner ssh.Signer, user string, host string, port string, dialTimeout time.Duration, ) (*ssh.Client, error) { clientConfig := ssh.ClientConfig{ User: user, Auth: []ssh.AuthMethod{ssh.PublicKeys(sshSigner)}, // ssh.Password("password"), } addr := host + ":" + port // if we need to set up dial timeout. // c, err := net.DialTimeout("tcp", addr, dialTimeout) if err != nil { return nil, err } if tc, ok := c.(*net.TCPConn); ok { // if c is tcp connection, set these: tc.SetKeepAlive(true) tc.SetKeepAlivePeriod(5 * time.Second) } // func NewClientConn(c net.Conn, addr string, config *ClientConfig) // (Conn, <-chan NewChannel, <-chan *Request, error) conn, newChan, reqChan, err := ssh.NewClientConn( c, addr, &clientConfig, ) if err != nil { return nil, err } if conn == nil { return nil, errors.New("Can't establish SSH") } // func NewClient(c Conn, chans <-chan NewChannel, reqs <-chan *Request) *Client return ssh.NewClient(conn, newChan, reqChan), nil // or // // return ssh.Dial("tcp", addr, &clientConfig) }
func dialSSH(info *SSHTunnel, config *ssh.ClientConfig, proxyCommand string) (*ssh.Client, error) { var conn net.Conn var err error if proxyCommand == "" { conn, err = directConnect(`tcp`, info.Address, 5*time.Second) } else { conn, err = connectProxy(proxyCommand, info.Address) } if err != nil { return nil, err } c, chans, reqs, err := ssh.NewClientConn(conn, info.Address, config) if err != nil { return nil, err } return ssh.NewClient(c, chans, reqs), nil }
//DialSSH will open an ssh session using the specified authentication func DialSSH(server, username string, timeout int, auth ...ssh.AuthMethod) (*Session, error) { config := &ssh.ClientConfig{ User: username, Auth: auth, } if strings.Index(server, ":") < 0 { server += ":22" } conn, err := net.DialTimeout("tcp", server, time.Duration(timeout)*time.Second) if err != nil { return nil, err } c, chans, reqs, err := ssh.NewClientConn(conn, server, config) if err != nil { return nil, err } return NewSession(ssh.NewClient(c, chans, reqs)) }
// a goroutine - dedicated to serially attacking a host func attack(host string, credCh <-chan *Cred, doneCh chan<- string) { netfailed := 0 target := net.JoinHostPort(host, strconv.Itoa(DefPort)) timer := time.NewTimer(DefCacheTimeout) L: for { timer.Reset(DefCacheTimeout) select { case cred := <-credCh: if netfailed >= 3 { if netfailed == 3 { log.Printf("NOT attacking %s: too many network failures.\n", host) netfailed = netfailed + 1 } continue // don't connect out after 3 network failures in a row. } c, err := net.Dial("tcp", target) if err != nil { log.Printf("Fail: unable to establish tcp connection to %s\n", target) netfailed = netfailed + 1 continue } netfailed = 0 cConfig := &ssh.ClientConfig{ User: cred.user, Auth: []ssh.AuthMethod{ssh.Password(cred.pass)}, ClientVersion: DefBanner, } conn, _, _, err := ssh.NewClientConn(c, target, cConfig) if err != nil { log.Printf("Fail: tried attacking %s with %s:%s\n", host, cred.user, cred.pass) } else { conn.Close() log.Printf("*** SUCCESS ***: %s:%s worked on %s\n", cred.user, cred.pass, host) } c.Close() case <-timer.C: break L } } doneCh <- host }
/* clientDial dials the real server and makes an SSH client */ func clientDial( addr string, conf *ssh.ClientConfig, ) (ssh.Conn, <-chan ssh.NewChannel, <-chan *ssh.Request, error) { /* Connect to the server */ c, err := net.Dial("tcp", addr) if nil != err { return nil, nil, nil, err } return ssh.NewClientConn(c, addr, conf) //sc,chans,reqs,err := //func NewClientConn(c net.Conn, addr string, config *ClientConfig) ///* Connect to server */ //c, err := ssh.Dial("tcp", addr, conf) //if nil != err { // return nil, err //} //return c, nil }
func TestAuth(t *testing.T) { a, b, err := netPipe() if err != nil { t.Fatalf("netPipe: %v", err) } defer a.Close() defer b.Close() agent, _, cleanup := startAgent(t) defer cleanup() if err := agent.Add(AddedKey{PrivateKey: testPrivateKeys["rsa"], Comment: "comment"}); err != nil { t.Errorf("Add: %v", err) } serverConf := ssh.ServerConfig{} serverConf.AddHostKey(testSigners["rsa"]) serverConf.PublicKeyCallback = func(c ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) { if bytes.Equal(key.Marshal(), testPublicKeys["rsa"].Marshal()) { return nil, nil } return nil, errors.New("pubkey rejected") } go func() { conn, _, _, err := ssh.NewServerConn(a, &serverConf) if err != nil { t.Fatalf("Server: %v", err) } conn.Close() }() conf := ssh.ClientConfig{} conf.Auth = append(conf.Auth, ssh.PublicKeysCallback(agent.Signers)) conn, _, _, err := ssh.NewClientConn(b, "", &conf) if err != nil { t.Fatalf("NewClientConn: %v", err) } conn.Close() }
// Create a new NETCONF session using an existing net.Conn. func NewSSHSession(conn net.Conn, config *ssh.ClientConfig) (*Session, error) { var ( t TransportSSH err error ) c, chans, reqs, err := ssh.NewClientConn(conn, conn.RemoteAddr().String(), config) if err != nil { return nil, err } t.sshClient = ssh.NewClient(c, chans, reqs) err = t.setupSession() if err != nil { return nil, err } return NewSession(&t), nil }
func NewTunnelledSSHClient(user, tunaddr, tgtaddr string, checker *HostKeyChecker, agentForwarding bool, timeout time.Duration) (*SSHForwardingClient, error) { clientConfig, err := sshClientConfig(user, checker) if err != nil { return nil, err } tunaddr = maybeAddDefaultPort(tunaddr) tgtaddr = maybeAddDefaultPort(tgtaddr) var tunnelClient *gossh.Client dialFunc := func(echan chan error) { var err error tunnelClient, err = gossh.Dial("tcp", tunaddr, clientConfig) echan <- err } err = timeoutSSHDial(dialFunc, timeout) if err != nil { return nil, err } var targetConn net.Conn dialFunc = func(echan chan error) { tgtTCPAddr, err := net.ResolveTCPAddr("tcp", tgtaddr) if err != nil { echan <- err return } targetConn, err = tunnelClient.DialTCP("tcp", nil, tgtTCPAddr) echan <- err } err = timeoutSSHDial(dialFunc, timeout) if err != nil { return nil, err } c, chans, reqs, err := gossh.NewClientConn(targetConn, tgtaddr, clientConfig) if err != nil { return nil, err } return newSSHForwardingClient(gossh.NewClient(c, chans, reqs), agentForwarding) }