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
atSystem.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!
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.