package lbms.plugins.mldht.kad.messages;
import java.util.*;
//import org.gudy.azureus2.core3.util.BEncoder;
import lbms.plugins.mldht.kad.*;
import lbms.plugins.mldht.kad.DHT.DHTtype;
import lbms.plugins.mldht.kad.DHT.LogLevel;
import lbms.plugins.mldht.kad.messages.MessageBase.Method;
/**
* @author Damokles
*
*/
public class MessageDecoder {
public static MessageBase parseMessage (Map<String, Object> map,
RPCServerBase srv) {
try {
String vn = getStringFromBytes((byte[]) map.get(DHTConstants.TYP));
if (vn == null) {
return null;
}
String version = getStringFromBytes((byte[]) map
.get(DHTConstants.VER),true);
MessageBase mb = null;
if (vn.equals(DHTConstants.REQ)) {
mb = parseRequest(map, srv);
} else if (vn.equals(DHTConstants.RSP)) {
mb = parseResponse(map, srv);
} else if (vn.equals(DHTConstants.ERR)) {
mb = parseError(map);
}
if (mb != null && version != null) {
mb.setVersion(version);
}
return mb;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* @param map
* @return
*/
private static MessageBase parseError (Map<String, Object> map) {
Object error = map.get(DHTConstants.ERR);
int errorCode = 0;
String errorMsg = null;
if(error instanceof byte[])
errorMsg = getStringFromBytes((byte[])error);
else if (error instanceof List<?>)
{
List<Object> errmap = (List<Object>)error;
try
{
errorCode = ((Long) errmap.get(0)).intValue();
errorMsg = getStringFromBytes((byte[]) errmap.get(1));
} catch (Exception e)
{
// do nothing
}
}
if (errorMsg == null || !map.containsKey(DHTConstants.TID)) {
return null;
}
byte[] mtid;
if (map.get(DHTConstants.TID) instanceof Long) {
return null;//mtid = ((Long) map.get(DHTConstants.TID)).byteValue();
} else if (map.get(DHTConstants.TID) instanceof byte[]) {
byte[] ba = (byte[]) map.get(DHTConstants.TID);
if (ba == null || ba.length < 1) {
return null;
}
mtid = ba;
} else {
return null;
}
return new ErrorMessage(mtid, errorCode,errorMsg);
}
/**
* @param map
* @param srv
* @return
*/
private static MessageBase parseResponse (Map<String, Object> map,RPCServerBase srv) {
Map<String, Object> args = (Map<String, Object>) map.get(DHTConstants.RSP);
if (args == null || map.get(DHTConstants.TID) == null) {
DHT.logDebug("ParseRsp : args || !args.getValue(id) || !dict.getValue(TID)");
return null;
}
byte[] mtid;
if (map.get(DHTConstants.TID) instanceof Long) {
return null;//mtid = ((Long) map.get(DHTConstants.TID)).byteValue();
} else if (map.get(DHTConstants.TID) instanceof byte[]) {
byte[] ba = (byte[]) map.get(DHTConstants.TID);
if (ba == null || ba.length != 2) {
return null;
}
mtid = ba;
} else {
return null;
}
// find the call
RPCCallBase c = srv.findCall(mtid);
if (c == null) {
DHT.logDebug("Cannot find RPC call for response: "
+ new String(mtid));
return null;
}
return parseResponse(map, c.getMessageMethod(), mtid,c);
}
/**
* @param map
* @param msgMethod
* @param mtid
* @return
*/
private static MessageBase parseResponse (Map<String, Object> map,
Method msgMethod, byte[] mtid,RPCCallBase base) {
Map<String, Object> args = (Map<String, Object>) map.get(DHTConstants.RSP);
if (args == null || !args.containsKey("id")) {
return null;
}
byte[] hash = (byte[]) args.get("id");
if (hash == null || hash.length != Key.SHA1_HASH_LENGTH) {
return null;
}
Key id = new Key(hash);
switch (msgMethod) {
case PING:
return new PingResponse(mtid, id);
case ANNOUNCE_PEER:
return new AnnounceResponse(mtid, id);
case FIND_NODE:
if (!args.containsKey("nodes") && !args.containsKey("nodes6"))
return null;
return new FindNodeResponse(mtid, id, (byte[]) args.get("nodes"),(byte[])args.get("nodes6"));
case GET_PEERS:
byte[] token = (byte[]) args.get("token");
byte[] nodes = (byte[]) args.get("nodes");
byte[] nodes6 = (byte[]) args.get("nodes6");
List<byte[]> vals = (List<byte[]>) args.get("values");
List<DBItem> dbl = null;
if (vals != null && vals.size() > 0)
{
dbl = new ArrayList<DBItem>(vals.size());
for (int i = 0; i < vals.size(); i++)
{
// only accept ipv4 or ipv6 for now
if (vals.get(i).length != 6 && vals.get(i).length != 18)
continue;
dbl.add(new DBItem((byte[]) vals.get(i)));
}
}
if (dbl != null || nodes != null || nodes6 != null)
{
GetPeersResponse resp = new GetPeersResponse(mtid, id, nodes, nodes6, token);
resp.setPeerItems(dbl);
return resp;
}
DHT.logDebug("No nodes or values in get_peers response");
return null;
default:
return null;
}
}
/**
* @param map
* @return
*/
private static MessageBase parseRequest (Map<String, Object> map, RPCServerBase srv) {
Object vn = map.get(DHTConstants.REQ);
Map<String, Object> args = (Map<String, Object>) map.get(DHTConstants.ARG);
if (vn == null || args == null) {
return null;
}
if (!args.containsKey("id")) {
return null;
}
if (!map.containsKey(DHTConstants.TID)) {
return null;
}
byte[] hash = (byte[]) args.get("id");
if (hash == null || hash.length != Key.SHA1_HASH_LENGTH) {
return null;
}
Key id = new Key(hash);
byte[] mtid;
if(map.get(DHTConstants.TID) instanceof byte[]) {
mtid = (byte[]) map.get(DHTConstants.TID);
if (mtid == null || mtid.length < 1) {
return null;
}
} else {
return null;
}
MessageBase msg = null;
String str = getStringFromBytes((byte[]) vn);
if (str.equalsIgnoreCase("ping")) {
msg = new PingRequest(id);
} else if (str.equalsIgnoreCase("find_node")) {
if (args.containsKey("target")) {
hash = (byte[]) args.get("target");
if (hash == null || hash.length != Key.SHA1_HASH_LENGTH) {
return null;
}
AbstractLookupRequest req = new FindNodeRequest(id, new Key(hash));
req.setWant4(srv.getDHT().getType() == DHTtype.IPV4_DHT );
req.setWant6(srv.getDHT().getType() == DHTtype.IPV6_DHT );
req.decodeWant((List<byte[]>)args.get("want"));
msg = req;
}
} else if (str.equalsIgnoreCase("get_peers")) {
if (args.containsKey("info_hash")) {
hash = (byte[]) args.get("info_hash");
if (hash == null || hash.length != Key.SHA1_HASH_LENGTH) {
return null;
}
AbstractLookupRequest req = new GetPeersRequest(id, new Key(hash));
List<String> want = (List<String>)args.get("want");
req.setWant4(srv.getDHT().getType() == DHTtype.IPV4_DHT );
req.setWant6(srv.getDHT().getType() == DHTtype.IPV6_DHT );
req.decodeWant((List<byte[]>)args.get("want"));
msg = req;
}
} else if (str.equalsIgnoreCase("announce_peer")) {
if (args.containsKey("info_hash") && args.containsKey("port")
&& args.containsKey("token")) {
hash = (byte[]) args.get("info_hash");
if (hash == null || hash.length != Key.SHA1_HASH_LENGTH) {
return null;
}
Key infoHash = new Key(hash);
byte[] token = (byte[]) args.get("token");
msg = new AnnounceRequest(id, infoHash, ((Long) args
.get("port")).intValue(), token);
}
} else {
DHT.logDebug("Received unknown Message Type: " + str);
}
if (msg != null) {
msg.setMTID(mtid);
}
return msg;
}
private static String getStringFromBytes (byte[] bytes, boolean preserveBytes) {
if (bytes == null) {
return null;
}
try {
return new String(bytes, preserveBytes ? "ISO-8859-1" : "UTF-8");
} catch (Exception e) {
DHT.log(e, LogLevel.Verbose);
return null;
}
}
private static String getStringFromBytes (byte[] bytes) {
return getStringFromBytes(bytes, false);
}
}