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; }
/* 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(); } }