/*
* Created on Jan 31, 2007
* Created by Paul Gardner
* Copyright (C) 2007 Aelitis, All Rights Reserved.
*
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* AELITIS, SAS au capital de 63.529,40 euros
* 8 Allee Lenotre, La Grille Royale, 78600 Le Mesnil le Roi, France.
*
*/
/**
*
* This class handles communication with the CDP server.
* Original code from
* @author Matthias Scheler <mscheler@cachelogic.com>
*
*/
package com.aelitis.azureus.core.peer.cache.cachelogic;
import java.io.File;
import java.net.*;
import org.gudy.azureus2.core3.torrent.TOTorrent;
import org.gudy.azureus2.core3.torrent.TOTorrentException;
import org.gudy.azureus2.core3.torrent.TOTorrentFactory;
import org.gudy.azureus2.core3.util.Constants;
import org.gudy.azureus2.core3.util.Debug;
import org.gudy.azureus2.core3.util.SHA1Hasher;
import com.aelitis.azureus.core.peer.cache.CacheDiscoverer;
import com.aelitis.azureus.core.peer.cache.CacheDiscovery;
import com.aelitis.azureus.core.peer.cache.CachePeer;
import com.aelitis.azureus.core.peermanager.utils.PeerClassifier;
public class
CLCacheDiscovery
implements CacheDiscoverer
{
public static final String CDPDomainName = ".find-cache.com";
public static final String CDPServerName = "cls" + CDPDomainName;
public static final int CDPPort = 19523;
public static final int CDPVersion = 0;
public static final int CDPTimeout = 5000;
static CDPResponse Response;
/* Convert an array of bytes into a its hexadecimal representation. */
private String
byteArrayToHex(byte[] Bytes, int Max) {
int Length, Index, Value;
String Result;
Length = Bytes.length;
if (Length > Max)
Length = Max;
Result = new String();
for (Index = 0; Index < Length; Index++) {
Value = Bytes[Index] & 0xff;
if (Value < 16)
Result += "0";
Result += Integer.toHexString(Value);
}
return Result;
}
/* Find out the farm name via CDP. */
private String
lookupFarm()
{
if (Response != null) {
if (Response.isStillValid()){
return Response.getFarmID();
}
Response = null;
}
try {
InetAddress CDPServer;
DatagramSocket Socket;
CDPQuery Query;
byte[] Buffer;
DatagramPacket Packet;
/* Get the IP address of the CDP servers. */
CDPServer = InetAddress.getByName(CDPServerName);
/* Create a UDP socket for the CDP query. */
Socket = new DatagramSocket();
Socket.setSoTimeout(CDPTimeout);
/* Build the CDP query. */
Query = new CDPQuery(Constants.AZUREUS_NAME + " " + Constants.AZUREUS_VERSION);
Buffer = Query.getBytes();
Packet = new DatagramPacket(Buffer, Buffer.length, CDPServer, CDPPort);
/* Send the query to the CDP server. */
Socket.send(Packet);
/* Receive the CDP response. */
Buffer = new byte[CDPResponse.MaxSize];
Packet.setData(Buffer);
Socket.receive(Packet);
if (Packet.getAddress() != CDPServer || Packet.getPort() != CDPPort)
throw(new Exception("CDP server address mismatch on response"));
/* Parse the CDP response. */
Response = new CDPResponse(Packet.getData());
/* Return the farmID from the CDP response. */
return Response.getFarmID();
} catch (Throwable Excpt) {
if ( Excpt instanceof UnknownHostException ){
}else{
Excpt.printStackTrace();
}
return "default";
}
}
/* Calculate publisher string for DNS query from announce URL. */
private String
hashAnnounceURL(
URL announce_url )
{
/* Calculate SHA1 hash of the hostname. */
byte[] Digest = new SHA1Hasher().calculateHash( announce_url.getHost().getBytes());
/*
* Return first 32 characters of the hexadecimal representation of the
* SHA1 hash.
*/
return byteArrayToHex(Digest, 16);
}
/* Find a cache for a given announce URL and BitTorrent hash. */
public InetAddress[]
findCache(
URL announce_url,
String hex_hash )
{
String Hostname;
InetAddress[] Caches;
/*
* Build the hostname for the DNS query:
* bt-<short hash>.bt-<announce hash>-<farm>.find-cache.com
*
* short hash: first four hexadecimal digits of the BitTorrent hash
* announce hash: see hashAnnounceURL()
* farm: farm name returned by CDP query.
*/
Hostname = "bt-" + hex_hash.substring(0, 4) +
".bt-" + hashAnnounceURL(announce_url) +
"-" + lookupFarm() + CDPDomainName;
// System.out.println("findCache(): " + announce_url + " " + hex_hash + " --> " + Hostname);
try {
Caches = InetAddress.getAllByName(Hostname);
} catch (UnknownHostException NoCache) {
Caches = new InetAddress[0];
}
return Caches;
}
/* Find a cache for a given announce URL and BitTorrent hash. */
public InetAddress[]
findCache(
URL announce_url,
byte[] hash )
{
return findCache(announce_url, byteArrayToHex(hash, 4));
}
public CachePeer[]
lookup(
TOTorrent torrent )
{
try{
InetAddress[] addresses = findCache( torrent.getAnnounceURL(), torrent.getHash());
CachePeer[] result = new CachePeer[addresses.length];
for (int i=0;i<addresses.length;i++){
result[i] = new CacheDiscovery.CachePeerImpl( CachePeer.PT_CACHE_LOGIC, addresses[i], 6881 );
}
return( result );
}catch( TOTorrentException e ){
Debug.printStackTrace( e );
return( new CachePeer[0] );
}
}
public CachePeer
lookup(
byte[] peer_id,
InetAddress ip,
int port )
{
if ( PeerClassifier.getClientDescription( peer_id ).startsWith( PeerClassifier.CACHE_LOGIC )){
return( new CacheDiscovery.CachePeerImpl( CachePeer.PT_CACHE_LOGIC, ip, port ));
}
return( null );
}
class
CDPQuery
{
private String Client;
/* Construct a query for a given client identifier. */
public CDPQuery(String _Client)
{
Client = _Client;
}
/* Return the binary representation. */
byte[] getBytes()
{
String Temp;
byte[] Bytes;
Temp = "@@@" + Client;
Bytes = Temp.getBytes();
Bytes[0] = CDPVersion; // Version
Bytes[1] = 0; // Flags
Bytes[2] = (byte)Client.length(); // Length of the client identifier
return Bytes;
}
}
/**
*
* This class parses a CDP response returned by the CDP server.
*
* @author Matthias Scheler <mscheler@cachelogic.com>
*
*/
class
CDPResponse
{
public static final int MinSize = 7;
public static final int MaxSize = 262;
/* The CDP farm ID. */
String farmID;
/* This CDP response is valid until this point of time. */
long validUntil;
/* Create a response object from a given binary encoded CDP message. */
public CDPResponse(byte[] Bytes) throws Exception
{
int Index;
long Timeout;
if (Bytes.length < MinSize || MinSize + Bytes[6] > Bytes.length)
throw new Exception("CDP response too short");
if (Bytes[0] != CDPVersion)
throw new Exception("Unsupported CDP version");
farmID = new String();
for (Index = 0; Index < Bytes[6]; Index++)
farmID += (char)Bytes[MinSize + Index];
Timeout = 0;
for (Index = 2; Index < 6; Index++)
Timeout = (Timeout << 8) + ((int)Bytes[Index] & 0xff);
validUntil = System.currentTimeMillis() + Timeout * 1000;
}
/* Return the farm ID. */
public String getFarmID()
{
return farmID;
}
/* Is the information in this CDP response still valid? */
public boolean isStillValid()
{
return (System.currentTimeMillis() < validUntil);
}
}
public static void
main(
String[] args )
{
try{
TOTorrent torrent = TOTorrentFactory.deserialiseFromBEncodedFile( new File( "C:\\temp\\test.torrent" ));
CachePeer[] peers = new CLCacheDiscovery().lookup( torrent );
System.out.println( "peers=" + peers.length );
for (int i=0;i<peers.length;i++){
System.out.println( " cache: " + peers[i].getAddress() + ":" + peers[i].getPort() );
}
}catch( Throwable e ){
e.printStackTrace();
}
}
}