// Dial establishes a port forward connection through the tunnel // This Dial doesn't support split tunnel, so alwaysTunnel is not referenced func (tunnel *Tunnel) Dial( remoteAddr string, alwaysTunnel bool, downstreamConn net.Conn) (conn net.Conn, err error) { tunnel.mutex.Lock() isClosed := tunnel.isClosed tunnel.mutex.Unlock() if isClosed { return nil, errors.New("tunnel is closed") } sshPortForwardConn, err := tunnel.sshClient.Dial("tcp", remoteAddr) if err != nil { // TODO: conditional on type of error or error message? select { case tunnel.portForwardFailures <- 1: default: } return nil, ContextError(err) } conn = &TunneledConn{ Conn: sshPortForwardConn, tunnel: tunnel, downstreamConn: downstreamConn} // Tunnel does not have a session when DisableApi is set if tunnel.session != nil { conn = transferstats.NewConn( conn, tunnel.session.StatsServerID(), tunnel.session.StatsRegexps()) } return conn, nil }
// Dial establishes a port forward connection through the tunnel // This Dial doesn't support split tunnel, so alwaysTunnel is not referenced func (tunnel *Tunnel) Dial( remoteAddr string, alwaysTunnel bool, downstreamConn net.Conn) (conn net.Conn, err error) { tunnel.mutex.Lock() isClosed := tunnel.isClosed tunnel.mutex.Unlock() if isClosed { return nil, errors.New("tunnel is closed") } type tunnelDialResult struct { sshPortForwardConn net.Conn err error } resultChannel := make(chan *tunnelDialResult, 2) if *tunnel.config.TunnelPortForwardTimeoutSeconds > 0 { time.AfterFunc(time.Duration(*tunnel.config.TunnelPortForwardTimeoutSeconds)*time.Second, func() { resultChannel <- &tunnelDialResult{nil, errors.New("tunnel dial timeout")} }) } go func() { sshPortForwardConn, err := tunnel.sshClient.Dial("tcp", remoteAddr) resultChannel <- &tunnelDialResult{sshPortForwardConn, err} }() result := <-resultChannel if result.err != nil { // TODO: conditional on type of error or error message? select { case tunnel.signalPortForwardFailure <- *new(struct{}): default: } return nil, ContextError(result.err) } conn = &TunneledConn{ Conn: result.sshPortForwardConn, tunnel: tunnel, downstreamConn: downstreamConn} // Tunnel does not have a serverContext when DisableApi is set. We still use // transferstats.Conn to count bytes transferred for monitoring tunnel // quality. var regexps *transferstats.Regexps if tunnel.serverContext != nil { regexps = tunnel.serverContext.StatsRegexps() } conn = transferstats.NewConn(conn, tunnel.serverEntry.IpAddress, regexps) return conn, nil }
// Dial establishes a port forward connection through the tunnel // This Dial doesn't support split tunnel, so alwaysTunnel is not referenced func (tunnel *Tunnel) Dial( remoteAddr string, alwaysTunnel bool, downstreamConn net.Conn) (conn net.Conn, err error) { tunnel.mutex.Lock() isClosed := tunnel.isClosed tunnel.mutex.Unlock() if isClosed { return nil, errors.New("tunnel is closed") } type tunnelDialResult struct { sshPortForwardConn net.Conn err error } resultChannel := make(chan *tunnelDialResult, 2) time.AfterFunc(TUNNEL_PORT_FORWARD_DIAL_TIMEOUT, func() { resultChannel <- &tunnelDialResult{nil, errors.New("tunnel dial timeout")} }) go func() { sshPortForwardConn, err := tunnel.sshClient.Dial("tcp", remoteAddr) resultChannel <- &tunnelDialResult{sshPortForwardConn, err} }() result := <-resultChannel if result.err != nil { // TODO: conditional on type of error or error message? select { case tunnel.portForwardFailures <- 1: default: } return nil, ContextError(result.err) } conn = &TunneledConn{ Conn: result.sshPortForwardConn, tunnel: tunnel, downstreamConn: downstreamConn} // Tunnel does not have a session when DisableApi is set if tunnel.session != nil { conn = transferstats.NewConn( conn, tunnel.session.StatsServerID(), tunnel.session.StatsRegexps()) } return conn, nil }