/**
* Copyright (c) 2012, University of Konstanz, Distributed Systems Group
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the University of Konstanz nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.jscsi.parser.login;
import org.jscsi.exception.InternetSCSIException;
import org.jscsi.parser.BasicHeaderSegment;
import org.jscsi.parser.Constants;
import org.jscsi.parser.InitiatorMessageParser;
import org.jscsi.parser.ProtocolDataUnit;
import org.jscsi.parser.datasegment.DataSegmentFactory.DataSegmentFormat;
import org.jscsi.utils.Utils;
/**
* <h1>LoginRequestParser</h1>
* <p>
* This class parses a Login Request message defined in the iSCSI Standard (RFC3720).
* <p>
* After establishing a TCP connection between an initiator and a target, the initiator MUST start a Login
* Phase to gain further access to the target’s resources.
* <p>
* The Login Phase (see Chapter 5) consists of a sequence of Login Requests and Responses that carry the same
* Initiator Task Tag.
* <p>
* <b>Login Requests are always considered as immediate.</b>
* <p/>
* The version number of the current draft is <code>0x00</code>. As such, all devices MUST carry version
* <code>0x00</code> for both Version-min and Version-max.
* <p>
* Here the final flag has the following meaning: If set to <code>1</code>, indicates that the initiator is
* ready to transit to the next stage.
* <p>
* If the <code>T</code> bit is set to <code>1</code> and <code>NSG</code> is <code>FullFeaturePhase</code>,
* then this also indicates that the initiator is ready for the Final Login Response (see Chapter 5).
* <p>
* <h4>T (Transit) Bit</h4> If set to <code>1</code>, indicates that the initiator is ready to transit to the
* next stage.
* <p>
* If the T bit is set to <code>1</code> and NSG is FullFeaturePhase, then this also indicates that the
* initiator is ready for the Final Login Response (see Chapter 5).
* <p>
* <h4>CmdSN</h4> CmdSN is either the initial command sequence number of a session (for the first Login
* Request of a session - the "leading" login), or the command sequence number in the command stream if the
* login is for a new connection in an existing session.<br/>
* Examples:<br/>
* <ul>
* <li>Login on a leading connection - if the leading login carries the CmdSN <code>123</code>, all other
* Login Requests in the same Login Phase carry the CmdSN <code>123</code> and the first non-immediate command
* in FullFeaturePhase also carries the CmdSN <code>123</code>.</li>
* <li>Login on other than a leading connection - if the current CmdSN at the time the first login on the
* connection is issued is <code>500</code>, then that PDU carries <code>CmdSN=500</code>. Subsequent Login
* Requests that are needed to complete this Login Phase may carry a CmdSN higher than <code>500</code> if
* non-immediate requests that were issued on other connections in the same session advance CmdSN.</li>
* </ul>
* If the Login Request is a leading Login Request, the target MUST use the value presented in CmdSN as the
* target value for ExpCmdSN.
* <p>
* <h4>ExpStatSN</h4> For the first Login Request on a connection this is ExpStatSN for the old connection and
* this field is only valid if the Login Request restarts a connection (see Section 5.3.4 Connection
* Reinstatement). For subsequent Login Requests it is used to acknowledge the Login Responses with their
* increasing StatSN values.
* <p>
* <h4>Login Parameters (Data Segment)</h4> The initiator MUST provide some basic parameters in order to
* enable the target to determine if the initiator may use the target’s resources and the initial text
* parameters for the security exchange. All the rules specified in Section 10.10.5 Text for text requests
* also hold for Login Requests. Keys and their explanations are listed in Chapter 11 (security negotiation
* keys) and Chapter 12 (operational parameter negotiation keys). All keys in Chapter 12, except for the X
* extension formats, MUST be supported by iSCSI initiators and targets. Keys in Chapter 11 only need to be
* supported when the function to which they refer is mandatory to implement.
* <p>
*
* @author Volker Wildi
*/
public final class LoginRequestParser extends InitiatorMessageParser {
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
/** The Continue Flag. */
private boolean continueFlag;
/** The Current stage in the session. */
private LoginStage currentStageNumber;
/** The next stage in the session. */
private LoginStage nextStageNumber;
/** The maximum version number to support. */
private byte maxVersion;
/** The minimum version number to support. */
private byte minVersion;
/** The Initiator Session ID (ISID). */
private ISID initiatorSessionID;
/** The Target Session Identifying Handle (TSIH). */
private short targetSessionIdentifyingHandle;
/** The Connection ID. */
private int connectionID;
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
/**
* Default constructor, creates a new, empty <code>LoginRequestParser</code> object.
*
* @param initProtocolDataUnit
* The reference <code>ProtocolDataUnit</code> instance, which
* contains this <code>LoginRequestParser</code> subclass object.
*/
public LoginRequestParser(final ProtocolDataUnit initProtocolDataUnit) {
super(initProtocolDataUnit);
initiatorSessionID = new ISID();
}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
/** {@inheritDoc} */
@Override
public final String toString() {
final StringBuilder sb = new StringBuilder(Constants.LOG_INITIAL_SIZE);
Utils.printField(sb, "Continue Flag", continueFlag, 1);
Utils.printField(sb, "CSG", currentStageNumber.value(), 1);
Utils.printField(sb, "NSG", nextStageNumber.value(), 1);
Utils.printField(sb, "minVersion", minVersion, 1);
Utils.printField(sb, "maxVersion", maxVersion, 1);
sb.append(initiatorSessionID.toString());
Utils.printField(sb, "TSIH", targetSessionIdentifyingHandle, 1);
Utils.printField(sb, "CID", connectionID, 1);
sb.append(super.toString());
return sb.toString();
}
/** {@inheritDoc} */
@Override
public final DataSegmentFormat getDataSegmentFormat() {
return DataSegmentFormat.TEXT;
}
/** {@inheritDoc} */
@Override
public final boolean canHaveDigests() {
return false;
}
/** {@inheritDoc} */
@Override
public final void clear() {
super.clear();
continueFlag = false;
currentStageNumber = LoginStage.SECURITY_NEGOTIATION;
nextStageNumber = LoginStage.SECURITY_NEGOTIATION;
maxVersion = 0x00;
minVersion = 0x00;
initiatorSessionID.clear();
targetSessionIdentifyingHandle = 0x0000;
connectionID = 0x00000000;
}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
/**
* A unique ID for this connection within the session.
* <p>
* All Login Requests within the Login Phase MUST carry the same <code>CID</code>.
* <p>
* The target MUST use the value presented with the first Login Request.
* <p>
* A Login Request with a non-zero <code>TSIH</code> and a <code>CID</code> equal to that of an existing
* connection implies a logout of the connection followed by a Login (see Section 5.3.4 Connection
* Reinstatement). For the details of the implicit Logout Request, see Section 10.14 Logout Request.
*
* @return The Connection ID of this LoginRequestParser object.
*/
public final int getConnectionID() {
return connectionID;
}
/**
* When set to <code>1</code>, indicates that the text (set of key=value
* pairs) in this Login Request is not complete (it will be continued on
* subsequent Login Requests); otherwise, it indicates that this Login
* Request ends a set of key=value pairs. A Login Request with the <code>C</code> bit set to
* <code>1</code> MUST have the <code>T</code> bit
* set to <code>0</code>.
*
* @return Returns <code>true</code>, if the Continue Bit is set. Else <code>false</code>.
*/
public final boolean isContinueFlag() {
return continueFlag;
}
/**
* Returns the <em>Current Stage Number</em> of this Login Request Message.
* <p>
* Through these fields, Current Stage (CSG) and Next Stage (NSG), the Login negotiation requests and
* responses are associated with a specific stage in the session (SecurityNegotiation,
* LoginOperationalNegotiation, FullFeaturePhase) and may indicate the next stage to which they want to
* move (see Chapter 5). The next stage value is only valid when the T bit is 1; otherwise, it is
* reserved.
* <p>
* The stage codes are:
* <ul>
* <li><code>0</code> - SecurityNegotiation</li>
* <li><code>1</code> - LoginOperationalNegotiation</li>
* <li><code>3</code> - FullFeaturePhase</li>
* </ul>
* <p>
* All other codes are reserved.
*
* @return Number of the Current Stage.
* @see org.jscsi.parser.login.LoginStage
*/
public final LoginStage getCurrentStageNumber() {
return currentStageNumber;
}
/**
* Returns the <em>Initiator Session ID (ISID)</em> of this
* LoginRequestParser object.
*
* @return Returns the <em>Initiator Session ID (ISID)</em> of this <code>LoginRequestParser</code>
* object.
* @see ISID
*/
public final ISID getInitiatorSessionID() {
return initiatorSessionID;
}
/**
* Maximum Version number supported.
* <p>
* All Login Requests within the Login Phase MUST carry the same Version-max.
* <p>
* The target MUST use the value presented with the first Login Request.
*
* @return The maximum version of this login request message.
*/
public final byte getMaxVersion() {
return maxVersion;
}
/**
* All Login Requests within the Login Phase MUST carry the same
* Version-min. The target MUST use the value presented with the first Login
* Request.
*
* @return The minimum version of this login request message.
*/
public final byte getMinVersion() {
return minVersion;
}
/**
* Returns the <em>Next Stage Number</em> of this Login Request Message.
* <p>
* Through these fields, Current Stage (CSG) and Next Stage (NSG), the Login negotiation requests and
* responses are associated with a specific stage in the session (SecurityNegotiation,
* LoginOperationalNegotiation, FullFeaturePhase) and may indicate the next stage to which they want to
* move (see Chapter 5). The next stage value is only valid when the T bit is 1; otherwise, it is
* reserved.
* <p>
* The stage codes are:
* <ul>
* <li><code>0</code> - SecurityNegotiation</li>
* <li><code>1</code> - LoginOperationalNegotiation</li>
* <li><code>3</code> - FullFeaturePhase</li>
* </ul>
* <p>
* All other codes are reserved.
*
* @return The Number of the Next Stage.
* @see org.jscsi.parser.login.LoginStage
*/
public final LoginStage getNextStageNumber() {
return nextStageNumber;
}
/**
* <em>Target Session Identifying Handle (TSIH)</em> must be set in the
* first Login Request. The reserved value <code>0</code> MUST be used on
* the first connection for a new session. Otherwise, the <em>TSIH</em> sent
* by the target at the conclusion of the successful login of the first
* connection for this session MUST be used. The <em>TSIH</em> identifies to
* the target the associated existing session for this new connection.
* <p>
* All Login Requests within a Login Phase MUST carry the same <em>TSIH</em>.
* <p>
* The target MUST check the value presented with the first Login Request and act as specified in Section
* 5.3.1 Login Phase Start.
*
* @return Returns the Target Session Identifying Handle of this
* LoginRequestParser object.
*/
public final short getTargetSessionIdentifyingHandle() {
return targetSessionIdentifyingHandle;
}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
/**
* Sets the new Connection ID of this LoginRequestParser object.
*
* @param initCID
* The new Connection ID.
* @see #getConnectionID()
*/
public final void setConnectionID(final int initCID) {
connectionID = initCID;
}
/**
* Sets the new state of the <em>Continue Flag</em> of this <code>LoginRequestParser</code> obejct.
*
* @param initContinueFlag
* The new state of the Continue Flag.
* @see #isContinueFlag()
*/
public final void setContinueFlag(final boolean initContinueFlag) {
continueFlag = initContinueFlag;
}
/**
* Sets the new <em>Current Stage Number</em> of this <code>LoginRequestParser</code> object.
*
* @param initCSG
* The new Current Stage Number.
* @see #getCurrentStageNumber()
*/
public final void setCurrentStageNumber(final LoginStage initCSG) {
currentStageNumber = initCSG;
}
/**
* Sets the new <em>Initiator Session ID (ISID)</em> of this <code>LoginRequestParser</code> object.
*
* @param initISID
* The new Initiator Session ID (ISID).
*/
public final void setInitiatorSessionID(final ISID initISID) {
initiatorSessionID = initISID;
}
/**
* Sets the new <em>Maximum Version number</em> of this <code>LoginRequestParser</code> object.
*
* @param initMaxVersion
* The new Maximum Version.
* @see #getMaxVersion
*/
public final void setMaxVersion(final byte initMaxVersion) {
maxVersion = initMaxVersion;
}
/**
* Sets the new <em>Minimum Version number</em> of this <code>LoginRequestParser</code> object.
*
* @param initMinVersion
* The new Minimum Version.
* @see #getMinVersion
*/
public final void setMinVersion(final byte initMinVersion) {
minVersion = initMinVersion;
}
/**
* Sets the new <em>Next Stage Number</em> of this <code>LoginRequestParser</code> object.
*
* @param initNSG
* The new Next Stage Number.
* @see #getNextStageNumber()
*/
public final void setNextStageNumber(final LoginStage initNSG) {
nextStageNumber = initNSG;
}
/**
* Sets the new <em>Target Session Identifying Handle</em> of this <code>LoginRequestParser</code> object.
*
* @param initTSIH
* The new Target Session Identifying Handle.
*/
public final void setTargetSessionIdentifyingHandle(final short initTSIH) {
targetSessionIdentifyingHandle = initTSIH;
}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
/** {@inheritDoc} */
@Override
protected final void deserializeBytes1to3(final int line) throws InternetSCSIException {
continueFlag = Utils.isBitSet(line & Constants.CONTINUE_FLAG_MASK);
Utils.isReserved(line & LoginConstants.BIT_11_AND_12_FLAG_MASK);
currentStageNumber =
LoginStage
.valueOf((byte)((line & LoginConstants.CSG_FLAG_MASK) >>> LoginConstants.CSG_BIT_SHIFT));
nextStageNumber =
LoginStage.valueOf((byte)((line & LoginConstants.NSG_FLAG_MASK) >>> Constants.TWO_BYTES_SHIFT));
maxVersion = (byte)((line & Constants.THIRD_BYTE_MASK) >> Constants.ONE_BYTE_SHIFT);
minVersion = (byte)(line & Constants.FOURTH_BYTE_MASK);
}
/** {@inheritDoc} */
@Override
protected final void deserializeBytes12to15(final int line) throws InternetSCSIException {
// use the logicalUnitNumber variable as temporary storage
final long l = Utils.getUnsignedLong(line);
logicalUnitNumber |= l;
initiatorSessionID.deserialize(logicalUnitNumber);
targetSessionIdentifyingHandle = (short)(line & Constants.LAST_TWO_BYTES_MASK);
}
/** {@inheritDoc} */
@Override
protected void deserializeBytes20to23(final int line) throws InternetSCSIException {
connectionID = (line & Constants.FIRST_TWO_BYTES_MASK) >>> Constants.TWO_BYTES_SHIFT;
Utils.isReserved(line & Constants.LAST_TWO_BYTES_MASK);
}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
/** {@inheritDoc} */
@Override
protected final void checkIntegrity() throws InternetSCSIException {
String exceptionMessage;
do {
final BasicHeaderSegment bhs = protocolDataUnit.getBasicHeaderSegment();
if (bhs.isFinalFlag() && continueFlag) {
exceptionMessage = "Transit and Continue Flag cannot be set at the same time.";
break;
}
if (!bhs.isFinalFlag() && nextStageNumber != LoginStage.SECURITY_NEGOTIATION) {
exceptionMessage = "NextStageNumber is reserved, when the TransitFlag is not set.";
break;
}
if (bhs.isFinalFlag()) {
if (currentStageNumber == LoginStage.SECURITY_NEGOTIATION) {
if (nextStageNumber == LoginStage.SECURITY_NEGOTIATION) {
exceptionMessage = "This transition (SNP -> SNP) is not allowed.";
break;
}
} else if (currentStageNumber == LoginStage.LOGIN_OPERATIONAL_NEGOTIATION) {
if (nextStageNumber == LoginStage.SECURITY_NEGOTIATION) {
exceptionMessage = "This transition (LONP -> SNP) is not allowed.";
break;
} else if (nextStageNumber == LoginStage.LOGIN_OPERATIONAL_NEGOTIATION) {
exceptionMessage = "This transition (LONP -> LONP) is not allowed.";
break;
}
}
}
if (minVersion != 0x00) {
exceptionMessage = "MinVersion is not in a valid range.";
break;
}
if (maxVersion != 0x00) {
exceptionMessage = "MaxVersion is not in a valid range.";
break;
}
if (minVersion != maxVersion) {
exceptionMessage = "MinVersion and MaxVersion have not the same value.";
break;
}
// message is checked correctly
return;
} while (false);
throw new InternetSCSIException(exceptionMessage);
}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
/** {@inheritDoc} */
@Override
protected final int serializeBytes1to3() {
int line = minVersion;
line |= maxVersion << Constants.ONE_BYTE_SHIFT;
line |= nextStageNumber.value() << Constants.TWO_BYTES_SHIFT;
line |= currentStageNumber.value() << LoginConstants.CSG_BIT_SHIFT;
if (continueFlag) {
line |= Constants.CONTINUE_FLAG_MASK;
}
return line;
}
/** {@inheritDoc} */
@Override
protected final int serializeBytes8to11() throws InternetSCSIException {
logicalUnitNumber = initiatorSessionID.serialize();
return (int)(logicalUnitNumber >>> Constants.FOUR_BYTES_SHIFT);
}
/** {@inheritDoc} */
@Override
protected final int serializeBytes12to15() {
int line = (int)(logicalUnitNumber & Constants.LAST_FOUR_BYTES_MASK);
line |= targetSessionIdentifyingHandle;
return line;
}
/** {@inheritDoc} */
@Override
protected final int serializeBytes20to23() {
return connectionID << Constants.TWO_BYTES_SHIFT;
}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
}