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 BindIPEndPoint(ServicePoint servicePoint, IPEndPoint remoteEndPoint, int retryCount)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 MONO_FEATURE_CAS 97 ExceptionHelper.InfrastructurePermission.Demand(); 98 #endif 99 m_BindIPEndPointDelegate = value; 100 } 101 } 102 103 // 104 // constructors 105 // ServicePoint(Uri address, TimerThread.Queue defaultIdlingQueue, int defaultConnectionLimit, string lookupString, bool userChangedLimit, bool proxyServicePoint)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 ServicePoint(string host, int port, TimerThread.Queue defaultIdlingQueue, int defaultConnectionLimit, string lookupString, bool userChangedLimit, bool proxyServicePoint)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 SetCachedChannelBinding(Uri uri, ChannelBinding binding)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 FindConnectionGroup(string connName, bool dontCreate)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> GetConnection(PooledStream PooledStream, object owner, bool async, out IPAddress address, ref Socket abortSocket, ref Socket abortSocket6)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 sync 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> CompleteGetConnection(Socket socket, Socket socket6, Socket finalSocket, IPAddress address)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 SubmitRequest(HttpWebRequest request)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. SubmitRequest(HttpWebRequest request, string connName)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 MONO_FEATURE_CAS 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 CloseConnectionGroup(string connectionGroupName)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 CloseConnectionGroupInternal(string connectionGroupName)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. ResolveConnectionLimit()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 } UpdateServerCertificate(X509Certificate certificate)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 } UpdateClientCertificate(X509Certificate certificate)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 // SetTcpKeepAlive( bool enabled, int keepAliveTime, int keepAliveInterval)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 IncrementConnection()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 DecrementConnection()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 SetupHandshakeDoneProcedure(TlsStream secureStream, Object request)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 CreateAdapter(ServicePoint serviePoint, TlsStream secureStream, Object request)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 HandshakeDoneProcedure(ServicePoint serviePoint, TlsStream secureStream, Object request)995 private HandshakeDoneProcedure (ServicePoint serviePoint, TlsStream secureStream, Object request) { 996 m_ServicePoint = serviePoint; 997 m_SecureStream = secureStream; 998 m_Request = request; 999 } 1000 CertValidationCallback(string hostName, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)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 IdleConnectionGroupTimeoutCallback(TimerThread.Timer timer, int timeNoticed, object context)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 CreateConnectionGroupTimer(ConnectionGroup connectionGroup)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> ReleaseConnectionGroup(string connName)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> ReleaseAllConnectionGroups()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 { ConnectSocketState(ServicePoint servicePoint, PooledStream pooledStream, object owner, Socket s4, Socket s6)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> ConnectSocketCallback(IAsyncResult asyncResult)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 BindUsingDelegate(Socket socket, IPEndPoint remoteIPEndPoint)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> SetUnicastReusePortForSocket(Socket socket)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> ConnectSocketInternal(bool connectFailure, Socket s4, Socket s6, ref Socket socket, ref IPAddress address, ConnectSocketState state, IAsyncResult asyncResult, out Exception exception)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> ConnectSocket(Socket s4, Socket s6, ref Socket socket, ref IPAddress address, ConnectSocketState state, out Exception exception)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")] DebugMembers(int requestHash)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 Failed(IPAddress[] addresses)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 UpdateCurrentIndex(IPAddress[] addresses, int currentIndex)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 GetIPAddressInfoList(out int currentIndex, IPAddress[] addresses)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 // SetAddressList(IPHostEntry ipHostEntry)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 IsAddressListLoopback(IPAddress[] addressList)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 } 1690