Design for eliminating the 64 client limitation of the RAS Server Service ========================================================================= The general idea is to have the Supervisor spawn Gateway processes that can handle up to N remote NetBIOS clients. The Supervisor and Gateway processes will communicate via named pipes. When all Gateway processes have as many clients as they can handle, a new one will be spawned to handle the next N clients. This will be accomplished with 2 new modules - a Gateway substitute, or proxy, DLL that the Supervisor will link to and a Supervisor proxy process that wil link to the Gateway DLL. The Gateway proxy will be called RASGPRXY.DLL. The Supervisor proxy will be called RASSPRXY.EXE. RASGPRXY.DLL ============ RASGPRXY.DLL will maintain a list of Process Control Blocks (PCBs), one for each Gateway process. typedef struct _CLIENT_INFO { HPORT hPort; } CLIENT_INFO, *PCLIENT_INFO; typedef struct _PROCESS_CONTROL_BLOCK { DWORD cClients; // # of clients currently being serviced by this proc HANDLE hPipe; // Named Pipe used for communicating with this proc CLIENT_INFO ClientInfo; OVERLAPPED ol; // For overlapped (async) i/o. CHAR ReadBuffer[]; // Recv buffer for Named Pipe reads struct _PROCESS_CONTROL_BLOCK *pNextPCB; } PROCESS_CONTROL_BLOCK, PCB, *PPROCESS_CONTROL_BLOCK, *PPCB; RASGPRXY.DLL will export entry points that coincide directly with RASGTWY.DLL. This will allow the Supervisor to link to it and work without any change to the Supervisor. The entry points are as follows: NbGatewayStart NbGatewayProjectClient NbGatewayStartClient NbGatewayStopClient NbGatewayTimer NbGatewayRemoteListen There will also be one thread at all times for handling reads on the named pipe. This thread also handles spawning off new processes and establishing pipe connections with those processes. We have a separate thread for this because the ConnectNamedPipe call blocks and we don't want the Supervisor thread to block. NbGatewayStart will handle 1. Initializing the DLL o Create necessary mutexes (1 to serialize access to list of PCBs, 1 to serialize access to list of projection info structs) o Create WriteCompletion Event (will be set when WriteFile operation completes. o Create New Process Event (signals Read thread that a new process needs to be spawned. o Starting the Read thread o Initialize globals (# ports/process and addr of Supervisor sendmsg routine o Load the Gateway parameters which will be passed to each new Gateway process o Do security agent check NbGatewayProjectClient will 1. Find a PCB of a process that can handle a new client. If none exist, signal the NewProcessEvent and queue up projection info. 2. Write a ProjectClient message on the named pipe for that process. NbGatewayStartClient will 1. Find the PCB of the process that is handling this client. 2. Write a StartClient message on the named pipe for that process. NbGatewayStopClient will 1. Find the PCB of the process that is handling this client. 2. Write a StopClient message on the named pipe for that process. NbGatewayTimer will merely return - it won't do anything. In the normal case, it calls the Gateway timer function. Instead, the Gateway process will have its own timer thread that will call the Gateway timer function as appropriate. NbGatewayRemoteListen will merely return 1 if RemoteListen is on and 0 if not. The Read thread will for (;;) { for all processes { if (!pcb->read_posted) { pcb->read_posted = TRUE; ReadFileEx(pcb->hPipe); } WaitForSingleObject(NewProcessEvent); if (new Gateway process) { NewProcess(); } if (Read Completion) { for all reads { if (read completed) { send appropriate msg to Supervisor; pcb->read_posted = FALSE; if (DisconnectRequest or ClientStopped) { if (!--ClientCount) { Write Terminate Message on pipe for this proc Update PCB list; } } } } } } } Spawning a new process involves: o Create a named pipe instance for communicating with this process o Allocating and initializing a new PCB o Spawn the new process o Establish named pipe connection with new process o Post a read on the new pipe handle o Write start up parameters on the named pipe (# ports, etc. Note that bogus port handles and names will be used since they are not known at this point) Read completion is handled by a ReadCompletion routine. This routine basically sends the message received to the Supervisor, does any house- keeping, and posts a new read as necessary. RASSRV.EXE ========== Two changes will be needed for the existing supervisor: 1) Read a new parameter - MaxClientsPerGatewayInstance - from the Registry at start up time. Legal range is 1-64. 2) Call NbGatewayStart with this value instead of the number of ports in the system. RASSPRXY.EXE ============ main() 1. OpenFile for named pipe and read startup parameters from it NOTE: The port handles and port names are bogus at this point 2. Load gateway (RASGTWY.DLL) 3. Start Read thread 4. Start Timer thread The Read thread for (;;) { ReadFile(hPipe) Unpackage parameters and dispatch command to proper gateway procedure - Start gateway - Project Client - Start Client - Stop Client - Remote Listen - Terminate } The Timer thread for (;;) { Sleep 1000 msec Call gateway timer function } Send Message Routine WriteFile(hPipe) RASGTWY.DLL =========== Needs a minor change to the Project Client API. This API needs to accept the port name because the port names received over the named pipe were bogus. RASMAN, NETBIOS, NBF, and RASHUB ================================ The problem here is that NETBIOS can only support up to 255 lanas. RAS needs a lana per client and we're proposing infinite clients here. So we need infinite lanas. Also there is currently one NBF=>RASHUB binding for every port in the system that today has a 1:1 correspondence with the lana numbers. We only want one NBF=>RASHUB binding in the new system. Proposal 1: ----------- NetBIOS lana map contains only info for LAN - nothing for WAN. There would then be a new Reset NCB that would return a lana to the calling process that is currently unused by that process. This new Reset NCB would contain a pointer in the NCB_BUFFER field to 12 byte handle which is formed by the 2 6-byte local, remote addresess assigned by RASHUB at connection time. The Reset NCB would then cause NetBIOS to do a TDI.OPEN.ADDRESS to NBF using this handle. NetBIOS would have to dynamically manage memory for its lana and Device tables as devices got connected/disconnected. RASHUB would return these addresses to RASMAN (the one that told it to route). RASMAN would hand them back to the Supervisor who would then pass them to the Gateway, which submits the Reset NCB. RASHUB would have passed the proper info (local, remote addresses) to NBF at line-up time so that NBF could do the right thing at Reset time. Each Gateway process would be able to use lanas in use by other Gateway processes without any conflict, so we effectively get infinite lanas. And we would only have one NBF=>RASHUB binding. Proposal 2: ----------- NetBIOS lana map contains info for LAN plus N (max number of clients that one Gateway process can handle) place holders for WAN lanas. These lanas do not correspond 1:1 with NBF=>RASHUB bindings (there would still only be one NBF=>RASHUB binding). The Gateway process would have knowledge of these lana numbers and manage which ones it was using and which were still available to it. At connect time, the Gateway would use an available lana and submit an Action NCB that would contain a device name of the form \\Device\RasHubNNN. This Action NCB would allow NetBIOS to store the device name in it's Device table that was allocated at init time and contained only place holders. (The Device table would then be unchanged until the next Action NCB.) After the Action NCB, Reset adapter is done as usual. This Reset causes NetBIOS to do a TDI.OPEN.ADDRESS using the correct device name. Each Gateway process would have all these lanas available to it without any conflict, so we effectively get infinite lanas.