private void x_startTorrent() { boolean ok = _util.connect(); if (!ok) fatal("Unable to connect to I2P"); if (coordinator == null) { I2PServerSocket serversocket = _util.getServerSocket(); if (serversocket == null) fatal("Unable to listen for I2P connections"); else { Destination d = serversocket.getManager().getSession().getMyDestination(); if (_log.shouldLog(Log.INFO)) _log.info( "Listening on I2P destination " + d.toBase64() + " / " + d.calculateHash().toBase64()); } if (_log.shouldLog(Log.INFO)) _log.info("Starting PeerCoordinator, ConnectionAcceptor, and TrackerClient"); activity = "Collecting pieces"; coordinator = new PeerCoordinator(_util, id, infoHash, meta, storage, this, this); coordinator.setUploaded(savedUploaded); if (_peerCoordinatorSet != null) { // multitorrent _peerCoordinatorSet.add(coordinator); } else { // single torrent acceptor = new ConnectionAcceptor(_util, new PeerAcceptor(coordinator)); } // TODO pass saved closest DHT nodes to the tracker? or direct to the coordinator? trackerclient = new TrackerClient(_util, meta, additionalTrackerURL, coordinator, this); } // ensure acceptor is running when in multitorrent if (_peerCoordinatorSet != null && acceptor != null) { acceptor.startAccepting(); } stopped = false; if (coordinator.halted()) { coordinator.restart(); if (_peerCoordinatorSet != null) _peerCoordinatorSet.add(coordinator); } if (!trackerclient.started()) { trackerclient.start(); } else if (trackerclient.halted()) { if (storage != null) { try { storage.reopen(); } catch (IOException ioe) { try { storage.close(); } catch (IOException ioee) { ioee.printStackTrace(); } fatal("Could not reopen storage", ioe); } } trackerclient.start(); } else { if (_log.shouldLog(Log.INFO)) _log.info("NOT starting TrackerClient???"); } }
/** * Called when the PeerCoordinator got the MetaInfo via magnet. CoordinatorListener. Create the * storage, tell SnarkManager, and give the storage back to the coordinator. * * @throws RuntimeException via fatal() * @since 0.8.4 */ public void gotMetaInfo(PeerCoordinator coordinator, MetaInfo metainfo) { try { String base = Storage.filterName(metainfo.getName()); File baseFile; if (_util.getFilesPublic()) baseFile = new File(rootDataDir, base); else baseFile = new SecureFile(rootDataDir, base); // The following two may throw IOE... storage = new Storage(_util, baseFile, metainfo, this, false); storage.check(); // ... so don't set meta until here meta = metainfo; if (completeListener != null) { String newName = completeListener.gotMetaInfo(this); if (newName != null) torrent = newName; // else some horrible problem } coordinator.setStorage(storage); } catch (IOException ioe) { if (storage != null) { try { storage.close(); } catch (IOException ioee) { } } fatal("Could not check or create storage", ioe); } }
/** * multitorrent, magnet * * @param torrent a fake name for now (not a file name) * @param ih 20-byte info hash * @param trackerURL may be null * @throws RuntimeException via fatal() * @since 0.8.4 */ public Snark( I2PSnarkUtil util, String torrent, byte[] ih, String trackerURL, CompleteListener complistener, PeerCoordinatorSet peerCoordinatorSet, ConnectionAcceptor connectionAcceptor, boolean start, String rootDir) { completeListener = complistener; _util = util; _log = util.getContext().logManager().getLog(Snark.class); _peerCoordinatorSet = peerCoordinatorSet; acceptor = connectionAcceptor; this.torrent = torrent; this.infoHash = ih; this.additionalTrackerURL = trackerURL; this.rootDataDir = rootDir != null ? new File(rootDir) : null; // null only for FetchAndAdd extension savedUploaded = 0; stopped = true; id = generateID(); // All we have is an infoHash // meta remains null // storage remains null if (start) startTorrent(); }
public boolean overUpBWLimit() { if (_peerCoordinatorSet == null) return false; long total = 0; for (PeerCoordinator c : _peerCoordinatorSet) { if (!c.halted()) total += c.getCurrentUploadRate(); } long limit = 1024l * _util.getMaxUpBW(); if (_log.shouldLog(Log.INFO)) _log.info("Total up bw: " + total + " Limit: " + limit); return total > limit; }
public boolean overUploadLimit(int uploaders) { if (_peerCoordinatorSet == null || uploaders <= 0) return false; int totalUploaders = 0; for (PeerCoordinator c : _peerCoordinatorSet) { if (!c.halted()) totalUploaders += c.uploaders; } int limit = _util.getMaxUploaders(); if (_log.shouldLog(Log.DEBUG)) _log.debug("Total uploaders: " + totalUploaders + " Limit: " + limit); return totalUploaders > limit; }
@Override public String toString() { return "MetaInfo[info_hash='" + I2PSnarkUtil.toHex(info_hash) + "', announce='" + announce + "', name='" + name + "', files=" + files + ", #pieces='" + piece_hashes.length / 20 + "', piece_length='" + piece_length + "', length='" + length + "']"; }
private byte[] calculateInfoHash() { Map<String, BEValue> info = createInfoMap(); if (_log.shouldLog(Log.DEBUG)) { StringBuilder buf = new StringBuilder(128); buf.append("info: "); for (Map.Entry<String, BEValue> entry : info.entrySet()) { String key = entry.getKey(); Object val = entry.getValue(); buf.append(key).append('='); buf.append(val.toString()); } _log.debug(buf.toString()); } byte[] infoBytes = BEncoder.bencode(info); // _log.debug("info bencoded: [" + Base64.encode(infoBytes, true) + "]"); MessageDigest digest = SHA1.getInstance(); byte hash[] = digest.digest(infoBytes); if (_log.shouldLog(Log.DEBUG)) _log.debug("info hash: " + I2PSnarkUtil.toHex(hash)); return hash; }
/** @since 0.8.5 */ public static void main(String[] args) { if (args.length <= 0) { System.err.println("Usage: MetaInfo files..."); return; } for (int i = 0; i < args.length; i++) { InputStream in = null; try { in = new FileInputStream(args[i]); MetaInfo meta = new MetaInfo(in); System.out.println(args[i] + " InfoHash: " + I2PSnarkUtil.toHex(meta.getInfoHash())); } catch (IOException ioe) { System.err.println("Error in file " + args[i] + ": " + ioe); } finally { try { if (in != null) in.close(); } catch (IOException ioe) { } } } }
/** * Stop contacting the tracker and talking with peers * * @param fast if true, limit the life of the unannounce threads * @since 0.9.1 */ public synchronized void stopTorrent(boolean fast) { stopped = true; TrackerClient tc = trackerclient; if (tc != null) tc.halt(fast); PeerCoordinator pc = coordinator; if (pc != null) pc.halt(); Storage st = storage; if (st != null) { // TODO: Cache the config-in-mem to compare vs config-on-disk // (needed for auto-save to not double-save in some cases) // boolean changed = storage.isChanged() || getUploaded() != savedUploaded; boolean changed = true; try { storage.close(); } catch (IOException ioe) { System.out.println("Error closing " + torrent); ioe.printStackTrace(); } if (changed && completeListener != null) completeListener.updateStatus(this); } if (pc != null && _peerCoordinatorSet != null) _peerCoordinatorSet.remove(pc); if (_peerCoordinatorSet == null) _util.disconnect(); }
/** * Runs the connection to the other peer. This method does not return until the connection is * terminated. * * <p>When the connection is correctly started the connected() method of the given PeerListener is * called. If the connection ends or the connection could not be setup correctly the * disconnected() method is called. * * <p>If the given BitField is non-null it is send to the peer as first message. */ public void runConnection( I2PSnarkUtil util, PeerListener listener, BitField bitfield, MagnetState mState) { if (state != null) throw new IllegalStateException("Peer already started"); if (_log.shouldLog(Log.DEBUG)) _log.debug("Running connection to " + peerID.toString(), new Exception("connecting")); try { // Do we need to handshake? if (din == null) { // Outgoing connection sock = util.connect(peerID); if (_log.shouldLog(Log.DEBUG)) _log.debug("Connected to " + peerID + ": " + sock); if ((sock == null) || (sock.isClosed())) { throw new IOException("Unable to reach " + peerID); } InputStream in = sock.getInputStream(); OutputStream out = sock.getOutputStream(); byte[] id = handshake(in, out); byte[] expected_id = peerID.getID(); if (expected_id == null) { peerID.setID(id); } else if (Arrays.equals(expected_id, id)) { if (_log.shouldLog(Log.DEBUG)) _log.debug("Handshake got matching IDs with " + toString()); } else { throw new IOException( "Unexpected peerID '" + PeerID.idencode(id) + "' expected '" + PeerID.idencode(expected_id) + "'"); } } else { // Incoming connection if (_log.shouldLog(Log.DEBUG)) _log.debug("Already have din [" + sock + "] with " + toString()); } // bad idea? if (metainfo == null && (options & OPTION_EXTENSION) == 0) { if (_log.shouldLog(Log.INFO)) _log.info("Peer does not support extensions and we need metainfo, dropping"); throw new IOException("Peer does not support extensions and we need metainfo, dropping"); } PeerConnectionIn in = new PeerConnectionIn(this, din); PeerConnectionOut out = new PeerConnectionOut(this, dout); PeerState s = new PeerState(this, listener, metainfo, in, out); if ((options & OPTION_EXTENSION) != 0) { if (_log.shouldLog(Log.DEBUG)) _log.debug("Peer supports extensions, sending reply message"); int metasize = metainfo != null ? metainfo.getInfoBytes().length : -1; out.sendExtension(0, ExtensionHandler.getHandshake(metasize)); } if ((options & OPTION_I2P_DHT) != 0 && util.getDHT() != null) { if (_log.shouldLog(Log.DEBUG)) _log.debug("Peer supports DHT, sending PORT message"); int port = util.getDHT().getPort(); out.sendPort(port); } // Send our bitmap if (bitfield != null) s.out.sendBitfield(bitfield); // We are up and running! state = s; magnetState = mState; listener.connected(this); if (_log.shouldLog(Log.DEBUG)) _log.debug("Start running the reader with " + toString()); // Use this thread for running the incomming connection. // The outgoing connection creates its own Thread. out.startup(); Thread.currentThread().setName("Snark reader from " + peerID); s.in.run(); } catch (IOException eofe) { // Ignore, probably just the other side closing the connection. // Or refusing the connection, timing out, etc. if (_log.shouldLog(Log.DEBUG)) _log.debug(this.toString(), eofe); } catch (Throwable t) { _log.error(this + ": " + t.getMessage(), t); if (t instanceof OutOfMemoryError) throw (OutOfMemoryError) t; } finally { if (deregister) listener.disconnected(this); disconnect(); } }
/** * multitorrent * * @param baseFile if null, use rootDir/torrentName; if non-null, use it instead * @throws RuntimeException via fatal() * @since 0.9.11 */ public Snark( I2PSnarkUtil util, String torrent, String ip, int user_port, StorageListener slistener, CoordinatorListener clistener, CompleteListener complistener, PeerCoordinatorSet peerCoordinatorSet, ConnectionAcceptor connectionAcceptor, boolean start, String rootDir, File baseFile) { if (slistener == null) slistener = this; completeListener = complistener; _util = util; _log = util.getContext().logManager().getLog(Snark.class); _peerCoordinatorSet = peerCoordinatorSet; acceptor = connectionAcceptor; this.torrent = torrent; this.rootDataDir = new File(rootDir); stopped = true; activity = "Network setup"; id = generateID(); if (_log.shouldLog(Log.INFO)) _log.info("My peer id: " + PeerID.idencode(id)); /* * Don't start a tunnel if the torrent isn't going to be started. * If we are starting, * startTorrent() will force a connect. * boolean ok = util.connect(); if (!ok) fatal("Unable to connect to I2P"); I2PServerSocket serversocket = util.getServerSocket(); if (serversocket == null) fatal("Unable to listen for I2P connections"); else { Destination d = serversocket.getManager().getSession().getMyDestination(); debug("Listening on I2P destination " + d.toBase64() + " / " + d.calculateHash().toBase64(), NOTICE); } */ // Figure out what the torrent argument represents. File f = null; InputStream in = null; byte[] x_infoHash = null; try { f = new File(torrent); if (f.exists()) in = new FileInputStream(f); else { /** * ** No, we don't ever fetch a torrent file this way and we don't want to block in the * constructor activity = "Getting torrent"; File torrentFile = _util.get(torrent, 3); if * (torrentFile == null) { fatal("Unable to fetch " + torrent); if (false) return; // never * reached - fatal(..) throws } else { torrentFile.deleteOnExit(); in = new * FileInputStream(torrentFile); } *** */ throw new IOException("not found"); } meta = new MetaInfo(in); x_infoHash = meta.getInfoHash(); } catch (IOException ioe) { // OK, so it wasn't a torrent metainfo file. if (f != null && f.exists()) if (ip == null) fatal( "'" + torrent + "' exists," + " but is not a valid torrent metainfo file." + System.getProperty("line.separator"), ioe); else fatal("I2PSnark does not support creating and tracking a torrent at the moment"); /* { // Try to create a new metainfo file debug ("Trying to create metainfo torrent for '" + torrent + "'", NOTICE); try { activity = "Creating torrent"; storage = new Storage (f, "http://" + ip + ":" + port + "/announce", slistener); storage.create(); meta = storage.getMetaInfo(); } catch (IOException ioe2) { fatal("Could not create torrent for '" + torrent + "'", ioe2); } } */ else fatal("Cannot open '" + torrent + "'", ioe); } catch (OutOfMemoryError oom) { fatal("ERROR - Out of memory, cannot create torrent " + torrent + ": " + oom.getMessage()); } finally { if (in != null) try { in.close(); } catch (IOException ioe) { } } infoHash = x_infoHash; // final if (_log.shouldLog(Log.INFO)) _log.info(meta.toString()); // When the metainfo torrent was created from an existing file/dir // it already exists. if (storage == null) { try { activity = "Checking storage"; boolean shouldPreserve = completeListener != null && completeListener.getSavedPreserveNamesSetting(this); if (baseFile == null) { String base = meta.getName(); if (!shouldPreserve) base = Storage.filterName(base); if (_util.getFilesPublic()) baseFile = new File(rootDataDir, base); else baseFile = new SecureFile(rootDataDir, base); } storage = new Storage(_util, baseFile, meta, slistener, shouldPreserve); if (completeListener != null) { storage.check( completeListener.getSavedTorrentTime(this), completeListener.getSavedTorrentBitField(this)); } else { storage.check(); } // have to figure out when to reopen // if (!start) // storage.close(); } catch (IOException ioe) { try { storage.close(); } catch (IOException ioee) { ioee.printStackTrace(); } fatal("Could not check or create storage", ioe); } } /* * see comment above * activity = "Collecting pieces"; coordinator = new PeerCoordinator(id, meta, storage, clistener, this); PeerCoordinatorSet set = PeerCoordinatorSet.instance(); set.add(coordinator); ConnectionAcceptor acceptor = ConnectionAcceptor.instance(); acceptor.startAccepting(set, serversocket); trackerclient = new TrackerClient(meta, coordinator); */ savedUploaded = (completeListener != null) ? completeListener.getSavedUploaded(this) : 0; if (start) startTorrent(); }
public boolean overUpBWLimit(long total) { long limit = 1024l * _util.getMaxUpBW(); return total > limit; }