telnet.cxx
上传用户:hzhsqp
上传日期:2007-01-06
资源大小:1600k
文件大小:22k
- /*
- * telnet.cxx
- *
- * TELNET socket I/O channel class.
- *
- * Portable Windows Library
- *
- * Copyright (c) 1993-1998 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.
- *
- * Portions are Copyright (C) 1993 Free Software Foundation, Inc.
- * All Rights Reserved.
- *
- * Contributor(s): ______________________________________.
- *
- * $Log: telnet.cxx,v $
- * 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;
- #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 = ' ';
- 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;
- osError = EBADF;
- lastError = NotOpen;
- return FALSE;
- }
- 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.";
- opt.ourState = OptionInfo::WantNo;
- break;
- case OptionInfo::WantYes :
- PDebugError << "queued.";
- opt.ourState = OptionInfo::WantYesQueued;
- break;
- case OptionInfo::WantYesQueued :
- PDebugError << "already queued." << endl;
- opt.ourState = OptionInfo::IsYes;
- return FALSE;
- }
- PDebugError << endl;
- return TRUE;
- }
- BOOL PTelnetSocket::SendSubOption(BYTE code,
- const BYTE * info, PINDEX len, int subCode)
- {
- if (!StartSend("SendSubOption", code))
- return FALSE;
- PDebugError << "with " << len << " bytes." << endl;
- PBYTEArray buffer(len + 6);
- buffer[0] = IAC;
- buffer[1] = SB;
- buffer[2] = code;
- PINDEX i = 3;
- if (subCode >= 0)
- buffer[i++] = (BYTE)subCode;
- while (len-- > 0) {
- if (*info == IAC)
- buffer[i++] = IAC;
- buffer[i++] = *info++;
- }
- buffer[i++] = IAC;
- buffer[i++] = SE;
- return PTCPSocket::Write((const BYTE *)buffer, i);
- }
- void PTelnetSocket::SetTerminalType(const PString & newType)
- {
- terminalType = newType;
- }
- void PTelnetSocket::SetWindowSize(WORD width, WORD height)
- {
- windowWidth = width;
- windowHeight = height;
- if (IsOurOption(WindowSize)) {
- BYTE buffer[4];
- buffer[0] = (BYTE)(width >> 8);
- buffer[1] = (BYTE)width;
- buffer[2] = (BYTE)(height >> 8);
- buffer[3] = (BYTE)height;
- SendSubOption(WindowSize, buffer, 4);
- }
- else {
- SetOurOption(WindowSize);
- SendWill(WindowSize);
- }
- }
- void PTelnetSocket::GetWindowSize(WORD & width, WORD & height) const
- {
- width = windowWidth;
- height = windowHeight;
- }
- BOOL PTelnetSocket::Read(void * data, PINDEX bytesToRead)
- {
- PBYTEArray buffer(bytesToRead);
- PINDEX charsLeft = bytesToRead;
- BYTE * dst = (BYTE *)data;
- while (charsLeft > 0) {
- BYTE * src = buffer.GetPointer(charsLeft);
- if (!PTCPSocket::Read(src, charsLeft)) {
- lastReadCount = bytesToRead - charsLeft;
- return lastReadCount > 0;
- }
- while (lastReadCount > 0) {
- BYTE currentByte = *src++;
- lastReadCount--;
- switch (state) {
- case StateCarriageReturn :
- state = StateNormal;
- if (currentByte == ' ')
- break; // Ignore