[System] xamarin_start_wwan is not available for tvOS and watchOS.
[mono.git] / mcs / class / System / System.Net / WebConnection.cs
index 5fb20ed5f0ae9a675b06bcb057e0147cac3231bf..8c1523ad82484bb59a94c69298e2e3337a0b2c98 100644 (file)
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
 
-#if SECURITY_DEP
-
-#if MONOTOUCH || MONODROID
-using Mono.Security.Protocol.Tls;
-#else
-extern alias MonoSecurity;
-using MonoSecurity::Mono.Security.Protocol.Tls;
-#endif
-
-#endif
-
 using System.IO;
 using System.Collections;
 using System.Net.Sockets;
-using System.Reflection;
 using System.Security.Cryptography.X509Certificates;
 using System.Text;
 using System.Threading;
 using System.Diagnostics;
-
 using Mono.Net.Security;
 
 namespace System.Net
@@ -95,18 +82,11 @@ namespace System.Net
                NtlmAuthState connect_ntlm_auth_state;
                HttpWebRequest connect_request;
 
-               bool ssl;
-               bool certsAvailable;
                Exception connect_exception;
                static object classLock = new object ();
-               static Type sslStream;
-#if !MONOTOUCH && !MONODROID
-               static PropertyInfo piClient;
-               static PropertyInfo piServer;
-               static PropertyInfo piTrustFailure;
-#endif
+               MonoTlsStream tlsStream;
 
-#if MONOTOUCH
+#if MONOTOUCH && !MONOTOUCH_TV && !MONOTOUCH_WATCH
                [System.Runtime.InteropServices.DllImport ("__Internal")]
                static extern void xamarin_start_wwan (string uri);
 #endif
@@ -172,7 +152,7 @@ namespace System.Net
                                IPHostEntry hostEntry = sPoint.HostEntry;
 
                                if (hostEntry == null) {
-#if MONOTOUCH
+#if MONOTOUCH && !MONOTOUCH_TV && !MONOTOUCH_WATCH
                                        xamarin_start_wwan (sPoint.Address.ToString ());
                                        hostEntry = sPoint.HostEntry;
                                        if (hostEntry == null) {
@@ -180,7 +160,7 @@ namespace System.Net
                                                status = sPoint.UsesProxy ? WebExceptionStatus.ProxyNameResolutionFailure :
                                                                            WebExceptionStatus.NameResolutionFailure;
                                                return;
-#if MONOTOUCH
+#if MONOTOUCH && !MONOTOUCH_TV && !MONOTOUCH_WATCH
                                        }
 #endif
                                }
@@ -239,34 +219,6 @@ namespace System.Net
                        }
                }
 
-               static void EnsureSSLStreamAvailable ()
-               {
-                       lock (classLock) {
-                               if (sslStream != null)
-                                       return;
-
-#if NET_2_1 && SECURITY_DEP
-                               sslStream = typeof (HttpsClientStream);
-#else
-                               // HttpsClientStream is an internal glue class in Mono.Security.dll
-                               sslStream = Type.GetType ("Mono.Security.Protocol.Tls.HttpsClientStream, " +
-                                                       Consts.AssemblyMono_Security, false);
-
-                               if (sslStream == null) {
-                                       string msg = "Missing Mono.Security.dll assembly. " +
-                                                       "Support for SSL/TLS is unavailable.";
-
-                                       throw new NotSupportedException (msg);
-                               }
-#endif
-#if !MONOTOUCH && !MONODROID
-                               piClient = sslStream.GetProperty ("SelectedClientCertificate");
-                               piServer = sslStream.GetProperty ("ServerCertificate");
-                               piTrustFailure = sslStream.GetProperty ("TrustFailure");
-#endif
-                       }
-               }
-
                bool CreateTunnel (HttpWebRequest request, Uri connectUri,
                                   Stream stream, out byte[] buffer)
                {
@@ -441,40 +393,31 @@ namespace System.Net
                                NetworkStream serverStream = new NetworkStream (socket, false);
 
                                if (request.Address.Scheme == Uri.UriSchemeHttps) {
-                                       ssl = true;
-                                       EnsureSSLStreamAvailable ();
-                                       if (!reused || nstream == null || nstream.GetType () != sslStream) {
+#if SECURITY_DEP
+                                       if (!reused || nstream == null || tlsStream == null) {
                                                byte [] buffer = null;
                                                if (sPoint.UseConnect) {
                                                        bool ok = CreateTunnel (request, sPoint.Address, serverStream, out buffer);
                                                        if (!ok)
                                                                return false;
                                                }
-#if SECURITY_DEP
-#if MONOTOUCH || MONODROID
-                                               nstream = new HttpsClientStream (serverStream, request.ClientCertificates, request, buffer);
-#else
-                                               object[] args = new object [4] { serverStream,
-                                                       request.ClientCertificates,
-                                                       request, buffer};
-                                               nstream = (Stream) Activator.CreateInstance (sslStream, args);
-#endif
-                                               SslClientStream scs = (SslClientStream) nstream;
-                                               var helper = new ChainValidationHelper (request);
-                                               scs.ServerCertValidation2 += (certs) => helper.ValidateChain (request.Address.Host, certs);
-#endif
-                                               certsAvailable = false;
+                                               tlsStream = new MonoTlsStream (request, serverStream);
+                                               nstream = tlsStream.CreateStream (buffer);
                                        }
                                        // we also need to set ServicePoint.Certificate 
                                        // and ServicePoint.ClientCertificate but this can
                                        // only be done later (after handshake - which is
                                        // done only after a read operation).
+#else
+                                       throw new NotSupportedException ();
+#endif
                                } else {
-                                       ssl = false;
                                        nstream = serverStream;
                                }
-                       } catch (Exception) {
-                               if (!request.Aborted)
+                       } catch (Exception ex) {
+                               if (tlsStream != null)
+                                       status = tlsStream.ExceptionStatus;
+                               else if (!request.Aborted)
                                        status = WebExceptionStatus.ConnectFailure;
                                return false;
                        }
@@ -625,21 +568,6 @@ namespace System.Net
                        return (statusCode >= 200 && statusCode != 204 && statusCode != 304);
                }
 
-               internal void GetCertificates (Stream stream) 
-               {
-                       // here the SSL negotiation have been done
-#if SECURITY_DEP && (MONOTOUCH || MONODROID)
-                       HttpsClientStream s = (stream as HttpsClientStream);
-                       X509Certificate client = s.SelectedClientCertificate;
-                       X509Certificate server = s.ServerCertificate;
-#else
-                       X509Certificate client = (X509Certificate) piClient.GetValue (stream, null);
-                       X509Certificate server = (X509Certificate) piServer.GetValue (stream, null);
-#endif
-                       sPoint.SetCertificates (client, server);
-                       certsAvailable = (server != null);
-               }
-
                internal static void InitRead (object state)
                {
                        WebConnection cnc = (WebConnection) state;
@@ -911,126 +839,91 @@ namespace System.Net
                        return true;
                }
 
-               internal void ReadAsync (HttpWebRequest request, byte [] buffer, int offset, int size, WebAsyncResult result)
+
+               internal IAsyncResult BeginRead (HttpWebRequest request, byte [] buffer, int offset, int size, AsyncCallback cb, object state)
                {
-                       bool error = false;
                        Stream s = null;
                        lock (this) {
                                if (Data.request != request)
-                                       error = true;
+                                       throw new ObjectDisposedException (typeof (NetworkStream).FullName);
+                               if (nstream == null)
+                                       return null;
                                s = nstream;
                        }
 
-                       if (error) {
-                               result.SetCompleted (true, new ObjectDisposedException (typeof(NetworkStream).FullName));
-                               result.DoCallback ();
-                               return;
-                       } else if (s == null) {
-                               result.SetCompleted (true);
-                               result.DoCallback ();
-                               return;
-                       }
-
-                       var data = new WebAsyncData (request, s, buffer, offset, size);
-                       ReadAsync (data, result);
-               }
-
-               void ReadAsync (WebAsyncData data, WebAsyncResult result)
-               {
-                       bool running;
-                       try {
-                               running = ReadAsyncInner (data, result);
-                       } catch (Exception ex) {
-                               result.SetCompleted (true, ex);
-                               running = false;
+                       IAsyncResult result = null;
+                       if (!chunkedRead || (!chunkStream.DataAvailable && chunkStream.WantMore)) {
+                               try {
+                                       result = s.BeginRead (buffer, offset, size, cb, state);
+                                       cb = null;
+                               } catch (Exception) {
+                                       HandleError (WebExceptionStatus.ReceiveFailure, null, "chunked BeginRead");
+                                       throw;
+                               }
                        }
-                       if (!running)
-                               result.DoCallback ();
-               }
 
-               bool ReadAsyncInner (WebAsyncData data, WebAsyncResult result)
-               {
-                       if (chunkedRead && (chunkStream.DataAvailable || !chunkStream.WantMore)) {
-                               ReadAsyncInnerCB (data, result, null);
-                               return false;
+                       if (chunkedRead) {
+                               WebAsyncResult wr = new WebAsyncResult (cb, state, buffer, offset, size);
+                               wr.InnerAsyncResult = result;
+                               if (result == null) {
+                                       // Will be completed from the data in ChunkStream
+                                       wr.SetCompleted (true, (Exception) null);
+                                       wr.DoCallback ();
+                               }
+                               return wr;
                        }
 
-                       result.InnerAsyncResult = data.Stream.BeginRead (data.Buffer, data.Offset, data.Size, inner => ReadAsyncInnerCB (data, result, inner), null);
-                       return result.InnerAsyncResult != null && !result.InnerAsyncResult.CompletedSynchronously;
+                       return result;
                }
-
-               bool ReadAsyncInnerCB (WebAsyncData data, WebAsyncResult result, IAsyncResult innerResult)
+               
+               internal int EndRead (HttpWebRequest request, IAsyncResult result)
                {
-                       var synch = innerResult != null ? innerResult.CompletedSynchronously : false;
-
-                       bool error = false;
                        Stream s = null;
                        lock (this) {
-                               if (Data.request != data.Request || nstream == null)
-                                       error = true;
+                               if (Data.request != request)
+                                       throw new ObjectDisposedException (typeof (NetworkStream).FullName);
+                               if (nstream == null)
+                                       throw new ObjectDisposedException (typeof (NetworkStream).FullName);
                                s = nstream;
                        }
 
-                       if (error) {
-                               result.SetCompleted (synch, new ObjectDisposedException (typeof(NetworkStream).FullName));
-                               result.DoCallback ();
-                               return false;
-                       }
-
                        int nbytes = 0;
-                       try {
-                               if (innerResult != null)
-                                       nbytes = s.EndRead (innerResult);
-                       } catch (Exception ex) {
-                               result.SetCompleted (synch, ex);
-                               result.DoCallback ();
-                               return false;
-                       }
-
-                       var done = nbytes == 0;
-
-                       if (!chunkedRead) {
-                               result.SetCompleted (synch, nbytes);
-                               result.DoCallback ();
-                               return false;
+                       bool done = false;
+                       WebAsyncResult wr = null;
+                       IAsyncResult nsAsync = ((WebAsyncResult) result).InnerAsyncResult;
+                       if (chunkedRead && (nsAsync is WebAsyncResult)) {
+                               wr = (WebAsyncResult) nsAsync;
+                               IAsyncResult inner = wr.InnerAsyncResult;
+                               if (inner != null && !(inner is WebAsyncResult)) {
+                                       nbytes = s.EndRead (inner);
+                                       done = nbytes == 0;
+                               }
+                       } else if (!(nsAsync is WebAsyncResult)) {
+                               nbytes = s.EndRead (nsAsync);
+                               wr = (WebAsyncResult) result;
+                               done = nbytes == 0;
                        }
 
-                       try {
-                               chunkStream.WriteAndReadBack (data.Buffer, data.Offset, data.Size, ref nbytes);
-                       } catch (Exception e) {
-                               if (!(e is WebException))
-                                       e = new WebException ("Invalid chunked data.", e, WebExceptionStatus.ServerProtocolViolation, null);
-                               result.SetCompleted (synch, e);
-                               result.DoCallback ();
-                               return false;
-                       }
+                       if (chunkedRead) {
+                               try {
+                                       chunkStream.WriteAndReadBack (wr.Buffer, wr.Offset, wr.Size, ref nbytes);
+                                       if (!done && nbytes == 0 && chunkStream.WantMore)
+                                               nbytes = EnsureRead (wr.Buffer, wr.Offset, wr.Size);
+                               } catch (Exception e) {
+                                       if (e is WebException)
+                                               throw e;
 
-                       try {
-                               if (!done && nbytes == 0 && chunkStream.WantMore)
-                                       return ReadAsyncInner (data, result);
-                       } catch (Exception e) {
-                               if (!(e is WebException))
-                                       e = new WebException ("Invalid chunked data.", e, WebExceptionStatus.ServerProtocolViolation, null);
-                               result.SetCompleted (synch, e);
-                               result.DoCallback ();
-                               return false;
-                       }
+                                       throw new WebException ("Invalid chunked data.", e,
+                                                               WebExceptionStatus.ServerProtocolViolation, null);
+                               }
 
-                       if (done || nbytes == 0) {
-                               if (chunkStream.ChunkLeft != 0) {
-                                       result.SetCompleted (synch, new WebException ("Read error", null, WebExceptionStatus.ConnectionClosed, null));
-                                       result.DoCallback ();
-                                       return false;
-                               } else if (chunkStream.WantMore) {
-                                       result.SetCompleted (synch, new IOException ("Connection closed"));
-                                       result.DoCallback ();
-                                       return false;
+                               if ((done || nbytes == 0) && chunkStream.ChunkLeft != 0) {
+                                       HandleError (WebExceptionStatus.ReceiveFailure, null, "chunked EndRead");
+                                       throw new WebException ("Read error", null, WebExceptionStatus.ReceiveFailure, null);
                                }
                        }
 
-                       result.SetCompleted (synch, nbytes);
-                       result.DoCallback ();
-                       return false;
+                       return (nbytes != 0) ? nbytes : -1;
                }
 
                // To be called on chunkedRead when we can read no data from the ChunkStream yet
@@ -1179,9 +1072,6 @@ namespace System.Net
 
                        try {
                                s.Write (buffer, offset, size);
-                               // here SSL handshake should have been done
-                               if (ssl && !certsAvailable)
-                                       GetCertificates (s);
                        } catch (Exception e) {
                                err_msg = e.Message;
                                WebExceptionStatus wes = WebExceptionStatus.SendFailure;
@@ -1191,19 +1081,6 @@ namespace System.Net
                                        return false;
                                }
 
-                               // if SSL is in use then check for TrustFailure
-                               if (ssl) {
-#if SECURITY_DEP && (MONOTOUCH || MONODROID)
-                                       HttpsClientStream https = (s as HttpsClientStream);
-                                       if (https.TrustFailure) {
-#else
-                                       if ((bool) piTrustFailure.GetValue (s , null)) {
-#endif
-                                               wes = WebExceptionStatus.TrustFailure;
-                                               msg = "Trust failure";
-                                       }
-                               }
-
                                HandleError (wes, e, msg);
                                return false;
                        }