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