1 //------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //------------------------------------------------------------
4 
5 namespace System.ServiceModel.Channels
6 {
7     using System.Collections.Generic;
8     using System.Diagnostics;
9     using System.Net;
10     using System.Net.Sockets;
11     using System.Runtime;
12     using System.Runtime.Diagnostics;
13     using System.Runtime.InteropServices;
14     using System.Security;
15     using System.Security.Permissions;
16     using System.ServiceModel;
17     using System.ServiceModel.Diagnostics;
18     using System.ServiceModel.Diagnostics.Application;
19     using System.Text;
20     using System.Threading;
21 
22     class SocketConnection : IConnection
23     {
24         static AsyncCallback onReceiveCompleted;
25         static EventHandler<SocketAsyncEventArgs> onReceiveAsyncCompleted;
26         static EventHandler<SocketAsyncEventArgs> onSocketSendCompleted;
27 
28         // common state
29         Socket socket;
30         TimeSpan asyncSendTimeout;
31         TimeSpan readFinTimeout;
32         TimeSpan asyncReceiveTimeout;
33 
34         // Socket.SendTimeout/Socket.ReceiveTimeout only work with the synchronous API calls and therefore they
35         // do not get updated when asynchronous Send/Read operations are performed.  In order to make sure we
36         // Set the proper timeouts on the Socket itself we need to keep these two additional fields.
37         TimeSpan socketSyncSendTimeout;
38         TimeSpan socketSyncReceiveTimeout;
39 
40         CloseState closeState;
41         bool isShutdown;
42         bool noDelay = false;
43         bool aborted;
44         TraceEventType exceptionEventType;
45 
46         // close state
47         TimeoutHelper closeTimeoutHelper;
48         static WaitCallback onWaitForFinComplete = new WaitCallback(OnWaitForFinComplete);
49 
50         // read state
51         int asyncReadSize;
52         SocketAsyncEventArgs asyncReadEventArgs;
53         byte[] readBuffer;
54         int asyncReadBufferSize;
55         object asyncReadState;
56         WaitCallback asyncReadCallback;
57         Exception asyncReadException;
58         bool asyncReadPending;
59 
60         // write state
61         SocketAsyncEventArgs asyncWriteEventArgs;
62         object asyncWriteState;
63         WaitCallback asyncWriteCallback;
64         Exception asyncWriteException;
65         bool asyncWritePending;
66 
67         IOThreadTimer receiveTimer;
68         static Action<object> onReceiveTimeout;
69         IOThreadTimer sendTimer;
70         static Action<object> onSendTimeout;
71         string timeoutErrorString;
72         TransferOperation timeoutErrorTransferOperation;
73         IPEndPoint remoteEndpoint;
74         ConnectionBufferPool connectionBufferPool;
75         string remoteEndpointAddress;
76 
SocketConnection(Socket socket, ConnectionBufferPool connectionBufferPool, bool autoBindToCompletionPort)77         public SocketConnection(Socket socket, ConnectionBufferPool connectionBufferPool, bool autoBindToCompletionPort)
78         {
79             if (socket == null)
80             {
81                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("socket");
82             }
83 
84             Fx.Assert(connectionBufferPool != null, "Argument connectionBufferPool cannot be null");
85 
86             this.closeState = CloseState.Open;
87             this.exceptionEventType = TraceEventType.Error;
88             this.socket = socket;
89             this.connectionBufferPool = connectionBufferPool;
90             this.readBuffer = this.connectionBufferPool.Take();
91             this.asyncReadBufferSize = this.readBuffer.Length;
92             this.socket.SendBufferSize = this.socket.ReceiveBufferSize = this.asyncReadBufferSize;
93             this.asyncSendTimeout = this.asyncReceiveTimeout = TimeSpan.MaxValue;
94             this.socketSyncSendTimeout = this.socketSyncReceiveTimeout = TimeSpan.MaxValue;
95 
96             this.remoteEndpoint = null;
97 
98             if (autoBindToCompletionPort)
99             {
100                 this.socket.UseOnlyOverlappedIO = false;
101             }
102 
103             // In SMSvcHost, sockets must be duplicated to the target process. Binding a handle to a completion port
104             // prevents any duplicated handle from ever binding to a completion port. The target process is where we
105             // want to use completion ports for performance. This means that in SMSvcHost, socket.UseOnlyOverlappedIO
106             // must be set to true to prevent completion port use.
107             if (this.socket.UseOnlyOverlappedIO)
108             {
109                 // Init BeginRead state
110                 if (onReceiveCompleted == null)
111                 {
112                     onReceiveCompleted = Fx.ThunkCallback(new AsyncCallback(OnReceiveCompleted));
113                 }
114             }
115 
116             this.TraceSocketInfo(socket, TraceCode.SocketConnectionCreate, SR.TraceCodeSocketConnectionCreate, null);
117         }
118         public int AsyncReadBufferSize
119         {
120             get { return asyncReadBufferSize; }
121         }
122 
123         public byte[] AsyncReadBuffer
124         {
125             get
126             {
127                 return readBuffer;
128             }
129         }
130 
131         object ThisLock
132         {
133             get { return this; }
134         }
135 
136         public TraceEventType ExceptionEventType
137         {
138             get { return this.exceptionEventType; }
139             set { this.exceptionEventType = value; }
140         }
141 
142         public IPEndPoint RemoteIPEndPoint
143         {
144             get
145             {
146                 // this property should only be called on the receive path
147                 if (remoteEndpoint == null && this.closeState == CloseState.Open)
148                 {
149                     try
150                     {
151                         remoteEndpoint = (IPEndPoint)socket.RemoteEndPoint;
152                     }
153                     catch (SocketException socketException)
154                     {
155                         // will never be a timeout error, so TimeSpan.Zero is ok
156 #pragma warning suppress 56503 // Called from Receive path, SocketConnection cannot allow a SocketException to escape.
157                         throw DiagnosticUtility.ExceptionUtility.ThrowHelper(
158                             ConvertReceiveException(socketException, TimeSpan.Zero, TimeSpan.Zero), ExceptionEventType);
159                     }
160                     catch (ObjectDisposedException objectDisposedException)
161                     {
162                         Exception exceptionToThrow = ConvertObjectDisposedException(objectDisposedException, TransferOperation.Undefined);
163                         if (object.ReferenceEquals(exceptionToThrow, objectDisposedException))
164                         {
165 #pragma warning suppress 56503 // rethrow
166                             throw;
167                         }
168                         else
169                         {
170 #pragma warning suppress 56503 // Called from Receive path, SocketConnection must convert ObjectDisposedException properly.
171                             throw DiagnosticUtility.ExceptionUtility.ThrowHelper(exceptionToThrow, ExceptionEventType);
172                         }
173                     }
174                 }
175 
176                 return remoteEndpoint;
177             }
178         }
179 
180         IOThreadTimer SendTimer
181         {
182             get
183             {
184                 if (this.sendTimer == null)
185                 {
186                     if (onSendTimeout == null)
187                     {
188                         onSendTimeout = new Action<object>(OnSendTimeout);
189                     }
190 
191                     this.sendTimer = new IOThreadTimer(onSendTimeout, this, false);
192                 }
193 
194                 return this.sendTimer;
195             }
196         }
197 
198         IOThreadTimer ReceiveTimer
199         {
200             get
201             {
202                 if (this.receiveTimer == null)
203                 {
204                     if (onReceiveTimeout == null)
205                     {
206                         onReceiveTimeout = new Action<object>(OnReceiveTimeout);
207                     }
208 
209                     this.receiveTimer = new IOThreadTimer(onReceiveTimeout, this, false);
210                 }
211 
212                 return this.receiveTimer;
213             }
214         }
215 
216 
217         string RemoteEndpointAddress
218         {
219             get
220             {
221                 if (remoteEndpointAddress == null)
222                 {
223                     try
224                     {
225                         IPEndPoint local, remote;
226                         if (TryGetEndpoints(out local, out remote))
227                         {
228                             this.remoteEndpointAddress = TraceUtility.GetRemoteEndpointAddressPort(remote);
229                         }
230                         else
231                         {
232                             //null indicates not initialized.
233                             remoteEndpointAddress = string.Empty;
234                         }
235                     }
236                     catch (Exception exception)
237                     {
238                         if (Fx.IsFatal(exception))
239                         {
240                             throw;
241                         }
242 
243                     }
244                 }
245                 return remoteEndpointAddress;
246             }
247         }
248 
OnReceiveTimeout(object state)249         static void OnReceiveTimeout(object state)
250         {
251             SocketConnection thisPtr = (SocketConnection)state;
252             thisPtr.Abort(SR.GetString(SR.SocketAbortedReceiveTimedOut, thisPtr.asyncReceiveTimeout), TransferOperation.Read);
253         }
254 
OnSendTimeout(object state)255         static void OnSendTimeout(object state)
256         {
257             SocketConnection thisPtr = (SocketConnection)state;
258             thisPtr.Abort(TraceEventType.Warning,
259                 SR.GetString(SR.SocketAbortedSendTimedOut, thisPtr.asyncSendTimeout), TransferOperation.Write);
260         }
261 
OnReceiveCompleted(IAsyncResult result)262         static void OnReceiveCompleted(IAsyncResult result)
263         {
264             ((SocketConnection)result.AsyncState).OnReceive(result);
265         }
266 
OnReceiveAsyncCompleted(object sender, SocketAsyncEventArgs e)267         static void OnReceiveAsyncCompleted(object sender, SocketAsyncEventArgs e)
268         {
269             ((SocketConnection)e.UserToken).OnReceiveAsync(sender, e);
270         }
271 
OnSendAsyncCompleted(object sender, SocketAsyncEventArgs e)272         static void OnSendAsyncCompleted(object sender, SocketAsyncEventArgs e)
273         {
274             ((SocketConnection)e.UserToken).OnSendAsync(sender, e);
275         }
276 
Abort()277         public void Abort()
278         {
279             Abort(null, TransferOperation.Undefined);
280         }
281 
Abort(string timeoutErrorString, TransferOperation transferOperation)282         void Abort(string timeoutErrorString, TransferOperation transferOperation)
283         {
284             TraceEventType traceEventType = TraceEventType.Warning;
285 
286             // we could be timing out a cached connection
287             if (this.ExceptionEventType == TraceEventType.Information)
288             {
289                 traceEventType = this.ExceptionEventType;
290             }
291 
292             Abort(traceEventType, timeoutErrorString, transferOperation);
293         }
294 
Abort(TraceEventType traceEventType)295         void Abort(TraceEventType traceEventType)
296         {
297             Abort(traceEventType, null, TransferOperation.Undefined);
298         }
299 
Abort(TraceEventType traceEventType, string timeoutErrorString, TransferOperation transferOperation)300         void Abort(TraceEventType traceEventType, string timeoutErrorString, TransferOperation transferOperation)
301         {
302             if (TD.SocketConnectionAbortIsEnabled())
303             {
304                 TD.SocketConnectionAbort(this.socket.GetHashCode());
305             }
306             lock (ThisLock)
307             {
308                 if (closeState == CloseState.Closed)
309                 {
310                     return;
311                 }
312 
313                 this.timeoutErrorString = timeoutErrorString;
314                 this.timeoutErrorTransferOperation = transferOperation;
315                 aborted = true;
316                 closeState = CloseState.Closed;
317 
318                 if (this.asyncReadPending)
319                 {
320                     CancelReceiveTimer();
321                 }
322                 else
323                 {
324                     this.DisposeReadEventArgs();
325                 }
326 
327                 if (this.asyncWritePending)
328                 {
329                     CancelSendTimer();
330                 }
331                 else
332                 {
333                     this.DisposeWriteEventArgs();
334                 }
335             }
336 
337             if (DiagnosticUtility.ShouldTrace(traceEventType))
338             {
339                 TraceUtility.TraceEvent(traceEventType, TraceCode.SocketConnectionAbort,
340                     SR.GetString(SR.TraceCodeSocketConnectionAbort), this);
341             }
342 
343             socket.Close(0);
344         }
345 
AbortRead()346         void AbortRead()
347         {
348             lock (ThisLock)
349             {
350                 if (this.asyncReadPending)
351                 {
352                     if (closeState != CloseState.Closed)
353                     {
354                         this.SetUserToken(this.asyncReadEventArgs, null);
355                         this.asyncReadPending = false;
356                         CancelReceiveTimer();
357                     }
358                     else
359                     {
360                         this.DisposeReadEventArgs();
361                     }
362                 }
363             }
364         }
365 
CancelReceiveTimer()366         void CancelReceiveTimer()
367         {
368             // CSDMain 34539: Snapshot the timer so that we don't null ref if there is a ----
369             // between calls to CancelReceiveTimer (e.g., Abort, AsyncReadCallback)
370 
371             IOThreadTimer receiveTimerSnapshot = this.receiveTimer;
372             this.receiveTimer = null;
373 
374             if (receiveTimerSnapshot != null)
375             {
376                 receiveTimerSnapshot.Cancel();
377             }
378         }
379 
CancelSendTimer()380         void CancelSendTimer()
381         {
382             IOThreadTimer sendTimerSnapshot = this.sendTimer;
383             this.sendTimer = null;
384 
385             if (sendTimerSnapshot != null)
386             {
387                 sendTimerSnapshot.Cancel();
388             }
389         }
390 
CloseAsyncAndLinger()391         void CloseAsyncAndLinger()
392         {
393             readFinTimeout = closeTimeoutHelper.RemainingTime();
394 
395             try
396             {
397                 if (BeginReadCore(0, 1, readFinTimeout, onWaitForFinComplete, this) == AsyncCompletionResult.Queued)
398                 {
399                     return;
400                 }
401 
402                 int bytesRead = EndRead();
403 
404                 if (bytesRead > 0)
405                 {
406                     throw DiagnosticUtility.ExceptionUtility.ThrowHelper(
407                         new CommunicationException(SR.GetString(SR.SocketCloseReadReceivedData, socket.RemoteEndPoint)),
408                         ExceptionEventType);
409                 }
410             }
411             catch (TimeoutException timeoutException)
412             {
413                 throw DiagnosticUtility.ExceptionUtility.ThrowHelper(new TimeoutException(
414                     SR.GetString(SR.SocketCloseReadTimeout, socket.RemoteEndPoint, readFinTimeout), timeoutException),
415                     ExceptionEventType);
416             }
417 
418             ContinueClose(closeTimeoutHelper.RemainingTime());
419         }
420 
OnWaitForFinComplete(object state)421         static void OnWaitForFinComplete(object state)
422         {
423             SocketConnection thisPtr = (SocketConnection)state;
424 
425             try
426             {
427                 int bytesRead;
428 
429                 try
430                 {
431                     bytesRead = thisPtr.EndRead();
432 
433                     if (bytesRead > 0)
434                     {
435                         throw DiagnosticUtility.ExceptionUtility.ThrowHelper(
436                             new CommunicationException(SR.GetString(SR.SocketCloseReadReceivedData, thisPtr.socket.RemoteEndPoint)),
437                             thisPtr.ExceptionEventType);
438                     }
439                 }
440                 catch (TimeoutException timeoutException)
441                 {
442                     throw DiagnosticUtility.ExceptionUtility.ThrowHelper(new TimeoutException(
443                         SR.GetString(SR.SocketCloseReadTimeout, thisPtr.socket.RemoteEndPoint, thisPtr.readFinTimeout),
444                         timeoutException), thisPtr.ExceptionEventType);
445                 }
446 
447                 thisPtr.ContinueClose(thisPtr.closeTimeoutHelper.RemainingTime());
448             }
449             catch (Exception e)
450             {
451                 if (Fx.IsFatal(e))
452                 {
453                     throw;
454                 }
455 
456                 DiagnosticUtility.TraceHandledException(e, TraceEventType.Warning);
457 
458                 // The user has no opportunity to clean up the connection in the async and linger
459                 // code path, ensure cleanup finishes.
460                 thisPtr.Abort();
461             }
462         }
463 
Close(TimeSpan timeout, bool asyncAndLinger)464         public void Close(TimeSpan timeout, bool asyncAndLinger)
465         {
466             lock (ThisLock)
467             {
468                 if (closeState == CloseState.Closing || closeState == CloseState.Closed)
469                 {
470                     // already closing or closed, so just return
471                     return;
472                 }
473                 this.TraceSocketInfo(this.socket, TraceCode.SocketConnectionClose, SR.TraceCodeSocketConnectionClose, timeout.ToString());
474                 closeState = CloseState.Closing;
475             }
476 
477             // first we shutdown our send-side
478             closeTimeoutHelper = new TimeoutHelper(timeout);
479             Shutdown(closeTimeoutHelper.RemainingTime());
480 
481             if (asyncAndLinger)
482             {
483                 CloseAsyncAndLinger();
484             }
485             else
486             {
487                 CloseSync();
488             }
489         }
490 
CloseSync()491         void CloseSync()
492         {
493             byte[] dummy = new byte[1];
494 
495             // then we check for a FIN from the other side (i.e. read zero)
496             int bytesRead;
497             readFinTimeout = closeTimeoutHelper.RemainingTime();
498 
499             try
500             {
501                 bytesRead = ReadCore(dummy, 0, 1, readFinTimeout, true);
502 
503                 if (bytesRead > 0)
504                 {
505                     throw DiagnosticUtility.ExceptionUtility.ThrowHelper(
506                         new CommunicationException(SR.GetString(SR.SocketCloseReadReceivedData, socket.RemoteEndPoint)), ExceptionEventType);
507                 }
508             }
509             catch (TimeoutException timeoutException)
510             {
511                 throw DiagnosticUtility.ExceptionUtility.ThrowHelper(new TimeoutException(
512                     SR.GetString(SR.SocketCloseReadTimeout, socket.RemoteEndPoint, readFinTimeout), timeoutException), ExceptionEventType);
513             }
514 
515             // finally we call Close with whatever time is remaining
516             ContinueClose(closeTimeoutHelper.RemainingTime());
517         }
518 
ContinueClose(TimeSpan timeout)519         public void ContinueClose(TimeSpan timeout)
520         {
521             // trace if we're effectively aborting
522             if (timeout <= TimeSpan.Zero && DiagnosticUtility.ShouldTraceWarning)
523             {
524                 TraceUtility.TraceEvent(TraceEventType.Warning, TraceCode.SocketConnectionAbortClose,
525                     SR.GetString(SR.TraceCodeSocketConnectionAbortClose), this);
526             }
527 
528             socket.Close(TimeoutHelper.ToMilliseconds(timeout));
529 
530             lock (ThisLock)
531             {
532                 // Abort could have been called on a separate thread and cleaned up
533                 // our buffers/completion here
534                 if (this.closeState != CloseState.Closed)
535                 {
536                     if (!this.asyncReadPending)
537                     {
538                         this.DisposeReadEventArgs();
539                     }
540 
541                     if (!this.asyncWritePending)
542                     {
543                         this.DisposeWriteEventArgs();
544                     }
545                 }
546 
547                 closeState = CloseState.Closed;
548             }
549         }
550 
Shutdown(TimeSpan timeout)551         public void Shutdown(TimeSpan timeout)
552         {
553             lock (ThisLock)
554             {
555                 if (isShutdown)
556                 {
557                     return;
558                 }
559 
560                 isShutdown = true;
561             }
562 
563             try
564             {
565                 socket.Shutdown(SocketShutdown.Send);
566             }
567             catch (SocketException socketException)
568             {
569                 throw DiagnosticUtility.ExceptionUtility.ThrowHelper(
570                     ConvertSendException(socketException, TimeSpan.MaxValue, this.socketSyncSendTimeout), ExceptionEventType);
571             }
572             catch (ObjectDisposedException objectDisposedException)
573             {
574                 Exception exceptionToThrow = ConvertObjectDisposedException(objectDisposedException, TransferOperation.Undefined);
575                 if (object.ReferenceEquals(exceptionToThrow, objectDisposedException))
576                 {
577                     throw;
578                 }
579                 else
580                 {
581                     throw DiagnosticUtility.ExceptionUtility.ThrowHelper(exceptionToThrow, ExceptionEventType);
582                 }
583             }
584         }
585 
ThrowIfNotOpen()586         void ThrowIfNotOpen()
587         {
588             if (closeState == CloseState.Closing || closeState == CloseState.Closed)
589             {
590                 throw DiagnosticUtility.ExceptionUtility.ThrowHelper(
591                     ConvertObjectDisposedException(new ObjectDisposedException(
592                     this.GetType().ToString(), SR.GetString(SR.SocketConnectionDisposed)), TransferOperation.Undefined), ExceptionEventType);
593             }
594         }
595 
ThrowIfClosed()596         void ThrowIfClosed()
597         {
598             if (closeState == CloseState.Closed)
599             {
600                 throw DiagnosticUtility.ExceptionUtility.ThrowHelper(
601                     ConvertObjectDisposedException(new ObjectDisposedException(
602                     this.GetType().ToString(), SR.GetString(SR.SocketConnectionDisposed)), TransferOperation.Undefined), ExceptionEventType);
603             }
604         }
605 
TraceSocketInfo(Socket socket, int traceCode, string srString, string timeoutString)606         void TraceSocketInfo(Socket socket, int traceCode, string srString, string timeoutString)
607         {
608             if (DiagnosticUtility.ShouldTraceInformation)
609             {
610                 Dictionary<string, string> values = new Dictionary<string, string>(4);
611                 values["State"] = this.closeState.ToString();
612 
613                 if (timeoutString != null)
614                 {
615                     values["Timeout"] = timeoutString;
616                 }
617 
618                 if (socket != null && this.closeState != CloseState.Closing)
619                 {
620                     if (socket.LocalEndPoint != null)
621                     {
622                         values["LocalEndpoint"] = socket.LocalEndPoint.ToString();
623                     }
624                     if (socket.RemoteEndPoint != null)
625                     {
626                         values["RemoteEndPoint"] = socket.RemoteEndPoint.ToString();
627                     }
628                 }
629                 TraceUtility.TraceEvent(TraceEventType.Information, traceCode, SR.GetString(srString), new DictionaryTraceRecord(values), this, null);
630             }
631         }
632 
TryGetEndpoints(out IPEndPoint localIPEndpoint, out IPEndPoint remoteIPEndpoint)633         bool TryGetEndpoints(out IPEndPoint localIPEndpoint, out IPEndPoint remoteIPEndpoint)
634         {
635             localIPEndpoint = null;
636             remoteIPEndpoint = null;
637 
638             if (this.closeState == CloseState.Open)
639             {
640                 try
641                 {
642                     remoteIPEndpoint = this.remoteEndpoint ?? (IPEndPoint)this.socket.RemoteEndPoint;
643                     localIPEndpoint = (IPEndPoint)this.socket.LocalEndPoint;
644                 }
645                 catch (Exception exception)
646                 {
647                     if (Fx.IsFatal(exception))
648                     {
649                         throw;
650                     }
651 
652                     DiagnosticUtility.TraceHandledException(exception, TraceEventType.Warning);
653                 }
654             }
655 
656             return localIPEndpoint != null && remoteIPEndpoint != null;
657         }
658 
DuplicateAndClose(int targetProcessId)659         public object DuplicateAndClose(int targetProcessId)
660         {
661             object result = socket.DuplicateAndClose(targetProcessId);
662             this.Abort(TraceEventType.Information);
663             return result;
664         }
665 
GetCoreTransport()666         public object GetCoreTransport()
667         {
668             return socket;
669         }
670 
BeginValidate(Uri uri, AsyncCallback callback, object state)671         public IAsyncResult BeginValidate(Uri uri, AsyncCallback callback, object state)
672         {
673             return new CompletedAsyncResult<bool>(true, callback, state);
674         }
675 
EndValidate(IAsyncResult result)676         public bool EndValidate(IAsyncResult result)
677         {
678             return CompletedAsyncResult<bool>.End(result);
679         }
680 
ConvertSendException(SocketException socketException, TimeSpan remainingTime, TimeSpan timeout)681         Exception ConvertSendException(SocketException socketException, TimeSpan remainingTime, TimeSpan timeout)
682         {
683             return ConvertTransferException(socketException, timeout, socketException,
684                 TransferOperation.Write, this.aborted, this.timeoutErrorString, this.timeoutErrorTransferOperation, this, remainingTime);
685         }
686 
ConvertReceiveException(SocketException socketException, TimeSpan remainingTime, TimeSpan timeout)687         Exception ConvertReceiveException(SocketException socketException, TimeSpan remainingTime, TimeSpan timeout)
688         {
689             return ConvertTransferException(socketException, timeout, socketException,
690                 TransferOperation.Read, this.aborted, this.timeoutErrorString, this.timeoutErrorTransferOperation, this, remainingTime);
691         }
692 
ConvertTransferException(SocketException socketException, TimeSpan timeout, Exception originalException)693         internal static Exception ConvertTransferException(SocketException socketException, TimeSpan timeout, Exception originalException)
694         {
695             return ConvertTransferException(socketException, timeout, originalException,
696                 TransferOperation.Undefined, false, null, TransferOperation.Undefined, null, TimeSpan.MaxValue);
697         }
698 
ConvertObjectDisposedException(ObjectDisposedException originalException, TransferOperation transferOperation)699         Exception ConvertObjectDisposedException(ObjectDisposedException originalException, TransferOperation transferOperation)
700         {
701             if (this.timeoutErrorString != null)
702             {
703                 return ConvertTimeoutErrorException(originalException, transferOperation, this.timeoutErrorString, this.timeoutErrorTransferOperation);
704             }
705             else if (this.aborted)
706             {
707                 return new CommunicationObjectAbortedException(SR.GetString(SR.SocketConnectionDisposed), originalException);
708             }
709             else
710             {
711                 return originalException;
712             }
713         }
714 
ConvertTransferException(SocketException socketException, TimeSpan timeout, Exception originalException, TransferOperation transferOperation, bool aborted, string timeoutErrorString, TransferOperation timeoutErrorTransferOperation, SocketConnection socketConnection, TimeSpan remainingTime)715         static Exception ConvertTransferException(SocketException socketException, TimeSpan timeout, Exception originalException,
716             TransferOperation transferOperation, bool aborted, string timeoutErrorString, TransferOperation timeoutErrorTransferOperation,
717             SocketConnection socketConnection, TimeSpan remainingTime)
718         {
719             if (socketException.ErrorCode == UnsafeNativeMethods.ERROR_INVALID_HANDLE)
720             {
721                 return new CommunicationObjectAbortedException(socketException.Message, socketException);
722             }
723 
724             if (timeoutErrorString != null)
725             {
726                 return ConvertTimeoutErrorException(originalException, transferOperation, timeoutErrorString, timeoutErrorTransferOperation);
727             }
728 
729             TraceEventType exceptionEventType = socketConnection == null ? TraceEventType.Error : socketConnection.ExceptionEventType;
730 
731             // 10053 can occur due to our timeout sockopt firing, so map to TimeoutException in that case
732             if (socketException.ErrorCode == UnsafeNativeMethods.WSAECONNABORTED &&
733                 remainingTime <= TimeSpan.Zero)
734             {
735                 TimeoutException timeoutException = new TimeoutException(SR.GetString(SR.TcpConnectionTimedOut, timeout), originalException);
736                 if (TD.TcpConnectionTimedOutIsEnabled())
737                 {
738                     if (socketConnection != null)
739                     {
740                         int socketid = (socketConnection != null && socketConnection.socket != null) ? socketConnection.socket.GetHashCode() : -1;
741                         TD.TcpConnectionTimedOut(socketid, socketConnection.RemoteEndpointAddress);
742                     }
743                 }
744                 if (DiagnosticUtility.ShouldTrace(exceptionEventType))
745                 {
746                     TraceUtility.TraceEvent(exceptionEventType, TraceCode.TcpConnectionTimedOut, GetEndpointString(SR.TcpConnectionTimedOut, timeout, null, socketConnection), timeoutException, null);
747                 }
748                 return timeoutException;
749             }
750 
751             if (socketException.ErrorCode == UnsafeNativeMethods.WSAENETRESET ||
752                 socketException.ErrorCode == UnsafeNativeMethods.WSAECONNABORTED ||
753                 socketException.ErrorCode == UnsafeNativeMethods.WSAECONNRESET)
754             {
755                 if (aborted)
756                 {
757                     return new CommunicationObjectAbortedException(SR.GetString(SR.TcpLocalConnectionAborted), originalException);
758                 }
759                 else
760                 {
761                     CommunicationException communicationException = new CommunicationException(SR.GetString(SR.TcpConnectionResetError, timeout), originalException);
762                     if (TD.TcpConnectionResetErrorIsEnabled())
763                     {
764                         if (socketConnection != null)
765                         {
766                             int socketId = (socketConnection.socket != null) ? socketConnection.socket.GetHashCode() : -1;
767                             TD.TcpConnectionResetError(socketId, socketConnection.RemoteEndpointAddress);
768                         }
769                     }
770                     if (DiagnosticUtility.ShouldTrace(exceptionEventType))
771                     {
772                         TraceUtility.TraceEvent(exceptionEventType, TraceCode.TcpConnectionResetError, GetEndpointString(SR.TcpConnectionResetError, timeout, null, socketConnection), communicationException, null);
773                     }
774                     return communicationException;
775                 }
776             }
777             else if (socketException.ErrorCode == UnsafeNativeMethods.WSAETIMEDOUT)
778             {
779                 TimeoutException timeoutException = new TimeoutException(SR.GetString(SR.TcpConnectionTimedOut, timeout), originalException);
780                 if (DiagnosticUtility.ShouldTrace(exceptionEventType))
781                 {
782                     TraceUtility.TraceEvent(exceptionEventType, TraceCode.TcpConnectionTimedOut, GetEndpointString(SR.TcpConnectionTimedOut, timeout, null, socketConnection), timeoutException, null);
783                 }
784                 return timeoutException;
785             }
786             else
787             {
788                 if (aborted)
789                 {
790                     return new CommunicationObjectAbortedException(SR.GetString(SR.TcpTransferError, socketException.ErrorCode, socketException.Message), originalException);
791                 }
792                 else
793                 {
794                     CommunicationException communicationException = new CommunicationException(SR.GetString(SR.TcpTransferError, socketException.ErrorCode, socketException.Message), originalException);
795                     if (DiagnosticUtility.ShouldTrace(exceptionEventType))
796                     {
797                         TraceUtility.TraceEvent(exceptionEventType, TraceCode.TcpTransferError, GetEndpointString(SR.TcpTransferError, TimeSpan.MinValue, socketException, socketConnection), communicationException, null);
798                     }
799                     return communicationException;
800                 }
801             }
802         }
803 
ConvertTimeoutErrorException(Exception originalException, TransferOperation transferOperation, string timeoutErrorString, TransferOperation timeoutErrorTransferOperation)804         static Exception ConvertTimeoutErrorException(Exception originalException,
805             TransferOperation transferOperation, string timeoutErrorString, TransferOperation timeoutErrorTransferOperation)
806         {
807             if (timeoutErrorString == null)
808             {
809                 Fx.Assert("Argument timeoutErrorString must not be null.");
810             }
811 
812             if (transferOperation == timeoutErrorTransferOperation)
813             {
814                 return new TimeoutException(timeoutErrorString, originalException);
815             }
816             else
817             {
818                 return new CommunicationException(timeoutErrorString, originalException);
819             }
820         }
821 
GetEndpointString(string sr, TimeSpan timeout, SocketException socketException, SocketConnection socketConnection)822         static string GetEndpointString(string sr, TimeSpan timeout, SocketException socketException, SocketConnection socketConnection)
823         {
824             IPEndPoint remoteEndpoint = null;
825             IPEndPoint localEndpoint = null;
826             bool haveEndpoints = socketConnection != null && socketConnection.TryGetEndpoints(out localEndpoint, out remoteEndpoint);
827 
828             if (string.Compare(sr, SR.TcpConnectionTimedOut, StringComparison.OrdinalIgnoreCase) == 0)
829             {
830                 return haveEndpoints
831                     ? SR.GetString(SR.TcpConnectionTimedOutWithIP, timeout, localEndpoint, remoteEndpoint)
832                     : SR.GetString(SR.TcpConnectionTimedOut, timeout);
833             }
834             else if (string.Compare(sr, SR.TcpConnectionResetError, StringComparison.OrdinalIgnoreCase) == 0)
835             {
836                 return haveEndpoints
837                     ? SR.GetString(SR.TcpConnectionResetErrorWithIP, timeout, localEndpoint, remoteEndpoint)
838                     : SR.GetString(SR.TcpConnectionResetError, timeout);
839             }
840             else
841             {
842                 // sr == SR.TcpTransferError
843                 return haveEndpoints
844                     ? SR.GetString(SR.TcpTransferErrorWithIP, socketException.ErrorCode, socketException.Message, localEndpoint, remoteEndpoint)
845                     : SR.GetString(SR.TcpTransferError, socketException.ErrorCode, socketException.Message);
846             }
847         }
848 
BeginWrite(byte[] buffer, int offset, int size, bool immediate, TimeSpan timeout, WaitCallback callback, object state)849         public AsyncCompletionResult BeginWrite(byte[] buffer, int offset, int size, bool immediate, TimeSpan timeout,
850             WaitCallback callback, object state)
851         {
852             ConnectionUtilities.ValidateBufferBounds(buffer, offset, size);
853             bool abortWrite = true;
854 
855             try
856             {
857                 if (TD.SocketAsyncWriteStartIsEnabled())
858                 {
859                     TraceWriteStart(size, true);
860                 }
861 
862                 lock (ThisLock)
863                 {
864                     Fx.Assert(!this.asyncWritePending, "Called BeginWrite twice.");
865                     this.ThrowIfClosed();
866                     this.EnsureWriteEventArgs();
867                     SetImmediate(immediate);
868                     SetWriteTimeout(timeout, false);
869                     this.SetUserToken(this.asyncWriteEventArgs, this);
870                     this.asyncWritePending = true;
871                     this.asyncWriteCallback = callback;
872                     this.asyncWriteState = state;
873                 }
874 
875                 this.asyncWriteEventArgs.SetBuffer(buffer, offset, size);
876 
877                 if (socket.SendAsync(this.asyncWriteEventArgs))
878                 {
879                     abortWrite = false;
880                     return AsyncCompletionResult.Queued;
881                 }
882 
883                 this.HandleSendAsyncCompleted();
884                 abortWrite = false;
885                 return AsyncCompletionResult.Completed;
886             }
887             catch (SocketException socketException)
888             {
889                 throw DiagnosticUtility.ExceptionUtility.ThrowHelper(
890                     ConvertSendException(socketException, TimeSpan.MaxValue, this.asyncSendTimeout), ExceptionEventType);
891             }
892             catch (ObjectDisposedException objectDisposedException)
893             {
894                 Exception exceptionToThrow = ConvertObjectDisposedException(objectDisposedException, TransferOperation.Write);
895                 if (object.ReferenceEquals(exceptionToThrow, objectDisposedException))
896                 {
897                     throw;
898                 }
899                 else
900                 {
901                     throw DiagnosticUtility.ExceptionUtility.ThrowHelper(exceptionToThrow, ExceptionEventType);
902                 }
903             }
904             finally
905             {
906                 if (abortWrite)
907                 {
908                     this.AbortWrite();
909                 }
910             }
911         }
912 
EndWrite()913         public void EndWrite()
914         {
915             if (this.asyncWriteException != null)
916             {
917                 this.AbortWrite();
918                 throw DiagnosticUtility.ExceptionUtility.ThrowHelper(this.asyncWriteException, ExceptionEventType);
919             }
920 
921             lock (ThisLock)
922             {
923                 if (!this.asyncWritePending)
924                 {
925                     throw Fx.AssertAndThrow("SocketConnection.EndWrite called with no write pending.");
926                 }
927 
928                 this.SetUserToken(this.asyncWriteEventArgs, null);
929                 this.asyncWritePending = false;
930 
931                 if (this.closeState == CloseState.Closed)
932                 {
933                     this.DisposeWriteEventArgs();
934                 }
935             }
936         }
937 
OnSendAsync(object sender, SocketAsyncEventArgs eventArgs)938         void OnSendAsync(object sender, SocketAsyncEventArgs eventArgs)
939         {
940             Fx.Assert(eventArgs != null, "Argument 'eventArgs' cannot be NULL.");
941             this.CancelSendTimer();
942 
943             try
944             {
945                 this.HandleSendAsyncCompleted();
946                 Fx.Assert(eventArgs.BytesTransferred == this.asyncWriteEventArgs.Count, "The socket SendAsync did not send all the bytes.");
947             }
948             catch (SocketException socketException)
949             {
950                 this.asyncWriteException = ConvertSendException(socketException, TimeSpan.MaxValue, this.asyncSendTimeout);
951             }
952 #pragma warning suppress 56500 // Microsoft, transferring exception to caller
953             catch (Exception exception)
954             {
955                 if (Fx.IsFatal(exception))
956                 {
957                     throw;
958                 }
959 
960                 this.asyncWriteException = exception;
961             }
962 
963             this.FinishWrite();
964         }
965 
HandleSendAsyncCompleted()966         void HandleSendAsyncCompleted()
967         {
968             if (this.asyncWriteEventArgs.SocketError == SocketError.Success)
969             {
970                 return;
971             }
972 
973             throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SocketException((int)this.asyncWriteEventArgs.SocketError));
974         }
975 
976         // This method should be called inside ThisLock
DisposeWriteEventArgs()977         void DisposeWriteEventArgs()
978         {
979             if (this.asyncWriteEventArgs != null)
980             {
981                 this.asyncWriteEventArgs.Completed -= onSocketSendCompleted;
982                 this.asyncWriteEventArgs.Dispose();
983             }
984         }
985 
AbortWrite()986         void AbortWrite()
987         {
988             lock (ThisLock)
989             {
990                 if (this.asyncWritePending)
991                 {
992                     if (this.closeState != CloseState.Closed)
993                     {
994                         this.SetUserToken(this.asyncWriteEventArgs, null);
995                         this.asyncWritePending = false;
996                         this.CancelSendTimer();
997                     }
998                     else
999                     {
1000                         this.DisposeWriteEventArgs();
1001                     }
1002                 }
1003             }
1004         }
1005 
FinishWrite()1006         void FinishWrite()
1007         {
1008             WaitCallback asyncWriteCallback = this.asyncWriteCallback;
1009             object asyncWriteState = this.asyncWriteState;
1010 
1011             this.asyncWriteState = null;
1012             this.asyncWriteCallback = null;
1013 
1014             asyncWriteCallback(asyncWriteState);
1015         }
1016 
Write(byte[] buffer, int offset, int size, bool immediate, TimeSpan timeout)1017         public void Write(byte[] buffer, int offset, int size, bool immediate, TimeSpan timeout)
1018         {
1019             // as per http://support.microsoft.com/default.aspx?scid=kb%3ben-us%3b201213
1020             // we shouldn't write more than 64K synchronously to a socket
1021             const int maxSocketWrite = 64 * 1024;
1022 
1023             ConnectionUtilities.ValidateBufferBounds(buffer, offset, size);
1024 
1025             TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
1026             try
1027             {
1028                 if (TD.SocketWriteStartIsEnabled())
1029                 {
1030                     TraceWriteStart(size, false);
1031                 }
1032 
1033                 SetImmediate(immediate);
1034                 int bytesToWrite = size;
1035 
1036                 while (bytesToWrite > 0)
1037                 {
1038                     SetWriteTimeout(timeoutHelper.RemainingTime(), true);
1039                     size = Math.Min(bytesToWrite, maxSocketWrite);
1040                     socket.Send(buffer, offset, size, SocketFlags.None);
1041                     bytesToWrite -= size;
1042                     offset += size;
1043                     timeout = timeoutHelper.RemainingTime();
1044                 }
1045             }
1046             catch (SocketException socketException)
1047             {
1048                 throw DiagnosticUtility.ExceptionUtility.ThrowHelper(
1049                     ConvertSendException(socketException, timeoutHelper.RemainingTime(), this.socketSyncSendTimeout), ExceptionEventType);
1050             }
1051             catch (ObjectDisposedException objectDisposedException)
1052             {
1053                 Exception exceptionToThrow = ConvertObjectDisposedException(objectDisposedException, TransferOperation.Write);
1054                 if (object.ReferenceEquals(exceptionToThrow, objectDisposedException))
1055                 {
1056                     throw;
1057                 }
1058                 else
1059                 {
1060                     throw DiagnosticUtility.ExceptionUtility.ThrowHelper(exceptionToThrow, ExceptionEventType);
1061                 }
1062             }
1063         }
1064 
TraceWriteStart(int size, bool async)1065         void TraceWriteStart(int size, bool async)
1066         {
1067             if (!async)
1068             {
1069                 TD.SocketWriteStart(this.socket.GetHashCode(), size, this.RemoteEndpointAddress);
1070             }
1071             else
1072             {
1073                 TD.SocketAsyncWriteStart(this.socket.GetHashCode(), size, this.RemoteEndpointAddress);
1074             }
1075         }
1076 
Write(byte[] buffer, int offset, int size, bool immediate, TimeSpan timeout, BufferManager bufferManager)1077         public void Write(byte[] buffer, int offset, int size, bool immediate, TimeSpan timeout, BufferManager bufferManager)
1078         {
1079             try
1080             {
1081                 Write(buffer, offset, size, immediate, timeout);
1082             }
1083             finally
1084             {
1085                 bufferManager.ReturnBuffer(buffer);
1086             }
1087         }
1088 
Read(byte[] buffer, int offset, int size, TimeSpan timeout)1089         public int Read(byte[] buffer, int offset, int size, TimeSpan timeout)
1090         {
1091             ConnectionUtilities.ValidateBufferBounds(buffer, offset, size);
1092             ThrowIfNotOpen();
1093             return ReadCore(buffer, offset, size, timeout, false);
1094         }
1095 
ReadCore(byte[] buffer, int offset, int size, TimeSpan timeout, bool closing)1096         int ReadCore(byte[] buffer, int offset, int size, TimeSpan timeout, bool closing)
1097         {
1098             int bytesRead = 0;
1099             TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
1100             try
1101             {
1102                 SetReadTimeout(timeoutHelper.RemainingTime(), true, closing);
1103                 bytesRead = socket.Receive(buffer, offset, size, SocketFlags.None);
1104 
1105                 if (TD.SocketReadStopIsEnabled())
1106                 {
1107                     TraceSocketReadStop(bytesRead, false);
1108                 }
1109             }
1110             catch (SocketException socketException)
1111             {
1112                 throw DiagnosticUtility.ExceptionUtility.ThrowHelper(
1113                     ConvertReceiveException(socketException, timeoutHelper.RemainingTime(), this.socketSyncReceiveTimeout), ExceptionEventType);
1114             }
1115             catch (ObjectDisposedException objectDisposedException)
1116             {
1117                 Exception exceptionToThrow = ConvertObjectDisposedException(objectDisposedException, TransferOperation.Read);
1118                 if (object.ReferenceEquals(exceptionToThrow, objectDisposedException))
1119                 {
1120                     throw;
1121                 }
1122                 else
1123                 {
1124                     throw DiagnosticUtility.ExceptionUtility.ThrowHelper(exceptionToThrow, ExceptionEventType);
1125                 }
1126             }
1127 
1128             return bytesRead;
1129         }
1130 
TraceSocketReadStop(int bytesRead, bool async)1131         private void TraceSocketReadStop(int bytesRead, bool async)
1132         {
1133             if (!async)
1134             {
1135                 TD.SocketReadStop((this.socket != null) ? this.socket.GetHashCode() : -1, bytesRead, this.RemoteEndpointAddress);
1136             }
1137             else
1138             {
1139                 TD.SocketAsyncReadStop((this.socket != null) ? this.socket.GetHashCode() : -1, bytesRead, this.RemoteEndpointAddress);
1140             }
1141         }
1142 
BeginRead(int offset, int size, TimeSpan timeout, WaitCallback callback, object state)1143         public virtual AsyncCompletionResult BeginRead(int offset, int size, TimeSpan timeout,
1144             WaitCallback callback, object state)
1145         {
1146             ConnectionUtilities.ValidateBufferBounds(AsyncReadBufferSize, offset, size);
1147             this.ThrowIfNotOpen();
1148             return this.BeginReadCore(offset, size, timeout, callback, state);
1149         }
1150 
BeginReadCore(int offset, int size, TimeSpan timeout, WaitCallback callback, object state)1151         AsyncCompletionResult BeginReadCore(int offset, int size, TimeSpan timeout,
1152             WaitCallback callback, object state)
1153         {
1154             bool abortRead = true;
1155 
1156             lock (ThisLock)
1157             {
1158                 this.ThrowIfClosed();
1159                 this.EnsureReadEventArgs();
1160                 this.asyncReadState = state;
1161                 this.asyncReadCallback = callback;
1162                 this.SetUserToken(this.asyncReadEventArgs, this);
1163                 this.asyncReadPending = true;
1164                 this.SetReadTimeout(timeout, false, false);
1165             }
1166 
1167             try
1168             {
1169                 if (socket.UseOnlyOverlappedIO)
1170                 {
1171                     // ReceiveAsync does not respect UseOnlyOverlappedIO but BeginReceive does.
1172                     IAsyncResult result = socket.BeginReceive(AsyncReadBuffer, offset, size, SocketFlags.None, onReceiveCompleted, this);
1173 
1174                     if (!result.CompletedSynchronously)
1175                     {
1176                         abortRead = false;
1177                         return AsyncCompletionResult.Queued;
1178                     }
1179 
1180                     asyncReadSize = socket.EndReceive(result);
1181                 }
1182                 else
1183                 {
1184                     if (offset != this.asyncReadEventArgs.Offset ||
1185                         size != this.asyncReadEventArgs.Count)
1186                     {
1187                         this.asyncReadEventArgs.SetBuffer(offset, size);
1188                     }
1189 
1190                     if (this.ReceiveAsync())
1191                     {
1192                         abortRead = false;
1193                         return AsyncCompletionResult.Queued;
1194                     }
1195 
1196                     this.HandleReceiveAsyncCompleted();
1197                     this.asyncReadSize = this.asyncReadEventArgs.BytesTransferred;
1198                 }
1199 
1200                 if (TD.SocketReadStopIsEnabled())
1201                 {
1202                     TraceSocketReadStop(asyncReadSize, true);
1203                 }
1204 
1205                 abortRead = false;
1206                 return AsyncCompletionResult.Completed;
1207             }
1208             catch (SocketException socketException)
1209             {
1210                 throw DiagnosticUtility.ExceptionUtility.ThrowHelper(ConvertReceiveException(socketException, TimeSpan.MaxValue, this.asyncReceiveTimeout), ExceptionEventType);
1211             }
1212             catch (ObjectDisposedException objectDisposedException)
1213             {
1214                 Exception exceptionToThrow = ConvertObjectDisposedException(objectDisposedException, TransferOperation.Read);
1215                 if (object.ReferenceEquals(exceptionToThrow, objectDisposedException))
1216                 {
1217                     throw;
1218                 }
1219                 else
1220                 {
1221                     throw DiagnosticUtility.ExceptionUtility.ThrowHelper(exceptionToThrow, ExceptionEventType);
1222                 }
1223             }
1224             finally
1225             {
1226                 if (abortRead)
1227                 {
1228                     AbortRead();
1229                 }
1230             }
1231         }
1232 
1233         [Fx.Tag.SecurityNote(Critical = "Uses a SecurityCritical method to suppress ExecutionContext flow when running in fullTrust.",
1234             Safe = "Safe because we're only suppressing the ExecutionContext if we're already in full trust.")]
1235         [SecuritySafeCritical]
ReceiveAsync()1236         bool ReceiveAsync()
1237         {
1238             if (!PartialTrustHelpers.ShouldFlowSecurityContext)
1239             {
1240                 if (!ExecutionContext.IsFlowSuppressed())
1241                 {
1242                     return ReceiveAsyncNoFlow();
1243                 }
1244             }
1245 
1246             return this.socket.ReceiveAsync(this.asyncReadEventArgs);
1247         }
1248 
1249         [Fx.Tag.SecurityNote(Critical = "Suppresses execution context flow and restores it after invocation. Fulltrust async callbacks " +
1250             "will not have an ExecutionContext, LogicalCallcontext or SecurityContext and should not take dependency on them.")]
1251         [SecurityCritical]
ReceiveAsyncNoFlow()1252         bool ReceiveAsyncNoFlow()
1253         {
1254             using (ExecutionContext.SuppressFlow())
1255             {
1256                 return this.socket.ReceiveAsync(this.asyncReadEventArgs);
1257             }
1258         }
1259 
OnReceive(IAsyncResult result)1260         void OnReceive(IAsyncResult result)
1261         {
1262             this.CancelReceiveTimer();
1263             if (result.CompletedSynchronously)
1264             {
1265                 return;
1266             }
1267 
1268             try
1269             {
1270                 this.asyncReadSize = socket.EndReceive(result);
1271 
1272                 if (TD.SocketReadStopIsEnabled())
1273                 {
1274                     TraceSocketReadStop(this.asyncReadSize, true);
1275                 }
1276             }
1277             catch (SocketException socketException)
1278             {
1279                 this.asyncReadException = ConvertReceiveException(socketException, TimeSpan.MaxValue, this.asyncReceiveTimeout);
1280             }
1281             catch (ObjectDisposedException objectDisposedException)
1282             {
1283                 this.asyncReadException = ConvertObjectDisposedException(objectDisposedException, TransferOperation.Read);
1284             }
1285 #pragma warning suppress 56500 // Microsoft, transferring exception to caller
1286             catch (Exception exception)
1287             {
1288                 if (Fx.IsFatal(exception))
1289                 {
1290                     throw;
1291                 }
1292                 this.asyncReadException = exception;
1293             }
1294 
1295             this.FinishRead();
1296         }
1297 
OnReceiveAsync(object sender, SocketAsyncEventArgs eventArgs)1298         void OnReceiveAsync(object sender, SocketAsyncEventArgs eventArgs)
1299         {
1300             Fx.Assert(eventArgs != null, "Argument 'eventArgs' cannot be NULL.");
1301             this.CancelReceiveTimer();
1302 
1303             try
1304             {
1305                 this.HandleReceiveAsyncCompleted();
1306                 this.asyncReadSize = eventArgs.BytesTransferred;
1307 
1308                 if (TD.SocketReadStopIsEnabled())
1309                 {
1310                     TraceSocketReadStop(asyncReadSize, true);
1311                 }
1312             }
1313             catch (SocketException socketException)
1314             {
1315                 asyncReadException = ConvertReceiveException(socketException, TimeSpan.MaxValue, this.asyncReceiveTimeout);
1316             }
1317 #pragma warning suppress 56500 // Microsoft, transferring exception to caller
1318             catch (Exception exception)
1319             {
1320                 if (Fx.IsFatal(exception))
1321                 {
1322                     throw;
1323                 }
1324                 asyncReadException = exception;
1325             }
1326 
1327             FinishRead();
1328         }
1329 
HandleReceiveAsyncCompleted()1330         void HandleReceiveAsyncCompleted()
1331         {
1332             if (this.asyncReadEventArgs.SocketError == SocketError.Success)
1333             {
1334                 return;
1335             }
1336 
1337             throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SocketException((int)this.asyncReadEventArgs.SocketError));
1338         }
1339 
FinishRead()1340         void FinishRead()
1341         {
1342             WaitCallback asyncReadCallback = this.asyncReadCallback;
1343             object asyncReadState = this.asyncReadState;
1344 
1345             this.asyncReadState = null;
1346             this.asyncReadCallback = null;
1347 
1348             asyncReadCallback(asyncReadState);
1349         }
1350 
1351         // Both BeginRead/ReadAsync paths completed themselves. EndRead's only job is to deliver the result.
EndRead()1352         public int EndRead()
1353         {
1354             if (this.asyncReadException != null)
1355             {
1356                 AbortRead();
1357                 throw DiagnosticUtility.ExceptionUtility.ThrowHelper(this.asyncReadException, ExceptionEventType);
1358             }
1359 
1360             lock (ThisLock)
1361             {
1362                 if (!this.asyncReadPending)
1363                 {
1364                     throw Fx.AssertAndThrow("SocketConnection.EndRead called with no read pending.");
1365                 }
1366 
1367                 this.SetUserToken(this.asyncReadEventArgs, null);
1368                 this.asyncReadPending = false;
1369 
1370                 if (closeState == CloseState.Closed)
1371                 {
1372                     this.DisposeReadEventArgs();
1373                 }
1374             }
1375 
1376             return this.asyncReadSize;
1377         }
1378 
1379         // This method should be called inside ThisLock
DisposeReadEventArgs()1380         void DisposeReadEventArgs()
1381         {
1382             if (this.asyncReadEventArgs != null)
1383             {
1384                 this.asyncReadEventArgs.Completed -= onReceiveAsyncCompleted;
1385                 this.asyncReadEventArgs.Dispose();
1386             }
1387 
1388             // We release the buffer only if there is no outstanding I/O
1389             this.TryReturnReadBuffer();
1390         }
1391 
TryReturnReadBuffer()1392         void TryReturnReadBuffer()
1393         {
1394             // The buffer must not be returned and nulled when an abort occurs. Since the buffer
1395             // is also accessed by higher layers, code that has not yet realized the stack is
1396             // aborted may be attempting to read from the buffer.
1397             if (this.readBuffer != null && !this.aborted)
1398             {
1399                 this.connectionBufferPool.Return(this.readBuffer);
1400                 this.readBuffer = null;
1401             }
1402         }
1403 
SetUserToken(SocketAsyncEventArgs args, object userToken)1404         void SetUserToken(SocketAsyncEventArgs args, object userToken)
1405         {
1406             // The socket args can be pinned by the overlapped callback. Ensure SocketConnection is
1407             // only pinned when there is outstanding IO.
1408             if (args != null)
1409             {
1410                 args.UserToken = userToken;
1411             }
1412         }
1413 
SetImmediate(bool immediate)1414         void SetImmediate(bool immediate)
1415         {
1416             if (immediate != this.noDelay)
1417             {
1418                 lock (ThisLock)
1419                 {
1420                     ThrowIfNotOpen();
1421                     socket.NoDelay = immediate;
1422                 }
1423                 this.noDelay = immediate;
1424             }
1425         }
1426 
SetReadTimeout(TimeSpan timeout, bool synchronous, bool closing)1427         void SetReadTimeout(TimeSpan timeout, bool synchronous, bool closing)
1428         {
1429             if (synchronous)
1430             {
1431                 CancelReceiveTimer();
1432 
1433                 // 0 == infinite for winsock timeouts, so we should preempt and throw
1434                 if (timeout <= TimeSpan.Zero)
1435                 {
1436                     throw DiagnosticUtility.ExceptionUtility.ThrowHelper(
1437                         new TimeoutException(SR.GetString(SR.TcpConnectionTimedOut, timeout)), ExceptionEventType);
1438                 }
1439 
1440                 if (ShouldUpdateTimeout(this.socketSyncReceiveTimeout, timeout))
1441                 {
1442                     lock (ThisLock)
1443                     {
1444                         if (!closing || this.closeState != CloseState.Closing)
1445                         {
1446                             ThrowIfNotOpen();
1447                         }
1448                         this.socket.ReceiveTimeout = TimeoutHelper.ToMilliseconds(timeout);
1449                     }
1450                     this.socketSyncReceiveTimeout = timeout;
1451                 }
1452             }
1453             else
1454             {
1455                 this.asyncReceiveTimeout = timeout;
1456                 if (timeout == TimeSpan.MaxValue)
1457                 {
1458                     CancelReceiveTimer();
1459                 }
1460                 else
1461                 {
1462                     ReceiveTimer.Set(timeout);
1463                 }
1464             }
1465         }
1466 
SetWriteTimeout(TimeSpan timeout, bool synchronous)1467         void SetWriteTimeout(TimeSpan timeout, bool synchronous)
1468         {
1469             if (synchronous)
1470             {
1471                 CancelSendTimer();
1472 
1473                 // 0 == infinite for winsock timeouts, so we should preempt and throw
1474                 if (timeout <= TimeSpan.Zero)
1475                 {
1476                     throw DiagnosticUtility.ExceptionUtility.ThrowHelper(
1477                         new TimeoutException(SR.GetString(SR.TcpConnectionTimedOut, timeout)), ExceptionEventType);
1478                 }
1479 
1480                 if (ShouldUpdateTimeout(this.socketSyncSendTimeout, timeout))
1481                 {
1482                     lock (ThisLock)
1483                     {
1484                         ThrowIfNotOpen();
1485                         this.socket.SendTimeout = TimeoutHelper.ToMilliseconds(timeout);
1486                     }
1487                     this.socketSyncSendTimeout = timeout;
1488                 }
1489             }
1490             else
1491             {
1492                 this.asyncSendTimeout = timeout;
1493                 if (timeout == TimeSpan.MaxValue)
1494                 {
1495                     CancelSendTimer();
1496                 }
1497                 else
1498                 {
1499                     SendTimer.Set(timeout);
1500                 }
1501             }
1502         }
1503 
ShouldUpdateTimeout(TimeSpan oldTimeout, TimeSpan newTimeout)1504         bool ShouldUpdateTimeout(TimeSpan oldTimeout, TimeSpan newTimeout)
1505         {
1506             if (oldTimeout == newTimeout)
1507             {
1508                 return false;
1509             }
1510 
1511             long threshold = oldTimeout.Ticks / 10;
1512             long delta = Math.Max(oldTimeout.Ticks, newTimeout.Ticks) - Math.Min(oldTimeout.Ticks, newTimeout.Ticks);
1513 
1514             return delta > threshold;
1515         }
1516 
1517         // This method should be called inside ThisLock
EnsureReadEventArgs()1518         void EnsureReadEventArgs()
1519         {
1520             if (this.asyncReadEventArgs == null)
1521             {
1522                 // Init ReadAsync state
1523                 if (onReceiveAsyncCompleted == null)
1524                 {
1525                     onReceiveAsyncCompleted = new EventHandler<SocketAsyncEventArgs>(OnReceiveAsyncCompleted);
1526                 }
1527 
1528                 this.asyncReadEventArgs = new SocketAsyncEventArgs();
1529                 this.asyncReadEventArgs.SetBuffer(this.readBuffer, 0, this.readBuffer.Length);
1530                 this.asyncReadEventArgs.Completed += onReceiveAsyncCompleted;
1531             }
1532         }
1533 
1534         // This method should be called inside ThisLock
EnsureWriteEventArgs()1535         void EnsureWriteEventArgs()
1536         {
1537             if (this.asyncWriteEventArgs == null)
1538             {
1539                 // Init SendAsync state
1540                 if (onSocketSendCompleted == null)
1541                 {
1542                     onSocketSendCompleted = new EventHandler<SocketAsyncEventArgs>(OnSendAsyncCompleted);
1543                 }
1544 
1545                 this.asyncWriteEventArgs = new SocketAsyncEventArgs();
1546                 this.asyncWriteEventArgs.Completed += onSocketSendCompleted;
1547             }
1548         }
1549 
1550         enum CloseState
1551         {
1552             Open,
1553             Closing,
1554             Closed,
1555         }
1556 
1557         enum TransferOperation
1558         {
1559             Write,
1560             Read,
1561             Undefined,
1562         }
1563     }
1564 
1565     class SocketConnectionInitiator : IConnectionInitiator
1566     {
1567         int bufferSize;
1568         ConnectionBufferPool connectionBufferPool;
1569 
SocketConnectionInitiator(int bufferSize)1570         public SocketConnectionInitiator(int bufferSize)
1571         {
1572             this.bufferSize = bufferSize;
1573             this.connectionBufferPool = new ConnectionBufferPool(bufferSize);
1574         }
1575 
CreateConnection(Socket socket)1576         IConnection CreateConnection(Socket socket)
1577         {
1578             return new SocketConnection(socket, this.connectionBufferPool, false);
1579         }
1580 
ConvertConnectException(SocketException socketException, Uri remoteUri, TimeSpan timeSpent, Exception innerException)1581         public static Exception ConvertConnectException(SocketException socketException, Uri remoteUri, TimeSpan timeSpent, Exception innerException)
1582         {
1583             if (socketException.ErrorCode == UnsafeNativeMethods.ERROR_INVALID_HANDLE)
1584             {
1585                 return new CommunicationObjectAbortedException(socketException.Message, socketException);
1586             }
1587 
1588             if (socketException.ErrorCode == UnsafeNativeMethods.WSAEADDRNOTAVAIL ||
1589                 socketException.ErrorCode == UnsafeNativeMethods.WSAECONNREFUSED ||
1590                 socketException.ErrorCode == UnsafeNativeMethods.WSAENETDOWN ||
1591                 socketException.ErrorCode == UnsafeNativeMethods.WSAENETUNREACH ||
1592                 socketException.ErrorCode == UnsafeNativeMethods.WSAEHOSTDOWN ||
1593                 socketException.ErrorCode == UnsafeNativeMethods.WSAEHOSTUNREACH ||
1594                 socketException.ErrorCode == UnsafeNativeMethods.WSAETIMEDOUT)
1595             {
1596                 if (timeSpent == TimeSpan.MaxValue)
1597                 {
1598                     return new EndpointNotFoundException(SR.GetString(SR.TcpConnectError, remoteUri.AbsoluteUri, socketException.ErrorCode, socketException.Message), innerException);
1599                 }
1600                 else
1601                 {
1602                     return new EndpointNotFoundException(SR.GetString(SR.TcpConnectErrorWithTimeSpan, remoteUri.AbsoluteUri, socketException.ErrorCode, socketException.Message, timeSpent), innerException);
1603                 }
1604             }
1605             else if (socketException.ErrorCode == UnsafeNativeMethods.WSAENOBUFS)
1606             {
1607                 return new InsufficientMemoryException(SR.GetString(SR.TcpConnectNoBufs), innerException);
1608             }
1609             else if (socketException.ErrorCode == UnsafeNativeMethods.ERROR_NOT_ENOUGH_MEMORY ||
1610                 socketException.ErrorCode == UnsafeNativeMethods.ERROR_NO_SYSTEM_RESOURCES ||
1611                 socketException.ErrorCode == UnsafeNativeMethods.ERROR_OUTOFMEMORY)
1612             {
1613                 return new InsufficientMemoryException(SR.GetString(SR.InsufficentMemory), socketException);
1614             }
1615             else
1616             {
1617                 if (timeSpent == TimeSpan.MaxValue)
1618                 {
1619                     return new CommunicationException(SR.GetString(SR.TcpConnectError, remoteUri.AbsoluteUri, socketException.ErrorCode, socketException.Message), innerException);
1620                 }
1621                 else
1622                 {
1623                     return new CommunicationException(SR.GetString(SR.TcpConnectErrorWithTimeSpan, remoteUri.AbsoluteUri, socketException.ErrorCode, socketException.Message, timeSpent), innerException);
1624                 }
1625             }
1626         }
1627 
GetIPAddresses(Uri uri)1628         static IPAddress[] GetIPAddresses(Uri uri)
1629         {
1630             if (uri.HostNameType == UriHostNameType.IPv4 ||
1631                 uri.HostNameType == UriHostNameType.IPv6)
1632             {
1633                 IPAddress ipAddress = IPAddress.Parse(uri.DnsSafeHost);
1634                 return new IPAddress[] { ipAddress };
1635             }
1636 
1637             IPHostEntry hostEntry = null;
1638 
1639             try
1640             {
1641                 hostEntry = DnsCache.Resolve(uri);
1642             }
1643             catch (SocketException socketException)
1644             {
1645                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
1646                     new EndpointNotFoundException(SR.GetString(SR.UnableToResolveHost, uri.Host), socketException));
1647             }
1648 
1649             if (hostEntry.AddressList.Length == 0)
1650             {
1651                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
1652                     new EndpointNotFoundException(SR.GetString(SR.UnableToResolveHost, uri.Host)));
1653             }
1654 
1655             return hostEntry.AddressList;
1656         }
1657 
CreateTimeoutException(Uri uri, TimeSpan timeout, IPAddress[] addresses, int invalidAddressCount, SocketException innerException)1658         static TimeoutException CreateTimeoutException(Uri uri, TimeSpan timeout, IPAddress[] addresses, int invalidAddressCount,
1659             SocketException innerException)
1660         {
1661             StringBuilder addressStringBuilder = new StringBuilder();
1662             for (int i = 0; i < invalidAddressCount; i++)
1663             {
1664                 if (addresses[i] == null)
1665                 {
1666                     continue;
1667                 }
1668 
1669                 if (addressStringBuilder.Length > 0)
1670                 {
1671                     addressStringBuilder.Append(", ");
1672                 }
1673                 addressStringBuilder.Append(addresses[i].ToString());
1674             }
1675 
1676             throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new TimeoutException(
1677                 SR.GetString(SR.TcpConnectingToViaTimedOut, uri.AbsoluteUri, timeout.ToString(),
1678                 invalidAddressCount, addresses.Length, addressStringBuilder.ToString()), innerException));
1679         }
1680 
Connect(Uri uri, TimeSpan timeout)1681         public IConnection Connect(Uri uri, TimeSpan timeout)
1682         {
1683             if (DiagnosticUtility.ShouldTraceInformation)
1684             {
1685                 TraceUtility.TraceEvent(TraceEventType.Information, TraceCode.InitiatingTcpConnection,
1686                     SR.GetString(SR.TraceCodeInitiatingTcpConnection),
1687                     new StringTraceRecord("Uri", uri.ToString()), this, null);
1688             }
1689 
1690             int port = uri.Port;
1691             IPAddress[] addresses = SocketConnectionInitiator.GetIPAddresses(uri);
1692             Socket socket = null;
1693             SocketException lastException = null;
1694 
1695             if (port == -1)
1696             {
1697                 port = TcpUri.DefaultPort;
1698             }
1699 
1700             int invalidAddressCount = 0;
1701             TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
1702             for (int i = 0; i < addresses.Length; i++)
1703             {
1704                 if (timeoutHelper.RemainingTime() == TimeSpan.Zero)
1705                 {
1706                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
1707                         CreateTimeoutException(uri, timeoutHelper.OriginalTimeout, addresses, invalidAddressCount, lastException));
1708                 }
1709 
1710                 AddressFamily addressFamily = addresses[i].AddressFamily;
1711 
1712                 if (addressFamily == AddressFamily.InterNetworkV6 && !Socket.OSSupportsIPv6)
1713                 {
1714                     addresses[i] = null; // disregard for exception attempt purposes
1715                     continue;
1716                 }
1717 
1718                 DateTime connectStartTime = DateTime.UtcNow;
1719                 try
1720                 {
1721                     socket = new Socket(addressFamily, SocketType.Stream, ProtocolType.Tcp);
1722                     socket.Connect(new IPEndPoint(addresses[i], port));
1723                     lastException = null;
1724                     break;
1725                 }
1726                 catch (SocketException socketException)
1727                 {
1728                     invalidAddressCount++;
1729                     SocketConnectionInitiator.TraceConnectFailure(socket, socketException, uri, DateTime.UtcNow - connectStartTime);
1730                     lastException = socketException;
1731                     socket.Close();
1732                 }
1733             }
1734 
1735             if (socket == null)
1736             {
1737                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
1738                     new EndpointNotFoundException(SR.GetString(SR.NoIPEndpointsFoundForHost, uri.Host)));
1739             }
1740 
1741             if (lastException != null)
1742             {
1743                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
1744                     SocketConnectionInitiator.ConvertConnectException(lastException, uri,
1745                     timeoutHelper.ElapsedTime(), lastException));
1746             }
1747 
1748             return CreateConnection(socket);
1749         }
1750 
BeginConnect(Uri uri, TimeSpan timeout, AsyncCallback callback, object state)1751         public IAsyncResult BeginConnect(Uri uri, TimeSpan timeout, AsyncCallback callback, object state)
1752         {
1753             if (DiagnosticUtility.ShouldTraceInformation)
1754             {
1755                 TraceUtility.TraceEvent(TraceEventType.Information, TraceCode.InitiatingTcpConnection,
1756                     SR.GetString(SR.TraceCodeInitiatingTcpConnection),
1757                     new StringTraceRecord("Uri", uri.ToString()), this, null);
1758             }
1759             return new ConnectAsyncResult(uri, timeout, callback, state);
1760         }
1761 
EndConnect(IAsyncResult result)1762         public IConnection EndConnect(IAsyncResult result)
1763         {
1764             Socket socket = ConnectAsyncResult.End(result);
1765             return CreateConnection(socket);
1766         }
1767 
TraceConnectFailure(Socket socket, SocketException socketException, Uri remoteUri, TimeSpan timeSpentInConnect)1768         public static void TraceConnectFailure(Socket socket, SocketException socketException, Uri remoteUri,
1769             TimeSpan timeSpentInConnect)
1770         {
1771             if (DiagnosticUtility.ShouldTraceWarning)
1772             {
1773                 Exception traceException = ConvertConnectException(socketException, remoteUri, timeSpentInConnect, socketException);
1774                 TraceUtility.TraceEvent(TraceEventType.Warning, TraceCode.TcpConnectError,
1775                     SR.GetString(SR.TraceCodeTcpConnectError), socket, traceException);
1776             }
1777         }
1778 
1779         class ConnectAsyncResult : AsyncResult
1780         {
1781             IPAddress[] addresses;
1782             int currentIndex;
1783             int port;
1784             SocketException lastException;
1785             TimeSpan timeout;
1786             TimeoutHelper timeoutHelper;
1787             int invalidAddressCount;
1788             DateTime connectStartTime;
1789             Socket socket;
1790             Uri uri;
1791             static Action<object> startConnectCallback;
1792             static AsyncCallback onConnect = Fx.ThunkCallback(new AsyncCallback(OnConnect));
1793 
ConnectAsyncResult(Uri uri, TimeSpan timeout, AsyncCallback callback, object state)1794             public ConnectAsyncResult(Uri uri, TimeSpan timeout, AsyncCallback callback, object state)
1795                 : base(callback, state)
1796             {
1797                 this.uri = uri;
1798                 addresses = SocketConnectionInitiator.GetIPAddresses(uri);
1799                 port = uri.Port;
1800                 if (port == -1)
1801                 {
1802                     port = TcpUri.DefaultPort;
1803                 }
1804 
1805                 currentIndex = 0;
1806                 this.timeout = timeout;
1807                 this.timeoutHelper = new TimeoutHelper(timeout);
1808 
1809                 if (Thread.CurrentThread.IsThreadPoolThread)
1810                 {
1811                     if (StartConnect())
1812                     {
1813                         base.Complete(true);
1814                     }
1815                 }
1816                 else
1817                 {
1818                     // If we're not on a threadpool thread, then we need to post a callback to start our accepting loop
1819                     // Otherwise if the calling thread aborts then the async I/O will get inadvertantly cancelled
1820                     if (startConnectCallback == null)
1821                     {
1822                         startConnectCallback = StartConnectCallback;
1823                     }
1824 
1825                     ActionItem.Schedule(startConnectCallback, this);
1826                 }
1827             }
1828 
StartConnectCallback(object state)1829             static void StartConnectCallback(object state)
1830             {
1831                 ConnectAsyncResult connectAsyncResult = (ConnectAsyncResult)state;
1832                 bool completeSelf = false;
1833                 Exception completionException = null;
1834                 try
1835                 {
1836                     completeSelf = connectAsyncResult.StartConnect();
1837                 }
1838 #pragma warning suppress 56500 // covered by FxCOP
1839                 catch (Exception e)
1840                 {
1841                     if (Fx.IsFatal(e))
1842                     {
1843                         throw;
1844                     }
1845                     completeSelf = true;
1846                     completionException = e;
1847                 }
1848 
1849                 if (completeSelf)
1850                 {
1851                     connectAsyncResult.Complete(false, completionException);
1852                 }
1853             }
1854 
StartConnect()1855             bool StartConnect()
1856             {
1857                 while (currentIndex < addresses.Length)
1858                 {
1859                     if (timeoutHelper.RemainingTime() == TimeSpan.Zero)
1860                     {
1861                         throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
1862                             CreateTimeoutException(uri, timeoutHelper.OriginalTimeout, addresses, invalidAddressCount, lastException));
1863                     }
1864 
1865                     AddressFamily addressFamily = addresses[currentIndex].AddressFamily;
1866 
1867                     if (addressFamily == AddressFamily.InterNetworkV6 && !Socket.OSSupportsIPv6)
1868                     {
1869                         addresses[currentIndex++] = null; // disregard for exception attempt purposes
1870                         continue;
1871                     }
1872 
1873                     this.connectStartTime = DateTime.UtcNow;
1874                     try
1875                     {
1876                         IPEndPoint ipEndPoint = new IPEndPoint(addresses[currentIndex], port);
1877                         this.socket = new Socket(addressFamily, SocketType.Stream, ProtocolType.Tcp);
1878                         IAsyncResult result = socket.BeginConnect(ipEndPoint, onConnect, this);
1879                         if (!result.CompletedSynchronously)
1880                         {
1881                             return false;
1882                         }
1883 
1884                         socket.EndConnect(result);
1885                         return true;
1886                     }
1887                     catch (SocketException socketException)
1888                     {
1889                         invalidAddressCount++;
1890                         this.TraceConnectFailure(socketException);
1891                         lastException = socketException;
1892                         currentIndex++;
1893                     }
1894                 }
1895 
1896                 if (socket == null)
1897                 {
1898                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
1899                         new EndpointNotFoundException(SR.GetString(SR.NoIPEndpointsFoundForHost, uri.Host)));
1900                 }
1901 
1902                 Fx.Assert(lastException != null, "StartConnect: Can't get here without an exception.");
1903                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
1904                     SocketConnectionInitiator.ConvertConnectException(lastException, uri,
1905                     timeoutHelper.ElapsedTime(), lastException));
1906             }
1907 
TraceConnectFailure(SocketException exception)1908             void TraceConnectFailure(SocketException exception)
1909             {
1910                 SocketConnectionInitiator.TraceConnectFailure(this.socket, exception, uri, DateTime.UtcNow - connectStartTime);
1911                 this.socket.Close();
1912             }
1913 
OnConnect(IAsyncResult result)1914             static void OnConnect(IAsyncResult result)
1915             {
1916                 if (result.CompletedSynchronously)
1917                 {
1918                     return;
1919                 }
1920 
1921                 bool completeSelf = false;
1922                 Exception completionException = null;
1923                 ConnectAsyncResult thisPtr = (ConnectAsyncResult)result.AsyncState;
1924                 try
1925                 {
1926                     thisPtr.socket.EndConnect(result);
1927                     completeSelf = true;
1928                 }
1929                 catch (SocketException socketException)
1930                 {
1931                     thisPtr.TraceConnectFailure(socketException);
1932                     thisPtr.lastException = socketException;
1933                     thisPtr.currentIndex++;
1934                     try
1935                     {
1936                         completeSelf = thisPtr.StartConnect();
1937                     }
1938 #pragma warning suppress 56500 // Microsoft, transferring exception to another thread
1939                     catch (Exception e)
1940                     {
1941                         if (Fx.IsFatal(e))
1942                         {
1943                             throw;
1944                         }
1945                         completeSelf = true;
1946                         completionException = e;
1947                     }
1948                 }
1949 
1950                 if (completeSelf)
1951                 {
1952                     thisPtr.Complete(false, completionException);
1953                 }
1954             }
1955 
End(IAsyncResult result)1956             public static Socket End(IAsyncResult result)
1957             {
1958                 ConnectAsyncResult thisPtr = AsyncResult.End<ConnectAsyncResult>(result);
1959                 return thisPtr.socket;
1960             }
1961         }
1962     }
1963 
1964     internal interface ISocketListenerSettings
1965     {
1966         int BufferSize { get; }
1967         bool TeredoEnabled { get; }
1968         int ListenBacklog { get; }
1969     }
1970 
1971     class SocketConnectionListener : IConnectionListener
1972     {
1973         IPEndPoint localEndpoint;
1974         bool isDisposed;
1975         bool isListening;
1976         Socket listenSocket;
1977         ISocketListenerSettings settings;
1978         bool useOnlyOverlappedIO;
1979         ConnectionBufferPool connectionBufferPool;
1980         SocketAsyncEventArgsPool socketAsyncEventArgsPool;
1981 
SocketConnectionListener(Socket listenSocket, ISocketListenerSettings settings, bool useOnlyOverlappedIO)1982         public SocketConnectionListener(Socket listenSocket, ISocketListenerSettings settings, bool useOnlyOverlappedIO)
1983             : this(settings, useOnlyOverlappedIO)
1984         {
1985             this.listenSocket = listenSocket;
1986         }
1987 
SocketConnectionListener(IPEndPoint localEndpoint, ISocketListenerSettings settings, bool useOnlyOverlappedIO)1988         public SocketConnectionListener(IPEndPoint localEndpoint, ISocketListenerSettings settings, bool useOnlyOverlappedIO)
1989             : this(settings, useOnlyOverlappedIO)
1990         {
1991             this.localEndpoint = localEndpoint;
1992         }
1993 
SocketConnectionListener(ISocketListenerSettings settings, bool useOnlyOverlappedIO)1994         SocketConnectionListener(ISocketListenerSettings settings, bool useOnlyOverlappedIO)
1995         {
1996             Fx.Assert(settings != null, "Input settings should not be null");
1997             this.settings = settings;
1998             this.useOnlyOverlappedIO = useOnlyOverlappedIO;
1999             this.connectionBufferPool = new ConnectionBufferPool(settings.BufferSize);
2000         }
2001 
2002         object ThisLock
2003         {
2004             get { return this; }
2005         }
2006 
BeginAccept(AsyncCallback callback, object state)2007         public IAsyncResult BeginAccept(AsyncCallback callback, object state)
2008         {
2009             return new AcceptAsyncResult(this, callback, state);
2010         }
2011 
TakeSocketAsyncEventArgs()2012         SocketAsyncEventArgs TakeSocketAsyncEventArgs()
2013         {
2014             return this.socketAsyncEventArgsPool.Take();
2015         }
2016 
ReturnSocketAsyncEventArgs(SocketAsyncEventArgs socketAsyncEventArgs)2017         void ReturnSocketAsyncEventArgs(SocketAsyncEventArgs socketAsyncEventArgs)
2018         {
2019             Fx.Assert(socketAsyncEventArgsPool != null, "The socketAsyncEventArgsPool should not be null");
2020             this.socketAsyncEventArgsPool.Return(socketAsyncEventArgs);
2021         }
2022 
2023         // This is the buffer size that is used by the System.Net for accepting new connections
GetAcceptBufferSize(Socket listenSocket)2024         static int GetAcceptBufferSize(Socket listenSocket)
2025         {
2026             return (listenSocket.LocalEndPoint.Serialize().Size + 16) * 2;
2027         }
2028 
InternalBeginAccept(Func<Socket, bool> acceptAsyncFunc)2029         bool InternalBeginAccept(Func<Socket, bool> acceptAsyncFunc)
2030         {
2031             lock (ThisLock)
2032             {
2033                 if (isDisposed)
2034                 {
2035                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ObjectDisposedException(this.GetType().ToString(), SR.GetString(SR.SocketListenerDisposed)));
2036                 }
2037 
2038                 if (!isListening)
2039                 {
2040                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SocketListenerNotListening)));
2041                 }
2042 
2043                 return acceptAsyncFunc(listenSocket);
2044             }
2045         }
2046 
EndAccept(IAsyncResult result)2047         public IConnection EndAccept(IAsyncResult result)
2048         {
2049             Socket socket = AcceptAsyncResult.End(result);
2050 
2051             if (socket == null)
2052                 return null;
2053 
2054             if (useOnlyOverlappedIO)
2055             {
2056                 socket.UseOnlyOverlappedIO = true;
2057             }
2058             return new SocketConnection(socket, this.connectionBufferPool, false);
2059         }
2060 
Dispose()2061         public void Dispose()
2062         {
2063             lock (ThisLock)
2064             {
2065                 if (!isDisposed)
2066                 {
2067                     if (listenSocket != null)
2068                     {
2069                         listenSocket.Close();
2070                     }
2071 
2072                     if (this.socketAsyncEventArgsPool != null)
2073                     {
2074                         this.socketAsyncEventArgsPool.Close();
2075                     }
2076 
2077                     isDisposed = true;
2078                 }
2079             }
2080         }
2081 
2082 
Listen()2083         public void Listen()
2084         {
2085             // If you call listen() on a port, then kill the process, then immediately start a new process and
2086             // try to listen() on the same port, you sometimes get WSAEADDRINUSE.  Even if nothing was accepted.
2087             // Ports don't immediately free themselves on process shutdown.  We call listen() in a loop on a delay
2088             // for a few iterations for this reason.
2089             //
2090             TimeSpan listenTimeout = TimeSpan.FromSeconds(1);
2091             BackoffTimeoutHelper backoffHelper = new BackoffTimeoutHelper(listenTimeout);
2092 
2093             lock (ThisLock)
2094             {
2095                 if (this.listenSocket != null)
2096                 {
2097                     this.listenSocket.Listen(settings.ListenBacklog);
2098                     isListening = true;
2099                 }
2100 
2101                 while (!isListening)
2102                 {
2103                     try
2104                     {
2105                         this.listenSocket = new Socket(localEndpoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
2106 
2107                         if (localEndpoint.AddressFamily == AddressFamily.InterNetworkV6 && settings.TeredoEnabled)
2108                         {
2109                             this.listenSocket.SetSocketOption(SocketOptionLevel.IPv6, (SocketOptionName)23, 10);
2110                         }
2111 
2112                         this.listenSocket.Bind(localEndpoint);
2113                         this.listenSocket.Listen(settings.ListenBacklog);
2114                         isListening = true;
2115                     }
2116                     catch (SocketException socketException)
2117                     {
2118                         bool retry = false;
2119 
2120                         if (socketException.ErrorCode == UnsafeNativeMethods.WSAEADDRINUSE)
2121                         {
2122                             if (!backoffHelper.IsExpired())
2123                             {
2124                                 backoffHelper.WaitAndBackoff();
2125                                 retry = true;
2126                             }
2127                         }
2128 
2129                         if (!retry)
2130                         {
2131                             throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
2132                                 SocketConnectionListener.ConvertListenException(socketException, this.localEndpoint));
2133                         }
2134                     }
2135                 }
2136 
2137                 this.socketAsyncEventArgsPool = new SocketAsyncEventArgsPool(GetAcceptBufferSize(this.listenSocket));
2138             }
2139         }
2140 
ConvertListenException(SocketException socketException, IPEndPoint localEndpoint)2141         public static Exception ConvertListenException(SocketException socketException, IPEndPoint localEndpoint)
2142         {
2143             if (socketException.ErrorCode == UnsafeNativeMethods.ERROR_INVALID_HANDLE)
2144             {
2145                 return new CommunicationObjectAbortedException(socketException.Message, socketException);
2146             }
2147             if (socketException.ErrorCode == UnsafeNativeMethods.WSAEADDRINUSE)
2148             {
2149                 return new AddressAlreadyInUseException(SR.GetString(SR.TcpAddressInUse, localEndpoint.ToString()), socketException);
2150             }
2151             else
2152             {
2153                 return new CommunicationException(
2154                     SR.GetString(SR.TcpListenError, socketException.ErrorCode, socketException.Message, localEndpoint.ToString()),
2155                     socketException);
2156             }
2157         }
2158 
2159         class AcceptAsyncResult : AsyncResult
2160         {
2161             SocketConnectionListener listener;
2162             Socket socket;
2163             SocketAsyncEventArgs socketAsyncEventArgs;
2164             static Action<object> startAccept;
2165             EventTraceActivity eventTraceActivity;
2166 
2167             //
2168             static EventHandler<SocketAsyncEventArgs> acceptAsyncCompleted = new EventHandler<SocketAsyncEventArgs>(AcceptAsyncCompleted);
2169             static Action<AsyncResult, Exception> onCompleting = new Action<AsyncResult, Exception>(OnInternalCompleting);
2170 
AcceptAsyncResult(SocketConnectionListener listener, AsyncCallback callback, object state)2171             public AcceptAsyncResult(SocketConnectionListener listener, AsyncCallback callback, object state)
2172                 : base(callback, state)
2173             {
2174 
2175                 if (TD.SocketAcceptEnqueuedIsEnabled())
2176                 {
2177                     TD.SocketAcceptEnqueued(this.EventTraceActivity);
2178                 }
2179 
2180                 Fx.Assert(listener != null, "listener should not be null");
2181                 this.listener = listener;
2182                 this.socketAsyncEventArgs = listener.TakeSocketAsyncEventArgs();
2183                 this.socketAsyncEventArgs.UserToken = this;
2184                 this.socketAsyncEventArgs.Completed += acceptAsyncCompleted;
2185                 this.OnCompleting = onCompleting;
2186 
2187                 // If we're going to start up the thread pool eventually anyway, avoid using RegisterWaitForSingleObject
2188                 if (!Thread.CurrentThread.IsThreadPoolThread)
2189                 {
2190                     if (startAccept == null)
2191                     {
2192                         startAccept = new Action<object>(StartAccept);
2193                     }
2194 
2195                     ActionItem.Schedule(startAccept, this);
2196                 }
2197                 else
2198                 {
2199                     bool completeSelf;
2200                     bool success = false;
2201                     try
2202                     {
2203                         completeSelf = StartAccept();
2204                         success = true;
2205                     }
2206                     finally
2207                     {
2208                         if (!success)
2209                         {
2210                             // Return the args when an exception is thrown
2211                             ReturnSocketAsyncEventArgs();
2212                         }
2213                     }
2214 
2215                     if (completeSelf)
2216                     {
2217                         base.Complete(true);
2218                     }
2219                 }
2220             }
2221 
2222             public EventTraceActivity EventTraceActivity
2223             {
2224                 get
2225                 {
2226                     if (this.eventTraceActivity == null)
2227                     {
2228                         this.eventTraceActivity = new EventTraceActivity();
2229                     }
2230 
2231                     return this.eventTraceActivity;
2232                 }
2233             }
2234 
StartAccept(object state)2235             static void StartAccept(object state)
2236             {
2237                 AcceptAsyncResult thisPtr = (AcceptAsyncResult)state;
2238 
2239                 Exception completionException = null;
2240                 bool completeSelf;
2241                 try
2242                 {
2243                     completeSelf = thisPtr.StartAccept();
2244                 }
2245 #pragma warning suppress 56500 // Microsoft, transferring exception to another thread
2246                 catch (Exception e)
2247                 {
2248                     if (Fx.IsFatal(e))
2249                     {
2250                         throw;
2251                     }
2252                     completeSelf = true;
2253                     completionException = e;
2254                 }
2255                 if (completeSelf)
2256                 {
2257                     thisPtr.Complete(false, completionException);
2258                 }
2259             }
2260 
StartAccept()2261             bool StartAccept()
2262             {
2263                 while (true)
2264                 {
2265                     try
2266                     {
2267                         return listener.InternalBeginAccept(DoAcceptAsync);
2268                     }
2269                     catch (SocketException socketException)
2270                     {
2271                         if (ShouldAcceptRecover(socketException))
2272                         {
2273                             continue;
2274                         }
2275                         else
2276                         {
2277                             throw;
2278                         }
2279                     }
2280                 }
2281             }
2282 
ShouldAcceptRecover(SocketException exception)2283             static bool ShouldAcceptRecover(SocketException exception)
2284             {
2285                 return (
2286                     (exception.ErrorCode == UnsafeNativeMethods.WSAECONNRESET) ||
2287                     (exception.ErrorCode == UnsafeNativeMethods.WSAEMFILE) ||
2288                     (exception.ErrorCode == UnsafeNativeMethods.WSAENOBUFS) ||
2289                     (exception.ErrorCode == UnsafeNativeMethods.WSAETIMEDOUT)
2290                 );
2291             }
2292 
2293             // Return true means completed synchronously
DoAcceptAsync(Socket listenSocket)2294             bool DoAcceptAsync(Socket listenSocket)
2295             {
2296                 SocketAsyncEventArgsPool.CleanupAcceptSocket(this.socketAsyncEventArgs);
2297 
2298                 if (listenSocket.AcceptAsync(this.socketAsyncEventArgs))
2299                 {
2300                     // AcceptAsync returns true to indicate that the I/O operation is pending (asynchronous)
2301                     return false;
2302                 }
2303 
2304                 Exception exception = HandleAcceptAsyncCompleted();
2305                 if (exception != null)
2306                 {
2307                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(exception);
2308                 }
2309 
2310                 return true;
2311             }
2312 
AcceptAsyncCompleted(object sender, SocketAsyncEventArgs e)2313             static void AcceptAsyncCompleted(object sender, SocketAsyncEventArgs e)
2314             {
2315                 AcceptAsyncResult thisPtr = (AcceptAsyncResult)e.UserToken;
2316                 Fx.Assert(thisPtr.socketAsyncEventArgs == e, "Got wrong socketAsyncEventArgs");
2317                 Exception completionException = thisPtr.HandleAcceptAsyncCompleted();
2318                 if (completionException != null && ShouldAcceptRecover((SocketException)completionException))
2319                 {
2320                     DiagnosticUtility.TraceHandledException(completionException, TraceEventType.Warning);
2321 
2322                     StartAccept(thisPtr);
2323                     return;
2324                 }
2325 
2326                 thisPtr.Complete(false, completionException);
2327             }
2328 
OnInternalCompleting(AsyncResult result, Exception exception)2329             static void OnInternalCompleting(AsyncResult result, Exception exception)
2330             {
2331                 AcceptAsyncResult thisPtr = result as AcceptAsyncResult;
2332 
2333                 if (TD.SocketAcceptedIsEnabled())
2334                 {
2335                     int hashCode = thisPtr.socket != null ? thisPtr.socket.GetHashCode() : -1;
2336                     if (hashCode != -1)
2337                     {
2338                         TD.SocketAccepted(
2339                             thisPtr.EventTraceActivity,
2340                             thisPtr.listener != null ? thisPtr.listener.GetHashCode() : -1,
2341                             hashCode);
2342                     }
2343                     else
2344                     {
2345                         TD.SocketAcceptClosed(thisPtr.EventTraceActivity);
2346                     }
2347                 }
2348 
2349                 Fx.Assert(result != null, "Wrong async result has been passed in to OnInternalCompleting");
2350                 thisPtr.ReturnSocketAsyncEventArgs();
2351             }
2352 
ReturnSocketAsyncEventArgs()2353             void ReturnSocketAsyncEventArgs()
2354             {
2355                 if (this.socketAsyncEventArgs != null)
2356                 {
2357                     this.socketAsyncEventArgs.UserToken = null;
2358                     this.socketAsyncEventArgs.Completed -= acceptAsyncCompleted;
2359                     this.listener.ReturnSocketAsyncEventArgs(this.socketAsyncEventArgs);
2360                     this.socketAsyncEventArgs = null;
2361                 }
2362             }
2363 
HandleAcceptAsyncCompleted()2364             Exception HandleAcceptAsyncCompleted()
2365             {
2366                 Exception completionException = null;
2367                 if (this.socketAsyncEventArgs.SocketError == SocketError.Success)
2368                 {
2369                     this.socket = this.socketAsyncEventArgs.AcceptSocket;
2370                     this.socketAsyncEventArgs.AcceptSocket = null;
2371                 }
2372                 else
2373                 {
2374                     completionException = new SocketException((int)this.socketAsyncEventArgs.SocketError);
2375                 }
2376 
2377                 return completionException;
2378             }
2379 
End(IAsyncResult result)2380             public static Socket End(IAsyncResult result)
2381             {
2382                 AcceptAsyncResult thisPtr = AsyncResult.End<AcceptAsyncResult>(result);
2383                 return thisPtr.socket;
2384             }
2385         }
2386     }
2387 }
2388