using System; using System.Collections.Generic; using System.ComponentModel; using System.IO; using System.Linq; using System.Net.Security; using System.Runtime.InteropServices; using System.Security.Cryptography; using System.ServiceModel; using System.ServiceModel.Channels; using System.Text; using System.Xml; internal static class Program { private const string Namespace = "aa"; private const string HistoryService = "Hist"; private static int Main(string[] args) { string targetName = GetArg(args, "--target") ?? @"NT SERVICE\aahClientAccessPoint"; string endpoint = GetArg(args, "--endpoint") ?? "net.pipe://localhost/Hist"; string retrievalEndpoint = GetArg(args, "--retr-endpoint") ?? "net.pipe://localhost/Retr"; string? open2ReplayPath = GetArg(args, "--open2-replay"); string? dataQueryReplayPath = GetArg(args, "--data-query-replay"); int maxBufferSize = int.TryParse(GetArg(args, "--max-buffer-size"), out int parsedMaxBufferSize) ? parsedMaxBufferSize : 66303; try { IHistoryServiceContract2 channel = CreatePipeChannel(endpoint, maxBufferSize); uint getVersionReturn = channel.GetInterfaceVersion(out uint interfaceVersion); using SspiClient sspi = new("Negotiate", targetName); byte[] incoming = Array.Empty(); List roundJson = new(); bool? finalServerSuccess = null; string? finalStatus = null; int? finalServerOutputLength = null; NativeError? finalNativeError = null; Guid contextKey = Guid.NewGuid(); string handle = contextKey.ToString("D").ToUpperInvariant(); for (int round = 0; round < 8; round++) { SspiStepResult clientStep = sspi.Next(incoming); ApplyNativeNtlmNegotiateVersionFlag(clientStep.Token); byte[] wrapped = WrapValidateClientCredentialToken(round == 0, clientStep.Token); bool serverSuccess = channel.ValidateClientCredential(handle, wrapped, out byte[] serverOutput, out byte[] errorBuffer); serverOutput ??= Array.Empty(); errorBuffer ??= Array.Empty(); NativeError? nativeError = TryReadNativeError(errorBuffer); bool serverContinue = serverOutput.Length > 0 && serverOutput[0] != 0; byte[] serverToken = serverContinue && serverOutput.Length > 1 ? serverOutput.Skip(1).ToArray() : Array.Empty(); roundJson.Add("{" + JsonProp("Round", round) + "," + JsonProp("ClientStatus", clientStep.Status) + "," + JsonProp("OutgoingLength", clientStep.Token.Length) + "," + JsonProp("OutgoingSha256", HashBytesOrNull(clientStep.Token)) + "," + JsonProp("OutgoingPrefixHex", ToPrefixHex(clientStep.Token, 32)) + "," + JsonProp("WrappedOutgoingLength", wrapped.Length) + "," + JsonProp("WrappedOutgoingSha256", HashBytesOrNull(wrapped)) + "," + JsonProp("WrappedOutgoingPrefixHex", ToPrefixHex(wrapped, 32)) + "," + JsonProp("ServerSuccess", serverSuccess) + "," + JsonProp("ServerOutputLength", serverOutput.Length) + "," + JsonProp("ServerOutputSha256", HashBytesOrNull(serverOutput)) + "," + JsonProp("ServerOutputPrefixHex", ToPrefixHex(serverOutput, 32)) + "," + JsonProp("ServerContinue", serverContinue) + "," + JsonProp("ServerTokenLength", serverToken.Length) + "," + JsonProp("ErrorLength", errorBuffer.Length) + "," + "\"NativeError\":" + FormatNativeError(nativeError) + "}"); finalServerSuccess = serverSuccess; finalStatus = clientStep.Status; finalServerOutputLength = serverOutput.Length; finalNativeError = nativeError; if (!serverSuccess || clientStep.Done || !serverContinue) { break; } incoming = serverToken; } string? chainJson = null; if (finalServerSuccess == true && finalNativeError is null && open2ReplayPath != null) { chainJson = RunOpen2AndQueryChain(channel, retrievalEndpoint, contextKey, open2ReplayPath, dataQueryReplayPath, maxBufferSize); } Console.WriteLine("{" + JsonProp("Runtime", ".NET Framework") + "," + JsonProp("Endpoint", endpoint) + "," + JsonProp("Operation", "ValCl") + "," + JsonProp("Transport", "NamedPipeNone") + "," + JsonProp("GetVersionReturnCode", getVersionReturn) + "," + JsonProp("InterfaceVersion", interfaceVersion) + "," + JsonProp("TargetName", targetName) + "," + JsonProp("HandleSha256", Sha256Utf8(handle)) + "," + JsonProp("HandleLength", handle.Length) + "," + JsonProp("FinalStatus", finalStatus) + "," + JsonProp("FinalServerSuccess", finalServerSuccess) + "," + JsonProp("FinalServerOutputLength", finalServerOutputLength) + "," + "\"FinalNativeError\":" + FormatNativeError(finalNativeError) + "," + "\"Rounds\":[" + string.Join(",", roundJson) + "]" + (chainJson is null ? string.Empty : "," + chainJson) + "}"); return 0; } catch (Exception ex) { Console.Error.WriteLine(Environment.GetEnvironmentVariable("AVEVA_HISTORIAN_RE_STACK") == "1" ? ex.ToString() : ex.Message); return 1; } } private static IHistoryServiceContract2 CreatePipeChannel(string endpoint, int maxBufferSize) { ChannelFactory factory = new(BuildMdasPipeBinding(maxBufferSize), new EndpointAddress(endpoint)); return factory.CreateChannel(); } private static IRetrievalServiceContract2 CreateRetrievalPipeChannel(string endpoint, int maxBufferSize) { ChannelFactory factory = new(BuildMdasPipeBinding(maxBufferSize), new EndpointAddress(endpoint)); return factory.CreateChannel(); } private static CustomBinding BuildMdasPipeBinding(int maxBufferSize) { NetNamedPipeBinding nativeShape = new() { MaxBufferSize = maxBufferSize, MaxReceivedMessageSize = maxBufferSize }; nativeShape.Security.Mode = NetNamedPipeSecurityMode.None; nativeShape.ReaderQuotas.MaxArrayLength = maxBufferSize; BindingElementCollection elements = nativeShape.CreateBindingElements(); for (int i = 0; i < elements.Count; i++) { if (elements[i] is MessageEncodingBindingElement encoding) { elements[i] = new MdasMessageEncodingBindingElement(encoding); break; } } return new CustomBinding(elements) { OpenTimeout = TimeSpan.FromSeconds(10), CloseTimeout = TimeSpan.FromSeconds(10), SendTimeout = TimeSpan.FromSeconds(10), ReceiveTimeout = TimeSpan.FromSeconds(10) }; } private static string RunOpen2AndQueryChain( IHistoryServiceContract2 historyChannel, string retrievalEndpoint, Guid contextKey, string open2ReplayPath, string? dataQueryReplayPath, int maxBufferSize) { StringBuilder json = new(); json.Append("\"Chain\":{"); byte[] open2RequestRaw = File.ReadAllBytes(open2ReplayPath); if (open2RequestRaw.Length < 17 || open2RequestRaw[0] != 6) { json.Append(JsonProp("Open2ReplaySource", open2ReplayPath)); json.Append("," + JsonProp("Open2ReplayLength", open2RequestRaw.Length)); json.Append("," + JsonProp("Open2Skipped", "replay must be a v6 OpenConnection3 buffer of at least 17 bytes")); json.Append("}"); return json.ToString(); } byte[] open2Request = (byte[])open2RequestRaw.Clone(); byte[] keyBytes = contextKey.ToByteArray(); Buffer.BlockCopy(keyBytes, 0, open2Request, 1, 16); json.Append(JsonProp("Open2RequestLength", open2Request.Length)); json.Append("," + JsonProp("Open2RequestOriginalSha256", Sha256(open2RequestRaw))); json.Append("," + JsonProp("Open2RequestSplicedSha256", Sha256(open2Request))); byte[] open2In = open2Request; bool open2Success; byte[] open2Out; byte[] open2Err; try { open2Success = historyChannel.OpenConnection2(ref open2In, out open2Out, out open2Err); } catch (Exception ex) { json.Append("," + JsonProp("Open2Exception", ex.GetType().Name + ": " + ex.Message)); json.Append("}"); return json.ToString(); } open2Out ??= Array.Empty(); open2Err ??= Array.Empty(); json.Append("," + JsonProp("Open2Success", open2Success)); json.Append("," + JsonProp("Open2ResponseLength", open2Out.Length)); json.Append("," + JsonProp("Open2ResponseSha256", HashBytesOrNull(open2Out))); json.Append("," + JsonProp("Open2ResponsePrefixHex", ToPrefixHex(open2Out, 8))); json.Append("," + JsonProp("Open2ErrorLength", open2Err.Length)); json.Append("," + "\"Open2NativeError\":" + FormatNativeError(TryReadNativeError(open2Err))); uint clientHandle = 0; byte responseVersion = 0; if (open2Success && open2Out.Length >= 5) { responseVersion = open2Out[0]; clientHandle = ReadUInt32LittleEndian(open2Out, 1); json.Append("," + JsonProp("Open2ResponseVersion", responseVersion)); json.Append("," + JsonProp("Open2ClientHandlePresent", true)); } if (!open2Success || clientHandle == 0) { json.Append("}"); return json.ToString(); } IRetrievalServiceContract2 retrChannel = CreateRetrievalPipeChannel(retrievalEndpoint, maxBufferSize); try { uint retrGetVersionReturn = retrChannel.GetInterfaceVersion(out uint retrInterfaceVersion); json.Append("," + JsonProp("RetrGetVersionReturnCode", retrGetVersionReturn)); json.Append("," + JsonProp("RetrInterfaceVersion", retrInterfaceVersion)); uint isOriginalReturn = retrChannel.IsOriginalAllowed(clientHandle, out bool isAllowed); json.Append("," + JsonProp("IsOriginalAllowedReturnCode", isOriginalReturn)); json.Append("," + JsonProp("IsOriginalAllowedIsAllowed", isAllowed)); if (dataQueryReplayPath is null) { json.Append("," + JsonProp("StartQuery2Skipped", "no --data-query-replay path")); json.Append("}"); return json.ToString(); } byte[] dataQueryRequest = File.ReadAllBytes(dataQueryReplayPath); json.Append("," + JsonProp("StartQuery2RequestLength", dataQueryRequest.Length)); json.Append("," + JsonProp("StartQuery2RequestSha256", Sha256(dataQueryRequest))); uint queryHandle = 0; bool startQuerySuccess; uint responseSize; byte[] responseBuffer; uint errorSize; byte[] errorBuffer; try { startQuerySuccess = retrChannel.StartQuery2( clientHandle, queryRequestType: 1, requestSize: (uint)dataQueryRequest.Length, requestBuffer: dataQueryRequest, out responseSize, out responseBuffer, ref queryHandle, out errorSize, out errorBuffer); } catch (Exception ex) { json.Append("," + JsonProp("StartQuery2Exception", ex.GetType().Name + ": " + ex.Message)); json.Append("}"); return json.ToString(); } responseBuffer ??= Array.Empty(); errorBuffer ??= Array.Empty(); json.Append("," + JsonProp("StartQuery2Success", startQuerySuccess)); json.Append("," + JsonProp("StartQuery2ResponseSize", (int)responseSize)); json.Append("," + JsonProp("StartQuery2ResponseSha256", HashBytesOrNull(responseBuffer))); json.Append("," + JsonProp("StartQuery2ResponsePrefixHex", ToPrefixHex(responseBuffer, 8))); json.Append("," + JsonProp("StartQuery2ErrorSize", (int)errorSize)); json.Append("," + JsonProp("StartQuery2QueryHandlePresent", queryHandle != 0)); json.Append("," + "\"StartQuery2NativeError\":" + FormatNativeError(TryReadNativeError(errorBuffer))); } finally { try { ((ICommunicationObject)retrChannel).Close(); } catch { try { ((ICommunicationObject)retrChannel).Abort(); } catch { } } } json.Append("}"); return json.ToString(); } private static byte[] WrapValidateClientCredentialToken(bool isFirstRound, byte[] token) { byte[] buffer = new byte[checked(1 + sizeof(uint) + token.Length)]; buffer[0] = isFirstRound ? (byte)1 : (byte)0; WriteUInt32LittleEndian(buffer, 1, checked((uint)token.Length)); Buffer.BlockCopy(token, 0, buffer, 1 + sizeof(uint), token.Length); return buffer; } private static void ApplyNativeNtlmNegotiateVersionFlag(byte[] token) { byte[] ntlmSignature = Encoding.ASCII.GetBytes("NTLMSSP\0"); if (token.Length >= 16 && token.Take(ntlmSignature.Length).SequenceEqual(ntlmSignature) && ReadUInt32LittleEndian(token, 8) == 1) { uint flags = ReadUInt32LittleEndian(token, 12); flags |= 0x0010_0000; WriteUInt32LittleEndian(token, 12, flags); } } private static NativeError? TryReadNativeError(byte[] bytes) { if (bytes.Length < 5) { return null; } uint type = bytes[0]; uint code = ReadUInt32LittleEndian(bytes, 1); return new NativeError(type, code, code == 1 ? "Failure" : null); } private static uint ReadUInt32LittleEndian(byte[] bytes, int offset) { return (uint)(bytes[offset] | bytes[offset + 1] << 8 | bytes[offset + 2] << 16 | bytes[offset + 3] << 24); } private static void WriteUInt32LittleEndian(byte[] bytes, int offset, uint value) { bytes[offset] = (byte)value; bytes[offset + 1] = (byte)(value >> 8); bytes[offset + 2] = (byte)(value >> 16); bytes[offset + 3] = (byte)(value >> 24); } private static string? GetArg(string[] args, string name) { for (int i = 0; i < args.Length - 1; i++) { if (string.Equals(args[i], name, StringComparison.OrdinalIgnoreCase)) { return args[i + 1]; } } return null; } private static string JsonProp(string name, string? value) { return "\"" + JsonEscape(name) + "\":" + (value is null ? "null" : "\"" + JsonEscape(value) + "\""); } private static string JsonProp(string name, bool value) => "\"" + JsonEscape(name) + "\":" + (value ? "true" : "false"); private static string JsonProp(string name, bool? value) => "\"" + JsonEscape(name) + "\":" + (value.HasValue ? value.Value ? "true" : "false" : "null"); private static string JsonProp(string name, int value) => "\"" + JsonEscape(name) + "\":" + value; private static string JsonProp(string name, int? value) => "\"" + JsonEscape(name) + "\":" + (value.HasValue ? value.Value.ToString() : "null"); private static string JsonProp(string name, uint value) => "\"" + JsonEscape(name) + "\":" + value; private static string FormatNativeError(NativeError? error) { return error is null ? "null" : "{" + JsonProp("Type", error.Value.Type) + "," + JsonProp("Code", error.Value.Code) + "," + JsonProp("Name", error.Value.Name) + "}"; } private static string? HashBytesOrNull(byte[] bytes) { return bytes.Length == 0 ? null : Sha256(bytes); } private static string Sha256Utf8(string value) => Sha256(Encoding.UTF8.GetBytes(value)); private static string Sha256(byte[] bytes) { using SHA256 sha256 = SHA256.Create(); return BitConverter.ToString(sha256.ComputeHash(bytes)).Replace("-", string.Empty).ToLowerInvariant(); } private static string ToPrefixHex(byte[] bytes, int maxBytes) { int count = Math.Min(bytes.Length, maxBytes); StringBuilder builder = new(count * 2); for (int i = 0; i < count; i++) { builder.Append(bytes[i].ToString("x2")); } return builder.ToString(); } private static string JsonEscape(string value) { return value .Replace("\\", "\\\\") .Replace("\"", "\\\"") .Replace("\r", "\\r") .Replace("\n", "\\n"); } private readonly struct NativeError { public NativeError(uint type, uint code, string? name) { Type = type; Code = code; Name = name; } public uint Type { get; } public uint Code { get; } public string? Name { get; } } private readonly struct SspiStepResult { public SspiStepResult(byte[] token, string status, bool done) { Token = token; Status = status; Done = done; } public byte[] Token { get; } public string Status { get; } public bool Done { get; } } private sealed class SspiClient : IDisposable { private const int SECPKG_CRED_OUTBOUND = 2; private const int SECBUFFER_TOKEN = 2; private const int ISC_REQ_REPLAY_DETECT = 0x4; private const int ISC_REQ_SEQUENCE_DETECT = 0x8; private const int ISC_REQ_CONFIDENTIALITY = 0x10; private const int ISC_REQ_CONNECTION = 0x800; private const int ISC_REQ_IDENTIFY = 0x20000; private const int ISC_REQ_ALLOCATE_MEMORY = 0x100; private const int SEC_E_OK = 0; private const int SEC_I_CONTINUE_NEEDED = 0x00090312; private readonly string targetName; private SecHandle credential; private SecHandle context; private bool haveContext; private int roundIndex; public SspiClient(string package, string targetName) { this.targetName = targetName; credential = default; long expiry; int status = AcquireCredentialsHandle(null, package, SECPKG_CRED_OUTBOUND, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, ref credential, out expiry); ThrowIfFailed(status, "AcquireCredentialsHandle"); } public SspiStepResult Next(byte[] incoming) { SecBufferDesc outBufferDesc = CreateOutputBufferDesc(); SecBufferDesc? inBufferDesc = incoming.Length == 0 ? null : CreateInputBufferDesc(incoming); try { uint contextAttributes; long expiry; SecHandle newContext = default; int status; int nativeBase = ISC_REQ_REPLAY_DETECT | ISC_REQ_SEQUENCE_DETECT | ISC_REQ_CONFIDENTIALITY | ISC_REQ_CONNECTION; int contextRequirements = ISC_REQ_ALLOCATE_MEMORY | nativeBase | (roundIndex == 0 ? ISC_REQ_IDENTIFY : 0); if (inBufferDesc.HasValue) { SecBufferDesc input = inBufferDesc.Value; status = InitializeSecurityContext( ref credential, ref context, targetName, contextRequirements, 0, 0, ref input, 0, ref newContext, ref outBufferDesc, out contextAttributes, out expiry); } else { status = InitializeSecurityContext( ref credential, IntPtr.Zero, targetName, contextRequirements, 0, 0, IntPtr.Zero, 0, ref newContext, ref outBufferDesc, out contextAttributes, out expiry); } if (!haveContext) { context = newContext; haveContext = true; } ThrowIfFailed(status, "InitializeSecurityContext", allowContinue: true); byte[] token = ReadTokenAndFree(outBufferDesc); roundIndex++; return new SspiStepResult(token, status == SEC_E_OK ? "Completed" : "ContinueNeeded", status == SEC_E_OK); } finally { if (inBufferDesc.HasValue) { FreeBufferDesc(inBufferDesc.Value, freeToken: true); } } } public void Dispose() { if (haveContext) { DeleteSecurityContext(ref context); } FreeCredentialsHandle(ref credential); } private static byte[] ReadTokenAndFree(SecBufferDesc desc) { try { SecBuffer buffer = Marshal.PtrToStructure(desc.pBuffers); if (buffer.cbBuffer == 0 || buffer.pvBuffer == IntPtr.Zero) { return Array.Empty(); } byte[] bytes = new byte[buffer.cbBuffer]; Marshal.Copy(buffer.pvBuffer, bytes, 0, bytes.Length); FreeContextBuffer(buffer.pvBuffer); return bytes; } finally { FreeBufferDesc(desc, freeToken: false); } } private static SecBufferDesc CreateOutputBufferDesc() { SecBuffer buffer = new() { BufferType = SECBUFFER_TOKEN, cbBuffer = 0, pvBuffer = IntPtr.Zero }; IntPtr bufferPtr = Marshal.AllocHGlobal(Marshal.SizeOf()); Marshal.StructureToPtr(buffer, bufferPtr, false); return new SecBufferDesc { ulVersion = 0, cBuffers = 1, pBuffers = bufferPtr }; } private static SecBufferDesc CreateInputBufferDesc(byte[] token) { IntPtr tokenPtr = Marshal.AllocHGlobal(token.Length); Marshal.Copy(token, 0, tokenPtr, token.Length); SecBuffer buffer = new() { BufferType = SECBUFFER_TOKEN, cbBuffer = token.Length, pvBuffer = tokenPtr }; IntPtr bufferPtr = Marshal.AllocHGlobal(Marshal.SizeOf()); Marshal.StructureToPtr(buffer, bufferPtr, false); return new SecBufferDesc { ulVersion = 0, cBuffers = 1, pBuffers = bufferPtr }; } private static void FreeBufferDesc(SecBufferDesc desc, bool freeToken) { if (desc.pBuffers == IntPtr.Zero) { return; } if (freeToken) { SecBuffer buffer = Marshal.PtrToStructure(desc.pBuffers); if (buffer.pvBuffer != IntPtr.Zero) { Marshal.FreeHGlobal(buffer.pvBuffer); } } Marshal.FreeHGlobal(desc.pBuffers); } private static void ThrowIfFailed(int status, string operation, bool allowContinue = false) { if (status == SEC_E_OK || allowContinue && status == SEC_I_CONTINUE_NEEDED) { return; } throw new Win32Exception(status, operation + " failed with 0x" + status.ToString("X8")); } [DllImport("secur32.dll", CharSet = CharSet.Unicode, SetLastError = false)] private static extern int AcquireCredentialsHandle( string? pszPrincipal, string pszPackage, int fCredentialUse, IntPtr pvLogonId, IntPtr pAuthData, IntPtr pGetKeyFn, IntPtr pvGetKeyArgument, ref SecHandle phCredential, out long ptsExpiry); [DllImport("secur32.dll", CharSet = CharSet.Unicode, SetLastError = false)] private static extern int InitializeSecurityContext( ref SecHandle phCredential, IntPtr phContext, string pszTargetName, int fContextReq, int Reserved1, int TargetDataRep, IntPtr pInput, int Reserved2, ref SecHandle phNewContext, ref SecBufferDesc pOutput, out uint pfContextAttr, out long ptsExpiry); [DllImport("secur32.dll", CharSet = CharSet.Unicode, SetLastError = false)] private static extern int InitializeSecurityContext( ref SecHandle phCredential, ref SecHandle phContext, string pszTargetName, int fContextReq, int Reserved1, int TargetDataRep, ref SecBufferDesc pInput, int Reserved2, ref SecHandle phNewContext, ref SecBufferDesc pOutput, out uint pfContextAttr, out long ptsExpiry); [DllImport("secur32.dll", CharSet = CharSet.Unicode, SetLastError = false)] private static extern int InitializeSecurityContext( ref SecHandle phCredential, ref SecHandle phContext, string pszTargetName, int fContextReq, int Reserved1, int TargetDataRep, IntPtr pInput, int Reserved2, ref SecHandle phNewContext, ref SecBufferDesc pOutput, out uint pfContextAttr, out long ptsExpiry); [DllImport("secur32.dll", SetLastError = false)] private static extern int DeleteSecurityContext(ref SecHandle phContext); [DllImport("secur32.dll", SetLastError = false)] private static extern int FreeCredentialsHandle(ref SecHandle phCredential); [DllImport("secur32.dll", SetLastError = false)] private static extern int FreeContextBuffer(IntPtr pvContextBuffer); [StructLayout(LayoutKind.Sequential)] private struct SecHandle { public IntPtr dwLower; public IntPtr dwUpper; } [StructLayout(LayoutKind.Sequential)] private struct SecBuffer { public int cbBuffer; public int BufferType; public IntPtr pvBuffer; } [StructLayout(LayoutKind.Sequential)] private struct SecBufferDesc { public int ulVersion; public int cBuffers; public IntPtr pBuffers; } } } [ServiceContract(Name = "Hist", Namespace = "aa")] internal interface IHistoryServiceContract { [OperationContract(Name = "GetV")] uint GetInterfaceVersion(out uint version); } [ServiceContract(Name = "Hist", Namespace = "aa")] internal interface IHistoryServiceContract2 : IHistoryServiceContract { [OperationContract(Name = "ValCl")] [return: MarshalAs(UnmanagedType.U1)] bool ValidateClientCredential( string handle, [MessageParameter(Name = "inBuff")] byte[] inputBuffer, [MessageParameter(Name = "outBuff")] out byte[] outputBuffer, out byte[] errorBuffer); [OperationContract(Name = "Open2")] [return: MarshalAs(UnmanagedType.U1)] bool OpenConnection2( [MessageParameter(Name = "inParameters")] ref byte[] inParameters, [MessageParameter(Name = "outParameters")] out byte[] outParameters, [MessageParameter(Name = "err")] out byte[] err); } [ServiceContract(Name = "Retr", Namespace = "aa")] internal interface IRetrievalServiceContract { [OperationContract(Name = "GetV")] uint GetInterfaceVersion(out uint version); [OperationContract] uint IsOriginalAllowed(uint clientHandle, out bool isAllowed); } [ServiceContract(Name = "Retr", Namespace = "aa")] internal interface IRetrievalServiceContract2 : IRetrievalServiceContract { [OperationContract] [return: MarshalAs(UnmanagedType.U1)] bool StartQuery2( uint clientHandle, ushort queryRequestType, uint requestSize, [MessageParameter(Name = "pRequestBuff")] byte[] requestBuffer, out uint responseSize, [MessageParameter(Name = "pResponseBuff")] out byte[] responseBuffer, ref uint queryHandle, [MessageParameter(Name = "errSize")] out uint errorSize, [MessageParameter(Name = "err")] out byte[] errorBuffer); [OperationContract] [return: MarshalAs(UnmanagedType.U1)] bool GetNextQueryResultBuffer2( uint clientHandle, uint queryHandle, out uint resultSize, [MessageParameter(Name = "pResultBuff")] out byte[] resultBuffer, [MessageParameter(Name = "errSize")] out uint errorSize, [MessageParameter(Name = "err")] out byte[] errorBuffer); } internal sealed class MdasMessageEncodingBindingElement : MessageEncodingBindingElement { private readonly MessageEncodingBindingElement inner; public MdasMessageEncodingBindingElement(MessageEncodingBindingElement inner) { this.inner = inner; } private MdasMessageEncodingBindingElement(MdasMessageEncodingBindingElement source) { inner = (MessageEncodingBindingElement)source.inner.Clone(); } public override MessageVersion MessageVersion { get => inner.MessageVersion; set => inner.MessageVersion = value; } public override MessageEncoderFactory CreateMessageEncoderFactory() { return new MdasMessageEncoderFactory(inner.CreateMessageEncoderFactory()); } public override BindingElement Clone() { return new MdasMessageEncodingBindingElement(this); } public override IChannelFactory BuildChannelFactory(BindingContext context) { context.BindingParameters.Add(this); return context.BuildInnerChannelFactory(); } public override bool CanBuildChannelFactory(BindingContext context) { context.BindingParameters.Add(this); return context.CanBuildInnerChannelFactory(); } public override T GetProperty(BindingContext context) { return inner.GetProperty(context) ?? context.GetInnerProperty(); } } internal sealed class MdasMessageEncoderFactory : MessageEncoderFactory { private readonly MessageEncoderFactory inner; private readonly MessageEncoder encoder; public MdasMessageEncoderFactory(MessageEncoderFactory inner) { this.inner = inner; encoder = new MdasMessageEncoder(inner.Encoder); } public override MessageEncoder Encoder => encoder; public override MessageVersion MessageVersion => inner.MessageVersion; } internal sealed class MdasMessageEncoder : MessageEncoder { private const string MdasContentType = "application/x-mdas"; private readonly MessageEncoder inner; public MdasMessageEncoder(MessageEncoder inner) { this.inner = inner; } public override string ContentType => MdasContentType; public override string MediaType => MdasContentType; public override MessageVersion MessageVersion => inner.MessageVersion; public override bool IsContentTypeSupported(string contentType) { return contentType.StartsWith(MdasContentType, StringComparison.OrdinalIgnoreCase) || inner.IsContentTypeSupported(contentType); } public override Message ReadMessage(ArraySegment buffer, BufferManager bufferManager, string contentType) { return inner.ReadMessage(buffer, bufferManager, inner.ContentType); } public override Message ReadMessage(Stream stream, int maxSizeOfHeaders, string contentType) { return inner.ReadMessage(stream, maxSizeOfHeaders, inner.ContentType); } public override void WriteMessage(Message message, Stream stream) { inner.WriteMessage(message, stream); } public override ArraySegment WriteMessage(Message message, int maxMessageSize, BufferManager bufferManager, int messageOffset) { return inner.WriteMessage(message, maxMessageSize, bufferManager, messageOffset); } }