Initial project state: .NET reference, design, Rust port (M0+M1), evidence
rust / build / test / clippy / fmt (push) Has been cancelled
rust / build / test / clippy / fmt (push) Has been cancelled
Layout:
- src/ .NET 10 x64 reference: MxNativeCodec, MxNativeClient,
MxAsbClient, probes, tests, harnesses. Executable spec.
- design/ Architectural plan for the Rust port (M0–M6), error
model, protocol invariants, risks (R1–R16), adversarial
review log (review.md).
- rust/ Rust workspace. M0 skeleton + M1 codec parity.
mxaccess-codec: 215 unit tests + 2 cross-implementation
parity tests (byte-identical against .NET reference).
Other crates are M0 stubs awaiting M2+.
- captures/ Frida + netsh + pcap evidence per CLAUDE.md
("captures are evidence, not throwaway logs").
- analysis/ Decompiled C# (frida/proxy/decompiled-*),
Ghidra exports for native DLLs (`exports/` only —
working state at `projects/` and AVEVA's input
binaries at `input/` are gitignored).
- docs/ Reverse-engineering reference docs.
- tools/ Setup-LiveProbeEnv.ps1 (Infisical credential fetcher),
Compute-Crc.ps1 (.NET parity helper).
- .github/workflows/ Rust CI: fmt + build + test + clippy on Windows.
- LICENSE MIT (Joseph Doherty, 2026).
Verified:
- cargo test --workspace → 217 passed (215 unit + 2 .NET parity), 0 failed
- cargo clippy --workspace -- -D warnings → clean
- cargo fmt --all -- --check → clean
- cargo publish --dry-run -p mxaccess-codec → packages cleanly
Excluded from history (see .gitignore):
- **/bin, **/obj, **/target — build artifacts
- analysis/ghidra/projects/ — Ghidra working state (regenerable)
- analysis/ghidra/input/ — AVEVA proprietary DLLs (vendor IP)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+52
@@ -0,0 +1,52 @@
|
||||
using System;
|
||||
using System.IO.Pipes;
|
||||
using System.Text;
|
||||
|
||||
namespace ArchestrAServices.Common.Resolution;
|
||||
|
||||
public class PipeClient : IDisposable
|
||||
{
|
||||
private string _pipeName;
|
||||
|
||||
private NamedPipeClientStream _pipeClient;
|
||||
|
||||
private bool disposed;
|
||||
|
||||
public PipeClient(string PipeName)
|
||||
{
|
||||
_pipeName = PipeName;
|
||||
_pipeClient = new NamedPipeClientStream(".", _pipeName, PipeDirection.Out);
|
||||
}
|
||||
|
||||
public bool SendString(string Data)
|
||||
{
|
||||
_pipeClient.Connect(5000);
|
||||
byte[] bytes = Encoding.UTF8.GetBytes(Data);
|
||||
_pipeClient.Write(bytes, 0, bytes.Length);
|
||||
_pipeClient.WaitForPipeDrain();
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(freeManagedObjectsAlso: true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool freeManagedObjectsAlso)
|
||||
{
|
||||
if (!disposed)
|
||||
{
|
||||
if (freeManagedObjectsAlso)
|
||||
{
|
||||
_pipeClient.Dispose();
|
||||
}
|
||||
disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
~PipeClient()
|
||||
{
|
||||
Dispose(freeManagedObjectsAlso: false);
|
||||
}
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
using System;
|
||||
using System.IO.Pipes;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
namespace ArchestrAServices.Common.Resolution;
|
||||
|
||||
public class PipeServer : IDisposable
|
||||
{
|
||||
private string _pipeName;
|
||||
|
||||
private NamedPipeServerStream _pipeServer;
|
||||
|
||||
private bool disposed;
|
||||
|
||||
public PipeServer(string PipeName)
|
||||
{
|
||||
_pipeName = PipeName;
|
||||
_pipeServer = new NamedPipeServerStream(_pipeName, PipeDirection.In, 1, PipeTransmissionMode.Message);
|
||||
}
|
||||
|
||||
public string ReadString()
|
||||
{
|
||||
_pipeServer.WaitForConnection();
|
||||
string result = string.Empty;
|
||||
if (_pipeServer.IsConnected)
|
||||
{
|
||||
while (!_pipeServer.IsMessageComplete)
|
||||
{
|
||||
Thread.Sleep(50);
|
||||
}
|
||||
byte[] array = new byte[100];
|
||||
int num = _pipeServer.Read(array, 0, 100);
|
||||
if (num > 0)
|
||||
{
|
||||
result = Encoding.UTF8.GetString(array, 0, num);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(freeManagedObjectsAlso: true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool freeManagedObjectsAlso)
|
||||
{
|
||||
if (!disposed)
|
||||
{
|
||||
if (freeManagedObjectsAlso)
|
||||
{
|
||||
_pipeServer.Dispose();
|
||||
}
|
||||
disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
~PipeServer()
|
||||
{
|
||||
Dispose(freeManagedObjectsAlso: false);
|
||||
}
|
||||
}
|
||||
+182
@@ -0,0 +1,182 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Net.Sockets;
|
||||
using System.ServiceModel.Discovery;
|
||||
|
||||
namespace ArchestrAServices.Common.Resolution;
|
||||
|
||||
public class Resolution
|
||||
{
|
||||
private static string MostRecentIPAddressFromExe;
|
||||
|
||||
[Obsolete("Use ArchestrAServices.Common.HostedService.Resolution.GetDiscoveryEndpointMetadata() instead.")]
|
||||
public static EndpointDiscoveryMetadata GetDiscoveryEndpointMetada(EndpointDiscoveryMetadata metaData)
|
||||
{
|
||||
string errorMessage;
|
||||
return GetDiscoveryEndpointMetada(metaData, out errorMessage);
|
||||
}
|
||||
|
||||
[Obsolete("Use ArchestrAServices.Common.HostedService.Resolution.GetDiscoveryEndpointMetadata() instead.")]
|
||||
public static EndpointDiscoveryMetadata GetDiscoveryEndpointMetada(EndpointDiscoveryMetadata metaData, out string errorMessage)
|
||||
{
|
||||
errorMessage = string.Empty;
|
||||
try
|
||||
{
|
||||
if (metaData != null)
|
||||
{
|
||||
string[] array = null;
|
||||
NetworkInterface[] array2 = null;
|
||||
try
|
||||
{
|
||||
array2 = NetworkInterface.GetAllNetworkInterfaces();
|
||||
}
|
||||
catch (NetworkInformationException)
|
||||
{
|
||||
array2 = null;
|
||||
}
|
||||
if (array2 != null)
|
||||
{
|
||||
List<string> list = new List<string>();
|
||||
NetworkInterface[] array3 = array2;
|
||||
foreach (NetworkInterface networkInterface in array3)
|
||||
{
|
||||
if (networkInterface.NetworkInterfaceType != NetworkInterfaceType.Ethernet)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
foreach (UnicastIPAddressInformation unicastAddress in networkInterface.GetIPProperties().UnicastAddresses)
|
||||
{
|
||||
if (unicastAddress.Address.AddressFamily == AddressFamily.InterNetwork && unicastAddress.SuffixOrigin != SuffixOrigin.LinkLayerAddress)
|
||||
{
|
||||
if (unicastAddress.SuffixOrigin == SuffixOrigin.Manual || unicastAddress.SuffixOrigin == SuffixOrigin.OriginDhcp)
|
||||
{
|
||||
list.Insert(0, unicastAddress.Address.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
list.Add(unicastAddress.Address.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
list.Add(Environment.MachineName);
|
||||
array = list.ToArray();
|
||||
}
|
||||
UriBuilder uriBuilder = new UriBuilder(metaData.Address.Uri);
|
||||
if (uriBuilder != null && !string.IsNullOrEmpty(uriBuilder.Host))
|
||||
{
|
||||
metaData.ListenUris.Clear();
|
||||
string[] array4 = array;
|
||||
foreach (string host in array4)
|
||||
{
|
||||
uriBuilder.Host = host;
|
||||
Uri uri = uriBuilder.Uri;
|
||||
metaData.ListenUris.Add(uri);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex2)
|
||||
{
|
||||
errorMessage = "GetDiscoveryEndpointMetada: Exception " + ex2.Message;
|
||||
}
|
||||
return metaData;
|
||||
}
|
||||
|
||||
[Obsolete("Legacy method. No longer supported due to deprecated Win32 methods being used.")]
|
||||
public static string ResolveIPAddress(string HostOrIP, out string errorMessage)
|
||||
{
|
||||
string empty = string.Empty;
|
||||
errorMessage = string.Empty;
|
||||
if (!Environment.Is64BitProcess)
|
||||
{
|
||||
try
|
||||
{
|
||||
empty = ResolveLocalIPAddr(HostOrIP, out errorMessage);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
empty = string.Empty;
|
||||
errorMessage = "ResolveIPAddress caught exception: " + ex.Message;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (string.IsNullOrEmpty(MostRecentIPAddressFromExe))
|
||||
{
|
||||
string text = Guid.NewGuid().ToString() + "_Resolution";
|
||||
using PipeServer pipeServer = new PipeServer(text);
|
||||
if (File.Exists("aaASBResolveIP.exe"))
|
||||
{
|
||||
try
|
||||
{
|
||||
Process.Start("aaASBResolveIP.exe", text + " " + HostOrIP);
|
||||
MostRecentIPAddressFromExe = pipeServer.ReadString();
|
||||
}
|
||||
catch (Exception ex2)
|
||||
{
|
||||
errorMessage = $"ResolveIPAddress called in 64-bit process - exception starting or reading from helper process aaASBResolveIP.exe: {ex2.Message}";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
errorMessage = "ResolveIPAddress called in 64-bit process - could not find helper process aaASBResolveIP.exe, no resolution";
|
||||
}
|
||||
}
|
||||
empty = MostRecentIPAddressFromExe;
|
||||
}
|
||||
return empty;
|
||||
}
|
||||
|
||||
private unsafe static string ResolveLocalIPAddr(string HostOrIP, out string errorMessage)
|
||||
{
|
||||
WinSock.WSAData lpWSAData;
|
||||
int num = WinSock.WSAStartup(514, out lpWSAData);
|
||||
string result = string.Empty;
|
||||
errorMessage = "Unknown failure in ResolveLocalIPAddr";
|
||||
if (num == 0)
|
||||
{
|
||||
int addr = WinSock.inet_addr(HostOrIP);
|
||||
WinSock.hostent* ptr = null;
|
||||
if (addr != -1)
|
||||
{
|
||||
int len = 4;
|
||||
ptr = WinSock.gethostbyaddr(ref addr, len, ProtocolFamily.InterNetwork);
|
||||
if (ptr == null)
|
||||
{
|
||||
result = string.Empty;
|
||||
errorMessage = "ResolveLocalIPAddr: call to gethostbyaddr for IP address " + HostOrIP + " failed to resolve a hostent struct";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ptr = WinSock.gethostbyname(HostOrIP);
|
||||
if (ptr == null)
|
||||
{
|
||||
result = string.Empty;
|
||||
errorMessage = "ResolveLocalIPAddr: call to gethostbyname for host name " + HostOrIP + " failed to resolve a hostent struct";
|
||||
}
|
||||
}
|
||||
if (ptr != null)
|
||||
{
|
||||
byte* h_addr_list = *ptr->h_addr_list;
|
||||
int num2 = *h_addr_list;
|
||||
int num3 = h_addr_list[1];
|
||||
int num4 = h_addr_list[2];
|
||||
int num5 = h_addr_list[3];
|
||||
result = $"{num2}.{num3}.{num4}.{num5}";
|
||||
errorMessage = string.Empty;
|
||||
}
|
||||
WinSock.WSACleanup();
|
||||
}
|
||||
else
|
||||
{
|
||||
result = string.Empty;
|
||||
errorMessage = "ResolveLocalIPAddr: unable to initialize WinSock, WSAStartup returned error";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
namespace ArchestrAServices.Common.Resolution;
|
||||
|
||||
internal static class Validation
|
||||
{
|
||||
internal static string IPAddressRegex = "^([1-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(\\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3}$";
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
using System;
|
||||
using System.Net.Sockets;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security;
|
||||
|
||||
namespace ArchestrAServices.Common.Resolution;
|
||||
|
||||
[SuppressUnmanagedCodeSecurity]
|
||||
internal class WinSock
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct WSAData
|
||||
{
|
||||
[MarshalAs(UnmanagedType.U2)]
|
||||
public ushort wVersion;
|
||||
|
||||
[MarshalAs(UnmanagedType.U2)]
|
||||
public ushort wHighVersion;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 257)]
|
||||
private byte[] szDescription;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 129)]
|
||||
private byte[] szSystemStatus;
|
||||
|
||||
[MarshalAs(UnmanagedType.U2)]
|
||||
public ushort iMaxSockets;
|
||||
|
||||
[MarshalAs(UnmanagedType.U2)]
|
||||
public ushort iMaxUdpDg;
|
||||
|
||||
[MarshalAs(UnmanagedType.LPStr)]
|
||||
public string lpVendorInfo;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct hostent
|
||||
{
|
||||
public IntPtr h_name;
|
||||
|
||||
public IntPtr h_aliases;
|
||||
|
||||
[MarshalAs(UnmanagedType.I2)]
|
||||
public short h_addrtype;
|
||||
|
||||
[MarshalAs(UnmanagedType.I2)]
|
||||
public short h_length;
|
||||
|
||||
public unsafe byte** h_addr_list;
|
||||
}
|
||||
|
||||
internal const uint INADDR_NONE = uint.MaxValue;
|
||||
|
||||
[DllImport("ws2_32", CharSet = CharSet.Ansi, SetLastError = true)]
|
||||
internal static extern int WSAStartup([In][MarshalAs(UnmanagedType.U2)] ushort wVersionRequested, [MarshalAs(UnmanagedType.Struct)] out WSAData lpWSAData);
|
||||
|
||||
[DllImport("ws2_32", CharSet = CharSet.Ansi, SetLastError = true)]
|
||||
internal static extern int WSACleanup();
|
||||
|
||||
[DllImport("ws2_32", CharSet = CharSet.Ansi, SetLastError = true)]
|
||||
internal unsafe static extern hostent* gethostbyaddr([In][MarshalAs(UnmanagedType.I4)] ref int addr, [In][MarshalAs(UnmanagedType.I4)] int len, [In] ProtocolFamily type);
|
||||
|
||||
[DllImport("ws2_32", BestFitMapping = false, CharSet = CharSet.Ansi, SetLastError = true)]
|
||||
internal unsafe static extern hostent* gethostbyname([In] string host);
|
||||
|
||||
[DllImport("ws2_32", BestFitMapping = false, CharSet = CharSet.Ansi, SetLastError = true)]
|
||||
internal static extern int inet_addr([In] string cp);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Versioning;
|
||||
using System.Security;
|
||||
using System.Security.Permissions;
|
||||
|
||||
[assembly: AssemblyCompany("AVEVA Software, LLC")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2020 AVEVA Group plc and its subsidiaries. All rights reserved.")]
|
||||
[assembly: AssemblyProduct("ArchestrA Data Store")]
|
||||
[assembly: AssemblyFileVersion("2019.4.20078.1")]
|
||||
[assembly: AssemblyInformationalVersion("4.4.6")]
|
||||
[assembly: AssemblyTitle("ArchestrAServices.Common.Resolution")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: ComVisible(false)]
|
||||
[assembly: Guid("8a59d007-5515-4d89-b34c-aecf3d1fb35e")]
|
||||
[assembly: AssemblyTrademark("Refer to: https://sw.aveva.com/legal/trademarks")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
@@ -0,0 +1,19 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<AssemblyName>aaServicesCommonResolution</AssemblyName>
|
||||
<GenerateAssemblyInfo>False</GenerateAssemblyInfo>
|
||||
<TargetFramework>net40</TargetFramework>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<LangVersion>14.0</LangVersion>
|
||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||
<CheckForOverflowUnderflow>False</CheckForOverflowUnderflow>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup />
|
||||
<ItemGroup />
|
||||
<ItemGroup>
|
||||
<Reference Include="System.ServiceModel.Discovery" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.ServiceModel" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
Reference in New Issue
Block a user