using System; using System.IO; using System.Reflection; using System.Collections.Generic; using System.Threading; using Renci.SshNet; using System.Linq; namespace bdf { public class sftp { public class ConnectionDetails { public Int32 maxAttempts = 5; public Int32 timeOutSecs = 30; public Int32 retrySecs = 5; public Int32 OperationTimeout = 30; public Int32 KeepAliveInterval = 15; public string ppkFile = ""; public string passPhrase = ""; public string username = ""; public string password = ""; public string host = ""; public Int32 port = 22; } public static Renci.SshNet.ConnectionInfo BuildConnectionInfo(ConnectionDetails cd) { var auth = new System.Collections.Generic.List(); PrivateKeyFile pkFile = null; if (cd.username != "" && cd.ppkFile != "") { if (cd.ppkFile.Contains("Embedded")) { Logger.Log(1, " Processing internal PPK"); // Get the assembly where the resource is stored Assembly asm = Assembly.GetExecutingAssembly(); Stream stream = Stream.Null; if (asm.GetManifestResourceNames().Contains(cd.ppkFile)) // Fully-qualified resource name (namespace + filename) stream = asm.GetManifestResourceStream(cd.ppkFile); else { Logger.Log(0, "EXCEPTION: PPK resource not found."); throw new Exception("Private key file not found. "); } //Console.WriteLine("b - {0} {1}", cd.ppkFile, stream == null); try { if (stream == null || !stream.CanRead) throw new Exception("Private key file not found/not readable. "); else stream.Position = 0; // reset stream position } catch (Exception e) { Logger.Log(3, "EXCEPTION PPK {0}:{1}", e.Message, e.InnerException.Message); } try { pkFile = string.IsNullOrEmpty(cd.passPhrase) ? new PrivateKeyFile(stream) : new PrivateKeyFile(stream, cd.passPhrase); } catch (Renci.SshNet.Common.SshException e) { throw new Exception("Problems with your internal private key and/or passphrase. " + e.Message); } stream.Close(); Logger.Log(5, " Internal PPK loaded"); } else { try { pkFile = string.IsNullOrEmpty(cd.passPhrase) ? new PrivateKeyFile(cd.ppkFile) : new PrivateKeyFile(cd.ppkFile, cd.passPhrase); } catch (Renci.SshNet.Common.SshException e) { throw new Exception("Problems with your private key and/or passphrase. " + e.Message); } } if (pkFile != null) { auth.Add(new Renci.SshNet.PrivateKeyAuthenticationMethod(cd.username, pkFile)); //preferred Logger.Log(0, " Added PPK method."); } } if (!string.IsNullOrEmpty(cd.password)) { var kb = new Renci.SshNet.KeyboardInteractiveAuthenticationMethod(cd.username); kb.AuthenticationPrompt += (s, e) => { foreach (var p in e.Prompts) p.Response = cd.password; }; auth.Add(kb); auth.Add(new Renci.SshNet.PasswordAuthenticationMethod(cd.username, cd.password)); Logger.Log(0, " Added user/pass method."); } return new Renci.SshNet.ConnectionInfo(cd.host, cd.port, cd.username, auth.ToArray()) { Timeout = System.TimeSpan.FromSeconds(30), RetryAttempts = 1 }; } 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 bool serverExists(string host) { try { // 1. DNS resolution System.Net.IPAddress[] addresses = System.Net.Dns.GetHostAddresses(host); if (addresses == null || addresses.Length == 0) return false; // 2. Ping first resolved address using (System.Net.NetworkInformation.Ping ping = new System.Net.NetworkInformation.Ping()) { Logger.Log(1, "Validating server: {0} at {1}", host, addresses[0]); System.Net.NetworkInformation.PingReply reply = ping.Send(addresses[0], 3000); // 3s timeout return reply != null && reply.Status == System.Net.NetworkInformation.IPStatus.Success; } } catch { return false; } } public static void Upload_Stream(MemoryStream ms, string filename, ConnectionDetails cd) { try { //using (var sftpClient = CreateClient(host, port, username, password)) var sftpInfo = BuildConnectionInfo(cd); Renci.SshNet.SftpClient sftpClient = null; try { sftpClient = TrySftpConnectWithHardTimeout(sftpInfo, cd.maxAttempts, cd.retrySecs); } 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, cd.host, cd.port, (ms.Length / 1024)+1); //Console.WriteLine("starting dir pull"); var files = sftpClient.ListDirectory(".").Where(f => f.IsRegularFile && (f.Name.EndsWith(".ttf", System.StringComparison.OrdinalIgnoreCase) || //pdf f.Name.EndsWith(".xlsx", System.StringComparison.OrdinalIgnoreCase))); foreach (var s in files) { Logger.Log(0, " => {0} {1}", s.FullName, s.Attributes); } 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); } } // Legacy modules /* public static void Upload_Stream_old(MemoryStream ms, string outfile, ConnectionDetails cd) { //Logger.Log(0, "usp: file={0} host={1}:{2} user={3} pass={4}", outfile, cd.host, cd.port, cd.username, cd.password); int attempts = 0; while (true) { try { var valid_methods = new List(); PrivateKeyFile keyFile; if (cd.username != "" && cd.ppkFile != "") { if (cd.ppkFile.StartsWith("BDF")) { Logger.Log(3, "Processing PPK"); // Add in loading the private.key from an embedded resource file // Get the assembly where the resource is stored Assembly asm = Assembly.GetExecutingAssembly(); // Fully-qualified resource name (namespace + filename) Stream stream = asm.GetManifestResourceStream(cd.ppkFile); if (stream == null || !stream.CanRead) throw new Exception("Private key file not found. "); else stream.Position = 0; // reset stream position try { keyFile = string.IsNullOrEmpty(cd.passPhrase) ? new PrivateKeyFile(stream) : new PrivateKeyFile(stream, cd.passPhrase); } catch (Renci.SshNet.Common.SshException e) { throw new Exception("Problems with your internal private key and/or passphrase. " + e.Message); } stream.Close(); } else { try { keyFile = string.IsNullOrEmpty(cd.passPhrase) ? new PrivateKeyFile(cd.ppkFile) : new PrivateKeyFile(cd.ppkFile, cd.passPhrase);Console.WriteLine("b"); } catch (Renci.SshNet.Common.SshException e) { throw new Exception("Problems with your private key and/or passphrase. " + e.Message); } } valid_methods.Add(new PrivateKeyAuthenticationMethod(cd.username, keyFile)); //preferred } if (cd.username != "" && cd.password != "") { valid_methods.Add(new PasswordAuthenticationMethod(cd.username, cd.password)); //fallback to user/pass } if (valid_methods.Count == 0) throw new Exception("No valid login credentials found."); Logger.Log(0, " Found {0} valid methods. Using method={1}", valid_methods.Count, valid_methods[0].Name); AuthenticationMethod[] methods = valid_methods.ToArray(); ConnectionInfo connectionInfo = new ConnectionInfo( cd.host, cd.port, cd.username, methods ) //fallback to user/pass { Timeout = System.TimeSpan.FromSeconds(cd.timeOutSecs) }; //Console.WriteLine("Connection info done:"); using (var sftpClient = new SftpClient(connectionInfo)) { //sftpClient.ConnectionInfo.Timeout = System.TimeSpan.FromSeconds(15); //set above in connectionInfo sftpClient.OperationTimeout = System.TimeSpan.FromSeconds(cd.OperationTimeout); sftpClient.KeepAliveInterval = System.TimeSpan.FromSeconds(cd.KeepAliveInterval); attempts++; try { Logger.Log(5, "trying connect {0}", sftpClient.ConnectionInfo.Username); sftpClient.Connect(); } catch (Renci.SshNet.Common.SshAuthenticationException e) { Logger.Log(1, " authentication exception ({0}) : {1}", attempts, e.Message); } catch (Renci.SshNet.Common.SshConnectionException e) { Logger.Log(1, " connection exception ({0}) : {1}", attempts, e.Message); } catch (Exception 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, outfile, 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}", outfile, cd.host, cd.port, ((ms.Length / 1024) + 1)); //Console.WriteLine("starting dir pull"); var files = sftpClient.ListDirectory(".").Where(f => f.IsRegularFile && ( f.Name.EndsWith(".ttf", System.StringComparison.OrdinalIgnoreCase) || //pdf f.Name.EndsWith(".xlsx", System.StringComparison.OrdinalIgnoreCase) ) ); foreach (var s in files) { Logger.Log(0, " => {0} {1}", s.FullName, s.Attributes); } //if (client.Exists(path)) //{ // client.DeleteFile(path); //} 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 >= cd.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_dev(MemoryStream ms, string filename) { int attempts = 0; int maxAttempts = 5; int timeOut = 30; //seconds string ppkFile = "BDF.Embedded.private_passphr.ppk"; //passPhrase = ""; string passPhrase = "Rogers1!"; string username = "DEV_APP_BDF"; string host = "dev.mft.rogers.com"; int port = 22; Logger.Log(5, "usp: file={0} host={1}:{2} user={3} pass={4}", filename, host, port, username, passPhrase); while (true) { try { // Add in loading the private.key from an embedded resource file // Get the assembly where the resource is stored Assembly asm = Assembly.GetExecutingAssembly(); // Fully-qualified resource name (namespace + filename) Stream stream = asm.GetManifestResourceStream(ppkFile); //Stream stream = asm.GetManifestResourceStream("BDF.Embedded.private_nopassphr.ppk"); if (stream == null || !stream.CanRead) throw new Exception("Private keys not found. "); PrivateKeyFile keyFile = string.IsNullOrEmpty(passPhrase) ? new PrivateKeyFile(stream) : new PrivateKeyFile(stream, passPhrase); stream.Close(); ConnectionInfo connectionInfo = new ConnectionInfo( host, port, username, new PrivateKeyAuthenticationMethod(username, keyFile)) { Timeout = System.TimeSpan.FromSeconds(timeOut) }; //Console.WriteLine("Connection info done:"); using (var sftpClient = new SftpClient(connectionInfo)) { sftpClient.ConnectionInfo.Timeout = System.TimeSpan.FromSeconds(15); sftpClient.OperationTimeout = System.TimeSpan.FromSeconds(30); sftpClient.KeepAliveInterval = System.TimeSpan.FromSeconds(10); attempts++; try { Logger.Log(5, "trying connect {0}", sftpClient.ConnectionInfo.Username); 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)+1)); //Console.WriteLine("starting dir pull"); var files = sftpClient.ListDirectory(".").Where(f => f.IsRegularFile && f.Name.EndsWith(".ttf", System.StringComparison.OrdinalIgnoreCase)); foreach (var s in files) { Logger.Log(0, " => {0} {1}", s.FullName, s.Attributes); } //if (client.Exists(path)) //{ // client.DeleteFile(path); //} 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_ppk(MemoryStream ms, string filename, string host, int port, string username) { int attempts = 0; int maxAttempts = 5; int timeOut = 30; //seconds string passPhrase = "Rogers1!"; //passPhrase = ""; //string username = "DEV_APP_BDF"; //string host = "dev.mft.rogers.com"; //int port = 22; Logger.Log(5,"usp: file={0} host={1}:{2} user={3} pass={4}", filename, host, port, username, passPhrase); while (true) { try { // Add in loading the private.key from an embedded resource file // Get the assembly where the resource is stored Assembly asm = Assembly.GetExecutingAssembly(); // Fully-qualified resource name (namespace + filename) Stream stream = asm.GetManifestResourceStream("BDF.Embedded.private_passphr.ppk"); //Stream stream = asm.GetManifestResourceStream("BDF.Embedded.private_nopassphr.ppk"); if (stream == null || !stream.CanRead) throw new Exception("Private keys not found. "); PrivateKeyFile keyFile = string.IsNullOrEmpty(passPhrase) ? new PrivateKeyFile(stream) : new PrivateKeyFile(stream, passPhrase); stream.Close(); ConnectionInfo connectionInfo = new ConnectionInfo( host, port, username, new PrivateKeyAuthenticationMethod(username, keyFile)) { Timeout = System.TimeSpan.FromSeconds(timeOut) }; //Console.WriteLine("Connection info done:"); using (var sftpClient = new SftpClient(connectionInfo)) { sftpClient.ConnectionInfo.Timeout = System.TimeSpan.FromSeconds(15); sftpClient.OperationTimeout = System.TimeSpan.FromSeconds(30); sftpClient.KeepAliveInterval = System.TimeSpan.FromSeconds(10); attempts++; try { Logger.Log(5,"trying connect {0}", sftpClient.ConnectionInfo.Username); 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 { Console.WriteLine("trying to upload"); 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)); Console.WriteLine("starting dir pull"); var files = sftpClient.ListDirectory("."); foreach(var s in files) { Console.WriteLine("* {0}", s.FullName); } Console.WriteLine("disconnecting"); 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); } } */ } }