package com.limegroup.gnutella.messages; import java.io.IOException; import java.io.InputStream; import java.io.InterruptedIOException; import java.net.SocketAddress; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.limewire.core.settings.ConnectionSettings; import org.limewire.core.settings.MessageSettings; import org.limewire.inject.EagerSingleton; import org.limewire.util.ByteUtils; import com.google.inject.Inject; import com.limegroup.gnutella.messages.Message.Network; import com.limegroup.gnutella.util.DataUtils; /** * Factory to turn binary input as read from Network to Message * Objects. */ @EagerSingleton public class MessageFactoryImpl implements MessageFactory { private static final Log LOG = LogFactory.getLog(MessageFactoryImpl.class); /** Array of MessageParser(s) */ private final MessageParser[] PARSERS = new MessageParser[0xFF + 1]; /** * Cached soft max ttl -- if the TTL+hops is greater than SOFT_MAX, * the TTL is set to SOFT_MAX-hops. */ private final byte defaultSoftMax = ConnectionSettings.SOFT_MAX.getValue(); public MessageFactoryImpl() { } @Inject public MessageFactoryImpl(MessageParserBinder messageParserBinder) { messageParserBinder.bind(this); } public void setParser(byte functionId, MessageParser parser) { if (parser == null) { throw new NullPointerException("MessageParser is null"); } int index = functionId & 0xFF; Object o = null; synchronized (PARSERS) { o = PARSERS[index]; PARSERS[index] = parser; } if (o != null && LOG.isErrorEnabled()) { LOG.error("There was already a MessageParser of type " + o.getClass() + " registered for functionId " + functionId); } } public MessageParser getParser(byte functionId) { return PARSERS[functionId & 0xFF]; } public Message read(InputStream in, Network network) throws BadPacketException, IOException { return read(in, network, new byte[23], defaultSoftMax, null); } public Message read(InputStream in, Network network, byte softMax) throws BadPacketException, IOException { return read(in, network, new byte[23], softMax, null); } public Message read(InputStream in, Network network, byte[] buf, byte softMax) throws BadPacketException, IOException { return read(in, network, buf, softMax, null); } public Message read(InputStream in, Network network, byte[] buf, SocketAddress addr) throws BadPacketException, IOException { return read(in, network, buf, defaultSoftMax, addr); } public Message read(InputStream in, Network network, byte[] buf, byte softMax, SocketAddress addr) throws BadPacketException, IOException { // 1. Read header bytes from network. If we timeout before any // data has been read, return null instead of throwing an // exception. for (int i = 0; i < 23;) { int got; try { got = in.read(buf, i, 23 - i); } catch (InterruptedIOException e) { // have we read any of the message yet? if (i == 0) return null; else throw e; } if (got == -1) { throw new IOException("Connection closed."); } i += got; } // 2. Unpack. int length = ByteUtils.leb2int(buf, 19); // 2.5 If the length is hopelessly off (this includes lengths > // than 2^31 bytes, throw an irrecoverable exception to // cause this connection to be closed. if (length < 0 || length > MessageSettings.MAX_LENGTH.getValue()) { throw new IOException("Unreasonable message length: " + length); } // 3. Read rest of payload. This must be done even for bad // packets, so we can resume reading packets. byte[] payload = null; if (length != 0) { payload = new byte[length]; for (int i = 0; i < length;) { int got = in.read(payload, i, length - i); if (got == -1) { throw new IOException("Read EOF before EOM."); } i += got; } } else { payload = DataUtils.EMPTY_BYTE_ARRAY; } return createMessage(buf, payload, network, softMax, addr); } public Message createMessage(byte[] header, byte[] payload, Network network, byte softMax, SocketAddress addr) throws BadPacketException, IOException { if (header.length < 19) { throw new IllegalArgumentException("header must be >= 19 bytes."); } byte func = header[16]; // Get Parser based on opcode. MessageParser parser = getParser(func); if (parser == null) { throw new BadPacketException("Unrecognized function code: " + func); } return parser.parse(header, payload, network, softMax, addr); } }