C# WebClient DownloadFile works only once - c#

I built a function that downloads a series of reports from my website (.NET webforms, old application), saves them as .html files in a temporary folder, zips them and return an archive to the user.
The application uses windows authentication and I managed to pass the current user credentials in the request by enabling
Credentials = CredentialCache.DefaultCredentials
Everything works seamlessly in my dev environment (both in IIS Express that on IIS), but on production server (Windows server 2008 R2, IIS 7.5) it only works if I limit the cycle to one iteration only.
It looks like the WebClient underlying connection remains open and the server refuses to open another one on the following cycle.
The error message I get is
The request was aborted: Could not create SSL/TLS secure channel.
and, enabling WCF tracing, I can narrow the issue to a "401 unauthorized" error.
Here's the significant part of my function:
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls |
SecurityProtocolType.Tls12 |
SecurityProtocolType.Tls11;
foreach (var project in list.Take(1)) //fails if I try list.Take(2) or more
{
using (WebClient client = new WebClient
{
Credentials = CredentialCache.DefaultCredentials
})
{
UriBuilder address = new UriBuilder
{
Scheme = Request.Url.Scheme,
Host = Request.Url.Host,
Port = Request.Url.Port,
Path = "/ERP_ProjectPrint.aspx",
Query = string.Format("bpId={0}&bpVid={1}", project.Id, project.VersionId)
};
string fileName = project.VersionProtocol + ".html";
client.DownloadFile(address.Uri.ToString(), tempFilePath + fileName);
}
}
Any hint about IIS settings I could tweak to solve this issue?

It looks like Dispose() is not working correct within the using() statement:
using (WebClient client = new WebClient())
{ ... }
Workaround without a using() statement:
WebClient client = new WebClient();
client.DownloadFileCompleted += OnDownloadFileCompleted;
when download is completed:
client.Dispose()
It works for me.

Try moving you loop into your request, like below. Does that make a difference?
Also don't think you need to use list.Take
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls |
SecurityProtocolType.Tls12 |
SecurityProtocolType.Tls11;
System.Net.ServicePointManager.DefaultConnectionLimit = 10
using (WebClient client = new WebClient
{
Credentials = CredentialCache.DefaultCredentials
})
{
foreach (var project in list)
{
UriBuilder address = new UriBuilder
{
Scheme = Request.Url.Scheme,
Host = Request.Url.Host,
Port = Request.Url.Port,
Path = "/ERP_ProjectPrint.aspx",
Query = string.Format("bpId={0}&bpVid={1}", project.Id, project.VersionId)
};
string fileName = project.VersionProtocol + ".html";
client.DownloadFileTaskAsync(address.Uri.ToString(), tempFilePath + fileName).Wait();
}
}
}

The issue has been solved by enabling TLS 1.2 on the server: it wasn't enabled by default. Refer to https://tecadmin.net/enable-tls-on-windows-server-and-iis/.
BE AWARE that enabling it may break the RDP connection functionality to your server.
Thanks to Jokies Ding for pointing me in the right direction (see comments above)

Related

FTP Error The remote server returned an error: 150 Opening data channel for file download

I have an FTPS site I need to connect to and get a file from. A vendor will be dropping a csv file there daily and I have to retrieve it and process it. My problem is no matter what I try and I can't connect to this site. I realize FTPS is different than SFTP and according to my research my normal method of getting files from FTP should work simply by adding an EnableSsl flag as seen below (ip, port, credentials have been changed obviously):
string uri = "ftp://127.0.0.1:123/";
string filename = "remoteFile.txt";
uri += filename;
var request = (FtpWebRequest)WebRequest.Create(uri);
request.Credentials = new NetworkCredential("user1", "secure-password1");
request.EnableSsl = true;
request.KeepAlive = false;
request.UseBinary = true;
request.UsePassive = true;
ServicePointManager.ServerCertificateValidationCallback = (s, certificate, chain, sslPolicyErrors) => true;
request.Method = WebRequestMethods.Ftp.DownloadFile;
var response = (FtpWebResponse)request.GetResponse(); //<-- error here
Stream responseStream = response.GetResponseStream();
var reader = new StreamReader(responseStream);
var fileContents = reader.ReadToEnd();
reader.Close();
response.Close();
string filePath = #"C:\Temp\localFile.txt";
using var stream = new StreamWriter(filePath);
{
stream.Write(fileContents);
}
I've tried variations of the 4 booleans I set on the request object. In this configuration I get the error in the title. If I switch passive to false I get a timeout. I can connect to this FTP site using WinSCP. There is a certificate on the site and I imported my connection configuration from a co-worker. There is an SHA-1 fingerprint.
I have also tried creating a connection with the WinSCP Nuget package and followed their example, I just can't seem to get the fingerprint correct:
var options = new SessionOptions
{
Protocol = Protocol.Ftp,
HostName = "ftp://127.0.0.1:21/",
UserName = "user1",
Password = "secure-password1",
SshHostKeyFingerprint = "???",
};
using var session = new WinSCP.Session();
session.Open(options);
No matter what I've tried in that finger print property it doesn't match the pattern they want and I can't find a good example of what it should look like. On the WinSCP page it says to obtain the fingerprint from your administrator, ours provided a certificate file that has an RSA section and a Certificate section. I've tried assigning the whole file to that field, the RSA section, certificate, nothing works. I tried the fingerprint displayed in my working session from WinSCP and that doesn't work.
I've found a few questions on this site with this error but all seem to point to server issues. I figure if I can connect and get files using WinSCP then I should be able to do it through code as well.
thanks
As Martin suggested I opened WinSCP, logged into my FTP site using the app. Then I clicked the Session menu and chose Generate URL/Code... Click the .NET assembly code tab at the top and pasted the code into my project
// Set up session options
SessionOptions sessionOptions = new SessionOptions
{
Protocol = Protocol.Ftp,
HostName = "127.0.0.1",
PortNumber = 21,
UserName = "user1",
Password = "secure-password1",
FtpSecure = FtpSecure.Explicit,
TlsHostCertificateFingerprint = "2f:f5:ab:e5:f7:27:65:12:30:73:3d:9a:b7:12:88:11:62:0e:6f:a1",
};
using (Session session = new Session())
{
// Connect
session.Open(sessionOptions);
// Your code
}
I hope this helps whoever is stuck on this like I was.

SharePointOnlineCredentials.GetAuthenticationCookie(Uri url) returns null

I have created a C# application which connects to SharePoint online site. When calling the SharePointOnlineCredentials.GetAuthenticationCookie(siteURI) method sometimes it is able to fetch the cookie successfully, but sometimes the result returned is null. Why does this happen?
string siteUrl = "https://mySite.sharepoint.com/sites/TestSite";
var clientContext = new ClientContext(siteUrl);
var pswd = new SecureString();
const string mypwd = "P#ssword";
foreach (var c in mypwd.ToCharArray())
{
pswd.AppendChar(c);
}
SharePointOnlineCredentials spCred = new SharePointOnlineCredentials("username", pswd);
clientContext.Credentials = spCred;
var cookie = spCred.GetAuthenticationCookie(new Uri(siteUrl));
Web web = clientContext.Web;
clientContext.Load(web);
clientContext.ExecuteQuery();
To me it seems like the user you are using cannot access to this site. It's maybe a user right problem.
I have tested the GetAuthenticationCookie method and it return null when the user has no access rights on the site.
If user has all right access. It could be because of deprecated security protocols. Try specifying the security protocol to TLS 1.2 version.
You may use the below code::
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 |
SecurityProtocolType.Tls11 |
SecurityProtocolType.Tls |
SecurityProtocolType.Ssl3;

400 Bad Request - Invalid Hostname when making nancy self hosted Web API calls locally

I'm self hosing a website in a windows service via nancy/owin. The site gets hosted fine, and I can hit the site in a browser both locally and from another machine. All calls to my Web API endpoints work just fine when interacting with the site.
The problem comes when trying to hit the Web API endpoints locally via another application - except when I connect with the IP address. For instance, I have a console application where I am making a simple GET request. If I run the app locally connecting with the machine name (base name or fully qualified) or localhost, I get 400 Bad Request - Invalid Hostname. If I do that same request using the IP address, it works fine. On the other hand, if I run the console app on another machine on the same domain, it will work regardless of which host name I use. Firewalls are off on both machines. What could be causing this?
Here is the code I am using to register my urls:
public void Start(int port = 1234)
{
// Get URIS with port num
var uris = GetUriParams(port);
// Add Host URLs
var startOptions = new Microsoft.Owin.Hosting.StartOptions();
foreach (var uri in uris)
startOptions.Urls.Add(uri.ToString());
// Start OWIN
this._owinApp = WebApp.Start<Startup>(startOptions);
}
private static Uri[] GetUriParams(int port)
{
var uriParams = new List<Uri>();
string hostName = Dns.GetHostName();
// Host name URI
string hostNameUri = string.Format("http://{0}:{1}", Dns.GetHostName(), port);
uriParams.Add(new Uri(hostNameUri));
// Host address URI(s)
var hostEntry = Dns.GetHostEntry(hostName);
foreach (var ipAddress in hostEntry.AddressList)
{
if (ipAddress.AddressFamily == AddressFamily.InterNetwork) // IPv4 addresses only
{
var addrBytes = ipAddress.GetAddressBytes();
string hostAddressUri = string.Format("http://{0}.{1}.{2}.{3}:{4}", addrBytes[0], addrBytes[1], addrBytes[2], addrBytes[3], port);
uriParams.Add(new Uri(hostAddressUri));
}
}
// Localhost URI
uriParams.Add(new Uri(string.Format("http://localhost:{0}", port)));
return uriParams.ToArray();
}
And here is the web request I am making in my console app:
var getDataFormatString = "http://{0}:{1}/API/MyService/GetData";
var fqdn = "mymachine";
var address = string.Format(getDataFormatString, fqdn, 1234);
var request = (HttpWebRequest)WebRequest.Create(new Uri(address));
request.Method = "GET";
request.Credentials = new NetworkCredential("username", "password", "domain");
var response = (HttpWebResponse)request.GetResponse();
Nancy may have changed since you posted this question, but in my experience this has to do with the HostConfiguration.RewriteLocalhost setting. When I set this to false I get the same behavior you see.
When HostConfiguration.RewriteLocalhost=true, then I also need to create a URL reservation using netsh http add urlacl url=http://+:port user=<appropriate-user>

c# Application cannot access the internet if I am connected to a VPN

I am working on an application that needs constant internet connectivity in order to function according to specifications.
The problem that I am facing is that if I connect to a VPN then the application is unable to access the internet at all.
Here is a part of the code where I try to check whether the server is reachable or not:
try
{
using (var client = new System.Net.WebClient())
{
//var networkCredentials = new NetworkCredential("shumais", "password");
//WebProxy myProxy = new WebProxy("192.168.0.61", 8080) { Credentials = networkCredentials };
//WebProxy myProxy = new WebProxy("192.168.0.61", 8080);
//client.Proxy = WebRequest.GetSystemWebProxy();
IWebProxy proxy = WebRequest.GetSystemWebProxy();
proxy.Credentials = CredentialCache.DefaultCredentials;
client.UseDefaultCredentials = true;
client.Proxy = proxy;
using (var stream = client.OpenRead(WebUrls.URL_BASE_REQUEST))
{
_isServerReachable = true;
}
}
}
catch (Exception)
{
_isServerReachable = false;
}
I have got the code working with/without proxies and now just need to get the application to work when I am connected to any VPNs.
Any help will be greatly appreciated, thanks.
If you are using a Microsoft PPTP VPN, you need to uncheck "Use default gateway on remote network" in the TCP/IPv4 advanced settings for the VPN connection.
Since you're trying to access the site with the default network credentials, make sure to add the default proxy to the app.config file, and add a cookie container (seriously sounds stupid but it looks like it's helping other people out too).
Check out my answer to this post: How to use IE proxy server settings and its credentials in .Net Application

setExpressCheckout and SSL/TLS error

I'm trying to develop a simple application that will enable users to purchase services off a website through the Paypal API. This application is running on ASP.NET with C#.
I have had very little luck trying to get the Paypal API to co-operate. The method I'm calling is SetExpressCheckout with all the appropriate variables.
I did my research and discovered that since I'm testing in Localhost, it may affect Paypal's ability to communicate with the application. So the next thing I tried was accessing my application through an open port and a publicly accessible IP address, but the same error occurs on the call to SetExpressCheckout.
Here is the error:
Exception Details: System.Net.WebException: The request was aborted: Could not create SSL/TLS secure channel.
Source Error:
Line 1790: [return: System.Xml.Serialization.XmlElementAttribute("SetExpressCheckoutResponse", Namespace="urn:ebay:api:PayPalAPI")]
Line 1791: public SetExpressCheckoutResponseType SetExpressCheckout([System.Xml.Serialization.XmlElementAttribute(Namespace="urn:ebay:api:PayPalAPI")] SetExpressCheckoutReq SetExpressCheckoutReq) {
Line 1792: object[] results = this.Invoke("SetExpressCheckout", new object[] {
Line 1793: SetExpressCheckoutReq});
Line 1794: return ((SetExpressCheckoutResponseType)(results[0]));
Source File: c:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\Temporary ASP.NET Files\anan_p2\730602d6\31a8d74e\App_WebReferences.c8vgyrf8.2.cs Line: 1792
I've also tried generating certificates using OpenSSL and uploading them to the Paypal account's encrypted seller option but still no effect.
Thank you very much for reading through my question!
Update: As requested here is the code being used.
String hostingOn = ConfigurationManager.AppSettings["default_site_url"];
reqDetails.ReturnURL = hostingOn + "marketplace_confirm.aspx";
reqDetails.CancelURL = hostingOn + "marketplace.aspx";
reqDetails.NoShipping = "1";
reqDetails.ReqConfirmShipping = "0";
reqDetails.OrderTotal = new BasicAmountType()
{
currencyID = CurrencyCodeType.CAD,
Value = payment_amt.Value,
};
SetExpressCheckoutReq req = new SetExpressCheckoutReq()
{
SetExpressCheckoutRequest = new SetExpressCheckoutRequestType()
{
Version = UtilPayPalAPI.Version,
SetExpressCheckoutRequestDetails = reqDetails
}
};
PayPalAPIAASoapBinding paypal = new PayPalAPIAASoapBinding();
paypal.SetExpressCheckout(req);
I am also using the https://api-aa-3t.paypal.com/2.0/ url for accessing the API
Since early 2016, Paypal started requiring TLS 1.2 protocol for communications in the Sandbox, and will enforce it for the live environment starting June 17. See here for reference.
In most .NET applications TLS 1.2 will come disabled by default, and therefore you'll need to enable it.
You need to add the following line, for example, at the beginning of you Application_Start method:
public class Site : HttpApplication
{
protected void Application_Start()
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
// other configuration
}
}
You're probably connecting to api.paypal.com or api.sandbox.paypal.com, and not sending along your API certificate. The API certificate is a client SSL certificate used to complete the SSL chain.
If you don't have or are not using an API certificate, you should connect to api-3t.paypal.com or api-3t.sandbox.paypal.com for Live or Sandbox respectively.
I've been working with a PayPal (NVP/Signature) Express Checkout integration and have been hit with this SSL/TLS error.
Nothing I did seemed to get around it but then I found the following code to add above my request. For reference, I'm using MVC3/.NET 4 so Tls1.2 isn't available to me by default (like in .NET 4.5 +). This first three lines of this code gets around that. I hope it helps people!
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072;
ServicePointManager.DefaultConnectionLimit = 9999;
var url = "https://[paypal-api-url]/nvp";
var uri = new Uri(url);
var request = WebRequest.Create(uri);
var encoding = new UTF8Encoding();
var requestData = encoding.GetBytes(data);
request.ContentType = "application/x-www-form-urlencoded";
request.Method = "POST";
request.Timeout = (300 * 1000);
request.ContentLength = requestData.Length;
using (var stream = request.GetRequestStream())
{
stream.Write(requestData, 0, requestData.Length);
}
var response = request.GetResponse();
...
Thanks a lot that really helps me.
For reference here is my code for establishing the interface in VB.NET
'Create a service Binding in code
Dim ppEndpointAddress As New System.ServiceModel.EndpointAddress("https://api-3t.sandbox.paypal.com/2.0/")
Dim ppBinding As New System.ServiceModel.BasicHttpBinding(System.ServiceModel.BasicHttpSecurityMode.Transport)
Dim ppIface As New PayPalAPI.PayPalAPIAAInterfaceClient(ppBinding, ppEndpointAddress)
Dim ppPaymentReq As New PayPalAPI.DoDirectPaymentReq()
ppPaymentReq.DoDirectPaymentRequest = ppRequest

Categories

Resources