1 //------------------------------------------------------------------------------
2 // <copyright file="ServicePoint.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 //------------------------------------------------------------------------------
8 using System.Net.Sockets;
9 using System.Collections;
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;
19 public delegate IPEndPoint BindIPEndPoint(ServicePoint servicePoint, IPEndPoint remoteEndPoint, int retryCount);
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
28 /// <para>Provides connection management for other classes.</para>
31 public class ServicePoint {
33 internal const int LoopbackConnectionLimit = Int32.MaxValue;
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;
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;
60 private static readonly AsyncCallback m_ConnectCallbackDelegate = new AsyncCallback(ConnectSocketCallback);
62 private readonly TimerThread.Callback m_IdleConnectionGroupTimeoutDelegate;
65 private object m_ServerCertificateOrBytes;
66 private object m_ClientCertificateOrBytes;
67 #endif // !FEATURE_PAL
69 private bool m_UseTcpKeepAlive = false;
70 private int m_TcpKeepAliveTime;
71 private int m_TcpKeepAliveInterval;
73 internal string LookupString {
75 return m_LookupString;
79 internal string Hostname {
85 internal bool IsTrustedHost {
87 return m_IsTrustedHost;
91 public BindIPEndPoint BindIPEndPointDelegate {
93 return m_BindIPEndPointDelegate;
96 ExceptionHelper.InfrastructurePermission.Demand();
97 m_BindIPEndPointDelegate = value;
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);
108 m_ProxyServicePoint = proxyServicePoint;
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;
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;
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);
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);
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;
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);
168 internal object CachedChannelBinding
170 get { return m_CachedChannelBinding; }
173 internal void SetCachedChannelBinding(Uri uri, ChannelBinding binding)
175 if (uri.Scheme == Uri.UriSchemeHttps)
177 m_CachedChannelBinding = (binding != null ? (object)binding : (object)DBNull.Value);
183 FindConnectionGroup -
185 Searches for the a Group object that actually holds the connections
186 that we want to peak at.
190 request - Request that's being submitted.
191 connName - Connection Name if needed
198 private ConnectionGroup FindConnectionGroup(string connName, bool dontCreate) {
199 string lookupStr = ConnectionGroup.MakeQueryStr(connName);
201 GlobalLog.Print("ServicePoint#" + ValidationHelper.HashString(this) + "::FindConnectionGroup() lookupStr:[" + ValidationHelper.ToString(connName) + "]");
203 ConnectionGroup entry = m_ConnectionGroupList[lookupStr] as ConnectionGroup;
205 if (entry==null && !dontCreate) {
206 entry = new ConnectionGroup(this, connName);
207 GlobalLog.Print("ServicePoint#" + ValidationHelper.HashString(this) + "::FindConnectionGroup() adding ConnectionGroup lookupStr:[" + lookupStr + "]");
209 m_ConnectionGroupList[lookupStr] = entry;
212 GlobalLog.Print("ServicePoint#" + ValidationHelper.HashString(this) + "::FindConnectionGroup() using existing ConnectionGroup");
214 GlobalLog.Print("ServicePoint#" + ValidationHelper.HashString(this) + "::FindConnectionGroup() returning ConnectionGroup:" + ValidationHelper.ToString(entry) + (entry!=null ? " ConnLimit:" + entry.ConnectionLimit.ToString() : ""));
221 /// Tempory for getting a new Connection for FTP client, for the time being
224 internal Socket GetConnection(PooledStream PooledStream, object owner, bool async, out IPAddress address, ref Socket abortSocket, ref Socket abortSocket6)
226 Socket socket = null;
227 Socket socket6 = null;
228 Socket finalSocket = null;
229 Exception innerException = null;
230 WebExceptionStatus ws = WebExceptionStatus.ConnectFailure;
234 // if we will not create a tunnel through a proxy then create
235 // and connect the socket we will use for the connection
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.
242 if ( Socket.OSSupportsIPv4 ) {
243 socket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
246 if ( Socket.OSSupportsIPv6 ) {
247 socket6 = new Socket(AddressFamily.InterNetworkV6,SocketType.Stream,ProtocolType.Tcp);
250 abortSocket = socket;
251 abortSocket6 = socket6;
254 // Setup socket timeouts for sync requests
258 ConnectSocketState state = null;
261 state = new ConnectSocketState(this, PooledStream, owner, socket, socket6);
264 ws = ConnectSocket(socket, socket6, ref finalSocket, ref address, state, out innerException);
266 if (ws == WebExceptionStatus.Pending) {
270 if (ws != WebExceptionStatus.Success) {
271 throw new WebException(
272 NetRes.GetWebStatusString(ws),
273 ws == WebExceptionStatus.ProxyNameResolutionFailure || ws == WebExceptionStatus.NameResolutionFailure ? Host : null,
276 null, /* no response */
277 WebExceptionInternalStatus.ServicePointFatal);
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.
285 if ( finalSocket == null ) {
286 throw new IOException(SR.GetString(SR.net_io_transportfailure));
289 CompleteGetConnection(socket, socket6, finalSocket, address);
295 /// Complete the GetConnection(...) call, the function was divided for async completion
298 private void CompleteGetConnection(Socket socket, Socket socket6, Socket finalSocket, IPAddress address) {
300 // Decide which socket to retain
302 if ( finalSocket.AddressFamily == AddressFamily.InterNetwork ) {
303 if ( socket6 != null ) {
309 if (socket != null) {
315 // make this configurable from the user:
316 if (!UseNagleAlgorithm) {
317 finalSocket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, 1);
319 if (ReceiveBufferSize != -1) {
320 finalSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveBuffer, ReceiveBufferSize);
322 if (m_UseTcpKeepAlive) {
324 // Marshal a byte array containing the params for WsaIoctl
325 // struct tcp_keepalive {
327 // u_long keepalivetime;
328 // u_long keepaliveinterval;
330 byte[] input = new byte[sizeof(int)*3];
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);
342 finalSocket.IOControl(
343 IOControlCode.KeepAliveValues,
349 //return CreateConnection(NetworkStream stream, IPAddress address);
350 //return new NetworkStream(finalSocket, true);
356 SubmitRequest - Submit a request for sending.
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.
365 In this version, we use HttpWebRequest. In the future we use IRequest
368 Request - Request that's being submitted.
369 SubmitDelegate - Delegate to be invoked.
376 internal virtual void SubmitRequest(HttpWebRequest request) {
377 SubmitRequest(request, null);
380 // userReqeustThread says whether we can post IO from this thread or not.
381 internal void SubmitRequest(HttpWebRequest request, string connName)
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
389 Connection connToUse;
390 ConnectionGroup connGroup;
391 bool forcedsubmit = false;
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));
399 connToUse = connGroup.FindConnection(request, connName, out forcedsubmit);
400 // The request could be already aborted
401 if (connToUse == null)
404 GlobalLog.Print("ServicePoint#" + ValidationHelper.HashString(this) + "::SubmitRequest() Using Connection#" + ValidationHelper.HashString(connToUse));
405 // finally sumbit delegate
406 if (connToUse.SubmitRequest(request, forcedsubmit)) {
416 /// Gets and sets timeout for when connections should be recycled.
419 public int ConnectionLeaseTimeout {
421 return m_ConnectionLeaseTimeout;
424 if ( !ValidationHelper.ValidateRange(value, Timeout.Infinite, Int32.MaxValue)) {
425 throw new ArgumentOutOfRangeException("value");
427 if (value != m_ConnectionLeaseTimeout) {
428 m_ConnectionLeaseTimeout = value;
429 m_ConnectionLeaseTimerQueue = null;
435 /// <para>Returns a timer queue that can be used internally to create timers of
436 /// ConnectionLeaseTimeout duration.</para>
438 internal TimerThread.Queue ConnectionLeaseTimerQueue {
440 TimerThread.Queue queue = m_ConnectionLeaseTimerQueue;
442 queue = TimerThread.GetOrCreateQueue(ConnectionLeaseTimeout);
443 m_ConnectionLeaseTimerQueue = queue;
445 return m_ConnectionLeaseTimerQueue;
449 // Only the scheme and hostport, for example http://www.microsoft.com
452 /// Gets the Uniform Resource Identifier of the <see cref='System.Net.ServicePoint'/>.
458 throw new NotSupportedException(SR.GetString(SR.net_servicePointAddressNotSupportedInHostMode));
461 // Don't let low-trust apps discover the proxy information.
462 if (m_ProxyServicePoint)
464 ExceptionHelper.WebPermissionUnrestricted.Demand();
471 internal Uri InternalAddress
475 GlobalLog.Assert(!m_HostMode, "ServicePoint#{0}::InternalAddress|Can't be used in Host Mode.", ValidationHelper.HashString(this));
480 internal string Host {
485 return m_Address.Host;
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
500 public int MaxIdleTime {
502 return m_IdlingQueue.Duration;
505 if ( !ValidationHelper.ValidateRange(value, Timeout.Infinite, Int32.MaxValue)) {
506 throw new ArgumentOutOfRangeException("value");
510 if (value == m_IdlingQueue.Duration)
514 // Make sure we can cancel the existing one. If not, we already idled out.
515 if (m_ExpiringTimer == null || m_ExpiringTimer.Cancel())
517 m_IdlingQueue = TimerThread.GetOrCreateQueue(value);
518 if (m_ExpiringTimer != null)
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);
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.
537 public bool UseNagleAlgorithm {
539 return m_UseNagleAlgorithm;
542 m_UseNagleAlgorithm = value;
548 /// Gets and sets the socket's receive buffer size.
551 public int ReceiveBufferSize {
553 return m_ReceiveBufferSize;
556 if ( !ValidationHelper.ValidateRange(value, -1, Int32.MaxValue)) {
557 throw new ArgumentOutOfRangeException("value");
559 m_ReceiveBufferSize = value;
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.
571 public bool Expect100Continue {
573 m_Expect100Continue = value;
576 return m_Expect100Continue;
582 /// Gets the date/time that the <see cref='System.Net.ServicePoint'/> went idle.
585 public DateTime IdleSince {
591 // HTTP Server Version
594 /// The version of the protocol being used on this <see cref='System.Net.ServicePoint'/>.
597 public virtual Version ProtocolVersion {
599 return (m_HttpBehaviour>HttpBehaviour.HTTP10 || m_HttpBehaviour == HttpBehaviour.Unknown) ? HttpVersion.Version11 : HttpVersion.Version10;
603 // Contains set accessor for Version property. Version is a read-only
604 // property at the API
605 internal HttpBehaviour HttpBehaviour {
607 return m_HttpBehaviour;
610 m_HttpBehaviour = value;
612 // if version is greater than HTTP/1.1, and server undesrtood
613 // 100 Continue so far, keep expecting it.
615 m_Understands100Continue = m_Understands100Continue && (m_HttpBehaviour>HttpBehaviour.HTTP10 || m_HttpBehaviour == HttpBehaviour.Unknown);
621 /// Gets the connection name established by the <see cref='System.Net.WebRequest'/> that created the connection.
624 public string ConnectionName {
626 return m_ConnectionName;
632 /// Gets the connection mode in use by the <see cref='System.Net.ServicePoint'/>. One of the <see cref='System.Net.ConnectionModes'/>
635 internal ConnectionModes ConnectionMode {
637 return m_HttpBehaviour>=HttpBehaviour.HTTP11 ? ConnectionModes.Pipeline : ConnectionModes.Persistent;
643 /// Removes the specified Connection group from the ServicePoint, destroys safe and unsafe groups, but not internal.
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)) {
652 GlobalLog.Leave("ServicePoint#" + ValidationHelper.HashString(this) + "::CloseConnectionGroup()","true");
655 GlobalLog.Leave("ServicePoint#" + ValidationHelper.HashString(this) + "::CloseConnectionGroup()","false");
659 internal void CloseConnectionGroupInternal(string connectionGroupName) {
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
665 string connectionGroupPrefixSafe = HttpWebRequest.GenerateConnectionGroup(connectionGroupName, false, true).ToString();
666 string connectionGroupPrefixUnsafe = HttpWebRequest.GenerateConnectionGroup(connectionGroupName, true, true).ToString();
667 List<string> connectionGroupNames = null;
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>();
679 connectionGroupNames.Add(current);
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);
694 /// Gets or sets the maximum number of connections allowed on this <see cref='System.Net.ServicePoint'/>.
697 public int ConnectionLimit
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)
705 // This can only happen the first time through, and before any ConnectionGroups are made.
708 if (!m_UserChangedLimit && m_IPAddressInfoList == null && m_HostLoopbackGuess == TriState.Unspecified)
710 // First check if it's just an IP address anyway.
711 IPAddress addr = null;
712 if (IPAddress.TryParse(m_Host,out addr))
714 m_HostLoopbackGuess = IsAddressListLoopback(new IPAddress[] { addr }) ? TriState.True : TriState.False;
718 m_HostLoopbackGuess = NclUtilities.GuessWhetherHostIsLoopback(m_Host) ? TriState.True : TriState.False;
724 return m_UserChangedLimit || (m_IPAddressInfoList == null ? m_HostLoopbackGuess != TriState.True : !m_IPAddressesAreLoopback) ? m_ConnectionLimit : LoopbackConnectionLimit;
731 throw new ArgumentOutOfRangeException("value");
734 if (!m_UserChangedLimit || m_ConnectionLimit != value)
738 if (!m_UserChangedLimit || m_ConnectionLimit != value)
740 m_ConnectionLimit = value;
741 m_UserChangedLimit = true;
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();
752 // Must be called under lock.
753 private void ResolveConnectionLimit()
755 int limit = ConnectionLimit;
756 foreach (ConnectionGroup cg in m_ConnectionGroupList.Values)
758 cg.ConnectionLimit = limit;
764 /// Gets the current number of connections associated with this
765 /// <see cref='System.Net.ServicePoint'/>.
768 public int CurrentConnections {
773 foreach (ConnectionGroup group in m_ConnectionGroupList.Values)
775 connections += group.CurrentConnections;
785 /// Gets the certificate received for this <see cref='System.Net.ServicePoint'/>.
788 public X509Certificate Certificate {
790 object chkCert = m_ServerCertificateOrBytes;
791 if (chkCert != null && chkCert.GetType() == typeof(byte[]))
792 return (X509Certificate)(m_ServerCertificateOrBytes = new X509Certificate((byte[]) chkCert));
794 return chkCert as X509Certificate;
797 internal void UpdateServerCertificate(X509Certificate certificate)
799 if (certificate != null)
800 m_ServerCertificateOrBytes = certificate.GetRawCertData();
802 m_ServerCertificateOrBytes = null;
807 /// Gets the Client Certificate sent by us to the Server.
810 public X509Certificate ClientCertificate {
812 object chkCert = m_ClientCertificateOrBytes;
813 if (chkCert != null && chkCert.GetType() == typeof(byte[]))
814 return (X509Certificate)(m_ClientCertificateOrBytes = new X509Certificate((byte[]) chkCert));
816 return chkCert as X509Certificate;
819 internal void UpdateClientCertificate(X509Certificate certificate)
821 if (certificate != null)
822 m_ClientCertificateOrBytes = certificate.GetRawCertData();
824 m_ClientCertificateOrBytes = null;
827 #endif // !FEATURE_PAL
832 /// Indicates that the <see cref='System.Net.ServicePoint'/> supports pipelined connections.
835 public bool SupportsPipelining {
837 return (m_HttpBehaviour>HttpBehaviour.HTTP10 || m_HttpBehaviour==HttpBehaviour.Unknown);
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
851 // enabled - if true enables the use of the TCP keepalive option
852 // for ServicePoint connections.
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.
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.
862 public void SetTcpKeepAlive(
865 int keepAliveInterval) {
868 "ServicePoint::SetTcpKeepAlive()" +
869 " enabled: " + enabled.ToString() +
870 " keepAliveTime: " + keepAliveTime.ToString() +
871 " keepAliveInterval: " + keepAliveInterval.ToString()
874 m_UseTcpKeepAlive = true;
875 if (keepAliveTime <= 0) {
876 throw new ArgumentOutOfRangeException("keepAliveTime");
878 if (keepAliveInterval <= 0) {
879 throw new ArgumentOutOfRangeException("keepAliveInterval");
881 m_TcpKeepAliveTime = keepAliveTime;
882 m_TcpKeepAliveInterval = keepAliveInterval;
884 m_UseTcpKeepAlive = false;
885 m_TcpKeepAliveTime = 0;
886 m_TcpKeepAliveInterval =0;
888 GlobalLog.Leave("ServicePoint::SetTcpKeepAlive()");
892 // Internal Properties
895 internal bool Understands100Continue {
897 m_Understands100Continue = value;
900 return m_Understands100Continue;
905 // InternalProxyServicePoint
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
912 internal bool InternalProxyServicePoint {
914 return m_ProxyServicePoint;
919 // IncrementConnection
921 // call to indicate that we now are starting a new
922 // connection within this service point
925 internal void IncrementConnection() {
926 GlobalLog.Enter("ServicePoint#" + ValidationHelper.HashString(this) + "::IncrementConnection()", m_CurrentConnections.ToString());
927 // we need these to be atomic operations
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));
938 m_ExpiringTimer.Cancel();
939 m_ExpiringTimer = null;
942 GlobalLog.Leave("ServicePoint#" + ValidationHelper.HashString(this) + "::IncrementConnection()", m_CurrentConnections.ToString());
946 // DecrementConnection
948 // call to indicate that we now are removing
949 // a connection within this connection group
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());
957 // we need these to be atomic operations
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);
965 else if ( m_CurrentConnections < 0 ) {
966 m_CurrentConnections = 0;
967 Diagnostics.Debug.Assert(false, "ServicePoint; Too many decrements.");
970 GlobalLog.Leave("ServicePoint#" + ValidationHelper.HashString(this) + "::DecrementConnection()", m_CurrentConnections.ToString());
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);
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;
983 ServicePoint m_ServicePoint;
985 internal static RemoteCertValidationCallback CreateAdapter(ServicePoint serviePoint, TlsStream secureStream, Object request)
987 HandshakeDoneProcedure adapter = new HandshakeDoneProcedure(serviePoint, secureStream, request);
988 return new RemoteCertValidationCallback(adapter.CertValidationCallback);
991 private HandshakeDoneProcedure (ServicePoint serviePoint, TlsStream secureStream, Object request) {
992 m_ServicePoint = serviePoint;
993 m_SecureStream = secureStream;
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;
1002 // Request specific validator takes priority
1003 HttpWebRequest httpWebRequest = m_Request as HttpWebRequest;
1004 if (httpWebRequest != null && httpWebRequest.ServerCertValidationCallback != null)
1006 return httpWebRequest.ServerCertValidationCallback.
1013 // If a policy is set, call the user callback inside the ExecutionContext.
1014 if (ServicePointManager.GetLegacyCertificatePolicy() != null && (m_Request is WebRequest))
1018 bool checkResult = ServicePointManager.CertPolicyValidationCallback.
1022 (WebRequest) m_Request,
1026 if (checkResult == false){
1027 if (!ServicePointManager.CertPolicyValidationCallback.UsesDefault
1028 || ServicePointManager.ServerCertificateValidationCallback == null)
1033 if (ServicePointManager.ServerCertificateValidationCallback != null)
1036 return ServicePointManager.ServerCertValidationCallback.
1044 return sslPolicyErrors == SslPolicyErrors.None;
1052 #endif // !FEATURE_PAL
1054 private void IdleConnectionGroupTimeoutCallback(TimerThread.Timer timer, int timeNoticed, object context) {
1055 ConnectionGroup connectionGroup = (ConnectionGroup)context;
1057 if (Logging.On) Logging.PrintInfo(Logging.Web, this, SR.GetString(SR.net_log_closed_idle,
1058 "ConnectionGroup", connectionGroup.GetHashCode()));
1060 ReleaseConnectionGroup(connectionGroup.Name);
1063 internal TimerThread.Timer CreateConnectionGroupTimer(ConnectionGroup connectionGroup) {
1064 return m_IdlingQueue.CreateTimer(m_IdleConnectionGroupTimeoutDelegate, connectionGroup);
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.
1074 internal bool ReleaseConnectionGroup(string connName) {
1076 ConnectionGroup connectionGroup = null;
1079 // look up the ConnectionGroup based on the name
1083 connectionGroup = FindConnectionGroup(connName, true);
1085 // force all connections on the ConnectionGroup to not be KeepAlive
1087 if (connectionGroup == null) {
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();
1096 // remove ConnectionGroup from our Hashtable
1098 m_ConnectionGroupList.Remove(connName);
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();
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
1117 internal void ReleaseAllConnectionGroups()
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");
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);
1127 foreach (ConnectionGroup cg in m_ConnectionGroupList.Values)
1131 m_ConnectionGroupList.Clear();
1133 foreach (ConnectionGroup cg in cgs)
1135 cg.DisableKeepAliveOnConnections();
1141 /// <para>Internal class, used to store state for async Connect</para>
1143 private class ConnectSocketState {
1144 internal ConnectSocketState(ServicePoint servicePoint, PooledStream pooledStream, object owner, Socket s4, Socket s6)
1146 this.servicePoint = servicePoint;
1147 this.pooledStream = pooledStream;
1152 internal ServicePoint servicePoint;
1155 internal object owner;
1156 internal IPAddress[] addresses;
1157 internal int currentIndex;
1159 internal int unsuccessfulAttempts;
1160 internal bool connectFailure;
1161 internal PooledStream pooledStream;
1166 /// <para>Proviates an async callback that is called when Connect completes [part of ConnectSocket(...)]</para>
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;
1178 ws = state.servicePoint.ConnectSocketInternal(state.connectFailure, state.s4, state.s6, ref socket, ref address, state, asyncResult, out innerException);
1180 catch (SocketException socketException) {
1181 exception = socketException;
1183 catch (ObjectDisposedException socketException) {
1184 exception = socketException;
1187 if (ws == WebExceptionStatus.Pending) {
1191 if (ws == WebExceptionStatus.Success) {
1193 state.servicePoint.CompleteGetConnection(state.s4, state.s6, socket, address);
1195 catch (SocketException socketException) {
1196 exception = socketException;
1198 catch (ObjectDisposedException socketException) {
1199 exception = socketException;
1204 exception = new WebException(
1205 NetRes.GetWebStatusString(ws),
1206 ws == WebExceptionStatus.ProxyNameResolutionFailure || ws == WebExceptionStatus.NameResolutionFailure ? state.servicePoint.Host : null,
1209 null, /* no response */
1210 WebExceptionInternalStatus.ServicePointFatal);
1213 state.pooledStream.ConnectionCallback(state.owner, exception, socket, address);
1217 if (socket != null && socket.CleanedUp)
1218 return; // The connection was aborted and requests dispatched
1224 private void BindUsingDelegate(Socket socket, IPEndPoint remoteIPEndPoint)
1226 IPEndPoint clonedRemoteIPEndPoint = new IPEndPoint(remoteIPEndPoint.Address, remoteIPEndPoint.Port);
1229 for (retryCount=0; retryCount<int.MaxValue; retryCount++) {
1230 IPEndPoint localIPEndPoint = BindIPEndPointDelegate(this, clonedRemoteIPEndPoint, retryCount);
1231 if (localIPEndPoint == null)
1235 socket.InternalBind(localIPEndPoint);
1242 if (retryCount == int.MaxValue)
1243 throw new OverflowException("Reached maximum number of BindIPEndPointDelegate retries");
1248 /// <para>Set SocketOptionName.ReuseUnicastPort (SO_REUSE_UNICASTPORT) socket option on the outbound connection.</para>
1250 private void SetUnicastReusePortForSocket(Socket socket)
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.
1260 reusePort = ServicePointManager.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.
1267 socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseUnicastPort, 0x1);
1269 Logging.PrintInfo(Logging.Web, this, SR.GetString(SR.net_log_set_socketoption_reuseport,
1270 "Socket", socket.GetHashCode()));
1273 ServicePointManager.ReusePortSupported = true;
1275 catch (SocketException) {
1276 // The socket option is not supported. We will ignore this error and fail gracefully.
1278 Logging.PrintInfo(Logging.Web, this, SR.GetString(SR.net_log_set_socketoption_reuseport_not_supported,
1279 "Socket", socket.GetHashCode()));
1281 ServicePointManager.ReusePortSupported = false;
1283 catch (Exception ex) {
1284 // We want to preserve app compat and trap any other unusual exceptions.
1286 Logging.PrintInfo(Logging.Web, this, SR.GetString(SR.net_log_unexpected_exception, ex.Message));
1293 /// <para>This is the real logic for doing the Connect with IPv4 and IPv6 addresses, see ConnectSocket for details</para>
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;
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.
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.
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++) {
1323 // Use asyncResult to make sure it is only called at initiation time.
1324 if (asyncResult == null)
1326 // the second time, determine if the list was recent.
1328 addresses = GetIPAddressInfoList(out currentIndex, addresses);
1330 //the addresses were recent, or we couldn't resolve the addresses.
1331 if (addresses == null || addresses.Length == 0)
1336 GlobalLog.Print("ServicePoint#" + ValidationHelper.HashString(this) + "::ConnectSocketInternal() resuming previous state");
1338 addresses = state.addresses;
1339 currentIndex = state.currentIndex;
1341 unsuccessfulAttempts = state.unsuccessfulAttempts;
1344 //otherwise, try all of the addresses in the list.
1345 for (; i < addresses.Length; i++)
1347 IPAddress ipAddressInfo = addresses[currentIndex];
1349 remoteIPEndPoint = new IPEndPoint(ipAddressInfo, m_Port);
1350 Socket attemptSocket;
1351 if ( remoteIPEndPoint.Address.AddressFamily==AddressFamily.InterNetwork ) {
1360 if (asyncResult != null)
1362 IAsyncResult asyncResultCopy = asyncResult;
1364 attemptSocket.EndConnect(asyncResultCopy);
1367 GlobalLog.Print("ServicePoint#" + ValidationHelper.HashString(this) + "::ConnectSocketInternal() calling BeginConnect() to:" + remoteIPEndPoint.ToString());
1369 // save off our state and do our async call
1370 state.addresses = addresses;
1371 state.currentIndex = currentIndex;
1373 state.unsuccessfulAttempts = unsuccessfulAttempts;
1374 state.connectFailure = connectFailure;
1376 if (!attemptSocket.IsBound) {
1377 if (ServicePointManager.ReusePort) {
1378 SetUnicastReusePortForSocket(attemptSocket);
1381 if (BindIPEndPointDelegate != null) {
1382 BindUsingDelegate(attemptSocket, remoteIPEndPoint);
1386 attemptSocket.UnsafeBeginConnect(remoteIPEndPoint, m_ConnectCallbackDelegate, state);
1387 return WebExceptionStatus.Pending;
1391 if (!attemptSocket.IsBound) {
1392 if (ServicePointManager.ReusePort) {
1393 SetUnicastReusePortForSocket(attemptSocket);
1396 if (BindIPEndPointDelegate != null) {
1397 BindUsingDelegate(attemptSocket, remoteIPEndPoint);
1401 attemptSocket.InternalConnect(remoteIPEndPoint);
1403 socket = attemptSocket;
1404 address = ipAddressInfo;
1406 UpdateCurrentIndex(addresses, currentIndex);
1407 return WebExceptionStatus.Success;
1409 catch (ObjectDisposedException)
1411 // This can happen if the request has been aborted and the attemptSocket got closed.
1412 return WebExceptionStatus.RequestCanceled;
1416 if (NclUtilities.IsFatal(e)) throw;
1419 connectFailure = true;
1422 if (currentIndex >= addresses.Length) {
1430 return connectFailure ? WebExceptionStatus.ConnectFailure :
1431 InternalProxyServicePoint ? WebExceptionStatus.ProxyNameResolutionFailure :
1432 WebExceptionStatus.NameResolutionFailure;
1436 /// <para>private implimentation of ConnectSocket(...)</para>
1438 private WebExceptionStatus ConnectSocket(Socket s4, Socket s6, ref Socket socket, ref IPAddress address,
1439 ConnectSocketState state, out Exception exception) {
1441 // we need this for the call to connect()
1443 return ConnectSocketInternal(false, s4, s6, ref socket, ref address, state, null, out exception);
1446 [System.Diagnostics.Conditional("DEBUG")]
1447 internal void DebugMembers(int requestHash) {
1448 foreach(ConnectionGroup connectGroup in m_ConnectionGroupList.Values) {
1449 if (connectGroup!=null) {
1451 connectGroup.DebugMembers(requestHash);
1461 // Previously: class IPHostInformation
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;
1473 private void Failed(IPAddress[] addresses)
1475 if (addresses == m_IPAddressInfoList){
1477 if (addresses == m_IPAddressInfoList){
1478 m_AddressListFailed = true;
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)
1490 if (addresses == m_IPAddressInfoList && (m_CurrentAddressInfoIndex != currentIndex || !m_ConnectedSinceDns)){
1492 if (addresses == m_IPAddressInfoList){
1493 if (!ServicePointManager.EnableDnsRoundRobin ) {
1494 m_CurrentAddressInfoIndex = currentIndex;
1496 m_ConnectedSinceDns = true;
1503 private bool HasTimedOut {
1505 int dnsRefreshTimeout = ServicePointManager.DnsRefreshTimeout;
1506 return dnsRefreshTimeout != Timeout.Infinite &&
1507 (m_LastDnsResolve + new TimeSpan(0, 0, 0, 0, dnsRefreshTimeout)) < DateTime.UtcNow;
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.
1517 private IPAddress[] GetIPAddressInfoList(out int currentIndex, IPAddress[] addresses)
1519 IPHostEntry ipHostEntry = null;
1521 bool needDnsResolution = false;
1522 bool dnsResolutionFailed = false;
1524 // Phase 1: Decide if we need to do a DNS resolution
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.
1530 if (addresses != null && !m_ConnectedSinceDns && !m_AddressListFailed && addresses == m_IPAddressInfoList)
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;
1540 needDnsResolution = true;
1544 // Phase 2: If we have to do a DNS resolution now, then do it now
1545 if (needDnsResolution) {
1547 dnsResolutionFailed = !Dns.TryInternalResolve(m_Host, out ipHostEntry);
1549 catch (Exception exception)
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());
1557 // Phase 3: If we did a DNS resolution, then deal with the results safely under a lock
1559 if (needDnsResolution) {
1561 m_IPAddressInfoList = null;
1563 if (!dnsResolutionFailed) {
1564 if (ipHostEntry!=null && ipHostEntry.AddressList!=null && ipHostEntry.AddressList.Length>0) {
1565 SetAddressList(ipHostEntry);
1568 GlobalLog.Print("IPHostInformation#" + ValidationHelper.HashString(this) + "::GetIPAddressInfoList() Dns.InternalResolveFast() failed with null");
1571 GlobalLog.Print("IPHostInformation#" + ValidationHelper.HashString(this) + "::GetIPAddressInfoList() Dns.InternalResolveFast() had thrown an exception");
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;
1579 //auto increment index for next connect request if round robin is enabled
1580 if (ServicePointManager.EnableDnsRoundRobin)
1582 m_CurrentAddressInfoIndex++;
1583 if (m_CurrentAddressInfoIndex >= m_IPAddressInfoList.Length) {
1584 m_CurrentAddressInfoIndex = 0;
1587 return m_IPAddressInfoList;
1590 GlobalLog.Print("IPHostInformation#" + ValidationHelper.HashString(this) + "::GetIPAddressInfoList() GetIPAddressInfoList returning null");
1595 // Called under lock(this)
1597 private void SetAddressList(IPHostEntry ipHostEntry)
1599 GlobalLog.Print("IPHostInformation#" + ValidationHelper.HashString(this) + "::SetAddressList("+ipHostEntry.HostName+")");
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
1607 // Only update the member with successfull final result.
1608 // In case of an exception m_IPAddressInfoList will stay as null
1610 bool wasLoopback = m_IPAddressesAreLoopback;
1611 bool wasNull = m_IPAddressInfoList == null;
1613 m_IPAddressesAreLoopback = IsAddressListLoopback(ipHostEntry.AddressList);
1614 m_IPAddressInfoList = ipHostEntry.AddressList;
1615 m_HostName = ipHostEntry.HostName;
1616 m_IsTrustedHost = ipHostEntry.isTrustedHost;
1618 if (wasNull || wasLoopback != m_IPAddressesAreLoopback)
1620 ResolveConnectionLimit();
1624 private static bool IsAddressListLoopback(IPAddress[] addressList)
1626 GlobalLog.Print("IPHostInformation::CheckAddressList(" + addressList.Length + ")");
1629 // Walk through each member of the input list, copying it into our temp array.
1633 IPAddress[] localAddresses = null;
1635 localAddresses = NclUtilities.LocalAddresses;
1637 catch (Exception exception)
1639 if (NclUtilities.IsFatal(exception)) throw;
1641 // ATTN: If LocalAddresses has failed terribly we will treat just resolved name as a remote server.
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));
1651 for (i = 0; i < addressList.Length; i++)
1653 // First, check to see if the current address is a loopback address.
1654 if (IPAddress.IsLoopback(addressList[i]))
1659 if (localAddresses != null)
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++)
1666 // IPv6 Changes: Use .Equals for this check !
1668 if (addressList[i].Equals(localAddresses[k]))
1673 if (k < localAddresses.Length)
1682 return i == addressList.Length;