import GivenTools.*; import java.io.DataInputStream; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.*; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Timer; import java.util.logging.Logger; /** * The Class Tracker handles announcing our status to the swarm of * peers and getting updated lists of peers that are in the swarm. * * @author Deepak, Mike, Josh */ public class Tracker { /** The Constant log. */ private static final Logger log = Log2.getLogger(Tracker.class); /** The Constant KEY_FAILURE. */ public static final ByteBuffer KEY_FAILURE = ByteBuffer.wrap(new byte[] { 'f', 'a', 'i', 'l', 'u', 'r', 'e', ' ', 'r', 'e', 'a', 's', 'o', 'n' }); /** The Constant KEY_PEERS. */ public static final ByteBuffer KEY_PEERS = ByteBuffer.wrap(new byte[] { 'p', 'e', 'e', 'r', 's' }); /** The Constant KEY_IP. */ public static final ByteBuffer KEY_IP = ByteBuffer.wrap(new byte[] { 'i', 'p' }); /** The Constant KEY_PORT. */ public static final ByteBuffer KEY_PORT = ByteBuffer.wrap(new byte[] { 'p', 'o', 'r', 't' }); /** The Constant KEY_PEERID. */ public static final ByteBuffer KEY_PEERID = ByteBuffer.wrap(new byte[] { 'p', 'e', 'e', 'r', ' ', 'i', 'd' }); /** The Constant KEY_INTERVAL. */ public static final ByteBuffer KEY_INTERVAL = ByteBuffer.wrap(new byte[] { 'i', 'n', 't', 'e', 'r', 'v', 'a', 'l' }); /** The Constant requestSize. */ public static final int requestSize = 16000; /** The infohash. */ public byte[] infohash; /** The peerid. */ public byte[] peerid; /** The uploaded. */ public static int uploaded; /** The downloaded. */ public static int downloaded; /** The left. */ public int left; /** The port. */ private int port; /** The announce. */ private URL announce; /** The event. */ private String event; /** The request string. */ private URL requestString; /** The is running. */ public boolean isRunning = true; /** The interval. */ public int interval = -1; /** The manager. */ public Manager manager; /** The timer. */ public Timer timer; /** The tracker update. */ public TrackerUpdate trackerUpdate; /** * Instantiates a new tracker. * * @param torrentData the torrent data * @param peerId the peer id * @param port the port * @param manager the manager */ Tracker(TorrentInfo torrentData, final byte[] peerId, final int port, Manager manager) { this.infohash = torrentData.info_hash.array(); this.peerid = peerId; this.left = torrentData.file_length; this.port = port; this.event = null; this.announce = torrentData.announce_url; this.requestString = createURL(torrentData.announce_url); this.manager = manager; } // TODO: Correct this hook for shutdown/stop gotta figure this out /* * Runtime.getRuntime().addShutdownHook(new Thread() { public void run() { * Tracker.this.update("stopped", manager); } * * }); */ /** * Update. * * @param event the event * @param manager the manager * @return the array list */ @SuppressWarnings("unchecked") public ArrayList<Peer> update(String event, Manager manager) { this.event = event; if(this.event.equals("started")){ try { manager.getUpload(); } catch (IOException e) { this.downloaded = 0; this.uploaded = 0; } } else { manager.setDownloadUpload(this.downloaded, this.uploaded); } this.requestString = createURL(this.announce); HashMap<ByteBuffer, Object> response = null; try { byte[] trackerResponse = sendGETRecieveResponse(); if (trackerResponse == null) { log.severe("Error communicating with tracker"); return null; } response = (HashMap<ByteBuffer, Object>) Bencoder2.decode(trackerResponse); } catch (BencodingException e1) { log.severe("Error decoding tracker response"); return null; } if (response.containsKey(KEY_FAILURE)) { log.severe("Failure from the tracker."); return null; } ArrayList<Peer> peers = new ArrayList<Peer>(); this.interval = (Integer)response.get(KEY_INTERVAL); List<Map<ByteBuffer, Object>> peersList = (List<Map<ByteBuffer, Object>>) response .get(KEY_PEERS); if (peersList == null) { log.severe("List of peers given by tracker is null"); return null; } for (Map<ByteBuffer, Object> rawPeer : peersList) { int peerPort = ((Integer) rawPeer.get(KEY_PORT)).intValue(); byte[] peerId = ((ByteBuffer) rawPeer.get(KEY_PEERID)).array(); String ip = null; try { ip = new String(((ByteBuffer) rawPeer.get(KEY_IP)).array(), "ASCII"); } catch (UnsupportedEncodingException e) { log.severe("Unable to parse encoding"); continue; } peers.add(new Peer(peerId, peerPort, ip, manager)); } if(this.interval < 0){ this.interval = 120000; } log.fine("Converted: " + peers); return peers; } /** * Send get recieve response. * * @return the byte[] */ public byte[] sendGETRecieveResponse() { try { HttpURLConnection httpConnection = (HttpURLConnection)this.requestString.openConnection(); DataInputStream dataInputStream = new DataInputStream(httpConnection.getInputStream()); int dataSize = httpConnection.getContentLength(); byte[] retArray = new byte[dataSize]; dataInputStream.readFully(retArray); dataInputStream.close(); return retArray; } catch (IOException e) { log.severe("Error communicating with tracker"); return null; } } /** * Creates the url. * * @param announceURL the announce url * @return the url */ public URL createURL(URL announceURL) { String newURL = announceURL.toString(); newURL += "?info_hash=" + Utils.toHexString(this.infohash) + "&peer_id=" + Utils.toHexString(this.peerid) + "&port=" + this.port + "&uploaded=" + this.uploaded + "&downloaded=" + this.downloaded + "&left=" + this.left; if (this.event != null) newURL += "&event=" + this.event; try { return new URL(newURL); } catch (MalformedURLException e) { log.severe("Unable to create URL"); return null; } } }