580 lines
18 KiB
C
580 lines
18 KiB
C
//+-------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
//
|
|
// Copyright (C) Microsoft Corporation, 1994 - 1999
|
|
//
|
|
// File: server.c
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Filename: server.c
|
|
//
|
|
// Description: This file contains the source code for IPC performance.
|
|
// This module is written using win32 API calls, and will
|
|
// generate a console server app.
|
|
//
|
|
// Authors: Scott Holden (Translator from NT API to win32 API)
|
|
// Mahesh Keni (Mahesh wrote this application using mostly
|
|
// NT native API calls)
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "rawcom.h"
|
|
#include "server.h"
|
|
|
|
/************************************************************************/
|
|
// Global variables
|
|
/************************************************************************/
|
|
|
|
struct client Clients[MAXCLIENTS]; // all the client data
|
|
HANDLE Threads[MAXCLIENTS];
|
|
USHORT NClients = 1; // number of clients
|
|
USHORT IPCType = NP; // IPC type
|
|
LARGE_INTEGER Timeout; // for the main thread
|
|
CHAR Xport[9]; // Xport type Name
|
|
USHORT ThreadError = 0;
|
|
BOOLEAN Failure = FALSE;
|
|
|
|
// function pointers for redirecting calls according to Xport types
|
|
NTSTATUS (* IPC_Initialize)();
|
|
NTSTATUS (* IPC_PerClientInit)();
|
|
NTSTATUS (* IPC_Wait_For_Client)();
|
|
NTSTATUS (* IPC_Disconnect_Client)();
|
|
NTSTATUS (* IPC_Cleanup)();
|
|
NTSTATUS (* IPC_Allocate_Memory)();
|
|
NTSTATUS (* IPC_DoHandshake)();
|
|
NTSTATUS (* IPC_ReadFromIPC)();
|
|
NTSTATUS (* IPC_WriteToIPC)();
|
|
NTSTATUS (* IPC_XactIO)();
|
|
NTSTATUS (* IPC_Deallocate_Memory)();
|
|
NTSTATUS (* IPC_ThreadCleanUp)();
|
|
|
|
// Later all of this should move under an union
|
|
// Globals for NamedPipe
|
|
//OBJECT_ATTRIBUTES objectAttributes;
|
|
//UNICODE_STRING unicodePipeName;
|
|
char *pipeName;
|
|
ULONG Quotas = 32768; // read/write quota
|
|
ULONG PipeType = PIPE_TYPE_MESSAGE; // pipe type
|
|
ULONG PipeMode = PIPE_READMODE_MESSAGE; // read mode
|
|
ULONG BlockorNot = PIPE_NOWAIT; // non blocking
|
|
|
|
// Globals for NetBIOS
|
|
USHORT LanaCount = 1;
|
|
USHORT LanaBase = 0;
|
|
USHORT MachineNumber = 1;
|
|
UCHAR NameNumber = 1;
|
|
CHAR LocalName[NCBNAMSZ];
|
|
CHAR RemoteName[NCBNAMSZ];
|
|
|
|
// GLobals for Sockets
|
|
PCHAR ServerName = NULL; // local host ip address
|
|
PCHAR HostName = NULL; // local host ip address
|
|
int AddrFly;
|
|
|
|
/************************************************************************/
|
|
NTSTATUS
|
|
__cdecl main (
|
|
IN USHORT argc,
|
|
IN PSZ argv[],
|
|
IN PSZ envp[])
|
|
{
|
|
NTSTATUS mstatus;
|
|
USHORT Cindex = 0; // client index
|
|
//UCHAR NBretcode; // NetBIOS call return code
|
|
//USHORT LanaNumber;
|
|
|
|
// argc, argv, envp; // Shut up the compiler
|
|
mstatus = Parse_Cmd_Line(argc, argv);
|
|
if (!NT_SUCCESS(mstatus)) {
|
|
return(1);
|
|
}
|
|
// based on the Xport type set up all the function pointers
|
|
Setup_Function_Pointers();
|
|
|
|
// do IPC dependent initializing
|
|
mstatus = IPC_Initialize(NClients, HostName, SRV);
|
|
if (!NT_SUCCESS(mstatus)) {
|
|
return(1);
|
|
}
|
|
// now create worker threads for handling NetBIOS I/O requests
|
|
printf("Creating %s Server threads...", Xport);
|
|
for (Cindex = 0; Cindex < NClients; Cindex++) {
|
|
// do appropriate per client initialization
|
|
mstatus = IPC_PerClientInit(Cindex,SRV);
|
|
|
|
// use win32 API instead of NT native so that NetBIOS works fine
|
|
Clients[Cindex].c_hThHandle = CreateThread(
|
|
NULL,
|
|
0,
|
|
(LPTHREAD_START_ROUTINE)&SrvService,
|
|
(PUSHORT)&(Clients[Cindex].c_client_num),
|
|
0,
|
|
(LPDWORD)&(Clients[Cindex].c_ThClientID));
|
|
Threads[Cindex] = Clients[Cindex].c_hThHandle; //*get rid of one handle
|
|
printf("%d..",Cindex+1);
|
|
}
|
|
printf("\n");
|
|
|
|
//The main thread should now wait for all the other threads
|
|
if (!mstatus) { // if no error then wait for everyone to sync
|
|
mstatus = Wait_For_Client_Threads();
|
|
if (!NT_SUCCESS(mstatus)) {
|
|
printf ("Failed on wait err=%lx\n",mstatus);
|
|
}
|
|
}
|
|
// now do the cleanup. i.e. clear all memory and close all handles
|
|
IPC_Cleanup(NClients);
|
|
exit(0);
|
|
} // main
|
|
|
|
/************************************************************************/
|
|
/*++
|
|
This routine sets up all the function pointers based on Xport type
|
|
I could just set one pointer to a function table supporting all the
|
|
functions and move all tis initialization in individual init routines.
|
|
Will be done in future.
|
|
--*/
|
|
|
|
VOID
|
|
Setup_Function_Pointers()
|
|
{
|
|
// based on Xport type take the action
|
|
switch(IPCType) {
|
|
case NP:
|
|
IPC_Initialize = NMP_Initialize;
|
|
IPC_PerClientInit = NMP_PerClientInit;
|
|
IPC_Wait_For_Client = NMP_Wait_For_Client;
|
|
IPC_Disconnect_Client = NMP_Disconnect_Client;
|
|
IPC_Cleanup = NMP_Cleanup;
|
|
IPC_Allocate_Memory = NMP_Allocate_Memory;
|
|
IPC_DoHandshake = NMP_DoHandshake;
|
|
IPC_ReadFromIPC = NMP_ReadFromIPC;
|
|
IPC_WriteToIPC = NMP_WriteToIPC;
|
|
IPC_XactIO = NMP_XactIO;
|
|
IPC_Deallocate_Memory = NMP_Deallocate_Memory;
|
|
IPC_ThreadCleanUp = NMP_ThreadCleanUp;
|
|
break;
|
|
case NB:
|
|
IPC_Initialize = NB_Initialize;
|
|
IPC_PerClientInit = NB_PerClientInit;
|
|
IPC_Cleanup = NB_Cleanup;
|
|
IPC_Wait_For_Client = NB_Wait_For_Client;
|
|
IPC_Disconnect_Client = NB_Disconnect_Client;
|
|
IPC_Allocate_Memory = NB_Allocate_Memory;
|
|
IPC_DoHandshake = NB_DoHandshake;
|
|
IPC_ReadFromIPC = NB_ReadFromIPC;
|
|
IPC_WriteToIPC = NB_WriteToIPC;
|
|
IPC_XactIO = NB_XactIO;
|
|
IPC_Deallocate_Memory = NB_Deallocate_Memory;
|
|
IPC_ThreadCleanUp = NB_ThreadCleanUp;
|
|
break;
|
|
|
|
case SCTCP:
|
|
IPC_Initialize = SCTCP_Initialize;
|
|
IPC_PerClientInit = SCTCP_PerClientInit;
|
|
IPC_Cleanup = SCTCP_Cleanup;
|
|
IPC_Wait_For_Client = SCTCP_Wait_For_Client;
|
|
IPC_Disconnect_Client = SCTCP_Disconnect_Client;
|
|
IPC_Allocate_Memory = SCTCP_Allocate_Memory;
|
|
IPC_DoHandshake = SCTCP_DoHandshake;
|
|
IPC_ReadFromIPC = SCTCP_ReadFromIPC;
|
|
IPC_WriteToIPC = SCTCP_WriteToIPC;
|
|
IPC_XactIO = SCTCP_XactIO;
|
|
IPC_Deallocate_Memory = SCTCP_Deallocate_Memory;
|
|
IPC_ThreadCleanUp = SCTCP_ThreadCleanUp;
|
|
break;
|
|
|
|
case SCXNS:
|
|
IPC_Initialize = SCXNS_Initialize;
|
|
IPC_PerClientInit = SCXNS_PerClientInit;
|
|
IPC_Wait_For_Client = SCXNS_Wait_For_Client;
|
|
IPC_Disconnect_Client = SCXNS_Disconnect_Client;
|
|
IPC_Cleanup = SCXNS_Cleanup;
|
|
IPC_Allocate_Memory = SCXNS_Allocate_Memory;
|
|
IPC_DoHandshake = SCXNS_DoHandshake;
|
|
IPC_ReadFromIPC = SCXNS_ReadFromIPC;
|
|
IPC_WriteToIPC = SCXNS_WriteToIPC;
|
|
IPC_XactIO = SCXNS_XactIO;
|
|
IPC_Deallocate_Memory = SCXNS_Deallocate_Memory;
|
|
IPC_ThreadCleanUp = SCXNS_ThreadCleanUp;
|
|
break;
|
|
|
|
case SCUDP:
|
|
IPC_Initialize = SCUDP_Initialize;
|
|
IPC_PerClientInit = SCUDP_PerClientInit;
|
|
IPC_Wait_For_Client = SCUDP_Wait_For_Client;
|
|
IPC_Disconnect_Client = SCUDP_Disconnect_Client;
|
|
IPC_Cleanup = SCUDP_Cleanup;
|
|
IPC_Allocate_Memory = SCUDP_Allocate_Memory;
|
|
IPC_DoHandshake = SCUDP_DoHandshake;
|
|
IPC_ReadFromIPC = SCUDP_ReadFromIPC;
|
|
IPC_WriteToIPC = SCUDP_WriteToIPC;
|
|
IPC_Deallocate_Memory = SCUDP_Deallocate_Memory;
|
|
IPC_ThreadCleanUp = SCUDP_ThreadCleanUp;
|
|
break;
|
|
|
|
case SCIPX:
|
|
IPC_Initialize = SCIPX_Initialize;
|
|
IPC_PerClientInit = SCIPX_PerClientInit;
|
|
IPC_Wait_For_Client = SCIPX_Wait_For_Client;
|
|
IPC_Disconnect_Client = SCIPX_Disconnect_Client;
|
|
IPC_Cleanup = SCIPX_Cleanup;
|
|
IPC_Allocate_Memory = SCIPX_Allocate_Memory;
|
|
IPC_DoHandshake = SCIPX_DoHandshake;
|
|
IPC_ReadFromIPC = SCIPX_ReadFromIPC;
|
|
IPC_WriteToIPC = SCIPX_WriteToIPC;
|
|
IPC_Deallocate_Memory = SCIPX_Deallocate_Memory;
|
|
IPC_ThreadCleanUp = SCIPX_ThreadCleanUp;
|
|
break;
|
|
|
|
case DGNB:
|
|
IPC_Initialize = DGNB_Initialize;
|
|
IPC_PerClientInit = DGNB_PerClientInit;
|
|
IPC_Cleanup = DGNB_Cleanup;
|
|
IPC_Wait_For_Client = DGNB_Wait_For_Client;
|
|
IPC_Disconnect_Client = DGNB_Disconnect_Client;
|
|
IPC_Allocate_Memory = DGNB_Allocate_Memory;
|
|
IPC_DoHandshake = DGNB_DoHandshake;
|
|
IPC_ReadFromIPC = DGNB_ReadFromIPC;
|
|
IPC_WriteToIPC = DGNB_WriteToIPC;
|
|
IPC_XactIO = DGNB_XactIO;
|
|
IPC_Deallocate_Memory = DGNB_Deallocate_Memory;
|
|
IPC_ThreadCleanUp = DGNB_ThreadCleanUp;
|
|
break;
|
|
|
|
default :
|
|
// problem here
|
|
printf("Incorrect Xport selection\n");
|
|
}
|
|
}
|
|
/************************************************************************/
|
|
|
|
/*++
|
|
This routine makes the main thread wait for all the client threads to
|
|
exit.
|
|
--*/
|
|
|
|
NTSTATUS
|
|
Wait_For_Client_Threads(VOID)
|
|
{
|
|
NTSTATUS wstatus;
|
|
DWORD MTimeout = INFINITE;
|
|
|
|
printf("Main thread waiting for Client threads ");
|
|
|
|
wstatus = WaitForMultipleObjectsEx(NClients,
|
|
Threads,
|
|
TRUE, // Wait for all objects
|
|
MTimeout, // Default timeout
|
|
TRUE); // Alertable
|
|
printf(".. Wait over with status:%lx\n",wstatus);
|
|
|
|
if (!NT_SUCCESS(wstatus)) {
|
|
printf ("Failed on wait err=%lx\n",wstatus);
|
|
}
|
|
if (ThreadError) {
|
|
printf("Thread Error: %d \n", ThreadError);
|
|
}
|
|
return wstatus;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/*++
|
|
This routine is the thread service routine. It first waits for clients to
|
|
connect. Then it does a handshake to gather all the test info. Then it
|
|
services all requests.
|
|
--*/
|
|
|
|
VOID
|
|
SrvService( // provides server service
|
|
IN PUSHORT pTindex)
|
|
{
|
|
NTSTATUS tstatus;
|
|
USHORT tCindex;
|
|
//UCHAR RetCode;
|
|
BOOLEAN keepdoing;
|
|
BOOLEAN Terminate;
|
|
ULONG RecvLen = 0;
|
|
ULONG SendLen = 0;
|
|
ULONG Iterations = 0;
|
|
BOOLEAN First = TRUE;
|
|
|
|
tCindex = *pTindex;
|
|
keepdoing = TRUE;
|
|
ThreadError = 0;
|
|
Failure = FALSE;
|
|
|
|
do { // keep doing the work till it receives 'E' from client
|
|
//DbgPrint("Srv:Waiting for a client %d\n",tCindex);
|
|
// First post listen for a client's connection request
|
|
tstatus = IPC_Wait_For_Client(tCindex);
|
|
|
|
ThreadError = 1;
|
|
FAIL_CHECK(PERFSRV, " Wait for Client" , tstatus);
|
|
|
|
// now the thread has been connected
|
|
|
|
// now do the handshake to read the request message
|
|
tstatus = IPC_DoHandshake(tCindex,SRV);
|
|
|
|
ThreadError = 2;
|
|
FAIL_CHECK(PERFSRV, " Doing Handshake" , tstatus);
|
|
|
|
// allocate memory for data buffers and start the I/O
|
|
|
|
tstatus = IPC_Allocate_Memory(tCindex);
|
|
|
|
ThreadError = 3;
|
|
FAIL_CHECK(PERFSRV, " Memory Allocation" , tstatus);
|
|
|
|
|
|
// check if the client wants to quit.
|
|
// Set up number of iterations
|
|
|
|
Iterations = Clients[tCindex].c_reqbuf.Iterations;
|
|
|
|
Terminate = FALSE;
|
|
First = TRUE;
|
|
|
|
while (Iterations--) {
|
|
if (Clients[tCindex].c_reqbuf.TestCmd = 'P') {
|
|
// now the server has to first receive X messages and then
|
|
// send Y messages.
|
|
|
|
MyDbgPrint("Srv:Reading \n");
|
|
tstatus = IPC_ReadFromIPC( tCindex, &RecvLen,SRV);
|
|
|
|
ThreadError = 4;
|
|
FAIL_CHECK(PERFSRV, " Read from IPC" , tstatus);
|
|
|
|
|
|
// We can check for recv length and data integrity out here
|
|
|
|
MyDbgPrint("Srv:Writing \n");
|
|
tstatus = IPC_WriteToIPC( tCindex, &SendLen, SRV);
|
|
|
|
ThreadError = 5;
|
|
FAIL_CHECK(PERFSRV, " Read from IPC" , tstatus);
|
|
}
|
|
else { // for 'U' or 'T' do transct I/O
|
|
// We have to do something different for NetBIOS as
|
|
// it sould first post a receive and then do RecvSend for
|
|
// Other iterations. for Client this works fine as it always
|
|
// does send/Recv.
|
|
|
|
|
|
tstatus = IPC_XactIO( tCindex, &SendLen, &RecvLen, SRV,First);
|
|
|
|
// FAIL_CHECK(PERFSRV, " Xact IPC" , tstatus);
|
|
ThreadError = 6;
|
|
if (!NT_SUCCESS(tstatus)) {
|
|
if ((tstatus != STATUS_PIPE_BROKEN) &&
|
|
(tstatus != STATUS_INVALID_PIPE_STATE)) {
|
|
|
|
//DbgPrint("Error in XactIO: %lx \n", tstatus);
|
|
}
|
|
break;
|
|
}
|
|
First = FALSE;
|
|
}
|
|
}
|
|
// now we are done with the current tests so do the cleanup and
|
|
// start all over again
|
|
tstatus = IPC_Disconnect_Client(tCindex);
|
|
|
|
ThreadError = 7;
|
|
FAIL_CHECK(PERFSRV, " Disconnect Client " , tstatus);
|
|
|
|
tstatus = IPC_Deallocate_Memory(tCindex);
|
|
|
|
ThreadError = 8;
|
|
FAIL_CHECK(PERFSRV, " Memory Deallocation" , tstatus);
|
|
|
|
ThreadError = 0;
|
|
|
|
} while (keepdoing); // till we receive 'E' or bad status
|
|
|
|
// we should check if we have to deallocate all the buffers.
|
|
// Do all the thread cleanup work
|
|
|
|
tstatus = IPC_ThreadCleanUp(tCindex);
|
|
}
|
|
/************************************************************************/
|
|
VOID
|
|
Cleanup(VOID)
|
|
{
|
|
USHORT Cindex = 0; // client index
|
|
NTSTATUS cstatus;
|
|
NTSTATUS exitstatus = 0;
|
|
|
|
for (Cindex = 0; Cindex < NClients; Cindex++) {
|
|
// terminate the thread
|
|
cstatus = TerminateThread(Clients[Cindex].c_hThHandle, exitstatus);
|
|
|
|
if (!NT_SUCCESS(cstatus)) {
|
|
printf("Failed to terminate thread no:%d err=%lx\n", Cindex,cstatus);
|
|
}
|
|
}
|
|
printf("Terminated All Threads\n");
|
|
}
|
|
/************************************************************************/
|
|
NTSTATUS
|
|
Parse_Cmd_Line(USHORT argc, CHAR *argv[])
|
|
{
|
|
USHORT i;
|
|
PCHAR s;
|
|
NTSTATUS pstatus = 0L;
|
|
BOOLEAN doingok = TRUE;
|
|
|
|
if (argc > 5) {
|
|
printf("Too many arguments \n");
|
|
pstatus = -1L;
|
|
}
|
|
// copy default Xport name : Nmp
|
|
strncpy(Xport, NamePipe, 8);
|
|
|
|
for (i=1; (doingok) && (i< argc); i++) {
|
|
|
|
s = argv[i];
|
|
|
|
if ((*s == '/') &&(*(s+2) == ':'))
|
|
{
|
|
s++;
|
|
|
|
switch(*s) {
|
|
|
|
case 'b' :
|
|
case 'B' :
|
|
LanaBase = (USHORT)atoi(s+2);
|
|
break;
|
|
|
|
case 'c' : // number of Clients
|
|
case 'C' :
|
|
NClients = (USHORT)atoi(s+2);
|
|
break;
|
|
|
|
case 'h' :
|
|
case 'H' :
|
|
HostName = (PCHAR) malloc(strlen(s+2)+1);
|
|
strcpy(HostName,s+2);
|
|
// check for the validity of the address
|
|
break;
|
|
|
|
case 'l' :
|
|
case 'L' :
|
|
LanaCount = (USHORT)atoi(s+2);
|
|
break;
|
|
|
|
case 'p' : // NamedPipe mode
|
|
case 'P' :
|
|
switch(*(s+2)) {
|
|
case 'm':
|
|
case 'M':
|
|
PipeType = PIPE_TYPE_MESSAGE;
|
|
PipeMode = PIPE_READMODE_MESSAGE;
|
|
break;
|
|
|
|
case 's':
|
|
case 'S':
|
|
PipeType = PIPE_TYPE_BYTE;
|
|
PipeMode = PIPE_READMODE_BYTE;
|
|
break;
|
|
|
|
default:
|
|
doingok = FALSE;
|
|
break;
|
|
}
|
|
|
|
case 'x' : // Xport Type
|
|
case 'X' :
|
|
strncpy(Xport, (PUCHAR) (s+2), 8);
|
|
|
|
if (!_stricmp(Xport,NamePipe)) {
|
|
IPCType = NP;
|
|
break;
|
|
}
|
|
|
|
if (!_stricmp(Xport,NetBIOS)) {
|
|
IPCType = NB;
|
|
break;
|
|
}
|
|
|
|
if (!_stricmp(Xport,SocketXNS)) {
|
|
IPCType = SCXNS;
|
|
AddrFly = AF_NS;
|
|
break;
|
|
}
|
|
|
|
if (!_stricmp(Xport,SocketTCP)) {
|
|
IPCType = SCTCP;
|
|
AddrFly = AF_INET;
|
|
break;
|
|
}
|
|
|
|
if (!_stricmp(Xport,UDP)) {
|
|
IPCType = SCUDP;
|
|
AddrFly = AF_INET;
|
|
break;
|
|
}
|
|
|
|
if (!_stricmp(Xport,IPX)) {
|
|
IPCType = SCIPX;
|
|
AddrFly = AF_NS;
|
|
break;
|
|
}
|
|
|
|
if (!_stricmp(Xport,DGNetBIOS)) {
|
|
IPCType = DGNB;
|
|
break;
|
|
}
|
|
|
|
// bad choice of Xport
|
|
doingok = FALSE;
|
|
break;
|
|
|
|
default :
|
|
doingok = FALSE;
|
|
|
|
}
|
|
}
|
|
else {
|
|
doingok = FALSE;
|
|
}
|
|
}
|
|
|
|
if (!doingok) {
|
|
Usage(argv[0]);
|
|
pstatus = -1L;
|
|
}
|
|
else {
|
|
if (((IPCType == SCTCP)|| (IPCType == SCXNS)) && (HostName == NULL)) {
|
|
printf("Please enter Host address \n");
|
|
pstatus = -1L;
|
|
}
|
|
}
|
|
|
|
return(pstatus);
|
|
}
|
|
|
|
/************************************************************************/
|
|
VOID Usage(char * PrgName)
|
|
{
|
|
|
|
fprintf(stderr, "Usage: %s [/c: ] [/x: ] \n", PrgName);
|
|
fprintf(stderr, " Opt Default Defines\n");
|
|
fprintf(stderr, " === ======= =======\n");
|
|
fprintf(stderr, " /c: 1 Number of clients\n");
|
|
fprintf(stderr, " /x: Nmp Xport(IPC)type\n");
|
|
fprintf(stderr, " Nmp/NetB/SockTCP/\n");
|
|
fprintf(stderr, " SockXNS/UDP/IPX/DGNetB\n");
|
|
fprintf(stderr, " /p: m Nmp : Pip Type m/s\n");
|
|
fprintf(stderr, " /l: 0 NetB: lana count\n");
|
|
fprintf(stderr, " /b: 0 NetB: lana base\n");
|
|
fprintf(stderr, " /h: NULL Server Name/Host IP addr.\n");
|
|
}
|
|
/************************************************************************/
|