//+------------------------------------------------------------------------- // // 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"); } /************************************************************************/