Ejemplo n.º 1
0
  /**
   * Return the right SAM handler depending on the protocol version required by the client.
   *
   * @param s Socket attached to SAM client
   * @param i2cpProps config options for our i2cp connection
   * @throws SAMException if the connection handshake (HELLO message) was malformed
   * @return A SAM protocol handler, or null if the client closed before the handshake
   */
  public static SAMHandler createSAMHandler(SocketChannel s, Properties i2cpProps, SAMBridge parent)
      throws SAMException {
    String line;
    Log log = I2PAppContext.getGlobalContext().logManager().getLog(SAMHandlerFactory.class);

    try {
      Socket sock = s.socket();
      sock.setKeepAlive(true);
      StringBuilder buf = new StringBuilder(128);
      ReadLine.readLine(sock, buf, HELLO_TIMEOUT);
      sock.setSoTimeout(0);
      line = buf.toString();
    } catch (SocketTimeoutException e) {
      throw new SAMException("Timeout waiting for HELLO VERSION", e);
    } catch (IOException e) {
      throw new SAMException("Error reading from socket", e);
    } catch (RuntimeException e) {
      throw new SAMException("Unexpected error", e);
    }
    if (log.shouldDebug()) log.debug("New message received: [" + line + ']');

    // Message format: HELLO VERSION [MIN=v1] [MAX=v2]
    Properties props = SAMUtils.parseParams(line);
    if (!"HELLO".equals(props.remove(SAMUtils.COMMAND))
        || !"VERSION".equals(props.remove(SAMUtils.OPCODE))) {
      throw new SAMException("Must start with HELLO VERSION");
    }

    String minVer = props.getProperty("MIN");
    if (minVer == null) {
      // throw new SAMException("Missing MIN parameter in HELLO VERSION message");
      // MIN optional as of 0.9.14
      minVer = "1";
    }

    String maxVer = props.getProperty("MAX");
    if (maxVer == null) {
      // throw new SAMException("Missing MAX parameter in HELLO VERSION message");
      // MAX optional as of 0.9.14
      maxVer = "99.99";
    }

    String ver = chooseBestVersion(minVer, maxVer);

    if (ver == null) {
      SAMHandler.writeString("HELLO REPLY RESULT=NOVERSION\n", s);
      return null;
    }

    if (Boolean.parseBoolean(i2cpProps.getProperty(SAMBridge.PROP_AUTH))) {
      String user = props.getProperty("USER");
      String pw = props.getProperty("PASSWORD");
      if (user == null || pw == null) throw new SAMException("USER and PASSWORD required");
      String savedPW =
          i2cpProps.getProperty(SAMBridge.PROP_PW_PREFIX + user + SAMBridge.PROP_PW_SUFFIX);
      if (savedPW == null) throw new SAMException("Authorization failed");
      PasswordManager pm = new PasswordManager(I2PAppContext.getGlobalContext());
      if (!pm.checkHash(savedPW, pw)) throw new SAMException("Authorization failed");
    }

    // Let's answer positively
    if (!SAMHandler.writeString("HELLO REPLY RESULT=OK VERSION=" + ver + "\n", s))
      throw new SAMException("Error writing to socket");

    // ...and instantiate the right SAM handler
    int verMajor = getMajor(ver);
    int verMinor = getMinor(ver);
    SAMHandler handler;

    try {
      switch (verMajor) {
        case 1:
          handler = new SAMv1Handler(s, verMajor, verMinor, i2cpProps, parent);
          break;
        case 2:
          handler = new SAMv2Handler(s, verMajor, verMinor, i2cpProps, parent);
          break;
        case 3:
          handler = new SAMv3Handler(s, verMajor, verMinor, i2cpProps, parent);
          break;
        default:
          log.error("BUG! Trying to initialize the wrong SAM version!");
          throw new SAMException("BUG! (in handler instantiation)");
      }
    } catch (IOException e) {
      log.error("Error creating the handler for version " + verMajor, e);
      throw new SAMException("IOException caught during SAM handler instantiation");
    }
    return handler;
  }
Ejemplo n.º 2
0
  public void handle() {
    String msg = null;
    String domain = null;
    String opcode = null;
    boolean canContinue = false;
    Properties props;
    final StringBuilder buf = new StringBuilder(128);

    this.thread.setName("SAMv1Handler " + _id);
    if (_log.shouldLog(Log.DEBUG)) _log.debug("SAM handling started");

    try {
      boolean gotFirstLine = false;
      while (true) {
        if (shouldStop()) {
          if (_log.shouldLog(Log.DEBUG)) _log.debug("Stop request found");
          break;
        }

        SocketChannel clientSocketChannel = getClientSocket();
        if (clientSocketChannel == null) {
          _log.info("Connection closed by client");
          break;
        }
        if (clientSocketChannel.socket() == null) {
          _log.info("Connection closed by client");
          break;
        }
        buf.setLength(0);
        // first time, set a timeout
        try {
          Socket sock = clientSocketChannel.socket();
          ReadLine.readLine(sock, buf, gotFirstLine ? 0 : FIRST_READ_TIMEOUT);
          sock.setSoTimeout(0);
        } catch (SocketTimeoutException ste) {
          writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"command timeout, bye\"\n");
          break;
        }
        msg = buf.toString();

        if (_log.shouldLog(Log.DEBUG)) {
          _log.debug("New message received: [" + msg + ']');
        }
        props = SAMUtils.parseParams(msg);
        domain = (String) props.remove(SAMUtils.COMMAND);
        if (domain == null) {
          if (_log.shouldLog(Log.DEBUG)) _log.debug("Ignoring newline");
          continue;
        }
        opcode = (String) props.remove(SAMUtils.OPCODE);
        if (opcode == null) {
          if (_log.shouldLog(Log.DEBUG)) _log.debug("Error in message format");
          break;
        }
        if (_log.shouldLog(Log.DEBUG)) {
          _log.debug("Parsing (domain: \"" + domain + "\"; opcode: \"" + opcode + "\")");
        }
        gotFirstLine = true;
        if (domain.equals("STREAM")) {
          canContinue = execStreamMessage(opcode, props);
        } else if (domain.equals("DATAGRAM")) {
          canContinue = execDatagramMessage(opcode, props);
        } else if (domain.equals("RAW")) {
          canContinue = execRawMessage(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 (_log.shouldLog(Log.DEBUG))
            _log.debug("Unrecognized message domain: \"" + domain + "\"");
          break;
        }

        if (!canContinue) {
          break;
        }
      }
    } catch (IOException e) {
      if (_log.shouldLog(Log.DEBUG)) _log.debug("Caught IOException for message [" + msg + "]", e);
    } catch (SAMException e) {
      _log.error("Unexpected exception for message [" + msg + "]", e);
    } catch (RuntimeException e) {
      _log.error("Unexpected exception for message [" + msg + "]", e);
    } finally {
      if (_log.shouldLog(Log.DEBUG)) _log.debug("Stopping handler");
      try {
        closeClientSocket();
      } catch (IOException e) {
        if (_log.shouldWarn()) _log.warn("Error closing socket", e);
      }
      if (getRawSession() != null) {
        getRawSession().close();
      }
      if (getDatagramSession() != null) {
        getDatagramSession().close();
      }
      if (getStreamSession() != null) {
        getStreamSession().close();
      }
    }
  }
Ejemplo n.º 3
0
  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();
    }
  }