/* ======================================================================== * Copyright (c) 2005-2026 The OPC Foundation, Inc. All rights reserved. * * OPC Foundation MIT License 1.00 * * 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. * * The complete license agreement can be found here: * http://opcfoundation.org/License/MIT/1.00/ * ======================================================================*/ using System; namespace Opc.Ua.Bindings.Pcap.Frame { /// /// Direction of a captured OPC UA chunk relative to the client (the /// peer that initiated the secure channel). /// chunks were sent by the client; chunks /// were sent by the server. The direction is needed for offline /// decryption because client- and server-sent symmetric messages use /// different derived key material. /// public enum CaptureFrameDirection { /// /// Direction not yet determined. /// Unknown = 0, /// /// Sent by the client (uses ClientSigningKey / ClientEncryptingKey). /// ClientToServer = 1, /// /// Sent by the server (uses ServerSigningKey / ServerEncryptingKey). /// ServerToClient = 2 } /// /// A single OPC UA UA-SC binary chunk lifted out of a pcap or in-proc /// tap. Carries the raw wire bytes plus the metadata needed to feed /// the offline decoder (timestamp, direction, peer endpoints). /// /// /// /// The chunk includes the 8-byte OPC UA message header (type + size), /// so a consumer can decide whether it is HEL/ACK/ERR/RHE/OPN/MSG/CLO /// without re-parsing the header out of the byte stream. Multi-chunk /// messages (intermediate + final) are surfaced as multiple /// records; assembly into full messages is /// the job of the offline decoder. /// /// /// is owned by the producer and is generally backed /// by a pooled buffer. Consumers must not mutate it and must not keep /// references after the enclosing pipeline returns. /// /// public readonly struct CaptureFrame : IEquatable { /// /// Constructs a new capture frame. /// public CaptureFrame( DateTimeOffset timestamp, CaptureFrameDirection direction, string clientEndpoint, string serverEndpoint, ReadOnlyMemory data) { Timestamp = timestamp; Direction = direction; ClientEndpoint = clientEndpoint ?? string.Empty; ServerEndpoint = serverEndpoint ?? string.Empty; Data = data; } /// /// The capture timestamp. /// public DateTimeOffset Timestamp { get; } /// /// The direction of the chunk relative to the secure channel. /// public CaptureFrameDirection Direction { get; } /// /// String identifier for the client endpoint, e.g. /// 192.0.2.10:54321. Empty if not available (in-proc tap). /// public string ClientEndpoint { get; } /// /// String identifier for the server endpoint, e.g. /// 192.0.2.20:62541. Empty if not available. /// public string ServerEndpoint { get; } /// /// The raw chunk bytes, including the 8-byte OPC UA message /// header. /// public ReadOnlyMemory Data { get; } /// public bool Equals(CaptureFrame other) { return Timestamp.Equals(other.Timestamp) && Direction == other.Direction && string.Equals(ClientEndpoint, other.ClientEndpoint, StringComparison.Ordinal) && string.Equals(ServerEndpoint, other.ServerEndpoint, StringComparison.Ordinal) && Data.Span.SequenceEqual(other.Data.Span); } /// public override bool Equals(object? obj) { return obj is CaptureFrame other && Equals(other); } /// public override int GetHashCode() { return HashCode.Combine( Timestamp, (int)Direction, ClientEndpoint, ServerEndpoint, Data.Length); } /// /// Equality comparison. /// public static bool operator ==(CaptureFrame left, CaptureFrame right) => left.Equals(right); /// /// Inequality comparison. /// public static bool operator !=(CaptureFrame left, CaptureFrame right) => !left.Equals(right); } }