package org.jscsi.target.connection.phase; import java.io.IOException; import java.security.DigestException; import javax.naming.OperationNotSupportedException; import org.jscsi.exception.InternetSCSIException; import org.jscsi.parser.BasicHeaderSegment; import org.jscsi.parser.OperationCode; import org.jscsi.parser.ProtocolDataUnit; import org.jscsi.parser.login.ISID; import org.jscsi.parser.login.LoginRequestParser; import org.jscsi.parser.login.LoginStage; import org.jscsi.target.TargetServer; import org.jscsi.target.connection.Connection; import org.jscsi.target.connection.SessionType; import org.jscsi.target.connection.TargetSession; import org.jscsi.target.connection.stage.login.LoginOperationalParameterNegotiationStage; import org.jscsi.target.connection.stage.login.SecurityNegotiationStage; import org.jscsi.target.connection.stage.login.TargetLoginStage; import org.jscsi.target.settings.ConnectionSettingsNegotiator; import org.jscsi.target.settings.Settings; import org.jscsi.target.settings.SettingsException; /** * Objects of this class represent the Target Login Phase of a connection. * * @see TargetPhase * @author Andreas Ergenzinger */ public final class TargetLoginPhase extends TargetPhase { /** * The current stage of this phase */ private TargetLoginStage stage; /** * This variable indicates if the initiator is to be considered as * authenticated, i.e. if it has given sufficient proof of its identity to * proceed to the next (Target Full Feature) phase. * <p> * Currently the jSCSI Target does not support any authentication methods and this value is initialized to * <code>true</code> for all initiators. */ private boolean authenticated = true;// TODO false if authentication // required /** * This variable will be <code>true</code> until the first call of {@link #getFirstPduAndSetToFalse()} has * happened. * <p> * This value will be <code>true</code> if the currently processed PDU is the first PDU sent by the * initiator over this phase's connection. This means that it must contain all text parameters necessary * for either starting a discovery session or a normal session. */ private boolean firstPdu = true; /** * The constructor. * * @param connection * {@inheritDoc} */ public TargetLoginPhase(Connection connection) { super(connection); } /** * Starts the login phase. * * @param pdu * {@inheritDoc} * @return {@inheritDoc} * @throws OperationNotSupportedException * {@inheritDoc} * @throws IOException * {@inheritDoc} * @throws InterruptedException * {@inheritDoc} * @throws InternetSCSIException * {@inheritDoc} * @throws DigestException * {@inheritDoc} * @throws SettingsException * {@inheritDoc} */ @Override public PHASE_EXEC_STATUS execute(ProtocolDataUnit pduInput) throws IOException, InterruptedException, InternetSCSIException, DigestException, SettingsException { assert pduInput == null; ProtocolDataUnit pdu ; LoginRequestParser parser; TargetSession session; boolean loginSuccessful = false;// will determine if settings are // committed // OODRIVE try { pdu = connection.receivePdu(); // TODO: socket should be in blocking mode // confirm OpCode if (pdu.getBasicHeaderSegment().getOpCode() != OperationCode.LOGIN_REQUEST) throw new InternetSCSIException(); // get initiatorSessionID parser = (LoginRequestParser)pdu.getBasicHeaderSegment().getParser(); ISID initiatorSessionID = parser.getInitiatorSessionID(); int cid = parser.getConnectionID(); short tsih = parser.getTargetSessionIdentifyingHandle(); /* * TODO get (new or existing) session based on TSIH But * since we don't do session reinstatement and * MaxConnections=1, we can just create a new one.*/ TargetServer targetServer = connection.getTargetServer(); session = new TargetSession(targetServer, connection, initiatorSessionID, parser .getCommandSequenceNumber(),// set ExpCmdSN // (PDU is // immediate, // hence no ++) parser.getExpectedStatusSequenceNumber() , cid, tsih); } catch (DigestException | InternetSCSIException | SettingsException e) { // LOGGER.info("Throws Exception", e); throw e; } // begin login negotiation final ConnectionSettingsNegotiator negotiator = connection.getConnectionSettingsNegotiator(); while (!negotiator.beginNegotiation()) { // do nothing, just wait for permission to begin, method is blocking } try { // if possible, enter LOPN Stage //BasicHeaderSegment bhs = pdu.getBasicHeaderSegment(); //LoginRequestParser parser = (LoginRequestParser)bhs.getParser(); LoginStage nextStageNumber;// will store return value from the last // login stage // Security Negotiation Stage (optional) if (parser.getCurrentStageNumber() == LoginStage.SECURITY_NEGOTIATION) { // complete SNS stage = new SecurityNegotiationStage(this); stage.execute(pdu); nextStageNumber = stage.getNextStageNumber(); if (nextStageNumber != null) authenticated = true; else { loginSuccessful = false; return PHASE_EXEC_STATUS.CLOSE; } if (nextStageNumber == LoginStage.LOGIN_OPERATIONAL_NEGOTIATION) { // receive first PDU from LOPNS pdu = connection.receivePdu(); BasicHeaderSegment bhs = pdu.getBasicHeaderSegment(); parser = (LoginRequestParser)bhs.getParser(); } else if (nextStageNumber == LoginStage.FULL_FEATURE_PHASE) { // we are done here loginSuccessful = true; return PHASE_EXEC_STATUS.DONE; } else { // should be unreachable, since SNS may not return NSG==SNS loginSuccessful = false; return PHASE_EXEC_STATUS.CLOSE; } } // Login Operational Parameter Negotiation Stage (also optional, but // either SNS or LOPNS must be passed before proceeding to FFP) if (authenticated && parser.getCurrentStageNumber() == LoginStage.LOGIN_OPERATIONAL_NEGOTIATION) { stage = new LoginOperationalParameterNegotiationStage(this); stage.execute(pdu); nextStageNumber = stage.getNextStageNumber(); if (nextStageNumber == LoginStage.FULL_FEATURE_PHASE) { loginSuccessful = true; return PHASE_EXEC_STATUS.DONE; } } // else loginSuccessful = false; return PHASE_EXEC_STATUS.CLOSE; } catch (DigestException e) { loginSuccessful = false; throw e; } catch (IOException e) { loginSuccessful = false; throw e; } catch (InterruptedException e) { loginSuccessful = false; throw e; } catch (InternetSCSIException e) { loginSuccessful = false; throw e; } catch (SettingsException e) { loginSuccessful = false; throw e; } finally { // commit or roll back changes and release exclusive negotiator lock negotiator.finishNegotiation(loginSuccessful); // OODRIVE if (loginSuccessful) { // if this is the leading connection, set the session type final Settings settings = connection.getSettings(); if (connection.isLeadingConnection()) session.setSessionType(SessionType.getSessionType(settings.getSessionType())); session.setTargetName(settings.getTargetName()); connection.setPhase(new TargetFullFeaturePhase(connection)); } } } /** * This method will return <code>true</code> if currently processed PDU is * the first PDU sent by the initiator over this phase's connection. * Subsequent calls will always return <code>false</code>. * * @return <code>true</code> if and only if this method is called for the * first time */ public boolean getFirstPduAndSetToFalse() { if (!firstPdu) return false; firstPdu = false; return true; } public final boolean getAuthenticated() { return authenticated; } }