2

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:

  1. 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)

  2. 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?

Community
  • 1
  • 1
Owen Blacker
  • 4,117
  • 2
  • 33
  • 70
  • 1
    I strongly suspect it's the user the code is running as that isn't allowed to make outbound network connections. – Jon Skeet Feb 05 '13 at 18:58
  • Ah, thanks (and that was quick!). How would I test for that (or, better, grant that access)? – Owen Blacker Feb 05 '13 at 18:58
  • Are you able to interrogate the production environment with Wireshark? You'll be able to identify whether it's getting as far as actually making the request and then perhaps have an easier time identifying it whether it's a permissions issue. – Cashley Feb 05 '13 at 19:01
  • I think our security auditor would get rather upset with me if I were to install Wireshark on the production server. How would I grant that access to the code? Alternatively, how would I demand that access programmatically (in which case I'd see a SecurityException, right?)? – Owen Blacker Feb 05 '13 at 19:04
  • I believe there's a [SocketPermission attribute](http://msdn.microsoft.com/en-gb/library/system.net.socketpermissionattribute.aspx), although I've never actually used it so can't give an example. Perhaps you could try using TcpClient with your FTP port to see if it throws a more meaningful exception (I've never been a fan of how FtpWebRequest was modelled). Sorry - this is all just speculative! – Cashley Feb 05 '13 at 19:08
  • What account are you running the windows service under? Does it have network access? Checkout http://stackoverflow.com/questions/510170/the-difference-between-the-local-system-account-and-the-network-service-acco – Richard Schneider Feb 05 '13 at 19:21
  • Of course! It's almost certainly that I'm running the service in the wrong user account. I'll check in the morning (and update y'all). Thank you :) – Owen Blacker Feb 05 '13 at 21:20
  • Frustratingly, I'm running under the Local System account. I probably shouldn't be, in the longer term, but that does suggest it's not the user account's permissions that are at fault. I'll have a look around the [SocketPermission attribute](http://msdn.microsoft.com/en-gb/library/system.net.socketpermissionattribute.aspx) that [Cashley](http://stackoverflow.com/users/1566466/cashley) mentioned. Thanks, both! – Owen Blacker Feb 06 '13 at 10:58
  • @Cashley Thanks for your suggestion. I've taken a further look and the SocketPermission doesn't appear to be the problem; I've updated the question with more information. – Owen Blacker Feb 06 '13 at 12:53
  • @RichardSchneider Thanks for prompting me to check the user context. Unfortunately, that doesn't appear to be the issue; I've updated the question with more information. – Owen Blacker Feb 06 '13 at 12:53
  • @OwenBlacker Did you manage to try any test code in production using TcpClient? Just a simple `.Connect` call to the same port and IP might give you a more meaningful exception, judging by the [remarks](http://msdn.microsoft.com/en-gb/library/fkbhht5w.aspx) here. I also found [this page](http://mattmitchell.com.au/ftpwebrequest-is-broken/) listing some arguments against FtpWebRequest and a bunch of alternatives. I had a similar issue to you about five years in production with FtpWebRequest but I can't recall what the issue was in the end unfortunately. – Cashley Feb 06 '13 at 15:00
  • @OwenBlacker, did you get to solve this issue?. I am facing similar issue with exact same error – Shanadas Mar 13 '15 at 06:04
  • @Shanadas I'm afraid the client took the project to another agency, so we've never come back to this. It might be worth looking at Cashley's comments here, but I have no solution to this problem; sorry! – Owen Blacker Mar 13 '15 at 16:48
  • 1
    @Owen, thanks mine got fixed. The actual problem was with some DNS resolution. We were using hostname to frame FTP url. But there was some problem with DNS resolving this and hence caused this error – Shanadas Mar 16 '15 at 10:03

1 Answers1

3

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.

Bluesm
  • 31
  • 2