From 534184380cd7762bc1be3c5174af001e33f9b08d Mon Sep 17 00:00:00 2001 From: Kenny Root Date: Mon, 15 Jun 2009 16:35:59 +0000 Subject: Add multiple transports git-svn-id: https://connectbot.googlecode.com/svn/trunk/connectbot@298 df292f66-193f-0410-a5fc-6d59da041ff2 --- src/de/mud/telnet/TelnetProtocolHandler.java | 648 +++++++++++++++++++++++++++ 1 file changed, 648 insertions(+) create mode 100644 src/de/mud/telnet/TelnetProtocolHandler.java (limited to 'src/de') diff --git a/src/de/mud/telnet/TelnetProtocolHandler.java b/src/de/mud/telnet/TelnetProtocolHandler.java new file mode 100644 index 0000000..3d24d12 --- /dev/null +++ b/src/de/mud/telnet/TelnetProtocolHandler.java @@ -0,0 +1,648 @@ +/* + * This file is part of "JTA - Telnet/SSH for the JAVA(tm) platform". + * + * (c) Matthias L. Jugel, Marcus Meißner 1996-2005. All Rights Reserved. + * + * Please visit http://javatelnet.org/ for updates and contact. + * + * --LICENSE NOTICE-- + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * --LICENSE NOTICE-- + * + */ + +package de.mud.telnet; + +import java.io.IOException; +/** + * This is a telnet protocol handler. The handler needs implementations + * for several methods to handle the telnet options and to be able to + * read and write the buffer. + *

+ * Maintainer: Marcus Meissner + * + * @version $Id: TelnetProtocolHandler.java 503 2005-10-24 07:34:13Z marcus $ + * @author Matthias L. Jugel, Marcus Meissner + */ +public abstract class TelnetProtocolHandler { + /** contains the current revision id */ + public final static String ID = "$Id: TelnetProtocolHandler.java 503 2005-10-24 07:34:13Z marcus $"; + + /** debug level */ + private final static int debug = 0; + + /** temporary buffer for data-telnetstuff-data transformation */ + private byte[] tempbuf = new byte[0]; + + /** the data sent on pressing \n */ + private byte[] crlf = new byte[2]; + /** the data sent on pressing \r */ + private byte[] cr = new byte[2]; + + /** + * Create a new telnet protocol handler. + */ + public TelnetProtocolHandler() { + reset(); + + crlf[0] = 13; crlf[1] = 10; + cr[0] = 13; cr[1] = 0; + } + + /** + * Get the current terminal type for TTYPE telnet option. + * @return the string id of the terminal + */ + protected abstract String getTerminalType(); + + /** + * Get the current window size of the terminal for the + * NAWS telnet option. + * @return the size of the terminal as Dimension + */ + protected abstract int[] getWindowSize(); + + /** + * Set the local echo option of telnet. + * @param echo true for local echo, false for no local echo + */ + protected abstract void setLocalEcho(boolean echo); + + /** + * Generate an EOR (end of record) request. For use by prompt displaying. + */ + protected abstract void notifyEndOfRecord(); + + /** + * Send data to the remote host. + * @param b array of bytes to send + */ + protected abstract void write(byte[] b) throws IOException; + + /** + * Send one byte to the remote host. + * @param b the byte to be sent + * @see #write(byte[] b) + */ + private static byte[] one = new byte[1]; + private void write(byte b) throws IOException { + one[0] = b; + write(one); + } + + /** + * Reset the protocol handler. This may be necessary after the + * connection was closed or some other problem occured. + */ + public void reset() { + neg_state = 0; + receivedDX = new byte[256]; + sentDX = new byte[256]; + receivedWX = new byte[256]; + sentWX = new byte[256]; + } + + // =================================================================== + // the actual negotiation handling for the telnet protocol follows: + // =================================================================== + + /** state variable for telnet negotiation reader */ + private byte neg_state = 0; + + /** constants for the negotiation state */ + private final static byte STATE_DATA = 0; + private final static byte STATE_IAC = 1; + private final static byte STATE_IACSB = 2; + private final static byte STATE_IACWILL = 3; + private final static byte STATE_IACDO = 4; + private final static byte STATE_IACWONT = 5; + private final static byte STATE_IACDONT = 6; + private final static byte STATE_IACSBIAC = 7; + private final static byte STATE_IACSBDATA = 8; + private final static byte STATE_IACSBDATAIAC = 9; + + /** What IAC SB we are handling right now */ + private byte current_sb; + + /** current SB negotiation buffer */ + private byte[] sbbuf; + + /** IAC - init sequence for telnet negotiation. */ + private final static byte IAC = (byte)255; + /** [IAC] End Of Record */ + private final static byte EOR = (byte)239; + /** [IAC] WILL */ + private final static byte WILL = (byte)251; + /** [IAC] WONT */ + private final static byte WONT = (byte)252; + /** [IAC] DO */ + private final static byte DO = (byte)253; + /** [IAC] DONT */ + private final static byte DONT = (byte)254; + /** [IAC] Sub Begin */ + private final static byte SB = (byte)250; + /** [IAC] Sub End */ + private final static byte SE = (byte)240; + /** Telnet option: binary mode */ + private final static byte TELOPT_BINARY= (byte)0; /* binary mode */ + /** Telnet option: echo text */ + private final static byte TELOPT_ECHO = (byte)1; /* echo on/off */ + /** Telnet option: sga */ + private final static byte TELOPT_SGA = (byte)3; /* supress go ahead */ + /** Telnet option: End Of Record */ + private final static byte TELOPT_EOR = (byte)25; /* end of record */ + /** Telnet option: Negotiate About Window Size */ + private final static byte TELOPT_NAWS = (byte)31; /* NA-WindowSize*/ + /** Telnet option: Terminal Type */ + private final static byte TELOPT_TTYPE = (byte)24; /* terminal type */ + + private final static byte[] IACWILL = { IAC, WILL }; + private final static byte[] IACWONT = { IAC, WONT }; + private final static byte[] IACDO = { IAC, DO }; + private final static byte[] IACDONT = { IAC, DONT }; + private final static byte[] IACSB = { IAC, SB }; + private final static byte[] IACSE = { IAC, SE }; + + /** Telnet option qualifier 'IS' */ + private final static byte TELQUAL_IS = (byte)0; + /** Telnet option qualifier 'SEND' */ + private final static byte TELQUAL_SEND = (byte)1; + + /** What IAC DO(NT) request do we have received already ? */ + private byte[] receivedDX; + /** What IAC WILL/WONT request do we have received already ? */ + private byte[] receivedWX; + /** What IAC DO/DONT request do we have sent already ? */ + private byte[] sentDX; + /** What IAC WILL/WONT request do we have sent already ? */ + private byte[] sentWX; + + /** + * Send a Telnet Escape character (IAC ) + */ + public void sendTelnetControl(byte code) + throws IOException { + byte[] b = new byte[2]; + + b[0] = IAC; + b[1] = code; + write(b); + } + + /** + * Send the new Window Size (via NAWS) + */ + public void setWindowSize(int columns,int rows) + throws IOException { + if(debug > 2) System.err.println("sending NAWS"); + + if (receivedDX[TELOPT_NAWS] != DO) { + System.err.println("not allowed to send NAWS? (DONT NAWS)"); + return; + } + write(IAC);write(SB);write(TELOPT_NAWS); + write((byte) (columns >> 8)); + write((byte) (columns & 0xff)); + write((byte) (rows >> 8)); + write((byte) (rows & 0xff)); + write(IAC);write(SE); + } + + + /** + * Handle an incoming IAC SB <type> <bytes> IAC SE + * @param type type of SB + * @param sbata byte array as <bytes> + */ + private void handle_sb(byte type, byte[] sbdata) + throws IOException { + if(debug > 1) + System.err.println("TelnetIO.handle_sb("+type+")"); + switch (type) { + case TELOPT_TTYPE: + if (sbdata.length>0 && sbdata[0]==TELQUAL_SEND) { + write(IACSB);write(TELOPT_TTYPE);write(TELQUAL_IS); + /* FIXME: need more logic here if we use + * more than one terminal type + */ + String ttype = getTerminalType(); + if(ttype == null) ttype = "dumb"; + write(ttype.getBytes()); + write(IACSE); + } + + } + } + + /** + * Do not send any notifications at startup. We do not know, + * whether the remote client understands telnet protocol handling, + * so we are silent. + * (This used to send IAC WILL SGA, but this is false for a compliant + * client.) + */ + public void startup() throws IOException { + } + /** + * Transpose special telnet codes like 0xff or newlines to values + * that are compliant to the protocol. This method will also send + * the buffer immediately after transposing the data. + * @param buf the data buffer to be sent + */ + public void transpose(byte[] buf) throws IOException { + int i; + + byte[] nbuf,xbuf; + int nbufptr=0; + nbuf = new byte[buf.length*2]; // FIXME: buffer overflows possible + + for (i = 0; i < buf.length ; i++) { + switch (buf[i]) { + // Escape IAC twice in stream ... to be telnet protocol compliant + // this is there in binary and non-binary mode. + case IAC: + nbuf[nbufptr++]=IAC; + nbuf[nbufptr++]=IAC; + break; + // We need to heed RFC 854. LF (\n) is 10, CR (\r) is 13 + // we assume that the Terminal sends \n for lf+cr and \r for just cr + // linefeed+carriage return is CR LF */ + case 10: // \n + if (receivedDX[TELOPT_BINARY + 128 ] != DO) { + while (nbuf.length - nbufptr < crlf.length) { + xbuf = new byte[nbuf.length*2]; + System.arraycopy(nbuf,0,xbuf,0,nbufptr); + nbuf = xbuf; + } + for (int j=0;jRFC-Telnet + * @param nbuf the byte buffer put out after negotiation + * @return number of bytes processed, 0 for none, and -1 for end of buffer. + */ + public int negotiate(byte nbuf[], int offset) + throws IOException + { + int count = tempbuf.length; + byte[] buf = tempbuf; + byte sendbuf[] = new byte[3]; + byte b,reply; + int boffset = 0, noffset = offset; + boolean dobreak = false; + + if (count == 0) // buffer is empty. + return -1; + + while(!dobreak && (boffset < count) && (noffset < nbuf.length)) { + b=buf[boffset++]; + // of course, byte is a signed entity (-128 -> 127) + // but apparently the SGI Netscape 3.0 doesn't seem + // to care and provides happily values up to 255 + if (b>=128) + b=(byte)(b-256); + if(debug > 2) { + Byte B = new Byte(b); + System.err.print("byte: " + B.intValue()+ " "); + } + switch (neg_state) { + case STATE_DATA: + if (b==IAC) { + neg_state = STATE_IAC; + dobreak = true; // leave the loop so we can sync. + } else + nbuf[noffset++]=b; + break; + case STATE_IAC: + switch (b) { + case IAC: + if(debug > 2) System.err.print("IAC "); + neg_state = STATE_DATA; + nbuf[noffset++]=IAC; + break; + case WILL: + if(debug > 2) System.err.print("WILL "); + neg_state = STATE_IACWILL; + break; + case WONT: + if(debug > 2) System.err.print("WONT "); + neg_state = STATE_IACWONT; + break; + case DONT: + if(debug > 2) System.err.print("DONT "); + neg_state = STATE_IACDONT; + break; + case DO: + if(debug > 2) System.err.print("DO "); + neg_state = STATE_IACDO; + break; + case EOR: + if(debug > 1) System.err.print("EOR "); + notifyEndOfRecord(); + dobreak = true; // leave the loop so we can sync. + neg_state = STATE_DATA; + break; + case SB: + if(debug > 2) System.err.print("SB "); + neg_state = STATE_IACSB; + break; + default: + if(debug > 2) System.err.print(" "); + neg_state = STATE_DATA; + break; + } + break; + case STATE_IACWILL: + switch(b) { + case TELOPT_ECHO: + if(debug > 2) System.err.println("ECHO"); + reply = DO; + setLocalEcho(false); + break; + case TELOPT_SGA: + if(debug > 2) System.err.println("SGA"); + reply = DO; + break; + case TELOPT_EOR: + if(debug > 2) System.err.println("EOR"); + reply = DO; + break; + case TELOPT_BINARY: + if(debug > 2) System.err.println("BINARY"); + reply = DO; + break; + default: + if(debug > 2) System.err.println(""); + reply = DONT; + break; + } + if(debug > 1) System.err.println("<"+b+", WILL ="+WILL+">"); + if (reply != sentDX[b+128] || WILL != receivedWX[b+128]) { + sendbuf[0]=IAC; + sendbuf[1]=reply; + sendbuf[2]=b; + write(sendbuf); + sentDX[b+128] = reply; + receivedWX[b+128] = WILL; + } + neg_state = STATE_DATA; + break; + case STATE_IACWONT: + switch(b) { + case TELOPT_ECHO: + if(debug > 2) System.err.println("ECHO"); + setLocalEcho(true); + reply = DONT; + break; + case TELOPT_SGA: + if(debug > 2) System.err.println("SGA"); + reply = DONT; + break; + case TELOPT_EOR: + if(debug > 2) System.err.println("EOR"); + reply = DONT; + break; + case TELOPT_BINARY: + if(debug > 2) System.err.println("BINARY"); + reply = DONT; + break; + default: + if(debug > 2) System.err.println(""); + reply = DONT; + break; + } + if(reply != sentDX[b+128] || WONT != receivedWX[b+128]) { + sendbuf[0]=IAC; + sendbuf[1]=reply; + sendbuf[2]=b; + write(sendbuf); + sentDX[b+128] = reply; + receivedWX[b+128] = WILL; + } + neg_state = STATE_DATA; + break; + case STATE_IACDO: + switch (b) { + case TELOPT_ECHO: + if(debug > 2) System.err.println("ECHO"); + reply = WILL; + setLocalEcho(true); + break; + case TELOPT_SGA: + if(debug > 2) System.err.println("SGA"); + reply = WILL; + break; + case TELOPT_TTYPE: + if(debug > 2) System.err.println("TTYPE"); + reply = WILL; + break; + case TELOPT_BINARY: + if(debug > 2) System.err.println("BINARY"); + reply = WILL; + break; + case TELOPT_NAWS: + if(debug > 2) System.err.println("NAWS"); + int[] size = getWindowSize(); + receivedDX[b] = DO; + if(size == null) { + // this shouldn't happen + write(IAC); + write(WONT); + write(TELOPT_NAWS); + reply = WONT; + sentWX[b] = WONT; + break; + } + reply = WILL; + sentWX[b] = WILL; + sendbuf[0]=IAC; + sendbuf[1]=WILL; + sendbuf[2]=TELOPT_NAWS; + write(sendbuf); + write(IAC);write(SB);write(TELOPT_NAWS); + write((byte) (size[0] >> 8)); + write((byte) (size[0] & 0xff)); + write((byte) (size[1] >> 8)); + write((byte) (size[1] & 0xff)); + write(IAC);write(SE); + break; + default: + if(debug > 2) System.err.println(""); + reply = WONT; + break; + } + if(reply != sentWX[128+b] || DO != receivedDX[128+b]) { + sendbuf[0]=IAC; + sendbuf[1]=reply; + sendbuf[2]=b; + write(sendbuf); + sentWX[b+128] = reply; + receivedDX[b+128] = DO; + } + neg_state = STATE_DATA; + break; + case STATE_IACDONT: + switch (b) { + case TELOPT_ECHO: + if(debug > 2) System.err.println("ECHO"); + reply = WONT; + setLocalEcho(false); + break; + case TELOPT_SGA: + if(debug > 2) System.err.println("SGA"); + reply = WONT; + break; + case TELOPT_NAWS: + if(debug > 2) System.err.println("NAWS"); + reply = WONT; + break; + case TELOPT_BINARY: + if(debug > 2) System.err.println("BINARY"); + reply = WONT; + break; + default: + if(debug > 2) System.err.println(""); + reply = WONT; + break; + } + if(reply != sentWX[b+128] || DONT != receivedDX[b+128]) { + write(IAC);write(reply);write(b); + sentWX[b+128] = reply; + receivedDX[b+128] = DONT; + } + neg_state = STATE_DATA; + break; + case STATE_IACSBIAC: + if(debug > 2) System.err.println(""+b+" "); + if (b == IAC) { + sbbuf = new byte[0]; + current_sb = b; + neg_state = STATE_IACSBDATA; + } else { + System.err.println("(bad) "+b+" "); + neg_state = STATE_DATA; + } + break; + case STATE_IACSB: + if(debug > 2) System.err.println(""+b+" "); + switch (b) { + case IAC: + neg_state = STATE_IACSBIAC; + break; + default: + current_sb = b; + sbbuf = new byte[0]; + neg_state = STATE_IACSBDATA; + break; + } + break; + case STATE_IACSBDATA: + if (debug > 2) System.err.println(""+b+" "); + switch (b) { + case IAC: + neg_state = STATE_IACSBDATAIAC; + break; + default: + byte[] xsb = new byte[sbbuf.length+1]; + System.arraycopy(sbbuf,0,xsb,0,sbbuf.length); + sbbuf = xsb; + sbbuf[sbbuf.length-1] = b; + break; + } + break; + case STATE_IACSBDATAIAC: + if (debug > 2) System.err.println(""+b+" "); + switch (b) { + case IAC: + neg_state = STATE_IACSBDATA; + byte[] xsb = new byte[sbbuf.length+1]; + System.arraycopy(sbbuf,0,xsb,0,sbbuf.length); + sbbuf = xsb; + sbbuf[sbbuf.length-1] = IAC; + break; + case SE: + handle_sb(current_sb,sbbuf); + current_sb = 0; + neg_state = STATE_DATA; + break; + case SB: + handle_sb(current_sb,sbbuf); + neg_state = STATE_IACSB; + break; + default: + neg_state = STATE_DATA; + break; + } + break; + default: + if (debug > 1) + System.err.println("This should not happen: "+neg_state+" "); + neg_state = STATE_DATA; + break; + } + } + // shrink tempbuf to new processed size. + byte[] xb = new byte[count-boffset]; + System.arraycopy(tempbuf,boffset,xb,0,count-boffset); + tempbuf = xb; + return noffset - offset; + } + + public void inputfeed(byte[] b, int offset, int len) { + byte[] xb = new byte[tempbuf.length+offset+len]; + + System.arraycopy(b,0,xb,0,offset); + System.arraycopy(tempbuf,0,xb,offset,tempbuf.length); + System.arraycopy(b,offset,xb,offset+tempbuf.length,len); + tempbuf = xb; + } +} -- cgit v1.2.3