package com.robonobo.eon; /* * Eye-Of-Needle * Copyright (C) 2008 Will Morton (macavity@well.com) & Ray Hilton (ray@wirestorm.net) * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import java.nio.ByteBuffer; import java.util.List; public class SEONPacket extends EONPacket { /** Our sequence numbers go up to 2^32-1, so we store them in longs - check against this value to ensure they are kosher */ public static final long MAX_SEQNUM = 4294967295L; // 2^32-1 public static final int MAX_SACK_BLOCKS = 3; private EonSocketAddress sourceEP, destEP; private long seqNum, ackNum; // NOTE: although these are typed long, // their max value is 2^32 private boolean syn, ack, rst, fin; private long[] sackBegins; private long[] sackEnds; private ByteBuffer payload; public SEONPacket(EonSocketAddress sourceEndPoint, EonSocketAddress destEndPoint, ByteBuffer payload) { sourceEP = sourceEndPoint; destEP = destEndPoint; this.payload = payload; ack = rst = syn = fin = false; } public EonSocketAddress getDestSocketAddress() { return destEP; } public long[] getSackBegins() { return sackBegins; } public long[] getSackEnds() { return sackEnds; } public void setSackBlocks(long[] begins, long[] ends) { if(begins.length != ends.length) throw new IllegalArgumentException("begins must have the same length as ends"); sackBegins = begins; sackEnds = ends; } public void setSackBlocks(List<Long> begins, List<Long> ends) { if(begins.size() != ends.size()) throw new IllegalArgumentException("begins must have the same length as ends"); int numBlocks = Math.min(begins.size(), MAX_SACK_BLOCKS); sackBegins = new long[numBlocks]; sackEnds = new long[numBlocks]; for(int i=0;i<numBlocks;i++) { sackBegins[i] = begins.get(i); sackEnds[i] = ends.get(i); } } // Internal Properties public EonSocketAddress getSourceSocketAddress() { return sourceEP; } public void setDestSocketAddress(EonSocketAddress endPoint) { destEP = endPoint; } public void setSourceSocketAddress(EonSocketAddress endPoint) { sourceEP = endPoint; } public void toByteBuffer(ByteBuffer buf) { // Source Port buf.put((byte) ((sourceEP.getEonPort() & 0xFF00) >> 8)); buf.put((byte) (sourceEP.getEonPort() & 0xFF)); // Destination Port buf.put((byte) ((destEP.getEonPort() & 0xFF00) >> 8)); buf.put((byte) (destEP.getEonPort() & 0xFF)); // Protocol buf.put((byte) 0x2); // S-EON is protocol 2 // Flags int flagsAndSack = 0; if (ack) flagsAndSack += 0x80; if (rst) flagsAndSack += 0x40; if (syn) flagsAndSack += 0x20; if (fin) flagsAndSack += 0x10; // Number of sack regions goes in bits 4-5 of byte 5 int numSackRegions = (sackBegins == null) ? 0 : sackBegins.length; if(numSackRegions > MAX_SACK_BLOCKS) numSackRegions = MAX_SACK_BLOCKS; if (numSackRegions > 0) flagsAndSack += ((numSackRegions & 0x3) << 2); buf.put((byte) flagsAndSack); // Sequence Number buf.put((byte) ((seqNum >> 24) & 0xFF)); buf.put((byte) ((seqNum >> 16) & 0xFF)); buf.put((byte) ((seqNum >> 8) & 0xFF)); buf.put((byte) ((seqNum) & 0xFF)); // Ack Number buf.put((byte) ((ackNum >> 24) & 0xFF)); buf.put((byte) ((ackNum >> 16) & 0xFF)); buf.put((byte) ((ackNum >> 8) & 0xFF)); buf.put((byte) ((ackNum) & 0xFF)); // Sack blocks for (int i = 0; i < numSackRegions; i++) { long begin = sackBegins[i]; buf.put((byte) ((begin >> 24) & 0xFF)); buf.put((byte) ((begin >> 16) & 0xFF)); buf.put((byte) ((begin >> 8) & 0xFF)); buf.put((byte) ((begin) & 0xFF)); long end = sackEnds[i]; buf.put((byte) ((end >> 24) & 0xFF)); buf.put((byte) ((end >> 16) & 0xFF)); buf.put((byte) ((end >> 8) & 0xFF)); buf.put((byte) ((end) & 0xFF)); } // Packet payload if (payload != null) { payload.position(0); buf.put(payload); } } public String toString() { StringBuffer sb = new StringBuffer(); sb.append("SEONPacket: "); sb.append("Source ").append(sourceEP.toString()).append(", Dest ").append(destEP.toString()).append(" "); sb.append("SN ").append(String.valueOf(seqNum)).append(" AN ").append(String.valueOf(ackNum)).append(" "); if (isSYN()) { sb.append("SYN "); } if (isACK()) { sb.append("ACK "); } if (isRST()) { sb.append("RST "); } if (isFIN()) { sb.append("FIN "); } if (payload != null && payload.limit() > 0) { sb.append(payload.limit()).append(" bytes data"); } else { sb.append("no data"); } if (sackBegins != null && sackBegins.length > 0) { sb.append(" SACK["); for (int i = 0; i < sackBegins.length; i++) { if (i > 0) sb.append(":"); sb.append(sackBegins[i]).append(",").append(sackEnds[i]); } sb.append("]"); } return sb.toString(); } public long getAckNumber() { return ackNum; } @Override public ByteBuffer getPayload() { return payload; } public int getPayloadSize() { if(payload == null) return 0; return payload.limit(); } public int getProtocol() { return 2; // S-EON is Protocol 2 } public long getSequenceNumber() { return seqNum; } public boolean isACK() { return ack; } public boolean isFIN() { return fin; } public boolean isRST() { return rst; } public boolean isSYN() { return syn; } public void setACK(boolean ack) { this.ack = ack; } public void setAckNumber(long n) { if (n > MAX_SEQNUM) throw new IllegalArgumentException("ack number cannot be greater than 2^32-1"); if (n < 0) throw new IllegalArgumentException("ack number cannot be less than 0"); ackNum = n; } public void setPayload(ByteBuffer payload) { this.payload = payload; } public void setFIN(boolean fin) { this.fin = fin; } public void setRST(boolean rst) { this.rst = rst; } public void setSequenceNumber(long n) { if (n > MAX_SEQNUM) throw new IllegalArgumentException("sequence number cannot be greater than 2^32-1"); if (n < 0) throw new IllegalArgumentException("sequence number cannot be less than 0"); seqNum = n; } public void setSYN(boolean syn) { this.syn = syn; } }