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