Added a parameter to semaphore constructor to avoid ambiguity
[pwlib.git] / src / ptclib / telnet.cxx
blob244fb0da1b7704ef60088628892a47f0999ecf5d
1 /*
2 * telnet.cxx
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
18 * under the License.
20 * The Original Code is Portable Windows Library.
22 * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
24 * Contributor(s): ______________________________________.
26 * $Log$
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
47 * GNU support.
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
59 * Initial revision
63 #ifdef __GNUC__
64 #pragma implementation "telnet.h"
65 #endif
67 #include <ptlib.h>
68 #include <ptlib/sockets.h>
69 #include <ptclib/telnet.h>
72 //////////////////////////////////////////////////////////////////////////////
73 // PTelnetSocket
75 PTelnetSocket::PTelnetSocket()
76 : PTCPSocket("telnet")
78 Construct();
82 PTelnetSocket::PTelnetSocket(const PString & address)
83 : PTCPSocket("telnet")
85 Construct();
86 Connect(address);
90 void PTelnetSocket::Construct()
92 synchronising = 0;
93 terminalType = "UNKNOWN";
94 windowWidth = windowHeight = 0;
95 state = StateNormal;
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);
110 #ifdef _DEBUG
111 debug = TRUE;
112 #else
113 debug = FALSE;
114 #endif
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))
126 return FALSE;
128 SendDo(SuppressGoAhead);
129 SendDo(StatusOption);
130 SendWill(TerminalSpeed);
131 return TRUE;
135 BOOL PTelnetSocket::Accept(PSocket & sock)
137 if (!PTCPSocket::Accept(sock))
138 return FALSE;
140 SendDo(SuppressGoAhead);
141 SendWill(StatusOption);
142 return TRUE;
146 BOOL PTelnetSocket::Write(void const * buffer, PINDEX length)
148 const BYTE * base = (const BYTE *)buffer;
149 const BYTE * next = base;
150 int count = 0;
152 while (length > 0) {
153 if (*next == '\r' &&
154 !(length > 1 && next[1] == '\n') && !IsOurOption(TransmitBinary)) {
155 // send the characters
156 if (!PTCPSocket::Write(base, (next - base) + 1))
157 return FALSE;
158 count += lastWriteCount;
160 char null = '\0';
161 if (!PTCPSocket::Write(&null, 1))
162 return FALSE;
163 count += lastWriteCount;
165 base = next+1;
168 if (*next == IAC) {
169 // send the characters
170 if (!PTCPSocket::Write(base, (next - base) + 1))
171 return FALSE;
172 count += lastWriteCount;
173 base = next;
176 next++;
177 length--;
180 if (next > base) {
181 if (!PTCPSocket::Write(base, next - base))
182 return FALSE;
183 count += lastWriteCount;
186 lastWriteCount = count;
187 return TRUE;
191 BOOL PTelnetSocket::SendCommand(Command cmd, int opt)
193 BYTE buffer[3];
194 buffer[0] = IAC;
195 buffer[1] = (BYTE)cmd;
197 switch (cmd) {
198 case DO :
199 case DONT :
200 case WILL :
201 case WONT :
202 buffer[2] = (BYTE)opt;
203 return PTCPSocket::Write(buffer, 3);
205 case InterruptProcess :
206 case Break :
207 case AbortProcess :
208 case SuspendProcess :
209 case AbortOutput :
210 if (opt) {
211 // Send the command
212 if (!PTCPSocket::Write(buffer, 2))
213 return FALSE;
214 // Send a TimingMark for output flush.
215 buffer[1] = TimingMark;
216 if (!PTCPSocket::Write(buffer, 2))
217 return FALSE;
218 // Send a DataMark for synchronisation.
219 if (cmd != AbortOutput) {
220 buffer[1] = DataMark;
221 if (!PTCPSocket::Write(buffer, 2))
222 return FALSE;
223 // Send the datamark character as the only out of band data byte.
224 if (!WriteOutOfBand(&buffer[1], 1))
225 return FALSE;
227 // Then flush any waiting input data.
228 PTimeInterval oldTimeout = readTimeout;
229 readTimeout = 0;
230 while (PTCPSocket::Read(buffer, sizeof(buffer)))
232 readTimeout = oldTimeout;
234 break;
236 default :
237 return PTCPSocket::Write(buffer, 2);
240 return TRUE;
244 static PString GetTELNETOptionName(PINDEX code)
246 static const char * const name[] = {
247 "TransmitBinary",
248 "EchoOption",
249 "ReconnectOption",
250 "SuppressGoAhead",
251 "MessageSizeOption",
252 "StatusOption",
253 "TimingMark",
254 "RCTEOption",
255 "OutputLineWidth",
256 "OutputPageSize",
257 "CRDisposition",
258 "HorizontalTabsStops",
259 "HorizTabDisposition",
260 "FormFeedDisposition",
261 "VerticalTabStops",
262 "VertTabDisposition",
263 "LineFeedDisposition",
264 "ExtendedASCII",
265 "ForceLogout",
266 "ByteMacroOption",
267 "DataEntryTerminal",
268 "SupDupProtocol",
269 "SupDupOutput",
270 "SendLocation",
271 "TerminalType",
272 "EndOfRecordOption",
273 "TACACSUID",
274 "OutputMark",
275 "TerminalLocation",
276 "Use3270RegimeOption",
277 "UseX3PADOption",
278 "WindowSize",
279 "TerminalSpeed",
280 "FlowControl",
281 "LineMode",
282 "XDisplayLocation",
283 "EnvironmentOption",
284 "AuthenticateOption",
285 "EncriptionOption"
288 if (code < PARRAYSIZE(name))
289 return name[code];
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) << ' ';
299 if (IsOpen())
300 return TRUE;
302 PDebugError << "not open yet." << endl;
303 return SetErrorValues(NotOpen, EBADF);
307 BOOL PTelnetSocket::SendDo(BYTE code)
309 if (!StartSend("SendDo", code))
310 return FALSE;
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;
319 break;
321 case OptionInfo::IsYes :
322 PDebugError << "already enabled." << endl;
323 return FALSE;
325 case OptionInfo::WantNo :
326 PDebugError << "queued.";
327 opt.theirState = OptionInfo::WantNoQueued;
328 break;
330 case OptionInfo::WantNoQueued :
331 PDebugError << "already queued." << endl;
332 opt.theirState = OptionInfo::IsNo;
333 return FALSE;
335 case OptionInfo::WantYes :
336 PDebugError << "already negotiating." << endl;
337 opt.theirState = OptionInfo::IsNo;
338 return FALSE;
340 case OptionInfo::WantYesQueued :
341 PDebugError << "dequeued.";
342 opt.theirState = OptionInfo::WantYes;
343 break;
346 PDebugError << endl;
347 return TRUE;
351 BOOL PTelnetSocket::SendDont(BYTE code)
353 if (!StartSend("SendDont", code))
354 return FALSE;
356 OptionInfo & opt = option[code];
358 switch (opt.theirState) {
359 case OptionInfo::IsNo :
360 PDebugError << "already disabled." << endl;
361 return FALSE;
363 case OptionInfo::IsYes :
364 PDebugError << "initiated.";
365 SendCommand(DONT, code);
366 opt.theirState = OptionInfo::WantNo;
367 break;
369 case OptionInfo::WantNo :
370 PDebugError << "already negotiating." << endl;
371 opt.theirState = OptionInfo::IsNo;
372 return FALSE;
374 case OptionInfo::WantNoQueued :
375 PDebugError << "dequeued.";
376 opt.theirState = OptionInfo::WantNo;
377 break;
379 case OptionInfo::WantYes :
380 PDebugError << "queued.";
381 opt.theirState = OptionInfo::WantYesQueued;
382 break;
384 case OptionInfo::WantYesQueued :
385 PDebugError << "already queued." << endl;
386 opt.theirState = OptionInfo::IsYes;
387 return FALSE;
390 PDebugError << endl;
391 return TRUE;
395 BOOL PTelnetSocket::SendWill(BYTE code)
397 if (!StartSend("SendWill", code))
398 return FALSE;
400 if (!IsOpen())
401 return FALSE;
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;
410 break;
412 case OptionInfo::IsYes :
413 PDebugError << "already enabled." << endl;
414 return FALSE;
416 case OptionInfo::WantNo :
417 PDebugError << "queued.";
418 opt.ourState = OptionInfo::WantNoQueued;
419 break;
421 case OptionInfo::WantNoQueued :
422 PDebugError << "already queued." << endl;
423 opt.ourState = OptionInfo::IsNo;
424 return FALSE;
426 case OptionInfo::WantYes :
427 PDebugError << "already negotiating." << endl;
428 opt.ourState = OptionInfo::IsNo;
429 return FALSE;
431 case OptionInfo::WantYesQueued :
432 PDebugError << "dequeued.";
433 opt.ourState = OptionInfo::WantYes;
434 break;
437 PDebugError << endl;
438 return TRUE;
442 BOOL PTelnetSocket::SendWont(BYTE code)
444 if (!StartSend("SendWont", code))
445 return FALSE;
447 OptionInfo & opt = option[code];
449 switch (opt.ourState) {
450 case OptionInfo::IsNo :
451 PDebugError << "already disabled." << endl;
452 return FALSE;
454 case OptionInfo::IsYes :
455 PDebugError << "initiated.";
456 SendCommand(WONT, code);
457 opt.ourState = OptionInfo::WantNo;
458 break;
460 case OptionInfo::WantNo :
461 PDebugError << "already negotiating." << endl;
462 opt.ourState = OptionInfo::IsNo;
463 return FALSE;
465 case OptionInfo::WantNoQueued :
466 PDebugError << "dequeued.";
467 opt.ourState = OptionInfo::WantNo;
468 break;
470 case OptionInfo::WantYes :
471 PDebugError << "queued.";
472 opt.ourState = OptionInfo::WantYesQueued;
473 break;
475 case OptionInfo::WantYesQueued :
476 PDebugError << "already queued." << endl;
477 opt.ourState = OptionInfo::IsYes;
478 return FALSE;
481 PDebugError << endl;
482 return TRUE;
486 BOOL PTelnetSocket::SendSubOption(BYTE code,
487 const BYTE * info, PINDEX len, int subCode)
489 if (!StartSend("SendSubOption", code))
490 return FALSE;
492 PDebugError << "with " << len << " bytes." << endl;
494 PBYTEArray buffer(len + 6);
495 buffer[0] = IAC;
496 buffer[1] = SB;
497 buffer[2] = code;
498 PINDEX i = 3;
499 if (subCode >= 0)
500 buffer[i++] = (BYTE)subCode;
501 while (len-- > 0) {
502 if (*info == IAC)
503 buffer[i++] = IAC;
504 buffer[i++] = *info++;
506 buffer[i++] = IAC;
507 buffer[i++] = SE;
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)
521 windowWidth = width;
522 windowHeight = height;
523 if (IsOurOption(WindowSize)) {
524 BYTE buffer[4];
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);
531 else {
532 SetOurOption(WindowSize);
533 SendWill(WindowSize);
538 void PTelnetSocket::GetWindowSize(WORD & width, WORD & height) const
540 width = windowWidth;
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++;
560 lastReadCount--;
561 switch (state) {
562 case StateCarriageReturn :
563 state = StateNormal;
564 if (currentByte == '\0')
565 break; // Ignore \0 after CR
566 // Else, fall through for normal processing
568 case StateNormal :
569 if (currentByte == IAC)
570 state = StateIAC;
571 else {
572 if (currentByte == '\r' && !IsTheirOption(TransmitBinary))
573 state = StateCarriageReturn;
574 *dst++ = currentByte;
575 charsLeft--;
577 break;
579 case StateIAC :
580 switch (currentByte) {
581 case IAC :
582 state = StateNormal;
583 *dst++ = IAC;
584 charsLeft--;
585 break;
587 case DO :
588 state = StateDo;
589 break;
591 case DONT :
592 state = StateDont;
593 break;
595 case WILL :
596 state = StateWill;
597 break;
599 case WONT :
600 state = StateWont;
601 break;
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)
609 synchronising--;
610 break;
612 case SB : // subnegotiation start
613 state = StateSubNegotiations;
614 subOption.SetSize(0);
615 break;
617 default:
618 if (OnCommand(currentByte))
619 state = StateNormal;
620 break;
622 break;
624 case StateDo :
625 OnDo(currentByte);
626 state = StateNormal;
627 break;
629 case StateDont :
630 OnDont(currentByte);
631 state = StateNormal;
632 break;
634 case StateWill :
635 OnWill(currentByte);
636 state = StateNormal;
637 break;
639 case StateWont :
640 OnWont(currentByte);
641 state = StateNormal;
642 break;
644 case StateSubNegotiations :
645 if (currentByte == IAC)
646 state = StateEndNegotiations;
647 else
648 subOption[subOption.GetSize()] = currentByte;
649 break;
651 case StateEndNegotiations :
652 if (currentByte == SE)
653 state = StateNormal;
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.
663 state = StateIAC;
664 src--; // Go back to character for IAC ccommand
666 else {
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);
674 break;
676 default :
677 PTelnetError << "illegal state: " << (int)state << endl;
678 state = StateNormal;
680 if (synchronising > 0) {
681 charsLeft = bytesToRead; // Flush data being received.
682 dst = (BYTE *)data;
686 lastReadCount = bytesToRead;
687 return TRUE;
691 void PTelnetSocket::OnDo(BYTE code)
693 PTelnetError << "OnDo " << GetTELNETOptionName(code) << ' ';
695 OptionInfo & opt = option[code];
697 switch (opt.ourState) {
698 case OptionInfo::IsNo :
699 if (opt.weCan) {
700 PDebugError << "WILL.";
701 SendCommand(WILL, code);
702 opt.ourState = OptionInfo::IsYes;
704 else {
705 PDebugError << "WONT.";
706 SendCommand(WONT, code);
708 break;
710 case OptionInfo::IsYes :
711 PDebugError << "ignored.";
712 break;
714 case OptionInfo::WantNo :
715 PDebugError << "is answer to WONT.";
716 opt.ourState = OptionInfo::IsNo;
717 break;
719 case OptionInfo::WantNoQueued :
720 PDebugError << "impossible answer.";
721 opt.ourState = OptionInfo::IsYes;
722 break;
724 case OptionInfo::WantYes :
725 PDebugError << "accepted.";
726 opt.ourState = OptionInfo::IsYes;
727 break;
729 case OptionInfo::WantYesQueued :
730 PDebugError << "refused.";
731 opt.ourState = OptionInfo::WantNo;
732 SendCommand(WONT, code);
733 break;
736 PDebugError << endl;
738 if (IsOurOption(code)) {
739 switch (code) {
740 case TerminalSpeed : {
741 static BYTE defSpeed[] = "38400,38400";
742 SendSubOption(TerminalSpeed,defSpeed,sizeof(defSpeed)-1,SubOptionIs);
744 break;
746 case TerminalType :
747 SendSubOption(TerminalType,
748 terminalType, terminalType.GetLength(), SubOptionIs);
749 break;
751 case WindowSize :
752 SetWindowSize(windowWidth, windowHeight);
753 break;
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.";
768 break;
770 case OptionInfo::IsYes :
771 PDebugError << "WONT.";
772 opt.ourState = OptionInfo::IsNo;
773 SendCommand(WONT, code);
774 break;
776 case OptionInfo::WantNo :
777 PDebugError << "disabled.";
778 opt.ourState = OptionInfo::IsNo;
779 break;
781 case OptionInfo::WantNoQueued :
782 PDebugError << "accepting.";
783 opt.ourState = OptionInfo::WantYes;
784 SendCommand(DO, code);
785 break;
787 case OptionInfo::WantYes :
788 PDebugError << "queued disable.";
789 opt.ourState = OptionInfo::IsNo;
790 break;
792 case OptionInfo::WantYesQueued :
793 PDebugError << "refused.";
794 opt.ourState = OptionInfo::IsNo;
795 break;
798 PDebugError << endl;
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;
815 else {
816 PDebugError << "DONT.";
817 SendCommand(DONT, code);
819 break;
821 case OptionInfo::IsYes :
822 PDebugError << "ignored.";
823 break;
825 case OptionInfo::WantNo :
826 PDebugError << "is answer to DONT.";
827 opt.theirState = OptionInfo::IsNo;
828 break;
830 case OptionInfo::WantNoQueued :
831 PDebugError << "impossible answer.";
832 opt.theirState = OptionInfo::IsYes;
833 break;
835 case OptionInfo::WantYes :
836 PDebugError << "accepted.";
837 opt.theirState = OptionInfo::IsYes;
838 break;
840 case OptionInfo::WantYesQueued :
841 PDebugError << "refused.";
842 opt.theirState = OptionInfo::WantNo;
843 SendCommand(DONT, code);
844 break;
847 PDebugError << endl;
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.";
860 break;
862 case OptionInfo::IsYes :
863 PDebugError << "DONT.";
864 opt.theirState = OptionInfo::IsNo;
865 SendCommand(DONT, code);
866 break;
868 case OptionInfo::WantNo :
869 PDebugError << "disabled.";
870 opt.theirState = OptionInfo::IsNo;
871 break;
873 case OptionInfo::WantNoQueued :
874 PDebugError << "accepting.";
875 opt.theirState = OptionInfo::WantYes;
876 SendCommand(DO, code);
877 break;
879 case OptionInfo::WantYes :
880 PDebugError << "refused.";
881 opt.theirState = OptionInfo::IsNo;
882 break;
884 case OptionInfo::WantYesQueued :
885 PDebugError << "queued refusal.";
886 opt.theirState = OptionInfo::IsNo;
887 break;
890 PDebugError << endl;
894 void PTelnetSocket::OnSubOption(BYTE code, const BYTE * info, PINDEX len)
896 PTelnetError << "OnSubOption " << GetTELNETOptionName(code)
897 << " of " << len << " bytes." << endl;
898 switch (code) {
899 case TerminalType :
900 if (*info == SubOptionSend)
901 SendSubOption(TerminalType,
902 terminalType, terminalType.GetLength(), SubOptionIs);
903 break;
905 case TerminalSpeed :
906 if (*info == SubOptionSend) {
907 static BYTE defSpeed[] = "38400,38400";
908 SendSubOption(TerminalSpeed,defSpeed,sizeof(defSpeed)-1,SubOptionIs);
910 break;
915 BOOL PTelnetSocket::OnCommand(BYTE code)
917 if (code == NOP)
918 return TRUE;
919 PTelnetError << "unknown command " << (int)code << endl;
920 return TRUE;
923 void PTelnetSocket::OnOutOfBand(const void *, PINDEX length)
925 PTelnetError << "out of band data received of length " << length << endl;
926 synchronising++;
930 // End Of File ///////////////////////////////////////////////////////////////