Files
cdrtool/SSH.cs
Doug Macintosh 336b0dbb7e first real commit
2026-03-08 17:05:59 -04:00

372 lines
13 KiB
C#

using System;
using System.IO;
using System.Threading;
using Renci.SshNet;
namespace cdrtool
{
public class sftp
{
private static SftpClient CreateClient(
string host,
int port,
string username,
string password)
{
var client = new SftpClient(host, port, username, password);
client.ConnectionInfo.Timeout = System.TimeSpan.FromSeconds(15);
client.OperationTimeout = System.TimeSpan.FromSeconds(30);
client.KeepAliveInterval = System.TimeSpan.FromSeconds(10);
return client;
}
public static Renci.SshNet.ConnectionInfo BuildConnectionInfo(
string host,
int port,
string username,
string password,
Renci.SshNet.PrivateKeyFile privateKeyFile = null)
{
var auth = new System.Collections.Generic.List<Renci.SshNet.AuthenticationMethod>();
if (privateKeyFile != null)
auth.Add(new Renci.SshNet.PrivateKeyAuthenticationMethod(username, privateKeyFile));
if (!string.IsNullOrEmpty(password))
{
var kb = new Renci.SshNet.KeyboardInteractiveAuthenticationMethod(username);
kb.AuthenticationPrompt += (s, e) =>
{
foreach (var p in e.Prompts)
p.Response = password;
};
auth.Add(kb);
auth.Add(new Renci.SshNet.PasswordAuthenticationMethod(username, password));
}
return new Renci.SshNet.ConnectionInfo(host, port, username, auth.ToArray())
{
Timeout = System.TimeSpan.FromSeconds(30),
RetryAttempts = 1
};
}
/*
public static bool ConnectWithRetries(
Renci.SshNet.ConnectionInfo info,
int maxAttempts,
int timeoutSeconds)
{
for (int i = 0; i < maxAttempts; i++)
{
if (TrySftpConnectWithHardTimeout(info, timeoutSeconds))
return true;
System.Threading.Thread.Sleep(5000);
}
return false;
}
*/
public static Renci.SshNet.SftpClient TrySftpConnectWithHardTimeout(
Renci.SshNet.ConnectionInfo connectionInfo,
int maxAttempts,
int timeoutSeconds)
{
for (int i = 0; i < maxAttempts; i++)
{
Renci.SshNet.SftpClient client = null;
try
{
var task = System.Threading.Tasks.Task.Run(() =>
{
client = new Renci.SshNet.SftpClient(connectionInfo);
// These are still useful, but not sufficient alone
client.ConnectionInfo.Timeout =
System.TimeSpan.FromSeconds(30);
client.KeepAliveInterval =
System.TimeSpan.FromSeconds(15);
client.Connect();
});
bool completed =
task.Wait(System.TimeSpan.FromSeconds(timeoutSeconds));
if (!completed)
{
// HARD TIMEOUT
try
{
client?.Dispose();
}
catch { }
//return null;
}
else
{
return client;
}
}
catch
{
try
{
client?.Dispose();
}
catch { }
//return null;
}
System.Threading.Thread.Sleep(5000);
}
return null;
}
public static void Upload_stream(MemoryStream ms, string filename, string host, int port, string username, string password)
{
int maxAttempts = 5;
int timeout = 120; //seconds
try
{
//using (var sftpClient = CreateClient(host, port, username, password))
var sftpInfo = BuildConnectionInfo(host, port, username, password);
Renci.SshNet.SftpClient sftpClient = null;
try
{
sftpClient = TrySftpConnectWithHardTimeout(sftpInfo, maxAttempts, timeout);
}
catch (Renci.SshNet.Common.SshConnectionException e)
{
Logger.Log(1, " connection exception ({0}) : {1}", "", e.Message);
}
if ( sftpClient == null)
{
Logger.Log(1, " null connection exception. Transfer failed.");
return;
}
try
{
sftpClient.UploadFile(
ms,
filename,
uploaded =>
{
Logger.Log(5, $"Uploaded {(double)uploaded / ms.Length * 100}% of the file.");
});
}
catch (Renci.SshNet.Common.SshException e)
{
Logger.Log(0, " SSH upload Exception {0}:{1}", e.Message, e.InnerException.Message);
}
Logger.Log(1, " -- uploaded: {0} ({3}KB) to {1}:{2}", filename, host, port, (ms.Length / 1024));
sftpClient.Disconnect();
Logger.Log(" SSH Disconnected");
try
{
sftpClient?.Dispose();
}
catch { }
return; // success
}
catch (Exception e)
{
Logger.Log(0, "SFTP exception {0}:{1}", e.Message, e.InnerException.Message);
}
}
public static void Upload_stream_linear(MemoryStream ms, string filename, string host, int port, string username, string password)
{
int attempts = 0;
int maxAttempts = 5;
while (true)
{
try
{
using (var sftpClient = CreateClient(host, port, username, password))
{
attempts++;
try
{
sftpClient.Connect();
}
catch (Renci.SshNet.Common.SshConnectionException e)
{
Logger.Log(1, " connection exception ({0}) : {1}", attempts, e.Message);
}
//------------
if (!sftpClient.IsConnected)
{
Logger.Log(1, " connection failure {0}", attempts);
if (attempts < 10) { Thread.Sleep(TimeSpan.FromSeconds(5)); continue; } else { break; };
}
try
{
sftpClient.UploadFile(
ms,
filename,
uploaded =>
{
Logger.Log(5, $"Uploaded {(double)uploaded / ms.Length * 100}% of the file.");
});
}
catch (Renci.SshNet.Common.SshException e)
{
Logger.Log(0, " SSH upload Exception {0}:{1}", e.Message, e.InnerException.Message);
}
Logger.Log(1, " -- uploaded: {0} ({3}KB) to {1}:{2}", filename, host, port, (ms.Length / 1024));
sftpClient.Disconnect();
Logger.Log(" SSH Disconnected");
return; // success
}
}
catch (Exception e)
{
Logger.Log(0, "SFTP exception {0}:{1}", e.Message, e.InnerException.Message);
}
attempts++;
if (attempts >= maxAttempts)
{
Logger.Log(0, ("Failed to Connect after 10 attempts."));
return;
}
Logger.Log(1, " ...retrying ({0})", attempts);
Thread.Sleep(TimeSpan.FromSeconds(5));
} // end of while true/do forever
}
public static void Upload_stream_old( MemoryStream ms, string filename, string host, int port, string username, string password)
{
try
{
using (var sftpClient = new SftpClient(host, port, username, password))
//using (var fs = new FileStream(fileToUpload, FileMode.Open))
{
sftpClient.OperationTimeout = TimeSpan.FromSeconds(30);
sftpClient.ConnectionInfo.Timeout = TimeSpan.FromSeconds(30);
int attempts = 0;
do
{
attempts++;
try
{
sftpClient.Connect();
}
catch (Renci.SshNet.Common.SshConnectionException e)
{
Logger.Log(1," retrying ({0}) : {1}",attempts, e.Message);
Thread.Sleep(TimeSpan.FromSeconds(5));
}
}
while (attempts < 10 && !sftpClient.IsConnected);
if (attempts >= 10)
{
throw new Exception("Failed to Connect after 10 attempts.");
}
else
{
Logger.Log(" SSH Connected");
try
{
sftpClient.UploadFile(
ms,
filename,
uploaded =>
{
Logger.Log(5, $"Uploaded {(double)uploaded / ms.Length * 100}% of the file.");
});
}
catch (Renci.SshNet.Common.SshException e)
{
Logger.Log(0, " SSH upload Exception {0}:{1}", e.Message, e.InnerException.Message);
}
Logger.Log(" SSH Disconnected");
sftpClient.Disconnect();
}
}
Logger.Log(1, " -- uploaded: {0} ({3}KB) to {1}:{2}", filename, host, port, ( ms.Length/1024) );
}
catch (Exception e)
{
Logger.Log(e.Message);
}
}
public static void Upload_file(string fileToUpload, string host, int port, string username, string password)
{
try
{
using (var sftpClient = new SftpClient(host, port, username, password))
using (var fs = new FileStream(fileToUpload, FileMode.Open))
{
sftpClient.OperationTimeout = TimeSpan.FromSeconds(30);
int attempts = 0;
do
{
try
{
attempts += 1;
sftpClient.Connect();
}
catch (Renci.SshNet.Common.SshConnectionException e)
{
Logger.Log(1, " retrying in 5s ({0}) : {1}", attempts, e.Message);
Thread.Sleep(TimeSpan.FromSeconds(5));
}
} while (attempts < 10 && !sftpClient.IsConnected);
Logger.Log(" SSH Connected");
try
{
sftpClient.UploadFile(
fs,
"/Doug/" + Path.GetFileName(fileToUpload),
uploaded =>
{
Logger.Log(5,$"Uploaded {(double)uploaded / fs.Length * 100}% of the file.");
});
}
catch (Renci.SshNet.Common.SshException e)
{
Logger.Log(0, " SSH Exception {0}:{1}", e.Message, e.InnerException.Message);
}
sftpClient.Disconnect();
}
Logger.Log(1," uploaded: {0}", fileToUpload);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
}
}