SRVHSHAK.C


/******************************************************************************\
* This is a part of the Microsoft Source Code Samples.
* Copyright 1995 - 1997 Microsoft Corporation.
* All rights reserved.
* This source code is only intended as a supplement to
* Microsoft Development Tools and/or WinHelp documentation.
* See these sources for detailed information regarding the
* Microsoft samples programs.
\******************************************************************************/

/*++

Copyright (c) 1997 Microsoft Corporation

Module Name:

SrvHShak.c

Abstract:

The server component of Remote. Handshake with
client at start of session.


Author:

Dave Hart 30 May 1997

Environment:

Console App. User mode.

Revision History:

--*/

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <io.h>
#include <string.h>
#include "Remote.h"
#include "Server.h"




VOID
FASTCALL
HandshakeWithRemoteClient(
PREMOTE_CLIENT pClient
)
{
pClient->ServerFlags |= SFLG_HANDSHAKING;

AddClientToHandshakingList(pClient);

//
// Read hostname from client
//

ZeroMemory(
&pClient->ReadOverlapped,
sizeof(pClient->ReadOverlapped)
);

if ( ! ReadFileEx(
pClient->PipeReadH,
pClient->Name,
HOSTNAMELEN - 1,
&pClient->ReadOverlapped,
ReadClientNameCompleted
)) {

CloseClient(pClient);
}
}

VOID
WINAPI
ReadClientNameCompleted(
DWORD dwError,
DWORD cbRead,
LPOVERLAPPED lpO
)
{
PREMOTE_CLIENT pClient;
SESSION_STARTREPLY ssr;

pClient = CONTAINING_RECORD(lpO, REMOTE_CLIENT, ReadOverlapped);

if (pClient->ServerFlags & SFLG_CLOSING) {

return;
}

if (dwError) {
CloseClient(pClient);
return;
}

if ((HOSTNAMELEN - 1) != cbRead) {
printf("ReadClientNameCompleted read %d s/b %d.\n", cbRead, (HOSTNAMELEN - 1));
CloseClient(pClient);
return;
}

//
// The client name read is 15 bytes always. The last four
// should match MAGICNUMBER, which conveniently has the
// low byte zeroed to terminate the client name after 11
// characters.
//

if (MAGICNUMBER != *(DWORD *)&pClient->Name[11]) {

pClient->Name[11] = 0;
CloseClient(pClient);
return;
}

//
// Now we can tell if this is a single-pipe or two-pipe
// client, because single-pipe clients replace the
// first byte of the computername with the illegal
// character '?'.
//

if ('?' == pClient->Name[0]) {

pClient->PipeWriteH = pClient->PipeReadH;

TRACE(CONNECT, ("Client %d pipe %x is single-pipe.\n", pClient->dwID, pClient->PipeWriteH));

//
// In order for things to work reliably for 2-pipe clients
// when there are multiple remote servers on the same pipename,
// we need to tear down the listening OUT pipe and recreate it so
// that the oldest listening IN pipe will be from the same process
// as the oldest listening OUT pipe.
//

if (1 == cConnectIns) {

TRACE(CONNECT, ("Recycling OUT pipe %x as well for round-robin behavior.\n",
hPipeOut));

CANCELIO(hPipeOut);
DisconnectNamedPipe(hPipeOut);
CloseHandle(hPipeOut);
hPipeOut = INVALID_HANDLE_VALUE;
bOutPipeConnected = FALSE;

CreatePipeAndIssueConnect(OUT_PIPE);
}

} else {

if ( ! bOutPipeConnected ) {

printf("Remote: %x two-pipe client connected to IN pipe but not OUT?\n", pClient);
CloseClient(pClient);
return;
}

bOutPipeConnected = FALSE;

if (INVALID_HANDLE_VALUE != hConnectOutTimer) {
pfnCancelWaitableTimer(hConnectOutTimer);
}

pClient->PipeWriteH = hPipeOut;
hPipeOut = INVALID_HANDLE_VALUE;

TRACE(CONNECT, ("Client %d is dual-pipe IN %x OUT %x.\n", pClient->dwID, pClient->PipeReadH, pClient->PipeWriteH));

CreatePipeAndIssueConnect(OUT_PIPE);
}

TRACE(SHAKE, ("Read client name %s\n", pClient->Name));

//
// Send our little pile of goodies to the client
//

ssr.MagicNumber = MAGICNUMBER;
ssr.Size = sizeof(ssr);
ssr.FileSize = dwWriteFilePointer;

//
// Copy ssr structure to a buffer that will be around
// for the entire I/O.
//

CopyMemory(pClient->WriteBuffer, &ssr, sizeof(ssr));

if ( ! WriteFileEx(
pClient->PipeWriteH,
pClient->WriteBuffer,
sizeof(ssr),
&pClient->WriteOverlapped,
WriteServerReplyCompleted
)) {

CloseClient(pClient);
}
}


VOID
WINAPI
WriteServerReplyCompleted(
DWORD dwError,
DWORD cbWritten,
LPOVERLAPPED lpO
)
{
PREMOTE_CLIENT pClient;

pClient = CONTAINING_RECORD(lpO, REMOTE_CLIENT, WriteOverlapped);

if (pClient->ServerFlags & SFLG_CLOSING) {

return;
}

if (HandleSessionError(pClient, dwError)) {
return;
}

TRACE(SHAKE, ("Wrote server reply\n"));

//
// Read the size of the SESSION_STARTUPINFO the client is
// sending us, to deal gracefully with different versions
// on client and server.
//

if ( ! ReadFileEx(
pClient->PipeReadH,
pClient->ReadBuffer,
sizeof(DWORD),
&pClient->ReadOverlapped,
ReadClientStartupInfoSizeCompleted
)) {

CloseClient(pClient);
}
}


VOID
WINAPI
ReadClientStartupInfoSizeCompleted(
DWORD dwError,
DWORD cbRead,
LPOVERLAPPED lpO
)
{
PREMOTE_CLIENT pClient;
DWORD dwSize;

pClient = CONTAINING_RECORD(lpO, REMOTE_CLIENT, ReadOverlapped);

if (HandleSessionError(pClient, dwError)) {

return;
}

if (cbRead != sizeof(DWORD)) {

CloseClient(pClient);
return;
}

//
// Sanity check the size
//

dwSize = *(DWORD *)pClient->ReadBuffer;

if (dwSize > 1024) {
CloseClient(pClient);
return;
}

//
// Squirrel away the size in the write buffer,
// since during handshaking we never have both a
// read and write pending this is OK.
//

*(DWORD *)pClient->WriteBuffer = dwSize;

TRACE(SHAKE, ("Read client reply size %d\n", dwSize));

//
// Read the rest of the SESSION_STARTUPINFO into the read buffer
// after the size.
//

RtlZeroMemory(
&pClient->ReadOverlapped,
sizeof(pClient->ReadOverlapped)
);

if ( ! ReadFileEx(
pClient->PipeReadH,
pClient->ReadBuffer + sizeof(DWORD),
dwSize - sizeof(DWORD),
&pClient->ReadOverlapped,
ReadClientStartupInfoCompleted
)) {

CloseClient(pClient);
}
}


VOID
WINAPI
ReadClientStartupInfoCompleted(
DWORD dwError,
DWORD cbRead,
LPOVERLAPPED lpO
)
{
PREMOTE_CLIENT pClient;
DWORD dwSize;
SESSION_STARTUPINFO ssi;
char Buf[256];

pClient = CONTAINING_RECORD(lpO, REMOTE_CLIENT, ReadOverlapped);

if (HandleSessionError(pClient, dwError)) {

return;
}

dwSize = *(DWORD *)pClient->WriteBuffer;

if (cbRead != (dwSize - sizeof(ssi.Size))) {

CloseClient(pClient);
return;
}

CopyMemory(&ssi, pClient->ReadBuffer, min(dwSize, sizeof(ssi)));

CopyMemory(pClient->Name, ssi.ClientName, sizeof(pClient->Name));
pClient->Flag = ssi.Flag;

if (ssi.Version != VERSION) {

printf("Remote Warning: Server Version=%d Client Version=%d for %s\n", VERSION, ssi.Version, pClient->Name);
}

TRACE(SHAKE, ("Read client info, new name %s, %d lines\n", pClient->Name, ssi.LinesToSend));


//
// Set temp file position according to the client's
// requested lines to send. The heuristic of 45 chars
// per average line is used by the client. However since old clients
// hardcode this knowledge and sit and spin trying to read that many
// bytes before completing initialization, and because we might not send
// that many due to stripping BEGINMARK and ENDMARK characters, we
// use 50 chars per line to calculate the temp file position in hopes
// the extra bytes will overcome the missing MARK characters.
//

pClient->dwFilePos = dwWriteFilePointer > (ssi.LinesToSend * 50)
? dwWriteFilePointer - (ssi.LinesToSend * 50)
: 0;

//
// This client's ready to roll.
//

pClient->ServerFlags &= ~SFLG_HANDSHAKING;

MoveClientToNormalList(pClient);

//
// Start read operation against this client's input.
//

StartReadClientInput(pClient);

//
// Announce the connection.
//

sprintf(Buf,
"\n**Remote: Connected to %s %s%s [%s]\n",
pClient->Name,
pClient->UserName,
(pClient->PipeReadH != pClient->PipeWriteH)
? " (two pipes)"
: "",
GetFormattedTime(TRUE));

if (WriteFileSynch(hWriteTempFile,Buf,strlen(Buf),&dwSize,dwWriteFilePointer,&olMainThread)) {
dwWriteFilePointer += dwSize;
StartServerToClientFlow();
}

//
// Start write cycle for client output from the temp
// file.
// not needed because of StartServerToClientFlow() just above
// StartReadTempFile(pClient);

}