private void loadProps(Properties props, File file) throws IOException { InputStream fin = null; try { fin = new BufferedInputStream(new FileInputStream(file), 1); fin.mark(1); int c = fin.read(); fin.reset(); if (c == '#') { // uncompressed if (_log.shouldLog(Log.INFO)) _log.info("Loading uncompressed profile data from " + file.getName()); DataHelper.loadProps(props, fin); } else { // compressed (or corrupt...) if (_log.shouldLog(Log.INFO)) _log.info("Loading compressed profile data from " + file.getName()); DataHelper.loadProps(props, new GZIPInputStream(fin)); } } finally { try { if (fin != null) fin.close(); } catch (IOException e) { } } }
/** new style requests need to fill in the tunnel IDs before hand */ private static void prepare(RouterContext ctx, PooledTunnelCreatorConfig cfg) { int len = cfg.getLength(); boolean isIB = cfg.isInbound(); for (int i = 0; i < len; i++) { if ((!isIB) && (i == 0)) { // outbound gateway (us) doesn't receive on a tunnel id if (len <= 1) { // zero hop, pretend to have a send id long id = ctx.tunnelDispatcher().getNewOBGWID(); cfg.getConfig(i).setSendTunnelId(DataHelper.toLong(4, id)); } } else { long id; if (isIB && len == 1) id = ctx.tunnelDispatcher().getNewIBZeroHopID(); else if (isIB && i == len - 1) id = ctx.tunnelDispatcher().getNewIBEPID(); else id = 1 + ctx.random().nextLong(TunnelId.MAX_ID_VALUE - 1); cfg.getConfig(i).setReceiveTunnelId(DataHelper.toLong(4, id)); } if (i > 0) cfg.getConfig(i - 1).setSendTunnelId(cfg.getConfig(i).getReceiveTunnelId()); byte iv[] = new byte[16]; ctx.random().nextBytes(iv); cfg.getConfig(i).setReplyIV(iv); cfg.getConfig(i).setReplyKey(ctx.keyGenerator().generateSessionKey()); } // This is in BuildExecutor.buildTunnel() now // And it was overwritten by the one in createTunnelBuildMessage() anyway! // cfg.setReplyMessageId(ctx.random().nextLong(I2NPMessage.MAX_ID_VALUE)); }
public void readBytes(InputStream rawConfig) throws DataFormatException, IOException { _destination = Destination.create(rawConfig); _options = DataHelper.readProperties(rawConfig); _creationDate = DataHelper.readDate(rawConfig); _signature = new Signature(); _signature.readBytes(rawConfig); }
/** * As of 0.9.3, expiration MUST be all zeros as it is ignored on readin and the signature will * fail. */ public void writeBytes(OutputStream out) throws DataFormatException, IOException { if (_transportStyle == null) throw new DataFormatException("uninitialized"); DataHelper.writeLong(out, 1, _cost); DataHelper.writeLong(out, 8, _expiration); DataHelper.writeString(out, _transportStyle); DataHelper.writeProperties(out, _options); }
/** * Use the Date header as a backup time source */ public void headerReceived(String url, int attemptNum, String key, String val) { // We do this more than once, because // the first SSL handshake may take a while, and it may take the server // a while to render the index page. if (_gotDate < MAX_DATE_SETS && "date".equalsIgnoreCase(key) && _attemptStarted > 0) { long timeRcvd = System.currentTimeMillis(); long serverTime = RFC822Date.parse822Date(val); if (serverTime > 0) { // add 500ms since it's 1-sec resolution, and add half the RTT long now = serverTime + 500 + ((timeRcvd - _attemptStarted) / 2); long offset = now - _context.clock().now(); if (_context.clock().getUpdatedSuccessfully()) { // 2nd time better than the first if (_gotDate > 0) _context.clock().setNow(now, RouterClock.DEFAULT_STRATUM - 2); else _context.clock().setNow(now, RouterClock.DEFAULT_STRATUM - 1); if (_log.shouldLog(Log.WARN)) _log.warn("Reseed adjusting clock by " + DataHelper.formatDuration(Math.abs(offset))); } else { // No peers or NTP yet, this is probably better than the peer average will be for a while // default stratum - 1, so the peer average is a worse stratum _context.clock().setNow(now, RouterClock.DEFAULT_STRATUM - 1); _log.logAlways(Log.WARN, "NTP failure, Reseed adjusting clock by " + DataHelper.formatDuration(Math.abs(offset))); } _gotDate++; } } }
public void run() { _started = _context.clock().now(); _context.statManager().addRateData("swarm." + _connectionId + ".started", 1, 0); byte data[] = new byte[32 * 1024]; long value = 0; long lastSend = _context.clock().now(); while (!_closed) { byte msg[] = (DataHelper.getASCII( "STREAM SEND ID=" + _connectionId + " SIZE=" + data.length + "\n")); DataHelper.toLong(data, 0, 4, value); try { synchronized (_samOut) { _samOut.write(msg); _samOut.write(data); _samOut.flush(); } } catch (IOException ioe) { _log.error("Error talking to SAM", ioe); return; } _totalSent += data.length; _context.statManager().addRateData("swarm." + _connectionId + ".totalSent", _totalSent, 0); value++; try { Thread.sleep(20); } catch (InterruptedException ie) { } long now = _context.clock().now(); _log.debug("Sending " + value + " on " + _connectionId + " after " + (now - lastSend)); lastSend = now; } }
private void connectWithPeers() { if (_peerDestFiles != null) { for (int i = 0; i < _peerDestFiles.length; i++) { try { FileInputStream fin = new FileInputStream(_peerDestFiles[i]); byte dest[] = new byte[1024]; int read = DataHelper.read(fin, dest); String remDest = new String(dest, 0, read); int con = 0; Flooder flooder = null; synchronized (_remotePeers) { con = _remotePeers.size() + 1; flooder = new Flooder(con, remDest); _remotePeers.put(new Integer(con), flooder); } byte msg[] = (DataHelper.getUTF8("STREAM CONNECT ID=" + con + " DESTINATION=" + remDest + "\n")); synchronized (_samOut) { _samOut.write(msg); _samOut.flush(); } I2PThread flood = new I2PThread(flooder, "Flood " + con); flood.start(); _log.debug("Starting flooder with peer from " + _peerDestFiles[i] + ": " + con); } catch (IOException ioe) { _log.error("Unable to read the peer from " + _peerDestFiles[i]); } } } }
/** write the message body to the output array, starting at the given index */ protected int writeMessageBody(byte out[], int curIndex) throws I2NPMessageException { if ((_tunnelId == null) || ((_msg == null) && (_msgData == null))) { _log.log(Log.CRIT, "failing to write out gateway message"); throw new I2NPMessageException( "Not enough data to write out (id=" + _tunnelId + " data=" + _msg + ")"); } DataHelper.toLong(out, curIndex, 4, _tunnelId.getTunnelId()); curIndex += 4; synchronized (this) { if (_msgData == null) { _msgData = _msg.toByteArray(); _msg = null; } } DataHelper.toLong(out, curIndex, 2, _msgData.length); curIndex += 2; // where is this coming from? if (curIndex + _msgData.length > out.length) { _log.log( Log.ERROR, "output buffer too small idx: " + curIndex + " len: " + _msgData.length + " outlen: " + out.length); throw new I2NPMessageException( "Too much data to write out (id=" + _tunnelId + " data=" + _msg + ")"); } System.arraycopy(_msgData, 0, out, curIndex, _msgData.length); curIndex += _msgData.length; return curIndex; }
/** * As of 0.9.3, expiration MUST be all zeros as it is ignored on readin and the signature will * fail. Restored as of 0.9.12, wait several more releases before using. * * @throws IllegalStateException if was already read in */ public void readBytes(InputStream in) throws DataFormatException, IOException { if (_transportStyle != null) throw new IllegalStateException(); _cost = (short) DataHelper.readLong(in, 1); _expiration = DataHelper.readLong(in, 8); _transportStyle = DataHelper.readString(in); // reduce Object proliferation if (_transportStyle.equals("SSU")) _transportStyle = "SSU"; else if (_transportStyle.equals("NTCP")) _transportStyle = "NTCP"; DataHelper.readProperties(in, _options); }
public void writeBytes(OutputStream out) throws DataFormatException, IOException { if ((_destination == null) || (_options == null) || (_signature == null) || (_creationDate == null)) throw new DataFormatException("Not enough data to create the session config"); _destination.writeBytes(out); DataHelper.writeProperties(out, _options, true); // UTF-8 DataHelper.writeDate(out, _creationDate); _signature.writeBytes(out); }
/** Transport, host, and port only. Never look at cost or other properties. */ @Override public boolean equals(Object object) { if (object == this) return true; if ((object == null) || !(object instanceof RouterAddress)) return false; RouterAddress addr = (RouterAddress) object; return getPort() == addr.getPort() && DataHelper.eq(getHost(), addr.getHost()) && DataHelper.eq(_transportStyle, addr._transportStyle); // DataHelper.eq(_options, addr._options) && // DataHelper.eq(_expiration, addr._expiration); }
@Override public boolean equals(Object object) { if ((object != null) && (object instanceof TunnelGatewayMessage)) { TunnelGatewayMessage msg = (TunnelGatewayMessage) object; return DataHelper.eq(getTunnelId(), msg.getTunnelId()) && DataHelper.eq(_msgData, msg._msgData) && DataHelper.eq(getMessage(), msg.getMessage()); } else { return false; } }
/* FIXME missing hashCode() method FIXME */ @Override public boolean equals(Object object) { if ((object != null) && (object instanceof SessionConfig)) { SessionConfig cfg = (SessionConfig) object; return DataHelper.eq(getSignature(), cfg.getSignature()) && DataHelper.eq(getDestination(), cfg.getDestination()) && DataHelper.eq(getCreationDate(), cfg.getCreationDate()) && DataHelper.eq(getOptions(), cfg.getOptions()); } return false; }
/** @return an unmodifiable view of the Map */ private Map<String, BEValue> createInfoMap() { // If we loaded this metainfo from a file, we have the map, and we must use it // or else we will lose any non-standard keys and corrupt the infohash. if (infoMap != null) return Collections.unmodifiableMap(infoMap); // we should only get here if serving a magnet on a torrent we created if (_log.shouldLog(Log.WARN)) _log.warn("Creating new infomap", new Exception()); // otherwise we must create it Map<String, BEValue> info = new HashMap<String, BEValue>(); info.put("name", new BEValue(DataHelper.getUTF8(name))); if (name_utf8 != null) info.put("name.utf-8", new BEValue(DataHelper.getUTF8(name_utf8))); // BEP 27 if (privateTorrent) // switched to number in 0.9.14 // info.put("private", new BEValue(DataHelper.getUTF8("1"))); info.put("private", new BEValue(Integer.valueOf(1))); info.put("piece length", new BEValue(Integer.valueOf(piece_length))); info.put("pieces", new BEValue(piece_hashes)); if (files == null) info.put("length", new BEValue(Long.valueOf(length))); else { List<BEValue> l = new ArrayList<BEValue>(); for (int i = 0; i < files.size(); i++) { Map<String, BEValue> file = new HashMap<String, BEValue>(); List<String> fi = files.get(i); List<BEValue> befiles = new ArrayList<BEValue>(fi.size()); for (int j = 0; j < fi.size(); j++) { befiles.add(new BEValue(DataHelper.getUTF8(fi.get(j)))); } file.put("path", new BEValue(befiles)); if ((files_utf8 != null) && (files_utf8.size() > i)) { List<String> fiu = files_utf8.get(i); List<BEValue> beufiles = new ArrayList<BEValue>(fiu.size()); for (int j = 0; j < fiu.size(); j++) { beufiles.add(new BEValue(DataHelper.getUTF8(fiu.get(j)))); } file.put("path.utf-8", new BEValue(beufiles)); } file.put("length", new BEValue(lengths.get(i))); l.add(new BEValue(file)); } info.put("files", new BEValue(l)); } // TODO if we add the ability for other keys in the first constructor // if (otherInfo != null) // info.putAll(otherInfo); infoMap = info; return Collections.unmodifiableMap(infoMap); }
/** * Creates a copy of this MetaInfo that shares everything except the announce URL. Drops any * announce-list. Preserves infohash and info map, including any non-standard fields. * * @param announce may be null */ public MetaInfo reannounce(String announce) throws InvalidBEncodingException { Map<String, BEValue> m = new HashMap<String, BEValue>(); if (announce != null) m.put("announce", new BEValue(DataHelper.getUTF8(announce))); Map<String, BEValue> info = createInfoMap(); m.put("info", new BEValue(info)); return new MetaInfo(m); }
public long readACK(int index) { if (!readACKsIncluded()) return -1; int off = readBodyOffset() + 1; // int num = (int)DataHelper.fromLong(_message, off, 1); off++; return DataHelper.fromLong(_message, off + (4 * index), 4); }
public void run() { String header = null; String nick; String dest; String version; try { header = DataHelper.readLine(is).trim(); StringTokenizer tok = new StringTokenizer(header, " "); if (tok.countTokens() != 3) { // This is not a correct message, for sure _log.debug("Error in message format"); return; } version = tok.nextToken(); if (!"3.0".equals(version)) return; nick = tok.nextToken(); dest = tok.nextToken(); byte[] data = new byte[is.available()]; is.read(data); SessionRecord rec = sSessionsHash.get(nick); if (rec != null) { rec.getHandler().session.sendBytes(dest, data); } } catch (Exception e) { } }
/** plugins.config */ public static void storePluginProperties(Properties props) { File cfgFile = new File(I2PAppContext.getGlobalContext().getConfigDir(), CONFIG_FILE); try { DataHelper.storeProps(props, cfgFile); } catch (IOException ioe) { } }
/** * Can't find a published standard for this anywhere. See the libtorrent code. Here we use the * "added" key as a single string of concatenated 32-byte peer hashes. added.f and dropped * unsupported * * @since 0.8.4 */ private static void handlePEX(Peer peer, PeerListener listener, byte[] bs, Log log) { if (log.shouldLog(Log.DEBUG)) log.debug("Got PEX msg from " + peer); try { InputStream is = new ByteArrayInputStream(bs); BDecoder dec = new BDecoder(is); BEValue bev = dec.bdecodeMap(); Map<String, BEValue> map = bev.getMap(); bev = map.get("added"); if (bev == null) return; byte[] ids = bev.getBytes(); if (ids.length < HASH_LENGTH) return; int len = Math.min(ids.length, (I2PSnarkUtil.MAX_CONNECTIONS - 1) * HASH_LENGTH); List<PeerID> peers = new ArrayList<PeerID>(len / HASH_LENGTH); for (int off = 0; off < len; off += HASH_LENGTH) { byte[] hash = new byte[HASH_LENGTH]; System.arraycopy(ids, off, hash, 0, HASH_LENGTH); if (DataHelper.eq(hash, peer.getPeerID().getDestHash())) continue; PeerID pID = new PeerID(hash, listener.getUtil()); peers.add(pID); } // could include ourselves, listener must remove listener.gotPeers(peer, peers); } catch (Exception e) { if (log.shouldLog(Log.INFO)) log.info("PEX msg exception from " + peer, e); // peer.disconnect(false); } }
@Override public String toString() { return super.toString() + " started " + DataHelper.formatDuration((getContext().clock().now() - _startedOn)) + " ago"; }
private int getFragmentBegin(int fragmentNum) throws DataFormatException { int off = readBodyOffset() + 1; if (readACKsIncluded()) { int numACKs = _message[off] & 0xff; off++; off += 4 * numACKs; } if (readACKBitfieldsIncluded()) { int numBitfields = _message[off] & 0xff; off++; PacketACKBitfield bf[] = new PacketACKBitfield[numBitfields]; for (int i = 0; i < numBitfields; i++) { bf[i] = new PacketACKBitfield(off); off += bf[i].getByteLength(); } } if (readExtendedDataIncluded()) { int size = _message[off] & 0xff; off++; off += size; } off++; // # fragments if (fragmentNum > 0) { for (int i = 0; i < fragmentNum; i++) { off += 5; // messageId+info off += ((int) DataHelper.fromLong(_message, off, 2)) & 0x3FFF; off += 2; } } return off; }
/** * Reads the version string from an input stream * * @param inputStream containing at least 56 bytes * @return The version string read, or an empty string if no version string is present. * @since 0.7.12 */ public static String getVersionString(InputStream inputStream) { try { long skipped = inputStream.skip(Signature.SIGNATURE_BYTES); if (skipped != Signature.SIGNATURE_BYTES) return ""; byte[] data = new byte[VERSION_BYTES]; int bytesRead = DataHelper.read(inputStream, data); if (bytesRead != VERSION_BYTES) { return ""; } for (int i = 0; i < VERSION_BYTES; i++) if (data[i] == 0x00) { return new String(data, 0, i, "UTF-8"); } return new String(data, "UTF-8"); } catch (UnsupportedEncodingException uee) { throw new RuntimeException("wtf, your JVM doesnt support utf-8? " + uee.getMessage()); } catch (IOException ioe) { return ""; } finally { if (inputStream != null) try { inputStream.close(); } catch (IOException ioe) { } } }
/** what IP Alice is reachable on */ public int readPort() { int offset = readBodyOffset() + NONCE_LENGTH; int size = _message[offset] & 0xff; offset++; offset += size; // skip the IP return (int) DataHelper.fromLong(_message, offset, 2); }
@Override public Destination lookup(String hostname) { Destination rv = super.lookup(hostname); if (rv != null) { hostname = hostname.toLowerCase(Locale.US); // If it's long, assume it's a key. if (hostname.length() < 516 && hostname.endsWith(".i2p") && !hostname.endsWith(".b32.i2p")) { File f = new File(_context.getRouterDir(), DEFAULT_HOSTS_FILE); if ((f.exists()) && (f.canWrite())) { synchronized (this) { FileOutputStream fos = null; try { fos = new FileOutputStream(f, true); String line = hostname + '=' + rv.toBase64() + System.getProperty("line.separator"); fos.write(DataHelper.getASCII(line)); } catch (IOException ioe) { System.err.println("Error appending: " + ioe); } finally { if (fos != null) try { fos.close(); } catch (IOException cioe) { } } } } } } return rv; }
public void testKeyGen() { RandomSource.getInstance().nextBoolean(); byte src[] = new byte[200]; RandomSource.getInstance().nextBytes(src); I2PAppContext ctx = I2PAppContext.getGlobalContext(); for (int i = 0; i < 10; i++) { Object keys[] = KeyGenerator.getInstance().generatePKIKeypair(); byte ctext[] = ctx.elGamalEngine().encrypt(src, (PublicKey) keys[0]); byte ptext[] = ctx.elGamalEngine().decrypt(ctext, (PrivateKey) keys[1]); assertTrue(DataHelper.eq(ptext, src)); } Object obj[] = KeyGenerator.getInstance().generateSigningKeypair(); SigningPublicKey fake = (SigningPublicKey) obj[0]; for (int i = 0; i < 10; i++) { Object keys[] = KeyGenerator.getInstance().generateSigningKeypair(); Signature sig = DSAEngine.getInstance().sign(src, (SigningPrivateKey) keys[1]); assertTrue(DSAEngine.getInstance().verifySignature(sig, src, (SigningPublicKey) keys[0])); assertFalse(DSAEngine.getInstance().verifySignature(sig, src, fake)); } for (int i = 0; i < 1000; i++) { KeyGenerator.getInstance().generateSessionKey(); } }
/** * Sets DataIn/OutputStreams, does the handshake and returns the id reported by the other side. */ private byte[] handshake(InputStream in, OutputStream out) throws IOException { din = new DataInputStream(in); dout = new DataOutputStream(out); // Handshake write - header dout.write(19); dout.write("BitTorrent protocol".getBytes("UTF-8")); // Handshake write - options long myOptions = OPTION_EXTENSION; // FIXME get util here somehow // if (util.getDHT() != null) // myOptions |= OPTION_I2P_DHT; dout.writeLong(myOptions); // Handshake write - metainfo hash dout.write(infohash); // Handshake write - peer id dout.write(my_id); dout.flush(); if (_log.shouldLog(Log.DEBUG)) _log.debug("Wrote my shared hash and ID to " + toString()); // Handshake read - header byte b = din.readByte(); if (b != 19) throw new IOException("Handshake failure, expected 19, got " + (b & 0xff) + " on " + sock); byte[] bs = new byte[19]; din.readFully(bs); String bittorrentProtocol = new String(bs, "UTF-8"); if (!"BitTorrent protocol".equals(bittorrentProtocol)) throw new IOException( "Handshake failure, expected " + "'Bittorrent protocol', got '" + bittorrentProtocol + "'"); // Handshake read - options options = din.readLong(); // Handshake read - metainfo hash bs = new byte[20]; din.readFully(bs); if (!Arrays.equals(infohash, bs)) throw new IOException("Unexpected MetaInfo hash"); // Handshake read - peer id din.readFully(bs); if (_log.shouldLog(Log.DEBUG)) _log.debug("Read the remote side's hash and peerID fully from " + toString()); if (DataHelper.eq(my_id, bs)) throw new IOException("Connected to myself"); if (options != 0) { // send them something in runConnection() above if (_log.shouldLog(Log.DEBUG)) _log.debug("Peer supports options 0x" + Long.toString(options, 16) + ": " + toString()); } return bs; }
public int readPort() { int offset = readBodyOffset() + 4; offset += _message[offset] & 0xff; offset++; int rv = (int) DataHelper.fromLong(_message, offset, 2); if (_log.shouldLog(Log.DEBUG)) _log.debug("read alice port: " + rv); return rv; }
/** * Unused? see MuxedImpl override * * @param keyUsed unused - no end-to-end crypto * @param tagsSent unused - no end-to-end crypto */ public boolean sendMessage( Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent, long expires) throws I2PSessionException { if (_log.shouldLog(Log.DEBUG)) _log.debug("sending message"); if (isClosed()) throw new I2PSessionException("Already closed"); updateActivity(); // Sadly there is no way to send something completely uncompressed in a backward-compatible way, // so we have to still send it in a gzip format, which adds 23 bytes (2.4% for a 960-byte msg) // (10 byte header + 5 byte block header + 8 byte trailer) // In the future we can add a one-byte magic number != 0x1F to signal an uncompressed msg // (Gzip streams start with 0x1F 0x8B 0x08) // assuming we don't need the CRC-32 that comes with gzip (do we?) // Maybe implement this soon in receiveMessage() below so we are ready // in case we ever make an incompatible network change. // This would save 22 of the 23 bytes and a little CPU. boolean sc = shouldCompress(size); if (sc) payload = DataHelper.compress(payload, offset, size); else payload = DataHelper.compress(payload, offset, size, DataHelper.NO_COMPRESSION); // else throw new IllegalStateException("we need to update sendGuaranteed to support partial // send"); int compressed = payload.length; if (_log.shouldLog(Log.INFO)) { String d = dest.calculateHash().toBase64().substring(0, 4); _log.info( "sending message to: " + d + " compress? " + sc + " sizeIn=" + size + " sizeOut=" + compressed); } _context.statManager().addRateData("i2cp.tx.msgCompressed", compressed, 0); _context.statManager().addRateData("i2cp.tx.msgExpanded", size, 0); if (_noEffort) return sendNoEffort(dest, payload, expires, 0); else return sendBestEffort(dest, payload, keyUsed, tagsSent, expires); }
/** * Creates a new MetaInfo from the given BDecoder. The BDecoder must have a complete dictionary * describing the torrent. */ private MetaInfo(BDecoder be) throws IOException { // Note that evaluation order matters here... this(be.bdecodeMap().getMap()); byte[] origInfohash = be.get_special_map_digest(); // shouldn't ever happen if (!DataHelper.eq(origInfohash, info_hash)) throw new InvalidBEncodingException("Infohash mismatch, please report"); }
public String getMaxFileSize() { int bytes = _context.logManager().getFileSize(); if (bytes <= 0) return "1.00 MB"; // " " comes back in the POST as 0xc2 0xa0 // non-breaking space is U+00A0 which is 0xc2 0xa0 in UTF-8. // we could figure out where the UTF-8 problem is but why bother. return DataHelper.formatSize2(bytes).replace(" ", " ") + 'B'; }