package org.torrent.internal.protocol;
import java.nio.ByteBuffer;
import org.torrent.internal.data.Hash;
import org.torrent.internal.protocol.message.BTMessageVisitor;
import org.torrent.internal.protocol.message.BitField;
import org.torrent.internal.protocol.message.BittorrentMessage;
import org.torrent.internal.protocol.message.Cancel;
import org.torrent.internal.protocol.message.Choke;
import org.torrent.internal.protocol.message.HandShakeA;
import org.torrent.internal.protocol.message.HandShakeB;
import org.torrent.internal.protocol.message.Have;
import org.torrent.internal.protocol.message.Interested;
import org.torrent.internal.protocol.message.KeepAlive;
import org.torrent.internal.protocol.message.NotInterested;
import org.torrent.internal.protocol.message.Piece;
import org.torrent.internal.protocol.message.Port;
import org.torrent.internal.protocol.message.RawMessage;
import org.torrent.internal.protocol.message.Request;
import org.torrent.internal.protocol.message.UnChoke;
import org.torrent.internal.util.Bits;
import org.torrent.internal.util.Validator;
public class BTTransformImpl implements BTTransform {
public enum MessageID {
KEEPALIVE(null), CHOKE(0), UNCHOKE(1), INTERESTED(2), NOT_INTERESTED(3), HAVE(
4), BITFIELD(5), REQUEST(6), PIECE(7), CANCEL(8), PORT(9), HANDSHAKE_A(
null), HANDSHAKE_B(null), DONT_HAVE(10), WIN_UPDATE(11);
public final Byte id;
private MessageID(Integer id) {
this.id = id != null ? id.byteValue() : null;
}
}
/*
* (non-Javadoc)
*
* @see org.torrent.internal.protocol.BittorrentProtocol#decodeBitField(ByteBuffer)
*/
public BitField decodeBitField(ByteBuffer data) {
Validator.notNull(data, "Data is null!");
ByteBuffer buf = partialDecode(data, data.remaining() - 4,
MessageID.BITFIELD);
Bits set = new Bits(data.remaining() * 8);
int i = 0;
while (buf.hasRemaining()) {
byte b = buf.get();
for (int j = 128; j > 0; j >>= 1) {
set.set(i++, (b & j) != 0);
}
}
return new BitField(set);
}
/*
* (non-Javadoc)
*
* @see org.torrent.internal.protocol.BittorrentProtocol#decodeCancel(ByteBuffer)
*/
public Cancel decodeCancel(ByteBuffer data) {
Validator.notNull(data, "Data is null!");
ByteBuffer buf = partialDecode(data, 13, MessageID.CANCEL);
int i = buf.getInt();
int s = buf.getInt();
int l = buf.getInt();
return new Cancel(i, s, l);
}
/*
* (non-Javadoc)
*
* @see org.torrent.internal.protocol.BittorrentProtocol#decodeChoke(ByteBuffer)
*/
public Choke decodeChoke(ByteBuffer data) {
partialDecode(data, 1, MessageID.CHOKE);
return Choke.CHOKE;
}
/*
* (non-Javadoc)
*
* @see org.torrent.internal.protocol.BittorrentProtocol#decodeHandshakeA(ByteBuffer)
*/
public HandShakeA decodeHandshakeA(ByteBuffer buf)
throws BittorrentMessageDecodingException {
Validator.notNull(buf, "Buffer is null");
Validator.isTrue(buf.remaining() >= 48, "Expected 48 bytes, got "
+ buf.remaining());
ByteBuffer proto = HandShakeA.getPROTOCOL();
if (buf.get() != proto.remaining()) {
throw new BittorrentMessageDecodingException(
"Handshake length invalid: " + buf.get(buf.position() - 1));
}
ByteBuffer tmp = buf.duplicate();
tmp.limit(tmp.position() + proto.remaining());
buf.position(buf.position() + proto.remaining());
if (!proto.equals(tmp)) {
throw new BittorrentMessageDecodingException(
"Handshake protocol invalid!");
}
byte res[] = new byte[8];
buf.get(res);
byte[] tmp2 = new byte[20];
buf.get(tmp2);
Hash info = new Hash(tmp2, Hash.Type.SHA1);
HandShakeA hs = new HandShakeA(info, res);
return hs;
}
/*
* (non-Javadoc)
*
* @see org.torrent.internal.protocol.BittorrentProtocol#decodeHandshakeB(ByteBuffer)
*/
public HandShakeB decodeHandshakeB(ByteBuffer data) {
Validator.notNull(data, "Data is null!");
Validator.isTrue(data.remaining() >= 20, "Invalid peer id size");
byte[] id = new byte[20];
data.get(id);
return new HandShakeB(new Hash(id, Hash.Type.ID));
}
/*
* (non-Javadoc)
*
* @see org.torrent.internal.protocol.BittorrentProtocol#decodeHave(ByteBuffer)
*/
public Have decodeHave(ByteBuffer data) {
Validator.notNull(data, "Data is null!");
ByteBuffer buf = partialDecode(data, 5, MessageID.HAVE);
return new Have(buf.getInt());
}
/*
* (non-Javadoc)
*
* @see org.torrent.internal.protocol.BittorrentProtocol#decodeInterested(ByteBuffer)
*/
public Interested decodeInterested(ByteBuffer data) {
partialDecode(data, 1, MessageID.INTERESTED);
return Interested.INTERESTED;
}
/*
* (non-Javadoc)
*
* @see org.torrent.internal.protocol.BittorrentProtocol#decodeKeepAlive(ByteBuffer)
*/
public KeepAlive decodeKeepAlive(ByteBuffer data) {
Validator.notNull(data, "Data is null!");
validateEmptyMessage(data);
return KeepAlive.KEEPALIVE;
}
/*
* (non-Javadoc)
*
* @see
* org.torrent.internal.protocol.BittorrentProtocol#decodeNotInterested(ByteBuffer)
*/
public NotInterested decodeNotInterested(ByteBuffer data) {
partialDecode(data, 1, MessageID.NOT_INTERESTED);
return NotInterested.NOTINTERESTED;
}
/*
* (non-Javadoc)
*
* @see org.torrent.internal.protocol.BittorrentProtocol#decodePiece(ByteBuffer)
*/
public Piece decodePiece(ByteBuffer data) {
Validator.notNull(data, "Data is null!");
ByteBuffer buf = partialDecode(data, data.remaining() - 4,
MessageID.PIECE);
int i = buf.getInt();
int s = buf.getInt();
data = data.duplicate();
data.position(13);
return new Piece(i, s, data);
}
/*
* (non-Javadoc)
*
* @see org.torrent.internal.protocol.BittorrentProtocol#decodePort(ByteBuffer)
*/
public Port decodePort(ByteBuffer data) {
Validator.notNull(data, "Data is null!");
ByteBuffer buf = partialDecode(data, 3, MessageID.PORT);
int port = buf.getShort() & 0xFFFF;
// System.out.println("Message of type port");
// System.out.println("Requesting piece index: " + port);
// if (!ml_dht_enabled) {return;}
// MainlineDHTProvider provider = getDHTProvider();
// if (provider == null) {return;}
//
// try {provider.notifyOfIncomingPort(socket.getInetAddress().getHostAddress(), ((Port) msg).getPort());}
// catch (Throwable t) {t.printStackTrace();}
// con.send(new Request(((Have) msg).getPieceIndex(), 0, info.getPieceLength()), null);
// //bitFieldSender(bitProv, (BTSocketConnection) con));
//
return new Port(port);
}
/*
* (non-Javadoc)
*
* @see org.torrent.internal.protocol.BittorrentProtocol#decodeRequest(ByteBuffer)
*/
public Request decodeRequest(ByteBuffer data) {
Validator.notNull(data, "Data is null!");
ByteBuffer buf = partialDecode(data, 13, MessageID.REQUEST);
int i = buf.getInt();
int s = buf.getInt();
int l = buf.getInt();
return new Request(i, s, l);
}
/*
* (non-Javadoc)
*
* @see org.torrent.internal.protocol.BittorrentProtocol#decodeUnChoke(ByteBuffer)
*/
public UnChoke decodeUnChoke(ByteBuffer data) {
partialDecode(data, 1, MessageID.UNCHOKE);
return UnChoke.UNCHOKE;
}
/*
* (non-Javadoc)
*
* @see
* org.torrent.internal.protocol.BittorrentProtocol#encode(org.torrent.internal.protocol.message
* .BitField)
*/
public ByteBuffer encode(ByteBuffer a, BitField bitField) {
Bits set = bitField.getBitSet();
ByteBuffer buf = prepareMessage(a, (set.size() + 7) / 8,
MessageID.BITFIELD);
for (int i = 0; i < set.size();) {
byte data = 0;
for (int j = 128; i < set.size() && j > 0; j >>= 1, i++) {
if (set.get(i)) {
data |= j;
}
}
buf.put(data);
}
assert buf.remaining() == 0;
return buf;
}
/*
* (non-Javadoc)
*
* @see
* org.torrent.internal.protocol.BittorrentProtocol#encode(org.torrent.internal.protocol.message
* .Cancel)
*/
public ByteBuffer encode(ByteBuffer a, Cancel cancel) {
ByteBuffer buf = prepareMessage(a, 12, MessageID.CANCEL);
buf.putInt(cancel.getIndex());
buf.putInt(cancel.getStart());
buf.putInt(cancel.getLength());
return buf;
}
/*
* (non-Javadoc)
*
* @see
* org.torrent.internal.protocol.BittorrentProtocol#encode(org.torrent.internal.protocol.message
* .Choke)
*/
public ByteBuffer encode(ByteBuffer a, Choke choke) {
return prepareMessage(a, 0, MessageID.CHOKE);
}
/*
* (non-Javadoc)
*
* @see
* org.torrent.internal.protocol.BittorrentProtocol#encode(org.torrent.internal.protocol.message
* .HandShakeA)
*/
public ByteBuffer encode(ByteBuffer a, HandShakeA hs) {
ByteBuffer buf = ByteBuffer.allocate(48);
ByteBuffer proto = HandShakeA.getPROTOCOL();
buf.put((byte) proto.remaining());
buf.put(proto);
buf.put(new byte[8]);
buf.put(hs.getInfoHash().toByteArray());
return buf;
}
/*
* (non-Javadoc)
*
* @see
* org.torrent.internal.protocol.BittorrentProtocol#encode(org.torrent.internal.protocol.message
* .HandShakeB)
*/
public ByteBuffer encode(ByteBuffer a, HandShakeB hs) {
byte[] id = hs.getPeerID().toByteArray();
if (a == null || a.remaining() < id.length) {
a = ByteBuffer.allocate(id.length);
}
a.put(id);
return a;
}
/*
* (non-Javadoc)
*
* @see
* org.torrent.internal.protocol.BittorrentProtocol#encode(org.torrent.internal.protocol.message
* .Have)
*/
public ByteBuffer encode(ByteBuffer a, Have have) {
ByteBuffer buf = prepareMessage(a, 4, MessageID.HAVE);
buf.putInt(have.getPieceIndex());
return buf;
}
/*
* (non-Javadoc)
*
* @see
* org.torrent.internal.protocol.BittorrentProtocol#encode(org.torrent.internal.protocol.message
* .Interested)
*/
public ByteBuffer encode(ByteBuffer a, Interested interested) {
return prepareMessage(a, 0, MessageID.INTERESTED);
}
/*
* (non-Javadoc)
*
* @see
* org.torrent.internal.protocol.BittorrentProtocol#encode(org.torrent.internal.protocol.message
* .KeepAlive)
*/
public ByteBuffer encode(ByteBuffer a, KeepAlive keepAlive) {
return prepareMessage(a, 0, MessageID.KEEPALIVE);
}
/*
* (non-Javadoc)
*
* @see
* org.torrent.internal.protocol.BittorrentProtocol#encode(org.torrent.internal.protocol.message
* .NotInterested)
*/
public ByteBuffer encode(ByteBuffer a, NotInterested notInterested) {
return prepareMessage(a, 0, MessageID.NOT_INTERESTED);
}
/*
* (non-Javadoc)
*
* @see
* org.torrent.internal.protocol.BittorrentProtocol#encode(org.torrent.internal.protocol.message
* .Piece)
*/
public ByteBuffer encode(ByteBuffer a, Piece piece) {
ByteBuffer buf = prepareMessage(a, 8 + piece.getLength(),
MessageID.PIECE);
buf.putInt(piece.getIndex());
buf.putInt(piece.getStart());
buf.put(piece.getData());
return buf;
}
/*
* (non-Javadoc)
*
* @see
* org.torrent.internal.protocol.BittorrentProtocol#encode(org.torrent.internal.protocol.message
* .Port)
*/
public ByteBuffer encode(ByteBuffer a, Port port) {
ByteBuffer buf = prepareMessage(a, 2, MessageID.PORT);
buf.putShort((short) (port.getPort() & 0xffff));
return buf;
}
/*
* (non-Javadoc)
*
* @see
* org.torrent.internal.protocol.BittorrentProtocol#encode(org.torrent.internal.protocol.message
* .Request)
*/
public ByteBuffer encode(ByteBuffer a, Request request) {
ByteBuffer buf = prepareMessage(a, 12, MessageID.REQUEST);
buf.putInt(request.getIndex());
buf.putInt(request.getStart());
buf.putInt(request.getLength());
return buf;
}
/*
* (non-Javadoc)
*
* @see
* org.torrent.internal.protocol.BittorrentProtocol#encode(org.torrent.internal.protocol.message
* .UnChoke)
*/
public ByteBuffer encode(ByteBuffer a, UnChoke unChoke) {
return prepareMessage(a, 0, MessageID.UNCHOKE);
}
/**
* @param data
* @return
*/
public ByteBuffer partialDecode(ByteBuffer buf, int len, MessageID msgID) {
int length = buf.getInt();
Validator.isTrue(length == len, "Invalid length: " + length
+ " expected " + len);
byte type = buf.get();
Validator.isTrue(type == msgID.id, "Invalid type: " + type);
return buf;
}
protected ByteBuffer prepareMessage(ByteBuffer a, int payloadLength,
MessageID id) {
assert id != null;
ByteBuffer buffer;
if (id.id != null) {
buffer = a != null && a.remaining() >= payloadLength + 5 ? a
: ByteBuffer.wrap(new byte[payloadLength + 5]);
} else {
buffer = a != null && a.remaining() >= payloadLength + 4 ? a
: ByteBuffer.wrap(new byte[payloadLength + 4]);
}
buffer.putInt(buffer.capacity() - 4);
if (id.id != null) {
buffer.put(id.id);
}
return buffer;
}
/**
* @param data
*/
private void validateEmptyMessage(ByteBuffer buf) {
int length = buf.getInt();
Validator.isTrue(length == 0, "Not a valid keep-alive message!");
}
@Override
public int getHandShakeASize() {
return 29 + HandShakeA.getPROTOCOL().remaining();
}
@Override
public int getHandShakeBSize() {
return 20;
}
@Override
public ByteBuffer encodeMessage(final ByteBuffer a, BittorrentMessage msg) {
final ByteBuffer[] result = new ByteBuffer[1];
msg.accept(new BTMessageVisitor() {
@Override
public void visitBitField(BitField bitField) {
result[0] = encode(a, bitField);
}
@Override
public void visitCancel(Cancel cancel) {
result[0] = encode(a, cancel);
}
@Override
public void visitChoke(Choke choke) {
result[0] = encode(a, choke);
}
@Override
public void visitHandShakeA(HandShakeA handShakeA) {
result[0] = encode(a, handShakeA);
}
@Override
public void visitHandShakeB(HandShakeB handShakeB) {
result[0] = encode(a, handShakeB);
}
@Override
public void visitHave(Have have) {
result[0] = encode(a, have);
}
@Override
public void visitInterested(Interested interested) {
result[0] = encode(a, interested);
}
@Override
public void visitKeepAlive(KeepAlive keepAlive) {
result[0] = encode(a, keepAlive);
}
@Override
public void visitNotInterested(NotInterested notInterested) {
result[0] = encode(a, notInterested);
}
@Override
public void visitPiece(Piece piece) {
result[0] = encode(a, piece);
}
@Override
public void visitPort(Port port) {
result[0] = encode(a, port);
}
@Override
public void visitRequest(Request request) {
result[0] = encode(a, request);
}
@Override
public void visitUnChoke(UnChoke unChoke) {
result[0] = encode(a, unChoke);
}
@Override
public void visitRawMessage(RawMessage rawMessage) {
result[0] = rawMessage.getBuffer();
}
// @Override
// public void visitDontHave(DontHave dontHave) {
// result[0] = encode(a, dontHave);
//
// }
//
// @Override
// public void visitWinUpdate(WinUpdate winUpdate) {
// result[0] = encode(a, winUpdate);
//
// }
});
return result[0];
}
// @Override
// public DontHave decodeDontHave(ByteBuffer data) {
// // TODO Auto-generated method stub
// return null;
// }
//
// @Override
// public WinUpdate decodeWinUpdate(ByteBuffer data) {
// // TODO Auto-generated method stub
// return null;
// }
//
// @Override
// public ByteBuffer encode(ByteBuffer a, DontHave dontHave) {
//// Bits set = bitField.getBitSet();
//// ByteBuffer buf = prepareMessage(a, (set.size() + 7) / 8,
//// MessageID.BITFIELD);
//// for (int i = 0; i < set.size();) {
//// byte data = 0;
//// for (int j = 128; i < set.size() && j > 0; j >>= 1, i++) {
//// if (set.get(i)) {
//// data |= j;
//// }
//// }
//// buf.put(data);
//// }
//// assert buf.remaining() == 0;
//// return buf;
//
// ByteBuffer buf = prepareMessage(a, 5, MessageID.DONT_HAVE);
// buf.putInt(dontHave.getIndex());
//// buf.putInt(dontHave.getStart());
//// buf.putInt(dontHave.getLength());
// return buf;
// }
//
// @Override
// public ByteBuffer encode(ByteBuffer a, WinUpdate winUpdate) {
// ByteBuffer buf = prepareMessage(a, 5, MessageID.WIN_UPDATE);
// buf.putInt(winUpdate.getIndex());
//// buf.putInt(dontHave.getStart());
//// buf.putInt(dontHave.getLength());
// return buf;
// }
//
//// ssize_t btStream::Send_DontHave(size_t idx)
//// {
//// char msg[H_LEN + H_DONTHAVE_LEN];
////
//// msg_Dbg( p_sys->p_btc, "GoalbitParser|DONT_HAVE_OUT|%d|%lld", p_sys->p_btc->i_object_id, mdate());
////
//// set_nl(msg, H_DONTHAVE_LEN);
//// msg[H_LEN] = (char)M_DONT_HAVE;
//// set_nl(msg + H_LEN + H_BASE_LEN, idx);
////
//// return out_buffer.Put(sock, msg, H_LEN + H_DONTHAVE_LEN);
//// }
////
////
//// ssize_t btStream::Send_WinUpdate(size_t idx)
//// {
//// char msg[H_LEN + H_WIN_UPDATE];
////
//// msg_Dbg( p_sys->p_btc, "GoalbitParser|WIN_UPDATE_OUT|%d|%lld", p_sys->p_btc->i_object_id, mdate());
////
//// set_nl(msg, H_WIN_UPDATE);
//// msg[H_LEN] = (char)M_WIN_UPDATE;
//// set_nl(msg + H_LEN + H_BASE_LEN, idx);
////
//// return out_buffer.Put(sock,msg,H_LEN + H_WIN_UPDATE);
//// }
}