private Task <ITunnel> ConnectIfNecessaryAsync(
            TunnelDestination endpoint,
            ISshRelayPolicy relayPolicy)
        {
            lock (this.tunnelsLock)
            {
                if (!this.tunnels.TryGetValue(endpoint, out Task <ITunnel> tunnel))
                {
                    return(ConnectAndCacheAsync(endpoint, relayPolicy));
                }
                else if (tunnel.IsFaulted)
                {
                    ApplicationTraceSources.Default.TraceVerbose(
                        "Tunnel to {0} is faulted.. reconnecting", endpoint);

                    // There is no point in handing out a faulty attempt
                    // to create a tunnel. So start anew.
                    return(ConnectAndCacheAsync(endpoint, relayPolicy));
                }
                else
                {
                    ApplicationTraceSources.Default.TraceVerbose(
                        "Reusing tunnel to {0}", endpoint);

                    // This tunnel is good or still in the process
                    // of connecting.
                    return(tunnel);
                }
            }
        }
        private Task <ITunnel> ConnectAndCacheAsync(
            TunnelDestination endpoint,
            ISshRelayPolicy relayPolicy)
        {
            var tunnel = this.tunnelService.CreateTunnelAsync(endpoint, relayPolicy);

            ApplicationTraceSources.Default.TraceVerbose("Created tunnel to {0}", endpoint);

            this.tunnels[endpoint] = tunnel;
            return(tunnel);
        }
 public bool IsConnected(TunnelDestination endpoint)
 {
     lock (this.tunnelsLock)
     {
         if (this.tunnels.TryGetValue(endpoint, out Task <ITunnel> tunnel))
         {
             return(!tunnel.IsFaulted);
         }
         else
         {
             return(false);
         }
     }
 }
        public async Task DisconnectAsync(TunnelDestination endpoint)
        {
            using (ApplicationTraceSources.Default.TraceMethod().WithParameters(endpoint))
            {
                lock (this.tunnelsLock)
                {
                    if (!this.tunnels.TryGetValue(endpoint, out var tunnel))
                    {
                        throw new KeyNotFoundException($"No active tunnel to {endpoint}");
                    }

                    tunnel.Result.Close();
                    this.tunnels.Remove(endpoint);
                }

                await this.eventService
                .FireAsync(new TunnelClosedEvent(endpoint))
                .ConfigureAwait(false);
            }
        }
        public Task <ITunnel> CreateTunnelAsync(
            TunnelDestination tunnelEndpoint,
            ISshRelayPolicy relayPolicy)
        {
            using (ApplicationTraceSources.Default.TraceMethod().WithParameters(tunnelEndpoint))
            {
                var clientCertificate =
                    (this.authorizationService.DeviceEnrollment != null &&
                     this.authorizationService.DeviceEnrollment.State == DeviceEnrollmentState.Enrolled)
                    ? this.authorizationService.DeviceEnrollment.Certificate
                    : null;

                if (clientCertificate != null)
                {
                    ApplicationTraceSources.Default.TraceInformation(
                        "Using client certificate (valid till {0})", clientCertificate.NotAfter);
                }

                var iapEndpoint = new IapTunnelingEndpoint(
                    this.authorizationService.Authorization.Credential,
                    tunnelEndpoint.Instance,
                    tunnelEndpoint.RemotePort,
                    IapTunnelingEndpoint.DefaultNetworkInterface,
                    Globals.UserAgent,
                    clientCertificate);

                // Start listener to enable clients to connect. Do not await
                // the listener as we want to continue listeining in the
                // background.
                var listener = SshRelayListener.CreateLocalListener(
                    iapEndpoint,
                    relayPolicy);
                var cts = new CancellationTokenSource();

                _ = listener.ListenAsync(cts.Token);

                // Return the tunnel which allows the listener to be stopped
                // via the CancellationTokenSource.
                return(Task.FromResult <ITunnel>(new Tunnel(iapEndpoint, listener, cts)));
            }
        }
        public async Task <ITunnel> ConnectAsync(
            TunnelDestination endpoint,
            ISshRelayPolicy relayPolicy,
            TimeSpan timeout)
        {
            using (ApplicationTraceSources.Default.TraceMethod().WithParameters(endpoint, timeout))
            {
                var tunnel = await ConnectIfNecessaryAsync(endpoint, relayPolicy)
                             .ConfigureAwait(false);

                try
                {
                    // Whether it is a new or existing tunnel, probe it first before
                    // handing it out. It might be broken after all (because of reauth
                    // or for other reasons).
                    await tunnel.Probe(timeout).ConfigureAwait(false);

                    ApplicationTraceSources.Default.TraceVerbose(
                        "Probing tunnel to {0} succeeded", endpoint);
                }
                catch (Exception e)
                {
                    ApplicationTraceSources.Default.TraceVerbose(
                        "Probing tunnel to {0} failed: {1}", endpoint, e.Message);

                    // Un-cache this broken tunnel.
                    await DisconnectAsync(endpoint).ConfigureAwait(false);

                    throw;
                }

                await this.eventService
                .FireAsync(new TunnelOpenedEvent(endpoint))
                .ConfigureAwait(false);

                return(tunnel);
            }
        }
 public TunnelClosedEvent(TunnelDestination destination)
 {
     this.Destination = destination;
 }
 public TunnelOpenedEvent(TunnelDestination destination)
 {
     this.Destination = destination;
 }