Skip to content

Commit f091c74

Browse files
fix: Hotfix v4.3.1
1 parent 88d8303 commit f091c74

File tree

5 files changed

+80
-4
lines changed

5 files changed

+80
-4
lines changed

src/CommonLib/Helpers.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,29 @@ public static string DumpDirectoryObject(this IDirectoryObject directoryObject)
317317

318318
return builder.ToString();
319319
}
320+
321+
/// <summary>
322+
/// Attempt an action a number of times, quietly eating a specific exception until the last attempt if it throws.
323+
/// </summary>
324+
/// <param name="action"></param>
325+
/// <param name="retryCount"></param>
326+
/// <param name="logger"></param>
327+
public static async Task RetryOnException<T>(Func<Task> action, int retryCount, ILogger logger = null) where T : Exception {
328+
int attempt = 0;
329+
bool success = false;
330+
do {
331+
try {
332+
await action();
333+
success = true;
334+
}
335+
catch (T e) {
336+
attempt++;
337+
logger?.LogDebug(e, "Exception caught, retrying attempt {Attempt}", attempt);
338+
if (attempt >= retryCount)
339+
throw;
340+
}
341+
} while (!success && attempt < retryCount);
342+
}
320343
}
321344

322345
public class ParsedGPLink {

src/CommonLib/LdapConnectionPool.cs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -972,14 +972,23 @@ private LdapConnection CreateBaseConnection(string directoryIdentifier, bool ssl
972972
return (false, testResult);
973973
}
974974

975-
SearchResponse response;
975+
SearchResponse response = null;
976976
try {
977977
//Do an initial search request to get the rootDSE
978978
//This ldap filter is equivalent to (objectclass=*)
979979
var searchRequest = CreateSearchRequest("", new LdapFilter().AddAllObjects().GetFilter(),
980980
SearchScope.Base, null);
981981

982-
response = await SendRequestWithTimeout(connection, searchRequest, _testConnectionAdaptiveTimeout);
982+
await Helpers.RetryOnException<TimeoutException>(async () => {
983+
response = await SendRequestWithTimeout(connection, searchRequest, _testConnectionAdaptiveTimeout);
984+
}, retryCount: 2, logger: _log);
985+
}
986+
catch (TimeoutException e) {
987+
/*
988+
* We've retried this connection a few times but haven't succeeded
989+
*/
990+
testResult.Message = e.Message;
991+
return (false, testResult);
983992
}
984993
catch (LdapException e) {
985994
/*

src/CommonLib/SharpHoundCommonLib.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
<PackageDescription>Common library for C# BloodHound enumeration tasks</PackageDescription>
1010
<PackageLicenseExpression>GPL-3.0-only</PackageLicenseExpression>
1111
<RepositoryUrl>https://github.com/BloodHoundAD/SharpHoundCommon</RepositoryUrl>
12-
<Version>4.2.8</Version>
12+
<Version>4.3.1</Version>
1313
<AssemblyName>SharpHoundCommonLib</AssemblyName>
1414
<RootNamespace>SharpHoundCommonLib</RootNamespace>
1515
</PropertyGroup>

src/SharpHoundRPC/SharpHoundRPC.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
<Company>SpecterOps</Company>
99
<PackageDescription>SAM/LSA Wrapper for C# BloodHound tasks</PackageDescription>
1010
<PackageLicenseExpression>GPL-3.0-only</PackageLicenseExpression>
11-
<Version>4.2.1</Version>
11+
<Version>4.3.1</Version>
1212
<AssemblyName>SharpHoundRPC</AssemblyName>
1313
<RootNamespace>SharpHoundRPC</RootNamespace>
1414
</PropertyGroup>

test/unit/CommonLibHelperTests.cs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Text;
3+
using System.Threading.Tasks;
34
using SharpHoundCommonLib;
45
using SharpHoundCommonLib.Enums;
56
using Xunit;
@@ -249,5 +250,48 @@ public void ConvertTimestampToUnixEpoch_InvalidTimestamp() {
249250

250251
Assert.Equal(0, result);
251252
}
253+
254+
[Fact]
255+
public async Task RetryOnException_ThrowsExpected() {
256+
int attemptCount = 0;
257+
Func<Task> throws = () => {
258+
attemptCount++;
259+
throw new ApplicationException();
260+
};
261+
262+
await Assert.ThrowsAsync<ApplicationException>(() => Helpers.RetryOnException<ApplicationException>(throws, 3));
263+
Assert.Equal(3, attemptCount);
264+
}
265+
266+
[Fact]
267+
public async Task RetryOnException_ThrowsUnexpected() {
268+
int attemptCount = 0;
269+
Func<Task> throws = () => {
270+
attemptCount++;
271+
throw new Exception();
272+
};
273+
274+
await Assert.ThrowsAsync<Exception>(() => Helpers.RetryOnException<ApplicationException>(throws, 3));
275+
// First try throws an Exception, but retry only happens on ApplicationException
276+
Assert.Equal(1, attemptCount);
277+
}
278+
279+
[Fact]
280+
public async Task RetryOnException_SucceedsOnLastAttempt() {
281+
int attemptCount = 0;
282+
bool success = false;
283+
Func<Task> throws = () => {
284+
attemptCount++;
285+
if (attemptCount < 3)
286+
throw new ApplicationException();
287+
288+
success = true;
289+
return Task.CompletedTask;
290+
};
291+
292+
await Helpers.RetryOnException<ApplicationException>(throws, 3);
293+
294+
Assert.True(success);
295+
}
252296
}
253297
}

0 commit comments

Comments
 (0)