/** @return socket debug string (for debug printing) */ public String getSocket() { if (state != null) { String r = state.getRequests(); if (r != null) return sock.toString() + "<br>Requests: " + r; } return sock.toString(); }
void disconnect() { PeerState s = state; if (s != null) { // try to save partial piece if (this.deregister) { PeerListener p = s.listener; if (p != null) { List<PartialPiece> pcs = s.returnPartialPieces(); if (!pcs.isEmpty()) p.savePartialPieces(this, pcs); // now covered by savePartialPieces // p.markUnrequested(this); } } state = null; PeerConnectionIn in = s.in; if (in != null) in.disconnect(); PeerConnectionOut out = s.out; if (out != null) out.disconnect(); PeerListener pl = s.listener; if (pl != null) pl.disconnected(this); } I2PSocket csock = sock; sock = null; if ((csock != null) && (!csock.isClosed())) { try { csock.close(); } catch (IOException ioe) { _log.warn("Error disconnecting " + toString(), ioe); } } }
protected void clientConnectionRun(Socket s) { I2PSocket i2ps = null; try { I2PSocketAddress addr = pickDestination(); if (addr == null) throw new UnknownHostException("No valid destination configured"); Destination clientDest = addr.getAddress(); if (clientDest == null) throw new UnknownHostException("Could not resolve " + addr.getHostName()); int port = addr.getPort(); i2ps = createI2PSocket(clientDest, port); i2ps.setReadTimeout(readTimeout); new I2PTunnelRunner(s, i2ps, sockLock, null, mySockets); } catch (Exception ex) { if (_log.shouldLog(Log.INFO)) _log.info("Error connecting", ex); // l.log("Error connecting: " + ex.getMessage()); closeSocket(s); if (i2ps != null) { synchronized (sockLock) { mySockets.remove(sockLock); } } } }
/** * Incoming connection. Creates a unconnected peer from the input and output stream got from the * socket. Note that the complete handshake (which can take some time or block indefinitely) is * done in the calling Thread to get the remote peer id. To completely start the connection call * the connect() method. * * @param metainfo null if in magnet mode * @exception IOException when an error occurred during the handshake. */ public Peer( final I2PSocket sock, InputStream in, OutputStream out, byte[] my_id, byte[] infohash, MetaInfo metainfo) throws IOException { this.my_id = my_id; this.infohash = infohash; this.metainfo = metainfo; this.sock = sock; byte[] id = handshake(in, out); this.peerID = new PeerID(id, sock.getPeerDestination()); _id = ++__id; if (_log.shouldLog(Log.DEBUG)) _log.debug("Creating a new peer " + peerID.toString(), new Exception("creating " + _id)); }
/** @since 0.8.4 */ public Destination getDestination() { if (sock == null) return null; return sock.getPeerDestination(); }
/** * 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(); } }
/** * Act as a SOCKS 5 client to connect to an outproxy * * @return open socket or throws error * @since 0.8.2 */ private I2PSocket outproxyConnect(I2PSOCKSTunnel tun, String proxy) throws IOException, SOCKSException, DataFormatException, I2PException { Properties overrides = new Properties(); overrides.setProperty("option.i2p.streaming.connectDelay", "1000"); I2PSocketOptions proxyOpts = tun.buildOptions(overrides); Destination dest = I2PAppContext.getGlobalContext().namingService().lookup(proxy); if (dest == null) throw new SOCKSException("Outproxy not found"); I2PSocket destSock = tun.createI2PSocket( I2PAppContext.getGlobalContext().namingService().lookup(proxy), proxyOpts); try { DataOutputStream out = new DataOutputStream(destSock.getOutputStream()); boolean authAvail = Boolean.parseBoolean(props.getProperty(I2PTunnelHTTPClientBase.PROP_OUTPROXY_AUTH)); String configUser = null; String configPW = null; if (authAvail) { configUser = props.getProperty(I2PTunnelHTTPClientBase.PROP_OUTPROXY_USER_PREFIX + proxy); configPW = props.getProperty(I2PTunnelHTTPClientBase.PROP_OUTPROXY_PW_PREFIX + proxy); if (configUser == null || configPW == null) { configUser = props.getProperty(I2PTunnelHTTPClientBase.PROP_OUTPROXY_USER); configPW = props.getProperty(I2PTunnelHTTPClientBase.PROP_OUTPROXY_PW); if (configUser == null || configPW == null) authAvail = false; } } // send the init out.writeByte(SOCKS_VERSION_5); if (authAvail) { out.writeByte(2); out.writeByte(Method.NO_AUTH_REQUIRED); out.writeByte(Method.USERNAME_PASSWORD); } else { out.writeByte(1); out.writeByte(Method.NO_AUTH_REQUIRED); } out.flush(); // read init reply DataInputStream in = new DataInputStream(destSock.getInputStream()); // is this right or should we not try to do 5-to-4 conversion? int hisVersion = in.readByte(); if (hisVersion != SOCKS_VERSION_5 /* && addrtype == AddressType.DOMAINNAME */) throw new SOCKSException("SOCKS Outproxy is not Version 5"); // else if (hisVersion != 4) // throw new SOCKSException("Unsupported SOCKS Outproxy Version"); int method = in.readByte(); if (method == Method.NO_AUTH_REQUIRED) { // good } else if (method == Method.USERNAME_PASSWORD) { if (authAvail) { // send the auth out.writeByte(AUTH_VERSION); byte[] user = configUser.getBytes("UTF-8"); byte[] pw = configPW.getBytes("UTF-8"); out.writeByte(user.length); out.write(user); out.writeByte(pw.length); out.write(pw); out.flush(); // read the auth reply if (in.readByte() != AUTH_VERSION) throw new SOCKSException("Bad auth version from outproxy"); if (in.readByte() != AUTH_SUCCESS) throw new SOCKSException("Outproxy authorization failure"); } else { throw new SOCKSException( "Outproxy requires authorization, please configure username/password"); } } else { throw new SOCKSException("Outproxy authorization failure"); } // send the connect command out.writeByte(SOCKS_VERSION_5); out.writeByte(Command.CONNECT); out.writeByte(0); // reserved out.writeByte(addressType); if (addressType == AddressType.IPV4) { out.write(InetAddress.getByName(connHostName).getAddress()); } else if (addressType == AddressType.DOMAINNAME) { byte[] d = connHostName.getBytes("ISO-8859-1"); out.writeByte(d.length); out.write(d); } else { // shouldn't happen throw new SOCKSException("Unknown address type for outproxy?"); } out.writeShort(connPort); out.flush(); // read the connect reply hisVersion = in.readByte(); if (hisVersion != SOCKS_VERSION_5) throw new SOCKSException("Outproxy response is not Version 5"); int reply = in.readByte(); in.readByte(); // reserved int type = in.readByte(); int count = 0; if (type == AddressType.IPV4) { count = 4; } else if (type == AddressType.DOMAINNAME) { count = in.readUnsignedByte(); } else if (type == AddressType.IPV6) { count = 16; } else { throw new SOCKSException("Unsupported address type in outproxy response"); } byte[] addr = new byte[count]; in.readFully(addr); // address in.readUnsignedShort(); // port if (reply != Reply.SUCCEEDED) throw new SOCKSException("Outproxy rejected request, response = " + reply); // throw away the address in the response // todo pass the response through? } catch (IOException e) { try { destSock.close(); } catch (IOException ioe) { } throw e; } catch (SOCKSException e) { try { destSock.close(); } catch (IOException ioe) { } throw e; } // that's it, caller will send confirmation to our client return destSock; }