1 package de.mud.telnet.socket;
2 /* "In case you would like to use the packages as libraries please
3 * apply the GNU Library General Public License as documented in the
4 * file COPYING.LIB." (from Telnet/Documentation/index.html)
8 * socket.TelnetIO - a telnet implementation
10 * $Id: TelnetIO.java,v 1.10 1998/02/09 10:22:18 leo Exp $
11 * $timestamp: Tue May 27 13:27:05 1997 by Matthias L. Jugel :$
13 * This file is part of "The Java Telnet Applet".
15 * This is free software; you can redistribute it and/or modify
16 * it under the terms of the GNU General Public License as published by
17 * the Free Software Foundation; either version 2, or (at your option)
20 * "The Java Telnet Applet" is distributed in the hope that it will be
21 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
25 * You should have received a copy of the GNU General Public License
26 * along with this software; see the file COPYING. If not, write to the
27 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
28 * Boston, MA 02111-1307, USA.
31 import java.net.Socket;
32 import java.io.BufferedInputStream;
33 import java.io.BufferedOutputStream;
34 import java.io.IOException;
35 import java.awt.Dimension;
36 import java.util.Vector;
39 * Implements simple telnet io
41 * @version $Id: TelnetIO.java,v 1.10 1998/02/09 10:22:18 leo Exp $
42 * @author Matthias L. Jugel, Marcus Meißner
43 * @version 1.2 3/7/97 George Ruban added available() because it was needed.
45 public class TelnetIO implements StatusPeer
48 * Return the version of TelnetIO.
50 public String toString() { return "$Id: TelnetIO.java,v 1.10 1998/02/09 10:22:18 leo Exp $"; }
53 * Debug level. This results in additional diagnostic messages on the
56 private static int debug = 0;
59 * State variable for telnetnegotiation reader
61 private byte neg_state = 0;
64 * constants for the negotiation state
66 private final static byte STATE_DATA = 0;
67 private final static byte STATE_IAC = 1;
68 private final static byte STATE_IACSB = 2;
69 private final static byte STATE_IACWILL = 3;
70 private final static byte STATE_IACDO = 4;
71 private final static byte STATE_IACWONT = 5;
72 private final static byte STATE_IACDONT = 6;
73 private final static byte STATE_IACSBIAC = 7;
74 private final static byte STATE_IACSBDATA = 8;
75 private final static byte STATE_IACSBDATAIAC = 9;
78 * What IAC SB <xx> we are handling right now
80 private byte current_sb;
83 * IAC - init sequence for telnet negotiation.
85 private final static byte IAC = (byte)255;
89 private final static byte EOR = (byte)239;
93 private final static byte WILL = (byte)251;
97 private final static byte WONT = (byte)252;
101 private final static byte DO = (byte)253;
105 private final static byte DONT = (byte)254;
109 private final static byte SB = (byte)250;
113 private final static byte SE = (byte)240;
115 * Telnet option: echo text
117 private final static byte TELOPT_ECHO = (byte)1; /* echo on/off */
119 * Telnet option: End Of Record
121 private final static byte TELOPT_EOR = (byte)25; /* end of record */
123 * Telnet option: Negotiate About Window Size
125 private final static byte TELOPT_NAWS = (byte)31; /* NA-WindowSize*/
127 * Telnet option: Terminal Type
129 private final static byte TELOPT_TTYPE = (byte)24; /* terminal type */
131 private final static byte[] IACWILL = { IAC, WILL };
132 private final static byte[] IACWONT = { IAC, WONT };
133 private final static byte[] IACDO = { IAC, DO };
134 private final static byte[] IACDONT = { IAC, DONT };
135 private final static byte[] IACSB = { IAC, SB };
136 private final static byte[] IACSE = { IAC, SE };
139 * Telnet option qualifier 'IS'
141 private final static byte TELQUAL_IS = (byte)0;
144 * Telnet option qualifier 'SEND'
146 private final static byte TELQUAL_SEND = (byte)1;
149 * What IAC DO(NT) request do we have received already ?
151 private byte[] receivedDX;
154 * What IAC WILL/WONT request do we have received already ?
156 private byte[] receivedWX;
158 * What IAC DO/DONT request do we have sent already ?
160 private byte[] sentDX;
162 * What IAC WILL/WONT request do we have sent already ?
164 private byte[] sentWX;
166 private Socket socket;
167 private BufferedInputStream is;
168 private BufferedOutputStream os;
170 private StatusPeer peer = this; /* peer, notified on status */
173 * Connect to the remote host at the specified port.
174 * @param address the symbolic host address
175 * @param port the numeric port
178 public void connect(String address, int port) throws IOException {
179 if(debug > 0) System.out.println("Telnet.connect("+address+","+port+")");
180 socket = new Socket(address, port);
181 is = new BufferedInputStream(socket.getInputStream());
182 os = new BufferedOutputStream(socket.getOutputStream());
184 receivedDX = new byte[256];
185 sentDX = new byte[256];
186 receivedWX = new byte[256];
187 sentWX = new byte[256];
191 * Disconnect from remote host.
194 public void disconnect() throws IOException {
195 if(debug > 0) System.out.println("TelnetIO.disconnect()");
196 if(socket !=null) socket.close();
200 * Connect to the remote host at the default telnet port (23).
201 * @param address the symbolic host address
203 public void connect(String address) throws IOException {
204 connect(address, 23);
208 * Set the object to be notified about current status.
209 * @param obj object to be notified.
211 public void setPeer(StatusPeer obj) { peer = obj; }
213 /** Returns bytes available to be read. Since they haven't been
214 * negotiated over, this could be misleading.
215 * Most useful as a boolean value - "are any bytes available" -
216 * rather than as an exact count of "how many ara available."
218 * @exception IOException on problems with the socket connection
220 public int available() throws IOException
222 return is.available();
227 * Read data from the remote host. Blocks until data is available.
228 * Returns an array of bytes.
231 public byte[] receive() throws IOException {
232 int count = is.available();
233 byte buf[] = new byte[count];
234 count = is.read(buf);
235 if(count < 0) throw new IOException("Connection closed.");
236 if(debug > 1) System.out.println("TelnetIO.receive(): read bytes: "+count);
237 buf = negotiate(buf, count);
242 * Send data to the remote host.
243 * @param buf array of bytes to send
246 public void send(byte[] buf) throws IOException {
247 if(debug > 1) System.out.println("TelnetIO.send("+buf+")");
252 public void send(byte b) throws IOException {
253 if(debug > 1) System.out.println("TelnetIO.send("+b+")");
259 * Handle an incoming IAC SB <type> <bytes> IAC SE
260 * @param type type of SB
261 * @param sbata byte array as <bytes>
262 * @param sbcount nr of bytes. may be 0 too.
264 private void handle_sb(byte type, byte[] sbdata, int sbcount)
268 System.out.println("TelnetIO.handle_sb("+type+")");
271 if (sbcount>0 && sbdata[0]==TELQUAL_SEND) {
273 send(IACSB);send(TELOPT_TTYPE);send(TELQUAL_IS);
274 /* FIXME: need more logic here if we use
275 * more than one terminal type
277 Vector vec = new Vector(2);
278 vec.addElement("TTYPE");
279 ttype = (String)peer.notifyStatus(vec);
280 if(ttype == null) ttype = "dumb";
281 byte[] bttype = new byte[ttype.length()];
283 ttype.getBytes(0,ttype.length(), bttype, 0);
292 * Notify about current telnet status. This method is called top-down.
293 * @param status contains status information
295 public Object notifyStatus(Vector status) {
297 System.out.println("TelnetIO.notifyStatus("+status+")");
301 /* wo faengt buf an bei buf[0] oder bei buf[1] */
302 private byte[] negotiate(byte buf[], int count) throws IOException {
304 System.out.println("TelnetIO.negotiate("+buf+","+count+")");
305 byte nbuf[] = new byte[count];
306 byte sbbuf[] = new byte[count];
307 byte sendbuf[] = new byte[3];
310 int boffset = 0, noffset = 0;
311 Vector vec = new Vector(2);
313 while(boffset < count) {
315 /* of course, byte is a signed entity (-128 -> 127)
316 * but apparently the SGI Netscape 3.0 doesn't seem
317 * to care and provides happily values up to 255
320 b=(byte)((int)b-256);
324 neg_state = STATE_IAC;
333 System.out.print("IAC ");
334 neg_state = STATE_DATA;
339 System.out.print("WILL ");
340 neg_state = STATE_IACWILL;
344 System.out.print("WONT ");
345 neg_state = STATE_IACWONT;
349 System.out.print("DONT ");
350 neg_state = STATE_IACDONT;
354 System.out.print("DO ");
355 neg_state = STATE_IACDO;
359 System.out.print("EOR ");
360 neg_state = STATE_DATA;
364 System.out.print("SB ");
365 neg_state = STATE_IACSB;
373 neg_state = STATE_DATA;
381 System.out.println("ECHO");
384 vec.addElement("NOLOCALECHO");
385 peer.notifyStatus(vec);
389 System.out.println("EOR");
401 System.out.println("<"+b+", WILL ="+WILL+">");
402 if ( reply != sentDX[b+128] ||
403 WILL != receivedWX[b+128]
409 sentDX[b+128] = reply;
410 receivedWX[b+128] = WILL;
412 neg_state = STATE_DATA;
418 System.out.println("ECHO");
421 vec.addElement("LOCALECHO");
422 peer.notifyStatus(vec);
427 System.out.println("EOR");
438 if ( reply != sentDX[b+128] ||
439 WONT != receivedWX[b+128]
445 sentDX[b+128] = reply;
446 receivedWX[b+128] = WILL;
448 neg_state = STATE_DATA;
454 System.out.println("ECHO");
457 vec.addElement("LOCALECHO");
458 peer.notifyStatus(vec);
462 System.out.println("TTYPE");
467 System.out.println("NAWS");
469 vec.addElement("NAWS");
470 Dimension size = (Dimension)
471 peer.notifyStatus(vec);
475 /* this shouldn't happen */
487 sendbuf[2]=TELOPT_NAWS;
489 send(IAC);send(SB);send(TELOPT_NAWS);
490 send((byte) (size.width >> 8));
491 send((byte) (size.width & 0xff));
492 send((byte) (size.height >> 8));
493 send((byte) (size.height & 0xff));
504 if ( reply != sentWX[128+b] ||
505 DO != receivedDX[128+b]
511 sentWX[b+128] = reply;
512 receivedDX[b+128] = DO;
514 neg_state = STATE_DATA;
520 System.out.println("ECHO");
523 vec.addElement("NOLOCALECHO");
524 peer.notifyStatus(vec);
528 System.out.println("NAWS");
539 if ( reply != sentWX[b+128] ||
540 DONT != receivedDX[b+128]
542 send(IAC);send(reply);send(b);
543 sentWX[b+128] = reply;
544 receivedDX[b+128] = DONT;
546 neg_state = STATE_DATA;
549 if(debug > 2) System.out.println(""+b+" ");
553 neg_state = STATE_IACSBDATA;
555 System.out.println("(bad) "+b+" ");
556 neg_state = STATE_DATA;
560 if(debug > 2) System.out.println(""+b+" ");
563 neg_state = STATE_IACSBIAC;
568 neg_state = STATE_IACSBDATA;
572 case STATE_IACSBDATA:
573 if (debug > 2) System.out.println(""+b+" ");
576 neg_state = STATE_IACSBDATAIAC;
579 sbbuf[sbcount++] = b;
583 case STATE_IACSBDATAIAC:
584 if (debug > 2) System.out.println(""+b+" ");
587 neg_state = STATE_IACSBDATA;
588 sbbuf[sbcount++] = IAC;
591 handle_sb(current_sb,sbbuf,sbcount);
593 neg_state = STATE_DATA;
596 handle_sb(current_sb,sbbuf,sbcount);
597 neg_state = STATE_IACSB;
600 neg_state = STATE_DATA;
607 "This should not happen: "+
610 neg_state = STATE_DATA;
614 buf = new byte[noffset];
615 System.arraycopy(nbuf, 0, buf, 0, noffset);