public void StartStealthEnumeration()
        {
            var output = new BlockingCollection <Wrapper <OutputBase> >();
            var writer = _options.Uri == null
                ? StartOutputWriter(output)
                : StartRestWriter(output);

            foreach (var domainName in _utils.GetDomainList())
            {
                Console.WriteLine($"Starting stealth enumeration for {domainName}\n");
                var domainSid = _utils.GetDomainSid(domainName);
                var data      = LdapFilter.GetLdapFilter(_options.CollectMethod, _options.ExcludeDC, true);
                switch (_options.CollectMethod)
                {
                case CollectionMethod.ObjectProps:
                    Console.WriteLine("Doing stealth enumeration for object properties");
                    foreach (var entry in _utils.DoSearch(data.Filter, SearchScope.Subtree, data.Properties, domainName))
                    {
                        var        resolved = entry.ResolveAdEntry();
                        OutputBase props;
                        if (resolved.ObjectType.Equals("computer"))
                        {
                            props = ObjectPropertyHelpers.GetComputerProps(entry, resolved);
                        }
                        else
                        {
                            props = ObjectPropertyHelpers.GetUserProps(entry, resolved);
                        }
                        if (props != null)
                        {
                            output.Add(new Wrapper <OutputBase> {
                                Item = props
                            });
                        }
                    }
                    break;

                case CollectionMethod.Session:
                    Console.WriteLine("Doing stealth enumeration for sessions");
                    foreach (var path in SessionHelpers.CollectStealthTargets(domainName))
                    {
                        var sessions = SessionHelpers.GetNetSessions(path, domainName);
                        foreach (var s in sessions)
                        {
                            output.Add(new Wrapper <OutputBase> {
                                Item = s
                            });
                        }
                    }
                    break;

                case CollectionMethod.ComputerOnly:
                    Console.WriteLine("Doing stealth enumeration for sessions");
                    foreach (var path in SessionHelpers.CollectStealthTargets(domainName))
                    {
                        var sessions = SessionHelpers.GetNetSessions(path, domainName);
                        foreach (var s in sessions)
                        {
                            output.Add(new Wrapper <OutputBase> {
                                Item = s
                            });
                        }
                    }

                    Console.WriteLine("Doing stealth enumeration for admins");
                    foreach (var entry in _utils.DoSearch(
                                 data.Filter, SearchScope.Subtree,
                                 data.Properties, domainName))
                    {
                        foreach (var admin in LocalAdminHelpers.GetGpoAdmins(entry, domainName))
                        {
                            output.Add(new Wrapper <OutputBase> {
                                Item = admin
                            });
                        }
                    }
                    break;

                case CollectionMethod.Default:
                    Console.WriteLine("Doing stealth enumeration for sessions");
                    foreach (var path in SessionHelpers.CollectStealthTargets(domainName))
                    {
                        var sessions = SessionHelpers.GetNetSessions(path, domainName);
                        foreach (var s in sessions)
                        {
                            output.Add(new Wrapper <OutputBase> {
                                Item = s
                            });
                        }
                    }

                    Console.WriteLine("Doing stealth enumeration for admins");
                    foreach (var entry in _utils.DoSearch(
                                 "(&(objectCategory=groupPolicyContainer)(name=*)(gpcfilesyspath=*))", SearchScope.Subtree,
                                 new[] { "displayname", "name", "gpcfilesyspath" }, domainName))
                    {
                        foreach (var admin in LocalAdminHelpers.GetGpoAdmins(entry, domainName))
                        {
                            output.Add(new Wrapper <OutputBase> {
                                Item = admin
                            });
                        }
                    }

                    Console.WriteLine("Doing stealth enumeration for groups");
                    foreach (var entry in _utils.DoSearch("(|(memberof=*)(primarygroupid=*))",
                                                          SearchScope.Subtree,
                                                          new[]
                    {
                        "samaccountname", "distinguishedname", "dnshostname", "samaccounttype",
                        "primarygroupid", "memberof", "serviceprincipalname"
                    }, domainName))
                    {
                        var resolvedEntry = entry.ResolveAdEntry();
                        foreach (var group in GroupHelpers.ProcessAdObject(entry, resolvedEntry, domainSid))
                        {
                            output.Add(new Wrapper <OutputBase> {
                                Item = group
                            });
                        }
                    }
                    break;

                case CollectionMethod.SessionLoop:
                    Console.WriteLine("Doing stealth enumeration for sessions");
                    foreach (var path in SessionHelpers.CollectStealthTargets(domainName))
                    {
                        var sessions = SessionHelpers.GetNetSessions(path, domainName);
                        foreach (var s in sessions)
                        {
                            output.Add(new Wrapper <OutputBase> {
                                Item = s
                            });
                        }
                    }
                    break;

                case CollectionMethod.LoggedOn:
                    Console.WriteLine("Doing LoggedOn enumeration for stealth targets");
                    foreach (var path in SessionHelpers.CollectStealthTargets(domainName))
                    {
                        var sessions = SessionHelpers.GetNetLoggedOn(path, domainName);
                        foreach (var s in sessions)
                        {
                            output.Add(new Wrapper <OutputBase> {
                                Item = s
                            });
                        }
                        sessions = SessionHelpers.GetRegistryLoggedOn(path);
                        foreach (var s in sessions)
                        {
                            output.Add(new Wrapper <OutputBase> {
                                Item = s
                            });
                        }
                    }
                    break;

                case CollectionMethod.Group:
                    Console.WriteLine("Doing stealth enumeration for groups");
                    foreach (var entry in _utils.DoSearch(data.Filter,
                                                          SearchScope.Subtree,
                                                          data.Properties, domainName))
                    {
                        var resolvedEntry = entry.ResolveAdEntry();
                        foreach (var group in GroupHelpers.ProcessAdObject(entry, resolvedEntry, domainSid))
                        {
                            output.Add(new Wrapper <OutputBase> {
                                Item = group
                            });
                        }
                    }
                    break;

                case CollectionMethod.LocalGroup:
                    //This case will never happen
                    break;

                case CollectionMethod.GPOLocalGroup:
                    Console.WriteLine("Doing stealth enumeration for admins");
                    foreach (var entry in _utils.DoSearch(
                                 data.Filter, SearchScope.Subtree,
                                 data.Properties, domainName))
                    {
                        foreach (var admin in LocalAdminHelpers.GetGpoAdmins(entry, domainName))
                        {
                            output.Add(new Wrapper <OutputBase> {
                                Item = admin
                            });
                        }
                    }
                    break;

                case CollectionMethod.Trusts:
                    var trusts = DomainTrustEnumeration.DoTrustEnumeration(domainName);
                    foreach (var trust in trusts)
                    {
                        output.Add(new Wrapper <OutputBase> {
                            Item = trust
                        });
                    }
                    break;

                case CollectionMethod.ACL:
                    Console.WriteLine("Doing stealth enumeration for ACLs");
                    foreach (var entry in _utils.DoSearch(
                                 data.Filter,
                                 SearchScope.Subtree,
                                 data.Properties, domainName))
                    {
                        foreach (var acl in AclHelpers.ProcessAdObject(entry, domainName))
                        {
                            output.Add(new Wrapper <OutputBase> {
                                Item = acl
                            });
                        }
                    }
                    break;

                default:
                    throw new ArgumentOutOfRangeException();
                }
                output.CompleteAdding();
                writer.Wait();
                Console.WriteLine($"Finished stealth enumeration for {domainName}");
            }
            if (!_options.CollectMethod.Equals(CollectionMethod.SessionLoop))
            {
                return;
            }
            if (_options.MaxLoopTime != null)
            {
                if (DateTime.Now > _options.LoopEnd)
                {
                    Console.WriteLine("Exiting session loop as LoopEndTime as passed");
                    return;
                }
            }

            Console.WriteLine($"Starting next session run in {_options.LoopTime} minutes");
            new ManualResetEvent(false).WaitOne(_options.LoopTime * 60 * 1000);
            if (_options.MaxLoopTime != null)
            {
                if (DateTime.Now > _options.LoopEnd)
                {
                    Console.WriteLine("Exiting session loop as LoopEndTime as passed");
                    return;
                }
            }
            Console.WriteLine("Starting next enumeration loop");
            StartStealthEnumeration();
        }
        public void StartEnumeration()
        {
            //Let's determine what LDAP filter we need first
            var data       = LdapFilter.GetLdapFilter(_options.CollectMethod, _options.ExcludeDC, _options.Stealth);
            var ldapFilter = data.Filter;
            var props      = data.Properties;
            var c          = _options.CollectMethod;


            foreach (var domainName in _utils.GetDomainList())
            {
                _noPing   = 0;
                _timeouts = 0;
                Console.WriteLine($"Starting enumeration for {domainName}");

                _watch            = Stopwatch.StartNew();
                _currentDomain    = domainName;
                _currentDomainSid = _utils.GetDomainSid(domainName);
                _currentCount     = 0;
                var outputQueue = new BlockingCollection <Wrapper <OutputBase> >();

                if (_options.ComputerFile == null)
                {
                    var inputQueue = new BlockingCollection <Wrapper <SearchResultEntry> >(1000);

                    var taskhandles = new Task[_options.Threads];

                    var writer = _options.Uri == null
                        ? StartOutputWriter(outputQueue)
                        : StartRestWriter(outputQueue);

                    if (c.Equals(CollectionMethod.Trusts) ||
                        c.Equals(CollectionMethod.Default))
                    {
                        foreach (var domain in DomainTrustEnumeration.DoTrustEnumeration(domainName))
                        {
                            outputQueue.Add(new Wrapper <OutputBase> {
                                Item = domain
                            });
                        }
                        if (_options.CollectMethod.Equals(CollectionMethod.Trusts))
                        {
                            outputQueue.CompleteAdding();
                            writer.Wait();
                            continue;
                        }
                    }

                    if (c.Equals(CollectionMethod.Container))
                    {
                        foreach (var container in ContainerHelpers.GetContainersForDomain(domainName))
                        {
                            outputQueue.Add(new Wrapper <OutputBase> {
                                Item = container
                            });
                        }
                        if (_options.CollectMethod.Equals(CollectionMethod.Container))
                        {
                            outputQueue.CompleteAdding();
                            writer.Wait();
                            continue;
                        }
                    }

                    for (var i = 0; i < _options.Threads; i++)
                    {
                        taskhandles[i] = StartRunner(inputQueue, outputQueue);
                    }

                    _statusTimer.Start();

                    IEnumerable <Wrapper <SearchResultEntry> > items;

                    if ((c.Equals(CollectionMethod.ComputerOnly) || c.Equals(CollectionMethod.Session) ||
                         c.Equals(CollectionMethod.LocalGroup) || c.Equals(CollectionMethod.LoggedOn)) &&
                        _options.Ou != null)
                    {
                        items = _utils.DoWrappedSearch(ldapFilter, SearchScope.Subtree, props, domainName, _options.Ou);
                    }
                    else
                    {
                        items = _utils.DoWrappedSearch(ldapFilter, SearchScope.Subtree, props, domainName);
                    }

                    foreach (var item in items)
                    {
                        inputQueue.Add(item);
                    }

                    inputQueue.CompleteAdding();
                    Utils.Verbose("Waiting for enumeration threads to finish");
                    Task.WaitAll(taskhandles);

                    _statusTimer.Stop();

                    if (_options.CollectMethod.Equals(CollectionMethod.ACL))
                    {
                        foreach (var a in AclHelpers.GetSyncers())
                        {
                            outputQueue.Add(new Wrapper <OutputBase> {
                                Item = a
                            });
                        }
                        AclHelpers.ClearSyncers();
                    }
                    PrintStatus();
                    outputQueue.CompleteAdding();
                    Utils.Verbose("Waiting for writer thread to finish");
                    writer.Wait();
                    _watch.Stop();
                    Console.WriteLine($"Finished enumeration for {domainName} in {_watch.Elapsed}");
                    Console.WriteLine($"{_noPing} hosts failed ping. {_timeouts} hosts timedout.");
                    _watch = null;
                }
                else
                {
                    var inputQueue = new BlockingCollection <Wrapper <string> >(1000);

                    var taskhandles = new Task[_options.Threads];

                    var writer = _options.Uri == null?StartOutputWriter(outputQueue) : StartRestWriter(outputQueue);

                    for (var i = 0; i < _options.Threads; i++)
                    {
                        taskhandles[i] = StartCompListRunner(inputQueue, outputQueue);
                    }

                    _statusTimer.Start();

                    using (var reader = new StreamReader(_options.ComputerFile))
                    {
                        string line;
                        while ((line = reader.ReadLine()) != null)
                        {
                            inputQueue.Add(new Wrapper <string> {
                                Item = line
                            });
                        }
                        inputQueue.CompleteAdding();
                    }
                    Utils.Verbose("Waiting for enumeration threads to finish");
                    Task.WaitAll(taskhandles);

                    _statusTimer.Stop();
                    PrintStatus();
                    outputQueue.CompleteAdding();
                    Utils.Verbose("Waiting for writer thread to finish");
                    writer.Wait();
                    _watch.Stop();
                    Console.WriteLine($"Finished enumeration for {domainName} in {_watch.Elapsed}");
                    Console.WriteLine($"{_noPing} hosts failed ping. {_timeouts} hosts timedout.");
                    _watch = null;
                }
            }

            if (!_options.CollectMethod.Equals(CollectionMethod.SessionLoop))
            {
                return;
            }
            if (_options.MaxLoopTime != null)
            {
                if (DateTime.Now > _options.LoopEnd)
                {
                    Console.WriteLine("Exiting session loop as LoopEndTime as passed");
                    return;
                }
            }

            Console.WriteLine($"Starting next session run in {_options.LoopTime} minutes");
            new ManualResetEvent(false).WaitOne(_options.LoopTime * 60 * 1000);
            if (_options.MaxLoopTime != null)
            {
                if (DateTime.Now > _options.LoopEnd)
                {
                    Console.WriteLine("Exiting session loop as LoopEndTime as passed");
                    return;
                }
            }
            Console.WriteLine("Starting next enumeration loop");
            StartEnumeration();
        }