4 * TELNET socket I/O channel class.
6 * Portable Windows Library
8 * Copyright (c) 1993-2002 Equivalence Pty. Ltd.
10 * The contents of this file are subject to the Mozilla Public License
11 * Version 1.0 (the "License"); you may not use this file except in
12 * compliance with the License. You may obtain a copy of the License at
13 * http://www.mozilla.org/MPL/
15 * Software distributed under the License is distributed on an "AS IS"
16 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
17 * the License for the specific language governing rights and limitations
20 * The Original Code is Portable Windows Library.
22 * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
24 * Contributor(s): ______________________________________.
27 * Revision 1.11 2003/11/13 21:14:57 csoutheren
28 * Added fix for telnet NOP command thanks to Andrei Koulik
30 * Revision 1.10 2002/11/06 22:47:25 robertj
31 * Fixed header comment (copyright etc)
33 * Revision 1.9 2002/09/18 06:38:59 robertj
34 * Fixed initialisation of debug flag, thanks wolfboy@netease.com
36 * Revision 1.8 2001/09/10 02:51:23 robertj
37 * Major change to fix problem with error codes being corrupted in a
38 * PChannel when have simultaneous reads and writes in threads.
40 * Revision 1.7 1998/11/30 04:52:11 robertj
41 * New directory structure
43 * Revision 1.6 1998/09/23 06:22:47 robertj
44 * Added open source copyright license.
46 * Revision 1.5 1998/01/26 02:49:23 robertj
49 * Revision 1.4 1997/07/14 11:47:18 robertj
50 * Added "const" to numerous variables.
52 * Revision 1.3 1996/08/08 10:08:48 robertj
53 * Directory structure changes for common files.
55 * Revision 1.2 1996/05/26 03:47:08 robertj
56 * Compatibility to GNU 2.7.x
58 * Revision 1.1 1996/03/04 12:12:51 robertj
64 #pragma implementation "telnet.h"
68 #include <ptlib/sockets.h>
69 #include <ptclib/telnet.h>
72 //////////////////////////////////////////////////////////////////////////////
75 PTelnetSocket::PTelnetSocket()
76 : PTCPSocket("telnet")
82 PTelnetSocket::PTelnetSocket(const PString
& address
)
83 : PTCPSocket("telnet")
90 void PTelnetSocket::Construct()
93 terminalType
= "UNKNOWN";
94 windowWidth
= windowHeight
= 0;
97 memset(option
, 0, sizeof(option
));
98 SetOurOption(TransmitBinary
);
99 SetOurOption(SuppressGoAhead
);
100 SetOurOption(StatusOption
);
101 SetOurOption(TimingMark
);
102 SetOurOption(TerminalSpeed
);
103 SetOurOption(TerminalType
);
104 SetTheirOption(TransmitBinary
);
105 SetTheirOption(SuppressGoAhead
);
106 SetTheirOption(StatusOption
);
107 SetTheirOption(TimingMark
);
108 SetTheirOption(EchoOption
);
118 #define PTelnetError if (debug) PError << "PTelnetSocket: "
119 #define PDebugError if (debug) PError
121 BOOL
PTelnetSocket::Connect(const PString
& host
)
123 PTelnetError
<< "Connect" << endl
;
125 if (!PTCPSocket::Connect(host
))
128 SendDo(SuppressGoAhead
);
129 SendDo(StatusOption
);
130 SendWill(TerminalSpeed
);
135 BOOL
PTelnetSocket::Accept(PSocket
& sock
)
137 if (!PTCPSocket::Accept(sock
))
140 SendDo(SuppressGoAhead
);
141 SendWill(StatusOption
);
146 BOOL
PTelnetSocket::Write(void const * buffer
, PINDEX length
)
148 const BYTE
* base
= (const BYTE
*)buffer
;
149 const BYTE
* next
= base
;
154 !(length
> 1 && next
[1] == '\n') && !IsOurOption(TransmitBinary
)) {
155 // send the characters
156 if (!PTCPSocket::Write(base
, (next
- base
) + 1))
158 count
+= lastWriteCount
;
161 if (!PTCPSocket::Write(&null
, 1))
163 count
+= lastWriteCount
;
169 // send the characters
170 if (!PTCPSocket::Write(base
, (next
- base
) + 1))
172 count
+= lastWriteCount
;
181 if (!PTCPSocket::Write(base
, next
- base
))
183 count
+= lastWriteCount
;
186 lastWriteCount
= count
;
191 BOOL
PTelnetSocket::SendCommand(Command cmd
, int opt
)
195 buffer
[1] = (BYTE
)cmd
;
202 buffer
[2] = (BYTE
)opt
;
203 return PTCPSocket::Write(buffer
, 3);
205 case InterruptProcess
:
208 case SuspendProcess
:
212 if (!PTCPSocket::Write(buffer
, 2))
214 // Send a TimingMark for output flush.
215 buffer
[1] = TimingMark
;
216 if (!PTCPSocket::Write(buffer
, 2))
218 // Send a DataMark for synchronisation.
219 if (cmd
!= AbortOutput
) {
220 buffer
[1] = DataMark
;
221 if (!PTCPSocket::Write(buffer
, 2))
223 // Send the datamark character as the only out of band data byte.
224 if (!WriteOutOfBand(&buffer
[1], 1))
227 // Then flush any waiting input data.
228 PTimeInterval oldTimeout
= readTimeout
;
230 while (PTCPSocket::Read(buffer
, sizeof(buffer
)))
232 readTimeout
= oldTimeout
;
237 return PTCPSocket::Write(buffer
, 2);
244 static PString
GetTELNETOptionName(PINDEX code
)
246 static const char * const name
[] = {
258 "HorizontalTabsStops",
259 "HorizTabDisposition",
260 "FormFeedDisposition",
262 "VertTabDisposition",
263 "LineFeedDisposition",
276 "Use3270RegimeOption",
284 "AuthenticateOption",
288 if (code
< PARRAYSIZE(name
))
290 if (code
== PTelnetSocket::ExtendedOptionsList
)
291 return "ExtendedOptionsList";
292 return PString(PString::Printf
, "Option #%u", code
);
296 BOOL
PTelnetSocket::StartSend(const char * which
, BYTE code
)
298 PTelnetError
<< which
<< ' ' << GetTELNETOptionName(code
) << ' ';
302 PDebugError
<< "not open yet." << endl
;
303 return SetErrorValues(NotOpen
, EBADF
);
307 BOOL
PTelnetSocket::SendDo(BYTE code
)
309 if (!StartSend("SendDo", code
))
312 OptionInfo
& opt
= option
[code
];
314 switch (opt
.theirState
) {
315 case OptionInfo::IsNo
:
316 PDebugError
<< "initiated.";
317 SendCommand(DO
, code
);
318 opt
.theirState
= OptionInfo::WantYes
;
321 case OptionInfo::IsYes
:
322 PDebugError
<< "already enabled." << endl
;
325 case OptionInfo::WantNo
:
326 PDebugError
<< "queued.";
327 opt
.theirState
= OptionInfo::WantNoQueued
;
330 case OptionInfo::WantNoQueued
:
331 PDebugError
<< "already queued." << endl
;
332 opt
.theirState
= OptionInfo::IsNo
;
335 case OptionInfo::WantYes
:
336 PDebugError
<< "already negotiating." << endl
;
337 opt
.theirState
= OptionInfo::IsNo
;
340 case OptionInfo::WantYesQueued
:
341 PDebugError
<< "dequeued.";
342 opt
.theirState
= OptionInfo::WantYes
;
351 BOOL
PTelnetSocket::SendDont(BYTE code
)
353 if (!StartSend("SendDont", code
))
356 OptionInfo
& opt
= option
[code
];
358 switch (opt
.theirState
) {
359 case OptionInfo::IsNo
:
360 PDebugError
<< "already disabled." << endl
;
363 case OptionInfo::IsYes
:
364 PDebugError
<< "initiated.";
365 SendCommand(DONT
, code
);
366 opt
.theirState
= OptionInfo::WantNo
;
369 case OptionInfo::WantNo
:
370 PDebugError
<< "already negotiating." << endl
;
371 opt
.theirState
= OptionInfo::IsNo
;
374 case OptionInfo::WantNoQueued
:
375 PDebugError
<< "dequeued.";
376 opt
.theirState
= OptionInfo::WantNo
;
379 case OptionInfo::WantYes
:
380 PDebugError
<< "queued.";
381 opt
.theirState
= OptionInfo::WantYesQueued
;
384 case OptionInfo::WantYesQueued
:
385 PDebugError
<< "already queued." << endl
;
386 opt
.theirState
= OptionInfo::IsYes
;
395 BOOL
PTelnetSocket::SendWill(BYTE code
)
397 if (!StartSend("SendWill", code
))
403 OptionInfo
& opt
= option
[code
];
405 switch (opt
.ourState
) {
406 case OptionInfo::IsNo
:
407 PDebugError
<< "initiated.";
408 SendCommand(WILL
, code
);
409 opt
.ourState
= OptionInfo::WantYes
;
412 case OptionInfo::IsYes
:
413 PDebugError
<< "already enabled." << endl
;
416 case OptionInfo::WantNo
:
417 PDebugError
<< "queued.";
418 opt
.ourState
= OptionInfo::WantNoQueued
;
421 case OptionInfo::WantNoQueued
:
422 PDebugError
<< "already queued." << endl
;
423 opt
.ourState
= OptionInfo::IsNo
;
426 case OptionInfo::WantYes
:
427 PDebugError
<< "already negotiating." << endl
;
428 opt
.ourState
= OptionInfo::IsNo
;
431 case OptionInfo::WantYesQueued
:
432 PDebugError
<< "dequeued.";
433 opt
.ourState
= OptionInfo::WantYes
;
442 BOOL
PTelnetSocket::SendWont(BYTE code
)
444 if (!StartSend("SendWont", code
))
447 OptionInfo
& opt
= option
[code
];
449 switch (opt
.ourState
) {
450 case OptionInfo::IsNo
:
451 PDebugError
<< "already disabled." << endl
;
454 case OptionInfo::IsYes
:
455 PDebugError
<< "initiated.";
456 SendCommand(WONT
, code
);
457 opt
.ourState
= OptionInfo::WantNo
;
460 case OptionInfo::WantNo
:
461 PDebugError
<< "already negotiating." << endl
;
462 opt
.ourState
= OptionInfo::IsNo
;
465 case OptionInfo::WantNoQueued
:
466 PDebugError
<< "dequeued.";
467 opt
.ourState
= OptionInfo::WantNo
;
470 case OptionInfo::WantYes
:
471 PDebugError
<< "queued.";
472 opt
.ourState
= OptionInfo::WantYesQueued
;
475 case OptionInfo::WantYesQueued
:
476 PDebugError
<< "already queued." << endl
;
477 opt
.ourState
= OptionInfo::IsYes
;
486 BOOL
PTelnetSocket::SendSubOption(BYTE code
,
487 const BYTE
* info
, PINDEX len
, int subCode
)
489 if (!StartSend("SendSubOption", code
))
492 PDebugError
<< "with " << len
<< " bytes." << endl
;
494 PBYTEArray
buffer(len
+ 6);
500 buffer
[i
++] = (BYTE
)subCode
;
504 buffer
[i
++] = *info
++;
509 return PTCPSocket::Write((const BYTE
*)buffer
, i
);
513 void PTelnetSocket::SetTerminalType(const PString
& newType
)
515 terminalType
= newType
;
519 void PTelnetSocket::SetWindowSize(WORD width
, WORD height
)
522 windowHeight
= height
;
523 if (IsOurOption(WindowSize
)) {
525 buffer
[0] = (BYTE
)(width
>> 8);
526 buffer
[1] = (BYTE
)width
;
527 buffer
[2] = (BYTE
)(height
>> 8);
528 buffer
[3] = (BYTE
)height
;
529 SendSubOption(WindowSize
, buffer
, 4);
532 SetOurOption(WindowSize
);
533 SendWill(WindowSize
);
538 void PTelnetSocket::GetWindowSize(WORD
& width
, WORD
& height
) const
541 height
= windowHeight
;
545 BOOL
PTelnetSocket::Read(void * data
, PINDEX bytesToRead
)
547 PBYTEArray
buffer(bytesToRead
);
548 PINDEX charsLeft
= bytesToRead
;
549 BYTE
* dst
= (BYTE
*)data
;
551 while (charsLeft
> 0) {
552 BYTE
* src
= buffer
.GetPointer(charsLeft
);
553 if (!PTCPSocket::Read(src
, charsLeft
)) {
554 lastReadCount
= bytesToRead
- charsLeft
;
555 return lastReadCount
> 0;
558 while (lastReadCount
> 0) {
559 BYTE currentByte
= *src
++;
562 case StateCarriageReturn
:
564 if (currentByte
== '\0')
565 break; // Ignore \0 after CR
566 // Else, fall through for normal processing
569 if (currentByte
== IAC
)
572 if (currentByte
== '\r' && !IsTheirOption(TransmitBinary
))
573 state
= StateCarriageReturn
;
574 *dst
++ = currentByte
;
580 switch (currentByte
) {
603 case DataMark
: // data stream portion of a Synch
604 /* We may have missed an urgent notification, so make sure we
605 flush whatever is in the buffer currently.
607 PTelnetError
<< "received DataMark" << endl
;
608 if (synchronising
> 0)
612 case SB
: // subnegotiation start
613 state
= StateSubNegotiations
;
614 subOption
.SetSize(0);
618 if (OnCommand(currentByte
))
644 case StateSubNegotiations
:
645 if (currentByte
== IAC
)
646 state
= StateEndNegotiations
;
648 subOption
[subOption
.GetSize()] = currentByte
;
651 case StateEndNegotiations
:
652 if (currentByte
== SE
)
654 else if (currentByte
!= IAC
) {
655 /* This is an error. We only expect to get "IAC IAC" or "IAC SE".
656 Several things may have happend. An IAC was not doubled, the
657 IAC SE was left off, or another option got inserted into the
658 suboption are all possibilities. If we assume that the IAC was
659 not doubled, and really the IAC SE was left off, we could get
660 into an infinate loop here. So, instead, we terminate the
661 suboption, and process the partial suboption if we can.
664 src
--; // Go back to character for IAC ccommand
667 subOption
[subOption
.GetSize()] = currentByte
;
668 state
= StateSubNegotiations
;
669 break; // Was IAC IAC, subnegotiation not over yet.
671 if (subOption
.GetSize() > 1 && IsOurOption(subOption
[0]))
672 OnSubOption(subOption
[0],
673 ((const BYTE
*)subOption
)+1, subOption
.GetSize()-1);
677 PTelnetError
<< "illegal state: " << (int)state
<< endl
;
680 if (synchronising
> 0) {
681 charsLeft
= bytesToRead
; // Flush data being received.
686 lastReadCount
= bytesToRead
;
691 void PTelnetSocket::OnDo(BYTE code
)
693 PTelnetError
<< "OnDo " << GetTELNETOptionName(code
) << ' ';
695 OptionInfo
& opt
= option
[code
];
697 switch (opt
.ourState
) {
698 case OptionInfo::IsNo
:
700 PDebugError
<< "WILL.";
701 SendCommand(WILL
, code
);
702 opt
.ourState
= OptionInfo::IsYes
;
705 PDebugError
<< "WONT.";
706 SendCommand(WONT
, code
);
710 case OptionInfo::IsYes
:
711 PDebugError
<< "ignored.";
714 case OptionInfo::WantNo
:
715 PDebugError
<< "is answer to WONT.";
716 opt
.ourState
= OptionInfo::IsNo
;
719 case OptionInfo::WantNoQueued
:
720 PDebugError
<< "impossible answer.";
721 opt
.ourState
= OptionInfo::IsYes
;
724 case OptionInfo::WantYes
:
725 PDebugError
<< "accepted.";
726 opt
.ourState
= OptionInfo::IsYes
;
729 case OptionInfo::WantYesQueued
:
730 PDebugError
<< "refused.";
731 opt
.ourState
= OptionInfo::WantNo
;
732 SendCommand(WONT
, code
);
738 if (IsOurOption(code
)) {
740 case TerminalSpeed
: {
741 static BYTE defSpeed
[] = "38400,38400";
742 SendSubOption(TerminalSpeed
,defSpeed
,sizeof(defSpeed
)-1,SubOptionIs
);
747 SendSubOption(TerminalType
,
748 terminalType
, terminalType
.GetLength(), SubOptionIs
);
752 SetWindowSize(windowWidth
, windowHeight
);
759 void PTelnetSocket::OnDont(BYTE code
)
761 PTelnetError
<< "OnDont " << GetTELNETOptionName(code
) << ' ';
763 OptionInfo
& opt
= option
[code
];
765 switch (opt
.ourState
) {
766 case OptionInfo::IsNo
:
767 PDebugError
<< "ignored.";
770 case OptionInfo::IsYes
:
771 PDebugError
<< "WONT.";
772 opt
.ourState
= OptionInfo::IsNo
;
773 SendCommand(WONT
, code
);
776 case OptionInfo::WantNo
:
777 PDebugError
<< "disabled.";
778 opt
.ourState
= OptionInfo::IsNo
;
781 case OptionInfo::WantNoQueued
:
782 PDebugError
<< "accepting.";
783 opt
.ourState
= OptionInfo::WantYes
;
784 SendCommand(DO
, code
);
787 case OptionInfo::WantYes
:
788 PDebugError
<< "queued disable.";
789 opt
.ourState
= OptionInfo::IsNo
;
792 case OptionInfo::WantYesQueued
:
793 PDebugError
<< "refused.";
794 opt
.ourState
= OptionInfo::IsNo
;
802 void PTelnetSocket::OnWill(BYTE code
)
804 PTelnetError
<< "OnWill " << GetTELNETOptionName(code
) << ' ';
806 OptionInfo
& opt
= option
[code
];
808 switch (opt
.theirState
) {
809 case OptionInfo::IsNo
:
810 if (opt
.theyShould
) {
811 PDebugError
<< "DO.";
812 SendCommand(DO
, code
);
813 opt
.theirState
= OptionInfo::IsYes
;
816 PDebugError
<< "DONT.";
817 SendCommand(DONT
, code
);
821 case OptionInfo::IsYes
:
822 PDebugError
<< "ignored.";
825 case OptionInfo::WantNo
:
826 PDebugError
<< "is answer to DONT.";
827 opt
.theirState
= OptionInfo::IsNo
;
830 case OptionInfo::WantNoQueued
:
831 PDebugError
<< "impossible answer.";
832 opt
.theirState
= OptionInfo::IsYes
;
835 case OptionInfo::WantYes
:
836 PDebugError
<< "accepted.";
837 opt
.theirState
= OptionInfo::IsYes
;
840 case OptionInfo::WantYesQueued
:
841 PDebugError
<< "refused.";
842 opt
.theirState
= OptionInfo::WantNo
;
843 SendCommand(DONT
, code
);
851 void PTelnetSocket::OnWont(BYTE code
)
853 PTelnetError
<< "OnWont " << GetTELNETOptionName(code
) << ' ';
855 OptionInfo
& opt
= option
[code
];
857 switch (opt
.theirState
) {
858 case OptionInfo::IsNo
:
859 PDebugError
<< "ignored.";
862 case OptionInfo::IsYes
:
863 PDebugError
<< "DONT.";
864 opt
.theirState
= OptionInfo::IsNo
;
865 SendCommand(DONT
, code
);
868 case OptionInfo::WantNo
:
869 PDebugError
<< "disabled.";
870 opt
.theirState
= OptionInfo::IsNo
;
873 case OptionInfo::WantNoQueued
:
874 PDebugError
<< "accepting.";
875 opt
.theirState
= OptionInfo::WantYes
;
876 SendCommand(DO
, code
);
879 case OptionInfo::WantYes
:
880 PDebugError
<< "refused.";
881 opt
.theirState
= OptionInfo::IsNo
;
884 case OptionInfo::WantYesQueued
:
885 PDebugError
<< "queued refusal.";
886 opt
.theirState
= OptionInfo::IsNo
;
894 void PTelnetSocket::OnSubOption(BYTE code
, const BYTE
* info
, PINDEX len
)
896 PTelnetError
<< "OnSubOption " << GetTELNETOptionName(code
)
897 << " of " << len
<< " bytes." << endl
;
900 if (*info
== SubOptionSend
)
901 SendSubOption(TerminalType
,
902 terminalType
, terminalType
.GetLength(), SubOptionIs
);
906 if (*info
== SubOptionSend
) {
907 static BYTE defSpeed
[] = "38400,38400";
908 SendSubOption(TerminalSpeed
,defSpeed
,sizeof(defSpeed
)-1,SubOptionIs
);
915 BOOL
PTelnetSocket::OnCommand(BYTE code
)
919 PTelnetError
<< "unknown command " << (int)code
<< endl
;
923 void PTelnetSocket::OnOutOfBand(const void *, PINDEX length
)
925 PTelnetError
<< "out of band data received of length " << length
<< endl
;
930 // End Of File ///////////////////////////////////////////////////////////////