in ctsTraffic/ctsConfig.cpp [2608:2903]
bool Startup(int argc, _In_reads_(argc) const wchar_t** argv)
{
ctsConfigInitOnce();
if (argc < 2)
{
PrintUsage();
return false;
}
// ignore the first argv... the exe itself
const wchar_t** argBegin = argv + 1;
const wchar_t** argEnd = argv + argc;
vector args(argBegin, argEnd);
//
// first check of they asked for help text
//
// ReSharper disable once CppTooWideScopeInitStatement
const auto foundHelp = ranges::find_if(args,
[](const wchar_t* arg) -> bool {
return ctString::ctOrdinalStartsWithCaseInsensative(arg, L"-Help") ||
ctString::ctOrdinalEqualsCaseInsensative(arg, L"-?");
});
if (foundHelp != end(args))
{
const auto* helpString = *foundHelp;
if (ctString::ctOrdinalEqualsCaseInsensative(helpString, L"-Help:Advanced"))
{
PrintUsage(PrintUsageOption::Advanced);
return false;
}
if (ctString::ctOrdinalEqualsCaseInsensative(helpString, L"-Help:Tcp"))
{
PrintUsage(PrintUsageOption::Tcp);
return false;
}
if (ctString::ctOrdinalEqualsCaseInsensative(helpString, L"-Help:Udp"))
{
PrintUsage(PrintUsageOption::Udp);
return false;
}
if (ctString::ctOrdinalEqualsCaseInsensative(helpString, L"-Help:Logging"))
{
PrintUsage(PrintUsageOption::Logging);
return false;
}
PrintUsage();
return false;
}
//
// create the handle for ctrl-c
//
g_configSettings->CtrlCHandle = CreateEventW(nullptr, TRUE, FALSE, nullptr);
if (!g_configSettings->CtrlCHandle)
{
THROW_WIN32_MSG(GetLastError(), "CreateEvent");
}
//
// Many of the below settings must be made in a specified order - comments below help to explain this reasoning
// note: the IO function definitions must come after *all* other settings
// since instantiations of those IO functions might reference global Settings values
//
// First:
// Establish logging settings including verbosity levels and error policies before any functional settings
// Create the threadpool before instantiating any other object
//
ParseForError(args);
ParseForLogging(args);
//
// Next: check for static machine configuration
// - note these are checking system settings, not user arguments
//
CheckSystemSettings();
//
// Next: establish the address and port # to be used
//
ParseForAddress(args);
ParseForPort(args);
ParseForLocalport(args);
ParseForIfIndex(args);
//
// ensure a Port is assigned to all listening addresses and target addresses
//
for (auto& addr : g_configSettings->ListenAddresses)
{
if (addr.port() == 0x0000)
{
addr.SetPort(g_configSettings->Port);
}
}
for (auto& addr : g_configSettings->TargetAddresses)
{
if (addr.port() == 0x0000)
{
addr.SetPort(g_configSettings->Port);
}
}
if (g_configSettings->OutgoingIfIndex != 0 && !g_configSettings->ListenAddresses.empty())
{
throw invalid_argument("-IfIndex can only be used for outgoing connections, not listening sockets");
}
//
// Next: gather the protocol and Pattern to be used
// - set the threadpool value after identifying the pattern
ParseForProtocol(args);
// default to keep-alive on TCP servers
if (ProtocolType::TCP == g_configSettings->Protocol && !g_configSettings->ListenAddresses.empty())
{
g_configSettings->Options |= Keepalive;
}
ParseForIoPattern(args);
ParseForThreadpool(args);
// validate protocol & pattern combinations
if (ProtocolType::UDP == g_configSettings->Protocol && IoPatternType::MediaStream != g_configSettings->IoPattern)
{
throw invalid_argument("UDP only supports the MediaStream IO Pattern");
}
if (ProtocolType::TCP == g_configSettings->Protocol && IoPatternType::MediaStream == g_configSettings->IoPattern)
{
throw invalid_argument("TCP does not support the MediaStream IO Pattern");
}
// set appropriate defaults for # of connections for TCP vs. UDP
if (ProtocolType::UDP == g_configSettings->Protocol)
{
g_configSettings->ConnectionLimit = c_defaultUdpConnectionLimit;
}
else
{
g_configSettings->ConnectionLimit = c_defaultTcpConnectionLimit;
}
//
// Next, set the ctsStatusInformation to be used to print status updates for this protocol
// - this must be called after both set_logging and set_protocol
//
if (ProtocolType::TCP == g_configSettings->Protocol)
{
g_printStatusInformation = make_shared<ctsTcpStatusInformation>();
}
else
{
g_printStatusInformation = make_shared<ctsUdpStatusInformation>();
}
//
// Next: capture other various settings which do not have explicit dependencies
//
ParseForOptions(args);
ParseForKeepAlive(args);
ParseForCompartment(args);
ParseForConnections(args);
ParseForThrottleConnections(args);
ParseForBuffer(args);
ParseForTransfer(args);
ParseForIterations(args);
ParseForServerExitLimit(args);
ParseForRatelimit(args);
ParseForTimelimit(args);
if (g_rateLimitLow > 0LL && g_configSettings->BurstDelay.has_value())
{
throw invalid_argument("-RateLimit and -Burstdelay cannot be used concurrently");
}
// ReSharper disable once CppTooWideScopeInitStatement
const auto ratePerPeriod = g_rateLimitLow * g_configSettings->TcpBytesPerSecondPeriod / 1000LL;
if (g_configSettings->Protocol == ProtocolType::TCP && g_rateLimitLow > 0 && ratePerPeriod < 1)
{
throw invalid_argument("RateLimit * RateLimitPeriod / 1000 must be greater than zero - meaning every period should send at least 1 byte");
}
//
// verify jitter logging requirements
//
if (g_jitterLogger && g_configSettings->Protocol != ProtocolType::UDP)
{
throw invalid_argument("Jitter can only be logged using UDP");
}
if (g_jitterLogger && !g_configSettings->ListenAddresses.empty())
{
throw invalid_argument("Jitter can only be logged on the client");
}
if (g_jitterLogger && g_configSettings->ConnectionLimit != 1)
{
throw invalid_argument("Jitter can only be logged for a single UDP connection");
}
if (g_mediaStreamSettings.FrameSizeBytes > 0)
{
// the buffersize is now effectively the frame size
g_bufferSizeHigh = 0;
g_bufferSizeLow = g_mediaStreamSettings.FrameSizeBytes;
if (g_bufferSizeLow < 20)
{
throw invalid_argument("The media stream frame size (buffer) must be at least 20 bytes");
}
}
// validate localport usage
if (!g_configSettings->ListenAddresses.empty() && g_configSettings->LocalPortLow != 0)
{
throw invalid_argument("Cannot specify both -listen and -LocalPort. To listen on a specific port, use -Port:####");
}
if (g_configSettings->LocalPortLow != 0)
{
// ReSharper disable once CppTooWideScopeInitStatement
const USHORT numberOfPorts = g_configSettings->LocalPortHigh == 0 ? 1 : static_cast<USHORT>(g_configSettings->LocalPortHigh - g_configSettings->LocalPortLow + 1);
if (numberOfPorts < g_configSettings->ConnectionLimit)
{
throw invalid_argument(
"Cannot specify more connections than specified local ports. "
"Reduce the number of connections or increase the range of local ports.");
}
}
//
// Set the default buffer values as these settings are optional
//
g_configSettings->ShouldVerifyBuffers = true;
g_configSettings->UseSharedBuffer = false;
ParseForShouldVerifyBuffers(args);
if (ProtocolType::UDP == g_configSettings->Protocol)
{
// UDP clients can never recv into the same shared buffer since it uses it for seq. numbers, etc
if (!IsListening())
{
g_configSettings->UseSharedBuffer = false;
}
}
//
// finally set the functions to use once all other settings are established
// set_ioFunction changes global options for socket operation for instance WSA_FLAG_REGISTERED_IO flag
// - hence it is requirement to invoke it prior to any socket operation
//
ParseForIoFunction(args);
ParseForInlineCompletions(args);
ParseForMsgWaitAll(args);
ParseForCreate(args);
ParseForConnect(args);
ParseForAccept(args);
if (!g_configSettings->ListenAddresses.empty())
{
// servers 'create' connections when they accept them
g_configSettings->CreateFunction = g_configSettings->AcceptFunction;
g_configSettings->ConnectFunction = nullptr;
}
g_configSettings->TcpShutdown = TcpShutdownType::GracefulShutdown;
ParseForShutdown(args);
ParseForPrepostrecvs(args);
if (ProtocolType::TCP == g_configSettings->Protocol && g_configSettings->ShouldVerifyBuffers && g_configSettings->PrePostRecvs > 1)
{
throw invalid_argument("-PrePostRecvs > 1 requires -Verify:connection when using TCP");
}
ParseForPrepostsends(args);
ParseForRecvbufvalue(args);
ParseForSendbufvalue(args);
if (!args.empty())
{
wstring errorString;
for (const auto& argString : args)
{
errorString.append(L" ");
errorString.append(argString);
}
errorString.append(L"\n");
PrintErrorInfoOverride(errorString.c_str());
throw invalid_argument(ctString::ctConvertToString(errorString).c_str());
}
if (ProtocolType::UDP == g_configSettings->Protocol)
{
if (const auto timerResult = timeBeginPeriod(1); timerResult != TIMERR_NOERROR)
{
THROW_WIN32_MSG(timerResult, "timeBeginPeriod");
}
++g_timePeriodRefCount;
}
return true;
}