package com.limegroup.gnutella.udpconnect; import java.io.IOException; import java.io.OutputStream; import com.limegroup.gnutella.messages.BadPacketException; import com.limegroup.gnutella.messages.Message; /** Outline of message to allow a reliable udp protocol to be built on top of * Gnutella messages. */ public abstract class UDPConnectionMessage extends Message { // Referenced from message // public static final byte F_UDP_CONNECTION = (byte)0x41; // The version number of the protocol to allow for future improvements public static final int PROTOCOL_VERSION_NUMBER = 0; // Opcode identifiers for the sub-message types protected static final byte OP_SYN = 0x0; protected static final byte OP_ACK = 0x1; protected static final byte OP_KEEPALIVE = 0x2; protected static final byte OP_DATA = 0x3; protected static final byte OP_FIN = 0x4; /** The number of bytes in a GUID */ protected static final int GUID_SIZE = 16; /** The maximum amount of data that can go into the GUID */ protected static final int MAX_GUID_DATA = 12; /** The start location of data embedded in the GUID */ protected static final int GUID_DATA_START = GUID_SIZE - MAX_GUID_DATA; /** An empty byte array for internal use */ protected static byte[] emptyByteArray = new byte[0]; /** This is an identifier for a stream from a given IP. 1 byte */ protected byte _connectionID; /** This is the opcode for the sub-message type. 1 nibble */ protected byte _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. 14 bytes */ protected byte _data1[]; /** The offset of the data in data1. 4 in GUID data. 0 in raw data */ protected int _data1Offset; /** The amount of data used in the GUID. Up to 12 bytes by design. */ protected int _data1Length; // 1 nibble /** The second piece of data in this message. This will hold any data stored in the payload. Up to 512 bytes by design. */ protected byte _data2[]; /** The offset of the data in data1. 0 in network payload. 12 in raw data. */ protected int _data2Offset; /** The usable length of data2. Up to 500 bytes by design. */ protected int _data2Length; private static final BadPacketException NO_MATCH = new BadPacketException("No matching UDPConnectionMessage"); public static UDPConnectionMessage createMessage( byte[] guid, byte ttl, byte hops, byte[] payload) throws BadPacketException { byte opcode = (byte)((int)(guid[1] & 0xf0) >> 4); // Create appropriate UDPConnectionMessage switch (opcode) { case OP_SYN: return new SynMessage(guid,ttl,hops, payload); case OP_ACK: return new AckMessage(guid,ttl,hops, payload); case OP_KEEPALIVE: return new KeepAliveMessage(guid,ttl,hops, payload); case OP_DATA: return new DataMessage(guid,ttl,hops, payload); case OP_FIN: return new FinMessage(guid,ttl,hops, payload); } throw NO_MATCH; } /** * Construct a new UDPConnectionMessage with the specified * settings and data. */ protected UDPConnectionMessage( byte connectionID, byte opcode, long sequenceNumber, byte[] data, int datalength ) { super( /*guid*/ buildGUID(connectionID, opcode, sequenceNumber, data, datalength), /*func*/ F_UDP_CONNECTION, /*ttl*/ (byte)1, /*hops*/ (byte)0, /*length*/ calcPayloadLength(datalength)); _connectionID = connectionID; _opcode = opcode; _sequenceNumber = sequenceNumber; _data1 = data; _data1Offset = 0; _data1Length = (datalength >= MAX_GUID_DATA ? MAX_GUID_DATA : datalength); _data2 = data; _data2Offset = MAX_GUID_DATA; _data2Length = getLength(); } /** * Construct a new UDPConnectionMessage from the network. */ protected UDPConnectionMessage( byte[] guid, byte ttl, byte hops, byte[] payload) throws BadPacketException { super( /*guid*/ guid, /*func*/ F_UDP_CONNECTION, /*ttl*/ ttl, /*hops*/ hops, /*length*/ payload.length); unpackFromWire(guid, payload); } /** * Return the messages connectionID identifier. */ public byte getConnectionID() { return _connectionID; } /** * Return the messages sequence number */ public long getSequenceNumber() { return _sequenceNumber; } /** * Extend the sequence number of incoming messages with the full 8 bytes * of state */ public void extendSequenceNumber(long seqNo) { _sequenceNumber = seqNo; } /** * Allocate and fill in the data packed in the GUID. */ private static byte[] buildGUID(byte connectionID, byte opcode, long sequenceNumber, byte[] data, int datalength) { int guidDataLength = (datalength >= MAX_GUID_DATA ? MAX_GUID_DATA : datalength); // Fill up the GUID byte guid[] = new byte[GUID_SIZE]; guid[0] = connectionID; guid[1] = (byte) ((((opcode & 0x0f) << 4) | (((byte)guidDataLength) & 0x0f))); guid[2] = (byte)((sequenceNumber & 0xff00) >> 8); guid[3] = (byte)((sequenceNumber & 0x00ff)); int end = GUID_DATA_START + guidDataLength; for ( int i = GUID_DATA_START; i < end; i++ ) { guid[i] = data[i - GUID_DATA_START]; } return guid; } /** * Calculate the payload length when the data is available in one array. */ private static int calcPayloadLength(int datalength) { return(datalength <= MAX_GUID_DATA ? 0 : datalength - MAX_GUID_DATA); } /** * Given the guid and the payload, recreate the in memory data structures */ private void unpackFromWire(byte[] guid, byte[] payload) throws BadPacketException { _connectionID = guid[0]; _opcode = (byte)((int)(guid[1] & 0xf0) >> 4); _sequenceNumber = (((long) guid[2] & 0xff) << 8) | ((long) guid[3] & 0xff); _data1 = guid; _data1Offset = GUID_DATA_START; _data1Length = ((int) guid[1] & 0x0f); _data2 = payload; _data2Offset = 0; _data2Length = payload.length; if ( _data1Length > MAX_GUID_DATA ) throw new BadPacketException("GUID data too big"); } /** * Create a byte array for data sending purposes. */ public static byte[] buildByteArray(byte data) { byte[] darray = new byte[1]; darray[0] = data; return darray; } /** * Create a byte array from 1 byte and 1 short int */ public static byte[] buildByteArray(byte val1, int val2) { byte[] darray = new byte[3]; darray[0] = val1; darray[1] = (byte)((val2 & 0xff00) >> 8); darray[2] = (byte)((val2 & 0x00ff)); return darray; } /** * Create a byte array for data sending purposes. */ public static byte[] buildByteArray(int val1, int val2) { byte[] darray = new byte[4]; darray[0] = (byte)((val1 & 0xff00) >> 8); darray[1] = (byte)((val1 & 0x00ff)); darray[2] = (byte)((val2 & 0xff00) >> 8); darray[3] = (byte)((val2 & 0x00ff)); return darray; } /** * Return an int from 2 unsigned bytes */ public static int getShortInt(byte b1, byte b2) { return (((int) b1 & 0xff) << 8) | ((int) b2 & 0xff); } /** * Return the length of data stored in this message. */ public int getDataLength() { return _data1Length + getLength(); } /** * Output additional data as payload. */ protected void writePayload(OutputStream out) throws IOException { if ( _data2Length > 0 ) out.write(_data2, _data2Offset, _data2Length); } /** * There is no extended information. */ public Message stripExtendedPayload() { return this; } /** * Drop nothing. */ public void recordDrop() { } }