package lava.net.mmp; import java.io.InputStream; import java.io.IOException; import java.util.Enumeration; import java.util.Hashtable; import java.util.Vector; import lava.net.common.UNA; import lava.net.common.Value; import lava.net.common.VariableModifier; import lava.net.common.VariableNormalizer; import lava.net.common.ModifierParser; /** * **/ public class MMPInput extends Thread { /** * The maximum length of an header line. * This is to prevent denial of service. * Sizes smaller than 5 are considered infinite. **/ public static int maxLineLength = 2048; /** * The maximum amount of header lines per packet. * This is to prevent denial of service. * Sizes smaller than 5 are considered infinite. **/ public static int maxLineCount = 30; /** * The maximum length of the body per packet. * This is to prevent denial of service. * Sizes smaller than 5 are considered infinite. **/ public static int maxBodyCount = 65535; /** * The maximum time in milliseconds a new connected stream is * allowed to say nothing until it'll get closed. * This is to prevent denial of service. * Numbers smaller than 1 are considered infinite. **/ public static long silenceOnConnectTimeout = 300000; /** * Marker, if a new connected stream said something. **/ private boolean sentSomething = false; /** * Checks after a specified timeout, if we got some data. * If not, it closes the Stream. **/ private class TimeoutGuard extends Thread { /** * **/ public TimeoutGuard() { super(); start(); } /** * **/ public void run() { synchronized(this) { try { //MMPInput.this.join(silenceOnConnectTimeout); wait(silenceOnConnectTimeout); } catch(InterruptedException e) { } } guard = null; if(!sentSomething) forceClose(); } } /** * **/ private TimeoutGuard guard = null; /** * **/ private InputStream in = null; /** * **/ private MMPDispatcherPeer peer = null; /** * **/ private VariableNormalizer normalizer = null; /** * **/ private UNA remote = null; /** * **/ private StringBuffer rawHeader = new StringBuffer(); /** * **/ private StringBuffer body = new StringBuffer(); /** * **/ private int globalLength = -1; /** * **/ private int length = -1; /** * **/ private boolean emptyPacket = true; /** * **/ public final static char END_PACKET = '.'; /** * **/ //public final static char CR = '\r'; public MMPInput(MMPDispatcherPeer peer, VariableNormalizer normalizer, // UNA remote, InputStream in) { if(peer == null || normalizer == null || remote == null || in == null) throw new IllegalArgumentException(); this.peer = peer; this.normalizer = normalizer; this.remote = remote; this.in = in; if(silenceOnConnectTimeout > 0) guard = new TimeoutGuard(); setDaemon(true); start(); } /** * **/ public synchronized void close() throws IOException { if(guard != null) { sentSomething = true; guard.interrupt(); } if(!isInterrupted()) { interrupt(); peer.error(new MMPReceiverException(MMPReceiverException.CLOSED),// remote,null); in.close(); // stop() is deprecated in java 1.2 } } /** * **/ private void forceClose() { try { close(); } catch(IOException e) { } } /** * **/ private void handlePacket(Vector header) { sentSomething = true; if(emptyPacket) header.removeAllElements(); peer.manage(remote,new MMPPacket(remote,header,body.toString())); body.setLength(0); length = -1; emptyPacket = true; } /** * **/ private Vector parseHeader() { Vector header = ModifierParser.parseHeader(normalizer,// rawHeader.toString()); rawHeader.setLength(0); if(globalLength != -1) length = globalLength; else length = -1; if(header == null || header.size() <= 0) return header; VariableModifier modifier; char glyph; String name; Value value; for(int i = 0;i < header.size();++i) { modifier = (VariableModifier)header.elementAt(i); name = modifier.getName(); if(MMPMessageCenter.lengthTag.equals(name)) { glyph = modifier.getGlyph(); if(glyph == VariableModifier.GLYPH_DIMINISH) { globalLength = -1; length = -1; } else if(glyph == VariableModifier.GLYPH_SET || // glyph == VariableModifier.GLYPH_ASSIGN) { value = modifier.getValue(); try { length = value == null ? -1 : value.toInt(); } catch(NumberFormatException e) { length = -1; } if(glyph == VariableModifier.GLYPH_ASSIGN) globalLength = length; } header.removeElementAt(i--); } } return header; } /** * **/ public void run() { Vector header = null; boolean gotEnd = false; boolean gotEol = true; boolean inHeader = true; int ch = 0; char c = 0; int lineLength = 0; int lineCount = 0; int bodyCount = 0; int maxValue = Integer.MAX_VALUE - 10; while(true) { try { ch = (char)in.read(); } catch(IOException e) { ch = -1; } if(ch < 0 || ch > 0xff || isInterrupted()) { if(!isInterrupted()) forceClose(); return; } c = (char)ch; //if(inHeader && c == CR) continue; if(inHeader && gotEol && // (c == VariableModifier.EOL || c == END_PACKET)) { // end of header inHeader = false; if(rawHeader.length() > 0) emptyPacket = false; header = parseHeader(); if(length >= 0) { // special case: body length given if(length > maxBodyCount || maxBodyCount < 5) length = maxBodyCount; byte[] b = new byte[length]; try { ch = in.read(b); } catch(IOException e) { ch = -1; } if(ch < 0) forceClose(); body.setLength(0); body.append(new String(b)); b = null; // wait for end of body gotEnd = false; gotEol = false; while(true) { try { ch = in.read(); } catch(IOException e) { ch = -1; } if(ch < 0 || ch > 0xff) forceClose(); c = (char)ch; if(gotEnd && c == VariableModifier.EOL) break; gotEnd = gotEol && c == END_PACKET ? true : false; gotEol = c == VariableModifier.EOL ? true : false; } if(body.length() > 0) emptyPacket = false; handlePacket(header); length = -1; gotEnd = false; gotEol = true; inHeader = true; lineLength = 0; lineCount = 0; bodyCount = 0; } else { gotEnd = gotEol && c == END_PACKET ? true : false; gotEol = c == VariableModifier.EOL ? true : false; } continue; } if(gotEnd && c == VariableModifier.EOL) { // end of body if(inHeader) { // remove END_PACKET from header and parse it try { rawHeader.setLength(rawHeader.length() - 1); } catch(StringIndexOutOfBoundsException e) { rawHeader.setLength(0); } if(rawHeader.length() > 0) emptyPacket = false; header = parseHeader(); } else { // remove EOL + END_PACKET from body try { body.setLength(body.length() - 2); } catch(StringIndexOutOfBoundsException e) { body.setLength(0); } } if(body.length() > 0) emptyPacket = false; handlePacket(header); length = -1; gotEnd = false; gotEol = true; inHeader = true; lineLength = 0; lineCount = 0; bodyCount = 0; continue; } gotEnd = gotEol && c == END_PACKET ? true : false; gotEol = c == VariableModifier.EOL ? true : false; // TODO: send some error, if packet is shortcutted if(inHeader) { if(gotEol) { lineLength = 0; ++lineCount; } if(lineCount < maxLineCount || maxLineCount < 5) { if(lineLength++ < maxLineLength || maxLineLength < 5) { rawHeader.append(c); } else { if(lineLength > maxValue) { // prevent overflows --lineLength; } } } else { if(lineCount > maxValue) { // prevent overflows --lineCount; } } } else { if(bodyCount++ < maxBodyCount || maxBodyCount < 5) { body.append(c); } else { if(bodyCount > maxValue) { // prevent overflows --bodyCount; } } } } } }