📄 telnet.cxx
字号:
/*
* telnet.cxx
*
* TELNET socket I/O channel class.
*
* Portable Windows Library
*
* Copyright (c) 1993-2002 Equivalence Pty. Ltd.
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.0 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
*
* The Original Code is Portable Windows Library.
*
* The Initial Developer of the Original Code is Equivalence Pty. Ltd.
*
* Contributor(s): ______________________________________.
*
* $Log: telnet.cxx,v $
* Revision 1.11 2003/11/13 21:14:57 csoutheren
* Added fix for telnet NOP command thanks to Andrei Koulik
*
* Revision 1.10 2002/11/06 22:47:25 robertj
* Fixed header comment (copyright etc)
*
* Revision 1.9 2002/09/18 06:38:59 robertj
* Fixed initialisation of debug flag, thanks wolfboy@netease.com
*
* Revision 1.8 2001/09/10 02:51:23 robertj
* Major change to fix problem with error codes being corrupted in a
* PChannel when have simultaneous reads and writes in threads.
*
* Revision 1.7 1998/11/30 04:52:11 robertj
* New directory structure
*
* Revision 1.6 1998/09/23 06:22:47 robertj
* Added open source copyright license.
*
* Revision 1.5 1998/01/26 02:49:23 robertj
* GNU support.
*
* Revision 1.4 1997/07/14 11:47:18 robertj
* Added "const" to numerous variables.
*
* Revision 1.3 1996/08/08 10:08:48 robertj
* Directory structure changes for common files.
*
* Revision 1.2 1996/05/26 03:47:08 robertj
* Compatibility to GNU 2.7.x
*
* Revision 1.1 1996/03/04 12:12:51 robertj
* Initial revision
*
*/
#ifdef __GNUC__
#pragma implementation "telnet.h"
#endif
#include <ptlib.h>
#include <ptlib/sockets.h>
#include <ptclib/telnet.h>
//////////////////////////////////////////////////////////////////////////////
// PTelnetSocket
PTelnetSocket::PTelnetSocket()
: PTCPSocket("telnet")
{
Construct();
}
PTelnetSocket::PTelnetSocket(const PString & address)
: PTCPSocket("telnet")
{
Construct();
Connect(address);
}
void PTelnetSocket::Construct()
{
synchronising = 0;
terminalType = "UNKNOWN";
windowWidth = windowHeight = 0;
state = StateNormal;
memset(option, 0, sizeof(option));
SetOurOption(TransmitBinary);
SetOurOption(SuppressGoAhead);
SetOurOption(StatusOption);
SetOurOption(TimingMark);
SetOurOption(TerminalSpeed);
SetOurOption(TerminalType);
SetTheirOption(TransmitBinary);
SetTheirOption(SuppressGoAhead);
SetTheirOption(StatusOption);
SetTheirOption(TimingMark);
SetTheirOption(EchoOption);
#ifdef _DEBUG
debug = TRUE;
#else
debug = FALSE;
#endif
}
#define PTelnetError if (debug) PError << "PTelnetSocket: "
#define PDebugError if (debug) PError
BOOL PTelnetSocket::Connect(const PString & host)
{
PTelnetError << "Connect" << endl;
if (!PTCPSocket::Connect(host))
return FALSE;
SendDo(SuppressGoAhead);
SendDo(StatusOption);
SendWill(TerminalSpeed);
return TRUE;
}
BOOL PTelnetSocket::Accept(PSocket & sock)
{
if (!PTCPSocket::Accept(sock))
return FALSE;
SendDo(SuppressGoAhead);
SendWill(StatusOption);
return TRUE;
}
BOOL PTelnetSocket::Write(void const * buffer, PINDEX length)
{
const BYTE * base = (const BYTE *)buffer;
const BYTE * next = base;
int count = 0;
while (length > 0) {
if (*next == '\r' &&
!(length > 1 && next[1] == '\n') && !IsOurOption(TransmitBinary)) {
// send the characters
if (!PTCPSocket::Write(base, (next - base) + 1))
return FALSE;
count += lastWriteCount;
char null = '\0';
if (!PTCPSocket::Write(&null, 1))
return FALSE;
count += lastWriteCount;
base = next+1;
}
if (*next == IAC) {
// send the characters
if (!PTCPSocket::Write(base, (next - base) + 1))
return FALSE;
count += lastWriteCount;
base = next;
}
next++;
length--;
}
if (next > base) {
if (!PTCPSocket::Write(base, next - base))
return FALSE;
count += lastWriteCount;
}
lastWriteCount = count;
return TRUE;
}
BOOL PTelnetSocket::SendCommand(Command cmd, int opt)
{
BYTE buffer[3];
buffer[0] = IAC;
buffer[1] = (BYTE)cmd;
switch (cmd) {
case DO :
case DONT :
case WILL :
case WONT :
buffer[2] = (BYTE)opt;
return PTCPSocket::Write(buffer, 3);
case InterruptProcess :
case Break :
case AbortProcess :
case SuspendProcess :
case AbortOutput :
if (opt) {
// Send the command
if (!PTCPSocket::Write(buffer, 2))
return FALSE;
// Send a TimingMark for output flush.
buffer[1] = TimingMark;
if (!PTCPSocket::Write(buffer, 2))
return FALSE;
// Send a DataMark for synchronisation.
if (cmd != AbortOutput) {
buffer[1] = DataMark;
if (!PTCPSocket::Write(buffer, 2))
return FALSE;
// Send the datamark character as the only out of band data byte.
if (!WriteOutOfBand(&buffer[1], 1))
return FALSE;
}
// Then flush any waiting input data.
PTimeInterval oldTimeout = readTimeout;
readTimeout = 0;
while (PTCPSocket::Read(buffer, sizeof(buffer)))
;
readTimeout = oldTimeout;
}
break;
default :
return PTCPSocket::Write(buffer, 2);
}
return TRUE;
}
static PString GetTELNETOptionName(PINDEX code)
{
static const char * const name[] = {
"TransmitBinary",
"EchoOption",
"ReconnectOption",
"SuppressGoAhead",
"MessageSizeOption",
"StatusOption",
"TimingMark",
"RCTEOption",
"OutputLineWidth",
"OutputPageSize",
"CRDisposition",
"HorizontalTabsStops",
"HorizTabDisposition",
"FormFeedDisposition",
"VerticalTabStops",
"VertTabDisposition",
"LineFeedDisposition",
"ExtendedASCII",
"ForceLogout",
"ByteMacroOption",
"DataEntryTerminal",
"SupDupProtocol",
"SupDupOutput",
"SendLocation",
"TerminalType",
"EndOfRecordOption",
"TACACSUID",
"OutputMark",
"TerminalLocation",
"Use3270RegimeOption",
"UseX3PADOption",
"WindowSize",
"TerminalSpeed",
"FlowControl",
"LineMode",
"XDisplayLocation",
"EnvironmentOption",
"AuthenticateOption",
"EncriptionOption"
};
if (code < PARRAYSIZE(name))
return name[code];
if (code == PTelnetSocket::ExtendedOptionsList)
return "ExtendedOptionsList";
return PString(PString::Printf, "Option #%u", code);
}
BOOL PTelnetSocket::StartSend(const char * which, BYTE code)
{
PTelnetError << which << ' ' << GetTELNETOptionName(code) << ' ';
if (IsOpen())
return TRUE;
PDebugError << "not open yet." << endl;
return SetErrorValues(NotOpen, EBADF);
}
BOOL PTelnetSocket::SendDo(BYTE code)
{
if (!StartSend("SendDo", code))
return FALSE;
OptionInfo & opt = option[code];
switch (opt.theirState) {
case OptionInfo::IsNo :
PDebugError << "initiated.";
SendCommand(DO, code);
opt.theirState = OptionInfo::WantYes;
break;
case OptionInfo::IsYes :
PDebugError << "already enabled." << endl;
return FALSE;
case OptionInfo::WantNo :
PDebugError << "queued.";
opt.theirState = OptionInfo::WantNoQueued;
break;
case OptionInfo::WantNoQueued :
PDebugError << "already queued." << endl;
opt.theirState = OptionInfo::IsNo;
return FALSE;
case OptionInfo::WantYes :
PDebugError << "already negotiating." << endl;
opt.theirState = OptionInfo::IsNo;
return FALSE;
case OptionInfo::WantYesQueued :
PDebugError << "dequeued.";
opt.theirState = OptionInfo::WantYes;
break;
}
PDebugError << endl;
return TRUE;
}
BOOL PTelnetSocket::SendDont(BYTE code)
{
if (!StartSend("SendDont", code))
return FALSE;
OptionInfo & opt = option[code];
switch (opt.theirState) {
case OptionInfo::IsNo :
PDebugError << "already disabled." << endl;
return FALSE;
case OptionInfo::IsYes :
PDebugError << "initiated.";
SendCommand(DONT, code);
opt.theirState = OptionInfo::WantNo;
break;
case OptionInfo::WantNo :
PDebugError << "already negotiating." << endl;
opt.theirState = OptionInfo::IsNo;
return FALSE;
case OptionInfo::WantNoQueued :
PDebugError << "dequeued.";
opt.theirState = OptionInfo::WantNo;
break;
case OptionInfo::WantYes :
PDebugError << "queued.";
opt.theirState = OptionInfo::WantYesQueued;
break;
case OptionInfo::WantYesQueued :
PDebugError << "already queued." << endl;
opt.theirState = OptionInfo::IsYes;
return FALSE;
}
PDebugError << endl;
return TRUE;
}
BOOL PTelnetSocket::SendWill(BYTE code)
{
if (!StartSend("SendWill", code))
return FALSE;
if (!IsOpen())
return FALSE;
OptionInfo & opt = option[code];
switch (opt.ourState) {
case OptionInfo::IsNo :
PDebugError << "initiated.";
SendCommand(WILL, code);
opt.ourState = OptionInfo::WantYes;
break;
case OptionInfo::IsYes :
PDebugError << "already enabled." << endl;
return FALSE;
case OptionInfo::WantNo :
PDebugError << "queued.";
opt.ourState = OptionInfo::WantNoQueued;
break;
case OptionInfo::WantNoQueued :
PDebugError << "already queued." << endl;
opt.ourState = OptionInfo::IsNo;
return FALSE;
case OptionInfo::WantYes :
PDebugError << "already negotiating." << endl;
opt.ourState = OptionInfo::IsNo;
return FALSE;
case OptionInfo::WantYesQueued :
PDebugError << "dequeued.";
opt.ourState = OptionInfo::WantYes;
break;
}
PDebugError << endl;
return TRUE;
}
BOOL PTelnetSocket::SendWont(BYTE code)
{
if (!StartSend("SendWont", code))
return FALSE;
OptionInfo & opt = option[code];
switch (opt.ourState) {
case OptionInfo::IsNo :
PDebugError << "already disabled." << endl;
return FALSE;
case OptionInfo::IsYes :
PDebugError << "initiated.";
SendCommand(WONT, code);
opt.ourState = OptionInfo::WantNo;
break;
case OptionInfo::WantNo :
PDebugError << "already negotiating." << endl;
opt.ourState = OptionInfo::IsNo;
return FALSE;
case OptionInfo::WantNoQueued :
PDebugError << "dequeued.";
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -