// // StandardBindingImporter.cs // // Author: // Martin Baulig // // Copyright (c) 2012 Xamarin Inc. (http://www.xamarin.com) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. using System; using System.Net; using System.Xml; using System.Xml.Schema; using System.Collections.Generic; using System.ServiceModel; using System.ServiceModel.Channels; using System.ServiceModel.Description; using WS = System.Web.Services.Description; using QName = System.Xml.XmlQualifiedName; namespace System.ServiceModel.Channels { public class StandardBindingImporter : IWsdlImportExtension { #region IWsdlImportExtension implementation public void BeforeImport (WS.ServiceDescriptionCollection wsdlDocuments, XmlSchemaSet xmlSchemas, ICollection policy) { } public void ImportContract (WsdlImporter importer, WsdlContractConversionContext contractContext) { } WS.Port LookupPort (WsdlImporter importer, QName name) { foreach (WS.ServiceDescription doc in importer.WsdlDocuments) { foreach (WS.Service service in doc.Services) { foreach (WS.Port port in service.Ports) { if (!name.Namespace.Equals (port.Binding.Namespace)) continue; if (!name.Name.Equals (port.Binding.Name)) continue; return port; } } } return null; } public void ImportEndpoint (WsdlImporter importer, WsdlEndpointConversionContext context) { var custom = context.Endpoint.Binding as CustomBinding; if (custom == null) return; var soapHttp = GetHttpSoapBinding (context.WsdlBinding); if (soapHttp != null) { ImportBasicHttpBinding (importer, context, custom, soapHttp); return; } var soapTcp = GetTcpSoapBinding (context.WsdlBinding); if (soapTcp != null) { ImportNetTcpBinding (importer, context, custom, soapTcp); return; } } internal static WS.SoapBinding GetHttpSoapBinding (WS.Binding binding) { WS.SoapBinding soap = null; foreach (var extension in binding.Extensions) { var check = extension as WS.SoapBinding; if (check != null) { soap = check; break; } } if (soap == null) return null; if (soap.Transport != WS.SoapBinding.HttpTransport) return null; if (soap.Style != WS.SoapBindingStyle.Document) return null; return soap; } const string TcpTransport = "http://schemas.microsoft.com/soap/tcp"; internal static WS.Soap12Binding GetTcpSoapBinding (WS.Binding binding) { WS.Soap12Binding soap = null; foreach (var extension in binding.Extensions) { var check = extension as WS.Soap12Binding; if (check != null) { soap = check; break; } } if (soap == null) return null; if (soap.Transport != TcpTransport) return null; if (soap.Style != WS.SoapBindingStyle.Document) return null; return soap; } bool ImportBasicHttpBinding ( WsdlImporter importer, WsdlEndpointConversionContext context, CustomBinding custom, WS.SoapBinding soap) { TransportBindingElement transportElement = null; MtomMessageEncodingBindingElement mtomElement = null; TextMessageEncodingBindingElement textElement = null; bool foundUnknownElement = false; foreach (var element in custom.Elements) { if (element is TransportBindingElement) transportElement = (TransportBindingElement)element; else if (element is MtomMessageEncodingBindingElement) mtomElement = (MtomMessageEncodingBindingElement)element; else if (element is TextMessageEncodingBindingElement) textElement = (TextMessageEncodingBindingElement)element; else { importer.AddWarning ( "Found unknown binding element `{0}' while attempting " + "to import binding `{0}'.", element.GetType (), custom.Name); foundUnknownElement = true; } } if (foundUnknownElement) return false; if ((mtomElement != null) && (textElement != null)) { // FIXME: Should never happen importer.AddWarning ( "Found both MtomMessageEncodingBindingElement and " + "TextMessageEncodingBindingElement while attempting to " + "import binding `{0}'.", custom.Name); return false; } BasicHttpBinding httpBinding; AuthenticationSchemes authScheme; /* * FIXME: Maybe make the BasicHttpBinding use the transport element * that we created with the TransportBindingElementImporter ? * * There seems to be no public API to do that, so maybe add a private .ctor ? * */ var httpsTransport = transportElement as HttpsTransportBindingElement; var httpTransport = transportElement as HttpTransportBindingElement; if (httpsTransport != null) { httpBinding = new BasicHttpBinding (BasicHttpSecurityMode.Transport); authScheme = httpsTransport.AuthenticationScheme; } else if (httpTransport != null) { authScheme = httpTransport.AuthenticationScheme; if ((authScheme != AuthenticationSchemes.None) && (authScheme != AuthenticationSchemes.Anonymous)) httpBinding = new BasicHttpBinding ( BasicHttpSecurityMode.TransportCredentialOnly); else httpBinding = new BasicHttpBinding (); } else { httpBinding = new BasicHttpBinding (); authScheme = AuthenticationSchemes.Anonymous; } if (mtomElement != null) httpBinding.MessageEncoding = WSMessageEncoding.Mtom; else if (textElement != null) httpBinding.MessageEncoding = WSMessageEncoding.Text; else { importer.AddWarning ( "Found neither MtomMessageEncodingBindingElement nor " + "TextMessageEncodingBindingElement while attempting to " + "import binding `{0}'.", custom.Name); return false; } httpBinding.Name = context.Endpoint.Binding.Name; httpBinding.Namespace = context.Endpoint.Binding.Namespace; switch (authScheme) { case AuthenticationSchemes.None: case AuthenticationSchemes.Anonymous: httpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None; break; case AuthenticationSchemes.Basic: httpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic; break; case AuthenticationSchemes.Digest: httpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Digest; break; case AuthenticationSchemes.Ntlm: httpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Ntlm; break; case AuthenticationSchemes.Negotiate: httpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Windows; break; default: importer.AddWarning ("Invalid auth scheme: {0}", authScheme); return false; } if ((httpsTransport != null) && httpsTransport.RequireClientCertificate) { if (httpBinding.Security.Transport.ClientCredentialType != HttpClientCredentialType.None) { importer.AddWarning ("Cannot use both client certificate and explicit auth type."); return false; } httpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate; } context.Endpoint.Binding = httpBinding; return true; } bool ImportNetTcpBinding ( WsdlImporter importer, WsdlEndpointConversionContext context, CustomBinding custom, WS.Soap12Binding soap) { TcpTransportBindingElement transportElement = null; BinaryMessageEncodingBindingElement binaryElement = null; TransactionFlowBindingElement transactionFlowElement = null; WindowsStreamSecurityBindingElement windowsStreamElement = null; SslStreamSecurityBindingElement sslStreamElement = null; bool foundUnknownElement = false; foreach (var element in custom.Elements) { if (element is TcpTransportBindingElement) transportElement = (TcpTransportBindingElement)element; else if (element is BinaryMessageEncodingBindingElement) binaryElement = (BinaryMessageEncodingBindingElement)element; else if (element is TransactionFlowBindingElement) transactionFlowElement = (TransactionFlowBindingElement)element; else if (element is WindowsStreamSecurityBindingElement) windowsStreamElement = (WindowsStreamSecurityBindingElement)element; else if (element is SslStreamSecurityBindingElement) sslStreamElement = (SslStreamSecurityBindingElement)element; else { importer.AddWarning ( "Found unknown binding element `{0}' while importing " + "binding `{1}'.", element.GetType (), custom.Name); foundUnknownElement = true; } } if (foundUnknownElement) return false; if (transportElement == null) { importer.AddWarning ( "Missing TcpTransportBindingElement while importing " + "binding `{0}'.", custom.Name); return false; } if (binaryElement == null) { importer.AddWarning ( "Missing BinaryMessageEncodingBindingElement while importing " + "binding `{0}'.", custom.Name); return false; } if ((windowsStreamElement != null) && (sslStreamElement != null)) { importer.AddWarning ( "Found both WindowsStreamSecurityBindingElement and " + "SslStreamSecurityBindingElement while importing binding `{0}.", custom.Name); return false; } NetTcpSecurity security; if (windowsStreamElement != null) { security = new NetTcpSecurity (SecurityMode.Transport); security.Transport.ProtectionLevel = windowsStreamElement.ProtectionLevel; } else if (sslStreamElement != null) { security = new NetTcpSecurity (SecurityMode.TransportWithMessageCredential); } else { security = new NetTcpSecurity (SecurityMode.None); } var netTcp = new NetTcpBinding (transportElement, security, false); netTcp.Name = context.Endpoint.Binding.Name; netTcp.Namespace = context.Endpoint.Binding.Namespace; context.Endpoint.Binding = netTcp; return true; } #endregion } }