Beispiel #1
0
  /**
   * 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;
  }