/** * Destroy the socket manager, freeing all the associated resources. This method will block until * all the managed sockets are closed. * * <p>CANNOT be restarted. */ public void destroySocketManager() { if (!_isDestroyed.compareAndSet(false, true)) { // shouldn't happen, log a stack trace to find out why it happened _log.logCloseLoop("I2PSocketManager", getName()); return; } _connectionManager.setAllowIncomingConnections(false); _connectionManager.shutdown(); if (!_subsessions.isEmpty()) { for (I2PSession sess : _subsessions) { removeSubsession(sess); } } // should we destroy the _session too? // yes, since the old lib did (and SAM wants it to, and i dont know why not) if ((_session != null) && (!_session.isClosed())) { try { _session.destroySession(); } catch (I2PSessionException ise) { _log.warn("Unable to destroy the session", ise); } PcapWriter pcap = null; synchronized (_pcapInitLock) { pcap = pcapWriter; } if (pcap != null) pcap.flush(); } }
/** * This updates ALL the session options (not just the bw) and tells the router * * @param limit KBps */ public void setMaxUpBW(int limit) { _maxUpBW = limit; _opts.put(PROP_MAX_BW, Integer.toString(limit * (1024 * 6 / 5))); // add a little for overhead _configured = true; if (_manager != null) { I2PSession sess = _manager.getSession(); if (sess != null) { Properties newProps = new Properties(); newProps.putAll(_opts); sess.updateOptions(newProps); } } }
/** * Remove the subsession * * @since 0.9.21 */ public void removeSubsession(I2PSession session) { _session.removeSubsession(session); boolean removed = _subsessions.remove(session); if (removed) { if (_log.shouldLog(Log.WARN)) _log.warn("Removeed subsession " + session); } else { if (_log.shouldLog(Log.WARN)) _log.warn("Subsession not found to remove " + session); } }
/** * @param name b64 or b32 or host name * @since 0.9.11 */ private Destination lookup(String name) { I2PAppContext ctx = I2PAppContext.getGlobalContext(); boolean b32 = name.length() == 60 && name.toLowerCase(Locale.US).endsWith(".b32.i2p"); if (ctx.isRouterContext() && !b32) { // Local lookup. // Even though we could do b32 outside router ctx here, // we do it below instead so we can use the session, // which we can't do with lookup() Destination dest = ctx.namingService().lookup(name); if (dest != null || ctx.isRouterContext() || name.length() >= 516) return dest; } try { I2PSession sess = sockMgr.getSession(); return sess.lookupDest(name); } catch (I2PSessionException ise) { _log.error("Error looking up " + name, ise); return null; } }
/** * Create a new connected socket. Blocks until the socket is created, unless the connectDelay * option (i2p.streaming.connectDelay) is set and greater than zero. If so this will return * immediately, and the client may quickly write initial data to the socket and this data will be * bundled in the SYN packet. * * @param peer Destination to connect to * @param options I2P socket options to be used for connecting, may be null * @return I2PSocket if successful * @throws NoRouteToHostException if the peer is not found or not reachable * @throws I2PException if there is some other I2P-related problem */ public I2PSocket connect(Destination peer, I2PSocketOptions options) throws I2PException, NoRouteToHostException { if (options == null) options = _defaultOptions; ConnectionOptions opts = null; if (options instanceof ConnectionOptions) opts = new ConnectionOptions((ConnectionOptions) options); else opts = new ConnectionOptions(options); if (_log.shouldLog(Log.INFO)) _log.info( "Connecting to " + peer.calculateHash().toBase64().substring(0, 6) + " with options: " + opts); // pick the subsession here I2PSession session = _session; if (!_subsessions.isEmpty()) { updateUserDsaList(); Hash h = peer.calculateHash(); if (_dsaOnly.contains(h) || (!_userDsaOnly.isEmpty() && _userDsaOnly.contains(h))) { // FIXME just taking the first one for now for (I2PSession sess : _subsessions) { if (sess.getMyDestination().getSigType() == SigType.DSA_SHA1) { session = sess; break; } } } } verifySession(session); // the following blocks unless connect delay > 0 Connection con = _connectionManager.connect(peer, opts, session); if (con == null) throw new TooManyStreamsException("Too many streams, max " + _defaultOptions.getMaxConns()); I2PSocketFull socket = new I2PSocketFull(con, _context); con.setSocket(socket); if (con.getConnectionError() != null) { con.disconnect(false); throw new NoRouteToHostException(con.getConnectionError()); } return socket; }
/** Base64 Hash or Hash.i2p or name.i2p using naming service */ Destination getDestination(String ip) { if (ip == null) return null; if (ip.endsWith(".i2p")) { if (ip.length() < 520) { // key + ".i2p" if (_manager != null && ip.length() == BASE32_HASH_LENGTH + 8 && ip.endsWith(".b32.i2p")) { // Use existing I2PSession for b32 lookups if we have it // This is much more efficient than using the naming service I2PSession sess = _manager.getSession(); if (sess != null) { byte[] b = Base32.decode(ip.substring(0, BASE32_HASH_LENGTH)); if (b != null) { // Hash h = new Hash(b); Hash h = Hash.create(b); if (_log.shouldLog(Log.INFO)) _log.info("Using existing session for lookup of " + ip); try { return sess.lookupDest(h, 15 * 1000); } catch (I2PSessionException ise) { } } } } if (_log.shouldLog(Log.INFO)) _log.info("Using naming service for lookup of " + ip); return _context.namingService().lookup(ip); } if (_log.shouldLog(Log.INFO)) _log.info("Creating Destination for " + ip); try { return new Destination(ip.substring(0, ip.length() - 4)); // sans .i2p } catch (DataFormatException dfe) { return null; } } else { if (_log.shouldLog(Log.INFO)) _log.info("Creating Destination for " + ip); try { return new Destination(ip); } catch (DataFormatException dfe) { return null; } } }
/** * Instruct the client that the given session has received a message with size # of bytes. * * @param session session to notify * @param msgId message number available * @param size size of the message */ public void messageAvailable( I2PSession session, int msgId, long size, int proto, int fromPort, int toPort) { byte data[] = null; try { data = session.receiveMessage(msgId); } catch (I2PSessionException ise) { _context.statManager().addRateData("stream.packetReceiveFailure", 1); if (_log.shouldLog(Log.WARN)) _log.warn("Error receiving the message", ise); return; } if (data == null) return; Packet packet = new Packet(); try { packet.readPacket(data, 0, data.length); packet.setRemotePort(fromPort); packet.setLocalPort(toPort); _manager.getPacketHandler().receivePacket(packet); } catch (IllegalArgumentException iae) { _context.statManager().addRateData("stream.packetReceiveFailure", 1); if (_log.shouldLog(Log.WARN)) _log.warn("Received an invalid packet", iae); } }
/** * @return a new subsession, non-null * @param privateKeyStream null for transient, if non-null must have same encryption keys as * primary session and different signing keys * @param opts subsession options if any, may be null * @since 0.9.21 */ public I2PSession addSubsession(InputStream privateKeyStream, Properties opts) throws I2PSessionException { if (privateKeyStream == null) { // We don't actually need the same pubkey in the dest, just in the LS. // The dest one is unused. But this is how we find the LS keys // to reuse in RequestLeaseSetMessageHandler. ByteArrayOutputStream keyStream = new ByteArrayOutputStream(1024); try { SigType type = getSigType(opts); if (type != SigType.DSA_SHA1) { // hassle, have to set up the padding and cert, see I2PClientImpl throw new I2PSessionException("type " + type + " unsupported"); } PublicKey pub = _session.getMyDestination().getPublicKey(); PrivateKey priv = _session.getDecryptionKey(); SimpleDataStructure[] keys = _context.keyGenerator().generateSigningKeys(type); pub.writeBytes(keyStream); keys[0].writeBytes(keyStream); // signing pub Certificate.NULL_CERT.writeBytes(keyStream); priv.writeBytes(keyStream); keys[1].writeBytes(keyStream); // signing priv } catch (Exception e) { throw new I2PSessionException("Error creating keys", e); } privateKeyStream = new ByteArrayInputStream(keyStream.toByteArray()); } I2PSession rv = _session.addSubsession(privateKeyStream, opts); boolean added = _subsessions.add(rv); if (!added) { // shouldn't happen _session.removeSubsession(rv); throw new I2PSessionException("dup"); } ConnectionOptions defaultOptions = new ConnectionOptions(opts); int protocol = defaultOptions.getEnforceProtocol() ? I2PSession.PROTO_STREAMING : I2PSession.PROTO_ANY; rv.addMuxedSessionListener( _connectionManager.getMessageHandler(), protocol, defaultOptions.getLocalPort()); if (_log.shouldLog(Log.WARN)) _log.warn("Added subsession " + rv); return rv; }
/** * @return dest or null * @since 0.8.4 */ Destination getMyDestination() { if (_manager == null) return null; I2PSession sess = _manager.getSession(); if (sess != null) return sess.getMyDestination(); return null; }
/** @since 0.9.21 */ private void verifySession(I2PSession session) throws I2PException { if (_isDestroyed.get()) throw new I2PException("Session was closed"); if (!session.isClosed()) return; session.connect(); }
/** * @return a list of subsessions, non-null, does not include the primary session * @since 0.9.21 */ public List<I2PSession> getSubsessions() { return _session.getSubsessions(); }
@Override public String getPublicDestinationInfo() { I2PSession ses = manager.getSession(); return ses.getMyDestination().toBase64(); }