89f94e57088c37b5862e70e56aa1ef5cabdcb6f2
[mono.git] / mcs / class / referencesource / System / net / System / Net / ServicePoint.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="ServicePoint.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
6
7 namespace System.Net {
8     using System.Net.Sockets;
9     using System.Collections;
10     using System.IO;
11     using System.Threading;
12     using System.Security.Authentication.ExtendedProtection;
13     using System.Security.Cryptography.X509Certificates;
14     using System.Net.Security;
15     using System.Globalization;
16     using System.Collections.Generic;
17     using System.Runtime.CompilerServices;
18
19     public delegate IPEndPoint BindIPEndPoint(ServicePoint servicePoint, IPEndPoint remoteEndPoint, int retryCount);
20
21
22     // ServicePoints are never created directly but always handed out by the
23     // ServicePointManager. The ServicePointManager and the ServicePoints must be in
24     // the same name space so that the ServicePointManager can call the
25     // internal constructor
26
27     /// <devdoc>
28     ///    <para>Provides connection management for other classes.</para>
29     /// </devdoc>
30     [FriendAccessAllowed]
31     public class ServicePoint {
32
33         internal const int LoopbackConnectionLimit = Int32.MaxValue;
34
35         private int                 m_ConnectionLeaseTimeout;
36         private TimerThread.Queue   m_ConnectionLeaseTimerQueue;
37         private bool                m_ProxyServicePoint;
38         private bool                m_UserChangedLimit;
39         private bool                m_UseNagleAlgorithm;
40         private TriState            m_HostLoopbackGuess;
41         private int                 m_ReceiveBufferSize;
42         private bool                m_Expect100Continue;
43         private bool                m_Understands100Continue;
44         private HttpBehaviour       m_HttpBehaviour;
45         private string              m_LookupString;
46         private int                 m_ConnectionLimit;
47         private Hashtable           m_ConnectionGroupList;
48         private Uri                 m_Address;
49         private string              m_Host;
50         private int                 m_Port;
51         private TimerThread.Queue   m_IdlingQueue;
52         private TimerThread.Timer   m_ExpiringTimer;
53         private DateTime            m_IdleSince;
54         private string              m_ConnectionName;
55         private int                 m_CurrentConnections;
56         private bool                m_HostMode;
57         private BindIPEndPoint      m_BindIPEndPointDelegate = null;
58         private object              m_CachedChannelBinding;
59
60         private static readonly AsyncCallback m_ConnectCallbackDelegate = new AsyncCallback(ConnectSocketCallback);
61
62         private readonly TimerThread.Callback m_IdleConnectionGroupTimeoutDelegate;
63
64 #if !FEATURE_PAL
65         private object m_ServerCertificateOrBytes;
66         private object m_ClientCertificateOrBytes;
67 #endif // !FEATURE_PAL
68
69         private bool m_UseTcpKeepAlive = false;
70         private int m_TcpKeepAliveTime;
71         private int m_TcpKeepAliveInterval;
72
73         internal string LookupString {
74             get {
75                 return m_LookupString;
76             }
77         }
78
79         internal string Hostname {
80             get {
81                 return m_HostName;
82             }
83         }
84
85         internal bool IsTrustedHost {
86             get {
87                 return m_IsTrustedHost;
88             }
89         }
90
91         public BindIPEndPoint BindIPEndPointDelegate {
92             get {
93                 return m_BindIPEndPointDelegate;
94             }
95             set {
96                 ExceptionHelper.InfrastructurePermission.Demand();
97                 m_BindIPEndPointDelegate = value;
98             }
99         }
100
101         //
102         // constructors
103         //
104         internal ServicePoint(Uri address, TimerThread.Queue defaultIdlingQueue, int defaultConnectionLimit, string lookupString, bool userChangedLimit, bool proxyServicePoint) {
105             GlobalLog.Print("ServicePoint#" + ValidationHelper.HashString(this) + "::.ctor(" + lookupString+")");
106             if (Logging.On) Logging.Enter(Logging.Web, this, "ServicePoint", address.DnsSafeHost + ":" + address.Port);
107
108             m_ProxyServicePoint     = proxyServicePoint;
109             m_Address               = address;
110             m_ConnectionName        = address.Scheme;
111             m_Host                  = address.DnsSafeHost;
112             m_Port                  = address.Port;
113             m_IdlingQueue           = defaultIdlingQueue;
114             m_ConnectionLimit       = defaultConnectionLimit;
115             m_HostLoopbackGuess     = TriState.Unspecified;
116             m_LookupString          = lookupString;
117             m_UserChangedLimit      = userChangedLimit;
118             m_UseNagleAlgorithm     = ServicePointManager.UseNagleAlgorithm;
119             m_Expect100Continue     = ServicePointManager.Expect100Continue;
120             m_ConnectionGroupList   = new Hashtable(10);
121             m_ConnectionLeaseTimeout = System.Threading.Timeout.Infinite;
122             m_ReceiveBufferSize     = -1;
123             m_UseTcpKeepAlive       = ServicePointManager.s_UseTcpKeepAlive;
124             m_TcpKeepAliveTime      = ServicePointManager.s_TcpKeepAliveTime;
125             m_TcpKeepAliveInterval  = ServicePointManager.s_TcpKeepAliveInterval;
126
127             // it would be safer to make sure the server is 1.1
128             // but assume it is at the beginning, and update it later
129             m_Understands100Continue = true;
130             m_HttpBehaviour         = HttpBehaviour.Unknown;
131
132             // upon creation, the service point should be idle, by default
133             m_IdleSince             = DateTime.Now;
134             m_ExpiringTimer         = m_IdlingQueue.CreateTimer(ServicePointManager.IdleServicePointTimeoutDelegate, this);
135             m_IdleConnectionGroupTimeoutDelegate = new TimerThread.Callback(IdleConnectionGroupTimeoutCallback);
136         }
137
138
139
140         internal ServicePoint(string host, int port, TimerThread.Queue defaultIdlingQueue, int defaultConnectionLimit, string lookupString, bool userChangedLimit, bool proxyServicePoint) {
141             GlobalLog.Print("ServicePoint#" + ValidationHelper.HashString(this) + "::.ctor(" + lookupString+")");
142             if (Logging.On) Logging.Enter(Logging.Web, this, "ServicePoint", host + ":" + port);
143             
144             m_ProxyServicePoint     = proxyServicePoint;
145             m_ConnectionName        = "ByHost:"+host+":"+port.ToString(CultureInfo.InvariantCulture);
146             m_IdlingQueue           = defaultIdlingQueue;
147             m_ConnectionLimit       = defaultConnectionLimit;
148             m_HostLoopbackGuess     = TriState.Unspecified;
149             m_LookupString          = lookupString;
150             m_UserChangedLimit      = userChangedLimit;
151             m_ConnectionGroupList   = new Hashtable(10);
152             m_ConnectionLeaseTimeout = System.Threading.Timeout.Infinite;
153             m_ReceiveBufferSize     = -1;
154             m_Host = host;
155             m_Port = port;
156             m_HostMode = true;
157
158             // upon creation, the service point should be idle, by default
159             m_IdleSince             = DateTime.Now;
160             m_ExpiringTimer         = m_IdlingQueue.CreateTimer(ServicePointManager.IdleServicePointTimeoutDelegate, this);
161             m_IdleConnectionGroupTimeoutDelegate = new TimerThread.Callback(IdleConnectionGroupTimeoutCallback);
162         }
163
164
165
166         // methods
167
168         internal object CachedChannelBinding
169         {
170             get { return m_CachedChannelBinding; }
171         }
172
173         internal void SetCachedChannelBinding(Uri uri, ChannelBinding binding)
174         {
175             if (uri.Scheme == Uri.UriSchemeHttps)
176             {
177                 m_CachedChannelBinding = (binding != null ? (object)binding : (object)DBNull.Value);
178             }
179         }
180
181         /*++
182
183             FindConnectionGroup       -
184
185             Searches for the a Group object that actually holds the connections
186               that we want to peak at.
187
188
189             Input:
190                     request                 - Request that's being submitted.
191                     connName                - Connection Name if needed
192
193             Returns:
194                     ConnectionGroup
195
196         --*/
197
198         private ConnectionGroup FindConnectionGroup(string connName, bool dontCreate) {
199             string lookupStr = ConnectionGroup.MakeQueryStr(connName);
200
201             GlobalLog.Print("ServicePoint#" + ValidationHelper.HashString(this) + "::FindConnectionGroup() lookupStr:[" + ValidationHelper.ToString(connName) + "]");
202
203             ConnectionGroup entry = m_ConnectionGroupList[lookupStr] as ConnectionGroup;
204
205             if (entry==null && !dontCreate) {
206                 entry = new ConnectionGroup(this, connName);
207                 GlobalLog.Print("ServicePoint#" + ValidationHelper.HashString(this) + "::FindConnectionGroup() adding ConnectionGroup lookupStr:[" + lookupStr + "]");
208
209                 m_ConnectionGroupList[lookupStr] = entry;
210             }
211             else {
212                 GlobalLog.Print("ServicePoint#" + ValidationHelper.HashString(this) + "::FindConnectionGroup() using existing ConnectionGroup");
213             }
214             GlobalLog.Print("ServicePoint#" + ValidationHelper.HashString(this) + "::FindConnectionGroup() returning ConnectionGroup:" + ValidationHelper.ToString(entry) + (entry!=null ? " ConnLimit:" + entry.ConnectionLimit.ToString() : ""));
215             return entry;
216         }
217
218
219         /// <devdoc>
220         ///    <para>
221         ///     Tempory for getting a new Connection for FTP client, for the time being
222         ///    </para>
223         /// </devdoc>
224         internal Socket GetConnection(PooledStream PooledStream, object owner, bool async, out IPAddress address, ref Socket abortSocket, ref Socket abortSocket6)
225         {
226             Socket socket = null;
227             Socket socket6 = null;
228             Socket finalSocket = null;
229             Exception innerException = null;
230             WebExceptionStatus ws = WebExceptionStatus.ConnectFailure;
231             address = null;
232
233             //
234             // if we will not create a tunnel through a proxy then create
235             // and connect the socket we will use for the connection
236             //
237
238             //
239             // IPv6 Support: If IPv6 is enabled, then we create a second socket that ServicePoint
240             //               will use if it wants to connect via IPv6.
241             //
242             if ( Socket.OSSupportsIPv4 ) {
243                 socket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
244             }
245
246             if ( Socket.OSSupportsIPv6 ) {
247                 socket6 = new Socket(AddressFamily.InterNetworkV6,SocketType.Stream,ProtocolType.Tcp);
248             }
249
250             abortSocket = socket;
251             abortSocket6 = socket6;
252
253             //
254             // Setup socket timeouts for sync requests
255             //
256             // 
257
258             ConnectSocketState state = null;
259
260             if (async) {
261                 state = new ConnectSocketState(this, PooledStream, owner, socket, socket6);
262             }
263
264             ws = ConnectSocket(socket, socket6, ref finalSocket, ref address, state, out innerException);
265
266             if (ws == WebExceptionStatus.Pending) {
267                 return null;
268             }
269
270             if (ws != WebExceptionStatus.Success) {
271                 throw new WebException(
272                     NetRes.GetWebStatusString(ws),
273                     ws == WebExceptionStatus.ProxyNameResolutionFailure || ws == WebExceptionStatus.NameResolutionFailure ? Host : null,
274                     innerException,
275                     ws,
276                     null, /* no response */
277                     WebExceptionInternalStatus.ServicePointFatal);
278             }
279
280             //
281             // There should be no means for socket to be null at this
282             // point, but the damage is greater if we just carry on
283             // without ensuring that it's good.
284             //
285             if ( finalSocket == null ) {
286                 throw new IOException(SR.GetString(SR.net_io_transportfailure));
287             }
288
289             CompleteGetConnection(socket, socket6, finalSocket, address);
290             return finalSocket;
291         }
292
293         /// <devdoc>
294         ///    <para>
295         ///     Complete the GetConnection(...) call, the function was divided for async completion
296         ///    </para>
297         /// </devdoc>
298         private void CompleteGetConnection(Socket socket, Socket socket6, Socket finalSocket, IPAddress address) {
299             //
300             // Decide which socket to retain
301             //
302             if ( finalSocket.AddressFamily == AddressFamily.InterNetwork ) {
303                 if ( socket6 != null ) {
304                     socket6.Close();
305                     socket6 = null;
306                 }
307             }
308             else {
309                 if (socket != null) {
310                     socket.Close();
311                     socket = null;
312                 }
313             }
314
315             // make this configurable from the user:
316             if (!UseNagleAlgorithm) {
317                 finalSocket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, 1);
318             }
319             if (ReceiveBufferSize != -1) {
320                 finalSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveBuffer, ReceiveBufferSize);
321             }
322             if (m_UseTcpKeepAlive) {
323                 
324                 // Marshal a byte array containing the params for WsaIoctl
325                 // struct tcp_keepalive {
326                 //    u_long  onoff;
327                 //    u_long  keepalivetime;
328                 //    u_long  keepaliveinterval;
329                 // };
330                 byte[] input = new byte[sizeof(int)*3];
331                 input[0]  = 1; 
332                 input[4]  = (byte)  (m_TcpKeepAliveTime & 0xff);
333                 input[5]  = (byte) ((m_TcpKeepAliveTime >>  8) & 0xff);
334                 input[6]  = (byte) ((m_TcpKeepAliveTime >> 16) & 0xff);
335                 input[7]  = (byte) ((m_TcpKeepAliveTime >> 24) & 0xff);
336                 input[8]  = (byte)  (m_TcpKeepAliveInterval & 0xff);
337                 input[9]  = (byte) ((m_TcpKeepAliveInterval >>  8) & 0xff);
338                 input[10] = (byte) ((m_TcpKeepAliveInterval >> 16) & 0xff);
339                 input[11] = (byte) ((m_TcpKeepAliveInterval >> 24) & 0xff);
340
341                 // do WSAIoctl call
342                 finalSocket.IOControl(
343                                 IOControlCode.KeepAliveValues,
344                                 input,
345                                 null);
346             }
347             
348             
349             //return CreateConnection(NetworkStream stream, IPAddress address);
350             //return new NetworkStream(finalSocket, true);
351         }
352
353
354         /*++
355
356             SubmitRequest       - Submit a request for sending.
357
358             The service point submit handler. This is called when a request needs
359             to be submitted to the network. This routine is asynchronous; the caller
360             passes in an HttpSubmitDelegate that is invoked when the caller
361             can use the underlying network. The delegate is invoked with the
362             stream that it can write to.
363
364
365             In this version, we use HttpWebRequest. In the future we use IRequest
366
367             Input:
368                     Request                 - Request that's being submitted.
369                     SubmitDelegate          - Delegate to be invoked.
370
371             Returns:
372                     Nothing.
373
374         --*/
375
376         internal virtual void SubmitRequest(HttpWebRequest request) {
377             SubmitRequest(request, null);
378         }
379
380         // userReqeustThread says whether we can post IO from this thread or not.
381         internal void SubmitRequest(HttpWebRequest request, string connName)
382         {
383             //
384             // We attempt to locate a free connection sitting on our list
385             //  avoiding multiple loops of the same the list.
386             //  We do this, by enumerating the list of the connections,
387             //   looking for Free items, and the least busy item
388             //
389             Connection connToUse;
390             ConnectionGroup connGroup;
391             bool forcedsubmit = false;
392             lock(this) {
393                 GlobalLog.Print("ServicePoint#" + ValidationHelper.HashString(this) + "::SubmitRequest() Finding ConnectionGroup:[" + connName + "]");
394                 connGroup = FindConnectionGroup(connName, false);
395                 GlobalLog.Assert(connGroup != null, "ServicePoint#{0}::SubmitRequest()|connGroup == null", ValidationHelper.HashString(this));
396             }
397
398             do {
399                 connToUse = connGroup.FindConnection(request, connName, out forcedsubmit);
400                 // The request could be already aborted
401                 if (connToUse == null)
402                     return;
403
404                 GlobalLog.Print("ServicePoint#" + ValidationHelper.HashString(this) + "::SubmitRequest() Using Connection#" + ValidationHelper.HashString(connToUse));
405                 // finally sumbit delegate
406                 if (connToUse.SubmitRequest(request, forcedsubmit)) {
407                     break;
408                 }
409             } while (true);
410         }
411
412         // properties
413
414         /// <devdoc>
415         ///    <para>
416         ///       Gets and sets timeout for when connections should be recycled.
417         ///    </para>
418         /// </devdoc>
419         public int ConnectionLeaseTimeout {
420             get {
421                 return m_ConnectionLeaseTimeout;
422             }
423             set {
424                 if ( !ValidationHelper.ValidateRange(value, Timeout.Infinite, Int32.MaxValue)) {
425                     throw new ArgumentOutOfRangeException("value");
426                 }
427                 if (value != m_ConnectionLeaseTimeout) {
428                     m_ConnectionLeaseTimeout = value;
429                     m_ConnectionLeaseTimerQueue = null;
430                 }
431             }
432         }
433
434         /// <summary>
435         /// <para>Returns a timer queue that can be used internally to create timers of
436         /// ConnectionLeaseTimeout duration.</para>
437         /// </summary>
438         internal TimerThread.Queue ConnectionLeaseTimerQueue {
439             get {
440                 TimerThread.Queue queue = m_ConnectionLeaseTimerQueue;
441                 if (queue == null) {
442                     queue = TimerThread.GetOrCreateQueue(ConnectionLeaseTimeout);
443                     m_ConnectionLeaseTimerQueue = queue;
444                 }
445                 return m_ConnectionLeaseTimerQueue;
446             }
447         }
448
449         // Only the scheme and hostport, for example http://www.microsoft.com
450         /// <devdoc>
451         ///    <para>
452         ///       Gets the Uniform Resource Identifier of the <see cref='System.Net.ServicePoint'/>.
453         ///    </para>
454         /// </devdoc>
455         public Uri Address {
456             get {
457                 if(m_HostMode){
458                     throw new NotSupportedException(SR.GetString(SR.net_servicePointAddressNotSupportedInHostMode));
459                 }
460
461                 // Don't let low-trust apps discover the proxy information.
462                 if (m_ProxyServicePoint)
463                 {
464                     ExceptionHelper.WebPermissionUnrestricted.Demand();
465                 }
466
467                 return m_Address;
468             }
469         }
470
471         internal Uri InternalAddress
472         {
473             get
474             {
475                 GlobalLog.Assert(!m_HostMode, "ServicePoint#{0}::InternalAddress|Can't be used in Host Mode.", ValidationHelper.HashString(this));
476                 return m_Address;
477             }
478         }
479
480         internal string Host {
481             get {
482                 if(m_HostMode){
483                     return m_Host;
484                 }
485                 return m_Address.Host;
486             }
487         }
488
489         internal int Port {
490             get {
491                 return m_Port;
492             }
493         }
494
495
496         //
497         // Gets or sets the maximum idle time allowed for connections of this ServicePoint and then for ServicePoint itself
498         // Default value coming in ctor is ServicePointManager.s_MaxServicePointIdleTime which 100 sec
499         //
500         public int MaxIdleTime {
501             get {
502                 return m_IdlingQueue.Duration;
503             }
504             set {
505                 if ( !ValidationHelper.ValidateRange(value, Timeout.Infinite, Int32.MaxValue)) {
506                     throw new ArgumentOutOfRangeException("value");
507                 }
508
509                 // Already set?
510                 if (value == m_IdlingQueue.Duration)
511                     return;
512
513                 lock(this) {
514                     // Make sure we can cancel the existing one.  If not, we already idled out.
515                     if (m_ExpiringTimer == null || m_ExpiringTimer.Cancel())
516                     {
517                         m_IdlingQueue = TimerThread.GetOrCreateQueue(value);
518                         if (m_ExpiringTimer != null)
519                         {
520                             // Need to create a one-off timer for the remaining period.
521                             double elapsedDouble = (DateTime.Now - m_IdleSince).TotalMilliseconds;
522                             int elapsed = elapsedDouble >= (double) Int32.MaxValue ? Int32.MaxValue : (int) elapsedDouble;
523                             int timeLeft = value == Timeout.Infinite ? Timeout.Infinite : elapsed >= value ? 0 : value - elapsed;
524                             m_ExpiringTimer = TimerThread.CreateQueue(timeLeft).CreateTimer(ServicePointManager.IdleServicePointTimeoutDelegate, this);
525                         }
526                     }
527                 }
528             }
529         }
530
531         /// <devdoc>
532         ///    <para>
533         ///       Gets or sets the Nagling algorithm on the connections that are created to this <see cref='System.Net.ServicePoint'/>.
534         ///       Changing this value does not affect existing connections but only to new ones that are created from that moment on.
535         ///    </para>
536         /// </devdoc>
537         public bool UseNagleAlgorithm {
538             get {
539                 return m_UseNagleAlgorithm;
540             }
541             set {
542                 m_UseNagleAlgorithm = value;
543             }
544         }
545
546         /// <devdoc>
547         ///    <para>
548         ///       Gets and sets the socket's receive buffer size.
549         ///    </para>
550         /// </devdoc>
551         public int ReceiveBufferSize {
552             get {
553                 return m_ReceiveBufferSize;
554             }
555             set {
556                 if ( !ValidationHelper.ValidateRange(value, -1, Int32.MaxValue)) {
557                     throw new ArgumentOutOfRangeException("value");
558                 }
559                 m_ReceiveBufferSize = value;
560             }
561
562         }
563
564
565         /// <devdoc>
566         ///    <para>
567         ///       Gets or sets indication whether 100-continue behaviour is desired when using this <see cref='System.Net.ServicePoint'/>.
568         ///       Changing this value does not affect existing connections but only to new ones that are created from that moment on.
569         ///    </para>
570         /// </devdoc>
571         public bool Expect100Continue {
572             set {
573                 m_Expect100Continue = value;
574             }
575             get {
576                 return m_Expect100Continue;
577             }
578         }
579
580         /// <devdoc>
581         ///    <para>
582         ///       Gets the date/time that the <see cref='System.Net.ServicePoint'/> went idle.
583         ///    </para>
584         /// </devdoc>
585         public DateTime IdleSince {
586             get {
587                 return m_IdleSince;
588             }
589         }
590
591         // HTTP Server Version
592         /// <devdoc>
593         ///    <para>
594         ///       The version of the protocol being used on this <see cref='System.Net.ServicePoint'/>.
595         ///    </para>
596         /// </devdoc>
597         public virtual Version ProtocolVersion {
598             get {
599                 return (m_HttpBehaviour>HttpBehaviour.HTTP10 || m_HttpBehaviour == HttpBehaviour.Unknown) ? HttpVersion.Version11 : HttpVersion.Version10;
600             }
601         }
602
603         // Contains set accessor for Version property. Version is a read-only
604         // property at the API
605         internal HttpBehaviour HttpBehaviour {
606             get {
607                 return m_HttpBehaviour;
608             }
609             set {
610                 m_HttpBehaviour = value;
611                 //
612                 // if version is greater than HTTP/1.1, and server undesrtood
613                 // 100 Continue so far, keep expecting it.
614                 //
615                 m_Understands100Continue = m_Understands100Continue && (m_HttpBehaviour>HttpBehaviour.HTTP10 || m_HttpBehaviour == HttpBehaviour.Unknown);
616             }
617         }
618
619         /// <devdoc>
620         ///    <para>
621         ///       Gets the connection name established by the <see cref='System.Net.WebRequest'/> that created the connection.
622         ///    </para>
623         /// </devdoc>
624         public string ConnectionName {
625             get {
626                 return m_ConnectionName;
627             }
628         }
629
630         /*
631         /// <devdoc>
632         ///    Gets the connection mode in use by the <see cref='System.Net.ServicePoint'/>. One of the <see cref='System.Net.ConnectionModes'/>
633         ///    values.
634         /// </devdoc>
635         internal ConnectionModes ConnectionMode {
636             get {
637                 return m_HttpBehaviour>=HttpBehaviour.HTTP11 ? ConnectionModes.Pipeline : ConnectionModes.Persistent;
638             }
639         }
640         */
641
642         /// <devdoc>
643         ///     Removes the specified Connection group from the ServicePoint, destroys safe and unsafe groups, but not internal.
644         /// </devdoc>
645
646         public bool CloseConnectionGroup(string connectionGroupName) {
647             GlobalLog.Enter("ServicePoint#" + ValidationHelper.HashString(this) + "::CloseConnectionGroup() lookupStr:[" + connectionGroupName + "]");
648             if ( ReleaseConnectionGroup(HttpWebRequest.GenerateConnectionGroup(connectionGroupName, false, false).ToString())  ||
649                  ReleaseConnectionGroup(HttpWebRequest.GenerateConnectionGroup(connectionGroupName, true, false).ToString())  ||
650                  ConnectionPoolManager.RemoveConnectionPool(this, connectionGroupName)) {
651
652                 GlobalLog.Leave("ServicePoint#" + ValidationHelper.HashString(this) + "::CloseConnectionGroup()","true");
653                 return true;
654             }
655             GlobalLog.Leave("ServicePoint#" + ValidationHelper.HashString(this) + "::CloseConnectionGroup()","false");
656             return false;
657         }
658
659         internal void CloseConnectionGroupInternal(string connectionGroupName) {
660             
661             // Release all internal connection groups (both 'safe' and 'unsafe') with the given name. We're not 
662             // interested in the result value (it's OK if it is 'false', i.e. not found).
663             // We don't need to call ConnectionPoolManager.RemoveConnectionPool() since this method is only used for
664             // HTTP.
665             string connectionGroupPrefixSafe = HttpWebRequest.GenerateConnectionGroup(connectionGroupName, false, true).ToString();
666             string connectionGroupPrefixUnsafe = HttpWebRequest.GenerateConnectionGroup(connectionGroupName, true, true).ToString();
667             List<string> connectionGroupNames = null;
668
669             lock (this) {                
670                 // Find all connecion groups starting with the provided prefix. We just compare prefixes, since connection 
671                 // groups may include suffixes for client certificates, SSL over proxy, authentication IDs.
672                 foreach (var item in m_ConnectionGroupList.Keys) {                    
673                     string current = item as string;
674                     if (current.StartsWith(connectionGroupPrefixSafe, StringComparison.Ordinal) || 
675                         current.StartsWith(connectionGroupPrefixUnsafe, StringComparison.Ordinal)) {
676                         if (connectionGroupNames == null) {
677                             connectionGroupNames = new List<string>();
678                         }
679                         connectionGroupNames.Add(current);
680                     }
681                 }
682             }
683
684             // If this service point contains any connection groups with the provided prefix, remove them.
685             if (connectionGroupNames != null) {
686                 foreach (string item in connectionGroupNames) {
687                     ReleaseConnectionGroup(item);
688                 }
689             }
690         }
691
692         /// <devdoc>
693         ///    <para>
694         ///       Gets or sets the maximum number of connections allowed on this <see cref='System.Net.ServicePoint'/>.
695         ///    </para>
696         /// </devdoc>
697         public int ConnectionLimit
698         {
699             get
700             {
701                 // If there hasn't been a DNS resolution yet, make a guess based on the host name.  It might change
702                 // when DNS is finally done, but that's ok.  It can change anyway based on other factors like redirects.
703                 if (!m_UserChangedLimit && m_IPAddressInfoList == null && m_HostLoopbackGuess == TriState.Unspecified)
704                 {
705                     // This can only happen the first time through, and before any ConnectionGroups are made.
706                     lock (this)
707                     {
708                         if (!m_UserChangedLimit && m_IPAddressInfoList == null && m_HostLoopbackGuess == TriState.Unspecified)
709                         {
710                             // First check if it's just an IP address anyway.
711                             IPAddress addr = null;
712                             if (IPAddress.TryParse(m_Host,out addr))
713                             {
714                                 m_HostLoopbackGuess = IsAddressListLoopback(new IPAddress[] { addr }) ? TriState.True : TriState.False;
715                             }
716                             else
717                             {
718                                 m_HostLoopbackGuess = NclUtilities.GuessWhetherHostIsLoopback(m_Host) ? TriState.True : TriState.False;
719                             }
720                         }
721                     }
722                 }
723
724                 return m_UserChangedLimit || (m_IPAddressInfoList == null ? m_HostLoopbackGuess != TriState.True : !m_IPAddressesAreLoopback) ? m_ConnectionLimit : LoopbackConnectionLimit;
725             }
726
727             set
728             {
729                 if (value <= 0)
730                 {
731                     throw new ArgumentOutOfRangeException("value");
732                 }
733
734                 if (!m_UserChangedLimit || m_ConnectionLimit != value)
735                 {
736                     lock (this)
737                     {
738                         if (!m_UserChangedLimit || m_ConnectionLimit != value)
739                         {
740                             m_ConnectionLimit = value;
741                             m_UserChangedLimit = true;
742
743                             // Don't want to call ResolveConnectionLimit() or ConnectionLimit before setting m_UserChangedLimit
744                             // in order to avoid the 'guess' logic in ConnectionLimit.
745                             ResolveConnectionLimit();
746                         }
747                     }
748                 }
749             }
750         }
751
752         // Must be called under lock.
753         private void ResolveConnectionLimit()
754         {
755             int limit = ConnectionLimit;
756             foreach (ConnectionGroup cg in m_ConnectionGroupList.Values)
757             {
758                 cg.ConnectionLimit = limit;
759             }
760         }
761
762         /// <devdoc>
763         ///    <para>
764         ///       Gets the current number of connections associated with this
765         ///    <see cref='System.Net.ServicePoint'/>.
766         ///    </para>
767         /// </devdoc>
768         public int CurrentConnections {
769             get {
770                 int connections = 0;
771                 lock(this)
772                 {
773                     foreach (ConnectionGroup group in m_ConnectionGroupList.Values)
774                     {
775                         connections += group.CurrentConnections;
776                     }
777                 }
778                 return connections;
779             }
780         }
781
782 #if !FEATURE_PAL
783         /// <devdoc>
784         ///    <para>
785         ///       Gets the certificate received for this <see cref='System.Net.ServicePoint'/>.
786         ///    </para>
787         /// </devdoc>
788         public  X509Certificate Certificate {
789             get {
790                     object chkCert = m_ServerCertificateOrBytes;
791                     if (chkCert != null && chkCert.GetType() == typeof(byte[]))
792                         return (X509Certificate)(m_ServerCertificateOrBytes = new X509Certificate((byte[]) chkCert));
793                     else
794                         return chkCert as X509Certificate;
795                 }
796         }
797         internal void UpdateServerCertificate(X509Certificate certificate)
798         {
799             if (certificate != null)
800                 m_ServerCertificateOrBytes = certificate.GetRawCertData();
801             else
802                 m_ServerCertificateOrBytes = null;
803         }
804
805         /// <devdoc>
806         /// <para>
807         /// Gets the Client Certificate sent by us to the Server.
808         /// </para>
809         /// </devdoc>
810         public  X509Certificate ClientCertificate {
811             get {
812                 object chkCert = m_ClientCertificateOrBytes;
813                 if (chkCert != null && chkCert.GetType() == typeof(byte[]))
814                     return (X509Certificate)(m_ClientCertificateOrBytes = new X509Certificate((byte[]) chkCert));
815                 else
816                     return chkCert as X509Certificate;
817             }
818         }
819         internal void UpdateClientCertificate(X509Certificate certificate)
820         {
821             if (certificate != null)
822                 m_ClientCertificateOrBytes = certificate.GetRawCertData();
823             else
824                 m_ClientCertificateOrBytes = null;
825         }
826
827 #endif // !FEATURE_PAL
828
829
830         /// <devdoc>
831         ///    <para>
832         ///       Indicates that the <see cref='System.Net.ServicePoint'/> supports pipelined connections.
833         ///    </para>
834         /// </devdoc>
835         public bool SupportsPipelining {
836             get {
837                 return (m_HttpBehaviour>HttpBehaviour.HTTP10 || m_HttpBehaviour==HttpBehaviour.Unknown);
838             }
839         }
840
841         //
842         // SetTcpKeepAlive 
843         //
844         // Enable/Disable the use of TCP keepalive option on ServicePoint
845         // connections. This method does not affect existing ServicePoints.
846         // When a ServicePoint is constructed it will inherit the current 
847         // settings.
848         //
849         // Parameters:
850         //
851         // enabled - if true enables the use of the TCP keepalive option 
852         // for ServicePoint connections.
853         //
854         // keepAliveTime - specifies the timeout, in milliseconds, with no
855         // activity until the first keep-alive packet is sent. Ignored if 
856         // enabled parameter is false.
857         //
858         // keepAliveInterval - specifies the interval, in milliseconds, between
859         // when successive keep-alive packets are sent if no acknowledgement is
860         // received. Ignored if enabled parameter is false.
861         //
862         public void SetTcpKeepAlive(
863                             bool enabled, 
864                             int keepAliveTime, 
865                             int keepAliveInterval) {
866         
867             GlobalLog.Enter(
868                 "ServicePoint::SetTcpKeepAlive()" + 
869                 " enabled: " + enabled.ToString() +
870                 " keepAliveTime: " + keepAliveTime.ToString() +
871                 " keepAliveInterval: " + keepAliveInterval.ToString()
872             );
873             if (enabled) {
874                 m_UseTcpKeepAlive = true;
875                 if (keepAliveTime <= 0) {
876                     throw new ArgumentOutOfRangeException("keepAliveTime");
877                 }
878                 if (keepAliveInterval <= 0) {
879                     throw new ArgumentOutOfRangeException("keepAliveInterval");
880                 }
881                 m_TcpKeepAliveTime = keepAliveTime;
882                 m_TcpKeepAliveInterval = keepAliveInterval;
883             } else {
884                 m_UseTcpKeepAlive = false;
885                 m_TcpKeepAliveTime = 0;
886                 m_TcpKeepAliveInterval =0;
887             }
888             GlobalLog.Leave("ServicePoint::SetTcpKeepAlive()");
889         }
890
891         //
892         // Internal Properties
893         //
894
895         internal bool Understands100Continue {
896             set {
897                 m_Understands100Continue = value;
898             }
899             get {
900                 return m_Understands100Continue;
901             }
902         }
903
904         //
905         // InternalProxyServicePoint
906         //
907         // Indicates if we are using this service point to represent
908         //  a proxy connection, if so we may have to use special
909         //  semantics when creating connections
910         //
911
912         internal bool InternalProxyServicePoint {
913             get {
914                 return m_ProxyServicePoint;
915             }
916         }
917
918         //
919         // IncrementConnection
920         //
921         // call to indicate that we now are starting a new
922         //  connection within this service point
923         //
924
925         internal void IncrementConnection() {
926             GlobalLog.Enter("ServicePoint#" + ValidationHelper.HashString(this) + "::IncrementConnection()", m_CurrentConnections.ToString());
927             // we need these to be atomic operations
928             lock(this) {
929                 m_CurrentConnections++;
930                 if (m_CurrentConnections==1) {
931                     GlobalLog.Assert(m_ExpiringTimer != null, "ServicePoint#{0}::IncrementConnection|First connection active, but ServicePoint wasn't idle.", ValidationHelper.HashString(this));
932
933                     // 
934
935
936
937
938                     m_ExpiringTimer.Cancel();
939                     m_ExpiringTimer = null;
940                 }
941             }
942             GlobalLog.Leave("ServicePoint#" + ValidationHelper.HashString(this) + "::IncrementConnection()", m_CurrentConnections.ToString());
943         }
944
945         //
946         // DecrementConnection
947         //
948         // call to indicate that we now are removing
949         //  a connection within this connection group
950         //
951
952         internal void DecrementConnection() {
953             // The timer thread is allowed to call this.  (It doesn't call user code and doesn't block.)
954             GlobalLog.ThreadContract(ThreadKinds.Unknown, ThreadKinds.SafeSources | ThreadKinds.Timer, "ServicePoint#" + ValidationHelper.HashString(this) + "::DecrementConnection");
955             GlobalLog.Enter("ServicePoint#" + ValidationHelper.HashString(this) + "::DecrementConnection()", m_CurrentConnections.ToString());
956
957             // we need these to be atomic operations
958             lock(this) {
959                 m_CurrentConnections--;
960                 if (m_CurrentConnections==0) {
961                     GlobalLog.Assert(m_ExpiringTimer == null, "ServicePoint#{0}::DecrementConnection|Expiring timer set on non-idle ServicePoint.", ValidationHelper.HashString(this));
962                     m_IdleSince = DateTime.Now;
963                     m_ExpiringTimer = m_IdlingQueue.CreateTimer(ServicePointManager.IdleServicePointTimeoutDelegate, this);
964                 }
965                 else if ( m_CurrentConnections < 0 ) {
966                     m_CurrentConnections = 0;
967                     Diagnostics.Debug.Assert(false, "ServicePoint; Too many decrements.");
968                 }
969             }
970             GlobalLog.Leave("ServicePoint#" + ValidationHelper.HashString(this) + "::DecrementConnection()", m_CurrentConnections.ToString());
971         }
972         
973 #if !FEATURE_PAL
974         internal RemoteCertValidationCallback SetupHandshakeDoneProcedure(TlsStream secureStream, Object request) {
975             // Use a private adapter to connect tlsstream and this service point
976             return HandshakeDoneProcedure.CreateAdapter(this, secureStream, request);
977         }
978
979         // This is an adapter class that ties a servicePoint and a TlsStream on the SSL handshake completion
980         private class HandshakeDoneProcedure {
981             TlsStream    m_SecureStream;
982             Object       m_Request;
983             ServicePoint m_ServicePoint;
984
985             internal static RemoteCertValidationCallback CreateAdapter(ServicePoint serviePoint, TlsStream secureStream, Object request)
986             {
987                 HandshakeDoneProcedure adapter = new HandshakeDoneProcedure(serviePoint, secureStream, request);
988                 return new RemoteCertValidationCallback(adapter.CertValidationCallback);
989             }
990
991             private HandshakeDoneProcedure (ServicePoint serviePoint, TlsStream secureStream, Object request) {
992                 m_ServicePoint  = serviePoint;
993                 m_SecureStream  = secureStream;
994                 m_Request       = request;
995             }
996
997             private bool CertValidationCallback(string hostName, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) {
998                 m_ServicePoint.UpdateServerCertificate(certificate);
999                 m_ServicePoint.UpdateClientCertificate(m_SecureStream.ClientCertificate);
1000                 bool useDefault = true;
1001
1002                 // Request specific validator takes priority
1003                 HttpWebRequest httpWebRequest = m_Request as HttpWebRequest;
1004                 if (httpWebRequest != null && httpWebRequest.ServerCertValidationCallback != null)
1005                 {
1006                     return httpWebRequest.ServerCertValidationCallback.
1007                                           Invoke(m_Request,
1008                                                  certificate,
1009                                                  chain,
1010                                                  sslPolicyErrors);
1011                 }
1012
1013                 // If a policy is set, call the user callback inside the ExecutionContext.
1014                 if (ServicePointManager.GetLegacyCertificatePolicy() != null && (m_Request is WebRequest))
1015                 {
1016                     useDefault = false;
1017
1018                     bool checkResult = ServicePointManager.CertPolicyValidationCallback.
1019                                                            Invoke(hostName,
1020                                                                   m_ServicePoint,
1021                                                                   certificate,
1022                                                                   (WebRequest) m_Request,
1023                                                                   chain,
1024                                                                   sslPolicyErrors);
1025
1026                     if (checkResult == false){
1027                         if (!ServicePointManager.CertPolicyValidationCallback.UsesDefault
1028                             || ServicePointManager.ServerCertificateValidationCallback == null)
1029                             return checkResult;
1030                     }
1031                 }
1032
1033                 if (ServicePointManager.ServerCertificateValidationCallback != null)
1034                 {
1035                     useDefault = false;
1036                     return ServicePointManager.ServerCertValidationCallback.
1037                                                Invoke(m_Request,
1038                                                       certificate,
1039                                                       chain,
1040                                                       sslPolicyErrors);
1041                 }
1042
1043                 if (useDefault)
1044                     return sslPolicyErrors == SslPolicyErrors.None;
1045
1046                 return true;
1047             }
1048
1049
1050         }
1051
1052 #endif // !FEATURE_PAL
1053         
1054         private void IdleConnectionGroupTimeoutCallback(TimerThread.Timer timer, int timeNoticed, object context) {
1055             ConnectionGroup connectionGroup = (ConnectionGroup)context;
1056
1057             if (Logging.On) Logging.PrintInfo(Logging.Web, this, SR.GetString(SR.net_log_closed_idle, 
1058                 "ConnectionGroup", connectionGroup.GetHashCode()));
1059
1060             ReleaseConnectionGroup(connectionGroup.Name);
1061         }
1062         
1063         internal TimerThread.Timer CreateConnectionGroupTimer(ConnectionGroup connectionGroup) {
1064             return m_IdlingQueue.CreateTimer(m_IdleConnectionGroupTimeoutDelegate, connectionGroup);
1065         }
1066
1067         /// <devdoc>
1068         ///    <para>
1069         ///       Sets connections in this group to not be KeepAlive.
1070         ///       This is called to force cleanup of the ConnectionGroup by the
1071         ///       NTLM and Negotiate authentication modules.
1072         ///    </para>
1073         /// </devdoc>
1074         internal bool ReleaseConnectionGroup(string connName) {
1075
1076             ConnectionGroup connectionGroup = null;
1077
1078             //
1079             // look up the ConnectionGroup based on the name
1080             //
1081             lock(this) {
1082                 
1083                 connectionGroup = FindConnectionGroup(connName, true);
1084                 //
1085                 // force all connections on the ConnectionGroup to not be KeepAlive
1086                 //
1087                 if (connectionGroup == null) {
1088                     return false;
1089                 }
1090
1091                 // Cancel the timer so it doesn't fire later and clean up a different 
1092                 // connection group with the same name.
1093                 connectionGroup.CancelIdleTimer();
1094
1095                 //
1096                 // remove ConnectionGroup from our Hashtable
1097                 //
1098                 m_ConnectionGroupList.Remove(connName);
1099             }
1100
1101             // Don't call the following under the lock: ConnectionGroup will call into Connection that
1102             // may take a lock on the Connection. ServicePoint should never call members under the lock that
1103             // end up taking a lock on a Connection (risk of deadlock).
1104             connectionGroup.DisableKeepAliveOnConnections();
1105
1106             return true;
1107         }
1108
1109         /// <devdoc>
1110         ///    <para>
1111         ///       - Sets all connections in all connections groups to not be KeepAlive.
1112         ///       - Causes all connections to be closed, if they are not active
1113         ///       - Removes all references to all connection groups and their connections
1114         ///       does essentially a "ReleaseConnectionGroup" of each group in this ServicePoint
1115         ///    </para>
1116         /// </devdoc>
1117         internal void ReleaseAllConnectionGroups()
1118         {
1119             // The timer thread is allowed to call this.  (It doesn't call user code and doesn't block.)
1120             GlobalLog.ThreadContract(ThreadKinds.Unknown, ThreadKinds.SafeSources | ThreadKinds.Timer, "ServicePoint#" + ValidationHelper.HashString(this) + "::ReleaseAllConnectionGroups");
1121
1122             // To avoid deadlock (can't lock a ServicePoint followed by a Connection), copy out all the
1123             // connection groups in a lock, then release them all outside of it.
1124             ArrayList cgs = new ArrayList(m_ConnectionGroupList.Count);
1125             lock(this)
1126             {
1127                 foreach (ConnectionGroup cg in m_ConnectionGroupList.Values)
1128                 {
1129                     cgs.Add(cg);
1130                 }
1131                 m_ConnectionGroupList.Clear();
1132             }
1133             foreach (ConnectionGroup cg in cgs)
1134             {
1135                 cg.DisableKeepAliveOnConnections();
1136             }
1137         }
1138
1139
1140         /// <summary>
1141         ///    <para>Internal class, used to store state for async Connect</para>
1142         /// </summary>
1143         private class ConnectSocketState {
1144             internal ConnectSocketState(ServicePoint servicePoint, PooledStream pooledStream, object owner, Socket s4, Socket s6)
1145             {
1146                 this.servicePoint = servicePoint;
1147                 this.pooledStream = pooledStream;
1148                 this.owner = owner;
1149                 this.s4 = s4;
1150                 this.s6 = s6;
1151             }
1152             internal ServicePoint servicePoint;
1153             internal Socket s4;
1154             internal Socket s6;
1155             internal object owner;
1156             internal IPAddress[] addresses;
1157             internal int currentIndex;
1158             internal int i;
1159             internal int unsuccessfulAttempts;
1160             internal bool connectFailure;
1161             internal PooledStream pooledStream;
1162         }
1163
1164
1165         /// <summary>
1166         ///    <para>Proviates an async callback that is called when Connect completes [part of ConnectSocket(...)]</para>
1167         /// </summary>
1168         private static void ConnectSocketCallback(IAsyncResult asyncResult) {
1169             ConnectSocketState state = (ConnectSocketState)asyncResult.AsyncState;
1170             Socket socket = null;
1171             IPAddress address = null;
1172             Exception innerException = null;
1173             Exception exception = null;
1174             WebExceptionStatus ws = WebExceptionStatus.ConnectFailure;
1175
1176
1177             try {
1178                 ws = state.servicePoint.ConnectSocketInternal(state.connectFailure, state.s4, state.s6, ref socket, ref address, state, asyncResult, out innerException);
1179             }
1180             catch (SocketException socketException) {
1181                 exception = socketException;
1182             }
1183             catch (ObjectDisposedException socketException) {
1184                 exception = socketException;
1185             }
1186
1187             if (ws == WebExceptionStatus.Pending) {
1188                 return;
1189             }
1190
1191             if (ws == WebExceptionStatus.Success) {
1192                 try {
1193                     state.servicePoint.CompleteGetConnection(state.s4, state.s6, socket, address);
1194                 }
1195                 catch (SocketException socketException) {
1196                     exception = socketException;
1197                 }
1198                 catch (ObjectDisposedException socketException) {
1199                     exception = socketException;
1200                 }
1201
1202             }
1203             else {
1204                 exception = new WebException(
1205                         NetRes.GetWebStatusString(ws),
1206                         ws == WebExceptionStatus.ProxyNameResolutionFailure || ws == WebExceptionStatus.NameResolutionFailure ? state.servicePoint.Host : null,
1207                         innerException,
1208                         ws,
1209                         null, /* no response */
1210                         WebExceptionInternalStatus.ServicePointFatal);
1211             }
1212             try {
1213                 state.pooledStream.ConnectionCallback(state.owner, exception, socket, address);
1214             }
1215             catch
1216             {
1217                 if (socket != null && socket.CleanedUp)
1218                     return;   // The connection was aborted and requests dispatched
1219                 throw;
1220             }
1221
1222         }
1223
1224         private void BindUsingDelegate(Socket socket, IPEndPoint remoteIPEndPoint)
1225         {
1226             IPEndPoint clonedRemoteIPEndPoint = new IPEndPoint(remoteIPEndPoint.Address, remoteIPEndPoint.Port);
1227             int retryCount;
1228
1229             for (retryCount=0; retryCount<int.MaxValue; retryCount++) {
1230                 IPEndPoint localIPEndPoint = BindIPEndPointDelegate(this, clonedRemoteIPEndPoint, retryCount);
1231                 if (localIPEndPoint == null)
1232                     break;
1233
1234                 try {
1235                     socket.InternalBind(localIPEndPoint);
1236                 }
1237                 catch {
1238                     continue;
1239                 }
1240                 break;
1241             }
1242             if (retryCount == int.MaxValue)
1243                 throw new OverflowException("Reached maximum number of BindIPEndPointDelegate retries");
1244         }
1245
1246
1247         /// <summary>
1248         ///    <para>Set SocketOptionName.ReuseUnicastPort (SO_REUSE_UNICASTPORT) socket option on the outbound connection.</para>
1249         /// </summary>
1250         private void SetUnicastReusePortForSocket(Socket socket)
1251         {
1252             bool reusePort;
1253          
1254             if (ServicePointManager.ReusePortSupported.HasValue && !ServicePointManager.ReusePortSupported.Value) {
1255                 // We tried to set the socket option before and it isn't supported on this system.  So, we'll save some
1256                 // time by not trying again.
1257                 reusePort = false;
1258             }
1259             else {
1260                 reusePort = ServicePointManager.ReusePort;
1261             }
1262
1263             if (reusePort) {
1264                 // This socket option is defined in Windows 10.0 or later.  It is also
1265                 // available if an LDR servicing patch has been installed on downlevel OS.
1266                 try {
1267                     socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseUnicastPort, 0x1);
1268                     if (Logging.On) { 
1269                         Logging.PrintInfo(Logging.Web, this, SR.GetString(SR.net_log_set_socketoption_reuseport, 
1270                             "Socket", socket.GetHashCode()));
1271                     }
1272                     
1273                     ServicePointManager.ReusePortSupported = true;
1274                 }
1275                 catch (SocketException) {
1276                     // The socket option is not supported.  We will ignore this error and fail gracefully.
1277                     if (Logging.On) { 
1278                         Logging.PrintInfo(Logging.Web, this, SR.GetString(SR.net_log_set_socketoption_reuseport_not_supported, 
1279                             "Socket", socket.GetHashCode()));
1280                     }                    
1281                     ServicePointManager.ReusePortSupported = false;
1282                 }
1283                 catch (Exception ex) {
1284                     // We want to preserve app compat and trap any other unusual exceptions.
1285                     if (Logging.On) { 
1286                         Logging.PrintInfo(Logging.Web, this, SR.GetString(SR.net_log_unexpected_exception, ex.Message));
1287                     }
1288                 }
1289             }            
1290         }
1291         
1292         /// <summary>
1293         ///    <para>This is the real logic for doing the Connect with IPv4 and IPv6 addresses, see ConnectSocket for details</para>
1294         /// </summary>
1295         private WebExceptionStatus ConnectSocketInternal(bool connectFailure, Socket s4, Socket s6, ref Socket socket, 
1296             ref IPAddress address, ConnectSocketState state, IAsyncResult asyncResult, out Exception exception) {
1297             IPEndPoint remoteIPEndPoint;
1298             exception = null;
1299
1300             //
1301             // so, here we are: we have the EndPoint we need to connect to, all we
1302             // need to do is call into winsock and try to connect to this HTTP server.
1303             //
1304             // this is how we do it:
1305             // we'll keep trying to Connect() until either:
1306             // (1) Connect() succeeds (on which we increment the number of connections opened) or
1307             // (2) we can't get any new address for this host.
1308             //
1309             // (1) is pretty easy, here's how we do (2):
1310             // If the hostinformation is every marked as failed, we will automatically refresh it
1311             // the next time it is read.
1312             // If we fail the first time using the DNS information and the DNS information is recent,
1313             // which mean's it either hasn't been tried or it failed, we will mark the
1314             // hostinformation as failed, and quit.  Otherwise we'll refresh the DNS information and
1315             // try one more time. If we fail the second time, then we'll mark the DNS information
1316             // as failed and quit.
1317             IPAddress[] addresses = null;
1318             for (int unsuccessfulAttempts = 0; unsuccessfulAttempts < 2; unsuccessfulAttempts++) {
1319
1320                 int currentIndex;
1321                 int i = 0;
1322
1323                 // Use asyncResult to make sure it is only called at initiation time.
1324                 if (asyncResult == null)
1325                 {
1326                     // the second time, determine if the list was recent.
1327
1328                     addresses = GetIPAddressInfoList(out currentIndex, addresses);
1329
1330                     //the addresses were recent, or we couldn't resolve the addresses.
1331                     if (addresses == null || addresses.Length == 0)
1332                         break;
1333                 }
1334                 else
1335                 {
1336                     GlobalLog.Print("ServicePoint#" + ValidationHelper.HashString(this) + "::ConnectSocketInternal() resuming previous state");
1337
1338                     addresses = state.addresses;
1339                     currentIndex = state.currentIndex;
1340                     i = state.i;
1341                     unsuccessfulAttempts = state.unsuccessfulAttempts;
1342                 }
1343
1344                 //otherwise, try all of the addresses in the list.
1345                 for (; i < addresses.Length; i++)
1346                 {
1347                     IPAddress ipAddressInfo = addresses[currentIndex];
1348                     try {
1349                         remoteIPEndPoint = new IPEndPoint(ipAddressInfo, m_Port);
1350                         Socket attemptSocket;
1351                         if ( remoteIPEndPoint.Address.AddressFamily==AddressFamily.InterNetwork ) {
1352                             attemptSocket = s4;
1353                         }
1354                         else {
1355                             attemptSocket = s6;
1356                         }
1357
1358                         if (state != null)
1359                         {
1360                             if (asyncResult != null)
1361                             {
1362                                 IAsyncResult asyncResultCopy = asyncResult;
1363                                 asyncResult = null;
1364                                 attemptSocket.EndConnect(asyncResultCopy);
1365                             }
1366                             else {
1367                                 GlobalLog.Print("ServicePoint#" + ValidationHelper.HashString(this) + "::ConnectSocketInternal() calling BeginConnect() to:" + remoteIPEndPoint.ToString());
1368
1369                                 // save off our state and do our async call
1370                                 state.addresses = addresses;
1371                                 state.currentIndex = currentIndex;
1372                                 state.i = i;
1373                                 state.unsuccessfulAttempts = unsuccessfulAttempts;
1374                                 state.connectFailure = connectFailure;
1375
1376                                 if (!attemptSocket.IsBound) {
1377                                     if (ServicePointManager.ReusePort) {
1378                                         SetUnicastReusePortForSocket(attemptSocket);
1379                                     }
1380                                 
1381                                     if (BindIPEndPointDelegate != null) {
1382                                         BindUsingDelegate(attemptSocket, remoteIPEndPoint);
1383                                     }
1384                                 }
1385
1386                                 attemptSocket.UnsafeBeginConnect(remoteIPEndPoint, m_ConnectCallbackDelegate, state);
1387                                 return WebExceptionStatus.Pending;
1388                             }
1389                         }
1390                         else {
1391                             if (!attemptSocket.IsBound) {
1392                                     if (ServicePointManager.ReusePort) {
1393                                         SetUnicastReusePortForSocket(attemptSocket);
1394                                     }
1395                                 
1396                                 if (BindIPEndPointDelegate != null) {
1397                                     BindUsingDelegate(attemptSocket, remoteIPEndPoint);
1398                                 }
1399                             }
1400
1401                             attemptSocket.InternalConnect(remoteIPEndPoint);
1402                         }
1403                         socket  = attemptSocket;
1404                         address = ipAddressInfo;
1405                         exception = null;
1406                         UpdateCurrentIndex(addresses, currentIndex);
1407                         return WebExceptionStatus.Success;
1408                     }
1409                     catch (ObjectDisposedException)
1410                     {
1411                         // This can happen if the request has been aborted and the attemptSocket got closed.
1412                         return WebExceptionStatus.RequestCanceled;
1413                     }
1414                     catch (Exception e)
1415                     {
1416                         if (NclUtilities.IsFatal(e)) throw;
1417
1418                         exception = e;
1419                         connectFailure = true;
1420                     }
1421                     currentIndex++;
1422                     if (currentIndex >= addresses.Length) {
1423                         currentIndex = 0;
1424                     }
1425                 }
1426             }
1427
1428             Failed(addresses);
1429
1430             return connectFailure ? WebExceptionStatus.ConnectFailure :
1431                 InternalProxyServicePoint ? WebExceptionStatus.ProxyNameResolutionFailure :
1432                 WebExceptionStatus.NameResolutionFailure;
1433         }
1434
1435         /// <summary>
1436         ///    <para>private implimentation of ConnectSocket(...)</para>
1437         /// </summary>
1438         private WebExceptionStatus ConnectSocket(Socket s4, Socket s6, ref Socket socket, ref IPAddress address, 
1439             ConnectSocketState state, out Exception exception) {
1440             //
1441             // we need this for the call to connect()
1442             //
1443             return ConnectSocketInternal(false, s4, s6, ref socket, ref address, state, null, out exception);
1444         }
1445
1446         [System.Diagnostics.Conditional("DEBUG")]
1447         internal void DebugMembers(int requestHash) {
1448             foreach(ConnectionGroup connectGroup in  m_ConnectionGroupList.Values) {
1449                 if (connectGroup!=null) {
1450                     try {
1451                         connectGroup.DebugMembers(requestHash);
1452                     }
1453                     catch {
1454                     }
1455                 }
1456             }
1457         }
1458
1459
1460         //
1461         // Previously: class IPHostInformation
1462         //
1463
1464         private string              m_HostName = String.Empty;
1465         private bool                m_IsTrustedHost = true; // CBT: False if the DNS resolve changed the host
1466         private IPAddress[]         m_IPAddressInfoList;
1467         private int                 m_CurrentAddressInfoIndex;
1468         private bool                m_ConnectedSinceDns = false;
1469         private bool                m_AddressListFailed = false;
1470         private DateTime            m_LastDnsResolve;
1471         private bool                m_IPAddressesAreLoopback;
1472
1473         private void Failed(IPAddress[] addresses)
1474         {
1475             if (addresses == m_IPAddressInfoList){
1476                lock(this){
1477                    if (addresses == m_IPAddressInfoList){
1478                        m_AddressListFailed = true;
1479                    }
1480                }
1481            }
1482         }
1483
1484
1485         //if dns round robin is enabled, we don't want to update the index
1486         //because other connections may have skipped to the next address.
1487         //we need a better mechanism to handle dead connections
1488         private void UpdateCurrentIndex(IPAddress[] addresses, int currentIndex)
1489         {
1490             if (addresses == m_IPAddressInfoList && (m_CurrentAddressInfoIndex != currentIndex || !m_ConnectedSinceDns)){
1491                 lock(this){
1492                     if (addresses == m_IPAddressInfoList){
1493                         if (!ServicePointManager.EnableDnsRoundRobin ) {
1494                             m_CurrentAddressInfoIndex = currentIndex;
1495                         }
1496                         m_ConnectedSinceDns = true;
1497                     }
1498                 }
1499             }
1500         }
1501
1502
1503         private bool HasTimedOut {
1504             get {
1505                 int dnsRefreshTimeout = ServicePointManager.DnsRefreshTimeout;
1506                 return dnsRefreshTimeout != Timeout.Infinite &&
1507                     (m_LastDnsResolve + new TimeSpan(0, 0, 0, 0, dnsRefreshTimeout)) < DateTime.UtcNow;
1508             }
1509         }
1510
1511
1512         // If addresses is specified, we determine if the addresslist is recent
1513         // If the answer is yes, we return null.  Whether its recent is determined by whether
1514         // or not the current hostinformation has ever been marked as succeeded or failed (meaning
1515         // even tried). If it isn't recent, we'll refresh the addresslist.
1516
1517         private IPAddress[] GetIPAddressInfoList(out int currentIndex, IPAddress[] addresses)
1518         {
1519             IPHostEntry ipHostEntry = null;
1520             currentIndex = 0;
1521             bool needDnsResolution = false;
1522             bool dnsResolutionFailed = false;
1523
1524             // Phase 1: Decide if we need to do a DNS resolution
1525             lock (this) {
1526
1527                 // return null if the current hostinformation has never been marked as succeeded or failed
1528                 // (the hostinformation hasn't been used) and it hasn't changed.
1529
1530                 if (addresses != null && !m_ConnectedSinceDns && !m_AddressListFailed && addresses == m_IPAddressInfoList)
1531                     return null;
1532
1533                 // refresh the list if its already failed, or if the addresslist isn't recent
1534                 if (m_IPAddressInfoList == null || m_AddressListFailed || addresses == m_IPAddressInfoList || HasTimedOut) {
1535                     m_CurrentAddressInfoIndex = 0;
1536                     m_ConnectedSinceDns = false;
1537                     m_AddressListFailed = false;
1538                     m_LastDnsResolve = DateTime.UtcNow;
1539
1540                     needDnsResolution = true;
1541                 }
1542             }
1543
1544             // Phase 2: If we have to do a DNS resolution now, then do it now
1545             if (needDnsResolution) {
1546                 try {
1547                     dnsResolutionFailed = !Dns.TryInternalResolve(m_Host, out ipHostEntry);
1548                 }
1549                 catch (Exception exception)
1550                 {
1551                     if (NclUtilities.IsFatal(exception)) throw;
1552                     dnsResolutionFailed = true;
1553                     GlobalLog.Print("IPHostInformation#" + ValidationHelper.HashString(this) + "::GetIPAddressInfoList() Dns.InternalResolveFast() failed with exception:\r\n" + exception.ToString());
1554                 }
1555             }
1556
1557             // Phase 3: If we did a DNS resolution, then deal with the results safely under a lock
1558             lock (this) {
1559                 if (needDnsResolution) {
1560
1561                     m_IPAddressInfoList = null;
1562
1563                     if (!dnsResolutionFailed) {
1564                         if (ipHostEntry!=null && ipHostEntry.AddressList!=null && ipHostEntry.AddressList.Length>0) {
1565                             SetAddressList(ipHostEntry);
1566                         }
1567                         else {
1568                             GlobalLog.Print("IPHostInformation#" + ValidationHelper.HashString(this) + "::GetIPAddressInfoList() Dns.InternalResolveFast() failed with null");
1569                         }
1570                     } else {
1571                         GlobalLog.Print("IPHostInformation#" + ValidationHelper.HashString(this) + "::GetIPAddressInfoList() Dns.InternalResolveFast() had thrown an exception");
1572                     }
1573                 }
1574
1575                 if (m_IPAddressInfoList!=null && m_IPAddressInfoList.Length > 0) {
1576                     GlobalLog.Print("IPHostInformation#" + ValidationHelper.HashString(this) + "::GetIPAddressInfoList() m_IPAddressInfoList = "+m_IPAddressInfoList);
1577                     currentIndex = m_CurrentAddressInfoIndex;
1578
1579                     //auto increment index for next connect request if round robin is enabled
1580                     if (ServicePointManager.EnableDnsRoundRobin)
1581                     {
1582                         m_CurrentAddressInfoIndex++;
1583                         if (m_CurrentAddressInfoIndex >= m_IPAddressInfoList.Length) {
1584                             m_CurrentAddressInfoIndex = 0;
1585                         }
1586                     }
1587                     return m_IPAddressInfoList;
1588                 }
1589             }
1590             GlobalLog.Print("IPHostInformation#" + ValidationHelper.HashString(this) + "::GetIPAddressInfoList() GetIPAddressInfoList returning null");
1591             return null;
1592         }
1593
1594         //
1595         // Called under lock(this)
1596         //
1597         private void SetAddressList(IPHostEntry ipHostEntry)
1598         {
1599             GlobalLog.Print("IPHostInformation#" + ValidationHelper.HashString(this) + "::SetAddressList("+ipHostEntry.HostName+")");
1600             //
1601             // Create an array of IPAddress of the appropriate size, then
1602             // get a list of our local addresses. Walk through the input
1603             // address list. Copy each address in the address list into
1604             // our array, and if the address is a loopback address, mark it as
1605             // such.
1606             //
1607             // Only update the member with successfull final result.
1608             // In case of an exception m_IPAddressInfoList will stay as null
1609             //
1610             bool wasLoopback = m_IPAddressesAreLoopback;
1611             bool wasNull = m_IPAddressInfoList == null;
1612
1613             m_IPAddressesAreLoopback = IsAddressListLoopback(ipHostEntry.AddressList);
1614             m_IPAddressInfoList = ipHostEntry.AddressList;
1615             m_HostName = ipHostEntry.HostName;
1616             m_IsTrustedHost = ipHostEntry.isTrustedHost;
1617
1618             if (wasNull || wasLoopback != m_IPAddressesAreLoopback)
1619             {
1620                 ResolveConnectionLimit();
1621             }
1622         }
1623
1624         private static bool IsAddressListLoopback(IPAddress[] addressList)
1625         {
1626             GlobalLog.Print("IPHostInformation::CheckAddressList(" + addressList.Length + ")");
1627
1628             //
1629             // Walk through each member of the input list, copying it into our temp array.
1630             //
1631
1632             int i, k;
1633             IPAddress[] localAddresses = null;
1634             try {
1635                 localAddresses = NclUtilities.LocalAddresses;
1636             }
1637             catch (Exception exception)
1638             {
1639                 if (NclUtilities.IsFatal(exception)) throw;
1640
1641                 // ATTN: If LocalAddresses has failed terribly we will treat just resolved name as a remote server.
1642                 //       
1643
1644                 if (Logging.On)
1645                 {
1646                     Logging.PrintError(Logging.Web, SR.GetString(SR.net_log_retrieving_localhost_exception, exception));
1647                     Logging.PrintWarning(Logging.Web, SR.GetString(SR.net_log_resolved_servicepoint_may_not_be_remote_server));
1648                 }
1649             }
1650
1651             for (i = 0; i < addressList.Length; i++)
1652             {
1653                 // First, check to see if the current address is a loopback address.
1654                 if (IPAddress.IsLoopback(addressList[i]))
1655                 {
1656                     continue;
1657                 }
1658
1659                 if (localAddresses != null)
1660                 {
1661                     // See if the current IP address is a local address, and if
1662                     // so mark it as such.
1663                     for (k = 0; k < localAddresses.Length; k++)
1664                     {
1665                         //
1666                         // IPv6 Changes: Use .Equals for this check !
1667                         //
1668                         if (addressList[i].Equals(localAddresses[k]))
1669                         {
1670                             break;
1671                         }
1672                     }
1673                     if (k < localAddresses.Length)
1674                     {
1675                         continue;
1676                     }
1677                 }
1678
1679                 break;
1680             }
1681
1682             return i == addressList.Length;
1683         }
1684     }
1685 }