From e1d9c75ae9ab68fc1d54b739d85b4c2d05d54ff3 Mon Sep 17 00:00:00 2001 From: Haga Rak Date: Wed, 15 Apr 2026 17:52:56 +0200 Subject: [PATCH] Fix OOP pcap capture race with archive packaging Dispose the capture scope before PackDirectoryToFile so the subprocess closes its pcapng FileStreams and flushes buffered data to disk. Previously the scope was method-scoped and disposed after packaging, so small captures could sit in the 4KB FileStream buffer and get skipped by ZipHelper's Length==0 check, leaving captures/*.pcapng missing from the fxzy archive. Also flush the BinaryWriter in OutOfProcessCaptureContext.Flush so the flush message actually reaches the subprocess, and tighten the CLI test assertion to Assert.NotNull before draining the capture stream. --- src/Fluxzy.Core.Pcap/OutOfProcessCaptureContext.cs | 1 + src/Fluxzy/Commands/StartCommandBuilder.cs | 8 +++++++- test/Fluxzy.Tests/Cli/CliTestBase.cs | 3 ++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/Fluxzy.Core.Pcap/OutOfProcessCaptureContext.cs b/src/Fluxzy.Core.Pcap/OutOfProcessCaptureContext.cs index 63bf9d4c5..a99e7a360 100644 --- a/src/Fluxzy.Core.Pcap/OutOfProcessCaptureContext.cs +++ b/src/Fluxzy.Core.Pcap/OutOfProcessCaptureContext.cs @@ -124,6 +124,7 @@ public void Flush() lock (this) { _writer.Write((byte) MessageType.Flush); + _writer.Flush(); } } diff --git a/src/Fluxzy/Commands/StartCommandBuilder.cs b/src/Fluxzy/Commands/StartCommandBuilder.cs index 30ec24b44..082bc665e 100644 --- a/src/Fluxzy/Commands/StartCommandBuilder.cs +++ b/src/Fluxzy/Commands/StartCommandBuilder.cs @@ -308,7 +308,11 @@ public async Task Run(InvocationContext invocationContext, CancellationToken pro var uaParserProvider = parseUserAgent ? new UaParserUserAgentInfoProvider() : null; var systemProxyManager = new SystemProxyRegistrationManager(new NativeProxySetterManager().Get()); - await using var scope = new ProxyScope(() => new FluxzyNetOutOfProcessHost(), a => new OutOfProcessCaptureContext(a)); + // Scope owns the out-of-proc capture subprocess lifetime. It must be disposed + // BEFORE PackDirectoryToFile runs so the subprocess closes its pcapng FileStreams + // and flushes all buffered packet data to disk; otherwise small captures can sit + // in the 4 KB FileStream buffer and the packager's Length==0 skip drops them. + await using (var scope = new ProxyScope(() => new FluxzyNetOutOfProcessHost(), a => new OutOfProcessCaptureContext(a))) { if (!ValidateSetting(invocationContext, proxyStartUpSetting)) { invocationContext.ExitCode = 1; @@ -390,6 +394,8 @@ public async Task Run(InvocationContext invocationContext, CancellationToken pro } } + } // scope dispose: subprocess exits, pcapng FileStreams closed + flushed + invocationContext.Console.Out.WriteLine("Proxy ended, gracefully"); if (outFileInfo != null) { diff --git a/test/Fluxzy.Tests/Cli/CliTestBase.cs b/test/Fluxzy.Tests/Cli/CliTestBase.cs index 3660e20d6..54cfa8398 100644 --- a/test/Fluxzy.Tests/Cli/CliTestBase.cs +++ b/test/Fluxzy.Tests/Cli/CliTestBase.cs @@ -132,7 +132,8 @@ protected async Task Run_Cli_Output(string proto, CaptureType rawCap, if (rawCap != CaptureType.None) { var rawCapStream = archiveReader.GetRawCaptureStream(connection.Id); - Assert.True(await rawCapStream!.DrainAsync(disposeStream: true) > 0); + Assert.NotNull(rawCapStream); + Assert.True(await rawCapStream.DrainAsync(disposeStream: true) > 0); } if (rule)