comparison src/de/mud/telnet/TelnetProtocolHandler.java @ 0:0ce5cc452d02

initial version
author Carl Byington <carl@five-ten-sg.com>
date Thu, 22 May 2014 10:41:19 -0700
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:0ce5cc452d02
1 /*
2 * This file is part of "JTA - Telnet/SSH for the JAVA(tm) platform".
3 *
4 * (c) Matthias L. Jugel, Marcus Meißner 1996-2005. All Rights Reserved.
5 *
6 * Please visit http://javatelnet.org/ for updates and contact.
7 *
8 * --LICENSE NOTICE--
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 * --LICENSE NOTICE--
23 *
24 */
25
26 package de.mud.telnet;
27
28 import java.io.IOException;
29 /**
30 * This is a telnet protocol handler. The handler needs implementations
31 * for several methods to handle the telnet options and to be able to
32 * read and write the buffer.
33 * <P>
34 * <B>Maintainer:</B> Marcus Meissner
35 *
36 * @version $Id: TelnetProtocolHandler.java 503 2005-10-24 07:34:13Z marcus $
37 * @author Matthias L. Jugel, Marcus Meissner
38 */
39 public abstract class TelnetProtocolHandler {
40 /** contains the current revision id */
41 public final static String ID = "$Id: TelnetProtocolHandler.java 503 2005-10-24 07:34:13Z marcus $";
42
43 /** debug level */
44 private final static int debug = 0;
45
46 /** temporary buffer for data-telnetstuff-data transformation */
47 private byte[] tempbuf = new byte[0];
48
49 /** the data sent on pressing <RETURN> \n */
50 private byte[] crlf = new byte[2];
51 /** the data sent on pressing <LineFeed> \r */
52 private byte[] cr = new byte[2];
53
54 /**
55 * Create a new telnet protocol handler.
56 */
57 public TelnetProtocolHandler() {
58 reset();
59 crlf[0] = 13; crlf[1] = 10;
60 cr[0] = 13; cr[1] = 0;
61 }
62
63 /**
64 * Get the current terminal type for TTYPE telnet option.
65 * @return the string id of the terminal
66 */
67 protected abstract String getTerminalType();
68
69 /**
70 * Get the current window size of the terminal for the
71 * NAWS telnet option.
72 * @return the size of the terminal as Dimension
73 */
74 protected abstract int[] getWindowSize();
75
76 /**
77 * Set the local echo option of telnet.
78 * @param echo true for local echo, false for no local echo
79 */
80 protected abstract void setLocalEcho(boolean echo);
81
82 /**
83 * Generate an EOR (end of record) request. For use by prompt displaying.
84 */
85 protected abstract void notifyEndOfRecord();
86
87 /**
88 * Send data to the remote host.
89 * @param b array of bytes to send
90 */
91 protected abstract void write(byte[] b) throws IOException;
92
93 /**
94 * Read the charset name from terminal.
95 */
96 protected abstract String getCharsetName();
97
98 /**
99 * Send one byte to the remote host.
100 * @param b the byte to be sent
101 * @see #write(byte[] b)
102 */
103 private static byte[] one = new byte[1];
104 private void write(byte b) throws IOException {
105 one[0] = b;
106 write(one);
107 }
108
109 /**
110 * Reset the protocol handler. This may be necessary after the
111 * connection was closed or some other problem occured.
112 */
113 public void reset() {
114 neg_state = 0;
115 receivedDX = new byte[256];
116 sentDX = new byte[256];
117 receivedWX = new byte[256];
118 sentWX = new byte[256];
119 }
120
121 // ===================================================================
122 // the actual negotiation handling for the telnet protocol follows:
123 // ===================================================================
124
125 /** state variable for telnet negotiation reader */
126 private byte neg_state = 0;
127
128 /** constants for the negotiation state */
129 private final static byte STATE_DATA = 0;
130 private final static byte STATE_IAC = 1;
131 private final static byte STATE_IACSB = 2;
132 private final static byte STATE_IACWILL = 3;
133 private final static byte STATE_IACDO = 4;
134 private final static byte STATE_IACWONT = 5;
135 private final static byte STATE_IACDONT = 6;
136 private final static byte STATE_IACSBIAC = 7;
137 private final static byte STATE_IACSBDATA = 8;
138 private final static byte STATE_IACSBDATAIAC = 9;
139
140 /** What IAC SB <xx> we are handling right now */
141 private byte current_sb;
142
143 /** current SB negotiation buffer */
144 private byte[] sbbuf;
145
146 /** IAC - init sequence for telnet negotiation. */
147 private final static byte IAC = (byte)255;
148 /** [IAC] End Of Record */
149 private final static byte EOR = (byte)239;
150 /** [IAC] WILL */
151 private final static byte WILL = (byte)251;
152 /** [IAC] WONT */
153 private final static byte WONT = (byte)252;
154 /** [IAC] DO */
155 private final static byte DO = (byte)253;
156 /** [IAC] DONT */
157 private final static byte DONT = (byte)254;
158 /** [IAC] Sub Begin */
159 private final static byte SB = (byte)250;
160 /** [IAC] Sub End */
161 private final static byte SE = (byte)240;
162 /** Telnet option: binary mode */
163 private final static byte TELOPT_BINARY = (byte)0; /* binary mode */
164 /** Telnet option: echo text */
165 private final static byte TELOPT_ECHO = (byte)1; /* echo on/off */
166 /** Telnet option: sga */
167 private final static byte TELOPT_SGA = (byte)3; /* supress go ahead */
168 /** Telnet option: End Of Record */
169 private final static byte TELOPT_EOR = (byte)25; /* end of record */
170 /** Telnet option: Negotiate About Window Size */
171 private final static byte TELOPT_NAWS = (byte)31; /* NA-WindowSize*/
172 /** Telnet option: Terminal Type */
173 private final static byte TELOPT_TTYPE = (byte)24; /* terminal type */
174 /** Telnet option: CHARSET */
175 private final static byte TELOPT_CHARSET = (byte)42; /* charset */
176
177 private final static byte[] IACWILL = { IAC, WILL };
178 private final static byte[] IACWONT = { IAC, WONT };
179 private final static byte[] IACDO = { IAC, DO };
180 private final static byte[] IACDONT = { IAC, DONT };
181 private final static byte[] IACSB = { IAC, SB };
182 private final static byte[] IACSE = { IAC, SE };
183
184 private final static byte CHARSET_ACCEPTED = (byte)2;
185 private final static byte CHARSET_REJECTED = (byte)3;
186
187 /** Telnet option qualifier 'IS' */
188 private final static byte TELQUAL_IS = (byte)0;
189 /** Telnet option qualifier 'SEND' */
190 private final static byte TELQUAL_SEND = (byte)1;
191
192 /** What IAC DO(NT) request do we have received already ? */
193 private byte[] receivedDX;
194 /** What IAC WILL/WONT request do we have received already ? */
195 private byte[] receivedWX;
196 /** What IAC DO/DONT request do we have sent already ? */
197 private byte[] sentDX;
198 /** What IAC WILL/WONT request do we have sent already ? */
199 private byte[] sentWX;
200
201 /**
202 * Send a Telnet Escape character (IAC <code>)
203 */
204 public void sendTelnetControl(byte code)
205 throws IOException {
206 byte[] b = new byte[2];
207 b[0] = IAC;
208 b[1] = code;
209 write(b);
210 }
211
212 /**
213 * Send the new Window Size (via NAWS)
214 */
215 public void setWindowSize(int columns, int rows)
216 throws IOException {
217 if (debug > 2) System.err.println("sending NAWS");
218
219 if (receivedDX[TELOPT_NAWS] != DO) {
220 System.err.println("not allowed to send NAWS? (DONT NAWS)");
221 return;
222 }
223
224 write(IAC); write(SB); write(TELOPT_NAWS);
225 write((byte)(columns >> 8));
226 write((byte)(columns & 0xff));
227 write((byte)(rows >> 8));
228 write((byte)(rows & 0xff));
229 write(IAC); write(SE);
230 }
231
232
233 /**
234 * Handle an incoming IAC SB &lt;type&gt; &lt;bytes&gt; IAC SE
235 * @param type type of SB
236 * @param sbata byte array as &lt;bytes&gt;
237 */
238 private void handle_sb(byte type, byte[] sbdata)
239 throws IOException {
240 if (debug > 1)
241 System.err.println("TelnetIO.handle_sb(" + type + ")");
242
243 switch (type) {
244 case TELOPT_TTYPE:
245 if (sbdata.length > 0 && sbdata[0] == TELQUAL_SEND) {
246 write(IACSB); write(TELOPT_TTYPE); write(TELQUAL_IS);
247 /* FIXME: need more logic here if we use
248 * more than one terminal type
249 */
250 String ttype = getTerminalType();
251
252 if (ttype == null) ttype = "dumb";
253
254 write(ttype.getBytes());
255 write(IACSE);
256 }
257
258 break;
259
260 case TELOPT_CHARSET:
261 System.out.println("Got SB CHARSET");
262 String charsetStr = new String(sbdata, "US-ASCII");
263
264 if (charsetStr.startsWith("TTABLE ")) {
265 charsetStr = charsetStr.substring(7);
266 }
267
268 String[] charsets = charsetStr.split(charsetStr.substring(0, 0));
269 String myCharset = getCharsetName();
270
271 for (String charset : charsets) {
272 if (charset.equals(myCharset)) {
273 write(IACSB); write(TELOPT_CHARSET); write(CHARSET_ACCEPTED);
274 write(charset.getBytes());
275 write(IACSE);
276 System.out.println("Sent our charset!");
277 return;
278 }
279 }
280
281 write(IACSB); write(TELOPT_CHARSET); write(CHARSET_REJECTED);
282 write(IACSE);
283 break;
284 }
285 }
286
287 /**
288 * Do not send any notifications at startup. We do not know,
289 * whether the remote client understands telnet protocol handling,
290 * so we are silent.
291 * (This used to send IAC WILL SGA, but this is false for a compliant
292 * client.)
293 */
294 public void startup() throws IOException {
295 }
296 /**
297 * Transpose special telnet codes like 0xff or newlines to values
298 * that are compliant to the protocol. This method will also send
299 * the buffer immediately after transposing the data.
300 * @param buf the data buffer to be sent
301 */
302 public void transpose(byte[] buf) throws IOException {
303 int i;
304 byte[] nbuf, xbuf;
305 int nbufptr = 0;
306 nbuf = new byte[buf.length * 2]; // FIXME: buffer overflows possible
307
308 for (i = 0; i < buf.length ; i++) {
309 switch (buf[i]) {
310 // Escape IAC twice in stream ... to be telnet protocol compliant
311 // this is there in binary and non-binary mode.
312 case IAC:
313 nbuf[nbufptr++] = IAC;
314 nbuf[nbufptr++] = IAC;
315 break;
316
317 // We need to heed RFC 854. LF (\n) is 10, CR (\r) is 13
318 // we assume that the Terminal sends \n for lf+cr and \r for just cr
319 // linefeed+carriage return is CR LF */
320 case 10: // \n
321 if (receivedDX[TELOPT_BINARY + 128 ] != DO) {
322 while (nbuf.length - nbufptr < crlf.length) {
323 xbuf = new byte[nbuf.length * 2];
324 System.arraycopy(nbuf, 0, xbuf, 0, nbufptr);
325 nbuf = xbuf;
326 }
327
328 for (int j = 0; j < crlf.length; j++)
329 nbuf[nbufptr++] = crlf[j];
330
331 break;
332 }
333 else {
334 // copy verbatim in binary mode.
335 nbuf[nbufptr++] = buf[i];
336 }
337
338 break;
339
340 // carriage return is CR NUL */
341 case 13: // \r
342 if (receivedDX[TELOPT_BINARY + 128 ] != DO) {
343 while (nbuf.length - nbufptr < cr.length) {
344 xbuf = new byte[nbuf.length * 2];
345 System.arraycopy(nbuf, 0, xbuf, 0, nbufptr);
346 nbuf = xbuf;
347 }
348
349 for (int j = 0; j < cr.length; j++)
350 nbuf[nbufptr++] = cr[j];
351 }
352 else {
353 // copy verbatim in binary mode.
354 nbuf[nbufptr++] = buf[i];
355 }
356
357 break;
358
359 // all other characters are just copied
360 default:
361 nbuf[nbufptr++] = buf[i];
362 break;
363 }
364 }
365
366 xbuf = new byte[nbufptr];
367 System.arraycopy(nbuf, 0, xbuf, 0, nbufptr);
368 write(xbuf);
369 }
370
371 public void setCRLF(String xcrlf) { crlf = xcrlf.getBytes(); }
372 public void setCR(String xcr) { cr = xcr.getBytes(); }
373
374 /**
375 * Handle telnet protocol negotiation. The buffer will be parsed
376 * and necessary actions are taken according to the telnet protocol.
377 * See <A HREF="RFC-Telnet-URL">RFC-Telnet</A>
378 * @param nbuf the byte buffer put out after negotiation
379 * @return number of bytes processed, 0 for none, and -1 for end of buffer.
380 */
381 public int negotiate(byte nbuf[], int offset)
382 throws IOException {
383 int count = tempbuf.length;
384 byte[] buf = tempbuf;
385 byte sendbuf[] = new byte[3];
386 byte b, reply;
387 int boffset = 0, noffset = offset;
388 boolean dobreak = false;
389
390 if (count == 0) // buffer is empty.
391 return -1;
392
393 while (!dobreak && (boffset < count) && (noffset < nbuf.length)) {
394 b = buf[boffset++];
395
396 // of course, byte is a signed entity (-128 -> 127)
397 // but apparently the SGI Netscape 3.0 doesn't seem
398 // to care and provides happily values up to 255
399 if (b >= 128)
400 b = (byte)(b - 256);
401
402 if (debug > 2) {
403 Byte B = new Byte(b);
404 System.err.print("byte: " + B.intValue() + " ");
405 }
406
407 switch (neg_state) {
408 case STATE_DATA:
409 if (b == IAC) {
410 neg_state = STATE_IAC;
411 dobreak = true; // leave the loop so we can sync.
412 }
413 else
414 nbuf[noffset++] = b;
415
416 break;
417
418 case STATE_IAC:
419 switch (b) {
420 case IAC:
421 if (debug > 2) System.err.print("IAC ");
422
423 neg_state = STATE_DATA;
424 nbuf[noffset++] = IAC;
425 break;
426
427 case WILL:
428 if (debug > 2) System.err.print("WILL ");
429
430 neg_state = STATE_IACWILL;
431 break;
432
433 case WONT:
434 if (debug > 2) System.err.print("WONT ");
435
436 neg_state = STATE_IACWONT;
437 break;
438
439 case DONT:
440 if (debug > 2) System.err.print("DONT ");
441
442 neg_state = STATE_IACDONT;
443 break;
444
445 case DO:
446 if (debug > 2) System.err.print("DO ");
447
448 neg_state = STATE_IACDO;
449 break;
450
451 case EOR:
452 if (debug > 1) System.err.print("EOR ");
453
454 notifyEndOfRecord();
455 dobreak = true; // leave the loop so we can sync.
456 neg_state = STATE_DATA;
457 break;
458
459 case SB:
460 if (debug > 2) System.err.print("SB ");
461
462 neg_state = STATE_IACSB;
463 break;
464
465 default:
466 if (debug > 2) System.err.print("<UNKNOWN " + b + " > ");
467
468 neg_state = STATE_DATA;
469 break;
470 }
471
472 break;
473
474 case STATE_IACWILL:
475 switch (b) {
476 case TELOPT_ECHO:
477 if (debug > 2) System.err.println("ECHO");
478
479 reply = DO;
480 setLocalEcho(false);
481 break;
482
483 case TELOPT_SGA:
484 if (debug > 2) System.err.println("SGA");
485
486 reply = DO;
487 break;
488
489 case TELOPT_EOR:
490 if (debug > 2) System.err.println("EOR");
491
492 reply = DO;
493 break;
494
495 case TELOPT_BINARY:
496 if (debug > 2) System.err.println("BINARY");
497
498 reply = DO;
499 break;
500
501 default:
502 if (debug > 2) System.err.println("<UNKNOWN," + b + ">");
503
504 reply = DONT;
505 break;
506 }
507
508 if (debug > 1) System.err.println("<" + b + ", WILL =" + WILL + ">");
509
510 if (reply != sentDX[b + 128] || WILL != receivedWX[b + 128]) {
511 sendbuf[0] = IAC;
512 sendbuf[1] = reply;
513 sendbuf[2] = b;
514 write(sendbuf);
515 sentDX[b + 128] = reply;
516 receivedWX[b + 128] = WILL;
517 }
518
519 neg_state = STATE_DATA;
520 break;
521
522 case STATE_IACWONT:
523 switch (b) {
524 case TELOPT_ECHO:
525 if (debug > 2) System.err.println("ECHO");
526
527 setLocalEcho(true);
528 reply = DONT;
529 break;
530
531 case TELOPT_SGA:
532 if (debug > 2) System.err.println("SGA");
533
534 reply = DONT;
535 break;
536
537 case TELOPT_EOR:
538 if (debug > 2) System.err.println("EOR");
539
540 reply = DONT;
541 break;
542
543 case TELOPT_BINARY:
544 if (debug > 2) System.err.println("BINARY");
545
546 reply = DONT;
547 break;
548
549 default:
550 if (debug > 2) System.err.println("<UNKNOWN," + b + ">");
551
552 reply = DONT;
553 break;
554 }
555
556 if (reply != sentDX[b + 128] || WONT != receivedWX[b + 128]) {
557 sendbuf[0] = IAC;
558 sendbuf[1] = reply;
559 sendbuf[2] = b;
560 write(sendbuf);
561 sentDX[b + 128] = reply;
562 receivedWX[b + 128] = WILL;
563 }
564
565 neg_state = STATE_DATA;
566 break;
567
568 case STATE_IACDO:
569 switch (b) {
570 case TELOPT_ECHO:
571 if (debug > 2) System.err.println("ECHO");
572
573 reply = WILL;
574 setLocalEcho(true);
575 break;
576
577 case TELOPT_SGA:
578 if (debug > 2) System.err.println("SGA");
579
580 reply = WILL;
581 break;
582
583 case TELOPT_TTYPE:
584 if (debug > 2) System.err.println("TTYPE");
585
586 reply = WILL;
587 break;
588
589 case TELOPT_BINARY:
590 if (debug > 2) System.err.println("BINARY");
591
592 reply = WILL;
593 break;
594
595 case TELOPT_NAWS:
596 if (debug > 2) System.err.println("NAWS");
597
598 int[] size = getWindowSize();
599 receivedDX[b] = DO;
600
601 if (size == null) {
602 // this shouldn't happen
603 write(IAC);
604 write(WONT);
605 write(TELOPT_NAWS);
606 reply = WONT;
607 sentWX[b] = WONT;
608 break;
609 }
610
611 reply = WILL;
612 sentWX[b] = WILL;
613 sendbuf[0] = IAC;
614 sendbuf[1] = WILL;
615 sendbuf[2] = TELOPT_NAWS;
616 write(sendbuf);
617 write(IAC); write(SB); write(TELOPT_NAWS);
618 write((byte)(size[0] >> 8));
619 write((byte)(size[0] & 0xff));
620 write((byte)(size[1] >> 8));
621 write((byte)(size[1] & 0xff));
622 write(IAC); write(SE);
623 break;
624
625 default:
626 if (debug > 2) System.err.println("<UNKNOWN," + b + ">");
627
628 reply = WONT;
629 break;
630 }
631
632 if (reply != sentWX[128 + b] || DO != receivedDX[128 + b]) {
633 sendbuf[0] = IAC;
634 sendbuf[1] = reply;
635 sendbuf[2] = b;
636 write(sendbuf);
637 sentWX[b + 128] = reply;
638 receivedDX[b + 128] = DO;
639 }
640
641 neg_state = STATE_DATA;
642 break;
643
644 case STATE_IACDONT:
645 switch (b) {
646 case TELOPT_ECHO:
647 if (debug > 2) System.err.println("ECHO");
648
649 reply = WONT;
650 setLocalEcho(false);
651 break;
652
653 case TELOPT_SGA:
654 if (debug > 2) System.err.println("SGA");
655
656 reply = WONT;
657 break;
658
659 case TELOPT_NAWS:
660 if (debug > 2) System.err.println("NAWS");
661
662 reply = WONT;
663 break;
664
665 case TELOPT_BINARY:
666 if (debug > 2) System.err.println("BINARY");
667
668 reply = WONT;
669 break;
670
671 default:
672 if (debug > 2) System.err.println("<UNKNOWN," + b + ">");
673
674 reply = WONT;
675 break;
676 }
677
678 if (reply != sentWX[b + 128] || DONT != receivedDX[b + 128]) {
679 write(IAC); write(reply); write(b);
680 sentWX[b + 128] = reply;
681 receivedDX[b + 128] = DONT;
682 }
683
684 neg_state = STATE_DATA;
685 break;
686
687 case STATE_IACSBIAC:
688 if (debug > 2) System.err.println("" + b + " ");
689
690 if (b == IAC) {
691 sbbuf = new byte[0];
692 current_sb = b;
693 neg_state = STATE_IACSBDATA;
694 }
695 else {
696 System.err.println("(bad) " + b + " ");
697 neg_state = STATE_DATA;
698 }
699
700 break;
701
702 case STATE_IACSB:
703 if (debug > 2) System.err.println("" + b + " ");
704
705 switch (b) {
706 case IAC:
707 neg_state = STATE_IACSBIAC;
708 break;
709
710 default:
711 current_sb = b;
712 sbbuf = new byte[0];
713 neg_state = STATE_IACSBDATA;
714 break;
715 }
716
717 break;
718
719 case STATE_IACSBDATA:
720 if (debug > 2) System.err.println("" + b + " ");
721
722 switch (b) {
723 case IAC:
724 neg_state = STATE_IACSBDATAIAC;
725 break;
726
727 default:
728 byte[] xsb = new byte[sbbuf.length + 1];
729 System.arraycopy(sbbuf, 0, xsb, 0, sbbuf.length);
730 sbbuf = xsb;
731 sbbuf[sbbuf.length - 1] = b;
732 break;
733 }
734
735 break;
736
737 case STATE_IACSBDATAIAC:
738 if (debug > 2) System.err.println("" + b + " ");
739
740 switch (b) {
741 case IAC:
742 neg_state = STATE_IACSBDATA;
743 byte[] xsb = new byte[sbbuf.length + 1];
744 System.arraycopy(sbbuf, 0, xsb, 0, sbbuf.length);
745 sbbuf = xsb;
746 sbbuf[sbbuf.length - 1] = IAC;
747 break;
748
749 case SE:
750 handle_sb(current_sb, sbbuf);
751 current_sb = 0;
752 neg_state = STATE_DATA;
753 break;
754
755 case SB:
756 handle_sb(current_sb, sbbuf);
757 neg_state = STATE_IACSB;
758 break;
759
760 default:
761 neg_state = STATE_DATA;
762 break;
763 }
764
765 break;
766
767 default:
768 if (debug > 1)
769 System.err.println("This should not happen: " + neg_state + " ");
770
771 neg_state = STATE_DATA;
772 break;
773 }
774 }
775
776 // shrink tempbuf to new processed size.
777 byte[] xb = new byte[count - boffset];
778 System.arraycopy(tempbuf, boffset, xb, 0, count - boffset);
779 tempbuf = xb;
780 return noffset - offset;
781 }
782
783 public void inputfeed(byte[] b, int offset, int len) {
784 byte[] xb = new byte[tempbuf.length + len];
785 System.arraycopy(tempbuf, 0, xb, 0, tempbuf.length);
786 System.arraycopy(b, offset, xb, tempbuf.length, len);
787 tempbuf = xb;
788 }
789 }