LICENSE CHANGED TO MIT
[nikiroo-utils.git] / src / jexer / net / TelnetInputStream.java
CommitLineData
daa4106c 1/*
ea91242c
KL
2 * Jexer - Java Text User Interface
3 *
e16dda65 4 * The MIT License (MIT)
ea91242c 5 *
e16dda65 6 * Copyright (C) 2016 Kevin Lamonte
ea91242c 7 *
e16dda65
KL
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the "Software"),
10 * to deal in the Software without restriction, including without limitation
11 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 * and/or sell copies of the Software, and to permit persons to whom the
13 * Software is furnished to do so, subject to the following conditions:
ea91242c 14 *
e16dda65
KL
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
ea91242c 17 *
e16dda65
KL
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 * DEALINGS IN THE SOFTWARE.
ea91242c
KL
25 *
26 * @author Kevin Lamonte [kevin.lamonte@gmail.com]
27 * @version 1
28 */
29package jexer.net;
30
31import java.io.InputStream;
32import java.io.IOException;
9b1afdde 33import java.util.ArrayList;
ea91242c
KL
34import java.util.Map;
35import java.util.TreeMap;
36
37import jexer.session.SessionInfo;
38import static jexer.net.TelnetSocket.*;
39
40/**
41 * TelnetInputStream works with TelnetSocket to perform the telnet protocol.
42 */
9b1afdde
KL
43public final class TelnetInputStream extends InputStream
44 implements SessionInfo {
ea91242c
KL
45
46 /**
47 * The root TelnetSocket that has my telnet protocol state.
48 */
49 private TelnetSocket master;
50
51 /**
52 * The raw socket's InputStream.
53 */
54 private InputStream input;
55
005ec497
KL
56 /**
57 * The telnet-aware OutputStream.
58 */
59 private TelnetOutputStream output;
60
61 /**
62 * Persistent read buffer. In practice this will only be used if the
63 * single-byte read() is called sometime.
64 */
65 private byte [] readBuffer;
66
67 /**
68 * Current writing position in readBuffer - what is passed into
69 * input.read().
70 */
71 private int readBufferEnd;
72
73 /**
74 * Current read position in readBuffer - what is passed to the client in
75 * response to this.read().
76 */
77 private int readBufferStart;
78
ea91242c
KL
79 /**
80 * Package private constructor.
81 *
82 * @param master the master TelnetSocket
83 * @param input the underlying socket's InputStream
005ec497 84 * @param output the telnet-aware OutputStream
ea91242c 85 */
005ec497
KL
86 TelnetInputStream(final TelnetSocket master, final InputStream input,
87 final TelnetOutputStream output) {
88
ea91242c
KL
89 this.master = master;
90 this.input = input;
005ec497
KL
91 this.output = output;
92
93 // Setup new read buffer
94 readBuffer = new byte[1024];
95 readBufferStart = 0;
96 readBufferEnd = 0;
9b1afdde 97 subnegBuffer = new ArrayList<Byte>();
ea91242c
KL
98 }
99
100 // SessionInfo interface --------------------------------------------------
101
102 /**
103 * User name.
104 */
105 private String username = "";
106
107 /**
108 * Language.
109 */
110 private String language = "en_US";
111
112 /**
113 * Text window width.
114 */
115 private int windowWidth = 80;
116
117 /**
118 * Text window height.
119 */
120 private int windowHeight = 24;
121
122 /**
123 * Username getter.
124 *
125 * @return the username
126 */
127 public String getUsername() {
128 return this.username;
129 }
130
131 /**
132 * Username setter.
133 *
134 * @param username the value
135 */
136 public void setUsername(final String username) {
137 this.username = username;
138 }
139
140 /**
141 * Language getter.
142 *
143 * @return the language
144 */
145 public String getLanguage() {
146 return this.language;
147 }
148
149 /**
150 * Language setter.
151 *
152 * @param language the value
153 */
154 public void setLanguage(final String language) {
155 this.language = language;
156 }
157
158 /**
159 * Text window width getter.
160 *
161 * @return the window width
162 */
163 public int getWindowWidth() {
164 return windowWidth;
165 }
166
167 /**
168 * Text window height getter.
169 *
170 * @return the window height
171 */
172 public int getWindowHeight() {
173 return windowHeight;
174 }
175
176 /**
177 * Re-query the text window size.
178 */
179 public void queryWindowSize() {
180 // NOP
181 }
182
183 // InputStream interface --------------------------------------------------
184
185 /**
186 * Returns an estimate of the number of bytes that can be read (or
187 * skipped over) from this input stream without blocking by the next
188 * invocation of a method for this input stream.
189 *
9b1afdde
KL
190 * @return an estimate of the number of bytes that can be read (or
191 * skipped over) from this input stream without blocking or 0 when it
192 * reaches the end of the input stream.
193 * @throws IOException if an I/O error occurs
ea91242c
KL
194 */
195 @Override
196 public int available() throws IOException {
005ec497
KL
197 if (readBuffer == null) {
198 throw new IOException("InputStream is closed");
199 }
200 if (readBufferEnd - readBufferStart > 0) {
201 return (readBufferEnd - readBufferStart);
202 }
203 return input.available();
ea91242c
KL
204 }
205
206 /**
207 * Closes this input stream and releases any system resources associated
208 * with the stream.
9b1afdde
KL
209 *
210 * @throws IOException if an I/O error occurs
ea91242c
KL
211 */
212 @Override
213 public void close() throws IOException {
005ec497
KL
214 if (readBuffer != null) {
215 readBuffer = null;
216 input.close();
217 }
ea91242c
KL
218 }
219
220 /**
221 * Marks the current position in this input stream.
9b1afdde
KL
222 *
223 * @param readLimit the maximum limit of bytes that can be read before
224 * the mark position becomes invalid
ea91242c
KL
225 */
226 @Override
9b1afdde 227 public void mark(final int readLimit) {
005ec497 228 // Do nothing
ea91242c
KL
229 }
230
231 /**
232 * Tests if this input stream supports the mark and reset methods.
9b1afdde
KL
233 *
234 * @return true if this stream instance supports the mark and reset
235 * methods; false otherwise
ea91242c
KL
236 */
237 @Override
238 public boolean markSupported() {
239 return false;
240 }
241
242 /**
243 * Reads the next byte of data from the input stream.
9b1afdde
KL
244 *
245 * @return the next byte of data, or -1 if there is no more data because
246 * the end of the stream has been reached.
247 * @throws IOException if an I/O error occurs
ea91242c
KL
248 */
249 @Override
250 public int read() throws IOException {
005ec497
KL
251
252 // If the post-processed buffer has bytes, use that.
253 if (readBufferEnd - readBufferStart > 0) {
254 readBufferStart++;
255 return readBuffer[readBufferStart - 1];
256 }
257
258 // The buffer is empty, so reset the indexes to 0.
259 readBufferStart = 0;
260 readBufferEnd = 0;
261
262 // Read some fresh data and run it through the telnet protocol.
263 int rc = readImpl(readBuffer, readBufferEnd,
264 readBuffer.length - readBufferEnd);
265
266 // If we got something, return it.
267 if (rc > 0) {
268 readBufferStart++;
269 return readBuffer[readBufferStart - 1];
270 }
271 // If we read 0, I screwed up big time.
272 assert (rc != 0);
273
274 // We read -1 (EOF).
275 return rc;
ea91242c
KL
276 }
277
278 /**
279 * Reads some number of bytes from the input stream and stores them into
280 * the buffer array b.
9b1afdde
KL
281 *
282 * @param b the buffer into which the data is read.
283 * @return the total number of bytes read into the buffer, or -1 if there
284 * is no more data because the end of the stream has been reached.
285 * @throws IOException if an I/O error occurs
ea91242c
KL
286 */
287 @Override
9b1afdde 288 public int read(final byte[] b) throws IOException {
005ec497 289 return read(b, 0, b.length);
ea91242c
KL
290 }
291
292 /**
293 * Reads up to len bytes of data from the input stream into an array of
294 * bytes.
9b1afdde
KL
295 *
296 * @param b the buffer into which the data is read.
297 * @param off the start offset in array b at which the data is written.
298 * @param len the maximum number of bytes to read.
299 * @return the total number of bytes read into the buffer, or -1 if there
300 * is no more data because the end of the stream has been reached.
301 * @throws IOException if an I/O error occurs
ea91242c
KL
302 */
303 @Override
9b1afdde
KL
304 public int read(final byte[] b, final int off,
305 final int len) throws IOException {
306
005ec497
KL
307 // The only time we can return 0 is if len is 0, as per the
308 // InputStream contract.
309 if (len == 0) {
310 return 0;
311 }
312
313 // If the post-processed buffer has bytes, use that.
314 if (readBufferEnd - readBufferStart > 0) {
315 int n = Math.min(len, readBufferEnd - readBufferStart);
316 System.arraycopy(b, off, readBuffer, readBufferStart, n);
317 readBufferStart += n;
318 return n;
319 }
320
321 // The buffer is empty, so reset the indexes to 0.
322 readBufferStart = 0;
323 readBufferEnd = 0;
324
325 // The maximum number of bytes we will ask for will definitely be
326 // within the bounds of what we can return in a single call.
327 int n = Math.min(len, readBuffer.length);
328
329 // Read some fresh data and run it through the telnet protocol.
330 int rc = readImpl(readBuffer, readBufferEnd, n);
331
332 // If we got something, return it.
333 if (rc > 0) {
9b1afdde 334 System.arraycopy(readBuffer, 0, b, off, rc);
005ec497
KL
335 return rc;
336 }
337 // If we read 0, I screwed up big time.
338 assert (rc != 0);
339
340 // We read -1 (EOF).
341 return rc;
ea91242c
KL
342 }
343
344 /**
345 * Repositions this stream to the position at the time the mark method
9b1afdde
KL
346 * was last called on this input stream. This is not supported by
347 * TelnetInputStream, so IOException is always thrown.
348 *
349 * @throws IOException if this function is used
ea91242c
KL
350 */
351 @Override
352 public void reset() throws IOException {
005ec497 353 throw new IOException("InputStream does not support mark/reset");
ea91242c
KL
354 }
355
356 /**
357 * Skips over and discards n bytes of data from this input stream.
9b1afdde
KL
358 *
359 * @param n the number of bytes to be skipped
360 * @return the actual number of bytes skipped
361 * @throws IOException if an I/O error occurs
ea91242c
KL
362 */
363 @Override
9b1afdde 364 public long skip(final long n) throws IOException {
005ec497
KL
365 if (n < 0) {
366 return 0;
367 }
368 for (int i = 0; i < n; i++) {
369 read();
370 }
371 return n;
ea91242c
KL
372 }
373
ea91242c
KL
374 // Telnet protocol --------------------------------------------------------
375
9b1afdde
KL
376
377 /**
378 * When true, the last read byte from the remote side was IAC.
379 */
380 private boolean iac = false;
381
382 /**
383 * When true, we are in the middle of a DO/DONT/WILL/WONT negotiation.
384 */
385 private boolean dowill = false;
386
387 /**
388 * The telnet option being negotiated.
389 */
390 private int dowillType = 0;
391
392 /**
393 * When true, we are waiting to see the end of the sub-negotiation
394 * sequence.
395 */
396 private boolean subnegEnd = false;
397
398 /**
399 * When true, the last byte read from the remote side was CR.
400 */
401 private boolean readCR = false;
402
ea91242c
KL
403 /**
404 * The subnegotiation buffer.
405 */
9b1afdde 406 private ArrayList<Byte> subnegBuffer;
ea91242c
KL
407
408 /**
409 * For debugging, return a descriptive string for this telnet option.
410 * These are pulled from: http://www.iana.org/assignments/telnet-options
411 *
9b1afdde 412 * @param option the telnet option byte
ea91242c
KL
413 * @return a string describing the telnet option code
414 */
0d47c546 415 @SuppressWarnings("unused")
ea91242c
KL
416 private String optionString(final int option) {
417 switch (option) {
418 case 0: return "Binary Transmission";
419 case 1: return "Echo";
420 case 2: return "Reconnection";
421 case 3: return "Suppress Go Ahead";
422 case 4: return "Approx Message Size Negotiation";
423 case 5: return "Status";
424 case 6: return "Timing Mark";
425 case 7: return "Remote Controlled Trans and Echo";
426 case 8: return "Output Line Width";
427 case 9: return "Output Page Size";
428 case 10: return "Output Carriage-Return Disposition";
429 case 11: return "Output Horizontal Tab Stops";
430 case 12: return "Output Horizontal Tab Disposition";
431 case 13: return "Output Formfeed Disposition";
432 case 14: return "Output Vertical Tabstops";
433 case 15: return "Output Vertical Tab Disposition";
434 case 16: return "Output Linefeed Disposition";
435 case 17: return "Extended ASCII";
436 case 18: return "Logout";
437 case 19: return "Byte Macro";
438 case 20: return "Data Entry Terminal";
439 case 21: return "SUPDUP";
440 case 22: return "SUPDUP Output";
441 case 23: return "Send Location";
442 case 24: return "Terminal Type";
443 case 25: return "End of Record";
444 case 26: return "TACACS User Identification";
445 case 27: return "Output Marking";
446 case 28: return "Terminal Location Number";
447 case 29: return "Telnet 3270 Regime";
448 case 30: return "X.3 PAD";
449 case 31: return "Negotiate About Window Size";
450 case 32: return "Terminal Speed";
451 case 33: return "Remote Flow Control";
452 case 34: return "Linemode";
453 case 35: return "X Display Location";
454 case 36: return "Environment Option";
455 case 37: return "Authentication Option";
456 case 38: return "Encryption Option";
457 case 39: return "New Environment Option";
458 case 40: return "TN3270E";
459 case 41: return "XAUTH";
460 case 42: return "CHARSET";
461 case 43: return "Telnet Remote Serial Port (RSP)";
462 case 44: return "Com Port Control Option";
463 case 45: return "Telnet Suppress Local Echo";
464 case 46: return "Telnet Start TLS";
465 case 47: return "KERMIT";
466 case 48: return "SEND-URL";
467 case 49: return "FORWARD_X";
468 case 138: return "TELOPT PRAGMA LOGON";
469 case 139: return "TELOPT SSPI LOGON";
470 case 140: return "TELOPT PRAGMA HEARTBEAT";
471 case 255: return "Extended-Options-List";
472 default:
473 if ((option >= 50) && (option <= 137)) {
474 return "Unassigned";
475 }
476 return "UNKNOWN - OTHER";
477 }
478 }
479
480 /**
481 * Send a DO/DON'T/WILL/WON'T response to the remote side.
482 *
483 * @param response a TELNET_DO/DONT/WILL/WONT byte
484 * @param option telnet option byte (binary mode, term type, etc.)
9b1afdde 485 * @throws IOException if an I/O error occurs
ea91242c
KL
486 */
487 private void respond(final int response,
488 final int option) throws IOException {
489
490 byte [] buffer = new byte[3];
491 buffer[0] = (byte)TELNET_IAC;
492 buffer[1] = (byte)response;
493 buffer[2] = (byte)option;
494
005ec497 495 output.rawWrite(buffer);
ea91242c
KL
496 }
497
498 /**
499 * Tell the remote side we WILL support an option.
500 *
501 * @param option telnet option byte (binary mode, term type, etc.)
9b1afdde 502 * @throws IOException if an I/O error occurs
ea91242c
KL
503 */
504 private void WILL(final int option) throws IOException {
505 respond(TELNET_WILL, option);
506 }
507
508 /**
509 * Tell the remote side we WON'T support an option.
510 *
511 * @param option telnet option byte (binary mode, term type, etc.)
9b1afdde 512 * @throws IOException if an I/O error occurs
ea91242c
KL
513 */
514 private void WONT(final int option) throws IOException {
515 respond(TELNET_WONT, option);
516 }
517
518 /**
519 * Tell the remote side we DO support an option.
520 *
521 * @param option telnet option byte (binary mode, term type, etc.)
9b1afdde 522 * @throws IOException if an I/O error occurs
ea91242c
KL
523 */
524 private void DO(final int option) throws IOException {
525 respond(TELNET_DO, option);
526 }
527
528 /**
529 * Tell the remote side we DON'T support an option.
530 *
531 * @param option telnet option byte (binary mode, term type, etc.)
9b1afdde 532 * @throws IOException if an I/O error occurs
ea91242c
KL
533 */
534 private void DONT(final int option) throws IOException {
535 respond(TELNET_DONT, option);
536 }
537
538 /**
539 * Tell the remote side we WON't or DON'T support an option.
540 *
541 * @param remoteQuery a TELNET_DO/DONT/WILL/WONT byte
542 * @param option telnet option byte (binary mode, term type, etc.)
9b1afdde 543 * @throws IOException if an I/O error occurs
ea91242c
KL
544 */
545 private void refuse(final int remoteQuery,
546 final int option) throws IOException {
547
548 if (remoteQuery == TELNET_DO) {
549 WONT(option);
550 } else {
551 DONT(option);
552 }
553 }
554
555 /**
9b1afdde 556 * Build sub-negotiation packet (RFC 855).
ea91242c
KL
557 *
558 * @param option telnet option
559 * @param response output buffer of response bytes
9b1afdde 560 * @throws IOException if an I/O error occurs
ea91242c
KL
561 */
562 private void telnetSendSubnegResponse(final int option,
563 final byte [] response) throws IOException {
564
565 byte [] buffer = new byte[response.length + 5];
566 buffer[0] = (byte)TELNET_IAC;
567 buffer[1] = (byte)TELNET_SB;
568 buffer[2] = (byte)option;
569 System.arraycopy(response, 0, buffer, 3, response.length);
570 buffer[response.length + 3] = (byte)TELNET_IAC;
571 buffer[response.length + 4] = (byte)TELNET_SE;
005ec497 572 output.rawWrite(buffer);
ea91242c
KL
573 }
574
575 /**
576 * Telnet option: Terminal Speed (RFC 1079). Client side.
9b1afdde
KL
577 *
578 * @throws IOException if an I/O error occurs
ea91242c
KL
579 */
580 private void telnetSendTerminalSpeed() throws IOException {
581 byte [] response = {0, '3', '8', '4', '0', '0', ',',
582 '3', '8', '4', '0', '0'};
583 telnetSendSubnegResponse(32, response);
584 }
585
586 /**
587 * Telnet option: Terminal Type (RFC 1091). Client side.
9b1afdde
KL
588 *
589 * @throws IOException if an I/O error occurs
ea91242c
KL
590 */
591 private void telnetSendTerminalType() throws IOException {
592 byte [] response = {0, 'v', 't', '1', '0', '0' };
593 telnetSendSubnegResponse(24, response);
594 }
595
596 /**
597 * Telnet option: Terminal Type (RFC 1091). Server side.
9b1afdde
KL
598 *
599 * @throws IOException if an I/O error occurs
ea91242c
KL
600 */
601 private void requestTerminalType() throws IOException {
602 byte [] response = new byte[1];
603 response[0] = 1;
604 telnetSendSubnegResponse(24, response);
605 }
606
607 /**
608 * Telnet option: Terminal Speed (RFC 1079). Server side.
9b1afdde
KL
609 *
610 * @throws IOException if an I/O error occurs
ea91242c
KL
611 */
612 private void requestTerminalSpeed() throws IOException {
613 byte [] response = new byte[1];
614 response[0] = 1;
615 telnetSendSubnegResponse(32, response);
616 }
617
618 /**
619 * Telnet option: New Environment (RFC 1572). Server side.
9b1afdde
KL
620 *
621 * @throws IOException if an I/O error occurs
ea91242c
KL
622 */
623 private void requestEnvironment() throws IOException {
624 byte [] response = new byte[1];
625 response[0] = 1;
626 telnetSendSubnegResponse(39, response);
627 }
628
629 /**
9b1afdde
KL
630 * Send the options we want to negotiate on.
631 *
632 * <p>The options we use are:
633 *
634 * <p>
635 * <pre>
ea91242c
KL
636 * Binary Transmission RFC 856
637 * Suppress Go Ahead RFC 858
638 * Negotiate About Window Size RFC 1073
639 * Terminal Type RFC 1091
640 * Terminal Speed RFC 1079
641 * New Environment RFC 1572
642 *
643 * When run as a server:
9b1afdde
KL
644 * Echo RFC 857
645 * </pre>
646 *
647 * @throws IOException if an I/O error occurs
ea91242c 648 */
005ec497 649 void telnetSendOptions() throws IOException {
9b1afdde 650 if (master.binaryMode == false) {
ea91242c
KL
651 // Binary Transmission: must ask both do and will
652 DO(0);
653 WILL(0);
654 }
655
9b1afdde 656 if (master.goAhead == true) {
ea91242c
KL
657 // Suppress Go Ahead
658 DO(3);
659 WILL(3);
660 }
661
662 // Server only options
9b1afdde 663 if (master.isServer == true) {
ea91242c
KL
664 // Enable Echo - I echo to them, they do not echo back to me.
665 DONT(1);
666 WILL(1);
667
9b1afdde 668 if (master.doTermType == true) {
ea91242c
KL
669 // Terminal type - request it
670 DO(24);
671 }
672
9b1afdde 673 if (master.doTermSpeed == true) {
ea91242c
KL
674 // Terminal speed - request it
675 DO(32);
676 }
677
9b1afdde 678 if (master.doNAWS == true) {
ea91242c
KL
679 // NAWS - request it
680 DO(31);
681 }
682
9b1afdde 683 if (master.doEnvironment == true) {
ea91242c
KL
684 // Environment - request it
685 DO(39);
686 }
687
688 } else {
689
9b1afdde 690 if (master.doTermType == true) {
ea91242c
KL
691 // Terminal type - request it
692 WILL(24);
693 }
694
9b1afdde 695 if (master.doTermSpeed == true) {
ea91242c
KL
696 // Terminal speed - request it
697 WILL(32);
698 }
699
9b1afdde 700 if (master.doNAWS == true) {
ea91242c
KL
701 // NAWS - request it
702 WILL(31);
703 }
704
9b1afdde 705 if (master.doEnvironment == true) {
ea91242c
KL
706 // Environment - request it
707 WILL(39);
708 }
ea91242c 709 }
9b1afdde
KL
710
711 // Push it all out
712 output.flush();
ea91242c
KL
713 }
714
715 /**
716 * New Environment parsing state.
717 */
718 private enum EnvState {
719 INIT,
720 TYPE,
721 NAME,
722 VALUE
723 }
724
725 /**
726 * Handle the New Environment option. Note that this implementation
727 * fails to handle ESC as defined in RFC 1572.
728 */
729 private void handleNewEnvironment() {
9b1afdde
KL
730 Map<StringBuilder, StringBuilder> newEnv =
731 new TreeMap<StringBuilder, StringBuilder>();
732
ea91242c
KL
733 EnvState state = EnvState.INIT;
734 StringBuilder name = new StringBuilder();
735 StringBuilder value = new StringBuilder();
736
9b1afdde
KL
737 for (int i = 0; i < subnegBuffer.size(); i++) {
738 Byte b = subnegBuffer.get(i);
ea91242c
KL
739
740 switch (state) {
741
742 case INIT:
743 // Looking for "IS"
744 if (b == 0) {
745 state = EnvState.TYPE;
746 } else {
747 // The other side isn't following the rules, see ya.
748 return;
749 }
750 break;
751
752 case TYPE:
753 // Looking for "VAR" or "USERVAR"
754 if (b == 0) {
755 // VAR
756 state = EnvState.NAME;
757 name = new StringBuilder();
758 } else if (b == 3) {
759 // USERVAR
760 state = EnvState.NAME;
761 name = new StringBuilder();
762 } else {
763 // The other side isn't following the rules, see ya
764 return;
765 }
766 break;
767
768 case NAME:
769 // Looking for "VALUE" or a name byte
770 if (b == 1) {
771 // VALUE
772 state = EnvState.VALUE;
773 value = new StringBuilder();
774 } else {
775 // Take it as an environment variable name/key byte
9b1afdde 776 name.append((char)b.byteValue());
ea91242c
KL
777 }
778
779 break;
780
781 case VALUE:
782 // Looking for "VAR", "USERVAR", or a name byte, or the end
783 if (b == 0) {
784 // VAR
785 state = EnvState.NAME;
786 if (value.length() > 0) {
787 newEnv.put(name, value);
788 }
789 name = new StringBuilder();
790 } else if (b == 3) {
791 // USERVAR
792 state = EnvState.NAME;
793 if (value.length() > 0) {
794 newEnv.put(name, value);
795 }
796 name = new StringBuilder();
797 } else {
798 // Take it as an environment variable value byte
9b1afdde 799 value.append((char)b.byteValue());
ea91242c
KL
800 }
801 break;
9b1afdde
KL
802
803 default:
804 throw new RuntimeException("Invalid state: " + state);
805
ea91242c
KL
806 }
807 }
808
809 if ((name.length() > 0) && (value.length() > 0)) {
810 newEnv.put(name, value);
811 }
812
813 for (StringBuilder key: newEnv.keySet()) {
814 if (key.equals("LANG")) {
815 language = newEnv.get(key).toString();
816 }
817 if (key.equals("LOGNAME")) {
818 username = newEnv.get(key).toString();
819 }
820 if (key.equals("USER")) {
821 username = newEnv.get(key).toString();
822 }
823 }
824 }
825
826 /**
827 * Handle an option sub-negotiation.
9b1afdde
KL
828 *
829 * @throws IOException if an I/O error occurs
ea91242c
KL
830 */
831 private void handleSubneg() throws IOException {
9b1afdde 832 Byte option;
ea91242c
KL
833
834 // Sanity check: there must be at least 1 byte in subnegBuffer
9b1afdde 835 if (subnegBuffer.size() < 1) {
ea91242c
KL
836 // Buffer too small: the other side is a broken telnetd, it did
837 // not send the right sub-negotiation data. Bail out now.
838 return;
839 }
9b1afdde 840 option = subnegBuffer.get(0);
ea91242c
KL
841
842 switch (option) {
843
844 case 24:
845 // Terminal Type
9b1afdde 846 if ((subnegBuffer.size() > 1) && (subnegBuffer.get(1) == 1)) {
ea91242c
KL
847 // Server sent "SEND", we say "IS"
848 telnetSendTerminalType();
849 }
9b1afdde 850 if ((subnegBuffer.size() > 1) && (subnegBuffer.get(1) == 0)) {
ea91242c
KL
851 // Client sent "IS", record it
852 StringBuilder terminalString = new StringBuilder();
9b1afdde
KL
853 for (int i = 2; i < subnegBuffer.size(); i++) {
854 terminalString.append((char)subnegBuffer.
855 get(i).byteValue());
ea91242c 856 }
9b1afdde 857 master.terminalType = terminalString.toString();
ea91242c
KL
858 }
859 break;
860
861 case 32:
862 // Terminal Speed
9b1afdde 863 if ((subnegBuffer.size() > 1) && (subnegBuffer.get(1) == 1)) {
ea91242c
KL
864 // Server sent "SEND", we say "IS"
865 telnetSendTerminalSpeed();
866 }
9b1afdde 867 if ((subnegBuffer.size() > 1) && (subnegBuffer.get(1) == 0)) {
ea91242c
KL
868 // Client sent "IS", record it
869 StringBuilder speedString = new StringBuilder();
9b1afdde
KL
870 for (int i = 2; i < subnegBuffer.size(); i++) {
871 speedString.append((char)subnegBuffer.get(i).byteValue());
ea91242c 872 }
9b1afdde 873 master.terminalSpeed = speedString.toString();
ea91242c
KL
874 }
875 break;
876
877 case 31:
878 // NAWS
9b1afdde 879 if (subnegBuffer.size() >= 5) {
ea91242c
KL
880 int i = 0;
881
882 i++;
9b1afdde 883 if (subnegBuffer.get(i) == (byte)TELNET_IAC) {
ea91242c
KL
884 i++;
885 }
9b1afdde 886 windowWidth = subnegBuffer.get(i) * 256;
ea91242c
KL
887
888 i++;
9b1afdde 889 if (subnegBuffer.get(i) == (byte)TELNET_IAC) {
ea91242c
KL
890 i++;
891 }
9b1afdde 892 windowWidth += subnegBuffer.get(i);
ea91242c
KL
893
894 i++;
9b1afdde 895 if (subnegBuffer.get(i) == (byte)TELNET_IAC) {
ea91242c
KL
896 i++;
897 }
9b1afdde 898 windowHeight = subnegBuffer.get(i) * 256;
ea91242c
KL
899
900 i++;
9b1afdde 901 if (subnegBuffer.get(i) == (byte)TELNET_IAC) {
ea91242c
KL
902 i++;
903 }
9b1afdde 904 windowHeight += subnegBuffer.get(i);
ea91242c
KL
905 }
906 break;
907
908 case 39:
909 // Environment
910 handleNewEnvironment();
911 break;
912
913 default:
914 // Ignore this one
915 break;
916 }
917 }
918
005ec497
KL
919 /**
920 * Reads up to len bytes of data from the input stream into an array of
921 * bytes.
9b1afdde
KL
922 *
923 * @param buf the buffer into which the data is read.
924 * @param off the start offset in array b at which the data is written.
925 * @param len the maximum number of bytes to read.
926 * @return the total number of bytes read into the buffer, or -1 if there
927 * is no more data because the end of the stream has been reached.
928 * @throws IOException if an I/O error occurs
005ec497 929 */
9b1afdde
KL
930 private int readImpl(final byte[] buf, final int off,
931 final int len) throws IOException {
932
005ec497 933 assert (len > 0);
9b1afdde
KL
934
935 // The current writing position in buf.
936 int bufN = 0;
937
938 // We will keep trying to read() until we have something to return.
939 do {
940
941 // Read up to len bytes
942 byte [] buffer = new byte[len];
943 int bufferN = 0;
944
945 // Read some data from the other end
946 int rc = input.read(buffer);
947
948 // Check for EOF or error
949 if (rc > 0) {
950 // More data came in
951 bufferN = rc;
952 } else {
953 // EOF, just return it.
954 return rc;
955 }
956
957 // Loop through the read bytes
958 for (int i = 0; i < bufferN; i++) {
959 byte b = buffer[i];
960
961 if (subnegEnd == true) {
962 // Looking for IAC SE to end this subnegotiation
963 if (b == (byte)TELNET_SE) {
964 if (iac == true) {
965 iac = false;
966 subnegEnd = false;
967 handleSubneg();
968 }
969 } else if (b == (byte)TELNET_IAC) {
970 if (iac == true) {
971 // An argument to the subnegotiation option
972 subnegBuffer.add((byte)TELNET_IAC);
973 } else {
974 iac = true;
975 }
976 } else {
977 // An argument to the subnegotiation option
978 subnegBuffer.add(b);
979 }
980 continue;
981 }
982
983 // Look for DO/DON'T/WILL/WON'T option
984 if (dowill == true) {
985
986 // Look for option/
987 switch (b) {
988
989 case 0:
990 // Binary Transmission
991 if (dowillType == (byte)TELNET_WILL) {
992 // Server will use binary transmission, yay.
993 master.binaryMode = true;
994 } else if (dowillType == (byte)TELNET_DO) {
995 // Server asks for binary transmission.
996 WILL(b);
997 master.binaryMode = true;
998 } else if (dowillType == (byte)TELNET_WONT) {
999 // We're screwed, server won't do binary
1000 // transmission.
1001 master.binaryMode = false;
1002 } else {
1003 // Server demands NVT ASCII mode.
1004 master.binaryMode = false;
1005 }
1006 break;
1007
1008 case 1:
1009 // Echo
1010 if (dowillType == (byte)TELNET_WILL) {
1011 // Server will use echo, yay.
1012 master.echoMode = true;
1013 } else if (dowillType == (byte)TELNET_DO) {
1014 // Server asks for echo.
1015 WILL(b);
1016 master.echoMode = true;
1017 } else if (dowillType == (byte)TELNET_WONT) {
1018 // We're screwed, server won't do echo.
1019 master.echoMode = false;
1020 } else {
1021 // Server demands no echo.
1022 master.echoMode = false;
1023 }
1024 break;
1025
1026 case 3:
1027 // Suppress Go Ahead
1028 if (dowillType == (byte)TELNET_WILL) {
1029 // Server will use suppress go-ahead, yay.
1030 master.goAhead = false;
1031 } else if (dowillType == (byte)TELNET_DO) {
1032 // Server asks for suppress go-ahead.
1033 WILL(b);
1034 master.goAhead = false;
1035 } else if (dowillType == (byte)TELNET_WONT) {
1036 // We're screwed, server won't do suppress
1037 // go-ahead.
1038 master.goAhead = true;
1039 } else {
1040 // Server demands Go-Ahead mode.
1041 master.goAhead = true;
1042 }
1043 break;
1044
1045 case 24:
1046 // Terminal Type - send what's in TERM
1047 if (dowillType == (byte)TELNET_WILL) {
1048 // Server will use terminal type, yay.
1049 if (master.isServer
1050 && master.doTermType
1051 ) {
1052 requestTerminalType();
1053 master.doTermType = false;
1054 } else if (!master.isServer) {
1055 master.doTermType = true;
1056 }
1057 } else if (dowillType == (byte)TELNET_DO) {
1058 // Server asks for terminal type.
1059 WILL(b);
1060 master.doTermType = true;
1061 } else if (dowillType == (byte)TELNET_WONT) {
1062 // We're screwed, server won't do terminal type.
1063 master.doTermType = false;
1064 } else {
1065 // Server will not listen to terminal type.
1066 master.doTermType = false;
1067 }
1068 break;
1069
1070 case 31:
1071 // NAWS
1072 if (dowillType == (byte)TELNET_WILL) {
1073 // Server will use NAWS, yay.
1074 master.doNAWS = true;
1075 // NAWS cannot be requested by the server, it is
1076 // only sent by the client.
1077 } else if (dowillType == (byte)TELNET_DO) {
1078 // Server asks for NAWS.
1079 WILL(b);
1080 master.doNAWS = true;
1081 } else if (dowillType == (byte)TELNET_WONT) {
1082 // Server won't do NAWS.
1083 master.doNAWS = false;
1084 } else {
1085 // Server will not listen to NAWS.
1086 master.doNAWS = false;
1087 }
1088 break;
1089
1090 case 32:
1091 // Terminal Speed
1092 if (dowillType == (byte)TELNET_WILL) {
1093 // Server will use terminal speed, yay.
1094 if (master.isServer
1095 && master.doTermSpeed
1096 ) {
1097 requestTerminalSpeed();
1098 master.doTermSpeed = false;
1099 } else if (!master.isServer) {
1100 master.doTermSpeed = true;
1101 }
1102 } else if (dowillType == (byte)TELNET_DO) {
1103 // Server asks for terminal speed.
1104 WILL(b);
1105 master.doTermSpeed = true;
1106 } else if (dowillType == (byte)TELNET_WONT) {
1107 // We're screwed, server won't do terminal speed.
1108 master.doTermSpeed = false;
1109 } else {
1110 // Server will not listen to terminal speed.
1111 master.doTermSpeed = false;
1112 }
1113 break;
1114
1115 case 39:
1116 // New Environment
1117 if (dowillType == (byte)TELNET_WILL) {
1118 // Server will use NewEnvironment, yay.
1119 if (master.isServer
1120 && master.doEnvironment
1121 ) {
1122 requestEnvironment();
1123 master.doEnvironment = false;
1124 } else if (!master.isServer) {
1125 master.doEnvironment = true;
1126 }
1127 } else if (dowillType == (byte)TELNET_DO) {
1128 // Server asks for NewEnvironment.
1129 WILL(b);
1130 master.doEnvironment = true;
1131 } else if (dowillType == (byte)TELNET_WONT) {
1132 // Server won't do NewEnvironment.
1133 master.doEnvironment = false;
1134 } else {
1135 // Server will not listen to New Environment.
1136 master.doEnvironment = false;
1137 }
1138 break;
1139
1140
1141 default:
1142 // Other side asked for something we don't
1143 // understand. Tell them we will not do this option.
1144 refuse(dowillType, b);
1145 break;
1146 }
1147
1148 dowill = false;
1149 continue;
1150 } // if (dowill == true)
1151
1152 // Perform read processing
1153 if (b == (byte)TELNET_IAC) {
1154
1155 // Telnet command
1156 if (iac == true) {
1157 // IAC IAC -> IAC
1158 buf[bufN++] = (byte)TELNET_IAC;
1159 iac = false;
1160 } else {
1161 iac = true;
1162 }
1163 continue;
1164 } else {
1165 if (iac == true) {
1166
1167 switch (b) {
1168
1169 case (byte)TELNET_SE:
1170 // log.debug1(" END Sub-Negotiation");
1171 break;
1172 case (byte)TELNET_NOP:
1173 // log.debug1(" NOP");
1174 break;
1175 case (byte)TELNET_DM:
1176 // log.debug1(" Data Mark");
1177 break;
1178 case (byte)TELNET_BRK:
1179 // log.debug1(" Break");
1180 break;
1181 case (byte)TELNET_IP:
1182 // log.debug1(" Interrupt Process");
1183 break;
1184 case (byte)TELNET_AO:
1185 // log.debug1(" Abort Output");
1186 break;
1187 case (byte)TELNET_AYT:
1188 // log.debug1(" Are You There?");
1189 break;
1190 case (byte)TELNET_EC:
1191 // log.debug1(" Erase Character");
1192 break;
1193 case (byte)TELNET_EL:
1194 // log.debug1(" Erase Line");
1195 break;
1196 case (byte)TELNET_GA:
1197 // log.debug1(" Go Ahead");
1198 break;
1199 case (byte)TELNET_SB:
1200 // log.debug1(" START Sub-Negotiation");
1201 // From here we wait for the IAC SE
1202 subnegEnd = true;
1203 subnegBuffer.clear();
1204 break;
1205 case (byte)TELNET_WILL:
1206 // log.debug1(" WILL");
1207 dowill = true;
1208 dowillType = b;
1209 break;
1210 case (byte)TELNET_WONT:
1211 // log.debug1(" WON'T");
1212 dowill = true;
1213 dowillType = b;
1214 break;
1215 case (byte)TELNET_DO:
1216 // log.debug1(" DO");
1217 dowill = true;
1218 dowillType = b;
1219
1220 if (master.binaryMode == true) {
1221 // log.debug1("Telnet DO in binary mode");
1222 }
1223
1224 break;
1225 case (byte)TELNET_DONT:
1226 // log.debug1(" DON'T");
1227 dowill = true;
1228 dowillType = b;
1229 break;
1230 default:
1231 // This should be equivalent to IAC NOP
1232 // log.debug1("Will treat as IAC NOP");
1233 break;
1234 }
1235 iac = false;
1236 continue;
1237
1238 } // if (iac == true)
1239
1240 /*
1241 * All of the regular IAC processing is completed at this
1242 * point. Now we need to handle the CR and CR LF cases.
1243 *
1244 * According to RFC 854, in NVT ASCII mode:
1245 * Bare CR -> CR NUL
1246 * CR LF -> CR LF
1247 *
1248 */
1249 if (master.binaryMode == false) {
1250
1251 if (b == C_LF) {
1252 if (readCR == true) {
1253 // This is CR LF. Send CR LF and turn the cr
1254 // flag off.
1255 buf[bufN++] = C_CR;
1256 buf[bufN++] = C_LF;
1257 readCR = false;
1258 continue;
1259 }
1260 // This is bare LF. Send LF.
1261 buf[bufN++] = C_LF;
1262 continue;
1263 }
1264
1265 if (b == C_NUL) {
1266 if (readCR == true) {
1267 // This is CR NUL. Send CR and turn the cr
1268 // flag off.
1269 buf[bufN++] = C_CR;
1270 readCR = false;
1271 continue;
1272 }
1273 // This is bare NUL. Send NUL.
1274 buf[bufN++] = C_NUL;
1275 continue;
1276 }
1277
1278 if (b == C_CR) {
1279 if (readCR == true) {
1280 // This is CR CR. Send a CR NUL and leave
1281 // the cr flag on.
1282 buf[bufN++] = C_CR;
1283 buf[bufN++] = C_NUL;
1284 continue;
1285 }
1286 // This is the first CR. Set the cr flag.
1287 readCR = true;
1288 continue;
1289 }
1290
1291 if (readCR == true) {
1292 // This was a bare CR in the stream.
1293 buf[bufN++] = C_CR;
1294 readCR = false;
1295 }
1296
1297 // This is a regular character. Pass it on.
1298 buf[bufN++] = b;
1299 continue;
1300 }
1301
1302 /*
1303 * This is the case for any of:
1304 *
1305 * 1) A NVT ASCII character that isn't CR, LF, or
1306 * NUL.
1307 *
1308 * 2) A NVT binary character.
1309 *
1310 * For all of these cases, we just pass the character on.
1311 */
1312 buf[bufN++] = b;
1313
1314 } // if (b == TELNET_IAC)
1315
1316 } // for (int i = 0; i < bufferN; i++)
1317
1318 } while (bufN == 0);
1319
1320 // Return bytes read
1321 return bufN;
005ec497
KL
1322 }
1323
ea91242c
KL
1324
1325}