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