Skip to content

Commit 5bf70ce

Browse files
authored
Fix marshalling of StringBuilder errors (#533)
* Fix marshalling of StringBuilder errors * Move StringBuilder logic to PcapStringBuilderMarshaler * Drop ICustomMarshaler for error string, use ErrorBuffer class instead.ss * Revert csproj changes * Fix build * Cleanup
1 parent 2fff830 commit 5bf70ce

8 files changed

+56
-66
lines changed

Diff for: SharpPcap/LibPcap/CaptureFileReaderDevice.cs

+3-4
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,7 @@ public CaptureFileReaderDevice(string captureFilename)
7272
/// </summary>
7373
public override void Open(DeviceConfiguration configuration)
7474
{
75-
// holds errors
76-
StringBuilder errbuf = new StringBuilder(Pcap.PCAP_ERRBUF_SIZE); //will hold errors
75+
ErrorBuffer errbuf; //will hold errors
7776

7877
PcapHandle adapterHandle;
7978

@@ -82,7 +81,7 @@ public override void Open(DeviceConfiguration configuration)
8281
var resolution = configuration.TimestampResolution ?? TimestampResolution.Microsecond;
8382
if (has_offline_with_tstamp_precision_support)
8483
{
85-
adapterHandle = LibPcapSafeNativeMethods.pcap_open_offline_with_tstamp_precision(m_pcapFile, (uint)resolution, errbuf);
84+
adapterHandle = LibPcapSafeNativeMethods.pcap_open_offline_with_tstamp_precision(m_pcapFile, (uint)resolution, out errbuf);
8685
}
8786
else
8887
{
@@ -97,7 +96,7 @@ public override void Open(DeviceConfiguration configuration)
9796
);
9897
}
9998

100-
adapterHandle = LibPcapSafeNativeMethods.pcap_open_offline(m_pcapFile, errbuf);
99+
adapterHandle = LibPcapSafeNativeMethods.pcap_open_offline(m_pcapFile, out errbuf);
101100
}
102101

103102
// handle error

Diff for: SharpPcap/LibPcap/CaptureHandleReaderDevice.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,14 @@ public CaptureHandleReaderDevice(SafeHandle handle)
4848
public override void Open(DeviceConfiguration configuration)
4949
{
5050
// holds errors
51-
StringBuilder errbuf = new StringBuilder(Pcap.PCAP_ERRBUF_SIZE);
51+
ErrorBuffer errbuf;
5252

5353
var resolution = configuration.TimestampResolution ?? TimestampResolution.Microsecond;
5454
PcapHandle adapterHandle;
5555
try
5656
{
5757
adapterHandle = LibPcapSafeNativeMethods.pcap_open_handle_offline_with_tstamp_precision(
58-
FileHandle, (uint)resolution, errbuf);
58+
FileHandle, (uint)resolution, out errbuf);
5959
}
6060
catch (TypeLoadException ex)
6161
{

Diff for: SharpPcap/LibPcap/LibPcapLiveDevice.cs

+7-11
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ public override void Open(DeviceConfiguration configuration)
102102
// See https://www.tcpdump.org/manpages/pcap_set_immediate_mode.3pcap.html
103103
var mintocopy_supported = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
104104

105-
var errbuf = new StringBuilder(Pcap.PCAP_ERRBUF_SIZE); //will hold errors
105+
ErrorBuffer errbuf; //will hold errors
106106

107107
// set the StopCaptureTimeout value to twice the read timeout to ensure that
108108
// we wait long enough before considering the capture thread to be stuck when stopping
@@ -131,7 +131,7 @@ public override void Open(DeviceConfiguration configuration)
131131
{
132132
Handle = LibPcapSafeNativeMethods.pcap_create(
133133
Name, // name of the device
134-
errbuf); // error buffer
134+
out errbuf); // error buffer
135135

136136
if (Handle.IsInvalid)
137137
{
@@ -171,7 +171,7 @@ public override void Open(DeviceConfiguration configuration)
171171
(short)mode, // flags
172172
(short)configuration.ReadTimeout, // read timeout
173173
ref auth, // authentication
174-
errbuf); // error buffer
174+
out errbuf); // error buffer
175175
}
176176
catch (TypeLoadException)
177177
{
@@ -275,14 +275,12 @@ public bool NonBlockingMode
275275
get
276276
{
277277
ThrowIfNotOpen("Can't get blocking mode, the device is closed");
278-
279-
var errbuf = new StringBuilder(Pcap.PCAP_ERRBUF_SIZE); //will hold errors
280-
int ret = LibPcapSafeNativeMethods.pcap_getnonblock(Handle, errbuf);
278+
int ret = LibPcapSafeNativeMethods.pcap_getnonblock(Handle, out var errbuf);
281279

282280
// Errorbuf is only filled when ret = -1
283281
if (ret == -1)
284282
{
285-
string err = "Unable to set get blocking" + errbuf.ToString();
283+
string err = "Unable to get blocking mode. " + errbuf.ToString();
286284
throw new PcapException(err);
287285
}
288286

@@ -294,18 +292,16 @@ public bool NonBlockingMode
294292
{
295293
ThrowIfNotOpen("Can't set blocking mode, the device is closed");
296294

297-
var errbuf = new StringBuilder(Pcap.PCAP_ERRBUF_SIZE); //will hold errors
298-
299295
int block = disableBlocking;
300296
if (value)
301297
block = enableBlocking;
302298

303-
int ret = LibPcapSafeNativeMethods.pcap_setnonblock(Handle, block, errbuf);
299+
int ret = LibPcapSafeNativeMethods.pcap_setnonblock(Handle, block, out var errbuf);
304300

305301
// Errorbuf is only filled when ret = -1
306302
if (ret == -1)
307303
{
308-
string err = "Unable to set non blocking" + errbuf.ToString();
304+
string err = "Unable to set blocking mode. " + errbuf.ToString();
309305
throw new PcapException(err);
310306
}
311307
}

Diff for: SharpPcap/LibPcap/LibPcapSafeNativeMethods.Encoding.cs

+25-23
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ internal static partial class LibPcapSafeNativeMethods
1515
{
1616

1717
/// <summary>
18-
/// This defaul is good enough for .NET Framework and .NET Core on non Windows with Libpcap default config
18+
/// This default is good enough for .NET Framework and .NET Core on non Windows with Libpcap default config
1919
/// </summary>
20-
private static readonly Encoding StringEncoding = Encoding.Default;
20+
internal static readonly Encoding StringEncoding = Encoding.Default;
2121

2222
private static Encoding ConfigureStringEncoding()
2323
{
@@ -29,9 +29,8 @@ private static Encoding ConfigureStringEncoding()
2929
try
3030
{
3131
// Try to change Libpcap to UTF-8 mode
32-
var errorBuffer = new StringBuilder(Pcap.PCAP_ERRBUF_SIZE);
3332
const uint PCAP_CHAR_ENC_UTF_8 = 1;
34-
var res = pcap_init(PCAP_CHAR_ENC_UTF_8, errorBuffer);
33+
var res = pcap_init(PCAP_CHAR_ENC_UTF_8, out _);
3534
if (res == 0)
3635
{
3736
// We made it
@@ -90,25 +89,11 @@ public IntPtr MarshalManagedToNative(object managedObj)
9089
{
9190
return IntPtr.Zero;
9291
}
93-
byte[] bytes = null;
94-
var byteCount = 0;
95-
if (managedObj is string str)
96-
{
97-
bytes = StringEncoding.GetBytes(str);
98-
byteCount = bytes.Length + 1;
99-
}
100-
101-
if (managedObj is StringBuilder builder)
102-
{
103-
bytes = StringEncoding.GetBytes(builder.ToString());
104-
byteCount = StringEncoding.GetMaxByteCount(builder.Capacity) + 1;
105-
}
106-
107-
if (bytes is null)
108-
{
109-
throw new ArgumentException("The input argument is not a supported type.");
110-
}
111-
var ptr = Marshal.AllocHGlobal(byteCount);
92+
var str = (string)managedObj;
93+
var bytes = StringEncoding.GetBytes(str);
94+
// The problem is that we need a reference to the StringBuilder in MarshalNativeToManaged
95+
// So we get a pointer to it with GCHandle, and put it as prefix of the pointer we return
96+
var ptr = Marshal.AllocHGlobal(bytes.Length + 1);
11297
Marshal.Copy(bytes, 0, ptr, bytes.Length);
11398
// Put zero string termination
11499
Marshal.WriteByte(ptr + bytes.Length, 0);
@@ -131,4 +116,21 @@ public unsafe object MarshalNativeToManaged(IntPtr nativeData)
131116
}
132117
}
133118
}
119+
120+
[StructLayout(LayoutKind.Sequential)]
121+
internal struct ErrorBuffer
122+
{
123+
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
124+
internal byte[] Data;
125+
126+
public override string ToString()
127+
{
128+
var nbBytes = 0;
129+
while (Data[nbBytes] != 0)
130+
{
131+
nbBytes++;
132+
}
133+
return LibPcapSafeNativeMethods.StringEncoding.GetString(Data, 0, nbBytes);
134+
}
135+
}
134136
}

Diff for: SharpPcap/LibPcap/LibPcapSafeNativeMethods.Interop.cs

+11-14
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,8 @@
55
// SPDX-License-Identifier: MIT
66

77
using System;
8-
using System.IO;
9-
using System.Reflection;
108
using System.Runtime.InteropServices;
119
using System.Security;
12-
using System.Text;
1310
using static SharpPcap.LibPcap.PcapUnmanagedStructures;
1411

1512
namespace SharpPcap.LibPcap
@@ -30,21 +27,21 @@ internal static partial class LibPcapSafeNativeMethods
3027
[DllImport(PCAP_DLL, CallingConvention = CallingConvention.Cdecl)]
3128
internal extern static int pcap_init(
3229
uint opts,
33-
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(PcapStringMarshaler))] StringBuilder /* char* */ errbuf
30+
out ErrorBuffer /* char* */ errbuf
3431
);
3532

3633
[DllImport(PCAP_DLL, CallingConvention = CallingConvention.Cdecl)]
3734
internal extern static int pcap_findalldevs(
3835
ref IntPtr /* pcap_if_t** */ alldevs,
39-
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(PcapStringMarshaler))] StringBuilder /* char* */ errbuf
36+
out ErrorBuffer /* char* */ errbuf
4037
);
4138

4239
[DllImport(PCAP_DLL, CallingConvention = CallingConvention.Cdecl)]
4340
internal extern static int pcap_findalldevs_ex(
4441
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(PcapStringMarshaler))] string /*char **/source,
4542
ref pcap_rmtauth /*pcap_rmtauth **/auth,
4643
ref IntPtr /*pcap_if_t ** */alldevs,
47-
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(PcapStringMarshaler))] StringBuilder /*char * */errbuf
44+
out ErrorBuffer /* char* */ errbuf
4845
);
4946

5047
[DllImport(PCAP_DLL, CallingConvention = CallingConvention.Cdecl)]
@@ -57,19 +54,19 @@ internal extern static int pcap_findalldevs_ex(
5754
int flags,
5855
int read_timeout,
5956
ref pcap_rmtauth rmtauth,
60-
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(PcapStringMarshaler))] StringBuilder errbuf
57+
out ErrorBuffer /* char* */ errbuf
6158
);
6259

6360
[DllImport(PCAP_DLL, CallingConvention = CallingConvention.Cdecl)]
6461
internal extern static PcapHandle /* pcap_t* */ pcap_create(
6562
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(PcapStringMarshaler))] string dev,
66-
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(PcapStringMarshaler))] StringBuilder errbuf
63+
out ErrorBuffer /* char* */ errbuf
6764
);
6865

6966
[DllImport(PCAP_DLL, CallingConvention = CallingConvention.Cdecl)]
7067
internal extern static PcapHandle /* pcap_t* */ pcap_open_offline(
7168
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(PcapStringMarshaler))] string/*const char* */ fname,
72-
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(PcapStringMarshaler))] StringBuilder/* char* */ errbuf
69+
out ErrorBuffer /* char* */ errbuf
7370
);
7471

7572
[DllImport(PCAP_DLL, CallingConvention = CallingConvention.Cdecl)]
@@ -187,7 +184,7 @@ internal extern static int pcap_compile(
187184
internal extern static int pcap_setnonblock(
188185
PcapHandle /* pcap_if_t** */ adaptHandle,
189186
int nonblock,
190-
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(PcapStringMarshaler))] StringBuilder /* char* */ errbuf
187+
out ErrorBuffer /* char* */ errbuf
191188
);
192189

193190
/// <summary>
@@ -196,7 +193,7 @@ internal extern static int pcap_setnonblock(
196193
[DllImport(PCAP_DLL, CallingConvention = CallingConvention.Cdecl)]
197194
internal extern static int pcap_getnonblock(
198195
PcapHandle /* pcap_if_t** */ adaptHandle,
199-
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(PcapStringMarshaler))] StringBuilder /* char* */ errbuf
196+
out ErrorBuffer /* char* */ errbuf
200197
);
201198

202199
/// <summary>
@@ -422,7 +419,7 @@ internal extern static int pcap_tstamp_type_name_to_val(
422419
internal extern static PcapHandle /* pcap_t* */ pcap_open_offline_with_tstamp_precision(
423420
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(PcapStringMarshaler))] string /* const char* */ fname,
424421
uint precision,
425-
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(PcapStringMarshaler))] StringBuilder /* char* */ errbuf
422+
out ErrorBuffer /* char* */ errbuf
426423
);
427424

428425
/// <summary>
@@ -488,7 +485,7 @@ internal extern static int pcap_tstamp_type_name_to_val(
488485
internal extern static IntPtr /* pcap_t* */ _pcap_hopen_offline_with_tstamp_precision(
489486
SafeHandle handle,
490487
uint precision,
491-
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(PcapStringMarshaler))] StringBuilder /* char* */ errbuf
488+
out ErrorBuffer /* char* */ errbuf
492489
);
493490

494491
/// <summary>
@@ -503,7 +500,7 @@ internal extern static int pcap_tstamp_type_name_to_val(
503500
internal extern static IntPtr /* pcap_t* */ _pcap_fopen_offline_with_tstamp_precision(
504501
SafeHandle fileObject,
505502
uint precision,
506-
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(PcapStringMarshaler))] StringBuilder /* char* */ errbuf
503+
out ErrorBuffer /* char* */ errbuf
507504
);
508505
}
509506
}

Diff for: SharpPcap/LibPcap/LibPcapSafeNativeMethods.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -93,11 +93,11 @@ internal static int pcap_get_tstamp_precision(PcapHandle /* pcap_t* p */ adapter
9393
/// <param name="errbuf">Buffer that will receive an error description if an error occurs.</param>
9494
/// <returns></returns>
9595
internal static PcapHandle pcap_open_handle_offline_with_tstamp_precision(
96-
SafeHandle handle, uint precision, StringBuilder errbuf)
96+
SafeHandle handle, uint precision, out ErrorBuffer errbuf)
9797
{
9898
var pointer = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
99-
? _pcap_hopen_offline_with_tstamp_precision(handle, precision, errbuf)
100-
: _pcap_fopen_offline_with_tstamp_precision(handle, precision, errbuf);
99+
? _pcap_hopen_offline_with_tstamp_precision(handle, precision, out errbuf)
100+
: _pcap_fopen_offline_with_tstamp_precision(handle, precision, out errbuf);
101101
if (pointer == IntPtr.Zero)
102102
{
103103
return PcapHandle.Invalid;

Diff for: SharpPcap/LibPcap/PcapInterface.cs

+5-8
Original file line numberDiff line numberDiff line change
@@ -176,15 +176,14 @@ static public IReadOnlyList<PcapInterface> GetAllPcapInterfaces(IPEndPoint sourc
176176
static public IReadOnlyList<PcapInterface> GetAllPcapInterfaces(string source, RemoteAuthentication credentials)
177177
{
178178
var devicePtr = IntPtr.Zero;
179-
var errorBuffer = new StringBuilder(Pcap.PCAP_ERRBUF_SIZE);
180179
var auth = RemoteAuthentication.CreateAuth(credentials);
181180

182181
try
183182
{
184-
var result = LibPcapSafeNativeMethods.pcap_findalldevs_ex(source, ref auth, ref devicePtr, errorBuffer);
183+
var result = LibPcapSafeNativeMethods.pcap_findalldevs_ex(source, ref auth, ref devicePtr, out var errbuf);
185184
if (result < 0)
186185
{
187-
throw new PcapException(errorBuffer.ToString());
186+
throw new PcapException(errbuf.ToString());
188187
}
189188
}
190189
catch (TypeLoadException ex)
@@ -206,12 +205,11 @@ static public IReadOnlyList<PcapInterface> GetAllPcapInterfaces(string source, R
206205
static public IReadOnlyList<PcapInterface> GetAllPcapInterfaces()
207206
{
208207
var devicePtr = IntPtr.Zero;
209-
var errorBuffer = new StringBuilder(Pcap.PCAP_ERRBUF_SIZE);
210208

211-
int result = LibPcapSafeNativeMethods.pcap_findalldevs(ref devicePtr, errorBuffer);
209+
int result = LibPcapSafeNativeMethods.pcap_findalldevs(ref devicePtr, out var errbuf);
212210
if (result < 0)
213211
{
214-
throw new PcapException(errorBuffer.ToString());
212+
throw new PcapException(errbuf.ToString());
215213
}
216214
var pcapInterfaces = GetAllPcapInterfaces(devicePtr, null);
217215

@@ -260,8 +258,7 @@ public System.Collections.Generic.List<PcapClock> TimestampsSupported
260258
{
261259
get
262260
{
263-
StringBuilder errbuf = new StringBuilder(Pcap.PCAP_ERRBUF_SIZE); //will hold errors
264-
using (var handle = LibPcapSafeNativeMethods.pcap_create(Name, errbuf))
261+
using (var handle = LibPcapSafeNativeMethods.pcap_create(Name, out var errbuf))
265262
{
266263

267264
IntPtr typePtr = IntPtr.Zero;

Diff for: SharpPcap/Pcap.cs

-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ public class Pcap
2121
/* interface is loopback */
2222
internal const uint PCAP_IF_LOOPBACK = 0x00000001;
2323
internal const int MAX_PACKET_SIZE = 65536;
24-
internal const int PCAP_ERRBUF_SIZE = 256;
2524

2625
// Constants for address families
2726
// These are set in a Pcap static initializer because the values

0 commit comments

Comments
 (0)