2025-11-28 00:35:46 +09:00

392 lines
12 KiB
C++

// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (C) 2002 Microsoft Corporation. All Rights Reserved.
//
// Module Name: main.cpp
//
// Description:
// This file contains the functions for parsing the command-line
// arguments and the main function.
#include "common.h"
// g_AcceptContext will hold all the global variables used across all
// the files.
AcceptContext g_AcceptContext;
/*
This function converts a given address family into its corresponding string
representation for display purposes.
*/
const char *AFImage(BYTE addressFamily)
{
char *szRetVal;
// return the printable string equivalent of the corresponding
// address family.
switch (addressFamily)
{
case AF_UNSPEC : szRetVal = "AF_UNSPEC";
break;
case AF_INET : szRetVal = "AF_INET";
break;
case AF_INET6 : szRetVal = "AF_INET6";
break;
default : szRetVal = "Unrecognized";
break;
}
return szRetVal;
}
/*
This function converts a given type of accept into its corresponding string
representation for display purposes.
*/
const char *AcceptTypeImage(BYTE typeOfAccept)
{
char *szRetVal;
// return a string equivalent of the accept type for displaying
// to the user of his choices.
switch (typeOfAccept)
{
case NON_BLOCKING_ACCEPT : szRetVal = "NON_BLOCKING_ACCEPT";
break;
case ASYNC_SELECT_ACCEPT : szRetVal = "ASYNC_SELECT_ACCEPT";
break;
default : szRetVal = "Unrecognized";
break;
}
return szRetVal;
}
/*
This function prints the available command-line options, the arguments
expected by each of them and the valid input values and the default
values for each them.
*/
void PrintUsage(char *szProgramName)
{
printf("\n\n"
"Usage:\n"
"------\n"
" %s <options> \n\n"
"where <options> is one or more of the following: \n\n"
" -a <0|4|6> Address Family: 0 for Either\n"
" 4 for IPv4\n"
" 6 for IPv6\n"
" Default: %d\n\n"
" -i <interface> Interface address\n"
" Default: %s\n\n"
" -e <endpoint> Port number\n"
" Default: %s\n\n"
" -t <1|2> Type of Accept: 1 for Non-blocking accept\n"
" 2 for Accept with WSAAsyncSelect\n"
" Default: %d\n\n"
"\n",
szProgramName,
DEFAULT_ADDRESS_FAMILY,
( DEFAULT_INTERFACE == NULL ? "NULL" : DEFAULT_INTERFACE),
DEFAULT_PORT,
DEFAULT_TYPE_OF_ACCEPT
);
return;
}
/*
This function parses the given input arguments and fills up the
corresponding fields in the g_AcceptContext structure.
*/
BOOL ParseArguments(int argc, char *argv[])
{
// holds the return value from this function.
// TRUE indicates that all the supplied arguments are valid.
// FALSE indicates incorrect or insufficient number of arguments.
BOOL retVal = FALSE;
// loop index to go over the command-line arguments one by one.
int i;
printf("Entering ParseArguments()\n");
// fill up the default arguments and let the user options override these.
g_AcceptContext.addressFamily = DEFAULT_ADDRESS_FAMILY;
g_AcceptContext.szInterface = DEFAULT_INTERFACE;
g_AcceptContext.szPort = DEFAULT_PORT;
g_AcceptContext.typeOfAccept = DEFAULT_TYPE_OF_ACCEPT;
// process each argument in the argv list.
for (i = 1; i < argc ; i++)
{
char firstChar = argv[i][0];
// make sure the option begins with a hyphen or a forward slash.
if (!(firstChar == '-' || firstChar == '/'))
{
printf("ERROR: Option has to begin with - or / : %s\n", argv[i]);
PrintUsage(argv[0]);
goto CLEANUP;
}
// process the option.
switch(argv[i][1])
{
case 'a' :
// Address Family.
// should be -a 0 or -a 4 or -a 6
// first check if there's one more argument.
if (i + 1 >= argc)
{
printf("ERROR: Argument 0/4/6 needed for -a option\n");
PrintUsage(argv[0]);
goto CLEANUP;
}
// extract and validate the AF number.
switch(atoi(argv[i+1]))
{
// Unspecified.
case 0:
g_AcceptContext.addressFamily = AF_UNSPEC;
break;
// IPv4.
case 4:
g_AcceptContext.addressFamily = AF_INET;
break;
// IPv6.
case 6:
g_AcceptContext.addressFamily = AF_INET6;
break;
// Invalid value.
default:
printf("ERROR: Invalid address family. Must be 0/4/6\n");
PrintUsage(argv[0]);
goto CLEANUP;
}
// indicate that we have processed the next argument as well.
i++;
// AF was fine. continue.
break;
case 'i' :
// Interface to listen on.
// should be -i <interface>
// first check if there's one more argument.
if (i + 1 >= argc)
{
printf("ERROR: Interface needed for -i option\n");
PrintUsage(argv[0]);
goto CLEANUP;
}
// make sure the input string length is less than
// the INET6_ADDRSTRLEN, the maximum valid IP address length.
if (FAILED(StringCchLength(argv[i+1],INET6_ADDRSTRLEN, NULL)))
{
printf("ERROR: Interface string too long. "
"can't exceed %d characters\n",
INET6_ADDRSTRLEN);
PrintUsage(argv[0]);
goto CLEANUP;
}
// remember the interface string.
g_AcceptContext.szInterface = argv[i+1];
// indicate that we have processed the next argument as well.
i++;
// continue.
break;
case 'e' :
// Endpoint or Port.
// should be -e <port number>
// first check if there's one more argument.
if (i + 1 >= argc)
{
printf("ERROR: Port number needed for -e option\n");
PrintUsage(argv[0]);
goto CLEANUP;
}
// make sure the input string length is less than
// the maximum length for a service name.
if (FAILED(StringCchLength(argv[i+1], NI_MAXSERV, NULL)))
{
printf("ERROR: Port number too long. "
"can't exceed %d characters\n",
NI_MAXSERV);
PrintUsage(argv[0]);
goto CLEANUP;
}
// remember the port number string.
g_AcceptContext.szPort = argv[i+1];
// indicate that we have processed the next argument as well.
i++;
// continue.
break;
case 't' :
// Type of Accept.
// should be -t 1 or -t 2
// first check if there's one more argument.
if (i + 1 >= argc)
{
printf("ERROR: Argument 1 or 2 needed for -t option\n");
PrintUsage(argv[0]);
goto CLEANUP;
}
// extract the type value
g_AcceptContext.typeOfAccept = (BYTE) atoi(argv[i+1]);
// indicate that we have processed the next argument as well.
i++;
// validate the accept type.
if (!(g_AcceptContext.typeOfAccept == 1 ||
g_AcceptContext.typeOfAccept == 2))
{
printf("ERROR: Invalid accept type: %d. Must be 1 or 2\n",
g_AcceptContext.addressFamily);
PrintUsage(argv[0]);
goto CLEANUP;
}
// Accept type was fine. continue.
break;
case 'h' : // help
case '?' : // help
PrintUsage(argv[0]);
goto CLEANUP;
default :
printf("ERROR: Unrecognized option: %s\n", argv[i]);
PrintUsage(argv[0]);
goto CLEANUP;
}
}
// echo the final list of values that'll be used.
// remember, these need not be the same as the input arguments.
// rather, this is what we'll use inside our program.
printf("Parsed input arguments. The following values will be used : \n");
printf("\tAddress Family = %s\n", AFImage(g_AcceptContext.addressFamily));
printf("\tInterface = %s\n",g_AcceptContext.szInterface);
printf("\tPort = %s\n", g_AcceptContext.szPort);
printf("\tType of Accept = %s\n",
AcceptTypeImage(g_AcceptContext.typeOfAccept));
// all went well, signal that we can proceed.
retVal = TRUE;
CLEANUP:
printf("Exiting ParseArguments()\n");
return retVal;
}
/*
This function is the entry point for this program.
Based on the command-line arguments, it invokes the suitable functions.
*/
int _cdecl main(int argc, char *argv[])
{
// holds the return value from this function.
// 0 indicates success, non-zero indicates failure.
int retVal;
WSADATA wsaData;
printf("Entering main()\n");
// parse and validate the given arguments and determine if we should
// continue the execution or return error.
if (ParseArguments(argc, argv) == FALSE)
{
// error input. return a non-zero error code.
retVal = 1;
goto CLEANUP;
}
// call WSAStartup before calling any of the Winsock API functions.
retVal = WSAStartup(MAKEWORD(2,2), &wsaData);
if (retVal != 0)
{
printf("WSAStartup failed. Error = %d\n", retVal);
goto CLEANUP;
}
// Depending on the command-line options given, create one or more
// listening sockets on the requested interface(s).
CreateListeningSockets();
// Depending on the type of accept requested, call the suitable function.
switch(g_AcceptContext.typeOfAccept)
{
case NON_BLOCKING_ACCEPT:
NonBlockingAcceptMain();
break;
case ASYNC_SELECT_ACCEPT:
AsyncSelectAcceptMain();
break;
default:
// some error. return a non-zero error code.
retVal = 1;
break;
}
// we may not come here as per the current implementation since
// the XXXAcceptMain functions themselves are waiting forever for
// connections or data. But in case we add a timeout option in future
// we might come here and so we'll cleanup everything properly.
// Close all the listening sockets and remove them from the global
// list. In case there are some accepted sockets still in the list,
// (due to some error), they'll also be closed as well.
DestroyListeningSockets();
// Inform Winsock that we're done with all the Winsock APIs.
WSACleanup();
CLEANUP:
printf("Exiting main()\n");
return retVal;
}