//---------------------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. //---------------------------------------------------------------------------- namespace System.ServiceModel.Channels { using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Runtime; using System.Runtime.Serialization; using System.ServiceModel; [DataContract] sealed class BaseUriWithWildcard { [DataMember] Uri baseAddress; const char segmentDelimiter = '/'; [DataMember] HostNameComparisonMode hostNameComparisonMode; const string plus = "+"; const string star = "*"; const int HttpUriDefaultPort = 80; const int HttpsUriDefaultPort = 443; // Derived from [DataMember] fields Comparand comparand; int hashCode; public BaseUriWithWildcard(Uri baseAddress, HostNameComparisonMode hostNameComparisonMode) { this.baseAddress = baseAddress; this.hostNameComparisonMode = hostNameComparisonMode; this.SetComparisonAddressAndHashCode(); // Note the Uri may contain query string for WSDL purpose. // So do not check IsValid(). } BaseUriWithWildcard(string protocol, int defaultPort, string binding, int segmentCount, string path, string sampleBinding) { string[] urlParameters = SplitBinding(binding); if (urlParameters.Length != segmentCount) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( new UriFormatException(SR.GetString(SR.Hosting_MisformattedBinding, binding, protocol, sampleBinding))); } int currentIndex = segmentCount - 1; string host = ParseHostAndHostNameComparisonMode(urlParameters[currentIndex]); int port = -1; if (--currentIndex >= 0) { string portString = urlParameters[currentIndex].Trim(); if (!string.IsNullOrEmpty(portString) && !int.TryParse(portString, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out port)) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new UriFormatException(SR.GetString(SR.Hosting_MisformattedPort, protocol, binding, portString))); } if (port == defaultPort) { // Set to -1 so that Uri does not show it in the string. port = -1; } } try { Fx.Assert(path != null, "path should never be null here"); this.baseAddress = new UriBuilder(protocol, host, port, path).Uri; } catch (Exception exception) { if (Fx.IsFatal(exception)) { throw; } DiagnosticUtility.TraceHandledException(exception, TraceEventType.Error); throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new UriFormatException(SR.GetString(SR.Hosting_MisformattedBindingData, binding, protocol))); } SetComparisonAddressAndHashCode(); } internal Uri BaseAddress { get { return this.baseAddress; } } internal HostNameComparisonMode HostNameComparisonMode { get { return this.hostNameComparisonMode; } } static string[] SplitBinding(string binding) { bool parsingIPv6Address = false; string[] tokens = null; const char splitChar = ':', startIPv6Address = '[', endIPv6Address = ']'; List splitLocations = null; for (int i = 0; i < binding.Length; i++) { if (parsingIPv6Address && binding[i] == endIPv6Address) { parsingIPv6Address = false; } else if (binding[i] == startIPv6Address) { parsingIPv6Address = true; } else if (!parsingIPv6Address && binding[i] == splitChar) { if (splitLocations == null) { splitLocations = new List(); } splitLocations.Add(i); } } if (splitLocations == null) { tokens = new string[] { binding }; } else { tokens = new string[splitLocations.Count + 1]; int startIndex = 0; for (int i = 0; i < tokens.Length; i++) { if (i < splitLocations.Count) { int nextSplitIndex = splitLocations[i]; tokens[i] = binding.Substring(startIndex, nextSplitIndex - startIndex); startIndex = nextSplitIndex + 1; } else //splitting the last segment { if (startIndex < binding.Length) { tokens[i] = binding.Substring(startIndex, binding.Length - startIndex); } else { //splitChar was the last character in the string tokens[i] = string.Empty; } } } } return tokens; } internal static BaseUriWithWildcard CreateHostedUri(string protocol, string binding, string path) { Fx.Assert(protocol != null, "caller must verify"); if (binding == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("binding"); } if (path == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("path"); } if (protocol.Equals(Uri.UriSchemeHttp, StringComparison.OrdinalIgnoreCase)) { // For http, binding format is: "::" // as specified in http://www.microsoft.com/resources/documentation/WindowsServ/2003/standard/proddocs/en-us/Default.asp?url=/resources/documentation/WindowsServ/2003/standard/proddocs/en-us/ref_mb_serverbindings.asp return new BaseUriWithWildcard(Uri.UriSchemeHttp, HttpUriDefaultPort, binding, 3, path, ":80:"); } else if (protocol.Equals(Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase)) { // For https, binding format is the same as http return new BaseUriWithWildcard(Uri.UriSchemeHttps, HttpsUriDefaultPort, binding, 3, path, ":443:"); } else if (protocol.Equals(Uri.UriSchemeNetTcp, StringComparison.OrdinalIgnoreCase)) { // For net.tcp, binding format is: ":" return new BaseUriWithWildcard(Uri.UriSchemeNetTcp, TcpUri.DefaultPort, binding, 2, path, "808:*"); } else if (protocol.Equals(Uri.UriSchemeNetPipe, StringComparison.OrdinalIgnoreCase)) { return CreateHostedPipeUri(binding, path); } else if (protocol.Equals(MsmqUri.NetMsmqAddressTranslator.Scheme, StringComparison.OrdinalIgnoreCase)) { return new BaseUriWithWildcard(MsmqUri.NetMsmqAddressTranslator.Scheme, -1, binding, 1, path, "*"); } else if (protocol.Equals(MsmqUri.FormatNameAddressTranslator.Scheme, StringComparison.OrdinalIgnoreCase)) { return new BaseUriWithWildcard(MsmqUri.FormatNameAddressTranslator.Scheme, -1, binding, 1, path, "*"); } throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new UriFormatException(SR.GetString(SR.Hosting_NotSupportedProtocol, binding))); } internal static BaseUriWithWildcard CreateHostedPipeUri(string binding, string path) { // For net.pipe, binding format is: "" return new BaseUriWithWildcard(Uri.UriSchemeNetPipe, -1, binding, 1, path, "*"); } public override bool Equals(object o) { BaseUriWithWildcard other = o as BaseUriWithWildcard; if (other == null || other.hashCode != this.hashCode || other.hostNameComparisonMode != this.hostNameComparisonMode || other.comparand.Port != this.comparand.Port) { return false; } if (!object.ReferenceEquals(other.comparand.Scheme, this.comparand.Scheme)) { return false; } return this.comparand.Address.Equals(other.comparand.Address); } public override int GetHashCode() { return this.hashCode; } internal bool IsBaseOf(Uri fullAddress) { if ((object)baseAddress.Scheme != (object)fullAddress.Scheme) { return false; } if (baseAddress.Port != fullAddress.Port) { return false; } if (this.HostNameComparisonMode == HostNameComparisonMode.Exact) { if (string.Compare(baseAddress.Host, fullAddress.Host, StringComparison.OrdinalIgnoreCase) != 0) { return false; } } string s1 = baseAddress.GetComponents(UriComponents.Path | UriComponents.KeepDelimiter, UriFormat.Unescaped); string s2 = fullAddress.GetComponents(UriComponents.Path | UriComponents.KeepDelimiter, UriFormat.Unescaped); if (s1.Length > s2.Length) { return false; } if (s1.Length < s2.Length && s1[s1.Length - 1] != segmentDelimiter && s2[s1.Length] != segmentDelimiter) { // Matching over segments return false; } return string.Compare(s2, 0, s1, 0, s1.Length, StringComparison.OrdinalIgnoreCase) == 0; } [OnDeserialized] internal void OnDeserialized(StreamingContext context) { UriSchemeKeyedCollection.ValidateBaseAddress(baseAddress, "context"); if (!HostNameComparisonModeHelper.IsDefined(this.HostNameComparisonMode)) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("context", SR.GetString(SR.Hosting_BaseUriDeserializedNotValid)); } this.SetComparisonAddressAndHashCode(); } string ParseHostAndHostNameComparisonMode(string host) { if (string.IsNullOrEmpty(host) || host.Equals(star)) { hostNameComparisonMode = HostNameComparisonMode.WeakWildcard; host = DnsCache.MachineName; } else if (host.Equals(plus)) { hostNameComparisonMode = HostNameComparisonMode.StrongWildcard; host = DnsCache.MachineName; } else { hostNameComparisonMode = HostNameComparisonMode.Exact; } return host; } void SetComparisonAddressAndHashCode() { if (this.HostNameComparisonMode == HostNameComparisonMode.Exact) { // Use canonical string representation of the full base address for comparison this.comparand.Address = this.baseAddress.ToString(); } else { // Use canonical string representation of the absolute path for comparison this.comparand.Address = this.baseAddress.GetComponents(UriComponents.Path | UriComponents.KeepDelimiter, UriFormat.UriEscaped); } this.comparand.Port = this.baseAddress.Port; this.comparand.Scheme = this.baseAddress.Scheme; if ((this.comparand.Port == -1) && ((object)this.comparand.Scheme == (object)Uri.UriSchemeNetTcp)) { // Compensate for the fact that the Uri type doesn't know about our default TCP port number this.comparand.Port = TcpUri.DefaultPort; } this.hashCode = this.comparand.Address.GetHashCode() ^ this.comparand.Port ^ (int)this.HostNameComparisonMode; } public override string ToString() { return string.Format(CultureInfo.InvariantCulture, "{0}:{1}", this.HostNameComparisonMode, this.BaseAddress); } struct Comparand { public string Address; public int Port; public string Scheme; } } }