protected boolean execStreamForwardIncoming(Properties props) {
   try {
     try {
       streamForwardingSocket = true;
       streamSession.startForwardingIncoming(props);
       notifyStreamResult(true, "OK", null);
       return true;
     } catch (SAMException e) {
       _log.debug("Forwarding STREAM connections failed: " + e.getMessage());
       notifyStreamResult(true, "I2P_ERROR", "Forwarding failed : " + e.getMessage());
     }
   } catch (IOException e) {
   }
   return false;
 }
 protected boolean execStreamAccept(Properties props) {
   boolean verbose = props.getProperty("SILENT", "false").equals("false");
   try {
     try {
       notifyStreamResult(verbose, "OK", null);
       streamSession.accept(this, verbose);
       return true;
     } catch (InterruptedIOException e) {
       _log.debug("STREAM ACCEPT failed: " + e.getMessage());
       notifyStreamResult(verbose, "TIMEOUT", e.getMessage());
     } catch (I2PException e) {
       _log.debug("STREAM ACCEPT failed: " + e.getMessage());
       notifyStreamResult(verbose, "I2P_ERROR", e.getMessage());
     } catch (SAMException e) {
       _log.debug("STREAM ACCEPT failed: " + e.getMessage());
       notifyStreamResult(verbose, "ALREADY_ACCEPTING", null);
     }
   } catch (IOException e) {
   }
   return false;
 }
Example #3
0
  /* Parse and execute a SESSION message */
  protected boolean execSessionMessage(String opcode, Properties props) {

    String dest = "BUG!";

    try {
      if (opcode.equals("CREATE")) {
        if ((getRawSession() != null)
            || (getDatagramSession() != null)
            || (getStreamSession() != null)) {
          if (_log.shouldLog(Log.DEBUG))
            _log.debug("Trying to create a session, but one still exists");
          return writeString(
              "SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"Session already exists\"\n");
        }
        if (props.isEmpty()) {
          if (_log.shouldLog(Log.DEBUG))
            _log.debug("No parameters specified in SESSION CREATE message");
          return writeString(
              "SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"No parameters for SESSION CREATE\"\n");
        }

        dest = props.getProperty("DESTINATION");
        if (dest == null) {
          if (_log.shouldLog(Log.DEBUG)) _log.debug("SESSION DESTINATION parameter not specified");
          return writeString(
              "SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"DESTINATION not specified\"\n");
        }
        props.remove("DESTINATION");

        String destKeystream = null;

        if (dest.equals("TRANSIENT")) {
          _log.debug("TRANSIENT destination requested");
          ByteArrayOutputStream priv = new ByteArrayOutputStream(640);
          SAMUtils.genRandomKey(priv, null);

          destKeystream = Base64.encode(priv.toByteArray());
        } else {
          destKeystream = bridge.getKeystream(dest);
          if (destKeystream == null) {
            if (_log.shouldLog(Log.DEBUG))
              _log.debug(
                  "Custom destination specified ["
                      + dest
                      + "] but it isn't known, creating a new one");
            ByteArrayOutputStream baos = new ByteArrayOutputStream(640);
            SAMUtils.genRandomKey(baos, null);
            destKeystream = Base64.encode(baos.toByteArray());
            bridge.addKeystream(dest, destKeystream);
          } else {
            if (_log.shouldLog(Log.DEBUG))
              _log.debug("Custom destination specified [" + dest + "] and it is already known");
          }
        }

        String style = props.getProperty("STYLE");
        if (style == null) {
          if (_log.shouldLog(Log.DEBUG)) _log.debug("SESSION STYLE parameter not specified");
          return writeString(
              "SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"No SESSION STYLE specified\"\n");
        }
        props.remove("STYLE");

        // Unconditionally override what the client may have set
        // (iMule sets BestEffort) as None is more efficient
        // and the client has no way to access delivery notifications
        props.setProperty(I2PClient.PROP_RELIABILITY, I2PClient.PROP_RELIABILITY_NONE);

        if (style.equals("RAW")) {
          rawSession = new SAMRawSession(destKeystream, props, this);
        } else if (style.equals("DATAGRAM")) {
          datagramSession = new SAMDatagramSession(destKeystream, props, this);
        } else if (style.equals("STREAM")) {
          String dir = props.getProperty("DIRECTION");
          if (dir == null) {
            if (_log.shouldLog(Log.DEBUG))
              _log.debug("No DIRECTION parameter in STREAM session, defaulting to BOTH");
            dir = "BOTH";
          } else if (!dir.equals("CREATE") && !dir.equals("RECEIVE") && !dir.equals("BOTH")) {
            if (_log.shouldLog(Log.DEBUG))
              _log.debug("Unknown DIRECTION parameter value: [" + dir + "]");
            return writeString(
                "SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"Unknown DIRECTION parameter\"\n");
          } else {
            props.remove("DIRECTION");
          }

          streamSession = newSAMStreamSession(destKeystream, dir, props);
        } else {
          if (_log.shouldLog(Log.DEBUG))
            _log.debug("Unrecognized SESSION STYLE: \"" + style + "\"");
          return writeString(
              "SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"Unrecognized SESSION STYLE\"\n");
        }
        return writeString("SESSION STATUS RESULT=OK DESTINATION=" + dest + "\n");
      } else {
        if (_log.shouldLog(Log.DEBUG))
          _log.debug("Unrecognized SESSION message opcode: \"" + opcode + "\"");
        return writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"Unrecognized opcode\"\n");
      }
    } catch (DataFormatException e) {
      if (_log.shouldLog(Log.DEBUG)) _log.debug("Invalid destination specified");
      return writeString(
          "SESSION STATUS RESULT=INVALID_KEY DESTINATION="
              + dest
              + " MESSAGE=\""
              + e.getMessage()
              + "\"\n");
    } catch (I2PSessionException e) {
      if (_log.shouldLog(Log.DEBUG)) _log.debug("I2P error when instantiating session", e);
      return writeString(
          "SESSION STATUS RESULT=I2P_ERROR DESTINATION="
              + dest
              + " MESSAGE=\""
              + e.getMessage()
              + "\"\n");
    } catch (SAMException e) {
      _log.error("Unexpected SAM error", e);
      return writeString(
          "SESSION STATUS RESULT=I2P_ERROR DESTINATION="
              + dest
              + " MESSAGE=\""
              + e.getMessage()
              + "\"\n");
    } catch (IOException e) {
      _log.error("Unexpected IOException", e);
      return writeString(
          "SESSION STATUS RESULT=I2P_ERROR DESTINATION="
              + dest
              + " MESSAGE=\""
              + e.getMessage()
              + "\"\n");
    }
  }
  /* Parse and execute a SESSION message */
  @Override
  protected boolean execSessionMessage(String opcode, Properties props) {

    String dest = "BUG!";
    String nick = null;
    boolean ok = false;

    try {
      if (opcode.equals("CREATE")) {
        if ((this.getRawSession() != null)
            || (this.getDatagramSession() != null)
            || (this.getStreamSession() != null)) {
          _log.debug("Trying to create a session, but one still exists");
          return writeString(
              "SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"Session already exists\"\n");
        }
        if (props == null) {
          _log.debug("No parameters specified in SESSION CREATE message");
          return writeString(
              "SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"No parameters for SESSION CREATE\"\n");
        }

        dest = props.getProperty("DESTINATION");
        if (dest == null) {
          _log.debug("SESSION DESTINATION parameter not specified");
          return writeString(
              "SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"DESTINATION not specified\"\n");
        }
        props.remove("DESTINATION");

        if (dest.equals("TRANSIENT")) {
          _log.debug("TRANSIENT destination requested");
          ByteArrayOutputStream priv = new ByteArrayOutputStream(640);
          SAMUtils.genRandomKey(priv, null);

          dest = Base64.encode(priv.toByteArray());
        } else {
          _log.debug("Custom destination specified [" + dest + "]");
        }

        try {
          SAMUtils.checkPrivateDestination(dest);
        } catch (SAMUtils.InvalidDestination e) {
          return writeString("SESSION STATUS RESULT=INVALID_KEY\n");
        }

        nick = props.getProperty("ID");
        if (nick == null) {
          _log.debug("SESSION ID parameter not specified");
          return writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"ID not specified\"\n");
        }
        props.remove("ID");

        String style = props.getProperty("STYLE");
        if (style == null) {
          _log.debug("SESSION STYLE parameter not specified");
          return writeString(
              "SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"No SESSION STYLE specified\"\n");
        }
        props.remove("STYLE");

        // Unconditionally override what the client may have set
        // (iMule sets BestEffort) as None is more efficient
        // and the client has no way to access delivery notifications
        i2cpProps.setProperty(I2PClient.PROP_RELIABILITY, I2PClient.PROP_RELIABILITY_NONE);

        // Record the session in the database sSessionsHash
        Properties allProps = new Properties();
        allProps.putAll(i2cpProps);
        allProps.putAll(props);

        try {
          sSessionsHash.put(nick, new SessionRecord(dest, allProps, this));
        } catch (SessionsDB.ExistingId e) {
          _log.debug("SESSION ID parameter already in use");
          return writeString("SESSION STATUS RESULT=DUPLICATED_ID\n");
        } catch (SessionsDB.ExistingDest e) {
          return writeString("SESSION STATUS RESULT=DUPLICATED_DEST\n");
        }

        // Create the session

        if (style.equals("RAW")) {
          DatagramServer.getInstance(i2cpProps);
          rawSession = newSAMRawSession(nick);
          this.session = rawSession;
        } else if (style.equals("DATAGRAM")) {
          DatagramServer.getInstance(i2cpProps);
          datagramSession = newSAMDatagramSession(nick);
          this.session = datagramSession;
        } else if (style.equals("STREAM")) {
          streamSession = newSAMStreamSession(nick);
          this.session = streamSession;
        } else {
          _log.debug("Unrecognized SESSION STYLE: \"" + style + "\"");
          return writeString(
              "SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"Unrecognized SESSION STYLE\"\n");
        }
        ok = true;
        return writeString("SESSION STATUS RESULT=OK DESTINATION=" + dest + "\n");
      } else {
        _log.debug("Unrecognized SESSION message opcode: \"" + opcode + "\"");
        return writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"Unrecognized opcode\"\n");
      }
    } catch (DataFormatException e) {
      _log.debug("Invalid destination specified");
      return writeString(
          "SESSION STATUS RESULT=INVALID_KEY DESTINATION="
              + dest
              + " MESSAGE=\""
              + e.getMessage()
              + "\"\n");
    } catch (I2PSessionException e) {
      _log.debug("I2P error when instantiating session", e);
      return writeString(
          "SESSION STATUS RESULT=I2P_ERROR DESTINATION="
              + dest
              + " MESSAGE=\""
              + e.getMessage()
              + "\"\n");
    } catch (SAMException e) {
      _log.info("Funny SAM error", e);
      return writeString(
          "SESSION STATUS RESULT=I2P_ERROR DESTINATION="
              + dest
              + " MESSAGE=\""
              + e.getMessage()
              + "\"\n");
    } catch (IOException e) {
      _log.error("Unexpected IOException", e);
      return writeString(
          "SESSION STATUS RESULT=I2P_ERROR DESTINATION="
              + dest
              + " MESSAGE=\""
              + e.getMessage()
              + "\"\n");
    } finally {
      // unregister the session if it has not been created
      if (!ok && nick != null) {
        sSessionsHash.del(nick);
        session = null;
      }
    }
  }
  public void handle() {
    String msg = null;
    String domain = null;
    String opcode = null;
    boolean canContinue = false;
    StringTokenizer tok;
    Properties props;

    this.thread.setName("SAMv3Handler " + _id);
    _log.debug("SAM handling started");

    try {
      InputStream in = getClientSocket().socket().getInputStream();

      while (true) {
        if (shouldStop()) {
          _log.debug("Stop request found");
          break;
        }
        String line = DataHelper.readLine(in);
        if (line == null) {
          _log.debug("Connection closed by client (line read : null)");
          break;
        }
        msg = line.trim();

        if (_log.shouldLog(Log.DEBUG)) {
          _log.debug("New message received: [" + msg + "]");
        }

        if (msg.equals("")) {
          _log.debug("Ignoring newline");
          continue;
        }

        tok = new StringTokenizer(msg, " ");
        if (tok.countTokens() < 2) {
          // This is not a correct message, for sure
          _log.debug("Error in message format");
          break;
        }
        domain = tok.nextToken();
        opcode = tok.nextToken();
        if (_log.shouldLog(Log.DEBUG)) {
          _log.debug("Parsing (domain: \"" + domain + "\"; opcode: \"" + opcode + "\")");
        }
        props = SAMUtils.parseParams(tok);

        if (domain.equals("STREAM")) {
          canContinue = execStreamMessage(opcode, props);
        } else if (domain.equals("SESSION")) {
          if (i2cpProps != null) props.putAll(i2cpProps); // make sure we've got the i2cp settings
          canContinue = execSessionMessage(opcode, props);
        } else if (domain.equals("DEST")) {
          canContinue = execDestMessage(opcode, props);
        } else if (domain.equals("NAMING")) {
          canContinue = execNamingMessage(opcode, props);
        } else if (domain.equals("DATAGRAM")) {
          canContinue = execDatagramMessage(opcode, props);
        } else {
          _log.debug("Unrecognized message domain: \"" + domain + "\"");
          break;
        }

        if (!canContinue) {
          break;
        }
      }
    } catch (IOException e) {
      _log.debug("Caught IOException (" + e.getMessage() + ") for message [" + msg + "]", e);
    } catch (Exception e) {
      _log.error("Unexpected exception for message [" + msg + "]", e);
    } finally {
      _log.debug("Stopping handler");

      if (!this.stolenSocket) {
        try {
          closeClientSocket();
        } catch (IOException e) {
          _log.error("Error closing socket: " + e.getMessage());
        }
      }
      if (streamForwardingSocket) {
        if (this.getStreamSession() != null) {
          try {
            this.streamSession.stopForwardingIncoming();
          } catch (SAMException e) {
            _log.error("Error while stopping forwarding connections: " + e.getMessage());
          } catch (InterruptedIOException e) {
            _log.error("Interrupted while stopping forwarding connections: " + e.getMessage());
          }
        }
      }

      die();
    }
  }