/**
* 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.Constants;
import org.jscsi.parser.ProtocolDataUnit;
import org.jscsi.parser.TargetMessageParser;
import org.jscsi.parser.datasegment.DataSegmentFactory.DataSegmentFormat;
import org.jscsi.utils.Utils;
/**
* <h1>LoginResponseParser</h1>
* <p>
* This is a parser for the Login Response Message of the iSCSI Protocol (RFC3720). So it parses all the field
* of this login message and offers it fields with its getter methods.
* <p>
* The Login Response indicates the progress and/or end of the Login Phase.
* <p>
* <h4>T (Transit) bit</h4> The <code>T</code> bit is set to <code>1</code> as an indicator of the end of the
* stage. If the <code>T</code> bit is set to <code>1</code> and NSG is FullFeaturePhase, then this is also
* the Final Login Response (see Chapter 5). A <code>T</code> bit of <code>0</code> indicates a "partial"
* response, which means "more negotiation needed".
* <p>
* A Login Response with a <code>T</code> bit set to <code>1</code> MUST NOT contain key=value pairs that may
* require additional answers from the initiator within the same stage.
* <p>
* A Login Response with a <code>T</code> bit set to <code>1</code> MUST NOT contain key=value pairs that may
* require additional answers from the initiator within the same stage.
* <p>
* <h4>StatSN</h4> For the first Login Response (the response to the first Login Request), this is the
* starting status Sequence Number for the connection. The next response of any kind, including the next Login
* Response, if any, in the same Login Phase, will carry this <code>number + 1</code>. This field is only
* valid if the Status-Class is <code>0</code>.
* <p>
* <h4>Login Parameters</h4> The target MUST provide some basic parameters in order to enable the initiator to
* determine if it is connected to the correct port and the initial text parameters for the security exchange.
* <p>
* All the rules specified in Section 10.11.6 Text Response Data for text responses also hold for Login
* Responses. 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.
*
* @author Volker Wildi
*/
public final class LoginResponseParser extends TargetMessageParser {
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
/** The Continue Flag. */
private boolean continueFlag;
/**
* The Status returned in a Login Response indicates the execution status of
* the Login Phase. The status includes:
* <ul>
* <li>Status-Class</li>
* <li>Status-Detail</li>
* </ul>
* <p>
* <code>0</code> Status-Class indicates success.
* <p>
* A non-zero Status-Class indicates an exception. In this case, Status-Class is sufficient for a simple
* initiator to use when handling exceptions, without having to look at the Status-Detail. The
* Status-Detail allows finer-grained exception handling for more sophisticated initiators and for better
* information for logging.
* <p>
* If the Status Class is not <code>0</code>, the initiator and target MUST close the TCP connection.
* <p>
* If the target wishes to reject the Login Request for more than one reason, it should return the primary
* reason for the rejection.
*
* @see LoginStatus
*/
private LoginStatus status;
/** Current stage. */
private LoginStage currentStageNumber;
/** Next stage. */
private LoginStage nextStageNumber;
/** The maximum version. */
private int maxVersion;
/** The active version. */
private int activeVersion;
/** Initiator Session ID (ISID). */
private ISID initiatorSessionID;
/** Target Session Identifying Handle (TSIH). */
private short targetSessionIdentifyingHandle;
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
/**
* Default constructor, creates a new, empty <code>LoginResponseParser</code> object.
*
* @param initProtocolDataUnit
* The reference <code>ProtocolDataUnit</code> instance, which
* contains this <code>LoginResponseParser</code> subclass
* object.
*/
public LoginResponseParser(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, "activeVersion", activeVersion, 1);
Utils.printField(sb, "maxVersion", maxVersion, 1);
sb.append(initiatorSessionID.toString());
Utils.printField(sb, "TSIH", targetSessionIdentifyingHandle, 1);
sb.append(super.toString());
Utils.printField(sb, "Status", status.value(), 1);
return sb.toString();
}
/** {@inheritDoc} */
@Override
public final void clear() {
super.clear();
continueFlag = false;
currentStageNumber = LoginStage.SECURITY_NEGOTIATION;
nextStageNumber = LoginStage.SECURITY_NEGOTIATION;
maxVersion = 0;
activeVersion = 0;
initiatorSessionID.clear();
targetSessionIdentifyingHandle = 0;
}
/** {@inheritDoc} */
@Override
public final DataSegmentFormat getDataSegmentFormat() {
return DataSegmentFormat.TEXT;
}
/** {@inheritDoc} */
@Override
public final boolean canHaveDigests() {
return false;
}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
/**
* The Status returned in a Login Response indicates the execution status of
* the Login Phase. The status includes:
* <ul>
* <li>Status-Class</li>
* <li>Status-Detail</li>
* </ul>
* <p>
* <code>0</code> Status-Class indicates success.
* <p>
* A non-zero Status-Class indicates an exception. In this case, Status-Class is sufficient for a simple
* initiator to use when handling exceptions, without having to look at the Status-Detail. The
* Status-Detail allows finer-grained exception handling for more sophisticated initiators and for better
* information for logging.
*
* @return The status of this LoginResponseParser object.
*/
public final LoginStatus getStatus() {
return status;
}
/**
* When set to <code>1</code>, indicates that the text (set of key=value
* pairs) in this Login Response is not complete (it will be continued on
* subsequent Login Responses); otherwise, it indicates that this Login
* Response ends a set of key=value pairs. A Login Response with the <code>C</code> bit set to
* <code>1</code> MUST have the <code>T</code> bit
* set to <code>0</code>.
*
* @return The status of the Continue Flag of this <code>LoginResponseParser</code> object.
*/
public final boolean isContinueFlag() {
return continueFlag;
}
/**
* Returns the <em>Current Stage Number</em> of this Login Response Message.
*
* @return Number of the Current Stage.
* @see org.jscsi.parser.login.LoginStage
*/
public final LoginStage getCurrentStageNumber() {
return currentStageNumber;
}
/**
* Returns the Initiator Session ID (ISID) of this LoginResponseParser
* object.
*
* @return Returns the Initiator Session ID (ISID) of this
* LoginResponseParser object.
* @see ISID
*/
public final ISID getInitiatorSessionID() {
return initiatorSessionID;
}
/**
* This is the highest version number supported by the target.
* <p>
* All Login Responses within the Login Phase MUST carry the same Version-max.
* <p>
* The initiator MUST use the value presented as a response to the first Login Request.
*
* @return The maximum version of this login request message.
*/
public final int getMaxVersion() {
return maxVersion;
}
/**
* Indicates the highest version supported by the target and initiator.
* <p>
* If the target does not support a version within the range specified by the initiator, the target
* rejects the login and this field indicates the lowest version supported by the target.
* <p>
* All Login Responses within the Login Phase MUST carry the same Version-active.
* <p>
* The initiator MUST use the value presented as a response to the first Login Request.
*
* @return The active version of this <code>LoginResponseParser</code> object.
*/
public final int getActiveVersion() {
return activeVersion;
}
/**
* Returns the <em> Next Stage Number</em> of this Login Response Message.
*
* @return The Number of the Next Stage.
* @see org.jscsi.parser.login.LoginStage
*/
public final LoginStage getNextStageNumber() {
return nextStageNumber;
}
/**
* The TSIH is the target assigned session identifying handle. Its internal
* format and content are not defined by this protocol except for the value <code>0</code> that is
* reserved. With the exception of the Login
* Final-Response in a new session, this field should be set to the TSIH
* provided by the initiator in the Login Request. For a new session, the
* target MUST generate a non-zero TSIH and ONLY return it in the Login
* Final-Response (see Section 5.3 Login Phase).
*
* @return Returns the Target Session Identifying Handle of this <code>LoginResponseParser</code> object.
*/
public final short getTargetSessionIdentifyingHandle() {
return targetSessionIdentifyingHandle;
}
public void setStatus(LoginStatus status) {
this.status = status;
}
public void setContinueFlag(boolean continueFlag) {
this.continueFlag = continueFlag;
}
public void setCurrentStageNumber(LoginStage currentStage) {
this.currentStageNumber = currentStage;
}
public void setNextStageNumber(LoginStage nextStage) {
this.nextStageNumber = nextStage;
}
public void setInitiatorSessionID(ISID initiatorSessionID) {
this.initiatorSessionID = initiatorSessionID;
}
public void setTargetSessionIdentifyingHandle(short targetSessionIdentifyingHandle) {
this.targetSessionIdentifyingHandle = targetSessionIdentifyingHandle;
}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
/** {@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 = (line & Constants.THIRD_BYTE_MASK) >> Constants.ONE_BYTE_SHIFT;
activeVersion = line & Constants.FOURTH_BYTE_MASK;
}
/** {@inheritDoc} */
@Override
protected final void deserializeBytes12to15(final int line) throws InternetSCSIException {
// use the logicalUnitNumber variable as temporary storage
logicalUnitNumber |= Utils.getUnsignedLong(line);
initiatorSessionID.deserialize(logicalUnitNumber);
targetSessionIdentifyingHandle = (short)(line & Constants.LAST_TWO_BYTES_MASK);
}
/** {@inheritDoc} */
@Override
protected final void deserializeBytes36to39(final int line) throws InternetSCSIException {
status =
LoginStatus
.valueOf((short)((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 {
if (status != LoginStatus.SUCCESS) statusSequenceNumber = 0;
if (status != LoginStatus.SUCCESS && statusSequenceNumber != 0) {
exceptionMessage =
"While no successful login is preformed, the StatusSequenceNumber must be 0. statusSequenceNumber="+statusSequenceNumber+", status="+status;
break;
}
// message is checked correctly
return;
} while (false);
throw new InternetSCSIException(exceptionMessage);
}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
/** {@inheritDoc} */
@Override
protected final int serializeBytes1to3() {
int line = activeVersion;
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 serializeBytes36to39() {
int line = 0;
line |= status.value() << Constants.TWO_BYTES_SHIFT;
return line;
}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
}