/*
* Copyright 2010 NCHOVY
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.krakenapps.pcap.decoder.telnet;
import java.util.Arrays;
import java.util.Set;
import org.krakenapps.pcap.util.Buffer;
public class TelnetSession {
final byte findCharNormal[] = { (byte) 0xff, 0x0d, 0x1b };
final byte findCharOption[] = { (byte) 0xff };
final int bufSize = 4096;
final int argSize = 16;
public TelnetSession() {
setAnsiState(NORMAL);
setState(NORMAL);
buffer = new byte[bufSize];
args = new int[argSize];
}
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
switch (state) {
case NORMAL:
findChar = findCharNormal;
if (ansiState != NORMAL) {
findLen = 1;
return;
} else {
findLen = bufSize;
}
break;
case COMMAND:
findChar = null;
findLen = 1;
break;
case OPTION:
findChar = null;
findLen = 1;
break;
case SUBOPTION:
findChar = findCharOption;
findLen = bufSize;
return;
}
bufferPos = 0;
}
public int getNextAnsiState() {
byte ch = buffer[bufferPos - 1];
switch (ansiState) {
case ANSI:
ansiMode = ANSI;
if (ch == '[')
return CSI;
else if (ch == ']')
return XTERM;
else if (ch == '#')
return SHARP;
else if (ch == '!')
return EXCL;
dispatchAnsiControl(ch);
break;
case SHARP:
ansiMode = SHARP;
dispatchAnsiControl(ch);
break;
case EXCL:
ansiMode = EXCL;
dispatchAnsiControl(ch);
break;
case CSI:
ansiMode = CSI;
if (ch >= '0' && ch <= '9')
return VALUE;
else if (ch == '?' || ch == '>')
return QUOT;
dispatchAnsiControl(ch);
break;
case QUOT:
ansiMode = QUOT;
if (ch >= '0' && ch <= '9')
return VALUE;
dispatchAnsiControl(ch);
break;
case VALUE:
if (ch >= '0' && ch <= '9')
return VALUE;
else if (ch == ';' || ch == '"') {
if (bufferPos > 1)
args[argPos++] = Integer.parseInt(new String(buffer, 0, bufferPos - 1));
bufferPos = 0;
return VALUE;
} else {
if (bufferPos > 1)
args[argPos++] = Integer.parseInt(new String(buffer, 0, bufferPos - 1));
dispatchAnsiControl(ch);
}
break;
case XTERM:
if (ch == ';') {
bufferPos = 0;
return XTERM;
} else if (ch != 7)
return XTERM;
else if (bufferPos > 1) {
bufferPos--;
dispatchTitle();
}
}
return NORMAL;
}
public void setAnsiState(int ansiState) {
switch (ansiState) {
case ANSI:
argPos = 0;
break;
case XTERM:
case VALUE:
this.ansiState = ansiState;
return;
}
bufferPos = 0;
this.ansiState = ansiState;
}
public byte[] getBuffer() {
return buffer;
}
public int getNextState(boolean hit, int index) {
switch (state) {
case NORMAL:
if (hit) {
if (index == 0)
return COMMAND;
else if (index == 2)
setAnsiState(ANSI);
} else if (ansiState != NORMAL)
setAnsiState(getNextAnsiState());
if (ansiState == NORMAL) {
dispatchData();
}
break;
case COMMAND:
int next = NORMAL;
command = buffer[0];
switch (command) {
case (byte) 250: // SB
next = SUBOPTION;
bufferPos = 0;
break;
case (byte) 251: // WILL
next = OPTION;
break;
case (byte) 252: // WON'T
next = OPTION;
break;
case (byte) 253: // DO
next = OPTION;
break;
case (byte) 254: // DON'T
next = OPTION;
break;
default:
option = -1;
bufferPos = 0;
dispatchCommand();
}
return next;
case OPTION:
option = buffer[0];
bufferPos = 0;
dispatchCommand();
break;
case SUBOPTION:
if (bufferPos == 1)
option = buffer[0];
if (hit && index == 0) {
dispatchCommand();
return COMMAND;
}
return SUBOPTION;
}
return NORMAL;
}
public void handlePacket(byte[] data) {
for (int i = 0; i < data.length; i++) {
if (findChar != null) {
int j;
for (j = 0; j < findChar.length; j++) {
if (data[i] == findChar[j])
break;
}
if (j < findChar.length) {
setState(getNextState(true, j));
continue;
}
}
buffer[bufferPos++] = data[i];
if (bufferPos >= findLen)
setState(getNextState(false, 0));
}
if (ansiState == NORMAL) {
dispatchData();
bufferPos = 0;
}
}
public void handlePacket(Buffer data) {
int readable = data.readableBytes();
for (int i = 0; i < readable; i++) {
byte b = data.get();
if(findChar != null) {
int j;
for (j = 0; j < findChar.length; j++) {
if (b == findChar[j])
break;
}
if (j < findChar.length) {
setState(getNextState(true, j));
continue;
}
}
buffer[bufferPos++] = b;
if (bufferPos >= findLen)
setState(getNextState(false, 0));
}
if (ansiState == NORMAL) {
dispatchData();
bufferPos = 0;
}
}
public void setCallbacks(Set<TelnetProcessor> callbacks) {
this.callbacks = callbacks;
}
private void dispatchTitle() {
String title = new String(buffer, 0, bufferPos);
for (TelnetProcessor processor : callbacks) {
if (isTx)
processor.onClientTitle(title);
else
processor.onServerTitle(title);
}
}
private void dispatchData() {
String data = new String(buffer, 0, bufferPos);
if (bufferPos == 0)
return;
for (TelnetProcessor processor : callbacks) {
if (isTx)
processor.onClientData(data);
else
processor.onServerData(data);
}
}
private void dispatchCommand() {
TelnetCommand telnetCommand = TelnetCommand.parse(command);
TelnetOption telnetOption = TelnetOption.parse(option);
byte[] data = Arrays.copyOfRange(buffer, 0, bufferPos);
for (TelnetProcessor processor : callbacks) {
if (isTx)
processor.onClientCommand(telnetCommand, telnetOption, data);
else
processor.onServerCommand(telnetCommand, telnetOption, data);
}
}
private void dispatchAnsiControl(byte command) {
AnsiMode mode = AnsiMode.parse(ansiMode);
TelnetCommand telnetCommand = TelnetCommand.parse(command);
int[] arguments = Arrays.copyOf(args, argPos);
for (TelnetProcessor processor : callbacks) {
if (isTx)
processor.onClientAnsiControl(mode, telnetCommand, arguments);
else
processor.onServerAnsiControl(mode, telnetCommand, arguments);
}
}
public void setTx(boolean isTx) {
this.isTx = isTx;
}
private int args[];
private int state;
private int ansiState;
private int ansiMode;
private int bufferPos = 0;
private int argPos = 0;
private byte command;
private byte option;
private byte buffer[];
private byte findChar[];
private int findLen;
private Set<TelnetProcessor> callbacks;
private boolean isTx;
static final int NORMAL = 0;
static final int COMMAND = 1;
static final int OPTION = 2;
static final int SUBOPTION = 3;
static final int ANSI = 1;
static final int CSI = 2;
static final int VALUE = 3;
static final int QUOT = 4;
static final int XTERM = 5;
static final int SHARP = 6;
static final int EXCL = 7;
}