< Summary

Class:SharpHoundCommonLib.Processors.PortScanner
Assembly:SharpHoundCommonLib
File(s):D:\a\SharpHoundCommon\SharpHoundCommon\src\CommonLib\Processors\PortScanner.cs
Covered lines:4
Uncovered lines:54
Coverable lines:58
Total lines:105
Line coverage:6.8% (4 of 58)
Covered branches:0
Total branches:18
Branch coverage:0% (0 of 18)

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.cctor()100%100%
.ctor()100%10100%
.ctor(...)0%200%
CheckPort()0%800%
ClearCache()100%100%
Equals(...)0%200%
Equals(...)0%600%
GetHashCode()100%100%

File(s)

D:\a\SharpHoundCommon\SharpHoundCommon\src\CommonLib\Processors\PortScanner.cs

#LineLine coverage
 1using System;
 2using System.Collections.Concurrent;
 3using System.Net.Sockets;
 4using System.Threading.Tasks;
 5using Microsoft.Extensions.Logging;
 6
 7namespace SharpHoundCommonLib.Processors
 8{
 9    public class PortScanner
 10    {
 011        private static readonly ConcurrentDictionary<PingCacheKey, bool> PortScanCache = new();
 12        private readonly ILogger _log;
 13
 7414        public PortScanner()
 7415        {
 7416            _log = Logging.LogProvider.CreateLogger("PortScanner");
 7417        }
 18
 019        public PortScanner(ILogger log = null)
 020        {
 021            _log = log ?? Logging.LogProvider.CreateLogger("PortScanner");
 022        }
 23
 24        /// <summary>
 25        ///     Checks if a specified port is open on a host. Defaults to 445 (SMB)
 26        /// </summary>
 27        /// <param name="hostname"></param>
 28        /// <param name="port"></param>
 29        /// <param name="timeout">Timeout in milliseconds</param>
 30        /// <returns>True if port is open, otherwise false</returns>
 31        public virtual async Task<bool> CheckPort(string hostname, int port = 445, int timeout = 500)
 032        {
 033            var key = new PingCacheKey
 034            {
 035                Port = port,
 036                HostName = hostname
 037            };
 38
 039            if (PortScanCache.TryGetValue(key, out var status))
 040            {
 041                _log.LogTrace("Port scan cache hit for {HostName}:{Port}: {Status}", hostname, port, status);
 042                return status;
 43            }
 44
 45            try
 046            {
 047                using var client = new TcpClient();
 048                var ca = client.ConnectAsync(hostname, port);
 049                if (await Task.WhenAny(ca, Task.Delay(timeout)) == ca)
 050                {
 051                    if (ca.IsFaulted)
 052                    {
 053                        _log.LogDebug("PortScan faulted on {Hostname}:{Port} with error {Error}", hostname, port, ca.Exc
 054                        PortScanCache.TryAdd(key, false);
 055                        return false;
 56                    }
 057                    PortScanCache.TryAdd(key, true);
 058                    return true;
 59                }
 60
 061                _log.LogDebug("{HostName} did not respond to scan on port {Port} within {Timeout}ms", hostname, port, ti
 062                PortScanCache.TryAdd(key, false);
 063                return false;
 64            }
 065            catch (Exception e)
 066            {
 067                _log.LogDebug(e, "Exception checking {Hostname}:{Port}", hostname, port);
 068                PortScanCache.TryAdd(key, false);
 069                return false;
 70            }
 071        }
 72
 73        public static void ClearCache()
 074        {
 075            PortScanCache.Clear();
 076        }
 77
 78        private class PingCacheKey
 79        {
 080            internal string HostName { get; set; }
 081            internal int Port { get; set; }
 82
 83            protected bool Equals(PingCacheKey other)
 084            {
 085                return HostName == other.HostName && Port == other.Port;
 086            }
 87
 88            public override bool Equals(object obj)
 089            {
 090                if (ReferenceEquals(null, obj)) return false;
 091                if (ReferenceEquals(this, obj)) return true;
 092                if (obj.GetType() != GetType()) return false;
 093                return Equals((PingCacheKey) obj);
 094            }
 95
 96            public override int GetHashCode()
 097            {
 98                unchecked
 099                {
 0100                    return (HostName.GetHashCode() * 397) ^ Port;
 101                }
 0102            }
 103        }
 104    }
 105}