Establish FtpWebRequest through a HTTP proxy using the CONNECT method - c#

I try to establish a FTP connection using the FtpWebRequest. I need to establish the connection through a squid proxy server, that requires me to send a CONNECT command first.
I have tried the code below, but this results in a GET request to the proxy server, which it refuses.
FtpWebRequest request = (FtpWebRequest)WebRequest.Create("ftp://" +server);
request.Credentials = new NetworkCredential(user, pwd);
var webproxy = new WebProxy(new Uri("http://" + proxy+ ":" + proxyPort));
webproxy.UseDefaultCredentials = true;
request.Proxy = webproxy;
request.UsePassive = true;
request.Method = WebRequestMethods.Ftp.ListDirectory;
FtpWebResponse response = (FtpWebResponse)request.GetResponse();
Is there any way that I can use the FtpWebRequest to establish my connection through the proxy?
If not, do you know a good and hopefully free .NET FTP client that I can use?
Thank you very much for your help.

FtpWebRequest is a "session-less" client, so it doesn't allow you to interact with the remote end while connected. I examined the code of FtpWebRequest.cs and I don't see how you can get it to send a "CONNECT" command.
"CONNECT" command doesn't seem to be part of the standard FTP command set, so I can't wrap my brain how this fits into something that an FTP library should do.
A good free FTP client that is session-oriented that I use is "SSH.NET Library" at http://sshnet.codeplex.com/ but I believe it only supports sftp. FtpWebRequest supports ftps and ftp. Find out if your FTP server supports sftp and you might be able to use SSH.NET . If your only option is ftps, I can report that I spent a long time looking for a free .net ftps client and I came away using FtpWebRequest.

FtpWebRequest supports HTTP proxy (and CONNECT command is mandatory part of that support). Your code is correct.
Though the HTTP proxy is not supported uploads (it is for all other operations). So maybe your actual problem is the upload, while you do not mention it.
For FTP uploads through HTTP proxy you have to use a 3rd party FTP client.
For example with WinSCP .NET assembly, you can use:
// Setup session options
SessionOptions sessionOptions = new SessionOptions
{
Protocol = Protocol.Ftp,
HostName = "example.com",
UserName = "user",
Password = "mypassword",
};
// Configure proxy
sessionOptions.AddRawSettings("ProxyMethod", "3"); // HTTP proxy
sessionOptions.AddRawSettings("ProxyHost", "proxy");
using (Session session = new Session())
{
// Connect
session.Open(sessionOptions);
// Upload file
string localFilePath = #"C:\path\file.txt";
string pathUpload = "/file.txt";
session.PutFiles(localFilePath, pathUpload).Check();
}
For the options for the SessionOptions.AddRawSettings, see raw settings.
Easier is to have WinSCP GUI generate C# FTP code template for you.
Note that WinSCP .NET assembly is not a native .NET library. It's rather a thin .NET wrapper over a console application.
(I'm the author of WinSCP)

Related

Running a C# FTP client against FTP server (active mode) from an Azure Function - "500 Syntax error, command unrecognized"

I am trying to connect to a remote FTP server that uses Active mode.
I have implemented a simple C# FTP client using .NET Core 3.1
var filename = _ftpServerUrl + "/" + targetFilePath;
System.Net.FtpWebRequest request = (System.Net.FtpWebRequest)System.Net.WebRequest.Create(filename);
request.Method = WebRequestMethods.Ftp.UploadFile;
request.Proxy = null;
request.Credentials = new NetworkCredential(username, password);
request.KeepAlive = false;
request.UsePassive = false;
request.UseBinary = true;
byte[] fileContents;
using (StreamReader sourceStream = new StreamReader(fileStream))
{
fileContents = Encoding.UTF8.GetBytes(sourceStream.ReadToEnd());
}
request.ContentLength = fileContents.Length;
using (Stream requestStream = request.GetRequestStream())
{
requestStream.Write(fileContents, 0, fileContents.Length);
}
This works excellent when running in VS 2019 on localhost.
But when the code is running as an Azure function (v3) in Azure, I get this response from the server:
500 Syntax error, command unrecognized
I understand the FTP Active mode requires the client to send its public IP address to the server, which will then try and open and incoming connection to the client.
Is there anything in the Azure Function sandbox preventing this?
I have even tried running my Azure Function using an App Service plan with a static public IP, without any luck.
Is there anyone out there who have had success connecting to an FTP server using Active Mode from an Azure App Service or Function?
FTP servers do not "use active mode". It's the FTP clients that may choose to use the active mode. But nowadays with ubiquitous firewalls and NATs, using the active mode can hardly work. That's true even more so in case of cloud services, VMs, and even more so with such a specific virtual services as Azure functions.
The FTP active mode needs an incoming connection. That's prevented by the Azure firewall and NATs. I'd expect that the VMs running the Azure functions won't have any incoming ports forwarded/opened. Why would they? It goes against the purpose of the Azure functions. They are intended to run a short-lived functionality, not servers. The active FTP mode is a strange beast in this respect. There's hardly any other client-side functionality that needs incoming ports.
Allowing the incoming connections (if possible at all in Azure functions) is not a programming question. No change in your code would help. In general, you should use the passive mode.
For some background, see my article about a network configuration needed for the active and passive FTP modes.

SSH.NET timeout when connecting to AWS Managed SFTP server in C#

I'm having issues trying to establish a connection to an AWS Managed SFTP server. Using the credentials I have on hand, I'm able to connect to the server from my Windows command line using the sftp command. Here's my .NET code:
using (var client = new SshClient(new ConnectionInfo(baseHost, user,
new AuthenticationMethod[]{
new PrivateKeyAuthenticationMethod(user,new PrivateKeyFile[]{
new PrivateKeyFile(keyLocation, pkpassword)
}),
}
)))
{
client.Connect(); // Timeout here
}
The code above gets to the client.Connect() line, then times out after 30 seconds with a Renci.SshNet.Common.SshOperationTimeoutException exception. When I look at what's happening with Wireshark, I see that the protocol being used by the sftp command line utility is SSH, while the SSH.NET is using TCP, and the packet sizes are completely different.
Does anybody know what I might be missing here?
I'm running the sftp command-line utility on the same computer as the above code. The first Wireshark image below is from the C# code above. The second is from the sFTP utility:
When I attempt to connect to the server's port 22 using PuTTY in raw mode, I get no response.
Thanks, Jim
This is bug both in old versions of SSH.NET and on Amazon Managed SFTP server side.
According to RFC 4253 Section 4.2. Protocol Version Exchange:
When the connection has been established, both sides MUST send an identification string.
Both SSH.NET client and Amazon Managed SFTP server fail this requirement. Both first wait for the other side to send the identification string before sending its own. A deadlock is inevitable (interrupted only by a timeout). That also explains why Wireshark does not identify the session as SSH, as there's no data exchanged at all. Hence, there's nothing by which the protocol can be identified.
If you can modify SSH.NET source code, moving this line in Session.Connect:
SocketAbstraction.Send(_socket, Encoding.UTF8.GetBytes(string.Format(CultureInfo.InvariantCulture, "{0}\x0D\x0A", ClientVersion)));
... above this block:
Match versionMatch;
// Get server version from the server,
// ignore text lines which are sent before if any
while (true)
{
...
}
... should fix the problem.
Also consider reporting the bug to Amazon.
I have reported the bug to SSH.NET
including the needed change. The fix is included from SSH.NET 2020.0.0 up.
If you cannot change SSH.NET code, you will need to use another SFTP library.
For example my WinSCP .NET assembly is compatible with Amazon Managed SFTP server.
This is an equivalent of your code:
// Set up session options
SessionOptions sessionOptions = new SessionOptions
{
Protocol = Protocol.Sftp,
HostName = baseHost,
UserName = user,
SshHostKeyFingerprint = ...,
SshPrivateKeyPath = keyLocation,
PrivateKeyPassphrase = pkpassword,
};
using (Session session = new Session())
{
// Connect
session.Open(sessionOptions);
// Your code
}
WinSCP GUI can generate a code template like the one above for you.

How to perform "double hop" via SFTP/SSH in .NET?

In my scenario i am trying to go from Local App Server -> Middle Server (DMZ) -> Client Server
I need to move files from the Local App Server to the Client Server and back.
So my question is what is the most widely used standard for doing this?
I am currently using WinSCP to connect to the Middle Server via SFTP, and then invoking a command on the Middle Server to download and upload files to the Client Server. I'm not really a fan of this, as i feel like its prone to error as i am manually entering a command, rather than using the WinSCP's library to upload and download. It also leaves me stuck when i try to list all files on the Client Server with a command, as the function returns void
I have looked at SSH.NET which seems like its more widely used, however i cant see any real way of performing a "double hop" with that library either.
With WinSCP .NET assembly, it's easy:
SessionOptions sessionOptions = new SessionOptions
{
Protocol = Protocol.Sftp,
HostName = "example.com",
UserName = "username",
Password = "password",
SshHostKeyFingerprint = "ssh-rsa 2048 xxxxxxxxxxx...=",
};
sessionOptions.AddRawSettings("Tunnel", "on");
sessionOptions.AddRawSettings("TunnelHostName", "tunnel.example.com");
sessionOptions.AddRawSettings("TunnelUserName", "username");
sessionOptions.AddRawSettings("TunnelPasswordPlain", "password");
sessionOptions.AddRawSettings("TunnelHostKey", "ssh-rsa 2048 xxxxxxxxxxx...=");
using (Session session = new Session())
{
session.Open(sessionOptions);
// Your code
}
WinSCP GUI can generate a code template to connect through tunnel, like the one above, for you (except for the TunnelHostKey).
With SSH.NET you can implement a port forwarding explicitly by:
opening connection to the "Middle Server";
creating a forwarded port;
opening a second connection to the forwarded port.
For some example, see Connection to MySQL from .NET using SSH.NET Library.
Another hackish solution is to execute ssh on the "Middle Server" to facilitate the second "hop".

download data using web client through proxies on a C# console application

I have written a code to hit a particular URL and download data using a WebClient, and have set a proxy which I have purchased from MyPrivateProxies.net to this webclient with credentials and other needed parameters through which it hits the mentioned URL.
But I get this exception every time at the last line.
System.Net.WebException: Unable to connect to the remote server
System.Net.Sockets.SocketException: A connection attempt failed
because the connected party did not properly respond after a period of
time, or established connection failed because connected host has
failed to respond
below is my snippet of code:
WebClient client = new WebClient();
WebProxy webProxy = new WebProxy("my_proxy_with_port");
NetworkCredential netcredit = new NetworkCredential(username, password, domain);
client.Credentials = netcredit;
webProxy.Credentials = netcredit;
webProxy.UseDefaultCredentials = false;
webProxy.BypassProxyOnLocal = false;
client.Proxy = webProxy;
client.Encoding = Encoding.UTF8;
String strURL = "url_to_be_hit";
String jsonString = client.DownloadString(strURL);
Do I need some other settings on my windows 7 system or any config changes in app.config?
Are there other configuration for a console app to use proxies?
I think you just need to assign the proxy credentials to the WebProxy object, not to the WebClient.
I dont know myprivateproxies.net but you have to make sure that you are using a http proxy, not a socks proxy or something else.
got it resolved wasn't my code issue or URI points or any of my strings which I was passing...the port was not opened at my end on local system

How can I force a C# FtpWebRequest to use a direct IP to get onto the Internet instead of going through an HTTP proxy?

(Related to: FTP directory listing returned as HTML instead of simple Linux ls output)
How can I force a C# program (FtpWebRequest) to use a direct IP to get onto the Internet instead of going through an HTTP proxy? (My knowledge of IT networks and related terminology is scant. Apologies in advance.)
To break out from the internal company network there is
Indirect way via the IP of a (HTTP) proxy server.
Direct IP of an internet service (not a proxy server).
I know this because, when using FileZilla with / without the proxy set in Internet Explorer, then on the remote FTP server, the logs either show the IP of the proxy, or the direct IP.
Code using C# FtpWebRequest to connect to FTP server outside of the company.
FtpWebRequest request = WebRequest.Create(uri) as FtpWebRequest;
request.Method = WebRequestMethods.Ftp.ListDirectory;
request.Credentials = server.Credential;
request.KeepAlive = true;
request.UsePassive = true;
request.EnableSsl = false;
//proxy options
//1.
// do nothing
//2.
request.Proxy = null;
//3. setup HTTP proxy
request.Proxy = new WebProxy(proxyuri, true);
request.Proxy.Credentials = System.Net.CredentialCache.DefaultCredentials;
request.Proxy.Credentials = new NetworkCredential("Username", "Password");
Stream responseStream = response.GetResponseStream();
StreamReader reader = new StreamReader(responseStream);
while (!reader.EndOfStream)
{ ... }
When I set the proxy, then the C# program (obviously) uses the proxy server to get to the FTP server. However because this is an HTTP proxy, the directory listing is returned as HTML and deleting, uploading, and making directories is not possible. The program needs to delete files, create folders on the FTP.
Similarly, when not setting the proxy, then the program uses the settings in Internet Explorer, where the proxy is set, which then again uses the HTTP proxy.
In the code, setting the proxy to null (request.Proxy = null OR WebRequest.DefaultWebProxy OR GlobalProxySelection.GetEmptyWebProxy() OR new WebProxy(); ) causes an exception "The remote server returned an error: (550) File unavailable (e.g., file not found, no access)."
This problem only occurs for one particular external FTP server (that happens to be running vsftpd). I tested using a different external FTP and both the proxy and the non proxy / direct connection work.
Questions
1. It seems the C# program can only breakout using the proxy. Why, when the proxy is not set, the program does not use the direct IP?
2. How can I force the program to use the direct IP?
3. Could the problem be due to the FTP server (vsftpd)?
The windows hosts file (%systemroot%\system32\drivers\etc) had erroneous entries. I am not exactly sure which entry was the problem but the file now only contains one line
127.0.0.1 localhost

Categories