#if SECURITY_DEP
+#if MONOTOUCH
+using Mono.Security.Protocol.Tls;
+using MSX = Mono.Security.X509;
+using Mono.Security.X509.Extensions;
+#else
extern alias MonoSecurity;
-
-using System.Text.RegularExpressions;
using MonoSecurity::Mono.Security.X509.Extensions;
using MonoSecurity::Mono.Security.Protocol.Tls;
using MSX = MonoSecurity::Mono.Security.X509;
#endif
+using System.Text.RegularExpressions;
+#endif
+
using System;
+using System.Threading;
using System.Collections;
+using System.Collections.Generic;
using System.Collections.Specialized;
using System.Configuration;
using System.Net.Configuration;
using System.Globalization;
using System.Net.Security;
+using System.Diagnostics;
//
// notes:
public class ServicePointManager {
class SPKey {
Uri uri; // schema/host/port
+ Uri proxy;
bool use_connect;
- public SPKey (Uri uri, bool use_connect) {
+ public SPKey (Uri uri, Uri proxy, bool use_connect) {
this.uri = uri;
+ this.proxy = proxy;
this.use_connect = use_connect;
}
get { return use_connect; }
}
+ public bool UsesProxy {
+ get { return proxy != null; }
+ }
+
public override int GetHashCode () {
- return uri.GetHashCode () + ((use_connect) ? 1 : 0);
+ int hash = 23;
+ hash = hash * 31 + ((use_connect) ? 1 : 0);
+ hash = hash * 31 + uri.GetHashCode ();
+ hash = hash * 31 + (proxy != null ? proxy.GetHashCode () : 0);
+ return hash;
}
public override bool Equals (object obj) {
return false;
}
- return (uri.Equals (other.uri) && other.use_connect == use_connect);
+ if (!uri.Equals (other.uri))
+ return false;
+ if (use_connect != other.use_connect || UsesProxy != other.UsesProxy)
+ return false;
+ if (UsesProxy && !proxy.Equals (other.proxy))
+ return false;
+ return true;
}
}
private static ICertificatePolicy policy = new DefaultCertificatePolicy ();
private static int defaultConnectionLimit = DefaultPersistentConnectionLimit;
- private static int maxServicePointIdleTime = 900000; // 15 minutes
+ private static int maxServicePointIdleTime = 100000; // 100 seconds
private static int maxServicePoints = 0;
private static bool _checkCRL = false;
private static SecurityProtocolType _securityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls;
if (address == null)
throw new ArgumentNullException ("address");
- RecycleServicePoints ();
+ if ((servicePoints.Count % 4) == 0)
+ RecycleServicePoints ();
var origAddress = new Uri (address.Scheme + "://" + address.Authority);
address = new Uri (address.Scheme + "://" + address.Authority);
ServicePoint sp = null;
+ SPKey key = new SPKey (origAddress, usesProxy ? address : null, useConnect);
lock (servicePoints) {
- SPKey key = new SPKey (origAddress, useConnect);
sp = servicePoints [key] as ServicePoint;
if (sp != null)
return sp;
// Internal Methods
- internal static void RecycleServicePoints ()
+ static void RecycleServicePoints ()
{
- ArrayList toRemove = new ArrayList ();
lock (servicePoints) {
+ var toRemove = new ArrayList ();
+ var idleList = new SortedDictionary<DateTime, ServicePoint> ();
IDictionaryEnumerator e = servicePoints.GetEnumerator ();
while (e.MoveNext ()) {
ServicePoint sp = (ServicePoint) e.Value;
- if (sp.AvailableForRecycling) {
+ DateTime idleSince;
+ if (sp.CheckAvailableForRecycling (out idleSince)) {
toRemove.Add (e.Key);
+ continue;
}
+
+ while (idleList.ContainsKey (idleSince))
+ idleSince = idleSince.AddMilliseconds (1);
+ idleList.Add (idleSince, sp);
}
for (int i = 0; i < toRemove.Count; i++)
return;
// get rid of the ones with the longest idle time
- SortedList list = new SortedList (servicePoints.Count);
- e = servicePoints.GetEnumerator ();
- while (e.MoveNext ()) {
- ServicePoint sp = (ServicePoint) e.Value;
- if (sp.CurrentConnections == 0) {
- while (list.ContainsKey (sp.IdleSince))
- sp.IdleSince = sp.IdleSince.AddMilliseconds (1);
- list.Add (sp.IdleSince, sp.Address);
- }
+ foreach (var sp in idleList.Values) {
+ if (servicePoints.Count <= maxServicePoints)
+ break;
+ servicePoints.Remove (sp);
}
-
- for (int i = 0; i < list.Count && servicePoints.Count > maxServicePoints; i++)
- servicePoints.Remove (list.GetByIndex (i));
}
}
#if SECURITY_DEP
internal class ChainValidationHelper {
object sender;
string host;
+ RemoteCertificateValidationCallback cb;
#if !MONOTOUCH
static bool is_macosx = System.IO.File.Exists (OSX509Certificates.SecurityLibrary);
}
#endif
- public ChainValidationHelper (object sender)
+ public ChainValidationHelper (object sender, string hostName)
{
this.sender = sender;
+ host = hostName;
}
- public string Host {
+ public RemoteCertificateValidationCallback ServerCertificateValidationCallback {
get {
- if (host == null && sender is HttpWebRequest)
- host = ((HttpWebRequest) sender).Address.Host;
- return host;
+ if (cb == null)
+ cb = ServicePointManager.ServerCertificateValidationCallback;
+ return cb;
}
-
- set { host = value; }
+ set { cb = value; }
}
// Used when the obsolete ICertificatePolicy is set to DefaultCertificatePolicy
return null;
ICertificatePolicy policy = ServicePointManager.CertificatePolicy;
- RemoteCertificateValidationCallback cb = ServicePointManager.ServerCertificateValidationCallback;
X509Certificate2 leaf = new X509Certificate2 (certs [0].RawData);
int status11 = 0; // Error code passed to the obsolete ICertificatePolicy callback
// the certificates that the server provided (which generally does not include the root) so, only
// if there's a user callback, we'll create the X509Chain but won't build it
// ref: https://bugzilla.xamarin.com/show_bug.cgi?id=7245
- if (cb != null) {
+ if (ServerCertificateValidationCallback != null) {
#endif
chain = new X509Chain ();
chain.ChainPolicy = new X509ChainPolicy ();
status11 = -2146762490; //CERT_E_PURPOSE 0x800B0106
}
- if (!CheckServerIdentity (certs [0], Host)) {
+ if (!CheckServerIdentity (certs [0], host)) {
errors |= SslPolicyErrors.RemoteCertificateNameMismatch;
status11 = -2146762481; // CERT_E_CN_NO_MATCH 0x800B010F
}
// Ideally we should return the SecTrustResult
OSX509Certificates.SecTrustResult trustResult = OSX509Certificates.SecTrustResult.Deny;
try {
- trustResult = OSX509Certificates.TrustEvaluateSsl (certs, Host);
+ trustResult = OSX509Certificates.TrustEvaluateSsl (certs, host);
// We could use the other values of trustResult to pass this extra information
// to the .NET 2 callback for values like SecTrustResult.Confirm
result = (trustResult == OSX509Certificates.SecTrustResult.Proceed ||
}
#endif
-#if MONODROID
+#if MONODROID && SECURITY_DEP
result = AndroidPlatform.TrustEvaluateSsl (certs, sender, leaf, chain, errors);
if (result) {
// chain.Build() + GetErrorsFromChain() (above) will ALWAYS fail on
user_denied = !result && !(policy is DefaultCertificatePolicy);
}
// If there's a 2.0 callback, it takes precedence
- if (cb != null) {
- result = cb (sender, leaf, chain, errors);
+ if (ServerCertificateValidationCallback != null) {
+ result = ServerCertificateValidationCallback (sender, leaf, chain, errors);
user_denied = !result;
}
return new ValidationResult (result, user_denied, status11);