Files
histsdk/tools/AVEVA.Historian.NetFxWcfProbe/Program.cs
T
dohertj2 c95824a65d Initial commit: managed .NET 10 AVEVA Historian SDK + reverse-engineering toolkit
Full read-only SDK (src/AVEVA.Historian.Client) implementing the CLAUDE.md required
surface against AVEVA Historian's binary WCF protocol — no native AVEVA runtime
dependency. All operations live-verified against a local Historian:

- ProbeAsync, ReadRawAsync, ReadAggregateAsync, ReadAtTimeAsync, ReadEventsAsync
- BrowseTagNamesAsync, GetTagMetadataAsync (17 native data-type codes mapped)
- GetConnectionStatusAsync, GetStoreForwardStatusAsync, GetSystemParameterAsync
- 108/108 unit + integration tests pass

Includes the reverse-engineering toolkit (tools/AVEVA.Historian.ReverseEngineering)
used to decode the protocol: WCF probes, IL inspection via dnlib, and IL-rewrite
instrumentation (instrument-wcf-{write,read}message etc.) plus the .NET Framework
trace harness (tools/AVEVA.Historian.NativeTraceHarness) for parity testing.

Sanitized handoff evidence under docs/reverse-engineering/. Native AVEVA binaries
(current/, aveva-install-x64/, aveva-install-x86/) are gitignored — fetch separately
from the AVEVA installer.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 06:31:48 -04:00

881 lines
33 KiB
C#

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<byte>();
List<string> 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<byte>();
errorBuffer ??= Array.Empty<byte>();
NativeError? nativeError = TryReadNativeError(errorBuffer);
bool serverContinue = serverOutput.Length > 0 && serverOutput[0] != 0;
byte[] serverToken = serverContinue && serverOutput.Length > 1
? serverOutput.Skip(1).ToArray()
: Array.Empty<byte>();
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<IHistoryServiceContract2> factory = new(BuildMdasPipeBinding(maxBufferSize), new EndpointAddress(endpoint));
return factory.CreateChannel();
}
private static IRetrievalServiceContract2 CreateRetrievalPipeChannel(string endpoint, int maxBufferSize)
{
ChannelFactory<IRetrievalServiceContract2> 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<byte>();
open2Err ??= Array.Empty<byte>();
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<byte>();
errorBuffer ??= Array.Empty<byte>();
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<SecBuffer>(desc.pBuffers);
if (buffer.cbBuffer == 0 || buffer.pvBuffer == IntPtr.Zero)
{
return Array.Empty<byte>();
}
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<SecBuffer>());
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<SecBuffer>());
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<SecBuffer>(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<TChannel> BuildChannelFactory<TChannel>(BindingContext context)
{
context.BindingParameters.Add(this);
return context.BuildInnerChannelFactory<TChannel>();
}
public override bool CanBuildChannelFactory<TChannel>(BindingContext context)
{
context.BindingParameters.Add(this);
return context.CanBuildInnerChannelFactory<TChannel>();
}
public override T GetProperty<T>(BindingContext context)
{
return inner.GetProperty<T>(context) ?? context.GetInnerProperty<T>();
}
}
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<byte> 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<byte> WriteMessage(Message message, int maxMessageSize, BufferManager bufferManager, int messageOffset)
{
return inner.WriteMessage(message, maxMessageSize, bufferManager, messageOffset);
}
}