Search in sources :

Example 6 with SHA1Hasher

use of com.biglybt.core.util.SHA1Hasher in project BiglyBT by BiglySoftware.

the class UDPConnectionSet method receive.

public void receive(byte[] initial_data, int initial_data_length) throws IOException {
    if (failed) {
        throw (new IOException("Connection set has failed"));
    }
    dumpState();
    if (manager.trace()) {
        trace("Read: total=" + initial_data_length);
    }
    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 {
            // seq1
            initial_buffer.getInt();
            Integer seq2 = new Integer(initial_buffer.getInt());
            // seq3
            initial_buffer.getInt();
            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) {
                    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);
                } else if (command == UDPPacket.COMMAND_ACK) {
                    receiveAckCommand(buffer);
                } else if (command == UDPPacket.COMMAND_CLOSE) {
                    receiveCloseCommand(buffer);
                } else if (command == UDPPacket.COMMAND_STAT_REQUEST) {
                    receiveStatsRequest(buffer);
                } else if (command == UDPPacket.COMMAND_STAT_REPLY) {
                    receiveStatsReply(buffer);
                } else {
                // ignore unrecognised commands to support future change
                }
            }
            if (this_is_oop) {
                synchronized (this) {
                    current_receive_out_of_order_count++;
                    total_packets_out_of_order++;
                }
            }
        } finally {
            boolean send_ack = false;
            synchronized (this) {
                long unack_diff = current_receive_unack_in_sequence_count - sent_receive_unack_in_sequence_count;
                long oos_diff = current_receive_out_of_order_count - sent_receive_out_of_order_count;
                if (unack_diff > RECEIVE_UNACK_IN_SEQUENCE_LIMIT || oos_diff > RECEIVE_OUT_OF_ORDER_ACK_LIMIT) {
                    send_ack = true;
                }
            }
            if (send_ack) {
                sendAckCommand(false);
            }
            synchronized (this) {
                // if we have either received in-order packets that we haven't sent an ack for or
                // out-of-order packets start the ack timer
                // only exception is if we have only a lazy-ack packet outstanding and no out-of-order
                long unack_diff = current_receive_unack_in_sequence_count - sent_receive_unack_in_sequence_count;
                if (unack_diff == 1 && lazy_ack_found && receive_out_of_order_packets.size() == 0) {
                    if (manager.trace()) {
                        trace("Not starting ack timer, only lazy ack received");
                    }
                    startKeepAliveTimer();
                } else {
                    stopKeepAliveTimer();
                    if (unack_diff > 0 || receive_out_of_order_packets.size() > 0) {
                        if (explicitack_ticks == 0) {
                            explicitack_ticks = getExplicitAckTicks();
                        }
                    }
                }
            }
            if (write_select) {
                synchronized (connection_writers) {
                    Iterator it = connection_writers.iterator();
                    while (it.hasNext()) {
                        UDPConnection c = (UDPConnection) it.next();
                        if (c.isConnected()) {
                            // we can safely do this while holding monitor as this simply queues an async selector notification if required
                            c.sent();
                        } else {
                            it.remove();
                        }
                    }
                }
            }
        }
    }
}
Also used : IOException(java.io.IOException) ByteBuffer(java.nio.ByteBuffer) SHA1Hasher(com.biglybt.core.util.SHA1Hasher)

Aggregations

SHA1Hasher (com.biglybt.core.util.SHA1Hasher)6 IOException (java.io.IOException)2 TOTorrentException (com.biglybt.core.torrent.TOTorrentException)1 ED2KHasher (com.biglybt.core.util.ED2KHasher)1 StringPattern (com.biglybt.ui.common.util.StringPattern)1 BufferedInputStream (java.io.BufferedInputStream)1 FileInputStream (java.io.FileInputStream)1 InputStream (java.io.InputStream)1 ByteBuffer (java.nio.ByteBuffer)1 Iterator (java.util.Iterator)1 RC4Engine (org.gudy.bouncycastle.crypto.engines.RC4Engine)1