I am trying to get the directory list of a FTPS FileZilla server using the code below :
ftpRequest = (FtpWebRequest)FtpWebRequest.Create(host + "/" + directory);
ftpRequest.EnableSsl = true;
ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(ValidateCertificate);
ftpRequest.Credentials = new NetworkCredential(user, pass);
ftpRequest.UseBinary = true;
ftpRequest.UsePassive = true;
ftpRequest.KeepAlive = true;
ftpRequest.Method = WebRequestMethods.Ftp.ListDirectory;
ftpResponse = (FtpWebResponse)ftpRequest.GetResponse();
I got an exception when FtpWebResponse)ftpRequest.GetResponse() is executed :
the underlying connection was closed. The server committed a protocol
violation.
When I switch to normal FTP connection. Everything works correctly.
Did I miss something to establish this FTPS connection ?
thanks for help
Implicit FTPS is not supported by the FtpWebRequest class (see here).
When EnableSsl is set to true, it actually triggers an AUTH TLS command to the server, asking to start an Explicit FTPS session.
In your case, you have to configure Filezilla Server to use Explicit FTPS. The procedure is documented on Filezilla Wiki
I've encountered the same problem but for uploading a file, on ftpWriter.Close().
Also, I was unable to do a GetRequestStream after a successful PrinWorkingDirectory for example.
The problem seems to be a "Expect: 100-continue" in the post - while I didn't quite checked this, the problem is somewhere there.
I've tried every solution found on the internet : changing the KeepAlive to true, adding to the App.Config file
<system.net>
<settings>
<servicePointManager expect100Continue="false"/>
<httpWebRequest useUnsafeHeaderParsing="true"/>
</settings>
</system.net>
Nothing really worked.
I've spend a lot of time and trying different other third party libraries (idea I didn't like too much), until finally I came on a code that used the same classes and method but worked !!
After analyzing the code, I've finally figured out : the code targeted .NET Framework 2.0 while my code was targeting .NET Framework 4.5.
I seems that Microsoft did a little bug while passing from Framework 3.5 to Framework 4.
As it's not a solution to convert your new projects to target an old framework, you can create a dll for the FTP operations, pointing to the 3.5 .NET Framework, or, you can use third party libraries.
I'm maybe a little bit late, but it will probably help other frustrated developers on this matter, in the future.
This is the time to migrate from ftp to sftp!
you can follow to, this code reference:
using Renci.SshNet;
using System.IO;
private void UploadFileToSFTP()
{
try
{
String sourcefile = #"C:\path\file.txt";
String host = #"000.000.000.0";
String username = #"usename";
String password = #"password";
int port = 22;
string destinationpath = "/var/www/html/path/public/destinationfolder";
using (SftpClient client = new SftpClient(host, port, username, password))
{
client.Connect();
client.ChangeDirectory(destinationpath);
using (FileStream fs = new FileStream(sourcefile, FileMode.Open))
{
client.BufferSize = 4 * 1024;
client.UploadFile(fs, Path.GetFileName(sourcefile));
}
}
}
catch (Exception)
{
throw;
}
}
hope it would be helpful.
Thank you!
Related
How to run Verify the existence of a mailbox address.?
http://www.mimekit.net/docs/html/M_MailKit_Net_Smtp_SmtpClient_Verify.htm
using (var client = new SmtpClient())
{
client.Connect("smtp.mail.ru", 465, true);
client.Authenticate(name, pass);
var d = client.Verify(email);
}
Error MailKit.Net.Smtp.SmtpCommandException: "unrecognized command"
Most SMTP servers do not support the VRFY command anymore. Your server doesn't support it which is why you are getting an error.
Possible a duplicate.
You can read this question, it's in PHP but all the concepts are the same for your situation.
As jstedfast said, that command isn't supported anymore and to sum up your reading: There is not a reliable way to verify that anymore because of spams.
The following console app works fine on Windows but when I run it in WSL or as a Linux Docker container I get a timeout from the server.
using System;
using System.IO;
using System.Net.Sockets;
namespace ClientTest
{
class Program
{
static void Main(string[] args)
{
const string server = "inbound-smtp.us-west-2.amazonaws.com";
const int port = 25;
Console.WriteLine($"Connecting to: {server}");
using (var client = new TcpClient(server, port))
{
using (NetworkStream stream = client.GetStream())
using (StreamReader clearTextReader = new StreamReader(stream))
using (StreamWriter clearTextWriter = new StreamWriter(stream) { AutoFlush = true })
{
// read connection response
var connectResponse = clearTextReader.ReadLine();
Console.WriteLine(connectResponse);
// send command
Console.WriteLine($"HELO domain.com");
clearTextWriter.WriteLine("HELO domain.com");
// read command response
var commandResponse = clearTextReader.ReadLine();
Console.WriteLine(commandResponse);
}
}
}
}
}
Output on Windows with 250 success...
Output on Linux with 451 Timeout...
However, If I change the server address to ASPMX.L.GOOGLE.com for example then the app works as expected on Windows and Linux.
The console app is targeting .net5.0 but have also tried it with .net3.1 and got the same results. I have also tried running it on a Linux VM with Docker in the cloud but also got the same results.
Are there differences with StreamWriter/NetworkStream on Linux (compared to Windows) and more specifically why do I not get the same issue when using the Google server?
I wonder... is this as simple as a line-ending delta? Windows and Linux have different Environment.NewLine values. Perhaps try adding \r\n explicitly (since Windows seems to work), rather than relying on the local OS line-ending?
clearTextWriter.Write("HELO domain.com\r\n");
Also: try adding an explicit flush - clearTextWriter.Flush();.
As for why the Google server works: perhaps it is more forgiving in what it accepts, where-as the first server is following a specification more precisely, and that specification presumably says that messages are terminated by \r\n. Or the specification may be ambiguous, and Google just chose to be permissive.
BTW: for similar reasons, it would be a good idea to explicitly specify an Encoding here, presumably UTF8.
I am very new to ssl/tls, so please be patient. The code works for me few months for another sites (iprima.cz for example), but dont working for rtvs.sk.
it writes: Could not create SSL/TLS secure channel.
I have tried everything, but nothing works. Please help me.
string url1 = "https://www.rtvs.sk/televizia/archiv";
ServicePointManager.SecurityProtocol = (SecurityProtocolType)(0xc0 | 0x300 | 0xc00);
ServicePointManager.Expect100Continue = true;
ServicePointManager.DefaultConnectionLimit = 9999;
ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, errors) => true;
//It is because I have tried enerything
string rets = "";
using (WebClient wc = new WebClient())
{
wc.Proxy = null;
int i = 0;
zatry:
try
{
Stream stream = wc.OpenRead(url1);
Encoding kod = Encoding.UTF8;
StreamReader sr = new StreamReader(stream, kod);
rets = sr.ReadToEnd();
sr.Close();
}
It seems to me to be site-specific, it works on Opera and other browsers, but not on my code.
It maybe a simple thing, but after reviewing all solutions for 5 hours and nothing works, I need a help. Thank you very much
Microsoft .NET Framework 4.7.03062SP1Rel
It has the same error on Microsoft Visual Studio 2010 and 2019 version 16.4.3
Introduction
It seems like the https://www.rtvs.sk/ server uses a not standard way of communication, that is not supported in .NET Framework libraries.
The server redirects any HTTP connection to HTTPS connection and this can not be avoided.
To bypass the "Could not create SSL/TLS secure channel" error you need to use a different HTTPS client. And I do not mean use the WebRequest.Create instead of the WebClient. The error is still here as this was the code I used in my application. A communication error is probably somewhere deeper in system since even Internet Explorer does not open the page (at least not in Windows 7).
There are a few possible options of an alternate client:
Write your own client from scratch - I cannot recommend this solution, it is hard, prone to errors...
Buy an existing alternative client - I found only one alternative HTTPS client for C# and the price was high. Too much for my simple personal application.
Use an alternative external client - e.g. wget, or an other downloader (custom and/or existing).
Try to use the application from Linux (or Mono framework). I did not test this solution but it is (at least theoretically) possible that the problem can be solved this way.
My Solution
After a simple test I found out, that there is no problem when downloading using Python. So I made a simple Python downloader and use it from my C# application.
The code in C# is:
private const string PythonSciptPath = "PythonDownloader.py";
private static bool UseStandardDownloader(string address) =>
!address.StartsWith("https://www.rtvs.sk/", StringComparison.Ordinal);
private static byte[] GetData(string address)
{
var start = new ProcessStartInfo
{
FileName = "python.exe",
Arguments = PythonSciptPath + " \"" + address + "\"",
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = true
};
string result;
using (Process process = Process.Start(start))
using (StreamReader reader = process.StandardOutput)
{
result = reader.ReadToEnd();
}
return Convert.FromBase64String(result);
}
The code in Python (the "PythonDownloader.py" file) is:
import base64
import sys
import urllib.request
opener = urllib.request.URLopener()
stream = opener.open(sys.argv[1])
while True:
contentBytes = stream.read(60)
if len(contentBytes) == 0:
break
contentbase64Bytes = base64.b64encode(contentBytes)
contentString = contentbase64Bytes.decode("UTF-8")
print(contentString)
A simple way how to work with this is to add the file to project and select "Copy to output directory" option to "Copy if newer".
The current simple solution the code does not handle any error. I leave that part to you.
I also tried to use IronPython, but the simple client I wrote did not work because of a missing library. And it is possible that it will not work because it uses the .NET Framework libraries as interpreter.
I'm not sure why am I getting this result. I'm running this on a Linux server. (It's my small web site's shared web hosting account.)
The files are grouped as follows:
and the Another Dir has one file inside:
So I'm trying to retrieve the contents of the badname directory inside 1somelongdir1234567 directory that doesn't exist on the server, using this code:
try
{
FtpWebRequest ftpRequest = (FtpWebRequest)WebRequest.Create(
"ftp://server12.some-domain.com/public_html/1somelongdir1234567/badname");
ftpRequest.EnableSsl = true;
ftpRequest.Credentials = new NetworkCredential("user", "password");
ftpRequest.KeepAlive = true;
ftpRequest.Timeout = -1;
ftpRequest.Method = WebRequestMethods.Ftp.ListDirectoryDetails;
using (FtpWebResponse response1 = (FtpWebResponse)ftpRequest.GetResponse())
{
//*****BEGIN OF EDIT*****
Console.WriteLine(response1.StatusDescription);
Console.WriteLine(response1.StatusCode);
//*****END OF EDIT*****
using (StreamReader streamReader = new StreamReader(response1.GetResponseStream()))
{
List<string> arrList = new List<string>();
for (; ; )
{
string line = streamReader.ReadLine();
//I get to here, where `line` is null????
if (string.IsNullOrEmpty(line))
break;
arrList.Add(line);
//*****BEGIN OF EDIT*****
Console.WriteLine(line);
//*****END OF EDIT*****
}
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
So as you see, there's no such folder badname but instead of throwing an exception, my ftpRequest.GetResponse() succeeds and then streamReader.ReadLine() returns null like I showed in the code above.
More strangely, if I provide an actual directory as such:
FtpWebRequest ftpRequest = (FtpWebRequest)WebRequest.Create(
"ftp://server12.some-domain.com/public_html/1somelongdir1234567/Another%20Dir");
the streamReader.ReadLine() still returns null.
Can someone explain why?
Edit: OK, guys, I updated the code above to retrieve the status code. I'm still puzzled though.
First, here's three values of connection URI and the response/output that I'm getting:
Example 1:
//Existing folder
"ftp://server12.some-domain.com/public_html/1somelongdir1234567"
Output:
150 Accepted data connection
OpeningData
drwxr-xr-x 3 username username 4096 Sep 5 05:51 .
drwxr-x--- 118 username 99 4096 Sep 5 05:54 ..
drwxr-xr-x 2 username username 4096 Sep 5 05:52 Another Dir
-rw-r--r-- 1 username username 11 Sep 5 05:51 test123.txt
Example 2:
//Another existing folder
"ftp://server12.some-domain.com/public_html/1somelongdir1234567/Another%20Dir"
Output:
150 Accepted data connection
OpeningData
Example 3:
//Nonexistent folder
"ftp://server12.some-domain.com/public_html/1somelongdir1234567/SomeBogusName"
Output:
150 Accepted data connection
OpeningData
So why is it giving me the same result for example 2 as I'm getting for 3?
As for what ftp server it is, I wasn't able to see it in the network logs nor in FtpWebRequest itself. Here's what I was able to get from Microsoft Network Monitor:
Welcome to Pure-FTPd [privsep] [TLS] ----------..
220-You are user number 4 of 50 allowed...
220-Local time is now 13:14. Server port: 21...
220-This is a private system - No anonymous login..
220-IPv6 connections are also welcome on this server...
220 You will be disconnected after 15 minutes of inactivity...
The URL for the ListDirectory/ListDirectoryDetails method should end with a slash, in general.
Without a slash, results tend to be uncertain.
WebRequest.Create("ftp://example.com/public_html/1somelongdir1234567/Another%20Dir/");
As stuartd noted, you get an FTP status code on the response, As far as I know, because of the way FTP commands work, you will never get an exception when a server fails to execute a command you requested. The server will instead just tell you it failed through the FTP response code.
One thing you have to realize is that FTP has no standards for the text it sends back, because it was never designed for its responses to be machine-interpreted. If you got some time, look up the code of FileZilla, and check their class for interpreting the file listings (directorylistingparser.cpp). There are dozens of ways to interpret it, and the application will just try them all until one works. Because, unless you know what kind of FTP you're connecting to, that's the only way to do it.
So if that specific server decides to send an empty string back if you request a nonexistent directory, that's just how their FTP implementation does it, and it's the client's problem to figure out what that means.
If you can't figure out it's a failure from the FTP response code, you can just test if it returns something different for an existing but empty folder. If so, you can easily distinguish between that and this empty response, and treat the empty response as "not found" error in your program.
I have code in a Windows Service that successfully connects to an FTP server when I run it locally through a test harness (with the FTP server being on another machine on the local network).
When I install it in the production hosting environment, though, I get the dreaded WebException "Unable to connect to the remote server". It doesn't seem to matter whether I'm using ACTV or PASV FTP, all I get it this WebException. If I try to FTP from the Windows command line, however, it works perfectly well (so it's not the firewall at fault).
The code I'm using (adapted from How to List Directory Contents with FTP in C#?) reads:
private static readonly string __ftpSourceServer = "ftp://"
+ ConfigurationManager.AppSettings["FtpServer"] + "/";
private static readonly NetworkCredential __ftpCreds = new NetworkCredential(
ConfigurationManager.AppSettings["Username"],
ConfigurationManager.AppSettings["Password"]);
// And now the method MediaSyncDaemon.GetFilesToFetch:
bool usePassive = Boolean.TryParse(ConfigurationManager.AppSettings["UsePassive"]
, out usePassive) && usePassive;
Uri ftpSrv = new Uri(__ftpSourceServer + Uri.EscapeUriString(
ConfigurationManager.AppSettings["FtpPath"]));
Logger.Debug("Connecting to FTP server at " + ftpSrv + "; PASV? " + usePassive);
FtpWebRequest listRequest = (FtpWebRequest) WebRequest.Create(ftpSrv);
listRequest.Method = WebRequestMethods.Ftp.ListDirectory;
listRequest.Credentials = __ftpCreds;
listRequest.UsePassive = usePassive;
listRequest.UseBinary = false;
using (FtpWebResponse listResponse = (FtpWebResponse) listRequest.GetResponse())
{
// ReSharper disable AssignNullToNotNullAttribute
return new StreamReader(listResponse.GetResponseStream())
.ReadToEnd().Split(new[] { '\n', '\r' },
StringSplitOptions.RemoveEmptyEntries)
.Where(s => s.EndsWith(".zip", true, CultureInfo.InvariantCulture))
.ToList();
// ReSharper restore AssignNullToNotNullAttribute
}
The exception is thrown at the FtpWebRequest.GetResponse() call in the using line (outside the return statement), with nothing else in the stack trace:
System.Net.WebException: Unable to connect to the remote server
at System.Net.FtpWebRequest.GetResponse()
at (that line number in that file)
The only real difference between my test harness (which works) and the production environment (which doesn't) is the presence of a firewall in the production environment — all four servers are on slightly different subnets:
Dev client 10.16.6.155 subnet 255.255.255.128
Dev server 10.16.7.242 subnet 255.255.255.0
Prod client 192.168.102.107 subnet 255.255.255.0
Prod server 192.168.203.110 subnet 255.255.255.0
but the firewall can't be the problem is I can FTP from Prod client to Prod server interactively, just not programmatically.
I've tried changing the bool appSettings value for UsePassive and that makes no difference and, in every case, nothing shows up in the FTP server log (so it's not getting that far).
Now I'm not expecting anyone to be able to debug the hardware infrastructure of my hosting environment, but I'm struggling to think of what else I could vary to get this to work. I've seen it work locally in my test harness, calling the same method. In case it helps, the test harness code reads as follows:
[NUnit.Framework.Test]
public void FtpFileListTest()
{
ICollection<string> files = MediaSyncDaemon.GetFilesToFetch();
Assert.IsNotNull(files);
Assert.Greater(files.Count, 0);
}
Does anyone have any ideas of what else I could try, please?
Thanks!
Update
Having had some suggestions of places to look in the comments, I can update this a little further:
The problem does not appear to be user permissions — the service is running in the context of the Local System account (which has more permissions than Administrator does)
The problem does not appear to be code-access security. I've added a SocketPermission.Demand call to the entry method for this chunk of code:
System.Security.CodeAccessPermission socketPermission;
socketPermission = new SocketPermission(NetworkAccess.Connect,
TransportType.Tcp, ConfigurationManager.AppSettings["FtpServer"], 20);
socketPermission.Demand();
socketPermission = new SocketPermission(NetworkAccess.Connect,
TransportType.Tcp, ConfigurationManager.AppSettings["FtpServer"], 21);
socketPermission.Demand();
And I'm not seeing any SecurityException being thrown as a result; I'm still getting the same WebException, at the new line number for that same code position.
Does anyone have any further suggestions of what I could try?
I would like to share our problem and solution:
We were not able to connect to the FTP server with ftpwebrequest on a corporative PC, but on ours it would work fine.
The issue was that ftpwebrequest() was grabbing the proxy configuration that the company’s IT forces on the PC.
To resolve this issue, we added (ftpwebrequest object).proxy = null; before connecting.