| | 1 | | using System; |
| | 2 | | using System.Collections.Generic; |
| | 3 | | using System.DirectoryServices; |
| | 4 | | using System.Linq; |
| | 5 | | using System.Security.Principal; |
| | 6 | | using System.Threading; |
| | 7 | | using System.Threading.Tasks; |
| | 8 | | using Microsoft.Extensions.Logging; |
| | 9 | | using SharpHoundCommonLib.Enums; |
| | 10 | |
|
| | 11 | | namespace SharpHoundCommonLib |
| | 12 | | { |
| | 13 | | public static class Extensions |
| | 14 | | { |
| | 15 | | private static readonly ILogger Log; |
| | 16 | |
|
| | 17 | | static Extensions() |
| 1 | 18 | | { |
| 1 | 19 | | Log = Logging.LogProvider.CreateLogger("Extensions"); |
| 1 | 20 | | } |
| | 21 | |
|
| | 22 | | public static async Task<List<T>> ToListAsync<T>(this IAsyncEnumerable<T> items) |
| 4 | 23 | | { |
| 4 | 24 | | if (items == null) { |
| 0 | 25 | | return new List<T>(); |
| | 26 | | } |
| 4 | 27 | | var results = new List<T>(); |
| 16 | 28 | | await foreach (var item in items |
| 4 | 29 | | .ConfigureAwait(false)) |
| 2 | 30 | | results.Add(item); |
| 4 | 31 | | return results; |
| 4 | 32 | | } |
| | 33 | |
|
| | 34 | | public static async Task<T[]> ToArrayAsync<T>(this IAsyncEnumerable<T> items) |
| 0 | 35 | | { |
| 0 | 36 | | if (items == null) { |
| 0 | 37 | | return Array.Empty<T>(); |
| | 38 | | } |
| 0 | 39 | | var results = new List<T>(); |
| 0 | 40 | | await foreach (var item in items |
| 0 | 41 | | .ConfigureAwait(false)) |
| 0 | 42 | | results.Add(item); |
| 0 | 43 | | return results.ToArray(); |
| 0 | 44 | | } |
| | 45 | |
|
| | 46 | | public static async Task<T> FirstOrDefaultAsync<T>(this IAsyncEnumerable<T> source, |
| 10 | 47 | | CancellationToken cancellationToken = default) { |
| 10 | 48 | | if (source == null) { |
| 0 | 49 | | return default; |
| | 50 | | } |
| | 51 | |
|
| 20 | 52 | | await using (var enumerator = source.GetAsyncEnumerator(cancellationToken)) { |
| 10 | 53 | | var first = await enumerator.MoveNextAsync() ? enumerator.Current : default; |
| 10 | 54 | | return first; |
| | 55 | | } |
| 10 | 56 | | } |
| | 57 | |
|
| | 58 | | public static async Task<T> FirstOrDefaultAsync<T>(this IAsyncEnumerable<T> source, T defaultValue, |
| 1 | 59 | | CancellationToken cancellationToken = default) { |
| 1 | 60 | | if (source == null) { |
| 0 | 61 | | return defaultValue; |
| | 62 | | } |
| | 63 | |
|
| 2 | 64 | | await using (var enumerator = source.GetAsyncEnumerator(cancellationToken)) { |
| 1 | 65 | | var first = await enumerator.MoveNextAsync() ? enumerator.Current : defaultValue; |
| 1 | 66 | | return first; |
| | 67 | | } |
| 1 | 68 | | } |
| | 69 | |
|
| | 70 | | public static IAsyncEnumerable<T> DefaultIfEmpty<T>(this IAsyncEnumerable<T> source, |
| 8 | 71 | | T defaultValue, CancellationToken cancellationToken = default) { |
| 8 | 72 | | return new DefaultIfEmptyAsyncEnumerable<T>(source, defaultValue); |
| 8 | 73 | | } |
| | 74 | |
|
| | 75 | | private sealed class DefaultIfEmptyAsyncEnumerable<T> : IAsyncEnumerable<T> { |
| | 76 | | private readonly DefaultIfEmptyAsyncEnumerator<T> _enumerator; |
| 16 | 77 | | public DefaultIfEmptyAsyncEnumerable(IAsyncEnumerable<T> source, T defaultValue) { |
| 8 | 78 | | _enumerator = new DefaultIfEmptyAsyncEnumerator<T>(source, defaultValue); |
| 8 | 79 | | } |
| 8 | 80 | | public IAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = new CancellationToken()) |
| 8 | 81 | | return _enumerator; |
| 8 | 82 | | } |
| | 83 | | } |
| | 84 | |
|
| | 85 | | private sealed class DefaultIfEmptyAsyncEnumerator<T> : IAsyncEnumerator<T> { |
| | 86 | | private readonly IAsyncEnumerable<T> _source; |
| | 87 | | private readonly T _defaultValue; |
| | 88 | | private T _current; |
| | 89 | | private bool _enumeratorDisposed; |
| | 90 | |
|
| | 91 | | private IAsyncEnumerator<T> _enumerator; |
| | 92 | |
|
| 16 | 93 | | public DefaultIfEmptyAsyncEnumerator(IAsyncEnumerable<T> source, T defaultValue) { |
| 8 | 94 | | _source = source; |
| 8 | 95 | | _defaultValue = defaultValue; |
| 8 | 96 | | } |
| | 97 | |
|
| 13 | 98 | | public async ValueTask DisposeAsync() { |
| 13 | 99 | | _enumeratorDisposed = true; |
| 21 | 100 | | if (_enumerator != null) { |
| 8 | 101 | | await _enumerator.DisposeAsync().ConfigureAwait(false); |
| 8 | 102 | | _enumerator = null; |
| 8 | 103 | | } |
| 13 | 104 | | } |
| | 105 | |
|
| 9 | 106 | | public async ValueTask<bool> MoveNextAsync() { |
| 10 | 107 | | if (_enumeratorDisposed) { |
| 1 | 108 | | return false; |
| | 109 | | } |
| 8 | 110 | | _enumerator ??= _source.GetAsyncEnumerator(); |
| | 111 | |
|
| 10 | 112 | | if (await _enumerator.MoveNextAsync().ConfigureAwait(false)) { |
| 2 | 113 | | _current = _enumerator.Current; |
| 2 | 114 | | return true; |
| | 115 | | } |
| | 116 | |
|
| 6 | 117 | | _current = _defaultValue; |
| 6 | 118 | | await DisposeAsync().ConfigureAwait(false); |
| 6 | 119 | | return true; |
| 9 | 120 | | } |
| | 121 | |
|
| 8 | 122 | | public T Current => _current; |
| | 123 | | } |
| | 124 | |
|
| 0 | 125 | | internal static IAsyncEnumerable<T> ToAsyncEnumerable<T>(this IEnumerable<T> source) { |
| 0 | 126 | | return source switch { |
| 0 | 127 | | ICollection<T> collection => new IAsyncEnumerableCollectionAdapter<T>(collection), |
| 0 | 128 | | _ => null |
| 0 | 129 | | }; |
| 0 | 130 | | } |
| | 131 | |
|
| | 132 | | private sealed class IAsyncEnumerableCollectionAdapter<T> : IAsyncEnumerable<T> { |
| | 133 | | private readonly IAsyncEnumerator<T> _enumerator; |
| | 134 | |
|
| 0 | 135 | | public IAsyncEnumerableCollectionAdapter(ICollection<T> source) { |
| 0 | 136 | | _enumerator = new IAsyncEnumeratorCollectionAdapter<T>(source); |
| 0 | 137 | | } |
| 0 | 138 | | public IAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = new CancellationToken()) |
| 0 | 139 | | return _enumerator; |
| 0 | 140 | | } |
| | 141 | | } |
| | 142 | |
|
| | 143 | | private sealed class IAsyncEnumeratorCollectionAdapter<T> : IAsyncEnumerator<T> { |
| | 144 | | private readonly IEnumerable<T> _source; |
| | 145 | | private IEnumerator<T> _enumerator; |
| | 146 | |
|
| 0 | 147 | | public IAsyncEnumeratorCollectionAdapter(ICollection<T> source) { |
| 0 | 148 | | _source = source; |
| 0 | 149 | | } |
| | 150 | |
|
| 0 | 151 | | public ValueTask DisposeAsync() { |
| 0 | 152 | | _enumerator = null; |
| 0 | 153 | | return new ValueTask(Task.CompletedTask); |
| 0 | 154 | | } |
| | 155 | |
|
| 0 | 156 | | public ValueTask<bool> MoveNextAsync() { |
| 0 | 157 | | if (_enumerator == null) { |
| 0 | 158 | | _enumerator = _source.GetEnumerator(); |
| 0 | 159 | | } |
| 0 | 160 | | return new ValueTask<bool>(_enumerator.MoveNext()); |
| 0 | 161 | | } |
| | 162 | |
|
| 0 | 163 | | public T Current => _enumerator.Current; |
| | 164 | | } |
| | 165 | |
|
| | 166 | |
|
| | 167 | | public static string LdapValue(this SecurityIdentifier s) |
| 0 | 168 | | { |
| 0 | 169 | | var bytes = new byte[s.BinaryLength]; |
| 0 | 170 | | s.GetBinaryForm(bytes, 0); |
| | 171 | |
|
| 0 | 172 | | var output = $"\\{BitConverter.ToString(bytes).Replace('-', '\\')}"; |
| 0 | 173 | | return output; |
| 0 | 174 | | } |
| | 175 | |
|
| | 176 | | public static string LdapValue(this Guid s) |
| 0 | 177 | | { |
| 0 | 178 | | var bytes = s.ToByteArray(); |
| 0 | 179 | | var output = $"\\{BitConverter.ToString(bytes).Replace('-', '\\')}"; |
| 0 | 180 | | return output; |
| 0 | 181 | | } |
| | 182 | |
|
| | 183 | | /// <summary> |
| | 184 | | /// Returns true if any computer collection methods are set |
| | 185 | | /// </summary> |
| | 186 | | /// <param name="methods"></param> |
| | 187 | | /// <returns></returns> |
| 0 | 188 | | public static bool IsComputerCollectionSet(this CollectionMethod methods) { |
| | 189 | | const CollectionMethod test = CollectionMethod.ComputerOnly | CollectionMethod.LoggedOn; |
| 0 | 190 | | return (methods & test) != 0; |
| 0 | 191 | | } |
| | 192 | |
|
| | 193 | | /// <summary> |
| | 194 | | /// Returns true if any local group collections are set |
| | 195 | | /// </summary> |
| | 196 | | /// <param name="methods"></param> |
| | 197 | | /// <returns></returns> |
| | 198 | | public static bool IsLocalGroupCollectionSet(this CollectionMethod methods) |
| 0 | 199 | | { |
| 0 | 200 | | return (methods & CollectionMethod.LocalGroups) != 0; |
| 0 | 201 | | } |
| | 202 | |
|
| | 203 | | /// <summary> |
| | 204 | | /// Gets the relative identifier for a SID |
| | 205 | | /// </summary> |
| | 206 | | /// <param name="securityIdentifier"></param> |
| | 207 | | /// <returns></returns> |
| | 208 | | public static int Rid(this SecurityIdentifier securityIdentifier) |
| 5 | 209 | | { |
| 5 | 210 | | var value = securityIdentifier.Value; |
| 5 | 211 | | var rid = int.Parse(value.Substring(value.LastIndexOf("-", StringComparison.Ordinal) + 1)); |
| 5 | 212 | | return rid; |
| 5 | 213 | | } |
| | 214 | |
|
| 1 | 215 | | public static IDirectoryObject ToDirectoryObject(this DirectoryEntry entry) { |
| 1 | 216 | | return new DirectoryEntryWrapper(entry); |
| 1 | 217 | | } |
| | 218 | | } |
| | 219 | | } |