package dmg.protocols.telnet ;
import javax.security.auth.Subject;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.lang.reflect.InvocationTargetException;
import java.net.InetAddress;
import java.net.Socket;
import dmg.util.DummyStreamEngine;
import org.dcache.auth.UserNamePrincipal;
public class TelnetStreamEngine extends DummyStreamEngine
{
//
// the telnet constants
//
private static final byte telnetSE = (-16);
private static final byte telnetNOP = (-15);
private static final byte telnetDM = (-14);
private static final byte telnetBRK = (-13);
private static final byte telnetIP = (-12);
private static final byte telnetAO = (-11);
private static final byte telnetAYT = (-10);
private static final byte telnetEC = (-9);
private static final byte telnetEL = (-8);
private static final byte telnetGA = (-7);
private static final byte telnetSB = (-6);
private static final byte telnetWILL = (-5);
private static final byte telnetWONT = (-4);
private static final byte telnetDO = (-3);
private static final byte telnetDONT = (-2);
private static final byte telnetIAC = (-1);
private static final byte telnetCR = (0xd);
private static final byte telnetLF = (0xa);
private static final byte telnetNUL = (0);
private static final byte telnetOptionEcho = (1);
private static final byte telnetOptionLine = (3);
private static final int cctData = 1 ;
private static final int cctCR = 2 ;
private static final int cctCR2 = 3 ;
private static final int cctCT1 = 4 ;
private static final int cctCT2 = 5 ;
private static final int cctSUB = 6 ;
private static final int cctESC = 7 ;
public static final byte [] telnetBN = { telnetCR , telnetLF } ;
private final byte[] willEcho = {
telnetIAC, telnetWILL, telnetOptionLine,
telnetIAC, telnetWILL, telnetOptionEcho
};
private final byte[] wontEcho = {
telnetIAC, telnetWONT, telnetOptionLine,
telnetIAC, telnetWONT, telnetOptionEcho
};
//
// class variables
//
private int _engineState ;
private int _controlDataPos ;
private byte [] _controlData ;
boolean _lineMode = true ;
boolean _echoMode = true ;
boolean _passwordMode;
private TelnetServerAuthentication _serverAuth;
private OutputStream _outputStream ;
private InputStream _inputStream ;
private TelnetInputStream2 _telnetInputStream ;
private TelnetOutputStream2 _telnetOutputStream ;
private TelnetInputStreamReader _reader ;
private TelnetOutputStreamWriter _writer ;
public TelnetStreamEngine( Socket socket ,
TelnetServerAuthentication auth )
throws IOException, TelnetAuthenticationException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
super(socket);
_serverAuth = auth ;
_engineState = 0 ;
_controlData = null ;
_controlDataPos = 0 ;
_inputStream = super.getInputStream();
_outputStream = super.getOutputStream();
_telnetInputStream = new TelnetInputStream2( this ) ;
_telnetOutputStream = new TelnetOutputStream2( this ) ;
_writer = new TelnetOutputStreamWriter( _telnetOutputStream ) ;
_reader = new TelnetInputStreamReader( _telnetInputStream , _writer ) ;
if( _serverAuth != null ) {
doAuthentication();
}
}
//
// the stream engine interface
//
@Override
public Reader getReader(){ return _reader ; }
@Override
public Writer getWriter(){ return _writer ; }
@Override
public InputStream getInputStream(){ return _telnetInputStream ; }
@Override
public OutputStream getOutputStream(){ return _telnetOutputStream ; }
//
// the public and package part
//
public void setEcho( boolean echo ) throws IOException {
_outputStream.write( echo ? wontEcho : willEcho ) ;
_outputStream.flush();
}
public void setPasswordMode(boolean p ) throws IOException {
_passwordMode = p ;
setEcho( ! p ) ;
}
void close() throws IOException {
getSocket().close();
}
int read() throws IOException {
Object obj ;
while(true){
if( ( obj = readNext() ) == null ){
return -1 ;
}else if( obj instanceof Byte ){
int b = ((Byte)obj).intValue() ;
b = b == 13 ? '\n' : b ;
if( ( ! _echoMode ) && ( ! _passwordMode ) ) {
write(b);
}
return b ;
}else if( obj instanceof byte [] ){
_handleControl( (byte [])obj ) ;
}
}
}
void write( int c ) throws IOException {
if( c == '\n' ){
_outputStream.write( 0xd ) ;
_outputStream.write( 0xa ) ;
}else {
_outputStream.write(c);
}
}
//
// now the private parts
//
private void doAuthentication()
throws TelnetAuthenticationException,
IOException {
InetAddress host = getInetAddress();
if( _serverAuth.isHostOk( host ) ) {
return;
}
setEcho( true ) ;
_writer.write( "\n User : " ) ;
_writer.flush() ;
BufferedReader r = new BufferedReader( _reader ) ;
String user = r.readLine() ;
if( _serverAuth.isUserOk( host , user ) ){
UserNamePrincipal principal = new UserNamePrincipal(user);
Subject subject = new Subject();
subject.getPrincipals().add(principal);
setSubject(subject);
return ;
}
setPasswordMode( true ) ;
_writer.write( " Password : " ) ;
_writer.flush() ;
String password = r.readLine() ;
if( _serverAuth.isPasswordOk( host , user , password ) ){
setPasswordMode( false ) ;
UserNamePrincipal principal = new UserNamePrincipal(user);
Subject subject = new Subject();
subject.getPrincipals().add(principal);
setSubject(subject);
_writer.write("\n\n") ;
_writer.flush();
return ;
}
_writer.write( "\n\n !!! Access Denied !!! \n" ) ;
_writer.flush() ;
close();
throw new
TelnetAuthenticationException(
"Not authenticated (host=" + host + ";user=" + user + ')') ;
}
private void _handleControl( byte [] cntr )throws IOException{
// System.out.print("Control arrived : ");
// for( int i = 0 ; i < cntr.length ; i++ )
// System.out.print( ""+cntr[i]+" " ) ;
// System.out.println("");
if( cntr.length == 3 ){
switch( cntr[1] ){
case telnetDONT :
switch( cntr[2] ) {
case telnetOptionLine : _lineMode = true ; break ;
case telnetOptionEcho : _echoMode = true ; break ;
}
_writer.flush() ;
cntr[1] = telnetWONT ;
_outputStream.write( cntr ) ;
_outputStream.flush() ;
break ;
case telnetDO :
switch( cntr[2] ) {
case telnetOptionLine : _lineMode = false ; break ;
case telnetOptionEcho : _echoMode = false ; break ;
}
_writer.flush() ;
cntr[1] = telnetWILL ;
_outputStream.write( cntr ) ;
_outputStream.flush() ;
break ;
}
}
}
private void _engineControlAdd( byte c ){
//
if( _controlDataPos >= _controlData.length ){
//
// somethin' wrong with telnet engine
//
_controlDataPos = 0 ;
}
_controlData[_controlDataPos++] = c ;
}
private void _engineControlClear(){
if( _controlData == null ) {
_controlData = new byte[32];
}
_controlDataPos = 0 ;
}
private byte [] _engineControlGet(){
if( _controlDataPos == 0 ) {
return null;
}
byte [] rc = new byte[ _controlDataPos ] ;
System.arraycopy( _controlData , 0 , rc , 0 , _controlDataPos ) ;
_engineControlClear() ;
return rc ;
}
private Object readNext() throws IOException {
int rc ;
Object obj ;
while( true ){
if( ( rc = _inputStream.read() ) < 0 ) {
return null;
}
if( ( obj = _next( (byte)rc ) ) != null ) {
return obj;
}
}
}
private Object _next( byte c ){
if( _engineState == 0 ){
_engineControlClear() ;
_engineState = cctData ;
}
switch( _engineState ){
case cctData :
if( c == telnetIAC ){
_engineState = cctCT1 ;
}else if( c == telnetCR ){
_engineState = cctCR ;
}else{
return c;
}
break ;
case cctCT1 :
if( c == telnetIAC ){
_engineState = cctData ;
return c;
}else if( c == telnetSB ){
_engineState = cctSUB ;
_engineControlAdd( telnetIAC ) ;
_engineControlAdd( c ) ;
}else{
_engineState = cctCT2 ;
_engineControlAdd( telnetIAC ) ;
_engineControlAdd( c ) ;
}
break ;
case cctCT2 :
_engineState = cctData ;
_engineControlAdd( c ) ;
return _engineControlGet() ;
case cctSUB :
if( c == telnetIAC ){
_engineState = cctESC ;
}else{
_engineControlAdd( c ) ;
}
break ;
case cctESC :
if( c == telnetSE ){
_engineState = cctData ;
_engineControlAdd( telnetIAC ) ;
_engineControlAdd( c ) ;
return _engineControlGet() ;
}else{
_engineState = cctSUB ;
_engineControlAdd( telnetIAC ) ;
_engineControlAdd( c ) ;
}
break ;
case cctCR :
_engineState = cctCR2 ;
return (byte) '\n';
case cctCR2 :
if( c == telnetIAC ){
_engineState = cctCT1 ;
}else if( c == telnetCR ){
_engineState = cctCR ;
}else{
_engineState = cctData ;
return c;
}
break ;
}
return null ;
}
public void flush() throws IOException
{
_outputStream.flush();
}
}