|
|
/* 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
|