Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

695 lines
17 KiB

/* File: D:\WACKER\comwsock\comnvt.c (Created: 14-Feb-1996)
*
* Copyright 1996 by Hilgraeve Inc. -- Monroe, MI
* All rights reserved
*
* $Revision: 6 $
* $Date: 3/26/02 5:05p $
*/
//#define DEBUGSTR
#include <windows.h>
#pragma hdrstop
#include <tdll\stdtyp.h>
#if defined (INCL_WINSOCK)
#include <tdll\session.h>
#include <tdll\com.h>
#include <tdll\comdev.h>
#include <comstd\comstd.hh>
#include "comwsock.hh"
#include <tdll\assert.h>
#include <tdll\htchar.h>
#include <emu\emu.h>
static PSTOPT LookupOption( ST_STDCOM *hhDriver, ECHAR mc );
// This is the "Network Virtual Terminal" emulation, i.e., the code
// that handles Telnet option negotiations. WinSockNetworkVirtualTerminal
// is called to check incoming data to see if there is
// a Telnet command in there.
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
* FUNCTION:
* WinSockCreateNVT
*
* DESCRIPTION:
* This function is called to create the necessary hooks and stuff to create
* a Telnet NVT(network virtual terminal).
*
* PARAMETERS:
* hhDriver -- private connection handle
*
* RETURNS:
* Nothing.
*
* AUTHOR
* mcc 01/09/96 (Ported from NPORT)
*/
VOID WinSockCreateNVT(ST_STDCOM * hhDriver)
{
int ix;
DbgOutStr("WinSockCreateNVT\r\n", 0,0,0,0,0);
hhDriver->NVTstate = NVT_THRU;
hhDriver->stMode[ECHO_MODE].option = TELOPT_ECHO;
hhDriver->stMode[SGA_MODE].option = TELOPT_SGA;
hhDriver->stMode[TTYPE_MODE].option = TELOPT_TTYPE;
hhDriver->stMode[BINARY_MODE].option = TELOPT_BINARY;
hhDriver->stMode[NAWS_MODE].option = TELOPT_NAWS;
for (ix = 0; ix < MODE_MAX; ++ix) //jkh 6/18/98
{
hhDriver->stMode[ix].us = hhDriver->stMode[ix].him = NO;
hhDriver->stMode[ix].usq = hhDriver->stMode[ix].himq = EMPTY;
}
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
* FUNCTION:
* WinSockReleaseNVT
*
* DESCRIPTION:
* This function is currently a stub
*
* PARAMETERS:
* hhDriver -- private connection handle
*
* RETURNS:
* Nothing.
*/
VOID WinSockReleaseNVT(ST_STDCOM * hhDriver)
{
DbgOutStr("WS releaseNVT\r\n", 0,0,0,0,0);
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
* FUNCTION:
* WinSockGotDO
*
* DESCRIPTION:
* Handles the case of us agreeing that the other side should enable an option
*
* PARAMETERS:
* hhDriver -- private handle for this connection driver
* pstO -- Telnet options data structure
*
* RETURNS:
* nothing
*
* AUTHOR:
* mcc 01/09/96 (Ported from NPORT)
*/
VOID WinSockGotDO (ST_STDCOM * hhDriver, const PSTOPT pstO)
{
DbgOutStr("Got DO: %lx\r\n", pstO->option, 0,0,0,0);
switch (pstO->us)
{
case NO:
// We were off, but server want's us on so we agree and respond
pstO->us = YES;
WinSockSendMessage(hhDriver, WILL, pstO->option);
break;
case YES:
// Ignore, we're already enabled
break;
case WANTNO:
// This is an error,we had sent a WON'T and they responded with DO
if (pstO->usq == EMPTY)
pstO->us = NO; // leave option as WE wanted it
else if (pstO->usq == OPPOSITE) // we were going to enable anyway so turn us on
pstO->us = YES;
pstO->usq = EMPTY;
break;
case WANTYES:
// They're agreeing with our earlier WILL
if (pstO->usq == EMPTY)
{
pstO->us = YES; // all done negotiating
}
else if (pstO->usq == OPPOSITE)
{
// we changed our mind while negotiating, renegotiate for WONT
pstO->us = WANTNO;
pstO->usq = EMPTY;
WinSockSendMessage(hhDriver, WONT, pstO->option);
}
break;
default:
assert(FALSE);
break;
}
// If the NAWS option was just turned on, we must respond with our terminal size
// right away. (The WinsockSendNAWS function will check whether the option is now
// on or off).
if ( pstO->option == TELOPT_NAWS )
WinSockSendNAWS( hhDriver );
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
* FUNCTION:
* WinSockGotWILL
*
* DESCRIPTION:
* Handles the case of getting a WILL response from the remote Telnet,
* indicating that an option will be enabled
*
* PARAMETERS:
* hhDriver -- private handle for this connection driver
* pstO -- Telnet options data structure
*
* RETURNS:
* nothing
*
* AUTHOR:
* mcc 01/09/96 (Ported from NPORT)
*/
VOID WinSockGotWILL(ST_STDCOM * hhDriver, const PSTOPT pstO)
{
DbgOutStr("Got WILL: %lx\r\n", pstO->option, 0,0,0,0);
switch(pstO->him)
{
case NO:
// He was off but want's to be on so agree and respond
pstO->him = YES;
WinSockSendMessage(hhDriver, DO, pstO->option);
break;
case YES:
// He was already on so do nothing
break;
case WANTNO:
// Error: he responded to our DONT with a WILL
if (pstO->himq == EMPTY)
pstO->him = NO;
else if (pstO->himq == OPPOSITE)
pstO->him = YES;
pstO->himq = EMPTY;
break;
case WANTYES:
// He responded to our DO with a WILL (life is good!)
if (pstO->himq == EMPTY)
{
pstO->him = YES;
}
else if (pstO->himq == OPPOSITE)
{
// He agreed to our DO, but we changed our mind -- renegotiate
pstO->him = WANTNO;
pstO->himq = EMPTY;
WinSockSendMessage(hhDriver, DONT, pstO->option);
}
break;
default:
assert(FALSE);
break;
}
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
* FUNCTION:
* WinSockGotDONT
*
* DESCRIPTION:
* Handles the case of getting a DONT option from the remote Telnet,
* indicating a request not to implement a particular option
*
* PARAMETERS:
* hhDriver Private driver handle
* pstO
*
* RETURNS:
* nothing
*/
VOID WinSockGotDONT(ST_STDCOM * hhDriver, const PSTOPT pstO)
{
DbgOutStr("Got DONT: %lx\r\n", pstO->option, 0,0,0,0);
switch (pstO->us)
{
case NO:
// Got a DONT while we were already off, just ignore
break;
case YES:
// Got a DONT while we were on, agree and respond
pstO->us = NO;
WinSockSendMessage(hhDriver, WONT, pstO->option);
break;
case WANTNO:
// He responded to our WONT with a DONT (how appropriate)
if (pstO->usq == EMPTY)
{
pstO->us = NO;
}
else if (pstO->usq == OPPOSITE)
{
// He agreed to our earlier WONT but we changed our mind
pstO->us = WANTYES;
pstO->usq = EMPTY;
WinSockSendMessage(hhDriver, WILL, pstO->option);
}
break;
case WANTYES:
// He responded to our WILL with a DONT, so leave it off
if (pstO->usq == EMPTY)
{
pstO->us = NO;
}
else if (pstO->usq == OPPOSITE)
{
// If he'd agreed to our WILL, we'd have immediately asked for WONT
// but since he didn't agree, we already got what we wanted
pstO->us = NO;
pstO->usq = EMPTY;
}
break;
default:
assert(FALSE);
break;
}
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
* FUNCTION:
*
* DESCRIPTION:
*
* PARAMETERS:
*
* RETURNS:
*/
VOID WinSockGotWONT(ST_STDCOM * hhDriver, const PSTOPT pstO)
{
DbgOutStr("Got WONT: %lx\r\n", pstO->option, 0,0,0,0);
switch (pstO->him)
{
case NO:
// Got a WONT while he was already off, just ignore
break;
case YES:
// He wants to change from on to off, agree and respond
pstO->him = NO;
WinSockSendMessage(hhDriver, DONT, pstO->option);
break;
case WANTNO:
// He responded to our DONT with a WONT (how agreeable of him)
if (pstO->himq == EMPTY)
{
pstO->him = NO;
}
else if (pstO->himq == OPPOSITE)
{
// He agreed to our DONT but we changed our mind while waiting
pstO->him = WANTYES;
pstO->himq = EMPTY;
WinSockSendMessage(hhDriver, DO, pstO->option);
}
break;
case WANTYES:
// He responded to our DO with a WONT -- let the wimp have his way
if (pstO->himq == EMPTY)
{
pstO->him = NO;
}
else if (pstO->himq == OPPOSITE)
{
// If he'd agreed to our DO, we'd have asked for a DONT so
// now we're happy anyway
pstO->him = NO;
pstO->himq = EMPTY;
}
break;
default:
assert(FALSE);
break;
}
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
* FUNCTION:
* WinSockNetworkVirtualTerminal
*
* DESCRIPTION:
* called from CLoop to handle Telnet option negotiation
*
* PARAMETERS:
* mc The current character being processed
* pD Pointer to Winsock connection driver private handle
*
* RETURNS:
* NVT_DISCARD if mc is to be discarded
* NVT_KEEP if mc is to be processed further
*
* AUTHOR
* mcc 01/09/96 (mostly from NPORT)
*/
int FAR PASCAL WinSockNetworkVirtualTerminal(ECHAR mc, void *pD)
{
ST_STDCOM * hhDriver = (ST_STDCOM *)pD;
#ifdef INCL_USER_DEFINED_BACKSPACE_AND_TELNET_TERMINAL_ID
STEMUSET stEmuSet;
#else
int nTtype;
#endif
LPSTR pszPtr;
UCHAR acTerm[64];
HEMU hEmu;
HSESSION hSession;
PSTOPT pstTelnetOpt;
assert(hhDriver);
//DbgOutStr("NVT %d %c(0x%x = %d)\n", hhDriver->NVTstate,
// ((mc == 0)? ' ': mc), mc, mc,0);
switch (hhDriver->NVTstate)
{
case NVT_THRU:
if (mc == IAC)
{
hhDriver->NVTstate = NVT_IAC;
return NVT_DISCARD ;
}
return NVT_KEEP ;
case NVT_IAC:
switch (mc)
{
case IAC:
hhDriver->NVTstate = NVT_THRU; // Got a doubled IAC, keep one
return NVT_KEEP ;
case DONT:
hhDriver->NVTstate = NVT_DONT;
return NVT_DISCARD ;
case DO:
hhDriver->NVTstate = NVT_DO;
return NVT_DISCARD ;
case WONT:
hhDriver->NVTstate = NVT_WONT;
return NVT_DISCARD ;
case WILL:
hhDriver->NVTstate = NVT_WILL;
return NVT_DISCARD ;
case SB:
hhDriver->NVTstate = NVT_SB;
return NVT_DISCARD ;
case GA:
case EL:
case EC:
case AYT:
case AO:
case IP:
case BREAK:
case DM:
case SE:
//mscMessageBeep((UINT)-1);
hhDriver->NVTstate = NVT_THRU;
return NVT_DISCARD ; // ignore all these
case NOP:
default:
hhDriver->NVTstate = NVT_THRU;
return NVT_KEEP;
}
case NVT_WILL:
pstTelnetOpt = LookupOption( hhDriver, mc );
if ( pstTelnetOpt )
WinSockGotWILL( hhDriver, pstTelnetOpt ); // We support the option, negotiate
else
WinSockSendMessage( hhDriver, DONT, mc ); // We don't support it, decline
hhDriver->NVTstate = NVT_THRU;
return NVT_DISCARD ;
case NVT_WONT:
pstTelnetOpt = LookupOption( hhDriver, mc );
if ( pstTelnetOpt )
WinSockGotWONT( hhDriver, pstTelnetOpt ); // We support the option, negotiate
// Since we don't support this option, it is always off, and we never respond
// when the other side tries to set a state that already exists
hhDriver->NVTstate = NVT_THRU;
return NVT_DISCARD ;
case NVT_DO:
pstTelnetOpt = LookupOption( hhDriver, mc );
if ( pstTelnetOpt )
WinSockGotDO( hhDriver, pstTelnetOpt ); // We support the option, negotiate
else
WinSockSendMessage( hhDriver, WONT, mc ); // We don't support it, decline
hhDriver->NVTstate = NVT_THRU;
return NVT_DISCARD ;
case NVT_DONT:
pstTelnetOpt = LookupOption( hhDriver, mc );
if ( pstTelnetOpt )
WinSockGotDONT( hhDriver, pstTelnetOpt ); // We support the option, negotiate
// Since we don't support this option, it is always off, and we never respond
// when the other side tries to set a state that already exists
hhDriver->NVTstate = NVT_THRU;
return NVT_DISCARD ;
case NVT_SB:
/* At this time we only handle one sub-negotiation */
switch (mc)
{
case TELOPT_TTYPE:
hhDriver->NVTstate = NVT_SB_TT;
return NVT_DISCARD ;
default:
break;
}
hhDriver->NVTstate = NVT_THRU;
return NVT_KEEP;
case NVT_SB_TT:
switch (mc)
{
case TELQUAL_SEND:
hhDriver->NVTstate = NVT_SB_TT_S;
return NVT_DISCARD ;
default:
break;
}
hhDriver->NVTstate = NVT_THRU;
return NVT_KEEP;
case NVT_SB_TT_S:
switch (mc)
{
case IAC:
hhDriver->NVTstate = NVT_SB_TT_S_I;
return NVT_DISCARD ;
default:
break;
}
hhDriver->NVTstate = NVT_THRU;
return NVT_KEEP;
case NVT_SB_TT_S_I:
switch (mc)
{
case SE:
memset(acTerm, 0, sizeof(acTerm));
pszPtr = (LPSTR)acTerm;
*pszPtr++ = (UCHAR)IAC;
*pszPtr++ = (UCHAR)SB;
*pszPtr++ = (UCHAR)TELOPT_TTYPE;
*pszPtr++ = (UCHAR)TELQUAL_IS;
ComGetSession(hhDriver->hCom, &hSession);
assert(hSession);
hEmu = sessQueryEmuHdl(hSession);
assert(hEmu);
#ifdef INCL_USER_DEFINED_BACKSPACE_AND_TELNET_TERMINAL_ID
// The telnet terminal ids are no longer hard-coded. We
// are now using the terminal id that is supplied by the
// user in the "Settings" properties page. - cab:11/18/96
//
emuQuerySettings(hEmu, &stEmuSet);
strcpy(pszPtr, stEmuSet.acTelnetId);
#else
nTtype = emuQueryEmulatorId(hEmu);
switch (nTtype)
{
case EMU_ANSI:
strcpy(pszPtr, "ANSI");
break;
case EMU_TTY:
strcpy(pszPtr, "TELETYPE-33");
break;
case EMU_VT52:
strcpy(pszPtr, "DEC-VT52");
break;
case EMU_VT100:
// strcpy(pszPtr, "VT100");
strcpy(pszPtr, "DEC-VT100");
break;
#if defined(INCL_VT220)
case EMU_VT220:
// strcpy(pszPtr, "VT220");
strcpy(pszPtr, "DEC-VT220");
break;
#endif
#if defined(INCL_VT320)
case EMU_VT220:
// strcpy(pszPtr, "VT320");
strcpy(pszPtr, "DEC-VT320");
break;
#endif
#if defined(INCL_VT100PLUS)
case EMU_VT100PLUS:
// strcpy(pszPtr, "VT100");
strcpy(pszPtr, "DEC-VT100");
break;
#endif
#if defined(INCL_VTUTF8)
case EMU_VTUTF8:
strcpy(pszPtr, "VT-UTF8");
break;
#endif
default:
strcpy(pszPtr, "DEC-VT100"); // "UNKNOWN");
break;
}
#endif
DbgOutStr("NVT: Terminal=%s", pszPtr, 0,0,0,0);
pszPtr = pszPtr + strlen(pszPtr);
*pszPtr++ = (UCHAR)IAC;
*pszPtr++ = (UCHAR)SE;
WinSockSendBuffer(hhDriver,
(INT)(pszPtr - (LPSTR)acTerm),
(LPSTR)acTerm);
hhDriver->NVTstate = NVT_THRU;
return NVT_DISCARD ;
default:
break;
}
hhDriver->NVTstate = NVT_THRU;
return NVT_KEEP;
default:
hhDriver->NVTstate = NVT_THRU;
return NVT_KEEP;
}
}
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// FUNCTION: WinSockSendNAWS
//
// DESCRIPTION: Sends our terminal dimensions according to the Telnet NAWS option
// specification. NAWS stands for Negotiate About Window Size. It is
// defined in RFC 1073, "Telnet Window Size Option". If a telnet server
// enables this capability by sending us an IAC DO NAWS sequence, we
// will agree to it by responding with IAC WILL NAWS and then sending
// the number of rows and columns in a sub-option negotiation sequence
// as implemented here. We also send the sub-option sequence whenever
// our terminal size changes.
//
// ARGUMENTS: hhDriver -- pointer to our com driver
//
// RETURNS: void
//
// AUTHOR: John Hile, 6/17/98
//
VOID WinSockSendNAWS( ST_STDCOM *hhDriver )
{
HEMU hEmu;
HSESSION hSession;
int iRows;
int iCols;
UCHAR achOutput[9]; // exact size
// We've been asked to send our terminal size to the server. We're only
// allowed to do so if we have successfully enabled the NAWS option with
// the server.
if ( hhDriver->stMode[NAWS_MODE].us == YES)
{
// OK, option has been turned on. Send
// "IAC SB NAWS WIDTH[1] WIDTH[0] HEIGHT[1] HEIGHT[0] IAC SE" to server
// Get actual terminal size (not menu settings) from emulator
ComGetSession(hhDriver->hCom, &hSession);
assert(hSession);
hEmu = sessQueryEmuHdl(hSession);
assert(hEmu);
emuQueryRowsCols( hEmu, &iRows, &iCols );
achOutput[0] = (UCHAR)IAC;
achOutput[1] = (UCHAR)SB;
achOutput[2] = (UCHAR)TELOPT_NAWS;
achOutput[3] = (UCHAR)(iCols / 0xFF);
achOutput[4] = (UCHAR)(iCols % 0xFF);
achOutput[5] = (UCHAR)(iRows / 0xFF);
achOutput[6] = (UCHAR)(iRows % 0xFF);
achOutput[7] = (UCHAR)IAC;
achOutput[8] = (UCHAR)SE;
WinSockSendBuffer(hhDriver, sizeof(achOutput), (LPSTR)achOutput);
}
}
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// FUNCTION: LookupTelnetOption
//
// DESCRIPTION: Searches our table of telnet option management structures to
// see whether we support the option coded by character mc.
//
// ARGUMENTS: hhDriver -- our comm driver handle
// mc -- the character that defines the option we're looking up
//
// RETURNS: Pointer to the option management structure if found or NULL otherwise
//
// AUTHOR: John Hile, 6/17/98
//
static PSTOPT LookupOption( ST_STDCOM *hhDriver, ECHAR mc )
{
int ix;
for (ix = 0; ix < MODE_MAX; ix++)
if (hhDriver->stMode[ix].option == mc)
return &hhDriver->stMode[ix];
return (PSTOPT)0;
}
#endif