package org.limewire.rudp.messages.impl; import java.io.IOException; import java.io.OutputStream; import java.nio.ByteBuffer; import java.nio.ByteOrder; import org.limewire.rudp.messages.MessageFormatException; import org.limewire.rudp.messages.RUDPMessage; import org.limewire.util.BufferUtils; /** Abstract message class to allow a reliable UDP * protocol to be built on top of Gnutella messages. */ public abstract class RUDPMessageImpl implements RUDPMessage { /** The maximum amount of data that can go into the GUID */ protected static final int MAX_DATA1_SIZE = 12; /** An empty byte array for internal use */ protected static byte[] emptyByteArray = new byte[16]; /** This is an identifier for a stream from a given IP. 1 byte */ protected final byte _connectionID; /** This is the opcode for the sub-message type. 1 nibble */ protected final OpCode _opcode; /** The communications message sequence number. 2 bytes */ protected long _sequenceNumber; /** The first piece of data in this message. This will hold any data stored in the GUID. Up to MAX_DATA1_SIZE bytes */ protected final ByteBuffer _data1; /** The second piece of data in this message. This will hold any data stored in the payload. Up to 512 bytes by design. */ protected final ByteBuffer _data2; /** Constructs an RUDPMessage with shorts integers of data. */ protected RUDPMessageImpl(byte connectionID, OpCode opcode, long sequenceNumber, short d1, short d2) { _connectionID = connectionID; _opcode = opcode; _sequenceNumber = sequenceNumber; _data1 = ByteBuffer.allocate(4); _data1.order(ByteOrder.BIG_ENDIAN); _data1.putShort(d1); _data1.putShort(d2); _data1.flip(); _data2 = BufferUtils.getEmptyBuffer(); } /** Constructs an RUDPMessage with one byte of data. */ protected RUDPMessageImpl(byte connectionID, OpCode opcode, long sequenceNumber, byte b) { _connectionID = connectionID; _opcode = opcode; _sequenceNumber = sequenceNumber; _data1 = ByteBuffer.allocate(1); _data1.put(b); _data1.flip(); _data2 = BufferUtils.getEmptyBuffer(); } /** Constructs an RUDPMessage with one byte and one short of data. */ protected RUDPMessageImpl(byte connectionID, OpCode opcode, long sequenceNumber, byte b, short d) { _connectionID = connectionID; _opcode = opcode; _sequenceNumber = sequenceNumber; _data1 = ByteBuffer.allocate(3); _data1.order(ByteOrder.BIG_ENDIAN); _data1.put(b); _data1.putShort(d); _data1.flip(); _data2 = BufferUtils.getEmptyBuffer(); } protected RUDPMessageImpl(byte connectionID, OpCode opcode, long sequenceNumber, byte[] data) { this(connectionID, opcode, sequenceNumber, data, data.length); } /** Constructs an RUDPMessage with a byte[] of data. */ protected RUDPMessageImpl(byte connectionID, OpCode opcode, long sequenceNumber, byte[] data, int datalength ) { _connectionID = connectionID; _opcode = opcode; _sequenceNumber = sequenceNumber; if(datalength > 0) _data1 = ByteBuffer.wrap(data, 0, Math.min(datalength, MAX_DATA1_SIZE)); else _data1 = BufferUtils.getEmptyBuffer(); int data2Length = Math.max(0, datalength - MAX_DATA1_SIZE); if(data2Length > 0) _data2 = ByteBuffer.wrap(data, MAX_DATA1_SIZE, data2Length); else _data2 = BufferUtils.getEmptyBuffer(); } /** * Construct a new UDPConnectionMessage from the network. */ protected RUDPMessageImpl(OpCode opcode, byte connectionId, long sequenceNumber, ByteBuffer data1, ByteBuffer data2) throws MessageFormatException { _opcode = opcode; _connectionID = connectionId; _sequenceNumber = sequenceNumber; _data1 = data1; _data2 = data2; } /* (non-Javadoc) * @see org.limewire.rudp.RUDPMessage#getConnectionID() */ public byte getConnectionID() { return _connectionID; } /* (non-Javadoc) * @see org.limewire.rudp.RUDPMessage#getSequenceNumber() */ public long getSequenceNumber() { return _sequenceNumber; } /* (non-Javadoc) * @see org.limewire.rudp.RUDPMessage#extendSequenceNumber(long) */ public void extendSequenceNumber(long seqNo) { _sequenceNumber = seqNo; } /* (non-Javadoc) * @see org.limewire.rudp.RUDPMessage#getDataLength() */ public int getDataLength() { return _data1.limit() + _data2.limit(); } public int getLength() { return 23 + _data2.limit(); } public OpCode getOpCode() { return _opcode; } /** * Returns the length of the data1 block. Since only some packets have * an actual length value the default implementation returns 0; */ protected int getData1Length() { return 0; } /** Writes the entire message to an OutputStream. */ public void write(OutputStream out) throws IOException { out.write(_connectionID); out.write(((_opcode.toByte() & 0x0F) << 4) | ((byte)getData1Length() & 0x0F)); out.write((byte)((_sequenceNumber & 0xFF00) >> 8)); out.write((byte)(_sequenceNumber & 0x00FF)); if(_data1.hasRemaining()) out.write(_data1.array(), _data1.arrayOffset() + _data1.position(), _data1.remaining()); //make sure we fill up the remaining header data. if(_data1.remaining() < MAX_DATA1_SIZE) out.write(emptyByteArray, 0, MAX_DATA1_SIZE - _data1.remaining()); // write out the reserved area numbers. out.write(F_RUDP_MESSAGE); out.write((byte)1); out.write((byte)0); // write out the length of the payload. org.limewire.util.ByteUtils.int2leb(_data2.remaining(), out); // write the payload. if ( _data2.hasRemaining() ) out.write(_data2.array(), _data2.arrayOffset() + _data2.position(), _data2.remaining()); } }