#37 fix signed byte type math
[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 }
0895a25f
KL
929 int width = subnegBuffer.get(i);
930 if (width < 0) {
931 width += 256;
932 }
933 windowWidth = width * 256;
ea91242c
KL
934
935 i++;
9b1afdde 936 if (subnegBuffer.get(i) == (byte)TELNET_IAC) {
ea91242c
KL
937 i++;
938 }
0895a25f
KL
939 width = subnegBuffer.get(i);
940 windowWidth += width;
941 if (width < 0) {
942 windowWidth += 256;
943 }
ea91242c
KL
944
945 i++;
9b1afdde 946 if (subnegBuffer.get(i) == (byte)TELNET_IAC) {
ea91242c
KL
947 i++;
948 }
0895a25f
KL
949 int height = subnegBuffer.get(i);
950 if (height < 0) {
951 height += 256;
952 }
953 windowHeight = height * 256;
ea91242c
KL
954
955 i++;
9b1afdde 956 if (subnegBuffer.get(i) == (byte)TELNET_IAC) {
ea91242c
KL
957 i++;
958 }
0895a25f
KL
959 height = subnegBuffer.get(i);
960 windowHeight += height;
961 if (height < 0) {
962 windowHeight += 256;
963 }
ea91242c
KL
964 }
965 break;
966
967 case 39:
968 // Environment
969 handleNewEnvironment();
970 break;
971
972 default:
973 // Ignore this one
974 break;
975 }
976 }
977
005ec497
KL
978 /**
979 * Reads up to len bytes of data from the input stream into an array of
980 * bytes.
9b1afdde
KL
981 *
982 * @param buf the buffer into which the data is read.
983 * @param off the start offset in array b at which the data is written.
984 * @param len the maximum number of bytes to read.
985 * @return the total number of bytes read into the buffer, or -1 if there
986 * is no more data because the end of the stream has been reached.
987 * @throws IOException if an I/O error occurs
005ec497 988 */
9b1afdde
KL
989 private int readImpl(final byte[] buf, final int off,
990 final int len) throws IOException {
991
005ec497 992 assert (len > 0);
9b1afdde
KL
993
994 // The current writing position in buf.
995 int bufN = 0;
996
997 // We will keep trying to read() until we have something to return.
998 do {
999
1000 // Read up to len bytes
1001 byte [] buffer = new byte[len];
1002 int bufferN = 0;
1003
1004 // Read some data from the other end
1005 int rc = input.read(buffer);
1006
1007 // Check for EOF or error
1008 if (rc > 0) {
1009 // More data came in
1010 bufferN = rc;
1011 } else {
1012 // EOF, just return it.
1013 return rc;
1014 }
1015
1016 // Loop through the read bytes
1017 for (int i = 0; i < bufferN; i++) {
1018 byte b = buffer[i];
1019
1020 if (subnegEnd == true) {
1021 // Looking for IAC SE to end this subnegotiation
1022 if (b == (byte)TELNET_SE) {
1023 if (iac == true) {
1024 iac = false;
1025 subnegEnd = false;
1026 handleSubneg();
1027 }
1028 } else if (b == (byte)TELNET_IAC) {
1029 if (iac == true) {
1030 // An argument to the subnegotiation option
1031 subnegBuffer.add((byte)TELNET_IAC);
1032 } else {
1033 iac = true;
1034 }
1035 } else {
1036 // An argument to the subnegotiation option
1037 subnegBuffer.add(b);
1038 }
1039 continue;
1040 }
1041
1042 // Look for DO/DON'T/WILL/WON'T option
1043 if (dowill == true) {
1044
1045 // Look for option/
1046 switch (b) {
1047
1048 case 0:
1049 // Binary Transmission
1050 if (dowillType == (byte)TELNET_WILL) {
1051 // Server will use binary transmission, yay.
1052 master.binaryMode = true;
1053 } else if (dowillType == (byte)TELNET_DO) {
1054 // Server asks for binary transmission.
1055 WILL(b);
1056 master.binaryMode = true;
1057 } else if (dowillType == (byte)TELNET_WONT) {
1058 // We're screwed, server won't do binary
1059 // transmission.
1060 master.binaryMode = false;
1061 } else {
1062 // Server demands NVT ASCII mode.
1063 master.binaryMode = false;
1064 }
1065 break;
1066
1067 case 1:
1068 // Echo
1069 if (dowillType == (byte)TELNET_WILL) {
1070 // Server will use echo, yay.
1071 master.echoMode = true;
1072 } else if (dowillType == (byte)TELNET_DO) {
1073 // Server asks for echo.
1074 WILL(b);
1075 master.echoMode = true;
1076 } else if (dowillType == (byte)TELNET_WONT) {
1077 // We're screwed, server won't do echo.
1078 master.echoMode = false;
1079 } else {
1080 // Server demands no echo.
1081 master.echoMode = false;
1082 }
1083 break;
1084
1085 case 3:
1086 // Suppress Go Ahead
1087 if (dowillType == (byte)TELNET_WILL) {
1088 // Server will use suppress go-ahead, yay.
1089 master.goAhead = false;
1090 } else if (dowillType == (byte)TELNET_DO) {
1091 // Server asks for suppress go-ahead.
1092 WILL(b);
1093 master.goAhead = false;
1094 } else if (dowillType == (byte)TELNET_WONT) {
1095 // We're screwed, server won't do suppress
1096 // go-ahead.
1097 master.goAhead = true;
1098 } else {
1099 // Server demands Go-Ahead mode.
1100 master.goAhead = true;
1101 }
1102 break;
1103
1104 case 24:
1105 // Terminal Type - send what's in TERM
1106 if (dowillType == (byte)TELNET_WILL) {
1107 // Server will use terminal type, yay.
1108 if (master.isServer
1109 && master.doTermType
1110 ) {
1111 requestTerminalType();
1112 master.doTermType = false;
1113 } else if (!master.isServer) {
1114 master.doTermType = true;
1115 }
1116 } else if (dowillType == (byte)TELNET_DO) {
1117 // Server asks for terminal type.
1118 WILL(b);
1119 master.doTermType = true;
1120 } else if (dowillType == (byte)TELNET_WONT) {
1121 // We're screwed, server won't do terminal type.
1122 master.doTermType = false;
1123 } else {
1124 // Server will not listen to terminal type.
1125 master.doTermType = false;
1126 }
1127 break;
1128
1129 case 31:
1130 // NAWS
1131 if (dowillType == (byte)TELNET_WILL) {
1132 // Server will use NAWS, yay.
1133 master.doNAWS = true;
1134 // NAWS cannot be requested by the server, it is
1135 // only sent by the client.
1136 } else if (dowillType == (byte)TELNET_DO) {
1137 // Server asks for NAWS.
1138 WILL(b);
1139 master.doNAWS = true;
1140 } else if (dowillType == (byte)TELNET_WONT) {
1141 // Server won't do NAWS.
1142 master.doNAWS = false;
1143 } else {
1144 // Server will not listen to NAWS.
1145 master.doNAWS = false;
1146 }
1147 break;
1148
1149 case 32:
1150 // Terminal Speed
1151 if (dowillType == (byte)TELNET_WILL) {
1152 // Server will use terminal speed, yay.
1153 if (master.isServer
1154 && master.doTermSpeed
1155 ) {
1156 requestTerminalSpeed();
1157 master.doTermSpeed = false;
1158 } else if (!master.isServer) {
1159 master.doTermSpeed = true;
1160 }
1161 } else if (dowillType == (byte)TELNET_DO) {
1162 // Server asks for terminal speed.
1163 WILL(b);
1164 master.doTermSpeed = true;
1165 } else if (dowillType == (byte)TELNET_WONT) {
1166 // We're screwed, server won't do terminal speed.
1167 master.doTermSpeed = false;
1168 } else {
1169 // Server will not listen to terminal speed.
1170 master.doTermSpeed = false;
1171 }
1172 break;
1173
1174 case 39:
1175 // New Environment
1176 if (dowillType == (byte)TELNET_WILL) {
1177 // Server will use NewEnvironment, yay.
1178 if (master.isServer
1179 && master.doEnvironment
1180 ) {
1181 requestEnvironment();
1182 master.doEnvironment = false;
1183 } else if (!master.isServer) {
1184 master.doEnvironment = true;
1185 }
1186 } else if (dowillType == (byte)TELNET_DO) {
1187 // Server asks for NewEnvironment.
1188 WILL(b);
1189 master.doEnvironment = true;
1190 } else if (dowillType == (byte)TELNET_WONT) {
1191 // Server won't do NewEnvironment.
1192 master.doEnvironment = false;
1193 } else {
1194 // Server will not listen to New Environment.
1195 master.doEnvironment = false;
1196 }
1197 break;
1198
1199
1200 default:
1201 // Other side asked for something we don't
1202 // understand. Tell them we will not do this option.
1203 refuse(dowillType, b);
1204 break;
1205 }
1206
1207 dowill = false;
1208 continue;
1209 } // if (dowill == true)
1210
1211 // Perform read processing
1212 if (b == (byte)TELNET_IAC) {
1213
1214 // Telnet command
1215 if (iac == true) {
1216 // IAC IAC -> IAC
1217 buf[bufN++] = (byte)TELNET_IAC;
1218 iac = false;
1219 } else {
1220 iac = true;
1221 }
1222 continue;
1223 } else {
1224 if (iac == true) {
1225
1226 switch (b) {
1227
1228 case (byte)TELNET_SE:
1229 // log.debug1(" END Sub-Negotiation");
1230 break;
1231 case (byte)TELNET_NOP:
1232 // log.debug1(" NOP");
1233 break;
1234 case (byte)TELNET_DM:
1235 // log.debug1(" Data Mark");
1236 break;
1237 case (byte)TELNET_BRK:
1238 // log.debug1(" Break");
1239 break;
1240 case (byte)TELNET_IP:
1241 // log.debug1(" Interrupt Process");
1242 break;
1243 case (byte)TELNET_AO:
1244 // log.debug1(" Abort Output");
1245 break;
1246 case (byte)TELNET_AYT:
1247 // log.debug1(" Are You There?");
1248 break;
1249 case (byte)TELNET_EC:
1250 // log.debug1(" Erase Character");
1251 break;
1252 case (byte)TELNET_EL:
1253 // log.debug1(" Erase Line");
1254 break;
1255 case (byte)TELNET_GA:
1256 // log.debug1(" Go Ahead");
1257 break;
1258 case (byte)TELNET_SB:
1259 // log.debug1(" START Sub-Negotiation");
1260 // From here we wait for the IAC SE
1261 subnegEnd = true;
1262 subnegBuffer.clear();
1263 break;
1264 case (byte)TELNET_WILL:
1265 // log.debug1(" WILL");
1266 dowill = true;
1267 dowillType = b;
1268 break;
1269 case (byte)TELNET_WONT:
1270 // log.debug1(" WON'T");
1271 dowill = true;
1272 dowillType = b;
1273 break;
1274 case (byte)TELNET_DO:
1275 // log.debug1(" DO");
1276 dowill = true;
1277 dowillType = b;
1278
1279 if (master.binaryMode == true) {
1280 // log.debug1("Telnet DO in binary mode");
1281 }
1282
1283 break;
1284 case (byte)TELNET_DONT:
1285 // log.debug1(" DON'T");
1286 dowill = true;
1287 dowillType = b;
1288 break;
1289 default:
1290 // This should be equivalent to IAC NOP
1291 // log.debug1("Will treat as IAC NOP");
1292 break;
1293 }
1294 iac = false;
1295 continue;
1296
1297 } // if (iac == true)
1298
1299 /*
1300 * All of the regular IAC processing is completed at this
1301 * point. Now we need to handle the CR and CR LF cases.
1302 *
1303 * According to RFC 854, in NVT ASCII mode:
1304 * Bare CR -> CR NUL
1305 * CR LF -> CR LF
1306 *
1307 */
1308 if (master.binaryMode == false) {
1309
1310 if (b == C_LF) {
1311 if (readCR == true) {
1312 // This is CR LF. Send CR LF and turn the cr
1313 // flag off.
1314 buf[bufN++] = C_CR;
1315 buf[bufN++] = C_LF;
1316 readCR = false;
1317 continue;
1318 }
1319 // This is bare LF. Send LF.
1320 buf[bufN++] = C_LF;
1321 continue;
1322 }
1323
1324 if (b == C_NUL) {
1325 if (readCR == true) {
1326 // This is CR NUL. Send CR and turn the cr
1327 // flag off.
1328 buf[bufN++] = C_CR;
1329 readCR = false;
1330 continue;
1331 }
1332 // This is bare NUL. Send NUL.
1333 buf[bufN++] = C_NUL;
1334 continue;
1335 }
1336
1337 if (b == C_CR) {
1338 if (readCR == true) {
1339 // This is CR CR. Send a CR NUL and leave
1340 // the cr flag on.
1341 buf[bufN++] = C_CR;
1342 buf[bufN++] = C_NUL;
1343 continue;
1344 }
1345 // This is the first CR. Set the cr flag.
1346 readCR = true;
1347 continue;
1348 }
1349
1350 if (readCR == true) {
1351 // This was a bare CR in the stream.
1352 buf[bufN++] = C_CR;
1353 readCR = false;
1354 }
1355
1356 // This is a regular character. Pass it on.
1357 buf[bufN++] = b;
1358 continue;
1359 }
1360
1361 /*
1362 * This is the case for any of:
1363 *
1364 * 1) A NVT ASCII character that isn't CR, LF, or
1365 * NUL.
1366 *
1367 * 2) A NVT binary character.
1368 *
1369 * For all of these cases, we just pass the character on.
1370 */
1371 buf[bufN++] = b;
1372
1373 } // if (b == TELNET_IAC)
1374
1375 } // for (int i = 0; i < bufferN; i++)
1376
1377 } while (bufN == 0);
1378
1379 // Return bytes read
1380 return bufN;
005ec497
KL
1381 }
1382
ea91242c 1383}