synchronized( this ){
total_packets_received++;
}
ByteBuffer initial_buffer = ByteBuffer.wrap( initial_data );
initial_buffer.limit( initial_data_length );
if ( !crypto_done ){
// first packet - connection setup and crypto handshake
// derive the sequence number in the normal way so that if a retranmission occurs
// after crypto has been setup then it'll get handled correctly as a dupliate packet
// below
initial_buffer.position( 4 );
Integer pseudo_seq = new Integer( initial_buffer.getInt());
initial_buffer.position( 0 );
if ( !receive_done_sequences.contains( pseudo_seq )){
receive_done_sequences.addFirst( pseudo_seq );
if ( receive_done_sequences.size() > RECEIVE_DONE_SEQ_MAX ){
receive_done_sequences.removeLast();
}
}
if ( outgoing ){
// a reply received by the initiator acknowledges that the initial message sent has
// been received
remoteLastInSequence( -1 );
}
receiveCrypto( initial_buffer );
}else{
// pull out the alternative last-in-order seq
byte[] alt_seq = new byte[4];
alt_seq[0] = initial_data[0];
alt_seq[1] = initial_data[1];
alt_seq[2] = initial_data[8];
alt_seq[3] = initial_data[9];
int alt = bytesToInt( alt_seq, 0 );
boolean write_select = remoteLastInSequence( alt );
boolean lazy_ack_found = false;
try{
initial_buffer.getInt(); // seq1
Integer seq2 = new Integer( initial_buffer.getInt());
initial_buffer.getInt(); // seq3
// first see if we know about this sequence number already
if ( receive_done_sequences.contains( seq2 )){
if ( manager.trace() ){
trace( "Duplicate processed packet: " + seq2 );
}
// if we're gone quiescent and our lazy-ack packet failed to be delivered we end up here with the other end
// resending their last message. We pick up this delivery failure and resend the packet
UDPPacket packet_to_send = null;
synchronized( this ){
stats_packets_duplicates++;
total_packets_duplicates++;
if ( transmit_unack_packets.size() == 1 ){
UDPPacket packet = (UDPPacket)transmit_unack_packets.get(0);
if ( !packet.isAutoRetransmit()){
if ( total_tick_count - packet.getSendTickCount() >= MIN_RETRANSMIT_TICKS ){
if ( manager.trace() ){
trace( "Retrans non-auto-retrans packet" );
}
packet_to_send = packet;
}
}
}
}
if ( packet_to_send != null ){
send( packet_to_send );
}
return;
}
if ( !out_seq_generator.isValidAlterativeSequence( alt )){
if ( manager.trace() ){
trace( "Received invalid alternative sequence " + alt + " - dropping packet" );
}
return;
}
boolean oop = false;
for (int i=0;i<receive_out_of_order_packets.size();i++){
Object[] entry = (Object[])receive_out_of_order_packets.get(i);
Integer oop_seq = (Integer)entry[0];
ByteBuffer oop_buffer = (ByteBuffer)entry[2];
if ( oop_seq.equals( seq2 )){
synchronized( this ){
if ( oop_buffer != null ){
stats_packets_duplicates++;
total_packets_duplicates++;
if ( manager.trace() ){
trace( "Duplicate out-of-order packet: " + seq2 );
}
return;
}
stats_packets_unique_received++;
total_packets_unique_received++;
if ( manager.trace() ){
trace( "Out-of-order packet entry data matched for seq " + seq2 );
}
// got data matching out-of-order-entry, add it in!
entry[2] = initial_buffer;
oop = true;
break;
}
}
}
if ( !oop ){
// not a known out-of-order packet. If our oop list is full then all we can do is drop
// the packet
boolean added = false;
while ( receive_out_of_order_packets.size() < RECEIVE_OUT_OF_ORDER_PACKETS_MAX ){
int[] seq_in = in_seq_generator.getNextSequenceNumber();
if ( seq2.intValue() == seq_in[1] ){
synchronized( this ){
stats_packets_unique_received++;
total_packets_unique_received++;
}
if ( receive_out_of_order_packets.size() == 0 ){
// this is an in-order packet :)
}else{
if ( manager.trace() ){
trace( "Out-of-order packet entry adding for seq " + seq_in[1] );
}
}
receive_out_of_order_packets.add( new Object[]{ seq2, new Integer( seq_in[3]), initial_buffer } );
added = true;
break;
}else{
if ( manager.trace() ){
trace( "Out-of-order packet: adding spacer for seq " + seq_in[1] );
}
receive_out_of_order_packets.add( new Object[]{ new Integer( seq_in[1]), new Integer( seq_in[3]), null } );
}
}
if ( !added ){
// drop the packet, we have no room to store it
if ( manager.trace() ){
trace( "Out-of-order packet dropped as too many pending" );
}
return;
}
}
boolean this_is_oop = true;
// process any ready packets
Iterator it = receive_out_of_order_packets.iterator();
while( it.hasNext()){
Object[] entry = (Object[])it.next();
ByteBuffer buffer = (ByteBuffer)entry[2];
if ( buffer == null ){
break;
}
it.remove();
byte[] data = buffer.array();
if ( buffer == initial_buffer ){
this_is_oop = false;
}
synchronized( this ){
current_receive_unack_in_sequence_count++;
}
Integer seq = (Integer)entry[0];
receive_last_inorder_sequence = seq.intValue();
receive_last_inorder_alt_sequence = ((Integer)entry[1]).intValue();
if ( !receive_done_sequences.contains( seq )){
receive_done_sequences.addFirst( seq );
if ( receive_done_sequences.size() > RECEIVE_DONE_SEQ_MAX ){
receive_done_sequences.removeLast();
}
}
header_cipher_in.processBytes( data, 12, 2, data, 12 );
int header_len = buffer.getShort()&0xffff;
if ( header_len > data.length ){
if ( manager.trace() ){
trace( "Header length too large" );
}
return;
}
header_cipher_in.processBytes( data, 14, header_len-14, data, 14 );
SHA1Hasher hasher = new SHA1Hasher();
hasher.update( data, 4, 4 );
hasher.update( data, 12, header_len - 4 - 12 );
byte[] hash = hasher.getDigest();
for (int i=0;i<4;i++){
if ( hash[i] != data[header_len-4+i] ){
if ( manager.trace() ){
trace( "hash incorrect" );
}
return;
}
}
byte version = buffer.get();
if ( version != UDPPacket.PROTOCOL_VERSION ){
// continue, assumption is that version changes are backward compatible
// throw( new IOException( "Invalid protocol version '" + version + "'" ));
}
byte flags = buffer.get();
if ( ( flags & UDPPacket.FLAG_LAZY_ACK ) != 0 ){
lazy_ack_found = true;
}
int their_timer_base = (buffer.getShort()&0xffff)*10;
receiveTimerBase( their_timer_base );
byte command = buffer.get();
if ( command == UDPPacket.COMMAND_DATA ){
receiveDataCommand( seq.intValue(), buffer, header_len );