static void ParseForAddress()

in ctsTraffic/ctsConfig.cpp [1079:1279]


static void ParseForAddress(vector<const wchar_t*>& args)
{
    // -listen:<addr> 
    auto foundListen = begin(args);
    while (foundListen != end(args))
    {
        foundListen = ranges::find_if(args, [](const wchar_t* parameter) -> bool {
            const auto* const value = ParseArgument(parameter, L"-listen");
            return value != nullptr;
        });
        if (foundListen != end(args))
        {
            // ReSharper disable once CppTooWideScopeInitStatement
            const auto* const value = ParseArgument(*foundListen, L"-listen");
            if (ctString::ctOrdinalEqualsCaseInsensative(L"*", value))
            {
                // add both v4 and v6
                ctSockaddr listenAddr(AF_INET, ctSockaddr::AddressType::Any);
                g_configSettings->ListenAddresses.push_back(listenAddr);
                listenAddr.set(AF_INET6, ctSockaddr::AddressType::Any);
                g_configSettings->ListenAddresses.push_back(listenAddr);
            }
            else
            {
                auto tempAddresses(ctSockaddr::ResolveName(value));
                if (tempAddresses.empty())
                {
                    throw invalid_argument("-listen value did not resolve to an IP address");
                }
                g_configSettings->ListenAddresses.insert(end(g_configSettings->ListenAddresses), begin(tempAddresses), end(tempAddresses));
            }
            // always remove the arg from our vector
            args.erase(foundListen);
            // found_listen is now invalidated since we just erased what it's pointing to
            // - reset it to begin() since we know it's not end()
            foundListen = args.begin();
        }
    }

    // -target:<addr> 
    auto foundTarget = begin(args);
    while (foundTarget != end(args))
    {
        foundTarget = ranges::find_if(args, [](const wchar_t* parameter) -> bool {
            const auto* const value = ParseArgument(parameter, L"-target");
            return value != nullptr;
        });
        if (foundTarget != end(args))
        {
            if (!g_configSettings->ListenAddresses.empty())
            {
                throw invalid_argument("cannot specify both -Listen and -Target");
            }
            const auto* const value = ParseArgument(*foundTarget, L"-target");
            auto tempAddresses(ctSockaddr::ResolveName(value));
            if (tempAddresses.empty())
            {
                throw invalid_argument("-target value did not resolve to an IP address");
            }
            g_configSettings->TargetAddresses.insert(end(g_configSettings->TargetAddresses), begin(tempAddresses), end(tempAddresses));
            // always remove the arg from our vector
            args.erase(foundTarget);
            // found_target is now invalidated since we just erased what it's pointing to
            // - reset it to begin() since we know it's not end()
            foundTarget = args.begin();
        }
    }

    // -bind:<addr> 
    auto foundBind = begin(args);
    while (foundBind != end(args))
    {
        foundBind = ranges::find_if(args, [](const wchar_t* parameter) -> bool {
            const auto* const value = ParseArgument(parameter, L"-bind");
            return value != nullptr;
        });
        if (foundBind != end(args))
        {
            // ReSharper disable once CppTooWideScopeInitStatement
            const auto* const value = ParseArgument(*foundBind, L"-bind");
            // check for a comma-delimited list of IP Addresses
            if (ctString::ctOrdinalEqualsCaseInsensative(L"*", value))
            {
                // add both v4 and v6
                ctSockaddr bindAddr(AF_INET, ctSockaddr::AddressType::Any);
                g_configSettings->BindAddresses.push_back(bindAddr);
                bindAddr.set(AF_INET6, ctSockaddr::AddressType::Any);
                g_configSettings->BindAddresses.push_back(bindAddr);
            }
            else
            {
                auto tempAddresses(ctSockaddr::ResolveName(value));
                if (tempAddresses.empty())
                {
                    throw invalid_argument("-bind value did not resolve to an IP address");
                }
                g_configSettings->BindAddresses.insert(end(g_configSettings->BindAddresses), begin(tempAddresses), end(tempAddresses));
            }
            // always remove the arg from our vector
            args.erase(foundBind);
            // found_bind is now invalidated since we just erased what it's pointing to
            // - reset it to begin() since we know it's not end()
            foundBind = args.begin();
        }
    }

    if (!g_configSettings->ListenAddresses.empty() && !g_configSettings->TargetAddresses.empty())
    {
        throw invalid_argument("cannot specify both -target and -listen");
    }
    if (!g_configSettings->ListenAddresses.empty() && !g_configSettings->BindAddresses.empty())
    {
        throw invalid_argument("cannot specify both -bind and -listen");
    }
    if (g_configSettings->ListenAddresses.empty() && g_configSettings->TargetAddresses.empty())
    {
        throw invalid_argument("must specify either -target or -listen");
    }

    // default bind addresses if not listening and did not exclusively want to bind
    if (g_configSettings->ListenAddresses.empty() && g_configSettings->BindAddresses.empty())
    {
        ctSockaddr defaultAddr(AF_INET, ctSockaddr::AddressType::Any);
        g_configSettings->BindAddresses.push_back(defaultAddr);
        defaultAddr.set(AF_INET6, ctSockaddr::AddressType::Any);
        g_configSettings->BindAddresses.push_back(defaultAddr);
    }

    if (!g_configSettings->TargetAddresses.empty())
    {
        //
        // guarantee that bindaddress and targetaddress families can match
        // - can't allow a bind address to be chosen if there are no TargetAddresses with the same family
        //
        uint32_t bindV4 = 0;
        uint32_t bindV6 = 0;
        uint32_t targetV4 = 0;
        uint32_t targetV6 = 0;
        for (const auto& addr : g_configSettings->BindAddresses)
        {
            if (addr.family() == AF_INET)
            {
                ++bindV4;
            }
            else
            {
                ++bindV6;
            }
        }
        for (const auto& addr : g_configSettings->TargetAddresses)
        {
            if (addr.family() == AF_INET)
            {
                ++targetV4;
            }
            else
            {
                ++targetV6;
            }
        }
        //
        // if either bind or target has zero of either family, remove those addrs from the other vector
        //
        if (0 == bindV4)
        {
            std::erase_if(
                g_configSettings->TargetAddresses,
                [](const ctSockaddr& addr) noexcept { return addr.family() == AF_INET; }
            );
        }
        else if (0 == targetV4)
        {
            std::erase_if(
                g_configSettings->BindAddresses,
                [](const ctSockaddr& addr) noexcept { return addr.family() == AF_INET; }
            );
        }

        if (0 == bindV6)
        {
            std::erase_if(
                g_configSettings->TargetAddresses,
                [](const ctSockaddr& addr) noexcept { return addr.family() == AF_INET6; }
            );
        }
        else if (0 == targetV6)
        {
            std::erase_if(
                g_configSettings->BindAddresses,
                [](const ctSockaddr& addr) noexcept { return addr.family() == AF_INET6; }
            );
        }
        //
        // now if either are of size zero, the user specified addresses which didn't align
        //
        if (g_configSettings->BindAddresses.empty() || g_configSettings->TargetAddresses.empty())
        {
            throw invalid_argument("-bind addresses and target addresses must match families");
        }
    }
}