[System]: Correctly implement close and shutdown in SslStream. (#4969)
authorMartin Baulig <mabaul@microsoft.com>
Tue, 6 Jun 2017 15:46:29 +0000 (11:46 -0400)
committerGitHub <noreply@github.com>
Tue, 6 Jun 2017 15:46:29 +0000 (11:46 -0400)
* [System]: Correctly implement close and shutdown in SslStream.

* Cleanup.

* Cosmetic.

* More ReferenceSources/SR2.cs into common.sources to make it build.

19 files changed:
mcs/class/Mono.Security/Mono.Security.Interface/IMonoSslStream.cs
mcs/class/System/Mono.AppleTls/AppleTlsContext.cs
mcs/class/System/Mono.AppleTls/AppleTlsStream.cs
mcs/class/System/Mono.Btls/MonoBtlsContext.cs
mcs/class/System/Mono.Btls/MonoBtlsObject.cs
mcs/class/System/Mono.Btls/MonoBtlsSsl.cs
mcs/class/System/Mono.Btls/MonoBtlsStream.cs
mcs/class/System/Mono.Net.Security/AsyncProtocolRequest.cs
mcs/class/System/Mono.Net.Security/LegacySslStream.cs
mcs/class/System/Mono.Net.Security/MobileAuthenticatedStream.cs
mcs/class/System/Mono.Net.Security/MobileTlsContext.cs
mcs/class/System/ReferenceSources/SR2.cs
mcs/class/System/System.Net.Security/SslStream.cs
mcs/class/System/System.Net.Security/SslStream.platformnotsupported.cs
mcs/class/System/common.sources
mcs/class/System/net_4_x_System.dll.sources
mono/btls/btls-bio.c
mono/btls/btls-ssl.c
mono/btls/btls-ssl.h

index 4982e8044770054d00257db4ad577db6263b326a..00556863db9c5c4397e41d6ee370cc827979d4f8 100644 (file)
@@ -86,6 +86,8 @@ namespace Mono.Security.Interface
 
                void EndWrite (IAsyncResult asyncResult);
 
+               Task ShutdownAsync ();
+
                TransportContext TransportContext {
                        get;
                }
index 6a57babab4e274b994407f3426bc31c2c4796fbb..4fa1730df2a10ffc33fc7d9a5a792259737b8db6 100644 (file)
@@ -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;
index e3b9fa85933912d704580bbb9374cf43d822c188..412b4da1f105b7d9f158bdb7e52c1fa490b34f32 100644 (file)
@@ -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);
                }
index 841daa270fa12ac1cd20b9f959a73c47c14f1dbb..08c2678a2276f6554da8f4b50d6127550b7b84bc 100644 (file)
@@ -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);
                                }
index 4264411bd90fea0fce98dc86ea7d681c02d7b992..19e72cbda640bea62ad6a05aac647e17fc6432b0 100644 (file)
@@ -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);
 
index 09e171485f6ca659199d70ab9f0f07a4f8d48bd3..e5fac698c0e18c35c32040e2d4d2679a4ed6c130 100644 (file)
@@ -47,6 +47,7 @@ namespace Mono.Btls
                        protected override bool ReleaseHandle ()
                        {
                                mono_btls_ssl_destroy (handle);
+                               handle = IntPtr.Zero;
                                return true;
                        }
                }
@@ -78,6 +79,12 @@ namespace Mono.Btls
                [DllImport (BTLS_DYLIB)]
                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);
 
@@ -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 ());
                }
        }
 }
index e941fcd1c0c132674b6fa6d2f61871b4bfadf9bb..b3bb65f9b8eb8023265be0ce8bdf5a47d1301bc4 100644 (file)
@@ -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);
                }
index 5f90ffe2b82241f4390b9c112c0c36c0e0abaeb9..3c537a7e42798b46ee80b02477db30759666c7fa 100644 (file)
@@ -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
index a5572addc13c4583642104a84fd321feadbd07d9..e4032496da167517db3ff54076a60e184a6ba167 100644 (file)
@@ -575,6 +575,11 @@ namespace Mono.Net.Security.Private
 
                #region IMonoSslStream
 
+               Task IMonoSslStream.ShutdownAsync ()
+               {
+                       return Task.CompletedTask;
+               }
+
                AuthenticatedStream IMonoSslStream.AuthenticatedStream {
                        get { return this; }
                }
index 1e57c67341597cd191bf27b21a245ee276972b7d..2b380a1ae6c2ca3f9d5ffb5f94d4cf2de72cac19 100644 (file)
@@ -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
index 79f0468d4619aee415b238d9171366fe801abb8e..b4ba1c014cabf7cb5e47d7410d46d19e5aa10106 100644 (file)
@@ -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)
                {
index cfcd038e9b54b2ee876d4b8b5d5010f9275ecacd..896362d98a4b14de35203b4f70c0a0f812c7de14 100644 (file)
@@ -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";
index 6d61e08dfb1bf659161ca1869175ffa3e70cdb16..c0b702fde1b2f92798271fdf943402e1460eb27a 100644 (file)
@@ -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; }
                }
index 06fefad105ac81e87e83fa25a76f8b3dc74e8343..1c2a4167301b6df077b7ef1467aa4fd626571d22 100644 (file)
@@ -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); }
                }
index f20e501c6878d4a4eb5487887e2de1265b12d68b..a38c2ff9c486fded93b9ba91633e543ae750671d 100644 (file)
@@ -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
 
index 46b53b430521e615b5766bb2d9fd720efbd9d4ed..110199b4bcd9869ae5ed238045261358ed207c23 100644 (file)
@@ -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
 
index ea8fda67dc35096689e0e580561e7e4b68b38577..016175bea3b4c56f370f7ff512f47093c7e0e70b 100644 (file)
@@ -39,8 +39,10 @@ mono_read (BIO *bio, char *out, int outl)
 
        ret = mono->read_func (mono->instance, out, outl, &wantMore);
 
-       if (ret < 0)
+       if (ret < 0) {
+               errno = EIO;
                return -1;
+       }
        if (ret > 0)
                return ret;
 
index eca323ce2ae27a194018f0180f89131d3a34443d..17291b5c585e18cd0f11e2c2b4f8bb8c01766776 100644 (file)
@@ -60,6 +60,18 @@ mono_btls_ssl_close (MonoBtlsSsl *ptr)
        ;
 }
 
+MONO_API int
+mono_btls_ssl_shutdown (MonoBtlsSsl *ptr)
+{
+    return SSL_shutdown (ptr->ssl);
+}
+
+MONO_API void
+mono_btls_ssl_set_quiet_shutdown (MonoBtlsSsl *ptr, int mode)
+{
+    SSL_set_quiet_shutdown (ptr->ssl, mode);
+}
+
 MONO_API void
 mono_btls_ssl_set_bio (MonoBtlsSsl *ptr, BIO *bio)
 {
index 98b017340e1b1a956da2bafdaf056e5bd3c0b33b..ac93c114995fee4bdf6f24f6c838158ca34aee4c 100644 (file)
@@ -68,6 +68,12 @@ mono_btls_ssl_get_peer_certificate (MonoBtlsSsl *ptr);
 void
 mono_btls_ssl_close (MonoBtlsSsl *ptr);
 
+int
+mono_btls_ssl_shutdown (MonoBtlsSsl *ptr);
+
+MONO_API void
+mono_btls_ssl_set_quiet_shutdown (MonoBtlsSsl *ptr, int mode);
+
 int
 mono_btls_ssl_get_error (MonoBtlsSsl *ptr, int ret_code);