/// <summary> /// Runs Reverse SSH Tunnel, using command: "ssh -NvR bindAddress:remoteAccessPort:localSideHost:localSidePort user@remoteHost /// </summary> /// <param name="bindAddress"> /// By default, the listening socket on the server will be bound to the loopback interface only. /// This may be overridden by specifying a bind_address. /// An empty bind_address, or the address '*', indicates that the remote socket should listen on all interfaces. /// Specifying a remote bind_address will only succeed if the server's GatewayPorts option is enabled. /// </param> /// <returns></returns> public bool Start(string bindAddress = DEFAULT_BIND_ADDRESS) { if (bindAddress == DEFAULT_BIND_ADDRESS && !IsHostChanged) { LastStartedCommandSSH = DefaultSSHCommand; } else { LastStartedCommandSSH = CreateCommandString(SSH_FORMAT_STRING, bindAddress, RemoteHost.Port, LocalSideHost.IP, LocalSideHost.Port, RemoteHost.User, RemoteHost.IP); } _SSHProcess = new BashProcess(LastStartedCommandSSH); _SSHProcess.StandardOutputStringReceived += _SSHProcess_StandardOutputStringReceived; _SSHProcess.StandardErrorStringReceived += _SSHProcess_StandardOutputStringReceived; IsTunnelEstablished = _SSHProcess.RunNewProcess(true, true, true); return(IsTunnelEstablished); }
/// <summary> /// Finds old processes used by last command and kill them /// </summary> /// <returns>Current tunnel connection type</returns> public async Task <(TunnelConnectionState, string)> CheckAndKillOldProcesses() { try { if (await CheckConnectionType() != TunnelConnectionState.NoConnection) { List <int> PIDs = BashProcess.FindPIDs(LastStartedCommandSSH ?? DefaultSSHCommand); foreach (int pid in PIDs) { BashProcess.KillProcess(pid); } } return(await CheckConnectionType(), null); } catch (TunnelEstablishedException ex) { return(TunnelConnectionState.StoppedWithoutChecking, ex.Message); } }
/// <summary> /// Checking connection type (local/remote) / state (connected/disconnected) /// (To execute this method, device must be ssh authorised_host (ssh can't ask for password)) /// </summary> /// <returns>Tunnel connection state or throws TunnelUnknownConnectionStateException when netstat has no result.</returns> /// <exception cref="TunnelUnknownConnectionStateException">When netstat on remote device (command executed via SSH) returns null/whitespace (no netstatResult) /// or when regexp match for netstat result, finds other address (than `0.0.0.0`/`127.0.0.1`) using tunnel's port.</exception> public async Task <TunnelConnectionState> CheckConnectionType() { string netstatLocalAddress = null; BashProcess netstatProcess = new BashProcess($"ssh {RemoteHost.User}@{RemoteHost.IP} netstat -tlnp") { SubscribeStandardOutput = false }; string netstatResult = await netstatProcess.RunNewProcessAndReadStdOutputAsync(); if (!string.IsNullOrWhiteSpace(netstatResult)) { Regex regex = new Regex($@"[0-255].[0-255].[0-255].[0-255](?=:{RemoteHost.Port})"); Match match = regex.Match(netstatResult); if (match.Success) { netstatLocalAddress = match.Groups[0].Value; } switch (netstatLocalAddress) { case "0.0.0.0": IsTunnelEstablished = true; return(TunnelConnectionState.RemoteConnection); case "127.0.0.1": IsTunnelEstablished = true; return(TunnelConnectionState.LocalConnection); case null: // No match for regexp pattern - it means, no connection established IsTunnelEstablished = false; return(TunnelConnectionState.NoConnection); default: throw new TunnelUnknownConnectionStateException($"Unknown Tunnel Connection State: {netstatLocalAddress}"); } } throw new TunnelUnknownConnectionStateException("Unknown Tunnel Connection State: no netstatResult"); }