package com.jds.jn_module.network.session; import jpcap.packet.TCPPacket; import java.util.ArrayList; import java.util.HashMap; import java.util.List; /** * Author: VISTALL * Company: J Develop Station * Date: 3:08:26/04.04.2010 */ public class Sequenced { private static final long MODULO = 4294967296L; private HashMap<Long, SeqHolder> _waitingPrevious = new HashMap<Long, SeqHolder>(); private ArrayList<TCPPacket> _sequenced = new ArrayList<TCPPacket>(); private long _lastAck; public Sequenced() { } public void add(TCPPacket p) { for (SeqHolder sh : _waitingPrevious.values()) { TCPPacket old = sh.getPacket(); if (sh.getPacket().sequence == p.sequence) { if (old.data.length < p.data.length) { int diff = p.data.length - old.data.length; //System.err.printf("DIFF %d = %d - %d \n",diff, p.data.length, old.data.length); long seq = (old.sequence + old.data.length) % MODULO; //System.err.println("ADJUSTED TO SEQ: "+seq+"\nPACKET: "+p); byte[] data = new byte[diff]; System.arraycopy(p.data, p.data.length - diff, data, 0, data.length); p.data = data; p.sequence = seq; //System.err.println(Util.hexDump(data)); } else if (old.data.length == p.data.length) { // packet retransmitted // dont add, else the data will be duped (acked again) return; } } } long nextSeq = (p.sequence + p.data.length) % MODULO; _waitingPrevious.put(nextSeq, new SeqHolder(nextSeq, p)); this.processAck(_lastAck); } public void ack(TCPPacket p) { _lastAck = p.ack_num; processAck(p.ack_num); byte[] options = p.option; if (options != null) { long start = -1; long end = -1; for (int i = 0; i < options.length;) { // 0 end of opts // 1 nop if (options[i] == 0 || options[i] == 1) { i++; continue; } int type = options[i++] & 0xff; int size = options[i++] & 0xff; switch (type) { case 5: //SACK if (size - 2 == 8) { //System.err.println("SACK no pacote: "+p); //System.exit(0); start = getUInt(options, i); i += 4; end = getUInt(options, i); } else { throw new RuntimeException("No support for multiple S-ACK"); } break; default: //unsupported operation break; } i += size - 2; } while (end != -1 && start != -1) { SeqHolder sh = null; for (SeqHolder seqHolder : _waitingPrevious.values()) { if (seqHolder.getPacket().sequence == start) { long nextSeq = (seqHolder.getPacket().sequence + seqHolder.getPacket().data.length) % MODULO; // ignore if SACK covers whole packet if (end != nextSeq) { sh = seqHolder; break; } } } // if not already ack'ed (duplicate ack recvd) if (sh != null) { //System.err.println("NULL ACK: "+_lastAck+" -> "+p.toString()); //System.err.println("END: "+end+" START: "+start); int diff = (int) ((end - start) % MODULO); TCPPacket packet = sh.getPacket(); // check if sack doesnt overlap to next packet if (diff > packet.data.length) { // this one covers whole packet so just ignore start = (start + packet.data.length) % MODULO; continue; } start = -1; end = -1; byte[] data = new byte[diff]; //System.err.println("sh SEQ: "+sh.getPacket().sequence+" LEN: "+packet.data.length+" DIFF: "+diff); byte[] data2 = new byte[packet.data.length - diff]; System.arraycopy(sh.getPacket().data, 0, data, 0, data.length); System.arraycopy(sh.getPacket().data, diff, data2, 0, data2.length); //System.err.println("### SACK ("+data2.length+")\nCAUSED BY: "+p+"\nON: "+sh.getPacket()); TCPPacket sackPacket = new TCPPacket(packet.src_port, packet.dst_port, packet.sequence, packet.ack_num, packet.urg, packet.ack, packet.psh, packet.rst, packet.syn, packet.fin, packet.rsv1, packet.rsv2, packet.window, packet.urgent_pointer); sackPacket.data = data; sh.getPacket().data = data2; this.addSequenced(sackPacket); } else { end = -1; start = -1; } } } else { //processAck(p.ack_num); } } private static long getUInt(byte[] array, int offset) { if (array.length < offset + 4) { throw new IllegalArgumentException("Invalid offset for size"); } long ret = ((array[offset] & 0xffl) << 24); ret |= ((array[offset + 1] & 0xffl) << 16); ret |= ((array[offset + 2] & 0xffl) << 8); ret |= (array[offset + 3] & 0xffl); return ret; } public void processAck(long ack) { SeqHolder sh = _waitingPrevious.get(ack); if (sh != null && !sh.isAcked()) { //long previousSeq = (ack - sh.getPacket().data.length)%MODULO; long previousSeq = sh.getPacket().sequence; processAck(previousSeq); this.addSequenced(ack); } } private void addSequenced(long ack) { SeqHolder seqHolder = _waitingPrevious.get(ack); seqHolder.ack(); TCPPacket packet = seqHolder.getPacket(); this.addSequenced(packet); } private void addSequenced(TCPPacket packet) { _sequenced.add(packet); } public List<TCPPacket> getSequencedPackets() { return _sequenced; } public int getPendingSequencePackets() { return _waitingPrevious.size(); } public void flush() { _sequenced.clear(); } public boolean hasSequencedPacket() { return (!_sequenced.isEmpty()); } }