Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/mono.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/mcs/class
diff options
context:
space:
mode:
authorMartin Baulig <mabaul@microsoft.com>2017-06-06 18:46:29 +0300
committerGitHub <noreply@github.com>2017-06-06 18:46:29 +0300
commit21d5c9c346113d47455d96e545410733bfccf1fb (patch)
treeaf9a3c55214f726550f7ded76a729b5de5fcea84 /mcs/class
parentd0b0e2b880f49726afcfab48ee4a8c80becfc93f (diff)
[System]: Correctly implement close and shutdown in SslStream. (#4969)
* [System]: Correctly implement close and shutdown in SslStream. * Cleanup. * Cosmetic. * More ReferenceSources/SR2.cs into common.sources to make it build.
Diffstat (limited to 'mcs/class')
-rw-r--r--mcs/class/Mono.Security/Mono.Security.Interface/IMonoSslStream.cs2
-rw-r--r--mcs/class/System/Mono.AppleTls/AppleTlsContext.cs6
-rw-r--r--mcs/class/System/Mono.AppleTls/AppleTlsStream.cs8
-rw-r--r--mcs/class/System/Mono.Btls/MonoBtlsContext.cs30
-rw-r--r--mcs/class/System/Mono.Btls/MonoBtlsObject.cs14
-rw-r--r--mcs/class/System/Mono.Btls/MonoBtlsSsl.cs48
-rw-r--r--mcs/class/System/Mono.Btls/MonoBtlsStream.cs8
-rw-r--r--mcs/class/System/Mono.Net.Security/AsyncProtocolRequest.cs384
-rw-r--r--mcs/class/System/Mono.Net.Security/LegacySslStream.cs5
-rw-r--r--mcs/class/System/Mono.Net.Security/MobileAuthenticatedStream.cs651
-rw-r--r--mcs/class/System/Mono.Net.Security/MobileTlsContext.cs2
-rw-r--r--mcs/class/System/ReferenceSources/SR2.cs2
-rw-r--r--mcs/class/System/System.Net.Security/SslStream.cs5
-rw-r--r--mcs/class/System/System.Net.Security/SslStream.platformnotsupported.cs5
-rw-r--r--mcs/class/System/common.sources1
-rw-r--r--mcs/class/System/net_4_x_System.dll.sources1
16 files changed, 646 insertions, 526 deletions
diff --git a/mcs/class/Mono.Security/Mono.Security.Interface/IMonoSslStream.cs b/mcs/class/Mono.Security/Mono.Security.Interface/IMonoSslStream.cs
index 4982e804477..00556863db9 100644
--- a/mcs/class/Mono.Security/Mono.Security.Interface/IMonoSslStream.cs
+++ b/mcs/class/Mono.Security/Mono.Security.Interface/IMonoSslStream.cs
@@ -86,6 +86,8 @@ namespace Mono.Security.Interface
void EndWrite (IAsyncResult asyncResult);
+ Task ShutdownAsync ();
+
TransportContext TransportContext {
get;
}
diff --git a/mcs/class/System/Mono.AppleTls/AppleTlsContext.cs b/mcs/class/System/Mono.AppleTls/AppleTlsContext.cs
index 6a57babab4e..4fa1730df2a 100644
--- a/mcs/class/System/Mono.AppleTls/AppleTlsContext.cs
+++ b/mcs/class/System/Mono.AppleTls/AppleTlsContext.cs
@@ -848,12 +848,12 @@ namespace Mono.AppleTls
[DllImport (SecurityLibrary)]
extern static /* OSStatus */ SslStatus SSLClose (/* SSLContextRef */ IntPtr context);
- public override void Close ()
+ public override void Shutdown ()
{
if (Interlocked.Exchange (ref pendingIO, 1) == 1)
throw new InvalidOperationException ();
- Debug ("Close");
+ Debug ("Shutdown");
lastException = null;
@@ -862,7 +862,7 @@ namespace Mono.AppleTls
return;
var status = SSLClose (Handle);
- Debug ("Close done: {0}", status);
+ Debug ("Shutdown done: {0}", status);
CheckStatusAndThrow (status);
} finally {
closed = true;
diff --git a/mcs/class/System/Mono.AppleTls/AppleTlsStream.cs b/mcs/class/System/Mono.AppleTls/AppleTlsStream.cs
index e3b9fa85933..412b4da1f10 100644
--- a/mcs/class/System/Mono.AppleTls/AppleTlsStream.cs
+++ b/mcs/class/System/Mono.AppleTls/AppleTlsStream.cs
@@ -38,12 +38,12 @@ namespace Mono.AppleTls
}
protected override MNS.MobileTlsContext CreateContext (
- MNS.MobileAuthenticatedStream parent, bool serverMode, string targetHost,
- SslProtocols enabledProtocols, X509Certificate serverCertificate,
- X509CertificateCollection clientCertificates, bool askForClientCert)
+ bool serverMode, string targetHost, SslProtocols enabledProtocols,
+ X509Certificate serverCertificate, X509CertificateCollection clientCertificates,
+ bool askForClientCert)
{
return new AppleTlsContext (
- parent, serverMode, targetHost,
+ this, serverMode, targetHost,
enabledProtocols, serverCertificate,
clientCertificates, askForClientCert);
}
diff --git a/mcs/class/System/Mono.Btls/MonoBtlsContext.cs b/mcs/class/System/Mono.Btls/MonoBtlsContext.cs
index 841daa270fa..08c2678a227 100644
--- a/mcs/class/System/Mono.Btls/MonoBtlsContext.cs
+++ b/mcs/class/System/Mono.Btls/MonoBtlsContext.cs
@@ -316,6 +316,9 @@ namespace Mono.Btls
if (status == MonoBtlsSslError.WantRead) {
wantMore = true;
return 0;
+ } else if (status == MonoBtlsSslError.ZeroReturn) {
+ wantMore = false;
+ return size;
} else if (status != MonoBtlsSslError.None) {
throw GetException (status);
}
@@ -358,26 +361,11 @@ namespace Mono.Btls
}
}
- public override void Close ()
+ public override void Shutdown ()
{
- Debug ("Close!");
-
- if (ssl != null) {
- ssl.Dispose ();
- ssl = null;
- }
- if (ctx != null) {
- ctx.Dispose ();
- ctx = null;
- }
- if (bio != null) {
- bio.Dispose ();
- bio = null;
- }
- if (errbio != null) {
- errbio.Dispose ();
- errbio = null;
- }
+ Debug ("Shutdown!");
+// ssl.SetQuietShutdown ();
+ ssl.Shutdown ();
}
void Dispose<T> (ref T disposable)
@@ -397,12 +385,12 @@ namespace Mono.Btls
{
try {
if (disposing) {
+ Dispose (ref ssl);
+ Dispose (ref ctx);
Dispose (ref remoteCertificate);
Dispose (ref nativeServerCertificate);
Dispose (ref nativeClientCertificate);
Dispose (ref clientCertificate);
- Dispose (ref ctx);
- Dispose (ref ssl);
Dispose (ref bio);
Dispose (ref errbio);
}
diff --git a/mcs/class/System/Mono.Btls/MonoBtlsObject.cs b/mcs/class/System/Mono.Btls/MonoBtlsObject.cs
index 4264411bd90..19e72cbda64 100644
--- a/mcs/class/System/Mono.Btls/MonoBtlsObject.cs
+++ b/mcs/class/System/Mono.Btls/MonoBtlsObject.cs
@@ -102,6 +102,20 @@ namespace Mono.Btls
CheckError (ret == 1, callerName);
}
+ protected internal void CheckLastError ([CallerMemberName] string callerName = null)
+ {
+ var error = Interlocked.Exchange (ref lastError, null);
+ if (error == null)
+ return;
+
+ string message;
+ if (callerName != null)
+ message = string.Format ("Caught unhandled exception in {0}.{1}.", GetType ().Name, callerName);
+ else
+ message = string.Format ("Caught unhandled exception.");
+ throw new MonoBtlsException (message, error);
+ }
+
[DllImport (BTLS_DYLIB)]
extern static void mono_btls_free (IntPtr data);
diff --git a/mcs/class/System/Mono.Btls/MonoBtlsSsl.cs b/mcs/class/System/Mono.Btls/MonoBtlsSsl.cs
index 09e171485f6..e5fac698c0e 100644
--- a/mcs/class/System/Mono.Btls/MonoBtlsSsl.cs
+++ b/mcs/class/System/Mono.Btls/MonoBtlsSsl.cs
@@ -47,6 +47,7 @@ namespace Mono.Btls
protected override bool ReleaseHandle ()
{
mono_btls_ssl_destroy (handle);
+ handle = IntPtr.Zero;
return true;
}
}
@@ -79,6 +80,12 @@ namespace Mono.Btls
extern static void mono_btls_ssl_close (IntPtr handle);
[DllImport (BTLS_DYLIB)]
+ extern static int mono_btls_ssl_shutdown (IntPtr handle);
+
+ [DllImport (BTLS_DYLIB)]
+ extern static void mono_btls_ssl_set_quiet_shutdown (IntPtr handle, int mode);
+
+ [DllImport (BTLS_DYLIB)]
extern static void mono_btls_ssl_set_bio (IntPtr handle, IntPtr bio);
[DllImport (BTLS_DYLIB)]
@@ -131,6 +138,7 @@ namespace Mono.Btls
return new BoringSslHandle (handle);
}
+ MonoBtlsBio bio;
PrintErrorsCallbackFunc printErrorsFunc;
IntPtr printErrorsFuncPtr;
@@ -148,6 +156,7 @@ namespace Mono.Btls
public void SetBio (MonoBtlsBio bio)
{
CheckThrow ();
+ this.bio = bio;
mono_btls_ssl_set_bio (
Handle.DangerousGetHandle (),
bio.Handle.DangerousGetHandle ());
@@ -164,18 +173,17 @@ namespace Mono.Btls
errors = null;
}
- if (errors != null) {
- Console.Error.WriteLine ("ERROR: {0} failed: {1}", callerName, errors);
+ if (errors != null)
throw new MonoBtlsException ("{0} failed: {1}.", callerName, errors);
- } else {
- Console.Error.WriteLine ("ERROR: {0} failed.", callerName);
+ else
throw new MonoBtlsException ("{0} failed.", callerName);
- }
}
MonoBtlsSslError GetError (int ret_code)
{
CheckThrow ();
+ bio.CheckLastError ();
+
var error = mono_btls_ssl_get_error (
Handle.DangerousGetHandle (), ret_code);
return (MonoBtlsSslError)error;
@@ -287,15 +295,20 @@ namespace Mono.Btls
var ret = mono_btls_ssl_read (
Handle.DangerousGetHandle (), data, dataSize);
- if (ret >= 0) {
+ if (ret > 0) {
dataSize = ret;
return MonoBtlsSslError.None;
}
- var error = mono_btls_ssl_get_error (
- Handle.DangerousGetHandle (), ret);
+ var error = GetError (ret);
+ if (ret == 0 && error == MonoBtlsSslError.Syscall) {
+ // End-of-stream
+ dataSize = 0;
+ return MonoBtlsSslError.None;
+ }
+
dataSize = 0;
- return (MonoBtlsSslError)error;
+ return error;
}
public MonoBtlsSslError Write (IntPtr data, ref int dataSize)
@@ -416,9 +429,24 @@ namespace Mono.Btls
return Marshal.PtrToStringAnsi (namePtr);
}
+ public void Shutdown ()
+ {
+ CheckThrow ();
+ var ret = mono_btls_ssl_shutdown (Handle.DangerousGetHandle ());
+ if (ret < 0)
+ throw ThrowError ();
+ }
+
+ public void SetQuietShutdown ()
+ {
+ CheckThrow ();
+ mono_btls_ssl_set_quiet_shutdown (Handle.DangerousGetHandle (), 1);
+ }
+
protected override void Close ()
{
- mono_btls_ssl_close (Handle.DangerousGetHandle ());
+ if (!Handle.IsInvalid)
+ mono_btls_ssl_close (Handle.DangerousGetHandle ());
}
}
}
diff --git a/mcs/class/System/Mono.Btls/MonoBtlsStream.cs b/mcs/class/System/Mono.Btls/MonoBtlsStream.cs
index e941fcd1c0c..b3bb65f9b8e 100644
--- a/mcs/class/System/Mono.Btls/MonoBtlsStream.cs
+++ b/mcs/class/System/Mono.Btls/MonoBtlsStream.cs
@@ -53,12 +53,12 @@ namespace Mono.Btls
}
protected override MNS.MobileTlsContext CreateContext (
- MNS.MobileAuthenticatedStream parent, bool serverMode, string targetHost,
- SslProtocols enabledProtocols, X509Certificate serverCertificate,
- X509CertificateCollection clientCertificates, bool askForClientCert)
+ bool serverMode, string targetHost, SslProtocols enabledProtocols,
+ X509Certificate serverCertificate, X509CertificateCollection clientCertificates,
+ bool askForClientCert)
{
return new MonoBtlsContext (
- parent, serverMode, targetHost,
+ this, serverMode, targetHost,
enabledProtocols, serverCertificate,
clientCertificates, askForClientCert);
}
diff --git a/mcs/class/System/Mono.Net.Security/AsyncProtocolRequest.cs b/mcs/class/System/Mono.Net.Security/AsyncProtocolRequest.cs
index 5f90ffe2b82..3c537a7e427 100644
--- a/mcs/class/System/Mono.Net.Security/AsyncProtocolRequest.cs
+++ b/mcs/class/System/Mono.Net.Security/AsyncProtocolRequest.cs
@@ -11,14 +11,14 @@ using System;
using System.IO;
using System.Net;
using System.Net.Security;
+using System.Security.Authentication;
using SD = System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
+using System.Runtime.ExceptionServices;
namespace Mono.Net.Security
{
- delegate AsyncOperationStatus AsyncOperation (AsyncProtocolRequest asyncRequest, AsyncOperationStatus status);
-
class BufferOffsetSize
{
public byte[] Buffer;
@@ -37,6 +37,13 @@ namespace Mono.Net.Security
public BufferOffsetSize (byte[] buffer, int offset, int size)
{
+ if (buffer == null)
+ throw new ArgumentNullException (nameof (buffer));
+ if (offset < 0)
+ throw new ArgumentOutOfRangeException (nameof (offset));
+ if (size < 0 || offset + size > buffer.Length)
+ throw new ArgumentOutOfRangeException (nameof (size));
+
Buffer = buffer;
Offset = offset;
Size = size;
@@ -54,7 +61,7 @@ namespace Mono.Net.Security
public readonly int InitialSize;
public BufferOffsetSize2 (int size)
- : base (new byte [size], 0, 0)
+ : base (new byte[size], 0, 0)
{
InitialSize = size;
}
@@ -63,7 +70,7 @@ namespace Mono.Net.Security
{
Offset = Size = 0;
TotalBytes = 0;
- Buffer = new byte [InitialSize];
+ Buffer = new byte[InitialSize];
Complete = false;
}
@@ -74,11 +81,11 @@ namespace Mono.Net.Security
int missing = size - Remaining;
if (Offset == 0 && Size == 0) {
- Buffer = new byte [size];
+ Buffer = new byte[size];
return;
}
- var buffer = new byte [Buffer.Length + missing];
+ var buffer = new byte[Buffer.Length + missing];
Buffer.CopyTo (buffer, 0);
Buffer = buffer;
}
@@ -91,201 +98,296 @@ namespace Mono.Net.Security
}
}
- enum AsyncOperationStatus {
- NotStarted,
+ enum AsyncOperationStatus
+ {
Initialize,
Continue,
- Running,
- Complete,
- WantRead,
- WantWrite,
ReadDone,
- FinishWrite
+ Complete
}
- class AsyncProtocolRequest
+ class AsyncProtocolResult
{
- public readonly MobileAuthenticatedStream Parent;
- public readonly BufferOffsetSize UserBuffer;
+ public int UserResult {
+ get;
+ }
+ public ExceptionDispatchInfo Error {
+ get;
+ }
- int RequestedSize;
- public int CurrentSize;
- public int UserResult;
+ public AsyncProtocolResult (int result)
+ {
+ UserResult = result;
+ }
- AsyncOperation Operation;
- int Status;
+ public AsyncProtocolResult (ExceptionDispatchInfo error)
+ {
+ Error = error;
+ }
+ }
- public readonly int ID = ++next_id;
- static int next_id;
+ abstract class AsyncProtocolRequest
+ {
+ public MobileAuthenticatedStream Parent {
+ get;
+ }
- public readonly LazyAsyncResult UserAsyncResult;
+ public bool RunSynchronously {
+ get;
+ }
- public AsyncProtocolRequest (MobileAuthenticatedStream parent, LazyAsyncResult lazyResult, BufferOffsetSize userBuffer = null)
- {
- Parent = parent;
- UserAsyncResult = lazyResult;
- UserBuffer = userBuffer;
+ public int ID => ++next_id;
+
+ public string Name => GetType ().Name;
+
+ public int UserResult {
+ get;
+ protected set;
}
- public bool CompleteWithError (Exception ex)
+ int Started;
+ int RequestedSize;
+ int WriteRequested;
+ readonly object locker = new object ();
+
+ static int next_id;
+
+ public AsyncProtocolRequest (MobileAuthenticatedStream parent, bool sync)
{
- Status = (int)AsyncOperationStatus.Complete;
- if (UserAsyncResult == null)
- return true;
- if (!UserAsyncResult.InternalPeekCompleted)
- UserAsyncResult.InvokeCallback (ex);
- return false;
+ Parent = parent;
+ RunSynchronously = sync;
}
[SD.Conditional ("MARTIN_DEBUG")]
protected void Debug (string message, params object[] args)
{
- Parent.Debug ("AsyncProtocolRequest({0}:{1}): {2}", Parent.ID, ID, string.Format (message, args));
+ Parent.Debug ("{0}({1}:{2}): {3}", Name, Parent.ID, ID, string.Format (message, args));
}
internal void RequestRead (int size)
{
- var oldStatus = (AsyncOperationStatus)Interlocked.CompareExchange (ref Status, (int)AsyncOperationStatus.WantRead, (int)AsyncOperationStatus.Running);
- Debug ("RequestRead: {0} {1}", oldStatus, size);
- if (oldStatus == AsyncOperationStatus.Running)
- RequestedSize = size;
- else if (oldStatus == AsyncOperationStatus.WantRead)
+ lock (locker) {
RequestedSize += size;
- else if (oldStatus != AsyncOperationStatus.WantWrite)
- throw new InvalidOperationException ();
+ Debug ("RequestRead: {0}", size);
+ }
}
- internal void ResetRead ()
+ internal void RequestWrite ()
{
- var oldStatus = (AsyncOperationStatus)Interlocked.CompareExchange (ref Status, (int)AsyncOperationStatus.Complete, (int)AsyncOperationStatus.WantRead);
- Debug ("ResetRead: {0} {1}", oldStatus, Status);
+ WriteRequested = 1;
}
- internal void ResetWrite ()
+ internal async Task<AsyncProtocolResult> StartOperation (CancellationToken cancellationToken)
{
- var oldStatus = (AsyncOperationStatus)Interlocked.CompareExchange (ref Status, (int)AsyncOperationStatus.Complete, (int)AsyncOperationStatus.WantWrite);
- Debug ("ResetWrite: {0} {1}", oldStatus, Status);
+ Debug ("Start Operation: {0}", this);
+ if (Interlocked.CompareExchange (ref Started, 1, 0) != 0)
+ throw new InvalidOperationException ();
+
+ try {
+ await ProcessOperation (cancellationToken).ConfigureAwait (false);
+ return new AsyncProtocolResult (UserResult);
+ } catch (Exception ex) {
+ var info = Parent.SetException (MobileAuthenticatedStream.GetSSPIException (ex));
+ return new AsyncProtocolResult (info);
+ }
}
- internal void RequestWrite ()
+ async Task ProcessOperation (CancellationToken cancellationToken)
{
- var oldStatus = (AsyncOperationStatus)Interlocked.CompareExchange (ref Status, (int)AsyncOperationStatus.WantWrite, (int)AsyncOperationStatus.Running);
- Debug ("RequestWrite: {0} {1}", oldStatus, Status);
- if (oldStatus == AsyncOperationStatus.Running)
- return;
- else if (oldStatus != AsyncOperationStatus.WantRead && oldStatus != AsyncOperationStatus.WantWrite)
- throw new InvalidOperationException ();
+ var status = AsyncOperationStatus.Initialize;
+ while (status != AsyncOperationStatus.Complete) {
+ cancellationToken.ThrowIfCancellationRequested ();
+ Debug ("ProcessOperation: {0}", status);
+
+ var ret = await InnerRead (cancellationToken).ConfigureAwait (false);
+ if (ret != null) {
+ if (ret == 0) {
+ // End-of-stream
+ Debug ("END OF STREAM!");
+ status = AsyncOperationStatus.ReadDone;
+ } else if (ret < 0) {
+ // remote prematurely closed connection.
+ throw new IOException ("Remote prematurely closed connection.");
+ }
+ }
+
+ Debug ("ProcessOperation run: {0}", status);
+
+ AsyncOperationStatus newStatus;
+ switch (status) {
+ case AsyncOperationStatus.Initialize:
+ case AsyncOperationStatus.Continue:
+ case AsyncOperationStatus.ReadDone:
+ newStatus = Run (status);
+ break;
+ default:
+ throw new InvalidOperationException ();
+ }
+
+ if (Interlocked.Exchange (ref WriteRequested, 0) != 0) {
+ // Flush the write queue.
+ await Parent.InnerWrite (RunSynchronously, cancellationToken);
+ }
+
+ Debug ("ProcessOperation done: {0} -> {1}", status, newStatus);
+
+ status = newStatus;
+ }
}
- internal void StartOperation (AsyncOperation operation)
+ async Task<int?> InnerRead (CancellationToken cancellationToken)
{
- Debug ("Start Operation: {0} {1}", Status, operation);
- if (Interlocked.CompareExchange (ref Status, (int)AsyncOperationStatus.Initialize, (int)AsyncOperationStatus.NotStarted) != (int)AsyncOperationStatus.NotStarted)
- throw new InvalidOperationException ();
+ int? totalRead = null;
+ var requestedSize = Interlocked.Exchange (ref RequestedSize, 0);
+ while (requestedSize > 0) {
+ Debug ("ProcessOperation - read inner: {0}", requestedSize);
- Operation = operation;
+ var ret = await Parent.InnerRead (RunSynchronously, requestedSize, cancellationToken).ConfigureAwait (false);
+ Debug ("ProcessOperation - read inner done: {0} - {1}", requestedSize, ret);
- if (UserAsyncResult == null) {
- StartOperation ();
- return;
+ if (ret <= 0)
+ return ret;
+ if (ret > requestedSize)
+ throw new InvalidOperationException ();
+
+ totalRead += ret;
+ requestedSize -= ret;
+ var newRequestedSize = Interlocked.Exchange (ref RequestedSize, 0);
+ requestedSize += newRequestedSize;
}
- ThreadPool.QueueUserWorkItem (_ => StartOperation ());
+ return totalRead;
}
- void StartOperation ()
+ /*
+ * This will operate on the internal buffers and never block.
+ */
+ protected abstract AsyncOperationStatus Run (AsyncOperationStatus status);
+
+ public override string ToString ()
{
- try {
- ProcessOperation ();
- if (UserAsyncResult != null && !UserAsyncResult.InternalPeekCompleted)
- UserAsyncResult.InvokeCallback (UserResult);
- } catch (Exception ex) {
- if (UserAsyncResult == null)
- throw;
- if (!UserAsyncResult.InternalPeekCompleted)
- UserAsyncResult.InvokeCallback (ex);
- }
+ return string.Format ("[{0}]", Name);
}
+ }
- void ProcessOperation ()
+ class AsyncHandshakeRequest : AsyncProtocolRequest
+ {
+ public AsyncHandshakeRequest (MobileAuthenticatedStream parent, bool sync)
+ : base (parent, sync)
{
- AsyncOperationStatus status;
- do {
- status = (AsyncOperationStatus)Interlocked.Exchange (ref Status, (int)AsyncOperationStatus.Running);
+ }
- Debug ("ProcessOperation: {0}", status);
+ protected override AsyncOperationStatus Run (AsyncOperationStatus status)
+ {
+ return Parent.ProcessHandshake (status);
+ }
+ }
- status = ProcessOperation (status);
+ abstract class AsyncReadOrWriteRequest : AsyncProtocolRequest
+ {
+ protected BufferOffsetSize UserBuffer {
+ get;
+ }
- Debug ("ProcessOperation done: {0}", status);
+ protected int CurrentSize {
+ get; set;
+ }
- AsyncOperationStatus oldStatus;
- if (status == AsyncOperationStatus.Complete) {
- oldStatus = (AsyncOperationStatus)Interlocked.CompareExchange (ref Status, (int)AsyncOperationStatus.FinishWrite, (int)AsyncOperationStatus.WantWrite);
- if (oldStatus == AsyncOperationStatus.WantWrite) {
- // We are done, but still need to flush the write queue.
- status = AsyncOperationStatus.FinishWrite;
- continue;
- }
- }
+ public AsyncReadOrWriteRequest (MobileAuthenticatedStream parent, bool sync, byte[] buffer, int offset, int size)
+ : base (parent, sync)
+ {
+ UserBuffer = new BufferOffsetSize (buffer, offset, size);
+ }
- oldStatus = (AsyncOperationStatus)Interlocked.CompareExchange (ref Status, (int)status, (int)AsyncOperationStatus.Running);
- Debug ("ProcessOperation done: {0} -> {1}", oldStatus, status);
+ public override string ToString ()
+ {
+ return string.Format ("[{0}: {1}]", Name, UserBuffer);
+ }
+ }
- if (oldStatus != AsyncOperationStatus.Running) {
- if (status == oldStatus || status == AsyncOperationStatus.Continue || status == AsyncOperationStatus.Complete)
- status = oldStatus;
- else
- throw new InvalidOperationException ();
- }
- } while (status != AsyncOperationStatus.Complete);
+ class AsyncReadRequest : AsyncReadOrWriteRequest
+ {
+ public AsyncReadRequest (MobileAuthenticatedStream parent, bool sync, byte[] buffer, int offset, int size)
+ : base (parent, sync, buffer, offset, size)
+ {
}
- AsyncOperationStatus ProcessOperation (AsyncOperationStatus status)
+ protected override AsyncOperationStatus Run (AsyncOperationStatus status)
{
- if (status == AsyncOperationStatus.WantRead) {
- if (RequestedSize < 0)
- throw new InvalidOperationException ();
- else if (RequestedSize == 0)
- return AsyncOperationStatus.Continue;
-
- Debug ("ProcessOperation - read inner: {0}", RequestedSize);
- var ret = Parent.InnerRead (RequestedSize);
- Debug ("ProcessOperation - read inner done: {0} - {1}", RequestedSize, ret);
-
- if (ret < 0)
- return AsyncOperationStatus.ReadDone;
-
- RequestedSize -= ret;
-
- if (ret == 0 || RequestedSize == 0)
- return AsyncOperationStatus.Continue;
- else
- return AsyncOperationStatus.WantRead;
- } else if (status == AsyncOperationStatus.WantWrite) {
- Debug ("ProcessOperation - want write");
- Parent.InnerWrite ();
- Debug ("ProcessOperation - want write done");
+ Debug ("ProcessRead - read user: {0} {1}", this, status);
+
+ var (ret, wantMore) = Parent.ProcessRead (UserBuffer);
+
+ Debug ("ProcessRead - read user done: {0} - {1} {2}", this, ret, wantMore);
+
+ if (ret < 0) {
+ UserResult = -1;
+ return AsyncOperationStatus.Complete;
+ }
+
+ CurrentSize += ret;
+ UserBuffer.Offset += ret;
+ UserBuffer.Size -= ret;
+
+ Debug ("Process Read - read user done #1: {0} - {1} {2}", this, CurrentSize, wantMore);
+
+ if (wantMore && CurrentSize == 0)
return AsyncOperationStatus.Continue;
- } else if (status == AsyncOperationStatus.Initialize || status == AsyncOperationStatus.Continue) {
- Debug ("ProcessOperation - continue");
- status = Operation (this, status);
- Debug ("ProcessOperation - continue done: {0}", status);
- return status;
- } else if (status == AsyncOperationStatus.ReadDone) {
- Debug ("ProcessOperation - read done");
- status = Operation (this, status);
- Debug ("ProcessOperation - read done: {0}", status);
- return status;
- } else if (status == AsyncOperationStatus.FinishWrite) {
- Debug ("ProcessOperation - finish write");
- Parent.InnerWrite ();
- Debug ("ProcessOperation - finish write done");
+
+ UserResult = CurrentSize;
+ return AsyncOperationStatus.Complete;
+ }
+ }
+
+ class AsyncWriteRequest : AsyncReadOrWriteRequest
+ {
+ public AsyncWriteRequest (MobileAuthenticatedStream parent, bool sync, byte[] buffer, int offset, int size)
+ : base (parent, sync, buffer, offset, size)
+ {
+ }
+
+ protected override AsyncOperationStatus Run (AsyncOperationStatus status)
+ {
+ Debug ("ProcessWrite - write user: {0} {1}", this, status);
+
+ if (UserBuffer.Size == 0) {
+ UserResult = CurrentSize;
+ return AsyncOperationStatus.Complete;
+ }
+
+ var (ret, wantMore) = Parent.ProcessWrite (UserBuffer);
+
+ Debug ("ProcessWrite - write user done: {0} - {1} {2}", this, ret, wantMore);
+
+ if (ret < 0) {
+ UserResult = -1;
return AsyncOperationStatus.Complete;
}
- throw new InvalidOperationException ();
+ CurrentSize += ret;
+ UserBuffer.Offset += ret;
+ UserBuffer.Size -= ret;
+
+ if (wantMore)
+ return AsyncOperationStatus.Continue;
+
+ UserResult = CurrentSize;
+ return AsyncOperationStatus.Complete;
+ }
+ }
+
+ class AsyncShutdownRequest : AsyncProtocolRequest
+ {
+ public AsyncShutdownRequest (MobileAuthenticatedStream parent)
+ : base (parent, false)
+ {
+ }
+
+ protected override AsyncOperationStatus Run (AsyncOperationStatus status)
+ {
+ return Parent.ProcessShutdown (status);
}
}
+
}
#endif
diff --git a/mcs/class/System/Mono.Net.Security/LegacySslStream.cs b/mcs/class/System/Mono.Net.Security/LegacySslStream.cs
index a5572addc13..e4032496da1 100644
--- a/mcs/class/System/Mono.Net.Security/LegacySslStream.cs
+++ b/mcs/class/System/Mono.Net.Security/LegacySslStream.cs
@@ -575,6 +575,11 @@ namespace Mono.Net.Security.Private
#region IMonoSslStream
+ Task IMonoSslStream.ShutdownAsync ()
+ {
+ return Task.CompletedTask;
+ }
+
AuthenticatedStream IMonoSslStream.AuthenticatedStream {
get { return this; }
}
diff --git a/mcs/class/System/Mono.Net.Security/MobileAuthenticatedStream.cs b/mcs/class/System/Mono.Net.Security/MobileAuthenticatedStream.cs
index 1e57c673415..2b380a1ae6c 100644
--- a/mcs/class/System/Mono.Net.Security/MobileAuthenticatedStream.cs
+++ b/mcs/class/System/Mono.Net.Security/MobileAuthenticatedStream.cs
@@ -23,6 +23,7 @@ using System.IO;
using System.Net;
using System.Net.Security;
using System.Globalization;
+using System.Security.Authentication;
using System.Runtime.ExceptionServices;
using System.Threading;
using System.Threading.Tasks;
@@ -36,8 +37,13 @@ namespace Mono.Net.Security
{
abstract class MobileAuthenticatedStream : AuthenticatedStream, MSI.IMonoSslStream
{
+ /*
+ * This is intentionally called `xobileTlsContext'. It is a "dangerous" object
+ * that must not be touched outside the `ioLock' and we need to be very careful
+ * where we access it.
+ */
MobileTlsContext xobileTlsContext;
- Exception lastException;
+ ExceptionDispatchInfo lastException;
AsyncProtocolRequest asyncHandshakeRequest;
AsyncProtocolRequest asyncReadRequest;
@@ -47,11 +53,12 @@ namespace Mono.Net.Security
object ioLock = new object ();
int closeRequested;
+ bool shutdown;
static int uniqueNameInteger = 123;
public MobileAuthenticatedStream (Stream innerStream, bool leaveInnerStreamOpen, SslStream owner,
- MSI.MonoTlsSettings settings, MSI.MonoTlsProvider provider)
+ MSI.MonoTlsSettings settings, MSI.MonoTlsProvider provider)
: base (innerStream, leaveInnerStreamOpen)
{
SslStream = owner;
@@ -78,42 +85,47 @@ namespace Mono.Net.Security
get { return xobileTlsContext != null; }
}
- internal MobileTlsContext Context {
- get {
- CheckThrow (true);
- return xobileTlsContext;
- }
- }
-
- internal void CheckThrow (bool authSuccessCheck)
+ internal void CheckThrow (bool authSuccessCheck, bool shutdownCheck = false)
{
- if (closeRequested != 0)
- throw new InvalidOperationException ("Stream is closed.");
if (lastException != null)
- throw lastException;
+ lastException.Throw ();
if (authSuccessCheck && !IsAuthenticated)
- throw new InvalidOperationException ("Must be authenticated.");
+ throw new InvalidOperationException (SR.net_auth_noauth);
+ if (shutdownCheck && shutdown)
+ throw new InvalidOperationException (SR.net_ssl_io_already_shutdown);
}
- Exception SetException (Exception e)
+ internal static Exception GetSSPIException (Exception e)
{
- e = SetException_internal (e);
- if (e != null && xobileTlsContext != null)
- xobileTlsContext.Dispose ();
- return e;
+ if (e is OperationCanceledException || e is IOException || e is ObjectDisposedException || e is AuthenticationException)
+ return e;
+ return new AuthenticationException (SR.net_auth_SSPI, e);
}
- Exception SetException_internal (Exception e)
+ internal static Exception GetIOException (Exception e, string message)
{
- if (lastException == null)
- lastException = e;
- return lastException;
+ if (e is OperationCanceledException || e is IOException || e is ObjectDisposedException || e is AuthenticationException)
+ return e;
+ return new IOException (message, e);
+ }
+
+ internal ExceptionDispatchInfo SetException (Exception e)
+ {
+ var info = ExceptionDispatchInfo.Capture (e);
+ var old = Interlocked.CompareExchange (ref lastException, info, null);
+ return old ?? info;
}
SslProtocols DefaultProtocols {
get { return SslProtocols.Tls12 | SslProtocols.Tls11 | SslProtocols.Tls; }
}
+ enum OperationType {
+ Read,
+ Write,
+ Shutdown
+ }
+
public void AuthenticateAsClient (string targetHost)
{
AuthenticateAsClient (targetHost, new X509CertificateCollection (), DefaultProtocols, false);
@@ -121,8 +133,8 @@ namespace Mono.Net.Security
public void AuthenticateAsClient (string targetHost, X509CertificateCollection clientCertificates, SslProtocols enabledSslProtocols, bool checkCertificateRevocation)
{
- ValidateCreateContext (false, targetHost, enabledSslProtocols, null, clientCertificates, false);
- ProcessAuthentication (null);
+ var task = ProcessAuthentication (true, false, targetHost, enabledSslProtocols, null, clientCertificates, false);
+ task.Wait ();
}
public IAsyncResult BeginAuthenticateAsClient (string targetHost, AsyncCallback asyncCallback, object asyncState)
@@ -132,15 +144,13 @@ namespace Mono.Net.Security
public IAsyncResult BeginAuthenticateAsClient (string targetHost, X509CertificateCollection clientCertificates, SslProtocols enabledSslProtocols, bool checkCertificateRevocation, AsyncCallback asyncCallback, object asyncState)
{
- ValidateCreateContext (false, targetHost, enabledSslProtocols, null, clientCertificates, false);
- var result = new LazyAsyncResult (this, asyncState, asyncCallback);
- ProcessAuthentication (result);
- return result;
+ var task = ProcessAuthentication (false, false, targetHost, enabledSslProtocols, null, clientCertificates, false);
+ return TaskToApm.Begin (task, asyncCallback, asyncState);
}
public void EndAuthenticateAsClient (IAsyncResult asyncResult)
{
- EndProcessAuthentication (asyncResult);
+ TaskToApm.End (asyncResult);
}
public void AuthenticateAsServer (X509Certificate serverCertificate)
@@ -150,8 +160,8 @@ namespace Mono.Net.Security
public void AuthenticateAsServer (X509Certificate serverCertificate, bool clientCertificateRequired, SslProtocols enabledSslProtocols, bool checkCertificateRevocation)
{
- ValidateCreateContext (true, string.Empty, enabledSslProtocols, serverCertificate, null, clientCertificateRequired);
- ProcessAuthentication (null);
+ var task = ProcessAuthentication (true, true, string.Empty, enabledSslProtocols, serverCertificate, null, clientCertificateRequired);
+ task.Wait ();
}
public IAsyncResult BeginAuthenticateAsServer (X509Certificate serverCertificate, AsyncCallback asyncCallback, object asyncState)
@@ -161,218 +171,214 @@ namespace Mono.Net.Security
public IAsyncResult BeginAuthenticateAsServer (X509Certificate serverCertificate, bool clientCertificateRequired, SslProtocols enabledSslProtocols, bool checkCertificateRevocation, AsyncCallback asyncCallback, object asyncState)
{
- ValidateCreateContext (true, string.Empty, enabledSslProtocols, serverCertificate, null, clientCertificateRequired);
- var result = new LazyAsyncResult (this, asyncState, asyncCallback);
- ProcessAuthentication (result);
- return result;
+ var task = ProcessAuthentication (false, true, string.Empty, enabledSslProtocols, serverCertificate, null, clientCertificateRequired);
+ return TaskToApm.Begin (task, asyncCallback, asyncState);
}
public void EndAuthenticateAsServer (IAsyncResult asyncResult)
{
- EndProcessAuthentication (asyncResult);
+ TaskToApm.End (asyncResult);
}
public Task AuthenticateAsClientAsync (string targetHost)
{
- return Task.Factory.FromAsync (BeginAuthenticateAsClient, EndAuthenticateAsClient, targetHost, null);
+ return ProcessAuthentication (false, false, targetHost, DefaultProtocols, null, null, false);
}
public Task AuthenticateAsClientAsync (string targetHost, X509CertificateCollection clientCertificates, SslProtocols enabledSslProtocols, bool checkCertificateRevocation)
{
- return Task.Factory.FromAsync ((callback, state) => BeginAuthenticateAsClient (targetHost, clientCertificates, enabledSslProtocols, checkCertificateRevocation, callback, state), EndAuthenticateAsClient, null);
+ return ProcessAuthentication (false, false, targetHost, enabledSslProtocols, null, clientCertificates, false);
}
public Task AuthenticateAsServerAsync (X509Certificate serverCertificate)
{
- return Task.Factory.FromAsync (BeginAuthenticateAsServer, EndAuthenticateAsServer, serverCertificate, null);
+ return AuthenticateAsServerAsync (serverCertificate, false, DefaultProtocols, false);
}
public Task AuthenticateAsServerAsync (X509Certificate serverCertificate, bool clientCertificateRequired, SslProtocols enabledSslProtocols, bool checkCertificateRevocation)
{
- return Task.Factory.FromAsync ((callback, state) => BeginAuthenticateAsServer (serverCertificate, clientCertificateRequired, enabledSslProtocols, checkCertificateRevocation, callback, state), EndAuthenticateAsServer, null);
+ return ProcessAuthentication (false, true, string.Empty, enabledSslProtocols, serverCertificate, null, clientCertificateRequired);
+ }
+
+ public Task ShutdownAsync ()
+ {
+ Debug ("ShutdownAsync");
+
+ /*
+ * SSLClose() is a little bit tricky as it might attempt to send a close_notify alert
+ * and thus call our write callback.
+ *
+ * It is also not thread-safe with SSLRead() or SSLWrite(), so we need to take the I/O lock here.
+ */
+ var asyncRequest = new AsyncShutdownRequest (this);
+ var task = StartOperation (OperationType.Shutdown, asyncRequest, CancellationToken.None);
+ return task;
}
public AuthenticatedStream AuthenticatedStream {
get { return this; }
}
- internal void ProcessAuthentication (LazyAsyncResult lazyResult)
+ async Task ProcessAuthentication (
+ bool runSynchronously, bool serverMode, string targetHost, SslProtocols enabledProtocols,
+ X509Certificate serverCertificate, X509CertificateCollection clientCertificates, bool clientCertRequired)
{
- var asyncRequest = new AsyncProtocolRequest (this, lazyResult);
+ if (serverMode) {
+ if (serverCertificate == null)
+ throw new ArgumentException (nameof (serverCertificate));
+ } else {
+ if (targetHost == null)
+ throw new ArgumentException (nameof (targetHost));
+ if (targetHost.Length == 0)
+ targetHost = "?" + Interlocked.Increment (ref uniqueNameInteger).ToString (NumberFormatInfo.InvariantInfo);
+ }
+
+ if (lastException != null)
+ lastException.Throw ();
+
+ var asyncRequest = new AsyncHandshakeRequest (this, runSynchronously);
if (Interlocked.CompareExchange (ref asyncHandshakeRequest, asyncRequest, null) != null)
throw new InvalidOperationException ("Invalid nested call.");
+ // Make sure no other async requests can be started during the handshake.
+ if (Interlocked.CompareExchange (ref asyncReadRequest, asyncRequest, null) != null)
+ throw new InvalidOperationException ("Invalid nested call.");
+ if (Interlocked.CompareExchange (ref asyncWriteRequest, asyncRequest, null) != null)
+ throw new InvalidOperationException ("Invalid nested call.");
+
+ AsyncProtocolResult result;
try {
- if (lastException != null)
- throw lastException;
- if (xobileTlsContext == null)
- throw new InvalidOperationException ();
+ lock (ioLock) {
+ if (xobileTlsContext != null)
+ throw new InvalidOperationException ();
+ readBuffer.Reset ();
+ writeBuffer.Reset ();
- readBuffer.Reset ();
- writeBuffer.Reset ();
+ xobileTlsContext = CreateContext (
+ serverMode, targetHost, enabledProtocols, serverCertificate,
+ clientCertificates, clientCertRequired);
+ }
try {
- asyncRequest.StartOperation (ProcessHandshake);
+ result = await asyncRequest.StartOperation (CancellationToken.None).ConfigureAwait (false);
} catch (Exception ex) {
- ExceptionDispatchInfo.Capture (SetException (ex)).Throw ();
+ result = new AsyncProtocolResult (SetException (GetSSPIException (ex)));
}
} finally {
- if (lazyResult == null || lastException != null) {
+ lock (ioLock) {
readBuffer.Reset ();
writeBuffer.Reset ();
+ asyncWriteRequest = null;
+ asyncReadRequest = null;
asyncHandshakeRequest = null;
}
}
- }
-
- internal void EndProcessAuthentication (IAsyncResult result)
- {
- if (result == null)
- throw new ArgumentNullException ("asyncResult");
-
- var lazyResult = (LazyAsyncResult)result;
- if (Interlocked.Exchange (ref asyncHandshakeRequest, null) == null)
- throw new InvalidOperationException ("Invalid end call.");
-
- lazyResult.InternalWaitForCompletion ();
-
- readBuffer.Reset ();
- writeBuffer.Reset ();
-
- var e = lazyResult.Result as Exception;
- if (e != null)
- ExceptionDispatchInfo.Capture (SetException (e)).Throw ();
- }
-
- internal void ValidateCreateContext (bool serverMode, string targetHost, SslProtocols enabledProtocols, X509Certificate serverCertificate, X509CertificateCollection clientCertificates, bool clientCertRequired)
- {
- if (xobileTlsContext != null)
- throw new InvalidOperationException ();
-
- if (serverMode) {
- if (serverCertificate == null)
- throw new ArgumentException ("serverCertificate");
- } else {
- if (targetHost == null)
- throw new ArgumentException ("targetHost");
- if (targetHost.Length == 0)
- targetHost = "?" + Interlocked.Increment (ref uniqueNameInteger).ToString (NumberFormatInfo.InvariantInfo);
- }
- xobileTlsContext = CreateContext (this, serverMode, targetHost, enabledProtocols, serverCertificate, clientCertificates, clientCertRequired);
+ if (result.Error != null)
+ result.Error.Throw ();
}
protected abstract MobileTlsContext CreateContext (
- MobileAuthenticatedStream parent, bool serverMode, string targetHost,
- SSA.SslProtocols enabledProtocols, X509Certificate serverCertificate,
- X509CertificateCollection clientCertificates, bool askForClientCert);
+ bool serverMode, string targetHost, SSA.SslProtocols enabledProtocols,
+ X509Certificate serverCertificate, X509CertificateCollection clientCertificates,
+ bool askForClientCert);
public override IAsyncResult BeginRead (byte[] buffer, int offset, int count, AsyncCallback asyncCallback, object asyncState)
{
- return BeginReadOrWrite (ref asyncReadRequest, ref readBuffer, ProcessRead, new BufferOffsetSize (buffer, offset, count), asyncCallback, asyncState);
+ var asyncRequest = new AsyncReadRequest (this, false, buffer, offset, count);
+ var task = StartOperation (OperationType.Read, asyncRequest, CancellationToken.None);
+ return TaskToApm.Begin (task, asyncCallback, asyncState);
}
public override int EndRead (IAsyncResult asyncResult)
{
- return (int)EndReadOrWrite (asyncResult, ref asyncReadRequest);
+ return TaskToApm.End<int> (asyncResult);
}
public override IAsyncResult BeginWrite (byte[] buffer, int offset, int count, AsyncCallback asyncCallback, object asyncState)
{
- return BeginReadOrWrite (ref asyncWriteRequest, ref writeBuffer, ProcessWrite, new BufferOffsetSize (buffer, offset, count), asyncCallback, asyncState);
+ var asyncRequest = new AsyncWriteRequest (this, false, buffer, offset, count);
+ var task = StartOperation (OperationType.Write, asyncRequest, CancellationToken.None);
+ return TaskToApm.Begin (task, asyncCallback, asyncState);
}
public override void EndWrite (IAsyncResult asyncResult)
{
- EndReadOrWrite (asyncResult, ref asyncWriteRequest);
+ TaskToApm.End (asyncResult);
}
public override int Read (byte[] buffer, int offset, int count)
{
- return ProcessReadOrWrite (ref asyncReadRequest, ref readBuffer, ProcessRead, new BufferOffsetSize (buffer, offset, count), null);
+ var asyncRequest = new AsyncReadRequest (this, true, buffer, offset, count);
+ var task = StartOperation (OperationType.Read, asyncRequest, CancellationToken.None);
+ return task.Result;
}
public void Write (byte[] buffer)
{
Write (buffer, 0, buffer.Length);
}
+
public override void Write (byte[] buffer, int offset, int count)
{
- ProcessReadOrWrite (ref asyncWriteRequest, ref writeBuffer, ProcessWrite, new BufferOffsetSize (buffer, offset, count), null);
+ var asyncRequest = new AsyncWriteRequest (this, true, buffer, offset, count);
+ var task = StartOperation (OperationType.Write, asyncRequest, CancellationToken.None);
+ task.Wait ();
}
- IAsyncResult BeginReadOrWrite (ref AsyncProtocolRequest nestedRequest, ref BufferOffsetSize2 internalBuffer, AsyncOperation operation, BufferOffsetSize userBuffer, AsyncCallback asyncCallback, object asyncState)
+ public override Task<int> ReadAsync (byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
- LazyAsyncResult lazyResult = new LazyAsyncResult (this, asyncState, asyncCallback);
- ProcessReadOrWrite (ref nestedRequest, ref internalBuffer, operation, userBuffer, lazyResult);
- return lazyResult;
+ var asyncRequest = new AsyncReadRequest (this, false, buffer, offset, count);
+ return StartOperation (OperationType.Read, asyncRequest, cancellationToken);
}
- object EndReadOrWrite (IAsyncResult asyncResult, ref AsyncProtocolRequest nestedRequest)
+ public override Task WriteAsync (byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
- if (asyncResult == null)
- throw new ArgumentNullException("asyncResult");
-
- var lazyResult = (LazyAsyncResult)asyncResult;
-
- if (Interlocked.Exchange (ref nestedRequest, null) == null)
- throw new InvalidOperationException ("Invalid end call.");
-
- // No "artificial" timeouts implemented so far, InnerStream controls timeout.
- lazyResult.InternalWaitForCompletion ();
-
- Debug ("EndReadOrWrite");
-
- var e = lazyResult.Result as Exception;
- if (e != null) {
- var ioEx = e as IOException;
- if (ioEx != null)
- throw ioEx;
- throw new IOException ("read failed", e);
- }
-
- return lazyResult.Result;
+ var asyncRequest = new AsyncWriteRequest (this, false, buffer, offset, count);
+ return StartOperation (OperationType.Write, asyncRequest, cancellationToken);
}
- int ProcessReadOrWrite (ref AsyncProtocolRequest nestedRequest, ref BufferOffsetSize2 internalBuffer, AsyncOperation operation, BufferOffsetSize userBuffer, LazyAsyncResult lazyResult)
+ async Task<int> StartOperation (OperationType type, AsyncProtocolRequest asyncRequest, CancellationToken cancellationToken)
{
- if (userBuffer == null || userBuffer.Buffer == null)
- throw new ArgumentNullException ("buffer");
- if (userBuffer.Offset < 0)
- throw new ArgumentOutOfRangeException ("offset");
- if (userBuffer.Size < 0 || userBuffer.Offset + userBuffer.Size > userBuffer.Buffer.Length)
- throw new ArgumentOutOfRangeException ("count");
-
- CheckThrow (true);
+ CheckThrow (true, type != OperationType.Read);
+ Debug ("StartOperationAsync: {0} {1}", asyncRequest, type);
- var name = internalBuffer == readBuffer ? "read" : "write";
- Debug ("ProcessReadOrWrite: {0} {1}", name, userBuffer);
+ if (type == OperationType.Read) {
+ if (Interlocked.CompareExchange (ref asyncReadRequest, asyncRequest, null) != null)
+ throw new InvalidOperationException ("Invalid nested call.");
+ } else {
+ if (Interlocked.CompareExchange (ref asyncWriteRequest, asyncRequest, null) != null)
+ throw new InvalidOperationException ("Invalid nested call.");
+ }
- var asyncRequest = new AsyncProtocolRequest (this, lazyResult, userBuffer);
- return StartOperation (ref nestedRequest, ref internalBuffer, operation, asyncRequest, name);
- }
+ AsyncProtocolResult result;
- int StartOperation (ref AsyncProtocolRequest nestedRequest, ref BufferOffsetSize2 internalBuffer, AsyncOperation operation, AsyncProtocolRequest asyncRequest, string name)
- {
- if (Interlocked.CompareExchange (ref nestedRequest, asyncRequest, null) != null)
- throw new InvalidOperationException ("Invalid nested call.");
-
- bool failed = false;
try {
- internalBuffer.Reset ();
- asyncRequest.StartOperation (operation);
- return asyncRequest.UserResult;
+ lock (ioLock) {
+ if (type == OperationType.Read)
+ readBuffer.Reset ();
+ else
+ writeBuffer.Reset ();
+ }
+ result = await asyncRequest.StartOperation (cancellationToken).ConfigureAwait (false);
} catch (Exception e) {
- failed = true;
- if (e is IOException)
- throw;
- throw new IOException (name + " failed", e);
+ var info = SetException (GetIOException (e, asyncRequest.Name + " failed"));
+ result = new AsyncProtocolResult (info);
} finally {
- if (asyncRequest.UserAsyncResult == null || failed) {
- internalBuffer.Reset ();
- nestedRequest = null;
+ lock (ioLock) {
+ if (type == OperationType.Read) {
+ readBuffer.Reset ();
+ asyncReadRequest = null;
+ } else {
+ writeBuffer.Reset ();
+ asyncWriteRequest = null;
+ }
}
}
+
+ if (result.Error != null)
+ result.Error.Throw ();
+ return result.UserResult;
}
static int nextId;
@@ -384,26 +390,31 @@ namespace Mono.Net.Security
Console.Error.WriteLine ("MobileAuthenticatedStream({0}): {1}", ID, string.Format (message, args));
}
- #region Called back from native code via SslConnection
+#region Called back from native code via SslConnection
/*
* Called from within SSLRead() and SSLHandshake(). We only access tha managed byte[] here.
*/
- internal int InternalRead (byte[] buffer, int offset, int size, out bool wantMore)
+ internal int InternalRead (byte[] buffer, int offset, int size, out bool outWantMore)
{
try {
- Debug ("InternalRead: {0} {1} {2} {3}", offset, size, asyncReadRequest != null, readBuffer != null);
+ Debug ("InternalRead: {0} {1} {2} {3} {4}", offset, size,
+ asyncHandshakeRequest != null ? "handshake" : "",
+ asyncReadRequest != null ? "async" : "",
+ readBuffer != null ? readBuffer.ToString () : "");
var asyncRequest = asyncHandshakeRequest ?? asyncReadRequest;
- return InternalRead (asyncRequest, readBuffer, buffer, offset, size, out wantMore);
+ var (ret, wantMore) = InternalRead (asyncRequest, readBuffer, buffer, offset, size);
+ outWantMore = wantMore;
+ return ret;
} catch (Exception ex) {
Debug ("InternalRead failed: {0}", ex);
- SetException_internal (ex);
- wantMore = false;
+ SetException (GetIOException (ex, "InternalRead() failed"));
+ outWantMore = false;
return -1;
}
}
- int InternalRead (AsyncProtocolRequest asyncRequest, BufferOffsetSize internalBuffer, byte[] buffer, int offset, int size, out bool wantMore)
+ (int, bool) InternalRead (AsyncProtocolRequest asyncRequest, BufferOffsetSize internalBuffer, byte[] buffer, int offset, int size)
{
if (asyncRequest == null)
throw new InvalidOperationException ();
@@ -422,11 +433,10 @@ namespace Mono.Net.Security
* native function again.
*/
if (internalBuffer.Size == 0 && !internalBuffer.Complete) {
- Debug ("InternalRead #1: {0} {1}", internalBuffer.Offset, internalBuffer.TotalBytes);
+ Debug ("InternalRead #1: {0} {1} {2}", internalBuffer.Offset, internalBuffer.TotalBytes, size);
internalBuffer.Offset = internalBuffer.Size = 0;
asyncRequest.RequestRead (size);
- wantMore = true;
- return 0;
+ return (0, true);
}
/*
@@ -441,8 +451,7 @@ namespace Mono.Net.Security
Buffer.BlockCopy (internalBuffer.Buffer, internalBuffer.Offset, buffer, offset, len);
internalBuffer.Offset += len;
internalBuffer.Size -= len;
- wantMore = !internalBuffer.Complete && len < size;
- return len;
+ return (len, !internalBuffer.Complete && len < size);
}
/*
@@ -456,7 +465,7 @@ namespace Mono.Net.Security
return InternalWrite (asyncRequest, writeBuffer, buffer, offset, size);
} catch (Exception ex) {
Debug ("InternalWrite failed: {0}", ex);
- SetException_internal (ex);
+ SetException (GetIOException (ex, "InternalWrite() failed"));
return false;
}
}
@@ -511,22 +520,30 @@ namespace Mono.Net.Security
return true;
}
- #endregion
+#endregion
- #region Inner Stream
+#region Inner Stream
/*
* Read / write data from the inner stream; we're only called from managed code and only manipulate
* the internal buffers.
*/
- internal int InnerRead (int requestedSize)
+ internal async Task<int> InnerRead (bool sync, int requestedSize, CancellationToken cancellationToken)
{
+ cancellationToken.ThrowIfCancellationRequested ();
Debug ("InnerRead: {0} {1} {2} {3}", readBuffer.Offset, readBuffer.Size, readBuffer.Remaining, requestedSize);
var len = System.Math.Min (readBuffer.Remaining, requestedSize);
if (len == 0)
throw new InvalidOperationException ();
- var ret = InnerStream.Read (readBuffer.Buffer, readBuffer.EndOffset, len);
+
+ Task<int> task;
+ if (sync)
+ task = Task.Run (() => InnerStream.Read (readBuffer.Buffer, readBuffer.EndOffset, len));
+ else
+ task = InnerStream.ReadAsync (readBuffer.Buffer, readBuffer.EndOffset, len, cancellationToken);
+
+ var ret = await task.ConfigureAwait (false);
Debug ("InnerRead done: {0} {1} - {2}", readBuffer.Remaining, len, ret);
if (ret >= 0) {
@@ -549,165 +566,126 @@ namespace Mono.Net.Security
return ret;
}
- internal void InnerWrite ()
+ internal async Task InnerWrite (bool sync, CancellationToken cancellationToken)
{
+ cancellationToken.ThrowIfCancellationRequested ();
Debug ("InnerWrite: {0} {1}", writeBuffer.Offset, writeBuffer.Size);
- InnerFlush ();
- }
- internal void InnerFlush ()
- {
- if (writeBuffer.Size > 0) {
- InnerStream.Write (writeBuffer.Buffer, writeBuffer.Offset, writeBuffer.Size);
- writeBuffer.TotalBytes += writeBuffer.Size;
- writeBuffer.Offset = writeBuffer.Size = 0;
- }
+ if (writeBuffer.Size == 0)
+ return;
+
+ Task task;
+ if (sync)
+ task = Task.Run (() => InnerStream.Write (writeBuffer.Buffer, writeBuffer.Offset, writeBuffer.Size));
+ else
+ task = InnerStream.WriteAsync (writeBuffer.Buffer, writeBuffer.Offset, writeBuffer.Size);
+
+ await task.ConfigureAwait (false);
+
+ writeBuffer.TotalBytes += writeBuffer.Size;
+ writeBuffer.Offset = writeBuffer.Size = 0;
}
- #endregion
+#endregion
- #region Main async I/O loop
+#region Main async I/O loop
- AsyncOperationStatus ProcessHandshake (AsyncProtocolRequest asyncRequest, AsyncOperationStatus status)
+ internal AsyncOperationStatus ProcessHandshake (AsyncOperationStatus status)
{
Debug ("ProcessHandshake: {0}", status);
- /*
- * The first time we're called (AsyncOperationStatus.Initialize), we need to setup the SslContext and
- * start the handshake.
- */
- if (status == AsyncOperationStatus.Initialize) {
- xobileTlsContext.StartHandshake ();
- return AsyncOperationStatus.Continue;
- } else if (status == AsyncOperationStatus.ReadDone) {
- // remote prematurely closed connection.
- throw new IOException ("Remote prematurely closed connection.");
- } else if (status != AsyncOperationStatus.Continue) {
- throw new InvalidOperationException ();
- }
+ lock (ioLock) {
+ /*
+ * The first time we're called (AsyncOperationStatus.Initialize), we need to setup the SslContext and
+ * start the handshake.
+ */
+ if (status == AsyncOperationStatus.Initialize) {
+ xobileTlsContext.StartHandshake ();
+ return AsyncOperationStatus.Continue;
+ } else if (status == AsyncOperationStatus.ReadDone) {
+ throw new IOException (SR.net_auth_eof);
+ } else if (status != AsyncOperationStatus.Continue) {
+ throw new InvalidOperationException ();
+ }
- /*
- * SSLHandshake() will return repeatedly with 'SslStatus.WouldBlock', we then need
- * to take care of I/O and call it again.
- */
- if (!xobileTlsContext.ProcessHandshake ()) {
/*
- * Flush the internal write buffer.
- */
- InnerFlush ();
+ * SSLHandshake() will return repeatedly with 'SslStatus.WouldBlock', we then need
+ * to take care of I/O and call it again.
+ */
+ if (xobileTlsContext.ProcessHandshake ()) {
+ xobileTlsContext.FinishHandshake ();
+ return AsyncOperationStatus.Complete;
+ }
return AsyncOperationStatus.Continue;
}
-
- xobileTlsContext.FinishHandshake ();
- return AsyncOperationStatus.Complete;
}
- AsyncOperationStatus ProcessRead (AsyncProtocolRequest asyncRequest, AsyncOperationStatus status)
+ internal (int, bool) ProcessRead (BufferOffsetSize userBuffer)
{
- Debug ("ProcessRead - read user: {0} {1}", status, asyncRequest.UserBuffer);
-
- int ret;
- bool wantMore;
lock (ioLock) {
- ret = Context.Read (asyncRequest.UserBuffer.Buffer, asyncRequest.UserBuffer.Offset, asyncRequest.UserBuffer.Size, out wantMore);
+ // This operates on the internal buffer and will never block.
+ var ret = xobileTlsContext.Read (userBuffer.Buffer, userBuffer.Offset, userBuffer.Size, out bool wantMore);
+ return (ret, wantMore);
}
- Debug ("ProcessRead - read user done: {0} - {1} {2}", asyncRequest.UserBuffer, ret, wantMore);
-
- if (ret < 0) {
- asyncRequest.UserResult = -1;
- return AsyncOperationStatus.Complete;
- }
-
- asyncRequest.CurrentSize += ret;
- asyncRequest.UserBuffer.Offset += ret;
- asyncRequest.UserBuffer.Size -= ret;
-
- Debug ("Process Read - read user done #1: {0} - {1} {2}", asyncRequest.UserBuffer, asyncRequest.CurrentSize, wantMore);
-
- if (wantMore && asyncRequest.CurrentSize == 0)
- return AsyncOperationStatus.WantRead;
-
- asyncRequest.ResetRead ();
- asyncRequest.UserResult = asyncRequest.CurrentSize;
- return AsyncOperationStatus.Complete;
}
- AsyncOperationStatus ProcessWrite (AsyncProtocolRequest asyncRequest, AsyncOperationStatus status)
+ internal (int, bool) ProcessWrite (BufferOffsetSize userBuffer)
{
- Debug ("ProcessWrite - write user: {0} {1}", status, asyncRequest.UserBuffer);
-
- if (asyncRequest.UserBuffer.Size == 0) {
- asyncRequest.UserResult = asyncRequest.CurrentSize;
- return AsyncOperationStatus.Complete;
- }
-
- int ret;
- bool wantMore;
lock (ioLock) {
- ret = Context.Write (asyncRequest.UserBuffer.Buffer, asyncRequest.UserBuffer.Offset, asyncRequest.UserBuffer.Size, out wantMore);
- }
- Debug ("ProcessWrite - write user done: {0} - {1} {2}", asyncRequest.UserBuffer, ret, wantMore);
-
- if (ret < 0) {
- asyncRequest.UserResult = -1;
- return AsyncOperationStatus.Complete;
+ // This operates on the internal buffer and will never block.
+ var ret = xobileTlsContext.Write (userBuffer.Buffer, userBuffer.Offset, userBuffer.Size, out bool wantMore);
+ return (ret, wantMore);
}
-
- asyncRequest.CurrentSize += ret;
- asyncRequest.UserBuffer.Offset += ret;
- asyncRequest.UserBuffer.Size -= ret;
-
- if (wantMore || writeBuffer.Size > 0)
- return AsyncOperationStatus.WantWrite;
-
- asyncRequest.ResetWrite ();
- asyncRequest.UserResult = asyncRequest.CurrentSize;
- return AsyncOperationStatus.Complete;
}
- AsyncOperationStatus ProcessClose (AsyncProtocolRequest asyncRequest, AsyncOperationStatus status)
+ internal AsyncOperationStatus ProcessShutdown (AsyncOperationStatus status)
{
- Debug ("ProcessClose: {0}", status);
+ Debug ("ProcessShutdown: {0}", status);
lock (ioLock) {
- if (xobileTlsContext == null)
- return AsyncOperationStatus.Complete;
-
- xobileTlsContext.Close ();
- xobileTlsContext = null;
- return AsyncOperationStatus.Continue;
+ xobileTlsContext.Shutdown ();
+ shutdown = true;
+ return AsyncOperationStatus.Complete;
}
}
- AsyncOperationStatus ProcessFlush (AsyncProtocolRequest asyncRequest, AsyncOperationStatus status)
- {
- Debug ("ProcessFlush: {0}", status);
- return AsyncOperationStatus.Complete;
- }
-
- #endregion
+#endregion
public override bool IsServer {
- get { return xobileTlsContext != null && xobileTlsContext.IsServer; }
+ get {
+ CheckThrow (false);
+ return xobileTlsContext != null && xobileTlsContext.IsServer;
+ }
}
public override bool IsAuthenticated {
- get { return xobileTlsContext != null && lastException == null && xobileTlsContext.IsAuthenticated; }
+ get {
+ lock (ioLock) {
+ // Don't use CheckThrow(), we want to return false if we're not authenticated.
+ return xobileTlsContext != null && lastException == null && xobileTlsContext.IsAuthenticated;
+ }
+ }
}
public override bool IsMutuallyAuthenticated {
get {
- return IsAuthenticated &&
- (Context.IsServer? Context.LocalServerCertificate: Context.LocalClientCertificate) != null &&
- Context.IsRemoteCertificateAvailable;
+ lock (ioLock) {
+ // Don't use CheckThrow() here.
+ if (!IsAuthenticated)
+ return false;
+ if ((xobileTlsContext.IsServer ? xobileTlsContext.LocalServerCertificate : xobileTlsContext.LocalClientCertificate) == null)
+ return false;
+ return xobileTlsContext.IsRemoteCertificateAvailable;
+ }
}
}
protected override void Dispose (bool disposing)
{
try {
- lastException = new ObjectDisposedException ("MobileAuthenticatedStream");
lock (ioLock) {
+ Debug ("Dispose: {0}", xobileTlsContext != null);
+ lastException = ExceptionDispatchInfo.Capture (new ObjectDisposedException ("MobileAuthenticatedStream"));
if (xobileTlsContext != null) {
xobileTlsContext.Dispose ();
xobileTlsContext = null;
@@ -720,26 +698,53 @@ namespace Mono.Net.Security
public override void Flush ()
{
- CheckThrow (true);
- var asyncRequest = new AsyncProtocolRequest (this, null);
- StartOperation (ref asyncWriteRequest, ref writeBuffer, ProcessFlush, asyncRequest, "flush");
+ // Write() automatically flushes the underlying stream.
}
- public override void Close ()
- {
- /*
- * SSLClose() is a little bit tricky as it might attempt to send a close_notify alert
- * and thus call our write callback.
- *
- * It is also not thread-safe with SSLRead() or SSLWrite(), so we need to take the I/O lock here.
- */
- if (Interlocked.Exchange (ref closeRequested, 1) == 1)
- return;
- if (xobileTlsContext == null)
- return;
+ public SslProtocols SslProtocol {
+ get {
+ lock (ioLock) {
+ CheckThrow (true);
+ return (SslProtocols)xobileTlsContext.NegotiatedProtocol;
+ }
+ }
+ }
+
+ public X509Certificate RemoteCertificate {
+ get {
+ lock (ioLock) {
+ CheckThrow (true);
+ return xobileTlsContext.RemoteCertificate;
+ }
+ }
+ }
+
+ public X509Certificate LocalCertificate {
+ get {
+ lock (ioLock) {
+ CheckThrow (true);
+ return InternalLocalCertificate;
+ }
+ }
+ }
+
+ public X509Certificate InternalLocalCertificate {
+ get {
+ lock (ioLock) {
+ CheckThrow (false);
+ if (xobileTlsContext == null)
+ return null;
+ return xobileTlsContext.IsServer ? xobileTlsContext.LocalServerCertificate : xobileTlsContext.LocalClientCertificate;
+ }
+ }
+ }
- var asyncRequest = new AsyncProtocolRequest (this, null);
- StartOperation (ref asyncWriteRequest, ref writeBuffer, ProcessClose, asyncRequest, "close");
+ public MSI.MonoTlsConnectionInfo GetConnectionInfo ()
+ {
+ lock (ioLock) {
+ CheckThrow (true);
+ return xobileTlsContext.ConnectionInfo;
+ }
}
//
@@ -769,7 +774,7 @@ namespace Mono.Net.Security
}
public override bool CanWrite {
- get { return IsAuthenticated & InnerStream.CanWrite; }
+ get { return IsAuthenticated & InnerStream.CanWrite && !shutdown; }
}
public override bool CanSeek {
@@ -803,46 +808,10 @@ namespace Mono.Net.Security
set { InnerStream.WriteTimeout = value; }
}
- public SslProtocols SslProtocol {
- get {
- CheckThrow (true);
- return (SslProtocols)Context.NegotiatedProtocol;
- }
- }
-
- public X509Certificate RemoteCertificate {
- get {
- CheckThrow (true);
- return Context.RemoteCertificate;
- }
- }
-
- public X509Certificate LocalCertificate {
- get {
- CheckThrow (true);
- return InternalLocalCertificate;
- }
- }
-
- public X509Certificate InternalLocalCertificate {
- get {
- CheckThrow (false);
- if (!HasContext)
- return null;
- return Context.IsServer ? Context.LocalServerCertificate : Context.LocalClientCertificate;
- }
- }
-
- public MSI.MonoTlsConnectionInfo GetConnectionInfo ()
- {
- CheckThrow (true);
- return Context.ConnectionInfo;
- }
-
public SSA.CipherAlgorithmType CipherAlgorithm {
get {
CheckThrow (true);
- var info = Context.ConnectionInfo;
+ var info = GetConnectionInfo ();
if (info == null)
return SSA.CipherAlgorithmType.None;
switch (info.CipherAlgorithmType) {
@@ -861,7 +830,7 @@ namespace Mono.Net.Security
public SSA.HashAlgorithmType HashAlgorithm {
get {
CheckThrow (true);
- var info = Context.ConnectionInfo;
+ var info = GetConnectionInfo ();
if (info == null)
return SSA.HashAlgorithmType.None;
switch (info.HashAlgorithmType) {
@@ -883,7 +852,7 @@ namespace Mono.Net.Security
public SSA.ExchangeAlgorithmType KeyExchangeAlgorithm {
get {
CheckThrow (true);
- var info = Context.ConnectionInfo;
+ var info = GetConnectionInfo ();
if (info == null)
return SSA.ExchangeAlgorithmType.None;
switch (info.ExchangeAlgorithmType) {
@@ -898,7 +867,7 @@ namespace Mono.Net.Security
}
}
- #region Need to Implement
+#region Need to Implement
public int CipherStrength {
get {
throw new NotImplementedException ();
@@ -920,7 +889,7 @@ namespace Mono.Net.Security
}
}
- #endregion
+#endregion
}
}
#endif
diff --git a/mcs/class/System/Mono.Net.Security/MobileTlsContext.cs b/mcs/class/System/Mono.Net.Security/MobileTlsContext.cs
index 79f0468d461..b4ba1c014ca 100644
--- a/mcs/class/System/Mono.Net.Security/MobileTlsContext.cs
+++ b/mcs/class/System/Mono.Net.Security/MobileTlsContext.cs
@@ -169,7 +169,7 @@ namespace Mono.Net.Security
public abstract int Write (byte[] buffer, int offset, int count, out bool wantMore);
- public abstract void Close ();
+ public abstract void Shutdown ();
protected bool ValidateCertificate (X509Certificate leaf, X509Chain chain)
{
diff --git a/mcs/class/System/ReferenceSources/SR2.cs b/mcs/class/System/ReferenceSources/SR2.cs
index cfcd038e9b5..896362d98a4 100644
--- a/mcs/class/System/ReferenceSources/SR2.cs
+++ b/mcs/class/System/ReferenceSources/SR2.cs
@@ -9,6 +9,8 @@ partial class SR
{
public const string mono_net_io_shutdown = "mono_net_io_shutdown";
public const string mono_net_io_renegotiate = "mono_net_io_renegotiate";
+
+ public const string net_ssl_io_already_shutdown = "Write operations are not allowed after the channel was shutdown.";
public const string net_log_set_socketoption_reuseport_default_on = "net_log_set_socketoption_reuseport_default_on";
public const string net_log_set_socketoption_reuseport_not_supported = "net_log_set_socketoption_reuseport_not_supported";
diff --git a/mcs/class/System/System.Net.Security/SslStream.cs b/mcs/class/System/System.Net.Security/SslStream.cs
index 6d61e08dfb1..c0b702fde1b 100644
--- a/mcs/class/System/System.Net.Security/SslStream.cs
+++ b/mcs/class/System/System.Net.Security/SslStream.cs
@@ -223,6 +223,11 @@ namespace System.Net.Security
return Impl.AuthenticateAsServerAsync (serverCertificate, clientCertificateRequired, enabledSslProtocols, checkCertificateRevocation);
}
+ public virtual Task ShutdownAsync ()
+ {
+ return Impl.ShutdownAsync ();
+ }
+
public override bool IsAuthenticated {
get { return Impl.IsAuthenticated; }
}
diff --git a/mcs/class/System/System.Net.Security/SslStream.platformnotsupported.cs b/mcs/class/System/System.Net.Security/SslStream.platformnotsupported.cs
index 06fefad105a..1c2a4167301 100644
--- a/mcs/class/System/System.Net.Security/SslStream.platformnotsupported.cs
+++ b/mcs/class/System/System.Net.Security/SslStream.platformnotsupported.cs
@@ -152,6 +152,11 @@ namespace System.Net.Security
throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
}
+ public virtual Task ShutdownAsync ()
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
public override bool IsAuthenticated {
get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
}
diff --git a/mcs/class/System/common.sources b/mcs/class/System/common.sources
index f20e501c687..a38c2ff9c48 100644
--- a/mcs/class/System/common.sources
+++ b/mcs/class/System/common.sources
@@ -276,6 +276,7 @@ ReferenceSources/SettingsSectionInternal.cs
ReferenceSources/SecureStringHelper.cs
ReferenceSources/Socket.cs
ReferenceSources/SR.cs
+ReferenceSources/SR2.cs
ReferenceSources/SRCategoryAttribute.cs
ReferenceSources/Win32Exception.cs
diff --git a/mcs/class/System/net_4_x_System.dll.sources b/mcs/class/System/net_4_x_System.dll.sources
index 46b53b43052..110199b4bcd 100644
--- a/mcs/class/System/net_4_x_System.dll.sources
+++ b/mcs/class/System/net_4_x_System.dll.sources
@@ -245,7 +245,6 @@ Mono.Net.Dns/ResolverError.cs
Mono.Net.Dns/SimpleResolverEventArgs.cs
ReferenceSources/BinaryCompatibility.cs
ReferenceSources/ConfigurationManagerInternalFactory.cs
-ReferenceSources/SR2.cs
../referencesource/System/misc/PrivilegedConfigurationManager.cs